Skip to content

Commit

Permalink
Merge pull request #1468 from hashicorp/update-service-resolver-crd
Browse files Browse the repository at this point in the history
Update the Service Resolver CRD to Support Failover Targets
  • Loading branch information
erichaberkorn authored Oct 3, 2022
2 parents 12d85fd + c674c40 commit d3258f9
Show file tree
Hide file tree
Showing 6 changed files with 168 additions and 4 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,9 @@ FEATURES:
* Kubernetes 1.24 Support
* Add support for Kubernetes 1.24 where ServiceAccounts no longer have long-term JWT tokens. [[GH-1431](https://github.com/hashicorp/consul-k8s/pull/1431)]
* Upgrade kubeVersion in helm chart to support Kubernetes 1.21+.
* Cluster Peering:
* Add support for setting failover `Targets` on the Service Resolver CRD. [[GH-1284](https://github.com/hashicorp/consul-k8s/pull/1284)]
* Add support for redirecting to cluster peers on the Service Resolver CRD. [[GH-1284](https://github.com/hashicorp/consul-k8s/pull/1284)]

BREAKING CHANGES:
* Kubernetes 1.24 Support
Expand Down
35 changes: 35 additions & 0 deletions charts/consul/templates/crd-serviceresolvers.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,37 @@ spec:
service to resolve as the failover group of instances. If
empty the default subset for the requested service is used.
type: string
targets:
description: Targets specifies a fixed list of failover targets
to try during failover.
items:
properties:
datacenter:
description: Datacenter specifies the datacenter to try
during failover.
type: string
namespace:
description: Namespace specifies the namespace to try
during failover.
type: string
partition:
description: Partition specifies the partition to try
during failover.
type: string
peer:
description: Peer specifies the name of the cluster peer
to try during failover.
type: string
service:
description: Service specifies the name of the service
to try during failover.
type: string
serviceSubset:
description: ServiceSubset specifies the service subset
to try during failover.
type: string
type: object
type: array
type: object
description: Failover controls when and how to reroute traffic to
an alternate pool of service instances. The map is keyed by the
Expand Down Expand Up @@ -197,6 +228,10 @@ spec:
service from instead of the current partition. If empty the
current partition is assumed.
type: string
peer:
description: Peer is the name of the cluster peer to resolve the
service from instead of the current one.
type: string
service:
description: Service is a service to resolve instead of the current
service.
Expand Down
47 changes: 45 additions & 2 deletions control-plane/api/v1alpha1/serviceresolver_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,9 @@ type ServiceResolverRedirect struct {
// Datacenter is the datacenter to resolve the service from instead of the
// current one.
Datacenter string `json:"datacenter,omitempty"`
// Peer is the name of the cluster peer to resolve the service from instead
// of the current one.
Peer string `json:"peer,omitempty"`
}

type ServiceResolverSubsetMap map[string]ServiceResolverSubset
Expand Down Expand Up @@ -123,6 +126,23 @@ type ServiceResolverFailover struct {
Namespace string `json:"namespace,omitempty"`
// Datacenters is a fixed list of datacenters to try during failover.
Datacenters []string `json:"datacenters,omitempty"`
// Targets specifies a fixed list of failover targets to try during failover.
Targets []ServiceResolverFailoverTarget `json:"targets,omitempty"`
}

type ServiceResolverFailoverTarget struct {
// Service specifies the name of the service to try during failover.
Service string `json:"service,omitempty"`
// ServiceSubset specifies the service subset to try during failover.
ServiceSubset string `json:"serviceSubset,omitempty"`
// Partition specifies the partition to try during failover.
Partition string `json:"partition,omitempty"`
// Namespace specifies the namespace to try during failover.
Namespace string `json:"namespace,omitempty"`
// Datacenter specifies the datacenter to try during failover.
Datacenter string `json:"datacenter,omitempty"`
// Peer specifies the name of the cluster peer to try during failover.
Peer string `json:"peer,omitempty"`
}

type LoadBalancer struct {
Expand Down Expand Up @@ -347,6 +367,8 @@ func (in *ServiceResolverRedirect) toConsul() *capi.ServiceResolverRedirect {
ServiceSubset: in.ServiceSubset,
Namespace: in.Namespace,
Datacenter: in.Datacenter,
Partition: in.Partition,
Peer: in.Peer,
}
}

Expand All @@ -362,11 +384,28 @@ func (in ServiceResolverFailoverMap) toConsul() map[string]capi.ServiceResolverF
}

func (in ServiceResolverFailover) toConsul() capi.ServiceResolverFailover {
var targets []capi.ServiceResolverFailoverTarget
for _, target := range in.Targets {
targets = append(targets, target.toConsul())
}

return capi.ServiceResolverFailover{
Service: in.Service,
ServiceSubset: in.ServiceSubset,
Namespace: in.Namespace,
Datacenters: in.Datacenters,
Targets: targets,
}
}

func (in ServiceResolverFailoverTarget) toConsul() capi.ServiceResolverFailoverTarget {
return capi.ServiceResolverFailoverTarget{
Service: in.Service,
ServiceSubset: in.ServiceSubset,
Namespace: in.Namespace,
Partition: in.Partition,
Datacenter: in.Datacenter,
Peer: in.Peer,
}
}

Expand Down Expand Up @@ -464,12 +503,16 @@ func (in *ServiceResolver) validateEnterprise(consulMeta common.ConsulMeta) fiel
return errs
}

func (in *ServiceResolverFailover) isEmpty() bool {
return in.Service == "" && in.ServiceSubset == "" && in.Namespace == "" && len(in.Datacenters) == 0 && len(in.Targets) == 0
}

func (in *ServiceResolverFailover) validate(path *field.Path) *field.Error {
if in.Service == "" && in.ServiceSubset == "" && in.Namespace == "" && len(in.Datacenters) == 0 {
if in.isEmpty() {
// NOTE: We're passing "{}" here as our value because we know that the
// error is we have an empty object.
return field.Invalid(path, "{}",
"service, serviceSubset, namespace and datacenters cannot all be empty at once")
"service, serviceSubset, namespace, datacenters, and targets cannot all be empty at once")
}
return nil
}
Expand Down
32 changes: 30 additions & 2 deletions control-plane/api/v1alpha1/serviceresolver_types_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ func TestServiceResolver_MatchesConsul(t *testing.T) {
ServiceSubset: "redirect_subset",
Namespace: "redirect_namespace",
Datacenter: "redirect_datacenter",
Peer: "redirect_peer",
},
Failover: map[string]ServiceResolverFailover{
"failover1": {
Expand All @@ -73,6 +74,12 @@ func TestServiceResolver_MatchesConsul(t *testing.T) {
Namespace: "failover_namespace2",
Datacenters: []string{"failover2_dc1", "failover2_dc2"},
},
"failover3": {
Targets: []ServiceResolverFailoverTarget{
{Peer: "failover_peer3"},
{Partition: "failover_partition3", Namespace: "failover_namespace3"},
},
},
},
ConnectTimeout: metav1.Duration{Duration: 1 * time.Second},
LoadBalancer: &LoadBalancer{
Expand Down Expand Up @@ -119,6 +126,7 @@ func TestServiceResolver_MatchesConsul(t *testing.T) {
ServiceSubset: "redirect_subset",
Namespace: "redirect_namespace",
Datacenter: "redirect_datacenter",
Peer: "redirect_peer",
},
Failover: map[string]capi.ServiceResolverFailover{
"failover1": {
Expand All @@ -133,6 +141,12 @@ func TestServiceResolver_MatchesConsul(t *testing.T) {
Namespace: "failover_namespace2",
Datacenters: []string{"failover2_dc1", "failover2_dc2"},
},
"failover3": {
Targets: []capi.ServiceResolverFailoverTarget{
{Peer: "failover_peer3"},
{Partition: "failover_partition3", Namespace: "failover_namespace3"},
},
},
},
ConnectTimeout: 1 * time.Second,
LoadBalancer: &capi.LoadBalancer{
Expand Down Expand Up @@ -228,6 +242,7 @@ func TestServiceResolver_ToConsul(t *testing.T) {
ServiceSubset: "redirect_subset",
Namespace: "redirect_namespace",
Datacenter: "redirect_datacenter",
Partition: "redirect_partition",
},
Failover: map[string]ServiceResolverFailover{
"failover1": {
Expand All @@ -242,6 +257,12 @@ func TestServiceResolver_ToConsul(t *testing.T) {
Namespace: "failover_namespace2",
Datacenters: []string{"failover2_dc1", "failover2_dc2"},
},
"failover3": {
Targets: []ServiceResolverFailoverTarget{
{Peer: "failover_peer3"},
{Partition: "failover_partition3", Namespace: "failover_namespace3"},
},
},
},
ConnectTimeout: metav1.Duration{Duration: 1 * time.Second},
LoadBalancer: &LoadBalancer{
Expand Down Expand Up @@ -288,6 +309,7 @@ func TestServiceResolver_ToConsul(t *testing.T) {
ServiceSubset: "redirect_subset",
Namespace: "redirect_namespace",
Datacenter: "redirect_datacenter",
Partition: "redirect_partition",
},
Failover: map[string]capi.ServiceResolverFailover{
"failover1": {
Expand All @@ -302,6 +324,12 @@ func TestServiceResolver_ToConsul(t *testing.T) {
Namespace: "failover_namespace2",
Datacenters: []string{"failover2_dc1", "failover2_dc2"},
},
"failover3": {
Targets: []capi.ServiceResolverFailoverTarget{
{Peer: "failover_peer3"},
{Partition: "failover_partition3", Namespace: "failover_namespace3"},
},
},
},
ConnectTimeout: 1 * time.Second,
LoadBalancer: &capi.LoadBalancer{
Expand Down Expand Up @@ -567,8 +595,8 @@ func TestServiceResolver_Validate(t *testing.T) {
},
namespacesEnabled: false,
expectedErrMsgs: []string{
"spec.failover[failA]: Invalid value: \"{}\": service, serviceSubset, namespace and datacenters cannot all be empty at once",
"spec.failover[failB]: Invalid value: \"{}\": service, serviceSubset, namespace and datacenters cannot all be empty at once",
"spec.failover[failA]: Invalid value: \"{}\": service, serviceSubset, namespace, datacenters, and targets cannot all be empty at once",
"spec.failover[failB]: Invalid value: \"{}\": service, serviceSubset, namespace, datacenters, and targets cannot all be empty at once",
},
},
"hashPolicy.field invalid": {
Expand Down
20 changes: 20 additions & 0 deletions control-plane/api/v1alpha1/zz_generated.deepcopy.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,37 @@ spec:
service to resolve as the failover group of instances. If
empty the default subset for the requested service is used.
type: string
targets:
description: Targets specifies a fixed list of failover targets
to try during failover.
items:
properties:
datacenter:
description: Datacenter specifies the datacenter to try
during failover.
type: string
namespace:
description: Namespace specifies the namespace to try
during failover.
type: string
partition:
description: Partition specifies the partition to try
during failover.
type: string
peer:
description: Peer specifies the name of the cluster peer
to try during failover.
type: string
service:
description: Service specifies the name of the service
to try during failover.
type: string
serviceSubset:
description: ServiceSubset specifies the service subset
to try during failover.
type: string
type: object
type: array
type: object
description: Failover controls when and how to reroute traffic to
an alternate pool of service instances. The map is keyed by the
Expand Down Expand Up @@ -190,6 +221,10 @@ spec:
service from instead of the current partition. If empty the
current partition is assumed.
type: string
peer:
description: Peer is the name of the cluster peer to resolve the
service from instead of the current one.
type: string
service:
description: Service is a service to resolve instead of the current
service.
Expand Down

0 comments on commit d3258f9

Please sign in to comment.