elecksee 1.0.10 → 1.0.12

Sign up to get free protection for your applications and to get access to all the features.
data/CHANGELOG.md CHANGED
@@ -1,3 +1,7 @@
1
+ ## v1.0.12
2
+ * Allow access to ephemeral setup without creation
3
+ * Add `#destroy` method to `Lxc` instances
4
+
1
5
  ## v1.0.10
2
6
  * Add clone support
3
7
  * Allow command passage to ephemeral nodes
data/Gemfile CHANGED
@@ -1,3 +1,5 @@
1
1
  source 'https://rubygems.org'
2
2
 
3
+ gem 'childprocess'
4
+
3
5
  gemspec
data/Gemfile.lock CHANGED
@@ -1,18 +1,22 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- elecksee (1.0.9)
4
+ elecksee (1.0.11)
5
5
  mixlib-shellout
6
6
  net-ssh
7
7
 
8
8
  GEM
9
9
  remote: https://rubygems.org/
10
10
  specs:
11
- mixlib-shellout (1.1.0)
12
- net-ssh (2.6.7)
11
+ childprocess (0.3.9)
12
+ ffi (~> 1.0, >= 1.0.11)
13
+ ffi (1.9.3)
14
+ mixlib-shellout (1.2.0)
15
+ net-ssh (2.7.0)
13
16
 
14
17
  PLATFORMS
15
18
  ruby
16
19
 
17
20
  DEPENDENCIES
21
+ childprocess
18
22
  elecksee!
Binary file
@@ -53,10 +53,10 @@ class Lxc
53
53
  @ephemeral_binds = []
54
54
  @lxc = nil
55
55
  end
56
-
56
+
57
57
  def register_traps
58
58
  %w(TERM INT QUIT).each do |sig|
59
- Signal.trap(sig){ cleanup }
59
+ Signal.trap(sig){ cleanup && raise }
60
60
  end
61
61
  end
62
62
 
@@ -71,6 +71,7 @@ class Lxc
71
71
  begin
72
72
  lxc.start
73
73
  if(ephemeral_command)
74
+ lxc.wait_for_state(:running)
74
75
  lxc.container_command(ephemeral_command)
75
76
  else
76
77
  cli_output
@@ -81,7 +82,11 @@ class Lxc
81
82
  end
82
83
  true
83
84
  end
84
-
85
+
86
+ def create!
87
+ setup
88
+ end
89
+
85
90
  def start!(*args)
86
91
  register_traps
87
92
  setup
@@ -114,7 +119,7 @@ class Lxc
114
119
  end
115
120
 
116
121
  private
117
-
122
+
118
123
  def setup
119
124
  create
120
125
  build_overlay
@@ -138,14 +143,14 @@ class Lxc
138
143
  )
139
144
  @ephemeral_overlay.mount
140
145
  end
141
-
146
+
142
147
  def create
143
148
  Dir.glob(File.join(lxc_dir, original, '*')).each do |o_path|
144
149
  next unless File.file?(o_path)
145
150
  command("cp #{o_path} #{File.join(path, File.basename(o_path))}", :sudo => true)
146
151
  end
147
152
  @lxc = Lxc.new(name)
148
- command("mkdir -p '#{lxc.path.join('rootfs')}'", :sudo => true)
153
+ command("mkdir -p #{lxc.path.join('rootfs')}", :sudo => true)
149
154
  update_net_hwaddr
150
155
  end
151
156
 
@@ -1,19 +1,117 @@
1
1
  class Lxc
2
2
  class CommandFailed < StandardError
