Skip to content

Commit

Permalink
add crds for prioritize by locality (#2357)
Browse files Browse the repository at this point in the history
  • Loading branch information
erichaberkorn authored Jun 15, 2023
1 parent 9121afc commit 3ce3302
Show file tree
Hide file tree
Showing 5 changed files with 99 additions and 9 deletions.
3 changes: 3 additions & 0 deletions .changelog/2357.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
```release-note:feature
Add the `PrioritizeByLocality` field to the `ServiceResolver` CRD.
```
9 changes: 9 additions & 0 deletions charts/consul/templates/crd-serviceresolvers.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -227,6 +227,15 @@ spec:
type: integer
type: object
type: object
prioritizeByLocality:
description: PrioritizeByLocality contains the configuration for
locality aware routing.
properties:
mode:
description: Mode specifies the behavior of PrioritizeByLocality
routing. Valid values are "", "none", and "failover".
type: string
type: object
redirect:
description: Redirect when configured, all attempts to resolve the
service this resolver defines will be substituted for the supplied
Expand Down
59 changes: 50 additions & 9 deletions control-plane/api/v1alpha1/serviceresolver_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,16 @@ type ServiceResolverSpec struct {
// LoadBalancer determines the load balancing policy and configuration for services
// issuing requests to this upstream service.
LoadBalancer *LoadBalancer `json:"loadBalancer,omitempty"`
// PrioritizeByLocality controls whether the locality of services within the
// local partition will be used to prioritize connectivity.
PrioritizeByLocality *ServiceResolverPrioritizeByLocality `json:"prioritizeByLocality,omitempty"`
}

type ServiceResolverPrioritizeByLocality struct {
// Mode specifies the type of prioritization that will be performed
// when selecting nodes in the local partition.
// Valid values are: "" (default "none"), "none", and "failover".
Mode string `json:"mode,omitempty"`
}

type ServiceResolverRedirect struct {
Expand Down Expand Up @@ -300,15 +310,16 @@ func (in *ServiceResolver) SyncedConditionStatus() corev1.ConditionStatus {
// ToConsul converts the entry into its Consul equivalent struct.
func (in *ServiceResolver) ToConsul(datacenter string) capi.ConfigEntry {
return &capi.ServiceResolverConfigEntry{
Kind: in.ConsulKind(),
Name: in.ConsulName(),
DefaultSubset: in.Spec.DefaultSubset,
Subsets: in.Spec.Subsets.toConsul(),
Redirect: in.Spec.Redirect.toConsul(),
Failover: in.Spec.Failover.toConsul(),
ConnectTimeout: in.Spec.ConnectTimeout.Duration,
LoadBalancer: in.Spec.LoadBalancer.toConsul(),
Meta: meta(datacenter),
Kind: in.ConsulKind(),
Name: in.ConsulName(),
DefaultSubset: in.Spec.DefaultSubset,
Subsets: in.Spec.Subsets.toConsul(),
Redirect: in.Spec.Redirect.toConsul(),
Failover: in.Spec.Failover.toConsul(),
ConnectTimeout: in.Spec.ConnectTimeout.Duration,
LoadBalancer: in.Spec.LoadBalancer.toConsul(),
PrioritizeByLocality: in.Spec.PrioritizeByLocality.toConsul(),
Meta: meta(datacenter),
}
}

Expand Down Expand Up @@ -338,6 +349,7 @@ func (in *ServiceResolver) Validate(consulMeta common.ConsulMeta) error {
}

errs = append(errs, in.Spec.Redirect.validate(path.Child("redirect"), consulMeta)...)
errs = append(errs, in.Spec.PrioritizeByLocality.validate(path.Child("prioritizeByLocality"))...)
errs = append(errs, in.Spec.Subsets.validate(path.Child("subsets"))...)
errs = append(errs, in.Spec.LoadBalancer.validate(path.Child("loadBalancer"))...)
errs = append(errs, in.validateEnterprise(consulMeta)...)
Expand Down Expand Up @@ -520,6 +532,16 @@ func (in *ServiceResolverFailover) toConsul() *capi.ServiceResolverFailover {
}
}

func (in *ServiceResolverPrioritizeByLocality) toConsul() *capi.ServiceResolverPrioritizeByLocality {
if in == nil {
return nil
}

return &capi.ServiceResolverPrioritizeByLocality{
Mode: in.Mode,
}
}

func (in ServiceResolverFailoverTarget) toConsul() capi.ServiceResolverFailoverTarget {
return capi.ServiceResolverFailoverTarget{
Service: in.Service,
Expand Down Expand Up @@ -629,6 +651,25 @@ func (in *ServiceResolverFailover) isEmpty() bool {
return in.Service == "" && in.ServiceSubset == "" && in.Namespace == "" && len(in.Datacenters) == 0 && len(in.Targets) == 0 && in.Policy == nil && in.SamenessGroup == ""
}

func (in *ServiceResolverPrioritizeByLocality) validate(path *field.Path) field.ErrorList {
var errs field.ErrorList

if in == nil {
return nil
}

switch in.Mode {
case "":
case "none":
case "failover":
default:
asJSON, _ := json.Marshal(in)
errs = append(errs, field.Invalid(path, string(asJSON),
"mode must be one of '', 'none', or 'failover'"))
}
return errs
}

func (in *ServiceResolverFailover) validate(path *field.Path, consulMeta common.ConsulMeta) field.ErrorList {
var errs field.ErrorList
if in.isEmpty() {
Expand Down
28 changes: 28 additions & 0 deletions control-plane/api/v1alpha1/serviceresolver_types_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,9 @@ func TestServiceResolver_MatchesConsul(t *testing.T) {
Datacenter: "redirect_datacenter",
Peer: "redirect_peer",
},
PrioritizeByLocality: &ServiceResolverPrioritizeByLocality{
Mode: "failover",
},
Failover: map[string]ServiceResolverFailover{
"failover1": {
Service: "failover1",
Expand Down Expand Up @@ -147,6 +150,9 @@ func TestServiceResolver_MatchesConsul(t *testing.T) {
Datacenter: "redirect_datacenter",
Peer: "redirect_peer",
},
PrioritizeByLocality: &capi.ServiceResolverPrioritizeByLocality{
Mode: "failover",
},
Failover: map[string]capi.ServiceResolverFailover{
"failover1": {
Service: "failover1",
Expand Down Expand Up @@ -277,6 +283,9 @@ func TestServiceResolver_ToConsul(t *testing.T) {
Datacenter: "redirect_datacenter",
Partition: "redirect_partition",
},
PrioritizeByLocality: &ServiceResolverPrioritizeByLocality{
Mode: "none",
},
Failover: map[string]ServiceResolverFailover{
"failover1": {
Service: "failover1",
Expand Down Expand Up @@ -358,6 +367,9 @@ func TestServiceResolver_ToConsul(t *testing.T) {
Datacenter: "redirect_datacenter",
Partition: "redirect_partition",
},
PrioritizeByLocality: &capi.ServiceResolverPrioritizeByLocality{
Mode: "none",
},
Failover: map[string]capi.ServiceResolverFailover{
"failover1": {
Service: "failover1",
Expand Down Expand Up @@ -882,6 +894,22 @@ func TestServiceResolver_Validate(t *testing.T) {
"spec.failover[failB].namespace: Invalid value: \"namespace-b\": Consul Enterprise namespaces must be enabled to set failover.namespace",
},
},
"prioritize by locality none": {
input: &ServiceResolver{
ObjectMeta: metav1.ObjectMeta{
Name: "foo",
},
Spec: ServiceResolverSpec{
PrioritizeByLocality: &ServiceResolverPrioritizeByLocality{
Mode: "bad",
},
},
},
namespacesEnabled: false,
expectedErrMsgs: []string{
"mode must be one of '', 'none', or 'failover'",
},
},
}
for name, testCase := range cases {
t.Run(name, func(t *testing.T) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -223,6 +223,15 @@ spec:
type: integer
type: object
type: object
prioritizeByLocality:
description: PrioritizeByLocality contains the configuration for
locality aware routing.
properties:
mode:
description: Mode specifies the behavior of PrioritizeByLocality
routing. Valid values are "", "none", and "failover".
type: string
type: object
redirect:
description: Redirect when configured, all attempts to resolve the
service this resolver defines will be substituted for the supplied
Expand Down

0 comments on commit 3ce3302

Please sign in to comment.