Initial Gitea chart with smoke CI
Some checks failed
trash-ci / smoke (push) Failing after 11s

This commit is contained in:
bootstrap 2026-05-06 17:35:56 +03:00
commit 90fd37101b
21 changed files with 1630 additions and 0 deletions

View File

@ -0,0 +1,19 @@
name: trash-ci
on:
push:
branches:
- main
workflow_dispatch:
jobs:
smoke:
runs-on: linux-shell
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Smoke
run: |
echo "runner-ok"
pwd
ls -la

23
.helm/.helmignore Executable file
View File

@ -0,0 +1,23 @@
# Patterns to ignore when building packages.
# This supports shell glob matching, relative path matching, and
# negation (prefixed with !). Only one pattern per line.
.DS_Store
# Common VCS dirs
.git/
.gitignore
.bzr/
.bzrignore
.hg/
.hgignore
.svn/
# Common backup files
*.swp
*.bak
*.tmp
*.orig
*~
# Various IDEs
.project
.idea/
*.tmproj
.vscode/

9
.helm/Chart.lock Normal file
View File

@ -0,0 +1,9 @@
dependencies:
- name: universal-chart
repository: oci://cr.yandex/crp3ccidau046kdj8g9q/charts
version: 0.1.9
- name: postgresql-preprod
repository: oci://cr.yandex/crp3ccidau046kdj8g9q/charts
version: 16.4.8
digest: sha256:2fff848510e52c012ae7d941e38fbed2a017c94011e4b3dbca294e672f45a686
generated: "2026-05-06T12:00:56.461784+03:00"

14
.helm/Chart.yaml Normal file
View File

@ -0,0 +1,14 @@
apiVersion: v2
name: gitea
description: Gitea with act_runner, PostgreSQL, S3 backups, and restore jobs.
type: application
version: 1.0.0
appVersion: "1.22.6"
dependencies:
- name: universal-chart
repository: oci://cr.yandex/crp3ccidau046kdj8g9q/charts
version: 0.1.9
- name: postgresql-preprod
alias: postgresql
repository: oci://cr.yandex/crp3ccidau046kdj8g9q/charts
version: 16.4.8

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1,42 @@
restore:
enabled: true
name: gitea-restore
files:
enabled: true
postgresql:
enabled: true
host: postgresql
s3:
bucket: gitops-gitea
endpointUrl: https://storage.yandexcloud.net
region: ru-central1
giteaFilesKey: gitops-backups/gitea-files/gitea-files-2026-05-06-141255.tar.gz
postgresqlDumpKey: gitops-backups/postgresql/gitea-postgresql-2026-05-06-111204.sql.gz
scaleGitea:
enabled: true
serviceAccountName: gitea-restore
deploymentName: gitea
waitTimeoutSeconds: 300
scaleUp:
enabled: true
replicas: 1
waitForRestoreTimeoutSeconds: 1800
images:
kubectl:
repository: bitnamilegacy/kubectl
tag: "1.30.6"
pullPolicy: IfNotPresent
awsCli:
repository: amazon/aws-cli
tag: "2.15.57"
pullPolicy: IfNotPresent
postgres:
repository: postgres
tag: "17"
pullPolicy: IfNotPresent
busybox:
repository: busybox
tag: "1.36"
pullPolicy: IfNotPresent
verify:
enabled: true

View File

@ -0,0 +1,45 @@
{{- define "gitea.name" -}}
{{- default .Chart.Name .Values.global.nameOverride | trunc 63 | trimSuffix "-" -}}
{{- end -}}
{{- define "gitea.fullname" -}}
{{- if .Values.global.fullnameOverride -}}
{{- .Values.global.fullnameOverride | trunc 63 | trimSuffix "-" -}}
{{- else -}}
{{- include "gitea.name" . -}}
{{- end -}}
{{- end -}}
{{- define "gitea.chart" -}}
{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" -}}
{{- end -}}
{{- define "gitea.labels" -}}
helm.sh/chart: {{ include "gitea.chart" . }}
app.kubernetes.io/name: {{ include "gitea.name" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
app.kubernetes.io/managed-by: {{ .Release.Service }}
{{- with .Values.global.labels }}
{{ toYaml . }}
{{- end }}
{{- end -}}
{{- define "gitea.secretName" -}}
{{- default "gitea-secret" .Values.giteaSecret.name -}}
{{- end -}}
{{- define "gitea.giteaPvcName" -}}
{{- if .Values.persistence.gitea.existingClaim -}}
{{- .Values.persistence.gitea.existingClaim -}}
{{- else -}}
{{- default "gitea-data" .Values.persistence.gitea.name -}}
{{- end -}}
{{- end -}}
{{- define "gitea.runnerPvcName" -}}
{{- if .Values.persistence.runner.existingClaim -}}
{{- .Values.persistence.runner.existingClaim -}}
{{- else -}}
{{- default "gitea-runner-data" .Values.persistence.runner.name -}}
{{- end -}}
{{- end -}}

View File

@ -0,0 +1,170 @@
{{- if and .Values.backup.enabled .Values.backup.giteaFiles.enabled (eq (default "sidecar" .Values.backup.giteaFiles.mode) "cronjob") }}
apiVersion: batch/v1
kind: CronJob
metadata:
name: gitea-files-backup
namespace: {{ .Release.Namespace | quote }}
labels:
{{- include "gitea.labels" . | nindent 4 }}
spec:
schedule: {{ .Values.backup.giteaFiles.schedule | quote }}
{{- with .Values.backup.timeZone }}
timeZone: {{ . | quote }}
{{- end }}
concurrencyPolicy: Forbid
successfulJobsHistoryLimit: {{ .Values.backup.giteaFiles.successfulJobsHistoryLimit }}
failedJobsHistoryLimit: {{ .Values.backup.giteaFiles.failedJobsHistoryLimit }}
jobTemplate:
spec:
{{- with .Values.backup.giteaFiles.ttlSecondsAfterFinished }}
ttlSecondsAfterFinished: {{ . }}
{{- end }}
template:
spec:
restartPolicy: OnFailure
containers:
- name: backup
image: "{{ .Values.backup.giteaFiles.image.repository }}:{{ .Values.backup.giteaFiles.image.tag }}"
imagePullPolicy: {{ .Values.backup.giteaFiles.image.pullPolicy }}
command:
- /bin/sh
- -ec
- |
case "${AWS_ACCESS_KEY_ID:-}" in ""|GENERATED_*) echo "AWS_ACCESS_KEY_ID is not configured" >&2; exit 1;; esac
case "${AWS_SECRET_ACCESS_KEY:-}" in ""|GENERATED_*) echo "AWS_SECRET_ACCESS_KEY is not configured" >&2; exit 1;; esac
test -n "${S3_BUCKET:-}" || { echo "S3_BUCKET is not configured" >&2; exit 1; }
timestamp="$(date +%F-%H%M%S)"
archive_name="gitea-files-${timestamp}.tar.gz"
tar -C /data -czf "/tmp/${archive_name}" .
aws --endpoint-url "${AWS_ENDPOINT_URL}" \
s3 cp "/tmp/${archive_name}" "s3://${S3_BUCKET}/${S3_PREFIX}/gitea-files/${archive_name}"
env:
- name: S3_BUCKET
value: {{ .Values.backup.s3.bucket | quote }}
- name: S3_PREFIX
value: {{ .Values.backup.s3.prefix | quote }}
- name: AWS_DEFAULT_REGION
value: {{ .Values.backup.s3.region | quote }}
- name: AWS_ENDPOINT_URL
value: {{ .Values.backup.s3.endpointUrl | quote }}
- name: AWS_ACCESS_KEY_ID
valueFrom:
secretKeyRef:
name: {{ include "gitea.secretName" . | quote }}
key: aws-access-key-id
- name: AWS_SECRET_ACCESS_KEY
valueFrom:
secretKeyRef:
name: {{ include "gitea.secretName" . | quote }}
key: aws-secret-access-key
volumeMounts:
- name: gitea-data
mountPath: /data
readOnly: true
resources:
{{- toYaml .Values.backup.giteaFiles.resources | nindent 16 }}
volumes:
- name: gitea-data
persistentVolumeClaim:
claimName: {{ include "gitea.giteaPvcName" . | quote }}
readOnly: true
{{- end }}
{{- if and .Values.backup.enabled .Values.backup.postgresql.enabled }}
---
apiVersion: batch/v1
kind: CronJob
metadata:
name: gitea-postgresql-backup
namespace: {{ .Release.Namespace | quote }}
labels:
{{- include "gitea.labels" . | nindent 4 }}
spec:
schedule: {{ .Values.backup.postgresql.schedule | quote }}
{{- with .Values.backup.timeZone }}
timeZone: {{ . | quote }}
{{- end }}
concurrencyPolicy: Forbid
successfulJobsHistoryLimit: {{ .Values.backup.postgresql.successfulJobsHistoryLimit }}
failedJobsHistoryLimit: {{ .Values.backup.postgresql.failedJobsHistoryLimit }}
jobTemplate:
spec:
{{- with .Values.backup.postgresql.ttlSecondsAfterFinished }}
ttlSecondsAfterFinished: {{ . }}
{{- end }}
template:
spec:
restartPolicy: OnFailure
initContainers:
- name: dump
image: "{{ .Values.backup.postgresql.dumpImage.repository }}:{{ .Values.backup.postgresql.dumpImage.tag }}"
imagePullPolicy: {{ .Values.backup.postgresql.dumpImage.pullPolicy }}
command:
- /bin/sh
- -ec
- |
timestamp="$(date +%F-%H%M%S)"
dump_name="gitea-postgresql-${timestamp}.sql.gz"
until pg_isready -h "${POSTGRES_HOST}" -U "${POSTGRES_USER}" -d "${POSTGRES_DB}"; do
sleep 5
done
pg_dump -h "${POSTGRES_HOST}" -U "${POSTGRES_USER}" "${POSTGRES_DB}" | gzip -9 > "/backup/${dump_name}"
printf "%s" "${dump_name}" > /backup/dump-name
env:
- name: POSTGRES_HOST
value: {{ .Values.backup.postgresql.host | quote }}
- name: POSTGRES_DB
value: {{ .Values.postgresql.auth.database | quote }}
- name: POSTGRES_USER
value: {{ .Values.postgresql.auth.username | quote }}
- name: PGPASSWORD
valueFrom:
secretKeyRef:
name: {{ .Values.databaseSecret.name | quote }}
key: {{ .Values.databaseSecret.passwordKey | quote }}
volumeMounts:
- name: backup
mountPath: /backup
containers:
- name: upload
image: "{{ .Values.backup.postgresql.uploadImage.repository }}:{{ .Values.backup.postgresql.uploadImage.tag }}"
imagePullPolicy: {{ .Values.backup.postgresql.uploadImage.pullPolicy }}
command:
- /bin/sh
- -ec
- |
case "${AWS_ACCESS_KEY_ID:-}" in ""|GENERATED_*) echo "AWS_ACCESS_KEY_ID is not configured" >&2; exit 1;; esac
case "${AWS_SECRET_ACCESS_KEY:-}" in ""|GENERATED_*) echo "AWS_SECRET_ACCESS_KEY is not configured" >&2; exit 1;; esac
test -n "${S3_BUCKET:-}" || { echo "S3_BUCKET is not configured" >&2; exit 1; }
dump_name="$(cat /backup/dump-name)"
aws --endpoint-url "${AWS_ENDPOINT_URL}" \
s3 cp "/backup/${dump_name}" "s3://${S3_BUCKET}/${S3_PREFIX}/postgresql/${dump_name}"
env:
- name: S3_BUCKET
value: {{ .Values.backup.s3.bucket | quote }}
- name: S3_PREFIX
value: {{ .Values.backup.s3.prefix | quote }}
- name: AWS_DEFAULT_REGION
value: {{ .Values.backup.s3.region | quote }}
- name: AWS_ENDPOINT_URL
value: {{ .Values.backup.s3.endpointUrl | quote }}
- name: AWS_ACCESS_KEY_ID
valueFrom:
secretKeyRef:
name: {{ include "gitea.secretName" . | quote }}
key: aws-access-key-id
- name: AWS_SECRET_ACCESS_KEY
valueFrom:
secretKeyRef:
name: {{ include "gitea.secretName" . | quote }}
key: aws-secret-access-key
volumeMounts:
- name: backup
mountPath: /backup
resources:
{{- toYaml .Values.backup.postgresql.resources | nindent 16 }}
volumes:
- name: backup
emptyDir: {}
{{- end }}

View File

