miasma-lxd 0.1.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 +7 -0
- data/CHANGELOG.md +2 -0
- data/LICENSE +13 -0
- data/README.md +46 -0
- data/lib/miasma/contrib/lxd/compute.rb +258 -0
- data/lib/miasma/contrib/lxd.rb +103 -0
- data/lib/miasma-lxd/version.rb +4 -0
- data/lib/miasma-lxd.rb +2 -0
- data/miasma-lxd.gemspec +21 -0
- metadata +149 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 768837675895e46e18d2454a8755dabf7a643689
|
4
|
+
data.tar.gz: 3696866fc4fae82a6c807826927acb6bd727dac2
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 9b76ccc003a7a69d5a83f1d4d0fc683ae344a7e4b9c80e121d81f05271306a7a62c89287a0dfa1cc3739380edcae548c627999a776fb9354d65799ff0cd7248e
|
7
|
+
data.tar.gz: 3c7c6f533d4c53855a5106118745356a9e77dfc060af697b18d1907d051b4fe83515b707622d9867d7753c2c6c61ca5dd9af5b4ca3a85f692ac3ffa9cd60d94f
|
data/CHANGELOG.md
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
Copyright 2015 Chris Roberts
|
2
|
+
|
3
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
4
|
+
you may not use this file except in compliance with the License.
|
5
|
+
You may obtain a copy of the License at
|
6
|
+
|
7
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
8
|
+
|
9
|
+
Unless required by applicable law or agreed to in writing, software
|
10
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
11
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
12
|
+
See the License for the specific language governing permissions and
|
13
|
+
limitations under the License.
|
data/README.md
ADDED
@@ -0,0 +1,46 @@
|
|
1
|
+
# Miasma LXD
|
2
|
+
|
3
|
+
LXD API plugin for the miasma cloud library
|
4
|
+
|
5
|
+
## Supported credential attributes:
|
6
|
+
|
7
|
+
Supported attributes used in the credentials section of API
|
8
|
+
configurations:
|
9
|
+
|
10
|
+
```ruby
|
11
|
+
Miasma.api(
|
12
|
+
:type => :compute,
|
13
|
+
:provider => :lxd,
|
14
|
+
:credentials => {
|
15
|
+
...
|
16
|
+
}
|
17
|
+
)
|
18
|
+
```
|
19
|
+
|
20
|
+
### Required attributes
|
21
|
+
|
22
|
+
* `api_endpoint` - LXD HTTPS endpoint (e.g. https://127.0.0.1:8443)
|
23
|
+
* `ssl_key` - Path to client SSL key
|
24
|
+
* `ssl_certificate` - Path to client SSL certificate
|
25
|
+
|
26
|
+
### Initial connection required attributes
|
27
|
+
|
28
|
+
* `name` - Name of this client (defaults to hostname)
|
29
|
+
* `password` - Shared password with LXD to establish trust
|
30
|
+
|
31
|
+
## Current support matrix
|
32
|
+
|
33
|
+
|Model |Create|Read|Update|Delete|
|
34
|
+
|--------------|------|----|------|------|
|
35
|
+
|AutoScale | | | | |
|
36
|
+
|BlockStorage | | | | |
|
37
|
+
|Compute | X | X | X | X |
|
38
|
+
|DNS | | | | |
|
39
|
+
|LoadBalancer | | | | |
|
40
|
+
|Network | | | | |
|
41
|
+
|Orchestration | | | | |
|
42
|
+
|Queues | | | | |
|
43
|
+
|Storage | | | | |
|
44
|
+
|
45
|
+
## Info
|
46
|
+
* Repository: https://github.com/miasma-rb/miasma-lxd
|
@@ -0,0 +1,258 @@
|
|
1
|
+
require 'miasma'
|
2
|
+
require 'shellwords'
|
3
|
+
require 'bogo-websocket'
|
4
|
+
|
5
|
+
module Miasma
|
6
|
+
module Models
|
7
|
+
class Compute
|
8
|
+
# LXD compute API
|
9
|
+
class Lxd < Compute
|
10
|
+
|
11
|
+
include Contrib::Lxd::Api
|
12
|
+
|
13
|
+
# State mapping based on status
|
14
|
+
SERVER_STATE_MAP = Smash.new(
|
15
|
+
'running' => :running,
|
16
|
+
'stopped' => :stopped
|
17
|
+
)
|
18
|
+
|
19
|
+
# Reload a server model's data
|
20
|
+
#
|
21
|
+
# @param server [Miasma::Models::Compute::Server]
|
22
|
+
# @return [Miasma::Models::Compute::Server]
|
23
|
+
def server_reload(server)
|
24
|
+
result = request(
|
25
|
+
:path => "containers/#{server.id}",
|
26
|
+
:expects => [200, 404]
|
27
|
+
)
|
28
|
+
if(result[:response].code == 200)
|
29
|
+
result = result.get(:body, :metadata)
|
30
|
+
server.load_data(
|
31
|
+
:id => result[:name],
|
32
|
+
:name => result[:name],
|
33
|
+
:state => SERVER_STATE_MAP.fetch(result.get(:status, :status).downcase, :pending),
|
34
|
+
:status => result.fetch(:status, :status, 'unknown').downcase,
|
35
|
+
:addresses_private => (result.get(:status, :ips) || []).map{ |ip|
|
36
|
+
Server::Address.new(
|
37
|
+
:version => ip[:protocol].downcase.sub('ipv', '').to_i,
|
38
|
+
:address => ip[:address]
|
39
|
+
)
|
40
|
+
},
|
41
|
+
:image_id => 'unknown',
|
42
|
+
:flavor_id => result.get(:profiles).first,
|
43
|
+
:userdata => result.get(:userdata),
|
44
|
+
:custom => Smash.new(
|
45
|
+
:ephemeral => result[:ephemeral]
|
46
|
+
)
|
47
|
+
)
|
48
|
+
else
|
49
|
+
server.load_data(
|
50
|
+
:id => server.id,
|
51
|
+
:name => server.name,
|
52
|
+
:state => :terminated,
|
53
|
+
:status => 'terminated',
|
54
|
+
:addresses_private => [],
|
55
|
+
:image_id => 'none',
|
56
|
+
:flavor_id => 'none'
|
57
|
+
)
|
58
|
+
end
|
59
|
+
server.valid_state
|
60
|
+
end
|
61
|
+
|
62
|
+
# Destroy an existing server
|
63
|
+
#
|
64
|
+
# @param server [Miasma::Models::Compute::Server]
|
65
|
+
# @return [TrueClass, FalseClass]
|
66
|
+
def server_destroy(server)
|
67
|
+
if(server.persisted?)
|
68
|
+
if(server.state == :running)
|
69
|
+
result = request(
|
70
|
+
:path => "containers/#{server.id}/state",
|
71
|
+
:method => :put,
|
72
|
+
:expects => 202,
|
73
|
+
:json => {
|
74
|
+
:action => :stop,
|
75
|
+
:force => true
|
76
|
+
}
|
77
|
+
)
|
78
|
+
wait_for_operation(result.get(:body, :operation))
|
79
|
+
end
|
80
|
+
request(
|
81
|
+
:path => "containers/#{server.id}",
|
82
|
+
:method => :delete,
|
83
|
+
:expects => 202
|
84
|
+
)
|
85
|
+
true
|
86
|
+
else
|
87
|
+
false
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
# Save the server (create or update)
|
92
|
+
#
|
93
|
+
# @param server [Miasma::Models::Compute::Server]
|
94
|
+
# @return [Miasma::Models::Compute::Server]
|
95
|
+
def server_save(server)
|
96
|
+
if(server.persisted?)
|
97
|
+
else
|
98
|
+
result = request(
|
99
|
+
:path => 'containers',
|
100
|
+
:method => :post,
|
101
|
+
:expects => 202,
|
102
|
+
:json => {
|
103
|
+
:name => server.name,
|
104
|
+
:profiles => [server.flavor_id],
|
105
|
+
:ephemeral => server.custom.fetch(:ephemeral, false),
|
106
|
+
:source => {
|
107
|
+
:type => :image,
|
108
|
+
:alias => server.image_id
|
109
|
+
}
|
110
|
+
}
|
111
|
+
)
|
112
|
+
wait_for_operation(result.get(:body, :operation))
|
113
|
+
request(
|
114
|
+
:path => "containers/#{server.name}/state",
|
115
|
+
:method => :put,
|
116
|
+
:expects => 202,
|
117
|
+
:json => {
|
118
|
+
:action => :start
|
119
|
+
}
|
120
|
+
)
|
121
|
+
wait_for_operation(result.get(:body, :operation))
|
122
|
+
server.id = server.name
|
123
|
+
server
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
# Return all servers
|
128
|
+
#
|
129
|
+
# @return [Array<Miasma::Models::Compute::Server>]
|
130
|
+
def server_all
|
131
|
+
result = request(
|
132
|
+
:path => 'containers'
|
133
|
+
).get(:body)
|
134
|
+
result.fetch(:metadata, []).map do |c_info|
|
135
|
+
c_name = c_info.sub("/#{version}/containers/", '')
|
136
|
+
Server.new(
|
137
|
+
self,
|
138
|
+
:id => c_name,
|
139
|
+
:name => c_name
|
140
|
+
).valid_state
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
# Fetch file from server
|
145
|
+
#
|
146
|
+
# @param server [Miasma::Models::Compute::Server]
|
147
|
+
# @param path [String] remote path
|
148
|
+
# @return [IO-ish]
|
149
|
+
def server_get_file(server, path)
|
150
|
+
request(
|
151
|
+
:path => "containers/#{server.id}/files",
|
152
|
+
:params => {
|
153
|
+
:path => path
|
154
|
+
}
|
155
|
+
).get(:body)
|
156
|
+
end
|
157
|
+
|
158
|
+
# Put file on server
|
159
|
+
#
|
160
|
+
# @param server [Miasma::Models::Compute::Server]
|
161
|
+
# @param io [IO-ish]
|
162
|
+
# @param remote_path [String]
|
163
|
+
# @return [TrueClass]
|
164
|
+
# @todo update to write in chunks when over `n` size
|
165
|
+
def server_put_file(server, io, remote_path, options={})
|
166
|
+
request(
|
167
|
+
:method => :post,
|
168
|
+
:path => "containers/#{server.id}/files",
|
169
|
+
:params => {
|
170
|
+
:path => remote_path
|
171
|
+
},
|
172
|
+
:body => io.read,
|
173
|
+
:headers => {
|
174
|
+
'X-LXD-uid' => options.fetch(:uid, 0),
|
175
|
+
'X-LXD-gid' => options.fetch(:gid, 0),
|
176
|
+
'X-LXD-mode' => options.fetch(:mode, 0700)
|
177
|
+
}
|
178
|
+
)
|
179
|
+
true
|
180
|
+
end
|
181
|
+
|
182
|
+
# Execute command
|
183
|
+
#
|
184
|
+
# @param server [Miasma::Models::Compute::Server]
|
185
|
+
# @param command [String]
|
186
|
+
# @param options [Hash]
|
187
|
+
# @option options [IO] :stream write command output
|
188
|
+
# @return [TrueClass, FalseClass] command was successful
|
189
|
+
def server_execute(server, command, options={})
|
190
|
+
result = request(
|
191
|
+
:method => :post,
|
192
|
+
:path => "containers/#{server.id}/exec",
|
193
|
+
:expects => 202,
|
194
|
+
:json => Smash.new(
|
195
|
+
:command => Shellwords.shellwords(command),
|
196
|
+
:interactive => true,
|
197
|
+
'wait-for-websocket' => true
|
198
|
+
)
|
199
|
+
)
|
200
|
+
dest = URI.parse(api_endpoint)
|
201
|
+
operation = result.get(:body, :operation).sub("/#{version}/operations/", '')
|
202
|
+
ws_common = Smash.new(
|
203
|
+
:ssl_key => ssl_key,
|
204
|
+
:ssl_certificate => ssl_certificate
|
205
|
+
)
|
206
|
+
if(options[:stream])
|
207
|
+
ws_common[:on_message] = proc{|message|
|
208
|
+
options[:stream].write(message)
|
209
|
+
}
|
210
|
+
end
|
211
|
+
websockets = Smash[
|
212
|
+
['control', '0'].map do |fd_id|
|
213
|
+
fd_secret = result.get(:body, :metadata, :fds, fd_id)
|
214
|
+
[
|
215
|
+
fd_id,
|
216
|
+
Bogo::Websocket::Client.new(
|
217
|
+
ws_common.merge(
|
218
|
+
:destination => "wss://#{[dest.host, dest.port].compact.join(':')}",
|
219
|
+
:path => "/#{version}/operations/#{operation}/websocket",
|
220
|
+
:params => {
|
221
|
+
:secret => fd_secret
|
222
|
+
}
|
223
|
+
)
|
224
|
+
)
|
225
|
+
]
|
226
|
+
end
|
227
|
+
]
|
228
|
+
wait_for_operation(operation, options.fetch(:timeout, 20))
|
229
|
+
websockets.map(&:last).map(&:close)
|
230
|
+
result = request(
|
231
|
+
:path => "operations/#{operation}"
|
232
|
+
)
|
233
|
+
result.get(:body, :metadata, :metadata, :return) == 0
|
234
|
+
end
|
235
|
+
|
236
|
+
protected
|
237
|
+
|
238
|
+
# Wait for a remote operation to complete
|
239
|
+
#
|
240
|
+
# @param op_uuid [String]
|
241
|
+
# @param timeout [Integer]
|
242
|
+
# @return [TrueClass]
|
243
|
+
def wait_for_operation(op_uuid, timeout=20)
|
244
|
+
op_uuid = op_uuid.sub("/#{version}/operations/", '')
|
245
|
+
request(
|
246
|
+
:path => "operations/#{op_uuid}/wait",
|
247
|
+
:params => {
|
248
|
+
:status_code => 200,
|
249
|
+
:timeout => 20
|
250
|
+
}
|
251
|
+
)
|
252
|
+
true
|
253
|
+
end
|
254
|
+
|
255
|
+
end
|
256
|
+
end
|
257
|
+
end
|
258
|
+
end
|
@@ -0,0 +1,103 @@
|
|
1
|
+
require 'miasma'
|
2
|
+
|
3
|
+
module Miasma
|
4
|
+
module Contrib
|
5
|
+
module Lxd
|
6
|
+
# API updates required for LXD communication
|
7
|
+
module Api
|
8
|
+
|
9
|
+
# Default API version to require
|
10
|
+
DEFAULT_API_VERSION = '1.0'
|
11
|
+
|
12
|
+
# Load required attributes into API class
|
13
|
+
#
|
14
|
+
# @param klass [Class]
|
15
|
+
def self.included(klass)
|
16
|
+
klass.class_eval do
|
17
|
+
attribute :name, String, :required => true, :default => Socket.gethostname
|
18
|
+
attribute :password, String
|
19
|
+
attribute :api_endpoint, String, :required => true
|
20
|
+
attribute :ssl_certificate, String, :required => true
|
21
|
+
attribute :ssl_key, String, :required => true
|
22
|
+
attribute :version, Gem::Version, :coerce => lambda{|v| Gem::Version.new(v.to_s)}, :required => true, :default => lambda{ Miasma::Contrib::Lxd::Api::DEFAULT_API_VERSION }
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
# @return [String] versioned endpoint
|
27
|
+
def endpoint
|
28
|
+
"#{api_endpoint}/#{version}"
|
29
|
+
end
|
30
|
+
|
31
|
+
# Clean up endpoint prior to data loading
|
32
|
+
#
|
33
|
+
# @param args [Hash]
|
34
|
+
def custom_setup(args)
|
35
|
+
if(args[:api_endpoint].to_s.end_with?('/'))
|
36
|
+
args[:api_endpoint] = args[:api_endpoint][0, endpoint.length - 1]
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
# Perform request
|
41
|
+
#
|
42
|
+
# @param connection [HTTP]
|
43
|
+
# @param http_method [Symbol]
|
44
|
+
# @param request_args [Array]
|
45
|
+
# @return [HTTP::Response]
|
46
|
+
def make_request(connection, http_method, request_args)
|
47
|
+
dest, options = request_args
|
48
|
+
options = Smash.new unless options
|
49
|
+
options[:ssl_context] = ssl_context
|
50
|
+
connection.send(http_method, dest, options)
|
51
|
+
end
|
52
|
+
|
53
|
+
protected
|
54
|
+
|
55
|
+
# @return [OpenSSL::SSL::SSLContext.new]
|
56
|
+
def ssl_context
|
57
|
+
memoize(:ssl_context) do
|
58
|
+
ctx = OpenSSL::SSL::SSLContext.new
|
59
|
+
ctx.cert = OpenSSL::X509::Certificate.new(File.read(ssl_certificate))
|
60
|
+
ctx.key = OpenSSL::PKey::RSA.new(File.read(ssl_key))
|
61
|
+
ctx
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
# Establish connection to endpoint and register client if not
|
66
|
+
# already registered
|
67
|
+
def connect
|
68
|
+
result = request(:endpoint => api_endpoint)[:body]
|
69
|
+
unless(result[:metadata].any?{|s| s.end_with?(version.to_s)})
|
70
|
+
raise InvalidVersionError
|
71
|
+
end
|
72
|
+
result = request(:endpoint => api_endpoint, :path => "/#{version}")[:body]
|
73
|
+
if(result[:auth] != 'trusted' && password)
|
74
|
+
authenticate_connection!
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
# Authenticate with the endpoint to create trusted connection
|
79
|
+
def authenticate_connection!
|
80
|
+
request(
|
81
|
+
:method => :post,
|
82
|
+
:path => 'certificates',
|
83
|
+
:json => {
|
84
|
+
:type => :client,
|
85
|
+
:name => name,
|
86
|
+
:password => password
|
87
|
+
}
|
88
|
+
)
|
89
|
+
end
|
90
|
+
|
91
|
+
# Always allow retry
|
92
|
+
#
|
93
|
+
# @return [TrueClass]
|
94
|
+
def retryable_allowed?(*_)
|
95
|
+
false
|
96
|
+
end
|
97
|
+
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
Models::Compute.autoload :Lxd, 'miasma/contrib/lxd/compute'
|
103
|
+
end
|
data/lib/miasma-lxd.rb
ADDED
data/miasma-lxd.gemspec
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
$LOAD_PATH.unshift File.expand_path(File.dirname(__FILE__)) + '/lib/'
|
2
|
+
require 'miasma-lxd/version'
|
3
|
+
Gem::Specification.new do |s|
|
4
|
+
s.name = 'miasma-lxd'
|
5
|
+
s.version = MiasmaLxd::VERSION.version
|
6
|
+
s.summary = 'Smoggy LXD API'
|
7
|
+
s.author = 'Chris Roberts'
|
8
|
+
s.email = 'code@chrisroberts.org'
|
9
|
+
s.homepage = 'https://github.com/miasma-rb/miasma-lxd'
|
10
|
+
s.description = 'Smoggy LXD API'
|
11
|
+
s.license = 'Apache 2.0'
|
12
|
+
s.require_path = 'lib'
|
13
|
+
s.add_runtime_dependency 'bogo-websocket', '< 1.0.0'
|
14
|
+
s.add_development_dependency 'miasma', '>= 0.2.31'
|
15
|
+
s.add_development_dependency 'pry'
|
16
|
+
s.add_development_dependency 'minitest'
|
17
|
+
s.add_development_dependency 'vcr'
|
18
|
+
s.add_development_dependency 'webmock'
|
19
|
+
s.add_development_dependency 'psych', '>= 2.0.8'
|
20
|
+
s.files = Dir['lib/**/*'] + %w(miasma-lxd.gemspec README.md CHANGELOG.md LICENSE)
|
21
|
+
end
|
metadata
ADDED
@@ -0,0 +1,149 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: miasma-lxd
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Chris Roberts
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2015-10-05 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: bogo-websocket
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "<"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: 1.0.0
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "<"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: 1.0.0
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: miasma
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: 0.2.31
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: 0.2.31
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: pry
|
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: minitest
|
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: vcr
|
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: webmock
|
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
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: psych
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - ">="
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: 2.0.8
|
104
|
+
type: :development
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - ">="
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: 2.0.8
|
111
|
+
description: Smoggy LXD API
|
112
|
+
email: code@chrisroberts.org
|
113
|
+
executables: []
|
114
|
+
extensions: []
|
115
|
+
extra_rdoc_files: []
|
116
|
+
files:
|
117
|
+
- CHANGELOG.md
|
118
|
+
- LICENSE
|
119
|
+
- README.md
|
120
|
+
- lib/miasma-lxd.rb
|
121
|
+
- lib/miasma-lxd/version.rb
|
122
|
+
- lib/miasma/contrib/lxd.rb
|
123
|
+
- lib/miasma/contrib/lxd/compute.rb
|
124
|
+
- miasma-lxd.gemspec
|
125
|
+
homepage: https://github.com/miasma-rb/miasma-lxd
|
126
|
+
licenses:
|
127
|
+
- Apache 2.0
|
128
|
+
metadata: {}
|
129
|
+
post_install_message:
|
130
|
+
rdoc_options: []
|
131
|
+
require_paths:
|
132
|
+
- lib
|
133
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
134
|
+
requirements:
|
135
|
+
- - ">="
|
136
|
+
- !ruby/object:Gem::Version
|
137
|
+
version: '0'
|
138
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
139
|
+
requirements:
|
140
|
+
- - ">="
|
141
|
+
- !ruby/object:Gem::Version
|
142
|
+
version: '0'
|
143
|
+
requirements: []
|
144
|
+
rubyforge_project:
|
145
|
+
rubygems_version: 2.4.8
|
146
|
+
signing_key:
|
147
|
+
specification_version: 4
|
148
|
+
summary: Smoggy LXD API
|
149
|
+
test_files: []
|