Stdlib Workflow #24838
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |