marathon-api 1.0.0 → 1.1.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 23bb47d2d3c76218c3aa461968660ac5551e3f76
4
- data.tar.gz: ce1149c5f48884ecd403b69e61fb9ab12f8e0b3b
3
+ metadata.gz: 37c0d0ae7d91ddeaa7861bffa40b81e72a434e19
4
+ data.tar.gz: c5d165cf0d590a78fea207dfc705fa409174a8a5
5
5
  SHA512:
6
- metadata.gz: 7fca9778495fc930fe67d956a7db8e749f08c56e2e0bcce4320bac3dfbf94ac6d1025909135c573e06938fa2c27705b1a2d7f774a8713830192d768c27990faa
7
- data.tar.gz: 6301fea04cd2a18a89aa6dd59a52c46b79d2ee8a6a580131783661c249021e75750aa758c19c7efad99dad13621e24243f64270654f29b6d3aa672c75f00684d
6
+ metadata.gz: 941597d6ae53616a9931735f9a016606933df061115ff21eb1202325388d785e8e824dedc64e00d3fd64725f2560aac1bfdec0758d3b22ad57245b485f94dcdd
7
+ data.tar.gz: 56afa004de719231016576cba81c13f2553d50e7378e9b79a05f68f5b8664e0ac52b0fa25f2df69f7f29710598f64b5809dfdaff39e1263efd43e4cccb0c828e
data/LICENSE CHANGED
@@ -1,6 +1,6 @@
1
1
  The MIT License (MIT)
2
2
 
3
- Copyright (c) 2014 Swipely, Inc.
3
+ Copyright (c) 2015 Otto (GmbH & Co KG)
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
data/README.md CHANGED
@@ -102,6 +102,12 @@ apps.last.delete!
102
102
 
103
103
  The other Marathon endpoints are available in the same way.
104
104
 
105
+ Contributing
106
+ ------------
107
+
108
+ Please fork and send pull request.
109
+ Make sure to have test cases for your changes.
110
+
105
111
  Credits
106
112
  -------
107
113
 
data/bin/marathon CHANGED
@@ -46,9 +46,9 @@ def subcmd_start(cmd_opts)
46
46
  if cmd_opts[:json]
47
47
  path = cmd_opts[:json]
48
48
  if path == '-'
49
- app_opts = Marathon::Util.keywordize_hash(JSON.parse($stdin.read))
49
+ app_opts = Marathon::Util.keywordize_hash!(JSON.parse($stdin.read))
50
50
  elsif File.exists?(path)
51
- app_opts = Marathon::Util.keywordize_hash(JSON.parse(File.read(cmd_opts[:json])))
51
+ app_opts = Marathon::Util.keywordize_hash!(JSON.parse(File.read(cmd_opts[:json])))
52
52
  else
53
53
  raise Marathon::Error::ArgumentError, "File '#{path}' does not exist"
54
54
  end
@@ -231,12 +231,10 @@ def run_subcmd(cmd, cmd_opts)
231
231
  end
232
232
  end
233
233
 
234
- if __FILE__ == $0
235
- global_opts = parse_global_opts
236
- set_global_opts(global_opts)
234
+ global_opts = parse_global_opts
235
+ set_global_opts(global_opts)
237
236
 
238
- cmd = parse_subcmd
239
- cmd_opts = parse_subcmd_opts(cmd)
237
+ cmd = parse_subcmd
238
+ cmd_opts = parse_subcmd_opts(cmd)
240
239
 
241
- run_subcmd(cmd, cmd_opts)
242
- end
240
+ run_subcmd(cmd, cmd_opts)
data/lib/marathon/app.rb CHANGED
@@ -6,13 +6,12 @@ class Marathon::App < Marathon::Base
6
6
  storeUris tasksRunning tasksStaged uris user version ]
7
7
 
8
8
  DEFAULTS = {
9
- :constraints => [],
10
9
  :env => {},
11
10
  :ports => [],
12
11
  :uris => []
13
12
  }
14
13
 
15
- attr_reader :read_only
14
+ attr_reader :healthChecks, :constraints, :container, :read_only, :tasks
16
15
 
17
16
  # Create a new application object.
18
17
  # ++hash++: Hash including all attributes.
