-
Notifications
You must be signed in to change notification settings - Fork 113
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
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 }}" | ||
|
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 |
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) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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. There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
should we follow the format of the current manifest?: https://github.com/opensearch-project/opensearch-build/blob/537ccdbfc23be07e6de08236dce95906ea5b6719/manifests/2.10.0/opensearch-2.10.0.yml#L18 it passes by url
There was a problem hiding this comment.
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
andrepo
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.