diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 35ee19c..f099ef9 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -49,7 +49,7 @@ test-schema: stage: build_and_test script: - lein uberjar - - java -jar target/uberjar/c4k-nextcloud-standalone.jar valid-config.edn valid-auth.edn | kubeconform --kubernetes-version 1.19.0 --strict --skip "Certificate,CronJob" - + - java -jar target/uberjar/c4k-nextcloud-standalone.jar src/test/resources/nextcloud-test/valid-config.yaml src/test/resources/nextcloud-test/valid-auth.yaml | kubeconform --kubernetes-version 1.19.0 --strict --skip "Certificate,CronJob" - artifacts: paths: - target/uberjar diff --git a/doc/LiveUpgrade.md b/doc/LiveUpgrade.md new file mode 100644 index 0000000..cb422a4 --- /dev/null +++ b/doc/LiveUpgrade.md @@ -0,0 +1,17 @@ +# Upgrade major or minor versions of nextcloud + +## Nextcloud versions of c4k-nextcloud docker images + +- 4.0.3: nextcloud 22 +- 5.0.0: nextcloud 23 +- 6.0.0: nextcloud 24 +- 7.0.0: nextcloud 25 + +## Uprgrading process + +1. Change the version of the docker image in the deployment to the next major version + - `kubectl edit deploy cloud-deployment` + - change `image: domaindrivenarchitecture/c4k-cloud:4.0.3` +2. Wait for the pod to finish restarting +3. Verify the website is working and https://URL/settings/admin/overview shows the correct version +4. Repeat until desired version is reached diff --git a/doc/SetupOnHetzner.md b/doc/SetupOnHetzner.md index 9872457..359eddf 100644 --- a/doc/SetupOnHetzner.md +++ b/doc/SetupOnHetzner.md @@ -45,15 +45,17 @@ output "ipv4" { ## k8s minicluster -For k8s installation we use our [dda-k8s-crate](https://github.com/DomainDrivenArchitecture/dda-k8s-crate) with the following configuation: +For k8s installation we use our [provs](https://repo.prod.meissa.de/meissa/provs) with the following configuation: ``` -{:user :k8s - :k8s {:external-ip "ip-from-above"} - :cert-manager :letsencrypt-prod-issuer - :persistent-dirs ["cloud", "postgres"] - } +postgres-db-user: "nextcloud" +postgres-db-password: "nextcloud-db-password" +nextcloud-admin-user: "cloudadmin" +nextcloud-admin-password: "cloudpassword" +aws-access-key-id: "aws-id" +aws-secret-access-key: "aws-secret" +restic-password: "restic-password" ``` ## kubectl apply c4k-nextcloud diff --git a/infrastructure/docker-nextcloud/image/Dockerfile b/infrastructure/docker-nextcloud/image/Dockerfile index 61ac8ab..e65e42b 100644 --- a/infrastructure/docker-nextcloud/image/Dockerfile +++ b/infrastructure/docker-nextcloud/image/Dockerfile @@ -1,4 +1,4 @@ -FROM nextcloud:22 +FROM nextcloud:25 # Prepare Entrypoint Script ADD resources /tmp diff --git a/package.json b/package.json index 256d0c5..85d98af 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "c4k-nextcloud", "description": "Generate c4k yaml for a nextcloud deployment.", "author": "meissa GmbH", - "version": "4.0.3-SNAPSHOT", + "version": "7.0.1-SNAPSHOT", "homepage": "https://gitlab.com/domaindrivenarchitecture/c4k-nextcloud#readme", "repository": "https://www.npmjs.com/package/c4k-nextcloud", "license": "APACHE2", diff --git a/project.clj b/project.clj index 8710683..d1ad577 100644 --- a/project.clj +++ b/project.clj @@ -1,11 +1,11 @@ -(defproject org.domaindrivenarchitecture/c4k-nextcloud "4.0.4-SNAPSHOT" +(defproject org.domaindrivenarchitecture/c4k-nextcloud "7.0.1-SNAPSHOT" :description "nextcloud c4k-installation package" :url "https://domaindrivenarchitecture.org" :license {:name "Apache License, Version 2.0" :url "https://www.apache.org/licenses/LICENSE-2.0.html"} :dependencies [[org.clojure/clojure "1.11.1"] [org.clojure/tools.reader "1.3.6"] - [org.domaindrivenarchitecture/c4k-common-clj "2.0.3"]] + [org.domaindrivenarchitecture/c4k-common-clj "6.0.1"]] :target-path "target/%s/" :source-paths ["src/main/cljc" "src/main/clj"] @@ -39,8 +39,7 @@ "-H:ResourceConfigurationFiles=graalvm-resource-config.json" "-H:Log=registerResource" "-H:Name=target/graalvm/${:name}"] - "inst" ["shell" "sudo" - "install" - "-m=755" - "target/uberjar/c4k-nextcloud-standalone.jar" - "/usr/local/bin/c4k-nextcloud-standalone.jar"]}) \ No newline at end of file + "inst" ["shell" + "sh" + "-c" + "lein uberjar && sudo install -m=755 target/uberjar/c4k-nextcloud-standalone.jar /usr/local/bin/c4k-nextcloud-standalone.jar"]}) \ No newline at end of file diff --git a/shadow-cljs.edn b/shadow-cljs.edn index 7578c71..fdc0345 100644 --- a/shadow-cljs.edn +++ b/shadow-cljs.edn @@ -4,7 +4,7 @@ "src/test/cljc" "src/test/cljs" "src/test/resources"] - :dependencies [[org.domaindrivenarchitecture/c4k-common-cljs "2.0.3"] + :dependencies [[org.domaindrivenarchitecture/c4k-common-cljs "6.0.1"] [hickory "0.7.1"]] :builds {:frontend {:target :browser :modules {:main {:init-fn dda.c4k-nextcloud.browser/init}} diff --git a/src/main/clj/dda/c4k_nextcloud/uberjar.clj b/src/main/clj/dda/c4k_nextcloud/uberjar.clj index 865b92c..368a49a 100644 --- a/src/main/clj/dda/c4k_nextcloud/uberjar.clj +++ b/src/main/clj/dda/c4k_nextcloud/uberjar.clj @@ -1,55 +1,15 @@ (ns dda.c4k-nextcloud.uberjar (:gen-class) (:require - [clojure.spec.alpha :as s] - [clojure.string :as cs] - [clojure.tools.reader.edn :as edn] - [expound.alpha :as expound] - [dda.c4k-common.yaml :as yaml] - [dda.c4k-nextcloud.core :as core] - [dda.c4k-nextcloud.nextcloud :as nextcloud])) - -(def usage - "usage: - - c4k-nextcloud {your configuraton file} {your authorization file}") - -(s/def ::options (s/* #{"-h"})) -(s/def ::filename (s/and string? - #(not (cs/starts-with? % "-")))) -(s/def ::cmd-args (s/cat :options ::options - :args (s/? - (s/cat :config ::filename - :auth ::filename)))) -(defn invalid-args-msg - [spec args] - (s/explain spec args) - (println (str "Bad commandline arguments\n" usage))) + [dda.c4k-common.uberjar :as uberjar] + [dda.c4k-nextcloud.nextcloud :as nextcloud] + [dda.c4k-nextcloud.core :as core])) (defn -main [& cmd-args] - (let [parsed-args-cmd (s/conform ::cmd-args cmd-args)] - (if (= ::s/invalid parsed-args-cmd) - (invalid-args-msg ::cmd-args cmd-args) - (let [{:keys [options args]} parsed-args-cmd - {:keys [config auth]} args] - (cond - (some #(= "-h" %) options) - (println usage) - :default - (let [config-str (slurp config) - auth-str (slurp auth) - config-parse-fn (if (yaml/is-yaml? config) yaml/from-string edn/read-string) - auth-parse-fn (if (yaml/is-yaml? auth) yaml/from-string edn/read-string) - parsed-config (config-parse-fn config-str) - parsed-auth (auth-parse-fn auth-str) - config-valid? (s/valid? ::core/config parsed-config) - auth-valid? (s/valid? ::core/auth parsed-auth)] - (if (and config-valid? auth-valid?) - (println (core/generate parsed-config parsed-auth)) - (do - (when (not config-valid?) - (println - (expound/expound-str ::core/config parsed-config {:print-specs? false}))) - (when (not auth-valid?) - (println - (expound/expound-str ::core/auth parsed-auth {:print-specs? false}))))))))))) + (uberjar/main-common + "c4k-nextcloud" + nextcloud/config? + nextcloud/auth? + core/config-defaults + core/k8s-objects + cmd-args)) diff --git a/src/main/cljc/dda/c4k_nextcloud/backup.cljc b/src/main/cljc/dda/c4k_nextcloud/backup.cljc index 4a499c9..1ef1694 100644 --- a/src/main/cljc/dda/c4k_nextcloud/backup.cljc +++ b/src/main/cljc/dda/c4k_nextcloud/backup.cljc @@ -23,14 +23,14 @@ (defn generate-config [my-conf] (let [{:keys [restic-repository]} my-conf] (-> - (yaml/from-string (yaml/load-resource "backup/config.yaml")) + (yaml/load-as-edn "backup/config.yaml") (cm/replace-key-value :restic-repository restic-repository)))) (defn generate-cron [] (yaml/from-string (yaml/load-resource "backup/cron.yaml"))) (defn generate-backup-restore-deployment [my-conf] - (let [backup-restore-yaml (yaml/from-string (yaml/load-resource "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) (= true (:local-integration-test my-conf))) (cm/replace-named-value backup-restore-yaml "CERTIFICATE_FILE" "/var/run/secrets/localstack-secrets/ca.crt") backup-restore-yaml))) @@ -38,7 +38,7 @@ (defn generate-secret [my-auth] (let [{:keys [aws-access-key-id aws-secret-access-key restic-password]} my-auth] (-> - (yaml/from-string (yaml/load-resource "backup/secret.yaml")) + (yaml/load-as-edn "backup/secret.yaml") (cm/replace-key-value :aws-access-key-id (b64/encode aws-access-key-id)) (cm/replace-key-value :aws-secret-access-key (b64/encode aws-secret-access-key)) (cm/replace-key-value :restic-password (b64/encode restic-password))))) diff --git a/src/main/cljc/dda/c4k_nextcloud/core.cljc b/src/main/cljc/dda/c4k_nextcloud/core.cljc index 8cd5205..32fb75d 100644 --- a/src/main/cljc/dda/c4k_nextcloud/core.cljc +++ b/src/main/cljc/dda/c4k_nextcloud/core.cljc @@ -1,60 +1,44 @@ (ns dda.c4k-nextcloud.core (:require - [clojure.string :as cs] [clojure.spec.alpha :as s] #?(:clj [orchestra.core :refer [defn-spec]] :cljs [orchestra.core :refer-macros [defn-spec]]) + [dda.c4k-common.common :as cm] + [dda.c4k-common.predicate :as cp] [dda.c4k-common.yaml :as yaml] [dda.c4k-common.postgres :as postgres] [dda.c4k-nextcloud.nextcloud :as nextcloud] - [dda.c4k-nextcloud.backup :as backup])) + [dda.c4k-nextcloud.backup :as backup] + [dda.c4k-common.monitoring :as mon])) (def default-storage-class :local-path) (def config-defaults {:issuer "staging"}) -(def config? (s/keys :req-un [::nextcloud/fqdn] - :opt-un [::nextcloud/issuer - ::nextcloud/restic-repository - ::nextcloud/pv-storage-size-gb - ::nextcloud/pvc-storage-class-name])) - -(def auth? (s/keys :req-un [::postgres/postgres-db-user ::postgres/postgres-db-password - ::nextcloud/nextcloud-admin-user ::nextcloud/nextcloud-admin-password - ::aws-access-key-id ::aws-secret-access-key - ::restic-password])) - -(s/def ::config config?) -(s/def ::auth auth?) - -(defn-spec k8s-objects any? - [config (s/merge config? auth?)] +(defn-spec k8s-objects cp/map-or-seq? + [config nextcloud/config? + auth nextcloud/auth?] (let [nextcloud-default-storage-config {:pvc-storage-class-name default-storage-class :pv-storage-size-gb 200}] - (into - [] - (concat [(yaml/to-string (postgres/generate-config {:postgres-size :8gb})) - (yaml/to-string (postgres/generate-secret config)) - (yaml/to-string (postgres/generate-pvc {:pv-storage-size-gb 50 - :pvc-storage-class-name default-storage-class})) - (yaml/to-string (postgres/generate-deployment)) - (yaml/to-string (postgres/generate-service)) - (yaml/to-string (nextcloud/generate-secret config)) - (yaml/to-string (nextcloud/generate-pvc (merge nextcloud-default-storage-config config))) - (yaml/to-string (nextcloud/generate-deployment config)) - (yaml/to-string (nextcloud/generate-service)) - (yaml/to-string (nextcloud/generate-certificate config)) - (yaml/to-string (nextcloud/generate-ingress config))] - (when (contains? config :restic-repository) - [(yaml/to-string (backup/generate-config config)) - (yaml/to-string (backup/generate-secret config)) - (yaml/to-string (backup/generate-cron)) - (yaml/to-string (backup/generate-backup-restore-deployment config))]))))) - -(defn-spec generate any? - [my-config config? - my-auth auth?] - (let [resulting-config (merge config-defaults my-config my-auth)] - (cs/join - "\n---\n" - (k8s-objects resulting-config)))) + (map yaml/to-string + (filter + #(not (nil? %)) + (cm/concat-vec + [(postgres/generate-config {:postgres-size :8gb :db-name "nextcloud"}) + (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) + (nextcloud/generate-secret auth) + (nextcloud/generate-pvc (merge nextcloud-default-storage-config config)) + (nextcloud/generate-deployment config) + (nextcloud/generate-service)] + (nextcloud/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)))))))) diff --git a/src/main/cljc/dda/c4k_nextcloud/nextcloud.cljc b/src/main/cljc/dda/c4k_nextcloud/nextcloud.cljc index 70bed75..f6fb875 100644 --- a/src/main/cljc/dda/c4k_nextcloud/nextcloud.cljc +++ b/src/main/cljc/dda/c4k_nextcloud/nextcloud.cljc @@ -5,9 +5,12 @@ #?(:clj [orchestra.core :refer [defn-spec]] :cljs [orchestra.core :refer-macros [defn-spec]]) [dda.c4k-common.yaml :as yaml] + [dda.c4k-common.ingress :as ing] [dda.c4k-common.base64 :as b64] [dda.c4k-common.predicate :as cp] - [dda.c4k-common.common :as cm])) + [dda.c4k-common.postgres :as postgres] + [dda.c4k-common.common :as cm] + [dda.c4k-common.monitoring :as mon])) (s/def ::fqdn cp/fqdn-string?) (s/def ::issuer cp/letsencrypt-issuer?) @@ -17,58 +20,58 @@ (s/def ::pvc-storage-class-name cp/pvc-storage-class-name?) (s/def ::pv-storage-size-gb pos?) -(def strong-config? (s/keys :req-un [::fqdn ::issuer ::pv-storage-size-gb - ::pvc-storage-class-name] - :opt-un [::restic-repository])) +(def config? (s/keys :req-un [::fqdn] + :opt-un [::issuer + ::restic-repository + ::pv-storage-size-gb + ::pvc-storage-class-name + ::mon/mon-cfg])) + +(def auth? (s/keys :req-un [::postgres/postgres-db-user ::postgres/postgres-db-password + ::nextcloud-admin-user ::nextcloud-admin-password + ::aws-access-key-id ::aws-secret-access-key + ::restic-password] + :opt-un [::mon/mon-auth])) #?(:cljs (defmethod yaml/load-resource :nextcloud [resource-name] (case resource-name - "nextcloud/certificate.yaml" (rc/inline "nextcloud/certificate.yaml") "nextcloud/deployment.yaml" (rc/inline "nextcloud/deployment.yaml") - "nextcloud/ingress.yaml" (rc/inline "nextcloud/ingress.yaml") "nextcloud/pvc.yaml" (rc/inline "nextcloud/pvc.yaml") "nextcloud/service.yaml" (rc/inline "nextcloud/service.yaml") "nextcloud/secret.yaml" (rc/inline "nextcloud/secret.yaml") (throw (js/Error. "Undefined Resource!"))))) -(defn generate-certificate [config] - (let [{:keys [fqdn issuer]} config - letsencrypt-issuer issuer] - (-> - (yaml/from-string (yaml/load-resource "nextcloud/certificate.yaml")) - (assoc-in [:spec :commonName] fqdn) - (assoc-in [:spec :dnsNames] [fqdn]) - (assoc-in [:spec :issuerRef :name] letsencrypt-issuer)))) - -(defn generate-deployment [config] +(defn-spec generate-deployment cp/map-or-seq? + [config config?] (let [{:keys [fqdn]} config] - (-> (yaml/from-string (yaml/load-resource "nextcloud/deployment.yaml")) + (-> (yaml/load-as-edn "nextcloud/deployment.yaml") (cm/replace-all-matching-values-by-new-value "fqdn" fqdn)))) -(defn generate-ingress [config] - (let [{:keys [fqdn issuer] - :or {issuer "staging"}} config - letsencrypt-issuer issuer] - (-> - (yaml/from-string (yaml/load-resource "nextcloud/ingress.yaml")) - (assoc-in [:metadata :annotations :cert-manager.io/cluster-issuer] letsencrypt-issuer) - (cm/replace-all-matching-values-by-new-value "fqdn" fqdn)))) +(defn-spec generate-ingress-and-cert cp/map-or-seq? + [config config?] + (ing/generate-ingress-and-cert + (merge + {:service-name "cloud-service" + :service-port 80 + :fqdns [(:fqdn config)]} + config))) (defn-spec generate-pvc cp/map-or-seq? [config (s/keys :req-un [::pv-storage-size-gb ::pvc-storage-class-name])] (let [{:keys [pv-storage-size-gb pvc-storage-class-name]} config] (-> - (yaml/from-string (yaml/load-resource "nextcloud/pvc.yaml")) + (yaml/load-as-edn "nextcloud/pvc.yaml") (assoc-in [:spec :resources :requests :storage] (str pv-storage-size-gb "Gi")) (assoc-in [:spec :storageClassName] (name pvc-storage-class-name))))) (defn generate-service [] - (yaml/from-string (yaml/load-resource "nextcloud/service.yaml"))) + (yaml/load-as-edn "nextcloud/service.yaml")) -(defn generate-secret [config] - (let [{:keys [nextcloud-admin-user nextcloud-admin-password]} config] +(defn-spec generate-secret cp/map-or-seq? + [auth auth?] + (let [{:keys [nextcloud-admin-user nextcloud-admin-password]} auth] (-> - (yaml/from-string (yaml/load-resource "nextcloud/secret.yaml")) + (yaml/load-as-edn "nextcloud/secret.yaml") (cm/replace-key-value :nextcloud-admin-user (b64/encode nextcloud-admin-user)) (cm/replace-key-value :nextcloud-admin-password (b64/encode nextcloud-admin-password))))) diff --git a/src/main/cljs/dda/c4k_nextcloud/browser.cljs b/src/main/cljs/dda/c4k_nextcloud/browser.cljs index eee519c..cb151c1 100644 --- a/src/main/cljs/dda/c4k_nextcloud/browser.cljs +++ b/src/main/cljs/dda/c4k_nextcloud/browser.cljs @@ -1,31 +1,42 @@ (ns dda.c4k-nextcloud.browser (:require [clojure.tools.reader.edn :as edn] + [dda.c4k-common.common :as cm] + [dda.c4k-common.monitoring :as mon] [dda.c4k-nextcloud.core :as core] [dda.c4k-nextcloud.nextcloud :as nextcloud] [dda.c4k-common.browser :as br] [dda.c4k-common.postgres :as pgc])) -(defn generate-content - [] - (into [] (concat [(assoc (br/generate-needs-validation) :content - (into [] (concat (br/generate-input-field "fqdn" "Your fqdn:" "nextcloud-neu.prod.meissa-gmbh.de") - (br/generate-input-field "nextcloud-data-volume-path" "(Optional) Your nextcloud-data-volume-path:" "/var/nextcloud") - (br/generate-input-field "postgres-data-volume-path" "(Optional) Your postgres-data-volume-path:" "/var/postgres") - (br/generate-input-field "restic-repository" "(Optional) Your restic-repository:" "restic-repository") - (br/generate-input-field "issuer" "(Optional) Your issuer prod/staging:" "") - [(br/generate-br)] - (br/generate-text-area "auth" "Your auth.edn:" "{:postgres-db-user \"nextcloud\" +(defn generate-content [] + (cm/concat-vec + [(assoc + (br/generate-needs-validation) :content + (cm/concat-vec + (br/generate-group "domain" + (cm/concat-vec (br/generate-input-field "fqdn" "Your fqdn:" "nextcloud-neu.prod.meissa-gmbh.de") + (br/generate-input-field "issuer" "(Optional) Your issuer prod/staging:" "") + (br/generate-input-field "pv-storage-size-gb" "(Optional) Your nextcloud storage size in GB" "8") + (br/generate-input-field "pvc-storage-class-name" "(Optional) Your storage class type (manual / local-path):" "local-path") + (br/generate-input-field "postgres-data-volume-path" "(Optional) Your postgres-data-volume-path:" "/var/postgres") + (br/generate-input-field "restic-repository" "(Optional) Your restic-repository:" "restic-repository") + (br/generate-input-field "mon-cluster-name" "(Optional) monitoring cluster name:" "keycloak") + (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"))) + (br/generate-group "credentials" + (br/generate-text-area "auth" "Your auth.edn:" "{:postgres-db-user \"nextcloud\" :postgres-db-password \"nextcloud-db-password\" :nextcloud-admin-password \"nextcloud-admin-password\" :nextcloud-admin-user \"nextcloud-admin-user\" :aws-access-key-id \"aws-id\" :aws-secret-access-key \"aws-secret\" - :restic-password \"restic-password\"}" - "5") - [(br/generate-br)] - (br/generate-button "generate-button" "Generate c4k yaml"))))] - (br/generate-output "c4k-nextcloud-output" "Your c4k deployment.yaml:" "25")))) + :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-nextcloud-output" "Your c4k deployment.yaml:" "25"))) (defn generate-content-div [] @@ -35,29 +46,40 @@ (generate-content)}) (defn config-from-document [] - (let [nextcloud-data-volume-path (br/get-content-from-element "nextcloud-data-volume-path" :optional true) + (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 :deserializer keyword)] + 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 (some? nextcloud-data-volume-path) - {:nextcloud-data-volume-path nextcloud-data-volume-path}) + (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! [] (br/validate! "fqdn" ::nextcloud/fqdn) - (br/validate! "nextcloud-data-volume-path" ::nextcloud/nextcloud-data-volume-path :optional true) + (br/validate! "pv-storage-size-gb" ::nextcloud/pv-storage-size-gb :optional true) + (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 :deserializer keyword) - (br/validate! "auth" core/auth? :deserializer edn/read-string) + (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!)) (defn add-validate-listener [name] @@ -70,13 +92,19 @@ (.getElementById "generate-button") (.addEventListener "click" #(do (validate-all!) - (-> (core/generate - (config-from-document) - (br/get-content-from-element "auth" :deserializer edn/read-string)) + (-> (cm/generate-common + (config-from-document) + (br/get-content-from-element "auth" :deserializer edn/read-string) + {} + core/k8s-objects) (br/set-output!))))) (add-validate-listener "fqdn") - (add-validate-listener "nextcloud-data-volume-path") + (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")) diff --git a/src/main/resources/nextcloud/certificate.yaml b/src/main/resources/nextcloud/certificate.yaml deleted file mode 100644 index 461168d..0000000 --- a/src/main/resources/nextcloud/certificate.yaml +++ /dev/null @@ -1,15 +0,0 @@ -apiVersion: cert-manager.io/v1 -kind: Certificate -metadata: - name: cloud-cert - namespace: default -spec: - secretName: cloud-cert - duration: 2160h # 90d - renewBefore: 360h # 15d - commonName: fqdn - dnsNames: - - fqdn - issuerRef: - name: staging - kind: ClusterIssuer \ No newline at end of file diff --git a/src/main/resources/nextcloud/deployment.yaml b/src/main/resources/nextcloud/deployment.yaml index 4bca0a9..36bccec 100644 --- a/src/main/resources/nextcloud/deployment.yaml +++ b/src/main/resources/nextcloud/deployment.yaml @@ -18,7 +18,7 @@ spec: redeploy: v3 spec: containers: - - image: domaindrivenarchitecture/c4k-cloud + - image: domaindrivenarchitecture/c4k-cloud:7.0.0 name: cloud-app imagePullPolicy: IfNotPresent ports: diff --git a/src/main/resources/nextcloud/ingress.yaml b/src/main/resources/nextcloud/ingress.yaml deleted file mode 100644 index 96671eb..0000000 --- a/src/main/resources/nextcloud/ingress.yaml +++ /dev/null @@ -1,29 +0,0 @@ -apiVersion: networking.k8s.io/v1 -kind: Ingress -metadata: - name: ingress-cloud - annotations: - cert-manager.io/cluster-issuer: letsencrypt-staging-issuer - ingress.kubernetes.io/ssl-redirect: "true" - ingress.kubernetes.io/rewrite-target: / - ingress.kubernetes.io/proxy-body-size: "256m" - ingress.kubernetes.io/proxy-connect-timeout: "300" - ingress.kubernetes.io/proxy-send-timeout: "300" - ingress.kubernetes.io/proxy-read-timeout: "300" - namespace: default -spec: - tls: - - hosts: - - fqdn - secretName: cloud-cert - rules: - - host: fqdn - http: - paths: - - path: / - pathType: Prefix - backend: - service: - name: cloud-service - port: - number: 80 diff --git a/src/test/cljc/dda/c4k_nextcloud/core_test.cljc b/src/test/cljc/dda/c4k_nextcloud/core_test.cljc deleted file mode 100644 index d4fbda6..0000000 --- a/src/test/cljc/dda/c4k_nextcloud/core_test.cljc +++ /dev/null @@ -1,55 +0,0 @@ -(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.string :as st] - [dda.c4k-nextcloud.core :as cut] - )) - -(deftest should-k8s-objects - (is (= 15 - (count (cut/k8s-objects {:fqdn "nextcloud-neu.prod.meissa-gmbh.de" - :postgres-db-user "nextcloud" - :postgres-db-password "nextcloud-db-password" - :nextcloud-admin-user "cloudadmin" - :nextcloud-admin-password "cloudpassword" - :issuer "prod" - :aws-access-key-id "aws-id" - :aws-secret-access-key "aws-secret" - :restic-password "restic-pw" - :restic-repository "restic-repository"})))) - (is (= 11 - (count (cut/k8s-objects {:fqdn "nextcloud-neu.prod.meissa-gmbh.de" - :postgres-db-user "nextcloud" - :postgres-db-password "nextcloud-db-password" - :nextcloud-admin-user "cloudadmin" - :nextcloud-admin-password "cloudpassword" - :issuer "prod" - :aws-access-key-id "aws-id" - :aws-secret-access-key "aws-secret" - :restic-password "restic-pw"})))) - (is (st/includes? - (get-in (cut/k8s-objects {:fqdn "nextcloud-neu.prod.meissa-gmbh.de" - :postgres-db-user "nextcloud" - :postgres-db-password "nextcloud-db-password" - :nextcloud-admin-user "cloudadmin" - :nextcloud-admin-password "cloudpassword" - :issuer "prod" - :aws-access-key-id "aws-id" - :aws-secret-access-key "aws-secret" - :restic-password "restic-pw"}) - [0]) - "max_connections = 700")) - (is (st/includes? - (get-in (cut/k8s-objects {:fqdn "nextcloud-neu.prod.meissa-gmbh.de" - :postgres-db-user "nextcloud" - :postgres-db-password "nextcloud-db-password" - :nextcloud-admin-user "cloudadmin" - :nextcloud-admin-password "cloudpassword" - :issuer "prod" - :aws-access-key-id "aws-id" - :aws-secret-access-key "aws-secret" - :restic-password "restic-pw"}) - [6]) - "storageClassName: local-path")) -) diff --git a/src/test/cljc/dda/c4k_nextcloud/nextcloud_test.cljc b/src/test/cljc/dda/c4k_nextcloud/nextcloud_test.cljc index 88e3376..583fa18 100644 --- a/src/test/cljc/dda/c4k_nextcloud/nextcloud_test.cljc +++ b/src/test/cljc/dda/c4k_nextcloud/nextcloud_test.cljc @@ -2,8 +2,28 @@ (:require #?(:clj [clojure.test :refer [deftest is are testing run-tests]] :cljs [cljs.test :refer-macros [deftest is are testing run-tests]]) + #?(:cljs [shadow.resource :as rc]) + [clojure.spec.alpha :as s] + [clojure.spec.test.alpha :as st] + [dda.c4k-common.yaml :as yaml] [dda.c4k-nextcloud.nextcloud :as cut])) +(st/instrument) + +#?(:cljs + (defmethod yaml/load-resource :nextcloud-test [resource-name] + (case resource-name + "nextcloud-test/valid-auth.yaml" (rc/inline "nextcloud-test/valid-auth.yaml") + "nextcloud-test/valid-config.yaml" (rc/inline "nextcloud-test/valid-config.yaml") + "nextcloud-test/invalid-auth.yaml" (rc/inline "nextcloud-test/invalid-auth.yaml") + "nextcloud-test/invalid-config.yaml" (rc/inline "nextcloud-test/invalid-config.yaml")))) + +(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 (is (= {:apiVersion "v1" :kind "Secret" @@ -12,49 +32,50 @@ :data {:nextcloud-admin-user "Y2xvdWRhZG1pbg==" :nextcloud-admin-password "Y2xvdWRwYXNzd29yZA=="}} - (cut/generate-secret {:nextcloud-admin-user "cloudadmin" + (cut/generate-secret {:postgres-db-user "postgres-user" + :postgres-db-password "postgres-password" + :aws-access-key-id "aws-key" + :aws-secret-access-key "aws-secret-key" + :restic-password "restic-password" + :nextcloud-admin-user "cloudadmin" :nextcloud-admin-password "cloudpassword"})))) -(deftest should-generate-certificate - (is (= {:apiVersion "cert-manager.io/v1" - :kind "Certificate" - :metadata {:name "cloud-cert", :namespace "default"} - :spec - {:secretName "cloud-cert" - :duration "2160h" - :renewBefore "360h", - :commonName "xx", - :dnsNames ["xx"] - :issuerRef - {:name "prod", :kind "ClusterIssuer"}}} - (cut/generate-certificate {:fqdn "xx" :issuer "prod"})))) - -(deftest should-generate-ingress - (is (= {:apiVersion "networking.k8s.io/v1" - :kind "Ingress" - :metadata - {:name "ingress-cloud" - :annotations - {:cert-manager.io/cluster-issuer "staging" - :ingress.kubernetes.io/proxy-body-size "256m" - :ingress.kubernetes.io/ssl-redirect "true" - :ingress.kubernetes.io/rewrite-target "/" - :ingress.kubernetes.io/proxy-connect-timeout "300" - :ingress.kubernetes.io/proxy-send-timeout "300" - :ingress.kubernetes.io/proxy-read-timeout "300"} - :namespace "default"} - :spec - {:tls [{:hosts ["xx"], :secretName "cloud-cert"}] - :rules - [{:host "xx" - :http - {:paths - [{:path "/" - :pathType "Prefix" - :backend - {:service - {:name "cloud-service", :port {:number 80}}}}]}}]}} - (cut/generate-ingress {:fqdn "xx"})))) +(deftest should-generate-ingress-and-cert + (is (= [{:apiVersion "cert-manager.io/v1", + :kind "Certificate", + :metadata + {:name "cloud-service", + :labels {:app.kubernetes.part-of "cloud-service"}, + :namespace "default"}, + :spec + {:secretName "cloud-service", + :commonName "somefqdn.de", + :duration "2160h", + :renewBefore "360h", + :dnsNames ["somefqdn.de"], + :issuerRef {:name "staging", :kind "ClusterIssuer"}}} + {:apiVersion "networking.k8s.io/v1", + :kind "Ingress", + :metadata + {:name "cloud-service", + :namespace "default", + :labels {:app.kubernetes.part-of "cloud-service"}, + :annotations + {:traefik.ingress.kubernetes.io/router.entrypoints "web, websecure", + :traefik.ingress.kubernetes.io/router.middlewares + "default-redirect-https@kubernetescrd", + :metallb.universe.tf/address-pool "public"}}, + :spec + {:tls [{:hosts ["somefqdn.de"], :secretName "cloud-service"}], + :rules + [{:host "somefqdn.de", + :http + {:paths + [{:pathType "Prefix", + :path "/", + :backend + {:service {:name "cloud-service", :port {:number 80}}}}]}}]}}] + (cut/generate-ingress-and-cert {:fqdn "somefqdn.de"})))) (deftest should-generate-pvc (is (= {:apiVersion "v1" @@ -77,7 +98,7 @@ {:metadata {:labels {:app "cloud-app", :app.kubernetes.io/name "cloud-pod", :app.kubernetes.io/application "cloud", :redeploy "v3"}} :spec {:containers - [{:image "domaindrivenarchitecture/c4k-cloud" + [{:image "domaindrivenarchitecture/c4k-cloud:7.0.0" :name "cloud-app" :imagePullPolicy "IfNotPresent" :ports [{:containerPort 80}] @@ -93,11 +114,11 @@ [{:name "NEXTCLOUD_ADMIN_USER", :valueFrom {:secretKeyRef {:name "cloud-secret", :key "nextcloud-admin-user"}}} {:name "NEXTCLOUD_ADMIN_PASSWORD" :valueFrom {:secretKeyRef {:name "cloud-secret", :key "nextcloud-admin-password"}}} - {:name "NEXTCLOUD_TRUSTED_DOMAINS", :value "xx"} + {:name "NEXTCLOUD_TRUSTED_DOMAINS", :value "somefqdn.de"} {:name "POSTGRES_USER", :valueFrom {:secretKeyRef {:name "postgres-secret", :key "postgres-user"}}} {:name "POSTGRES_PASSWORD", :valueFrom {:secretKeyRef {:name "postgres-secret", :key "postgres-password"}}} {:name "POSTGRES_DB", :valueFrom {:configMapKeyRef {:name "postgres-config", :key "postgres-db"}}} {:name "POSTGRES_HOST", :value "postgresql-service:5432"}] :volumeMounts [{:name "cloud-data-volume", :mountPath "/var/www/html"}]}] :volumes [{:name "cloud-data-volume", :persistentVolumeClaim {:claimName "cloud-pvc"}}]}}}} - (cut/generate-deployment {:fqdn "xx"})))) + (cut/generate-deployment {:fqdn "somefqdn.de"})))) \ No newline at end of file diff --git a/valid-auth.yaml b/src/test/resources/nextcloud-test/invalid-auth.yaml similarity index 88% rename from valid-auth.yaml rename to src/test/resources/nextcloud-test/invalid-auth.yaml index 247409a..11b9684 100644 --- a/valid-auth.yaml +++ b/src/test/resources/nextcloud-test/invalid-auth.yaml @@ -2,6 +2,6 @@ postgres-db-user: "nextcloud" postgres-db-password: "nextcloud-db-password" nextcloud-admin-user: "cloudadmin" nextcloud-admin-password: "cloudpassword" -aws-access-key-id: "aws-id" +aws-accss-key-id: "aws-id" aws-secret-access-key: "aws-secret" restic-password: "restic-password" \ No newline at end of file diff --git a/valid-config.yaml b/src/test/resources/nextcloud-test/invalid-config.yaml similarity index 74% rename from valid-config.yaml rename to src/test/resources/nextcloud-test/invalid-config.yaml index c716cf2..86120a4 100644 --- a/valid-config.yaml +++ b/src/test/resources/nextcloud-test/invalid-config.yaml @@ -1,5 +1,5 @@ -fqdn: "cloud.test.meissa-gmbh.de" -issuer: "staging" +fqdns: "cloud.test.meissa-gmbh.de" +issuer: "none" nextcloud-data-volume-path: "/var/cloud" postgres-data-volume-path: "/var/postgres" restic-repository: "s3:s3.amazonaws.com/your-bucket/your-folder" \ No newline at end of file diff --git a/valid-auth.edn b/src/test/resources/nextcloud-test/valid-auth.edn similarity index 64% rename from valid-auth.edn rename to src/test/resources/nextcloud-test/valid-auth.edn index 60f0255..5cee93d 100644 --- a/valid-auth.edn +++ b/src/test/resources/nextcloud-test/valid-auth.edn @@ -4,4 +4,6 @@ :nextcloud-admin-password "cloudpassword" :aws-access-key-id "aws-id" :aws-secret-access-key "aws-secret" - :restic-password "restic-password"} \ No newline at end of file + :restic-password "restic-password" + :mon-auth {:grafana-cloud-user "user" + :grafana-cloud-password "password"}} \ No newline at end of file diff --git a/src/test/resources/nextcloud-test/valid-auth.yaml b/src/test/resources/nextcloud-test/valid-auth.yaml new file mode 100644 index 0000000..6d88bf3 --- /dev/null +++ b/src/test/resources/nextcloud-test/valid-auth.yaml @@ -0,0 +1,10 @@ +postgres-db-user: "nextcloud" +postgres-db-password: "nextcloud-db-password" +nextcloud-admin-user: "cloudadmin" +nextcloud-admin-password: "cloudpassword" +aws-access-key-id: "aws-id" +aws-secret-access-key: "aws-secret" +restic-password: "restic-password" +mon-auth: + grafana-cloud-user: "user" + grafana-cloud-password: "password" \ No newline at end of file diff --git a/valid-config.edn b/src/test/resources/nextcloud-test/valid-config.edn similarity index 57% rename from valid-config.edn rename to src/test/resources/nextcloud-test/valid-config.edn index b50ced9..374f961 100644 --- a/valid-config.edn +++ b/src/test/resources/nextcloud-test/valid-config.edn @@ -2,4 +2,7 @@ :issuer "staging" :nextcloud-data-volume-path "/var/cloud" :postgres-data-volume-path "/var/postgres" - :restic-repository "s3:s3.amazonaws.com/your-bucket/your-folder"} \ No newline at end of file + :restic-repository "s3:s3.amazonaws.com/your-bucket/your-folder" + :mon-cfg {:grafana-cloud-url "url-for-your-prom-remote-write-endpoint" + :k3s-cluster-name "jitsi" + :k3s-cluster-stage "test"}} \ No newline at end of file diff --git a/src/test/resources/nextcloud-test/valid-config.yaml b/src/test/resources/nextcloud-test/valid-config.yaml new file mode 100644 index 0000000..48178b0 --- /dev/null +++ b/src/test/resources/nextcloud-test/valid-config.yaml @@ -0,0 +1,9 @@ +fqdn: "cloud.test.meissa-gmbh.de" +issuer: "staging" +nextcloud-data-volume-path: "/var/cloud" +postgres-data-volume-path: "/var/postgres" +restic-repository: "s3:s3.amazonaws.com/your-bucket/your-folder" +mon-cfg: + grafana-cloud-url: "url-for-your-prom-remote-write-endpoint" + cluster-name: "keycloak" + cluster-stage: "test" \ No newline at end of file