@@ -22,6 +21,7 @@ class Marathon::App < Marathon::Base
22
21
  super(Marathon::Util.merge_keywordized_hash(DEFAULTS, hash), ACCESSORS)
23
22
  raise ArgumentError, 'App must have an id' unless id
24
23
  @read_only = read_only
24
+ refresh_attributes
25
25
  end
26
26
 
27
27
  # Prevent actions on read only instances.
@@ -32,40 +32,6 @@ class Marathon::App < Marathon::Base
32
32
  end
33
33
  end
34
34
 
35
- # Get container info.
36
- # This is read only!
37
- def container
38
- if @info[:container]
39
- Marathon::Container.new(@info[:container])
40
- else
41
- nil
42
- end
43
- end
44
-
45
- # Get constrains.
46
- # This is read only!
47
- def constraints
48
- @info[:constraints].map { |e| Marathon::Constraint.new(e) }
49
- end
50
-
51
- # Get health checks.
52
- # This is read only!
53
- def healthChecks
54
- @info[:healthChecks].map { |e| Marathon::HealthCheck.new(e) }
55
- end
56
-
57
- # List all running tasks for the application.
58
- # This is read only!
59
- def tasks
60
- check_read_only
61
- unless @info[:tasks]
62
- refresh
63
- end
64
-
65
- raise UnexpectedResponseError, "Expected to find tasks element in app's info" unless @info[:tasks]
66
- @info[:tasks].map { |e| Marathon::Task.new(e) }
67
- end
68
-
69
35
  # List the versions of the application.
70
36
  # ++version++: Get a specific versions
71
37
  # Returns Array of Strings if ++version = nil++,
@@ -83,13 +49,14 @@ class Marathon::App < Marathon::Base
83
49
  check_read_only
84
50
  new_app = self.class.get(id)
85
51
  @info = new_app.info
52
+ refresh_attributes
86
53
  end
87
54
 
88
55
  # Create and start the application.
89
- def start!
90
- check_read_only
91
- new_app = self.class.start(info)
92
- @info = new_app.info
56
+ # ++force++: If the app is affected by a running deployment, then the update operation will fail.
57
+ # The current deployment can be overridden by setting the `force` query parameter.
58
+ def start!(force = false)
59
+ change!(info, force)
93
60
  end
94
61
 
95
62
  # Initiates a rolling restart of all running tasks of the given app.
@@ -109,7 +76,14 @@ class Marathon::App < Marathon::Base
109
76
  # The current deployment can be overridden by setting the `force` query parameter.
110
77
  def change!(hash, force = false)
111
78
  check_read_only
112
- self.class.change(id, hash, force)
79
+ Marathon::Util.keywordize_hash!(hash)
80
+ if hash[:version] and hash.size > 1
81
+ # remove :version if it's not the only key
82
+ new_hash = Marathon::Util.remove_keys(hash, [:version])
83
+ else
84
+ new_hash = hash
85
+ end
86
+ self.class.change(id, new_hash, force)
113
87
  end
114
88
 
115
89
  # Create a new version with parameters of an old version.
@@ -118,7 +92,7 @@ class Marathon::App < Marathon::Base
118
92
  # ++force++: If the app is affected by a running deployment, then the update operation will fail.
119
93
  # The current deployment can be overridden by setting the `force` query parameter.
120
94
  def roll_back!(version, force = false)
121
- change!({'version' => version}, force)
95
+ change!({:version => version}, force)
122
96
  end
123
97
 
124
98
  # Change the number of desired instances.
@@ -126,7 +100,7 @@ class Marathon::App < Marathon::Base
126
100
  # ++force++: If the app is affected by a running deployment, then the update operation will fail.
127
101
  # The current deployment can be overridden by setting the `force` query parameter.
128
102
  def scale!(instances, force = false)
129
- change!({'instances' => instances}, force)
103
+ change!({:instances => instances}, force)
130
104
  end
131
105
 
132
106
  # Change the number of desired instances to 0.
@@ -169,6 +143,18 @@ Version: #{version}
169
143
  constraints.map { |e| "Constraint: #{e.to_pretty_s}" }.join("\n")
170
144
  end
171
145
 
