edge-containers-cli 4.3.1__tar.gz → 4.4.0__tar.gz

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.
Files changed (109) hide show
  1. {edge_containers_cli-4.3.1 → edge_containers_cli-4.4.0}/PKG-INFO +3 -2
  2. {edge_containers_cli-4.3.1 → edge_containers_cli-4.4.0}/pyproject.toml +1 -1
  3. {edge_containers_cli-4.3.1 → edge_containers_cli-4.4.0}/src/edge_containers_cli/__main__.py +28 -20
  4. {edge_containers_cli-4.3.1 → edge_containers_cli-4.4.0}/src/edge_containers_cli/_version.py +2 -2
  5. {edge_containers_cli-4.3.1 → edge_containers_cli-4.4.0}/src/edge_containers_cli/autocomplete.py +4 -1
  6. {edge_containers_cli-4.3.1 → edge_containers_cli-4.4.0}/src/edge_containers_cli/backend.py +3 -0
  7. {edge_containers_cli-4.3.1 → edge_containers_cli-4.4.0}/src/edge_containers_cli/cli.py +93 -32
  8. {edge_containers_cli-4.3.1 → edge_containers_cli-4.4.0}/src/edge_containers_cli/cmds/argo_commands.py +72 -12
  9. {edge_containers_cli-4.3.1 → edge_containers_cli-4.4.0}/src/edge_containers_cli/cmds/commands.py +44 -3
  10. {edge_containers_cli-4.3.1 → edge_containers_cli-4.4.0}/src/edge_containers_cli/cmds/helm.py +9 -2
  11. {edge_containers_cli-4.3.1 → edge_containers_cli-4.4.0}/src/edge_containers_cli/cmds/k8s_commands.py +14 -6
  12. edge_containers_cli-4.4.0/src/edge_containers_cli/git.py +236 -0
  13. {edge_containers_cli-4.3.1 → edge_containers_cli-4.4.0}/src/edge_containers_cli/utils.py +62 -16
  14. {edge_containers_cli-4.3.1 → edge_containers_cli-4.4.0}/src/edge_containers_cli.egg-info/PKG-INFO +3 -2
  15. edge_containers_cli-4.4.0/src/edge_containers_cli.egg-info/entry_points.txt +2 -0
  16. {edge_containers_cli-4.3.1 → edge_containers_cli-4.4.0}/tests/conftest.py +4 -1
  17. {edge_containers_cli-4.3.1 → edge_containers_cli-4.4.0}/tests/data/argocd.yaml +65 -0
  18. edge_containers_cli-4.4.0/tests/data/cli.yaml +50 -0
  19. {edge_containers_cli-4.3.1 → edge_containers_cli-4.4.0}/tests/data/k8s.yaml +19 -2
  20. {edge_containers_cli-4.3.1 → edge_containers_cli-4.4.0}/tests/data/yaml.yaml +1 -0
  21. {edge_containers_cli-4.3.1 → edge_containers_cli-4.4.0}/tests/test_argocd.py +15 -0
  22. {edge_containers_cli-4.3.1 → edge_containers_cli-4.4.0}/tests/test_cli.py +4 -4
  23. {edge_containers_cli-4.3.1 → edge_containers_cli-4.4.0}/tests/test_k8s.py +1 -1
  24. {edge_containers_cli-4.3.1 → edge_containers_cli-4.4.0}/tests/test_unit.py +21 -3
  25. edge_containers_cli-4.3.1/src/edge_containers_cli/git.py +0 -182
  26. edge_containers_cli-4.3.1/src/edge_containers_cli.egg-info/entry_points.txt +0 -2
  27. edge_containers_cli-4.3.1/tests/data/cli.yaml +0 -24
  28. {edge_containers_cli-4.3.1 → edge_containers_cli-4.4.0}/.copier-answers.yml +0 -0
  29. {edge_containers_cli-4.3.1 → edge_containers_cli-4.4.0}/.devcontainer/devcontainer.json +0 -0
  30. {edge_containers_cli-4.3.1 → edge_containers_cli-4.4.0}/.github/CONTRIBUTING.md +0 -0
  31. {edge_containers_cli-4.3.1 → edge_containers_cli-4.4.0}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
  32. {edge_containers_cli-4.3.1 → edge_containers_cli-4.4.0}/.github/ISSUE_TEMPLATE/issue.md +0 -0
  33. {edge_containers_cli-4.3.1 → edge_containers_cli-4.4.0}/.github/PULL_REQUEST_TEMPLATE/pull_request_template.md +0 -0
  34. {edge_containers_cli-4.3.1 → edge_containers_cli-4.4.0}/.github/actions/install_requirements/action.yml +0 -0
  35. {edge_containers_cli-4.3.1 → edge_containers_cli-4.4.0}/.github/dependabot.yml +0 -0
  36. {edge_containers_cli-4.3.1 → edge_containers_cli-4.4.0}/.github/pages/index.html +0 -0
  37. {edge_containers_cli-4.3.1 → edge_containers_cli-4.4.0}/.github/pages/make_switcher.py +0 -0
  38. {edge_containers_cli-4.3.1 → edge_containers_cli-4.4.0}/.github/workflows/_check.yml +0 -0
  39. {edge_containers_cli-4.3.1 → edge_containers_cli-4.4.0}/.github/workflows/_dist.yml +0 -0
  40. {edge_containers_cli-4.3.1 → edge_containers_cli-4.4.0}/.github/workflows/_pypi.yml +0 -0
  41. {edge_containers_cli-4.3.1 → edge_containers_cli-4.4.0}/.github/workflows/_release.yml +0 -0
  42. {edge_containers_cli-4.3.1 → edge_containers_cli-4.4.0}/.github/workflows/_test.yml +0 -0
  43. {edge_containers_cli-4.3.1 → edge_containers_cli-4.4.0}/.github/workflows/_tox.yml +0 -0
  44. {edge_containers_cli-4.3.1 → edge_containers_cli-4.4.0}/.github/workflows/ci.yml +0 -0
  45. {edge_containers_cli-4.3.1 → edge_containers_cli-4.4.0}/.gitignore +0 -0
  46. {edge_containers_cli-4.3.1 → edge_containers_cli-4.4.0}/.pre-commit-config.yaml +0 -0
  47. {edge_containers_cli-4.3.1 → edge_containers_cli-4.4.0}/.vscode/extensions.json +0 -0
  48. {edge_containers_cli-4.3.1 → edge_containers_cli-4.4.0}/.vscode/launch.json +0 -0
  49. {edge_containers_cli-4.3.1 → edge_containers_cli-4.4.0}/.vscode/settings.json +0 -0
  50. {edge_containers_cli-4.3.1 → edge_containers_cli-4.4.0}/.vscode/tasks.json +0 -0
  51. {edge_containers_cli-4.3.1 → edge_containers_cli-4.4.0}/Dockerfile +0 -0
  52. {edge_containers_cli-4.3.1 → edge_containers_cli-4.4.0}/LICENSE +0 -0
  53. {edge_containers_cli-4.3.1 → edge_containers_cli-4.4.0}/README.md +0 -0
  54. {edge_containers_cli-4.3.1 → edge_containers_cli-4.4.0}/docs/conf.py +0 -0
  55. {edge_containers_cli-4.3.1 → edge_containers_cli-4.4.0}/docs/developer/explanations/decisions/0001-record-architecture-decisions.rst +0 -0
  56. {edge_containers_cli-4.3.1 → edge_containers_cli-4.4.0}/docs/developer/explanations/decisions/0002-switched-to-pip-skeleton.rst +0 -0
  57. {edge_containers_cli-4.3.1 → edge_containers_cli-4.4.0}/docs/developer/explanations/decisions.rst +0 -0
  58. {edge_containers_cli-4.3.1 → edge_containers_cli-4.4.0}/docs/developer/how-to/build-docs.rst +0 -0
  59. {edge_containers_cli-4.3.1 → edge_containers_cli-4.4.0}/docs/developer/how-to/contribute.rst +0 -0
  60. {edge_containers_cli-4.3.1 → edge_containers_cli-4.4.0}/docs/developer/how-to/lint.rst +0 -0
  61. {edge_containers_cli-4.3.1 → edge_containers_cli-4.4.0}/docs/developer/how-to/make-release.rst +0 -0
  62. {edge_containers_cli-4.3.1 → edge_containers_cli-4.4.0}/docs/developer/how-to/pin-requirements.rst +0 -0
  63. {edge_containers_cli-4.3.1 → edge_containers_cli-4.4.0}/docs/developer/how-to/run-tests.rst +0 -0
  64. {edge_containers_cli-4.3.1 → edge_containers_cli-4.4.0}/docs/developer/how-to/static-analysis.rst +0 -0
  65. {edge_containers_cli-4.3.1 → edge_containers_cli-4.4.0}/docs/developer/how-to/test-container.rst +0 -0
  66. {edge_containers_cli-4.3.1 → edge_containers_cli-4.4.0}/docs/developer/how-to/update-tools.rst +0 -0
  67. {edge_containers_cli-4.3.1 → edge_containers_cli-4.4.0}/docs/developer/index.rst +0 -0
  68. {edge_containers_cli-4.3.1 → edge_containers_cli-4.4.0}/docs/developer/reference/standards.rst +0 -0
  69. {edge_containers_cli-4.3.1 → edge_containers_cli-4.4.0}/docs/developer/tutorials/dev-install.rst +0 -0
  70. {edge_containers_cli-4.3.1 → edge_containers_cli-4.4.0}/docs/genindex.rst +0 -0
  71. {edge_containers_cli-4.3.1 → edge_containers_cli-4.4.0}/docs/images/dls-favicon.ico +0 -0
  72. {edge_containers_cli-4.3.1 → edge_containers_cli-4.4.0}/docs/images/dls-logo.svg +0 -0
  73. {edge_containers_cli-4.3.1 → edge_containers_cli-4.4.0}/docs/images/ec-logs.png +0 -0
  74. {edge_containers_cli-4.3.1 → edge_containers_cli-4.4.0}/docs/images/ec-web.png +0 -0
  75. {edge_containers_cli-4.3.1 → edge_containers_cli-4.4.0}/docs/index.rst +0 -0
  76. {edge_containers_cli-4.3.1 → edge_containers_cli-4.4.0}/docs/user/explanations/docs-structure.rst +0 -0
  77. {edge_containers_cli-4.3.1 → edge_containers_cli-4.4.0}/docs/user/how-to/run-container.rst +0 -0
  78. {edge_containers_cli-4.3.1 → edge_containers_cli-4.4.0}/docs/user/index.rst +0 -0
  79. {edge_containers_cli-4.3.1 → edge_containers_cli-4.4.0}/docs/user/reference/api.rst +0 -0
  80. {edge_containers_cli-4.3.1 → edge_containers_cli-4.4.0}/docs/user/tutorials/installation.rst +0 -0
  81. {edge_containers_cli-4.3.1 → edge_containers_cli-4.4.0}/setup.cfg +0 -0
  82. {edge_containers_cli-4.3.1 → edge_containers_cli-4.4.0}/src/edge_containers_cli/__init__.py +0 -0
  83. {edge_containers_cli-4.3.1 → edge_containers_cli-4.4.0}/src/edge_containers_cli/cmds/__init__.py +0 -0
  84. {edge_containers_cli-4.3.1 → edge_containers_cli-4.4.0}/src/edge_containers_cli/cmds/demo_commands.py +0 -0
  85. {edge_containers_cli-4.3.1 → edge_containers_cli-4.4.0}/src/edge_containers_cli/cmds/monitor.py +0 -0
  86. {edge_containers_cli-4.3.1 → edge_containers_cli-4.4.0}/src/edge_containers_cli/cmds/monitor.tcss +0 -0
  87. {edge_containers_cli-4.3.1 → edge_containers_cli-4.4.0}/src/edge_containers_cli/definitions.py +0 -0
  88. {edge_containers_cli-4.3.1 → edge_containers_cli-4.4.0}/src/edge_containers_cli/globals.py +0 -0
  89. {edge_containers_cli-4.3.1 → edge_containers_cli-4.4.0}/src/edge_containers_cli/logging.py +0 -0
  90. {edge_containers_cli-4.3.1 → edge_containers_cli-4.4.0}/src/edge_containers_cli/shell.py +0 -0
  91. {edge_containers_cli-4.3.1 → edge_containers_cli-4.4.0}/src/edge_containers_cli.egg-info/SOURCES.txt +0 -0
  92. {edge_containers_cli-4.3.1 → edge_containers_cli-4.4.0}/src/edge_containers_cli.egg-info/dependency_links.txt +0 -0
  93. {edge_containers_cli-4.3.1 → edge_containers_cli-4.4.0}/src/edge_containers_cli.egg-info/requires.txt +0 -0
  94. {edge_containers_cli-4.3.1 → edge_containers_cli-4.4.0}/src/edge_containers_cli.egg-info/top_level.txt +0 -0
  95. {edge_containers_cli-4.3.1 → edge_containers_cli-4.4.0}/tests/__init__.py +0 -0
  96. {edge_containers_cli-4.3.1 → edge_containers_cli-4.4.0}/tests/data/bl01t-deployment/apps/values.yaml +0 -0
  97. {edge_containers_cli-4.3.1 → edge_containers_cli-4.4.0}/tests/data/bl01t-services/services/bl01t-ea-test-01/Chart.yaml +0 -0
  98. {edge_containers_cli-4.3.1 → edge_containers_cli-4.4.0}/tests/data/bl01t-services/services/bl01t-ea-test-01/config/ioc.db +0 -0
  99. {edge_containers_cli-4.3.1 → edge_containers_cli-4.4.0}/tests/data/bl01t-services/services/bl01t-ea-test-01/config/ioc.yaml +0 -0
  100. {edge_containers_cli-4.3.1 → edge_containers_cli-4.4.0}/tests/data/bl01t-services/services/bl01t-ea-test-01/templates/configmap.yaml +0 -0
  101. {edge_containers_cli-4.3.1 → edge_containers_cli-4.4.0}/tests/data/bl01t-services/services/bl01t-ea-test-01/values.yaml +0 -0
  102. {edge_containers_cli-4.3.1 → edge_containers_cli-4.4.0}/tests/data/bl01t-services/services/bl01t-ea-test-02/Chart.yaml +0 -0
  103. {edge_containers_cli-4.3.1 → edge_containers_cli-4.4.0}/tests/data/bl01t-services/services/bl01t-ea-test-02/values.yaml +0 -0
  104. {edge_containers_cli-4.3.1 → edge_containers_cli-4.4.0}/tests/data/bl01t-services/services/dls-aravis/Chart.yaml +0 -0
  105. {edge_containers_cli-4.3.1 → edge_containers_cli-4.4.0}/tests/data/bl01t-services/services/dls-aravis/config/ioc.yaml +0 -0
  106. {edge_containers_cli-4.3.1 → edge_containers_cli-4.4.0}/tests/data/bl01t-services/services/dls-aravis/templates/configmap.yaml +0 -0
  107. {edge_containers_cli-4.3.1 → edge_containers_cli-4.4.0}/tests/data/bl01t-services/services/dls-aravis/values.yaml +0 -0
  108. {edge_containers_cli-4.3.1 → edge_containers_cli-4.4.0}/tests/data/bl01t-services/services/values.yaml +0 -0
  109. {edge_containers_cli-4.3.1 → edge_containers_cli-4.4.0}/tests/test_demo.py +0 -0
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.2
1
+ Metadata-Version: 2.4
2
2
  Name: edge-containers-cli
3
- Version: 4.3.1
3
+ Version: 4.4.0
4
4
  Summary: CLI for deploying and managing epics containers IOCs and services
5
5
  Author-email: Giles Knap <giles.knap@gmail.com>
6
6
  License: Apache License
@@ -232,6 +232,7 @@ Requires-Dist: pytest-mock; extra == "dev"
232
232
  Requires-Dist: ruff; extra == "dev"
233
233
  Requires-Dist: tox-direct; extra == "dev"
234
234
  Requires-Dist: types-mock; extra == "dev"
235
+ Dynamic: license-file
235
236
 
236
237
  [![CI](https://github.com/epics-containers/edge-containers-cli/actions/workflows/ci.yml/badge.svg)](https://github.com/epics-containers/edge-containers-cli/actions/workflows/ci.yml)
237
238
  [![Coverage](https://codecov.io/gh/epics-containers/edge-containers-cli/branch/main/graph/badge.svg)](https://codecov.io/gh/epics-containers/edge-containers-cli)
@@ -43,7 +43,7 @@ dev = [
43
43
  ]
44
44
 
45
45
  [project.scripts]
46
- ec = "edge_containers_cli.__main__:cli"
46
+ ec = "edge_containers_cli.__main__:main"
47
47
 
48
48
  [project.urls]
49
49
  GitHub = "https://github.com/epics-containers/edge-containers-cli"
@@ -1,8 +1,10 @@
1
+ import os
2
+ import sys
1
3
  from typing import Optional
2
4
 
3
5
  import typer
4
6
 
5
- from edge_containers_cli.cli import cli
7
+ from edge_containers_cli.cli import cli, drop_methods, drop_options, set_optional
6
8
  from edge_containers_cli.definitions import ENV, ECBackends, ECContext, ECLogLevels
7
9
 
8
10
  from . import __version__
@@ -14,6 +16,8 @@ from .utils import init_cleanup
14
16
 
15
17
  __all__ = ["main"]
16
18
 
19
+ DEFAULT_BACKEND = ECBackends.ARGOCD
20
+
17
21
 
18
22
  def version_callback(value: bool):
19
23
  if value:
@@ -23,27 +27,14 @@ def version_callback(value: bool):
23
27
 
24
28
  def backend_callback(ctx: typer.Context, backend: ECBackends):
25
29
  init_backend(backend)
26
-
27
- # Dynamically drop any method not implemented
28
- not_implemented = [
29
- mthd.replace("_", "-") for mthd in ec_backend.get_notimplemented_cmds()
30
- ]
31
- typer_commands = ctx.command.commands # type: ignore
32
- for command in not_implemented:
33
- if command in typer_commands:
34
- typer_commands.pop(command)
35
-
36
- # Dynamically drop any cli options as specified
37
- for cmd_name, drop_params in ec_backend.get_notimplemented_params().items():
38
- for index, param in enumerate(typer_commands[cmd_name].params):
39
- if param.name in drop_params:
40
- typer_commands[cmd_name].params.pop(index)
41
-
30
+ drop_methods(ctx, ec_backend.get_notimplemented_cmds())
31
+ drop_options(ctx, ec_backend.get_notimplemented_params())
32
+ set_optional(ctx, ec_backend.get_optional_params())
42
33
  return backend.value
43
34
 
44
35
 
45
36
  @cli.callback()
46
- def main(
37
+ def _main(
47
38
  ctx: typer.Context,
48
39
  version: Optional[bool] = typer.Option(
49
40
  None,
@@ -67,7 +58,7 @@ def main(
67
58
  envvar=ENV.target.value,
68
59
  ),
69
60
  backend: ECBackends = typer.Option(
70
- ECBackends.ARGOCD,
61
+ DEFAULT_BACKEND,
71
62
  "-b",
72
63
  "--backend",
73
64
  callback=backend_callback,
@@ -123,7 +114,24 @@ def main(
123
114
  ec_backend.set_context(context)
124
115
 
125
116
 
117
+ def force_backend_callback():
118
+ """
119
+ Typer does not execute option callbacks before running --help unless the
120
+ option is explicitly provided
121
+ """
122
+ if "--help" in sys.argv:
123
+ if "-b" not in sys.argv:
124
+ backend = os.environ.get("EC_CLI_BACKEND", DEFAULT_BACKEND)
125
+ sys.argv.insert(1, backend)
126
+ sys.argv.insert(1, "-b")
127
+
128
+
129
+ def main():
130
+ force_backend_callback()
131
+ cli()
132
+
133
+
126
134
  # test with:
127
135
  # python -m edge_containers_cli
128
136
  if __name__ == "__main__":
129
- cli()
137
+ main()
@@ -17,5 +17,5 @@ __version__: str
17
17
  __version_tuple__: VERSION_TUPLE
18
18
  version_tuple: VERSION_TUPLE
19
19
 
20
- __version__ = version = '4.3.1'
21
- __version_tuple__ = version_tuple = (4, 3, 1)
20
+ __version__ = version = '4.4.0'
21
+ __version_tuple__ = version_tuple = (4, 4, 0)
@@ -33,7 +33,10 @@ def fetch_service_graph(repo: str) -> dict:
33
33
  if not version_map:
34
34
  with new_workdir() as path:
35
35
  version_map = create_version_map(
36
- repo, Path(globals.SERVICES_DIR), path, shared=[globals.SHARED_VALUES]
36
+ repo,
37
+ Path(globals.SERVICES_DIR),
38
+ path,
39
+ shared_files=[globals.SHARED_VALUES],
37
40
  )
38
41
  cache_dict(
39
42
  globals.CACHE_ROOT / url_encode(repo),
@@ -68,6 +68,9 @@ class Backend:
68
68
  def get_notimplemented_params(self) -> dict[str, list[str]]:
69
69
  return self.Commands.params_opt_out
70
70
 
71
+ def get_optional_params(self) -> dict[str, list[str]]:
72
+ return self.Commands.params_optional
73
+
71
74
 
72
75
  backend = Backend()
73
76
 
@@ -40,7 +40,10 @@ cli = ErrorHandlingTyper(pretty_exceptions_show_locals=False)
40
40
  @cli.command()
41
41
  def attach(
42
42
  service_name: str = typer.Argument(
43
- ..., help="Name of the service to attach to", autocompletion=running_svc
43
+ ...,
44
+ help="Name of the service to attach to",
45
+ autocompletion=running_svc,
46
+ show_default=False,
44
47
  ),
45
48
  ):
46
49
  """
@@ -52,15 +55,18 @@ def attach(
52
55
  @cli.command()
53
56
  def delete(
54
57
  service_name: str = typer.Argument(
55
- ..., help="Name of the service to delete", autocompletion=all_svc
58
+ ...,
59
+ help="Name of the service to delete",
60
+ autocompletion=all_svc,
61
+ show_default=False,
56
62
  ),
57
63
  yes: bool = typer.Option(False, "-y", "--yes", help="Skip confirmation prompt"),
58
64
  ):
59
65
  """
60
- Remove a helm deployment from the cluster
66
+ Remove a service from the cluster
61
67
  """
62
68
  confirmation(
63
- f"Remove all versions of {service_name} from the target `{backend.commands.target}`",
69
+ f"Remove {service_name} from the target `{backend.commands.target}`",
64
70
  yes,
65
71
  )
66
72
  backend.commands.delete(service_name)
@@ -69,12 +75,16 @@ def delete(
69
75
  @cli.command()
70
76
  def deploy(
71
77
  service_name: str = typer.Argument(
72
- ..., help="Name of the service to deploy", autocompletion=avail_services
78
+ ...,
79
+ help="Name of the service to deploy",
80
+ autocompletion=avail_services,
81
+ show_default=False,
73
82
  ),
74
83
  version: str = typer.Argument(
75
- ...,
76
- help="Version tag of the service to deploy",
84
+ "latest tag",
85
+ help="Version tag or branch of the service to deploy",
77
86
  autocompletion=avail_versions,
87
+ show_default=False,
78
88
  ),
79
89
  wait: bool = typer.Option(False, "--wait", help="Waits for readiness"),
80
90
  yes: bool = typer.Option(False, "-y", "--yes", help="Skip confirmation prompt"),
@@ -83,15 +93,19 @@ def deploy(
83
93
  ),
84
94
  ):
85
95
  """
86
- Pull an service helm chart version from the domain repo and deploy it to the cluster
96
+ Add a service to the cluster from its source repository
87
97
  """
88
- confirmation(
89
- f"Deploy {service_name.lower()} "
90
- f"of version `{version}` to target `{backend.commands.target}`",
91
- yes,
92
- )
98
+
99
+ def confirm_callback(svc_version):
100
+ confirmation(
101
+ f"Deploy {service_name.lower()} "
102
+ f"of version `{svc_version}` to target `{backend.commands.target}`",
103
+ yes,
104
+ )
105
+
93
106
  args = args if not wait else args + " --wait"
94
- backend.commands.deploy(service_name, version, args)
107
+ version = version if version != "latest tag" else ""
108
+ backend.commands.deploy(service_name, version, args, confirm_callback)
95
109
 
96
110
 
97
111
  @cli.command()
@@ -103,19 +117,23 @@ def deploy_local(
103
117
  file_okay=False,
104
118
  resolve_path=True,
105
119
  autocompletion=force_plain_completion,
120
+ show_default=False,
106
121
  ),
107
122
  yes: bool = typer.Option(False, "-y", "--yes", help="Skip confirmation prompt"),
108
123
  args: str = typer.Option("", help="Additional args for helm, 'must be quoted'"),
109
124
  ):
110
125
  """
111
- Deploy a local service helm chart directly to the cluster with dated beta version
126
+ Add a local service helm chart directly to the cluster with dated beta version
112
127
  """
113
- confirmation(
114
- f"Deploy local {svc_instance.name.lower()} "
115
- f"from {svc_instance} to target `{backend.commands.target}`",
116
- yes,
117
- )
118
- backend.commands.deploy_local(svc_instance, args)
128
+
129
+ def confirm_callback(svc_version):
130
+ confirmation(
131
+ f"Deploy local {svc_instance.name.lower()} of version `{svc_version}` "
132
+ f"from {svc_instance} to target `{backend.commands.target}`",
133
+ yes,
134
+ )
135
+
136
+ backend.commands.deploy_local(svc_instance, args, confirm_callback)
119
137
 
120
138
 
121
139
  @cli.command()
@@ -131,6 +149,7 @@ def exec(
131
149
  ...,
132
150
  help="Name of the service container to run in",
133
151
  autocompletion=running_svc,
152
+ show_default=False,
134
153
  ),
135
154
  ):
136
155
  """Execute a bash prompt in a running container"""
@@ -140,7 +159,10 @@ def exec(
140
159
  @cli.command()
141
160
  def instances(
142
161
  service_name: str = typer.Argument(
143
- ..., help="Name of the service to inspect", autocompletion=avail_services
162
+ ...,
163
+ help="Name of the service to inspect",
164
+ autocompletion=avail_services,
165
+ show_default=False,
144
166
  ),
145
167
  ):
146
168
  """List all versions of the specified service in the repository"""
@@ -149,19 +171,19 @@ def instances(
149
171
  service_name,
150
172
  backend.commands.repo,
151
173
  Path(globals.SERVICES_DIR),
152
- shared=[globals.SHARED_VALUES],
174
+ shared_files=[globals.SHARED_VALUES],
153
175
  )
154
176
  )
155
177
 
156
178
 
157
- @cli.command()
158
- def list():
179
+ @cli.command(name="list")
180
+ def _list():
159
181
  """List all services available in the service repository"""
160
182
  print(
161
183
  list_all(
162
184
  backend.commands.repo,
163
185
  Path(globals.SERVICES_DIR),
164
- shared=[globals.SHARED_VALUES],
186
+ shared_files=[globals.SHARED_VALUES],
165
187
  )
166
188
  )
167
189
 
@@ -172,16 +194,20 @@ def log_history(
172
194
  ...,
173
195
  help="Name of the service to inspect",
174
196
  autocompletion=all_svc,
197
+ show_default=False,
175
198
  ),
176
199
  ):
177
- """Open historical logs for an service"""
200
+ """Open historical logs for a service"""
178
201
  backend.commands.log_history(service_name)
179
202
 
180
203
 
181
204
  @cli.command()
182
205
  def logs(
183
206
  service_name: str = typer.Argument(
184
- ..., help="Name of the service to inspect", autocompletion=running_svc
207
+ ...,
208
+ help="Name of the service to inspect",
209
+ autocompletion=running_svc,
210
+ show_default=False,
185
211
  ),
186
212
  prev: bool = typer.Option(
187
213
  False,
@@ -190,7 +216,7 @@ def logs(
190
216
  help="Show log from the previous instance of the service",
191
217
  ),
192
218
  ):
193
- """Show logs for current and previous instances of an service"""
219
+ """Show logs for current and previous instances of a service"""
194
220
  backend.commands.logs(service_name, prev)
195
221
 
196
222
 
@@ -200,7 +226,7 @@ def monitor(
200
226
  False, "-r", "--running-only", help="list only services that are running"
201
227
  ),
202
228
  ):
203
- """Open monitor TUI."""
229
+ """Open monitor TUI"""
204
230
  from edge_containers_cli.cmds.monitor import (
205
231
  MonitorApp, # Lazy import for performace
206
232
  )
@@ -222,7 +248,10 @@ def ps(
222
248
  @cli.command()
223
249
  def restart(
224
250
  service_name: str = typer.Argument(
225
- ..., help="Name of the container to restart", autocompletion=running_svc
251
+ ...,
252
+ help="Name of the container to restart",
253
+ autocompletion=running_svc,
254
+ show_default=False,
226
255
  ),
227
256
  ):
228
257
  """Restart a service"""
@@ -232,7 +261,10 @@ def restart(
232
261
  @cli.command()
233
262
  def start(
234
263
  service_name: str = typer.Argument(
235
- ..., help="Name of the service container to start", autocompletion=all_svc
264
+ ...,
265
+ help="Name of the service container to start",
266
+ autocompletion=all_svc,
267
+ show_default=False,
236
268
  ),
237
269
  commit: bool = typer.Option(False, help="Commits the values to the git repo"),
238
270
  ):
@@ -250,6 +282,7 @@ def stop(
250
282
  ...,
251
283
  help="Name of the service container to stop",
252
284
  autocompletion=running_svc,
285
+ show_default=False,
253
286
  ),
254
287
  commit: bool = typer.Option(False, help="Commits the values to the git repo"),
255
288
  ):
@@ -270,6 +303,7 @@ def template(
270
303
  file_okay=False,
271
304
  resolve_path=True,
272
305
  autocompletion=force_plain_completion,
306
+ show_default=False,
273
307
  ),
274
308
  args: str = typer.Option("", help="Additional args for helm, 'must be quoted'"),
275
309
  ):
@@ -278,3 +312,30 @@ def template(
278
312
  """
279
313
  args = f"{args} --debug"
280
314
  backend.commands.template(svc_instance, args)
315
+
316
+
317
+ def drop_methods(ctx: typer.Context, to_drop: list[str]):
318
+ """Dynamically drop any method not implemented"""
319
+ not_implemented = [mthd.replace("_", "-") for mthd in to_drop]
320
+ typer_commands = ctx.command.commands # type: ignore
321
+ for command in not_implemented:
322
+ if command in typer_commands:
323
+ typer_commands.pop(command)
324
+
325
+
326
+ def drop_options(ctx: typer.Context, to_drop: dict[str, list[str]]):
327
+ """Dynamically drop any cli options as specified"""
328
+ typer_commands = ctx.command.commands # type: ignore
329
+ for cmd_name, drop_params in to_drop.items():
330
+ for i, param in enumerate(typer_commands[cmd_name].params):
331
+ if param.name in drop_params:
332
+ typer_commands[cmd_name].params.pop(i)
333
+
334
+
335
+ def set_optional(ctx: typer.Context, to_set: dict[str, list[str]]):
336
+ """Dynamically set any cli options optional as specified"""
337
+ typer_commands = ctx.command.commands # type: ignore
338
+ for cmd_name, optional_params in to_set.items():
339
+ for i, param in enumerate(typer_commands[cmd_name].params):
340
+ if param.name in optional_params:
341
+ typer_commands[cmd_name].params[i].required = False
@@ -4,6 +4,7 @@ implements commands for deploying and managing service instances suing argocd
4
4
  Relies on the Helm class for deployment aspects.
5
5
  """
6
6
 
7
+ import re
7
8
  import webbrowser
8
9
  from datetime import datetime
9
10
  from pathlib import Path
@@ -12,6 +13,7 @@ from time import sleep
12
13
  import polars
13
14
  from ruamel.yaml import YAML
14
15
 
16
+ from edge_containers_cli import globals
15
17
  from edge_containers_cli.cmds.commands import (
16
18
  CommandError,
17
19
  Commands,
@@ -19,10 +21,10 @@ from edge_containers_cli.cmds.commands import (
19
21
  ServicesSchema,
20
22
  )
21
23
  from edge_containers_cli.definitions import ECContext
22
- from edge_containers_cli.git import set_values
23
- from edge_containers_cli.globals import TIME_FORMAT
24
+ from edge_containers_cli.git import del_key, set_value
24
25
  from edge_containers_cli.logging import log
25
26
  from edge_containers_cli.shell import ShellError, shell
27
+ from edge_containers_cli.utils import YamlTypes
26
28
 
27
29
 
28
30
  def extract_ns_app(target: str) -> tuple[str, str]:
@@ -30,6 +32,18 @@ def extract_ns_app(target: str) -> tuple[str, str]:
30
32
  return namespace, app
31
33
 
32
34
 
35
+ def get_patches(target) -> dict:
36
+ app_resp = shell.run_command(
37
+ f"argocd app get --show-params {target} -o json",
38
+ )
39
+ app_dicts = YAML(typ="safe").load(app_resp)
40
+ try:
41
+ patch_dict = app_dicts["spec"]["source"]["helm"]["parameters"]
42
+ except KeyError:
43
+ patch_dict = {}
44
+ return patch_dict
45
+
46
+
33
47
  def do_retry(cmd):
34
48
  def _do_retry(*args, **kwargs):
35
49
  max_attempts = 5
@@ -52,14 +66,14 @@ def do_retry(cmd):
52
66
 
53
67
 
54
68
  @do_retry
55
- def patch_value(target: str, key: str, value: str | bool | int):
69
+ def patch_value(target: str, key: str, value: YamlTypes):
56
70
  cmd_temp_ = f"argocd app set {target} -p {key}={value}"
57
71
  shell.run_command(cmd_temp_, skip_on_dryrun=True)
58
72
  # Rely on argocd autosync to get the cluster into the right state
59
73
 
60
74
 
61
75
  @do_retry
62
- def push_value(target: str, key: str, value: str | bool | int):
76
+ def push_value(target: str, key: str, value: YamlTypes):
63
77
  # Get source details
64
78
  app_resp = shell.run_command(
65
79
  f"argocd app get {target} -o yaml",
@@ -68,12 +82,7 @@ def push_value(target: str, key: str, value: str | bool | int):
68
82
  repo_url = app_dicts["spec"]["source"]["repoURL"]
69
83
  path = Path(app_dicts["spec"]["source"]["path"])
70
84
 
71
- set_values(
72
- repo_url,
73
- path / "values.yaml",
74
- key,
75
- value,
76
- )
85
+ set_value(repo_url, path / "values.yaml", key, value)
77
86
 
78
87
  # Free a possible patched value & refresh repo
79
88
  cmd_unset = f"argocd app unset {target} -p {key}"
@@ -83,17 +92,65 @@ def push_value(target: str, key: str, value: str | bool | int):
83
92
  # Rely on argocd autosync to get the cluster into the right state
84
93
 
85
94
 
95
+ @do_retry
96
+ def push_remove_key(target: str, key: str):
97
+ # Get source details
98
+ app_resp = shell.run_command(
99
+ f"argocd app get {target} -o yaml",
100
+ )
101
+ app_dicts = YAML(typ="safe").load(app_resp)
102
+ repo_url = app_dicts["spec"]["source"]["repoURL"]
103
+ path = Path(app_dicts["spec"]["source"]["path"])
104
+
105
+ del_key(repo_url, path / "values.yaml", key)
106
+
107
+ # Free a possible patched value, its children & refresh repo
108
+ cmd_unset = f"argocd app unset {target} -p {key}"
109
+ shell.run_command(cmd_unset, skip_on_dryrun=True)
110
+ app_patches = get_patches(target)
111
+ for patch in app_patches:
112
+ if re.match(rf"{key}\..*", patch["name"]):
113
+ cmd_unset_child = f"argocd app unset {target} -p {patch['name']}"
114
+ shell.run_command(cmd_unset_child, skip_on_dryrun=True)
115
+ cmd_refresh = f"argocd app get {target} --refresh"
116
+ shell.run_command(cmd_refresh, skip_on_dryrun=True)
117
+ # Rely on argocd autosync to get the cluster into the right state
118
+
119
+
120
+ def get_services_repo(deployment_repo_url: str) -> str:
121
+ services_repo_url = ""
122
+ return services_repo_url
123
+
124
+
86
125
  class ArgoCommands(Commands):
87
126
  """
88
127
  A class for implementing the Kubernetes based commands
89
128
  """
90
129
 
130
+ params_opt_out = {
131
+ "deploy": ["args", "wait"],
132
+ }
133
+
91
134
  def __init__(
92
135
  self,
93
136
  ctx: ECContext,
94
137
  ):
95
138
  super().__init__(ctx)
96
139
 
140
+ def delete(self, service_name: str) -> None:
141
+ self._check_service(service_name)
142
+ push_remove_key(self.target, f"ec_services.{service_name}")
143
+
144
+ def deploy(self, service_name, version, args, confirm_callback=None) -> None:
145
+ latest_version = self._get_latest_version(service_name)
146
+ if not version:
147
+ version = latest_version
148
+ if confirm_callback:
149
+ confirm_callback(version)
150
+ deploy_dict: YamlTypes = {"enabled": True, "targetRevision": version}
151
+
152
+ push_value(self.target, f"ec_services.{service_name}", deploy_dict)
153
+
97
154
  def logs(self, service_name, prev):
98
155
  self._logs(service_name, prev)
99
156
 
@@ -153,7 +210,10 @@ class ArgoCommands(Commands):
153
210
 
154
211
  if app_dicts:
155
212
  for app in app_dicts:
156
- resources_dict = app["status"]["resources"]
213
+ try:
214
+ resources_dict = app["status"]["resources"]
215
+ except KeyError:
216
+ continue
157
217
 
158
218
  for resource in resources_dict:
159
219
  is_ready = False
@@ -188,7 +248,7 @@ class ArgoCommands(Commands):
188
248
  )
189
249
  service_data["ready"].append(is_ready)
190
250
  service_data["deployed"].append(
191
- datetime.strftime(time_stamp, TIME_FORMAT)
251
+ datetime.strftime(time_stamp, globals.TIME_FORMAT)
192
252
  )
193
253
 
194
254
  services_df = polars.from_dict(service_data, schema=ServicesSchema)
@@ -1,10 +1,15 @@
1
1
  from abc import ABC, abstractmethod
2
+ from collections.abc import Callable
2
3
  from pathlib import Path
3
4
 
4
5
  import polars
6
+ from natsort import natsorted
5
7
 
8
+ from edge_containers_cli import globals
6
9
  from edge_containers_cli.definitions import ENV, ECContext
10
+ from edge_containers_cli.git import create_version_map
7
11
  from edge_containers_cli.logging import log
12
+ from edge_containers_cli.utils import new_workdir
8
13
 
9
14
 
10
15
  class CommandError(Exception):
@@ -39,7 +44,8 @@ class Commands(ABC):
39
44
  Methods not exposed to the CLI should be private
40
45
  """
41
46
 
42
- params_opt_out: dict[str, list[str]] = {} # Optionally drop parameters from the CLI
47
+ params_opt_out: dict[str, list[str]] = {} # Drop parameters from the CLI
48
+ params_optional: dict[str, list[str]] = {} # Set optional parameters from the CLI
43
49
 
44
50
  def __init__(self, ctx: ECContext):
45
51
  self._target = ctx.target
@@ -108,10 +114,21 @@ class Commands(ABC):
108
114
  def delete(self, service_name: str) -> None:
109
115
  raise NotImplementedError
110
116
 
111
- def deploy(self, service_name: str, version: str, args: str) -> None:
117
+ def deploy(
118
+ self,
119
+ service_name: str,
120
+ version: str,
121
+ args: str,
122
+ confirm_callback: Callable[[str], None] | None = None,
123
+ ) -> None:
112
124
  raise NotImplementedError
113
125
 
114
- def deploy_local(self, svc_instance: Path, args: str) -> None:
126
+ def deploy_local(
127
+ self,
128
+ svc_instance: Path,
129
+ args: str,
130
+ confirm_callback: Callable[[str], None] | None = None,
131
+ ) -> None:
115
132
  raise NotImplementedError
116
133
 
117
134
  def exec(self, service_name: str) -> None:
@@ -171,3 +188,27 @@ class Commands(ABC):
171
188
  pass
172
189
  else:
173
190
  raise CommandError(f"Service '{service_name}' not found in {self.target}")
191
+
192
+ def _get_latest_version(self, service_name) -> str:
193
+ with new_workdir() as path:
194
+ version_map = create_version_map(
195
+ self.repo,
196
+ Path(globals.SERVICES_DIR),
197
+ path,
198
+ shared_files=[globals.SHARED_VALUES],
199
+ )
200
+ svc_list = version_map.keys()
201
+ log.debug(f"Found the following services: {svc_list}")
202
+ if service_name not in svc_list:
203
+ raise CommandError(f"Service '{service_name}' not found in {self.repo}")
204
+
205
+ version_list = natsorted(version_map[service_name])
206
+ log.debug(f"Found the following versions of {service_name}: {version_list}")
207
+ try:
208
+ version = version_list[-1]
209
+ except IndexError as err:
210
+ raise CommandError(
211
+ f"No versions of '{service_name}' found in {self.repo}"
212
+ ) from err
213
+
214
+ return version
@@ -1,3 +1,4 @@
1
+ from collections.abc import Callable
1
2
  from pathlib import Path
2
3
  from typing import Optional
3
4
 
@@ -50,19 +51,25 @@ class Helm:
50
51
  for package in service_path.glob("*.tgz"):
51
52
  package.unlink(missing_ok=True)
52
53
 
53
- def deploy_local(self, service_path: Path):
54
+ def deploy_local(
55
+ self, service_path: Path, confirm_callback: Callable[[str], None] | None = None
56
+ ):
54
57
  """
55
58
  Deploy a local helm chart directly to the cluster with dated beta version
56
59
  """
57
60
 
61
+ if confirm_callback:
62
+ confirm_callback(self.version)
58
63
  validate_instance_path(service_path)
59
64
  self.cleanup_chart(service_path)
60
65
  self._do_deploy(service_path)
61
66
 
62
- def deploy(self):
67
+ def deploy(self, confirm_callback: Callable[[str], None] | None = None):
63
68
  """
64
69
  Clone a helm chart and deploy it to the cluster
65
70
  """
71
+ if confirm_callback:
72
+ confirm_callback(self.version)
66
73
  shell.run_command(
67
74
  f"git clone {self.repo} {self.tmp} --depth=1 "
68
75
  f"--single-branch --branch={self.version}",