diff --git a/infrastructure/docker-backup/build.py b/infrastructure/docker-backup/build.py new file mode 100644 index 0000000..fbe23c0 --- /dev/null +++ b/infrastructure/docker-backup/build.py @@ -0,0 +1,51 @@ +from os import environ +from pybuilder.core import task, init +from ddadevops import * +import logging + +name = 'c4k-gitea-backup' +MODULE = 'docker' +PROJECT_ROOT_PATH = '../..' + +class MyBuild(DevopsDockerBuild): + pass + +@init +def initialize(project): + project.build_depends_on('ddadevops>=0.12.4') + stage = 'prod' + dockerhub_user = environ.get('DOCKERHUB_USER') + if not dockerhub_user: + dockerhub_user = gopass_field_from_path('meissa/web/docker.com', 'login') + dockerhub_password = environ.get('DOCKERHUB_PASSWORD') + if not dockerhub_password: + dockerhub_password = gopass_password_from_path('meissa/web/docker.com') + tag = environ.get('CI_COMMIT_TAG') + if not tag: + tag = get_tag_from_latest_commit() + config = create_devops_docker_build_config( + stage, PROJECT_ROOT_PATH, MODULE, dockerhub_user, dockerhub_password, docker_publish_tag=tag) + build = MyBuild(project, config) + build.initialize_build_dir() + + +@task +def image(project): + build = get_devops_build(project) + build.image() + +@task +def drun(project): + build = get_devops_build(project) + build.drun() + +@task +def publish(project): + build = get_devops_build(project) + build.dockerhub_login() + build.dockerhub_publish() + +@task +def test(project): + build = get_devops_build(project) + build.test() diff --git a/infrastructure/docker-backup/image/Dockerfile b/infrastructure/docker-backup/image/Dockerfile new file mode 100644 index 0000000..9b6d4f0 --- /dev/null +++ b/infrastructure/docker-backup/image/Dockerfile @@ -0,0 +1,5 @@ +FROM domaindrivenarchitecture/dda-backup:1.0.5 + +# Prepare Entrypoint Script +ADD resources /tmp +RUN /tmp/install.sh diff --git a/infrastructure/docker-backup/image/resources/backup.sh b/infrastructure/docker-backup/image/resources/backup.sh new file mode 100755 index 0000000..9bdbde1 --- /dev/null +++ b/infrastructure/docker-backup/image/resources/backup.sh @@ -0,0 +1,20 @@ +#!/bin/bash + +set -o pipefail + +function main() { + file_env AWS_ACCESS_KEY_ID + file_env AWS_SECRET_ACCESS_KEY + file_env RESTIC_DAYS_TO_KEEP 30 + file_env RESTIC_MONTHS_TO_KEEP 12 + + #backup-roles 'TODO' + backup-db-dump + backup-fs-from-directory '/var/backups/' 'gitea/' 'git/repositories/' +} + +source /usr/local/lib/functions.sh +source /usr/local/lib/pg-functions.sh +source /usr/local/lib/file-functions.sh + +main diff --git a/infrastructure/docker-backup/image/resources/entrypoint-start-and-wait.sh b/infrastructure/docker-backup/image/resources/entrypoint-start-and-wait.sh new file mode 100644 index 0000000..0915071 --- /dev/null +++ b/infrastructure/docker-backup/image/resources/entrypoint-start-and-wait.sh @@ -0,0 +1,11 @@ +#!/bin/bash + +function main() { + + while true; do + sleep 1m + done +} + +source /usr/local/lib/functions.sh +main \ No newline at end of file diff --git a/infrastructure/docker-backup/image/resources/entrypoint.sh b/infrastructure/docker-backup/image/resources/entrypoint.sh new file mode 100755 index 0000000..b25e15f --- /dev/null +++ b/infrastructure/docker-backup/image/resources/entrypoint.sh @@ -0,0 +1,9 @@ +#!/bin/bash + +function main() { + + /usr/local/bin/backup.sh +} + +source /usr/local/lib/functions.sh +main diff --git a/infrastructure/docker-backup/image/resources/init.sh b/infrastructure/docker-backup/image/resources/init.sh new file mode 100755 index 0000000..678f57c --- /dev/null +++ b/infrastructure/docker-backup/image/resources/init.sh @@ -0,0 +1,12 @@ +#!/bin/bash + +function main() { + file_env AWS_ACCESS_KEY_ID + file_env AWS_SECRET_ACCESS_KEY + + init-file-repo +} + +source /usr/local/lib/functions.sh +source /usr/local/lib/file-functions.sh +main diff --git a/infrastructure/docker-backup/image/resources/install.sh b/infrastructure/docker-backup/image/resources/install.sh new file mode 100755 index 0000000..1a8cbd7 --- /dev/null +++ b/infrastructure/docker-backup/image/resources/install.sh @@ -0,0 +1,11 @@ +#!/bin/bash + +apt-get update > /dev/null; + +install -m 0700 /tmp/entrypoint.sh / +install -m 0700 /tmp/entrypoint-start-and-wait.sh / + +install -m 0700 /tmp/init.sh /usr/local/bin/ +install -m 0700 /tmp/backup.sh /usr/local/bin/ +install -m 0700 /tmp/restore.sh /usr/local/bin/ +install -m 0700 /tmp/restic-snapshots.sh /usr/local/bin/ diff --git a/infrastructure/docker-backup/image/resources/restic-snapshots.sh b/infrastructure/docker-backup/image/resources/restic-snapshots.sh new file mode 100755 index 0000000..ca889ce --- /dev/null +++ b/infrastructure/docker-backup/image/resources/restic-snapshots.sh @@ -0,0 +1,15 @@ +#!/bin/bash + +set -o pipefail + +function main() { + file_env AWS_ACCESS_KEY_ID + file_env AWS_SECRET_ACCESS_KEY + + restic -r ${RESTIC_REPOSITORY}/files snapshots +} + +source /usr/local/lib/functions.sh +source /usr/local/lib/file-functions.sh + +main diff --git a/infrastructure/docker-backup/image/resources/restore.sh b/infrastructure/docker-backup/image/resources/restore.sh new file mode 100755 index 0000000..264ebd6 --- /dev/null +++ b/infrastructure/docker-backup/image/resources/restore.sh @@ -0,0 +1,39 @@ +#!/bin/bash + +set -Eeo pipefail + +function main() { + + file_env AWS_ACCESS_KEY_ID + file_env AWS_SECRET_ACCESS_KEY + + file_env POSTGRES_DB + file_env POSTGRES_PASSWORD + file_env POSTGRES_USER + + # Restore latest snapshot into /var/backups/restore + rm -rf /var/backups/restore + restore-directory '/var/backups/restore' + + rm -rf /data/gitea/* + rm -rf /data/git/repositories/* + cp /var/backups/restore/gitea/* /data/gitea/ + cp /var/backups/restore/git/repositories/* /data/git/repositories/ + + # adjust file permissions + #chown -R git:git /data + + # Regenerate Git Hooks + /usr/local/bin/gitea -c '/data/gitea/conf/app.ini' admin regenerate hooks + + # Restore db + drop-create-db + #restore-roles + restore-db +} + +source /usr/local/lib/functions.sh +source /usr/local/lib/pg-functions.sh +source /usr/local/lib/file-functions.sh + +main diff --git a/infrastructure/docker-backup/test/Dockerfile b/infrastructure/docker-backup/test/Dockerfile new file mode 100644 index 0000000..f2e19b6 --- /dev/null +++ b/infrastructure/docker-backup/test/Dockerfile @@ -0,0 +1,11 @@ +FROM c4k-jira-backup + +RUN apt update +RUN apt -yqq --no-install-recommends --yes install curl default-jre-headless + +RUN curl -L -o /tmp/serverspec.jar \ + https://github.com/DomainDrivenArchitecture/dda-serverspec-crate/releases/download/2.0.0/dda-serverspec-standalone.jar + +COPY serverspec.edn /tmp/serverspec.edn + +RUN java -jar /tmp/serverspec.jar /tmp/serverspec.edn -v \ No newline at end of file diff --git a/infrastructure/docker-backup/test/serverspec.edn b/infrastructure/docker-backup/test/serverspec.edn new file mode 100644 index 0000000..09623c7 --- /dev/null +++ b/infrastructure/docker-backup/test/serverspec.edn @@ -0,0 +1,6 @@ +{:file [{:path "/usr/local/bin/init.sh" :mod "700"} + {:path "/usr/local/bin/backup.sh" :mod "700"} + {:path "/usr/local/bin/restore.sh" :mod "700"} + {:path "/usr/local/bin/restic-snapshots.sh" :mod "700"} + {:path "/entrypoint.sh" :mod "700"} + {:path "/entrypoint-start-and-wait.sh" :mod "700"}]} diff --git a/src/main/resources/backup/backup-restore-deployment.yaml b/src/main/resources/backup/backup-restore-deployment.yaml new file mode 100644 index 0000000..c74145b --- /dev/null +++ b/src/main/resources/backup/backup-restore-deployment.yaml @@ -0,0 +1,50 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: backup-restore +spec: + replicas: 0 + selector: + matchLabels: + app: backup-restore + strategy: + type: Recreate + template: + metadata: + labels: + app: backup-restore + app.kubernetes.io/name: backup-restore + app.kubernetes.io/part-of: gitea + spec: + containers: + - image: domaindrivenarchitecture/c4k-gitea-backup + name: backup-app + imagePullPolicy: IfNotPresent + command: ["/entrypoint-start-and-wait.sh"] + env: + - name: AWS_DEFAULT_REGION + value: eu-central-1 + - name: AWS_ACCESS_KEY_ID_FILE + value: /var/run/secrets/backup-secrets/aws-access-key-id + - name: AWS_SECRET_ACCESS_KEY_FILE + value: /var/run/secrets/backup-secrets/aws-secret-access-key + - name: RESTIC_REPOSITORY + valueFrom: + configMapKeyRef: + name: backup-config + key: restic-repository + - name: RESTIC_PASSWORD_FILE + value: /var/run/secrets/backup-secrets/restic-password + volumeMounts: + - name: gitea-data-volume + mountPath: /var/backups + - name: backup-secret-volume + mountPath: /var/run/secrets/backup-secrets + readOnly: true + volumes: + - name: gitea-data-volume + persistentVolumeClaim: + claimName: gitea-data-pvc + - name: backup-secret-volume + secret: + secretName: backup-secret \ No newline at end of file diff --git a/src/main/resources/backup/config.yaml b/src/main/resources/backup/config.yaml new file mode 100644 index 0000000..2d60d3c --- /dev/null +++ b/src/main/resources/backup/config.yaml @@ -0,0 +1,9 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: backup-config + labels: + app.kubernetes.io/name: backup + app.kubernetes.io/part-of: gitea +data: + restic-repository: restic-repository \ No newline at end of file diff --git a/src/main/resources/backup/cron.yaml b/src/main/resources/backup/cron.yaml new file mode 100644 index 0000000..7ee682e --- /dev/null +++ b/src/main/resources/backup/cron.yaml @@ -0,0 +1,47 @@ +apiVersion: batch/v1beta1 +kind: CronJob +metadata: + name: gitea-backup + labels: + app.kubernetes.part-of: gitea +spec: + schedule: "10 23 * * *" + successfulJobsHistoryLimit: 1 + failedJobsHistoryLimit: 1 + jobTemplate: + spec: + template: + spec: + containers: + - name: backup-app + image: domaindrivenarchitecture/c4k-gitea-backup + imagePullPolicy: IfNotPresent + command: ["/entrypoint.sh"] + env: + - name: AWS_DEFAULT_REGION + value: eu-central-1 + - name: AWS_ACCESS_KEY_ID_FILE + value: /var/run/secrets/backup-secrets/aws-access-key-id + - name: AWS_SECRET_ACCESS_KEY_FILE + value: /var/run/secrets/backup-secrets/aws-secret-access-key + - name: RESTIC_REPOSITORY + valueFrom: + configMapKeyRef: + name: backup-config + key: restic-repository + - name: RESTIC_PASSWORD_FILE + value: /var/run/secrets/backup-secrets/restic-password + volumeMounts: + - name: gitea-data-volume + mountPath: /var/backups + - name: backup-secret-volume + mountPath: /var/run/secrets/backup-secrets + readOnly: true + volumes: + - name: gitea-data-volume + persistentVolumeClaim: + claimName: gitea-data-pvc + - name: backup-secret-volume + secret: + secretName: backup-secret + restartPolicy: OnFailure \ No newline at end of file diff --git a/src/main/resources/backup/secret.yaml b/src/main/resources/backup/secret.yaml new file mode 100644 index 0000000..c5809e0 --- /dev/null +++ b/src/main/resources/backup/secret.yaml @@ -0,0 +1,9 @@ +apiVersion: v1 +kind: Secret +metadata: + name: backup-secret +type: Opaque +data: + aws-access-key-id: aws-access-key-id + aws-secret-access-key: aws-secret-access-key + restic-password: restic-password \ No newline at end of file