-
Notifications
You must be signed in to change notification settings - Fork 168
387 lines (365 loc) · 15.7 KB
/
integration.yml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
name: Deploy APL
on:
workflow_call:
inputs:
kubernetes_versions:
description: 'Kubernetes version'
type: string
default: "['1.31']"
install_profile:
description: 'APL installation profile'
type: string
default: minimal-with-team
domain_zone:
description: 'Select Domain Zone'
type: string
default: DNS-Integration
kms:
description: 'Should APL encrypt secrets in values repo (DNS or KMS is turned on)?'
type: string
default: age
certificate:
description: 'Select certificate issuer'
type: string
default: letsencrypt_production
is_pre_installed:
description: Fake if Otomi is pre-installed by Installer
type: string
default: 'false'
workflow_dispatch:
inputs:
kubernetes_versions:
description: 'Kubernetes version'
type: choice
options:
- "['1.29']"
- "['1.30']"
- "['1.31']"
default: "['1.31']"
install_profile:
description: APL installation profile
default: minimal-with-team
type: choice
options:
- minimal
- minimal-with-team
- full
- upgrade
- no-apl
domain_zone:
type: choice
description: 'Select Domain Zone'
options:
- Zone-1
- Zone-2
- Random
- DNS-Integration
kms:
type: choice
description: Should APL encrypt secrets in values repo (DNS or KMS is turned on)?
options:
- age
- no_kms
default: age
certificate:
type: choice
description: Select certificate issuer
options:
- gen_custom_ca
- letsencrypt_staging
- letsencrypt_production
default: letsencrypt_production
is_pre_installed:
type: choice
description: Fake if Otomi is pre-installed by Installer
options:
- 'true'
- 'false'
default: 'false'
env:
CACHE_REGISTRY: ghcr.io
CACHE_REPO: linode/apl-core
REPO: linode/apl-core
GIT_USER: svcAPLBot
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 }}
DEV_DOMAINS: ${{ secrets.DEV_DOMAINS }}
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 'kms: ${{ inputs.kms }}'
echo 'domain_zone: ${{ inputs.domain_zone }}'
echo 'certificate: ${{ inputs.certificate }}'
echo 'is_pre_installed: ${{ inputs.is_pre_installed }}'
preprocess-linode-input:
needs: preprocess-input
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 }}
- name: Check if cluster is running
run: |
case "${{ inputs.domain_zone }}" in
"Zone-1") LINODE_CLUSTER_NAME=${{ github.actor }}-1 ;;
"Zone-2") LINODE_CLUSTER_NAME=${{ github.actor }}-2 ;;
"Random") LINODE_CLUSTER_NAME=${{ github.actor }}-$RANDOM ;;
"DNS-Integration") LINODE_CLUSTER_NAME=apl-test-${{ inputs.install_profile }} ;;
esac
[[ ${{ inputs.install_profile }} == 'no-apl' ]] && LINODE_CLUSTER_NAME=$LINODE_CLUSTER_NAME-no-apl
if [[ $(linode-cli lke clusters-list --json | jq --arg name "$LINODE_CLUSTER_NAME" '[.[] | select(.label == $name)] | length > 0') == "true" ]]; then
echo "An LKE cluster with the same name ($LINODE_CLUSTER_NAME) already exists."
echo "Visit https://cloud.linode.com/kubernetes/clusters to delete your cluster"
echo "Exiting workflow..."
exit 1
fi
- 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-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: |
case "${{ inputs.domain_zone }}" in
"Zone-1") LINODE_CLUSTER_NAME=${{ github.actor }}-1 ;;
"Zone-2") LINODE_CLUSTER_NAME=${{ github.actor }}-2 ;;
"Random") LINODE_CLUSTER_NAME=${{ github.actor }}-$RANDOM ;;
"DNS-Integration") LINODE_CLUSTER_NAME=apl-test-${{ inputs.install_profile }} ;;
esac
[[ ${{ inputs.install_profile }} == 'no-apl' ]] && LINODE_CLUSTER_NAME=$LINODE_CLUSTER_NAME-no-apl
echo LINODE_CLUSTER_NAME=$LINODE_CLUSTER_NAME >> $GITHUB_ENV
- name: Determine exact k8s version
run: |
echo LINODE_K8S_VERSION=$(linode-cli lke versions-list --json | jq -ce --arg version "$(echo ${{ matrix.kubernetes_versions }} | sed -E 's/^([0-9]+\.[0-9])$/\10/')" '.[] | select(.id | tostring | startswith($version)) | .id') >> $GITHUB_ENV
- name: Determine domain name to use for scheduled integration test
env:
EDGEDNS_ZONE: ${{ secrets.EDGEDNS_ZONE }}
if: ${{ inputs.domain_zone == 'DNS-Integration' && inputs.install_profile != 'no-apl'}}
run: |
RAND=$(openssl rand -hex 4)
DOMAIN="integration-${RAND}.${EDGEDNS_ZONE}"
echo "::add-mask::$DOMAIN"
echo DOMAIN=$DOMAIN >> $GITHUB_ENV
- name: Determine domain name
if: ${{ inputs.domain_zone != 'DNS-Integration' && inputs.install_profile != 'no-apl' }}
env:
EDGEDNS_ZONE: ${{ secrets.EDGEDNS_ZONE }}
run: |
# Mapping of domain_zone to domain names
case "${{ inputs.domain_zone }}" in
"Zone-1") DOMAIN=$(jq -r '."${{ github.actor }}"[0]' <<< ${{ env.DEV_DOMAINS }}) ;;
"Zone-2") DOMAIN=$(jq -r '."${{ github.actor }}"[1]' <<< ${{ env.DEV_DOMAINS }}) ;;
"Random") DOMAIN="$(openssl rand -hex 4)$(date +"%d%m%y").${EDGEDNS_ZONE}" ;;
esac
echo "::add-mask::$DOMAIN"
echo DOMAIN=$DOMAIN >> $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
if: ${{ inputs.install_profile != 'no-apl' }}
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
if: ${{ inputs.install_profile != 'no-apl' }}
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
if: ${{ inputs.install_profile != 'no-apl' }}
uses: actions/checkout@v4
- name: Prepare APL chart
if: ${{ inputs.install_profile != 'no-apl' }}
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/CLUSTER_NAME_PLACEHOLDER/aplinstall${{ env.LINODE_CLUSTER_ID }}/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 APL 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: APL install
if: ${{ inputs.install_profile != 'no-apl' }}
env:
LETSENCRYPT_STAGING: ${{ secrets.LETSENCRYPT_STAGING }}
LETSENCRYPT_PRODUCTION: ${{ secrets.LETSENCRYPT_PRODUCTION }}
EDGEDNS_ACCESS_TOKEN: ${{ secrets.EDGEDNS_ACCESS_TOKEN }}
EDGEDNS_CLIENT_TOKEN: ${{ secrets.EDGEDNS_CLIENT_TOKEN }}
EDGEDNS_CLIENT_SECRET: ${{ secrets.EDGEDNS_CLIENT_SECRET }}
EDGEDNS_ZONE: ${{ secrets.EDGEDNS_ZONE }}
EDGEDNS_HOST: ${{ secrets.EDGEDNS_HOST }}
run: |
touch values.yaml
adminPassword="$(head /dev/urandom | tr -dc 'A-Za-z0-9' | head -c 24)"
[[ '${{ inputs.certificate }}' == 'letsencrypt_staging' ]] && echo "$LETSENCRYPT_STAGING" >> values.yaml
[[ '${{ inputs.certificate }}' == 'letsencrypt_production' ]] && echo "$LETSENCRYPT_PRODUCTION" >> values.yaml
[[ '${{ inputs.kms }}' == 'age' ]] && kms="--set kms.sops.provider=age"
if [[ '${{ inputs.is_pre_installed }}' == 'true' ]]; then
cat <<EOF >> values.yaml
otomi:
isPreInstalled: true
EOF
fi
if [[ '${{ inputs.kms }}' == 'age' ]]; then
cat <<EOF >> values.yaml
kms:
sops:
provider: age
EOF
fi
install_args="otomi chart/apl --wait --wait-for-jobs --timeout 90m0s \
--values tests/integration/${{ inputs.install_profile }}.yaml \
--values values-container-registry.yaml \
--values values.yaml \
--set cluster.provider=linode \
--set dns.domainFilters[0]=${{ env.DOMAIN }} \
--set dns.provider.akamai.clientSecret=${EDGEDNS_CLIENT_SECRET} \
--set dns.provider.akamai.host=${EDGEDNS_HOST} \
--set dns.provider.akamai.accessToken=${EDGEDNS_ACCESS_TOKEN} \
--set dns.provider.akamai.clientToken=${EDGEDNS_CLIENT_TOKEN} \
--set otomi.hasExternalDNS=true \
--set cluster.domainSuffix=${{ env.DOMAIN }} \
--set otomi.adminPassword=$adminPassword \
$kms"
helm install $install_args &
HELM_PID=$!
sleep 120
# While helm is installing we can crete the wildcard dns record
while true; do
PUB_IP=$(kubectl get svc ingress-nginx-platform-controller -n ingress -ojson | jq '.status.loadBalancer.ingress[0].ip' -r)
if [[ -n "$PUB_IP" ]]; then
echo "::add-mask::$PUB_IP"
echo PUB_IP=$PUB_IP >> $GITHUB_ENV
break
else
echo "Waiting for ingress-nginx-platform-controller IP..."
sleep 5
fi
done
pip3 install edgegrid-python requests
python3 bin/edgedns_A_record.py create "*.${DOMAIN}" $PUB_IP || \
(echo "Will try to recreate it" && \
python3 bin/edgedns_A_record.py delete "*.${DOMAIN}" && \
python3 bin/edgedns_A_record.py create "*.${DOMAIN}" $PUB_IP)
wait $HELM_PID
- 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 APL logs on failure
if: failure()
run: |
kubectl logs jobs/otomi-apl --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() && inputs.domain_zone == 'DNS-Integration' }}
run: |
linode-cli lke cluster-delete ${{ env.LINODE_CLUSTER_ID }}
- name: Delete Domain
if: ${{ always() && inputs.domain_zone == 'DNS-Integration' }}
env:
EDGEDNS_ACCESS_TOKEN: ${{ secrets.EDGEDNS_ACCESS_TOKEN }}
EDGEDNS_CLIENT_TOKEN: ${{ secrets.EDGEDNS_CLIENT_TOKEN }}
EDGEDNS_CLIENT_SECRET: ${{ secrets.EDGEDNS_CLIENT_SECRET }}
EDGEDNS_ZONE: ${{ secrets.EDGEDNS_ZONE }}
EDGEDNS_HOST: ${{ secrets.EDGEDNS_HOST }}
run: |
python3 bin/edgedns_A_record.py delete "*.${DOMAIN}"
- 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