dpl 1.8.18.travis.1561.3 → 1.8.18.travis.1563.3
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 +8 -8
- data/README.md +25 -12
- data/lib/dpl/provider/exoscale.rb +100 -0
- data/spec/provider/exoscale_spec.rb +191 -0
- metadata +3 -1
checksums.yaml
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
---
|
|
2
2
|
!binary "U0hBMQ==":
|
|
3
3
|
metadata.gz: !binary |-
|
|
4
|
-
|
|
4
|
+
YTk5ZDRiZTFmNjUwYWMwNWY0MThjYTg2MzQwOWQ0Nzc4ZmU5ODVhZA==
|
|
5
5
|
data.tar.gz: !binary |-
|
|
6
|
-
|
|
6
|
+
YjczYmFmNmU0ZTI3MmJkNDJkNjA3Y2MzNzg2YTcyMmVjYzRkYTQ2OA==
|
|
7
7
|
SHA512:
|
|
8
8
|
metadata.gz: !binary |-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
9
|
+
ZjUxY2UzMDM2Mjg4NWY2MDIzNTFiNWZkZjNkYTZkM2I4NTQzMWU5MDY4NDdk
|
|
10
|
+
ZjcwZDZjOTYzN2NhODlhY2E3YTQ1OTc5ODRhZjUwOTc5YTQ4NTg0OGNlMGUx
|
|
11
|
+
Mjg2MjQyMjA3NzE2ZDlhZmI0NWFiOGVjOTNiZDg2MTE3ZWQyOTU=
|
|
12
12
|
data.tar.gz: !binary |-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
13
|
+
NDljYzc4N2QyMDdmNGNmOGI2Y2U3OGVkNTMzODg4MjI3MTljYmNlMTYzMTcy
|
|
14
|
+
M2EwZmNlYmIyNDM0MmU4OGYxY2MyMTFlM2I4MjVlZGU2YTM4NGFjOTMzZGM1
|
|
15
|
+
ZWZmNTMwMjc5NTE3N2Q5OWNiYWJjNGU2N2I2MGFiZjQ4Y2YzNzQ=
|
data/README.md
CHANGED
|
@@ -24,6 +24,7 @@ Dpl supports the following providers:
|
|
|
24
24
|
* [Deis](#deis)
|
|
25
25
|
* [Divshot.io](#divshotio)
|
|
26
26
|
* [Engine Yard](#engine-yard)
|
|
27
|
+
* [ExoScale](#exoscale)
|
|
27
28
|
* [Firebase](#firebase)
|
|
28
29
|
* [Github Releases](#github-releases)
|
|
29
30
|
* [Google App Engine (experimental)](#google-app-engine)
|
|
@@ -631,7 +632,7 @@ For accounts using two factor authentication, you have to use an oauth token as
|
|
|
631
632
|
#### Options:
|
|
632
633
|
|
|
633
634
|
* **target**: Required. The git remote repository to deploy to.
|
|
634
|
-
* **path**: Optional. If using the skip_cleanup option to deploy from current file state, you can optionally specify the pathspec for the files to deploy. If not specified then all files are deployed.
|
|
635
|
+
* **path**: Optional. If using the skip_cleanup option to deploy from current file state, you can optionally specify the pathspec for the files to deploy. If not specified then all files are deployed.
|
|
635
636
|
|
|
636
637
|
#### Examples:
|
|
637
638
|
|
|
@@ -642,17 +643,17 @@ For accounts using two factor authentication, you have to use an oauth token as
|
|
|
642
643
|
|
|
643
644
|
#### Setup:
|
|
644
645
|
|
|
645
|
-
1. Get the deployment target for Catalyze:
|
|
646
|
-
a. Make sure your catalyze environment is [associated](https://resources.catalyze.io/paas/paas-cli-reference/#associate).
|
|
647
|
-
b. Get the git remote by running ```git remote -v``` from within the associated repo.
|
|
648
|
-
2. Setup a deployment key to Catalyze for Travis CI:
|
|
649
|
-
a. Install the travis-ci cli.
|
|
650
|
-
b. Get the public SSH key for your travis project and save it to a file by running ```travis pubkey > travis.pub```
|
|
651
|
-
c. Add the key as a deploy key using the catalyze cli within the associated repo. For example: ```catalyze deploy-keys add travisci ./travis.pub code-1```
|
|
652
|
-
3. Setup Catalyze as a known host for Travis CI:
|
|
653
|
-
a. List your known hosts by running ```cat ~/.ssh/known_hosts```
|
|
654
|
-
b. Find and copy the line from known_hosts that includes the git remote found in step #1. It'll look something like "[git.catalyzeapps.com]:2222 ecdsa-sha2-nistp256 BBBB12abZmKlLXNo..."
|
|
655
|
-
c. Update your `before_deploy` step in `.travis.yml` to update the `known_hosts` file:
|
|
646
|
+
1. Get the deployment target for Catalyze:
|
|
647
|
+
a. Make sure your catalyze environment is [associated](https://resources.catalyze.io/paas/paas-cli-reference/#associate).
|
|
648
|
+
b. Get the git remote by running ```git remote -v``` from within the associated repo.
|
|
649
|
+
2. Setup a deployment key to Catalyze for Travis CI:
|
|
650
|
+
a. Install the travis-ci cli.
|
|
651
|
+
b. Get the public SSH key for your travis project and save it to a file by running ```travis pubkey > travis.pub```
|
|
652
|
+
c. Add the key as a deploy key using the catalyze cli within the associated repo. For example: ```catalyze deploy-keys add travisci ./travis.pub code-1```
|
|
653
|
+
3. Setup Catalyze as a known host for Travis CI:
|
|
654
|
+
a. List your known hosts by running ```cat ~/.ssh/known_hosts```
|
|
655
|
+
b. Find and copy the line from known_hosts that includes the git remote found in step #1. It'll look something like "[git.catalyzeapps.com]:2222 ecdsa-sha2-nistp256 BBBB12abZmKlLXNo..."
|
|
656
|
+
c. Update your `before_deploy` step in `.travis.yml` to update the `known_hosts` file:
|
|
656
657
|
```
|
|
657
658
|
before_deploy: echo "[git.catalyzeapps.com]:2222 ecdsa-sha2-nistp256 BBBB12abZmKlLXNo..." >> ~/.ssh/known_hosts
|
|
658
659
|
```
|
|
@@ -776,6 +777,18 @@ and your testers can start testing your app.
|
|
|
776
777
|
|
|
777
778
|
dpl --provider=codedeploy --access-key-id=<aws access key> --secret_access_key=<aws secret access key> --application=<application name> --deployment_group=<deployment group> --revision_type=<s3/github> --commit_id=<commit ID> --repository=<repo name> --region=<AWS availability zone> --wait-until-deployed=<true>
|
|
778
779
|
|
|
780
|
+
### ExoScale:
|
|
781
|
+
|
|
782
|
+
#### Options:
|
|
783
|
+
|
|
784
|
+
* **email**: ExoScale email or Organization ID.
|
|
785
|
+
* **password**: ExoScale password.
|
|
786
|
+
* **deployment**: ExoScale Deployment. Follows the format "APP_NAME/DEP_NAME".
|
|
787
|
+
|
|
788
|
+
#### Examples:
|
|
789
|
+
|
|
790
|
+
dpl --provider=exoscale --email=<email> --password<password> --deployment=`APP_NAME/DEP_NAME`
|
|
791
|
+
|
|
779
792
|
### Scalingo:
|
|
780
793
|
|
|
781
794
|
#### Options:
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
require 'json'
|
|
2
|
+
require 'net/http'
|
|
3
|
+
require 'net/https'
|
|
4
|
+
|
|
5
|
+
module DPL
|
|
6
|
+
class Provider
|
|
7
|
+
class ExoScale < Provider
|
|
8
|
+
attr_accessor :app_name
|
|
9
|
+
attr_accessor :dep_name
|
|
10
|
+
|
|
11
|
+
def initialize(context, options)
|
|
12
|
+
super
|
|
13
|
+
option(:email) && option(:password) && option(:deployment)
|
|
14
|
+
@app_name, @dep_name = options[:deployment].split('/')
|
|
15
|
+
|
|
16
|
+
@http = Net::HTTP.new('api.app.exo.io', 443)
|
|
17
|
+
@http.use_ssl = true
|
|
18
|
+
|
|
19
|
+
@tokenHttp = Net::HTTP.new('portal.exoscale.ch', 443)
|
|
20
|
+
@tokenHttp.use_ssl = true
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def check_auth
|
|
24
|
+
headers_with_token
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def check_app
|
|
28
|
+
response = api_call('GET', "/app/#{ app_name }/deployment/#{ dep_name }")
|
|
29
|
+
error('application check failed') if response.code != '200'
|
|
30
|
+
@repository = JSON.parse(response.body)["branch"]
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def setup_key(file)
|
|
34
|
+
data = { 'key' => File.read(file).chomp }
|
|
35
|
+
response = api_call('POST', "/user/#{ user['username'] }/key", JSON.dump(data))
|
|
36
|
+
error('adding key failed') if response.code != '200'
|
|
37
|
+
key = JSON.parse response.body
|
|
38
|
+
@ssh_key_id = key['key_id']
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def remove_key
|
|
42
|
+
response = api_call('DELETE', "/user/#{ user['username']}/key/#{ @ssh_key_id }")
|
|
43
|
+
error('key removal failed') if response.code != '204'
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def push_app
|
|
47
|
+
branch = (dep_name == 'default') ? 'master' : dep_name
|
|
48
|
+
context.shell "git push #{ @repository } HEAD:#{ branch } -f"
|
|
49
|
+
deploy_app
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
private
|
|
53
|
+
|
|
54
|
+
def get_token
|
|
55
|
+
request = Net::HTTP::Post.new '/api/apps/token'
|
|
56
|
+
request.basic_auth options[:email], options[:password]
|
|
57
|
+
response = @tokenHttp.request(request)
|
|
58
|
+
error('authorization failed') if response.code != '200'
|
|
59
|
+
return JSON.parse response.body
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def headers_with_token(options = {})
|
|
63
|
+
@token = get_token if options[:new_token] || @token.nil?
|
|
64
|
+
return {
|
|
65
|
+
'Authorization' => %Q|cc_auth_token="#{ @token['token'] }"|,
|
|
66
|
+
'Content-Type' => 'application/json'
|
|
67
|
+
}
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def get_headers
|
|
71
|
+
headers = headers_with_token
|
|
72
|
+
response = api_call('GET', '/user/', nil, headers)
|
|
73
|
+
return headers if response.code == '200'
|
|
74
|
+
|
|
75
|
+
return headers_with_token :new_token => true
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def api_call(method, path, data = nil, headers = nil)
|
|
79
|
+
return @http.send_request(method, path, data, headers || get_headers)
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def deploy_app
|
|
83
|
+
data = {'version' => -1}
|
|
84
|
+
response = api_call('PUT', "/app/#{ app_name }/deployment/#{ dep_name }", JSON.dump(data))
|
|
85
|
+
error('deployment failed - the deployment is already deploying') if response.code != '200'
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
def user
|
|
89
|
+
if @user.nil?
|
|
90
|
+
response = api_call('GET', '/user/')
|
|
91
|
+
error('can not find the user') if response.code != '200'
|
|
92
|
+
users = JSON.parse response.body
|
|
93
|
+
@user = users[0]
|
|
94
|
+
end
|
|
95
|
+
return @user
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
end
|
|
99
|
+
end
|
|
100
|
+
end
|
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
require 'dpl/provider/exoscale'
|
|
3
|
+
|
|
4
|
+
describe DPL::Provider::ExoScale do
|
|
5
|
+
subject :provider do
|
|
6
|
+
described_class.new(DummyContext.new, :deployment => 'foo_app/default', :email => 'foo@test.com', :password => 'password')
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
its(:app_name) { should == 'foo_app' }
|
|
10
|
+
its(:dep_name) { should == 'default' }
|
|
11
|
+
|
|
12
|
+
its(:needs_key?) { should be true }
|
|
13
|
+
|
|
14
|
+
describe 'constructor' do
|
|
15
|
+
it 'with wrong arguments' do
|
|
16
|
+
expect {
|
|
17
|
+
described_class.new(DummyContext.new, :foo_dep => 'foo_app/default', :email => 'foo@test.com', :password => 'password')
|
|
18
|
+
}.to raise_error(DPL::Error)
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
it '#check_auth should call #headers_with_token' do
|
|
23
|
+
expect(provider).to receive(:headers_with_token)
|
|
24
|
+
provider.check_auth
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
describe '#check_app' do
|
|
28
|
+
it 'on deployment found' do
|
|
29
|
+
expect(provider).to receive(:api_call).and_return double(
|
|
30
|
+
:code => '200',
|
|
31
|
+
:body => '{"branch":"foo_repo.git"}'
|
|
32
|
+
)
|
|
33
|
+
expect(provider.instance_variable_get(:@repository)).to be_nil
|
|
34
|
+
provider.check_app
|
|
35
|
+
expect(provider.instance_variable_get(:@repository)).to eq('foo_repo.git')
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
it 'on deployment not found' do
|
|
39
|
+
expect(provider).to receive(:api_call).and_return double(:code => '410')
|
|
40
|
+
expect { provider.check_app }.to raise_error(DPL::Error)
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
describe '#setup_key' do
|
|
45
|
+
before do
|
|
46
|
+
expect(File).to receive(:read).with('file').and_return('foo_key')
|
|
47
|
+
expect(provider).to receive(:user).and_return({ 'username' => 'foo_user' })
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
it 'on api success' do
|
|
51
|
+
expect(provider).to receive(:api_call).with('POST', '/user/foo_user/key', '{"key":"foo_key"}').and_return double(
|
|
52
|
+
:code => '200',
|
|
53
|
+
:body => '{ "key": "foo_key", "key_id": "foo_key_id"}'
|
|
54
|
+
)
|
|
55
|
+
|
|
56
|
+
expect(provider.instance_variable_get(:@ssh_key_id)).to be_nil
|
|
57
|
+
provider.setup_key 'file'
|
|
58
|
+
expect(provider.instance_variable_get(:@ssh_key_id)).to eq('foo_key_id')
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
it 'on api failure' do
|
|
62
|
+
expect(provider).to receive(:api_call).with('POST', '/user/foo_user/key', '{"key":"foo_key"}').and_return double(:code => '401')
|
|
63
|
+
|
|
64
|
+
expect { provider.setup_key 'file' }.to raise_error(DPL::Error)
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
describe '#remove_key' do
|
|
69
|
+
before do
|
|
70
|
+
provider.instance_variable_set(:@ssh_key_id, 'foo_key_id')
|
|
71
|
+
expect(provider).to receive(:user).and_return({ 'username' => 'foo_user' })
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
it 'on api success' do
|
|
75
|
+
expect(provider).to receive(:api_call).with('DELETE', '/user/foo_user/key/foo_key_id').and_return double(:code => '204')
|
|
76
|
+
provider.remove_key
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
it 'on api failure' do
|
|
80
|
+
expect(provider).to receive(:api_call).with('DELETE', '/user/foo_user/key/foo_key_id').and_return double(:code => '410')
|
|
81
|
+
expect { provider.remove_key }.to raise_error(DPL::Error)
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
it '#push_app shuld deploy the app' do
|
|
86
|
+
provider.instance_variable_set(:@repository, 'foo_repo.git')
|
|
87
|
+
context = double(:shell)
|
|
88
|
+
expect(context).to receive(:shell).with("git push foo_repo.git HEAD:master -f")
|
|
89
|
+
expect(provider).to receive(:context).and_return context
|
|
90
|
+
expect(provider).to receive(:deploy_app)
|
|
91
|
+
|
|
92
|
+
provider.push_app
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
describe 'private method' do
|
|
96
|
+
describe '#get_token' do
|
|
97
|
+
it 'on api success' do
|
|
98
|
+
request = double()
|
|
99
|
+
expect(request).to receive(:basic_auth).with('foo@test.com', 'password')
|
|
100
|
+
expect(Net::HTTP::Post).to receive(:new).with('/api/apps/token').and_return request
|
|
101
|
+
|
|
102
|
+
expect(provider.instance_variable_get(:@tokenHttp)).to receive(:request).and_return double(
|
|
103
|
+
:code => '200',
|
|
104
|
+
:body => '{ "token": "foo_token"}'
|
|
105
|
+
)
|
|
106
|
+
|
|
107
|
+
expect(provider.instance_eval { get_token }).to eq({ 'token' => 'foo_token' })
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
it 'on api failure' do
|
|
111
|
+
expect(provider.instance_variable_get(:@tokenHttp)).to receive(:request).and_return double(:code => '401')
|
|
112
|
+
|
|
113
|
+
expect do
|
|
114
|
+
provider.instance_eval { get_token }
|
|
115
|
+
end.to raise_error(DPL::Error)
|
|
116
|
+
end
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
it '#headers_with_token should return headers' do
|
|
120
|
+
expect(provider).to receive(:get_token).and_return({ 'token' => 'foo_token' })
|
|
121
|
+
expected_return = {
|
|
122
|
+
'Authorization' => 'cc_auth_token="foo_token"',
|
|
123
|
+
'Content-Type' => 'application/json'
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
expect(provider.instance_eval { headers_with_token }).to eq(expected_return)
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
describe '#get_headers' do
|
|
130
|
+
let(:expected_args) { [ 'GET', '/user/', nil, {'foo' => 'headers'} ] }
|
|
131
|
+
|
|
132
|
+
before do
|
|
133
|
+
expect(provider).to receive(:headers_with_token).and_return({ 'foo' => 'headers' })
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
it 'on token valid' do
|
|
137
|
+
expect(provider).to receive(:api_call).with(*expected_args).and_return double(:code => '200')
|
|
138
|
+
expect(provider.instance_eval { get_headers }).to eq({ 'foo' => 'headers' })
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
it 'on token expired' do
|
|
142
|
+
expect(provider).to receive(:api_call).with(*expected_args).and_return double(:code => '401')
|
|
143
|
+
expect(provider).to receive(:headers_with_token).with({ :new_token => true})
|
|
144
|
+
|
|
145
|
+
provider.instance_eval { get_headers }
|
|
146
|
+
end
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
it '#api_call should send request' do
|
|
150
|
+
expected_args = [ "foo_method", "foo_path", "\"foo\":\"data\"", {"foo"=>"headers"} ]
|
|
151
|
+
expect(provider.instance_variable_get(:@http)).to receive(:send_request).with(*expected_args)
|
|
152
|
+
|
|
153
|
+
provider.instance_eval do
|
|
154
|
+
api_call('foo_method', 'foo_path', '"foo":"data"', { 'foo' => 'headers'})
|
|
155
|
+
end
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
describe '#deploy_app' do
|
|
159
|
+
it 'on api success' do
|
|
160
|
+
expect(provider).to receive(:api_call).with('PUT', '/app/foo_app/deployment/default', '{"version":-1}').and_return double(:code => '200')
|
|
161
|
+
provider.instance_eval { deploy_app }
|
|
162
|
+
end
|
|
163
|
+
|
|
164
|
+
it 'on api failure' do
|
|
165
|
+
expect(provider).to receive(:api_call).with('PUT', '/app/foo_app/deployment/default', '{"version":-1}').and_return double(:code => '410')
|
|
166
|
+
expect do
|
|
167
|
+
provider.instance_eval { deploy_app }
|
|
168
|
+
end.to raise_error(DPL::Error)
|
|
169
|
+
end
|
|
170
|
+
end
|
|
171
|
+
|
|
172
|
+
describe '#user' do
|
|
173
|
+
it 'on api success' do
|
|
174
|
+
expect(provider).to receive(:api_call).with('GET', '/user/').and_return double(
|
|
175
|
+
:code => '200',
|
|
176
|
+
:body => '["foo_user"]'
|
|
177
|
+
)
|
|
178
|
+
|
|
179
|
+
expect(provider.instance_eval { user }).to eq('foo_user')
|
|
180
|
+
end
|
|
181
|
+
|
|
182
|
+
it 'on api failure' do
|
|
183
|
+
expect(provider).to receive(:api_call).with('GET', '/user/').and_return double(:code => '410')
|
|
184
|
+
|
|
185
|
+
expect do
|
|
186
|
+
provider.instance_eval { user }
|
|
187
|
+
end.to raise_error(DPL::Error)
|
|
188
|
+
end
|
|
189
|
+
end
|
|
190
|
+
end
|
|
191
|
+
end
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: dpl
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.8.18.travis.
|
|
4
|
+
version: 1.8.18.travis.1563.3
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Konstantin Haase
|
|
@@ -138,6 +138,7 @@ files:
|
|
|
138
138
|
- lib/dpl/provider/divshot.rb
|
|
139
139
|
- lib/dpl/provider/elastic_beanstalk.rb
|
|
140
140
|
- lib/dpl/provider/engine_yard.rb
|
|
141
|
+
- lib/dpl/provider/exoscale.rb
|
|
141
142
|
- lib/dpl/provider/firebase.rb
|
|
142
143
|
- lib/dpl/provider/gae.rb
|
|
143
144
|
- lib/dpl/provider/gcs.rb
|
|
@@ -186,6 +187,7 @@ files:
|
|
|
186
187
|
- spec/provider/deis_spec.rb
|
|
187
188
|
- spec/provider/divshot_spec.rb
|
|
188
189
|
- spec/provider/elastic_beanstalk_spec.rb
|
|
190
|
+
- spec/provider/exoscale_spec.rb
|
|
189
191
|
- spec/provider/firebase_spec.rb
|
|
190
192
|
- spec/provider/gae_spec.rb
|
|
191
193
|
- spec/provider/gcs_spec.rb
|