146
+ # Rebuild attribute classes
147
+ def refresh_attributes
148
+ @healthChecks = (info[:healthChecks] || []).map { |e| Marathon::HealthCheck.new(e) }
149
+ @constraints = (info[:constraints] || []).map { |e| Marathon::Constraint.new(e) }
150
+ if info[:container]
151
+ @container = Marathon::Container.new(info[:container])
152
+ else
153
+ @container = nil
154
+ end
155
+ @tasks = (@info[:tasks] || []).map { |e| Marathon::Task.new(e) }
156
+ end
157
+
172
158
  class << self
173
159
 
174
160
  # List the application with id.
data/lib/marathon/base.rb CHANGED
@@ -7,13 +7,13 @@ class Marathon::Base
7
7
 
8
8
  # Create the object
9
9
  # ++hash++: object returned from API. May be Hash or Array.
10
- # ++attr_readers++: List of attribute readers.
11
- def initialize(hash, attr_readers = [])
12
- raise ArgumentError, 'hash must be a Hash' if attr_readers and attr_readers.size > 0 and not hash.is_a?(Hash)
10
+ # ++attr_accessors++: List of attribute accessors.
11
+ def initialize(hash, attr_accessors = [])
12
+ raise ArgumentError, 'hash must be a Hash' if attr_accessors and attr_accessors.size > 0 and not hash.is_a?(Hash)
13
13
  raise ArgumentError, 'hash must be Hash or Array' unless hash.is_a?(Hash) or hash.is_a?(Array)
14
- raise ArgumentError, 'attr_readers must be an Array' unless attr_readers.is_a?(Array)
15
- @info = Marathon::Util.keywordize_hash(hash)
16
- attr_readers.each { |e| add_attr_reader(e) }
14
+ raise ArgumentError, 'attr_accessors must be an Array' unless attr_accessors.is_a?(Array)
15
+ @info = Marathon::Util.keywordize_hash!(hash)
16
+ attr_accessors.each { |e| add_attr_accessor(e) }
17
17
  end
18
18
 
19
19
  # Return application as JSON formatted string.
@@ -23,10 +23,11 @@ class Marathon::Base
23
23
 
24
24
  private
25
25
 
26
- # Create attr_reader for @info[key].
26
+ # Create attr_accessor for @info[key].
27
27
  # ++key++: key in @info
28
- def add_attr_reader(key)
28
+ def add_attr_accessor(key)
29
29
  sym = key.to_sym
30
- self.class.send(:define_method, sym.id2name) { |*args, &block| info[sym] }
30
+ self.class.send(:define_method, sym.id2name) { info[sym] }
31
+ self.class.send(:define_method, "#{sym.id2name}=") { |v| info[sym] = v }
31
32
  end
32
- end
33
+ end
@@ -10,27 +10,15 @@ class Marathon::Container < Marathon::Base
10
10
  :volumes => []
11
11
  }
12
12
 
13
+ attr_reader :docker, :volumes
14
+
13
15
  # Create a new container object.
14
16
  # ++hash++: Hash returned by API.
15
17
  def initialize(hash)
16
18
  super(Marathon::Util.merge_keywordized_hash(DEFAULTS, hash), ACCESSORS)
17
19
  Marathon::Util.validate_choice('type', type, SUPPERTED_TYPES)
18
- end
19
-
20
- # Get container's docker information.
21
- # This is read only!
22
- def docker
23
- if @info[:docker]
24
- Marathon::ContainerDocker.new(@info[:docker])
25
- else
26
- nil
27
- end
28
- end
29
-
30
- # Get container's volumes.
31
- # This is read only!
32
- def volumes
33
- @info[:volumes].map { |e| Marathon::ContainerVolume.new(e) }
20
+ @docker = Marathon::ContainerDocker.new(info[:docker]) if info[:docker]
21
+ @volumes = info[:volumes].map { |e| Marathon::ContainerVolume.new(e) }
34
22
  end
35
23
 
36
24
  def to_s
@@ -38,4 +26,4 @@ class Marathon::Container < Marathon::Base
38
26
  + " :volumes => #{Marathon::Util.items_to_pretty_s(volumes)} }"
39
27
  end
40
28
 
41
- end
29
+ end
@@ -8,18 +8,15 @@ class Marathon::ContainerDocker < Marathon::Base
8
8
  :portMappings => []
