diff --git a/.github/workflows/PRBuild.yaml b/.github/workflows/PRBuild.yaml index 26b47bc..5889eab 100644 --- a/.github/workflows/PRBuild.yaml +++ b/.github/workflows/PRBuild.yaml @@ -1,9 +1,7 @@ -# Copyright © 2025 OpenCHAMI a Series of LF Projects, LLC -# +# Copyright © 2026 OpenCHAMI a Series of LF Projects, LLC # SPDX-License-Identifier: MIT name: Build each PR for testing and validation - on: pull_request: branches: @@ -17,91 +15,64 @@ on: type: string permissions: write-all # Necessary for the generate-build-provenance action with containers - jobs: - build: - - + config: runs-on: ubuntu-latest - + outputs: + rpm-unsigned: ${{ steps.names.outputs.rpm-unsigned }} + rpm-signed: ${{ steps.names.outputs.rpm-signed }} + keys-public: ${{ steps.names.outputs.keys-public }} steps: - - name: Set up latest stable Go - uses: actions/setup-go@v6.4.0 - with: - go-version: stable - - name: Set up QEMU - uses: docker/setup-qemu-action@v3 - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v3 - with: - driver-opts: | - image=moby/buildkit:master - network=host - - name: Docker Login - uses: docker/login-action@v3 - with: - registry: ghcr.io - username: ${{ github.actor }} - password: ${{ secrets.GITHUB_TOKEN }} - - name: Checkout - uses: actions/checkout@v6.0.2 - with: - fetch-tags: 1 - fetch-depth: 0 - # Set environment variables required by GoReleaser - - name: Set build environment variables + - id: names run: | - echo "GIT_STATE=$(if git diff-index --quiet HEAD --; then echo 'clean'; else echo 'dirty'; fi)" >> $GITHUB_ENV - echo "BUILD_HOST=$(hostname)" >> $GITHUB_ENV - echo "GO_VERSION=$(go version | awk '{print $3}')" >> $GITHUB_ENV - echo "BUILD_USER=$(whoami)" >> $GITHUB_ENV - echo "CGO_ENABLED=0" >> $GITHUB_ENV - echo "IS_PR_BUILD=true" >> $GITHUB_ENV + { + echo "rpm-unsigned=rpms-unsigned" + echo "rpm-signed=rpms-signed" + echo "keys-public=public-keys" + } >> "$GITHUB_OUTPUT" - - name: Docker Login - uses: docker/login-action@v3 - with: - registry: ghcr.io - username: ${{ github.actor }} - password: ${{ secrets.GITHUB_TOKEN }} + build: + uses: OpenCHAMI/github-actions/.github/workflows/build-publish-container-goreleaser.yml@v3.7 + secrets: inherit + with: + cgo_enabled: 0 + go_version_file: go.mod + registry_subject_name: ghcr.io/openchami/metadata-service + is_pr_build: true + pr_number: ${{ inputs.pr_number || github.event.pull_request.number || 0 }} - - name: Create Tag for PR - if: github.event_name == 'pull_request' || (github.event_name == 'workflow_dispatch' && inputs.pr_number != '') - run: | - git config --global user.name "github-actions[bot]" - git config --global user.email "github-actions[bot]@users.noreply.github.com" - PR_NUM="${{ github.event.number }}" - if [[ "${{ inputs.pr_number }}" != "" ]]; then - PR_NUM="${{ inputs.pr_number }}" - fi - git tag -f -a pr-${PR_NUM} -m "PR Release" + rpmbuild: + needs: [config, build] + uses: OpenCHAMI/github-actions/.github/workflows/build-rpm-quadlet.yml@v3.7 + secrets: inherit + with: + artifact-name-unsigned-rpms: ${{ needs.config.outputs.rpm-unsigned }} - - name: Build/Push container with goreleaser - uses: goreleaser/goreleaser-action@v6 - env: - GITHUB_TOKEN: ${{ github.token }} - with: - version: '~> 2' - args: release --clean --skip=announce,validate,archive - id: goreleaser - - name: Process goreleaser output - id: process_goreleaser_output - run: | - echo "const fs = require('fs');" > process.js - echo 'const artifacts = ${{ steps.goreleaser.outputs.artifacts }}' >> process.js - echo "const firstNonNullDigest = artifacts.find(artifact => artifact.extra && artifact.extra.Digest != null)?.extra.Digest;" >> process.js - echo "console.log(firstNonNullDigest);" >> process.js - echo "fs.writeFileSync('digest.txt', firstNonNullDigest);" >> process.js - node process.js - echo "digest=$(cat digest.txt)" >> $GITHUB_OUTPUT - - name: Attest Binaries - uses: actions/attest-build-provenance@v4.1.0 - with: - subject-path: dist/** - - name: generate build provenance - uses: actions/attest-build-provenance@v4.1.0 - with: - subject-name: ghcr.io/openchami/metadata-service - subject-digest: ${{ steps.process_goreleaser_output.outputs.digest }} - push-to-registry: true + rpmsign: + needs: [config, rpmbuild] + uses: OpenCHAMI/github-actions/.github/workflows/gpg-sign-artifacts.yml@v3.7 + secrets: inherit + with: + artifact-name-unsigned-rpms: ${{ needs.config.outputs.rpm-unsigned }} + artifact-name-signed-rpms: ${{ needs.config.outputs.rpm-signed }} + artifact-name-public-keys: ${{ needs.config.outputs.keys-public }} + + rpmvalidate: + needs: [config, rpmsign] + uses: OpenCHAMI/github-actions/.github/workflows/validate-rpm-quadlet.yml@v3.7 + secrets: inherit + with: + artifact-name-signed-rpms: ${{ needs.config.outputs.rpm-signed }} + rpms: | + - name: metadata-service-*.rpm + files: + - /etc/openchami + - /etc/openchami/configs + - /etc/openchami/configs/metadata-service.yaml + - /usr/share/containers/systemd/metadata-service-data.volume + - /usr/share/containers/systemd/metadata-service.container + - /usr/share/containers/systemd/metadata-service.container.d + - /usr/share/containers/systemd/metadata-service.container.d/10-defaults.conf + - /usr/share/licenses/metadata-service-quadlet + - /usr/share/licenses/metadata-service-quadlet/MIT.txt diff --git a/.github/workflows/Release.yaml b/.github/workflows/Release.yaml index 24862a8..d2d8396 100644 --- a/.github/workflows/Release.yaml +++ b/.github/workflows/Release.yaml @@ -1,9 +1,7 @@ -# Copyright © 2025 OpenCHAMI a Series of LF Projects, LLC -# +# Copyright © 2026 OpenCHAMI a Series of LF Projects, LLC # SPDX-License-Identifier: MIT -name: Release with goreleaser - +name: Release on: workflow_dispatch: push: @@ -14,70 +12,50 @@ permissions: write-all # Necessary for the generate-build-provenance action with jobs: - build: - - + config: runs-on: ubuntu-latest - + outputs: + rpm-unsigned: ${{ steps.names.outputs.rpm-unsigned }} + rpm-signed: ${{ steps.names.outputs.rpm-signed }} + keys-public: ${{ steps.names.outputs.keys-public }} steps: - - name: Set up latest stable Go - uses: actions/setup-go@v6.4.0 - with: - go-version: stable - - name: Set up QEMU - uses: docker/setup-qemu-action@v3 - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v3 - with: - driver-opts: | - image=moby/buildkit:master - network=host - - name: Docker Login - uses: docker/login-action@v3 - with: - registry: ghcr.io - username: ${{ github.actor }} - password: ${{ secrets.GITHUB_TOKEN }} - - name: Checkout - uses: actions/checkout@v6.0.2 - with: - fetch-tags: 1 - fetch-depth: 0 - # Set environment variables required by GoReleaser - - name: Set build environment variables + - id: names run: | - echo "GIT_STATE=$(if git diff-index --quiet HEAD --; then echo 'clean'; else echo 'dirty'; fi)" >> $GITHUB_ENV - echo "BUILD_HOST=$(hostname)" >> $GITHUB_ENV - echo "GO_VERSION=$(go version | awk '{print $3}')" >> $GITHUB_ENV - echo "BUILD_USER=$(whoami)" >> $GITHUB_ENV - echo "CGO_ENABLED=0" >> $GITHUB_ENV - echo "IS_PR_BUILD=false" >> $GITHUB_ENV + { + echo "rpm-unsigned=rpms-unsigned" + echo "rpm-signed=rpms-signed" + echo "keys-public=gpg-public-keys" + } >> "$GITHUB_OUTPUT" - - name: Release with goreleaser - uses: goreleaser/goreleaser-action@v6 - env: - GITHUB_TOKEN: ${{ github.token }} - with: - version: latest - args: release --clean - id: goreleaser - - name: Process goreleaser output - id: process_goreleaser_output - run: | - echo "const fs = require('fs');" > process.js - echo 'const artifacts = ${{ steps.goreleaser.outputs.artifacts }}' >> process.js - echo "const firstNonNullDigest = artifacts.find(artifact => artifact.extra && artifact.extra.Digest != null)?.extra.Digest;" >> process.js - echo "console.log(firstNonNullDigest);" >> process.js - echo "fs.writeFileSync('digest.txt', firstNonNullDigest);" >> process.js - node process.js - echo "digest=$(cat digest.txt)" >> $GITHUB_OUTPUT - - name: Attest Binaries - uses: actions/attest-build-provenance@v1 - with: - subject-path: dist/** - - name: generate build provenance - uses: actions/attest-build-provenance@v1 - with: - subject-name: ghcr.io/openchami/metadata-service - subject-digest: ${{ steps.process_goreleaser_output.outputs.digest }} - push-to-registry: true + build: + uses: OpenCHAMI/github-actions/.github/workflows/build-publish-container-goreleaser.yml@v3.7 + secrets: inherit + with: + cgo_enabled: 0 + go_version_file: go.mod + is_pr_build: false + registry_subject_name: ghcr.io/openchami/metadata-service + + rpmbuild: + needs: [config, build] + uses: OpenCHAMI/github-actions/.github/workflows/build-rpm-quadlet.yml@v3.7 + secrets: inherit + with: + artifact-name-unsigned-rpms: ${{ needs.config.outputs.rpm-unsigned }} + + rpmsign: + needs: [config, rpmbuild] + uses: OpenCHAMI/github-actions/.github/workflows/gpg-sign-artifacts.yml@v3.7 + secrets: inherit + with: + artifact-name-unsigned-rpms: ${{ needs.config.outputs.rpm-unsigned }} + artifact-name-signed-rpms: ${{ needs.config.outputs.rpm-signed }} + artifact-name-public-keys: ${{ needs.config.outputs.keys-public }} + + release: + needs: [config, rpmsign] + uses: OpenCHAMI/github-actions/.github/workflows/release-signed-artifacts.yml@v3.7 + secrets: inherit + with: + artifact-name-signed-rpms: ${{ needs.config.outputs.rpm-signed }} + artifact-name-public-keys: ${{ needs.config.outputs.keys-public }} diff --git a/.gitignore b/.gitignore index d3ce130..901f965 100644 --- a/.gitignore +++ b/.gitignore @@ -40,6 +40,8 @@ dist/ # Runtime config (contains environment-specific values) config.yaml config.yml +!packaging/**/configs/* +!config.example.yaml # IDE files .vscode/ @@ -49,4 +51,4 @@ config.yml *~ # Graphify Cache -graphify-out/cache/ \ No newline at end of file +graphify-out/cache/ diff --git a/Makefile b/Makefile index bd11a87..eb82b3b 100644 --- a/Makefile +++ b/Makefile @@ -3,7 +3,7 @@ # # SPDX-License-Identifier: MIT -.PHONY: help build test lint clean install run container-build container-run generate generate-check dev +.PHONY: help build test lint clean install run container-build container-run generate generate-check dev rpm-build rpm-clean # Variables BINARY_NAME=metadata-service @@ -15,6 +15,13 @@ DATE ?= $(shell date -u +"%Y-%m-%dT%H:%M:%SZ") CONTAINER_PROG ?= $(shell command -v docker 2>/dev/null) CONTAINER_TAG ?= latest CONTAINER_GO_VERSION ?= $(shell awk '/^go / {print $$2; exit}' go.mod) +# RPM version/release: strip the leading 'v' and drop git-describe's +# '-N-gHASH[-dirty]' suffix (hyphens aren't allowed in an RPM Version +# field anyway). An exact tag like v0.1.2 becomes 0.1.2. +RPM_VERSION ?= $(shell echo "$(VERSION)" | sed -e 's/^v//' -e 's/-.*//') +RPM_RELEASE ?= 1 +RPM_TOPDIR ?= $(CURDIR)/dist/rpmbuild +RPM_NAME ?= metadata-service-quadlet LDFLAGS=-ldflags "-X main.version=$(VERSION) -X main.commit=$(COMMIT) -X main.date=$(DATE)" FABRICA_CMD ?= go run github.com/openchami/fabrica/cmd/fabrica@latest FABRICA_SOURCE_ARG ?= @@ -102,6 +109,24 @@ container-run: container-build ## Build and run Docker container release-snapshot: ## Create a snapshot release with GoReleaser goreleaser release --snapshot --clean +rpm-build: ## Build the metadata-service RPM (VERSION/RPM_RELEASE override the derived defaults) + @command -v rpmbuild >/dev/null 2>&1 || { echo "rpmbuild is required but not installed."; exit 1; } + rm -rf $(RPM_TOPDIR) + mkdir -p $(RPM_TOPDIR)/SOURCES/$(RPM_NAME)-$(RPM_VERSION)/LICENSES + cp -rL packaging/rpm-quadlet/systemd/* $(RPM_TOPDIR)/SOURCES/$(RPM_NAME)-$(RPM_VERSION)/ + cp -rL packaging/rpm-quadlet/configs/* $(RPM_TOPDIR)/SOURCES/$(RPM_NAME)-$(RPM_VERSION)/ + cp LICENSES/MIT.txt $(RPM_TOPDIR)/SOURCES/$(RPM_NAME)-$(RPM_VERSION)/LICENSES/ + tar -C $(RPM_TOPDIR)/SOURCES -czf $(RPM_TOPDIR)/SOURCES/$(RPM_NAME)-$(RPM_VERSION).tar.gz \ + $(RPM_NAME)-$(RPM_VERSION) + rpmbuild --define "_topdir $(RPM_TOPDIR)" \ + --define "version $(RPM_VERSION)" \ + --define "rel $(RPM_RELEASE)" \ + -bb packaging/rpm-quadlet/$(RPM_NAME).spec + @echo "Built: $(RPM_TOPDIR)/RPMS/noarch/$$(ls $(RPM_TOPDIR)/RPMS/noarch)" + +rpm-clean: ## Remove local RPM build artifacts + rm -rf $(RPM_TOPDIR) + fmt: ## Format code $(GO) fmt ./... goimports -w . diff --git a/README.md b/README.md index cef972a..acc9fdb 100644 --- a/README.md +++ b/README.md @@ -19,6 +19,8 @@ Key capabilities The server defaults to port `8080`. The examples below use `8888` explicitly. +Flags used below can also be set in a config file (`--config`, default `/etc/metadata-service/config.yaml`) or via `METADATA_SERVICE_*` environment variables; see [`config.example.yaml`](config.example.yaml) for every option and its default. + 1. Start the server with the built-in mock SMD data: ```bash diff --git a/config.example.yaml b/config.example.yaml new file mode 100644 index 0000000..45b38a9 --- /dev/null +++ b/config.example.yaml @@ -0,0 +1,224 @@ +--- +# SPDX-FileCopyrightText: © 2026 OpenCHAMI a Series of LF Projects, LLC +# SPDX-License-Identifier: MIT +# +# /etc/metadata-service/config.yaml +# +# ============================================================================= +# Metadata‑Service - Example Configuration +# ============================================================================= +# +# This file serves as a reference that users can copy, uncomment, and edit +# for the options they need. All keys shown use the service's default +# values. Each option lists the flag (--flag-name) and the environment +# variable (METADATA_SERVICE_...) that can be used to override it. +# +# Environment variables may be set using a systemd drop-in (in order of highest +# precedence): +# /etc/containers/systemd/metadata-service.container.d/*.conf (site, wins) +# /usr/share/containers/systemd/metadata-service.container.d/*.conf (packaged) +# /etc/systemd/system/metadata-service.service.d/*.conf (generated unit) +# Run `systemctl daemon-reload` after editing any of them. +# +# Configuration precedence (highest first): +# 1. command-line flag +# 2. environment variable +# 3. this file +# 4. compiled-in default + +# ------------------------------------------------------------------------- +# Server Configuration +# ------------------------------------------------------------------------- + +# TCP port to listen on +# +# Default: 8080 +# Flag: --port +# Env var: METADATA_SERVICE_PORT +port: 8080 + +# Host/IP address to bind to +# +# Default: 0.0.0.0 (i.e. all interfaces) +# Flag: --host +# Env var: METADATA_SERVICE_HOST +host: "0.0.0.0" + +# HTTP request read timeout, in seconds +# +# Default: 15 +# Flag: --read-timeout +# Env var: METADATA_SERVICE_READ_TIMEOUT +#read_timeout: 15 + +# HTTP request write timeout, in seconds +# +# Default: 15 +# Flag: --write-timeout +# Env var: METADATA_SERVICE_WRITE_TIMEOUT +#write_timeout: 15 + +# HTTP connection idle timeout, in seconds +# +# Default: 60 +# Flag: --idle-timeout +# Env var: METADATA_SERVICE_IDLE_TIMEOUT +#idle_timeout: 60 + +# Enable debug‑level logging. When true, log entries include file and line +# information to aid troubleshooting. +# +# Flag: --debug +# Env var: METADATA_SERVICE_DEBUG +debug: false + +# Enable Prometheus metrics collection and expose the `/metrics` endpoint. +# Flag: --enable-metrics +# Env var: METADATA_SERVICE_ENABLE_METRICS +enable_metrics: false + +# Port on which the Prometheus metrics endpoint listens. +# Separate from the main service port for security isolation. +# Flag: --metrics-port +# Env var: METADATA_SERVICE_METRICS_PORT +metrics_port: 9090 + +# ------------------------------------------------------------------------- +# Storage Configuration +# ------------------------------------------------------------------------- + +# Directory used by the file‑based storage backend. +# Stores ClusterDefaults, Group, InstanceInfo, and WireGuardPeer resources. +# +# NOTE: When using the container, this should be the path where a volume/host +# directory is mounted into the container. The default /data is unwritable by +# the non-root container user. +# +# Default: /data +# Flag: --data-dir +# Env var: METADATA_SERVICE_DATA_DIR +data_dir: "/app/data" + +# ------------------------------------------------------------------------- +# WireGuard Configuration (Optional) +# ------------------------------------------------------------------------- + +# Path to the WireGuard state file for persistence. +# +# Example: /data/wireguard/state.yaml +# Flag: --wireguard-state-file +# Env var: METADATA_SERVICE_WIREGUARD_STATE_FILE +#wireguard_state_file: "/data/wireguard/state.yaml" + +# When true, the service rejects all requests that do not originate from the +# configured WireGuard CIDR. Set to false to allow regular network access. +# +# Flag: --wireguard-only +# Env var: METADATA_SERVICE_WIREGUARD_ONLY +wireguard_only: false + +# ------------------------------------------------------------------------- +# TokenSmith Configuration (Optional - for dynamic authentication) +# ------------------------------------------------------------------------- + +# Base URL of the TokenSmith service. +# Supports both mTLS and bootstrap‑token authentication modes. +# +# Default: "" +# Example: https://tokensmith.example.com +# Flag: --tokensmith-url +# Env var: METADATA_SERVICE_TOKENSMITH_URL +# TOKENSMITH_URL +tokensmith_url: "http://tokensmith:8080" + +# Bootstrap token used when the service cannot present a client certificate. +# Required only if `tokensmith_url` is set and mTLS is not used. +# +# Flag: --tokensmith-bootstrap-token +# Env var: METADATA_SERVICE_TOKENSMITH_BOOTSTRAP_TOKEN +# TOKENSMITH_BOOTSTRAP_TOKEN +#tokensmith_bootstrap_token: "" + +# Path to the client certificate (PEM) for mTLS authentication with TokenSmith. +# +# Flag: --tokensmith-service-identity-cert +# Env var: METADATA_SERVICE_TOKENSMITH_SERVICE_IDENTITY_CERT +# TOKENSMITH_SERVICE_IDENTITY_CERT +#tokensmith_service_identity_cert: "" + +# Path to the client private key (PEM) for mTLS authentication with TokenSmith. +# +# Flag: --tokensmith-service-identity-key +# Env var: METADATA_SERVICE_TOKENSMITH_SERVICE_IDENTITY_KEY +# TOKENSMITH_SERVICE_IDENTITY_KEY +#tokensmith_service_identity_key: "" + +# Optional CA certificate (PEM) used to verify the TokenSmith server's +# TLS certificate. If omitted the system's default CA pool is used. +# +# Flag: --tokensmith-service-identity-ca +# Env var: METADATA_SERVICE_TOKENSMITH_SERVICE_IDENTITY_CA +# TOKENSMITH_SERVICE_IDENTITY_CA +#tokensmith_service_identity_ca: "" + +# Target downstream service name for which TokenSmith should issue a token. +# +# Default: "smd" +# Flag: --tokensmith-target-service +# Env var: METADATA_SERVICE_TOKENSMITH_TARGET_SERVICE +# TOKENSMITH_TARGET_SERVICE +tokensmith_target_service: "smd" + +# Diagnostic hint: a comma‑separated list of scopes that the bootstrap token +# is allowed to request. Not enforced by the service. +# +# Example: "metadata:read,groups:read" +# Flag: --tokensmith-bootstrap-policy-scopes-hint +# Env var: METADATA_SERVICE_TOKENSMITH_BOOTSTRAP_POLICY_SCOPES_HINT +# TOKENSMITH_BOOTSTRAP_POLICY_SCOPES_HINT +#tokensmith_bootstrap_policy_scopes_hint: "" + +# Number of seconds before a token's expiry when the service should attempt a +# refresh. This avoids edge‑case failures when the token is about to expire. +# +# Flag: --tokensmith-refresh-skew-sec +# Env var: METADATA_SERVICE_TOKENSMITH_REFRESH_SKEW_SEC +#tokensmith_refresh_skew_sec: 300 + +# ------------------------------------------------------------------------- +# SMD Integration Configuration +# ------------------------------------------------------------------------- + +# Enable the background worker that synchronizes node/group membership from +# the System Management Database (SMD). Set to false to disable the cache. +# +# Flag: --smd-sync-enabled +# Env var: METADATA_SERVICE_SMD_SYNC_ENABLED +smd_sync_enabled: true + +# Interval, in seconds, at which the SMD cache is refreshed. +# +# Flag: --smd-sync-interval +# Env var: METADATA_SERVICE_SMD_SYNC_INTERVAL +smd_sync_interval: 60 + +# ------------------------------------------------------------------------- +# Additional (Non‑Config‑File) Flags +# ------------------------------------------------------------------------- +# The following flags are persistent (global) and are NOT represented in the +# YAML configuration, but they can still be used on the command line or via +# environment variables: +# +# --mock-smd (env: METADATA_SERVICE_MOCK_SMD) +# Use built‑in mock SMD data instead of contacting a real SMD service. +# +# --config (env: METADATA_SERVICE_CONFIG) +# Path to an alternative configuration file. +# +# --debug (already listed above) +# +# --help, --version, etc. are standard Cobra flags. + +# ============================================================================= +# End of example configuration. +# ============================================================================= diff --git a/docs/DEPLOYMENT.md b/docs/DEPLOYMENT.md index 7b2504b..145f6b5 100644 --- a/docs/DEPLOYMENT.md +++ b/docs/DEPLOYMENT.md @@ -554,6 +554,9 @@ sudo systemctl start metadata-service ## Configuration Reference +See [`config.example.yaml`](../config.example.yaml) for a documented example +configuration file. + ### Environment Variables | Variable | Description | Default | Required | diff --git a/packaging/common/configs/metadata-service.yaml b/packaging/common/configs/metadata-service.yaml new file mode 120000 index 0000000..5543fc5 --- /dev/null +++ b/packaging/common/configs/metadata-service.yaml @@ -0,0 +1 @@ +../../../config.example.yaml \ No newline at end of file diff --git a/packaging/common/systemd/metadata-service-data.volume b/packaging/common/systemd/metadata-service-data.volume new file mode 100644 index 0000000..41ae9ff --- /dev/null +++ b/packaging/common/systemd/metadata-service-data.volume @@ -0,0 +1,7 @@ +# SPDX-FileCopyrightText: 2026 OpenCHAMI Contributors +# SPDX-License-Identifier: MIT + +[Unit] +Description=%N Volume + +[Volume] diff --git a/packaging/common/systemd/metadata-service.container b/packaging/common/systemd/metadata-service.container new file mode 100644 index 0000000..14b38d4 --- /dev/null +++ b/packaging/common/systemd/metadata-service.container @@ -0,0 +1,16 @@ +# SPDX-FileCopyrightText: 2026 OpenCHAMI Contributors +# SPDX-License-Identifier: MIT + +[Unit] +Description=The %N container +Wants=smd.service +After=smd.service +PartOf=openchami.target + +[Container] +ContainerName=%N +HostName=%N +Image=ghcr.io/openchami/%N:@IMAGE_TAG@ + +[Service] +Restart=always diff --git a/packaging/common/systemd/metadata-service.container.d/10-defaults.conf b/packaging/common/systemd/metadata-service.container.d/10-defaults.conf new file mode 100644 index 0000000..46e5256 --- /dev/null +++ b/packaging/common/systemd/metadata-service.container.d/10-defaults.conf @@ -0,0 +1,36 @@ +# SPDX-FileCopyrightText: 2026 OpenCHAMI Contributors +# SPDX-License-Identifier: MIT +# +# Deployment glue for metadata-service, shipped with this repo's package. +# It supplies the Podman and systemd wiring for the default deployment style; +# meta-configuration packages from the OpenCHAMI distro repositories override +# it with drop-ins for other styles (single-node, multi-node, and so on). +# +# Scope: Podman and systemd directives only - volumes, networks, secrets, +# capabilities, devices, ExecStartPre, and the like. +# +# Not for environment variables. Service configuration lives in +# /etc/openchami/configs/metadata-service.yaml, which the maintainers keep as +# the single place it is set. Set environment variables in your own drop-in +# under /etc/containers/systemd/metadata-service.container.d/ if you need them, +# but note they override the config file. + +[Container] + +# SECRETS +Secret=%N-bootstrap-token,type=env,target=TOKENSMITH_BOOTSTRAP_TOKEN + +# VOLUMES +Volume=/etc/openchami/configs/%N.yaml:/etc/%N/config.yaml:ro,Z +Volume=metadata-service-data.volume:/app/data:rw,Z + +# NETWORK +Network=openchami-internal.network +HttpProxy=false + +# For WireGuard tunnel creation +AddCapability=cap_net_admin +AddDevice=/dev/net/tun + +[Service] +ExecStartPre=/usr/sbin/tokensmith_bootstrap_token metadata-service diff --git a/packaging/rpm-quadlet/configs b/packaging/rpm-quadlet/configs new file mode 120000 index 0000000..cc94d77 --- /dev/null +++ b/packaging/rpm-quadlet/configs @@ -0,0 +1 @@ +../common/configs \ No newline at end of file diff --git a/packaging/rpm-quadlet/metadata-service-quadlet.spec b/packaging/rpm-quadlet/metadata-service-quadlet.spec new file mode 100644 index 0000000..065cf71 --- /dev/null +++ b/packaging/rpm-quadlet/metadata-service-quadlet.spec @@ -0,0 +1,71 @@ +# SPDX-FileCopyrightText: 2026 OpenCHAMI Contributors +# SPDX-License-Identifier: MIT +# +# See `make rpm-build` and docs/RPM_PACKAGING.md for the tag-to-version +# mapping and how the packaged quadlet's image tag is pinned to it. + +Name: metadata-service-quadlet +Version: %{version} +Release: %{rel}%{?dist} +Summary: OpenCHAMI metadata-service Quadlet units + +License: MIT +URL: https://github.com/OpenCHAMI/metadata-service +Source0: %{name}-%{version}.tar.gz + +BuildArch: noarch + +Requires(post,preun,postun): systemd +Requires: podman >= 5.0.0 +Suggests: smd-quadlet >= 2.20.0 +Suggests: tokensmith-quadlet >= 0.4.0 + +%description +Podman Quadlet unit files (container + volume) for running metadata-service +as part of an OpenCHAMI deployment. + +%prep +%setup -q + +%install +mkdir -p %{buildroot}/usr/share/containers/systemd +mkdir -p %{buildroot}/etc/openchami/configs + +grep -q '@IMAGE_TAG@' metadata-service.container +sed "s|@IMAGE_TAG@|v%{version}|" metadata-service.container \ + > %{buildroot}/usr/share/containers/systemd/metadata-service.container +chmod 644 %{buildroot}/usr/share/containers/systemd/metadata-service.container +install -d %{buildroot}/usr/share/containers/systemd/metadata-service.container.d +install -m 644 metadata-service.container.d/10-defaults.conf \ + %{buildroot}/usr/share/containers/systemd/metadata-service.container.d/ + +install -m 644 metadata-service-data.volume \ + %{buildroot}/usr/share/containers/systemd/metadata-service-data.volume +install -m 644 metadata-service.yaml \ + %{buildroot}/etc/openchami/configs/metadata-service.yaml + +%files +%license LICENSES/MIT.txt +%dir /etc/openchami +%dir /etc/openchami/configs +%config(noreplace) /etc/openchami/configs/metadata-service.yaml +/usr/share/containers/systemd/metadata-service.container +/usr/share/containers/systemd/metadata-service.container.d +/usr/share/containers/systemd/metadata-service.container.d/10-defaults.conf +/usr/share/containers/systemd/metadata-service-data.volume + +%post +# reload systemd so the new Quadlet-generated unit is seen +systemctl daemon-reload || : +if [ $1 -ge 2 ]; then + systemctl try-restart metadata-service.service || : +fi + +%preun +if [ $1 -eq 0 ]; then + systemctl stop metadata-service.service >/dev/null 2>&1 || : +fi + +%postun +# reload systemd so the removed unit is dropped +systemctl daemon-reload || : diff --git a/packaging/rpm-quadlet/systemd b/packaging/rpm-quadlet/systemd new file mode 120000 index 0000000..2ce8a7a --- /dev/null +++ b/packaging/rpm-quadlet/systemd @@ -0,0 +1 @@ +../common/systemd \ No newline at end of file