diff --git a/src/main/clj/dda/c4k_taiga/uberjar.clj b/src/main/clj/dda/c4k_taiga/uberjar.clj index ec762a5..cff656b 100644 --- a/src/main/clj/dda/c4k_taiga/uberjar.clj +++ b/src/main/clj/dda/c4k_taiga/uberjar.clj @@ -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)) diff --git a/src/main/cljc/dda/c4k_taiga/core.cljc b/src/main/cljc/dda/c4k_taiga/core.cljc index 46abbf7..51b226c 100644 --- a/src/main/cljc/dda/c4k_taiga/core.cljc +++ b/src/main/cljc/dda/c4k_taiga/core.cljc @@ -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))))))))) diff --git a/src/main/cljs/dda/c4k_taiga/browser.cljs b/src/main/cljs/dda/c4k_taiga/browser.cljs index 911185e..343576b 100644 --- a/src/main/cljs/dda/c4k_taiga/browser.cljs +++ b/src/main/cljs/dda/c4k_taiga/browser.cljs @@ -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")) diff --git a/src/main/resources/backup/backup-restore-deployment.yaml b/src/main/resources/backup/backup-restore-deployment.yaml index 557d18c..550113d 100644 --- a/src/main/resources/backup/backup-restore-deployment.yaml +++ b/src/main/resources/backup/backup-restore-deployment.yaml @@ -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 diff --git a/src/test/cljc/dda/c4k_taiga/core_test.cljc b/src/test/cljc/dda/c4k_taiga/core_test.cljc index a5de164..0913f98 100644 --- a/src/test/cljc/dda/c4k_taiga/core_test.cljc +++ b/src/test/cljc/dda/c4k_taiga/core_test.cljc @@ -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"))) diff --git a/src/test/cljc/dda/c4k_taiga/taiga_test.cljc b/src/test/cljc/dda/c4k_taiga/taiga_test.cljc index edda554..08fbab4 100644 --- a/src/test/cljc/dda/c4k_taiga/taiga_test.cljc +++ b/src/test/cljc/dda/c4k_taiga/taiga_test.cljc @@ -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",