lxd-common 0.9.8 → 0.9.9
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 +5 -5
- data/.gitattributes +3 -0
- data/.gitignore +15 -15
- data/.rubocop.yml +2 -0
- data/.travis.yml +35 -34
- data/Gemfile +1 -1
- data/LICENSE +203 -202
- data/README.md +200 -11
- data/Rakefile +3 -3
- data/Vagrantfile +4 -4
- data/bin/console +6 -6
- data/lib/nexussw/lxd.rb +9 -4
- data/lib/nexussw/lxd/driver.rb +33 -17
- data/lib/nexussw/lxd/driver/cli.rb +2 -2
- data/lib/nexussw/lxd/driver/mixins/cli.rb +99 -47
- data/lib/nexussw/lxd/driver/mixins/helpers/wait.rb +4 -4
- data/lib/nexussw/lxd/driver/mixins/rest.rb +21 -27
- data/lib/nexussw/lxd/driver/rest.rb +2 -2
- data/lib/nexussw/lxd/rest_api.rb +49 -35
- data/lib/nexussw/lxd/rest_api/connection.rb +18 -14
- data/lib/nexussw/lxd/transport.rb +6 -6
- data/lib/nexussw/lxd/transport/cli.rb +2 -2
- data/lib/nexussw/lxd/transport/local.rb +2 -2
- data/lib/nexussw/lxd/transport/mixins/cli.rb +31 -15
- data/lib/nexussw/lxd/transport/mixins/helpers/execute.rb +2 -2
- data/lib/nexussw/lxd/transport/mixins/helpers/folder_txfr.rb +9 -9
- data/lib/nexussw/lxd/transport/mixins/helpers/users.rb +3 -3
- data/lib/nexussw/lxd/transport/mixins/local.rb +22 -18
- data/lib/nexussw/lxd/transport/mixins/rest.rb +51 -43
- data/lib/nexussw/lxd/transport/rest.rb +2 -2
- data/lib/nexussw/lxd/version.rb +1 -1
- data/lxd-common.gemspec +17 -17
- metadata +9 -7
@@ -1,4 +1,4 @@
|
|
1
|
-
require
|
1
|
+
require "nexussw/lxd/rest_api/errors"
|
2
2
|
|
3
3
|
module NexusSW
|
4
4
|
module LXD
|
@@ -42,7 +42,7 @@ module NexusSW
|
|
42
42
|
options ||= {}
|
43
43
|
return execute_chunked(command, options) if options[:capture] == false && !block_given?
|
44
44
|
|
45
|
-
capture_options = { stdout:
|
45
|
+
capture_options = { stdout: "", stderr: "" }
|
46
46
|
capture_options[:capture] = block if block_given?
|
47
47
|
capture_options[:capture] ||= options[:capture] if options[:capture].respond_to? :call
|
48
48
|
# capture_options[:capture] ||= options[:stream] if options[:stream].respond_to? :call
|
@@ -1,5 +1,5 @@
|
|
1
|
-
require
|
2
|
-
require
|
1
|
+
require "zlib"
|
2
|
+
require "archive/tar/minitar"
|
3
3
|
|
4
4
|
module NexusSW
|
5
5
|
module LXD
|
@@ -17,8 +17,8 @@ module NexusSW
|
|
17
17
|
|
18
18
|
def upload_files_individually(local_path, path)
|
19
19
|
dest = File.join(path, File.basename(local_path))
|
20
|
-
execute(
|
21
|
-
Dir.entries(local_path).map { |f| (f ==
|
20
|
+
execute("mkdir -p " + dest).error! # for parity with tarball extract
|
21
|
+
Dir.entries(local_path).map { |f| (f == "." || f == "..") ? nil : File.join(local_path, f) }.compact.each do |f|
|
22
22
|
upload_files_individually f, dest if File.directory? f
|
23
23
|
upload_file f, File.join(dest, File.basename(f)) if File.file? f
|
24
24
|
end
|
@@ -44,13 +44,13 @@ module NexusSW
|
|
44
44
|
|
45
45
|
return false unless can_archive?
|
46
46
|
tfile = Transport.remote_tempname(container_name)
|
47
|
-
tarball_name = File.join Transport.local_tempdir, File.basename(tfile) +
|
47
|
+
tarball_name = File.join Transport.local_tempdir, File.basename(tfile) + ".tgz"
|
48
48
|
execute("tar -czf #{tfile} -C #{File.dirname path} #{File.basename path}").error!
|
49
49
|
|
50
50
|
download_file tfile, tarball_name
|
51
51
|
|
52
|
-
Archive::Tar::Minitar.unpack Zlib::GzipReader.new(File.open(tarball_name,
|
53
|
-
|
52
|
+
Archive::Tar::Minitar.unpack Zlib::GzipReader.new(File.open(tarball_name, "rb")), local_path
|
53
|
+
true
|
54
54
|
ensure
|
55
55
|
if tarball_name
|
56
56
|
File.delete tarball_name if File.exist? tarball_name
|
@@ -65,11 +65,11 @@ module NexusSW
|
|
65
65
|
tfile.close
|
66
66
|
Transport.chdir_mutex.synchronize do
|
67
67
|
Dir.chdir File.dirname(local_path) do
|
68
|
-
Archive::Tar::Minitar.pack File.basename(local_path), Zlib::GzipWriter.new(File.open(tfile.path,
|
68
|
+
Archive::Tar::Minitar.pack File.basename(local_path), Zlib::GzipWriter.new(File.open(tfile.path, "wb"))
|
69
69
|
end
|
70
70
|
end
|
71
71
|
# `tar -c#{flag}f #{tfile.path} -C #{File.dirname local_path} ./#{File.basename local_path}`
|
72
|
-
fname =
|
72
|
+
fname = "/tmp/" + File.basename(tfile.path) + ".tgz"
|
73
73
|
upload_file tfile.path, fname
|
74
74
|
|
75
75
|
execute("bash -c 'mkdir -p #{path} && cd #{path} && tar -xf #{fname} && rm -rf #{fname}'").error!
|
@@ -1,4 +1,4 @@
|
|
1
|
-
require
|
1
|
+
require "shellwords"
|
2
2
|
|
3
3
|
module NexusSW
|
4
4
|
module LXD
|
@@ -8,7 +8,7 @@ module NexusSW
|
|
8
8
|
module UsersMixin
|
9
9
|
def user(user_nameorid, options = {})
|
10
10
|
return unless user_nameorid
|
11
|
-
passwd = read_file options[:passwd_file] ||
|
11
|
+
passwd = read_file options[:passwd_file] || "/etc/passwd"
|
12
12
|
|
13
13
|
# rework into .split(':') if this gets more complicated
|
14
14
|
@uid = user_nameorid.is_a?(String) ? passwd[/^#{user_nameorid}:[^:]*:([^:]*):/, 1] : user_nameorid
|
@@ -33,7 +33,7 @@ module NexusSW
|
|
33
33
|
uname = options[:runas] || username
|
34
34
|
return command unless uname
|
35
35
|
command = command.shelljoin if command.is_a? Array
|
36
|
-
[
|
36
|
+
["su", uname, "-c", command]
|
37
37
|
end
|
38
38
|
end
|
39
39
|
end
|
@@ -1,6 +1,6 @@
|
|
1
|
-
require
|
2
|
-
require
|
3
|
-
require
|
1
|
+
require "nexussw/lxd/transport/mixins/helpers/execute"
|
2
|
+
require "open3"
|
3
|
+
require "nio/websocket"
|
4
4
|
|
5
5
|
module NexusSW
|
6
6
|
module LXD
|
@@ -58,7 +58,7 @@ module NexusSW
|
|
58
58
|
end
|
59
59
|
|
60
60
|
def write_file(path, content)
|
61
|
-
File.open path,
|
61
|
+
File.open path, "w" do |f|
|
62
62
|
f.write content
|
63
63
|
end
|
64
64
|
end
|
@@ -70,27 +70,31 @@ module NexusSW
|
|
70
70
|
def read(monitor)
|
71
71
|
monitor.io.read_nonblock(16384)
|
72
72
|
rescue IO::WaitReadable # rubocop:disable Lint/ShadowedException
|
73
|
-
|
73
|
+
nil
|
74
74
|
rescue Errno::ECONNRESET, EOFError, IOError
|
75
75
|
monitor.close
|
76
|
-
|
76
|
+
nil
|
77
77
|
end
|
78
78
|
|
79
79
|
def chunk_callback(stdout, stderr = nil)
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
80
|
+
if stdout
|
81
|
+
NIO::WebSocket::Reactor.queue_task do
|
82
|
+
@mon_out = NIO::WebSocket::Reactor.selector.register(stdout, :r)
|
83
|
+
@mon_out.value = proc do
|
84
|
+
data = read(@mon_out) # read regardless of block_given? so that we don't spin out on :r availability
|
85
|
+
yield(data) if data
|
86
|
+
end
|
85
87
|
end
|
86
|
-
end
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
88
|
+
end
|
89
|
+
if stderr
|
90
|
+
NIO::WebSocket::Reactor.queue_task do
|
91
|
+
@mon_err = NIO::WebSocket::Reactor.selector.register(stderr, :r)
|
92
|
+
@mon_err.value = proc do
|
93
|
+
data = read(@mon_err) # read regardless of block_given? so that we don't spin out on :r availability
|
94
|
+
yield(nil, data) if data
|
95
|
+
end
|
92
96
|
end
|
93
|
-
end
|
97
|
+
end
|
94
98
|
end
|
95
99
|
end
|
96
100
|
end
|
@@ -1,10 +1,10 @@
|
|
1
|
-
require
|
2
|
-
require
|
3
|
-
require
|
4
|
-
require
|
5
|
-
require
|
6
|
-
require
|
7
|
-
require
|
1
|
+
require "nexussw/lxd/transport/mixins/helpers/execute"
|
2
|
+
require "nexussw/lxd/transport/mixins/helpers/users"
|
3
|
+
require "nexussw/lxd/transport/mixins/helpers/folder_txfr"
|
4
|
+
require "nio/websocket"
|
5
|
+
require "tempfile"
|
6
|
+
require "json"
|
7
|
+
require "shellwords"
|
8
8
|
|
9
9
|
module NexusSW
|
10
10
|
module LXD
|
@@ -17,7 +17,7 @@ module NexusSW
|
|
17
17
|
@rest_endpoint = config[:rest_endpoint]
|
18
18
|
@driver_options = config[:driver_options]
|
19
19
|
@api = config[:connection]
|
20
|
-
raise
|
20
|
+
raise "The rest transport requires the following keys: { :connection, :driver_options, :rest_endpoint }" unless @rest_endpoint && @api && @driver_options
|
21
21
|
end
|
22
22
|
|
23
23
|
attr_reader :api, :rest_endpoint, :container_name, :config
|
@@ -54,10 +54,10 @@ module NexusSW
|
|
54
54
|
def self.read(monitor)
|
55
55
|
monitor.io.read_nonblock(16384)
|
56
56
|
rescue IO::WaitReadable # rubocop:disable Lint/ShadowedException
|
57
|
-
|
57
|
+
nil
|
58
58
|
rescue Errno::ECONNRESET, EOFError, IOError
|
59
59
|
monitor.close
|
60
|
-
|
60
|
+
nil
|
61
61
|
end
|
62
62
|
end
|
63
63
|
|
@@ -66,26 +66,28 @@ module NexusSW
|
|
66
66
|
backchannel = nil
|
67
67
|
getlogs = false
|
68
68
|
command = runas_command(command, options)
|
69
|
-
if block_given? && (options[:capture] || !config[:info][:api_extensions].include?(
|
70
|
-
apiopts = {
|
69
|
+
if block_given? && (options[:capture] || !config[:info][:api_extensions].include?("container_exec_recording"))
|
70
|
+
apiopts = { 'wait-for-websocket': true, interactive: false, sync: false }
|
71
71
|
apiopts[:interactive] = true if options[:capture] == :interactive
|
72
72
|
retval = api.execute_command(container_name, command, apiopts)[:metadata]
|
73
73
|
opid = retval[:id]
|
74
74
|
backchannel = options[:capture] == :interactive ? ws_connect(opid, retval[:metadata][:fds]) : ws_connect(opid, retval[:metadata][:fds], &block)
|
75
75
|
|
76
76
|
# patch for interactive session
|
77
|
-
|
78
|
-
backchannel.
|
79
|
-
|
77
|
+
if options[:capture] == :interactive
|
78
|
+
return Helpers::ExecuteMixin::InteractiveResult.new(command, options, StdinStub.pipe(backchannel.waitlist[:'0']), backchannel).tap do |active|
|
79
|
+
backchannel.callback = proc do |stdout|
|
80
|
+
active.send_output stdout
|
81
|
+
end
|
82
|
+
yield active
|
83
|
+
backchannel.exit if backchannel.respond_to? :exit
|
84
|
+
retval = api.wait_for_operation opid
|
85
|
+
active.exitstatus = retval[:metadata][:return].to_i
|
80
86
|
end
|
81
|
-
|
82
|
-
|
83
|
-
retval = api.wait_for_operation opid
|
84
|
-
active.exitstatus = retval[:metadata][:return].to_i
|
85
|
-
end if options[:capture] == :interactive
|
86
|
-
elsif block_given? && config[:info][:api_extensions].include?('container_exec_recording')
|
87
|
+
end
|
88
|
+
elsif block_given? && config[:info][:api_extensions].include?("container_exec_recording")
|
87
89
|
getlogs = true
|
88
|
-
retval = api.execute_command(container_name, command,
|
90
|
+
retval = api.execute_command(container_name, command, 'record-output': true, interactive: false, sync: false)
|
89
91
|
opid = retval[:metadata][:id]
|
90
92
|
else
|
91
93
|
opid = api.execute_command(container_name, command, sync: false)[:metadata][:id]
|
@@ -96,8 +98,8 @@ module NexusSW
|
|
96
98
|
backchannel.join if backchannel.respond_to? :join
|
97
99
|
if getlogs
|
98
100
|
begin
|
99
|
-
stdout_log = retval[:metadata][:output][:'1'].split(
|
100
|
-
stderr_log = retval[:metadata][:output][:'2'].split(
|
101
|
+
stdout_log = retval[:metadata][:output][:'1'].split("/").last
|
102
|
+
stderr_log = retval[:metadata][:output][:'2'].split("/").last
|
101
103
|
stdout = api.log container_name, stdout_log
|
102
104
|
stderr = api.log container_name, stderr_log
|
103
105
|
yield stdout, stderr
|
@@ -144,28 +146,34 @@ module NexusSW
|
|
144
146
|
waitlist[:control] = NIO::WebSocket.connect(baseurl + endpoints[:control], ws_options) do |driver|
|
145
147
|
driver.on :io_error do # usually I get an EOF
|
146
148
|
@closed = true
|
147
|
-
waitlist.each { |_, v| v.close if v.respond_to? :close }
|
149
|
+
# waitlist.each { |_, v| v.close if v.respond_to? :close }
|
150
|
+
waitlist[:'0'].close
|
148
151
|
end
|
149
152
|
driver.on :close do # but on occasion I get a legit close
|
150
153
|
@closed = true
|
151
|
-
waitlist.each { |_, v| v.close if v.respond_to? :close }
|
154
|
+
# waitlist.each { |_, v| v.close if v.respond_to? :close }
|
155
|
+
waitlist[:'0'].close
|
152
156
|
end
|
153
157
|
end
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
+
if endpoints[:'2']
|
159
|
+
waitlist[:'2'] = NIO::WebSocket.connect(baseurl + endpoints[:'2'], ws_options) do |driver|
|
160
|
+
driver.on :message do |ev|
|
161
|
+
data = ev.data.is_a?(String) ? ev.data : ev.data.pack("U*")
|
162
|
+
callback.call nil, data
|
163
|
+
end
|
158
164
|
end
|
159
|
-
end
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
165
|
+
end
|
166
|
+
if endpoints[:'1']
|
167
|
+
waitlist[:'1'] = NIO::WebSocket.connect(baseurl + endpoints[:'1'], ws_options) do |driver|
|
168
|
+
driver.on :message do |ev|
|
169
|
+
data = ev.data.is_a?(String) ? ev.data : ev.data.pack("U*")
|
170
|
+
callback.call data
|
171
|
+
end
|
164
172
|
end
|
165
|
-
end
|
173
|
+
end
|
166
174
|
waitlist[:'0'] = NIO::WebSocket.connect(baseurl + endpoints[:'0'], ws_options) do |driver|
|
167
175
|
driver.on :message do |ev|
|
168
|
-
data = ev.data.is_a?(String) ? ev.data : ev.data.pack(
|
176
|
+
data = ev.data.is_a?(String) ? ev.data : ev.data.pack("U*")
|
169
177
|
callback.call data
|
170
178
|
end
|
171
179
|
end
|
@@ -198,21 +206,21 @@ module NexusSW
|
|
198
206
|
end
|
199
207
|
|
200
208
|
def window_resize(width, height)
|
201
|
-
send_control_msg
|
209
|
+
send_control_msg "window-resize", width: width.to_s, height: height.to_s
|
202
210
|
end
|
203
211
|
|
204
212
|
def signal(signum)
|
205
|
-
send_control_msg
|
213
|
+
send_control_msg "signal", signum
|
206
214
|
end
|
207
215
|
|
208
216
|
private
|
209
217
|
|
210
218
|
def send_control_msg(message, val)
|
211
219
|
msg = {}.tap do |retval|
|
212
|
-
retval[
|
220
|
+
retval["command"] = message
|
213
221
|
case message
|
214
|
-
when
|
215
|
-
when
|
222
|
+
when "window-resize" then retval["args"] = val
|
223
|
+
when "signal" then retval["signal"] = val.to_i
|
216
224
|
end
|
217
225
|
end.to_json
|
218
226
|
|
@@ -226,7 +234,7 @@ module NexusSW
|
|
226
234
|
ws_options = { ssl_context: { verify_mode: verify_ssl } } unless verify_ssl.nil?
|
227
235
|
ws_options ||= {}
|
228
236
|
baseurl = rest_endpoint.sub(%r{^http([s]?://)}, 'ws\1')
|
229
|
-
baseurl +=
|
237
|
+
baseurl += "/" unless baseurl.end_with? "/"
|
230
238
|
baseurl += "1.0/operations/#{opid}/websocket?secret="
|
231
239
|
|
232
240
|
WSController.new ws_options, baseurl, endpoints, &block
|
data/lib/nexussw/lxd/version.rb
CHANGED
data/lxd-common.gemspec
CHANGED
@@ -1,30 +1,30 @@
|
|
1
1
|
# coding: utf-8
|
2
|
-
lib = File.expand_path(
|
2
|
+
lib = File.expand_path("../lib", __FILE__)
|
3
3
|
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
-
require
|
4
|
+
require "nexussw/lxd/version"
|
5
5
|
|
6
6
|
Gem::Specification.new do |spec|
|
7
|
-
spec.name =
|
7
|
+
spec.name = "lxd-common"
|
8
8
|
spec.version = NexusSW::LXD::VERSION
|
9
|
-
spec.authors = [
|
10
|
-
spec.email = [
|
11
|
-
spec.license =
|
12
|
-
spec.summary =
|
13
|
-
spec.homepage =
|
9
|
+
spec.authors = ["Sean Zachariasen"]
|
10
|
+
spec.email = ["thewyzard@hotmail.com"]
|
11
|
+
spec.license = "Apache-2.0"
|
12
|
+
spec.summary = "Shared LXD Container Access Library"
|
13
|
+
spec.homepage = "http://github.com/NexusSW/lxd-common"
|
14
14
|
|
15
15
|
spec.files = `git ls-files -z`.split("\x0").reject do |f|
|
16
16
|
f.match(%r{^(test|spec|features)/})
|
17
17
|
end
|
18
|
-
spec.bindir =
|
18
|
+
spec.bindir = "exe"
|
19
19
|
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
20
|
-
spec.require_paths = [
|
20
|
+
spec.require_paths = ["lib"]
|
21
21
|
|
22
|
-
spec.add_dependency
|
23
|
-
spec.add_dependency
|
24
|
-
spec.add_dependency
|
22
|
+
spec.add_dependency "faraday", "~> 0.12"
|
23
|
+
spec.add_dependency "nio4r-websocket", "~> 0.6"
|
24
|
+
spec.add_dependency "minitar", "~> 0.6"
|
25
25
|
|
26
|
-
spec.add_development_dependency
|
27
|
-
spec.add_development_dependency
|
28
|
-
spec.add_development_dependency
|
29
|
-
spec.add_development_dependency
|
26
|
+
spec.add_development_dependency "bundler"
|
27
|
+
spec.add_development_dependency "rake"
|
28
|
+
spec.add_development_dependency "rspec"
|
29
|
+
spec.add_development_dependency "simplecov"
|
30
30
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: lxd-common
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.9.
|
4
|
+
version: 0.9.9
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Sean Zachariasen
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2018-
|
11
|
+
date: 2018-12-30 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: faraday
|
@@ -16,14 +16,14 @@ dependencies:
|
|
16
16
|
requirements:
|
17
17
|
- - "~>"
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version: '0.
|
19
|
+
version: '0.12'
|
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: '0.
|
26
|
+
version: '0.12'
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: nio4r-websocket
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
@@ -44,14 +44,14 @@ dependencies:
|
|
44
44
|
requirements:
|
45
45
|
- - "~>"
|
46
46
|
- !ruby/object:Gem::Version
|
47
|
-
version: '0.
|
47
|
+
version: '0.6'
|
48
48
|
type: :runtime
|
49
49
|
prerelease: false
|
50
50
|
version_requirements: !ruby/object:Gem::Requirement
|
51
51
|
requirements:
|
52
52
|
- - "~>"
|
53
53
|
- !ruby/object:Gem::Version
|
54
|
-
version: '0.
|
54
|
+
version: '0.6'
|
55
55
|
- !ruby/object:Gem::Dependency
|
56
56
|
name: bundler
|
57
57
|
requirement: !ruby/object:Gem::Requirement
|
@@ -115,8 +115,10 @@ executables: []
|
|
115
115
|
extensions: []
|
116
116
|
extra_rdoc_files: []
|
117
117
|
files:
|
118
|
+
- ".gitattributes"
|
118
119
|
- ".gitignore"
|
119
120
|
- ".rspec"
|
121
|
+
- ".rubocop.yml"
|
120
122
|
- ".travis.yml"
|
121
123
|
- Gemfile
|
122
124
|
- LICENSE
|
@@ -167,7 +169,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
167
169
|
version: '0'
|
168
170
|
requirements: []
|
169
171
|
rubyforge_project:
|
170
|
-
rubygems_version: 2.
|
172
|
+
rubygems_version: 2.6.14
|
171
173
|
signing_key:
|
172
174
|
specification_version: 4
|
173
175
|
summary: Shared LXD Container Access Library
|