elecksee 1.0.8 → 1.0.10

Sign up to get free protection for your applications and to get access to all the features.
data/CHANGELOG.md CHANGED
@@ -1,3 +1,9 @@
1
+ ## v1.0.10
2
+ * Add clone support
3
+ * Allow command passage to ephemeral nodes
4
+ * Update temp directories to be world accessible/readable
5
+ * Clean up and dry out some reusable bits
6
+
1
7
  ## v1.0.8
2
8
  * Delete overlay directories using `sudo`
3
9
 
data/Gemfile.lock ADDED
@@ -0,0 +1,18 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ elecksee (1.0.9)
5
+ mixlib-shellout
6
+ net-ssh
7
+
8
+ GEM
9
+ remote: https://rubygems.org/
10
+ specs:
11
+ mixlib-shellout (1.1.0)
12
+ net-ssh (2.6.7)
13
+
14
+ PLATFORMS
15
+ ruby
16
+
17
+ DEPENDENCIES
18
+ elecksee!
data/README.md CHANGED
@@ -1,47 +1,25 @@
1
1
  # Elecksee
2
2
 
3
- This is a simple library for interacting with LXC. It relies on
4
- LXC tools being installed on the system to properly function.
5
- It is extracted from the Chef LXC cookbook, so poke around there
3
+ An LXC library for Ruby
6
4
 
7
5
  ## Usage
8
6
 
9
7
  ```ruby
10
-
11
8
  require 'elecksee/lxc'
12
9
 
13
- lxc = Lxc.new('my-container')
14
- p lxc.info
15
- ```
16
-
17
- ## Awesome
18
-
19
- Awesome ephemerals lets you create ephemeral nodes with different
20
- overlays for the rootfs. Since tmpfs will place a size restriction
21
- on ephemerals based on current memory available, this provides a
22
- simple workaround. Currently supported is a raw temporary directory
23
- on the host, or a VBD that defaults to 2GB.
24
-
25
- ### Using temporary directory
26
-
27
- ```
28
- $ lxc-awesome-ephemeral -o ubuntu -d -z /tmp/lxc-roots
10
+ lxc = Lxc.new('container')
11
+ lxc.start unless lxc.running?
29
12
  ```
30
13
 
31
- ### Using VBD
32
-
33
- ```
34
- $ lxc-awesome-ephemeral -o ubuntu -d -D 2000
35
- ```
14
+ ## Included
36
15
 
37
- This will create a 2GB virtual block device for the container
38
- to use for the overlay.
16
+ * Container inspect and interaction (`Lxc`)
17
+ * Container cloning (`Lxc::Clone`)
18
+ * Ephemeral containers (`Lxc::Ephemeral`)
39
19
 
40
- Note
41
- ----
20
+ ### Notes
42
21
 
43
- Overlays are not persistent (thus ephemeral) and will be automatically
44
- cleaned up when the container has reached a stop state.
22
+ This library is currently tested on ubuntu platforms >= 12.04
45
23
 
46
24
  # Info
47
25
 
@@ -1,15 +1,124 @@
1
- require 'elecksee/helpers'
2
- require 'elecksee/lxc'
1
+ %w(
2
+ helpers/base helpers/options helpers/copies lxc
3
+ storage/overlay_directory storage/overlay_mount
4
+ storage/virtual_device
5
+ ).each do |path|
6
+ require "elecksee/#{path}"
7
+ end
3
8
 
4
9
  class Lxc
5
10
  class Clone
6
11
 
7
-
12
+ include Helpers
13
+ include Helpers::Options
14
+ include Helpers::Copies
15
+
16
+ option :original, '-o', :string, :required => true, :desc => 'Original container name', :aliases => 'orig'
17
+ option :new_name, '-n', :string, :required => true, :desc => 'New container name', :aliases => 'new'
18
+ option :snapshot, '-s', :boolean, :desc => 'Make new rootfs a snapshot of original'
19
+ option :fssize, '-L', :string, :desc => 'Size of new file system', :default => '2G'
20
+ option :vgname, '-v', :string, :desc => 'LVM volume group name', :default => 'lxc'
21
+ option :lvprefix, '-p', :string, :desc => 'LVM volume name prefix'
22
+ option :fstype, '-t', :string, :desc => 'New container file system', :default => 'ext4'
23
+ option :device, '-D', :integer, :desc => 'Make copy in VBD of {SIZE}M'
24
+ option :ipaddress, '-I', :string, :desc => 'Custom IP address'
25
+ option :gateway, '-G', :string, :desc => 'Custom gateway'
26
+ option :netmask, '-N', :string, :default => '255.255.255.0', :desc => 'Custom netmask'
27
+
28
+ # Hash containing original and new container instances
29
+ attr_reader :lxcs
30
+
8
31
  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]
