commit a12d48a9343ac94e62a364e6532af3a36a71e65b Author: Ansible Date: Tue Oct 6 22:29:42 2026 +0200 ci: update hello-world app files diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..ce7abba --- /dev/null +++ b/Dockerfile @@ -0,0 +1,7 @@ +FROM nginx:alpine + +COPY index.html /usr/share/nginx/html/index.html + +EXPOSE 80 + +ENTRYPOINT ["nginx", "-g", "daemon off;"] \ No newline at end of file diff --git a/Jenkinsfile b/Jenkinsfile new file mode 100644 index 0000000..82afa9f --- /dev/null +++ b/Jenkinsfile @@ -0,0 +1,129 @@ +// Build -> registry -> deploy, with nothing written back into this repository: Jenkins builds the image with +// rootless BuildKit (no privileged docker-in-docker) and pushes it to Harbor as : and :latest, using the +// project's push-only robot account. Argo CD Image Updater (ImageUpdater hello-world in namespace argocd) sees the +// new : tag in Harbor and points the Argo CD application at it. No commit back means no webhook loop, so +// no [skip ci] commits and no extra builds. +pipeline { + agent { + kubernetes { + label 'buildkit-agent' + yaml """ +apiVersion: v1 +kind: Pod +spec: + serviceAccountName: jenkins + containers: + - name: buildkit + image: moby/buildkit:v0.33.1-rootless + command: ['sleep'] + args: ['99d'] + env: + # Rootless BuildKit inside an unprivileged pod: no process sandbox of its own (the pod is the sandbox). + - name: BUILDKITD_FLAGS + value: --oci-worker-no-process-sandbox + # buildctl and buildkitd are Go: SSL_CERT_DIR adds the internal CA (/certs) to both trust stores, beside the + # image's own bundle -- the push talks TLS to Harbor's registry and to its token endpoint. + - name: SSL_CERT_DIR + value: /etc/ssl/certs:/certs + - name: HTTP_PROXY + value: http://10.0.10.10:3128 + - name: HTTPS_PROXY + value: http://10.0.10.10:3128 + - name: NO_PROXY + value: localhost,127.0.0.1,10.0.0.0/8,.cluster.local,.svc,.demo-06-10-2026.app.ariki.ai + - name: http_proxy + value: http://10.0.10.10:3128 + - name: https_proxy + value: http://10.0.10.10:3128 + - name: no_proxy + value: localhost,127.0.0.1,10.0.0.0/8,.cluster.local,.svc,.demo-06-10-2026.app.ariki.ai + securityContext: + runAsUser: 1000 + runAsGroup: 1000 + seccompProfile: + type: Unconfined + appArmorProfile: + type: Unconfined + volumeMounts: + # buildkitd.toml: trust the internal CA for harbor.demo-06-10-2026.app.ariki.ai (ConfigMap hello-world-buildkitd) + - name: buildkitd-config + mountPath: /home/user/.config/buildkit + - name: internal-root-ca + mountPath: /certs + readOnly: true + # Harbor push credentials: the push-only robot (ExternalSecret hello-world-harbor-push) + - name: harbor-push + mountPath: /home/user/.docker + readOnly: true + - name: buildkit-state + mountPath: /home/user/.local/share/buildkit + volumes: + - name: buildkitd-config + configMap: + name: hello-world-buildkitd + - name: internal-root-ca + secret: + secretName: internal-root-ca + - name: harbor-push + secret: + secretName: hello-world-harbor-push + items: + - key: .dockerconfigjson + path: config.json + - name: buildkit-state + emptyDir: {} +""" + } + } + + triggers { + GenericTrigger( + genericVariables: [ + [key: 'ref', value: '$.ref'], + [key: 'commit_message', value: '$.head_commit.message'] + ], + token: 'gitea-webhook' + ) + } + + environment { + HARBOR_HOST = "harbor.demo-06-10-2026.app.ariki.ai" + HARBOR_PROJECT = "demo-06-10-2026" + IMAGE_NAME = "${HARBOR_HOST}/${HARBOR_PROJECT}/hello-world" + } + + stages { + stage('Build and push image') { + steps { + script { + // The commit, not BUILD_NUMBER: unique even if the job is recreated, and it names the source + // the image was built from. The ImageUpdater only accepts tags of this shape. + env.IMAGE_TAG = env.GIT_COMMIT.take(12) + currentBuild.description = env.IMAGE_TAG + } + container('buildkit') { + // buildkitd.toml gives the daemon (blob push) the internal CA; the registry token is fetched by + // buildctl itself, which needs it too (--registry-auth-tlscontext), or the push fails with + // "failed to fetch oauth token ... x509: certificate signed by unknown authority". + sh ''' + buildctl-daemonless.sh build \ + --frontend dockerfile.v0 \ + --local context=. \ + --local dockerfile=. \ + --registry-auth-tlscontext "host=${HARBOR_HOST},ca=/certs/ca.crt" \ + --output "type=image,\\"name=${IMAGE_NAME}:${IMAGE_TAG},${IMAGE_NAME}:latest\\",push=true" + ''' + } + } + } + } + + post { + success { + echo "Pushed ${IMAGE_NAME}:${IMAGE_TAG}; Argo CD Image Updater deploys it to https://hello-world.demo-06-10-2026.app.ariki.ai" + } + failure { + echo "Pipeline failed at: ${env.STAGE_NAME}" + } + } +} diff --git a/index.html b/index.html new file mode 100644 index 0000000..9093078 --- /dev/null +++ b/index.html @@ -0,0 +1,9 @@ + + + + Hello World + + +