9
9
  }
10
10
 
11
+ attr_reader :portMappings
12
+
11
13
  # Create a new container docker object.
12
14
  # ++hash++: Hash returned by API.
13
15
  def initialize(hash)
14
16
  super(Marathon::Util.merge_keywordized_hash(DEFAULTS, hash), ACCESSORS)
15
17
  Marathon::Util.validate_choice('network', network, %w[BRIDGE HOST])
16
18
  raise Marathon::Error::ArgumentError, 'image must not be nil' unless image
17
- end
18
-
19
- # Get container's port mappings.
20
- # This is read only!
21
- def portMappings
22
- @info[:portMappings].map { |e| Marathon::ContainerDockerPortMapping.new(e) }
19
+ @portMappings = (info[:portMappings] || []).map { |e| Marathon::ContainerDockerPortMapping.new(e) }
23
20
  end
24
21
 
25
22
  def to_pretty_s
@@ -30,4 +27,4 @@ class Marathon::ContainerDocker < Marathon::Base
30
27
  "Marathon::ContainerDocker { :image => #{image} }"
31
28
  end
32
29
 
33
- end
30
+ end
@@ -5,36 +5,26 @@ class Marathon::Group < Marathon::Base
5
5
  ACCESSORS = %w[ id dependencies version ]
6
6
 
7
7
  DEFAULTS = {
8
- :apps => [],
9
- :dependencies => [],
10
- :groups => []
8
+ :dependencies => []
11
9
  }
12
10
 
11
+ attr_reader :apps, :groups
12
+
13
13
  # Create a new group object.
14
14
  # ++hash++: Hash including all attributes.
15
15
  # See https://mesosphere.github.io/marathon/docs/rest-api.html#post-/v2/groups for full details.
16
16
  def initialize(hash)
17
17
  super(Marathon::Util.merge_keywordized_hash(DEFAULTS, hash), ACCESSORS)
18
18
  raise ArgumentError, 'Group must have an id' unless id
19
+ refresh_attributes
19
20
  raise ArgumentError, 'Group can have either groups or apps, not both' if apps.size > 0 and groups.size > 0
20
21
  end
21
22
 
22
- # Get apps.
23
- # This is read only!
24
- def apps
25
- @info[:apps].map { |e| Marathon::App.new(e) }
26
- end
27
-
28
- # Get groups.
29
- # This is read only!
30
- def groups
31
- @info[:groups].map { |e| Marathon::Group.new(e) }
32
- end
33
-
34
23
  # Reload attributes from marathon API.
35
24
  def refresh
36
25
  new_app = self.class.get(id)
37
26
  @info = new_app.info
27
+ refresh_attributes
38
28
  end
39
29
 
40
30
  # Create and start a the application group. Application groups can contain other application groups.
@@ -65,7 +55,14 @@ class Marathon::Group < Marathon::Base
65
55
  # ++force++: If the group is affected by a running deployment, then the update operation will fail.
66
56
  # The current deployment can be overridden by setting the `force` query parameter.
67
57
  def change!(hash, force = false)
68
- self.class.change(id, hash, force)
58
+ Marathon::Util.keywordize_hash!(hash)
59
+ if hash[:version] and hash.size > 1
60
+ # remove :version if it's not the only key
61
+ new_hash = Marathon::Util.remove_keys(hash, [:version])
62
+ else
63
+ new_hash = hash
64
+ end
65
+ self.class.change(id, new_hash, force)
69
66
  end
70
67
 
71
68
  # Create a new version with parameters of an old version.
@@ -97,6 +94,12 @@ Version: #{version}
97
94
  array.map { |e| e.to_pretty_s.split("\n").map { |e| " #{e}" }}.join("\n")
98
95
  end
99
96
 
97
+ # Rebuild attribute classes
98
+ def refresh_attributes
99
+ @apps = (info[:apps] || []).map { |e| Marathon::App.new(e) }
100
+ @groups = (info[:groups] || []).map { |e| Marathon::Group.new(e) }
101
+ end
102
+
100
103
  class << self
101
104
 
102
105
  # List the group with the specified ID.
data/lib/marathon/util.rb CHANGED
@@ -40,21 +40,33 @@ class Marathon::Util
40
40
 
41
41
  # Swap keys of the hash against their symbols.
42
42
  # ++hash++: the hash
43
- def keywordize_hash(hash)
43
+ # ++ignore_keys++: don't keywordize hashes under theses keys
44
+ def keywordize_hash!(hash, ignore_keys = [:env])
44
45
  if hash.is_a?(Hash)
45
- new_hash = {}
46
- hash.each do |k,v|
46
+ hmap!(hash) do |k,v|
47
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]
48
+ if ignore_keys.include?(key) and v.is_a?(Hash)
49
+ { key => v }
51
50
  else
52
- new_hash[key] = keywordize_hash(hash[k])
51
+ { key => keywordize_hash!(v) }
53
52
  end
54
53
  end
54
+ elsif hash.is_a?(Array)
55
+ hash.map! { |e| keywordize_hash!(e) }
56
+ end
57
+ hash
58
+ end
59
+
60
+ # Remove keys from hash and all it's sub hashes.
61
+ # ++hash++: the hash
62
+ # ++keys++: list of keys to remove
63
+ def remove_keys(hash, keys)
64
+ if hash.is_a?(Hash)
65
+ new_hash = {}
66
+ hash.each { |k,v| new_hash[k] = remove_keys(v, keys) unless keys.include?(k) }
55
67
  new_hash
56
68
  elsif hash.is_a?(Array)
57
- hash.map { |e| keywordize_hash(e) }
69
+ hash.map { |e| remove_keys(e, keys) }
58
70
  else
59
71
  hash
60
72
  end
@@ -62,7 +74,7 @@ class Marathon::Util
62
74
 
63
75
  # Merge two hashes but keywordize both.
64
76
  def merge_keywordized_hash(h1, h2)
65
- keywordize_hash(h1).merge(keywordize_hash(h2))
77
+ keywordize_hash!(h1).merge(keywordize_hash!(h2))
66
78
  end
67
79
 
68
80
  # Stringify an item or an array of items.
@@ -75,5 +87,16 @@ class Marathon::Util
75
87
  item.to_pretty_s
76
88
  end
77
89
  end
90
+
91
+ # Implement map! on a hash
92
+ def hmap!(hash, &block)
93
+ hash.keys.each do |key|
94
+ new_hash = block.call(key, hash[key])
95
+ new_key = new_hash.keys.first
96
+ hash[new_key] = new_hash[new_key]
97
+ hash.delete(key) unless key == new_key
98
+ end
99
+ hash
100
+ end
78
101
  end
79
102
  end
@@ -1,3 +1,3 @@
1
1
  module Marathon
2
- VERSION = '1.0.0'
2
+ VERSION = '1.1.0'
3
3
  end
@@ -38,7 +38,7 @@ describe Marathon::App do
38
38
  subject { described_class.new({ 'id' => '/app/foo' }) }
39
39
 
40
40
  let(:expected_string) do
41
- '{"constraints":[],"env":{},"ports":[],"uris":[],"id":"/app/foo"}'
41
+ '{"env":{},"ports":[],"uris":[],"id":"/app/foo"}'
42
42
  end
43
43
 
44
44
  its(:to_json) { should == expected_string }
@@ -84,26 +84,6 @@ describe Marathon::App do
84
84
  describe '#tasks' do
85
85
  subject { described_class.new({ 'id' => '/ubuntu2' }) }
86
86
 
87
- it 'checks for read only' do
88
- expect(subject).to receive(:check_read_only)
89
- subject.info[:tasks] = []
90
- subject.tasks
91
- end
92
-
93
- it 'has tasks', :vcr do
94
- tasks = subject.tasks
95
- expect(tasks).to be_instance_of(Array)
96
- expect(tasks.size).to eq(1)
97
- expect(tasks.first).to be_instance_of(Marathon::Task)
98
- expect(tasks.first.appId).to eq(subject.id)
99
- end
100
-
101
- it 'loads tasks from API when not loaded already' do
102
- subject.info[:tasks] = nil
103
- expect(subject).to receive(:refresh) { subject.info[:tasks] = [] }
104
- expect(subject.tasks).to eq([])
105
- end
106
-
107
87
  it 'shows already loaded tasks w/o API call' do
108
88
  subject.info[:tasks] = []
109
89
  expect(subject).not_to receive(:refresh)
@@ -132,17 +112,22 @@ describe Marathon::App do
132
112
 
133
113
  it 'checks for read only' do
134
114
  expect(subject).to receive(:check_read_only)
135
- expect(described_class).to receive(:start) { described_class.new('id' => subject.id) }
115
+ expect(described_class).to receive(:change).with(
116
+ '/app/foo',
117
+ {:env=>{}, :ports=>[], :uris=>[], :id=>"/app/foo"},
118
+ false
119
+ )
136
120
  subject.start!
137
121
  end
138
122
 
139
123
  it 'starts the app' do
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 })
143
- end
124
+ expect(described_class).to receive(:change)
125
+ .with(
126
+ '/app/foo',
127
+ {:env=>{}, :ports=>[], :uris=>[], :id=>"/app/foo"},
128
+ false
129
+ )
144
130
  subject.start!
145
- expect(subject.info[:started]).to be(true)
146
131
  end
147
132
  end
148
133
 
@@ -196,13 +181,13 @@ describe Marathon::App do
196
181
  end
197
182
 
198
183
  it 'changes the app' do
199
- expect(described_class).to receive(:change).with('/app/foo', {'instances' => 9000 }, false)
200
- subject.change!('instances' => 9000)
184
+ expect(described_class).to receive(:change).with('/app/foo', {:instances => 9000 }, false)
185
+ subject.change!('instances' => 9000, 'version' => 'old-version')
201
186
  end
202
187
  end
203
188
 
204
189
  describe '#roll_back!' do
205
- subject { described_class.new({ 'id' => '/app/foo', 'instances' => 10 }) }
190
+ subject { described_class.new({:id => '/app/foo', :instances => 10}) }
206
191
 
207
192
  it 'checks for read only' do
208
193
  expect(subject).to receive(:check_read_only)
@@ -211,18 +196,18 @@ describe Marathon::App do
211
196
  end
212
197
 
213
198
  it 'changes the app' do
214
- expect(subject).to receive(:change!).with({'version' => 'old_version' }, false)
199
+ expect(subject).to receive(:change!).with({:version => 'old_version' }, false)
215
200
  subject.roll_back!('old_version')
216
201
  end
217
202
 
218
203
  it 'changes the app with force' do
219
- expect(subject).to receive(:change!).with({'version' => 'old_version' }, true)
204
+ expect(subject).to receive(:change!).with({:version => 'old_version' }, true)
220
205
  subject.roll_back!('old_version', true)
221
206
  end
222
207
  end
223
208
 
224
209
  describe '#scale!' do
225
- subject { described_class.new({ 'id' => '/app/foo', 'instances' => 10 }) }
210
+ subject { described_class.new({:id => '/app/foo', :instances => 10}) }
226
211
 
227
212
  it 'checks for read only' do
228
213
  expect(subject).to receive(:check_read_only)
@@ -231,18 +216,18 @@ describe Marathon::App do
231
216
  end
232
217
 
233
218
  it 'changes the app' do
234
- expect(subject).to receive(:change!).with({'instances' => 9000 }, false)
219
+ expect(subject).to receive(:change!).with({:instances => 9000}, false)
235
220
  subject.scale!(9000)
236
221
  end
237
222
 
238
223
  it 'changes the app with force' do
239
- expect(subject).to receive(:change!).with({'instances' => 9000 }, true)
224
+ expect(subject).to receive(:change!).with({:instances => 9000}, true)
240
225
  subject.scale!(9000, true)
241
226
  end
242
227
  end
243
228
 
244
229
  describe '#suspend!' do
245
- subject { described_class.new({ 'id' => '/app/foo', 'instances' => 10 }) }
230
+ subject { described_class.new({'id' => '/app/foo', :instances => 10}) }
246
231
 
247
232
  it 'checks for read only' do
248
233
  expect(subject).to receive(:check_read_only)
@@ -290,7 +275,14 @@ describe Marathon::App do
290
275
  subject { described_class }
291
276
 
292
277
  it 'starts the app', :vcr do
