vagrant-lxd 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/README.md ADDED
@@ -0,0 +1,121 @@
1
+ # vagrant-lxd
2
+
3
+ This is a [Vagrant][] plugin that adds the ability to manage containers
4
+ with [LXD][].
5
+
6
+ [Vagrant]: https://www.vagrantup.com/
7
+ [LXD]: https://linuxcontainers.org/lxd/
8
+
9
+ ## Features
10
+
11
+ The following features are currently supported:
12
+
13
+ - VM management (create, suspend, destroy, etc.)
14
+ - IPv4 networking
15
+ - Synced folders
16
+ - Snapshots
17
+
18
+ The following features are not expected to work yet:
19
+
20
+ - Forwarded ports
21
+ - Static IP addresses
22
+ - IPv6 networking
23
+
24
+ The plugin requires LXD 2.0 and Vagrant 1.8.7 or newer.
25
+
26
+ ## Installation
27
+
28
+ Installing the plugin from this repository is a three-step process.
29
+
30
+ 1. Use Bundler to install development dependencies:
31
+
32
+ $ bundle install
33
+
34
+ 2. Build the gem:
35
+
36
+ $ bundle exec rake build
37
+
38
+ 3. Install it as a Vagrant plugin:
39
+
40
+ $ vagrant plugin install pkg/vagrant-lxd-<version>.gem
41
+
42
+ ## Usage
43
+
44
+ ### Quick Start
45
+
46
+ This plugin reuses the `lxc` box format, so VM images from [Vagrant
47
+ Cloud][cloud] should work without modification:
48
+
49
+ $ vagrant init --minimal debian/stretch64
50
+ $ vagrant up --provider lxd
51
+
52
+ [cloud]: https://app.vagrantup.com/boxes/search?provider=lxc
53
+
54
+ #### Configuration
55
+
56
+ Below is an example Vagrantfile showing all of the provider's
57
+ configurable values, along with their defaults. The `debian/stretch64`
58
+ box is available on the Vagrant Cloud, so you should be able to copy
59
+ this file and adjust it as you see fit.
60
+
61
+ ``` ruby
62
+ Vagrant.configure('2') do |config|
63
+ config.vm.box = 'debian/stretch64'
64
+
65
+ config.vm.provider 'lxd' do |lxd|
66
+ lxd.api_endpoint = 'https://127.0.0.1:8443'
67
+ lxd.timeout = 10
68
+ lxd.name = nil
69
+ lxd.ephemeral = false
70
+ end
71
+ end
72
+ ```
73
+
74
+ ### Shared Folders
75
+
76
+ In order to use shared folders, you must first add your user ID to the
77
+ host machine's subuid(5) and subgid(5) files:
78
+
79
+ $ echo root:$(id -u):1 | sudo tee -a /etc/subuid
80
+ $ echo root:$(id -g):1 | sudo tee -a /etc/subgid
81
+
82
+ For more information about these commands, and user/group ID mapping in
83
+ general, we recommend [this article][1].
84
+
85
+ [1]: https://insights.ubuntu.com/2017/06/15/custom-user-mappings-in-lxd-containers/
86
+
87
+ ### Shared LXD Containers
88
+
89
+ It's possible to share a single LXD container between multiple Vagrant
90
+ VMs by "attaching" them to the container by name.
91
+
92
+ For example, to associate the "default" VM with a preexisting LXD
93
+ container called "my-container", use the `vagrant lxd attach` command:
94
+
95
+ $ lxc list -cn # list available containers
96
+ +--------------+
97
+ | NAME |
98
+ +--------------+
99
+ | my-container |
100
+ +--------------+
101
+
102
+ $ vagrant lxd detach default # detach from current container, if necessary
103
+ ==> default: Machine is not attached to a container, skipping...
104
+
105
+ $ vagrant lxd attach default my-container
106
+ ==> default: Attaching to container 'my-container'...
107
+
108
+ ## Hacking
109
+
110
+ To run Vagrant with the plugin automatically loaded, you can use the
111
+ `bundle exec` command:
112
+
113
+ $ bundle exec vagrant <command>
114
+
115
+ ## Contributing
116
+
117
+ 1. Fork it from <https://gitlab.com/catalyst-it/vagrant-lxd>
118
+ 2. Create a feature branch (`git checkout -b my-new-feature`)
119
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
120
+ 4. Push to the branch (`git push origin my-new-feature`)
121
+ 5. Create a Merge Request at <https://gitlab.com/catalyst-it/vagrant-lxd/merge_requests>
data/Rakefile ADDED
@@ -0,0 +1,12 @@
1
+ require 'rubygems'
2
+ require 'bundler/setup'
3
+ require 'rspec/core/rake_task'
4
+
5
+ Bundler::GemHelper.install_tasks
6
+
7
+ RSpec::Core::RakeTask.new(:spec) do |t|
8
+ t.rspec_opts = '-I. -rspec/common'
9
+ t.verbose = false
10
+ end
11
+
12
+ task :default => :spec
@@ -0,0 +1,23 @@
1
+ #
2
+ # Copyright (c) 2017 Catalyst.net Ltd
3
+ #
4
+ # This file is part of vagrant-lxd.
5
+ #
6
+ # vagrant-lxd is free software: you can redistribute it and/or modify
7
+ # it under the terms of the GNU General Public License as published by
8
+ # the Free Software Foundation, either version 3 of the License, or (at
9
+ # your option) any later version.
10
+ #
11
+ # vagrant-lxd is distributed in the hope that it will be useful, but
12
+ # WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14
+ # General Public License for more details.
15
+ #
16
+ # You should have received a copy of the GNU General Public License
17
+ # along with vagrant-lxd. If not, see <http://www.gnu.org/licenses/>.
18
+ #
19
+
20
+ require 'i18n'
21
+ require 'vagrant-lxd/plugin'
22
+
23
+ I18n.load_path << File.expand_path('../../templates/locales/en.yml', __FILE__)
@@ -0,0 +1,314 @@
1
+ #
2
+ # Copyright (c) 2017 Catalyst.net Ltd
3
+ #
4
+ # This file is part of vagrant-lxd.
5
+ #
6
+ # vagrant-lxd is free software: you can redistribute it and/or modify
7
+ # it under the terms of the GNU General Public License as published by
8
+ # the Free Software Foundation, either version 3 of the License, or (at
9
+ # your option) any later version.
10
+ #
11
+ # vagrant-lxd is distributed in the hope that it will be useful, but
12
+ # WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14
+ # General Public License for more details.
15
+ #
16
+ # You should have received a copy of the GNU General Public License
17
+ # along with vagrant-lxd. If not, see <http://www.gnu.org/licenses/>.
18
+ #
19
+
20
+ require 'vagrant/action/builder'
21
+ require 'vagrant/machine_state'
22
+ require 'vagrant-lxd/driver'
23
+
24
+ module VagrantLXD
25
+ module Action
26
+
27
+ #
28
+ # The LXD class is middleware that simply forwards its call to the
29
+ # corresponding method on the LXD driver and copies the result into
30
+ # the env hash under the key `:machine_<method>`.
31
+ #
32
+ # The method to be called is controlled by the proxy object's class
33
+ # name. The correct instance to use for a particular method call is
34
+ # retrieved with `LXD.action`.
35
+ #
36
+ class LXD
37
+ def initialize(app, env, *args)
38
+ @app = app
39
+ @args = args
40
+ @driver = Driver.new(env[:machine])
41
+ end
42
+
43
+ def call(env)
44
+ env[:"machine_#{method}"] = @driver.send(method, *@args)
45
+ @app.call(env)
46
+ end
47
+
48
+ private
49
+
50
+ def method
51
+ self.class.to_s.split('::').last.downcase
52
+ end
53
+
54
+ def LXD.action(name)
55
+ const = name.to_s.sub(/[a-z]/, &:upcase)
56
+ const_get(const)
57
+ rescue NameError
58
+ Class.new(LXD).tap do |proxy|
59
+ const_set(const, proxy)
60
+ end
61
+ end
62
+ end
63
+
64
+ #
65
+ # Message issues a message to the user through the `env[:ui]` object
66
+ # provided to this middleware. The level is controlled via `type`,
67
+ # which should be a method on `env[:ui]`.
68
+ #
69
+ class Message
70
+ def initialize(app, env, type, message)
71
+ @app = app
72
+ @type = type
73
+ @message = message
74
+ end
75
+
76
+ def call(env)
77
+ env[:ui].send(@type, @message)
78
+ @app.call(env)
79
+ end
80
+ end
81
+
82
+ #
83
+ # Check whether the LXD driver is usable and immediately signal an
84
+ # error if not (preventing any remaining middlewares from running).
85
+ #
86
+ class ConnectionValidate
87
+ def initialize(app, env)
88
+ @app = app
89
+ @driver = Driver.new(env[:machine])
90
+ end
91
+
92
+ def call(env)
93
+ @driver.validate!
94
+ @app.call(env)
95
+ end
96
+ end
97
+
98
+ #
99
+ # Action definitions.
100
+ #
101
+ class << Action
102
+ include Vagrant::Action::Builtin
103
+
104
+ def up
105
+ builder do |b|
106
+ b.use Call, state do |env, c|
107
+ case env[:machine_state]
108
+ when Vagrant::MachineState::NOT_CREATED_ID
109
+ c.use Message, :info, 'Machine has not been created yet, starting...'
110
+ c.use HandleBox
111
+ c.use LXD.action(:create)
112
+ c.use LXD.action(:resume)
113
+ c.use SyncedFolders
114
+ c.use WaitForCommunicator
115
+ c.use Provision
116
+ when :running
117
+ c.use Message, :info, 'Machine is already running.'
118
+ when :frozen, :stopped
119
+ c.use resume
120
+ else
121
+ c.use Message, :error, "Machine cannot be started while #{env[:machine_state]}."
122
+ end
123
+ end
124
+ end
125
+ end
126
+
127
+ def destroy
128
+ builder do |b|
129
+ b.use Call, IsState, Vagrant::MachineState::NOT_CREATED_ID do |env, c|
130
+ if env[:result]
131
+ next
132
+ else
133
+ c.use Call, DestroyConfirm do |env, d|
134
+ if env[:result]
135
+ d.use halt
136
+ d.use Message, :info, 'Destroying machine and associated data...'
137
+ d.use LXD.action(:destroy)
138
+ else
139
+ d.use Message, :info, 'Machine will not be destroyed.'
140
+ end
141
+ end
142
+ end
143
+ end
144
+ end
145
+ end
146
+
147
+ def halt
148
+ builder do |b|
149
+ b.use Call, state do |env, c|
150
+ case env[:machine_state]
151
+ when Vagrant::MachineState::NOT_CREATED_ID
152
+ next
153
+ when :stopped
154
+ c.use Message, :info, 'Machine is already stopped.'
155
+ when :frozen, :running
156
+ c.use Message, :info, 'Stopping machine...'
157
+ c.use LXD.action(:halt)
158
+ else
159
+ c.use Message, :error, "Machine cannot be stopped while #{env[:machine_state]}."
160
+ end
161
+ end
162
+ end
163
+ end
164
+
165
+ def suspend
166
+ builder do |b|
167
+ b.use Call, state do |env, c|
168
+ case env[:machine_state]
169
+ when Vagrant::MachineState::NOT_CREATED_ID
170
+ next
171
+ when :frozen
172
+ c.use Message, :info, 'Machine is already suspended.'
173
+ when :running
174
+ c.use Message, :info, 'Suspending machine...'
175
+ c.use LXD.action(:suspend)
176
+ else
177
+ c.use Message, :error, "Machine cannot be suspended while #{env[:machine_state]}."
178
+ end
179
+ end
180
+ end
181
+ end
182
+
183
+ def resume
184
+ builder do |b|
185
+ b.use Call, state do |env, c|
186
+ case env[:machine_state]
187
+ when Vagrant::MachineState::NOT_CREATED_ID
188
+ next
189
+ when :running
190
+ c.use Message, :info, 'Machine is already running.'
191
+ when :frozen, :stopped
192
+ c.use Message, :info, 'Resuming machine...'
193
+ c.use LXD.action(:resume)
194
+ c.use SyncedFolders
195
+ c.use WaitForCommunicator
196
+ c.use Provision
197
+ else
198
+ c.use Message, :error, "Machine cannot be resumed while #{env[:machine_state]}."
199
+ end
200
+ end
201
+ end
202
+ end
203
+
204
+ def reload
205
+ builder do |b|
206
+ b.use Call, state do |env, c|
207
+ case env[:machine_state]
208
+ when Vagrant::MachineState::NOT_CREATED_ID
209
+ next
210
+ when :frozen, :running
211
+ c.use halt
212
+ end
213
+ c.use resume
214
+ end
215
+ end
216
+ end
217
+
218
+ def provision
219
+ builder do |b|
220
+ b.use Call, IsState, Vagrant::MachineState::NOT_CREATED_ID do |env, c|
221
+ if env[:result]
222
+ next
223
+ else
224
+ c.use Provision
225
+ end
226
+ end
227
+ end
228
+ end
229
+
230
+ def snapshot_list
231
+ builder do |b|
232
+ b.use Call, IsState, Vagrant::MachineState::NOT_CREATED_ID do |env, c|
233
+ if env[:result]
234
+ next
235
+ else
236
+ c.use LXD.action(:snapshot_list)
237
+ end
238
+ end
239
+ end
240
+ end
241
+
242
+ def snapshot_save
243
+ builder do |b|
244
+ b.use Call, IsState, Vagrant::MachineState::NOT_CREATED_ID do |env, c|
245
+ if env[:result]
246
+ next
247
+ else
248
+ c.use Message, :info, I18n.t('vagrant.actions.vm.snapshot.saving', name: env[:snapshot_name])
249
+ c.use LXD.action(:snapshot_save), env[:snapshot_name]
250
+ c.use Message, :success, I18n.t('vagrant.actions.vm.snapshot.saved', name: env[:snapshot_name])
251
+ end
252
+ end
253
+ end
254
+ end
255
+
256
+ def snapshot_restore
257
+ builder do |b|
258
+ b.use Call, IsState, Vagrant::MachineState::NOT_CREATED_ID do |env, c|
259
+ if env[:result]
260
+ next
261
+ else
262
+ c.use Message, :info, I18n.t('vagrant.actions.vm.snapshot.restoring', name: env[:snapshot_name])
263
+ c.use LXD.action(:snapshot_restore), env[:snapshot_name]
264
+ c.use Message, :success, I18n.t('vagrant.actions.vm.snapshot.restored', name: env[:snapshot_name])
265
+ c.use Call, IsEnvSet, :snapshot_delete do |env, d|
266
+ d.use snapshot_delete if env[:result]
267
+ end
268
+ end
269
+ end
270
+ end
271
+ end
272
+
273
+ def snapshot_delete
274
+ builder do |b|
275
+ b.use Call, IsState, Vagrant::MachineState::NOT_CREATED_ID do |env, c|
276
+ if env[:result]
277
+ next
278
+ else
279
+ c.use Message, :info, I18n.t('vagrant.actions.vm.snapshot.deleting', name: env[:snapshot_name])
280
+ c.use LXD.action(:snapshot_delete), env[:snapshot_name]
281
+ c.use Message, :info, I18n.t('vagrant.actions.vm.snapshot.deleted', name: env[:snapshot_name])
282
+ end
283
+ end
284
+ end
285
+ end
286
+
287
+ def state
288
+ builder { |b| b.use LXD.action(:state) }
289
+ end
290
+
291
+ def info
292
+ builder { |b| b.use LXD.action(:info) }
293
+ end
294
+
295
+ def ssh
296
+ builder { |b| b.use SSHExec }
297
+ end
298
+
299
+ def ssh_run
300
+ builder { |b| b.use SSHRun }
301
+ end
302
+
303
+ private
304
+
305
+ def builder
306
+ Vagrant::Action::Builder.new.tap do |b|
307
+ b.use ConfigValidate
308
+ b.use ConnectionValidate
309
+ yield b
310
+ end
311
+ end
312
+ end
313
+ end
314
+ end