Skip to content

Commit 9d8550e

Browse files
authored
Merge pull request #3 from epics-containers/RTEMS
first working draft
2 parents b6a6b37 + bf35637 commit 9d8550e

File tree

15 files changed

+587
-84
lines changed

15 files changed

+587
-84
lines changed

.copier-answers.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,11 @@ component_owner: group:default/sscc
77
description: Support for a K8S proxy container in controlling and monitoring RTEMS
88
EPICS IOCs
99
distribution_name: rtems-proxy
10-
docker: true
10+
docker: false
1111
docs_type: README
1212
git_platform: github.com
1313
github_org: epics-containers
1414
package_name: rtems_proxy
1515
pypi: true
1616
repo_name: rtems-proxy
17-
type_checker: pyright
17+
type_checker: mypy

.devcontainer/devcontainer.json

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,7 @@
22
{
33
"name": "Python 3 Developer Container",
44
"build": {
5-
"dockerfile": "../Dockerfile",
6-
"target": "developer"
5+
"dockerfile": "../Dockerfile"
76
},
87
"remoteEnv": {
98
// Allow X11 apps to run inside the container

.github/workflows/_container.yml

Lines changed: 0 additions & 56 deletions
This file was deleted.

.github/workflows/_pypi.yml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
on:
22
workflow_call:
3+
secrets:
4+
PYPI_TOKEN:
5+
required: true
36

47
jobs:
58
upload:
@@ -15,3 +18,5 @@ jobs:
1518

1619
- name: Publish to PyPI using trusted publishing
1720
uses: pypa/gh-action-pypi-publish@release/v1
21+
with:
22+
password: ${{ secrets.PYPI_TOKEN }}

.github/workflows/ci.yml

Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -34,25 +34,20 @@ jobs:
3434
secrets:
3535
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
3636

37-
container:
38-
needs: check
39-
if: needs.check.outputs.branch-pr == ''
40-
uses: ./.github/workflows/_container.yml
41-
permissions:
42-
packages: write
43-
4437
dist:
4538
needs: check
4639
if: needs.check.outputs.branch-pr == ''
4740
uses: ./.github/workflows/_dist.yml
48-
41+
4942
pypi:
5043
if: github.ref_type == 'tag'
5144
needs: dist
5245
uses: ./.github/workflows/_pypi.yml
5346
permissions:
5447
id-token: write
55-
48+
secrets:
49+
PYPI_TOKEN: ${{ secrets.PYPI_TOKEN }}
50+
5651
release:
5752
if: github.ref_type == 'tag'
5853
needs: [dist]

README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@ how it does it, and why people should use it.
1313
Source | <https://github.com/epics-containers/rtems-proxy>
1414
:---: | :---:
1515
PyPI | `pip install rtems-proxy`
16-
Docker | `docker run ghcr.io/epics-containers/rtems-proxy:latest`
1716
Releases | <https://github.com/epics-containers/rtems-proxy/releases>
1817

1918
This is where you should put some images or code snippets that illustrate
@@ -28,6 +27,8 @@ print(f"Hello rtems_proxy {__version__}")
2827

2928
Or if it is a commandline tool then you might put some example commands here:
3029

30+
3131
```
3232
python -m rtems_proxy --version
3333
```
34+

proxy-start.sh

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
#!/bin/bash
2+
3+
set -x
4+
5+
# This is the folder the PVC for the nfsv2tftp shared volume is mounted into.
6+
export RTEMS_TFTP_PATH=${RTEMS_TFTP_PATH:-/nfsv2-tftp}
7+
8+
if [ ! -d ${RTEMS_TFTP_PATH} ]; then
9+
echo "ERROR: No PVC folder found."
10+
# make a folder for testing outside of the cluster
11+
mkdir -p ${RTEMS_TFTP_PATH}
12+
fi
13+
14+
# copy the IOC instance's runtime assets into the shared volume
15+
cp -rL /epics/ioc ${RTEMS_TFTP_PATH}
16+
cp -r /epics/runtime ${RTEMS_TFTP_PATH}
17+
# move binary to the root for shorter paths
18+
mv ${RTEMS_TFTP_PATH}/ioc/bin/*/ioc.boot ${RTEMS_TFTP_PATH}
19+
# fix up the paths in st.cmd
20+
sed -i "s|/epics/|/iocs/${IOC_LOCATION}/${IOC_NAME}/|" ${RTEMS_TFTP_PATH}/runtime/st.cmd
21+
22+
# keep the container running ...
23+
while true; do
24+
sleep 2
25+
done

pyproject.toml

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ classifiers = [
1414
"Programming Language :: Python :: 3.11",
1515
]
1616
description = "Support for a K8S proxy container in controlling and monitoring RTEMS EPICS IOCs"
17-
dependencies = [] # Add project dependencies here, e.g. ["click", "numpy"]
17+
dependencies = ["jinja2", "pexpect", "ruamel.yaml", "telnetlib3", "typer"]
1818
dynamic = ["version"]
1919
license.file = "LICENSE"
2020
readme = "README.md"
@@ -23,9 +23,9 @@ requires-python = ">=3.7"
2323
[project.optional-dependencies]
2424
dev = [
2525
"copier",
26+
"mypy",
2627
"pipdeptree",
2728
"pre-commit",
28-
"pyright",
2929
"pytest",
3030
"pytest-cov",
3131
"ruff",
@@ -34,7 +34,7 @@ dev = [
3434
]
3535

3636
[project.scripts]
37-
rtems-proxy = "rtems_proxy.__main__:main"
37+
rtems-proxy = "rtems_proxy.__main__:cli"
3838

3939
[project.urls]
4040
GitHub = "https://github.com/epics-containers/rtems-proxy"
@@ -47,8 +47,8 @@ name = "Giles Knap"
4747
[tool.setuptools_scm]
4848
write_to = "src/rtems_proxy/_version.py"
4949

50-
[tool.pyright]
51-
reportMissingImports = false # Ignore missing stubs in imported modules
50+
[tool.mypy]
51+
ignore_missing_imports = true # Ignore missing stubs in imported modules
5252

5353
[tool.pytest.ini_options]
5454
# Run pytest with all our checkers, and don't spam us with massive tracebacks on error
@@ -81,14 +81,19 @@ passenv = *
8181
allowlist_externals =
8282
pytest
8383
pre-commit
84-
pyright
84+
mypy
8585
commands =
8686
pre-commit: pre-commit run --all-files {posargs}
87-
type-checking: pyright src tests {posargs}
87+
type-checking: mypy src tests {posargs}
8888
tests: pytest --cov=rtems_proxy --cov-report term --cov-report xml:cov.xml {posargs}
8989
"""
9090

9191
[tool.ruff]
92+
ignore = [
93+
"B008", # Do not perform unnecessary work in __all__
94+
"C408", # Unnecessary collection call - e.g. list(...) instead of [...]
95+
"E501", # Line too long, should be fixed by black.
96+
]
9297
src = ["src", "tests"]
9398
line-length = 88
9499
lint.select = [

requirements.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# these requirements are for additional python packages to install into the
2+
# container
3+
ibek==1.7.2b6

src/rtems_proxy/__main__.py

Lines changed: 139 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,148 @@
1-
from argparse import ArgumentParser
1+
from pathlib import Path
2+
from typing import Optional
3+
4+
import typer
5+
from jinja2 import Template
6+
from ruamel.yaml import YAML
27

38
from . import __version__
9+
from .copy import copy_rtems
10+
from .globals import GLOBALS
11+
from .telnet import ioc_connect
412

513
__all__ = ["main"]
614

15+
cli = typer.Typer()
16+
17+
18+
def version_callback(value: bool):
19+
if value:
20+
typer.echo(__version__)
21+
raise typer.Exit()
22+
23+
24+
@cli.callback()
25+
def main(
26+
version: Optional[bool] = typer.Option(
27+
None,
28+
"--version",
29+
callback=version_callback,
30+
is_eager=True,
31+
help="Print the version of ibek and exit",
32+
),
33+
):
34+
"""
35+
Proxy for RTEMS IOCs controlling and monitoring
36+
"""
37+
38+
39+
@cli.command()
40+
def start(
41+
copy: bool = typer.Option(
42+
True, "--copy/--no-copy", help="copy binaries before connecting"
43+
),
44+
reboot: bool = typer.Option(
45+
True, "--reboot/--no-reboot", help="reboot the IOC first"
46+
),
47+
):
48+
"""
49+
Starts an RTEMS IOC. Places the IOC binaries in the expected location,
50+
restarts the IOC and connects stdio to the IOC console.
51+
52+
This should be called inside of a runtime IOC container after ibek
53+
has generated the runtime assets for the IOC.
54+
55+
The standard 'start.sh' in the runtime IOC will call this entry point if
56+
it detects that EPICS_HOST_ARCH==RTEMS-beatnik
57+
58+
args:
59+
copy: Copy the RTEMS binaries to the IOCs TFTP and NFS directories first
60+
reboot: Reboot the IOC once the binaries are copied and the connection is made
61+
"""
62+
print(
63+
f"Remote control startup of RTEMS IOC {GLOBALS.IOC_NAME}"
64+
f" at {GLOBALS.RTEMS_IOC_IP}"
65+
)
66+
if copy:
67+
copy_rtems()
68+
ioc_connect(GLOBALS.RTEMS_CONSOLE, reboot=reboot)
69+
70+
71+
@cli.command()
72+
def dev(
73+
ioc_repo: Path = typer.Argument(
74+
...,
75+
help="The beamline/accelerator repo holding the IOC instance",
76+
file_okay=False,
77+
exists=True,
78+
),
79+
ioc_name: str = typer.Argument(
80+
...,
81+
help="The name of the IOC instance to work on",
82+
),
83+
):
84+
"""
85+
Sets up a devcontainer to work on an IOC instance. Must be run from within
86+
the developer container for the generic IOC that the instance uses.
87+
88+
args:
89+
ioc_repo: The path to the IOC repository that holds the instance
90+
ioc_name: The name of the IOC instance to work on
91+
"""
92+
93+
ioc_path = ioc_repo / "services" / ioc_name
94+
95+
values = ioc_repo / "helm/shared/values.yaml"
96+
if not values.exists():
97+
typer.echo(f"Global settings file {values} not found. Exiting")
98+
raise typer.Exit(1)
99+
100+
ioc_values = ioc_path / "values.yaml"
101+
if not ioc_values.exists():
102+
typer.echo(f"Instance settings file {ioc_values} not found. Exiting")
103+
raise typer.Exit(1)
104+
105+
env_vars = {}
106+
# TODO in future use pydantic and make a model for this but for now let's cheese it.
107+
with open(values) as fp:
108+
yaml = YAML(typ="safe").load(fp)
109+
try:
110+
ioc_group = yaml["ioc-instance"]["ioc_group"]
111+
for item in yaml["ioc-instance"]["globalEnv"]:
112+
env_vars[item["name"]] = item["value"]
113+
except KeyError:
114+
typer.echo(f"{values} not in expected format")
115+
raise typer.Exit(1) from None
116+
117+
with open(ioc_values) as fp:
118+
yaml = YAML(typ="safe").load(fp)
119+
try:
120+
for item in yaml["shared"]["ioc-instance"]["iocEnv"]:
121+
env_vars[item["name"]] = item["value"]
122+
except KeyError:
123+
typer.echo(f"{ioc_values} not in expected format")
124+
raise typer.Exit(1) from None
125+
126+
this_dir = Path(__file__).parent
127+
template = Path(this_dir / "rsync.sh.jinja").read_text()
128+
129+
script = Template(template).render(
130+
env_vars=env_vars,
131+
ioc_group=ioc_group,
132+
ioc_name=ioc_name,
133+
ioc_path=ioc_path,
134+
)
135+
136+
script_file = Path("/tmp/dev_proxy.sh")
137+
script_file.write_text(script)
7138

8-
def main(args=None):
9-
parser = ArgumentParser()
10-
parser.add_argument("-v", "--version", action="version", version=__version__)
11-
args = parser.parse_args(args)
139+
typer.echo(f"\nIOC {ioc_name} dev environment prepared for {ioc_repo}")
140+
typer.echo("You can now change and compile support module or iocs.")
141+
typer.echo("Then start the ioc with '/epics/ioc/start.sh'")
142+
typer.echo(f"\n\nPlease first source {script_file} to set up the dev environment.")
12143

13144

14-
# test with: python -m rtems_proxy
145+
# test with:
146+
# pipenv run python -m ibek
15147
if __name__ == "__main__":
16-
main()
148+
cli()

0 commit comments

Comments
 (0)