nightcrawler_swift 0.11.1 → 1.0.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 +4 -4
- data/.ruby-version +1 -1
- data/Gemfile.lock +5 -5
- data/README.md +18 -2
- data/lib/nightcrawler_swift/commands/delete.rb +1 -1
- data/lib/nightcrawler_swift/commands/upload.rb +5 -1
- data/lib/nightcrawler_swift/connection.rb +30 -10
- data/lib/nightcrawler_swift/version.rb +1 -1
- data/spec/fixtures/auth_success.json +81 -56
- data/spec/lib/nightcrawler_swift/commands/delete_spec.rb +15 -1
- data/spec/lib/nightcrawler_swift/commands/upload_spec.rb +7 -0
- data/spec/lib/nightcrawler_swift/connection_spec.rb +55 -19
- metadata +3 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 7368872bbed33a3a4928360a3162cbec161ad31d
|
4
|
+
data.tar.gz: 7599b6e39f3e7a0c376518beed4deb89978a68f9
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c4d15c9c9fc23c71b7dfa04cceaa00bf0152603fae8372c5527a5a0cbfce9bbf54438d4c334d39ad483e94b4d2ff724f90e663273e7ca833aef65341243bcbc0
|
7
|
+
data.tar.gz: 866b75e3f995ce7754712bb12b13967ff5009d2db7a5e657aba7b14bc6865b9b5222b212ba7b21c96331d6b0ed9663b57ba6bcdd7853ac5502470e21c2348bb8
|
data/.ruby-version
CHANGED
@@ -1 +1 @@
|
|
1
|
-
ruby-2.1
|
1
|
+
ruby-2.4.1
|
data/Gemfile.lock
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
nightcrawler_swift (0.
|
4
|
+
nightcrawler_swift (1.0.0)
|
5
5
|
concurrent-ruby (~> 0.8.0)
|
6
6
|
multi_mime (>= 1.1.0)
|
7
7
|
rest-client
|
@@ -21,7 +21,7 @@ GEM
|
|
21
21
|
debugger-linecache (1.2.0)
|
22
22
|
diff-lcs (1.2.5)
|
23
23
|
docile (1.1.5)
|
24
|
-
domain_name (0.5.
|
24
|
+
domain_name (0.5.20170404)
|
25
25
|
unf (>= 0.0.5, < 1.0.0)
|
26
26
|
http-cookie (1.0.3)
|
27
27
|
domain_name (~> 0.5)
|
@@ -33,7 +33,7 @@ GEM
|
|
33
33
|
netrc (0.11.0)
|
34
34
|
rake (10.3.2)
|
35
35
|
ref (1.0.5)
|
36
|
-
rest-client (2.0.
|
36
|
+
rest-client (2.0.2)
|
37
37
|
http-cookie (>= 1.0.2, < 2.0)
|
38
38
|
mime-types (>= 1.16, < 4.0)
|
39
39
|
netrc (~> 0.8)
|
@@ -58,7 +58,7 @@ GEM
|
|
58
58
|
timecop (0.8.0)
|
59
59
|
unf (0.1.4)
|
60
60
|
unf_ext
|
61
|
-
unf_ext (0.0.7.
|
61
|
+
unf_ext (0.0.7.4)
|
62
62
|
|
63
63
|
PLATFORMS
|
64
64
|
ruby
|
@@ -73,4 +73,4 @@ DEPENDENCIES
|
|
73
73
|
timecop
|
74
74
|
|
75
75
|
BUNDLED WITH
|
76
|
-
1.
|
76
|
+
1.15.3
|
data/README.md
CHANGED
@@ -6,6 +6,10 @@
|
|
6
6
|
|
7
7
|
Like the X-Men nightcrawler this gem teleports your assets to a OpenStack Swift bucket/container. It was designed to sync your assets with OpenStack Swift and allow some operations with your buckets/containers.
|
8
8
|
|
9
|
+
## Compatibility
|
10
|
+
|
11
|
+
nightcrawler_swift version 1.x is compatible with [Identity API v3](https://developer.openstack.org/api-ref/identity/v3/index.html). To use [API v2](https://developer.openstack.org/api-ref/identity/v2/index.html), you should install version 0.x.
|
12
|
+
|
9
13
|
## Installation
|
10
14
|
|
11
15
|
Add this line to your application's Gemfile:
|
@@ -14,16 +18,28 @@ Add this line to your application's Gemfile:
|
|
14
18
|
gem 'nightcrawler_swift'
|
15
19
|
```
|
16
20
|
|
21
|
+
Or, if you want to use Identity API v2:
|
22
|
+
|
23
|
+
```ruby
|
24
|
+
gem 'nightcrawler_swift', '< 1.0'
|
25
|
+
```
|
26
|
+
|
17
27
|
And then execute:
|
18
28
|
|
19
29
|
```sh
|
20
30
|
$ bundle
|
21
31
|
```
|
22
32
|
|
23
|
-
Or install it
|
33
|
+
Or install it manually:
|
34
|
+
|
35
|
+
```sh
|
36
|
+
$ gem install nightcrawler_swift # Install latest version
|
37
|
+
```
|
38
|
+
|
39
|
+
Or set a specific version:
|
24
40
|
|
25
41
|
```sh
|
26
|
-
$ gem install nightcrawler_swift
|
42
|
+
$ gem install nightcrawler_swift -v 0.11.1
|
27
43
|
```
|
28
44
|
|
29
45
|
## Usage
|
@@ -2,6 +2,10 @@ module NightcrawlerSwift
|
|
2
2
|
class Upload < Command
|
3
3
|
|
4
4
|
def execute path, file, opts = {}
|
5
|
+
if path.nil? or path.empty?
|
6
|
+
raise Exceptions::ValidationError.new "Upload command requires a path parameter"
|
7
|
+
end
|
8
|
+
|
5
9
|
body = file.read
|
6
10
|
headers = {etag: etag(body), content_type: content_type(file)}
|
7
11
|
|
@@ -18,7 +22,7 @@ module NightcrawlerSwift
|
|
18
22
|
headers.merge!(custom_headers) if custom_headers
|
19
23
|
|
20
24
|
response = put "#{connection.upload_url}/#{path}", body: body, headers: headers
|
21
|
-
|
25
|
+
response.code >= 200 && response.code < 300
|
22
26
|
end
|
23
27
|
|
24
28
|
private
|
@@ -50,7 +50,7 @@ module NightcrawlerSwift
|
|
50
50
|
headers = {content_type: :json, accept: :json}
|
51
51
|
response = Gateway.new(url).request {|r| r.post(auth_options.to_json, headers)}
|
52
52
|
|
53
|
-
@auth_response = OpenStruct.new(JSON.parse(response.body))
|
53
|
+
@auth_response = OpenStruct.new(headers: response.headers, body: JSON.parse(response.body))
|
54
54
|
rescue StandardError => e
|
55
55
|
raise Exceptions::ConnectionError.new(e)
|
56
56
|
end
|
@@ -58,32 +58,52 @@ module NightcrawlerSwift
|
|
58
58
|
def auth_options
|
59
59
|
{
|
60
60
|
auth: {
|
61
|
-
|
62
|
-
|
61
|
+
identity: {
|
62
|
+
methods: [
|
63
|
+
"password"
|
64
|
+
],
|
65
|
+
password: {
|
66
|
+
user: {
|
67
|
+
domain: {
|
68
|
+
id: "default"
|
69
|
+
},
|
70
|
+
name: opts.username,
|
71
|
+
password: opts.password
|
72
|
+
}
|
73
|
+
}
|
74
|
+
},
|
75
|
+
scope: {
|
76
|
+
project: {
|
77
|
+
domain: {
|
78
|
+
id: "default"
|
79
|
+
},
|
80
|
+
name: opts.tenant_name
|
81
|
+
}
|
82
|
+
}
|
63
83
|
}
|
64
84
|
}
|
65
85
|
end
|
66
86
|
|
67
87
|
def select_token
|
68
|
-
@token_id = auth_response.
|
69
|
-
@expires_at = auth_response.
|
88
|
+
@token_id = auth_response.headers[:x_subject_token]
|
89
|
+
@expires_at = auth_response.body["token"]["expires_at"]
|
70
90
|
@expires_at = DateTime.parse(@expires_at).to_time
|
71
91
|
end
|
72
92
|
|
73
93
|
def select_catalog
|
74
|
-
catalogs = auth_response
|
94
|
+
catalogs = auth_response["body"]["token"]["catalog"]
|
75
95
|
@catalog = catalogs.find {|catalog| catalog["type"] == "object-store"}
|
76
96
|
end
|
77
97
|
|
78
98
|
def select_endpoints
|
79
99
|
raise Exceptions::ConfigurationError.new "No catalog of type 'object-store' found" if @catalog.nil?
|
80
|
-
@endpoints = @catalog["endpoints"]
|
100
|
+
@endpoints = @catalog["endpoints"]
|
81
101
|
end
|
82
102
|
|
83
103
|
def configure_urls
|
84
|
-
@admin_url = opts.admin_url || @endpoints["
|
85
|
-
@public_url = opts.public_url || @endpoints["
|
86
|
-
@internal_url = opts.internal_url || @endpoints["
|
104
|
+
@admin_url = opts.admin_url || @endpoints.find {|e| e["interface"] == "admin"}["url"]
|
105
|
+
@public_url = opts.public_url || @endpoints.find {|e| e["interface"] == "public"}["url"]
|
106
|
+
@internal_url = opts.internal_url || @endpoints.find {|e| e["interface"] == "internal"}["url"]
|
87
107
|
@upload_url = "#{@admin_url}/#{opts.bucket}"
|
88
108
|
end
|
89
109
|
end
|
@@ -1,62 +1,87 @@
|
|
1
1
|
{
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
{
|
10
|
-
"publicURL" : "http://public-url-com",
|
11
|
-
"internalURL" : "http://internal-url-com",
|
12
|
-
"adminURL" : "http://api-url-com",
|
13
|
-
"region" : "RegionOne",
|
14
|
-
"id" : "1"
|
15
|
-
}
|
16
|
-
]
|
17
|
-
},
|
18
|
-
{
|
19
|
-
"name" : "keystone",
|
20
|
-
"endpoints_links" : [],
|
21
|
-
"type" : "identity",
|
22
|
-
"endpoints" : [
|
23
|
-
{
|
24
|
-
"publicURL" : "http://auth-url-com:123/v2.0",
|
25
|
-
"internalURL" : "https://auth-url-com:123/v2.0",
|
26
|
-
"adminURL" : "https://auth-url-com:321/v2.0",
|
27
|
-
"region" : "RegionOne",
|
28
|
-
"id" : "2"
|
29
|
-
}
|
30
|
-
]
|
31
|
-
}
|
2
|
+
"headers": {
|
3
|
+
"X-Subject-Token": "12345"
|
4
|
+
},
|
5
|
+
"body": {
|
6
|
+
"token": {
|
7
|
+
"methods": [
|
8
|
+
"password"
|
32
9
|
],
|
33
|
-
"
|
34
|
-
|
10
|
+
"roles": [
|
11
|
+
{
|
12
|
+
"id": "3fe2ff9ee4384b1894a90878d3e92bab",
|
13
|
+
"name": "_member_"
|
14
|
+
},
|
15
|
+
{
|
16
|
+
"id": "c573d00d11ed4f75a7cae8e7527eb1ed",
|
17
|
+
"name": "swiftoperator"
|
18
|
+
}
|
19
|
+
],
|
20
|
+
"expires_at": "2017-07-06T18:24:31.037559Z",
|
21
|
+
"project": {
|
22
|
+
"domain": {
|
23
|
+
"id": "default",
|
24
|
+
"name": "Default"
|
25
|
+
},
|
26
|
+
"id": "5c6a4110f7f9436b8e1f6696f9f6a13f",
|
27
|
+
"name": "tenant_username1"
|
28
|
+
},
|
29
|
+
"catalog": [
|
30
|
+
{
|
31
|
+
"name" : "swift",
|
32
|
+
"id": "0f7d75e8a91249628ad63356002b4869",
|
33
|
+
"type" : "object-store",
|
34
|
+
"endpoints" : [
|
35
|
+
{
|
36
|
+
"region_id": "RegionOne",
|
37
|
+
"url": "http://auth-url-com:123/v3/admin",
|
38
|
+
"region": "RegionOne",
|
39
|
+
"interface": "admin",
|
40
|
+
"id": "a723954cc89d3e93a65c1f658132773d"
|
41
|
+
},
|
35
42
|
{
|
36
|
-
|
43
|
+
"region_id": "RegionOne",
|
44
|
+
"url": "http://auth-url-com:123/v3/internal",
|
45
|
+
"region": "RegionOne",
|
46
|
+
"interface": "internal",
|
47
|
+
"id": "beea797b69fa47159e44a220ea05c61c"
|
48
|
+
},
|
49
|
+
{
|
50
|
+
"region_id": "RegionOne",
|
51
|
+
"url": "http://auth-url-com:123/v3/public",
|
52
|
+
"region": "RegionOne",
|
53
|
+
"interface": "public",
|
54
|
+
"id": "9e36056aba514263a1d1d8ae10cfb796"
|
37
55
|
}
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
56
|
+
]
|
57
|
+
},
|
58
|
+
{
|
59
|
+
"name" : "keystone",
|
60
|
+
"id" : "7efb509d3a0b446fa324d0bc4503e6d3",
|
61
|
+
"type" : "identity",
|
62
|
+
"endpoints" : [
|
63
|
+
{
|
64
|
+
"region_id" : "RegionOne",
|
65
|
+
"url" : "http://auth-url-com:123/v3/public",
|
66
|
+
"interface": "public",
|
67
|
+
"id" : "29e36056aba514263a1d1d8ae10cfb79"
|
68
|
+
}
|
69
|
+
]
|
70
|
+
}
|
71
|
+
],
|
72
|
+
"extras": {},
|
73
|
+
"user": {
|
74
|
+
"domain": {
|
75
|
+
"id": "default",
|
76
|
+
"name": "Default"
|
77
|
+
},
|
78
|
+
"id": "83e2c39492e343f08b0621ddc2e46eab",
|
79
|
+
"name": "username1"
|
49
80
|
},
|
50
|
-
"
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
},
|
57
|
-
"issued_at" : "2014-08-25T17:53:26.667691",
|
58
|
-
"id" : "5",
|
59
|
-
"expires" : "2014-08-25T18:53:26Z"
|
60
|
-
}
|
61
|
-
}
|
81
|
+
"audit_ids": [
|
82
|
+
"B77HzuC7QimfKre8DhDrhw"
|
83
|
+
],
|
84
|
+
"issued_at": "2017-07-06T17:24:31.037629Z"
|
85
|
+
}
|
86
|
+
}
|
62
87
|
}
|
@@ -25,7 +25,7 @@ describe NightcrawlerSwift::Delete do
|
|
25
25
|
|
26
26
|
context "success" do
|
27
27
|
let :response do
|
28
|
-
double(:response, code:
|
28
|
+
double(:response, code: 204)
|
29
29
|
end
|
30
30
|
|
31
31
|
before do
|
@@ -42,6 +42,20 @@ describe NightcrawlerSwift::Delete do
|
|
42
42
|
end
|
43
43
|
end
|
44
44
|
|
45
|
+
context "failure" do
|
46
|
+
let :response do
|
47
|
+
double(:response, code: 404)
|
48
|
+
end
|
49
|
+
|
50
|
+
before do
|
51
|
+
allow(subject).to receive(:delete).and_return(response)
|
52
|
+
end
|
53
|
+
|
54
|
+
it "returns false" do
|
55
|
+
expect(execute).to eql false
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
45
59
|
context "when the path was not informed" do
|
46
60
|
it "raises NightcrawlerSwift::Exceptions::ValidationError" do
|
47
61
|
expect { subject.execute nil }.to raise_error NightcrawlerSwift::Exceptions::ValidationError
|
@@ -165,6 +165,13 @@ describe NightcrawlerSwift::Upload do
|
|
165
165
|
let(:response) { double(:response, code: 500) }
|
166
166
|
it { expect(execute).to be false }
|
167
167
|
end
|
168
|
+
|
169
|
+
context "when the path was not informed" do
|
170
|
+
it "raises NightcrawlerSwift::Exceptions::ValidationError" do
|
171
|
+
expect { subject.execute(nil, file) }.to raise_error NightcrawlerSwift::Exceptions::ValidationError
|
172
|
+
expect { subject.execute("", file) }.to raise_error NightcrawlerSwift::Exceptions::ValidationError
|
173
|
+
end
|
174
|
+
end
|
168
175
|
end
|
169
176
|
|
170
177
|
end
|
@@ -8,7 +8,7 @@ describe NightcrawlerSwift::Connection do
|
|
8
8
|
tenant_name: "tenant_username1",
|
9
9
|
username: "username1",
|
10
10
|
password: "some-pass",
|
11
|
-
auth_url: "https://auth-url-com:123/
|
11
|
+
auth_url: "https://auth-url-com:123/v3/auth/tokens",
|
12
12
|
max_age: 31536000 # 1 year
|
13
13
|
}
|
14
14
|
end
|
@@ -30,19 +30,48 @@ describe NightcrawlerSwift::Connection do
|
|
30
30
|
let :auth_json do
|
31
31
|
{
|
32
32
|
auth: {
|
33
|
-
|
34
|
-
|
33
|
+
identity: {
|
34
|
+
methods: [
|
35
|
+
"password"
|
36
|
+
],
|
37
|
+
password: {
|
38
|
+
user: {
|
39
|
+
domain: {
|
40
|
+
id: "default"
|
41
|
+
},
|
42
|
+
name: opts[:username],
|
43
|
+
password: opts[:password]
|
44
|
+
}
|
45
|
+
}
|
46
|
+
},
|
47
|
+
scope: {
|
48
|
+
project: {
|
49
|
+
domain: {
|
50
|
+
id: "default"
|
51
|
+
},
|
52
|
+
name: opts[:tenant_name]
|
53
|
+
}
|
54
|
+
}
|
35
55
|
}
|
36
56
|
}.to_json
|
37
57
|
end
|
38
58
|
|
39
59
|
let :auth_success_response do
|
40
60
|
path = File.join(File.dirname(__FILE__), "../..", "fixtures/auth_success.json")
|
41
|
-
|
61
|
+
file_contents = JSON.parse(File.read(File.expand_path(path)))
|
62
|
+
headers = file_contents["headers"].reduce({}) do |h, item|
|
63
|
+
key, value = item
|
64
|
+
h[key.downcase.gsub("-", "_").to_sym] = value
|
65
|
+
h
|
66
|
+
end
|
67
|
+
OpenStruct.new(headers: headers, body: file_contents["body"].to_json)
|
42
68
|
end
|
43
69
|
|
44
70
|
let :auth_success_json do
|
45
|
-
|
71
|
+
{
|
72
|
+
"headers" => auth_success_response.headers,
|
73
|
+
"body" => JSON.parse(auth_success_response.body)
|
74
|
+
}
|
46
75
|
end
|
47
76
|
|
48
77
|
describe "when it connects" do
|
@@ -61,48 +90,55 @@ describe NightcrawlerSwift::Connection do
|
|
61
90
|
and_return(auth_success_response)
|
62
91
|
end
|
63
92
|
|
64
|
-
it "stores the
|
93
|
+
it "stores the auth response body" do
|
65
94
|
subject.connect!
|
66
95
|
# This test uses 'eq' instead of 'eql' because in Ruby 1.9.x the method
|
67
96
|
# 'equal?' is different than '==' making this test fail
|
68
|
-
expect(subject.auth_response).to eq(
|
97
|
+
expect(subject.auth_response.body).to eq(auth_success_json["body"])
|
98
|
+
end
|
99
|
+
|
100
|
+
it "stores the auth response headers" do
|
101
|
+
subject.connect!
|
102
|
+
expect(subject.auth_response.headers).to eq(auth_success_json["headers"])
|
69
103
|
end
|
70
104
|
|
71
105
|
it "stores the token id" do
|
72
106
|
subject.connect!
|
73
|
-
expect(subject.token_id).
|
107
|
+
expect(subject.token_id).not_to be_nil
|
108
|
+
expect(subject.token_id).to eql(auth_success_json["headers"][:x_subject_token])
|
74
109
|
end
|
75
110
|
|
76
111
|
it "stores the expires_at" do
|
77
112
|
subject.connect!
|
78
|
-
expires_at = DateTime.parse(auth_success_json["
|
113
|
+
expires_at = DateTime.parse(auth_success_json["body"]["token"]["expires_at"]).to_time
|
79
114
|
expect(subject.expires_at).to eql(expires_at)
|
80
115
|
end
|
81
116
|
|
82
117
|
it "stores the catalog" do
|
83
|
-
|
84
|
-
expect(subject.catalog).to eql(auth_success_json["
|
118
|
+
subject.connect!
|
119
|
+
expect(subject.catalog).to eql(auth_success_json["body"]["token"]["catalog"][0])
|
120
|
+
expect(subject.catalog["type"]).to eql("object-store")
|
85
121
|
end
|
86
122
|
|
87
123
|
it "stores the admin_url" do
|
88
|
-
|
89
|
-
expect(subject.admin_url).to eql(auth_success_json["
|
124
|
+
subject.connect!
|
125
|
+
expect(subject.admin_url).to eql(auth_success_json["body"]["token"]["catalog"][0]["endpoints"][0]["url"])
|
90
126
|
end
|
91
127
|
|
92
128
|
it "stores the upload_url" do
|
129
|
+
subject.connect!
|
93
130
|
admin_url = subject.admin_url
|
94
|
-
expect(subject).to receive(:connect!).and_call_original
|
95
131
|
expect(subject.upload_url).to eql("#{admin_url}/#{opts[:bucket]}")
|
96
132
|
end
|
97
133
|
|
98
134
|
it "stores the public_url" do
|
99
|
-
|
100
|
-
expect(subject.public_url).to eql(auth_success_json["
|
135
|
+
subject.connect!
|
136
|
+
expect(subject.public_url).to eql(auth_success_json["body"]["token"]["catalog"][0]["endpoints"][2]["url"])
|
101
137
|
end
|
102
138
|
|
103
139
|
it "stores the internal_url" do
|
104
|
-
|
105
|
-
expect(subject.internal_url).to eql(auth_success_json["
|
140
|
+
subject.connect!
|
141
|
+
expect(subject.internal_url).to eql(auth_success_json["body"]["token"]["catalog"][0]["endpoints"][1]["url"])
|
106
142
|
end
|
107
143
|
|
108
144
|
it "returns self" do
|
@@ -111,7 +147,7 @@ describe NightcrawlerSwift::Connection do
|
|
111
147
|
|
112
148
|
context "and there isn't any catalog configured" do
|
113
149
|
before do
|
114
|
-
auth_success_json["
|
150
|
+
auth_success_json["body"]["token"]["catalog"] = []
|
115
151
|
allow(subject).to receive(:auth_response).and_return(OpenStruct.new(auth_success_json))
|
116
152
|
end
|
117
153
|
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: nightcrawler_swift
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 1.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- tulios
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date:
|
12
|
+
date: 2017-08-22 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: rest-client
|
@@ -230,7 +230,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
230
230
|
version: '0'
|
231
231
|
requirements: []
|
232
232
|
rubyforge_project:
|
233
|
-
rubygems_version: 2.5.
|
233
|
+
rubygems_version: 2.5.2
|
234
234
|
signing_key:
|
235
235
|
specification_version: 4
|
236
236
|
summary: Like the X-Men nightcrawler this gem teleports your assets to a OpenStack
|