Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add test orchestrator script to trigger remote workflow runner outside functional test repo for cypress tests #971

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 36 additions & 0 deletions .github/workflows/remote-cypress-workflow.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
name: Trigger Remote Cypress Workflow

on:
workflow_dispatch:
inputs:
repo:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure, we can take input in url format but then we will have to extract the owner and repo name from it as the workflow dispatch API expects it in that format https://docs.github.com/en/rest/actions/workflow-runs?apiVersion=2022-11-28#get-a-workflow-run.

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
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not opposed to passing this value but i think it can be optional. in case i wanted to setup a workflow that scales up a snapshot for cypress tests but then i can write my workflow so that if i do get release artifacts then i can utilize that to scale up

Copy link
Member Author

@manasvinibs manasvinibs Dec 7, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, that can be totally optional. Just to make it clear, this workflow yml file is different from the workflow yml file we will use in components repo. Here the input params are required because this gets triggered from release and hence it expects to pass release artifact to the test orchestrator script. But yes, within component repo workflow yml file, it can be optional as the same workflow can be used by plugins to scale up snapshot to run the tests against.

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 }}"

103 changes: 103 additions & 0 deletions poll_remote_workflow.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
#!/bin/bash

# Accessing the secret as an environment variable using GitHub actions while invoking this script
GITHUB_TOKEN=$GITHUB_SECRET_TOKEN
REPO="$1"
UNIQUE_WORKFLOW_ID="$2"
API_URL="$3"

# Function to check the status of the remote github 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" \
"$API_URL/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/$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"

job_details=$(curl -L -H "Authorization: Bearer $GITHUB_TOKEN" \
-H "Accept: application/vnd.github.v3+json" \
-H "X-GitHub-Api-Version: 2022-11-28" \
"$jobs_url")

# 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..."
return 1 # Failure
fi

echo "Remote workflow didn't complete within the specified time."
return 1 # Failure
}

check_remote_workflow_status

exit 0
113 changes: 113 additions & 0 deletions remoteCypress.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
#!/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 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 "--------------------------------------------------------------------------"
}

# Parse command-line arguments
while getopts ":h:r:w:o:d:b:i:" opt; do
case $opt in
h)
usage
exit 1
;;
r)
REPO="$OPTARG"
;;
w)
WORKFLOW_NAME="$OPTARG"
;;
o)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do we think we have a pre-verification step? don't dispatch jobs. if OpenSearch and OpenSearch Dashboards can't start

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Currently, I'm dispatching workflow event and not really dispatching jobs which are part of the workflow. I believe Opensearch and Opensearch Dashboards start setup script is part of the workflow yaml. I think if we have it as a jobs and if they cannot start which fails the job, it will terminate the orchestrator script as it is. But let me try to see if there is way to do pre-verification as you are suggesting in the follow-up iteration.

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 "$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"
# For now we are using Github action API to trigger github workflows in plugins component
# ToDo: We can explore other test runners such as Jenkins to integrate with.
API_URL="https://api.github.com/repos/$REPO/actions/workflows/$WORKFLOW_NAME"
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
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is the automatic? or this the amount of triggered from the UI?

if so i think it should just fail fast.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is for every run of the script, it triggers max 3 times the dispatch event API. Are you suggesting we can fail at first try if dispatch API errors out?


# 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/dispatches" -d "$PAYLOAD"
}

echo "Triggering the remote GitHub workflow for Cypress tests in the repository: $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


# Check the status of the remote workflow
source ./poll_remote_workflow.sh "$REPO" "$UNIQUE_WORKFLOW_ID" "$API_URL"
echo "Return code: $?"

exit 0
Loading