edge-containers-cli 4.0.0__tar.gz → 4.1.1b0__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 (105) hide show
  1. edge_containers_cli-4.1.1b0/.github/workflows/_container.yml +51 -0
  2. {edge_containers_cli-4.0.0 → edge_containers_cli-4.1.1b0}/.github/workflows/ci.yml +7 -0
  3. edge_containers_cli-4.1.1b0/Dockerfile +29 -0
  4. {edge_containers_cli-4.0.0 → edge_containers_cli-4.1.1b0}/PKG-INFO +1 -1
  5. {edge_containers_cli-4.0.0 → edge_containers_cli-4.1.1b0}/src/edge_containers_cli/__main__.py +9 -2
  6. {edge_containers_cli-4.0.0 → edge_containers_cli-4.1.1b0}/src/edge_containers_cli/_version.py +2 -2
  7. {edge_containers_cli-4.0.0 → edge_containers_cli-4.1.1b0}/src/edge_containers_cli/backend.py +19 -5
  8. {edge_containers_cli-4.0.0 → edge_containers_cli-4.1.1b0}/src/edge_containers_cli/cli.py +12 -2
  9. {edge_containers_cli-4.0.0 → edge_containers_cli-4.1.1b0}/src/edge_containers_cli/cmds/argo_commands.py +42 -8
  10. {edge_containers_cli-4.0.0 → edge_containers_cli-4.1.1b0}/src/edge_containers_cli/cmds/commands.py +20 -18
  11. edge_containers_cli-4.1.1b0/src/edge_containers_cli/cmds/demo_commands.py +135 -0
  12. {edge_containers_cli-4.0.0 → edge_containers_cli-4.1.1b0}/src/edge_containers_cli/cmds/k8s_commands.py +7 -2
  13. {edge_containers_cli-4.0.0 → edge_containers_cli-4.1.1b0}/src/edge_containers_cli/cmds/monitor.py +3 -3
  14. {edge_containers_cli-4.0.0 → edge_containers_cli-4.1.1b0}/src/edge_containers_cli/definitions.py +1 -0
  15. {edge_containers_cli-4.0.0 → edge_containers_cli-4.1.1b0}/src/edge_containers_cli/git.py +28 -2
  16. {edge_containers_cli-4.0.0 → edge_containers_cli-4.1.1b0}/src/edge_containers_cli/utils.py +45 -0
  17. {edge_containers_cli-4.0.0 → edge_containers_cli-4.1.1b0}/src/edge_containers_cli.egg-info/PKG-INFO +1 -1
  18. {edge_containers_cli-4.0.0 → edge_containers_cli-4.1.1b0}/src/edge_containers_cli.egg-info/SOURCES.txt +19 -13
  19. {edge_containers_cli-4.0.0 → edge_containers_cli-4.1.1b0}/tests/conftest.py +11 -1
  20. edge_containers_cli-4.1.1b0/tests/data/argocd.yaml +83 -0
  21. edge_containers_cli-4.1.1b0/tests/data/bl01t-deployment/apps/values.yaml +3 -0
  22. {edge_containers_cli-4.0.0 → edge_containers_cli-4.1.1b0}/tests/data/k8s.yaml +3 -3
  23. edge_containers_cli-4.1.1b0/tests/data/yaml.yaml +12 -0
  24. {edge_containers_cli-4.0.0 → edge_containers_cli-4.1.1b0}/tests/test_argocd.py +22 -2
  25. {edge_containers_cli-4.0.0 → edge_containers_cli-4.1.1b0}/tests/test_cli.py +2 -2
  26. edge_containers_cli-4.1.1b0/tests/test_demo.py +32 -0
  27. {edge_containers_cli-4.0.0 → edge_containers_cli-4.1.1b0}/tests/test_k8s.py +5 -3
  28. edge_containers_cli-4.1.1b0/tests/test_unit.py +30 -0
  29. edge_containers_cli-4.0.0/Dockerfile +0 -13
  30. edge_containers_cli-4.0.0/tests/data/argocd.yaml +0 -45
  31. {edge_containers_cli-4.0.0 → edge_containers_cli-4.1.1b0}/.copier-answers.yml +0 -0
  32. {edge_containers_cli-4.0.0 → edge_containers_cli-4.1.1b0}/.devcontainer/devcontainer.json +0 -0
  33. {edge_containers_cli-4.0.0 → edge_containers_cli-4.1.1b0}/.github/CONTRIBUTING.md +0 -0
  34. {edge_containers_cli-4.0.0 → edge_containers_cli-4.1.1b0}/.github/actions/install_requirements/action.yml +0 -0
  35. {edge_containers_cli-4.0.0 → edge_containers_cli-4.1.1b0}/.github/dependabot.yml +0 -0
  36. {edge_containers_cli-4.0.0 → edge_containers_cli-4.1.1b0}/.github/pages/index.html +0 -0
  37. {edge_containers_cli-4.0.0 → edge_containers_cli-4.1.1b0}/.github/pages/make_switcher.py +0 -0
  38. {edge_containers_cli-4.0.0 → edge_containers_cli-4.1.1b0}/.github/workflows/_check.yml +0 -0
  39. {edge_containers_cli-4.0.0 → edge_containers_cli-4.1.1b0}/.github/workflows/_dist.yml +0 -0
  40. {edge_containers_cli-4.0.0 → edge_containers_cli-4.1.1b0}/.github/workflows/_pypi.yml +0 -0
  41. {edge_containers_cli-4.0.0 → edge_containers_cli-4.1.1b0}/.github/workflows/_release.yml +0 -0
  42. {edge_containers_cli-4.0.0 → edge_containers_cli-4.1.1b0}/.github/workflows/_test.yml +0 -0
  43. {edge_containers_cli-4.0.0 → edge_containers_cli-4.1.1b0}/.github/workflows/_tox.yml +0 -0
  44. {edge_containers_cli-4.0.0 → edge_containers_cli-4.1.1b0}/.gitignore +0 -0
  45. {edge_containers_cli-4.0.0 → edge_containers_cli-4.1.1b0}/.pre-commit-config.yaml +0 -0
  46. {edge_containers_cli-4.0.0 → edge_containers_cli-4.1.1b0}/.vscode/extensions.json +0 -0
  47. {edge_containers_cli-4.0.0 → edge_containers_cli-4.1.1b0}/.vscode/launch.json +0 -0
  48. {edge_containers_cli-4.0.0 → edge_containers_cli-4.1.1b0}/.vscode/settings.json +0 -0
  49. {edge_containers_cli-4.0.0 → edge_containers_cli-4.1.1b0}/.vscode/tasks.json +0 -0
  50. {edge_containers_cli-4.0.0 → edge_containers_cli-4.1.1b0}/LICENSE +0 -0
  51. {edge_containers_cli-4.0.0 → edge_containers_cli-4.1.1b0}/README.md +0 -0
  52. {edge_containers_cli-4.0.0 → edge_containers_cli-4.1.1b0}/catalog-info.yaml +0 -0
  53. {edge_containers_cli-4.0.0 → edge_containers_cli-4.1.1b0}/docs/conf.py +0 -0
  54. {edge_containers_cli-4.0.0 → edge_containers_cli-4.1.1b0}/docs/developer/explanations/decisions/0001-record-architecture-decisions.rst +0 -0
  55. {edge_containers_cli-4.0.0 → edge_containers_cli-4.1.1b0}/docs/developer/explanations/decisions/0002-switched-to-pip-skeleton.rst +0 -0
  56. {edge_containers_cli-4.0.0 → edge_containers_cli-4.1.1b0}/docs/developer/explanations/decisions.rst +0 -0
  57. {edge_containers_cli-4.0.0 → edge_containers_cli-4.1.1b0}/docs/developer/how-to/build-docs.rst +0 -0
  58. {edge_containers_cli-4.0.0 → edge_containers_cli-4.1.1b0}/docs/developer/how-to/contribute.rst +0 -0
  59. {edge_containers_cli-4.0.0 → edge_containers_cli-4.1.1b0}/docs/developer/how-to/lint.rst +0 -0
  60. {edge_containers_cli-4.0.0 → edge_containers_cli-4.1.1b0}/docs/developer/how-to/make-release.rst +0 -0
  61. {edge_containers_cli-4.0.0 → edge_containers_cli-4.1.1b0}/docs/developer/how-to/pin-requirements.rst +0 -0
  62. {edge_containers_cli-4.0.0 → edge_containers_cli-4.1.1b0}/docs/developer/how-to/run-tests.rst +0 -0
  63. {edge_containers_cli-4.0.0 → edge_containers_cli-4.1.1b0}/docs/developer/how-to/static-analysis.rst +0 -0
  64. {edge_containers_cli-4.0.0 → edge_containers_cli-4.1.1b0}/docs/developer/how-to/test-container.rst +0 -0
  65. {edge_containers_cli-4.0.0 → edge_containers_cli-4.1.1b0}/docs/developer/how-to/update-tools.rst +0 -0
  66. {edge_containers_cli-4.0.0 → edge_containers_cli-4.1.1b0}/docs/developer/index.rst +0 -0
  67. {edge_containers_cli-4.0.0 → edge_containers_cli-4.1.1b0}/docs/developer/reference/standards.rst +0 -0
  68. {edge_containers_cli-4.0.0 → edge_containers_cli-4.1.1b0}/docs/developer/tutorials/dev-install.rst +0 -0
  69. {edge_containers_cli-4.0.0 → edge_containers_cli-4.1.1b0}/docs/genindex.rst +0 -0
  70. {edge_containers_cli-4.0.0 → edge_containers_cli-4.1.1b0}/docs/images/dls-favicon.ico +0 -0
  71. {edge_containers_cli-4.0.0 → edge_containers_cli-4.1.1b0}/docs/images/dls-logo.svg +0 -0
  72. {edge_containers_cli-4.0.0 → edge_containers_cli-4.1.1b0}/docs/index.rst +0 -0
  73. {edge_containers_cli-4.0.0 → edge_containers_cli-4.1.1b0}/docs/user/explanations/docs-structure.rst +0 -0
  74. {edge_containers_cli-4.0.0 → edge_containers_cli-4.1.1b0}/docs/user/how-to/run-container.rst +0 -0
  75. {edge_containers_cli-4.0.0 → edge_containers_cli-4.1.1b0}/docs/user/index.rst +0 -0
  76. {edge_containers_cli-4.0.0 → edge_containers_cli-4.1.1b0}/docs/user/reference/api.rst +0 -0
  77. {edge_containers_cli-4.0.0 → edge_containers_cli-4.1.1b0}/docs/user/tutorials/installation.rst +0 -0
  78. {edge_containers_cli-4.0.0 → edge_containers_cli-4.1.1b0}/pyproject.toml +0 -0
  79. {edge_containers_cli-4.0.0 → edge_containers_cli-4.1.1b0}/setup.cfg +0 -0
  80. {edge_containers_cli-4.0.0 → edge_containers_cli-4.1.1b0}/src/edge_containers_cli/__init__.py +0 -0
  81. {edge_containers_cli-4.0.0 → edge_containers_cli-4.1.1b0}/src/edge_containers_cli/autocomplete.py +0 -0
  82. {edge_containers_cli-4.0.0 → edge_containers_cli-4.1.1b0}/src/edge_containers_cli/cmds/__init__.py +0 -0
  83. {edge_containers_cli-4.0.0 → edge_containers_cli-4.1.1b0}/src/edge_containers_cli/cmds/helm.py +0 -0
  84. {edge_containers_cli-4.0.0 → edge_containers_cli-4.1.1b0}/src/edge_containers_cli/cmds/monitor.tcss +0 -0
  85. {edge_containers_cli-4.0.0 → edge_containers_cli-4.1.1b0}/src/edge_containers_cli/globals.py +0 -0
  86. {edge_containers_cli-4.0.0 → edge_containers_cli-4.1.1b0}/src/edge_containers_cli/logging.py +0 -0
  87. {edge_containers_cli-4.0.0 → edge_containers_cli-4.1.1b0}/src/edge_containers_cli/shell.py +0 -0
  88. {edge_containers_cli-4.0.0 → edge_containers_cli-4.1.1b0}/src/edge_containers_cli.egg-info/dependency_links.txt +0 -0
  89. {edge_containers_cli-4.0.0 → edge_containers_cli-4.1.1b0}/src/edge_containers_cli.egg-info/entry_points.txt +0 -0
  90. {edge_containers_cli-4.0.0 → edge_containers_cli-4.1.1b0}/src/edge_containers_cli.egg-info/requires.txt +0 -0
  91. {edge_containers_cli-4.0.0 → edge_containers_cli-4.1.1b0}/src/edge_containers_cli.egg-info/top_level.txt +0 -0
  92. {edge_containers_cli-4.0.0 → edge_containers_cli-4.1.1b0}/tests/__init__.py +0 -0
  93. {edge_containers_cli-4.0.0/tests/data/bl01t → edge_containers_cli-4.1.1b0/tests/data/bl01t-services}/services/bl01t-ea-test-01/Chart.yaml +0 -0
  94. {edge_containers_cli-4.0.0/tests/data/bl01t → edge_containers_cli-4.1.1b0/tests/data/bl01t-services}/services/bl01t-ea-test-01/config/ioc.db +0 -0
  95. {edge_containers_cli-4.0.0/tests/data/bl01t → edge_containers_cli-4.1.1b0/tests/data/bl01t-services}/services/bl01t-ea-test-01/config/ioc.yaml +0 -0
  96. {edge_containers_cli-4.0.0/tests/data/bl01t → edge_containers_cli-4.1.1b0/tests/data/bl01t-services}/services/bl01t-ea-test-01/templates/configmap.yaml +0 -0
  97. {edge_containers_cli-4.0.0/tests/data/bl01t → edge_containers_cli-4.1.1b0/tests/data/bl01t-services}/services/bl01t-ea-test-01/values.yaml +0 -0
  98. {edge_containers_cli-4.0.0/tests/data/bl01t → edge_containers_cli-4.1.1b0/tests/data/bl01t-services}/services/bl01t-ea-test-02/Chart.yaml +0 -0
  99. {edge_containers_cli-4.0.0/tests/data/bl01t → edge_containers_cli-4.1.1b0/tests/data/bl01t-services}/services/bl01t-ea-test-02/values.yaml +0 -0
  100. {edge_containers_cli-4.0.0/tests/data/bl01t → edge_containers_cli-4.1.1b0/tests/data/bl01t-services}/services/dls-aravis/Chart.yaml +0 -0
  101. {edge_containers_cli-4.0.0/tests/data/bl01t → edge_containers_cli-4.1.1b0/tests/data/bl01t-services}/services/dls-aravis/config/ioc.yaml +0 -0
  102. {edge_containers_cli-4.0.0/tests/data/bl01t → edge_containers_cli-4.1.1b0/tests/data/bl01t-services}/services/dls-aravis/templates/configmap.yaml +0 -0
  103. {edge_containers_cli-4.0.0/tests/data/bl01t → edge_containers_cli-4.1.1b0/tests/data/bl01t-services}/services/dls-aravis/values.yaml +0 -0
  104. {edge_containers_cli-4.0.0/tests/data/bl01t → edge_containers_cli-4.1.1b0/tests/data/bl01t-services}/services/values.yaml +0 -0
  105. {edge_containers_cli-4.0.0 → edge_containers_cli-4.1.1b0}/tests/data/cli.yaml +0 -0
@@ -0,0 +1,51 @@
1
+ on:
2
+ workflow_call:
3
+
4
+ jobs:
5
+ build:
6
+ runs-on: ubuntu-latest
7
+
8
+ steps:
9
+ - name: Checkout
10
+ uses: actions/checkout@v4
11
+ with:
12
+ # Need this to get version number from last tag
13
+ fetch-depth: 0
14
+
15
+ - name: Set up Docker Buildx
16
+ id: buildx
17
+ uses: docker/setup-buildx-action@v3
18
+
19
+ - name: Log in to GitHub Docker Registry
20
+ if: github.event_name != 'pull_request'
21
+ uses: docker/login-action@v3
22
+ with:
23
+ registry: ghcr.io
24
+ username: ${{ github.actor }}
25
+ password: ${{ secrets.GITHUB_TOKEN }}
26
+
27
+ - name: Build and export to Docker local cache
28
+ uses: docker/build-push-action@v5
29
+ with:
30
+ context: .
31
+ target: server
32
+
33
+ - name: Create tags for publishing image
34
+ id: meta
35
+ uses: docker/metadata-action@v5
36
+ with:
37
+ images: ghcr.io/${{ github.repository }}
38
+ tags: |
39
+ type=ref,event=tag
40
+ type=raw,value=latest
41
+
42
+ - name: Push cached image to container registry
43
+ if: github.ref_type == 'tag'
44
+ uses: docker/build-push-action@v5
45
+ # This does not build the image again, it will find the image in the
46
+ # Docker cache and publish it
47
+ with:
48
+ context: .
49
+ push: true
50
+ tags: ${{ steps.meta.outputs.tags }}
51
+ labels: ${{ steps.meta.outputs.labels }}
@@ -34,6 +34,13 @@ jobs:
34
34
  secrets:
35
35
  CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
36
36
 
37
+ container:
38
+ needs: check
39
+ if: needs.check.outputs.branch-pr == ''
40
+ uses: ./.github/workflows/_container.yml
41
+ permissions:
42
+ packages: write
43
+
37
44
  dist:
38
45
  needs: check
39
46
  if: needs.check.outputs.branch-pr == ''
@@ -0,0 +1,29 @@
1
+ # The devcontainer should use the developer target and run as root with podman
2
+ # or docker with user namespaces.
3
+ ARG PYTHON_VERSION=3.11
4
+ FROM python:${PYTHON_VERSION} as developer
5
+
6
+ # Add any system dependencies for the developer/build environment here
7
+ RUN apt-get update && apt-get install -y --no-install-recommends \
8
+ graphviz \
9
+ && rm -rf /var/lib/apt/lists/*
10
+
11
+ # Set up a virtual environment and put it in PATH
12
+ RUN python -m venv /venv
13
+ ENV PATH=/venv/bin:$PATH
14
+
15
+ # The build stage installs the context into the venv
16
+ FROM developer as build
17
+ COPY . /context
18
+ WORKDIR /context
19
+ RUN pip install .
20
+
21
+ # The runtime stage copies the built venv into a slim runtime container
22
+ FROM python:${PYTHON_VERSION}-slim as server
23
+ # Add apt-get system dependecies for runtime here if needed
24
+ COPY --from=build /venv/ /venv/
25
+ ENV PATH=/venv/bin:$PATH
26
+ RUN pip install textual-dev
27
+
28
+ # change this entrypoint if it is not the same as the repo
29
+ ENTRYPOINT ["textual", "serve"] # Usage: "ec -b DEMO monitor" -p 8081
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: edge-containers-cli
3
- Version: 4.0.0
3
+ Version: 4.1.1b0
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
@@ -23,15 +23,22 @@ def version_callback(value: bool):
23
23
 
24
24
  def backend_callback(ctx: typer.Context, backend: ECBackends):
25
25
  init_backend(backend)
26
+
26
27
  # Dynamically drop any method not implemented
27
28
  not_implemented = [
28
- mthd.replace("_", "-") for mthd in ec_backend.get_notimplemented()
29
+ mthd.replace("_", "-") for mthd in ec_backend.get_notimplemented_cmds()
29
30
  ]
31
+ typer_commands = ctx.command.commands # type: ignore
30
32
  for command in not_implemented:
31
- typer_commands = ctx.command.commands # type: ignore
32
33
  if command in typer_commands:
33
34
  typer_commands.pop(command)
34
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
+
35
42
  return backend.value
36
43
 
37
44
 
@@ -12,5 +12,5 @@ __version__: str
12
12
  __version_tuple__: VERSION_TUPLE
13
13
  version_tuple: VERSION_TUPLE
14
14
 
15
- __version__ = version = '4.0.0'
16
- __version_tuple__ = version_tuple = (4, 0, 0)
15
+ __version__ = version = '4.1.1b0'
16
+ __version_tuple__ = version_tuple = (4, 1, 1)
@@ -4,6 +4,7 @@ Manage a backend for the project
4
4
 
5
5
  from edge_containers_cli.cmds.argo_commands import ArgoCommands
6
6
  from edge_containers_cli.cmds.commands import Commands
7
+ from edge_containers_cli.cmds.demo_commands import DemoCommands
7
8
  from edge_containers_cli.cmds.k8s_commands import K8sCommands
8
9
  from edge_containers_cli.definitions import ECBackends, ECContext
9
10
  from edge_containers_cli.utils import public_methods
@@ -20,6 +21,13 @@ class Backend:
20
21
  self._Commands: type | None = None
21
22
  self._commands: Commands | None = None
22
23
 
24
+ @property
25
+ def Commands(self):
26
+ if self._Commands is None:
27
+ raise BackendError("Backend commands not set")
28
+ else:
29
+ return self._Commands
30
+
23
31
  @property
24
32
  def commands(self):
25
33
  if self._commands is None:
@@ -29,10 +37,13 @@ class Backend:
29
37
 
30
38
  def set_backend(self, backend: ECBackends):
31
39
  self._value = backend
32
- if backend == ECBackends.K8S:
33
- self._Commands = K8sCommands
34
- elif backend == ECBackends.ARGOCD:
35
- self._Commands = ArgoCommands
40
+ match backend:
41
+ case ECBackends.DEMO:
42
+ self._Commands = DemoCommands
43
+ case ECBackends.K8S:
44
+ self._Commands = K8sCommands
45
+ case ECBackends.ARGOCD:
46
+ self._Commands = ArgoCommands
36
47
 
37
48
  def set_context(self, context: ECContext):
38
49
  """
@@ -44,7 +55,7 @@ class Backend:
44
55
  self._cxt = context
45
56
  self._commands = self._Commands(context)
46
57
 
47
- def get_notimplemented(self) -> list[str]:
58
+ def get_notimplemented_cmds(self) -> list[str]:
48
59
  notimplemented = []
49
60
  if self._Commands is None:
50
61
  return []
@@ -54,6 +65,9 @@ class Backend:
54
65
  notimplemented.append(command)
55
66
  return notimplemented
56
67
 
68
+ def get_notimplemented_params(self) -> dict[str, list[str]]:
69
+ return self.Commands.params_opt_out
70
+
57
71
 
58
72
  backend = Backend()
59
73
 
@@ -234,9 +234,14 @@ def start(
234
234
  service_name: str = typer.Argument(
235
235
  ..., help="Name of the service container to start", autocompletion=all_svc
236
236
  ),
237
+ temp: bool = typer.Option(False, help="Directly overrides the controller values"),
237
238
  ):
238
239
  """Start a service"""
239
- backend.commands.start(service_name)
240
+ try:
241
+ backend.commands.start(service_name, temp)
242
+ except GitError as e:
243
+ msg = f"{str(e)} - Try 'ec start <service> --temp' to bypass the git server"
244
+ raise GitError(msg) from e
240
245
 
241
246
 
242
247
  @cli.command()
@@ -246,9 +251,14 @@ def stop(
246
251
  help="Name of the service container to stop",
247
252
  autocompletion=running_svc,
248
253
  ),
254
+ temp: bool = typer.Option(False, help="Directly overrides the controller values"),
249
255
  ):
250
256
  """Stop a service"""
251
- backend.commands.stop(service_name)
257
+ try:
258
+ backend.commands.stop(service_name, temp)
259
+ except GitError as e:
260
+ msg = f"{str(e)} - Try ec stop <service> --temp to bypass the git server"
261
+ raise GitError(msg) from e
252
262
 
253
263
 
254
264
  @cli.command()
@@ -6,12 +6,14 @@ Relies on the Helm class for deployment aspects.
6
6
 
7
7
  import webbrowser
8
8
  from datetime import datetime
9
+ from pathlib import Path
9
10
 
10
11
  import polars
11
12
  from ruamel.yaml import YAML
12
13
 
13
14
  from edge_containers_cli.cmds.commands import CommandError, Commands, ServicesDataFrame
14
15
  from edge_containers_cli.definitions import ECContext
16
+ from edge_containers_cli.git import set_values
15
17
  from edge_containers_cli.globals import TIME_FORMAT
16
18
  from edge_containers_cli.shell import ShellError, shell
17
19
 
@@ -21,6 +23,36 @@ def extract_ns_app(target: str) -> tuple[str, str]:
21
23
  return namespace, app
22
24
 
23
25
 
26
+ def patch_value(target: str, key: str, value: str | bool | int):
27
+ cmd_temp_ = f"argocd app set {target} -p {key}={value}"
28
+ shell.run_command(cmd_temp_, skip_on_dryrun=True)
29
+ cmd_sync = f"argocd app sync {target} --apply-out-of-sync-only"
30
+ shell.run_command(cmd_sync, skip_on_dryrun=True)
31
+
32
+
33
+ def push_value(target: str, key: str, value: str | bool | int):
34
+ # Get source details
35
+ app_resp = shell.run_command(
36
+ f"argocd app get {target} -o yaml",
37
+ )
38
+ app_dicts = YAML(typ="safe").load(app_resp)
39
+ repo_url = app_dicts["spec"]["source"]["repoURL"]
40
+ path = Path(app_dicts["spec"]["source"]["path"])
41
+
42
+ set_values(
43
+ repo_url,
44
+ path / "values.yaml",
45
+ key,
46
+ value,
47
+ )
48
+
49
+ # Sync & release a possible patched value
50
+ cmd_sync = f"argocd app sync {target} --apply-out-of-sync-only"
51
+ shell.run_command(cmd_sync, skip_on_dryrun=True)
52
+ cmd_unset = f"argocd app unset {target} -p {key}"
53
+ shell.run_command(cmd_unset, skip_on_dryrun=True)
54
+
55
+
24
56
  class ArgoCommands(Commands):
25
57
  """
26
58
  A class for implementing the Kubernetes based commands
@@ -52,17 +84,19 @@ class ArgoCommands(Commands):
52
84
  )
53
85
  shell.run_command(cmd, skip_on_dryrun=True)
54
86
 
55
- def start(self, service_name):
87
+ def start(self, service_name, temp):
56
88
  self._check_service(service_name)
57
- namespace, app = extract_ns_app(self.target)
58
- cmd = f"argocd app set {namespace}/{service_name} -p global.enabled=true"
59
- shell.run_command(cmd, skip_on_dryrun=True)
89
+ if temp:
90
+ patch_value(self.target, f"ec_services.{service_name}.enabled", True)
91
+ else:
92
+ push_value(self.target, f"ec_services.{service_name}.enabled", True)
60
93
 
61
- def stop(self, service_name):
94
+ def stop(self, service_name, temp):
62
95
  self._check_service(service_name)
63
- namespace, app = extract_ns_app(self.target)
64
- cmd = f"argocd app set {namespace}/{service_name} -p global.enabled=false"
65
- shell.run_command(cmd, skip_on_dryrun=True)
96
+ if temp:
97
+ patch_value(self.target, f"ec_services.{service_name}.enabled", False)
98
+ else:
99
+ push_value(self.target, f"ec_services.{service_name}.enabled", False)
66
100
 
67
101
  def _get_logs(self, service_name, prev) -> str:
68
102
  namespace, app = extract_ns_app(self.target)
@@ -39,6 +39,8 @@ class Commands(ABC):
39
39
  Methods not exposed to the CLI should be private
40
40
  """
41
41
 
42
+ params_opt_out: dict[str, list[str]] = {}
43
+
42
44
  def __init__(self, ctx: ECContext):
43
45
  self._target = ctx.target
44
46
  self._target_valid = False
@@ -72,50 +74,50 @@ class Commands(ABC):
72
74
  log.debug("log_url = %s", self._log_url)
73
75
  return self._log_url
74
76
 
75
- def attach(self, service_name: str):
77
+ def attach(self, service_name: str) -> None:
76
78
  raise NotImplementedError
77
79
 
78
- def delete(self, service_name: str):
80
+ def delete(self, service_name: str) -> None:
79
81
  raise NotImplementedError
80
82
 
81
- def deploy(self, service_name: str, version: str, args: str):
83
+ def deploy(self, service_name: str, version: str, args: str) -> None:
82
84
  raise NotImplementedError
83
85
 
84
- def deploy_local(self, svc_instance: Path, args: str):
86
+ def deploy_local(self, svc_instance: Path, args: str) -> None:
85
87
  raise NotImplementedError
86
88
 
87
- def exec(self, service_name: str):
89
+ def exec(self, service_name: str) -> None:
88
90
  raise NotImplementedError
89
91
 
90
- def logs(self, service_name: str, prev: bool):
92
+ def logs(self, service_name: str, prev: bool) -> None:
91
93
  raise NotImplementedError
92
94
 
93
- def log_history(self, service_name: str):
95
+ def log_history(self, service_name: str) -> None:
94
96
  raise NotImplementedError
95
97
 
96
- def ps(self, running_only: bool):
98
+ def ps(self, running_only: bool) -> None:
97
99
  raise NotImplementedError
98
100
 
99
101
  @abstractmethod
100
- def restart(self, service_name: str):
102
+ def restart(self, service_name: str) -> None:
101
103
  raise NotImplementedError
102
104
 
103
105
  @abstractmethod
104
- def start(self, service_name: str):
106
+ def start(self, service_name: str, temp: bool) -> None:
105
107
  raise NotImplementedError
106
108
 
107
109
  @abstractmethod
108
- def stop(self, service_name: str):
110
+ def stop(self, service_name: str, temp: bool) -> None:
109
111
  raise NotImplementedError
110
112
 
111
- def template(self, svc_instance: Path, args: str):
113
+ def template(self, svc_instance: Path, args: str) -> None:
112
114
  raise NotImplementedError
113
115
 
114
116
  @abstractmethod
115
117
  def _get_services(self, running_only: bool) -> ServicesDataFrame:
116
118
  raise NotImplementedError
117
119
 
118
- def _ps(self, running_only: bool):
120
+ def _ps(self, running_only: bool) -> None:
119
121
  services_df = self._get_services(running_only)
120
122
  print(services_df)
121
123
 
@@ -123,19 +125,19 @@ class Commands(ABC):
123
125
  def _get_logs(self, service_name: str, prev: bool) -> str:
124
126
  raise NotImplementedError
125
127
 
126
- def _logs(self, service_name: str, prev: bool):
128
+ def _logs(self, service_name: str, prev: bool) -> None:
127
129
  print(self._get_logs(service_name, prev))
128
130
 
129
- def _validate_target(self):
131
+ def _validate_target(self) -> None:
130
132
  raise NotImplementedError
131
133
 
132
- def _running_services(self):
134
+ def _running_services(self) -> list[str]:
133
135
  return self._get_services(running_only=True)["name"].to_list()
134
136
 
135
- def _all_services(self):
137
+ def _all_services(self) -> list[str]:
136
138
  return self._get_services(running_only=False)["name"].to_list()
137
139
 
138
- def _check_service(self, service_name: str):
140
+ def _check_service(self, service_name: str) -> None:
139
141
  services_list = self._get_services(running_only=False)["name"]
140
142
  if service_name in services_list:
141
143
  pass
@@ -0,0 +1,135 @@
1
+ """
2
+ implements commands for deploying and managing service instances suing argocd
3
+
4
+ Relies on the Helm class for deployment aspects.
5
+ """
6
+
7
+ import time
8
+ from datetime import datetime
9
+
10
+ import polars
11
+
12
+ from edge_containers_cli.cmds.commands import CommandError, Commands, ServicesDataFrame
13
+ from edge_containers_cli.definitions import ECContext
14
+ from edge_containers_cli.globals import TIME_FORMAT
15
+
16
+ DELAY = 0.0
17
+
18
+
19
+ def process_t(time_string) -> str:
20
+ time_stamp = datetime.strptime(time_string, "%Y-%m-%dT%H:%M:%SZ")
21
+ return datetime.strftime(time_stamp, TIME_FORMAT)
22
+
23
+
24
+ sample_data = {
25
+ "name": ["demo-ea-01", "demo-ea-02", "demo-ea-03"], # type: ignore
26
+ "version": ["2024.10.1", "2024.10.1b", "2024.10.1"],
27
+ "ready": [True, True, False],
28
+ "deployed": [
29
+ process_t("2024-10-22T11:23:10Z"),
30
+ process_t("2024-10-28T14:53:55Z"),
31
+ process_t("2024-10-22T12:51:50Z"),
32
+ ],
33
+ }
34
+ sample_ServicesDataFrame = ServicesDataFrame(polars.from_dict(sample_data))
35
+
36
+
37
+ def demo_wrapper():
38
+ """Using closure to display once"""
39
+ called = False
40
+
41
+ def decorator(function):
42
+ """Called selectively to avoid breaking autocompletion"""
43
+
44
+ def wrapper(*args, **kwargs):
45
+ nonlocal called
46
+ if not called:
47
+ called = True
48
+ print("***RUNNING IN DEMO MODE***")
49
+ return function(*args, **kwargs)
50
+
51
+ return wrapper
52
+
53
+ return decorator
54
+
55
+
56
+ demo_message = demo_wrapper()
57
+
58
+
59
+ class DemoCommands(Commands):
60
+ """
61
+ A class for implementing the Kubernetes based commands
62
+ """
63
+
64
+ def __init__(
65
+ self,
66
+ ctx: ECContext,
67
+ ):
68
+ self._target = "Demo Beamline"
69
+ self._target_valid = False
70
+ self._stateDF = sample_ServicesDataFrame
71
+
72
+ @demo_message
73
+ def logs(self, service_name, prev):
74
+ self._logs(service_name, prev)
75
+
76
+ @demo_message
77
+ def log_history(self, service_name):
78
+ pass
79
+
80
+ @demo_message
81
+ def ps(self, running_only):
82
+ self._ps(running_only)
83
+
84
+ @demo_message
85
+ def restart(self, service_name):
86
+ self.stop(service_name, False)
87
+ self.start(service_name, False)
88
+
89
+ @demo_message
90
+ def start(self, service_name, temp):
91
+ self._check_service(service_name)
92
+ time.sleep(DELAY)
93
+ self._stateDF = self._stateDF.with_columns(
94
+ polars.when(polars.col("name") == service_name)
95
+ .then(True)
96
+ .otherwise(polars.col("ready"))
97
+ .alias("ready")
98
+ )
99
+
100
+ @demo_message
101
+ def stop(self, service_name, temp):
102
+ self._check_service(service_name)
103
+ time.sleep(DELAY)
104
+ self._stateDF = self._stateDF.with_columns(
105
+ polars.when(polars.col("name") == service_name)
106
+ .then(False)
107
+ .otherwise(polars.col("ready"))
108
+ .alias("ready")
109
+ )
110
+
111
+ def _get_logs(self, service_name, prev) -> str:
112
+ self._check_service(service_name)
113
+ logs_list = ["Lorem ipsum dolor sit amet"] * 25
114
+ return "\n".join(logs_list)
115
+
116
+ def _get_services(self, running_only) -> ServicesDataFrame:
117
+ if running_only:
118
+ return ServicesDataFrame(self._stateDF.filter(polars.col("ready").eq(True)))
119
+ else:
120
+ return ServicesDataFrame(self._stateDF)
121
+
122
+ def _check_service(self, service_name: str):
123
+ """
124
+ validate that there is a app with the given service_name
125
+ """
126
+ services_list = self._get_services(running_only=False)["name"]
127
+ if service_name in services_list:
128
+ pass
129
+ else:
130
+ raise CommandError(
131
+ f"Service '{service_name}' not found in '{self._target}'"
132
+ )
133
+
134
+ def _validate_target(self):
135
+ pass
@@ -23,6 +23,11 @@ class K8sCommands(Commands):
23
23
  A class for implementing the Kubernetes based commands
24
24
  """
25
25
 
26
+ params_opt_out = {
27
+ "stop": ["temp"],
28
+ "start": ["temp"],
29
+ }
30
+
26
31
  def __init__(
27
32
  self,
28
33
  ctx: ECContext,
@@ -84,14 +89,14 @@ class K8sCommands(Commands):
84
89
  f"kubectl delete -n {self.target} {pod_name}", skip_on_dryrun=True
85
90
  )
86
91
 
87
- def start(self, service_name):
92
+ def start(self, service_name, temp):
88
93
  self._check_service(service_name)
89
94
  shell.run_command(
90
95
  f"kubectl scale -n {self.target} statefulset {service_name} --replicas=1",
91
96
  skip_on_dryrun=True,
92
97
  )
93
98
 
94
- def stop(self, service_name):
99
+ def stop(self, service_name, temp):
95
100
  self._check_service(service_name)
96
101
  shell.run_command(
97
102
  f"kubectl scale -n {self.target} statefulset {service_name} --replicas=0 ",
@@ -385,7 +385,7 @@ class MonitorApp(App):
385
385
  Binding("r", "restart_ioc", "Restart IOC"),
386
386
  Binding("l", "ioc_logs", "IOC Logs"),
387
387
  Binding("o", "sort", "Sort"),
388
- # Binding("d", "toggle_dark", "Toggle dark mode"),
388
+ Binding("d", "toggle_dark", "Toggle dark mode"),
389
389
  ]
390
390
 
391
391
  def __init__(
@@ -446,7 +446,7 @@ class MonitorApp(App):
446
446
  def check_start(start: bool | None) -> None:
447
447
  """Called when StartScreen is dismissed."""
448
448
  if start:
449
- self.commands.start(service_name)
449
+ self.commands.start(service_name, False)
450
450
 
451
451
  self.push_screen(StartScreen(service_name), check_start)
452
452
 
@@ -457,7 +457,7 @@ class MonitorApp(App):
457
457
  def check_stop(stop: bool | None) -> None:
458
458
  """Called when StopScreen is dismissed."""
459
459
  if stop:
460
- self.commands.stop(service_name)
460
+ self.commands.stop(service_name, False)
461
461
 
462
462
  self.push_screen(StopScreen(service_name), check_stop)
463
463
 
@@ -5,6 +5,7 @@ from enum import Enum
5
5
  class ECBackends(str, Enum):
6
6
  ARGOCD = "ARGOCD"
7
7
  K8S = "K8S"
8
+ DEMO = "DEMO"
8
9
 
9
10
 
10
11
  class ECLogLevels(str, Enum):
@@ -9,14 +9,40 @@ import polars
9
9
  from natsort import natsorted
10
10
 
11
11
  from edge_containers_cli.logging import log
12
- from edge_containers_cli.shell import shell
13
- from edge_containers_cli.utils import chdir, new_workdir
12
+ from edge_containers_cli.shell import ShellError, shell
13
+ from edge_containers_cli.utils import YamlFile, chdir, new_workdir
14
14
 
15
15
 
16
16
  class GitError(Exception):
17
17
  pass
18
18
 
19
19
 
20
+ def set_values(repo_url: str, file: Path, key: str, value: str | bool | int) -> None:
21
+ """
22
+ sets an existing key value pair in a yaml file and push the changes
23
+ """
24
+ with new_workdir() as path:
25
+ try:
26
+ shell.run_command(f"git clone --depth=1 {repo_url} {path}")
27
+ with chdir(path): # From python 3.11 can use contextlib.chdir(working_dir)
28
+ file_data = YamlFile(file)
29
+
30
+ value_repo = file_data.get_key(key)
31
+ if value_repo == value:
32
+ log.debug(f"{key} already set as {value}")
33
+ else:
34
+ file_data.set_key(key, value)
35
+ file_data.dump_file()
36
+
37
+ commit_msg = f"Set {key}={value} in {file}"
38
+ shell.run_command("git add .")
39
+ shell.run_command(f'git commit -m "{commit_msg}"')
40
+ shell.run_command("git push", skip_on_dryrun=True)
41
+
42
+ except (FileNotFoundError, ShellError) as e:
43
+ raise GitError(str(e)) from e
44
+
45
+
20
46
  def create_version_map(
21
47
  repo: str, root_dir: Path, working_dir: Path, shared: list[str] | None = None
22
48
  ) -> dict[str, list[str]]: