Compare commits

..

No commits in common. "main" and "4.1.1" have entirely different histories.
main ... 4.1.1

24 changed files with 450 additions and 179 deletions

1
.gitignore vendored
View file

@ -27,4 +27,3 @@ public/js/
.eastwood .eastwood
.envrc

View file

@ -122,3 +122,11 @@ forgejo-backup-image-publish:
stage: image stage: image
script: script:
- cd infrastructure/backup && pyb image publish - cd infrastructure/backup && pyb image publish
# This is currently not needed
#forgejo-federated-image-publish:
# <<: *img
# <<: *tag_only
# stage: image
# script:
# - cd infrastructure/federated && pyb image publish

View file

@ -12,9 +12,9 @@ c4k-forgejo provides a k8s deployment file for forgejo containing:
* ingress having a letsencrypt managed certificate * ingress having a letsencrypt managed certificate
* postgres database * postgres database
* encrypted backup on S3 & restore * encrypted backup on S3 & restore
* monitoring on grafana-cloud * monitoring on graphana-cloud
c4k-forgejo is an example how to create efficient k8s one-shot deployments with https://repo.prod.meissa.de/meissa/c4k-common. c4k-forgejo is an example how to create efficient k8s one shot deployments with https://repo.prod.meissa.de/meissa/c4k-common.
## Try out ## Try out
@ -32,7 +32,7 @@ After having deployed the yaml-file generated by the c4k-forgejo module you need
* Adjust the settings according to your needs * Adjust the settings according to your needs
* Add the administrator's data (name, password and email) and submit the page. * Add the administrator's data (name, password and email) and submit the page.
* The required database will be created and the forgejo setup will be completed. * The required database will be created and the forgejo setup will be completed.
* The SSH-URL for a repo has the format: "ssh://git@domain:2222/[username]/[repo].git" * The SSH-URL for a repo has the format: "ssh://git@domain:2222/[username]/[repo].git
Example: "git clone ssh://git@repo.test.meissa.de:2222/myuser/c4k-forgejo.git" Example: "git clone ssh://git@repo.test.meissa.de:2222/myuser/c4k-forgejo.git"
### Add Impressum ### Add Impressum

View file

