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,35 @@
1
+ # This class represents a Marathon HealthCheck.
2
+ # See https://mesosphere.github.io/marathon/docs/health-checks.html for full details.
3
+ class Marathon::HealthCheck < Marathon::Base
4
+
5
+ DEFAULTS = {
6
+ :gracePeriodSeconds => 300,
7
+ :intervalSeconds => 60,
8
+ :maxConsecutiveFailures => 3,
9
+ :path => '/',
10
+ :portIndex => 0,
11
+ :protocol => 'HTTP',
12
+ :timeoutSeconds => 20
13
+ }
14
+
15
+ ACCESSORS = %w[ command gracePeriodSeconds intervalSeconds maxConsecutiveFailures
16
+ path portIndex protocol timeoutSeconds ]
17
+
18
+ # Create a new health check object.
19
+ # ++hash++: Hash returned by API.
20
+ def initialize(hash)
21
+ super(Marathon::Util.merge_keywordized_hash(DEFAULTS, hash), ACCESSORS)
22
+ Marathon::Util.validate_choice(:protocol, protocol, %w[HTTP TCP COMMAND])
23
+ end
24
+
25
+ def to_s
26
+ if protocol == 'COMMAND'
27
+ "Marathon::HealthCheck { :protocol => #{protocol} :command => #{command} }"
28
+ elsif protocol == 'HTTP'
29
+ "Marathon::HealthCheck { :protocol => #{protocol} :portIndex => #{portIndex} :path => #{path} }"
30
+ else
31
+ "Marathon::HealthCheck { :protocol => #{protocol} :portIndex => #{portIndex} }"
32
+ end
33
+ end
34
+
35
+ end
@@ -1,29 +1,20 @@
1
1
  # This class represents a Marathon Queue element.
2
2
  # See https://mesosphere.github.io/marathon/docs/rest-api.html#queue for full list of API's methods.
3
- class Marathon::Queue
3
+ class Marathon::Queue < Marathon::Base
4
4
 
5
5
  attr_reader :app
6
- attr_reader :delay
7
6
 
8
7
  # Create a new queue element object.
9
8
  # ++hash++: Hash returned by API, including 'app' and 'delay'
10
- def initialize(hash = {})
11
- @app = Marathon::App.new(hash['app'], true)
12
- @delay = hash['delay']
9
+ def initialize(hash)
10
+ super(hash, %w[delay])
11
+ @app = Marathon::App.new(info[:app], true)
13
12
  end
14
13
 
15
14
  def to_s
16
15
  "Marathon::Queue { :appId => #{app.id} :delay => #{delay} }"
17
16
  end
18
17
 
19
- # Return queue element as JSON formatted string.
20
- def to_json
21
- {
22
- 'app' => @app.info,
23
- 'delay' => @delay
24
- }.to_json
25
- end
26
-
27
18
  class << self
28
19
 
29
20
  # Show content of the task queue.
@@ -1,18 +1,13 @@
1
1
  # This class represents a Marathon Task.
2
2
  # See https://mesosphere.github.io/marathon/docs/rest-api.html#get-/v2/tasks for full list of API's methods.
3
- class Marathon::Task
3
+ class Marathon::Task < Marathon::Base
4
4
 
5
- attr_reader :info
5
+ ACCESSORS = %w[ id appId host ports servicePorts version stagedAt startedAt ]
6
6
 
7
7
  # Create a new task object.
8
8
  # ++hash++: Hash including all attributes
9
- def initialize(hash = {})
10
- @info = hash
11
- end
12
-
13
- # Shortcuts for reaching attributes
14
- %w[ id appId host ports servicePorts version stagedAt startedAt ].each do |method|
15
- define_method(method) { |*args, &block| info[method] }
9
+ def initialize(hash)
10
+ super(hash, ACCESSORS)
16
11
  end
17
12
 
18
13
  # Kill the task that belongs to an application.
@@ -28,9 +23,17 @@ class Marathon::Task
28
23
  "Marathon::Task { :id => #{self.id} :appId => #{appId} :host => #{host} }"
29
24
  end
30
25
 
