marathon-api 0.9.0 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.simplecov +2 -2
- data/.travis.yml +1 -1
- data/README.md +40 -14
- data/bin/marathon +242 -0
- data/fixtures/marathon_docker_sample_2.json +2 -1
- data/fixtures/vcr/Marathon_App/_restart/restarts_an_app.yml +32 -0
- data/fixtures/vcr/Marathon_Group/_changes/changes_the_group.yml +61 -0
- data/fixtures/vcr/Marathon_Group/_delete/deletes_the_group.yml +32 -0
- data/fixtures/vcr/Marathon_Group/_delete/fails_deleting_not_existing_app.yml +32 -0
- data/fixtures/vcr/Marathon_Group/_get/fails_getting_not_existing_app.yml +32 -0
- data/fixtures/vcr/Marathon_Group/_get/gets_the_group.yml +32 -0
- data/fixtures/vcr/Marathon_Group/_list/lists_apps.yml +33 -0
- data/fixtures/vcr/Marathon_Group/_start/fails_getting_not_existing_group.yml +32 -0
- data/fixtures/vcr/Marathon_Group/_start/starts_the_group.yml +35 -0
- data/lib/marathon.rb +33 -5
- data/lib/marathon/app.rb +72 -22
- data/lib/marathon/base.rb +32 -0
- data/lib/marathon/connection.rb +32 -22
- data/lib/marathon/constraint.rb +39 -0
- data/lib/marathon/container.rb +41 -0
- data/lib/marathon/container_docker.rb +33 -0
- data/lib/marathon/container_docker_port_mapping.rb +32 -0
- data/lib/marathon/container_volume.rb +31 -0
- data/lib/marathon/deployment.rb +5 -15
- data/lib/marathon/deployment_info.rb +20 -0
- data/lib/marathon/error.rb +8 -2
- data/lib/marathon/group.rb +166 -0
- data/lib/marathon/health_check.rb +35 -0
- data/lib/marathon/queue.rb +4 -13
- data/lib/marathon/task.rb +15 -12
- data/lib/marathon/util.rb +47 -3
- data/lib/marathon/version.rb +1 -1
- data/marathon-api.gemspec +4 -3
- data/spec/marathon/app_spec.rb +108 -50
- data/spec/marathon/base_spec.rb +53 -0
- data/spec/marathon/connection_spec.rb +1 -1
- data/spec/marathon/constraint_spec.rb +27 -0
- data/spec/marathon/container_docker_port_mapping_spec.rb +55 -0
- data/spec/marathon/container_docker_spec.rb +42 -0
- data/spec/marathon/container_spec.rb +40 -0
- data/spec/marathon/container_volume_spec.rb +50 -0
- data/spec/marathon/deployment_info_spec.rb +43 -0
- data/spec/marathon/deployment_spec.rb +15 -16
- data/spec/marathon/error_spec.rb +17 -0
- data/spec/marathon/group_spec.rb +172 -0
- data/spec/marathon/health_check_spec.rb +50 -0
- data/spec/marathon/marathon_spec.rb +31 -0
- data/spec/marathon/queue_spec.rb +2 -2
- data/spec/marathon/task_spec.rb +24 -11
- data/spec/marathon/util_spec.rb +21 -1
- metadata +58 -6
@@ -0,0 +1,53 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Marathon::Base do
|
4
|
+
|
5
|
+
describe '#init' do
|
6
|
+
subject { described_class }
|
7
|
+
|
8
|
+
it 'fails with strange input' do
|
9
|
+
expect { subject.new('foo') }.to raise_error(Marathon::Error::ArgumentError)
|
10
|
+
expect { subject.new({}, 'foo') }.to raise_error(Marathon::Error::ArgumentError)
|
11
|
+
expect { subject.new(nil) }.to raise_error(Marathon::Error::ArgumentError)
|
12
|
+
expect { subject.new({}, nil) }.to raise_error(Marathon::Error::ArgumentError)
|
13
|
+
expect { subject.new([], ['foo']) }.to raise_error(Marathon::Error::ArgumentError)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
describe '#to_json' do
|
18
|
+
subject { described_class.new({
|
19
|
+
'app' => { 'id' => '/app/foo' },
|
20
|
+
:foo => 'blubb',
|
21
|
+
:bar => 1
|
22
|
+
}) }
|
23
|
+
|
24
|
+
let(:expected_string) do
|
25
|
+
'{"app":{"id":"/app/foo"},"foo":"blubb","bar":1}'
|
26
|
+
end
|
27
|
+
|
28
|
+
its(:to_json) { should == expected_string }
|
29
|
+
end
|
30
|
+
|
31
|
+
describe '#attr_readers' do
|
32
|
+
subject { described_class.new({
|
33
|
+
'foo' => 'blubb',
|
34
|
+
:bar => 1
|
35
|
+
}, [:foo, 'bar']) }
|
36
|
+
|
37
|
+
its(:info) { should == {:foo => 'blubb', :bar => 1} }
|
38
|
+
its(:foo) { should == 'blubb' }
|
39
|
+
its(:bar) { should == 1 }
|
40
|
+
end
|
41
|
+
|
42
|
+
describe '#attr_readers, from string array' do
|
43
|
+
subject { described_class.new({
|
44
|
+
'foo' => 'blubb',
|
45
|
+
:bar => 1
|
46
|
+
}, %w[foo bar]) }
|
47
|
+
|
48
|
+
its(:info) { should == {:foo => 'blubb', :bar => 1} }
|
49
|
+
its(:foo) { should == 'blubb' }
|
50
|
+
its(:bar) { should == 1 }
|
51
|
+
end
|
52
|
+
|
53
|
+
end
|
@@ -6,7 +6,7 @@ describe Marathon::Connection do
|
|
6
6
|
subject { described_class.new('http://foo:8080') }
|
7
7
|
|
8
8
|
let(:expected_string) do
|
9
|
-
"Marathon::Connection { :url => http://foo:8080 }"
|
9
|
+
"Marathon::Connection { :url => http://foo:8080 :options => {} }"
|
10
10
|
end
|
11
11
|
|
12
12
|
its(:to_s) { should == expected_string }
|
@@ -0,0 +1,27 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Marathon::Constraint do
|
4
|
+
|
5
|
+
describe '#to_s w/o parameter' do
|
6
|
+
subject { described_class.new(['hostname', 'UNIQUE']) }
|
7
|
+
|
8
|
+
let(:expected_string) do
|
9
|
+
'Marathon::Constraint { :attribute => hostname :operator => UNIQUE }'
|
10
|
+
end
|
11
|
+
|
12
|
+
its(:to_s) { should == expected_string }
|
13
|
+
its(:to_pretty_s) { should == 'hostname:UNIQUE' }
|
14
|
+
end
|
15
|
+
|
16
|
+
describe '#to_s with parameter' do
|
17
|
+
subject { described_class.new(['hostname', 'LIKE', 'foo-host']) }
|
18
|
+
|
19
|
+
let(:expected_string) do
|
20
|
+
'Marathon::Constraint { :attribute => hostname :operator => LIKE :parameter => foo-host }'
|
21
|
+
end
|
22
|
+
|
23
|
+
its(:to_s) { should == expected_string }
|
24
|
+
its(:to_pretty_s) { should == 'hostname:LIKE:foo-host' }
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
CONTAINER_DOCKER_PORT_MAPPING_EXAMPLE = {
|
4
|
+
:protocol => 'tcp',
|
5
|
+
:hostPort => 0,
|
6
|
+
:containerPort => 8080
|
7
|
+
}
|
8
|
+
|
9
|
+
describe Marathon::ContainerDockerPortMapping do
|
10
|
+
|
11
|
+
describe '#init' do
|
12
|
+
subject { described_class }
|
13
|
+
|
14
|
+
it 'should fail with invalid protocol' do
|
15
|
+
expect { subject.new(:protocol => 'foo', :containerPort => 8080) }
|
16
|
+
.to raise_error(Marathon::Error::ArgumentError, /protocol must be one of /)
|
17
|
+
end
|
18
|
+
|
19
|
+
it 'should fail with invalid containerPort' do
|
20
|
+
expect { subject.new(:containerPort => 'foo') }
|
21
|
+
.to raise_error(Marathon::Error::ArgumentError, /containerPort must be/)
|
22
|
+
expect { subject.new(:containerPort => 0) }
|
23
|
+
.to raise_error(Marathon::Error::ArgumentError, /containerPort must be/)
|
24
|
+
expect { subject.new(:containerPort => -1) }
|
25
|
+
.to raise_error(Marathon::Error::ArgumentError, /containerPort must be/)
|
26
|
+
end
|
27
|
+
|
28
|
+
it 'should fail with invalid hostPort' do
|
29
|
+
expect { subject.new(:hostPort => 'foo', :containerPort => 8080) }
|
30
|
+
.to raise_error(Marathon::Error::ArgumentError, /hostPort must be/)
|
31
|
+
expect { subject.new(:hostPort => -1, :containerPort => 8080) }
|
32
|
+
.to raise_error(Marathon::Error::ArgumentError, /hostPort must be/)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
describe '#attributes' do
|
37
|
+
subject { described_class.new(CONTAINER_DOCKER_PORT_MAPPING_EXAMPLE) }
|
38
|
+
|
39
|
+
its(:protocol) { should == 'tcp' }
|
40
|
+
its(:hostPort) { should == 0 }
|
41
|
+
its(:containerPort) { should == 8080 }
|
42
|
+
end
|
43
|
+
|
44
|
+
describe '#to_s' do
|
45
|
+
subject { described_class.new(CONTAINER_DOCKER_PORT_MAPPING_EXAMPLE) }
|
46
|
+
|
47
|
+
let(:expected_string) do
|
48
|
+
'Marathon::ContainerDockerPortMapping { :protocol => tcp :containerPort => 8080 :hostPort => 0 }'
|
49
|
+
end
|
50
|
+
|
51
|
+
its(:to_s) { should == expected_string }
|
52
|
+
its(:to_pretty_s) { should == 'tcp/8080:0' }
|
53
|
+
end
|
54
|
+
|
55
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
CONTAINER_DOCKER_EXAMPLE = {
|
4
|
+
:network => 'HOST',
|
5
|
+
:image => 'felixb/yocto-httpd'
|
6
|
+
}
|
7
|
+
|
8
|
+
describe Marathon::ContainerDocker do
|
9
|
+
|
10
|
+
describe '#init' do
|
11
|
+
subject { described_class }
|
12
|
+
|
13
|
+
it 'should fail with invalid network' do
|
14
|
+
expect { subject.new(:network => 'foo', :image => 'foo') }
|
15
|
+
.to raise_error(Marathon::Error::ArgumentError, /network must be one of /)
|
16
|
+
end
|
17
|
+
|
18
|
+
it 'should fail w/o image' do
|
19
|
+
expect { subject.new({}) }
|
20
|
+
.to raise_error(Marathon::Error::ArgumentError, /image must not be/)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
describe '#attributes' do
|
25
|
+
subject { described_class.new(CONTAINER_DOCKER_EXAMPLE) }
|
26
|
+
|
27
|
+
its(:network) { should == 'HOST' }
|
28
|
+
its(:image) { should == 'felixb/yocto-httpd' }
|
29
|
+
end
|
30
|
+
|
31
|
+
describe '#to_s' do
|
32
|
+
subject { described_class.new(CONTAINER_DOCKER_EXAMPLE) }
|
33
|
+
|
34
|
+
let(:expected_string) do
|
35
|
+
'Marathon::ContainerDocker { :image => felixb/yocto-httpd }'
|
36
|
+
end
|
37
|
+
|
38
|
+
its(:to_s) { should == expected_string }
|
39
|
+
its(:to_pretty_s) { should == 'felixb/yocto-httpd' }
|
40
|
+
end
|
41
|
+
|
42
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
CONTAINER_EXAMPLE = {
|
4
|
+
:type => 'DOCKER',
|
5
|
+
:docker => {
|
6
|
+
:image => 'felixb/yocto-httpd',
|
7
|
+
:portMappings => [{:containerPort => 8080}]
|
8
|
+
},
|
9
|
+
:volumes => [{
|
10
|
+
:containerPath => '/data',
|
11
|
+
:hostPath => '/var/opt/foo'
|
12
|
+
}]
|
13
|
+
}
|
14
|
+
|
15
|
+
describe Marathon::Container do
|
16
|
+
|
17
|
+
describe '#attributes' do
|
18
|
+
subject { described_class.new(CONTAINER_EXAMPLE) }
|
19
|
+
|
20
|
+
its(:type) { should == 'DOCKER' }
|
21
|
+
its(:docker) { should be_instance_of(Marathon::ContainerDocker) }
|
22
|
+
its("docker.portMappings") { should be_instance_of(Array) }
|
23
|
+
its("docker.portMappings.first") { should be_instance_of(Marathon::ContainerDockerPortMapping) }
|
24
|
+
its("docker.portMappings.first.containerPort") { should == 8080 }
|
25
|
+
its(:volumes) { should be_instance_of(Array) }
|
26
|
+
its("volumes.first") { should be_instance_of(Marathon::ContainerVolume) }
|
27
|
+
its("volumes.first.containerPath") { should == '/data' }
|
28
|
+
end
|
29
|
+
|
30
|
+
describe '#to_s' do
|
31
|
+
subject { described_class.new(CONTAINER_EXAMPLE) }
|
32
|
+
|
33
|
+
let(:expected_string) do
|
34
|
+
'Marathon::Container { :type => DOCKER :docker => felixb/yocto-httpd :volumes => /data:/var/opt/foo:RW }'
|
35
|
+
end
|
36
|
+
|
37
|
+
its(:to_s) { should == expected_string }
|
38
|
+
end
|
39
|
+
|
40
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
CONTAINER_VOLUME_EXAMPLE = {
|
4
|
+
:containerPath => '/data',
|
5
|
+
:hostPath => '/var/opt/foo',
|
6
|
+
:mode => 'RO'
|
7
|
+
}
|
8
|
+
|
9
|
+
describe Marathon::ContainerVolume do
|
10
|
+
|
11
|
+
describe '#init' do
|
12
|
+
subject { described_class }
|
13
|
+
|
14
|
+
it 'should fail with invalid mode' do
|
15
|
+
expect { subject.new(:containerPath => '/', :hostPath => '/', :mode => 'foo') }
|
16
|
+
.to raise_error(Marathon::Error::ArgumentError, /mode must be one of /)
|
17
|
+
end
|
18
|
+
|
19
|
+
it 'should fail with invalid path' do
|
20
|
+
expect { subject.new(:hostPath => '/') }
|
21
|
+
.to raise_error(Marathon::Error::ArgumentError, /containerPath .* not be nil/)
|
22
|
+
expect { subject.new(:containerPath => 'foo', :hostPath => '/') }
|
23
|
+
.to raise_error(Marathon::Error::ArgumentError, /containerPath .* absolute path/)
|
24
|
+
expect { subject.new(:containerPath => '/') }
|
25
|
+
.to raise_error(Marathon::Error::ArgumentError, /hostPath .* not be nil/)
|
26
|
+
expect { subject.new(:containerPath => '/', :hostPath => 'foo') }
|
27
|
+
.to raise_error(Marathon::Error::ArgumentError, /hostPath .* absolute path/)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
describe '#attributes' do
|
32
|
+
subject { described_class.new(CONTAINER_VOLUME_EXAMPLE) }
|
33
|
+
|
34
|
+
its(:containerPath) { should == '/data' }
|
35
|
+
its(:hostPath) { should == '/var/opt/foo' }
|
36
|
+
its(:mode) { should == 'RO' }
|
37
|
+
end
|
38
|
+
|
39
|
+
describe '#to_s' do
|
40
|
+
subject { described_class.new(CONTAINER_VOLUME_EXAMPLE) }
|
41
|
+
|
42
|
+
let(:expected_string) do
|
43
|
+
'Marathon::ContainerVolume { :containerPath => /data :hostPath => /var/opt/foo :mode => RO }'
|
44
|
+
end
|
45
|
+
|
46
|
+
its(:to_s) { should == expected_string }
|
47
|
+
its(:to_pretty_s) { should == '/data:/var/opt/foo:RO' }
|
48
|
+
end
|
49
|
+
|
50
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
DEPLOYMENT_INFO_EXAMPLE = {
|
4
|
+
'deploymentId' => 'deployment-123',
|
5
|
+
'version' => 'version-456'
|
6
|
+
}
|
7
|
+
|
8
|
+
describe Marathon::DeploymentInfo do
|
9
|
+
|
10
|
+
describe '#attributes' do
|
11
|
+
subject { described_class.new(DEPLOYMENT_INFO_EXAMPLE) }
|
12
|
+
|
13
|
+
its(:deploymentId) { should == 'deployment-123' }
|
14
|
+
its(:version) { should == 'version-456' }
|
15
|
+
end
|
16
|
+
|
17
|
+
describe '#to_s' do
|
18
|
+
subject { described_class.new(DEPLOYMENT_INFO_EXAMPLE) }
|
19
|
+
|
20
|
+
let(:expected_string) do
|
21
|
+
'Marathon::DeploymentInfo { :version => version-456 :deploymentId => deployment-123 }'
|
22
|
+
end
|
23
|
+
|
24
|
+
its(:to_s) { should == expected_string }
|
25
|
+
end
|
26
|
+
|
27
|
+
describe '#to_s w/o deploymentId' do
|
28
|
+
subject { described_class.new(:version => 'foo-version') }
|
29
|
+
|
30
|
+
let(:expected_string) do
|
31
|
+
'Marathon::DeploymentInfo { :version => foo-version }'
|
32
|
+
end
|
33
|
+
|
34
|
+
its(:to_s) { should == expected_string }
|
35
|
+
end
|
36
|
+
|
37
|
+
describe '#to_json' do
|
38
|
+
subject { described_class.new(DEPLOYMENT_INFO_EXAMPLE) }
|
39
|
+
|
40
|
+
its(:to_json) { should == DEPLOYMENT_INFO_EXAMPLE.to_json }
|
41
|
+
end
|
42
|
+
|
43
|
+
end
|
@@ -1,6 +1,6 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
|
-
|
3
|
+
DEPLOYMENT_EXAMPLE = {
|
4
4
|
"affectedApps" => ["/test"],
|
5
5
|
"id" => "867ed450-f6a8-4d33-9b0e-e11c5513990b",
|
6
6
|
"steps" => [
|
@@ -25,7 +25,7 @@ EXAMPLE = {
|
|
25
25
|
describe Marathon::Deployment do
|
26
26
|
|
27
27
|
describe '#to_s' do
|
28
|
-
subject { described_class.new(
|
28
|
+
subject { described_class.new(DEPLOYMENT_EXAMPLE) }
|
29
29
|
|
30
30
|
let(:expected_string) do
|
31
31
|
'Marathon::Deployment { ' \
|
@@ -36,31 +36,31 @@ describe Marathon::Deployment do
|
|
36
36
|
end
|
37
37
|
|
38
38
|
describe '#to_json' do
|
39
|
-
subject { described_class.new(
|
39
|
+
subject { described_class.new(DEPLOYMENT_EXAMPLE) }
|
40
40
|
|
41
|
-
its(:to_json) { should ==
|
41
|
+
its(:to_json) { should == DEPLOYMENT_EXAMPLE.to_json }
|
42
42
|
end
|
43
43
|
|
44
44
|
describe 'attributes' do
|
45
|
-
subject { described_class.new(
|
45
|
+
subject { described_class.new(DEPLOYMENT_EXAMPLE) }
|
46
46
|
|
47
|
-
its(:id) { should ==
|
48
|
-
its(:affectedApps) { should ==
|
49
|
-
its(:version) { should ==
|
50
|
-
its(:currentStep) { should ==
|
51
|
-
its(:totalSteps) { should ==
|
47
|
+
its(:id) { should == DEPLOYMENT_EXAMPLE['id'] }
|
48
|
+
its(:affectedApps) { should == DEPLOYMENT_EXAMPLE['affectedApps'] }
|
49
|
+
its(:version) { should == DEPLOYMENT_EXAMPLE['version'] }
|
50
|
+
its(:currentStep) { should == DEPLOYMENT_EXAMPLE['currentStep'] }
|
51
|
+
its(:totalSteps) { should == DEPLOYMENT_EXAMPLE['totalSteps'] }
|
52
52
|
end
|
53
53
|
|
54
54
|
describe '#delete' do
|
55
|
-
subject { described_class.new(
|
55
|
+
subject { described_class.new(DEPLOYMENT_EXAMPLE) }
|
56
56
|
|
57
57
|
it 'deletes the deployment' do
|
58
|
-
expect(described_class).to receive(:delete).with(
|
58
|
+
expect(described_class).to receive(:delete).with(DEPLOYMENT_EXAMPLE['id'], false)
|
59
59
|
subject.delete
|
60
60
|
end
|
61
61
|
|
62
62
|
it 'force deletes the deployment' do
|
63
|
-
expect(described_class).to receive(:delete).with(
|
63
|
+
expect(described_class).to receive(:delete).with(DEPLOYMENT_EXAMPLE['id'], true)
|
64
64
|
subject.delete(true)
|
65
65
|
end
|
66
66
|
end
|
@@ -86,9 +86,8 @@ describe Marathon::Deployment do
|
|
86
86
|
|
87
87
|
it 'deletes deployments', :vcr do
|
88
88
|
# start a deployment
|
89
|
-
|
90
|
-
|
91
|
-
subject.delete(id)
|
89
|
+
info = Marathon::App.change('/test', {'instances' => 1})
|
90
|
+
expect(subject.delete(info.deploymentId)).to be_instance_of(Marathon::DeploymentInfo)
|
92
91
|
end
|
93
92
|
end
|
94
93
|
|
data/spec/marathon/error_spec.rb
CHANGED
@@ -10,6 +10,11 @@ describe Marathon::Error do
|
|
10
10
|
.to be(Marathon::Error::ClientError)
|
11
11
|
end
|
12
12
|
|
13
|
+
it 'returns ClientError on 422' do
|
14
|
+
expect(subject.error_class(Net::HTTPResponse.new(1.1, 422, 'Client Error')))
|
15
|
+
.to be(Marathon::Error::ClientError)
|
16
|
+
end
|
17
|
+
|
13
18
|
it 'returns NotFoundError on 404' do
|
14
19
|
expect(subject.error_class(Net::HTTPResponse.new(1.1, 404, 'Not Found')))
|
15
20
|
.to be(Marathon::Error::NotFoundError)
|
@@ -30,6 +35,18 @@ describe Marathon::Error do
|
|
30
35
|
expect(subject.error_message(r)).to eq('fooo')
|
31
36
|
end
|
32
37
|
|
38
|
+
it 'returns "errors" from respose json' do
|
39
|
+
r = { 'errors' => 'fooo' }
|
40
|
+
expect(r).to receive(:parsed_response) { r }
|
41
|
+
expect(subject.error_message(r)).to eq('fooo')
|
42
|
+
end
|
43
|
+
|
44
|
+
it 'returns full hash from respose json, if keys are missing' do
|
45
|
+
r = { 'bars' => 'fooo' }
|
46
|
+
expect(r).to receive(:parsed_response) { r }
|
47
|
+
expect(subject.error_message(r)).to eq(r)
|
48
|
+
end
|
49
|
+
|
33
50
|
it 'returns full body if not a hash with "message"' do
|
34
51
|
r = 'fooo'
|
35
52
|
expect(r).to receive(:parsed_response) { r }
|
@@ -0,0 +1,172 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
EXAMPLE_GROUP = {
|
4
|
+
"id" => "/test-group",
|
5
|
+
"apps" => [
|
6
|
+
{
|
7
|
+
"backoffFactor" => 1.15,
|
8
|
+
"backoffSeconds" => 1,
|
9
|
+
"maxLaunchDelaySeconds" => 3600,
|
10
|
+
"cmd" => "sleep 30",
|
11
|
+
"constraints" => [],
|
12
|
+
"cpus" => 1.0,
|
13
|
+
"dependencies" => [],
|
14
|
+
"disk" => 0.0,
|
15
|
+
"env" => {},
|
16
|
+
"executor" => "",
|
17
|
+
"id" => "app",
|
18
|
+
"instances" => 1,
|
19
|
+
"mem" => 128.0,
|
20
|
+
"ports" => [10000],
|
21
|
+
"requirePorts" => false,
|
22
|
+
"storeUrls" => [],
|
23
|
+
"upgradeStrategy" => {
|
24
|
+
"minimumHealthCapacity" => 1.0
|
25
|
+
},
|
26
|
+
"tasks" => []
|
27
|
+
}
|
28
|
+
],
|
29
|
+
"dependencies" => [],
|
30
|
+
"groups" => []
|
31
|
+
}
|
32
|
+
|
33
|
+
describe Marathon::Group do
|
34
|
+
|
35
|
+
describe '#to_s' do
|
36
|
+
subject { described_class.new(EXAMPLE_GROUP) }
|
37
|
+
|
38
|
+
let(:expected_string) do
|
39
|
+
"Marathon::Group { :id => /test-group }"
|
40
|
+
end
|
41
|
+
|
42
|
+
let(:expected_pretty_string) do
|
43
|
+
"Group ID: /test-group\n" + \
|
44
|
+
" App ID: app\n" + \
|
45
|
+
" Instances: 0/1\n" + \
|
46
|
+
" Command: sleep 30\n" + \
|
47
|
+
" CPUs: 1.0\n" + \
|
48
|
+
" Memory: 128.0 MB\n" + \
|
49
|
+
" Version:\n" + \
|
50
|
+
"Version:"
|
51
|
+
|
52
|
+
end
|
53
|
+
|
54
|
+
its(:to_s) { should == expected_string }
|
55
|
+
its(:to_pretty_s) { should == expected_pretty_string }
|
56
|
+
end
|
57
|
+
|
58
|
+
describe '#start!' do
|
59
|
+
subject { described_class.new({ 'id' => '/group/foo' }) }
|
60
|
+
|
61
|
+
it 'starts the group' do
|
62
|
+
expect(described_class).to receive(:start)
|
63
|
+
.with({:apps=>[], :dependencies=>[], :groups=>[], :id=>'/group/foo'}) do
|
64
|
+
Marathon::DeploymentInfo.new({ 'version' => 'new-version' })
|
65
|
+
end
|
66
|
+
expect(subject.start!.version).to eq('new-version')
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
describe '#refresh' do
|
71
|
+
subject { described_class.new({ 'id' => '/app/foo' }) }
|
72
|
+
|
73
|
+
it 'refreshs the group' do
|
74
|
+
expect(described_class).to receive(:get).with('/app/foo') do
|
75
|
+
described_class.new({ 'id' => '/app/foo', 'refreshed' => true })
|
76
|
+
end
|
77
|
+
subject.refresh
|
78
|
+
expect(subject.info[:refreshed]).to be(true)
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
describe '#change!' do
|
83
|
+
subject { described_class.new({ 'id' => '/app/foo' }) }
|
84
|
+
|
85
|
+
it 'changes the group' do
|
86
|
+
expect(described_class).to receive(:change).with('/app/foo', {'instances' => 9000 }, false)
|
87
|
+
subject.change!('instances' => 9000)
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
describe '#roll_back!' do
|
92
|
+
subject { described_class.new({ 'id' => '/app/foo', 'instances' => 10 }) }
|
93
|
+
|
94
|
+
it 'changes the group' do
|
95
|
+
expect(subject).to receive(:change!).with({'version' => 'old_version' }, false)
|
96
|
+
subject.roll_back!('old_version')
|
97
|
+
end
|
98
|
+
|
99
|
+
it 'changes the group with force' do
|
100
|
+
expect(subject).to receive(:change!).with({'version' => 'old_version' }, true)
|
101
|
+
subject.roll_back!('old_version', true)
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
describe '.start' do
|
106
|
+
subject { described_class }
|
107
|
+
|
108
|
+
it 'starts the group', :vcr do
|
109
|
+
expect(subject.start(EXAMPLE_GROUP)).to be_instance_of(Marathon::DeploymentInfo)
|
110
|
+
end
|
111
|
+
|
112
|
+
it 'fails getting not existing group', :vcr do
|
113
|
+
expect {
|
114
|
+
subject.get('fooo group')
|
115
|
+
}.to raise_error(Marathon::Error::NotFoundError)
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
describe '.list' do
|
120
|
+
subject { described_class }
|
121
|
+
|
122
|
+
it 'lists apps', :vcr do
|
123
|
+
groups = subject.list
|
124
|
+
expect(groups).to be_instance_of(described_class)
|
125
|
+
expect(groups.groups.size).not_to eq(0)
|
126
|
+
expect(groups.groups.first).to be_instance_of(described_class)
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
describe '.get' do
|
131
|
+
subject { described_class }
|
132
|
+
|
133
|
+
it 'gets the group', :vcr do
|
134
|
+
group = subject.get('/test-group')
|
135
|
+
expect(group).to be_instance_of(described_class)
|
136
|
+
expect(group.id).to eq('/test-group')
|
137
|
+
expect(group.apps.first).to be_instance_of(Marathon::App)
|
138
|
+
end
|
139
|
+
|
140
|
+
it 'fails getting not existing app', :vcr do
|
141
|
+
expect {
|
142
|
+
subject.get('fooo group')
|
143
|
+
}.to raise_error(Marathon::Error::NotFoundError)
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
describe '.delete' do
|
148
|
+
subject { described_class }
|
149
|
+
|
150
|
+
it 'deletes the group', :vcr do
|
151
|
+
subject.delete('/test-group', true)
|
152
|
+
end
|
153
|
+
|
154
|
+
it 'fails deleting not existing app', :vcr do
|
155
|
+
expect {
|
156
|
+
subject.delete('fooo group')
|
157
|
+
}.to raise_error(Marathon::Error::NotFoundError)
|
158
|
+
end
|
159
|
+
end
|
160
|
+
|
161
|
+
describe '.changes' do
|
162
|
+
subject { described_class }
|
163
|
+
|
164
|
+
it 'changes the group', :vcr do
|
165
|
+
expect(subject.change('/ubuntu2', { 'instances' => 2 }))
|
166
|
+
.to be_instance_of(Marathon::DeploymentInfo)
|
167
|
+
expect(subject.change('/ubuntu2', { 'instances' => 1 }, true))
|
168
|
+
.to be_instance_of(Marathon::DeploymentInfo)
|
169
|
+
end
|
170
|
+
end
|
171
|
+
|
172
|
+
end
|