32
+ configure!(args)
33
+ @lxcs = {}
34
+ @lxcs[:original] = Lxc.new(original)
35
+ @lxcs[:new] = Lxc.new(new_name)
36
+ @created = []
37
+ validate!
38
+ end
39
+
40
+ def clone!
41
+ begin
42
+ copy_original
43
+ update_naming(:no_config)
44
+ apply_custom_addressing if ipaddress
45
+ lxcs[:new]
46
+ rescue Exception
47
+ @created.map(&:destroy)
48
+ raise
49
+ end
50
+ end
51
+
52
+ private
53
+
54
+ alias_method :name, :new_name
55
+
56
+ # Returns new lxc instance
57
+ def lxc
58
+ lxcs[:new]
59
+ end
60
+
61
+ def created(thing)
62
+ @created << thing
63
+ end
64
+
65
+ def validate!
66
+ unless(lxcs[:original].exists?)
67
+ raise "Requested `original` container does not exist (#{original})"
12
68
  end
69
+ if(lxcs[:new].exists?)
70
+ raise "Requested `new` container already exists (#{new_name})"
71
+ end
72
+ if(lxcs[:original].running?)
73
+ raise "Requested `original` container is current running (#{original})"
74
+ end
75
+ end
76
+
77
+ def copy_original
78
+ copy_init
79
+ if(device)
80
+ rootfs_dir = copy_vbd
81
+ elsif(File.stat(lxcs[:original].path.to_s).blockdev?)
82
+ rootfs_dir = copy_lvm
83
+ elsif(command("btrfs subvolume list '#{lxcs[:original].path}'", :allow_failure => true))
84
+ rootfs_dir = copy_btrfs
85
+ else
86
+ rootfs_dir = copy_fs
87
+ end
88
+ update_rootfs(rootfs_dir)
89
+ end
90
+
91
+ def copy_init
92
+ directory = CloneDirectory.new(lxcs[:new].name, :dir => File.dirname(lxcs[:original].path.to_s))
93
+ created(directory)
94
+ %w(config fstab).each do |file|
95
+ command("cp '#{lxcs[:original].path.join(file)}' '#{directory.target_path}'", :sudo => true)
96
+ end
97
+ end
98
+
99
+ def copy_fs
100
+ directory = CloneDirectory.new(lxcs[:new].name, :dir => File.dirname(lxcs[:original].path.to_s))
101
+ created(directory)
102
+ command("rsync -ax '#{lxcs[:original].rootfs}/' '#{File.join(directory.target_path, 'rootfs')}/'", :sudo => true)
103
+ File.join(directory.target_path, 'rootfs')
104
+ end
105
+
106
+ def copy_vbd
107
+ storage = VirtualDevice.new(lxcs[:new].name, :tmp_dir => '/opt/lxc-vbd')
108
+ created(storage)
109
+ command("rsync -ax '#{lxcs[:original].rootfs}/' '#{storage.target_path}/'", :sudo => true)
110
+ storage.target_path
111
+ end
112
+
113
+ def copy_lvm
114
+ raise 'Not implemented'
115
+ end
116
+
117
+ def copy_btrfs
118
+ rootfs_path = lxcs[:new].path.join('rootfs')
119
+ command("btrfs subvolume snapshot '#{lxcs[:original].rootfs}' '#{rootfs_path}'", :sudo => true)
120
+ # TODO: Remove on failure
121
+ rootfs_path
13
122
  end
14
123
  end
15
124
  end
@@ -4,8 +4,9 @@ require 'tmpdir'
4
4
  require 'etc'
5
5
 
6
6
  %w(
7
- helpers lxc storage/overlay_directory
8
- storage/overlay_mount storage/virtual_device
7
+ helpers/base helpers/options helpers/copies lxc
8
+ storage/overlay_directory storage/overlay_mount
9
+ storage/virtual_device
9
10
  ).each do |path|
10
11
  require "elecksee/#{path}"
11
12
  end
@@ -15,26 +16,8 @@ class Lxc
15
16
  class Ephemeral
16
17
 
17
18
  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
19
+ include Helpers::Options
20
+ include Helpers::Copies
38
21
 
39
22
  option :original, '-o', :string, :required => true, :desc => 'Original container name'
40
23
  option :ipaddress, '-I', :string, :desc => 'Custom IP address'
@@ -49,6 +32,7 @@ class Lxc
49
32
  option :ssh_key, '-S', :string, :default => '/opt/hw-lxc-config/id_rsa', :aliases => 'ssh-key', :desc => 'Deprecated: Provided for compatibility'
50
33
  option :lxc_dir, '-L', :string, :default => '/var/lib/lxc', :aliases => 'lxc-dir', :desc => 'Directory of LXC store'
51
34
  option :tmp_dir, '-T', :string, :default => '/tmp/lxc/ephemerals', :aliases => 'tmp-dir', :desc => 'Directory of ephemeral temp files'
35
+ option :ephemeral_command, '-C', :string, :aliases => 'command'
52
36
 
53
37
  attr_reader :name
54
38
  attr_reader :cli
@@ -63,6 +47,7 @@ class Lxc
63
47
  configure!(args)
64
48
  @cli = args[:cli]
65
49
  @path = command("mktemp -d -p #{lxc_dir} #{original}-XXXXXXXXXXXX", :sudo => true).stdout.strip
50
+ command("chmod 0755 #{@path}", :sudo => true)
66
51
  @name = File.basename(@path)
67
52
  @hostname = @name.gsub(%r{[^A-Za-z0-9\-]}, '')
68
53
  @ephemeral_binds = []
@@ -81,6 +66,21 @@ class Lxc
81
66
  puts " - Connect using: sudo ssh -i #{ssh_key} root@#{lxc.container_ip(10)}"
82
67
  end
83
68
  end
69
+
70
+ def start_action
71
+ begin
72
+ lxc.start
73
+ if(ephemeral_command)
74
+ lxc.container_command(ephemeral_command)
75
+ else
76
+ cli_output
77
+ lxc.wait_for_state(:stopped)
78
+ end
79
+ ensure
80
+ cleanup
81
+ end
82
+ true
83
+ end
84
84
 
85
85
  def start!(*args)
86
86
  register_traps
@@ -88,23 +88,14 @@ class Lxc
88
88
  if(daemon)
89
89
  if(args.include?(:fork))
90
90
  fork do
91
- lxc.start
92
- cli_output
93
- lxc.wait_for_state(:stopped)
94
- cleanup
91
+ start_action
95
92
  end
96
93
  else
97
94
  Process.daemon
98
- lxc.start
99
- cli_output
100
- lxc.wait_for_state(:stopped)
101
- cleanup
95
+ start_action
102
96
  end
103
97
  else
104
- lxc.start
105
- cli_output
106
- lxc.wait_for_state(:stopped)
107
- cleanup
98
+ start_action
108
99
  end
109
100
  end
110
101
 
@@ -123,39 +114,7 @@ class Lxc
123
114
  end
124
115
 
125
116
  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
-
117
+
159
118
  def setup
160
119
  create
161
120
  build_overlay
@@ -174,137 +133,50 @@ class Lxc
174
133
  @ephemeral_overlay = OverlayMount.new(
175
134
  :base => Lxc.new(original).rootfs.to_path,
176
135
  :overlay => ephemeral_device.target_path,
177
- :target => lxc.rootfs.to_path,
136
+ :target => lxc.path.join('rootfs').to_path,
178
137
  :overlay_type => union
179
138
  )
180
139
  @ephemeral_overlay.mount
181
140
  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
141
 
193
142
  def create
194
143
  Dir.glob(File.join(lxc_dir, original, '*')).each do |o_path|
195
144
  next unless File.file?(o_path)
