@@ -216,11 +216,244 @@ jobs:
216216 path : data/nextcloud.log
217217 if-no-files-found : warn
218218
219+ k8s-update-preserves-deploy-options :
220+ runs-on : ubuntu-22.04
221+ name : Update preserves deploy options (K8s)
222+ # Regression test for https://github.com/nextcloud/app_api/issues/808
223+ # on the K8s deploy path. Mirrors the Docker job in tests-deploy.yml.
224+
225+ services :
226+ postgres :
227+ image : ghcr.io/nextcloud/continuous-integration-postgres-14:latest # zizmor: ignore[unpinned-images]
228+ ports :
229+ - 4444:5432/tcp
230+ env :
231+ POSTGRES_USER : root
232+ POSTGRES_PASSWORD : rootpassword
233+ POSTGRES_DB : nextcloud
234+ options : --health-cmd pg_isready --health-interval 5s --health-timeout 2s --health-retries 5
235+
236+ steps :
237+ - name : Set app env
238+ run : echo "APP_NAME=${GITHUB_REPOSITORY##*/}" >> $GITHUB_ENV
239+
240+ - name : Checkout server
241+ uses : actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
242+ with :
243+ persist-credentials : false
244+ submodules : true
245+ repository : nextcloud/server
246+ ref : master
247+
248+ - name : Checkout AppAPI
249+ uses : actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
250+ with :
251+ persist-credentials : false
252+ path : apps/${{ env.APP_NAME }}
253+
254+ - name : Set up php
255+ uses : shivammathur/setup-php@accd6127cb78bee3e8082180cb391013d204ef9f # v2
256+ with :
257+ php-version : ' 8.3'
258+ extensions : bz2, ctype, curl, dom, fileinfo, gd, iconv, intl, json, libxml, mbstring, openssl, pcntl, posix, session, simplexml, xmlreader, xmlwriter, zip, zlib, pgsql, pdo_pgsql
259+ coverage : none
260+ ini-file : development
261+ env :
262+ GITHUB_TOKEN : ${{ secrets.GITHUB_TOKEN }}
263+
264+ - name : Check composer file existence
265+ id : check_composer
266+ uses : andstor/file-existence-action@558493d6c74bf472d87c84eab196434afc2fa029 # v2
267+ with :
268+ files : apps/${{ env.APP_NAME }}/composer.json
269+
270+ - name : Set up dependencies
271+ if : steps.check_composer.outputs.files_exists == 'true'
272+ working-directory : apps/${{ env.APP_NAME }}
273+ run : composer i
274+
275+ - name : Set up Nextcloud
276+ env :
277+ DB_PORT : 4444
278+ run : |
279+ mkdir data
280+ ./occ maintenance:install --verbose --database=pgsql --database-name=nextcloud --database-host=127.0.0.1 \
281+ --database-port=$DB_PORT --database-user=root --database-pass=rootpassword \
282+ --admin-user admin --admin-pass admin
283+ ./occ config:system:set loglevel --value=0 --type=integer
284+ ./occ config:system:set debug --value=true --type=boolean
285+ ./occ app:enable --force ${{ env.APP_NAME }}
286+
287+ - name : Install k3s
288+ run : |
289+ curl -sfL https://get.k3s.io | INSTALL_K3S_EXEC="--disable traefik --disable servicelb" sh -
290+ sudo chmod 644 /etc/rancher/k3s/k3s.yaml
291+ echo "KUBECONFIG=/etc/rancher/k3s/k3s.yaml" >> $GITHUB_ENV
292+
293+ - name : Wait for k3s and create namespace
294+ run : |
295+ kubectl wait --for=condition=Ready node --all --timeout=120s
296+ kubectl create namespace nextcloud-exapps
297+ NODE_IP=$(kubectl get node -o jsonpath='{.items[0].status.addresses[?(@.type=="InternalIP")].address}')
298+ echo "NODE_IP=${NODE_IP}" >> $GITHUB_ENV
299+
300+ - name : Configure Nextcloud for k3s networking
301+ run : |
302+ ./occ config:system:set overwrite.cli.url --value "http://${{ env.NODE_IP }}" --type=string
303+ ./occ config:system:set trusted_domains 1 --value "${{ env.NODE_IP }}"
304+
305+ - name : Create K8s service account for HaRP
306+ run : |
307+ kubectl -n nextcloud-exapps create serviceaccount harp-sa
308+ kubectl create clusterrolebinding harp-admin \
309+ --clusterrole=cluster-admin \
310+ --serviceaccount=nextcloud-exapps:harp-sa
311+ K3S_TOKEN=$(kubectl -n nextcloud-exapps create token harp-sa --duration=2h)
312+ echo "K3S_TOKEN=${K3S_TOKEN}" >> $GITHUB_ENV
313+
314+ - name : Pre-pull ExApp image into k3s
315+ run : sudo k3s ctr images pull ghcr.io/nextcloud/app-skeleton-python:latest
316+
317+ - name : Pull HaRP image
318+ run : docker pull ghcr.io/nextcloud/nextcloud-appapi-harp:latest
319+
320+ - name : Start HaRP with K8s backend
321+ run : |
322+ docker run --net host --name appapi-harp \
323+ -e HP_SHARED_KEY="${{ env.HP_SHARED_KEY }}" \
324+ -e NC_INSTANCE_URL="http://${{ env.NODE_IP }}" \
325+ -e HP_LOG_LEVEL="debug" \
326+ -e HP_K8S_ENABLED="true" \
327+ -e HP_K8S_API_SERVER="https://127.0.0.1:6443" \
328+ -e HP_K8S_BEARER_TOKEN="${{ env.K3S_TOKEN }}" \
329+ -e HP_K8S_NAMESPACE="nextcloud-exapps" \
330+ -e HP_K8S_VERIFY_SSL="false" \
331+ --restart unless-stopped \
332+ -d ghcr.io/nextcloud/nextcloud-appapi-harp:latest
333+
334+ - name : Start nginx proxy
335+ run : |
336+ docker run --net host --name nextcloud --rm \
337+ -v $(pwd)/apps/${{ env.APP_NAME }}/tests/simple-nginx-NOT-FOR-PRODUCTION.conf:/etc/nginx/conf.d/default.conf:ro \
338+ -d nginx
339+
340+ - name : Start Nextcloud
341+ run : PHP_CLI_SERVER_WORKERS=2 php -S 0.0.0.0:8080 &
342+
343+ - name : Wait for HaRP K8s readiness
344+ run : |
345+ for i in $(seq 1 30); do
346+ if curl -sf http://${{ env.NODE_IP }}:8780/exapps/app_api/info \
347+ -H "harp-shared-key: ${{ env.HP_SHARED_KEY }}" 2>/dev/null | grep -q '"kubernetes"'; then
348+ echo "HaRP is ready"
349+ exit 0
350+ fi
351+ sleep 2
352+ done
353+ docker logs appapi-harp
354+ exit 1
355+
356+ - name : Register K8s daemon and Skeleton v1 with user env vars
357+ run : |
358+ ./occ app_api:daemon:register \
359+ k8s_test "K8s Test" "kubernetes-install" "http" "${{ env.NODE_IP }}:8780" "http://${{ env.NODE_IP }}" \
360+ --harp --harp_shared_key "${{ env.HP_SHARED_KEY }}" \
361+ --k8s --k8s_expose_type=nodeport --set-default
362+ ./occ app_api:app:register app-skeleton-python k8s_test \
363+ --info-xml https://raw.githubusercontent.com/nextcloud/app-skeleton-python/main/appinfo/info.xml \
364+ --env='TEST_ENV_2=user_provided_value' --wait-finish
365+
366+ - name : Verify env vars on the freshly registered Deployment
367+ run : |
368+ kubectl -n nextcloud-exapps get deploy -l app.kubernetes.io/component=exapp -o json \
369+ | python3 -c '
370+ import json, sys
371+ items = json.load(sys.stdin)["items"]
372+ env = {}
373+ for item in items:
374+ for c in item["spec"]["template"]["spec"].get("containers", []):
375+ for e in c.get("env", []):
376+ env[e["name"]] = e.get("value", "")
377+ assert env.get("TEST_ENV_1") == "0", f"TEST_ENV_1 default missing after register: {env}"
378+ assert env.get("TEST_ENV_2") == "user_provided_value", f"TEST_ENV_2 user value missing after register: {env}"
379+ '
380+
381+ - name : Seed stray ex_deploy_options row for a second app
382+ # Without a second appid in the table, the Update.php bug from #808 is
383+ # latent: formatDeployOptions() with no $appId filter still produces
384+ # the single app's rows by luck. The `zz_` prefix ensures this stray row
385+ # iterates AFTER `app-skeleton-python`, so the last-wins flattening
386+ # actually clobbers the skeleton's env_vars entry.
387+ run : |
388+ php apps/${{ env.APP_NAME }}/tests/integration_helper.php \
389+ set-env zz_fake_second_app UNRELATED_VAR x
390+
391+ - name : Build v2 info.xml with bumped version
392+ run : |
393+ curl -sS https://raw.githubusercontent.com/nextcloud/app-skeleton-python/main/appinfo/info.xml \
394+ | sed 's#<version>[^<]*</version>#<version>999.0.0</version>#' > /tmp/info-v2.xml
395+ grep -q '<version>999.0.0</version>' /tmp/info-v2.xml || { echo "version bump failed"; exit 1; }
396+
397+ - name : Update ExApp
398+ run : |
399+ ./occ app_api:app:update app-skeleton-python --info-xml /tmp/info-v2.xml --wait-finish
400+
401+ - name : After update, env vars still present on the new Deployment
402+ run : |
403+ kubectl -n nextcloud-exapps get deploy -l app.kubernetes.io/component=exapp -o json \
404+ | python3 -c '
405+ import json, sys
406+ items = json.load(sys.stdin)["items"]
407+ env = {}
408+ for item in items:
409+ for c in item["spec"]["template"]["spec"].get("containers", []):
410+ for e in c.get("env", []):
411+ env[e["name"]] = e.get("value", "")
412+ assert env.get("TEST_ENV_1") == "0", f"#808 regression: TEST_ENV_1 lost on update; env={env}"
413+ assert env.get("TEST_ENV_2") == "user_provided_value", f"#808 regression: TEST_ENV_2 user value lost on update; env={env}"
414+ '
415+
416+ - name : Collect HaRP logs
417+ if : always()
418+ run : docker logs appapi-harp > harp.log 2>&1
419+
420+ - name : Collect K8s resources
421+ if : always()
422+ run : |
423+ kubectl -n nextcloud-exapps get all -o wide > k8s-resources.txt 2>&1 || true
424+ kubectl -n nextcloud-exapps describe pods > k8s-pods-describe.txt 2>&1 || true
425+
426+ - name : Upload HaRP logs
427+ if : always()
428+ uses : actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
429+ with :
430+ name : k8s_update_preserves_deploy_options_harp.log
431+ path : harp.log
432+ if-no-files-found : warn
433+
434+ - name : Upload K8s resources
435+ if : always()
436+ uses : actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
437+ with :
438+ name : k8s_update_preserves_deploy_options_resources.txt
439+ path : |
440+ k8s-resources.txt
441+ k8s-pods-describe.txt
442+ if-no-files-found : warn
443+
444+ - name : Upload NC logs
445+ if : always()
446+ uses : actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
447+ with :
448+ name : k8s_update_preserves_deploy_options_nextcloud.log
449+ path : data/nextcloud.log
450+ if-no-files-found : warn
451+
219452 tests-success :
220453 permissions :
221454 contents : none
222455 runs-on : ubuntu-22.04
223- needs : [k8s-deploy-nodeport]
456+ needs : [k8s-deploy-nodeport, k8s-update-preserves-deploy-options ]
224457 name : K8s-NodePort-Tests-OK
225458 steps :
226459 - run : echo "K8s NodePort tests passed successfully"
0 commit comments