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.
Files changed (52) hide show
  1. checksums.yaml +4 -4
  2. data/.simplecov +2 -2
  3. data/.travis.yml +1 -1
  4. data/README.md +40 -14
  5. data/bin/marathon +242 -0
  6. data/fixtures/marathon_docker_sample_2.json +2 -1
  7. data/fixtures/vcr/Marathon_App/_restart/restarts_an_app.yml +32 -0
  8. data/fixtures/vcr/Marathon_Group/_changes/changes_the_group.yml +61 -0
  9. data/fixtures/vcr/Marathon_Group/_delete/deletes_the_group.yml +32 -0
  10. data/fixtures/vcr/Marathon_Group/_delete/fails_deleting_not_existing_app.yml +32 -0
  11. data/fixtures/vcr/Marathon_Group/_get/fails_getting_not_existing_app.yml +32 -0
  12. data/fixtures/vcr/Marathon_Group/_get/gets_the_group.yml +32 -0
  13. data/fixtures/vcr/Marathon_Group/_list/lists_apps.yml +33 -0
  14. data/fixtures/vcr/Marathon_Group/_start/fails_getting_not_existing_group.yml +32 -0
  15. data/fixtures/vcr/Marathon_Group/_start/starts_the_group.yml +35 -0
  16. data/lib/marathon.rb +33 -5
  17. data/lib/marathon/app.rb +72 -22
  18. data/lib/marathon/base.rb +32 -0
  19. data/lib/marathon/connection.rb +32 -22
  20. data/lib/marathon/constraint.rb +39 -0
  21. data/lib/marathon/container.rb +41 -0
  22. data/lib/marathon/container_docker.rb +33 -0
  23. data/lib/marathon/container_docker_port_mapping.rb +32 -0
  24. data/lib/marathon/container_volume.rb +31 -0
  25. data/lib/marathon/deployment.rb +5 -15
  26. data/lib/marathon/deployment_info.rb +20 -0
  27. data/lib/marathon/error.rb +8 -2
  28. data/lib/marathon/group.rb +166 -0
  29. data/lib/marathon/health_check.rb +35 -0
  30. data/lib/marathon/queue.rb +4 -13
  31. data/lib/marathon/task.rb +15 -12
  32. data/lib/marathon/util.rb +47 -3
  33. data/lib/marathon/version.rb +1 -1
  34. data/marathon-api.gemspec +4 -3
  35. data/spec/marathon/app_spec.rb +108 -50
  36. data/spec/marathon/base_spec.rb +53 -0
  37. data/spec/marathon/connection_spec.rb +1 -1
  38. data/spec/marathon/constraint_spec.rb +27 -0
  39. data/spec/marathon/container_docker_port_mapping_spec.rb +55 -0
  40. data/spec/marathon/container_docker_spec.rb +42 -0
  41. data/spec/marathon/container_spec.rb +40 -0
  42. data/spec/marathon/container_volume_spec.rb +50 -0
  43. data/spec/marathon/deployment_info_spec.rb +43 -0
  44. data/spec/marathon/deployment_spec.rb +15 -16
  45. data/spec/marathon/error_spec.rb +17 -0
  46. data/spec/marathon/group_spec.rb +172 -0
  47. data/spec/marathon/health_check_spec.rb +50 -0
  48. data/spec/marathon/marathon_spec.rb +31 -0
  49. data/spec/marathon/queue_spec.rb +2 -2
  50. data/spec/marathon/task_spec.rb +24 -11
  51. data/spec/marathon/util_spec.rb +21 -1
  52. 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
- EXAMPLE = {
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(EXAMPLE) }
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(EXAMPLE) }
39
+ subject { described_class.new(DEPLOYMENT_EXAMPLE) }
40
40
 
41
- its(:to_json) { should == EXAMPLE.to_json }
41
+ its(:to_json) { should == DEPLOYMENT_EXAMPLE.to_json }
42
42
  end
43
43
 
44
44
  describe 'attributes' do
45
- subject { described_class.new(EXAMPLE) }
45
+ subject { described_class.new(DEPLOYMENT_EXAMPLE) }
46
46
 
47
- its(:id) { should == EXAMPLE['id'] }
48
- its(:affectedApps) { should == EXAMPLE['affectedApps'] }
49
- its(:version) { should == EXAMPLE['version'] }
50
- its(:currentStep) { should == EXAMPLE['currentStep'] }
51
- its(:totalSteps) { should == EXAMPLE['totalSteps'] }
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(EXAMPLE) }
55
+ subject { described_class.new(DEPLOYMENT_EXAMPLE) }
56
56
 
57
57
  it 'deletes the deployment' do
58
- expect(described_class).to receive(:delete).with(EXAMPLE['id'], false)
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(EXAMPLE['id'], true)
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
- json = Marathon::App.change('/test', {'instances' => 1})
90
- id = json['deploymentId']
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
 
@@ -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