snapctl 0.39.2__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.

@@ -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
- from snapctl.utils.echo import success, info, warning
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, name: str,
51
- desc: str, platform_type: str, language: str, input_tag: Union[str, None],
52
- path: Union[str, None], resources_path: Union[str, None], dockerfile: str,
53
- prefix: str, version: Union[str, None], http_port: Union[int, None],
54
- byosnap_profile: Union[str, None], skip_build: bool = False,
55
- readiness_path: Union[str, None] = None, readiness_delay: Union[int, None] = None
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.input_tag: Union[str, None] = input_tag
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('BYOSnap dependencies verified',
130
- progress, no_exit=True)
141
+ return snapctl_success(
142
+ message='BYOSnap dependencies verified',
143
+ progress=progress, no_exit=True)
131
144
  except subprocess.CalledProcessError:
132
- snapctl_error('Snapctl Exception',
133
- SNAPCTL_BYOSNAP_DEPENDENCY_MISSING, progress)
134
- snapctl_error('Docker not running. Please start docker.',
135
- SNAPCTL_BYOSNAP_DEPENDENCY_MISSING, progress)
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('BYOSnap ECR login successful',
166
- progress, no_exit=True)
182
+ return snapctl_success(
183
+ message='BYOSnap ECR login successful',
184
+ progress=progress, no_exit=True)
167
185
  except subprocess.CalledProcessError:
168
- snapctl_error('Snapctl Exception',
169
- SNAPCTL_BYOSNAP_ECR_LOGIN_ERROR, progress)
170
- snapctl_error('BYOSnap ECR login failure',
171
- SNAPCTL_BYOSNAP_ECR_LOGIN_ERROR, progress)
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.input_tag}'
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.input_tag,
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.input_tag} " +
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('BYOSnap build successful',
219
- progress, no_exit=True)
240
+ return snapctl_success(
241
+ message='BYOSnap build successful', progress=progress, no_exit=True)
220
242
  except subprocess.CalledProcessError:
221
- snapctl_error('Snapctl Exception',
222
- SNAPCTL_BYOSNAP_BUILD_ERROR, progress)
223
- snapctl_error('BYOSnap build failure',
224
- SNAPCTL_BYOSNAP_BUILD_ERROR, progress)
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.input_tag}'
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.input_tag, full_ecr_repo_url
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.input_tag} {full_ecr_repo_url}"
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('BYOSnap tag successful',
251
- progress, no_exit=True)
276
+ return snapctl_success(
277
+ message='BYOSnap tag successful', progress=progress, no_exit=True)
252
278
  except subprocess.CalledProcessError:
253
- snapctl_error('Snapctl Exception',
254
- SNAPCTL_BYOSNAP_TAG_ERROR, progress)
255
- snapctl_error('BYOSnap tag failure',
256
- SNAPCTL_BYOSNAP_TAG_ERROR, progress)
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.input_tag}'
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('BYOSnap upload successful',
286
- progress, no_exit=True)
315
+ return snapctl_success(
316
+ message='BYOSnap upload successful', progress=progress, no_exit=True)
287
317
  except subprocess.CalledProcessError:
