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.
- 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
|