introduce postgres & persistent volumes

This commit is contained in:
jem 2021-07-05 18:02:25 +02:00
parent a5aece2e14
commit 2fc6a177ad
16 changed files with 279 additions and 116 deletions

View file

@ -1,12 +1,45 @@
#!/bin/bash #!/bin/bash
function main() {
local fqdn_value=file_env "FQDN"
local db_username_value=file_env "DB_USERNAME"
local db_username_value=file_env "DB_PASSWORD"
xmlstarlet ed -L -u "/Server/Service/Connector[@proxyName='{subdomain}.{domain}.com']/@proxyName" \ xmlstarlet ed -L -u "/Server/Service/Connector[@proxyName='{subdomain}.{domain}.com']/@proxyName" \
-v "$1" /opt/atlassian-jira-software-standalone/conf/server.xml -v "$fqdn_value" /opt/atlassian-jira-software-standalone/conf/server.xml
xmlstarlet ed -L -u "/jira-database-config/jdbc-datasource/username" \ xmlstarlet ed -L -u "/jira-database-config/jdbc-datasource/username" \
-v "$2" /app/dbconfig.xml -v "$db_username_value" /app/dbconfig.xml
xmlstarlet ed -L -u "/jira-database-config/jdbc-datasource/password" \ xmlstarlet ed -L -u "/jira-database-config/jdbc-datasource/password" \
-v "$3" /app/dbconfig.xml -v "$db_username_value" /app/dbconfig.xml
install -ojira -gjira -m660 /app/dbconfig.xml /var/jira/dbconfig.xml install -ojira -gjira -m660 /app/dbconfig.xml /var/jira/dbconfig.xml
/opt/atlassian-jira-software-standalone/bin/setenv.sh run /opt/atlassian-jira-software-standalone/bin/setenv.sh run
/opt/atlassian-jira-software-standalone/bin/start-jira.sh run /opt/atlassian-jira-software-standalone/bin/start-jira.sh run
}
# usage: file_env VAR [DEFAULT]
# ie: file_env 'XYZ_DB_PASSWORD' 'example'
# (will allow for "$XYZ_DB_PASSWORD_FILE" to fill in the value of
# "$XYZ_DB_PASSWORD" from a file, especially for Docker's secrets feature)
function file_env() {
local var="$1"
local fileVar="${var}_FILE"
local def="${2:-}"
local varValue=$(env | grep -E "^${var}=" | sed -E -e "s/^${var}=//")
local fileVarValue=$(env | grep -E "^${fileVar}=" | sed -E -e "s/^${fileVar}=//")
if [ -n "${varValue}" ] && [ -n "${fileVarValue}" ]; then
echo >&2 "error: both $var and $fileVar are set (but are exclusive)"
exit 1
fi
if [ -n "${varValue}" ]; then
export "$var"="${varValue}"
elif [ -n "${fileVarValue}" ]; then
export "$var"="$(cat "${fileVarValue}")"
elif [ -n "${def}" ]; then
export "$var"="$def"
fi
unset "$fileVar"
}
main

View file

@ -5,30 +5,48 @@
#?(: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.yaml :as yaml] [dda.c4k-common.yaml :as yaml]
[dda.c4k-jira.jira :as jira])) [dda.c4k-jira.jira :as jira]
[dda.c4k-jira.postgres :as postgres]))
(def config-defaults {:issuer :staging}) (def config-defaults {:issuer :staging})
(def config? (s/keys :req-un [::jira/fqdn] (def config? (s/keys :req-un [::jira/fqdn]
:opt-un [::jira/issuer])) :opt-un [::jira/issuer ::jira/jira-data-volume-path
::postgres/postgres-data-volume-path]))
(def auth? (s/keys :req-un [::jira/db-user-name ::jira/db-user-password])) (def auth? (s/keys :req-un [::postgres/db-user-name ::postgres/db-user-password]))
(defn-spec generate any? (defn-spec generate any?
[my-config config? [my-config config?
my-auth auth?] my-auth auth?]
(let [resulting-config (merge config-defaults my-config)] (let [resulting-config (merge config-defaults my-config my-auth)]
(cs/join "\n" (cs/join
[(yaml/to-string (jira/generate-secret my-auth)) "\n"
(into
[(yaml/to-string (postgres/generate-config))
"---" "---"
(yaml/to-string (jira/generate-persistent-volume)) (yaml/to-string (postgres/generate-secret resulting-config))]
(when-some [{:keys [postgres-data-volume-path]} resulting-config]
["---"
(yaml/to-string (postgres/generate-persistent-volume resulting-config))])
["---"
(yaml/to-string (postgres/generate-pvc))
"---" "---"
(yaml/to-string (postgres/generate-deployment))
"---"
(yaml/to-string (postgres/generate-service))]
(when-some [{:keys [jira-data-volume-path]} resulting-config]
["---"
(yaml/to-string (jira/generate-persistent-volume resulting-config))])
["---"
(yaml/to-string (jira/generate-pvc)) (yaml/to-string (jira/generate-pvc))
"---" "---"
(yaml/to-string (jira/generate-pod resulting-config))
"---"
(yaml/to-string (jira/generate-service))
"---"
(yaml/to-string (jira/generate-certificate resulting-config)) (yaml/to-string (jira/generate-certificate resulting-config))
"---" "---"
(yaml/to-string (jira/generate-ingress resulting-config)) (yaml/to-string (jira/generate-ingress resulting-config))
"---" "---"
(yaml/to-string (jira/generate-service)) (yaml/to-string (jira/generate-service))]))))
"---"
(yaml/to-string (jira/generate-pod resulting-config))])))

View file

@ -2,24 +2,14 @@
(:require (:require
[clojure.spec.alpha :as s] [clojure.spec.alpha :as s]
[dda.c4k-common.yaml :as yaml] [dda.c4k-common.yaml :as yaml]
[dda.c4k-common.base64 :as b64]
[dda.c4k-common.common :as cm])) [dda.c4k-common.common :as cm]))
(s/def ::db-user-name cm/bash-env-string?)
(s/def ::db-user-password cm/bash-env-string?)
(s/def ::fqdn cm/fqdn-string?) (s/def ::fqdn cm/fqdn-string?)
(s/def ::issuer cm/letsencrypt-issuer?) (s/def ::issuer cm/letsencrypt-issuer?)
(s/def ::jira-data-volume-path string?)
(defn generate-secret [auth]
(let [{:keys [db-user-name db-user-password]} auth]
(->
(yaml/from-string (yaml/load-resource "jira/secret.yaml"))
(assoc-in [:data :db-user-name] db-user-name)
(assoc-in [:data :db-user-password] db-user-password))))
(defn generate-certificate [config] (defn generate-certificate [config]
(let [{:keys [fqdn issuer] (let [{:keys [fqdn issuer]} config
:or {issuer :staging}} config
letsencrypt-issuer (str "letsencrypt-" (name issuer) "-issuer")] letsencrypt-issuer (str "letsencrypt-" (name issuer) "-issuer")]
(-> (->
(yaml/from-string (yaml/load-resource "jira/certificate.yaml")) (yaml/from-string (yaml/load-resource "jira/certificate.yaml"))
@ -36,16 +26,19 @@
(assoc-in [:metadata :annotations :cert-manager.io/cluster-issuer] letsencrypt-issuer) (assoc-in [:metadata :annotations :cert-manager.io/cluster-issuer] letsencrypt-issuer)
(cm/replace-all-matching-values-by-new-value "fqdn" fqdn)))) (cm/replace-all-matching-values-by-new-value "fqdn" fqdn))))
(defn generate-persistent-volume [] (defn generate-persistent-volume [config]
(yaml/from-string (yaml/load-resource "jira/persistent-volume.yaml"))) (let [{:keys [jira-data-volume-path]} config]
(->
(yaml/from-string (yaml/load-resource "jira/persistent-volume.yaml"))
(assoc-in [:spec :hostPath :path] jira-data-volume-path))))
(defn generate-pod [config]
(let [{:keys [fqdn]} config]
(-> (yaml/from-string (yaml/load-resource "jira/pod.yaml"))
(cm/replace-named-value "FQDN" fqdn))))
(defn generate-pvc [] (defn generate-pvc []
(yaml/from-string (yaml/load-resource "jira/pvc.yaml"))) (yaml/from-string (yaml/load-resource "jira/pvc.yaml")))
(defn generate-service [] (defn generate-service []
(yaml/from-string (yaml/load-resource "jira/service.yaml"))) (yaml/from-string (yaml/load-resource "jira/service.yaml")))
(defn generate-pod [config]
(let [{:keys [fqdn db-user-name db-user-password]} config]
(-> (yaml/from-string (yaml/load-resource "jira/pod.yaml"))
(assoc-in [:spec :containers 0 :args] [fqdn, db-user-name, db-user-password]))))

