diff --git a/ansible/roles/schulcloud-calendar-core/tasks/main.yml b/ansible/roles/schulcloud-calendar-core/tasks/main.yml index 00c90d9..37f44ce 100644 --- a/ansible/roles/schulcloud-calendar-core/tasks/main.yml +++ b/ansible/roles/schulcloud-calendar-core/tasks/main.yml @@ -1,6 +1,5 @@ - name: Create database - include_role: - name: dof_postgresql_management + include_tasks: postgres_management.yml vars: database_name: calendar when: WITH_BRANCH_POSTGRES_DB_MANAGEMENT is defined and WITH_BRANCH_POSTGRES_DB_MANAGEMENT diff --git a/ansible/roles/schulcloud-calendar-core/tasks/postgres_management.yml b/ansible/roles/schulcloud-calendar-core/tasks/postgres_management.yml new file mode 100644 index 0000000..0fe21b6 --- /dev/null +++ b/ansible/roles/schulcloud-calendar-core/tasks/postgres_management.yml @@ -0,0 +1,48 @@ +- name: Add or Update Postgres Cluster Secret by 1Password + kubernetes.core.k8s: + kubeconfig: ~/.kube/config + namespace: "{{ NAMESPACE }}" + template: postgres_management/onepassword-pg-cluster.yml.j2 + when: ONEPASSWORD_OPERATOR is defined and ONEPASSWORD_OPERATOR|bool + +- name: Check if secret with database credentials already exists + kubernetes.core.k8s_info: + kubeconfig: ~/.kube/config + namespace: "{{ NAMESPACE }}" + kind: Secret + name: "pg-{{ database_name }}-secret" + register: db_secret_present + +- name: Create Secret for the database (if not existing) + kubernetes.core.k8s: + kubeconfig: ~/.kube/config + namespace: "{{ NAMESPACE }}" + template: postgres_management/secret-database.yml.j2 + when: db_secret_present.resources|length == 0 + +- name: Create ConfigMap with Script + kubernetes.core.k8s: + kubeconfig: ~/.kube/config + namespace: "{{ NAMESPACE }}" + template: postgres_management/configmap-database-init.yml.j2 + apply: yes + +- name: Create/execute database configuration script + kubernetes.core.k8s: + kubeconfig: ~/.kube/config + namespace: "{{ NAMESPACE }}" + template: postgres_management/job-database-init.yml.j2 + +- name: Create ConfigMap with Script for database deletion + kubernetes.core.k8s: + kubeconfig: ~/.kube/config + namespace: "{{ NAMESPACE }}" + template: postgres_management/configmap-database-deletion.yml.j2 + apply: yes + +- name: Create suspended Job for database deletion + kubernetes.core.k8s: + kubeconfig: ~/.kube/config + namespace: "{{ NAMESPACE }}" + template: postgres_management/job-database-deletion.yml.j2 + apply: yes diff --git a/ansible/roles/schulcloud-calendar-core/templates/postgres_management/configmap-database-deletion.yml.j2 b/ansible/roles/schulcloud-calendar-core/templates/postgres_management/configmap-database-deletion.yml.j2 new file mode 100644 index 0000000..3338fbf --- /dev/null +++ b/ansible/roles/schulcloud-calendar-core/templates/postgres_management/configmap-database-deletion.yml.j2 @@ -0,0 +1,19 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: pg-configmap-deletion + namespace: {{ NAMESPACE }} + labels: + app: postgres +data: + config_script.sh: | + #!/bin/bash + DB_PREFIX="{{ POSTGRES_PREFIX }}" + if [[ {{ '${#DB_PREFIX}' }} -le 5 ]]; then + echo "Postgres prefix \"{{ POSTGRES_PREFIX }}\" seems too short. Dropping all matching databases could be dangerous. Aborting." + exit 1 + fi + echo "Delete databases starting with {{ POSTGRES_PREFIX }}" + echo "SELECT 'DROP DATABASE ' || quote_ident(datname) || ' WITH (FORCE);' FROM pg_database WHERE datname LIKE '{{ POSTGRES_PREFIX | replace('_','#_')}}%' ESCAPE '#' \gexec" | psql -d postgres -w + echo "Delete users starting with {{ POSTGRES_PREFIX }}" + echo "SELECT 'DROP USER ' || quote_ident(usename) || ';' FROM pg_catalog.pg_user WHERE usename LIKE '{{ POSTGRES_PREFIX | replace('_','#_')}}%' ESCAPE '#' \gexec" | psql -d postgres -w \ No newline at end of file diff --git a/ansible/roles/schulcloud-calendar-core/templates/postgres_management/configmap-database-init.yml.j2 b/ansible/roles/schulcloud-calendar-core/templates/postgres_management/configmap-database-init.yml.j2 new file mode 100644 index 0000000..8e34ca2 --- /dev/null +++ b/ansible/roles/schulcloud-calendar-core/templates/postgres_management/configmap-database-init.yml.j2 @@ -0,0 +1,19 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: pg-{{ database_name }}-configmap-init + namespace: {{ NAMESPACE }} + labels: + app: postgres +data: + config_script.sh: | + #!/bin/bash + echo "Create owner of the DB" + echo "SELECT 'CREATE USER $DB_USER' WHERE NOT EXISTS (SELECT FROM pg_user WHERE usename = '$DB_USER')\gexec" | psql -d postgres -w + echo "GRANT $DB_USER TO $PGUSER;" | psql -d postgres -w + echo "Set/update password for user $DB_USER" + echo "ALTER USER $DB_USER WITH ENCRYPTED PASSWORD '$DB_USER_PASSWORD';" | psql -d postgres -w + echo "Create database" + echo "SELECT 'CREATE DATABASE $DB_NAME OWNER $DB_USER' WHERE NOT EXISTS (SELECT FROM pg_database WHERE datname = '$DB_NAME')\gexec" | psql -d postgres -w + echo "Revoke permissions for public role" + echo "REVOKE ALL ON DATABASE $DB_NAME FROM PUBLIC;" | psql -d postgres -w diff --git a/ansible/roles/schulcloud-calendar-core/templates/postgres_management/job-database-deletion.yml.j2 b/ansible/roles/schulcloud-calendar-core/templates/postgres_management/job-database-deletion.yml.j2 new file mode 100644 index 0000000..6529bbe --- /dev/null +++ b/ansible/roles/schulcloud-calendar-core/templates/postgres_management/job-database-deletion.yml.j2 @@ -0,0 +1,51 @@ +apiVersion: batch/v1 +kind: Job +metadata: + name: pg-deletion-job + namespace: {{ NAMESPACE }} +spec: + template: + metadata: + labels: + app: postgres + spec: + volumes: + - name: config-script + configMap: + name: pg-configmap-deletion + # 711 in decimal is 457 + defaultMode: 457 + containers: + - name: psql-config + image: {{ POSTGRES_JOB_IMAGE }} + command: + - /bin/bash + - -c + args: + - /scripts/config_script.sh + resources: + limits: + cpu: 1000m + memory: 1Gi + requests: + cpu: 100m + memory: 200Mi + volumeMounts: + - name: config-script + mountPath: /scripts/ + env: + - name: PGHOST + value: {{ POSTGRES_HOST }} + - name: PGUSER + valueFrom: + secretKeyRef: + name: pg-cluster-secret + key: username + - name: PGPASSWORD + valueFrom: + secretKeyRef: + name: pg-cluster-secret + key: password + restartPolicy: Never + suspend: true + ttlSecondsAfterFinished: 0 \ No newline at end of file diff --git a/ansible/roles/schulcloud-calendar-core/templates/postgres_management/job-database-init.yml.j2 b/ansible/roles/schulcloud-calendar-core/templates/postgres_management/job-database-init.yml.j2 new file mode 100644 index 0000000..f8d730d --- /dev/null +++ b/ansible/roles/schulcloud-calendar-core/templates/postgres_management/job-database-init.yml.j2 @@ -0,0 +1,53 @@ +apiVersion: batch/v1 +kind: Job +metadata: + name: pg-{{ database_name }}-init-job-{{ 1000000 | random | hash('md5') }} + namespace: {{ NAMESPACE }} +spec: + template: + metadata: + labels: + app: postgres + spec: + volumes: + - name: config-script + configMap: + name: pg-{{ database_name }}-configmap-init + # 711 in decimal is 457 + defaultMode: 457 + containers: + - name: psql-config + image: {{ POSTGRES_JOB_IMAGE }} + command: + - /bin/bash + - -c + args: + - /scripts/config_script.sh + resources: + limits: + cpu: 1000m + memory: 1Gi + requests: + cpu: 100m + memory: 200Mi + volumeMounts: + - name: config-script + mountPath: /scripts/ + envFrom: + - secretRef: + name: pg-{{ database_name }}-secret + env: + - name: PGHOST + value: {{ POSTGRES_HOST }} + - name: PGUSER + valueFrom: + secretKeyRef: + name: pg-cluster-secret + key: username + - name: PGPASSWORD + valueFrom: + secretKeyRef: + name: pg-cluster-secret + key: password + restartPolicy: Never + ttlSecondsAfterFinished: 86400 \ No newline at end of file diff --git a/ansible/roles/schulcloud-calendar-core/templates/postgres_management/onepassword-pg-cluster.yml.j2 b/ansible/roles/schulcloud-calendar-core/templates/postgres_management/onepassword-pg-cluster.yml.j2 new file mode 100644 index 0000000..e23e932 --- /dev/null +++ b/ansible/roles/schulcloud-calendar-core/templates/postgres_management/onepassword-pg-cluster.yml.j2 @@ -0,0 +1,9 @@ +apiVersion: onepassword.com/v1 +kind: OnePasswordItem +metadata: + name: pg-cluster-secret + namespace: {{ NAMESPACE }} + labels: + app: postgres +spec: + itemPath: "vaults/{{ ONEPASSWORD_OPERATOR_VAULT }}/items/pg-cluster-schulcloud" \ No newline at end of file diff --git a/ansible/roles/schulcloud-calendar-core/templates/postgres_management/secret-database.yml.j2 b/ansible/roles/schulcloud-calendar-core/templates/postgres_management/secret-database.yml.j2 new file mode 100644 index 0000000..3dc45f5 --- /dev/null +++ b/ansible/roles/schulcloud-calendar-core/templates/postgres_management/secret-database.yml.j2 @@ -0,0 +1,12 @@ +apiVersion: v1 +kind: Secret +metadata: + name: pg-{{ database_name }}-secret + namespace: {{ NAMESPACE }} + labels: + app: postgres +type: Opaque +data: + DB_USER: "{{ (POSTGRES_PREFIX + database_name) | b64encode }}" + DB_USER_PASSWORD: "{{ lookup('ansible.builtin.password', '/dev/null') | b64encode }}" + DB_NAME: "{{ (POSTGRES_PREFIX + database_name) | b64encode }}" \ No newline at end of file