From 65636fda041389ad90ec8229060a54192c303fe5 Mon Sep 17 00:00:00 2001 From: manasvinibs Date: Mon, 6 Nov 2023 04:05:34 +0000 Subject: [PATCH] Add test orchestrator script to trigger remote workflow runner outside functional test repo for cypress tests Issue details - https://github.com/opensearch-project/OpenSearch-Dashboards/issues/5392 Signed-off-by: manasvinibs --- .github/workflows/remote-cypress-workflow.yml | 36 ++++ remoteCypress.sh | 203 ++++++++++++++++++ 2 files changed, 239 insertions(+) create mode 100644 .github/workflows/remote-cypress-workflow.yml create mode 100755 remoteCypress.sh diff --git a/.github/workflows/remote-cypress-workflow.yml b/.github/workflows/remote-cypress-workflow.yml new file mode 100644 index 000000000..9e75fdef9 --- /dev/null +++ b/.github/workflows/remote-cypress-workflow.yml @@ -0,0 +1,36 @@ +name: Trigger Remote Cypress Workflow + +on: + workflow_dispatch: + inputs: + repo: + description: 'Name of the repository in {owner}/{repository} format' + required: true + workflow_name: + description: 'Name of the Github workflow yml file in the component repository' + required: true + os_url: + description: 'Release artifact of OpenSearch' + required: true + osd_url: + description: 'Release artifact of OpenSearch Dashboards' + required: true + branch_ref: + description: 'Test branch name or commit reference id' + required: true + +jobs: + trigger-cypress: + runs-on: ubuntu-latest + name: Remote Cypress Tests + + steps: + - name: Checkout code + uses: actions/checkout@v2 + + - name: Run Bash Script + env: + GITHUB_SECRET_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + ./remoteCypress.sh -r "${{ github.event.inputs.repo }}" -w "${{ github.event.inputs.workflow_name }}" -o "${{ github.event.inputs.os_url }}" -d "${{ github.event.inputs.osd_url }}" -b "${{ github.event.inputs.branch_ref }}" + diff --git a/remoteCypress.sh b/remoteCypress.sh new file mode 100755 index 000000000..4b03b78ed --- /dev/null +++ b/remoteCypress.sh @@ -0,0 +1,203 @@ +#!/bin/bash + +set -e + +function usage() { + echo "" + echo "This script triggers GitHub workflow runners within the component repository which runs Cypress integration tests on a remote OpenSearch/Dashboards cluster." + echo "--------------------------------------------------------------------------" + echo "Usage: $0 [args]" + echo "Required arguments:" + echo -e "-r REMOTE_REPO\t, Name of the repository in {owner}/{repository} format" + echo -e "-w GITHUB_WORKFLOW_NAME\t, Name of the GitHub workflow file name with .yml extension that contain jobs that run Cypress tests in the component repository. For example, main.yaml" + echo -e "-o OS_URL\t, Release artifact of the OpenSearch" + echo -e "-d OSD_URL\t, Release artifact of the OpenSearch Dashboards" + echo -e "-b BRANCH_REF\t Test Branch name or commit reference id" + echo -e "-i BUILD_ID\t Release-specific build id for reference" + echo "--------------------------------------------------------------------------" +} + +# Initialize variables to empty strings +REMOTE_REPO="" +WORKFLOW_NAME="" +OS_URL="" +OSD_URL="" +BRANCH_REF="" +BUILD_ID="" + +# Parse command-line arguments +while getopts ":h:r:w:o:d:b:i:" opt; do + case $opt in + h) + usage + exit 1 + ;; + r) + REMOTE_REPO="$OPTARG" + ;; + w) + WORKFLOW_NAME="$OPTARG" + ;; + o) + OS_URL="$OPTARG" + ;; + d) + OSD_URL="$OPTARG" + ;; + b) + BRANCH_REF="$OPTARG" + ;; + i) + BUILD_ID="$OPTARG" + ;; + \?) + echo "Invalid option: -$OPTARG" >&2 + exit 1 + ;; + :) + echo "Option -$OPTARG requires an argument." >&2 + exit 1 + ;; + esac +done + +# Check if required arguments are provided +if [[ -z "$REMOTE_REPO" || -z "$WORKFLOW_NAME" || -z "$OS_URL" || -z "$OSD_URL" || -z "$BRANCH_REF" ]]; then + echo "Error: Missing required arguments. See usage below." + usage + exit 1 +fi + +# Accessing the secret as an environment variable using Github actions while invoking this script +GITHUB_TOKEN=$GITHUB_SECRET_TOKEN +# This is to uniquely identify each execution workflow. This ID has to be appended to the workflow_run +# name in the component repository yaml file for polling purpose. +UNIQUE_WORKFLOW_ID=$(uuidgen) +echo "Unique Execution ID: $UNIQUE_WORKFLOW_ID" +API_URL="https://api.github.com/repos/$REMOTE_REPO/actions/workflows/$WORKFLOW_NAME/dispatches" +PAYLOAD="{\"ref\": \"$BRANCH_REF\",\"inputs\":{\"build_id\":\"$BUILD_ID\", \"OS_URL\":\"$OS_URL\", \"OSD_URL\":\"$OSD_URL\", \"UNIQUE_ID\":\"$UNIQUE_WORKFLOW_ID\"}}" + +# Maximum number of retries for triggering the remote runner +MAX_RETRIES=3 + +# Trigger the remote GitHub workflow using curl and the PAT token +trigger_remote_workflow() { + curl -L -X POST -H "Authorization: Bearer $GITHUB_TOKEN" \ + -H "Accept: application/vnd.github.v3+json" \ + -H "X-GitHub-Api-Version: 2022-11-28" \ + -w "%{http_code}" \ + "$API_URL" -d "$PAYLOAD" +} + +echo "Triggering the remote GitHub workflow for Cypress tests in the repository: $REMOTE_REPO" + +# Attempt to trigger the remote workflow with retries +for ((i = 1; i <= MAX_RETRIES; i++)); do + echo "Attempting to trigger the remote workflow (Attempt $i)" + status_code=$(trigger_remote_workflow) + echo "status_code: $status_code" + + if [[ $status_code -ge 200 && $status_code -lt 300 ]]; then + echo "Remote workflow triggered successfully." + break + else + echo "Failed to trigger the remote workflow. Retrying..." + sleep 10 # Adds a delay between retries + fi + + if [ $i -eq $MAX_RETRIES ]; then + echo "Maximum number of retries reached. Exiting." + exit 1 + fi +done + +# Function to check the status of the remote workflow by constantly polling the workflow-run +check_remote_workflow_status() { + local run_id + local status + local conclusion + local run_details + local workflow_runs + local matching_workflow + local polling_run_id_retries=1 + local polling_workflow_completion_retries=1 + local max_polling_run_id_retries=5 # Keep polling for the first 5 minutes to fetch the workflow run id till the workflow gets generated + local max_polling_workflow_completion_retries=12 # Set the polling window period to be 1 hour + + # Check if a matching workflow object was found + while [ -z "$matching_workflow" ] && [ $polling_run_id_retries -le $max_polling_run_id_retries ]; do + echo "Querying for the workflow run id..." + sleep 60 # Wait for 1 minute before polling for the workflow run_id till it gets created + + # Make a GET request to the GitHub API to get the list of workflow runs + workflow_runs=$(curl -s -H "Authorization: Bearer $GITHUB_TOKEN" \ + -H "Accept: application/vnd.github.v3+json" \ + -H "X-GitHub-Api-Version: 2022-11-28" \ + "https://api.github.com/repos/$REMOTE_REPO/actions/workflows/$WORKFLOW_NAME/runs") + + # Extract the JSON object whose "name" field contains the string with a unique id of the workflow + matching_workflow=$(echo "$workflow_runs" | jq --arg unique_id "$UNIQUE_WORKFLOW_ID" '.workflow_runs[] | select(.name | contains($unique_id))') + ((polling_run_id_retries++)) + + done + echo "matching_workflow: $matching_workflow" + + if [ -n "$matching_workflow" ]; then + # Extract the "jobs_url" and "run_id" values from the matching object + jobs_url=$(echo "$matching_workflow" | jq -r '.jobs_url') + run_id=$(echo "$matching_workflow" | jq -r '.id') + echo "Jobs URL: $jobs_url" + echo "Run Id: $run_id" + + # Poll the status until the workflow is completed + while [ $polling_workflow_completion_retries -le $max_polling_workflow_completion_retries ]; do + echo "Checking the workflow run API status, attempt: $polling_workflow_completion_retries" + + run_details=$(curl -L -H "Authorization: Bearer $GITHUB_TOKEN" \ + -H "Accept: application/vnd.github.v3+json" \ + -H "X-GitHub-Api-Version: 2022-11-28" \ + "https://api.github.com/repos/$REMOTE_REPO/actions/runs/$run_id") + echo "Workflow run details: $run_details" + + # Extract status and conclusion from the run details + status=$(echo "$run_details" | jq -r ".status") + conclusion=$(echo "$run_details" | jq -r ".conclusion") + + # Check if the status indicates that the workflow is complete + if [[ "$status" == "completed" ]]; then + echo "Workflow completed with status: $status" + + # Check if it was successful + if [[ $conclusion == "success" ]]; then + echo "Remote workflow completed successfully." + return 0 # Success + elif [[ $conclusion == "failure" ]]; then + echo "Remote workflow completed with errors. Conclusion: $conclusion" + + # Parse the workflow to find any failures in the test + failures=$(echo "$run_details" | jq -r '.jobs[] | select(.conclusion == "failure") | .name') + echo "Test failures: $failures" + + return 1 # Failure + else + echo "Remote workflow completed with unexpected conclusion. Conclusion: $conclusion" + return 1 # Failure + fi + else + echo "Remote workflow is still running. Waiting..." + sleep 300 # Wait for 5 minutes before checking again + ((polling_workflow_completion_retries++)) + fi + done + else + echo "No matching workflow run object found even after retries. Exiting..." + fi + + echo "Remote workflow didn't complete within the specified time." + return 1 # Failure +} + +# Check the status of the remote workflow +check_remote_workflow_status + +exit 0