View file

@ -0,0 +1,35 @@
(ns dda.c4k-jira.postgres
(:require
[clojure.spec.alpha :as s]
[dda.c4k-common.yaml :as yaml]
[dda.c4k-common.base64 :as b64]
[dda.c4k-common.common :as cm]))
(s/def ::postgres-db-user cm/bash-env-string?)
(s/def ::postgres-db-password cm/bash-env-string?)
(s/def ::postgres-data-volume-path string?)
(defn generate-config []
(yaml/from-string (yaml/load-resource "postgres/config.yaml")))
(defn generate-deployment []
(yaml/from-string (yaml/load-resource "postgres/deployment.yaml")))
(defn generate-persistent-volume [config]
(let [{:keys [postgres-data-volume-path]} config]
(->
(yaml/from-string (yaml/load-resource "postgres/persistent-volume.yaml"))
(assoc-in [:spec :hostPath :path] postgres-data-volume-path))))
(defn generate-pvc []
(yaml/from-string (yaml/load-resource "postgres/pvc.yaml")))
(defn generate-secret [my-auth]
(let [{:keys [postgres-db-user postgres-db-password]} my-auth]
(->
(yaml/from-string (yaml/load-resource "postgres/secret.yaml"))
(cm/replace-key-value :postgres-user (b64/encode postgres-db-user))
(cm/replace-key-value :postgres-password (b64/encode postgres-db-password)))))
(defn generate-service []
(yaml/from-string (yaml/load-resource "postgres/service.yaml")))

View file

@ -6,15 +6,27 @@ metadata:
app: jira app: jira
spec: spec:
containers: containers:
- name: jira-app - image: domaindrivenarchitecture/c4k-jira
image: domaindrivenarchitecture/c4k-jira name: jira-app
imagePullPolicy: IfNotPresent imagePullPolicy: IfNotPresent
env:
- name: DB_USERNAME_FILE
value: /var/run/secrets/postgres-secret/postgres-user
- name: DB_PASSWORD_FILE
value: /var/run/secrets/postgres-secret/postgres-password
- name: FQDN
value: fqdn
command: ["/app/entrypoint.sh"] command: ["/app/entrypoint.sh"]
args: ["fqdn", "db-user-name", "db-user-password"]
volumeMounts: volumeMounts:
- mountPath: /var/jira - mountPath: /var/jira
name: jira-data-volume name: jira-data-volume
- name: postgres-secret-volume
mountPath: /var/run/secrets/postgres-secret
readOnly: true
volumes: volumes:
- name: jira-data-volume - name: jira-data-volume
persistentVolumeClaim: persistentVolumeClaim:
claimName: jira-pvc claimName: jira-pvc
- name: postgres-secret-volume
secret:
secretName: postgres-secret

View file

@ -1,8 +0,0 @@
apiVersion: v1
kind: Secret
metadata:
name: jira-secret
type: Opaque
data:
db-user-name: admin
db-user-password: admin

