edge-containers-cli 3.0.4__tar.gz → 3.1.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 (92) hide show
  1. {edge-containers-cli-3.0.4 → edge-containers-cli-3.1.1}/.github/workflows/_release.yml +1 -1
  2. edge-containers-cli-3.1.1/.github/workflows/_sys_test.yml +36 -0
  3. {edge-containers-cli-3.0.4 → edge-containers-cli-3.1.1}/.github/workflows/ci.yml +13 -0
  4. {edge-containers-cli-3.0.4 → edge-containers-cli-3.1.1}/PKG-INFO +2 -3
  5. {edge-containers-cli-3.0.4 → edge-containers-cli-3.1.1}/pyproject.toml +1 -2
  6. {edge-containers-cli-3.0.4 → edge-containers-cli-3.1.1}/src/edge_containers_cli/_version.py +2 -2
  7. {edge-containers-cli-3.0.4 → edge-containers-cli-3.1.1}/src/edge_containers_cli/cmds/cli.py +6 -9
  8. {edge-containers-cli-3.0.4 → edge-containers-cli-3.1.1}/src/edge_containers_cli/cmds/helm.py +8 -5
  9. {edge-containers-cli-3.0.4 → edge-containers-cli-3.1.1}/src/edge_containers_cli/cmds/k8s_commands.py +64 -22
  10. edge-containers-cli-3.1.1/src/edge_containers_cli/cmds/kubectl.py +15 -0
  11. {edge-containers-cli-3.0.4 → edge-containers-cli-3.1.1}/src/edge_containers_cli/cmds/local_commands.py +48 -27
  12. {edge-containers-cli-3.0.4 → edge-containers-cli-3.1.1}/src/edge_containers_cli/docker.py +0 -80
  13. edge-containers-cli-3.1.1/src/edge_containers_cli/git.py +71 -0
  14. {edge-containers-cli-3.0.4 → edge-containers-cli-3.1.1}/src/edge_containers_cli/globals.py +10 -48
  15. {edge-containers-cli-3.0.4 → edge-containers-cli-3.1.1}/src/edge_containers_cli/utils.py +13 -0
  16. {edge-containers-cli-3.0.4 → edge-containers-cli-3.1.1}/src/edge_containers_cli.egg-info/PKG-INFO +2 -3
  17. {edge-containers-cli-3.0.4 → edge-containers-cli-3.1.1}/src/edge_containers_cli.egg-info/SOURCES.txt +1 -0
  18. {edge-containers-cli-3.0.4 → edge-containers-cli-3.1.1}/src/edge_containers_cli.egg-info/requires.txt +1 -2
  19. {edge-containers-cli-3.0.4 → edge-containers-cli-3.1.1}/tests/conftest.py +9 -3
  20. {edge-containers-cli-3.0.4 → edge-containers-cli-3.1.1}/tests/data/autocomplete.yaml +8 -3
  21. {edge-containers-cli-3.0.4 → edge-containers-cli-3.1.1}/tests/data/ioc.yaml +18 -5
  22. {edge-containers-cli-3.0.4 → edge-containers-cli-3.1.1}/tests/data/local.yaml +43 -5
  23. {edge-containers-cli-3.0.4 → edge-containers-cli-3.1.1}/tests/test_autocomplete.py +11 -9
  24. {edge-containers-cli-3.0.4 → edge-containers-cli-3.1.1}/tests/test_ioc.py +0 -3
  25. {edge-containers-cli-3.0.4 → edge-containers-cli-3.1.1}/tests/test_system.py +20 -14
  26. edge-containers-cli-3.0.4/src/edge_containers_cli/cmds/kubectl.py +0 -10
  27. edge-containers-cli-3.0.4/src/edge_containers_cli/git.py +0 -152
  28. {edge-containers-cli-3.0.4 → edge-containers-cli-3.1.1}/.copier-answers.yml +0 -0
  29. {edge-containers-cli-3.0.4 → edge-containers-cli-3.1.1}/.devcontainer/devcontainer.json +0 -0
  30. {edge-containers-cli-3.0.4 → edge-containers-cli-3.1.1}/.github/CONTRIBUTING.md +0 -0
  31. {edge-containers-cli-3.0.4 → edge-containers-cli-3.1.1}/.github/actions/install_requirements/action.yml +0 -0
  32. {edge-containers-cli-3.0.4 → edge-containers-cli-3.1.1}/.github/dependabot.yml +0 -0
  33. {edge-containers-cli-3.0.4 → edge-containers-cli-3.1.1}/.github/pages/index.html +0 -0
  34. {edge-containers-cli-3.0.4 → edge-containers-cli-3.1.1}/.github/pages/make_switcher.py +0 -0
  35. {edge-containers-cli-3.0.4 → edge-containers-cli-3.1.1}/.github/workflows/_check.yml +0 -0
  36. {edge-containers-cli-3.0.4 → edge-containers-cli-3.1.1}/.github/workflows/_dist.yml +0 -0
  37. {edge-containers-cli-3.0.4 → edge-containers-cli-3.1.1}/.github/workflows/_pypi.yml +0 -0
  38. {edge-containers-cli-3.0.4 → edge-containers-cli-3.1.1}/.github/workflows/_test.yml +0 -0
  39. {edge-containers-cli-3.0.4 → edge-containers-cli-3.1.1}/.github/workflows/_tox.yml +0 -0
  40. {edge-containers-cli-3.0.4 → edge-containers-cli-3.1.1}/.gitignore +0 -0
  41. {edge-containers-cli-3.0.4 → edge-containers-cli-3.1.1}/.pre-commit-config.yaml +0 -0
  42. {edge-containers-cli-3.0.4 → edge-containers-cli-3.1.1}/.vscode/extensions.json +0 -0
  43. {edge-containers-cli-3.0.4 → edge-containers-cli-3.1.1}/.vscode/launch.json +0 -0
  44. {edge-containers-cli-3.0.4 → edge-containers-cli-3.1.1}/.vscode/settings.json +0 -0
  45. {edge-containers-cli-3.0.4 → edge-containers-cli-3.1.1}/.vscode/tasks.json +0 -0
  46. {edge-containers-cli-3.0.4 → edge-containers-cli-3.1.1}/Dockerfile +0 -0
  47. {edge-containers-cli-3.0.4 → edge-containers-cli-3.1.1}/LICENSE +0 -0
  48. {edge-containers-cli-3.0.4 → edge-containers-cli-3.1.1}/README.md +0 -0
  49. {edge-containers-cli-3.0.4 → edge-containers-cli-3.1.1}/catalog-info.yaml +0 -0
  50. {edge-containers-cli-3.0.4 → edge-containers-cli-3.1.1}/docs/conf.py +0 -0
  51. {edge-containers-cli-3.0.4 → edge-containers-cli-3.1.1}/docs/developer/explanations/decisions/0001-record-architecture-decisions.rst +0 -0
  52. {edge-containers-cli-3.0.4 → edge-containers-cli-3.1.1}/docs/developer/explanations/decisions/0002-switched-to-pip-skeleton.rst +0 -0
  53. {edge-containers-cli-3.0.4 → edge-containers-cli-3.1.1}/docs/developer/explanations/decisions.rst +0 -0
  54. {edge-containers-cli-3.0.4 → edge-containers-cli-3.1.1}/docs/developer/how-to/build-docs.rst +0 -0
  55. {edge-containers-cli-3.0.4 → edge-containers-cli-3.1.1}/docs/developer/how-to/contribute.rst +0 -0
  56. {edge-containers-cli-3.0.4 → edge-containers-cli-3.1.1}/docs/developer/how-to/lint.rst +0 -0
  57. {edge-containers-cli-3.0.4 → edge-containers-cli-3.1.1}/docs/developer/how-to/make-release.rst +0 -0
  58. {edge-containers-cli-3.0.4 → edge-containers-cli-3.1.1}/docs/developer/how-to/pin-requirements.rst +0 -0
  59. {edge-containers-cli-3.0.4 → edge-containers-cli-3.1.1}/docs/developer/how-to/run-tests.rst +0 -0
  60. {edge-containers-cli-3.0.4 → edge-containers-cli-3.1.1}/docs/developer/how-to/static-analysis.rst +0 -0
  61. {edge-containers-cli-3.0.4 → edge-containers-cli-3.1.1}/docs/developer/how-to/test-container.rst +0 -0
  62. {edge-containers-cli-3.0.4 → edge-containers-cli-3.1.1}/docs/developer/how-to/update-tools.rst +0 -0
  63. {edge-containers-cli-3.0.4 → edge-containers-cli-3.1.1}/docs/developer/index.rst +0 -0
  64. {edge-containers-cli-3.0.4 → edge-containers-cli-3.1.1}/docs/developer/reference/standards.rst +0 -0
  65. {edge-containers-cli-3.0.4 → edge-containers-cli-3.1.1}/docs/developer/tutorials/dev-install.rst +0 -0
  66. {edge-containers-cli-3.0.4 → edge-containers-cli-3.1.1}/docs/genindex.rst +0 -0
  67. {edge-containers-cli-3.0.4 → edge-containers-cli-3.1.1}/docs/images/dls-favicon.ico +0 -0
  68. {edge-containers-cli-3.0.4 → edge-containers-cli-3.1.1}/docs/images/dls-logo.svg +0 -0
  69. {edge-containers-cli-3.0.4 → edge-containers-cli-3.1.1}/docs/index.rst +0 -0
  70. {edge-containers-cli-3.0.4 → edge-containers-cli-3.1.1}/docs/user/explanations/docs-structure.rst +0 -0
  71. {edge-containers-cli-3.0.4 → edge-containers-cli-3.1.1}/docs/user/how-to/run-container.rst +0 -0
  72. {edge-containers-cli-3.0.4 → edge-containers-cli-3.1.1}/docs/user/index.rst +0 -0
  73. {edge-containers-cli-3.0.4 → edge-containers-cli-3.1.1}/docs/user/reference/api.rst +0 -0
  74. {edge-containers-cli-3.0.4 → edge-containers-cli-3.1.1}/docs/user/tutorials/installation.rst +0 -0
  75. {edge-containers-cli-3.0.4 → edge-containers-cli-3.1.1}/setup.cfg +0 -0
  76. {edge-containers-cli-3.0.4 → edge-containers-cli-3.1.1}/src/edge_containers_cli/__init__.py +0 -0
  77. {edge-containers-cli-3.0.4 → edge-containers-cli-3.1.1}/src/edge_containers_cli/__main__.py +0 -0
  78. {edge-containers-cli-3.0.4 → edge-containers-cli-3.1.1}/src/edge_containers_cli/autocomplete.py +0 -0
  79. {edge-containers-cli-3.0.4 → edge-containers-cli-3.1.1}/src/edge_containers_cli/cmds/__init__.py +0 -0
  80. {edge-containers-cli-3.0.4 → edge-containers-cli-3.1.1}/src/edge_containers_cli/logging.py +0 -0
  81. {edge-containers-cli-3.0.4 → edge-containers-cli-3.1.1}/src/edge_containers_cli/shell.py +0 -0
  82. {edge-containers-cli-3.0.4 → edge-containers-cli-3.1.1}/src/edge_containers_cli.egg-info/dependency_links.txt +0 -0
  83. {edge-containers-cli-3.0.4 → edge-containers-cli-3.1.1}/src/edge_containers_cli.egg-info/entry_points.txt +0 -0
  84. {edge-containers-cli-3.0.4 → edge-containers-cli-3.1.1}/src/edge_containers_cli.egg-info/top_level.txt +0 -0
  85. {edge-containers-cli-3.0.4 → edge-containers-cli-3.1.1}/tests/__init__.py +0 -0
  86. {edge-containers-cli-3.0.4 → edge-containers-cli-3.1.1}/tests/data/example-ibek-config/ioc.db +0 -0
  87. {edge-containers-cli-3.0.4 → edge-containers-cli-3.1.1}/tests/data/example-ibek-config/ioc.yaml +0 -0
  88. {edge-containers-cli-3.0.4 → edge-containers-cli-3.1.1}/tests/data/services/bl45p-ea-ioc-01/config/ioc.yaml +0 -0
  89. {edge-containers-cli-3.0.4 → edge-containers-cli-3.1.1}/tests/data/services/bl45p-ea-ioc-01/pretend_helm.tgz +0 -0
  90. {edge-containers-cli-3.0.4 → edge-containers-cli-3.1.1}/tests/data/services/bl45p-ea-ioc-01/values.yaml +0 -0
  91. {edge-containers-cli-3.0.4 → edge-containers-cli-3.1.1}/tests/test_cli.py +0 -0
  92. {edge-containers-cli-3.0.4 → edge-containers-cli-3.1.1}/tests/test_local.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@de2c0eb89ae2a093876385947365aca7b0e5f844 # v0.1.15