196
145
  command("cp #{o_path} #{File.join(path, File.basename(o_path))}", :sudo => true)
197
146
  end
198
- command("chown -R #{Etc.getlogin} #{path}", :sudo => true)
199
147
  @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
148
+ command("mkdir -p '#{lxc.path.join('rootfs')}'", :sudo => true)
149
+ update_net_hwaddr
211
150
  end
212
151
 
213
152
  # TODO: Discovered binds for ephemeral are all tmpfs for now.
153
+ # TODO: We should default to overlay mount, make virt dev optional
214
154
  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
155
+ contents = File.readlines(lxc.path.join('fstab')).each do |line|
156
+ parts = line.split(' ')
157
+ if(parts[3] == 'bind')
158
+ source = parts.first
159
+ target = parts[1].sub(%r{^.+rootfs/}, '')
160
+ container_target = lxc.rootfs.join(target).to_path
161
+ device = VirtualDevice.new(target.gsub('/', '_'), :tmp_fs => true)
162
+ device.mount
163
+ FileUtils.mkdir_p(container_target)
164
+ ephemeral_binds << device
165
+ if(union == 'overlayfs')
166
+ "none #{container_target} overlayfs upperdir=#{device.mount_path},lowerdir=#{source} 0 0"
232
167
  else
233
- file.write line
168
+ "none #{container_target} aufs br=#{device.mount_path}=rw:#{source}=ro,noplink 0 0"
234
169
  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)
170
+ else
171
+ line
259
172
  end
260
173
  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
174
+ # If bind option used, bind in for rw
175
+ if(bind)
176
+ command("mkdir -p #{lxc.rootfs.join(bind).to_path}", :sudo => true)
177
+ contents << "#{bind} #{lxc.rootfs.join(bind)} none bind 0 0\n"
307
178
  end
179
+ write_file(lxc.path.join('fstab'), contents.join)
308
180
  end
309
181
  end
310
182
  end
@@ -1,9 +1,9 @@
1
1
  class Lxc
2
2
  class CommandFailed < StandardError
3
3
  end
4
-
5
- module Helpers
6
4
 
5
+ module Helpers
6
+
7
7
  def sudo
8
8
  Lxc.sudo
9
9
  end
@@ -14,7 +14,7 @@ class Lxc
14
14
  cmd = [sudo, cmd].join(' ') if args[:sudo]
15
15
  begin
