snapctl 0.39.3__py3-none-any.whl → 0.41.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of snapctl might be problematic. Click here for more details.
- snapctl/commands/byogs.py +135 -56
- snapctl/commands/byosnap.py +450 -225
- snapctl/commands/game.py +49 -25
- snapctl/commands/generate.py +48 -40
- snapctl/commands/snapend.py +225 -142
- snapctl/config/constants.py +16 -4
- snapctl/config/hashes.py +5 -3
- snapctl/main.py +106 -34
- snapctl/utils/helper.py +2 -4
- {snapctl-0.39.3.dist-info → snapctl-0.41.0.dist-info}/METADATA +66 -15
- snapctl-0.41.0.dist-info/RECORD +23 -0
- snapctl-0.39.3.dist-info/RECORD +0 -23
- {snapctl-0.39.3.dist-info → snapctl-0.41.0.dist-info}/LICENSE +0 -0
- {snapctl-0.39.3.dist-info → snapctl-0.41.0.dist-info}/WHEEL +0 -0
- {snapctl-0.39.3.dist-info → snapctl-0.41.0.dist-info}/entry_points.txt +0 -0
snapctl/commands/byosnap.py
CHANGED
|
@@ -6,6 +6,7 @@ from binascii import Error as BinasciiError
|
|
|
6
6
|
import json
|
|
7
7
|
import os
|
|
8
8
|
import re
|
|
9
|
+
import time
|
|
9
10
|
import subprocess
|
|
10
11
|
import platform as sys_platform
|
|
11
12
|
from sys import platform
|
|
@@ -14,6 +15,7 @@ import requests
|
|
|
14
15
|
from requests.exceptions import RequestException
|
|
15
16
|
|
|
16
17
|
from rich.progress import Progress, SpinnerColumn, TextColumn
|
|
18
|
+
from snapctl.commands.snapend import Snapend
|
|
17
19
|
from snapctl.config.constants import SERVER_CALL_TIMEOUT
|
|
18
20
|
from snapctl.config.constants import HTTP_ERROR_SERVICE_VERSION_EXISTS, \
|
|
19
21
|
HTTP_ERROR_TAG_NOT_AVAILABLE, HTTP_ERROR_ADD_ON_NOT_ENABLED, SNAPCTL_INPUT_ERROR, \
|
|
@@ -22,8 +24,11 @@ from snapctl.config.constants import HTTP_ERROR_SERVICE_VERSION_EXISTS, \
|
|
|
22
24
|
SNAPCTL_BYOSNAP_PUBLISH_IMAGE_DUPLICATE_TAG_ERROR, \
|
|
23
25
|
SNAPCTL_BYOSNAP_CREATE_DUPLICATE_NAME_ERROR, SNAPCTL_BYOSNAP_CREATE_PERMISSION_ERROR, \
|
|
24
26
|
SNAPCTL_BYOSNAP_CREATE_ERROR, SNAPCTL_BYOSNAP_PUBLISH_VERSION_DUPLICATE_TAG_ERROR, \
|
|
25
|
-
SNAPCTL_BYOSNAP_PUBLISH_VERSION_ERROR
|
|
26
|
-
|
|
27
|
+
SNAPCTL_BYOSNAP_PUBLISH_VERSION_ERROR, HTTP_ERROR_SERVICE_IN_USE, \
|
|
28
|
+
SNAPCTL_BYOSNAP_UPDATE_VERSION_ERROR, SNAPCTL_BYOSNAP_UPDATE_VERSION_SERVICE_IN_USE_ERROR, \
|
|
29
|
+
SNAPCTL_BYOSNAP_UPDATE_VERSION_TAG_ERROR, SNAPCTL_BYOSNAP_NOT_FOUND, \
|
|
30
|
+
HTTP_ERROR_RESOURCE_NOT_FOUND
|
|
31
|
+
from snapctl.utils.echo import info, warning
|
|
27
32
|
from snapctl.utils.helper import get_composite_token, snapctl_error, snapctl_success, \
|
|
28
33
|
check_dockerfile_architecture
|
|
29
34
|
|
|
@@ -34,7 +39,8 @@ class ByoSnap:
|
|
|
34
39
|
"""
|
|
35
40
|
ID_PREFIX = 'byosnap-'
|
|
36
41
|
SUBCOMMANDS = [
|
|
37
|
-
'create', 'publish-image', 'publish-version', 'upload-docs',
|
|
42
|
+
'create', 'publish-image', 'publish-version', 'upload-docs', 'update-version',
|
|
43
|
+
'sync'
|
|
38
44
|
]
|
|
39
45
|
PLATFORMS = ['linux/arm64', 'linux/amd64']
|
|
40
46
|
LANGUAGES = ['go', 'python', 'ruby', 'c#', 'c++', 'rust', 'java', 'node']
|
|
@@ -47,16 +53,20 @@ class ByoSnap:
|
|
|
47
53
|
MAX_MIN_REPLICAS = 4
|
|
48
54
|
|
|
49
55
|
def __init__(
|
|
50
|
-
self, subcommand: str, base_url: str, api_key: Union[str, None], sid: str,
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
+
self, *, subcommand: str, base_url: str, api_key: Union[str, None], sid: str,
|
|
57
|
+
name: Union[str, None] = None, desc: Union[str, None] = None,
|
|
58
|
+
platform_type: Union[str, None] = None, language: Union[str, None] = None,
|
|
59
|
+
tag: Union[str, None] = None, path: Union[str, None] = None,
|
|
60
|
+
resources_path: Union[str, None] = None, dockerfile: Union[str, None] = None,
|
|
61
|
+
prefix: Union[str, None] = None, version: Union[str, None] = None,
|
|
62
|
+
http_port: Union[int, None] = None,
|
|
63
|
+
byosnap_profile: Union[str, None] = None, skip_build: bool = False,
|
|
64
|
+
readiness_path: Union[str, None] = None, readiness_delay: Union[int, None] = None,
|
|
65
|
+
snapend_id: Union[str, None] = None, blocking: bool = False
|
|
56
66
|
) -> None:
|
|
57
67
|
self.subcommand: str = subcommand
|
|
58
68
|
self.base_url: str = base_url
|
|
59
|
-
self.api_key: str = api_key
|
|
69
|
+
self.api_key: Union[str, None] = api_key
|
|
60
70
|
self.sid: str = sid
|
|
61
71
|
self.name: str = name
|
|
62
72
|
self.desc: str = desc
|
|
@@ -71,7 +81,7 @@ class ByoSnap:
|
|
|
71
81
|
self.token: Union[str, None] = None
|
|
72
82
|
self.token_parts: Union[list, None] = ByoSnap._get_token_values(
|
|
73
83
|
self.token) if self.token is not None else None
|
|
74
|
-
self.
|
|
84
|
+
self.tag: Union[str, None] = tag
|
|
75
85
|
self.path: Union[str, None] = path
|
|
76
86
|
self.resources_path: Union[str, None] = resources_path
|
|
77
87
|
self.dockerfile: str = dockerfile
|
|
@@ -82,6 +92,8 @@ class ByoSnap:
|
|
|
82
92
|
self.skip_build: bool = skip_build
|
|
83
93
|
self.readiness_path: Union[str, None] = readiness_path
|
|
84
94
|
self.readiness_delay: Union[int, None] = readiness_delay
|
|
95
|
+
self.snapend_id: Union[str, None] = snapend_id
|
|
96
|
+
self.blocking: bool = blocking
|
|
85
97
|
# Validate the input
|
|
86
98
|
self.validate_input()
|
|
87
99
|
|
|
@@ -126,13 +138,18 @@ class ByoSnap:
|
|
|
126
138
|
"docker", "info"
|
|
127
139
|
], stdout=subprocess.DEVNULL, stderr=subprocess.STDOUT, check=False)
|
|
128
140
|
if not result.returncode:
|
|
129
|
-
return snapctl_success(
|
|
130
|
-
|
|
141
|
+
return snapctl_success(
|
|
142
|
+
message='BYOSnap dependencies verified',
|
|
143
|
+
progress=progress, no_exit=True)
|
|
131
144
|
except subprocess.CalledProcessError:
|
|
132
|
-
snapctl_error(
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
145
|
+
snapctl_error(
|
|
146
|
+
message='Snapctl Exception',
|
|
147
|
+
code=SNAPCTL_BYOSNAP_DEPENDENCY_MISSING, progress=progress)
|
|
148
|
+
finally:
|
|
149
|
+
progress.stop()
|
|
150
|
+
snapctl_error(
|
|
151
|
+
message='Docker not running. Please start docker.',
|
|
152
|
+
code=SNAPCTL_BYOSNAP_DEPENDENCY_MISSING, progress=progress)
|
|
136
153
|
|
|
137
154
|
def _docker_login(self) -> None:
|
|
138
155
|
"""
|
|
@@ -162,17 +179,22 @@ class ByoSnap:
|
|
|
162
179
|
f'--username {ecr_repo_username} --password-stdin {ecr_repo_url}'
|
|
163
180
|
], shell=True, stdout=subprocess.DEVNULL, stderr=subprocess.STDOUT, check=False)
|
|
164
181
|
if not response.returncode:
|
|
165
|
-
return snapctl_success(
|
|
166
|
-
|
|
182
|
+
return snapctl_success(
|
|
183
|
+
message='BYOSnap ECR login successful',
|
|
184
|
+
progress=progress, no_exit=True)
|
|
167
185
|
except subprocess.CalledProcessError:
|
|
168
|
-
snapctl_error(
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
186
|
+
snapctl_error(
|
|
187
|
+
message='Snapctl Exception',
|
|
188
|
+
code=SNAPCTL_BYOSNAP_ECR_LOGIN_ERROR, progress=progress)
|
|
189
|
+
finally:
|
|
190
|
+
progress.stop()
|
|
191
|
+
snapctl_error(
|
|
192
|
+
message='BYOSnap ECR login failure',
|
|
193
|
+
code=SNAPCTL_BYOSNAP_ECR_LOGIN_ERROR, progress=progress)
|
|
172
194
|
|
|
173
195
|
def _docker_build(self) -> None:
|
|
174
196
|
# Get the data
|
|
175
|
-
# image_tag = f'{self.sid}.{self.
|
|
197
|
+
# image_tag = f'{self.sid}.{self.tag}'
|
|
176
198
|
build_platform = ByoSnap.DEFAULT_BUILD_PLATFORM
|
|
177
199
|
if len(self.token_parts) == 4:
|
|
178
200
|
build_platform = self.token_parts[3]
|
|
@@ -202,7 +224,7 @@ class ByoSnap:
|
|
|
202
224
|
if platform == "win32":
|
|
203
225
|
response = subprocess.run([
|
|
204
226
|
# f"docker build --no-cache -t {tag} {path}"
|
|
205
|
-
'docker', 'build', '--platform', build_platform, '-t', self.
|
|
227
|
+
'docker', 'build', '--platform', build_platform, '-t', self.tag,
|
|
206
228
|
'-f', docker_file_path, self.path
|
|
207
229
|
], shell=True, check=False)
|
|
208
230
|
# stdout=subprocess.DEVNULL, stderr=subprocess.STDOUT)
|
|
@@ -210,23 +232,27 @@ class ByoSnap:
|
|
|
210
232
|
response = subprocess.run([
|
|
211
233
|
# f"docker build --no-cache -t {tag} {path}"
|
|
212
234
|
"docker build --platform " +
|
|
213
|
-
f"{build_platform} -t {self.
|
|
235
|
+
f"{build_platform} -t {self.tag} " +
|
|
214
236
|
f"-f {docker_file_path} {self.path}"
|
|
215
237
|
], shell=True, check=False)
|
|
216
238
|
# stdout=subprocess.DEVNULL, stderr=subprocess.STDOUT)
|
|
217
239
|
if not response.returncode:
|
|
218
|
-
return snapctl_success(
|
|
219
|
-
|
|
240
|
+
return snapctl_success(
|
|
241
|
+
message='BYOSnap build successful', progress=progress, no_exit=True)
|
|
220
242
|
except subprocess.CalledProcessError:
|
|
221
|
-
snapctl_error(
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
243
|
+
snapctl_error(
|
|
244
|
+
message='Snapctl Exception',
|
|
245
|
+
code=SNAPCTL_BYOSNAP_BUILD_ERROR, progress=progress)
|
|
246
|
+
finally:
|
|
247
|
+
progress.stop()
|
|
248
|
+
snapctl_error(
|
|
249
|
+
message='BYOSnap build failure',
|
|
250
|
+
code=SNAPCTL_BYOSNAP_BUILD_ERROR, progress=progress)
|
|
225
251
|
|
|
226
252
|
def _docker_tag(self) -> None:
|
|
227
253
|
# Get the data
|
|
228
254
|
ecr_repo_url = self.token_parts[0]
|
|
229
|
-
image_tag = f'{self.sid}.{self.
|
|
255
|
+
image_tag = f'{self.sid}.{self.tag}'
|
|
230
256
|
full_ecr_repo_url = f'{ecr_repo_url}:{image_tag}'
|
|
231
257
|
progress = Progress(
|
|
232
258
|
SpinnerColumn(),
|
|
@@ -240,20 +266,24 @@ class ByoSnap:
|
|
|
240
266
|
# Tag the repo
|
|
241
267
|
if platform == "win32":
|
|
242
268
|
response = subprocess.run([
|
|
243
|
-
'docker', 'tag', self.
|
|
269
|
+
'docker', 'tag', self.tag, full_ecr_repo_url
|
|
244
270
|
], shell=True, stdout=subprocess.DEVNULL, stderr=subprocess.STDOUT, check=False)
|
|
245
271
|
else:
|
|
246
272
|
response = subprocess.run([
|
|
247
|
-
f"docker tag {self.
|
|
273
|
+
f"docker tag {self.tag} {full_ecr_repo_url}"
|
|
248
274
|
], shell=True, stdout=subprocess.DEVNULL, stderr=subprocess.STDOUT, check=False)
|
|
249
275
|
if not response.returncode:
|
|
250
|
-
return snapctl_success(
|
|
251
|
-
|
|
276
|
+
return snapctl_success(
|
|
277
|
+
message='BYOSnap tag successful', progress=progress, no_exit=True)
|
|
252
278
|
except subprocess.CalledProcessError:
|
|
253
|
-
snapctl_error(
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
279
|
+
snapctl_error(
|
|
280
|
+
message='Snapctl Exception',
|
|
281
|
+
code=SNAPCTL_BYOSNAP_TAG_ERROR, progress=progress)
|
|
282
|
+
finally:
|
|
283
|
+
progress.stop()
|
|
284
|
+
snapctl_error(
|
|
285
|
+
message='BYOSnap tag failure',
|
|
286
|
+
code=SNAPCTL_BYOSNAP_TAG_ERROR, progress=progress)
|
|
257
287
|
|
|
258
288
|
def _docker_push(self) -> bool:
|
|
259
289
|
"""
|
|
@@ -269,7 +299,7 @@ class ByoSnap:
|
|
|
269
299
|
try:
|
|
270
300
|
# Push the image
|
|
271
301
|
ecr_repo_url = self.token_parts[0]
|
|
272
|
-
image_tag = f'{self.sid}.{self.
|
|
302
|
+
image_tag = f'{self.sid}.{self.tag}'
|
|
273
303
|
full_ecr_repo_url = f'{ecr_repo_url}:{image_tag}'
|
|
274
304
|
if platform == "win32":
|
|
275
305
|
response = subprocess.run([
|
|
@@ -282,25 +312,30 @@ class ByoSnap:
|
|
|
282
312
|
], shell=True, check=False)
|
|
283
313
|
# stdout=subprocess.DEVNULL, stderr=subprocess.STDOUT)
|
|
284
314
|
if not response.returncode:
|
|
285
|
-
return snapctl_success(
|
|
286
|
-
|
|
315
|
+
return snapctl_success(
|
|
316
|
+
message='BYOSnap upload successful', progress=progress, no_exit=True)
|
|
287
317
|
except subprocess.CalledProcessError:
|
|
288
|
-
snapctl_error(
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
318
|
+
snapctl_error(
|
|
319
|
+
message='Snapctl Exception',
|
|
320
|
+
code=SNAPCTL_BYOSNAP_PUBLISH_IMAGE_ERROR, progress=progress)
|
|
321
|
+
finally:
|
|
322
|
+
progress.stop()
|
|
323
|
+
snapctl_error(
|
|
324
|
+
message='BYOSnap upload failure. Duplicate image error.',
|
|
325
|
+
code=SNAPCTL_BYOSNAP_PUBLISH_IMAGE_DUPLICATE_TAG_ERROR, progress=progress)
|
|
292
326
|
|
|
293
327
|
def _validate_byosnap_profile(self) -> None:
|
|
294
328
|
"""
|
|
295
329
|
Validate the BYOSnap profile
|
|
296
330
|
"""
|
|
297
331
|
if not self.byosnap_profile:
|
|
298
|
-
snapctl_error(
|
|
332
|
+
snapctl_error(
|
|
333
|
+
message="Missing BYOSnap profile path", code=SNAPCTL_INPUT_ERROR)
|
|
299
334
|
if not os.path.isfile(self.byosnap_profile):
|
|
300
335
|
snapctl_error(
|
|
301
|
-
"Unable to find BYOSnap profile " +
|
|
336
|
+
message="Unable to find BYOSnap profile " +
|
|
302
337
|
f"JSON at path {self.byosnap_profile}",
|
|
303
|
-
SNAPCTL_INPUT_ERROR
|
|
338
|
+
code=SNAPCTL_INPUT_ERROR
|
|
304
339
|
)
|
|
305
340
|
profile_data = None
|
|
306
341
|
with open(self.byosnap_profile, 'rb') as file:
|
|
@@ -310,15 +345,15 @@ class ByoSnap:
|
|
|
310
345
|
pass
|
|
311
346
|
if not profile_data:
|
|
312
347
|
snapctl_error(
|
|
313
|
-
'Invalid BYOSnap profile JSON. Please check the JSON structure',
|
|
314
|
-
SNAPCTL_INPUT_ERROR
|
|
348
|
+
message='Invalid BYOSnap profile JSON. Please check the JSON structure',
|
|
349
|
+
code=SNAPCTL_INPUT_ERROR
|
|
315
350
|
)
|
|
316
351
|
if 'dev_template' not in profile_data or \
|
|
317
352
|
'stage_template' not in profile_data or \
|
|
318
353
|
'prod_template' not in profile_data:
|
|
319
354
|
snapctl_error(
|
|
320
|
-
'Invalid BYOSnap profile JSON. Please check the JSON structure',
|
|
321
|
-
SNAPCTL_INPUT_ERROR
|
|
355
|
+
message='Invalid BYOSnap profile JSON. Please check the JSON structure',
|
|
356
|
+
code=SNAPCTL_INPUT_ERROR
|
|
322
357
|
)
|
|
323
358
|
for profile in ['dev_template', 'stage_template', 'prod_template']:
|
|
324
359
|
# Currently, not checking for 'min_replicas' not in profile_data[profile]
|
|
@@ -328,30 +363,30 @@ class ByoSnap:
|
|
|
328
363
|
'args' not in profile_data[profile] or \
|
|
329
364
|
'env_params' not in profile_data[profile]:
|
|
330
365
|
snapctl_error(
|
|
331
|
-
'Invalid BYOSnap profile JSON. Please check the JSON structure',
|
|
332
|
-
SNAPCTL_INPUT_ERROR
|
|
366
|
+
message='Invalid BYOSnap profile JSON. Please check the JSON structure',
|
|
367
|
+
code=SNAPCTL_INPUT_ERROR
|
|
333
368
|
)
|
|
334
369
|
if profile_data[profile]['cpu'] not in ByoSnap.VALID_CPU_MARKS:
|
|
335
370
|
snapctl_error(
|
|
336
|
-
'Invalid CPU value in BYOSnap profile. Valid values are' +
|
|
371
|
+
message='Invalid CPU value in BYOSnap profile. Valid values are' +
|
|
337
372
|
f'{", ".join(map(str, ByoSnap.VALID_CPU_MARKS))}',
|
|
338
|
-
SNAPCTL_INPUT_ERROR
|
|
373
|
+
code=SNAPCTL_INPUT_ERROR
|
|
339
374
|
)
|
|
340
375
|
if profile_data[profile]['memory'] not in ByoSnap.VALID_MEMORY_MARKS:
|
|
341
376
|
snapctl_error(
|
|
342
|
-
'Invalid Memory value in BYOSnap profile. Valid values are ' +
|
|
377
|
+
message='Invalid Memory value in BYOSnap profile. Valid values are ' +
|
|
343
378
|
f'{", ".join(map(str, ByoSnap.VALID_MEMORY_MARKS))}',
|
|
344
|
-
SNAPCTL_INPUT_ERROR
|
|
379
|
+
code=SNAPCTL_INPUT_ERROR
|
|
345
380
|
)
|
|
346
381
|
if 'min_replicas' in profile_data[profile] and \
|
|
347
382
|
(not isinstance(profile_data[profile]['min_replicas'], int) or
|
|
348
383
|
int(profile_data[profile]['min_replicas']) < 0 or
|
|
349
384
|
int(profile_data[profile]['min_replicas']) > ByoSnap.MAX_MIN_REPLICAS):
|
|
350
385
|
snapctl_error(
|
|
351
|
-
'Invalid Min Replicas value in BYOSnap profile. ' +
|
|
386
|
+
message='Invalid Min Replicas value in BYOSnap profile. ' +
|
|
352
387
|
'Minimum replicas should be between 0 and ' +
|
|
353
388
|
f'{ByoSnap.MAX_MIN_REPLICAS}',
|
|
354
|
-
SNAPCTL_INPUT_ERROR
|
|
389
|
+
code=SNAPCTL_INPUT_ERROR
|
|
355
390
|
)
|
|
356
391
|
|
|
357
392
|
def _clean_slate(self) -> None:
|
|
@@ -375,7 +410,8 @@ class ByoSnap:
|
|
|
375
410
|
f"docker logout {ecr_domain}"
|
|
376
411
|
], shell=True, check=False)
|
|
377
412
|
if not logout_response.returncode:
|
|
378
|
-
return snapctl_success(
|
|
413
|
+
return snapctl_success(
|
|
414
|
+
message='Cleanup complete.', progress=progress, no_exit=True)
|
|
379
415
|
except subprocess.CalledProcessError:
|
|
380
416
|
warning('Unable to initialize with a clean slate.')
|
|
381
417
|
finally:
|
|
@@ -390,130 +426,183 @@ class ByoSnap:
|
|
|
390
426
|
"""
|
|
391
427
|
# Check API Key and Base URL
|
|
392
428
|
if not self.api_key or self.base_url == '':
|
|
393
|
-
snapctl_error(
|
|
429
|
+
snapctl_error(
|
|
430
|
+
message="Missing API Key.", code=SNAPCTL_INPUT_ERROR)
|
|
394
431
|
# Check subcommand
|
|
395
432
|
if not self.subcommand in ByoSnap.SUBCOMMANDS:
|
|
396
433
|
snapctl_error(
|
|
397
|
-
"Invalid command. Valid commands are " +
|
|
434
|
+
message="Invalid command. Valid commands are " +
|
|
398
435
|
f"{', '.join(ByoSnap.SUBCOMMANDS)}.",
|
|
399
|
-
SNAPCTL_INPUT_ERROR
|
|
436
|
+
code=SNAPCTL_INPUT_ERROR
|
|
400
437
|
)
|
|
401
438
|
# Validate the SID
|
|
402
439
|
if not self.sid.startswith(ByoSnap.ID_PREFIX):
|
|
403
440
|
snapctl_error(
|
|
404
|
-
"Invalid Snap ID. Valid Snap IDs start with " +
|
|
441
|
+
message="Invalid Snap ID. Valid Snap IDs start with " +
|
|
405
442
|
f"{ByoSnap.ID_PREFIX}.",
|
|
406
|
-
SNAPCTL_INPUT_ERROR
|
|
443
|
+
code=SNAPCTL_INPUT_ERROR
|
|
407
444
|
)
|
|
408
445
|
if len(self.sid) > ByoSnap.SID_CHARACTER_LIMIT:
|
|
409
446
|
snapctl_error(
|
|
410
|
-
"Invalid Snap ID. Snap ID should be less than " +
|
|
447
|
+
message="Invalid Snap ID. Snap ID should be less than " +
|
|
411
448
|
f"{ByoSnap.SID_CHARACTER_LIMIT} characters",
|
|
412
|
-
SNAPCTL_INPUT_ERROR
|
|
449
|
+
code=SNAPCTL_INPUT_ERROR
|
|
413
450
|
)
|
|
414
451
|
# Validation for subcommands
|
|
415
452
|
if self.subcommand == 'create':
|
|
416
453
|
if self.name == '':
|
|
417
|
-
snapctl_error("Missing name", SNAPCTL_INPUT_ERROR)
|
|
454
|
+
snapctl_error(message="Missing name", code=SNAPCTL_INPUT_ERROR)
|
|
418
455
|
if not self.language:
|
|
419
|
-
snapctl_error("Missing language",
|
|
456
|
+
snapctl_error(message="Missing language",
|
|
457
|
+
code=SNAPCTL_INPUT_ERROR)
|
|
420
458
|
if self.language not in ByoSnap.LANGUAGES:
|
|
421
459
|
snapctl_error(
|
|
422
|
-
"Invalid language. Valid languages are " +
|
|
460
|
+
message="Invalid language. Valid languages are " +
|
|
423
461
|
f"{', '.join(ByoSnap.LANGUAGES)}.",
|
|
424
|
-
SNAPCTL_INPUT_ERROR
|
|
462
|
+
code=SNAPCTL_INPUT_ERROR
|
|
425
463
|
)
|
|
426
464
|
if self.platform_type not in ByoSnap.PLATFORMS:
|
|
427
465
|
snapctl_error(
|
|
428
|
-
"Invalid platform. Valid platforms are " +
|
|
466
|
+
message="Invalid platform. Valid platforms are " +
|
|
429
467
|
f"{', '.join(ByoSnap.PLATFORMS)}.",
|
|
468
|
+
code=SNAPCTL_INPUT_ERROR
|
|
469
|
+
)
|
|
470
|
+
return
|
|
471
|
+
# Now validate all other commands Check the token
|
|
472
|
+
if self.token_parts is None:
|
|
473
|
+
snapctl_error('Invalid token. Please reach out to your support team.',
|
|
474
|
+
SNAPCTL_INPUT_ERROR)
|
|
475
|
+
if self.subcommand in ['publish-image']:
|
|
476
|
+
if not self.tag:
|
|
477
|
+
snapctl_error(
|
|
478
|
+
"Missing required parameter: tag", SNAPCTL_INPUT_ERROR)
|
|
479
|
+
if len(self.tag.split()) > 1 or \
|
|
480
|
+
len(self.tag) > ByoSnap.TAG_CHARACTER_LIMIT:
|
|
481
|
+
snapctl_error(
|
|
482
|
+
"Tag should be a single word with maximum of " +
|
|
483
|
+
f"{ByoSnap.TAG_CHARACTER_LIMIT} characters",
|
|
430
484
|
SNAPCTL_INPUT_ERROR
|
|
431
485
|
)
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
if self.token_parts is None:
|
|
435
|
-
snapctl_error('Invalid token. Please reach out to your support team.',
|
|
486
|
+
if not self.skip_build and not self.path:
|
|
487
|
+
snapctl_error("Missing required parameter: path",
|
|
436
488
|
SNAPCTL_INPUT_ERROR)
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
)
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
snapctl_error("Missing version", SNAPCTL_INPUT_ERROR)
|
|
491
|
-
pattern = r'^v\d+\.\d+\.\d+$'
|
|
492
|
-
if not re.match(pattern, self.version):
|
|
493
|
-
snapctl_error("Version should be in the format vX.X.X",
|
|
494
|
-
SNAPCTL_INPUT_ERROR)
|
|
495
|
-
if not self.http_port:
|
|
496
|
-
snapctl_error("Missing Ingress HTTP Port",
|
|
489
|
+
# Check path
|
|
490
|
+
if self.resources_path:
|
|
491
|
+
docker_file_path = \
|
|
492
|
+
f"{self.resources_path}/{self.dockerfile}"
|
|
493
|
+
else:
|
|
494
|
+
docker_file_path = f"{self.path}/{self.dockerfile}"
|
|
495
|
+
if not self.skip_build and not os.path.isfile(docker_file_path):
|
|
496
|
+
snapctl_error(
|
|
497
|
+
"Unable to find " +
|
|
498
|
+
f"{self.dockerfile} at path {docker_file_path}",
|
|
499
|
+
SNAPCTL_INPUT_ERROR)
|
|
500
|
+
# elif self.subcommand == 'push':
|
|
501
|
+
# if not self.tag:
|
|
502
|
+
# error("Missing required parameter: tag", SNAPCTL_INPUT_ERROR)
|
|
503
|
+
# raise typer.Exit(code=SNAPCTL_INPUT_ERROR)
|
|
504
|
+
elif self.subcommand == 'upload-docs':
|
|
505
|
+
if self.path is None and self.resources_path is None:
|
|
506
|
+
snapctl_error(
|
|
507
|
+
"Missing one of: path or resources-path parameter", SNAPCTL_INPUT_ERROR)
|
|
508
|
+
elif self.subcommand == 'publish-version':
|
|
509
|
+
if not self.tag:
|
|
510
|
+
snapctl_error(
|
|
511
|
+
"Missing required parameter: tag", SNAPCTL_INPUT_ERROR)
|
|
512
|
+
if len(self.tag.split()) > 1 or \
|
|
513
|
+
len(self.tag) > ByoSnap.TAG_CHARACTER_LIMIT:
|
|
514
|
+
snapctl_error(
|
|
515
|
+
"Tag should be a single word with maximum of " +
|
|
516
|
+
f"{ByoSnap.TAG_CHARACTER_LIMIT} characters",
|
|
517
|
+
SNAPCTL_INPUT_ERROR
|
|
518
|
+
)
|
|
519
|
+
if not self.prefix or self.prefix == '':
|
|
520
|
+
snapctl_error("Missing prefix", SNAPCTL_INPUT_ERROR)
|
|
521
|
+
if not self.prefix.startswith('/'):
|
|
522
|
+
snapctl_error("Prefix should start with a forward slash (/)",
|
|
523
|
+
SNAPCTL_INPUT_ERROR)
|
|
524
|
+
if self.prefix.endswith('/'):
|
|
525
|
+
snapctl_error("Prefix should not end with a forward slash (/)",
|
|
526
|
+
SNAPCTL_INPUT_ERROR)
|
|
527
|
+
if not self.version:
|
|
528
|
+
snapctl_error("Missing version", SNAPCTL_INPUT_ERROR)
|
|
529
|
+
pattern = r'^v\d+\.\d+\.\d+$'
|
|
530
|
+
if not re.match(pattern, self.version):
|
|
531
|
+
snapctl_error("Version should be in the format vX.X.X",
|
|
532
|
+
SNAPCTL_INPUT_ERROR)
|
|
533
|
+
if not self.http_port:
|
|
534
|
+
snapctl_error("Missing Ingress HTTP Port",
|
|
535
|
+
SNAPCTL_INPUT_ERROR)
|
|
536
|
+
if not self.http_port.isdigit():
|
|
537
|
+
snapctl_error("Ingress HTTP Port should be a number",
|
|
538
|
+
SNAPCTL_INPUT_ERROR)
|
|
539
|
+
if self.readiness_path is not None:
|
|
540
|
+
if self.readiness_path.strip() == '':
|
|
541
|
+
snapctl_error("Readiness path cannot be empty",
|
|
497
542
|
SNAPCTL_INPUT_ERROR)
|
|
498
|
-
if not self.
|
|
499
|
-
snapctl_error("
|
|
543
|
+
if not self.readiness_path.strip().startswith('/'):
|
|
544
|
+
snapctl_error("Readiness path has to start with /",
|
|
500
545
|
SNAPCTL_INPUT_ERROR)
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
546
|
+
if self.readiness_delay is not None:
|
|
547
|
+
if self.readiness_delay < 0 or \
|
|
548
|
+
self.readiness_delay > ByoSnap.MAX_READINESS_TIMEOUT:
|
|
549
|
+
snapctl_error(
|
|
550
|
+
"Readiness delay should be between 0 " +
|
|
551
|
+
f"and {ByoSnap.MAX_READINESS_TIMEOUT}", SNAPCTL_INPUT_ERROR)
|
|
552
|
+
# Check byosnap_profile path
|
|
553
|
+
self._validate_byosnap_profile()
|
|
554
|
+
elif self.subcommand == 'update-version':
|
|
555
|
+
if not self.version:
|
|
556
|
+
snapctl_error(message="Missing version",
|
|
557
|
+
code=SNAPCTL_INPUT_ERROR)
|
|
558
|
+
pattern = r'^v\d+\.\d+\.\d+$'
|
|
559
|
+
if not re.match(pattern, self.version):
|
|
560
|
+
snapctl_error(
|
|
561
|
+
message="Version should be in the format vX.X.X",
|
|
562
|
+
code=SNAPCTL_INPUT_ERROR)
|
|
563
|
+
if not self.tag:
|
|
564
|
+
snapctl_error(
|
|
565
|
+
message="Missing required parameter: tag", code=SNAPCTL_INPUT_ERROR)
|
|
566
|
+
if len(self.tag.split()) > 1 or \
|
|
567
|
+
len(self.tag) > ByoSnap.TAG_CHARACTER_LIMIT:
|
|
568
|
+
snapctl_error(
|
|
569
|
+
message="Tag should be a single word with maximum of " +
|
|
570
|
+
f"{ByoSnap.TAG_CHARACTER_LIMIT} characters",
|
|
571
|
+
code=SNAPCTL_INPUT_ERROR
|
|
572
|
+
)
|
|
573
|
+
elif self.subcommand == 'sync':
|
|
574
|
+
if not self.version:
|
|
575
|
+
snapctl_error(message="Missing version",
|
|
576
|
+
code=SNAPCTL_INPUT_ERROR)
|
|
577
|
+
pattern = r'^v\d+\.\d+\.\d+$'
|
|
578
|
+
if not re.match(pattern, self.version):
|
|
579
|
+
snapctl_error(message="Version should be in the format vX.X.X",
|
|
580
|
+
code=SNAPCTL_INPUT_ERROR)
|
|
581
|
+
if not self.tag:
|
|
582
|
+
snapctl_error(
|
|
583
|
+
message="Missing required parameter: tag", code=SNAPCTL_INPUT_ERROR)
|
|
584
|
+
if len(self.tag.split()) > 1 or \
|
|
585
|
+
len(self.tag) > ByoSnap.TAG_CHARACTER_LIMIT:
|
|
586
|
+
snapctl_error(
|
|
587
|
+
message="Tag should be a single word with maximum of " +
|
|
588
|
+
f"{ByoSnap.TAG_CHARACTER_LIMIT} characters",
|
|
589
|
+
code=SNAPCTL_INPUT_ERROR
|
|
590
|
+
)
|
|
591
|
+
if not self.skip_build and not self.path:
|
|
592
|
+
snapctl_error(
|
|
593
|
+
message="Missing required parameter: path",
|
|
594
|
+
code=SNAPCTL_INPUT_ERROR)
|
|
595
|
+
# Check path
|
|
596
|
+
if self.resources_path:
|
|
597
|
+
docker_file_path = \
|
|
598
|
+
f"{self.resources_path}/{self.dockerfile}"
|
|
599
|
+
else:
|
|
600
|
+
docker_file_path = f"{self.path}/{self.dockerfile}"
|
|
601
|
+
if not self.skip_build and not os.path.isfile(docker_file_path):
|
|
602
|
+
snapctl_error(
|
|
603
|
+
message="Unable to find " +
|
|
604
|
+
f"{self.dockerfile} at path {docker_file_path}",
|
|
605
|
+
code=SNAPCTL_INPUT_ERROR)
|
|
517
606
|
# CRUD methods
|
|
518
607
|
|
|
519
608
|
def build(self) -> None:
|
|
@@ -541,9 +630,9 @@ class ByoSnap:
|
|
|
541
630
|
self._docker_push()
|
|
542
631
|
|
|
543
632
|
# Upper echelon commands
|
|
544
|
-
def upload_docs(self) -> None:
|
|
633
|
+
def upload_docs(self, no_exit: bool = False) -> None:
|
|
545
634
|
'''
|
|
546
|
-
|
|
635
|
+
Note this step is optional hence we do not raise a typer.Exit
|
|
547
636
|
'''
|
|
548
637
|
progress = Progress(
|
|
549
638
|
SpinnerColumn(),
|
|
@@ -562,23 +651,24 @@ class ByoSnap:
|
|
|
562
651
|
swagger_file = os.path.join(base_dir, 'swagger.json')
|
|
563
652
|
readme_file = os.path.join(base_dir, 'README.md')
|
|
564
653
|
|
|
654
|
+
# Upload swagger.json
|
|
565
655
|
if os.path.isfile(swagger_file):
|
|
566
|
-
# Push the swagger.json
|
|
567
656
|
try:
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
657
|
+
with open(swagger_file, "rb") as attachment_file:
|
|
658
|
+
url = (
|
|
659
|
+
f"{self.base_url}/v1/snapser-api/byosnaps/"
|
|
660
|
+
f"{self.sid}/docs/{self.tag}/openapispec"
|
|
661
|
+
)
|
|
662
|
+
test_res = requests.post(
|
|
663
|
+
url, files={"attachment": attachment_file},
|
|
664
|
+
headers={'api-key': self.api_key},
|
|
665
|
+
timeout=SERVER_CALL_TIMEOUT
|
|
666
|
+
)
|
|
667
|
+
if test_res.ok:
|
|
668
|
+
snapctl_success(
|
|
669
|
+
message='Uploaded swagger.json', progress=None, no_exit=True)
|
|
670
|
+
else:
|
|
671
|
+
info('Unable to upload your swagger.json')
|
|
582
672
|
except RequestException as e:
|
|
583
673
|
info(
|
|
584
674
|
'Exception: Unable to find swagger.json at ' +
|
|
@@ -586,28 +676,28 @@ class ByoSnap:
|
|
|
586
676
|
)
|
|
587
677
|
else:
|
|
588
678
|
info(
|
|
589
|
-
f'No swagger.json found at {
|
|
590
|
-
|
|
679
|
+
f'No swagger.json found at {
|
|
680
|
+
base_dir}. Skipping swagger.json upload'
|
|
591
681
|
)
|
|
592
682
|
|
|
593
|
-
#
|
|
683
|
+
# Upload README.md
|
|
594
684
|
if os.path.isfile(readme_file):
|
|
595
|
-
# Push the swagger.json
|
|
596
685
|
try:
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
686
|
+
with open(readme_file, "rb") as attachment_file:
|
|
687
|
+
url = (
|
|
688
|
+
f"{self.base_url}/v1/snapser-api/byosnaps/"
|
|
689
|
+
f"{self.sid}/docs/{self.tag}/markdown"
|
|
690
|
+
)
|
|
691
|
+
test_res = requests.post(
|
|
692
|
+
url, files={"attachment": attachment_file},
|
|
693
|
+
headers={'api-key': self.api_key},
|
|
694
|
+
timeout=SERVER_CALL_TIMEOUT
|
|
695
|
+
)
|
|
696
|
+
if test_res.ok:
|
|
697
|
+
snapctl_success(
|
|
698
|
+
message='Uploaded README.md', progress=None, no_exit=True)
|
|
699
|
+
else:
|
|
700
|
+
info('Unable to upload your README.md')
|
|
611
701
|
except RequestException as e:
|
|
612
702
|
info(
|
|
613
703
|
'Exception: Unable to find README.md at ' +
|
|
@@ -615,14 +705,45 @@ class ByoSnap:
|
|
|
615
705
|
)
|
|
616
706
|
else:
|
|
617
707
|
info(
|
|
618
|
-
f'No README.md found at {
|
|
708
|
+
f'No README.md found at {
|
|
709
|
+
base_dir}. Skipping README.md upload'
|
|
710
|
+
)
|
|
711
|
+
|
|
712
|
+
# Upload any snapser-tool-*.json files
|
|
713
|
+
for file_name in os.listdir(base_dir):
|
|
714
|
+
if file_name.startswith("snapser-tool-") and file_name.endswith(".json"):
|
|
715
|
+
file_path = os.path.join(base_dir, file_name)
|
|
716
|
+
if os.path.isfile(file_path):
|
|
717
|
+
try:
|
|
718
|
+
with open(file_path, "rb") as attachment_file:
|
|
719
|
+
url = (
|
|
720
|
+
f"{self.base_url}/v1/snapser-api/byosnaps/"
|
|
721
|
+
f"{self.sid}/docs/{self.tag}/tools"
|
|
722
|
+
)
|
|
723
|
+
test_res = requests.post(
|
|
724
|
+
url, files={"attachment": attachment_file},
|
|
725
|
+
headers={'api-key': self.api_key},
|
|
726
|
+
timeout=SERVER_CALL_TIMEOUT
|
|
727
|
+
)
|
|
728
|
+
if test_res.ok:
|
|
729
|
+
snapctl_success(
|
|
730
|
+
message=f'Uploaded tool {file_name}',
|
|
731
|
+
progress=None, no_exit=True)
|
|
732
|
+
else:
|
|
733
|
+
info(f'Unable to upload tool {file_name}')
|
|
734
|
+
except RequestException as e:
|
|
735
|
+
info('Exception: Unable to upload tool ' +
|
|
736
|
+
f'{file_name} {str(e)}')
|
|
737
|
+
|
|
738
|
+
# Show success message
|
|
739
|
+
snapctl_success(
|
|
740
|
+
message='Completed the docs uploading process', progress=progress, no_exit=no_exit)
|
|
619
741
|
except RequestException as e:
|
|
620
742
|
info(f'Exception: Unable to upload your API Json {str(e)}')
|
|
621
743
|
finally:
|
|
622
744
|
progress.stop()
|
|
623
|
-
# snapctl_success('BYOSnap upload successful', no_exit=no_exit)
|
|
624
745
|
|
|
625
|
-
def create(self) -> None:
|
|
746
|
+
def create(self, no_exit: bool = False) -> None:
|
|
626
747
|
"""
|
|
627
748
|
Creating a new snap
|
|
628
749
|
"""
|
|
@@ -647,34 +768,44 @@ class ByoSnap:
|
|
|
647
768
|
timeout=SERVER_CALL_TIMEOUT
|
|
648
769
|
)
|
|
649
770
|
if res.ok:
|
|
650
|
-
snapctl_success(
|
|
771
|
+
return snapctl_success(
|
|
772
|
+
message='BYOSNAP create successful', progress=progress, no_exit=no_exit)
|
|
651
773
|
response_json = res.json()
|
|
652
774
|
if "api_error_code" in response_json and "message" in response_json:
|
|
775
|
+
if response_json['api_error_code'] == HTTP_ERROR_RESOURCE_NOT_FOUND:
|
|
776
|
+
snapctl_error(
|
|
777
|
+
message='BYOSnap not found.',
|
|
778
|
+
code=SNAPCTL_BYOSNAP_NOT_FOUND, progress=progress
|
|
779
|
+
)
|
|
653
780
|
if response_json['api_error_code'] == HTTP_ERROR_SERVICE_VERSION_EXISTS:
|
|
654
781
|
snapctl_error(
|
|
655
|
-
f'BYOSnap {self.name} already exists. ' +
|
|
782
|
+
message=f'BYOSnap {self.name} already exists. ' +
|
|
656
783
|
'Please use a different name',
|
|
657
|
-
SNAPCTL_BYOSNAP_CREATE_DUPLICATE_NAME_ERROR,
|
|
658
|
-
progress
|
|
784
|
+
code=SNAPCTL_BYOSNAP_CREATE_DUPLICATE_NAME_ERROR,
|
|
785
|
+
progress=progress
|
|
659
786
|
)
|
|
660
787
|
# elif response_json['api_error_code'] == HTTP_ERROR_TAG_NOT_AVAILABLE:
|
|
661
788
|
# error('Invalid tag. Please use the correct tag')
|
|
662
789
|
if response_json['api_error_code'] == HTTP_ERROR_ADD_ON_NOT_ENABLED:
|
|
663
790
|
snapctl_error(
|
|
664
|
-
'Missing Add-on. Please enable the add-on via the Snapser Web app.',
|
|
665
|
-
SNAPCTL_BYOSNAP_CREATE_PERMISSION_ERROR,
|
|
791
|
+
message='Missing Add-on. Please enable the add-on via the Snapser Web app.',
|
|
792
|
+
code=SNAPCTL_BYOSNAP_CREATE_PERMISSION_ERROR,
|
|
793
|
+
progress=progress
|
|
666
794
|
)
|
|
667
795
|
snapctl_error(
|
|
668
|
-
f'Server error: {json.dumps(response_json, indent=2)}',
|
|
669
|
-
SNAPCTL_BYOSNAP_CREATE_ERROR, progress)
|
|
796
|
+
message=f'Server error: {json.dumps(response_json, indent=2)}',
|
|
797
|
+
code=SNAPCTL_BYOSNAP_CREATE_ERROR, progress=progress)
|
|
670
798
|
except RequestException as e:
|
|
671
799
|
snapctl_error(
|
|
672
|
-
f"Exception: Unable to create your snap {e}",
|
|
673
|
-
SNAPCTL_BYOSNAP_CREATE_ERROR, progress)
|
|
674
|
-
|
|
675
|
-
|
|
800
|
+
message=f"Exception: Unable to create your snap {e}",
|
|
801
|
+
code=SNAPCTL_BYOSNAP_CREATE_ERROR, progress=progress)
|
|
802
|
+
finally:
|
|
803
|
+
progress.stop()
|
|
804
|
+
snapctl_error(
|
|
805
|
+
message='Failed to create snap',
|
|
806
|
+
code=SNAPCTL_BYOSNAP_CREATE_ERROR, progress=progress)
|
|
676
807
|
|
|
677
|
-
def publish_image(self) -> None:
|
|
808
|
+
def publish_image(self, no_exit: bool = False) -> None:
|
|
678
809
|
"""
|
|
679
810
|
Publish the image
|
|
680
811
|
1. Check Dependencies
|
|
@@ -694,10 +825,11 @@ class ByoSnap:
|
|
|
694
825
|
self._docker_login()
|
|
695
826
|
self._docker_push()
|
|
696
827
|
if self.path is not None or self.resources_path is not None:
|
|
697
|
-
self.upload_docs()
|
|
698
|
-
snapctl_success(
|
|
828
|
+
self.upload_docs(no_exit=True)
|
|
829
|
+
snapctl_success(
|
|
830
|
+
message='BYOSNAP publish successful', no_exit=no_exit)
|
|
699
831
|
|
|
700
|
-
def publish_version(self) -> None:
|
|
832
|
+
def publish_version(self, no_exit: bool = False) -> None:
|
|
701
833
|
"""
|
|
702
834
|
Publish the version
|
|
703
835
|
"""
|
|
@@ -718,7 +850,7 @@ class ByoSnap:
|
|
|
718
850
|
profile_data = json.load(file)
|
|
719
851
|
payload = {
|
|
720
852
|
"version": self.version,
|
|
721
|
-
"image_tag": self.
|
|
853
|
+
"image_tag": self.tag,
|
|
722
854
|
"base_url": f"{self.prefix}/{self.sid}",
|
|
723
855
|
"http_port": self.http_port,
|
|
724
856
|
"readiness_probe_config": {
|
|
@@ -735,22 +867,115 @@ class ByoSnap:
|
|
|
735
867
|
timeout=SERVER_CALL_TIMEOUT
|
|
736
868
|
)
|
|
737
869
|
if res.ok:
|
|
738
|
-
snapctl_success(
|
|
870
|
+
return snapctl_success(
|
|
871
|
+
message='BYOSNAP publish version successful',
|
|
872
|
+
progress=progress, no_exit=no_exit)
|
|
739
873
|
response_json = res.json()
|
|
740
874
|
if "api_error_code" in response_json:
|
|
875
|
+
if response_json['api_error_code'] == HTTP_ERROR_RESOURCE_NOT_FOUND:
|
|
876
|
+
snapctl_error(
|
|
877
|
+
message='BYOSnap not found.',
|
|
878
|
+
code=SNAPCTL_BYOSNAP_NOT_FOUND, progress=progress
|
|
879
|
+
)
|
|
741
880
|
if response_json['api_error_code'] == HTTP_ERROR_SERVICE_VERSION_EXISTS:
|
|
742
881
|
snapctl_error(
|
|
743
|
-
'Version already exists. Please update your version and try again',
|
|
744
|
-
SNAPCTL_BYOSNAP_PUBLISH_IMAGE_DUPLICATE_TAG_ERROR,
|
|
745
|
-
progress
|
|
882
|
+
message='Version already exists. Please update your version and try again',
|
|
883
|
+
code=SNAPCTL_BYOSNAP_PUBLISH_IMAGE_DUPLICATE_TAG_ERROR,
|
|
884
|
+
progress=progress
|
|
746
885
|
)
|
|
747
886
|
if response_json['api_error_code'] == HTTP_ERROR_TAG_NOT_AVAILABLE:
|
|
748
|
-
snapctl_error(
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
887
|
+
snapctl_error(
|
|
888
|
+
message='Invalid tag. Please use the correct tag.',
|
|
889
|
+
code=SNAPCTL_BYOSNAP_PUBLISH_VERSION_DUPLICATE_TAG_ERROR,
|
|
890
|
+
progress=progress)
|
|
891
|
+
snapctl_error(
|
|
892
|
+
message=f'Server error: {json.dumps(response_json, indent=2)}',
|
|
893
|
+
code=SNAPCTL_BYOSNAP_PUBLISH_VERSION_ERROR, progress=progress)
|
|
752
894
|
except RequestException as e:
|
|
753
|
-
snapctl_error(
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
895
|
+
snapctl_error(
|
|
896
|
+
message='Exception: Unable to publish a ' +
|
|
897
|
+
f'version for your snap. Exception: {e}',
|
|
898
|
+
code=SNAPCTL_BYOSNAP_PUBLISH_VERSION_ERROR, progress=progress)
|
|
899
|
+
finally:
|
|
900
|
+
progress.stop()
|
|
901
|
+
snapctl_error(
|
|
902
|
+
message='Failed to publish version',
|
|
903
|
+
code=SNAPCTL_BYOSNAP_PUBLISH_VERSION_ERROR, progress=progress)
|
|
904
|
+
|
|
905
|
+
def update_version(self, no_exit: bool = False) -> None:
|
|
906
|
+
"""
|
|
907
|
+
Update the byosnap version
|
|
908
|
+
"""
|
|
909
|
+
progress = Progress(
|
|
910
|
+
SpinnerColumn(),
|
|
911
|
+
TextColumn("[progress.description]{task.description}"),
|
|
912
|
+
transient=True,
|
|
913
|
+
)
|
|
914
|
+
progress.start()
|
|
915
|
+
progress.add_task(
|
|
916
|
+
description='Updating your Byosnap...', total=None)
|
|
917
|
+
try:
|
|
918
|
+
payload = {
|
|
919
|
+
'image_tag': self.tag,
|
|
920
|
+
}
|
|
921
|
+
res = requests.patch(
|
|
922
|
+
f"{self.base_url}/v1/snapser-api/byosnaps/{self.sid}/versions/{self.version}",
|
|
923
|
+
json=payload, headers={'api-key': self.api_key},
|
|
924
|
+
timeout=SERVER_CALL_TIMEOUT
|
|
925
|
+
)
|
|
926
|
+
if res.ok:
|
|
927
|
+
return snapctl_success(
|
|
928
|
+
message='BYOSNAP update version successful',
|
|
929
|
+
progress=progress, no_exit=no_exit)
|
|
930
|
+
response_json = res.json()
|
|
931
|
+
if "api_error_code" in response_json:
|
|
932
|
+
if response_json['api_error_code'] == HTTP_ERROR_RESOURCE_NOT_FOUND:
|
|
933
|
+
snapctl_error(
|
|
934
|
+
message='BYOSnap not found.',
|
|
935
|
+
code=SNAPCTL_BYOSNAP_NOT_FOUND, progress=progress
|
|
936
|
+
)
|
|
937
|
+
if response_json['api_error_code'] == HTTP_ERROR_SERVICE_IN_USE:
|
|
938
|
+
snapctl_error(
|
|
939
|
+
message='Version already in use in a staging or production snapend. ' +
|
|
940
|
+
'Please publish an unused version and start using that instead.',
|
|
941
|
+
code=SNAPCTL_BYOSNAP_UPDATE_VERSION_SERVICE_IN_USE_ERROR,
|
|
942
|
+
progress=progress
|
|
943
|
+
)
|
|
944
|
+
if response_json['api_error_code'] == HTTP_ERROR_TAG_NOT_AVAILABLE:
|
|
945
|
+
snapctl_error(
|
|
946
|
+
message='Invalid tag. Please use the correct tag.',
|
|
947
|
+
code=SNAPCTL_BYOSNAP_UPDATE_VERSION_TAG_ERROR, progress=progress)
|
|
948
|
+
snapctl_error(
|
|
949
|
+
message=f'Server error: {json.dumps(response_json, indent=2)}',
|
|
950
|
+
code=SNAPCTL_BYOSNAP_UPDATE_VERSION_ERROR, progress=progress)
|
|
951
|
+
except RequestException as e:
|
|
952
|
+
snapctl_error(
|
|
953
|
+
message='Exception: Unable to update a ' +
|
|
954
|
+
f'version for your snap. Exception: {e}',
|
|
955
|
+
code=SNAPCTL_BYOSNAP_UPDATE_VERSION_ERROR, progress=progress)
|
|
956
|
+
finally:
|
|
957
|
+
progress.stop()
|
|
958
|
+
snapctl_error(
|
|
959
|
+
message='Failed to update version',
|
|
960
|
+
code=SNAPCTL_BYOSNAP_UPDATE_VERSION_ERROR, progress=progress)
|
|
961
|
+
|
|
962
|
+
def sync(self) -> None:
|
|
963
|
+
'''
|
|
964
|
+
Sync the snap
|
|
965
|
+
'''
|
|
966
|
+
try:
|
|
967
|
+
self.tag = f'{self.tag}-{int(time.time())}'
|
|
968
|
+
self.publish_image(no_exit=True)
|
|
969
|
+
self.update_version(no_exit=True)
|
|
970
|
+
byosnap_list: str = f"{self.sid}:{self.version}"
|
|
971
|
+
snapend = Snapend(
|
|
972
|
+
subcommand='update', base_url=self.base_url, api_key=self.api_key,
|
|
973
|
+
snapend_id=self.snapend_id, byosnaps=byosnap_list, blocking=self.blocking
|
|
974
|
+
)
|
|
975
|
+
snapend.update(no_exit=True)
|
|
976
|
+
return snapctl_success(message='BYOSNAP sync successful')
|
|
977
|
+
except RequestException as e:
|
|
978
|
+
snapctl_error(
|
|
979
|
+
message='Exception: Unable to update a ' +
|
|
980
|
+
f' version for your snap. Exception: {e}',
|
|
981
|
+
code=SNAPCTL_BYOSNAP_UPDATE_VERSION_ERROR)
|