26
+ uses: softprops/action-gh-release@9d7c94cfd0a1f3ed45544c887983e9fa900f0564 # 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: "*"
@@ -0,0 +1,36 @@
1
+ on:
2
+ workflow_call:
3
+ inputs:
4
+ python-version:
5
+ type: string
6
+ description: The version of python to install
7
+ required: true
8
+ runs-on:
9
+ type: string
10
+ description: The runner to run this job on
11
+ required: true
12
+
13
+ jobs:
14
+ run:
15
+ runs-on: ${{ inputs.runs-on }}
16
+ env:
17
+ EC_INTERACTIVE_TESTING: "true"
18
+ EC_K8S_NAMESPACE: "default"
19
+ EC_SERVICES_REPO: "https://github.com/marcelldls/bl01t.git"
20
+ steps:
21
+ - name: start minikube
22
+ id: minikube
23
+ uses: medyagh/setup-minikube@master
24
+ - run: kubectl version
25
+ - run: helm version
26
+ - name: Checkout
27
+ uses: actions/checkout@v4
28
+ - name: Install latest versions of python packages
29
+ uses: ./.github/actions/install_requirements
30
+ with:
31
+ python-version: ${{ inputs.python-version }}
32
+ pip-install: ".[dev]"
33
+ - name: Set up namespace
34
+ run: ec deploy epics-pvcs 2024.2.1
35
+ - name: system tests
36
+ run: tox -e tests
@@ -34,6 +34,19 @@ jobs:
34
34
  secrets:
