This repository has been archived by the owner on Nov 9, 2024. It is now read-only.
ansible-easy-vpn Testing #449
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: CI | |
run-name: ansible-easy-vpn Testing | |
on: | |
push: | |
pull_request_review: | |
workflow_dispatch: | |
inputs: | |
only_ubuntu_22: | |
description: "Only run on Ubuntu 22.04" | |
required: false | |
type: boolean | |
default: false | |
only_ubuntu_20: | |
description: "Only run on Ubuntu 20.04" | |
required: false | |
type: boolean | |
default: false | |
only_debian_11: | |
description: "Only run on Debian 11" | |
required: false | |
type: boolean | |
default: false | |
only_debian_12: | |
description: "Only run on Debian 12" | |
required: false | |
type: boolean | |
default: false | |
manual_mode: | |
description: "Don't destroy the server after the setup is complete" | |
required: false | |
type: boolean | |
default: false | |
jobs: | |
lint: | |
runs-on: ubuntu-latest | |
steps: | |
- name: Checkout the current branch | |
uses: actions/checkout@v3 | |
- name: Set up Python | |
uses: actions/setup-python@v2 | |
with: | |
python-version: "3.10" | |
- name: Install ansible-lint | |
run: pip install ansible ansible-lint | |
- name: Comment out the ask_vault_password bit | |
run: "sed -i'.bak' -e 's/ask_vault_pass/#ask_vault_pass/' ansible.cfg" | |
- name: Ansible-lint | |
run: ansible-lint | |
matrix_prep: | |
runs-on: ubuntu-latest | |
outputs: | |
matrix: ${{ steps.set-matrix.outputs.matrix }} | |
letsencrypt_staging: ${{ steps.set-matrix.outputs.letsencrypt_staging }} | |
steps: | |
- name: Checkout the code | |
uses: actions/checkout@v2 | |
- id: set-matrix | |
run: | | |
if [[ ${ONLY_UBUNTU_22} == 'true' ]]; then | |
# Only deploy on Ubuntu 22.04, don't use Letsencrypt Staging | |
matrix=$(jq 'map(. | select((.os=="ubuntu-22.04")) )' .github/workflows/matrix_includes.json) | |
elif [[ ${ONLY_UBUNTU_20} == 'true' ]]; then | |
# Only deploy on Ubuntu 20.04, don't use Letsencrypt Staging | |
matrix=$(jq 'map(. | select((.os=="ubuntu-20.04")) )' .github/workflows/matrix_includes.json) | |
elif [[ ${ONLY_DEBIAN_11} == 'true' ]]; then | |
# Only deploy on Debian 11, don't use Letsencrypt Staging | |
matrix=$(jq 'map(. | select((.os=="debian-11")) )' .github/workflows/matrix_includes.json) | |
elif [[ ${ONLY_DEBIAN_12} == 'true' ]]; then | |
# Only deploy on Debian 11, don't use Letsencrypt Staging | |
matrix=$(jq 'map(. | select((.os=="debian-12")) )' .github/workflows/matrix_includes.json) | |
else | |
# Deploy on all supported OSes, use Letsencrypt Staging to avoid rate-limiting | |
matrix=$(jq 'map(.)' .github/workflows/matrix_includes.json) | |
fi | |
echo "matrix={\"include\":$(echo $matrix)}" >> $GITHUB_OUTPUT | |
echo "letsencrypt_staging='yes'" >> $GITHUB_OUTPUT | |
env: | |
ONLY_UBUNTU_22: ${{ inputs.only_ubuntu_22 }} | |
ONLY_UBUNTU_20: ${{ inputs.only_ubuntu_20 }} | |
ONLY_DEBIAN_11: ${{ inputs.only_debian_11 }} | |
ONLY_DEBIAN_12: ${{ inputs.only_debian_12 }} | |
build: | |
runs-on: ubuntu-latest | |
needs: matrix_prep | |
environment: cicd | |
strategy: | |
fail-fast: false | |
matrix: ${{fromJson(needs.matrix_prep.outputs.matrix)}} | |
steps: | |
- name: Initialize the ssh-agent | |
uses: webfactory/[email protected] | |
with: | |
ssh-private-key: ${{ secrets.SSH_PRIVATE_KEY }} | |
- name: Checkout the branch locally | |
uses: actions/checkout@v3 | |
- name: Update apt cache on the runner and install dependencies | |
run: sudo apt update && sudo apt install sshpass expect wamerican | |
- name: Generate a random username and password | |
run: | | |
echo "EASYVPN_USERNAME=$(shuf -n 1 /usr/share/dict/words | tr '[:upper:]' '[:lower:]' | tr -dc '[:alnum:]' )" >> $GITHUB_ENV && | |
echo "EASYVPN_PASSWORD=$(openssl rand -base64 20)" >> $GITHUB_ENV | |
- name: Set the username and password outputs | |
id: random_username | |
run: | | |
case $INDEX in | |
"1") | |
echo "EASYVPN_USERNAME_1=$EASYVPN_USERNAME" >> $GITHUB_OUTPUT | |
echo "EASYVPN_PASSWORD_1=$EASYVPN_PASSWORD" >> $GITHUB_OUTPUT | |
;; | |
"2") | |
echo "EASYVPN_USERNAME_2=$EASYVPN_USERNAME" >> $GITHUB_OUTPUT | |
echo "EASYVPN_PASSWORD_2=$EASYVPN_PASSWORD" >> $GITHUB_OUTPUT | |
;; | |
"3") | |
echo "EASYVPN_USERNAME_3=$EASYVPN_USERNAME" >> $GITHUB_OUTPUT | |
echo "EASYVPN_PASSWORD_3=$EASYVPN_PASSWORD" >> $GITHUB_OUTPUT | |
;; | |
"4") | |
echo "EASYVPN_USERNAME_4=$EASYVPN_USERNAME" >> $GITHUB_OUTPUT | |
echo "EASYVPN_PASSWORD_4=$EASYVPN_PASSWORD" >> $GITHUB_OUTPUT | |
;; | |
*) | |
exit 1 | |
;; | |
esac | |
env: | |
INDEX: ${{ matrix.index }} | |
- name: Spawn a Hetzner node | |
uses: TimDaub/hetzner-cloud-deploy-server-action@v2 | |
with: | |
server-name: ansible-easy-vpn-${{ env.EASYVPN_USERNAME }} | |
server-image: ${{ matrix.os }} | |
server-type: "cx11" | |
ssh-key-name: "github-actions" | |
hcloud-token: ${{ secrets.HCLOUD_TOKEN }} | |
delete-server: false | |
- name: Add the host key to known hosts | |
run: mkdir -p ~/.ssh/ && ssh-keyscan -H $SERVER_IPV4 >> ~/.ssh/known_hosts | |
- name: Update apt cache | |
run: ssh root@$SERVER_IPV4 apt update | |
- name: Install git and expect (Debian-based) | |
run: ssh root@$SERVER_IPV4 apt install -y git expect wamerican | |
- uses: infraway/[email protected] | |
with: | |
type: "A" | |
name: "${{ env.EASYVPN_USERNAME }}.${{ secrets.CLOUDFLARE_DOMAIN }}" | |
content: "${{ env.SERVER_IPV4 }}" | |
proxied: false | |
token: ${{ secrets.CLOUDFLARE_TOKEN }} | |
zone: ${{ secrets.CLOUDFLARE_ZONE }} | |
- uses: infraway/[email protected] | |
with: | |
type: "CNAME" | |
name: "auth.${{ env.EASYVPN_USERNAME }}.${{ secrets.CLOUDFLARE_DOMAIN }}" | |
content: "${{ env.EASYVPN_USERNAME }}.${{ secrets.CLOUDFLARE_DOMAIN }}" | |
proxied: false | |
token: ${{ secrets.CLOUDFLARE_TOKEN }} | |
zone: ${{ secrets.CLOUDFLARE_ZONE }} | |
- uses: infraway/[email protected] | |
with: | |
type: "CNAME" | |
name: "wg.${{ env.EASYVPN_USERNAME }}.${{ secrets.CLOUDFLARE_DOMAIN }}" | |
content: "${{ env.EASYVPN_USERNAME }}.${{ secrets.CLOUDFLARE_DOMAIN }}" | |
proxied: false | |
token: ${{ secrets.CLOUDFLARE_TOKEN }} | |
zone: ${{ secrets.CLOUDFLARE_ZONE }} | |
- uses: infraway/[email protected] | |
with: | |
type: "CNAME" | |
name: "adguard.${{ env.EASYVPN_USERNAME }}.${{ secrets.CLOUDFLARE_DOMAIN }}" | |
content: "${{ env.EASYVPN_USERNAME }}.${{ secrets.CLOUDFLARE_DOMAIN }}" | |
proxied: false | |
token: ${{ secrets.CLOUDFLARE_TOKEN }} | |
zone: ${{ secrets.CLOUDFLARE_ZONE }} | |
- name: Clone the branch to the Hetzner instance | |
run: ssh root@$SERVER_IPV4 git clone https://github.com/notthebee/ansible-easy-vpn -b ${{ github.head_ref || github.ref_name }} | |
env: | |
GIT_SSL_NO_VERIFY: "true" | |
- name: Execute the automated bootstrap script | |
run: >- | |
ssh -t -t root@$SERVER_IPV4 | |
"export LETSENCRYPT_STAGING=$LETSENCRYPT_STAGING && | |
ansible-easy-vpn/testing/expect/defaults.exp | |
--username $EASYVPN_USERNAME | |
--password $EASYVPN_PASSWORD | |
--smtp_server $SMTP_SERVER | |
--smtp_login $SMTP_LOGIN | |
--smtp_password $SMTP_PASSWORD | |
--domain $DOMAIN" | |
env: | |
LETSENCRYPT_STAGING: ${{ needs.matrix_prep.outputs.letsencrypt_staging }} | |
SMTP_SERVER: ${{ secrets.SMTP_SERVER }} | |
SMTP_LOGIN: ${{ secrets.SMTP_LOGIN }} | |
SMTP_PASSWORD: ${{ secrets.SMTP_PASSWORD }} | |
DOMAIN: ${{ env.EASYVPN_USERNAME }}.${{ secrets.CLOUDFLARE_DOMAIN }} | |
- name: Copy the private key from the Hetzner host | |
run: sshpass -p$EASYVPN_PASSWORD scp root@$SERVER_IPV4:/tmp/id_ssh_ed25519 ~/.ssh/id_vpn | |
- name: Initialize the ssh-agent | |
uses: webfactory/[email protected] | |
with: | |
ssh-private-key: ${{ secrets.SSH_PRIVATE_KEY }} | |
- name: Regsiter the private key with the ssh-agent | |
run: expect -c "spawn ssh-add $HOME/.ssh/id_vpn; expect -re \"Enter passphrase.*\"; send -- \"$EASYVPN_PASSWORD\r\"; expect -re \"Identity added.*\"" | |
- name: Reboot the server | |
run: | | |
ssh -t -t $EASYVPN_USERNAME@$SERVER_IPV4 "sudo reboot" || true | |
- name: Wait for the server to reboot | |
run: >- | |
sleep 60s | |
- name: Copy the private key to the Github root | |
run: cp $HOME/.ssh/id_vpn id_vpn | |
- name: Log into the server and execute the script again for idempotency | |
run: >- | |
ssh -t -t $EASYVPN_USERNAME@$SERVER_IPV4 "export LETSENCRYPT_STAGING=$LETSENCRYPT_STAGING; ansible-easy-vpn/testing/expect/idempotency.exp --password $EASYVPN_PASSWORD" | |
env: | |
LETSENCRYPT_STAGING: ${{ needs.matrix_prep.outputs.letsencrypt_staging }} | |
- name: Add the personal SSH key for debugging | |
run: >- | |
curl https://github.com/notthebee.keys > notthebee.pub && | |
ssh-copy-id -f -i notthebee.pub $EASYVPN_USERNAME@$SERVER_IPV4 | |
- name: Archive the private SSH key (Matrix 1) | |
if: ${{ matrix.index == '1' }} | |
uses: actions/upload-artifact@v3 | |
with: | |
name: "private-ssh-key-1" | |
path: "id_vpn" | |
- name: Archive the private SSH key (Matrix 2) | |
if: ${{ matrix.index == '2' }} | |
uses: actions/upload-artifact@v3 | |
with: | |
name: "private-ssh-key-2" | |
path: "id_vpn" | |
- name: Archive the private SSH key (Matrix 3) | |
if: ${{ matrix.index == '3' }} | |
uses: actions/upload-artifact@v3 | |
with: | |
name: "private-ssh-key-3" | |
path: "id_vpn" | |
- name: Archive the private SSH key (Matrix 4) | |
if: ${{ matrix.index == '4' }} | |
uses: actions/upload-artifact@v3 | |
with: | |
name: "private-ssh-key-4" | |
path: "id_vpn" | |
outputs: | |
EASYVPN_USERNAME_1: "${{ steps.random_username.outputs.EASYVPN_USERNAME_1 }}" | |
EASYVPN_USERNAME_2: "${{ steps.random_username.outputs.EASYVPN_USERNAME_2 }}" | |
EASYVPN_USERNAME_3: "${{ steps.random_username.outputs.EASYVPN_USERNAME_3 }}" | |
EASYVPN_USERNAME_4: "${{ steps.random_username.outputs.EASYVPN_USERNAME_4 }}" | |
EASYVPN_PASSWORD_1: "${{ steps.random_username.outputs.EASYVPN_PASSWORD_1 }}" | |
EASYVPN_PASSWORD_2: "${{ steps.random_username.outputs.EASYVPN_PASSWORD_2 }}" | |
EASYVPN_PASSWORD_3: "${{ steps.random_username.outputs.EASYVPN_PASSWORD_3 }}" | |
EASYVPN_PASSWORD_4: "${{ steps.random_username.outputs.EASYVPN_PASSWORD_4 }}" | |
debug: | |
runs-on: ubuntu-latest | |
environment: cicd | |
strategy: | |
fail-fast: false | |
matrix: ${{fromJson(needs.matrix_prep.outputs.matrix)}} | |
needs: | |
- matrix_prep | |
- build | |
steps: | |
- name: Wait for the server to reboot | |
if: inputs.manual_mode | |
run: >- | |
sleep infinity | |
fetch_config: | |
runs-on: ubuntu-latest | |
environment: cicd | |
strategy: | |
fail-fast: false | |
matrix: ${{fromJson(needs.matrix_prep.outputs.matrix)}} | |
needs: | |
- matrix_prep | |
- debug | |
- build | |
steps: | |
- name: Check out this repo | |
uses: actions/checkout@v2 | |
- name: Set up Python | |
uses: actions/setup-python@v2 | |
with: | |
python-version: "3.10" | |
- name: Install expect | |
run: >- | |
sudo apt install expect | |
- name: Create the .ssh folder | |
run: >- | |
mkdir /home/runner/.ssh | |
- name: Get the private SSH key artifact (Matrix 1) | |
if: matrix.index == '1' | |
uses: actions/download-artifact@v3 | |
with: | |
name: "private-ssh-key-1" | |
path: /home/runner/.ssh | |
- name: Get the private SSH key artifact (Matrix 2) | |
if: matrix.index == '2' | |
uses: actions/download-artifact@v3 | |
with: | |
name: "private-ssh-key-2" | |
path: /home/runner/.ssh | |
- name: Get the private SSH key artifact (Matrix 3) | |
if: matrix.index == '3' | |
uses: actions/download-artifact@v3 | |
with: | |
name: "private-ssh-key-3" | |
path: /home/runner/.ssh | |
- name: Get the private SSH key artifact (Matrix 4) | |
if: matrix.index == '4' | |
uses: actions/download-artifact@v3 | |
with: | |
name: "private-ssh-key-4" | |
path: /home/runner/.ssh | |
- name: Set the correct permissions for the SSH key | |
run: | | |
chmod 700 $HOME/.ssh | |
chmod 600 $HOME/.ssh/* | |
- name: Initialize the ssh-agent | |
uses: webfactory/[email protected] | |
with: | |
ssh-private-key: ${{ secrets.SSH_PRIVATE_KEY }} | |
- name: Regsiter the private key with the ssh-agent | |
run: expect -c "spawn ssh-add $HOME/.ssh/id_vpn; expect -re \"Enter passphrase.*\"; send -- \"$EASYVPN_PASSWORD\r\"; expect -re \"Identity added.*\"" | |
env: | |
EASYVPN_PASSWORD: "${{ needs.build.outputs[format('EASYVPN_PASSWORD_{0}', matrix.index)] }}" | |
- name: Remove Chrome | |
run: sudo apt purge google-chrome-stable | |
- name: Remove default Chromium | |
run: sudo apt purge chromium-browser | |
- name: Install snapd | |
run: sudo apt install snapd | |
- name: Install chromium | |
run: sudo snap install chromium | |
- name: Install all necessary packages | |
run: pip install webdriver-manager selenium pyotp pexpect | |
- name: Create the config dir | |
run: mkdir /home/runner/wireguard | |
- name: Run the testing script | |
run: python testing/selenium/acceptance.py --username $EASYVPN_USERNAME --password $EASYVPN_PASSWORD --base_url $DOMAIN --ssh_agent $SSH_AUTH_SOCK | |
env: | |
EASYVPN_USERNAME: "${{ needs.build.outputs[format('EASYVPN_USERNAME_{0}', matrix.index)] }}" | |
EASYVPN_PASSWORD: "${{ needs.build.outputs[format('EASYVPN_PASSWORD_{0}', matrix.index)] }}" | |
DOMAIN: "${{ needs.build.outputs[format('EASYVPN_USERNAME_{0}', matrix.index)] }}.${{ secrets.CLOUDFLARE_DOMAIN }}" | |
- name: Upload Selenium testing screenshots | |
if: always() | |
uses: actions/upload-artifact@v4 | |
with: | |
name: "Screenshots" | |
path: "screenshots/" | |
destroy: | |
runs-on: ubuntu-latest | |
environment: cicd | |
strategy: | |
fail-fast: false | |
matrix: ${{fromJson(needs.matrix_prep.outputs.matrix)}} | |
if: always() | |
needs: | |
- matrix_prep | |
- build | |
- debug | |
- fetch_config | |
steps: | |
- name: Destroy the Hetzner instances | |
run: >- | |
export SERVER_ID=$(curl -H | |
"Authorization: Bearer $HCLOUD_TOKEN" https://api.hetzner.cloud/v1/servers | | |
jq --arg SERVER_NAME "$SERVER_NAME" '.servers[] | select(.name==$SERVER_NAME) | .id') && | |
curl -X DELETE -H "Authorization: Bearer $HCLOUD_TOKEN" | |
https://api.hetzner.cloud/v1/servers/$SERVER_ID | |
env: | |
HCLOUD_TOKEN: ${{ secrets.HCLOUD_TOKEN }} | |
SERVER_NAME: ansible-easy-vpn-${{ needs.build.outputs[format('EASYVPN_USERNAME_{0}', matrix.index)] }} | |
- name: Delete all Cloudflare domains | |
run: | | |
curl --silent "https://api.cloudflare.com/client/v4/zones/$ZONE_ID/dns_records?per_page=50000" \ | |
--header "Authorization: Bearer $CLOUDFLARE_TOKEN" \ | |
| jq --raw-output '.result[].id' | while read id | |
do | |
curl --silent --request DELETE "https://api.cloudflare.com/client/v4/zones/$ZONE_ID/dns_records/$id" \ | |
--header "Authorization: Bearer $CLOUDFLARE_TOKEN" | |
done | |
env: | |
CLOUDFLARE_TOKEN: ${{ secrets.CLOUDFLARE_TOKEN }} | |
ZONE_ID: ${{ secrets.CLOUDFLARE_ZONE }} |