View file

@ -5,7 +5,7 @@ metadata:
labels: labels:
app: postgres app: postgres
data: data:
postgres-db: keycloak postgres-db: jira
postgresql.conf: | postgresql.conf: |
max_connections = 1000 max_connections = 1000
shared_buffers = 512MB shared_buffers = 512MB

View file

@ -40,7 +40,12 @@ spec:
mountPath: /etc/postgresql/postgresql.conf mountPath: /etc/postgresql/postgresql.conf
subPath: postgresql.conf subPath: postgresql.conf
readOnly: true readOnly: true
- name: postgre-data-volume
mountPath: /var/lib/postgresql/data
volumes: volumes:
- name: postgres-config-volume - name: postgres-config-volume
configMap: configMap:
name: postgres-config name: postgres-config
- name: postgre-d
persistentVolumeClaim:
claimName: postgres-claim

View file

@ -0,0 +1,14 @@
kind: PersistentVolume
apiVersion: v1
metadata:
name: postgres-pv-volume
labels:
type: local
spec:
storageClassName: manual
accessModes:
- ReadWriteOnce
capacity:
storage: 10Gi
hostPath:
path: "/var/postgres"

View file

@ -0,0 +1,13 @@
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: postgres-claim
labels:
app: postgres
spec:
storageClassName: manual
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 10Gi

View file

@ -4,3 +4,81 @@
:cljs [cljs.test :refer-macros [deftest is are testing run-tests]]) :cljs [cljs.test :refer-macros [deftest is are testing run-tests]])
[dda.c4k-jira.jira :as cut])) [dda.c4k-jira.jira :as cut]))
(deftest should-generate-certificate
(is (= {:apiVersion "cert-manager.io/v1alpha2"
:kind "Certificate"
:metadata {:name "jira-cert", :namespace "default"}
:spec
{:secretName "jira-secret"
:commonName "xx"
:dnsNames ["xx"]
:issuerRef
{:name "letsencrypt-prod-issuer", :kind "ClusterIssuer"}}}
(cut/generate-certificate {:fqdn "xx" :issuer :prod}))))
(deftest should-generate-ingress
(is (= {:apiVersion "extensions/v1beta1"
:kind "Ingress"
:metadata
{:name "ingress-jira"
:annotations
{:cert-manager.io/cluster-issuer
"letsencrypt-staging-issuer"
:nginx.ingress.kubernetes.io/proxy-body-size "256m"
:nginx.ingress.kubernetes.io/ssl-redirect "true"
:nginx.ingress.kubernetes.io/rewrite-target "/"
:nginx.ingress.kubernetes.io/proxy-connect-timeout "300"
:nginx.ingress.kubernetes.io/proxy-send-timeout "300"
:nginx.ingress.kubernetes.io/proxy-read-timeout "300"}
:namespace "default"}
:spec
{:tls [{:hosts ["xx"], :secretName "jira-secret"}]
:rules
[{:host "xx"
:http
{:paths
[{:path "/"
:backend
{:serviceName "jira-service", :servicePort 8080}}]}}]}}
(cut/generate-ingress {:fqdn "xx"}))))
(deftest should-generate-persistent-volume
(is (= {:kind "PersistentVolume"
:apiVersion "v1"
:metadata {:name "jira-pv-volume", :labels {:type "local"}}
:spec
{:storageClassName "manual"
:accessModes ["ReadWriteOnce"]
:capacity {:storage "30Gi"}
:hostPath {:path "xx"}}}
(cut/generate-persistent-volume {:jira-data-volume-path "xx"}))))
(deftest should-generate-pod
(is (= {:kind "Pod"
:apiVersion "v1"
:metadata {:name "jira-app", :labels {:app "jira"}}
:spec
{:containers
[{:image "domaindrivenarchitecture/c4k-jira"
:name "jira-app"
:imagePullPolicy "IfNotPresent"
:env
[{:name "DB_USERNAME_FILE"
:value
"/var/run/secrets/postgres-secret/postgres-user"}
{:name "DB_PASSWORD_FILE"
:value
"/var/run/secrets/postgres-secret/postgres-password"}
{:name "FQDN", :value "xx"}]
:command ["/app/entrypoint.sh"]
:volumeMounts
[{:mountPath "/var/jira", :name "jira-data-volume"}
{:name "postgres-secret-volume"
:mountPath "/var/run/secrets/postgres-secret"
:readOnly true}]}]
:volumes
[{:name "jira-data-volume"
:persistentVolumeClaim {:claimName "jira-pvc"}}
{:name "postgres-secret-volume"
:secret {:secretName "postgres-secret"}}]}}
(cut/generate-pod {:fqdn "xx"}))))

