blob: 02229ea0da7464749a737e7b841fd130f5ff4518 [file] [log] [blame]
Piotr Dobrowolski3a3b4252023-09-24 14:12:59 +02001/*
2
3 Deploy a Forgejo instance with PostgreSQL database and additional PV for git data.
4 Pre-provision the secrets with:
5
6 kubectl -n $KUBE_NAMESPACE create secret generic forgejo \
7 --from-literal=postgres_password=$(pwgen -s 24 1) \
8 --from-literal=secret_key=$(pwgen -s 128 1) \
9 --from-literal=admin_password=$(pwgen -s 128 1) \
10 --from-literal=oauth2_client_id=$SSO_CLIENT_ID \
11 --from-literal=oauth2_client_secret=$SSO_CLIENT_SECRET \
12 --from-literal=ldap_bind_dn=$LDAP_BIND_DN \
13 --from-literal=ldap_bind_password=$LDAP_BIND_PASSWORD \
14 --from-literal=smtp_password=$SMTP_PASSWORD
15
16 Import objectstore secret:
17
18 ceph_ns=ceph-waw3; ceph_pool=waw-hdd-redundant-3
19 kubectl -n $ceph_ns get secrets rook-ceph-object-user-${ceph_pool}-object-codehosting -o json | jq 'del(.metadata.namespace,.metadata.resourceVersion,.metadata.uid) | .metadata.creationTimestamp=null' | kubectl apply -f - -n $KUBE_NAMESPACE
20
21 Import oidc auth trigger:
22
23 kubectl -n $KUBE_NAMESPACE exec deploy/postgres -i -- psql -U forgejo forgejo < create-oidc-binding.sql
24
25*/
26
27local kube = import "../../kube/kube.libsonnet";
28local postgres = import "../../kube/postgres.libsonnet";
29
30{
31 local forgejo = self,
32 local cfg = forgejo.cfg,
33 cfg:: {
34 namespace: error "namespace must be set",
35 prefix: "",
36
37 image: "codeberg.org/forgejo/forgejo:1.20.5-0",
38 storageClassName: "waw-hdd-redundant-3",
39 storageSize: { git: "200Gi" },
40
41 admin_username: error "admin_username must be set",
42 admin_email: error "admin_email must be set",
43
44 # Forgejo configuration, roughly representing the structure of app.ini
45 instanceName: error "instanceName (e.g. 'Warsaw Hackerspace Forgejo') must be set",
46 runMode: "prod",
Piotr Dobrowolski9e438252023-10-08 22:08:24 +020047 altDomains: [],
Piotr Dobrowolski3a3b4252023-09-24 14:12:59 +020048 server: {
49 domain: error "domain (e.g. git.hackerspace.pl) must be set",
50 sshDomain: cfg.server.domain,
51 rootURL: "https://" + cfg.server.domain + "/",
52 offlineMode: "true",
53 },
54 security: {
55 installLock: "true",
56 },
57 service: {
58 disableRegistration: "false",
59 allowOnlyExternalRegistration: "true",
60 },
61
62 s3: {
63 endpoint: "rook-ceph-rgw-waw-hdd-redundant-3-object.ceph-waw3.svc:80", #{ secretKeyRef: {name: "rook-ceph-object-user-waw-hdd-redundant-3-object-codehosting", key: "Endpoint" } },
64 accessKey: { secretKeyRef: {name: "rook-ceph-object-user-waw-hdd-redundant-3-object-codehosting", key: "AccessKey" } },
65 secretKey: { secretKeyRef: {name: "rook-ceph-object-user-waw-hdd-redundant-3-object-codehosting", key: "SecretKey" } },
66 bucket: "codehosting",
67 },
68
69 mailer: {
70 from: "forgejo@hackerspace.pl",
71 host: "mail.hackerspace.pl",
72 port: 465,
73 user: "forgejo",
74 password: { secretKeyRef: { name: "forgejo", key: "smtp_password" } },
75 },
76 },
77
78 name(suffix):: cfg.prefix + suffix,
79 ns: kube.Namespace(cfg.namespace),
80
81 postgres: postgres {
82 cfg+: {
83 namespace: cfg.namespace,
84 appName: "forgejo",
85 database: "forgejo",
86 username: "forgejo",
87 password: { secretKeyRef: { name: "forgejo", key: "postgres_password" } },
88 storageClassName: cfg.storageClassName,
89 },
90 },
91
92 configMap: forgejo.ns.Contain(kube.ConfigMap(forgejo.name("forgejo"))) {
93 data: {
94 "app.ini.template": importstr 'app.ini.template',
95 "entrypoint.sh": importstr 'entrypoint.sh',
96 "bootstrap-auth.sh": importstr 'bootstrap-auth.sh',
97 },
98 },
99
100 dataVolume: forgejo.ns.Contain(kube.PersistentVolumeClaim(forgejo.name("forgejo"))) {
101 spec+: {
102 storageClassName: cfg.storageClassName,
103 accessModes: [ "ReadWriteOnce" ],
104 resources: {
105 requests: {
106 storage: cfg.storageSize.git,
107 },
108 },
109 },
110 },
111
112 forgejoCustom: forgejo.ns.Contain(kube.ConfigMap(forgejo.name("forgejo-custom"))) {
113 data: {
114 "signin_inner.tmpl": importstr 'signin_inner.tmpl',
115 },
116 },
117
118 statefulSet: forgejo.ns.Contain(kube.StatefulSet(forgejo.name("forgejo"))) {
119 spec+: {
120 replicas: 1,
121 template+: {
122 spec+: {
123 securityContext: {
124 runAsUser: 1000,
125 runAsGroup: 1000,
126 fsGroup: 1000,
127 },
128 volumes_: {
129 configmap: kube.ConfigMapVolume(forgejo.configMap),
130 custom: kube.ConfigMapVolume(forgejo.forgejoCustom),
131 data: kube.PersistentVolumeClaimVolume(forgejo.dataVolume),
132 empty: kube.EmptyDirVolume(),
133 },
134 containers_: {
135 server: kube.Container(forgejo.name("forgejo")) {
136 image: cfg.image,
137 command: [ "bash", "/usr/bin/entrypoint" ],
138 ports_: {
139 server: { containerPort: 3000 },
140 ssh: { containerPort: 22 },
141 },
142 readinessProbe: {
143 tcpSocket: {
144 port: "server",
145 },
146 initialDelaySeconds: 5,
147 periodSeconds: 5,
148 successThreshold: 1,
149 failureThreshold: 3
150 },
151 env_: {
152 APP_NAME: cfg.instanceName,
153 RUN_MODE: cfg.runMode,
154 INSTALL_LOCK: cfg.security.installLock,
155 SECRET_KEY: { secretKeyRef: { name: "forgejo", key: "secret_key" } },
156 DB_TYPE: "postgres",
157 DB_HOST: "postgres:5432",
158 DB_USER: forgejo.postgres.cfg.username,
159 DB_PASSWD: forgejo.postgres.cfg.password,
160 DB_NAME: forgejo.postgres.cfg.appName,
161 DOMAIN: cfg.server.domain,
162 SSH_DOMAIN: cfg.server.sshDomain,
163 SSH_LISTEN_PORT: "2222",
164 ROOT_URL: forgejo.cfg.server.rootURL,
165 DISABLE_REGISTRATION: cfg.service.disableRegistration,
166 ALLOW_ONLY_EXTERNAL_REGISTRATION: cfg.service.allowOnlyExternalRegistration,
167 OFFLINE_MODE: cfg.server.offlineMode,
168 USER_UID: "1000",
169 USER_GID: "1000",
170 GITEA_CUSTOM: "/custom",
171 MINIO_ENDPOINT: cfg.s3.endpoint,
172 MINIO_BUCKET: cfg.s3.bucket,
173 MINIO_ACCESS_KEY_ID: cfg.s3.accessKey,
174 MINIO_SECRET_ACCESS_KEY: cfg.s3.secretKey,
175 MAILER_FROM: cfg.mailer.from,
176 MAILER_HOST: cfg.mailer.host,
177 MAILER_PORT: cfg.mailer.port,
178 MAILER_USER: cfg.mailer.user,
179 MAILER_PASSWORD: cfg.mailer.password,
180 },
181 volumeMounts: [
182 { name: "configmap", subPath: "entrypoint.sh", mountPath: "/usr/bin/entrypoint" },
183 { name: "configmap", subPath: "app.ini.template", mountPath: "/etc/templates/app.ini" },
184 { name: "data", mountPath: "/data" },
185 { name: "empty", mountPath: "/custom" },
186 { name: "custom", subPath: "signin_inner.tmpl", mountPath: "/custom/templates/user/auth/signin_inner.tmpl" },
187 ],
188 },
189 },
190 initContainers: [
191 kube.Container(forgejo.name("forgejo-dbmigrate")) {
192 image: forgejo.statefulSet.spec.template.spec.containers_.server.image,
193 command: [ "bash", "/usr/bin/entrypoint", "/app/gitea/gitea", "migrate" ],
194 env_: forgejo.statefulSet.spec.template.spec.containers_.server.env_,
195 volumeMounts: forgejo.statefulSet.spec.template.spec.containers_.server.volumeMounts,
196 },
197 kube.Container(forgejo.name("forgejo-bootstrap-auth")) {
198 image: forgejo.statefulSet.spec.template.spec.containers_.server.image,
199 command: [
200 "bash", "/bootstrap-auth.sh"
201 ],
202 env_: forgejo.statefulSet.spec.template.spec.containers_.server.env_ + {
203 ADMIN_PASSWORD: { secretKeyRef: { name: "forgejo", key: "admin_password" } },
204 SSO_CLIENT_ID: { secretKeyRef: { name: "forgejo", key: "oauth2_client_id" } },
205 SSO_CLIENT_SECRET: { secretKeyRef: { name: "forgejo", key: "oauth2_client_secret" } },
206 LDAP_BIND_DN: { secretKeyRef: { name: "forgejo", key: "ldap_bind_dn" } },
207 LDAP_BIND_PASSWORD: { secretKeyRef: { name: "forgejo", key: "ldap_bind_password" } },
208 },
209 volumeMounts: forgejo.statefulSet.spec.template.spec.containers_.server.volumeMounts + [
210 { name: "configmap", subPath: "bootstrap-auth.sh", mountPath: "/bootstrap-auth.sh" },
211 ]
212 },
213 ],
214 },
215 },
216 },
217 },
218
219 svc: forgejo.ns.Contain(kube.Service(forgejo.name("forgejo"))) {
220 target_pod:: forgejo.statefulSet.spec.template,
221 spec+: {
222 ports: [
223 { name: "server", port: 80, targetPort: 3000, protocol: "TCP" },
224 { name: "ssh", port: 22, targetPort: 2222, protocol: "TCP" },
225 ],
226 },
227 },
228
229 ingress: forgejo.ns.Contain(kube.Ingress(forgejo.name("forgejo"))) {
230 metadata+: {
231 annotations+: {
232 "kubernetes.io/tls-acme": "true",
233 "cert-manager.io/cluster-issuer": "letsencrypt-prod",
234 "nginx.ingress.kubernetes.io/proxy-body-size": "0",
235 },
236 },
237 spec+: {
238 tls: [
Piotr Dobrowolski9e438252023-10-08 22:08:24 +0200239 { hosts: [cfg.server.domain] + cfg.altDomains, secretName: forgejo.name("acme") },
Piotr Dobrowolski3a3b4252023-09-24 14:12:59 +0200240 ],
241 rules: [
242 {
Piotr Dobrowolski9e438252023-10-08 22:08:24 +0200243 host: domain,
Piotr Dobrowolski3a3b4252023-09-24 14:12:59 +0200244 http: {
245 paths: [
246 { path: "/", backend: forgejo.svc.name_port },
247 ],
248 },
249 }
Piotr Dobrowolski9e438252023-10-08 22:08:24 +0200250 for domain in [cfg.server.domain] + cfg.altDomains
Piotr Dobrowolski3a3b4252023-09-24 14:12:59 +0200251 ],
252 },
253 },
254
255}