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.
- checksums.yaml +4 -4
- data/Boltdir/Puppetfile +2 -0
- data/CONTRIBUTING.md +27 -6
- data/README.md +169 -5
- data/bin/ref_arch_setup +171 -22
- data/fixtures/pe.conf +119 -0
- data/fixtures/puppet-enterprise-2019.0-rc1-7-gd82666f-el-7-x86_64.tar +0 -0
- data/lib/ref_arch_setup.rb +8 -2
- data/lib/ref_arch_setup/bolt_helper.rb +263 -0
- data/lib/ref_arch_setup/cli.rb +76 -7
- data/lib/ref_arch_setup/download_helper.rb +399 -0
- data/lib/ref_arch_setup/install.rb +425 -14
- data/lib/ref_arch_setup/version.rb +1 -1
- data/modules/ref_arch_setup/tasks/download_pe_tarball.json +13 -0
- data/modules/ref_arch_setup/tasks/download_pe_tarball.sh +48 -0
- data/modules/ref_arch_setup/tasks/generate_pe_conf.json +10 -0
- data/modules/ref_arch_setup/tasks/generate_pe_conf.rb +128 -0
- data/modules/ref_arch_setup/tasks/install_pe.json +14 -0
- data/modules/ref_arch_setup/tasks/install_pe.sh +132 -0
- metadata +59 -6
@@ -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]
|
26
|
-
#
|
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
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
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
|