View file

@ -0,0 +1,26 @@
(ns dda.c4k-jira.postgres-test
(:require
#?(:clj [clojure.test :refer [deftest is are testing run-tests]]
:cljs [cljs.test :refer-macros [deftest is are testing run-tests]])
[dda.c4k-jira.postgres :as cut]))
(deftest should-generate-persistent-volume
(is (= {:kind "PersistentVolume"
:apiVersion "v1"
:metadata
{:name "postgres-pv-volume", :labels {:type "local"}}
:spec
{:storageClassName "manual"
:accessModes ["ReadWriteOnce"]
:capacity {:storage "10Gi"}
:hostPath {:path "xx"}}}
(cut/generate-persistent-volume {:postgres-data-volume-path "xx"}))))
(deftest should-generate-secret
(is (= {:apiVersion "v1"
:kind "Secret"
:metadata {:name "postgres-secret"}
:type "Opaque"
:data
{:postgres-user "eHgtdXM=", :postgres-password "eHgtcHc="}}
(cut/generate-secret {:postgres-db-user "xx-us" :postgres-db-password "xx-pw"}))))

View file

@ -1 +0,0 @@
{:xx {}}

View file

@ -1,27 +0,0 @@
{:transform [{:source {:source-type :twitter
;; optional, defaults to false
:include-replies? false
;; optional, defaults to false
:include-rts? false
;; Replace Twitter links by Nitter
:nitter-urls? 42
;; accounts you wish to mirror
:accounts ["arstechnica" "WIRED"]}
:target {:target-type :mastodon
;; optional flag specifying wether the name of the account
;; will be appended in the post, defaults to false
:append-screen-name? false
;; optional visibility flag: direct, private, unlisted, public
;; defaults to public
:visibility "unlisted"
;; optional boolean to mark content as sensitive. Defaults to true.
:sensitive? true
;; optional boolean defaults to false
;; only sources containing media will be posted when set to true
:media-only? true
;; optional limit for the post length. Defaults to 300.
:max-post-length 300
;; optional signature for posts. Defaults to "not present".
:signature "#newsbot"}
}]
:auth {}}

View file

@ -1 +0,0 @@
{:auth {}}

View file

@ -1,27 +0,0 @@
{:transform [{:source {:source-type :twitter
;; optional, defaults to false
:include-replies? false
;; optional, defaults to false
:include-rts? false
;; Replace Twitter links by Nitter
:nitter-urls? false
;; accounts you wish to mirror
:accounts ["arstechnica" "WIRED"]}
:target {:target-type :mastodon
;; optional flag specifying wether the name of the account
;; will be appended in the post, defaults to false
:append-screen-name? false
;; optional visibility flag: direct, private, unlisted, public
;; defaults to public
:visibility "unlisted"
;; optional boolean to mark content as sensitive. Defaults to true.
:sensitive? true
;; optional boolean defaults to false
;; only sources containing media will be posted when set to true
:media-only? true
;; optional limit for the post length. Defaults to 300.
:max-post-length 300
;; optional signature for posts. Defaults to "not present".
:signature "#newsbot"}
}]
:auth {}}