Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ module follows [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

### Added

- An externally provisioned runner account whose primary group is named differently from the account now converges on the first apply: the new optional `runner_account.group` names the account's primary group and feeds every group ownership the module manages. It defaults to the account name, so existing hosts are unchanged.
- A Hiera data check ships with the module: it fails on any data key that no deployed class declares, instead of Hiera silently ignoring it, and flags subkeys set under a disabled `manage` toggle with a non-failing advisory.

### Changed
Expand Down
18 changes: 13 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -283,16 +283,24 @@ dependencies; the example skeleton carries the lines).

#### `runner_account`

The OS account the runner manager and the rootless daemon run as: `name`, `uid`, and `home`,
with `manage` deciding ownership. The identity keys are read by every concern even when
`manage: false` (socket derivation, file ownership, service `ExecStart`); the toggle only
decides whether the module creates and enforces the group, user, and home. Keep `manage` off
where another configuration-management system owns the account; two owners would fight over
The OS account the runner manager and the rootless daemon run as: `name`, an optional `group`,
`uid`, and `home`, with `manage` deciding ownership. The identity keys are read by every concern
even when `manage: false` (socket derivation, file ownership, service `ExecStart`); the toggle
only decides whether the module creates and enforces the group, user, and home. Keep `manage`
off where another configuration-management system owns the account; two owners would fight over
it. The subordinate UID/GID ranges rootless Docker needs are owned by
[`rootless_docker`](#rootless_docker), not this toggle. Home internals (`.ssh`, `.config`) are
never managed, beyond the no-detach-netns drop-in the module places under
`~/.config/systemd/user/`.

`group` names the account's primary group and defaults to `name`. Set it for an externally
provisioned account whose primary group is named differently (e.g. account `ci-worker`, group `ci`):
it feeds every group ownership the module manages — the group resource and the user's primary
group where `manage` is on, and the group of the runner configuration file, its directory, and
the account's systemd user tree — so the first apply converges instead of failing to resolve a
group that does not exist. Left unset, the account name doubles as the group, which is correct
by construction where the module creates the account.

#### `rootless_docker`

With `rootless_docker.manage` on, the module brings up the rootless-Docker user daemon:
Expand Down
11 changes: 10 additions & 1 deletion REFERENCE.md
Original file line number Diff line number Diff line change
Expand Up @@ -175,7 +175,8 @@ Struct[{
```

The rendered runner configuration file (GitLab Runner's `config.toml`).
Owner and group derive from `runner_account.name`; the mode is fixed 0600
Owner derives from `runner_account.name` and group from
`runner_account.group` (which defaults to the name); the mode is fixed 0600
(the file carries the runner tokens).

Options:
Expand All @@ -190,6 +191,7 @@ Data type:
Struct[{
manage => Boolean,
name => Rootless_gitlab_runner::Username,
group => Optional[Rootless_gitlab_runner::Username],
uid => Optional[Integer[1]],
home => Stdlib::Absolutepath,
}]
Expand All @@ -207,6 +209,13 @@ configuration-management system owns the account. The subordinate UID/GID
ranges rootless docker needs are owned by `rootless_docker.manage`.
Default false.
* **:name** `Rootless_gitlab_runner::Username`: Username of the runner account. Default `gitlab-runner`.
* **:group** `Optional[Rootless_gitlab_runner::Username]`: Name of the account's primary group. Defaults to the account name (the
data layer cannot express that default, so an unset key falls back to
`name` in code). Set it for an externally provisioned account whose primary
group is named differently (e.g. account `ci-worker`, group `ci`): it feeds
every group ownership the module manages — the group resource and the
user's primary group where `manage` is on, and the group of the runner
configuration file, its directory, and the account's systemd user tree.
* **:uid** `Optional[Integer[1]]`: Numeric uid of the runner account. No default: the uid is host data, not
something a module can sensibly invent. It derives the rootless runtime
paths (`/run/user/<uid>`, the docker socket) and is enforced on the user
Expand Down
1 change: 1 addition & 0 deletions data/common.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ rootless_gitlab_runner::configuration_file:
rootless_gitlab_runner::runner_account:
manage: false
name: 'gitlab-runner'
group: ~
uid: ~
home: '/home/gitlab-runner'

Expand Down
13 changes: 8 additions & 5 deletions manifests/config.pp
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@
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.
$runner_group = $rootless_gitlab_runner::runner_group
$runner_home = $rootless_gitlab_runner::runner_account['home']
$configuration_file_path = $rootless_gitlab_runner::configuration_file['path']

Expand Down Expand Up @@ -87,7 +90,7 @@
file { $config_dir:
ensure => directory,
owner => 'root',
group => $runner_name,
group => $runner_group,
mode => '0770',
}

Expand All @@ -104,7 +107,7 @@
file { "${config_dir}/.runner_system_id":
ensure => file,
owner => $runner_name,
group => $runner_name,
group => $runner_group,
mode => '0600',
}
}
Expand All @@ -118,7 +121,7 @@
file { $configuration_file_path:
ensure => file,
owner => $runner_name,
group => $runner_name,
group => $runner_group,
mode => '0600',
content => Sensitive(epp('rootless_gitlab_runner/config.toml.epp', {
'concurrent' => $rootless_gitlab_runner::concurrent,
Expand All @@ -145,7 +148,7 @@
file { [$user_config_dir, $user_systemd_dir, $user_units_dir, $dropin_dir]:
ensure => directory,
owner => $runner_name,
group => $runner_name,
group => $runner_group,
mode => '0755',
}

Expand All @@ -155,7 +158,7 @@
file { $dropin_path:
ensure => file,
owner => $runner_name,
group => $runner_name,
group => $runner_group,
mode => '0644',
source => 'puppet:///modules/rootless_gitlab_runner/no-detach-netns.conf',
require => File[$dropin_dir],
Expand Down
21 changes: 20 additions & 1 deletion manifests/init.pp
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,8 @@
# and never managed. Default `/etc/gitlab-runner-infra`.
# @param configuration_file
# The rendered runner configuration file (GitLab Runner's `config.toml`).
# Owner and group derive from `runner_account.name`; the mode is fixed 0600
# Owner derives from `runner_account.name` and group from
# `runner_account.group` (which defaults to the name); the mode is fixed 0600
# (the file carries the runner tokens).
# @option configuration_file [Stdlib::Absolutepath] :path
# Where the file is written. Default `/etc/gitlab-runner/config.toml`.
Expand All @@ -109,6 +110,14 @@
# Default false.
# @option runner_account [Rootless_gitlab_runner::Username] :name
# Username of the runner account. Default `gitlab-runner`.
# @option runner_account [Optional[Rootless_gitlab_runner::Username]] :group
# Name of the account's primary group. Defaults to the account name (the
# data layer cannot express that default, so an unset key falls back to
# `name` in code). Set it for an externally provisioned account whose primary
# group is named differently (e.g. account `ci-worker`, group `ci`): it feeds
# every group ownership the module manages — the group resource and the
# user's primary group where `manage` is on, and the group of the runner
# configuration file, its directory, and the account's systemd user tree.
# @option runner_account [Optional[Integer[1]]] :uid
# Numeric uid of the runner account. No default: the uid is host data, not
# something a module can sensibly invent. It derives the rootless runtime
Expand Down Expand Up @@ -229,6 +238,7 @@
Struct[{
manage => Boolean,
name => Rootless_gitlab_runner::Username,
group => Optional[Rootless_gitlab_runner::Username],
uid => Optional[Integer[1]],
home => Stdlib::Absolutepath,
}] $runner_account,
Expand Down Expand Up @@ -291,6 +301,15 @@
]))
}

# 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'])

# Defaults merged under every runner entry; keys set on the entry win.
$effective_runners = $runners.map |$r| { $runner_defaults + $r }

Expand Down
10 changes: 6 additions & 4 deletions manifests/user.pp
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,11 @@
$account = $rootless_gitlab_runner::runner_account

if $account['manage'] {
$runner_name = $account['name']
$runner_name = $account['name']
# Primary group, defaulting to the account name (derived in init.pp).
$runner_group = $rootless_gitlab_runner::runner_group

group { $runner_name:
group { $runner_group:
ensure => present,
system => true,
}
Expand All @@ -19,11 +21,11 @@
ensure => present,
system => true,
uid => $account['uid'],
gid => $runner_name,
gid => $runner_group,
home => $account['home'],
managehome => true,
shell => '/bin/bash',
require => Group[$runner_name],
require => Group[$runner_group],
}
}
}
48 changes: 46 additions & 2 deletions spec/classes/rootless_gitlab_runner_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -438,14 +438,15 @@ def struct_param(name, overrides = {})
it { is_expected.to compile.with_all_deps }
it { is_expected.to contain_group('gitlab-runner').with('ensure' => 'present', 'system' => true) }

it 'owns the user with the declared uid and home' do
it 'owns the user with the declared uid and home, primary group defaulting to the name' do
is_expected.to contain_user('gitlab-runner').with(
'ensure' => 'present',
'system' => true,
'uid' => 4242,
'gid' => 'gitlab-runner',
'home' => '/home/gitlab-runner',
'managehome' => true,
)
).that_requires('Group[gitlab-runner]')
end

%w[subuid subgid].each do |f|
Expand All @@ -455,6 +456,23 @@ def struct_param(name, overrides = {})
end
end

# runner_account.group names the account's primary group; unset it defaults
# to the account name. With manage on, the module creates that group and sets
# 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') }
end

it { is_expected.to compile.with_all_deps }

it 'creates the named group and sets it as the user primary group, not the account name' do
is_expected.to contain_group('ci').with('ensure' => 'present', 'system' => true)
is_expected.not_to contain_group('gitlab-runner')
is_expected.to contain_user('gitlab-runner').with('gid' => 'ci').that_requires('Group[ci]')
end
end

context 'with rootless_docker.manage' do
let(:params) do
{ 'rootless_docker' => struct_param('rootless_docker', 'manage' => true),
Expand Down Expand Up @@ -708,6 +726,32 @@ def struct_param(name, overrides = {})
.with_content(%r{^ExecStart=/usr/bin/gitlab-runner run --working-directory /srv/ci-worker --config /etc/gitlab-runner/config\.toml --service gitlab-runner$})
.with_content(%r{^Environment=DOCKER_HOST=unix:///run/user/5000/docker\.sock$})
end

# An externally provisioned account (manage off) whose primary group is
# named differently. Every managed group ownership must derive from the
# group, while owners stay the account name — otherwise the first apply
# 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'))
end

it { is_expected.to compile.with_all_deps }

it 'derives every managed group ownership from the group, owners staying the account name' do
is_expected.to contain_file('/etc/gitlab-runner/config.toml')
.with('owner' => 'ci-worker', 'group' => 'ci')
is_expected.to contain_file('/etc/gitlab-runner').with('group' => 'ci')
is_expected.to contain_file('/etc/gitlab-runner/.runner_system_id')
.with('owner' => 'ci-worker', 'group' => 'ci')
is_expected.to contain_file('/srv/ci-worker/.config/systemd/user')
.with('owner' => 'ci-worker', 'group' => 'ci')
is_expected.to contain_file('/srv/ci-worker/.config/systemd/user/docker.service.d/no-detach-netns.conf')
.with('owner' => 'ci-worker', 'group' => 'ci')
end
end
end

context 'with standalone.manage alone (no self-update loop)' do
Expand Down