Merge branch 'main' into password-rotation

This commit is contained in:
Michael Jerger 2025-01-11 15:33:33 +01:00
commit 91985c7bb7
12 changed files with 90 additions and 94 deletions
infrastructure
backup
nextcloud
package.jsonproject.clj
src
main
clj/dda/c4k_nextcloud
cljc/dda/c4k_nextcloud
cljs/dda/c4k_nextcloud
test/cljc/dda/c4k_nextcloud

View file

@ -6,7 +6,7 @@ from ddadevops import *
name = "c4k-cloud" name = "c4k-cloud"
MODULE = "backup" MODULE = "backup"
PROJECT_ROOT_PATH = "../.." PROJECT_ROOT_PATH = "../.."
version = "10.4.3-dev" version = "10.4.5-dev"
@init @init

View file

@ -6,7 +6,7 @@ from ddadevops import *
name = 'c4k-cloud' name = 'c4k-cloud'
MODULE = 'not_set' MODULE = 'not_set'
PROJECT_ROOT_PATH = '../..' PROJECT_ROOT_PATH = '../..'
version = "10.4.3-dev" version = "10.4.5-dev"
@init @init
def initialize(project): def initialize(project):

View file

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

View file

@ -1,4 +1,4 @@
(defproject org.domaindrivenarchitecture/c4k-nextcloud "10.4.3-SNAPSHOT" (defproject org.domaindrivenarchitecture/c4k-nextcloud "10.4.5-SNAPSHOT"
:description "nextcloud c4k-installation package" :description "nextcloud 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"

View file

@ -2,14 +2,13 @@
(:gen-class) (:gen-class)
(:require (:require
[dda.c4k-common.uberjar :as uberjar] [dda.c4k-common.uberjar :as uberjar]
[dda.c4k-nextcloud.nextcloud :as nextcloud]
[dda.c4k-nextcloud.core :as core])) [dda.c4k-nextcloud.core :as core]))
(defn -main [& cmd-args] (defn -main [& cmd-args]
(uberjar/main-cm (uberjar/main-cm
"c4k-nextcloud" "c4k-nextcloud"
nextcloud/config? core/config?
nextcloud/auth? core/auth?
core/config-defaults core/config-defaults
core/config-objects core/config-objects
core/auth-objects core/auth-objects

View file

@ -1,6 +1,8 @@
(ns dda.c4k-nextcloud.backup (ns dda.c4k-nextcloud.backup
(:require (:require
[clojure.spec.alpha :as s] [clojure.spec.alpha :as s]
#?(:clj [orchestra.core :refer [defn-spec]]
:cljs [orchestra.core :refer-macros [defn-spec]])
[dda.c4k-common.yaml :as yaml] [dda.c4k-common.yaml :as yaml]
[dda.c4k-common.base64 :as b64] [dda.c4k-common.base64 :as b64]
[dda.c4k-common.common :as cm] [dda.c4k-common.common :as cm]
@ -13,27 +15,36 @@
(s/def ::restic-new-password cp/bash-env-string?) (s/def ::restic-new-password cp/bash-env-string?)
(s/def ::restic-repository cp/bash-env-string?) (s/def ::restic-repository cp/bash-env-string?)
(s/def ::config (s/keys :req-un [::restic-repository]))
(s/def ::auth (s/keys :req-un [::restic-password ::aws-access-key-id ::aws-secret-access-key]))
#?(:cljs #?(:cljs
(defmethod yaml/load-resource :backup [resource-name] (defmethod yaml/load-resource :backup [resource-name]
(get (inline-resources "backup") resource-name))) (get (inline-resources "backup") resource-name)))
(defn generate-config [my-conf] (defn-spec generate-config map?
[my-conf ::config]
(let [{:keys [restic-repository]} my-conf] (let [{:keys [restic-repository]} my-conf]
(-> (->
(yaml/load-as-edn "backup/config.yaml") (yaml/load-as-edn "backup/config.yaml")
(cm/replace-key-value :restic-repository restic-repository)))) (cm/replace-key-value :restic-repository restic-repository))))
(defn generate-cron [] (defn-spec generate-cron map?
[]
(yaml/from-string (yaml/load-resource "backup/cron.yaml"))) (yaml/from-string (yaml/load-resource "backup/cron.yaml")))
(defn generate-backup-restore-deployment [my-conf] (defn-spec generate-backup-restore-deployment map?
[my-conf ::config]
(let [backup-restore-yaml (yaml/load-as-edn "backup/backup-restore-deployment.yaml")] (let [backup-restore-yaml (yaml/load-as-edn "backup/backup-restore-deployment.yaml")]
(if (and (contains? my-conf :local-integration-test) (if (and (contains? my-conf :local-integration-test)
(= true (:local-integration-test my-conf))) (= true (:local-integration-test my-conf)))
(cm/replace-named-value backup-restore-yaml "CERTIFICATE_FILE" "/var/run/secrets/localstack-secrets/ca.crt") (cm/replace-named-value backup-restore-yaml "CERTIFICATE_FILE" "/var/run/secrets/localstack-secrets/ca.crt")
backup-restore-yaml))) backup-restore-yaml)))
(defn generate-secret [my-auth] (defn-spec generate-secret map?
[my-auth ::auth]
(let [{:keys [aws-access-key-id aws-secret-access-key restic-password]} my-auth] (let [{:keys [aws-access-key-id aws-secret-access-key restic-password]} my-auth]
(-> (->
(yaml/load-as-edn "backup/secret.yaml") (yaml/load-as-edn "backup/secret.yaml")

View file

@ -1,5 +1,6 @@
(ns dda.c4k-nextcloud.core (ns dda.c4k-nextcloud.core
(:require (:require
[clojure.spec.alpha :as s]
#?(:clj [orchestra.core :refer [defn-spec]] #?(:clj [orchestra.core :refer [defn-spec]]
:cljs [orchestra.core :refer-macros [defn-spec]]) :cljs [orchestra.core :refer-macros [defn-spec]])
[dda.c4k-common.common :as cm] [dda.c4k-common.common :as cm]
@ -16,8 +17,15 @@
:pvc-storage-class-name "hcloud-volumes-encrypted" :pvc-storage-class-name "hcloud-volumes-encrypted"
:pv-storage-size-gb 200}) :pv-storage-size-gb 200})
(def config? (s/merge ::nextcloud/config
::backup/config))
(def auth? (s/merge ::nextcloud/auth
::backup/auth))
(defn-spec config-objects cp/map-or-seq? (defn-spec config-objects cp/map-or-seq?
[config nextcloud/config?] [config config?]
(let [resolved-config (merge config-defaults config)] (let [resolved-config (merge config-defaults config)]
(map yaml/to-string (map yaml/to-string
(filter (filter
@ -40,8 +48,8 @@
(mon/generate-config))))))) (mon/generate-config)))))))
(defn-spec auth-objects cp/map-or-seq? (defn-spec auth-objects cp/map-or-seq?
[config nextcloud/config? [config config?
auth nextcloud/auth?] auth auth?]
(let [resolved-config (merge config-defaults config)] (let [resolved-config (merge config-defaults config)]
(map yaml/to-string (map yaml/to-string
(filter (filter

View file

@ -14,23 +14,20 @@
(s/def ::fqdn cp/fqdn-string?) (s/def ::fqdn cp/fqdn-string?)
(s/def ::issuer cp/letsencrypt-issuer?) (s/def ::issuer cp/letsencrypt-issuer?)
(s/def ::restic-repository string?)
(s/def ::nextcloud-admin-user cp/bash-env-string?) (s/def ::nextcloud-admin-user cp/bash-env-string?)
(s/def ::nextcloud-admin-password cp/bash-env-string?) (s/def ::nextcloud-admin-password cp/bash-env-string?)
(s/def ::pvc-storage-class-name cp/pvc-storage-class-name?) (s/def ::pvc-storage-class-name cp/pvc-storage-class-name?)
(s/def ::pv-storage-size-gb pos?) (s/def ::pv-storage-size-gb pos?)
(def config? (s/keys :req-un [::fqdn] (s/def ::config (s/keys :req-un [::fqdn]
:opt-un [::issuer :opt-un [::issuer
::restic-repository
::pv-storage-size-gb ::pv-storage-size-gb
::pvc-storage-class-name ::pvc-storage-class-name
::mon/mon-cfg])) ::mon/mon-cfg]))
(def auth? (s/keys :req-un [::postgres/postgres-db-user ::postgres/postgres-db-password (s/def ::auth (s/keys :req-un [::postgres/postgres-db-user ::postgres/postgres-db-password
::nextcloud-admin-user ::nextcloud-admin-password ::nextcloud-admin-user ::nextcloud-admin-password
::aws-access-key-id ::aws-secret-access-key ::aws-access-key-id ::aws-secret-access-key]
::restic-password]
:opt-un [::mon/mon-auth])) :opt-un [::mon/mon-auth]))
#?(:cljs #?(:cljs
@ -38,13 +35,13 @@
(get (inline-resources "nextcloud") resource-name))) (get (inline-resources "nextcloud") resource-name)))
(defn-spec generate-deployment cp/map-or-seq? (defn-spec generate-deployment cp/map-or-seq?
[config config?] [config ::config]
(let [{:keys [fqdn]} config] (let [{:keys [fqdn]} config]
(-> (yaml/load-as-edn "nextcloud/deployment.yaml") (-> (yaml/load-as-edn "nextcloud/deployment.yaml")
(cm/replace-all-matching "fqdn" fqdn)))) (cm/replace-all-matching "fqdn" fqdn))))
(defn-spec generate-ingress-and-cert cp/map-or-seq? (defn-spec generate-ingress-and-cert cp/map-or-seq?
[config config?] [config ::config]
(ing/generate-ingress-and-cert (ing/generate-ingress-and-cert
(merge (merge
{:service-name "cloud-service" {:service-name "cloud-service"
@ -64,7 +61,7 @@
(yaml/load-as-edn "nextcloud/service.yaml")) (yaml/load-as-edn "nextcloud/service.yaml"))
(defn-spec generate-secret cp/map-or-seq? (defn-spec generate-secret cp/map-or-seq?
[auth auth?] [auth ::auth]
(let [{:keys [nextcloud-admin-user nextcloud-admin-password]} auth] (let [{:keys [nextcloud-admin-user nextcloud-admin-password]} auth]
(-> (->
(yaml/load-as-edn "nextcloud/secret.yaml") (yaml/load-as-edn "nextcloud/secret.yaml")

View file

@ -2,28 +2,27 @@
(:require (:require
[clojure.tools.reader.edn :as edn] [clojure.tools.reader.edn :as edn]
[dda.c4k-common.common :as cm] [dda.c4k-common.common :as cm]
[dda.c4k-common.monitoring :as mon]
[dda.c4k-nextcloud.core :as core] [dda.c4k-nextcloud.core :as core]
[dda.c4k-nextcloud.nextcloud :as nextcloud] [dda.c4k-common.browser :as br]))
[dda.c4k-common.browser :as br]
[dda.c4k-common.postgres :as pgc]))
(defn generate-content [] (defn generate-content []
(cm/concat-vec (cm/concat-vec
[(assoc [(assoc
(br/generate-needs-validation) :content (br/generate-needs-validation) :content
(cm/concat-vec (cm/concat-vec
(br/generate-group "domain" (br/generate-group "config"
(cm/concat-vec (br/generate-input-field "fqdn" "Your fqdn:" "nextcloud-neu.prod.meissa-gmbh.de") (br/generate-text-area "config" "Your config.edn:" "{:fqdn \"cloud.your.domain\"
(br/generate-input-field "issuer" "(Optional) Your issuer prod/staging:" "") :issuer \"staging\"
(br/generate-input-field "pv-storage-size-gb" "(Optional) Your nextcloud storage size in GB" "8") :pv-storage-size-gb \"400\"
(br/generate-input-field "pvc-storage-class-name" "(Optional) Your storage class type (manual / local-path):" "local-path") :pvc-storage-class-name \"local-path\"
(br/generate-input-field "postgres-data-volume-path" "(Optional) Your postgres-data-volume-path:" "/var/postgres") :postgres-data-volume-path \"/var/postgres\"
(br/generate-input-field "restic-repository" "(Optional) Your restic-repository:" "restic-repository") :restic-repository \"s3://yourbucket/your-repo\"
(br/generate-input-field "mon-cluster-name" "(Optional) monitoring cluster name:" "keycloak") :restic-password \"restic-password\"}
(br/generate-input-field "mon-cluster-stage" "(Optional) monitoring cluster stage:" "test") :mon-cfg {:cluster-name \"cloud\"
(br/generate-input-field "mon-cloud-url" "(Optional) grafana cloud url:" "https://prometheus-prod-01-eu-west-0.grafana.net/api/prom/push"))) :cluster-stage \"test\"
(br/generate-group "credentials" :cloud-url \"https://prometheus-prod-01-eu-west-0.grafana.net/api/prom/push\"}"
"5"))
(br/generate-group "auth"
(br/generate-text-area "auth" "Your auth.edn:" "{:postgres-db-user \"nextcloud\" (br/generate-text-area "auth" "Your auth.edn:" "{:postgres-db-user \"nextcloud\"
:postgres-db-password \"nextcloud-db-password\" :postgres-db-password \"nextcloud-db-password\"
:nextcloud-admin-password \"nextcloud-admin-password\" :nextcloud-admin-password \"nextcloud-admin-password\"
@ -45,41 +44,9 @@
:content :content
(generate-content)}) (generate-content)})
(defn config-from-document []
(let [pv-storage-size-gb (br/get-content-from-element "pv-storage-size-gb" :optional true)
pvc-storage-class-name (br/get-content-from-element "pvc-storage-class-name" :optional true)
postgres-data-volume-path (br/get-content-from-element "postgres-data-volume-path" :optional true)
restic-repository (br/get-content-from-element "restic-repository" :optional true)
issuer (br/get-content-from-element "issuer" :optional true)
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 (br/get-content-from-element "fqdn")}
(when (and (some? pv-storage-size-gb) (some? pvc-storage-class-name))
{:pv-storage-size-gb pv-storage-size-gb :pvc-storage-class-name pvc-storage-class-name})
(when (some? postgres-data-volume-path)
{:postgres-data-volume-path postgres-data-volume-path})
(when (some? restic-repository)
{:restic-repository restic-repository})
(when (some? 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! [] (defn validate-all! []
(br/validate! "fqdn" ::nextcloud/fqdn) (br/validate! "config" core/config? :deserializer edn/read-string)
(br/validate! "pv-storage-size-gb" ::nextcloud/pv-storage-size-gb :optional true) (br/validate! "auth" core/auth? :deserializer edn/read-string)
(br/validate! "pvc-storage-class-name" ::nextcloud/pvc-storage-class-name :optional true)
(br/validate! "postgres-data-volume-path" ::pgc/postgres-data-volume-path :optional true)
(br/validate! "restic-repository" ::nextcloud/restic-repository :optional true)
(br/validate! "issuer" ::nextcloud/issuer :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! "auth" nextcloud/auth? :deserializer edn/read-string)
(br/set-validated!)) (br/set-validated!))
(defn add-validate-listener [name] (defn add-validate-listener [name]
@ -92,19 +59,14 @@
(.getElementById "generate-button") (.getElementById "generate-button")
(.addEventListener "click" (.addEventListener "click"
#(do (validate-all!) #(do (validate-all!)
(-> (cm/generate-common (-> (cm/generate-cm
(config-from-document) (br/get-content-from-element "config" :deserializer edn/read-string)
(br/get-content-from-element "auth" :deserializer edn/read-string) (br/get-content-from-element "auth" :deserializer edn/read-string)
{} core/config-defaults
core/k8s-objects) core/config-objects
core/auth-objects
false
false)
(br/set-output!))))) (br/set-output!)))))
(add-validate-listener "fqdn") (add-validate-listener "config")
(add-validate-listener "pv-storage-size-gb")
(add-validate-listener "pvc-storage-class-name")
(add-validate-listener "postgres-data-volume-path")
(add-validate-listener "restic-repository")
(add-validate-listener "issuer")
(add-validate-listener "mon-cluster-name")
(add-validate-listener "mon-cluster-stage")
(add-validate-listener "mon-cloud-url")
(add-validate-listener "auth")) (add-validate-listener "auth"))

View file

@ -2,8 +2,12 @@
(:require (:require
#?(: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.test.alpha :as st]
[dda.c4k-nextcloud.backup :as cut])) [dda.c4k-nextcloud.backup :as cut]))
(st/instrument `cut/generate-secret)
(st/instrument `cut/generate-config)
(st/instrument `cut/generate-cron)
(deftest should-generate-secret (deftest should-generate-secret
(is (= {:apiVersion "v1" (is (= {:apiVersion "v1"

View file

@ -0,0 +1,19 @@
(ns dda.c4k-nextcloud.core-test
(:require
#?(: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]
[dda.c4k-common.yaml :as yaml]
[dda.c4k-nextcloud.core :as cut]
#?(:cljs [dda.c4k-common.macros :refer-macros [inline-resources]])))
#?(:cljs
(defmethod yaml/load-resource :nextcloud-test [resource-name]
(get (inline-resources "nextcloud-test") resource-name)))
(deftest validate-valid-resources
(is (s/valid? cut/config? (yaml/load-as-edn "nextcloud-test/valid-config.yaml")))
(is (s/valid? cut/auth? (yaml/load-as-edn "nextcloud-test/valid-auth.yaml")))
(is (not (s/valid? cut/config? (yaml/load-as-edn "nextcloud-test/invalid-config.yaml"))))
(is (not (s/valid? cut/auth? (yaml/load-as-edn "nextcloud-test/invalid-auth.yaml")))))

View file

@ -2,24 +2,20 @@
(:require (:require
#?(: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.test.alpha :as st] [clojure.spec.test.alpha :as st]
[dda.c4k-common.yaml :as yaml] [dda.c4k-common.yaml :as yaml]
[dda.c4k-nextcloud.nextcloud :as cut] [dda.c4k-nextcloud.nextcloud :as cut]
#?(:cljs [dda.c4k-common.macros :refer-macros [inline-resources]]))) #?(:cljs [dda.c4k-common.macros :refer-macros [inline-resources]])))
(st/instrument) (st/instrument `cut/generate-secret)
(st/instrument `cut/generate-ingress-and-cert)
(st/instrument `cut/generate-pvc)
(st/instrument `cut/generate-deployment)
#?(:cljs #?(:cljs
(defmethod yaml/load-resource :nextcloud-test [resource-name] (defmethod yaml/load-resource :nextcloud-test [resource-name]
(get (inline-resources "nextcloud-test") resource-name))) (get (inline-resources "nextcloud-test") resource-name)))
(deftest validate-valid-resources
(is (s/valid? cut/config? (yaml/load-as-edn "nextcloud-test/valid-config.yaml")))
(is (s/valid? cut/auth? (yaml/load-as-edn "nextcloud-test/valid-auth.yaml")))
(is (not (s/valid? cut/config? (yaml/load-as-edn "nextcloud-test/invalid-config.yaml"))))
(is (not (s/valid? cut/auth? (yaml/load-as-edn "nextcloud-test/invalid-auth.yaml")))))
(deftest should-generate-secret (deftest should-generate-secret
(is (= {:apiVersion "v1" (is (= {:apiVersion "v1"
:kind "Secret" :kind "Secret"
@ -95,7 +91,7 @@
{:metadata {:labels {:app "cloud-app", :app.kubernetes.io/name "cloud-pod", :app.kubernetes.io/application "cloud", :redeploy "v3"}} {:metadata {:labels {:app "cloud-app", :app.kubernetes.io/name "cloud-pod", :app.kubernetes.io/application "cloud", :redeploy "v3"}}
:spec :spec
{:containers {:containers
[{:image "domaindrivenarchitecture/c4k-cloud:10.4.0" [{:image "domaindrivenarchitecture/c4k-cloud:10.4.2"
:name "cloud-app" :name "cloud-app"
:imagePullPolicy "IfNotPresent" :imagePullPolicy "IfNotPresent"
:ports [{:containerPort 80}] :ports [{:containerPort 80}]