edge-containers-cli 4.2.1__tar.gz → 4.3.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 (107) hide show
  1. {edge_containers_cli-4.2.1 → edge_containers_cli-4.3.0}/.copier-answers.yml +1 -1
  2. {edge_containers_cli-4.2.1 → edge_containers_cli-4.3.0}/.devcontainer/devcontainer.json +4 -4
  3. {edge_containers_cli-4.2.1 → edge_containers_cli-4.3.0}/.github/CONTRIBUTING.md +1 -1
  4. {edge_containers_cli-4.2.1 → edge_containers_cli-4.3.0}/.github/workflows/_pypi.yml +2 -0
  5. {edge_containers_cli-4.2.1 → edge_containers_cli-4.3.0}/.github/workflows/_release.yml +1 -1
  6. {edge_containers_cli-4.2.1 → edge_containers_cli-4.3.0}/.github/workflows/_test.yml +1 -1
  7. {edge_containers_cli-4.2.1 → edge_containers_cli-4.3.0}/.vscode/launch.json +13 -0
  8. {edge_containers_cli-4.2.1 → edge_containers_cli-4.3.0}/Dockerfile +1 -1
  9. {edge_containers_cli-4.2.1 → edge_containers_cli-4.3.0}/PKG-INFO +3 -3
  10. {edge_containers_cli-4.2.1 → edge_containers_cli-4.3.0}/README.md +1 -1
  11. {edge_containers_cli-4.2.1 → edge_containers_cli-4.3.0}/src/edge_containers_cli/_version.py +9 -4
  12. {edge_containers_cli-4.2.1 → edge_containers_cli-4.3.0}/src/edge_containers_cli/cli.py +2 -2
  13. {edge_containers_cli-4.2.1 → edge_containers_cli-4.3.0}/src/edge_containers_cli/cmds/argo_commands.py +80 -49
  14. {edge_containers_cli-4.2.1 → edge_containers_cli-4.3.0}/src/edge_containers_cli/cmds/commands.py +30 -2
  15. {edge_containers_cli-4.2.1 → edge_containers_cli-4.3.0}/src/edge_containers_cli/cmds/demo_commands.py +29 -12
  16. {edge_containers_cli-4.2.1 → edge_containers_cli-4.3.0}/src/edge_containers_cli/cmds/helm.py +1 -1
  17. {edge_containers_cli-4.2.1 → edge_containers_cli-4.3.0}/src/edge_containers_cli/cmds/k8s_commands.py +33 -21
  18. {edge_containers_cli-4.2.1 → edge_containers_cli-4.3.0}/src/edge_containers_cli/cmds/monitor.py +245 -178
  19. {edge_containers_cli-4.2.1 → edge_containers_cli-4.3.0}/src/edge_containers_cli/cmds/monitor.tcss +12 -8
  20. {edge_containers_cli-4.2.1 → edge_containers_cli-4.3.0}/src/edge_containers_cli/definitions.py +9 -0
  21. {edge_containers_cli-4.2.1 → edge_containers_cli-4.3.0}/src/edge_containers_cli/git.py +3 -1
  22. {edge_containers_cli-4.2.1 → edge_containers_cli-4.3.0}/src/edge_containers_cli/logging.py +1 -1
  23. {edge_containers_cli-4.2.1 → edge_containers_cli-4.3.0}/src/edge_containers_cli/shell.py +1 -0
  24. {edge_containers_cli-4.2.1 → edge_containers_cli-4.3.0}/src/edge_containers_cli/utils.py +22 -5
  25. {edge_containers_cli-4.2.1 → edge_containers_cli-4.3.0}/src/edge_containers_cli.egg-info/PKG-INFO +3 -3
  26. {edge_containers_cli-4.2.1 → edge_containers_cli-4.3.0}/tests/data/argocd.yaml +4 -8
  27. {edge_containers_cli-4.2.1 → edge_containers_cli-4.3.0}/tests/data/yaml.yaml +1 -1
  28. edge_containers_cli-4.3.0/tests/test_demo.py +37 -0
  29. {edge_containers_cli-4.2.1 → edge_containers_cli-4.3.0}/tests/test_unit.py +9 -4
  30. edge_containers_cli-4.2.1/tests/test_demo.py +0 -32
  31. {edge_containers_cli-4.2.1 → edge_containers_cli-4.3.0}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
  32. {edge_containers_cli-4.2.1 → edge_containers_cli-4.3.0}/.github/ISSUE_TEMPLATE/issue.md +0 -0
  33. {edge_containers_cli-4.2.1 → edge_containers_cli-4.3.0}/.github/PULL_REQUEST_TEMPLATE/pull_request_template.md +0 -0
  34. {edge_containers_cli-4.2.1 → edge_containers_cli-4.3.0}/.github/actions/install_requirements/action.yml +0 -0
  35. {edge_containers_cli-4.2.1 → edge_containers_cli-4.3.0}/.github/dependabot.yml +0 -0
  36. {edge_containers_cli-4.2.1 → edge_containers_cli-4.3.0}/.github/pages/index.html +0 -0
  37. {edge_containers_cli-4.2.1 → edge_containers_cli-4.3.0}/.github/pages/make_switcher.py +0 -0
  38. {edge_containers_cli-4.2.1 → edge_containers_cli-4.3.0}/.github/workflows/_check.yml +0 -0
  39. {edge_containers_cli-4.2.1 → edge_containers_cli-4.3.0}/.github/workflows/_dist.yml +0 -0
  40. {edge_containers_cli-4.2.1 → edge_containers_cli-4.3.0}/.github/workflows/_tox.yml +0 -0
  41. {edge_containers_cli-4.2.1 → edge_containers_cli-4.3.0}/.github/workflows/ci.yml +0 -0
  42. {edge_containers_cli-4.2.1 → edge_containers_cli-4.3.0}/.gitignore +0 -0
  43. {edge_containers_cli-4.2.1 → edge_containers_cli-4.3.0}/.pre-commit-config.yaml +0 -0
  44. {edge_containers_cli-4.2.1 → edge_containers_cli-4.3.0}/.vscode/extensions.json +0 -0
  45. {edge_containers_cli-4.2.1 → edge_containers_cli-4.3.0}/.vscode/settings.json +0 -0
  46. {edge_containers_cli-4.2.1 → edge_containers_cli-4.3.0}/.vscode/tasks.json +0 -0
  47. {edge_containers_cli-4.2.1 → edge_containers_cli-4.3.0}/LICENSE +0 -0
  48. {edge_containers_cli-4.2.1 → edge_containers_cli-4.3.0}/docs/conf.py +0 -0
  49. {edge_containers_cli-4.2.1 → edge_containers_cli-4.3.0}/docs/developer/explanations/decisions/0001-record-architecture-decisions.rst +0 -0
  50. {edge_containers_cli-4.2.1 → edge_containers_cli-4.3.0}/docs/developer/explanations/decisions/0002-switched-to-pip-skeleton.rst +0 -0
  51. {edge_containers_cli-4.2.1 → edge_containers_cli-4.3.0}/docs/developer/explanations/decisions.rst +0 -0
  52. {edge_containers_cli-4.2.1 → edge_containers_cli-4.3.0}/docs/developer/how-to/build-docs.rst +0 -0
  53. {edge_containers_cli-4.2.1 → edge_containers_cli-4.3.0}/docs/developer/how-to/contribute.rst +0 -0
  54. {edge_containers_cli-4.2.1 → edge_containers_cli-4.3.0}/docs/developer/how-to/lint.rst +0 -0
  55. {edge_containers_cli-4.2.1 → edge_containers_cli-4.3.0}/docs/developer/how-to/make-release.rst +0 -0
  56. {edge_containers_cli-4.2.1 → edge_containers_cli-4.3.0}/docs/developer/how-to/pin-requirements.rst +0 -0
  57. {edge_containers_cli-4.2.1 → edge_containers_cli-4.3.0}/docs/developer/how-to/run-tests.rst +0 -0
  58. {edge_containers_cli-4.2.1 → edge_containers_cli-4.3.0}/docs/developer/how-to/static-analysis.rst +0 -0
  59. {edge_containers_cli-4.2.1 → edge_containers_cli-4.3.0}/docs/developer/how-to/test-container.rst +0 -0
  60. {edge_containers_cli-4.2.1 → edge_containers_cli-4.3.0}/docs/developer/how-to/update-tools.rst +0 -0
  61. {edge_containers_cli-4.2.1 → edge_containers_cli-4.3.0}/docs/developer/index.rst +0 -0
  62. {edge_containers_cli-4.2.1 → edge_containers_cli-4.3.0}/docs/developer/reference/standards.rst +0 -0
  63. {edge_containers_cli-4.2.1 → edge_containers_cli-4.3.0}/docs/developer/tutorials/dev-install.rst +0 -0
  64. {edge_containers_cli-4.2.1 → edge_containers_cli-4.3.0}/docs/genindex.rst +0 -0
  65. {edge_containers_cli-4.2.1 → edge_containers_cli-4.3.0}/docs/images/dls-favicon.ico +0 -0
  66. {edge_containers_cli-4.2.1 → edge_containers_cli-4.3.0}/docs/images/dls-logo.svg +0 -0
  67. {edge_containers_cli-4.2.1 → edge_containers_cli-4.3.0}/docs/images/ec-logs.png +0 -0
  68. {edge_containers_cli-4.2.1 → edge_containers_cli-4.3.0}/docs/images/ec-web.png +0 -0
  69. {edge_containers_cli-4.2.1 → edge_containers_cli-4.3.0}/docs/index.rst +0 -0
  70. {edge_containers_cli-4.2.1 → edge_containers_cli-4.3.0}/docs/user/explanations/docs-structure.rst +0 -0
  71. {edge_containers_cli-4.2.1 → edge_containers_cli-4.3.0}/docs/user/how-to/run-container.rst +0 -0
  72. {edge_containers_cli-4.2.1 → edge_containers_cli-4.3.0}/docs/user/index.rst +0 -0
  73. {edge_containers_cli-4.2.1 → edge_containers_cli-4.3.0}/docs/user/reference/api.rst +0 -0
  74. {edge_containers_cli-4.2.1 → edge_containers_cli-4.3.0}/docs/user/tutorials/installation.rst +0 -0
  75. {edge_containers_cli-4.2.1 → edge_containers_cli-4.3.0}/pyproject.toml +0 -0
  76. {edge_containers_cli-4.2.1 → edge_containers_cli-4.3.0}/setup.cfg +0 -0
  77. {edge_containers_cli-4.2.1 → edge_containers_cli-4.3.0}/src/edge_containers_cli/__init__.py +0 -0
  78. {edge_containers_cli-4.2.1 → edge_containers_cli-4.3.0}/src/edge_containers_cli/__main__.py +0 -0
  79. {edge_containers_cli-4.2.1 → edge_containers_cli-4.3.0}/src/edge_containers_cli/autocomplete.py +0 -0
  80. {edge_containers_cli-4.2.1 → edge_containers_cli-4.3.0}/src/edge_containers_cli/backend.py +0 -0
  81. {edge_containers_cli-4.2.1 → edge_containers_cli-4.3.0}/src/edge_containers_cli/cmds/__init__.py +0 -0
  82. {edge_containers_cli-4.2.1 → edge_containers_cli-4.3.0}/src/edge_containers_cli/globals.py +0 -0
  83. {edge_containers_cli-4.2.1 → edge_containers_cli-4.3.0}/src/edge_containers_cli.egg-info/SOURCES.txt +0 -0
  84. {edge_containers_cli-4.2.1 → edge_containers_cli-4.3.0}/src/edge_containers_cli.egg-info/dependency_links.txt +0 -0
  85. {edge_containers_cli-4.2.1 → edge_containers_cli-4.3.0}/src/edge_containers_cli.egg-info/entry_points.txt +0 -0
  86. {edge_containers_cli-4.2.1 → edge_containers_cli-4.3.0}/src/edge_containers_cli.egg-info/requires.txt +0 -0
  87. {edge_containers_cli-4.2.1 → edge_containers_cli-4.3.0}/src/edge_containers_cli.egg-info/top_level.txt +0 -0
  88. {edge_containers_cli-4.2.1 → edge_containers_cli-4.3.0}/tests/__init__.py +0 -0
  89. {edge_containers_cli-4.2.1 → edge_containers_cli-4.3.0}/tests/conftest.py +0 -0
  90. {edge_containers_cli-4.2.1 → edge_containers_cli-4.3.0}/tests/data/bl01t-deployment/apps/values.yaml +0 -0
  91. {edge_containers_cli-4.2.1 → edge_containers_cli-4.3.0}/tests/data/bl01t-services/services/bl01t-ea-test-01/Chart.yaml +0 -0
  92. {edge_containers_cli-4.2.1 → edge_containers_cli-4.3.0}/tests/data/bl01t-services/services/bl01t-ea-test-01/config/ioc.db +0 -0
  93. {edge_containers_cli-4.2.1 → edge_containers_cli-4.3.0}/tests/data/bl01t-services/services/bl01t-ea-test-01/config/ioc.yaml +0 -0
  94. {edge_containers_cli-4.2.1 → edge_containers_cli-4.3.0}/tests/data/bl01t-services/services/bl01t-ea-test-01/templates/configmap.yaml +0 -0
  95. {edge_containers_cli-4.2.1 → edge_containers_cli-4.3.0}/tests/data/bl01t-services/services/bl01t-ea-test-01/values.yaml +0 -0
  96. {edge_containers_cli-4.2.1 → edge_containers_cli-4.3.0}/tests/data/bl01t-services/services/bl01t-ea-test-02/Chart.yaml +0 -0
  97. {edge_containers_cli-4.2.1 → edge_containers_cli-4.3.0}/tests/data/bl01t-services/services/bl01t-ea-test-02/values.yaml +0 -0
  98. {edge_containers_cli-4.2.1 → edge_containers_cli-4.3.0}/tests/data/bl01t-services/services/dls-aravis/Chart.yaml +0 -0
  99. {edge_containers_cli-4.2.1 → edge_containers_cli-4.3.0}/tests/data/bl01t-services/services/dls-aravis/config/ioc.yaml +0 -0
  100. {edge_containers_cli-4.2.1 → edge_containers_cli-4.3.0}/tests/data/bl01t-services/services/dls-aravis/templates/configmap.yaml +0 -0
  101. {edge_containers_cli-4.2.1 → edge_containers_cli-4.3.0}/tests/data/bl01t-services/services/dls-aravis/values.yaml +0 -0
  102. {edge_containers_cli-4.2.1 → edge_containers_cli-4.3.0}/tests/data/bl01t-services/services/values.yaml +0 -0
  103. {edge_containers_cli-4.2.1 → edge_containers_cli-4.3.0}/tests/data/cli.yaml +0 -0
  104. {edge_containers_cli-4.2.1 → edge_containers_cli-4.3.0}/tests/data/k8s.yaml +0 -0
  105. {edge_containers_cli-4.2.1 → edge_containers_cli-4.3.0}/tests/test_argocd.py +0 -0
  106. {edge_containers_cli-4.2.1 → edge_containers_cli-4.3.0}/tests/test_cli.py +0 -0
  107. {edge_containers_cli-4.2.1 → edge_containers_cli-4.3.0}/tests/test_k8s.py +0 -0
@@ -1,5 +1,5 @@
1
1
  # Changes here will be overwritten by Copier
2
- _commit: 2.3.0
2
+ _commit: 2.6.0
3
3
  _src_path: gh:DiamondLightSource/python-copier-template
4
4
  author_email: giles.knap@gmail.com
5
5
  author_name: Giles Knap
@@ -28,11 +28,11 @@
28
28
  }
29
29
  },
30
30
  "features": {
31
- // Some default things like git config
32
- "ghcr.io/devcontainers/features/common-utils:2": {
33
- "upgradePackages": false
34
- }
31
+ // add in eternal history and other bash features
32
+ "ghcr.io/diamondlightsource/devcontainer-features/bash-config:1.0.0": {}
35
33
  },
34
+ // Create the config folder for the bash-config feature
35
+ "initializeCommand": "mkdir -p ${localEnv:HOME}/.config/bash-config",
36
36
  "runArgs": [
37
37
  // Allow the container to access the host X11 display and EPICS CA
38
38
  "--net=host",
@@ -24,4 +24,4 @@ It is recommended that developers use a [vscode devcontainer](https://code.visua
24
24
 
25
25
  This project was created using the [Diamond Light Source Copier Template](https://github.com/DiamondLightSource/python-copier-template) for Python projects.
26
26
 
27
- For more information on common tasks like setting up a developer environment, running the tests, and setting a pre-commit hook, see the template's [How-to guides](https://diamondlightsource.github.io/python-copier-template/2.3.0/how-to.html).
27
+ For more information on common tasks like setting up a developer environment, running the tests, and setting a pre-commit hook, see the template's [How-to guides](https://diamondlightsource.github.io/python-copier-template/2.6.0/how-to.html).
@@ -15,3 +15,5 @@ jobs:
15
15
 
16
16
  - name: Publish to PyPI using trusted publishing
17
17
  uses: pypa/gh-action-pypi-publish@release/v1
18
+ with:
19
+ attestations: false
@@ -23,7 +23,7 @@ jobs:
23
23
  - name: Create GitHub Release
24
24
  # We pin to the SHA, not the tag, for security reasons.
25
25
  # https://docs.github.com/en/actions/learn-github-actions/security-hardening-for-github-actions#using-third-party-actions
26
- uses: softprops/action-gh-release@c062e08bd532815e2082a85e87e3ef29c3e6d191 # v2.0.8
26
+ uses: softprops/action-gh-release@c95fe1489396fe8a9eb87c0abf8aa5b2ef267fda # v2.2.1
27
27
  with:
28
28
  prerelease: ${{ contains(github.ref_name, 'a') || contains(github.ref_name, 'b') || contains(github.ref_name, 'rc') }}
29
29
  files: "*"
@@ -54,7 +54,7 @@ jobs:
54
54
  run: tox -e tests
55
55
 
56
56
  - name: Upload coverage to Codecov
57
- uses: codecov/codecov-action@v4
57
+ uses: codecov/codecov-action@v5
58
58
  with:
59
59
  name: ${{ inputs.python-version }}/${{ inputs.runs-on }}
60
60
  files: cov.xml
@@ -18,6 +18,19 @@
18
18
  // Enable break on exception when debugging tests (see: tests/conftest.py)
19
19
  "PYTEST_RAISE": "1",
20
20
  },
21
+ },
22
+ {
23
+ "name": "Debug Monitor",
24
+ "type": "debugpy",
25
+ "request": "launch",
26
+ "module": "edge_containers_cli",
27
+ "args": [
28
+ "monitor"
29
+ ],
30
+ "console": "integratedTerminal",
31
+ "env": {
32
+ "EC_CLI_BACKEND": "DEMO",
33
+ },
21
34
  }
22
35
  ]
23
36
  }
@@ -1,7 +1,7 @@
1
1
  # The devcontainer should use the developer target and run as root with podman
2
2
  # or docker with user namespaces.
3
3
  ARG PYTHON_VERSION=3.11
4
- FROM python:${PYTHON_VERSION} as developer
4
+ FROM python:${PYTHON_VERSION} AS developer
5
5
 
6
6
  # Add any system dependencies for the developer/build environment here
7
7
  RUN apt-get update && apt-get install -y --no-install-recommends \
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.2
2
2
  Name: edge-containers-cli
3
- Version: 4.2.1
3
+ Version: 4.3.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
@@ -236,7 +236,7 @@ Requires-Dist: types-mock; extra == "dev"
236
236
  [![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
237
  [![Coverage](https://codecov.io/gh/epics-containers/edge-containers-cli/branch/main/graph/badge.svg)](https://codecov.io/gh/epics-containers/edge-containers-cli)
238
238
  [![PyPI](https://img.shields.io/pypi/v/edge-containers-cli.svg)](https://pypi.org/project/edge-containers-cli)
239
- [![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://opensource.org/licenses/Apache-2.0)
239
+ [![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://www.apache.org/licenses/LICENSE-2.0)
240
240
 
241
241
  # edge_containers_cli
242
242
 
@@ -1,7 +1,7 @@
1
1
  [![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)
2
2
  [![Coverage](https://codecov.io/gh/epics-containers/edge-containers-cli/branch/main/graph/badge.svg)](https://codecov.io/gh/epics-containers/edge-containers-cli)
3
3
  [![PyPI](https://img.shields.io/pypi/v/edge-containers-cli.svg)](https://pypi.org/project/edge-containers-cli)
4
- [![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://opensource.org/licenses/Apache-2.0)
4
+ [![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://www.apache.org/licenses/LICENSE-2.0)
5
5
 
6
6
  # edge_containers_cli
7
7
 
@@ -1,8 +1,13 @@
1
- # file generated by setuptools_scm
1
+ # file generated by setuptools-scm
2
2
  # don't change, don't track in version control
3
+
4
+ __all__ = ["__version__", "__version_tuple__", "version", "version_tuple"]
5
+
3
6
  TYPE_CHECKING = False
4
7
  if TYPE_CHECKING:
5
- from typing import Tuple, Union
8
+ from typing import Tuple
9
+ from typing import Union
10
+
6
11
  VERSION_TUPLE = Tuple[Union[int, str], ...]
7
12
  else:
8
13
  VERSION_TUPLE = object
@@ -12,5 +17,5 @@ __version__: str
12
17
  __version_tuple__: VERSION_TUPLE
13
18
  version_tuple: VERSION_TUPLE
14
19
 
15
- __version__ = version = '4.2.1'
16
- __version_tuple__ = version_tuple = (4, 2, 1)
20
+ __version__ = version = '4.3.0'
21
+ __version_tuple__ = version_tuple = (4, 3, 0)
@@ -238,7 +238,7 @@ def start(
238
238
  ):
239
239
  """Start a service"""
240
240
  try:
241
- backend.commands.start(service_name, commit)
241
+ backend.commands.start(service_name, commit=commit)
242
242
  except GitError as e:
243
243
  msg = f"{str(e)} - Commit failed. Try 'ec start <service> --no-commit to set values without updating git"
244
244
  raise GitError(msg) from e
@@ -255,7 +255,7 @@ def stop(
255
255
  ):
256
256
  """Stop a service"""
257
257
  try:
258
- backend.commands.stop(service_name, commit)
258
+ backend.commands.stop(service_name, commit=commit)
259
259
  except GitError as e:
260
260
  msg = f"{str(e)} - Commit failed. Try ec stop <service> --no-commit to set values without updating git"
261
261
  raise GitError(msg) from e
@@ -7,14 +7,21 @@ Relies on the Helm class for deployment aspects.
7
7
  import webbrowser
8
8
  from datetime import datetime
9
9
  from pathlib import Path
10
+ from time import sleep
10
11
 
11
12
  import polars
12
13
  from ruamel.yaml import YAML
13
14
 
14
- from edge_containers_cli.cmds.commands import CommandError, Commands, ServicesDataFrame
15
+ from edge_containers_cli.cmds.commands import (
16
+ CommandError,
17
+ Commands,
18
+ ServicesDataFrame,
19
+ ServicesSchema,
20
+ )
15
21
  from edge_containers_cli.definitions import ECContext
16
22
  from edge_containers_cli.git import set_values
17
23
  from edge_containers_cli.globals import TIME_FORMAT
24
+ from edge_containers_cli.logging import log
18
25
  from edge_containers_cli.shell import ShellError, shell
19
26
 
20
27
 
@@ -23,13 +30,35 @@ def extract_ns_app(target: str) -> tuple[str, str]:
23
30
  return namespace, app
24
31
 
25
32
 
33
+ def do_retry(cmd):
34
+ def _do_retry(*args, **kwargs):
35
+ max_attempts = 5
36
+ attempt = 1
37
+ sleep_time = 1
38
+ while attempt <= max_attempts:
39
+ try:
40
+ cmd(*args, **kwargs)
41
+ return None
42
+ except ShellError:
43
+ if attempt == max_attempts:
44
+ log.debug(f"Retry failed after {max_attempts} attempts")
45
+ raise
46
+ else:
47
+ log.debug(f"Retry attempt {attempt} failed. Retrying...")
48
+ sleep(sleep_time)
49
+ attempt += 1
50
+
51
+ return _do_retry
52
+
53
+
54
+ @do_retry
26
55
  def patch_value(target: str, key: str, value: str | bool | int):
27
56
  cmd_temp_ = f"argocd app set {target} -p {key}={value}"
28
57
  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)
58
+ # Rely on argocd autosync to get the cluster into the right state
31
59
 
32
60
 
61
+ @do_retry
33
62
  def push_value(target: str, key: str, value: str | bool | int):
34
63
  # Get source details
35
64
  app_resp = shell.run_command(
@@ -46,11 +75,12 @@ def push_value(target: str, key: str, value: str | bool | int):
46
75
  value,
47
76
  )
48
77
 
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)
78
+ # Free a possible patched value & refresh repo
52
79
  cmd_unset = f"argocd app unset {target} -p {key}"
53
80
  shell.run_command(cmd_unset, skip_on_dryrun=True)
81
+ cmd_refresh = f"argocd app get {target} --refresh"
82
+ shell.run_command(cmd_refresh, skip_on_dryrun=True)
83
+ # Rely on argocd autosync to get the cluster into the right state
54
84
 
55
85
 
56
86
  class ArgoCommands(Commands):
@@ -79,19 +109,18 @@ class ArgoCommands(Commands):
79
109
  self._check_service(service_name)
80
110
  namespace, app = extract_ns_app(self.target)
81
111
  cmd = (
82
- f"argocd app delete-resource {namespace}/{service_name} "
83
- f"--kind StatefulSet"
112
+ f"argocd app delete-resource {namespace}/{service_name} --kind StatefulSet"
84
113
  )
85
114
  shell.run_command(cmd, skip_on_dryrun=True)
86
115
 
87
- def start(self, service_name, commit):
116
+ def start(self, service_name, commit=False):
88
117
  self._check_service(service_name)
89
118
  if commit:
90
119
  push_value(self.target, f"ec_services.{service_name}.enabled", True)
91
120
  else:
92
121
  patch_value(self.target, f"ec_services.{service_name}.enabled", True)
93
122
 
94
- def stop(self, service_name, commit):
123
+ def stop(self, service_name, commit=False):
95
124
  self._check_service(service_name)
96
125
  if commit:
97
126
  push_value(self.target, f"ec_services.{service_name}.enabled", False)
@@ -122,45 +151,47 @@ class ArgoCommands(Commands):
122
151
  )
123
152
  app_dicts = YAML(typ="safe").load(app_resp)
124
153
 
125
- if not app_dicts:
126
- raise CommandError(f"No ec-services found in {self.target}")
127
- for app in app_dicts:
128
- resources_dict = app["status"]["resources"]
129
-
130
- for resource in resources_dict:
131
- is_ready = False
132
- if resource["kind"] == "StatefulSet":
133
- name = app["metadata"]["name"]
134
-
135
- # check if replicas ready
136
- mani_resp = shell.run_command(
137
- f"argocd app manifests {namespace}/{name} --source live",
138
- )
139
- for resource_manifest in mani_resp.split("---")[1:]:
140
- manifest = YAML(typ="safe").load(resource_manifest)
141
- if not manifest:
142
- continue
143
- kind = manifest["kind"]
144
- resource_name = manifest["metadata"]["name"]
145
- if kind == "StatefulSet" and resource_name == name:
146
- try:
147
- is_ready = bool(manifest["status"]["readyReplicas"])
148
- except (KeyError, TypeError): # Not ready if doesnt exist
149
- is_ready = False
150
- time_stamp = datetime.strptime(
151
- manifest["metadata"]["creationTimestamp"],
152
- "%Y-%m-%dT%H:%M:%SZ",
153
- )
154
- service_data["name"].append(name)
155
- service_data["version"].append(
156
- app["spec"]["source"]["targetRevision"]
157
- )
158
- service_data["ready"].append(is_ready)
159
- service_data["deployed"].append(
160
- datetime.strftime(time_stamp, TIME_FORMAT)
161
- )
162
-
163
- services_df = polars.from_dict(service_data)
154
+ if app_dicts:
155
+ for app in app_dicts:
156
+ resources_dict = app["status"]["resources"]
157
+
158
+ for resource in resources_dict:
159
+ is_ready = False
160
+ if resource["kind"] == "StatefulSet":
161
+ name = app["metadata"]["name"]
162
+
163
+ # check if replicas ready
164
+ mani_resp = shell.run_command(
165
+ f"argocd app manifests {namespace}/{name} --source live",
166
+ )
167
+ for resource_manifest in mani_resp.split("---")[1:]:
168
+ manifest = YAML(typ="safe").load(resource_manifest)
169
+ if not manifest:
170
+ continue
171
+ kind = manifest["kind"]
172
+ resource_name = manifest["metadata"]["name"]
173
+ if kind == "StatefulSet" and resource_name == name:
174
+ try:
175
+ is_ready = bool(manifest["status"]["readyReplicas"])
176
+ except (
177
+ KeyError,
178
+ TypeError,
179
+ ): # Not ready if doesnt exist
180
+ is_ready = False
181
+ time_stamp = datetime.strptime(
182
+ manifest["metadata"]["creationTimestamp"],
183
+ "%Y-%m-%dT%H:%M:%SZ",
184
+ )
185
+ service_data["name"].append(name)
186
+ service_data["version"].append(
187
+ app["spec"]["source"]["targetRevision"]
188
+ )
189
+ service_data["ready"].append(is_ready)
190
+ service_data["deployed"].append(
191
+ datetime.strftime(time_stamp, TIME_FORMAT)
192
+ )
193
+
194
+ services_df = polars.from_dict(service_data, schema=ServicesSchema)
164
195
 
165
196
  if running_only:
166
197
  services_df = services_df.filter(polars.col("ready").eq(True))
@@ -47,6 +47,34 @@ class Commands(ABC):
47
47
  self._repo = ctx.repo
48
48
  self._log_url = ctx.log_url
49
49
 
50
+ def __getattribute__(self, attr_name):
51
+ """Override to wrap each abstract method"""
52
+ attr = super().__getattribute__(attr_name)
53
+ if callable(attr):
54
+ if attr_name[0] == "_":
55
+ log_f = log.debug
56
+ else:
57
+ log_f = log.info
58
+
59
+ def wrapper(*args, **kwargs):
60
+ call_msg = (
61
+ f"Calling: {attr_name} {', '.join(str(item) for item in args)}"
62
+ )
63
+ return_msg = (
64
+ f"Returned: {attr_name} {', '.join(str(item) for item in args)}"
65
+ )
66
+ if kwargs:
67
+ call_msg = f"{call_msg} [{', '.join(f'{k}={v}' for k, v in kwargs.items())}]"
68
+ return_msg = f"{return_msg} [{', '.join(f'{k}={v}' for k, v in kwargs.items())}]"
69
+ log_f(call_msg)
70
+ result = attr(*args, **kwargs)
71
+ log_f(return_msg)
72
+ return result
73
+
74
+ return wrapper
75
+ else:
76
+ return attr
77
+
50
78
  @property
51
79
  def target(self):
52
80
  if not self._target_valid: # Only validate once
@@ -103,11 +131,11 @@ class Commands(ABC):
103
131
  raise NotImplementedError
104
132
 
105
133
  @abstractmethod
106
- def start(self, service_name: str, commit: bool) -> None:
134
+ def start(self, service_name: str, commit: bool = False) -> None:
107
135
  raise NotImplementedError
108
136
 
109
137
  @abstractmethod
110
- def stop(self, service_name: str, commit: bool) -> None:
138
+ def stop(self, service_name: str, commit: bool = False) -> None:
111
139
  raise NotImplementedError
112
140
 
113
141
  def template(self, svc_instance: Path, args: str) -> None:
@@ -6,6 +6,7 @@ Relies on the Helm class for deployment aspects.
6
6
 
7
7
  import time
8
8
  from datetime import datetime
9
+ from random import randrange, seed
9
10
 
10
11
  import polars
11
12
 
@@ -13,7 +14,9 @@ from edge_containers_cli.cmds.commands import CommandError, Commands, ServicesDa
13
14
  from edge_containers_cli.definitions import ECContext
14
15
  from edge_containers_cli.globals import TIME_FORMAT
15
16
 
16
- DELAY = 0.0
17
+ DELAY = 2.0
18
+ NUM_SERVICES = 8
19
+ seed(237)
17
20
 
18
21
 
19
22
  def process_t(time_string) -> str:
@@ -22,13 +25,12 @@ def process_t(time_string) -> str:
22
25
 
23
26
 
24
27
  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
+ "name": [f"demo-ea-0{cnt}" for cnt in range(NUM_SERVICES)],
29
+ "version": ["1.0." + str(25 - cnt) for cnt in range(NUM_SERVICES)],
30
+ "ready": [True] * NUM_SERVICES,
28
31
  "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
+ process_t(f"2024-10-22T11:23:0{randrange(1, 9)}Z")
33
+ for cnt in range(NUM_SERVICES)
32
34
  ],
33
35
  }
34
36
  sample_ServicesDataFrame = ServicesDataFrame(polars.from_dict(sample_data))
@@ -69,6 +71,11 @@ class DemoCommands(Commands):
69
71
  self._target_valid = False
70
72
  self._stateDF = sample_ServicesDataFrame
71
73
 
74
+ self.lorem_min = 10
75
+ self.lorem_max = 50
76
+ self.lorem_step = 5
77
+ self.lorem_count = self.lorem_min
78
+
72
79
  @demo_message
73
80
  def logs(self, service_name, prev):
74
81
  self._logs(service_name, prev)
@@ -83,11 +90,14 @@ class DemoCommands(Commands):
83
90
 
84
91
  @demo_message
85
92
  def restart(self, service_name):
86
- self.stop(service_name, False)
87
- self.start(service_name, False)
93
+ self._stop(service_name, commit=False)
94
+ self._start(service_name, commit=False)
88
95
 
89
96
  @demo_message
90
- def start(self, service_name, commit):
97
+ def start(self, service_name, commit=False):
98
+ self._start(service_name, commit=commit)
99
+
100
+ def _start(self, service_name, commit=False):
91
101
  self._check_service(service_name)
92
102
  time.sleep(DELAY)
93
103
  self._stateDF = self._stateDF.with_columns(
@@ -98,7 +108,10 @@ class DemoCommands(Commands):
98
108
  )
99
109
 
100
110
  @demo_message
101
- def stop(self, service_name, commit):
111
+ def stop(self, service_name, commit=False):
112
+ self._stop(service_name, commit=commit)
113
+
114
+ def _stop(self, service_name, commit=False):
102
115
  self._check_service(service_name)
103
116
  time.sleep(DELAY)
104
117
  self._stateDF = self._stateDF.with_columns(
@@ -110,7 +123,11 @@ class DemoCommands(Commands):
110
123
 
111
124
  def _get_logs(self, service_name, prev) -> str:
112
125
  self._check_service(service_name)
113
- logs_list = ["Lorem ipsum dolor sit amet"] * 25
126
+ if self.lorem_count < self.lorem_max:
127
+ self.lorem_count += self.lorem_step
128
+ else:
129
+ self.lorem_count = self.lorem_min
130
+ logs_list = ["Lorem ipsum dolor sit amet"] * self.lorem_count
114
131
  return "\n".join(logs_list)
115
132
 
116
133
  def _get_services(self, running_only) -> ServicesDataFrame:
@@ -87,7 +87,7 @@ class Helm:
87
87
  with open("Chart.yaml") as fp:
88
88
  chart_yaml = YAML(typ="safe").load(fp)
89
89
  package_path = (
90
- service_folder / f'{chart_yaml["name"]}-{chart_yaml["version"]}.tgz'
90
+ service_folder / f"{chart_yaml['name']}-{chart_yaml['version']}.tgz"
91
91
  )
92
92
 
93
93
  # use helm to install the chart
@@ -89,14 +89,14 @@ class K8sCommands(Commands):
89
89
  f"kubectl delete -n {self.target} {pod_name}", skip_on_dryrun=True
90
90
  )
91
91
 
92
- def start(self, service_name, commit):
92
+ def start(self, service_name, commit=False):
93
93
  self._check_service(service_name)
94
94
  shell.run_command(
95
95
  f"kubectl scale -n {self.target} statefulset {service_name} --replicas=1",
96
96
  skip_on_dryrun=True,
97
97
  )
98
98
 
99
- def stop(self, service_name, commit):
99
+ def stop(self, service_name, commit=False):
100
100
  self._check_service(service_name)
101
101
  shell.run_command(
102
102
  f"kubectl scale -n {self.target} statefulset {service_name} --replicas=0 ",
@@ -124,34 +124,46 @@ class K8sCommands(Commands):
124
124
  f'kubectl get statefulset -l "is_ioc==true" -n {self.target} -o yaml',
125
125
  )
126
126
  sts_dicts = YAML(typ="safe").load(kubectl_res)
127
- if not sts_dicts["items"]:
128
- raise CommandError(f"No ec-services found in {self.target}")
129
127
  service_data = {
130
128
  "name": [], # type: ignore
131
129
  "ready": [],
132
130
  "deployed": [],
133
131
  }
134
- for sts in sts_dicts["items"]:
135
- name = sts["metadata"]["name"]
136
- time_stamp = datetime.strptime(
137
- sts["metadata"]["creationTimestamp"], "%Y-%m-%dT%H:%M:%SZ"
138
- )
139
- try:
140
- is_ready = bool(sts["status"]["readyReplicas"])
141
- except KeyError: # Not ready if doesnt exist
142
- is_ready = False
143
-
144
- # Fill app data
145
- service_data["name"].append(name)
146
- service_data["ready"].append(is_ready)
147
- service_data["deployed"].append(datetime.strftime(time_stamp, TIME_FORMAT))
148
-
149
- services_df = polars.from_dict(service_data)
132
+ if sts_dicts["items"]:
133
+ for sts in sts_dicts["items"]:
134
+ name = sts["metadata"]["name"]
135
+ time_stamp = datetime.strptime(
136
+ sts["metadata"]["creationTimestamp"], "%Y-%m-%dT%H:%M:%SZ"
137
+ )
138
+ try:
139
+ is_ready = bool(sts["status"]["readyReplicas"])
140
+ except KeyError: # Not ready if doesnt exist
141
+ is_ready = False
142
+
143
+ # Fill app data
144
+ service_data["name"].append(name)
145
+ service_data["ready"].append(is_ready)
146
+ service_data["deployed"].append(
147
+ datetime.strftime(time_stamp, TIME_FORMAT)
148
+ )
149
+
150
+ services_df = polars.from_dict(
151
+ service_data,
152
+ schema=polars.Schema(
153
+ {
154
+ "name": polars.String,
155
+ "ready": polars.Boolean,
156
+ "deployed": polars.String,
157
+ }
158
+ ),
159
+ )
150
160
 
151
161
  # Adds the version for all services
152
162
  helm_out = str(shell.run_command(f"helm list -n {self.target} -o json"))
153
163
  if helm_out == "[]\n":
154
- helm_df = polars.DataFrame({"name": [""], "version": [""]})
164
+ helm_df = polars.DataFrame(
165
+ schema=polars.Schema({"name": polars.String, "version": polars.String})
166
+ )
155
167
  else:
156
168
  helm_df = polars.read_json(StringIO(str(helm_out)))
157
169
  helm_df = helm_df.rename({"app_version": "version"})