Skip to content
This repository was archived by the owner on Jun 2, 2021. It is now read-only.

Commit 534b848

Browse files
v3(services): filter route bindings by labels
[#174252446](https://www.pivotaltracker.com/story/show/174252446)
1 parent 76add1c commit 534b848

6 files changed

Lines changed: 84 additions & 36 deletions

File tree

app/fetchers/route_binding_list_fetcher.rb

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,14 @@ def filter(message, bindings)
3939
end,
4040
route_guids: ->(dataset, message) do
4141
dataset.where { Sequel[:routes][:guid] =~ message.route_guids }
42+
end,
43+
label_selector: ->(dataset, message) do
44+
LabelSelectorQueryGenerator.add_selector_queries(
45+
label_klass: RouteBindingLabelModel,
46+
resource_dataset: dataset,
47+
requirements: message.requirements,
48+
resource_klass: RouteBinding
49+
)
4250
end
4351
}.freeze
4452
end

app/messages/service_route_bindings_list_message.rb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
1-
require 'messages/list_message'
1+
require 'messages/metadata_list_message'
22

33
module VCAP
44
module CloudController
5-
class ServiceRouteBindingsListMessage < ListMessage
5+
class ServiceRouteBindingsListMessage < MetadataListMessage
66
QUERY_PARAMS = %w[
77
service_instance_guids
88
service_instance_names

spec/request/service_route_bindings_spec.rb

Lines changed: 30 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -86,9 +86,7 @@
8686

8787
expect(last_response).to have_status_code(200)
8888

89-
expected_route_binding_guids = filtered_route_bindings.map(&:guid)
90-
route_binding_guids = parsed_response['resources'].map { |x| x['guid'] }
91-
expect(route_binding_guids).to match_array(expected_route_binding_guids)
89+
expect_route_bindings(filtered_route_bindings)
9290
end
9391

9492
it 'can be filtered by service instance names' do
@@ -103,9 +101,7 @@
103101

104102
expect(last_response).to have_status_code(200)
105103

106-
expected_route_binding_guids = filtered_route_bindings.map(&:guid)
107-
route_binding_guids = parsed_response['resources'].map { |x| x['guid'] }
108-
expect(route_binding_guids).to match_array(expected_route_binding_guids)
104+
expect_route_bindings(filtered_route_bindings)
109105
end
110106

111107
it 'can be filtered by route guids' do
@@ -120,9 +116,29 @@
120116

121117
expect(last_response).to have_status_code(200)
122118

123-
expected_route_binding_guids = filtered_route_bindings.map(&:guid)
124-
route_binding_guids = parsed_response['resources'].map { |x| x['guid'] }
125-
expect(route_binding_guids).to match_array(expected_route_binding_guids)
119+
expect_route_bindings(filtered_route_bindings)
120+
end
121+
122+
it 'filters by label' do
123+
rb1 = VCAP::CloudController::RouteBinding.make
124+
rb2 = VCAP::CloudController::RouteBinding.make
125+
rb3 = VCAP::CloudController::RouteBinding.make
126+
rb4 = VCAP::CloudController::RouteBinding.make
127+
rb5 = VCAP::CloudController::RouteBinding.make
128+
filtered_route_bindings = [rb2, rb3]
129+
VCAP::CloudController::RouteBindingLabelModel.make(key_name: 'fruit', value: 'strawberry', route_binding: rb1)
130+
VCAP::CloudController::RouteBindingLabelModel.make(key_name: 'animal', value: 'horse', route_binding: rb1)
131+
VCAP::CloudController::RouteBindingLabelModel.make(key_name: 'env', value: 'prod', route_binding: rb2)
132+
VCAP::CloudController::RouteBindingLabelModel.make(key_name: 'animal', value: 'dog', route_binding: rb2)
133+
VCAP::CloudController::RouteBindingLabelModel.make(key_name: 'env', value: 'prod', route_binding: rb3)
134+
VCAP::CloudController::RouteBindingLabelModel.make(key_name: 'animal', value: 'horse', route_binding: rb3)
135+
VCAP::CloudController::RouteBindingLabelModel.make(key_name: 'env', value: 'prod', route_binding: rb4)
136+
VCAP::CloudController::RouteBindingLabelModel.make(key_name: 'env', value: 'staging', route_binding: rb5)
137+
VCAP::CloudController::RouteBindingLabelModel.make(key_name: 'animal', value: 'dog', route_binding: rb5)
138+
139+
get '/v3/service_route_bindings?label_selector=!fruit,env=prod,animal in (dog,horse)', nil, admin_headers
140+
141+
expect_route_bindings(filtered_route_bindings)
126142
end
127143
end
128144

@@ -1607,4 +1623,9 @@ def bind_service_to_route(service_instance, route)
16071623
{ type: 'create', state: 'successful' }
16081624
)
16091625
end
1626+
1627+
def expect_route_bindings(route_bindings)
1628+
response_guids = parsed_response['resources'].map { |x| x['guid'] }
1629+
expect(response_guids).to match_array(route_bindings.map(&:guid))
1630+
end
16101631
end

