Class: VagrantLXD::Driver

Inherits:
Object
  • Object
show all
Extended by:
MonitorMixin
Includes:
Vagrant::Util
Defined in:
lib/vagrant-lxd/driver.rb,
lib/vagrant-lxd/driver/certificate.rb

Defined Under Namespace

Classes: AuthenticationFailure, Certificate, CertificateGenerationFailure, ConnectionFailure, ContainerAlreadyExists, ContainerConfigurationFailure, ContainerCreationFailure, ContainerDeletionFailure, ContainerNotFound, DiskMountFailure, DiskUnmountFailure, DuplicateAttachmentFailure, ImageCreationFailure, ImageExportFailure, NetworkAddressAcquisitionTimeout, OperationTimeout, ProjectMiddleware, SnapshotNotFound

Constant Summary collapse

USER_AGENT =
"#{Version::DESCRIPTION} #{Version::VERSION} (#{Hyperkit::Default.user_agent})"
IMAGE_PROPERTIES =
{ description: "#{Version::DESCRIPTION} #{Version::VERSION}" }
NOT_CREATED =
Vagrant::MachineState::NOT_CREATED_ID

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(machine) ⇒ Driver

Returns a new instance of Driver.



165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
# File 'lib/vagrant-lxd/driver.rb', line 165

def initialize(machine)
  @machine = machine
  @image = machine.provider_config.image
  @timeout = machine.provider_config.timeout
  @api_endpoint = machine.provider_config.api_endpoint
  @config = machine.provider_config.config
  @devices = machine.provider_config.devices
  @environment = machine.provider_config.environment
  @nesting = machine.provider_config.nesting
  @privileged = machine.provider_config.privileged
  @ephemeral = machine.provider_config.ephemeral
  @profiles = machine.provider_config.profiles
  @project = machine.provider_config.project
  @name = machine.provider_config.name
  @client_certificate = machine.provider_config.client_certificate
  @client_key = machine.provider_config.client_key
  @vagrant_uid = machine.provider_config.vagrant_uid
  @vagrant_gid = machine.provider_config.vagrant_gid
  @logger = Log4r::Logger.new('vagrant::lxd::driver')
end

Instance Attribute Details

#api_endpointObject (readonly)

Returns the value of attribute api_endpoint.



150
151
152
# File 'lib/vagrant-lxd/driver.rb', line 150

def api_endpoint
  @api_endpoint
end

#client_certificateObject (readonly)

Returns the value of attribute client_certificate.



160
161
162
# File 'lib/vagrant-lxd/driver.rb', line 160

def client_certificate
  @client_certificate
end

#client_keyObject (readonly)

Returns the value of attribute client_key.



161
162
163
# File 'lib/vagrant-lxd/driver.rb', line 161

def client_key
  @client_key
end

#devicesObject (readonly)

Returns the value of attribute devices.



153
154
155
# File 'lib/vagrant-lxd/driver.rb', line 153

def devices
  @devices
end

#environmentObject (readonly)

Returns the value of attribute environment.



154
155
156
# File 'lib/vagrant-lxd/driver.rb', line 154

def environment
  @environment
end

#ephemeralObject (readonly)

Returns the value of attribute ephemeral.



155
156
157
# File 'lib/vagrant-lxd/driver.rb', line 155

def ephemeral
  @ephemeral
end

#nameObject (readonly)

Returns the value of attribute name.



151
152
153
# File 'lib/vagrant-lxd/driver.rb', line 151

def name
  @name
end

#nestingObject (readonly)

Returns the value of attribute nesting.



156
157
158
# File 'lib/vagrant-lxd/driver.rb', line 156

def nesting
  @nesting
end

#privilegedObject (readonly)

Returns the value of attribute privileged.



157
158
159
# File 'lib/vagrant-lxd/driver.rb', line 157

def privileged
  @privileged
end

#profilesObject (readonly)

Returns the value of attribute profiles.



158
159
160
# File 'lib/vagrant-lxd/driver.rb', line 158

def profiles
  @profiles
end

#projectObject (readonly)

Returns the value of attribute project.



159
160
161
# File 'lib/vagrant-lxd/driver.rb', line 159

def project
  @project
end

#timeoutObject (readonly)

Returns the value of attribute timeout.



152
153
154
# File 'lib/vagrant-lxd/driver.rb', line 152

def timeout
  @timeout
end

#vagrant_gidObject (readonly)

Returns the value of attribute vagrant_gid.



163
164
165
# File 'lib/vagrant-lxd/driver.rb', line 163

def vagrant_gid
  @vagrant_gid
end

#vagrant_uidObject (readonly)

Returns the value of attribute vagrant_uid.



162
163
164
# File 'lib/vagrant-lxd/driver.rb', line 162

def vagrant_uid
  @vagrant_uid
end

Instance Method Details

#attach(container) ⇒ Object



235
236
237
238
239
240
241
242
243
244
245
246
# File 'lib/vagrant-lxd/driver.rb', line 235

