ref_arch_setup 0.0.1 → 0.5.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.
@@ -0,0 +1,399 @@
1
+ # rubocop:disable Metrics/ClassLength
2
+ require "oga"
3
+ require "net/http"
4
+ require "json"
5
+
6
+ # General namespace for RAS
7
+ module RefArchSetup
8
+ # Download helper methods
9
+ class DownloadHelper
10
+ # the 'Puppet Enterprise Version History' url
11
+ PE_VERSIONS_URL = "https://puppet.com/misc/version-history".freeze
12
+
13
+ # the base of the URL where prod PE tarballs are hosted
14
+ BASE_PROD_URL = "https://s3.amazonaws.com/pe-builds/released".freeze
15
+
16
+ # the minimum prod version supported by RAS
17
+ MIN_PROD_VERSION = "2018.1.0".freeze
18
+
19
+ # the supported platforms for PE installation using RAS
20
+ PE_PLATFORMS = %w[el-6-x86_64
21
+ el-7-x86_64
22
+ sles-12-x86_64
23
+ ubuntu-16.04-amd64
24
+ ubuntu-18.04-amd64].freeze
25
+
26
+ # Initializes the instance variables to the defaults
27
+ # or values specified by environment variables
28
+ #
29
+ # @author Bill Claytor
30
+ #
31
+ # @example:
32
+ # init
33
+ #
34
+ def self.init
35
+ @pe_versions_url = ENV["PE_VERSIONS_URL"] ? ENV["PE_VERSIONS_URL"] : PE_VERSIONS_URL
36
+ @base_prod_url = ENV["BASE_PROD_URL"] ? ENV["BASE_PROD_URL"] : BASE_PROD_URL
37
+ @min_prod_version = ENV["MIN_PROD_VERSION"] ? ENV["MIN_PROD_VERSION"] : MIN_PROD_VERSION
38
+ @pe_platforms = PE_PLATFORMS
39
+ end
40
+
41
+ # Builds the prod tarball URL for the specified or default version of PE
42
+ #
43
+ # @author Bill Claytor
44
+ #
45
+ # @param [string] version The desired version of PE (default = "latest")
46
+ # @param [string] host The target host for this PE tarball (default = "localhost")
47
+ # @param [string] platform The target platform for this PE tarball
48
+ #
49
+ # *** Specifying the host will determine and validate the platform for the host
50
+ # *** Specifying the platform will ignore the host value and only perform the validation
51
+ #
52
+ # @return [string] the prod tarball URL
53
+ #
54
+ # @example:
55
+ # url = build_prod_tarball_url()
56
+ # url = build_prod_tarball_url("2018.1.4", "master.mydomain.net", "")
57
+ # url = build_prod_tarball_url("2018.1.4", "value_is_ignored", "sles-12-x86_64")
58
+ #
59
+ def self.build_prod_tarball_url(version = "latest", host = "localhost", platform = "default")
60
+ init
61
+ pe_version = handle_prod_version(version)
62
+ pe_platform = handle_platform(host, platform)
63
+ url = "#{@base_prod_url}/#{pe_version}/puppet-enterprise-#{pe_version}-#{pe_platform}.tar.gz"
64
+ puts "URL: #{url}"
65
+ return url
66
+ end
67
+
68
+ # Retrieves the latest production PE version or verifies a user-specified version
69
+ #
70
+ # @author Bill Claytor
71
+ #
72
+ # @param [string] version The desired version of PE
73
+ #
74
+ # @raise [RuntimeError] If the specified version is not valid
75
+ #
76
+ # @return [string] The corresponding PE version
77
+ #
78
+ # @example:
79
+ # version = handle_prod_version("latest")
80
+ # version = handle_prod_version("2018.1.4")
81
+ #
82
+ def self.handle_prod_version(version)
83
+ if version == "latest"
84
+ pe_version = latest_prod_version
85
+ puts "The latest version is #{pe_version}"
86
+ else
87
+ success = ensure_valid_prod_version(version) && ensure_supported_prod_version(version)
88
+ raise "Invalid version: #{version}" unless success
89
+
90
+ pe_version = version
91
+ puts "Proceeding with specified version: #{pe_version}"
92
+ end
93
+
94
+ puts
95
+
96
+ return pe_version
97
+ end
98
+
99
+ # Retrieves the latest production version of PE from the 'Puppet Enterprise Version History'
100
+ #
101
+ # @author Bill Claytor
102
+ #
103
+ # @return [string] The latest production version of PE
104
+ #
105
+ # @example:
106
+ # latest_version = latest_prod_version
107
+ #
108
+ def self.latest_prod_version
109
+ result = parse_prod_versions_url("//table/tbody/tr[1]/td[1]")
110
+ latest_version = result.text
111
+ return latest_version
112
+ end
113
+
114
+ # Determines whether the specified version is an actual production version of PE
115
+ #
116
+ # @author Bill Claytor
117
+ #
118
+ # @param [string] version The specified version of PE
119
+ #
120
+ # @raise [RuntimeError] If the specified version is not found
121
+ #
122
+ # @return [true, false] Whether the specified version was found
123
+ #
124
+ # @example:
125
+ # result = ensure_valid_prod_version("2018.1.4")
126
+ #
127
+ def self.ensure_valid_prod_version(version)
128
+ puts "Verifying specified PE version: #{version}"
129
+
130
+ result = parse_prod_versions_url("//table/tbody/tr/td[contains(., '#{version}')]")
131
+ found = result.text == version ? true : false
132
+
133
+ puts "Specified version #{version} was found" if found
134
+ raise "Specified version not found: #{version}" unless found
135
+
136
+ return found
137
+ end
138
+
139
+ # Determines whether the specified version is supported by RAS
140
+ #
141
+ # @author Bill Claytor
142
+ #
143
+ # @param [string] version The specified version of PE
144
+ #
145
+ # @raise [RuntimeError] If the specified version is not supported
146
+ #
147
+ # @return [true, false] Whether the specified version is supported
148
+ #
149
+ # @example:
150
+ # result = ensure_supported_prod_version("2018.1.4")
151
+ #
152
+ def self.ensure_supported_prod_version(version)
153
+ major_version = version.split(".")[0]
154
+ supported_version = @min_prod_version.split(".")[0]
155
+
156
+ supported = major_version >= supported_version ? true : false
157
+ puts "Specified version #{version} is supported by RAS" if supported
158
+ puts "The minimum supported version is #{@min_prod_version}" unless supported
159
+
160
+ raise "Specified version #{version} is not supported by RAS" unless supported
161
+
162
+ return supported
163
+ end
164
+
165
+ # Fetches the list of production PE versions from the 'Puppet Enterprise Version History'
166
+ # *note: this is no longer used but has been left as an example
167
+ # @author Bill Claytor
168
+ #
169
+ # @return [Oga::XML::NodeSet] The versions list
170
+ #
171
+ # @example:
172
+ # versions = fetch_prod_versions
173
+ #
174
+ def self.fetch_prod_versions
175
+ versions = parse_prod_versions_url
176
+ puts "Versions:"
177
+
178
+ versions.each do |version|
179
+ version_text = version.text
180
+ puts version_text
181
+ end
182
+ puts
183
+
184
+ return versions
185
+ end
186
+
187
+ # Parses the 'Puppet Enterprise Version History'
188
+ # using the specified xpath and returns the result
189
+ #
190
+ # @author Bill Claytor
191
+ #
192
+ # @param [string] xpath The xpath to use when parsing the version history
193
+ #
194
+ # @return [Oga::XML::NodeSet] The resulting Oga NodeSet
195
+ #
196
+ # @example:
197
+ # versions = parse_prod_versions_url
198
+ # latest_version = parse_prod_versions_url("//table/tbody/tr[1]/td[1]")
199
+ # result = parse_prod_versions_url("//table/tbody/tr/td[contains(., '#{version}')]")
200
+ #
201
+ def self.parse_prod_versions_url(xpath = "//table/tbody/tr/td[1]")
202
+ puts "Checking Puppet Enterprise Version History: #{@pe_versions_url}"
203
+
204
+ uri = URI.parse(@pe_versions_url)
205
+ response = Net::HTTP.get_response(uri)
206
+ validate_response(response)
207
+
208
+ document = Oga.parse_html(response.body)
209
+ result = document.xpath(xpath)
210
+ return result
211
+ end
212
+
213
+ # Determines whether the response is valid
214
+ #
215
+ # @author Bill Claytor
216
+ #
217
+ # @param [Net::HTTPResponse] res The HTTP response to evaluate
218
+ # @param [Array] valid_response_codes The list of valid response codes
219
+ # @param [Array] invalid_response_bodies The list of invalid response bodies
220
+ #
221
+ # @raise [RuntimeError] If the response is not valid
222
+ #
223
+ # @return [true,false] Based on whether the response is valid
224
+ #
225
+ # @example
226
+ # valid = validate_response(res)
227
+ # valid = validate_response(res, ["200", "123"], ["", nil])
228
+ #
229
+ # rubocop:disable Metrics/AbcSize
230
+ # rubocop:disable Metrics/MethodLength
231
+ # rubocop:disable Metrics/CyclomaticComplexity
232
+ # rubocop:disable Metrics/PerceivedComplexity
233
+ def self.validate_response(res, valid_response_codes = ["200"],
234
+ invalid_response_bodies = ["", nil])
235
+ is_valid_response = false
236
+
237
+ if res.nil?
238
+ puts "Invalid response:"
239
+ puts "nil"
240
+ puts
241
+ elsif !valid_response_codes.include?(res.code) ||
242
+ invalid_response_bodies.include?(res.body)
243
+ code = res.code.nil? ? "nil" : res.code
244
+ body = res.body.nil? ? "nil" : res.body
245
+
246
+ puts "Invalid response:"
247
+ puts "code: #{code}"
248
+ puts "body: #{body}"
249
+ puts
250
+ else
251
+ is_valid_response = true
252
+ end
253
+
254
+ raise "Invalid response" unless is_valid_response
255
+
256
+ return is_valid_response
257
+ end
258
+ # rubocop:enable Metrics/AbcSize
259
+ # rubocop:enable Metrics/MethodLength
260
+ # rubocop:enable Metrics/CyclomaticComplexity
261
+ # rubocop:enable Metrics/PerceivedComplexity
262
+
263
+ # Handles the host and platform and determines the appropriate PE platform
264
+ #
265
+ # @author Bill Claytor
266
+ #
267
+ # @param [string] host The target host for this PE tarball (default = "localhost")
268
+ # @param [string] platform The target platform for this PE tarball
269
+ #
270
+ # *** Specifying the host will determine and validate the platform for the host
271
+ # *** Specifying the platform will ignore the host value and only perform the validation
272
+ #
273
+ # @raise [RuntimeError] If the platform is not valid
274
+ #
275
+ # @return [string] The PE platform
276
+ #
277
+ # @example:
278
+ # handle_platform("my_host", "default")
279
+ # handle_platform("value_is_ignored", "sles-12-x86_64")
280
+ #
281
+ def self.handle_platform(host, platform)
282
+ if platform == "default"
283
+ puts "Default platform specified; determining platform for host: #{host}"
284
+ pe_platform = get_host_platform(host)
285
+ else
286
+ puts "Specified platform: #{platform}"
287
+ pe_platform = platform
288
+ end
289
+ raise "Invalid PE platform: #{pe_platform}" unless valid_platform?(pe_platform)
290
+ return pe_platform
291
+ end
292
+
293
+ # Handles the platform for the specified host using the host's facts
294
+ #
295
+ # @author Bill Claytor
296
+ #
297
+ # @param [string] host The target host
298
+ #
299
+ # @raise [RuntimeError] If the facts status is 'failure'
300
+ # @raise [RuntimeError] If the platform can't be determined
301
+ #
302
+ # @return [string] The corresponding platform for the specified host
303
+ #
304
+ # @example:
305
+ # platform = get_host_platform("localhost")
306
+ #
307
+ # rubocop:disable Metrics/MethodLength
308
+ # rubocop:disable Metrics/AbcSize
309
+ # rubocop:disable Metrics/CyclomaticComplexity
310
+ def self.get_host_platform(host)
311
+ facts = retrieve_facts(host)
312
+
313
+ status = facts[0]["status"]
314
+ raise "Facts indicate that status for host #{host} is failure" if status == "failure"
315
+
316
+ os = facts[0]["result"]["os"]
317
+ os_name = os["name"]
318
+ os_family = os["family"]
319
+ platform_type = nil
320
+
321
+ case os_family
322
+ when "RedHat"
323
+ platform_type = "el"
324
+ platform_arch = "x86_64"
325
+ release = os["release"]["major"]
326
+ when "SLES"
327
+ platform_type = "sles"
328
+ platform_arch = "x86_64"
329
+ release = os["release"]["major"]
330
+ when "Debian"
331
+ if os_name == "Ubuntu"
332
+ platform_type = "ubuntu"
333
+ platform_arch = "amd64"
334
+ release = os["release"]["full"]
335
+ end
336
+ end
337
+
338
+ raise "Unable to determine platform for host: #{host}" unless platform_type
339
+ platform = "#{platform_type}-#{release}-#{platform_arch}"
340
+ puts "Host platform: #{platform}"
341
+
342
+ return platform
343
+ end
344
+ # rubocop:enable Metrics/MethodLength
345
+ # rubocop:enable Metrics/AbcSize
346
+ # rubocop:enable Metrics/CyclomaticComplexity
347
+
348
+ # Retrieves the facts for the specified host(s) using the facts::retrieve plan
349
+ #
350
+ # @author Bill Claytor
351
+ #
352
+ # @param [string] hosts The host(s) from which to retrieve facts
353
+ #
354
+ # @raise [JSON::ParserError] If the output from the bolt plan can't be parsed
355
+ # @raise [BoltCommandError] If the bolt command is not successful or the output is nil
356
+ #
357
+ # @return [Array<Hash] The retrieved facts
358
+ #
359
+ # @example:
360
+ # facts = retrieve_facts("localhost")
361
+ #
362
+ def self.retrieve_facts(hosts)
363
+ plan = "facts::retrieve"
364
+ puts "Retrieving facts for hosts: #{hosts}"
365
+
366
+ output = BoltHelper.run_forge_plan_with_bolt(plan, nil, hosts)
367
+
368
+ begin
369
+ facts = JSON.parse(output)
370
+ rescue
371
+ puts "Unable to parse bolt output"
372
+ raise
373
+ end
374
+
375
+ return facts
376
+ end
377
+
378
+ # Determines whether the specified platform is a valid PE platform
379
+ #
380
+ # @author Bill Claytor
381
+ #
382
+ # @param [string] platform The platform
383
+ #
384
+ # @return [true,false] Based on validity of the specified platform
385
+ #
386
+ # @example:
387
+ # is_valid = valid_platform?("sles-12-x86_64")
388
+ #
389
+ # TODO: validate for a specified version?
390
+ #
391
+ def self.valid_platform?(platform)
392
+ valid = @pe_platforms.include?(platform) ? true : false
393
+ puts "Platform #{platform} is valid" if valid
394
+ puts "Platform #{platform} is not valid" unless valid
395
+ puts "Valid platforms are: #{@pe_platforms}" unless valid
396
+ return valid
397
+ end
398
+ end
399
+ end
@@ -1,10 +1,21 @@
1
+ require "uri"
2
+ require "open-uri"
3
+
1
4
  # General namespace for RAS