Hello from K3s cluster!

+ + \ No newline at end of file diff --git a/k8s/deployment.yml b/k8s/deployment.yml new file mode 100644 index 0000000..a0c5287 --- /dev/null +++ b/k8s/deployment.yml @@ -0,0 +1,35 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: hello-world + namespace: hello-world +spec: + replicas: 2 + selector: + matchLabels: + app: hello-world + template: + metadata: + labels: + app: hello-world + spec: + imagePullSecrets: + - name: harbor-pull-secret + containers: + - name: hello-world + image: harbor.demo-06-10-2026.app.ariki.ai/demo-06-10-2026/hello-world:latest + ports: + - containerPort: 80 + resources: + requests: + cpu: 50m + memory: 64Mi + limits: + cpu: 100m + memory: 128Mi + readinessProbe: + httpGet: + path: / + port: 80 + initialDelaySeconds: 5 + periodSeconds: 5 \ No newline at end of file diff --git a/k8s/ingress.yml b/k8s/ingress.yml new file mode 100644 index 0000000..4cc3652 --- /dev/null +++ b/k8s/ingress.yml @@ -0,0 +1,23 @@ +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: hello-world + namespace: hello-world +spec: + ingressClassName: nginx + tls: + - hosts: + - hello-world.demo-06-10-2026.app.ariki.ai + secretName: hello-world-tls + + rules: + - host: hello-world.demo-06-10-2026.app.ariki.ai + http: + paths: + - path: / + pathType: Prefix + backend: + service: + name: hello-world + port: + number: 80 diff --git a/k8s/kustomization.yml b/k8s/kustomization.yml new file mode 100644 index 0000000..ce0fca1 --- /dev/null +++ b/k8s/kustomization.yml @@ -0,0 +1,8 @@ +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization +# A Kustomize application, so Argo CD Image Updater can set the image tag as an Application override +# (spec.source.kustomize.images) instead of anyone committing it into deployment.yml. +resources: + - deployment.yml + - service.yml + - ingress.yml diff --git a/k8s/service.yml b/k8s/service.yml new file mode 100644 index 0000000..b287a5f --- /dev/null +++ b/k8s/service.yml @@ -0,0 +1,11 @@ +apiVersion: v1 +kind: Service +metadata: + name: hello-world + namespace: hello-world +spec: + selector: + app: hello-world + ports: + - port: 80 + targetPort: 80