class Cyclid::API::Plugins::LxdApi

LXD based transport

Attributes

exit_code[R]
exit_signal[R]

Public Class Methods

new(args = {}) click to toggle source
# File lib/cyclid/plugins/transport/lxdapi.rb, line 30
def initialize(args = {})
  args.symbolize_keys!

  Cyclid.logger.debug "args=#{args}"

  # Container name & a log target are required
  return false unless args.include?(:host) && \
                      args.include?(:log)

  @name = args[:host]
  @log = args[:log]
  Cyclid.logger.debug "log=#{@log}"

  @config = load_lxd_config(Cyclid.config.plugins)
  @client = Hyperkit::Client.new(api_endpoint: @config[:api],
                                 verify_ssl: @config[:verify_ssl],
                                 client_cert: @config[:client_cert],
                                 client_key: @config[:client_key])

  # Grab some data from the context
  Cyclid.logger.debug "lxdapi args=#{args.inspect}"
  ctx = args[:ctx]
  @base_env = { 'HOME' => ctx[:workspace],
                'TERM' => 'xterm-mono' }

  @username = ctx[:username]
end

Public Instance Methods

download(io, path) click to toggle source

Copy a data from remote file to a local IO object

# File lib/cyclid/plugins/transport/lxdapi.rb, line 131
def download(io, path)
  @client.pull_file(@name, path, io)
end
exec(cmd, args = {}) click to toggle source

Execute a command via. the LXD API

# File lib/cyclid/plugins/transport/lxdapi.rb, line 59
def exec(cmd, args = {})
  sudo = args[:sudo] || false
  path = args[:path]

  command = build_command(cmd, sudo, path)
  Cyclid.logger.debug "command=#{command}"

  # Ensure some important variables are set, like HOME & TERM
  env = @env || {}
  env = env.merge @base_env

  # Run the command...
  rc = @client.execute_command(@name,
                               command,
                               environment: env,
                               wait_for_websocket: true,
                               interactive: true,
                               sync: false)

  # ... and then connect a Websocket and read the output
  operation = rc[:id]
  ws_secret = rc[:metadata][:fds][:'0']
  ws_url = "#{@config[:api]}/1.0/operations/#{operation}/websocket?secret=#{ws_secret}"

  closed = false
  log = @log
  WebSocket::Client::Simple.connect ws_url do |ws|
    ws.on :message do |msg|
      close if msg.data.empty?
      # Strip out any XTerm control characters and convert lint endings
      data = msg.data.encode('US-ASCII',
                             invalid: :replace,
                             undef: :replace,
                             universal_newline: true)
                .squeeze("\n")
                .gsub(/\033\[(.?\d+\D?|\w+)/, '')

      log.write data
    end
    ws.on :open do
      Cyclid.logger.debug 'websocket opened'
      closed = false
    end
    ws.on :close do |e|
      Cyclid.logger.debug "websocket closed: #{e}"
      closed = true
    end
    ws.on :error do |e|
      Cyclid.logger.debug "websocket error: #{e}"
    end
  end

  # Wait until the Websocket thread has finished.
  loop do
    break if closed
    sleep 1
  end

  # Get exit status
  status = @client.operation(operation)
  Cyclid.logger.debug "status=#{status.inspect}"

  @exit_code = status[:metadata][:return]
  @exit_code.zero? ? true : false
end
upload(io, path) click to toggle source

Copy data from a local IO object to a remote file via. the API

# File lib/cyclid/plugins/transport/lxdapi.rb, line 126
def upload(io, path)
  @client.push_file(io, @name, path)
end

Private Instance Methods

build_command(cmd, sudo, path = nil) click to toggle source
# File lib/cyclid/plugins/transport/lxdapi.rb, line 163
def build_command(cmd, sudo, path = nil)
  command = []
  command << "cd #{path}" if path
  command << if @username == 'root'
               cmd
             elsif sudo
               "sudo -E -n $SHELL -l -c '#{cmd}'"
             else
               cmd
             end
  "sh -l -c '#{command.join(';')}'"
end
load_lxd_config(config) click to toggle source

Load the config for the LXD Builder plugin and set defaults if they're not in the config

# File lib/cyclid/plugins/transport/lxdapi.rb, line 142
def load_lxd_config(config)
  config.symbolize_keys!

  lxd_config = config[:lxd] || {}
  lxd_config.symbolize_keys!
  Cyclid.logger.debug "config=#{lxd_config}"

  raise 'the LXD API URL must be provided' \
    unless lxd_config.key? :api

  lxd_config[:client_cert] = File.join(%w[/ etc cyclid lxd_client.crt]) \
    unless lxd_config.key? :client_cert
  lxd_config[:client_key] = File.join(%w[/ etc cyclid lxd_client.key]) \
    unless lxd_config.key? :client_key

  lxd_config[:verify_ssl] = false \
    unless lxd_config.key? :verify_ssl

  lxd_config
end