2
5
  module RefArchSetup
6
+ # A space to use as a default location to put files on target_host
7
+ TMP_WORK_DIR = "/tmp/ref_arch_setup".freeze
8
+ DOWNLOAD_PE_TARBALL_TASK = "ref_arch_setup::download_pe_tarball".freeze
9
+ INSTALL_PE_TASK = "ref_arch_setup::install_pe".freeze
10
+
3
11
  # Installation helper
4
12
  #
5
13
  # @author Randell Pelak
6
14
  #
7
15
  # @attr [string] target_master Host to install on
16
+ #
17
+ # TODO: review the value for ClassLength
18
+ # rubocop:disable Metrics/ClassLength
8
19
  class Install
9
20
  # Initialize class
10
21
  #
@@ -22,22 +33,422 @@ module RefArchSetup
22
33
  # @author Randell Pelak
23
34
  #
24
35
  # @param [string] pe_conf_path Path to pe.conf
25
- # @param [string] pe_tarball_path Path to pe tarball
26
- # @param [string] target_master Host to install on
36
+ # @param [string] pe_tarball Path or URL for the pe tarball
37
+ #
38
+ # @raise [RuntimeError] Unless either a pe_tarball or pe_version is specified
39
+ # @raise [RuntimeError] If a valid pe_tarball is not available
40
+ # @raise [RuntimeError] If a RAS working directory can not be created
41
+ # @raise [BoltCommandError] If the bolt command is not successful or the output is nil
42
+ #
43
+ # @return [true,false] Based on exit status of the bolt task
44
+ # rubocop:disable Metrics/MethodLength
45
+ # rubocop:disable Metrics/AbcSize
46
+ def bootstrap(pe_conf_path, pe_tarball, pe_version)
47
+ if specified_option?(pe_tarball)
48
+ puts "Proceeding with specified pe_tarball: #{pe_tarball}"
49
+ @pe_tarball = pe_tarball
50
+ else
51
+ options_error = "Either a pe_version or pe_tarball must be specified"
52
+ raise options_error unless specified_option?(pe_version)
53
+ puts "Proceeding with specified pe_version: #{pe_version}"
54
+ @pe_tarball = RefArchSetup::DownloadHelper.build_prod_tarball_url(pe_version,
55
+ @target_master)
56
+ end
57
+
58
+ raise "Unable to create RAS working directory" unless make_tmp_work_dir
59
+
60
+ conf_path_on_master = handle_pe_conf(pe_conf_path)
61
+ tarball_path_on_master = handle_pe_tarball(@pe_tarball)
62
+
63
+ params = {}
64
+ params["pe_conf_path"] = conf_path_on_master
65
+ params["pe_tarball_path"] = tarball_path_on_master
66
+
67
+ output = BoltHelper.run_task_with_bolt(INSTALL_PE_TASK, params, @target_master)
68
+ success = output.nil? ? false : true
69
+ return success
70
+ end
71
+ # rubocop:enable Metrics/MethodLength
72
+ # rubocop:enable Metrics/PerceivedComplexity
73
+ # rubocop:enable Metrics/AbcSize
74
+
75
+ # Determines whether the option is 'specified' (not nil and not empty)
76
+ #
77
+ # @author Bill Claytor
78
+ #
79
+ # @param [string] value The option value to evaluate
80
+ #
81
+ # @return [true, false] Based on the evaluation of the value
82
+ #
83
+ def specified_option?(value)
84
+ specified = value.nil? || value.empty? ? false : true
85
+ return specified
86
+ end
87
+
88
+ # Handles user inputted pe.conf or if nil assumes it is in the CWD
89
+ # Validates file exists (allows just a dir to be given if pe.conf is in it)
90
+ # TODO Ensure it is valid once we have a reader/validator class
91
+ # Move it to the target_master
92
+ #
93
+ # @author Randell Pelak
94
+ #
95
+ # @param [string] pe_conf_path Path to pe.conf file or dir
96
+ #
97
+ # @raise [RuntimeError] If a pe.conf file is not found
98
+ # @raise [RuntimeError] If the file is not uploaded successfully
99
+ #
100
+ # @return [string] The path to the pe.conf file on the target master
101
+ def handle_pe_conf(pe_conf_path)
102
+ conf_path_on_master = "#{TMP_WORK_DIR}/pe.conf"
103
+ if pe_conf_path.nil?
104
+ file_path = Dir.pwd + "/pe.conf"
105
+ raise("No pe.conf file found in current working directory") unless File.exist?(file_path)
106
+ else
107
+ file_path = handle_pe_conf_path(pe_conf_path)
108
+ end
109
+ success = upload_pe_conf(file_path)
110
+ raise "Unable to upload pe.conf file to #{@target_master}" unless success
111
+
112
+ return conf_path_on_master
113
+ end
114
+
115
+ # Handles user inputted pe.conf
116
+ # Validates file exists (allows just a dir to be given if pe.conf is in it)
117
+ # Also ensures the file is named pe.conf
118
+ #
119
+ # @author Randell Pelak
120
+ #
121
+ # @param [string] pe_conf_path Path to pe.conf file or dir
122
+ #
123
+ # @raise [RuntimeError] If a directory is specified and a pe.conf file is not found
124
+ # @raise [RuntimeError] If the specified file is not named pe.conf
125
+ # @raise [RuntimeError] If the specified file is not found
126
+ #
127
+ # @return [string] The path to the pe.conf file
128
+ def handle_pe_conf_path(pe_conf_path)
129
+ file_path = File.expand_path(pe_conf_path)
130
+ if File.directory?(file_path)
131
+ full_path = file_path + "/pe.conf"
132
+ raise("No pe.conf file found in directory: #{file_path}") unless File.exist?(full_path)
133
+ file_path = full_path
134
+ else
135
+ filename = File.basename(file_path)
136
+ raise("Specified file is not named pe.conf #{file_path}") unless filename.eql?("pe.conf")
137
+ raise("pe.conf file not found #{file_path}") unless File.exist?(file_path)
138
+ end
139
+
140
+ return file_path
141
+ end
142
+
143
+ # Determines whether the specified path is a valid URL
144
+ #
145
+ # @author Bill Claytor
146
+ #
147
+ # @param [string] pe_tarball Path to PE tarball file
148
+ #
149
+ # @return [true,false] Based on whether the path is a valid URL
150
+ def valid_url?(pe_tarball)
151
+ valid = false
152
+ valid = true if pe_tarball =~ /\A#{URI.regexp(%w[http https])}\z/
153
+ return valid
154
+ end
155
+
156
+ # Parses the specified URL
157
+ #
158
+ # @author Bill Claytor
159
+ #
160
+ # @param [string] url URL for the PE tarball file
161
+ #
162
+ # @raise [RuntimeError] If the specified URL can not be parsed
163
+ #
164
+ # @return [true] Based on the validity of the URL
165
+ def parse_url(url)
166
+ begin
167
+ @pe_tarball_uri = URI.parse(url)
168
+ @pe_tarball_filename = File.basename(@pe_tarball_uri.path)
169
+ rescue
170
+ raise "Unable to parse the specified URL: #{url}"
171
+ end
172
+ return true
173
+ end
174
+
175
+ # Determines whether the specified path / url has a valid extension (.gz)
176
+ #
177
+ # @author Bill Claytor
178
+ #
179
+ # @param [string] pe_tarball Path to PE tarball file
180
+ #
181
+ # @raise [RuntimeError] If the extension of the specified tarball is invalid
182
+ #
183
+ # @return [true] Based on the validity of the extension
184
+ def validate_tarball_extension(pe_tarball)
185
+ message = "Invalid extension for tarball: #{pe_tarball}; extension must be .tar or .tar.gz"
186
+ raise(message) unless pe_tarball =~ /.*\.(tar|tar\.gz)$/
187
+ return true
188
+ end
189
+
190
+ # Determines whether the target master is localhost
191
+ #
192
+ # @author Bill Claytor
193
+ #
194
+ # @return [true,false] Based on whether the target master is localhost
195
+ #
196
+ # TODO: (SLV-185) Improve check for localhost
197
+ #
198
+ def target_master_is_localhost?
199
+ is_localhost = false
200
+ is_localhost = true if @target_master.include?("localhost")
201
+ return is_localhost
202
+ end
203
+
204
+ # Determines whether the specified file exists on the target master
205
+ #
206
+ # @author Bill Claytor
207
+ #
208
+ # @param [string] path Path to PE tarball file
209
+ #
210
+ # @return [true,false] Based on whether the file exists on the target master
211
+ #
212
+ # TODO: SLV-187 - combine with copy_pe_tarball_on_target_master
213
+ #
214
+ def file_exist_on_target_master?(path)
215
+ command = "[ -f #{path} ]"
216
+ exists = true
217
+
218
+ begin
219
+ BoltHelper.run_cmd_with_bolt(command, @target_master)
220
+ rescue
221
+ exists = false
222
+ end
223
+
224
+ return exists
225
+ end
226
+
227
+ # Runs the download_pe_tarball Bolt task
228
+ #
229
+ # @author Bill Claytor
230
+ #
231
+ # @param [string] url The pe tarball URL
232
+ # @param [string] nodes Nodes where the task should be run
233
+ #
234
+ # @return [true,false] Based on exit status of the bolt task
235
+ #
236
+ # TODO: update to return the download path (SLV-186)
237
+ #
238
+ def download_pe_tarball(url, nodes)
239
+ puts "Attempting to download #{url} to #{nodes}"
240
+ puts
241
+
242
+ params = {}
243
+ params["url"] = url
244
+ params["destination"] = TMP_WORK_DIR
245
+
246
+ output = BoltHelper.run_task_with_bolt(DOWNLOAD_PE_TARBALL_TASK, params, nodes)
247
+ success = output.nil? ? false : true
248
+ return success
249
+ end
250
+
251
+ # Downloads the PE tarball locally and moves it to the target master
252
+ #
253
+ # @author Bill Claytor
254
+ #
255
+ # @param [string] url The pe tarball URL
256
+ #
257
+ # @return [true,false] Based on exit status of the bolt task
258
+ def download_and_move_pe_tarball(url)
259
+ download_path = "#{TMP_WORK_DIR}/#{@pe_tarball_filename}"
260
+ success = download_pe_tarball(url, "localhost")
261
+ success = upload_pe_tarball(download_path) if success
262
+ return success
263
+ end
264
+
265
+ # Handles the specified PE tarball URL by either downloading directly to the
266
+ # target master or downloading locally and copying to the target master
267
+ #
268
+ # @author Bill Claytor
269
+ #
270
+ # @param [string] url The PE tarball URL
271
+ #
272
+ # @raise [RuntimeError] If the tarball is not handled successfully
273
+ #
274
+ # @return [string] The tarball path on the target master
275
+ # rubocop:disable Metrics/MethodLength
276
+ def handle_tarball_url(url)
277
+ parse_url(url)
278
+ tarball_path_on_master = "#{TMP_WORK_DIR}/#{@pe_tarball_filename}"
279
+ success = false
280
+
281
+ if target_master_is_localhost?
282
+ success = download_pe_tarball(url, "localhost")
283
+ raise "Failed downloading #{url} to localhost" unless success
284
+ else
285
+ begin
286
+ # if downloading to the target master fails
287
+ success = download_pe_tarball(url, @target_master)
288
+ rescue
289
+ # try to download locally and then upload
290
+ puts "Unable to download the tarball directly to #{@target_master}"
291
+ success = download_and_move_pe_tarball(url)
292
+ ensure
293
+ raise "Failed downloading #{url} locally and moving to #{@target_master}" unless success
294
+ end
295
+
296
+ end
297
+
298
+ return tarball_path_on_master
299
+ end
300
+ # rubocop:enable Metrics/MethodLength
301
+
302
+ # Copies the PE tarball from the specified location to the temp working directory
303
+ # on the target master
304
+ #
305
+ # @author Bill Claytor
306
+ #
307
+ # @param [string] tarball_path_on_target_master The pe tarball path on the target master
308
+ #
309
+ # @return [true,false] Based on exit status of the bolt task
310
+ def copy_pe_tarball_on_target_master(tarball_path_on_target_master)
311
+ filename = File.basename(tarball_path_on_target_master)
312
+ success = if tarball_path_on_target_master == "#{TMP_WORK_DIR}/#{filename}"
313
+ puts "Not copying the tarball as the source and destination are the same"
314
+ true
315
+ else
316
+ command = "cp #{tarball_path_on_target_master} #{TMP_WORK_DIR}"
317
+ output = BoltHelper.run_cmd_with_bolt(command, @target_master)
318
+ output.nil? ? false : true
319
+ end
320
+
321
+ return success
322
+ end
323
+
324
+ # Handles the specified tarball path when the target master is not localhost
325
+ #
326
+ # @author Bill Claytor
327
+ #
328
+ # @param [string] path The pe tarball path
329
+ #
330
+ # @raise [RuntimeError] If the specified tarball is not found
331
+ #
332
+ # @return [true,false] Based on exit status of the bolt task
333
+ def handle_tarball_path_with_remote_target_master(path)
334
+ remote_flag = "#{@target_master}:"
335
+
336
+ if path.start_with?(remote_flag)
337
+ actual_path = path.sub!(remote_flag, "")
338
+ success = file_exist_on_target_master?(actual_path)
339
+ success = copy_pe_tarball_on_target_master(actual_path) if success
340
+ else
341
+ raise "File not found: #{path}" unless File.exist?(path)
342
+ success = upload_pe_tarball(path)
343
+ end
344
+
345
+ return success
346
+ end
347
+
348
+ # Handles the specified tarball path based on the target_master
349
+ #
350
+ # @author Bill Claytor
351
+ #
352
+ # @param [string] path The PE tarball path
353
+ #
354
+ # @raise [RuntimeError] If the specified tarball is not found
355
+ # @raise [RuntimeError] If the specified tarball is not uploaded successfully
356
+ #
357
+ # @return [string] The tarball path on the target master
358
+ #
359
+ # TODO: improve "host to install on"? ("host where PE will be installed?")
360
+ def handle_tarball_path(path)
361
+ filename = File.basename(path)
362
+ tarball_path_on_master = "#{TMP_WORK_DIR}/#{filename}"
363
+ file_not_found_error = "File not found: #{path}"
364
+ upload_error = "Unable to upload tarball to the RAS working directory on #{@target_master}"
365
+ copy_error = "Unable to copy tarball to the RAS working directory on #{@target_master}"
366
+
367
+ if target_master_is_localhost?
368
+ raise file_not_found_error unless File.exist?(path)
369
+ success = upload_pe_tarball(path)
370
+ error = copy_error unless success
371
+ else
372
+ success = handle_tarball_path_with_remote_target_master(path)
373
+ error = upload_error unless success
374
+ end
375
+
376
+ raise error unless success
377
+
378
+ return tarball_path_on_master
379
+ end
380
+
381
+ # Handles the PE tarball based on the the path (URL / file)
382
+ # and target master (local / remote)
383
+ #
384
+ # @author Bill Claytor
385
+ #
386
+ # @param [string] pe_tarball Path to PE tarball file
387
+ #
388
+ # @raise [RuntimeError] If the specified tarball is not handled successfully
389
+ #
390
+ # @return [string] The tarball path on the master after copying if successful
391
+ def handle_pe_tarball(pe_tarball)
392
+ error = "Unable to handle the specified PE tarball path: #{pe_tarball}"
393
+ validate_tarball_extension(pe_tarball)
394
+ tarball_path_on_master = if valid_url?(pe_tarball)
395
+ handle_tarball_url(pe_tarball)
396
+ else
397
+ handle_tarball_path(pe_tarball)
398
+ end
399
+
400
+ raise error unless tarball_path_on_master
401
+
402
+ return tarball_path_on_master
403
+ end
404
+
405
+ # Creates a tmp work dir for ref_arch_setup on the target_host
406
+ # Doesn't fail if the dir is already there.
407
+ #
408
+ # @author Randell Pelak
409
+ #
410
+ # @raise [BoltCommandError] If the bolt command is not successful or the output is nil
411
+ #
412
+ # @return [true,false] Based on the output returned from the bolt command
413
+ def make_tmp_work_dir
414
+ BoltHelper.make_dir(TMP_WORK_DIR, @target_master)
415
+ end
416
+
417
+ # Upload the pe.conf to the target_host
418
+ #
419
+ # @author Randell Pelak
420
+ #
421
+ # @param [string] src_pe_conf_path Path to the source copy of the pe.conf file
422
+ # @param [string] dest_pe_conf_path Path to put the pe.conf at on the target host
423
+ # @param [string] target_master Host to upload to
27
424
  #
