edge-containers-cli 3.4.1__tar.gz → 3.5.1__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 (108) hide show
  1. {edge-containers-cli-3.4.1 → edge_containers_cli-3.5.1}/.github/workflows/_release.yml +1 -1
  2. {edge-containers-cli-3.4.1 → edge_containers_cli-3.5.1}/.pre-commit-config.yaml +1 -1
  3. {edge-containers-cli-3.4.1 → edge_containers_cli-3.5.1}/PKG-INFO +2 -1
  4. {edge-containers-cli-3.4.1 → edge_containers_cli-3.5.1}/pyproject.toml +5 -0
  5. {edge-containers-cli-3.4.1 → edge_containers_cli-3.5.1}/src/edge_containers_cli/_version.py +2 -2
  6. {edge-containers-cli-3.4.1 → edge_containers_cli-3.5.1}/src/edge_containers_cli/autocomplete.py +8 -8
  7. {edge-containers-cli-3.4.1 → edge_containers_cli-3.5.1}/src/edge_containers_cli/cmds/cli.py +61 -60
  8. edge_containers_cli-3.5.1/src/edge_containers_cli/cmds/commands.py +86 -0
  9. {edge-containers-cli-3.4.1 → edge_containers_cli-3.5.1}/src/edge_containers_cli/cmds/helm.py +10 -5
  10. {edge-containers-cli-3.4.1 → edge_containers_cli-3.5.1}/src/edge_containers_cli/cmds/k8s_commands.py +51 -54
  11. {edge-containers-cli-3.4.1 → edge_containers_cli-3.5.1}/src/edge_containers_cli/cmds/local_commands.py +29 -50
  12. edge_containers_cli-3.5.1/src/edge_containers_cli/cmds/monitor.py +483 -0
  13. edge_containers_cli-3.5.1/src/edge_containers_cli/cmds/monitor.tcss +51 -0
  14. {edge-containers-cli-3.4.1 → edge_containers_cli-3.5.1}/src/edge_containers_cli/docker.py +18 -2
  15. edge_containers_cli-3.5.1/src/edge_containers_cli/git.py +116 -0
  16. {edge-containers-cli-3.4.1 → edge_containers_cli-3.5.1}/src/edge_containers_cli.egg-info/PKG-INFO +2 -1
  17. {edge-containers-cli-3.4.1 → edge_containers_cli-3.5.1}/src/edge_containers_cli.egg-info/SOURCES.txt +3 -0
  18. {edge-containers-cli-3.4.1 → edge_containers_cli-3.5.1}/src/edge_containers_cli.egg-info/requires.txt +1 -0
  19. {edge-containers-cli-3.4.1 → edge_containers_cli-3.5.1}/tests/data/autocomplete.yaml +4 -0
  20. {edge-containers-cli-3.4.1 → edge_containers_cli-3.5.1}/tests/data/ioc.yaml +15 -8
  21. {edge-containers-cli-3.4.1 → edge_containers_cli-3.5.1}/tests/data/local.yaml +12 -6
  22. {edge-containers-cli-3.4.1 → edge_containers_cli-3.5.1}/tests/test_ioc.py +1 -1
  23. edge-containers-cli-3.4.1/src/edge_containers_cli/git.py +0 -71
  24. {edge-containers-cli-3.4.1 → edge_containers_cli-3.5.1}/.copier-answers.yml +0 -0
  25. {edge-containers-cli-3.4.1 → edge_containers_cli-3.5.1}/.devcontainer/devcontainer.json +0 -0
  26. {edge-containers-cli-3.4.1 → edge_containers_cli-3.5.1}/.github/CONTRIBUTING.md +0 -0
  27. {edge-containers-cli-3.4.1 → edge_containers_cli-3.5.1}/.github/actions/install_requirements/action.yml +0 -0
  28. {edge-containers-cli-3.4.1 → edge_containers_cli-3.5.1}/.github/dependabot.yml +0 -0
  29. {edge-containers-cli-3.4.1 → edge_containers_cli-3.5.1}/.github/pages/index.html +0 -0
  30. {edge-containers-cli-3.4.1 → edge_containers_cli-3.5.1}/.github/pages/make_switcher.py +0 -0
  31. {edge-containers-cli-3.4.1 → edge_containers_cli-3.5.1}/.github/workflows/_check.yml +0 -0
  32. {edge-containers-cli-3.4.1 → edge_containers_cli-3.5.1}/.github/workflows/_dist.yml +0 -0
  33. {edge-containers-cli-3.4.1 → edge_containers_cli-3.5.1}/.github/workflows/_pypi.yml +0 -0
  34. {edge-containers-cli-3.4.1 → edge_containers_cli-3.5.1}/.github/workflows/_sys_test.yml +0 -0
  35. {edge-containers-cli-3.4.1 → edge_containers_cli-3.5.1}/.github/workflows/_test.yml +0 -0
  36. {edge-containers-cli-3.4.1 → edge_containers_cli-3.5.1}/.github/workflows/_tox.yml +0 -0
  37. {edge-containers-cli-3.4.1 → edge_containers_cli-3.5.1}/.github/workflows/ci.yml +0 -0
  38. {edge-containers-cli-3.4.1 → edge_containers_cli-3.5.1}/.gitignore +0 -0
  39. {edge-containers-cli-3.4.1 → edge_containers_cli-3.5.1}/.vscode/extensions.json +0 -0
  40. {edge-containers-cli-3.4.1 → edge_containers_cli-3.5.1}/.vscode/launch.json +0 -0
  41. {edge-containers-cli-3.4.1 → edge_containers_cli-3.5.1}/.vscode/settings.json +0 -0
  42. {edge-containers-cli-3.4.1 → edge_containers_cli-3.5.1}/.vscode/tasks.json +0 -0
  43. {edge-containers-cli-3.4.1 → edge_containers_cli-3.5.1}/Dockerfile +0 -0
  44. {edge-containers-cli-3.4.1 → edge_containers_cli-3.5.1}/LICENSE +0 -0
  45. {edge-containers-cli-3.4.1 → edge_containers_cli-3.5.1}/README.md +0 -0
  46. {edge-containers-cli-3.4.1 → edge_containers_cli-3.5.1}/catalog-info.yaml +0 -0
  47. {edge-containers-cli-3.4.1 → edge_containers_cli-3.5.1}/docs/conf.py +0 -0
  48. {edge-containers-cli-3.4.1 → edge_containers_cli-3.5.1}/docs/developer/explanations/decisions/0001-record-architecture-decisions.rst +0 -0
  49. {edge-containers-cli-3.4.1 → edge_containers_cli-3.5.1}/docs/developer/explanations/decisions/0002-switched-to-pip-skeleton.rst +0 -0
  50. {edge-containers-cli-3.4.1 → edge_containers_cli-3.5.1}/docs/developer/explanations/decisions.rst +0 -0
  51. {edge-containers-cli-3.4.1 → edge_containers_cli-3.5.1}/docs/developer/how-to/build-docs.rst +0 -0
  52. {edge-containers-cli-3.4.1 → edge_containers_cli-3.5.1}/docs/developer/how-to/contribute.rst +0 -0
  53. {edge-containers-cli-3.4.1 → edge_containers_cli-3.5.1}/docs/developer/how-to/lint.rst +0 -0
  54. {edge-containers-cli-3.4.1 → edge_containers_cli-3.5.1}/docs/developer/how-to/make-release.rst +0 -0
  55. {edge-containers-cli-3.4.1 → edge_containers_cli-3.5.1}/docs/developer/how-to/pin-requirements.rst +0 -0
  56. {edge-containers-cli-3.4.1 → edge_containers_cli-3.5.1}/docs/developer/how-to/run-tests.rst +0 -0
  57. {edge-containers-cli-3.4.1 → edge_containers_cli-3.5.1}/docs/developer/how-to/static-analysis.rst +0 -0
  58. {edge-containers-cli-3.4.1 → edge_containers_cli-3.5.1}/docs/developer/how-to/test-container.rst +0 -0
  59. {edge-containers-cli-3.4.1 → edge_containers_cli-3.5.1}/docs/developer/how-to/update-tools.rst +0 -0
  60. {edge-containers-cli-3.4.1 → edge_containers_cli-3.5.1}/docs/developer/index.rst +0 -0
  61. {edge-containers-cli-3.4.1 → edge_containers_cli-3.5.1}/docs/developer/reference/standards.rst +0 -0
  62. {edge-containers-cli-3.4.1 → edge_containers_cli-3.5.1}/docs/developer/tutorials/dev-install.rst +0 -0
  63. {edge-containers-cli-3.4.1 → edge_containers_cli-3.5.1}/docs/genindex.rst +0 -0
  64. {edge-containers-cli-3.4.1 → edge_containers_cli-3.5.1}/docs/images/dls-favicon.ico +0 -0
  65. {edge-containers-cli-3.4.1 → edge_containers_cli-3.5.1}/docs/images/dls-logo.svg +0 -0
  66. {edge-containers-cli-3.4.1 → edge_containers_cli-3.5.1}/docs/index.rst +0 -0
  67. {edge-containers-cli-3.4.1 → edge_containers_cli-3.5.1}/docs/user/explanations/docs-structure.rst +0 -0
  68. {edge-containers-cli-3.4.1 → edge_containers_cli-3.5.1}/docs/user/how-to/run-container.rst +0 -0
  69. {edge-containers-cli-3.4.1 → edge_containers_cli-3.5.1}/docs/user/index.rst +0 -0
  70. {edge-containers-cli-3.4.1 → edge_containers_cli-3.5.1}/docs/user/reference/api.rst +0 -0
  71. {edge-containers-cli-3.4.1 → edge_containers_cli-3.5.1}/docs/user/tutorials/installation.rst +0 -0
  72. {edge-containers-cli-3.4.1 → edge_containers_cli-3.5.1}/setup.cfg +0 -0
  73. {edge-containers-cli-3.4.1 → edge_containers_cli-3.5.1}/src/edge_containers_cli/__init__.py +0 -0
  74. {edge-containers-cli-3.4.1 → edge_containers_cli-3.5.1}/src/edge_containers_cli/__main__.py +0 -0
  75. {edge-containers-cli-3.4.1 → edge_containers_cli-3.5.1}/src/edge_containers_cli/cmds/__init__.py +0 -0
  76. {edge-containers-cli-3.4.1 → edge_containers_cli-3.5.1}/src/edge_containers_cli/cmds/kubectl.py +0 -0
  77. {edge-containers-cli-3.4.1 → edge_containers_cli-3.5.1}/src/edge_containers_cli/globals.py +0 -0
  78. {edge-containers-cli-3.4.1 → edge_containers_cli-3.5.1}/src/edge_containers_cli/logging.py +0 -0
  79. {edge-containers-cli-3.4.1 → edge_containers_cli-3.5.1}/src/edge_containers_cli/shell.py +0 -0
  80. {edge-containers-cli-3.4.1 → edge_containers_cli-3.5.1}/src/edge_containers_cli/utils.py +0 -0
  81. {edge-containers-cli-3.4.1 → edge_containers_cli-3.5.1}/src/edge_containers_cli.egg-info/dependency_links.txt +0 -0
  82. {edge-containers-cli-3.4.1 → edge_containers_cli-3.5.1}/src/edge_containers_cli.egg-info/entry_points.txt +0 -0
  83. {edge-containers-cli-3.4.1 → edge_containers_cli-3.5.1}/src/edge_containers_cli.egg-info/top_level.txt +0 -0
  84. {edge-containers-cli-3.4.1 → edge_containers_cli-3.5.1}/tests/__init__.py +0 -0
  85. {edge-containers-cli-3.4.1 → edge_containers_cli-3.5.1}/tests/conftest.py +0 -0
  86. {edge-containers-cli-3.4.1 → edge_containers_cli-3.5.1}/tests/data/bl01t/.copier-answers.yml +0 -0
  87. {edge-containers-cli-3.4.1 → edge_containers_cli-3.5.1}/tests/data/bl01t/.github/dependabot.yml +0 -0
  88. {edge-containers-cli-3.4.1 → edge_containers_cli-3.5.1}/tests/data/bl01t/.github/workflows/ci_verify.sh +0 -0
  89. {edge-containers-cli-3.4.1 → edge_containers_cli-3.5.1}/tests/data/bl01t/.github/workflows/verify.yml +0 -0
  90. {edge-containers-cli-3.4.1 → edge_containers_cli-3.5.1}/tests/data/bl01t/.gitignore +0 -0
  91. {edge-containers-cli-3.4.1 → edge_containers_cli-3.5.1}/tests/data/bl01t/LICENSE +0 -0
  92. {edge-containers-cli-3.4.1 → edge_containers_cli-3.5.1}/tests/data/bl01t/README.md +0 -0
  93. {edge-containers-cli-3.4.1 → edge_containers_cli-3.5.1}/tests/data/bl01t/environment.sh +0 -0
  94. {edge-containers-cli-3.4.1 → edge_containers_cli-3.5.1}/tests/data/bl01t/helm/shared/Chart.yaml +0 -0
  95. {edge-containers-cli-3.4.1 → edge_containers_cli-3.5.1}/tests/data/bl01t/helm/shared/README.md +0 -0
  96. {edge-containers-cli-3.4.1 → edge_containers_cli-3.5.1}/tests/data/bl01t/helm/shared/values.yaml +0 -0
  97. {edge-containers-cli-3.4.1 → edge_containers_cli-3.5.1}/tests/data/bl01t/include/iocs/Chart.yaml +0 -0
  98. {edge-containers-cli-3.4.1 → edge_containers_cli-3.5.1}/tests/data/bl01t/include/iocs/templates/configmap.yaml +0 -0
  99. {edge-containers-cli-3.4.1 → edge_containers_cli-3.5.1}/tests/data/bl01t/services/bl01t-ea-test-01/Chart.yaml +0 -0
  100. {edge-containers-cli-3.4.1 → edge_containers_cli-3.5.1}/tests/data/bl01t/services/bl01t-ea-test-01/config/ioc.db +0 -0
  101. {edge-containers-cli-3.4.1 → edge_containers_cli-3.5.1}/tests/data/bl01t/services/bl01t-ea-test-01/config/ioc.yaml +0 -0
  102. {edge-containers-cli-3.4.1 → edge_containers_cli-3.5.1}/tests/data/bl01t/services/bl01t-ea-test-01/pretend_helm.tgz +0 -0
  103. {edge-containers-cli-3.4.1 → edge_containers_cli-3.5.1}/tests/data/bl01t/services/bl01t-ea-test-01/values.yaml +0 -0
  104. {edge-containers-cli-3.4.1 → edge_containers_cli-3.5.1}/tests/data/bl01t/update-helm +0 -0
  105. {edge-containers-cli-3.4.1 → edge_containers_cli-3.5.1}/tests/test_autocomplete.py +0 -0
  106. {edge-containers-cli-3.4.1 → edge_containers_cli-3.5.1}/tests/test_cli.py +0 -0
  107. {edge-containers-cli-3.4.1 → edge_containers_cli-3.5.1}/tests/test_local.py +0 -0
  108. {edge-containers-cli-3.4.1 → edge_containers_cli-3.5.1}/tests/test_system.py +0 -0
