vagrant-lxc-2.1-patch 1.4.0
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 +31 -0
- data/.rspec +2 -0
- data/.travis.yml +10 -0
- data/.vimrc +1 -0
- data/BOXES.md +47 -0
- data/CHANGELOG.md +510 -0
- data/CONTRIBUTING.md +24 -0
- data/Gemfile +24 -0
- data/Guardfile +7 -0
- data/LICENSE.txt +22 -0
- data/README.md +187 -0
- data/Rakefile +3 -0
- data/lib/vagrant-lxc.rb +10 -0
- data/lib/vagrant-lxc/action.rb +234 -0
- data/lib/vagrant-lxc/action/boot.rb +42 -0
- data/lib/vagrant-lxc/action/clear_forwarded_ports.rb +56 -0
- data/lib/vagrant-lxc/action/compress_rootfs.rb +30 -0
- data/lib/vagrant-lxc/action/create.rb +57 -0
- data/lib/vagrant-lxc/action/destroy.rb +18 -0
- data/lib/vagrant-lxc/action/destroy_confirm.rb +17 -0
- data/lib/vagrant-lxc/action/fetch_ip_with_lxc_info.rb +43 -0
- data/lib/vagrant-lxc/action/forced_halt.rb +20 -0
- data/lib/vagrant-lxc/action/forward_ports.rb +121 -0
- data/lib/vagrant-lxc/action/gc_private_network_bridges.rb +47 -0
- data/lib/vagrant-lxc/action/handle_box_metadata.rb +94 -0
- data/lib/vagrant-lxc/action/prepare_nfs_settings.rb +64 -0
- data/lib/vagrant-lxc/action/prepare_nfs_valid_ids.rb +19 -0
- data/lib/vagrant-lxc/action/private_networks.rb +46 -0
- data/lib/vagrant-lxc/action/setup_package_files.rb +60 -0
- data/lib/vagrant-lxc/action/warn_networks.rb +25 -0
- data/lib/vagrant-lxc/command/root.rb +58 -0
- data/lib/vagrant-lxc/command/sudoers.rb +97 -0
- data/lib/vagrant-lxc/config.rb +73 -0
- data/lib/vagrant-lxc/driver.rb +288 -0
- data/lib/vagrant-lxc/driver/cli.rb +166 -0
- data/lib/vagrant-lxc/errors.rb +62 -0
- data/lib/vagrant-lxc/plugin.rb +51 -0
- data/lib/vagrant-lxc/provider.rb +101 -0
- data/lib/vagrant-lxc/provider/cap/public_address.rb +17 -0
- data/lib/vagrant-lxc/sudo_wrapper.rb +104 -0
- data/lib/vagrant-lxc/synced_folder.rb +72 -0
- data/lib/vagrant-lxc/version.rb +5 -0
- data/locales/en.yml +82 -0
- data/scripts/lxc-template +171 -0
- data/scripts/pipework +422 -0
- data/spec/Vagrantfile +26 -0
- data/spec/fixtures/sample-ip-addr-output +2 -0
- data/spec/spec_helper.rb +35 -0
- data/spec/support/.gitkeep +0 -0
- data/spec/unit/action/clear_forwarded_ports_spec.rb +43 -0
- data/spec/unit/action/compress_rootfs_spec.rb +29 -0
- data/spec/unit/action/forward_ports_spec.rb +117 -0
- data/spec/unit/action/handle_box_metadata_spec.rb +126 -0
- data/spec/unit/action/setup_package_files_spec.rb +83 -0
- data/spec/unit/driver/cli_spec.rb +263 -0
- data/spec/unit/driver_spec.rb +268 -0
- data/spec/unit/support/unit_example_group.rb +38 -0
- data/spec/unit_helper.rb +17 -0
- data/tasks/spec.rake +40 -0
- data/templates/sudoers.rb.erb +129 -0
- data/vagrant-lxc.gemspec +20 -0
- data/vagrant-spec.config.rb +24 -0
- metadata +119 -0
@@ -0,0 +1,73 @@
|
|
1
|
+
module Vagrant
|
2
|
+
module LXC
|
3
|
+
class Config < Vagrant.plugin("2", :config)
|
4
|
+
# An array of container's configuration overrides to be provided to `lxc-start`.
|
5
|
+
#
|
6
|
+
# @return [Array]
|
7
|
+
attr_reader :customizations
|
8
|
+
|
9
|
+
# A string that contains the backing store type used with lxc-create -B
|
10
|
+
attr_accessor :backingstore
|
11
|
+
|
12
|
+
# Optional arguments for the backing store, such as --fssize, --fstype, ...
|
13
|
+
#
|
14
|
+
# @return [Array]
|
15
|
+
attr_accessor :backingstore_options
|
16
|
+
|
17
|
+
# A string to explicitly set the container name. To use the vagrant
|
18
|
+
# machine name, set this to :machine
|
19
|
+
attr_accessor :container_name
|
20
|
+
|
21
|
+
# Size (as a string like '400M') of the tmpfs to mount at /tmp on boot.
|
22
|
+
# Set to false or nil to disable the tmpfs mount altogether. Defaults to '2G'.
|
23
|
+
attr_accessor :tmpfs_mount_size
|
24
|
+
|
25
|
+
attr_accessor :fetch_ip_tries
|
26
|
+
|
27
|
+
# Whether the container needs to be privileged. Defaults to true (unprivileged containers
|
28
|
+
# is a very new feature in vagrant-lxc). If false, will try creating an unprivileged
|
29
|
+
# container. If it can't, will revert to the old "sudo wrapper" method to create a privileged
|
30
|
+
# container.
|
31
|
+
attr_accessor :privileged
|
32
|
+
|
33
|
+
def initialize
|
34
|
+
@customizations = []
|
35
|
+
@backingstore = UNSET_VALUE
|
36
|
+
@backingstore_options = []
|
37
|
+
@container_name = UNSET_VALUE
|
38
|
+
@tmpfs_mount_size = UNSET_VALUE
|
39
|
+
@fetch_ip_tries = UNSET_VALUE
|
40
|
+
@privileged = UNSET_VALUE
|
41
|
+
end
|
42
|
+
|
43
|
+
# Customize the container by calling `lxc-start` with the given
|
44
|
+
# configuration overrides.
|
45
|
+
#
|
46
|
+
# For example, if you want to set the memory limit, you can use it
|
47
|
+
# like: config.customize 'cgroup.memory.limit_in_bytes', '400M'
|
48
|
+
#
|
49
|
+
# When `lxc-start`ing the container, vagrant-lxc will pass in
|
50
|
+
# "-s lxc.cgroup.memory.limit_in_bytes=400M" to it.
|
51
|
+
#
|
52
|
+
# @param [String] key Configuration key to override
|
53
|
+
# @param [String] value Configuration value to override
|
54
|
+
def customize(key, value)
|
55
|
+
@customizations << [key, value]
|
56
|
+
end
|
57
|
+
|
58
|
+
# Stores options for backingstores like lvm, btrfs, etc
|
59
|
+
def backingstore_option(key, value)
|
60
|
+
@backingstore_options << [key, value]
|
61
|
+
end
|
62
|
+
|
63
|
+
def finalize!
|
64
|
+
@container_name = nil if @container_name == UNSET_VALUE
|
65
|
+
@backingstore = nil if @backingstore == UNSET_VALUE
|
66
|
+
@existing_container_name = nil if @existing_container_name == UNSET_VALUE
|
67
|
+
@tmpfs_mount_size = '2G' if @tmpfs_mount_size == UNSET_VALUE
|
68
|
+
@fetch_ip_tries = 10 if @fetch_ip_tries == UNSET_VALUE
|
69
|
+
@privileged = true if @privileged == UNSET_VALUE
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
@@ -0,0 +1,288 @@
|
|
1
|
+
require "vagrant/util/retryable"
|
2
|
+
require "vagrant/util/subprocess"
|
3
|
+
|
4
|
+
require "vagrant-lxc/errors"
|
5
|
+
require "vagrant-lxc/driver/cli"
|
6
|
+
require "vagrant-lxc/sudo_wrapper"
|
7
|
+
|
8
|
+
require "etc"
|
9
|
+
|
10
|
+
require "tempfile"
|
11
|
+
|
12
|
+
module Vagrant
|
13
|
+
module LXC
|
14
|
+
class Driver
|
15
|
+
# This is raised if the container can't be found when initializing it with
|
16
|
+
# a name.
|
17
|
+
class ContainerNotFound < StandardError; end
|
18
|
+
|
19
|
+
# Default root folder where container configs are stored
|
20
|
+
attr_reader :container_name,
|
21
|
+
:customizations
|
22
|
+
|
23
|
+
def initialize(container_name, sudo_wrapper = nil, cli = nil, privileged: true)
|
24
|
+
@container_name = container_name
|
25
|
+
@sudo_wrapper = sudo_wrapper || SudoWrapper.new(privileged: privileged)
|
26
|
+
@cli = cli || CLI.new(@sudo_wrapper, container_name)
|
27
|
+
@logger = Log4r::Logger.new("vagrant::provider::lxc::driver")
|
28
|
+
@customizations = []
|
29
|
+
end
|
30
|
+
|
31
|
+
def validate!
|
32
|
+
raise ContainerNotFound if @container_name && ! @cli.list.include?(@container_name)
|
33
|
+
end
|
34
|
+
|
35
|
+
# Root folder where container configs are stored
|
36
|
+
def containers_path
|
37
|
+
@containers_path ||= @cli.config('lxc.lxcpath')
|
38
|
+
end
|
39
|
+
|
40
|
+
def all_containers
|
41
|
+
@cli.list
|
42
|
+
end
|
43
|
+
|
44
|
+
def base_path
|
45
|
+
Pathname.new("#{containers_path}/#{@container_name}")
|
46
|
+
end
|
47
|
+
|
48
|
+
def config_path
|
49
|
+
base_path.join('config').to_s
|
50
|
+
end
|
51
|
+
|
52
|
+
def rootfs_path
|
53
|
+
config_entry = config_string.match(/^lxc\.rootfs\s+=\s+(.+)$/)[1]
|
54
|
+
case config_entry
|
55
|
+
when /^overlayfs:/
|
56
|
+
# Split on colon (:), ignoring any colon escaped by an escape character ( \ )
|
57
|
+
# Pays attention to when the escape character is itself escaped.
|
58
|
+
fs_type, master_path, overlay_path = config_entry.split(/(?<!\\)(?:\\\\)*:/)
|
59
|
+
if overlay_path
|
60
|
+
Pathname.new(overlay_path)
|
61
|
+
else
|
62
|
+
# Malformed: fall back to prior behaviour
|
63
|
+
Pathname.new(config_entry)
|
64
|
+
end
|
65
|
+
else
|
66
|
+
Pathname.new(config_entry)
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
def mac_address
|
71
|
+
return @mac_address if @mac_address
|
72
|
+
|
73
|
+
if config_string =~ /^lxc\.network\.hwaddr\s*+=\s*+(.+)$/
|
74
|
+
@mac_address = $1
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
def config_string
|
79
|
+
@sudo_wrapper.run('cat', config_path)
|
80
|
+
end
|
81
|
+
|
82
|
+
def create(name, backingstore, backingstore_options, template_path, config_file, template_options = {})
|
83
|
+
@cli.name = @container_name = name
|
84
|
+
|
85
|
+
@logger.debug "Creating container..."
|
86
|
+
@cli.create template_path, backingstore, backingstore_options, config_file, template_options
|
87
|
+
end
|
88
|
+
|
89
|
+
def share_folders(folders)
|
90
|
+
folders.each do |f|
|
91
|
+
share_folder(f[:hostpath], f[:guestpath], f.fetch(:mount_options, nil))
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
def share_folder(host_path, guest_path, mount_options = nil)
|
96
|
+
guest_path = guest_path.gsub(/^\//, '').gsub(' ', '\\\040')
|
97
|
+
mount_options = Array(mount_options || ['bind', 'create=dir'])
|
98
|
+
host_path = host_path.to_s.gsub(' ', '\\\040')
|
99
|
+
@customizations << ['mount.entry', "#{host_path} #{guest_path} none #{mount_options.join(',')} 0 0"]
|
100
|
+
end
|
101
|
+
|
102
|
+
def start(customizations)
|
103
|
+
@logger.info('Starting container...')
|
104
|
+
|
105
|
+
if ENV['LXC_START_LOG_FILE']
|
106
|
+
extra = ['-o', ENV['LXC_START_LOG_FILE'], '-l', 'DEBUG']
|
107
|
+
end
|
108
|
+
|
109
|
+
prune_customizations
|
110
|
+
write_customizations(customizations + @customizations)
|
111
|
+
|
112
|
+
@cli.start(extra)
|
113
|
+
end
|
114
|
+
|
115
|
+
def forced_halt
|
116
|
+
@logger.info('Shutting down container...')
|
117
|
+
@cli.transition_to(:stopped) { |c| c.stop }
|
118
|
+
end
|
119
|
+
|
120
|
+
def destroy
|
121
|
+
@cli.destroy
|
122
|
+
end
|
123
|
+
|
124
|
+
def supports_attach?
|
125
|
+
@cli.supports_attach?
|
126
|
+
end
|
127
|
+
|
128
|
+
def attach(*command)
|
129
|
+
@cli.attach(*command)
|
130
|
+
end
|
131
|
+
|
132
|
+
def info(*command)
|
133
|
+
@cli.info(*command)
|
134
|
+
end
|
135
|
+
|
136
|
+
def configure_private_network(bridge_name, bridge_ip, container_name, address_type, ip)
|
137
|
+
@logger.info "Configuring network interface for #{container_name} using #{ip} and bridge #{bridge_name}"
|
138
|
+
if ip
|
139
|
+
ip += '/24'
|
140
|
+
end
|
141
|
+
|
142
|
+
if ! bridge_exists?(bridge_name)
|
143
|
+
if not bridge_ip
|
144
|
+
raise "Bridge is missing and no IP was specified!"
|
145
|
+
end
|
146
|
+
|
147
|
+
@logger.info "Creating the bridge #{bridge_name}"
|
148
|
+
cmd = [
|
149
|
+
'brctl',
|
150
|
+
'addbr',
|
151
|
+
bridge_name
|
152
|
+
]
|
153
|
+
@sudo_wrapper.run(*cmd)
|
154
|
+
end
|
155
|
+
|
156
|
+
if ! bridge_has_an_ip?(bridge_name)
|
157
|
+
if not bridge_ip
|
158
|
+
raise "Bridge has no IP and none was specified!"
|
159
|
+
end
|
160
|
+
@logger.info "Adding #{bridge_ip} to the bridge #{bridge_name}"
|
161
|
+
cmd = [
|
162
|
+
'ip',
|
163
|
+
'addr',
|
164
|
+
'add',
|
165
|
+
"#{bridge_ip}/24",
|
166
|
+
'dev',
|
167
|
+
bridge_name
|
168
|
+
]
|
169
|
+
@sudo_wrapper.run(*cmd)
|
170
|
+
@sudo_wrapper.run('ip', 'link', 'set', bridge_name, 'up')
|
171
|
+
end
|
172
|
+
|
173
|
+
cmd = [
|
174
|
+
Vagrant::LXC.source_root.join('scripts/pipework').to_s,
|
175
|
+
bridge_name,
|
176
|
+
container_name,
|
177
|
+
ip ||= "dhcp"
|
178
|
+
]
|
179
|
+
@sudo_wrapper.run(*cmd)
|
180
|
+
end
|
181
|
+
|
182
|
+
def bridge_has_an_ip?(bridge_name)
|
183
|
+
@logger.info "Checking whether the bridge #{bridge_name} has an IP"
|
184
|
+
`ip -4 addr show scope global #{bridge_name}` =~ /^\s+inet ([0-9.]+)\/[0-9]+\s+/
|
185
|
+
end
|
186
|
+
|
187
|
+
def bridge_exists?(bridge_name)
|
188
|
+
@logger.info "Checking whether bridge #{bridge_name} exists"
|
189
|
+
brctl_output = `ip link | egrep -q " #{bridge_name}:"`
|
190
|
+
$?.to_i == 0
|
191
|
+
end
|
192
|
+
|
193
|
+
def bridge_is_in_use?(bridge_name)
|
194
|
+
# REFACTOR: This method is **VERY** hacky
|
195
|
+
@logger.info "Checking if bridge #{bridge_name} is in use"
|
196
|
+
brctl_output = `brctl show #{bridge_name} 2>/dev/null | tail -n +2 | grep -q veth`
|
197
|
+
$?.to_i == 0
|
198
|
+
end
|
199
|
+
|
200
|
+
def remove_bridge(bridge_name)
|
201
|
+
if ['lxcbr0', 'virbr0'].include? bridge_name
|
202
|
+
@logger.info "Skipping removal of system bridge #{bridge_name}"
|
203
|
+
return
|
204
|
+
end
|
205
|
+
|
206
|
+
return unless bridge_exists?(bridge_name)
|
207
|
+
|
208
|
+
@logger.info "Removing bridge #{bridge_name}"
|
209
|
+
@sudo_wrapper.run('ip', 'link', 'set', bridge_name, 'down')
|
210
|
+
@sudo_wrapper.run('brctl', 'delbr', bridge_name)
|
211
|
+
end
|
212
|
+
|
213
|
+
def version
|
214
|
+
@version ||= @cli.version
|
215
|
+
end
|
216
|
+
|
217
|
+
# TODO: This needs to be reviewed and specs needs to be written
|
218
|
+
def compress_rootfs
|
219
|
+
# TODO: Pass in tmpdir so we can clean up from outside
|
220
|
+
target_path = "#{Dir.mktmpdir}/rootfs.tar.gz"
|
221
|
+
|
222
|
+
@logger.info "Compressing '#{rootfs_path}' rootfs to #{target_path}"
|
223
|
+
@sudo_wrapper.run('tar', '--numeric-owner', '-cvzf', target_path, '-C',
|
224
|
+
rootfs_path.parent.to_s, "./#{rootfs_path.basename.to_s}")
|
225
|
+
|
226
|
+
@logger.info "Changing rootfs tarball owner"
|
227
|
+
user_details = Etc.getpwnam(Etc.getlogin)
|
228
|
+
@sudo_wrapper.run('chown', "#{user_details.uid}:#{user_details.gid}", target_path)
|
229
|
+
|
230
|
+
target_path
|
231
|
+
end
|
232
|
+
|
233
|
+
def state
|
234
|
+
if @container_name
|
235
|
+
@cli.state
|
236
|
+
end
|
237
|
+
end
|
238
|
+
|
239
|
+
def prune_customizations
|
240
|
+
# Use sed to just strip out the block of code which was inserted by Vagrant
|
241
|
+
@logger.debug 'Prunning vagrant-lxc customizations'
|
242
|
+
contents = config_string
|
243
|
+
contents.gsub! /^# VAGRANT-BEGIN(.|\s)*# VAGRANT-END\n/, ''
|
244
|
+
write_config(contents)
|
245
|
+
end
|
246
|
+
|
247
|
+
def update_config_keys
|
248
|
+
@cli.update_config(config_path)
|
249
|
+
rescue Errors::ExecuteError
|
250
|
+
# not on LXC 2.1+. Doesn't matter, ignore.
|
251
|
+
end
|
252
|
+
|
253
|
+
protected
|
254
|
+
|
255
|
+
def write_customizations(customizations)
|
256
|
+
customizations = customizations.map do |key, value|
|
257
|
+
"lxc.#{key}=#{value}"
|
258
|
+
end
|
259
|
+
customizations.unshift '# VAGRANT-BEGIN'
|
260
|
+
customizations << "# VAGRANT-END\n"
|
261
|
+
|
262
|
+
contents = config_string
|
263
|
+
contents << customizations.join("\n")
|
264
|
+
|
265
|
+
write_config(contents)
|
266
|
+
end
|
267
|
+
|
268
|
+
def write_config(contents)
|
269
|
+
confpath = base_path.join('config').to_s
|
270
|
+
begin
|
271
|
+
File.open(confpath, File::RDWR) do |file|
|
272
|
+
file.write contents
|
273
|
+
end
|
274
|
+
rescue
|
275
|
+
# We don't have permissions to write in the conf file. That's probably because it's a
|
276
|
+
# privileged container. Work around that through sudo_wrapper.
|
277
|
+
Tempfile.new('lxc-config').tap do |file|
|
278
|
+
file.chmod 0644
|
279
|
+
file.write contents
|
280
|
+
file.close
|
281
|
+
@sudo_wrapper.run 'cp', '-f', file.path, confpath
|
282
|
+
@sudo_wrapper.run 'chown', 'root:root', confpath
|
283
|
+
end
|
284
|
+
end
|
285
|
+
end
|
286
|
+
end
|
287
|
+
end
|
288
|
+
end
|
@@ -0,0 +1,166 @@
|
|
1
|
+
require "vagrant/util/retryable"
|
2
|
+
require "vagrant/util/subprocess"
|
3
|
+
|
4
|
+
require "vagrant-lxc/errors"
|
5
|
+
|
6
|
+
module Vagrant
|
7
|
+
module LXC
|
8
|
+
class Driver
|
9
|
+
class CLI
|
10
|
+
attr_accessor :name
|
11
|
+
|
12
|
+
class TransitionBlockNotProvided < RuntimeError; end
|
13
|
+
class TargetStateNotReached < RuntimeError
|
14
|
+
def initialize(target_state, state)
|
15
|
+
msg = "Target state '#{target_state}' not reached, currently on '#{state}'"
|
16
|
+
super(msg)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def initialize(sudo_wrapper, name = nil)
|
21
|
+
@sudo_wrapper = sudo_wrapper
|
22
|
+
@name = name
|
23
|
+
@logger = Log4r::Logger.new("vagrant::provider::lxc::container::cli")
|
24
|
+
end
|
25
|
+
|
26
|
+
def list
|
27
|
+
run(:ls).split(/\s+/).uniq
|
28
|
+
end
|
29
|
+
|
30
|
+
def version
|
31
|
+
return @version if @version
|
32
|
+
@version = run(:create, '--version')
|
33
|
+
if @version =~ /(lxc version:\s+|)(.+)\s*$/
|
34
|
+
@version = $2.downcase
|
35
|
+
else
|
36
|
+
# TODO: Raise an user friendly error
|
37
|
+
raise 'Unable to parse lxc version!'
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def config(param)
|
42
|
+
run(:config, param).gsub("\n", '')
|
43
|
+
end
|
44
|
+
|
45
|
+
def update_config(path)
|
46
|
+
run('update-config', '-c', path)
|
47
|
+
end
|
48
|
+
|
49
|
+
def state
|
50
|
+
if @name && run(:info, '--name', @name, retryable: true) =~ /^state:[^A-Z]+([A-Z]+)$/i
|
51
|
+
$1.downcase.to_sym
|
52
|
+
elsif @name
|
53
|
+
:unknown
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
def create(template, backingstore, backingstore_options, config_file, template_opts = {})
|
58
|
+
if config_file
|
59
|
+
config_opts = ['-f', config_file]
|
60
|
+
end
|
61
|
+
|
62
|
+
extra = template_opts.to_a.flatten
|
63
|
+
extra.unshift '--' unless extra.empty?
|
64
|
+
|
65
|
+
run :create,
|
66
|
+
'-B', backingstore,
|
67
|
+
'--template', template,
|
68
|
+
'--name', @name,
|
69
|
+
*(backingstore_options.to_a.flatten),
|
70
|
+
*(config_opts),
|
71
|
+
*extra
|
72
|
+
rescue Errors::ExecuteError => e
|
73
|
+
if e.stderr =~ /already exists/i
|
74
|
+
raise Errors::ContainerAlreadyExists, name: @name
|
75
|
+
else
|
76
|
+
raise
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
def destroy
|
81
|
+
run :destroy, '--name', @name
|
82
|
+
end
|
83
|
+
|
84
|
+
def start(options = [])
|
85
|
+
run :start, '-d', '--name', @name, *Array(options)
|
86
|
+
end
|
87
|
+
|
88
|
+
## lxc-stop will exit 2 if machine was already stopped
|
89
|
+
# Man Page:
|
90
|
+
# 2 The specified container exists but was not running.
|
91
|
+
def stop
|
92
|
+
attach '/sbin/halt' if supports_attach?
|
93
|
+
begin
|
94
|
+
run :stop, '--name', @name
|
95
|
+
rescue LXC::Errors::ExecuteError => e
|
96
|
+
if e.exitcode == 2
|
97
|
+
@logger.debug "Machine already stopped, lxc-stop returned 2"
|
98
|
+
else
|
99
|
+
raise e
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
def attach(*cmd)
|
105
|
+
cmd = ['--'] + cmd
|
106
|
+
|
107
|
+
if cmd.last.is_a?(Hash)
|
108
|
+
opts = cmd.pop
|
109
|
+
namespaces = Array(opts[:namespaces]).map(&:upcase).join('|')
|
110
|
+
|
111
|
+
# HACK: The wrapper script should be able to handle this
|
112
|
+
if @sudo_wrapper.wrapper_path
|
113
|
+
namespaces = "'#{namespaces}'"
|
114
|
+
end
|
115
|
+
|
116
|
+
if namespaces
|
117
|
+
extra = ['--namespaces', namespaces]
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
run :attach, '--name', @name, *((extra || []) + cmd)
|
122
|
+
end
|
123
|
+
|
124
|
+
def info(*cmd)
|
125
|
+
run(:info, '--name', @name, *cmd)
|
126
|
+
end
|
127
|
+
|
128
|
+
def transition_to(target_state, tries = 30, timeout = 1, &block)
|
129
|
+
raise TransitionBlockNotProvided unless block_given?
|
130
|
+
|
131
|
+
yield self
|
132
|
+
|
133
|
+
while (last_state = self.state) != target_state && tries > 0
|
134
|
+
@logger.debug "Target state '#{target_state}' not reached, currently on '#{last_state}'"
|
135
|
+
sleep timeout
|
136
|
+
tries -= 1
|
137
|
+
end
|
138
|
+
|
139
|
+
unless last_state == target_state
|
140
|
+
# TODO: Raise an user friendly message
|
141
|
+
raise TargetStateNotReached.new target_state, last_state
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
def supports_attach?
|
146
|
+
unless defined?(@supports_attach)
|
147
|
+
begin
|
148
|
+
@supports_attach = true
|
149
|
+
run(:attach, '--name', @name, '--', '/bin/true')
|
150
|
+
rescue LXC::Errors::ExecuteError
|
151
|
+
@supports_attach = false
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
155
|
+
return @supports_attach
|
156
|
+
end
|
157
|
+
|
158
|
+
private
|
159
|
+
|
160
|
+
def run(command, *args)
|
161
|
+
@sudo_wrapper.run("lxc-#{command}", *args)
|
162
|
+
end
|
163
|
+
end
|
164
|
+
end
|
165
|
+
end
|
166
|
+
end
|