refactor: move ioc logic from ansible to the gitea runner

The ioc install/start/restart logic moves out of the ansible playbooks and
the 'agebd ioc ...' cli into scripts run by the gitea runner:

- ioc-install.sh / ioc-restart.sh run on the runner itself
- ioc-start.sh triggers the shellbox commands over ssh
- ioc-for-services.sh runs an action for one or all services

They read the deployed ioc files from /sls instead of cloning the repo on
the target host, so a service has to be deployed before its ioc can be
installed. The deploy script therefore also takes over the environment
suffix renaming of the subs/parameters files that ansible used to do, and
the workflow steps are ordered deploy -> install.

The commented-out ioc placeholders in the add-new-service workflow are
filled in, and a manually triggered 'Ioc' workflow replaces the removed
cli commands.

With this, nothing uses ansible anymore, so the ansible dir, the cli ioc
commands and the ansible dependencies are removed.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PJTevZiSsHepW2xnJGQY49
This commit is contained in:
Benjamin Labrecque
2026-08-25 10:58:39 +02:00
co-authored by Claude Opus 5
parent 720e260b3e
commit 430f99baa1
33 changed files with 258 additions and 706 deletions
+38
View File
@@ -0,0 +1,38 @@
#!/bin/bash
# Shared helpers for the scripts run by the gitea runner.
# Source it as: source "$(dirname "$0")/common.sh"
# Overridable so the scripts can be exercised outside of the runner
HLA_ROOT=${HLA_ROOT:-/sls/bd/hla}
# Sets the naming and path variables used by the deploy and ioc scripts.
# Usage: init_service_env <service-name> <dev|prod>
init_service_env() {
local SERVICE_NAME=$1
AGEBD_ENV=$2
if [ -z "${SERVICE_NAME}" ] || [ -z "${AGEBD_ENV}" ]; then
echo "Usage: $0 <service-name> <dev|prod>" >&2
exit 1
fi
if [ "${AGEBD_ENV}" = "dev" ]; then
AGEBD_ENV_SUFFIX_UPPER="-DEV"
AGEBD_ENV_SUFFIX_LOWER="-dev"
elif [ "${AGEBD_ENV}" = "prod" ]; then
AGEBD_ENV_SUFFIX_UPPER=""
AGEBD_ENV_SUFFIX_LOWER=""
else
echo "Unknown environment '${AGEBD_ENV}', expected 'dev' or 'prod'" >&2
exit 1
fi
SERVICE_NAME_LOWER=$(echo "${SERVICE_NAME}" | tr '[:upper:]' '[:lower:]')
SERVICE_NAME_UPPER=$(echo "${SERVICE_NAME}" | tr '[:lower:]' '[:upper:]')
SERVICE_DIR=${HLA_ROOT}/${AGEBD_ENV}/services/${SERVICE_NAME_LOWER}/current
IOC_DIR=${SERVICE_DIR}/ioc
IOC_NAME=AGEBD-CPCL-${SERVICE_NAME_UPPER}${AGEBD_ENV_SUFFIX_UPPER}
# Host running shellbox, i.e. the host the ioc itself runs on
IOC_HOST=sls-vserv-bd-01${AGEBD_ENV_SUFFIX_LOWER}
}
+19 -15
View File
@@ -1,28 +1,19 @@
#!/bin/bash
# Deploys a service to /sls and (re)starts its systemd unit.
#
# Usage: ./.gitea/scripts/deploy-and-restart-service.sh <service-name> <dev|prod>
SERVICE_NAME=$1
AGEBD_ENV=$2
source "$(dirname "$0")/common.sh"
init_service_env "${1:-}" "${2:-}"
echo "=========================================="
echo "Deploying service: $SERVICE_NAME (${AGEBD_ENV})"
echo "Deploying service: $SERVICE_NAME_LOWER (${AGEBD_ENV})"
echo "=========================================="
SERVICE_NAME_LOWER=$(echo "$SERVICE_NAME" | tr '[:upper:]' '[:lower:]')
SERVICE_NAME_UPPER=$(echo "$SERVICE_NAME" | tr '[:lower:]' '[:upper:]')
SERVICE_DIR=/sls/bd/hla/${AGEBD_ENV}/services/${SERVICE_NAME_LOWER}/current
SYSTEMD_UNIT_NAME=AGEBD-SERVICE-${SERVICE_NAME_UPPER}.service
SYSTEMD_UNIT_FILE_PATH=${SERVICE_DIR}/systemd/${SYSTEMD_UNIT_NAME}
IOC_DIR=${SERVICE_DIR}/ioc
IOC_PORT=$(uv run --directory scripts ci/get_ioc_port.py ${SERVICE_NAME_LOWER})
if [ "${AGEBD_ENV}" = "dev" ]; then
AGEBD_ENV_SUFFIX_UPPER="-DEV"
AGEBD_ENV_SUFFIX_LOWER="-dev"
else
AGEBD_ENV_SUFFIX_UPPER=""
AGEBD_ENV_SUFFIX_LOWER=""
fi
# TODO: see https://gitea.psi.ch/sls/hla_framework_bd/issues/50
# rm -rf "${SERVICE_DIR}"
# mkdir -p ${SERVICE_DIR}
@@ -43,6 +34,19 @@ sed -i "s/{{ *agebd_env_suffix_upper* }}/${AGEBD_ENV_SUFFIX_UPPER}/g" "${IOC_DIR
sed -i "s/{{ *agebd_env_suffix_lower* }}/${AGEBD_ENV_SUFFIX_LOWER}/g" "${IOC_DIR}/"*
sed -i "s/{{ *ioc_port* }}/${IOC_PORT}/g" "${IOC_DIR}/"*
### RENAMING
### The ioc files carry the environment suffix in their name, e.g. in dev
### AGEBD-CPCL-NTURNS_main.subs -> AGEBD-CPCL-NTURNS-DEV_main.subs
### (rsync --delete restores the original names on every deploy, so this runs every time)
if [ -n "${AGEBD_ENV_SUFFIX_UPPER}" ]; then
for FILE_SUFFIX in "_main.subs" "_parameters.yaml"; do
ORIG_FILE=${IOC_DIR}/AGEBD-CPCL-${SERVICE_NAME_UPPER}${FILE_SUFFIX}
if [ -f "${ORIG_FILE}" ]; then
mv "${ORIG_FILE}" "${IOC_DIR}/AGEBD-CPCL-${SERVICE_NAME_UPPER}${AGEBD_ENV_SUFFIX_UPPER}${FILE_SUFFIX}"
fi
done
fi
### SYSTEMD
if [ -f "${SYSTEMD_UNIT_FILE_PATH}" ]; then
sed -i "s/{{ *agebd_env* }}/${AGEBD_ENV}/g" "${SYSTEMD_UNIT_FILE_PATH}"
+27
View File
@@ -0,0 +1,27 @@
#!/bin/bash
# Runs an ioc action for a single service or for all services.
#
# Usage: ./.gitea/scripts/ioc-for-services.sh <install|start|restart> <service-name|all> <dev|prod>
set -euo pipefail
IOC_ACTION=${1:-}
SERVICE=${2:-}
AGEBD_ENV=${3:-}
SCRIPT_DIR=$(dirname "$0")
IOC_SCRIPT=${SCRIPT_DIR}/ioc-${IOC_ACTION}.sh
if [ ! -f "${IOC_SCRIPT}" ]; then
echo "Unknown ioc action '${IOC_ACTION}', expected 'install', 'start' or 'restart'" >&2
exit 1
fi
if [ "${SERVICE}" = "all" ]; then
SERVICES=$(ls -d services/*/ | xargs -n1 basename)
else
SERVICES=${SERVICE}
fi
for SERVICE_NAME in ${SERVICES}; do
"${IOC_SCRIPT}" "${SERVICE_NAME}" "${AGEBD_ENV}"
done
+37
View File
@@ -0,0 +1,37 @@
#!/bin/bash
# Installs the ioc of a service from its deployed ioc dir on /sls.
# Requires the service to have been deployed first (deploy-and-restart-service.sh),
# which is what puts the substituted and environment-renamed ioc files in place.
#
# Usage: ./.gitea/scripts/ioc-install.sh <service-name> <dev|prod>
set -euo pipefail
source "$(dirname "$0")/common.sh"
init_service_env "${1:-}" "${2:-}"
echo "=========================================="
echo "Installing ioc: ${IOC_NAME} (${AGEBD_ENV})"
echo "=========================================="
if [ ! -d "${IOC_DIR}" ]; then
echo "Ioc dir '${IOC_DIR}' not found, deploy the service first" >&2
exit 1
fi
STDERR_FILE=$(mktemp)
trap 'rm -f "${STDERR_FILE}"' EXIT
set +e
(cd "${IOC_DIR}" && ioc install -V --facility sls --ioc "${IOC_NAME}" --clean) 2>"${STDERR_FILE}"
INSTALL_RC=$?
set -e
cat "${STDERR_FILE}" >&2
# 'ioc install ...' returns exit code 0 even on failure, so its stderr is
# inspected for error keywords to force a pipeline failure.
# Note: -a because some responses of the 'ioc ...' cli are non-utf8.
if [ ${INSTALL_RC} -ne 0 ] || grep -qa -e "\[error\]" -e "Unable to write" "${STDERR_FILE}"; then
echo "Failed to install ioc ${IOC_NAME}" >&2
exit 1
fi
+15
View File
@@ -0,0 +1,15 @@
#!/bin/bash
# Restarts the ioc of a service.
#
# Usage: ./.gitea/scripts/ioc-restart.sh <service-name> <dev|prod>
set -euo pipefail
source "$(dirname "$0")/common.sh"
init_service_env "${1:-}" "${2:-}"
echo "=========================================="
echo "Restarting ioc: ${IOC_NAME} (${AGEBD_ENV})"
echo "=========================================="
cd "${IOC_DIR}"
ioc restart "${IOC_NAME}"
+23
View File
@@ -0,0 +1,23 @@
#!/bin/bash
# Starts the ioc of a service through shellbox on the ioc host.
# Needed once after the initial install of a new ioc.
#
# Usage: ./.gitea/scripts/ioc-start.sh <service-name> <dev|prod>
set -euo pipefail
source "$(dirname "$0")/common.sh"
init_service_env "${1:-}" "${2:-}"
IOC_PORT=$(uv run --directory scripts ci/get_ioc_port.py "${SERVICE_NAME_LOWER}")
echo "=========================================="
echo "Starting ioc: ${IOC_NAME} on ${IOC_HOST}:${IOC_PORT}"
echo "=========================================="
ssh -o BatchMode=yes "${IOC_HOST}" bash -s <<REMOTE
set -e
sudo shellbox status
sudo shellbox reload
sudo shellbox status
sudo shellbox start ${IOC_PORT}
REMOTE
+19 -7
View File
@@ -9,7 +9,7 @@ env:
UV_CACHE_DIR: /var/cache/uv
UV_LINK_MODE: hardlink
# TODO: when to deploy to prod? Manual (i.e. cli command)?
# TODO: when to deploy to prod? Manual (i.e. the 'Ioc' workflow)?
jobs:
deploy:
runs-on: hla-dev
@@ -31,19 +31,31 @@ jobs:
SERVICE_NAME="${BRANCH#feature/add-service-}"
echo "service_name_lower=$SERVICE_NAME" >> $GITHUB_OUTPUT
# - name: Install Master IOC
# - name: Restart Master IOC
# The ioc steps read the deployed ioc files on /sls, so the service
# has to be deployed before its ioc is installed.
- name: Deploy and Restart Master Service
run: |
./.gitea/scripts/deploy-and-restart-service.sh "master" "dev"
# - name: Install New IOC
- name: Install Master IOC
run: |
./.gitea/scripts/ioc-install.sh "master" "dev"
# - name: Start New IOC
- name: Restart Master IOC
run: |
./.gitea/scripts/ioc-restart.sh "master" "dev"
- name: Deploy and Restart New Service
run: |
SERVICE_NAME=${{ steps.parse_branch.outputs.service_name_lower }}
./.gitea/scripts/deploy-and-restart-service.sh "${SERVICE_NAME}" "dev"
- name: Install New IOC
run: |
SERVICE_NAME=${{ steps.parse_branch.outputs.service_name_lower }}
./.gitea/scripts/ioc-install.sh "${SERVICE_NAME}" "dev"
- name: Start New IOC
run: |
SERVICE_NAME=${{ steps.parse_branch.outputs.service_name_lower }}
./.gitea/scripts/ioc-start.sh "${SERVICE_NAME}" "dev"
+56
View File
@@ -0,0 +1,56 @@
name: Ioc
# Manually install / start / restart iocs, i.e. what the 'agebd ioc ...' cli
# commands used to do. The ioc files are read from the deployed dir on /sls,
# so the service has to be deployed (see the 'Deploy' workflow) beforehand.
on:
workflow_dispatch:
inputs:
action:
description: 'What to do with the ioc(s)'
type: choice
required: true
default: 'install'
options:
- install
- restart
- start
service:
description: "Service name in lower case, or 'all' for every service"
type: string
required: true
agebd_env:
description: 'Environment'
type: choice
required: true
default: 'dev'
options:
- dev
- prod
env:
UV_CACHE_DIR: /var/cache/uv
UV_LINK_MODE: hardlink
jobs:
ioc-dev:
if: ${{ inputs.agebd_env == 'dev' }}
runs-on: hla-dev
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Run ioc ${{ inputs.action }}
run: |
./.gitea/scripts/ioc-for-services.sh "${{ inputs.action }}" "${{ inputs.service }}" "dev"
ioc-prod:
if: ${{ inputs.agebd_env == 'prod' }}
runs-on: hla-prod
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Run ioc ${{ inputs.action }}
run: |
./.gitea/scripts/ioc-for-services.sh "${{ inputs.action }}" "${{ inputs.service }}" "prod"
-8
View File
@@ -1,8 +0,0 @@
[defaults]
remote_tmp = /tmp/ansible-$USER
# some responses of 'ioc ...' cli are non-utf8
module_strict_utf8_response = False
# ./ refers to dir in which this file is located
collections_path = ./installed-collections
-3
View File
@@ -1,3 +0,0 @@
collections:
- name: ansible.posix
version: 1.6.0
-19
View File
@@ -1,19 +0,0 @@
all:
vars:
agebd_env: "dev"
env_suffix_upper: "-DEV"
env_suffix_lower: "-dev"
children:
lc:
hosts:
sls-lc:
ansible_remote_tmp: /tmp/.ansible_${ansible_user}_tmp
vserv:
hosts:
sls-vserv-bd-01-dev:
ansible_ssh_pipelining: true
ansible_ssh_common_args: '-o ForwardAgent=yes'
vserv-hla:
hosts:
sls-vserv-bd-hla01-dev:
-18
View File
@@ -1,18 +0,0 @@
all:
vars:
agebd_env: "prod"
env_suffix_upper: ""
env_suffix_lower: ""
children:
lc:
hosts:
sls-lca:
ansible_remote_tmp: /tmp/.ansible_${ansible_user}_tmp
vserv:
hosts:
sls-vserv-bd-01:
ansible_ssh_pipelining: true
vserv-hla:
hosts:
sls-vserv-bd-hla01:
-42
View File
@@ -1,42 +0,0 @@
---
- name: Add a new service
hosts: vserv-hla
connection: local
gather_facts: true # Gather once here, available to all included tasks!
vars:
repo_root: "{{ playbook_dir }}/../.."
tasks:
# - name: Install Master IOC
# vars:
# preferred_service_name_lower: master
# branch_name: "feature/add-service-{{ service_name_lower }}"
# import_tasks: ../tasks/ioc/ioc-install.yml
# - name: Restart Master IOC
# vars:
# preferred_service_name_lower: master
# branch_name: "feature/add-service-{{ service_name_lower }}"
# import_tasks: ../tasks/ioc/ioc-restart.yml
- name: Deploy and Restart Master Service
vars:
preferred_service_name_lower: master
branch_name: "feature/add-service-{{ service_name_lower }}"
import_tasks: ../tasks/service/service-deploy-and-restart.yml
# - name: Install New IOC
# vars:
# branch_name: "feature/add-service-{{ service_name_lower }}"
# import_tasks: ../tasks/ioc/ioc-install.yml
# - name: Start New IOC
# vars:
# branch_name: "feature/add-service-{{ service_name_lower }}"
# import_tasks: ../tasks/ioc/ioc-start.yml
- name: Deploy and Restart New Service
vars:
branch_name: "feature/add-service-{{ service_name_lower }}"
import_tasks: ../tasks/service/service-deploy-and-restart.yml
@@ -1,24 +0,0 @@
---
- name: Deploy and Restart Modified Services
hosts: vserv-hla
connection: local
vars:
# Passed from the Gitea workflow, e.g., "master playground"
changed_services_raw: ""
agebd_env: "dev"
repo_root: "{{ playbook_dir }}/../.."
tasks:
# Fail early if no services were modified
- name: Check if there are services to deploy
ansible.builtin.meta: end_play
when: changed_services_raw | trim == ""
# Main Loop: loop through the list of modified service names
- name: Deploy and Restart Modified Services
include_tasks: ../tasks/service/service-deploy-and-restart.yml
loop: "{{ changed_services_raw.split() }}"
loop_control:
loop_var: service_name_lower
extended: yes
-11
View File
@@ -1,11 +0,0 @@
---
- name: Install an IOC
hosts: lc
gather_facts: false
vars:
workspace_dir: "/tmp/hla_framework_bd"
tasks:
- name: Run ioc install tasks
ansible.builtin.import_tasks: ../tasks/ioc/ioc-install.yml
-11
View File
@@ -1,11 +0,0 @@
---
- name: Restart an IOC
hosts: lc
gather_facts: false
vars:
workspace_dir: "/tmp/hla_framework_bd"
tasks:
- name: Run ioc restart tasks
ansible.builtin.include_tasks: ../tasks/ioc/ioc-restart.yml
-14
View File
@@ -1,14 +0,0 @@
# TODO: ansible seems to be very slow on this machine
# each step takes quite a while (e.g. cloning repo, which
# is significantly faster on other machines).
---
- name: Start an IOC
hosts: vserv
gather_facts: false
vars:
workspace_dir: "/tmp/hla_framework_bd"
tasks:
- name: Run ioc start tasks
ansible.builtin.import_tasks: ../tasks/ioc/ioc-start.yml
-63
View File
@@ -1,63 +0,0 @@
---
- name: Init service name
ansible.builtin.import_tasks: ../utils/init-service-name.yml
- name: Clone repo
ansible.builtin.import_tasks: ../utils/clone-repo.yml
- name: Get and set ioc_port
ansible.builtin.import_tasks: ../utils/get-ioc-port.yml
- name: "[{{ target_service_name_lower }}] Establish environment naming conventions"
ansible.builtin.set_fact:
service_name_upper: "{{ target_service_name_lower | upper }}"
- name: "[{{ target_service_name_lower }}] Define file paths"
ansible.builtin.set_fact:
ioc_base_dir: "{{ workspace_dir }}/services/{{ target_service_name_lower }}/current/ioc"
orig_subs_name: "AGEBD-CPCL-{{ service_name_upper }}_main.subs"
target_subs_name: "AGEBD-CPCL-{{ service_name_upper }}{{ env_suffix_upper }}_main.subs"
orig_params_name: "AGEBD-CPCL-{{ service_name_upper }}_parameters.yaml"
target_params_name: "AGEBD-CPCL-{{ service_name_upper }}{{ env_suffix_upper }}_parameters.yaml"
# Rename the subs file if we are in DEV
- name: "[{{ target_service_name_lower }}] Rename substitution file for environment"
ansible.builtin.command:
cmd: "mv {{ ioc_base_dir }}/{{ orig_subs_name }} {{ ioc_base_dir }}/{{ target_subs_name }}"
removes: "{{ ioc_base_dir }}/{{ orig_subs_name }}" # Only runs if the original file exists
# Rename the paramters file if we are in DEV
- name: "[{{ target_service_name_lower }}] Rename parameters file for environment"
ansible.builtin.command:
cmd: "mv {{ ioc_base_dir }}/{{ orig_params_name }} {{ ioc_base_dir }}/{{ target_params_name }}"
removes: "{{ ioc_base_dir }}/{{ orig_params_name }}" # Only runs if the original file exists
# Replace {{ agebd_env_suffix_upper }} inside the newly renamed subs file
- name: "[{{ target_service_name_lower }}] Substitute environment tokens inside subs file"
ansible.builtin.replace:
path: "{{ ioc_base_dir }}/{{ target_subs_name }}"
regexp: '\{\{\s*agebd_env_suffix_upper\s*\}\}'
replace: "{{ env_suffix_upper }}"
# Replace {{ agebd_env_suffix_lower }} and {{ ioc_port }} inside the newly renamed params file
- name: "[{{ target_service_name_lower }}] Substitute environment token inside params file"
ansible.builtin.replace:
path: "{{ ioc_base_dir }}/{{ target_params_name }}"
regexp: "{{ item.regexp }}"
replace: "{{ item.replace | string }}"
with_items:
- { regexp: '\{\{\s*agebd_env_suffix_lower\s*\}\}', replace: "{{ env_suffix_lower }}" }
- { regexp: '\{\{\s*ioc_port\s*\}\}', replace: "{{ ioc_port }}" }
- name: "[{{ target_service_name_lower }}] Run ioc install"
ansible.builtin.shell:
cmd: "ioc install -V --facility sls --ioc AGEBD-CPCL-{{ service_name_upper }}{{ env_suffix_upper }} --clean"
chdir: "{{ ioc_base_dir }}"
changed_when: true
register: ioc_install_result
# Force a pipeline failure if the command output contains error keywords
# (ioc install ... returns exit code 0 even on failure...)
failed_when: >
ioc_install_result.rc != 0 or
"[error]" in ioc_install_result.stderr or
"Unable to write" in ioc_install_result.stderr
-20
View File
@@ -1,20 +0,0 @@
---
- name: Init service name
ansible.builtin.import_tasks: ../utils/init-service-name.yml
- name: Clone repo
ansible.builtin.import_tasks: ../utils/clone-repo.yml
- name: "[{{ target_service_name_lower }}] Establish environment naming conventions"
ansible.builtin.set_fact:
service_name_upper: "{{ target_service_name_lower | upper }}"
- name: "[{{ target_service_name_lower }}] Define file paths"
ansible.builtin.set_fact:
ioc_base_dir: "{{ workspace_dir }}/services/{{ target_service_name_lower }}/current/ioc"
- name: "[{{ target_service_name_lower }}] Restart IOC"
ansible.builtin.shell:
cmd: "ioc restart AGEBD-CPCL-{{ service_name_upper }}{{ env_suffix_upper }}"
chdir: "{{ ioc_base_dir }}"
changed_when: true
-17
View File
@@ -1,17 +0,0 @@
---
- name: Init service name
ansible.builtin.import_tasks: ../utils/init-service-name.yml
- name: Clone repo
ansible.builtin.import_tasks: ../utils/clone-repo.yml
- name: Get and set ioc_port
ansible.builtin.import_tasks: ../utils/get-ioc-port.yml
- name: "[{{ ioc_port }}] Start IOC"
ansible.builtin.shell: |
sudo shellbox status
sudo shellbox reload
sudo shellbox status
sudo shellbox start {{ ioc_port }}
changed_when: true
@@ -1,53 +0,0 @@
---
- name: Initialize service name
ansible.builtin.set_fact:
# Uses preferred_service_name_lower if defined in vars, otherwise falls back to the CLI extra-vars
target_service_name_lower: "{{ preferred_service_name_lower | default(service_name_lower) }}"
- name: "[{{ target_service_name_lower }}] Define service specific paths and names"
ansible.builtin.set_fact:
svc_current_dir: "/sls/bd/hla/{{ agebd_env }}/services/{{ target_service_name_lower }}/current"
service_unit_name: "AGEBD-SERVICE-{{ target_service_name_lower | upper }}.service"
- name: "[{{ target_service_name_lower }}] Ensure target directory structure exists"
ansible.builtin.file:
path: "{{ svc_current_dir }}"
state: directory
# ansible.builtin.shell: |
# mkdir -p "{{ svc_current_dir }}"
# TODO: make sure this is what we want (essentially wipes existing dest dir). Does it remove .venv?
- name: "[{{ target_service_name_lower }}] Sync service deployment files"
ansible.posix.synchronize:
src: "{{ repo_root }}/services/{{ target_service_name_lower }}/current/"
dest: "{{ svc_current_dir }}/"
delete: yes # This removes stale files on the target that no longer exist locally
recursive: yes
- name: "[{{ target_service_name_lower }}] Substitute environment variable inline"
ansible.builtin.replace:
path: "{{ svc_current_dir }}/systemd/{{ service_unit_name }}"
regexp: '\{\{\s*agebd_env\s*\}\}'
replace: "{{ agebd_env }}"
# systemctl --user link --force "./{{ service_unit_name }}"
- name: "[{{ target_service_name_lower }}] Symlink systemd unit file (Absolute Path)"
ansible.builtin.file:
src: "{{ svc_current_dir }}/systemd/{{ service_unit_name }}"
dest: "~/.config/systemd/user/{{ service_unit_name }}"
state: link
force: yes # This overwrites the broken/existing symlink cleanly without error
# export XDG_RUNTIME_DIR=/run/user/$(id -u)
# systemctl --user daemon-reload
# systemctl --user enable "{{ service_unit_name }}"
# systemctl --user restart "{{ service_unit_name }}"
- name: "[{{ target_service_name_lower }}] Enable and Restart Systemd Service"
ansible.builtin.systemd_service:
name: "{{ service_unit_name }}"
scope: user
daemon_reload: yes
enabled: yes
state: restarted
environment:
XDG_RUNTIME_DIR: "/run/user/{{ ansible_user_uid }}"
-8
View File
@@ -1,8 +0,0 @@
---
- name: Clone this repository
ansible.builtin.git:
repo: "git@gitea.psi.ch:sls/hla_framework_bd.git"
dest: "{{ workspace_dir }}"
accept_hostkey: true
version: "{{ branch_name }}"
force: true
-18
View File
@@ -1,18 +0,0 @@
---
- name: Read the service registry file
ansible.builtin.slurp:
src: "{{ workspace_dir }}/config/services_registry.yml"
register: remote_registry_file
- name: Parse the service registry YAML content into a fact
ansible.builtin.set_fact:
services_registry: "{{ remote_registry_file.content | b64decode | from_yaml }}"
- name: Extract the port from the services registry and save it as a persistent fact
ansible.builtin.set_fact:
ioc_port: "{{ services_registry.services | selectattr('name', 'equalto', target_service_name_lower) | map(attribute='ioc_port') | first | default(none) }}"
- name: Fail if the service or port wasn't found in the registry
ansible.builtin.fail:
msg: "Service '{{ target_service_name_lower }}' not found in services registry or port not set"
when: ioc_port | trim == "" or ioc_port is none
@@ -1,5 +0,0 @@
---
- name: Initialize service name
ansible.builtin.set_fact:
# Uses preferred_service_name_lower if defined in vars, otherwise falls back to the CLI extra-vars
target_service_name_lower: "{{ preferred_service_name_lower | default(service_name_lower) }}"
-2
View File
@@ -9,8 +9,6 @@ authors = [
dependencies = [
"agebd",
"ansible>=8.7.0",
"ansible-runner>=2.4.3",
"copier>=9.10.3",
"pydantic>=2.13.4",
"pydantic-settings>=2.14.2",
-40
View File
@@ -1,40 +0,0 @@
import os
import sys
import ansible_runner
import typer
from agebd.utils import get_git_root
from core.enums import IOC_ENV
ioc = typer.Typer(no_args_is_help=True)
REPO_ROOT = get_git_root(__file__)
def run_playbook(playbook: str, env: IOC_ENV, vars: list[str]):
env_str = str(env)
if env_str not in ["dev", "prod"]:
raise ValueError("env must be 'dev' or 'prod'")
current_env = os.environ.copy()
current_env["ANSIBLE_CONFIG"] = f"{REPO_ROOT}/ansible/ansible.cfg"
extra_vars = [f"--extra-vars={var}" for var in vars]
out, err, rc = ansible_runner.run_command(
executable_cmd=f"ansible-playbook",
cmdline_args=[
playbook,
"-i",
f"{REPO_ROOT}/ansible/inventories/{env_str}/hosts.yml",
*extra_vars,
"-v",
],
envvars=current_env,
input_fd=sys.stdin,
output_fd=sys.stdout,
error_fd=sys.stderr,
)
print("rc: {}".format(rc))
print("out: {}".format(out))
print("err: {}".format(err))
-8
View File
@@ -6,14 +6,6 @@ class ServiceStatus(str, Enum):
DISABLED = "disabled"
class IOC_ENV(str, Enum):
PROD = "prod"
DEV = "dev"
def __str__(self) -> str:
return self.value
class GuiOption(str, Enum):
NEW = "new"
EXISTING = "existing"
-112
View File
@@ -1,112 +0,0 @@
import typer
from agebd.utils import get_git_root
from config.paths import Paths
from core.ansible import run_playbook
from core.enums import IOC_ENV
from models import ServiceRegistry
ioc = typer.Typer(no_args_is_help=True)
REPO_ROOT = get_git_root(__file__)
ANSIBLE_PLAYBOOKS_DIR = REPO_ROOT / "ansible" / "playbooks"
IOC_PLAYBOOK_INSTALL = ANSIBLE_PLAYBOOKS_DIR / "ioc-install.yml"
IOC_PLAYBOOK_RESTART = ANSIBLE_PLAYBOOKS_DIR / "ioc-restart.yml"
IOC_PLAYBOOK_START = ANSIBLE_PLAYBOOKS_DIR / "ioc-start.yml"
SERVICE_NAME_HELP = "Service name in lower case"
def _resolve_branch_name(branch_name: str | None, service_name_lower: str) -> str:
return branch_name or f"feature/add-service-{service_name_lower}"
def _get_registry_services():
paths = Paths()
registry = ServiceRegistry.read_from_file(paths.service_registry_file)
return registry.get_services()
@ioc.command(no_args_is_help=True)
def install(
env: IOC_ENV = typer.Option(..., "--env"),
service_name_lower: str = typer.Option(..., "--service-name", "-s", help=SERVICE_NAME_HELP),
branch_name: str | None = typer.Option(
None, "--branch-name", "-b", help="Default is 'feature/add-service-{service-name}'"
),
):
branch_name = _resolve_branch_name(branch_name, service_name_lower)
run_playbook(
playbook=str(IOC_PLAYBOOK_INSTALL),
env=env,
vars=[f"branch_name={branch_name}", f"service_name_lower={service_name_lower}"],
)
@ioc.command(no_args_is_help=True)
def install_all(
env: IOC_ENV = typer.Option(..., "--env"),
):
for service in _get_registry_services():
run_playbook(
playbook=str(IOC_PLAYBOOK_INSTALL),
env=env,
vars=["branch_name=main", f"service_name_lower={service.name_lower}"],
)
@ioc.command(no_args_is_help=True)
def start(
env: IOC_ENV = typer.Option(..., "--env"),
service_name_lower: str = typer.Option(..., "--service-name", "-s", help=SERVICE_NAME_HELP),
branch_name: str | None = typer.Option(
None, "--branch-name", "-b", help="Default is 'feature/add-service-{service-name}'"
),
):
branch_name = _resolve_branch_name(branch_name, service_name_lower)
run_playbook(
playbook=str(IOC_PLAYBOOK_START),
env=env,
vars=[f"service_name_lower={service_name_lower}", f"branch_name={branch_name}"],
)
@ioc.command()
def start_all(
env: IOC_ENV = typer.Option(..., "--env"),
):
for service in _get_registry_services():
run_playbook(
playbook=str(IOC_PLAYBOOK_START),
env=env,
vars=[f"service_name_lower={service.name_lower}"],
)
@ioc.command(no_args_is_help=True)
def restart(
env: IOC_ENV = typer.Option(..., "--env"),
service_name_lower: str = typer.Option(..., "--service-name", "-s", help=SERVICE_NAME_HELP),
branch_name: str | None = typer.Option(
None, "--branch-name", "-b", help="Default is 'feature/add-service-{service-name}'"
),
):
branch_name = _resolve_branch_name(branch_name, service_name_lower)
run_playbook(
playbook=str(IOC_PLAYBOOK_RESTART),
env=env,
vars=[f"branch_name={branch_name}", f"service_name_lower={service_name_lower}"],
)
@ioc.command(no_args_is_help=True)
def restart_all(
env: IOC_ENV = typer.Option(..., "--env"),
):
for service in _get_registry_services():
run_playbook(
playbook=str(IOC_PLAYBOOK_RESTART),
env=env,
vars=["branch_name=main", f"service_name_lower={service.name_lower}"],
)
-2
View File
@@ -2,7 +2,6 @@ import logging
import typer
import ioc
import service
logger = logging.getLogger(__name__)
@@ -10,7 +9,6 @@ logger = logging.getLogger(__name__)
cli = typer.Typer(no_args_is_help=True, add_completion=False)
cli.add_typer(service.service, name="service")
cli.add_typer(ioc.ioc, name="ioc")
if __name__ == "__main__":
Generated
-147
View File
@@ -37,8 +37,6 @@ version = "0.1.0"
source = { editable = "." }
dependencies = [
{ name = "agebd" },
{ name = "ansible" },
{ name = "ansible-runner" },
{ name = "copier" },
{ name = "pydantic" },
{ name = "pydantic-settings" },
@@ -60,8 +58,6 @@ dev = [
[package.metadata]
requires-dist = [
{ name = "agebd", editable = "../packages/agebd" },
{ name = "ansible", specifier = ">=8.7.0" },
{ name = "ansible-runner", specifier = ">=2.4.3" },
{ name = "copier", specifier = ">=9.10.3" },
{ name = "pydantic", specifier = ">=2.13.4" },
{ name = "pydantic-settings", specifier = ">=2.14.2" },
@@ -98,49 +94,6 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/78/b6/6307fbef88d9b5ee7421e68d78a9f162e0da4900bc5f5793f6d3d0e34fb8/annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53", size = 13643, upload-time = "2024-05-20T21:33:24.1Z" },
]
[[package]]
name = "ansible"
version = "10.7.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "ansible-core" },
]
sdist = { url = "https://files.pythonhosted.org/packages/d4/64/29fdff6fe7682342adb54802c1cd90b2272d382e1743089af88f90a1d986/ansible-10.7.0.tar.gz", hash = "sha256:59d29e3de1080e740dfa974517d455217601b16d16880314d9be26145c68dc22", size = 41256974, upload-time = "2024-12-03T18:04:25.794Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/f3/95/cb8944902a2cdd94b1e19ff73695548679a388b9c473dc63c8dc64ffea3a/ansible-10.7.0-py3-none-any.whl", hash = "sha256:0089f08e047ceb70edd011be009f5c6273add613fbe491e9697c0556c989d8ea", size = 51576038, upload-time = "2024-12-03T18:04:20.065Z" },
]
[[package]]
name = "ansible-core"
version = "2.17.14"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "cryptography" },
{ name = "jinja2" },
{ name = "packaging" },
{ name = "pyyaml" },
{ name = "resolvelib" },
]
sdist = { url = "https://files.pythonhosted.org/packages/ff/80/2925a0564f6f99a8002c3be3885b83c3a1dc5f57ebf00163f528889865f5/ansible_core-2.17.14.tar.gz", hash = "sha256:7c17fee39f8c29d70e3282a7e9c10bd70d5cd4fd13ddffc5dcaa52adbd142ff8", size = 3119687, upload-time = "2025-09-08T18:28:03.158Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/86/29/d694562f1a875b50aa74f691521fe493704f79cf1938cd58f28f7e2327d2/ansible_core-2.17.14-py3-none-any.whl", hash = "sha256:34a49582a57c2f2af17ede2cefd3b3602a2d55d22089f3928570d52030cafa35", size = 2189656, upload-time = "2025-09-08T18:28:00.375Z" },
]
[[package]]
name = "ansible-runner"
version = "2.4.3"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "packaging" },
{ name = "pexpect" },
{ name = "python-daemon" },
{ name = "pyyaml" },
]
sdist = { url = "https://files.pythonhosted.org/packages/68/4f/62222b42b52cc771db51ccc77ffac19cc5f0196ff61de7c93585845a819b/ansible_runner-2.4.3.tar.gz", hash = "sha256:5f3025529bb968fdc3b627457dd8d418dbf9d53bc73735d50e69c28544d53031", size = 154148, upload-time = "2026-03-16T18:42:14.861Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/e3/89/fa7f81db6134caa455c9351373ee934b5fdeed212eb939d6c42d80d683d7/ansible_runner-2.4.3-py3-none-any.whl", hash = "sha256:cdac6daa151a50084ffda710e769db23fa975fc0507796191d7708831b286e37", size = 80257, upload-time = "2026-03-16T18:42:13.72Z" },
]
[[package]]
name = "asttokens"
version = "3.0.2"
@@ -150,29 +103,6 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/d4/2b/04b8a15f3a1c77bc79ddf5c73875327f34b4fa75982df2b76e45e402d364/asttokens-3.0.2-py3-none-any.whl", hash = "sha256:9da13157f5b28becde0bd374fc677dcd3c290614264eff096f167c469cd9f933", size = 28702, upload-time = "2026-07-12T03:31:47.542Z" },
]
[[package]]
name = "cffi"
version = "2.1.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "pycparser", marker = "implementation_name != 'PyPy'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/57/5f/ff100cae70ebe9d8df1c01a00e510e45d9adb5c1fdda84791b199141de97/cffi-2.1.0.tar.gz", hash = "sha256:efc1cdd798b1aaf39b4610bba7aad28c9bea9b910f25c784ccf9ec1fa719d1f9", size = 531036, upload-time = "2026-07-06T21:34:30.382Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/c0/e9/6d7724983b3d5a0908dbf74f64038ade77c18646ff6636ec7894fd392ce1/cffi-2.1.0-cp310-cp310-macosx_10_15_x86_64.whl", hash = "sha256:b65f590ef2a44640f9a05dbb548a429b4ade77913ce683ac8b1480777658a6c0", size = 183837, upload-time = "2026-07-06T21:32:09.655Z" },
{ url = "https://files.pythonhosted.org/packages/69/aa/24580a278de21fd7322635556334d9b535f1cbc00b0a3919447cdf464c65/cffi-2.1.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:164bff1657b2a74f0b6d54e11c9b375bc97b931f2ca9c43fcf875838da1570dd", size = 184226, upload-time = "2026-07-06T21:32:11.196Z" },
{ url = "https://files.pythonhosted.org/packages/88/a9/02cae418ec4beb282ace11958d9d4737793439d561fadc7e6d56f2e2b354/cffi-2.1.0-cp310-cp310-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:c941bb58d5a6e1c3892d86e42927ed6c180302f07e6d395d08c416e594b98b46", size = 211107, upload-time = "2026-07-06T21:32:12.328Z" },
{ url = "https://files.pythonhosted.org/packages/3b/30/c806937ed5e4c2c7ac30d9d6b76b5dc57ff8b75d83800d9bb11a8253cf2a/cffi-2.1.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:a016194dbe13d14ee9556e734b772d8d67b947092b268d757fd4290e3ba2dfc2", size = 218733, upload-time = "2026-07-06T21:32:13.67Z" },
{ url = "https://files.pythonhosted.org/packages/f9/cf/398272b8bbfd58aa314fda5a7f1cdbb26d1d78ae324a11211521315dd1f0/cffi-2.1.0-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:03e9810d18c646077e501f661b682fbf5dee4676048527ca3cffe66faa9960dd", size = 205543, upload-time = "2026-07-06T21:32:15.148Z" },
{ url = "https://files.pythonhosted.org/packages/45/ca/f91641185cdd90c36d317a9dc7f85e88ef8682d8b300977baff5e23c35d8/cffi-2.1.0-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:19c54ac121cad98450b4896fa9a43ee0180d57bc4bc911a33db6cab1efab6cd3", size = 205460, upload-time = "2026-07-06T21:32:16.479Z" },
{ url = "https://files.pythonhosted.org/packages/38/66/04781a77b411f0bb5b234d62c1814754ab75ebe455ccff1b08e8d7aae98f/cffi-2.1.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:4d433a51f1870e43a13b6732f92aaf540ff77c2015097c78556f75a2d6c030e0", size = 218760, upload-time = "2026-07-06T21:32:17.98Z" },
{ url = "https://files.pythonhosted.org/packages/d0/9a/bb1d5ed9c3fcae158e9f6391bf309c95d98c2ac37ed56573228471d0af5e/cffi-2.1.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:3d7f118b5adbfdfead90c25822690b02bc8074fba949bb7858bec4ebd55adb43", size = 221230, upload-time = "2026-07-06T21:32:19.407Z" },
{ url = "https://files.pythonhosted.org/packages/41/aa/3c1409cdd26094efacd1c36c66e0a6eb9d4296e4fd4f9901b8b2042f4323/cffi-2.1.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:c5f5df567f6eb216de69be06ce55c8b714090fae02b18a3b40da8163b8c5fa9c", size = 213524, upload-time = "2026-07-06T21:32:20.828Z" },
{ url = "https://files.pythonhosted.org/packages/fa/75/74dfb7c3fc6ebbd408038476bd4c1d7e925c62614e7b9c534ecc34218288/cffi-2.1.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:11b3fb55f4f8ad92274ed26705f65d8f91457de71f5380061eb6d125a768fecd", size = 220341, upload-time = "2026-07-06T21:32:21.9Z" },
{ url = "https://files.pythonhosted.org/packages/70/b6/9003c33a3e7d2c1306f5962e646457dcfe5a8cd8fce6bbe02d7af25db783/cffi-2.1.0-cp310-cp310-win32.whl", hash = "sha256:9d72af0cf10a76a600a9690078fe31c63b9588c8e86bf9fd353f713c84b5db0f", size = 174578, upload-time = "2026-07-06T21:32:23.073Z" },
{ url = "https://files.pythonhosted.org/packages/8a/26/710688310447531c7a22f857c7f79d9855ec18b03e04494ced723fb37e2f/cffi-2.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:fb62edb5bb52cca65fab91a63afa7561607120d26090a7e8fda6fb9f064726da", size = 185071, upload-time = "2026-07-06T21:32:24.671Z" },
]
[[package]]
name = "colorama"
version = "0.4.6"
@@ -207,44 +137,6 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/22/2c/68dec88ee26f96af2d4d8ee9d8567313a0826f4fd6bc5ae4527be670d354/copier-9.17.0-py3-none-any.whl", hash = "sha256:fe4e7b59faf4c0e1386eccbb79c6797e6df33ca20d5de49a8372571d8669f2ad", size = 65954, upload-time = "2026-07-13T14:33:03.301Z" },
]
[[package]]
name = "cryptography"
version = "49.0.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "cffi", marker = "platform_python_implementation != 'PyPy'" },
{ name = "typing-extensions" },
]
sdist = { url = "https://files.pythonhosted.org/packages/1f/99/d1c90d6041656cc6ee229dc99cd67fd0cd5aec3c5f7d72fffc27cc750054/cryptography-49.0.0.tar.gz", hash = "sha256:f89660a348f4f78a92366240a61404e337586ef7f5909a2fef59ca88ef505493", size = 854345, upload-time = "2026-06-12T20:02:30.512Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/9b/22/adf66990e63584a68dfb50c24f48a125c07b1699899381c8151e63ed458c/cryptography-49.0.0-cp311-abi3-macosx_11_0_arm64.whl", hash = "sha256:966fe0e9c67490071f14c0d2b1cb2dfb3023c5ce39457343931415f08382f2db", size = 4032100, upload-time = "2026-06-12T20:02:32.143Z" },
{ url = "https://files.pythonhosted.org/packages/09/41/3797cfaf69cae04a13ee78ebd83f0678d9c02b4779d21ce24445326f1a69/cryptography-49.0.0-cp311-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:36d1709f992593689b45bda411498d62c6e365f2ca00b84657d4dadd24de16db", size = 4692978, upload-time = "2026-06-12T20:01:21.305Z" },
{ url = "https://files.pythonhosted.org/packages/e6/8b/43011f7ebe515a8aa20d61f290a326cd890c2e738e16e59eaff8d9c3a412/cryptography-49.0.0-cp311-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:0e959b578856a3924bc0cbb710fc12c387b9412a951389f3ca61704a9e25f325", size = 4716422, upload-time = "2026-06-12T20:01:48.566Z" },
{ url = "https://files.pythonhosted.org/packages/4a/91/01ce7303a4579e6d3a6abef01bd322848e9ea7a219adcabc5048b9033571/cryptography-49.0.0-cp311-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:53ecee2e23f7169b6117e99fc8a944e5e50f79e69758a83b52a00cb98ab2b2d2", size = 4700503, upload-time = "2026-06-12T20:02:47.091Z" },
{ url = "https://files.pythonhosted.org/packages/62/99/a2c95cf8293f07491e9e27c20cc4dcd18176d944e674679adeb1d0173fd6/cryptography-49.0.0-cp311-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:2eda353d8a27bcbcaa4cbed18994a74ab4d19a2ca897db188ea269ab9b71419b", size = 5309779, upload-time = "2026-06-12T20:02:08.987Z" },
{ url = "https://files.pythonhosted.org/packages/20/2c/0622f20ff02b2ef32558733443805dc82fd4c275be01b2d19d14676f3a1b/cryptography-49.0.0-cp311-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:2afe9051da7ae7bd5905da5a949280c7d2bb75682e188f650a9d0f2756b834c6", size = 4749683, upload-time = "2026-06-12T20:02:03.335Z" },
{ url = "https://files.pythonhosted.org/packages/a3/5b/c5246635d5fd3b64e0d45ae10e99fd32fe9676a79915ccfe5a61ba9af1a5/cryptography-49.0.0-cp311-abi3-manylinux_2_31_armv7l.whl", hash = "sha256:0b82e28ee398a386f0807bba7884d30f25218855690f45115831bcce5d90822c", size = 4337874, upload-time = "2026-06-12T20:02:54.323Z" },
{ url = "https://files.pythonhosted.org/packages/6d/88/05563c7fe2e914e87d1a536d06fe83e66b4e1d95cb593e05aea375531da8/cryptography-49.0.0-cp311-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:ccac2bfebc306b862133e3bb71f3f6ee8bb525240089b2d952e4144b3a6d5da7", size = 4700283, upload-time = "2026-06-12T20:01:34.822Z" },
{ url = "https://files.pythonhosted.org/packages/c4/b6/d7696e4e890d6ae1469935164c9e5215c557671cb78d6e3f458ccceaa632/cryptography-49.0.0-cp311-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:d0527ce944105f257f605a827d6ebead966c752038b6e8656abb9c5edee6fc68", size = 5265844, upload-time = "2026-06-12T20:01:24.09Z" },
{ url = "https://files.pythonhosted.org/packages/a9/3c/f3ad17eecc1a57b0ba236dc01f90e783c51f4a2f35f64777cc4f47a184b2/cryptography-49.0.0-cp311-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:cbc77da8c523d5abd028635ba850a6966fcee2c82e2bf65a41d1d8afe0f98be9", size = 4749290, upload-time = "2026-06-12T20:01:30.848Z" },
{ url = "https://files.pythonhosted.org/packages/4f/01/339573cf1023163a400b0b5d16f6d507de413b9f60be6fd1b77feeaf6737/cryptography-49.0.0-cp311-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:b87e65d263b3e5d3bb92a57e2a6638e2f31110fa7aa890c7b2dbba42248d0a3f", size = 4834612, upload-time = "2026-06-12T20:01:29.246Z" },
{ url = "https://files.pythonhosted.org/packages/71/fd/577302e213a1be9468f92d1afef66fcf1ef83d516819d9992ca547f592bd/cryptography-49.0.0-cp311-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:66ec79c3904820572d7e987abdf304281f141d37ad9a489b8e97066e7b9b6459", size = 4980804, upload-time = "2026-06-12T20:01:42.853Z" },
{ url = "https://files.pythonhosted.org/packages/1f/09/f42b1d190c5ba75f72062a387f8030d1d75f6ab035788f1d9c4b01de6525/cryptography-49.0.0-cp311-abi3-win_amd64.whl", hash = "sha256:e5dfc1e64de5677cec922ffa8da89c546d0415bf6efdf081842e5d44c84e1f0e", size = 3810026, upload-time = "2026-06-12T20:02:39.262Z" },
{ url = "https://files.pythonhosted.org/packages/19/2a/5bb823f5bedcf80718cea7fbc95ec5515cca3769633c4b01a32be7f30e7c/cryptography-49.0.0-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:ec5e529fb80935c94fe7b729f9972b50e351a0e6b50aa294fd5cabb109fcc29a", size = 4025947, upload-time = "2026-06-12T20:01:25.745Z" },
{ url = "https://files.pythonhosted.org/packages/3d/df/40577043ca124e17012f408ddddaeb213b856336ac82ddb3bc915f39e29f/cryptography-49.0.0-cp39-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:f78ff2c9ed8dc2d036b0f4d640e22522213d047c1b14e61205a7e55c80a494d4", size = 4692429, upload-time = "2026-06-12T20:01:53.628Z" },
{ url = "https://files.pythonhosted.org/packages/2c/99/2d13299eb3dd27b02dcfaafcc91d6b5cb3329f7cbd6d8f51921acd566c1a/cryptography-49.0.0-cp39-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:35b151772baff2c74cba7fa290ceaff4c3b11c0c881eb93eb5dbc05a7cfbba18", size = 4700968, upload-time = "2026-06-12T20:02:45.383Z" },
{ url = "https://files.pythonhosted.org/packages/a5/4d/9c0cd02f95e2602dd5e563da149ee0830abef3537be8b34dc56281ebe27a/cryptography-49.0.0-cp39-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:0f21641cf4b30fca7aee061ced0ec7ad7b073518088b7c9969a297c0ae796c69", size = 4697758, upload-time = "2026-06-12T20:01:41.13Z" },
{ url = "https://files.pythonhosted.org/packages/24/01/186c825898477d77e2324d5360fefe622ff1d8d1963ec0554e2cada8ec77/cryptography-49.0.0-cp39-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:9e82dcc8e56052715fb18b2429e3bca4823b1629136a2084fc45a9a5cecb9b64", size = 5298863, upload-time = "2026-06-12T20:02:24.579Z" },
{ url = "https://files.pythonhosted.org/packages/b8/7b/62cbbab75d0659865bf0273790031544a0b16c8072d258f9428dcd8190dc/cryptography-49.0.0-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:6f2debedf9ca60cf1d5bd466475638af5130f89965605cd818484d19987d3a21", size = 4735983, upload-time = "2026-06-12T20:01:50.14Z" },
{ url = "https://files.pythonhosted.org/packages/6c/72/3e798c064bc39e471008075d0f9bc9daf77a80879c092e4a8e170c585ed4/cryptography-49.0.0-cp39-abi3-manylinux_2_31_armv7l.whl", hash = "sha256:8c25ceb16df5b9435f3f6a9829204985b0e0cbee3b48aacd432c7d2c850b44d9", size = 4334173, upload-time = "2026-06-12T20:01:44.743Z" },
{ url = "https://files.pythonhosted.org/packages/f0/ee/6fca21d1ac73e06f8bef71940abfd4d2f6472b4bca284d770f32bd4086f6/cryptography-49.0.0-cp39-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:28d8b15e6275f12c8a207dc309dfa957903c927d08d0cc937ee3f63f200693cc", size = 4697298, upload-time = "2026-06-12T20:02:20.918Z" },
{ url = "https://files.pythonhosted.org/packages/67/d0/a5fcd3515f0bae49a7b6d0413cc1bdccdcc1fc0047037a0d480642cdc5d6/cryptography-49.0.0-cp39-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:6fc361c34fb6aac015ce19435876635e5c6d21db31998b0920f675f131e043b8", size = 5254338, upload-time = "2026-06-12T20:02:22.737Z" },
{ url = "https://files.pythonhosted.org/packages/a0/84/84fe36f19caf857d61cb7fc9c63035a47ffabd84ea12d1d393148efa3615/cryptography-49.0.0-cp39-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:2400ef9c9e2299a25614eb1dea3db54a69b1349efd043bfac9c67630d136df36", size = 4735650, upload-time = "2026-06-12T20:02:41.389Z" },
{ url = "https://files.pythonhosted.org/packages/6c/a0/db537264e234f7273a73ec020873d6d6b39dfd8a53db78b550ca8320440e/cryptography-49.0.0-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:67e1d20ad9ef3a563c59ef22e7a8a0b8210bd26604369ea4a30a7c66aefe504e", size = 4834820, upload-time = "2026-06-12T20:01:51.847Z" },
{ url = "https://files.pythonhosted.org/packages/93/77/8df9eb486495979bccecd1062e2eaf435250e84437040295b57d09048b0b/cryptography-49.0.0-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:42b0684e0e40cf26122427802486f6d93aea593612603a94fbf260c7eb1e9c1b", size = 4967968, upload-time = "2026-06-12T20:02:12.524Z" },
{ url = "https://files.pythonhosted.org/packages/c2/e6/f60198ea8d9dfa15fff9ed4ca02ce362f6eadd9ba757dcc50634c4257b63/cryptography-49.0.0-cp39-abi3-win_amd64.whl", hash = "sha256:026ac7423e6fa66872d3bf889be5974507da3944f866f704fa200eadacd00001", size = 3785547, upload-time = "2026-06-12T20:02:26.847Z" },
]
[[package]]
name = "decorator"
version = "5.3.1"
@@ -406,15 +298,6 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/72/b9/313e8f2f2e9517ae050a692ae7b3e4b3f17cc5e6dfea0db51fe14e586580/jinja2_ansible_filters-1.3.2-py3-none-any.whl", hash = "sha256:e1082f5564917649c76fed239117820610516ec10f87735d0338688800a55b34", size = 18975, upload-time = "2022-06-30T14:08:49.571Z" },
]
[[package]]
name = "lockfile"
version = "0.12.2"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/17/47/72cb04a58a35ec495f96984dddb48232b551aafb95bde614605b754fe6f7/lockfile-0.12.2.tar.gz", hash = "sha256:6aed02de03cba24efabcd600b30540140634fc06cfa603822d508d5361e9f799", size = 20874, upload-time = "2015-11-25T18:29:58.279Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/c8/22/9460e311f340cb62d26a38c419b1381b8593b0bb6b5d1f056938b086d362/lockfile-0.12.2-py2.py3-none-any.whl", hash = "sha256:6c3cb24f344923d30b2785d5ad75182c8ea7ac1b6171b08657258ec7429d50fa", size = 13564, upload-time = "2015-11-25T18:29:51.462Z" },
]
[[package]]
name = "markdown-it-py"
version = "4.2.0"
@@ -597,15 +480,6 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/8e/37/efad0257dc6e593a18957422533ff0f87ede7c9c6ea010a2177d738fb82f/pure_eval-0.2.3-py3-none-any.whl", hash = "sha256:1db8e35b67b3d218d818ae653e27f06c3aa420901fa7b081ca98cbedc874e0d0", size = 11842, upload-time = "2024-07-21T12:58:20.04Z" },
]
[[package]]
name = "pycparser"
version = "3.0"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/1b/7d/92392ff7815c21062bea51aa7b87d45576f649f16458d78b7cf94b9ab2e6/pycparser-3.0.tar.gz", hash = "sha256:600f49d217304a5902ac3c37e1281c9fe94e4d0489de643a9504c5cdfdfc6b29", size = 103492, upload-time = "2026-01-21T14:26:51.89Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/0c/c3/44f3fbbfa403ea2a7c779186dc20772604442dde72947e7d01069cbe98e3/pycparser-3.0-py3-none-any.whl", hash = "sha256:b727414169a36b7d524c1c3e31839a521725078d7b2ff038656844266160a992", size = 48172, upload-time = "2026-01-21T14:26:50.693Z" },
]
[[package]]
name = "pydantic"
version = "2.13.4"
@@ -748,18 +622,6 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/5a/cc/06253936f4a7fa2e0f48dfe6d851d9c56df896a9ab09ac019d70b760619c/pytest_mock-3.15.1-py3-none-any.whl", hash = "sha256:0a25e2eb88fe5168d535041d09a4529a188176ae608a6d249ee65abc0949630d", size = 10095, upload-time = "2025-09-16T16:37:25.734Z" },
]
[[package]]
name = "python-daemon"
version = "3.1.2"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "lockfile" },
]
sdist = { url = "https://files.pythonhosted.org/packages/3d/37/4f10e37bdabc058a32989da2daf29e57dc59dbc5395497f3d36d5f5e2694/python_daemon-3.1.2.tar.gz", hash = "sha256:f7b04335adc473de877f5117e26d5f1142f4c9f7cd765408f0877757be5afbf4", size = 71576, upload-time = "2024-12-03T08:41:07.843Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/45/3c/b88167e2d6785c0e781ee5d498b07472aeb9b6765da3b19e7cc9e0813841/python_daemon-3.1.2-py3-none-any.whl", hash = "sha256:b906833cef63502994ad48e2eab213259ed9bb18d54fa8774dcba2ff7864cec6", size = 30872, upload-time = "2024-12-03T08:41:03.322Z" },
]
[[package]]
name = "python-dotenv"
version = "1.2.2"
@@ -798,15 +660,6 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/3c/26/1062c7ec1b053db9e499b4d2d5bc231743201b74051c973dadeac80a8f43/questionary-2.1.1-py3-none-any.whl", hash = "sha256:a51af13f345f1cdea62347589fbb6df3b290306ab8930713bfae4d475a7d4a59", size = 36753, upload-time = "2025-08-28T19:00:19.56Z" },
]
[[package]]
name = "resolvelib"
version = "1.0.1"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/ce/10/f699366ce577423cbc3df3280063099054c23df70856465080798c6ebad6/resolvelib-1.0.1.tar.gz", hash = "sha256:04ce76cbd63fded2078ce224785da6ecd42b9564b1390793f64ddecbe997b309", size = 21065, upload-time = "2023-03-09T05:10:38.292Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/d2/fc/e9ccf0521607bcd244aa0b3fbd574f71b65e9ce6a112c83af988bbbe2e23/resolvelib-1.0.1-py2.py3-none-any.whl", hash = "sha256:d2da45d1a8dfee81bdd591647783e340ef3bcb104b54c383f70d422ef5cc7dbf", size = 17194, upload-time = "2023-03-09T05:10:36.214Z" },
]
[[package]]
name = "rich"
version = "15.0.0"
-36
View File
@@ -1,36 +0,0 @@
# Ansible
## Running a playbook manually
Set vars on command line:
```
ansible-playbook ansible/playbooks/add-new-service.yml \
-i ansible/hosts.yml \
--extra-vars="repo_root=${{ github.workspace }} agebd_env=dev service_name_lower=<service dir name>" \
-v
```
### Install an IOC
```
ansible-playbook ansible/playbooks/ioc-install.yml \
-i ansible/hosts.yml \
--extra-vars="agebd_env=dev branch_name=feature/add-service-playground service_name_lower=playground ioc_port=50003" \
-v
```
### Start an IOC
```
ansible-playbook ansible/playbooks/ioc-start.yml \
-i ansible/hosts.yml \
--extra-vars="service_name_lower=playground ioc_port=50003" \
-v
```
### Restart an IOC
```
ansible-playbook ansible/playbooks/ioc-restart.yml \
-i ansible/hosts.yml \
--extra-vars="agebd_env=dev branch_name=feature/add-service-playground service_name_lower=playground" \
-v
```
+4 -2
View File
@@ -3,9 +3,11 @@
## Hosts
- `sls-vserv-bd-01(-dev)`
- Runs shellbox commands (and `ioc install ...`?)
- Runs the shellbox commands, which the runner triggers over `ssh`
(see `.gitea/scripts/ioc-start.sh`)
- `sls-vserv-bd-hla01(-dev)`
- Runs everything else
- Runs the runner itself, and therefore everything else: the deployments
to `/sls/bd/hla/<env>/` and the `ioc ...` commands
## Setup
+20 -1
View File
@@ -1,6 +1,8 @@
# IOC
The following instructions explain the basics of starting and managing IOCs on the IOC host.
The following instructions explain the basics of starting and managing IOCs on the IOC host.
They describe the manual steps. In practice the CI/CD runner does this for you, see
[Automation](#automation) below.
- TODO: some iocs not associated to a service, where to put?
- TODO: need to dev/prod template them too
@@ -92,3 +94,20 @@ sudo shellbox start 50045 # start ioc
sudo shellbox log 50045 -f # log file (tail)
exit # exit ssh
```
## Automation
Installing, starting and restarting IOCs is done by the gitea runner, not by hand:
- `.gitea/scripts/ioc-install.sh <service-name> <dev|prod>`
- `.gitea/scripts/ioc-start.sh <service-name> <dev|prod>` (shellbox, over `ssh`)
- `.gitea/scripts/ioc-restart.sh <service-name> <dev|prod>`
The scripts operate on the deployed IOC files in
`/sls/bd/hla/<env>/services/<service-name>/current/ioc`, so a service has to be deployed
before its IOC can be installed.
They run automatically when a new service is added (`.gitea/workflows/add-new-service.yml`),
and can be triggered manually for any service (or `all` services) through the `Ioc` workflow
(`.gitea/workflows/ioc.yml`) in the gitea Actions tab.