lxc-ruby 0.2.3 → 0.3.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.
- checksums.yaml +7 -0
- data/.gitignore +2 -0
- data/.travis.yml +1 -0
- data/Gemfile +1 -1
- data/README.md +13 -16
- data/Vagrantfile +111 -0
- data/lib/lxc/configuration.rb +17 -2
- data/lib/lxc/container.rb +51 -45
- data/lib/lxc/shell.rb +7 -4
- data/lib/lxc/status.rb +22 -0
- data/lib/lxc/version.rb +1 -1
- data/lib/lxc.rb +60 -51
- data/{lxc.gemspec → lxc-ruby.gemspec} +4 -2
- data/spec/configuration_spec.rb +9 -9
- data/spec/container_spec.rb +80 -39
- data/spec/lxc_spec.rb +74 -63
- data/spec/spec_helper.rb +10 -1
- data/spec/status_spec.rb +33 -0
- metadata +33 -29
- data/lib/lxc/errors.rb +0 -5
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA1:
|
|
3
|
+
metadata.gz: 9b048cb271bb43ee64cf3759aa1916899b9c85e5
|
|
4
|
+
data.tar.gz: c91c3b7c6e0998b64eccf7e81f033781d997ff51
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: fc5eddbca427353c4f33dbcab6f559949dddd2472239488badebc54eae7d8ed831462832ba7e7b67ec8230fc8e7841094d549d418a55814448d05fd08ed16217
|
|
7
|
+
data.tar.gz: de0f0ad1dea5a915b1ceddc781aaf488e1b19194ef4143e6604bb7d5c901cf67231c8355415a791e91245f4d12ad945a833ba34a019fe9cf07f9e286a696fca1
|
data/.gitignore
CHANGED
data/.travis.yml
CHANGED
data/Gemfile
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
source
|
|
1
|
+
source 'https://rubygems.org'
|
|
2
2
|
gemspec
|
data/README.md
CHANGED
|
@@ -1,23 +1,17 @@
|
|
|
1
|
-
# LXC Ruby Wrapper
|
|
1
|
+
# LXC Ruby Wrapper [](http://travis-ci.org/sosedoff/lxc-ruby)
|
|
2
2
|
|
|
3
|
-
Ruby
|
|
4
|
-
|
|
5
|
-
## Build status
|
|
6
|
-
|
|
7
|
-
This library uses Travis-CI service for automated tests.
|
|
8
|
-
|
|
9
|
-
[](http://travis-ci.org/sosedoff/lxc-ruby)
|
|
3
|
+
Ruby library to integrate with [Linux Containers](http://lxc.sourceforge.net/) CLI tools
|
|
10
4
|
|
|
11
5
|
## Requirements
|
|
12
6
|
|
|
13
7
|
Supported LXC versions:
|
|
14
8
|
|
|
15
9
|
- 0.7.5
|
|
16
|
-
- 0.8.0
|
|
17
|
-
- 0.8.0-rc2 - in works
|
|
10
|
+
- 0.8.0
|
|
18
11
|
|
|
19
|
-
For testing purposes you can use [Vagrant](http://vagrantup.com/)
|
|
20
|
-
was tested on Ubuntu 11.
|
|
12
|
+
For testing purposes you can use [Vagrant](http://vagrantup.com/) with [VirtualBox](https://www.virtualbox.org/).
|
|
13
|
+
Most of the functionality was tested on 64-bit Ubuntu 11.10 and 12.04. Recommended version is 12.04.
|
|
14
|
+
Additional boxes could be found [here](http://www.vagrantbox.es/).
|
|
21
15
|
|
|
22
16
|
## Installation
|
|
23
17
|
|
|
@@ -64,10 +58,13 @@ Container instance is a simple abstaction for lxc's container tools:
|
|
|
64
58
|
c = LXC.container('foo')
|
|
65
59
|
|
|
66
60
|
# Get current status of container
|
|
67
|
-
c.
|
|
61
|
+
c.state # => 'running'
|
|
62
|
+
c.pid # => 1234
|
|
63
|
+
c.status # => <LXC::Status @state='running' @pid=123456>
|
|
68
64
|
|
|
69
65
|
# Check if container exists?
|
|
70
|
-
#
|
|
66
|
+
#
|
|
67
|
+
# This is needed since lxc does not raise any errors if container is
|
|
71
68
|
# not present in the system, and returns the same result as if container
|
|
72
69
|
# is actually stopped
|
|
73
70
|
c.exists? # => true
|
|
@@ -78,8 +75,8 @@ c.stopped? # => false
|
|
|
78
75
|
c.frozen? # => false
|
|
79
76
|
|
|
80
77
|
# Start and stop containers
|
|
81
|
-
c.start
|
|
82
|
-
c.stop
|
|
78
|
+
c.start # will be started in daemonized mode
|
|
79
|
+
c.stop
|
|
83
80
|
|
|
84
81
|
# Free and unfreeze (also returns current status)
|
|
85
82
|
c.freeze
|
data/Vagrantfile
ADDED
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
# -*- mode: ruby -*-
|
|
2
|
+
# vi: set ft=ruby :
|
|
3
|
+
|
|
4
|
+
Vagrant.configure("2") do |config|
|
|
5
|
+
# All Vagrant configuration is done here. The most common configuration
|
|
6
|
+
# options are documented and commented below. For a complete reference,
|
|
7
|
+
# please see the online documentation at vagrantup.com.
|
|
8
|
+
|
|
9
|
+
# Every Vagrant virtual environment requires a box to build off of.
|
|
10
|
+
config.vm.box = "precise64"
|
|
11
|
+
|
|
12
|
+
# The url from where the 'config.vm.box' box will be fetched if it
|
|
13
|
+
# doesn't already exist on the user's system.
|
|
14
|
+
config.vm.box_url = "http://files.vagrantup.com/precise64.box"
|
|
15
|
+
|
|
16
|
+
# Create a forwarded port mapping which allows access to a specific port
|
|
17
|
+
# within the machine from a port on the host machine. In the example below,
|
|
18
|
+
# accessing "localhost:8080" will access port 80 on the guest machine.
|
|
19
|
+
# config.vm.network :forwarded_port, guest: 80, host: 8080
|
|
20
|
+
|
|
21
|
+
# Create a private network, which allows host-only access to the machine
|
|
22
|
+
# using a specific IP.
|
|
23
|
+
config.vm.network :private_network, ip: "192.168.33.10"
|
|
24
|
+
|
|
25
|
+
# Create a public network, which generally matched to bridged network.
|
|
26
|
+
# Bridged networks make the machine appear as another physical device on
|
|
27
|
+
# your network.
|
|
28
|
+
# config.vm.network :public_network
|
|
29
|
+
|
|
30
|
+
# Share an additional folder to the guest VM. The first argument is
|
|
31
|
+
# the path on the host to the actual folder. The second argument is
|
|
32
|
+
# the path on the guest to mount the folder. And the optional third
|
|
33
|
+
# argument is a set of non-required options.
|
|
34
|
+
config.vm.synced_folder "./shared", "/shared"
|
|
35
|
+
|
|
36
|
+
# Provider-specific configuration so you can fine-tune various
|
|
37
|
+
# backing providers for Vagrant. These expose provider-specific options.
|
|
38
|
+
# Example for VirtualBox:
|
|
39
|
+
#
|
|
40
|
+
# config.vm.provider :virtualbox do |vb|
|
|
41
|
+
# # Don't boot with headless mode
|
|
42
|
+
# vb.gui = true
|
|
43
|
+
#
|
|
44
|
+
# # Use VBoxManage to customize the VM. For example to change memory:
|
|
45
|
+
# vb.customize ["modifyvm", :id, "--memory", "1024"]
|
|
46
|
+
# end
|
|
47
|
+
#
|
|
48
|
+
# View the documentation for the provider you're using for more
|
|
49
|
+
# information on available options.
|
|
50
|
+
|
|
51
|
+
# Enable provisioning with Puppet stand alone. Puppet manifests
|
|
52
|
+
# are contained in a directory path relative to this Vagrantfile.
|
|
53
|
+
# You will need to create the manifests directory and a manifest in
|
|
54
|
+
# the file base.pp in the manifests_path directory.
|
|
55
|
+
#
|
|
56
|
+
# An example Puppet manifest to provision the message of the day:
|
|
57
|
+
#
|
|
58
|
+
# # group { "puppet":
|
|
59
|
+
# # ensure => "present",
|
|
60
|
+
# # }
|
|
61
|
+
# #
|
|
62
|
+
# # File { owner => 0, group => 0, mode => 0644 }
|
|
63
|
+
# #
|
|
64
|
+
# # file { '/etc/motd':
|
|
65
|
+
# # content => "Welcome to your Vagrant-built virtual machine!
|
|
66
|
+
# # Managed by Puppet.\n"
|
|
67
|
+
# # }
|
|
68
|
+
#
|
|
69
|
+
# config.vm.provision :puppet do |puppet|
|
|
70
|
+
# puppet.manifests_path = "manifests"
|
|
71
|
+
# puppet.manifest_file = "base.pp"
|
|
72
|
+
# end
|
|
73
|
+
|
|
74
|
+
# Enable provisioning with chef solo, specifying a cookbooks path, roles
|
|
75
|
+
# path, and data_bags path (all relative to this Vagrantfile), and adding
|
|
76
|
+
# some recipes and/or roles.
|
|
77
|
+
#
|
|
78
|
+
# config.vm.provision :chef_solo do |chef|
|
|
79
|
+
# chef.cookbooks_path = "../my-recipes/cookbooks"
|
|
80
|
+
# chef.roles_path = "../my-recipes/roles"
|
|
81
|
+
# chef.data_bags_path = "../my-recipes/data_bags"
|
|
82
|
+
# chef.add_recipe "mysql"
|
|
83
|
+
# chef.add_role "web"
|
|
84
|
+
#
|
|
85
|
+
# # You may also specify custom JSON attributes:
|
|
86
|
+
# chef.json = { :mysql_password => "foo" }
|
|
87
|
+
# end
|
|
88
|
+
|
|
89
|
+
# Enable provisioning with chef server, specifying the chef server URL,
|
|
90
|
+
# and the path to the validation key (relative to this Vagrantfile).
|
|
91
|
+
#
|
|
92
|
+
# The Opscode Platform uses HTTPS. Substitute your organization for
|
|
93
|
+
# ORGNAME in the URL and validation key.
|
|
94
|
+
#
|
|
95
|
+
# If you have your own Chef Server, use the appropriate URL, which may be
|
|
96
|
+
# HTTP instead of HTTPS depending on your configuration. Also change the
|
|
97
|
+
# validation key to validation.pem.
|
|
98
|
+
#
|
|
99
|
+
# config.vm.provision :chef_client do |chef|
|
|
100
|
+
# chef.chef_server_url = "https://api.opscode.com/organizations/ORGNAME"
|
|
101
|
+
# chef.validation_key_path = "ORGNAME-validator.pem"
|
|
102
|
+
# end
|
|
103
|
+
#
|
|
104
|
+
# If you're using the Opscode platform, your validator client is
|
|
105
|
+
# ORGNAME-validator, replacing ORGNAME with your organization name.
|
|
106
|
+
#
|
|
107
|
+
# If you have your own Chef Server, the default validation client name is
|
|
108
|
+
# chef-validator, unless you changed the configuration.
|
|
109
|
+
#
|
|
110
|
+
# chef.validation_client_name = "ORGNAME-validator"
|
|
111
|
+
end
|
data/lib/lxc/configuration.rb
CHANGED
|
@@ -17,9 +17,11 @@ module LXC
|
|
|
17
17
|
# @return [LXC::Configuration]
|
|
18
18
|
def self.load_file(path)
|
|
19
19
|
fullpath = File.expand_path(path)
|
|
20
|
+
|
|
20
21
|
if !File.exists?(fullpath)
|
|
21
22
|
raise ArgumentError, "File '#{path}' does not exist."
|
|
22
23
|
end
|
|
24
|
+
|
|
23
25
|
LXC::Configuration.new(File.read(fullpath))
|
|
24
26
|
end
|
|
25
27
|
|
|
@@ -45,15 +47,20 @@ module LXC
|
|
|
45
47
|
def save_to_file(path)
|
|
46
48
|
fullpath = File.expand_path(path)
|
|
47
49
|
lines = []
|
|
50
|
+
|
|
48
51
|
@content.each_pair do |key,value|
|
|
49
52
|
k = "lxc.#{key.gsub('_', '.')}"
|
|
53
|
+
|
|
50
54
|
if value.kind_of?(Array)
|
|
51
55
|
lines << value.map { |v| "#{k} = #{v}" }
|
|
52
56
|
else
|
|
53
57
|
lines << "#{k} = #{value}"
|
|
54
58
|
end
|
|
55
59
|
end
|
|
56
|
-
|
|
60
|
+
|
|
61
|
+
File.open(path, 'w') do |f|
|
|
62
|
+
f.write(lines.flatten.join("\n"))
|
|
63
|
+
end
|
|
57
64
|
end
|
|
58
65
|
|
|
59
66
|
private
|
|
@@ -61,16 +68,24 @@ module LXC
|
|
|
61
68
|
def parse(data)
|
|
62
69
|
hash = {}
|
|
63
70
|
lines = data.split("\n").map(&:strip).select { |l| !l.empty? && l[0,1] != '#' }
|
|
71
|
+
|
|
64
72
|
lines.each do |l|
|
|
65
73
|
key,value = l.split('=').map(&:strip)
|
|
74
|
+
|
|
66
75
|
if !valid_option?(key)
|
|
67
76
|
raise ConfigurationError, "Invalid config attribute: #{key}."
|
|
68
77
|
end
|
|
78
|
+
|
|
69
79
|
key.gsub!(/^lxc\./, '').gsub!('.', '_')
|
|
80
|
+
|
|
70
81
|
hash[key] = [] if !hash.key?(key)
|
|
71
82
|
hash[key] << value
|
|
72
83
|
end
|
|
73
|
-
|
|
84
|
+
|
|
85
|
+
hash.each_pair do |k, v|
|
|
86
|
+
hash[k] = v.first if v.size == 1
|
|
87
|
+
end
|
|
88
|
+
|
|
74
89
|
hash
|
|
75
90
|
end
|
|
76
91
|
end
|
data/lib/lxc/container.rb
CHANGED
|
@@ -1,8 +1,6 @@
|
|
|
1
1
|
module LXC
|
|
2
2
|
class Container
|
|
3
3
|
attr_accessor :name
|
|
4
|
-
attr_reader :state
|
|
5
|
-
attr_reader :pid
|
|
6
4
|
|
|
7
5
|
# Initialize a new LXC::Container instance
|
|
8
6
|
# @param [String] name container name
|
|
@@ -14,17 +12,28 @@ module LXC
|
|
|
14
12
|
# Get container attributes hash
|
|
15
13
|
# @return [Hash]
|
|
16
14
|
def to_hash
|
|
17
|
-
status
|
|
18
|
-
{'name' => name, 'state' => state, 'pid' => pid}
|
|
15
|
+
status.to_hash.merge('name' => name)
|
|
19
16
|
end
|
|
20
17
|
|
|
21
18
|
# Get current status of container
|
|
22
19
|
# @return [Hash] hash with :state and :pid attributes
|
|
23
20
|
def status
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
21
|
+
output = run('info')
|
|
22
|
+
result = output.scan(/^state:\s+([\w]+)|pid:\s+(-?[\d]+)$/).flatten
|
|
23
|
+
|
|
24
|
+
LXC::Status.new(result.first, result.last)
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
# Get state of the container
|
|
28
|
+
# @return [String]
|
|
29
|
+
def state
|
|
30
|
+
status.state
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
# Get PID of the container
|
|
34
|
+
# @return [Integer]
|
|
35
|
+
def pid
|
|
36
|
+
status.pid
|
|
28
37
|
end
|
|
29
38
|
|
|
30
39
|
# Check if container exists
|
|
@@ -36,19 +45,19 @@ module LXC
|
|
|
36
45
|
# Check if container is running
|
|
37
46
|
# @return [Boolean]
|
|
38
47
|
def running?
|
|
39
|
-
status
|
|
48
|
+
status.state == 'running'
|
|
40
49
|
end
|
|
41
50
|
|
|
42
51
|
# Check if container is frozen
|
|
43
52
|
# @return [Boolean]
|
|
44
53
|
def frozen?
|
|
45
|
-
status
|
|
54
|
+
status.state == 'frozen'
|
|
46
55
|
end
|
|
47
56
|
|
|
48
|
-
# Check if container is stopped
|
|
57
|
+
# Check if container is stopped
|
|
49
58
|
# @return [Boolean]
|
|
50
59
|
def stopped?
|
|
51
|
-
exists? && status
|
|
60
|
+
exists? && status.state == 'stopped'
|
|
52
61
|
end
|
|
53
62
|
|
|
54
63
|
# Start container
|
|
@@ -68,8 +77,8 @@ module LXC
|
|
|
68
77
|
# Restart container
|
|
69
78
|
# @return [Hash] container status hash
|
|
70
79
|
def restart
|
|
71
|
-
stop
|
|
72
|
-
|
|
80
|
+
stop ; start
|
|
81
|
+
status
|
|
73
82
|
end
|
|
74
83
|
|
|
75
84
|
# Freeze container
|
|
@@ -87,9 +96,9 @@ module LXC
|
|
|
87
96
|
end
|
|
88
97
|
|
|
89
98
|
# Wait for container to change status
|
|
90
|
-
# @param [String] state
|
|
99
|
+
# @param [String] state name
|
|
91
100
|
def wait(state)
|
|
92
|
-
if !LXC::Shell.valid_state?(state)
|
|
101
|
+
if !LXC::Shell.valid_state?(status.state)
|
|
93
102
|
raise ArgumentError, "Invalid container state: #{state}"
|
|
94
103
|
end
|
|
95
104
|
|
|
@@ -111,14 +120,14 @@ module LXC
|
|
|
111
120
|
# Get container cpu shares
|
|
112
121
|
# @return [Integer]
|
|
113
122
|
def cpu_shares
|
|
114
|
-
result = run('cgroup', "cpu.shares").strip
|
|
123
|
+
result = run('cgroup', "cpu.shares").to_s.strip
|
|
115
124
|
result.empty? ? nil : result.to_i
|
|
116
125
|
end
|
|
117
126
|
|
|
118
127
|
# Get container cpu usage in seconds
|
|
119
128
|
# @return [Float]
|
|
120
129
|
def cpu_usage
|
|
121
|
-
result = run('cgroup', "cpuacct.usage").strip
|
|
130
|
+
result = run('cgroup', "cpuacct.usage").to_s.strip
|
|
122
131
|
result.empty? ? nil : Float('%.4f' % (result.to_i / 1E9))
|
|
123
132
|
end
|
|
124
133
|
|
|
@@ -149,7 +158,8 @@ module LXC
|
|
|
149
158
|
end
|
|
150
159
|
|
|
151
160
|
if !!path[:template]
|
|
152
|
-
|
|
161
|
+
template_dir = path[:template_dir] || '/usr/lib/lxc/templates'
|
|
162
|
+
template_path = File.join(template_dir,"lxc-#{path[:template]}")
|
|
153
163
|
unless File.exists?(template_path)
|
|
154
164
|
raise ArgumentError, "Template #{path[:template]} does not exist."
|
|
155
165
|
end
|
|
@@ -163,7 +173,7 @@ module LXC
|
|
|
163
173
|
exists?
|
|
164
174
|
else
|
|
165
175
|
raise ArgumentError, "File #{path} does not exist." unless File.exists?(path)
|
|
166
|
-
LXC.run('create', '-
|
|
176
|
+
LXC.run('create', '-f', path)
|
|
167
177
|
exists?
|
|
168
178
|
end
|
|
169
179
|
end
|
|
@@ -173,12 +183,13 @@ module LXC
|
|
|
173
183
|
# @return [LXC::Container] new container instance
|
|
174
184
|
def clone_to(target)
|
|
175
185
|
raise ContainerError, "Container does not exist." unless exists?
|
|
176
|
-
|
|
186
|
+
|
|
187
|
+
if LXC.container(target).exists?
|
|
177
188
|
raise ContainerError, "New container already exists."
|
|
178
189
|
end
|
|
179
190
|
|
|
180
191
|
LXC.run('clone', '-o', name, '-n', target)
|
|
181
|
-
|
|
192
|
+
LXC.container(target)
|
|
182
193
|
end
|
|
183
194
|
|
|
184
195
|
# Create a new container from an existing container
|
|
@@ -186,11 +197,12 @@ module LXC
|
|
|
186
197
|
# @return [Boolean]
|
|
187
198
|
def clone_from(source)
|
|
188
199
|
raise ContainerError, "Container already exists." if exists?
|
|
189
|
-
|
|
200
|
+
|
|
201
|
+
unless LXC.container(source).exists?
|
|
190
202
|
raise ContainerError, "Source container does not exist."
|
|
191
203
|
end
|
|
192
204
|
|
|
193
|
-
|
|
205
|
+
run('clone', '-o', source)
|
|
194
206
|
exists?
|
|
195
207
|
end
|
|
196
208
|
|
|
@@ -202,18 +214,19 @@ module LXC
|
|
|
202
214
|
# it will be stopped first. Otherwise it will raise exception.
|
|
203
215
|
#
|
|
204
216
|
def destroy(force=false)
|
|
205
|
-
|
|
217
|
+
unless exists?
|
|
218
|
+
raise ContainerError, "Container does not exist."
|
|
219
|
+
end
|
|
206
220
|
|
|
207
221
|
if running?
|
|
208
|
-
if force
|
|
209
|
-
|
|
210
|
-
run('destroy', '-f')
|
|
222
|
+
if force == true
|
|
223
|
+
stop
|
|
211
224
|
else
|
|
212
225
|
raise ContainerError, "Container is running. Stop it first or use force=true"
|
|
213
|
-
end
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
226
|
+
end
|
|
227
|
+
end
|
|
228
|
+
|
|
229
|
+
run('destroy')
|
|
217
230
|
|
|
218
231
|
!exists?
|
|
219
232
|
end
|
|
@@ -228,20 +241,13 @@ module LXC
|
|
|
228
241
|
chunks = line.split(' ')
|
|
229
242
|
chunks.delete_at(0)
|
|
230
243
|
|
|
231
|
-
pid = chunks.shift
|
|
232
|
-
user = chunks.shift
|
|
233
|
-
cpu = chunks.shift
|
|
234
|
-
mem = chunks.shift
|
|
235
|
-
command = chunks.shift
|
|
236
|
-
args = chunks.join(' ')
|
|
237
|
-
|
|
238
244
|
{
|
|
239
|
-
'pid' =>
|
|
240
|
-
'user' =>
|
|
241
|
-
'cpu' =>
|
|
242
|
-
'memory' =>
|
|
243
|
-
'command' =>
|
|
244
|
-
'args' =>
|
|
245
|
+
'pid' => chunks.shift,
|
|
246
|
+
'user' => chunks.shift,
|
|
247
|
+
'cpu' => chunks.shift,
|
|
248
|
+
'memory' => chunks.shift,
|
|
249
|
+
'command' => chunks.shift,
|
|
250
|
+
'args' => chunks.join(' ')
|
|
245
251
|
}
|
|
246
252
|
end
|
|
247
253
|
end
|
data/lib/lxc/shell.rb
CHANGED
|
@@ -78,15 +78,18 @@ module LXC
|
|
|
78
78
|
# provide a block that returns string
|
|
79
79
|
def run(command, *args)
|
|
80
80
|
command_name = "lxc-#{command}"
|
|
81
|
+
|
|
81
82
|
unless BIN_FILES.include?(command_name)
|
|
82
83
|
raise ArgumentError, "Invalid command: #{command_name}."
|
|
83
84
|
end
|
|
84
85
|
|
|
85
86
|
cmd = ""
|
|
86
|
-
cmd
|
|
87
|
-
cmd
|
|
88
|
-
cmd
|
|
89
|
-
|
|
87
|
+
cmd << "sudo " if use_sudo == true
|
|
88
|
+
cmd << "#{command_name} #{args.join(' ')}".strip
|
|
89
|
+
cmd << " | #{yield}" if block_given?
|
|
90
|
+
|
|
91
|
+
child = POSIX::Spawn::Child.new(cmd.strip)
|
|
92
|
+
child.out
|
|
90
93
|
end
|
|
91
94
|
end
|
|
92
95
|
end
|
data/lib/lxc/status.rb
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
module LXC
|
|
2
|
+
class Status
|
|
3
|
+
attr_reader :state, :pid
|
|
4
|
+
|
|
5
|
+
def initialize(state, pid)
|
|
6
|
+
@state = state.to_s.downcase
|
|
7
|
+
@pid = Integer(pid)
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def == (instance)
|
|
11
|
+
instance.pid == pid && instance.state == state
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def to_hash
|
|
15
|
+
{'state' => state, 'pid' => pid}
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def to_s
|
|
19
|
+
"state=#{state} pid=#{pid}"
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
data/lib/lxc/version.rb
CHANGED
data/lib/lxc.rb
CHANGED
|
@@ -1,58 +1,67 @@
|
|
|
1
|
+
require 'posix/spawn'
|
|
1
2
|
require 'lxc/version'
|
|
2
|
-
require 'lxc/errors'
|
|
3
|
-
require 'lxc/shell'
|
|
4
|
-
require 'lxc/configuration_options'
|
|
5
|
-
require 'lxc/configuration'
|
|
6
|
-
require 'lxc/container'
|
|
7
3
|
|
|
8
4
|
module LXC
|
|
5
|
+
class Error < StandardError ; end
|
|
6
|
+
class ContainerError < Error ; end
|
|
7
|
+
class ConfigurationError < Error ; end
|
|
8
|
+
|
|
9
|
+
autoload :Shell, 'lxc/shell'
|
|
10
|
+
autoload :Configuration, 'lxc/configuration'
|
|
11
|
+
autoload :ConfigurationOptions, 'lxc/configuration_options'
|
|
12
|
+
autoload :Container, 'lxc/container'
|
|
13
|
+
autoload :Status, 'lxc/status'
|
|
14
|
+
|
|
9
15
|
class << self
|
|
10
16
|
include LXC::Shell
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
# Check if binary file is installed
|
|
20
|
+
# @param [String] binary filename
|
|
21
|
+
# @return [Boolean] true if installed
|
|
22
|
+
def self.binary_installed?(name)
|
|
23
|
+
path = File.join(LXC::Shell::BIN_PREFIX, name)
|
|
24
|
+
File.exists?(path)
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
# Check if all binaries are present in the system
|
|
28
|
+
# @return [Boolean] true if binary files are found
|
|
29
|
+
def self.installed?
|
|
30
|
+
LXC::Shell::BIN_FILES.all?{ |f| binary_installed?(f) }
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
# Get LXC configuration info
|
|
34
|
+
# @return [Hash] hash containing config groups
|
|
35
|
+
def self.config
|
|
36
|
+
str = LXC.run('checkconfig') { LXC::Shell::REMOVE_COLORS }
|
|
37
|
+
|
|
38
|
+
data = str.scan(/^([\w\s]+): (enabled|disabled)$/).map { |r|
|
|
39
|
+
[r.first.downcase.gsub(' ', '_'), r.last == 'enabled']
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
Hash[data]
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
# Get a single container instance
|
|
46
|
+
# @param [String] name of the container
|
|
47
|
+
# @return [LXC::Container] container instance
|
|
48
|
+
def self.container(name)
|
|
49
|
+
LXC::Container.new(name)
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
# Get a list of all available containers
|
|
53
|
+
# @param [String] select containers that match string
|
|
54
|
+
# @return [Array] array of LXC::Containers
|
|
55
|
+
def self.containers(filter=nil)
|
|
56
|
+
names = LXC.run('ls').split("\n").uniq
|
|
57
|
+
|
|
58
|
+
names.delete_if { |v| !v.include?(filter) } if filter.kind_of?(String)
|
|
59
|
+
names.map { |name| LXC::Container.new(name) }
|
|
60
|
+
end
|
|
11
61
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
path = File.join(LXC::Shell::BIN_PREFIX, name)
|
|
17
|
-
File.exists?(path)
|
|
18
|
-
end
|
|
19
|
-
|
|
20
|
-
# Check if all binaries are present in the system
|
|
21
|
-
# @return [Boolean] true if binary files are found
|
|
22
|
-
def installed?
|
|
23
|
-
!BIN_FILES.map { |f| binary_installed?(f) }.uniq.include?(false)
|
|
24
|
-
end
|
|
25
|
-
|
|
26
|
-
# Get LXC configuration info
|
|
27
|
-
# @return [Hash] hash containing config groups
|
|
28
|
-
def config
|
|
29
|
-
str = LXC.run('checkconfig') { LXC::Shell::REMOVE_COLORS }
|
|
30
|
-
data = str.scan(/^([\w\s]+): (enabled|disabled)$/).map { |r|
|
|
31
|
-
[r.first.downcase.gsub(' ', '_'), r.last == 'enabled']
|
|
32
|
-
}
|
|
33
|
-
Hash[data]
|
|
34
|
-
end
|
|
35
|
-
|
|
36
|
-
# Get container information record
|
|
37
|
-
# @param [name] container name
|
|
38
|
-
# @return [LXC::Container] container instance
|
|
39
|
-
def container(name)
|
|
40
|
-
LXC::Container.new(name)
|
|
41
|
-
end
|
|
42
|
-
|
|
43
|
-
# Get a list of all available containers
|
|
44
|
-
# @param [String] select containers that match string
|
|
45
|
-
# @return [Array] array of LXC::Containers
|
|
46
|
-
def containers(filter=nil)
|
|
47
|
-
names = LXC.run('ls').split("\n").uniq
|
|
48
|
-
names.delete_if { |v| !v.include?(filter) } if filter.kind_of?(String)
|
|
49
|
-
names.map { |name| Container.new(name) }
|
|
50
|
-
end
|
|
51
|
-
|
|
52
|
-
# Get current LXC version
|
|
53
|
-
# @return [String] current LXC version
|
|
54
|
-
def version
|
|
55
|
-
LXC.run('version').strip.split(' ').last
|
|
56
|
-
end
|
|
62
|
+
# Get currently installeded LXC version
|
|
63
|
+
# @return [String] current LXC version
|
|
64
|
+
def self.version
|
|
65
|
+
LXC.run('version').strip.split(' ').last
|
|
57
66
|
end
|
|
58
|
-
end
|
|
67
|
+
end
|
|
@@ -8,10 +8,12 @@ Gem::Specification.new do |s|
|
|
|
8
8
|
s.homepage = "http://github.com/sosedoff/lxc-ruby"
|
|
9
9
|
s.authors = ["Dan Sosedoff"]
|
|
10
10
|
s.email = ["dan.sosedoff@gmail.com"]
|
|
11
|
+
|
|
12
|
+
s.add_dependency 'posix-spawn', '~> 0.3.6'
|
|
11
13
|
|
|
12
14
|
s.add_development_dependency 'rake'
|
|
13
|
-
s.add_development_dependency 'rspec', '~> 2.
|
|
14
|
-
s.add_development_dependency 'simplecov', '~> 0.
|
|
15
|
+
s.add_development_dependency 'rspec', '~> 2.13'
|
|
16
|
+
s.add_development_dependency 'simplecov', '~> 0.7'
|
|
15
17
|
|
|
16
18
|
s.files = `git ls-files`.split("\n")
|
|
17
19
|
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
data/spec/configuration_spec.rb
CHANGED
|
@@ -3,13 +3,13 @@ require 'spec_helper'
|
|
|
3
3
|
describe LXC::Configuration do
|
|
4
4
|
it 'parses config data' do
|
|
5
5
|
conf = LXC::Configuration.new(fixture('configuration.txt'))
|
|
6
|
-
conf.content.
|
|
7
|
-
conf.attributes.
|
|
8
|
-
conf.attributes.
|
|
9
|
-
conf.utsname.
|
|
10
|
-
conf['utsname'].
|
|
11
|
-
conf[:utsname].
|
|
12
|
-
conf.network_ipv4.
|
|
6
|
+
expect(conf.content).to be_a Hash
|
|
7
|
+
expect(conf.attributes).to be_an Array
|
|
8
|
+
expect(conf.attributes).to_not be_empty
|
|
9
|
+
expect(conf.utsname).to_not be_nil
|
|
10
|
+
expect(conf['utsname']).to_not be_nil
|
|
11
|
+
expect(conf[:utsname]).to_not be_nil
|
|
12
|
+
expect(conf.network_ipv4).to_not be_nil
|
|
13
13
|
end
|
|
14
14
|
|
|
15
15
|
it 'saves config data into file' do
|
|
@@ -17,6 +17,6 @@ describe LXC::Configuration do
|
|
|
17
17
|
conf.save_to_file('/tmp/lxc.txt')
|
|
18
18
|
c1 = LXC::Configuration.new(fixture('configuration.txt'))
|
|
19
19
|
c2 = LXC::Configuration.new(File.read('/tmp/lxc.txt'))
|
|
20
|
-
c1.content.
|
|
20
|
+
expect(c1.content).to eq(c2.content)
|
|
21
21
|
end
|
|
22
|
-
end
|
|
22
|
+
end
|
data/spec/container_spec.rb
CHANGED
|
@@ -3,28 +3,29 @@ require 'spec_helper'
|
|
|
3
3
|
describe LXC::Container do
|
|
4
4
|
subject { LXC::Container.new('app') }
|
|
5
5
|
|
|
6
|
-
it { should respond_to
|
|
7
|
-
it { should respond_to
|
|
8
|
-
it { should respond_to
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
6
|
+
it { should respond_to :name }
|
|
7
|
+
it { should respond_to :status }
|
|
8
|
+
it { should respond_to :state }
|
|
9
|
+
it { should respond_to :pid }
|
|
10
|
+
|
|
11
|
+
describe '#name' do
|
|
12
|
+
it 'should be set to "app"' do
|
|
13
|
+
expect(subject.name).to eq 'app'
|
|
14
|
+
end
|
|
14
15
|
end
|
|
15
16
|
|
|
16
17
|
describe '#exists?' do
|
|
17
18
|
context 'for existing container' do
|
|
18
19
|
it 'returns true' do
|
|
19
20
|
stub_lxc('ls') { "app\napp2" }
|
|
20
|
-
subject.
|
|
21
|
+
expect(subject).to exist
|
|
21
22
|
end
|
|
22
23
|
end
|
|
23
24
|
|
|
24
25
|
context 'for non-existing container' do
|
|
25
26
|
it 'returns false' do
|
|
26
27
|
stub_lxc('ls') { "app2\napp3" }
|
|
27
|
-
subject.
|
|
28
|
+
expect(subject).to_not exist
|
|
28
29
|
end
|
|
29
30
|
end
|
|
30
31
|
end
|
|
@@ -32,41 +33,60 @@ describe LXC::Container do
|
|
|
32
33
|
describe '#status' do
|
|
33
34
|
it 'returns STOPPED' do
|
|
34
35
|
stub_lxc('info', '-n', 'app') { fixture('lxc-info-stopped.txt') }
|
|
35
|
-
subject.status.
|
|
36
|
+
expect(subject.status).to eq LXC::Status.new('STOPPED', '-1')
|
|
36
37
|
end
|
|
37
38
|
|
|
38
39
|
it 'returns RUNNING' do
|
|
39
40
|
stub_lxc('info', '-n', 'app') { fixture('lxc-info-running.txt') }
|
|
40
|
-
subject.status.
|
|
41
|
+
expect(subject.status).to eq LXC::Status.new('RUNNING', '2125')
|
|
41
42
|
end
|
|
42
43
|
end
|
|
43
44
|
|
|
44
45
|
describe '#destroy' do
|
|
45
46
|
it 'raises error if container does not exist' do
|
|
46
47
|
stub_lxc('ls') { "app2" }
|
|
47
|
-
|
|
48
|
-
|
|
48
|
+
|
|
49
|
+
expect {
|
|
50
|
+
subject.destroy
|
|
51
|
+
}.to raise_error LXC::ContainerError, "Container does not exist."
|
|
49
52
|
end
|
|
50
53
|
|
|
51
54
|
it 'raises error if container is running' do
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
55
|
+
subject.stub(:exists?).and_return(true)
|
|
56
|
+
subject.stub(:running?).and_return(true)
|
|
57
|
+
|
|
58
|
+
expect {
|
|
59
|
+
subject.destroy
|
|
60
|
+
}.to raise_error LXC::ContainerError, "Container is running. Stop it first or use force=true"
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
context 'with force=true' do
|
|
64
|
+
before do
|
|
65
|
+
stub_lxc('ls') { 'app' }
|
|
66
|
+
stub_lxc('info', '-n', 'app') { fixture 'lxc-info-running.txt' }
|
|
67
|
+
stub_lxc('stop', '-n', 'app') { '' }
|
|
68
|
+
stub_lxc('info', '-n', 'app') { fixture 'lxc-info-stopped.txt' }
|
|
69
|
+
stub_lxc('destroy', '-n', 'app') { '' }
|
|
70
|
+
stub_lxc('ls') { '' }
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
it 'stops and destroys container' do
|
|
74
|
+
expect(subject.destroy(true)).to be_true
|
|
75
|
+
end
|
|
56
76
|
end
|
|
57
77
|
end
|
|
58
78
|
|
|
59
79
|
describe '#memory_usage' do
|
|
60
80
|
it 'returns the amount of used memory' do
|
|
61
81
|
stub_lxc('cgroup', '-n', 'app', 'memory.usage_in_bytes') { "3280896\n" }
|
|
62
|
-
subject.memory_usage.
|
|
82
|
+
expect(subject.memory_usage).to eq(3280896)
|
|
63
83
|
end
|
|
64
84
|
end
|
|
65
85
|
|
|
66
86
|
describe '#memory_limit' do
|
|
67
87
|
it 'returns the memory limit' do
|
|
68
88
|
stub_lxc('cgroup', '-n', 'app', 'memory.limit_in_bytes') { "268435456\n" }
|
|
69
|
-
subject.memory_limit.
|
|
89
|
+
expect(subject.memory_limit).to eq(268435456)
|
|
70
90
|
end
|
|
71
91
|
end
|
|
72
92
|
|
|
@@ -74,8 +94,9 @@ describe LXC::Container do
|
|
|
74
94
|
it 'raises error if container is not running' do
|
|
75
95
|
stub_lxc('info', '-n', 'app') { fixture('lxc-info-stopped.txt') }
|
|
76
96
|
|
|
77
|
-
|
|
78
|
-
|
|
97
|
+
expect {
|
|
98
|
+
subject.processes
|
|
99
|
+
}.to raise_error LXC::ContainerError, "Container is not running"
|
|
79
100
|
end
|
|
80
101
|
|
|
81
102
|
it 'returns list of all processes' do
|
|
@@ -83,16 +104,16 @@ describe LXC::Container do
|
|
|
83
104
|
stub_lxc('ps', '-n', 'app', '--', '-eo pid,user,%cpu,%mem,args') { fixture('lxc-ps-aux.txt') }
|
|
84
105
|
|
|
85
106
|
list = subject.processes
|
|
86
|
-
list.
|
|
107
|
+
expect(list).to be_an Array
|
|
87
108
|
|
|
88
109
|
p = list.first
|
|
89
|
-
p.
|
|
90
|
-
p.
|
|
91
|
-
p.
|
|
92
|
-
p.
|
|
93
|
-
p.
|
|
94
|
-
p.
|
|
95
|
-
p.
|
|
110
|
+
expect(p).to be_a Hash
|
|
111
|
+
expect(p).to have_key('pid')
|
|
112
|
+
expect(p).to have_key('user')
|
|
113
|
+
expect(p).to have_key('cpu')
|
|
114
|
+
expect(p).to have_key('memory')
|
|
115
|
+
expect(p).to have_key('command')
|
|
116
|
+
expect(p).to have_key('args')
|
|
96
117
|
end
|
|
97
118
|
end
|
|
98
119
|
|
|
@@ -100,7 +121,7 @@ describe LXC::Container do
|
|
|
100
121
|
context 'when container does not exist' do
|
|
101
122
|
it 'returns false' do
|
|
102
123
|
stub_lxc('ls') { "foo-app" }
|
|
103
|
-
subject.
|
|
124
|
+
expect(subject).to_not be_stopped
|
|
104
125
|
end
|
|
105
126
|
end
|
|
106
127
|
|
|
@@ -109,14 +130,14 @@ describe LXC::Container do
|
|
|
109
130
|
stub_lxc('ls') { 'app' }
|
|
110
131
|
stub_lxc('info', '-n', 'app') { fixture('lxc-info-stopped.txt') }
|
|
111
132
|
|
|
112
|
-
subject.
|
|
133
|
+
expect(subject).to be_stopped
|
|
113
134
|
end
|
|
114
135
|
|
|
115
136
|
it 'returns false if running' do
|
|
116
137
|
stub_lxc('ls') { 'app' }
|
|
117
138
|
stub_lxc('info', '-n', 'app') { fixture('lxc-info-running.txt') }
|
|
118
139
|
|
|
119
|
-
subject.
|
|
140
|
+
expect(subject).to_not be_stopped
|
|
120
141
|
end
|
|
121
142
|
end
|
|
122
143
|
end
|
|
@@ -128,7 +149,7 @@ describe LXC::Container do
|
|
|
128
149
|
end
|
|
129
150
|
|
|
130
151
|
it 'returns cpu shares value' do
|
|
131
|
-
subject.cpu_shares.
|
|
152
|
+
expect(subject.cpu_shares).to eq 1024
|
|
132
153
|
end
|
|
133
154
|
end
|
|
134
155
|
|
|
@@ -138,7 +159,17 @@ describe LXC::Container do
|
|
|
138
159
|
end
|
|
139
160
|
|
|
140
161
|
it 'returns nil' do
|
|
141
|
-
subject.cpu_shares.
|
|
162
|
+
expect(subject.cpu_shares).to be_nil
|
|
163
|
+
end
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
context 'when run command is nil' do
|
|
167
|
+
before do
|
|
168
|
+
subject.stub(:run).and_return(nil)
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
it 'should return nil' do
|
|
172
|
+
expect(subject.cpu_shares).to be_nil
|
|
142
173
|
end
|
|
143
174
|
end
|
|
144
175
|
end
|
|
@@ -150,7 +181,7 @@ describe LXC::Container do
|
|
|
150
181
|
end
|
|
151
182
|
|
|
152
183
|
it 'returns usage in seconds' do
|
|
153
|
-
subject.cpu_usage.
|
|
184
|
+
expect(subject.cpu_usage).to eq 4239.0819
|
|
154
185
|
end
|
|
155
186
|
end
|
|
156
187
|
|
|
@@ -160,7 +191,17 @@ describe LXC::Container do
|
|
|
160
191
|
end
|
|
161
192
|
|
|
162
193
|
it 'returns nil' do
|
|
163
|
-
subject.cpu_usage.
|
|
194
|
+
expect(subject.cpu_usage).to be_nil
|
|
195
|
+
end
|
|
196
|
+
end
|
|
197
|
+
|
|
198
|
+
context 'when run command is nil' do
|
|
199
|
+
before do
|
|
200
|
+
subject.stub(:run).and_return(nil)
|
|
201
|
+
end
|
|
202
|
+
|
|
203
|
+
it 'should return nil' do
|
|
204
|
+
expect(subject.cpu_usage).to be_nil
|
|
164
205
|
end
|
|
165
206
|
end
|
|
166
207
|
end
|
|
@@ -178,7 +219,7 @@ describe LXC::Container do
|
|
|
178
219
|
|
|
179
220
|
it 'executes a command with container name' do
|
|
180
221
|
stub_lxc("info", "-n", "app") { "info" }
|
|
181
|
-
subject.info.
|
|
222
|
+
expect(subject.info).to eq "info"
|
|
182
223
|
end
|
|
183
224
|
end
|
|
184
|
-
end
|
|
225
|
+
end
|
data/spec/lxc_spec.rb
CHANGED
|
@@ -18,101 +18,112 @@ describe LXC do
|
|
|
18
18
|
end
|
|
19
19
|
|
|
20
20
|
it 'returns true if all files are found' do
|
|
21
|
-
LXC.installed
|
|
21
|
+
expect(LXC.installed?).to be_true
|
|
22
22
|
end
|
|
23
23
|
|
|
24
24
|
it 'returns false on missing files' do
|
|
25
25
|
FileUtils.rm("/tmp/lxc/lxc-version")
|
|
26
|
-
LXC.installed
|
|
26
|
+
expect(LXC.installed?).to be_false
|
|
27
27
|
end
|
|
28
28
|
end
|
|
29
29
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
30
|
+
describe '.version' do
|
|
31
|
+
it 'returns installed LXC version' do
|
|
32
|
+
stub_lxc('version') { fixture('lxc-version.txt') }
|
|
33
|
+
expect(LXC.version).to eq('0.7.5')
|
|
34
|
+
end
|
|
33
35
|
end
|
|
34
36
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
37
|
+
describe '.config' do
|
|
38
|
+
it 'returns config hash with attributes' do
|
|
39
|
+
stub_lxc('checkconfig') { fixture('lxc-checkconfig.txt') }
|
|
40
|
+
|
|
41
|
+
info = LXC.config
|
|
42
|
+
expect(info).to be_a(Hash)
|
|
43
|
+
|
|
44
|
+
expect(info['namespaces']).to be_true
|
|
45
|
+
expect(info['utsname_namespace']).to be_true
|
|
46
|
+
expect(info['ipc_namespace']).to be_true
|
|
47
|
+
expect(info['pid_namespace']).to be_true
|
|
48
|
+
expect(info['user_namespace']).to be_true
|
|
49
|
+
expect(info['network_namespace']).to be_true
|
|
50
|
+
expect(info['cgroup']).to be_true
|
|
51
|
+
expect(info['cgroup_clone_children_flag']).to be_true
|
|
52
|
+
expect(info['cgroup_device']).to be_true
|
|
53
|
+
expect(info['cgroup_sched']).to be_true
|
|
54
|
+
expect(info['cgroup_cpu_account']).to be_true
|
|
55
|
+
expect(info['cgroup_memory_controller']).to be_true
|
|
56
|
+
expect(info['cgroup_cpuset']).to be_true
|
|
57
|
+
expect(info['veth_pair_device']).to be_true
|
|
58
|
+
expect(info['macvlan']).to be_true
|
|
59
|
+
expect(info['vlan']).to be_true
|
|
60
|
+
expect(info['file_capabilities']).to be_true
|
|
61
|
+
end
|
|
58
62
|
end
|
|
59
63
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
c.name.should eq('foo')
|
|
64
|
-
end
|
|
64
|
+
describe '.container' do
|
|
65
|
+
it 'returns a container for name' do
|
|
66
|
+
c = LXC.container('foo')
|
|
65
67
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
list.should be_an Array
|
|
70
|
-
list.size.should eq(2)
|
|
71
|
-
list.first.should be_a LXC::Container
|
|
72
|
-
list.first.name.should eq('vm0')
|
|
68
|
+
expect(c).to be_a LXC::Container
|
|
69
|
+
expect(c.name).to eq('foo')
|
|
70
|
+
end
|
|
73
71
|
end
|
|
74
72
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
73
|
+
describe '.containers' do
|
|
74
|
+
it 'returns all available containers' do
|
|
75
|
+
stub_lxc('ls') { "vm0\nvm1\nvm0" }
|
|
76
|
+
|
|
77
|
+
list = LXC.containers
|
|
78
|
+
expect(list).to be_an Array
|
|
79
|
+
expect(list.size).to eq(2)
|
|
80
|
+
expect(list.first).to be_a LXC::Container
|
|
81
|
+
expect(list.first.name).to eq('vm0')
|
|
82
|
+
end
|
|
80
83
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
+
context 'with argument' do
|
|
85
|
+
it 'returns containers filtered by name' do
|
|
86
|
+
stub_lxc('ls') { "vm0\nvm1\nfoo\n"}
|
|
87
|
+
|
|
88
|
+
list = LXC.containers("vm")
|
|
89
|
+
expect(list.size).to eq(2)
|
|
90
|
+
end
|
|
84
91
|
end
|
|
92
|
+
end
|
|
85
93
|
|
|
86
|
-
|
|
87
|
-
|
|
94
|
+
describe '.sudo' do
|
|
95
|
+
before { LXC.use_sudo = true }
|
|
96
|
+
let(:result) do
|
|
97
|
+
klass = Struct.new(:out)
|
|
98
|
+
klass.new(fixture('lxc-version.txt'))
|
|
88
99
|
end
|
|
89
100
|
|
|
90
101
|
it 'executes command using sudo' do
|
|
91
|
-
LXC.use_sudo.
|
|
102
|
+
expect(LXC.use_sudo).to be_true
|
|
103
|
+
|
|
104
|
+
POSIX::Spawn::Child.stub(:new).
|
|
105
|
+
with('sudo lxc-version').
|
|
106
|
+
and_return(result)
|
|
92
107
|
|
|
93
|
-
|
|
94
|
-
bar.should_receive(:'`').with('sudo lxc-version').and_return(fixture('lxc-version.txt'))
|
|
95
|
-
bar.run('version').should_not be_empty
|
|
108
|
+
expect(LXC.run('version')).to eq 'lxc version: 0.7.5'
|
|
96
109
|
end
|
|
97
110
|
end
|
|
98
111
|
|
|
99
|
-
|
|
100
|
-
class Bar
|
|
101
|
-
include LXC::Shell
|
|
102
|
-
end
|
|
112
|
+
describe '.use_sudo' do
|
|
113
|
+
class Bar ; include LXC::Shell ; end
|
|
103
114
|
|
|
104
115
|
it 'should be true' do
|
|
105
116
|
foo = Bar.new
|
|
106
|
-
foo.use_sudo.
|
|
107
|
-
LXC.use_sudo.
|
|
117
|
+
expect(foo.use_sudo).to be_true
|
|
118
|
+
expect(LXC.use_sudo).to be_true
|
|
108
119
|
end
|
|
109
120
|
|
|
110
121
|
it 'should be false' do
|
|
111
122
|
LXC.use_sudo = false
|
|
112
123
|
foo = Bar.new
|
|
113
124
|
|
|
114
|
-
LXC.use_sudo.
|
|
115
|
-
foo.use_sudo.
|
|
125
|
+
expect(LXC.use_sudo).to be_false
|
|
126
|
+
expect(foo.use_sudo).to be_false
|
|
116
127
|
end
|
|
117
128
|
end
|
|
118
|
-
end
|
|
129
|
+
end
|
data/spec/spec_helper.rb
CHANGED
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
$:.unshift File.expand_path("../..", __FILE__)
|
|
2
2
|
|
|
3
|
+
require 'simplecov'
|
|
4
|
+
SimpleCov.start do
|
|
5
|
+
add_filter "/.bundle/"
|
|
6
|
+
end
|
|
7
|
+
|
|
3
8
|
require 'lib/lxc'
|
|
4
9
|
|
|
5
10
|
def fixture_path(filename=nil)
|
|
@@ -13,5 +18,9 @@ end
|
|
|
13
18
|
|
|
14
19
|
def stub_lxc(command, *args)
|
|
15
20
|
output = yield
|
|
16
|
-
|
|
21
|
+
|
|
22
|
+
LXC.should_receive(:run).
|
|
23
|
+
exactly(1).times.
|
|
24
|
+
with(command, *args).
|
|
25
|
+
and_return(output)
|
|
17
26
|
end
|
data/spec/status_spec.rb
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
|
|
3
|
+
describe LXC::Status do
|
|
4
|
+
describe '.new' do
|
|
5
|
+
let(:status) { LXC::Status.new('RUNNING', '12345') }
|
|
6
|
+
|
|
7
|
+
it 'makes state downcase' do
|
|
8
|
+
expect(status.state).to eq 'running'
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
it 'converts given pid into integer' do
|
|
12
|
+
expect(status.pid).to be_an Integer
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
describe '#to_hash' do
|
|
17
|
+
let(:status) { LXC::Status.new('RUNNING', '12345') }
|
|
18
|
+
|
|
19
|
+
it 'returns a hash representation' do
|
|
20
|
+
result = status.to_hash
|
|
21
|
+
expect(result).to be_a Hash
|
|
22
|
+
expect(result).to include 'state', 'pid'
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
describe '#to_s' do
|
|
27
|
+
let(:status) { LXC::Status.new('RUNNING', '12345') }
|
|
28
|
+
|
|
29
|
+
it 'returns a string representation' do
|
|
30
|
+
expect(status.to_s).to eq 'state=running pid=12345'
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
metadata
CHANGED
|
@@ -1,64 +1,71 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: lxc-ruby
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
5
|
-
prerelease:
|
|
4
|
+
version: 0.3.1
|
|
6
5
|
platform: ruby
|
|
7
6
|
authors:
|
|
8
7
|
- Dan Sosedoff
|
|
9
8
|
autorequire:
|
|
10
9
|
bindir: bin
|
|
11
10
|
cert_chain: []
|
|
12
|
-
date: 2013-
|
|
11
|
+
date: 2013-09-22 00:00:00.000000000 Z
|
|
13
12
|
dependencies:
|
|
13
|
+
- !ruby/object:Gem::Dependency
|
|
14
|
+
name: posix-spawn
|
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
|
16
|
+
requirements:
|
|
17
|
+
- - ~>
|
|
18
|
+
- !ruby/object:Gem::Version
|
|
19
|
+
version: 0.3.6
|
|
20
|
+
type: :runtime
|
|
21
|
+
prerelease: false
|
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
23
|
+
requirements:
|
|
24
|
+
- - ~>
|
|
25
|
+
- !ruby/object:Gem::Version
|
|
26
|
+
version: 0.3.6
|
|
14
27
|
- !ruby/object:Gem::Dependency
|
|
15
28
|
name: rake
|
|
16
29
|
requirement: !ruby/object:Gem::Requirement
|
|
17
|
-
none: false
|
|
18
30
|
requirements:
|
|
19
|
-
- -
|
|
31
|
+
- - '>='
|
|
20
32
|
- !ruby/object:Gem::Version
|
|
21
33
|
version: '0'
|
|
22
34
|
type: :development
|
|
23
35
|
prerelease: false
|
|
24
36
|
version_requirements: !ruby/object:Gem::Requirement
|
|
25
|
-
none: false
|
|
26
37
|
requirements:
|
|
27
|
-
- -
|
|
38
|
+
- - '>='
|
|
28
39
|
- !ruby/object:Gem::Version
|
|
29
40
|
version: '0'
|
|
30
41
|
- !ruby/object:Gem::Dependency
|
|
31
42
|
name: rspec
|
|
32
43
|
requirement: !ruby/object:Gem::Requirement
|
|
33
|
-
none: false
|
|
34
44
|
requirements:
|
|
35
45
|
- - ~>
|
|
36
46
|
- !ruby/object:Gem::Version
|
|
37
|
-
version: '2.
|
|
47
|
+
version: '2.13'
|
|
38
48
|
type: :development
|
|
39
49
|
prerelease: false
|
|
40
50
|
version_requirements: !ruby/object:Gem::Requirement
|
|
41
|
-
none: false
|
|
42
51
|
requirements:
|
|
43
52
|
- - ~>
|
|
44
53
|
- !ruby/object:Gem::Version
|
|
45
|
-
version: '2.
|
|
54
|
+
version: '2.13'
|
|
46
55
|
- !ruby/object:Gem::Dependency
|
|
47
56
|
name: simplecov
|
|
48
57
|
requirement: !ruby/object:Gem::Requirement
|
|
49
|
-
none: false
|
|
50
58
|
requirements:
|
|
51
59
|
- - ~>
|
|
52
60
|
- !ruby/object:Gem::Version
|
|
53
|
-
version: '0.
|
|
61
|
+
version: '0.7'
|
|
54
62
|
type: :development
|
|
55
63
|
prerelease: false
|
|
56
64
|
version_requirements: !ruby/object:Gem::Requirement
|
|
57
|
-
none: false
|
|
58
65
|
requirements:
|
|
59
66
|
- - ~>
|
|
60
67
|
- !ruby/object:Gem::Version
|
|
61
|
-
version: '0.
|
|
68
|
+
version: '0.7'
|
|
62
69
|
description: Ruby wrapper to manage LXC (Linux Containers).
|
|
63
70
|
email:
|
|
64
71
|
- dan.sosedoff@gmail.com
|
|
@@ -72,14 +79,15 @@ files:
|
|
|
72
79
|
- Gemfile
|
|
73
80
|
- README.md
|
|
74
81
|
- Rakefile
|
|
82
|
+
- Vagrantfile
|
|
75
83
|
- lib/lxc.rb
|
|
76
84
|
- lib/lxc/configuration.rb
|
|
77
85
|
- lib/lxc/configuration_options.rb
|
|
78
86
|
- lib/lxc/container.rb
|
|
79
|
-
- lib/lxc/errors.rb
|
|
80
87
|
- lib/lxc/shell.rb
|
|
88
|
+
- lib/lxc/status.rb
|
|
81
89
|
- lib/lxc/version.rb
|
|
82
|
-
- lxc.gemspec
|
|
90
|
+
- lxc-ruby.gemspec
|
|
83
91
|
- spec/configuration_spec.rb
|
|
84
92
|
- spec/container_spec.rb
|
|
85
93
|
- spec/fixtures/configuration.txt
|
|
@@ -90,35 +98,29 @@ files:
|
|
|
90
98
|
- spec/fixtures/lxc-version.txt
|
|
91
99
|
- spec/lxc_spec.rb
|
|
92
100
|
- spec/spec_helper.rb
|
|
101
|
+
- spec/status_spec.rb
|
|
93
102
|
homepage: http://github.com/sosedoff/lxc-ruby
|
|
94
103
|
licenses: []
|
|
104
|
+
metadata: {}
|
|
95
105
|
post_install_message:
|
|
96
106
|
rdoc_options: []
|
|
97
107
|
require_paths:
|
|
98
108
|
- lib
|
|
99
109
|
required_ruby_version: !ruby/object:Gem::Requirement
|
|
100
|
-
none: false
|
|
101
110
|
requirements:
|
|
102
|
-
- -
|
|
111
|
+
- - '>='
|
|
103
112
|
- !ruby/object:Gem::Version
|
|
104
113
|
version: '0'
|
|
105
|
-
segments:
|
|
106
|
-
- 0
|
|
107
|
-
hash: 2803095474129589431
|
|
108
114
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
109
|
-
none: false
|
|
110
115
|
requirements:
|
|
111
|
-
- -
|
|
116
|
+
- - '>='
|
|
112
117
|
- !ruby/object:Gem::Version
|
|
113
118
|
version: '0'
|
|
114
|
-
segments:
|
|
115
|
-
- 0
|
|
116
|
-
hash: 2803095474129589431
|
|
117
119
|
requirements: []
|
|
118
120
|
rubyforge_project:
|
|
119
|
-
rubygems_version:
|
|
121
|
+
rubygems_version: 2.0.5
|
|
120
122
|
signing_key:
|
|
121
|
-
specification_version:
|
|
123
|
+
specification_version: 4
|
|
122
124
|
summary: Ruby wrapper to LXC
|
|
123
125
|
test_files:
|
|
124
126
|
- spec/configuration_spec.rb
|
|
@@ -131,3 +133,5 @@ test_files:
|
|
|
131
133
|
- spec/fixtures/lxc-version.txt
|
|
132
134
|
- spec/lxc_spec.rb
|
|
133
135
|
- spec/spec_helper.rb
|
|
136
|
+
- spec/status_spec.rb
|
|
137
|
+
has_rdoc:
|