35
35
  CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
36
36
 
37
+ system:
38
+ needs: check
39
+ if: needs.check.outputs.branch-pr == ''
40
+ strategy:
41
+ matrix:
42
+ runs-on: ["ubuntu-latest"]
43
+ python-version: ["3.11"]
44
+ fail-fast: false
45
+ uses: ./.github/workflows/_sys_test.yml
46
+ with:
47
+ runs-on: ${{ matrix.runs-on }}
48
+ python-version: ${{ matrix.python-version }}
49
+
37
50
  dist:
38
51
  needs: check
39
52
  if: needs.check.outputs.branch-pr == ''
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: edge-containers-cli
3
- Version: 3.0.4
3
+ Version: 3.1.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
@@ -219,8 +219,7 @@ Requires-Dist: typer[all]
219
219
  Requires-Dist: requests
220
220
  Requires-Dist: ruamel.yaml
221
221
  Requires-Dist: jinja2
222
- Requires-Dist: pandas
223
- Requires-Dist: pyarrow
222
+ Requires-Dist: polars
224
223
  Provides-Extra: dev
225
224
  Requires-Dist: copier; extra == "dev"
226
225
  Requires-Dist: pipdeptree; extra == "dev"
@@ -19,8 +19,7 @@ dependencies = [
19
19
  "requests",
20
20
  "ruamel.yaml",
21
21
  "jinja2",
22
- "pandas",
23
- "pyarrow",
22
+ "polars",
24
23
  ]
