beaker 2.7.1 → 2.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (53) hide show
  1. checksums.yaml +8 -8
  2. data/HISTORY.md +121 -2
  3. data/lib/beaker/dsl.rb +2 -2
  4. data/lib/beaker/dsl/helpers.rb +13 -1429
  5. data/lib/beaker/dsl/helpers/facter_helpers.rb +48 -0
  6. data/lib/beaker/dsl/helpers/hiera_helpers.rb +71 -0
  7. data/lib/beaker/dsl/helpers/host_helpers.rb +506 -0
  8. data/lib/beaker/dsl/helpers/puppet_helpers.rb +698 -0
  9. data/lib/beaker/dsl/helpers/tk_helpers.rb +101 -0
  10. data/lib/beaker/dsl/helpers/web_helpers.rb +115 -0
  11. data/lib/beaker/dsl/install_utils.rb +8 -1570
  12. data/lib/beaker/dsl/install_utils/ezbake_utils.rb +256 -0
  13. data/lib/beaker/dsl/install_utils/module_utils.rb +237 -0
  14. data/lib/beaker/dsl/install_utils/pe_utils.rb +518 -0
  15. data/lib/beaker/dsl/install_utils/puppet_utils.rb +722 -0
  16. data/lib/beaker/dsl/outcomes.rb +0 -4
  17. data/lib/beaker/dsl/roles.rb +0 -3
  18. data/lib/beaker/dsl/structure.rb +127 -4
  19. data/lib/beaker/dsl/wrappers.rb +0 -4
  20. data/lib/beaker/host.rb +23 -0
  21. data/lib/beaker/host/unix/pkg.rb +4 -4
  22. data/lib/beaker/host_prebuilt_steps.rb +11 -5
  23. data/lib/beaker/hypervisor/vagrant.rb +1 -0
  24. data/lib/beaker/hypervisor/vmpooler.rb +38 -0
  25. data/lib/beaker/logger.rb +10 -4
  26. data/lib/beaker/network_manager.rb +5 -4
  27. data/lib/beaker/options/command_line_parser.rb +7 -0
  28. data/lib/beaker/shared.rb +2 -1
  29. data/lib/beaker/shared/semvar.rb +41 -0
  30. data/lib/beaker/test_suite.rb +20 -6
  31. data/lib/beaker/version.rb +1 -1
  32. data/spec/beaker/dsl/helpers/facter_helpers_spec.rb +59 -0
  33. data/spec/beaker/dsl/helpers/hiera_helpers_spec.rb +96 -0
  34. data/spec/beaker/dsl/helpers/host_helpers_spec.rb +413 -0
  35. data/spec/beaker/dsl/{helpers_spec.rb → helpers/puppet_helpers_spec.rb} +2 -611
  36. data/spec/beaker/dsl/helpers/tk_helpers_spec.rb +83 -0
  37. data/spec/beaker/dsl/helpers/web_helpers_spec.rb +60 -0
  38. data/spec/beaker/dsl/install_utils/module_utils_spec.rb +241 -0
  39. data/spec/beaker/dsl/install_utils/pe_utils_spec.rb +475 -0
  40. data/spec/beaker/dsl/install_utils/puppet_utils_spec.rb +523 -0
  41. data/spec/beaker/dsl/structure_spec.rb +108 -0
  42. data/spec/beaker/host_prebuilt_steps_spec.rb +44 -0
  43. data/spec/beaker/host_spec.rb +41 -0
  44. data/spec/beaker/hypervisor/vagrant_spec.rb +2 -1
  45. data/spec/beaker/logger_spec.rb +9 -2
  46. data/spec/beaker/network_manager_spec.rb +7 -1
  47. data/spec/beaker/options/command_line_parser_spec.rb +3 -2
  48. data/spec/beaker/shared/semvar_spec.rb +36 -0
  49. data/spec/beaker/test_suite_spec.rb +48 -0
  50. data/spec/mocks.rb +10 -0
  51. metadata +23 -5
  52. data/lib/beaker/dsl/ezbake_utils.rb +0 -259
  53. data/spec/beaker/dsl/install_utils_spec.rb +0 -1242
@@ -0,0 +1,101 @@
1
+ # -*- coding: utf-8 -*-
2
+ require 'hocon'
3
+ require 'hocon/config_error'
4
+ require 'inifile'
5
+
6
+ module Beaker
7
+ module DSL
8
+ module Helpers
9
+ # Convenience methods for modifying and reading TrapperKeeper configs
10
+ module TKHelpers
11
+
12
+ # @!macro common_opts
13
+ # @param [Hash{Symbol=>String}] opts Options to alter execution.
14
+ # @option opts [Boolean] :silent (false) Do not produce log output
15
+ # @option opts [Array<Fixnum>] :acceptable_exit_codes ([0]) An array
16
+ # (or range) of integer exit codes that should be considered
17
+ # acceptable. An error will be thrown if the exit code does not
18
+ # match one of the values in this list.
19
+ # @option opts [Hash{String=>String}] :environment ({}) These will be
20
+ # treated as extra environment variables that should be set before
21
+ # running the command.
22
+ #
23
+
24
+ # Modify the given TrapperKeeper config file.
25
+ #
26
+ # @param [Host] host A host object
27
+ # @param [OptionsHash] options_hash New hash which will be merged into
28
+ # the given TrapperKeeper config.
29
+ # @param [String] config_file_path Path to the TrapperKeeper config on
30
+ # the given host which is to be
31
+ # modified.
32
+ # @param [Bool] replace If set true, instead of updating the existing
33
+ # TrapperKeeper configuration, replace it entirely
34
+ # with the contents of the given hash.
35
+ #
36
+ # @note TrapperKeeper config files can be HOCON, JSON, or Ini. We don't
37
+ # particularly care which of these the file named by `config_file_path` on
38
+ # the SUT actually is, just that the contents can be parsed into a map.
39
+ #
40
+ def modify_tk_config(host, config_file_path, options_hash, replace=false)
41
+ if options_hash.empty?
42
+ return nil
43
+ end
44
+
45
+ new_hash = Beaker::Options::OptionsHash.new
46
+
47
+ if replace
48
+ new_hash.merge!(options_hash)
49
+ else
50
+ if not host.file_exist?( config_file_path )
51
+ raise "Error: #{config_file_path} does not exist on #{host}"
52
+ end
53
+ file_string = host.exec( Command.new( "cat #{config_file_path}" )).stdout
54
+
55
+ begin
56
+ tk_conf_hash = read_tk_config_string(file_string)
57
+ rescue RuntimeError
58
+ raise "Error reading trapperkeeper config: #{config_file_path} at host: #{host}"
59
+ end
60
+
61
+ new_hash.merge!(tk_conf_hash)
62
+ new_hash.merge!(options_hash)
63
+ end
64
+
65
+ file_string = JSON.dump(new_hash)
66
+ create_remote_file host, config_file_path, file_string
67
+ end
68
+
69
+ # The Trapperkeeper config service will accept HOCON (aka typesafe), JSON,
70
+ # or Ini configuration files which means we need to safely handle the the
71
+ # exceptions that might come from parsing the given string with the wrong
72
+ # parser and fall back to the next valid parser in turn. We finally raise
73
+ # a RuntimeException if none of the parsers succeed.
74
+ #
75
+ # @!visibility private
76
+ def read_tk_config_string( string )
77
+ begin
78
+ return Hocon.parse(string)
79
+ rescue Hocon::ConfigError
80
+ nil
81
+ end
82
+
83
+ begin
84
+ return JSON.parse(string)
85
+ rescue JSON::JSONError
86
+ nil
87
+ end
88
+
89
+ begin
90
+ return IniFile.new(string)
91
+ rescue IniFile::Error
92
+ nil
93
+ end
94
+
95
+ raise "Failed to read TrapperKeeper config!"
96
+ end
97
+ end
98
+
99
+ end
100
+ end
101
+ end
@@ -0,0 +1,115 @@
1
+ module Beaker
2
+ module DSL
3
+ module Helpers
4
+ # Convenience methods for checking links and moving web content to hosts
5
+ module WebHelpers
6
+
7
+ # @!macro common_opts
8
+ # @param [Hash{Symbol=>String}] opts Options to alter execution.
9
+ # @option opts [Boolean] :silent (false) Do not produce log output
10
+ # @option opts [Array<Fixnum>] :acceptable_exit_codes ([0]) An array
11
+ # (or range) of integer exit codes that should be considered
12
+ # acceptable. An error will be thrown if the exit code does not
13
+ # match one of the values in this list.
14
+ # @option opts [Hash{String=>String}] :environment ({}) These will be
15
+ # treated as extra environment variables that should be set before
16
+ # running the command.
17
+ #
18
+
19
+ # Blocks until the port is open on the host specified, returns false
20
+ # on failure
21
+ def port_open_within?( host, port = 8140, seconds = 120 )
22
+ repeat_for( seconds ) do
23
+ host.port_open?( port )
24
+ end
25
+ end
26
+
27
+ #Determine is a given URL is accessible
28
+ #@param [String] link The URL to examine
29
+ #@return [Boolean] true if the URL has a '200' HTTP response code, false otherwise
30
+ #@example
31
+ # extension = link_exists?("#{URL}.tar.gz") ? ".tar.gz" : ".tar"
32
+ def link_exists?(link)
33
+ require "net/http"
34
+ require "net/https"
35
+ require "open-uri"
36
+ url = URI.parse(link)
37
+ http = Net::HTTP.new(url.host, url.port)
38
+ http.use_ssl = (url.scheme == 'https')
39
+ http.start do |http|
40
+ return http.head(url.request_uri).code == "200"
41
+ end
42
+ end
43
+
44
+ # Fetch file_name from the given base_url into dst_dir.
45
+ #
46
+ # @param [String] base_url The base url from which to recursively download
47
+ # files.
48
+ # @param [String] file_name The trailing name compnent of both the source url
49
+ # and the destination file.
50
+ # @param [String] dst_dir The local destination directory.
51
+ #
52
+ # @return [String] dst The name of the newly-created file.
53
+ #
54
+ # @!visibility private
55
+ def fetch_http_file(base_url, file_name, dst_dir)
56
+ FileUtils.makedirs(dst_dir)
57
+ src = "#{base_url}/#{file_name}"
58
+ dst = File.join(dst_dir, file_name)
59
+ if File.exists?(dst)
60
+ logger.notify "Already fetched #{dst}"
61
+ else
62
+ logger.notify "Fetching: #{src}"
63
+ logger.notify " and saving to #{dst}"
64
+ open(src) do |remote|
65
+ File.open(dst, "w") do |file|
66
+ FileUtils.copy_stream(remote, file)
67
+ end
68
+ end
69
+ end
70
+ return dst
71
+ end
72
+
73
+ # Recursively fetch the contents of the given http url, ignoring
74
+ # `index.html` and `*.gif` files.
75
+ #
76
+ # @param [String] url The base http url from which to recursively download
77
+ # files.
78
+ # @param [String] dst_dir The local destination directory.
79
+ #
80
+ # @return [String] dst The name of the newly-created subdirectory of
81
+ # dst_dir.
82
+ #
83
+ # @!visibility private
84
+ def fetch_http_dir(url, dst_dir)
85
+ logger.notify "fetch_http_dir (url: #{url}, dst_dir #{dst_dir})"
86
+ if url[-1, 1] !~ /\//
87
+ url += '/'
88
+ end
89
+ url = URI.parse(url)
90
+ chunks = url.path.split('/')
91
+ dst = File.join(dst_dir, chunks.last)
92
+ #determine directory structure to cut
93
+ #only want to keep the last directory, thus cut total number of dirs - 2 (hostname + last dir name)
94
+ cut = chunks.length - 2
95
+ wget_command = "wget -nv -P #{dst_dir} --reject \"index.html*\",\"*.gif\" --cut-dirs=#{cut} -np -nH --no-check-certificate -r #{url}"
96
+
97
+ logger.notify "Fetching remote directory: #{url}"
98
+ logger.notify " and saving to #{dst}"
99
+ logger.notify " using command: #{wget_command}"
100
+
101
+ #in ruby 1.9+ we can upgrade this to popen3 to gain access to the subprocess pid
102
+ result = `#{wget_command} 2>&1`
103
+ result.each_line do |line|
104
+ logger.debug(line)
105
+ end
106
+ if $?.to_i != 0
107
+ raise "Failed to fetch_remote_dir '#{url}' (exit code #{$?}"
108
+ end
109
+ dst
110
+ end
111
+
112
+ end
113
+ end
114
+ end
115
+ end
@@ -1,1577 +1,15 @@
1
- require 'pathname'
1
+ [ 'pe', 'puppet', 'ezbake', 'module' ].each do |lib|
2
+ require "beaker/dsl/install_utils/#{lib}_utils"
3
+ end
2
4
 