spec/support/fakes/blueprints.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@
3232

3333
module VCAP::CloudController
3434
%w/App Build Buildpack Deployment Domain Droplet IsolationSegment Organization Package
35-
Process Revision Route ServiceInstance ServiceOffering ServiceBroker Space Stack
35+
Process Revision Route RouteBinding ServiceInstance ServiceOffering ServiceBroker Space Stack
3636
ServicePlan Task User/.each do |root|
3737
"VCAP::CloudController::#{root}LabelModel".constantize.blueprint do end
3838
"VCAP::CloudController::#{root}AnnotationModel".constantize.blueprint do end

spec/unit/fetchers/route_binding_list_fetcher_spec.rb

Lines changed: 30 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,7 @@ module CloudController
1313
ServiceRouteBindingsListMessage.from_params({})
1414
)
1515

16-
fetched_route_binding_guids = fetched_route_bindings.map(&:guid)
17-
expected_route_binding_guids = route_bindings.map(&:guid)
18-
expect(fetched_route_binding_guids).to match_array(expected_route_binding_guids)
16+
expect_binding_guids(fetched_route_bindings, route_bindings)
1917
end
2018

2119
it 'can be filtered by service_instance_guids' do
@@ -26,9 +24,7 @@ module CloudController
2624
ServiceRouteBindingsListMessage.from_params({ 'service_instance_guids' => service_instance_guids })
2725
)
2826

29-
fetched_route_binding_guids = fetched_route_bindings.map(&:guid)
30-
expected_binding_guids = filtered_route_bindings.map(&:guid)
31-
expect(fetched_route_binding_guids).to match_array(expected_binding_guids)
27+
expect_binding_guids(fetched_route_bindings, filtered_route_bindings)
3228
end
3329

3430
it 'can be filtered by service_instance_names' do
@@ -39,9 +35,7 @@ module CloudController
3935
ServiceRouteBindingsListMessage.from_params({ 'service_instance_names' => service_instance_names })
4036
)
4137

42-
fetched_route_binding_guids = fetched_route_bindings.map(&:guid)
43-
expected_binding_guids = filtered_route_bindings.map(&:guid)
44-
expect(fetched_route_binding_guids).to match_array(expected_binding_guids)
38+
expect_binding_guids(fetched_route_bindings, filtered_route_bindings)
4539
end
4640

4741
it 'can be filtered by route_guids' do
@@ -52,9 +46,25 @@ module CloudController
5246
ServiceRouteBindingsListMessage.from_params({ 'route_guids' => route_guids })
5347
)
5448

55-
fetched_route_binding_guids = fetched_route_bindings.map(&:guid)
56-
expected_binding_guids = filtered_route_bindings.map(&:guid)
57-
expect(fetched_route_binding_guids).to match_array(expected_binding_guids)
49+
expect_binding_guids(fetched_route_bindings, filtered_route_bindings)
50+
end
51+
52+
context 'can be filtered by label selector' do
53+
before do
54+
RouteBindingLabelModel.make(key_name: 'fruit', value: 'strawberry', route_binding: route_bindings[0])
55+
RouteBindingLabelModel.make(key_name: 'fruit', value: 'strawberry', route_binding: route_bindings[1])
56+
RouteBindingLabelModel.make(key_name: 'fruit', value: 'lemon', route_binding: route_bindings[2])
57+
end
58+
59+
it 'returns instances with matching labels' do
60+
filtered_route_bindings = route_bindings[0..1]
61+
62+
fetched_route_bindings = fetcher.fetch_all(
63+
ServiceRouteBindingsListMessage.from_params({ 'label_selector' => 'fruit=strawberry' })
64+
)
65+
66+
expect_binding_guids(fetched_route_bindings, filtered_route_bindings)
67+
end
5868
end
5969
end
6070