25
24
 
26
25
  dynamic = ["version"]
@@ -12,5 +12,5 @@ __version__: str
12
12
  __version_tuple__: VERSION_TUPLE
13
13
  version_tuple: VERSION_TUPLE
14
14
 
15
- __version__ = version = '3.0.4'
16
- __version_tuple__ = version_tuple = (3, 0, 4)
15
+ __version__ = version = '3.1.1'
16
+ __version_tuple__ = version_tuple = (3, 1, 1)
@@ -1,6 +1,7 @@
1
1
  import tempfile
2
2
  from pathlib import Path
3
3
 
4
+ import polars
4
5
  import typer
5
6
  from natsort import natsorted
6
7
 
@@ -159,18 +160,14 @@ def list(
159
160
  ctx: typer.Context,
160
161
  ):
161
162
  """List all IOCs/services available in the helm registry"""
162
- typer.echo(typer.style(f"{'Available services:':35}Latest instance:", bold=True))
163
163
  tmp_dir = Path(tempfile.mkdtemp())
164
164
  svc_graph = create_svc_graph(ctx.obj.beamline_repo, tmp_dir)
165
165
  svc_list = natsorted(svc_graph.keys())
166
166
  log.debug(f"svc_graph = {svc_graph}")
167
167
 
168
- for svc in svc_list:
169
- if len(svc_graph[svc]) == 0:
170
- latest_instance = "None found."
171
- else:
172
- latest_instance = natsorted(svc_graph[svc])[-1]
173
- typer.echo(f"{svc:35}{latest_instance}")
168
+ versions = [natsorted(svc_graph[svc])[-1] for svc in svc_list]
169
+ services_df = polars.from_dict({"name": svc_list, "version": versions})
170
+ print(services_df)
174
171
 
175
172
  cleanup_temp(tmp_dir)
176
173
 
@@ -183,7 +180,6 @@ def instances(
183
180
  ),
184
181
  ):
185
182
  """List all versions of the IOC/service available in the helm registry"""
186
- typer.echo(f"Available instances for {service_name}:")
187
183
  tmp_dir = Path(tempfile.mkdtemp())
188
184
  svc_graph = create_svc_graph(ctx.obj.beamline_repo, tmp_dir)
189
185
  try:
