Skip to content

Commit ee17ee8

Browse files
committed
add pyopenssl to gsutil virtualenv, improvements to export instance script
1 parent 4c3f704 commit ee17ee8

2 files changed

Lines changed: 102 additions & 3 deletions

File tree

docker-build.sh

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@ hostname | tee -a /etc/ckan-cloud-operator-build-info
55

66
(
77
echo == system dependencies, gcloud-sdk &&\
8-
apt-get update && apt-get install -y gnupg bash-completion build-essential jq &&\
8+
apt-get update && apt-get install -y gnupg bash-completion build-essential jq python-pip &&\
9+
/usr/bin/pip2 install pyopenssl &&\
910
echo "deb http://packages.cloud.google.com/apt cloud-sdk-stretch main" >> /etc/apt/sources.list.d/google-cloud-sdk.list && \
1011
curl https://packages.cloud.google.com/apt/doc/apt-key.gpg | apt-key add - && \
1112
apt-get update -y && apt-get install -y google-cloud-sdk kubectl postgresql nano dnsutils &&\

scripts/export instance.py

Lines changed: 100 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,15 @@
33
import shutil
44
import tarfile
55
import subprocess
6+
import datetime
7+
import tempfile
8+
import yaml
69
from glob import glob
710
from ckan_cloud_operator import kubectl
811
from ckan_cloud_operator.gitlab import CkanGitlab
12+
from ckan_cloud_operator.config import manager as config_manager
13+
from ckan_cloud_operator.drivers.gcloud import driver as gcloud_driver
14+
from ckan_cloud_operator.providers.cluster import manager as cluster_manager
915

1016

1117
INSTANCE_NAME = os.environ.get('INSTANCE_NAME')
@@ -64,21 +70,113 @@ def export_storage(instance_id):
6470
shell=True)
6571

6672

73+
def export_db(instance_id):
74+
instance_spec = kubectl.get(f'ckancloudckaninstance {instance_id}')['spec']
75+
db_name = instance_spec['db']['name']
76+
db_prefix = instance_spec['db'].get('dbPrefix')
77+
datastore_name = instance_spec['datastore']['name']
78+
datastore_prefix = instance_spec['datastore'].get('dbPrefix')
79+
gs_base_url = config_manager.get(secret_name='ckan-cloud-provider-db-gcloudsql-credentials',
80+
key='backups-gs-base-url')
81+
db_prefix_path = f'{db_prefix}/' if db_prefix else ''
82+
datastore_prefix_path = f'{datastore_prefix}/' if datastore_prefix else ''
83+
latest_gs_urls = {
84+
'db': None,
85+
'datastore': None
86+
}
87+
latest_gs_urls_datetimes = {
88+
'db': None,
89+
'datastore': None
90+
}
91+
for dbtype in ['db', 'datastore']:
92+
for minus_days in (0, 1, 2):
93+
dt = (datetime.datetime.now() - datetime.timedelta(days=minus_days))
94+
datepath = dt.strftime('%Y/%m/%d')
95+
datesuffix = dt.strftime('%Y%m%d')
96+
if dbtype == 'datastore':
97+
ls_arg = f'{gs_base_url}{datastore_prefix_path}/{datepath}/*/{datastore_name}_{datesuffix}*.gz'
98+
else:
99+
ls_arg = f'{gs_base_url}{db_prefix_path}/{datepath}/*/{db_name}_{datesuffix}*.gz'
100+
output = gcloud_driver.check_output(
101+
*cluster_manager.get_provider().get_project_zone(), f'ls -l "{ls_arg}"',
102+
gsutil=True
103+
)
104+
for line in output.decode().splitlines():
105+
gsurl = line.strip().split(' ')[-1].strip()
106+
if gsurl.startswith('gs://'):
107+
gs_url_datetime = datetime.datetime.strptime(gsurl.split('/')[-1].split('.')[-2].split('_')[-1], '%Y%m%d%H%M')
108+
if not latest_gs_urls[dbtype] or latest_gs_urls_datetimes[dbtype] < gs_url_datetime:
109+
latest_gs_urls[dbtype], latest_gs_urls_datetimes[dbtype] = gsurl, gs_url_datetime
110+
if latest_gs_urls[dbtype]:
111+
break
112+
return latest_gs_urls['db'], latest_gs_urls['datastore']
113+
# for dbtype in ['db', 'datastore']:
114+
# gsurl, gsurl_datetime = latest_gs_urls[dbtype], latest_gs_urls_datetimes[dbtype]
115+
# filename = '{}-{}'.format(dbtype, gsurl.split('/')[-1])
116+
# print(f'Downloading {gsurl} ({gsurl_datetime}) --> {filename}')
117+
# gcloud_driver.check_call(
118+
# *cluster_manager.get_provider().get_project_zone(),
119+
# f'cp {gsurl} ./{filename}',
120+
# gsutil=True
121+
# )
122+
123+
124+
def gsutil_publish(filename, gsurl, duration='7d'):
125+
if gsurl:
126+
if filename:
127+
subprocess.check_call(f'ls -lah {filename}', shell=True)
128+
gcloud_driver.check_call(
129+
*cluster_manager.get_provider().get_project_zone(),
130+
f'cp ./{filename} {gsurl}',
131+
gsutil=True
132+
)
133+
with tempfile.NamedTemporaryFile('w') as f:
134+
f.write(config_manager.get(key='service-account-json', secret_name='ckan-cloud-provider-cluster-gcloud'))
135+
f.flush()
136+
output = gcloud_driver.check_output(
137+
*cluster_manager.get_provider().get_project_zone(),
138+
f'signurl -d {duration} {f.name} {gsurl}',
139+
gsutil=True
140+
)
141+
signed_gsurls = [line for line in [line.strip().split('\t')[-1].strip() for line in output.decode().splitlines()] if len(line) > 20]
142+
assert len(signed_gsurls) == 1
143+
return signed_gsurls[0]
144+
else:
145+
return None
146+
147+
67148
def export_instance(instance_name, allowed_instance_names):
68149
instance_id = get_instance_id(instance_name, allowed_instance_names)
150+
gs_base_url = config_manager.get(secret_name='ckan-cloud-provider-db-gcloudsql-credentials',
151+
key='backups-gs-base-url')
152+
backup_prefix = 'export/{}/{}'.format(instance_name, datetime.datetime.now().strftime('%Y%m%d%H%M'))
69153
export_code(instance_id)
70-
export_storage(instance_id)
71154
with tarfile.open('code.tar.gz', 'w:gz') as tar:
72155
for filename in glob('code_with_internal_secrets/**/*'):
73156
fileparts = filename.split('/')
74157
assert fileparts.pop(0) == 'code_with_internal_secrets'
75158
tar.add(filename, '/'.join(fileparts))
159+
code_signed_gsurl = gsutil_publish('code.tar.gz', f'{gs_base_url}/{backup_prefix}/code.tar.gz')
160+
code_with_secrets_signed_gsurl = gsutil_publish('code_with_internal_secrets.tar.gz', f'{gs_base_url}/{backup_prefix}/code_with_internal_secrets.tar.gz')
161+
export_storage(instance_id)
76162
with tarfile.open('storage.tar.gz', 'w:gz') as tar:
77163
for filename in glob('storage/**/*'):
78164
fileparts = filename.split('/')
79165
assert fileparts.pop(0) == 'storage'
80166
tar.add(filename, '/'.join(fileparts))
167+
storage_signed_gsurl = gsutil_publish('storage.tar.gz', f'{gs_base_url}/{backup_prefix}/storage.tar.gz')
168+
db_gs_url, datastore_gs_url = export_db(instance_id)
169+
db_signed_gsurl = gsutil_publish(None, db_gs_url)
170+
datastore_signed_gsurl = gsutil_publish(None, datastore_gs_url)
171+
return {
172+
'code': code_signed_gsurl,
173+
'code_with_secrets': code_with_secrets_signed_gsurl,
174+
'storage': storage_signed_gsurl,
175+
'db': db_signed_gsurl,
176+
'datastore': datastore_signed_gsurl
177+
}
81178

82179

83180
if __name__ == '__main__':
84-
export_instance(INSTANCE_NAME, ALLOWED_INSTANCE_NAMES)
181+
print(yaml.dump(export_instance(INSTANCE_NAME, ALLOWED_INSTANCE_NAMES), default_flow_style=False))
182+
exit(0)

0 commit comments

Comments
 (0)