Last year |
2 |
1 |
name: Backup (full) | ||
2 |
|||||
Last year |
3 |
args: | |||
4 |
- | ||||
5 |
name: backup.store | ||||
6 |
label: Backup location | ||||
7 |
placeholder: Path to blob directory | ||||
8 |
- | ||||
9 |
name: backup.pattern | ||||
10 |
label: Backup names | ||||
11 |
placeholder: The bit between the directory and _<ver>_full | ||||
Last year |
2 |
12 |
|||
13 |
role: | ||||
14 |
policies: | ||||
15 |
- urn/api/policy/svnplace/Allow Everything | ||||
16 |
parents: | ||||
17 |
- urn/api/role/{user.username}/ | ||||
18 |
- urn/api/role/svnplace/Backup Creater | ||||
19 |
|||||
20 |
jobs: | ||||
21 |
|||||
22 |
do_backup: | ||||
23 |
runs_on: | ||||
24 |
host_instance: "{repo.primary.rootdirectory.instance.api_id}" | ||||
25 |
mapped_repo: "{repo.primary.api_id}" | ||||
26 |
steps: | ||||
27 |
- | ||||
28 |
name: Backup | ||||
29 |
python: | | ||||
30 |
import os | ||||
31 |
import subprocess | ||||
32 |
import sys | ||||
33 |
import xml.etree.ElementTree as ET | ||||
34 |
from api_api import API | ||||
35 |
|||||
36 |
repo = '{repo.urn}' | ||||
37 |
repo_user = repo.rsplit('/',2)[1] | ||||
38 |
repo_name = repo.rsplit('/',2)[2] | ||||
39 |
repo_file = '/var/svnplace/process/repo' | ||||
40 |
backup_file = os.path.abspath('./backup') | ||||
41 |
|||||
42 |
def get_repo_version(repo): | ||||
43 |
r = subprocess.run(['svn','info','--xml', 'FILE://'+repo], capture_output=True) | ||||
44 |
if r.returncode != 0: | ||||
45 |
print(r.stdout) | ||||
46 |
print(r.stderr) | ||||
47 |
return None | ||||
48 |
|||||
49 |
tree = ET.fromstring(r.stdout) | ||||
50 |
return int(tree.find('entry').attrib['revision']) | ||||
51 |
|||||
52 |
def backup_blob(ver): | ||||
Last year |
53 |
return '{backup.store}/{backup.pattern}_%(ver)d_full' % {{ | |||
Last year |
2 |
54 |
'backups': '{backup.store}', | ||
55 |
'username': repo_user, | ||||
56 |
'reponame': repo_name, | ||||
57 |
'ver': ver, | ||||
58 |
} | ||||
59 |
|||||
60 |
api = API('https://svnplace.com/api', ['~/.svnplace/config']) | ||||
61 |
|||||
62 |
repo_desc = api.repos.describe_repo(repo) | ||||
63 |
repo_ver = get_repo_version(repo_file) | ||||
64 |
|||||
65 |
try: | ||||
66 |
r = api.blobs.describe_blob('urn/blob/'+backup_blob(repo_ver)) | ||||
67 |
except FileNotFoundError: | ||||
68 |
pass | ||||
69 |
else: | ||||
70 |
print(f'{{repo_user}/{{repo_name}@{{repo_ver} is already backed up - nothing to do') | ||||
71 |
sys.exit(0) | ||||
72 |
|||||
73 |
print('Creating backup copy') | ||||
74 |
|||||
75 |
r = subprocess.run(['svnadmin','hotcopy', repo_file, backup_file]) | ||||
76 |
if r.returncode != 0: | ||||
77 |
print('Failed to complete backup') | ||||
78 |
sys.exit(1) | ||||
79 |
|||||
80 |
backup_ver = get_repo_version(backup_file) | ||||
81 |
if backup_ver is None: | ||||
82 |
print('Failed to read backup version') | ||||
83 |
sys.exit(1) | ||||
84 |
|||||
85 |
print(f'Archiving backup {{repo_user}/{{repo_name}@{{backup_ver}') | ||||
86 |
|||||
Last year |
87 |
r0 = subprocess.Popen(('tar', '--strip-components', '1', '-C', backup_file, '-cz', '.'), stdout=subprocess.PIPE) | |||
Last year |
2 |
88 |
r1 = subprocess.run(['blobcp', '-s', repo_desc.seal.api_id, '-s', 'urn/seal/user/svnplace/Backup - Full', '-', 'blob:'+backup_blob(backup_ver)], stdin=r0.stdout) | ||
89 |
|||||
90 |
if r1.returncode: | ||||
91 |
print('Backup failed') | ||||
92 |
sys.exit(1) | ||||
93 |
|||||
94 |
print('Backup complete') | ||||
95 |