288
- snapctl_error('Snapctl Exception',
289
- SNAPCTL_BYOSNAP_PUBLISH_IMAGE_ERROR, progress)
290
- snapctl_error('BYOSnap upload failure. Duplicate image error.',
291
- SNAPCTL_BYOSNAP_PUBLISH_IMAGE_DUPLICATE_TAG_ERROR, progress)
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("Missing BYOSnap profile path", SNAPCTL_INPUT_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,32 +363,60 @@ 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
 
392
+ def _clean_slate(self) -> None:
393
+ progress = Progress(
394
+ SpinnerColumn(),
395
+ TextColumn("[progress.description]{task.description}"),
396
+ transient=True,
397
+ )
398
+ progress.start()
399
+ progress.add_task(
400
+ description='Cleaning cache and initializing...', total=None)
401
+ try:
402
+ # Specific ECR repository URL to check against
403
+ ecr_domain = self.token_parts[0].split('/')[0]
404
+ # Perform the Docker logout
405
+ if platform == "win32":
406
+ logout_response = subprocess.run(['docker', 'logout', ecr_domain],
407
+ shell=True, check=False)
408
+ else:
409
+ logout_response = subprocess.run([
410
+ f"docker logout {ecr_domain}"
411
+ ], shell=True, check=False)
412
+ if not logout_response.returncode:
413
+ return snapctl_success(
414
+ message='Cleanup complete.', progress=progress, no_exit=True)
415
+ except subprocess.CalledProcessError:
416
+ warning('Unable to initialize with a clean slate.')
417
+ finally:
418
+ progress.stop()
419
+
357
420
  # Public methods
358
421
 
359
422
  # Validator
@@ -363,130 +426,183 @@ class ByoSnap:
363
426
  """
364
427
  # Check API Key and Base URL
365
428
  if not self.api_key or self.base_url == '':
366
- snapctl_error("Missing API Key.", SNAPCTL_INPUT_ERROR)
429
+ snapctl_error(
430
+ message="Missing API Key.", code=SNAPCTL_INPUT_ERROR)
367
431
  # Check subcommand
368
432
  if not self.subcommand in ByoSnap.SUBCOMMANDS:
369
433
  snapctl_error(
370
- "Invalid command. Valid commands are " +
434
+ message="Invalid command. Valid commands are " +
371
435
  f"{', '.join(ByoSnap.SUBCOMMANDS)}.",
372
- SNAPCTL_INPUT_ERROR
436
+ code=SNAPCTL_INPUT_ERROR
373
437
  )
374
438
  # Validate the SID
375
439
  if not self.sid.startswith(ByoSnap.ID_PREFIX):
376
440
  snapctl_error(
377
- "Invalid Snap ID. Valid Snap IDs start with " +
441
+ message="Invalid Snap ID. Valid Snap IDs start with " +
378
442
  f"{ByoSnap.ID_PREFIX}.",
379
- SNAPCTL_INPUT_ERROR
443
+ code=SNAPCTL_INPUT_ERROR
380
444
  )
381
445
  if len(self.sid) > ByoSnap.SID_CHARACTER_LIMIT:
382
446
  snapctl_error(
383
- "Invalid Snap ID. Snap ID should be less than " +
447
+ message="Invalid Snap ID. Snap ID should be less than " +
384
448
  f"{ByoSnap.SID_CHARACTER_LIMIT} characters",
385
- SNAPCTL_INPUT_ERROR
449
+ code=SNAPCTL_INPUT_ERROR
386
450
  )
387
451
  # Validation for subcommands
388
452
  if self.subcommand == 'create':
389
453
  if self.name == '':
390
- snapctl_error("Missing name", SNAPCTL_INPUT_ERROR)
454
+ snapctl_error(message="Missing name", code=SNAPCTL_INPUT_ERROR)
391
455
  if not self.language:
392
- snapctl_error("Missing language", SNAPCTL_INPUT_ERROR)
456
+ snapctl_error(message="Missing language",
457
+ code=SNAPCTL_INPUT_ERROR)
393
458
  if self.language not in ByoSnap.LANGUAGES:
394
459
  snapctl_error(
395
- "Invalid language. Valid languages are " +
460
+ message="Invalid language. Valid languages are " +
396
461
  f"{', '.join(ByoSnap.LANGUAGES)}.",
397
- SNAPCTL_INPUT_ERROR
462
+ code=SNAPCTL_INPUT_ERROR
398
463
  )
399
464
  if self.platform_type not in ByoSnap.PLATFORMS:
400
465
  snapctl_error(
401
- "Invalid platform. Valid platforms are " +
466
+ message="Invalid platform. Valid platforms are " +
402
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",
403
484
  SNAPCTL_INPUT_ERROR
404
485
  )
405
- else:
406
- # Check the token
407
- if self.token_parts is None:
408
- 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",
409
488
  SNAPCTL_INPUT_ERROR)
410
- if self.subcommand in ['publish-image']:
411
- if not self.input_tag:
412
- snapctl_error(
413
- "Missing required parameter: tag", SNAPCTL_INPUT_ERROR)
414
- if len(self.input_tag.split()) > 1 or \
415
- len(self.input_tag) > ByoSnap.TAG_CHARACTER_LIMIT:
416
- snapctl_error(
417
- "Tag should be a single word with maximum of " +
418
- f"{ByoSnap.TAG_CHARACTER_LIMIT} characters",
419
- SNAPCTL_INPUT_ERROR
420
- )
421
- if not self.skip_build and not self.path:
422
- snapctl_error("Missing required parameter: path",
423
- SNAPCTL_INPUT_ERROR)
424
- # Check path
425
- if self.resources_path:
426
- docker_file_path = \
427
- f"{self.resources_path}/{self.dockerfile}"
428
- else:
429
- docker_file_path = f"{self.path}/{self.dockerfile}"
430
- if not self.skip_build and not os.path.isfile(docker_file_path):
431
- snapctl_error(
432
- "Unable to find " +
433
- f"{self.dockerfile} at path {docker_file_path}",
434
- SNAPCTL_INPUT_ERROR)
435
- # elif self.subcommand == 'push':
436
- # if not self.input_tag:
437
- # error("Missing required parameter: tag", SNAPCTL_INPUT_ERROR)
438
- # raise typer.Exit(code=SNAPCTL_INPUT_ERROR)
439
- elif self.subcommand == 'upload-docs':
440
- if self.path is None and self.resources_path is None:
441
- snapctl_error(
442
- "Missing one of: path or resources-path parameter", SNAPCTL_INPUT_ERROR)
443
- elif self.subcommand == 'publish-version':
444
- if not self.input_tag:
445
- snapctl_error(
446
- "Missing required parameter: tag", SNAPCTL_INPUT_ERROR)
447
- if len(self.input_tag.split()) > 1 or \
448
- len(self.input_tag) > ByoSnap.TAG_CHARACTER_LIMIT:
449
- snapctl_error(
450
- "Tag should be a single word with maximum of " +
451
- f"{ByoSnap.TAG_CHARACTER_LIMIT} characters",
452
- SNAPCTL_INPUT_ERROR
453
- )
454
- if not self.prefix or self.prefix == '':
455
- snapctl_error("Missing prefix", SNAPCTL_INPUT_ERROR)
456
- if not self.prefix.startswith('/'):
457
- snapctl_error("Prefix should start with a forward slash (/)",
458
- SNAPCTL_INPUT_ERROR)
459
- if self.prefix.endswith('/'):
460
- snapctl_error("Prefix should not end with a forward slash (/)",
461
- SNAPCTL_INPUT_ERROR)
462
- if not self.version:
463
- snapctl_error("Missing version", SNAPCTL_INPUT_ERROR)
464
- pattern = r'^v\d+\.\d+\.\d+$'
465
- if not re.match(pattern, self.version):
466
- snapctl_error("Version should be in the format vX.X.X",
467
- SNAPCTL_INPUT_ERROR)
468
- if not self.http_port:
469
- 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",
470
542
  SNAPCTL_INPUT_ERROR)
471
- if not self.http_port.isdigit():
472
- snapctl_error("Ingress HTTP Port should be a number",
543
+ if not self.readiness_path.strip().startswith('/'):
544
+ snapctl_error("Readiness path has to start with /",
473
545
  SNAPCTL_INPUT_ERROR)
474
- if self.readiness_path is not None:
475
- if self.readiness_path.strip() == '':
476
- snapctl_error("Readiness path cannot be empty",
477
- SNAPCTL_INPUT_ERROR)
478
- if not self.readiness_path.strip().startswith('/'):
479
- snapctl_error("Readiness path has to start with /",
480
- SNAPCTL_INPUT_ERROR)
481
- if self.readiness_delay is not None:
482
- if self.readiness_delay < 0 or \
483
- self.readiness_delay > ByoSnap.MAX_READINESS_TIMEOUT:
484
- snapctl_error(
485
- "Readiness delay should be between 0 " +
486
- f"and {ByoSnap.MAX_READINESS_TIMEOUT}", SNAPCTL_INPUT_ERROR)
487
- # Check byosnap_profile path
488
- self._validate_byosnap_profile()
489
-
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)
490
606
  # CRUD methods
491
607
 
492
608
  def build(self) -> None:
@@ -509,13 +625,14 @@ class ByoSnap:
509
625
  """
510
626
  self._check_dependencies()
511
627
  self._docker_tag()
628
+ self._clean_slate()
512
629
  self._docker_login()
513
630
  self._docker_push()
514
631
 
515
632
  # Upper echelon commands
516
- def upload_docs(self) -> None:
633
+ def upload_docs(self, no_exit: bool = False) -> None:
517
634
  '''
518
- Note this step is optional hence we do not raise a typer.Exit
635
+ Note this step is optional hence we do not raise a typer.Exit
519
636
  '''
520
637
  progress = Progress(
521
638
  SpinnerColumn(),
@@ -534,23 +651,24 @@ class ByoSnap:
534
651
  swagger_file = os.path.join(base_dir, 'swagger.json')
535
652
  readme_file = os.path.join(base_dir, 'README.md')
536
653
 
654
+ # Upload swagger.json
537
655
  if os.path.isfile(swagger_file):
538
- # Push the swagger.json
539
656
  try:
540
- attachment_file = open(swagger_file, "rb")
541
- url = (
542
- f"{self.base_url}/v1/snapser-api/byosnaps/"
543
- f"{self.sid}/docs/{self.input_tag}/openapispec"
544
- )
545
- test_res = requests.post(
546
- url, files={"attachment": attachment_file},
547
- headers={'api-key': self.api_key},
548
- timeout=SERVER_CALL_TIMEOUT
549
- )
550
- if test_res.ok:
551
- success('Uploaded swagger.json')
552
- else:
553
- info('Unable to upload your swagger.json')
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')
554
672
  except RequestException as e:
555
673
  info(
556
674
  'Exception: Unable to find swagger.json at ' +
@@ -558,28 +676,28 @@ class ByoSnap:
558
676
  )
559
677
  else:
560
678
  info(
561
- f'No swagger.json found at {base_dir}' +
562
- '. Skipping swagger.json upload'
679
+ f'No swagger.json found at {
680
+ base_dir}. Skipping swagger.json upload'
563
681
  )
564
682
 
565
- # Push the README.md
683
+ # Upload README.md
566
684
  if os.path.isfile(readme_file):
567
- # Push the swagger.json
568
685
  try:
569
- attachment_file = open(readme_file, "rb")
570
- url = (
571
- f"{self.base_url}/v1/snapser-api/byosnaps/"
572
- f"{self.sid}/docs/{self.input_tag}/markdown"
573
- )
574
- test_res = requests.post(
575
- url, files={"attachment": attachment_file},
576
- headers={'api-key': self.api_key},
577
- timeout=SERVER_CALL_TIMEOUT
578
- )
579
- if test_res.ok:
580
- success('Uploaded README.md')
581
- else:
582
- info('Unable to upload your README.md')
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')
583
701
  except RequestException as e:
584
702
  info(
585
703
  'Exception: Unable to find README.md at ' +
@@ -587,14 +705,45 @@ class ByoSnap:
587
705
  )
588
706
  else:
589
707
  info(
590
- f'No README.md found at {base_dir}. Skipping README.md upload')
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)
591
741
  except RequestException as e:
592
742
  info(f'Exception: Unable to upload your API Json {str(e)}')
593
743
  finally:
594
744
  progress.stop()
595
- # snapctl_success('BYOSnap upload successful', no_exit=no_exit)
596
745
 
597
- def create(self) -> None:
746
+ def create(self, no_exit: bool = False) -> None:
598
747
  """
599
748
  Creating a new snap
600
749
  """
@@ -619,34 +768,44 @@ class ByoSnap:
619
768
  timeout=SERVER_CALL_TIMEOUT
620
769
  )
621
770
  if res.ok:
622
- snapctl_success('BYOSNAP create successful', progress)
771
+ return snapctl_success(
772
+ message='BYOSNAP create successful', progress=progress, no_exit=no_exit)
623
773
  response_json = res.json()
624
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
+ )
625
780
  if response_json['api_error_code'] == HTTP_ERROR_SERVICE_VERSION_EXISTS:
626
781
  snapctl_error(
627
- f'BYOSnap {self.name} already exists. ' +
782
+ message=f'BYOSnap {self.name} already exists. ' +
628
783
  'Please use a different name',
629
- SNAPCTL_BYOSNAP_CREATE_DUPLICATE_NAME_ERROR,
630
- progress
784
+ code=SNAPCTL_BYOSNAP_CREATE_DUPLICATE_NAME_ERROR,
785
+ progress=progress
631
786
  )
632
787
  # elif response_json['api_error_code'] == HTTP_ERROR_TAG_NOT_AVAILABLE:
633
788
  # error('Invalid tag. Please use the correct tag')
634
789
  if response_json['api_error_code'] == HTTP_ERROR_ADD_ON_NOT_ENABLED:
635
790
  snapctl_error(
636
- 'Missing Add-on. Please enable the add-on via the Snapser Web app.',
637
- SNAPCTL_BYOSNAP_CREATE_PERMISSION_ERROR, progress
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
638
794
  )
639
795
  snapctl_error(
640
- f'Server error: {json.dumps(response_json, indent=2)}',
641
- SNAPCTL_BYOSNAP_CREATE_ERROR, progress)
796
+ message=f'Server error: {json.dumps(response_json, indent=2)}',
797
+ code=SNAPCTL_BYOSNAP_CREATE_ERROR, progress=progress)
642
798
  except RequestException as e:
643
799
  snapctl_error(
644
- f"Exception: Unable to create your snap {e}",
645
- SNAPCTL_BYOSNAP_CREATE_ERROR, progress)
646
- snapctl_error('Failed to create snap',
647
- SNAPCTL_BYOSNAP_CREATE_ERROR, progress)
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)
648
807
 
649
- def publish_image(self) -> None:
808
+ def publish_image(self, no_exit: bool = False) -> None:
650
809
  """
651
810
  Publish the image
652
811
  1. Check Dependencies
@@ -662,13 +821,15 @@ class ByoSnap:
662
821
  else:
663
822
  info('--skip-build set. Skipping the build step.')
664
823
  self._docker_tag()
824
+ self._clean_slate()
665
825
  self._docker_login()
666
826
  self._docker_push()
667
827
  if self.path is not None or self.resources_path is not None:
668
- self.upload_docs()
669
- snapctl_success('BYOSNAP publish successful')
828
+ self.upload_docs(no_exit=True)
829
+ snapctl_success(
830
+ message='BYOSNAP publish successful', no_exit=no_exit)
670
831
 
671
- def publish_version(self) -> None:
832
+ def publish_version(self, no_exit: bool = False) -> None:
672
833
  """
