nextmv 0.39.0.dev1__py3-none-any.whl → 1.0.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.
- nextmv/__about__.py +1 -1
- nextmv/__entrypoint__.py +1 -2
- nextmv/__init__.py +2 -4
- nextmv/cli/CONTRIBUTING.md +583 -0
- nextmv/cli/cloud/__init__.py +49 -0
- nextmv/cli/cloud/acceptance/__init__.py +27 -0
- nextmv/cli/cloud/acceptance/create.py +391 -0
- nextmv/cli/cloud/acceptance/delete.py +64 -0
- nextmv/cli/cloud/acceptance/get.py +103 -0
- nextmv/cli/cloud/acceptance/list.py +62 -0
- nextmv/cli/cloud/acceptance/update.py +95 -0
- nextmv/cli/cloud/account/__init__.py +28 -0
- nextmv/cli/cloud/account/create.py +83 -0
- nextmv/cli/cloud/account/delete.py +59 -0
- nextmv/cli/cloud/account/get.py +66 -0
- nextmv/cli/cloud/account/update.py +70 -0
- nextmv/cli/cloud/app/__init__.py +35 -0
- nextmv/cli/cloud/app/create.py +140 -0
- nextmv/cli/cloud/app/delete.py +57 -0
- nextmv/cli/cloud/app/exists.py +44 -0
- nextmv/cli/cloud/app/get.py +66 -0
- nextmv/cli/cloud/app/list.py +61 -0
- nextmv/cli/cloud/app/push.py +432 -0
- nextmv/cli/cloud/app/update.py +124 -0
- nextmv/cli/cloud/batch/__init__.py +29 -0
- nextmv/cli/cloud/batch/create.py +452 -0
- nextmv/cli/cloud/batch/delete.py +64 -0
- nextmv/cli/cloud/batch/get.py +104 -0
- nextmv/cli/cloud/batch/list.py +63 -0
- nextmv/cli/cloud/batch/metadata.py +66 -0
- nextmv/cli/cloud/batch/update.py +95 -0
- nextmv/cli/cloud/data/__init__.py +26 -0
- nextmv/cli/cloud/data/upload.py +162 -0
- nextmv/cli/cloud/ensemble/__init__.py +33 -0
- nextmv/cli/cloud/ensemble/create.py +413 -0
- nextmv/cli/cloud/ensemble/delete.py +63 -0
- nextmv/cli/cloud/ensemble/get.py +65 -0
- nextmv/cli/cloud/ensemble/list.py +63 -0
- nextmv/cli/cloud/ensemble/update.py +103 -0
- nextmv/cli/cloud/input_set/__init__.py +32 -0
- nextmv/cli/cloud/input_set/create.py +168 -0
- nextmv/cli/cloud/input_set/delete.py +64 -0
- nextmv/cli/cloud/input_set/get.py +63 -0
- nextmv/cli/cloud/input_set/list.py +63 -0
- nextmv/cli/cloud/input_set/update.py +123 -0
- nextmv/cli/cloud/instance/__init__.py +35 -0
- nextmv/cli/cloud/instance/create.py +289 -0
- nextmv/cli/cloud/instance/delete.py +61 -0
- nextmv/cli/cloud/instance/exists.py +39 -0
- nextmv/cli/cloud/instance/get.py +62 -0
- nextmv/cli/cloud/instance/list.py +60 -0
- nextmv/cli/cloud/instance/update.py +216 -0
- nextmv/cli/cloud/managed_input/__init__.py +31 -0
- nextmv/cli/cloud/managed_input/create.py +144 -0
- nextmv/cli/cloud/managed_input/delete.py +64 -0
- nextmv/cli/cloud/managed_input/get.py +63 -0
- nextmv/cli/cloud/managed_input/list.py +60 -0
- nextmv/cli/cloud/managed_input/update.py +97 -0
- nextmv/cli/cloud/run/__init__.py +37 -0
- nextmv/cli/cloud/run/cancel.py +37 -0
- nextmv/cli/cloud/run/create.py +524 -0
- nextmv/cli/cloud/run/get.py +199 -0
- nextmv/cli/cloud/run/input.py +86 -0
- nextmv/cli/cloud/run/list.py +80 -0
- nextmv/cli/cloud/run/logs.py +166 -0
- nextmv/cli/cloud/run/metadata.py +67 -0
- nextmv/cli/cloud/run/track.py +500 -0
- nextmv/cli/cloud/scenario/__init__.py +29 -0
- nextmv/cli/cloud/scenario/create.py +451 -0
- nextmv/cli/cloud/scenario/delete.py +61 -0
- nextmv/cli/cloud/scenario/get.py +102 -0
- nextmv/cli/cloud/scenario/list.py +63 -0
- nextmv/cli/cloud/scenario/metadata.py +67 -0
- nextmv/cli/cloud/scenario/update.py +93 -0
- nextmv/cli/cloud/secrets/__init__.py +33 -0
- nextmv/cli/cloud/secrets/create.py +206 -0
- nextmv/cli/cloud/secrets/delete.py +63 -0
- nextmv/cli/cloud/secrets/get.py +66 -0
- nextmv/cli/cloud/secrets/list.py +60 -0
- nextmv/cli/cloud/secrets/update.py +144 -0
- nextmv/cli/cloud/shadow/__init__.py +33 -0
- nextmv/cli/cloud/shadow/create.py +184 -0
- nextmv/cli/cloud/shadow/delete.py +64 -0
- nextmv/cli/cloud/shadow/get.py +61 -0
- nextmv/cli/cloud/shadow/list.py +63 -0
- nextmv/cli/cloud/shadow/metadata.py +66 -0
- nextmv/cli/cloud/shadow/start.py +43 -0
- nextmv/cli/cloud/shadow/stop.py +53 -0
- nextmv/cli/cloud/shadow/update.py +96 -0
- nextmv/cli/cloud/switchback/__init__.py +33 -0
- nextmv/cli/cloud/switchback/create.py +151 -0
- nextmv/cli/cloud/switchback/delete.py +64 -0
- nextmv/cli/cloud/switchback/get.py +62 -0
- nextmv/cli/cloud/switchback/list.py +63 -0
- nextmv/cli/cloud/switchback/metadata.py +68 -0
- nextmv/cli/cloud/switchback/start.py +43 -0
- nextmv/cli/cloud/switchback/stop.py +53 -0
- nextmv/cli/cloud/switchback/update.py +96 -0
- nextmv/cli/cloud/upload/__init__.py +22 -0
- nextmv/cli/cloud/upload/create.py +39 -0
- nextmv/cli/cloud/version/__init__.py +33 -0
- nextmv/cli/cloud/version/create.py +96 -0
- nextmv/cli/cloud/version/delete.py +61 -0
- nextmv/cli/cloud/version/exists.py +39 -0
- nextmv/cli/cloud/version/get.py +62 -0
- nextmv/cli/cloud/version/list.py +60 -0
- nextmv/cli/cloud/version/update.py +92 -0
- nextmv/cli/community/__init__.py +24 -0
- nextmv/cli/community/clone.py +86 -0
- nextmv/cli/community/list.py +200 -0
- nextmv/cli/configuration/__init__.py +23 -0
- nextmv/cli/configuration/config.py +228 -0
- nextmv/cli/configuration/create.py +94 -0
- nextmv/cli/configuration/delete.py +67 -0
- nextmv/cli/configuration/list.py +77 -0
- nextmv/cli/confirm.py +34 -0
- nextmv/cli/main.py +161 -3
- nextmv/cli/message.py +170 -0
- nextmv/cli/options.py +220 -0
- nextmv/cli/version.py +22 -2
- nextmv/cloud/__init__.py +17 -38
- nextmv/cloud/acceptance_test.py +20 -83
- nextmv/cloud/account.py +269 -30
- nextmv/cloud/application/__init__.py +898 -0
- nextmv/cloud/application/_acceptance.py +424 -0
- nextmv/cloud/application/_batch_scenario.py +845 -0
- nextmv/cloud/application/_ensemble.py +251 -0
- nextmv/cloud/application/_input_set.py +263 -0
- nextmv/cloud/application/_instance.py +289 -0
- nextmv/cloud/application/_managed_input.py +227 -0
- nextmv/cloud/application/_run.py +1393 -0
- nextmv/cloud/application/_secrets.py +294 -0
- nextmv/cloud/application/_shadow.py +320 -0
- nextmv/cloud/application/_switchback.py +332 -0
- nextmv/cloud/application/_utils.py +54 -0
- nextmv/cloud/application/_version.py +304 -0
- nextmv/cloud/batch_experiment.py +6 -2
- nextmv/cloud/community.py +446 -0
- nextmv/cloud/instance.py +11 -1
- nextmv/cloud/integration.py +8 -5
- nextmv/cloud/package.py +50 -9
- nextmv/cloud/shadow.py +254 -0
- nextmv/cloud/switchback.py +228 -0
- nextmv/deprecated.py +5 -3
- nextmv/input.py +20 -88
- nextmv/local/application.py +3 -15
- nextmv/local/runner.py +1 -1
- nextmv/model.py +50 -11
- nextmv/options.py +11 -256
- nextmv/output.py +0 -62
- nextmv/polling.py +54 -16
- nextmv/run.py +84 -37
- nextmv/status.py +1 -51
- {nextmv-0.39.0.dev1.dist-info → nextmv-1.0.0.dist-info}/METADATA +37 -11
- nextmv-1.0.0.dist-info/RECORD +185 -0
- nextmv-1.0.0.dist-info/entry_points.txt +2 -0
- nextmv/cloud/application.py +0 -4204
- nextmv-0.39.0.dev1.dist-info/RECORD +0 -55
- nextmv-0.39.0.dev1.dist-info/entry_points.txt +0 -2
- {nextmv-0.39.0.dev1.dist-info → nextmv-1.0.0.dist-info}/WHEEL +0 -0
- {nextmv-0.39.0.dev1.dist-info → nextmv-1.0.0.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
"""
|
|
2
|
+
This module defines the cloud scenario metadata command for the Nextmv CLI.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import json
|
|
6
|
+
from typing import Annotated
|
|
7
|
+
|
|
8
|
+
import typer
|
|
9
|
+
|
|
10
|
+
from nextmv.cli.configuration.config import build_app
|
|
11
|
+
from nextmv.cli.message import in_progress, print_json, success
|
|
12
|
+
from nextmv.cli.options import AppIDOption, ProfileOption, ScenarioTestIDOption
|
|
13
|
+
|
|
14
|
+
# Set up subcommand application.
|
|
15
|
+
app = typer.Typer()
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
@app.command()
|
|
19
|
+
def metadata(
|
|
20
|
+
app_id: AppIDOption,
|
|
21
|
+
scenario_test_id: ScenarioTestIDOption,
|
|
22
|
+
output: Annotated[
|
|
23
|
+
str | None,
|
|
24
|
+
typer.Option(
|
|
25
|
+
"--output",
|
|
26
|
+
"-o",
|
|
27
|
+
help="Saves the scenario test metadata to this location.",
|
|
28
|
+
metavar="OUTPUT_PATH",
|
|
29
|
+
),
|
|
30
|
+
] = None,
|
|
31
|
+
profile: ProfileOption = None,
|
|
32
|
+
) -> None:
|
|
33
|
+
"""
|
|
34
|
+
Get metadata for a Nextmv Cloud scenario test.
|
|
35
|
+
|
|
36
|
+
This command retrieves metadata for a specific scenario test, including
|
|
37
|
+
status, creation date, and other high-level information without the full
|
|
38
|
+
run details.
|
|
39
|
+
|
|
40
|
+
[bold][underline]Examples[/underline][/bold]
|
|
41
|
+
|
|
42
|
+
- Get metadata for scenario test [magenta]bunny-warren-optimization[/magenta] from application
|
|
43
|
+
[magenta]hare-app[/magenta].
|
|
44
|
+
$ [dim]nextmv cloud scenario metadata --app-id hare-app --scenario-test-id bunny-warren-optimization[/dim]
|
|
45
|
+
|
|
46
|
+
- Get metadata and save to a file.
|
|
47
|
+
$ [dim]nextmv cloud scenario metadata --app-id hare-app --scenario-test-id lettuce-delivery \\
|
|
48
|
+
--output metadata.json[/dim]
|
|
49
|
+
|
|
50
|
+
- Get metadata using a specific profile.
|
|
51
|
+
$ [dim]nextmv cloud scenario metadata --app-id hare-app --scenario-test-id hop-schedule --profile prod[/dim]
|
|
52
|
+
"""
|
|
53
|
+
|
|
54
|
+
cloud_app = build_app(app_id=app_id, profile=profile)
|
|
55
|
+
in_progress(msg="Getting scenario test metadata...")
|
|
56
|
+
scenario_metadata = cloud_app.scenario_test_metadata(scenario_test_id=scenario_test_id)
|
|
57
|
+
scenario_metadata_dict = scenario_metadata.to_dict()
|
|
58
|
+
|
|
59
|
+
if output is not None and output != "":
|
|
60
|
+
with open(output, "w") as f:
|
|
61
|
+
json.dump(scenario_metadata_dict, f, indent=2)
|
|
62
|
+
|
|
63
|
+
success(msg=f"Scenario test metadata saved to [magenta]{output}[/magenta].")
|
|
64
|
+
|
|
65
|
+
return
|
|
66
|
+
|
|
67
|
+
print_json(scenario_metadata_dict)
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
"""
|
|
2
|
+
This module defines the cloud scenario update command for the Nextmv CLI.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import json
|
|
6
|
+
from typing import Annotated
|
|
7
|
+
|
|
8
|
+
import typer
|
|
9
|
+
|
|
10
|
+
from nextmv.cli.configuration.config import build_app
|
|
11
|
+
from nextmv.cli.message import in_progress, print_json, success
|
|
12
|
+
from nextmv.cli.options import AppIDOption, ProfileOption, ScenarioTestIDOption
|
|
13
|
+
|
|
14
|
+
# Set up subcommand application.
|
|
15
|
+
app = typer.Typer()
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
@app.command()
|
|
19
|
+
def update(
|
|
20
|
+
app_id: AppIDOption,
|
|
21
|
+
scenario_test_id: ScenarioTestIDOption,
|
|
22
|
+
description: Annotated[
|
|
23
|
+
str | None,
|
|
24
|
+
typer.Option(
|
|
25
|
+
"--description",
|
|
26
|
+
"-d",
|
|
27
|
+
help="Updated description of the scenario test.",
|
|
28
|
+
metavar="DESCRIPTION",
|
|
29
|
+
),
|
|
30
|
+
] = None,
|
|
31
|
+
name: Annotated[
|
|
32
|
+
str | None,
|
|
33
|
+
typer.Option(
|
|
34
|
+
"--name",
|
|
35
|
+
"-n",
|
|
36
|
+
help="Updated name of the scenario test.",
|
|
37
|
+
metavar="NAME",
|
|
38
|
+
),
|
|
39
|
+
] = None,
|
|
40
|
+
output: Annotated[
|
|
41
|
+
str | None,
|
|
42
|
+
typer.Option(
|
|
43
|
+
"--output",
|
|
44
|
+
"-o",
|
|
45
|
+
help="Saves the updated scenario test information to this location.",
|
|
46
|
+
metavar="OUTPUT_PATH",
|
|
47
|
+
),
|
|
48
|
+
] = None,
|
|
49
|
+
profile: ProfileOption = None,
|
|
50
|
+
) -> None:
|
|
51
|
+
"""
|
|
52
|
+
Update a Nextmv Cloud scenario test.
|
|
53
|
+
|
|
54
|
+
Update the name and/or description of a scenario test. Any fields not
|
|
55
|
+
specified will remain unchanged.
|
|
56
|
+
|
|
57
|
+
[bold][underline]Examples[/underline][/bold]
|
|
58
|
+
|
|
59
|
+
- Update the name of a scenario test.
|
|
60
|
+
$ [dim]nextmv cloud scenario update --app-id hare-app --scenario-test-id carrot-feast \\
|
|
61
|
+
--name "Spring Carrot Harvest"[/dim]
|
|
62
|
+
|
|
63
|
+
- Update the description of a scenario test.
|
|
64
|
+
$ [dim]nextmv cloud scenario update --app-id hare-app --scenario-test-id bunny-hop-routes \\
|
|
65
|
+
--description "Optimizing hop paths through the meadow"[/dim]
|
|
66
|
+
|
|
67
|
+
- Update both name and description and save the result.
|
|
68
|
+
$ [dim]nextmv cloud scenario update --app-id hare-app --scenario-test-id lettuce-delivery \\
|
|
69
|
+
--name "Warren Lettuce Express" --description "Fast lettuce delivery to all burrows" \\
|
|
70
|
+
--output updated-scenario.json[/dim]
|
|
71
|
+
"""
|
|
72
|
+
|
|
73
|
+
cloud_app = build_app(app_id=app_id, profile=profile)
|
|
74
|
+
in_progress(msg="Updating scenario test...")
|
|
75
|
+
scenario_info = cloud_app.update_scenario_test(
|
|
76
|
+
scenario_test_id=scenario_test_id,
|
|
77
|
+
name=name,
|
|
78
|
+
description=description,
|
|
79
|
+
)
|
|
80
|
+
success(
|
|
81
|
+
f"Scenario test [magenta]{scenario_test_id}[/magenta] updated successfully "
|
|
82
|
+
f"in application [magenta]{app_id}[/magenta]."
|
|
83
|
+
)
|
|
84
|
+
scenario_info_dict = scenario_info.to_dict()
|
|
85
|
+
|
|
86
|
+
if output is not None and output != "":
|
|
87
|
+
with open(output, "w") as f:
|
|
88
|
+
json.dump(scenario_info_dict, f, indent=2)
|
|
89
|
+
|
|
90
|
+
success(msg=f"Scenario test information saved to [magenta]{output}[/magenta].")
|
|
91
|
+
return
|
|
92
|
+
|
|
93
|
+
print_json(scenario_info_dict)
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
"""
|
|
2
|
+
This module defines the cloud secrets command tree for the Nextmv CLI.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import typer
|
|
6
|
+
|
|
7
|
+
from nextmv.cli.cloud.secrets.create import app as create_app
|
|
8
|
+
from nextmv.cli.cloud.secrets.delete import app as delete_app
|
|
9
|
+
from nextmv.cli.cloud.secrets.get import app as get_app
|
|
10
|
+
from nextmv.cli.cloud.secrets.list import app as list_app
|
|
11
|
+
from nextmv.cli.cloud.secrets.update import app as update_app
|
|
12
|
+
|
|
13
|
+
# Set up subcommand application.
|
|
14
|
+
app = typer.Typer()
|
|
15
|
+
app.add_typer(create_app)
|
|
16
|
+
app.add_typer(delete_app)
|
|
17
|
+
app.add_typer(get_app)
|
|
18
|
+
app.add_typer(list_app)
|
|
19
|
+
app.add_typer(update_app)
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
@app.callback()
|
|
23
|
+
def callback() -> None:
|
|
24
|
+
"""
|
|
25
|
+
Create and manage Nextmv Cloud secrets collections.
|
|
26
|
+
|
|
27
|
+
A secret collection defines one or more secrets used by your optimization
|
|
28
|
+
model during execution. You can reference a secret collection either in an
|
|
29
|
+
application instance configuration, or directly when starting a run. The
|
|
30
|
+
platform then injects the secrets into the container during the
|
|
31
|
+
optimization run as environment variables and files.
|
|
32
|
+
"""
|
|
33
|
+
pass
|
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
"""
|
|
2
|
+
This module defines the cloud secrets create command for the Nextmv CLI.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from typing import Annotated
|
|
6
|
+
|
|
7
|
+
import typer
|
|
8
|
+
|
|
9
|
+
from nextmv.cli.configuration.config import build_app
|
|
10
|
+
from nextmv.cli.message import enum_values, error, in_progress, print_json
|
|
11
|
+
from nextmv.cli.options import AppIDOption, ProfileOption
|
|
12
|
+
from nextmv.cloud.secrets import Secret, SecretType
|
|
13
|
+
|
|
14
|
+
# Set up subcommand application.
|
|
15
|
+
app = typer.Typer()
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
@app.command()
|
|
19
|
+
def create(
|
|
20
|
+
app_id: AppIDOption,
|
|
21
|
+
secrets: Annotated[
|
|
22
|
+
list[str],
|
|
23
|
+
typer.Option(
|
|
24
|
+
"--secrets",
|
|
25
|
+
"-e",
|
|
26
|
+
help="Secrets to configure in the app. Data should be valid [magenta]json[/magenta]. "
|
|
27
|
+
"Pass multiple secrets by repeating the flag, or providing a list of objects. "
|
|
28
|
+
"Allowed values for [magenta]type[/magenta] are: "
|
|
29
|
+
f"{enum_values(SecretType)}. "
|
|
30
|
+
"Object format: [dim]{'type': type, 'location': location, 'value': value}[/dim].",
|
|
31
|
+
metavar="SECRETS",
|
|
32
|
+
),
|
|
33
|
+
],
|
|
34
|
+
description: Annotated[
|
|
35
|
+
str | None,
|
|
36
|
+
typer.Option(
|
|
37
|
+
"--description",
|
|
38
|
+
"-d",
|
|
39
|
+
help="An optional description for the secrets collection.",
|
|
40
|
+
metavar="DESCRIPTION",
|
|
41
|
+
),
|
|
42
|
+
] = None,
|
|
43
|
+
name: Annotated[
|
|
44
|
+
str | None,
|
|
45
|
+
typer.Option(
|
|
46
|
+
"--name",
|
|
47
|
+
"-n",
|
|
48
|
+
help="A name for the secrets collection.",
|
|
49
|
+
metavar="NAME",
|
|
50
|
+
),
|
|
51
|
+
] = None,
|
|
52
|
+
secrets_collection_id: Annotated[
|
|
53
|
+
str | None,
|
|
54
|
+
typer.Option(
|
|
55
|
+
"--secrets-collection-id",
|
|
56
|
+
"-s",
|
|
57
|
+
help="The ID to assign to the new secrets collection. If not provided, a random ID will be generated.",
|
|
58
|
+
envvar="NEXTMV_SECRETS_COLLECTION_ID",
|
|
59
|
+
metavar="SECRETS_COLLECTION_ID",
|
|
60
|
+
),
|
|
61
|
+
] = None,
|
|
62
|
+
profile: ProfileOption = None,
|
|
63
|
+
) -> None:
|
|
64
|
+
"""
|
|
65
|
+
Create a new Nextmv Cloud secrets collection.
|
|
66
|
+
|
|
67
|
+
A secrets collection is a group of key-value pairs that can be used by
|
|
68
|
+
your application instances during execution. Each collection can contain
|
|
69
|
+
up to 20 secrets. Secrets are provided as JSON objects using the
|
|
70
|
+
--secrets flag.
|
|
71
|
+
|
|
72
|
+
Each secret must include three fields:
|
|
73
|
+
- [magenta]type[/magenta]: Either [magenta]env[/magenta] or [magenta]file[/magenta],
|
|
74
|
+
which determines how the secret is injected into the runtime.
|
|
75
|
+
- [magenta]location[/magenta]: Where to place the secret.
|
|
76
|
+
- [magenta]env[/magenta]: the environment variable name. E.g.: [magenta]BURROW_ENTRANCE[/magenta].
|
|
77
|
+
- [magenta]file[/magenta]: the relative path from the execution
|
|
78
|
+
directory. E.g.: [magenta]licenses/burrow.entr[/magenta].
|
|
79
|
+
- [magenta]value[/magenta]: The secret value as text (limited to 1 KB).
|
|
80
|
+
|
|
81
|
+
You can provide secrets in three ways:
|
|
82
|
+
- A single secret as a [magenta]json[/magenta] object.
|
|
83
|
+
- Multiple secrets by repeating the --secrets flag.
|
|
84
|
+
- Multiple secrets as a [magenta]json[/magenta] array in a single --secrets flag.
|
|
85
|
+
|
|
86
|
+
The --secrets-collection-id and --name are optional.
|
|
87
|
+
If not provided, they will be automatically generated.
|
|
88
|
+
|
|
89
|
+
[bold][underline]Examples[/underline][/bold]
|
|
90
|
+
|
|
91
|
+
- Create a secrets collection with a single environment variable secret.
|
|
92
|
+
$ [dim]nextmv cloud secrets create --app-id hare-app \\
|
|
93
|
+
--secrets '{"type": "env", "location": "API_KEY", "value": "secret-value"}'[/dim]
|
|
94
|
+
|
|
95
|
+
- Create a secrets collection with multiple secrets by repeating the flag.
|
|
96
|
+
$ [dim]nextmv cloud secrets create --app-id hare-app \\
|
|
97
|
+
--secrets '{"type": "env", "location": "API_KEY", "value": "secret-value"}' \\
|
|
98
|
+
--secrets '{"type": "env", "location": "DATABASE_URL", "value": "postgres://localhost"}'[/dim]
|
|
99
|
+
|
|
100
|
+
- Create a secrets collection with multiple secrets in a single JSON array.
|
|
101
|
+
$ [dim]nextmv cloud secrets create --app-id hare-app \\
|
|
102
|
+
--secrets '[{"type": "env", "location": "DB_USER", "value": "admin"}, {...}]'[/dim]
|
|
103
|
+
|
|
104
|
+
- Create a secrets collection with custom ID, name, and description.
|
|
105
|
+
$ [dim]nextmv cloud secrets create --app-id hare-app \\
|
|
106
|
+
--secrets-collection-id db-creds --name "Database Credentials" \\
|
|
107
|
+
--description "Production database credentials" \\
|
|
108
|
+
--secrets '{"type": "env", "location": "DB_USER", "value": "admin"}' \\
|
|
109
|
+
--secrets '{"type": "env", "location": "DB_PASS", "value": "secure123"}'[/dim]
|
|
110
|
+
|
|
111
|
+
- Create a secrets collection with file-based secrets.
|
|
112
|
+
$ [dim]nextmv cloud secrets create --app-id hare-app \\
|
|
113
|
+
--secrets-collection-id certs --name "Certificates" \\
|
|
114
|
+
--secrets '{"type": "file", "location": "licenses/acme.lic", "value": "LICENSE_CONTENT_HERE"}'[/dim]
|
|
115
|
+
|
|
116
|
+
- Mix environment and file-based secrets.
|
|
117
|
+
$ [dim]nextmv cloud secrets create --app-id hare-app \\
|
|
118
|
+
--secrets '{"type": "env", "location": "ACME_LICENSE_KEY", "value": "abc123"}' \\
|
|
119
|
+
--secrets '{"type": "file", "location": "config/app.conf", "value": "server=prod\\nport=8080"}'[/dim]
|
|
120
|
+
"""
|
|
121
|
+
|
|
122
|
+
cloud_app = build_app(app_id=app_id, profile=profile)
|
|
123
|
+
in_progress(msg="Creating secrets collection...")
|
|
124
|
+
|
|
125
|
+
# Build the secrets list from the CLI options
|
|
126
|
+
secrets_list = build_secrets(secrets)
|
|
127
|
+
|
|
128
|
+
collection = cloud_app.new_secrets_collection(
|
|
129
|
+
secrets=secrets_list,
|
|
130
|
+
id=secrets_collection_id,
|
|
131
|
+
name=name,
|
|
132
|
+
description=description,
|
|
133
|
+
)
|
|
134
|
+
print_json(collection.to_dict())
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
def build_secrets(secrets: list[str]) -> list[Secret]:
|
|
138
|
+
"""
|
|
139
|
+
Builds the secrets list from the CLI option(s).
|
|
140
|
+
|
|
141
|
+
Parameters
|
|
142
|
+
----------
|
|
143
|
+
secrets : list[str]
|
|
144
|
+
List of secrets provided via the CLI.
|
|
145
|
+
|
|
146
|
+
Returns
|
|
147
|
+
-------
|
|
148
|
+
list[Secret]
|
|
149
|
+
The built secrets list.
|
|
150
|
+
"""
|
|
151
|
+
import json
|
|
152
|
+
|
|
153
|
+
secrets_list = []
|
|
154
|
+
|
|
155
|
+
for secret_str in secrets:
|
|
156
|
+
try:
|
|
157
|
+
secret_data = json.loads(secret_str)
|
|
158
|
+
|
|
159
|
+
# Handle the case where the value is a list of secrets.
|
|
160
|
+
if isinstance(secret_data, list):
|
|
161
|
+
for ix, item in enumerate(secret_data):
|
|
162
|
+
if item.get("type") is None or item.get("location") is None or item.get("value") is None:
|
|
163
|
+
error(
|
|
164
|
+
f"Invalid secret format at index [magenta]{ix}[/magenta] in "
|
|
165
|
+
f"[magenta]{secret_str}[/magenta]. Each secret must have "
|
|
166
|
+
"[magenta]type[/magenta], [magenta]location[/magenta], "
|
|
167
|
+
"and [magenta]value[/magenta] fields."
|
|
168
|
+
)
|
|
169
|
+
|
|
170
|
+
secret = Secret(
|
|
171
|
+
type=SecretType(item["type"]),
|
|
172
|
+
location=item["location"],
|
|
173
|
+
value=item["value"],
|
|
174
|
+
)
|
|
175
|
+
secrets_list.append(secret)
|
|
176
|
+
|
|
177
|
+
# Handle the case where the value is a single secret.
|
|
178
|
+
elif isinstance(secret_data, dict):
|
|
179
|
+
if (
|
|
180
|
+
secret_data.get("type") is None
|
|
181
|
+
or secret_data.get("location") is None
|
|
182
|
+
or secret_data.get("value") is None
|
|
183
|
+
):
|
|
184
|
+
error(
|
|
185
|
+
f"Invalid secret format in [magenta]{secret_str}[/magenta]. "
|
|
186
|
+
"Each secret must have [magenta]type[/magenta], [magenta]location[/magenta], "
|
|
187
|
+
"and [magenta]value[/magenta] fields."
|
|
188
|
+
)
|
|
189
|
+
|
|
190
|
+
secret = Secret(
|
|
191
|
+
type=SecretType(secret_data["type"]),
|
|
192
|
+
location=secret_data["location"],
|
|
193
|
+
value=secret_data["value"],
|
|
194
|
+
)
|
|
195
|
+
secrets_list.append(secret)
|
|
196
|
+
|
|
197
|
+
else:
|
|
198
|
+
error(
|
|
199
|
+
f"Invalid secret format: [magenta]{secret_str}[/magenta]. "
|
|
200
|
+
"Expected [magenta]json[/magenta] object or array."
|
|
201
|
+
)
|
|
202
|
+
|
|
203
|
+
except (json.JSONDecodeError, KeyError, ValueError) as e:
|
|
204
|
+
error(f"Invalid secret format: [magenta]{secret_str}[/magenta]. Error: {e}")
|
|
205
|
+
|
|
206
|
+
return secrets_list
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
"""
|
|
2
|
+
This module defines the cloud secrets delete command for the Nextmv CLI.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from typing import Annotated
|
|
6
|
+
|
|
7
|
+
import typer
|
|
8
|
+
|
|
9
|
+
from nextmv.cli.configuration.config import build_app
|
|
10
|
+
from nextmv.cli.confirm import get_confirmation
|
|
11
|
+
from nextmv.cli.message import info, success
|
|
12
|
+
from nextmv.cli.options import AppIDOption, ProfileOption, SecretsCollectionIDOption
|
|
13
|
+
|
|
14
|
+
# Set up subcommand application.
|
|
15
|
+
app = typer.Typer()
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
@app.command()
|
|
19
|
+
def delete(
|
|
20
|
+
app_id: AppIDOption,
|
|
21
|
+
secrets_collection_id: SecretsCollectionIDOption,
|
|
22
|
+
yes: Annotated[
|
|
23
|
+
bool,
|
|
24
|
+
typer.Option(
|
|
25
|
+
"--yes",
|
|
26
|
+
"-y",
|
|
27
|
+
help="Agree to deletion confirmation prompt. Useful for non-interactive sessions.",
|
|
28
|
+
),
|
|
29
|
+
] = False,
|
|
30
|
+
profile: ProfileOption = None,
|
|
31
|
+
) -> None:
|
|
32
|
+
"""
|
|
33
|
+
Deletes a Nextmv Cloud secrets collection.
|
|
34
|
+
|
|
35
|
+
This action is permanent and cannot be undone. Use the --yes
|
|
36
|
+
flag to skip the confirmation prompt.
|
|
37
|
+
|
|
38
|
+
[bold][underline]Examples[/underline][/bold]
|
|
39
|
+
|
|
40
|
+
- Delete the secrets collection with the ID [magenta]api-keys[/magenta] from application
|
|
41
|
+
[magenta]hare-app[/magenta].
|
|
42
|
+
$ [dim]nextmv cloud secrets delete --app-id hare-app --secrets-collection-id api-keys[/dim]
|
|
43
|
+
|
|
44
|
+
- Delete the secrets collection without confirmation prompt.
|
|
45
|
+
$ [dim]nextmv cloud secrets delete --app-id hare-app --secrets-collection-id api-keys --yes[/dim]
|
|
46
|
+
"""
|
|
47
|
+
|
|
48
|
+
if not yes:
|
|
49
|
+
confirm = get_confirmation(
|
|
50
|
+
f"Are you sure you want to delete secrets collection [magenta]{secrets_collection_id}[/magenta] "
|
|
51
|
+
f"from application [magenta]{app_id}[/magenta]? This action cannot be undone.",
|
|
52
|
+
)
|
|
53
|
+
|
|
54
|
+
if not confirm:
|
|
55
|
+
info(f"Secrets collection [magenta]{secrets_collection_id}[/magenta] will not be deleted.")
|
|
56
|
+
return
|
|
57
|
+
|
|
58
|
+
cloud_app = build_app(app_id=app_id, profile=profile)
|
|
59
|
+
cloud_app.delete_secrets_collection(secrets_collection_id=secrets_collection_id)
|
|
60
|
+
success(
|
|
61
|
+
f"Secrets collection [magenta]{secrets_collection_id}[/magenta] deleted successfully "
|
|
62
|
+
f"from application [magenta]{app_id}[/magenta]."
|
|
63
|
+
)
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
"""
|
|
2
|
+
This module defines the cloud secrets get command for the Nextmv CLI.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import json
|
|
6
|
+
from typing import Annotated
|
|
7
|
+
|
|
8
|
+
import typer
|
|
9
|
+
|
|
10
|
+
from nextmv.cli.configuration.config import build_app
|
|
11
|
+
from nextmv.cli.message import in_progress, print_json, success
|
|
12
|
+
from nextmv.cli.options import AppIDOption, ProfileOption, SecretsCollectionIDOption
|
|
13
|
+
|
|
14
|
+
# Set up subcommand application.
|
|
15
|
+
app = typer.Typer()
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
@app.command()
|
|
19
|
+
def get(
|
|
20
|
+
app_id: AppIDOption,
|
|
21
|
+
secrets_collection_id: SecretsCollectionIDOption,
|
|
22
|
+
output: Annotated[
|
|
23
|
+
str | None,
|
|
24
|
+
typer.Option(
|
|
25
|
+
"--output",
|
|
26
|
+
"-o",
|
|
27
|
+
help="Saves the secrets collection information to this location.",
|
|
28
|
+
metavar="OUTPUT_PATH",
|
|
29
|
+
),
|
|
30
|
+
] = None,
|
|
31
|
+
profile: ProfileOption = None,
|
|
32
|
+
) -> None:
|
|
33
|
+
"""
|
|
34
|
+
Get a Nextmv Cloud secrets collection.
|
|
35
|
+
|
|
36
|
+
This command is useful to get the attributes of an existing Nextmv Cloud
|
|
37
|
+
secrets collection by its ID. :construction: [yellow bold]Warning:
|
|
38
|
+
secret values will be included in the output.[/yellow bold]
|
|
39
|
+
|
|
40
|
+
[bold][underline]Examples[/underline][/bold]
|
|
41
|
+
|
|
42
|
+
- Get the secrets collection with the ID [magenta]api-keys[/magenta] from
|
|
43
|
+
application [magenta]hare-app[/magenta].
|
|
44
|
+
$ [dim]nextmv cloud secrets get --app-id hare-app \\
|
|
45
|
+
--secrets-collection-id api-keys[/dim]
|
|
46
|
+
|
|
47
|
+
- Get the secrets collection with the ID [magenta]api-keys[/magenta] and
|
|
48
|
+
save the information to a [magenta]secrets.json[/magenta] file.
|
|
49
|
+
$ [dim]nextmv cloud secrets get --app-id hare-app \\
|
|
50
|
+
--secrets-collection-id api-keys --output secrets.json[/dim]
|
|
51
|
+
"""
|
|
52
|
+
|
|
53
|
+
cloud_app = build_app(app_id=app_id, profile=profile)
|
|
54
|
+
in_progress(msg="Getting secrets collection...")
|
|
55
|
+
collection = cloud_app.secrets_collection(secrets_collection_id=secrets_collection_id)
|
|
56
|
+
collection_dict = collection.to_dict()
|
|
57
|
+
|
|
58
|
+
if output is not None and output != "":
|
|
59
|
+
with open(output, "w") as f:
|
|
60
|
+
json.dump(collection_dict, f, indent=2)
|
|
61
|
+
|
|
62
|
+
success(msg=f"Secrets collection information saved to [magenta]{output}[/magenta].")
|
|
63
|
+
|
|
64
|
+
return
|
|
65
|
+
|
|
66
|
+
print_json(collection_dict)
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
"""
|
|
2
|
+
This module defines the cloud secrets list command for the Nextmv CLI.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import json
|
|
6
|
+
from typing import Annotated
|
|
7
|
+
|
|
8
|
+
import typer
|
|
9
|
+
|
|
10
|
+
from nextmv.cli.configuration.config import build_app
|
|
11
|
+
from nextmv.cli.message import in_progress, print_json, success
|
|
12
|
+
from nextmv.cli.options import AppIDOption, ProfileOption
|
|
13
|
+
|
|
14
|
+
# Set up subcommand application.
|
|
15
|
+
app = typer.Typer()
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
@app.command()
|
|
19
|
+
def list(
|
|
20
|
+
app_id: AppIDOption,
|
|
21
|
+
output: Annotated[
|
|
22
|
+
str | None,
|
|
23
|
+
typer.Option(
|
|
24
|
+
"--output",
|
|
25
|
+
"-o",
|
|
26
|
+
help="Saves the secrets collections list information to this location.",
|
|
27
|
+
metavar="OUTPUT_PATH",
|
|
28
|
+
),
|
|
29
|
+
] = None,
|
|
30
|
+
profile: ProfileOption = None,
|
|
31
|
+
) -> None:
|
|
32
|
+
"""
|
|
33
|
+
List all secrets collections of a Nextmv Cloud application.
|
|
34
|
+
|
|
35
|
+
[bold][underline]Examples[/underline][/bold]
|
|
36
|
+
|
|
37
|
+
- List all secrets collections of application [magenta]hare-app[/magenta].
|
|
38
|
+
$ [dim]nextmv cloud secrets list --app-id hare-app[/dim]
|
|
39
|
+
|
|
40
|
+
- List all secrets collections using the profile named [magenta]hare[/magenta].
|
|
41
|
+
$ [dim]nextmv cloud secrets list --app-id hare-app --profile hare[/dim]
|
|
42
|
+
|
|
43
|
+
- List all secrets collections and save the information to a [magenta]secrets.json[/magenta] file.
|
|
44
|
+
$ [dim]nextmv cloud secrets list --app-id hare-app --output secrets.json[/dim]
|
|
45
|
+
"""
|
|
46
|
+
|
|
47
|
+
cloud_app = build_app(app_id=app_id, profile=profile)
|
|
48
|
+
in_progress(msg="Listing secrets collections...")
|
|
49
|
+
collections = cloud_app.list_secrets_collections()
|
|
50
|
+
collections_dicts = [collection.to_dict() for collection in collections]
|
|
51
|
+
|
|
52
|
+
if output is not None and output != "":
|
|
53
|
+
with open(output, "w") as f:
|
|
54
|
+
json.dump(collections_dicts, f, indent=2)
|
|
55
|
+
|
|
56
|
+
success(msg=f"Secrets collections list information saved to [magenta]{output}[/magenta].")
|
|
57
|
+
|
|
58
|
+
return
|
|
59
|
+
|
|
60
|
+
print_json(collections_dicts)
|