@ -0,0 +1,211 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: gitea
namespace: {{ .Release.Namespace | quote }}
labels:
{{- include "gitea.labels" . | nindent 4 }}
app: gitea
service: gitea
annotations:
owner: "platform"
spec:
replicas: {{ .Values.gitea.replicaCount }}
revisionHistoryLimit: 10
strategy:
type: Recreate
selector:
matchLabels:
app: gitea
template:
metadata:
labels:
{{- include "gitea.labels" . | nindent 8 }}
app: gitea
service: gitea
annotations:
traffic.sidecar.istio.io/excludeOutboundPorts: "4317,4318,9411"
spec:
containers:
- name: gitea
image: "{{ .Values.gitea.image.repository }}:{{ .Values.gitea.image.tag }}"
imagePullPolicy: {{ .Values.gitea.image.pullPolicy }}
ports:
- name: http
containerPort: {{ .Values.gitea.service.targetPort }}
protocol: TCP
- name: ssh
containerPort: 22
protocol: TCP
env:
- name: USER_UID
value: {{ .Values.gitea.uid | quote }}
- name: USER_GID
value: {{ .Values.gitea.gid | quote }}
- name: GITEA__database__DB_TYPE
value: postgres
- name: GITEA__database__HOST
value: postgresql:5432
- name: GITEA__database__NAME
value: {{ .Values.postgresql.auth.database | quote }}
- name: GITEA__database__USER
value: {{ .Values.postgresql.auth.username | quote }}
- name: GITEA__database__PASSWD
valueFrom:
secretKeyRef:
name: {{ .Values.databaseSecret.name | quote }}
key: {{ .Values.databaseSecret.passwordKey | quote }}
- name: GITEA__server__DOMAIN
value: {{ .Values.gitea.domain | quote }}
- name: GITEA__server__SSH_DOMAIN
value: {{ .Values.gitea.sshDomain | quote }}
- name: GITEA__server__ROOT_URL
value: {{ .Values.gitea.rootUrl | quote }}
- name: GITEA__server__HTTP_PORT
value: {{ .Values.gitea.httpPort | quote }}
- name: GITEA__server__SSH_PORT
value: {{ .Values.gitea.sshPort | quote }}
- name: GITEA__server__SSH_LISTEN_PORT
value: {{ .Values.gitea.sshListenPort | quote }}
- name: GITEA__security__INSTALL_LOCK
value: "true"
- name: GITEA__actions__ENABLED
value: "true"
- name: TZ
value: {{ .Values.gitea.timezone | quote }}
startupProbe:
tcpSocket:
port: http
initialDelaySeconds: {{ .Values.gitea.probes.startup.initialDelaySeconds }}
periodSeconds: {{ .Values.gitea.probes.startup.periodSeconds }}
timeoutSeconds: {{ .Values.gitea.probes.startup.timeoutSeconds }}
failureThreshold: {{ .Values.gitea.probes.startup.failureThreshold }}
readinessProbe:
tcpSocket:
port: http
initialDelaySeconds: {{ .Values.gitea.probes.readiness.initialDelaySeconds }}
periodSeconds: {{ .Values.gitea.probes.readiness.periodSeconds }}
livenessProbe:
tcpSocket:
port: http
initialDelaySeconds: {{ .Values.gitea.probes.liveness.initialDelaySeconds }}
periodSeconds: {{ .Values.gitea.probes.liveness.periodSeconds }}
volumeMounts:
- name: gitea-data
mountPath: /data
resources:
{{- toYaml .Values.gitea.resources | nindent 12 }}
{{- if and .Values.backup.enabled .Values.backup.giteaFiles.enabled (eq (default "sidecar" .Values.backup.giteaFiles.mode) "sidecar") }}
- name: gitea-files-archive
image: "{{ .Values.backup.giteaFiles.archiveImage.repository }}:{{ .Values.backup.giteaFiles.archiveImage.tag }}"
imagePullPolicy: {{ .Values.backup.giteaFiles.archiveImage.pullPolicy }}
command:
- /bin/sh
- -ec
- |
run_archive() {
timestamp="$(date +%F-%H%M%S)"
archive_name="gitea-files-${timestamp}.tar.gz"
tmp_path="/backup/${archive_name}.tmp"
archive_path="/backup/${archive_name}"
marker_path="/backup/${archive_name}.ready"
tar -C /data -czf "${tmp_path}" .
mv "${tmp_path}" "${archive_path}"
printf "%s" "${archive_name}" > "${marker_path}"
}
last_run_file="/backup/.gitea-files-backup-last-run"
if [ "${RUN_ON_START}" = "true" ]; then
run_archive || true
date +%F > "${last_run_file}"
fi
while true; do
current_time="$(date +%H:%M)"
current_day="$(date +%F)"
last_run="$(cat "${last_run_file}" 2>/dev/null || true)"
if [ "${current_time}" = "${BACKUP_TIME}" ] && [ "${last_run}" != "${current_day}" ]; then
if run_archive; then
echo "${current_day}" > "${last_run_file}"
fi
fi
sleep 60
done
env:
- name: BACKUP_TIME
value: {{ .Values.backup.giteaFiles.time | quote }}
- name: RUN_ON_START
value: {{ ternary "true" "false" .Values.backup.giteaFiles.runOnStart | quote }}
- name: TZ
value: {{ .Values.backup.timeZone | quote }}
volumeMounts:
- name: gitea-data
mountPath: /data
readOnly: true
- name: gitea-files-backup
mountPath: /backup
resources:
{{- toYaml .Values.backup.giteaFiles.resources | nindent 12 }}
- name: gitea-files-upload
image: "{{ .Values.backup.giteaFiles.uploadImage.repository }}:{{ .Values.backup.giteaFiles.uploadImage.tag }}"
imagePullPolicy: {{ .Values.backup.giteaFiles.uploadImage.pullPolicy }}
command:
- /bin/sh
- -ec
- |
credentials_ready() {
case "${AWS_ACCESS_KEY_ID:-}" in ""|GENERATED_*) echo "AWS_ACCESS_KEY_ID is not configured" >&2; return 1;; esac
case "${AWS_SECRET_ACCESS_KEY:-}" in ""|GENERATED_*) echo "AWS_SECRET_ACCESS_KEY is not configured" >&2; return 1;; esac
test -n "${S3_BUCKET:-}" || { echo "S3_BUCKET is not configured" >&2; return 1; }
}
while true; do
for marker_path in /backup/*.ready; do
[ -e "${marker_path}" ] || continue
archive_name="$(cat "${marker_path}")"
archive_path="/backup/${archive_name}"
[ -f "${archive_path}" ] || continue
if credentials_ready; then
aws --endpoint-url "${AWS_ENDPOINT_URL}" \
s3 cp "${archive_path}" "s3://${S3_BUCKET}/${S3_PREFIX}/gitea-files/${archive_name}"
rm -f "${archive_path}" "${marker_path}"
else
sleep 300
fi
done
sleep 60
done
env:
- name: S3_BUCKET
value: {{ .Values.backup.s3.bucket | quote }}
- name: S3_PREFIX
value: {{ .Values.backup.s3.prefix | quote }}
- name: AWS_DEFAULT_REGION
value: {{ .Values.backup.s3.region | quote }}
- name: AWS_ENDPOINT_URL
value: {{ .Values.backup.s3.endpointUrl | quote }}
- name: AWS_ACCESS_KEY_ID
valueFrom:
secretKeyRef:
name: {{ include "gitea.secretName" . | quote }}
key: aws-access-key-id
- name: AWS_SECRET_ACCESS_KEY
valueFrom:
secretKeyRef:
name: {{ include "gitea.secretName" . | quote }}
key: aws-secret-access-key
volumeMounts:
- name: gitea-files-backup
mountPath: /backup
resources:
{{- toYaml .Values.backup.giteaFiles.resources | nindent 12 }}
{{- end }}
volumes:
- name: gitea-data
persistentVolumeClaim:
claimName: {{ include "gitea.giteaPvcName" . | quote }}
{{- if and .Values.backup.enabled .Values.backup.giteaFiles.enabled (eq (default "sidecar" .Values.backup.giteaFiles.mode) "sidecar") }}
- name: gitea-files-backup
emptyDir: {}
{{- end }}

View File

@ -0,0 +1,18 @@
apiVersion: v1
kind: Service
metadata:
name: gitea
namespace: {{ .Release.Namespace | quote }}
labels:
{{- include "gitea.labels" . | nindent 4 }}
app: gitea
service: gitea
spec:
type: {{ .Values.gitea.service.type }}
selector:
app: gitea
ports:
- name: http
port: {{ .Values.gitea.service.port }}
targetPort: http
protocol: TCP

45
.helm/templates/pvc.yaml Normal file
View File

@ -0,0 +1,45 @@
{{- if and .Values.persistence.gitea.create (not .Values.persistence.gitea.existingClaim) }}
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: {{ include "gitea.giteaPvcName" . }}
namespace: {{ .Release.Namespace | quote }}
labels:
{{- include "gitea.labels" . | nindent 4 }}
{{- with .Values.persistence.gitea.annotations }}
annotations:
{{- toYaml . | nindent 4 }}
{{- end }}
spec:
accessModes:
{{- toYaml .Values.persistence.gitea.accessModes | nindent 4 }}
{{- with .Values.persistence.gitea.storageClass }}
storageClassName: {{ . | quote }}
{{- end }}
resources:
requests:
storage: {{ .Values.persistence.gitea.size }}
{{- end }}
{{- if and .Values.persistence.runner.create (not .Values.persistence.runner.existingClaim) }}
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: {{ include "gitea.runnerPvcName" . }}
namespace: {{ .Release.Namespace | quote }}
labels:
{{- include "gitea.labels" . | nindent 4 }}
{{- with .Values.persistence.runner.annotations }}
annotations:
{{- toYaml . | nindent 4 }}
{{- end }}
spec:
accessModes:
{{- toYaml .Values.persistence.runner.accessModes | nindent 4 }}
{{- with .Values.persistence.runner.storageClass }}
storageClassName: {{ . | quote }}
{{- end }}
resources:
requests:
storage: {{ .Values.persistence.runner.size }}
{{- end }}

View File

@ -0,0 +1,182 @@
{{- if and .Values.restore .Values.restore.enabled }}
{{- $restoreFiles := default false .Values.restore.files.enabled }}
{{- $restorePostgresql := default false .Values.restore.postgresql.enabled }}
{{- if not (or $restoreFiles $restorePostgresql) }}
{{- fail "restore.enabled=true requires restore.files.enabled=true or restore.postgresql.enabled=true" }}
{{- end }}
apiVersion: batch/v1
kind: Job
metadata:
name: {{ .Values.restore.name | quote }}
namespace: {{ .Release.Namespace | quote }}
labels:
{{- include "gitea.labels" . | nindent 4 }}
annotations:
helm.sh/hook: pre-install,pre-upgrade
helm.sh/hook-weight: "0"
helm.sh/hook-delete-policy: before-hook-creation
spec:
backoffLimit: 1
template:
spec:
restartPolicy: Never
initContainers:
- name: download
image: "{{ .Values.restore.images.awsCli.repository }}:{{ .Values.restore.images.awsCli.tag }}"
imagePullPolicy: {{ .Values.restore.images.awsCli.pullPolicy }}
command:
- /bin/sh
- -ec
- |
case "${AWS_ACCESS_KEY_ID:-}" in ""|GENERATED_*) echo "AWS_ACCESS_KEY_ID is not configured" >&2; exit 1;; esac
case "${AWS_SECRET_ACCESS_KEY:-}" in ""|GENERATED_*) echo "AWS_SECRET_ACCESS_KEY is not configured" >&2; exit 1;; esac
test -n "${S3_BUCKET:-}" || { echo "S3_BUCKET is not configured" >&2; exit 1; }
{{- if $restoreFiles }}
test -n "${GITEA_FILES_KEY}"
aws --endpoint-url "${AWS_ENDPOINT_URL}" s3 cp "s3://${S3_BUCKET}/${GITEA_FILES_KEY}" /restore/gitea-files.tar.gz
{{- end }}
{{- if $restorePostgresql }}
test -n "${POSTGRESQL_DUMP_KEY}"
aws --endpoint-url "${AWS_ENDPOINT_URL}" s3 cp "s3://${S3_BUCKET}/${POSTGRESQL_DUMP_KEY}" /restore/postgresql.sql.gz
{{- end }}
env:
- name: S3_BUCKET
value: {{ .Values.restore.s3.bucket | quote }}
- name: GITEA_FILES_KEY
value: {{ default "" .Values.restore.s3.giteaFilesKey | quote }}
- name: POSTGRESQL_DUMP_KEY
value: {{ default "" .Values.restore.s3.postgresqlDumpKey | quote }}
- name: AWS_DEFAULT_REGION
value: {{ .Values.restore.s3.region | quote }}
- name: AWS_ENDPOINT_URL
value: {{ .Values.restore.s3.endpointUrl | quote }}
- name: AWS_ACCESS_KEY_ID
valueFrom:
secretKeyRef:
name: {{ include "gitea.secretName" . | quote }}
key: aws-access-key-id
- name: AWS_SECRET_ACCESS_KEY
valueFrom:
secretKeyRef:
name: {{ include "gitea.secretName" . | quote }}
key: aws-secret-access-key
volumeMounts:
- name: restore
mountPath: /restore
{{- if $restoreFiles }}
- name: restore-files
image: "{{ .Values.restore.images.busybox.repository }}:{{ .Values.restore.images.busybox.tag }}"
imagePullPolicy: {{ .Values.restore.images.busybox.pullPolicy }}
command:
- /bin/sh
- -ec
- |
rm -rf /data/* /data/.[!.]* /data/..?*
tar -C /data -xzf /restore/gitea-files.tar.gz
volumeMounts:
- name: restore
mountPath: /restore
- name: gitea-data
mountPath: /data
{{- end }}
{{- if $restorePostgresql }}
- name: restore-postgresql
image: "{{ .Values.restore.images.postgres.repository }}:{{ .Values.restore.images.postgres.tag }}"
imagePullPolicy: {{ .Values.restore.images.postgres.pullPolicy }}
command:
- /bin/sh
- -ec
- |
export PGPASSWORD="${POSTGRES_PASSWORD}"
until pg_isready -h "${POSTGRES_HOST}" -U "${POSTGRES_USER}" -d postgres; do
sleep 5
done
escaped_user="$(printf "%s" "${POSTGRES_USER}" | sed 's/"/""/g')"
escaped_db="$(printf "%s" "${POSTGRES_DB}" | sed 's/"/""/g')"
if ! psql -h "${POSTGRES_HOST}" -U "${POSTGRES_USER}" -d postgres -tAc "select 1 from pg_database where datname = '${POSTGRES_DB}'" | grep -q 1; then
createdb -h "${POSTGRES_HOST}" -U "${POSTGRES_USER}" -O "${POSTGRES_USER}" "${POSTGRES_DB}"
fi
psql -h "${POSTGRES_HOST}" -U "${POSTGRES_USER}" -d postgres -v ON_ERROR_STOP=1 \
-c "ALTER DATABASE \"${escaped_db}\" OWNER TO \"${escaped_user}\";"
psql -h "${POSTGRES_HOST}" -U "${POSTGRES_USER}" -d "${POSTGRES_DB}" -v ON_ERROR_STOP=1 \
-c "DROP SCHEMA IF EXISTS public CASCADE; CREATE SCHEMA public AUTHORIZATION \"${POSTGRES_USER}\"; GRANT ALL ON SCHEMA public TO \"${POSTGRES_USER}\";"
gunzip -c /restore/postgresql.sql.gz | psql -h "${POSTGRES_HOST}" -U "${POSTGRES_USER}" -d "${POSTGRES_DB}" -v ON_ERROR_STOP=1
env:
- name: POSTGRES_HOST
value: {{ .Values.restore.postgresql.host | quote }}
- name: POSTGRES_DB
value: {{ .Values.postgresql.auth.database | quote }}
- name: POSTGRES_USER
value: {{ .Values.postgresql.auth.username | quote }}
- name: POSTGRES_PASSWORD
valueFrom:
secretKeyRef:
name: {{ .Values.databaseSecret.name | quote }}
key: {{ .Values.databaseSecret.passwordKey | quote }}
volumeMounts:
- name: restore
mountPath: /restore
{{- end }}
containers:
{{- if .Values.restore.verify.enabled }}
- name: verify
image: "{{ .Values.restore.images.postgres.repository }}:{{ .Values.restore.images.postgres.tag }}"
imagePullPolicy: {{ .Values.restore.images.postgres.pullPolicy }}
command:
- /bin/sh
- -ec
- |
{{- if $restoreFiles }}
test -d /data
objects="$(find /data -mindepth 1 -maxdepth 2 | head -n 1)"
test -n "${objects}"
{{- end }}
{{- if $restorePostgresql }}
tables="$(psql -h "${POSTGRES_HOST}" -U "${POSTGRES_USER}" -d "${POSTGRES_DB}" -tAc "select count(*) from information_schema.tables where table_schema = 'public'")"
test "${tables}" -gt 0
core_tables="$(psql -h "${POSTGRES_HOST}" -U "${POSTGRES_USER}" -d "${POSTGRES_DB}" -tAc "select count(*) from information_schema.tables where table_schema = 'public' and table_name in ('user', 'repository', 'version')")"
test "${core_tables}" -gt 0
echo "Gitea database restore verification passed: ${tables} public tables, ${core_tables} core tables"
{{- end }}
echo "Gitea restore verification passed"
env:
- name: POSTGRES_HOST
value: {{ .Values.restore.postgresql.host | quote }}
- name: POSTGRES_DB
value: {{ .Values.postgresql.auth.database | quote }}
- name: POSTGRES_USER
value: {{ .Values.postgresql.auth.username | quote }}
- name: PGPASSWORD
valueFrom:
secretKeyRef:
name: {{ .Values.databaseSecret.name | quote }}
key: {{ .Values.databaseSecret.passwordKey | quote }}
{{- if $restoreFiles }}
volumeMounts:
- name: gitea-data
mountPath: /data
readOnly: true
{{- end }}
{{- else }}
- name: done
image: "{{ .Values.restore.images.busybox.repository }}:{{ .Values.restore.images.busybox.tag }}"
imagePullPolicy: {{ .Values.restore.images.busybox.pullPolicy }}
command:
- /bin/sh
- -ec
- echo "Gitea restore completed"
{{- end }}
volumes:
- name: restore
emptyDir: {}
{{- if $restoreFiles }}
- name: gitea-data
persistentVolumeClaim:
claimName: {{ include "gitea.giteaPvcName" . | quote }}
{{- end }}
{{- end }}

View File

@ -0,0 +1,73 @@
{{- if and .Values.restore .Values.restore.enabled .Values.restore.scaleGitea.enabled }}
apiVersion: v1
kind: ServiceAccount
metadata:
name: {{ default .Values.restore.name .Values.restore.scaleGitea.serviceAccountName | quote }}
namespace: {{ .Release.Namespace | quote }}
labels:
{{- include "gitea.labels" . | nindent 4 }}
annotations:
helm.sh/hook: pre-install,pre-upgrade
helm.sh/hook-weight: "-30"
helm.sh/hook-delete-policy: before-hook-creation
---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: {{ default .Values.restore.name .Values.restore.scaleGitea.serviceAccountName | quote }}
namespace: {{ .Release.Namespace | quote }}
labels:
{{- include "gitea.labels" . | nindent 4 }}
annotations:
helm.sh/hook: pre-install,pre-upgrade
helm.sh/hook-weight: "-30"
helm.sh/hook-delete-policy: before-hook-creation
rules:
- apiGroups:
- apps
resources:
- deployments
- deployments/scale
verbs:
- get
- list
- watch
- patch
- update
- apiGroups:
- ""
resources:
- pods
verbs:
- get
- list
- watch
- apiGroups:
- batch
resources:
- jobs
verbs:
- get
- list
- watch
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: {{ default .Values.restore.name .Values.restore.scaleGitea.serviceAccountName | quote }}
namespace: {{ .Release.Namespace | quote }}
labels:
{{- include "gitea.labels" . | nindent 4 }}
annotations:
helm.sh/hook: pre-install,pre-upgrade
helm.sh/hook-weight: "-30"
helm.sh/hook-delete-policy: before-hook-creation
subjects:
- kind: ServiceAccount
name: {{ default .Values.restore.name .Values.restore.scaleGitea.serviceAccountName | quote }}
namespace: {{ .Release.Namespace | quote }}
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: Role
name: {{ default .Values.restore.name .Values.restore.scaleGitea.serviceAccountName | quote }}
{{- end }}

View File

@ -0,0 +1,38 @@
{{- if and .Values.restore .Values.restore.enabled .Values.restore.scaleGitea.enabled }}
apiVersion: batch/v1
kind: Job
metadata:
name: {{ printf "%s-scale-down" .Values.restore.name | quote }}
namespace: {{ .Release.Namespace | quote }}
labels:
{{- include "gitea.labels" . | nindent 4 }}
annotations:
helm.sh/hook: pre-install,pre-upgrade
helm.sh/hook-weight: "-10"
helm.sh/hook-delete-policy: before-hook-creation
spec:
backoffLimit: 1
template:
spec:
restartPolicy: Never
serviceAccountName: {{ default .Values.restore.name .Values.restore.scaleGitea.serviceAccountName | quote }}
containers:
- name: scale-down-gitea
image: "{{ .Values.restore.images.kubectl.repository }}:{{ .Values.restore.images.kubectl.tag }}"
imagePullPolicy: {{ .Values.restore.images.kubectl.pullPolicy }}
command:
- /bin/sh
- -ec
- |
kubectl -n "${K8S_NAMESPACE}" scale deployment "${GITEA_DEPLOYMENT_NAME}" --replicas=0
kubectl -n "${K8S_NAMESPACE}" wait --for=delete pod -l app=gitea --timeout="${WAIT_TIMEOUT_SECONDS}s" || true
env:
- name: K8S_NAMESPACE
valueFrom:
fieldRef:
fieldPath: metadata.namespace
- name: GITEA_DEPLOYMENT_NAME
value: {{ .Values.restore.scaleGitea.deploymentName | quote }}
- name: WAIT_TIMEOUT_SECONDS
value: {{ .Values.restore.scaleGitea.waitTimeoutSeconds | quote }}
{{- end }}

View File

@ -0,0 +1,42 @@
{{- if and .Values.restore .Values.restore.enabled .Values.restore.scaleGitea.enabled .Values.restore.scaleGitea.scaleUp.enabled }}
apiVersion: batch/v1
kind: Job
metadata:
name: {{ printf "%s-scale-up" .Values.restore.name | quote }}
namespace: {{ .Release.Namespace | quote }}
labels:
{{- include "gitea.labels" . | nindent 4 }}
annotations:
helm.sh/hook: pre-install,pre-upgrade
helm.sh/hook-weight: "10"
helm.sh/hook-delete-policy: before-hook-creation
spec:
backoffLimit: 1
template:
spec:
restartPolicy: Never
serviceAccountName: {{ default .Values.restore.name .Values.restore.scaleGitea.serviceAccountName | quote }}
containers:
- name: scale-up-gitea
image: "{{ .Values.restore.images.kubectl.repository }}:{{ .Values.restore.images.kubectl.tag }}"
imagePullPolicy: {{ .Values.restore.images.kubectl.pullPolicy }}
command:
- /bin/sh
- -ec
- |
kubectl -n "${K8S_NAMESPACE}" wait --for=condition=complete "job/${RESTORE_JOB_NAME}" --timeout="${RESTORE_WAIT_TIMEOUT_SECONDS}s"
kubectl -n "${K8S_NAMESPACE}" scale deployment "${GITEA_DEPLOYMENT_NAME}" --replicas="${REPLICAS_AFTER_RESTORE}"
env:
- name: K8S_NAMESPACE
valueFrom:
fieldRef:
fieldPath: metadata.namespace
- name: RESTORE_JOB_NAME
value: {{ .Values.restore.name | quote }}
- name: GITEA_DEPLOYMENT_NAME
value: {{ .Values.restore.scaleGitea.deploymentName | quote }}
- name: REPLICAS_AFTER_RESTORE
value: {{ .Values.restore.scaleGitea.scaleUp.replicas | quote }}
- name: RESTORE_WAIT_TIMEOUT_SECONDS
value: {{ .Values.restore.scaleGitea.scaleUp.waitForRestoreTimeoutSeconds | quote }}
{{- end }}

View File

@ -0,0 +1,43 @@
apiVersion: v1
kind: ConfigMap
metadata:
name: gitea-runner-config
namespace: {{ .Release.Namespace | quote }}
labels:
{{- include "gitea.labels" . | nindent 4 }}
data:
config.yaml: |-
{{ toYaml .Values.runner.config | nindent 4 }}
---
apiVersion: v1
kind: ConfigMap
metadata:
name: gitea-runner-entrypoint
namespace: {{ .Release.Namespace | quote }}
labels:
{{- include "gitea.labels" . | nindent 4 }}
data:
runner-entrypoint.sh: |-
#!/bin/sh
set -eu
case "${GITEA_RUNNER_REGISTRATION_TOKEN:-}" in
""|GENERATED_*)
echo "GITEA_RUNNER_REGISTRATION_TOKEN is not configured in Kubernetes secret"
echo "Patch secret gitea-secret key runner-registration-token and restart this pod"
exec tail -f /dev/null
;;
esac
if [ ! -f /data/.runner ]; then
echo "Registering Gitea runner ${GITEA_RUNNER_NAME} ..."
act_runner register --no-interactive \
--instance "${GITEA_INSTANCE_URL}" \
--token "${GITEA_RUNNER_REGISTRATION_TOKEN}" \
--name "${GITEA_RUNNER_NAME}" \
--labels "${GITEA_RUNNER_LABELS}" \
--config /config.yaml
fi
echo "Starting Gitea runner daemon"
exec act_runner daemon --config /config.yaml

View File

@ -0,0 +1,31 @@
{{- if .Values.giteaSecret.create }}
{{- $secretName := include "gitea.secretName" . }}
{{- $existingSecret := lookup "v1" "Secret" .Release.Namespace $secretName }}
{{- if not $existingSecret }}
{{- $runnerToken := printf "GENERATED_%s" (randAlphaNum 48) }}
{{- $awsAccessKeyId := printf "GENERATED_%s" (randAlphaNum 32) }}
{{- $awsSecretAccessKey := printf "GENERATED_%s" (randAlphaNum 64) }}
{{- $kubeconfig := "apiVersion: v1\nclusters: []\ncontexts: []\ncurrent-context: \"\"\nkind: Config\npreferences: {}\nusers: []" }}
{{- $dockerConfigJson := "{\"auths\":{}}" }}
apiVersion: v1
kind: Secret
metadata:
name: {{ $secretName }}
namespace: {{ .Release.Namespace | quote }}
labels:
{{- include "gitea.labels" . | nindent 4 }}
{{- if .Values.giteaSecret.keep }}
annotations:
helm.sh/resource-policy: keep
{{- end }}
type: Opaque
stringData:
runner-registration-token: {{ $runnerToken | quote }}
aws-access-key-id: {{ $awsAccessKeyId | quote }}
aws-secret-access-key: {{ $awsSecretAccessKey | quote }}
kubeconfig: |-
{{ $kubeconfig | nindent 4 }}
docker-config.json: |-
{{ $dockerConfigJson | nindent 4 }}
{{- end }}
{{- end }}

View File

@ -0,0 +1,24 @@
{{- if .Values.sshService.enabled }}
apiVersion: v1
kind: Service
metadata:
name: {{ .Values.sshService.name | quote }}
namespace: {{ .Release.Namespace | quote }}
labels:
{{- include "gitea.labels" . | nindent 4 }}
spec:
type: {{ .Values.sshService.type }}
{{- if and .Values.sshService.externalTrafficPolicy (or (eq .Values.sshService.type "NodePort") (eq .Values.sshService.type "LoadBalancer")) }}
externalTrafficPolicy: {{ .Values.sshService.externalTrafficPolicy }}
{{- end }}
selector:
app: gitea
ports:
- name: ssh
port: {{ .Values.sshService.port }}
targetPort: {{ .Values.sshService.targetPort }}
protocol: TCP
{{- if and (eq .Values.sshService.type "NodePort") .Values.sshService.nodePort }}
nodePort: {{ .Values.sshService.nodePort }}
{{- end }}
{{- end }}

521
.helm/values.yaml Normal file
View File

@ -0,0 +1,521 @@
global:
nameOverride: ""
fullnameOverride: ""
imagePullSecrets: []
labels: {}
giteaSecret:
create: true
name: gitea-secret
keep: true
databaseSecret:
name: postgresql-secret
adminPasswordKey: admin-password
passwordKey: user-password
persistence:
gitea:
create: true
name: gitea-data
existingClaim: ""
accessModes:
- ReadWriteOnce
storageClass: ""
size: 50Gi
annotations: {}
runner:
create: true
name: gitea-runner-data
existingClaim: ""
accessModes:
- ReadWriteOnce
storageClass: ""
size: 10Gi
annotations: {}
postgresql:
enabled: true
fullnameOverride: postgresql
global:
imagePullSecrets: []
security:
allowInsecureImages: true
postgresql:
auth:
username: gitea
database: gitea
existingSecret: postgresql-secret
secretKeys:
adminPasswordKey: admin-password
userPasswordKey: user-password
replicationPasswordKey: replication-password
auth:
username: gitea
database: gitea
existingSecret: postgresql-secret
secretKeys:
adminPasswordKey: admin-password
userPasswordKey: user-password
replicationPasswordKey: replication-password
image:
repository: contour/postgresql
pullSecrets: []
primary:
podAntiAffinityPreset: ""
networkPolicy:
enabled: false
podSecurityContext:
enabled: false
containerSecurityContext:
enabled: false
persistence:
storageClass: ""
size: 20Gi
resources:
requests:
cpu: 100m
memory: 256Mi
limits:
cpu: 1000m
memory: 1Gi
metrics:
enabled: false
image:
pullSecrets: []
serviceMonitor:
enabled: false
prometheusRule:
enabled: false
volumePermissions:
image:
pullSecrets: []
sshService:
enabled: true
name: gitea-ssh
type: NodePort
port: 22
targetPort: 22
nodePort: 30222
externalTrafficPolicy: Cluster
runner:
config:
log:
level: info
runner:
file: /data/.runner
capacity: 2
insecure: false
timeout: 3h
cache:
enabled: true
dir: /data/cache
container:
network: ""
privileged: false
gitea:
replicaCount: 1
image:
repository: gitea/gitea
tag: "1.22.6"
pullPolicy: IfNotPresent
service:
type: ClusterIP
port: 3000
targetPort: 3000
uid: "1000"
gid: "1000"
domain: 158-160-253-227.nip.io
sshDomain: 158-160-253-227.nip.io
rootUrl: https://158-160-253-227.nip.io/
httpPort: "3000"
sshPort: "30222"
sshListenPort: "22"
timezone: Europe/Moscow
resources:
requests:
cpu: 200m
memory: 512Mi
limits:
cpu: 2000m
memory: 2Gi
probes:
startup:
initialDelaySeconds: 10
periodSeconds: 10
timeoutSeconds: 2
failureThreshold: 60
readiness:
initialDelaySeconds: 10
periodSeconds: 10
liveness:
initialDelaySeconds: 30
periodSeconds: 30
backup:
enabled: true
timeZone: Europe/Moscow
s3:
bucket: gitops-gitea
region: ru-central1
endpointUrl: https://storage.yandexcloud.net
prefix: gitops-backups
giteaFiles:
enabled: true
mode: sidecar
schedule: "30 2 * * *"
time: "02:30"
runOnStart: false
archiveImage:
repository: busybox
tag: "1.36"
pullPolicy: IfNotPresent
uploadImage:
repository: amazon/aws-cli
tag: "2.15.57"
pullPolicy: IfNotPresent
successfulJobsHistoryLimit: 3
failedJobsHistoryLimit: 3
ttlSecondsAfterFinished: 86400
resources:
requests:
cpu: 100m
memory: 128Mi
limits:
cpu: 1000m
memory: 1Gi
postgresql:
enabled: true
schedule: "45 2 * * *"
host: postgresql
dumpImage:
repository: postgres
tag: "17"
pullPolicy: IfNotPresent
uploadImage:
repository: amazon/aws-cli
tag: "2.15.57"
pullPolicy: IfNotPresent
successfulJobsHistoryLimit: 3
failedJobsHistoryLimit: 3
ttlSecondsAfterFinished: 86400
resources:
requests:
cpu: 100m
memory: 128Mi
limits:
cpu: 1000m
memory: 1Gi
universal-chart:
global:
env: _default
services:
gitea:
enabled: false
deployment:
enabled: true
name:
_default: gitea
replicaCount:
_default: 1
port:
_default: 3000
revisionHistoryLimit:
_default: 10
resources:
requests:
cpu:
_default: 200m
memory:
_default: 512Mi
limits:
cpu:
_default: 2000m
memory:
_default: 2Gi
probes:
startup:
enabled:
_default: true
type:
_default: tcpSocket
tcpSocket:
port:
_default: 3000
initialDelaySeconds:
_default: 10
periodSeconds:
_default: 10
timeoutSeconds:
_default: 2
failureThreshold:
_default: 60
liveness:
enabled:
_default: true
type:
_default: tcpSocket
tcpSocket:
port:
_default: 3000
initialDelaySeconds:
_default: 30
periodSeconds:
_default: 30
readiness:
enabled:
_default: true
type:
_default: tcpSocket
tcpSocket:
port:
_default: 3000
initialDelaySeconds:
_default: 10
periodSeconds:
_default: 10
image:
name:
_default: gitea/gitea:1.22.6
pullPolicy:
_default: IfNotPresent
imagePullSecrets:
enabled:
_default: false
name:
_default: dockerhub
service:
enabled: true
name:
_default: gitea
type:
_default: ClusterIP
portName:
_default: http
port:
_default: 3000
targetPort:
_default: http
envs:
- name: USER_UID
value:
_default: "1000"
- name: USER_GID
value:
_default: "1000"
- name: GITEA__database__DB_TYPE
value:
_default: postgres
- name: GITEA__database__HOST
value:
_default: postgresql:5432
- name: GITEA__database__NAME
value:
_default: gitea
- name: GITEA__database__USER
value:
_default: gitea
- name: GITEA__server__DOMAIN
value:
_default: 158-160-253-227.nip.io
- name: GITEA__server__SSH_DOMAIN
value:
_default: 158-160-253-227.nip.io
- name: GITEA__server__ROOT_URL
value:
_default: https://158-160-253-227.nip.io/
- name: GITEA__server__HTTP_PORT
value:
_default: "3000"
- name: GITEA__server__SSH_PORT
value:
_default: "30222"
- name: GITEA__server__SSH_LISTEN_PORT
value:
_default: "22"
- name: GITEA__security__INSTALL_LOCK
value:
_default: "true"
- name: GITEA__actions__ENABLED
value:
_default: "true"
- name: TZ
value:
_default: Europe/Moscow
secretEnvs:
- name: GITEA__database__PASSWD
secretName:
_default: postgresql-secret
secretKey:
_default: user-password
volumes:
_default:
- name: gitea-data
mountPath: /data
persistentVolumeClaim:
claimName:
_default: gitea-data
commitSha: ""
gitlabUri: ""
gitlabJobUrl: ""
owner: platform
gitea-ci-worker:
enabled: true
deployment:
enabled: true
name:
_default: gitea-ci-worker
replicaCount:
_default: 1
port:
_default: 8088
command:
_default:
- /bin/sh
- /runner-entrypoint.sh
revisionHistoryLimit:
_default: 10
resources:
requests:
cpu:
_default: 200m
memory:
_default: 256Mi
limits:
cpu:
_default: 2000m
memory:
_default: 2Gi
probes:
liveness:
enabled: false
readiness:
enabled: false
image:
name:
_default: gitea/act_runner:0.2.11
pullPolicy:
_default: IfNotPresent
imagePullSecrets:
enabled:
_default: false
name:
_default: dockerhub
service:
enabled: false
name:
_default: gitea-ci-worker
type:
_default: ClusterIP
portName:
_default: http
port:
_default: 8088
targetPort:
_default: http
envs:
- name: GITEA_INSTANCE_URL
value:
_default: http://gitea:3000/
- name: GITEA_RUNNER_NAME
value:
_default: registry01-runner
- name: GITEA_RUNNER_LABELS
value:
_default: linux-amd64:docker://node:20-bookworm,linux-shell:host
- name: DOCKER_HOST
value:
_default: unix:///var/run/docker.sock
- name: KUBECONFIG
value:
_default: /data/.kube/config
- name: KUBE_CONTEXT
value:
_default: yc-k8s-test
- name: AWS_DEFAULT_REGION
value:
_default: ru-central1
- name: AWS_ENDPOINT_URL
value:
_default: https://storage.yandexcloud.net
- name: S3_BUCKET
value:
_default: gitops-gitea
- name: S3_PREFIX
value:
_default: gitops-backups
secretEnvs:
- name: GITEA_RUNNER_REGISTRATION_TOKEN
secretName:
_default: gitea-secret
secretKey:
_default: runner-registration-token
- name: AWS_ACCESS_KEY_ID
secretName:
_default: gitea-secret
secretKey:
_default: aws-access-key-id
- name: AWS_SECRET_ACCESS_KEY
secretName:
_default: gitea-secret
secretKey:
_default: aws-secret-access-key
volumes:
_default:
- name: runner-data
mountPath: /data
persistentVolumeClaim:
claimName:
_default: gitea-runner-data
- name: runner-config
mountPath: /config.yaml
subPath: config.yaml
readOnly: true
configMap:
name: gitea-runner-config
items:
- key: config.yaml
path: config.yaml
- name: runner-entrypoint
mountPath: /runner-entrypoint.sh
subPath: runner-entrypoint.sh
readOnly: true
configMap:
name: gitea-runner-entrypoint
defaultMode: 493
items:
- key: runner-entrypoint.sh
path: runner-entrypoint.sh
- name: docker-config
mountPath: /root/.docker/config.json
subPath: config.json
readOnly: true
secret:
secretName: gitea-secret
items:
- key: docker-config.json
path: config.json
- name: kubeconfig
mountPath: /data/.kube/config
subPath: config
readOnly: true
secret:
secretName: gitea-secret
items:
- key: kubeconfig
path: config
- name: docker-sock
mountPath: /var/run/docker.sock
hostPath:
path: /var/run/docker.sock
type: Socket
commitSha: ""
gitlabUri: ""
gitlabJobUrl: ""
owner: platform

80
README.md Normal file
View File

@ -0,0 +1,80 @@
# Gitea Helm chart
Чарт лежит в `.helm`.
Зависимости:
- `universal-chart` `0.1.9` для `gitea-ci-worker`
- статический `Deployment` для `gitea`, потому что файловый backup должен жить sidecar-ом в том же pod-е, где смонтирован RWO PVC
- `postgresql-preprod` `16.4.8` из `oci://cr.yandex/crp3ccidau046kdj8g9q/charts`
HTTP-маршрутизации здесь нет: ни `Ingress`, ни `VirtualService`. VS управляется отдельным чартом.
## Секреты
В `values.yaml` нет чувствительных значений. Чарт создает `gitea-secret` через `lookup`: если Secret уже существует, значения сохраняются; если нет, создаются сгенерированные заглушки. С заглушками runner и S3 backup не заработают, пока Secret не будет заполнен реальными данными.
Перед установкой можно создать Secret отдельно:
```bash
kubectl create namespace gitea --dry-run=client -o yaml | kubectl apply -f -
kubectl -n gitea create secret generic gitea-secret \
--from-literal=runner-registration-token="$GITEA_RUNNER_REGISTRATION_TOKEN" \
--from-literal=aws-access-key-id="$AWS_ACCESS_KEY_ID" \
--from-literal=aws-secret-access-key="$AWS_SECRET_ACCESS_KEY" \
--from-file=kubeconfig=./runner/kubeconfig/config \
--from-file=docker-config.json=./runner/docker-config/config.json \
--dry-run=client -o yaml | kubectl apply -f -
```
PostgreSQL пароль живет в `postgresql-secret`, который создает PostgreSQL chart и сохраняет через `helm.sh/resource-policy: keep`. Gitea читает ключ `user-password`.
## Установка
```bash
helm dependency update .helm
helm upgrade --install gitea .helm -n gitea --create-namespace
```
Если Secret создан уже после установки, перезапустить runner:
```bash
kubectl -n gitea rollout restart deploy/gitea-ci-worker
```
## Backup
Backup включен по умолчанию:
- `gitea-files-backup`: sidecar внутри pod `gitea`, каждый день в `02:30` архивирует `/data` и кладет в `s3://gitops-gitea/gitops-backups/gitea-files/`. Отдельного pod-а с mount PVC нет, поэтому не возникает second attach/Multi-Attach для `gitea-data`.
- `gitea-postgresql-backup`: каждый день в `02:45`, делает `pg_dump | gzip` и кладет в `s3://gitops-gitea/gitops-backups/postgresql/`
Оба backup-процесса используют S3-ключи из `gitea-secret`. Если там сгенерированные заглушки, backup завершится с понятной ошибкой и ничего не загрузит.
## Restore
Restore не включен в обычную установку. Это разовый Job, который включается отдельным values-файлом.
1. Взять имена объектов из S3:
```bash
aws --endpoint-url "$AWS_ENDPOINT_URL" s3 ls s3://gitops-gitea/gitops-backups/gitea-files/
aws --endpoint-url "$AWS_ENDPOINT_URL" s3 ls s3://gitops-gitea/gitops-backups/postgresql/
```
2. Скопировать `.helm/restore-values.example.yaml`, указать реальные `giteaFilesKey` и/или `postgresqlDumpKey`.
3. Запустить restore:
```bash
helm upgrade --install gitea .helm -n gitea -f restore-values.yaml
```
Режимы restore:
- полный restore: `restore.files.enabled=true` и `restore.postgresql.enabled=true`
- только файлы: `restore.files.enabled=true`, `restore.postgresql.enabled=false`
- только база: `restore.files.enabled=false`, `restore.postgresql.enabled=true`
Restore Job при `restore.scaleGitea.enabled=true` сначала масштабирует deployment `gitea` в `0` и ждёт удаления pod-а. Это нужно, чтобы restore файлов не монтировал RWO PVC одновременно с Gitea. После успешного completion отдельный `gitea-restore-scale-up` Job возвращает replicas обратно.