Compare commits

..

1 commit

Author SHA1 Message Date
a55fead350 initial try outs 2024-08-30 14:15:47 +02:00
41 changed files with 255 additions and 853 deletions

View file

@ -5,7 +5,7 @@ stages:
- image
.img: &img
image: "domaindrivenarchitecture/ddadevops-dind:4.14.0"
image: "domaindrivenarchitecture/ddadevops-dind:4.13.0"
services:
- docker:dind
before_script:
@ -15,15 +15,14 @@ stages:
- export IMAGE_TAG=$CI_COMMIT_TAG
.clj-job: &clj
image: "domaindrivenarchitecture/ddadevops-clj:4.14.0"
image: "domaindrivenarchitecture/ddadevops-clj:4.13.0"
cache:
key: ${CI_COMMIT_REF_SLUG}
paths:
- .m2
before_script:
- export RELEASE_ARTIFACT_TOKEN=$MEISSA_REPO_BUERO_RW
- export CLOJARS_USERNAME=$CLOJARS_USER
- export CLOJARS_PASSWORD=$CLOJARS_TOKEN_DOMAINDRIVENARCHITECTURE
- mkdir -p /root/.lein
- echo "{:auth {:repository-auth {#\"clojars\" {:username \"${CLOJARS_USER}\" :password \"${CLOJARS_TOKEN_DOMAINDRIVENARCHITECTURE}\" }}}}" > ~/.lein/profiles.clj
.tag_only: &tag_only

View file

@ -176,7 +176,6 @@ swagger-editor-down: ## Stop Swagger Editor in Docker
# https://makefiletutorial.com/#delete_on_error
# TODO: focus runner on ^:integration` tests
# This is a pending decision about continue drop bb in favor of rust
test-ci: deps ## Test runner for integration tests
$(info --------- Runner for integration tests ---------)
clojure -P -X:test/env:test/run

View file