@@ -74,9 +84,7 @@ module CloudController
7484
space_guids: [target_space.guid]
7585
)
7686

77-
fetched_route_binding_guids = fetched_route_bindings.map(&:guid)
78-
target_space_route_binding_guids = route_bindings_in_target_space.map(&:guid)
79-
expect(fetched_route_binding_guids).to match_array(target_space_route_binding_guids)
87+
expect_binding_guids(fetched_route_bindings, route_bindings_in_target_space)
8088
end
8189

8290
it 'can be filtered by service_instance_guids' do
@@ -93,9 +101,7 @@ module CloudController
93101
space_guids: [target_space.guid]
94102
)
95103

96-
fetched_route_binding_guids = fetched_route_bindings.map(&:guid)
97-
expected_binding_guids = filtered_route_bindings.map(&:guid)
98-
expect(fetched_route_binding_guids).to match_array(expected_binding_guids)
104+
expect_binding_guids(fetched_route_bindings, filtered_route_bindings)
99105
end
100106

101107
it 'can be filtered by service_instance_names' do
@@ -112,9 +118,7 @@ module CloudController
112118
space_guids: [target_space.guid]
113119
)
114120

115-
fetched_route_binding_guids = fetched_route_bindings.map(&:guid)
116-
expected_binding_guids = filtered_route_bindings.map(&:guid)
117-
expect(fetched_route_binding_guids).to match_array(expected_binding_guids)
121+
expect_binding_guids(fetched_route_bindings, filtered_route_bindings)
118122
end
119123

120124
it 'can be filtered by route_guids' do
@@ -131,15 +135,17 @@ module CloudController
131135
space_guids: [target_space.guid]
132136
)
133137

134-
fetched_route_binding_guids = fetched_route_bindings.map(&:guid)
135-
expected_binding_guids = filtered_route_bindings.map(&:guid)
136-
expect(fetched_route_binding_guids).to match_array(expected_binding_guids)
138+
expect_binding_guids(fetched_route_bindings, filtered_route_bindings)
137139
end
138140

139141
def make_other_route_bindings
140142
Array.new(3) { RouteBinding.make }
141143
end
142144
end
145+
146+
def expect_binding_guids(fetched_route_bindings, expected_route_bindings)
147+
expect(fetched_route_bindings.map(&:guid)).to match_array(expected_route_bindings.map(&:guid))
148+
end
143149
end
144150
end
145151
end

spec/unit/messages/service_route_bindings_list_message_spec.rb

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ module CloudController
1212
'service_instance_guids' => 'guid-1,guid-2,guid-3',
1313
'service_instance_names' => 'name-1,name-2,name-3',
1414
'route_guids' => 'guid-4,guid-5,guid-6',
15+
'label_selector' => 'key=value',
1516
'include' => 'service_instance'
1617
}
1718
end
@@ -27,6 +28,7 @@ module CloudController
2728
expect(message.service_instance_names).to eq(%w[name-1 name-2 name-3])
2829
expect(message.route_guids).to eq(%w[guid-4 guid-5 guid-6])
2930
expect(message.include).to eq(%w[service_instance])
31+
expect(message.label_selector).to eq('key=value')
3032
end
3133

3234
it 'converts requested keys to symbols' do
@@ -35,6 +37,7 @@ module CloudController
3537
expect(message.requested?(:service_instance_guids)).to be_truthy
3638
expect(message.requested?(:service_instance_names)).to be_truthy
3739
expect(message.requested?(:route_guids)).to be_truthy
40+
expect(message.requested?(:label_selector)).to be_truthy
3841
end
3942
end
4043

@@ -51,6 +54,16 @@ module CloudController
5154
expect(message).to be_valid
5255
end
5356
end
57+
58+
it 'validates metadata requirements' do
59+
message = described_class.from_params({ 'label_selector' => '' }.with_indifferent_access)
60+
61+
expect_any_instance_of(Validators::LabelSelectorRequirementValidator).
62+
to receive(:validate).
63+
with(message).
64+
and_call_original
65+
message.valid?
66+
end
5467
end
5568
end
5669
end

0 commit comments

Comments
 (0)