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