293
- app = subject.start({ :id => '/test', :cmd => 'sleep 10', :instances => 1, :cpus => 0.1, :mem => 32})
278
+ app = subject.start({
279
+ :id => '/test',
280
+ :cmd => 'sleep 10',
281
+ :instances => 1,
282
+ :cpus => 0.1,
283
+ :mem => 32,
284
+ :version => 'foo-version'
285
+ })
294
286
  expect(app).to be_instance_of(described_class)
295
287
  expect(app.id).to eq('/test')
296
288
  expect(app.instances).to eq(1)
@@ -22,7 +22,7 @@ describe Marathon::Base do
22
22
  }) }
23
23
 
24
24
  let(:expected_string) do
25
- '{"app":{"id":"/app/foo"},"foo":"blubb","bar":1}'
25
+ '{"foo":"blubb","bar":1,"app":{"id":"/app/foo"}}'
26
26
  end
27
27
 
28
28
  its(:to_json) { should == expected_string }
@@ -50,4 +50,4 @@ describe Marathon::Base do
50
50
  its(:bar) { should == 1 }
51
51
  end
52
52
 
53
- end
53
+ end
@@ -1,25 +1,25 @@
1
1
  require 'spec_helper'
2
2
 
3
3
  DEPLOYMENT_EXAMPLE = {
4
- "affectedApps" => ["/test"],
5
- "id" => "867ed450-f6a8-4d33-9b0e-e11c5513990b",
6
- "steps" => [
4
+ :affectedApps => ["/test"],
5
+ :id => "867ed450-f6a8-4d33-9b0e-e11c5513990b",
6
+ :steps => [
7
7
  [
8
8
  {
9
- "action" => "ScaleApplication",
10
- "app" => "/test"
9
+ :action => "ScaleApplication",
10
+ :app => "/test"
11
11
  }
12
12
  ]
13
13
  ],
14
- "currentActions" => [
14
+ :currentActions => [
15
15
  {
16
- "action" => "ScaleApplication",
17
- "app" => "/test"
16
+ :action => "ScaleApplication",
17
+ :app => "/test"
18
18
  }
19
19
  ],
20
- "version" => "2014-08-26T08:18:03.595Z",
21
- "currentStep" => 1,
22
- "totalSteps" => 1
20
+ :version => "2014-08-26T08:18:03.595Z",
21
+ :currentStep => 1,
22
+ :totalSteps => 1
23
23
  }
24
24
 
25
25
  describe Marathon::Deployment do
@@ -44,23 +44,23 @@ describe Marathon::Deployment do
44
44
  describe 'attributes' do
45
45
  subject { described_class.new(DEPLOYMENT_EXAMPLE) }
46
46
 
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'] }
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
55
  subject { described_class.new(DEPLOYMENT_EXAMPLE) }
56
56
 
57
57
  it 'deletes the deployment' do
58
- expect(described_class).to receive(:delete).with(DEPLOYMENT_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(DEPLOYMENT_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
@@ -70,9 +70,9 @@ describe Marathon::Deployment do
70
70
 
71
71
  it 'lists deployments', :vcr do
72
72
  # start a deployment
73
- Marathon::App.change('/test', {'instances' => 0})
73
+ Marathon::App.change('/test', {:instances => 0})
74
74
  sleep 1
75
- Marathon::App.change('/test', {'instances' => 2})
75
+ Marathon::App.change('/test', {:instances => 2})
76
76
  sleep 1
77
77
 
78
78
  deployments = subject.list
@@ -86,9 +86,9 @@ describe Marathon::Deployment do
86
86
 
87
87
  it 'deletes deployments', :vcr do
88
88
  # start a deployment
89
- info = Marathon::App.change('/test', {'instances' => 1})
89
+ info = Marathon::App.change('/test', {:instances => 1})
90
90
  expect(subject.delete(info.deploymentId)).to be_instance_of(Marathon::DeploymentInfo)
91
91
  end
92
92
  end
93
93
 
94
- end
94
+ end
@@ -23,11 +23,13 @@ EXAMPLE_GROUP = {
23
23
  "upgradeStrategy" => {
24
24
  "minimumHealthCapacity" => 1.0
25
25
  },
26
- "tasks" => []
26
+ "tasks" => [],
27
+ "version" => 'foo-version'
27
28
  }
28
29
  ],
29
30
  "dependencies" => [],
30
- "groups" => []
31
+ "groups" => [],
32
+ "version" => 'foo-version'
31
33
  }
32
34
 
33
35
  describe Marathon::Group do
@@ -46,8 +48,8 @@ describe Marathon::Group do
46
48
  " Command: sleep 30\n" + \
47
49
  " CPUs: 1.0\n" + \
48
50
  " Memory: 128.0 MB\n" + \
49
- " Version:\n" + \
50
- "Version:"
51
+ " Version: foo-version\n" + \
52
+ "Version: foo-version"
51
53
 
52
54
  end
53
55
 
@@ -60,7 +62,7 @@ describe Marathon::Group do
60
62
 
61
63
  it 'starts the group' do
62
64
  expect(described_class).to receive(:start)
63
- .with({:apps=>[], :dependencies=>[], :groups=>[], :id=>'/group/foo'}) do
65
+ .with({:dependencies=>[], :id=>'/group/foo'}) do
64
66
  Marathon::DeploymentInfo.new({ 'version' => 'new-version' })
65
67
  end
66
68
  expect(subject.start!.version).to eq('new-version')
@@ -83,9 +85,14 @@ describe Marathon::Group do
83
85
  subject { described_class.new({ 'id' => '/app/foo' }) }
84
86
 
85
87
  it 'changes the group' do
86
- expect(described_class).to receive(:change).with('/app/foo', {'instances' => 9000 }, false)
88
+ expect(described_class).to receive(:change).with('/app/foo', {:instances => 9000 }, false)
87
89
  subject.change!('instances' => 9000)
88
90
  end
91
+
92
+ it 'changes the group and strips :version' do
93
+ expect(described_class).to receive(:change).with('/app/foo', {:instances => 9000 }, false)
94
+ subject.change!('instances' => 9000, :version => 'old-version')
95
+ end
89
96
  end
90
97
 
91
98
  describe '#roll_back!' do
@@ -41,23 +41,46 @@ describe Marathon::Util do
41
41
  end
42
42
  end
43
43
 
44
- describe '.keywordize_hash' do
44
+ describe '.keywordize_hash!' do
45
45
  subject { described_class }
46
46
 
47
47
  it 'keywordizes the hash' do
48
- expect(subject.keywordize_hash({
49
- 'foo' => 'bar',
50
- 'f00' => {'w00h00' => 'yeah'},
51
- 'bang' => [{'tricky' => 'one'}],
52
- 'env' => {'foo' => 'bar'},
53
- 'null' => nil
54
- })).to eq({
48
+ hash = {
49
+ 'foo' => 'bar',
50
+ 'f00' => {'w00h00' => 'yeah'},
51
+ 'bang' => [{'tricky' => 'one'}],
52
+ 'env' => {'foo' => 'bar'},
53
+ 'null' => nil
54
+ }
55
+
56
+ expect(subject.keywordize_hash!(hash)).to eq({
55
57
  :foo => 'bar',
56
58
  :f00 => {:w00h00 => 'yeah'},
57
59
  :bang => [{:tricky => 'one'}],
58
60
  :env => {'foo' => 'bar'},
59
61
  :null => nil
60
62
  })
63
+ # make sure, it changes the hash w/o creating a new one
64
+ expect(hash[:foo]).to eq('bar')
65
+ end
66
+ end
67
+
68
+ describe '.remove_keys' do
69
+ subject { described_class }
70
+
71
+ it 'removes keys from hash' do
72
+ hash = {
73
+ :foo => 'bar',
74
+ :deleteme => {'w00h00' => 'yeah'},
75
+ :blah => [{:deleteme => :foo}, 1]
76
+ }
77
+
78
+ expect(subject.remove_keys(hash, [:deleteme])).to eq({
79
+ :foo => 'bar',
80
+ :blah => [{}, 1]
81
+ })
82
+ # make sure, it does not changes the original hash
83
+ expect(hash.size).to eq(3)
61
84
  end
62
85
  end
63
86
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: marathon-api
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.0
4
+ version: 1.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Felix Bechstein
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-03-09 00:00:00.000000000 Z
11
+ date: 2015-03-11 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: json