Skip to content

Commit 97dc432

Browse files
committed
wip validations
1 parent 5befa06 commit 97dc432

File tree

6 files changed

+210
-285
lines changed

6 files changed

+210
-285
lines changed

app/actions/deployment_create.rb

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -219,6 +219,8 @@ def starting_state(message)
219219

220220
def starting_process_instances(message, desired_instances)
221221
if message.strategy == DeploymentModel::CANARY_STRATEGY
222+
# TODO: replace this with [message.max_in_flight, deployment.next_canary_step].min
223+
# or remove this 'if' and make `desired_instances` do this work
222224
1
223225
else
224226
[message.max_in_flight, desired_instances].min

app/messages/deployment_create_message.rb

Lines changed: 66 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -10,25 +10,21 @@ class DeploymentCreateMessage < MetadataBaseMessage
1010
options
1111
]
1212

13+
ALLOWED_STEP_KEYS = [
14+
:instance_weight
15+
]
16+
1317
validates_with NoAdditionalKeysValidator
1418
validates :strategy,
1519
inclusion: { in: %w[rolling canary], message: "'%<value>s' is not a supported deployment strategy" },
1620
allow_nil: true
1721
validate :mutually_exclusive_droplet_sources
1822

19-
validates :options,
20-
allow_nil: true,
21-
hash: true
22-
23-
validates :canary_steps,
24-
allow_nil: true,
25-
array: true
23+
# validates :options,
24+
# allow_nil: true,
25+
# hash: true
2626

27-
validates :canary_options,
28-
allow_nil: true,
29-
hash: true
30-
31-
validate :validate_canary_options
27+
validate :validate_options
3228

3329
def app_guid
3430
relationships&.dig(:app, :data, :guid)
@@ -46,30 +42,75 @@ def max_in_flight
4642
options&.dig(:max_in_flight) || 1
4743
end
4844

49-
def canary_steps
50-
return [] unless canary_options.present? && canary_options.is_a?(Hash)
45+
private
46+
47+
def mutually_exclusive_droplet_sources
48+
return unless revision.present? && droplet.present?
5149

52-
canary_options&.dig(:steps) || []
50+
errors.add(:droplet, "Cannot set both fields 'droplet' and 'revision'")
5351
end
5452

55-
def canary_options
56-
return {} unless options.present? && options.is_a?(Hash)
53+
def validate_options
54+
return unless options.present?
5755

58-
options&.dig(:canary) || {}
56+
unless options.is_a?(Hash)
57+
errors.add(:options, 'must be an object')
58+
return
59+
end
60+
61+
validate_max_in_flight if options[:max_in_flight]
62+
validate_canary if options[:canary]
5963
end
6064

61-
private
65+
def validate_max_in_flight
66+
max_in_flight = options[:max_in_flight]
6267

63-
def mutually_exclusive_droplet_sources
64-
return unless revision.present? && droplet.present?
68+
return unless !max_in_flight.is_a?(Integer) || max_in_flight < 1
6569

66-
errors.add(:droplet, "Cannot set both fields 'droplet' and 'revision'")
70+
errors.add(:max_in_flight, 'must be an integer greater than 0')
71+
end
72+
73+
def validate_canary
74+
canary_options = options[:canary]
75+
unless canary_options.is_a?(Hash)
76+
errors.add(:'options.canary', 'must be an object')
77+
return
78+
end
79+
80+
if canary_options && strategy != 'canary'
81+
errors.add(:'options.canary', 'are only valid for Canary deployments')
82+
return
83+
end
84+
85+
validate_steps if options[:canary][:steps]
6786
end
6887

69-
def validate_canary_options
70-
return if canary_options.blank?
88+
def validate_steps
89+
steps = options[:canary][:steps]
90+
if !steps.is_a?(Array) || steps.any? { |step| !step.is_a?(Hash) }
91+
errors.add(:'options.canary.steps', 'must be an array of objects')
92+
return
93+
end
94+
95+
steps.each do |step|
96+
disallowed_keys = step.keys - ALLOWED_STEP_KEYS
97+
98+
errors.add(:'options.canary.steps', "has unsupported key(s): #{disallowed_keys.join(', ')}") if disallowed_keys.present?
99+
end
100+
101+
errors.add(:'options.canary.steps', 'missing key: "instance_weight"') if steps.any? { |step| !step.key?(:instance_weight) }
102+
103+
if steps.any? { |step| !step[:instance_weight].is_a?(Integer) }
104+
errors.add(:'options.canary.steps.instance_weight', 'must be an Integer between 1-100 (inclusive)')
105+
return
106+
end
107+
108+
errors.add(:'options.canary.steps.instance_weight', 'must be an Integer between 1-100 (inclusive)') if steps.any? { |step| (1..100).exclude?(step[:instance_weight]) }
109+
110+
weights = steps.map { |step| step[:instance_weight] }
111+
return unless weights.sort != weights
71112

72-
errors.add(:canary_options, 'are only valid for Canary deployments') unless strategy == 'canary'
113+
errors.add(:'options.canary.steps.instance_weight', 'must be sorted in ascending order')
73114
end
74115
end
75116
end

app/messages/validators.rb

Lines changed: 11 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -262,40 +262,38 @@ def validate(record)
262262
options = record.options
263263
return unless options.present?
264264

265-
if !options.is_a?(Hash)
265+
unless options.is_a?(Hash)
266266
record.errors.add(:options, 'must be an object')
267267
return
268268
end
269-
269+
270270
max_in_flight = options[:max_in_flight]
271271

272272
# TODO: figure out if we should allow max_in_flight set to nil explicitly
273-
if options.key?(:max_in_flight) && (!max_in_flight.is_a?(Integer) || max_in_flight < 1)
274-
record.errors.add(:max_in_flight, 'must be an integer greater than 0')
275-
end
273+
record.errors.add(:max_in_flight, 'must be an integer greater than 0') if options.key?(:max_in_flight) && (!max_in_flight.is_a?(Integer) || max_in_flight < 1)
276274

277275
canary_options = options[:canary]
278276
return if canary_options.nil?
279-
280-
if !canary_options.is_a?(Hash)
281-
record.errors.add(:"options.canary", 'must be an object')
277+
278+
unless canary_options.is_a?(Hash)
279+
record.errors.add(:'options.canary', 'must be an object')
282280
return
283281
end
284282

285283
# binding.pry
286284

287285
strategy = record.strategy
288286

289-
if canary_options && strategy != "canary"
290-
record.errors.add(:"options.canary", 'are only valid for Canary deployments')
287+
if canary_options && strategy != 'canary'
288+
record.errors.add(:'options.canary', 'are only valid for Canary deployments')
291289
return
292290
end
293291

294292
steps = canary_options[:steps]
295293

296-
if steps && (!steps.is_a?(Array) || steps.any? { |step| !step.is_a?(Hash) })
297-
record.errors.add(:"options.canary.steps", 'must be an array of objects')
298-
end
294+
return unless steps && (!steps.is_a?(Array) || steps.any? { |step| !step.is_a?(Hash) })
295+
296+
record.errors.add(:'options.canary.steps', 'must be an array of objects')
299297
end
300298
end
301299

spec/lightweight_spec_helper.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
$LOAD_PATH.push(File.expand_path(File.join(__dir__, '..', 'lib')))
33

44
require 'active_support/all'
5-
5+
require 'pry'
66
# So that specs using this helper don't fail with undefined constant error
77
module VCAP
88
module CloudController

0 commit comments

Comments
 (0)