Skip to content

Commit 00653e6

Browse files
committed
fix(occ): preserve ExApp deploy options on update (#808)
Signed-off-by: Oleksander Piskun <oleksandr2088@icloud.com>
1 parent 47b612f commit 00653e6

5 files changed

Lines changed: 575 additions & 3 deletions

File tree

.github/workflows/tests-deploy-k8s.yml

Lines changed: 234 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -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

Comments
 (0)