31
- # Return task as JSON formatted string.
32
- def to_json
33
- info.to_json
26
+ # Returns a string for listing the task.
27
+ def to_pretty_s
28
+ %Q[
29
+ Task ID: #{id}
30
+ App ID: #{appId}
31
+ Host: #{host}
32
+ Ports: #{(ports || []).join(',')}
33
+ Staged at: #{stagedAt}
34
+ Started at: #{startedAt}
35
+ Version: #{version}
36
+ ].strip
34
37
  end
35
38
 
36
39
  class << self
@@ -8,6 +8,7 @@ class Marathon::Util
8
8
  # ++allowed++: array of allowd values
9
9
  # ++nil_allowed++: allow nil values
10
10
  def validate_choice(name, value, allowed, nil_allowed = true)
11
+ value = value[name] if value.is_a?(Hash)
11
12
  if value.nil?
12
13
  unless nil_allowed
13
14
  raise Marathon::Error::ArgumentError, "#{name} must not be nil"
@@ -15,8 +16,13 @@ class Marathon::Util
15
16
  else
16
17
  # value is not nil
17
18
  unless allowed.include?(value)
18
- msg = nil_allowed ? "#{name} must be one of #{allowed}, or nil" : "#{name} must be one of #{allowed}"
19
- raise Marathon::Error::ArgumentError, msg
19
+ if nil_allowed
20
+ raise Marathon::Error::ArgumentError,
21
+ "#{name} must be one of #{allowed.join(', ')} or nil, but is '#{value}'"
22
+ else
23
+ raise Marathon::Error::ArgumentError,
24
+ "#{name} must be one of #{allowed.join(', ')} or nil, but is '#{value}'"
25
+ end
20
26
  end
21
27
  end
22
28
  end
@@ -31,5 +37,43 @@ class Marathon::Util
31
37
  validate_choice(name, value, allowed, nil_allowed)
32
38
  opts[name] = value if value
33
39
  end
40
+
41
+ # Swap keys of the hash against their symbols.
42
+ # ++hash++: the hash
43
+ def keywordize_hash(hash)
44
+ if hash.is_a?(Hash)
45
+ new_hash = {}
46
+ hash.each do |k,v|
47
+ key = k.to_sym
48
+ if key == :env and v.is_a?(Hash)
49
+ # ignore :env => {} to prevent renameing environment variables
50
+ new_hash[key] = hash[k]
51
+ else
52
+ new_hash[key] = keywordize_hash(hash[k])
53
+ end
54
+ end
55
+ new_hash
56
+ elsif hash.is_a?(Array)
57
+ hash.map { |e| keywordize_hash(e) }
58
+ else
59
+ hash
60
+ end
61
+ end
62
+
63
+ # Merge two hashes but keywordize both.
64
+ def merge_keywordized_hash(h1, h2)
65
+ keywordize_hash(h1).merge(keywordize_hash(h2))
66
+ end
67
+
68
+ # Stringify an item or an array of items.
69
+ def items_to_pretty_s(item)
70
+ if item.nil?
71
+ nil
72
+ elsif item.is_a?(Array)
73
+ item.map {|e| e.to_pretty_s}.join(',')
74
+ else
75
+ item.to_pretty_s
76
+ end
77
+ end
34
78
  end
35
- end
79
+ end
@@ -1,3 +1,3 @@
1
1
  module Marathon
2
- VERSION = '0.9.0'
2
+ VERSION = '1.0.0'
3
3
  end
@@ -5,10 +5,10 @@ Gem::Specification.new do |gem|
5
5
  gem.name = "marathon-api"
6
6
  gem.version = Marathon::VERSION
7
7
  gem.authors = ["Felix Bechstein"]
8
- gem.email = %w{f@ub0r.de}
8
+ gem.email = %w{felix.bechstein@otto.de}
9
9
  gem.description = %q{A simple REST client for the Marathon Remote API}
10
10
  gem.summary = %q{A simple REST client for the Marathon Remote API}
11
- gem.homepage = 'https://github.com/felixb/marathon-api'
11
+ gem.homepage = 'https://github.com/otto-de/marathon-api'
12
12
  gem.license = 'MIT'
13
13
 
14
14
  gem.files = `git ls-files`.split($\)
@@ -17,7 +17,8 @@ Gem::Specification.new do |gem|
17
17
  gem.require_paths = %w{lib}
18
18
 
19
19
  gem.add_dependency 'json'
