Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

run foreman in a container #1164

Draft
wants to merge 4 commits into
base: master
Choose a base branch
from
Draft
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
3 changes: 3 additions & 0 deletions .fixtures.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@ fixtures:
concat: 'https://github.com/puppetlabs/puppetlabs-concat'
cron_core: 'https://github.com/puppetlabs/puppetlabs-cron_core'
extlib: 'https://github.com/voxpupuli/puppet-extlib'
podman:
repo: 'https://github.com/evgeni/puppet-podman'
branch: 'quadlet'
postgresql: 'https://github.com/puppetlabs/puppetlabs-postgresql'
puppet: 'https://github.com/theforeman/puppet-puppet'
redis: 'https://github.com/voxpupuli/puppet-redis'
Expand Down
13 changes: 10 additions & 3 deletions manifests/config.pp
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,7 @@
)
$min_puma_threads = pick($foreman::foreman_service_puma_threads_min, $foreman::foreman_service_puma_threads_max)
systemd::dropin_file { 'foreman-service':
ensure => bool2str($foreman::deployment_mode == 'package', 'present', 'absent'),
filename => 'installer.conf',
unit => "${foreman::foreman_service}.service",
content => template('foreman/foreman.service-overrides.erb'),
Expand Down Expand Up @@ -153,7 +154,13 @@
}

if $foreman::apache {
$listen_socket = '/run/foreman.sock'
if $foreman::deployment_mode == 'container' {
$listen_socket = 'localhost:3000/'
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This one was tricky. Apache (or foreman::config::apache) doesn't add a trailing slash to the backend, which then tries to access things like http://localhost:3000users/login, obviously failing

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can use systemd socket activation with podman containers. I've used that myself with gunicorn to avoid needing to deal with a firewall (and instead deal with SELinux). Any reason you don't use that now?

See https://github.com/containers/podman/blob/main/docs/tutorials/socket_activation.md#socket-activation-of-containers for more info.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh cool. I didn't try that (yet), but I also think that the above is a legit bug in our current deployment if for some reason users do not want sockets.

also lol @ "and instead deal with SELinux" ;)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Quite possible that there's a bug there

$backend_protocol = 'http'
} else {
$listen_socket = '/run/foreman.sock'
$backend_protocol = 'unix'
}

class { 'foreman::config::apache':
app_root => $foreman::app_root,
Expand All @@ -162,7 +169,7 @@
serveraliases => $foreman::serveraliases,
server_port => $foreman::server_port,
server_ssl_port => $foreman::server_ssl_port,
proxy_backend => "unix://${listen_socket}",
proxy_backend => "${backend_protocol}://${listen_socket}",
ssl => $foreman::ssl,
ssl_ca => $foreman::server_ssl_ca,
ssl_chain => $foreman::server_ssl_chain,
Expand Down Expand Up @@ -281,7 +288,7 @@
}

systemd::dropin_file { 'foreman-socket':
ensure => bool2str($foreman_socket_override =~ Undef, 'absent', 'present'),
ensure => bool2str($foreman_socket_override =~ Undef or $foreman::deployment_mode != 'package', 'absent', 'present'),
filename => 'installer.conf',
unit => "${foreman::foreman_service}.socket",
content => $foreman_socket_override,
Expand Down
3 changes: 3 additions & 0 deletions manifests/init.pp
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,8 @@
#
# $provisioning_fcct_location:: The location of the binary to call when transpiling Fedora CoreOS templates.
#
# $deployment_mode:: The way foreman is deployed: packages or container
#
# === Dynflow parameters:
#
# $dynflow_manage_services:: Whether to manage the dynflow services
Expand Down Expand Up @@ -307,6 +309,7 @@
Boolean $register_in_foreman = true,
Optional[Stdlib::Absolutepath] $provisioning_ct_location = undef,
Optional[Stdlib::Absolutepath] $provisioning_fcct_location = undef,
Enum['package', 'container'] $deployment_mode = 'package',
) inherits foreman::params {
assert_type(Array[Stdlib::IP::Address], $trusted_proxies)

Expand Down
36 changes: 34 additions & 2 deletions manifests/service.pp
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@
Enum['present', 'absent'] $dynflow_orchestrator_ensure = $foreman::dynflow_orchestrator_ensure,
Integer[0] $dynflow_worker_instances = $foreman::dynflow_worker_instances,
Integer[0] $dynflow_worker_concurrency = $foreman::dynflow_worker_concurrency,
Enum['package', 'container'] $deployment_mode = $foreman::deployment_mode,
String[1] $container_image = 'quay.io/evgeni/foreman-rpm:latest',
) {
if $dynflow_manage_services {
foreman::dynflow::worker { 'orchestrator':
Expand All @@ -36,13 +38,43 @@
}

service { "${foreman_service}.socket":
ensure => $foreman_service_ensure,
enable => $foreman_service_enable,
ensure => bool2str($deployment_mode == 'package', $foreman_service_ensure, 'stopped'),
enable => $foreman_service_enable and $deployment_mode == 'package',
}

service { $foreman_service:
ensure => $foreman_service_ensure,
enable => $foreman_service_enable,
before => Service["${foreman_service}.socket"],
}

if $deployment_mode == 'container' {
file { '/etc/containers/systemd':
ensure => directory,
}
File['/etc/containers/systemd/foreman.container'] ~> Service[$foreman_service]
Systemd::Daemon_reload['foreman.container'] ~> Service[$foreman_service]
}

podman::quadlet { 'foreman.container':
ensure => bool2str($deployment_mode == 'container', 'present', 'absent'),
unit_entry => {
'Description' => 'Foreman',
},
service_entry => {
'TimeoutStartSec' => '900',
},
container_entry => {
'Image' => $container_image,
'Volume' => ['/etc/foreman/:/etc/foreman/'],
'AddCapability' => ['CAP_DAC_OVERRIDE', 'CAP_IPC_OWNER'],
'Network' => 'host',
'HostName' => $foreman::servername,
'Notify' => true,
},
install_entry => {
'WantedBy' => 'default.target',
},
# don't set active true here, it makes podman::quadlet create a service that clashes with ours
}
}
4 changes: 4 additions & 0 deletions metadata.json
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,10 @@
{
"name": "puppet/redis",
"version_requirement": ">= 5.0.0 < 12.0.0"
},
{
"name": "southalc/podman",
"version_requirement": ">= 0.6.7 < 1.0.0"
}
],
"requirements": [
Expand Down
20 changes: 20 additions & 0 deletions spec/acceptance/foreman_basic_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -55,4 +55,24 @@ class { 'foreman':

it_behaves_like 'the foreman application', { expected_login_url_path: '/users/extlogin' }
end

# needs to happen after GSSAPI, something is wrong with its cleanup
context 'in a Container' do
before(:context) { purge_foreman }
describe 'in a Container' do
it_behaves_like 'an idempotent resource' do
let(:manifest) do
<<~PUPPET
class { 'foreman':
deployment_mode => 'container',
db_host => 'localhost',
db_manage_rake => false,
Comment on lines +68 to +69
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder if these two could be made more "dynamic"?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What do you mean? That db_manage_rake would be undef by default and the deployment mode determines the value?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah, same for host (there is not really a socket in the container)

}
PUPPET
end
end

it_behaves_like 'the foreman application', { deployment_mode: 'container' }
end
end
end
6 changes: 4 additions & 2 deletions spec/support/acceptance/examples.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
].each do |service_name|
describe service(service_name) do
it { is_expected.to be_enabled }
it { is_expected.to be_running }

Check failure on line 10 in spec/support/acceptance/examples.rb

View workflow job for this annotation

GitHub Actions / Puppet / Puppet 8 - Debian 12

Foreman in a Container in a Container behaves like the foreman application Service "foreman" is expected to be running Failure/Error: it { is_expected.to be_running } expected Service "foreman" to be running Shared Example Group: "the foreman application" called from ./spec/acceptance/foreman_basic_spec.rb:75

Check failure on line 10 in spec/support/acceptance/examples.rb

View workflow job for this annotation

GitHub Actions / Puppet / Puppet 7 - Ubuntu 22.04

Foreman in a Container in a Container behaves like the foreman application Service "foreman" is expected to be running Failure/Error: it { is_expected.to be_running } expected Service "foreman" to be running Shared Example Group: "the foreman application" called from ./spec/acceptance/foreman_basic_spec.rb:75

Check failure on line 10 in spec/support/acceptance/examples.rb

View workflow job for this annotation

GitHub Actions / Puppet / Puppet 7 - Debian 12

Foreman in a Container in a Container behaves like the foreman application Service "foreman" is expected to be running Failure/Error: it { is_expected.to be_running } expected Service "foreman" to be running Shared Example Group: "the foreman application" called from ./spec/acceptance/foreman_basic_spec.rb:75

Check failure on line 10 in spec/support/acceptance/examples.rb

View workflow job for this annotation

GitHub Actions / Puppet / Puppet 8 - Ubuntu 22.04

Foreman in a Container in a Container behaves like the foreman application Service "foreman" is expected to be running Failure/Error: it { is_expected.to be_running } expected Service "foreman" to be running Shared Example Group: "the foreman application" called from ./spec/acceptance/foreman_basic_spec.rb:75
end
end

Expand All @@ -19,12 +19,14 @@
it { is_expected.to be_listening }
end

describe file('/run/foreman.sock') do
it { should be_socket }
if params.fetch(:deployment_mode, 'package') == 'package'
describe file('/run/foreman.sock') do
it { should be_socket }
end
end

describe command("curl -s --cacert /etc/foreman-certs/certificate.pem https://#{host_inventory['fqdn']} -w '\%{redirect_url}' -o /dev/null") do
its(:stdout) { is_expected.to eq("https://#{host_inventory['fqdn']}#{params.fetch(:expected_login_url_path, '/users/login')}") }

Check failure on line 29 in spec/support/acceptance/examples.rb

View workflow job for this annotation

GitHub Actions / Puppet / Puppet 8 - Debian 12

Foreman in a Container in a Container behaves like the foreman application Command "curl -s --cacert /etc/foreman-certs/certificate.pem https://debian12-64-puppet8.example.com -w '%{redirect_url}' -o /dev/null" stdout is expected to eq "https://debian12-64-puppet8.example.com/users/login" Failure/Error: its(:stdout) { is_expected.to eq("https://#{host_inventory['fqdn']}#{params.fetch(:expected_login_url_path, '/users/login')}") } expected: #<Encoding:UTF-8> "https://debian12-64-puppet8.example.com/users/login" got: #<Encoding:US-ASCII> "" (compared using ==) Shared Example Group: "the foreman application" called from ./spec/acceptance/foreman_basic_spec.rb:75

Check failure on line 29 in spec/support/acceptance/examples.rb

View workflow job for this annotation

GitHub Actions / Puppet / Puppet 7 - Ubuntu 22.04

Foreman in a Container in a Container behaves like the foreman application Command "curl -s --cacert /etc/foreman-certs/certificate.pem https://ubuntu2204-64-puppet7.example.com -w '%{redirect_url}' -o /dev/null" stdout is expected to eq "https://ubuntu2204-64-puppet7.example.com/users/login" Failure/Error: its(:stdout) { is_expected.to eq("https://#{host_inventory['fqdn']}#{params.fetch(:expected_login_url_path, '/users/login')}") } expected: #<Encoding:UTF-8> "https://ubuntu2204-64-puppet7.example.com/users/login" got: #<Encoding:US-ASCII> "" (compared using ==) Shared Example Group: "the foreman application" called from ./spec/acceptance/foreman_basic_spec.rb:75

Check failure on line 29 in spec/support/acceptance/examples.rb

View workflow job for this annotation

GitHub Actions / Puppet / Puppet 7 - Debian 12

Foreman in a Container in a Container behaves like the foreman application Command "curl -s --cacert /etc/foreman-certs/certificate.pem https://debian12-64-puppet7.example.com -w '%{redirect_url}' -o /dev/null" stdout is expected to eq "https://debian12-64-puppet7.example.com/users/login" Failure/Error: its(:stdout) { is_expected.to eq("https://#{host_inventory['fqdn']}#{params.fetch(:expected_login_url_path, '/users/login')}") } expected: #<Encoding:UTF-8> "https://debian12-64-puppet7.example.com/users/login" got: #<Encoding:US-ASCII> "" (compared using ==) Shared Example Group: "the foreman application" called from ./spec/acceptance/foreman_basic_spec.rb:75

Check failure on line 29 in spec/support/acceptance/examples.rb

View workflow job for this annotation

GitHub Actions / Puppet / Puppet 8 - Ubuntu 22.04

Foreman in a Container in a Container behaves like the foreman application Command "curl -s --cacert /etc/foreman-certs/certificate.pem https://ubuntu2204-64-puppet8.example.com -w '%{redirect_url}' -o /dev/null" stdout is expected to eq "https://ubuntu2204-64-puppet8.example.com/users/login" Failure/Error: its(:stdout) { is_expected.to eq("https://#{host_inventory['fqdn']}#{params.fetch(:expected_login_url_path, '/users/login')}") } expected: #<Encoding:UTF-8> "https://ubuntu2204-64-puppet8.example.com/users/login" got: #<Encoding:US-ASCII> "" (compared using ==) Shared Example Group: "the foreman application" called from ./spec/acceptance/foreman_basic_spec.rb:75
its(:exit_status) { is_expected.to eq 0 }
end
end
Expand Down
1 change: 1 addition & 0 deletions spec/support/acceptance/purge.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ def purge_foreman
on default, 'apt-get purge -y foreman*', { :acceptable_exit_codes => [0, 100] }
on default, 'apt-get purge -y ruby-hammer-cli-*', { :acceptable_exit_codes => [0, 100] }
end
on default, 'rm -rf /etc/systemd/system/foreman* /etc/containers/systemd/foreman*'

apache_service_name = ['debian', 'ubuntu'].include?(os[:family]) ? 'apache2' : 'httpd'
on default, "systemctl stop #{apache_service_name}", { :acceptable_exit_codes => [0, 5] }
Expand Down
Loading