lxd-common 0.4.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,71 @@
1
+ require 'open3'
2
+ require 'nio/websocket'
3
+
4
+ module NexusSW
5
+ module LXD
6
+ class Transport
7
+ module Mixins
8
+ module Local
9
+ def initialize(config = {})
10
+ @config = config
11
+ end
12
+
13
+ attr_reader :config
14
+
15
+ include ExecuteMixin
16
+
17
+ def execute_chunked(command, options)
18
+ NIO::WebSocket::Reactor.start
19
+ LXD.with_timeout_and_retries options do
20
+ # Let's borrow the NIO::WebSocket reactor
21
+ Open3.popen3(command) do |_stdin, stdout, stderr, th|
22
+ mon_out = mon_err = nil
23
+ NIO::WebSocket::Reactor.queue_task do
24
+ mon_out = NIO::WebSocket::Reactor.selector.register(stdout, :r)
25
+ mon_out.value = proc do
26
+ data = read(mon_out) # read regardless of block_given? so that we don't spin out on :r availability
27
+ yield(data) if data && block_given?
28
+ end
29
+ end
30
+ NIO::WebSocket::Reactor.queue_task do
31
+ mon_err = NIO::WebSocket::Reactor.selector.register(stderr, :r)
32
+ mon_err.value = proc do
33
+ data = read(mon_err) # read regardless of block_given? so that we don't spin out on :r availability
34
+ yield(nil, data) if data && block_given?
35
+ end
36
+ end
37
+ th.join
38
+ loop do
39
+ return LXDExecuteResult.new(command, options, th.value.exitstatus) if th.value.exited? && mon_out && mon_err && mon_out.closed? && mon_err.closed?
40
+ Thread.pass
41
+ end
42
+ end
43
+ end
44
+ end
45
+
46
+ def read_file(path)
47
+ return '' unless File.exist? path
48
+ File.read path
49
+ end
50
+
51
+ def write_file(path, content)
52
+ File.open path, 'w' do |f|
53
+ f.write content
54
+ end
55
+ end
56
+
57
+ private
58
+
59
+ def read(monitor)
60
+ monitor.io.read_nonblock(16384)
61
+ rescue IO::WaitReadable # rubocop:disable Lint/ShadowedException
62
+ return nil
63
+ rescue Errno::ECONNRESET, EOFError, IOError
64
+ monitor.close
65
+ return nil
66
+ end
67
+ end
68
+ end
69
+ end
70
+ end
71
+ end
@@ -0,0 +1,120 @@
1
+ require 'nio/websocket'
2
+
3
+ module NexusSW
4
+ module LXD
5
+ class Transport
6
+ module Mixins
7
+ module Rest
8
+ def initialize(driver, container_name, config = {})
9
+ @container_name = container_name
10
+ @config = config
11
+ raise "The rest transport requires the Rest Driver. You supplied #{driver}" unless driver.respond_to?(:hk) && driver.respond_to?(:rest_endpoint) # driver.is_a? NexusSW::LXD::Driver::Rest
12
+ @rest_endpoint = driver.rest_endpoint
13
+ @driver_options = driver.driver_options
14
+ @hk = driver.hk
15
+ end
16
+
17
+ attr_reader :hk, :rest_endpoint, :container_name, :config
18
+
19
+ include ExecuteMixin
20
+
21
+ def execute_chunked(command, options = {}, &block)
22
+ opid = nil
23
+ backchannel = nil
24
+ if block_given? # Allow for an optimized case that doesn't require the support of 3 new websocket connections
25
+ retval = hk.execute_command(container_name, command, wait_for_websocket: true, interactive: false, sync: false)
26
+ opid = retval[:id]
27
+ backchannel = ws_connect opid, retval[:metadata][:fds], &block
28
+ else
29
+ opid = hk.execute_command(container_name, command, sync: false)[:id]
30
+ end
31
+ LXD.with_timeout_and_retries({ timeout: 0 }.merge(options)) do
32
+ begin
33
+ retval = hk.wait_for_operation opid
34
+ backchannel.exit if backchannel.respond_to? :exit
35
+ return LXDExecuteResult.new command, options, retval[:metadata][:return].to_i
36
+ rescue Faraday::TimeoutError => e
37
+ raise Timeout::Retry.new e # rubocop:disable Style/RaiseArgs
38
+ end
39
+ end
40
+ end
41
+
42
+ def read_file(path)
43
+ hk.read_file container_name, path
44
+ rescue ::Hyperkit::NotFound
45
+ return ''
46
+ end
47
+
48
+ def write_file(path, content)
49
+ hk.write_file container_name, path, content: content
50
+ end
51
+
52
+ def download_file(path, local_path)
53
+ hk.pull_file container_name, path, local_path
54
+ end
55
+
56
+ def upload_file(local_path, path)
57
+ hk.push_file local_path, container_name, path
58
+ end
59
+
60
+ protected
61
+
62
+ class WSWrapper
63
+ def initialize(waitlist)
64
+ @waitlist = waitlist.compact
65
+ end
66
+ attr_reader :waitlist
67
+
68
+ def exit
69
+ loop do
70
+ allclosed = true
71
+ waitlist.each do |driver|
72
+ allclosed = false unless driver.state == :closed
73
+ end
74
+ break if allclosed
75
+ Thread.pass
76
+ sleep 0.1
77
+ end
78
+ end
79
+ end
80
+
81
+ def ws_connect(opid, endpoints)
82
+ # NIO::WebSocket.log_traffic = true
83
+ verify_ssl = OpenSSL::SSL::VERIFY_NONE if @driver_options[:verify_ssl] == false
84
+ ws_options = { ssl_context: { verify_mode: verify_ssl } } unless verify_ssl.nil?
85
+ ws_options ||= {}
86
+ baseurl = rest_endpoint.sub(%r{^http([s]?://)}, 'ws\1')
87
+ baseurl += '/' unless baseurl.end_with? '/'
88
+ baseurl += "1.0/operations/#{opid}/websocket?secret="
89
+
90
+ pipes = {}
91
+ NIO::WebSocket.connect(baseurl + endpoints[:control], ws_options) do |driver|
92
+ driver.on :io_error do # usually I get an EOF
93
+ pipes.each { |_, v| v.close if v.respond_to? :close }
94
+ end
95
+ driver.on :close do # but on occasion I get a legit close
96
+ pipes.each { |_, v| v.close if v.respond_to? :close }
97
+ end
98
+ end
99
+ pipes[:'1'] = NIO::WebSocket.connect(baseurl + endpoints[:'1'], ws_options) do |driver|
100
+ driver.on :message do |ev|
101
+ data = ev.data.is_a?(String) ? ev.data : ev.data.pack('U*')
102
+ yield data
103
+ end
104
+ end
105
+ endpoints.each do |fd, secret|
106
+ next if [:control, :'1'].include? fd
107
+ pipes[fd] = NIO::WebSocket.connect(baseurl + secret, ws_options) do |driver|
108
+ driver.on :message do |ev|
109
+ data = ev.data.is_a?(String) ? ev.data : ev.data.pack('U*')
110
+ yield nil, data
111
+ end
112
+ end
113
+ end
114
+ WSWrapper.new [pipes[:'1'], pipes[:'2']]
115
+ end
116
+ end
117
+ end
118
+ end
119
+ end
120
+ end
@@ -0,0 +1,12 @@
1
+ require 'nexussw/lxd/transport'
2
+ require 'nexussw/lxd/transport/mixins/rest'
3
+
4
+ module NexusSW
5
+ module LXD
6
+ class Transport
7
+ class Rest < Transport
8
+ include Mixins::Rest
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,5 @@
1
+ module NexusSW
2
+ module LXD
3
+ VERSION = '0.4.1'.freeze
4
+ end
5
+ end
@@ -0,0 +1,35 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'nexussw/lxd/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = 'lxd-common'
8
+ spec.version = NexusSW::LXD::VERSION
9
+ spec.authors = ['Sean Zachariasen']
10
+ spec.email = ['thewyzard@hotmail.com']
11
+
12
+ spec.summary = 'Shared LXD Container Access Library'
13
+ # spec.description = %q{TODO: Write a longer description or delete this line.}
14
+ spec.homepage = 'http://github.com/NexusSW/lxd-common'
15
+
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
+ spec.files = `git ls-files -z`.split("\x0").reject do |f|
22
+ f.match(%r{^(test|spec|features)/})
23
+ end
24
+ spec.bindir = 'exe'
25
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
26
+ spec.require_paths = ['lib']
27
+
28
+ spec.add_dependency 'hyperkit', '~> 1.1'
29
+ spec.add_dependency 'nio4r-websocket', '~> 0.6'
30
+
31
+ spec.add_development_dependency 'bundler'
32
+ spec.add_development_dependency 'rake'
33
+ spec.add_development_dependency 'rspec'
34
+ spec.add_development_dependency 'simplecov'
35
+ end
metadata ADDED
@@ -0,0 +1,152 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: lxd-common
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.4.1
5
+ platform: ruby
6
+ authors:
7
+ - Sean Zachariasen
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2017-11-08 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: hyperkit
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.1'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.1'
27
+ - !ruby/object:Gem::Dependency
28
+ name: nio4r-websocket
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '0.6'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '0.6'
41
+ - !ruby/object:Gem::Dependency
42
+ name: bundler
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rake
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: rspec
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: simplecov
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ description:
98
+ email:
99
+ - thewyzard@hotmail.com
100
+ executables: []
101
+ extensions: []
102
+ extra_rdoc_files: []
103
+ files:
104
+ - ".gitignore"
105
+ - ".rspec"
106
+ - ".travis.yml"
107
+ - Gemfile
108
+ - LICENSE
109
+ - README.md
110
+ - Rakefile
111
+ - Vagrantfile
112
+ - bin/console
113
+ - bin/setup
114
+ - lib/nexussw/lxd.rb
115
+ - lib/nexussw/lxd/driver.rb
116
+ - lib/nexussw/lxd/driver/cli.rb
117
+ - lib/nexussw/lxd/driver/mixins/cli.rb
118
+ - lib/nexussw/lxd/driver/mixins/rest.rb
119
+ - lib/nexussw/lxd/driver/rest.rb
120
+ - lib/nexussw/lxd/transport.rb
121
+ - lib/nexussw/lxd/transport/cli.rb
122
+ - lib/nexussw/lxd/transport/local.rb
123
+ - lib/nexussw/lxd/transport/mixins/cli.rb
124
+ - lib/nexussw/lxd/transport/mixins/local.rb
125
+ - lib/nexussw/lxd/transport/mixins/rest.rb
126
+ - lib/nexussw/lxd/transport/rest.rb
127
+ - lib/nexussw/lxd/version.rb
128
+ - lxd-common.gemspec
129
+ homepage: http://github.com/NexusSW/lxd-common
130
+ licenses: []
131
+ metadata: {}
132
+ post_install_message:
133
+ rdoc_options: []
134
+ require_paths:
135
+ - lib
136
+ required_ruby_version: !ruby/object:Gem::Requirement
137
+ requirements:
138
+ - - ">="
139
+ - !ruby/object:Gem::Version
140
+ version: '0'
141
+ required_rubygems_version: !ruby/object:Gem::Requirement
142
+ requirements:
143
+ - - ">="
144
+ - !ruby/object:Gem::Version
145
+ version: '0'
146
+ requirements: []
147
+ rubyforge_project:
148
+ rubygems_version: 2.6.13
149
+ signing_key:
150
+ specification_version: 4
151
+ summary: Shared LXD Container Access Library
152
+ test_files: []