3
5
  module Beaker
4
6
  module DSL
5
- #
6
- # This module contains methods to help cloning, extracting git info,
7
- # ordering of Puppet packages, and installing ruby projects that
8
- # contain an `install.rb` script.
9
- #
10
- # To mix this is into a class you need the following:
11
- # * a method *hosts* that yields any hosts implementing
12
- # {Beaker::Host}'s interface to act upon.
13
- # * a method *options* that provides an options hash, see {Beaker::Options::OptionsHash}
14
- # * the module {Beaker::DSL::Roles} that provides access to the various hosts implementing
15
- # {Beaker::Host}'s interface to act upon
16
- # * the module {Beaker::DSL::Wrappers} the provides convenience methods for {Beaker::DSL::Command} creation
7
+ # Collection of installation methods and support
17
8
  module InstallUtils
18
-
19
- # The default install path
20
- SourcePath = "/opt/puppet-git-repos"
21
-
22
- # A regex to know if the uri passed is pointing to a git repo
23
- GitURI = %r{^(git|https?|file)://|^git@|^gitmirror@}
24
-
25
- # Github's ssh signature for cloning via ssh
26
- GitHubSig = 'github.com,207.97.227.239 ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAq2A7hRGmdnm9tUDbO9IDSwBK6TbQa+PXYPCPy6rbTrTtw7PHkccKrpp0yVhp5HdEIcKr6pLlVDBfOLX9QUsyCOV0wzfjIJNlGEYsdlLJizHhbn2mUjvSAHQqZETYP81eFzLQNnPHt4EVVUh7VfDESU84KezmD5QlWpXLmvU31/yMf+Se8xhHTvKSCZIFImWwoG6mbUoWf9nzpIoaSjB+weqqUUmpaaasXVal72J+UX2B+2RPW3RcT0eOzQgqlJL3RKrTJvdsjE3JEAvGq3lGHSZXy28G3skua2SmVi/w4yCE6gbODqnTWlg7+wC604ydGXA8VJiS5ap43JXiUFFAaQ=='
27
-
28
- # The directories in the module directory that will not be scp-ed to the test system when using `copy_module_to`
29
- PUPPET_MODULE_INSTALL_IGNORE = ['.bundle', '.git', '.idea', '.vagrant', '.vendor', 'vendor', 'acceptance', 'bundle', 'spec', 'tests', 'log']
30
-
31
- # @param [String] uri A uri in the format of <git uri>#<revision>
32
- # the `git://`, `http://`, `https://`, and ssh
33
- # (if cloning as the remote git user) protocols
34
- # are valid for <git uri>
35
- #
36
- # @example Usage
37
- # project = extract_repo_info_from 'git@github.com:puppetlabs/SuperSecretSauce#what_is_justin_doing'
38
- #
39
- # puts project[:name]
40
- # #=> 'SuperSecretSauce'
41
- #
42
- # puts project[:rev]
43
- # #=> 'what_is_justin_doing'
44
- #
45
- # @return [Hash{Symbol=>String}] Returns a hash containing the project
46
- # name, repository path, and revision
47
- # (defaults to HEAD)
48
- #
49
- # @api dsl
50
- def extract_repo_info_from uri
51
- project = {}
52
- repo, rev = uri.split('#', 2)
53
- project[:name] = Pathname.new(repo).basename('.git').to_s
54
- project[:path] = repo
55
- project[:rev] = rev || 'HEAD'
56
- return project
57
- end
58
-
59
- # Takes an array of package info hashes (like that returned from
60
- # {#extract_repo_info_from}) and sorts the `puppet`, `facter`, `hiera`
61
- # packages so that puppet's dependencies will be installed first.
62
- #
63
- # @!visibility private
64
- def order_packages packages_array
65
- puppet = packages_array.select {|e| e[:name] == 'puppet' }
66
- puppet_depends_on = packages_array.select do |e|
67
- e[:name] == 'hiera' or e[:name] == 'facter'
68
- end
69
- depends_on_puppet = (packages_array - puppet) - puppet_depends_on
70
- [puppet_depends_on, puppet, depends_on_puppet].flatten
71
- end
72
-
73
- # @param [Host] host An object implementing {Beaker::Hosts}'s
74
- # interface.
75
- # @param [String] path The path on the remote [host] to the repository
76
- # @param [Hash{Symbol=>String}] repository A hash representing repo
77
- # info like that emitted by
78
- # {#extract_repo_info_from}
79
- #
80
- # @example Getting multiple project versions
81
- # versions = [puppet_repo, facter_repo, hiera_repo].inject({}) do |vers, repo_info|
82
- # vers.merge(find_git_repo_versions(host, '/opt/git-puppet-repos', repo_info) )
83
- # end
84
- # @return [Hash] Executes git describe on [host] and returns a Hash
85
- # with the key of [repository[:name]] and value of
86
- # the output from git describe.
87
- #
88
- # @note This requires the helper methods:
89
- # * {Beaker::DSL::Structure#step}
90
- # * {Beaker::DSL::Helpers#on}
91
- #
92
- # @api dsl
93
- def find_git_repo_versions host, path, repository
94
- version = {}
95
- step "Grab version for #{repository[:name]}" do
96
- on host, "cd #{path}/#{repository[:name]} && " +
97
- "git describe || true" do
98
- version[repository[:name]] = stdout.chomp
99
- end
100
- end
101
- version
102
- end
103
-
104
- #
105
- # @see #find_git_repo_versions
106
- def install_from_git host, path, repository
107
- name = repository[:name]
108
- repo = repository[:path]
109
- rev = repository[:rev]
110
- depth = repository[:depth]
111
- depth_branch = repository[:depth_branch]
112
- target = "#{path}/#{name}"
113
-
114
- if (depth_branch.nil?)
115
- depth_branch = rev
116
- end
117
-
118
- clone_cmd = "git clone #{repo} #{target}"
119
- if (depth)
120
- clone_cmd = "git clone --branch #{depth_branch} --depth #{depth} #{repo} #{target}"
121
- end
122
-
123
- step "Clone #{repo} if needed" do
124
- on host, "test -d #{path} || mkdir -p #{path}"
125
- on host, "test -d #{target} || #{clone_cmd}"
126
- end
127
-
128
- step "Update #{name} and check out revision #{rev}" do
129
- commands = ["cd #{target}",
130
- "remote rm origin",
131
- "remote add origin #{repo}",
132
- "fetch origin +refs/pull/*:refs/remotes/origin/pr/* +refs/heads/*:refs/remotes/origin/*",
133
- "clean -fdx",
134
- "checkout -f #{rev}"]
135
- on host, commands.join(" && git ")
136
- end
137
-
138
- step "Install #{name} on the system" do
139
- # The solaris ruby IPS package has bindir set to /usr/ruby/1.8/bin.
140
- # However, this is not the path to which we want to deliver our
141
- # binaries. So if we are using solaris, we have to pass the bin and
142
- # sbin directories to the install.rb
143
- install_opts = ''
144
- install_opts = '--bindir=/usr/bin --sbindir=/usr/sbin' if
145
- host['platform'].include? 'solaris'
146
-
147
- on host, "cd #{target} && " +
148
- "if [ -f install.rb ]; then " +
149
- "ruby ./install.rb #{install_opts}; " +
150
- "else true; fi"
151
- end
152
- end
153
-
154
- #Create the PE install command string based upon the host and options settings
155
- # @param [Host] host The host that PE is to be installed on
156
- # For UNIX machines using the full PE installer, the host object must have the 'pe_installer' field set correctly.
157
- # @param [Hash{Symbol=>String}] opts The options
158
- # @option opts [String] :pe_ver_win Default PE version to install or upgrade to on Windows hosts
159
- # (Othersie uses individual Windows hosts pe_ver)
160
- # @option opts [String] :pe_ver Default PE version to install or upgrade to
161
- # (Otherwise uses individual hosts pe_ver)
162
- # @option opts [Boolean] :pe_debug (false) Should we run the installer in debug mode?
163
- # @example
164
- # on host, "#{installer_cmd(host, opts)} -a #{host['working_dir']}/answers"
165
- # @api private
166
- def installer_cmd(host, opts)
167
- version = host['pe_ver'] || opts[:pe_ver]
168
- if host['platform'] =~ /windows/
169
- log_file = "#{File.basename(host['working_dir'])}.log"
170
- pe_debug = host[:pe_debug] || opts[:pe_debug] ? " && cat #{log_file}" : ''
171
- if host.is_cygwin?
172
- "cd #{host['working_dir']} && cmd /C 'start /w msiexec.exe /qn /L*V #{log_file} /i #{host['dist']}.msi PUPPET_MASTER_SERVER=#{master} PUPPET_AGENT_CERTNAME=#{host}'#{pe_debug}"
173
- else
174
- "cd #{host['working_dir']} && msiexec.exe /qn /L*V #{log_file} /i #{host['dist']}.msi PUPPET_MASTER_SERVER=#{master} PUPPET_AGENT_CERTNAME=#{host}#{pe_debug}"
175
- end
176
- # Frictionless install didn't exist pre-3.2.0, so in that case we fall
177
- # through and do a regular install.
178
- elsif host['roles'].include? 'frictionless' and ! version_is_less(version, '3.2.0')
179
- # PE 3.4 introduced the ability to pass in config options to the bash script in the form
180
- # of <section>:<key>=<value>
181
- frictionless_install_opts = []
182
- if host.has_key?('frictionless_options') and ! version_is_less(version, '3.4.0')
183
- # since we have options to pass in, we need to tell the bash script
184
- host['frictionless_options'].each do |section, settings|
185
- settings.each do |key, value|
186
- frictionless_install_opts << "#{section}:#{key}=#{value}"
187
- end
188
- end
189
- end
190
-
191
- pe_debug = host[:pe_debug] || opts[:pe_debug] ? ' -x' : ''
192
- "cd #{host['working_dir']} && curl --tlsv1 -kO https://#{master}:8140/packages/#{version}/install.bash && bash#{pe_debug} install.bash #{frictionless_install_opts.join(' ')}".strip
193
- elsif host['platform'] =~ /osx/
194
- version = host['pe_ver'] || opts[:pe_ver]
195
- pe_debug = host[:pe_debug] || opts[:pe_debug] ? ' -verboseR' : ''
196
- "cd #{host['working_dir']} && hdiutil attach #{host['dist']}.dmg && installer#{pe_debug} -pkg /Volumes/puppet-enterprise-#{version}/puppet-enterprise-installer-#{version}.pkg -target /"
197
- elsif host['platform'] =~ /eos/
198
- commands = ['enable', "extension puppet-enterprise-#{version}-#{host['platform']}.swix"]
199
- command = commands.join("\n")
200
- "Cli -c '#{command}'"
201
- else
202
- pe_debug = host[:pe_debug] || opts[:pe_debug] ? ' -D' : ''
203
- "cd #{host['working_dir']}/#{host['dist']} && ./#{host['pe_installer']}#{pe_debug} -a #{host['working_dir']}/answers"
204
- end
205
- end
206
-
207
- #Create the Higgs install command string based upon the host and options settings. Installation command will be run as a
208
- #background process. The output of the command will be stored in the provided host['higgs_file'].
209
- # @param [Host] host The host that Higgs is to be installed on
210
- # The host object must have the 'working_dir', 'dist' and 'pe_installer' field set correctly.
211
- # @api private
212
- def higgs_installer_cmd host
213
-
214
- "cd #{host['working_dir']}/#{host['dist']} ; nohup ./#{host['pe_installer']} <<<Y > #{host['higgs_file']} 2>&1 &"
215
-
216
- end
217
-
218
- #Determine is a given URL is accessible
219
- #@param [String] link The URL to examine
220
- #@return [Boolean] true if the URL has a '200' HTTP response code, false otherwise
221
- #@example
222
- # extension = link_exists?("#{URL}.tar.gz") ? ".tar.gz" : ".tar"
223
- # @api private
224
- def link_exists?(link)
225
- require "net/http"
226
- require "net/https"
227
- require "open-uri"
228
- url = URI.parse(link)
229
- http = Net::HTTP.new(url.host, url.port)
230
- http.use_ssl = (url.scheme == 'https')
231
- http.start do |http|
232
- return http.head(url.request_uri).code == "200"
233
- end
234
- end
235
-
236
- # Fetch file_name from the given base_url into dst_dir.
237
- #
238
- # @param [String] base_url The base url from which to recursively download
239
- # files.
240
- # @param [String] file_name The trailing name compnent of both the source url
241
- # and the destination file.
242
- # @param [String] dst_dir The local destination directory.
243
- #
244
- # @return [String] dst The name of the newly-created file.
245
- #
246
- # @!visibility private
247
- def fetch_http_file(base_url, file_name, dst_dir)
248
- FileUtils.makedirs(dst_dir)
249
- src = "#{base_url}/#{file_name}"
250
- dst = File.join(dst_dir, file_name)
251
- if File.exists?(dst)
252
- logger.notify "Already fetched #{dst}"
253
- else
254
- logger.notify "Fetching: #{src}"
255
- logger.notify " and saving to #{dst}"
256
- open(src) do |remote|
257
- File.open(dst, "w") do |file|
258
- FileUtils.copy_stream(remote, file)
259
- end
260
- end
261
- end
262
- return dst
263
- end
264
-
265
- # Recursively fetch the contents of the given http url, ignoring
266
- # `index.html` and `*.gif` files.
267
- #
268
- # @param [String] url The base http url from which to recursively download
269
- # files.
270
- # @param [String] dst_dir The local destination directory.
271
- #
272
- # @return [String] dst The name of the newly-created subdirectory of
273
- # dst_dir.
274
- #
275
- # @!visibility private
276
- def fetch_http_dir(url, dst_dir)
277
- logger.notify "fetch_http_dir (url: #{url}, dst_dir #{dst_dir})"
278
- if url[-1, 1] !~ /\//
279
- url += '/'
280
- end
281
- url = URI.parse(url)
282
- chunks = url.path.split('/')
283
- dst = File.join(dst_dir, chunks.last)
284
- #determine directory structure to cut
285
- #only want to keep the last directory, thus cut total number of dirs - 2 (hostname + last dir name)
286
- cut = chunks.length - 2
287
- wget_command = "wget -nv -P #{dst_dir} --reject \"index.html*\",\"*.gif\" --cut-dirs=#{cut} -np -nH --no-check-certificate -r #{url}"
288
-
289
- logger.notify "Fetching remote directory: #{url}"
290
- logger.notify " and saving to #{dst}"
291
- logger.notify " using command: #{wget_command}"
292
-
293
- #in ruby 1.9+ we can upgrade this to popen3 to gain access to the subprocess pid
294
- result = `#{wget_command} 2>&1`
295
- result.each_line do |line|
296
- logger.debug(line)
297
- end
298
- if $?.to_i != 0
299
- raise "Failed to fetch_remote_dir '#{url}' (exit code #{$?}"
300
- end
301
- dst
302
- end
303
-
304
- #Determine the PE package to download/upload on a mac host, download/upload that package onto the host.
305
- # Assumed file name format: puppet-enterprise-3.3.0-rc1-559-g97f0833-osx-10.9-x86_64.dmg.
306
- # @param [Host] host The mac host to download/upload and unpack PE onto
307
- # @param [Hash{Symbol=>Symbol, String}] opts The options
308
- # @option opts [String] :pe_dir Default directory or URL to pull PE package from
309
- # (Otherwise uses individual hosts pe_dir)
310
- # @api private
311
- def fetch_puppet_on_mac(host, opts)
312
- path = host['pe_dir'] || opts[:pe_dir]
313
- local = File.directory?(path)
314
- filename = "#{host['dist']}"
315
- extension = ".dmg"
316
- if local
317
- if not File.exists?("#{path}/#{filename}#{extension}")
318
- raise "attempting installation on #{host}, #{path}/#{filename}#{extension} does not exist"
319
- end
320
- scp_to host, "#{path}/#{filename}#{extension}", "#{host['working_dir']}/#{filename}#{extension}"
321
- else
322
- if not link_exists?("#{path}/#{filename}#{extension}")
323
- raise "attempting installation on #{host}, #{path}/#{filename}#{extension} does not exist"
324
- end
325
- on host, "cd #{host['working_dir']}; curl -O #{path}/#{filename}#{extension}"
326
- end
327
- end
328
-
329
- #Determine the PE package to download/upload on a windows host, download/upload that package onto the host.
330
- #Assumed file name format: puppet-enterprise-3.3.0-rc1-559-g97f0833.msi
331
- # @param [Host] host The windows host to download/upload and unpack PE onto
332
- # @param [Hash{Symbol=>Symbol, String}] opts The options
333
- # @option opts [String] :pe_dir Default directory or URL to pull PE package from
334
- # (Otherwise uses individual hosts pe_dir)
335
- # @option opts [String] :pe_ver_win Default PE version to install or upgrade to
336
- # (Otherwise uses individual hosts pe_ver)
337
- # @api private
338
- def fetch_puppet_on_windows(host, opts)
339
- path = host['pe_dir'] || opts[:pe_dir]
340
- local = File.directory?(path)
341
- version = host['pe_ver'] || opts[:pe_ver_win]
342
- filename = "#{host['dist']}"
343
- extension = ".msi"
344
- if local
345
- if not File.exists?("#{path}/#{filename}#{extension}")
346
- raise "attempting installation on #{host}, #{path}/#{filename}#{extension} does not exist"
347
- end
348
- scp_to host, "#{path}/#{filename}#{extension}", "#{host['working_dir']}/#{filename}#{extension}"
349
- else
350
- if not link_exists?("#{path}/#{filename}#{extension}")
351
- raise "attempting installation on #{host}, #{path}/#{filename}#{extension} does not exist"
352
- end
353
- if host.is_cygwin?
354
- on host, "cd #{host['working_dir']}; curl -O #{path}/#{filename}#{extension}"
355
- else
356
- on host, powershell("$webclient = New-Object System.Net.WebClient; $webclient.DownloadFile('#{path}/#{filename}#{extension}','#{host['working_dir']}\\#{filename}#{extension}')")
357
- end
358
- end
359
- end
360
-
361
- #Determine the PE package to download/upload on a unix style host, download/upload that package onto the host
362
- #and unpack it.
363
- # @param [Host] host The unix style host to download/upload and unpack PE onto
364
- # @param [Hash{Symbol=>Symbol, String}] opts The options
365
- # @option opts [String] :pe_dir Default directory or URL to pull PE package from
366
- # (Otherwise uses individual hosts pe_dir)
367
- # @api private
368
- def fetch_puppet_on_unix(host, opts)
369
- path = host['pe_dir'] || opts[:pe_dir]
370
- local = File.directory?(path)
371
- filename = "#{host['dist']}"
372
- if local
373
- extension = File.exists?("#{path}/#{filename}.tar.gz") ? ".tar.gz" : ".tar"
374
- if not File.exists?("#{path}/#{filename}#{extension}")
375
- raise "attempting installation on #{host}, #{path}/#{filename}#{extension} does not exist"
376
- end
377
- scp_to host, "#{path}/#{filename}#{extension}", "#{host['working_dir']}/#{filename}#{extension}"
378
- if extension =~ /gz/
379
- on host, "cd #{host['working_dir']}; gunzip #{filename}#{extension}"
380
- end
381
- if extension =~ /tar/
382
- on host, "cd #{host['working_dir']}; tar -xvf #{filename}.tar"
383
- end
384
- else
385
- if host['platform'] =~ /eos/
386
- extension = '.swix'
387
- else
388
- extension = link_exists?("#{path}/#{filename}.tar.gz") ? ".tar.gz" : ".tar"
389
- end
390
- if not link_exists?("#{path}/#{filename}#{extension}")
391
- raise "attempting installation on #{host}, #{path}/#{filename}#{extension} does not exist"
392
- end
393
-
394
- if host['platform'] =~ /eos/
395
- commands = ['enable', "copy #{path}/#{filename}#{extension} extension:"]
396
- command = commands.join("\n")
397
- on host, "Cli -c '#{command}'"
398
- else
399
- unpack = 'tar -xvf -'
400
- unpack = extension =~ /gz/ ? 'gunzip | ' + unpack : unpack
401
- on host, "cd #{host['working_dir']}; curl #{path}/#{filename}#{extension} | #{unpack}"
402
- end
403
- end
404
- end
405
-
406
- #Determine the PE package to download/upload per-host, download/upload that package onto the host
407
- #and unpack it.
408
- # @param [Array<Host>] hosts The hosts to download/upload and unpack PE onto
409
- # @param [Hash{Symbol=>Symbol, String}] opts The options
410
- # @option opts [String] :pe_dir Default directory or URL to pull PE package from
411
- # (Otherwise uses individual hosts pe_dir)
412
- # @option opts [String] :pe_ver Default PE version to install or upgrade to
413
- # (Otherwise uses individual hosts pe_ver)
414
- # @option opts [String] :pe_ver_win Default PE version to install or upgrade to on Windows hosts
415
- # (Otherwise uses individual Windows hosts pe_ver)
416
- # @api private
417
- def fetch_puppet(hosts, opts)
418
- hosts.each do |host|
419
- # We install Puppet from the master for frictionless installs, so we don't need to *fetch* anything
420
- next if host['roles'].include?('frictionless') && (! version_is_less(opts[:pe_ver] || host['pe_ver'], '3.2.0'))
421
-
422
- if host['platform'] =~ /windows/
423
- fetch_puppet_on_windows(host, opts)
424
- elsif host['platform'] =~ /osx/
425
- fetch_puppet_on_mac(host, opts)
426
- else
427
- fetch_puppet_on_unix(host, opts)
428
- end
429
- end
430
- end
431
-
432
- #Classify the master so that it can deploy frictionless packages for a given host.
433
- # @param [Host] host The host to install pacakges for
434
- # @api private
435
- def deploy_frictionless_to_master(host)
436
- klass = host['platform'].gsub(/-/, '_').gsub(/\./,'')
437
- klass = "pe_repo::platform::#{klass}"
438
- on dashboard, "cd /opt/puppet/share/puppet-dashboard && /opt/puppet/bin/bundle exec /opt/puppet/bin/rake nodeclass:add[#{klass},skip]"
439
- on dashboard, "cd /opt/puppet/share/puppet-dashboard && /opt/puppet/bin/bundle exec /opt/puppet/bin/rake node:add[#{master},,,skip]"
440
- on dashboard, "cd /opt/puppet/share/puppet-dashboard && /opt/puppet/bin/bundle exec /opt/puppet/bin/rake node:addclass[#{master},#{klass}]"
441
- on master, puppet("agent -t"), :acceptable_exit_codes => [0,2]
442
- end
443
-
444
- #Perform a Puppet Enterprise upgrade or install
445
- # @param [Array<Host>] hosts The hosts to install or upgrade PE on
446
- # @param [Hash{Symbol=>Symbol, String}] opts The options
447
- # @option opts [String] :pe_dir Default directory or URL to pull PE package from
448
- # (Otherwise uses individual hosts pe_dir)
449
- # @option opts [String] :pe_ver Default PE version to install or upgrade to
450
- # (Otherwise uses individual hosts pe_ver)
451
- # @option opts [String] :pe_ver_win Default PE version to install or upgrade to on Windows hosts
452
- # (Otherwise uses individual Windows hosts pe_ver)
453
- # @option opts [Symbol] :type (:install) One of :upgrade or :install
454
- # @option opts [Boolean] :set_console_password Should we set the PE console password in the answers file? Used during upgrade only.
455
- # @option opts [Hash<String>] :answers Pre-set answers based upon ENV vars and defaults
456
- # (See {Beaker::Options::Presets.env_vars})
457
- #
458
- # @example
459
- # do_install(hosts, {:type => :upgrade, :pe_dir => path, :pe_ver => version, :pe_ver_win => version_win})
460
- #
461
- # @api private
462
- #
463
- def do_install hosts, opts = {}
464
- masterless = (defined? options) ? options[:masterless] : false
465
- opts[:masterless] = masterless # has to pass masterless down for answer generation awareness
466
- opts[:type] = opts[:type] || :install
467
- unless masterless
468
- pre30database = version_is_less(opts[:pe_ver] || database['pe_ver'], '3.0')
469
- pre30master = version_is_less(opts[:pe_ver] || master['pe_ver'], '3.0')
470
-
471
- unless version_is_less(opts[:pe_ver] || master['pe_ver'], '3.4')
472
- master['puppetservice'] = 'pe-puppetserver'
473
- end
474
- end
475
-
476
- # Set PE distribution for all the hosts, create working dir
477
- use_all_tar = ENV['PE_USE_ALL_TAR'] == 'true'
478
- hosts.each do |host|
479
- host['pe_installer'] ||= 'puppet-enterprise-installer'
480
- if host['platform'] !~ /windows|osx/
481
- platform = use_all_tar ? 'all' : host['platform']
482
- version = host['pe_ver'] || opts[:pe_ver]
483
- host['dist'] = "puppet-enterprise-#{version}-#{platform}"
484
- elsif host['platform'] =~ /osx/
485
- version = host['pe_ver'] || opts[:pe_ver]
486
- host['dist'] = "puppet-enterprise-#{version}-#{host['platform']}"
487
- elsif host['platform'] =~ /windows/
488
- version = host[:pe_ver] || opts['pe_ver_win']
489
- should_install_64bit = !(version_is_less(version, '3.4')) && host.is_x86_64? && !host['install_32'] && !opts['install_32']
490
- #only install 64bit builds if
491
- # - we are on pe version 3.4+
492
- # - we do not have install_32 set on host
493
- # - we do not have install_32 set globally
494
- if !(version_is_less(version, '4.0'))
495
- if should_install_64bit
496
- host['dist'] = "puppet-agent-#{version}-x64"
497
- else
498
- host['dist'] = "puppet-agent-#{version}-x86"
499
- end
500
- elsif should_install_64bit
501
- host['dist'] = "puppet-enterprise-#{version}-x64"
502
- else
503
- host['dist'] = "puppet-enterprise-#{version}"
504
- end
505
- end
506
- host['working_dir'] = host.tmpdir(Time.new.strftime("%Y-%m-%d_%H.%M.%S"))
507
- end
508
-
509
- fetch_puppet(hosts, opts)
510
-
511
- install_hosts = hosts.dup
512
- unless masterless
513
- # If we're installing a database version less than 3.0, ignore the database host
514
- install_hosts.delete(database) if pre30database and database != master and database != dashboard
515
- end
516
-
517
- install_hosts.each do |host|
518
- if host['platform'] =~ /windows/
519
- on host, installer_cmd(host, opts)
520
- if not host.is_cygwin?
521
- # HACK: for some reason, post install we need to refresh the connection to make puppet available for execution
522
- host.close
523
- end
524
- else
525
- # We only need answers if we're using the classic installer
526
- version = host['pe_ver'] || opts[:pe_ver]
527
- if host['roles'].include?('frictionless') && (! version_is_less(version, '3.2.0'))
528
- # If We're *not* running the classic installer, we want
529
- # to make sure the master has packages for us.
530
- deploy_frictionless_to_master(host)
531
- on host, installer_cmd(host, opts)
532
- elsif host['platform'] =~ /osx|eos/
533
- # If we're not frictionless, we need to run the OSX special-case
534
- on host, installer_cmd(host, opts)
535
- #set the certname and master
536
- on host, puppet("config set server #{master}")
537
- on host, puppet("config set certname #{host}")
538
- #run once to request cert
539
- acceptable_codes = host['platform'] =~ /osx/ ? [1] : [0, 1]
540
- on host, puppet_agent('-t'), :acceptable_exit_codes => acceptable_codes
541
- else
542
- answers = Beaker::Answers.create(opts[:pe_ver] || host['pe_ver'], hosts, opts)
543
- create_remote_file host, "#{host['working_dir']}/answers", answers.answer_string(host)
544
- on host, installer_cmd(host, opts)
545
- end
546
- end
547
-
548
- # On each agent, we ensure the certificate is signed then shut down the agent
549
- sign_certificate_for(host) unless masterless
550
- stop_agent_on(host)
551
- end
552
-
553
- unless masterless
554
- # Wait for PuppetDB to be totally up and running (post 3.0 version of pe only)
555
- sleep_until_puppetdb_started(database) unless pre30database
556
-
557
- # Run the agent once to ensure everything is in the dashboard
558
- install_hosts.each do |host|
559
- on host, puppet_agent('-t'), :acceptable_exit_codes => [0,2]
560
-
561
- # Workaround for PE-1105 when deploying 3.0.0
562
- # The installer did not respect our database host answers in 3.0.0,
563
- # and would cause puppetdb to be bounced by the agent run. By sleeping
564
- # again here, we ensure that if that bounce happens during an upgrade
565
- # test we won't fail early in the install process.
566
- if host['pe_ver'] == '3.0.0' and host == database
567
- sleep_until_puppetdb_started(database)
568
- end
569
- end
570
-
571
- install_hosts.each do |host|
572
- wait_for_host_in_dashboard(host)
573
- end
574
-
575
- if pre30master
576
- task = 'nodegroup:add_all_nodes group=default'
577
- else
578
- task = 'defaultgroup:ensure_default_group'
579
- end
580
- on dashboard, "/opt/puppet/bin/rake -sf /opt/puppet/share/puppet-dashboard/Rakefile #{task} RAILS_ENV=production"
581
-
582
- # Now that all hosts are in the dashbaord, run puppet one more
583
- # time to configure mcollective
584
- on install_hosts, puppet_agent('-t'), :acceptable_exit_codes => [0,2]
585
- end
586
- end
587
-
588
- #Perform a Puppet Enterprise Higgs install up until web browser interaction is required, runs on linux hosts only.
589
- # @param [Host] host The host to install higgs on
590
- # @param [Hash{Symbol=>Symbol, String}] opts The options
591
- # @option opts [String] :pe_dir Default directory or URL to pull PE package from
592
- # (Otherwise uses individual hosts pe_dir)
593
- # @option opts [String] :pe_ver Default PE version to install
594
- # (Otherwise uses individual hosts pe_ver)
595
- # @raise [StandardError] When installation times out
596
- #
597
- # @example
598
- # do_higgs_install(master, {:pe_dir => path, :pe_ver => version})
599
- #
600
- # @api private
601
- #
602
- def do_higgs_install host, opts
603
- use_all_tar = ENV['PE_USE_ALL_TAR'] == 'true'
604
- platform = use_all_tar ? 'all' : host['platform']
605
- version = host['pe_ver'] || opts[:pe_ver]
606
- host['dist'] = "puppet-enterprise-#{version}-#{platform}"
607
-
608
- use_all_tar = ENV['PE_USE_ALL_TAR'] == 'true'
609
- host['pe_installer'] ||= 'puppet-enterprise-installer'
610
- host['working_dir'] = host.tmpdir(Time.new.strftime("%Y-%m-%d_%H.%M.%S"))
611
-
612
- fetch_puppet([host], opts)
613
-
614
- host['higgs_file'] = "higgs_#{File.basename(host['working_dir'])}.log"
615
- on host, higgs_installer_cmd(host), opts
616
-
617
- #wait for output to host['higgs_file']
618
- #we're all done when we find this line in the PE installation log
619
- higgs_re = /Please\s+go\s+to\s+https:\/\/.*\s+in\s+your\s+browser\s+to\s+continue\s+installation/m
620
- res = Result.new(host, 'tmp cmd')
621
- tries = 10
622
- attempts = 0
623
- prev_sleep = 0
624
- cur_sleep = 1
625
- while (res.stdout !~ higgs_re) and (attempts < tries)
626
- res = on host, "cd #{host['working_dir']}/#{host['dist']} && cat #{host['higgs_file']}", :acceptable_exit_codes => (0..255)
627
- attempts += 1
628
- sleep( cur_sleep )
629
- prev_sleep = cur_sleep
630
- cur_sleep = cur_sleep + prev_sleep
631
- end
632
-
633
- if attempts >= tries
634
- raise "Failed to kick off PE (Higgs) web installation"
635
- end
636
-
637
- end
638
-
639
- #Sort array of hosts so that it has the correct order for PE installation based upon each host's role
640
- # @example
641
- # h = sorted_hosts
642
- #
643
- # @note Order for installation should be
644
- # First : master
645
- # Second: database host (if not same as master)
646
- # Third: dashboard (if not same as master or database)
647
- # Fourth: everything else
648
- #
649
- # @!visibility private
650
- def sorted_hosts
651
- special_nodes = []
652
- [master, database, dashboard].uniq.each do |host|
653
- special_nodes << host if host != nil
654
- end
655
- real_agents = agents - special_nodes
656
- special_nodes + real_agents
657
- end
658
-
659
- #Install FOSS based upon host configuration and options
660
- # @example will install puppet 3.6.1 from native puppetlabs provided packages wherever possible and will fail over to gem installation when impossible
661
- # install_puppet({
662
- # :version => '3.6.1',
663
- # :facter_version => '2.0.1',
664
- # :hiera_version => '1.3.3',
665
- # :default_action => 'gem_install',
666
- #
667
- # })
668
- #
669
- #
670
- # @example Will install latest packages on Enterprise Linux and Debian based distros and fail hard on all othere platforms.
671
- # install_puppet()
672
- #
673
- # @note This will attempt to add a repository for apt.puppetlabs.com on
674
- # Debian, Ubuntu, or Cumulus machines, or yum.puppetlabs.com on EL or Fedora
675
- # machines, then install the package 'puppet'.
676
- # @param [Hash{Symbol=>String}] opts
677
- # @option opts [String] :version Version of puppet to download
678
- # @option opts [String] :mac_download_url Url to download msi pattern of %url%/puppet-%version%.msi
679
- # @option opts [String] :win_download_url Url to download dmg pattern of %url%/(puppet|hiera|facter)-%version%.msi
680
- #
681
- # @api dsl
682
- # @return nil
683
- # @raise [StandardError] When encountering an unsupported platform by default, or if gem cannot be found when default_action => 'gem_install'
684
- # @raise [FailTest] When error occurs during the actual installation process
685
- def install_puppet(opts = {})
686
- default_download_url = 'http://downloads.puppetlabs.com'
687
- opts = {:win_download_url => "#{default_download_url}/windows",
688
- :mac_download_url => "#{default_download_url}/mac"}.merge(opts)
689
- hosts.each do |host|
690
- if host['platform'] =~ /el-(5|6|7)/
691
- relver = $1
692
- install_puppet_from_rpm host, opts.merge(:release => relver, :family => 'el')
693
- elsif host['platform'] =~ /fedora-(\d+)/
694
- relver = $1
695
- install_puppet_from_rpm host, opts.merge(:release => relver, :family => 'fedora')
696
- elsif host['platform'] =~ /(ubuntu|debian|cumulus)/
697
- install_puppet_from_deb host, opts
698
- elsif host['platform'] =~ /windows/
699
- relver = opts[:version]
700
- install_puppet_from_msi host, opts
701
- elsif host['platform'] =~ /osx/
702
- install_puppet_from_dmg host, opts
703
- else
704
- if opts[:default_action] == 'gem_install'
705
- install_puppet_from_gem host, opts
706
- else
707
- raise "install_puppet() called for unsupported platform '#{host['platform']}' on '#{host.name}'"
708
- end
709
- end
710
-
711
- # Certain install paths may not create the config dirs/files needed
712
- on host, "mkdir -p #{host['puppetpath']}" unless host[:type] =~ /aio/
713
- on host, "echo '' >> #{host.puppet['hiera_config']}"
714
- end
715
- nil
716
- end
717
-
718
- # Configure a host entry on the give host
719
- # @example: will add a host entry for forge.puppetlabs.com
720
- # add_system32_hosts_entry(host, { :ip => '23.251.154.122', :name => 'forge.puppetlabs.com' })
721
- #
722
- # @api dsl
723
- # @return nil
724
- def add_system32_hosts_entry(host, opts = {})
725
- if host['platform'] =~ /windows/
726
- hosts_file = "C:\\Windows\\System32\\Drivers\\etc\\hosts"
727
- host_entry = "#{opts['ip']}`t`t#{opts['name']}"
728
- on host, powershell("\$text = \\\"#{host_entry}\\\"; Add-Content -path '#{hosts_file}' -value \$text")
729
- else
730
- raise "nothing to do for #{host.name} on #{host['platform']}"
731
- end
732
- end
733
-
734
- # Configure puppet.conf for all hosts based upon a provided Hash
735
- # @param [Hash{Symbol=>String}] opts
736
- # @option opts [Hash{String=>String}] :main configure the main section of puppet.conf
737
- # @option opts [Hash{String=>String}] :agent configure the agent section of puppet.conf
738
- #
739
- # @api dsl
740
- # @return nil
741
- def configure_puppet(opts={})
742
- hosts.each do |host|
743
- configure_puppet_on(host,opts)
744
- end
745
- end
746
-
747
- # Configure puppet.conf on the given host based upon a provided hash
748
- # @param [Host] host The host to configure puppet.conf on
749
- # @param [Hash{Symbol=>String}] opts
750
- # @option opts [Hash{String=>String}] :main configure the main section of puppet.conf
751
- # @option opts [Hash{String=>String}] :agent configure the agent section of puppet.conf
752
- #
753
- # @example will configure /etc/puppet.conf on the puppet master.
754
- # config = {
755
- # 'main' => {
756
- # 'server' => 'testbox.test.local',
757
- # 'certname' => 'testbox.test.local',
758
- # 'logdir' => '/var/log/puppet',
759
- # 'vardir' => '/var/lib/puppet',
760
- # 'ssldir' => '/var/lib/puppet/ssl',
761
- # 'rundir' => '/var/run/puppet'
762
- # },
763
- # 'agent' => {
764
- # 'environment' => 'dev'
765
- # }
766
- # }
767
- # configure_puppet(master, config)
768
- #
769
- # @api dsl
770
- # @return nil
771
- def configure_puppet_on(host, opts = {})
772
- if host['platform'] =~ /windows/
773
- puppet_conf = host.puppet['config']
774
- conf_data = ''
775
- opts.each do |section,options|
776
- conf_data << "[#{section}]`n"
777
- options.each do |option,value|
778
- conf_data << "#{option}=#{value}`n"
779
- end
780
- conf_data << "`n"
781
- end
782
- on host, powershell("\$text = \\\"#{conf_data}\\\"; Set-Content -path '#{puppet_conf}' -value \$text")
783
- else
784
- puppet_conf = host.puppet['config']
785
- conf_data = ''
786
- opts.each do |section,options|
787
- conf_data << "[#{section}]\n"
788
- options.each do |option,value|
789
- conf_data << "#{option}=#{value}\n"
790
- end
791
- conf_data << "\n"
792
- end
793
- on host, "echo \"#{conf_data}\" > #{puppet_conf}"
794
- end
795
- end
796
-
797
- # Installs Puppet and dependencies using rpm
798
- #
799
- # @param [Host] host The host to install packages on
800
- # @param [Hash{Symbol=>String}] opts An options hash
801
- # @option opts [String] :version The version of Puppet to install, if nil installs latest version
802
- # @option opts [String] :facter_version The version of Facter to install, if nil installs latest version
803
- # @option opts [String] :hiera_version The version of Hiera to install, if nil installs latest version
804
- # @option opts [String] :default_action What to do if we don't know how to install native packages on host.
805
- # Valid value is 'gem_install' or nil. If nil raises an exception when
806
- # on an unsupported platform. When 'gem_install' attempts to install
807
- # Puppet via gem.
808
- # @option opts [String] :release The major release of the OS
809
- # @option opts [String] :family The OS family (one of 'el' or 'fedora')
810
- #
811
- # @return nil
812
- # @api private
813
- def install_puppet_from_rpm( host, opts )
814
- release_package_string = "http://yum.puppetlabs.com/puppetlabs-release-#{opts[:family]}-#{opts[:release]}.noarch.rpm"
815
-
816
- on host, "rpm -q --quiet puppetlabs-release || rpm -ivh #{release_package_string}"
817
-
818
- if opts[:facter_version]
819
- on host, "yum install -y facter-#{opts[:facter_version]}"
820
- end
821
-
822
- if opts[:hiera_version]
823
- on host, "yum install -y hiera-#{opts[:hiera_version]}"
824
- end
825
-
826
- puppet_pkg = opts[:version] ? "puppet-#{opts[:version]}" : 'puppet'
827
- on host, "yum install -y #{puppet_pkg}"
828
- end
829
-
830
- # Installs Puppet and dependencies from deb
831
- #
832
- # @param [Host] host The host to install packages on
833
- # @param [Hash{Symbol=>String}] opts An options hash
834
- # @option opts [String] :version The version of Puppet to install, if nil installs latest version
835
- # @option opts [String] :facter_version The version of Facter to install, if nil installs latest version
836
- # @option opts [String] :hiera_version The version of Hiera to install, if nil installs latest version
837
- #
838
- # @return nil
839
- # @api private
840
- def install_puppet_from_deb( host, opts )
841
- if ! host.check_for_package 'lsb-release'
842
- host.install_package('lsb-release')
843
- end
844
-
845
- if ! host.check_for_command 'curl'
846
- on host, 'apt-get install -y curl'
847
- end
848
-
849
- on host, 'curl -O http://apt.puppetlabs.com/puppetlabs-release-$(lsb_release -c -s).deb'
850
- on host, 'dpkg -i puppetlabs-release-$(lsb_release -c -s).deb'
851
- on host, 'apt-get update'
852
-
853
- if opts[:facter_version]
854
- on host, "apt-get install -y facter=#{opts[:facter_version]}-1puppetlabs1"
855
- end
856
-
857
- if opts[:hiera_version]
858
- on host, "apt-get install -y hiera=#{opts[:hiera_version]}-1puppetlabs1"
859
- end
860
-
861
- if opts[:version]
862
- on host, "apt-get install -y puppet-common=#{opts[:version]}-1puppetlabs1"
863
- on host, "apt-get install -y puppet=#{opts[:version]}-1puppetlabs1"
864
- else
865
- on host, 'apt-get install -y puppet'
866
- end
867
- end
868
-
869
- # Installs Puppet and dependencies from msi
870
- #
871
- # @param [Host] host The host to install packages on
872
- # @param [Hash{Symbol=>String}] opts An options hash
873
- # @option opts [String] :version The version of Puppet to install, required
874
- # @option opts [String] :win_download_url The url to download puppet from
875
- def install_puppet_from_msi( host, opts )
876
- #only install 64bit builds if
877
- # - we are on puppet version 3.7+
878
- # - we do not have install_32 set on host
879
- # - we do not have install_32 set globally
880
- version = opts[:version]
881
- if !(version_is_less(version, '3.7')) and host.is_x86_64? and not host['install_32'] and not opts['install_32']
882
- host['dist'] = "puppet-#{version}-x64"
883
- else
884
- host['dist'] = "puppet-#{version}"
885
- end
886
- link = "#{opts[:win_download_url]}/#{host['dist']}.msi"
887
- if not link_exists?( link )
888
- raise "Puppet #{version} at #{link} does not exist!"
889
- end
890
-
891
- if host.is_cygwin?
892
- dest = "#{host['dist']}.msi"
893
- on host, "curl -O #{link}"
894
-
895
- #Because the msi installer doesn't add Puppet to the environment path
896
- #Add both potential paths for simplicity
897
- #NOTE - this is unnecessary if the host has been correctly identified as 'foss' during set up
898
- puppetbin_path = "\"/cygdrive/c/Program Files (x86)/Puppet Labs/Puppet/bin\":\"/cygdrive/c/Program Files/Puppet Labs/Puppet/bin\""
899
- on host, %Q{ echo 'export PATH=$PATH:#{puppetbin_path}' > /etc/bash.bashrc }
900
- else
901
- dest = "C:\\Windows\\Temp\\#{host['dist']}.msi"
902
-
903
- on host, "set PATH=\"%PATH%;#{host['puppetbindir']}\""
904
- on host, "setx PATH \"%PATH%;#{host['puppetbindir']}\""
905
-
906
- on host, powershell("$webclient = New-Object System.Net.WebClient; $webclient.DownloadFile('#{link}','#{dest}')")
907
-
908
- on host, "if not exist #{host['distmoduledir']} (md #{host['distmoduledir']})"
909
- end
910
-
911
- on host, "cmd /C 'start /w msiexec.exe /qn /i #{dest}'"
912
- end
913
-
914
- # Installs Puppet and dependencies from dmg
915
- #
916
- # @param [Host] host The host to install packages on
917
- # @param [Hash{Symbol=>String}] opts An options hash
918
- # @option opts [String] :version The version of Puppet to install, required
919
- # @option opts [String] :facter_version The version of Facter to install, required
920
- # @option opts [String] :hiera_version The version of Hiera to install, required
921
- # @option opts [String] :mac_download_url Url to download msi pattern of %url%/puppet-%version%.msi
922
- #
923
- # @return nil
924
- # @api private
925
- def install_puppet_from_dmg( host, opts )
926
-
927
- puppet_ver = opts[:version]
928
- facter_ver = opts[:facter_version]
929
- hiera_ver = opts[:hiera_version]
930
-
931
- if [puppet_ver, facter_ver, hiera_ver].include?(nil)
932
- raise "You need to specify versions for OSX host\n eg. install_puppet({:version => '3.6.2',:facter_version => '2.1.0',:hiera_version => '1.3.4',})"
933
- end
934
-
935
- on host, "curl -O #{opts[:mac_download_url]}/puppet-#{puppet_ver}.dmg"
936
- on host, "curl -O #{opts[:mac_download_url]}/facter-#{facter_ver}.dmg"
937
- on host, "curl -O #{opts[:mac_download_url]}/hiera-#{hiera_ver}.dmg"
938
-
939
- on host, "hdiutil attach puppet-#{puppet_ver}.dmg"
940
- on host, "hdiutil attach facter-#{facter_ver}.dmg"
941
- on host, "hdiutil attach hiera-#{hiera_ver}.dmg"
942
-
943
- on host, "installer -pkg /Volumes/puppet-#{puppet_ver}/puppet-#{puppet_ver}.pkg -target /"
944
- on host, "installer -pkg /Volumes/facter-#{facter_ver}/facter-#{facter_ver}.pkg -target /"
945
- on host, "installer -pkg /Volumes/hiera-#{hiera_ver}/hiera-#{hiera_ver}.pkg -target /"
946
- end
947
-
948
- # Installs Puppet and dependencies from gem
949
- #
950
- # @param [Host] host The host to install packages on
951
- # @param [Hash{Symbol=>String}] opts An options hash
952
- # @option opts [String] :version The version of Puppet to install, if nil installs latest
953
- # @option opts [String] :facter_version The version of Facter to install, if nil installs latest
954
- # @option opts [String] :hiera_version The version of Hiera to install, if nil installs latest
955
- #
956
- # @return nil
957
- # @raise [StandardError] if gem does not exist on target host
958
- # @api private
959
- def install_puppet_from_gem( host, opts )
960
- # There are a lot of special things to do for Solaris and Solaris 10.
961
- # This is easier than checking host['platform'] every time.
962
- is_solaris10 = host['platform'] =~ /solaris-10/
963
- is_solaris = host['platform'] =~ /solaris/
964
-
965
- # Hosts may be provisioned with csw but pkgutil won't be in the
966
- # PATH by default to avoid changing the behavior for Puppet's tests
967
- if is_solaris10
968
- on host, 'ln -s /opt/csw/bin/pkgutil /usr/bin/pkgutil'
969
- end
970
-
971
- # Solaris doesn't necessarily have this, but gem needs it
972
- if is_solaris
973
- on host, 'mkdir -p /var/lib'
974
- end
975
-
976
- unless host.check_for_command( 'gem' )
977
- gempkg = case host['platform']
978
- when /solaris-11/ then 'ruby-18'
979
- when /ubuntu-14/ then 'ruby'
980
- when /solaris-10|ubuntu|debian|el-|cumulus/ then 'rubygems'
981
- else
982
- raise "install_puppet() called with default_action " +
983
- "'gem_install' but program `gem' is " +
984
- "not installed on #{host.name}"
985
- end
986
-
987
- host.install_package gempkg
988
- end
989
-
990
- # Link 'gem' to /usr/bin instead of adding /opt/csw/bin to PATH.
991
- if is_solaris10
992
- on host, 'ln -s /opt/csw/bin/gem /usr/bin/gem'
993
- end
994
-
995
- if host['platform'] =~ /debian|ubuntu|solaris|cumulus/
996
- gem_env = YAML.load( on( host, 'gem environment' ).stdout )
997
- gem_paths_array = gem_env['RubyGems Environment'].find {|h| h['GEM PATHS'] != nil }['GEM PATHS']
998
- path_with_gem = 'export PATH=' + gem_paths_array.join(':') + ':${PATH}'
999
- on host, "echo '#{path_with_gem}' >> ~/.bashrc"
1000
- end
1001
-
1002
- if opts[:facter_version]
1003
- on host, "gem install facter -v#{opts[:facter_version]} --no-ri --no-rdoc"
1004
- end
1005
-
1006
- if opts[:hiera_version]
1007
- on host, "gem install hiera -v#{opts[:hiera_version]} --no-ri --no-rdoc"
1008
- end
1009
-
1010
- ver_cmd = opts[:version] ? "-v#{opts[:version]}" : ''
1011
- on host, "gem install puppet #{ver_cmd} --no-ri --no-rdoc"
1012
-
1013
- # Similar to the treatment of 'gem' above.
1014
- # This avoids adding /opt/csw/bin to PATH.
1015
- if is_solaris
1016
- gem_env = YAML.load( on( host, 'gem environment' ).stdout )
1017
- # This is the section we want - this has the dir where gem executables go.
1018
- env_sect = 'EXECUTABLE DIRECTORY'
1019
- # Get the directory where 'gem' installs executables.
1020
- # On Solaris 10 this is usually /opt/csw/bin
1021
- gem_exec_dir = gem_env['RubyGems Environment'].find {|h| h[env_sect] != nil }[env_sect]
1022
-
1023
- on host, "ln -s #{gem_exec_dir}/hiera /usr/bin/hiera"
1024
- on host, "ln -s #{gem_exec_dir}/facter /usr/bin/facter"
1025
- on host, "ln -s #{gem_exec_dir}/puppet /usr/bin/puppet"
1026
- end
1027
- end
1028
-
1029
-
1030
- #Install PE based upon host configuration and options
1031
- # @example
1032
- # install_pe
1033
- #
1034
- # @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.
1035
- # Install file names are assumed to be of the format puppet-enterprise-VERSION-PLATFORM.(tar)|(tar.gz)
1036
- # for Unix like systems and puppet-enterprise-VERSION.msi for Windows systems.
1037
- #
1038
- # @api dsl
1039
- def install_pe
1040
- #process the version files if necessary
1041
- hosts.each do |host|
1042
- host['pe_dir'] ||= options[:pe_dir]
1043
- if host['platform'] =~ /windows/
1044
- host['pe_ver'] = host['pe_ver'] || options['pe_ver'] ||
1045
- Beaker::Options::PEVersionScraper.load_pe_version(host[:pe_dir] || options[:pe_dir], options[:pe_version_file_win])
1046
- else
1047
- host['pe_ver'] = host['pe_ver'] || options['pe_ver'] ||
1048
- Beaker::Options::PEVersionScraper.load_pe_version(host[:pe_dir] || options[:pe_dir], options[:pe_version_file])
1049
- end
1050
- end
1051
- #send in the global options hash
1052
- do_install sorted_hosts, options
1053
- end
1054
-
1055
- #Upgrade PE based upon host configuration and options
1056
- # @param [String] path A path (either local directory or a URL to a listing of PE builds).
1057
- # Will contain a LATEST file indicating the latest build to install.
1058
- # This is ignored if a pe_upgrade_ver and pe_upgrade_dir are specified
1059
- # in the host configuration file.
1060
- # @example
1061
- # upgrade_pe("http://neptune.puppetlabs.lan/3.0/ci-ready/")
1062
- #
1063
- # @note Install file names are assumed to be of the format puppet-enterprise-VERSION-PLATFORM.(tar)|(tar.gz)
1064
- # for Unix like systems and puppet-enterprise-VERSION.msi for Windows systems.
1065
- # @api dsl
1066
- def upgrade_pe path=nil
1067
- set_console_password = false
1068
- # if we are upgrading from something lower than 3.4 then we need to set the pe console password
1069
- if (dashboard[:pe_ver] ? version_is_less(dashboard[:pe_ver], "3.4.0") : true)
1070
- set_console_password = true
1071
- end
1072
- # get new version information
1073
- hosts.each do |host|
1074
- host['pe_dir'] = host['pe_upgrade_dir'] || path
1075
- if host['platform'] =~ /windows/
1076
- host['pe_ver'] = host['pe_upgrade_ver'] || options['pe_upgrade_ver'] ||
1077
- Options::PEVersionScraper.load_pe_version(host['pe_dir'], options[:pe_version_file_win])
1078
- else
1079
- host['pe_ver'] = host['pe_upgrade_ver'] || options['pe_upgrade_ver'] ||
1080
- Options::PEVersionScraper.load_pe_version(host['pe_dir'], options[:pe_version_file])
1081
- end
1082
- if version_is_less(host['pe_ver'], '3.0')
1083
- host['pe_installer'] ||= 'puppet-enterprise-upgrader'
1084
- end
1085
- end
1086
- # send in the global options hash
1087
- do_install(sorted_hosts, options.merge({:type => :upgrade, :set_console_password => set_console_password}))
1088
- options['upgrade'] = true
1089
- end
1090
-
1091
- # Install official puppetlabs release repository configuration on host.
1092
- #
1093
- # @param [Host] host An object implementing {Beaker::Hosts}'s
1094
- # interface.
1095
- #
1096
- # @note This method only works on redhat-like and debian-like hosts.
1097
- #
1098
- def install_puppetlabs_release_repo ( host )
1099
- variant, version, arch, codename = host['platform'].to_array
1100
-
1101
- case variant
1102
- when /^(fedora|el|centos)$/
1103
- variant = (($1 == 'centos') ? 'el' : $1)
1104
-
1105
- rpm = options[:release_yum_repo_url] +
1106
- "/puppetlabs-release-%s-%s.noarch.rpm" % [variant, version]
1107
-
1108
- on host, "rpm -ivh #{rpm}"
1109
-
1110
- when /^(debian|ubuntu|cumulus)$/
1111
- deb = URI.join(options[:release_apt_repo_url], "puppetlabs-release-%s.deb" % codename)
1112
-
1113
- on host, "wget -O /tmp/puppet.deb #{deb}"
1114
- on host, "dpkg -i --force-all /tmp/puppet.deb"
1115
- on host, "apt-get update"
1116
- else
1117
- raise "No repository installation step for #{variant} yet..."
1118
- end
1119
- end
1120
-
1121
- # Install development repository on the given host. This method pushes all
1122
- # repository information including package files for the specified
1123
- # package_name to the host and modifies the repository configuration file
1124
- # to point at the new repository. This is particularly useful for
1125
- # installing development packages on hosts that can't access the builds
1126
- # server.
1127
- #
1128
- # @param [Host] host An object implementing {Beaker::Hosts}'s
1129
- # interface.
1130
- # @param [String] package_name The name of the package whose repository is
1131
- # being installed.
1132
- # @param [String] build_version A string identifying the output of a
1133
- # packaging job for use in looking up
1134
- # repository directory information
1135
- # @param [String] repo_configs_dir A local directory where repository files will be
1136
- # stored as an intermediate step before
1137
- # pushing them to the given host.
1138
- #
1139
- # @note This method only works on redhat-like and debian-like hosts.
1140
- #
1141
- def install_puppetlabs_dev_repo ( host, package_name, build_version,
1142
- repo_configs_dir = 'tmp/repo_configs' )
1143
- variant, version, arch, codename = host['platform'].to_array
1144
- platform_configs_dir = File.join(repo_configs_dir, variant)
1145
-
1146
- # some of the uses of dev_builds_url below can't include protocol info,
1147
- # pluse this opens up possibility of switching the behavior on provided
1148
- # url type
1149
- _, protocol, hostname = options[:dev_builds_url].partition /.*:\/\//
1150
- dev_builds_url = protocol + hostname
1151
-
1152
- on host, "mkdir -p /root/#{package_name}"
1153
-
1154
- case variant
1155
- when /^(fedora|el|centos)$/
1156
- variant = (($1 == 'centos') ? 'el' : $1)
1157
- fedora_prefix = ((variant == 'fedora') ? 'f' : '')
1158
-
1159
- if host.is_pe?
1160
- pattern = "pl-%s-%s-repos-pe-%s-%s%s-%s.repo"
1161
- else
1162
- pattern = "pl-%s-%s-%s-%s%s-%s.repo"
1163
- end
1164
-
1165
- repo_filename = pattern % [
1166
- package_name,
1167
- build_version,
1168
- variant,
1169
- fedora_prefix,
1170
- version,
1171
- arch
1172
- ]
1173
-
1174
- repo = fetch_http_file( "%s/%s/%s/repo_configs/rpm/" %
1175
- [ dev_builds_url, package_name, build_version ],
1176
- repo_filename,
1177
- platform_configs_dir)
1178
-
1179
- link = "%s/%s/%s/repos/%s/%s%s/products/%s/" %
1180
- [ dev_builds_url, package_name, build_version, variant,
1181
- fedora_prefix, version, arch ]
1182
-
1183
- if not link_exists?( link )
1184
- link = "%s/%s/%s/repos/%s/%s%s/devel/%s/" %
1185
- [ dev_builds_url, package_name, build_version, variant,
1186
- fedora_prefix, version, arch ]
1187
- end
1188
-
1189
- if not link_exists?( link )
1190
- raise "Unable to reach a repo directory at #{link}"
1191
- end
1192
-
1193
- repo_dir = fetch_http_dir( link, platform_configs_dir )
1194
-
1195
- config_dir = '/etc/yum.repos.d/'
1196
- scp_to host, repo, config_dir
1197
- scp_to host, repo_dir, "/root/#{package_name}"
1198
-
1199
- search = "baseurl\\s*=\\s*http:\\/\\/#{hostname}.*$"
1200
- replace = "baseurl=file:\\/\\/\\/root\\/#{package_name}\\/#{arch}"
1201
- sed_command = "sed -i 's/#{search}/#{replace}/'"
1202
- find_and_sed = "find #{config_dir} -name \"*.repo\" -exec #{sed_command} {} \\;"
1203
-
1204
- on host, find_and_sed
1205
-
1206
- when /^(debian|ubuntu|cumulus)$/
1207
- list = fetch_http_file( "%s/%s/%s/repo_configs/deb/" %
1208
- [ dev_builds_url, package_name, build_version ],
1209
- "pl-%s-%s-%s.list" %
1210
- [ package_name, build_version, codename ],
1211
- platform_configs_dir )
1212
-
1213
- repo_dir = fetch_http_dir( "%s/%s/%s/repos/apt/%s" %
1214
- [ dev_builds_url, package_name,
1215
- build_version, codename ],
1216
- platform_configs_dir )
1217
-
1218
- config_dir = '/etc/apt/sources.list.d'
1219
- scp_to host, list, config_dir
1220
- scp_to host, repo_dir, "/root/#{package_name}"
1221
-
1222
- search = "deb\\s\\+http:\\/\\/#{hostname}.*$"
1223
- replace = "deb file:\\/\\/\\/root\\/#{package_name}\\/#{codename} #{codename} main"
1224
- sed_command = "sed -i 's/#{search}/#{replace}/'"
1225
- find_and_sed = "find #{config_dir} -name \"*.list\" -exec #{sed_command} {} \\;"
1226
-
1227
- on host, find_and_sed
1228
- on host, "apt-get update"
1229
-
1230
- else
1231
- raise "No repository installation step for #{variant} yet..."
1232
- end
1233
- end
1234
-
1235
- # Installs packages from the local development repository on the given host
1236
- #
1237
- # @param [Host] host An object implementing {Beaker::Hosts}'s
1238
- # interface.
1239
- # @param [Regexp] package_name The name of the package whose repository is
1240
- # being installed.
1241
- #
1242
- # @note This method only works on redhat-like and debian-like hosts.
1243
- # @note This method is paired to be run directly after {#install_puppetlabs_dev_repo}
1244
- #
1245
- def install_packages_from_local_dev_repo( host, package_name )
1246
- if host['platform'] =~ /debian|ubuntu|cumulus/
1247
- find_filename = '*.deb'
1248
- find_command = 'dpkg -i'
1249
- elsif host['platform'] =~ /fedora|el|centos/
1250
- find_filename = '*.rpm'
1251
- find_command = 'rpm -ivh'
1252
- else
1253
- raise "No repository installation step for #{host['platform']} yet..."
1254
- end
1255
- find_command = "find /root/#{package_name} -type f -name '#{find_filename}' -exec #{find_command} {} \\;"
1256
- on host, find_command
1257
- end
1258
-
1259
- # Install development repo of the puppet-agent on the given host
1260
- #
1261
- # @param [Host] host An object implementing {Beaker::Hosts}'s interface
1262
- # @param [Hash{Symbol=>String}] opts An options hash
1263
- # @option opts [String] :version The version of puppet-agent to install
1264
- # @option opts [String] :copy_base_local Directory where puppet-agent artifact
1265
- # will be stored locally
1266
- # (default: 'tmp/repo_configs')
1267
- # @option opts [String] :copy_dir_external Directory where puppet-agent
1268
- # artifact will be pushed to on the external machine
1269
- # (default: '/root')
1270
- # @return nil
1271
- def install_puppetagent_dev_repo( host, opts )
1272
- opts[:copy_base_local] ||= File.join('tmp', 'repo_configs')
1273
- opts[:copy_dir_external] ||= File.join('/', 'root')
1274
- variant, version, arch, codename = host['platform'].to_array
1275
- release_path = "#{options[:dev_builds_url]}/puppet-agent/#{opts[:version]}/artifacts/"
1276
- copy_dir_local = File.join(opts[:copy_base_local], variant)
1277
- onhost_copy_base = opts[:copy_dir_external]
1278
-
1279
- case variant
1280
- when /^(fedora|el|centos)$/
1281
- release_path << "el/#{version}/products/#{arch}"
1282
- release_file = "puppet-agent-#{opts[:version]}-1.#{arch}.rpm"
1283
- when /^(debian|ubuntu|cumulus)$/
1284
- release_path << "deb/#{codename}"
1285
- release_file = "puppet-agent_#{opts[:version]}-1_#{arch}.deb"
1286
- when /^windows$/
1287
- release_path << 'windows'
1288
- onhost_copy_base = '`cygpath -smF 35`/'
1289
- arch_suffix = arch =~ /64/ ? '64' : '86'
1290
- release_file = "puppet-agent-x#{arch_suffix}.msi"
1291
- else
1292
- raise "No repository installation step for #{variant} yet..."
1293
- end
1294
-
1295
- onhost_copied_file = File.join(onhost_copy_base, release_file)
1296
- fetch_http_file( release_path, release_file, copy_dir_local)
1297
- scp_to host, File.join(copy_dir_local, release_file), onhost_copy_base
1298
-
1299
- case variant
1300
- when /^(fedora|el|centos)$/
1301
- on host, "rpm -ivh #{onhost_copied_file}"
1302
- when /^(debian|ubuntu|cumulus)$/
1303
- on host, "dpkg -i --force-all #{onhost_copied_file}"
1304
- on host, "apt-get update"
1305
- when /^windows$/
1306
- result = on host, "echo #{onhost_copied_file}"
1307
- onhost_copied_file = result.raw_output.chomp
1308
- on host, Command.new("start /w #{onhost_copied_file}", [], { :cmdexe => true })
1309
- end
1310
- end
1311
-
1312
- #Install Higgs up till the point where you need to continue installation in a web browser, defaults to execution
1313
- #on the master node.
1314
- #@param [Host] higgs_host The host to install Higgs on (supported on linux platform only)
1315
- # @example
1316
- # install_higgs
1317
- #
1318
- # @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.
1319
- # Install file names are assumed to be of the format puppet-enterprise-VERSION-PLATFORM.(tar)|(tar.gz).
1320
- #
1321
- # @api dsl
1322
- def install_higgs( higgs_host = master )
1323
- #process the version files if necessary
1324
- master['pe_dir'] ||= options[:pe_dir]
1325
- master['pe_ver'] = master['pe_ver'] || options['pe_ver'] ||
1326
- Beaker::Options::PEVersionScraper.load_pe_version(master[:pe_dir] || options[:pe_dir], options[:pe_version_file])
1327
- if higgs_host['platform'] =~ /osx|windows/
1328
- raise "Attempting higgs installation on host #{higgs_host.name} with unsupported platform #{higgs_host['platform']}"
1329
- end
1330
- #send in the global options hash
1331
- do_higgs_install higgs_host, options
1332
- end
1333
-
1334
- # Install the desired module on all hosts using either the PMT or a
1335
- # staging forge
1336
- #
1337
- # @see install_dev_puppet_module
1338
- def install_dev_puppet_module_on( host, opts )
1339
- if options[:forge_host]
1340
- with_forge_stubbed_on( host ) do
1341
- install_puppet_module_via_pmt_on( host, opts )
1342
- end
1343
- else
1344
- copy_module_to( host, opts )
1345
- end
1346
- end
1347
- alias :puppet_module_install_on :install_dev_puppet_module_on
1348
-
1349
- # Install the desired module on all hosts using either the PMT or a
1350
- # staging forge
1351
- #
1352
- # Passes options through to either `install_puppet_module_via_pmt_on`
1353
- # or `copy_module_to`
1354
- #
1355
- # @param opts [Hash]
1356
- #
1357
- # @example Installing a module from the local directory
1358
- # install_dev_puppet_module( :source => './', :module_name => 'concat' )
1359
- #
1360
- # @example Installing a module from a staging forge
1361
- # options[:forge_host] = 'my-forge-api.example.com'
1362
- # install_dev_puppet_module( :source => './', :module_name => 'concat' )
1363
- #
1364
- # @see install_puppet_module_via_pmt
1365
- # @see copy_module_to
1366
- def install_dev_puppet_module( opts )
1367
- block_on( hosts ) {|h| install_dev_puppet_module_on( h, opts ) }
1368
- end
1369
- alias :puppet_module_install :install_dev_puppet_module
1370
-
1371
- # Install the desired module with the PMT on a given host
1372
- #
1373
- # @param opts [Hash]
1374
- # @option opts [String] :module_name The short name of the module to be installed
1375
- # @option opts [String] :version The version of the module to be installed
1376
- def install_puppet_module_via_pmt_on( host, opts = {} )
1377
- block_on host do |h|
1378
- version_info = opts[:version] ? "-v #{opts[:version]}" : ""
1379
- if opts[:source]
1380
- author_name, module_name = parse_for_modulename( opts[:source] )
1381
- modname = "#{author_name}-#{module_name}"
1382
- else
1383
- modname = opts[:module_name]
1384
- end
1385
-
1386
- puppet_opts = {}
1387
- if host[:default_module_install_opts].respond_to? :merge
1388
- puppet_opts = host[:default_module_install_opts].merge( puppet_opts )
1389
- end
1390
-
1391
- on h, puppet("module install #{modname} #{version_info}", puppet_opts)
1392
- end
1393
- end
1394
-
1395
- # Install the desired module with the PMT on all known hosts
1396
- # @see #install_puppet_module_via_pmt_on
1397
- def install_puppet_module_via_pmt( opts = {} )
1398
- install_puppet_module_via_pmt_on(hosts, opts)
1399
- end
1400
-
1401
- # Install local module for acceptance testing
1402
- # should be used as a presuite to ensure local module is copied to the hosts you want, particularly masters
1403
- # @api dsl
1404
- # @param [Host, Array<Host>, String, Symbol] one_or_more_hosts
1405
- # One or more hosts to act upon,
1406
- # or a role (String or Symbol) that identifies one or more hosts.
1407
- # @option opts [String] :source ('./')
1408
- # The current directory where the module sits, otherwise will try
1409
- # and walk the tree to figure out
1410
- # @option opts [String] :module_name (nil)
1411
- # Name which the module should be installed under, please do not include author,
1412
- # if none is provided it will attempt to parse the metadata.json and then the Modulefile to determine
1413
- # the name of the module
1414
- # @option opts [String] :target_module_path (host['distmoduledir']/modules)
1415
- # Location where the module should be installed, will default
1416
- # to host['distmoduledir']/modules
1417
- # @option opts [Array] :ignore_list
1418
- # @option opts [String] :protocol
1419
- # Name of the underlying transfer method. Valid options are 'scp' or 'rsync'.
1420
- # @raise [ArgumentError] if not host is provided or module_name is not provided and can not be found in Modulefile
1421
- #
1422
- def copy_module_to(one_or_more_hosts, opts = {})
1423
- block_on one_or_more_hosts do |host|
1424
- opts = {:source => './',
1425
- :target_module_path => host['distmoduledir'],
1426
- :ignore_list => PUPPET_MODULE_INSTALL_IGNORE}.merge(opts)
1427
- ignore_list = build_ignore_list(opts)
1428
- target_module_dir = on( host, "echo #{opts[:target_module_path]}" ).stdout.chomp
1429
- source_path = File.expand_path( opts[:source] )
1430
- source_dir = File.dirname(source_path)
1431
- source_name = File.basename(source_path)
1432
- if opts.has_key?(:module_name)
1433
- module_name = opts[:module_name]
1434
- else
1435
- _, module_name = parse_for_modulename( source_path )
1436
- end
1437
-
1438
- opts[:protocol] ||= 'scp'
1439
- case opts[:protocol]
1440
- when 'scp'
1441
- #move to the host
1442
- scp_to host, source_path, target_module_dir, {:ignore => ignore_list}
1443
- #rename to the selected module name, if not correct
1444
- cur_path = File.join(target_module_dir, source_name)
1445
- new_path = File.join(target_module_dir, module_name)
1446
- if cur_path != new_path
1447
- # NOTE: this will need to be updated to handle powershell only windows SUTs
1448
- on host, "mv #{cur_path} #{new_path}"
1449
- end
1450
- when 'rsync'
1451
- rsync_to host, source, File.join(target_module_dir, module_name), {:ignore => ignore_list}
1452
- else
1453
- logger.debug "Unsupported transfer protocol, returning nil"
1454
- nil
1455
- end
1456
- end
1457
- end
1458
- alias :copy_root_module_to :copy_module_to
1459
-
1460
- # Install a package on a host
1461
- #
1462
- # @param [Host] host A host object
1463
- # @param [String] package_name Name of the package to install
1464
- #
1465
- # @return [Result] An object representing the outcome of *install command*.
1466
- def install_package host, package_name, package_version = nil
1467
- host.install_package package_name, '', package_version
1468
- end
1469
-
1470
- # Check to see if a package is installed on a remote host
1471
- #
1472
- # @param [Host] host A host object
1473
- # @param [String] package_name Name of the package to check for.
1474
- #
1475
- # @return [Boolean] true/false if the package is found
1476
- def check_for_package host, package_name
1477
- host.check_for_package package_name
1478
- end
1479
-
1480
- # Upgrade a package on a host. The package must already be installed
1481
- #
1482
- # @param [Host] host A host object
1483
- # @param [String] package_name Name of the package to install
1484
- #
1485
- # @return [Result] An object representing the outcome of *upgrade command*.
1486
- def upgrade_package host, package_name
1487
- host.upgrade_package package_name
1488
- end
1489
-
1490
- #Recursive method for finding the module root
1491
- # Assumes that a Modulefile exists
1492
- # @param [String] possible_module_directory
1493
- # will look for Modulefile and if none found go up one level and try again until root is reached
1494
- #
1495
- # @return [String,nil]
1496
- def parse_for_moduleroot(possible_module_directory)
1497
- if File.exists?("#{possible_module_directory}/Modulefile") || File.exists?("#{possible_module_directory}/metadata.json")
1498
- possible_module_directory
1499
- elsif possible_module_directory === '/'
1500
- logger.error "At root, can't parse for another directory"
1501
- nil
1502
- else
1503
- logger.debug "No Modulefile or metadata.json found at #{possible_module_directory}, moving up"
1504
- parse_for_moduleroot File.expand_path(File.join(possible_module_directory,'..'))
1505
- end
1506
- end
1507
-
1508
- #Parse root directory of a module for module name
1509
- # Searches for metadata.json and then if none found, Modulefile and parses for the Name attribute
1510
- # @param [String] root_module_dir
1511
- # @return [String] module name
1512
- def parse_for_modulename(root_module_dir)
1513
- author_name, module_name = nil, nil
1514
- if File.exists?("#{root_module_dir}/metadata.json")
1515
- logger.debug "Attempting to parse Modulename from metadata.json"
1516
- module_json = JSON.parse(File.read "#{root_module_dir}/metadata.json")
1517
- if(module_json.has_key?('name'))
1518
- author_name, module_name = get_module_name(module_json['name'])
1519
- end
1520
- end
1521
- if !module_name && File.exists?("#{root_module_dir}/Modulefile")
1522
- logger.debug "Attempting to parse Modulename from Modulefile"
1523
- if /^name\s+'?(\w+-\w+)'?\s*$/i.match(File.read("#{root_module_dir}/Modulefile"))
1524
- author_name, module_name = get_module_name(Regexp.last_match[1])
1525
- end
1526
- end
1527
- if !module_name && !author_name
1528
- logger.debug "Unable to determine name, returning null"
1529
- end
1530
- return author_name, module_name
1531
- end
1532
-
1533
- #Parse modulename from the pattern 'Auther-ModuleName'
1534
- #
1535
- # @param [String] author_module_name <Author>-<ModuleName> pattern
1536
- #
1537
- # @return [String,nil]
1538
- #
1539
- def get_module_name(author_module_name)
1540
- split_name = split_author_modulename(author_module_name)
1541
- if split_name
1542
- return split_name[:author], split_name[:module]
1543
- end
1544
- end
1545
-
1546
- #Split the Author-Name into a hash
1547
- # @param [String] author_module_attr
1548
- #
1549
- # @return [Hash<Symbol,String>,nil] :author and :module symbols will be returned
1550
- #
1551
- def split_author_modulename(author_module_attr)
1552
- result = /(\w+)-(\w+)/.match(author_module_attr)
1553
- if result
1554
- {:author => result[1], :module => result[2]}
1555
- else
1556
- nil
1557
- end
1558
- end
1559
-
1560
- # Build an array list of files/directories to ignore when pushing to remote host
1561
- # Automatically adds '..' and '.' to array. If not opts of :ignore list is provided
1562
- # it will use the static variable PUPPET_MODULE_INSTALL_IGNORE
1563
- #
1564
- # @param opts [Hash]
1565
- # @option opts [Array] :ignore_list A list of files/directories to ignore
1566
- def build_ignore_list(opts = {})
1567
- ignore_list = opts[:ignore_list] || PUPPET_MODULE_INSTALL_IGNORE
1568
- if !ignore_list.kind_of?(Array) || ignore_list.nil?
1569
- raise ArgumentError "Ignore list must be an Array"
1570
- end
1571
- ignore_list << '.' unless ignore_list.include? '.'
1572
- ignore_list << '..' unless ignore_list.include? '..'
1573
- ignore_list
1574
- end
9
+ include DSL::InstallUtils::PEUtils
10
+ include DSL::InstallUtils::PuppetUtils
11
+ include DSL::InstallUtils::ModuleUtils
12
+ include DSL::InstallUtils::EZBakeUtils
1575
13
  end
1576
14
  end
1577
15
  end