Skip to content

Openstack COS: Issue metrics even if runner creation fails #134

Openstack COS: Issue metrics even if runner creation fails

Openstack COS: Issue metrics even if runner creation fails #134

name: End-to-End Openstack Test
on:
pull_request:
jobs:
build-charm:
name: Build Charm
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Remove Unnecessary Components
run: |
rm -rf .git
rm -rf .github
- name: Write lxd-profile.yaml
run: |
cat << EOF > ./lxd-profile.yaml
config:
security.nesting: true
security.privileged: true
raw.lxc: |
lxc.apparmor.profile=unconfined
lxc.mount.auto=proc:rw sys:rw cgroup:rw
lxc.cgroup.devices.allow=a
lxc.cap.drop=
devices:
kmsg:
path: /dev/kmsg
source: /dev/kmsg
type: unix-char
EOF
- name: Cache github-runner Charm
uses: actions/cache@v4
id: cache-charm
with:
path: github-runner_ubuntu-22.04-amd64.charm
key: github-runner-charm-${{ hashFiles('**/*') }}
- name: Setup LXD
if: steps.cache-charm.outputs.cache-hit != 'true'
uses: canonical/setup-lxd@main
- name: Install charmcraft
if: steps.cache-charm.outputs.cache-hit != 'true'
run: sudo snap install charmcraft --classic
- name: Pack github-runner Charm
if: steps.cache-charm.outputs.cache-hit != 'true'
run: charmcraft pack || ( cat ~/.local/state/charmcraft/log/* && exit 1 )
- name: Upload github-runner Charm
uses: actions/upload-artifact@v4
with:
name: dangerous-test-only-github-runner_ubuntu-22.04-amd64.charm
path: github-runner_ubuntu-22.04-amd64.charm
run-id:
name: Generate Run ID
runs-on: ubuntu-latest
outputs:
run-id: ${{ steps.run-id.outputs.run-id }}
steps:
- name: Generate Run ID
id: run-id
run: |
echo "run-id=e2e-$(LC_ALL=C tr -dc 'a-z' < /dev/urandom | head -c4)" >> $GITHUB_OUTPUT
deploy-e2e-test-runner:
name: Deploy End-to-End Test OpenStack Runner (${{ matrix.event.name }})
runs-on: ["self-hosted", "xlarge", "x64"]
needs: [build-charm, run-id]
strategy:
matrix:
event:
- name: pull_request
abbreviation: pr
- name: workflow_dispatch
abbreviation: wd
- name: push
abbreviation: push
- name: schedule
abbreviation: sd
- name: issues
abbreviation: is
steps:
- name: Install GitHub Cli
run: which gh || sudo apt install gh -y
- name: Check rate limit
env:
GH_TOKEN: ${{ (matrix.event.name == 'issues' || matrix.event.name == 'schedule') && secrets.E2E_TESTING_TOKEN || secrets.GITHUB_TOKEN }}
run: |
# Check rate limit, this check does not count against the primary rate limit:
# https://docs.github.com/en/rest/using-the-rest-api/rate-limits-for-the-rest-api?apiVersion=2022-11-28#checking-the-status-of-your-rate-limit
gh api \
--method GET \
-H "Accept: application/vnd.github+json" \
-H "X-GitHub-Api-Version: 2022-11-28" --jq ".resources.core" \
/rate_limit
- name: Setup Lxd Juju Controller
uses: charmed-kubernetes/actions-operator@main
with:
juju-channel: 3.2/stable
provider: microk8s
microk8s-addons: "dns ingress hostpath-storage"
channel: 1.26-strict/stable
- uses: actions/[email protected]
- name: Setup microstack
run: bash -xe scripts/setup-microstack.sh
- name: Create Testing Juju Model
run: juju add-model testing
- name: Set Testing Model Proxy Configuration
run: |
juju model-config juju-http-proxy=$http_proxy
juju model-config juju-https-proxy=$https_proxy
juju model-config juju-no-proxy=$no_proxy
- name: Change Testing Model Logging Level
run: juju model-config logging-config="<root>=INFO;unit=DEBUG"
- name: Download github-runner Charm
uses: actions/download-artifact@v4
with:
name: dangerous-test-only-github-runner_ubuntu-22.04-amd64.charm
- name: Copy github-runner Charm
run: |
cp github-runner_ubuntu-22.04-amd64.charm /home/$USER/github-runner_ubuntu-22.04-amd64.charm
- name: Generate Runner Name
id: runner-name
run: echo name=${{ matrix.event.abbreviation }}-${{ needs.run-id.outputs.run-id }}${{ github.run_attempt }} >> $GITHUB_OUTPUT
- name: Create Runner OpenStack Flavor
run: |
OS_CLIENT_CONFIG_FILE="clouds.yaml" openstack --os-cloud sunbeam flavor create runner --ram 16384 --disk 20 --vcpus 16
- name: Deploy github-runner Charm (Pull Request, Workflow Dispatch and Push)
if: matrix.event.name == 'workflow_dispatch' || matrix.event.name == 'push' || matrix.event.name == 'pull_request'
run: |
CLOUDS_YAML="`cat clouds.yaml`"
juju deploy /home/$USER/github-runner_ubuntu-22.04-amd64.charm \
${{ steps.runner-name.outputs.name }} \
--base [email protected] \
--config path=${{ secrets.E2E_TESTING_REPO }} \
--config token=${{ secrets.E2E_TESTING_TOKEN }} \
--config virtual-machines=1 \
--config test-mode=insecure \
--config experimental-openstack-clouds-yaml="$CLOUDS_YAML" \
--config experimental-openstack-network=demo-network \
--config experimental-openstack-flavor=runner
- name: Checkout branch (Issues, Schedule)
if: matrix.event.name == 'issues' || matrix.event.name == 'schedule'
uses: actions/checkout@v4
with:
ref: ${{ github.head_ref }}
token: ${{ secrets.E2E_TESTING_TOKEN }}
- name: Create temporary orphan branch (Issues, Schedule)
if: matrix.event.name == 'issues' || matrix.event.name == 'schedule'
run: |
# We dont need all content for the test, so create an orphan branch.
git checkout --orphan ${{ steps.runner-name.outputs.name }}
git reset
WF_FILE=".github/workflows/schedule_issues_test.yaml"
# Replace workflow event in schedule_issues_test.yaml
if [[ ${{ matrix.event.name }} == 'schedule' ]]; then
sed -i "s/workflow_dispatch:/schedule:\n - cron: '*\/5 * * * *'/" $WF_FILE
else
sed -i "s/workflow_dispatch:/issues:\n types: [opened]/" $WF_FILE
fi
git add $WF_FILE
git config user.name github-actions
git config user.email [email protected]
git commit -m"Add ${{matrix.event.name}} workflow"
git push origin ${{ steps.runner-name.outputs.name }}
- name: Deploy github-runner Charm (Issues, Schedule)
if: matrix.event.name == 'issues' || matrix.event.name == 'schedule'
env:
GH_TOKEN: ${{ secrets.E2E_TESTING_TOKEN }}
run: |
# GitHub does not allow to create multiple forks of the same repo under the same user,
# so we need to create a new repository and push the branch to it.
gh api \
--method POST \
-H "Accept: application/vnd.github+json" \
-H "X-GitHub-Api-Version: 2022-11-28" \
/user/repos \
-f name=${{ steps.runner-name.outputs.name }}
TESTING_REPO=${{ secrets.E2E_TESTING_TOKEN_ORG }}/${{ steps.runner-name.outputs.name }}
# Create registration token in order to allow listing of runner binaries
gh api \
--method POST \
-H "Accept: application/vnd.github+json" \
-H "X-GitHub-Api-Version: 2022-11-28" \
repos/${TESTING_REPO}/actions/runners/registration-token
# Push the orphan branch to the newly created repo.
git pull origin ${{ steps.runner-name.outputs.name }}
git remote add testing https://github.com/${TESTING_REPO}.git
git push testing ${{ steps.runner-name.outputs.name }}:main
juju deploy /home/$USER/github-runner_ubuntu-22.04-amd64.charm \
${{ steps.runner-name.outputs.name }}
--config path=$TESTING_REPO \
--config token=${{ secrets.E2E_TESTING_TOKEN }} \
--config virtual-machines=1 \
--config test-mode=insecure \
--config experimental-openstack-clouds-yaml="$CLOUDS_YAML" \
--config experimental-openstack-network=demo-network \
--config experimental-openstack-flavor=runner
- name: Watch github-runner (Pull Request)
if: matrix.event.name == 'pull_request'
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
timeout-minutes: 30
run: |
juju debug-log --replay --tail &
while :; do
JOBS=$(gh api \
-H "Accept: application/vnd.github+json" \
-H "X-GitHub-Api-Version: 2022-11-28" \
/repos/${{ secrets.E2E_TESTING_REPO }}/actions/runs/$GITHUB_RUN_ID/attempts/$GITHUB_RUN_ATTEMPT/jobs)
CONCLUSION=$(echo $JOBS | jq -r '.jobs[] | select(.name == "End-to-End Test / End-to-End Test Run") | .conclusion')
STATUS=$(echo $JOBS | jq -r '.jobs[] | select(.name == "End-to-End Test / End-to-End Test Run") | .status')
if [[ $STATUS != "queued" && $STATUS != "in_progress" ]]; then
break
fi
sleep 10
done
if [[ $STATUS != "completed" || $CONCLUSION != "success" ]]; then
echo "test workflow failed with status: $STATUS, conclusion: $CONCLUSION"
kill $(jobs -p)
exit 1
fi
- name: Trigger workflow (Workflow Dispatch and Push)
if: matrix.event.name == 'workflow_dispatch' || matrix.event.name == 'push'
env:
# push requires E2E_TESTING_TOKEN, because if GITHUB_TOKEN is used, no workflow is triggered for a push:
# https://docs.github.com/en/actions/using-workflows/triggering-a-workflow#triggering-a-workflow-from-a-workflow
GH_TOKEN: ${{ matrix.event.name == 'workflow_dispatch' && secrets.GITHUB_TOKEN || secrets.E2E_TESTING_TOKEN }}
run: |
# Base any future branches on the current branch
REF_SHA=$(gh api \
-H "Accept: application/vnd.github+json" \
-H "X-GitHub-Api-Version: 2022-11-28" \
/repos/${{ secrets.E2E_TESTING_REPO }}/git/ref/heads/$GITHUB_HEAD_REF \
--jq .object.sha)
# Create a temporary reference/branch
# For push, this should trigger the "Push Event Tests" workflow automatically
# because the test is run for branches matching the pattern "push-e2e-*"
gh api \
--method POST \
-H "Accept: application/vnd.github+json" \
-H "X-GitHub-Api-Version: 2022-11-28" \
/repos/${{ secrets.E2E_TESTING_REPO }}/git/refs \
-f ref='refs/heads/${{ steps.runner-name.outputs.name }}' \
-f sha=$REF_SHA
# For workflow_dispatch, we need to trigger the "Workflow Dispatch Tests" workflow manually
if ${{ matrix.event.name == 'workflow_dispatch' }}; then
gh workflow run workflow_dispatch_test.yaml \
-R ${{ secrets.E2E_TESTING_REPO }} \
--ref ${{ steps.runner-name.outputs.name }} \
-f runner=${{ steps.runner-name.outputs.name }}
fi
- name: Watch github-runner (Workflow Dispatch and Push)
if: matrix.event.name == 'workflow_dispatch' || matrix.event.name == 'push'
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
juju debug-log --replay --tail &
get-workflow-status() {
# Search recent workflow runs for the one designated by the run-id ref
output=$(gh run list \
-R ${{ secrets.E2E_TESTING_REPO }} \
-L 100 \
--json headBranch,status \
--jq '[.[] | select(.headBranch=="${{ steps.runner-name.outputs.name }}")]')
# Workflows that have not started have no status
if [ $(echo "$output" | jq 'length') -eq 0 ]
then
echo "not_started"
else
# Parse output with jq to get the status field of the first object
status=$(echo "$output" | jq -r '.[0].status')
echo "$status"
fi
}
# Wait for the workflow to start while checking its status
for i in {1..360}
do
status=$(get-workflow-status)
echo "workflow status: $status"
if [[ $status != "not_started" && $status != "queued" && $status != "in_progress" ]]; then
break
fi
sleep 10
done
# Make sure the workflow was completed or else consider it failed
conclusion=$(gh run list \
-R ${{ secrets.E2E_TESTING_REPO }} \
-L 100 \
--json headBranch,conclusion \
--jq '.[] | select(.headBranch=="${{ steps.runner-name.outputs.name }}") | .conclusion')
if [[ $status != "completed" || $conclusion != "success" ]]; then
echo "test workflow failed with status: $status, conclusion: $conclusion"
kill $(jobs -p)
exit 1
else
echo "Workflow completed with status: $status, conclusion: $conclusion, run-id: ${{ steps.runner-name.outputs.name }}"
kill $(jobs -p)
fi
- name: Trigger workflow and watch github-runner (Issues, Schedule)
if: matrix.event.name == 'issues' || matrix.event.name == 'schedule'
env:
GH_TOKEN: ${{ secrets.E2E_TESTING_TOKEN }}
run: |
juju debug-log --replay --tail &
TESTING_REPO=${{ secrets.E2E_TESTING_TOKEN_ORG }}/${{ steps.runner-name.outputs.name }}
# For issues, we need to trigger the workflow by opening an issue
if ${{ matrix.event.name == 'issues' }}; then
gh api \
--method POST \
-H "Accept: application/vnd.github+json" \
-H "X-GitHub-Api-Version: 2022-11-28" \
/repos/${TESTING_REPO}/issues \
-f title="Test issue ${{ steps.runner-name.outputs.name }}"
fi
get-workflow-status() {
# Search recent workflow runs for the one designated by the run-id ref
output=$(gh run list \
-R ${TESTING_REPO} \
-L 100 \
--json headBranch,status,createdAt \
--jq '[.[] | select(.headBranch=="main")] | sort_by(.createdAt)')
# Workflows that have not started have no status
if [ $(echo "$output" | jq 'length') -eq 0 ]
then
echo "not_started"
else
# Parse output with jq to get the status field of the first object
status=$(echo "$output" | jq -r '.[0].status')
echo "$status"
fi
}
# Wait for the workflow to start while checking its status
for i in {1..360}
do
status=$(get-workflow-status)
echo "workflow status: $status"
if [[ $status != "not_started" && $status != "queued" && $status != "in_progress" ]]; then
break
fi
sleep 10
done
# Make sure the workflow was completed or else consider it failed
runs=$(gh api \
-H "Accept: application/vnd.github+json" \
-H "X-GitHub-Api-Version: 2022-11-28" \
/repos/${TESTING_REPO}/actions/runs \
--jq '[.workflow_runs[] | select(.head_branch=="main")] | sort_by(.created_at)')
conclusion=$(echo $runs | jq -r '.[0].conclusion')
wf_run_id=$(echo $runs | jq -r '.[0].id')
logs_filename=${{matrix.event.name}}-workflow-logs.zip
# We retrieve the logs because the testing repo is deleted at the end of the test
gh api \
-H "Accept: application/vnd.github+json" \
-H "X-GitHub-Api-Version: 2022-11-28" \
/repos/${TESTING_REPO}/actions/runs/${wf_run_id}/logs > ${logs_filename} \
|| (echo "Failed to retrieve logs from schedule tests" && rm ${logs_filename})
if [[ $status != "completed" || $conclusion != "success" ]]; then
echo "test workflow failed with status: $status, conclusion: $conclusion"
kill $(jobs -p)
exit 1
else
echo "Workflow completed with status: $status, conclusion: $conclusion, run-id: ${{ steps.runner-name.outputs.name }}"
kill $(jobs -p)
fi
- name: Upload test logs (Issues, Schedule)
if: always() && (matrix.event.name == 'issues' || matrix.event.name == 'schedule')
uses: actions/upload-artifact@v4
with:
name: ${{matrix.event.name}}-workflow-logs.zip
path: ${{matrix.event.name}}-workflow-logs.zip
if-no-files-found: ignore
- name: Show Firewall Rules
run: |
juju ssh ${{ steps.runner-name.outputs.name }}/0 sudo nft list ruleset
- name: Clean Up (Workflow Dispatch and Push)
if: always() && (matrix.event.name == 'workflow_dispatch' || matrix.event.name == 'push')
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
gh api \
--method DELETE \
-H "Accept: application/vnd.github+json" \
-H "X-GitHub-Api-Version: 2022-11-28" \
"/repos/${{ secrets.E2E_TESTING_REPO }}/git/refs/heads/${{ steps.runner-name.outputs.name }}"
echo "Deleted ref ${{ steps.runner-name.outputs.name }}"
- name: Clean Up (Issues, Schedule)
if: always() && (matrix.event.name == 'issues' || matrix.event.name == 'schedule')
env:
GH_TOKEN: ${{ secrets.E2E_TESTING_TOKEN }}
run: |
set +e
gh api \
--method DELETE \
-H "Accept: application/vnd.github+json" \
-H "X-GitHub-Api-Version: 2022-11-28" \
"/repos/${{ secrets.E2E_TESTING_REPO }}/git/refs/heads/${{ steps.runner-name.outputs.name }}" \
&& echo "Deleted ref ${{ steps.runner-name.outputs.name }}"
TESTING_REPO=${{ secrets.E2E_TESTING_TOKEN_ORG }}/${{ steps.runner-name.outputs.name }}
set -e
gh api \
--method DELETE \
-H "Accept: application/vnd.github+json" \
-H "X-GitHub-Api-Version: 2022-11-28" \
"/repos/${TESTING_REPO}"
echo "Deleted repo ${TESTING_REPO}"
e2e-test:
name: End-to-End Test
needs: [build-charm, run-id]
uses: ./.github/workflows/e2e_test_run.yaml
with:
runner-tag: "pr-${{ needs.run-id.outputs.run-id }}${{ github.run_attempt}}"