16
16
  shlout = Mixlib::ShellOut.new(cmd,
17
- :logger => defined?(Chef) ? Chef::Log.logger : log,
17
+ :logger => defined?(Chef) && defined?(Chef::Log) ? Chef::Log.logger : log,
18
18
  :live_stream => args[:livestream] ? STDOUT : nil,
19
19
  :timeout => args[:timeout] || 1200,
20
20
  :environment => {'HOME' => detect_home}
@@ -30,7 +30,7 @@ class Lxc
30
30
  retries -= 1
31
31
  retry
32
32
  elsif(args[:allow_failure])
33
- true
33
+ false
34
34
  else
35
35
  raise
36
36
  end
@@ -0,0 +1,106 @@
1
+ require 'tempfile'
2
+
3
+ class Lxc
4
+ module Helpers
5
+ module Copies
6
+
7
+ NAME_FILES = %w(fstab config)
8
+ HOSTNAME_FILES = %w(
9
+ rootfs/etc/hostname
10
+ rootfs/etc/hosts
11
+ rootfs/etc/sysconfig/network
12
+ rootfs/etc/sysconfig/network-scripts/ifcfg-eth0
13
+ )
14
+
15
+ def update_rootfs(rootfs_path)
16
+ contents = File.readlines(lxc.config.to_s).map do |line|
17
+ if(line.start_with?('lxc.rootfs'))
18
+ "lxc.rootfs = #{rootfs_path}\n"
19
+ else
20
+ line
21
+ end
22
+ end.join
23
+ write_file(lxc.config, contents)
24
+ end
25
+
26
+ def update_net_hwaddr
27
+ contents = File.readlines(lxc.config).map do |line|
28
+ if(line.start_with?('lxc.network.hwaddr'))
29
+ parts = line.split('=')
30
+ "#{parts.first.strip} = 00:16:3e#{SecureRandom.hex(3).gsub(/(..)/, ':\1')}\n"
31
+ else
32
+ line
33
+ end
34
+ end.join
35
+ write_file(lxc.config, contents)
36
+ end
37
+
38
+ def write_file(path, contents)
39
+ contents = contents.join if contents.is_a?(Array)
40
+ tmp = Tempfile.new('lxc-copy')
41
+ tmp.write(contents)
42
+ tmp.close
43
+ command("cp #{tmp.path} #{path}", :sudo => true)
44
+ tmp.unlink
45
+ command("chmod 0644 #{path}", :sudo => true)
46
+ end
47
+
48
+ def update_naming(*args)
49
+ NAME_FILES.each do |file|
50
+ next unless File.exists?(lxc.path.join(file))
51
+ next if args.include?("no_#{file}".to_sym)
52
+ contents = File.read(lxc.path.join(file)).gsub(original, name)
53
+ write_file(lxc.path.join(file), contents)
54
+ end
55
+ HOSTNAME_FILES.each do |file|
56
+ next unless File.exists?(lxc.path.join(file))
57
+ next if args.include?("no_#{file}".to_sym)
58
+ contents = File.read(lxc.path.join(file)).gsub(original, name)
59
+ write_file(lxc.path.join(file), contents)
60
+ end
61
+ end
62
+
63
+ def el_platform?
64
+ lxc.rootfs.join('etc/redhat-release').exist?
65
+ end
66
+
67
+ def apply_custom_networking
68
+ if(el_platform?)
69
+ path = lxc.rootfs.join('etc/sysconfig/network-scripts/ifcfg-eth0')
70
+ content = <<-EOF
71
+ DEVICE=eth0
72
+ BOOTPROTO=static
73
+ NETMASK=#{netmask}
74
+ IPADDR=#{ipaddress}
75
+ ONBOOT=yes
76
+ TYPE=Ethernet
77
+ USERCTL=yes
78
+ PEERDNS=yes
79
+ IPV6INIT=no
80
+ GATEWAY=#{gateway}
81
+ EOF
82
+ write_file(path, content)
83
+ path = lxc.rootfs.join('etc/sysconfig/network')
84
+ content = <<-EOF
85
+ NETWORKING=yes
86
+ HOSTNAME=#{hostname}
87
+ EOF
88
+ write_file(path, content)
89
+ write_file(lxc.rootfs.join('etc/rc.local'), "hostname #{hostname}\n")
90
+ else
91
+ path = lxc.rootfs.join('etc/network/interfaces')
92
+ content = <<-EOF
93
+ auto lo
94
+ iface lo inet loopback
95
+ auto eth0
96
+ iface eth0 inet static
97
+ address #{ipaddress}
98
+ netmask #{netmask}
99
+ gateway #{gateway}
100
+ EOF
101
+ write_file(path, content)
102
+ end
103
+ end
104
+ end
105
+ end
106
+ end
@@ -0,0 +1,58 @@
1
+ class Lxc
2
+ module Helpers
3
+
4
+ module Options
5
+ class << self
6
+ def included(klass)
7
+ klass.class_eval do
8
+ class << self
9
+ attr_reader :options
10
+
11
+ def option(name, short, type, args={})
12
+ @options ||= {}
13
+ @options[name] = args.merge(:short => short, :type => type)
14
+ instance_eval do
15
+ attr_accessor name.to_sym
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
22
+
23
+ private
24
+
25
+ def configure!(args)
26
+ self.class.options.each do |name, opts|
27
+ argv = args.detect{|k,v| (Array(opts[:aliases]) + Array(opts[:short]) + [name]).include?(k.to_sym)}
28
+ argv = argv.last if argv
29
+ argv ||= opts[:default]
30
+ if(argv)
31
+ check_type!(name, argv, opts[:type])
32
+ self.send("#{name}=", argv)
33
+ else
34
+ if(opts[:required])
35
+ raise ArgumentError.new "Missing required argument: #{name}"
36
+ end
37
+ end
38
+ end
39
+ if(ipaddress && gateway.nil?)
40
+ self.gateway = ipaddress.sub(%r{\d+$}, '1')
41
+ end
42
+ end
43
+
44
+ def check_type!(arg_name, val, type)
45
+ valid = false
46
+ case type
47
+ when :string
48
+ valid = val.is_a?(String)
49
+ when :boolean
50
+ valid = val.is_a?(TrueClass) || val.is_a?(FalseClass)
51
+ when :integer
52
+ valid = val.is_a?(Numeric)
53
+ end
54
+ raise ArgumentError.new "Invalid type provided for #{arg_name}. Expecting value type of: #{type.inspect} Got: #{val.class} - #{val}" unless valid
55
+ end
56
+ end
57
+ end
58
+ end
data/lib/elecksee/lxc.rb CHANGED
@@ -1,4 +1,4 @@
1
- require 'elecksee/helpers'
1
+ require 'elecksee/helpers/base'
2
2
  require 'mixlib/shellout'
3
3
  require 'pathname'
4
4
  require 'tmpdir'
@@ -245,7 +245,12 @@ class Lxc
245
245
  alias_method :config, :container_config
246
246
 
247
247
  def container_rootfs
248
- container_path.join('rootfs')
248
+ if(File.exists?(config))
249
+ r_path = File.readlines(config).detect do |line|
250
+ line.start_with?('lxc.rootfs')
251
+ end.to_s.split('=').last.to_s.strip
252
+ end
253
+ r_path.to_s.empty? ? container_path.join('rootfs') : Pathname.new(r_path)
249
254
  end
250
255
  alias_method :rootfs, :container_rootfs
251
256
 
@@ -1,4 +1,4 @@
1
- require 'elecksee/helpers'
1
+ require 'elecksee/helpers/base'
2
2
 
3
3
  class Lxc
4
4
  class OverlayDirectory
@@ -32,4 +32,19 @@ class Lxc
32
32
  end
33
33
 
34
34
  end
35
+
36
+ # Clone directory does the same as the overlay, just in
37
+ # a persistent place
38
+ class CloneDirectory < OverlayDirectory
39
+ def initialize(name, args={})
40
+ args[:tmp_dir] = args[:dir] if args[:dir]
41
+ args[:tmp_dir] || '/var/lib/lxc'
42
+ super
43
+ end
44
+
45
+ def overlay_path
46
+ File.join(tmp_dir, name)
47
+ end
48
+ alias_method :target_path, :overlay_path
49
+ end
35
50
  end
@@ -1,4 +1,4 @@
1
- require 'elecksee/helpers'
1
+ require 'elecksee/helpers/base'
2
2
 
3
3
  class Lxc
4
4
  class OverlayMount
@@ -1,4 +1,4 @@
1
- require 'elecksee/helpers'
1
+ require 'elecksee/helpers/base'
2
2
 
3
3
  class Lxc
4
4
 
@@ -2,5 +2,5 @@ module Elecksee
2
2
  class Version < Gem::Version
3
3
  end
4
4
 
5
- VERSION = Version.new('1.0.8')
5
+ VERSION = Version.new('1.0.10')
6
6
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: elecksee
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.8
4
+ version: 1.0.10
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2013-07-12 00:00:00.000000000 Z
12
+ date: 2013-08-06 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: mixlib-shellout
@@ -54,7 +54,9 @@ files:
54
54
  - lib/elecksee/clone.rb
55
55
  - lib/elecksee/version.rb
56
56
  - lib/elecksee/lxc_file_config.rb
57
- - lib/elecksee/helpers.rb
57
+ - lib/elecksee/helpers/copies.rb
58
+ - lib/elecksee/helpers/options.rb
59
+ - lib/elecksee/helpers/base.rb
58
60
  - lib/elecksee/storage/overlay_mount.rb
59
61
  - lib/elecksee/storage/virtual_device.rb
60
62
  - lib/elecksee/storage/overlay_directory.rb
@@ -65,8 +67,8 @@ files:
65
67
  - README.md
66
68
  - LICENSE
67
69
  - bin/lxc-awesome-ephemeral
68
- - elecksee-1.0.8.gem
69
70
  - CHANGELOG.md
71
+ - Gemfile.lock
70
72
  homepage: http://github.com/chrisroberts/elecksee
71
73
  licenses: []
72
74
  post_install_message: