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