def attach(container)
  lxd.container(container) # Query LXD to make sure the container exists.

  if in_state? NOT_CREATED
    @machine.id = container
  else
    fail DuplicateAttachmentFailure, machine_name: @machine.name, container: container
  end
rescue Hyperkit::NotFound
  @machine.ui.error "Container doesn't exist: #{container}"
  fail ContainerNotFound, machine_name: @machine.name, container: container
end

#configureObject



359
360
361
362
363
364
365
366
367
# File 'lib/vagrant-lxd/driver.rb', line 359

def configure
  container = lxd.container(machine_id)
  container[:config] = container[:config].to_hash.merge(config)
  container[:devices] = container[:devices].to_hash.merge(devices)
  lxd.update_container(machine_id, container)
rescue Hyperkit::Error => e
  @machine.ui.error 'Failed to configure container'
  fail ContainerConfigurationFailure, machine_name: @machine.name, reason: e.reason
end

#createObject



288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
# File 'lib/vagrant-lxd/driver.rb', line 288

def create
  if in_state? NOT_CREATED
    fingerprint = image_from_box
    machine_id = generate_machine_id
    container = lxd.create_container(machine_id, devices: devices, ephemeral: ephemeral, fingerprint: fingerprint, config: config, profiles: profiles)
    @logger.debug 'Created container: ' << container.inspect
    @machine.id = machine_id
  end
rescue Hyperkit::Error => e
  lxd.delete_container(machine_id) rescue nil unless container.nil?
  lxd.delete_image(fingerprint) rescue nil unless fingerprint.nil?
  if e.reason =~ /Container '([^']+)' already exists/
    @machine.ui.error e.reason
    fail ContainerAlreadyExists, machine_name: @machine.name, container: $1
  else
    @machine.ui.error 'Failed to create container'
    fail ContainerCreationFailure, machine_name: @machine.name, reason: e.reason
  end
end

#destroyObject



342
343
344
345
346
347
348
349
# File 'lib/vagrant-lxd/driver.rb', line 342

def destroy
  if in_state? :stopped
    delete_image
    delete_container
  else
    @logger.debug "Skipped container destroy (#{machine_id} is not stopped)"
  end
end

#detachObject



248
249
250
# File 'lib/vagrant-lxd/driver.rb', line 248

def detach
  @machine.id = nil
end

#exec(command) ⇒ Object



398
399
400
401
402
403
404
# File 'lib/vagrant-lxd/driver.rb', line 398

def exec(command)
  operation = lxd.execute_command(machine_id, command, sync: false, record_output: true)
  wait_for_operation(operation)..tap do |result|
    result.output[:'1'] = lxd.get(result.output[:'1'])
    result.output[:'2'] = lxd.get(result.output[:'2'])
  end
end

#halt(force = false) ⇒ Object



320
321
322
323
324
325
326
327
328
329
330
331
# File 'lib/vagrant-lxd/driver.rb', line 320

def halt(force = false)
  if in_state? :running, :frozen
    lxd.stop_container(machine_id, timeout: timeout, force: force)
  end
rescue Hyperkit::BadRequest
  if force
    fail OperationTimeout, time_limit: timeout, operation: 'stop', machine_id: machine_id
  else
    @machine.ui.warn "Container failed to stop within #{timeout} seconds, forcing shutdown..."
    halt(true)
  end
end

#infoObject



406
407
408
409
410
411
412
413
# File 'lib/vagrant-lxd/driver.rb', line 406

def info
  if in_state? :running, :frozen
    {
      host: ipv4_address,
      port: ipv4_port,
    }
  end
end

#mount(name, options) ⇒ Object



203
204
205
206
207
208
209
210
211
212
# File 'lib/vagrant-lxd/driver.rb', line 203

def mount(name, options)
  container = lxd.container(machine_id)
  devices = container[:devices].to_hash
  devices[name] = { type: 'disk', path: options[:guestpath], source: options[:hostpath] }.merge(options[:config])
  container[:devices] = devices
  lxd.update_container(machine_id, container)
rescue Hyperkit::BadRequest => e
  @machine.ui.error 'Failed to mount synced folder'
  fail DiskMountFailure, machine_name: @machine.name, guestpath: options[:guestpath], reason: e.reason
end

#mounted?(name, options) ⇒ Boolean

Returns:

  • (Boolean)


214
215
216
217
218
219
220
221
222
223
224
# File 'lib/vagrant-lxd/driver.rb', line 214

def mounted?(name, options)
  container = lxd.container(machine_id)
  devices = container[:devices].to_hash
  name = name.to_sym
  begin
    devices[name] and
    devices[name][:type] == 'disk' and
    devices[name][:path] == options[:guestpath] and
    devices[name][:source] == options[:hostpath]
  end
end

#packageObject



351
352
353
354
355
356
357
# File 'lib/vagrant-lxd/driver.rb', line 351

def package
  if in_state? :stopped
    create_package_directory
  else
    @logger.debug "Skipped packaging (#{machine_id} is not stopped)"
  end
end

#reconnectObject