3
+ attr_accessor :original, :result
4
+ def initialize(orig, result=nil)
5
+ @original = orig
6
+ @result = result
7
+ super(orig.to_s)
8
+ end
9
+ end
10
+
11
+ class Timeout < CommandFailed
12
+ end
13
+
14
+ class CommandResult
15
+ attr_reader :original, :stdout, :stderr
16
+ def initialize(result)
17
+ @original = result
18
+ if(result.class.ancestors.map(&:to_s).include?('ChildProcess::AbstractProcess'))
19
+ extract_childprocess
20
+ elsif(result.class.to_s == 'Mixlib::ShellOut')
21
+ extract_shellout
22
+ else
23
+ raise TypeError.new("Unknown process result type received: #{result.class}")
24
+ end
25
+ end
26
+
27
+ def extract_childprocess
28
+ original.io.stdout.rewind
29
+ original.io.stderr.rewind
30
+ @stdout = original.io.stdout.read
31
+ @stderr = original.io.stderr.read
32
+ original.io.stdout.delete
33
+ original.io.stderr.delete
34
+ end
35
+
36
+ def extract_shellout
37
+ @stdout = original.stdout
38
+ @stderr = original.stderr
39
+ end
3
40
  end
4
41
 
5
42
  module Helpers
6
-
43
+
7
44
  def sudo
8
45
  Lxc.sudo
9
46
  end
10
-
47
+
11
48
  # Simple helper to shell out
12
49
  def run_command(cmd, args={})
50
+ cmd_type = Lxc.shellout_helper
51
+ unless(cmd_type)
52
+ if(defined?(ChildProcess))
53
+ cmd_type = :childprocess
54
+ else
55
+ cmd_type = :mixlib_shellout
56
+ end
57
+ end
58
+ case cmd_type
59
+ when :childprocess
60
+ require 'tempfile'
61
+ result = child_process_command(cmd, args)
62
+ when :mixlib_shellout
63
+ require 'mixlib/shellout'
64
+ result = mixlib_shellout_command(cmd, args)
65
+ else
66
+ raise ArgumentError.new("Unknown shellout helper provided: #{cmd_type}")
67
+ end
68
+ CommandResult.new(result)
69
+ end
70
+
71
+ def child_process_command(cmd, args)
13
72
  retries = args[:allow_failure_retry].to_i
14
73
  cmd = [sudo, cmd].join(' ') if args[:sudo]
15
74
  begin