@ -23,6 +23,7 @@ def initialize(project):
"release_secondary_build_files": [ "release_secondary_build_files": [
"package.json", "package.json",
"infrastructure/backup/build.py", "infrastructure/backup/build.py",
"infrastructure/federated/build.py",
], ],
"release_artifact_server_url": "https://repo.prod.meissa.de", "release_artifact_server_url": "https://repo.prod.meissa.de",
"release_organisation": "meissa", "release_organisation": "meissa",
@ -149,12 +150,12 @@ def inst(project):
shell=True, shell=True,
check=True, check=True,
) )
package_native(project)
run( run(
"sudo install -m=755 target/uberjar/" + project.name + "-standalone.jar /usr/local/bin/" + project.name + "-standalone.jar", "sudo install -m=755 target/uberjar/" + project.name + "-standalone.jar /usr/local/bin/" + project.name + "-standalone.jar",
shell=True, shell=True,
check=True, check=True,
) )
package_native(project)
run( run(
"sudo install -m=755 target/graalvm/" + project.name + " /usr/local/bin/" + project.name + "", "sudo install -m=755 target/graalvm/" + project.name + " /usr/local/bin/" + project.name + "",
shell=True, shell=True,

View file

@ -1,8 +1,73 @@
# Backup Architecture details # Backup Architecture details
Use process documented at https://repo.prod.meissa.de/meissa/dda-backup/src/branch/main/docs/Backup.md ![](backup.svg)
Parameters are: * we use restic to produce small & encrypted backups
* backup is scheduled at `schedule: "10 23 * * *"`
* Forgejo stores files in `/data/gitea` and `/data/git/repositories`, these files are backed up.
* The postgres db is also backed up
1. **deployment-name**: forgejo ## Manual backup
2. **deployment-namespace**: forgejo
1. Scale down forgejo deployment:
`kubectl -n forgejo scale deployment forgejo --replicas=0`
2. apply backup-and-restore pod:
`kubectl -n forgejo scale deployment backup-restore --replicas=1`
3. exec into pod and execute backup pod (press tab to get your exact pod name)
`kubectl -n forgejo exec -it backup-restore-... -- /usr/local/bin/backup.bb`
4. remove backup-and-restore pod:
`kubectl -n forgejo scale deployment backup-restore --replicas=0`
5. Scale up forgejo deployment:
`kubectl -n forgejo scale deployment forgejo --replicas=1`
## Manual restore
1. Scale down forgejo deployment:
`kubectl -n forgejo scale deployment forgejo --replicas=0`
2. apply backup-and-restore pod:
`kubectl -n forgejo scale deployment backup-restore --replicas=1`
3. exec into pod and execute restore pod (press tab to get your exact pod name)
`kubectl -n forgejo exec -it backup-restore-... -- /usr/local/bin/restore.bb`
4. Start forgejo again:
`kubectl -n forgejo scale deployment forgejo --replicas=1`
5. remove backup-and-restore pod:
`kubectl -n forgejo scale deployment backup-restore --replicas=0`
## Change Password
1. Check restic-new-password env is set in backup deployment
```
kind: Deployment
metadata:
name: backup-restore
spec:
spec:
containers:
- name: backup-app
env:
- name: RESTIC_NEW_PASSWORD_FILE
value: /var/run/secrets/backup-secrets/restic-new-password
```
2. Add restic-new-password to secret
```
kind: Secret
metadata:
name: backup-secret
data:
restic-password: old
restic-new-password: new
```
3. Scale backup-restore deployment up:
`kubectl -n nextcloud scale deployment backup-restore --replicas=1`
4. exec into pod and execute restore pod
`kubectl -n nextcloud exec -it backup-restore -- change-password.bb`
5. Scale backup-restore deployment down:
`kubectl -n nextcloud scale deployment backup-restore --replicas=0`
6. Replace restic-password with restic-new-password in secret
```
kind: Secret
metadata:
name: backup-secret
data:
restic-password: new
```

View file

@ -7,8 +7,7 @@
* 1.21.1-0: https://codeberg.org/forgejo/forgejo/src/branch/forgejo/RELEASE-NOTES.md#1-21-1-0 * 1.21.1-0: https://codeberg.org/forgejo/forgejo/src/branch/forgejo/RELEASE-NOTES.md#1-21-1-0
* 7.0.0: https://codeberg.org/forgejo/forgejo/src/branch/forgejo/RELEASE-NOTES.md#7-0-0 * 7.0.0: https://codeberg.org/forgejo/forgejo/src/branch/forgejo/RELEASE-NOTES.md#7-0-0
* 8.0.0: https://codeberg.org/forgejo/forgejo/src/branch/forgejo/RELEASE-NOTES.md#8-0-0 * 8.0.0: https://codeberg.org/forgejo/forgejo/src/branch/forgejo/RELEASE-NOTES.md#8-0-0
* 9.0.0: https://codeberg.org/forgejo/forgejo/src/branch/forgejo/release-notes-published/9.0.0.md
*
## Preparations ## Preparations
1. Stop Forgejo Prod: `k scale deployment forgejo --replicas=0` 1. Stop Forgejo Prod: `k scale deployment forgejo --replicas=0`
@ -87,20 +86,6 @@
1. Scale up Forgejo Deployment: `k scale -n forgejo deployment forgejo --replicas=1` 1. Scale up Forgejo Deployment: `k scale -n forgejo deployment forgejo --replicas=1`
1. Check for errors: `k logs -n forgejo forgejo-...` 1. Check for errors: `k logs -n forgejo forgejo-...`
## Upgrade to 9.0.3 (no relevant breaking changes)
1. Scale down Forgejo Deployment: `k scale -n forgejo deployment forgejo --replicas=0`
1. Set version to `9.0.3` with `k edit -n forgejo deployment forgejo`
1. Scale up Forgejo Deployment: `k scale -n forgejo deployment forgejo --replicas=1`
1. Check for errors: `k logs -n forgejo forgejo-...`
## Upgrade to 10.0.3 (no relevant breaking changes)
1. Scale down Forgejo Deployment: `k scale -n forgejo deployment forgejo --replicas=0`
1. Set version to `10.0.3` with `k edit -n forgejo deployment forgejo`
1. Scale up Forgejo Deployment: `k scale -n forgejo deployment forgejo --replicas=1`
1. Check for errors: `k logs -n forgejo forgejo-...`
## Enable Federation ## Enable Federation
1. Scale down Forgejo Deployment: `k scale -n forgejo deployment forgejo --replicas=0` 1. Scale down Forgejo Deployment: `k scale -n forgejo deployment forgejo --replicas=0`

Binary file not shown.

Before

(image error) Size: 119 KiB

After

(image error) Size: 43 KiB

View file

@ -6,7 +6,7 @@ from ddadevops import *
name = "c4k-forgejo" name = "c4k-forgejo"
MODULE = "backup" MODULE = "backup"
PROJECT_ROOT_PATH = "../.." PROJECT_ROOT_PATH = "../.."
version = "6.0.1-dev" version = "4.1.1"
@init @init

View file

@ -1,4 +1,4 @@
FROM domaindrivenarchitecture/dda-backup:5.4.0 FROM domaindrivenarchitecture/dda-backup:5.3.0
ADD resources /tmp ADD resources /tmp
RUN /tmp/install.bb RUN /tmp/install.bb

View file

@ -5,7 +5,6 @@
'[dda.backup.config :as cfg] '[dda.backup.config :as cfg]
'[dda.backup.restic :as rc] '[dda.backup.restic :as rc]
'[dda.backup.postgresql :as pg] '[dda.backup.postgresql :as pg]
'[dda.backup.monitoring :as mon]
'[dda.backup.backup :as bak]) '[dda.backup.backup :as bak])
(def config (cfg/read-config "/usr/local/bin/config.edn")) (def config (cfg/read-config "/usr/local/bin/config.edn"))
@ -25,13 +24,6 @@
(bak/backup-file! (:file-config config)) (bak/backup-file! (:file-config config))
(bak/backup-db! (:db-config config))) (bak/backup-db! (:db-config config)))
(prepare!)
(try (restic-repo-init!)
(restic-repo-init!) (restic-backup!)
(mon/backup-start-metrics! (:db-config config))
(prepare!)
(restic-repo-init!)
(restic-backup!)
(mon/backup-success-metrics! (:db-config config))
(catch Exception e
(mon/backup-fail-metrics! (:db-config config))))

View file

