Skip to content

Stdlib Workflow

Stdlib Workflow #24844

name: Stdlib Workflow
on:
workflow_dispatch:
inputs:
repo-name:
description: >
Name of the repo containing tests. Repo should be in ballerina-platform org.
Example- module-ballerina-http
required: true
tests:
description: >
Array of test names
Example-["hello world","https_passthrough"]
required: false
zipURL:
description: 'Ballerina Zip Distribution URL'
required: false
clusterName:
description: 'Cluster name'
default: 'bal-perf-cluster-test'
required: false
dispatchType:
description: 'Repository dispatch type'
default: ''
required: false
branch:
description: 'Branch of the given repository'
default: ''
required: false
jobs:
setup:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v2
- name: Set up JDK 17
uses: actions/setup-java@v3
with:
distribution: 'temurin'
java-version: 17.0.7
- name: Setup outputs
id: write
run: |
testsInput='${{ github.event.inputs.tests }}'
branch='${{ github.event.inputs.branch }}'
if [[ -z $testsInput ]]; then
repoName=${{ github.event.inputs.repo-name }}
git clone https://github.com/ballerina-platform/$repoName
if [[ -z $repoName ]]; then
echo "Github repo should be in `ballerina-platform` org"
exit 1
fi
if [[ ! -z $branch ]]; then
(cd $repoName ; git checkout $branch)
fi
cd $repoName/load-tests
tests=$(ls -d */ | cut -f1 -d'/' | jq -R -s -c 'split("\n")[:-1]')
echo "::set-output name=test::${tests}"
else
tests=$(echo $testsInput | jq '.')
if [[ -z $tests ]]; then
echo "Invalid json array input for tests"
exit 1
fi
if [[ $tests == "[]" ]]; then
echo "Empty test case array"
exit 1
fi
tests=${tests//$'\n'/}
echo "::set-output name=test::${tests}"
fi
- name: AZURE LOGIN
uses: azure/login@v1
with:
creds: ${{secrets.AZURE_CREDENTIALS}}
- name: Create Cluster
env:
AZURE_APP_ID: ${{ secrets.AZURE_APP_ID }}
AZURE_APP_PASSWORD: ${{ secrets.AZURE_APP_PASSWORD }}
RESOURCE_GROUP: ${{ secrets.CLUSTER_RESOURCE_GROUP }}
run: |
az aks create \
--resource-group "${RESOURCE_GROUP}" \
--name "${{ github.event.inputs.clusterName }}" \
--service-principal "${AZURE_APP_ID}"\
--client-secret "${AZURE_APP_PASSWORD}" \
--node-vm-size "Standard_F4s_v2" \
--node-osdisk-size 256 \
--node-osdisk-type Managed \
--node-count 1 \
--location "eastus" \
--generate-ssh-keys
- name: Configure AKS
uses: azure/aks-set-context@v1
with:
creds: '${{ secrets.AZURE_CREDENTIALS }}'
cluster-name: '${{ github.event.inputs.clusterName }}'
resource-group: ${{ secrets.CLUSTER_RESOURCE_GROUP }}
- name: Deploy Niginx
run: |
# Create a namespace for your ingress resources
kubectl create namespace ingress-basic
# Add the ingress-nginx repository
helm repo add ingress-nginx https://kubernetes.github.io/ingress-nginx
# Use Helm to deploy an NGINX ingress controller
helm install nginx-ingress ingress-nginx/ingress-nginx \
--namespace ingress-basic \
--set controller.replicaCount=2 \
--set controller.nodeSelector."beta\.kubernetes\.io/os"=linux \
--set defaultBackend.nodeSelector."beta\.kubernetes\.io/os"=linux \
--set controller.admissionWebhooks.patch.nodeSelector."beta\.kubernetes\.io/os"=linux
# Wait for ingress ip
kubectl get service nginx-ingress-ingress-nginx-controller --namespace ingress-basic -w \
-o 'go-template={{with .status.loadBalancer.ingress}}{{range .}}{{.ip}}{{"\n"}}{{end}}{{.err}}{{end}}' 2>/dev/null \
| head -n1
kubectl apply -f https://raw.githubusercontent.com/ballerina-platform/ballerina-performance-cloud/main/base-image/volumes.yaml
- name: Create results branch in target repo
id: branch
run: |
dispatchType='${{ github.event.inputs.dispatchType }}'
if [[ -z $dispatchType ]]; then
repoName=${{ github.event.inputs.repo-name }}
git clone https://ballerina-bot:${{ secrets.BALLERINA_BOT_TOKEN }}@github.com/ballerina-platform/"${repoName}" remote_repo
cd remote_repo
timestamp=$(date +%s -u)
branch_name="nightly-${timestamp}"
git checkout -b ${branch_name}
git push origin ${branch_name}
echo "::set-output name=branch-name::${branch_name}"
fi
outputs:
matrix: ${{ steps.write.outputs.test }}
branch-name: ${{ steps.branch.outputs.branch-name }}
run_test:
needs: setup
runs-on: ubuntu-latest
strategy:
max-parallel: 1
fail-fast: false
matrix:
test_name: ${{ fromJson(needs.setup.outputs.matrix) }}
payload: [50]
users: [60]
env:
TEST_NAME: "${{ matrix.test_name }}"
TEST_ROOT: "load-tests"
steps:
- uses: actions/checkout@v2
if: ${{ github.event.inputs.branch == '' }}
with:
repository: ballerina-platform/${{ github.event.inputs.repo-name }}
- uses: actions/checkout@v2
if: ${{ github.event.inputs.branch != '' }}
with:
repository: ballerina-platform/${{ github.event.inputs.repo-name }}
ref: ${{ github.event.inputs.branch }}
- name: Login to DockerHub
uses: docker/login-action@v1
with:
username: ${{ secrets.DOCKER_HUB_USERNAME }}
password: ${{ secrets.DOCKER_HUB_ACCESS_TOKEN }}
- name: Ballerina Build
if: ${{ github.event.inputs.zipURL == '' }}
uses: ballerina-platform/ballerina-action@nightly
env:
DOCKER_BUILDKIT: 0
WORKING_DIR: load-tests/${{ matrix.test_name }}/src
with:
args:
build
- name: Set up JDK 17
uses: actions/setup-java@v3
with:
distribution: 'temurin'
java-version: 17.0.7
- name: Ballerina Build from Distribution ZIP
if: ${{ github.event.inputs.zipURL != '' }}
run: |
export packageUser=ballerina-bot
curl -u $packageUser:${{ secrets.BALLERINA_BOT_TOKEN }} -L -o ballerina-dist.zip ${{ github.event.inputs.zipURL }}
mkdir ballerina-dist
unzip -q ballerina-dist.zip -d ballerina-dist
export DOCKER_BUILDKIT=0
export BAL_PATH=`pwd`/ballerina-dist
echo "var ${BAL_PATH}"
pushd load-tests/${TEST_NAME}/src
chmod +x $BAL_PATH/bin/bal
$BAL_PATH/bin/bal build
- name: Docker push
run: docker push ballerina/${TEST_NAME}:latest
- name: Copy artifacts
run: |
ls -ltr
cp -a ${TEST_ROOT}/${TEST_NAME}/src/target/kubernetes/${TEST_NAME}/. ${TEST_ROOT}/${TEST_NAME}/deployment/
- name: 'Install Kustomize'
run: |
curl -H "Accept: application/vnd.github.v3+json" -H "Authorization: Bearer ${{ secrets.GITHUB_TOKEN }}" -s "https://raw.githubusercontent.com/kubernetes-sigs/kustomize/master/hack/install_kustomize.sh" | bash
- name: 'Run Kustomize'
run: |
kustomize build ${TEST_ROOT}/${TEST_NAME}/deployment > ${TEST_ROOT}/${TEST_NAME}/final.yaml
- name: Configure AKS
uses: azure/aks-set-context@v1
with:
creds: '${{ secrets.AZURE_CREDENTIALS }}'
cluster-name: '${{ github.event.inputs.clusterName }}'
resource-group: ${{ secrets.CLUSTER_RESOURCE_GROUP }}
- name: Deploy artifacts
run: |
kubectl apply -f ${TEST_ROOT}/${TEST_NAME}/final.yaml
- name: Login via Az module
uses: azure/login@v1
with:
creds: ${{secrets.AZURE_CREDENTIALS}}
- name: Write values to outputs
id: write
run: |
echo "::set-output name=cluster-ip::$(kubectl get service nginx-ingress-ingress-nginx-controller --namespace ingress-basic -w \
-o 'go-template={{with .status.loadBalancer.ingress}}{{range .}}{{.ip}}{{"\n"}}{{end}}{{.err}}{{end}}' 2>/dev/null \
| head -n1)"
echo "::set-output name=scenario-name::${TEST_NAME}"
echo "::set-output name=vm-name::bal-perf-vm-`echo ${TEST_NAME} | tr '_' '-'`-${{ matrix.users }}-${{ matrix.payload }}-${{ GITHUB.RUN_NUMBER }}"
echo "::set-output name=git-token::${{ secrets.BALLERINA_BOT_TOKEN }}"
echo "::set-output name=space-id::${{ secrets.SPACE_ID }}"
echo "::set-output name=message-key::${{ secrets.MESSAGE_KEY }}"
echo "::set-output name=chat-token::${{ secrets.CHAT_TOKEN }}"
echo "::set-output name=dispatch-type::${{ github.event.inputs.dispatchType }}"
version_out=""
echo "::set-output name=version::${version_out}"
- name: Wait for VM instance
run: sleep 60s
shell: bash
- name: Wait for pods
run: |
kubectl wait --for=condition=ready pod -l load=true --timeout=10m || true
- name: Deploy Jmeter container
run: |
wget https://raw.githubusercontent.com/ballerina-platform/ballerina-performance-cloud/main/base-image/k8s.yaml
sed -i -e 's/<<CLUSTER_IP>>/${{ steps.write.outputs.cluster-ip }}/g' k8s.yaml
sed -i -e 's/<<REPO_NAME>>/${{ github.event.inputs.repo-name }}/g' k8s.yaml
sed -i -e 's/<<SCENARIO_NAME>>/${{ steps.write.outputs.scenario-name }}/g' k8s.yaml
sed -i -e 's/<<GITHUB_TOKEN>>/${{steps.write.outputs.git-token}}/g' k8s.yaml
sed -i -e 's/<<PAYLOAD_SIZE>>/${{ matrix.payload }}/g' k8s.yaml
sed -i -e 's/<<SPACE_ID>>/${{steps.write.outputs.space-id}}/g' k8s.yaml
sed -i -e 's/<<MESSAGE_KEY>>/${{steps.write.outputs.message-key}}/g' k8s.yaml
sed -i -e 's/<<CHAT_TOKEN>>/${{steps.write.outputs.chat-token}}/g' k8s.yaml
sed -i -e 's/<<DISPATCH_TYPE>>/${{steps.write.outputs.dispatch-type}}/g' k8s.yaml
sed -i -e 's/<<BRANCH_NAME>>/${{ needs.setup.outputs.branch-name }}/g' k8s.yaml
sed -i -e 's/<<CONCURRENT_USERS>>/${{ matrix.users }}/g' k8s.yaml
sed -i -e 's/<<VERSION>>/${{ steps.write.outputs.version }}/g' k8s.yaml
sed -i -e 's/<<BASE_BRANCH>>/${{ github.event.inputs.branch }}/g' k8s.yaml
kubectl apply -f k8s.yaml
echo "Waiting till the Jmeter job to finish"
attempt_num=1
until kubectl wait --for=condition=complete --timeout=2h job/jmeter-job
do
if ((attempt_num==3))
then
echo "Job waiting failed on the $attempt_num attempt and there are no more attempts left! Failing the workflow"
kubectl get jobs
kubectl get pods
kubectl logs job.batch/jmeter-job
exit 1
else
echo "Attempt $attempt_num failed! Trying again in 30 seconds..."
((attempt_num++))
sleep 30
kubectl get jobs
kubectl get pods
kubectl logs job.batch/jmeter-job
fi
done
kubectl logs job.batch/jmeter-job
- name: Pod logs
if: always()
run: |
kubectl logs -l logs=true
- name: Undeploy Kubernetes artifacts
if: always()
run: |
kubectl delete -f ${TEST_ROOT}/${TEST_NAME}/final.yaml
kubectl delete -f k8s.yaml
cleanup:
needs: [setup,run_test]
name: clean up
if: always()
runs-on: ubuntu-latest
steps:
- name: AZURE LOGIN
uses: azure/login@v1
with:
creds: ${{secrets.AZURE_CREDENTIALS}}
- name: Configure AKS
uses: azure/aks-set-context@v1
with:
creds: '${{ secrets.AZURE_CREDENTIALS }}'
cluster-name: '${{ github.event.inputs.clusterName }}'
resource-group: ${{ secrets.CLUSTER_RESOURCE_GROUP }}
- name: Deploy data collector pod
if: github.event.inputs.dispatchType != ''
shell: bash
run: |
kubectl apply -f https://raw.githubusercontent.com/ballerina-platform/ballerina-performance-cloud/main/base-image/notification.yaml
kubectl wait --for=condition=Ready pod/dataaccess
kubectl cp dataaccess:/data data/
DISPATCH_TYPE="${{ github.event.inputs.dispatchType }}"
BRANCH="${{ github.event.inputs.branch }}"
pushd data
if [ -z "$(ls -A ./)" ]; then
CONTENT=$(echo '{ "results": [] }' | jq )
else
CONTENT=$(for SCENARIO_NAME in *; do
STATUS="success"
SUMMARY_STRING=$(cat $SCENARIO_NAME/summary.csv)
ERROR_RATE=$(echo $SUMMARY_STRING | cut -d ',' -f10)
ERROR_RATE=$(echo $ERROR_RATE | sed 's/%//')
if [[ -z $BRANCH ]]; then
DATA_STRING=$( jq -n \
--arg status "$STATUS" \
--arg summary "$SUMMARY_STRING" \
--arg errorRate "$ERROR_RATE" \
--arg scenarioName "${SCENARIO_NAME}" \
'{ "name": $scenarioName, "status": $status, "result": $summary, "errorRate": $errorRate}' )
else
DATA_STRING=$( jq -n \
--arg status "$STATUS" \
--arg summary "$SUMMARY_STRING" \
--arg errorRate "$ERROR_RATE" \
--arg scenarioName "${SCENARIO_NAME}" \
--arg branch "${BRANCH}" \
'{ "name": $scenarioName, "status": $status, "branch": $branch,"result": $summary, "errorRate": $errorRate}' )
fi
echo $DATA_STRING
done | jq -n '.results |= [inputs]')
fi
echo "Complete scenario list from the inputs"
COMPLETE_LIST='${{ needs.setup.outputs.matrix }}'
echo $COMPLETE_LIST
REMAINING_LIST=$COMPLETE_LIST
for SCENARIO_NAME in *; do
REMAINING_LIST=$(echo $REMAINING_LIST | jq --arg SCENARIO $SCENARIO_NAME 'del(.[] | select(. == $SCENARIO))')
done
echo "Remaining scenario list after removing successful runs"
echo $REMAINING_LIST
while read i; do
SCENARIO=$(echo $i | tr -d '"')
CONTENT=$(echo $CONTENT | jq --arg SCENARIO $SCENARIO '.results += [{"name":$SCENARIO,"status": "failed"}]')
done <<<$(echo $REMAINING_LIST | jq -c '.[]')
echo $CONTENT
FINAL_PAYLOAD=$( jq -n \
--argjson content "${CONTENT}" \
--arg eventType "${DISPATCH_TYPE}" \
'{"event_type": $eventType, "client_payload": $content }' )
echo $FINAL_PAYLOAD
curl -X POST \
-H "Accept: application/vnd.github.v3+json" \
-H "Authorization: token ${{ secrets.BALLERINA_BOT_TOKEN }}" \
--data "$FINAL_PAYLOAD" \
"https://api.github.com/repos/ballerina-platform/${{ github.event.inputs.repo-name }}/dispatches"
popd
- name: Cleaning up the cluster
if: always()
uses: azure/CLI@v1
with:
azcliversion: "agentazcliversion"
inlineScript: |
az group delete --name mc_${{ secrets.CLUSTER_RESOURCE_GROUP }}_${{ github.event.inputs.clusterName }}_eastus -y
az aks delete --name ${{ github.event.inputs.clusterName }} --resource-group ${{ secrets.CLUSTER_RESOURCE_GROUP }} -y