16
- shlout = Mixlib::ShellOut.new(cmd,
75
+ s_out = Tempfile.new('stdout')
76
+ s_err = Tempfile.new('stderr')
77
+ s_out.sync
78
+ s_err.sync
79
+ cmd_parts = cmd.split(' ')
80
+ cmd_parts.delete_if(&:empty?)
81
+ c_proc = ChildProcess.build(*cmd_parts)
82
+ c_proc.environment.merge('HOME' => detect_home)
83
+ c_proc.io.stdout = s_out
84
+ c_proc.io.stderr = s_err
85
+ c_proc.start
86
+ begin
87
+ c_proc.poll_for_exit(args[:timeout] || 1200)
88
+ rescue ChildProcess::TimeoutError
89
+ c_proc.stop
90
+ ensure
91
+ raise CommandFailed.new("Command failed: #{cmd}", CommandResult.new(c_proc)) if c_proc.crashed?
92
+ end
93
+ c_proc
94
+ rescue CommandFailed
95
+ if(retries > 0)
96
+ log.warn "LXC run command failed: #{cmd}"
97
+ log.warn "Retrying command. #{args[:allow_failure_retry].to_i - retries} of #{args[:allow_failure_retry].to_i} retries remain"
98
+ sleep(0.3)
99
+ retries -= 1
100
+ retry
101
+ elsif(args[:allow_failure])
102
+ false
103
+ else
104
+ raise
105
+ end
106
+ end
107
+ end
108
+
109
+ def mixlib_shellout_command(cmd, args)
110
+ retries = args[:allow_failure_retry].to_i
111
+ cmd = [sudo, cmd].join(' ') if args[:sudo]
112
+ shlout = nil
113
+ begin
114
+ shlout = Mixlib::ShellOut.new(cmd,
17
115
  :logger => defined?(Chef) && defined?(Chef::Log) ? Chef::Log.logger : log,
18
116
  :live_stream => args[:livestream] ? STDOUT : nil,
19
117
  :timeout => args[:timeout] || 1200,
@@ -22,7 +120,7 @@ class Lxc
22
120
  shlout.run_command
23
121
  shlout.error!
24
122
  shlout
25
- rescue Mixlib::ShellOut::ShellCommandFailed, CommandFailed, Mixlib::ShellOut::CommandTimeout
123
+ rescue Mixlib::ShellOut::ShellCommandFailed, CommandFailed, Mixlib::ShellOut::CommandTimeout => e
26
124
  if(retries > 0)
27
125
  log.warn "LXC run command failed: #{cmd}"
28
126
  log.warn "Retrying command. #{args[:allow_failure_retry].to_i - retries} of #{args[:allow_failure_retry].to_i} retries remain"
@@ -32,7 +130,7 @@ class Lxc
32
130
  elsif(args[:allow_failure])
33
131
  false
34
132
  else
35
- raise
133
+ raise CommandFailed.new(e, CommandResult.new(shlout))
36
134
  end
37
135
  end
38
136
  end
@@ -40,7 +138,7 @@ class Lxc
40
138
  def command(*args)
41
139
  run_command(*args)
42
140
  end
43
-
141
+
44
142
  def log
45
143
  if(defined?(Chef))
46
144
  Chef::Log
@@ -52,7 +150,7 @@ class Lxc
52
150
  @logger
53
151
  end
54
152
  end
55
-
153
+
56
154
  # Detect HOME environment variable. If not an acceptable
57
155
  # value, set to /root or /tmp
58
156
  def detect_home(set_if_missing=false)
@@ -0,0 +1,37 @@
1
+ require 'chef/mash'
2
+ require 'chef/json_compat'
3
+
4
+ class Lxc
5
+ class Knife
6
+ class Config
7
+
8
+ attr_reader :base_path
9
+ attr_reader :store
10
+
11
+ def initialize(args={})
12
+
13
+
14
+ def initialize(path='/etc/knife-lxc/config.json')
15
+ if(File.exists?(path))
16
+ @base_path = path
17
+ @store = Chef::JSONCompat.from_json(File.read(path))
18
+ else
19
+ raise ArgumentError.new("Provided path is not valid: #{path}")
20
+ end
21
+ end
22
+
23
+ def [](k)
24
+ @store[k]
25
+ end
26
+
27
+ def
28
+
29
+ def used_addresses
30
+ end
31
+
32
+ def available_addresses
33
+ end
34
+
35
+ end
36
+ end
37
+ end
data/lib/elecksee/lxc.rb CHANGED
@@ -1,10 +1,9 @@
1
1
  require 'elecksee/helpers/base'
2
- require 'mixlib/shellout'
3
2
  require 'pathname'
4
3
  require 'tmpdir'
5
4
 
6
5
  class Lxc
7
-
6
+
8
7
  # Pathname#join does not act like File#join when joining paths that
9
8
  # begin with '/', and that's dumb. So we'll make our own Pathname,
10
9
  # with a #join that uses File
@@ -13,7 +12,7 @@ class Lxc
13
12
  self.class.new(::File.join(self.to_path, *args))
14
13
  end
15
14
  end
16
-
15
+
17
16
  include Helpers
18
17
 
19
18
  attr_reader :name, :base_path, :lease_file, :preferred_device
@@ -21,9 +20,10 @@ class Lxc
21
20
  class << self
22
21
 
23
22
  include Helpers
24
-
23
+
25
24
  attr_accessor :use_sudo
26
25
  attr_accessor :base_path
26
+ attr_accessor :shellout_helper
27
27
 
28
28
  def sudo
29
29
  case use_sudo
@@ -33,11 +33,11 @@ class Lxc
33
33
  "#{use_sudo} "
34
34
  end
35
35
  end
36
-
36
+
37
37
  def base_path
38
38
  @base_path || '/var/lib/lxc'
39
39
  end
40
-
40
+
41
41
  # List running containers
42
42
  def running
43
43
  full_list[:running]
@@ -67,7 +67,7 @@ class Lxc
67
67
  end
68
68
  end.compact
69
69
  end
70
-
70
+
71
71
  # name:: Name of container
72
72
  # Returns information about given container
73
73
  def info(name)
@@ -82,6 +82,7 @@ class Lxc
82
82
  else
83
83
  res[:state] = :unknown
84
84
  res[:pid] = -1
85
+ res
85
86
  end
86
87
  end
87
88
 
@@ -130,7 +131,7 @@ class Lxc
130
131
  def stopped?
131
132
  self.class.info(name)[:state] == :stopped
132
133
  end
133
-
134
+
134
135
  # Returns if container is frozen
135
136
  def frozen?
136
137
  self.class.info(name)[:state] == :frozen
@@ -281,7 +282,7 @@ class Lxc
281
282
  run_command("#{sudo}lxc-stop -n #{name}", :allow_failure_retry => 3)
282
283
  wait_for_state(:stopped)
283
284
  end
284
-
285
+
285
286
  # Freeze the container
286
287
  def freeze
287
288
  run_command("#{sudo}lxc-freeze -n #{name}")
@@ -309,15 +310,22 @@ class Lxc
309
310
  end
310
311
  end
311
312
 
313
+ # Destroy the container
314
+ def destroy
315
+ unless stopped?
316
+ stop
317
+ end
318
+ run_command("#{sudo}lxc-destroy -n #{name}")
319
+ end
320
+
312
321
  def direct_container_command(command, args={})
313
- com = "#{sudo}ssh root@#{args[:ip] || container_ip} -i /opt/hw-lxc-config/id_rsa -oStrictHostKeyChecking=no '#{command}'"
314
322
  begin
315
- cmd = Mixlib::ShellOut.new(com,
316
- :live_stream => args[:live_stream],
317
- :timeout => args[:timeout] || 1200
323
+ run_command(
324
+ "ssh root@#{args[:ip] || container_ip} -i /opt/hw-lxc-config/id_rsa -oStrictHostKeyChecking=no '#{command}'",
325
+ :sudo => true,
326
+ :timeout => args[:timeout],
327
+ :live_stream => args[:live_stream]
318
328
  )
319
- cmd.run_command
320
- cmd.error!
321
329
  true
322
330
  rescue
323
331
  raise if args[:raise_on_failure]
@@ -330,7 +338,7 @@ class Lxc
330
338
  def run_command(cmd, args={})
331
339
  retries = args[:allow_failure_retry].to_i
332
340
  begin
333
- shlout = Mixlib::ShellOut.new(cmd,
341
+ shlout = Mixlib::ShellOut.new(cmd,
334
342
  :logger => defined?(Chef) ? Chef::Log.logger : log,
335
343
  :live_stream => args[:livestream] ? nil : STDOUT,
336
344
  :timeout => args[:timeout] || 1200,
@@ -354,6 +362,7 @@ class Lxc
354
362
  end
355
363
  end
356
364
 
365
+ >>>>>>> [ephemeral] lxc.destroy()
357
366
  def wait_for_state(desired_state, args={})
358
367
  args[:sleep_interval] ||= 1.0
359
368
  wait_total = 0.0
@@ -376,7 +385,7 @@ class Lxc
376
385
  home
377
386
  end
378
387
  end
379
-
388
+
380
389
  # cmd:: Shell command string
381
390
  # retries:: Number of retry attempts (1 second sleep interval)
382
391
  # Runs command in container via ssh
@@ -414,3 +423,5 @@ class Lxc
414
423
  end
415
424
 
416
425
  end
426
+
427
+ Lxc.shellout_helper = :mixlib_shellout
@@ -5,13 +5,37 @@ class Lxc
5
5
  attr_reader :base
6
6
 
7
7
  class << self
8
+
9
+ def convert_to_hash(thing)
10
+ unless(thing.is_a?(Hash))
11
+ result = defined?(Mash) ? Mash.new : {}
12
+ thing.each do |k,v|
13
+ result[k] = v.respond_to?(:keys) && v.respond_to?(:values) ? convert_to_hash(v) : v
14
+ end
15
+ end
16
+ result || thing
17
+ end
18
+
19
+ def symbolize_hash(thing)
20
+ if(defined?(Mash))
21
+ Mash.new(thing)
22
+ else
23
+ result = {}
24
+ thing.each do |k,v|
25
+ result[k.to_sym] = v.is_a?(Hash) ? symbolize_hash(v) : v
26
+ end
27
+ result
28
+ end
29
+ end
30
+
8
31
  def generate_config(resource)
32
+ resource = symbolize_hash(convert_to_hash(resource))
9
33
  config = []
10
- config << "lxc.utsname = #{resource.utsname}"
11
- if(resource.aa_profile)
12
- config << "lxc.aa_profile = #{resource.aa_profile}"
34
+ config << "lxc.utsname = #{resource[:utsname]}"
35
+ if(resource[:aa_profile])
36
+ config << "lxc.aa_profile = #{resource[:aa_profile]}"
13
37
  end
14
- [resource.network].flatten.each do |net_hash|
38
+ [resource.[:network]].flatten.each do |net_hash|
15
39
  nhsh = Mash.new(net_hash)
16
40
  flags = nhsh.delete(:flags)
17
41
  %w(type link).each do |k|
@@ -24,14 +48,14 @@ class Lxc
24
48
  config << "lxc.network.flags = #{flags}"
25
49
  end
26
50
  end
27
- if(resource.cap_drop)
28
- config << "lxc.cap.drop = #{Array(resource.cap_drop).join(' ')}"
51
+ if(resource[:cap_drop])
52
+ config << "lxc.cap.drop = #{Array(resource[:cap_drop]).join(' ')}"
29
53
  end
30
54
  %w(pts tty arch devttydir mount mount_entry rootfs rootfs_mount pivotdir).each do |k|
31
- config << "lxc.#{k.sub('_', '.')} = #{resource.send(k)}" if resource.send(k)
55
+ config << "lxc.#{k.sub('_', '.')} = #{resource[k.to_sym]}" if resource[k.to_sym]
32
56
  end
33
57
  prefix = 'lxc.cgroup'
34
- resource.cgroup.each_pair do |key, value|
58
+ resource[:cgroup].each_pair do |key, value|
35
59
  if(value.is_a?(Array))
36
60
  value.each do |val|
37
61
  config << "#{prefix}.#{key} = #{val}"
@@ -49,7 +73,7 @@ class Lxc
49
73
  raise 'LXC config file not found' unless File.exists?(path)
50
74
  @path = path
51
75
  @network = []
52
- @base = Mash.new
76
+ @base = defined?(Mash) ? Mash.new : {}
53
77
  parse!
54
78
  end
55
79
 
@@ -2,5 +2,5 @@ module Elecksee
2
2
  class Version < Gem::Version
3
3
  end
4
4
 
5
- VERSION = Version.new('1.0.10')
5
+ VERSION = Version.new('1.0.12')
6
6
  end
metadata CHANGED
@@ -1,48 +1,52 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: elecksee
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.10
5
- prerelease:
4
+ version: 1.0.12
5
+ prerelease:
6
6
  platform: ruby
7
7
  authors:
8
8
  - Chris Roberts
9
- autorequire:
9
+ autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2013-08-06 00:00:00.000000000 Z
12
+ date: 2013-11-14 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: mixlib-shellout
16
- requirement: !ruby/object:Gem::Requirement
17
- none: false
16
+ version_requirements: !ruby/object:Gem::Requirement
18
17
  requirements:
19
- - - ! '>='
18
+ - - ">="
20
19
  - !ruby/object:Gem::Version
21
- version: '0'
22
- type: :runtime
23
- prerelease: false
24
- version_requirements: !ruby/object:Gem::Requirement
20
+ version: !binary |-
21
+ MA==
25
22
  none: false
23
+ requirement: !ruby/object:Gem::Requirement
26
24
  requirements:
27
- - - ! '>='
25
+ - - ">="
28
26
  - !ruby/object:Gem::Version
29
- version: '0'
27
+ version: !binary |-
28
+ MA==
29
+ none: false
30
+ prerelease: false
31
+ type: :runtime
30
32
  - !ruby/object:Gem::Dependency
31
33
  name: net-ssh
32
- requirement: !ruby/object:Gem::Requirement
33
- none: false
34
+ version_requirements: !ruby/object:Gem::Requirement
34
35
  requirements:
35
- - - ! '>='
36
+ - - ">="
36
37
  - !ruby/object:Gem::Version
37
- version: '0'
38
- type: :runtime
39
- prerelease: false
40
- version_requirements: !ruby/object:Gem::Requirement
38
+ version: !binary |-
39
+ MA==
41
40
  none: false
41
+ requirement: !ruby/object:Gem::Requirement
42
42
  requirements:
43
- - - ! '>='
43
+ - - ">="
44
44
  - !ruby/object:Gem::Version
45
- version: '0'
45
+ version: !binary |-
46
+ MA==
47
+ none: false
48
+ prerelease: false
49
+ type: :runtime
46
50
  description: LXC helpers
47
51
  email: chrisroberts.code@gmail.com
48
52
  executables:
@@ -51,47 +55,50 @@ extensions: []
51
55
  extra_rdoc_files: []
52
56
  files:
53
57
  - elecksee.gemspec
58
+ - Gemfile
59
+ - README.md
60
+ - elecksee-1.0.10.gem
61
+ - LICENSE
62
+ - CHANGELOG.md
63
+ - Gemfile.lock
64
+ - lib/elecksee.rb
54
65
  - lib/elecksee/clone.rb
55
66
  - lib/elecksee/version.rb
56
67
  - lib/elecksee/lxc_file_config.rb
68
+ - lib/elecksee/ephemeral.rb
69
+ - lib/elecksee/lxc.rb
57
70
  - lib/elecksee/helpers/copies.rb
58
71
  - lib/elecksee/helpers/options.rb
59
72
  - lib/elecksee/helpers/base.rb
73
+ - lib/elecksee/knife/config.rb
60
74
  - lib/elecksee/storage/overlay_mount.rb
61
75
  - lib/elecksee/storage/virtual_device.rb
62
76
  - lib/elecksee/storage/overlay_directory.rb
63
- - lib/elecksee/ephemeral.rb
64
- - lib/elecksee/lxc.rb
65
- - lib/elecksee.rb
66
- - Gemfile
67
- - README.md
68
- - LICENSE
69
77
  - bin/lxc-awesome-ephemeral
70
- - CHANGELOG.md
71
- - Gemfile.lock
72
78
  homepage: http://github.com/chrisroberts/elecksee
73
79
  licenses: []
74
- post_install_message:
80
+ post_install_message:
75
81
  rdoc_options: []
76
82
  require_paths:
77
83
  - lib
78
84
  required_ruby_version: !ruby/object:Gem::Requirement
79
- none: false
80
85
  requirements:
81
- - - ! '>='
86
+ - - ">="
82
87
  - !ruby/object:Gem::Version
83
- version: '0'
84
- required_rubygems_version: !ruby/object:Gem::Requirement
88
+ version: !binary |-
89
+ MA==
85
90
  none: false
91
+ required_rubygems_version: !ruby/object:Gem::Requirement
86
92
  requirements:
87
- - - ! '>='
93
+ - - ">="
88
94
  - !ruby/object:Gem::Version
89
- version: '0'
95
+ version: !binary |-
96
+ MA==
97
+ none: false
90
98
  requirements: []
91
- rubyforge_project:
99
+ rubyforge_project:
92
100
  rubygems_version: 1.8.24
93
- signing_key:
101
+ signing_key:
94
102
  specification_version: 3
95
103
  summary: LXC helpers
96
104
  test_files: []
97
- has_rdoc: