Skip to content

Commit 05041d7

Browse files
committed
Disallow null in manifest; Cleanup error outputs
1 parent 2d953f8 commit 05041d7

File tree

12 files changed

+48
-42
lines changed

12 files changed

+48
-42
lines changed

app/actions/route_update.rb

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,7 @@ module VCAP::CloudController
22
class RouteUpdate
33
def update(route:, message:)
44
Route.db.transaction do
5-
if message.requested?(:options)
6-
route.options = if message.options.nil?
7-
{}
8-
else
9-
route.options.symbolize_keys.merge(message.options).compact
10-
end
11-
end
5+
route.options = route.options.symbolize_keys.merge(message.options).compact if message.requested?(:options)
126

137
route.save
148
MetadataUpdate.update(route, message)

app/messages/manifest_routes_update_message.rb

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -50,11 +50,12 @@ def route_options_are_valid
5050
return if errors[:routes].present?
5151

5252
routes.any? do |r|
53-
next unless r[:options]
53+
next unless r.keys.include?(:options)
5454

55-
return true unless r[:options].is_a?(Hash)
56-
57-
return false if r[:options].empty?
55+
unless r[:options].is_a?(Hash)
56+
errors.add(:base, message: "Route '#{r[:route]}': options must be an object")
57+
next
58+
end
5859

5960
r[:options].each_key do |key|
6061
RouteOptionsMessage::VALID_MANIFEST_ROUTE_OPTIONS.exclude?(key) &&
@@ -69,13 +70,19 @@ def loadbalancings_are_valid
6970
return if errors[:routes].present?
7071

7172
routes.each do |r|
72-
next unless r[:options] && r[:options][:'loadbalancing']
73+
next unless r.keys.include?(:options) && r[:options].is_a?(Hash) && r[:options].keys.include?(:loadbalancing)
7374

