From df133b479bf8b4e7bdfb3c6361e6d2dba41ccdd1 Mon Sep 17 00:00:00 2001 From: Joshua Hoblitt Date: Tue, 24 Mar 2015 18:31:16 -0700 Subject: [PATCH 1/4] add get_num_executors()/set_num_executors() methods to puppet_helper These methods may be used to get/set the number of executors for the master. Eg.: java -jar /usr/lib/jenkins/jenkins-cli.jar -s http://localhost:8080 groovy /usr/lib/jenkins/puppet_helper.groovy get_num_executors java -jar /usr/lib/jenkins/jenkins-cli.jar -s http://localhost:8080 groovy /usr/lib/jenkins/puppet_helper.groovy set_num_executors 42 --- files/puppet_helper.groovy | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/files/puppet_helper.groovy b/files/puppet_helper.groovy index f17040592..77834770b 100644 --- a/files/puppet_helper.groovy +++ b/files/puppet_helper.groovy @@ -256,6 +256,30 @@ class Actions { instance.setAuthorizationStrategy(strategy) instance.setSecurityRealm(realm) } + + //////////////////////// + // get_num_executors + //////////////////////// + /* + * Print the number of executors for the master + */ + void get_num_executors() { + def j = Jenkins.getInstance() + def n = j.getNumExecutors() + out.println(n) + } + + //////////////////////// + // set_num_executors + //////////////////////// + /* + * Set the number of executors for the master + */ + void set_num_executors(String n) { + def j = Jenkins.getInstance() + j.setNumExecutors(n.toInteger()) + j.save() + } } // class Actions /////////////////////////////////////////////////////////////////////////////// From 979cb012c12bc80306a88465f8cd78f43b32f70d Mon Sep 17 00:00:00 2001 From: Joshua Hoblitt Date: Thu, 26 Mar 2015 10:26:35 -0700 Subject: [PATCH 2/4] move Exec['reload-jenkins'] from jenkins::cli -> jenkins::cli::reload Due to resource containment, it was not possible for a consumer of `jenkins::cli` to both have a dependency on `Class['jenkins::cli']` and to notify `Exec['reload-jenkins']`. It is recommended that notifications be instead be sent to `Class['jenkins::cli::reload']` to ease any future refactoring. --- manifests/cli.pp | 10 ---------- manifests/cli/reload.pp | 19 +++++++++++++++++++ manifests/init.pp | 6 ++++-- 3 files changed, 23 insertions(+), 12 deletions(-) create mode 100644 manifests/cli/reload.pp diff --git a/manifests/cli.pp b/manifests/cli.pp index 4edd9060a..ede3ac8d5 100644 --- a/manifests/cli.pp +++ b/manifests/cli.pp @@ -31,16 +31,6 @@ # The jenkins cli command with required parameter(s) $cmd = "java -jar ${jar} -s http://localhost:${port}" - # Reload all Jenkins config from disk (only when notified) - exec { 'reload-jenkins': - command => "${cmd} reload-configuration", - path => ['/bin', '/usr/bin'], - tries => 10, - try_sleep => 2, - refreshonly => true, - require => File[$jar], - } - # Do a safe restart of Jenkins (only when notified) exec { 'safe-restart-jenkins': command => "${cmd} safe-restart && /bin/sleep 10", diff --git a/manifests/cli/reload.pp b/manifests/cli/reload.pp new file mode 100644 index 000000000..cf9593dca --- /dev/null +++ b/manifests/cli/reload.pp @@ -0,0 +1,19 @@ +# Class: jenkins::cli::reload +# +# Command Jenkins to reload config.xml via the CLI. +# +class jenkins::cli::reload { + + if $caller_module_name != $module_name { + fail("Use of private class ${name} by ${caller_module_name}") + } + + # Reload all Jenkins config from disk (only when notified) + exec { 'reload-jenkins': + command => "${::jenkins::cli::cmd} reload-configuration", + path => ['/bin', '/usr/bin'], + tries => 10, + try_sleep => 2, + refreshonly => true, + } +} diff --git a/manifests/init.pp b/manifests/init.pp index a57cc3e2d..eac8645f9 100644 --- a/manifests/init.pp +++ b/manifests/init.pp @@ -199,6 +199,7 @@ if $cli { include jenkins::cli + include jenkins::cli::reload } Anchor['jenkins::begin'] -> @@ -213,8 +214,9 @@ Anchor['jenkins::begin'] -> Class['jenkins::service'] -> Class['jenkins::cli'] -> - Class['jenkins::jobs'] -> - Anchor['jenkins::end'] + Class['jenkins::cli::reload'] -> + Class['jenkins::jobs'] -> + Anchor['jenkins::end'] } if $install_java { From dad38cb8cbe27ac5fe36d365453e16805405a2d2 Mon Sep 17 00:00:00 2001 From: Joshua Hoblitt Date: Wed, 25 Mar 2015 11:07:24 -0700 Subject: [PATCH 3/4] add jenkins::cli::exec defined type --- README.md | 48 +++++++++- manifests/cli/exec.pp | 45 +++++++++ spec/defines/jenkins_cli_exec_spec.rb | 130 ++++++++++++++++++++++++++ spec/spec_helper.rb | 8 ++ 4 files changed, 228 insertions(+), 3 deletions(-) create mode 100644 manifests/cli/exec.pp create mode 100644 spec/defines/jenkins_cli_exec_spec.rb diff --git a/README.md b/README.md index 80a7efd13..f74492904 100644 --- a/README.md +++ b/README.md @@ -127,9 +127,11 @@ the following `require` statement: 2. Config Hash - jennkins::config 3. Configure Firewall - jenkins (init.pp) 4. Outbound Jenkins Proxy Config - jenkins (init.pp) -5. Jenkins Users -6. Credentials -7. Simple security model configuration +5. [CLI Helper](#cli-helper) + * [`exec_cli_helper`](#exec_cli_helper) +6. Jenkins Users +7. Credentials +8. Simple security model configuration ### API-based Resources and Settings (Users, Credentials, security) @@ -191,6 +193,46 @@ security policy are configured in the correct order. For example: touch $DONEFILE +#### `jenkins::cli::exec` + +The defined type `jenkins::cli::exec` may be used to execute arbitrary CLI helper +commands. + +Arguments to the CLI helper script may be specified as the resource's title. + +```puppet + jenkins::cli::exec { 'set_num_executors 0': } +``` + +Or passed as an array to the `command` parameter. This example is +semantically equivalent to the first. + +```puppet + jenkins::cli::exec { 'set_num_executors 0': + command => ['set_num_executors', '0'], + } +``` + +which is also equivalent to: + +```puppet + jenkins::cli::exec { 'set_num_executors 0': + command => 'set_num_executors 0', + } +``` + +If the `unless` parameter is specified, an environment variable named +`$HELPER_CMD` is declared which contains the complete string needed to execute +the CLI helper script (minus arguments). This may be useful in constructing +idempotent `exec` statements. + +```puppet + $num_executors = 0 + jenkins::cli::exec { "set_num_executors ${num_executors}": + unless => "[ \$(\$HELPER_CMD get_num_executors) -eq ${num_executors} ]" + } +``` + #### Users Email and password are required. diff --git a/manifests/cli/exec.pp b/manifests/cli/exec.pp new file mode 100644 index 000000000..887f56209 --- /dev/null +++ b/manifests/cli/exec.pp @@ -0,0 +1,45 @@ +# == Define: jenkins::cli::exec +# +# A defined type for executing custom helper script commands via the Jenkins +# CLI. +# +define jenkins::cli::exec( + $command = $title, + $unless = undef, +) { + if !(is_string($command) or is_array($command)) { + fail('$command is not a string or an Array.') + } + validate_string($unless) + + include ::jenkins + include ::jenkins::cli_helper + include ::jenkins::cli::reload + + # $command may be either a string or an array due to the use of flatten() + $run = join( + delete_undef_values( + flatten([ + $::jenkins::cli_helper::helper_cmd, + $command + ]) + ), + ' ' + ) + + if $unless { + $environment_run = [ "HELPER_CMD=${::jenkins::cli_helper::helper_cmd}" ] + } else { + $environment_run = undef + } + + exec { $title: + provider => 'shell', + command => $run, + environment => $environment_run, + unless => $unless, + tries => $::jenkins::cli_tries, + try_sleep => $::jenkins::cli_try_sleep, + notify => Class['jenkins::cli::reload'], + } +} diff --git a/spec/defines/jenkins_cli_exec_spec.rb b/spec/defines/jenkins_cli_exec_spec.rb new file mode 100644 index 000000000..e9fb51ff3 --- /dev/null +++ b/spec/defines/jenkins_cli_exec_spec.rb @@ -0,0 +1,130 @@ +require 'spec_helper' + +describe 'jenkins::cli::exec', :type => :define do + let(:title) { 'foo' } + + let(:facts) do + { + :osfamily => 'RedHat', + :operatingsystem => 'RedHat', # require by puppetlabs/java + } + end + + let(:helper_cmd) { '/usr/bin/java -jar /usr/lib/jenkins/jenkins-cli.jar -s http://127.0.0.1:8080 groovy /usr/lib/jenkins/puppet_helper.groovy' } + + describe 'title =>' do + context 'foo' do + # default title... + + it do + should contain_exec('foo').with( + :command => "#{helper_cmd} foo", + :tries => 10, + :try_sleep => 10, + :unless => nil, + ) + end + it { should contain_exec('foo').that_notifies('Class[jenkins::cli::reload]') } + end + + context 'bar' do + let(:title) { 'bar' } + + it do + should contain_exec('bar').with( + :command => "#{helper_cmd} bar", + :tries => 10, + :try_sleep => 10, + :unless => nil, + ) + end + it { should contain_exec('bar').that_notifies('Class[jenkins::cli::reload]') } + end + end # title => + + describe 'command =>' do + context 'bar' do + let(:params) {{ :command => 'bar' }} + + it do + should contain_exec('foo').with( + :command => "#{helper_cmd} bar", + :tries => 10, + :try_sleep => 10, + :unless => nil, + ) + end + end + + context "['bar']" do + let(:params) {{ :command => %w{ bar } }} + + it do + should contain_exec('foo').with( + :command => "#{helper_cmd} bar", + :tries => 10, + :try_sleep => 10, + :unless => nil, + ) + end + end + + context "['bar', 'baz']" do + let(:params) {{ :command => %w{bar baz} }} + + it do + should contain_exec('foo').with( + :command => "#{helper_cmd} bar baz", + :tries => 10, + :try_sleep => 10, + :unless => nil, + ) + end + end + + context "['bar', undef, 'baz']" do + let(:params) {{ :command => ['bar', Undef.new, 'baz'] }} + + it 'should remove the undef' do + should contain_exec('foo').with( + :command => "#{helper_cmd} bar baz", + :tries => 10, + :try_sleep => 10, + :unless => nil, + ) + end + end + + context '{}' do + let(:params) {{ :command => {} }} + + it 'should fail' do + should raise_error(Puppet::Error, /is not a string or an Array./) + end + end + end # command => + + describe 'unless =>' do + context 'bar' do + let(:params) {{ :unless => 'bar' }} + + it do + should contain_exec('foo').with( + :command => "#{helper_cmd} foo", + :environment => [ "HELPER_CMD=#{helper_cmd}" ], + :unless => 'bar', + :tries => 10, + :try_sleep => 10, + ) + end + end + + context '{}' do + let(:params) {{ :unless => {} }} + + it 'should fail' do + should raise_error(Puppet::Error, /is not a string./) + end + end + end # unless_cli_helper => +end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 048e3f5e4..45000d9e7 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -16,3 +16,11 @@ c.include(Jenkins::RSpecHelpers) end + +# a simple class to inject :undef +# https://groups.google.com/d/msg/puppet-users/6nL2eROH8is/UDqRNu34lB0J +class Undef + def inspect + 'undef' + end +end From f6ebc2153e708283f74169839d29fe5d47f2980f Mon Sep 17 00:00:00 2001 From: Joshua Hoblitt Date: Wed, 25 Mar 2015 16:32:35 -0700 Subject: [PATCH 4/4] add executors param to jenkins class Similar semantics as `class { 'jenkins::slave': executors => ... }` but for the Jenkins master. --- README.md | 10 ++++++++++ manifests/init.pp | 19 +++++++++++++++++++ spec/acceptance/class_spec.rb | 31 ++++++++++++++++++++++++++++++- spec/classes/jenkins_spec.rb | 29 +++++++++++++++++++++++++++++ 4 files changed, 88 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index f74492904..de7e04904 100644 --- a/README.md +++ b/README.md @@ -18,6 +18,16 @@ puppet module install rtyler/jenkins ``` Then the service should be running at [http://hostname.example.com:8080/](http://hostname.example.com:8080/). +### Jenkin's options + +#### Master Executor Threads + +```puppet +class { 'jenkins': + executors => 0, +} +``` + ### Managing Jenkins jobs diff --git a/manifests/init.pp b/manifests/init.pp index eac8645f9..584aa8dbd 100644 --- a/manifests/init.pp +++ b/manifests/init.pp @@ -44,6 +44,9 @@ # config_hash = undef (Default) # Hash with config options to set in sysconfig/jenkins defaults/jenkins # +# executors = undef (Default) +# Integer number of executors on the Jenkin's master. +# # Example use # # class{ 'jenkins': @@ -140,6 +143,7 @@ $cli_try_sleep = $jenkins::params::cli_try_sleep, $port = $jenkins::params::port, $libdir = $jenkins::params::libdir, + $executors = undef, ) inherits jenkins::params { validate_bool($lts, $install_java, $repo) @@ -153,6 +157,10 @@ validate_array($no_proxy_list) } + if $executors { + validate_integer($executors) + } + anchor {'jenkins::begin':} anchor {'jenkins::end':} @@ -202,6 +210,17 @@ include jenkins::cli::reload } + if $executors { + jenkins::cli::exec { 'set_num_executors': + command => ['set_num_executors', $executors], + unless => "[ \$(\$HELPER_CMD get_num_executors) -eq ${executors} ]" + } + + Class['jenkins::cli'] -> + Jenkins::Cli::Exec['set_num_executors'] -> + Class['jenkins::jobs'] + } + Anchor['jenkins::begin'] -> Class[$jenkins_package_class] -> Class['jenkins::config'] -> diff --git a/spec/acceptance/class_spec.rb b/spec/acceptance/class_spec.rb index 95969d37b..d4af7ed09 100644 --- a/spec/acceptance/class_spec.rb +++ b/spec/acceptance/class_spec.rb @@ -32,4 +32,33 @@ class {'jenkins': end end -end \ No newline at end of file + + context 'executors' do + it 'should work with no errors' do + pp = <<-EOS + class {'jenkins': + executors => 42, + } + EOS + + # Run it twice and test for idempotency + apply_manifest(pp, :catch_failures => true) + apply_manifest(pp, :catch_changes => true) + end + + describe port(8080) do + # jenkins should already have been running so we shouldn't have to + # sleep + it { should be_listening } + end + + describe service('jenkins') do + it { should be_running } + it { should be_enabled } + end + + describe file('/var/lib/jenkins/config.xml') do + it { should contain ' 42' } + end + end # executors +end diff --git a/spec/classes/jenkins_spec.rb b/spec/classes/jenkins_spec.rb index 609c27577..618ce9ab3 100755 --- a/spec/classes/jenkins_spec.rb +++ b/spec/classes/jenkins_spec.rb @@ -64,5 +64,34 @@ let(:pre_condition) { 'define firewall ($action, $state, $dport, $proto) {}' } it { expect { should raise_error(Puppet::Error) } } end + + describe 'executors =>' do + context 'undef' do + it { should_not contain_class('jenkins::cli_helper') } + it { should_not contain_jenkins__cli__exec('set_num_executors') } + end + + context '42' do + let(:params) {{ :executors => 42 }} + + it { should contain_class('jenkins::cli_helper') } + it do + should contain_jenkins__cli__exec('set_num_executors').with( + :command => ['set_num_executors', 42], + :unless => '[ $($HELPER_CMD get_num_executors) -eq 42 ]', + ) + end + it { should contain_jenkins__cli__exec('set_num_executors').that_requires('Class[jenkins::cli]') } + it { should contain_jenkins__cli__exec('set_num_executors').that_comes_before('Class[jenkins::jobs]') } + end + + context '{}' do + let(:params) {{ :executors => {} }} + + it 'should fail' do + should raise_error(Puppet::Error, /to be an Integer/) + end + end + end # executors => end end