@@ -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@9d7c94cfd0a1f3ed45544c887983e9fa900f0564 # v0.1.15
26
+ uses: softprops/action-gh-release@69320dbe05506a9a39fc8ae11030b214ec2d1f87 # v0.1.15
27
27
  with:
28
28
  prerelease: ${{ contains(github.ref_name, 'a') || contains(github.ref_name, 'b') || contains(github.ref_name, 'rc') }}
29
29
  files: "*"
@@ -1,6 +1,6 @@
1
1
  repos:
2
2
  - repo: https://github.com/pre-commit/pre-commit-hooks
3
- rev: v4.5.0
3
+ rev: v4.6.0
4
4
  hooks:
5
5
  - id: check-added-large-files
6
6
  - id: check-yaml
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: edge-containers-cli
3
- Version: 3.4.1
3
+ Version: 3.5.1
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
@@ -220,6 +220,7 @@ Requires-Dist: requests
220
220
  Requires-Dist: ruamel.yaml
221
221
  Requires-Dist: jinja2
222
222
  Requires-Dist: polars
223
+ Requires-Dist: textual
223
224
  Provides-Extra: dev
224
225
  Requires-Dist: copier; extra == "dev"
225
226
  Requires-Dist: pipdeptree; extra == "dev"
@@ -20,6 +20,7 @@ dependencies = [
20
20
  "ruamel.yaml",
21
21
  "jinja2",
22
22
  "polars",
23
+ "textual",
23
24
  ]
24
25
 
25
26
  dynamic = ["version"]
@@ -74,11 +75,15 @@ testpaths = "src tests"
74
75
 
75
76
  [tool.coverage.run]
76
77
  data_file = "/tmp/edge_containers_cli.coverage"
78
+ omit = ["src/edge_containers_cli/cmds/monitor.py"]
77
79
 
78
80
  [tool.coverage.paths]
79
81
  # Tests are run from installed location, map back to the src directory
80
82
  source = ["src", "**/site-packages/"]
81
83
 
84
+ [tool.coverage.report]
85
+ omit = ["src/edge_containers_cli/cmds/monitor.py"]
86
+
82
87
  # tox must currently be configured via an embedded ini string
83
88
  # See: https://github.com/tox-dev/tox/issues/999
84
89
  [tool.tox]
@@ -12,5 +12,5 @@ __version__: str
12
12
  __version_tuple__: VERSION_TUPLE
13
13
  version_tuple: VERSION_TUPLE
14
14
 
15
- __version__ = version = '3.4.1'
16
- __version_tuple__ = version_tuple = (3, 4, 1)
15
+ __version__ = version = '3.5.1'
16
+ __version_tuple__ = version_tuple = (3, 5, 1)
@@ -12,7 +12,7 @@ import edge_containers_cli.globals as globals
12
12
  import edge_containers_cli.shell as shell