74-
loadbalancing = r[:options][:'loadbalancing']
75+
loadbalancing = r[:options][:loadbalancing]
76+
unless loadbalancing.is_a?(String)
77+
errors.add(:base,
78+
message: "Invalid value for 'loadbalancing' for Route '#{r[:route]}'; \
79+
Valid values are: '#{RouteOptionsMessage::VALID_LOADBALANCING_ALGORITHMS.join(', ')}'")
80+
next
81+
end
7582
RouteOptionsMessage::VALID_LOADBALANCING_ALGORITHMS.exclude?(loadbalancing) &&
7683
errors.add(:base,
77-
message: "Route '#{r[:route]}' contains invalid load-balancing algorithm '#{loadbalancing}'. \
78-
Valid algorithms: '#{RouteOptionsMessage::VALID_LOADBALANCING_ALGORITHMS.join(', ')}'")
84+
message: "Cannot use loadbalancing value '#{loadbalancing}' for Route '#{r[:route]}'; \
85+
Valid values are: '#{RouteOptionsMessage::VALID_LOADBALANCING_ALGORITHMS.join(', ')}'")
7986
end
8087
end
8188

app/messages/route_options_message.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ class RouteOptionsMessage < BaseMessage
99
register_allowed_keys VALID_ROUTE_OPTIONS
1010
validates_with NoAdditionalKeysValidator
1111
validates :loadbalancing,
12-
inclusion: { in: VALID_LOADBALANCING_ALGORITHMS, message: "'%<value>s' is not supported" },
12+
inclusion: { in: VALID_LOADBALANCING_ALGORITHMS, message: "must be one of '#{RouteOptionsMessage::VALID_LOADBALANCING_ALGORITHMS.join(', ')}' if present" },
1313
presence: true,
1414
allow_nil: true
1515
end

app/presenters/v3/app_manifest_presenters/route_properties_presenter.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ def to_hash(route_mappings:, app:, **_)
1212

1313
if route_mapping.route.options
1414
route_hash[:options] = {}
15-
route_hash[:options][:'loadbalancing'] = route_mapping.route.options[:loadbalancing] if route_mapping.route.options[:loadbalancing]
15+
route_hash[:options][:loadbalancing] = route_mapping.route.options[:loadbalancing] if route_mapping.route.options[:loadbalancing]
1616
end
1717

1818
route_hash

docs/v3/source/includes/resources/routes/_route_options_object.md.erb

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,6 @@ Example Route-Options object
77
<%= yield_content :route_options %>
88
```
99

10-
| Name | Type | Description |
11-
|-------------|----------|----------------------------------------------------------------------------------------------------------|
12-
| **loadbalancing** | _string_ | The load balancer associated with this route. Valid algorithms are 'round-robin' and 'least-connections' |
10+
| Name | Type | Description |
11+
|-------------------|----------|------------------------------------------------------------------------------------------------------|
12+
| **loadbalancing** | _string_ | The load-balancer associated with this route. Valid values are 'round-robin' and 'least-connections' |

lib/cloud_controller/app_manifest/manifest_route.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ def self.parse(route, options=nil)
1717

1818
attrs[:full_route] = route
1919
attrs[:options] = {}
20-
attrs[:options][:loadbalancing] = options[:'loadbalancing'] if options && options.key?(:'loadbalancing')
20+
attrs[:options][:loadbalancing] = options[:loadbalancing] if options && options.key?(:loadbalancing)
2121

2222
ManifestRoute.new(attrs)
2323
end

spec/request/space_manifests_spec.rb

Lines changed: 17 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -658,7 +658,7 @@
658658
'applications' => [
659659
{ 'name' => app1_model.name,
660660
'routes' => [
661-
{ 'route' => "https://round-robin-app.#{shared_domain.name}",
661+
{ 'route' => "https://#{route.host}.#{shared_domain.name}",
662662
'options' => {
663663
'loadbalancing' => 'round-robin'
664664
} }
@@ -683,7 +683,7 @@
683683
'applications' => [
684684
{ 'name' => app1_model.name,
685685
'routes' => [
686-
{ 'route' => "https://round-robin-app.#{shared_domain.name}",
686+
{ 'route' => "https://#{route.host}.#{shared_domain.name}",
687687
'options' => {
688688
'loadbalancing' => 'least-connections'
689689
} }
@@ -707,7 +707,7 @@
707707
'applications' => [
708708
{ 'name' => app1_model.name,
709709
'routes' => [
710-
{ 'route' => "https://round-robin-app.#{shared_domain.name}" }
710+
{ 'route' => "https://#{route.host}.#{shared_domain.name}" }
711711
] }
712712
]
713713
}.to_yaml
@@ -724,12 +724,12 @@
724724
expect(app1_model.routes.first.options).to eq({ 'loadbalancing' => 'round-robin' })
725725
end
726726

727-
it 'does not modify any route options options: nil is provided' do
727+
it 'returns 422 when options: null is provided' do
728728
yml_manifest = {
729729
'applications' => [
730730
{ 'name' => app1_model.name,
731731
'routes' => [
732-
{ 'route' => "https://round-robin-app.#{shared_domain.name}",
732+
{ 'route' => "https://#{route.host}.#{shared_domain.name}",
733733
'options' => nil }
734734
] }
735735
]
@@ -738,9 +738,11 @@
738738
# apply the manifest with the route option
739739
post "/v3/spaces/#{space.guid}/actions/apply_manifest", yml_manifest, yml_headers(user_header)
740740

741-
expect(last_response.status).to eq(202)
742-
job_guid = VCAP::CloudController::PollableJobModel.last.guid
741+
expect(last_response.status).to eq(422)
742+
expect(last_response).to have_error_message("For application '#{app1_model.name}': \
743+
Route 'https://#{route.host}.#{route.domain.name}': options must be an object")
743744

745+
job_guid = VCAP::CloudController::PollableJobModel.last.guid
744746
Delayed::Worker.new.work_off
745747
expect(VCAP::CloudController::PollableJobModel.find(guid: job_guid)).to be_complete, VCAP::CloudController::PollableJobModel.find(guid: job_guid).cf_api_error
746748
app1_model.reload
@@ -752,7 +754,7 @@
752754
'applications' => [
753755
{ 'name' => app1_model.name,
754756
'routes' => [
755-
{ 'route' => "https://round-robin-app.#{shared_domain.name}",
757+
{ 'route' => "https://#{route.host}.#{shared_domain.name}",
756758
'options' => {} }
757759
] }
758760
]
@@ -767,12 +769,12 @@
767769
expect(app1_model.routes.first.options).to eq({ 'loadbalancing' => 'round-robin' })
768770
end
769771

770-
it 'does not modify any option when options: { key: nil } is provided' do
772+
it 'returns 422 when { loadbalancing: null } is provided' do
771773
yml_manifest = {
772774
'applications' => [
773775
{ 'name' => app1_model.name,
774776
'routes' => [
775-
{ 'route' => "https://round-robin-app.#{shared_domain.name}",
777+
{ 'route' => "https://#{route.host}.#{shared_domain.name}",
776778
'options' => {
777779
'loadbalancing' => nil
778780
} }
@@ -783,7 +785,9 @@
783785
# apply the manifest with the route option
784786
post "/v3/spaces/#{space.guid}/actions/apply_manifest", yml_manifest, yml_headers(user_header)
785787

786-
expect(last_response.status).to eq(202)
788+
expect(last_response.status).to eq(422)
789+
expect(last_response).to have_error_message("For application '#{app1_model.name}': \
790+
Invalid value for 'loadbalancing' for Route 'https://#{route.host}.#{route.domain.name}'; Valid values are: 'round-robin, least-connections'")
787791

788792
app1_model.reload
789793
expect(app1_model.routes.first.options).to eq({ 'loadbalancing' => 'round-robin' })
@@ -813,7 +817,7 @@
813817

814818
expect(last_response).to have_status_code(422)
815819
expect(last_response).to have_error_message("For application '#{app1_model.name}': \
816-
Route 'https://#{route.host}.#{route.domain.name}' contains invalid load-balancing algorithm 'unsupported-lb-algorithm'. Valid algorithms: 'round-robin, least-connections'")
820+
Cannot use loadbalancing value 'unsupported-lb-algorithm' for Route 'https://#{route.host}.#{route.domain.name}'; Valid values are: 'round-robin, least-connections'")
817821
end
818822
end
819823

@@ -823,7 +827,7 @@
823827
'applications' => [
824828
{ 'name' => app1_model.name,
825829
'routes' => [
826-
{ 'route' => "https://round-robin-app.#{shared_domain.name}",
830+
{ 'route' => "https://#{route.host}.#{shared_domain.name}",
827831
'options' => {
828832
'loadbalancing' => 'round-robin'
829833
} }

spec/unit/lib/cloud_controller/app_manifest/manifest_route_spec.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -176,7 +176,7 @@ module VCAP::CloudController
176176
end
177177

178178
it 'parses a route with a loadbalancing into route components' do
179-
route = ManifestRoute.parse('http://potato.sub.some-domain.com', { 'loadbalancing': 'round-robin' })
179+
route = ManifestRoute.parse('http://potato.sub.some-domain.com', { loadbalancing: 'round-robin' })
180180

181181
expect(route.to_hash).to eq({
182182
candidate_host_domain_pairs: [

spec/unit/messages/manifest_routes_update_message_spec.rb

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -237,7 +237,8 @@ module VCAP::CloudController
237237
it 'returns true' do
238238
msg = ManifestRoutesUpdateMessage.new(body)
239239

240-
expect(msg.valid?).to be(true)
240+
expect(msg.valid?).to be(false)
241+
expect(msg.errors.full_messages).to include("Route 'existing.example.com': options must be an object")
241242
end
242243
end
243244

@@ -291,7 +292,8 @@ module VCAP::CloudController
291292
it 'returns true' do
292293
msg = ManifestRoutesUpdateMessage.new(body)
293294

294-
expect(msg.valid?).to be(true)
295+
expect(msg.valid?).to be(false)
296+
expect(msg.errors.full_messages).to include("Invalid value for 'loadbalancing' for Route 'existing.example.com'; Valid values are: 'round-robin, least-connections'")
295297
end
296298
end
297299

@@ -311,8 +313,7 @@ module VCAP::CloudController
311313

312314
expect(msg.valid?).to be(false)
313315
expect(msg.errors.errors.length).to eq(1)
314-
expect(msg.errors.full_messages).to include("Route 'existing.example.com' contains invalid load-balancing algorithm 'sushi'. \
315-
Valid algorithms: 'round-robin, least-connections'")
316+
expect(msg.errors.full_messages).to include("Cannot use loadbalancing value 'sushi' for Route 'existing.example.com'; Valid values are: 'round-robin, least-connections'")
316317
end
317318
end
318319
end

spec/unit/messages/route_create_message_spec.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -492,7 +492,7 @@ module VCAP::CloudController
492492

493493
it 'is not valid' do
494494
expect(subject).not_to be_valid
495-
expect(subject.errors[:options]).to include("Loadbalancing 'random' is not supported")
495+
expect(subject.errors[:options]).to include("Loadbalancing must be one of 'round-robin, least-connections' if present")
496496
end
497497
end
498498
end

0 commit comments

Comments
 (0)