fix test / fix refactorings / prepare password-rotation

This commit is contained in:
Michael Jerger 2025-01-14 12:55:45 +01:00
parent ddda23bb10
commit 556fc9ae58
6 changed files with 145 additions and 220 deletions
src
main
clj/dda/c4k_taiga
cljc/dda/c4k_taiga
cljs/dda/c4k_taiga
resources/backup
test/cljc/dda/c4k_taiga

View file

@ -4,12 +4,12 @@
[dda.c4k-common.uberjar :as uberjar]
[dda.c4k-taiga.core :as core]))
(defn -main [& cmd-args]
(uberjar/main-common
(uberjar/main-cm
"c4k-taiga"
core/config?
core/auth?
core/config-defaults
core/k8s-objects
core/defaults
core/config-objects
core/auth-objects
cmd-args))

View file

@ -9,57 +9,103 @@
[dda.c4k-common.monitoring :as mon]
[dda.c4k-taiga.taiga :as taiga]
[dda.c4k-taiga.backup :as backup]
[dda.c4k-common.postgres :as postgres]))
[dda.c4k-common.postgres :as postgres]
[dda.c4k-common.namespace :as ns]))
(def default-storage-class :local-path)
(def defaults {:namespace "taiga"
:issuer "staging"
:storage-class-name "local-path"
:pv-storage-size-gb "5"
:storage-media-size "5"
:storage-static-size "5"
:storage-async-rabbitmq-size "5"
:storage-events-rabbitmq-size "5"
:public-register-enabled "false"
:enable-telemetry "false"})
(def config? taiga/config?)
(def auth? taiga/auth?)
(def config? (s/merge
::backup/config
(s/keys :req-un [::taiga/fqdn]
:opt-un [::taiga/issuer
::taiga/storage-class-name
::taiga/storage-media-size
::taiga/storage-static-size
::taiga/storage-async-rabbitmq-size
::taiga/storage-events-rabbitmq-size
::taiga/public-register-enabled
::taiga/enable-telemetry
::postgres/pv-storage-size-gb
::mon/mon-cfg])))
(def config-defaults taiga/config-defaults)
(def auth? (s/merge
::backup/auth
(s/keys :req-un [::postgres/postgres-db-user
::postgres/postgres-db-password
::taiga/taiga-secret-key
::taiga/mailer-pw
::taiga/mailer-user
::taiga/django-superuser-email
::taiga/django-superuser-password
::taiga/django-superuser-username
::taiga/rabbitmq-erlang-cookie
::taiga/rabbitmq-pw
::taiga/rabbitmq-user
::mon/mon-auth])))
(defn-spec k8s-objects cp/map-or-seq?
[config taiga/config?
auth taiga/auth?]
(defn-spec config-objects cp/map-or-seq?
[config config?]
(let [resolved-config (merge defaults config)
db-config (merge resolved-config {:postgres-size :8gb :db-name "taiga"
:pv-storage-size-gb 50})]
(cm/concat-vec
(map yaml/to-string
(filter
#(not (nil? %))
(cm/concat-vec
[(postgres/generate-config {:postgres-size :8gb :db-name "taiga"})
(postgres/generate-secret auth)
(postgres/generate-pvc {:pv-storage-size-gb 50
:pvc-storage-class-name default-storage-class})
(postgres/generate-deployment)
(postgres/generate-service)
(taiga/generate-async-deployment)
(taiga/generate-async-rabbitmq-deployment)
(taiga/generate-async-rabbitmq-service)
(taiga/generate-async-service)
(taiga/generate-back-deployment)
(taiga/generate-back-service)
(taiga/generate-configmap config)
(taiga/generate-pvc-taiga-media-data config)
(taiga/generate-pvc-taiga-static-data config)
(taiga/generate-events-deployment)
(taiga/generate-events-rabbitmq-deployment)
(taiga/generate-events-rabbitmq-service)
(taiga/generate-events-service)
(taiga/generate-front-deployment)
(taiga/generate-front-service)
(taiga/generate-gateway-configmap)
(taiga/generate-gateway-deployment)
(taiga/generate-gateway-service)
(taiga/generate-protected-deployment)
(taiga/generate-protected-service)
(taiga/generate-rabbitmq-pvc-async config)
(taiga/generate-rabbitmq-pvc-events config)
(taiga/generate-secret auth)]
(taiga/generate-ingress-and-cert config)
(when (contains? config :restic-repository)
[(backup/generate-config config)
(backup/generate-secret auth)
(backup/generate-cron)
(backup/generate-backup-restore-deployment config)])
(when (:contains? config :mon-cfg)
(mon/generate (:mon-cfg config) (:mon-auth auth))))))))
(map yaml/to-string
(filter
#(not (nil? %))
(cm/concat-vec
(ns/generate resolved-config)
(postgres/generate-config db-config)
[(taiga/generate-async-deployment)
(taiga/generate-async-rabbitmq-deployment)
(taiga/generate-async-rabbitmq-service)
(taiga/generate-async-service)
(taiga/generate-back-deployment)
(taiga/generate-back-service)
(taiga/generate-configmap resolved-config)
(taiga/generate-pvc-taiga-media-data resolved-config)
(taiga/generate-pvc-taiga-static-data resolved-config)
(taiga/generate-events-deployment)
(taiga/generate-events-rabbitmq-deployment)
(taiga/generate-events-rabbitmq-service)
(taiga/generate-events-service)
(taiga/generate-front-deployment)
(taiga/generate-front-service)
(taiga/generate-gateway-configmap)
(taiga/generate-gateway-deployment)
(taiga/generate-gateway-service)
(taiga/generate-protected-deployment)
(taiga/generate-protected-service)
(taiga/generate-rabbitmq-pvc-async resolved-config)
(taiga/generate-rabbitmq-pvc-events resolved-config)]
(taiga/generate-ingress-and-cert resolved-config)
(when (contains? resolved-config :restic-repository)
[(backup/generate-config resolved-config)
(backup/generate-cron)
(backup/generate-backup-restore-deployment resolved-config)])
(when (:contains? resolved-config :mon-cfg)
(mon/generate-config))))))))
(defn-spec auth-objects cp/map-or-seq?
[config config?
auth auth?]
(let [resolved-config (merge defaults config)]
(cm/concat-vec
(map yaml/to-string
(filter
#(not (nil? %))
(cm/concat-vec
[(postgres/generate-secret auth)
(taiga/generate-secret auth)]
(when (contains? resolved-config :restic-repository)
[(backup/generate-secret auth)])
(when (:contains? resolved-config :mon-cfg)
(mon/generate-auth (:mon-cfg resolved-config) (:mon-auth auth)))))))))