28
425
  # @return [true,false] Based on exit status of the bolt task
29
- def bootstrap_mono(pe_conf_path, pe_tarball_path, target_master = @target_master)
30
- env_vars = "PE_CONF_PATH=#{pe_conf_path};"
31
- env_vars << "PE_TARBALL_PATH=#{pe_tarball_path};"
32
- env_vars << "PE_TARGET_MASTER=#{target_master};"
33
- command = env_vars.to_s + "bolt task run bogus::foo "
34
- command << "--modulepath #{RAS_MODULE_PATH} --nodes #{target_master}"
35
- puts "Running: #{command}"
36
- output = `#{command}`
37
- success = $?.success? # rubocop:disable Style/SpecialGlobalVars
38
- puts "ERROR: bolt command failed!" unless success
39
- puts "Exit status was: #{$?.exitstatus}" # rubocop:disable Style/SpecialGlobalVars
40
- puts "Output was: #{output}"
426
+ def upload_pe_conf(src_pe_conf_path = "#{RAS_FIXTURES_PATH}/pe.conf",
427
+ dest_pe_conf_path = "#{TMP_WORK_DIR}/pe.conf",
428
+ target_master = @target_master)
429
+ output = BoltHelper.upload_file(src_pe_conf_path, dest_pe_conf_path, target_master)
430
+ success = output.nil? ? false : true
431
+
432
+ return success
433
+ end
434
+
435
+ # Upload the pe tarball to the target_host
436
+ #
437
+ # @author Randell Pelak
438
+ #
439
+ # @param [string] src_pe_tarball_path Path to the source copy of the tarball file
440
+ #
441
+ # @return [true,false] Based on exit status of the bolt task
442
+ def upload_pe_tarball(src_pe_tarball_path)
443
+ file_name = File.basename(src_pe_tarball_path)
444
+ dest_pe_tarball_path = "#{TMP_WORK_DIR}/#{file_name}"
445
+
446
+ puts "Attempting upload from #{src_pe_tarball_path} " \
447
+ "to #{dest_pe_tarball_path} on #{@target_master}"
448
+
449
+ output = BoltHelper.upload_file(src_pe_tarball_path, dest_pe_tarball_path, @target_master)
450
+ success = output.nil? ? false : true
451
+
41
452
  return success
42
453
  end
43
454
  end