@ -4,91 +4,9 @@
[<img src="https://domaindrivenarchitecture.org/img/delta-chat.svg" width=20 alt="DeltaChat"> chat over e-mail](mailto:buero@meissa-gmbh.de?subject=community-chat) | [<img src="https://meissa.de/images/parts/contact/mastodon36_hue9b2464f10b18e134322af482b9c915e_5501_filter_14705073121015236177.png" width=20 alt="M"> meissa@social.meissa-gmbh.de](https://social.meissa-gmbh.de/@meissa) | [Blog](https://domaindrivenarchitecture.org) | [Website](https://meissa.de)
Images are available at https://hub.docker.com/r/domaindrivenarchitecture/dda-backup
## Usage example
```clojure
#!/usr/bin/env bb
(require '[babashka.tasks :as tasks]
'[dda.backup.restic :as rc]
'[dda.backup.backup :as bak]
'[dda.backup.restore :as rs])
(def config {:restic-repo
:debug true
:restic-repository "/restic-repo"
:password-file "/tmp/file_password"
:new-password-file "/tmp/file_new_password"
:backup-path "files"
:execution-directory "/var/backups/"
:files ["test-backup"]
:restore-target-directory "test-restore"})
(defn prepare!
[]
(tasks/shell "mkdir" "-p" "/var/backups/test-backup")
(spit "/var/backups/test-backup/file" "I was here")
(tasks/shell "mkdir" "-p" "/var/backups/test-restore"))
(defn restic-repo-init!
[]
(println "\nrestic-repo-init!")
(rc/init! config))
(defn restic-backup!
[]
(println "\nrestic-backup!")
(bak/backup-file! config))
(defn list-snapshots!
[]
(println "\nlist-snapshots!")
(rc/list-snapshots! config))
(defn restic-restore!
[]
(println "\nrestic-restore!")
(rs/restore-file! config))
(defn change-password!
[]
(println "\nchange-password!")
(rc/change-password! config))
(defn restic-backup-with-new!
[]
(println "\nrestic-backup with new!")
(bak/backup-file! (merge {:password-file "/tmp/file_new_password"} config)))
(defn list-snapshots-with-new!
[]
(println "\nlist-snapshots with new!")
(rc/list-snapshots! (merge {:password-file "/tmp/file_new_password"} config)))
(prepare!)
(restic-repo-init!)
(restic-backup!)
(list-snapshots!)
(restic-restore!)
(change-password!)
(restic-backup!)
(list-snapshots!)
(restic-restore!)
(restic-backup-with-new!)
(list-snapshots-with-new!)
```
## Real Life Examples
see:
* https://repo.prod.meissa.de/meissa/c4k-taiga/src/branch/main/infrastructure/backup/image/resources
* https://repo.prod.meissa.de/meissa/c4k-forgejo/src/branch/main/infrastructure/backup/image/resources
## Development & mirrors
Development happens at: https://repo.prod.meissa.de/meissa/dda-backup
@ -101,6 +19,6 @@ Mirrors are:
For more details about our repository model see: https://repo.prod.meissa.de/meissa/federate-your-repos
## License
Copyright © 2024, 2025 meissa GmbH
Copyright © 2024 meissa GmbH
Published under [apache2.0 license](LICENSE.md)
Pls. find licenses of our subcomponents [here](doc/SUBCOMPONENT_LICENSE)

View file

@ -1,5 +1,5 @@
{:project {:name org.domaindrivenarchitecture/dda-backup
:version "5.4.1-SNAPSHOT"}
:version "5.0.0-SNAPSHOT"}
;; ---------------------------------------------------------
:paths
@ -9,27 +9,21 @@
;; ---------------------------------------------------------
:deps
{;; Application
org.clojure/clojure {:mvn/version "1.11.4"}
org.clojure/spec.alpha {:mvn/version "0.5.238"}
orchestra/orchestra {:mvn/version "2021.01.01-1"}
aero/aero {:mvn/version "1.1.6"}
cheshire/cheshire {:mvn/version "5.13.0"}
com.widdindustries/cljc.java-time {:mvn/version "0.1.21"}
org.babashka/http-client {:mvn/version "0.3.11"}}
orchestra/orchestra {:mvn/version "2021.01.01-1"}}
;; ---------------------------------------------------------
;; ---------------------------------------------------------
:aliases
{
;; ------------
;; Add libraries and paths to support additional test tools
:test/env
{}
;; Test runner - local and CI
;; call with :watch? true to start file watcher and re-run tests on saved changes
:test/run
{:extra-paths ["test"]
:extra-deps {lambdaisland/kaocha {:mvn/version "1.91.1392"}}
:extra-deps {lambdaisland/kaocha {:mvn/version "1.91.1392"}
expound/expound {:mvn/version "0.9.0"}}
:main-opts ["-m" "kaocha.runner"]
:exec-fn kaocha.runner/exec-fn
:exec-args {:randomize? false

View file

@ -1,132 +0,0 @@
# Application Backup for k8s
# Runtime View
```mermaid
C4Context
title Backup
Enterprise_Boundary(b0, "Infrastructure") {
Container_Boundary(k3s, "K3S") {
Container(wl, "workload")
Container(br, "backup", "cron job")
SystemDb(db, "postgres database")
SystemDb(pv, "persistent volume")
Rel(br, wl, "stop/start")
Rel(br, db, "read")
Rel(br, pv, "read")
}
System(s3, "S3", "")
Rel(br, s3, "backup & manage retention")
}
UpdateLayoutConfig($c4ShapeInRow="2", $c4BoundaryInRow="1")
```
```mermaid
C4Context
title Restore
Enterprise_Boundary(b0, "Infrastructure") {
Container_Boundary(k3s, "K3S") {
Container(wl, "workload")
Container(br, "backup-restore", "deployment")
SystemDb(db, "postgres database")
SystemDb(pv, "persistent volume")
Rel(br, db, "drop & import")
Rel(br, pv, "delete & write")
}
System(s3, "S3", "")
Rel(br, s3, "restore")
}
UpdateLayoutConfig($c4ShapeInRow="2", $c4BoundaryInRow="1")
```
* we use restic to produce small & encrypted backups
* backups are stored in s3
* backup is scheduled at `schedule: "10 23 * * *"`
* backup supports file or postgres-db-dump backup.
* supported restic usecases are backup, restore, change password, list snapshots, support retentions
## Parameter determined by k8s-application
1. name of application deployments **deployment-name** to scale down during backup / restore
2. namespace of application deployments **deployment-namespace**
## backup / restore
### Scale application down
1. Scale [deployment-name] deployment down:
`kubectl -n [deployment-namespace] scale deployment [deployment-name] --replicas=0`
### Manual backup
1. Scale backup-restore deployment up:
`kubectl -n [deployment-namespace] scale deployment backup-restore --replicas=1`
2. exec into pod and execute restore pod
`kubectl -n [deployment-namespace] exec -it backup-restore -- backup.bb`
3. Scale backup-restore deployment down:
`kubectl -n [deployment-namespace] scale deployment backup-restore --replicas=0`
### Manual restore
1. Scale backup-restore deployment up:
`kubectl -n [deployment-namespace] scale deployment backup-restore --replicas=1`
3. exec into pod and execute restore pod
`kubectl -n [deployment-namespace] exec -it backup-restore -- restore.bb`
4. Scale backup-restore deployment down:
`kubectl -n [deployment-namespace] scale deployment backup-restore --replicas=0`
### Scale application up
1. Scale [deployment-name] deployment up:
`kubectl -n [deployment-namespace] scale deployment [deployment-name] --replicas=1`
## Change Password
1. Check restic-new-password env is set in backup deployment
```
kind: Deployment
metadata:
name: backup-restore
spec:
spec:
containers:
- name: backup-app
env:
- name: RESTIC_NEW_PASSWORD_FILE
value: /var/run/secrets/backup-secrets/restic-new-password
```
2. Add restic-new-password to secret
```
kind: Secret
metadata:
name: backup-secret
data:
restic-password: old
restic-new-password: new
```
3. Scale backup-restore deployment up:
`kubectl -n [deployment-namespace] scale deployment backup-restore --replicas=1`
4. exec into pod and execute restore pod
`kubectl -n [deployment-namespace] exec -it backup-restore -- change-password.bb`
5. Scale backup-restore deployment down:
`kubectl -n [deployment-namespace] scale deployment backup-restore --replicas=0`
6. Replace restic-password with restic-new-password in secret
```
kind: Secret
metadata:
name: backup-secret
data:
restic-password: new
```

View file

@ -1,3 +0,0 @@
# Setup
See: https://clojure.org/guides/install_clojure#_linux_instructions

View file

@ -8,7 +8,7 @@ import logging
name = 'dda-backup'
MODULE = 'NOT_SET'
PROJECT_ROOT_PATH = '../..'
version = "5.4.1-dev"
version = "5.0.0-dev"
@init

View file

@ -1,50 +1,79 @@
# changing password on restic repository
## Init Statemachine
## config to use
### Inputs
1. `restic-password: ""`
2. `restic-password-to-rotate: ""`
To change the password add new-password-file to config.
### Manual init the restic repository for the first time
```clojure
{:password-file "/restic-pwd"
:new-password-file "/new-restic-pwd"}
```
1. apply backup-and-restore pod:
`kubectl scale deployment backup-restore --replicas=1`
2. exec into pod and execute restore pod (press tab to get your exact pod name)
`kubectl exec -it backup-restore-... -- /usr/local/bin/init.sh`
3. remove backup-and-restore pod:
`kubectl scale deployment backup-restore --replicas=0`
## restic: decide which password to choose
### Password Rotation
1. apply backup-and-restore pod:
`kubectl scale deployment backup-restore --replicas=1`
2. add new password to restic repository
`restic key add ....`
=> Trigger ::
field (1) credential current
filed (2) credential new
3. replace field (1) with (2) & clear (2)
4. remove old key - ???
`restic remove ....`
If there is a new-password-file defined, decide witch to use:
```mermaid
stateDiagram-v2
state new? <<choice>>
state "restic --password-file /new-pwd check" as check_new
state new_works? <<choice>>
[*] --> containsNewPassword?
containsNewPassword? --> new?
new? --> check_new: yes
new? --> use_old: no
check_new --> new_works?
new_works? --> use_new: yes
new_works? --> use_old: no
[*] --> init
init --> backup_ready: trigger, restic-password !empty
backup_ready --> new_password_added: restic-password !empty && restic-password-to-rotate !empty
new_password_added --> backup_ready: restic-password !empty && restic-password-to-rotate empty
```
# Process to change password in k8s
### First Steps
1. Cloud Testserver hochfahren
2. Dort backup-restore deployment (leeres Secret mgl.?), neues Secret "rotation-credential-secret" als Daten
3. mounten von angelegtem Secret in Pod backup-restore
4. ba*bash*ka Skript in pod starten -> liest Secret ?leer
5. Micha cons.
```mermaid
sequenceDiagram
actor d as DevOps
participant s as BackupSecret
participant a as ApplicationDeployment
participant b as BackupDeployment
d ->> s: add new-password-file
d ->> a: scale down
d ->> b: scale up
d ->> b: shell into
activate b
b ->> b: call change password
deactivate b
d ->> s: replace password-file with new-password-file
d ->> b: scale down
d ->> a: scale up
```
participant k8s
participant e as entrypoint.sh
participant rm as restic-management.clj
k8s ->> e: cronjob calls
e ->> rm: start-file
rm ->> rm: rotate
activate rm
rm ->> rm: read-backup-repository-state (state)
rm ->> rm: read-secret (backup-secret/restic-password, rotation-credential-secret/rotation-credential)
rm ->> rm: switch
activate rm
rm ->> rm: if init && restic-password != null
activate rm
rm ->> rm: init.sh
rm ->> rm: state init -> backup-ready
deactivate rm
rm ->> rm: if backup-ready && rotation-credential != null
activate rm
rm ->> rm: add-new-password-to-restic-repository.sh
rm ->> rm: state backup-ready -> new-password-added
deactivate rm
rm ->> rm: if new-password-added && rotation-credential == null
activate rm
rm ->> rm: remove-old-password-from-restic-repository.sh
rm ->> rm: state new-password-added -> backup-ready
deactivate rm
deactivate rm
rm ->> rm: store-repository-state (state)
deactivate rm
```

View file

@ -5,6 +5,3 @@ ADD resources /tmp/
RUN /tmp/install.sh
ADD local/ /usr/local/lib/dda-backup
RUN init-bb.bb
#ADD resources2 /tmp/
#RUN install -m 0700 -o root -g root /tmp/test.bb /usr/local/bin/
#RUN FILE_PASSWORD_FILE=/tmp/file_password test.bb

View file

@ -18,12 +18,12 @@ function main() {
apt-get install -qqy ca-certificates curl gnupg postgresql-client-16 restic openjdk-21-jre-headless nano
curl -Ss --fail https://www.postgresql.org/media/keys/ACCC4CF8.asc | gpg --dearmor | tee /etc/apt/trusted.gpg.d/postgresql-common_pgdg_archive_keyring.gpg
sh -c 'echo "deb [signed-by=/etc/apt/trusted.gpg.d/postgresql-common_pgdg_archive_keyring.gpg] https://apt.postgresql.org/pub/repos/apt jammy-pgdg main" > /etc/apt/sources.list.d/pgdg.list'
upgradeSystem
babashka_install
} > /dev/null
update-ca-certificates
install -m 0700 -o root -g root /tmp/init-bb.bb /usr/local/bin/
install -m 0600 -o root -g root /tmp/bb.edn /usr/local/bin/
cleanupDocker
}

View file

@ -1,24 +0,0 @@
{:restic-repo {:password-file #env-or-file "FILE_PASSWORD_FILE"
:restic-repository "/restic-repo"
:debug true}
:file-config #merge [#ref [:restic-repo]
{:new-password-file "/tmp/file_new_password"
:backup-path "files"
:execution-directory "/var/backups/"
:files ["test-backup"]
:restore-target-directory "test-restore"}]
:file-config-with-new #merge [#ref [:file-config]
{:password-file "/tmp/file_new_password"}]
:db-config #merge [#ref [:restic-repo]
{:new-password-file "/tmp/file_new_password"
:backup-path "db"
:pg-db "mydb"
:pg-user "user"
:pg-password "password"}]
:db-roles-config #merge [#ref [:restic-repo]
{:new-password-file "/tmp/file_new_password"
:backup-path "db-roles"
:pg-db "mydb"
:pg-user "user"
:pg-password "password"}]
:dry-run {:dry-run true :debug true}}

View file

@ -1,75 +0,0 @@
#!/usr/bin/env bb
(require '[babashka.tasks :as tasks]
'[dda.backup.config :as cfg]
'[dda.backup.restic :as rc]
'[dda.backup.postgresql :as pg]
'[dda.backup.backup :as bak]
'[dda.backup.restore :as rs])
(def config (cfg/read-config "/tmp/config.edn"))
(defn prepare!
[]
(println config)
(tasks/shell "mkdir" "-p" "/var/backups/test-backup")
(spit "/var/backups/test-backup/file" "I was here")
(tasks/shell "mkdir" "-p" "/var/backups/test-restore")
(pg/create-pg-pass! (:db-config config)))
(defn restic-repo-init!
[]
(println "\nrestic-repo-init!")
(rc/init! (:file-config config))
(rc/init! (merge (:db-config config) (:dry-run config)))
(rc/init! (merge (:db-roles-config config) (:dry-run config))))
(defn restic-backup!
[]
(println "\nrestic-backup!")
(bak/backup-file! (:file-config config))
(bak/backup-db-roles! (merge (:db-roles-config config) (:dry-run config)))
(bak/backup-db! (merge (:db-config config) (:dry-run config))))
(defn list-snapshots!
[]
(println "\nlist-snapshots!")
(rc/list-snapshots! (:file-config config))
(rc/list-snapshots! (merge (:db-roles-config config) (:dry-run config)))
(rc/list-snapshots! (merge (:db-config config) (:dry-run config))))
(defn restic-restore!
[]
(println "\nrestic-restore!")
(rs/restore-file! (:file-config config))
(pg/drop-create-db! (merge (:db-config config) (:dry-run config)))
(rs/restore-db-roles! (merge (:db-roles-config config) (:dry-run config)))
(rs/restore-db! (merge (:db-config config) (:dry-run config))))
(defn change-password!
[]
(println "\nchange-password!")
(rc/change-password! (:file-config config)))
(defn restic-backup-with-new!
[]
(println "\nrestic-backup with new!")
(bak/backup-file! (:file-config-with-new config)))
(defn list-snapshots-with-new!
[]
(println "\nlist-snapshots with new!")
(rc/list-snapshots! (:file-config-with-new config)))
(prepare!)
(restic-repo-init!)
(restic-backup!)
(list-snapshots!)
(restic-restore!)
(change-password!)
(restic-backup!)
(list-snapshots!)
(restic-restore!)
(restic-backup-with-new!)
(list-snapshots-with-new!)

View file

@ -1,5 +1,6 @@
FROM dda-backup:latest
# install it
#ADD local/ /usr/local/lib/dda-backup
ADD resources /tmp/
RUN install -m 0700 -o root -g root /tmp/test.bb /usr/local/bin/
RUN FILE_PASSWORD_FILE=/tmp/file_password test.bb
RUN ENV_PASSWORD=env-password FILE_PASSWORD_FILE=/tmp/file_password /tmp/test.bb

View file

@ -0,0 +1,4 @@
{:deps {org.babashka/spec.alpha {:git/url "https://github.com/babashka/spec.alpha"
:git/sha "1a841c4cc1d4f6dab7505a98ed2d532dd9d56b78"}
orchestra/orchestra {:mvn/version "2021.01.01-1"}
org.domaindrivenarchitecture/dda-backup {:local/root "/usr/local/lib/dda-backup"}}}

View file

@ -1,24 +0,0 @@
{:restic-repo {:password-file #env-or-file "FILE_PASSWORD_FILE"
:restic-repository "/restic-repo"
:debug true}
:file-config #merge [#ref [:restic-repo]
{:new-password-file "/tmp/file_new_password"
:backup-path "files"
:execution-directory "/var/backups/"
:files ["test-backup"]
:restore-target-directory "test-restore"}]
:file-config-with-new #merge [#ref [:file-config]
{:password-file "/tmp/file_new_password"}]
:db-config #merge [#ref [:restic-repo]
{:new-password-file "/tmp/file_new_password"
:backup-path "db"
:pg-db "mydb"
:pg-user "user"
:pg-password "password"}]
:db-roles-config #merge [#ref [:restic-repo]
{:new-password-file "/tmp/file_new_password"
:backup-path "db-roles"
:pg-db "mydb"
:pg-user "user"
:pg-password "password"}]
:dry-run {:dry-run true :debug true}}

View file

@ -0,0 +1,29 @@
#!/usr/bin/env bb
(require '[babashka.tasks :as tasks])
(defn curl-and-check!
[filename artifact-url sha256-url]
(let [filepath (str "/tmp/" filename)]
(tasks/shell "curl" "-SsLo" filepath artifact-url)
(tasks/shell "curl" "-SsLo" "/tmp/checksum" sha256-url)
(tasks/shell "bash" "-c" (str "echo \" " filepath "\"|tee -a /tmp/checksum"))
;(tasks/shell "sha256sum" "-c" "--status" "/tmp/checksum")
))
(defn tar-install!
[filename binname]
(let [filepath (str "/tmp/" filename)]
(tasks/shell "tar" "-C" "/tmp" "-xzf" filepath)
(tasks/shell "install" "-m" "0700" "-o" "root" "-g" "root" (str "/tmp/" binname) "/usr/local/bin/")))
(defn install!
[filename]
(tasks/shell "install" "-m" "0700" "-o" "root" "-g" "root" (str "/tmp/" filename) "/usr/local/bin/"))
(curl-and-check!
"provs-syspec.jar"
"https://repo.prod.meissa.de/attachments/0a1da41e-aa5b-4a3e-a3b1-215cf2d5b021"
"https://repo.prod.meissa.de/attachments/f227cf65-cb0f-46a7-a6cd-28f46917412a")
(install! "provs-syspec.jar")
(tasks/shell "java" "-jar" "/usr/local/bin/provs-syspec.jar" "local" "-c" "/tmp/spec.yml" )

View file

@ -0,0 +1,7 @@
package:
- name: "restic"
command:
- command: "bb -h"
- command: "/tmp/test.bb"

View file

@ -1,75 +1,62 @@
#!/usr/bin/env bb
(require '[babashka.tasks :as tasks]
'[dda.backup.config :as cfg]
'[dda.backup.core :as bc]
'[dda.backup.restic :as rc]
'[dda.backup.postgresql :as pg]
'[dda.backup.backup :as bak]
'[dda.backup.restore :as rs])
(def config (cfg/read-config "/tmp/config.edn"))
(def restic-repo {:password-file "restic-pwd"
:restic-repository "restic-repo"})
(def file-config (merge restic-repo {:backup-path "files"
:files ["test-backup"]
:restore-target-directory "test-restore"}))
(def db-config (merge restic-repo {:backup-path "db"
:pg-db "mydb"
:pg-user "user"
:pg-password "password"}))
(def dry-run {:dry-run true :debug true})
(defn prepare!
[]
(println config)
(tasks/shell "mkdir" "-p" "/var/backups/test-backup")
(spit "/var/backups/test-backup/file" "I was here")
(tasks/shell "mkdir" "-p" "/var/backups/test-restore")
(pg/create-pg-pass! (:db-config config)))
(spit "/tmp/file_password" "file-password")
(println (bc/env-or-file "FILE_PASSWORD"))
(println (bc/env-or-file "ENV_PASSWORD"))
(spit "restic-pwd" "ThePassword")
(tasks/shell "mkdir" "-p" "test-backup")
(spit "test-backup/file" "I was here")
(tasks/shell "mkdir" "-p" "test-restore")
(pg/create-pg-pass! db-config))
(defn restic-repo-init!
[]
(println "\nrestic-repo-init!")
(rc/init! (:file-config config))
(rc/init! (merge (:db-config config) (:dry-run config)))
(rc/init! (merge (:db-roles-config config) (:dry-run config))))
[]
(rc/init! file-config)
(rc/init! (merge db-config dry-run)))
(defn restic-backup!
[]
(println "\nrestic-backup!")
(bak/backup-file! (:file-config config))
(bak/backup-db-roles! (merge (:db-roles-config config) (:dry-run config)))
(bak/backup-db! (merge (:db-config config) (:dry-run config))))
[]
(bak/backup-file! file-config)
(bak/backup-db! (merge db-config dry-run)))
(defn list-snapshots!
[]
(println "\nlist-snapshots!")
(rc/list-snapshots! (:file-config config))
(rc/list-snapshots! (merge (:db-roles-config config) (:dry-run config)))
(rc/list-snapshots! (merge (:db-config config) (:dry-run config))))
(rc/list-snapshots! file-config)
(rc/list-snapshots! (merge db-config dry-run)))
(defn restic-restore!
[]
(println "\nrestic-restore!")
(rs/restore-file! (:file-config config))
(pg/drop-create-db! (merge (:db-config config) (:dry-run config)))
(rs/restore-db-roles! (merge (:db-roles-config config) (:dry-run config)))
(rs/restore-db! (merge (:db-config config) (:dry-run config))))
(defn change-password!
[]
(println "\nchange-password!")
(rc/change-password! (:file-config config)))
(defn restic-backup-with-new!
[]
(println "\nrestic-backup with new!")
(bak/backup-file! (:file-config-with-new config)))
(defn list-snapshots-with-new!
[]
(println "\nlist-snapshots with new!")
(rc/list-snapshots! (:file-config-with-new config)))
[]
(rs/restore-file! file-config)
(pg/drop-create-db! (merge db-config dry-run))
(rs/restore-db! (merge db-config dry-run)))
(prepare!)
(restic-repo-init!)
(restic-backup!)
(list-snapshots!)
(restic-restore!)
(change-password!)
(restic-backup!)
(list-snapshots!)
(restic-restore!)
(restic-backup-with-new!)
(list-snapshots-with-new!)

View file

@ -1,5 +1,4 @@
(ns dda.backup.backup
"Part of API, represents the application service layer."
(:require
[orchestra.core :refer [defn-spec]]
[clojure.spec.alpha :as s]
@ -27,31 +26,25 @@
(s/merge ::pg/pg-config
::restic/restic-config))
(defn- config-w-defaults
[config]
(if (restic/use-new-password? config)
(merge default config {:password-file (:new-password-file config)})
(merge default config)))
(defn-spec backup-file! nil?
[config ::backup-file-config]
(let [config-2-use (config-w-defaults config)]
(restic/unlock! config-2-use)
(let [config-w-defaults (merge default config)]
(restic/unlock! config-w-defaults)
(i/execute!
(domain/backup-files-command config-2-use)
config-2-use)
(restic/forget! config-2-use)))
(domain/backup-files-command config-w-defaults)
config-w-defaults)
(restic/forget! config-w-defaults)))
(defn-spec backup-db-roles! nil?
[config ::pg-role-dump-config]
(let [config-2-use (config-w-defaults config)]
(restic/unlock! config-2-use)
(i/execute! (domain/backup-role-command config-2-use) config-2-use)
(restic/forget! config-2-use)))
(let [config-w-defaults (merge default config)]
(restic/unlock! config-w-defaults)
(i/execute! (domain/backup-role-command config-w-defaults) config-w-defaults)
(restic/forget! config-w-defaults)))
(defn-spec backup-db! nil?
[config ::pg-db-dump-config]
(let [config-2-use (config-w-defaults config)]
(restic/unlock! config-2-use)
(i/execute! (domain/backup-db-command config-2-use) config-2-use)
(restic/forget! config-2-use)))
(let [config-w-defaults (merge default config)]
(restic/unlock! config-w-defaults)
(i/execute! (domain/backup-db-command config-w-defaults) config-w-defaults)
(restic/forget! config-w-defaults)))

View file

@ -1,5 +1,4 @@
(ns dda.backup.backup.domain
"Intended for internal use only, represents the domain layer."
(:require
[orchestra.core :refer [defn-spec]]
[clojure.spec.alpha :as s]
@ -18,7 +17,7 @@
(defn-spec backup-files-command ::cd/commands
[config ::backup-file-config]
(let [{:keys [files]} config]
[(rd/repo-command config (into ["backup"] files) false)]))
[(rd/repo-command config (into ["backup"] files))]))
(defn-spec backup-role-command ::cd/commands
[config ::pd/pg-role-dump-config]

View file

@ -1,22 +0,0 @@
(ns dda.backup.config
"Part of API, represents the application service layer."
(:require [aero.core :as aero]
[dda.backup.core :as bc]
[dda.backup.infrastructure :as i]))
(defmethod aero/reader 'env-or-file
[{:keys [profile] :as opts} tag value]
(bc/env-or-file value))
(defmethod aero/reader 'gopass
[{:keys [profile] :as opts} tag value]
(i/execute-out! (into ["gopass" "show" "-y" "-o"] value) {}))
(defn read-config
[file]
(try
(aero/read-config file)
(catch Exception e
(do (println (str "Warn: " e))
{}))
))

View file

@ -1,5 +1,4 @@
(ns dda.backup.core
"Part of API, represents the application service layer."
(:require
[orchestra.core :refer [defn-spec]]
[clojure.spec.alpha :as s]

View file

@ -1,5 +1,4 @@
(ns dda.backup.core.domain
"Intended for internal use only, represents the domain layer."
(:require
[clojure.spec.alpha :as s]))

View file

@ -1,29 +1,8 @@
(ns dda.backup.infrastructure
"Intenden for internal use, represents the infrastructure layer."
(:require [orchestra.core :refer [defn-spec]]
[babashka.tasks :as t]
[babashka.http-client :as http]
[dda.backup.core.domain :as core]))
(defn-spec execute-out! string?
[command ::core/command
config ::core/execution]
(let [{:keys [dry-run debug]} config]
(when debug
(println command))
(when-not dry-run
(:out (t/shell {:out :string :err :string} (clojure.string/join " " command))))))
(defn-spec execute-single! string?
[command ::core/command
config ::core/execution]
(let [{:keys [dry-run debug]} config]
(when debug
(println command))
(when-not dry-run
(:out (t/shell {:err :string} (clojure.string/join " " command))))))
(defn-spec execute! nil?
[commands ::core/commands
config ::core/execution]
@ -33,8 +12,3 @@
(println c))
(when-not dry-run
(apply t/shell c)))))
(defn-spec post! nil?
[url string?
content string?]
(http/post url {:body content}))

View file

@ -1,49 +0,0 @@
(ns dda.backup.monitoring
(:require
[orchestra.core :refer [defn-spec]]
[clojure.spec.alpha :as s]
[dda.backup.monitoring.domain :as domain]
[dda.backup.infrastructure :as i]))
(def default {:url "http://prometheus-pushgateway.monitoring.svc.cluster.local:9091/metrics/job"
:namespace "default"
:metrics {:kube_job_status_active 0
:kube_job_status_failed 0
:kube_job_status_succeeded 0}})
(s/def ::config (s/keys :req-un [::name]
:opt-un [::url ::metrics ::namespace]))
(defn- config-w-defaults
[config]
(merge default config))
(defn-spec send-metrics! nil?
[config ::config]
(let [config-2-use (config-w-defaults config)
{:keys [url name]} config-2-use]
(try
(i/post! (str url "/" name) (domain/collect-metrics config-2-use))
(catch Exception e (println (str "Warn: unable to send log" (.getMessage e)))))))
(defn-spec backup-start-metrics! nil?
[config ::config]
(send-metrics!
{:name "backup"
:metrics {:kube_job_status_active 1
:kube_job_status_start_time (long (/ (System/currentTimeMillis) 1000))}}))
(defn-spec backup-success-metrics! nil?
[config ::config]
(send-metrics!
{:name "backup"
:metrics {:kube_job_status_succeeded 1
:kube_job_status_completion_time (long (/ (System/currentTimeMillis) 1000))}}))
(defn-spec backup-fail-metrics! nil?
[config ::config]
(send-metrics!
{:name "backup"
:metrics {:kube_job_status_failed 1
:kube_job_status_completion_time (long (/ (System/currentTimeMillis) 1000))}}))

View file

@ -1,23 +0,0 @@
(ns dda.backup.monitoring.domain
(:require
[orchestra.core :refer [defn-spec]]
[clojure.spec.alpha :as s]
[clojure.string :as st]))
(s/def ::url string?)
(s/def ::name string?)
(s/def ::namespace string?)
(s/def ::metrics map?)
(s/def ::config (s/keys :req-un [::url ::name ::metrics ::namespace]))
(defn-spec collect-metrics string?
[config ::config]
(let [{:keys [metrics namespace]} config]
(str
(->> metrics
(map (fn [entry] (str (name (key entry))
"{namespace=\"" namespace "\"}" " "
(val entry))))
(st/join "\n"))
"\n")))

View file

@ -1,5 +1,4 @@
(ns dda.backup.postgresql
"Part of API, represents the application service layer."
(:require
[orchestra.core :refer [defn-spec]]
[clojure.spec.alpha :as s]

View file

@ -1,5 +1,4 @@
(ns dda.backup.postgresql.domain
"Intended for internal use only, represents the domain layer."
(:require
[orchestra.core :refer [defn-spec]]
[clojure.spec.alpha :as s]
@ -51,7 +50,7 @@
"--no-password"]
command
["|" "grep" pg-role-prefix "|"]
(rd/repo-command config ["backup" "--stdin"] false)))))
(rd/repo-command config ["backup" "--stdin"])))))
(defn-spec pgdump-command ::cd/command
[config ::pg-db-dump-config
@ -64,7 +63,7 @@
"--no-password"]
command
["|"]
(rd/repo-command config ["backup" "--stdin"] false)))))
(rd/repo-command config ["backup" "--stdin"])))))
(defn-spec pgpass string?
[config ::pg-config]

View file

@ -1,5 +1,4 @@
(ns dda.backup.restic
"Part of API, represents the application service layer."
(:require
[orchestra.core :refer [defn-spec]]
[clojure.spec.alpha :as s]
@ -13,81 +12,39 @@
:months-to-keep 12}))
(s/def ::restic-config
(s/merge ::core/execution
(s/merge ::core/execution
(s/keys :req-un [::domain/restic-repository
::domain/backup-path]
:opt-un [::domain/certificate-file
::domain/password-file
::domain/new-password-file
::domain/days-to-keep
::domain/months-to-keep])))
(s/def ::check-result #{:initialized :wrong-password :not-initialized :error})
(defn-spec check ::check-result
"Check a restic repository whether it is initialized and there are correct credentials given."
(defn-spec initalized? boolean?
[restic-config ::restic-config]
(let [config-w-defaults (merge core/default restic-config)]
(try
(i/execute! (domain/check-repo-command config-w-defaults) config-w-defaults)
:initialized
(catch Exception e
(let [data (ex-data e)
parsed-error (domain/parse-check-error (get-in data [:proc :err]))]
(cond
(= parsed-error :not-initialized) :not-initialized
(= parsed-error :wrong-password) :wrong-password
:default :error))))))
(defn-spec use-new-password? boolean?
"Check, whether a given new password should b used instead of the normal one."
[restic-config ::restic-config]
(if (contains? restic-config :new-password-file)
(= :initialized (check (merge restic-config {:password-file (:new-password-file restic-config)})))
false))
(defn- config-w-defaults
"Merge given configuration with defaults & replace restic-password with restic-new-password if necessary."
[restic-config]
(if (use-new-password? restic-config)
(merge default restic-config {:password-file (:new-password-file restic-config)})
(merge default restic-config)))
(defn-spec
^{:deprecated "0.2.0"
:superseded-by "check"}
initalized? boolean?
[restic-config ::restic-config]
(let [config-2-use (config-w-defaults restic-config)]
(= :initialized (check config-2-use))))
true
(catch Exception e false))))
(defn-spec init! nil?
[restic-config ::restic-config]
(let [config-2-use (config-w-defaults restic-config)]
(when (= :not-initialized (check config-2-use))
(i/execute! (domain/init-repo-command config-2-use) config-2-use))))
(let [config-w-defaults (merge core/default restic-config)]
(when (not (initalized? config-w-defaults))
(i/execute! (domain/init-repo-command config-w-defaults) config-w-defaults))))
(defn-spec unlock! nil?
[restic-config ::restic-config]
(let [config-2-use (config-w-defaults restic-config)]
(i/execute! (domain/unlock-repo-command config-2-use) config-2-use)))
(let [config-w-defaults (merge core/default restic-config)]
(i/execute! (domain/unlock-repo-command config-w-defaults) config-w-defaults)))
(defn-spec forget! nil?
[restic-config ::restic-config]
(let [config-2-use (config-w-defaults restic-config)]
(i/execute! (domain/forget-command config-2-use) config-2-use)))
(let [config-w-defaults (merge core/default restic-config)]
(i/execute! (domain/forget-command config-w-defaults) config-w-defaults)))
(defn-spec list-snapshots! nil?
[restic-config ::restic-config]
(let [config-2-use (config-w-defaults restic-config)]
(i/execute! (domain/list-snapshot-command config-2-use) config-2-use)))
(defn-spec change-password! nil?
[restic-config ::restic-config]
(when (contains? restic-config :new-password-file)
(let [config-2-use (merge core/default restic-config)]
(when (= :initialized (check config-2-use))
(do
(i/execute! (domain/change-password-command config-2-use) config-2-use)
(when-not (= :wrong-password (check config-2-use))
(throw (Exception. "password-change did not work!"))))))))
(let [config-w-defaults (merge core/default restic-config)]
(i/execute! (domain/list-snapshot-command config-w-defaults) config-w-defaults)))

View file

@ -1,5 +1,4 @@
(ns dda.backup.restic.domain
"Intended for internal use only, represents the domain layer."
(:require
[orchestra.core :refer [defn-spec]]
[clojure.spec.alpha :as s]
@ -7,40 +6,31 @@
(s/def ::certificate-file string?)
(s/def ::password-file string?)
(s/def ::new-password-file string?)
(s/def ::restic-repository string?)
(s/def ::backup-path string?)
(s/def ::days-to-keep pos?)
(s/def ::months-to-keep pos?)
(s/def ::restic-config
(s/keys :req-un [::restic-repository
::backup-path
::days-to-keep
(s/keys :req-un [::restic-repository
::backup-path
::days-to-keep
::months-to-keep]
:opt-un [::certificate-file
::password-file
::new-password-file
:opt-un [::certificate-file
::password-file
::cd/execution-directory]))
(s/def ::check-error #{:not-initialized :wrong-password :no-password :unknown})
(defn-spec repo-command ::cd/command
[config ::restic-config
command ::cd/command
read-error-stream boolean?]
(let [{:keys [certificate-file password-file execution-directory
restic-repository backup-path]} config
shell-option (if read-error-stream {:err :string} {})]
command ::cd/command]
(let [{:keys [certificate-file password-file execution-directory
restic-repository backup-path]} config]
(into
[]
(concat
(cond
(some? execution-directory)
[(merge shell-option {:dir execution-directory})]
read-error-stream
[shell-option]
:default [])
(if (some? execution-directory)
[{:dir execution-directory}]
[])
["restic" "-r" (str restic-repository "/" backup-path) "-v"]
(cond
(some? certificate-file)
@ -53,19 +43,19 @@
(defn-spec check-repo-command ::cd/commands
[config ::restic-config]
[(repo-command config ["check"] true)])
[(repo-command config ["check"])])
(defn-spec init-repo-command ::cd/commands
[config ::restic-config]
[(repo-command config ["init"] false)])
[(repo-command config ["init"])])
(defn-spec unlock-repo-command ::cd/commands
[config ::restic-config]
[(repo-command config ["--cleanup-cache" "unlock"] false)])
[(repo-command config ["--cleanup-cache" "unlock"])])
(defn-spec list-snapshot-command ::cd/commands
[config ::restic-config]
[(repo-command config ["snapshots"] false)])
[(repo-command config ["snapshots"])])
(defn-spec forget-command ::cd/commands
[config ::restic-config]
@ -73,20 +63,4 @@
[(repo-command config ["forget" "--group-by" ""
"--keep-last" "1"
"--keep-daily" (str days-to-keep)
"--keep-monthly" (str months-to-keep) "--prune"] false)]))
(defn-spec change-password-command ::cd/command
[config ::restic-config]
(if (contains? config :new-password-file)
(let [{:keys [new-password-file]} config]
[(repo-command config ["--new-password-file" new-password-file
"key" "passwd"] false)])
(throw (Exception. "change-password: new password required"))))
(defn-spec parse-check-error ::check-error
[error string?]
(cond
(clojure.string/includes? error "Fatal: unable to open config file") :not-initialized
(clojure.string/includes? error "Fatal: wrong password or no key found") :wrong-password
(clojure.string/includes? error "Resolving password failed") :no-password
:default :unknown))
"--keep-monthly" (str months-to-keep) "--prune"])]))

View file

@ -1,5 +1,4 @@
(ns dda.backup.restore
"Part of API, represents the application service layer."
(:require
[orchestra.core :refer [defn-spec]]
[clojure.spec.alpha :as s]
@ -21,29 +20,34 @@
(s/merge ::pg/pg-config
(s/keys :req-un [::domain/snapshot-id])))
(defn- config-w-defaults
[config]
(if (restic/use-new-password? config)
(merge default config {:password-file (:new-password-file config)})
(merge default config)))
(defn-spec restore-file! nil?
[config ::restore-file-config]
(let [config-2-use (config-w-defaults config)]
(restic/unlock! config-2-use)
(let [config-w-defaults (merge default config)]
(restic/unlock! config-w-defaults)
(i/execute!
(domain/restore-dir-command config-2-use)
config-2-use)))
(domain/restore-dir-command config-w-defaults)
config-w-defaults)))
(defn-spec restore-db! nil?
[config ::restore-db-config]
(let [config-2-use (config-w-defaults config)]
(restic/unlock! config-2-use)
(i/execute! (domain/restore-db-command config-2-use) config-2-use)))
(let [config-w-defaults (merge default config)]
(restic/unlock! config-w-defaults)
(i/execute! (domain/restore-db-command config-w-defaults) config-w-defaults)))
(defn-spec restore-db-roles! nil?
[config ::restore-db-config]
(let [config-2-use (config-w-defaults config)]
(restic/unlock! config-2-use)
(i/execute! (domain/restore-db-roles-command config-2-use) config-2-use)))
;; function restore-roles() {
;; local snapshot_id="${1:-latest}"; shift
;; if [ -z ${CERTIFICATE_FILE} ];
;; then
;; roles-unlock-command
;; restic -r ${RESTIC_REPOSITORY}/${backup_pg_role_path} dump ${snapshot_id} stdin | \
;; psql -d template1 -h ${POSTGRES_SERVICE} -p ${POSTGRES_PORT} -U ${POSTGRES_USER} \
;; --no-password
;; else
;; roles-unlock-command --cacert ${CERTIFICATE_FILE}
;; restic -r ${RESTIC_REPOSITORY}/${backup_pg_role_path} dump ${snapshot_id} stdin --cacert ${CERTIFICATE_FILE} | \
;; psql -d template1 -h ${POSTGRES_SERVICE} -p ${POSTGRES_PORT} -U ${POSTGRES_USER} \
;; --no-password
;; fi
;; }

View file

@ -1,5 +1,4 @@
(ns dda.backup.restore.domain
"Intended for internal use only, represents the domain layer."
(:require
[orchestra.core :refer [defn-spec]]
[clojure.spec.alpha :as s]
@ -10,13 +9,11 @@
(s/def ::restore-target-directory string?)
(s/def ::snapshot-id string?)
(s/def ::clean-up-element string?)
(s/def ::clean-up-elements (s/coll-of ::clean-up-element))
(s/def ::restore-file-config
(s/merge ::rd/restic-config
(s/keys :req-un [::restore-target-directory ::snapshot-id]
:opt-un [::clean-up-elements])))
(s/keys :req-un [::restore-target-directory
::snapshot-id])))
(s/def ::restore-db-config
(s/merge ::pd/pg-config
@ -25,13 +22,9 @@
(defn-spec restore-dir-command ::cd/commands
[config ::restore-file-config]
(let [{:keys [restore-target-directory snapshot-id clean-up-elements]} config]
[(if (contains? config :clean-up-elements)
(into
["rm" "-rf"]
(map #(str restore-target-directory "/" %) clean-up-elements))
["rm" "-rf" restore-target-directory])
(rd/repo-command config ["restore" snapshot-id "--target" restore-target-directory] false)]))
(let [{:keys [restore-target-directory snapshot-id]} config]
[["rm" "-rf" restore-target-directory]
(rd/repo-command config ["restore" snapshot-id "--target" restore-target-directory])]))
(defn-spec restore-db-command ::cd/commands
[config ::restore-db-config]
@ -42,19 +35,7 @@
(into
[]
(concat
(rd/repo-command config ["dump" snapshot-id "stdin"] false)
(rd/repo-command config ["dump" snapshot-id "stdin"])
["|"]
(pd/psql-command config []))))]]))
(defn-spec restore-db-roles-command ::cd/commands
[config ::restore-db-config]
(let [{:keys [snapshot-id]} config]
[["bash" "-c"
(st/join
" "
(into
[]
(concat
(rd/repo-command config ["dump" snapshot-id "stdin"] false)
["|"]
(pd/psql-command (merge config {:pg-db "template1"}) []))))]]))

View file

@ -18,7 +18,7 @@
"file2"
"file2"]]
(cut/backup-files-command {:restic-repository "repo"
:backup-path "dir-at-repo"
;:backup-path "dir-at-repo"
:execution-directory "dir-to-backup"
:days-to-keep 39
:months-to-keep 3

View file

@ -1,20 +0,0 @@
(ns dda.backup.monitoring.domain-test
(:require
[clojure.test :refer [deftest is are testing run-tests]]
[clojure.spec.test.alpha :as st]
[dda.backup.monitoring.domain :as cut]))
(st/instrument `cut/collect-metrics)
(deftest should-collect-metrics
(is (= "\n"
(cut/collect-metrics {:url "url"
:name "name"
:namespace "default"
:metrics {}})))
(is (= "metric1{namespace=\"default\"} 1\nmetric2{namespace=\"default\"} text\n"
(cut/collect-metrics {:url "url"
:name "name"
:namespace "default"
:metrics {:metric1 1
:metric2 "text"}}))))

View file

@ -10,7 +10,7 @@
(st/instrument `cut/forget-command)
(deftest should-calculate-repo-command
(is (= [{:dir "dir" :err :string}
(is (= [{:dir "dir"}
"restic"
"-r"
"repo/dir"
@ -26,45 +26,27 @@
:backup-path "dir"
:days-to-keep 39
:months-to-keep 3}
["backup" "file1" "file2"]
true)))
(is (= [{:dir "dir"}
"restic"
"-r"
"repo/dir"
"-v"
"--cacert"
"ca"
"backup"
"file1"
"file2"]
(cut/repo-command {:certificate-file "ca"
:execution-directory "dir"
:restic-repository "repo"
:backup-path "dir"
:days-to-keep 39
:months-to-keep 3}
["backup" "file1" "file2"] false)))
(is (= [{:err :string} "restic" "-r" "repo/dir" "-v" "--cacert" "ca" "snapshots"]
["backup" "file1" "file2"])))
(is (= ["restic" "-r" "repo/dir" "-v" "--cacert" "ca" "snapshots"]
(cut/repo-command {:certificate-file "ca"
:restic-repository "repo"
:backup-path "dir"
:days-to-keep 39
:months-to-keep 3}
["snapshots"] true)))
["snapshots"])))
(is (= ["restic" "-r" "repo/dir" "-v" "--password-file" "password" "snapshots"]
(cut/repo-command {:password-file "password"
:restic-repository "repo"
:backup-path "dir"
:days-to-keep 39
:months-to-keep 3}
["snapshots"] false)))
["snapshots"])))
(is (= ["restic" "-r" "repo/dir" "-v" "snapshots"]
(cut/repo-command {:restic-repository "repo"
:backup-path "dir"
:days-to-keep 39
:months-to-keep 3}
["snapshots"] false))))
["snapshots"]))))
(deftest should-calculate-init-repo-command
(is (= [["restic" "-r" "repo/dir" "-v" "init"]]
@ -87,29 +69,4 @@
(cut/forget-command {:restic-repository "repo"
:backup-path "dir"
:days-to-keep 39
:months-to-keep 3}))))
(deftest should-calculate-change-password-command
(is (= [["restic"
"-r"
"repo/dir"
"-v"
"--new-password-file"
"/new-pwd"
"key"
"passwd"]]
(cut/change-password-command {:restic-repository "repo"
:new-password-file "/new-pwd"
:backup-path "dir"
:days-to-keep 39
:months-to-keep 3}))))
(deftest should-parse-check-error
(is (= :not-initialized
(cut/parse-check-error "Fatal: unable to open config file: stat /restic-repo/files/config: no such file or directory\nIs there a repository at the following location?\n/restic-repo/files" )
))
(is (= :wrong-password
(cut/parse-check-error "Fatal: wrong password or no key found\n")))
(is (= :no-password
(cut/parse-check-error "Resolving password failed: Fatal: /restic-pwd does not exist\n")))
)
:months-to-keep 3}))))

View file

@ -7,7 +7,7 @@
(st/instrument `cut/restore-dir-command)
(st/instrument `cut/restore-db-command)
(deftest should-calculate-restore-dir-command
(deftest should-calculate-restore-dir
(is (= [["rm" "-rf" "dir-to-backup"]
["restic"
"-r"
@ -22,22 +22,6 @@
:restore-target-directory "dir-to-backup"
:days-to-keep 39
:months-to-keep 3
:snapshot-id "latest"})))
(is (= [["rm" "-rf" "dir-to-backup/file" "dir-to-backup/folder/"]
["restic"
"-r"
"repo/dir-at-repo"
"-v"
"restore"
"latest"
"--target"
"dir-to-backup"]]
(cut/restore-dir-command {:restic-repository "repo"
:backup-path "dir-at-repo"
:restore-target-directory "dir-to-backup"
:clean-up-elements ["file" "folder/"]
:days-to-keep 39
:months-to-keep 3
:snapshot-id "latest"}))))
(deftest should-calculate-restore-db
@ -55,19 +39,3 @@
:days-to-keep 39
:months-to-keep 3
:snapshot-id "latest"}))))
(deftest should-calculate-restore-db-roles
(is (= [["bash"
"-c"
(str "restic -r repo/dir-at-repo -v dump latest stdin | "
"psql -d template1 -h localhost -p 5432 -U user --no-password")]]
(cut/restore-db-roles-command {:restic-repository "repo"
:backup-path "dir-at-repo"
:pg-host "localhost"
:pg-port 5432
:pg-db "mydb"
:pg-user "user"
:pg-password "password"
:days-to-keep 39
:months-to-keep 3
:snapshot-id "latest"}))))

View file

@ -10,4 +10,15 @@
;; ---------------------------------------------------------
#kaocha/v1 {}
#kaocha/v1 {:plugins [:orchestra
:kaocha.plugin.alpha/info
:profiling
:print-invocations
:hooks
:notifier
:kaocha.plugin/version-filter]
:kaocha/bindings {kaocha.stacktrace/*stacktrace-filters* []
kaocha.stacktrace/*stacktrace-stop-list* []}
:reporter kaocha.report/documentation}