beaker 2.7.1 → 2.8.0

Sign up to get free protection for your applications and to get access to all the features.
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