ref_arch_setup 0.0.1 → 0.5.0

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