673
834
  Publish the version
674
835
  """
@@ -689,7 +850,7 @@ class ByoSnap:
689
850
  profile_data = json.load(file)
690
851
  payload = {
691
852
  "version": self.version,
692
- "image_tag": self.input_tag,
853
+ "image_tag": self.tag,
693
854
  "base_url": f"{self.prefix}/{self.sid}",
694
855
  "http_port": self.http_port,
695
856
  "readiness_probe_config": {
@@ -706,22 +867,115 @@ class ByoSnap:
706
867
  timeout=SERVER_CALL_TIMEOUT
707
868
  )
708
869
  if res.ok:
709
- snapctl_success('BYOSNAP publish version successful', progress)
870
+ return snapctl_success(
871
+ message='BYOSNAP publish version successful',
872
+ progress=progress, no_exit=no_exit)
710
873
  response_json = res.json()
711
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
+ )
712
880
  if response_json['api_error_code'] == HTTP_ERROR_SERVICE_VERSION_EXISTS:
713
881
  snapctl_error(
714
- 'Version already exists. Please update your version and try again',
715
- SNAPCTL_BYOSNAP_PUBLISH_IMAGE_DUPLICATE_TAG_ERROR,
716
- 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
717
885
  )
718
886
  if response_json['api_error_code'] == HTTP_ERROR_TAG_NOT_AVAILABLE:
719
- snapctl_error('Invalid tag. Please use the correct tag',
720
- SNAPCTL_BYOSNAP_PUBLISH_VERSION_DUPLICATE_TAG_ERROR, progress)
721
- snapctl_error(f'Server error: {json.dumps(response_json, indent=2)}',
722
- SNAPCTL_BYOSNAP_PUBLISH_VERSION_ERROR, progress)
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)
723
894
  except RequestException as e:
724
- snapctl_error(f'Exception: Unable to publish a version for your snap {e}',
725
- SNAPCTL_BYOSNAP_PUBLISH_VERSION_ERROR, progress)
726
- snapctl_error('Failed to publish version',
727
- SNAPCTL_BYOSNAP_PUBLISH_VERSION_ERROR, progress)
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)