Skip to content

Commit

Permalink
windows master support
Browse files Browse the repository at this point in the history
Signed-off-by: troyready <[email protected]>
  • Loading branch information
troyready committed Dec 18, 2017
1 parent d87e2c4 commit 04d18a1
Show file tree
Hide file tree
Showing 17 changed files with 258 additions and 45 deletions.
46 changes: 38 additions & 8 deletions .kitchen.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,24 +14,50 @@ provisioner:
name: chef_zero
data_path: test/fixtures/keys
data_bags_path: test/fixtures/data_bags
attributes:
jenkins:
master:
host: localhost
install_method: war
mirror: https://updates.jenkins.io

platforms:
- name: amazonlinux
driver_config:
box: mvbcoding/awslinux
attributes:
jenkins:
master:
install_method: war
- name: centos-6
attributes:
jenkins:
master:
install_method: war
- name: centos-7
attributes:
jenkins:
master:
install_method: war
- name: debian-7
attributes:
jenkins:
master:
install_method: war
- name: debian-8
attributes:
jenkins:
master:
install_method: war
- name: debian-9
attributes:
jenkins:
master:
install_method: war
- name: ubuntu-14.04
attributes:
jenkins:
master:
install_method: war
- name: ubuntu-16.04
attributes:
jenkins:
master:
install_method: war
- name: windows-2012r2
driver:
box: chef/windows-server-2012r2-standard # private box
Expand All @@ -48,8 +74,6 @@ suites:
excludes:
- ubuntu-14.04
- debian-7
- windows-2012r2
- windows-2016

- name: smoke_package_current
run_list: jenkins_server_wrapper::default
Expand All @@ -69,13 +93,19 @@ suites:
master:
install_method: war
source: https://updates.jenkins.io/stable/latest/jenkins.war
excludes:
- windows-2012r2
- windows-2016
- name: smoke_war_latest
run_list: jenkins_server_wrapper::default
attributes:
jenkins:
master:
install_method: war
source: https://updates.jenkins.io/latest/jenkins.war
excludes:
- windows-2012r2
- windows-2016

#
# Authentication suites
Expand Down
5 changes: 4 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,15 @@ Installs and configures Jenkins CI master & node slaves. Resource providers to s
- 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+
- Windows 2008R2+

### Chef

- Chef 12.1+

### Cookbooks

- ark
- compat_resource
- runit

Expand All @@ -37,10 +39,11 @@ Documentation and examples are provided inline using YARD. The tests and fixture

### master

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:
The master recipe will create the required directory structure and install jenkins. There are three 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
- `msi` - Install Jenkins on Windows from the official jenkins-ci.org packages

## Resource/Provider

Expand Down
5 changes: 4 additions & 1 deletion attributes/default.rb
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,9 @@
elsif ENV['JAVA_HOME']
File.join(ENV['JAVA_HOME'], 'bin', 'java')
else
'java'
case node['os']
when 'windows' then 'C:\Program Files (x86)\Jenkins\jre\bin\java.exe'
else 'java'
end
end
end
88 changes: 66 additions & 22 deletions attributes/master.rb
Original file line number Diff line number Diff line change
Expand Up @@ -33,16 +33,28 @@
# node.normal['jenkins']['master']['install_method'] = 'war'
#
master['install_method'] = case node['platform_family']
when 'debian', 'rhel', 'amazon' then 'package'
when 'debian', 'rhel', 'amazon'
'package'
when 'windows'
'msi'
else 'war'
end

#
# Installation options to pass to MSI installer.
#
# node.normal['jenkins']['master']['msi_install_options'] = "JENKINSDIR=\"#{node['jenkins']['master']['home']}\""
#
master['msi_install_options'] = nil

#
# The version of the Jenkins master to install. This can be a specific
# package version (from the yum or apt repo), or the version of the war
# file to download from the Jenkins mirror.
#
master['version'] = nil
master['version'] = case node['os']
when 'windows' then '2.89.2'
end

#
# The "channel" to use, default is stable
Expand All @@ -64,39 +76,62 @@
master['mirror'] = 'https://updates.jenkins.io'

