right_link 5.9.2 → 5.9.5
Sign up to get free protection for your applications and to get access to all the features.
- data/INSTALL.rdoc +91 -304
- data/LICENSE +1 -1
- data/README.rdoc +10 -1
- data/RELEASES.rdoc +48 -1
- data/actors/instance_setup.rb +19 -9
- data/bin/rs_state +31 -0
- data/lib/chef/providers/right_link_tag_provider.rb +1 -1
- data/lib/chef/providers/server_collection_provider.rb +3 -3
- data/lib/clouds/clouds/rackspace.rb +10 -1
- data/lib/instance/agent_config.rb +4 -1
- data/lib/instance/cook/repose_downloader.rb +1 -1
- data/lib/instance/executable_sequence_proxy.rb +0 -3
- data/lib/instance/instance_state.rb +12 -11
- data/lib/instance/login_user_manager.rb +0 -123
- data/lib/instance/right_scripts_cookbook.rb +3 -3
- data/lib/repo_conf_generators/yum_conf_generators.rb +2 -1
- data/lib/right_link/version.rb +5 -1
- data/scripts/bundle_runner.rb +3 -3
- data/scripts/command_helper.rb +10 -5
- data/scripts/log_level_manager.rb +2 -2
- data/scripts/reenroller.rb +2 -5
- data/scripts/shutdown_client.rb +0 -1
- data/scripts/state_controller.rb +98 -0
- data/scripts/system_configurator.rb +4 -0
- data/scripts/tagger.rb +14 -17
- data/scripts/thunker.rb +12 -28
- metadata +45 -45
- data/lib/clouds/clouds/rackspace-ng.rb +0 -54
data/LICENSE
CHANGED
data/README.rdoc
CHANGED
@@ -10,6 +10,15 @@ to monitor the stdout and stderr streams of scripted processes. Servers running
|
|
10
10
|
the RightLink agent configures themselves on startup an register with the
|
11
11
|
mapper so that operational recipes and scripts can be run at a later time.
|
12
12
|
|
13
|
+
Supported cloud types:
|
14
|
+
* azure
|
15
|
+
* cloudstack
|
16
|
+
* ec2
|
17
|
+
* google
|
18
|
+
* openstack
|
19
|
+
* rackspace (Open Cloud)
|
20
|
+
* softlayer
|
21
|
+
|
13
22
|
Refer to the wiki (https://github.com/rightscale/right_link/wikis) for up-to-date
|
14
23
|
documentation.
|
15
24
|
|
@@ -82,7 +91,7 @@ See INSTALL.rdoc for more information
|
|
82
91
|
|
83
92
|
<b>RightLink</b>
|
84
93
|
|
85
|
-
Copyright:: Copyright (c) 2009-
|
94
|
+
Copyright:: Copyright (c) 2009-2013 RightScale, Inc.
|
86
95
|
|
87
96
|
Permission is hereby granted, free of charge, to any person obtaining
|
88
97
|
a copy of this software and associated documentation files (the
|
data/RELEASES.rdoc
CHANGED
@@ -1,3 +1,50 @@
|
|
1
|
+
= 5.9.5 (RightLink 5.9 release candidate 3)
|
2
|
+
|
3
|
+
Released 2013-11-27
|
4
|
+
|
5
|
+
== Bug Fixes
|
6
|
+
|
7
|
+
* Added ruby as a package dependency.
|
8
|
+
* Removed unnecessary warning about rightscale user during package install.
|
9
|
+
* Fixed regression in patching mechanism.
|
10
|
+
|
11
|
+
= 5.9.4 (RightLink 5.9 release candidate 2)
|
12
|
+
|
13
|
+
Released 2013-11-20
|
14
|
+
|
15
|
+
== Changes to Existing Functionality
|
16
|
+
|
17
|
+
* Revert rs_tag query TAG_LIST param to split tags by space. No spaces allowed in a tag.
|
18
|
+
|
19
|
+
== Bug Fixes
|
20
|
+
|
21
|
+
* Redundant rs_state output when executed.
|
22
|
+
* Fix RightLink failure to run RightScripts that contain a single or double quote.
|
23
|
+
|
24
|
+
= 5.9.3 (RightLink 5.9 release candidate 1)
|
25
|
+
|
26
|
+
Released 2013-10-17
|
27
|
+
|
28
|
+
== New Features
|
29
|
+
* rs_state utility that lets users interrogate the run-state (rs_state --type run) or
|
30
|
+
agent state (rs_state --type agent)
|
31
|
+
|
32
|
+
== Changes to Existing Functionality
|
33
|
+
|
34
|
+
* RightLink changes the audit summary when waiting for missing inputs on boot. It also strands if
|
35
|
+
inputs are still missing after 45 minutes. ("Missing" means inputs that are set to a value that
|
36
|
+
cannot yet be computed, such as the IP address of a non-running server.)
|
37
|
+
* RS_DECOM_REASON is not populated anymore
|
38
|
+
* The cloud-support package for Rackspace Classic cloud suffixed as "rackspace-first-gen" has been removed.
|
39
|
+
* The cloud-support package for Rackspace OpenCloud is suffixed with "rackspace".
|
40
|
+
|
41
|
+
== Bug Fixes
|
42
|
+
|
43
|
+
* RightLink sets correct permissions on users' home directories (using OS defaults)
|
44
|
+
for users that have been created on login.
|
45
|
+
* RightLink CLI tools will no longer crash when executed without arguments.
|
46
|
+
* RightLink will no longer give a warning about missing tty name when boot scripts complete.
|
47
|
+
|
1
48
|
= 5.9.2 (RightLink 5.9 beta 3)
|
2
49
|
|
3
50
|
Released 2013-09-06.
|
@@ -118,7 +165,7 @@ Released 2013-07-13.
|
|
118
165
|
|
119
166
|
== Bug Fixes
|
120
167
|
|
121
|
-
* Managed login always displays MOTD, works with older versions of sudo,
|
168
|
+
* Managed login always displays MOTD, works with older versions of sudo,
|
122
169
|
* Cookbook download is more reliable in fail-and-retry scenarios
|
123
170
|
|
124
171
|
= 5.8.8 (General Availability release in conjunction with ServerTemplates v12.11 LTS)
|
data/actors/instance_setup.rb
CHANGED
@@ -51,6 +51,9 @@ class InstanceSetup
|
|
51
51
|
# Maximum time between nag audits for missing inputs.
|
52
52
|
MISSING_INPUT_AUDIT_DELAY_SECS = 2 * 60
|
53
53
|
|
54
|
+
# Maximum time that instances will wait for missing inputs before stranding
|
55
|
+
MISSING_INPUT_TIMEOUT = 45 * 60
|
56
|
+
|
54
57
|
# Tag set on instances that are part of an array
|
55
58
|
AUTO_LAUNCH_TAG ='rs_launch:type=auto'
|
56
59
|
|
@@ -135,9 +138,10 @@ class InstanceSetup
|
|
135
138
|
# true:: Always return true
|
136
139
|
def init_boot
|
137
140
|
RightScale::Sender.instance.initialize_offline_queue
|
138
|
-
payload = {:agent_identity
|
139
|
-
:
|
140
|
-
:
|
141
|
+
payload = {:agent_identity => @agent_identity,
|
142
|
+
:right_link_version => RightLink.version,
|
143
|
+
:r_s_version => RightScale::AgentConfig.protocol_version,
|
144
|
+
:resource_uid => RightScale::InstanceState.resource_uid}
|
141
145
|
req = RightScale::IdempotentRequest.new('/booter/declare', payload, :retry_on_error => true)
|
142
146
|
req.callback do |res|
|
143
147
|
RightScale::Sender.instance.start_offline_queue
|
@@ -403,6 +407,7 @@ class InstanceSetup
|
|
403
407
|
|
404
408
|
req.callback do |bundle|
|
405
409
|
if bundle.executables.any? { |e| !e.ready }
|
410
|
+
@audit.create_new_section("Waiting for missing inputs")
|
406
411
|
retrieve_missing_inputs(bundle) { cb.call(success_result(bundle)) }
|
407
412
|
else
|
408
413
|
yield success_result(bundle)
|
@@ -461,7 +466,7 @@ class InstanceSetup
|
|
461
466
|
yield
|
462
467
|
else
|
463
468
|
# keep state to provide fewer but more meaningful audits.
|
464
|
-
last_missing_inputs ||= {}
|
469
|
+
last_missing_inputs ||= {:started_at => Time.now}
|
465
470
|
last_missing_inputs[:executables] ||= {}
|
466
471
|
|
467
472
|
# don't need to audit on each attempt to resolve missing inputs, but nag
|
@@ -477,9 +482,11 @@ class InstanceSetup
|
|
477
482
|
missing_input_names = []
|
478
483
|
|
479
484
|
e.input_flags.each {|k,v| missing_input_names << k if v.member?("unready")}
|
480
|
-
|
485
|
+
if audit_missing_inputs
|
486
|
+
@audit.append_info("Waiting for the following missing inputs which are used by '#{e.nickname}': #{missing_input_names.join(", ")}")
|
487
|
+
sent_audit = true
|
488
|
+
end
|
481
489
|
|
482
|
-
sent_audit = true
|
483
490
|
missing_inputs_executables[e.nickname] = missing_input_names
|
484
491
|
end
|
485
492
|
|
@@ -488,14 +495,17 @@ class InstanceSetup
|
|
488
495
|
unless missing_inputs_executables[nickname]
|
489
496
|
title = RightScale::RightScriptsCookbook.recipe_title(nickname)
|
490
497
|
@audit.append_info("The inputs used by #{title} which had been missing have now been resolved.")
|
491
|
-
sent_audit = true
|
492
498
|
end
|
493
499
|
end
|
494
500
|
last_missing_inputs[:executables] = missing_inputs_executables
|
495
501
|
last_missing_inputs[:last_audit_time] = Time.now if sent_audit
|
496
502
|
|
497
|
-
|
498
|
-
|
503
|
+
if Time.now - last_missing_inputs[:started_at] < MISSING_INPUT_TIMEOUT
|
504
|
+
# schedule retry to retrieve missing inputs.
|
505
|
+
EM.add_timer(MISSING_INPUT_RETRY_DELAY_SECS) { retrieve_missing_inputs(bundle, last_missing_inputs, &cb) }
|
506
|
+
else
|
507
|
+
strand("Failed to retrieve missing inputs after #{MISSING_INPUT_TIMEOUT / 60} minutes")
|
508
|
+
end
|
499
509
|
end
|
500
510
|
end
|
501
511
|
|
data/bin/rs_state
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
# Copyright (c) 2013 RightScale Inc
|
2
|
+
#
|
3
|
+
# Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
# a copy of this software and associated documentation files (the
|
5
|
+
# "Software"), to deal in the Software without restriction, including
|
6
|
+
# without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
# distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
# permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
# the following conditions:
|
10
|
+
#
|
11
|
+
# The above copyright notice and this permission notice shall be
|
12
|
+
# included in all copies or substantial portions of the Software.
|
13
|
+
#
|
14
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
21
|
+
|
22
|
+
# rs_state --help for usage information
|
23
|
+
#
|
24
|
+
# See scripts/state_controller.rb for additional information.
|
25
|
+
|
26
|
+
require File.expand_path(File.join(File.dirname(__FILE__), '..', 'lib', 'gem_dependencies'))
|
27
|
+
|
28
|
+
$:.push(File.join(File.dirname(__FILE__), '..', 'scripts'))
|
29
|
+
require 'state_controller'
|
30
|
+
|
31
|
+
RightScale::RightLinkStateController::run
|
@@ -46,13 +46,13 @@ class Chef
|
|
46
46
|
# === Return
|
47
47
|
# true:: Always return true
|
48
48
|
def action_load
|
49
|
-
node[:server_collection] ||= {}
|
50
|
-
node[:server_collection][@new_resource.name] = {}
|
49
|
+
node.set[:server_collection] ||= {}
|
50
|
+
node.set[:server_collection][@new_resource.name] = {}
|
51
51
|
return unless @new_resource.tags && !@new_resource.tags.empty?
|
52
52
|
|
53
53
|
result = RightScale::Cook.instance.query_tags(@new_resource.tags, @new_resource.agent_ids, @new_resource.timeout)
|
54
54
|
collection = result.inject({}) { |res, (k, v)| res[k] = v['tags']; res }
|
55
|
-
node[:server_collection][@new_resource.name] = collection
|
55
|
+
node.set[:server_collection][@new_resource.name] = collection
|
56
56
|
true
|
57
57
|
end
|
58
58
|
|
@@ -45,7 +45,7 @@ end
|
|
45
45
|
|
46
46
|
# defaults.
|
47
47
|
default_option([:user_metadata, :metadata_tree_climber, :create_leaf_override], method(:create_user_metadata_leaf))
|
48
|
-
default_option([:metadata_source, :user_metadata_source_file_path], File.join(RightScale::Platform.filesystem.spool_dir,
|
48
|
+
default_option([:metadata_source, :user_metadata_source_file_path], File.join(RightScale::Platform.filesystem.spool_dir, 'rackspace', 'user-data.txt'))
|
49
49
|
|
50
50
|
# Determines if the current instance is running on rackspace.
|
51
51
|
#
|
@@ -74,5 +74,14 @@ def update_details
|
|
74
74
|
details[:private_ip] = ::RightScale::CloudUtilities.ip_for_interface(ohai, :eth1)
|
75
75
|
end
|
76
76
|
end
|
77
|
+
|
78
|
+
# rack_connect (and managed?) instances may not have network interfaces for
|
79
|
+
# public ip, so attempt the "what's my ip?" method in these cases.
|
80
|
+
unless details[:public_ip]
|
81
|
+
if public_ip = ::RightScale::CloudUtilities.query_whats_my_ip(:logger=>logger)
|
82
|
+
details[:public_ip] = public_ip
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
77
86
|
return details
|
78
87
|
end
|
@@ -29,7 +29,10 @@ module RightScale
|
|
29
29
|
|
30
30
|
# Path to RightScale files in parent directory of right_link
|
31
31
|
def self.parent_dir
|
32
|
-
|
32
|
+
# NOTE:
|
33
|
+
# RightScale is missing right_link home dir for Linux Platform
|
34
|
+
# will be fixed for v6.0, - ticket #16947
|
35
|
+
RightScale::Platform.windows? ? RightScale::Platform.filesystem.right_link_home_dir : '/opt/rightscale'
|
33
36
|
end
|
34
37
|
|
35
38
|
# @return [Array] an appropriate sequence of root directories for configuring the RightLink agent
|
@@ -112,7 +112,7 @@ module RightScale
|
|
112
112
|
# Previously we accessed RestClient directly and used it's wrapper method to instantiate
|
113
113
|
# a RestClient::Request object. This wrapper was not passing all options down the stack
|
114
114
|
# so now we invoke the RestClient::Request object directly, passing it our desired options
|
115
|
-
client.execute(:method => :get, :url => "https://#{endpoint}:443#{resource}", :timeout => calculate_timeout(attempts), :verify_ssl => OpenSSL::SSL::VERIFY_PEER, :ssl_ca_file => get_ca_file, :headers => {:user_agent => "RightLink v#{AgentConfig.protocol_version}", 'X-RightLink-Version' => RightLink
|
115
|
+
client.execute(:method => :get, :url => "https://#{endpoint}:443#{resource}", :timeout => calculate_timeout(attempts), :verify_ssl => OpenSSL::SSL::VERIFY_PEER, :ssl_ca_file => get_ca_file, :headers => {:user_agent => "RightLink v#{AgentConfig.protocol_version}", 'X-RightLink-Version' => RightLink.version }) do |response, request, result|
|
116
116
|
if result.kind_of?(Net::HTTPSuccess)
|
117
117
|
@size = result.content_length || response.size || 0
|
118
118
|
@speed = @size / (Time.now - t0)
|
@@ -149,9 +149,6 @@ module RightScale
|
|
149
149
|
::RightScale::OptionsBag::OPTIONS_ENV =>
|
150
150
|
::ENV[::RightScale::OptionsBag::OPTIONS_ENV]
|
151
151
|
}
|
152
|
-
if @context.decommission?
|
153
|
-
environment['RS_DECOM_REASON'] = @context.decommission_type
|
154
|
-
end
|
155
152
|
|
156
153
|
# spawn
|
157
154
|
RightScale::RightPopen.popen3_async(
|
@@ -32,10 +32,10 @@ module RightScale
|
|
32
32
|
|
33
33
|
CONFIG=\
|
34
34
|
if File.exists?(CONFIG_YAML_FILE)
|
35
|
-
RightSupport::Config.features(CONFIG_YAML_FILE)
|
35
|
+
RightSupport::Config.features(CONFIG_YAML_FILE)
|
36
36
|
else
|
37
37
|
RightSupport::Config.features({})
|
38
|
-
end
|
38
|
+
end
|
39
39
|
|
40
40
|
# States that are recorded in a standard fashion and audited when transitioned to
|
41
41
|
RECORDED_STATES = %w{ booting operational stranded decommissioning }
|
@@ -120,7 +120,7 @@ module RightScale
|
|
120
120
|
|
121
121
|
# (String) Type of decommission currently in progress or nil
|
122
122
|
def self.decommission_type
|
123
|
-
if @value == 'decommissioning'
|
123
|
+
if @value == 'decommissioning' || @value == 'decommissioned'
|
124
124
|
@decommission_type
|
125
125
|
else
|
126
126
|
raise RightScale::Exceptions::WrongState.new("Unexpected call to InstanceState.decommission_type for current state #{@value.inspect}")
|
@@ -175,7 +175,7 @@ module RightScale
|
|
175
175
|
# 3) bundled boot -- Agent already ran; agent ID changed: transition back to booting
|
176
176
|
# 4) decommission/crash -- Agent exited anyway; ID not changed; no reboot; keep old state entirely
|
177
177
|
# 5) ec2 restart -- Agent already ran; agent ID changed; instance ID is the same; transition back to booting
|
178
|
-
if state['identity'] && state['identity'] != identity
|
178
|
+
if state['identity'] && state['identity'] != identity && !@read_only
|
179
179
|
@last_recorded_value = state['last_recorded_value']
|
180
180
|
self.value = 'booting'
|
181
181
|
# if the current resource_uid is the same as the last
|
@@ -190,7 +190,7 @@ module RightScale
|
|
190
190
|
# CASE 3 -- identity has changed; bundled boot
|
191
191
|
Log.debug("Bundle detected; transitioning state to booting")
|
192
192
|
end
|
193
|
-
elsif state['reboot']
|
193
|
+
elsif state['reboot'] && !@read_only
|
194
194
|
# CASE 2 -- rebooting flagged by rightboot script in linux or by shutdown notification in windows
|
195
195
|
Log.debug("Reboot detected; transitioning state to booting")
|
196
196
|
@last_recorded_value = state['last_recorded_value']
|
@@ -199,11 +199,12 @@ module RightScale
|
|
199
199
|
else
|
200
200
|
# CASE 4 -- restart without reboot; continue with retries if recorded state does not match
|
201
201
|
@value = state['value']
|
202
|
+
@reboot = state['reboot']
|
202
203
|
@startup_tags = state['startup_tags']
|
203
204
|
@log_level = state['log_level']
|
204
205
|
@last_recorded_value = state['last_recorded_value']
|
205
206
|
@record_retries = state['record_retries']
|
206
|
-
@decommission_type = state['decommission_type'] if @value == 'decommissioning'
|
207
|
+
@decommission_type = state['decommission_type'] if (@value == 'decommissioning' || @value == 'decommissioned')
|
207
208
|
if @value != @last_recorded_value && RECORDED_STATES.include?(@value) &&
|
208
209
|
@record_retries < MAX_RECORD_STATE_RETRIES && !@read_only
|
209
210
|
record_state
|
@@ -251,7 +252,7 @@ module RightScale
|
|
251
252
|
Log.info("Transitioning state from #{previous_val} to #{val}")
|
252
253
|
@reboot = false if val != :booting
|
253
254
|
@value = val
|
254
|
-
@decommission_type = nil unless @value == 'decommissioning'
|
255
|
+
@decommission_type = nil unless (@value == 'decommissioning' || @value == 'decommissioned')
|
255
256
|
|
256
257
|
update_logger
|
257
258
|
update_motd
|
@@ -270,7 +271,7 @@ module RightScale
|
|
270
271
|
#
|
271
272
|
# === Return
|
272
273
|
# result(String):: new decommission type
|
273
|
-
#
|
274
|
+
#
|
274
275
|
# === Raise
|
275
276
|
# RightScale::Exceptions::Application:: Cannot update in read-only mod
|
276
277
|
def self.decommission_type=(decommission_type)
|
@@ -510,9 +511,9 @@ module RightScale
|
|
510
511
|
return unless RightScale::Platform.linux?
|
511
512
|
|
512
513
|
if SUCCESSFUL_STATES.include?(@value)
|
513
|
-
system('echo "RightScale installation complete. Details can be found in system logs." | wall') rescue nil
|
514
|
+
system('echo "RightScale installation complete. Details can be found in system logs." | wall > /dev/null 2>&1') rescue nil
|
514
515
|
elsif FAILED_STATES.include?(@value)
|
515
|
-
system('echo "RightScale installation failed. Please review system logs." | wall') rescue nil
|
516
|
+
system('echo "RightScale installation failed. Please review system logs." | wall > /dev/null 2>&1') rescue nil
|
516
517
|
end
|
517
518
|
|
518
519
|
return nil
|
@@ -537,7 +538,7 @@ module RightScale
|
|
537
538
|
'last_observed_resource_uid' => @resource_uid}
|
538
539
|
|
539
540
|
# Only include deommission_type when decommissioning
|
540
|
-
state_to_store['decommission_type'] = @decommission_type if @value == 'decommissioning'
|
541
|
+
state_to_store['decommission_type'] = @decommission_type if (@value == 'decommissioning' || @value == 'decommissioned')
|
541
542
|
|
542
543
|
RightScale::JsonUtilities::write_json(STATE_FILE, state_to_store)
|
543
544
|
true
|
@@ -190,7 +190,6 @@ module RightScale
|
|
190
190
|
when 0
|
191
191
|
home_dir = Shellwords.escape(Etc.getpwnam(username).dir)
|
192
192
|
|
193
|
-
sudo("chmod 0771 #{Shellwords.escape(home_dir)}")
|
194
193
|
# Locking account to prevent warning os SUSE(it complains on unlocking non-locked account)
|
195
194
|
modify_user(username, true, shell)
|
196
195
|
|
@@ -346,33 +345,6 @@ module RightScale
|
|
346
345
|
groups.include?(name)
|
347
346
|
end
|
348
347
|
|
349
|
-
def setup_profile(username, home_dir, custom_data, force)
|
350
|
-
return false if custom_data.nil? || custom_data.empty?
|
351
|
-
|
352
|
-
checksum_path = File.join('.rightscale', PROFILE_CHECKSUM)
|
353
|
-
return false if !force && File.exists?(File.join(home_dir, checksum_path))
|
354
|
-
|
355
|
-
t0 = Time.now.to_i
|
356
|
-
yield("Performing profile setup for #{username}...") if block_given?
|
357
|
-
|
358
|
-
tmpdir = Dir.mktmpdir
|
359
|
-
file_path = File.join(tmpdir, File.basename(custom_data))
|
360
|
-
if download_files(custom_data, file_path) && extract_files(username, file_path, home_dir)
|
361
|
-
save_checksum(username, file_path, checksum_path, home_dir)
|
362
|
-
t1 = Time.now.to_i
|
363
|
-
yield("Setup complete (#{t1 - t0} sec)") if block_given? && (t1 - t0 >= 2)
|
364
|
-
end
|
365
|
-
|
366
|
-
return true
|
367
|
-
rescue Exception => e
|
368
|
-
yield("Failed to create profile for #{username}; continuing") if block_given?
|
369
|
-
yield("#{e.class.name}: #{e.message} - #{e.backtrace.first}") if block_given?
|
370
|
-
Log.error("#{e.class.name}: #{e.message} - #{e.backtrace.first}")
|
371
|
-
return false
|
372
|
-
ensure
|
373
|
-
FileUtils.rm_rf(tmpdir) if tmpdir && File.exists?(tmpdir)
|
374
|
-
end
|
375
|
-
|
376
348
|
# Set some of the environment variables that would normally be set if a user
|
377
349
|
# were to login to an interactive shell. This is useful when simulating an
|
378
350
|
# interactive login, e.g. for purposes of running a user-specified command
|
@@ -432,100 +404,5 @@ module RightScale
|
|
432
404
|
|
433
405
|
File.join(path, cmd)
|
434
406
|
end
|
435
|
-
|
436
|
-
# Downloads a file from specified URL
|
437
|
-
#
|
438
|
-
# === Parameters
|
439
|
-
# url(String):: URL to file
|
440
|
-
# path(String):: downloaded file path
|
441
|
-
#
|
442
|
-
# === Return
|
443
|
-
# downloaded(Boolean):: true if downloaded and saved successfully
|
444
|
-
def download_files(url, path)
|
445
|
-
client = RightSupport::Net::HTTPClient.new
|
446
|
-
response = client.get(url, :timeout => 10)
|
447
|
-
File.open(path, "wb") { |file| file.write(response) } unless response.empty?
|
448
|
-
File.exists?(path)
|
449
|
-
rescue Exception => e
|
450
|
-
Log.error("#{e.class.name}: #{e.message} - #{e.backtrace.first}")
|
451
|
-
false
|
452
|
-
end
|
453
|
-
|
454
|
-
# Extracts an archive and moves files to destination directory
|
455
|
-
# Supported archive types are:
|
456
|
-
# .tar.bz2 / .tbz
|
457
|
-
# .tar.gz / .tgz
|
458
|
-
# .zip
|
459
|
-
#
|
460
|
-
# === Parameters
|
461
|
-
# username(String):: account's username
|
462
|
-
# filename(String):: archive's path
|
463
|
-
# destination_path(String):: path where extracted files should be
|
464
|
-
# moved
|
465
|
-
#
|
466
|
-
# === Return
|
467
|
-
# extracted(Boolean):: true if archive is extracted successfully
|
468
|
-
def extract_files(username, filename, destination_path)
|
469
|
-
escaped_filename = Shellwords.escape(filename)
|
470
|
-
|
471
|
-
case filename
|
472
|
-
when /(?:\.tar\.bz2|\.tbz)$/
|
473
|
-
result = sudo("tar jxf #{escaped_filename} -C #{destination_path}")
|
474
|
-
when /(?:\.tar\.gz|\.tgz)$/
|
475
|
-
result = sudo("tar zxf #{escaped_filename} -C #{destination_path}")
|
476
|
-
when /\.zip$/
|
477
|
-
result = sudo("unzip -o #{escaped_filename} -d #{destination_path}")
|
478
|
-
else
|
479
|
-
raise ArgumentError, "Don't know how to extract #{filename}'"
|
480
|
-
end
|
481
|
-
|
482
|
-
extracted = result.success?
|
483
|
-
chowned = change_owner(username, username, destination_path)
|
484
|
-
|
485
|
-
extracted && chowned
|
486
|
-
end
|
487
|
-
|
488
|
-
# Calculates MD5 checksum for specified file and saves it
|
489
|
-
#
|
490
|
-
# === Parameters
|
491
|
-
# username(String):: account's username
|
492
|
-
# target(String):: path to file
|
493
|
-
# checksum_path(String):: relative path to checksum file
|
494
|
-
# destination(String):: path to file where checksum should be saved
|
495
|
-
#
|
496
|
-
# === Return
|
497
|
-
# nil
|
498
|
-
def save_checksum(username, target, checksum_path, destination)
|
499
|
-
checksum = Digest::MD5.file(target).to_s
|
500
|
-
|
501
|
-
temp_dir = File.join(File.dirname(target), File.dirname(checksum_path))
|
502
|
-
temp_path = File.join(File.dirname(target), checksum_path)
|
503
|
-
|
504
|
-
FileUtils.mkdir_p(temp_dir)
|
505
|
-
FileUtils.chmod_R(0771, temp_dir) # need +x to others for File.exists? => true
|
506
|
-
File.open(temp_path, "w") { |f| f.write(checksum) }
|
507
|
-
|
508
|
-
change_owner(username, username, temp_dir)
|
509
|
-
sudo("mv #{temp_dir} #{destination}")
|
510
|
-
rescue Exception => e
|
511
|
-
STDERR.puts "Failed to save checksum for #{username} profile"
|
512
|
-
STDERR.puts "#{e.class.name}: #{e.message} - #{e.backtrace.first}"
|
513
|
-
Log.error("#{e.class.name}: #{e.message} - #{e.backtrace.first}")
|
514
|
-
end
|
515
|
-
|
516
|
-
# Changes owner of directories and files from given path
|
517
|
-
#
|
518
|
-
# === Parameters
|
519
|
-
# username(String):: desired owner's username
|
520
|
-
# group(String):: desired group name
|
521
|
-
# path(String):: path for owner changing
|
522
|
-
#
|
523
|
-
# === Return
|
524
|
-
# chowned(Boolean):: true if owner changed successfully
|
525
|
-
def change_owner(username, group, path)
|
526
|
-
result = sudo("chown -R #{Shellwords.escape(username)}:#{Shellwords.escape(group)} #{path}")
|
527
|
-
|
528
|
-
result.success?
|
529
|
-
end
|
530
407
|
end
|
531
408
|
end
|