When a container is restarted, the ‘forkproxy` processes that manage its proxy devices will persist but will not reliably recreate container-side listeners for devices that are configured with “bind=container”. Removing and re-creating the devices forces the proxy processes to be recreated, ensuring the listeners are as well.



374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
# File 'lib/vagrant-lxd/driver.rb', line 374

def reconnect
  # select proxy devices
  container = lxd.container(machine_id)
  devices = container[:devices].to_hash
  proxies = devices.select { |_, d| d[:type] == 'proxy' }

  # bail if there's nothing to do
  return if proxies.empty?

  # TODO move messaging into a dedicated action
  @machine.ui.info 'Reconnecting proxy devices...'

  # remove proxy devices
  container[:devices] = devices.except(*proxies.keys)
  lxd.update_container(machine_id, container)

  # restore all devices (including proxies)
  container[:devices] = devices.merge(proxies)
  lxd.update_container(machine_id, container)
rescue Hyperkit::Error => e
  @machine.ui.error 'Failed to connect proxy devices'
  fail ContainerConfigurationFailure, machine_name: @machine.name, reason: e.reason
end

#resumeObject



308
309
310
311
312
313
314
315
316
317
318
# File 'lib/vagrant-lxd/driver.rb', line 308

def resume
  case state
  when :stopped
    lxd.start_container(machine_id)
  when :frozen
    lxd.unfreeze_container(machine_id, timeout: timeout)
  end
rescue Hyperkit::BadRequest
  @machine.ui.warn "Container failed to start within #{timeout} seconds"
  fail OperationTimeout, time_limit: timeout, operation: 'start', machine_id: machine_id
end

#snapshot_delete(name) ⇒ Object



274
275
276
277
278
# File 'lib/vagrant-lxd/driver.rb', line 274

def snapshot_delete(name)
  lxd.delete_snapshot(machine_id, name)
rescue Hyperkit::NotFound
  @logger.warn 'No such snapshot: ' << name
end

#snapshot_listObject

The following methods correspond directly to middleware actions.



256
257
258
# File 'lib/vagrant-lxd/driver.rb', line 256

def snapshot_list
  lxd.snapshots(machine_id)
end

#snapshot_restore(name) ⇒ Object



266
267
268
269
270
271
272
# File 'lib/vagrant-lxd/driver.rb', line 266

def snapshot_restore(name)
  operation = lxd.restore_snapshot(machine_id, name, sync: false)
  wait_for_operation(operation)
rescue Hyperkit::BadRequest
  @logger.warn 'Snapshot restoration failed: ' << name
  fail SnapshotNotFound, machine: @machine.name, snapshot_name: name
end

#snapshot_save(name) ⇒ Object



260
261
262
263
264
# File 'lib/vagrant-lxd/driver.rb', line 260

def snapshot_save(name)
  snapshot_delete(name) # noops if the snapshot doesn't exist
  operation = lxd.create_snapshot(machine_id, name, sync: false)
  wait_for_operation(operation)
end

#stateObject



280
281
282
283
284
285
286
# File 'lib/vagrant-lxd/driver.rb', line 280

def state
  return NOT_CREATED if machine_id.nil?
  container_state = lxd.container_state(machine_id)
  container_state[:status].downcase.to_sym
rescue Hyperkit::NotFound
  NOT_CREATED
end

#suspendObject



333
334
335
336
337
338
339
340
# File 'lib/vagrant-lxd/driver.rb', line 333

def suspend
  if in_state? :running
    lxd.freeze_container(machine_id, timeout: timeout)
  end
rescue Hyperkit::BadRequest
  @machine.ui.warn "Container failed to suspend within #{timeout} seconds"
  fail OperationTimeout, time_limit: timeout, operation: 'info', machine_id: machine_id
end

#synced_folders_usable?Boolean

Returns:

  • (Boolean)


191
192
193
194
195
196
197
198
199
200
201
# File 'lib/vagrant-lxd/driver.rb', line 191

def synced_folders_usable?
  # Check whether we've registered an idmap for the current user.
  raw_idmap = config[:'raw.idmap']
  begin
    raw_idmap and
    id_in_map?(Process.uid, 'uid', raw_idmap) and
    id_in_map?(Process.gid, 'gid', raw_idmap)
  end
rescue Vagrant::Errors::ProviderNotUsable
  false
end

#unmount(name, options) ⇒ Object



226
227
228
229
230
231
232
233
# File 'lib/vagrant-lxd/driver.rb', line 226

def unmount(name, options)
  container = lxd.container(machine_id)
  container[:devices] = container[:devices].to_hash.except(name.to_sym)
  lxd.update_container(machine_id, container)
rescue Hyperkit::BadRequest => e
  @machine.ui.error 'Failed to unmount synced folder'
  fail DiskUnmountFailure, machine_name: @machine.name, guestpath: options[:guestpath], reason: e.reason
end

#validate!Object



186
187
188
189
# File 'lib/vagrant-lxd/driver.rb', line 186

def validate!
  raise error(ConnectionFailure) unless connection_usable?
  raise error(AuthenticationFailure) unless authentication_usable?
end