@@ -192,7 +188,8 @@ def instances(
192
188
  svc_list = []
193
189
 
194
190
  sorted_list = natsorted(svc_list)[::-1]
195
- typer.echo(" ".join(sorted_list))
191
+ services_df = polars.from_dict({"version": sorted_list})
192
+ print(services_df)
196
193
 
197
194
  cleanup_temp(tmp_dir)
198
195
 
@@ -1,5 +1,4 @@
1
1
  import tempfile
2
- from datetime import datetime
3
2
  from pathlib import Path
4
3
  from typing import Optional
5
4
 
@@ -7,7 +6,13 @@ import typer
7
6
 
8
7
  import edge_containers_cli.globals as globals
9
8
  import edge_containers_cli.shell as shell
10
- from edge_containers_cli.utils import chdir, check_instance_path, cleanup_temp, log
9
+ from edge_containers_cli.utils import (
10
+ chdir,
11
+ check_instance_path,
12
+ cleanup_temp,
13
+ local_version,
14
+ log,
15
+ )
11
16
 
12
17
 
13
18
  class Helm:
@@ -31,9 +36,7 @@ class Helm:
31
36
  self.beamline_repo = repo
32
37
  self.namespace = namespace
33
38
  self.args = args
34
- self.version = version or datetime.strftime(
35
- datetime.now(), "%Y.%-m.%-d-b%-H.%-M"
36
- )
39
+ self.version = version or local_version()
37
40
  self.template = template
38
41
 
39
42
  self.tmp = Path(tempfile.mkdtemp())
@@ -10,13 +10,13 @@ from io import StringIO
10
10
  from pathlib import Path
11
11
  from typing import Optional
12
12
 
13
- import pandas as pd
13
+ import polars
14
14
  import typer
15
15
 
16
16
  import edge_containers_cli.globals as globals
17
17
  import edge_containers_cli.shell as shell
18
18
  from edge_containers_cli.cmds.helm import Helm
19
- from edge_containers_cli.cmds.kubectl import json_service_info, json_service_types
19
+ from edge_containers_cli.cmds.kubectl import jsonpath_deploy_info, jsonpath_pod_info
20
20
  from edge_containers_cli.logging import log
21
21
 
22
22
 
@@ -72,7 +72,6 @@ class K8sCommands:
72
72
  ):
73
73
  self.namespace: str = ""
74
74
  self.beamline_repo: str = ""
75
- self.service_type: str = ""
76
75
  # TODO isnt ctx always set??
77
76
  if ctx is not None:
78
77
  namespace = ctx.namespace
@@ -170,27 +169,70 @@ class K8sCommands:
170
169
  def ps(self, all: bool, wide: bool):
171
170
  """List all IOCs and Services in the current namespace"""
172
171
 
173
- helm_json = shell.run_command(
174
- f"helm list -n {self.namespace} -o json", interactive=False
175
- )
176
- df = pd.read_json(StringIO(str(helm_json)))
177
- log.debug(df)
172
+ services_df = polars.DataFrame()
178
173
 
