From 830b7027c4f5a96aa71489d3c1af7e8a7c013cd4 Mon Sep 17 00:00:00 2001 From: Giles Knap Date: Thu, 28 Mar 2024 07:26:05 +0000 Subject: [PATCH] update to template 3.4.0 --- .copier-answers.yml | 9 + .devcontainer/devcontainer.json | 47 +++-- .devcontainer/initializeCommand | 13 +- .devcontainer/postCreateCommand | 30 ++-- .github/dependabot.yml | 4 - .github/workflows/build.yml | 103 +++++++---- .gitignore | 15 +- .vscode/launch.json | 49 +++++ Dockerfile | 42 +++-- README.md | 19 +- build | 33 ++-- ibek-support | 2 +- ioc/.gitignore | 11 ++ ioc/Makefile | 7 + ioc/configure/CONFIG | 30 ++++ ioc/configure/CONFIG_SITE | 4 + ioc/configure/Makefile | 8 + ioc/configure/RELEASE | 5 + ioc/configure/RULES | 7 + ioc/configure/RULES.ioc | 2 + ioc/configure/RULES_DIRS | 2 + ioc/configure/RULES_TOP | 3 + ioc/install.sh | 18 ++ ioc/install_proxy.sh | 23 +++ ioc/iocApp/src/Makefile | 21 +++ ioc/iocApp/src/iocMain.cpp | 20 +++ ioc/liveness.sh | 40 +++++ ioc/start.sh | 179 +++++++++++++++++++ ioc/stop.sh | 19 ++ requirements.txt | 3 + requirements_ec.txt | 3 - tests/config/bl00i-ea-test-ioc.yaml | 29 +++ tests/{example-ibek-config => config}/ioc.db | 0 tests/example-config/calc.db | 26 --- tests/example-config/ioc.substitutions | 16 -- tests/example-config/st.cmd | 12 -- tests/example-ibek-config/ioc.yaml | 9 - tests/run-tests.sh | 52 +++--- 38 files changed, 700 insertions(+), 215 deletions(-) create mode 100644 .copier-answers.yml create mode 100644 .vscode/launch.json create mode 100644 ioc/.gitignore create mode 100644 ioc/Makefile create mode 100644 ioc/configure/CONFIG create mode 100644 ioc/configure/CONFIG_SITE create mode 100644 ioc/configure/Makefile create mode 100644 ioc/configure/RELEASE create mode 100644 ioc/configure/RULES create mode 100644 ioc/configure/RULES.ioc create mode 100644 ioc/configure/RULES_DIRS create mode 100644 ioc/configure/RULES_TOP create mode 100755 ioc/install.sh create mode 100644 ioc/install_proxy.sh create mode 100644 ioc/iocApp/src/Makefile create mode 100644 ioc/iocApp/src/iocMain.cpp create mode 100755 ioc/liveness.sh create mode 100755 ioc/start.sh create mode 100644 ioc/stop.sh create mode 100644 requirements.txt delete mode 100644 requirements_ec.txt create mode 100644 tests/config/bl00i-ea-test-ioc.yaml rename tests/{example-ibek-config => config}/ioc.db (100%) delete mode 100644 tests/example-config/calc.db delete mode 100644 tests/example-config/ioc.substitutions delete mode 100644 tests/example-config/st.cmd delete mode 100644 tests/example-ibek-config/ioc.yaml diff --git a/.copier-answers.yml b/.copier-answers.yml new file mode 100644 index 0000000..db1b2cc --- /dev/null +++ b/.copier-answers.yml @@ -0,0 +1,9 @@ +# Changes here will be overwritten by Copier; NEVER EDIT MANUALLY +_commit: 3.4.0 +_src_path: gh:epics-containers/ioc-template +description: Generic IOC for simulation motors +git_platform: github.com +github_org: epics-containers +name: ioc-motorsim +repo_uri: git@github.com:epics-containers/ioc-motorsim.git +rtems: true diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index d4ccac8..23301e0 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -5,7 +5,11 @@ "dockerfile": "../Dockerfile", "target": "developer", "args": { - "TARGET_ARCHITECTURE": "linux" + // Native target development settings ============================== + "EPICS_TARGET_ARCH": "linux-x86_64" + // Local cross compilation settings ================================ + // "EPICS_TARGET_ARCH": "RTEMS-beatnik", + // "IMAGE_EXT": "-rtems-beatnik" } }, "remoteEnv": { @@ -15,15 +19,16 @@ "EC_PROJECT": "${localWorkspaceFolderBasename}" }, "features": { + // add quality of life features for developers including git config integration "ghcr.io/devcontainers/features/common-utils:2": { - "installZsh": false, - "installOhMyZsh": false, - "installOhMyZshConfig": false, + // don't upgrade to make this similar to the runtime container "upgradePackages": false } }, // IMPORTANT for this devcontainer to work with docker EC_REMOTE_USER must be - // set to vscode. For podman it should be left blank. + // set to vscode. You will run as vscode with full sudo rights. + // For podman it should be left blank. You will run as root but host mounts + // will be owned by your user. "remoteUser": "${localEnv:EC_REMOTE_USER}", "customizations": { "vscode": { @@ -34,37 +39,27 @@ "redhat.vscode-yaml", "ryanluker.vscode-coverage-gutters", "epicsdeb.vscode-epics", - "ms-python.black-formatter" + "charliermarsh.ruff" ] } }, - // Make sure the files we are mapping into the container exist on the host - // You can place any other outside of the container before-launch commands here + // You can place any outside of the container before-launch commands here "initializeCommand": "bash .devcontainer/initializeCommand ${devcontainerId}", - // Hooks the global .bashprofile_dev_container but also can add any other commands - // to run in the container at creation in here + // One time global setup commands inside the container "postCreateCommand": "bash .devcontainer/postCreateCommand ${devcontainerId}", "runArgs": [ // Allow the container to access the host X11 display and EPICS CA "--net=host", - // Make sure SELinux does not disable with access to host filesystems like tmp + // Make sure SELinux does not disable write access to host filesystems like tmp "--security-opt=label=disable" ], - "workspaceMount": "source=${localWorkspaceFolder},target=/epics/${localWorkspaceFolderBasename},type=bind", - "workspaceFolder": "/epics/${localWorkspaceFolderBasename}", + // Mount the parent of the project folder so we can access peer projects + "workspaceMount": "source=${localWorkspaceFolder}/..,target=/workspaces,type=bind", + // mount in other useful files from the host "mounts": [ - // Mount some useful local files from the user's home directory - // By mounting the parent of the workspace we can work on multiple peer projects - "source=${localWorkspaceFolder}/../,target=/repos,type=bind", - // this provides eternal bash history in and out of the container - "source=${localEnv:HOME}/.bash_eternal_history,target=/root/.bash_eternal_history,type=bind", - // this bashrc hooks up the .bashrc_dev_container in the following mount - "source=${localWorkspaceFolder}/.devcontainer/.bashrc,target=/root/.bashrc,type=bind", - // provides a place for you to put your shell customizations for all your dev containers - "source=${localEnv:HOME}/.bashrc_dev_container,target=/root/.bashrc_dev_container,type=bind", - // provides a place to install any packages you want to have across all your dev containers - "source=${localEnv:HOME}/.bashprofile_dev_container,target=/root/.bashprofile_dev_container,type=bind", - // provides the same command line editing experience as your host - "source=${localEnv:HOME}/.inputrc,target=/root/.inputrc,type=bind" + // we also mount the project folder into a know location in the container + // this is where the ibek-support and ioc folders reside in the container build + // in this way the devcontainer and runtime look very similar + "source=${localWorkspaceFolder},target=/epics/generic-source,type=bind" ] } \ No newline at end of file diff --git a/.devcontainer/initializeCommand b/.devcontainer/initializeCommand index 4f32aa2..732ec1f 100644 --- a/.devcontainer/initializeCommand +++ b/.devcontainer/initializeCommand @@ -1,15 +1,6 @@ #!/bin/bash -# make sure all the files we mount into the container exist -for i in \ - .bash_eternal_history \ - .bashrc_dev_container \ - .bashprofile_dev_container \ - .inputrc - do - if [ ! -f $HOME/$i ] ; then - touch $HOME/$i; - fi -done +# custom initialization goes here - runs outside of the dev container +# just before the container is launched but after the container is created echo "devcontainerID ${1}" diff --git a/.devcontainer/postCreateCommand b/.devcontainer/postCreateCommand index df4587c..517f5e8 100644 --- a/.devcontainer/postCreateCommand +++ b/.devcontainer/postCreateCommand @@ -1,5 +1,8 @@ #!/bin/bash +# Custom initialization goes here if needed. +# Runs inside the dev container after the container is created + ################################################################################ # When using docker we will not be root inside the container # the following steps are then required @@ -7,24 +10,23 @@ if [[ $USER != "root" ]] ; then # make sure the non-root user can build iocs and (mounted in) support modules - sudo chown -R ${USER}:${USER} /epics/links /venv - sudo chown -h ${USER}:${USER} /epics /epics/ioc - - # also give non-root user access to the same bash config we use in podman - sudo chmod a+rx /root - for f in .inputrc .bash_eternal_history .bashrc .bashrc_dev_container; do - sudo chmod a+rw /root/$f - ln -sf /root/$f $HOME/$f - done + sudo chown -R ${USER}:${USER} /epics/ibek-defs /epics/pvi-defs /epics/support/configure /venv + sudo chown -h ${USER}:${USER} /epics /epics/ioc /epics/support fi ################################################################################ -# Custom install script for each developer to add whatever they like to -# all epics-containers devcontainers +# Shell customizations for Generic IOC devcontainers ################################################################################ +# add ibek completion to bash and zsh +echo 'source <(ibek --show-completion bash)' >> $HOME/.bashrc +echo 'source <(ibek --show-completion zsh)' >> $HOME/.zshrc + +# pick a theme that does not cause completion corruption in zsh +sed -i $HOME/.zshrc -e 's/ZSH_THEME="devcontainers"/ZSH_THEME="lukerandall"/' -# add user's custom profile container creation script -if [ -f ~/.bashprofile_dev_container ]; then - . ~/.bashprofile_dev_container +# allow personalization of all devcontainers in this subdirectory +# by placing a .devcontainer_rc file in the workspace root +if [[ -f /workspaces/.devcontainer_rc ]] ; then + source /workspaces/.devcontainer_rc fi diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 5c6e6ec..b9e8b67 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -6,7 +6,3 @@ updates: directory: "/" schedule: interval: "daily" - - package-ecosystem: "pip" - directory: "/" - schedule: - interval: "daily" diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 4d85407..2343fd9 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -5,75 +5,108 @@ on: pull_request: jobs: - build-push-images: + build: # pull requests are a duplicate of a branch push if within the same repo. if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name != github.repository permissions: contents: read packages: write - env: - CACHE: /tmp/.buildx-cache strategy: fail-fast: false matrix: + epics-target: [RTEMS-beatnik,linux-x86_64] # , linux-aarch64] include: - - architecture: linux - os: ubuntu-latest + - os: ubuntu-latest # everyone is on ubuntu-latest + - epics-target: RTEMS-beatnik + extension: -rtems-beatnik + platform: linux/amd64 + - epics-target: linux-x86_64 + extension: "" platform: linux/amd64 + # # a temporary name until multi-arch is supported + # - epics-target: linux-aarch64 + # extension: -native-aarch64 + # platform: linux/arm64 + runs-on: ${{ matrix.os }} + env: + TAG: ghcr.io/${{ github.repository_owner }}/${{ github.event.repository.name }}${{ matrix.extension }} steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: submodules: recursive - - name: Cache Docker layers - uses: actions/cache@v2 - with: - path: ${{ env.CACHE }} - key: ${{ runner.os }}-${{ matrix.architecture }}-buildx-${{ github.sha }} - restore-keys: | - ${{ runner.os }}-${{ matrix.architecture }}-buildx- - - name: Log in to GitHub Docker Registry - uses: docker/login-action@v1 + uses: docker/login-action@v3 with: registry: ghcr.io/${{ github.repository_owner }} username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} - - name: Setup python - uses: actions/setup-python@v4 + - name: Set up QEMU + uses: docker/setup-qemu-action@v3 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Build image + uses: docker/build-push-action@v5 with: - python-version: "3.10" + context: . + platforms: ${{ matrix.platform }} + target: runtime + build-args: | + IMAGE_EXT=${{ matrix.extension }} + cache-from: type=gha,scope=${{ matrix.epics-target }} + cache-to: type=gha,mode=max,scope=${{ matrix.epics-target }} + tags: ci_test + load: true + + - name: Test image + # can't test non native without some hardware to run on + if: ${{ matrix.epics-target == 'linux-x86_64' }} + run: tests/run-tests.sh ci_test - - name: Docker Build Script - env: - ARCH: ${{ matrix.architecture }} - PLATFORM: ${{ matrix.platform }} - TAG: ${{ github.ref_name }} - PUSH: ${{ github.event_name == 'push' && startsWith(github.ref, 'refs/tags') }} - CACHE: ${{ env.CACHE }} - run: .github/workflows/build.sh + - name: Push developer image + if: ${{ github.event_name == 'push' && github.ref_type == 'tag' }} + uses: docker/build-push-action@v5 + with: + context: . + platforms: ${{ matrix.platform }} + target: developer + build-args: | + IMAGE_EXT=${{ matrix.extension }} + tags: ${{ env.TAG }}-developer:${{ github.ref_name }} + push: true - - name: Upload schema as artifact - uses: actions/upload-artifact@v3 + - name: Push runtime image + if: ${{ github.event_name == 'push' && github.ref_type == 'tag' }} + uses: docker/build-push-action@v5 with: - name: ioc-schema - path: ibek.ioc.schema.json + context: . + platforms: ${{ matrix.platform }} + target: runtime + build-args: | + IMAGE_EXT=${{ matrix.extension }} + tags: ${{ env.TAG }}-runtime:${{ github.ref_name }} + push: true release: # Release on tag push - publish ioc schema - needs: [build-push-images] + needs: [build] if: ${{ github.event_name == 'push' && github.ref_type == 'tag' }} runs-on: ubuntu-latest + # this job runs in the native developer container we just made + container: + image: ghcr.io/${{ github.repository_owner }}/${{ github.event.repository.name }}-developer:${{ github.ref_name }} + steps: - - uses: actions/download-artifact@v3 - with: - name: ioc-schema - path: ./ + - name: generate-schema + run: | + ibek ioc generate-schema --output ibek.ioc.schema.json - name: Github Release uses: softprops/action-gh-release@de2c0eb89ae2a093876385947365aca7b0e5f844 # v0.1.15 diff --git a/.gitignore b/.gitignore index c0a2532..1cac73b 100644 --- a/.gitignore +++ b/.gitignore @@ -8,10 +8,15 @@ repos* # while working on ibek with this project somethimes include it as subfolder ibek -# The ioc source tree is created here from a template inside the generic ioc -# repo. -# Remove from .gitignore if you want to customize the template. -/ioc/ - # dont save workspaces as other users will have differing folders *workspace + +# config folder is there to be replaced there is just a dummy with Readme. +ioc/config + +# podman may leave this around in aborted builds +.build.swp + +# this gets updated during RTEMS builds in devcontainer but we dont want to commit it +# TODO this entry does not work - why on earth not? +ioc/configure/CONFIG_SITE.Common.linux-x86_64 diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..5abf340 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,49 @@ +// Generic IOC debugging launcher +// To use this l +// 1. make sure you have gdb installed in your container with +// apt update; apt-get install gdb +// 2. run the IOC once to make sure rutime assets are generated: +// cd /epics/ioc; make +// ./start.sh +// 3. stop the IOC with 'exit' +// 4. In vscode go to the support source in /epics/support/xxx +// 5. Set any breakpoints in the source code that you require. +// 6. You may want to set 'HOST_OPT=NO' in CONFIG_SITE and rebuild the support +// 6. Go to the debug tab and select 'IOC devcontainer debug' from the +// RUN AND DEBUG dropdown +{ + "version": "0.2.0", + "configurations": [ + { + "name": "IOC devcontainer debug", + "type": "cppdbg", + "request": "launch", + "program": "/epics/ioc/bin/linux-x86_64/ioc", + "args": [ + "/epics/runtime/st.cmd" + ], + "stopAtEntry": false, + "cwd": "/epics/ioc", + "environment": [ + { + "name": "RUNTIME_DIR", + "value": "/epics/runtime" + } + ], + "externalConsole": false, + "MIMode": "gdb", + "setupCommands": [ + { + "description": "Enable pretty-printing for gdb", + "text": "-enable-pretty-printing", + "ignoreFailures": true + }, + { + "description": "Set Disassembly Flavor to Intel", + "text": "-gdb-set disassembly-flavor intel", + "ignoreFailures": true + } + ] + } + ] +} \ No newline at end of file diff --git a/Dockerfile b/Dockerfile index 24f8e61..ff69e81 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,20 +1,30 @@ -##### build stage ############################################################## +ARG IMAGE_EXT -ARG TARGET_ARCHITECTURE -ARG BASE=7.0.7ec3 +ARG BASE=7.0.8ec2 ARG REGISTRY=ghcr.io/epics-containers +ARG RUNTIME=${REGISTRY}/epics-base${IMAGE_EXT}-runtime:${BASE} +ARG DEVELOPER=${REGISTRY}/epics-base${IMAGE_EXT}-developer:${BASE} + +##### build stage ############################################################## +FROM ${DEVELOPER} AS developer -FROM ${REGISTRY}/epics-base-${TARGET_ARCHITECTURE}-developer:${BASE} AS developer +# The devcontainer mounts the project root to /epics/generic-source +# Using the same location here makes devcontainer/runtime differences transparent. +ENV SOURCE_FOLDER=/epics/generic-source +# connect ioc source folder to its know location +RUN ln -s ${SOURCE_FOLDER}/ioc ${IOC} -# The devcontainer mounts the project root to /epics/ioc-adsimdetector. Using -# the same location here makes devcontainer/runtime differences transparent. -WORKDIR /epics/ioc-motorsim/ibek-support +# Get the current version of ibek +COPY requirements.txt requirements.txt +RUN pip install --upgrade -r requirements.txt + +WORKDIR ${SOURCE_FOLDER}/ibek-support # copy the global ibek files COPY ibek-support/_global/ _global COPY ibek-support/iocStats/ iocStats -RUN iocStats/install.sh 3.1.16 +RUN iocStats/install.sh 3.2.0 COPY ibek-support/asyn/ asyn/ RUN asyn/install.sh R4-42 @@ -36,27 +46,27 @@ RUN motor/install.sh R7-3-1 COPY ibek-support/motorMotorSim/ motorMotorSim/ RUN motorMotorSim/install.sh R1-2 +# get the ioc source and build it +COPY ioc ${SOURCE_FOLDER}/ioc +RUN cd ${IOC} && ./install.sh && make -# create IOC source tree, generate Makefile and compile IOC Instance -RUN ibek ioc build +# install runtime proxy for non-native builds +RUN bash ${IOC}/install_proxy.sh ##### runtime preparation stage ################################################ - FROM developer AS runtime_prep # get the products from the build stage and reduce to runtime assets only RUN ibek ioc extract-runtime-assets /assets ##### runtime stage ############################################################ - -FROM ${REGISTRY}/epics-base-${TARGET_ARCHITECTURE}-runtime:${BASE} AS runtime +FROM ${RUNTIME} AS runtime # get runtime assets from the preparation stage COPY --from=runtime_prep /assets / # install runtime system dependencies, collected from install.sh scripts -RUN ibek support apt-install --runtime +RUN ibek support apt-install-runtime-packages --skip-non-native -ENV TARGET_ARCHITECTURE ${TARGET_ARCHITECTURE} +CMD "bash -c ${IOC}/start.sh" -ENTRYPOINT ["/bin/bash", "-c", "${IOC}/start.sh"] diff --git a/README.md b/README.md index 684b32f..1374294 100644 --- a/README.md +++ b/README.md @@ -1 +1,18 @@ -A container image for a generic IOC for Simulated Motors. +# Generic IOC Template Repository ioc-motorsim + +## Description +Generic IOC for simulation motors + +## Template Info +This repository was generated by +[ioc-template](https://github.com/epics-containers/ioc-template) + +To update to the latest version of the template: + +```bash +# activate a python virtual environment + +pip install copier +cd ioc-motorsim +copier update -a --trust . +``` \ No newline at end of file diff --git a/build b/build index ca9d319..0b94f16 100755 --- a/build +++ b/build @@ -1,21 +1,34 @@ #!/bin/bash ################################################################################ -# generic local build script for epics-containers ioc repositories # +# generic local build script for epics-containers repositories # ################################################################################ -# pass rtems as first argument to build RTEMS on cross-compiler -TARGET_ARCHITECTURE=${1:-linux} +set -e -set -xe +# set EPICS_TARGET_ARCH to rtems for RTEMS based targets +T_A=${EPICS_TARGET_ARCH:-linux_x86_64} +# set TARGET to runtime for runtime images +TARGET=${TARGET:-developer} +# set TAG to override the default tag +TAG=${TAG:-ec_test} + +if [[ ${T_A} != "linux_x86_64" ]]; then + # container image extension is the lcase of EPICS_TARGET_ARCH + IMAGE_EXT=-"${T_A,,}" +fi cd $(dirname ${0}) -# make sure new repos get their submodule ibek-support -git submodule update --init +# use docker if available else use podman +if ! docker version &>/dev/null; then docker=podman; else docker=docker; fi +if $docker buildx version &>/dev/null; then builx=buildx; load=--load; fi -# build runtime and developer images -ec dev build --arch ${TARGET_ARCHITECTURE} +# make sure new repos get their submodule ibek-support +if [ ! -d ibek-support/_global ] ; then git submodule update --init ; fi -# get the schema file from the developer container and save it locally -ec dev launch-local --execute 'ibek ioc generate-schema' > ibek.ioc.schema.json +# build and developer images +set -x +$docker build $buildx -t ${TAG} "${@}" $load \ + --build-arg IMAGE_EXT=$IMAGE_EXT \ + $runtime --target $TARGET . diff --git a/ibek-support b/ibek-support index 3c47a37..cc8df48 160000 --- a/ibek-support +++ b/ibek-support @@ -1 +1 @@ -Subproject commit 3c47a37327b089476b5cc7e6400b9acb039b5ccd +Subproject commit cc8df489c224e6628696b6aac7fa46760e864b93 diff --git a/ioc/.gitignore b/ioc/.gitignore new file mode 100644 index 0000000..436dc1c --- /dev/null +++ b/ioc/.gitignore @@ -0,0 +1,11 @@ +*~ +O.* +bin +dbd +db +data +lib +.svn* +iocs/*IOC* +.idea +.iocsh_history diff --git a/ioc/Makefile b/ioc/Makefile new file mode 100644 index 0000000..2c82d61 --- /dev/null +++ b/ioc/Makefile @@ -0,0 +1,7 @@ +TOP = . +include $(TOP)/configure/CONFIG + +DIRS += configure +DIRS += iocApp/src + +include $(TOP)/configure/RULES_TOP diff --git a/ioc/configure/CONFIG b/ioc/configure/CONFIG new file mode 100644 index 0000000..722d9ca --- /dev/null +++ b/ioc/configure/CONFIG @@ -0,0 +1,30 @@ +# CONFIG - Load build configuration data +# +# Do not make changes to this file! + +# Allow user to override where the build rules come from +RULES = $(EPICS_BASE) + +# RELEASE files point to other application tops +include $(TOP)/configure/RELEASE +-include $(TOP)/configure/RELEASE.$(EPICS_HOST_ARCH).Common +ifdef T_A +-include $(TOP)/configure/RELEASE.Common.$(T_A) +-include $(TOP)/configure/RELEASE.$(EPICS_HOST_ARCH).$(T_A) +endif + +CONFIG = $(RULES)/configure +include $(CONFIG)/CONFIG +-include $(CONFIG)/CONFIG.Dls + +# Override the Base definition: +INSTALL_LOCATION = $(TOP) + +# CONFIG_SITE files contain other build configuration settings +include $(TOP)/configure/CONFIG_SITE +-include $(TOP)/configure/CONFIG_SITE.$(EPICS_HOST_ARCH).Common +ifdef T_A + -include $(TOP)/configure/CONFIG_SITE.Common.$(T_A) + -include $(TOP)/configure/CONFIG_SITE.$(EPICS_HOST_ARCH).$(T_A) +endif + diff --git a/ioc/configure/CONFIG_SITE b/ioc/configure/CONFIG_SITE new file mode 100644 index 0000000..64da6a6 --- /dev/null +++ b/ioc/configure/CONFIG_SITE @@ -0,0 +1,4 @@ +# CONFIG_SITE + +CHECK_RELEASE = NO + diff --git a/ioc/configure/Makefile b/ioc/configure/Makefile new file mode 100644 index 0000000..9254309 --- /dev/null +++ b/ioc/configure/Makefile @@ -0,0 +1,8 @@ +TOP=.. + +include $(TOP)/configure/CONFIG + +TARGETS = $(CONFIG_TARGETS) +CONFIGS += $(subst ../,,$(wildcard $(CONFIG_INSTALLS))) + +include $(TOP)/configure/RULES diff --git a/ioc/configure/RELEASE b/ioc/configure/RELEASE new file mode 100644 index 0000000..57c536e --- /dev/null +++ b/ioc/configure/RELEASE @@ -0,0 +1,5 @@ +# The following definitions must be changed for each site +# +# Common prefixes +SUPPORT=/epics/support +include $(SUPPORT)/configure/RELEASE diff --git a/ioc/configure/RULES b/ioc/configure/RULES new file mode 100644 index 0000000..3425740 --- /dev/null +++ b/ioc/configure/RULES @@ -0,0 +1,7 @@ +# RULES + +-include $(CONFIG)/RULES.Dls +include $(CONFIG)/RULES + +# Library should be rebuilt because LIBOBJS may have changed. +$(LIBNAME): ../Makefile diff --git a/ioc/configure/RULES.ioc b/ioc/configure/RULES.ioc new file mode 100644 index 0000000..901987c --- /dev/null +++ b/ioc/configure/RULES.ioc @@ -0,0 +1,2 @@ +#RULES.ioc +include $(CONFIG)/RULES.ioc diff --git a/ioc/configure/RULES_DIRS b/ioc/configure/RULES_DIRS new file mode 100644 index 0000000..3ba269d --- /dev/null +++ b/ioc/configure/RULES_DIRS @@ -0,0 +1,2 @@ +#RULES_DIRS +include $(CONFIG)/RULES_DIRS diff --git a/ioc/configure/RULES_TOP b/ioc/configure/RULES_TOP new file mode 100644 index 0000000..d09d668 --- /dev/null +++ b/ioc/configure/RULES_TOP @@ -0,0 +1,3 @@ +#RULES_TOP +include $(CONFIG)/RULES_TOP + diff --git a/ioc/install.sh b/ioc/install.sh new file mode 100755 index 0000000..4f7dd9e --- /dev/null +++ b/ioc/install.sh @@ -0,0 +1,18 @@ +#!/bin/bash + +# global install script for all support modules to reference +# every install.sh script in ibek-support should call this script +# before it compiles its support module. + +THIS=$(realpath $(dirname $0)) + +x86_cfg="${THIS}/configure/CONFIG_SITE.Common.linux-x86_64" + +# for RTEMS builds don't build for the host architecture, target only +if [[ $EPICS_TARGET_ARCH == "RTEMS"* ]]; then + touch ${x86_cfg} + sed -i '/VALID_BUILDS/d' ${x86_cfg} + echo "VALID_BUILDS=Host" >> ${x86_cfg} +fi + + diff --git a/ioc/install_proxy.sh b/ioc/install_proxy.sh new file mode 100644 index 0000000..629f96d --- /dev/null +++ b/ioc/install_proxy.sh @@ -0,0 +1,23 @@ +#!/bin/bash + +# This script installs a python proxy for the IOC. The proxy is used to +# communicate with the IOC from the kubernetes pod where the IOC is non-native +# and cannot run in the cluster itself. + +# Only one proxy will be installed based on the EPICS_TARGET_ARCH +# the entry point for all proxies will be "epics-proxy start" and will +# be called by the default IOC start.sh script after it has prepared +# runtime assets. +# TODO: at present the rtems-proxy is the only supported proxy and uses +# rtems-proxy start as its entry point. +# TODO: consider renaming to ioc-proxy and genericizing. This would +# negate the need for this script. + +# Most IOCs are native linux-x86_64 and will run directly in the cluster +# so proxy installation is not required. + +if [[ $EPICS_TARGET_ARCH == "RTEMS-beatnik" ]] ; then + pip install rtems-proxy=="0.5.0" +fi + +# no other proxies supported at present diff --git a/ioc/iocApp/src/Makefile b/ioc/iocApp/src/Makefile new file mode 100644 index 0000000..53abd95 --- /dev/null +++ b/ioc/iocApp/src/Makefile @@ -0,0 +1,21 @@ +# Generic IOC Makefile + +TOP = ../.. +include $(TOP)/configure/CONFIG + +PROD_IOC = ioc +DBD += ioc.dbd +ioc_DBD += base.dbd + +# add in the dbds collected by ibek during container build +ioc_DBD += $(shell cat /epics/support/configure/dbd_list) + +ioc_SRCS += ioc_registerRecordDeviceDriver.cpp + +# add in the libs collected by ibek during container build (in reverse order) +ioc_LIBS += $(shell tac /epics/support/configure/lib_list) + +ioc_LIBS += $(EPICS_BASE_IOC_LIBS) +ioc_SRCS += iocMain.cpp + +include $(TOP)/configure/RULES diff --git a/ioc/iocApp/src/iocMain.cpp b/ioc/iocApp/src/iocMain.cpp new file mode 100644 index 0000000..e45dda1 --- /dev/null +++ b/ioc/iocApp/src/iocMain.cpp @@ -0,0 +1,20 @@ +/* This file was automatically generated on Fri 25 May 2018 08:06:10 BST from + * source: /home/hgv27681/R3.14.12.3/support/pmac/etc/makeIocs/lab.xml + * + * *** Please do not edit this file: edit the source file instead. *** + * */ +#include "epicsExit.h" +#include "epicsThread.h" +#include "iocsh.h" + +int main(int argc, char *argv[]) +{ + if(argc>=2) { + iocsh(argv[1]); + epicsThreadSleep(.2); + } + iocsh(NULL); + epicsExit(0); + return 0; +} + diff --git a/ioc/liveness.sh b/ioc/liveness.sh new file mode 100755 index 0000000..b0f5c0c --- /dev/null +++ b/ioc/liveness.sh @@ -0,0 +1,40 @@ +#!/bin/bash +TOP=/epics/ioc +cd ${TOP} +CONFIG_DIR=${TOP}/config + +set -ex + +CONFIG_DIR=/epics/ioc/config +THIS_SCRIPT=$(realpath ${0}) +override=${CONFIG_DIR}/liveness.sh + +if [[ -f ${override} && ${override} != ${THIS_SCRIPT} ]]; then + exec bash ${override} +fi + +if [[ ${K8S_IOC_LIVENESS_ENABLED} != 'true' ]]; then + exit 0 +fi + +# use devIOCStats UPTIME as the default liveness PV +# but allow override from the environment +K8S_IOC_PV=${K8S_IOC_PV:-"${IOC_PREFIX}:UPTIME"} + +# use default CA PORT or override from the environment +K8S_IOC_PORT=${K8S_IOC_PORT:-5064} + +export EPICS_CA_ADDR_LIST=${K8S_IOC_ADDRESS} +export EPICS_CA_SERVER_PORT=${K8S_IOC_PORT} + +# verify that the IOC is running +if caget ${K8S_IOC_PV} ; then + exit 0 +else + # send the error message to the container's main process stdout + echo "Liveness check failed for ${IOC_NAME}" > /proc/1/fd/1 + echo "Failing PV: ${K8S_IOC_PV}" > /proc/1/fd/2 + echo "Address list: ${EPICS_CA_ADDR_LIST}" > /proc/1/fd/2 + echo "CA Port: ${EPICS_CA_SERVER_PORT}" > /proc/1/fd/2 + exit 1 +fi diff --git a/ioc/start.sh b/ioc/start.sh new file mode 100755 index 0000000..ac912da --- /dev/null +++ b/ioc/start.sh @@ -0,0 +1,179 @@ +#!/bin/bash + +description=' + + The epics-containers IOC Startup Script + ======================================= + + This script is used to start an EPICS IOC in a Kubernetes pod. Implementers + of generic IOCs are free to replace this script with their own. But + this script as is should work for most IOCs. + + When a generic IOC runs in a kubernetes pod it is expected to have + a config folder that defines the IOC instance. + The helm chart for the generic IOC will mount the config folder + as a configMap and this turns a generic IOC into a specific IOC instance. + + Here we support the following set of options for the contents of + the config folder: + + 1. start.sh ****************************************************************** + If the config folder contains a start.sh script it will be executed. + This allows the instance implementer to provide a completely custom + startup script. Any other files that the script needs can also be placed + in the config folder. + + The presence of this file overrides all other options. + + WARNING: config maps are restricted to 1MB total. + + 2. ioc.yaml ************************************************************* + If the config folder contains a yaml file we invoke the ibek tool to + generate the startup script and database. Then launch with the generated + startup script. The file name should always be 'ioc.yaml'. The ioc instance + can determine its own name with the following as the first line in 'ioc.yaml' + + ioc_name: ""{{ __utils__.get_env('IOC_NAME') }}"" + + at the top of the file and in turn "{{ ioc_name }}"" can be used in any + of the fields within the file. For example: by default Kubernetes will be + looking at the iocStats PV IOC_NAME:Uptime to validate health of the IOC, + therefore most IOC instances should include: + + entities: + - type: epics.EpicsEnvSet + name: EPICS_TZ + value: "GMT0BST" + + - type: devIocStats.iocAdminSoft + IOC: "{{ ioc_name | upper }}" + + 3. st.cmd + ioc.subst ********************************************************* + If the config folder contains a st.cmd script and a ioc.subst file then + optionally generate ioc.db from the ioc.subst file and use the st.cmd script + as the IOC startup script. Note that the expanded database file will + be generated in ${RUNTIME_DIR}/ioc.db + + 4. empty config folder ******************************************************* + If the config folder is empty this message will be displayed. + + RTEMS IOCS - RTEMS IOC startup files can be generated using any of the above. + + For RTEMS we do not execute the ioc inside of the pod. Instead we: + - copy the IOC directory to the RTEMS mount point + - send a reboot command to the RTEMS crate + - start a telnet session to the RTEMS IOC console and connect that to + stdio of the pod. + +' + +# error reporting ************************************************************* + +function ibek_error { + echo "${1}" + + # Wait for a bit so the container does not exit and restart continually + sleep 120 +} + +# environment setup ************************************************************ + +# log commands and stop on errors +set -xe + +cd ${IOC} +CONFIG_DIR=${IOC}/config + +# add module paths to environment for use in ioc startup script +if [[ -f ${SUPPORT}/configure/RELEASE.shell ]]; then + source ${SUPPORT}/configure/RELEASE.shell +fi + +# override startup script +override=${CONFIG_DIR}/start.sh +# source YAML for IOC Builder for EPICS on Kubernetes (ibek) +ibek_yamls=(${CONFIG_DIR}/*.yaml) +# Startup script for EPICS IOC generated by ibek +ioc_startup=${CONFIG_DIR}/st.cmd + +# folder for runtime assets +export RUNTIME_DIR=${EPICS_ROOT}/runtime +mkdir -p ${RUNTIME_DIR} + +# expanded database file +epics_db=${RUNTIME_DIR}/ioc.db + +# in case there are multiple YAML, pick the first one in the glob +ibek_src=${ibek_yamls[0]} + +if [ -d ${CONFIG_DIR} ]; then + echo "checking config folder ${CONFIG_DIR}" + ls -al ${CONFIG_DIR} +else + echo "ERROR: No config folder found." + ibek_error "${description}" +fi + +# 1. start.sh override script ************************************************** +if [ -f ${override} ]; then + exec bash ${override} +# 2. ioc.yaml ****************************************************************** +elif [ -f ${ibek_src} ]; then + + if [[ ${#ibek_yams[@]} > 1 ]]; then + ibek_error "ERROR: Multiple YAML files found in ${CONFIG_DIR}." + fi + + # Database generation script generated by ibek + db_src=${RUNTIME_DIR}/ioc.subst + final_ioc_startup=${RUNTIME_DIR}/st.cmd + + # get the ibek support yaml files this ioc's support modules + defs=/epics/ibek-defs/*.ibek.support.yaml + ibek runtime generate ${ibek_src} ${defs} + + # build expanded database using msi + if [ -f ${db_src} ]; then + includes=$(for i in ${SUPPORT}/*/db; do echo -n "-I $i "; done) + bash -c "msi -o${epics_db} ${includes} -I${RUNTIME_DIR} -S${db_src}" + fi + +# 3. st.cmd + ioc.subst ************************************************ +elif [ -f ${ioc_startup} ] ; then + + if [ -f ${CONFIG_DIR}/ioc.subst ]; then + # generate ioc.db from ioc.subst, including all templates from SUPPORT + includes=$(for i in ${SUPPORT}/*/db; do echo -n "-I $i "; done) + msi ${includes} -I${RUNTIME_DIR} -S ${CONFIG_DIR}/ioc.subst -o ${epics_db} + fi + final_ioc_startup=${ioc_startup} +# 4. incorrect config folder *************************************************** +else + ibek_error " + ${description} + + ERROR: No IOC Instance Startup Assets found in ${CONFIG_DIR} + Please add ioc.yaml to the config folder (or see above for other options). + " +fi + +# Launch the IOC *************************************************************** + +if [[ ${EPICS_TARGET_ARCH} == "linux-x86_64" ]] ; then + # Execute the IOC binary and pass the startup script as an argument + exec ${IOC}/bin/linux-x86_64/ioc ${final_ioc_startup} +else + # for not native architectures use the appropriate python package + if [[ -f ${CONFIG_DIR}/proxy-start.sh ]]; then + # instances can provide proxy-start.sh to override default behavior + bash ${CONFIG_DIR}/proxy-start.sh + else + # the RTEMS container provides a python package to: + # - copy binaries to the IOC's shared folder + # - remotely configure the boot parameters + # - remotely launch the IOC + rtems-proxy start + fi +fi + + diff --git a/ioc/stop.sh b/ioc/stop.sh new file mode 100644 index 0000000..f734409 --- /dev/null +++ b/ioc/stop.sh @@ -0,0 +1,19 @@ +#!/bin/bash + +TOP=/epics/ioc +cd ${TOP} +CONFIG_DIR=${TOP}/config + +override=${CONFIG_DIR}/stop.sh + +if [[ -f ${override} ]]; then + exec bash ${override} +elif [[ ${RTEMS_VME_AUTO_REBOOT} == 'true' ]] ; then + # This is a placeholder for a script that is called when the pod is stopped. + # Placing your own stop.sh in the config directory will override this script. + + # No action is required here for RTEMS because the start.sh script will + # monitor for SIG_TERM and reboot the IOC when it is received. +fi + + diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..936d9cb --- /dev/null +++ b/requirements.txt @@ -0,0 +1,3 @@ +ibek==2.0.1 +# to install direct from github during development in a branch +# git+https://github.com/epics-containers/ibek.git@fix-extract-assets diff --git a/requirements_ec.txt b/requirements_ec.txt deleted file mode 100644 index ac3632c..0000000 --- a/requirements_ec.txt +++ /dev/null @@ -1,3 +0,0 @@ -epics-containers-cli==2.6.4 -# to install direct from github during development in the dev branch: -#git+https://github.com/epics-containers/epics-containers-cli.git@dev diff --git a/tests/config/bl00i-ea-test-ioc.yaml b/tests/config/bl00i-ea-test-ioc.yaml new file mode 100644 index 0000000..50c768d --- /dev/null +++ b/tests/config/bl00i-ea-test-ioc.yaml @@ -0,0 +1,29 @@ +# yaml-language-server: $schema=https://github.com/epics-containers/ioc-template/releases/download/2024.1.1/ibek.ioc.schema.json + +ioc_name: "{{ ioc_yaml_file_name }}" + +description: Very generic instance for testing generic IOCs + +entities: + - type: epics.EpicsEnvSet + name: EPICS_TZ + value: "GMT0BST" + + - type: devIocStats.iocAdminSoft + IOC: "{{ ioc_name | upper }}" + + - type: epics.StartupCommand + command: dbLoadRecords("/epics/ioc/config/ioc.db") + + - type: epics.dbpf + pv: EXAMPLE:IBEK:A + value: "2.54" + + - type: epics.dbpf + pv: EXAMPLE:IBEK:B + value: "2.61" + + - type: epics.PostStartupCommand + command: | + dbgf EXAMPLE:IBEK:SUM + dbgf BL00I-EA-TEST-IOC:ST_SCRIPT1 diff --git a/tests/example-ibek-config/ioc.db b/tests/config/ioc.db similarity index 100% rename from tests/example-ibek-config/ioc.db rename to tests/config/ioc.db diff --git a/tests/example-config/calc.db b/tests/example-config/calc.db deleted file mode 100644 index ae23846..0000000 --- a/tests/example-config/calc.db +++ /dev/null @@ -1,26 +0,0 @@ -record(calc, "EXAMPLE2:SUM") { - field(DESC, "Sum A and B") - field(CALC, "A+B") - field(INPA, "EXAMPLE2:A") - field(INPB, "EXAMPLE2:B") -} - -record(ao, "EXAMPLE2:A") { - field(DESC, "A voltage") - field(PREC, "3") - field(EGU, "Volts") - field(DRVL, "-10") - field(DRVH, "+10") - field(VAL, "0.000") - field(FLNK, "EXAMPLE2:SUM") -} - -record(ao, "EXAMPLE2:B") { - field(DESC, "B voltage") - field(PREC, "3") - field(EGU, "Volts") - field(DRVL, "-10") - field(DRVH, "+10") - field(VAL, "0.000") - field(FLNK, "EXAMPLE2:SUM") -} \ No newline at end of file diff --git a/tests/example-config/ioc.substitutions b/tests/example-config/ioc.substitutions deleted file mode 100644 index 4fa1884..0000000 --- a/tests/example-config/ioc.substitutions +++ /dev/null @@ -1,16 +0,0 @@ - -# Macros: -# IOC Device prefix -file $(IOCSTATS)/db/iocAdminSoft.db -{ -pattern { IOC } - { "test-ioc" } -} - -# Macros: -# IOC Device prefix -file $(IOCSTATS)/db/iocAdminScanMon.db -{ -pattern { IOC } - { "test-ioc" } -} \ No newline at end of file diff --git a/tests/example-config/st.cmd b/tests/example-config/st.cmd deleted file mode 100644 index 61720df..0000000 --- a/tests/example-config/st.cmd +++ /dev/null @@ -1,12 +0,0 @@ -# Example hand coded IOC startup script for example-config -cd "/epics/ioc" - -epicsEnvSet "EPICS_CA_MAX_ARRAY_BYTES", '6000000' - -dbLoadDatabase "dbd/ioc.dbd" -ioc_registerRecordDeviceDriver(pdbbase) - -dbLoadRecords("config/calc.db") -dbLoadRecords("/tmp/ioc.db") - -iocInit() diff --git a/tests/example-ibek-config/ioc.yaml b/tests/example-ibek-config/ioc.yaml deleted file mode 100644 index 5740795..0000000 --- a/tests/example-ibek-config/ioc.yaml +++ /dev/null @@ -1,9 +0,0 @@ -ioc_name: test-ibek-ioc -description: a basic example for testing ioc-template - -entities: - - type: devIocStats.iocAdminSoft - IOC: test-ibek-ioc - - - type: epics.StartupCommand - command: dbLoadRecords("/epics/ioc/config/ioc.db") diff --git a/tests/run-tests.sh b/tests/run-tests.sh index f56fde5..3aad907 100755 --- a/tests/run-tests.sh +++ b/tests/run-tests.sh @@ -5,36 +5,36 @@ # all present and correct and that mounting IOC config or ibek config # works as expected. -THIS_DIR=$(realpath $(dirname $0)) -ROOT=$(realpath ${THIS_DIR}/..) +TAG=${1} # pass a tag on the command line to test a prebuilt image +THIS=$(realpath $(dirname $0)) +ROOT=$(realpath ${THIS}/..) +CONF=/epics/ioc/config +# log commands and stop on errorsr set -ex +# use docker if available else use podman +if docker version &>/dev/null; then docker=docker; else docker=podman; fi + cd ${ROOT} -# Build the container (inherit arguments from CI workflow if set) ############## -ec dev build ${EC_TAG} - -# try out an ibek config IOC instance with the generic IOC ##################### -ec dev launch-local tests/example-config --args '-dit' ${EC_TAG} -ec dev wait-pv EXAMPLE2:A --attempts 20 -ec dev exec 'caput EXAMPLE2:A 1.3' -ec dev exec 'caput EXAMPLE2:B 1.2' -ec dev exec 'caget EXAMPLE2:SUM' | grep '2.5' - -# Test an ibek IOC ############################################################# -ec dev launch-local tests/example-ibek-config --args '-dit' ${EC_TAG} -ec dev wait-pv EXAMPLE:IBEK:A --attempts 20 -ec dev exec 'caput EXAMPLE:IBEK:A 1.3' -ec dev exec 'caput EXAMPLE:IBEK:B 1.2' -ec dev exec 'caget EXAMPLE:IBEK:SUM' | grep '2.5' -ec dev exec 'caget test-ibek-ioc:EPICS_VERS' | grep 'R7.0.7' - -# Stop the test IOC ############################################################ -ec dev stop - -# Done ######################################################################### -echo -echo "All tests passed!" + +# if a tag was passed in this implies it was already built +export TAG=${TAG:-ec_test} +if [[ ${TAG} == "ec_test" ]] ; then TARGET=runtime ./build; fi + +# try out a test ibek config IOC instance with the generic IOC +result=$($docker run --rm -v ${THIS}/config:${CONF} ${TAG} /epics/ioc/start.sh 2>&1) + +# check that the IOC output expected results +if echo "${result}" | grep -i error; then + echo "ERROR: errors in IOC startup" + exit 1 +elif [[ ! ${result} =~ "5.15" || ! ${result} =~ "/epics/runtime/st.cmd" ]]; then + echo "ERROR: dbgf output not as expected" + exit 1 +fi + +echo "Tests passed!"