beaker-pe 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,8 @@
1
+ HOSTS:
2
+ ubuntu-server-1404-x64:
3
+ roles:
4
+ - master
5
+ platform: ubuntu-14.04-amd64
6
+ box: puppetlabs/ubuntu-14.04-64-nocm
7
+ box_url: https://vagrantcloud.com/puppetlabs/boxes/ubuntu-14.04-64-nocm
8
+ hypervisor: vagrant
@@ -0,0 +1,14 @@
1
+
2
+ # Acceptance level testing goes into files in the tests directory like this one,
3
+ # Each file corresponding to a new test made up of individual testing steps
4
+ test_name "Template Acceptance Test Example"
5
+
6
+ # At this point, acceptance testing of beaker-pe has been left in beaker itself,
7
+ # since that requires no work at this point since beaker includes this.
8
+ # TODO note that when things change for Beaker 3.0, testing will have to be
9
+ # changed to match a different pattern, where the `install_pe` acceptance
10
+ # testing will all be done in this library's testing.
11
+
12
+ # step "Fail fast!"
13
+
14
+ # fail_test("There are no acceptance tests yet!")
data/beaker-pe.gemspec ADDED
@@ -0,0 +1,37 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $LOAD_PATH.unshift File.expand_path("../lib", __FILE__)
3
+ require 'beaker-pe/version'
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "beaker-pe"
7
+ s.version = Beaker::DSL::PE::Version::STRING
8
+ s.authors = ["Puppetlabs"]
9
+ s.email = ["qe-team@puppetlabs.com"]
10
+ s.homepage = "https://github.com/puppetlabs/beaker-pe"
11
+ s.summary = %q{Beaker PE DSL Helpers!}
12
+ s.description = %q{Puppet Enterprise (PE) Install & Helper library}
13
+ s.license = 'Apache2'
14
+
15
+ s.files = `git ls-files`.split("\n")
16
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
17
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
18
+ s.require_paths = ["lib"]
19
+
20
+ # Testing dependencies
21
+ s.add_development_dependency 'rspec', '~> 3.0'
22
+ s.add_development_dependency 'rspec-its'
23
+ s.add_development_dependency 'fakefs', '~> 0.6'
24
+ s.add_development_dependency 'rake', '~> 10.1'
25
+ s.add_development_dependency 'simplecov'
26
+ s.add_development_dependency 'pry', '~> 0.10'
27
+
28
+ # Documentation dependencies
29
+ s.add_development_dependency 'yard'
30
+ s.add_development_dependency 'markdown'
31
+ s.add_development_dependency 'thin'
32
+
33
+ # Run time dependencies
34
+ s.add_runtime_dependency 'stringify-hash', '~> 0.0.0'
35
+
36
+ end
37
+
@@ -0,0 +1,32 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'rubygems' unless defined?(Gem)
4
+ require 'beaker-template'
5
+
6
+ VERSION_STRING =
7
+ "
8
+ _ .--.
9
+ ( ` )
10
+ beaker-template .-' `--,
11
+ _..----.. ( )`-.
12
+ .'_|` _|` _|( .__, )
13
+ /_| _| _| _( (_, .-'
14
+ ;| _| _| _| '-'__,--'`--'
15
+ | _| _| _| _| |
16
+ _ || _| _| _| _| %s
17
+ _( `--.\\_| _| _| _|/
18
+ .-' )--,| _| _|.`
19
+ (__, (_ ) )_| _| /
20
+ `-.__.\\ _,--'\\|__|__/
21
+ ;____;
22
+ \\YT/
23
+ ||
24
+ |\"\"|
25
+ '=='
26
+ "
27
+
28
+
29
+
30
+ puts VERSION_STRING % [Beaker::DSL::Helpers::Template::Version::STRING]
31
+
32
+ exit 0
data/lib/beaker-pe.rb ADDED
@@ -0,0 +1,25 @@
1
+ require 'stringify-hash'
2
+ require 'beaker-pe/version'
3
+ require 'beaker-pe/install/pe_utils'
4
+ require 'beaker-pe/options/pe_version_scraper'
5
+
6
+ module Beaker
7
+ module DSL
8
+ module PE
9
+ include Beaker::DSL::InstallUtils::PEUtils
10
+ include Beaker::Options::PEVersionScraper
11
+ end
12
+ end
13
+ end
14
+
15
+
16
+ # This is commented out because Beaker is going to include this
17
+ # library itself until version 3.0.
18
+ # # Boilerplate DSL inclusion mechanism:
19
+ # # First we register our module with the Beaker DSL
20
+ # Beaker::DSL.register( Beaker::DSL::PE )
21
+ # # Then we have to re-include our amended DSL in the TestCase,
22
+ # # because in general, the DSL is included in TestCase far
23
+ # # before test files are executed, so our amendments wouldn't
24
+ # # come through otherwise
25
+ # include Beaker::DSL
@@ -0,0 +1,728 @@
1
+ [ 'aio_defaults', 'pe_defaults', 'puppet_utils', 'windows_utils' ].each do |lib|
2
+ require "beaker/dsl/install_utils/#{lib}"
3
+ end
4
+ require "beaker-answers"
5
+ module Beaker
6
+ module DSL
7
+ module InstallUtils
8
+ #
9
+ # This module contains methods to help installing/upgrading PE builds - including Higgs installs
10
+ #
11
+ # To mix this is into a class you need the following:
12
+ # * a method *hosts* that yields any hosts implementing
13
+ # {Beaker::Host}'s interface to act upon.
14
+ # * a method *options* that provides an options hash, see {Beaker::Options::OptionsHash}
15
+ # * the module {Beaker::DSL::Roles} that provides access to the various hosts implementing
16
+ # {Beaker::Host}'s interface to act upon
17
+ # * the module {Beaker::DSL::Wrappers} the provides convenience methods for {Beaker::DSL::Command} creation
18
+ module PEUtils
19
+ include AIODefaults
20
+ include PEDefaults
21
+ include PuppetUtils
22
+ include WindowsUtils
23
+
24
+ # @!macro [new] common_opts
25
+ # @param [Hash{Symbol=>String}] opts Options to alter execution.
26
+ # @option opts [Boolean] :silent (false) Do not produce log output
27
+ # @option opts [Array<Fixnum>] :acceptable_exit_codes ([0]) An array
28
+ # (or range) of integer exit codes that should be considered
29
+ # acceptable. An error will be thrown if the exit code does not
30
+ # match one of the values in this list.
31
+ # @option opts [Boolean] :accept_all_exit_codes (false) Consider all
32
+ # exit codes as passing.
33
+ # @option opts [Boolean] :dry_run (false) Do not actually execute any
34
+ # commands on the SUT
35
+ # @option opts [String] :stdin (nil) Input to be provided during command
36
+ # execution on the SUT.
37
+ # @option opts [Boolean] :pty (false) Execute this command in a pseudoterminal.
38
+ # @option opts [Boolean] :expect_connection_failure (false) Expect this command
39
+ # to result in a connection failure, reconnect and continue execution.
40
+ # @option opts [Hash{String=>String}] :environment ({}) These will be
41
+ # treated as extra environment variables that should be set before
42
+ # running the command.
43
+
44
+ #Sort array of hosts so that it has the correct order for PE installation based upon each host's role
45
+ #@param subset [Array<Host>] An array of hosts to sort, defaults to global 'hosts' object
46
+ # @example
47
+ # h = sorted_hosts
48
+ #
49
+ # @note Order for installation should be
50
+ # First : master
51
+ # Second: database host (if not same as master)
52
+ # Third: dashboard (if not same as master or database)
53
+ # Fourth: everything else
54
+ #
55
+ # @!visibility private
56
+ def sorted_hosts subset = hosts
57
+ special_nodes = []
58
+ [master, database, dashboard].uniq.each do |host|
59
+ special_nodes << host if host != nil && subset.include?(host)
60
+ end
61
+ real_agents = agents - special_nodes
62
+ real_agents = real_agents.delete_if{ |host| !subset.include?(host) }
63
+ special_nodes + real_agents
64
+ end
65
+
66
+ #Create the PE install command string based upon the host and options settings
67
+ # @param [Host] host The host that PE is to be installed on
68
+ # For UNIX machines using the full PE installer, the host object must have the 'pe_installer' field set correctly.
69
+ # @param [Hash{Symbol=>String}] opts The options
70
+ # @option opts [String] :pe_ver Default PE version to install or upgrade to
71
+ # (Otherwise uses individual hosts pe_ver)
72
+ # @option opts [Boolean] :pe_debug (false) Should we run the installer in debug mode?
73
+ # @example
74
+ # on host, "#{installer_cmd(host, opts)} -a #{host['working_dir']}/answers"
75
+ # @api private
76
+ def installer_cmd(host, opts)
77
+ version = host['pe_ver'] || opts[:pe_ver]
78
+ # Frictionless install didn't exist pre-3.2.0, so in that case we fall
79
+ # through and do a regular install.
80
+ if host['roles'].include? 'frictionless' and ! version_is_less(version, '3.2.0')
81
+ # PE 3.4 introduced the ability to pass in config options to the bash script in the form
82
+ # of <section>:<key>=<value>
83
+ frictionless_install_opts = []
84
+ if host.has_key?('frictionless_options') and ! version_is_less(version, '3.4.0')
85
+ # since we have options to pass in, we need to tell the bash script
86
+ host['frictionless_options'].each do |section, settings|
87
+ settings.each do |key, value|
88
+ frictionless_install_opts << "#{section}:#{key}=#{value}"
89
+ end
90
+ end
91
+ end
92
+
93
+ pe_debug = host[:pe_debug] || opts[:pe_debug] ? ' -x' : ''
94
+ if host['platform'] =~ /aix/ then
95
+ curl_opts = '--tlsv1 -O'
96
+ else
97
+ curl_opts = '--tlsv1 -kO'
98
+ end
99
+ "cd #{host['working_dir']} && curl #{curl_opts} https://#{master}:8140/packages/#{version}/install.bash && bash#{pe_debug} install.bash #{frictionless_install_opts.join(' ')}".strip
100
+ elsif host['platform'] =~ /osx/
101
+ version = host['pe_ver'] || opts[:pe_ver]
102
+ pe_debug = host[:pe_debug] || opts[:pe_debug] ? ' -verboseR' : ''
103
+ "cd #{host['working_dir']} && hdiutil attach #{host['dist']}.dmg && installer#{pe_debug} -pkg /Volumes/puppet-enterprise-#{version}/puppet-enterprise-installer-#{version}.pkg -target /"
104
+ elsif host['platform'] =~ /eos/
105
+ host.install_from_file("puppet-enterprise-#{version}-#{host['platform']}.swix")
106
+ else
107
+ pe_debug = host[:pe_debug] || opts[:pe_debug] ? ' -D' : ''
108
+ "cd #{host['working_dir']}/#{host['dist']} && ./#{host['pe_installer']}#{pe_debug} -a #{host['working_dir']}/answers"
109
+ end
110
+ end
111
+
112
+ #Determine the PE package to download/upload on a mac host, download/upload that package onto the host.
113
+ # Assumed file name format: puppet-enterprise-3.3.0-rc1-559-g97f0833-osx-10.9-x86_64.dmg.
114
+ # @param [Host] host The mac host to download/upload and unpack PE onto
115
+ # @param [Hash{Symbol=>Symbol, String}] opts The options
116
+ # @option opts [String] :pe_dir Default directory or URL to pull PE package from
117
+ # (Otherwise uses individual hosts pe_dir)
118
+ # @option opts [Boolean] :fetch_local_then_push_to_host determines whether
119
+ # you use Beaker as the middleman for this (true), or curl the
120
+ # file from the host (false; default behavior)
121
+ # @api private
122
+ def fetch_pe_on_mac(host, opts)
123
+ path = host['pe_dir'] || opts[:pe_dir]
124
+ local = File.directory?(path)
125
+ filename = "#{host['dist']}"
126
+ extension = ".dmg"
127
+ if local
128
+ if not File.exists?("#{path}/#{filename}#{extension}")
129
+ raise "attempting installation on #{host}, #{path}/#{filename}#{extension} does not exist"
130
+ end
131
+ scp_to host, "#{path}/#{filename}#{extension}", "#{host['working_dir']}/#{filename}#{extension}"
132
+ else
133
+ if not link_exists?("#{path}/#{filename}#{extension}")
134
+ raise "attempting installation on #{host}, #{path}/#{filename}#{extension} does not exist"
135
+ end
136
+ if opts[:fetch_local_then_push_to_host]
137
+ fetch_and_push_pe(host, path, filename, extension)
138
+ else
139
+ on host, "cd #{host['working_dir']}; curl -O #{path}/#{filename}#{extension}"
140
+ end
141
+ end
142
+ end
143
+
144
+ #Determine the PE package to download/upload on a windows host, download/upload that package onto the host.
145
+ #Assumed file name format: puppet-enterprise-3.3.0-rc1-559-g97f0833.msi
146
+ # @param [Host] host The windows host to download/upload and unpack PE onto
147
+ # @param [Hash{Symbol=>Symbol, String}] opts The options
148
+ # @option opts [String] :pe_dir Default directory or URL to pull PE package from
149
+ # (Otherwise uses individual hosts pe_dir)
150
+ # @option opts [String] :pe_ver_win Default PE version to install or upgrade to
151
+ # (Otherwise uses individual hosts pe_ver)
152
+ # @option opts [Boolean] :fetch_local_then_push_to_host determines whether
153
+ # you use Beaker as the middleman for this (true), or curl the
154
+ # file from the host (false; default behavior)
155
+ # @api private
156
+ def fetch_pe_on_windows(host, opts)
157
+ path = host['pe_dir'] || opts[:pe_dir]
158
+ local = File.directory?(path)
159
+ version = host['pe_ver'] || opts[:pe_ver_win]
160
+ filename = "#{host['dist']}"
161
+ extension = ".msi"
162
+ if local
163
+ if not File.exists?("#{path}/#{filename}#{extension}")
164
+ raise "attempting installation on #{host}, #{path}/#{filename}#{extension} does not exist"
165
+ end
166
+ scp_to host, "#{path}/#{filename}#{extension}", "#{host['working_dir']}/#{filename}#{extension}"
167
+ else
168
+ if not link_exists?("#{path}/#{filename}#{extension}")
169
+ raise "attempting installation on #{host}, #{path}/#{filename}#{extension} does not exist"
170
+ end
171
+ if opts[:fetch_local_then_push_to_host]
172
+ fetch_and_push_pe(host, path, filename, extension)
173
+ on host, "cd #{host['working_dir']}; chmod 644 #{filename}#{extension}"
174
+ elsif host.is_cygwin?
175
+ on host, "cd #{host['working_dir']}; curl -O #{path}/#{filename}#{extension}"
176
+ else
177
+ on host, powershell("$webclient = New-Object System.Net.WebClient; $webclient.DownloadFile('#{path}/#{filename}#{extension}','#{host['working_dir']}\\#{filename}#{extension}')")
178
+ end
179
+ end
180
+ end
181
+
182
+ #Determine the PE package to download/upload on a unix style host, download/upload that package onto the host
183
+ #and unpack it.
184
+ # @param [Host] host The unix style host to download/upload and unpack PE onto
185
+ # @param [Hash{Symbol=>Symbol, String}] opts The options
186
+ # @option opts [String] :pe_dir Default directory or URL to pull PE package from
187
+ # (Otherwise uses individual hosts pe_dir)
188
+ # @option opts [Boolean] :fetch_local_then_push_to_host determines whether
189
+ # you use Beaker as the middleman for this (true), or curl the
190
+ # file from the host (false; default behavior)
191
+ # @api private
192
+ def fetch_pe_on_unix(host, opts)
193
+ path = host['pe_dir'] || opts[:pe_dir]
194
+ local = File.directory?(path)
195
+ filename = "#{host['dist']}"
196
+ if local
197
+ extension = File.exists?("#{path}/#{filename}.tar.gz") ? ".tar.gz" : ".tar"
198
+ if not File.exists?("#{path}/#{filename}#{extension}")
199
+ raise "attempting installation on #{host}, #{path}/#{filename}#{extension} does not exist"
200
+ end
201
+ scp_to host, "#{path}/#{filename}#{extension}", "#{host['working_dir']}/#{filename}#{extension}"
202
+ if extension =~ /gz/
203
+ on host, "cd #{host['working_dir']}; gunzip #{filename}#{extension}"
204
+ end
205
+ if extension =~ /tar/
206
+ on host, "cd #{host['working_dir']}; tar -xvf #{filename}.tar"
207
+ end
208
+ else
209
+ if host['platform'] =~ /eos/
210
+ extension = '.swix'
211
+ else
212
+ extension = link_exists?("#{path}/#{filename}.tar.gz") ? ".tar.gz" : ".tar"
213
+ end
214
+ if not link_exists?("#{path}/#{filename}#{extension}")
215
+ raise "attempting installation on #{host}, #{path}/#{filename}#{extension} does not exist"
216
+ end
217
+
218
+ if host['platform'] =~ /eos/
219
+ host.get_remote_file("#{path}/#{filename}#{extension}")
220
+ else
221
+ unpack = 'tar -xvf -'
222
+ unpack = extension =~ /gz/ ? 'gunzip | ' + unpack : unpack
223
+ if opts[:fetch_local_then_push_to_host]
224
+ fetch_and_push_pe(host, path, filename, extension)
225
+ command_file_push = 'cat '
226
+ else
227
+ command_file_push = "curl #{path}/"
228
+ end
229
+ on host, "cd #{host['working_dir']}; #{command_file_push}#{filename}#{extension} | #{unpack}"
230
+
231
+ end
232
+ end
233
+ end
234
+
235
+ #Determine the PE package to download/upload per-host, download/upload that package onto the host
236
+ #and unpack it.
237
+ # @param [Array<Host>] hosts The hosts to download/upload and unpack PE onto
238
+ # @param [Hash{Symbol=>Symbol, String}] opts The options
239
+ # @option opts [String] :pe_dir Default directory or URL to pull PE package from
240
+ # (Otherwise uses individual hosts pe_dir)
241
+ # @option opts [String] :pe_ver Default PE version to install or upgrade to
242
+ # (Otherwise uses individual hosts pe_ver)
243
+ # @option opts [String] :pe_ver_win Default PE version to install or upgrade to on Windows hosts
244
+ # (Otherwise uses individual Windows hosts pe_ver)
245
+ # @option opts [Boolean] :fetch_local_then_push_to_host determines whether
246
+ # you use Beaker as the middleman for this (true), or curl the
247
+ # file from the host (false; default behavior)
248
+ # @api private
249
+ def fetch_pe(hosts, opts)
250
+ hosts.each do |host|
251
+ # We install Puppet from the master for frictionless installs, so we don't need to *fetch* anything
252
+ next if host['roles'].include?('frictionless') && (! version_is_less(opts[:pe_ver] || host['pe_ver'], '3.2.0'))
253
+
254
+ if host['platform'] =~ /windows/
255
+ fetch_pe_on_windows(host, opts)
256
+ elsif host['platform'] =~ /osx/
257
+ fetch_pe_on_mac(host, opts)
258
+ else
259
+ fetch_pe_on_unix(host, opts)
260
+ end
261
+ end
262
+ end
263
+
264
+ #Classify the master so that it can deploy frictionless packages for a given host.
265
+ # @param [Host] host The host to install pacakges for
266
+ # @api private
267
+ def deploy_frictionless_to_master(host)
268
+ klass = host['platform'].gsub(/-/, '_').gsub(/\./,'')
269
+ klass = "pe_repo::platform::#{klass}"
270
+ if version_is_less(host['pe_ver'], '3.8')
271
+ # use the old rake tasks
272
+ on dashboard, "cd /opt/puppet/share/puppet-dashboard && /opt/puppet/bin/bundle exec /opt/puppet/bin/rake nodeclass:add[#{klass},skip]"
273
+ on dashboard, "cd /opt/puppet/share/puppet-dashboard && /opt/puppet/bin/bundle exec /opt/puppet/bin/rake node:add[#{master},,,skip]"
274
+ on dashboard, "cd /opt/puppet/share/puppet-dashboard && /opt/puppet/bin/bundle exec /opt/puppet/bin/rake node:addclass[#{master},#{klass}]"
275
+ on master, puppet("agent -t"), :acceptable_exit_codes => [0,2]
276
+ else
277
+ # the new hotness
278
+ begin
279
+ require 'scooter'
280
+ rescue LoadError => e
281
+ @logger.notify('WARNING: gem scooter is required for frictionless installation post 3.8')
282
+ raise e
283
+ end
284
+ dispatcher = Scooter::HttpDispatchers::ConsoleDispatcher.new(dashboard)
285
+
286
+ # Check if we've already created a frictionless agent node group
287
+ # to avoid errors creating the same node group when the beaker hosts file contains
288
+ # multiple hosts with the same platform
289
+ node_group = dispatcher.get_node_group_by_name('Beaker Frictionless Agent')
290
+ if node_group.nil? || node_group.empty?
291
+ node_group = {}
292
+ node_group['name'] = "Beaker Frictionless Agent"
293
+ # Pin the master to the node
294
+ node_group['rule'] = [ "and", [ '=', 'name', master.to_s ]]
295
+ node_group['classes'] ||= {}
296
+ end
297
+
298
+ # add the pe_repo platform class
299
+ node_group['classes'][klass] = {}
300
+
301
+ dispatcher.create_new_node_group_model(node_group)
302
+ on master, puppet("agent -t"), :acceptable_exit_codes => [0,2]
303
+ end
304
+ end
305
+
306
+ #Perform a Puppet Enterprise upgrade or install
307
+ # @param [Array<Host>] hosts The hosts to install or upgrade PE on
308
+ # @param [Hash{Symbol=>Symbol, String}] opts The options
309
+ # @option opts [String] :pe_dir Default directory or URL to pull PE package from
310
+ # (Otherwise uses individual hosts pe_dir)
311
+ # @option opts [String] :pe_ver Default PE version to install or upgrade to
312
+ # (Otherwise uses individual hosts pe_ver)
313
+ # @option opts [String] :pe_ver_win Default PE version to install or upgrade to on Windows hosts
314
+ # (Otherwise uses individual Windows hosts pe_ver)
315
+ # @option opts [Symbol] :type (:install) One of :upgrade or :install
316
+ # @option opts [Boolean] :set_console_password Should we set the PE console password in the answers file? Used during upgrade only.
317
+ # @option opts [Hash<String>] :answers Pre-set answers based upon ENV vars and defaults
318
+ # (See {Beaker::Options::Presets.env_vars})
319
+ # @option opts [Boolean] :fetch_local_then_push_to_host determines whether
320
+ # you use Beaker as the middleman for this (true), or curl the
321
+ # file from the host (false; default behavior)
322
+ # @option opts [Boolean] :masterless Are we performing a masterless installation?
323
+ #
324
+ # @example
325
+ # do_install(hosts, {:type => :upgrade, :pe_dir => path, :pe_ver => version, :pe_ver_win => version_win})
326
+ #
327
+ # @note on windows, the +:ruby_arch+ host parameter can determine in addition
328
+ # to other settings whether the 32 or 64bit install is used
329
+ #
330
+ # @note for puppet-agent install options, refer to
331
+ # {Beaker::DSL::InstallUtils::FOSSUtils#install_puppet_agent_pe_promoted_repo_on}
332
+ #
333
+ # @api private
334
+ #
335
+ def do_install hosts, opts = {}
336
+ masterless = opts[:masterless]
337
+ opts[:type] = opts[:type] || :install
338
+ unless masterless
339
+ pre30database = version_is_less(opts[:pe_ver] || database['pe_ver'], '3.0')
340
+ pre30master = version_is_less(opts[:pe_ver] || master['pe_ver'], '3.0')
341
+ end
342
+
343
+ pe_versions = ( [] << opts['pe_ver'] << hosts.map{ |host| host['pe_ver'] } ).flatten.compact
344
+ agent_only_check_needed = version_is_less('3.99', max_version(pe_versions, '3.8'))
345
+ if agent_only_check_needed
346
+ hosts_agent_only, hosts_not_agent_only = create_agent_specified_arrays(hosts)
347
+ else
348
+ hosts_agent_only, hosts_not_agent_only = [], hosts.dup
349
+ end
350
+
351
+ # Set PE distribution for all the hosts, create working dir
352
+ use_all_tar = ENV['PE_USE_ALL_TAR'] == 'true'
353
+ hosts.each do |host|
354
+ next if agent_only_check_needed && hosts_agent_only.include?(host)
355
+ host['pe_installer'] ||= 'puppet-enterprise-installer'
356
+ if host['platform'] !~ /windows|osx/
357
+ platform = use_all_tar ? 'all' : host['platform']
358
+ version = host['pe_ver'] || opts[:pe_ver]
359
+ host['dist'] = "puppet-enterprise-#{version}-#{platform}"
360
+ elsif host['platform'] =~ /osx/
361
+ version = host['pe_ver'] || opts[:pe_ver]
362
+ host['dist'] = "puppet-enterprise-#{version}-#{host['platform']}"
363
+ elsif host['platform'] =~ /windows/
364
+ version = host[:pe_ver] || opts['pe_ver_win']
365
+ is_config_32 = true == (host['ruby_arch'] == 'x86') || host['install_32'] || opts['install_32']
366
+ should_install_64bit = !(version_is_less(version, '3.4')) && host.is_x86_64? && !is_config_32
367
+ #only install 64bit builds if
368
+ # - we are on pe version 3.4+
369
+ # - we do not have install_32 set on host
370
+ # - we do not have install_32 set globally
371
+ if !(version_is_less(version, '3.99'))
372
+ if should_install_64bit
373
+ host['dist'] = "puppet-agent-#{version}-x64"
374
+ else
375
+ host['dist'] = "puppet-agent-#{version}-x86"
376
+ end
377
+ elsif should_install_64bit
378
+ host['dist'] = "puppet-enterprise-#{version}-x64"
379
+ else
380
+ host['dist'] = "puppet-enterprise-#{version}"
381
+ end
382
+ end
383
+ host['working_dir'] = host.tmpdir(Time.new.strftime("%Y-%m-%d_%H.%M.%S"))
384
+ end
385
+
386
+ fetch_pe(hosts_not_agent_only, opts)
387
+
388
+ install_hosts = hosts.dup
389
+ unless masterless
390
+ # If we're installing a database version less than 3.0, ignore the database host
391
+ install_hosts.delete(database) if pre30database and database != master and database != dashboard
392
+ end
393
+
394
+ install_hosts.each do |host|
395
+ if agent_only_check_needed && hosts_agent_only.include?(host)
396
+ host['type'] = 'aio'
397
+ install_puppet_agent_pe_promoted_repo_on(host, { :puppet_agent_version => host[:puppet_agent_version] || opts[:puppet_agent_version],
398
+ :puppet_agent_sha => host[:puppet_agent_sha] || opts[:puppet_agent_sha],
399
+ :pe_ver => host[:pe_ver] || opts[:pe_ver],
400
+ :puppet_collection => host[:puppet_collection] || opts[:puppet_collection] })
401
+ # 1 since no certificate found and waitforcert disabled
402
+ acceptable_exit_codes = [0, 1]
403
+ acceptable_exit_codes << 2 if opts[:type] == :upgrade
404
+ setup_defaults_and_config_helper_on(host, master, acceptable_exit_codes)
405
+ elsif host['platform'] =~ /windows/
406
+ opts = { :debug => host[:pe_debug] || opts[:pe_debug] }
407
+ msi_path = "#{host['working_dir']}\\#{host['dist']}.msi"
408
+ install_msi_on(host, msi_path, {}, opts)
409
+
410
+ # 1 since no certificate found and waitforcert disabled
411
+ acceptable_exit_codes = 1
412
+ setup_defaults_and_config_helper_on(host, master, acceptable_exit_codes)
413
+ else
414
+ # We only need answers if we're using the classic installer
415
+ version = host['pe_ver'] || opts[:pe_ver]
416
+ if host['roles'].include?('frictionless') && (! version_is_less(version, '3.2.0'))
417
+ # If We're *not* running the classic installer, we want
418
+ # to make sure the master has packages for us.
419
+ if host['platform'] != master['platform'] # only need to do this if platform differs
420
+ deploy_frictionless_to_master(host)
421
+ end
422
+ on host, installer_cmd(host, opts)
423
+ configure_type_defaults_on(host)
424
+ elsif host['platform'] =~ /osx|eos/
425
+ # If we're not frictionless, we need to run the OSX special-case
426
+ on host, installer_cmd(host, opts)
427
+ acceptable_codes = host['platform'] =~ /osx/ ? [1] : [0, 1]
428
+ setup_defaults_and_config_helper_on(host, master, acceptable_codes)
429
+ else
430
+ answers = BeakerAnswers::Answers.create(opts[:pe_ver] || host['pe_ver'], hosts, opts)
431
+ create_remote_file host, "#{host['working_dir']}/answers", answers.answer_string(host)
432
+ on host, installer_cmd(host, opts)
433
+ configure_type_defaults_on(host)
434
+ end
435
+ end
436
+
437
+ # On each agent, we ensure the certificate is signed then shut down the agent
438
+ sign_certificate_for(host) unless masterless
439
+ stop_agent_on(host)
440
+ end
441
+
442
+ unless masterless
443
+ # Wait for PuppetDB to be totally up and running (post 3.0 version of pe only)
444
+ sleep_until_puppetdb_started(database) unless pre30database
445
+
446
+ # Run the agent once to ensure everything is in the dashboard
447
+ install_hosts.each do |host|
448
+ on host, puppet_agent('-t'), :acceptable_exit_codes => [0,2]
449
+
450
+ # Workaround for PE-1105 when deploying 3.0.0
451
+ # The installer did not respect our database host answers in 3.0.0,
452
+ # and would cause puppetdb to be bounced by the agent run. By sleeping
453
+ # again here, we ensure that if that bounce happens during an upgrade
454
+ # test we won't fail early in the install process.
455
+ if host['pe_ver'] == '3.0.0' and host == database
456
+ sleep_until_puppetdb_started(database)
457
+ end
458
+ end
459
+
460
+ install_hosts.each do |host|
461
+ wait_for_host_in_dashboard(host)
462
+ end
463
+
464
+ # only appropriate for pre-3.9 builds
465
+ if version_is_less(master[:pe_ver], '3.99')
466
+ if pre30master
467
+ task = 'nodegroup:add_all_nodes group=default'
468
+ else
469
+ task = 'defaultgroup:ensure_default_group'
470
+ end
471
+ on dashboard, "/opt/puppet/bin/rake -sf /opt/puppet/share/puppet-dashboard/Rakefile #{task} RAILS_ENV=production"
472
+ end
473
+
474
+ # Now that all hosts are in the dashbaord, run puppet one more
475
+ # time to configure mcollective
476
+ on install_hosts, puppet_agent('-t'), :acceptable_exit_codes => [0,2]
477
+ end
478
+ end
479
+
480
+ # Builds the agent_only and not_agent_only arrays needed for installation.
481
+ #
482
+ # @param [Array<Host>] hosts hosts to split up into the arrays
483
+ #
484
+ # @note should only be called against versions 4.0+, as this method
485
+ # assumes AIO packages will be required.
486
+ #
487
+ # @note agent_only hosts with the :pe_ver setting < 4.0 will not be
488
+ # included in the agent_only array, as AIO install can only happen
489
+ # in versions > 4.0
490
+ #
491
+ # @api private
492
+ # @return [Array<Host>, Array<Host>]
493
+ # the array of hosts to do an agent_only install on and
494
+ # the array of hosts to do our usual install methods on
495
+ def create_agent_specified_arrays(hosts)
496
+ hosts_agent_only = []
497
+ hosts_not_agent_only = []
498
+ non_agent_only_roles = %w(master database dashboard console frictionless)
499
+ hosts.each do |host|
500
+ if host['roles'].none? {|role| non_agent_only_roles.include?(role) }
501
+ if !aio_version?(host)
502
+ hosts_not_agent_only << host
503
+ else
504
+ hosts_agent_only << host
505
+ end
506
+ else
507
+ hosts_not_agent_only << host
508
+ end
509
+ end
510
+ return hosts_agent_only, hosts_not_agent_only
511
+ end
512
+
513
+ # Helper for setting up pe_defaults & setting up the cert on the host
514
+ # @param [Host] host host to setup
515
+ # @param [Host] master the master host, for setting up the relationship
516
+ # @param [Array<Fixnum>] acceptable_exit_codes The exit codes that we want to ignore
517
+ #
518
+ # @return nil
519
+ # @api private
520
+ def setup_defaults_and_config_helper_on(host, master, acceptable_exit_codes=nil)
521
+ configure_type_defaults_on(host)
522
+ #set the certname and master
523
+ on host, puppet("config set server #{master}")
524
+ on host, puppet("config set certname #{host}")
525
+ #run once to request cert
526
+ on host, puppet_agent('-t'), :acceptable_exit_codes => acceptable_exit_codes
527
+ end
528
+
529
+ #Install PE based on global hosts with global options
530
+ #@see #install_pe_on
531
+ def install_pe
532
+ install_pe_on(hosts, options)
533
+ end
534
+
535
+ #Install PE based upon host configuration and options
536
+ #
537
+ # @param [Host, Array<Host>] install_hosts One or more hosts to act upon
538
+ # @!macro common_opts
539
+ # @option opts [Boolean] :masterless Are we performing a masterless installation?
540
+ # @option opts [String] :puppet_agent_version Version of puppet-agent to install. Required for PE agent
541
+ # only hosts on 4.0+
542
+ # @option opts [String] :puppet_agent_sha The sha of puppet-agent to install, defaults to puppet_agent_version.
543
+ # Required for PE agent only hosts on 4.0+
544
+ # @option opts [String] :pe_ver The version of PE (will also use host['pe_ver']), defaults to '4.0'
545
+ # @option opts [String] :puppet_collection The puppet collection for puppet-agent install.
546
+ #
547
+ # @example
548
+ # install_pe_on(hosts, {})
549
+ #
550
+ # @note Either pe_ver and pe_dir should be set in the ENV or each host should have pe_ver and pe_dir set individually.
551
+ # Install file names are assumed to be of the format puppet-enterprise-VERSION-PLATFORM.(tar)|(tar.gz)
552
+ # for Unix like systems and puppet-enterprise-VERSION.msi for Windows systems.
553
+ #
554
+ # @note For further installation parameters (such as puppet-agent install)
555
+ # options, refer to {#do_install} documentation
556
+ #
557
+ def install_pe_on(install_hosts, opts)
558
+ confine_block(:to, {}, install_hosts) do
559
+ sorted_hosts.each do |host|
560
+ #process the version files if necessary
561
+ host['pe_dir'] ||= opts[:pe_dir]
562
+ if host['platform'] =~ /windows/
563
+ # we don't need the pe_version if:
564
+ # * master pe_ver > 4.0
565
+ if not (!opts[:masterless] && master[:pe_ver] && !version_is_less(master[:pe_ver], '3.99'))
566
+ host['pe_ver'] ||= Beaker::Options::PEVersionScraper.load_pe_version(host[:pe_dir] || opts[:pe_dir], opts[:pe_version_file_win])
567
+ else
568
+ # inherit the master's version
569
+ host['pe_ver'] ||= master[:pe_ver]
570
+ end
571
+ else
572
+ host['pe_ver'] ||= Beaker::Options::PEVersionScraper.load_pe_version(host[:pe_dir] || opts[:pe_dir], opts[:pe_version_file])
573
+ end
574
+ end
575
+ do_install sorted_hosts, opts
576
+ end
577
+ end
578
+
579
+ #Upgrade PE based upon global host configuration and global options
580
+ #@see #upgrade_pe_on
581
+ def upgrade_pe path=nil
582
+ upgrade_pe_on(hosts, options, path)
583
+ end
584
+
585
+ #Upgrade PE based upon host configuration and options
586
+ # @param [Host, Array<Host>] upgrade_hosts One or more hosts to act upon
587
+ # @!macro common_opts
588
+ # @param [String] path A path (either local directory or a URL to a listing of PE builds).
589
+ # Will contain a LATEST file indicating the latest build to install.
590
+ # This is ignored if a pe_upgrade_ver and pe_upgrade_dir are specified
591
+ # in the host configuration file.
592
+ # @example
593
+ # upgrade_pe_on(agents, {}, "http://neptune.puppetlabs.lan/3.0/ci-ready/")
594
+ #
595
+ # @note Install file names are assumed to be of the format puppet-enterprise-VERSION-PLATFORM.(tar)|(tar.gz)
596
+ # for Unix like systems and puppet-enterprise-VERSION.msi for Windows systems.
597
+ def upgrade_pe_on upgrade_hosts, opts, path=nil
598
+ confine_block(:to, {}, upgrade_hosts) do
599
+ set_console_password = false
600
+ # if we are upgrading from something lower than 3.4 then we need to set the pe console password
601
+ if (dashboard[:pe_ver] ? version_is_less(dashboard[:pe_ver], "3.4.0") : true)
602
+ set_console_password = true
603
+ end
604
+ # get new version information
605
+ hosts.each do |host|
606
+ host['pe_dir'] = host['pe_upgrade_dir'] || path
607
+ if host['platform'] =~ /windows/
608
+ host['pe_ver'] = host['pe_upgrade_ver'] || opts['pe_upgrade_ver'] ||
609
+ Options::PEVersionScraper.load_pe_version(host['pe_dir'], opts[:pe_version_file_win])
610
+ else
611
+ host['pe_ver'] = host['pe_upgrade_ver'] || opts['pe_upgrade_ver'] ||
612
+ Options::PEVersionScraper.load_pe_version(host['pe_dir'], opts[:pe_version_file])
613
+ end
614
+ if version_is_less(host['pe_ver'], '3.0')
615
+ host['pe_installer'] ||= 'puppet-enterprise-upgrader'
616
+ end
617
+ end
618
+ do_install(sorted_hosts, opts.merge({:type => :upgrade, :set_console_password => set_console_password}))
619
+ opts['upgrade'] = true
620
+ end
621
+ end
622
+
623
+ #Create the Higgs install command string based upon the host and options settings. Installation command will be run as a
624
+ #background process. The output of the command will be stored in the provided host['higgs_file'].
625
+ # @param [Host] host The host that Higgs is to be installed on
626
+ # The host object must have the 'working_dir', 'dist' and 'pe_installer' field set correctly.
627
+ # @api private
628
+ def higgs_installer_cmd host
629
+
630
+ "cd #{host['working_dir']}/#{host['dist']} ; nohup ./#{host['pe_installer']} <<<Y > #{host['higgs_file']} 2>&1 &"
631
+
632
+ end
633
+
634
+ #Perform a Puppet Enterprise Higgs install up until web browser interaction is required, runs on linux hosts only.
635
+ # @param [Host] host The host to install higgs on
636
+ # @param [Hash{Symbol=>Symbol, String}] opts The options
637
+ # @option opts [String] :pe_dir Default directory or URL to pull PE package from
638
+ # (Otherwise uses individual hosts pe_dir)
639
+ # @option opts [String] :pe_ver Default PE version to install
640
+ # (Otherwise uses individual hosts pe_ver)
641
+ # @option opts [Boolean] :fetch_local_then_push_to_host determines whether
642
+ # you use Beaker as the middleman for this (true), or curl the
643
+ # file from the host (false; default behavior)
644
+ # @raise [StandardError] When installation times out
645
+ #
646
+ # @example
647
+ # do_higgs_install(master, {:pe_dir => path, :pe_ver => version})
648
+ #
649
+ # @api private
650
+ #
651
+ def do_higgs_install host, opts
652
+ use_all_tar = ENV['PE_USE_ALL_TAR'] == 'true'
653
+ platform = use_all_tar ? 'all' : host['platform']
654
+ version = host['pe_ver'] || opts[:pe_ver]
655
+ host['dist'] = "puppet-enterprise-#{version}-#{platform}"
656
+
657
+ use_all_tar = ENV['PE_USE_ALL_TAR'] == 'true'
658
+ host['pe_installer'] ||= 'puppet-enterprise-installer'
659
+ host['working_dir'] = host.tmpdir(Time.new.strftime("%Y-%m-%d_%H.%M.%S"))
660
+
661
+ fetch_pe([host], opts)
662
+
663
+ host['higgs_file'] = "higgs_#{File.basename(host['working_dir'])}.log"
664
+ on host, higgs_installer_cmd(host), opts
665
+
666
+ #wait for output to host['higgs_file']
667
+ #we're all done when we find this line in the PE installation log
668
+ higgs_re = /Please\s+go\s+to\s+https:\/\/.*\s+in\s+your\s+browser\s+to\s+continue\s+installation/m
669
+ res = Result.new(host, 'tmp cmd')
670
+ tries = 10
671
+ attempts = 0
672
+ prev_sleep = 0
673
+ cur_sleep = 1
674
+ while (res.stdout !~ higgs_re) and (attempts < tries)
675
+ res = on host, "cd #{host['working_dir']}/#{host['dist']} && cat #{host['higgs_file']}", :accept_all_exit_codes => true
676
+ attempts += 1
677
+ sleep( cur_sleep )
678
+ prev_sleep = cur_sleep
679
+ cur_sleep = cur_sleep + prev_sleep
680
+ end
681
+
682
+ if attempts >= tries
683
+ raise "Failed to kick off PE (Higgs) web installation"
684
+ end
685
+ end
686
+
687
+ #Install Higgs up till the point where you need to continue installation in a web browser, defaults to execution
688
+ #on the master node.
689
+ #@param [Host] higgs_host The host to install Higgs on (supported on linux platform only)
690
+ # @example
691
+ # install_higgs
692
+ #
693
+ # @note Either pe_ver and pe_dir should be set in the ENV or each host should have pe_ver and pe_dir set individually.
694
+ # Install file names are assumed to be of the format puppet-enterprise-VERSION-PLATFORM.(tar)|(tar.gz).
695
+ #
696
+ def install_higgs( higgs_host = master )
697
+ #process the version files if necessary
698
+ master['pe_dir'] ||= options[:pe_dir]
699
+ master['pe_ver'] = master['pe_ver'] || options['pe_ver'] ||
700
+ Beaker::Options::PEVersionScraper.load_pe_version(master[:pe_dir] || options[:pe_dir], options[:pe_version_file])
701
+ if higgs_host['platform'] =~ /osx|windows/
702
+ raise "Attempting higgs installation on host #{higgs_host.name} with unsupported platform #{higgs_host['platform']}"
703
+ end
704
+ #send in the global options hash
705
+ do_higgs_install higgs_host, options
706
+ end
707
+
708
+ # Grabs the pe file from a remote host to the machine running Beaker, then
709
+ # scp's the file out to the host.
710
+ #
711
+ # @param [Host] host The host to install on
712
+ # @param [String] path path to the install file
713
+ # @param [String] filename the filename of the pe file (without the extension)
714
+ # @param [String] extension the extension of the pe file
715
+ # @param [String] local_dir the directory to store the pe file in on
716
+ # the Beaker-running-machine
717
+ #
718
+ # @api private
719
+ # @return nil
720
+ def fetch_and_push_pe(host, path, filename, extension, local_dir='tmp/pe')
721
+ fetch_http_file("#{path}", "#{filename}#{extension}", local_dir)
722
+ scp_to host, "#{local_dir}/#{filename}#{extension}", host['working_dir']
723
+ end
724
+
725
+ end
726
+ end
727
+ end
728
+ end