179
- if not all:
180
- # only show running services - get the pods list and merge with helm list
181
- services_csv = shell.run_command(
182
- f"kubectl get pods -n {self.namespace} {json_service_info}",
174
+ # Gives all services (running & not running) and their image
175
+ for resource in ["deployment", "statefulset"]:
176
+ kubectl_res = shell.run_command(
177
+ f"kubectl get {resource} -n {self.namespace} {jsonpath_deploy_info}",
183
178
  interactive=False,
184
179
  )
185
- pods_df = pd.read_csv( # type: ignore
186
- StringIO(str(services_csv)),
187
- names=json_service_types.keys(), # type: ignore
188
- dtype=json_service_types, # type: ignore
180
+ if kubectl_res:
181
+ res_df = polars.read_csv(
182
+ StringIO(str(kubectl_res)),
183
+ separator=",",
184
+ has_header=False,
185
+ new_columns=["name", "image"],
186
+ )
187
+ log.debug(res_df)
188
+ services_df = polars.concat([services_df, res_df], how="diagonal")
189
+ if services_df.is_empty():
190
+ print("No deployed services found")
191
+ raise typer.Exit()
192
+
193
+ # Gives the status, restarts for running services
194
+ kubectl_gtpo = shell.run_command(
195
+ f"kubectl get pods -n {self.namespace} {jsonpath_pod_info}",
196
+ interactive=False,
197
+ )
198
+ if kubectl_gtpo:
199
+ gtpo_df = polars.read_csv(
200
+ StringIO(str(kubectl_gtpo)),
201
+ separator=",",
202
+ has_header=False,
203
+ new_columns=["name", "running", "restarts"],
189
204
  )
190
- log.debug(pods_df)
191
-
192
- df = pd.merge(pods_df, df, left_on="name", right_on="name")
193
-
194
- df.drop(columns=["revision", "updated", "status", "chart"], inplace=True)
205
+ services_df = services_df.join(gtpo_df, on="name", how="left")
206
+ services_df = services_df.with_columns(
207
+ polars.col("running").replace({"Running": True}, default=False),
208
+ polars.col("restarts").fill_null(0),
209
+ )
210
+ elif all:
211
+ services_df = services_df.with_columns(
212
+ running=polars.lit(False), restarts=polars.lit(0)
213
+ )
214
+ else:
215
+ print("No running services found")
216
+ raise typer.Exit()
195
217
 
196
- print(df.to_string(index=False))
218
+ # Adds the version, deployment time for all services
219
+ helm_out = shell.run_command(
220
+ f"helm list -n {self.namespace} -o json", interactive=False
221
+ )
222
+ helm_df = polars.read_json(StringIO(str(helm_out)))
223
+ helm_df = helm_df.rename({"app_version": "version", "updated": "deployed"})
224
+ helm_df = helm_df.with_columns(polars.col("deployed").str.slice(0, 19))
225
+ services_df = services_df.join(helm_df, on="name", how="left")
226
+ log.debug(services_df)
227
+
228
+ # Arrange columns
229
+ services_df = services_df.select(
230
+ ["name", "version", "running", "restarts", "deployed", "image"]
231
+ )
232
+ if not all:
233
+ services_df = services_df.filter(polars.col("running").eq(True))
234
+ log.debug(services_df)
235
+ if not wide:
236
+ services_df.drop_in_place("image")
237
+ log.debug(services_df)
238
+ print(services_df)
@@ -0,0 +1,15 @@
1
+ jsonpath_pod_info = (
2
+ "-o jsonpath='"
3
+ r'{range .items[*]}{..labels.app}{","}{.status.phase}'
4
+ r'{","}{..containerStatuses[0].restartCount}'
5
+ r'{"\n"}{end}'
6
+ "'"
7
+ )
8
+
9
+ jsonpath_deploy_info = (
10
+ "-o jsonpath='"
11
+ r'{range .items[*]}{.metadata.name}{","}'
12
+ r"{range .spec.template.spec.containers[*]}{.image}"
13
+ r'{"\n"}{end}{end}'
14
+ "'"
15
+ )
@@ -9,6 +9,7 @@ However, for the moment, Using this by connecting to each server and running
9
9
  tool like Portainer is a decent workflow.
10
10
  """
11
11
 
12
+ import json
12
13
  import re
13
14
  import tempfile
14
15
  from datetime import datetime
@@ -16,7 +17,7 @@ from io import StringIO
16
17
  from pathlib import Path
17
18
  from typing import Optional
18
19
 
19
- import pandas as pd
20
+ import polars
20
21
  import requests
21
22
  import typer
22
23
 
@@ -31,6 +32,7 @@ from edge_containers_cli.utils import (
31
32
  cleanup_temp,
32
33
  generic_ioc_from_image,
33
34
  get_instance_image_name,
35
+ local_version,
34
36
  )
35
37
 
36
38
 
@@ -73,7 +75,7 @@ class LocalCommands:
73
75
  self.docker.remove(self.service_name)
74
76
 
75
77
  def _do_deploy(self, ioc_instance: Path, version: str, args: str):
76
- service_name, ioc_path = check_instance_path(ioc_instance)
78
+ service_name, _ = check_instance_path(ioc_instance)
77
79
 
78
80
  image = get_instance_image_name(ioc_instance)
79
81
  log.debug(f"deploying {ioc_instance} with image {image}")
@@ -120,7 +122,7 @@ class LocalCommands:
120
122
  Use a local copy of an ioc instance definition to deploy a temporary
121
123
  version of the IOC to the local docker instance
122
124
  """
123
- version = datetime.strftime(datetime.now(), "%Y.%-m.%-d-b%-H.%-M")
125
+ version = local_version()
124
126
  if not yes:
125
127
  typer.echo(
126
128
  f"Deploy TEMPORARY version {version} "
@@ -163,35 +165,54 @@ class LocalCommands:
163
165
  def ps(self, all: bool, wide: bool):
164
166
  all_arg = " --all" if all else ""
165
167
 
166
- # We have to build the table ourselves because docker is unable to
167
- # format a table with labels.
168
- format = "{{.Names}}%{{.Labels}}%{{.State}}%{{.Image}}"
169
-
170
- result = shell.run_command(
171
- f"{self.docker.docker} ps{all_arg} --filter label=is_IOC=true "
172
- f'--format "{format}"',
168
+ # List services
169
+ avail_services = shell.run_command(
170
+ f"{self.docker.docker} ps{all_arg} -q --filter label=is_IOC=true",
173
171
  interactive=False,
174
172
  )
175
173
 
176
- # this regex extracts just the version from the set of all labels,
177
- # docker and podman have different output formats
178
- if self.docker.is_docker:
179
- result = re.sub(r"%.*?[,%]version=([^,%]*).*?%", r"%\1%", str(result))
180
- else:
181
- result = re.sub(r"%.*? version:([^\],%]*).*?%", r"%\1%", str(result))
182
-
183
- result = result.replace("%", ",")
184
-
185
- df = pd.read_csv( # type: ignore
186
- StringIO(result),
187
- names=["name", "version", "state", "image"], # type: ignore
174
+ if not avail_services:
175
+ if all_arg:
176
+ print("No deployed services found")
177
+ else:
178
+ print("No running services found")
179
+ raise typer.Exit()
180
+
181
+ # Retrieve data
182
+ result_json = shell.run_command(
183
+ f"{self.docker.docker} inspect "
184
+ f"$({self.docker.docker} ps{all_arg} -q --filter label=is_IOC=true)",
185
+ interactive=False,
188
186
  )
189
- log.debug(df)
187
+ log.debug(result_json)
188
+ services_dicts = json.load(StringIO(str(result_json)))
189
+
190
+ select_data = []
191
+ for service in services_dicts:
192
+ # Make docker output look like podman
193
+ if self.docker.is_docker:
194
+ service["Name"] = service["Name"][1:] # inspect leads name with /
195
+
196
+ # Note if adding more keys that there are differences
197
+ # between what is available between docker and podman
198
+ select_data.append(
199
+ {
200
+ "name": service["Name"],
201
+ "version": service["Config"]["Labels"]["version"],
202
+ "running": "true" if service["State"]["Running"] else "false",
203
+ "restarts": service["RestartCount"],
204
+ "deployed": datetime.strptime(
205
+ service["Created"].split(".")[0], "%Y-%m-%dT%H:%M:%S"
206
+ ),
207
+ "image": service["Config"]["Image"],
208
+ }
209
+ )
210
+ log.debug(select_data)
190
211
 
191
- if len(df.index) == 0:
192
- typer.echo("No running services found")
193
- else:
194
- print(df.to_string(index=False))
212
+ services_df = polars.DataFrame(select_data)
213
+ if not wide:
214
+ services_df.drop_in_place("image")
215
+ print(services_df)
195
216
 
196
217
  def validate_instance(self, ioc_instance: Path):
197
218
  check_instance_path(ioc_instance)
@@ -3,10 +3,8 @@ Utility functions for working interacting with docker / podman CLI
3
3
  """
4
4
 
5
5
  import re
6
- import sys
7
6
  from pathlib import Path
8
7
  from time import sleep
9
- from typing import Optional
10
8
 
11
9
  import typer
12
10
 
@@ -14,12 +12,6 @@ import edge_containers_cli.globals as globals
14
12
  import edge_containers_cli.shell as shell
15
13
  from edge_containers_cli.logging import log
16
14
 
17
- IMAGE_TAG = "local"
18
- MOUNTED_FILES = ["/.bashrc", "/.inputrc", "/.bash_eternal_history"]
19
-
20
- # podman needs this security option to allow containers to mount tmp etc.
21
- PODMAN_OPT = " --security-opt=label=type:container_runtime_t"
22
-
23
15
 
24
16
  class Docker:
25
17
  """
@@ -67,78 +59,6 @@ class Docker:
67
59
 
68
60
  log.debug(f"buildx={self.is_buildx} ({result})")
69
61
 
70
- def _all_params(
71
- self, args: str, mounts: Optional[list[Path]] = None, exec: bool = False
72
- ):
73
- """
74
- set up parameters for call to docker/podman
75
- """
76
- opts = PODMAN_OPT if not self.is_docker and not exec else ""
77
-
78
- if self.devcontainer:
79
- if sys.stdin.isatty():
80
- # interactive
81
- env = "-e DISPLAY -e SHELL -e TERM -it"
82
- else:
83
- env = "-e DISPLAY -e SHELL"
84
-
85
- volumes = ""
86
- for file in MOUNTED_FILES:
87
- file_path = Path(file)
88
- if file_path.exists():
89
- volumes += f" -v {file}:/root/{file_path.name}"
90
- if mounts is not None:
91
- for mount in mounts:
92
- volumes += f" -v {mount}"
93
-
94
- log.debug(f"env={env} volumes={volumes} opts={opts}")
95
-
96
- params = f"{env}{opts}{volumes}" + f" {args}" if args else ""
97
- else:
98
- params = f"{opts}" + (f"{args}" if args else "")
99
-
100
- return params
101
-
102
- def run(self, name: str, args: str = "", mounts: Optional[list[Path]] = None):
103
- """
104
- run a command in a local container
105
- """
106
- params = self._all_params(args, mounts=mounts)
107
- shell.run_command(
108
- f"{self.docker} run --rm --name {name} {params}", interactive=True
109
- )
110
-
111
- def build(
112
- self,
113
- context: str,
114
- name: str,
115
- target: str,
116
- args: str = "",
117
- cache_from: str = "",
118
- cache_to: str = "",
119
- push: bool = False,
120
- arch: globals.Architecture = globals.Architecture.linux,
121
- ):
122
- """
123
- build a container
124
- """
125
- if self.is_buildx:
126
- cmd = f"{self.docker} buildx"
127
- shell.run_command(
128
- f"{cmd} create --driver docker-container --use", interactive=False
129
- )
130
- args += f" --cache-from={cache_from}" if cache_from else ""
131
- args += f" --cache-to={cache_to},mode=max" if cache_to else ""
132
- args += " --push" if push else " --load "
133
- else:
134
- cmd = f"{self.docker}"
135
-
136
- t_arch = f" --build-arg TARGET_ARCHITECTURE={arch}" if self.devcontainer else ""
137
-
138
- shell.run_command(
139
- f"{cmd} build --target {target}{t_arch} {args} -t {name} {context}"
140
- )
141
-
142
62
  def exec(
143
63
  self,
144
64
  container: str,
@@ -0,0 +1,71 @@
1
+ """
2
+ Utility functions for working with git
3
+ """
4
+
5
+ import os
6
+ from pathlib import Path
7
+
8
+ import edge_containers_cli.shell as shell
9
+ from edge_containers_cli.logging import log
10
+ from edge_containers_cli.shell import check_services_repo
11
+ from edge_containers_cli.utils import chdir
12
+
13
+
14
+ def create_svc_graph(repo: str, folder: Path) -> dict:
15
+ """
16
+ return a dictionary of the available IOCs (by discovering the children
17
+ to the services/ folder in the beamline repo) as well as a list of the corresponding
18
+ available versions for each IOC (by discovering the tags in the beamline repo at
19
+ which changes to the instance were made since the last tag) and the respective
20
+ list of available versions
21
+ """
22
+ svc_graph = {}
23
+
24
+ check_services_repo(repo)
25
+ shell.run_command(f"git clone {repo} {folder}", interactive=False)
26
+ path_list = os.listdir(os.path.join(folder, "services"))
27
+ service_list = [
28
+ path
29
+ for path in path_list
30
+ if os.path.isdir(os.path.join(folder, "services", path))
31
+ ]
32
+ log.debug(f"service_list = {service_list}")
33
+
34
+ with chdir(folder): # From python 3.11 can use contextlib.chdir(folder)
35
+ for service_name in service_list:
36
+ service_name = Path(service_name).name
37
+ result = str(
38
+ shell.run_command("git tag --sort=committerdate", interactive=False)
39
+ )
40
+ log.debug(f"checking these tags for changes in the instance: {result}")
41
+
42
+ version_list = []
43
+ tags = result.split("\n")
44
+ tags.remove("")
45
+
46
+ for tag_no, _ in enumerate(tags):
47
+ # Check initial configuration
48
+ if not tag_no:
49
+ cmd = f"git ls-tree -r {tags[tag_no]} --name-only"
50
+ result = str(
51
+ shell.run_command(cmd, interactive=False, error_OK=True)
52
+ )
53
+ if service_name in result:
54
+ version_list.append(tags[tag_no])
55
+
56
+ # Check repo changes
57
+ else:
58
+ cmd = f"git diff --name-only {tags[tag_no-1]} {tags[tag_no]}"
59
+ result = str(
60
+ shell.run_command(cmd, interactive=False, error_OK=True)
61
+ )
62
+ if service_name in result:
63
+ version_list.append(tags[tag_no])
64
+
65
+ # Capture services committed since the most recent tag
66
+ if not version_list:
67
+ version_list.append("")
68
+
69
+ svc_graph[service_name] = version_list
70
+
71
+ return svc_graph