openBalena 2024 #62
Workflow file for this run
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: deploy openBalena | |
on: | |
pull_request: | |
types: [opened, synchronize, closed] | |
branches: | |
- master | |
env: | |
# nested virtualisation not supported on AWS/EC2 instance types|classes other than X.metal | |
AWS_EC2_INSTANCE_TYPE: c6a.xlarge | |
AWS_EC2_LAUNCH_TEMPLATE: lt-011a66522ae6c5754 | |
AWS_EC2_LT_VERSION: 6 | |
AWS_IAM_USERNAME: balena-tests-iam-User-1GXO3XP12N6LL | |
AWS_REGION: us-east-1 | |
AWS_VPC_SECURITY_GROUP_IDS: sg-093fa6ade710210ab | |
AWS_VPC_SUBNET_ID: subnet-02d18a08ea4058574 | |
BALENA_CLI_URL: https://github.com/balena-io/balena-cli/releases/download | |
BALENA_CLI_VERSION: 13.10.0 | |
# https://github.com/balena-io/balena-cli/issues/2447 | |
DEBUG: 0 | |
DEVICE_PROXY_TLD: balena-devices.com | |
DEVICE_TYPE: genericx86-64-ext | |
# https://dash.cloudflare.com/001b3ed2352612aaa068aca1b0022736/balena-devices.com/dns | |
# https://github.com/balena-io/autohat/blob/master/resources/qemu.robot#L23 | |
# https://github.com/balena-io-playground/balena-nested/blob/ab77/open-balena/guests.yml#L29-L32 | |
DNS_TLD: auto.balena-devices.com | |
ENVIRONMENT: balena-cloud.com | |
FLEET: balena/open-balena | |
OPENBALENA_TESTS_SERVICE: balena-tests | |
REGISTRY_USER: balenaci | |
RELEASES: 50 | |
RETRY: 3 | |
SOCAT_VERSION: 1.7.4.3 | |
VARIANT: prod | |
VERBOSE: 'true' | |
jobs: | |
build-test-deploy: | |
runs-on: ubuntu-latest | |
timeout-minutes: 120 | |
strategy: | |
fail-fast: true | |
steps: | |
- uses: actions/checkout@v2 | |
with: | |
# FIXME: remove once balenaBlocks/balenaVirt is a thing | |
submodules: true | |
# https://github.com/pdcastro/ssh-uuid#why | |
# https://github.com/pdcastro/ssh-uuid#linux-debian-ubuntu-others | |
- name: install additional dependencies | |
id: extra-dependencies | |
run: | | |
set -ue | |
[[ '${{ env.VERBOSE }}' =~ on|On|Yes|yes|true|True ]] && set -x | |
pushd "${RUNNER_TEMP}" | |
sudo apt install -y \ | |
build-essential \ | |
git-secret \ | |
libreadline-dev \ | |
libssl-dev \ | |
libwrap0-dev \ | |
ssh | |
release_zip="balena-cli-v${BALENA_CLI_VERSION}-linux-x64-standalone.zip" | |
wget -q "${BALENA_CLI_URL}/v${BALENA_CLI_VERSION}/${release_zip}" \ | |
&& unzip -q "${release_zip}" -d "${RUNNER_TEMP}" | |
"${RUNNER_TEMP}/balena-cli/balena" version | |
"${RUNNER_TEMP}/balena-cli/balena" login --token '${{ secrets.BALENA_API_KEY_TEST }}' | |
"${RUNNER_TEMP}/balena-cli/balena" whoami | |
echo "${RUNNER_TEMP}/balena-cli" >> $GITHUB_PATH | |
mkdir -p "${RUNNER_TEMP}/ssh-uuid" | |
wget -q -O "${RUNNER_TEMP}/ssh-uuid/ssh-uuid" https://raw.githubusercontent.com/pdcastro/ssh-uuid/master/ssh-uuid.sh \ | |
&& chmod +x "${RUNNER_TEMP}/ssh-uuid/ssh-uuid" \ | |
&& ln -s "${RUNNER_TEMP}/ssh-uuid/ssh-uuid" "${RUNNER_TEMP}/ssh-uuid/scp-uuid" | |
"${RUNNER_TEMP}/ssh-uuid/scp-uuid" --help | |
echo "${RUNNER_TEMP}/ssh-uuid" >> $GITHUB_PATH | |
curl --silent --retry ${{ env.RETRY }} --fail \ | |
http://www.dest-unreach.org/socat/download/socat-${SOCAT_VERSION}.tar.gz | tar -xzvf - \ | |
&& cd socat-${SOCAT_VERSION} \ | |
&& ./configure \ | |
&& make \ | |
&& sudo make install | |
socat -V | |
popd | |
- name: push draft or finalise release | |
timeout-minutes: 60 | |
id: push-release | |
uses: balena-io/deploy-to-balena-action@master | |
with: | |
balena_token: ${{ secrets.BALENA_API_KEY_PUSH }} | |
cache: false | |
environment: ${{ env.ENVIRONMENT }} | |
fleet: ${{ env.FLEET }} | |
github_token: ${{ secrets.GITHUB_TOKEN }} | |
# FIXME: remove once balenaBlocks/balenaVirt is a thing | |
versionbot: false | |
registry_secrets: | | |
{ | |
"https://index.docker.io/v2/": { | |
"username": "${{ env.REGISTRY_USER }}", | |
"password": "${{ secrets.REGISTRY_PASS }}" | |
} | |
} | |
- name: (pre)register test device | |
id: register-test-device | |
if: ${{ github.event_name == 'pull_request' && github.event.action != 'closed'}} | |
run: | | |
set -ue | |
[[ '${{ env.VERBOSE }}' =~ on|On|Yes|yes|true|True ]] && set -x | |
balena login --token '${{ secrets.BALENA_API_KEY_TEST }}' | |
balena_device_uuid="$(openssl rand -hex 16)" | |
# https://www.balena.io/docs/learn/more/masterclasses/advanced-cli/#52-preregistering-a-device | |
balena device register '${{ env.FLEET }}' --uuid "${balena_device_uuid}" | |
device_id="$(balena device "${balena_device_uuid}" | grep ^ID: | cut -c20-)" | |
# the actual version deployed depends on the AWS EC2/AMI, defined in AWS_EC2_LAUNCH_TEMPLATE | |
os_version="$(balena os versions ${{ env.DEVICE_TYPE }} \ | |
| grep '${{ env.VARIANT }}' | head -n 1 | sed 's/.${{ env.VARIANT }}//g')" | |
balena config generate \ | |
--version "${os_version}" \ | |
--device ${balena_device_uuid} \ | |
--network ethernet \ | |
--appUpdatePollInterval 10 \ | |
--output config.json | |
balena tag set balena ephemeral-test-device --device "${balena_device_uuid}" | |
github_vars=(GITHUB_ACTOR GITHUB_BASE_REF GITHUB_HEAD_REF GITHUB_JOB \ | |
GITHUB_REF GITHUB_REF_NAME GITHUB_REF_TYPE GITHUB_REPOSITORY \ | |
GITHUB_REPOSITORY_OWNER GITHUB_RUN_ATTEMPT GITHUB_RUN_ID GITHUB_RUN_NUMBER \ | |
GITHUB_SHA GITHUB_WORKFLOW RUNNER_ARCH RUNNER_NAME RUNNER_OS) | |
for github_var in "${github_vars[@]}"; do | |
balena tag set ${github_var} "${!github_var}" --device "${balena_device_uuid}" | |
done | |
echo "::set-output name=balena_device_uuid::${balena_device_uuid}" | |
echo "::set-output name=balena_device_id::${device_id}" | |
# https://github.com/balena-io/balena-cli/issues/1543 | |
- name: pin device to draft release | |
id: pin-device | |
if: ${{ github.event_name == 'pull_request' && github.event.action != 'closed'}} | |
run: | | |
set -uae | |
[[ '${{ env.VERBOSE }}' =~ on|On|Yes|yes|true|True ]] && set -x | |
balena login --token '${{ secrets.BALENA_API_KEY_TEST }}' | |
balena_releases="$(mktemp)" | |
balena releases '${{ env.FLEET }}' | tail -n +2 | head -n ${{ env.RELEASES }} > "${balena_releases}" | |
# convert to JSON to find the correct draft release id and commit | |
release_id="$(while IFS=' ' read -r id commit created_at status semver is_final | |
do | |
printf '{"id":%s,"commit":"%s","created_at":"%s","status":"%s","semver":"%s","is_final":%s}\n' \ | |
"${id}" "${commit}" "${created_at}" "${status}" "${semver}" "${is_final}" | |
done < "${balena_releases}" | jq -s | jq -r '.[] | select((.id==${{ steps.push-release.outputs.release_id }}) and (.is_final==false) and (.status=="success")).id')" | |
commit="$(while IFS=' ' read -r id commit created_at status semver is_final | |
do | |
printf '{"id":%s,"commit":"%s","created_at":"%s","status":"%s","semver":"%s","is_final":%s}\n' \ | |
"${id}" "${commit}" "${created_at}" "${status}" "${semver}" "${is_final}" | |
done < "${balena_releases}" | jq -s | jq -r '.[] | select(.id==${{ steps.push-release.outputs.release_id }}).commit')" | |
if ! [ '${{ steps.register-test-device.outputs.balena_device_id }}' = '' ] \ | |
&& ! [ "${release_id}" = '' ] \ | |
&& ! [ "${commit}" = '' ]; then | |
# pin DUT to draft release | |
curl -X PATCH --silent --retry ${{ env.RETRY }} --fail -o /dev/null \ | |
'https://api.${{ env.ENVIRONMENT }}/v6/device?$filter=id%20in%20(${{ steps.register-test-device.outputs.balena_device_id }})' \ | |
-H 'authorization: Bearer ${{ secrets.BALENA_API_KEY_TEST }}' \ | |
-H 'content-type: application/json' \ | |
--data-raw "{\"should_be_running__release\":${release_id}}" \ | |
--compressed | |
fi | |
balena device ${{ steps.register-test-device.outputs.balena_device_uuid }} | |
app_id="$(balena fleet ${{ env.FLEET }} | grep ^ID: | cut -c14-)" | |
echo "::set-output name=balena_app_id::${app_id}" | |
- name: configure test device environment | |
id: configure-test-env | |
if: ${{ github.event_name == 'pull_request' && github.event.action != 'closed'}} | |
run: | | |
set -ue | |
[[ '${{ env.VERBOSE }}' =~ on|On|Yes|yes|true|True ]] && set -x | |
balena login --token '${{ secrets.BALENA_API_KEY_TEST }}' | |
balena env add VERBOSE '${{ env.VERBOSE }}' \ | |
--device '${{ steps.register-test-device.outputs.balena_device_uuid }}' | |
balena env add DNS_TLD '${{ env.DNS_TLD }}' \ | |
--device '${{ steps.register-test-device.outputs.balena_device_uuid }}' | |
balena env add MDNS_TLD '' \ | |
--service mdns \ | |
--device '${{ steps.register-test-device.outputs.balena_device_uuid }}' | |
balena env add DB_HOST db \ | |
--service api \ | |
--device '${{ steps.register-test-device.outputs.balena_device_uuid }}' | |
balena env add REDIS_HOST redis:6379 \ | |
--service api \ | |
--device '${{ steps.register-test-device.outputs.balena_device_uuid }}' | |
# to allow devices running locally to communicate to the local API, we can route | |
# to the local Docker network aliases instead of public DNS, since (a) DNS_TLD is | |
# guestfwd(ed) in QEMU to a special internal IP 10.0.2.100; (b) is proxied to | |
# haproxy network alias on device; and (c) made public with a wildcard DNS record | |
# (e.g.) | |
# | |
# $ dig +short api.auto.balena-devices.com | |
# 10.0.2.100 | |
# | |
balena env add API_HOST 'api.${{ env.DNS_TLD }}' \ | |
--device '${{ steps.register-test-device.outputs.balena_device_uuid }}' | |
# not used but required for config.json to be valid | |
balena env add DELTA_HOST 'delta.${{ env.DNS_TLD }}' \ | |
--device '${{ steps.register-test-device.outputs.balena_device_uuid }}' | |
balena env add REGISTRY2_HOST 'registry.${{ env.DNS_TLD }}' \ | |
--device '${{ steps.register-test-device.outputs.balena_device_uuid }}' | |
balena env add VPN_HOST 'vpn.${{ env.DNS_TLD }}' \ | |
--device '${{ steps.register-test-device.outputs.balena_device_uuid }}' | |
balena env add HOST 'api.${{ env.DNS_TLD }}' \ | |
--service api \ | |
--device '${{ steps.register-test-device.outputs.balena_device_uuid }}' | |
balena env add TOKEN_AUTH_CERT_ISSUER 'api.${{ env.DNS_TLD }}' \ | |
--service api \ | |
--device '${{ steps.register-test-device.outputs.balena_device_uuid }}' | |
balena env add REGISTRY2_TOKEN_AUTH_ISSUER 'api.${{ env.DNS_TLD }}' \ | |
--service registry \ | |
--device '${{ steps.register-test-device.outputs.balena_device_uuid }}' | |
balena env add REGISTRY2_TOKEN_AUTH_REALM 'https://api.${{ env.DNS_TLD }}/auth/v1/token' \ | |
--service registry \ | |
--device '${{ steps.register-test-device.outputs.balena_device_uuid }}' | |
balena env add REGISTRY2_S3_REGION_ENDPOINT 's3.${{ env.DNS_TLD }}' \ | |
--device '${{ steps.register-test-device.outputs.balena_device_uuid }}' | |
balena env add PRODUCTION_MODE false \ | |
--device '${{ steps.register-test-device.outputs.balena_device_uuid }}' | |
balena env add COMMON_REGION '${{ env.AWS_REGION }}' \ | |
--device '${{ steps.register-test-device.outputs.balena_device_uuid }}' | |
balena env add SUPERUSER_EMAIL 'admin@${{ env.DNS_TLD }}' \ | |
--device '${{ steps.register-test-device.outputs.balena_device_uuid }}' | |
balena env add ORG_UNIT openBalena \ | |
--device '${{ steps.register-test-device.outputs.balena_device_uuid }}' | |
- name: configure test device secrets | |
id: configure-test-secrets | |
if: ${{ github.event_name == 'pull_request' && github.event.action != 'closed'}} | |
run: | | |
set -ue | |
[[ '${{ env.VERBOSE }}' =~ on|On|Yes|yes|true|True ]] && set -x | |
balena login --token '${{ secrets.BALENA_API_KEY_TEST }}' | |
# cert-manager requires it to get whoami information for the user | |
balena env add API_TOKEN '${{ secrets.BALENA_API_KEY_TEST }}' \ | |
--service cert-manager \ | |
--device '${{ steps.register-test-device.outputs.balena_device_uuid }}' | |
# cert-manager requires is to request wildcard SSL certificate from LetsEncrypt | |
balena env add CLOUDFLARE_API_TOKEN '${{ secrets.CLOUDFLARE_API_TOKEN }}' \ | |
--service cert-manager \ | |
--device '${{ steps.register-test-device.outputs.balena_device_uuid }}' | |
- name: provision ephemeral test device | |
id: provision-test-device | |
if: ${{ github.event_name == 'pull_request' && github.event.action != 'closed'}} | |
run: | | |
set -ue | |
[[ '${{ env.VERBOSE }}' =~ on|On|Yes|yes|true|True ]] && set -x | |
for market_type in spot on-demand; do | |
# https://docs.aws.amazon.com/cli/latest/reference/ec2/run-instances.html | |
response="$(aws ec2 run-instances \ | |
--launch-template 'LaunchTemplateId=${{ env.AWS_EC2_LAUNCH_TEMPLATE }},Version=${{ env.AWS_EC2_LT_VERSION }}' \ | |
--instance-type '${{ env.AWS_EC2_INSTANCE_TYPE }}' \ | |
$([[ $market_type =~ spot ]] && echo '--instance-market-options MarketType=spot') \ | |
--security-group-ids '${{ env.AWS_VPC_SECURITY_GROUP_IDS }}' \ | |
--subnet-id '${{ env.AWS_VPC_SUBNET_ID }}' \ | |
--associate-public-ip-address \ | |
--user-data file://config.json \ | |
--tag-specifications "ResourceType=instance,Tags=[{Key=Name,Value=balena-tests},{Key=MarketType,Value=${market_type}},{Key=Owner,Value=${{ env.AWS_IAM_USERNAME }}},{Key=GITHUB_SHA,Value=${GITHUB_SHA}-tests}]" || true)" | |
[[ -n $response ]] && break | |
done | |
[[ -z $response ]] && exit 1 | |
instance_id="$(echo "${response}" | jq -r '.Instances[].InstanceId')" | |
aws ec2 wait instance-running --instance-ids "${instance_id}" | |
aws ec2 wait instance-status-ok --instance-ids "${instance_id}" | |
echo "::set-output name=instance_id::${instance_id}" | |
env: | |
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} | |
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} | |
AWS_DEFAULT_REGION: ${{ env.AWS_REGION }} | |
- name: provision SSH key | |
id: provision-ssh-key | |
# wait for cloud-config | |
# https://github.com/balena-os/cloud-config | |
timeout-minutes: 10 | |
if: ${{ github.event_name == 'pull_request' && github.event.action != 'closed'}} | |
run: | | |
set -ue | |
[[ '${{ env.VERBOSE }}' =~ on|On|Yes|yes|true|True ]] && set -x | |
balena login --token '${{ secrets.BALENA_API_KEY_TEST }}' | |
if ! [[ -e "${HOME}/.ssh/id_rsa" ]]; then | |
ssh-keygen -N '' \ | |
-C "$(balena whoami | grep EMAIL | cut -c11-)" \ | |
-f "${HOME}/.ssh/id_rsa" | |
fi | |
match='' | |
for key in $(balena keys | grep -v ID | awk '{print $1}'); do | |
fp=$(balena key ${key} | tail -n 1 | ssh-keygen -E md5 -lf /dev/stdin | awk '{print $2}') | |
if [[ $fp =~ $(ssh-keygen -E md5 -lf "${HOME}/.ssh/id_rsa" | awk '{print $2}') ]]; then | |
match="${key}" | |
break | |
fi | |
done | |
if [[ -z $match ]]; then | |
balena key add "${GITHUB_SHA}" "${HOME}/.ssh/id_rsa.pub" | |
else | |
balena keys | |
fi | |
pgrep ssh-agent || ssh-agent -a "${SSH_AUTH_SOCK}" | |
ssh-add "${HOME}/.ssh/id_rsa" | |
while ! [[ "$(ssh-uuid -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null \ | |
${{ steps.register-test-device.outputs.balena_device_uuid }}.balena \ | |
cat /mnt/boot/config.json | jq -r .uuid)" =~ ${{ steps.register-test-device.outputs.balena_device_uuid }} ]]; do | |
echo "::warning::Still working..." | |
sleep "$(( (RANDOM % 5) + 5 ))s" | |
done | |
echo "::set-output name=key_id::${GITHUB_SHA}" | |
env: | |
SSH_AUTH_SOCK: /tmp/ssh_agent.sock | |
# fall-back to QEMU software emulation when nested virtualisation is not available | |
- name: create dummy kvm device | |
id: nested-virtualisation-bypass | |
timeout-minutes: 10 | |
if: ${{ github.event_name == 'pull_request' && github.event.action != 'closed'}} | |
run: | | |
set -ue | |
[[ '${{ env.VERBOSE }}' =~ on|On|Yes|yes|true|True ]] && set -x | |
source functions | |
balena login --token '${{ secrets.BALENA_API_KEY_TEST }}' | |
balena whoami && ssh-add -l | |
with_backoff ssh-uuid -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null \ | |
${{ steps.register-test-device.outputs.balena_device_uuid }}.balena \ | |
'[[ -e /dev/kvm ]] || mknod /dev/kvm b 1 1' | |
env: | |
SSH_AUTH_SOCK: /tmp/ssh_agent.sock | |
- name: wait for application | |
id: wait-application | |
timeout-minutes: 30 | |
if: ${{ github.event_name == 'pull_request' && github.event.action != 'closed'}} | |
run: | | |
set -ue | |
[[ '${{ env.VERBOSE }}' =~ on|On|Yes|yes|true|True ]] && set -x | |
source functions | |
balena login --token '${{ secrets.BALENA_API_KEY_TEST }}' | |
balena whoami && ssh-add -l | |
while [[ "$(curl -X POST --silent --retry ${{ env.RETRY }} --fail \ | |
'https://api.${{ env.ENVIRONMENT }}/supervisor/v1/device' \ | |
--header 'authorization: Bearer ${{ secrets.BALENA_API_KEY_TEST }}' \ | |
--header 'Content-Type:application/json' \ | |
--data '{"uuid": "${{ steps.register-test-device.outputs.balena_device_uuid }}", "method": "GET"}' \ | |
--compressed | jq -r '.update_pending')" =~ ^true$ ]]; do | |
sleep "$(( ( RANDOM % ${{ env.RETRY }} ) + ${{ env.RETRY }} ))s" | |
done | |
# wait for services to start running | |
while with_backoff ssh-uuid -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null \ | |
${{ steps.register-test-device.outputs.balena_device_uuid }}.balena \ | |
'balena ps -q | xargs balena inspect | jq -r .[].State.Status' \ | |
| grep -E 'created|restarting|removing|paused|exited|dead'; do | |
echo "::warning::Still working..." | |
sleep "$(( (RANDOM % 30) + 30 ))s" | |
done | |
# wait for Docker healthchecks | |
while with_backoff ssh-uuid -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null \ | |
${{ steps.register-test-device.outputs.balena_device_uuid }}.balena \ | |
'balena ps -q | xargs balena inspect \ | |
| jq -r ".[] | select(.State.Health.Status!=null).Name + \":\" + .State.Health.Status"' \ | |
| grep -E ':starting|:unhealthy'; do | |
echo "::warning::Still working..." | |
sleep "$(( (RANDOM % 30) + 30 ))s" | |
done | |
env: | |
SSH_AUTH_SOCK: /tmp/ssh_agent.sock | |
# (TBC) https://www.balena.io/docs/reference/supervisor/docker-compose/ | |
# due to lack of long form depends_on support in compositions, restart to ensure all | |
# components are running with the latest configuration; preferred over restart via | |
# Supervisor API restart due to potential HTTP [timeouts](https://github.com/balena-os/balena-supervisor/issues/1157) | |
- name: restart components | |
id: restart-application | |
timeout-minutes: 15 | |
if: ${{ github.event_name == 'pull_request' && github.event.action != 'closed'}} | |
run: | | |
set -ue | |
[[ '${{ env.VERBOSE }}' =~ on|On|Yes|yes|true|True ]] && set -x | |
source functions | |
balena login --token '${{ secrets.BALENA_API_KEY_TEST }}' | |
balena whoami && ssh-add -l | |
with_backoff ssh-uuid -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null \ | |
${{ steps.register-test-device.outputs.balena_device_uuid }}.balena \ | |
'balena ps -q | xargs balena restart || true' | |
#balena device restart ${{ steps.register-test-device.outputs.balena_device_uuid }} | |
# wait for Docker healthchecks | |
while with_backoff ssh-uuid -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null \ | |
${{ steps.register-test-device.outputs.balena_device_uuid }}.balena \ | |
'balena ps -q | xargs balena inspect \ | |
| jq -r ".[] | select(.State.Health.Status!=null).Name + \":\" + .State.Health.Status"' \ | |
| grep -vE 'resin_supervisor|balena_supervisor' \ | |
| grep -E ':starting|:unhealthy'; do | |
echo "::warning::Still working..." | |
sleep "$(( (RANDOM % 30) + 30 ))s" | |
done | |
env: | |
SSH_AUTH_SOCK: /tmp/ssh_agent.sock | |
- name: device tests | |
id: device-tests | |
if: ${{ github.event_name == 'pull_request' && github.event.action != 'closed'}} | |
# https://giters.com/gfx/example-github-actions-with-tty | |
# https://github.com/actions/runner/issues/241#issuecomment-924327172 | |
shell: 'script -q -e -c "bash {0}"' | |
run: | | |
set -ue | |
[[ '${{ env.VERBOSE }}' =~ on|On|Yes|yes|true|True ]] && set -x | |
source functions | |
balena login --token '${{ secrets.BALENA_API_KEY_TEST }}' | |
balena whoami && ssh-add -l | |
while ! [[ $(curl -X POST --silent --retry ${{ env.RETRY }} --fail \ | |
'https://api.${{ env.ENVIRONMENT }}/supervisor/v2/applications/state' \ | |
--header 'authorization: Bearer ${{ secrets.BALENA_API_KEY_TEST }}' \ | |
--header 'Content-Type:application/json' \ | |
--data '{"uuid": "${{ steps.register-test-device.outputs.balena_device_uuid }}", "method": "GET"}' \ | |
--compressed | jq -r '.[].services."${{ env.OPENBALENA_TESTS_SERVICE }}".status') =~ Run|run ]]; do | |
echo "::warning::Still working..." | |
sleep "$(( ( RANDOM % ${{ env.RETRY }} ) + ${{ env.RETRY }} ))s" | |
done | |
# (TBC) placeholder for a complete end-to-end test suite | |
with_backoff ssh-uuid -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null \ | |
--service ${{ env.OPENBALENA_TESTS_SERVICE }} \ | |
${{ steps.register-test-device.outputs.balena_device_uuid }}.balena \ | |
'/root/run-tests.sh' | |
env: | |
SSH_AUTH_SOCK: /tmp/ssh_agent.sock | |
ATTEMPTS: 2 | |
- name: remove SSH key | |
if: always() | |
id: remove-ssh-key | |
run: | | |
set -ue | |
[[ '${{ env.VERBOSE }}' =~ on|On|Yes|yes|true|True ]] && set -x | |
balena login --token '${{ secrets.BALENA_API_KEY_TEST }}' | |
balena keys | grep ${{ steps.provision-ssh-key.outputs.key_id }} \ | |
| awk '{print $1}' | xargs balena key rm --yes || true | |
pgrep ssh-agent && (pgrep ssh-agent | xargs kill) | |
rm -f /tmp/ssh_agent.sock | |
- name: destroy ephemeral test device | |
if: always() | |
id: destroy-test-device | |
run: | | |
set -ue | |
[[ '${{ env.VERBOSE }}' =~ on|On|Yes|yes|true|True ]] && set -x | |
balena login --token '${{ secrets.BALENA_API_KEY_TEST }}' | |
aws ec2 terminate-instances \ | |
--instance-ids ${{ steps.provision-test-device.outputs.instance_id }} || true | |
balena device rm ${{ steps.register-test-device.outputs.balena_device_uuid }} --yes || true | |
env: | |
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} | |
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} | |
AWS_DEFAULT_REGION: ${{ env.AWS_REGION }} | |
# remove orphaned ACME DNS-01 validation records | |
# https://letsencrypt.org/docs/challenge-types/#dns-01-challenge | |
# FIXME: clean up older _acme-challenge.auto TXT records | |
- name: cleanup-dns-records | |
if: always() | |
id: cloudflare-dns-cleanup | |
run: | | |
set -ue | |
[[ '${{ env.VERBOSE }}' =~ on|On|Yes|yes|true|True ]] && set -x | |
subdomain="$(echo "${DNS_TLD}" | sed "s/.${DEVICE_PROXY_TLD}//g")" | |
match="${{ steps.register-test-device.outputs.balena_device_uuid }}.${subdomain}" | |
zone_id="$(curl --silent --retry ${{ env.RETRY }} \ | |
"https://api.cloudflare.com/client/v4/zones?name=${DEVICE_PROXY_TLD}" \ | |
-H 'Authorization: Bearer ${{ secrets.CLOUDFLARE_API_TOKEN }}' | jq -r '.result[].id')" | |
for record in "$(curl --silent --retry ${{ env.RETRY }} \ | |
"https://api.cloudflare.com/client/v4/zones/${zone_id}/dns_records" \ | |
-H 'Authorization: Bearer ${{ secrets.CLOUDFLARE_API_TOKEN }}' \ | |
| jq -r --arg match "${match}" '.result[] | select(((.type=="TXT") and (.name | contains($match))))' \ | |
| base64)"; do | |
json="$(echo "${record}" | base64 -d | jq -r)" | |
id="$(echo "${json}" | jq -r .id)" | |
name="$(echo "${json}" | jq -r .name)" | |
if [[ -n $id ]] && [[ -n $name ]]; then | |
echo "::warning::Orphaned DNS record ${name} (${id})..." | |
if [[ -z $DRY_RUN ]]; then | |
curl -X DELETE --silent --retry ${{ env.RETRY }} \ | |
"https://api.cloudflare.com/client/v4/zones/${zone_id}/dns_records/${id}" \ | |
-H 'Authorization: Bearer ${{ secrets.CLOUDFLARE_API_TOKEN }}' | |
fi | |
fi | |
done | |
env: | |
DRY_RUN: true | |
- name: remove registry secrets | |
if: always() | |
id: remove-registry-secrets | |
run: | | |
set -ue | |
[[ '${{ env.VERBOSE }}' =~ on|On|Yes|yes|true|True ]] && set -x | |
rm -f "${HOME}/.balena/secrets.json" |