@ -1,14 +1,12 @@
{:monitoring {:namespace "forgejo"} {:restic-repo {:password-file #env-or-file "RESTIC_PASSWORD_FILE"
:restic-repository #env-or-file "RESTIC_REPOSITORY"}
:restic-repo #merge [#ref [:monitoring]
{:password-file #env-or-file "RESTIC_PASSWORD_FILE"
:restic-repository #env-or-file "RESTIC_REPOSITORY"}]
:file-config #merge [#ref [:restic-repo] :file-config #merge [#ref [:restic-repo]
{:backup-path "files" {:backup-path "files"
:execution-directory "/var/backups/" :execution-directory "/var/backups/"
:files ["gitea/" "git/repositories/"]}] :files ["gitea/" "git/repositories/"]}]
:db-config #merge [#ref [:restic-repo] :db-config #merge [#ref [:restic-repo]
{:backup-path "pg-database" {:backup-path "pg-database"
:pg-host #env-or-file "POSTGRES_SERVICE" :pg-host #env-or-file "POSTGRES_SERVICE"

View file

@ -14,5 +14,4 @@
(in/install! "list-snapshots.bb") (in/install! "list-snapshots.bb")
(in/install! "change-password.bb") (in/install! "change-password.bb")
(in/install! "wait.bb") (in/install! "wait.bb")
(in/install! "monitoring.bb")
(ub/cleanup-container!) (ub/cleanup-container!)

View file

@ -1,11 +0,0 @@
#!/usr/bin/env bb
(require
'[dda.backup.monitoring :as mon])
(def config {:metrics {:kube_job_status_active 0
:kube_job_status_failed 1
:kube_job_status_succeeded 0
:kube_job_status_start_time (/ (System/currentTimeMillis) 1000)}})
(mon/send-metrics! config)

View file

@ -0,0 +1,55 @@
from os import environ
from datetime import datetime
from pybuilder.core import task, init
from ddadevops import *
name = 'c4k-forgejo'
MODULE = 'federated'
PROJECT_ROOT_PATH = '../..'
version = "4.1.1"
@init
def initialize(project):
image_tag = version
if "dev" in image_tag:
image_tag += datetime.now().strftime("%Y-%m-%d-%H-%M-%S")
input = {
"name": name,
"module": MODULE,
"stage": "notused",
"project_root_path": PROJECT_ROOT_PATH,
"build_types": ["IMAGE"],
"mixin_types": [],
"image_naming": "NAME_AND_MODULE",
"image_tag": f"{image_tag}",
}
project.build_depends_on("ddadevops>=4.7.0")
build = DevopsImageBuild(project, input)
build.initialize_build_dir()
@task
def image(project):
build = get_devops_build(project)
build.image()
@task
def drun(project):
build = get_devops_build(project)
build.drun()
@task
def test(project):
build = get_devops_build(project)
build.test()
@task
def publish(project):
build = get_devops_build(project)
build.dockerhub_login()
build.dockerhub_publish()

View file

@ -0,0 +1,78 @@
#Build stage
FROM docker.io/library/golang:1.21-alpine3.19 as build-env
ARG GOPROXY
ENV GOPROXY ${GOPROXY:-direct}
#ARG GITEA_VERSION
ARG TAGS="sqlite sqlite_unlock_notify"
ENV TAGS "bindata timetzdata $TAGS"
ARG CGO_EXTRA_CFLAGS
ENV FORGEJO_GIT_URL "https://codeberg.org/meissa/forgejo.git"
ENV FORGEJO_BRANCH "forgejo-federated-star"
#Build deps
RUN apk -U upgrade
RUN apk cache clean
RUN apk --no-cache add build-base git nodejs npm
#Setup repo
RUN git clone --single-branch --branch ${FORGEJO_BRANCH} ${FORGEJO_GIT_URL} ${GOPATH}/src/code.gitea.io/gitea
WORKDIR ${GOPATH}/src/code.gitea.io/gitea
#Checkout version if set
RUN make clean-all build
# Begin env-to-ini build
RUN go build contrib/environment-to-ini/environment-to-ini.go
# Run stage
FROM docker.io/library/alpine:3.18
LABEL maintainer="contact@forgejo.org"
EXPOSE 22 3000
RUN apk -U upgrade
RUN apk cache clean
RUN apk --no-cache add \
bash \
ca-certificates \
curl \
gettext \
git \
linux-pam \
openssh \
s6 \
sqlite \
su-exec \
gnupg
RUN addgroup \
-S -g 1000 \
git && \
adduser \
-S -H -D \
-h /data/git \
-s /bin/bash \
-u 1000 \
-G git \
git && \
echo "git:*" | chpasswd -e
ENV USER git
ENV GITEA_CUSTOM /data/gitea
VOLUME ["/data"]
ENTRYPOINT ["/usr/bin/entrypoint"]
CMD ["/bin/s6-svscan", "/etc/s6"]
COPY --from=build-env /go/src/code.gitea.io/gitea/docker/root /
COPY --from=build-env /go/src/code.gitea.io/gitea/gitea /app/gitea/gitea
COPY --from=build-env /go/src/code.gitea.io/gitea/environment-to-ini /usr/local/bin/environment-to-ini
COPY --from=build-env /go/src/code.gitea.io/gitea/contrib/autocompletion/bash_autocomplete /etc/profile.d/gitea_bash_autocomplete.sh
RUN chmod 755 /usr/bin/entrypoint /app/gitea/gitea /usr/local/bin/gitea /usr/local/bin/environment-to-ini
RUN chmod 755 /etc/s6/gitea/* /etc/s6/openssh/* /etc/s6/.s6-svscan/*
RUN chmod 644 /etc/profile.d/gitea_bash_autocomplete.sh

View file

@ -2,7 +2,7 @@
"name": "c4k-forgejo", "name": "c4k-forgejo",
"description": "Generate c4k yaml for a forgejo deployment.", "description": "Generate c4k yaml for a forgejo deployment.",
"author": "meissa GmbH", "author": "meissa GmbH",
"version": "6.0.1-SNAPSHOT", "version": "4.1.1",
"homepage": "https://gitlab.com/domaindrivenarchitecture/c4k-forgejo#readme", "homepage": "https://gitlab.com/domaindrivenarchitecture/c4k-forgejo#readme",
"repository": "https://www.npmjs.com/package/c4k-forgejo", "repository": "https://www.npmjs.com/package/c4k-forgejo",
"license": "APACHE2", "license": "APACHE2",

View file

@ -1,11 +1,11 @@
(defproject org.domaindrivenarchitecture/c4k-forgejo "6.0.1-SNAPSHOT" (defproject org.domaindrivenarchitecture/c4k-forgejo "4.1.1"
:description "forgejo c4k-installation package" :description "forgejo c4k-installation package"
:url "https://domaindrivenarchitecture.org" :url "https://domaindrivenarchitecture.org"
:license {:name "Apache License, Version 2.0" :license {:name "Apache License, Version 2.0"
:url "https://www.apache.org/licenses/LICENSE-2.0.html"} :url "https://www.apache.org/licenses/LICENSE-2.0.html"}
:dependencies [[org.clojure/clojure "1.12.0" :scope "provided"] :dependencies [[org.clojure/clojure "1.12.0" :scope "provided"]
[org.clojure/tools.reader "1.5.2"] [org.clojure/tools.reader "1.5.0"]
[org.domaindrivenarchitecture/c4k-common-clj "10.0.0"] [org.domaindrivenarchitecture/c4k-common-clj "9.0.1"]
[hickory "0.7.1" :exclusions [viebel/codox-klipse-theme]]] [hickory "0.7.1" :exclusions [viebel/codox-klipse-theme]]]
:target-path "target/%s/" :target-path "target/%s/"
:source-paths ["src/main/cljc" :source-paths ["src/main/cljc"
@ -24,9 +24,9 @@
:main dda.c4k-forgejo.uberjar :main dda.c4k-forgejo.uberjar
:uberjar-name "c4k-forgejo-standalone.jar" :uberjar-name "c4k-forgejo-standalone.jar"
:dependencies [[org.clojure/tools.cli "1.1.230"] :dependencies [[org.clojure/tools.cli "1.1.230"]
[ch.qos.logback/logback-classic "1.5.18" [ch.qos.logback/logback-classic "1.5.16"
:exclusions [com.sun.mail/javax.mail]] :exclusions [com.sun.mail/javax.mail]]
[org.slf4j/jcl-over-slf4j "2.0.17"] [org.slf4j/jcl-over-slf4j "2.0.16"]
[com.github.clj-easy/graal-build-time "1.0.5"]]}} [com.github.clj-easy/graal-build-time "1.0.5"]]}}
:release-tasks [["test"] :release-tasks [["test"]
["vcs" "assert-committed"] ["vcs" "assert-committed"]

View file

@ -4,7 +4,7 @@
"src/test/cljc" "src/test/cljc"
"src/test/cljs" "src/test/cljs"
"src/test/resources"] "src/test/resources"]
:dependencies [[org.domaindrivenarchitecture/c4k-common-cljs "10.0.0"] :dependencies [[org.domaindrivenarchitecture/c4k-common-cljs "9.0.1"]
[hickory "0.7.1"]] [hickory "0.7.1"]]
:builds {:frontend {:target :browser :builds {:frontend {:target :browser
:modules {:main {:init-fn dda.c4k-forgejo.browser/init}} :modules {:main {:init-fn dda.c4k-forgejo.browser/init}}

View file

@ -1,8 +1,8 @@
(ns dda.c4k-forgejo.uberjar (ns dda.c4k-forgejo.uberjar
(:gen-class) (:gen-class)
(:require (:require
[dda.c4k-common.uberjar :as uberjar] [dda.c4k-forgejo.core :as core]
[dda.c4k-forgejo.core :as core])) [dda.c4k-common.uberjar :as uberjar]))
(set! *warn-on-reflection* true) (set! *warn-on-reflection* true)

View file

@ -14,15 +14,16 @@
(def config-defaults {:namespace "forgejo" (def config-defaults {:namespace "forgejo"
:issuer "staging" :issuer "staging"
:deploy-federated "false"
:federation-enabled "false" :federation-enabled "false"
:forgejo-image "codeberg.org/forgejo/forgejo:10.0.3"
:db-name "forgejo" :db-name "forgejo"
:pv-storage-size-gb 5 :pv-storage-size-gb 5
:pvc-storage-class-name :local-path :pvc-storage-class-name ""
:postgres-image "postgres:14" :postgres-image "postgres:14"
:postgres-size :2gb :postgres-size :2gb})
:max-rate 10,
:max-concurrent-requests 5}) (def rate-limit-defaults {:max-rate 10, :max-concurrent-requests 5})
(def config? (s/keys :req-un [::forgejo/fqdn (def config? (s/keys :req-un [::forgejo/fqdn
::forgejo/mailer-from ::forgejo/mailer-from
@ -30,60 +31,57 @@
::forgejo/mailer-port ::forgejo/mailer-port
::forgejo/service-noreply-address] ::forgejo/service-noreply-address]
:opt-un [::forgejo/issuer :opt-un [::forgejo/issuer
::forgejo/deploy-federated
::forgejo/federation-enabled ::forgejo/federation-enabled
::forgejo/default-app-name ::forgejo/default-app-name
::forgejo/service-domain-whitelist ::forgejo/service-domain-whitelist
::forgejo/forgejo-image ::forgejo/forgejo-image-version-overwrite
::backup/restic-repository ::backup/restic-repository
::mon/mon-cfg])) ::mon/mon-cfg]))
(def auth? (s/keys :req-un [::postgres/postgres-db-user ::postgres/postgres-db-password (def auth? (s/keys :req-un [::postgres/postgres-db-user ::postgres/postgres-db-password
::forgejo/mailer-user ::forgejo/mailer-pw] ::forgejo/mailer-user ::forgejo/mailer-pw
::backup/aws-access-key-id ::backup/aws-secret-access-key]
:opt-un [::backup/restic-password :opt-un [::backup/restic-password
::backup/restic-new-password ::backup/restic-new-password
::backup/aws-access-key-id
::backup/aws-secret-access-key
::mon/mon-auth])) ::mon/mon-auth]))
(defn-spec config-objects p/map-or-seq? (defn-spec config-objects p/map-or-seq?
[config config?] [config config?]
(let [resolved-config (merge config-defaults config) (let [storage-class (if (contains? config :postgres-data-volume-path) :manual :local-path)]
storage-class (if (contains? resolved-config :postgres-data-volume-path) :manual :local-path)]
(map yaml/to-string (map yaml/to-string
(filter #(not (nil? %)) (filter #(not (nil? %))
(cm/concat-vec (cm/concat-vec
(ns/generate resolved-config) (ns/generate config)
[(postgres/generate-configmap resolved-config) [(postgres/generate-configmap config)
(when (contains? resolved-config :postgres-data-volume-path) (when (contains? config :postgres-data-volume-path)
(postgres/generate-persistent-volume (postgres/generate-persistent-volume (select-keys config [:postgres-data-volume-path :pv-storage-size-gb])))
(select-keys resolved-config [:postgres-data-volume-path :pv-storage-size-gb]))) (postgres/generate-pvc (merge config {:pvc-storage-class-name storage-class}))
(postgres/generate-pvc (merge resolved-config {:pvc-storage-class-name storage-class})) (postgres/generate-deployment config)
(postgres/generate-deployment resolved-config) (postgres/generate-service config)
(postgres/generate-service resolved-config) (forgejo/generate-deployment config)
(forgejo/generate-deployment resolved-config)
(forgejo/generate-service) (forgejo/generate-service)
(forgejo/generate-service-ssh) (forgejo/generate-service-ssh)
(forgejo/generate-data-volume resolved-config) (forgejo/generate-data-volume config)
(forgejo/generate-appini-env resolved-config)] (forgejo/generate-appini-env config)]
(forgejo/generate-ratelimit-ingress-and-cert resolved-config) ; this function has a vector as output (forgejo/generate-ratelimit-ingress-and-cert config) ; this function has a vector as output
(when (contains? resolved-config :restic-repository) (when (contains? config :restic-repository)
[(backup/generate-config resolved-config) [(backup/generate-config config)
(backup/generate-cron) (backup/generate-cron)
(backup/generate-backup-restore-deployment resolved-config)]) (backup/generate-backup-restore-deployment config)])
(when (contains? resolved-config :mon-cfg) (when (contains? config :mon-cfg)
(mon/generate-config))))))) (mon/generate-config)))))))
(defn-spec auth-objects p/map-or-seq? (defn-spec auth-objects p/map-or-seq?
[config config? [config config?
auth auth?] auth auth?]
(let [resolved-config (merge config-defaults config)] (map yaml/to-string
(map yaml/to-string (filter #(not (nil? %))
(filter #(not (nil? %)) (cm/concat-vec
(cm/concat-vec (ns/generate config)
(ns/generate resolved-config) [(postgres/generate-secret config auth)
[(postgres/generate-secret resolved-config auth) (forgejo/generate-secrets auth)]
(forgejo/generate-secrets auth)] (when (contains? config :restic-repository)
(when (contains? resolved-config :restic-repository) [(backup/generate-secret auth)])
[(backup/generate-secret auth)]) (when (contains? config :mon-cfg)
(when (contains? resolved-config :mon-cfg) (mon/generate-auth (:mon-cfg config) (:mon-auth auth)))))))
(mon/generate-auth (:mon-cfg resolved-config) (:mon-auth auth))))))))

View file

@ -32,13 +32,14 @@
(s/def ::default-app-name string?) (s/def ::default-app-name string?)
(s/def ::fqdn pred/fqdn-string?) (s/def ::fqdn pred/fqdn-string?)
(s/def ::deploy-federated boolean-string?)
(s/def ::federation-enabled boolean-string?) (s/def ::federation-enabled boolean-string?)
(s/def ::mailer-from pred/bash-env-string?) (s/def ::mailer-from pred/bash-env-string?)
(s/def ::mailer-host pred/bash-env-string?) (s/def ::mailer-host pred/bash-env-string?)
(s/def ::mailer-port pred/bash-env-string?) (s/def ::mailer-port pred/bash-env-string?)
(s/def ::service-domain-whitelist domain-list?) (s/def ::service-domain-whitelist domain-list?)
(s/def ::service-noreply-address string?) (s/def ::service-noreply-address string?)
(s/def ::forgejo-image string?) (s/def ::forgejo-image-version-overwrite string?)
(s/def ::mailer-user pred/bash-env-string?) (s/def ::mailer-user pred/bash-env-string?)
(s/def ::mailer-pw pred/bash-env-string?) (s/def ::mailer-pw pred/bash-env-string?)
(s/def ::issuer pred/letsencrypt-issuer?) (s/def ::issuer pred/letsencrypt-issuer?)
@ -47,7 +48,6 @@
(s/def ::max-concurrent-requests int?) (s/def ::max-concurrent-requests int?)
(s/def ::config (s/keys :req-un [::fqdn (s/def ::config (s/keys :req-un [::fqdn
::forgejo-image
::mailer-from ::mailer-from
::mailer-host ::mailer-host
::mailer-port ::mailer-port
@ -56,14 +56,30 @@
::max-rate ::max-rate
::max-concurrent-requests] ::max-concurrent-requests]
:opt-un [::issuer :opt-un [::issuer
::deploy-federated
::federation-enabled ::federation-enabled
::default-app-name ::default-app-name
::service-domain-whitelist ::service-domain-whitelist
])) ::forgejo-image-version-overwrite]))
(s/def ::auth (s/keys :req-un [::postgres/postgres-db-user (s/def ::auth (s/keys :req-un [::postgres/postgres-db-user ::postgres/postgres-db-password ::mailer-user ::mailer-pw]))
::postgres/postgres-db-password
::mailer-user ::mailer-pw])) (defn data-storage-by-volume-size
[total]
total)
;;TODO: remove unneccessaries, fedaration is merged
(def federated-image-name "domaindrivenarchitecture/c4k-forgejo-federated")
(def federated-image-version "latest")
(def non-federated-image-name "codeberg.org/forgejo/forgejo")
(def non-federated-image-version "8.0.3")
(defn-spec generate-image-str string?
[config ::config]
(let [{:keys [deploy-federated forgejo-image-version-overwrite]} config
deploy-federated-bool (boolean-from-string deploy-federated)]
(if deploy-federated-bool
(str federated-image-name ":" (or forgejo-image-version-overwrite federated-image-version))
(str non-federated-image-name ":" (or forgejo-image-version-overwrite non-federated-image-version)))))
#?(:cljs #?(:cljs
(defmethod yaml/load-resource :forgejo [resource-name] (defmethod yaml/load-resource :forgejo [resource-name]
@ -84,18 +100,18 @@
federation-enabled-bool (boolean-from-string federation-enabled)] federation-enabled-bool (boolean-from-string federation-enabled)]
(-> (->
(yaml/load-as-edn "forgejo/appini-env-configmap.yaml") (yaml/load-as-edn "forgejo/appini-env-configmap.yaml")
(cm/replace-all-matching "APPNAME" default-app-name) (cm/replace-all-matching-values-by-new-value "APPNAME" default-app-name)
(cm/replace-all-matching "FQDN" fqdn) (cm/replace-all-matching-values-by-new-value "FQDN" fqdn)
(cm/replace-all-matching "URL" (str "https://" fqdn)) (cm/replace-all-matching-values-by-new-value "URL" (str "https://" fqdn))
(cm/replace-all-matching "FROM" mailer-from) (cm/replace-all-matching-values-by-new-value "FROM" mailer-from)
(cm/replace-all-matching "MAILERHOST" mailer-host) (cm/replace-all-matching-values-by-new-value "MAILERHOST" mailer-host)
(cm/replace-all-matching "MAILERPORT" mailer-port) (cm/replace-all-matching-values-by-new-value "MAILERPORT" mailer-port)
(cm/replace-all-matching "WHITELISTDOMAINS" service-domain-whitelist) (cm/replace-all-matching-values-by-new-value "WHITELISTDOMAINS" service-domain-whitelist)
(cm/replace-all-matching "NOREPLY" service-noreply-address) (cm/replace-all-matching-values-by-new-value "NOREPLY" service-noreply-address)
(cm/replace-all-matching "IS_FEDERATED" (cm/replace-all-matching-values-by-new-value "IS_FEDERATED"
(if federation-enabled-bool (if federation-enabled-bool
"true" "true"
"false"))))) "false")))))
(defn-spec generate-secrets pred/map-or-seq? (defn-spec generate-secrets pred/map-or-seq?
[auth ::auth] [auth ::auth]
@ -124,17 +140,17 @@
(defn-spec generate-data-volume pred/map-or-seq? (defn-spec generate-data-volume pred/map-or-seq?
[config ::config] [config ::config]
(let [{:keys [volume-total-storage-size]} config] (let [{:keys [volume-total-storage-size]} config
data-storage-size (data-storage-by-volume-size volume-total-storage-size)]
(-> (->
(yaml/load-as-edn "forgejo/datavolume.yaml") (yaml/load-as-edn "forgejo/datavolume.yaml")
(cm/replace-all-matching "DATASTORAGESIZE" (str (str volume-total-storage-size) "Gi"))))) (cm/replace-all-matching "DATASTORAGESIZE" (str (str data-storage-size) "Gi")))))
(defn-spec generate-deployment pred/map-or-seq? (defn-spec generate-deployment pred/map-or-seq?
[config ::config] [config ::config]
(let [{:keys [forgejo-image]} config] (->
(-> (yaml/load-as-edn "forgejo/deployment.yaml")
(yaml/load-as-edn "forgejo/deployment.yaml") (cm/replace-all-matching "IMAGE_NAME" (generate-image-str config))))
(cm/replace-all-matching "IMAGE_NAME" forgejo-image))))
(defn generate-service (defn generate-service
[] []

View file

@ -1,9 +1,11 @@
(ns dda.c4k-forgejo.browser (ns dda.c4k-forgejo.browser
(:require (:require
[clojure.string :as st]
[clojure.tools.reader.edn :as edn] [clojure.tools.reader.edn :as edn]
[dda.c4k-forgejo.core :as core]
[dda.c4k-forgejo.forgejo :as forgejo]
[dda.c4k-common.browser :as br] [dda.c4k-common.browser :as br]
[dda.c4k-common.common :as cm] [dda.c4k-common.common :as cm]))
[dda.c4k-forgejo.core :as core]))
(defn generate-group (defn generate-group
[name [name
@ -24,32 +26,31 @@
[(assoc [(assoc
(br/generate-needs-validation) :content (br/generate-needs-validation) :content
(cm/concat-vec (cm/concat-vec
(br/generate-group
"config"
(br/generate-text-area
"config" "Your config.edn:"
"{:fqdn \"forgejo.your.domain\"
:mailer-from \"test@test.de\"
:mailer-host \"test.de\"
:mailer-port \"25\"
:deploy-federated \"false\"
:service-noreply-address \"no-reply@test.de\"
:volume-total-storage-size \"20\"
:mon-cfg {:cluster-name \"forgejo\"
:cluster-stage \"test\"
:grafana-cloud-url \"https://prometheus-prod-01-eu-west-0.grafana.net/api/prom/push\"}}"
"10"))
(generate-group (generate-group
"auth" "domain"
(cm/concat-vec
(br/generate-input-field "fqdn" "Your fqdn:" "repo.test.de")
(br/generate-input-field "mailer-from" "Your mailer email address:" "test@test.de")
(br/generate-input-field "mailer-host" "Your mailer host:" "test.de")
(br/generate-input-field "mailer-port" "Your mailer port:" "123")
(br/generate-input-field "service-noreply-address" "Your noreply domain:" "test.de")
(br/generate-input-field "deploy-federated" "(Optional) Deploy a federated version of forgejo:" "")
(br/generate-input-field "issuer" "(Optional) Your issuer prod/staging:" "")
(br/generate-input-field "app-name" "(Optional) Your app name:" "")
(br/generate-input-field "domain-whitelist" "(Optional) Domain whitelist for registration email-addresses:" "")))
(generate-group
"provider"
(cm/concat-vec
(br/generate-input-field "volume-total-storage-size" "Your forgejo volume-total-storage-size:" "20")))
(generate-group
"credentials"
(br/generate-text-area (br/generate-text-area
"auth" "Your auth.edn:" "auth" "Your auth.edn:"
"{:postgres-db-user \"forgejo\" "{:postgres-db-user \"forgejo\"
:postgres-db-password \"forgejo-db-password\" :postgres-db-password \"forgejo-db-password\"
:mailer-user \"test@test.de\" :mailer-user \"test@test.de\"
:mailer-pw \"mail-test-password\" :mailer-pw \"mail-test-password\"}"
:mon-auth {:grafana-cloud-user \"your-user-id\" "5"))
:grafana-cloud-password \"your-cloud-password\"}}"
"6"))
[(br/generate-br)] [(br/generate-br)]
(br/generate-button "generate-button" "Generate c4k yaml")))] (br/generate-button "generate-button" "Generate c4k yaml")))]
(br/generate-output "c4k-forgejo-output" "Your c4k deployment.yaml:" "25"))) (br/generate-output "c4k-forgejo-output" "Your c4k deployment.yaml:" "25")))
@ -61,15 +62,44 @@
:content :content
(generate-content)}) (generate-content)})
(defn config-from-document []
(let [issuer (br/get-content-from-element "issuer" :optional true)
app-name (br/get-content-from-element "app-name" :optional true)
domain-whitelist (br/get-content-from-element "domain-whitelist" :optional true)]
(merge
{:fqdn (br/get-content-from-element "fqdn")
:deploy-federated (br/get-content-from-element "deploy-federated")
:mailer-from (br/get-content-from-element "mailer-from")
:mailer-host (br/get-content-from-element "mailer-host")
:mailer-port (br/get-content-from-element "mailer-port")
:service-noreply-address (br/get-content-from-element "service-noreply-address")
:volume-total-storage-size (br/get-content-from-element "volume-total-storage-size" :deserializer js/parseInt)}
(when (not (st/blank? issuer))
{:issuer issuer})
(when (not (st/blank? app-name))
{:default-app-name app-name})
(when (not (st/blank? domain-whitelist))
{:service-domain-whitelist domain-whitelist}))))
(defn validate-all! [] (defn validate-all! []
(br/validate! "config" core/config? :deserializer edn/read-string) (br/validate! "fqdn" ::forgejo/fqdn)
(br/validate! "auth" core/auth? :deserializer edn/read-string) (br/validate! "mailer-from" ::forgejo/mailer-from)
(br/validate! "mailer-host" ::forgejo/mailer-host)
(br/validate! "mailer-port" ::forgejo/mailer-port)
(br/validate! "service-noreply-address" ::forgejo/service-noreply-address)
(br/validate! "deploy-federated" ::forgejo/deploy-federated :optional true)
(br/validate! "issuer" ::forgejo/issuer :optional true)
(br/validate! "app-name" ::forgejo/default-app-name :optional true)
(br/validate! "domain-whitelist" ::forgejo/service-domain-whitelist :optional true)
(br/validate! "volume-total-storage-size" ::forgejo/volume-total-storage-size :deserializer js/parseInt)
(br/validate! "auth" forgejo/auth? :deserializer edn/read-string)
(br/set-form-validated!)) (br/set-form-validated!))
(defn add-validate-listener [name] (defn add-validate-listener [name]
(-> (br/get-element-by-id name) (-> (br/get-element-by-id name)
(.addEventListener "blur" #(do (validate-all!))))) (.addEventListener "blur" #(do (validate-all!)))))
(defn init [] (defn init []
(br/append-hickory (generate-content-div)) (br/append-hickory (generate-content-div))
(let [config-only false (let [config-only false
@ -79,7 +109,7 @@
(.addEventListener "click" (.addEventListener "click"
#(do (validate-all!) #(do (validate-all!)
(-> (cm/generate-cm (-> (cm/generate-cm
(br/get-content-from-element "config" :deserializer edn/read-string) (config-from-document)
(br/get-content-from-element "auth" :deserializer edn/read-string) (br/get-content-from-element "auth" :deserializer edn/read-string)
core/config-defaults core/config-defaults
core/config-objects core/config-objects
@ -87,5 +117,14 @@
config-only config-only
auth-only) auth-only)
(br/set-output!)))))) (br/set-output!))))))
(add-validate-listener "config") (add-validate-listener "fqdn")
(add-validate-listener "deploy-federated")
(add-validate-listener "mailer-from")
(add-validate-listener "mailer-host")
(add-validate-listener "mailer-port")
(add-validate-listener "service-noreply-address")
(add-validate-listener "app-name")
(add-validate-listener "domain-whitelist")
(add-validate-listener "volume-total-storage-size")
(add-validate-listener "issuer")
(add-validate-listener "auth")) (add-validate-listener "auth"))

View file

@ -1,31 +1,19 @@
(ns dda.c4k-forgejo.core-test (ns dda.c4k-forgejo.core-test
(:require (:require
#?(:cljs [shadow.resource :as rc])
#?(:clj [clojure.test :refer [deftest is are testing run-tests]] #?(:clj [clojure.test :refer [deftest is are testing run-tests]]
:cljs [cljs.test :refer-macros [deftest is are testing run-tests]]) :cljs [cljs.test :refer-macros [deftest is are testing run-tests]])
[clojure.spec.alpha :as s] [clojure.spec.alpha :as s]
[clojure.spec.test.alpha :as st]
[dda.c4k-common.yaml :as yaml] [dda.c4k-common.yaml :as yaml]
[dda.c4k-forgejo.core :as cut] [dda.c4k-forgejo.core :as cut]))
#?(:cljs [dda.c4k-common.macros :refer-macros [inline-resources]])))
(st/instrument `cut/onfig-objects)
(st/instrument `cut/auth-objects)
#?(:cljs #?(:cljs
(defmethod yaml/load-resource :forgejo-test [resource-name] (defmethod yaml/load-resource :forgejo-test [resource-name]
(get (inline-resources "forgejo-test") resource-name))) (case resource-name
"forgejo-test/valid-auth.yaml" (rc/inline "forgejo-test/valid-auth.yaml")
"forgejo-test/valid-config.yaml" (rc/inline "forgejo-test/valid-config.yaml")
(throw (js/Error. "Undefined Resource!")))))
(deftest validate-valid-resources (deftest validate-valid-resources
(is (s/valid? cut/config? (yaml/load-as-edn "forgejo-test/valid-config.yaml"))) (is (s/valid? cut/config? (yaml/load-as-edn "forgejo-test/valid-config.yaml")))
(is (s/valid? cut/auth? (yaml/load-as-edn "forgejo-test/valid-auth.yaml")))) (is (s/valid? cut/auth? (yaml/load-as-edn "forgejo-test/valid-auth.yaml"))))
(deftest test-whole-generation
(is (= 35
(count
(cut/config-objects
(yaml/load-as-edn "forgejo-test/valid-config.yaml")))))
(is (= 5
(count
(cut/auth-objects
(yaml/load-as-edn "forgejo-test/valid-config.yaml")
(yaml/load-as-edn "forgejo-test/valid-auth.yaml"))))))

View file

@ -12,6 +12,40 @@
(st/instrument `cut/generate-ingress) (st/instrument `cut/generate-ingress)
(st/instrument `cut/generate-secrets) (st/instrument `cut/generate-secrets)
(deftest should-generate-image-str
(testing "non-federated-image"
(is (= "codeberg.org/forgejo/forgejo:8.0.3"
(cut/generate-image-str {:fqdn "test.de"
:mailer-from ""
:mailer-host "m.t.de"
:mailer-port "123"
:service-noreply-address ""
:deploy-federated "false"})))
(is (= "codeberg.org/forgejo/forgejo:1.19.3-0"
(cut/generate-image-str {:fqdn "test.de"
:mailer-from ""
:mailer-host "m.t.de"
:mailer-port "123"
:service-noreply-address ""
:deploy-federated "false"
:forgejo-image-version-overwrite "1.19.3-0"}))))
(testing "federated-image"
(is (= "domaindrivenarchitecture/c4k-forgejo-federated:latest"
(cut/generate-image-str {:fqdn "test.de"
:mailer-from ""
:mailer-host "m.t.de"
:mailer-port "123"
:service-noreply-address ""
:deploy-federated "true"})))
(is (= "domaindrivenarchitecture/c4k-forgejo-federated:3.2.0"
(cut/generate-image-str {:fqdn "test.de"
:mailer-from ""
:mailer-host "m.t.de"
:mailer-port "123"
:service-noreply-address ""
:deploy-federated "true"
:forgejo-image-version-overwrite "3.2.0"})))))
(deftest should-generate-appini-env (deftest should-generate-appini-env
(is (= {:APP_NAME-c1 "", (is (= {:APP_NAME-c1 "",
:APP_NAME-c2 "test forgejo", :APP_NAME-c2 "test forgejo",
@ -40,8 +74,7 @@
:mailer-host "m.t.de" :mailer-host "m.t.de"
:mailer-port "123" :mailer-port "123"
:service-domain-whitelist "adb.de" :service-domain-whitelist "adb.de"
:service-noreply-address "" :service-noreply-address ""})
:forgejo-image "codeberg.org/forgejo/forgejo:8.0.3"})
(cut/generate-appini-env {:default-app-name "test forgejo" (cut/generate-appini-env {:default-app-name "test forgejo"
:federation-enabled "true" :federation-enabled "true"
:fqdn "test.com" :fqdn "test.com"
@ -49,8 +82,7 @@
:mailer-host "mail.test.com" :mailer-host "mail.test.com"
:mailer-port "456" :mailer-port "456"
:service-domain-whitelist "test.com,test.net" :service-domain-whitelist "test.com,test.net"
:service-noreply-address "noreply@test.com" :service-noreply-address "noreply@test.com"})))))
:forgejo-image "codeberg.org/forgejo/forgejo:8.0.3"})))))
(deftest should-generate-deployment (deftest should-generate-deployment
(testing "non-federated" (testing "non-federated"
@ -82,8 +114,37 @@
:service-noreply-address "" :service-noreply-address ""
:volume-total-storage-size 10 :volume-total-storage-size 10
:max-rate 10 :max-rate 10
:max-concurrent-requests 1 :max-concurrent-requests 1}))))
:forgejo-image "codeberg.org/forgejo/forgejo:8.0.3"}))))) (testing "federated-deployment"
(is (= {:apiVersion "apps/v1",
:kind "Deployment",
:metadata {:name "forgejo", :namespace "forgejo", :labels {:app "forgejo"}},
:spec
{:replicas 1,
:selector {:matchLabels {:app "forgejo"}},
:template
{:metadata {:name "forgejo", :labels {:app "forgejo"}},
:spec
{:containers
[{:name "forgejo",
:image "domaindrivenarchitecture/c4k-forgejo-federated:latest",
:imagePullPolicy "IfNotPresent",
:envFrom [{:configMapRef {:name "forgejo-env"}} {:secretRef {:name "forgejo-secrets"}}],
:volumeMounts [{:name "forgejo-data-volume", :mountPath "/data"}],
:ports [{:containerPort 22, :name "git-ssh"} {:containerPort 3000, :name "forgejo"}]}],
:volumes [{:name "forgejo-data-volume", :persistentVolumeClaim {:claimName "forgejo-data-pvc"}}]}}}}
(cut/generate-deployment
{:default-app-name ""
:deploy-federated "true"
:fqdn "test.de"
:mailer-from ""
:mailer-host "m.t.de"
:mailer-port "123"
:service-domain-whitelist "adb.de"
:service-noreply-address ""
:volume-total-storage-size 10
:max-rate 10
:max-concurrent-requests 1})))))
(deftest should-generate-secret (deftest should-generate-secret
(is (= {:FORGEJO__database__USER-c1 "", (is (= {:FORGEJO__database__USER-c1 "",