Skip to content

Commit

Permalink
Convert to systemd resources (sous-chefs#771)
Browse files Browse the repository at this point in the history
* Convert to systemd resources

Signed-off-by: Robert Detjens <detjensrobert@osuosl.org>

* Manually specify java path

Older version of systemd require the full path to the java binary.
The current method of using node[java] is broken since the java cookbook no longer sets attributes.

Signed-off-by: Robert Detjens <detjensrobert@osuosl.org>

* appease lints

Signed-off-by: Robert Detjens <detjensrobert@osuosl.org>

* Add automatic upgrade and upgrade instructions

Signed-off-by: Robert Detjens <detjensrobert@osuosl.org>

* Exec with /bin/sh to support quotes in args

ExecStart does not fully support shell quoting, unlike Runit. Jenkins arguments
may be expecting shell quoting, so exec via /bin/sh to correctly interpret them

Signed-off-by: Robert Detjens <detjensrobert@osuosl.org>

Co-authored-by: davidsainty <>
  • Loading branch information
detjensrobert authored Jul 19, 2021
1 parent f7eb144 commit 3c66833
Show file tree
Hide file tree
Showing 18 changed files with 217 additions and 196 deletions.
12 changes: 12 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,18 @@ This file is used to list changes made in each version of the jenkins cookbook.

## Unreleased

- Remove runit dependency
- Use systemd units instead of runit services

### Breaking Changes / Deprecations

- `jenkins_jnlp_slave`:
- Renamed `runit_groups` property to `service_groups`
- New service created -- old Runit service will need manual cleanup

- `jenkins::_master_war`:
- New service created -- old Runit service will need manual cleanup

## 8.2.3 - *2021-03-25*

- Cookstyle fixes
Expand Down
28 changes: 7 additions & 21 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,18 +16,14 @@ This cookbook is maintained by the Sous Chefs. The Sous Chefs are a community of

### Platforms

- Debian 7+ (Package installs require 9+ due to dependencies)
- Ubuntu 14.04+ (Package installs require 16.04+ due to dependencies)
- RHEL/CentOS/Scientific/Oracle 6+
- Debian 9+
- Ubuntu 18.04+
- RHEL/CentOS 7+

### Chef

- Chef 13.0+

### Cookbooks

- runit

#### Java cookbook

This cookbook does not install, manage, or manipulate a JDK, as that is outside of the scope of Jenkins. The `package` installation method will automatically pull in a valid Java if one does not exist on Debian. RHEL jenkins packages do not depend on java as there are far too many options for a package to do the right thing. We recommend including the java cookbook on your system which allows for either openJDK or Oracle JDK installations.
Expand All @@ -47,7 +43,7 @@ Documentation and examples are provided inline using YARD. The tests and fixture
The master recipe will create the required directory structure and install jenkins. There are two installation methods, controlled by the `node['jenkins']['master']['install_method']` attribute:

- `package` - Install Jenkins from the official jenkins-ci.org packages
- `war` - Download the latest version of the WAR file and configure it with Runit
- `war` - Download the latest version of the WAR file and configure a systemd service

## Resources

Expand Down Expand Up @@ -456,22 +452,12 @@ end

Depending on the plugin, you may need to restart the Jenkins instance for the plugin to take affect:

Package installation method:

```ruby
jenkins_plugin 'a_complicated_plugin' do
notifies :restart, 'service[jenkins]', :immediately
end
```

War installation method:

```ruby
jenkins_plugin 'a_complicated_plugin' do
notifies :restart, 'runit_service[jenkins]', :immediately
end
```

For advanced users, this resource exposes an `options` attribute that will be passed to the installation command. For more information on the possible values of these options, please consult the documentation for your Jenkins installation.

```ruby
Expand Down Expand Up @@ -504,7 +490,7 @@ jenkins_plugin 'greenballs' do
end
```

**NOTE** You may need to restart Jenkins after changing a plugin. Because this varies on a case-by-case basis (and because everyone chooses to manage their Jenkins infrastructure differently) this LWRP does **NOT** restart Jenkins for you.
**NOTE** You may need to restart Jenkins after changing a plugin. Because this varies on a case-by-case basis (and because everyone chooses to manage their Jenkins infrastructure differently) this resource does **NOT** restart Jenkins for you.

### jenkins_slave

Expand Down Expand Up @@ -562,8 +548,8 @@ jenkins_jnlp_slave 'smoke' do
idle_delay 3
labels ['runner', 'fast']

# User's groups to be configured with the runit service.
runit_groups ['jenkins', 'docker']
# List of groups to run the slave service under
service_groups ['jenkins', 'docker']
end

# Create a slave with a full environment
Expand Down
17 changes: 17 additions & 0 deletions UPGRADING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# Upgrading

## (9.0.0) Runit to Systemd conversion

Version 9.0.0 of this cookbook replaced the Runit services for WAR installation and JNLP slaves with Systemd services.
This replacement is done mostly automatically, but some manual cleanup is needed to completely remove Runit.

What is done automatically:

- stop / disable the old runit service
- remove the old service files in `/etc/service` and `/etc/init.d`
- create / start / enable the new systemd service

What needs to be done manually:

- stop / disable runit manager service (`runit` / `runsvdir-start`)
- uninstall runit
13 changes: 0 additions & 13 deletions attributes/master.rb
Original file line number Diff line number Diff line change
Expand Up @@ -231,19 +231,6 @@
#
master['maxopenfiles'] = 8192

#
# The groups of user under which Jenkins is running. Works for runit only.
#
master['runit']['groups'] = [node['jenkins']['master']['group']]

#
# The timeout passed to the runit cookbook's service resource. Override the
# default timeout of 7 seconds. This option implies verbose.
#
# node.normal['jenkins']['master']['runit']['sv_timeout'] = 60
#
master['runit']['sv_timeout'] = 7

#
# The limits for the Java process running the master server.
# Example to configure the maximum number of open file descriptors:
Expand Down
32 changes: 14 additions & 18 deletions kitchen.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,6 @@ provisioner:
host: localhost
install_method: package
mirror: https://updates.jenkins.io
runit:
sv_timeout: 30
# This is to help slaves to connect to the master
# Sometimes with slow performance the connection is dropped/cut too early
# 30 seconds was too low, so I bumped it to 5 minutes
Expand All @@ -48,11 +46,9 @@ verifier:
name: inspec

suites:
#
# Smoke suite
#
- name: smoke_package_stable
run_list: jenkins_smoke::default
run_list:
- jenkins_smoke::default
attributes:
jenkins:
master:
Expand All @@ -63,7 +59,8 @@ suites:
- windows-2019

- name: smoke_package_current
run_list: jenkins_smoke::default
run_list:
- jenkins_smoke::default
attributes:
jenkins:
master:
Expand All @@ -75,15 +72,13 @@ suites:
- windows-2019

- name: smoke_war_stable
run_list: jenkins_smoke::default
run_list:
- jenkins_smoke::default
attributes:
jenkins:
master:
install_method: war
source: https://updates.jenkins.io/stable/latest/jenkins.war
verifier:
inputs:
service_type: 'runit'

- name: smoke_war_latest
run_list: jenkins_smoke::default
Expand All @@ -92,18 +87,17 @@ suites:
master:
install_method: war
source: https://updates.jenkins.io/latest/jenkins.war
verifier:
inputs:
service_type: 'runit'

#
# Authentication suites
#
- name: authentication_private_key # Tested in smoke suite
run_list: jenkins_authentication::private_key
run_list:
- jenkins_authentication::private_key

- name: authentication_username_password
run_list: jenkins_authentication::username_password
run_list:
- jenkins_authentication::username_password
excludes:
- windows-2012r2
- windows-2016
Expand All @@ -115,6 +109,8 @@ suites:
# As the proxy settings are global
#
- name: jenkins_proxy_config # Tested in smoke suite
run_list: jenkins_proxy::config
run_list:
- jenkins_proxy::config
- name: jenkins_proxy_remove
run_list: jenkins_proxy::remove
run_list:
- jenkins_proxy::remove
26 changes: 26 additions & 0 deletions libraries/helpers.rb
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,32 @@ def jenkins_font_packages
%w(fonts-dejavu-core fontconfig)
end
end

def ulimits_to_systemd(ulimits)
# see https://www.freedesktop.org/software/systemd/man/systemd.exec.html#Process%20Properties

return unless ulimits

mapping = {
t: 'LimitCPU',
f: 'LimitFSIZE',
d: 'LimitDATA',
s: 'LimitSTACK',
c: 'LimitCORE',
m: 'LimitRSS',
n: 'LimitNOFILE',
v: 'LimitAS',
u: 'LimitNPROC',
l: 'LimitMEMLOCK',
x: 'LimitLOCKS',
i: 'LimitSIGPENDING',
q: 'LimitMSGQUEUE',
e: 'LimitNICE',
r: 'LimitRTPRIO',
}

ulimits.map { |k, v| "#{mapping[k.to_sym]}=#{v}" }.join "\n"
end
end
end
end
Expand Down
104 changes: 71 additions & 33 deletions libraries/slave_jnlp.rb
Original file line number Diff line number Diff line change
Expand Up @@ -30,17 +30,19 @@ class Resource::JenkinsJnlpSlave < Resource::JenkinsSlave
actions :create, :delete, :connect, :disconnect, :online, :offline
default_action :create

# Attributes
attribute :group,
kind_of: String,
# Properties
property :group, String,
default: 'jenkins',
regex: Config[:group_valid_regex]
attribute :service_name,
kind_of: String,

property :service_name, String,
default: 'jenkins-slave'
attribute :runit_groups,
kind_of: Array,
default: ['jenkins']

property :service_groups, Array,
default: lazy { [group] }

deprecated_property_alias 'runit_groups', 'service_groups',
'`runit_groups` was renamed to `service_groups` with the move to systemd services'
end
end

Expand Down Expand Up @@ -89,20 +91,76 @@ def load_current_resource
r.backup(false)
r.mode('0755')
r.atomic_update(false)
r.notifies :restart, "runit_service[#{new_resource.service_name}]" unless platform?('windows')
r.notifies :restart, "systemd_unit[#{new_resource.service_name}.service]" unless platform?('windows')
end

# The Windows's specific child class manages it's own service
return if platform?('windows')

include_recipe 'runit'
# disable runit services before starting new service
# TODO: remove in future version

%W(
/etc/init.d/#{new_resource.service_name}
/etc/service/#{new_resource.service_name}
).each do |f|
file f do
action :delete
notifies :stop, "service[#{new_resource.service_name}]", :before
end
end

service_resource
# runit_service = if platform_family?('debian')
# 'runit'
# else
# 'runsvdir-start'
# end
# service runit_service do
# action [:stop, :disable]
# end

exec_string = "#{java} #{new_resource.jvm_options}"
exec_string << " -jar #{slave_jar}" if slave_jar
exec_string << " -secret #{jnlp_secret}" if jnlp_secret
exec_string << " -jnlpUrl #{jnlp_url}"

systemd_unit "#{new_resource.service_name}.service" do
content <<~EOU
#
# Generated by Chef for #{node['fqdn']}
# Changes will be overwritten!
#
[Unit]
Description=Jenkins JNLP Slave (#{new_resource.service_name})
After=network.target
[Service]
Type=simple
User=#{new_resource.user}
Group=#{new_resource.group}
SupplementaryGroups=#{(new_resource.service_groups - [new_resource.group]).join(' ')}
Environment="HOME=#{new_resource.remote_fs}"
Environment="JENKINS_HOME=#{new_resource.remote_fs}"
WorkingDirectory=#{new_resource.remote_fs}
ExecStart=#{exec_string}
[Install]
WantedBy=multi-user.target
EOU
action :create
end

service new_resource.service_name do
action [:enable, :start]
end
end

action :delete do
# Stop and remove the service
service_resource.run_action(:disable)
service "#{new_resource.service_name}" do
action [:disable, :stop]
end

do_delete
end
Expand Down Expand Up @@ -134,7 +192,7 @@ def jnlp_url
#
def jnlp_secret
return @jnlp_secret if @jnlp_secret
json = executor.groovy! <<-EOH.gsub(/^ {8}/, '')
json = executor.groovy! <<~EOH
output = [
secret:jenkins.slaves.JnlpSlaveAgentProtocol.SLAVE_SECRET.mac('#{new_resource.slave_name}')
]
Expand All @@ -155,26 +213,6 @@ def slave_jar_url
@slave_jar_url ||= uri_join(endpoint, 'jnlpJars', 'slave.jar')
end

def service_resource
declare_resource(:runit_service, new_resource.service_name).tap do |r|
# We need to use .tap() to access methods in the provider's scope.
r.cookbook('jenkins')
r.run_template_name('jenkins-slave')
r.log_template_name('jenkins-slave')
r.options(
service_name: new_resource.service_name,
jvm_options: new_resource.jvm_options,
user: new_resource.user,
runit_groups: new_resource.runit_groups,
remote_fs: new_resource.remote_fs,
java_bin: java,
slave_jar: slave_jar,
jnlp_url: jnlp_url,
jnlp_secret: jnlp_secret
)
end
end

#
# The path to the +slave.jar+ on disk (which may or may not exist).
#
Expand Down
1 change: 0 additions & 1 deletion metadata.rb
Original file line number Diff line number Diff line change
Expand Up @@ -16,5 +16,4 @@
supports 'scientific'
supports 'ubuntu'

depends 'runit', '>= 1.7'
depends 'dpkg_autostart'
Loading

0 comments on commit 3c66833

Please sign in to comment.