Last year |
3 |
1 |
name: Initialise RepoRepository from Full Backup | ||
2 |
|||||
Last year |
3 |
args: | |||
4 |
- | ||||
5 |
name: reporepository | ||||
6 |
label: Repo repository | ||||
7 |
placeholder: Urn or api id | ||||
8 |
python_type: str | ||||
9 |
- | ||||
10 |
name: host | ||||
11 |
label: Host instance | ||||
12 |
placeholder: Urn or api id | ||||
13 |
python_type: str | ||||
14 |
- | ||||
15 |
name: blob | ||||
16 |
label: Backup blob | ||||
17 |
placeholder: Urn or api id | ||||
18 |
python_type: str | ||||
Last year |
3 |
19 |
|||
20 |
role: | ||||
21 |
policies: | ||||
22 |
- urn/api/policy/svnplace/Allow Everything | ||||
23 |
parents: | ||||
24 |
- urn/api/role/{user.username}/ | ||||
25 |
- urn/api/role/svnplace/Backup Restorer | ||||
26 |
|||||
27 |
jobs: | ||||
28 |
restore: | ||||
29 |
runs_on: | ||||
Last year |
30 |
host_instance: "{host}" | |||
31 |
mapped_repo: "{reporepository}" | ||||
Last year |
3 |
32 |
steps: | ||
33 |
- | ||||
34 |
name: Restore | ||||
35 |
python: | | ||||
Last year |
36 |
blob = '{escape(blob)}' | |||
Last year |
3 |
37 |
import os | ||
38 |
import subprocess | ||||
39 |
import sys | ||||
40 |
import xml.etree.ElementTree as ET | ||||
41 |
from api_api import API | ||||
42 |
|||||
43 |
repo = '{repo.urn}' | ||||
44 |
repo_user = repo.rsplit('/',2)[1] | ||||
45 |
repo_name = repo.rsplit('/',2)[2] | ||||
46 |
repo_file = '/var/svnplace/process/repo' | ||||
47 |
|||||
48 |
api = API('https://svnplace.com/api', ['~/.svnplace/config']) | ||||
49 |
|||||
Last year |
50 |
reporepo_desc = api.repos.describe_reporepository('{reporepository}') | |||
Last year |
3 |
51 |
if reporepo_desc.state.name != 'Initialising': | ||
52 |
print(f'Reporepository in wrong state:{{reporepo_desc.state}') | ||||
53 |
sys.exit(1) | ||||
54 |
|||||
55 |
# confirm backup is an actual backup | ||||
Last year |
56 |
backup_desc = api.blobs.describe_blob(blob) | |||
Last year |
3 |
57 |
backup_seal = api.blobs.describe_seal('urn/seal/user/svnplace/Backup - Full') | ||
58 |
if backup_seal.id not in backup_desc.seals: | ||||
59 |
print('Blob not a full backup') | ||||
60 |
sys.exit(1) | ||||
61 |
|||||
62 |
# restore the backup files to the root directory | ||||
63 |
|||||
Last year |
64 |
r0 = subprocess.Popen(['blobcp', f'blob:{{backup_desc.fullpath}', '-'], stdout=subprocess.PIPE) | |||
Last year |
65 |
r1 = subprocess.run(['tar', '-xzf', '-', '-C', repo_file], stdin=r0.stdout) | |||
Last year |
3 |
66 |
|||
67 |
if r1.returncode: | ||||
68 |
print('Backup failed: could not unpack backup') | ||||
69 |
sys.exit(1) | ||||
70 |
|||||
71 |
r0.wait() | ||||
72 |
if r0.returncode: | ||||
73 |
print('Backup failed: reading blob failed') | ||||
74 |
sys.exit(1) | ||||
75 |
|||||
Last year |
76 |
backup_desc2 = api.blobs.describe_blob(blob) | |||
Last year |
3 |
77 |
if backup_desc2.seal_tag != backup_desc.seal_tag: | ||
78 |
print('Backup failed: backup changed during restore') | ||||
79 |
sys.exit(1) | ||||
80 |
|||||
81 |
repo_desc = api.repos.describe_repo(repo) | ||||
Last year |
82 |
if repo_desc.seal in backup_desc2.seals: | |||
Last year |
3 |
83 |
# backup is of this repo... | ||
84 |
# leave the UUID alone | ||||
85 |
# leave the hooks in place | ||||
86 |
pass | ||||
87 |
else: | ||||
88 |
# backup is of a different repo... | ||||
89 |
|||||
90 |
# replace the uuid with a new one | ||||
91 |
r = subprocess.run(['svnadmin', 'setuuid', repo_file]) | ||||
92 |
|||||
93 |
# clear all hooks | ||||
94 |
for item in os.scandir(f'{{repo_file}/hooks/descriptions'): | ||||
95 |
if item.is_file(): | ||||
96 |
os.remove(item.path) | ||||
97 |
|||||
Last year |
98 |
api.repos.modify_reporepository('{reporepository}', state='Ready') | |||
Last year |
3 |
99 |
|||
100 |
print('Restore complete') | ||||
101 |