elecksee 1.0.10 → 1.0.12

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/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: