elecksee 1.0.8 → 1.0.10

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