13
13
  from edge_containers_cli.cmds.k8s_commands import check_namespace
14
14
  from edge_containers_cli.docker import Docker
15
- from edge_containers_cli.git import create_svc_graph
15
+ from edge_containers_cli.git import create_version_map
16
16
  from edge_containers_cli.logging import log
17
17
  from edge_containers_cli.utils import cleanup_temp
18
18
 
@@ -46,14 +46,14 @@ def read_cached_dict(cache_folder: str, cached_file: str) -> dict:
46
46
 
47
47
 
48
48
  def fetch_service_graph(beamline_repo: str) -> dict:
49
- svc_graph = read_cached_dict(url_encode(beamline_repo), globals.IOC_CACHE)
50
- if not svc_graph:
49
+ version_map = read_cached_dict(url_encode(beamline_repo), globals.IOC_CACHE)
50
+ if not version_map:
51
51
  tmp_dir = Path(tempfile.mkdtemp())
52
- svc_graph = create_svc_graph(beamline_repo, tmp_dir)
53
- cache_dict(url_encode(beamline_repo), globals.IOC_CACHE, svc_graph)
52
+ version_map = create_version_map(beamline_repo, tmp_dir)
53
+ cache_dict(url_encode(beamline_repo), globals.IOC_CACHE, version_map)
54
54
  cleanup_temp(tmp_dir)
55
55
 
56
- return svc_graph
56
+ return version_map
57
57
 
58
58
 
59
59
  def avail_services(ctx: typer.Context) -> list[str]:
@@ -77,8 +77,8 @@ def avail_versions(ctx: typer.Context) -> list[str]:
77
77
 
78
78
  # This block prevents getting a stack trace during autocompletion
79
79
  try:
80
- svc_graph = fetch_service_graph(beamline_repo)
81
- svc_versions = svc_graph[service_name]
80
+ version_map = fetch_service_graph(beamline_repo)
81
+ svc_versions = version_map[service_name]
82
82
  return svc_versions
83
83
  except KeyError:
84
84
  log.error("IOC not found")
@@ -15,28 +15,61 @@ from edge_containers_cli.autocomplete import (
15
15
  )
16
16
  from edge_containers_cli.cmds.k8s_commands import K8sCommands
17
17
  from edge_containers_cli.cmds.local_commands import LocalCommands
18
- from edge_containers_cli.git import create_svc_graph
18
+ from edge_containers_cli.cmds.monitor import MonitorApp
19
+ from edge_containers_cli.git import create_version_map
20
+ from edge_containers_cli.globals import EC_K8S_NAMESPACE
19
21
  from edge_containers_cli.logging import log
20
22
  from edge_containers_cli.utils import cleanup_temp, drop_path
21
23
 
22
- cli = typer.Typer(pretty_exceptions_show_locals=False)
24
+
25
+ class ErrorHandlingTyper(typer.Typer):
26
+ def __call__(self, *args, **kwargs):
27
+ try:
28
+ super().__call__(*args, **kwargs)
29
+ except NotImplementedError:
30
+ typer.echo("This function is not available for the current namespace")
31
+
32
+
33
+ cli = ErrorHandlingTyper(pretty_exceptions_show_locals=False)
34
+
35
+
36
+ def commands(ctx):
37
+ """
38
+ Construct the appropriate Commands class for the local or K8S namespace
39
+ """
40
+ if ctx.obj.namespace == globals.LOCAL_NAMESPACE:
41
+ commands = LocalCommands(ctx.obj)
42
+ else:
43
+ commands = K8sCommands(ctx.obj)
44
+
45
+ return commands
23
46
 
24
47
 
25
48
  @cli.command()
26
49
  def ps(
27
50
  ctx: typer.Context,
28
- all: bool = typer.Option(
29
- False, "-a", "--all", help="list stopped IOCs/services as well as running ones"
51
+ running_only: bool = typer.Option(
52
+ False, "-r", "--running-only", help="list only IOCs/services that are running"
30
53
  ),
31
54
  wide: bool = typer.Option(
32
55
  False, "--wide", "-w", help="use a wide format with additional fields"
33
56
  ),
34
57
  ):
35
58
  """List the IOCs/services running in the current namespace"""
36
- if ctx.obj.namespace == globals.LOCAL_NAMESPACE:
37
- LocalCommands(ctx.obj).ps(all, wide)
38
- else:
39
- K8sCommands(ctx.obj).ps(all, wide)
59
+ commands(ctx).ps(running_only, wide)
60
+
61
+
62
+ @cli.command()
63
+ def monitor(
64
+ ctx: typer.Context,
65
+ running_only: bool = typer.Option(
66
+ False, "-r", "--running-only", help="list only IOCs/services that are running"
67
+ ),
68
+ ):
69
+ """Open IOC monitor TUI."""
70
+ cmds = commands(ctx)
71
+ app = MonitorApp(EC_K8S_NAMESPACE, cmds, running_only)
72
+ app.run()
40
73
 
41
74
 
42
75
  @cli.command()
@@ -47,7 +80,7 @@ def env(
47
80
  ),
48
81
  ):
49
82
  """List all relevant environment variables"""
50
- LocalCommands(ctx.obj).environment(verbose == verbose)
83
+ commands(ctx).environment(verbose == verbose)
51
84
 
52
85
 
53
86
  @cli.command()
