Skip to content

Commit 512e894

Browse files
authored
rls: support extra_keys and constant_keys (#4995)
* rls: support extra_keys and constant_keys * review comments * use the constant_keys map from the proto
1 parent f3bbd12 commit 512e894

File tree

3 files changed

+280
-131
lines changed

3 files changed

+280
-131
lines changed

balancer/rls/internal/keys/builder.go

Lines changed: 75 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -29,25 +29,11 @@ import (
2929
"google.golang.org/grpc/metadata"
3030
)
3131

32-
// BuilderMap provides a mapping from a request path to the key builder to be
33-
// used for that path.
34-
// The BuilderMap is constructed by parsing the RouteLookupConfig received by
35-
// the RLS balancer as part of its ServiceConfig, and is used by the picker in
36-
// the data path to build the RLS keys to be used for a given request.
32+
// BuilderMap maps from request path to the key builder for that path.
3733
type BuilderMap map[string]builder
3834

3935
// MakeBuilderMap parses the provided RouteLookupConfig proto and returns a map
4036
// from paths to key builders.
41-
//
42-
// The following conditions are validated, and an error is returned if any of
43-
// them is not met:
44-
// grpc_keybuilders field
45-
// * must have at least one entry
46-
// * must not have two entries with the same Name
47-
// * must not have any entry with a Name with the service field unset or empty
48-
// * must not have any entries without a Name
49-
// * must not have a headers entry that has required_match set
50-
// * must not have two headers entries with the same key within one entry
5137
func MakeBuilderMap(cfg *rlspb.RouteLookupConfig) (BuilderMap, error) {
5238
kbs := cfg.GetGrpcKeybuilders()
5339
if len(kbs) == 0 {
@@ -56,21 +42,46 @@ func MakeBuilderMap(cfg *rlspb.RouteLookupConfig) (BuilderMap, error) {
5642

5743
bm := make(map[string]builder)
5844
for _, kb := range kbs {
45+
// Extract keys from `headers`, `constant_keys` and `extra_keys` fields
46+
// and populate appropriate values in the builder struct. Also ensure
47+
// that keys are not repeated.
5948
var matchers []matcher
6049
seenKeys := make(map[string]bool)
50+
constantKeys := kb.GetConstantKeys()
51+
for k := range kb.GetConstantKeys() {
52+
seenKeys[k] = true
53+
}
6154
for _, h := range kb.GetHeaders() {
6255
if h.GetRequiredMatch() {
6356
return nil, fmt.Errorf("rls: GrpcKeyBuilder in RouteLookupConfig has required_match field set {%+v}", kbs)
6457
}
6558
key := h.GetKey()
6659
if seenKeys[key] {
67-
return nil, fmt.Errorf("rls: GrpcKeyBuilder in RouteLookupConfig contains repeated Key field in headers {%+v}", kbs)
60+
return nil, fmt.Errorf("rls: GrpcKeyBuilder in RouteLookupConfig contains repeated key %q across headers, constant_keys and extra_keys {%+v}", key, kbs)
6861
}
6962
seenKeys[key] = true
7063
matchers = append(matchers, matcher{key: h.GetKey(), names: h.GetNames()})
7164
}
72-
b := builder{matchers: matchers}
65+
if seenKeys[kb.GetExtraKeys().GetHost()] {
66+
return nil, fmt.Errorf("rls: GrpcKeyBuilder in RouteLookupConfig contains repeated key %q in extra_keys from constant_keys or headers {%+v}", kb.GetExtraKeys().GetHost(), kbs)
67+
}
68+
if seenKeys[kb.GetExtraKeys().GetService()] {
69+
return nil, fmt.Errorf("rls: GrpcKeyBuilder in RouteLookupConfig contains repeated key %q in extra_keys from constant_keys or headers {%+v}", kb.GetExtraKeys().GetService(), kbs)
70+
}
71+
if seenKeys[kb.GetExtraKeys().GetMethod()] {
72+
return nil, fmt.Errorf("rls: GrpcKeyBuilder in RouteLookupConfig contains repeated key %q in extra_keys from constant_keys or headers {%+v}", kb.GetExtraKeys().GetMethod(), kbs)
73+
}
74+
b := builder{
75+
headerKeys: matchers,
76+
constantKeys: constantKeys,
77+
hostKey: kb.GetExtraKeys().GetHost(),
78+
serviceKey: kb.GetExtraKeys().GetService(),
79+
methodKey: kb.GetExtraKeys().GetMethod(),
80+
}
7381

82+
// Store the builder created above in the BuilderMap based on the value
83+
// of the `Names` field, which wraps incoming request's service and
84+
// method. Also, ensure that there are no repeated `Names` field.
7485
names := kb.GetNames()
7586
if len(names) == 0 {
7687
return nil, fmt.Errorf("rls: GrpcKeyBuilder in RouteLookupConfig does not contain any Name {%+v}", kbs)
@@ -108,16 +119,31 @@ type KeyMap struct {
108119

109120
// RLSKey builds the RLS keys to be used for the given request, identified by
110121
// the request path and the request headers stored in metadata.
111-
func (bm BuilderMap) RLSKey(md metadata.MD, path string) KeyMap {
122+
func (bm BuilderMap) RLSKey(md metadata.MD, host, path string) KeyMap {
123+
i := strings.LastIndex(path, "/")
124+
service, method := path[:i+1], path[i+1:]
112125
b, ok := bm[path]
113126
if !ok {
114-
i := strings.LastIndex(path, "/")
115-
b, ok = bm[path[:i+1]]
127+
b, ok = bm[service]
116128
if !ok {
117129
return KeyMap{}
118130
}
119131
}
120-
return b.keys(md)
132+
133+
kvMap := b.buildHeaderKeys(md)
134+
if b.hostKey != "" {
135+
kvMap[b.hostKey] = host
136+
}
137+
if b.serviceKey != "" {
138+
kvMap[b.serviceKey] = service
139+
}
140+
if b.methodKey != "" {
141+
kvMap[b.methodKey] = method
142+
}
143+
for k, v := range b.constantKeys {
144+
kvMap[k] = v
145+
}
146+
return KeyMap{Map: kvMap, Str: mapToString(kvMap)}
121147
}
122148

123149
// Equal reports whether bm and am represent equivalent BuilderMaps.
@@ -141,40 +167,43 @@ func (bm BuilderMap) Equal(am BuilderMap) bool {
141167
return true
142168
}
143169

144-
// builder provides the actual functionality of building RLS keys. These are
145-
// stored in the BuilderMap.
146-
// While processing a pick, the picker looks in the BuilderMap for the
147-
// appropriate builder to be used for the given RPC. For each of the matchers
148-
// in the found builder, we iterate over the list of request headers (available
149-
// as metadata in the context). Once a header matches one of the names in the
150-
// matcher, we set the value of the header in the keyMap (with the key being
151-
// the one found in the matcher) and move on to the next matcher. If no
152-
// KeyBuilder was found in the map, or no header match was found, an empty
153-
// keyMap is returned.
170+
// builder provides the actual functionality of building RLS keys.
154171
type builder struct {
155-
matchers []matcher
172+
headerKeys []matcher
173+
constantKeys map[string]string
174+
// The following keys mirror corresponding fields in `extra_keys`.
175+
hostKey string
176+
serviceKey string
177+
methodKey string
156178
}
157179

158180
// Equal reports whether b and a represent equivalent key builders.
159181
func (b builder) Equal(a builder) bool {
160-
if (b.matchers == nil) != (a.matchers == nil) {
161-
return false
162-
}
163-
if len(b.matchers) != len(a.matchers) {
182+
if len(b.headerKeys) != len(a.headerKeys) {
164183
return false
165184
}
166185
// Protobuf serialization maintains the order of repeated fields. Matchers
167186
// are specified as a repeated field inside the KeyBuilder proto. If the
168187
// order changes, it means that the order in the protobuf changed. We report
169188
// this case as not being equal even though the builders could possible be
170189
// functionally equal.
171-
for i, bMatcher := range b.matchers {
172-
aMatcher := a.matchers[i]
190+
for i, bMatcher := range b.headerKeys {
191+
aMatcher := a.headerKeys[i]
173192
if !bMatcher.Equal(aMatcher) {
174193
return false
175194
}
176195
}
177-
return true
196+
197+
if len(b.constantKeys) != len(a.constantKeys) {
198+
return false
199+
}
200+
for k, v := range b.constantKeys {
201+
if a.constantKeys[k] != v {
202+
return false
203+
}
204+
}
205+
206+
return b.hostKey == a.hostKey && b.serviceKey == a.serviceKey && b.methodKey == a.methodKey
178207
}
179208

180209
// matcher helps extract a key from request headers based on a given name.
@@ -185,14 +214,11 @@ type matcher struct {
185214
names []string
186215
}
187216

188-
// Equal reports if m and are are equivalent matchers.
217+
// Equal reports if m and are are equivalent headerKeys.
189218
func (m matcher) Equal(a matcher) bool {
190219
if m.key != a.key {
191220
return false
192221
}
193-
if (m.names == nil) != (a.names == nil) {
194-
return false
195-
}
196222
if len(m.names) != len(a.names) {
197223
return false
198224
}
@@ -204,17 +230,20 @@ func (m matcher) Equal(a matcher) bool {
204230
return true
205231
}
206232

207-
func (b builder) keys(md metadata.MD) KeyMap {
233+
func (b builder) buildHeaderKeys(md metadata.MD) map[string]string {
208234
kvMap := make(map[string]string)
209-
for _, m := range b.matchers {
235+
if len(md) == 0 {
236+
return kvMap
237+
}
238+
for _, m := range b.headerKeys {
210239
for _, name := range m.names {
211240
if vals := md.Get(name); vals != nil {
212241
kvMap[m.key] = strings.Join(vals, ",")
213242
break
214243
}
215244
}
216245
}
217-
return KeyMap{Map: kvMap, Str: mapToString(kvMap)}
246+
return kvMap
218247
}
219248

220249
func mapToString(kv map[string]string) string {

0 commit comments

Comments
 (0)