mirror of https://github.com/status-im/consul.git
added computed failover controller (#20329)
* added computed failover controller * removed some uncessary changes * removed uncessary changes * minor refactor * minor refactor fmt * added copyright
This commit is contained in:
parent
0abf8f8426
commit
3446eb3b1b
|
@ -2,8 +2,9 @@
|
|||
flowchart TD
|
||||
auth/v2beta1/computedtrafficpermissions --> auth/v2beta1/trafficpermissions
|
||||
auth/v2beta1/computedtrafficpermissions --> auth/v2beta1/workloadidentity
|
||||
catalog/v2beta1/computedfailoverpolicy
|
||||
catalog/v2beta1/failoverpolicy --> catalog/v2beta1/service
|
||||
catalog/v2beta1/computedfailoverpolicy --> catalog/v2beta1/failoverpolicy
|
||||
catalog/v2beta1/computedfailoverpolicy --> catalog/v2beta1/service
|
||||
catalog/v2beta1/failoverpolicy
|
||||
catalog/v2beta1/healthstatus
|
||||
catalog/v2beta1/node --> catalog/v2beta1/nodehealthstatus
|
||||
catalog/v2beta1/nodehealthstatus
|
||||
|
|
|
@ -6,6 +6,7 @@ package failover
|
|||
import (
|
||||
"context"
|
||||
|
||||
"github.com/hashicorp/consul/internal/catalog/internal/controllers/failover/expander"
|
||||
"github.com/hashicorp/consul/internal/catalog/internal/types"
|
||||
"github.com/hashicorp/consul/internal/controller"
|
||||
"github.com/hashicorp/consul/internal/controller/cache"
|
||||
|
@ -13,44 +14,53 @@ import (
|
|||
"github.com/hashicorp/consul/internal/controller/dependency"
|
||||
"github.com/hashicorp/consul/internal/resource"
|
||||
pbcatalog "github.com/hashicorp/consul/proto-public/pbcatalog/v2beta1"
|
||||
pbmulticluster "github.com/hashicorp/consul/proto-public/pbmulticluster/v2beta1"
|
||||
"github.com/hashicorp/consul/proto-public/pbresource"
|
||||
"google.golang.org/protobuf/proto"
|
||||
"google.golang.org/protobuf/types/known/anypb"
|
||||
)
|
||||
|
||||
const (
|
||||
destRefsIndexName = "destination-refs"
|
||||
destRefsIndexName = "destination-refs"
|
||||
boundRefsIndexName = "bound-refs"
|
||||
)
|
||||
|
||||
func FailoverPolicyController() *controller.Controller {
|
||||
return controller.NewController(
|
||||
func FailoverPolicyController(sgExpander expander.SamenessGroupExpander) *controller.Controller {
|
||||
ctrl := controller.NewController(
|
||||
ControllerID,
|
||||
pbcatalog.FailoverPolicyType,
|
||||
// We index the destination references of a failover policy so that when the
|
||||
// Service watch fires we can find all FailoverPolicy resources that reference
|
||||
// it to rereconcile them.
|
||||
indexers.RefOrIDIndex(
|
||||
destRefsIndexName,
|
||||
func(res *resource.DecodedResource[*pbcatalog.FailoverPolicy]) []*pbresource.Reference {
|
||||
return res.Data.GetUnderlyingDestinationRefs()
|
||||
},
|
||||
)).
|
||||
pbcatalog.ComputedFailoverPolicyType,
|
||||
indexers.BoundRefsIndex[*pbcatalog.ComputedFailoverPolicy](boundRefsIndexName),
|
||||
).
|
||||
WithWatch(
|
||||
pbcatalog.ServiceType,
|
||||
dependency.MultiMapper(
|
||||
// FailoverPolicy is name-aligned with the Service it controls so always
|
||||
// re-reconcile the corresponding FailoverPolicy when a Service changes.
|
||||
dependency.ReplaceType(pbcatalog.FailoverPolicyType),
|
||||
// Also check for all FailoverPolicy resources that have this service as a
|
||||
// destination and re-reconcile those to check for port mapping conflicts.
|
||||
dependency.CacheListMapper(pbcatalog.FailoverPolicyType, destRefsIndexName),
|
||||
dependency.ReplaceType(pbcatalog.ComputedFailoverPolicyType),
|
||||
dependency.WrapAndReplaceType(
|
||||
pbcatalog.ComputedFailoverPolicyType,
|
||||
dependency.CacheParentsMapper(pbcatalog.ComputedFailoverPolicyType, boundRefsIndexName),
|
||||
),
|
||||
),
|
||||
).
|
||||
WithReconciler(newFailoverPolicyReconciler())
|
||||
WithWatch(
|
||||
pbcatalog.FailoverPolicyType,
|
||||
dependency.ReplaceType(pbcatalog.ComputedFailoverPolicyType),
|
||||
sgExpander.GetSamenessGroupIndex(),
|
||||
).
|
||||
WithReconciler(newFailoverPolicyReconciler(sgExpander))
|
||||
|
||||
return registerEnterpriseControllerWatchers(ctrl)
|
||||
}
|
||||
|
||||
type failoverPolicyReconciler struct{}
|
||||
type failoverPolicyReconciler struct {
|
||||
sgExpander expander.SamenessGroupExpander
|
||||
}
|
||||
|
||||
func newFailoverPolicyReconciler() *failoverPolicyReconciler {
|
||||
return &failoverPolicyReconciler{}
|
||||
func newFailoverPolicyReconciler(sgExpander expander.SamenessGroupExpander) *failoverPolicyReconciler {
|
||||
return &failoverPolicyReconciler{
|
||||
sgExpander: sgExpander,
|
||||
}
|
||||
}
|
||||
|
||||
func (r *failoverPolicyReconciler) Reconcile(ctx context.Context, rt controller.Runtime, req controller.Request) error {
|
||||
|
@ -58,18 +68,25 @@ func (r *failoverPolicyReconciler) Reconcile(ctx context.Context, rt controller.
|
|||
// reconciliation request processing will not affect future invocations.
|
||||
rt.Logger = rt.Logger.With("resource-id", req.ID)
|
||||
|
||||
rt.Logger.Trace("reconciling failover policy")
|
||||
|
||||
failoverPolicyID := req.ID
|
||||
rt.Logger.Trace("reconciling computed failover policy")
|
||||
|
||||
computedFailoverPolicy, err := cache.GetDecoded[*pbcatalog.ComputedFailoverPolicy](rt.Cache, pbcatalog.ComputedFailoverPolicyType, "id", req.ID)
|
||||
if err != nil {
|
||||
rt.Logger.Error("error retrieving computed failover policy", "error", err)
|
||||
return err
|
||||
}
|
||||
failoverPolicyID := resource.ReplaceType(pbcatalog.FailoverPolicyType, req.ID)
|
||||
failoverPolicy, err := cache.GetDecoded[*pbcatalog.FailoverPolicy](rt.Cache, pbcatalog.FailoverPolicyType, "id", failoverPolicyID)
|
||||
if err != nil {
|
||||
rt.Logger.Error("error retrieving failover policy", "error", err)
|
||||
return err
|
||||
}
|
||||
if failoverPolicy == nil {
|
||||
// Either the failover policy was deleted, or it doesn't exist but an
|
||||
// update to a Service came through and we can ignore it.
|
||||
if err := deleteResource(ctx, rt, computedFailoverPolicy.GetResource()); err != nil {
|
||||
rt.Logger.Error("failed to delete computed failover policy", "error", err)
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -85,81 +102,75 @@ func (r *failoverPolicyReconciler) Reconcile(ctx context.Context, rt controller.
|
|||
rt.Logger.Error("error retrieving corresponding service", "error", err)
|
||||
return err
|
||||
}
|
||||
destServices := make(map[resource.ReferenceKey]*resource.DecodedResource[*pbcatalog.Service])
|
||||
if service != nil {
|
||||
destServices[resource.NewReferenceKey(serviceID)] = service
|
||||
}
|
||||
|
||||
// Denormalize the ports and stuff. After this we have no empty ports.
|
||||
if service != nil {
|
||||
failoverPolicy.Data = types.SimplifyFailoverPolicy(
|
||||
service.Data,
|
||||
failoverPolicy.Data,
|
||||
)
|
||||
}
|
||||
|
||||
// Fetch services.
|
||||
for _, dest := range failoverPolicy.Data.GetUnderlyingDestinations() {
|
||||
if dest.Ref == nil || !isServiceType(dest.Ref.Type) || dest.Ref.Section != "" {
|
||||
continue // invalid, not possible due to validation hook
|
||||
}
|
||||
|
||||
key := resource.NewReferenceKey(dest.Ref)
|
||||
|
||||
if _, ok := destServices[key]; ok {
|
||||
continue
|
||||
}
|
||||
|
||||
destID := resource.IDFromReference(dest.Ref)
|
||||
|
||||
destService, err := cache.GetDecoded[*pbcatalog.Service](rt.Cache, pbcatalog.ServiceType, "id", destID)
|
||||
if err != nil {
|
||||
rt.Logger.Error("error retrieving destination service", "service", key, "error", err)
|
||||
if service == nil {
|
||||
if err := deleteResource(ctx, rt, computedFailoverPolicy.GetResource()); err != nil {
|
||||
rt.Logger.Error("failed to delete computed failover policy", "error", err)
|
||||
return err
|
||||
}
|
||||
|
||||
if destService != nil {
|
||||
destServices[key] = destService
|
||||
conds := []*pbresource.Condition{ConditionMissingService}
|
||||
|
||||
if err := writeStatus(ctx, rt, failoverPolicy.Resource, conds); err != nil {
|
||||
rt.Logger.Error("error encountered when attempting to update the resource's failover policy status", "error", err)
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
newStatus := computeNewStatus(failoverPolicy, service, destServices)
|
||||
|
||||
if resource.EqualStatus(failoverPolicy.Resource.Status[ControllerID], newStatus, false) {
|
||||
rt.Logger.Trace("resource's failover policy status is unchanged",
|
||||
"conditions", newStatus.Conditions)
|
||||
rt.Logger.Trace("resource's failover policy status was updated",
|
||||
"conditions", conds)
|
||||
return nil
|
||||
}
|
||||
|
||||
_, err = rt.Client.WriteStatus(ctx, &pbresource.WriteStatusRequest{
|
||||
Id: failoverPolicy.Resource.Id,
|
||||
Key: ControllerID,
|
||||
Status: newStatus,
|
||||
})
|
||||
|
||||
newComputedFailoverPolicy, destServices, missingSamenessGroups, err := makeComputedFailoverPolicy(ctx, rt, r.sgExpander, failoverPolicy, service)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
computedFailoverResource := computedFailoverPolicy.GetResource()
|
||||
|
||||
if !proto.Equal(computedFailoverPolicy.GetData(), newComputedFailoverPolicy) {
|
||||
|
||||
newCFPData, err := anypb.New(newComputedFailoverPolicy)
|
||||
if err != nil {
|
||||
rt.Logger.Error("error marshalling new computed failover policy", "error", err)
|
||||
return err
|
||||
}
|
||||
rt.Logger.Trace("writing computed failover policy")
|
||||
rsp, err := rt.Client.Write(ctx, &pbresource.WriteRequest{
|
||||
Resource: &pbresource.Resource{
|
||||
Id: req.ID,
|
||||
Data: newCFPData,
|
||||
},
|
||||
})
|
||||
if err != nil || rsp.Resource == nil {
|
||||
rt.Logger.Error("error writing new computed failover policy", "error", err)
|
||||
return err
|
||||
} else {
|
||||
rt.Logger.Trace("new computed failover policy was successfully written")
|
||||
computedFailoverResource = rsp.Resource
|
||||
}
|
||||
}
|
||||
|
||||
conds := computeNewConditions(failoverPolicy.Resource, newComputedFailoverPolicy, service, destServices, missingSamenessGroups)
|
||||
if err := writeStatus(ctx, rt, failoverPolicy.Resource, conds); err != nil {
|
||||
rt.Logger.Error("error encountered when attempting to update the resource's failover policy status", "error", err)
|
||||
return err
|
||||
}
|
||||
|
||||
rt.Logger.Trace("resource's failover policy status was updated",
|
||||
"conditions", newStatus.Conditions)
|
||||
conds = computeNewConditions(computedFailoverResource, newComputedFailoverPolicy, service, destServices, missingSamenessGroups)
|
||||
if err := writeStatus(ctx, rt, computedFailoverResource, conds); err != nil {
|
||||
rt.Logger.Error("error encountered when attempting to update the resource's computed failover policy status", "error", err)
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func computeNewStatus(
|
||||
failoverPolicy *resource.DecodedResource[*pbcatalog.FailoverPolicy],
|
||||
func computeNewConditions(
|
||||
fpRes *pbresource.Resource,
|
||||
fp *pbcatalog.ComputedFailoverPolicy,
|
||||
service *resource.DecodedResource[*pbcatalog.Service],
|
||||
destServices map[resource.ReferenceKey]*resource.DecodedResource[*pbcatalog.Service],
|
||||
) *pbresource.Status {
|
||||
if service == nil {
|
||||
return &pbresource.Status{
|
||||
ObservedGeneration: failoverPolicy.Resource.Generation,
|
||||
Conditions: []*pbresource.Condition{
|
||||
ConditionMissingService,
|
||||
},
|
||||
}
|
||||
}
|
||||
missingSamenessGroups map[string]struct{},
|
||||
) []*pbresource.Condition {
|
||||
|
||||
allowedPortProtocols := make(map[string]pbcatalog.Protocol)
|
||||
for _, port := range service.Data.Ports {
|
||||
|
@ -171,25 +182,7 @@ func computeNewStatus(
|
|||
|
||||
var conditions []*pbresource.Condition
|
||||
|
||||
if failoverPolicy.Data.Config != nil {
|
||||
for _, dest := range failoverPolicy.Data.Config.Destinations {
|
||||
// We know from validation that a Ref must be set, and the type it
|
||||
// points to is a Service.
|
||||
//
|
||||
// Rather than do additional validation, just do a quick
|
||||
// belt-and-suspenders check-and-skip if something looks weird.
|
||||
if dest.Ref == nil || !isServiceType(dest.Ref.Type) {
|
||||
continue
|
||||
}
|
||||
|
||||
if cond := serviceHasPort(dest, destServices); cond != nil {
|
||||
conditions = append(conditions, cond)
|
||||
}
|
||||
}
|
||||
// TODO: validate that referenced sameness groups exist
|
||||
}
|
||||
|
||||
for port, pc := range failoverPolicy.Data.PortConfigs {
|
||||
for port, pc := range fp.GetPortConfigs() {
|
||||
if _, ok := allowedPortProtocols[port]; !ok {
|
||||
conditions = append(conditions, ConditionUnknownPort(port))
|
||||
}
|
||||
|
@ -208,23 +201,28 @@ func computeNewStatus(
|
|||
conditions = append(conditions, cond)
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: validate that referenced sameness groups exist
|
||||
}
|
||||
|
||||
if len(conditions) > 0 {
|
||||
return &pbresource.Status{
|
||||
ObservedGeneration: failoverPolicy.Resource.Generation,
|
||||
Conditions: conditions,
|
||||
for destKey, svc := range destServices {
|
||||
if svc != nil {
|
||||
continue
|
||||
}
|
||||
conditions = append(conditions, ConditionMissingDestinationService(destKey.ToReference()))
|
||||
}
|
||||
|
||||
return &pbresource.Status{
|
||||
ObservedGeneration: failoverPolicy.Resource.Generation,
|
||||
Conditions: []*pbresource.Condition{
|
||||
ConditionOK,
|
||||
},
|
||||
for sg := range missingSamenessGroups {
|
||||
ref := &pbresource.Reference{
|
||||
Type: pbmulticluster.SamenessGroupType,
|
||||
Tenancy: &pbresource.Tenancy{
|
||||
Partition: fpRes.GetId().GetTenancy().GetPartition(),
|
||||
PeerName: resource.DefaultPeerName,
|
||||
},
|
||||
Name: sg,
|
||||
}
|
||||
conditions = append(conditions, ConditionMissingSamenessGroup(ref))
|
||||
}
|
||||
|
||||
return conditions
|
||||
}
|
||||
|
||||
func serviceHasPort(
|
||||
|
@ -233,8 +231,8 @@ func serviceHasPort(
|
|||
) *pbresource.Condition {
|
||||
key := resource.NewReferenceKey(dest.Ref)
|
||||
destService, ok := destServices[key]
|
||||
if !ok {
|
||||
return ConditionMissingDestinationService(dest.Ref)
|
||||
if !ok || destService == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
found := false
|
||||
|
@ -265,3 +263,139 @@ func isServiceType(typ *pbresource.Type) bool {
|
|||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func deleteResource(ctx context.Context, rt controller.Runtime, resource *pbresource.Resource) error {
|
||||
if resource == nil {
|
||||
return nil
|
||||
}
|
||||
_, err := rt.Client.Delete(ctx, &pbresource.DeleteRequest{
|
||||
Id: resource.GetId(),
|
||||
Version: resource.GetVersion(),
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func makeComputedFailoverPolicy(ctx context.Context, rt controller.Runtime, sgExpander expander.SamenessGroupExpander, failoverPolicy *resource.DecodedResource[*pbcatalog.FailoverPolicy], service *resource.DecodedResource[*pbcatalog.Service]) (*pbcatalog.ComputedFailoverPolicy, map[resource.ReferenceKey]*resource.DecodedResource[*pbcatalog.Service], map[string]struct{}, error) {
|
||||
simplified := types.SimplifyFailoverPolicy(
|
||||
service.Data,
|
||||
failoverPolicy.Data,
|
||||
)
|
||||
cfp := &pbcatalog.ComputedFailoverPolicy{
|
||||
|
||||
PortConfigs: simplified.PortConfigs,
|
||||
}
|
||||
missingSamenessGroups := make(map[string]struct{})
|
||||
destServices := map[resource.ReferenceKey]*resource.DecodedResource[*pbcatalog.Service]{
|
||||
resource.NewReferenceKey(service.Id): service,
|
||||
}
|
||||
|
||||
// Expand sameness group
|
||||
for port, fc := range cfp.PortConfigs {
|
||||
if fc.GetSamenessGroup() == "" {
|
||||
continue
|
||||
}
|
||||
|
||||
dests, missing, err := sgExpander.ComputeFailoverDestinationsFromSamenessGroup(rt, failoverPolicy.Id, fc.GetSamenessGroup(), port)
|
||||
if err != nil {
|
||||
return cfp, nil, missingSamenessGroups, err
|
||||
}
|
||||
|
||||
if missing != "" {
|
||||
delete(cfp.PortConfigs, port)
|
||||
missingSamenessGroups[missing] = struct{}{}
|
||||
continue
|
||||
}
|
||||
|
||||
if len(dests) == 0 {
|
||||
delete(cfp.PortConfigs, port)
|
||||
continue
|
||||
}
|
||||
|
||||
fc.SamenessGroup = ""
|
||||
fc.Destinations = dests
|
||||
}
|
||||
|
||||
// Filter missing destinations
|
||||
for port, fc := range cfp.PortConfigs {
|
||||
if len(fc.Destinations) == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
var err error
|
||||
fc.Destinations, err = filterInvalidDests(ctx, rt, fc.Destinations, destServices)
|
||||
if err != nil {
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
|
||||
if len(fc.GetDestinations()) == 0 {
|
||||
delete(cfp.GetPortConfigs(), port)
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
for ref := range destServices {
|
||||
cfp.BoundReferences = append(cfp.BoundReferences, ref.ToReference())
|
||||
}
|
||||
|
||||
return cfp, destServices, missingSamenessGroups, nil
|
||||
}
|
||||
|
||||
func filterInvalidDests(ctx context.Context, rt controller.Runtime, dests []*pbcatalog.FailoverDestination, destServices map[resource.ReferenceKey]*resource.DecodedResource[*pbcatalog.Service]) ([]*pbcatalog.FailoverDestination, error) {
|
||||
var out []*pbcatalog.FailoverDestination
|
||||
for _, dest := range dests {
|
||||
ref := resource.NewReferenceKey(dest.Ref)
|
||||
if svc, ok := destServices[ref]; ok {
|
||||
if svc != nil {
|
||||
out = append(out, dest)
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
destService, err := resource.GetDecodedResource[*pbcatalog.Service](ctx, rt.Client, resource.IDFromReference(dest.Ref))
|
||||
if err != nil {
|
||||
rt.Logger.Error("error retrieving destination service while filtering", "service", dest, "error", err)
|
||||
return nil, err
|
||||
}
|
||||
if destService != nil {
|
||||
out = append(out, dest)
|
||||
}
|
||||
destServices[resource.NewReferenceKey(dest.Ref)] = destService
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func writeStatus(ctx context.Context, rt controller.Runtime, res *pbresource.Resource, conditions []*pbresource.Condition) error {
|
||||
newStatus := &pbresource.Status{
|
||||
ObservedGeneration: res.GetGeneration(),
|
||||
Conditions: []*pbresource.Condition{
|
||||
ConditionOK,
|
||||
},
|
||||
}
|
||||
|
||||
if len(conditions) > 0 {
|
||||
newStatus = &pbresource.Status{
|
||||
ObservedGeneration: res.GetGeneration(),
|
||||
Conditions: conditions,
|
||||
}
|
||||
}
|
||||
|
||||
if !resource.EqualStatus(res.GetStatus()[ControllerID], newStatus, false) {
|
||||
|
||||
_, err := rt.Client.WriteStatus(ctx, &pbresource.WriteStatusRequest{
|
||||
Id: res.Id,
|
||||
Key: ControllerID,
|
||||
Status: newStatus,
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
rt.Logger.Trace("resource's status was updated",
|
||||
"conditions", newStatus.Conditions)
|
||||
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -7,14 +7,17 @@ import (
|
|||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/consul/internal/catalog/internal/controllers/failover/expander"
|
||||
"github.com/hashicorp/consul/internal/catalog/internal/types"
|
||||
"github.com/hashicorp/consul/internal/controller"
|
||||
"github.com/hashicorp/consul/internal/controller/controllertest"
|
||||
"github.com/hashicorp/consul/internal/multicluster"
|
||||
"github.com/hashicorp/consul/internal/resource"
|
||||
"github.com/hashicorp/consul/internal/resource/resourcetest"
|
||||
rtest "github.com/hashicorp/consul/internal/resource/resourcetest"
|
||||
pbcatalog "github.com/hashicorp/consul/proto-public/pbcatalog/v2beta1"
|
||||
"github.com/hashicorp/consul/proto-public/pbresource"
|
||||
"github.com/hashicorp/consul/proto/private/prototest"
|
||||
)
|
||||
|
||||
func TestController(t *testing.T) {
|
||||
|
@ -23,9 +26,9 @@ func TestController(t *testing.T) {
|
|||
|
||||
clientRaw := controllertest.NewControllerTestBuilder().
|
||||
WithTenancies(resourcetest.TestTenancies()...).
|
||||
WithResourceRegisterFns(types.Register).
|
||||
WithResourceRegisterFns(types.Register, multicluster.RegisterTypes).
|
||||
WithControllerRegisterFns(func(mgr *controller.Manager) {
|
||||
mgr.Register(FailoverPolicyController())
|
||||
mgr.Register(FailoverPolicyController(expander.GetSamenessGroupExpander()))
|
||||
}).
|
||||
Run(t)
|
||||
|
||||
|
@ -54,7 +57,10 @@ func TestController(t *testing.T) {
|
|||
|
||||
t.Cleanup(func() { client.MustDelete(t, failover.Id) })
|
||||
|
||||
var expectedComputedFP *pbcatalog.ComputedFailoverPolicy
|
||||
|
||||
client.WaitForStatusCondition(t, failover.Id, ControllerID, ConditionMissingService)
|
||||
client.RequireResourceNotFound(t, resource.ReplaceType(pbcatalog.ComputedFailoverPolicyType, failover.Id))
|
||||
t.Logf("reconciled to missing service status")
|
||||
|
||||
// Provide the service.
|
||||
|
@ -72,7 +78,34 @@ func TestController(t *testing.T) {
|
|||
|
||||
t.Cleanup(func() { client.MustDelete(t, svc.Id) })
|
||||
|
||||
client.WaitForStatusCondition(t, failover.Id, ControllerID, ConditionOK)
|
||||
expectedComputedFP = &pbcatalog.ComputedFailoverPolicy{
|
||||
PortConfigs: map[string]*pbcatalog.FailoverConfig{
|
||||
"http": {
|
||||
Destinations: []*pbcatalog.FailoverDestination{{
|
||||
Ref: apiServiceRef,
|
||||
Port: "http",
|
||||
}},
|
||||
},
|
||||
},
|
||||
BoundReferences: []*pbresource.Reference{apiServiceRef},
|
||||
}
|
||||
|
||||
waitAndAssertComputedFailoverPolicy(t, client, failover.Id, expectedComputedFP, ConditionOK)
|
||||
|
||||
t.Log("delete service")
|
||||
|
||||
client.MustDelete(t, svc.Id)
|
||||
|
||||
client.WaitForReconciliation(t, resource.ReplaceType(pbcatalog.ComputedFailoverPolicyType, failover.Id), ControllerID)
|
||||
client.WaitForStatusCondition(t, failover.Id, ControllerID, ConditionMissingService)
|
||||
client.RequireResourceNotFound(t, resource.ReplaceType(pbcatalog.ComputedFailoverPolicyType, failover.Id))
|
||||
|
||||
// re add the service
|
||||
rtest.Resource(pbcatalog.ServiceType, "api").
|
||||
WithData(t, apiServiceData).
|
||||
WithTenancy(tenancy).
|
||||
Write(t, client)
|
||||
|
||||
t.Logf("reconciled to accepted")
|
||||
|
||||
// Update the failover to reference an unknown port
|
||||
|
@ -92,14 +125,18 @@ func TestController(t *testing.T) {
|
|||
},
|
||||
},
|
||||
}
|
||||
svc = rtest.Resource(pbcatalog.FailoverPolicyType, "api").
|
||||
failover = rtest.Resource(pbcatalog.FailoverPolicyType, "api").
|
||||
WithData(t, failoverData).
|
||||
WithTenancy(tenancy).
|
||||
Write(t, client)
|
||||
|
||||
t.Cleanup(func() { client.MustDelete(t, svc.Id) })
|
||||
t.Cleanup(func() { client.MustDelete(t, failover.Id) })
|
||||
|
||||
client.WaitForStatusCondition(t, failover.Id, ControllerID, ConditionUnknownPort("admin"))
|
||||
expectedComputedFP = &pbcatalog.ComputedFailoverPolicy{
|
||||
PortConfigs: failoverData.PortConfigs,
|
||||
BoundReferences: []*pbresource.Reference{apiServiceRef},
|
||||
}
|
||||
waitAndAssertComputedFailoverPolicy(t, client, failover.Id, expectedComputedFP, ConditionUnknownPort("admin"))
|
||||
t.Logf("reconciled to unknown admin port")
|
||||
|
||||
// update the service to fix the stray reference, but point to a mesh port
|
||||
|
@ -123,7 +160,7 @@ func TestController(t *testing.T) {
|
|||
|
||||
t.Cleanup(func() { client.MustDelete(t, svc.Id) })
|
||||
|
||||
client.WaitForStatusCondition(t, failover.Id, ControllerID, ConditionUsingMeshDestinationPort(apiServiceRef, "admin"))
|
||||
waitAndAssertComputedFailoverPolicy(t, client, failover.Id, expectedComputedFP, ConditionUsingMeshDestinationPort(apiServiceRef, "admin"))
|
||||
t.Logf("reconciled to using mesh destination port")
|
||||
|
||||
// update the service to fix the stray reference to not be a mesh port
|
||||
|
@ -147,7 +184,7 @@ func TestController(t *testing.T) {
|
|||
|
||||
t.Cleanup(func() { client.MustDelete(t, svc.Id) })
|
||||
|
||||
client.WaitForStatusCondition(t, failover.Id, ControllerID, ConditionOK)
|
||||
waitAndAssertComputedFailoverPolicy(t, client, failover.Id, expectedComputedFP, ConditionOK)
|
||||
t.Logf("reconciled to accepted")
|
||||
|
||||
// change failover leg to point to missing service
|
||||
|
@ -167,14 +204,26 @@ func TestController(t *testing.T) {
|
|||
},
|
||||
},
|
||||
}
|
||||
svc = rtest.Resource(pbcatalog.FailoverPolicyType, "api").
|
||||
failover = rtest.Resource(pbcatalog.FailoverPolicyType, "api").
|
||||
WithData(t, failoverData).
|
||||
WithTenancy(tenancy).
|
||||
Write(t, client)
|
||||
|
||||
t.Cleanup(func() { client.MustDelete(t, svc.Id) })
|
||||
t.Cleanup(func() { client.MustDelete(t, failover.Id) })
|
||||
|
||||
client.WaitForStatusCondition(t, failover.Id, ControllerID, ConditionMissingDestinationService(otherServiceRef))
|
||||
expectedComputedFP = &pbcatalog.ComputedFailoverPolicy{
|
||||
PortConfigs: map[string]*pbcatalog.FailoverConfig{
|
||||
"http": {
|
||||
Destinations: []*pbcatalog.FailoverDestination{{
|
||||
Ref: apiServiceRef,
|
||||
Port: "http",
|
||||
}},
|
||||
},
|
||||
},
|
||||
BoundReferences: []*pbresource.Reference{apiServiceRef, otherServiceRef},
|
||||
}
|
||||
|
||||
waitAndAssertComputedFailoverPolicy(t, client, failover.Id, expectedComputedFP, ConditionMissingDestinationService(otherServiceRef))
|
||||
t.Logf("reconciled to missing dest service: other")
|
||||
|
||||
// Create the missing service, but forget the port.
|
||||
|
@ -192,7 +241,11 @@ func TestController(t *testing.T) {
|
|||
|
||||
t.Cleanup(func() { client.MustDelete(t, svc.Id) })
|
||||
|
||||
client.WaitForStatusCondition(t, failover.Id, ControllerID, ConditionUnknownDestinationPort(otherServiceRef, "admin"))
|
||||
expectedComputedFP = &pbcatalog.ComputedFailoverPolicy{
|
||||
PortConfigs: failoverData.PortConfigs,
|
||||
BoundReferences: []*pbresource.Reference{apiServiceRef, otherServiceRef},
|
||||
}
|
||||
waitAndAssertComputedFailoverPolicy(t, client, failover.Id, expectedComputedFP, ConditionUnknownDestinationPort(otherServiceRef, "admin"))
|
||||
t.Logf("reconciled to missing dest port other:admin")
|
||||
|
||||
// fix the destination leg's port
|
||||
|
@ -274,7 +327,24 @@ func TestController(t *testing.T) {
|
|||
|
||||
t.Cleanup(func() { client.MustDelete(t, failover.Id) })
|
||||
|
||||
client.WaitForStatusCondition(t, failover.Id, ControllerID, ConditionUnknownDestinationPort(otherServiceRef, "bar"))
|
||||
expectedComputedFP = &pbcatalog.ComputedFailoverPolicy{
|
||||
PortConfigs: map[string]*pbcatalog.FailoverConfig{
|
||||
"foo": {
|
||||
Destinations: []*pbcatalog.FailoverDestination{{
|
||||
Ref: otherServiceRef,
|
||||
Port: "foo",
|
||||
}},
|
||||
},
|
||||
"bar": {
|
||||
Destinations: []*pbcatalog.FailoverDestination{{
|
||||
Ref: otherServiceRef,
|
||||
Port: "bar",
|
||||
}},
|
||||
},
|
||||
},
|
||||
BoundReferences: []*pbresource.Reference{apiServiceRef, otherServiceRef},
|
||||
}
|
||||
waitAndAssertComputedFailoverPolicy(t, client, failover.Id, expectedComputedFP, ConditionUnknownDestinationPort(otherServiceRef, "bar"))
|
||||
t.Logf("reconciled to missing dest port other:bar")
|
||||
|
||||
// and fix it the silly way by removing it from api+failover
|
||||
|
@ -294,7 +364,18 @@ func TestController(t *testing.T) {
|
|||
|
||||
t.Cleanup(func() { client.MustDelete(t, svc.Id) })
|
||||
|
||||
client.WaitForStatusCondition(t, failover.Id, ControllerID, ConditionOK)
|
||||
expectedComputedFP = &pbcatalog.ComputedFailoverPolicy{
|
||||
PortConfigs: map[string]*pbcatalog.FailoverConfig{
|
||||
"foo": {
|
||||
Destinations: []*pbcatalog.FailoverDestination{{
|
||||
Ref: otherServiceRef,
|
||||
Port: "foo",
|
||||
}},
|
||||
},
|
||||
},
|
||||
BoundReferences: []*pbresource.Reference{apiServiceRef, otherServiceRef},
|
||||
}
|
||||
waitAndAssertComputedFailoverPolicy(t, client, failover.Id, expectedComputedFP, ConditionOK)
|
||||
t.Logf("reconciled to accepted")
|
||||
})
|
||||
}
|
||||
|
@ -303,3 +384,15 @@ func TestController(t *testing.T) {
|
|||
func tenancySubTestName(tenancy *pbresource.Tenancy) string {
|
||||
return fmt.Sprintf("%s_Namespace_%s_Partition", tenancy.Namespace, tenancy.Partition)
|
||||
}
|
||||
|
||||
func waitAndAssertComputedFailoverPolicy(t *testing.T, client *rtest.Client, failoverId *pbresource.ID, expectedComputedFP *pbcatalog.ComputedFailoverPolicy, cond *pbresource.Condition) {
|
||||
cfpID := resource.ReplaceType(pbcatalog.ComputedFailoverPolicyType, failoverId)
|
||||
client.WaitForReconciliation(t, cfpID, ControllerID)
|
||||
client.WaitForStatusCondition(t, failoverId, ControllerID, cond)
|
||||
client.WaitForStatusCondition(t, cfpID, ControllerID, cond)
|
||||
client.WaitForResourceState(t, cfpID, func(t rtest.T, r *pbresource.Resource) {
|
||||
computedFp := client.RequireResourceExists(t, cfpID)
|
||||
decodedComputedFp := rtest.MustDecode[*pbcatalog.ComputedFailoverPolicy](t, computedFp)
|
||||
prototest.AssertDeepEqual(t, expectedComputedFP, decodedComputedFp.Data)
|
||||
})
|
||||
}
|
||||
|
|
|
@ -0,0 +1,12 @@
|
|||
// Copyright (c) HashiCorp, Inc.
|
||||
// SPDX-License-Identifier: BUSL-1.1
|
||||
|
||||
//go:build !consulent
|
||||
|
||||
package expander
|
||||
|
||||
import "github.com/hashicorp/consul/internal/catalog/internal/controllers/failover/expander/expander_ce"
|
||||
|
||||
func GetSamenessGroupExpander() *expander_ce.SamenessGroupExpander {
|
||||
return expander_ce.New()
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
// Copyright (c) HashiCorp, Inc.
|
||||
// SPDX-License-Identifier: BUSL-1.1
|
||||
|
||||
package expander_ce
|
||||
|
||||
import (
|
||||
"github.com/hashicorp/consul/internal/controller"
|
||||
"github.com/hashicorp/consul/internal/controller/cache/index"
|
||||
"github.com/hashicorp/consul/internal/controller/cache/indexers"
|
||||
"github.com/hashicorp/consul/internal/resource"
|
||||
pbcatalog "github.com/hashicorp/consul/proto-public/pbcatalog/v2beta1"
|
||||
"github.com/hashicorp/consul/proto-public/pbresource"
|
||||
)
|
||||
|
||||
type SamenessGroupExpander struct{}
|
||||
|
||||
func New() *SamenessGroupExpander {
|
||||
return &SamenessGroupExpander{}
|
||||
}
|
||||
|
||||
func (sgE *SamenessGroupExpander) ComputeFailoverDestinationsFromSamenessGroup(rt controller.Runtime, id *pbresource.ID, sg string, port string) ([]*pbcatalog.FailoverDestination, string, error) {
|
||||
//no - op for CE
|
||||
return nil, "", nil
|
||||
}
|
||||
|
||||
const sgIndexName = "samenessGroupIndex"
|
||||
|
||||
func (sgE *SamenessGroupExpander) GetSamenessGroupIndex() *index.Index {
|
||||
return indexers.DecodedMultiIndexer(
|
||||
sgIndexName,
|
||||
index.ReferenceOrIDFromArgs,
|
||||
func(r *resource.DecodedResource[*pbcatalog.FailoverPolicy]) (bool, [][]byte, error) {
|
||||
//no - op for CE
|
||||
return false, nil, nil
|
||||
|
||||
},
|
||||
)
|
||||
}
|
|
@ -0,0 +1,67 @@
|
|||
// Copyright (c) HashiCorp, Inc.
|
||||
// SPDX-License-Identifier: BUSL-1.1
|
||||
|
||||
package expander_ce
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/consul/internal/controller"
|
||||
"github.com/hashicorp/consul/internal/controller/cache"
|
||||
"github.com/hashicorp/consul/internal/resource"
|
||||
rtest "github.com/hashicorp/consul/internal/resource/resourcetest"
|
||||
pbcatalog "github.com/hashicorp/consul/proto-public/pbcatalog/v2beta1"
|
||||
pbmulticluster "github.com/hashicorp/consul/proto-public/pbmulticluster/v2beta1"
|
||||
"github.com/hashicorp/consul/proto-public/pbresource"
|
||||
"github.com/hashicorp/consul/sdk/testutil"
|
||||
"github.com/stretchr/testify/require"
|
||||
"github.com/stretchr/testify/suite"
|
||||
)
|
||||
|
||||
type expanderSuite struct {
|
||||
suite.Suite
|
||||
ctx context.Context
|
||||
cache cache.Cache
|
||||
rt controller.Runtime
|
||||
tenancies []*pbresource.Tenancy
|
||||
|
||||
samenessGroupExpander *SamenessGroupExpander
|
||||
}
|
||||
|
||||
func (suite *expanderSuite) SetupTest() {
|
||||
suite.ctx = testutil.TestContext(suite.T())
|
||||
suite.tenancies = rtest.TestTenancies()
|
||||
|
||||
suite.samenessGroupExpander = New()
|
||||
|
||||
suite.cache = cache.New()
|
||||
suite.cache.AddType(pbmulticluster.SamenessGroupType)
|
||||
suite.rt = controller.Runtime{
|
||||
Cache: suite.cache,
|
||||
Logger: testutil.Logger(suite.T()),
|
||||
}
|
||||
}
|
||||
|
||||
func TestExpander(t *testing.T) {
|
||||
suite.Run(t, new(expanderSuite))
|
||||
}
|
||||
func (suite *expanderSuite) Test_ComputeFailoverDestinationsFromSamenessGroup() {
|
||||
fpData := &pbcatalog.FailoverPolicy{
|
||||
PortConfigs: map[string]*pbcatalog.FailoverConfig{
|
||||
"http": {
|
||||
SamenessGroup: "sg1",
|
||||
},
|
||||
},
|
||||
}
|
||||
fp := rtest.Resource(pbcatalog.FailoverPolicyType, "apisvc").
|
||||
WithData(suite.T(), fpData).
|
||||
WithTenancy(resource.DefaultNamespacedTenancy()).
|
||||
Build()
|
||||
decFp, err := resource.Decode[*pbcatalog.FailoverPolicy](fp)
|
||||
require.NoError(suite.T(), err)
|
||||
dests, sg, err := suite.samenessGroupExpander.ComputeFailoverDestinationsFromSamenessGroup(suite.rt, decFp.Id, "sg1", "http")
|
||||
require.NoError(suite.T(), err)
|
||||
require.Nil(suite.T(), dests)
|
||||
require.Equal(suite.T(), "", sg)
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
// Copyright (c) HashiCorp, Inc.
|
||||
// SPDX-License-Identifier: BUSL-1.1
|
||||
|
||||
package expander
|
||||
|
||||
import (
|
||||
"github.com/hashicorp/consul/internal/controller"
|
||||
"github.com/hashicorp/consul/internal/controller/cache/index"
|
||||
pbcatalog "github.com/hashicorp/consul/proto-public/pbcatalog/v2beta1"
|
||||
"github.com/hashicorp/consul/proto-public/pbresource"
|
||||
)
|
||||
|
||||
// SamenessgroupExpander is used to expand sameness group for a ComputedFailover resource
|
||||
type SamenessGroupExpander interface {
|
||||
ComputeFailoverDestinationsFromSamenessGroup(rt controller.Runtime, id *pbresource.ID, sg string, port string) ([]*pbcatalog.FailoverDestination, string, error)
|
||||
GetSamenessGroupIndex() *index.Index
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
// Copyright (c) HashiCorp, Inc.
|
||||
// SPDX-License-Identifier: BUSL-1.1
|
||||
|
||||
//go:build !consulent
|
||||
|
||||
package failover
|
||||
|
||||
import (
|
||||
"github.com/hashicorp/consul/internal/controller"
|
||||
)
|
||||
|
||||
func registerEnterpriseControllerWatchers(ctrl *controller.Controller) *controller.Controller {
|
||||
return ctrl
|
||||
}
|
|
@ -29,6 +29,9 @@ const (
|
|||
|
||||
UsingMeshDestinationPortReason = "UsingMeshDestinationPort"
|
||||
UsingMeshDestinationPortMessagePrefix = "port is a special unroutable mesh port on destination service: "
|
||||
|
||||
MissingSamenessGroupReason = "MissingSamenessGroup"
|
||||
MissingSamenessGroupMessagePrefix = "referenced sameness group does not exist: "
|
||||
)
|
||||
|
||||
var (
|
||||
|
@ -82,3 +85,12 @@ func ConditionUsingMeshDestinationPort(ref *pbresource.Reference, port string) *
|
|||
Message: UnknownDestinationPortMessagePrefix + port + " on " + resource.ReferenceToString(ref),
|
||||
}
|
||||
}
|
||||
|
||||
func ConditionMissingSamenessGroup(ref *pbresource.Reference) *pbresource.Condition {
|
||||
return &pbresource.Condition{
|
||||
Type: StatusConditionAccepted,
|
||||
State: pbresource.Condition_STATE_FALSE,
|
||||
Reason: MissingSamenessGroupReason,
|
||||
Message: MissingSamenessGroupMessagePrefix + resource.ReferenceToString(ref),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,6 +6,7 @@ package controllers
|
|||
import (
|
||||
"github.com/hashicorp/consul/internal/catalog/internal/controllers/endpoints"
|
||||
"github.com/hashicorp/consul/internal/catalog/internal/controllers/failover"
|
||||
"github.com/hashicorp/consul/internal/catalog/internal/controllers/failover/expander"
|
||||
"github.com/hashicorp/consul/internal/catalog/internal/controllers/nodehealth"
|
||||
"github.com/hashicorp/consul/internal/catalog/internal/controllers/workloadhealth"
|
||||
"github.com/hashicorp/consul/internal/controller"
|
||||
|
@ -15,5 +16,5 @@ func Register(mgr *controller.Manager) {
|
|||
mgr.Register(nodehealth.NodeHealthController())
|
||||
mgr.Register(workloadhealth.WorkloadHealthController())
|
||||
mgr.Register(endpoints.ServiceEndpointsController())
|
||||
mgr.Register(failover.FailoverPolicyController())
|
||||
mgr.Register(failover.FailoverPolicyController(expander.GetSamenessGroupExpander()))
|
||||
}
|
||||
|
|
|
@ -30,26 +30,49 @@ func RegisterComputedFailoverPolicy(r resource.Registry) {
|
|||
var ValidateComputedFailoverPolicy = resource.DecodeAndValidate(validateComputedFailoverPolicy)
|
||||
|
||||
func validateComputedFailoverPolicy(res *DecodedComputedFailoverPolicy) error {
|
||||
if res.Data.Config != nil && res.Data.Config.SamenessGroup != "" {
|
||||
return fmt.Errorf(`invalid "config" field: computed failover policy cannot have a sameness_group`)
|
||||
if res.Data.Config != nil {
|
||||
return fmt.Errorf(`invalid "config" field: computed failover policy cannot have a config`)
|
||||
}
|
||||
for _, fc := range res.Data.PortConfigs {
|
||||
if fc.GetSamenessGroup() != "" {
|
||||
return fmt.Errorf(`invalid "config" field: computed failover policy cannot have a sameness_group`)
|
||||
}
|
||||
}
|
||||
dfp := convertToDecodedFailoverPolicy(res)
|
||||
return validateFailoverPolicy(dfp)
|
||||
return validateCommonFailoverConfigs(&pbcatalog.FailoverPolicy{
|
||||
Config: res.Data.Config,
|
||||
PortConfigs: res.Data.PortConfigs,
|
||||
})
|
||||
}
|
||||
|
||||
func aclWriteHookComputedFailoverPolicy(authorizer acl.Authorizer, authzContext *acl.AuthorizerContext, res *DecodedComputedFailoverPolicy) error {
|
||||
dfp := convertToDecodedFailoverPolicy(res)
|
||||
return aclWriteHookFailoverPolicy(authorizer, authzContext, dfp)
|
||||
}
|
||||
// FailoverPolicy is name-aligned with Service
|
||||
serviceName := res.Id.Name
|
||||
|
||||
func convertToDecodedFailoverPolicy(res *DecodedComputedFailoverPolicy) *DecodedFailoverPolicy {
|
||||
dfp := &DecodedFailoverPolicy{}
|
||||
dfp.Data = (*pbcatalog.FailoverPolicy)(res.GetData())
|
||||
dfp.Resource = res.GetResource()
|
||||
return dfp
|
||||
// Check service:write permissions on the service this is controlling.
|
||||
if err := authorizer.ToAllowAuthorizer().ServiceWriteAllowed(serviceName, authzContext); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Ensure you have service:read on any destination that may be affected by
|
||||
// traffic FROM this config change.
|
||||
if res.Data.Config != nil {
|
||||
for _, dest := range res.Data.Config.Destinations {
|
||||
destAuthzContext := resource.AuthorizerContext(dest.Ref.GetTenancy())
|
||||
destServiceName := dest.Ref.GetName()
|
||||
if err := authorizer.ToAllowAuthorizer().ServiceReadAllowed(destServiceName, destAuthzContext); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
for _, pc := range res.Data.PortConfigs {
|
||||
for _, dest := range pc.Destinations {
|
||||
destAuthzContext := resource.AuthorizerContext(dest.Ref.GetTenancy())
|
||||
destServiceName := dest.Ref.GetName()
|
||||
if err := authorizer.ToAllowAuthorizer().ServiceReadAllowed(destServiceName, destAuthzContext); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -56,14 +56,14 @@ func TestValidateComputedFailoverPolicy(t *testing.T) {
|
|||
testutil.RequireErrorContains(t, err, tc.expectErr)
|
||||
}
|
||||
}
|
||||
cases := convertToComputedFailverPolicyTestCases(getCommonTestCases())
|
||||
cases["plain config: sameness_group"] = computedFailoverTestcase{
|
||||
cases := convertToComputedFailverPolicyTestCases(getComputedFailoverCases())
|
||||
cases["plain config: config"] = computedFailoverTestcase{
|
||||
failover: &pbcatalog.ComputedFailoverPolicy{
|
||||
Config: &pbcatalog.FailoverConfig{
|
||||
SamenessGroup: "test",
|
||||
},
|
||||
},
|
||||
expectErr: `invalid "config" field: computed failover policy cannot have a sameness_group`,
|
||||
expectErr: `invalid "config" field: computed failover policy cannot have a config`,
|
||||
}
|
||||
cases["ported config: sameness_group"] = computedFailoverTestcase{
|
||||
failover: &pbcatalog.ComputedFailoverPolicy{
|
||||
|
@ -109,7 +109,7 @@ func testFailOverPolicyAcls(t *testing.T, isComputedFailoverPolicy bool) {
|
|||
if isComputedFailoverPolicy {
|
||||
typ = pbcatalog.ComputedFailoverPolicyType
|
||||
cfgData = &pbcatalog.ComputedFailoverPolicy{
|
||||
Config: cfgDests,
|
||||
PortConfigs: portedCfgDests,
|
||||
}
|
||||
portedCfgData = &pbcatalog.ComputedFailoverPolicy{
|
||||
PortConfigs: portedCfgDests,
|
||||
|
|
|
@ -115,19 +115,27 @@ func validateFailoverPolicy(res *DecodedFailoverPolicy) error {
|
|||
})
|
||||
}
|
||||
|
||||
if res.Data.Config != nil {
|
||||
if err := validateCommonFailoverConfigs(res.Data); err != nil {
|
||||
merr = multierror.Append(merr, err)
|
||||
}
|
||||
return merr
|
||||
}
|
||||
|
||||
func validateCommonFailoverConfigs(res *pbcatalog.FailoverPolicy) error {
|
||||
var merr error
|
||||
if res.Config != nil {
|
||||
wrapConfigErr := func(err error) error {
|
||||
return resource.ErrInvalidField{
|
||||
Name: "config",
|
||||
Wrapped: err,
|
||||
}
|
||||
}
|
||||
if cfgErr := validateFailoverConfig(res.Data.Config, false, wrapConfigErr); cfgErr != nil {
|
||||
if cfgErr := validateFailoverConfig(res.Config, false, wrapConfigErr); cfgErr != nil {
|
||||
merr = multierror.Append(merr, cfgErr)
|
||||
}
|
||||
}
|
||||
|
||||
for portName, pc := range res.Data.PortConfigs {
|
||||
for portName, pc := range res.PortConfigs {
|
||||
wrapConfigErr := func(err error) error {
|
||||
return resource.ErrInvalidMapValue{
|
||||
Map: "port_configs",
|
||||
|
@ -147,7 +155,6 @@ func validateFailoverPolicy(res *DecodedFailoverPolicy) error {
|
|||
merr = multierror.Append(merr, cfgErr)
|
||||
}
|
||||
|
||||
// TODO: should sameness group be a ref once that's a resource?
|
||||
}
|
||||
|
||||
return merr
|
||||
|
|
|
@ -241,7 +241,112 @@ func addFailoverConfigSamenessGroupCases(fpcases map[string]failoverTestcase) {
|
|||
}
|
||||
}
|
||||
|
||||
func getCommonTestCases() map[string]failoverTestcase {
|
||||
func getComputedFailoverCases() map[string]failoverTestcase {
|
||||
configCases := getCommonConfigCases()
|
||||
fpcases := getCommonFpCases()
|
||||
for name, tc := range configCases {
|
||||
fpcases["ported config: "+name] = failoverTestcase{
|
||||
failover: &pbcatalog.FailoverPolicy{
|
||||
PortConfigs: map[string]*pbcatalog.FailoverConfig{
|
||||
"http": proto.Clone(tc.config).(*pbcatalog.FailoverConfig),
|
||||
},
|
||||
},
|
||||
expectErr: maybeWrap(`invalid value of key "http" within port_configs: `, tc.expectErr),
|
||||
}
|
||||
}
|
||||
return fpcases
|
||||
}
|
||||
func getFailoverCases() map[string]failoverTestcase {
|
||||
configCases := getCommonConfigCases()
|
||||
fpcases := getCommonFpCases()
|
||||
fpcases["non-empty: some plain config but no port configs"] = failoverTestcase{
|
||||
failover: &pbcatalog.FailoverPolicy{
|
||||
Config: &pbcatalog.FailoverConfig{
|
||||
Destinations: []*pbcatalog.FailoverDestination{
|
||||
{Ref: newRef(pbcatalog.ServiceType, "api-backup")},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
// plain config
|
||||
fpcases["plain config: bad dest: any port name"] = failoverTestcase{
|
||||
failover: &pbcatalog.FailoverPolicy{
|
||||
Config: &pbcatalog.FailoverConfig{
|
||||
Destinations: []*pbcatalog.FailoverDestination{
|
||||
{Ref: newRef(pbcatalog.ServiceType, "api-backup"), Port: "web"},
|
||||
},
|
||||
},
|
||||
},
|
||||
expectErr: `invalid "config" field: invalid element at index 0 of list "destinations": invalid "port" field: ports cannot be specified explicitly for the general failover section since it relies upon port alignment`,
|
||||
}
|
||||
// emptiness
|
||||
fpcases["empty"] = failoverTestcase{
|
||||
failover: &pbcatalog.FailoverPolicy{},
|
||||
expectErr: `invalid "config" field: at least one of config or port_configs must be set`,
|
||||
}
|
||||
|
||||
for name, tc := range configCases {
|
||||
fpcases["plain config: "+name] = failoverTestcase{
|
||||
failover: &pbcatalog.FailoverPolicy{
|
||||
Config: proto.Clone(tc.config).(*pbcatalog.FailoverConfig),
|
||||
},
|
||||
expectErr: maybeWrap(`invalid "config" field: `, tc.expectErr),
|
||||
}
|
||||
|
||||
fpcases["ported config: "+name] = failoverTestcase{
|
||||
failover: &pbcatalog.FailoverPolicy{
|
||||
PortConfigs: map[string]*pbcatalog.FailoverConfig{
|
||||
"http": proto.Clone(tc.config).(*pbcatalog.FailoverConfig),
|
||||
},
|
||||
},
|
||||
expectErr: maybeWrap(`invalid value of key "http" within port_configs: `, tc.expectErr),
|
||||
}
|
||||
}
|
||||
addFailoverConfigSamenessGroupCases(fpcases)
|
||||
return fpcases
|
||||
}
|
||||
func getCommonFpCases() map[string]failoverTestcase {
|
||||
fpcases := map[string]failoverTestcase{
|
||||
"non-empty: one port config but no plain config": {
|
||||
failover: &pbcatalog.FailoverPolicy{
|
||||
PortConfigs: map[string]*pbcatalog.FailoverConfig{
|
||||
"http": {
|
||||
Destinations: []*pbcatalog.FailoverDestination{
|
||||
{Ref: newRef(pbcatalog.ServiceType, "api-backup")},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
// ported config
|
||||
"ported config: bad dest: invalid port name": {
|
||||
failover: &pbcatalog.FailoverPolicy{
|
||||
PortConfigs: map[string]*pbcatalog.FailoverConfig{
|
||||
"http": {
|
||||
Destinations: []*pbcatalog.FailoverDestination{
|
||||
{Ref: newRef(pbcatalog.ServiceType, "api-backup"), Port: "$bad$"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expectErr: `invalid value of key "http" within port_configs: invalid element at index 0 of list "destinations": invalid "port" field: value must match regex: ^[a-z0-9]([a-z0-9\-_]*[a-z0-9])?$`,
|
||||
},
|
||||
"ported config: bad ported in map": {
|
||||
failover: &pbcatalog.FailoverPolicy{
|
||||
PortConfigs: map[string]*pbcatalog.FailoverConfig{
|
||||
"$bad$": {
|
||||
Destinations: []*pbcatalog.FailoverDestination{
|
||||
{Ref: newRef(pbcatalog.ServiceType, "api-backup"), Port: "http"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expectErr: `map port_configs contains an invalid key - "$bad$": value must match regex: ^[a-z0-9]([a-z0-9\-_]*[a-z0-9])?$`,
|
||||
},
|
||||
}
|
||||
return fpcases
|
||||
}
|
||||
func getCommonConfigCases() map[string]configTestcase {
|
||||
configCases := map[string]configTestcase{
|
||||
"dest without sameness": {
|
||||
config: &pbcatalog.FailoverConfig{
|
||||
|
@ -323,89 +428,7 @@ func getCommonTestCases() map[string]failoverTestcase {
|
|||
},
|
||||
},
|
||||
}
|
||||
|
||||
fpcases := map[string]failoverTestcase{
|
||||
// emptiness
|
||||
"empty": {
|
||||
failover: &pbcatalog.FailoverPolicy{},
|
||||
expectErr: `invalid "config" field: at least one of config or port_configs must be set`,
|
||||
},
|
||||
"non-empty: one port config but no plain config": {
|
||||
failover: &pbcatalog.FailoverPolicy{
|
||||
PortConfigs: map[string]*pbcatalog.FailoverConfig{
|
||||
"http": {
|
||||
Destinations: []*pbcatalog.FailoverDestination{
|
||||
{Ref: newRef(pbcatalog.ServiceType, "api-backup")},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
"non-empty: some plain config but no port configs": {
|
||||
failover: &pbcatalog.FailoverPolicy{
|
||||
Config: &pbcatalog.FailoverConfig{
|
||||
Destinations: []*pbcatalog.FailoverDestination{
|
||||
{Ref: newRef(pbcatalog.ServiceType, "api-backup")},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
// plain config
|
||||
"plain config: bad dest: any port name": {
|
||||
failover: &pbcatalog.FailoverPolicy{
|
||||
Config: &pbcatalog.FailoverConfig{
|
||||
Destinations: []*pbcatalog.FailoverDestination{
|
||||
{Ref: newRef(pbcatalog.ServiceType, "api-backup"), Port: "web"},
|
||||
},
|
||||
},
|
||||
},
|
||||
expectErr: `invalid "config" field: invalid element at index 0 of list "destinations": invalid "port" field: ports cannot be specified explicitly for the general failover section since it relies upon port alignment`,
|
||||
},
|
||||
// ported config
|
||||
"ported config: bad dest: invalid port name": {
|
||||
failover: &pbcatalog.FailoverPolicy{
|
||||
PortConfigs: map[string]*pbcatalog.FailoverConfig{
|
||||
"http": {
|
||||
Destinations: []*pbcatalog.FailoverDestination{
|
||||
{Ref: newRef(pbcatalog.ServiceType, "api-backup"), Port: "$bad$"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expectErr: `invalid value of key "http" within port_configs: invalid element at index 0 of list "destinations": invalid "port" field: value must match regex: ^[a-z0-9]([a-z0-9\-_]*[a-z0-9])?$`,
|
||||
},
|
||||
"ported config: bad ported in map": {
|
||||
failover: &pbcatalog.FailoverPolicy{
|
||||
PortConfigs: map[string]*pbcatalog.FailoverConfig{
|
||||
"$bad$": {
|
||||
Destinations: []*pbcatalog.FailoverDestination{
|
||||
{Ref: newRef(pbcatalog.ServiceType, "api-backup"), Port: "http"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expectErr: `map port_configs contains an invalid key - "$bad$": value must match regex: ^[a-z0-9]([a-z0-9\-_]*[a-z0-9])?$`,
|
||||
},
|
||||
}
|
||||
|
||||
for name, tc := range configCases {
|
||||
fpcases["plain config: "+name] = failoverTestcase{
|
||||
failover: &pbcatalog.FailoverPolicy{
|
||||
Config: proto.Clone(tc.config).(*pbcatalog.FailoverConfig),
|
||||
},
|
||||
expectErr: maybeWrap(`invalid "config" field: `, tc.expectErr),
|
||||
}
|
||||
|
||||
fpcases["ported config: "+name] = failoverTestcase{
|
||||
failover: &pbcatalog.FailoverPolicy{
|
||||
PortConfigs: map[string]*pbcatalog.FailoverConfig{
|
||||
"http": proto.Clone(tc.config).(*pbcatalog.FailoverConfig),
|
||||
},
|
||||
},
|
||||
expectErr: maybeWrap(`invalid value of key "http" within port_configs: `, tc.expectErr),
|
||||
}
|
||||
}
|
||||
return fpcases
|
||||
return configCases
|
||||
}
|
||||
|
||||
func TestValidateFailoverPolicy(t *testing.T) {
|
||||
|
@ -434,8 +457,7 @@ func TestValidateFailoverPolicy(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
cases := getCommonTestCases()
|
||||
addFailoverConfigSamenessGroupCases(cases)
|
||||
cases := getFailoverCases()
|
||||
|
||||
for name, tc := range cases {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
package catalogv2beta1
|
||||
|
||||
import (
|
||||
_ "github.com/hashicorp/consul/proto-public/pbresource"
|
||||
pbresource "github.com/hashicorp/consul/proto-public/pbresource"
|
||||
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
|
||||
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
|
||||
reflect "reflect"
|
||||
|
@ -35,6 +35,9 @@ type ComputedFailoverPolicy struct {
|
|||
// PortConfigs defines failover for a specific port on this service and takes
|
||||
// precedence over Config.
|
||||
PortConfigs map[string]*FailoverConfig `protobuf:"bytes,2,rep,name=port_configs,json=portConfigs,proto3" json:"port_configs,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"`
|
||||
// BoundReferences is a slice of mixed type references of resources that were
|
||||
// involved in the formulation of this resource.
|
||||
BoundReferences []*pbresource.Reference `protobuf:"bytes,3,rep,name=bound_references,json=boundReferences,proto3" json:"bound_references,omitempty"`
|
||||
}
|
||||
|
||||
func (x *ComputedFailoverPolicy) Reset() {
|
||||
|
@ -83,6 +86,13 @@ func (x *ComputedFailoverPolicy) GetPortConfigs() map[string]*FailoverConfig {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (x *ComputedFailoverPolicy) GetBoundReferences() []*pbresource.Reference {
|
||||
if x != nil {
|
||||
return x.BoundReferences
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
var File_pbcatalog_v2beta1_computed_failover_policy_proto protoreflect.FileDescriptor
|
||||
|
||||
var file_pbcatalog_v2beta1_computed_failover_policy_proto_rawDesc = []byte{
|
||||
|
@ -95,48 +105,54 @@ var file_pbcatalog_v2beta1_computed_failover_policy_proto_rawDesc = []byte{
|
|||
0x76, 0x32, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2f, 0x66, 0x61, 0x69, 0x6c, 0x6f, 0x76, 0x65, 0x72,
|
||||
0x5f, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1c, 0x70,
|
||||
0x62, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2f, 0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x61,
|
||||
0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xca, 0x02, 0x0a, 0x16,
|
||||
0x43, 0x6f, 0x6d, 0x70, 0x75, 0x74, 0x65, 0x64, 0x46, 0x61, 0x69, 0x6c, 0x6f, 0x76, 0x65, 0x72,
|
||||
0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x12, 0x48, 0x0a, 0x06, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67,
|
||||
0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x30, 0x2e, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f,
|
||||
0x72, 0x70, 0x2e, 0x63, 0x6f, 0x6e, 0x73, 0x75, 0x6c, 0x2e, 0x63, 0x61, 0x74, 0x61, 0x6c, 0x6f,
|
||||
0x67, 0x2e, 0x76, 0x32, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x46, 0x61, 0x69, 0x6c, 0x6f, 0x76,
|
||||
0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x06, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67,
|
||||
0x12, 0x6c, 0x0a, 0x0c, 0x70, 0x6f, 0x72, 0x74, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x73,
|
||||
0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x49, 0x2e, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f,
|
||||
0x72, 0x70, 0x2e, 0x63, 0x6f, 0x6e, 0x73, 0x75, 0x6c, 0x2e, 0x63, 0x61, 0x74, 0x61, 0x6c, 0x6f,
|
||||
0x67, 0x2e, 0x76, 0x32, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x43, 0x6f, 0x6d, 0x70, 0x75, 0x74,
|
||||
0x65, 0x64, 0x46, 0x61, 0x69, 0x6c, 0x6f, 0x76, 0x65, 0x72, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79,
|
||||
0x2e, 0x50, 0x6f, 0x72, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x73, 0x45, 0x6e, 0x74, 0x72,
|
||||
0x79, 0x52, 0x0b, 0x70, 0x6f, 0x72, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x73, 0x1a, 0x70,
|
||||
0x0a, 0x10, 0x50, 0x6f, 0x72, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x73, 0x45, 0x6e, 0x74,
|
||||
0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52,
|
||||
0x03, 0x6b, 0x65, 0x79, 0x12, 0x46, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20,
|
||||
0x01, 0x28, 0x0b, 0x32, 0x30, 0x2e, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2e,
|
||||
0x63, 0x6f, 0x6e, 0x73, 0x75, 0x6c, 0x2e, 0x63, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x2e, 0x76,
|
||||
0x32, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x46, 0x61, 0x69, 0x6c, 0x6f, 0x76, 0x65, 0x72, 0x43,
|
||||
0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01,
|
||||
0x3a, 0x06, 0xa2, 0x93, 0x04, 0x02, 0x08, 0x03, 0x42, 0xb1, 0x02, 0x0a, 0x24, 0x63, 0x6f, 0x6d,
|
||||
0x2e, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2e, 0x63, 0x6f, 0x6e, 0x73, 0x75,
|
||||
0x6c, 0x2e, 0x63, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x2e, 0x76, 0x32, 0x62, 0x65, 0x74, 0x61,
|
||||
0x31, 0x42, 0x1b, 0x43, 0x6f, 0x6d, 0x70, 0x75, 0x74, 0x65, 0x64, 0x46, 0x61, 0x69, 0x6c, 0x6f,
|
||||
0x76, 0x65, 0x72, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01,
|
||||
0x5a, 0x49, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x68, 0x61, 0x73,
|
||||
0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2f, 0x63, 0x6f, 0x6e, 0x73, 0x75, 0x6c, 0x2f, 0x70, 0x72,
|
||||
0x6f, 0x74, 0x6f, 0x2d, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x2f, 0x70, 0x62, 0x63, 0x61, 0x74,
|
||||
0x61, 0x6c, 0x6f, 0x67, 0x2f, 0x76, 0x32, 0x62, 0x65, 0x74, 0x61, 0x31, 0x3b, 0x63, 0x61, 0x74,
|
||||
0x61, 0x6c, 0x6f, 0x67, 0x76, 0x32, 0x62, 0x65, 0x74, 0x61, 0x31, 0xa2, 0x02, 0x03, 0x48, 0x43,
|
||||
0x43, 0xaa, 0x02, 0x20, 0x48, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2e, 0x43, 0x6f,
|
||||
0x6e, 0x73, 0x75, 0x6c, 0x2e, 0x43, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x2e, 0x56, 0x32, 0x62,
|
||||
0x65, 0x74, 0x61, 0x31, 0xca, 0x02, 0x20, 0x48, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70,
|
||||
0x5c, 0x43, 0x6f, 0x6e, 0x73, 0x75, 0x6c, 0x5c, 0x43, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x5c,
|
||||
0x56, 0x32, 0x62, 0x65, 0x74, 0x61, 0x31, 0xe2, 0x02, 0x2c, 0x48, 0x61, 0x73, 0x68, 0x69, 0x63,
|
||||
0x6f, 0x72, 0x70, 0x5c, 0x43, 0x6f, 0x6e, 0x73, 0x75, 0x6c, 0x5c, 0x43, 0x61, 0x74, 0x61, 0x6c,
|
||||
0x6f, 0x67, 0x5c, 0x56, 0x32, 0x62, 0x65, 0x74, 0x61, 0x31, 0x5c, 0x47, 0x50, 0x42, 0x4d, 0x65,
|
||||
0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0xea, 0x02, 0x23, 0x48, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f,
|
||||
0x72, 0x70, 0x3a, 0x3a, 0x43, 0x6f, 0x6e, 0x73, 0x75, 0x6c, 0x3a, 0x3a, 0x43, 0x61, 0x74, 0x61,
|
||||
0x6c, 0x6f, 0x67, 0x3a, 0x3a, 0x56, 0x32, 0x62, 0x65, 0x74, 0x61, 0x31, 0x62, 0x06, 0x70, 0x72,
|
||||
0x6f, 0x74, 0x6f, 0x33,
|
||||
0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x19, 0x70, 0x62, 0x72,
|
||||
0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2f, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65,
|
||||
0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x9b, 0x03, 0x0a, 0x16, 0x43, 0x6f, 0x6d, 0x70, 0x75,
|
||||
0x74, 0x65, 0x64, 0x46, 0x61, 0x69, 0x6c, 0x6f, 0x76, 0x65, 0x72, 0x50, 0x6f, 0x6c, 0x69, 0x63,
|
||||
0x79, 0x12, 0x48, 0x0a, 0x06, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28,
|
||||
0x0b, 0x32, 0x30, 0x2e, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2e, 0x63, 0x6f,
|
||||
0x6e, 0x73, 0x75, 0x6c, 0x2e, 0x63, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x2e, 0x76, 0x32, 0x62,
|
||||
0x65, 0x74, 0x61, 0x31, 0x2e, 0x46, 0x61, 0x69, 0x6c, 0x6f, 0x76, 0x65, 0x72, 0x43, 0x6f, 0x6e,
|
||||
0x66, 0x69, 0x67, 0x52, 0x06, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x6c, 0x0a, 0x0c, 0x70,
|
||||
0x6f, 0x72, 0x74, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28,
|
||||
0x0b, 0x32, 0x49, 0x2e, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2e, 0x63, 0x6f,
|
||||
0x6e, 0x73, 0x75, 0x6c, 0x2e, 0x63, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x2e, 0x76, 0x32, 0x62,
|
||||
0x65, 0x74, 0x61, 0x31, 0x2e, 0x43, 0x6f, 0x6d, 0x70, 0x75, 0x74, 0x65, 0x64, 0x46, 0x61, 0x69,
|
||||
0x6c, 0x6f, 0x76, 0x65, 0x72, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x50, 0x6f, 0x72, 0x74,
|
||||
0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0b, 0x70, 0x6f,
|
||||
0x72, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x73, 0x12, 0x4f, 0x0a, 0x10, 0x62, 0x6f, 0x75,
|
||||
0x6e, 0x64, 0x5f, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x73, 0x18, 0x03, 0x20,
|
||||
0x03, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2e,
|
||||
0x63, 0x6f, 0x6e, 0x73, 0x75, 0x6c, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e,
|
||||
0x52, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x52, 0x0f, 0x62, 0x6f, 0x75, 0x6e, 0x64,
|
||||
0x52, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x73, 0x1a, 0x70, 0x0a, 0x10, 0x50, 0x6f,
|
||||
0x72, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10,
|
||||
0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79,
|
||||
0x12, 0x46, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32,
|
||||
0x30, 0x2e, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2e, 0x63, 0x6f, 0x6e, 0x73,
|
||||
0x75, 0x6c, 0x2e, 0x63, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x2e, 0x76, 0x32, 0x62, 0x65, 0x74,
|
||||
0x61, 0x31, 0x2e, 0x46, 0x61, 0x69, 0x6c, 0x6f, 0x76, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69,
|
||||
0x67, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x3a, 0x06, 0xa2, 0x93,
|
||||
0x04, 0x02, 0x08, 0x03, 0x42, 0xb1, 0x02, 0x0a, 0x24, 0x63, 0x6f, 0x6d, 0x2e, 0x68, 0x61, 0x73,
|
||||
0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2e, 0x63, 0x6f, 0x6e, 0x73, 0x75, 0x6c, 0x2e, 0x63, 0x61,
|
||||
0x74, 0x61, 0x6c, 0x6f, 0x67, 0x2e, 0x76, 0x32, 0x62, 0x65, 0x74, 0x61, 0x31, 0x42, 0x1b, 0x43,
|
||||
0x6f, 0x6d, 0x70, 0x75, 0x74, 0x65, 0x64, 0x46, 0x61, 0x69, 0x6c, 0x6f, 0x76, 0x65, 0x72, 0x50,
|
||||
0x6f, 0x6c, 0x69, 0x63, 0x79, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x49, 0x67, 0x69,
|
||||
0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f,
|
||||
0x72, 0x70, 0x2f, 0x63, 0x6f, 0x6e, 0x73, 0x75, 0x6c, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2d,
|
||||
0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x2f, 0x70, 0x62, 0x63, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67,
|
||||
0x2f, 0x76, 0x32, 0x62, 0x65, 0x74, 0x61, 0x31, 0x3b, 0x63, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67,
|
||||
0x76, 0x32, 0x62, 0x65, 0x74, 0x61, 0x31, 0xa2, 0x02, 0x03, 0x48, 0x43, 0x43, 0xaa, 0x02, 0x20,
|
||||
0x48, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2e, 0x43, 0x6f, 0x6e, 0x73, 0x75, 0x6c,
|
||||
0x2e, 0x43, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x2e, 0x56, 0x32, 0x62, 0x65, 0x74, 0x61, 0x31,
|
||||
0xca, 0x02, 0x20, 0x48, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x5c, 0x43, 0x6f, 0x6e,
|
||||
0x73, 0x75, 0x6c, 0x5c, 0x43, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x5c, 0x56, 0x32, 0x62, 0x65,
|
||||
0x74, 0x61, 0x31, 0xe2, 0x02, 0x2c, 0x48, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x5c,
|
||||
0x43, 0x6f, 0x6e, 0x73, 0x75, 0x6c, 0x5c, 0x43, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x5c, 0x56,
|
||||
0x32, 0x62, 0x65, 0x74, 0x61, 0x31, 0x5c, 0x47, 0x50, 0x42, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61,
|
||||
0x74, 0x61, 0xea, 0x02, 0x23, 0x48, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x3a, 0x3a,
|
||||
0x43, 0x6f, 0x6e, 0x73, 0x75, 0x6c, 0x3a, 0x3a, 0x43, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x3a,
|
||||
0x3a, 0x56, 0x32, 0x62, 0x65, 0x74, 0x61, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
||||
}
|
||||
|
||||
var (
|
||||
|
@ -156,16 +172,18 @@ var file_pbcatalog_v2beta1_computed_failover_policy_proto_goTypes = []interface{
|
|||
(*ComputedFailoverPolicy)(nil), // 0: hashicorp.consul.catalog.v2beta1.ComputedFailoverPolicy
|
||||
nil, // 1: hashicorp.consul.catalog.v2beta1.ComputedFailoverPolicy.PortConfigsEntry
|
||||
(*FailoverConfig)(nil), // 2: hashicorp.consul.catalog.v2beta1.FailoverConfig
|
||||
(*pbresource.Reference)(nil), // 3: hashicorp.consul.resource.Reference
|
||||
}
|
||||
var file_pbcatalog_v2beta1_computed_failover_policy_proto_depIdxs = []int32{
|
||||
2, // 0: hashicorp.consul.catalog.v2beta1.ComputedFailoverPolicy.config:type_name -> hashicorp.consul.catalog.v2beta1.FailoverConfig
|
||||
1, // 1: hashicorp.consul.catalog.v2beta1.ComputedFailoverPolicy.port_configs:type_name -> hashicorp.consul.catalog.v2beta1.ComputedFailoverPolicy.PortConfigsEntry
|
||||
2, // 2: hashicorp.consul.catalog.v2beta1.ComputedFailoverPolicy.PortConfigsEntry.value:type_name -> hashicorp.consul.catalog.v2beta1.FailoverConfig
|
||||
3, // [3:3] is the sub-list for method output_type
|
||||
3, // [3:3] is the sub-list for method input_type
|
||||
3, // [3:3] is the sub-list for extension type_name
|
||||
3, // [3:3] is the sub-list for extension extendee
|
||||
0, // [0:3] is the sub-list for field type_name
|
||||
3, // 2: hashicorp.consul.catalog.v2beta1.ComputedFailoverPolicy.bound_references:type_name -> hashicorp.consul.resource.Reference
|
||||
2, // 3: hashicorp.consul.catalog.v2beta1.ComputedFailoverPolicy.PortConfigsEntry.value:type_name -> hashicorp.consul.catalog.v2beta1.FailoverConfig
|
||||
4, // [4:4] is the sub-list for method output_type
|
||||
4, // [4:4] is the sub-list for method input_type
|
||||
4, // [4:4] is the sub-list for extension type_name
|
||||
4, // [4:4] is the sub-list for extension extendee
|
||||
0, // [0:4] is the sub-list for field type_name
|
||||
}
|
||||
|
||||
func init() { file_pbcatalog_v2beta1_computed_failover_policy_proto_init() }
|
||||
|
|
|
@ -7,6 +7,7 @@ package hashicorp.consul.catalog.v2beta1;
|
|||
|
||||
import "pbcatalog/v2beta1/failover_policy.proto";
|
||||
import "pbresource/annotations.proto";
|
||||
import "pbresource/resource.proto";
|
||||
|
||||
// This is a Resource type.
|
||||
message ComputedFailoverPolicy {
|
||||
|
@ -18,4 +19,8 @@ message ComputedFailoverPolicy {
|
|||
// PortConfigs defines failover for a specific port on this service and takes
|
||||
// precedence over Config.
|
||||
map<string, FailoverConfig> port_configs = 2;
|
||||
|
||||
// BoundReferences is a slice of mixed type references of resources that were
|
||||
// involved in the formulation of this resource.
|
||||
repeated hashicorp.consul.resource.Reference bound_references = 3;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue