Deploy Otomi #2231
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 Otomi | |
on: | |
workflow_call: | |
inputs: | |
cloud_provider: | |
description: Provider where Otomi will be installed | |
type: string | |
default: scaleway | |
kubernetes_versions: | |
description: "Kubernetes versions (JSON formatted list e.g.: ['1.27'])" | |
type: string | |
default: "['1.28']" | |
install_profile: | |
description: Otomi installation profile | |
default: full | |
type: string | |
cluster_persistence: | |
type: string | |
description: Should a cluster be destroyed on pipeline finish | |
default: destroy | |
dns: | |
type: string | |
description: Select DNS provider | |
default: nip_io | |
kms: | |
type: string | |
description: Should Otomi encrypt secrets in values repo (DNS or KMS is used) | |
default: az_kms | |
generate_password: | |
type: string | |
description: Should a unique password be generated? | |
default: 'no' | |
oidc: | |
type: string | |
description: Should Otomi use external OIDC? | |
default: keycloak | |
certificate: | |
type: string | |
description: Select certificate issuer | |
default: gen_custom_ca | |
license: | |
type: string | |
description: Should a unique password be generated? | |
default: 'yes' | |
workflow_dispatch: | |
inputs: | |
cloud_provider: | |
description: Provider where Otomi will be installed | |
type: choice | |
options: | |
- scaleway | |
- linode | |
- digitalocean | |
default: 'linode' | |
kubernetes_versions: | |
description: 'Kubernetes version' | |
type: choice | |
options: | |
- "['1.27']" | |
- "['1.28']" | |
- "['1.29']" | |
default: "['1.29']" | |
install_profile: | |
description: Otomi installation profile | |
default: minimal-with-team | |
type: choice | |
options: | |
- minimal | |
- minimal-with-team | |
- monitoring-with-team | |
- full | |
- upgrade | |
- no-otomi | |
cluster_persistence: | |
type: choice | |
description: Should a cluster be destroyed on pipeline finish? | |
options: | |
- preserve | |
- destroy | |
default: preserve | |
dns: | |
type: choice | |
description: Select DNS provider | |
options: | |
- nip_io | |
- az_dns | |
default: nip_io | |
kms: | |
type: choice | |
description: Should Otomi encrypt secrets in values repo (DNS or KMS is turned on)? | |
options: | |
- no_kms | |
- az_kms | |
default: az_kms | |
generate_password: | |
type: choice | |
description: Should a unique password be generated? | |
options: | |
- 'yes' | |
- 'no' | |
default: 'no' | |
oidc: | |
type: choice | |
description: Should Otomi use external OIDC? | |
options: | |
- keycloak | |
- az_oidc | |
default: keycloak | |
certificate: | |
type: choice | |
description: Select certificate issuer | |
options: | |
- gen_custom_ca | |
- letsencrypt_staging | |
- letsencrypt_production | |
default: gen_custom_ca | |
license: | |
type: choice | |
description: Should a predefined Otomi license be injected? | |
options: | |
- 'yes' | |
- 'no' | |
default: 'yes' | |
env: | |
CACHE_REGISTRY: ghcr.io | |
CACHE_REPO: linode/apl-core | |
REPO: otomi/core | |
GIT_USER: svcAPLBot | |
SCALEWAY_NODE_TYPE: PRO2-M | |
SCALEWAY_NODE_POOL_MIN_SIZE: 3 | |
SCALEWAY_VPC_ID: e1019b0c-7c7d-49ef-86e4-b02f55b2e0d3 | |
DIGITALOCEAN_NODE_SIZE: s-8vcpu-16gb | |
DIGITALOCEAN_NODE_POOL_MIN_SIZE: 3 | |
CHECK_CONTEXT: continuous-integration/integration-test | |
COMMIT_ID: '${{ github.event.pull_request.head.sha || github.sha }}' | |
BOT_EMAIL: ${{ vars.BOT_EMAIL }} | |
BOT_USERNAME: ${{ vars.BOT_USERNAME }} | |
jobs: | |
preprocess-input: | |
name: Preprocess input variables | |
runs-on: ubuntu-latest | |
steps: | |
- name: Print user input | |
run: | | |
echo 'ref: ${{ github.event.pull_request.head.ref || github.ref }}' | |
echo 'install_profile: ${{ inputs.install_profile }}' | |
echo 'kubernetes_versions: ${{ inputs.kubernetes_versions }}' | |
echo 'cluster_persistence: ${{ inputs.cluster_persistence }}' | |
echo 'dns: ${{ inputs.dns }}' | |
echo 'kms: ${{ inputs.kms }}' | |
echo 'generate_password: ${{ inputs.generate_password }}' | |
echo 'oidc: ${{ inputs.oidc }}' | |
echo 'certificate: ${{ inputs.certificate }}' | |
preprocess-scaleway-input: | |
needs: preprocess-input | |
if: ${{ inputs.cloud_provider == 'scaleway' }} | |
name: Preprocess input variables for scaleway | |
runs-on: ubuntu-latest | |
outputs: | |
kubernetes_versions: ${{ steps.k8s-versions.outputs.versions }} | |
steps: | |
- name: Install scw-cli | |
uses: scaleway/action-scw@v0 | |
with: | |
version: v2.26.0 | |
- id: k8s-versions | |
name: Process k8s version input | |
run: | | |
if [ -z '${{ inputs.kubernetes_versions }}' ]; then | |
echo "Kubernetes versions not specified, determine Scaleway supported versions" | |
versions=`scw k8s versions -o json | jq -ce '.[] | .name'` | |
else | |
versions='${{ inputs.kubernetes_versions }}' | |
fi | |
echo $versions | |
echo "versions=$versions" >> $GITHUB_OUTPUT | |
preprocess-digitalocean-input: | |
needs: preprocess-input | |
if: ${{ inputs.cloud_provider == 'digitalocean' }} | |
name: Preprocess input variables for digital ocean | |
runs-on: ubuntu-latest | |
outputs: | |
kubernetes_versions: ${{ steps.k8s-versions.outputs.versions }} | |
steps: | |
- name: Install doctl | |
uses: digitalocean/action-doctl@v2 | |
with: | |
token: ${{ secrets.DIGITALOCEAN_ACCESS_TOKEN }} | |
- id: k8s-versions | |
name: Process k8s version input | |
run: | | |
if [ -z '${{ inputs.kubernetes_versions }}' ]; then | |
echo "Kubernetes versions not specified, determine DO supported versions" | |
versions=`doctl kubernetes options versions -o json | jq -ce 'map(.kubernetes_versions)'` | |
else | |
versions='${{ inputs.kubernetes_versions }}' | |
fi | |
echo $versions | |
echo "versions=$versions" >> $GITHUB_OUTPUT | |
preprocess-linode-input: | |
needs: preprocess-input | |
if: ${{ inputs.cloud_provider == 'linode' }} | |
name: Preprocess input variables for linode | |
runs-on: ubuntu-latest | |
outputs: | |
kubernetes_versions: ${{ steps.k8s-versions.outputs.versions }} | |
steps: | |
- name: Install the Linode CLI | |
uses: linode/action-linode-cli@v1 | |
with: | |
token: ${{ secrets.LINODE_TOKEN }} | |
- id: k8s-versions | |
name: Process k8s version input | |
run: | | |
if [ -z '${{ inputs.kubernetes_versions }}' ]; then | |
echo "Kubernetes versions not specified, determine Linode supported versions" | |
versions=`linode-cli lke versions-list --json | jq -ce '.[] | .id'` | |
else | |
versions='${{ inputs.kubernetes_versions }}' | |
fi | |
echo $versions | |
echo "versions=$versions" >> $GITHUB_OUTPUT | |
run-integration-test-scaleway: | |
if: ${{ inputs.cloud_provider == 'scaleway' }} | |
name: Run integration test on scaleway cluster | |
needs: preprocess-scaleway-input | |
runs-on: ubuntu-latest | |
strategy: | |
fail-fast: false | |
matrix: | |
kubernetes_versions: ${{ fromJSON(needs.preprocess-scaleway-input.outputs.kubernetes_versions) }} | |
max-parallel: 5 | |
steps: | |
- name: Use Scaleway CLI | |
uses: scaleway/action-scw@v0 | |
with: | |
save-config: true | |
export-config: true | |
version: v2.26.0 | |
access-key: ${{ secrets.SCW_ACCESS_KEY }} | |
secret-key: ${{ secrets.SCW_SECRET_KEY }} | |
default-project-id: ${{ secrets.SCW_DEFAULT_PROJECT_ID }} | |
default-organization-id: ${{ secrets.SCW_DEFAULT_ORGANIZATION_ID }} | |
- name: Set k8s cluster name | |
run: | | |
echo SCALEWAY_CLUSTER_NAME=${{ github.actor }}-$(TZ="GMT-2" date +"%m-%d-%H-%M") >> $GITHUB_ENV | |
# Cluster name must be no longer than 63 characters | |
- name: Determine exact k8s version | |
run: | | |
echo SCALEWAY_K8s_VERSION=$(scw k8s version list -o json | jq -re 'map(select(.name | startswith("${{ matrix.kubernetes_versions }}"))) | .[].name') >> $GITHUB_ENV | |
- name: Create private network | |
run: | | |
echo SCALEWAY_PRIVATE_NETWORK_ID=$(scw vpc private-network create project-id=${{ secrets.SCW_DEFAULT_PROJECT_ID }} name=$SCALEWAY_CLUSTER_NAME-pn region=nl-ams -ojson | jq -r .id) >> $GITHUB_ENV | |
- name: Create k8s cluster at Scaleway ${{env.SCALEWAY_CLUSTER_NAME}} | |
run: | | |
out=$(scw k8s cluster create name=${{env.SCALEWAY_CLUSTER_NAME}} \ | |
project-id=${{ env.SCW_DEFAULT_PROJECT_ID }} \ | |
private-network-id=${{ env.SCALEWAY_PRIVATE_NETWORK_ID }} \ | |
auto-upgrade.enable=false \ | |
cni=calico \ | |
pools.0.node-type=${{ env.SCALEWAY_NODE_TYPE }} \ | |
pools.0.min-size=${{env.SCALEWAY_NODE_POOL_MIN_SIZE}} \ | |
pools.0.size=${{env.SCALEWAY_NODE_POOL_MIN_SIZE}} \ | |
pools.0.max-size=3 \ | |
pools.0.autohealing=true \ | |
pools.0.autoscaling=true \ | |
pools.0.name=${{env.SCALEWAY_CLUSTER_NAME}} \ | |
pools.0.root-volume-size=50GB \ | |
version=${{ env.SCALEWAY_K8s_VERSION }} \ | |
region=nl-ams \ | |
--wait \ | |
-o=json) | |
- name: Retrieve cluster id | |
run: echo SCALEWAY_CLUSTER_ID=$(scw k8s cluster list region=nl-ams -o json | jq -r '.[] | select(.name == "${{ env.SCALEWAY_CLUSTER_NAME }}") | .id') >> $GITHUB_ENV | |
- name: Save kubectl config with auth token | |
run: scw k8s kubeconfig install ${{ env.SCALEWAY_CLUSTER_ID }} region=nl-ams | |
- name: Get kubectl environment | |
run: echo SCALEWAY_CLUSTER_CONTEXT=`kubectl config current-context` >> $GITHUB_ENV | |
- name: Create image pull secret on test cluster | |
run: | | |
kubectl create secret docker-registry reg-otomi-github \ | |
--docker-server=${{ env.CACHE_REGISTRY }} \ | |
--docker-username=${{ env.BOT_USERNAME }} \ | |
--docker-password='${{ secrets.BOT_PULL_TOKEN }}' | |
- name: Checkout | |
uses: actions/checkout@v4 | |
- name: Prepare Otomi chart | |
if: ${{ inputs.install_profile != 'no-otomi' }} | |
run: | | |
ref=${{ github.event.pull_request.head.ref || github.ref }} | |
tag=${ref##*/} | |
sed --in-place "s/APP_VERSION_PLACEHOLDER/$tag/g" chart/apl/Chart.yaml | |
sed --in-place "s/CONTEXT_PLACEHOLDER/${{ env.SCALEWAY_CLUSTER_CONTEXT }}/g" tests/integration/${{ inputs.install_profile }}.yaml | |
sed --in-place "s/OTOMI_VERSION_PLACEHOLDER/${GITHUB_REF##*/}/g" tests/integration/${{ inputs.install_profile }}.yaml | |
touch values-container-registry.yaml | |
# If a pipeline installs Otomi from the semver tag then pull container image from DockerHub | |
[[ ${GITHUB_REF##*/} =~ ^v[0-9].+$ ]] && exit 0 | |
# Pull image from cache registry | |
cat << EOF > values-container-registry.yaml | |
imageName: "${{ env.CACHE_REGISTRY }}/${{ env.CACHE_REPO }}" | |
imagePullSecretNames: | |
- reg-otomi-github | |
EOF | |
- name: Otomi install | |
if: ${{ inputs.install_profile != 'no-otomi' }} | |
env: | |
AZ_DNS: ${{ secrets.AZ_DNS }} | |
AZ_KMS: ${{ secrets.AZ_KMS }} | |
AZ_OIDC: ${{ secrets.AZ_OIDC }} | |
LETSENCRYPT_STAGING: ${{ secrets.LETSENCRYPT_STAGING }} | |
LETSENCRYPT_PRODUCTION: ${{ secrets.LETSENCRYPT_PRODUCTION }} | |
OTOMI_LICENSE: ${{ secrets.OTOMI_LICENSE }} | |
run: | | |
domainSuffix='' | |
touch values.yaml | |
[[ '${{ inputs.license }}' == 'yes' ]] && echo "$OTOMI_LICENSE" >> values.yaml | |
[[ '${{ inputs.dns }}' == 'az_dns' ]] && echo "$AZ_DNS" >> values.yaml && domainSuffix='--set cluster.domainSuffix=tst-${{ github.run_id }}.aks.redkubes.net' | |
[[ '${{ inputs.kms }}' == 'az_kms' ]] && echo "$AZ_KMS" >> values.yaml | |
[[ '${{ inputs.oidc }}' == 'az_oidc' ]] && echo "$AZ_OIDC" >> values.yaml | |
[[ '${{ inputs.certificate }}' == 'letsencrypt_staging' ]] && echo "$LETSENCRYPT_STAGING" >> values.yaml | |
[[ '${{ inputs.certificate }}' == 'letsencrypt_production' ]] && echo "$LETSENCRYPT_PRODUCTION" >> values.yaml | |
install_args="--wait --wait-for-jobs --timeout 90m0s otomi chart/apl \ | |
--values tests/integration/${{ inputs.install_profile }}.yaml \ | |
--values values-container-registry.yaml | |
--values values.yaml \ | |
--set cluster.provider=${{ inputs.cloud_provider }} | |
$domainSuffix" | |
[[ '${{ inputs.generate_password }}' == 'no' ]] && install_args="$install_args --set otomi.adminPassword=welcomeotomi" | |
helm install $install_args | |
- name: Gather k8s events on failure | |
if: failure() | |
run: | | |
kubectl get events --sort-by='.lastTimestamp' -A | |
- name: Gather k8s pods on failure | |
if: failure() | |
run: | | |
kubectl get pods -A -o wide | |
- name: Gather otomi logs on failure | |
if: failure() | |
run: | | |
kubectl logs jobs/otomi --tail 150 | |
- name: Delete k8s cluster at Scaleway | |
if: ${{ always() && inputs.cluster_persistence != 'preserve' }} | |
run: | | |
scw k8s cluster delete ${{ env.SCALEWAY_CLUSTER_ID }} with-additional-resources=true region=nl-ams | |
- name: Slack Notification | |
if: always() | |
uses: rtCamp/action-slack-notify@v2 | |
env: | |
SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK }} | |
SLACK_CHANNEL: github-ci | |
SLACK_COLOR: ${{ job.status }} | |
SLACK_ICON: https://github.com/redkubes.png?size=48 | |
SLACK_TITLE: Scheduled integration tests | |
SLACK_USERNAME: RedKubesBot | |
run-integration-test-digitalocean: | |
if: ${{ inputs.cloud_provider == 'digitalocean' }} | |
name: Run integration test on digitalocean cluster | |
needs: preprocess-digitalocean-input | |
runs-on: ubuntu-latest | |
strategy: | |
fail-fast: false | |
matrix: | |
kubernetes_versions: ${{ fromJSON(needs.preprocess-digitalocean-input.outputs.kubernetes_versions) }} | |
max-parallel: 5 | |
steps: | |
- name: Install doctl | |
uses: digitalocean/action-doctl@v2 | |
with: | |
token: ${{ secrets.DIGITALOCEAN_ACCESS_TOKEN }} | |
- name: Set k8s cluster name | |
run: | | |
echo "DIGITALOCEAN_CLUSTER_NAME=$(echo ${{ github.actor }} | tr '[:upper:]' '[:lower:]')-$(TZ='GMT-2' date +'%m-%d-%H-%M')" >> $GITHUB_ENV | |
# Cluster name must be no longer than 63 characters | |
- name: Determine exact k8s version | |
run: | | |
echo "DIGITALOCEAN_K8S_VERSION=$(doctl kubernetes options versions -o json | jq -r '.[] | select(.kubernetes_version | startswith("${{ matrix.kubernetes_versions }}")) | .slug')" >> $GITHUB_ENV | |
- name: Get default VPC for region | |
run: | | |
echo DIGITALOCEAN_VPC_UUID=`doctl vpcs list -o json | jq -re 'map(select((.region == "ams3") and .default)) | .[0] | .id'` >> $GITHUB_ENV | |
- name: Create k8s cluster for testing | |
run: | | |
doctl kubernetes cluster create ${{ env.DIGITALOCEAN_CLUSTER_NAME }} \ | |
--tag source:github \ | |
--ha \ | |
--maintenance-window any=03:00 \ | |
--region ams3 \ | |
--vpc-uuid ${{ env.DIGITALOCEAN_VPC_UUID }} \ | |
--node-pool "name=int-test-${{ strategy.job-index }}-${{ env.COMMIT_ID }};size=${{ env.DIGITALOCEAN_NODE_SIZE }};tag=integration-test;auto-scale=true;min-nodes=${{ env.DIGITALOCEAN_NODE_POOL_MIN_SIZE }};max-nodes=5;count=${{ env.DIGITALOCEAN_NODE_POOL_MIN_SIZE }};" \ | |
--version ${{ env.DIGITALOCEAN_K8S_VERSION }} \ | |
--wait | |
- name: Retrieve cluster id | |
run: echo DIGITALOCEAN_CLUSTER_ID=`doctl kubernetes cluster get ${{ env.DIGITALOCEAN_CLUSTER_NAME }} --format ID --no-header` >> $GITHUB_ENV | |
- name: Assign the cluster to the project | |
run: doctl projects resources assign ${{ secrets.DIGITALOCEAN_PROJECT }} --resource=do:kubernetes:${{ env.DIGITALOCEAN_CLUSTER_ID }} | |
- name: Save kubectl config with auth token | |
run: doctl kubernetes cluster kubeconfig save --expiry-seconds 36000 ${{ env.DIGITALOCEAN_CLUSTER_NAME }} | |
- name: Get kubectl environment | |
run: echo DIGITALOCEAN_CLUSTER_CONTEXT=`kubectl config current-context` >> $GITHUB_ENV | |
- name: Create image pull secret on test cluster | |
run: | | |
kubectl create secret docker-registry reg-otomi-github \ | |
--docker-server=${{ env.CACHE_REGISTRY }} \ | |
--docker-username=${{ env.BOT_USERNAME }} \ | |
--docker-password='${{ secrets.BOT_PULL_TOKEN }}' | |
- name: Checkout | |
uses: actions/checkout@v4 | |
- name: Prepare Otomi chart | |
if: ${{ inputs.install_profile != 'no-otomi' }} | |
run: | | |
ref=${{ github.event.pull_request.head.ref || github.ref }} | |
tag=${ref##*/} | |
sed --in-place "s/APP_VERSION_PLACEHOLDER/$tag/g" chart/apl/Chart.yaml | |
sed --in-place "s/CONTEXT_PLACEHOLDER/${{ env.DIGITALOCEAN_CLUSTER_CONTEXT }}/g" tests/integration/${{ inputs.install_profile }}.yaml | |
sed --in-place "s/OTOMI_VERSION_PLACEHOLDER/${GITHUB_REF##*/}/g" tests/integration/${{ inputs.install_profile }}.yaml | |
touch values-container-registry.yaml | |
# If a pipeline installs Otomi from the semver tag then pull container image from DockerHub | |
[[ ${GITHUB_REF##*/} =~ ^v[0-9].+$ ]] && exit 0 | |
# Pull image from cache registry | |
cat << EOF > values-container-registry.yaml | |
imageName: "${{ env.CACHE_REGISTRY }}/${{ env.CACHE_REPO }}" | |
imagePullSecretNames: | |
- reg-otomi-github | |
EOF | |
- name: Otomi install | |
if: ${{ inputs.install_profile != 'no-otomi' }} | |
env: | |
AZ_DNS: ${{ secrets.AZ_DNS }} | |
AZ_KMS: ${{ secrets.AZ_KMS }} | |
AZ_OIDC: ${{ secrets.AZ_OIDC }} | |
LETSENCRYPT_STAGING: ${{ secrets.LETSENCRYPT_STAGING }} | |
LETSENCRYPT_PRODUCTION: ${{ secrets.LETSENCRYPT_PRODUCTION }} | |
OTOMI_LICENSE: ${{ secrets.OTOMI_LICENSE }} | |
run: | | |
domainSuffix='' | |
touch values.yaml | |
[[ '${{ inputs.license }}' == 'yes' ]] && echo "$OTOMI_LICENSE" >> values.yaml | |
[[ '${{ inputs.dns }}' == 'az_dns' ]] && echo "$AZ_DNS" >> values.yaml && domainSuffix='--set cluster.domainSuffix=tst-${{ github.run_id }}.aks.redkubes.net' | |
[[ '${{ inputs.kms }}' == 'az_kms' ]] && echo "$AZ_KMS" >> values.yaml | |
[[ '${{ inputs.oidc }}' == 'az_oidc' ]] && echo "$AZ_OIDC" >> values.yaml | |
[[ '${{ inputs.certificate }}' == 'letsencrypt_staging' ]] && echo "$LETSENCRYPT_STAGING" >> values.yaml | |
[[ '${{ inputs.certificate }}' == 'letsencrypt_production' ]] && echo "$LETSENCRYPT_PRODUCTION" >> values.yaml | |
install_args="--wait --wait-for-jobs --timeout 90m0s otomi chart/apl \ | |
--values tests/integration/${{ inputs.install_profile }}.yaml \ | |
--values values-container-registry.yaml | |
--values values.yaml \ | |
--set cluster.provider=${{ inputs.cloud_provider }} | |
$domainSuffix" | |
[[ '${{ inputs.generate_password }}' == 'no' ]] && install_args="$install_args --set otomi.adminPassword=welcomeotomi" | |
helm install $install_args | |
- name: Gather k8s events on failure | |
if: failure() | |
run: | | |
kubectl get events --sort-by='.lastTimestamp' -A | |
- name: Gather k8s pods on failure | |
if: failure() | |
run: | | |
kubectl get pods -A -o wide | |
- name: Gather otomi logs on failure | |
if: failure() | |
run: | | |
kubectl logs jobs/otomi --tail 150 | |
- name: Gather otomi-e2e logs on failure | |
if: failure() | |
run: | | |
kubectl logs -n maintenance -l app.kubernetes.io/instance=job-e2e --tail 15000 | |
- name: Remove the test cluster | |
if: always() | |
run: | | |
[[ "${{ inputs.cluster_persistence }}" == "preserve" ]] && echo "The cluster ${{ env.DIGITALOCEAN_CLUSTER_NAME }} will NOT be destroyed!!" && exit 0 | |
doctl kubernetes cluster delete ${{ env.DIGITALOCEAN_CLUSTER_NAME }} -f --dangerous | |
- name: Slack Notification | |
if: always() | |
uses: rtCamp/action-slack-notify@v2 | |
env: | |
SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK }} | |
SLACK_CHANNEL: github-ci | |
SLACK_COLOR: ${{ job.status }} | |
SLACK_ICON: https://github.com/redkubes.png?size=48 | |
SLACK_TITLE: Scheduled integration tests | |
SLACK_USERNAME: RedKubesBot | |
run-integration-test-linode: | |
if: ${{ inputs.cloud_provider == 'linode' }} | |
name: Run integration test on linode cluster | |
needs: preprocess-linode-input | |
runs-on: ubuntu-latest | |
strategy: | |
fail-fast: false | |
matrix: | |
kubernetes_versions: ${{ fromJSON(needs.preprocess-linode-input.outputs.kubernetes_versions) }} | |
max-parallel: 5 | |
steps: | |
- name: Install the Linode CLI | |
uses: linode/action-linode-cli@v1 | |
with: | |
token: ${{ secrets.LINODE_TOKEN }} | |
- name: Set k8s cluster name | |
run: | | |
echo "LINODE_CLUSTER_NAME=$(echo ${{ github.actor }} | tr '[:upper:]' '[:lower:]')-$(TZ='GMT-2' date +'%m-%d-%H-%M')" >> $GITHUB_ENV | |
# Cluster name must be no longer than 63 characters | |
- name: Determine exact k8s version | |
run: | | |
echo LINODE_K8S_VERSION=$(linode-cli lke versions-list --json | jq -ce --arg version "${{ matrix.kubernetes_versions }}" '.[] | select(.id | tostring | startswith($version)) | .id') >> $GITHUB_ENV | |
- name: Create k8s cluster for testing | |
run: | | |
linode-cli lke cluster-create \ | |
--label ${{ env.LINODE_CLUSTER_NAME }} \ | |
--region nl-ams \ | |
--k8s_version ${{ env.LINODE_K8S_VERSION }} \ | |
--control_plane.high_availability true \ | |
--node_pools.type g6-dedicated-8 --node_pools.count 3 \ | |
--node_pools.autoscaler.enabled true \ | |
--node_pools.autoscaler.max 3 \ | |
--node_pools.autoscaler.min 3 \ | |
--tags testing \ | |
--no-defaults | |
- name: Retrieve cluster id | |
run: echo "LINODE_CLUSTER_ID=$(linode-cli lke clusters-list --json | jq -ce '.[] | select(.label | startswith("${{ env.LINODE_CLUSTER_NAME }}")) | .id')" >> $GITHUB_ENV | |
- name: Wait for cluster to be ready | |
run: | | |
echo "Waiting for the cluster to be active..." | |
while :; do | |
rawOutput=$(linode-cli lke pools-list ${{ env.LINODE_CLUSTER_ID }} --json) | |
allReady=$(echo "$rawOutput" | jq -r 'map(.nodes | .status == "ready") | all') | |
echo "All nodes ready: $allReady" | |
if [ "$allReady" == "true" ]; then | |
echo "Cluster is ready" | |
break | |
fi | |
sleep 30 | |
done | |
- name: Save kubectl config with auth token and Get kubectl environment and create docker secret | |
run: | | |
# Get the kubeconfig from linode-cli | |
kubeconfig=$(linode-cli lke kubeconfig-view ${{ env.LINODE_CLUSTER_ID }} --text | sed 1d | base64 --decode) | |
# Save the kubeconfig to a file | |
kubeconfigDir="$HOME/.kube" | |
kubeconfigPath="$HOME/.kube/config" | |
mkdir -p "$kubeconfigDir" # Create the directory if it doesn't exist | |
echo "$kubeconfig" > "$kubeconfigPath" | |
echo "Kubeconfig saved to $kubeconfigPath" | |
# Set the kubectl context to use the new kubeconfig | |
export KUBECONFIG="$kubeconfigPath" | |
contextName=$(kubectl config get-contexts -o name | head -n 1) | |
kubectl config use-context "$contextName" | |
echo "Kubectl context set to linode" | |
echo LINODE_CLUSTER_CONTEXT=`kubectl config current-context` >> $GITHUB_ENV | |
- name: Create image pull secret on test cluster | |
run: | | |
kubectl create secret docker-registry reg-otomi-github \ | |
--docker-server=${{ env.CACHE_REGISTRY }} \ | |
--docker-username=${{ env.BOT_USERNAME }} \ | |
--docker-password='${{ secrets.BOT_PULL_TOKEN }}' | |
- name: Checkout | |
uses: actions/checkout@v4 | |
- name: Prepare Otomi chart | |
if: ${{ inputs.install_profile != 'no-otomi' }} | |
run: | | |
ref=${{ github.event.pull_request.head.ref || github.ref }} | |
tag=${ref##*/} | |
sed --in-place "s/APP_VERSION_PLACEHOLDER/$tag/g" chart/apl/Chart.yaml | |
sed --in-place "s/CONTEXT_PLACEHOLDER/${{ env.LINODE_CLUSTER_CONTEXT }}/g" tests/integration/${{ inputs.install_profile }}.yaml | |
sed --in-place "s/OTOMI_VERSION_PLACEHOLDER/${GITHUB_REF##*/}/g" tests/integration/${{ inputs.install_profile }}.yaml | |
touch values-container-registry.yaml | |
# If a pipeline installs Otomi from the semver tag then pull container image from DockerHub | |
[[ ${GITHUB_REF##*/} =~ ^v[0-9].+$ ]] && exit 0 | |
# Pull image from cache registry | |
cat << EOF > values-container-registry.yaml | |
imageName: "${{ env.CACHE_REGISTRY }}/${{ env.CACHE_REPO }}" | |
imagePullSecretNames: | |
- reg-otomi-github | |
EOF | |
- name: Otomi install | |
if: ${{ inputs.install_profile != 'no-otomi' }} | |
env: | |
AZ_DNS: ${{ secrets.AZ_DNS }} | |
AZ_KMS: ${{ secrets.AZ_KMS }} | |
AZ_OIDC: ${{ secrets.AZ_OIDC }} | |
LETSENCRYPT_STAGING: ${{ secrets.LETSENCRYPT_STAGING }} | |
LETSENCRYPT_PRODUCTION: ${{ secrets.LETSENCRYPT_PRODUCTION }} | |
OTOMI_LICENSE: ${{ secrets.OTOMI_LICENSE }} | |
run: | | |
domainSuffix='' | |
touch values.yaml | |
[[ '${{ inputs.license }}' == 'yes' ]] && echo "$OTOMI_LICENSE" >> values.yaml | |
[[ '${{ inputs.dns }}' == 'az_dns' ]] && echo "$AZ_DNS" >> values.yaml && domainSuffix='--set cluster.domainSuffix=tst-${{ github.run_id }}.aks.redkubes.net' | |
[[ '${{ inputs.kms }}' == 'az_kms' ]] && echo "$AZ_KMS" >> values.yaml | |
[[ '${{ inputs.oidc }}' == 'az_oidc' ]] && echo "$AZ_OIDC" >> values.yaml | |
[[ '${{ inputs.certificate }}' == 'letsencrypt_staging' ]] && echo "$LETSENCRYPT_STAGING" >> values.yaml | |
[[ '${{ inputs.certificate }}' == 'letsencrypt_production' ]] && echo "$LETSENCRYPT_PRODUCTION" >> values.yaml | |
install_args="--wait --wait-for-jobs --timeout 90m0s otomi chart/apl \ | |
--values tests/integration/${{ inputs.install_profile }}.yaml \ | |
--values values-container-registry.yaml | |
--values values.yaml \ | |
--set cluster.provider=${{ inputs.cloud_provider }} | |
$domainSuffix" | |
[[ '${{ inputs.generate_password }}' == 'no' ]] && install_args="$install_args --set otomi.adminPassword=welcomeotomi" | |
helm install $install_args | |
- name: Gather k8s events on failure | |
if: failure() | |
run: | | |
kubectl get events --sort-by='.lastTimestamp' -A | |
- name: Gather k8s pods on failure | |
if: failure() | |
run: | | |
kubectl get pods -A -o wide | |
- name: Gather otomi logs on failure | |
if: failure() | |
run: | | |
kubectl logs jobs/otomi --tail 150 | |
- name: Gather otomi-e2e logs on failure | |
if: failure() | |
run: | | |
kubectl logs -n maintenance -l app.kubernetes.io/instance=job-e2e --tail 15000 | |
- name: Remove the test cluster | |
if: always() | |
run: | | |
[[ "${{ inputs.cluster_persistence }}" == "preserve" ]] && echo "The cluster ${{ env.LINODE_CLUSTER_NAME }} will NOT be destroyed!!" && exit 0 | |
linode-cli lke cluster-delete ${{ env.LINODE_CLUSTER_ID }} | |
- name: Slack Notification | |
if: always() | |
uses: rtCamp/action-slack-notify@v2 | |
env: | |
SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK }} | |
SLACK_CHANNEL: github-ci | |
SLACK_COLOR: ${{ job.status }} | |
SLACK_ICON: https://github.com/redkubes.png?size=48 | |
SLACK_TITLE: Scheduled integration tests | |
SLACK_USERNAME: RedKubesBot |