diff --git a/REFERENCE.md b/REFERENCE.md
index 26c2ece..24423d3 100644
--- a/REFERENCE.md
+++ b/REFERENCE.md
@@ -26,6 +26,7 @@
### Data types
+* [`Rootless_gitlab_runner::Apt_source`](#Rootless_gitlab_runner--Apt_source): An apt source's location and signing-key endpoint.
* [`Rootless_gitlab_runner::Username`](#Rootless_gitlab_runner--Username): A strict POSIX-portable Linux username.
## Classes
@@ -234,14 +235,8 @@ Struct[{
install => Array[String[1]],
sources => Struct[{
manage => Boolean,
- docker => Struct[{
- location => Stdlib::HTTPUrl,
- key_source => Stdlib::HTTPUrl,
- }],
- gitlab_runner => Struct[{
- location => Stdlib::HTTPUrl,
- key_source => Stdlib::HTTPUrl,
- }],
+ docker => Rootless_gitlab_runner::Apt_source,
+ gitlab_runner => Rootless_gitlab_runner::Apt_source,
}],
}]
```
@@ -253,7 +248,7 @@ Options:
* **:install** `Array[String[1]]`: Packages to ensure installed. The empty default installs nothing. Install
only: the module never removes packages absent from the list and never
pins or upgrades.
-* **:sources** `Hash`: The apt sources the `install` list installs from (Docker's and GitLab
+* **:sources** `Struct[{ manage => Boolean, docker => Apt_source, gitlab_runner => Apt_source }]`: The apt sources the `install` list installs from (Docker's and GitLab
Runner's, via `puppetlabs/apt`). `sources.manage` (default false) decides
ownership: keep it off where apt sources are owned elsewhere (central
configuration management, e.g. Foreman/Katello, or a mirror). The
@@ -317,11 +312,11 @@ Options:
* **:manage** `Boolean`: Whether to manage the runner system service, its privilege-drop systemd
drop-in, and the configuration directory's mode so a privilege-dropped
manager can traverse to its configuration file. Default false.
-* **:environment** `Optional[Array[String]]`: `Environment=` lines (KEY=value) rendered into the service drop-in. When
+* **:environment** `Optional[Array[Pattern[/\A[^\r\n]+\z/]]]`: `Environment=` lines (KEY=value) rendered into the service drop-in. When
unset, defaults to pointing DOCKER_HOST at the rootless docker socket
-derived from `runner_account.uid`. Each line must be a single line — a
-value containing a newline is rejected, so it cannot inject an extra
-systemd directive into the drop-in.
+derived from `runner_account.uid`. The `Pattern` enforces the type: each
+line must be non-empty and single-line — a value containing a newline is
+rejected, so it cannot inject an extra systemd directive into the drop-in.
* **:timeout_stop_sec** `Optional[Variant[Integer[0], String[1]]]`: `TimeoutStopSec=` written into the manager service drop-in — how long
systemd waits for a graceful drain before escalating to `SIGKILL`. Unset
by default, so systemd's `DefaultTimeoutStopSec` (typically 90s) applies;
@@ -360,10 +355,11 @@ Options:
(`/usr/local/sbin/rootless-gitlab-runner-apply`), the single definition
of the apply command for timer-driven and manual runs. Default false.
* **:control_repository_path** `Stdlib::Absolutepath`: Root-owned checkout of the control repository on the host (the apply and
-self-update target). The apply's manifest, module directory and Hiera
-configuration derive strictly from the documented layout beneath it
-(`puppet/manifests/site.pp`, `puppet/modules`, `puppet/hiera.yaml`).
-Default `/opt/gitlab-runner-infra`.
+self-update target). The apply's manifest, module directory, Hiera
+configuration and optional `Puppetfile` derive strictly from the documented
+layout beneath it (`puppet/manifests/site.pp`, `puppet/modules`,
+`puppet/hiera.yaml`, and `Puppetfile` at the root — r10k installs it each
+tick when present). Default `/opt/gitlab-runner-infra`.
* **:control_repository_branch** `String[1]`: Branch the self-update loop follows (protected, signed). Default `main`.
* **:puppet_confdir** `Stdlib::Absolutepath`: Isolated Puppet confdir for the apply — never the central agent's.
Default `/etc/gitlab-runner-infra/puppet`.
@@ -375,7 +371,7 @@ non-login apply can find them. Default `/opt/puppetlabs/bin`; the
AIO-reuse topology sets `/opt/puppetlabs/puppet/bin` (gem executables
live there, not in the symlink farm).
* **:healthcheck_interval** `String[1]`: systemd time span between healthcheck runs. Default `15min`.
-* **:self_update** `Hash`: The self-update loop: `manage` (default false) installs a oneshot
+* **:self_update** `Struct[{ manage => Boolean, apply_interval => String[1], apply_timeout => String[1] }]`: The self-update loop: `manage` (default false) installs a oneshot
service + timer that fetch the control repository, verify the commit
signature, reset to the remote branch, run `r10k puppetfile install` and
re-apply — plus the healthcheck script + timer. Only valid on a
@@ -427,6 +423,23 @@ the raw string to escape
## Data types
+### `Rootless_gitlab_runner::Apt_source`
+
+`location` and `key_source` are verbatim `apt::source` parameter names: the
+repository URL (suite = OS codename) and the URL of its armored signing key,
+stored as an apt keyring. Both the Docker and GitLab Runner sources under
+`packages.sources` share this shape, pointing at a vendor repository or a
+mirror.
+
+Alias of
+
+```puppet
+Struct[{
+ location => Stdlib::HTTPUrl,
+ key_source => Stdlib::HTTPUrl,
+}]
+```
+
### `Rootless_gitlab_runner::Username`
The runner user is interpolated raw into shell execs (the subuid/subgid
diff --git a/manifests/config.pp b/manifests/config.pp
index 8029fff..769b93a 100644
--- a/manifests/config.pp
+++ b/manifests/config.pp
@@ -3,11 +3,12 @@
class rootless_gitlab_runner::config {
assert_private()
- $runner_name = $rootless_gitlab_runner::runner_account['name']
- # Primary group, defaulting to the account name (derived in init.pp). Owners
- # stay the account name; only the group ownerships derive from it.
+ # Identity locals hoisted in init.pp. Owners stay the account name; only the
+ # group ownerships derive from $runner_group (the account's primary group,
+ # defaulting to the name).
+ $runner_name = $rootless_gitlab_runner::runner_name
$runner_group = $rootless_gitlab_runner::runner_group
- $runner_home = $rootless_gitlab_runner::runner_account['home']
+ $runner_home = $rootless_gitlab_runner::runner_home
$configuration_file_path = $rootless_gitlab_runner::configuration_file['path']
$socket_path = $rootless_gitlab_runner::socket_path
@@ -138,14 +139,18 @@
# side effects — and Puppet's file type never creates parents. Own the whole
# parent chain explicitly (ordered after the bring-up by the class order),
# so the first apply on a fresh host can always place the drop-in, even if a
- # future setuptool changes what it creates. Puppet autorequires each managed
- # parent, so the chain applies top-down.
- $dropin_path = "${runner_home}/.config/systemd/user/docker.service.d/no-detach-netns.conf"
- $dropin_dir = dirname($dropin_path)
- $user_units_dir = dirname($dropin_dir)
- $user_systemd_dir = dirname($user_units_dir)
- $user_config_dir = dirname($user_systemd_dir)
- file { [$user_config_dir, $user_systemd_dir, $user_units_dir, $dropin_dir]:
+ # future setuptool changes what it creates. The chain is derived bottom-up in
+ # init.pp (each directory its parent plus one element) and shared with the
+ # rootless bring-up; Puppet autorequires each managed parent, so it still
+ # applies top-down. Only the drop-in directory and file are leaves here.
+ $dropin_dir = "${rootless_gitlab_runner::user_systemd_dir}/docker.service.d"
+ $dropin_path = "${dropin_dir}/no-detach-netns.conf"
+ file { [
+ $rootless_gitlab_runner::user_config_dir,
+ $rootless_gitlab_runner::user_config_systemd_dir,
+ $rootless_gitlab_runner::user_systemd_dir,
+ $dropin_dir,
+ ]:
ensure => directory,
owner => $runner_name,
group => $runner_group,
diff --git a/manifests/init.pp b/manifests/init.pp
index 643bf1c..0770fc7 100644
--- a/manifests/init.pp
+++ b/manifests/init.pp
@@ -134,7 +134,7 @@
# Packages to ensure installed. The empty default installs nothing. Install
# only: the module never removes packages absent from the list and never
# pins or upgrades.
-# @option packages [Hash] :sources
+# @option packages [Struct[{ manage => Boolean, docker => Apt_source, gitlab_runner => Apt_source }]] :sources
# The apt sources the `install` list installs from (Docker's and GitLab
# Runner's, via `puppetlabs/apt`). `sources.manage` (default false) decides
# ownership: keep it off where apt sources are owned elsewhere (central
@@ -173,12 +173,12 @@
# Whether to manage the runner system service, its privilege-drop systemd
# drop-in, and the configuration directory's mode so a privilege-dropped
# manager can traverse to its configuration file. Default false.
-# @option runner_service [Optional[Array[String]]] :environment
+# @option runner_service [Optional[Array[Pattern[/\A[^\r\n]+\z/]]]] :environment
# `Environment=` lines (KEY=value) rendered into the service drop-in. When
# unset, defaults to pointing DOCKER_HOST at the rootless docker socket
-# derived from `runner_account.uid`. Each line must be a single line — a
-# value containing a newline is rejected, so it cannot inject an extra
-# systemd directive into the drop-in.
+# derived from `runner_account.uid`. The `Pattern` enforces the type: each
+# line must be non-empty and single-line — a value containing a newline is
+# rejected, so it cannot inject an extra systemd directive into the drop-in.
# @option runner_service [Optional[Variant[Integer[0], String[1]]]] :timeout_stop_sec
# `TimeoutStopSec=` written into the manager service drop-in — how long
# systemd waits for a graceful drain before escalating to `SIGKILL`. Unset
@@ -196,10 +196,11 @@
# of the apply command for timer-driven and manual runs. Default false.
# @option standalone [Stdlib::Absolutepath] :control_repository_path
# Root-owned checkout of the control repository on the host (the apply and
-# self-update target). The apply's manifest, module directory and Hiera
-# configuration derive strictly from the documented layout beneath it
-# (`puppet/manifests/site.pp`, `puppet/modules`, `puppet/hiera.yaml`).
-# Default `/opt/gitlab-runner-infra`.
+# self-update target). The apply's manifest, module directory, Hiera
+# configuration and optional `Puppetfile` derive strictly from the documented
+# layout beneath it (`puppet/manifests/site.pp`, `puppet/modules`,
+# `puppet/hiera.yaml`, and `Puppetfile` at the root — r10k installs it each
+# tick when present). Default `/opt/gitlab-runner-infra`.
# @option standalone [String[1]] :control_repository_branch
# Branch the self-update loop follows (protected, signed). Default `main`.
# @option standalone [Stdlib::Absolutepath] :puppet_confdir
@@ -216,7 +217,7 @@
# live there, not in the symlink farm).
# @option standalone [String[1]] :healthcheck_interval
# systemd time span between healthcheck runs. Default `15min`.
-# @option standalone [Hash] :self_update
+# @option standalone [Struct[{ manage => Boolean, apply_interval => String[1], apply_timeout => String[1] }]] :self_update
# The self-update loop: `manage` (default false) installs a oneshot
# service + timer that fetch the control repository, verify the commit
# signature, reset to the remote branch, run `r10k puppetfile install` and
@@ -246,14 +247,8 @@
install => Array[String[1]],
sources => Struct[{
manage => Boolean,
- docker => Struct[{
- location => Stdlib::HTTPUrl,
- key_source => Stdlib::HTTPUrl,
- }],
- gitlab_runner => Struct[{
- location => Stdlib::HTTPUrl,
- key_source => Stdlib::HTTPUrl,
- }],
+ docker => Rootless_gitlab_runner::Apt_source,
+ gitlab_runner => Rootless_gitlab_runner::Apt_source,
}],
}] $packages,
Struct[{
@@ -301,22 +296,28 @@
]))
}
- # The account's primary group, derived once and read as a group by every
- # concern (the group resource and the user's gid in user.pp, and every
- # managed file's group in config.pp). The data layer cannot express "same as
- # the account name", so the default is an absent key and the fallback lives
- # here: an unset group follows the account name — correct by construction
- # where the module creates the account — while a set group names the
- # differently named primary group of an externally provisioned account.
- $runner_group = pick($runner_account['group'], $runner_account['name'])
+ # The runner account's identity, hoisted once so every concern reads a local
+ # instead of re-subscripting runner_account per file. The primary group is
+ # read as a group everywhere the account name used to double as one (the
+ # group resource and the user's gid in user.pp, every managed file's group in
+ # config.pp); it defaults to the account name, since the data layer cannot
+ # express "same as the name" — an unset group falls back here, correct by
+ # construction where the module creates the account, while a set group names
+ # the differently named primary group of an externally provisioned account.
+ $runner_name = $runner_account['name']
+ $runner_home = $runner_account['home']
+ $runner_group = pick($runner_account['group'], $runner_name)
# Defaults merged under every runner entry; keys set on the entry win.
$effective_runners = $runners.map |$r| { $runner_defaults + $r }
# Rootless runtime paths derived from the uid where known: the module
# itself installs the daemon socket at /run/user//docker.sock, so the
- # path is derivation, not configuration. An exotic external socket is
- # expressible via runner_service.environment.
+ # path is derivation, not configuration. The one escape hatch is narrow:
+ # runner_service.environment can point the manager service's DOCKER_HOST at
+ # a socket elsewhere. Nothing else follows it — the socket_mount volume and
+ # the healthcheck probe keep using the derived path — so it is not a general
+ # "external daemon" switch.
if $runner_account['uid'] =~ Integer {
$runtime_dir = "/run/user/${runner_account['uid']}"
$socket_path = "${runtime_dir}/docker.sock"
@@ -329,8 +330,8 @@
# root Puppet provides neither XDG_RUNTIME_DIR nor DBUS_SESSION_BUS_ADDRESS,
# and systemctl --user / the setuptool fail without them.
$runner_user_env = [
- "HOME=${runner_account['home']}",
- "USER=${runner_account['name']}",
+ "HOME=${runner_home}",
+ "USER=${runner_name}",
"XDG_RUNTIME_DIR=${runtime_dir}",
"DBUS_SESSION_BUS_ADDRESS=unix:path=${runtime_dir}/bus",
]
@@ -342,6 +343,11 @@
]))
}
+ # The inner `undef => []` arm (environment unset and no derivable socket) is
+ # reached only when runner_service.manage is off: the fail-loud guard above
+ # rejects that exact combination while the service is managed. So [] is the
+ # never-consumed value that keeps the selector total — service.pp renders the
+ # drop-in only under manage — not a deliberately empty Environment.
$real_service_environment = $runner_service['environment'] ? {
undef => $socket_path ? {
undef => [],
@@ -350,6 +356,21 @@
default => $runner_service['environment'],
}
+ # The runner manager's systemd unit name is the one the gitlab-runner package
+ # defines, not configuration. Derived once and shared by the service class
+ # and the healthcheck so the literal has a single home.
+ $service_name = 'gitlab-runner'
+
+ # The runner account's systemd user unit directory (~/.config/systemd/user)
+ # and its parents, built bottom-up from the home so each directory is its
+ # parent plus one path element (no dirname() walk back up). Puppet's file
+ # type never creates parents, so config.pp manages the whole chain to place
+ # the no-detach-netns drop-in; rootless_docker.pp reads $user_systemd_dir for
+ # the docker.service the setuptool generates there.
+ $user_config_dir = "${runner_home}/.config"
+ $user_config_systemd_dir = "${user_config_dir}/systemd"
+ $user_systemd_dir = "${user_config_systemd_dir}/user"
+
contain rootless_gitlab_runner::apt_repos
contain rootless_gitlab_runner::packages
contain rootless_gitlab_runner::user
diff --git a/manifests/rootless_docker.pp b/manifests/rootless_docker.pp
index e0008dc..ee0f66c 100644
--- a/manifests/rootless_docker.pp
+++ b/manifests/rootless_docker.pp
@@ -4,8 +4,8 @@
assert_private()
if $rootless_gitlab_runner::rootless_docker['manage'] {
- $runner_user = $rootless_gitlab_runner::runner_account['name']
- $runner_home = $rootless_gitlab_runner::runner_account['home']
+ $runner_user = $rootless_gitlab_runner::runner_name
+ $runner_home = $rootless_gitlab_runner::runner_home
$runner_uid = $rootless_gitlab_runner::runner_account['uid']
$runtime_dir = $rootless_gitlab_runner::runtime_dir
$user_env = $rootless_gitlab_runner::runner_user_env
@@ -13,6 +13,8 @@
# defines; if it is absent the install exec below fails loud (a missing
# command is a command failure, not a guard failure).
$setuptool = '/usr/bin/dockerd-rootless-setuptool.sh'
+ # The user unit the setuptool generates, under the shared systemd user dir.
+ $docker_user_unit = "${rootless_gitlab_runner::user_systemd_dir}/docker.service"
# A fresh docker-ce install leaves the rootful system daemon running: the
# package postinst starts docker.service + docker.socket as root, and
@@ -128,8 +130,8 @@
# success. Assert the user unit exists after install, turning a silent skip
# into a hard failure.
exec { 'rootless_gitlab_runner setuptool install':
- command => "${setuptool} install && test -f ${runner_home}/.config/systemd/user/docker.service",
- creates => "${runner_home}/.config/systemd/user/docker.service",
+ command => "${setuptool} install && test -f ${docker_user_unit}",
+ creates => $docker_user_unit,
user => $runner_user,
environment => $user_env,
path => ['/usr/bin', '/bin'],
diff --git a/manifests/self_update.pp b/manifests/self_update.pp
index d04e76e..c49e6eb 100644
--- a/manifests/self_update.pp
+++ b/manifests/self_update.pp
@@ -20,12 +20,12 @@
group => 'root',
mode => '0755',
content => epp('rootless_gitlab_runner/apply.sh.epp', {
- 'control_repository_path' => $control_repository_path,
- 'manifest_path' => "${control_repository_path}/puppet/manifests/site.pp",
- 'module_directory' => "${control_repository_path}/puppet/modules",
- 'hiera_config' => "${control_repository_path}/puppet/hiera.yaml",
- 'confdir' => $standalone['puppet_confdir'],
- 'vardir' => $standalone['puppet_vardir'],
+ 'puppetfile_path' => "${control_repository_path}/Puppetfile",
+ 'manifest_path' => "${control_repository_path}/puppet/manifests/site.pp",
+ 'module_directory' => "${control_repository_path}/puppet/modules",
+ 'hiera_config' => "${control_repository_path}/puppet/hiera.yaml",
+ 'confdir' => $standalone['puppet_confdir'],
+ 'vardir' => $standalone['puppet_vardir'],
}),
}
}
@@ -49,11 +49,12 @@
group => 'root',
mode => '0755',
content => epp('rootless_gitlab_runner/healthcheck.sh.epp', {
- 'runner_name' => $rootless_gitlab_runner::runner_account['name'],
+ 'runner_name' => $rootless_gitlab_runner::runner_name,
'runtime_dir' => $rootless_gitlab_runner::runtime_dir,
'socket_path' => $rootless_gitlab_runner::socket_path,
'control_repository_path' => $control_repository_path,
'control_repository_branch' => $standalone['control_repository_branch'],
+ 'service_name' => $rootless_gitlab_runner::service_name,
}),
}
diff --git a/manifests/service.pp b/manifests/service.pp
index 4de3bf3..3adca5f 100644
--- a/manifests/service.pp
+++ b/manifests/service.pp
@@ -8,7 +8,7 @@
# as root would contradict the module's identity — and the service name is
# the package-defined unit, not configuration.
if $rootless_gitlab_runner::runner_service['manage'] {
- $service_name = 'gitlab-runner'
+ $service_name = $rootless_gitlab_runner::service_name
$service_dropin_dir = "/etc/systemd/system/${service_name}.service.d"
file { $service_dropin_dir:
@@ -34,8 +34,8 @@
group => 'root',
mode => '0644',
content => epp('rootless_gitlab_runner/service-dropin.conf.epp', {
- 'runner_name' => $rootless_gitlab_runner::runner_account['name'],
- 'runner_home' => $rootless_gitlab_runner::runner_account['home'],
+ 'runner_name' => $rootless_gitlab_runner::runner_name,
+ 'runner_home' => $rootless_gitlab_runner::runner_home,
'configuration_file_path' => $rootless_gitlab_runner::configuration_file['path'],
'environment' => $rootless_gitlab_runner::real_service_environment,
'timeout_stop_sec' => $rootless_gitlab_runner::runner_service['timeout_stop_sec'],
diff --git a/manifests/user.pp b/manifests/user.pp
index 7aaab10..ea87662 100644
--- a/manifests/user.pp
+++ b/manifests/user.pp
@@ -6,8 +6,8 @@
$account = $rootless_gitlab_runner::runner_account
if $account['manage'] {
- $runner_name = $account['name']
- # Primary group, defaulting to the account name (derived in init.pp).
+ # Identity locals hoisted in init.pp; only manage and uid stay on $account.
+ $runner_name = $rootless_gitlab_runner::runner_name
$runner_group = $rootless_gitlab_runner::runner_group
group { $runner_group:
@@ -22,7 +22,7 @@
system => true,
uid => $account['uid'],
gid => $runner_group,
- home => $account['home'],
+ home => $rootless_gitlab_runner::runner_home,
managehome => true,
shell => '/bin/bash',
require => Group[$runner_group],
diff --git a/spec/classes/rootless_gitlab_runner_spec.rb b/spec/classes/rootless_gitlab_runner_spec.rb
index 17c88a4..e838c6b 100644
--- a/spec/classes/rootless_gitlab_runner_spec.rb
+++ b/spec/classes/rootless_gitlab_runner_spec.rb
@@ -1,5 +1,6 @@
require 'spec_helper'
require 'yaml'
+require 'deep_merge'
# puppetlabs-apt compiles only on Debian-family facts; the suite otherwise runs
# factless, so the apt-enabled and example contexts pin the single supported
@@ -15,30 +16,23 @@
).freeze
describe 'rootless_gitlab_runner' do
- # config.toml content is Sensitive (it carries tokens); unwrap it for byte
- # and pattern assertions.
- def rendered_config(path = '/etc/gitlab-runner/config.toml')
- content = catalogue.resource('File', path)[:content]
- content.respond_to?(:unwrap) ? content.unwrap : content
- end
-
# The rendered content of any managed File resource (unwrapped if Sensitive).
def rendered_file(path)
content = catalogue.resource('File', path)[:content]
content.respond_to?(:unwrap) ? content.unwrap : content
end
+ # config.toml content is Sensitive (it carries tokens); rendered_file unwraps
+ # it for byte and pattern assertions. The path is the module default.
+ def rendered_config
+ rendered_file('/etc/gitlab-runner/config.toml')
+ end
+
# Load and parse a YAML file from examples/.
def example_yaml(*path)
YAML.safe_load(File.read(File.expand_path(File.join(__dir__, '..', '..', 'examples', *path))))
end
- def deep_merge(base, override)
- base.merge(override) do |_k, old, new|
- old.is_a?(Hash) && new.is_a?(Hash) ? deep_merge(old, new) : new
- end
- end
-
# rspec-puppet serializes a Ruby nil param value as the literal string
# "nil"; a YAML `~` default must reach Puppet as undef instead.
def undefize(value)
@@ -50,9 +44,22 @@ def undefize(value)
end
# A struct parameter's module-data default with overrides merged deep over
- # it, mirroring the deep-merge lookup_options a Hiera consumer gets.
+ # it, mirroring the deep-merge lookup_options a Hiera consumer gets (strategy:
+ # deep, knockout_prefix: '--'): scalars from the override win, sub-hashes
+ # merge recursively, arrays union across layers, and a '--'-prefixed element
+ # knocks the matching one out. Uses the same deep_merge gem Hiera's deep
+ # strategy is built on, so struct_param merges the way Hiera would rather than
+ # replacing whole arrays. Both operands are deep-copied first: deep_merge!
+ # mutates its destination, and MODULE_DATA must stay pristine across calls.
def struct_param(name, overrides = {})
- undefize(deep_merge(MODULE_DATA.fetch("rootless_gitlab_runner::#{name}"), overrides))
+ base = Marshal.load(Marshal.dump(MODULE_DATA.fetch("rootless_gitlab_runner::#{name}")))
+ undefize(DeepMerge.deep_merge!(Marshal.load(Marshal.dump(overrides)), base, knockout_prefix: '--'))
+ end
+
+ # The common runner_account shape: a derivable uid (the socket path derives
+ # from it) plus any extra keys. Most contexts need only the uid.
+ def account_with_uid(uid, extra = {})
+ struct_param('runner_account', { 'uid' => uid }.merge(extra))
end
context 'with defaults' do
@@ -433,7 +440,7 @@ def struct_param(name, overrides = {})
end
context 'with runner_account.manage' do
- let(:params) { { 'runner_account' => struct_param('runner_account', 'manage' => true, 'uid' => 4242) } }
+ let(:params) { { 'runner_account' => account_with_uid(4242, 'manage' => true) } }
it { is_expected.to compile.with_all_deps }
it { is_expected.to contain_group('gitlab-runner').with('ensure' => 'present', 'system' => true) }
@@ -461,7 +468,7 @@ def struct_param(name, overrides = {})
# it as the user's primary group instead of a same-named group.
context 'with runner_account.manage and a differently named primary group' do
let(:params) do
- { 'runner_account' => struct_param('runner_account', 'manage' => true, 'uid' => 4242, 'group' => 'ci') }
+ { 'runner_account' => account_with_uid(4242, 'manage' => true, 'group' => 'ci') }
end
it { is_expected.to compile.with_all_deps }
@@ -476,7 +483,7 @@ def struct_param(name, overrides = {})
context 'with rootless_docker.manage' do
let(:params) do
{ 'rootless_docker' => struct_param('rootless_docker', 'manage' => true),
- 'runner_account' => struct_param('runner_account', 'uid' => 4242) }
+ 'runner_account' => account_with_uid(4242) }
end
it { is_expected.to compile.with_all_deps }
@@ -584,7 +591,7 @@ def struct_param(name, overrides = {})
let(:params) do
{ 'rootless_docker' => struct_param('rootless_docker', 'manage' => true,
'subid_start' => 300_000, 'subid_count' => 131_072),
- 'runner_account' => struct_param('runner_account', 'manage' => true, 'uid' => 4242) }
+ 'runner_account' => account_with_uid(4242, 'manage' => true) }
end
it { is_expected.to compile.with_all_deps }
@@ -609,7 +616,7 @@ def struct_param(name, overrides = {})
let(:facts) { UBUNTU_FACTS }
let(:params) do
{
- 'runner_account' => struct_param('runner_account', 'manage' => true, 'uid' => 4242),
+ 'runner_account' => account_with_uid(4242, 'manage' => true),
'rootless_docker' => struct_param('rootless_docker', 'manage' => true),
'runner_service' => struct_param('runner_service', 'manage' => true),
'packages' => struct_param('packages', 'sources' => { 'manage' => true }),
@@ -632,7 +639,7 @@ def struct_param(name, overrides = {})
context 'with runner_service.manage' do
let(:params) do
{ 'runner_service' => struct_param('runner_service', 'manage' => true),
- 'runner_account' => struct_param('runner_account', 'uid' => 4242) }
+ 'runner_account' => account_with_uid(4242) }
end
it { is_expected.to compile.with_all_deps }
@@ -696,8 +703,7 @@ def struct_param(name, overrides = {})
context 'with a non-default runner account name and home' do
let(:params) do
{
- 'runner_account' => struct_param('runner_account',
- 'name' => 'ci-worker', 'uid' => 5000, 'home' => '/srv/ci-worker'),
+ 'runner_account' => account_with_uid(5000, 'name' => 'ci-worker', 'home' => '/srv/ci-worker'),
'runner_service' => struct_param('runner_service', 'manage' => true),
}
end
@@ -733,9 +739,8 @@ def struct_param(name, overrides = {})
# fails to resolve a group that does not exist.
context 'with a differently named primary group' do
let(:params) do
- super().merge('runner_account' => struct_param('runner_account',
- 'name' => 'ci-worker', 'uid' => 5000,
- 'home' => '/srv/ci-worker', 'group' => 'ci'))
+ super().merge('runner_account' => account_with_uid(5000, 'name' => 'ci-worker',
+ 'home' => '/srv/ci-worker', 'group' => 'ci'))
end
it { is_expected.to compile.with_all_deps }
@@ -788,7 +793,7 @@ def struct_param(name, overrides = {})
context 'with standalone.self_update.manage' do
let(:params) do
{ 'standalone' => struct_param('standalone', 'manage' => true, 'self_update' => { 'manage' => true }),
- 'runner_account' => struct_param('runner_account', 'uid' => 4242) }
+ 'runner_account' => account_with_uid(4242) }
end
it { is_expected.to compile.with_all_deps }
@@ -926,7 +931,7 @@ def struct_param(name, overrides = {})
context 'golden file: full two-runner config' do
let(:params) do
{
- 'runner_account' => struct_param('runner_account', 'uid' => 4242),
+ 'runner_account' => account_with_uid(4242),
'runner_tokens' => sensitive({ 'runner_a' => 'glrt-GOLDEN-TOKEN-A',
'runner_b' => 'glrt-GOLDEN-TOKEN-B' }),
# url + executor deliberately live in runner_defaults: the golden file
@@ -978,7 +983,7 @@ def struct_param(name, overrides = {})
context 'rendered shell scripts (golden + shellcheck)' do
let(:params) do
{ 'standalone' => struct_param('standalone', 'manage' => true, 'self_update' => { 'manage' => true }),
- 'runner_account' => struct_param('runner_account', 'uid' => 4242) }
+ 'runner_account' => account_with_uid(4242) }
end
{
@@ -1043,7 +1048,7 @@ def struct_param(name, overrides = {})
'runner_service' => struct_param('runner_service',
'manage' => true,
'environment' => ["DOCKER_HOST=unix:///run/x\nExecStartPre=/bin/evil"]),
- 'runner_account' => struct_param('runner_account', 'uid' => 2000),
+ 'runner_account' => account_with_uid(2000),
}
end
diff --git a/templates/apply.sh.epp b/templates/apply.sh.epp
index 30101d9..9e17974 100644
--- a/templates/apply.sh.epp
+++ b/templates/apply.sh.epp
@@ -1,4 +1,4 @@
-<%- | String $control_repository_path,
+<%- | String $puppetfile_path,
String $manifest_path,
String $module_directory,
String $hiera_config,
@@ -25,9 +25,9 @@ fi
# Deliver module updates pinned in the consumer's Puppetfile each tick.
# Without a Puppetfile (module vendored in the checkout) this is a no-op.
-if [[ -f "<%= $control_repository_path %>/Puppetfile" ]]; then
+if [[ -f "<%= $puppetfile_path %>" ]]; then
r10k puppetfile install \
- --puppetfile "<%= $control_repository_path %>/Puppetfile" \
+ --puppetfile "<%= $puppetfile_path %>" \
--moduledir "<%= $module_directory %>"
fi
diff --git a/templates/healthcheck.sh.epp b/templates/healthcheck.sh.epp
index 6921806..547f395 100644
--- a/templates/healthcheck.sh.epp
+++ b/templates/healthcheck.sh.epp
@@ -3,7 +3,11 @@
String $socket_path,
String $control_repository_path,
String $control_repository_branch,
+ String $service_name,
| -%>
+<%# The manager-service check below asserts the packaged gitlab-runner systemd
+ unit: $service_name is the unit name the gitlab-runner package defines,
+ threaded in from the manifest rather than restated here. Not rendered. -%>
#!/usr/bin/env bash
#
# Managed by Puppet (rootless_gitlab_runner). Do not edit by hand — this file
@@ -30,8 +34,8 @@ as_runner() {
"$@"
}
-systemctl is-active --quiet 'gitlab-runner' \
- || err "manager service 'gitlab-runner' is not active"
+systemctl is-active --quiet '<%= $service_name %>' \
+ || err "manager service '<%= $service_name %>' is not active"
# The apply timer drives self-update; a disabled or stopped timer silently
# halts convergence and would otherwise surface only later as checkout
diff --git a/types/apt_source.pp b/types/apt_source.pp
new file mode 100644
index 0000000..8ba3996
--- /dev/null
+++ b/types/apt_source.pp
@@ -0,0 +1,11 @@
+# @summary An apt source's location and signing-key endpoint.
+#
+# `location` and `key_source` are verbatim `apt::source` parameter names: the
+# repository URL (suite = OS codename) and the URL of its armored signing key,
+# stored as an apt keyring. Both the Docker and GitLab Runner sources under
+# `packages.sources` share this shape, pointing at a vendor repository or a
+# mirror.
+type Rootless_gitlab_runner::Apt_source = Struct[{
+ location => Stdlib::HTTPUrl,
+ key_source => Stdlib::HTTPUrl,
+}]