snapctl 0.32.0__py3-none-any.whl → 0.32.1__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 +175 -181
- snapctl/commands/byosnap.py +378 -371
- snapctl/commands/game.py +63 -57
- snapctl/commands/generate.py +48 -32
- snapctl/commands/snapend.py +361 -281
- snapctl/config/constants.py +72 -8
- snapctl/main.py +100 -135
- snapctl/types/definitions.py +20 -4
- snapctl/utils/echo.py +10 -2
- snapctl/utils/helper.py +60 -3
- {snapctl-0.32.0.dist-info → snapctl-0.32.1.dist-info}/METADATA +92 -2
- snapctl-0.32.1.dist-info/RECORD +23 -0
- snapctl-0.32.0.dist-info/RECORD +0 -23
- {snapctl-0.32.0.dist-info → snapctl-0.32.1.dist-info}/LICENSE +0 -0
- {snapctl-0.32.0.dist-info → snapctl-0.32.1.dist-info}/WHEEL +0 -0
- {snapctl-0.32.0.dist-info → snapctl-0.32.1.dist-info}/entry_points.txt +0 -0
snapctl/commands/snapend.py
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
"""
|
|
2
2
|
Snapend CLI commands
|
|
3
3
|
"""
|
|
4
|
-
from sys import platform
|
|
5
4
|
from typing import Union
|
|
6
5
|
|
|
7
6
|
import os
|
|
@@ -10,12 +9,20 @@ import time
|
|
|
10
9
|
import requests
|
|
11
10
|
from requests.exceptions import RequestException
|
|
12
11
|
|
|
12
|
+
import typer
|
|
13
13
|
from rich.progress import Progress, SpinnerColumn, TextColumn
|
|
14
|
-
from snapctl.config.constants import SERVER_CALL_TIMEOUT
|
|
14
|
+
from snapctl.config.constants import SERVER_CALL_TIMEOUT, SNAPCTL_INPUT_ERROR, \
|
|
15
|
+
SNAPCTL_SNAPEND_ENUMERATE_ERROR, SNAPCTL_SNAPEND_CLONE_SERVER_ERROR, \
|
|
16
|
+
SNAPCTL_SNAPEND_CLONE_TIMEOUT_ERROR, SNAPCTL_SUCCESS, SNAPCTL_SNAPEND_CLONE_ERROR, \
|
|
17
|
+
SNAPCTL_SNAPEND_APPLY_SERVER_ERROR, SNAPCTL_SNAPEND_APPLY_TIMEOUT_ERROR, \
|
|
18
|
+
SNAPCTL_SNAPEND_APPLY_ERROR, SNAPCTL_SNAPEND_PROMOTE_SERVER_ERROR, \
|
|
19
|
+
SNAPCTL_SNAPEND_PROMOTE_TIMEOUT_ERROR, SNAPCTL_SNAPEND_PROMOTE_ERROR, \
|
|
20
|
+
SNAPCTL_SNAPEND_DOWNLOAD_ERROR, SNAPCTL_SNAPEND_UPDATE_TIMEOUT_ERROR, \
|
|
21
|
+
SNAPCTL_SNAPEND_UPDATE_ERROR, SNAPCTL_SNAPEND_STATE_ERROR
|
|
15
22
|
from snapctl.config.hashes import CLIENT_SDK_TYPES, SERVER_SDK_TYPES, PROTOS_TYPES, \
|
|
16
23
|
SNAPEND_MANIFEST_TYPES
|
|
17
|
-
from snapctl.types.definitions import ResponseType
|
|
18
24
|
from snapctl.utils.echo import error, success, info
|
|
25
|
+
from snapctl.utils.helper import snapctl_error, snapctl_success
|
|
19
26
|
|
|
20
27
|
|
|
21
28
|
class Snapend:
|
|
@@ -77,6 +84,10 @@ class Snapend:
|
|
|
77
84
|
self.byogs_list: str | None = Snapend._make_byogs_list(
|
|
78
85
|
byogs) if byogs else None
|
|
79
86
|
self.blocking: bool = blocking
|
|
87
|
+
# Validate input
|
|
88
|
+
self.validate_input()
|
|
89
|
+
|
|
90
|
+
# Helpers
|
|
80
91
|
|
|
81
92
|
@staticmethod
|
|
82
93
|
def _get_manifest_file_name(manifest_path: str) -> str | None:
|
|
@@ -137,7 +148,7 @@ class Snapend:
|
|
|
137
148
|
'state' in cluster_object['cluster']:
|
|
138
149
|
return cluster_object['cluster']['state']
|
|
139
150
|
except RequestException as e:
|
|
140
|
-
|
|
151
|
+
info(f"Exception: Unable to get Snapend state {e}")
|
|
141
152
|
return 'INVALID'
|
|
142
153
|
|
|
143
154
|
def _blocking_get_status(self) -> bool:
|
|
@@ -152,7 +163,7 @@ class Snapend:
|
|
|
152
163
|
if current_state == 'LIVE':
|
|
153
164
|
success('Updated your snapend. Your snapend is Live.')
|
|
154
165
|
return True
|
|
155
|
-
|
|
166
|
+
info(
|
|
156
167
|
f"Update not completed successfully. Your Snapend status is {current_state}.")
|
|
157
168
|
return False
|
|
158
169
|
info(f'Current snapend state is {current_state}')
|
|
@@ -215,13 +226,10 @@ class Snapend:
|
|
|
215
226
|
f"snapser-{self.snapend_id}-{self.category}"
|
|
216
227
|
f"-{self.platform_type}-{self.auth_type}.zip"
|
|
217
228
|
)
|
|
218
|
-
file_path_symbol = '/'
|
|
219
|
-
if platform == 'win32':
|
|
220
|
-
file_path_symbol = '\\'
|
|
221
229
|
if self.out_path is not None:
|
|
222
|
-
file_save_path =
|
|
230
|
+
file_save_path = os.path.join(self.out_path, fn)
|
|
223
231
|
else:
|
|
224
|
-
file_save_path =
|
|
232
|
+
file_save_path = os.path.join(os.getcwd(), fn)
|
|
225
233
|
if res.ok:
|
|
226
234
|
content: bytes = res.content
|
|
227
235
|
with open(file_save_path, "wb") as file:
|
|
@@ -230,98 +238,105 @@ class Snapend:
|
|
|
230
238
|
json.dump(content, file, indent=4)
|
|
231
239
|
else:
|
|
232
240
|
file.write(res.content)
|
|
233
|
-
success(f"{self.category} saved at {file_save_path}")
|
|
234
241
|
return True
|
|
235
|
-
error(f'Unable to download {self.category}')
|
|
236
242
|
except RequestException as e:
|
|
237
|
-
|
|
243
|
+
info(
|
|
238
244
|
f"Exception: Unable to download {self.category}. Reason: {e}"
|
|
239
245
|
)
|
|
240
246
|
return False
|
|
241
247
|
|
|
242
|
-
|
|
248
|
+
# Validate input
|
|
249
|
+
def validate_input(self) -> None:
|
|
243
250
|
"""
|
|
244
251
|
Validator
|
|
245
252
|
"""
|
|
246
|
-
response: ResponseType = {
|
|
247
|
-
'error': True,
|
|
248
|
-
'msg': '',
|
|
249
|
-
'data': []
|
|
250
|
-
}
|
|
251
253
|
# Check API Key and Base URL
|
|
252
254
|
if not self.api_key or self.base_url == '':
|
|
253
|
-
|
|
254
|
-
return response
|
|
255
|
+
snapctl_error("Missing API Key.", SNAPCTL_INPUT_ERROR)
|
|
255
256
|
# Check subcommand
|
|
256
257
|
if not self.subcommand in Snapend.SUBCOMMANDS:
|
|
257
|
-
|
|
258
|
-
f"Invalid command. Valid commands are {', '.join(Snapend.SUBCOMMANDS)}."
|
|
259
|
-
|
|
258
|
+
snapctl_error(
|
|
259
|
+
f"Invalid command. Valid commands are {', '.join(Snapend.SUBCOMMANDS)}.",
|
|
260
|
+
SNAPCTL_INPUT_ERROR
|
|
261
|
+
)
|
|
260
262
|
if self.subcommand == 'enumerate':
|
|
261
263
|
if not self.game_id:
|
|
262
|
-
|
|
263
|
-
|
|
264
|
+
snapctl_error("Missing required parameter: game_id",
|
|
265
|
+
SNAPCTL_INPUT_ERROR)
|
|
264
266
|
elif self.subcommand == 'clone':
|
|
265
267
|
if not self.game_id:
|
|
266
|
-
|
|
267
|
-
|
|
268
|
+
snapctl_error("Missing required parameter: game_id",
|
|
269
|
+
SNAPCTL_INPUT_ERROR)
|
|
268
270
|
if not self.name:
|
|
269
|
-
|
|
270
|
-
|
|
271
|
+
snapctl_error("Missing required parameter: name",
|
|
272
|
+
SNAPCTL_INPUT_ERROR)
|
|
271
273
|
if self.env.upper() not in Snapend.ENV_TYPES:
|
|
272
|
-
|
|
274
|
+
snapctl_error(
|
|
273
275
|
"Invalid environment. Valid environments are "
|
|
274
|
-
f"{', '.join(Snapend.ENV_TYPES)}."
|
|
276
|
+
f"{', '.join(Snapend.ENV_TYPES)}.",
|
|
277
|
+
SNAPCTL_INPUT_ERROR
|
|
275
278
|
)
|
|
276
|
-
return response
|
|
277
279
|
if not self.manifest_path:
|
|
278
|
-
|
|
279
|
-
|
|
280
|
+
snapctl_error(
|
|
281
|
+
"Missing required parameter: manifest_path", SNAPCTL_INPUT_ERROR)
|
|
280
282
|
elif self.subcommand == 'apply':
|
|
281
283
|
if not self.manifest_path:
|
|
282
|
-
|
|
283
|
-
|
|
284
|
+
snapctl_error(
|
|
285
|
+
"Missing required parameter: manifest_path", SNAPCTL_INPUT_ERROR)
|
|
286
|
+
raise typer.Exit(code=SNAPCTL_INPUT_ERROR)
|
|
284
287
|
if not self.manifest_file_name:
|
|
285
|
-
|
|
286
|
-
|
|
288
|
+
snapctl_error(
|
|
289
|
+
"Invalid manifest file. Supported formats are .json, .yml, .yaml",
|
|
290
|
+
SNAPCTL_INPUT_ERROR)
|
|
287
291
|
elif self.subcommand == 'download':
|
|
292
|
+
if not self.snapend_id:
|
|
293
|
+
snapctl_error(
|
|
294
|
+
"Missing required parameter: snapend_id", SNAPCTL_INPUT_ERROR)
|
|
295
|
+
if not self.category:
|
|
296
|
+
snapctl_error(
|
|
297
|
+
"Missing required parameter: category", SNAPCTL_INPUT_ERROR)
|
|
288
298
|
if self.category not in Snapend.DOWNLOAD_CATEGORY:
|
|
289
|
-
|
|
299
|
+
snapctl_error(
|
|
290
300
|
"Invalid SDK category. Valid categories are "
|
|
291
|
-
f"{', '.join(Snapend.DOWNLOAD_CATEGORY)}."
|
|
301
|
+
f"{', '.join(Snapend.DOWNLOAD_CATEGORY)}.",
|
|
302
|
+
SNAPCTL_INPUT_ERROR
|
|
292
303
|
)
|
|
293
|
-
return response
|
|
294
304
|
if self.category not in Snapend.DOWNLOAD_TYPE_NOT_REQUIRED and \
|
|
295
305
|
(self.download_types is None or self.platform_type not in self.download_types):
|
|
296
|
-
|
|
297
|
-
return response
|
|
298
|
-
# Check file path
|
|
299
|
-
if self.out_path and not os.path.isdir(f"{self.out_path}"):
|
|
300
|
-
response['msg'] = (
|
|
301
|
-
f"Invalid path {self.out_path}. "
|
|
302
|
-
"Please enter a valid path to save your output file"
|
|
303
|
-
)
|
|
304
|
-
return response
|
|
306
|
+
snapctl_error("Invalid Download type.", SNAPCTL_INPUT_ERROR)
|
|
305
307
|
# Check the Protos category
|
|
306
308
|
if self.category == 'protos' and self.protos_category not in Snapend.PROTOS_CATEGORY:
|
|
307
|
-
|
|
309
|
+
snapctl_error(
|
|
308
310
|
"Invalid Protos category. Valid categories are "
|
|
309
|
-
f"{', '.join(Snapend.PROTOS_CATEGORY)}."
|
|
311
|
+
f"{', '.join(Snapend.PROTOS_CATEGORY)}.",
|
|
312
|
+
SNAPCTL_INPUT_ERROR
|
|
310
313
|
)
|
|
311
|
-
return response
|
|
312
314
|
# Check the auth type
|
|
313
315
|
if self.category == 'server-sdk' and self.auth_type not in Snapend.AUTH_TYPES:
|
|
314
|
-
|
|
316
|
+
snapctl_error(
|
|
315
317
|
"Invalid auth type. Valid auth types are "
|
|
316
|
-
f"{', '.join(Snapend.AUTH_TYPES)}."
|
|
318
|
+
f"{', '.join(Snapend.AUTH_TYPES)}.",
|
|
319
|
+
SNAPCTL_INPUT_ERROR
|
|
320
|
+
)
|
|
321
|
+
# Check file path
|
|
322
|
+
if not self.out_path:
|
|
323
|
+
snapctl_error("Missing required parameter: out-path",
|
|
324
|
+
SNAPCTL_INPUT_ERROR)
|
|
325
|
+
if self.out_path and not os.path.isdir(f"{self.out_path}"):
|
|
326
|
+
snapctl_error(
|
|
327
|
+
f"Invalid path {self.out_path}. "
|
|
328
|
+
"Please enter a valid path to save your output file",
|
|
329
|
+
SNAPCTL_INPUT_ERROR
|
|
317
330
|
)
|
|
318
|
-
return response
|
|
319
331
|
elif self.subcommand == 'promote':
|
|
320
332
|
if not self.snapend_id:
|
|
321
|
-
|
|
322
|
-
|
|
333
|
+
snapctl_error(
|
|
334
|
+
"Missing required parameter: snapend_id", SNAPCTL_INPUT_ERROR)
|
|
323
335
|
# Check update commands
|
|
324
336
|
elif self.subcommand == 'update':
|
|
337
|
+
if not self.snapend_id:
|
|
338
|
+
snapctl_error(
|
|
339
|
+
"Missing required parameter: snapend_id", SNAPCTL_INPUT_ERROR)
|
|
325
340
|
byosnap_present = True
|
|
326
341
|
if self.byosnap_list is None or len(self.byosnap_list) == 0:
|
|
327
342
|
byosnap_present = False
|
|
@@ -329,283 +344,348 @@ class Snapend:
|
|
|
329
344
|
if self.byogs_list is None or len(self.byogs_list) == 0:
|
|
330
345
|
byogs_present = False
|
|
331
346
|
if not byosnap_present and not byogs_present:
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
# Send success
|
|
335
|
-
response['error'] = False
|
|
336
|
-
return response
|
|
347
|
+
snapctl_error(
|
|
348
|
+
"The update command needs one of byosnaps or byogs", SNAPCTL_INPUT_ERROR)
|
|
337
349
|
|
|
338
350
|
## Subcommands ##
|
|
339
|
-
def enumerate(self) ->
|
|
351
|
+
def enumerate(self) -> None:
|
|
340
352
|
"""
|
|
341
353
|
List Snapends
|
|
342
354
|
"""
|
|
343
|
-
|
|
355
|
+
progress = Progress(
|
|
344
356
|
SpinnerColumn(),
|
|
345
357
|
TextColumn("[progress.description]{task.description}"),
|
|
346
358
|
transient=True,
|
|
347
|
-
)
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
359
|
+
)
|
|
360
|
+
progress.start()
|
|
361
|
+
progress.add_task(
|
|
362
|
+
description='Enumerating all your game snapends...', total=None)
|
|
363
|
+
try:
|
|
364
|
+
url = f"{self.base_url}/v1/snapser-api/snapends?game_id={self.game_id}"
|
|
365
|
+
res = requests.get(
|
|
366
|
+
url, headers={'api-key': self.api_key},
|
|
367
|
+
timeout=SERVER_CALL_TIMEOUT
|
|
368
|
+
)
|
|
369
|
+
response_json = res.json()
|
|
370
|
+
if res.ok and 'clusters' in response_json:
|
|
371
|
+
snapctl_success(response_json['clusters'], progress)
|
|
372
|
+
except RequestException:
|
|
373
|
+
snapctl_error("Snapctl Exception. Unable to enumerate snapends.",
|
|
374
|
+
SNAPCTL_SNAPEND_ENUMERATE_ERROR, progress)
|
|
375
|
+
snapctl_error("Unable to enumerate snapends.",
|
|
376
|
+
SNAPCTL_SNAPEND_ENUMERATE_ERROR, progress)
|
|
365
377
|
|
|
366
|
-
def clone(self) ->
|
|
378
|
+
def clone(self) -> None:
|
|
367
379
|
"""
|
|
368
380
|
Create a Snapend from a manifest
|
|
369
381
|
"""
|
|
370
|
-
|
|
382
|
+
progress = Progress(
|
|
371
383
|
SpinnerColumn(),
|
|
372
384
|
TextColumn("[progress.description]{task.description}"),
|
|
373
385
|
transient=True,
|
|
374
|
-
)
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
"You can check the status using "
|
|
422
|
-
f"`snapctl snapend state --snapend-id {response['cluster']['id']}`"
|
|
386
|
+
)
|
|
387
|
+
progress.start()
|
|
388
|
+
progress.add_task(
|
|
389
|
+
description='Cloning your snapend from the manifest...', total=None)
|
|
390
|
+
try:
|
|
391
|
+
with open(self.manifest_path, 'rb') as file:
|
|
392
|
+
files = {'snapend-manifest': file}
|
|
393
|
+
payload = {
|
|
394
|
+
'game_id': self.game_id,
|
|
395
|
+
'name': self.name,
|
|
396
|
+
'env': self.env.upper(),
|
|
397
|
+
'ext': self.manifest_file_name.split('.')[-1]
|
|
398
|
+
}
|
|
399
|
+
url = f"{self.base_url}/v1/snapser-api/snapends/snapend-manifest"
|
|
400
|
+
res = requests.post(
|
|
401
|
+
url, headers={'api-key': self.api_key},
|
|
402
|
+
files=files, data=payload, timeout=SERVER_CALL_TIMEOUT
|
|
403
|
+
)
|
|
404
|
+
if res.ok:
|
|
405
|
+
# extract the cluster ID
|
|
406
|
+
response = res.json()
|
|
407
|
+
if 'cluster' not in response or 'id' not in response['cluster']:
|
|
408
|
+
snapctl_error(
|
|
409
|
+
'Server Error. Unable to get a Snapend ID. '
|
|
410
|
+
'Please try again in sometime.',
|
|
411
|
+
SNAPCTL_SNAPEND_CLONE_SERVER_ERROR,
|
|
412
|
+
progress
|
|
413
|
+
)
|
|
414
|
+
self._assign_snapend_id(response['cluster']['id'])
|
|
415
|
+
info(
|
|
416
|
+
f"Cluster ID assigned: {response['cluster']['id']}")
|
|
417
|
+
if self.blocking:
|
|
418
|
+
status = self._blocking_get_status()
|
|
419
|
+
# Fetch the new manifest
|
|
420
|
+
if status is True:
|
|
421
|
+
# TODO: Uncomment this if we want to do an auto download
|
|
422
|
+
# self._setup_for_download(
|
|
423
|
+
# self.manifest_file_name.split('.')[-1])
|
|
424
|
+
# self._execute_download()
|
|
425
|
+
snapctl_success(
|
|
426
|
+
'Snapend clone successful. Do not forget to download the latest manifest.',
|
|
427
|
+
progress)
|
|
428
|
+
snapctl_error(
|
|
429
|
+
'Snapend clone has been initiated but the Snapend is not up yet.'
|
|
430
|
+
'Please try checking the status of the Snapend in some time',
|
|
431
|
+
SNAPCTL_SNAPEND_CLONE_TIMEOUT_ERROR,
|
|
432
|
+
progress
|
|
423
433
|
)
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
434
|
+
snapctl_success(
|
|
435
|
+
"Snapend clone has been initiated. "
|
|
436
|
+
"You can check the status using "
|
|
437
|
+
f"`snapctl snapend state --snapend-id {response['cluster']['id']}`",
|
|
438
|
+
progress
|
|
439
|
+
)
|
|
440
|
+
except RequestException as e:
|
|
441
|
+
snapctl_error(
|
|
442
|
+
f"Unable to apply the manifest snapend. {e}",
|
|
443
|
+
SNAPCTL_SNAPEND_CLONE_ERROR, progress
|
|
444
|
+
)
|
|
445
|
+
snapctl_error('Unable to apply the manifest.',
|
|
446
|
+
SNAPCTL_SNAPEND_CLONE_ERROR, progress)
|
|
429
447
|
|
|
430
|
-
def apply(self) ->
|
|
448
|
+
def apply(self) -> None:
|
|
431
449
|
"""
|
|
432
450
|
Apply a manifest
|
|
433
451
|
"""
|
|
434
|
-
|
|
452
|
+
progress = Progress(
|
|
435
453
|
SpinnerColumn(),
|
|
436
454
|
TextColumn("[progress.description]{task.description}"),
|
|
437
455
|
transient=True,
|
|
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
|
-
status = self._blocking_get_status()
|
|
463
|
-
# Fetch the new manifest
|
|
464
|
-
if status is True:
|
|
465
|
-
# TODO: Uncomment this if we want to do an auto download
|
|
466
|
-
# self._setup_for_download(
|
|
467
|
-
# self.manifest_file_name.split('.')[-1])
|
|
468
|
-
# self._execute_download()
|
|
469
|
-
info(
|
|
470
|
-
'Do not forget to download the latest manifest.'
|
|
471
|
-
)
|
|
472
|
-
return True
|
|
473
|
-
info(
|
|
474
|
-
'Snapend apply has been initiated but the Snapend is not up yet.'
|
|
475
|
-
'Please try checking the status of the Snapend in some time'
|
|
476
|
-
)
|
|
477
|
-
return False
|
|
478
|
-
info(
|
|
479
|
-
"Snapend apply has been initiated. "
|
|
480
|
-
"You can check the status using "
|
|
481
|
-
f"`snapctl snapend state --snapend-id {response['cluster']['id']}`"
|
|
456
|
+
)
|
|
457
|
+
progress.start()
|
|
458
|
+
progress.add_task(
|
|
459
|
+
description='Applying your manifest...', total=None)
|
|
460
|
+
try:
|
|
461
|
+
with open(self.manifest_path, 'rb') as file:
|
|
462
|
+
files = {'snapend-manifest': file}
|
|
463
|
+
payload = {
|
|
464
|
+
'ext': self.manifest_file_name.split('.')[-1]
|
|
465
|
+
}
|
|
466
|
+
url = f"{self.base_url}/v1/snapser-api/snapends/snapend-manifest"
|
|
467
|
+
res = requests.put(
|
|
468
|
+
url, headers={'api-key': self.api_key},
|
|
469
|
+
files=files, data=payload, timeout=SERVER_CALL_TIMEOUT
|
|
470
|
+
)
|
|
471
|
+
if res.ok:
|
|
472
|
+
# extract the cluster ID
|
|
473
|
+
response = res.json()
|
|
474
|
+
if 'cluster' not in response or 'id' not in response['cluster']:
|
|
475
|
+
snapctl_error(
|
|
476
|
+
'Server Error. Unable to get a Snapend ID. '
|
|
477
|
+
'Please try again in sometime.',
|
|
478
|
+
SNAPCTL_SNAPEND_APPLY_SERVER_ERROR,
|
|
479
|
+
progress
|
|
482
480
|
)
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
481
|
+
self._assign_snapend_id(response['cluster']['id'])
|
|
482
|
+
if self.blocking:
|
|
483
|
+
status = self._blocking_get_status()
|
|
484
|
+
# Fetch the new manifest
|
|
485
|
+
if status is True:
|
|
486
|
+
# TODO: Uncomment this if we want to do an auto download
|
|
487
|
+
# self._setup_for_download(
|
|
488
|
+
# self.manifest_file_name.split('.')[-1])
|
|
489
|
+
# self._execute_download()
|
|
490
|
+
snapctl_success(
|
|
491
|
+
'Snapend apply successful. Do not forget to download '
|
|
492
|
+
'the latest manifest.',
|
|
493
|
+
progress)
|
|
494
|
+
snapctl_error(
|
|
495
|
+
'Snapend apply has been initiated but the Snapend is not up yet.'
|
|
496
|
+
'Please try checking the status of the Snapend in some time',
|
|
497
|
+
SNAPCTL_SNAPEND_APPLY_TIMEOUT_ERROR, progress
|
|
498
|
+
)
|
|
499
|
+
snapctl_success(
|
|
500
|
+
"Snapend apply has been initiated. "
|
|
501
|
+
"You can check the status using "
|
|
502
|
+
f"`snapctl snapend state --snapend-id {response['cluster']['id']}`",
|
|
503
|
+
progress
|
|
504
|
+
)
|
|
505
|
+
except RequestException as e:
|
|
506
|
+
snapctl_error(
|
|
507
|
+
f"Unable to apply the manifest snapend. {e}",
|
|
508
|
+
SNAPCTL_SNAPEND_APPLY_ERROR, progress
|
|
509
|
+
)
|
|
510
|
+
snapctl_error(
|
|
511
|
+
'Unable to apply the manifest.', SNAPCTL_SNAPEND_APPLY_ERROR, progress)
|
|
488
512
|
|
|
489
|
-
def promote(self) ->
|
|
513
|
+
def promote(self) -> None:
|
|
490
514
|
"""
|
|
491
515
|
Promote a staging manifest to production
|
|
492
516
|
"""
|
|
493
|
-
|
|
517
|
+
progress = Progress(
|
|
494
518
|
SpinnerColumn(),
|
|
495
519
|
TextColumn("[progress.description]{task.description}"),
|
|
496
520
|
transient=True,
|
|
497
|
-
)
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
'
|
|
533
|
-
|
|
521
|
+
)
|
|
522
|
+
progress.start()
|
|
523
|
+
progress.add_task(
|
|
524
|
+
description='Promoting your staging snapend...', total=None)
|
|
525
|
+
try:
|
|
526
|
+
with open(self.manifest_path, 'rb') as file:
|
|
527
|
+
payload = {
|
|
528
|
+
'snapend_id': self.snapend_id
|
|
529
|
+
}
|
|
530
|
+
url = f"{self.base_url}/v1/snapser-api/snapends/promote"
|
|
531
|
+
res = requests.put(
|
|
532
|
+
url, headers={'api-key': self.api_key},
|
|
533
|
+
json=payload, timeout=SERVER_CALL_TIMEOUT
|
|
534
|
+
)
|
|
535
|
+
if res.ok:
|
|
536
|
+
# extract the cluster ID
|
|
537
|
+
response = res.json()
|
|
538
|
+
if 'cluster' not in response or 'id' not in response['cluster']:
|
|
539
|
+
snapctl_error(
|
|
540
|
+
'Server Error. Unable to get a Snapend ID. '
|
|
541
|
+
'Please try again in sometime.',
|
|
542
|
+
SNAPCTL_SNAPEND_PROMOTE_SERVER_ERROR,
|
|
543
|
+
progress
|
|
544
|
+
)
|
|
545
|
+
self._assign_snapend_id(response['cluster']['id'])
|
|
546
|
+
if self.blocking:
|
|
547
|
+
status = self._blocking_get_status()
|
|
548
|
+
if status is True:
|
|
549
|
+
# TODO: Uncomment this if we want to do an auto download
|
|
550
|
+
# self._setup_for_download(
|
|
551
|
+
# self.manifest_file_name.split('.')[-1])
|
|
552
|
+
# self._execute_download()
|
|
553
|
+
# Fetch the new manifest
|
|
554
|
+
snapctl_success(
|
|
555
|
+
'Snapend promote successful. Do not forget to '
|
|
556
|
+
'download the latest manifest.',
|
|
557
|
+
progress
|
|
534
558
|
)
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
559
|
+
snapctl_error(
|
|
560
|
+
'Snapend apply has been initiated but the Snapend is not up yet.'
|
|
561
|
+
'Please try checking the status of the Snapend in some time',
|
|
562
|
+
SNAPCTL_SNAPEND_PROMOTE_TIMEOUT_ERROR,
|
|
563
|
+
progress
|
|
540
564
|
)
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
565
|
+
snapctl_success(
|
|
566
|
+
"Snapend apply has been initiated. "
|
|
567
|
+
"You can check the status using "
|
|
568
|
+
f"`snapctl snapend state --snapend-id {response['cluster']['id']}`",
|
|
569
|
+
progress
|
|
570
|
+
)
|
|
571
|
+
snapctl_error(f'Unable to promote the manifest. Reason: {res.text}',
|
|
572
|
+
SNAPCTL_SNAPEND_PROMOTE_ERROR, progress)
|
|
573
|
+
except RequestException as e:
|
|
574
|
+
snapctl_error(
|
|
575
|
+
f"Unable to promote the snapend. {e}",
|
|
576
|
+
SNAPCTL_SNAPEND_PROMOTE_ERROR, progress
|
|
577
|
+
)
|
|
578
|
+
snapctl_error("Unable to promote the snapend.",
|
|
579
|
+
SNAPCTL_SNAPEND_PROMOTE_ERROR, progress)
|
|
546
580
|
|
|
547
|
-
def download(self) ->
|
|
581
|
+
def download(self) -> None:
|
|
548
582
|
"""
|
|
549
583
|
Download SDKs, Protos, Admin Settings and Configuration
|
|
550
584
|
"""
|
|
551
|
-
|
|
585
|
+
progress = Progress(
|
|
552
586
|
SpinnerColumn(),
|
|
553
587
|
TextColumn("[progress.description]{task.description}"),
|
|
554
588
|
transient=True,
|
|
555
|
-
)
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
589
|
+
)
|
|
590
|
+
progress.start()
|
|
591
|
+
progress.add_task(
|
|
592
|
+
description=f'Downloading your Custom {self.category}...', total=None)
|
|
593
|
+
try:
|
|
594
|
+
if self._execute_download():
|
|
595
|
+
snapctl_success(
|
|
596
|
+
f"Snapend download successful. {self.category} saved at {self.out_path}",
|
|
597
|
+
progress
|
|
598
|
+
)
|
|
599
|
+
except RequestException as e:
|
|
600
|
+
snapctl_error(
|
|
601
|
+
f"Unable to download {self.category}. Reason: {e}",
|
|
602
|
+
SNAPCTL_SNAPEND_DOWNLOAD_ERROR, progress
|
|
603
|
+
)
|
|
604
|
+
snapctl_error(f"Unable to download {self.category}",
|
|
605
|
+
SNAPCTL_SNAPEND_DOWNLOAD_ERROR, progress)
|
|
559
606
|
|
|
560
|
-
def update(self) ->
|
|
607
|
+
def update(self) -> None:
|
|
561
608
|
"""
|
|
562
609
|
Update a Snapend
|
|
563
610
|
"""
|
|
564
|
-
|
|
611
|
+
progress = Progress(
|
|
565
612
|
SpinnerColumn(),
|
|
566
613
|
TextColumn("[progress.description]{task.description}"),
|
|
567
614
|
transient=True,
|
|
568
|
-
)
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
615
|
+
)
|
|
616
|
+
progress.start()
|
|
617
|
+
progress.add_task(
|
|
618
|
+
description='Updating your Snapend...', total=None)
|
|
619
|
+
try:
|
|
620
|
+
payload = {
|
|
621
|
+
'byosnap_updates': self.byosnap_list,
|
|
622
|
+
'byogs_updates': self.byogs_list
|
|
623
|
+
}
|
|
624
|
+
url = f"{self.base_url}/v1/snapser-api/snapends/{self.snapend_id}"
|
|
625
|
+
res = requests.patch(
|
|
626
|
+
url, json=payload, headers={'api-key': self.api_key},
|
|
627
|
+
timeout=SERVER_CALL_TIMEOUT
|
|
628
|
+
)
|
|
629
|
+
if res.ok:
|
|
630
|
+
if self.blocking:
|
|
631
|
+
status = self._blocking_get_status()
|
|
632
|
+
# Fetch the new manifest
|
|
633
|
+
if status is True:
|
|
634
|
+
# TODO: Uncomment this if we want to do an auto download
|
|
635
|
+
# self._setup_for_download(
|
|
636
|
+
# self.manifest_file_name.split('.')[-1])
|
|
637
|
+
# self._execute_download()
|
|
638
|
+
snapctl_success(
|
|
639
|
+
'Snapend update successful. Do not forget to '
|
|
640
|
+
'download the latest manifest.',
|
|
641
|
+
progress
|
|
642
|
+
)
|
|
643
|
+
snapctl_error(
|
|
585
644
|
'Snapend update has been initiated. '
|
|
586
|
-
'You can check the status using `snapctl snapend state`'
|
|
645
|
+
'You can check the status using `snapctl snapend state`',
|
|
646
|
+
SNAPCTL_SNAPEND_UPDATE_TIMEOUT_ERROR,
|
|
647
|
+
progress
|
|
587
648
|
)
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
649
|
+
snapctl_success(
|
|
650
|
+
"Snapend update has been initiated. "
|
|
651
|
+
"You can check the status using "
|
|
652
|
+
f"`snapctl snapend state --snapend-id {self.snapend_id}`",
|
|
653
|
+
progress
|
|
654
|
+
)
|
|
655
|
+
snapctl_error(f"Unable to update the snapend. Reason: {res.text}",
|
|
656
|
+
SNAPCTL_SNAPEND_UPDATE_ERROR, progress)
|
|
657
|
+
except RequestException as e:
|
|
658
|
+
snapctl_error(
|
|
659
|
+
f"Unable to update the snapend {e}",
|
|
660
|
+
SNAPCTL_SNAPEND_UPDATE_ERROR, progress
|
|
661
|
+
)
|
|
662
|
+
snapctl_error("Unable to update the snapend.",
|
|
663
|
+
SNAPCTL_SNAPEND_UPDATE_ERROR, progress)
|
|
594
664
|
|
|
595
|
-
def state(self) ->
|
|
665
|
+
def state(self) -> None:
|
|
596
666
|
"""
|
|
597
667
|
Get the state of a Snapend
|
|
598
668
|
"""
|
|
599
|
-
|
|
669
|
+
progress = Progress(
|
|
600
670
|
SpinnerColumn(),
|
|
601
671
|
TextColumn("[progress.description]{task.description}"),
|
|
602
672
|
transient=True,
|
|
603
|
-
)
|
|
604
|
-
|
|
605
|
-
|
|
673
|
+
)
|
|
674
|
+
progress.start()
|
|
675
|
+
progress.add_task(
|
|
676
|
+
description='Getting your Snapend state...', total=None)
|
|
677
|
+
try:
|
|
606
678
|
current_state = self._get_snapend_state()
|
|
607
679
|
if current_state != 'INVALID':
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
680
|
+
snapctl_success(
|
|
681
|
+
f'Snapend get state successful. Current snapend state is: {current_state}',
|
|
682
|
+
progress)
|
|
683
|
+
snapctl_error("Unable to get the snapend state.",
|
|
684
|
+
SNAPCTL_SNAPEND_STATE_ERROR, progress)
|
|
685
|
+
except RequestException as e:
|
|
686
|
+
snapctl_error(
|
|
687
|
+
f"Unable to get your snapend state {e}",
|
|
688
|
+
SNAPCTL_SNAPEND_STATE_ERROR, progress
|
|
689
|
+
)
|
|
690
|
+
snapctl_error("Unable to get the snapend state.",
|
|
691
|
+
SNAPCTL_SNAPEND_STATE_ERROR, progress)
|