snapctl 1.0.4__py3-none-any.whl → 1.1.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/application.py +135 -0
- snapctl/commands/game.py +3 -3
- snapctl/commands/snapend.py +246 -28
- snapctl/commands/snapend_manifest.py +861 -0
- snapctl/commands/snaps.py +109 -0
- snapctl/config/constants.py +19 -2
- snapctl/data/releases/1.1.0.mdx +20 -0
- snapctl/data/releases/1.1.1.mdx +8 -0
- snapctl/main.py +181 -10
- snapctl/utils/exceptions.py +8 -0
- snapctl/utils/helper.py +10 -1
- {snapctl-1.0.4.dist-info → snapctl-1.1.1.dist-info}/METADATA +192 -35
- {snapctl-1.0.4.dist-info → snapctl-1.1.1.dist-info}/RECORD +16 -10
- {snapctl-1.0.4.dist-info → snapctl-1.1.1.dist-info}/LICENSE +0 -0
- {snapctl-1.0.4.dist-info → snapctl-1.1.1.dist-info}/WHEEL +0 -0
- {snapctl-1.0.4.dist-info → snapctl-1.1.1.dist-info}/entry_points.txt +0 -0
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Snaps CLI commands
|
|
3
|
+
"""
|
|
4
|
+
import json
|
|
5
|
+
from typing import Union
|
|
6
|
+
import requests
|
|
7
|
+
from requests.exceptions import RequestException
|
|
8
|
+
from rich.progress import Progress, SpinnerColumn, TextColumn
|
|
9
|
+
from snapctl.config.constants import SERVER_CALL_TIMEOUT, SNAPCTL_INPUT_ERROR, \
|
|
10
|
+
SNAPCTL_SNAPS_ENUMERATE_ERROR, SNAPCTL_INTERNAL_SERVER_ERROR
|
|
11
|
+
from snapctl.utils.helper import snapctl_error, snapctl_success
|
|
12
|
+
from snapctl.utils.echo import info
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class Snaps:
|
|
16
|
+
"""
|
|
17
|
+
CLI commands exposed for Snaps
|
|
18
|
+
"""
|
|
19
|
+
SUBCOMMANDS = ['enumerate']
|
|
20
|
+
|
|
21
|
+
def __init__(
|
|
22
|
+
self, *, subcommand: str, base_url: str, api_key: Union[str, None],
|
|
23
|
+
out_path_filename: Union[str, None] = None
|
|
24
|
+
) -> None:
|
|
25
|
+
self.subcommand: str = subcommand
|
|
26
|
+
self.base_url: str = base_url
|
|
27
|
+
self.api_key: Union[str, None] = api_key
|
|
28
|
+
self.out_path_filename: Union[str, None] = out_path_filename
|
|
29
|
+
# Validate input
|
|
30
|
+
self.validate_input()
|
|
31
|
+
|
|
32
|
+
def validate_input(self) -> None:
|
|
33
|
+
"""
|
|
34
|
+
Validator
|
|
35
|
+
"""
|
|
36
|
+
# Check API Key and Base URL
|
|
37
|
+
if not self.api_key or self.base_url == '':
|
|
38
|
+
snapctl_error(
|
|
39
|
+
message="Missing API Key.", code=SNAPCTL_INPUT_ERROR)
|
|
40
|
+
# Check subcommand
|
|
41
|
+
if not self.subcommand in Snaps.SUBCOMMANDS:
|
|
42
|
+
snapctl_error(
|
|
43
|
+
message="Invalid command. Valid commands are " +
|
|
44
|
+
f"{', '.join(Snaps.SUBCOMMANDS)}.",
|
|
45
|
+
code=SNAPCTL_INPUT_ERROR)
|
|
46
|
+
if self.subcommand == 'enumerate':
|
|
47
|
+
if self.out_path_filename:
|
|
48
|
+
if not (self.out_path_filename.endswith('.json')):
|
|
49
|
+
snapctl_error(
|
|
50
|
+
message="Output filename should end with .json",
|
|
51
|
+
code=SNAPCTL_INPUT_ERROR)
|
|
52
|
+
info(f"Output will be written to {self.out_path_filename}")
|
|
53
|
+
|
|
54
|
+
@staticmethod
|
|
55
|
+
def get_snaps(base_url: str, api_key: str) -> dict:
|
|
56
|
+
"""
|
|
57
|
+
Get snaps
|
|
58
|
+
"""
|
|
59
|
+
response_json = {}
|
|
60
|
+
try:
|
|
61
|
+
url = f"{base_url}/v1/snapser-api/services"
|
|
62
|
+
res = requests.get(
|
|
63
|
+
url, headers={'api-key': api_key},
|
|
64
|
+
timeout=SERVER_CALL_TIMEOUT
|
|
65
|
+
)
|
|
66
|
+
response_json = res.json()
|
|
67
|
+
except RequestException as e:
|
|
68
|
+
pass
|
|
69
|
+
return response_json
|
|
70
|
+
|
|
71
|
+
def enumerate(self) -> bool:
|
|
72
|
+
"""
|
|
73
|
+
Enumerate all snaps
|
|
74
|
+
"""
|
|
75
|
+
progress = Progress(
|
|
76
|
+
SpinnerColumn(),
|
|
77
|
+
TextColumn("[progress.description]{task.description}"),
|
|
78
|
+
transient=True,
|
|
79
|
+
)
|
|
80
|
+
progress.start()
|
|
81
|
+
progress.add_task(
|
|
82
|
+
description='Enumerating snaps...', total=None)
|
|
83
|
+
try:
|
|
84
|
+
response_json = Snaps.get_snaps(self.base_url, self.api_key)
|
|
85
|
+
if response_json == {}:
|
|
86
|
+
snapctl_error(
|
|
87
|
+
message="Something went wrong. No snaps found. Please try again in some time.",
|
|
88
|
+
code=SNAPCTL_INTERNAL_SERVER_ERROR, progress=progress)
|
|
89
|
+
if 'services' not in response_json:
|
|
90
|
+
snapctl_error(
|
|
91
|
+
message="Something went wrong. No snaps found. Please try again in some time.",
|
|
92
|
+
code=SNAPCTL_SNAPS_ENUMERATE_ERROR, progress=progress)
|
|
93
|
+
if self.out_path_filename:
|
|
94
|
+
with open(self.out_path_filename, 'w') as out_file:
|
|
95
|
+
out_file.write(json.dumps(response_json))
|
|
96
|
+
snapctl_success(
|
|
97
|
+
message=f"Output written to {self.out_path_filename}", progress=progress)
|
|
98
|
+
else:
|
|
99
|
+
snapctl_success(
|
|
100
|
+
message=response_json, progress=progress)
|
|
101
|
+
except RequestException as e:
|
|
102
|
+
snapctl_error(
|
|
103
|
+
message=f"Exception: Unable to enumerate snaps {e}",
|
|
104
|
+
code=SNAPCTL_SNAPS_ENUMERATE_ERROR, progress=progress)
|
|
105
|
+
finally:
|
|
106
|
+
progress.stop()
|
|
107
|
+
snapctl_error(
|
|
108
|
+
message='Failed to enumerate snaps.',
|
|
109
|
+
code=SNAPCTL_SNAPS_ENUMERATE_ERROR, progress=progress)
|
snapctl/config/constants.py
CHANGED
|
@@ -3,7 +3,7 @@ Constants used by snapctl
|
|
|
3
3
|
"""
|
|
4
4
|
COMPANY_NAME = 'Snapser'
|
|
5
5
|
VERSION_PREFIX = ''
|
|
6
|
-
VERSION = '1.
|
|
6
|
+
VERSION = '1.1.1'
|
|
7
7
|
CONFIG_FILE_MAC = '~/.snapser/config'
|
|
8
8
|
CONFIG_FILE_WIN = '%homepath%\\.snapser\\config'
|
|
9
9
|
|
|
@@ -25,7 +25,9 @@ HTTP_CONFLICT = 409
|
|
|
25
25
|
|
|
26
26
|
# HTTP Error codes
|
|
27
27
|
HTTP_ERROR_GAME_LIMIT_REACHED = 520
|
|
28
|
+
HTTP_ERROR_GAME_NOT_FOUND = 521
|
|
28
29
|
HTTP_ERROR_DUPLICATE_GAME_NAME = 523
|
|
30
|
+
HTTP_ERROR_CLUSTER_UPDATE_IN_PROGRESS = 535
|
|
29
31
|
HTTP_ERROR_RESOURCE_NOT_FOUND = 541
|
|
30
32
|
HTTP_ERROR_SERVICE_VERSION_EXISTS = 542
|
|
31
33
|
HTTP_ERROR_SERVICE_IN_USE = 543
|
|
@@ -38,12 +40,23 @@ SNAPCTL_SUCCESS = 0
|
|
|
38
40
|
SNAPCTL_ERROR = 1
|
|
39
41
|
SNAPCTL_INPUT_ERROR = 2
|
|
40
42
|
SNAPCTL_RESOURCE_NOT_FOUND = 3
|
|
43
|
+
SNAPCTL_INTERNAL_SERVER_ERROR = 4
|
|
41
44
|
|
|
42
|
-
# Configuration Errors - 10 -
|
|
45
|
+
# Configuration Errors - 10 - 12
|
|
43
46
|
SNAPCTL_CONFIGURATION_INCORRECT = 10
|
|
44
47
|
SNAPCTL_CONFIGURATION_ERROR = 11
|
|
45
48
|
SNAPCTL_DEPENDENCY_MISSING = 12
|
|
46
49
|
|
|
50
|
+
# Snaps Errors - 13 - 15
|
|
51
|
+
SNAPCTL_SNAPS_GENERIC_ERROR = 13
|
|
52
|
+
SNAPCTL_SNAPS_ENUMERATE_ERROR = 14
|
|
53
|
+
|
|
54
|
+
# Snapend Manifest Errors - 16 - 19
|
|
55
|
+
SNAPCTL_SNAPEND_MANIFEST_CREATE_ERROR = 16
|
|
56
|
+
SNAPCTL_SNAPEND_MANIFEST_SYNC_ERROR = 17
|
|
57
|
+
SNAPCTL_SNAPEND_MANIFEST_UPGRADE_ERROR = 18
|
|
58
|
+
SNAPCTL_SNAPEND_MANIFEST_UPDATE_ERROR = 19
|
|
59
|
+
|
|
47
60
|
# BYOGS Errors - 20 - 29
|
|
48
61
|
SNAPCTL_BYOGS_GENERIC_ERROR = 20
|
|
49
62
|
SNAPCTL_BYOGS_DEPENDENCY_MISSING = 21
|
|
@@ -108,6 +121,10 @@ SNAPCTL_SNAPEND_UPDATE_SERVER_ERROR = 73
|
|
|
108
121
|
SNAPCTL_SNAPEND_UPDATE_TIMEOUT_ERROR = 74
|
|
109
122
|
SNAPCTL_SNAPEND_STATE_ERROR = 75
|
|
110
123
|
SNAPCTL_SNAPEND_APPLY_MANIFEST_MISMATCH_ERROR = 76
|
|
124
|
+
SNAPCTL_SNAPEND_CREATE_ERROR = 77
|
|
125
|
+
SNAPCTL_SNAPEND_CREATE_SERVER_ERROR = 78
|
|
126
|
+
SNAPCTL_SNAPEND_CREATE_TIMEOUT_ERROR = 79
|
|
127
|
+
|
|
111
128
|
|
|
112
129
|
# Generate Errors - 80 - 85
|
|
113
130
|
SNAPCTL_GENERATE_GENERIC_ERROR = 80
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
## Release 1.1.0
|
|
2
|
+
##### Oct 13, 2025
|
|
3
|
+
|
|
4
|
+
### Deprecation Notice
|
|
5
|
+
1. The `Snapend` command now accepts `--application-id` for consistency with the web app. The `--game-id` parameter is still supported but will be removed in future releases.
|
|
6
|
+
2. The `Game` command has been renamed to `Application`. The old command is still supported but will be removed in future releases. Please use `snapctl application` instead of `snapctl game`.
|
|
7
|
+
|
|
8
|
+
### New Features
|
|
9
|
+
#### Snapend command
|
|
10
|
+
- Added a new `snapend create` command to create a new Snapend instance using a manifest file.
|
|
11
|
+
|
|
12
|
+
#### Snaps command
|
|
13
|
+
- Added a new `snaps enumerate` command to get a list of all available snaps for your organization, including all the metadata and dependencies.
|
|
14
|
+
|
|
15
|
+
#### Snapend Manifest commands
|
|
16
|
+
- We have added a new command `snapend-manifest` that enables you to create CI/CD pipelines for your Snapend manifests. The command supports the following subcommands:
|
|
17
|
+
- `create`: Allows you to create a brand new manifest file by telling the command which snaps you want to include.
|
|
18
|
+
- `update`: Tells the snapctl to update your manifest file with a set of snaps and features. These are synced with the manifest file you pass.
|
|
19
|
+
- `upgrade`: Tells snapctl to upgrade all the snaps in the manifest or selected snaps to their latest versions.
|
|
20
|
+
|
snapctl/main.py
CHANGED
|
@@ -8,12 +8,15 @@ from typing import Union
|
|
|
8
8
|
import typer
|
|
9
9
|
import pyfiglet
|
|
10
10
|
|
|
11
|
+
from snapctl.commands.application import Application
|
|
11
12
|
from snapctl.commands.byosnap import ByoSnap
|
|
12
13
|
from snapctl.commands.byogs import ByoGs
|
|
13
14
|
from snapctl.commands.game import Game
|
|
14
15
|
from snapctl.commands.generate import Generate
|
|
15
16
|
from snapctl.commands.snapend import Snapend
|
|
16
17
|
from snapctl.commands.byows import Byows
|
|
18
|
+
from snapctl.commands.snaps import Snaps
|
|
19
|
+
from snapctl.commands.snapend_manifest import SnapendManifest
|
|
17
20
|
from snapctl.commands.release_notes import ReleaseNotes
|
|
18
21
|
from snapctl.config.constants import COMPANY_NAME, API_KEY, URL_KEY, CONFIG_FILE_MAC, \
|
|
19
22
|
CONFIG_FILE_WIN, DEFAULT_PROFILE, VERSION, SNAPCTL_SUCCESS, CONFIG_PATH_KEY, \
|
|
@@ -21,7 +24,7 @@ from snapctl.config.constants import COMPANY_NAME, API_KEY, URL_KEY, CONFIG_FILE
|
|
|
21
24
|
from snapctl.config.endpoints import END_POINTS, GATEWAY_END_POINTS
|
|
22
25
|
from snapctl.config.hashes import PROTOS_TYPES, SERVICE_IDS, \
|
|
23
26
|
SNAPEND_MANIFEST_TYPES, SDK_TYPES
|
|
24
|
-
from snapctl.utils.echo import error, success, info
|
|
27
|
+
from snapctl.utils.echo import error, success, info, warning
|
|
25
28
|
from snapctl.utils.helper import validate_api_key
|
|
26
29
|
from snapctl.utils.telemetry import telemetry
|
|
27
30
|
|
|
@@ -504,8 +507,10 @@ def game(
|
|
|
504
507
|
),
|
|
505
508
|
) -> None:
|
|
506
509
|
"""
|
|
507
|
-
Game commands
|
|
510
|
+
Game commands - DEPRECATED: Use Application commands instead
|
|
508
511
|
"""
|
|
512
|
+
warning(
|
|
513
|
+
"Game commands have been deprecated. Please use Application commands instead.")
|
|
509
514
|
validate_command_context(ctx)
|
|
510
515
|
game_obj: Game = Game(
|
|
511
516
|
subcommand=subcommand,
|
|
@@ -518,6 +523,42 @@ def game(
|
|
|
518
523
|
raise typer.Exit(code=SNAPCTL_SUCCESS)
|
|
519
524
|
|
|
520
525
|
|
|
526
|
+
@app.command()
|
|
527
|
+
@telemetry("application", subcommand_arg="subcommand")
|
|
528
|
+
def application(
|
|
529
|
+
ctx: typer.Context,
|
|
530
|
+
# Required fields
|
|
531
|
+
subcommand: str = typer.Argument(
|
|
532
|
+
..., help="Application Subcommands: " + ", ".join(Application.SUBCOMMANDS) + "."
|
|
533
|
+
),
|
|
534
|
+
# name
|
|
535
|
+
name: str = typer.Option(
|
|
536
|
+
None, "--name",
|
|
537
|
+
help=("(req: create) Name of your application: ")
|
|
538
|
+
),
|
|
539
|
+
# overrides
|
|
540
|
+
api_key: Union[str, None] = typer.Option(
|
|
541
|
+
None, "--api-key", help="API Key override.", callback=api_key_context_callback
|
|
542
|
+
),
|
|
543
|
+
profile: Union[str, None] = typer.Option(
|
|
544
|
+
None, "--profile", help="Profile from the Snapser config to use.", callback=profile_context_callback
|
|
545
|
+
),
|
|
546
|
+
) -> None:
|
|
547
|
+
"""
|
|
548
|
+
Application commands
|
|
549
|
+
"""
|
|
550
|
+
validate_command_context(ctx)
|
|
551
|
+
application_obj: Application = Application(
|
|
552
|
+
subcommand=subcommand,
|
|
553
|
+
base_url=ctx.obj['base_url'],
|
|
554
|
+
api_key=ctx.obj['api_key'],
|
|
555
|
+
name=name
|
|
556
|
+
)
|
|
557
|
+
getattr(application_obj, subcommand.replace('-', '_'))()
|
|
558
|
+
success(f"Application {subcommand} complete")
|
|
559
|
+
raise typer.Exit(code=SNAPCTL_SUCCESS)
|
|
560
|
+
|
|
561
|
+
|
|
521
562
|
@app.command()
|
|
522
563
|
@telemetry("generate", subcommand_arg="subcommand")
|
|
523
564
|
def generate(
|
|
@@ -581,12 +622,16 @@ def snapend(
|
|
|
581
622
|
# enumerate
|
|
582
623
|
game_id: str = typer.Option(
|
|
583
624
|
None, "--game-id",
|
|
584
|
-
help="(
|
|
625
|
+
help="(DEPRECATED: Use --application-id instead) Game Id"
|
|
626
|
+
),
|
|
627
|
+
application_id: str = typer.Option(
|
|
628
|
+
None, "--application-id",
|
|
629
|
+
help="(req: enumerate, create, clone) Application Id"
|
|
585
630
|
),
|
|
586
631
|
# apply, clone
|
|
587
632
|
manifest_path_filename: str = typer.Option(
|
|
588
633
|
None, "--manifest-path-filename",
|
|
589
|
-
help="(req: apply|clone) Full Path to the manifest file including the filename."
|
|
634
|
+
help="(req: create|apply|clone) Full Path to the manifest file including the filename."
|
|
590
635
|
),
|
|
591
636
|
force: bool = typer.Option(
|
|
592
637
|
False, "--force",
|
|
@@ -627,7 +672,7 @@ def snapend(
|
|
|
627
672
|
"--http-lib " + Snapend.get_http_formats_str()
|
|
628
673
|
)
|
|
629
674
|
),
|
|
630
|
-
|
|
675
|
+
snaps_list_str: Union[str, None] = typer.Option(
|
|
631
676
|
None, "--snaps",
|
|
632
677
|
help=(
|
|
633
678
|
"(optional: download) Comma separated list of snap ids to customize the "
|
|
@@ -637,15 +682,15 @@ def snapend(
|
|
|
637
682
|
),
|
|
638
683
|
# Clone
|
|
639
684
|
name: Union[str, None] = typer.Option(
|
|
640
|
-
None, "--name", help="(req: clone) Snapend name"),
|
|
685
|
+
None, "--name", help="(req: clone, optional: create) Snapend name"),
|
|
641
686
|
env: Union[str, None] = typer.Option(
|
|
642
687
|
None, "--env", help=(
|
|
643
|
-
"(req: clone) Snapend environment"
|
|
688
|
+
"(req: clone, optional: create) Snapend environment"
|
|
644
689
|
"Environments: (" + ", ".join(Snapend.ENV_TYPES) + ")"
|
|
645
690
|
)),
|
|
646
691
|
# Download, Apply, Clone
|
|
647
692
|
out_path: Union[str, None] = typer.Option(
|
|
648
|
-
None, "--out-path", help="(optional: download|apply|clone) Path to save the output file"),
|
|
693
|
+
None, "--out-path", help="(optional: create|download|apply|clone) Path to save the output file"),
|
|
649
694
|
# update
|
|
650
695
|
byosnaps_list: str = typer.Option(
|
|
651
696
|
None, "--byosnaps",
|
|
@@ -687,7 +732,7 @@ def snapend(
|
|
|
687
732
|
api_key=ctx.obj['api_key'],
|
|
688
733
|
snapend_id=snapend_id,
|
|
689
734
|
# Enumerate, Clone
|
|
690
|
-
game_id=game_id,
|
|
735
|
+
game_id=application_id if application_id is not None else game_id,
|
|
691
736
|
# Clone
|
|
692
737
|
name=name, env=env,
|
|
693
738
|
# Apply, Clone
|
|
@@ -698,7 +743,7 @@ def snapend(
|
|
|
698
743
|
category_format=category_format,
|
|
699
744
|
category_type=category_type,
|
|
700
745
|
category_http_lib=category_http_lib,
|
|
701
|
-
snaps=
|
|
746
|
+
snaps=snaps_list_str,
|
|
702
747
|
# Download, Apply and Clone
|
|
703
748
|
out_path=out_path,
|
|
704
749
|
# Update
|
|
@@ -759,3 +804,129 @@ def byows(
|
|
|
759
804
|
getattr(byows_obj, subcommand.replace('-', '_'))()
|
|
760
805
|
success(f"BYOWs {subcommand} complete")
|
|
761
806
|
raise typer.Exit(code=SNAPCTL_SUCCESS)
|
|
807
|
+
|
|
808
|
+
|
|
809
|
+
@app.command()
|
|
810
|
+
@telemetry("snaps", subcommand_arg="subcommand")
|
|
811
|
+
def snaps(
|
|
812
|
+
ctx: typer.Context,
|
|
813
|
+
# Required fields
|
|
814
|
+
subcommand: str = typer.Argument(
|
|
815
|
+
..., help="Snaps Subcommands: " + ", ".join(Snaps.SUBCOMMANDS) + "."
|
|
816
|
+
),
|
|
817
|
+
out_path_filename: Union[str, None] = typer.Option(
|
|
818
|
+
None, "--out-path-filename", help=(
|
|
819
|
+
"(optional: enumerate) Path and filename to output the snaps list. The filename should end with .json."
|
|
820
|
+
)
|
|
821
|
+
),
|
|
822
|
+
# overrides
|
|
823
|
+
api_key: Union[str, None] = typer.Option(
|
|
824
|
+
None, "--api-key", help="API Key override.", callback=api_key_context_callback
|
|
825
|
+
),
|
|
826
|
+
profile: Union[str, None] = typer.Option(
|
|
827
|
+
None, "--profile", help="Profile from the Snapser config to use.", callback=profile_context_callback
|
|
828
|
+
),
|
|
829
|
+
) -> None:
|
|
830
|
+
"""
|
|
831
|
+
Bring your own workstation commands
|
|
832
|
+
"""
|
|
833
|
+
validate_command_context(ctx)
|
|
834
|
+
snaps_obj: Snaps = Snaps(
|
|
835
|
+
subcommand=subcommand,
|
|
836
|
+
base_url=ctx.obj['base_url'],
|
|
837
|
+
api_key=ctx.obj['api_key'],
|
|
838
|
+
out_path_filename=out_path_filename,
|
|
839
|
+
)
|
|
840
|
+
getattr(snaps_obj, subcommand.replace('-', '_'))()
|
|
841
|
+
success(f"Snaps {subcommand} complete")
|
|
842
|
+
raise typer.Exit(code=SNAPCTL_SUCCESS)
|
|
843
|
+
|
|
844
|
+
|
|
845
|
+
@app.command()
|
|
846
|
+
@telemetry("snapend_manifest", subcommand_arg="subcommand")
|
|
847
|
+
def snapend_manifest(
|
|
848
|
+
ctx: typer.Context,
|
|
849
|
+
# Required fields
|
|
850
|
+
subcommand: str = typer.Argument(
|
|
851
|
+
..., help="Snapend Manifest Subcommands: " + ", ".join(SnapendManifest.SUBCOMMANDS) + "."
|
|
852
|
+
),
|
|
853
|
+
name: Union[str, None] = typer.Option(
|
|
854
|
+
None, "--name", help="(req: create) Name for your snapend."
|
|
855
|
+
),
|
|
856
|
+
env: Union[str, None] = typer.Option(
|
|
857
|
+
None, "--env", help=(
|
|
858
|
+
"(req: create) Environment for your snapend - " +
|
|
859
|
+
", ".join(SnapendManifest.ENVIRONMENTS) + "."
|
|
860
|
+
)
|
|
861
|
+
),
|
|
862
|
+
manifest_path_filename: Union[str, None] = typer.Option(
|
|
863
|
+
None, "--manifest-path-filename", help=(
|
|
864
|
+
"(req: sync, upgrade) Full Path to the manifest file including the filename."
|
|
865
|
+
)
|
|
866
|
+
),
|
|
867
|
+
snaps_list_str: str = typer.Option(
|
|
868
|
+
None, "--snaps", help=(
|
|
869
|
+
"(use: create, sync, upgrade) Comma separated list of snap ids to add, sync or upgrade. "
|
|
870
|
+
)
|
|
871
|
+
),
|
|
872
|
+
features: str = typer.Option(
|
|
873
|
+
None, "--features", help=(
|
|
874
|
+
"(use: create, sync) Comma separated list of feature flags to add, sync. "
|
|
875
|
+
"Features: " + ", ".join(SnapendManifest.FEATURES)
|
|
876
|
+
)
|
|
877
|
+
),
|
|
878
|
+
add_snaps: str = typer.Option(
|
|
879
|
+
None, "--add-snaps", help=(
|
|
880
|
+
"(use: update) Comma separated list of snap ids to add. "
|
|
881
|
+
)
|
|
882
|
+
),
|
|
883
|
+
remove_snaps: str = typer.Option(
|
|
884
|
+
None, "--remove-snaps", help=(
|
|
885
|
+
"(use: update) Comma separated list of snap ids to remove. "
|
|
886
|
+
)
|
|
887
|
+
),
|
|
888
|
+
add_features: str = typer.Option(
|
|
889
|
+
None, "--add-features", help=(
|
|
890
|
+
"(use: update) Comma separated list of features to add. "
|
|
891
|
+
)
|
|
892
|
+
),
|
|
893
|
+
remove_features: str = typer.Option(
|
|
894
|
+
None, "--remove-features", help=(
|
|
895
|
+
"(use: update) Comma separated list of features to remove. "
|
|
896
|
+
)
|
|
897
|
+
),
|
|
898
|
+
out_path_filename: Union[str, None] = typer.Option(
|
|
899
|
+
None, "--out-path-filename", help=(
|
|
900
|
+
"(optional: enumerate) Path and filename to output the manifest. The filename should end with .json or .yaml"
|
|
901
|
+
)
|
|
902
|
+
),
|
|
903
|
+
# overrides
|
|
904
|
+
api_key: Union[str, None] = typer.Option(
|
|
905
|
+
None, "--api-key", help="API Key override.", callback=api_key_context_callback
|
|
906
|
+
),
|
|
907
|
+
profile: Union[str, None] = typer.Option(
|
|
908
|
+
None, "--profile", help="Profile from the Snapser config to use.", callback=profile_context_callback
|
|
909
|
+
),
|
|
910
|
+
) -> None:
|
|
911
|
+
"""
|
|
912
|
+
Bring your own workstation commands
|
|
913
|
+
"""
|
|
914
|
+
validate_command_context(ctx)
|
|
915
|
+
snapend_manifest_obj: SnapendManifest = SnapendManifest(
|
|
916
|
+
subcommand=subcommand,
|
|
917
|
+
base_url=ctx.obj['base_url'],
|
|
918
|
+
api_key=ctx.obj['api_key'],
|
|
919
|
+
name=name,
|
|
920
|
+
environment=env,
|
|
921
|
+
manifest_path_filename=manifest_path_filename,
|
|
922
|
+
snaps=snaps_list_str,
|
|
923
|
+
features=features,
|
|
924
|
+
add_snaps=add_snaps,
|
|
925
|
+
remove_snaps=remove_snaps,
|
|
926
|
+
add_features=add_features,
|
|
927
|
+
remove_features=remove_features,
|
|
928
|
+
out_path_filename=out_path_filename,
|
|
929
|
+
)
|
|
930
|
+
getattr(snapend_manifest_obj, subcommand.replace('-', '_'))()
|
|
931
|
+
success(f"Snapend Manifest {subcommand} complete")
|
|
932
|
+
raise typer.Exit(code=SNAPCTL_SUCCESS)
|
snapctl/utils/helper.py
CHANGED
|
@@ -3,6 +3,7 @@ Helper functions for snapctl
|
|
|
3
3
|
"""
|
|
4
4
|
from typing import Union, Dict
|
|
5
5
|
from pathlib import Path
|
|
6
|
+
from collections import Counter
|
|
6
7
|
import re
|
|
7
8
|
import platform
|
|
8
9
|
import os
|
|
@@ -49,7 +50,8 @@ def validate_api_key(base_url: str, api_key: Union[str, None]) -> bool:
|
|
|
49
50
|
raise typer.Exit(code=SNAPCTL_CONFIGURATION_ERROR)
|
|
50
51
|
|
|
51
52
|
|
|
52
|
-
def get_composite_token(
|
|
53
|
+
def get_composite_token(
|
|
54
|
+
base_url: str, api_key: Union[str, None], action: str, params: object) -> str:
|
|
53
55
|
"""
|
|
54
56
|
This function exchanges the api_key for a composite token.
|
|
55
57
|
"""
|
|
@@ -203,3 +205,10 @@ def get_config_value(environment: str, key: str) -> str:
|
|
|
203
205
|
if environment == '' or environment not in APP_CONFIG or key not in APP_CONFIG[environment]:
|
|
204
206
|
return ''
|
|
205
207
|
return APP_CONFIG[environment][key]
|
|
208
|
+
|
|
209
|
+
|
|
210
|
+
def check_duplicates_in_list(items: list[str]) -> list[str]:
|
|
211
|
+
'''
|
|
212
|
+
Check for duplicates in a list and return the duplicate items
|
|
213
|
+
'''
|
|
214
|
+
return [k for k, v in Counter(items).items() if v > 1]
|