#
# The full URL to the Jenkins WAR file on the remote mirror. This attribute is
# only used in the "war" installation method. This is a compiled attribute
# from the +mirror+ and +version+ attributes, but you can override this
# attribute and specify the full URL path to a remote file for the Jenkins
# war file. If you choose to override this file manually, it is highly
# recommended that you also set the +checksum+ attribute.
# The full URL to the Jenkins WAR/ZIP file on the remote mirror. This
# attribute is only used in the "war" & "msi" installation methods. This is a
# compiled attribute from the +mirror+ and +version+ attributes, but you can
# override this attribute and specify the full URL path to a remote file for
# the Jenkins war/zip file. If you choose to override this file manually, it
# is highly recommended that you also set the +checksum+ attribute.
#
# node.normal['jenkins']['master']['source'] = 'http://fs01.example.com/jenkins.war'
#
# Warning: Setting this attribute will negate/ignore any values for +mirror+
# and +version+.
#
master['source'] = "#{node['jenkins']['master']['mirror']}/"\
"#{node['jenkins']['master']['version'] || node['jenkins']['master']['channel']}/"\
'latest/jenkins.war'
# and (for the "war" installation method) +version+.
#
master['source'] =
case node['os']
when 'windows'
"http://mirrors.jenkins-ci.org/windows-#{node['jenkins']['master']['channel']}/"\
"jenkins-#{node['jenkins']['master']['version']}.zip"
else
"#{node['jenkins']['master']['mirror']}/"\
"#{node['jenkins']['master']['version'] || node['jenkins']['master']['channel']}/"\
'latest/jenkins.war'
end

#
# The checksum of the war file. This is use to verify that the remote war file
# has not been tampered with (such as a MITM attack). If you leave this #
# The checksum of the war or zip file. This is use to verify that the remote
# file has not been tampered with (such as a MITM attack). If you leave this
# attribute set to +nil+, no validation will be performed. If this attribute
# is set to the wrong SHA-256 checksum, the Chef Client run will fail.
#
# node.normal['jenkins']['master']['checksum'] = 'abcd1234...'
#
master['checksum'] = nil
master['checksum'] = case node['os']
when 'windows' then 'b0c65a14d554d2b5b588c3ee8ab69af68334aea1bcfeebefb40c84fa7b6d5526'
end

#
# The list of options to pass to the Java JVM script when using the package
# installer. For example:
# When installing Jenkins via a msi on Windows, this attribute can be used
# to specify the msi's SHA-256 checksum.
#
# node.normal['jenkins']['master']['msi_checksum'] = 'abcd1234...'
#
master['msi_checksum'] = nil

#
# The list of options to pass to the Java JVM script when using the
# package/msi installer. For example:
#
# node.normal['jenkins']['master']['jvm_options'] = '-Xmx256m'
#
master['jvm_options'] = '-Djenkins.install.runSetupWizard=false'
master['jvm_options'] =
case node['os']
when 'windows'
'-Xrs -Xmx256m -Djenkins.install.runSetupWizard=false -Dhudson.lifecycle=hudson.lifecycle.WindowsServiceLifecycle -jar "%BASE%\jenkins.war" --httpPort=8080 --webroot="%BASE%\war"'
else
'-Djenkins.install.runSetupWizard=false'
end

#
# The list of Jenkins arguments to pass to the initialize script. This varies
Expand Down Expand Up @@ -125,13 +160,19 @@
#
# node.normal['jenkins']['master']['user'] = 'root'
#
master['user'] = 'jenkins'
master['user'] = case node['os']
when 'windows' then 'SYSTEM'
else 'jenkins'
end

#
# The group under which Jenkins is running. Jenkins doesn't actually use or
# honor this attribute - it is used for file permission purposes.
#
master['group'] = 'jenkins'
master['group'] = case node['os']
when 'windows' then 'Administrators'
else 'jenkins'
end

#
# Jenkins user/group should be created as `system` accounts for `war` install.
Expand Down Expand Up @@ -180,7 +221,10 @@
# configuration and build artifacts. You should ensure this directory resides
# on a volume with adequate disk space.
#
master['home'] = '/var/lib/jenkins'
master['home'] = case node['os']
when 'windows' then 'C:\Program Files (x86)\Jenkins'
else '/var/lib/jenkins'
end

#
# The directory where Jenkins should write its logfile(s). **This attribute
Expand Down
7 changes: 6 additions & 1 deletion libraries/_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -220,7 +220,12 @@ def convert_blank_values_to_nil(hash)
# the escaped value
#
def escape(value)
Shellwords.escape(value)
case node['os']
when 'windows'
"\"#{value}\""
else
Shellwords.escape(value)
end
end

