elecksee 1.0.2 → 1.0.4
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG.md +4 -0
- data/bin/lxc-awesome-ephemeral +57 -2
- data/lib/elecksee/clone.rb +15 -0
- data/lib/elecksee/ephemeral.rb +310 -0
- data/lib/elecksee/helpers.rb +70 -0
- data/lib/elecksee/lxc.rb +409 -5
- data/lib/elecksee/lxc_file_config.rb +86 -0
- data/lib/elecksee/storage/overlay_directory.rb +31 -0
- data/lib/elecksee/storage/overlay_mount.rb +60 -0
- data/lib/elecksee/storage/virtual_device.rb +81 -0
- data/lib/elecksee/version.rb +1 -1
- metadata +9 -39
- data/Gemfile.lock +0 -18
- data/lib/elecksee/awesome.rb +0 -14
- data/lib/elecksee/vendor/lxc/CHANGELOG.md +0 -37
- data/lib/elecksee/vendor/lxc/Gemfile +0 -4
- data/lib/elecksee/vendor/lxc/Gemfile.lock +0 -41
- data/lib/elecksee/vendor/lxc/README.md +0 -112
- data/lib/elecksee/vendor/lxc/attributes/default.rb +0 -28
- data/lib/elecksee/vendor/lxc/files/default/knife_lxc +0 -228
- data/lib/elecksee/vendor/lxc/files/default/lxc-awesome-ephemeral +0 -495
- data/lib/elecksee/vendor/lxc/libraries/lxc.rb +0 -366
- data/lib/elecksee/vendor/lxc/libraries/lxc_expanded_resources.rb +0 -40
- data/lib/elecksee/vendor/lxc/libraries/lxc_file_config.rb +0 -84
- data/lib/elecksee/vendor/lxc/libraries/monkey.rb +0 -51
- data/lib/elecksee/vendor/lxc/metadata.rb +0 -12
- data/lib/elecksee/vendor/lxc/providers/config.rb +0 -75
- data/lib/elecksee/vendor/lxc/providers/container.rb +0 -318
- data/lib/elecksee/vendor/lxc/providers/default.rb +0 -57
- data/lib/elecksee/vendor/lxc/providers/ephemeral.rb +0 -40
- data/lib/elecksee/vendor/lxc/providers/fstab.rb +0 -30
- data/lib/elecksee/vendor/lxc/providers/interface.rb +0 -45
- data/lib/elecksee/vendor/lxc/providers/service.rb +0 -53
- data/lib/elecksee/vendor/lxc/recipes/containers.rb +0 -13
- data/lib/elecksee/vendor/lxc/recipes/default.rb +0 -58
- data/lib/elecksee/vendor/lxc/recipes/install_dependencies.rb +0 -15
- data/lib/elecksee/vendor/lxc/recipes/knife.rb +0 -37
- data/lib/elecksee/vendor/lxc/resources/config.rb +0 -19
- data/lib/elecksee/vendor/lxc/resources/container.rb +0 -54
- data/lib/elecksee/vendor/lxc/resources/default.rb +0 -12
- data/lib/elecksee/vendor/lxc/resources/ephemeral.rb +0 -13
- data/lib/elecksee/vendor/lxc/resources/fstab.rb +0 -12
- data/lib/elecksee/vendor/lxc/resources/interface.rb +0 -13
- data/lib/elecksee/vendor/lxc/resources/service.rb +0 -5
- data/lib/elecksee/vendor/lxc/templates/default/client.rb.erb +0 -13
- data/lib/elecksee/vendor/lxc/templates/default/default-lxc.erb +0 -3
- data/lib/elecksee/vendor/lxc/templates/default/file_content.erb +0 -2
- data/lib/elecksee/vendor/lxc/templates/default/fstab.erb +0 -5
- data/lib/elecksee/vendor/lxc/templates/default/interface.erb +0 -27
data/CHANGELOG.md
CHANGED
data/bin/lxc-awesome-ephemeral
CHANGED
@@ -1,5 +1,60 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
2
|
|
3
|
-
require 'elecksee/
|
3
|
+
require 'elecksee/ephemeral'
|
4
|
+
require 'getoptlong'
|
4
5
|
|
5
|
-
|
6
|
+
require 'pp'
|
7
|
+
|
8
|
+
opts = Lxc::Ephemeral.options.map do |k,v|
|
9
|
+
res = [
|
10
|
+
["--#{k}", v[:short], v[:type] == :boolean ? GetoptLong::NO_ARGUMENT : GetoptLong::REQUIRED_ARGUMENT]
|
11
|
+
]
|
12
|
+
if(v[:aliases])
|
13
|
+
Array(v[:aliases]).each do |al|
|
14
|
+
res << ["--#{al}", v[:type] == :boolean ? GetoptLong::NO_ARGUMENT : GetoptLong::REQUIRED_ARGUMENT]
|
15
|
+
end
|
16
|
+
end
|
17
|
+
res
|
18
|
+
end.flatten(1) << ['--help', '-h', GetoptLong::NO_ARGUMENT]
|
19
|
+
|
20
|
+
opts = GetoptLong.new(*opts)
|
21
|
+
|
22
|
+
config = {}
|
23
|
+
opts.each do |opt,arg|
|
24
|
+
case opt
|
25
|
+
when '--help'
|
26
|
+
puts 'Usage: lxc-awesome-ephemeral [OPTS] -o NAME'
|
27
|
+
output = []
|
28
|
+
Lxc::Ephemeral.options.map do |k,v|
|
29
|
+
option = "--#{k} #{v[:short]}"
|
30
|
+
unless(v[:type] == :boolean)
|
31
|
+
option << ' VAL'
|
32
|
+
end
|
33
|
+
output << {:opt => option, :desc => v[:desc], :alias => v[:aliases]}
|
34
|
+
end
|
35
|
+
opt_len = output.map{|o| o[:opt].length}.max + 2
|
36
|
+
output.each do |option|
|
37
|
+
puts " #{option[:opt]}:#{' ' * (opt_len - option[:opt].length)}#{option[:desc]}"
|
38
|
+
Array(option[:alias]).each do |a|
|
39
|
+
puts " --#{a}"
|
40
|
+
end
|
41
|
+
end
|
42
|
+
exit -1
|
43
|
+
when '--version'
|
44
|
+
else
|
45
|
+
key = opt.sub('--', '').to_sym
|
46
|
+
opt_conf = Lxc::Ephemeral.options[key]
|
47
|
+
case opt_conf[:type]
|
48
|
+
when :boolean
|
49
|
+
val = true
|
50
|
+
when :integer
|
51
|
+
val = arg.to_i
|
52
|
+
else
|
53
|
+
val = arg
|
54
|
+
end
|
55
|
+
end
|
56
|
+
config[key] = val
|
57
|
+
end
|
58
|
+
|
59
|
+
ephemeral = Lxc::Ephemeral.new(config.merge(:cli => true))
|
60
|
+
ephemeral.start!
|
@@ -0,0 +1,15 @@
|
|
1
|
+
require 'elecksee/helpers'
|
2
|
+
require 'elecksee/lxc'
|
3
|
+
|
4
|
+
class Lxc
|
5
|
+
class Clone
|
6
|
+
|
7
|
+
|
8
|
+
def initialize(args={})
|
9
|
+
args = Mash.new(args)
|
10
|
+
%w(original new_name).each do |key|
|
11
|
+
raise ArgumentError.new "Missing required parameter: #{key}" unless args[key]
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,310 @@
|
|
1
|
+
require 'securerandom'
|
2
|
+
require 'fileutils'
|
3
|
+
require 'tmpdir'
|
4
|
+
require 'etc'
|
5
|
+
|
6
|
+
%w(
|
7
|
+
helpers lxc storage/overlay_directory
|
8
|
+
storage/overlay_mount storage/virtual_device
|
9
|
+
).each do |path|
|
10
|
+
require "elecksee/#{path}"
|
11
|
+
end
|
12
|
+
|
13
|
+
class Lxc
|
14
|
+
|
15
|
+
class Ephemeral
|
16
|
+
|
17
|
+
include Helpers
|
18
|
+
|
19
|
+
NAME_FILES = %w(fstab config)
|
20
|
+
HOSTNAME_FILES = %w(
|
21
|
+
rootfs/etc/hostname
|
22
|
+
rootfs/etc/hosts
|
23
|
+
rootfs/etc/sysconfig/network
|
24
|
+
rootfs/etc/sysconfig/network-scripts/ifcfg-eth0
|
25
|
+
)
|
26
|
+
|
27
|
+
class << self
|
28
|
+
attr_reader :options
|
29
|
+
|
30
|
+
def option(name, short, type, args={})
|
31
|
+
@options ||= {}
|
32
|
+
@options[name] = args.merge(:short => short, :type => type)
|
33
|
+
instance_eval do
|
34
|
+
attr_accessor name.to_sym
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
option :original, '-o', :string, :required => true, :desc => 'Original container name'
|
40
|
+
option :ipaddress, '-I', :string, :desc => 'Custom IP address'
|
41
|
+
option :gateway, '-G', :string, :desc => 'Custom gateway'
|
42
|
+
option :netmask, '-N', :string, :default => '255.255.255.0', :desc => 'Custom netmask'
|
43
|
+
option :device, '-D', :integer, :desc => 'Create VBD for overlay of size {SIZE}M'
|
44
|
+
option :directory, '-z', :boolean, :desc => 'Use host based directory for overlay'
|
45
|
+
option :union, '-U', :string, :desc => 'Overlay FS to use (overlayfs or aufs)'
|
46
|
+
option :daemon, '-d', :boolean, :desc => 'Run as a daemon'
|
47
|
+
option :bind, '-b', :string, :desc => 'Bind provided directory (non-ephemeral)'
|
48
|
+
option :user, '-u', :string, :desc => 'Deprecated: Provided for compatibility'
|
49
|
+
option :ssh_key, '-S', :string, :default => '/opt/hw-lxc-config/id_rsa', :aliases => 'ssh-key', :desc => 'Deprecated: Provided for compatibility'
|
50
|
+
option :lxc_dir, '-L', :string, :default => '/var/lib/lxc', :aliases => 'lxc-dir', :desc => 'Directory of LXC store'
|
51
|
+
option :tmp_dir, '-T', :string, :default => '/tmp/lxc/ephemerals', :aliases => 'tmp-dir', :desc => 'Directory of ephemeral temp files'
|
52
|
+
|
53
|
+
attr_reader :name
|
54
|
+
attr_reader :cli
|
55
|
+
attr_reader :hostname
|
56
|
+
attr_reader :path
|
57
|
+
attr_reader :lxc
|
58
|
+
attr_reader :ephemeral_device
|
59
|
+
attr_reader :ephemeral_overlay
|
60
|
+
attr_reader :ephemeral_binds
|
61
|
+
|
62
|
+
def initialize(args={})
|
63
|
+
configure!(args)
|
64
|
+
@cli = args[:cli]
|
65
|
+
@path = Dir.mktmpdir(File.join(lxc_dir, original))
|
66
|
+
@name = File.basename(@path)
|
67
|
+
@hostname = @name.gsub(%r{[^A-Za-z0-9\-]}, '')
|
68
|
+
@ephemeral_binds = []
|
69
|
+
@lxc = nil
|
70
|
+
end
|
71
|
+
|
72
|
+
def register_traps
|
73
|
+
%w(TERM INT QUIT).each do |sig|
|
74
|
+
Signal.trap(sig){ cleanup }
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
def cli_output
|
79
|
+
if(cli)
|
80
|
+
puts "New ephemeral container started. (#{name})"
|
81
|
+
puts " - Connect using: sudo ssh -i #{ssh_key} root@#{lxc.container_ip(10)}"
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
def start!(*args)
|
86
|
+
register_traps
|
87
|
+
setup
|
88
|
+
if(daemon)
|
89
|
+
if(args.include?(:fork))
|
90
|
+
fork do
|
91
|
+
lxc.start
|
92
|
+
cli_output
|
93
|
+
lxc.wait_for_state(:stopped)
|
94
|
+
cleanup
|
95
|
+
end
|
96
|
+
else
|
97
|
+
Process.daemon
|
98
|
+
lxc.start
|
99
|
+
cli_output
|
100
|
+
lxc.wait_for_state(:stopped)
|
101
|
+
cleanup
|
102
|
+
end
|
103
|
+
else
|
104
|
+
lxc.start
|
105
|
+
cli_output
|
106
|
+
lxc.wait_for_state(:stopped)
|
107
|
+
cleanup
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
def cleanup
|
112
|
+
lxc.stop
|
113
|
+
@ephemeral_overlay.unmount
|
114
|
+
@ephemeral_binds.map(&:destroy)
|
115
|
+
@ephemeral_device.destroy
|
116
|
+
if(lxc.path.to_path.split('/').size > 1)
|
117
|
+
command("rm -rf #{lxc.path.to_path}", :sudo => true)
|
118
|
+
true
|
119
|
+
else
|
120
|
+
$stderr.puts "This path seems bad and I won't remove it: #{lxc.path.to_path}"
|
121
|
+
false
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
private
|
126
|
+
|
127
|
+
def configure!(args)
|
128
|
+
self.class.options.each do |name, opts|
|
129
|
+
argv = args.detect{|k,v| (Array(opts[:aliases]) + Array(opts[:short]) + [name]).include?(k.to_sym)}
|
130
|
+
argv = argv.last if argv
|
131
|
+
argv ||= opts[:default]
|
132
|
+
if(argv)
|
133
|
+
check_type!(name, argv, opts[:type])
|
134
|
+
self.send("#{name}=", argv)
|
135
|
+
else
|
136
|
+
if(opts[:required])
|
137
|
+
raise ArgumentError.new "Missing required argument: #{name}"
|
138
|
+
end
|
139
|
+
end
|
140
|
+
end
|
141
|
+
if(ipaddress && gateway.nil?)
|
142
|
+
self.gateway = ipaddress.sub(%r{\d+$}, '1')
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
def check_type!(arg_name, val, type)
|
147
|
+
valid = false
|
148
|
+
case type
|
149
|
+
when :string
|
150
|
+
valid = val.is_a?(String)
|
151
|
+
when :boolean
|
152
|
+
valid = val.is_a?(TrueClass) || val.is_a?(FalseClass)
|
153
|
+
when :integer
|
154
|
+
valid = val.is_a?(Numeric)
|
155
|
+
end
|
156
|
+
raise ArgumentError.new "Invalid type provided for #{arg_name}. Expecting value type of: #{type.inspect} Got: #{val.class} - #{val}" unless valid
|
157
|
+
end
|
158
|
+
|
159
|
+
def setup
|
160
|
+
create
|
161
|
+
build_overlay
|
162
|
+
update_naming
|
163
|
+
discover_binds
|
164
|
+
apply_custom_networking if ipaddress
|
165
|
+
end
|
166
|
+
|
167
|
+
def build_overlay
|
168
|
+
if(directory)
|
169
|
+
@ephemeral_device = OverlayDirectory.new(name, :tmp_dir => directory.is_a?(String) ? directory : tmp_dir)
|
170
|
+
else
|
171
|
+
@ephemeral_device = VirtualDevice.new(name, :size => device, :tmp_fs => !device, :tmp_dir => tmp_dir)
|
172
|
+
@ephemeral_device.mount
|
173
|
+
end
|
174
|
+
@ephemeral_overlay = OverlayMount.new(
|
175
|
+
:base => Lxc.new(original).rootfs.to_path,
|
176
|
+
:overlay => ephemeral_device.target_path,
|
177
|
+
:target => lxc.rootfs.to_path,
|
178
|
+
:overlay_type => union
|
179
|
+
)
|
180
|
+
@ephemeral_overlay.mount
|
181
|
+
end
|
182
|
+
|
183
|
+
def writable_path!(path)
|
184
|
+
unless(File.directory?(File.dirname(path)))
|
185
|
+
command("mkdir -p #{File.dirname(path)}", :sudo => true)
|
186
|
+
end
|
187
|
+
unless(File.exists?(path))
|
188
|
+
command("touch #{path}", :sudo => true)
|
189
|
+
end
|
190
|
+
command("chown #{Etc.getlogin} #{path}", :sudo => true)
|
191
|
+
end
|
192
|
+
|
193
|
+
def create
|
194
|
+
Dir.glob(File.join(lxc_dir, original, '*')).each do |o_path|
|
195
|
+
next unless File.file?(o_path)
|
196
|
+
command("cp #{o_path} #{File.join(path, File.basename(o_path))}", :sudo => true)
|
197
|
+
end
|
198
|
+
command("chown -R #{Etc.getlogin} #{path}", :sudo => true)
|
199
|
+
@lxc = Lxc.new(name)
|
200
|
+
Dir.mkdir(lxc.rootfs.to_path)
|
201
|
+
contents = File.readlines(lxc.config)
|
202
|
+
File.open(lxc.config, 'w') do |file|
|
203
|
+
contents.each do |line|
|
204
|
+
if(line.strip.start_with?('lxc.network.hwaddr'))
|
205
|
+
file.write "00:16:3e#{SecureRandom.hex(3).gsub(/(..)/, ':\1')}"
|
206
|
+
else
|
207
|
+
file.write line
|
208
|
+
end
|
209
|
+
end
|
210
|
+
end
|
211
|
+
end
|
212
|
+
|
213
|
+
# TODO: Discovered binds for ephemeral are all tmpfs for now.
|
214
|
+
def discover_binds
|
215
|
+
contents = File.readlines(lxc.path.join('fstab'))
|
216
|
+
File.open(lxc.path.join('fstab'), 'w') do |file|
|
217
|
+
contents.each do |line|
|
218
|
+
parts = line.split(' ')
|
219
|
+
if(parts[3] == 'bind')
|
220
|
+
source = parts.first
|
221
|
+
target = parts[1].sub(%r{^.+rootfs/}, '')
|
222
|
+
container_target = lxc.rootfs.join(target).to_path
|
223
|
+
device = VirtualDevice.new(target.gsub('/', '_'), :tmp_fs => true)
|
224
|
+
device.mount
|
225
|
+
FileUtils.mkdir_p(container_target)
|
226
|
+
ephemeral_binds << device
|
227
|
+
if(union == 'overlayfs')
|
228
|
+
file.write "none #{container_target} overlayfs upperdir=#{device.mount_path},lowerdir=#{source} 0 0"
|
229
|
+
else
|
230
|
+
file.write "none #{container_target} aufs br=#{device.mount_path}=rw:#{source}=ro,noplink 0 0"
|
231
|
+
end
|
232
|
+
else
|
233
|
+
file.write line
|
234
|
+
end
|
235
|
+
end
|
236
|
+
# If bind option used, bind in for rw
|
237
|
+
if(bind)
|
238
|
+
command("mkdir -p #{lxc.rootfs.join(bind).to_path}", :sudo => true)
|
239
|
+
file.puts "#{bind} #{lxc.rootfs.join(bind)} none bind 0 0"
|
240
|
+
end
|
241
|
+
end
|
242
|
+
end
|
243
|
+
|
244
|
+
def update_naming
|
245
|
+
NAME_FILES.each do |file|
|
246
|
+
next unless File.exists?(lxc.path.join(file))
|
247
|
+
writable_path!(lxc.path.join(file).to_path)
|
248
|
+
contents = File.read(lxc.path.join(file))
|
249
|
+
File.open(lxc.path.join(file), 'w') do |new_file|
|
250
|
+
new_file.write contents.gsub(original, name)
|
251
|
+
end
|
252
|
+
end
|
253
|
+
HOSTNAME_FILES.each do |file|
|
254
|
+
next unless File.exists?(lxc.path.join(file))
|
255
|
+
writable_path!(lxc.path.join(file).to_path)
|
256
|
+
contents = File.read(lxc.path.join(file))
|
257
|
+
File.open(lxc.path.join(file), 'w') do |new_file|
|
258
|
+
new_file.write contents.gsub(original, hostname)
|
259
|
+
end
|
260
|
+
end
|
261
|
+
end
|
262
|
+
|
263
|
+
def el_platform?
|
264
|
+
lxc.rootfs.join('etc/redhat-release').exist?
|
265
|
+
end
|
266
|
+
|
267
|
+
def apply_custom_networking
|
268
|
+
if(el_platform?)
|
269
|
+
writable_path!(path = lxc.rootfs.join('etc/sysconfig/network-scripts/ifcfg-eth0'))
|
270
|
+
File.open(path, 'w') do |file|
|
271
|
+
file.write <<-EOF
|
272
|
+
DEVICE=eth0
|
273
|
+
BOOTPROTO=static
|
274
|
+
NETMASK=#{netmask}
|
275
|
+
IPADDR=#{ipaddress}
|
276
|
+
ONBOOT=yes
|
277
|
+
TYPE=Ethernet
|
278
|
+
USERCTL=yes
|
279
|
+
PEERDNS=yes
|
280
|
+
IPV6INIT=no
|
281
|
+
GATEWAY=#{gateway}
|
282
|
+
EOF
|
283
|
+
end
|
284
|
+
writable_path!(path = lxc.rootfs.join('etc/sysconfig/network'))
|
285
|
+
File.open(path, 'w') do |file|
|
286
|
+
file.write <<-EOF
|
287
|
+
NETWORKING=yes
|
288
|
+
HOSTNAME=#{hostname}
|
289
|
+
EOF
|
290
|
+
end
|
291
|
+
File.open(@lxc.rootfs.join('etc/rc.local'), 'w') do |file|
|
292
|
+
file.puts "hostname #{hostname}"
|
293
|
+
end
|
294
|
+
else
|
295
|
+
writable_path!(path = lxc.rootfs.join('etc/network/interfaces'))
|
296
|
+
File.open(path, 'w') do |file|
|
297
|
+
file.write <<-EOF
|
298
|
+
auto lo
|
299
|
+
iface lo inet loopback
|
300
|
+
auto eth0
|
301
|
+
iface eth0 inet static
|
302
|
+
address #{ipaddress}
|
303
|
+
netmask #{netmask}
|
304
|
+
gateway #{gateway}
|
305
|
+
EOF
|
306
|
+
end
|
307
|
+
end
|
308
|
+
end
|
309
|
+
end
|
310
|
+
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
class Lxc
|
2
|
+
class CommandFailed < StandardError
|
3
|
+
end
|
4
|
+
|
5
|
+
module Helpers
|
6
|
+
|
7
|
+
def sudo
|
8
|
+
Lxc.sudo
|
9
|
+
end
|
10
|
+
|
11
|
+
# Simple helper to shell out
|
12
|
+
def run_command(cmd, args={})
|
13
|
+
retries = args[:allow_failure_retry].to_i
|
14
|
+
cmd = [sudo, cmd].join(' ') if args[:sudo]
|
15
|
+
begin
|
16
|
+
shlout = Mixlib::ShellOut.new(cmd,
|
17
|
+
:logger => defined?(Chef) ? Chef::Log.logger : log,
|
18
|
+
:live_stream => args[:livestream] ? STDOUT : nil,
|
19
|
+
:timeout => args[:timeout] || 1200,
|
20
|
+
:environment => {'HOME' => detect_home}
|
21
|
+
)
|
22
|
+
shlout.run_command
|
23
|
+
shlout.error!
|
24
|
+
shlout
|
25
|
+
rescue Mixlib::ShellOut::ShellCommandFailed, CommandFailed, Mixlib::ShellOut::CommandTimeout
|
26
|
+
if(retries > 0)
|
27
|
+
log.warn "LXC run command failed: #{cmd}"
|
28
|
+
log.warn "Retrying command. #{args[:allow_failure_retry].to_i - retries} of #{args[:allow_failure_retry].to_i} retries remain"
|
29
|
+
sleep(0.3)
|
30
|
+
retries -= 1
|
31
|
+
retry
|
32
|
+
elsif(args[:allow_failure])
|
33
|
+
true
|
34
|
+
else
|
35
|
+
raise
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def command(*args)
|
41
|
+
run_command(*args)
|
42
|
+
end
|
43
|
+
|
44
|
+
def log
|
45
|
+
if(defined?(Chef))
|
46
|
+
Chef::Log
|
47
|
+
else
|
48
|
+
unless(@logger)
|
49
|
+
require 'logger'
|
50
|
+
@logger = Logger.new('/dev/null')
|
51
|
+
end
|
52
|
+
@logger
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
# Detect HOME environment variable. If not an acceptable
|
57
|
+
# value, set to /root or /tmp
|
58
|
+
def detect_home(set_if_missing=false)
|
59
|
+
if(ENV['HOME'] && Pathname.new(ENV['HOME']).absolute?)
|
60
|
+
ENV['HOME']
|
61
|
+
else
|
62
|
+
home = File.directory?('/root') && File.writable?('/root') ? '/root' : '/tmp'
|
63
|
+
if(set_if_missing)
|
64
|
+
ENV['HOME'] = home
|
65
|
+
end
|
66
|
+
home
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|