-
Notifications
You must be signed in to change notification settings - Fork 4.5k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
xds: Add RLS Cluster Specifier Plugin #5004
Conversation
// LBConfigJSON is the RLS LB Policies configuration in JSON format. | ||
// RouteLookupConfig will be a raw JSON string from the passed in proto | ||
// configuration, and the other fields will be hardcoded. | ||
type LBConfigJSON struct { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This type shouldn't need to be exported.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Switched. This was exported only for testing, now that we moved testing to same package I can unexport.
// RouteLookupConfig will be a raw JSON string from the passed in proto | ||
// configuration, and the other fields will be hardcoded. | ||
type LBConfigJSON struct { | ||
RouteLookupConfig interface{} `json:"routeLookupConfig"` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[]byte
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Switched to json.RawMessage (an alias for []byte).
if err := ptypes.UnmarshalAny(any, rlcs); err != nil { | ||
return nil, fmt.Errorf("rls_csp: error parsing config %v: %v", cfg, err) | ||
} | ||
// Do we use protojson or regular marshal function - see if Marshal produces correct results? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why wouldn't we use protojson
? Even if json.Marshal
happens to work given what's currently there, protojson
is what you're supposed to use to marshal proto into JSON and vice-versa.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Switched. I think I could only find two instances throughout the codebase of using protojson, which is why I was a touch hesitant. Even ParseConfig for the RLS LB policy itself (json raw bytestring -> RouteLookupConfig proto) in master doesn't use protojson: https://github.com/grpc/grpc-go/blob/master/balancer/rls/internal/config.go#L132.
// return nil, fmt.Errorf("error: %v", err) | ||
// } | ||
|
||
return []map[string]interface{}{{"rls_experimental": lbCfgJSON}}, nil |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
return clusterspecifier.BalancerConfig{{"rls_experimental": lbCfgJSON}}, nil
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Switched.
// Will be uncommented once RLS LB Policy completed | ||
|
||
// rawJSON, err := json.Marshal(lbCfgJSON) | ||
// if err != nil { | ||
// return nil, fmt.Errorf("rls_csp: error marshaling load balancing config %v: %v", lbCfgJSON, err) | ||
// } | ||
|
||
// _, err := rls.ParseConfig(rawJSON) (will this function need to change its logic at all?) | ||
// if err != nil { | ||
// return nil, fmt.Errorf("error: %v", err) | ||
// } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
rls.ParseConfig
exists, actually. Can we make it accessible and use it?
Also, it would be used via:
rlsBB := balancer.Get("experimental_rls")
if rlsBB == nil {
return nil, fmt.Errorf("RLS LB policy not registered")
}
rlsBB.(balancer.ConfigParser).ParseConfig(.....)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
RouteLookupConfig: rlcJSON, // "JSON form of RouteLookupClusterSpecifier.config" - RLS in xDS Design Doc | ||
ChildPolicy: []map[string]interface{}{ | ||
{ | ||
"cds_experimental": struct{}{}, // Is this right? {} in design doc |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does nil
work?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I marshaled {} into a json.RawMessage to pass RLS Config Parsing.
var rlsClusterSpecifierConfig = testutils.MarshalAny(&grpc_lookup_v1.RouteLookupClusterSpecifier{ | ||
// Once Easwar merges the RLS LB implementation and the rls csp actually | ||
// calls into it for validation, this config will have to be scaled up to | ||
// pass the validations present in the RLS LB implementation. | ||
RouteLookupConfig: &grpc_lookup_v1.RouteLookupConfig{ | ||
LookupService: "rls-specifier", | ||
}, | ||
}) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure this is the right way to test this.
The xdsclient tests shouldn't rely on any specific plugins, and the tests for any specific plugins should not be done in the xdsclient tests. Instead, the rls plugin's tests should be done in xds/internal/clusterspecifier/rls
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmmmmmm ok noted. Switched to package, and tested input/output of ParseClusterSpecifierConfig().
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actually, manually testing and comparing the output (containing long marshaled json.RawMessage) understandably led to some flakiness. I pushed the fix in a separate commit, that way you can see what I had previously, and to see if you'd like me to go back to that and figure out an unflaky way to test the raw JSON string output (the problem was marshaling the proto would sometimes pop out spaces in between fields, sometimes not).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Got to all your comments, but commit is still a WIP.
// return nil, fmt.Errorf("error: %v", err) | ||
// } | ||
|
||
return []map[string]interface{}{{"rls_experimental": lbCfgJSON}}, nil |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Switched.
if err := ptypes.UnmarshalAny(any, rlcs); err != nil { | ||
return nil, fmt.Errorf("rls_csp: error parsing config %v: %v", cfg, err) | ||
} | ||
// Do we use protojson or regular marshal function - see if Marshal produces correct results? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Switched. I think I could only find two instances throughout the codebase of using protojson, which is why I was a touch hesitant. Even ParseConfig for the RLS LB policy itself (json raw bytestring -> RouteLookupConfig proto) in master doesn't use protojson: https://github.com/grpc/grpc-go/blob/master/balancer/rls/internal/config.go#L132.
// RouteLookupConfig will be a raw JSON string from the passed in proto | ||
// configuration, and the other fields will be hardcoded. | ||
type LBConfigJSON struct { | ||
RouteLookupConfig interface{} `json:"routeLookupConfig"` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Switched to json.RawMessage (an alias for []byte).
// LBConfigJSON is the RLS LB Policies configuration in JSON format. | ||
// RouteLookupConfig will be a raw JSON string from the passed in proto | ||
// configuration, and the other fields will be hardcoded. | ||
type LBConfigJSON struct { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Switched. This was exported only for testing, now that we moved testing to same package I can unexport.
var rlsClusterSpecifierConfig = testutils.MarshalAny(&grpc_lookup_v1.RouteLookupClusterSpecifier{ | ||
// Once Easwar merges the RLS LB implementation and the rls csp actually | ||
// calls into it for validation, this config will have to be scaled up to | ||
// pass the validations present in the RLS LB implementation. | ||
RouteLookupConfig: &grpc_lookup_v1.RouteLookupConfig{ | ||
LookupService: "rls-specifier", | ||
}, | ||
}) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmmmmmm ok noted. Switched to package, and tested input/output of ParseClusterSpecifierConfig().
// Will be uncommented once RLS LB Policy completed | ||
|
||
// rawJSON, err := json.Marshal(lbCfgJSON) | ||
// if err != nil { | ||
// return nil, fmt.Errorf("rls_csp: error marshaling load balancing config %v: %v", lbCfgJSON, err) | ||
// } | ||
|
||
// _, err := rls.ParseConfig(rawJSON) (will this function need to change its logic at all?) | ||
// if err != nil { | ||
// return nil, fmt.Errorf("error: %v", err) | ||
// } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
e48fc23
to
560f642
Compare
return nil, fmt.Errorf("rls_csp: validation error from rls lb policy parsing %v", err) | ||
} | ||
|
||
return clusterspecifier.BalancerConfig{{"rls_experimental": lbCfgJSON}}, nil |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was going to suggest using an exported constant in the RLS package, but I don't think we can do that for visibility reasons without moving stuff around invasively.
Will the tests break when there is no "rls_experimental" LB policy anymore (it will eventually be renamed to "rls")? If so, then I think this is fine.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is named rls in master. I'm going to switch what gets emitted out of this function to rls: config, that way once it gets to balancer group, it will successfully do balancer.Get("rls").
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As discussed, please rename to rls_experimental
throughout.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Switched.
*/ | ||
|
||
// Package rls implements the RLS cluster specifier plugin. | ||
package rls |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should this be importing the RLS package? Seems like it should.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added import of the RLS package.
) | ||
|
||
func init() { | ||
clusterspecifier.Register(rls{}) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should this be conditional on GRPC_EXPERIMENTAL_XDS_RLS_LB
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added.
if err != nil { | ||
return nil, fmt.Errorf("rls_csp: error marshaling route lookup config: %v: %v", rlcs.GetRouteLookupConfig(), err) | ||
} | ||
emptyJSON, err := json.Marshal(struct{}{}) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Isn't this just json.RawMessage("{}")
? If so, let's use that (inline below) instead of something that can error.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Switched.
return nil, fmt.Errorf("rls_csp: error marshaling load balancing config %v: %v", lbCfgJSON, err) | ||
} | ||
|
||
rlsBB := balancer.Get("rls") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do this as a global?
Also...shouldn't this match "rls_experimental"
below? Can you use a single const for both of these?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is the name it's registered by in balancer/rls/internal. Below corresponds to what is defined in design doc. Switched the below config to "rls", as that is what is currently in master, so used one const for both.
if cs == nil { | ||
t.Fatal("Error getting cluster specifier") | ||
} | ||
_, err := cs.ParseClusterSpecifierConfig(test.rlcs) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Validate the result? (I'm assuming it would be marshaling the result to JSON and compare to a string.)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done, with a solution discussed offline.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was expecting to see something like this here now:
lbCfgJSON, err := json.Marshal(lbCfg)
// handle err
var got interface{}
err := json.Unmarshal(lbCfgJSON, &got)
// handle err
wantJSON := `
{
// JSON that we wanted
}
`
var want interface{}
err := json.Unmarshal(wantJSON, &want)
// handle err
if diff := cmp.Diff(got, want); diff != "" {
// error
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done. Although I kept the want as a struct, and then Marshaled and then Unmarshaled. I think it's cleaner.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Whoops, these were sitting in pending.
*/ | ||
|
||
// Package rls implements the RLS cluster specifier plugin. | ||
package rls |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added import of the RLS package.
) | ||
|
||
func init() { | ||
clusterspecifier.Register(rls{}) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added.
if err != nil { | ||
return nil, fmt.Errorf("rls_csp: error marshaling route lookup config: %v: %v", rlcs.GetRouteLookupConfig(), err) | ||
} | ||
emptyJSON, err := json.Marshal(struct{}{}) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Switched.
return nil, fmt.Errorf("rls_csp: error marshaling load balancing config %v: %v", lbCfgJSON, err) | ||
} | ||
|
||
rlsBB := balancer.Get("rls") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is the name it's registered by in balancer/rls/internal. Below corresponds to what is defined in design doc. Switched the below config to "rls", as that is what is currently in master, so used one const for both.
return nil, fmt.Errorf("rls_csp: validation error from rls lb policy parsing %v", err) | ||
} | ||
|
||
return clusterspecifier.BalancerConfig{{"rls_experimental": lbCfgJSON}}, nil |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is named rls in master. I'm going to switch what gets emitted out of this function to rls: config, that way once it gets to balancer group, it will successfully do balancer.Get("rls").
if cs == nil { | ||
t.Fatal("Error getting cluster specifier") | ||
} | ||
_, err := cs.ParseClusterSpecifierConfig(test.rlcs) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done, with a solution discussed offline.
balancer/rls/internal/builder.go
Outdated
@@ -24,7 +24,7 @@ import ( | |||
"google.golang.org/grpc/internal/grpcsync" | |||
) | |||
|
|||
const rlsBalancerName = "rls" | |||
const rlsBalancerName = "rls_experimental" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks like this was changed on master; hopefully rebasing will go smoothly.
if !cmp.Equal(want, got, cmpopts.EquateEmpty()) { | ||
t.Fatalf("ParseClusterSpecifierConfig(%+v) returned expected, diff (-want +got):\\n%s", test.rlcs, cmp.Diff(want, got, cmpopts.EquateEmpty())) | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Use if diff := cmp.Diff(want, got, cmpopts.EquateEmpty()); diff != "" {
and then there's no need to call Diff
in the error message.
7dbaec0
to
4a1b633
Compare
This PR adds the RLS Cluster Specifier Plugin. This is branched off #4987. The RLS Cluster Specifier Plugin will not be functional until the RLS LB Policy itself gets completed, as right now the call into the RLS LB Policies ParseConfig() for validation purposes is commented out.
RELEASE NOTES: None