#
Expand Down
2 changes: 1 addition & 1 deletion libraries/plugin.rb
Original file line number Diff line number Diff line change
Expand Up @@ -317,7 +317,7 @@ def install_plugin_from_url(source_url, plugin_name, plugin_version = nil, opts
# Jenkins that prevents Jenkins from following 302 redirects, so we
# use Chef to download the plugin and then use Jenkins to install it.
# It's a bit backwards, but so is Jenkins.
executor.execute!('install-plugin', escape('file://' + plugin.path), '-name', escape(plugin_name), opts[:cli_opts])
executor.execute!('install-plugin', escape("#{node['os'] == 'windows' ? '' : 'file://'}#{plugin.path}"), '-name', escape(plugin_name), opts[:cli_opts])
end

#
Expand Down
3 changes: 2 additions & 1 deletion metadata.rb
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,11 @@

recipe 'jenkins::master', 'Installs a Jenkins master'

%w(ubuntu debian redhat centos scientific oracle amazon).each do |os|
%w(ubuntu debian redhat centos scientific oracle amazon windows).each do |os|
supports os
end

depends 'ark', '>= 2.2.0'
depends 'runit', '>= 1.7'
depends 'compat_resource', '>= 12.16.3'
depends 'dpkg_autostart'
Expand Down
76 changes: 76 additions & 0 deletions recipes/_master_msi.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
#
# Cookbook Name:: jenkins
# Recipe:: _master_msi
#
# Author: Troy Ready <[email protected]>
#
# Copyright:: 2017, Sturdy Networks
# Copyright:: 2014-2017, Chef Software, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#

if ::File.extname(node['jenkins']['master']['source']) == '.zip'
include_recipe 'ark::default'

cached_jenkins_msi = ::File.join(Chef::Config[:file_cache_path],
"jenkins-#{node['jenkins']['master']['version']}.msi")
jenkins_msi_source = cached_jenkins_msi

unless ::File.exist? cached_jenkins_msi
ark "jenkins-#{node['jenkins']['master']['version']}" do
url node['jenkins']['master']['source']
checksum node['jenkins']['master']['checksum'] if node['jenkins']['master']['checksum']
creates 'jenkins.msi'
path Chef::Config[:file_cache_path]
action :cherry_pick
end
ruby_block 'rename_generic_jenkins_msi_file' do
block do
require 'fileutils'
::FileUtils.mv(::File.join(Chef::Config[:file_cache_path], 'jenkins.msi'), cached_jenkins_msi)
end
end
end
else
jenkins_msi_source = node['jenkins']['master']['source']
end

windows_package "Jenkins #{node['jenkins']['master']['version']}" do
source jenkins_msi_source
checksum node['jenkins']['master']['msi_checksum'] if node['jenkins']['master']['msi_checksum']
options node['jenkins']['master']['msi_install_options'] if node['jenkins']['master']['msi_install_options']
end

service 'jenkins' do
action [:enable, :start]
end

jenkins_service_file = ::File.join(node['jenkins']['master']['home'], 'jenkins.xml')
ruby_block 'update_jenkins_jvm_options' do
block do
# This XML update would be nice to do with rexml, but the quotes in the
# options (e.g. -jar "%BASE%\jenkins.war") get escaped (&quot;) in an
# undesirable way
fe = Chef::Util::FileEdit.new(jenkins_service_file)
fe.search_file_replace(%r{^\s\s<arguments>.*</arguments>$},
" <arguments>#{node['jenkins']['master']['jvm_options']}</arguments>")
fe.write_file
end
not_if do
require 'rexml/document'
jenkinsdoc = ::REXML::Document.new ::File.read(jenkins_service_file)
jenkinsdoc.elements['service'].elements['arguments'].text == node['jenkins']['master']['jvm_options']
end
notifies :restart, 'service[jenkins]', :immediately
end
5 changes: 5 additions & 0 deletions recipes/_master_package.rb
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,11 @@
# limitations under the License.
#

if Chef::Platform.windows?
Chef::Application.fatal! 'Jenkins "package" installation method not '\
'supported on Windows (use "msi" instead)'
end

case node['platform_family']
when 'debian'
package 'apt-transport-https'
Expand Down
Loading

0 comments on commit 04d18a1

Please sign in to comment.