diff --git a/templates/_helpers.tpl b/templates/_helpers.tpl index 97c286c..74cd89d 100644 --- a/templates/_helpers.tpl +++ b/templates/_helpers.tpl @@ -46,6 +46,21 @@ Create image name and tag used by the deployment. {{- end -}} {{- end -}} +{{/* +Create image name and tag used by the actions runner. +*/}} +{{- define "gitea.actions-image" -}} +{{- $registry := .Values.gitea.actions.runner.image.registry | default (.Values.global.imageRegistry | default .Values.image.registry) -}} +{{- $name := .Values.gitea.actions.runner.image.repository -}} +{{- $tag := .Values.gitea.actions.runner.image.tag -}} +{{- $rootless := ternary "-rootless" "" (or .Values.image.rootless .Values.gitea.actions.runner.image.rootless) -}} +{{- if $registry -}} + {{- printf "%s/%s:%s%s" $registry $name $tag $rootless -}} +{{- else -}} + {{- printf "%s:%s%s" $name $tag $rootless -}} +{{- end -}} +{{- end -}} + {{/* Docker Image Registry Secret Names evaluating values as templates */}} @@ -91,6 +106,26 @@ app.kubernetes.io/name: {{ include "gitea.name" . }} app.kubernetes.io/instance: {{ .Release.Name }} {{- end -}} +{{/* +Actions runner labels +*/}} +{{- define "gitea.actions.runner.labels" -}} +helm.sh/chart: {{ include "gitea.chart" . }} +app: actions-runner +{{ include "gitea.actions.runner.selectorLabels" . }} +app.kubernetes.io/version: {{ .Values.gitea.actions.runner.image.tag | quote }} +version: {{ .Values.gitea.actions.runner.image.tag | quote }} +app.kubernetes.io/managed-by: {{ .Release.Service }} +{{- end -}} + +{{/* +Actions runner selector labels +*/}} +{{- define "gitea.actions.runner.selectorLabels" -}} +app.kubernetes.io/name: actions-runner +app.kubernetes.io/instance: {{ .Release.Name }} +{{- end -}} + {{- define "postgresql.dns" -}} {{- printf "%s-postgresql.%s.svc.%s:%g" .Release.Name .Release.Namespace .Values.clusterDomain .Values.postgresql.global.postgresql.service.ports.postgresql -}} {{- end -}} @@ -199,6 +234,9 @@ https {{- if not (hasKey .Values.gitea.config "metrics") -}} {{- $_ := set .Values.gitea.config "metrics" dict -}} {{- end -}} + {{- if not (hasKey .Values.gitea.config "actions") -}} + {{- $_ := set .Values.gitea.config "actions" dict -}} + {{- end -}} {{- if not (hasKey .Values.gitea.config "database") -}} {{- $_ := set .Values.gitea.config "database" dict -}} {{- end -}} @@ -226,6 +264,9 @@ https {{- if not (hasKey .Values.gitea.config.metrics "ENABLED") -}} {{- $_ := set .Values.gitea.config.metrics "ENABLED" .Values.gitea.metrics.enabled -}} {{- end -}} + {{- if not (hasKey .Values.gitea.config.actions "ENABLED") -}} + {{- $_ := set .Values.gitea.config.actions "ENABLED" .Values.gitea.actions.enabled -}} + {{- end -}} {{- if .Values.memcached.enabled -}} {{- $_ := set .Values.gitea.config.cache "ENABLED" "true" -}} {{- $_ := set .Values.gitea.config.cache "ADAPTER" "memcache" -}} diff --git a/templates/gitea/init.yaml b/templates/gitea/init.yaml index 838460b..412f694 100644 --- a/templates/gitea/init.yaml +++ b/templates/gitea/init.yaml @@ -127,3 +127,62 @@ stringData: configure_oauth echo '==== END GITEA CONFIGURATION ====' + + generate_token.sh: |- + #!/usr/bin/env bash + + set -euo pipefail + + status=0 + + if [ -f /data/.runner ]; + then + echo 'Runner seems to already be configured. We are not creating a new token.' + else + echo 'Generating runner secret...' + forgejo forgejo-cli actions generate-secret > /tmp/runner-secret || { + echo 'Failed to write secret to /tmp/runner-secret!' + exit 1 + } + + # try + { + echo 'Registering generated secret...' + forgejo forgejo-cli actions register \ + --secret-file /tmp/runner-secret \ + --config /data/gitea/conf/app.ini + } || + # catch + { + echo 'Failed to register!' + status=1 + } + fi + + exit $status + + configure_runner.sh: |- + #!/bin/ash + + set -euo pipefail + + echo '==== BEGIN RUNNER CONFIGURATION ====' + + if [ -f /data/.runner ]; + then + echo 'Runner seems to already be configured. We are not creating a new token.' + else + forgejo-runner create-runner-file \ + --secret $(cat /tmp/runner-secret) \ + --instance http://{{ .Release.Name }}-http.{{ .Release.Namespace }}.svc.cluster.local:{{ .Values.service.http.port }} + + echo 'Moving runner file to /data directory' + + mv /.runner /data/.runner + + echo 'Creating symlink from /data/.runner to /.runner' + + ln -sf /data/.runner /.runner + fi + + echo '==== END RUNNER CONFIGURATION ====' diff --git a/templates/gitea/runner.yaml b/templates/gitea/runner.yaml new file mode 100644 index 0000000..c2662cf --- /dev/null +++ b/templates/gitea/runner.yaml @@ -0,0 +1,260 @@ +{{- if .Values.gitea.actions.runner.enabled -}} +apiVersion: apps/v1 +kind: StatefulSet +metadata: + name: actions-runner + labels: + {{- include "gitea.actions.runner.labels" . | nindent 4 }} +spec: + replicas: {{ .Values.gitea.actions.runner.replicas }} + selector: + matchLabels: + {{- include "gitea.actions.runner.selectorLabels" . | nindent 6 }} + serviceName: actions-runner + template: + metadata: + labels: + {{- include "gitea.actions.runner.labels" . | nindent 8 }} + spec: + {{- if .Values.schedulerName }} + schedulerName: "{{ .Values.schedulerName }}" + {{- end }} + {{- if .Values.priorityClassName }} + priorityClassName: "{{ .Values.priorityClassName }}" + {{- end }} + {{- include "gitea.images.pullSecrets" . | nindent 6 }} + securityContext: + {{- toYaml .Values.podSecurityContext | nindent 8 }} + initContainers: + # First wait for a Forgejo instance to come online. This is to guard + # against race conditions between forgejo and the actions runner when + # both are being set up for the first time. + - name: await-forgejo + image: alpine/curl + command: + - sh + args: + - -c + - | + retries=0 + max=3 + echo 'Waiting for Forgejo to come online before proceeding' + until $(curl --output /dev/null --silent --head --fail http://{{ include "gitea.fullname" . }}-http.{{ .Release.Namespace }}.svc.cluster.local:{{ .Values.service.http.port }}); + do + retries=$((retries+1)) + if [ $retries -gt $max ]; then + echo 'Max retries reached, exiting' + exit 1 + fi + echo "Checking again in 5 seconds. Retry count: ${retries} of ${max}"; + sleep 5; + done; + # We are recreating just enough of the app.ini so that we can invoke + # the forgejo-cli to register secrets and create runner files out the + # newly registered secrets. + - name: init-directories + image: "{{ include "gitea.image" . }}" + imagePullPolicy: {{ .Values.image.pullPolicy }} + command: ["/usr/sbin/init_directory_structure.sh"] + env: + - name: GITEA_APP_INI + value: /data/gitea/conf/app.ini + - name: GITEA_CUSTOM + value: /data/gitea + - name: GITEA_WORK_DIR + value: /data + - name: GITEA_TEMP + value: /tmp/gitea + {{- if .Values.statefulset.env }} + {{- toYaml .Values.statefulset.env | nindent 12 }} + {{- end }} + volumeMounts: + - name: init + mountPath: /usr/sbin + - name: temp + mountPath: /tmp + - name: data + mountPath: /data + {{- if .Values.persistence.subPath }} + subPath: {{ .Values.persistence.subPath }} + {{- end }} + securityContext: + {{- toYaml .Values.containerSecurityContext | nindent 12 }} + resources: + {{- toYaml .Values.initContainers.resources | nindent 12 }} + - name: init-app-ini + image: "{{ include "gitea.image" . }}" + imagePullPolicy: {{ .Values.image.pullPolicy }} + command: ["/usr/sbin/config_environment.sh"] + env: + - name: GITEA_APP_INI + value: /data/gitea/conf/app.ini + - name: GITEA_CUSTOM + value: /data/gitea + - name: GITEA_WORK_DIR + value: /data + - name: GITEA_TEMP + value: /tmp/gitea + {{- if .Values.statefulset.env }} + {{- toYaml .Values.statefulset.env | nindent 12 }} + {{- end }} + {{- if .Values.gitea.additionalConfigFromEnvs }} + {{- toYaml .Values.gitea.additionalConfigFromEnvs | nindent 12 }} + {{- end }} + volumeMounts: + - name: config + mountPath: /usr/sbin + - name: temp + mountPath: /tmp + - name: data + mountPath: /data + {{- if .Values.persistence.subPath }} + subPath: {{ .Values.persistence.subPath }} + {{- end }} + - name: inline-config-sources + mountPath: /env-to-ini-mounts/inlines/ + {{- range $idx, $value := .Values.gitea.additionalConfigSources }} + - name: additional-config-sources-{{ $idx }} + mountPath: "/env-to-ini-mounts/additionals/{{ $idx }}/" + {{- end }} + securityContext: + {{- toYaml .Values.containerSecurityContext | nindent 12 }} + resources: + {{- toYaml .Values.initContainers.resources | nindent 12 }} + - name: configure-gitea + image: "{{ include "gitea.image" . }}" + command: + - sh + args: + - -c + - /usr/sbin/generate_token.sh + imagePullPolicy: {{ .Values.image.pullPolicy }} + securityContext: + {{- /* By default this container runs as user 1000 unless otherwise stated */ -}} + {{- $csc := deepCopy .Values.containerSecurityContext -}} + {{- if not (hasKey $csc "runAsUser") -}} + {{- $_ := set $csc "runAsUser" 1000 -}} + {{- end -}} + {{- toYaml $csc | nindent 12 }} + env: + - name: GITEA_APP_INI + value: /data/gitea/conf/app.ini + - name: GITEA_CUSTOM + value: /data/gitea + - name: GITEA_WORK_DIR + value: /data + - name: GITEA_TEMP + value: /tmp/gitea + volumeMounts: + - name: init + mountPath: /usr/sbin + - name: temp + mountPath: /tmp + - name: data + mountPath: /data + {{- if .Values.persistence.subPath }} + subPath: {{ .Values.persistence.subPath }} + {{- end }} + resources: + {{- toYaml .Values.initContainers.resources | nindent 12 }} + - name: configure-runner + image: "{{ include "gitea.actions-image" . }}" + imagePullPolicy: {{ default .Values.image.pullPolicy .Values.gitea.actions.runner.image.pullPolicy }} + command: ["/usr/sbin/configure_runner.sh"] + volumeMounts: + - name: init + mountPath: /usr/sbin + - name: data + mountPath: /data + - name: temp + mountPath: /tmp + containers: + - name: {{ .Chart.Name }} + image: "{{ include "gitea.actions-image" . }}" + imagePullPolicy: {{ default .Values.image.pullPolicy .Values.gitea.actions.runner.image.pullPolicy }} + env: + - name: DOCKER_HOST + value: tcp://localhost:2376 + - name: DOCKER_CERT_PATH + value: /certs/client + - name: DOCKER_TLS_VERIFY + value: "1" + command: + - sh + args: + - -c + - | + if [ ! -L /.runner ]; then + ln -s /data/.runner /.runner; + fi; + /bin/forgejo-runner daemon + volumeMounts: + - name: certs + mountPath: /certs + - name: data + mountPath: /data + {{- if .Values.persistence.subPath }} + subPath: {{ .Values.persistence.subPath }} + {{- end }} + - name: temp + mountPath: /tmp + - name: daemon + image: docker:23.0.6-dind-rootless + env: + - name: DOCKER_TLS_CERTDIR + value: /certs + # NOTE: the container needs to run as root to configure dockerd, but + # dockerd itself is NOT run as root + securityContext: + privileged: true + volumeMounts: + - name: certs + mountPath: /certs + volumes: + - name: init + secret: + secretName: {{ include "gitea.fullname" . }}-init + defaultMode: 110 + - name: config + secret: + secretName: {{ include "gitea.fullname" . }} + defaultMode: 110 + {{- if gt (len .Values.extraVolumes) 0 }} + {{- toYaml .Values.extraVolumes | nindent 8 }} + {{- end }} + - name: inline-config-sources + secret: + secretName: {{ include "gitea.fullname" . }}-inline-config + {{- range $idx, $value := .Values.gitea.additionalConfigSources }} + - name: additional-config-sources-{{ $idx }} + {{- toYaml $value | nindent 10 }} + {{- end }} + - name: certs + emptyDir: {} + - name: temp + emptyDir: {} + volumeClaimTemplates: + - metadata: + name: data + {{- with .Values.persistence.annotations }} + annotations: + {{- range $key, $value := . }} + {{ $key }}: {{ $value }} + {{- end }} + {{- end }} + {{- with .Values.persistence.labels }} + labels: + {{- range $key, $value := . }} + {{ $key }}: {{ $value }} + {{- end }} + {{- end }} + spec: + accessModes: + {{- range .Values.persistence.accessModes }} + - {{ . | quote }} + {{- end }} + {{- include "gitea.persistence.storageClass" . | indent 8 }} + resources: + requests: + storage: {{ .Values.persistence.size | quote }} +{{- end }} diff --git a/values.yaml b/values.yaml index 8b615a7..2a99fed 100644 --- a/values.yaml +++ b/values.yaml @@ -299,6 +299,26 @@ gitea: password: r8sA8CPHD9!bt6d email: "gitea@local.domain" + ## @param gitea.actions.enabled Enable Forgejo Actions + ## @param gitea.actions.runner.enabled Enable automatic deployment of a runner + ## @param gitea.actions.runner.image.registry Image registry, e.g. gcr.io,docker.io + ## @param gitea.actions.runner.image.repository Image to start for this pod + ## @param gitea.actions.runner.image.tag Visit: [Image tag](https://code.forgejo.org/forgejo/-/packages/container/runner/versions). + ## @param gitea.actions.runner.image.pullPolicy Overrides the pull policy set globally for actions runners + ## @param gitea.actions.runner.image.rootless Determines whether we use a rootless container or not + ## @param gitea.actions.runner.replicas Number of replicas to automatically deploy + actions: + enabled: false + runner: + enabled: false + image: + registry: "code.forgejo.org" + repository: forgejo/runner + tag: "2.3.0" + pullPolicy: Always + rootless: false + replicas: 1 + ## @param gitea.metrics.enabled Enable Forgejo metrics ## @param gitea.metrics.serviceMonitor.enabled Enable Forgejo metrics service monitor metrics: