linux_container 0.1

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 ADDED
@@ -0,0 +1 @@
1
+ v0.1. first version
data/Manifest ADDED
@@ -0,0 +1,6 @@
1
+ CHANGELOG
2
+ README.md
3
+ Rakefile
4
+ lib/linux_container.rb
5
+ test/test_all.rb
6
+ Manifest
data/README.md ADDED
@@ -0,0 +1,62 @@
1
+ ## LinuxContainer gem
2
+
3
+ This gem is an easy ruby interface to ubuntu linux containers (LXC). It
4
+ provides a convenient wrapper for starting Ubuntu Cloud image-based
5
+ ephemeral containers.
6
+
7
+ lxc-start-ephemeral uses AUFS overlay filesystem to clone an container using
8
+ very little disk space, which then disappears when shut down.
9
+
10
+ $ gem install linux_container
11
+
12
+ ### Create a new container
13
+
14
+ > c = LinuxContainer.new(
15
+ name: 'mycontainer',
16
+ username: 'ubuntu', # optional for SSH, defaults to ubuntu
17
+ ssh_key_path: '~/.ssh/id_rsa' # optional
18
+ )
19
+
20
+ > c.create(
21
+ template: 'ubuntu-cloud', # optional, defaults to ubuntu-cloud
22
+ hostid: 'i12345', # optional, autogenerated
23
+ userdata: 'userdata.txt', # optional cloud-init user-data script
24
+ auth-key: '~/.ssh/id_rsa', # optional, defaults to ssh_key_path
25
+ arch: 'i686', # optional, defaults to host arch
26
+ release: 'quantal' # optional, defaults to host release
27
+ )
28
+
29
+ > c.dir('/tmp')
30
+ => "/var/lib/lxc/mycontainer/rootfs/tmp"
31
+
32
+ ### Start container and mess around with it
33
+
34
+ > c.start
35
+ > c.state
36
+ > c.running?
37
+ > c.ip
38
+ > c.wait_for { ip && sshable? }
39
+ > c.ssh 'uptime'
40
+ > c.stop
41
+
42
+ ### Start an ephemeral container cloning an existing container
43
+
44
+ > e = c.start_ephemeral
45
+
46
+
47
+ ### Get existing containers
48
+
49
+ > LinuxContainer.all
50
+ => [container, container, container, ... ]
51
+
52
+ ### Force shutdown
53
+
54
+ c.destroy '-f'
55
+
56
+ ### other commands
57
+
58
+ execute, kill, wait, cgroup, ps, info, freeze, unfreeze, netstat
59
+
60
+
61
+
62
+
data/Rakefile ADDED
@@ -0,0 +1,8 @@
1
+ require 'echoe'
2
+
3
+ Echoe.new("linux_container") do |p|
4
+ p.author = "Andrew Snow"
5
+ p.email = 'andrew@modulus.org'
6
+ p.summary = "Ruby gem for ephemeral LXC linux containers"
7
+ p.url = "https://github.com/andys/linux_container"
8
+ end
@@ -0,0 +1,113 @@
1
+
2
+ require 'timeout'
3
+ require 'shellwords'
4
+ require 'tempfile'
5
+
6
+ class LinuxContainer
7
+ attr_accessor :name, :username, :ssh_key_path
8
+ attr_writer :ip
9
+
10
+ def self.all
11
+ `sudo lxc-ls`.lines.map(&:strip).uniq.map {|cname| new(name: cname) }
12
+ end
13
+
14
+ def initialize(params={})
15
+ params.each {|k,v| instance_variable_set "@#{k}", v }
16
+ @username ||= 'ubuntu'
17
+ end
18
+
19
+ def state
20
+ info('-s').chomp.split(':').last.strip
21
+ end
22
+
23
+ def ip
24
+ @ip ||= self.class.get_ip_for(name)
25
+ end
26
+
27
+ def create(config={}) # template, hostid, userdata, auth-key, arch, release
28
+ args = (ssh_key_path ? {'auth-key' => "#{ssh_key_path}.pub"} : {}).merge(config).map {|k, v| "--#{k}=#{v}" }
29
+ lxc_execute('create', '-t', config.delete(:template) || 'ubuntu-cloud', '--', *args)
30
+ end
31
+
32
+ [:start, :stop, :destroy, :execute, :kill, :wait, :cgroup, :ps, :info, :freeze, :unfreeze, :netstat].each do |cmd|
33
+ define_method(cmd) {|*args| lxc_execute(cmd, *args) }
34
+ end
35
+
36
+ def start_ephemeral
37
+ args = ['lxc-start-ephemeral','-U','aufs','-u',username,'-o',name]
38
+ logfile = bg_execute(*args)
39
+ newname = nil
40
+ while newname.nil?
41
+ sleep 1
42
+ logfile.rewind
43
+ newname = $1 if logfile.read =~ /^(.*) is running/
44
+ end
45
+ self.class.new(name: newname, ssh_key_path: ssh_key_path, username: username)
46
+ end
47
+
48
+ def ssh(cmd)
49
+ raise "cannot ssh without ip" unless ip
50
+ args = ['-o','StrictHostKeyChecking=no','-o','UserKnownHostsFile=/dev/null']
51
+ args.push('-i', ssh_key_path) if ssh_key_path
52
+ args.push("#{username}@#{ip}", cmd)
53
+ execute('ssh', *args)
54
+ end
55
+
56
+ def sshable?
57
+ ssh('true') rescue false
58
+ end
59
+
60
+ def running?
61
+ state == 'RUNNING'
62
+ end
63
+
64
+ def dir(subdir=nil)
65
+ "/var/lib/lxc/#{name}/rootfs/#{subdir}"
66
+ end
67
+
68
+ def wait_for(timeout=300, interval=1, &block)
69
+ Timeout.timeout(timeout) do
70
+ duration = 0
71
+ start = Time.now
72
+ until instance_eval(&block) || duration > timeout
73
+ sleep(interval.to_f)
74
+ duration = Time.now - start
75
+ end
76
+ if duration > timeout
77
+ false
78
+ else
79
+ duration
80
+ end
81
+ end
82
+ end
83
+
84
+ #############################################################################
85
+
86
+ def lxc_execute(cmd, *args)
87
+ execute("lxc-#{cmd}", '-n', name, *args)
88
+ end
89
+
90
+ def execute(*cmd)
91
+ cmdstring = "sudo #{cmd.shift} #{Shellwords.join(cmd)} "
92
+ result = `#{cmdstring} 2>&1`
93
+ raise "command failed: #{cmdstring.inspect}\n#{result}" unless $? == 0
94
+ result
95
+ end
96
+
97
+ def bg_execute(*cmd)
98
+ logfile = Tempfile.new(self.class.to_s)
99
+ cmdstring = "sudo #{cmd.shift} #{Shellwords.join(cmd)} >>#{logfile.path} 2>>#{logfile.path} &"
100
+ system(cmdstring)
101
+ raise "command failed: #{cmdstring.inspect}\n" unless $? == 0
102
+ logfile
103
+ end
104
+
105
+ def self.get_ip_for(name)
106
+ File.read('/var/lib/misc/dnsmasq.leases').each_line do |line|
107
+ (timestamp,macaddr,ip,hostname,misc) = line.split(' ')
108
+ return ip if hostname == name
109
+ end
110
+ nil
111
+ end
112
+
113
+ end
@@ -0,0 +1,30 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ Gem::Specification.new do |s|
4
+ s.name = "linux_container"
5
+ s.version = "0.1"
6
+
7
+ s.required_rubygems_version = Gem::Requirement.new(">= 1.2") if s.respond_to? :required_rubygems_version=
8
+ s.authors = ["Andrew Snow"]
9
+ s.date = "2013-02-08"
10
+ s.description = "Ruby gem for ephemeral LXC linux containers"
11
+ s.email = "andrew@modulus.org"
12
+ s.extra_rdoc_files = ["CHANGELOG", "README.md", "lib/linux_container.rb"]
13
+ s.files = ["CHANGELOG", "README.md", "Rakefile", "lib/linux_container.rb", "test/test_all.rb", "Manifest", "linux_container.gemspec"]
14
+ s.homepage = "https://github.com/andys/linux_container"
15
+ s.rdoc_options = ["--line-numbers", "--inline-source", "--title", "Linux_container", "--main", "README.md"]
16
+ s.require_paths = ["lib"]
17
+ s.rubyforge_project = "linux_container"
18
+ s.rubygems_version = "1.8.25"
19
+ s.summary = "Ruby gem for ephemeral LXC linux containers"
20
+ s.test_files = ["test/test_all.rb"]
21
+
22
+ if s.respond_to? :specification_version then
23
+ s.specification_version = 3
24
+
25
+ if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
26
+ else
27
+ end
28
+ else
29
+ end
30
+ end
data/test/test_all.rb ADDED
@@ -0,0 +1,48 @@
1
+ require "#{File.dirname(__FILE__)}/../lib/linux_container"
2
+
3
+ require 'minitest/autorun'
4
+
5
+ class TestLinuxContainer < MiniTest::Unit::TestCase
6
+ def self.startup
7
+ $sshkey = "/tmp/linuxcontainergemtestssh#{$$}"
8
+ `ssh-keygen -q -t rsa -f #{$sshkey} -N ''` unless File.exists?($sshkey)
9
+ $c = LinuxContainer.new(name: 'linuxcontainergemtest', ssh_key_path: $sshkey)
10
+ $c.create
11
+ end
12
+
13
+ def test_state
14
+ assert_equal 'STOPPED', $c.state
15
+ end
16
+
17
+ def test_running
18
+ assert_equal false, $c.running?
19
+ end
20
+
21
+ def test_dir
22
+ assert_equal '/var/lib/lxc/linuxcontainergemtest/rootfs/flibble', $c.dir('flibble')
23
+ end
24
+
25
+ def test_list
26
+ assert_includes LinuxContainer.all.map(&:name), 'linuxcontainergemtest'
27
+ end
28
+
29
+ def test_ephemeral
30
+ assert($ec = $c.start_ephemeral)
31
+ assert_match /^linuxcontainergemtest.+/, $ec.name
32
+ assert($ec.wait_for { running? }, 'wait_for running?')
33
+ assert($ec.wait_for { ip }, 'wait_for ip')
34
+ assert($ec.wait_for { sshable? }, 'wait_for sshable?')
35
+ assert_equal "hi\n", $ec.execute('echo hi')
36
+ $ec.stop
37
+ assert($ec.wait_for { !running? }, 'wait_for !running?')
38
+ end
39
+ end
40
+
41
+
42
+
43
+ MiniTest::Unit.after_tests do
44
+ File.unlink($sshkey) rescue nil
45
+ ($ec.destroy '-f' if $ec) rescue nil
46
+ $c.destroy '-f'
47
+ end
48
+ TestLinuxContainer.startup
metadata ADDED
@@ -0,0 +1,61 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: linux_container
3
+ version: !ruby/object:Gem::Version
4
+ version: '0.1'
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Andrew Snow
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2013-02-08 00:00:00.000000000 Z
13
+ dependencies: []
14
+ description: Ruby gem for ephemeral LXC linux containers
15
+ email: andrew@modulus.org
16
+ executables: []
17
+ extensions: []
18
+ extra_rdoc_files:
19
+ - CHANGELOG
20
+ - README.md
21
+ - lib/linux_container.rb
22
+ files:
23
+ - CHANGELOG
24
+ - README.md
25
+ - Rakefile
26
+ - lib/linux_container.rb
27
+ - test/test_all.rb
28
+ - Manifest
29
+ - linux_container.gemspec
30
+ homepage: https://github.com/andys/linux_container
31
+ licenses: []
32
+ post_install_message:
33
+ rdoc_options:
34
+ - --line-numbers
35
+ - --inline-source
36
+ - --title
37
+ - Linux_container
38
+ - --main
39
+ - README.md
40
+ require_paths:
41
+ - lib
42
+ required_ruby_version: !ruby/object:Gem::Requirement
43
+ none: false
44
+ requirements:
45
+ - - ! '>='
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ required_rubygems_version: !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - ! '>='
52
+ - !ruby/object:Gem::Version
53
+ version: '1.2'
54
+ requirements: []
55
+ rubyforge_project: linux_container
56
+ rubygems_version: 1.8.25
57
+ signing_key:
58
+ specification_version: 3
59
+ summary: Ruby gem for ephemeral LXC linux containers
60
+ test_files:
61
+ - test/test_all.rb