diff --git a/states/kopia/config.sls b/states/kopia/config.sls index c2fe004..6c77c0b 100644 --- a/states/kopia/config.sls +++ b/states/kopia/config.sls @@ -6,16 +6,55 @@ kopia-config-dir: - user: root - group: root - mode: 700 - - watch_in: - - service: kopia-service -kopia-config-file: +kopia-config-sudo: file.managed: - - name: /etc/kopia/kopia.conf - - source: salt://kopia/templates/kopia.conf.j2 + - name: /etc/sudoers.d/kopia + - source: salt://kopia/templates/kopia_sudo.j2 + - template: jinja - user: root - group: root - mode: 600 + - check_cmd: /usr/sbin/visudo -c -f + +{% for name, params in kopia.jobs.items() %} +kopia-config-{{ name }}: + file.directory: + - name: /etc/kopia/{{ name }} + - user: root + - group: root + - mode: 700 + - require: + - file: kopia-config-dir + +kopia-config-{{ name }}-run: + file.managed: + - name: /etc/kopia/{{ name }}/backup.py + - source: salt://kopia/templates/kopia_backup.py.j2 + - user: root + - group: root + - mode: 700 - template: jinja - - watch_in: - - service: kopia-service + - context: + name: {{ name }} + params: {{ params }} + - require: + - file: kopia-config-dir + - file: kopia-config-{{ name }} + +{% for dir in params.dirs %} +{% if "ignore" in dir.keys() %} +kopia-config-{{ name }}-{{ dir.path }}-ignore: + file.managed: + - name: {{ dir.path }}/.kopiaignore + - source: salt://kopia/templates/kopiaignore.j2 + - user: root + - group: root + - mode: 700 + - template: jinja + - context: + ignore: {{ dir.ignore }} +{% endif %} +{% endfor %} + +{% endfor %} diff --git a/states/kopia/defaults.yaml b/states/kopia/defaults.yaml index 3a20f77..fd4bc24 100644 --- a/states/kopia/defaults.yaml +++ b/states/kopia/defaults.yaml @@ -4,6 +4,10 @@ kopia: release_dir: /usr/local/apps/releases install_dir: /usr/local/apps mirror: https://github.com/kopia/kopia/releases/download + run_user: dkron + config_dir: /etc/kopia version: 0.15.0 os: linux arch: x64 + jobs: {} + repos: {} diff --git a/states/kopia/install.sls b/states/kopia/install.sls index 7024286..89a1514 100644 --- a/states/kopia/install.sls +++ b/states/kopia/install.sls @@ -20,6 +20,7 @@ kopia-bin-perm: - name: {{ kopia.release_dir }}/kopia-{{ kopia.version }}/kopia - user: root - group: root + - replace: False - require: - archive: kopia-archive-extract diff --git a/states/kopia/templates/kopia_backup.py.j2 b/states/kopia/templates/kopia_backup.py.j2 new file mode 100644 index 0000000..e40824d --- /dev/null +++ b/states/kopia/templates/kopia_backup.py.j2 @@ -0,0 +1,56 @@ +{%- from "kopia/map.jinja" import kopia with context -%} +#!/usr/bin/env python3 +# vim: ft=python + +import argparse +import subprocess + +KOPIA_BUCKET="kopia" +KOPIA_PASSWORD="{{ params.password|default('') }}" +KOPIA_CONFIG_PATH="/etc/kopia/{{ name }}/{{ name }}.config" +KOPIA_KEEP_LAST=7 + +parser = argparse.ArgumentParser(prog='{{ name }} backup') + +def init(mode="filesystem", path=None, prefix=None, gateway=None, region=None, ak=None, sak=None): + if mode == "filesystem" and path: + repo_connect = subprocess.Popen(f"kopia repository connect filesystem --config-file={KOPIA_CONFIG_PATH} --path={path} -p {KOPIA_PASSWORD}", shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + repo_create = subprocess.Popen(f"kopia repository create filesystem --config-file={KOPIA_CONFIG_PATH} --path={path} -p {KOPIA_PASSWORD}" , shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + elif mode == "s3" and prefix and gateway and region and ak and sak: + cmd_create = f"kopia repository create s3 --config-file={KOPIA_CONFIG_PATH} --bucket={KOPIA_BUCKET} --prefix={prefix}/ --endpoint={gateway} --region={region} --access-key={ak} --secret-access-key={sak} -p {KOPIA_PASSWORD} --no-check-for-updates --description='{{ name }} repository'" + cmd_connect = f"kopia repository connect s3 --config-file={KOPIA_CONFIG_PATH} --bucket={KOPIA_BUCKET} --prefix={prefix}/ --endpoint={gateway} --region={region} --access-key={ak} --secret-access-key={sak} -p {KOPIA_PASSWORD} --no-check-for-updates --description='{{ name }} repository'" + run_create = subprocess.Popen(cmd_create, shell=True) + run_create.wait() + run_connect = subprocess.Popen(cmd_connect, shell=True) + run_connect.wait() + else: + print("no valid mode or missing informations") + return None + return True + + +def set_policy(compression="zstd"): + cmd_policy = f"kopia policy set --global --config-file={KOPIA_CONFIG_PATH} --compression={compression} --keep-latest={KOPIA_KEEP_LAST} --keep-hourly 0 --keep-daily {{ params.keep_daily|default(7) }} --keep-weekly {{ params.keep_weekly|default(4) }} --keep-monthly {{ params.keep_monthly|default(6) }} --keep-annual 0 --one-file-system=true" + run_policy = subprocess.Popen(cmd_policy, shell=True) + run_policy.wait() + return + + +def run(): + cmd_launch = f"kopia snapshot create {% for dir in params.dirs %}{{ dir.path }} {% endfor %} --config-file={KOPIA_CONFIG_PATH}" + run_launch = subprocess.Popen(cmd_launch, shell=True) + run_launch.wait() + return + + +if __name__ == "__main__": + parser.add_argument('action', nargs="?") + args = parser.parse_args() + + res_init = init(mode="s3", prefix="{{ name }}", gateway="{{ kopia.repos[params.repo].gateway|default('gateway.storjshare.io') }}", region="{{ kopia.repos[params.repo].region|default('EU1') }}", ak="{{ kopia.repos[params.repo].ak }}", sak="{{ kopia.repos[params.repo].sak }}") + if not res_init: + sys.exit(1) + set_policy() + + if args.action == "run": + run() diff --git a/states/kopia/templates/kopia_sudo.j2 b/states/kopia/templates/kopia_sudo.j2 new file mode 100644 index 0000000..5c31c0b --- /dev/null +++ b/states/kopia/templates/kopia_sudo.j2 @@ -0,0 +1,4 @@ +{%- from "kopia/map.jinja" import kopia with context %} +## Sudo rights for kopia run_user +{{ kopia.run_user }} ALL=(ALL) NOPASSWD: {{ kopia.config_dir }}/*/backup.py + diff --git a/states/kopia/templates/kopiaignore.j2 b/states/kopia/templates/kopiaignore.j2 new file mode 100644 index 0000000..788d94c --- /dev/null +++ b/states/kopia/templates/kopiaignore.j2 @@ -0,0 +1,4 @@ +{%- from "kopia/map.jinja" import kopia with context -%} +{% for ign in ignore -%} +{{ ign }} +{%- endfor %}