20
- gem.add_dependency "httparty", ">= 0.11"
20
+ gem.add_dependency 'httparty', '>= 0.11'
21
+ gem.add_dependency 'trollop', '>= 2.0'
21
22
 
22
23
  gem.add_development_dependency 'rake'
23
24
  gem.add_development_dependency 'rspec', '~> 3.0'
@@ -3,20 +3,42 @@ require 'spec_helper'
3
3
  describe Marathon::App do
4
4
 
5
5
  describe '#to_s' do
6
- subject { described_class.new({ 'id' => '/app/foo' }) }
6
+ subject { described_class.new({
7
+ 'id' => '/app/foo',
8
+ 'instances' => 1,
9
+ 'tasks' => [],
10
+ 'env' => {'FOO' => 'BAR', 'blubb' => 'blah'},
11
+ 'constraints' => [['hostname', 'UNIQUE']],
12
+ 'uris' => ['http://example.com/big.tar'],
13
+ 'version' => 'foo-version'
14
+ }) }
7
15
 
8
16
  let(:expected_string) do
9
17
  "Marathon::App { :id => /app/foo }"
10
18
  end
11
19
 
20
+ let(:expected_pretty_string) do
21
+ "App ID: /app/foo\n" + \
22
+ "Instances: 0/1\n" + \
23
+ "Command: \n" + \
24
+ "CPUs: \n" + \
25
+ "Memory: MB\n" + \
26
+ "URI: http://example.com/big.tar\n" + \
27
+ "ENV: FOO=BAR\n" + \
28
+ "ENV: blubb=blah\n" + \
29
+ "Constraint: hostname:UNIQUE\n" + \
30
+ "Version: foo-version"
31
+ end
32
+
12
33
  its(:to_s) { should == expected_string }
34
+ its(:to_pretty_s) { should == expected_pretty_string }
13
35
  end
14
36
 
15
37
  describe '#to_json' do
16
38
  subject { described_class.new({ 'id' => '/app/foo' }) }
17
39
 
18
40
  let(:expected_string) do
19
- '{"id":"/app/foo"}'
41
+ '{"constraints":[],"env":{},"ports":[],"uris":[],"id":"/app/foo"}'
20
42
  end
21
43
 
22
44
  its(:to_json) { should == expected_string }
@@ -30,12 +52,41 @@ describe Marathon::App do
30
52
  end
31
53
  end
32
54
 
55
+ describe '#container' do
56
+ subject { described_class.new({
57
+ 'id' => '/ubuntu2', 'container' => {'type'=>'DOCKER', 'docker'=>{'image'=>'felixb/yocto-httpd'}}
58
+ })}
59
+
60
+ it 'has container' do
61
+ expect(subject.container).to be_instance_of(Marathon::Container)
62
+ expect(subject.container.type).to eq('DOCKER')
63
+ end
64
+ end
65
+
66
+ describe '#constraints' do
67
+ subject { described_class.new({ 'id' => '/ubuntu2', 'constraints' => [['hostname', 'UNIQUE']] }) }
68
+
69
+ it 'has constraints' do
70
+ expect(subject.constraints).to be_instance_of(Array)
71
+ expect(subject.constraints.first).to be_instance_of(Marathon::Constraint)
72
+ end
73
+ end
74
+
75
+ describe '#constraints' do
76
+ subject { described_class.new({ 'id' => '/ubuntu2', 'healthChecks' => [{ 'path' => '/ping' }] }) }
77
+
78
+ it 'has healthChecks' do
79
+ expect(subject.healthChecks).to be_instance_of(Array)
80
+ expect(subject.healthChecks.first).to be_instance_of(Marathon::HealthCheck)
81
+ end
82
+ end
83
+
33
84
  describe '#tasks' do
34
85
  subject { described_class.new({ 'id' => '/ubuntu2' }) }
35
86
 
36
87
  it 'checks for read only' do
37
88
  expect(subject).to receive(:check_read_only)
38
- subject.info['tasks'] = []
89
+ subject.info[:tasks] = []
39
90
  subject.tasks
40
91
  end
41
92
 
@@ -48,13 +99,13 @@ describe Marathon::App do
48
99
  end
49
100
 
50
101
  it 'loads tasks from API when not loaded already' do
