right_agent 0.17.2 → 1.0.1
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.
- data/lib/right_agent.rb +0 -1
- data/lib/right_agent/agent_config.rb +1 -1
- data/lib/right_agent/minimal.rb +8 -7
- data/lib/right_agent/monkey_patches.rb +4 -2
- data/lib/right_agent/monkey_patches/ruby_patch.rb +9 -9
- data/lib/right_agent/monkey_patches/ruby_patch/linux_patch/file_patch.rb +2 -2
- data/lib/right_agent/monkey_patches/ruby_patch/windows_patch/file_patch.rb +21 -51
- data/lib/right_agent/packets.rb +5 -1
- data/lib/right_agent/platform.rb +727 -299
- data/lib/right_agent/platform/unix/darwin/platform.rb +102 -0
- data/lib/right_agent/platform/unix/linux/platform.rb +305 -0
- data/lib/right_agent/platform/unix/platform.rb +226 -0
- data/lib/right_agent/platform/windows/mingw/platform.rb +447 -0
- data/lib/right_agent/platform/windows/mswin/platform.rb +236 -0
- data/lib/right_agent/platform/windows/platform.rb +1808 -0
- data/right_agent.gemspec +13 -8
- data/spec/platform/spec_helper.rb +216 -0
- data/spec/platform/unix/darwin/platform_spec.rb +181 -0
- data/spec/platform/unix/linux/platform_spec.rb +540 -0
- data/spec/platform/unix/spec_helper.rb +149 -0
- data/spec/platform/windows/mingw/platform_spec.rb +222 -0
- data/spec/platform/windows/mswin/platform_spec.rb +259 -0
- data/spec/platform/windows/spec_helper.rb +720 -0
- metadata +45 -30
- data/lib/right_agent/platform/darwin.rb +0 -285
- data/lib/right_agent/platform/linux.rb +0 -537
- data/lib/right_agent/platform/windows.rb +0 -1384
- data/spec/platform/darwin_spec.rb +0 -13
- data/spec/platform/linux_spec.rb +0 -38
- data/spec/platform/linux_volume_manager_spec.rb +0 -201
- data/spec/platform/platform_spec.rb +0 -80
- data/spec/platform/windows_spec.rb +0 -13
- data/spec/platform/windows_volume_manager_spec.rb +0 -318
data/lib/right_agent.rb
CHANGED
@@ -26,7 +26,6 @@ require File.expand_path(File.join(File.dirname(__FILE__), 'right_agent', 'minim
|
|
26
26
|
require 'json'
|
27
27
|
require 'openssl'
|
28
28
|
require 'right_amqp'
|
29
|
-
require 'right_support'
|
30
29
|
|
31
30
|
require File.normalize_path(File.join(RIGHT_AGENT_BASE_DIR, 'monkey_patches'))
|
32
31
|
require File.normalize_path(File.join(RIGHT_AGENT_BASE_DIR, 'payload_formatter'))
|
data/lib/right_agent/minimal.rb
CHANGED
@@ -23,13 +23,14 @@
|
|
23
23
|
require 'rubygems'
|
24
24
|
require 'eventmachine'
|
25
25
|
require 'fileutils'
|
26
|
+
require 'right_support'
|
26
27
|
require 'yaml'
|
27
28
|
|
28
29
|
# load definition for File.normalize_path, etc.
|
29
|
-
require File.expand_path(
|
30
|
+
require ::File.expand_path('../monkey_patches', __FILE__)
|
30
31
|
|
31
32
|
unless defined?(RIGHT_AGENT_BASE_DIR)
|
32
|
-
RIGHT_AGENT_BASE_DIR = File.normalize_path(
|
33
|
+
RIGHT_AGENT_BASE_DIR = ::File.normalize_path('..', __FILE__)
|
33
34
|
end
|
34
35
|
|
35
36
|
# require minimal gems needed to create a CommandClient and send a command.
|
@@ -37,8 +38,8 @@ end
|
|
37
38
|
# FIX: agent_controller is currently the only minimal-load use case so these
|
38
39
|
# requires are oriented toward that. any additional use cases may require a
|
39
40
|
# rethink of minimal loading.
|
40
|
-
require File.normalize_path(
|
41
|
-
require File.normalize_path(
|
42
|
-
require File.normalize_path(
|
43
|
-
require File.normalize_path(
|
44
|
-
require File.normalize_path(
|
41
|
+
require ::File.normalize_path('agent_config', RIGHT_AGENT_BASE_DIR)
|
42
|
+
require ::File.normalize_path('command', RIGHT_AGENT_BASE_DIR)
|
43
|
+
require ::File.normalize_path('log', RIGHT_AGENT_BASE_DIR)
|
44
|
+
require ::File.normalize_path('pid_file', RIGHT_AGENT_BASE_DIR)
|
45
|
+
require ::File.normalize_path('serialize/serializable', RIGHT_AGENT_BASE_DIR)
|
@@ -23,6 +23,8 @@
|
|
23
23
|
require 'rubygems'
|
24
24
|
require 'rbconfig'
|
25
25
|
|
26
|
-
|
26
|
+
unless defined?(RIGHT_AGENT_MONKEY_PATCHES_BASE_DIR)
|
27
|
+
require ::File.expand_path('../monkey_patches/ruby_patch', __FILE__)
|
27
28
|
|
28
|
-
|
29
|
+
RIGHT_AGENT_MONKEY_PATCHES_BASE_DIR = ::File.normalize_path('../monkey_patches', __FILE__)
|
30
|
+
end
|
@@ -32,24 +32,24 @@ require 'socket'
|
|
32
32
|
# using short path. Since this is where we define the File.normalize_path
|
33
33
|
# method to alleviate this issue, we have a chicken & egg problem. So detect if
|
34
34
|
# we already required this file and skip the rest if that was the case.
|
35
|
-
unless defined?(
|
35
|
+
unless defined?(RIGHT_AGENT_RUBY_PATCH_BASE_DIR)
|
36
36
|
|
37
37
|
# Load platform-specific patches before any other patches (in order to define
|
38
38
|
# File.normalize_path, etc.)
|
39
|
-
case
|
39
|
+
case ::RbConfig::CONFIG['host_os']
|
40
40
|
when /mswin|win32|dos|mingw|cygwin/i
|
41
|
-
require File.expand_path(
|
41
|
+
require ::File.expand_path('../ruby_patch/windows_patch', __FILE__)
|
42
42
|
when /linux/i
|
43
|
-
require File.expand_path(
|
43
|
+
require ::File.expand_path('../ruby_patch/linux_patch', __FILE__)
|
44
44
|
when /darwin/i
|
45
|
-
require File.expand_path(
|
45
|
+
require ::File.expand_path('../ruby_patch/darwin_patch', __FILE__)
|
46
46
|
else
|
47
|
-
raise LoadError, "Unsupported platform: #{
|
47
|
+
raise LoadError, "Unsupported platform: #{::RbConfig::CONFIG['host_os']}"
|
48
48
|
end
|
49
49
|
|
50
|
-
|
50
|
+
RIGHT_AGENT_RUBY_PATCH_BASE_DIR = ::File.normalize_path('../ruby_patch', __FILE__)
|
51
51
|
|
52
|
-
require File.
|
53
|
-
require File.
|
52
|
+
require File.join(RIGHT_AGENT_RUBY_PATCH_BASE_DIR, 'array_patch')
|
53
|
+
require File.join(RIGHT_AGENT_RUBY_PATCH_BASE_DIR, 'object_patch')
|
54
54
|
|
55
55
|
end # Unless already defined
|
@@ -1,5 +1,5 @@
|
|
1
1
|
#
|
2
|
-
# Copyright (c) 2011 RightScale Inc
|
2
|
+
# Copyright (c) 2011-2013 RightScale Inc
|
3
3
|
#
|
4
4
|
# Permission is hereby granted, free of charge, to any person obtaining
|
5
5
|
# a copy of this software and associated documentation files (the
|
@@ -24,7 +24,7 @@ class File
|
|
24
24
|
|
25
25
|
# On *nix systems, resolves to File.expand_path
|
26
26
|
def self.normalize_path(file_name, *dir_string)
|
27
|
-
|
27
|
+
self.expand_path(file_name, *dir_string)
|
28
28
|
end
|
29
29
|
|
30
30
|
end
|
@@ -1,5 +1,5 @@
|
|
1
1
|
#
|
2
|
-
# Copyright (c) 2011 RightScale Inc
|
2
|
+
# Copyright (c) 2011-2013 RightScale Inc
|
3
3
|
#
|
4
4
|
# Permission is hereby granted, free of charge, to any person obtaining
|
5
5
|
# a copy of this software and associated documentation files (the
|
@@ -23,68 +23,38 @@
|
|
23
23
|
require 'rubygems'
|
24
24
|
|
25
25
|
begin
|
26
|
-
|
26
|
+
|
27
|
+
# attempt to early-load basic Windows API gems so that we can rescue and
|
28
|
+
# switch to using the simple definition of File.normalize_path when these gems
|
29
|
+
# are unavailable.
|
30
|
+
require ::File.expand_path('../../../../platform', __FILE__)
|
27
31
|
|
28
32
|
class File
|
29
33
|
|
30
|
-
#
|
31
|
-
#
|
32
|
-
#
|
33
|
-
# names exist in the same directory. file extensions are simply chopped
|
34
|
-
# at three letters. the short name is equivalent for all API calls to
|
35
|
-
# the long path but requires no special quoting, etc. the path must
|
36
|
-
# exist at least partially for the API call to succeed.
|
34
|
+
# Expand the path then shorten the directory, if possible.
|
35
|
+
# Only shortens the parent directory and not the leaf (file or directory)
|
36
|
+
# name because 'gem' wants the file name to be long for require.
|
37
37
|
#
|
38
|
-
#
|
39
|
-
#
|
40
|
-
#
|
38
|
+
# @param [String] file_name to normalize
|
39
|
+
# @param [String] dir_string as base directory path for relative file_name
|
40
|
+
# or ignored when file_name is absolute (Default = working directory).
|
41
41
|
#
|
42
|
-
#
|
43
|
-
# short_path(String):: short path equivalent or same path if non-existent
|
44
|
-
def self.long_path_to_short_path(long_path)
|
45
|
-
if File.exists?(long_path)
|
46
|
-
length = 260
|
47
|
-
while true
|
48
|
-
buffer = 0.chr * length
|
49
|
-
length = ::Windows::File::GetShortPathName.call(long_path, buffer, buffer.length)
|
50
|
-
if length < buffer.length
|
51
|
-
break
|
52
|
-
end
|
53
|
-
end
|
54
|
-
return buffer.unpack('A*').first.gsub("\\", "/")
|
55
|
-
else
|
56
|
-
# must get short path for any existing ancestor since child doesn't
|
57
|
-
# (currently) exist.
|
58
|
-
child_name = File.basename(long_path)
|
59
|
-
long_parent_path = File.dirname(long_path)
|
60
|
-
|
61
|
-
# note that root dirname is root itself (at least in windows)
|
62
|
-
return long_path if long_path == long_parent_path
|
63
|
-
|
64
|
-
# recursion
|
65
|
-
short_parent_path = long_path_to_short_path(File.dirname(long_path))
|
66
|
-
return File.join(short_parent_path, child_name)
|
67
|
-
end
|
68
|
-
end
|
69
|
-
|
70
|
-
# First expand the path then shorten the directory.
|
71
|
-
# Only shorten the directory and not the file name because 'gem' wants
|
72
|
-
# long file names
|
42
|
+
# @return [String] normalized path
|
73
43
|
def self.normalize_path(file_name, *dir_string)
|
74
|
-
path =
|
75
|
-
dir =
|
76
|
-
|
44
|
+
path = self.expand_path(file_name, *dir_string)
|
45
|
+
dir = ::RightScale::Platform.filesystem.long_path_to_short_path(self.dirname(path))
|
46
|
+
self.join(dir, self.basename(path))
|
77
47
|
end
|
78
|
-
|
79
48
|
end
|
80
49
|
|
81
50
|
rescue LoadError
|
82
|
-
|
83
|
-
#
|
51
|
+
|
52
|
+
# use the simple definition of normalize_path on load error. the purpose of
|
53
|
+
# normalize_path is to disambiguate load paths but it is possible to continue
|
54
|
+
# with ambiguity in most cases and any other Win32 API calls will fail.
|
84
55
|
class File
|
85
56
|
def self.normalize_path(file_name, *dir_string)
|
86
|
-
|
57
|
+
self.expand_path(file_name, *dir_string)
|
87
58
|
end
|
88
59
|
end
|
89
|
-
|
90
60
|
end
|
data/lib/right_agent/packets.rb
CHANGED
@@ -24,6 +24,10 @@
|
|
24
24
|
module JSON
|
25
25
|
class << self
|
26
26
|
def parse(source, opts = {})
|
27
|
+
# In gem version this options is set to true by default
|
28
|
+
# but in ruby native json library is set to false by default
|
29
|
+
# Explicitly set it to true so both json libraries act the same
|
30
|
+
opts[:create_additions] = true
|
27
31
|
if source =~ /(.*)json_class":"Nanite::(.*)/
|
28
32
|
JSON.parser.new( Regexp.last_match(1) + 'json_class":"RightScale::' + Regexp.last_match(2), opts).parse
|
29
33
|
else
|
@@ -248,7 +252,7 @@ module RightScale
|
|
248
252
|
def trace
|
249
253
|
audit_id = self.respond_to?(:payload) && payload.is_a?(Hash) && (payload['audit_id'] || payload[:audit_id])
|
250
254
|
tok = self.respond_to?(:token) && token
|
251
|
-
tr = "<#{audit_id || nil}> <#{tok}>"
|
255
|
+
tr = "<#{audit_id || nil}> <#{tok}>"
|
252
256
|
end
|
253
257
|
|
254
258
|
# Retrieve protocol version of original creator of packet
|
data/lib/right_agent/platform.rb
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
#
|
2
|
-
# Copyright (c) 2009-
|
2
|
+
# Copyright (c) 2009-2013 RightScale Inc
|
3
3
|
#
|
4
4
|
# Permission is hereby granted, free of charge, to any person obtaining
|
5
5
|
# a copy of this software and associated documentation files (the
|
@@ -26,317 +26,745 @@
|
|
26
26
|
# we already required this file and skip the rest if that was the case.
|
27
27
|
unless defined?(RightScale::Platform)
|
28
28
|
|
29
|
-
# Note that the platform-specific submodules will be loaded on demand to resolve
|
30
|
-
# some install-time gem dependency issues.
|
31
|
-
|
32
|
-
require 'rbconfig'
|
33
|
-
require 'right_support'
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
class PlatformNotSupported < Exception; end
|
42
|
-
|
43
|
-
# A utility class that provides information about the platform on which the RightAgent is running
|
44
|
-
# Available information includes:
|
45
|
-
# - which flavor cloud (EC2, Rackspace, Eucalyptus, ..)
|
46
|
-
# - which flavor operating system (Linux, Windows or Mac)
|
47
|
-
# - which OS release (a numeric value that is specific to the OS)
|
48
|
-
# - directories in which various bits of RightScale state may be found
|
49
|
-
# - platform-specific information such as Linux flavor or release
|
50
|
-
#
|
51
|
-
# For platform information only used in specific contexts, the dispatch method may be used
|
52
|
-
#
|
53
|
-
# This is a summary of the information you can query by calling Platform's instance methods:
|
54
|
-
# - .flavor
|
55
|
-
# - .release
|
56
|
-
# - .linux?
|
57
|
-
# - .darwin?
|
58
|
-
# - .windows?
|
59
|
-
# - .ec2?
|
60
|
-
# - .rackspace?
|
61
|
-
# - .eucalyptus?
|
62
|
-
# - .filesystem
|
63
|
-
# - right_scale_state_dir
|
64
|
-
# - spool_dir
|
65
|
-
# - cache_dir
|
66
|
-
# - .linux (only available under Linux)
|
67
|
-
# - ubuntu?
|
68
|
-
# - centos?
|
69
|
-
# - suse?
|
70
|
-
class Platform
|
71
|
-
|
72
|
-
include RightSupport::Ruby::EasySingleton
|
73
|
-
|
74
|
-
# Generic platform family
|
75
|
-
#
|
76
|
-
# === Return
|
77
|
-
# family(Symbol):: One of :linux, :windows or :darwin
|
78
|
-
def family
|
79
|
-
@family ||= case RbConfig::CONFIG['host_os']
|
80
|
-
when /mswin|win32|dos|mingw|cygwin/i then :windows
|
81
|
-
when /darwin/i then :darwin
|
82
|
-
when /linux/i then :linux
|
83
|
-
end
|
84
|
-
end
|
85
|
-
|
86
|
-
# Is current platform linux?
|
87
|
-
#
|
88
|
-
# === Return
|
89
|
-
# true:: If current platform is linux
|
90
|
-
# false:: Otherwise
|
91
|
-
def linux?
|
92
|
-
return family == :linux
|
93
|
-
end
|
94
|
-
|
95
|
-
# Is current platform darwin?
|
96
|
-
#
|
97
|
-
# === Return
|
98
|
-
# true:: If current platform is darwin
|
99
|
-
# false:: Otherwise
|
100
|
-
def darwin?
|
101
|
-
return family == :darwin
|
102
|
-
end
|
103
|
-
# Is current platform windows?
|
104
|
-
#
|
105
|
-
# === Return
|
106
|
-
# true:: If current platform is Windows
|
107
|
-
# false:: Otherwise
|
108
|
-
def windows?
|
109
|
-
return family == :windows
|
110
|
-
end
|
111
|
-
|
112
|
-
# Call platform specific implementation of method whose symbol is returned
|
113
|
-
# by the passed in block. Arguments are passed through.
|
114
|
-
# e.g.
|
115
|
-
#
|
116
|
-
# Platform.dispatch(2) { :echo }
|
117
|
-
#
|
118
|
-
# will result in 'echo_linux(2)' being executed in self if running on linux,
|
119
|
-
# 'echo_windows(2)' if running on Windows and 'echo_darwin(2)' if on Mac OS X.
|
120
|
-
# Note that the method is run in the instance of the caller.
|
121
|
-
#
|
122
|
-
# === Parameters
|
123
|
-
# args:: Pass-through arguments
|
124
|
-
#
|
125
|
-
# === Block
|
126
|
-
# Given block should not take any argument and return a symbol for the
|
127
|
-
# method that should be called
|
128
|
-
#
|
129
|
-
# === Return
|
130
|
-
# res(Object):: Result returned by platform specific implementation
|
131
|
-
def dispatch(*args, &blk)
|
132
|
-
raise "Platform.dispatch requires a block" unless blk
|
133
|
-
binding = blk.binding.eval('self')
|
134
|
-
meth = blk.call
|
135
|
-
target = dispatch_candidates(meth).detect do |candidate|
|
136
|
-
binding.respond_to?(candidate)
|
137
|
-
end
|
138
|
-
raise "No platform dispatch target found in #{binding.class} for " +
|
139
|
-
"'#{meth.inspect}', tried " + dispatch_candidates(meth).join(', ') unless target
|
140
|
-
binding.__send__(target, *args)
|
141
|
-
end
|
142
|
-
|
143
|
-
# Load platform specific implementation
|
144
|
-
#
|
145
|
-
# === Return
|
146
|
-
# true:: Always return true
|
147
|
-
def load_platform_specific
|
148
|
-
if linux?
|
149
|
-
require_linux
|
150
|
-
elsif darwin?
|
151
|
-
require_darwin
|
152
|
-
elsif windows?
|
153
|
-
require_windows
|
154
|
-
else
|
155
|
-
raise PlatformError.new('Unknown platform')
|
156
|
-
end
|
157
|
-
end
|
158
|
-
|
159
|
-
# Are we in an EC2 cloud?
|
160
|
-
#
|
161
|
-
# === Return
|
162
|
-
# true:: If machine is located in an EC2 cloud
|
163
|
-
# false:: Otherwise
|
164
|
-
def ec2?
|
165
|
-
resolve_cloud_type if @ec2.nil?
|
166
|
-
@ec2
|
167
|
-
end
|
168
|
-
|
169
|
-
# Are we in a Rackspace cloud?
|
170
|
-
#
|
171
|
-
# === Return
|
172
|
-
# true:: If machine is located in an Rackspace cloud
|
173
|
-
# false:: Otherwise
|
174
|
-
def rackspace?
|
175
|
-
resolve_cloud_type if @rackspace.nil?
|
176
|
-
@rackspace
|
177
|
-
end
|
178
|
-
|
179
|
-
# Are we in a Eucalyptus cloud?
|
29
|
+
# Note that the platform-specific submodules will be loaded on demand to resolve
|
30
|
+
# some install-time gem dependency issues.
|
31
|
+
require 'rubygems'
|
32
|
+
require 'rbconfig'
|
33
|
+
require 'right_support'
|
34
|
+
|
35
|
+
require ::File.expand_path('../exceptions', __FILE__)
|
36
|
+
|
37
|
+
module RightScale
|
38
|
+
|
39
|
+
# A utility class that provides information about the platform on which the
|
40
|
+
# RightAgent is running.
|
180
41
|
#
|
181
|
-
#
|
182
|
-
#
|
183
|
-
#
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
end
|
188
|
-
|
189
|
-
# Controller object
|
42
|
+
# Available information includes:
|
43
|
+
# - which flavor cloud (EC2, Rackspace, Eucalyptus, ..)
|
44
|
+
# - which flavor operating system (Linux, Windows or Mac)
|
45
|
+
# - which OS release (a numeric value that is specific to the OS)
|
46
|
+
# - directories in which various bits of RightScale state may be found
|
47
|
+
# - platform-specific information such as Linux flavor or release
|
190
48
|
#
|
191
|
-
#
|
192
|
-
#
|
193
|
-
|
194
|
-
platform_service(:controller)
|
195
|
-
end
|
49
|
+
# For platform information only used in specific contexts, the dispatch
|
50
|
+
# method may be used.
|
51
|
+
class Platform
|
196
52
|
|
197
|
-
|
198
|
-
#
|
199
|
-
# === Return
|
200
|
-
# (Filesystem):: Platform-specific filesystem config object
|
201
|
-
def filesystem
|
202
|
-
platform_service(:filesystem)
|
203
|
-
end
|
53
|
+
include RightSupport::Ruby::EasySingleton
|
204
54
|
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
end
|
55
|
+
# exceptions
|
56
|
+
class CommandError < RightScale::Exceptions::PlatformError
|
57
|
+
attr_accessor :command
|
58
|
+
attr_accessor :status
|
59
|
+
attr_accessor :output_text
|
60
|
+
end
|
212
61
|
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
62
|
+
attr_reader :flavor, :release, :codename
|
63
|
+
|
64
|
+
# Generic platform family
|
65
|
+
#
|
66
|
+
# @deprecated family is a legacy definition, see genus/species for a more
|
67
|
+
# granular definition.
|
68
|
+
#
|
69
|
+
# @return [Symbol] result as one of :linux, :windows or :darwin
|
70
|
+
def family
|
71
|
+
warn "#{self.class.name}#family is deprecated, use genus or species instead.\n#{caller[0,2].join("\n")}"
|
72
|
+
(genus == :unix) ? species : genus
|
73
|
+
end
|
220
74
|
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
platform_service(:ssh)
|
227
|
-
end
|
75
|
+
# @return [Symbol] platform genus
|
76
|
+
def genus
|
77
|
+
resolve_taxonomy unless @genus
|
78
|
+
@genus
|
79
|
+
end
|
228
80
|
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
platform_service(:rng)
|
235
|
-
end
|
81
|
+
# @return [Symbol] platform species
|
82
|
+
def species
|
83
|
+
resolve_taxonomy unless @species
|
84
|
+
@species
|
85
|
+
end
|
236
86
|
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
|
267
|
-
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
|
273
|
-
|
274
|
-
|
275
|
-
|
276
|
-
|
277
|
-
|
278
|
-
@
|
279
|
-
|
280
|
-
@
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
-
|
285
|
-
|
286
|
-
|
287
|
-
#
|
288
|
-
|
289
|
-
|
290
|
-
|
291
|
-
|
292
|
-
|
293
|
-
|
294
|
-
|
295
|
-
|
296
|
-
|
297
|
-
|
298
|
-
|
299
|
-
|
300
|
-
|
301
|
-
|
302
|
-
|
303
|
-
|
304
|
-
|
305
|
-
|
306
|
-
|
307
|
-
|
308
|
-
|
309
|
-
|
310
|
-
|
311
|
-
|
312
|
-
|
313
|
-
|
314
|
-
|
315
|
-
|
316
|
-
|
317
|
-
|
318
|
-
|
319
|
-
|
87
|
+
# Is current platform in the Unix genus?
|
88
|
+
#
|
89
|
+
# @return [TrueClass|FalseClass] true if Unix
|
90
|
+
def unix?
|
91
|
+
genus == :unix
|
92
|
+
end
|
93
|
+
|
94
|
+
# Is current platform Linux?
|
95
|
+
#
|
96
|
+
# @return [TrueClass|FalseClass] true if Linux
|
97
|
+
def linux?
|
98
|
+
species == :linux
|
99
|
+
end
|
100
|
+
|
101
|
+
# Is current platform Darwin?
|
102
|
+
#
|
103
|
+
# @return [TrueClass|FalseClass] true if Darwin
|
104
|
+
def darwin?
|
105
|
+
species == :darwin
|
106
|
+
end
|
107
|
+
|
108
|
+
# Is current platform Windows?
|
109
|
+
#
|
110
|
+
# @return [TrueClass|FalseClass] true if Windows
|
111
|
+
def windows?
|
112
|
+
genus == :windows
|
113
|
+
end
|
114
|
+
|
115
|
+
# Are we on an EC2 cloud instance?
|
116
|
+
#
|
117
|
+
# @deprecated use right_link cloud libraries instead.
|
118
|
+
#
|
119
|
+
# @return [TrueClass|FalseClass] true if EC2
|
120
|
+
def ec2?
|
121
|
+
warn "#{self.class.name}#ec2? is deprecated, use right_link cloud libraries instead.\n#{caller[0,2].join("\n")}"
|
122
|
+
resolve_cloud_type unless @cloud_type
|
123
|
+
@cloud_type == 'ec2'
|
124
|
+
end
|
125
|
+
|
126
|
+
# Are we on an Rackspace cloud instance?
|
127
|
+
#
|
128
|
+
# @deprecated use right_link cloud libraries instead.
|
129
|
+
#
|
130
|
+
# @return [TrueClass|FalseClass] true if Rackspace
|
131
|
+
def rackspace?
|
132
|
+
warn "#{self.class.name}#rackspace? is deprecated, use right_link cloud libraries instead.\n#{caller[0,2].join("\n")}"
|
133
|
+
resolve_cloud_type unless @cloud_type
|
134
|
+
@cloud_type == 'rackspace'
|
135
|
+
end
|
136
|
+
|
137
|
+
# Are we on an Eucalyptus cloud instance?
|
138
|
+
#
|
139
|
+
# @deprecated use right_link cloud libraries instead.
|
140
|
+
#
|
141
|
+
# @return [TrueClass|FalseClass] true if Eucalyptus
|
142
|
+
def eucalyptus?
|
143
|
+
warn "#{self.class.name}#eucalyptus? is deprecated, use right_link cloud libraries instead.\n#{caller[0,2].join("\n")}"
|
144
|
+
resolve_cloud_type unless @cloud_type
|
145
|
+
@cloud_type == 'eucalyptus'
|
146
|
+
end
|
147
|
+
|
148
|
+
# Call platform specific implementation of method whose symbol is returned
|
149
|
+
# by the passed in block. Arguments are passed through.
|
150
|
+
# e.g.
|
151
|
+
#
|
152
|
+
# Platform.dispatch(2) { :echo }
|
153
|
+
#
|
154
|
+
# will result in 'echo_linux(2)' being executed in self if running on
|
155
|
+
# linux, 'echo_windows(2)' if running on Windows and 'echo_darwin(2)' if
|
156
|
+
# on Mac OS X. Note that the method is run in the instance of the caller.
|
157
|
+
#
|
158
|
+
# @param [Array] args as pass-through arguments
|
159
|
+
#
|
160
|
+
# @yield [] given block should not take any argument and return a symbol
|
161
|
+
# for the method that should be called.
|
162
|
+
#
|
163
|
+
# @return [Object] result of Platform-specific implementation
|
164
|
+
def dispatch(*args, &blk)
|
165
|
+
raise 'Platform.dispatch requires a block' unless blk
|
166
|
+
binding = blk.binding.eval('self')
|
167
|
+
meth = blk.call
|
168
|
+
target = dispatch_candidates(meth).detect do |candidate|
|
169
|
+
binding.respond_to?(candidate)
|
170
|
+
end
|
171
|
+
raise "No platform dispatch target found in #{binding.class} for " +
|
172
|
+
"'#{meth.inspect}', tried " + dispatch_candidates(meth).join(', ') unless target
|
173
|
+
binding.__send__(target, *args)
|
174
|
+
end
|
175
|
+
|
176
|
+
# @return [Controller] Platform-specific controller object
|
177
|
+
def controller
|
178
|
+
platform_service(:controller)
|
179
|
+
end
|
180
|
+
|
181
|
+
# @return [Filesystem] Platform-specific filesystem config object
|
182
|
+
def filesystem
|
183
|
+
platform_service(:filesystem)
|
184
|
+
end
|
185
|
+
|
186
|
+
# @return [VolumeManager] Platform-specific volume manager config object
|
187
|
+
def volume_manager
|
188
|
+
platform_service(:volume_manager)
|
189
|
+
end
|
190
|
+
|
191
|
+
# @return [Shell] Platform-specific shell information object
|
192
|
+
def shell
|
193
|
+
platform_service(:shell)
|
194
|
+
end
|
195
|
+
|
196
|
+
# @return [Rng] Platform-specific RNG object
|
197
|
+
def rng
|
198
|
+
platform_service(:rng)
|
199
|
+
end
|
200
|
+
|
201
|
+
# @return [Process] Platform-specific process facilities object
|
202
|
+
def process
|
203
|
+
platform_service(:process)
|
204
|
+
end
|
205
|
+
|
206
|
+
# @return [Installer] Platform-specific installer information object
|
207
|
+
def installer
|
208
|
+
platform_service(:installer)
|
209
|
+
end
|
210
|
+
|
211
|
+
# Blocking call to invoke a command line tool used to perform platform-
|
212
|
+
# specific tasks.
|
213
|
+
#
|
214
|
+
# Also provides a consistent interface for mocking command output during
|
215
|
+
# spec testing. Implementations should use this method instead of
|
216
|
+
# embedding popen/backtick calls to assist with testing.
|
217
|
+
#
|
218
|
+
# @param [String] command to run
|
219
|
+
# @param [Hash] options for execution
|
220
|
+
# @option options [TrueClass|FalseClass] :raise_on_failure true to raise on command failure (Default)
|
221
|
+
#
|
222
|
+
# @return [String] output from command or empty
|
223
|
+
#
|
224
|
+
# @raise [CommandError] on failure (by default)
|
225
|
+
def execute(command, options = {})
|
226
|
+
options = { :raise_on_failure => true }.merge(options)
|
227
|
+
raise_on_failure = options[:raise_on_failure]
|
228
|
+
output_text = ''
|
229
|
+
begin
|
230
|
+
output_text = `#{command}`
|
231
|
+
if !$?.success? && options[:raise_on_failure]
|
232
|
+
message = []
|
233
|
+
message << "Command failed with exit code = #{$?.exitstatus}:"
|
234
|
+
message << "> #{command}"
|
235
|
+
error_output_text = output_text.strip
|
236
|
+
message << error_output_text unless error_output_text.empty?
|
237
|
+
e = CommandError.new(message.join("\n"))
|
238
|
+
e.command = command
|
239
|
+
e.status = $?
|
240
|
+
e.output_text = output_text
|
241
|
+
raise e
|
242
|
+
end
|
243
|
+
rescue Errno::ENOENT => e
|
244
|
+
if raise_on_failure
|
245
|
+
raise CommandError, "Command failed: #{e.message}"
|
246
|
+
end
|
247
|
+
end
|
248
|
+
output_text
|
249
|
+
end
|
250
|
+
|
251
|
+
# Base class for platform helpers.
|
252
|
+
class PlatformHelperBase
|
253
|
+
|
254
|
+
private
|
255
|
+
|
256
|
+
# Convenience method for declaring must-be-overridden interfaces.
|
257
|
+
#
|
258
|
+
# @raise [NotImplementedError] always unless overridden
|
259
|
+
def must_be_overridden
|
260
|
+
raise ::NotImplementedError, 'Must be overridden'
|
261
|
+
end
|
262
|
+
|
263
|
+
# Convenience method for executing a command via platform.
|
264
|
+
#
|
265
|
+
# See Platform#execute
|
266
|
+
def execute(cmd, options = {})
|
267
|
+
::RightScale::Platform.execute(cmd, options)
|
268
|
+
end
|
269
|
+
end
|
270
|
+
|
271
|
+
# Declares various file system APIs.
|
272
|
+
class Filesystem < PlatformHelperBase
|
273
|
+
|
274
|
+
# Is given command available in the PATH?
|
275
|
+
#
|
276
|
+
# @param [String] command_name to be tested
|
277
|
+
#
|
278
|
+
# @return [TrueClass|FalseClass] true if command is in path
|
279
|
+
def has_executable_in_path(command_name)
|
280
|
+
return !!find_executable_in_path(command_name)
|
281
|
+
end
|
282
|
+
|
283
|
+
# Finds the given command name in the PATH. this emulates the 'which'
|
284
|
+
# command from linux (without the terminating newline).
|
285
|
+
#
|
286
|
+
# @param [String] command_name to be tested
|
287
|
+
#
|
288
|
+
# @return [String] path to first matching executable file in PATH or nil
|
289
|
+
def find_executable_in_path(command_name)
|
290
|
+
must_be_overridden
|
291
|
+
end
|
292
|
+
|
293
|
+
# @return [String] directory containing generated agent configuration files
|
294
|
+
def right_agent_cfg_dir
|
295
|
+
must_be_overridden
|
296
|
+
end
|
297
|
+
|
298
|
+
# @return [String] static (time-invariant) state that is common to all RightScale apps/agents
|
299
|
+
def right_scale_static_state_dir
|
300
|
+
must_be_overridden
|
301
|
+
end
|
302
|
+
|
303
|
+
# @return [String] static (time-invariant) state that is specific to RightLink
|
304
|
+
def right_link_static_state_dir
|
305
|
+
must_be_overridden
|
306
|
+
end
|
307
|
+
|
308
|
+
# @return [String] dynamic, persistent runtime state that is specific to RightLink
|
309
|
+
def right_link_dynamic_state_dir
|
310
|
+
must_be_overridden
|
311
|
+
end
|
312
|
+
|
313
|
+
# @return [String] data which is awaiting some kind of later processing
|
314
|
+
def spool_dir
|
315
|
+
must_be_overridden
|
316
|
+
end
|
317
|
+
|
318
|
+
# TEAL TODO description
|
319
|
+
def ssh_cfg_dir
|
320
|
+
must_be_overridden
|
321
|
+
end
|
322
|
+
|
323
|
+
# Cached data from applications. Such data is locally generated as a
|
324
|
+
# result of time-consuming I/O or calculation. The application must
|
325
|
+
# be able to regenerate or restore the data.
|
326
|
+
#
|
327
|
+
# @return [String] cache directory
|
328
|
+
def cache_dir
|
329
|
+
must_be_overridden
|
330
|
+
end
|
331
|
+
|
332
|
+
# @return [String] system logs
|
333
|
+
def log_dir
|
334
|
+
must_be_overridden
|
335
|
+
end
|
320
336
|
|
321
|
-
|
337
|
+
# For Unix compatibility; has no significance in Windows
|
338
|
+
#
|
339
|
+
# @return [String] source code directory, for reference purposes and for development
|
340
|
+
def source_code_dir
|
341
|
+
must_be_overridden
|
342
|
+
end
|
343
|
+
|
344
|
+
# @return [String] temporary files.
|
345
|
+
def temp_dir
|
346
|
+
must_be_overridden
|
347
|
+
end
|
348
|
+
|
349
|
+
# @return [String] path to place pid files
|
350
|
+
def pid_dir
|
351
|
+
must_be_overridden
|
352
|
+
end
|
353
|
+
|
354
|
+
# @return [String] installed home (parent of) right_link directory path
|
355
|
+
def right_link_home_dir
|
356
|
+
must_be_overridden
|
357
|
+
end
|
358
|
+
|
359
|
+
# @return [String] path to right link configuration and internal usage scripts
|
360
|
+
def private_bin_dir
|
361
|
+
must_be_overridden
|
362
|
+
end
|
363
|
+
|
364
|
+
# TEAL TODO description
|
365
|
+
def sandbox_dir
|
366
|
+
must_be_overridden
|
367
|
+
end
|
368
|
+
|
369
|
+
# Converts a long path (e.g. "C:/Program Files") to a short path
|
370
|
+
# (e.g. "C:/PROGRA~1") if necessary. See implementation for notes.
|
371
|
+
#
|
372
|
+
# For Windows compatibility; has no significance in Linux
|
373
|
+
#
|
374
|
+
# @param [String] long_path to convert
|
375
|
+
#
|
376
|
+
# @return [String] short path
|
377
|
+
def long_path_to_short_path(long_path)
|
378
|
+
must_be_overridden
|
379
|
+
end
|
380
|
+
|
381
|
+
# Converts slashes in a path to a consistent style.
|
382
|
+
#
|
383
|
+
# For Windows compatibility; has no significance in Linux
|
384
|
+
#
|
385
|
+
# @param [String] path to make pretty
|
386
|
+
# @param [String] native_fs_flag as true if path is pretty for native
|
387
|
+
# file system (i.e. file system calls more likely to succeed), false
|
388
|
+
# if pretty for Ruby interpreter (default).
|
389
|
+
#
|
390
|
+
# @return [String] pretty path
|
391
|
+
def pretty_path(path, native_fs_flag = false)
|
392
|
+
must_be_overridden
|
393
|
+
end
|
394
|
+
|
395
|
+
# Ensures a local drive location for the file or folder given by path
|
396
|
+
# by copying to a local temp directory given by name only if the item
|
397
|
+
# does not appear on the home drive. This method is useful because
|
398
|
+
# secure applications refuse to run scripts from network locations, etc.
|
399
|
+
# Replaces any similar files in given temp dir to ensure latest updates.
|
400
|
+
#
|
401
|
+
# For Windows compatibility; has no significance in Linux
|
402
|
+
#
|
403
|
+
# @param [String] path to file or directory to be placed locally
|
404
|
+
# @param [String] temp_dir_name relative to user temp_dir to use only if the file or folder is not on a local drive
|
405
|
+
#
|
406
|
+
# @return [String] local drive path
|
407
|
+
def ensure_local_drive_path(path, temp_dir_name)
|
408
|
+
must_be_overridden
|
409
|
+
end
|
410
|
+
|
411
|
+
# Creates a symlink (if supported by platform).
|
412
|
+
#
|
413
|
+
# @param [String] from_path the path to the real file/directory
|
414
|
+
# @param [String] to_path the path to the symlink to be created
|
415
|
+
#
|
416
|
+
# @return [Fixnum] always 0 as does File.symlink under Linux
|
417
|
+
#
|
418
|
+
# @raise [RightScale::Exceptions::PlatformError] on failure
|
419
|
+
def create_symlink(from_path, to_path)
|
420
|
+
must_be_overridden
|
421
|
+
end
|
422
|
+
end
|
423
|
+
|
424
|
+
# Provides utilities for managing volumes (disks).
|
425
|
+
class VolumeManager < PlatformHelperBase
|
426
|
+
|
427
|
+
# exceptions
|
428
|
+
class ParserError < ::RightScale::Exceptions::PlatformError; end
|
429
|
+
class VolumeError < ::RightScale::Exceptions::PlatformError; end
|
430
|
+
|
431
|
+
# Gets a list of currently visible volumes in the form:
|
432
|
+
# [{:device, :label, :uuid, :type, :filesystem}]
|
433
|
+
#
|
434
|
+
# @param [Hash] conditions to match, if any (Default = no conditions)
|
435
|
+
#
|
436
|
+
# @return [Array] volume info as an array of hashes or empty
|
437
|
+
#
|
438
|
+
# @raise [ParserError] on failure to parse volume list
|
439
|
+
# @raise [VolumeError] on failure to execute `blkid` to obtain raw output
|
440
|
+
def volumes(conditions = nil)
|
441
|
+
must_be_overridden
|
442
|
+
end
|
443
|
+
end # VolumeManager
|
444
|
+
|
445
|
+
# Declares various command shell APIs.
|
446
|
+
class Shell < PlatformHelperBase
|
447
|
+
|
448
|
+
# @return [String] name or path of file reserved for null output redirection
|
449
|
+
def null_output_name
|
450
|
+
must_be_overridden
|
451
|
+
end
|
452
|
+
|
453
|
+
# Fully qualifies a partial script file path to ensure it is executable on
|
454
|
+
# the current platform.
|
455
|
+
#
|
456
|
+
# For Windows compatibility; has no significance in Linux
|
457
|
+
#
|
458
|
+
# @param [String] partial_script_file_path to format
|
459
|
+
# @param [String] default_extension to use if no extension (Default = platform specific)
|
460
|
+
#
|
461
|
+
# @return [String] full script path
|
462
|
+
def format_script_file_name(partial_script_file_path, default_extension = nil)
|
463
|
+
must_be_overridden
|
464
|
+
end
|
465
|
+
|
466
|
+
# Formats an executable command by quoting any of the arguments as needed
|
467
|
+
# and building an executable command string.
|
468
|
+
#
|
469
|
+
# @param [String] executable_file_path full or partial
|
470
|
+
# @param [Array] arguments for executable, if any
|
471
|
+
#
|
472
|
+
# @return [String] executable command string
|
473
|
+
def format_executable_command(executable_file_path, *arguments)
|
474
|
+
must_be_overridden
|
475
|
+
end
|
476
|
+
|
477
|
+
# Formats a shell command using the given script path and arguments.
|
478
|
+
# Provides the path to the executable for the script as needed for the
|
479
|
+
# current platform.
|
480
|
+
#
|
481
|
+
# @param [String] shell_script_file_path shell script file path
|
482
|
+
# @param [Array] arguments for executable, if any
|
483
|
+
#
|
484
|
+
# @return [String] executable command string
|
485
|
+
def format_shell_command(shell_script_file_path, *arguments)
|
486
|
+
must_be_overridden
|
487
|
+
end
|
488
|
+
|
489
|
+
# Formats a ruby command using the given script path and arguments and
|
490
|
+
# the sandbox ruby path.
|
491
|
+
#
|
492
|
+
# @param [String] shell_script_file_path for formatting
|
493
|
+
# @param [Array] arguments for command or empty
|
494
|
+
#
|
495
|
+
# @return [String] executable command string
|
496
|
+
def format_ruby_command(shell_script_file_path, *arguments)
|
497
|
+
return format_executable_command(sandbox_ruby, [shell_script_file_path, arguments])
|
498
|
+
end
|
499
|
+
|
500
|
+
# Appends STDOUT redirection to the given shell command.
|
501
|
+
#
|
502
|
+
# @param [String] cmd to format
|
503
|
+
# @param [String] redirection target (Default = null output)
|
504
|
+
#
|
505
|
+
# @return [String] formatted for redirection
|
506
|
+
def format_redirect_stdout(cmd, target = nil)
|
507
|
+
target ||= null_output_name
|
508
|
+
return cmd + " 1>#{target}"
|
509
|
+
end
|
510
|
+
|
511
|
+
# Appends STDERR redirection to the given shell command.
|
512
|
+
#
|
513
|
+
# @param [String] cmd to format
|
514
|
+
# @param [String] redirection target (Default = null output)
|
515
|
+
#
|
516
|
+
# @return [String] formatted for redirection
|
517
|
+
def format_redirect_stderr(cmd, target = nil)
|
518
|
+
target ||= null_output_name
|
519
|
+
return cmd + " 2>#{target}"
|
520
|
+
end
|
521
|
+
|
522
|
+
# Appends STDERR redirection to the given shell command.
|
523
|
+
#
|
524
|
+
# @param [String] cmd to format
|
525
|
+
# @param [String] redirection target (Default = null output)
|
526
|
+
#
|
527
|
+
# @return [String] formatted for redirection
|
528
|
+
def format_redirect_both(cmd, target = nil)
|
529
|
+
target ||= null_output_name
|
530
|
+
return cmd + " 1>#{target} 2>&1"
|
531
|
+
end
|
532
|
+
|
533
|
+
# @return [String] full path to the RightScale sandboxed ruby executable
|
534
|
+
def sandbox_ruby
|
535
|
+
must_be_overridden
|
536
|
+
end
|
537
|
+
|
538
|
+
# Gets the current system uptime.
|
539
|
+
#
|
540
|
+
# @return [Float] time the machine has been up, in seconds, or 0.0
|
541
|
+
def uptime
|
542
|
+
must_be_overridden
|
543
|
+
end
|
544
|
+
|
545
|
+
# Gets the time at which the system was booted.
|
546
|
+
#
|
547
|
+
# @return [Integer] the UTC timestamp at which the system was booted or nil on failure
|
548
|
+
def booted_at
|
549
|
+
must_be_overridden
|
550
|
+
end
|
551
|
+
end
|
552
|
+
|
553
|
+
# System controller APIs.
|
554
|
+
class Controller < PlatformHelperBase
|
555
|
+
|
556
|
+
# Reboot machine now.
|
557
|
+
#
|
558
|
+
# @return [TrueClass] always true
|
559
|
+
def reboot
|
560
|
+
must_be_overridden
|
561
|
+
end
|
562
|
+
|
563
|
+
# Shutdown machine now.
|
564
|
+
#
|
565
|
+
# @return [TrueClass] always true
|
566
|
+
def shutdown
|
567
|
+
must_be_overridden
|
568
|
+
end
|
569
|
+
end
|
570
|
+
|
571
|
+
# Randomizer APIs.
|
572
|
+
class Rng < PlatformHelperBase
|
573
|
+
|
574
|
+
# Generates a pseudo-random byte string.
|
575
|
+
#
|
576
|
+
# @param [Fixnum] count of bytes
|
577
|
+
#
|
578
|
+
# @return [String] bytes
|
579
|
+
def pseudorandom_bytes(count)
|
580
|
+
must_be_overridden
|
581
|
+
end
|
582
|
+
end
|
583
|
+
|
584
|
+
# Process APIs.
|
585
|
+
class Process < PlatformHelperBase
|
586
|
+
|
587
|
+
# Queries resident/working set size (total memory used by process) for
|
588
|
+
# the process given by identifier (PID).
|
589
|
+
#
|
590
|
+
# @param [Fixnum] pid for query (Default = current process)
|
591
|
+
#
|
592
|
+
# @return [Integer] current set size in KB
|
593
|
+
def resident_set_size(pid = nil)
|
594
|
+
must_be_overridden
|
595
|
+
end
|
596
|
+
end
|
597
|
+
|
598
|
+
# Package installation APIs.
|
599
|
+
class Installer < PlatformHelperBase
|
600
|
+
|
601
|
+
# exceptions
|
602
|
+
class PackageNotFound < ::RightScale::Exceptions::PlatformError; end
|
603
|
+
class PackageManagerNotFound < ::RightScale::Exceptions::PlatformError; end
|
604
|
+
|
605
|
+
# @return [String] installer output or nil
|
606
|
+
attr_accessor :output
|
607
|
+
|
608
|
+
def initialize
|
609
|
+
@output = nil
|
610
|
+
end
|
611
|
+
|
612
|
+
# Install packages based on installed package manager.
|
613
|
+
#
|
614
|
+
# For Unix compatibility; has no significance in Windows
|
615
|
+
#
|
616
|
+
# @param [Array] packages to be installed
|
617
|
+
#
|
618
|
+
# @return [TrueClass] always true
|
619
|
+
#
|
620
|
+
# @raise [RightScale::Exceptions::PlatformError] if not supported by platform
|
621
|
+
# @raise [PackageNotFound] if package is not found
|
622
|
+
# @raise [PackageManagerNotFound] if package manager is not available
|
623
|
+
# @raise [CommandError] on any other command failure
|
624
|
+
def install(packages)
|
625
|
+
must_be_overridden
|
626
|
+
end
|
627
|
+
end
|
628
|
+
|
629
|
+
private
|
630
|
+
|
631
|
+
# Load platform specific implementation
|
632
|
+
def initialize
|
633
|
+
@genus = nil
|
634
|
+
@species = nil
|
635
|
+
@filesystem = nil
|
636
|
+
@shell = nil
|
637
|
+
@ssh = nil
|
638
|
+
@controller = nil
|
639
|
+
@installer = nil
|
640
|
+
@flavor = nil
|
641
|
+
@release = nil
|
642
|
+
@codename = nil
|
643
|
+
|
644
|
+
initialize_platform_specific
|
645
|
+
end
|
646
|
+
|
647
|
+
# First-initialization tasks. Also convenient for overriding during
|
648
|
+
# testing on platforms that differ from current platform.
|
649
|
+
def initialize_platform_specific
|
322
650
|
load_platform_specific
|
323
|
-
|
324
|
-
|
325
|
-
|
326
|
-
|
327
|
-
|
328
|
-
|
651
|
+
initialize_genus
|
652
|
+
initialize_species
|
653
|
+
end
|
654
|
+
|
655
|
+
# Load platform specific implementation
|
656
|
+
#
|
657
|
+
# @return [TrueClass|FalseClass] true if loaded first time, false if already loaded
|
658
|
+
def load_platform_specific
|
659
|
+
# TEAL NOTE the unusal thing about this singleton is that it is
|
660
|
+
# redefined incrementally by first loading the base then the genus then
|
661
|
+
# the species, all of which define parts of the whole.
|
662
|
+
result = require platform_genus_path
|
663
|
+
result = (require platform_species_path) && result
|
664
|
+
result
|
665
|
+
end
|
666
|
+
|
667
|
+
# Performs any platform genus-specific initialization. This method is
|
668
|
+
# invoked only after the current platform's specific implementation has
|
669
|
+
# been loaded.
|
670
|
+
#
|
671
|
+
# @return [TrueClass] always true
|
672
|
+
def initialize_genus
|
673
|
+
raise ::NotImplementedError, 'Must be overridden'
|
674
|
+
end
|
675
|
+
|
676
|
+
# Performs any platform species-specific initialization. This method is
|
677
|
+
# invoked only after the current platform's specific implementation has
|
678
|
+
# been loaded.
|
679
|
+
#
|
680
|
+
# @return [TrueClass] always true
|
681
|
+
def initialize_species
|
682
|
+
raise ::NotImplementedError, 'Must be overridden'
|
683
|
+
end
|
684
|
+
|
685
|
+
# Determines genus/species for current platform.
|
686
|
+
def resolve_taxonomy
|
687
|
+
case ::RbConfig::CONFIG['host_os']
|
688
|
+
when /darwin/i
|
689
|
+
@genus = :unix
|
690
|
+
@species = :darwin
|
691
|
+
when /linux/i
|
692
|
+
@genus = :unix
|
693
|
+
@species = :linux
|
694
|
+
when /mingw/i
|
695
|
+
@genus = :windows
|
696
|
+
@species = :mingw
|
697
|
+
when /mswin/i
|
698
|
+
@genus = :windows
|
699
|
+
@species = :mswin
|
700
|
+
when /windows|win32|dos|cygwin/i
|
701
|
+
raise ::RightScale::Exceptions::PlatformError,
|
702
|
+
'Unsupported Ruby-on-Windows variant'
|
703
|
+
else
|
704
|
+
raise ::RightScale::Exceptions::PlatformError, 'Unknown platform'
|
329
705
|
end
|
330
|
-
|
706
|
+
true
|
707
|
+
end
|
708
|
+
|
709
|
+
# @return [String] path to platform-independent implementation.
|
710
|
+
def platform_base_path
|
711
|
+
::File.expand_path('../platform', __FILE__)
|
712
|
+
end
|
713
|
+
|
714
|
+
# @return [String] path to platform-specific genus implementation.
|
715
|
+
def platform_genus_path
|
716
|
+
::File.expand_path("#{genus}/platform", platform_base_path)
|
717
|
+
end
|
718
|
+
|
719
|
+
# @return [String] path to platform-specific species implementation.
|
720
|
+
def platform_species_path
|
721
|
+
::File.expand_path("#{genus}/#{species}/platform", platform_base_path)
|
722
|
+
end
|
723
|
+
|
724
|
+
# Retrieve platform specific service implementation
|
725
|
+
#
|
726
|
+
# @param [Symbol] name of platform service
|
727
|
+
#
|
728
|
+
# @return [PlatformHelperBase] service instance
|
729
|
+
#
|
730
|
+
# @raise [RightScale::Exceptions::PlatformError] on unknown service
|
731
|
+
def platform_service(name)
|
732
|
+
instance_var = "@#{name.to_s}".to_sym
|
733
|
+
const_name = name.to_s.camelize
|
734
|
+
|
735
|
+
unless res = self.instance_variable_get(instance_var)
|
736
|
+
load_platform_specific
|
737
|
+
if clazz = Platform.const_get(const_name)
|
738
|
+
res = clazz.new
|
739
|
+
self.instance_variable_set(instance_var, res)
|
740
|
+
else
|
741
|
+
raise ::RightScale::Exceptions::PlatformError,
|
742
|
+
"Unknown platform service: #{name}"
|
743
|
+
end
|
744
|
+
end
|
745
|
+
return res
|
746
|
+
end
|
747
|
+
|
748
|
+
# Determines which cloud we're on by the cheap but simple expedient of
|
749
|
+
# reading the RightScale cloud file.
|
750
|
+
#
|
751
|
+
# @deprecated leverage the right_link cloud libraries for any cloud-
|
752
|
+
# specific behavior because the behavior of all possible clouds is
|
753
|
+
# beyond the scope of hard-coded case statements.
|
754
|
+
#
|
755
|
+
# @return [String] cloud type or nil
|
756
|
+
def resolve_cloud_type
|
757
|
+
cloud_file_path = ::File.join(self.filesystem.right_scale_static_state_dir, 'cloud')
|
758
|
+
@cloud_type = ::File.read(cloud_file_path) rescue nil
|
759
|
+
@cloud_type
|
331
760
|
end
|
332
|
-
return res
|
333
|
-
end
|
334
761
|
|
335
|
-
|
762
|
+
end # Platform
|
336
763
|
|
337
|
-
end # RightScale
|
764
|
+
end # RightScale
|
338
765
|
|
339
|
-
# Initialize for current platform
|
340
|
-
|
766
|
+
# Initialize for current platform and/or force singleton creation on current
|
767
|
+
# thread to avoid any weird threaded initialization issues.
|
768
|
+
::RightScale::Platform.instance
|
341
769
|
|
342
|
-
end #
|
770
|
+
end # unless already defined
|