lxd-common 0.6.0 → 0.7.0

Sign up to get free protection for your applications and to get access to all the features.
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: []