51
- subject.info['tasks'] = nil
52
- expect(subject).to receive(:refresh) { subject.info['tasks'] = [] }
102
+ subject.info[:tasks] = nil
103
+ expect(subject).to receive(:refresh) { subject.info[:tasks] = [] }
53
104
  expect(subject.tasks).to eq([])
54
105
  end
55
106
 
56
107
  it 'shows already loaded tasks w/o API call' do
57
- subject.info['tasks'] = []
108
+ subject.info[:tasks] = []
58
109
  expect(subject).not_to receive(:refresh)
59
110
  expect(subject.tasks).to eq([])
60
111
  end
@@ -77,29 +128,30 @@ describe Marathon::App do
77
128
  end
78
129
 
79
130
  describe '#start!' do
80
- let(:app) { described_class.new({ 'id' => '/app/foo' }) }
131
+ subject { described_class.new({ 'id' => '/app/foo' }) }
81
132
 
82
133
  it 'checks for read only' do
83
134
  expect(subject).to receive(:check_read_only)
84
- expect(described_class).to receive(:start) { described_class.new }
135
+ expect(described_class).to receive(:start) { described_class.new('id' => subject.id) }
85
136
  subject.start!
86
137
  end
87
138
 
88
139
  it 'starts the app' do
89
- expect(described_class).to receive(:start).with({ 'id' => '/app/foo'}) do
90
- described_class.new({ 'id' => '/app/foo', 'started' => true })
140
+ expect(described_class).to receive(:start)
141
+ .with({:constraints=>[], :env=>{}, :ports=>[], :uris=>[], :id=>"/app/foo"}) do
142
+ described_class.new({ 'id' => '/app/foo', 'started' => true })
91
143
  end
92
- app.start!
93
- expect(app.info['started']).to be(true)
144
+ subject.start!
145
+ expect(subject.info[:started]).to be(true)
94
146
  end
95
147
  end
96
148
 
97
149
  describe '#refresh' do
98
- let(:app) { described_class.new({ 'id' => '/app/foo' }) }
150
+ subject { described_class.new({ 'id' => '/app/foo' }) }
99
151
 
100
152
  it 'checks for read only' do
101
153
  expect(subject).to receive(:check_read_only)
102
- expect(described_class).to receive(:get) { described_class.new }
154
+ expect(described_class).to receive(:get) { described_class.new('id' => subject.id) }
103
155
  subject.refresh
104
156
  end
105
157
 
@@ -107,13 +159,13 @@ describe Marathon::App do
107
159
  expect(described_class).to receive(:get).with('/app/foo') do
108
160
  described_class.new({ 'id' => '/app/foo', 'refreshed' => true })
109
161
  end
110
- app.refresh
111
- expect(app.info['refreshed']).to be(true)
162
+ subject.refresh
163
+ expect(subject.info[:refreshed]).to be(true)
112
164
  end
113
165
  end
114
166
 
115
167
  describe '#restart!' do
116
- let(:app) { described_class.new({ 'id' => '/app/foo' }) }
168
+ subject { described_class.new({ 'id' => '/app/foo' }) }
117
169
 
118
170
  it 'checks for read only' do
119
171
  expect(subject).to receive(:check_read_only)
@@ -124,18 +176,18 @@ describe Marathon::App do
124
176
  it 'restarts the app' do
125
177
  expect(described_class).to receive(:restart)
126
178
  .with('/app/foo', false)
127
- app.restart!
179
+ subject.restart!
128
180
  end
129
181
 
130
182
  it 'restarts the app, force' do
131
183
  expect(described_class).to receive(:restart)
132
184
  .with('/app/foo', true)
133
- app.restart!(true)
185
+ subject.restart!(true)
134
186
  end
135
187
  end
136
188
 
137
189
  describe '#change!' do
138
- let(:app) { described_class.new({ 'id' => '/app/foo' }) }
190
+ subject { described_class.new({ 'id' => '/app/foo' }) }
139
191
 
140
192
  it 'checks for read only' do
141
193
  expect(subject).to receive(:check_read_only)
@@ -145,12 +197,12 @@ describe Marathon::App do
145
197
 
146
198
  it 'changes the app' do
147
199
  expect(described_class).to receive(:change).with('/app/foo', {'instances' => 9000 }, false)
148
- app.change!('instances' => 9000)
200
+ subject.change!('instances' => 9000)
149
201
  end
150
202
  end
151
203
 
152
204
  describe '#roll_back!' do
153
- let(:app) { described_class.new({ 'id' => '/app/foo', 'instances' => 10 }) }
205
+ subject { described_class.new({ 'id' => '/app/foo', 'instances' => 10 }) }
154
206
 
155
207
  it 'checks for read only' do
156
208
  expect(subject).to receive(:check_read_only)
@@ -159,18 +211,18 @@ describe Marathon::App do
159
211
  end
160
212
 
161
213
  it 'changes the app' do
162
- expect(app).to receive(:change!).with({'version' => 'old_version' }, false)
163
- app.roll_back!('old_version')
214
+ expect(subject).to receive(:change!).with({'version' => 'old_version' }, false)
215
+ subject.roll_back!('old_version')
164
216
  end
165
217
 
166
218
  it 'changes the app with force' do
167
- expect(app).to receive(:change!).with({'version' => 'old_version' }, true)
168
- app.roll_back!('old_version', true)
219
+ expect(subject).to receive(:change!).with({'version' => 'old_version' }, true)
220
+ subject.roll_back!('old_version', true)
169
221
  end
170
222
  end
171
223
 
172
224
  describe '#scale!' do
173
- let(:app) { described_class.new({ 'id' => '/app/foo', 'instances' => 10 }) }
225
+ subject { described_class.new({ 'id' => '/app/foo', 'instances' => 10 }) }
174
226
 
175
227
  it 'checks for read only' do
176
228
  expect(subject).to receive(:check_read_only)
@@ -179,18 +231,18 @@ describe Marathon::App do
179
231
  end
180
232
 
181
233
  it 'changes the app' do
182
- expect(app).to receive(:change!).with({'instances' => 9000 }, false)
183
- app.scale!(9000)
234
+ expect(subject).to receive(:change!).with({'instances' => 9000 }, false)
235
+ subject.scale!(9000)
184
236
  end
185
237
 
186
238
  it 'changes the app with force' do
187
- expect(app).to receive(:change!).with({'instances' => 9000 }, true)
188
- app.scale!(9000, true)
239
+ expect(subject).to receive(:change!).with({'instances' => 9000 }, true)
240
+ subject.scale!(9000, true)
189
241
  end
190
242
  end
191
243
 
192
244
  describe '#suspend!' do
193
- let(:app) { described_class.new({ 'id' => '/app/foo', 'instances' => 10 }) }
245
+ subject { described_class.new({ 'id' => '/app/foo', 'instances' => 10 }) }
194
246
 
195
247
  it 'checks for read only' do
196
248
  expect(subject).to receive(:check_read_only)
@@ -199,13 +251,13 @@ describe Marathon::App do
199
251
  end
200
252
 
201
253
  it 'scales the app to 0' do
202
- expect(app).to receive(:scale!).with(0, false)
203
- app.suspend!
254
+ expect(subject).to receive(:scale!).with(0, false)
255
+ subject.suspend!
204
256
  end
205
257
 
206
258
  it 'scales the app to 0 with force' do
207
- expect(app).to receive(:scale!).with(0, true)
208
- app.suspend!(true)
259
+ expect(subject).to receive(:scale!).with(0, true)
260
+ subject.suspend!(true)
209
261
  end
210
262
  end
211
263
 
@@ -216,17 +268,17 @@ describe Marathon::App do
216
268
  expect(Marathon.connection).to receive(:get)
217
269
  .with('/v2/apps', {:cmd => 'foo', :embed => 'apps.tasks'})
218
270
  .and_return({ 'apps' => [] })
219
- described_class.list('foo', 'apps.tasks')
271
+ subject.list('foo', 'apps.tasks')
220
272
  end
221
273
 
222
274
  it 'raises error when run with strange embed' do
223
275
  expect {
224
- described_class.list(nil, 'foo')
276
+ subject.list(nil, 'foo')
225
277
  }.to raise_error(Marathon::Error::ArgumentError)
226
278
  end
227
279
 
228
280
  it 'lists apps', :vcr do
229
- apps = described_class.list
281
+ apps = subject.list
230
282
  expect(apps.size).not_to eq(0)
