lxd-common 0.6.0 → 0.7.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 2862f300294934ad732a85a91460a901ddca30a1371b59f3c52d4823f845ba22
4
- data.tar.gz: cf9f7a414093dce415a457d144ea222ae8bc5e106d18df00a9d0510c61876ce2
3
+ metadata.gz: 031530f1093174e61e18c8a58e85805376bbc9b80368c800f7cc28cc3476e8ac
4
+ data.tar.gz: 0dea2d0bdb81700c0b0c05b990620fef2ae999b0828d1ddee0714e54a914909f
5
5
  SHA512:
6
- metadata.gz: d21f7a64be74067ff98a0170f0acb5006c1827000c3cc5dbe87f944c74fd362fd1b20202b3936e36ea84544b53159122b565732065a63d764f2bee5838a185fc
7
- data.tar.gz: 91f6e12a53b3255b53f04dd875777b79c548d01aa4c88fcae6e5a240a45071704e1ab62c84480e56bb251627e7054df675a9cebe697c7e5a93a2d0485f346267
6
+ metadata.gz: 677d8545906f020bb8a485362cc39e8b7d809a96822fdeb05225d65f9da79bd1865c5105c3b3706946fc29ed09d3d0cbab4efb8c67c4acdd8724d6f41184f75f
7
+ data.tar.gz: 18772ed5c102a70bc3e0fdacb0742c7de807d210e339ba83d1db5efc61fc529e36ef423f364f630a3215564e99225b2771572d3699e231617fb2dbad3643f8a4
data/.travis.yml CHANGED
@@ -2,8 +2,8 @@ sudo: false
2
2
  language: ruby
3
3
  rvm:
4
4
  - 2.1.0
5
- - 2.3.1
6
- - 2.4.2
5
+ - 2.4.3
6
+ - 2.5.0
7
7
  - ruby-head
8
8
  matrix:
9
9
  allow_failures:
@@ -32,4 +32,4 @@ deploy:
32
32
  gem: lxd-common
33
33
  on:
34
34
  tags: true
35
- rvm: 2.4.2
35
+ rvm: 2.4.3
@@ -103,7 +103,7 @@ module NexusSW
103
103
  res = inner_transport.execute("lxc list #{container_id} --format=json")
104
104
  res.error!
105
105
  JSON.parse(res.stdout).each do |c|
106
- return convert_keys(c.except('state')) if c['name'] == container_id
106
+ return convert_keys(c.reject { |k, _| k == 'state' }) if c['name'] == container_id
107
107
  end
108
108
  nil
109
109
  end
@@ -1,6 +1,6 @@
1
+ require 'nexussw/lxd/rest_api'
1
2
  require 'nexussw/lxd/driver/mixins/helpers/wait'
2
3
  require 'nexussw/lxd/transport/rest'
3
- require 'hyperkit'
4
4
 
5
5
  module NexusSW
6
6
  module LXD
@@ -10,27 +10,26 @@ module NexusSW
10
10
  # PARITY note: CLI functions are on an indefinite timeout by default, yet we have a 2 minute socket read timeout
11
11
  # Leaving it alone, for now, on calls that are quick in nature
12
12
  # Adapting on known long running calls such as create, stop, execute
13
- # REQUEST_TIMEOUT = 120 # upstream default: 120
14
13
  def initialize(rest_endpoint, driver_options = {}, inner_driver = nil)
15
14
  @rest_endpoint = rest_endpoint
16
15
  @driver_options = driver_options
17
- hkoptions = (driver_options || {}).merge(
16
+ apioptions = (driver_options || {}).merge(
18
17
  api_endpoint: rest_endpoint,
19
18
  auto_sync: true
20
19
  )
21
- @hk = inner_driver || Hyperkit::Client.new(hkoptions)
20
+ @api = inner_driver || RestAPI.new(apioptions)
22
21
  end
23
22
 
24
- attr_reader :hk, :rest_endpoint, :driver_options
23
+ attr_reader :api, :rest_endpoint, :driver_options
25
24
 
26
25
  include Helpers::WaitMixin
27
26
 
28
27
  def server_info
29
- @server_info ||= hk.get('/1.0')[:metadata]
28
+ @server_info ||= api.get('/1.0')[:metadata]
30
29
  end
31
30
 
32
31
  def transport_for(container_name)
33
- Transport::Rest.new container_name, info: server_info, connection: hk, driver_options: driver_options, rest_endpoint: rest_endpoint
32
+ Transport::Rest.new container_name, info: server_info, connection: api, driver_options: driver_options, rest_endpoint: rest_endpoint
34
33
  end
35
34
 
36
35
  def create_container(container_name, container_options = {})
@@ -40,7 +39,7 @@ module NexusSW
40
39
  end
41
40
  # parity note: CLI will run indefinitely rather than timeout hence the 0 timeout
42
41
  retry_forever do
43
- @hk.create_container(container_name, container_options.merge(sync: false))
42
+ api.create_container(container_name, container_options.merge(sync: false))
44
43
  end
45
44
  start_container container_name
46
45
  container_name
@@ -49,7 +48,7 @@ module NexusSW
49
48
  def start_container(container_id)
50
49
  return if container_status(container_id) == 'running'
51
50
  retry_forever do
52
- @hk.start_container(container_id, sync: false)
51
+ api.start_container(container_id, sync: false)
53
52
  end
54
53
  wait_for_status container_id, 'running'
55
54
  end
@@ -57,7 +56,7 @@ module NexusSW
57
56
  def stop_container(container_id, options = {})
58
57
  return if container_status(container_id) == 'stopped'
59
58
  if options[:force]
60
- @hk.stop_container(container_id, force: true)
59
+ api.stop_container(container_id, force: true)
61
60
  else
62
61
  last_id = nil
63
62
  use_last = false
@@ -67,14 +66,14 @@ module NexusSW
67
66
  unless use_last
68
67
  # Keep resubmitting until the server complains (Stops will be ignored/hang if init is not yet listening for SIGPWR i.e. recently started)
69
68
  begin
70
- last_id = @hk.stop_container(container_id, sync: false)[:id]
71
- rescue Hyperkit::BadRequest # Happens if a stop command has previously been accepted as well as other reasons. handle that on next line
69
+ last_id = api.stop_container(container_id, sync: false)[:metadata][:id]
70
+ rescue NexusSW::LXD::RestAPI::Error::BadRequest # Happens if a stop command has previously been accepted as well as other reasons. handle that on next line
72
71
  # if we have a last_id then a prior stop command has successfully initiated so we'll just wait on that one
73
72
  raise unless last_id # rubocop:disable Metrics/BlockNesting
74
73
  use_last = true
75
74
  end
76
75
  end
77
- @hk.wait_for_operation last_id # , options[:retry_interval]
76
+ api.wait_for_operation last_id # , options[:retry_interval]
78
77
  rescue Faraday::TimeoutError => e
79
78
  return if container_status(container_id) == 'stopped'
80
79
  raise Timeout::Retry.new e # if options[:retry_interval] # rubocop:disable Style/RaiseArgs
@@ -89,33 +88,35 @@ module NexusSW
89
88
  stop_container container_id, force: true
90
89
 
91
90
  # ISSUE 17: something upstream is causing a double-tap on the REST endpoint
92
- begin
93
- @hk.delete_container container_id
94
- rescue ::Faraday::ConnectionFailed, ::Hyperkit::BadRequest
95
- LXD.with_timeout_and_retries timeout: 120 do
96
- loop do
97
- return unless container_exists? container_id
98
- sleep 0.3
99
- end
100
- end
101
- end
91
+
92
+ # trial return to normal
93
+ # begin
94
+ api.delete_container container_id
95
+ # rescue ::Faraday::ConnectionFailed, ::NexusSW::LXD::RestAPI::Error::BadRequest
96
+ # LXD.with_timeout_and_retries timeout: 120 do
97
+ # loop do
98
+ # return unless container_exists? container_id
99
+ # sleep 0.3
100
+ # end
101
+ # end
102
+ # end
102
103
  end
103
104
 
104
105
  def container_status(container_id)
105
- STATUS_CODES[container(container_id)[:status_code].to_i]
106
+ STATUS_CODES[api.container(container_id)[:metadata][:status_code].to_i]
106
107
  end
107
108
 
108
109
  def container_state(container_id)
109
110
  return nil unless container_status(container_id) == 'running' # Parity with CLI
110
- @hk.container_state(container_id)
111
+ api.container_state(container_id)[:metadata]
111
112
  end
112
113
 
113
114
  def container(container_id)
114
- @hk.container container_id
115
+ api.container(container_id)[:metadata]
115
116
  end
116
117
 
117
118
  def container_exists?(container_id)
118
- hk.containers.include? container_id
119
+ api.containers[:metadata].map { |url| url.split('/').last }.include? container_id
119
120
  end
120
121
 
121
122
  protected
@@ -133,7 +134,7 @@ module NexusSW
133
134
  retval = yield
134
135
  LXD.with_timeout_and_retries timeout: 0 do
135
136
  begin
136
- @hk.wait_for_operation retval[:id]
137
+ api.wait_for_operation retval[:metadata][:id]
137
138
  rescue Faraday::TimeoutError => e
138
139
  raise Timeout::Retry.new e # rubocop:disable Style/RaiseArgs
139
140
  end
@@ -0,0 +1,98 @@
1
+ require 'faraday'
2
+ require 'json'
3
+ require 'openssl'
4
+
5
+ module NexusSW
6
+ module LXD
7
+ class RestAPI
8
+ module Connection
9
+ def get(relative_url, &block)
10
+ send_request :get, relative_url, &block
11
+ end
12
+
13
+ def put(relative_url, content)
14
+ send_request :put, relative_url, content
15
+ end
16
+
17
+ def patch(relative_url, content)
18
+ send_request :patch, relative_url, content
19
+ end
20
+
21
+ def post(relative_url, content, &block)
22
+ send_request :post, relative_url, content, &block
23
+ end
24
+
25
+ def delete(relative_url)
26
+ send_request :delete, relative_url
27
+ end
28
+
29
+ private
30
+
31
+ def connection(&block)
32
+ return @conn if @conn
33
+
34
+ opts = {
35
+ url: baseurl,
36
+ ssl: {
37
+ verify: verify_ssl,
38
+ client_cert: OpenSSL::X509::Certificate.new(cert),
39
+ client_key: OpenSSL::PKey::RSA.new(key),
40
+ },
41
+ }
42
+
43
+ @conn = Faraday.new opts, &block
44
+ end
45
+
46
+ def baseurl
47
+ api_options[:api_endpoint]
48
+ end
49
+
50
+ def ssl_opts
51
+ api_options[:ssl] || {}
52
+ end
53
+
54
+ def cert
55
+ File.read(ssl_opts[:client_cert] || "#{ENV['HOME']}/.config/lxc/client.crt")
56
+ end
57
+
58
+ def key
59
+ File.read(ssl_opts[:client_key] || "#{ENV['HOME']}/.config/lxc/client.key")
60
+ end
61
+
62
+ def verify_ssl
63
+ return ssl_opts[:verify] if ssl_opts.key? :verify
64
+ api_options[:verify_ssl].nil? ? true : api_options[:verify_ssl]
65
+ end
66
+
67
+ def parse_response(response)
68
+ LXD.symbolize_keys(JSON.parse(response.body))
69
+ end
70
+
71
+ def send_request(verb, relative_url, content = nil)
72
+ response = connection.send(verb) do |req|
73
+ req.url relative_url
74
+ if content.is_a? Hash
75
+ req.headers['Content-Type'] = 'application/json'
76
+ req.body = content.to_json
77
+ elsif content # Only upon file upload
78
+ req.headers['Content-Type'] = 'application/octet-stream'
79
+ req.headers['X-LXD-uid'] = '0'
80
+ req.headers['X-LXD-gid'] = '0'
81
+ req.headers['X-LXD-mode'] = '0600'
82
+ req.body = content.to_s
83
+ end
84
+ end
85
+ if response.status >= 400
86
+ err = JSON.parse(response.body)
87
+ case err['error_code']
88
+ when 404 then raise RestAPI::Error::NotFound, err['error']
89
+ when 400 then raise RestAPI::Error::BadRequest, err['error']
90
+ else raise "Error #{err['error_code']}: #{err['error']}"
91
+ end
92
+ end
93
+ block_given? ? yield(response) : parse_response(response)
94
+ end
95
+ end
96
+ end
97
+ end
98
+ end
@@ -0,0 +1,9 @@
1
+ class NexusSW::LXD::RestAPI
2
+ class Error < RuntimeError
3
+ class NotFound < Error
4
+ end
5
+
6
+ class BadRequest < Error
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,130 @@
1
+ require 'nexussw/lxd/rest_api/connection'
2
+ require 'nexussw/lxd/rest_api/errors'
3
+ require 'shellwords'
4
+
5
+ module NexusSW
6
+ module LXD
7
+ class RestAPI
8
+ def initialize(api_options)
9
+ @api_options = api_options
10
+ end
11
+
12
+ include RestAPI::Connection
13
+
14
+ def create_container(container_name, options)
15
+ options, sync = parse_options options
16
+ options[:config] = convert_bools(options[:config]) if options.key? :config
17
+ handle_async post('/1.0/containers', create_source(options).merge(name: container_name)), sync
18
+ end
19
+
20
+ def execute_command(container_name, command, options)
21
+ options, sync = parse_options options
22
+ command = command.shellsplit if command.is_a? String
23
+ handle_async post("/1.0/containers/#{container_name}/exec", options.merge(command: command)), sync
24
+ end
25
+
26
+ def log(container_name, log_name)
27
+ get "/1.0/containers/#{container_name}/logs/#{log_name}" do |response|
28
+ return response.body
29
+ end
30
+ end
31
+
32
+ def delete_log(container_name, log_name)
33
+ delete "/1.0/containers/#{container_name}/logs/#{log_name}"
34
+ end
35
+
36
+ def start_container(container_name, options)
37
+ options, sync = parse_options options
38
+ handle_async put("/1.0/containers/#{container_name}/state", options.merge(action: 'start')), sync
39
+ end
40
+
41
+ def stop_container(container_name, options)
42
+ options, sync = parse_options options
43
+ handle_async put("/1.0/containers/#{container_name}/state", options.merge(action: 'stop')), sync
44
+ end
45
+
46
+ def delete_container(container_name, options = {})
47
+ handle_async delete("/1.0/containers/#{container_name}"), options[:sync]
48
+ end
49
+
50
+ def read_file(container_name, path)
51
+ get "/1.0/containers/#{container_name}/files?path=#{path}" do |response|
52
+ return response.body
53
+ end
54
+ end
55
+
56
+ def write_file(container_name, path, options)
57
+ post "/1.0/containers/#{container_name}/files?path=#{path}", options[:content]
58
+ end
59
+
60
+ def push_file(local_path, container_name, remote_path)
61
+ write_file container_name, remote_path, content: IO.binread(local_path)
62
+ end
63
+
64
+ def pull_file(container_name, remote_path, local_path)
65
+ IO.binwrite(local_path, read_file(container_name, remote_path))
66
+ end
67
+
68
+ def container_state(container_name)
69
+ get "/1.0/containers/#{container_name}/state"
70
+ end
71
+
72
+ def container(container_name)
73
+ exceptkeys = %w(config expanded_config)
74
+ get "/1.0/containers/#{container_name}" do |response|
75
+ retval = JSON.parse(response.body)
76
+ lift = retval['metadata'].select { |k, _| exceptkeys.include? k }
77
+ retval['metadata'].delete_if { |k, _| exceptkeys.include? k }
78
+ retval = LXD.symbolize_keys(retval)
79
+ retval[:metadata][:config] = lift['config'] if lift.key? 'config'
80
+ retval[:metadata][:expanded_config] = lift['expanded_config'] if lift.key? 'expanded_config'
81
+ return retval
82
+ end
83
+ end
84
+
85
+ def containers
86
+ get('/1.0/containers')
87
+ end
88
+
89
+ def wait_for_operation(operation_id)
90
+ get "/1.0/operations/#{operation_id}/wait" do |response|
91
+ LXD.symbolize_keys(JSON.parse(response.body))
92
+ end
93
+ end
94
+
95
+ private
96
+
97
+ attr_reader :api_options
98
+
99
+ def handle_async(data, sync)
100
+ return data if sync == false
101
+ wait_for_operation data[:metadata][:id]
102
+ end
103
+
104
+ def parse_options(options)
105
+ sync = options[:sync]
106
+ [options.delete_if { |k, _| k == :sync }, sync]
107
+ end
108
+
109
+ def create_source(options)
110
+ moveprops = [:type, :alias, :fingerprint, :properties, :protocol, :server]
111
+ options.dup.tap do |retval|
112
+ retval[:source] = { type: 'image', mode: 'pull' }.merge(retval.select { |k, _| moveprops.include? k }) unless retval.key? :source
113
+ retval.delete_if { |k, _| moveprops.include? k }
114
+ end
115
+ end
116
+
117
+ def convert_bools(hash)
118
+ {}.tap do |retval|
119
+ hash.each do |k, v|
120
+ retval[k] = case v
121
+ when true then 'true'
122
+ when false then 'false'
123
+ else v.is_a?(Hash) ? convert_bools(v) : v
124
+ end
125
+ end
126
+ end
127
+ end
128
+ end
129
+ end
130
+ end
@@ -23,7 +23,8 @@ module NexusSW
23
23
  mycommand = command.is_a?(Array) ? command.join(' ') : command
24
24
  subcommand = options[:subcommand] || "exec #{container_name} --"
25
25
  mycommand = "lxc #{subcommand} #{mycommand}"
26
- options = options.except :subcommand if options.key? :subcommand
26
+ # options = options.except :subcommand if options.key? :subcommand
27
+ options = options.reject { |k, _| k == :subcommand }
27
28
  # We would have competing timeout logic depending on who the inner transport is
28
29
  # I'll just let rest & local do the timeouts, and if inner is a chef sourced transport, they have timeout logic of their own
29
30
  # with_timeout_and_retries(options) do
@@ -1,3 +1,5 @@
1
+ require 'nexussw/lxd/rest_api/errors'
2
+
1
3
  module NexusSW
2
4
  module LXD
3
5
  class Transport
@@ -26,7 +28,7 @@ module NexusSW
26
28
  msg = "Error: '#{command}' failed with exit code #{exitstatus}.\n"
27
29
  msg += "STDOUT: #{stdout}" if stdout.is_a?(String) && !stdout.empty?
28
30
  msg += "STDERR: #{stderr}" if stderr.is_a?(String) && !stderr.empty?
29
- raise msg
31
+ raise ::NexusSW::LXD::RestAPI::Error, msg
30
32
  end
31
33
  end
32
34
 
@@ -47,8 +49,8 @@ module NexusSW
47
49
  end
48
50
 
49
51
  class InteractiveResult < ExecuteResult
50
- def initialize(command, options, exitstatus, stdin, thread = nil)
51
- super(command, options, exitstatus)
52
+ def initialize(command, options, stdin, thread = nil)
53
+ super(command, options, nil)
52
54
  @stdin = stdin
53
55
  @thread = thread
54
56
  end
@@ -36,7 +36,8 @@ module NexusSW
36
36
  # TODO: serious: make sure the tar extract does an overwrite of existing files
37
37
  # multiple converge support as well as CI cycle/dev updated files get updated instead of .1 suffixed (?)
38
38
  # I think I need a flag (it's been a while)
39
- execute("bash -c 'mkdir -p #{path} && cd #{path} && tar -xf #{fname} && rm -rf #{fname}'").error!
39
+ # TODO: explore the chmod & make the same as if uploaded via CLI
40
+ execute("bash -c 'mkdir -p #{path} && cd #{path} && tar -xf #{fname} && rm -rf #{fname} && chmod -R 600 #{File.basename(local_path)}'").error!
40
41
  ensure
41
42
  tfile.unlink
42
43
  end
@@ -48,10 +49,10 @@ module NexusSW
48
49
  return false if @can_archive == false
49
50
  @can_archive ||= begin
50
51
  # I don't want to code tarball logic into the mock transport
51
- return false if respond_to?(:hk) && hk.respond_to?(:mock)
52
+ return false if respond_to?(:api) && api.respond_to?(:mock)
52
53
  return false if respond_to?(:inner_transport) && inner_transport.respond_to?(:mock)
53
54
  return false if respond_to?(:inner_transport) && inner_transport.respond_to?(:inner_transport) && inner_transport.inner_transport.respond_to?(:mock)
54
- return false if respond_to?(:inner_transport) && inner_transport.respond_to?(:hk) && inner_transport.hk.respond_to?(:mock)
55
+ return false if respond_to?(:inner_transport) && inner_transport.respond_to?(:api) && inner_transport.api.respond_to?(:mock)
55
56
  `tar --version`
56
57
  true
57
58
  rescue
@@ -21,7 +21,7 @@ module NexusSW
21
21
  Open3.popen3(command) do |stdin, stdout, stderr, th|
22
22
  if options[:capture] == :interactive
23
23
  # return immediately if interactive so that stdin may be used
24
- return Helpers::ExecuteMixin::InteractiveResult.new(command, options, -1, stdin, th).tap do |active|
24
+ return Helpers::ExecuteMixin::InteractiveResult.new(command, options, stdin, th).tap do |active|
25
25
  chunk_callback(stdout, stderr) do |stdout_chunk, stderr_chunk|
26
26
  active.send_output stdout_chunk if stdout_chunk
27
27
  active.send_output stderr_chunk if stderr_chunk
@@ -2,6 +2,7 @@ require 'nexussw/lxd/transport/mixins/helpers/execute'
2
2
  require 'nexussw/lxd/transport/mixins/helpers/upload_folder'
3
3
  require 'nio/websocket'
4
4
  require 'tempfile'
5
+ require 'json'
5
6
 
6
7
  module NexusSW
7
8
  module LXD
@@ -13,11 +14,11 @@ module NexusSW
13
14
  @config = config
14
15
  @rest_endpoint = config[:rest_endpoint]
15
16
  @driver_options = config[:driver_options]
16
- @hk = config[:connection]
17
- raise 'The rest transport requires the following keys: { :connection, :driver_options, :rest_endpoint }' unless @rest_endpoint && @hk && @driver_options
17
+ @api = config[:connection]
18
+ raise 'The rest transport requires the following keys: { :connection, :driver_options, :rest_endpoint }' unless @rest_endpoint && @api && @driver_options
18
19
  end
19
20
 
20
- attr_reader :hk, :rest_endpoint, :container_name, :config
21
+ attr_reader :api, :rest_endpoint, :container_name, :config
21
22
 
22
23
  include Helpers::ExecuteMixin
23
24
  include Helpers::UploadFolder
@@ -62,43 +63,43 @@ module NexusSW
62
63
  backchannel = nil
63
64
  getlogs = false
64
65
  if block_given? && (options[:capture] || !config[:info][:api_extensions].include?('container_exec_recording'))
65
- hkopts = { wait_for_websocket: true, interactive: false, sync: false }
66
- hkopts[:interactive] = true if options[:capture] == :interactive
67
- retval = hk.execute_command(container_name, command, hkopts)
66
+ apiopts = { :'wait-for-websocket' => true, interactive: false, sync: false }
67
+ apiopts[:interactive] = true if options[:capture] == :interactive
68
+ retval = api.execute_command(container_name, command, apiopts)[:metadata]
68
69
  opid = retval[:id]
69
70
  backchannel = options[:capture] == :interactive ? ws_connect(opid, retval[:metadata][:fds]) : ws_connect(opid, retval[:metadata][:fds], &block)
70
71
 
71
72
  # patch for interactive session
72
- return Helpers::ExecuteMixin::InteractiveResult.new(command, options, -1, StdinStub.pipe(backchannel.waitlist[:'0']), backchannel).tap do |active|
73
+ return Helpers::ExecuteMixin::InteractiveResult.new(command, options, StdinStub.pipe(backchannel.waitlist[:'0']), backchannel).tap do |active|
73
74
  backchannel.callback = proc do |stdout|
74
75
  active.send_output stdout
75
76
  end
76
77
  yield active
77
78
  backchannel.exit if backchannel.respond_to? :exit
78
- retval = hk.wait_for_operation opid
79
+ retval = api.wait_for_operation opid
79
80
  active.exitstatus = retval[:metadata][:return].to_i
80
81
  end if options[:capture] == :interactive
81
82
  elsif block_given? && config[:info][:api_extensions].include?('container_exec_recording')
82
83
  getlogs = true
83
- retval = hk.execute_command(container_name, command, record_output: true, interactive: false, sync: false)
84
- opid = retval[:id]
84
+ retval = api.execute_command(container_name, command, :'record-output' => true, interactive: false, sync: false)
85
+ opid = retval[:metadata][:id]
85
86
  else
86
- opid = hk.execute_command(container_name, command, sync: false)[:id]
87
+ opid = api.execute_command(container_name, command, sync: false)[:metadata][:id]
87
88
  end
88
89
  LXD.with_timeout_and_retries({ timeout: 0 }.merge(options)) do
89
90
  begin
90
- retval = hk.wait_for_operation opid
91
+ retval = api.wait_for_operation(opid)[:metadata]
91
92
  backchannel.join if backchannel.respond_to? :join
92
93
  if getlogs
93
94
  begin
94
95
  stdout_log = retval[:metadata][:output][:'1'].split('/').last
95
96
  stderr_log = retval[:metadata][:output][:'2'].split('/').last
96
- stdout = hk.log container_name, stdout_log
97
- stderr = hk.log container_name, stderr_log
97
+ stdout = api.log container_name, stdout_log
98
+ stderr = api.log container_name, stderr_log
98
99
  yield stdout, stderr
99
- ensure
100
- hk.delete_log container_name, stdout_log
101
- hk.delete_log container_name, stderr_log
100
+
101
+ api.delete_log container_name, stdout_log
102
+ api.delete_log container_name, stderr_log
102
103
  end
103
104
  end
104
105
  return Helpers::ExecuteMixin::ExecuteResult.new command, options, retval[:metadata][:return].to_i
@@ -108,23 +109,21 @@ module NexusSW
108
109
  end
109
110
  end
110
111
 
111
- # '' instead of Hyperkit::NotFound is a chef-provisioning expectation - at this level we'll let the exception propagate
112
+ # empty '' instead of an exception is a chef-provisioning expectation - at this level we'll let the exception propagate
112
113
  def read_file(path)
113
- hk.read_file container_name, path
114
- # rescue ::Hyperkit::NotFound
115
- # return ''
114
+ api.read_file container_name, path
116
115
  end
117
116
 
118
117
  def write_file(path, content)
119
- hk.write_file container_name, path, content: content
118
+ api.write_file container_name, path, content: content
120
119
  end
121
120
 
122
121
  def download_file(path, local_path)
123
- hk.pull_file container_name, path, local_path
122
+ api.pull_file container_name, path, local_path
124
123
  end
125
124
 
126
125
  def upload_file(local_path, path)
127
- # return hk.push_file(local_path, container_name, path)
126
+ # return api.push_file(local_path, container_name, path)
128
127
  write_file(path, IO.binread(local_path))
129
128
  end
130
129
 
@@ -182,6 +181,28 @@ module NexusSW
182
181
  sleep 0.1
183
182
  end
184
183
  end
184
+
185
+ def window_resize(width, height)
186
+ send_control_msg 'window-resize', width: width.to_s, height: height.to_s
187
+ end
188
+
189
+ def signal(signum)
190
+ send_control_msg 'signal', signum
191
+ end
192
+
193
+ private
194
+
195
+ def send_control_msg(message, val)
196
+ msg = {}.tap do |retval|
197
+ retval['command'] = message
198
+ case message
199
+ when 'window-resize' then retval['args'] = val
200
+ when 'signal' then retval['signal'] = val.to_i
201
+ end
202
+ end.to_json
203
+
204
+ waitlist[:control].binary msg
205
+ end
185
206
  end
186
207
 
187
208
  def ws_connect(opid, endpoints, &block)
@@ -1,5 +1,5 @@
1
1
  module NexusSW
2
2
  module LXD
3
- VERSION = '0.6.0'.freeze
3
+ VERSION = '0.7.0'.freeze
4
4
  end
5
5
  end
data/lib/nexussw/lxd.rb CHANGED
@@ -28,5 +28,16 @@ module NexusSW
28
28
  end
29
29
  end
30
30
  end
31
+
32
+ def self.symbolize_keys(hash)
33
+ {}.tap do |retval|
34
+ hash.each do |k, v|
35
+ v.map! do |a|
36
+ a.is_a?(Hash) ? symbolize_keys(a) : a
37
+ end if v.is_a?(Array)
38
+ retval[k.to_sym] = v.is_a?(Hash) ? symbolize_keys(v) : v
39
+ end
40
+ end
41
+ end
31
42
  end
32
43
  end
data/lxd-common.gemspec CHANGED
@@ -8,16 +8,10 @@ Gem::Specification.new do |spec|
8
8
  spec.version = NexusSW::LXD::VERSION
9
9
  spec.authors = ['Sean Zachariasen']
10
10
  spec.email = ['thewyzard@hotmail.com']
11
-
11
+ spec.license = 'Apache-2.0'
12
12
  spec.summary = 'Shared LXD Container Access Library'
13
- # spec.description = %q{TODO: Write a longer description or delete this line.}
14
13
  spec.homepage = 'http://github.com/NexusSW/lxd-common'
15
14
 
16
- # Prevent pushing this gem to RubyGems.org. To allow pushes either set the 'allowed_push_host'
17
- # to allow pushing to a single host or delete this section to allow pushing to any host.
18
-
19
- # spec.metadata['allowed_push_host'] = "TODO: Set to 'http://mygemserver.com'" if spec.respond_to?(:metadata)
20
-
21
15
  spec.files = `git ls-files -z`.split("\x0").reject do |f|
22
16
  f.match(%r{^(test|spec|features)/})
23
17
  end
@@ -25,7 +19,7 @@ Gem::Specification.new do |spec|
25
19
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
26
20
  spec.require_paths = ['lib']
27
21
 
28
- spec.add_dependency 'hyperkit', '~> 1.1'
22
+ spec.add_dependency 'faraday', '~> 0.13'
29
23
  spec.add_dependency 'nio4r-websocket', '~> 0.6'
30
24
 
31
25
  spec.add_development_dependency 'bundler'
metadata CHANGED
@@ -1,29 +1,29 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: lxd-common
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.6.0
4
+ version: 0.7.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Sean Zachariasen
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2017-12-31 00:00:00.000000000 Z
11
+ date: 2018-01-15 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
- name: hyperkit
14
+ name: faraday
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
17
  - - "~>"
18
18
  - !ruby/object:Gem::Version
19
- version: '1.1'
19
+ version: '0.13'
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - "~>"
25
25
  - !ruby/object:Gem::Version
26
- version: '1.1'
26
+ version: '0.13'
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: nio4r-websocket
29
29
  requirement: !ruby/object:Gem::Requirement
@@ -118,6 +118,9 @@ files:
118
118
  - lib/nexussw/lxd/driver/mixins/helpers/wait.rb
119
119
  - lib/nexussw/lxd/driver/mixins/rest.rb
120
120
  - lib/nexussw/lxd/driver/rest.rb
121
+ - lib/nexussw/lxd/rest_api.rb
122
+ - lib/nexussw/lxd/rest_api/connection.rb
123
+ - lib/nexussw/lxd/rest_api/errors.rb
121
124
  - lib/nexussw/lxd/transport.rb
122
125
  - lib/nexussw/lxd/transport/cli.rb
123
126
  - lib/nexussw/lxd/transport/local.rb
@@ -130,7 +133,8 @@ files:
130
133
  - lib/nexussw/lxd/version.rb
131
134
  - lxd-common.gemspec
132
135
  homepage: http://github.com/NexusSW/lxd-common
133
- licenses: []
136
+ licenses:
137
+ - Apache-2.0
134
138
  metadata: {}
135
139
  post_install_message:
136
140
  rdoc_options: []