@@ -60,10 +93,7 @@ def attach(
60
93
  """
61
94
  Attach to the console of a live service
62
95
  """
63
- if ctx.obj.namespace == globals.LOCAL_NAMESPACE:
64
- LocalCommands(ctx.obj, service_name).attach()
65
- else:
66
- K8sCommands(ctx.obj, service_name).attach()
96
+ commands(ctx).attach(service_name)
67
97
 
68
98
 
69
99
  @cli.command()
@@ -76,10 +106,7 @@ def delete(
76
106
  """
77
107
  Remove a helm deployment from the cluster
78
108
  """
79
- if ctx.obj.namespace == globals.LOCAL_NAMESPACE:
80
- LocalCommands(ctx.obj, service_name).delete()
81
- else:
82
- K8sCommands(ctx.obj, service_name).delete()
109
+ commands(ctx).delete(service_name)
83
110
 
84
111
 
85
112
  @cli.command()
@@ -99,10 +126,7 @@ def template(
99
126
  print out the helm template generated from a local service instance
100
127
  """
101
128
  args = f"{args} --debug"
102
- if ctx.obj.namespace == globals.LOCAL_NAMESPACE:
103
- typer.echo("Not applicable to local deployments")
104
- else:
105
- K8sCommands(ctx.obj).template(svc_instance, args)
129
+ commands(ctx).template(svc_instance, args)
106
130
 
107
131
 
108
132
  @cli.command()
@@ -123,11 +147,7 @@ def deploy_local(
123
147
  """
124
148
  Deploy a local IOC/service helm chart directly to the cluster with dated beta version
125
149
  """
126
- if ctx.obj.namespace == globals.LOCAL_NAMESPACE:
127
- LocalCommands(ctx.obj).deploy_local(svc_instance, yes, args)
128
- else:
129
- args = args if not wait else args + " --wait"
130
- K8sCommands(ctx.obj).deploy_local(svc_instance, yes, args)
150
+ commands(ctx).deploy_local(svc_instance, yes, args)
131
151
 
132
152
 
133
153
  @cli.command()
@@ -149,14 +169,9 @@ def deploy(
149
169
  """
150
170
  Pull an IOC/service helm chart version from the domain repo and deploy it to the cluster
151
171
  """
172
+ args = args if not wait else args + " --wait"
152
173
  service_name = drop_path(service_name)
153
- if ctx.obj.namespace == globals.LOCAL_NAMESPACE:
154
- LocalCommands(ctx.obj, service_name).deploy(service_name, version, args)
155
- else:
156
- args = args if not wait else args + " --wait"
157
- K8sCommands(ctx.obj, service_name, check=False).deploy(
158
- service_name, version, args
159
- )
174
+ commands(ctx).deploy(service_name, version, args)
160
175
 
161
176
 
162
177
  @cli.command()
@@ -165,11 +180,11 @@ def list(
165
180
  ):
166
181
  """List all IOCs/services available in the helm registry"""
167
182
  tmp_dir = Path(tempfile.mkdtemp())
168
- svc_graph = create_svc_graph(ctx.obj.beamline_repo, tmp_dir)
169
- svc_list = natsorted(svc_graph.keys())
170
- log.debug(f"svc_graph = {svc_graph}")
183
+ version_map = create_version_map(ctx.obj.beamline_repo, tmp_dir)
184
+ svc_list = natsorted(version_map.keys())
185
+ log.debug(f"version_map = {version_map}")
171
186
 
172
- versions = [natsorted(svc_graph[svc])[-1] for svc in svc_list]
187
+ versions = [natsorted(version_map[svc])[-1] for svc in svc_list]
173
188
  services_df = polars.from_dict({"name": svc_list, "version": versions})
174
189
  print(services_df)
175
190
 
@@ -185,9 +200,9 @@ def instances(
185
200
  ):
186
201
  """List all versions of the IOC/service available in the helm registry"""
187
202
  tmp_dir = Path(tempfile.mkdtemp())
188
- svc_graph = create_svc_graph(ctx.obj.beamline_repo, tmp_dir)
203
+ version_map = create_version_map(ctx.obj.beamline_repo, tmp_dir)
189
204
  try:
190
- svc_list = svc_graph[service_name]
205
+ svc_list = version_map[service_name]
191
206
  except KeyError:
192
207
  svc_list = []
193
208
 
@@ -208,14 +223,12 @@ def exec(
208
223
  ),
209
224
  ):
210
225
  """Execute a bash prompt in a running container"""
211
- if ctx.obj.namespace == globals.LOCAL_NAMESPACE:
212
- LocalCommands(ctx.obj, service_name).exec()
213
- else:
214
- K8sCommands(ctx.obj, service_name).exec()
226
+ commands(ctx).exec(service_name)
215
227
 
216
228
 
217
229
  @cli.command()
218
230
  def log_history(
231
+ ctx: typer.Context,
219
232
  service_name: str = typer.Argument(
220
233
  ...,
221
234
  help="Name of the IOC/service to inspect",
@@ -223,7 +236,7 @@ def log_history(
223
236
  ),
224
237
  ):
225
238
  """Open historical logs for an IOC/service"""
226
- K8sCommands(None, service_name).log_history()
239
+ commands(ctx).log_history(service_name)
227
240
 
228
241
 
229
242
  @cli.command()
@@ -241,10 +254,7 @@ def logs(
241
254
  follow: bool = typer.Option(False, "--follow", "-f", help="Follow the log stream"),
242
255
  ):
243
256
  """Show logs for current and previous instances of an IOC/service"""
244
- if ctx.obj.namespace == globals.LOCAL_NAMESPACE:
245
- LocalCommands(ctx.obj, service_name).logs(prev, follow)
246
- else:
247
- K8sCommands(ctx.obj, service_name).logs(prev, follow)
257
+ commands(ctx).logs(service_name, prev, follow)
248
258
 
249
259
 
250
260
  @cli.command()
@@ -255,10 +265,7 @@ def restart(
255
265
  ),
256
266
  ):
257
267
  """Restart an IOC/service"""
258
- if ctx.obj.namespace == globals.LOCAL_NAMESPACE:
259
- LocalCommands(ctx.obj, service_name).restart()
260
- else:
261
- K8sCommands(ctx.obj, service_name).restart()
268
+ commands(ctx).restart(service_name)
262
269
 
263
270
 
264
271
  @cli.command()
@@ -270,10 +277,7 @@ def start(
270
277
  ):
271
278
  """Start an IOC/service"""
272
279
  log.debug("Starting IOC/service with LOCAL={ctx.obj.namespace == " "}")
273
- if ctx.obj.namespace == globals.LOCAL_NAMESPACE:
274
- LocalCommands(ctx.obj, service_name).start()
275
- else:
276
- K8sCommands(ctx.obj, service_name).start()
280
+ commands(ctx).start(service_name)
277
281
 
278
282
 
279
283
  @cli.command()
@@ -286,10 +290,7 @@ def stop(
286
290
  ),
287
291
  ):
288
292
  """Stop an IOC/service"""
289
- if ctx.obj.namespace == globals.LOCAL_NAMESPACE:
290
- LocalCommands(ctx.obj, service_name).stop()
291
- else:
292
- K8sCommands(ctx.obj, service_name).stop()
293
+ commands(ctx).stop(service_name)
293
294
 
294
295
 
295
296
  @cli.command()
@@ -0,0 +1,86 @@
1
+ import webbrowser
2
+ from pathlib import Path
3
+
4
+ import polars
5
+ import typer
6
+
7
+ import edge_containers_cli.globals as globals
8
+ import edge_containers_cli.shell as shell
9
+ from edge_containers_cli.logging import log
10
+
11
+
12
+ class Commands:
13
+ """
14
+ A base class for K8SCommands and LocalCommands
15
+
16
+ Implements the common functionality but defers specialist functions to
17
+ the subclasss
18
+
19
+ Allows the CLI or the TUI to call functions without worrying about local
20
+ vs Kubernetes containers
21
+ """
22
+
23
+ def __init__(self, ctx: globals.Context):
24
+ self.namespace = ctx.namespace
25
+ self.beamline_repo = ctx.beamline_repo
26
+
27
+ def attach(self, service_name):
28
+ raise NotImplementedError
29
+
30
+ def delete(self, service_name):
31
+ raise NotImplementedError
32
+
33
+ def template(self, svc_instance: Path, args: str):
34
+ raise NotImplementedError
35
+
36
+ def deploy_local(self, svc_instance: Path, yes: bool, args: str):
37
+ raise NotImplementedError
38
+
39
+ def deploy(self, service_name: str, version: str, args: str):
40
+ raise NotImplementedError
41
+
42
+ def exec(self, service_name: str):
43
+ raise NotImplementedError
44
+
45
+ def logs(self, service_name: str, prev: bool, follow: bool, stdout: bool):
46
+ raise NotImplementedError
47
+
48
+ def restart(self, service_name: str):
49
+ raise NotImplementedError
50
+
51
+ def start(self, service_name: str):
52
+ raise NotImplementedError
53
+
54
+ def stop(self, service_name: str):
55
+ raise NotImplementedError
56
+
57
+ def get_services(self, running_only: bool) -> polars.DataFrame:
58
+ raise NotImplementedError
59
+
60
+ def ps(self, running_only: bool, wide: bool):
61
+ select_data = self.get_services(running_only)
62
+ if not wide:
63
+ select_data.drop_in_place("image")
64
+ print(select_data)
65
+
66
+ def environment(self, verbose: bool):
67
+ """
68
+ declare the environment settings for ec
69
+ """
70
+ ns = self.namespace
71
+
72
+ if ns == globals.LOCAL_NAMESPACE:
73
+ typer.echo("ioc commands deploy to the local docker/podman instance")
74
+ else:
75
+ typer.echo(f"ioc commands deploy to the {ns} namespace the K8S cluster")
76
+
77
+ typer.echo("\nEC environment variables:")
78
+ shell.run_command("env | grep '^EC_'", interactive=False, show=True)
79
+
80
+ def log_history(self, service_name):
81
+ if not globals.EC_LOG_URL:
82
+ log.error("EC_LOG_URL environment not set")
83
+ raise typer.Exit(1)
84
+
85
+ url = globals.EC_LOG_URL.format(service_name=service_name)
86
+ webbrowser.open(url)
@@ -3,6 +3,7 @@ from pathlib import Path
3
3
  from typing import Optional
4
4
 
5
5
  import typer
6
+ from ruamel.yaml import YAML
6
7
 
7
8
  import edge_containers_cli.globals as globals
8
9
  import edge_containers_cli.shell as shell
@@ -92,15 +93,19 @@ class Helm:
92
93
 
93
94
  with chdir(service_folder):
94
95
  shell.run_command(
95
- f"helm dependency update {service_folder}; "
96
- f"helm package {service_folder} --app-version {self.version}",
96
+ f"helm package {service_folder} -u --app-version {self.version}",
97
97
  interactive=False,
98
98
  )
99
- # find the packaged chart
100
- chart_file = list(service_folder.glob("*.tgz"))[0]
99
+
100
+ # Determine package name
101
+ with open("Chart.yaml") as fp:
102
+ chart_yaml = YAML(typ="safe").load(fp)
103
+ package_path = (
104
+ service_folder / f'{chart_yaml["name"]}-{chart_yaml["version"]}.tgz'
105
+ )
101
106
 
102
107
  # use helm to install the chart
103
- self._install(chart_file)
108
+ self._install(package_path)
104
109
 
105
110
  def _install(self, helm_chart: Path):
106
111
  """
@@ -4,17 +4,17 @@ implements commands for deploying and managing service instances in the k8s clus
4
4
  Relies on the Helm class for deployment aspects.
5
5
  """
6
6
 
7
- import webbrowser
8
7
  from datetime import datetime
9
8
  from io import StringIO
10
9
  from pathlib import Path
11
- from typing import Optional
10
+ from typing import Optional, Union
12
11
 
13
12
  import polars
14
13
  import typer
15
14
 
16
15
  import edge_containers_cli.globals as globals
17
16
  import edge_containers_cli.shell as shell
17
+ from edge_containers_cli.cmds.commands import Commands
18
18
  from edge_containers_cli.cmds.helm import Helm
19
19
  from edge_containers_cli.cmds.kubectl import jsonpath_deploy_info, jsonpath_pod_info
20
20
  from edge_containers_cli.logging import log
@@ -59,43 +59,35 @@ def check_namespace(namespace: Optional[str]):
59
59
  log.info("domain = %s", namespace)
60
60
 
61
61
 
62
- class K8sCommands:
62
+ class K8sCommands(Commands):
63
63
  """
64
64
  A class for implementing the Kubernetes based commands
65
65
  """
66
66
 
67
67
  def __init__(
68
68
  self,
69
- ctx: Optional[globals.Context],
70
- service_name: str = "",
71
- check: bool = True,
69
+ ctx: globals.Context,
70
+ # check: bool = True,
72
71
  ):
73
- self.namespace: str = ""
74
- self.beamline_repo: str = ""
75
- # TODO isnt ctx always set??
76
- if ctx is not None:
77
- namespace = ctx.namespace
78
- check_namespace(namespace)
79
- if service_name != "" and check:
80
- self.fullname = check_service(service_name, namespace)
81
- self.namespace = namespace
82
- self.beamline_repo = ctx.beamline_repo
83
- self.service_name: str = service_name
84
-
85
- def attach(self):
72
+ super().__init__(ctx)
73
+ check_namespace(self.namespace)
74
+
75
+ def attach(self, service_name):
76
+ fullname = check_service(service_name, self.namespace)
86
77
  shell.run_command(
87
- f"kubectl -it -n {self.namespace} attach {self.fullname}",
78
+ f"kubectl -it -n {self.namespace} attach {fullname}",
88
79
  interactive=True,
89
80
  )
90
81
 
91
- def delete(self):
82
+ def delete(self, service_name):
83
+ check_service(service_name, self.namespace)
92
84
  if not typer.confirm(
93
- f"This will remove all versions of {self.service_name} "
85
+ f"This will remove all versions of {service_name} "
94
86
  "from the cluster. Are you sure ?"
95
87
  ):
96
88
  raise typer.Abort()
97
89
 
98
- shell.run_command(f"helm delete -n {self.namespace} {self.service_name}")
90
+ shell.run_command(f"helm delete -n {self.namespace} {service_name}")
99
91
 
100
92
  def template(self, svc_instance: Path, args: str):
101
93
  datetime.strftime(datetime.now(), "%Y.%-m.%-d-b%-H.%-M")
@@ -127,48 +119,48 @@ class K8sCommands:
127
119
  )
128
120
  chart.deploy()
129
121
 
130
- def exec(self):
131
- shell.run_command(
132
- f"kubectl -it -n {self.namespace} exec {self.fullname} -- bash"
133
- )
134
-
135
- def log_history(self):
136
- if not globals.EC_LOG_URL:
137
- log.error("EC_LOG_URL environment not set")
138
- raise typer.Exit(1)
139
-
140
- url = globals.EC_LOG_URL.format(service_name=self.service_name)
141
- webbrowser.open(url)
122
+ def exec(self, service_name):
123
+ fullname = check_service(service_name, self.namespace)
124
+ shell.run_command(f"kubectl -it -n {self.namespace} exec {fullname} -- bash")
142
125
 
143
- def logs(self, prev: bool, follow: bool):
126
+ def logs(
127
+ self, service_name: str, prev: bool, follow: bool, stdout: bool = False
128
+ ) -> Optional[Union[str, bool]]:
129
+ fullname = check_service(service_name, self.namespace)
144
130
  previous = "-p" if prev else ""
145
131
  fol = "-f" if follow else ""
146
132
 
147
- shell.run_command(
148
- f"kubectl -n {self.namespace} logs {self.fullname} {previous} {fol}"
149
- )
133
+ if stdout:
134
+ a = shell.run_command(
135
+ f"kubectl -n {self.namespace} logs {fullname} {previous} {fol}",
136
+ interactive=False,
137
+ show=False,
138
+ error_OK=True,
139
+ )
140
+ return a
141
+ else:
142
+ shell.run_command(
143
+ f"kubectl -n {self.namespace} logs {fullname} {previous} {fol}",
144
+ )
150
145
 
151
- def restart(self):
146
+ def restart(self, service_name):
147
+ check_service(service_name, self.namespace)
152
148
  pod_name = shell.run_command(
153
- f"kubectl get -n {self.namespace} pod -l app={self.service_name} -o name",
149
+ f"kubectl get -n {self.namespace} pod -l app={service_name} -o name",
154
150
  interactive=False,
155
151
  )
156
152
  shell.run_command(f"kubectl delete -n {self.namespace} {pod_name}")
157
153
 
158
- def start(self):
159
- shell.run_command(
160
- f"kubectl scale -n {self.namespace} {self.fullname} --replicas=1"
161
- )
154
+ def start(self, service_name):
155
+ fullname = check_service(service_name, self.namespace)
156
+ shell.run_command(f"kubectl scale -n {self.namespace} {fullname} --replicas=1")
162
157
 
163
- def stop(self):
158
+ def stop(self, service_name):
159
+ fullname = check_service(service_name, self.namespace)
164
160
  """Stop an IOC"""
165
- shell.run_command(
166
- f"kubectl scale -n {self.namespace} {self.fullname} --replicas=0 "
167
- )
168
-
169
- def ps(self, all: bool, wide: bool):
170
- """List all IOCs and Services in the current namespace"""
161
+ shell.run_command(f"kubectl scale -n {self.namespace} {fullname} --replicas=0 ")
171
162
 
163
+ def get_services(self, running_only: bool) -> polars.DataFrame:
172
164
  services_df = polars.DataFrame()
173
165
 
174
166
  # Gives all services (running & not running) and their image
@@ -207,7 +199,7 @@ class K8sCommands:
207
199
  polars.col("running").replace({"Running": True}, default=False),
208
200
  polars.col("restarts").fill_null(0),
209
201
  )
210
- elif all:
202
+ elif not running_only:
211
203
  services_df = services_df.with_columns(
212
204
  running=polars.lit(False), restarts=polars.lit(0)
213
205
  )
@@ -229,9 +221,14 @@ class K8sCommands:
229
221
  services_df = services_df.select(
230
222
  ["name", "version", "running", "restarts", "deployed", "image"]
231
223
  )
232
- if not all:
224
+ if running_only:
233
225
  services_df = services_df.filter(polars.col("running").eq(True))
234
226
  log.debug(services_df)
227
+ return services_df
228
+
229
+ def ps(self, running_only: bool, wide: bool):
230
+ """List all IOCs and Services in the current namespace"""
231
+ services_df = self.get_services(running_only)
235
232
  if not wide:
236
233
  services_df.drop_in_place("image")
237
234
  log.debug(services_df)