View file

@ -1,14 +1,9 @@
(ns dda.c4k-taiga.browser
(:require
[clojure.string :as st]
[clojure.tools.reader.edn :as edn]
[dda.c4k-common.monitoring :as mon]
[dda.c4k-taiga.core :as core]
[dda.c4k-taiga.taiga :as taiga]
[dda.c4k-common.common :as cm]
[dda.c4k-common.predicate :as cp]
[dda.c4k-common.browser :as br]
[dda.c4k-common.postgres :as postgres]))
[dda.c4k-common.browser :as br]))
(defn generate-content []
(cm/concat-vec
@ -16,40 +11,33 @@
(br/generate-needs-validation) :content
(cm/concat-vec
(br/generate-group
"domain"
(cm/concat-vec
(br/generate-input-field "fqdn" "The fully qualified domain name of your Taiga Instance:" "taiga.example.com")
(br/generate-input-field "issuer" "(Optional) Your issuer prod/staging:" "staging")
(br/generate-input-field "mon-cluster-name" "(Optional) monitoring cluster name:" "taiga")
(br/generate-input-field "mon-cluster-stage" "(Optional) monitoring cluster stage:" "test")
(br/generate-input-field "mon-cloud-url" "(Optional) grafana cloud url:" "https://prometheus-prod-01-eu-west-0.grafana.net/api/prom/push")))
"config"
(br/generate-text-area "config" "Your config.edn:" "{:fqdn \"cloud.your.domain\"
:issuer \"staging\"
:restic-repository \"s3://yourbucket/your-repo\"
:mon-cfg {:cluster-name \"cloud\"
:cluster-stage \"test\"
:cloud-url \"https://prometheus-prod-01-eu-west-0.grafana.net/api/prom/push\"}}"
"5"))
(br/generate-group
"options"
(cm/concat-vec
(br/generate-input-field "public-register-enabled" "(Optional) Allow public registration?" "false")
(br/generate-input-field "enable-telemetry" "(Optional) Allow anonymous collection of usage data?" "false")
(br/generate-input-field "pv-storage-size-gb" "(Optional) The volume size of your postgres DB:" "5")
(br/generate-input-field "storage-class-name" "(Optional) Name of storage class:" "local-path")
(br/generate-input-field "storage-media-size" "(Optional) The size of your media storage:" "5")
(br/generate-input-field "storage-static-size" "(Optional) The size of your static data storage:" "5")
(br/generate-input-field "storage-async-rabbitmq-size" "(Optional) The size of your rabbitmq async storage:" "5")
(br/generate-input-field "storage-events-rabbitmq-size" "(Optional) The size of your rabbitmq events storage:" "5")))
(br/generate-group
"credentials"
(cm/concat-vec
(br/generate-input-field "postgres-db-user" "Your postgres user:" "postgres")
(br/generate-input-field "postgres-db-password" "Your postgres password:" "change-me")
(br/generate-input-field "mailer-user" "Allow taiga access to a mail account:" "mail[at]example.com")
(br/generate-input-field "mailer-pw" "Allow taiga access to a mail account:" "change-me")
(br/generate-input-field "django-superuser-username" "The superusers username:" "admin")
(br/generate-input-field "django-superuser-password" "The superusers password:" "change-me")
(br/generate-input-field "django-superuser-email" "The superusers email:" "mail[at]example.com")
(br/generate-input-field "rabbitmq-user" "User for rabbitmq:" "user")
(br/generate-input-field "rabbitmq-pw" "Password for the rabbitmq user:" "change-me")
(br/generate-input-field "rabbitmq-erlang-cookie" "Random hash shared among all rabbitmq pods:" "change-me")
(br/generate-input-field "taiga-secret-key" "Random key shared among all taiga pods:" "change-me")
(br/generate-input-field "grafana-cloud-user" "Your grafana user name:" "user")
(br/generate-input-field "grafana-cloud-password" "Your grafana password:" "change-me")))
"auth"
(br/generate-text-area "auth" "Your auth.edn:" "{:postgres-db-user \"taiga\"
:postgres-db-password \"db-password\"
:mailer-user \"mail[at]example.com\"
:mailer-pw \"change-me\"
:django-superuser-username \"admin\"
:django-superuser-password \"change-me\"
:django-superuser-email \"mail[at]example.com\"
:rabbitmq-user \"user\"
:rabbitmq-pw \"change-me\"
:rabbitmq-erlang-cookie \"change-me\"
:taiga-secret-key \"change-me\"
:aws-access-key-id \"aws-id\"
:aws-secret-access-key \"aws-secret\"
:restic-password \"restic-password\"}
:mon-auth {:grafana-cloud-user \"your-user-id\"
:grafana-cloud-password \"your-cloud-password\"}"
"5"))
[(br/generate-br)]
(br/generate-button "generate-button" "Generate c4k yaml")))]
(br/generate-output "c4k-taiga-output" "Your c4k deployment.yaml:" "15")))
@ -61,94 +49,9 @@
:content
(generate-content)})
(defn auth-from-document []
(let [postgres-db-user (br/get-content-from-element "postgres-db-user" )
postgres-db-password (br/get-content-from-element "postgres-db-password" )
mailer-user (br/get-content-from-element "mailer-user" )
mailer-pw (br/get-content-from-element "mailer-pw" )
django-superuser-username (br/get-content-from-element "django-superuser-username" )
django-superuser-password (br/get-content-from-element "django-superuser-password" )
django-superuser-email (br/get-content-from-element "django-superuser-email" )
rabbitmq-user (br/get-content-from-element "rabbitmq-user" )
rabbitmq-pw (br/get-content-from-element "rabbitmq-pw" )
rabbitmq-erlang-cookie (br/get-content-from-element "rabbitmq-erlang-cookie" )
taiga-secret-key (br/get-content-from-element "taiga-secret-key" )
grafana-cloud-user (br/get-content-from-element "grafana-cloud-user" :optional true)
grafana-cloud-password (br/get-content-from-element "grafana-cloud-password" :optional true)]
(merge
{:postgres-db-user postgres-db-user}
{:postgres-db-password postgres-db-password}
{:mailer-user mailer-user}
{:mailer-pw mailer-pw}
{:django-superuser-username django-superuser-username}
{:django-superuser-password django-superuser-password}
{:django-superuser-email django-superuser-email}
{:rabbitmq-user rabbitmq-user}
{:rabbitmq-pw rabbitmq-pw}
{:rabbitmq-erlang-cookie rabbitmq-erlang-cookie}
{:taiga-secret-key taiga-secret-key}
(when (some? grafana-cloud-user)
{:mon-auth {:grafana-cloud-user grafana-cloud-user
:grafana-cloud-password grafana-cloud-password}}))))
(defn config-from-document []
(let [issuer (br/get-content-from-element "issuer" :optional true)
fqdn (br/get-content-from-element "fqdn" :deserializer edn/read-string)
public-register-enabled (br/get-content-from-element "public-register-enabled" :deserializer edn/read-string)
enable-telemetry (br/get-content-from-element "enable-telemetry" :deserializer edn/read-string)
pv-storage-size-gb (br/get-content-from-element "pv-storage-size-gb" :deserializer edn/read-string)
storage-class-name (br/get-content-from-element "storage-class-name" :deserializer edn/read-string)
storage-media-size (br/get-content-from-element "storage-media-size" :deserializer edn/read-string)
storage-static-size (br/get-content-from-element "storage-static-size" :deserializer edn/read-string)
storage-async-rabbitmq-size (br/get-content-from-element "storage-async-rabbitmq-size" :deserializer edn/read-string)
storage-events-rabbitmq-size (br/get-content-from-element "storage-events-rabbitmq-size" :deserializer edn/read-string)
mon-cluster-name (br/get-content-from-element "mon-cluster-name" :optional true)
mon-cluster-stage (br/get-content-from-element "mon-cluster-stage" :optional true)
mon-cloud-url (br/get-content-from-element "mon-cloud-url" :optional true)]
(merge
{:fqdn fqdn}
{:public-register-enabled public-register-enabled}
{:enable-telemetry enable-telemetry}
{:pv-storage-size-gb pv-storage-size-gb}
{:storage-class-name storage-class-name}
{:storage-media-size storage-media-size}
{:storage-static-size storage-static-size}
{:storage-async-rabbitmq-size storage-async-rabbitmq-size}
{:storage-events-rabbitmq-size storage-events-rabbitmq-size}
(when (not (st/blank? issuer))
{:issuer issuer})
(when (some? mon-cluster-name)
{:mon-cfg {:cluster-name mon-cluster-name
:cluster-stage (keyword mon-cluster-stage)
:grafana-cloud-url mon-cloud-url}}))))
(defn validate-all! []
(br/validate! "fqdn" ::taiga/fqdn )
(br/validate! "issuer" ::taiga/issuer :optional true)
(br/validate! "public-register-enabled" ::taiga/public-register-enabled :optional true)
(br/validate! "enable-telemetry" ::taiga/enable-telemetry :optional true)
(br/validate! "pv-storage-size-gb" ::postgres/pv-storage-size-gb :optional true)
(br/validate! "storage-class-name" ::taiga/storage-class-name :optional true)
(br/validate! "storage-media-size" ::taiga/storage-media-size :optional true)
(br/validate! "storage-static-size" ::taiga/storage-static-size :optional true)
(br/validate! "storage-async-rabbitmq-size" ::taiga/storage-async-rabbitmq-size :optional true)
(br/validate! "storage-events-rabbitmq-size" ::taiga/storage-events-rabbitmq-size :optional true)
(br/validate! "mon-cluster-name" ::mon/cluster-name :optional true)
(br/validate! "mon-cluster-stage" ::mon/cluster-stage :optional true)
(br/validate! "mon-cloud-url" ::mon/grafana-cloud-url :optional true)
(br/validate! "postgres-db-user" ::postgres/postgres-db-user )
(br/validate! "postgres-db-password" ::postgres/postgres-db-password )
(br/validate! "mailer-user" ::taiga/mailer-user )
(br/validate! "mailer-pw" ::taiga/mailer-pw )
(br/validate! "django-superuser-username" ::taiga/django-superuser-username )
(br/validate! "django-superuser-password" ::taiga/django-superuser-password )
(br/validate! "django-superuser-email" ::taiga/django-superuser-email )
(br/validate! "rabbitmq-user" ::taiga/rabbitmq-user )
(br/validate! "rabbitmq-pw" ::taiga/rabbitmq-pw )
(br/validate! "rabbitmq-erlang-cookie" ::taiga/rabbitmq-erlang-cookie )
(br/validate! "taiga-secret-key" ::taiga/taiga-secret-key )
(br/validate! "grafana-cloud-user" ::mon/grafana-cloud-user )
(br/validate! "grafana-cloud-password" ::mon/grafana-cloud-password )
(br/validate! "config" core/config? :deserializer edn/read-string)
(br/validate! "auth" core/auth? :deserializer edn/read-string)
(br/set-form-validated!))
(defn add-validate-listener [name]
@ -161,35 +64,14 @@
(.getElementById "generate-button")
(.addEventListener "click"
#(do (validate-all!)
(-> (cm/generate-common
(config-from-document)
(auth-from-document)
core/config-defaults
core/k8s-objects)
(-> (cm/generate-cm
(br/get-content-from-element "config" :deserializer edn/read-string)
(br/get-content-from-element "auth" :deserializer edn/read-string)
core/config-defaults
core/config-objects
core/auth-objects
false
false)
(br/set-output!)))))
(add-validate-listener "fqdn")
(add-validate-listener "issuer")
(add-validate-listener "public-register-enabled")
(add-validate-listener "enable-telemetry")
(add-validate-listener "pv-storage-size-gb")
(add-validate-listener "storage-class-name")
(add-validate-listener "storage-media-size")
(add-validate-listener "storage-static-size")
(add-validate-listener "storage-async-rabbitmq-size")
(add-validate-listener "storage-events-rabbitmq-size")
(add-validate-listener "mon-cluster-name")
(add-validate-listener "mon-cluster-stage")
(add-validate-listener "mon-cloud-url")
(add-validate-listener "postgres-db-user")
(add-validate-listener "postgres-db-password")
(add-validate-listener "mailer-user")
(add-validate-listener "mailer-pw")
(add-validate-listener "django-superuser-username")
(add-validate-listener "django-superuser-password")
(add-validate-listener "django-superuser-email")
(add-validate-listener "rabbitmq-user")
(add-validate-listener "rabbitmq-pw")
(add-validate-listener "rabbitmq-erlang-cookie")
(add-validate-listener "taiga-secret-key")
(add-validate-listener "grafana-cloud-user")
(add-validate-listener "grafana-cloud-password"))
(add-validate-listener "config")
(add-validate-listener "authr"))

View file

@ -57,8 +57,8 @@ spec:
key: restic-repository
- name: RESTIC_PASSWORD_FILE
value: /var/run/secrets/backup-secrets/restic-password
- name: CERTIFICATE_FILE
value: ""
- name: RESTIC_NEW_PASSWORD_FILE
value: /var/run/secrets/backup-secrets/restic-new-password
volumeMounts:
- name: taiga-media
mountPath: /media

View file

@ -1,6 +1,6 @@
(ns dda.c4k-taiga.core-test
(:require
#?(:cljs [shadow.resource :as rc])
#?(:cljs [dda.c4k-common.macros :refer-macros [inline-resources]])
#?(:clj [clojure.test :refer [deftest is are testing run-tests]]
:cljs [cljs.test :refer-macros [deftest is are testing run-tests]])
[clojure.spec.alpha :as s]
@ -8,11 +8,8 @@
[dda.c4k-taiga.core :as cut]))
#?(:cljs
(defmethod yaml/load-resource :website-test [resource-name]
(case resource-name
"taiga-test/valid-config.yaml" (rc/inline "taiga-test/valid-config.yaml")
"taiga-test/valid-auth.yaml" (rc/inline "taiga-test/valid-auth.yaml")
(throw (js/Error. "Undefined Resource!")))))
(defmethod yaml/load-resource :taiga-test [resource-name]
(get (inline-resources "taiga-test") resource-name)))
(deftest validate-valid-resources
(is (s/valid? cut/config? (yaml/load-as-edn "taiga-test/valid-config.yaml")))

View file

@ -14,8 +14,8 @@
(st/instrument `cut/generate-secret)
#?(:cljs
(defmethod yaml/load-resource :nextcloud-test [resource-name]
(get (inline-resources "nextcloud-test") resource-name)))
(defmethod yaml/load-resource :taiga-test [resource-name]
(get (inline-resources "taiga-test") resource-name)))
(deftest should-generate-configmap
(is (= {:apiVersion "v1",