APIGW Normalize Status Conditions (#16994)

* normalize status conditions for gateways and routes

* Added tests for checking condition status and panic conditions for
validating combinations, added dummy code for fsm store

* get rid of unneeded gateway condition generator struct

* Remove unused file

* run go mod tidy

* Update tests, add conflicted gateway status

* put back removed status for test

* Fix linting violation, remove custom conflicted status

* Update fsm commands oss

* Fix incorrect combination of type/condition/status

* cleaning up from PR review

* Change "invalidCertificate" to be of accepted status

* Move status condition enums into api package

* Update gateways controller and generated code

* Update conditions in fsm oss tests

* run go mod tidy on consul-container module to fix linting

* Fix type for gateway endpoint test

* go mod tidy from changes to api

* go mod tidy on troubleshoot

* Fix route conflicted reason

* fix route conflict reason rename

* Fix text for gateway conflicted status

* Add valid certificate ref condition setting

* Revert change to resolved refs to be handled in future PR
This commit is contained in:
John Maguire 2023-04-24 16:22:55 -04:00 committed by GitHub
parent 001d540afc
commit e47f3216e5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
18 changed files with 835 additions and 431 deletions

View File

@ -13,11 +13,6 @@ import (
"testing"
"time"
"github.com/hashicorp/go-raftchunking"
raftchunkingtypes "github.com/hashicorp/go-raftchunking/types"
"github.com/hashicorp/go-uuid"
"github.com/hashicorp/raft"
"github.com/hashicorp/serf/coordinate"
"github.com/mitchellh/mapstructure"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
@ -34,6 +29,11 @@ import (
"github.com/hashicorp/consul/proto/private/prototest"
"github.com/hashicorp/consul/sdk/testutil"
"github.com/hashicorp/consul/types"
"github.com/hashicorp/go-raftchunking"
raftchunkingtypes "github.com/hashicorp/go-raftchunking/types"
"github.com/hashicorp/go-uuid"
"github.com/hashicorp/raft"
"github.com/hashicorp/serf/coordinate"
)
func generateUUID() (ret string) {
@ -1372,9 +1372,10 @@ func TestFSM_ConfigEntry_StatusCAS(t *testing.T) {
EnterpriseMeta: *structs.DefaultEnterpriseMetaInDefaultPartition(),
Status: structs.Status{
Conditions: []structs.Condition{{
Status: "Foo",
Status: string(api.ConditionStatusTrue),
}},
}}
},
}
// Create a new request.
req := &structs.ConfigEntryRequest{
@ -1403,7 +1404,7 @@ func TestFSM_ConfigEntry_StatusCAS(t *testing.T) {
// do a status update
entry.Status = structs.Status{
Conditions: []structs.Condition{{
Status: "Foo",
Status: string(api.ConditionStatusTrue),
}},
}
req = &structs.ConfigEntryRequest{
@ -1427,7 +1428,7 @@ func TestFSM_ConfigEntry_StatusCAS(t *testing.T) {
entry.RaftIndex.ModifyIndex = 2
conditions := config.(*structs.APIGatewayConfigEntry).Status.Conditions
require.Len(t, conditions, 1)
require.Equal(t, "Foo", conditions[0].Status)
require.Equal(t, string(api.ConditionStatusTrue), conditions[0].Status)
}
// attempt to change the status with a regular update and make sure it's ignored
@ -1456,7 +1457,7 @@ func TestFSM_ConfigEntry_StatusCAS(t *testing.T) {
require.NoError(t, err)
conditions := config.(*structs.APIGatewayConfigEntry).Status.Conditions
require.Len(t, conditions, 1)
require.Equal(t, "Foo", conditions[0].Status)
require.Equal(t, string(api.ConditionStatusTrue), conditions[0].Status)
}
}

View File

@ -1,80 +0,0 @@
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: MPL-2.0
package consul
import (
"github.com/hashicorp/consul/acl"
"github.com/hashicorp/consul/agent/consul/fsm"
"github.com/hashicorp/consul/agent/structs"
)
// FSMDataStore implements the DataStore interface using the Consul server and finite state manager.
type FSMDataStore struct {
server *Server
fsm *fsm.FSM
}
func NewFSMDataStore(server *Server, fsm *fsm.FSM) *FSMDataStore {
return &FSMDataStore{
server: server,
fsm: fsm,
}
}
// GetConfigEntry takes in a kind, name, and meta and returns a configentry and an error from the FSM state
func (f *FSMDataStore) GetConfigEntry(kind string, name string, meta *acl.EnterpriseMeta) (structs.ConfigEntry, error) {
store := f.fsm.State()
_, entry, err := store.ConfigEntry(nil, kind, name, meta)
if err != nil {
return nil, err
}
return entry, nil
}
// GetConfigEntriesByKind takes in a kind and returns all instances of that kind of config entry from the FSM state
func (f *FSMDataStore) GetConfigEntriesByKind(kind string) ([]structs.ConfigEntry, error) {
store := f.fsm.State()
_, entries, err := store.ConfigEntriesByKind(nil, kind, acl.WildcardEnterpriseMeta())
if err != nil {
return nil, err
}
return entries, nil
}
// Update takes a config entry and upserts it in the FSM state
func (f *FSMDataStore) Update(entry structs.ConfigEntry) error {
_, err := f.server.leaderRaftApply("ConfigEntry.Apply", structs.ConfigEntryRequestType, &structs.ConfigEntryRequest{
Op: structs.ConfigEntryUpsertCAS,
Entry: entry,
})
return err
}
// UpdateStatus takes a config entry, an error, and updates the status field as needed in the FSM state
func (f *FSMDataStore) UpdateStatus(entry structs.ControlledConfigEntry, err error) error {
if err == nil {
//TODO additional status messages for success?
return nil
}
status := structs.Status{
Conditions: []structs.Condition{{
Status: err.Error() + ": Accepted == false",
},
},
}
entry.SetStatus(status)
return f.Update(entry)
}
// Delete takes a config entry and deletes it from the FSM state
func (f *FSMDataStore) Delete(entry structs.ConfigEntry) error {
_, err := f.server.leaderRaftApply("ConfigEntry.Delete", structs.ConfigEntryRequestType, &structs.ConfigEntryRequest{
Op: structs.ConfigEntryDelete,
Entry: entry,
})
return err
}

View File

@ -8,7 +8,6 @@ import (
"errors"
"fmt"
"sync"
"time"
"github.com/hashicorp/go-hclog"
"github.com/hashicorp/go-memdb"
@ -20,6 +19,7 @@ import (
"github.com/hashicorp/consul/agent/consul/state"
"github.com/hashicorp/consul/agent/consul/stream"
"github.com/hashicorp/consul/agent/structs"
"github.com/hashicorp/consul/api"
)
var (
@ -211,8 +211,6 @@ func (r *apiGatewayReconciler) cleanupGateway(_ context.Context, req controller.
// referenced this gateway. It then persists any status updates for the gateway,
// the modified routes, and updates the bound gateway.
func (r *apiGatewayReconciler) reconcileGateway(_ context.Context, req controller.Request, store *state.Store, gateway *structs.APIGatewayConfigEntry) error {
conditions := newGatewayConditionGenerator()
logger := gatewayRequestLogger(r.logger, req)
logger.Trace("started reconciling gateway")
@ -244,13 +242,13 @@ func (r *apiGatewayReconciler) reconcileGateway(_ context.Context, req controlle
}
for ref, err := range certificateErrors {
updater.SetCondition(conditions.invalidCertificate(ref, err))
updater.SetCondition(invalidCertificate(ref, err))
}
if len(certificateErrors) > 0 {
updater.SetCondition(conditions.invalidCertificates())
updater.SetCondition(invalidCertificates())
} else {
updater.SetCondition(conditions.gatewayAccepted())
updater.SetCondition(gatewayAccepted())
}
// now we bind all of the routes we can
@ -262,19 +260,19 @@ func (r *apiGatewayReconciler) reconcileGateway(_ context.Context, req controlle
// unset the old gateway binding in case it's stale
for _, parent := range route.GetParents() {
if parent.Kind == gateway.Kind && parent.Name == gateway.Name && parent.EnterpriseMeta.IsSame(&gateway.EnterpriseMeta) {
routeUpdater.RemoveCondition(conditions.routeBound(parent))
routeUpdater.RemoveCondition(routeBound(parent))
}
}
// set the status for parents that have bound successfully
for _, ref := range boundRefs {
routeUpdater.SetCondition(conditions.routeBound(ref))
routeUpdater.SetCondition(routeBound(ref))
}
// set the status for any parents that have errored trying to
// bind
for ref, err := range bindErrors {
routeUpdater.SetCondition(conditions.routeUnbound(ref, err))
routeUpdater.SetCondition(routeUnbound(ref, err))
}
// if we've updated any statuses, then store them as needing
@ -354,8 +352,6 @@ func (r *apiGatewayReconciler) cleanupRoute(_ context.Context, req controller.Re
// gateways that now have route conflicts, and updates all statuses and states
// as necessary.
func (r *apiGatewayReconciler) reconcileRoute(_ context.Context, req controller.Request, store *state.Store, route structs.BoundRoute) error {
conditions := newGatewayConditionGenerator()
logger := routeRequestLogger(r.logger, req)
logger.Trace("reconciling route")
@ -435,23 +431,23 @@ func (r *apiGatewayReconciler) reconcileRoute(_ context.Context, req controller.
Entries: chainSet,
})
if err != nil {
updater.SetCondition(conditions.routeInvalidDiscoveryChain(err))
updater.SetCondition(routeInvalidDiscoveryChain(err))
continue
}
if chain.Protocol != string(route.GetProtocol()) {
updater.SetCondition(conditions.routeInvalidDiscoveryChain(errInvalidProtocol))
updater.SetCondition(routeInvalidDiscoveryChain(errInvalidProtocol))
continue
}
updater.SetCondition(conditions.routeAccepted())
updater.SetCondition(routeAccepted())
}
// if we have no upstream targets, then set the route as invalid
// this should already happen in the validation check on write, but
// we'll do it here too just in case
if len(route.GetServiceNames()) == 0 {
updater.SetCondition(conditions.routeNoUpstreams())
updater.SetCondition(routeNoUpstreams())
}
// the route is valid, attempt to bind it to all gateways
@ -460,12 +456,12 @@ func (r *apiGatewayReconciler) reconcileRoute(_ context.Context, req controller.
// set the status of the references that are bound
for _, ref := range boundRefs {
updater.SetCondition(conditions.routeBound(ref))
updater.SetCondition(routeBound(ref))
}
// set any binding errors
for ref, err := range bindErrors {
updater.SetCondition(conditions.routeUnbound(ref, err))
updater.SetCondition(routeUnbound(ref, err))
}
// set any refs that haven't been bound or explicitly errored
@ -479,7 +475,7 @@ PARENT_LOOP:
if _, ok := bindErrors[ref]; ok {
continue PARENT_LOOP
}
updater.SetCondition(conditions.gatewayNotFound(ref))
updater.SetCondition(gatewayNotFound(ref))
}
return finalize(modifiedGateways)
@ -549,8 +545,6 @@ type gatewayMeta struct {
// the map values are pointers so that we can update them directly
// and have the changes propagate back to the container gateways.
boundListeners map[string]*structs.BoundAPIGatewayListener
generator *gatewayConditionGenerator
}
// getAllGatewayMeta returns a pre-constructed list of all valid gateway and state
@ -678,13 +672,13 @@ func (g *gatewayMeta) bindRoute(listener *structs.APIGatewayListener, bound *str
}
// check to make sure we're not binding to an invalid gateway
if !g.Gateway.Status.MatchesConditionStatus(g.generator.gatewayAccepted()) {
if !g.Gateway.Status.MatchesConditionStatus(gatewayAccepted()) {
return false, fmt.Errorf("failed to bind route to gateway %s: gateway has not been accepted", g.Gateway.Name)
}
// check to make sure we're not binding to an invalid route
status := route.GetStatus()
if !status.MatchesConditionStatus(g.generator.routeAccepted()) {
if !status.MatchesConditionStatus(routeAccepted()) {
return false, fmt.Errorf("failed to bind route to gateway %s: route has not been accepted", g.Gateway.Name)
}
@ -755,7 +749,6 @@ func (g *gatewayMeta) checkCertificates(store *state.Store) (map[structs.Resourc
}
return nil
})
if err != nil {
return nil, err
}
@ -773,8 +766,6 @@ func (g *gatewayMeta) checkConflicts() (structs.ControlledConfigEntry, bool) {
// setConflicts ensures that no TCP listener has more than the one allowed route and
// assigns an appropriate status
func (g *gatewayMeta) setConflicts(updater *structs.StatusUpdater) {
conditions := newGatewayConditionGenerator()
g.eachListener(func(listener *structs.APIGatewayListener, bound *structs.BoundAPIGatewayListener) error {
ref := structs.ResourceReference{
Kind: structs.APIGateway,
@ -785,19 +776,17 @@ func (g *gatewayMeta) setConflicts(updater *structs.StatusUpdater) {
switch listener.Protocol {
case structs.ListenerProtocolTCP:
if len(bound.Routes) > 1 {
updater.SetCondition(conditions.gatewayListenerConflicts(ref))
updater.SetCondition(gatewayListenerConflicts(ref))
return nil
}
}
updater.SetCondition(conditions.gatewayListenerNoConflicts(ref))
updater.SetCondition(gatewayListenerNoConflicts(ref))
return nil
})
}
// initialize sets up the listener maps that we use for quickly indexing the listeners in our binding logic
func (g *gatewayMeta) initialize() *gatewayMeta {
g.generator = newGatewayConditionGenerator()
// set up the maps for fast access
g.boundListeners = make(map[string]*structs.BoundAPIGatewayListener, len(g.BoundGateway.Listeners))
for i, listener := range g.BoundGateway.Listeners {
@ -840,151 +829,132 @@ func newGatewayMeta(gateway *structs.APIGatewayConfigEntry, bound structs.Config
}).initialize()
}
// gatewayConditionGenerator is a simple struct used for isolating
// the status conditions that we generate for our components
type gatewayConditionGenerator struct {
now *time.Time
}
// newGatewayConditionGenerator initializes a status conditions generator
func newGatewayConditionGenerator() *gatewayConditionGenerator {
return &gatewayConditionGenerator{
now: pointerTo(time.Now().UTC()),
}
// gatewayAccepted marks the APIGateway as valid.
func gatewayAccepted() structs.Condition {
return structs.NewGatewayCondition(
api.GatewayConditionAccepted,
api.ConditionStatusTrue,
api.GatewayReasonAccepted,
"gateway is valid",
structs.ResourceReference{},
)
}
// invalidCertificate returns a condition used when a gateway references a
// certificate that does not exist. It takes a ref used to scope the condition
// to a given APIGateway listener.
func (g *gatewayConditionGenerator) invalidCertificate(ref structs.ResourceReference, err error) structs.Condition {
return structs.Condition{
Type: "Accepted",
Status: "False",
Reason: "InvalidCertificate",
Message: err.Error(),
Resource: pointerTo(ref),
LastTransitionTime: g.now,
}
func invalidCertificate(ref structs.ResourceReference, err error) structs.Condition {
return structs.NewGatewayCondition(
api.GatewayConditionAccepted,
api.ConditionStatusFalse,
api.GatewayListenerReasonInvalidCertificateRef,
err.Error(),
ref,
)
}
// invalidCertificates is used to set the overall condition of the APIGateway
// to invalid due to missing certificates that it references.
func (g *gatewayConditionGenerator) invalidCertificates() structs.Condition {
return structs.Condition{
Type: "Accepted",
Status: "False",
Reason: "InvalidCertificates",
Message: "gateway references invalid certificates",
LastTransitionTime: g.now,
}
}
// gatewayAccepted marks the APIGateway as valid.
func (g *gatewayConditionGenerator) gatewayAccepted() structs.Condition {
return structs.Condition{
Type: "Accepted",
Status: "True",
Reason: "Accepted",
Message: "gateway is valid",
LastTransitionTime: g.now,
}
}
// routeBound marks a Route as bound to the referenced APIGateway
func (g *gatewayConditionGenerator) routeBound(ref structs.ResourceReference) structs.Condition {
return structs.Condition{
Type: "Bound",
Status: "True",
Reason: "Bound",
Resource: pointerTo(ref),
Message: "successfully bound route",
LastTransitionTime: g.now,
}
}
// routeAccepted marks the Route as valid
func (g *gatewayConditionGenerator) routeAccepted() structs.Condition {
return structs.Condition{
Type: "Accepted",
Status: "True",
Reason: "Accepted",
Message: "route is valid",
LastTransitionTime: g.now,
}
}
// routeUnbound marks the route as having failed to bind to the referenced APIGateway
func (g *gatewayConditionGenerator) routeUnbound(ref structs.ResourceReference, err error) structs.Condition {
return structs.Condition{
Type: "Bound",
Status: "False",
Reason: "FailedToBind",
Resource: pointerTo(ref),
Message: err.Error(),
LastTransitionTime: g.now,
}
}
// routeInvalidDiscoveryChain marks the route as invalid due to an error while validating its referenced
// discovery chian
func (g *gatewayConditionGenerator) routeInvalidDiscoveryChain(err error) structs.Condition {
return structs.Condition{
Type: "Accepted",
Status: "False",
Reason: "InvalidDiscoveryChain",
Message: err.Error(),
LastTransitionTime: g.now,
}
}
// routeNoUpstreams marks the route as invalid because it has no upstreams that it targets
func (g *gatewayConditionGenerator) routeNoUpstreams() structs.Condition {
return structs.Condition{
Type: "Accepted",
Status: "False",
Reason: "NoUpstreamServicesTargeted",
Message: "route must target at least one upstream service",
LastTransitionTime: g.now,
}
}
// gatewayListenerConflicts marks an APIGateway listener as having bound routes that conflict with each other
// and make the listener, therefore invalid
func (g *gatewayConditionGenerator) gatewayListenerConflicts(ref structs.ResourceReference) structs.Condition {
return structs.Condition{
Type: "Conflicted",
Status: "True",
Reason: "RouteConflict",
Resource: pointerTo(ref),
Message: "TCP-based listeners currently only support binding a single route",
LastTransitionTime: g.now,
}
func invalidCertificates() structs.Condition {
return structs.NewGatewayCondition(
api.GatewayConditionAccepted,
api.ConditionStatusFalse,
api.GatewayReasonInvalidCertificates,
"gateway references invalid certificates",
structs.ResourceReference{},
)
}
// gatewayListenerNoConflicts marks an APIGateway listener as having no conflicts within its
// bound routes
func (g *gatewayConditionGenerator) gatewayListenerNoConflicts(ref structs.ResourceReference) structs.Condition {
return structs.Condition{
Type: "Conflicted",
Status: "False",
Reason: "NoConflict",
Resource: pointerTo(ref),
Message: "listener has no route conflicts",
LastTransitionTime: g.now,
}
func gatewayListenerNoConflicts(ref structs.ResourceReference) structs.Condition {
return structs.NewGatewayCondition(
api.GatewayConditionConflicted,
api.ConditionStatusFalse,
api.GatewayReasonNoConflict,
"listener has no route conflicts",
ref,
)
}
// gatewayListenerConflicts marks an APIGateway listener as having bound routes that conflict with each other
// and make the listener, therefore invalid
func gatewayListenerConflicts(ref structs.ResourceReference) structs.Condition {
return structs.NewGatewayCondition(
api.GatewayConditionConflicted,
api.ConditionStatusTrue,
api.GatewayReasonRouteConflict,
"TCP-based listeners currently only support binding a single route",
ref,
)
}
// routeBound marks a Route as bound to the referenced APIGateway
func routeBound(ref structs.ResourceReference) structs.Condition {
return structs.NewRouteCondition(
api.RouteConditionBound,
api.ConditionStatusTrue,
api.RouteReasonBound,
"successfully bound route",
ref,
)
}
// gatewayNotFound marks a Route as having failed to bind to a referenced APIGateway due to
// the Gateway not existing (or having not been reconciled yet)
func (g *gatewayConditionGenerator) gatewayNotFound(ref structs.ResourceReference) structs.Condition {
return structs.Condition{
Type: "Bound",
Status: "False",
Reason: "GatewayNotFound",
Resource: pointerTo(ref),
Message: "gateway was not found",
LastTransitionTime: g.now,
}
func gatewayNotFound(ref structs.ResourceReference) structs.Condition {
return structs.NewRouteCondition(
api.RouteConditionBound,
api.ConditionStatusFalse,
api.RouteReasonGatewayNotFound,
"gateway was not found",
ref,
)
}
// routeUnbound marks the route as having failed to bind to the referenced APIGateway
func routeUnbound(ref structs.ResourceReference, err error) structs.Condition {
return structs.NewRouteCondition(
api.RouteConditionBound,
api.ConditionStatusFalse,
api.RouteReasonFailedToBind,
err.Error(),
ref,
)
}
// routeAccepted marks the Route as valid
func routeAccepted() structs.Condition {
return structs.NewRouteCondition(
api.RouteConditionAccepted,
api.ConditionStatusTrue,
api.RouteReasonAccepted,
"route is valid",
structs.ResourceReference{},
)
}
// routeInvalidDiscoveryChain marks the route as invalid due to an error while validating its referenced
// discovery chian
func routeInvalidDiscoveryChain(err error) structs.Condition {
return structs.NewRouteCondition(
api.RouteConditionAccepted,
api.ConditionStatusFalse,
api.RouteReasonInvalidDiscoveryChain,
err.Error(),
structs.ResourceReference{},
)
}
// routeNoUpstreams marks the route as invalid because it has no upstreams that it targets
func routeNoUpstreams() structs.Condition {
return structs.NewRouteCondition(
api.RouteConditionAccepted,
api.ConditionStatusFalse,
api.RouteReasonNoUpstreamServicesTargeted,
"route must target at least one upstream service",
structs.ResourceReference{},
)
}
// bindRoutesToGateways takes a route variadic number of gateways.
@ -1024,7 +994,6 @@ func bindRoutesToGateways(route structs.BoundRoute, gateways ...*gatewayMeta) ([
// removeGateway sets the route's status appropriately when the gateway that it's
// attempting to bind to does not exist
func removeGateway(gateway structs.ResourceReference, entries ...structs.BoundRoute) []structs.ControlledConfigEntry {
conditions := newGatewayConditionGenerator()
modified := []structs.ControlledConfigEntry{}
for _, route := range entries {
@ -1032,7 +1001,7 @@ func removeGateway(gateway structs.ResourceReference, entries ...structs.BoundRo
for _, parent := range route.GetParents() {
if parent.Kind == gateway.Kind && parent.Name == gateway.Name && parent.EnterpriseMeta.IsSame(&gateway.EnterpriseMeta) {
updater.SetCondition(conditions.gatewayNotFound(parent))
updater.SetCondition(gatewayNotFound(parent))
}
}

View File

@ -11,9 +11,10 @@ import (
"testing"
"time"
"github.com/hashicorp/go-hclog"
"github.com/stretchr/testify/require"
"github.com/hashicorp/go-hclog"
"github.com/hashicorp/consul/acl"
"github.com/hashicorp/consul/agent/consul/controller"
"github.com/hashicorp/consul/agent/consul/fsm"
@ -55,7 +56,7 @@ func TestBoundAPIGatewayBindRoute(t *testing.T) {
},
Status: structs.Status{
Conditions: []structs.Condition{
newGatewayConditionGenerator().gatewayAccepted(),
gatewayAccepted(),
},
},
},
@ -72,7 +73,7 @@ func TestBoundAPIGatewayBindRoute(t *testing.T) {
},
Status: structs.Status{
Conditions: []structs.Condition{
newGatewayConditionGenerator().routeAccepted(),
routeAccepted(),
},
},
},
@ -132,7 +133,7 @@ func TestBoundAPIGatewayBindRoute(t *testing.T) {
},
Status: structs.Status{
Conditions: []structs.Condition{
newGatewayConditionGenerator().gatewayAccepted(),
gatewayAccepted(),
},
},
},
@ -148,7 +149,7 @@ func TestBoundAPIGatewayBindRoute(t *testing.T) {
},
Status: structs.Status{
Conditions: []structs.Condition{
newGatewayConditionGenerator().routeAccepted(),
routeAccepted(),
},
},
},
@ -443,7 +444,7 @@ func TestBindRoutesToGateways(t *testing.T) {
},
Status: structs.Status{
Conditions: []structs.Condition{
newGatewayConditionGenerator().gatewayAccepted(),
gatewayAccepted(),
},
},
},
@ -462,7 +463,7 @@ func TestBindRoutesToGateways(t *testing.T) {
},
Status: structs.Status{
Conditions: []structs.Condition{
newGatewayConditionGenerator().routeAccepted(),
routeAccepted(),
},
},
},
@ -557,7 +558,7 @@ func TestBindRoutesToGateways(t *testing.T) {
},
Status: structs.Status{
Conditions: []structs.Condition{
newGatewayConditionGenerator().gatewayAccepted(),
gatewayAccepted(),
},
},
},
@ -582,7 +583,7 @@ func TestBindRoutesToGateways(t *testing.T) {
},
Status: structs.Status{
Conditions: []structs.Condition{
newGatewayConditionGenerator().gatewayAccepted(),
gatewayAccepted(),
},
},
},
@ -606,7 +607,7 @@ func TestBindRoutesToGateways(t *testing.T) {
},
Status: structs.Status{
Conditions: []structs.Condition{
newGatewayConditionGenerator().routeAccepted(),
routeAccepted(),
},
},
},
@ -675,7 +676,7 @@ func TestBindRoutesToGateways(t *testing.T) {
},
Status: structs.Status{
Conditions: []structs.Condition{
newGatewayConditionGenerator().gatewayAccepted(),
gatewayAccepted(),
},
},
},
@ -694,7 +695,7 @@ func TestBindRoutesToGateways(t *testing.T) {
},
Status: structs.Status{
Conditions: []structs.Condition{
newGatewayConditionGenerator().routeAccepted(),
routeAccepted(),
},
},
},
@ -752,7 +753,7 @@ func TestBindRoutesToGateways(t *testing.T) {
},
Status: structs.Status{
Conditions: []structs.Condition{
newGatewayConditionGenerator().gatewayAccepted(),
gatewayAccepted(),
},
},
},
@ -771,7 +772,7 @@ func TestBindRoutesToGateways(t *testing.T) {
},
Status: structs.Status{
Conditions: []structs.Condition{
newGatewayConditionGenerator().routeAccepted(),
routeAccepted(),
},
},
},
@ -841,7 +842,7 @@ func TestBindRoutesToGateways(t *testing.T) {
},
Status: structs.Status{
Conditions: []structs.Condition{
newGatewayConditionGenerator().gatewayAccepted(),
gatewayAccepted(),
},
},
},
@ -860,7 +861,7 @@ func TestBindRoutesToGateways(t *testing.T) {
},
Status: structs.Status{
Conditions: []structs.Condition{
newGatewayConditionGenerator().routeAccepted(),
routeAccepted(),
},
},
},
@ -924,7 +925,7 @@ func TestBindRoutesToGateways(t *testing.T) {
},
Status: structs.Status{
Conditions: []structs.Condition{
newGatewayConditionGenerator().gatewayAccepted(),
gatewayAccepted(),
},
},
},
@ -957,7 +958,7 @@ func TestBindRoutesToGateways(t *testing.T) {
},
Status: structs.Status{
Conditions: []structs.Condition{
newGatewayConditionGenerator().gatewayAccepted(),
gatewayAccepted(),
},
},
},
@ -981,7 +982,7 @@ func TestBindRoutesToGateways(t *testing.T) {
},
Status: structs.Status{
Conditions: []structs.Condition{
newGatewayConditionGenerator().routeAccepted(),
routeAccepted(),
},
},
},
@ -1064,7 +1065,7 @@ func TestBindRoutesToGateways(t *testing.T) {
},
Status: structs.Status{
Conditions: []structs.Condition{
newGatewayConditionGenerator().gatewayAccepted(),
gatewayAccepted(),
},
},
},
@ -1083,7 +1084,7 @@ func TestBindRoutesToGateways(t *testing.T) {
},
Status: structs.Status{
Conditions: []structs.Condition{
newGatewayConditionGenerator().routeAccepted(),
routeAccepted(),
},
},
},
@ -1133,7 +1134,7 @@ func TestBindRoutesToGateways(t *testing.T) {
},
Status: structs.Status{
Conditions: []structs.Condition{
newGatewayConditionGenerator().gatewayAccepted(),
gatewayAccepted(),
},
},
},
@ -1158,7 +1159,7 @@ func TestBindRoutesToGateways(t *testing.T) {
},
Status: structs.Status{
Conditions: []structs.Condition{
newGatewayConditionGenerator().gatewayAccepted(),
gatewayAccepted(),
},
},
},
@ -1177,7 +1178,7 @@ func TestBindRoutesToGateways(t *testing.T) {
},
Status: structs.Status{
Conditions: []structs.Condition{
newGatewayConditionGenerator().routeAccepted(),
routeAccepted(),
},
},
},
@ -1193,7 +1194,7 @@ func TestBindRoutesToGateways(t *testing.T) {
},
Status: structs.Status{
Conditions: []structs.Condition{
newGatewayConditionGenerator().routeAccepted(),
routeAccepted(),
},
},
},
@ -1254,7 +1255,7 @@ func TestBindRoutesToGateways(t *testing.T) {
},
Status: structs.Status{
Conditions: []structs.Condition{
newGatewayConditionGenerator().gatewayAccepted(),
gatewayAccepted(),
},
},
},
@ -1273,7 +1274,7 @@ func TestBindRoutesToGateways(t *testing.T) {
},
Status: structs.Status{
Conditions: []structs.Condition{
newGatewayConditionGenerator().routeAccepted(),
routeAccepted(),
},
},
},
@ -1317,7 +1318,7 @@ func TestBindRoutesToGateways(t *testing.T) {
},
Status: structs.Status{
Conditions: []structs.Condition{
newGatewayConditionGenerator().gatewayAccepted(),
gatewayAccepted(),
},
},
},
@ -1336,14 +1337,13 @@ func TestBindRoutesToGateways(t *testing.T) {
},
Status: structs.Status{
Conditions: []structs.Condition{
newGatewayConditionGenerator().routeAccepted(),
routeAccepted(),
},
},
},
},
expectedBoundAPIGateways: []*structs.BoundAPIGatewayConfigEntry{
{
Name: "Gateway",
Listeners: []structs.BoundAPIGatewayListener{
{
@ -1435,7 +1435,7 @@ func TestBindRoutesToGateways(t *testing.T) {
},
Status: structs.Status{
Conditions: []structs.Condition{
newGatewayConditionGenerator().gatewayAccepted(),
gatewayAccepted(),
},
},
},
@ -1453,7 +1453,7 @@ func TestBindRoutesToGateways(t *testing.T) {
},
Status: structs.Status{
Conditions: []structs.Condition{
newGatewayConditionGenerator().routeAccepted(),
routeAccepted(),
},
},
},
@ -1493,7 +1493,6 @@ func TestBindRoutesToGateways(t *testing.T) {
}
func TestAPIGatewayController(t *testing.T) {
conditions := newGatewayConditionGenerator()
defaultMeta := acl.DefaultEnterpriseMeta()
for name, tc := range map[string]struct {
requests []controller.Request
@ -1521,7 +1520,7 @@ func TestAPIGatewayController(t *testing.T) {
EnterpriseMeta: *defaultMeta,
Status: structs.Status{
Conditions: []structs.Condition{
conditions.gatewayAccepted(),
gatewayAccepted(),
},
},
},
@ -1552,7 +1551,7 @@ func TestAPIGatewayController(t *testing.T) {
EnterpriseMeta: *defaultMeta,
Status: structs.Status{
Conditions: []structs.Condition{
conditions.routeNoUpstreams(),
routeNoUpstreams(),
},
},
},
@ -1578,7 +1577,7 @@ func TestAPIGatewayController(t *testing.T) {
EnterpriseMeta: *defaultMeta,
Status: structs.Status{
Conditions: []structs.Condition{
conditions.routeNoUpstreams(),
routeNoUpstreams(),
},
},
},
@ -1628,8 +1627,8 @@ func TestAPIGatewayController(t *testing.T) {
EnterpriseMeta: *defaultMeta,
Status: structs.Status{
Conditions: []structs.Condition{
conditions.routeAccepted(),
conditions.routeUnbound(structs.ResourceReference{
routeAccepted(),
routeUnbound(structs.ResourceReference{
Name: "api-gateway",
EnterpriseMeta: *defaultMeta,
}, errors.New("failed to bind route to gateway api-gateway: gateway has not been accepted")),
@ -1646,7 +1645,7 @@ func TestAPIGatewayController(t *testing.T) {
}},
Status: structs.Status{
Conditions: []structs.Condition{
conditions.gatewayListenerNoConflicts(structs.ResourceReference{
gatewayListenerNoConflicts(structs.ResourceReference{
Kind: structs.APIGateway,
Name: "api-gateway",
SectionName: "listener",
@ -1694,7 +1693,7 @@ func TestAPIGatewayController(t *testing.T) {
EnterpriseMeta: *defaultMeta,
Status: structs.Status{
Conditions: []structs.Condition{
conditions.routeInvalidDiscoveryChain(errInvalidProtocol),
routeInvalidDiscoveryChain(errInvalidProtocol),
},
},
},
@ -1733,8 +1732,8 @@ func TestAPIGatewayController(t *testing.T) {
EnterpriseMeta: *defaultMeta,
Status: structs.Status{
Conditions: []structs.Condition{
conditions.routeAccepted(),
conditions.gatewayNotFound(structs.ResourceReference{
routeAccepted(),
gatewayNotFound(structs.ResourceReference{
Kind: structs.APIGateway,
Name: "gateway",
EnterpriseMeta: *defaultMeta,
@ -1787,8 +1786,8 @@ func TestAPIGatewayController(t *testing.T) {
EnterpriseMeta: *defaultMeta,
Status: structs.Status{
Conditions: []structs.Condition{
conditions.routeAccepted(),
conditions.gatewayNotFound(structs.ResourceReference{
routeAccepted(),
gatewayNotFound(structs.ResourceReference{
Kind: structs.APIGateway,
Name: "gateway",
EnterpriseMeta: *defaultMeta,
@ -1851,8 +1850,8 @@ func TestAPIGatewayController(t *testing.T) {
EnterpriseMeta: *defaultMeta,
Status: structs.Status{
Conditions: []structs.Condition{
conditions.routeAccepted(),
conditions.routeUnbound(structs.ResourceReference{
routeAccepted(),
routeUnbound(structs.ResourceReference{
Kind: structs.APIGateway,
Name: "gateway",
EnterpriseMeta: *defaultMeta,
@ -1920,8 +1919,8 @@ func TestAPIGatewayController(t *testing.T) {
EnterpriseMeta: *defaultMeta,
Status: structs.Status{
Conditions: []structs.Condition{
conditions.routeAccepted(),
conditions.gatewayNotFound(structs.ResourceReference{
routeAccepted(),
gatewayNotFound(structs.ResourceReference{
Kind: structs.APIGateway,
Name: "gateway",
EnterpriseMeta: *defaultMeta,
@ -1956,7 +1955,7 @@ func TestAPIGatewayController(t *testing.T) {
}},
Status: structs.Status{
Conditions: []structs.Condition{
newGatewayConditionGenerator().routeAccepted(),
routeAccepted(),
},
},
},
@ -1976,7 +1975,7 @@ func TestAPIGatewayController(t *testing.T) {
}},
Status: structs.Status{
Conditions: []structs.Condition{
newGatewayConditionGenerator().gatewayAccepted(),
gatewayAccepted(),
},
},
},
@ -2006,8 +2005,8 @@ func TestAPIGatewayController(t *testing.T) {
EnterpriseMeta: *defaultMeta,
Status: structs.Status{
Conditions: []structs.Condition{
conditions.gatewayAccepted(),
conditions.gatewayListenerNoConflicts(structs.ResourceReference{
gatewayAccepted(),
gatewayListenerNoConflicts(structs.ResourceReference{
Kind: structs.APIGateway,
Name: "gateway",
EnterpriseMeta: *defaultMeta,
@ -2022,8 +2021,8 @@ func TestAPIGatewayController(t *testing.T) {
EnterpriseMeta: *defaultMeta,
Status: structs.Status{
Conditions: []structs.Condition{
conditions.routeAccepted(),
conditions.routeBound(structs.ResourceReference{
routeAccepted(),
routeBound(structs.ResourceReference{
Kind: structs.APIGateway,
Name: "gateway",
EnterpriseMeta: *defaultMeta,
@ -2058,7 +2057,7 @@ func TestAPIGatewayController(t *testing.T) {
}},
Status: structs.Status{
Conditions: []structs.Condition{
newGatewayConditionGenerator().routeAccepted(),
routeAccepted(),
},
},
},
@ -2098,8 +2097,8 @@ func TestAPIGatewayController(t *testing.T) {
EnterpriseMeta: *defaultMeta,
Status: structs.Status{
Conditions: []structs.Condition{
conditions.gatewayAccepted(),
conditions.gatewayListenerNoConflicts(structs.ResourceReference{
gatewayAccepted(),
gatewayListenerNoConflicts(structs.ResourceReference{
Kind: structs.APIGateway,
Name: "gateway",
EnterpriseMeta: *defaultMeta,
@ -2114,8 +2113,8 @@ func TestAPIGatewayController(t *testing.T) {
EnterpriseMeta: *defaultMeta,
Status: structs.Status{
Conditions: []structs.Condition{
conditions.routeAccepted(),
conditions.routeUnbound(structs.ResourceReference{
routeAccepted(),
routeUnbound(structs.ResourceReference{
Kind: structs.APIGateway,
Name: "gateway",
EnterpriseMeta: *defaultMeta,
@ -2154,7 +2153,7 @@ func TestAPIGatewayController(t *testing.T) {
}},
Status: structs.Status{
Conditions: []structs.Condition{
newGatewayConditionGenerator().routeAccepted(),
routeAccepted(),
},
},
},
@ -2172,7 +2171,7 @@ func TestAPIGatewayController(t *testing.T) {
}},
Status: structs.Status{
Conditions: []structs.Condition{
newGatewayConditionGenerator().routeAccepted(),
routeAccepted(),
},
},
},
@ -2221,8 +2220,8 @@ func TestAPIGatewayController(t *testing.T) {
EnterpriseMeta: *defaultMeta,
Status: structs.Status{
Conditions: []structs.Condition{
conditions.gatewayAccepted(),
conditions.gatewayListenerConflicts(structs.ResourceReference{
gatewayAccepted(),
gatewayListenerConflicts(structs.ResourceReference{
Kind: structs.APIGateway,
Name: "gateway",
EnterpriseMeta: *defaultMeta,
@ -2237,8 +2236,8 @@ func TestAPIGatewayController(t *testing.T) {
EnterpriseMeta: *defaultMeta,
Status: structs.Status{
Conditions: []structs.Condition{
conditions.routeAccepted(),
conditions.routeBound(structs.ResourceReference{
routeAccepted(),
routeBound(structs.ResourceReference{
Kind: structs.APIGateway,
Name: "gateway",
EnterpriseMeta: *defaultMeta,
@ -2252,8 +2251,8 @@ func TestAPIGatewayController(t *testing.T) {
EnterpriseMeta: *defaultMeta,
Status: structs.Status{
Conditions: []structs.Condition{
conditions.routeAccepted(),
conditions.routeBound(structs.ResourceReference{
routeAccepted(),
routeBound(structs.ResourceReference{
Kind: structs.APIGateway,
Name: "gateway",
EnterpriseMeta: *defaultMeta,
@ -2294,7 +2293,7 @@ func TestAPIGatewayController(t *testing.T) {
}},
Status: structs.Status{
Conditions: []structs.Condition{
newGatewayConditionGenerator().routeAccepted(),
routeAccepted(),
},
},
},
@ -2314,7 +2313,7 @@ func TestAPIGatewayController(t *testing.T) {
}},
Status: structs.Status{
Conditions: []structs.Condition{
newGatewayConditionGenerator().routeAccepted(),
routeAccepted(),
},
},
},
@ -2364,8 +2363,8 @@ func TestAPIGatewayController(t *testing.T) {
EnterpriseMeta: *defaultMeta,
Status: structs.Status{
Conditions: []structs.Condition{
conditions.gatewayAccepted(),
conditions.gatewayListenerNoConflicts(structs.ResourceReference{
gatewayAccepted(),
gatewayListenerNoConflicts(structs.ResourceReference{
Kind: structs.APIGateway,
Name: "gateway",
EnterpriseMeta: *defaultMeta,
@ -2380,8 +2379,8 @@ func TestAPIGatewayController(t *testing.T) {
EnterpriseMeta: *defaultMeta,
Status: structs.Status{
Conditions: []structs.Condition{
conditions.routeAccepted(),
conditions.routeBound(structs.ResourceReference{
routeAccepted(),
routeBound(structs.ResourceReference{
Kind: structs.APIGateway,
Name: "gateway",
EnterpriseMeta: *defaultMeta,
@ -2395,8 +2394,8 @@ func TestAPIGatewayController(t *testing.T) {
EnterpriseMeta: *defaultMeta,
Status: structs.Status{
Conditions: []structs.Condition{
conditions.routeAccepted(),
conditions.routeBound(structs.ResourceReference{
routeAccepted(),
routeBound(structs.ResourceReference{
Kind: structs.APIGateway,
Name: "gateway",
EnterpriseMeta: *defaultMeta,
@ -2505,8 +2504,8 @@ func TestAPIGatewayController(t *testing.T) {
EnterpriseMeta: *defaultMeta,
Status: structs.Status{
Conditions: []structs.Condition{
conditions.gatewayAccepted(),
conditions.gatewayListenerNoConflicts(structs.ResourceReference{
gatewayAccepted(),
gatewayListenerNoConflicts(structs.ResourceReference{
Kind: structs.APIGateway,
Name: "gateway",
EnterpriseMeta: *defaultMeta,
@ -2521,8 +2520,8 @@ func TestAPIGatewayController(t *testing.T) {
EnterpriseMeta: *defaultMeta,
Status: structs.Status{
Conditions: []structs.Condition{
conditions.routeAccepted(),
conditions.routeBound(structs.ResourceReference{
routeAccepted(),
routeBound(structs.ResourceReference{
Kind: structs.APIGateway,
Name: "gateway",
EnterpriseMeta: *defaultMeta,
@ -2536,8 +2535,8 @@ func TestAPIGatewayController(t *testing.T) {
EnterpriseMeta: *defaultMeta,
Status: structs.Status{
Conditions: []structs.Condition{
conditions.routeAccepted(),
conditions.routeUnbound(structs.ResourceReference{
routeAccepted(),
routeUnbound(structs.ResourceReference{
Kind: structs.APIGateway,
Name: "gateway",
EnterpriseMeta: *defaultMeta,
@ -2578,7 +2577,7 @@ func TestAPIGatewayController(t *testing.T) {
}},
Status: structs.Status{
Conditions: []structs.Condition{
newGatewayConditionGenerator().routeAccepted(),
routeAccepted(),
},
},
},
@ -2596,7 +2595,7 @@ func TestAPIGatewayController(t *testing.T) {
}},
Status: structs.Status{
Conditions: []structs.Condition{
newGatewayConditionGenerator().routeAccepted(),
routeAccepted(),
},
},
},
@ -2659,13 +2658,13 @@ func TestAPIGatewayController(t *testing.T) {
EnterpriseMeta: *defaultMeta,
Status: structs.Status{
Conditions: []structs.Condition{
conditions.gatewayAccepted(),
conditions.gatewayListenerNoConflicts(structs.ResourceReference{
gatewayAccepted(),
gatewayListenerNoConflicts(structs.ResourceReference{
Kind: structs.APIGateway,
Name: "gateway",
SectionName: "http-listener",
}),
conditions.gatewayListenerNoConflicts(structs.ResourceReference{
gatewayListenerNoConflicts(structs.ResourceReference{
Kind: structs.APIGateway,
Name: "gateway",
SectionName: "tcp-listener",
@ -2679,8 +2678,8 @@ func TestAPIGatewayController(t *testing.T) {
EnterpriseMeta: *defaultMeta,
Status: structs.Status{
Conditions: []structs.Condition{
conditions.routeAccepted(),
conditions.routeBound(structs.ResourceReference{
routeAccepted(),
routeBound(structs.ResourceReference{
Kind: structs.APIGateway,
Name: "gateway",
EnterpriseMeta: *defaultMeta,
@ -2694,8 +2693,8 @@ func TestAPIGatewayController(t *testing.T) {
EnterpriseMeta: *defaultMeta,
Status: structs.Status{
Conditions: []structs.Condition{
conditions.routeAccepted(),
conditions.routeBound(structs.ResourceReference{
routeAccepted(),
routeBound(structs.ResourceReference{
Kind: structs.APIGateway,
Name: "gateway",
EnterpriseMeta: *defaultMeta,
@ -2721,8 +2720,8 @@ func TestAPIGatewayController(t *testing.T) {
}},
Status: structs.Status{
Conditions: []structs.Condition{
conditions.routeAccepted(),
conditions.routeBound(structs.ResourceReference{
routeAccepted(),
routeBound(structs.ResourceReference{
Kind: structs.APIGateway,
Name: "gateway",
EnterpriseMeta: *defaultMeta,
@ -2776,7 +2775,7 @@ func TestAPIGatewayController(t *testing.T) {
EnterpriseMeta: *defaultMeta,
Status: structs.Status{
Conditions: []structs.Condition{
conditions.gatewayListenerNoConflicts(structs.ResourceReference{
gatewayListenerNoConflicts(structs.ResourceReference{
Kind: structs.APIGateway,
Name: "gateway",
SectionName: "tcp-listener",
@ -2791,7 +2790,7 @@ func TestAPIGatewayController(t *testing.T) {
EnterpriseMeta: *defaultMeta,
Status: structs.Status{
Conditions: []structs.Condition{
conditions.routeAccepted(),
routeAccepted(),
},
},
},
@ -2810,8 +2809,8 @@ func TestAPIGatewayController(t *testing.T) {
EnterpriseMeta: *defaultMeta,
Status: structs.Status{
Conditions: []structs.Condition{
conditions.routeAccepted(),
conditions.routeBound(structs.ResourceReference{
routeAccepted(),
routeBound(structs.ResourceReference{
Kind: structs.APIGateway,
Name: "gateway",
EnterpriseMeta: *defaultMeta,
@ -2865,7 +2864,7 @@ func TestAPIGatewayController(t *testing.T) {
EnterpriseMeta: *defaultMeta,
Status: structs.Status{
Conditions: []structs.Condition{
conditions.gatewayListenerNoConflicts(structs.ResourceReference{
gatewayListenerNoConflicts(structs.ResourceReference{
Kind: structs.APIGateway,
Name: "gateway",
SectionName: "tcp-listener",
@ -2880,7 +2879,7 @@ func TestAPIGatewayController(t *testing.T) {
EnterpriseMeta: *defaultMeta,
Status: structs.Status{
Conditions: []structs.Condition{
conditions.routeNoUpstreams(),
routeNoUpstreams(),
},
},
},
@ -2908,8 +2907,8 @@ func TestAPIGatewayController(t *testing.T) {
}},
Status: structs.Status{
Conditions: []structs.Condition{
conditions.routeAccepted(),
conditions.routeBound(structs.ResourceReference{
routeAccepted(),
routeBound(structs.ResourceReference{
Kind: structs.APIGateway,
Name: "gateway",
EnterpriseMeta: *defaultMeta,
@ -2934,8 +2933,8 @@ func TestAPIGatewayController(t *testing.T) {
}},
Status: structs.Status{
Conditions: []structs.Condition{
conditions.routeAccepted(),
conditions.routeUnbound(structs.ResourceReference{
routeAccepted(),
routeUnbound(structs.ResourceReference{
Kind: structs.APIGateway,
Name: "gateway",
}, errors.New("foo")),
@ -2965,8 +2964,8 @@ func TestAPIGatewayController(t *testing.T) {
}},
Status: structs.Status{
Conditions: []structs.Condition{
conditions.gatewayAccepted(),
conditions.gatewayListenerNoConflicts(structs.ResourceReference{
gatewayAccepted(),
gatewayListenerNoConflicts(structs.ResourceReference{
Kind: structs.APIGateway,
Name: "gateway",
SectionName: "tcp-listener",
@ -3008,8 +3007,8 @@ func TestAPIGatewayController(t *testing.T) {
EnterpriseMeta: *defaultMeta,
Status: structs.Status{
Conditions: []structs.Condition{
conditions.gatewayAccepted(),
conditions.gatewayListenerNoConflicts(structs.ResourceReference{
gatewayAccepted(),
gatewayListenerNoConflicts(structs.ResourceReference{
Kind: structs.APIGateway,
Name: "gateway",
SectionName: "http-listener",
@ -3023,8 +3022,8 @@ func TestAPIGatewayController(t *testing.T) {
EnterpriseMeta: *defaultMeta,
Status: structs.Status{
Conditions: []structs.Condition{
conditions.routeAccepted(),
conditions.routeUnbound(structs.ResourceReference{
routeAccepted(),
routeUnbound(structs.ResourceReference{
Kind: structs.APIGateway,
Name: "gateway",
}, errors.New("failed to bind route tcp-route to gateway gateway with listener ''")),
@ -3037,8 +3036,8 @@ func TestAPIGatewayController(t *testing.T) {
EnterpriseMeta: *defaultMeta,
Status: structs.Status{
Conditions: []structs.Condition{
conditions.routeAccepted(),
conditions.routeBound(structs.ResourceReference{
routeAccepted(),
routeBound(structs.ResourceReference{
Kind: structs.APIGateway,
Name: "gateway",
}),
@ -3073,8 +3072,8 @@ func TestAPIGatewayController(t *testing.T) {
}},
Status: structs.Status{
Conditions: []structs.Condition{
conditions.routeAccepted(),
conditions.routeBound(structs.ResourceReference{
routeAccepted(),
routeBound(structs.ResourceReference{
Kind: structs.APIGateway,
Name: "gateway",
EnterpriseMeta: *defaultMeta,
@ -3109,8 +3108,8 @@ func TestAPIGatewayController(t *testing.T) {
EnterpriseMeta: *defaultMeta,
Status: structs.Status{
Conditions: []structs.Condition{
conditions.routeAccepted(),
conditions.gatewayNotFound(structs.ResourceReference{
routeAccepted(),
gatewayNotFound(structs.ResourceReference{
Kind: structs.APIGateway,
Name: "gateway",
}),
@ -3150,8 +3149,8 @@ func TestAPIGatewayController(t *testing.T) {
}},
Status: structs.Status{
Conditions: []structs.Condition{
conditions.gatewayAccepted(),
conditions.gatewayListenerNoConflicts(structs.ResourceReference{
gatewayAccepted(),
gatewayListenerNoConflicts(structs.ResourceReference{
Kind: structs.APIGateway,
Name: "gateway",
SectionName: "tcp-listener",
@ -3176,8 +3175,8 @@ func TestAPIGatewayController(t *testing.T) {
EnterpriseMeta: *defaultMeta,
Status: structs.Status{
Conditions: []structs.Condition{
conditions.gatewayAccepted(),
conditions.gatewayListenerNoConflicts(structs.ResourceReference{
gatewayAccepted(),
gatewayListenerNoConflicts(structs.ResourceReference{
Kind: structs.APIGateway,
Name: "gateway",
SectionName: "tcp-listener",
@ -3221,8 +3220,8 @@ func TestAPIGatewayController(t *testing.T) {
}},
Status: structs.Status{
Conditions: []structs.Condition{
conditions.routeAccepted(),
conditions.routeBound(structs.ResourceReference{
routeAccepted(),
routeBound(structs.ResourceReference{
Kind: structs.APIGateway,
Name: "gateway",
}),
@ -3237,8 +3236,8 @@ func TestAPIGatewayController(t *testing.T) {
EnterpriseMeta: *defaultMeta,
Status: structs.Status{
Conditions: []structs.Condition{
conditions.routeAccepted(),
conditions.gatewayNotFound(structs.ResourceReference{
routeAccepted(),
gatewayNotFound(structs.ResourceReference{
Kind: structs.APIGateway,
Name: "gateway",
}),
@ -3287,12 +3286,12 @@ func TestAPIGatewayController(t *testing.T) {
}},
Status: structs.Status{
Conditions: []structs.Condition{
conditions.invalidCertificate(structs.ResourceReference{
invalidCertificate(structs.ResourceReference{
Kind: structs.InlineCertificate,
Name: "certificate",
}, errors.New("certificate not found")),
conditions.invalidCertificates(),
conditions.gatewayListenerNoConflicts(structs.ResourceReference{
invalidCertificates(),
gatewayListenerNoConflicts(structs.ResourceReference{
Kind: structs.APIGateway,
Name: "gateway",
SectionName: "http-listener",
@ -3355,8 +3354,8 @@ func TestAPIGatewayController(t *testing.T) {
}},
Status: structs.Status{
Conditions: []structs.Condition{
conditions.gatewayAccepted(),
conditions.gatewayListenerNoConflicts(structs.ResourceReference{
gatewayAccepted(),
gatewayListenerNoConflicts(structs.ResourceReference{
Kind: structs.APIGateway,
Name: "gateway",
SectionName: "http-listener",
@ -3402,12 +3401,12 @@ func TestAPIGatewayController(t *testing.T) {
}},
Status: structs.Status{
Conditions: []structs.Condition{
conditions.invalidCertificate(structs.ResourceReference{
invalidCertificate(structs.ResourceReference{
Kind: structs.InlineCertificate,
Name: "certificate",
}, errors.New("certificate not found")),
conditions.invalidCertificates(),
conditions.gatewayListenerNoConflicts(structs.ResourceReference{
invalidCertificates(),
gatewayListenerNoConflicts(structs.ResourceReference{
Kind: structs.APIGateway,
Name: "gateway",
SectionName: "http-listener",
@ -3438,8 +3437,8 @@ func TestAPIGatewayController(t *testing.T) {
}},
Status: structs.Status{
Conditions: []structs.Condition{
conditions.gatewayAccepted(),
conditions.gatewayListenerNoConflicts(structs.ResourceReference{
gatewayAccepted(),
gatewayListenerNoConflicts(structs.ResourceReference{
Kind: structs.APIGateway,
Name: "gateway",
SectionName: "http-listener",
@ -3705,12 +3704,15 @@ func (n *noopController) WithWorkers(i int) controller.Controller
func (n *noopController) WithQueueFactory(fn func(ctx context.Context, baseBackoff time.Duration, maxBackoff time.Duration) controller.WorkQueue) controller.Controller {
return n
}
func (n *noopController) AddTrigger(request controller.Request, trigger func(ctx context.Context) error) {
n.triggers[request] = struct{}{}
}
func (n *noopController) RemoveTrigger(request controller.Request) {
delete(n.triggers, request)
}
func (n *noopController) Enqueue(requests ...controller.Request) {
n.enqueued = append(n.enqueued, requests...)
}

View File

@ -9,6 +9,7 @@ import (
"time"
"github.com/hashicorp/consul/acl"
"github.com/hashicorp/consul/api"
)
// ResourceReference is a reference to a ConfigEntry
@ -67,30 +68,6 @@ func (s Status) SameConditions(other Status) bool {
if len(s.Conditions) != len(other.Conditions) {
return false
}
lessResource := func(one, two *ResourceReference) bool {
if one == nil && two == nil {
return false
}
if one == nil {
return true
}
if two == nil {
return false
}
if one.Kind < two.Kind {
return true
}
if one.Kind > two.Kind {
return false
}
if one.Name < two.Name {
return true
}
if one.Name > two.Name {
return false
}
return one.SectionName < two.SectionName
}
sortConditions := func(conditions []Condition) []Condition {
sort.SliceStable(conditions, func(i, j int) bool {
if conditions[i].Type < conditions[j].Type {
@ -114,6 +91,31 @@ func (s Status) SameConditions(other Status) bool {
return true
}
func lessResource(one, two *ResourceReference) bool {
if one == nil && two == nil {
return false
}
if one == nil {
return true
}
if two == nil {
return false
}
if one.Kind < two.Kind {
return true
}
if one.Kind > two.Kind {
return false
}
if one.Name < two.Name {
return true
}
if one.Name > two.Name {
return false
}
return one.SectionName < two.SectionName
}
// Condition is used for a single message and state associated
// with an object. For example, a ConfigEntry that references
// multiple other resources may have different statuses with
@ -195,3 +197,42 @@ func (u *StatusUpdater) UpdateEntry() (ControlledConfigEntry, bool) {
u.entry.SetStatus(u.status)
return u.entry, true
}
func NewGatewayCondition(name api.GatewayConditionType, status api.ConditionStatus, reason api.GatewayConditionReason, message string, resource ResourceReference) Condition {
if err := api.ValidateGatewayConditionReason(name, status, reason); err != nil {
// note we panic here because an invalid combination is a programmer error
// this should never actually be hit
panic(err)
}
return Condition{
Type: string(name),
Status: string(status),
Reason: string(reason),
Message: message,
Resource: ptrTo(resource),
LastTransitionTime: ptrTo(time.Now().UTC()),
}
}
// NewRouteCondition is a helper to build allowable Conditions for a Route config entry
func NewRouteCondition(name api.RouteConditionType, status api.ConditionStatus, reason api.RouteConditionReason, message string, ref ResourceReference) Condition {
if err := api.ValidateRouteConditionReason(name, status, reason); err != nil {
// note we panic here because an invalid combination is a programmer error
// this should never actually be hit
panic(err)
}
return Condition{
Type: string(name),
Status: string(status),
Reason: string(reason),
Message: message,
Resource: ptrTo(ref),
LastTransitionTime: ptrTo(time.Now().UTC()),
}
}
func ptrTo[T any](val T) *T {
return &val
}

View File

@ -4,7 +4,10 @@
package api
import (
"fmt"
"time"
"golang.org/x/exp/slices"
)
// ResourceReference is a reference to a ConfigEntry
@ -46,7 +49,7 @@ type Condition struct {
// Type is a value from a bounded set of types that an object might have
Type string
// Status is a value from a bounded set of statuses that an object might have
Status string
Status ConditionStatus
// Reason is a value from a bounded set of reasons for a given status
Reason string
// Message is a message that gives more detailed information about
@ -58,3 +61,280 @@ type Condition struct {
// LastTransitionTime is the time at which this Condition was created
LastTransitionTime *time.Time
}
type (
ConditionStatus string
)
const (
ConditionStatusTrue ConditionStatus = "True"
ConditionStatusFalse ConditionStatus = "False"
ConditionStatusUnknown ConditionStatus = "Unknown"
)
// GatewayConditionType is a type of condition associated with a
// Gateway. This type should be used with the GatewayStatus.Conditions
// field.
type GatewayConditionType string
// GatewayConditionReason defines the set of reasons that explain why a
// particular Gateway condition type has been raised.
type GatewayConditionReason string
// the following are directly from the k8s spec
const (
// This condition is true when the controller managing the Gateway is
// syntactically and semantically valid enough to produce some configuration
// in the underlying data plane. This does not indicate whether or not the
// configuration has been propagated to the data plane.
//
// Possible reasons for this condition to be True are:
//
// * "Accepted"
//
// Possible reasons for this condition to be False are:
//
// * InvalidCertificates
//
GatewayConditionAccepted GatewayConditionType = "Accepted"
// This reason is used with the "Accepted" condition when the condition is
// True.
GatewayReasonAccepted GatewayConditionReason = "Accepted"
// This reason is used with the "Accepted" condition when the gateway has multiple invalid
// certificates and cannot bind to any routes
GatewayReasonInvalidCertificates GatewayConditionReason = "InvalidCertificates"
// This condition indicates that the gateway was unable to resolve
// conflicting specification requirements for this Listener. If a
// Listener is conflicted, its network port should not be configured
// on any network elements.
//
// Possible reasons for this condition to be true are:
//
// * "RouteConflict"
//
// Possible reasons for this condition to be False are:
//
// * "NoConflict"
//
// Controllers may raise this condition with other reasons,
// but should prefer to use the reasons listed above to improve
// interoperability.
GatewayConditionConflicted GatewayConditionType = "Conflicted"
// This reason is used with the "Conflicted" condition when the condition
// is False.
GatewayReasonNoConflict GatewayConditionReason = "NoConflict"
// This reason is used with the "Conflicted" condition when the route is
// in a conflicted state, such as when a TCPListener attempts to bind to two routes
GatewayReasonRouteConflict GatewayConditionReason = "RouteConflict"
// This condition indicates whether the controller was able to
// resolve all the object references for the Gateway. When setting this
// condition to False, a ResourceReference to the misconfigured Listener should
// be provided.
//
// Possible reasons for this condition to be true are:
//
// * "ResolvedRefs"
//
// Possible reasons for this condition to be False are:
//
// * "InvalidCertificateRef"
// * "InvalidRouteKinds"
// * "RefNotPermitted"
//
GatewayConditionResolvedRefs GatewayConditionType = "ResolvedRefs"
// This reason is used with the "ResolvedRefs" condition when the condition
// is true.
GatewayReasonResolvedRefs GatewayConditionReason = "ResolvedRefs"
// This reason is used with the "ResolvedRefs" condition when a
// Listener has a TLS configuration with at least one TLS CertificateRef
// that is invalid or does not exist.
// A CertificateRef is considered invalid when it refers to a nonexistent
// or unsupported resource or kind, or when the data within that resource
// is malformed.
// This reason must be used only when the reference is allowed, either by
// referencing an object in the same namespace as the Gateway, or when
// a cross-namespace reference has been explicitly allowed by a ReferenceGrant.
// If the reference is not allowed, the reason RefNotPermitted must be used
// instead.
GatewayListenerReasonInvalidCertificateRef GatewayConditionReason = "InvalidCertificateRef"
)
var validGatewayConditionReasonsMapping = map[GatewayConditionType]map[ConditionStatus][]GatewayConditionReason{
GatewayConditionAccepted: {
ConditionStatusTrue: {
GatewayReasonAccepted,
},
ConditionStatusFalse: {
GatewayListenerReasonInvalidCertificateRef, // TODO: remove this in follow up PR
GatewayReasonInvalidCertificates,
},
ConditionStatusUnknown: {},
},
GatewayConditionConflicted: {
ConditionStatusTrue: {
GatewayReasonRouteConflict,
},
ConditionStatusFalse: {
GatewayReasonNoConflict,
},
ConditionStatusUnknown: {},
},
GatewayConditionResolvedRefs: {
ConditionStatusTrue: {
GatewayReasonResolvedRefs,
},
ConditionStatusFalse: {
GatewayListenerReasonInvalidCertificateRef,
},
ConditionStatusUnknown: {},
},
}
func ValidateGatewayConditionReason(name GatewayConditionType, status ConditionStatus, reason GatewayConditionReason) error {
if err := checkConditionStatus(status); err != nil {
return err
}
reasons, ok := validGatewayConditionReasonsMapping[name]
if !ok {
return fmt.Errorf("unrecognized GatewayConditionType %q", name)
}
reasonsForStatus, ok := reasons[status]
if !ok {
return fmt.Errorf("unrecognized ConditionStatus %q", status)
}
if !slices.Contains(reasonsForStatus, reason) {
return fmt.Errorf("gateway condition reason %q not allowed for gateway condition type %q with status %q", reason, name, status)
}
return nil
}
// RouteConditionType is a type of condition for a route.
type RouteConditionType string
// RouteConditionReason is a reason for a route condition.
type RouteConditionReason string
// The following statuses are taken from the K8's Spec
// With the exception of: "RouteReasonInvalidDiscoveryChain" and "NoUpstreamServicesTargeted"
const (
// This condition indicates whether the route has been accepted or rejected
// by a Gateway, and why.
//
// Possible reasons for this condition to be true are:
//
// * "Accepted"
//
// Possible reasons for this condition to be False are:
//
// * "InvalidDiscoveryChain"
// * "NoUpstreamServicesTargeted"
//
//
// Controllers may raise this condition with other reasons,
// but should prefer to use the reasons listed above to improve
// interoperability.
RouteConditionAccepted RouteConditionType = "Accepted"
// This reason is used with the "Accepted" condition when the Route has been
// accepted by the Gateway.
RouteReasonAccepted RouteConditionReason = "Accepted"
// This reason is used with the "Accepted" condition when the route has an
// invalid discovery chain, this includes conditions like the protocol being invalid
// or the discovery chain failing to compile
RouteReasonInvalidDiscoveryChain RouteConditionReason = "InvalidDiscoveryChain"
// This reason is used with the "Accepted" condition when the route
RouteReasonNoUpstreamServicesTargeted RouteConditionReason = "NoUpstreamServicesTargeted"
)
// the following statuses are custom to Consul
const (
// This condition indicates whether the route was able to successfully bind the
// Listener on the gateway
// Possible reasons for this condition to be true are:
//
// * "Bound"
//
// Possible reasons for this condition to be false are:
//
// * "FailedToBind"
// * "GatewayNotFound"
//
RouteConditionBound RouteConditionType = "Bound"
// This reason is used with the "Bound" condition when the condition
// is true
RouteReasonBound RouteConditionReason = "Bound"
// This reason is used with the "Bound" condition when the route failed
// to bind to the gateway
RouteReasonFailedToBind RouteConditionReason = "FailedToBind"
// This reason is used with the "Bound" condition when the route fails
// to find the gateway
RouteReasonGatewayNotFound RouteConditionReason = "GatewayNotFound"
)
var validRouteConditionReasonsMapping = map[RouteConditionType]map[ConditionStatus][]RouteConditionReason{
RouteConditionAccepted: {
ConditionStatusTrue: {
RouteReasonAccepted,
},
ConditionStatusFalse: {
RouteReasonInvalidDiscoveryChain,
RouteReasonNoUpstreamServicesTargeted,
},
ConditionStatusUnknown: {},
},
RouteConditionBound: {
ConditionStatusTrue: {
RouteReasonBound,
},
ConditionStatusFalse: {
RouteReasonGatewayNotFound,
RouteReasonFailedToBind,
},
ConditionStatusUnknown: {},
},
}
func ValidateRouteConditionReason(name RouteConditionType, status ConditionStatus, reason RouteConditionReason) error {
if err := checkConditionStatus(status); err != nil {
return err
}
reasons, ok := validRouteConditionReasonsMapping[name]
if !ok {
return fmt.Errorf("unrecognized RouteConditionType %s", name)
}
reasonsForStatus, ok := reasons[status]
if !ok {
return fmt.Errorf("unrecognized ConditionStatus %s", name)
}
if !slices.Contains(reasonsForStatus, reason) {
return fmt.Errorf("route condition reason %s not allowed for route condition type %s with status %s", reason, name, status)
}
return nil
}
func checkConditionStatus(status ConditionStatus) error {
switch status {
case ConditionStatusTrue, ConditionStatusFalse, ConditionStatusUnknown:
return nil
default:
return fmt.Errorf("unrecognized condition status: %q", status)
}
}

View File

@ -0,0 +1,187 @@
package api
import "testing"
func TestValidateGatewayConditionReasonWithValidCombinations(t *testing.T) {
testCases := map[string]struct {
status ConditionStatus
reason GatewayConditionReason
condType GatewayConditionType
}{
"accepted": {
status: ConditionStatusTrue,
reason: GatewayReasonAccepted,
condType: GatewayConditionAccepted,
},
"accepted invalid certificates": {
status: ConditionStatusFalse,
reason: GatewayReasonInvalidCertificates,
condType: GatewayConditionAccepted,
},
"conflicted": {
status: ConditionStatusTrue,
reason: GatewayReasonRouteConflict,
condType: GatewayConditionConflicted,
},
"conflicted no conflicts": {
status: ConditionStatusFalse,
reason: GatewayReasonNoConflict,
condType: GatewayConditionConflicted,
},
"resolved refs": {
status: ConditionStatusTrue,
reason: GatewayReasonResolvedRefs,
condType: GatewayConditionResolvedRefs,
},
"resolved refs invalid certificate ref": {
status: ConditionStatusFalse,
reason: GatewayListenerReasonInvalidCertificateRef,
condType: GatewayConditionResolvedRefs,
},
}
for name, tc := range testCases {
t.Run(name, func(t *testing.T) {
err := ValidateGatewayConditionReason(tc.condType, tc.status, tc.reason)
if err != nil {
t.Error("Expected gateway condition reason to be valid but it was not")
}
})
}
}
func TestValidateGatewayConditionReasonWithInvalidCombinationsReturnsError(t *testing.T) {
// This is not an exhaustive list of all invalid combinations, just a few to confirm
testCases := map[string]struct {
status ConditionStatus
reason GatewayConditionReason
condType GatewayConditionType
}{
"reason and condition type are valid but status is not": {
status: ConditionStatusTrue,
reason: GatewayReasonNoConflict,
condType: GatewayConditionConflicted,
},
"reason and status are valid but condition type is not": {
status: ConditionStatusFalse,
reason: GatewayReasonNoConflict,
condType: GatewayConditionResolvedRefs,
},
"condition type and status are valid but status is not": {
status: ConditionStatusTrue,
reason: GatewayReasonNoConflict,
condType: GatewayConditionAccepted,
},
"all are invalid": {
status: ConditionStatusUnknown,
reason: GatewayReasonAccepted,
condType: GatewayConditionResolvedRefs,
},
"pass something other than a condition status": {
status: ConditionStatus("hello"),
reason: GatewayReasonAccepted,
condType: GatewayConditionResolvedRefs,
},
}
for name, tc := range testCases {
t.Run(name, func(t *testing.T) {
err := ValidateGatewayConditionReason(tc.condType, tc.status, tc.reason)
if err == nil {
t.Error("Expected route condition reason to be invalid, but it was valid")
}
})
}
}
func TestValidateRouteConfigReasonWithValidCombinations(t *testing.T) {
testCases := map[string]struct {
status ConditionStatus
reason RouteConditionReason
condType RouteConditionType
}{
"accepted all around": {
status: ConditionStatusTrue,
reason: RouteReasonAccepted,
condType: RouteConditionAccepted,
},
"accepted invalid discovery chain": {
status: ConditionStatusFalse,
reason: RouteReasonInvalidDiscoveryChain,
condType: RouteConditionAccepted,
},
"accepted no upstream services targeted": {
status: ConditionStatusFalse,
reason: RouteReasonNoUpstreamServicesTargeted,
condType: RouteConditionAccepted,
},
"route bound": {
status: ConditionStatusTrue,
reason: RouteReasonBound,
condType: RouteConditionBound,
},
"route bound gateway not found": {
status: ConditionStatusFalse,
reason: RouteReasonGatewayNotFound,
condType: RouteConditionBound,
},
"route bound failed to bind": {
status: ConditionStatusFalse,
reason: RouteReasonFailedToBind,
condType: RouteConditionBound,
},
}
for name, tc := range testCases {
t.Run(name, func(t *testing.T) {
err := ValidateRouteConditionReason(tc.condType, tc.status, tc.reason)
if err != nil {
t.Errorf("Expected route condition reason to be valid, it was not")
}
})
}
}
func TestValidateRouteConditionReasonInvalidCombinationsCausePanic(t *testing.T) {
// This is not an exhaustive list of all invalid combinations, just a few to confirm
testCases := map[string]struct {
status ConditionStatus
reason RouteConditionReason
condType RouteConditionType
}{
"reason and condition type are valid but status is not": {
status: ConditionStatusTrue,
reason: RouteReasonNoUpstreamServicesTargeted,
condType: RouteConditionAccepted,
},
"reason and status are valid but condition type is not": {
status: ConditionStatusFalse,
reason: RouteReasonInvalidDiscoveryChain,
condType: RouteConditionBound,
},
"condition type and status are valid but status is not": {
status: ConditionStatusUnknown,
reason: RouteReasonBound,
condType: RouteConditionBound,
},
"all are invalid": {
status: ConditionStatusUnknown,
reason: RouteReasonGatewayNotFound,
condType: RouteConditionBound,
},
"pass something other than a condition status": {
status: ConditionStatus("hello"),
reason: RouteReasonAccepted,
condType: RouteConditionAccepted,
},
}
for name, tc := range testCases {
t.Run(name, func(t *testing.T) {
err := ValidateRouteConditionReason(tc.condType, tc.status, tc.reason)
if err == nil {
t.Error("Expected route condition reason to be invalid, it was valid")
}
})
}
}

View File

@ -5,7 +5,7 @@ go 1.19
replace github.com/hashicorp/consul/sdk => ../sdk
require (
github.com/google/go-cmp v0.5.7
github.com/google/go-cmp v0.5.8
github.com/hashicorp/consul/sdk v0.13.1
github.com/hashicorp/go-cleanhttp v0.5.1
github.com/hashicorp/go-hclog v0.12.0
@ -14,6 +14,7 @@ require (
github.com/hashicorp/serf v0.10.1
github.com/mitchellh/mapstructure v1.4.1
github.com/stretchr/testify v1.7.0
golang.org/x/exp v0.0.0-20230321023759-10a507213a29
)
require (
@ -40,7 +41,6 @@ require (
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529 // indirect
github.com/stretchr/objx v0.1.0 // indirect
golang.org/x/net v0.0.0-20211216030914-fe4d6282115f // indirect
golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10 // indirect
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect
golang.org/x/sys v0.1.0 // indirect
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c // indirect
)

View File

@ -13,8 +13,8 @@ github.com/fatih/color v1.9.0 h1:8xPHl4/q1VyqGIPif1F+1V3Y3lSmrq01EabUW3CoW5s=
github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU=
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c h1:964Od4U6p2jUkFxvCydnIczKteheJEzHRToSGK3Bnlw=
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/go-cmp v0.5.7 h1:81/ik6ipDQS2aGcBfIN5dHDB36BwrStyeAQquSYCV4o=
github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE=
github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg=
github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/UYA=
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
github.com/hashicorp/go-cleanhttp v0.5.1 h1:dH3aiDG9Jvb5r5+bYHsikaOUIpcM0xvgMXVoDkXMzJM=
@ -96,6 +96,8 @@ github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5Cc
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190923035154-9ee001bba392/go.mod h1:/lpIB1dKB+9EgE3H3cr1v9wB50oz8l4C4h62xy7jSTY=
golang.org/x/exp v0.0.0-20230321023759-10a507213a29 h1:ooxPy7fPvB4kwsA2h+iBNHkAbp/4JxTSwCmvdjEYmug=
golang.org/x/exp v0.0.0-20230321023759-10a507213a29/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
@ -118,8 +120,9 @@ golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210303074136-134d130e1a04/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10 h1:WIoqL4EROvwiPdUtaip4VcDdpZ4kha7wBWZrbVKCIZg=
golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.1.0 h1:kunALQeHf1/185U1i0GOB/fy1IPRDDpuoOOqRReG57U=
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
@ -128,9 +131,6 @@ golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190907020128-2ca718005c18/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=

View File

@ -36,6 +36,7 @@ require (
github.com/mitchellh/mapstructure v1.4.1 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10 // indirect
golang.org/x/exp v0.0.0-20230321023759-10a507213a29 // indirect
golang.org/x/sys v0.1.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)

View File

@ -55,7 +55,7 @@ github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.7 h1:81/ik6ipDQS2aGcBfIN5dHDB36BwrStyeAQquSYCV4o=
github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg=
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=
github.com/hashicorp/consul/sdk v0.13.1 h1:EygWVWWMczTzXGpO93awkHFzfUka6hLYJ0qhETd+6lY=
@ -143,6 +143,8 @@ golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACk
golang.org/x/crypto v0.0.0-20190923035154-9ee001bba392/go.mod h1:/lpIB1dKB+9EgE3H3cr1v9wB50oz8l4C4h62xy7jSTY=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20230321023759-10a507213a29 h1:ooxPy7fPvB4kwsA2h+iBNHkAbp/4JxTSwCmvdjEYmug=
golang.org/x/exp v0.0.0-20230321023759-10a507213a29/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
@ -185,8 +187,9 @@ golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10 h1:WIoqL4EROvwiPdUtaip4VcDdpZ4kha7wBWZrbVKCIZg=
golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.1.0 h1:kunALQeHf1/185U1i0GOB/fy1IPRDDpuoOOqRReG57U=
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
@ -200,7 +203,6 @@ golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBn
golang.org/x/tools v0.0.0-20190907020128-2ca718005c18/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=

4
go.mod
View File

@ -227,11 +227,11 @@ require (
go.opencensus.io v0.23.0 // indirect
go.opentelemetry.io/proto/otlp v0.7.0 // indirect
go.uber.org/atomic v1.9.0 // indirect
golang.org/x/exp v0.0.0-20220827204233-334a2380cb91 // indirect
golang.org/x/exp v0.0.0-20230321023759-10a507213a29 // indirect
golang.org/x/lint v0.0.0-20210508222113-6edffad5e616 // indirect
golang.org/x/term v0.5.0 // indirect
golang.org/x/text v0.7.0 // indirect
golang.org/x/tools v0.1.12 // indirect
golang.org/x/tools v0.2.0 // indirect
google.golang.org/api v0.57.0 // indirect
google.golang.org/appengine v1.6.7 // indirect
gopkg.in/inf.v0 v0.9.1 // indirect

8
go.sum
View File

@ -1115,8 +1115,8 @@ golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u0
golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM=
golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU=
golang.org/x/exp v0.0.0-20220827204233-334a2380cb91 h1:tnebWN09GYg9OLPss1KXj8txwZc6X6uMr6VFdcGNbHw=
golang.org/x/exp v0.0.0-20220827204233-334a2380cb91/go.mod h1:cyybsKvd6eL0RnXn6p/Grxp8F5bW7iYuBgsNCOHpMYE=
golang.org/x/exp v0.0.0-20230321023759-10a507213a29 h1:ooxPy7fPvB4kwsA2h+iBNHkAbp/4JxTSwCmvdjEYmug=
golang.org/x/exp v0.0.0-20230321023759-10a507213a29/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc=
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
@ -1426,8 +1426,8 @@ golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.12 h1:VveCTK38A2rkS8ZqFY25HIDFscX5X9OoEhJd3quQmXU=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/tools v0.2.0 h1:G6AHpWxTMGY1KyEYoAQ5WTtIekUUvDNjan3ugu60JvE=
golang.org/x/tools v0.2.0/go.mod h1:y4OqIKeOV/fWJetJ8bXPU1sEVniLMIyDAZWeHdV+NTA=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=

View File

@ -77,7 +77,7 @@ require (
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529 // indirect
github.com/sirupsen/logrus v1.8.1 // indirect
go.opencensus.io v0.23.0 // indirect
golang.org/x/exp v0.0.0-20230303215020-44a13b063f3e // indirect
golang.org/x/exp v0.0.0-20230321023759-10a507213a29 // indirect
golang.org/x/net v0.8.0 // indirect
golang.org/x/sys v0.6.0 // indirect
golang.org/x/text v0.8.0 // indirect

View File

@ -874,8 +874,8 @@ golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u0
golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM=
golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU=
golang.org/x/exp v0.0.0-20230303215020-44a13b063f3e h1:S8xf0d0OEmWrClvbMiUSp+7cGD00txONylwExlf9wR0=
golang.org/x/exp v0.0.0-20230303215020-44a13b063f3e/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc=
golang.org/x/exp v0.0.0-20230321023759-10a507213a29 h1:ooxPy7fPvB4kwsA2h+iBNHkAbp/4JxTSwCmvdjEYmug=
golang.org/x/exp v0.0.0-20230321023759-10a507213a29/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc=
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=

View File

@ -137,7 +137,7 @@ func isBound(conditions []api.Condition) bool {
func conditionStatusIsValue(typeName string, statusValue string, conditions []api.Condition) bool {
for _, c := range conditions {
if c.Type == typeName && c.Status == statusValue {
if c.Type == typeName && string(c.Status) == statusValue {
return true
}
}

View File

@ -28,7 +28,6 @@ require (
github.com/fatih/color v1.13.0 // indirect
github.com/golang/protobuf v1.5.2 // indirect
github.com/google/btree v1.0.0 // indirect
github.com/google/go-cmp v0.5.8 // indirect
github.com/hashicorp/errwrap v1.1.0 // indirect
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
github.com/hashicorp/go-hclog v1.2.1 // indirect
@ -47,6 +46,7 @@ require (
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
github.com/prometheus/client_model v0.2.0 // indirect
go.opentelemetry.io/proto/otlp v0.7.0 // indirect
golang.org/x/exp v0.0.0-20230321023759-10a507213a29 // indirect
golang.org/x/net v0.4.0 // indirect
golang.org/x/sys v0.3.0 // indirect
golang.org/x/text v0.5.0 // indirect

View File

@ -79,7 +79,6 @@ github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg=
github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=
@ -211,6 +210,8 @@ golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACk
golang.org/x/crypto v0.0.0-20190923035154-9ee001bba392/go.mod h1:/lpIB1dKB+9EgE3H3cr1v9wB50oz8l4C4h62xy7jSTY=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20230321023759-10a507213a29 h1:ooxPy7fPvB4kwsA2h+iBNHkAbp/4JxTSwCmvdjEYmug=
golang.org/x/exp v0.0.0-20230321023759-10a507213a29/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=