231
283
  expect(apps.first).to be_instance_of(described_class)
232
284
  expect(apps.first.cpus).to eq(0.1)
@@ -238,7 +290,7 @@ describe Marathon::App do
238
290
  subject { described_class }
239
291
 
240
292
  it 'starts the app', :vcr do
241
- app = described_class.start({ :id => '/test', :cmd => 'sleep 10', :instances => 1, :cpus => 0.1, :mem => 32})
293
+ app = subject.start({ :id => '/test', :cmd => 'sleep 10', :instances => 1, :cpus => 0.1, :mem => 32})
242
294
  expect(app).to be_instance_of(described_class)
243
295
  expect(app.id).to eq('/test')
244
296
  expect(app.instances).to eq(1)
@@ -248,7 +300,7 @@ describe Marathon::App do
248
300
 
249
301
  it 'fails getting not existing app', :vcr do
250
302
  expect {
251
- described_class.get('fooo app')
303
+ subject.get('fooo app')
252
304
  }.to raise_error(Marathon::Error::NotFoundError)
253
305
  end
254
306
  end
@@ -257,7 +309,7 @@ describe Marathon::App do
257
309
  subject { described_class }
258
310
 
259
311
  it 'gets the app', :vcr do
260
- app = described_class.get('/ubuntu')
312
+ app = subject.get('/ubuntu')
261
313
  expect(app).to be_instance_of(described_class)
262
314
  expect(app.id).to eq('/ubuntu')
263
315
  expect(app.instances).to eq(1)
@@ -267,7 +319,7 @@ describe Marathon::App do
267
319
 
268
320
  it 'fails getting not existing app', :vcr do
269
321
  expect {
270
- described_class.get('fooo app')
322
+ subject.get('fooo app')
271
323
  }.to raise_error(Marathon::Error::NotFoundError)
272
324
  end
273
325
  end
@@ -276,12 +328,12 @@ describe Marathon::App do
276
328
  subject { described_class }
277
329
 
278
330
  it 'deletes the app', :vcr do
279
- described_class.delete('/ubuntu')
331
+ subject.delete('/ubuntu')
280
332
  end
281
333
 
282
334
  it 'fails deleting not existing app', :vcr do
283
335
  expect {
284
- described_class.delete('fooo app')
336
+ subject.delete('fooo app')
285
337
  }.to raise_error(Marathon::Error::NotFoundError)
286
338
  end
287
339
  end
@@ -289,9 +341,13 @@ describe Marathon::App do
289
341
  describe '.restart' do
290
342
  subject { described_class }
291
343
 
344
+ it 'restarts an app', :vcr do
345
+ expect(subject.restart('/ubuntu2')).to be_instance_of(Marathon::DeploymentInfo)
346
+ end
347
+
292
348
  it 'fails restarting not existing app', :vcr do
293
349
  expect {
294
- described_class.restart('fooo app')
350
+ subject.restart('fooo app')
295
351
  }.to raise_error(Marathon::Error::NotFoundError)
296
352
  end
297
353
  end
@@ -300,13 +356,15 @@ describe Marathon::App do
300
356
  subject { described_class }
301
357
 
302
358
  it 'changes the app', :vcr do
303
- described_class.change('/ubuntu2', { 'instances' => 2 })
304
- described_class.change('/ubuntu2', { 'instances' => 1 }, true)
359
+ expect(subject.change('/ubuntu2', { 'instances' => 2 }))
360
+ .to be_instance_of(Marathon::DeploymentInfo)
361
+ expect(subject.change('/ubuntu2', { 'instances' => 1 }, true))
362
+ .to be_instance_of(Marathon::DeploymentInfo)
305
363
  end
306
364
 
307
365
  it 'fails with stange attributes', :vcr do
308
366
  expect {
309
- described_class.change('/ubuntu2', { 'instances' => 'foo' })
367
+ subject.change('/ubuntu2', { 'instances' => 'foo' })
310
368
  }.to raise_error(Marathon::Error::ClientError)
311
369
  end
312
370
  end
@@ -331,4 +389,4 @@ describe Marathon::App do
331
389
  expect(version.read_only).to be(true)
332
390
  end
333
391
  end
334
- end
392
+ end