marathon-api 0.9.0 → 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.
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