From aaf3c051f25ef0e9baaaa603524251991086bbd6 Mon Sep 17 00:00:00 2001 From: "Chris S. Kim" Date: Thu, 23 Jun 2022 11:07:19 -0400 Subject: [PATCH] Make memdb indexers generic (#13558) We have many indexer functions in Consul which take interface{} and type assert before building the index. We can use generics to get rid of the initial plumbing and pass around functions with better defined signatures. This has two benefits: 1) Less verbosity; 2) Developers can parse the argument types to memdb schemas without having to introspect the function for the type assertion. --- agent/consul/state/acl_oss.go | 13 +- agent/consul/state/acl_schema.go | 273 +++++++--------------- agent/consul/state/acl_test.go | 16 +- agent/consul/state/catalog_schema.go | 181 +++++--------- agent/consul/state/config_entry_schema.go | 38 +-- agent/consul/state/coordinate.go | 25 +- agent/consul/state/indexer.go | 87 ++++--- agent/consul/state/kvs.go | 7 +- agent/consul/state/kvs_oss.go | 10 +- agent/consul/state/peering.go | 26 +-- agent/consul/state/peering_oss.go | 21 +- agent/consul/state/query.go | 13 +- agent/consul/state/query_oss.go | 11 +- agent/consul/state/schema.go | 42 ++-- agent/consul/state/session.go | 28 +-- agent/consul/state/session_oss.go | 36 ++- 16 files changed, 303 insertions(+), 524 deletions(-) diff --git a/agent/consul/state/acl_oss.go b/agent/consul/state/acl_oss.go index 67a272c24..686713071 100644 --- a/agent/consul/state/acl_oss.go +++ b/agent/consul/state/acl_oss.go @@ -7,7 +7,7 @@ import ( "fmt" "strings" - memdb "github.com/hashicorp/go-memdb" + "github.com/hashicorp/go-memdb" "github.com/hashicorp/consul/acl" "github.com/hashicorp/consul/agent/structs" @@ -209,18 +209,13 @@ func (s *Store) ACLAuthMethodUpsertValidateEnterprise(method *structs.ACLAuthMet return nil } -func indexAuthMethodFromACLToken(raw interface{}) ([]byte, error) { - p, ok := raw.(*structs.ACLToken) - if !ok { - return nil, fmt.Errorf("unexpected type %T for structs.ACLToken index", raw) - } - - if p.AuthMethod == "" { +func indexAuthMethodFromACLToken(t *structs.ACLToken) ([]byte, error) { + if t.AuthMethod == "" { return nil, errMissingValueForIndex } var b indexBuilder - b.String(strings.ToLower(p.AuthMethod)) + b.String(strings.ToLower(t.AuthMethod)) return b.Bytes(), nil } diff --git a/agent/consul/state/acl_schema.go b/agent/consul/state/acl_schema.go index 5b9529bbd..485b5b92d 100644 --- a/agent/consul/state/acl_schema.go +++ b/agent/consul/state/acl_schema.go @@ -6,7 +6,6 @@ import ( "github.com/hashicorp/go-memdb" - "github.com/hashicorp/consul/acl" "github.com/hashicorp/consul/agent/structs" ) @@ -36,18 +35,18 @@ func tokensTableSchema() *memdb.TableSchema { // DEPRECATED (ACL-Legacy-Compat) - we should not AllowMissing here once legacy compat is removed AllowMissing: true, Unique: true, - Indexer: indexerSingle{ - readIndex: readIndex(indexFromUUIDString), - writeIndex: writeIndex(indexAccessorIDFromACLToken), + Indexer: indexerSingle[string, *structs.ACLToken]{ + readIndex: indexFromUUIDString, + writeIndex: indexAccessorIDFromACLToken, }, }, indexID: { Name: indexID, AllowMissing: false, Unique: true, - Indexer: indexerSingle{ - readIndex: readIndex(indexFromStringCaseSensitive), - writeIndex: writeIndex(indexSecretIDFromACLToken), + Indexer: indexerSingle[string, *structs.ACLToken]{ + readIndex: indexFromStringCaseSensitive, + writeIndex: indexSecretIDFromACLToken, }, }, indexPolicies: { @@ -55,58 +54,58 @@ func tokensTableSchema() *memdb.TableSchema { // Need to allow missing for the anonymous token AllowMissing: true, Unique: false, - Indexer: indexerMulti{ - readIndex: readIndex(indexFromUUIDQuery), - writeIndexMulti: writeIndexMulti(indexPoliciesFromACLToken), + Indexer: indexerMulti[Query, *structs.ACLToken]{ + readIndex: indexFromUUIDQuery, + writeIndexMulti: indexPoliciesFromACLToken, }, }, indexRoles: { Name: indexRoles, AllowMissing: true, Unique: false, - Indexer: indexerMulti{ - readIndex: readIndex(indexFromUUIDQuery), - writeIndexMulti: writeIndexMulti(indexRolesFromACLToken), + Indexer: indexerMulti[Query, *structs.ACLToken]{ + readIndex: indexFromUUIDQuery, + writeIndexMulti: indexRolesFromACLToken, }, }, indexAuthMethod: { Name: indexAuthMethod, AllowMissing: true, Unique: false, - Indexer: indexerSingle{ - readIndex: readIndex(indexFromAuthMethodQuery), - writeIndex: writeIndex(indexAuthMethodFromACLToken), + Indexer: indexerSingle[AuthMethodQuery, *structs.ACLToken]{ + readIndex: indexFromAuthMethodQuery, + writeIndex: indexAuthMethodFromACLToken, }, }, indexLocality: { Name: indexLocality, AllowMissing: false, Unique: false, - Indexer: indexerSingle{ - readIndex: readIndex(indexFromBoolQuery), - writeIndex: writeIndex(indexLocalFromACLToken), + Indexer: indexerSingle[BoolQuery, *structs.ACLToken]{ + readIndex: indexFromBoolQuery, + writeIndex: indexLocalFromACLToken, }, }, indexExpiresGlobal: { Name: indexExpiresGlobal, AllowMissing: true, Unique: false, - Indexer: indexerSingle{ - readIndex: readIndex(indexFromTimeQuery), - writeIndex: writeIndex(indexExpiresGlobalFromACLToken), + Indexer: indexerSingle[*TimeQuery, *structs.ACLToken]{ + readIndex: indexFromTimeQuery, + writeIndex: indexExpiresGlobalFromACLToken, }, }, indexExpiresLocal: { Name: indexExpiresLocal, AllowMissing: true, Unique: false, - Indexer: indexerSingle{ - readIndex: readIndex(indexFromTimeQuery), - writeIndex: writeIndex(indexExpiresLocalFromACLToken), + Indexer: indexerSingle[*TimeQuery, *structs.ACLToken]{ + readIndex: indexFromTimeQuery, + writeIndex: indexExpiresLocalFromACLToken, }, }, - //DEPRECATED (ACL-Legacy-Compat) - This index is only needed while we support upgrading v1 to v2 acls + // DEPRECATED (ACL-Legacy-Compat) - This index is only needed while we support upgrading v1 to v2 acls // This table indexes all the ACL tokens that do not have an AccessorID // TODO(ACL-Legacy-Compat): remove in phase 2 "needs-upgrade": { @@ -142,7 +141,7 @@ func policiesTableSchema() *memdb.TableSchema { Name: indexName, AllowMissing: false, Unique: true, - Indexer: indexerSingleWithPrefix{ + Indexer: indexerSingleWithPrefix[Query, *structs.ACLPolicy, any]{ readIndex: indexFromQuery, writeIndex: indexNameFromACLPolicy, prefixIndex: prefixIndexFromQuery, @@ -152,12 +151,7 @@ func policiesTableSchema() *memdb.TableSchema { } } -func indexNameFromACLPolicy(raw interface{}) ([]byte, error) { - p, ok := raw.(*structs.ACLPolicy) - if !ok { - return nil, fmt.Errorf("unexpected type %T for structs.ACLPolicy index", raw) - } - +func indexNameFromACLPolicy(p *structs.ACLPolicy) ([]byte, error) { if p.Name == "" { return nil, errMissingValueForIndex } @@ -183,7 +177,7 @@ func rolesTableSchema() *memdb.TableSchema { Name: indexName, AllowMissing: false, Unique: true, - Indexer: indexerSingleWithPrefix{ + Indexer: indexerSingleWithPrefix[Query, *structs.ACLRole, any]{ readIndex: indexFromQuery, writeIndex: indexNameFromACLRole, prefixIndex: prefixIndexFromQuery, @@ -194,7 +188,7 @@ func rolesTableSchema() *memdb.TableSchema { // Need to allow missing for the anonymous token AllowMissing: true, Unique: false, - Indexer: indexerMulti{ + Indexer: indexerMulti[Query, *structs.ACLRole]{ readIndex: indexFromUUIDQuery, writeIndexMulti: multiIndexPolicyFromACLRole, }, @@ -203,75 +197,43 @@ func rolesTableSchema() *memdb.TableSchema { } } -func indexNameFromACLRole(raw interface{}) ([]byte, error) { - p, ok := raw.(*structs.ACLRole) - if !ok { - return nil, fmt.Errorf("unexpected type %T for structs.ACLRole index", raw) - } - - if p.Name == "" { +func indexNameFromACLRole(r *structs.ACLRole) ([]byte, error) { + if r.Name == "" { return nil, errMissingValueForIndex } var b indexBuilder - b.String(strings.ToLower(p.Name)) + b.String(strings.ToLower(r.Name)) return b.Bytes(), nil } -func indexFromUUIDQuery(raw interface{}) ([]byte, error) { - q, ok := raw.(Query) - if !ok { - return nil, fmt.Errorf("unexpected type %T for UUIDQuery index", raw) - } +func indexFromUUIDQuery(q Query) ([]byte, error) { return uuidStringToBytes(q.Value) } -func prefixIndexFromUUIDQuery(arg interface{}) ([]byte, error) { - switch v := arg.(type) { - case *acl.EnterpriseMeta: - return nil, nil - case acl.EnterpriseMeta: - return nil, nil - case Query: - return variableLengthUUIDStringToBytes(v.Value) +func prefixIndexFromUUIDWithPeerQuery(q Query) ([]byte, error) { + var b indexBuilder + peername := q.PeerOrEmpty() + if peername == "" { + b.String(structs.LocalPeerKeyword) + } else { + b.String(strings.ToLower(peername)) } - - return nil, fmt.Errorf("unexpected type %T for Query prefix index", arg) + uuidBytes, err := variableLengthUUIDStringToBytes(q.Value) + if err != nil { + return nil, err + } + return append(b.Bytes(), uuidBytes...), nil } -func prefixIndexFromUUIDWithPeerQuery(arg interface{}) ([]byte, error) { - switch v := arg.(type) { - case Query: - var b indexBuilder - peername := v.PeerOrEmpty() - if peername == "" { - b.String(structs.LocalPeerKeyword) - } else { - b.String(strings.ToLower(peername)) - } - uuidBytes, err := variableLengthUUIDStringToBytes(v.Value) - if err != nil { - return nil, err - } - return append(b.Bytes(), uuidBytes...), nil - } - - return nil, fmt.Errorf("unexpected type %T for Query prefix index", arg) -} - -func multiIndexPolicyFromACLRole(raw interface{}) ([][]byte, error) { - role, ok := raw.(*structs.ACLRole) - if !ok { - return nil, fmt.Errorf("unexpected type %T for structs.ACLRole index", raw) - } - - count := len(role.Policies) +func multiIndexPolicyFromACLRole(r *structs.ACLRole) ([][]byte, error) { + count := len(r.Policies) if count == 0 { return nil, errMissingValueForIndex } vals := make([][]byte, 0, count) - for _, link := range role.Policies { + for _, link := range r.Policies { v, err := uuidStringToBytes(link.ID) if err != nil { return nil, err @@ -290,16 +252,16 @@ func bindingRulesTableSchema() *memdb.TableSchema { Name: indexID, AllowMissing: false, Unique: true, - Indexer: indexerSingle{ - readIndex: readIndex(indexFromUUIDString), - writeIndex: writeIndex(indexIDFromACLBindingRule), + Indexer: indexerSingle[string, *structs.ACLBindingRule]{ + readIndex: indexFromUUIDString, + writeIndex: indexIDFromACLBindingRule, }, }, indexAuthMethod: { Name: indexAuthMethod, AllowMissing: false, Unique: false, - Indexer: indexerSingle{ + Indexer: indexerSingle[Query, *structs.ACLBindingRule]{ readIndex: indexFromQuery, writeIndex: indexAuthMethodFromACLBindingRule, }, @@ -308,12 +270,8 @@ func bindingRulesTableSchema() *memdb.TableSchema { } } -func indexIDFromACLBindingRule(raw interface{}) ([]byte, error) { - p, ok := raw.(*structs.ACLBindingRule) - if !ok { - return nil, fmt.Errorf("unexpected type %T for structs.ACLBindingRule index", raw) - } - vv, err := uuidStringToBytes(p.ID) +func indexIDFromACLBindingRule(r *structs.ACLBindingRule) ([]byte, error) { + vv, err := uuidStringToBytes(r.ID) if err != nil { return nil, err } @@ -321,27 +279,18 @@ func indexIDFromACLBindingRule(raw interface{}) ([]byte, error) { return vv, err } -func indexAuthMethodFromACLBindingRule(raw interface{}) ([]byte, error) { - p, ok := raw.(*structs.ACLBindingRule) - if !ok { - return nil, fmt.Errorf("unexpected type %T for structs.ACLBindingRule index", raw) - } - - if p.AuthMethod == "" { +func indexAuthMethodFromACLBindingRule(r *structs.ACLBindingRule) ([]byte, error) { + if r.AuthMethod == "" { return nil, errMissingValueForIndex } var b indexBuilder - b.String(strings.ToLower(p.AuthMethod)) + b.String(strings.ToLower(r.AuthMethod)) return b.Bytes(), nil } -func indexFromUUIDString(raw interface{}) ([]byte, error) { - index, ok := raw.(string) - if !ok { - return nil, fmt.Errorf("unexpected type %T for UUID string index", raw) - } - uuid, err := uuidStringToBytes(index) +func indexFromUUIDString(raw string) ([]byte, error) { + uuid, err := uuidStringToBytes(raw) if err != nil { return nil, err } @@ -350,17 +299,12 @@ func indexFromUUIDString(raw interface{}) ([]byte, error) { return b.Bytes(), nil } -func indexAccessorIDFromACLToken(raw interface{}) ([]byte, error) { - p, ok := raw.(*structs.ACLToken) - if !ok { - return nil, fmt.Errorf("unexpected type %T for structs.ACLToken index", raw) - } - - if p.AccessorID == "" { +func indexAccessorIDFromACLToken(t *structs.ACLToken) ([]byte, error) { + if t.AccessorID == "" { return nil, errMissingValueForIndex } - uuid, err := uuidStringToBytes(p.AccessorID) + uuid, err := uuidStringToBytes(t.AccessorID) if err != nil { return nil, err } @@ -369,37 +313,23 @@ func indexAccessorIDFromACLToken(raw interface{}) ([]byte, error) { return b.Bytes(), nil } -func indexSecretIDFromACLToken(raw interface{}) ([]byte, error) { - p, ok := raw.(*structs.ACLToken) - if !ok { - return nil, fmt.Errorf("unexpected type %T for structs.ACLToken index", raw) - } - - if p.SecretID == "" { +func indexSecretIDFromACLToken(t *structs.ACLToken) ([]byte, error) { + if t.SecretID == "" { return nil, errMissingValueForIndex } var b indexBuilder - b.String(p.SecretID) + b.String(t.SecretID) return b.Bytes(), nil } -func indexFromStringCaseSensitive(raw interface{}) ([]byte, error) { - q, ok := raw.(string) - if !ok { - return nil, fmt.Errorf("unexpected type %T for string prefix query", raw) - } - +func indexFromStringCaseSensitive(s string) ([]byte, error) { var b indexBuilder - b.String(q) + b.String(s) return b.Bytes(), nil } -func indexPoliciesFromACLToken(raw interface{}) ([][]byte, error) { - token, ok := raw.(*structs.ACLToken) - if !ok { - return nil, fmt.Errorf("unexpected type %T for structs.ACLToken index", raw) - } +func indexPoliciesFromACLToken(token *structs.ACLToken) ([][]byte, error) { links := token.Policies numLinks := len(links) @@ -420,11 +350,7 @@ func indexPoliciesFromACLToken(raw interface{}) ([][]byte, error) { return vals, nil } -func indexRolesFromACLToken(raw interface{}) ([][]byte, error) { - token, ok := raw.(*structs.ACLToken) - if !ok { - return nil, fmt.Errorf("unexpected type %T for structs.ACLToken index", raw) - } +func indexRolesFromACLToken(token *structs.ACLToken) ([][]byte, error) { links := token.Roles numLinks := len(links) @@ -445,63 +371,45 @@ func indexRolesFromACLToken(raw interface{}) ([][]byte, error) { return vals, nil } -func indexFromBoolQuery(raw interface{}) ([]byte, error) { - q, ok := raw.(BoolQuery) - if !ok { - return nil, fmt.Errorf("unexpected type %T for BoolQuery index", raw) - } +func indexFromBoolQuery(q BoolQuery) ([]byte, error) { var b indexBuilder b.Bool(q.Value) return b.Bytes(), nil } -func indexLocalFromACLToken(raw interface{}) ([]byte, error) { - p, ok := raw.(*structs.ACLToken) - if !ok { - return nil, fmt.Errorf("unexpected type %T for structs.ACLPolicy index", raw) - } - +func indexLocalFromACLToken(token *structs.ACLToken) ([]byte, error) { var b indexBuilder - b.Bool(p.Local) + b.Bool(token.Local) return b.Bytes(), nil } -func indexFromTimeQuery(arg interface{}) ([]byte, error) { - p, ok := arg.(*TimeQuery) - if !ok { - return nil, fmt.Errorf("unexpected type %T for TimeQuery index", arg) - } - +func indexFromTimeQuery(q *TimeQuery) ([]byte, error) { var b indexBuilder - b.Time(p.Value) + b.Time(q.Value) return b.Bytes(), nil } -func indexExpiresLocalFromACLToken(raw interface{}) ([]byte, error) { - return indexExpiresFromACLToken(raw, true) +func indexExpiresLocalFromACLToken(token *structs.ACLToken) ([]byte, error) { + return indexExpiresFromACLToken(token, true) } -func indexExpiresGlobalFromACLToken(raw interface{}) ([]byte, error) { - return indexExpiresFromACLToken(raw, false) +func indexExpiresGlobalFromACLToken(token *structs.ACLToken) ([]byte, error) { + return indexExpiresFromACLToken(token, false) } -func indexExpiresFromACLToken(raw interface{}, local bool) ([]byte, error) { - p, ok := raw.(*structs.ACLToken) - if !ok { - return nil, fmt.Errorf("unexpected type %T for structs.ACLToken index", raw) - } - if p.Local != local { +func indexExpiresFromACLToken(t *structs.ACLToken, local bool) ([]byte, error) { + if t.Local != local { return nil, errMissingValueForIndex } - if !p.HasExpirationTime() { + if !t.HasExpirationTime() { return nil, errMissingValueForIndex } - if p.ExpirationTime.Unix() < 0 { - return nil, fmt.Errorf("token expiration time cannot be before the unix epoch: %s", p.ExpirationTime) + if t.ExpirationTime.Unix() < 0 { + return nil, fmt.Errorf("token expiration time cannot be before the unix epoch: %s", t.ExpirationTime) } var b indexBuilder - b.Time(*p.ExpirationTime) + b.Time(*t.ExpirationTime) return b.Bytes(), nil } @@ -513,7 +421,7 @@ func authMethodsTableSchema() *memdb.TableSchema { Name: indexID, AllowMissing: false, Unique: true, - Indexer: indexerSingle{ + Indexer: indexerSingle[Query, *structs.ACLAuthMethod]{ readIndex: indexFromQuery, writeIndex: indexNameFromACLAuthMethod, }, @@ -522,17 +430,12 @@ func authMethodsTableSchema() *memdb.TableSchema { } } -func indexNameFromACLAuthMethod(raw interface{}) ([]byte, error) { - p, ok := raw.(*structs.ACLAuthMethod) - if !ok { - return nil, fmt.Errorf("unexpected type %T for structs.ACLAuthMethod index", raw) - } - - if p.Name == "" { +func indexNameFromACLAuthMethod(m *structs.ACLAuthMethod) ([]byte, error) { + if m.Name == "" { return nil, errMissingValueForIndex } var b indexBuilder - b.String(strings.ToLower(p.Name)) + b.String(strings.ToLower(m.Name)) return b.Bytes(), nil } diff --git a/agent/consul/state/acl_test.go b/agent/consul/state/acl_test.go index 358b1dea8..9634bf52f 100644 --- a/agent/consul/state/acl_test.go +++ b/agent/consul/state/acl_test.go @@ -7,14 +7,14 @@ import ( "testing" "time" - memdb "github.com/hashicorp/go-memdb" + "github.com/hashicorp/go-memdb" "github.com/hashicorp/go-uuid" "github.com/stretchr/testify/require" "github.com/hashicorp/consul/acl" "github.com/hashicorp/consul/agent/structs" "github.com/hashicorp/consul/lib" - pbacl "github.com/hashicorp/consul/proto/pbacl" + "github.com/hashicorp/consul/proto/pbacl" ) const ( @@ -3702,18 +3702,18 @@ func TestTokenPoliciesIndex(t *testing.T) { Name: "global", AllowMissing: true, Unique: false, - Indexer: indexerSingle{ - readIndex: readIndex(indexFromTimeQuery), - writeIndex: writeIndex(indexExpiresGlobalFromACLToken), + Indexer: indexerSingle[*TimeQuery, *structs.ACLToken]{ + readIndex: indexFromTimeQuery, + writeIndex: indexExpiresGlobalFromACLToken, }, } localIndex := &memdb.IndexSchema{ Name: "local", AllowMissing: true, Unique: false, - Indexer: indexerSingle{ - readIndex: readIndex(indexFromTimeQuery), - writeIndex: writeIndex(indexExpiresLocalFromACLToken), + Indexer: indexerSingle[*TimeQuery, *structs.ACLToken]{ + readIndex: indexFromTimeQuery, + writeIndex: indexExpiresLocalFromACLToken, }, } schema := &memdb.DBSchema{ diff --git a/agent/consul/state/catalog_schema.go b/agent/consul/state/catalog_schema.go index 9a2fbecad..552d9c0bd 100644 --- a/agent/consul/state/catalog_schema.go +++ b/agent/consul/state/catalog_schema.go @@ -47,7 +47,7 @@ func nodesTableSchema() *memdb.TableSchema { Name: indexID, AllowMissing: false, Unique: true, - Indexer: indexerSingleWithPrefix{ + Indexer: indexerSingleWithPrefix[Query, *structs.Node, any]{ readIndex: indexWithPeerName(indexFromQuery), writeIndex: indexWithPeerName(indexFromNode), prefixIndex: prefixIndexFromQueryWithPeer, @@ -57,7 +57,7 @@ func nodesTableSchema() *memdb.TableSchema { Name: indexUUID, AllowMissing: true, Unique: true, - Indexer: indexerSingleWithPrefix{ + Indexer: indexerSingleWithPrefix[Query, *structs.Node, Query]{ readIndex: indexWithPeerName(indexFromUUIDQuery), writeIndex: indexWithPeerName(indexIDFromNode), prefixIndex: prefixIndexFromUUIDWithPeerQuery, @@ -67,7 +67,7 @@ func nodesTableSchema() *memdb.TableSchema { Name: indexMeta, AllowMissing: true, Unique: false, - Indexer: indexerMulti{ + Indexer: indexerMulti[KeyValueQuery, *structs.Node]{ readIndex: indexWithPeerName(indexFromKeyValueQuery), writeIndexMulti: multiIndexWithPeerName(indexMetaFromNode), }, @@ -76,12 +76,7 @@ func nodesTableSchema() *memdb.TableSchema { } } -func indexFromNode(raw interface{}) ([]byte, error) { - n, ok := raw.(*structs.Node) - if !ok { - return nil, fmt.Errorf("unexpected type %T for structs.Node index", raw) - } - +func indexFromNode(n *structs.Node) ([]byte, error) { if n.Node == "" { return nil, errMissingValueForIndex } @@ -91,12 +86,7 @@ func indexFromNode(raw interface{}) ([]byte, error) { return b.Bytes(), nil } -func indexIDFromNode(raw interface{}) ([]byte, error) { - n, ok := raw.(*structs.Node) - if !ok { - return nil, fmt.Errorf("unexpected type %T for structs.Node index", raw) - } - +func indexIDFromNode(n *structs.Node) ([]byte, error) { if n.ID == "" { return nil, errMissingValueForIndex } @@ -109,12 +99,7 @@ func indexIDFromNode(raw interface{}) ([]byte, error) { return v, nil } -func indexMetaFromNode(raw interface{}) ([][]byte, error) { - n, ok := raw.(*structs.Node) - if !ok { - return nil, fmt.Errorf("unexpected type %T for structs.Node index", raw) - } - +func indexMetaFromNode(n *structs.Node) ([][]byte, error) { // NOTE: this is case-sensitive! vals := make([][]byte, 0, len(n.Meta)) @@ -145,7 +130,7 @@ func servicesTableSchema() *memdb.TableSchema { Name: indexID, AllowMissing: false, Unique: true, - Indexer: indexerSingleWithPrefix{ + Indexer: indexerSingleWithPrefix[NodeServiceQuery, *structs.ServiceNode, any]{ readIndex: indexWithPeerName(indexFromNodeServiceQuery), writeIndex: indexWithPeerName(indexFromServiceNode), prefixIndex: prefixIndexFromQueryWithPeer, @@ -155,7 +140,7 @@ func servicesTableSchema() *memdb.TableSchema { Name: indexNode, AllowMissing: false, Unique: false, - Indexer: indexerSingle{ + Indexer: indexerSingle[Query, nodeIdentifier]{ readIndex: indexWithPeerName(indexFromQuery), writeIndex: indexWithPeerName(indexFromNodeIdentity), }, @@ -164,7 +149,7 @@ func servicesTableSchema() *memdb.TableSchema { Name: indexService, AllowMissing: true, Unique: false, - Indexer: indexerSingle{ + Indexer: indexerSingle[Query, *structs.ServiceNode]{ readIndex: indexWithPeerName(indexFromQuery), writeIndex: indexWithPeerName(indexServiceNameFromServiceNode), }, @@ -173,7 +158,7 @@ func servicesTableSchema() *memdb.TableSchema { Name: indexConnect, AllowMissing: true, Unique: false, - Indexer: indexerSingle{ + Indexer: indexerSingle[Query, *structs.ServiceNode]{ readIndex: indexWithPeerName(indexFromQuery), writeIndex: indexWithPeerName(indexConnectNameFromServiceNode), }, @@ -182,7 +167,7 @@ func servicesTableSchema() *memdb.TableSchema { Name: indexKind, AllowMissing: false, Unique: false, - Indexer: indexerSingle{ + Indexer: indexerSingle[Query, *structs.ServiceNode]{ readIndex: indexWithPeerName(indexFromQuery), writeIndex: indexWithPeerName(indexKindFromServiceNode), }, @@ -191,24 +176,14 @@ func servicesTableSchema() *memdb.TableSchema { } } -func indexFromNodeServiceQuery(arg interface{}) ([]byte, error) { - q, ok := arg.(NodeServiceQuery) - if !ok { - return nil, fmt.Errorf("unexpected type %T for NodeServiceQuery index", arg) - } - +func indexFromNodeServiceQuery(q NodeServiceQuery) ([]byte, error) { var b indexBuilder b.String(strings.ToLower(q.Node)) b.String(strings.ToLower(q.Service)) return b.Bytes(), nil } -func indexFromServiceNode(raw interface{}) ([]byte, error) { - n, ok := raw.(*structs.ServiceNode) - if !ok { - return nil, fmt.Errorf("unexpected type %T for structs.ServiceNode index", raw) - } - +func indexFromServiceNode(n *structs.ServiceNode) ([]byte, error) { if n.Node == "" { return nil, errMissingValueForIndex } @@ -219,14 +194,17 @@ func indexFromServiceNode(raw interface{}) ([]byte, error) { return b.Bytes(), nil } -func indexFromNodeIdentity(raw interface{}) ([]byte, error) { - n, ok := raw.(interface { - NodeIdentity() structs.Identity - }) - if !ok { - return nil, fmt.Errorf("unexpected type %T for index, type must provide NodeIdentity()", raw) - } +type nodeIdentifier interface { + partitionIndexable + peerIndexable + NodeIdentity() structs.Identity +} + +var _ nodeIdentifier = (*structs.HealthCheck)(nil) +var _ nodeIdentifier = (*structs.ServiceNode)(nil) + +func indexFromNodeIdentity(n nodeIdentifier) ([]byte, error) { id := n.NodeIdentity() if id.ID == "" { return nil, errMissingValueForIndex @@ -237,12 +215,7 @@ func indexFromNodeIdentity(raw interface{}) ([]byte, error) { return b.Bytes(), nil } -func indexServiceNameFromServiceNode(raw interface{}) ([]byte, error) { - n, ok := raw.(*structs.ServiceNode) - if !ok { - return nil, fmt.Errorf("unexpected type %T for structs.ServiceNode index", raw) - } - +func indexServiceNameFromServiceNode(n *structs.ServiceNode) ([]byte, error) { if n.Node == "" { return nil, errMissingValueForIndex } @@ -252,12 +225,7 @@ func indexServiceNameFromServiceNode(raw interface{}) ([]byte, error) { return b.Bytes(), nil } -func indexConnectNameFromServiceNode(raw interface{}) ([]byte, error) { - n, ok := raw.(*structs.ServiceNode) - if !ok { - return nil, fmt.Errorf("unexpected type %T for structs.ServiceNode index", raw) - } - +func indexConnectNameFromServiceNode(n *structs.ServiceNode) ([]byte, error) { name, ok := connectNameFromServiceNode(n) if !ok { return nil, errMissingValueForIndex @@ -284,33 +252,23 @@ func connectNameFromServiceNode(sn *structs.ServiceNode) (string, bool) { } } -func indexKindFromServiceNode(raw interface{}) ([]byte, error) { - n, ok := raw.(*structs.ServiceNode) - if !ok { - return nil, fmt.Errorf("unexpected type %T for structs.ServiceNode index", raw) - } - +func indexKindFromServiceNode(n *structs.ServiceNode) ([]byte, error) { var b indexBuilder b.String(strings.ToLower(string(n.ServiceKind))) return b.Bytes(), nil } // indexWithPeerName adds peer name to the index. -func indexWithPeerName( - fn func(interface{}) ([]byte, error), -) func(interface{}) ([]byte, error) { - return func(raw interface{}) ([]byte, error) { - v, err := fn(raw) +func indexWithPeerName[T peerIndexable]( + fn func(T) ([]byte, error), +) func(T) ([]byte, error) { + return func(e T) ([]byte, error) { + v, err := fn(e) if err != nil { return nil, err } - n, ok := raw.(peerIndexable) - if !ok { - return nil, fmt.Errorf("type must be peerIndexable: %T", raw) - } - - peername := n.PeerOrEmpty() + peername := e.PeerOrEmpty() if peername == "" { peername = structs.LocalPeerKeyword } @@ -322,20 +280,20 @@ func indexWithPeerName( } // multiIndexWithPeerName adds peer name to multiple indices, and returns multiple indices. -func multiIndexWithPeerName( - fn func(interface{}) ([][]byte, error), -) func(interface{}) ([][]byte, error) { - return func(raw interface{}) ([][]byte, error) { +func multiIndexWithPeerName[T any]( + fn func(T) ([][]byte, error), +) func(T) ([][]byte, error) { + return func(raw T) ([][]byte, error) { + n, ok := any(raw).(peerIndexable) + if !ok { + return nil, fmt.Errorf("type must be peerIndexable: %T", raw) + } + results, err := fn(raw) if err != nil { return nil, err } - n, ok := raw.(peerIndexable) - if !ok { - return nil, fmt.Errorf("type must be peerIndexable: %T", raw) - } - peername := n.PeerOrEmpty() if peername == "" { peername = structs.LocalPeerKeyword @@ -361,7 +319,7 @@ func checksTableSchema() *memdb.TableSchema { Name: indexID, AllowMissing: false, Unique: true, - Indexer: indexerSingleWithPrefix{ + Indexer: indexerSingleWithPrefix[NodeCheckQuery, *structs.HealthCheck, any]{ readIndex: indexWithPeerName(indexFromNodeCheckQuery), writeIndex: indexWithPeerName(indexFromHealthCheck), prefixIndex: prefixIndexFromQueryWithPeer, @@ -371,7 +329,7 @@ func checksTableSchema() *memdb.TableSchema { Name: indexStatus, AllowMissing: false, Unique: false, - Indexer: indexerSingle{ + Indexer: indexerSingle[Query, *structs.HealthCheck]{ readIndex: indexWithPeerName(indexFromQuery), writeIndex: indexWithPeerName(indexStatusFromHealthCheck), }, @@ -380,7 +338,7 @@ func checksTableSchema() *memdb.TableSchema { Name: indexService, AllowMissing: true, Unique: false, - Indexer: indexerSingle{ + Indexer: indexerSingle[Query, *structs.HealthCheck]{ readIndex: indexWithPeerName(indexFromQuery), writeIndex: indexWithPeerName(indexServiceNameFromHealthCheck), }, @@ -389,7 +347,7 @@ func checksTableSchema() *memdb.TableSchema { Name: indexNode, AllowMissing: true, Unique: false, - Indexer: indexerSingle{ + Indexer: indexerSingle[Query, nodeIdentifier]{ readIndex: indexWithPeerName(indexFromQuery), writeIndex: indexWithPeerName(indexFromNodeIdentity), }, @@ -398,7 +356,7 @@ func checksTableSchema() *memdb.TableSchema { Name: indexNodeService, AllowMissing: true, Unique: false, - Indexer: indexerSingle{ + Indexer: indexerSingle[NodeServiceQuery, *structs.HealthCheck]{ readIndex: indexWithPeerName(indexFromNodeServiceQuery), writeIndex: indexWithPeerName(indexNodeServiceFromHealthCheck), }, @@ -407,28 +365,18 @@ func checksTableSchema() *memdb.TableSchema { } } -func indexFromNodeCheckQuery(raw interface{}) ([]byte, error) { - hc, ok := raw.(NodeCheckQuery) - if !ok { - return nil, fmt.Errorf("unexpected type %T for NodeCheckQuery index", raw) - } - - if hc.Node == "" || hc.CheckID == "" { +func indexFromNodeCheckQuery(q NodeCheckQuery) ([]byte, error) { + if q.Node == "" || q.CheckID == "" { return nil, errMissingValueForIndex } var b indexBuilder - b.String(strings.ToLower(hc.Node)) - b.String(strings.ToLower(hc.CheckID)) + b.String(strings.ToLower(q.Node)) + b.String(strings.ToLower(q.CheckID)) return b.Bytes(), nil } -func indexFromHealthCheck(raw interface{}) ([]byte, error) { - hc, ok := raw.(*structs.HealthCheck) - if !ok { - return nil, fmt.Errorf("unexpected type %T for structs.HealthCheck index", raw) - } - +func indexFromHealthCheck(hc *structs.HealthCheck) ([]byte, error) { if hc.Node == "" || hc.CheckID == "" { return nil, errMissingValueForIndex } @@ -439,12 +387,7 @@ func indexFromHealthCheck(raw interface{}) ([]byte, error) { return b.Bytes(), nil } -func indexNodeServiceFromHealthCheck(raw interface{}) ([]byte, error) { - hc, ok := raw.(*structs.HealthCheck) - if !ok { - return nil, fmt.Errorf("unexpected type %T for structs.HealthCheck index", raw) - } - +func indexNodeServiceFromHealthCheck(hc *structs.HealthCheck) ([]byte, error) { if hc.Node == "" { return nil, errMissingValueForIndex } @@ -455,12 +398,7 @@ func indexNodeServiceFromHealthCheck(raw interface{}) ([]byte, error) { return b.Bytes(), nil } -func indexStatusFromHealthCheck(raw interface{}) ([]byte, error) { - hc, ok := raw.(*structs.HealthCheck) - if !ok { - return nil, fmt.Errorf("unexpected type %T for structs.HealthCheck index", raw) - } - +func indexStatusFromHealthCheck(hc *structs.HealthCheck) ([]byte, error) { if hc.Status == "" { return nil, errMissingValueForIndex } @@ -470,12 +408,7 @@ func indexStatusFromHealthCheck(raw interface{}) ([]byte, error) { return b.Bytes(), nil } -func indexServiceNameFromHealthCheck(raw interface{}) ([]byte, error) { - hc, ok := raw.(*structs.HealthCheck) - if !ok { - return nil, fmt.Errorf("unexpected type %T for structs.HealthCheck index", raw) - } - +func indexServiceNameFromHealthCheck(hc *structs.HealthCheck) ([]byte, error) { if hc.ServiceName == "" { return nil, errMissingValueForIndex } @@ -761,7 +694,7 @@ func kindServiceNameTableSchema() *memdb.TableSchema { Name: indexID, AllowMissing: false, Unique: true, - Indexer: indexerSingle{ + Indexer: indexerSingle[any, any]{ readIndex: indexFromKindServiceName, writeIndex: indexFromKindServiceName, }, @@ -770,7 +703,7 @@ func kindServiceNameTableSchema() *memdb.TableSchema { Name: indexKind, AllowMissing: false, Unique: false, - Indexer: indexerSingle{ + Indexer: indexerSingle[enterpriseIndexable, enterpriseIndexable]{ readIndex: indexFromKindServiceNameKindOnly, writeIndex: indexFromKindServiceNameKindOnly, }, @@ -798,7 +731,7 @@ func (q KindServiceNameQuery) PartitionOrDefault() string { return q.EnterpriseMeta.PartitionOrDefault() } -func indexFromKindServiceNameKindOnly(raw interface{}) ([]byte, error) { +func indexFromKindServiceNameKindOnly(raw enterpriseIndexable) ([]byte, error) { switch x := raw.(type) { case *KindServiceName: var b indexBuilder diff --git a/agent/consul/state/config_entry_schema.go b/agent/consul/state/config_entry_schema.go index 53c03d597..e99068d10 100644 --- a/agent/consul/state/config_entry_schema.go +++ b/agent/consul/state/config_entry_schema.go @@ -1,7 +1,6 @@ package state import ( - "fmt" "strings" "github.com/hashicorp/go-memdb" @@ -27,7 +26,7 @@ func configTableSchema() *memdb.TableSchema { Name: indexID, AllowMissing: false, Unique: true, - Indexer: indexerSingleWithPrefix{ + Indexer: indexerSingleWithPrefix[any, structs.ConfigEntry, any]{ readIndex: indexFromConfigEntryKindName, writeIndex: indexFromConfigEntry, prefixIndex: indexFromConfigEntryKindName, @@ -55,12 +54,30 @@ func configTableSchema() *memdb.TableSchema { } } -func indexFromConfigEntry(raw interface{}) ([]byte, error) { - c, ok := raw.(structs.ConfigEntry) - if !ok { - return nil, fmt.Errorf("type must be structs.ConfigEntry: %T", raw) - } +// configEntryIndexable is required because while structs.ConfigEntry +// has a GetEnterpriseMeta method, it does not directly expose the +// required NamespaceOrDefault and PartitionOrDefault methods of +// enterpriseIndexable. +// +// Config entries that embed *acl.EnterpriseMeta will automatically +// implement this interface. +type configEntryIndexable interface { + structs.ConfigEntry + enterpriseIndexable +} +var _ configEntryIndexable = (*structs.ExportedServicesConfigEntry)(nil) +var _ configEntryIndexable = (*structs.IngressGatewayConfigEntry)(nil) +var _ configEntryIndexable = (*structs.MeshConfigEntry)(nil) +var _ configEntryIndexable = (*structs.ProxyConfigEntry)(nil) +var _ configEntryIndexable = (*structs.ServiceConfigEntry)(nil) +var _ configEntryIndexable = (*structs.ServiceIntentionsConfigEntry)(nil) +var _ configEntryIndexable = (*structs.ServiceResolverConfigEntry)(nil) +var _ configEntryIndexable = (*structs.ServiceRouterConfigEntry)(nil) +var _ configEntryIndexable = (*structs.ServiceSplitterConfigEntry)(nil) +var _ configEntryIndexable = (*structs.TerminatingGatewayConfigEntry)(nil) + +func indexFromConfigEntry(c structs.ConfigEntry) ([]byte, error) { if c.GetName() == "" || c.GetKind() == "" { return nil, errMissingValueForIndex } @@ -73,12 +90,7 @@ func indexFromConfigEntry(raw interface{}) ([]byte, error) { // indexKindFromConfigEntry indexes kinds without a namespace for any config // entries that span all namespaces. -func indexKindFromConfigEntry(raw interface{}) ([]byte, error) { - c, ok := raw.(structs.ConfigEntry) - if !ok { - return nil, fmt.Errorf("type must be structs.ConfigEntry: %T", raw) - } - +func indexKindFromConfigEntry(c configEntryIndexable) ([]byte, error) { if c.GetKind() == "" { return nil, errMissingValueForIndex } diff --git a/agent/consul/state/coordinate.go b/agent/consul/state/coordinate.go index 0cbccf25c..19ae722f8 100644 --- a/agent/consul/state/coordinate.go +++ b/agent/consul/state/coordinate.go @@ -13,12 +13,7 @@ import ( const tableCoordinates = "coordinates" -func indexFromCoordinate(raw interface{}) ([]byte, error) { - c, ok := raw.(*structs.Coordinate) - if !ok { - return nil, fmt.Errorf("unexpected type %T for structs.Coordinate index", raw) - } - +func indexFromCoordinate(c *structs.Coordinate) ([]byte, error) { if c.Node == "" { return nil, errMissingValueForIndex } @@ -29,12 +24,7 @@ func indexFromCoordinate(raw interface{}) ([]byte, error) { return b.Bytes(), nil } -func indexNodeFromCoordinate(raw interface{}) ([]byte, error) { - c, ok := raw.(*structs.Coordinate) - if !ok { - return nil, fmt.Errorf("unexpected type %T for structs.Coordinate index", raw) - } - +func indexNodeFromCoordinate(c *structs.Coordinate) ([]byte, error) { if c.Node == "" { return nil, errMissingValueForIndex } @@ -44,12 +34,7 @@ func indexNodeFromCoordinate(raw interface{}) ([]byte, error) { return b.Bytes(), nil } -func indexFromCoordinateQuery(raw interface{}) ([]byte, error) { - q, ok := raw.(CoordinateQuery) - if !ok { - return nil, fmt.Errorf("unexpected type %T for CoordinateQuery index", raw) - } - +func indexFromCoordinateQuery(q CoordinateQuery) ([]byte, error) { if q.Node == "" { return nil, errMissingValueForIndex } @@ -80,7 +65,7 @@ func coordinatesTableSchema() *memdb.TableSchema { Name: indexID, AllowMissing: false, Unique: true, - Indexer: indexerSingleWithPrefix{ + Indexer: indexerSingleWithPrefix[CoordinateQuery, *structs.Coordinate, any]{ readIndex: indexFromCoordinateQuery, writeIndex: indexFromCoordinate, prefixIndex: prefixIndexFromQueryNoNamespace, @@ -90,7 +75,7 @@ func coordinatesTableSchema() *memdb.TableSchema { Name: indexNode, AllowMissing: false, Unique: false, - Indexer: indexerSingle{ + Indexer: indexerSingle[Query, *structs.Coordinate]{ readIndex: indexFromQuery, writeIndex: indexNodeFromCoordinate, }, diff --git a/agent/consul/state/indexer.go b/agent/consul/state/indexer.go index 70b769c58..83f205a2f 100644 --- a/agent/consul/state/indexer.go +++ b/agent/consul/state/indexer.go @@ -15,32 +15,42 @@ import ( // indexerSingle implements both memdb.Indexer and memdb.SingleIndexer. It may // be used in a memdb.IndexSchema to specify functions that generate the index // value for memdb.Txn operations. -type indexerSingle struct { +// +// R represents the type used to generate the read index. +// W represents the type used to generate the write index. +type indexerSingle[R, W any] struct { // readIndex is used by memdb for Txn.Get, Txn.First, and other operations // that read data. - readIndex + readIndex[R] // writeIndex is used by memdb for Txn.Insert, Txn.Delete, for operations // that write data to the index. - writeIndex + writeIndex[W] } // indexerMulti implements both memdb.Indexer and memdb.MultiIndexer. It may // be used in a memdb.IndexSchema to specify functions that generate the index // value for memdb.Txn operations. -type indexerMulti struct { +// +// R represents the type used to generate the read index. +// W represents the type used to generate the write index. +type indexerMulti[R, W any] struct { // readIndex is used by memdb for Txn.Get, Txn.First, and other operations // that read data. - readIndex + readIndex[R] // writeIndexMulti is used by memdb for Txn.Insert, Txn.Delete, for operations // that write data to the index. - writeIndexMulti + writeIndexMulti[W] } // indexerSingleWithPrefix is a indexerSingle which also supports prefix queries. -type indexerSingleWithPrefix struct { - readIndex - writeIndex - prefixIndex +// +// R represents the type used to generate the read index. +// W represents the type used to generate the write index. +// P represents the type used to generate the prefix index. +type indexerSingleWithPrefix[R, W, P any] struct { + readIndex[R] + writeIndex[W] + prefixIndex[P] } // readIndex implements memdb.Indexer. It exists so that a function can be used @@ -48,13 +58,18 @@ type indexerSingleWithPrefix struct { // // Unlike memdb.Indexer, a readIndex function accepts only a single argument. To // generate an index from multiple values, use a struct type with multiple fields. -type readIndex func(arg interface{}) ([]byte, error) +type readIndex[R any] func(arg R) ([]byte, error) -func (f readIndex) FromArgs(args ...interface{}) ([]byte, error) { +func (f readIndex[R]) FromArgs(args ...interface{}) ([]byte, error) { if len(args) != 1 { return nil, fmt.Errorf("index supports only a single arg") } - return f(args[0]) + arg, ok := args[0].(R) + if !ok { + var typ R + return nil, fmt.Errorf("unexpected type %T, does not implement %T", args[0], typ) + } + return f(arg) } var errMissingValueForIndex = fmt.Errorf("object is missing a value for this index") @@ -65,10 +80,15 @@ var errMissingValueForIndex = fmt.Errorf("object is missing a value for this ind // Instead of a bool return value, writeIndex expects errMissingValueForIndex to // indicate that an index could not be build for the object. It will translate // this error into a false value to satisfy the memdb.SingleIndexer interface. -type writeIndex func(raw interface{}) ([]byte, error) +type writeIndex[W any] func(raw W) ([]byte, error) -func (f writeIndex) FromObject(raw interface{}) (bool, []byte, error) { - v, err := f(raw) +func (f writeIndex[W]) FromObject(raw interface{}) (bool, []byte, error) { + obj, ok := raw.(W) + if !ok { + var typ W + return false, nil, fmt.Errorf("unexpected type %T, does not implement %T", raw, typ) + } + v, err := f(obj) if errors.Is(err, errMissingValueForIndex) { return false, nil, nil } @@ -81,10 +101,15 @@ func (f writeIndex) FromObject(raw interface{}) (bool, []byte, error) { // Instead of a bool return value, writeIndexMulti expects errMissingValueForIndex to // indicate that an index could not be build for the object. It will translate // this error into a false value to satisfy the memdb.MultiIndexer interface. -type writeIndexMulti func(raw interface{}) ([][]byte, error) +type writeIndexMulti[W any] func(raw W) ([][]byte, error) -func (f writeIndexMulti) FromObject(raw interface{}) (bool, [][]byte, error) { - v, err := f(raw) +func (f writeIndexMulti[W]) FromObject(raw interface{}) (bool, [][]byte, error) { + obj, ok := raw.(W) + if !ok { + var typ W + return false, nil, fmt.Errorf("unexpected type %T, does not implement %T", raw, typ) + } + v, err := f(obj) if errors.Is(err, errMissingValueForIndex) { return false, nil, nil } @@ -93,13 +118,18 @@ func (f writeIndexMulti) FromObject(raw interface{}) (bool, [][]byte, error) { // prefixIndex implements memdb.PrefixIndexer. It exists so that a function // can be used to provide this interface. -type prefixIndex func(args interface{}) ([]byte, error) +type prefixIndex[P any] func(args P) ([]byte, error) -func (f prefixIndex) PrefixFromArgs(args ...interface{}) ([]byte, error) { +func (f prefixIndex[P]) PrefixFromArgs(args ...interface{}) ([]byte, error) { if len(args) != 1 { return nil, fmt.Errorf("index supports only a single arg") } - return f(args[0]) + arg, ok := args[0].(P) + if !ok { + var typ P + return nil, fmt.Errorf("unexpected type %T, does not implement %T", args[0], typ) + } + return f(arg) } const null = "\x00" @@ -159,12 +189,7 @@ var _ singleValueID = (*Query)(nil) var _ singleValueID = (*structs.Session)(nil) // indexFromIDValue creates an index key from any struct that implements singleValueID -func indexFromIDValueLowerCase(raw interface{}) ([]byte, error) { - e, ok := raw.(singleValueID) - if !ok { - return nil, fmt.Errorf("unexpected type %T, does not implement singleValueID", raw) - } - +func indexFromIDValueLowerCase(e singleValueID) ([]byte, error) { v := strings.ToLower(e.IDValue()) if v == "" { return nil, errMissingValueForIndex @@ -176,11 +201,7 @@ func indexFromIDValueLowerCase(raw interface{}) ([]byte, error) { } // indexFromIDValue creates an index key from any struct that implements singleValueID -func indexFromMultiValueID(raw interface{}) ([]byte, error) { - e, ok := raw.(multiValueID) - if !ok { - return nil, fmt.Errorf("unexpected type %T, does not implement multiValueID", raw) - } +func indexFromMultiValueID(e multiValueID) ([]byte, error) { var b indexBuilder for _, v := range e.IDValue() { if v == "" { diff --git a/agent/consul/state/kvs.go b/agent/consul/state/kvs.go index 82aa842e8..62797125c 100644 --- a/agent/consul/state/kvs.go +++ b/agent/consul/state/kvs.go @@ -41,12 +41,7 @@ func kvsTableSchema() *memdb.TableSchema { } // indexFromIDValue creates an index key from any struct that implements singleValueID -func indexFromIDValue(raw interface{}) ([]byte, error) { - e, ok := raw.(singleValueID) - if !ok { - return nil, fmt.Errorf("unexpected type %T, does not implement singleValueID", raw) - } - +func indexFromIDValue(e singleValueID) ([]byte, error) { v := e.IDValue() if v == "" { return nil, errMissingValueForIndex diff --git a/agent/consul/state/kvs_oss.go b/agent/consul/state/kvs_oss.go index 3ded43255..149fe2c47 100644 --- a/agent/consul/state/kvs_oss.go +++ b/agent/consul/state/kvs_oss.go @@ -13,11 +13,11 @@ import ( "github.com/hashicorp/consul/agent/structs" ) -func kvsIndexer() indexerSingleWithPrefix { - return indexerSingleWithPrefix{ - readIndex: readIndex(indexFromIDValue), - writeIndex: writeIndex(indexFromIDValue), - prefixIndex: prefixIndex(prefixIndexForIDValue), +func kvsIndexer() indexerSingleWithPrefix[singleValueID, singleValueID, any] { + return indexerSingleWithPrefix[singleValueID, singleValueID, any]{ + readIndex: indexFromIDValue, + writeIndex: indexFromIDValue, + prefixIndex: prefixIndexForIDValue, } } diff --git a/agent/consul/state/peering.go b/agent/consul/state/peering.go index 6515055b2..78595e4e6 100644 --- a/agent/consul/state/peering.go +++ b/agent/consul/state/peering.go @@ -27,16 +27,16 @@ func peeringTableSchema() *memdb.TableSchema { Name: indexID, AllowMissing: false, Unique: true, - Indexer: indexerSingle{ - readIndex: readIndex(indexFromUUIDString), - writeIndex: writeIndex(indexIDFromPeering), + Indexer: indexerSingle[string, *pbpeering.Peering]{ + readIndex: indexFromUUIDString, + writeIndex: indexIDFromPeering, }, }, indexName: { Name: indexName, AllowMissing: false, Unique: true, - Indexer: indexerSingleWithPrefix{ + Indexer: indexerSingleWithPrefix[Query, *pbpeering.Peering, any]{ readIndex: indexPeeringFromQuery, writeIndex: indexFromPeering, prefixIndex: prefixIndexFromQueryNoNamespace, @@ -46,7 +46,7 @@ func peeringTableSchema() *memdb.TableSchema { Name: indexDeleted, AllowMissing: false, Unique: false, - Indexer: indexerSingle{ + Indexer: indexerSingle[BoolQuery, *pbpeering.Peering]{ readIndex: indexDeletedFromBoolQuery, writeIndex: indexDeletedFromPeering, }, @@ -63,7 +63,7 @@ func peeringTrustBundlesTableSchema() *memdb.TableSchema { Name: indexID, AllowMissing: false, Unique: true, - Indexer: indexerSingleWithPrefix{ + Indexer: indexerSingleWithPrefix[Query, *pbpeering.PeeringTrustBundle, any]{ readIndex: indexPeeringFromQuery, // same as peering table since we'll use the query.Value writeIndex: indexFromPeeringTrustBundle, prefixIndex: prefixIndexFromQueryNoNamespace, @@ -73,12 +73,7 @@ func peeringTrustBundlesTableSchema() *memdb.TableSchema { } } -func indexIDFromPeering(raw interface{}) ([]byte, error) { - p, ok := raw.(*pbpeering.Peering) - if !ok { - return nil, fmt.Errorf("unexpected type %T for pbpeering.Peering index", raw) - } - +func indexIDFromPeering(p *pbpeering.Peering) ([]byte, error) { if p.ID == "" { return nil, errMissingValueForIndex } @@ -92,12 +87,7 @@ func indexIDFromPeering(raw interface{}) ([]byte, error) { return b.Bytes(), nil } -func indexDeletedFromPeering(raw interface{}) ([]byte, error) { - p, ok := raw.(*pbpeering.Peering) - if !ok { - return nil, fmt.Errorf("unexpected type %T for *pbpeering.Peering index", raw) - } - +func indexDeletedFromPeering(p *pbpeering.Peering) ([]byte, error) { var b indexBuilder b.Bool(!p.IsActive()) return b.Bytes(), nil diff --git a/agent/consul/state/peering_oss.go b/agent/consul/state/peering_oss.go index 8229d78a6..a331b5648 100644 --- a/agent/consul/state/peering_oss.go +++ b/agent/consul/state/peering_oss.go @@ -10,23 +10,13 @@ import ( "github.com/hashicorp/consul/proto/pbpeering" ) -func indexPeeringFromQuery(raw interface{}) ([]byte, error) { - q, ok := raw.(Query) - if !ok { - return nil, fmt.Errorf("unexpected type %T for Query index", raw) - } - +func indexPeeringFromQuery(q Query) ([]byte, error) { var b indexBuilder b.String(strings.ToLower(q.Value)) return b.Bytes(), nil } -func indexFromPeering(raw interface{}) ([]byte, error) { - p, ok := raw.(*pbpeering.Peering) - if !ok { - return nil, fmt.Errorf("unexpected type %T for structs.Peering index", raw) - } - +func indexFromPeering(p *pbpeering.Peering) ([]byte, error) { if p.Name == "" { return nil, errMissingValueForIndex } @@ -36,12 +26,7 @@ func indexFromPeering(raw interface{}) ([]byte, error) { return b.Bytes(), nil } -func indexFromPeeringTrustBundle(raw interface{}) ([]byte, error) { - ptb, ok := raw.(*pbpeering.PeeringTrustBundle) - if !ok { - return nil, fmt.Errorf("unexpected type %T for pbpeering.PeeringTrustBundle index", raw) - } - +func indexFromPeeringTrustBundle(ptb *pbpeering.PeeringTrustBundle) ([]byte, error) { if ptb.PeerName == "" { return nil, errMissingValueForIndex } diff --git a/agent/consul/state/query.go b/agent/consul/state/query.go index a4725b875..2e4a78869 100644 --- a/agent/consul/state/query.go +++ b/agent/consul/state/query.go @@ -60,12 +60,7 @@ func (q MultiQuery) PartitionOrDefault() string { // indexFromQuery builds an index key where Query.Value is lowercase, and is // a required value. -func indexFromQuery(arg interface{}) ([]byte, error) { - q, ok := arg.(Query) - if !ok { - return nil, fmt.Errorf("unexpected type %T for Query index", arg) - } - +func indexFromQuery(q Query) ([]byte, error) { var b indexBuilder b.String(strings.ToLower(q.Value)) return b.Bytes(), nil @@ -164,12 +159,8 @@ func (q KeyValueQuery) PartitionOrDefault() string { return q.EnterpriseMeta.PartitionOrDefault() } -func indexFromKeyValueQuery(arg interface{}) ([]byte, error) { +func indexFromKeyValueQuery(q KeyValueQuery) ([]byte, error) { // NOTE: this is case-sensitive! - q, ok := arg.(KeyValueQuery) - if !ok { - return nil, fmt.Errorf("unexpected type %T for Query index", arg) - } var b indexBuilder b.String(q.Key) diff --git a/agent/consul/state/query_oss.go b/agent/consul/state/query_oss.go index 553e7aebe..e36850fe0 100644 --- a/agent/consul/state/query_oss.go +++ b/agent/consul/state/query_oss.go @@ -11,7 +11,7 @@ import ( "github.com/hashicorp/consul/agent/structs" ) -func prefixIndexFromQuery(arg interface{}) ([]byte, error) { +func prefixIndexFromQuery(arg any) ([]byte, error) { var b indexBuilder switch v := arg.(type) { case *acl.EnterpriseMeta: @@ -29,7 +29,7 @@ func prefixIndexFromQuery(arg interface{}) ([]byte, error) { return nil, fmt.Errorf("unexpected type %T for Query prefix index", arg) } -func prefixIndexFromQueryWithPeer(arg interface{}) ([]byte, error) { +func prefixIndexFromQueryWithPeer(arg any) ([]byte, error) { var b indexBuilder switch v := arg.(type) { case *acl.EnterpriseMeta: @@ -58,12 +58,7 @@ func prefixIndexFromQueryNoNamespace(arg interface{}) ([]byte, error) { // indexFromAuthMethodQuery builds an index key where Query.Value is lowercase, and is // a required value. -func indexFromAuthMethodQuery(arg interface{}) ([]byte, error) { - q, ok := arg.(AuthMethodQuery) - if !ok { - return nil, fmt.Errorf("unexpected type %T for Query index", arg) - } - +func indexFromAuthMethodQuery(q AuthMethodQuery) ([]byte, error) { var b indexBuilder b.String(strings.ToLower(q.Value)) return b.Bytes(), nil diff --git a/agent/consul/state/schema.go b/agent/consul/state/schema.go index c60bea856..428214bc0 100644 --- a/agent/consul/state/schema.go +++ b/agent/consul/state/schema.go @@ -84,7 +84,7 @@ func indexTableSchema() *memdb.TableSchema { Name: indexID, AllowMissing: false, Unique: true, - Indexer: indexerSingle{ + Indexer: indexerSingle[string, *IndexEntry]{ readIndex: indexFromString, writeIndex: indexNameFromIndexEntry, }, @@ -93,39 +93,37 @@ func indexTableSchema() *memdb.TableSchema { } } -func indexNameFromIndexEntry(raw interface{}) ([]byte, error) { - p, ok := raw.(*IndexEntry) - if !ok { - return nil, fmt.Errorf("unexpected type %T for IndexEntry index", raw) - } - - if p.Key == "" { +func indexNameFromIndexEntry(e *IndexEntry) ([]byte, error) { + if e.Key == "" { return nil, errMissingValueForIndex } var b indexBuilder - b.String(strings.ToLower(p.Key)) + b.String(strings.ToLower(e.Key)) return b.Bytes(), nil } -func indexFromString(raw interface{}) ([]byte, error) { - q, ok := raw.(string) - if !ok { - return nil, fmt.Errorf("unexpected type %T for string prefix query", raw) - } - +func indexFromString(s string) ([]byte, error) { var b indexBuilder - b.String(strings.ToLower(q)) + b.String(strings.ToLower(s)) return b.Bytes(), nil } -func indexDeletedFromBoolQuery(raw interface{}) ([]byte, error) { - q, ok := raw.(BoolQuery) - if !ok { - return nil, fmt.Errorf("unexpected type %T for BoolQuery index", raw) - } - +func indexDeletedFromBoolQuery(q BoolQuery) ([]byte, error) { var b indexBuilder b.Bool(q.Value) return b.Bytes(), nil } + +type enterpriseIndexable interface { + partitionIndexable + namespaceIndexable +} + +type partitionIndexable interface { + PartitionOrDefault() string +} + +type namespaceIndexable interface { + NamespaceOrDefault() string +} diff --git a/agent/consul/state/session.go b/agent/consul/state/session.go index cf2e78b6e..72d079166 100644 --- a/agent/consul/state/session.go +++ b/agent/consul/state/session.go @@ -19,12 +19,7 @@ const ( indexNodeCheck = "node_check" ) -func indexFromSession(raw interface{}) ([]byte, error) { - e, ok := raw.(*structs.Session) - if !ok { - return nil, fmt.Errorf("unexpected type %T, does not implement *structs.Session", raw) - } - +func indexFromSession(e *structs.Session) ([]byte, error) { v := strings.ToLower(e.ID) if v == "" { return nil, errMissingValueForIndex @@ -86,12 +81,7 @@ func sessionChecksTableSchema() *memdb.TableSchema { } // indexNodeFromSession creates an index key from *structs.Session -func indexNodeFromSession(raw interface{}) ([]byte, error) { - e, ok := raw.(*structs.Session) - if !ok { - return nil, fmt.Errorf("unexpected type %T, does not implement *structs.Session", raw) - } - +func indexNodeFromSession(e *structs.Session) ([]byte, error) { v := strings.ToLower(e.Node) if v == "" { return nil, errMissingValueForIndex @@ -103,12 +93,7 @@ func indexNodeFromSession(raw interface{}) ([]byte, error) { } // indexFromNodeCheckIDSession creates an index key from sessionCheck -func indexFromNodeCheckIDSession(raw interface{}) ([]byte, error) { - e, ok := raw.(*sessionCheck) - if !ok { - return nil, fmt.Errorf("unexpected type %T, does not implement sessionCheck", raw) - } - +func indexFromNodeCheckIDSession(e *sessionCheck) ([]byte, error) { var b indexBuilder v := strings.ToLower(e.Node) if v == "" { @@ -132,12 +117,7 @@ func indexFromNodeCheckIDSession(raw interface{}) ([]byte, error) { } // indexSessionCheckFromSession creates an index key from sessionCheck -func indexSessionCheckFromSession(raw interface{}) ([]byte, error) { - e, ok := raw.(*sessionCheck) - if !ok { - return nil, fmt.Errorf("unexpected type %T, does not implement *sessionCheck", raw) - } - +func indexSessionCheckFromSession(e *sessionCheck) ([]byte, error) { var b indexBuilder v := strings.ToLower(e.Session) if v == "" { diff --git a/agent/consul/state/session_oss.go b/agent/consul/state/session_oss.go index 96622387e..40e0b280a 100644 --- a/agent/consul/state/session_oss.go +++ b/agent/consul/state/session_oss.go @@ -14,48 +14,44 @@ import ( "github.com/hashicorp/consul/api" ) -func sessionIndexer() indexerSingleWithPrefix { - return indexerSingleWithPrefix{ - readIndex: readIndex(indexFromQuery), - writeIndex: writeIndex(indexFromSession), - prefixIndex: prefixIndex(prefixIndexFromQuery), +func sessionIndexer() indexerSingleWithPrefix[Query, *structs.Session, any] { + return indexerSingleWithPrefix[Query, *structs.Session, any]{ + readIndex: indexFromQuery, + writeIndex: indexFromSession, + prefixIndex: prefixIndexFromQuery, } } -func nodeSessionsIndexer() indexerSingle { - return indexerSingle{ - readIndex: readIndex(indexFromIDValueLowerCase), - writeIndex: writeIndex(indexNodeFromSession), +func nodeSessionsIndexer() indexerSingle[singleValueID, *structs.Session] { + return indexerSingle[singleValueID, *structs.Session]{ + readIndex: indexFromIDValueLowerCase, + writeIndex: indexNodeFromSession, } } -func idCheckIndexer() indexerSingle { - return indexerSingle{ +func idCheckIndexer() indexerSingle[*sessionCheck, *sessionCheck] { + return indexerSingle[*sessionCheck, *sessionCheck]{ readIndex: indexFromNodeCheckIDSession, writeIndex: indexFromNodeCheckIDSession, } } -func sessionCheckIndexer() indexerSingle { - return indexerSingle{ +func sessionCheckIndexer() indexerSingle[Query, *sessionCheck] { + return indexerSingle[Query, *sessionCheck]{ readIndex: indexFromQuery, writeIndex: indexSessionCheckFromSession, } } -func nodeChecksIndexer() indexerSingle { - return indexerSingle{ +func nodeChecksIndexer() indexerSingle[multiValueID, *sessionCheck] { + return indexerSingle[multiValueID, *sessionCheck]{ readIndex: indexFromMultiValueID, writeIndex: indexFromNodeCheckID, } } // indexFromNodeCheckID creates an index key from a sessionCheck structure -func indexFromNodeCheckID(raw interface{}) ([]byte, error) { - e, ok := raw.(*sessionCheck) - if !ok { - return nil, fmt.Errorf("unexpected type %T, does not implement *structs.Session", raw) - } +func indexFromNodeCheckID(e *sessionCheck) ([]byte, error) { var b indexBuilder v := strings.ToLower(e.Node) if v == "" {