diff --git a/agent/consul/state/catalog.go b/agent/consul/state/catalog.go index 4c22ddc711..57128b4950 100644 --- a/agent/consul/state/catalog.go +++ b/agent/consul/state/catalog.go @@ -28,191 +28,6 @@ const ( serviceLastExtinctionIndexName = "service_last_extinction" ) -// nodesTableSchema returns a new table schema used for storing node -// information. -func nodesTableSchema() *memdb.TableSchema { - return &memdb.TableSchema{ - Name: "nodes", - Indexes: map[string]*memdb.IndexSchema{ - "id": { - Name: "id", - AllowMissing: false, - Unique: true, - Indexer: &memdb.StringFieldIndex{ - Field: "Node", - Lowercase: true, - }, - }, - "uuid": { - Name: "uuid", - AllowMissing: true, - Unique: true, - Indexer: &memdb.UUIDFieldIndex{ - Field: "ID", - }, - }, - "meta": { - Name: "meta", - AllowMissing: true, - Unique: false, - Indexer: &memdb.StringMapFieldIndex{ - Field: "Meta", - Lowercase: false, - }, - }, - }, - } -} - -// gatewayServicesTableNameSchema returns a new table schema used to store information -// about services associated with terminating gateways. -func gatewayServicesTableNameSchema() *memdb.TableSchema { - return &memdb.TableSchema{ - Name: gatewayServicesTableName, - Indexes: map[string]*memdb.IndexSchema{ - "id": { - Name: "id", - AllowMissing: false, - Unique: true, - Indexer: &memdb.CompoundIndex{ - Indexes: []memdb.Indexer{ - &ServiceNameIndex{ - Field: "Gateway", - }, - &ServiceNameIndex{ - Field: "Service", - }, - &memdb.IntFieldIndex{ - Field: "Port", - }, - }, - }, - }, - "gateway": { - Name: "gateway", - AllowMissing: false, - Unique: false, - Indexer: &ServiceNameIndex{ - Field: "Gateway", - }, - }, - "service": { - Name: "service", - AllowMissing: true, - Unique: false, - Indexer: &ServiceNameIndex{ - Field: "Service", - }, - }, - }, - } -} - -// topologyTableNameSchema returns a new table schema used to store information -// relating upstream and downstream services -func topologyTableNameSchema() *memdb.TableSchema { - return &memdb.TableSchema{ - Name: topologyTableName, - Indexes: map[string]*memdb.IndexSchema{ - "id": { - Name: "id", - AllowMissing: false, - Unique: true, - Indexer: &memdb.CompoundIndex{ - Indexes: []memdb.Indexer{ - &ServiceNameIndex{ - Field: "Upstream", - }, - &ServiceNameIndex{ - Field: "Downstream", - }, - }, - }, - }, - "upstream": { - Name: "upstream", - AllowMissing: true, - Unique: false, - Indexer: &ServiceNameIndex{ - Field: "Upstream", - }, - }, - "downstream": { - Name: "downstream", - AllowMissing: false, - Unique: false, - Indexer: &ServiceNameIndex{ - Field: "Downstream", - }, - }, - }, - } -} - -type ServiceNameIndex struct { - Field string -} - -func (index *ServiceNameIndex) FromObject(obj interface{}) (bool, []byte, error) { - v := reflect.ValueOf(obj) - v = reflect.Indirect(v) // Dereference the pointer if any - - fv := v.FieldByName(index.Field) - isPtr := fv.Kind() == reflect.Ptr - fv = reflect.Indirect(fv) - if !isPtr && !fv.IsValid() || !fv.CanInterface() { - return false, nil, - fmt.Errorf("field '%s' for %#v is invalid %v ", index.Field, obj, isPtr) - } - - name, ok := fv.Interface().(structs.ServiceName) - if !ok { - return false, nil, fmt.Errorf("Field 'ServiceName' is not of type structs.ServiceName") - } - - // Enforce lowercase and add null character as terminator - id := strings.ToLower(name.String()) + "\x00" - - return true, []byte(id), nil -} - -func (index *ServiceNameIndex) FromArgs(args ...interface{}) ([]byte, error) { - if len(args) != 1 { - return nil, fmt.Errorf("must provide only a single argument") - } - name, ok := args[0].(structs.ServiceName) - if !ok { - return nil, fmt.Errorf("argument must be of type structs.ServiceName: %#v", args[0]) - } - - // Enforce lowercase and add null character as terminator - id := strings.ToLower(name.String()) + "\x00" - - return []byte(strings.ToLower(id)), nil -} - -func (index *ServiceNameIndex) PrefixFromArgs(args ...interface{}) ([]byte, error) { - val, err := index.FromArgs(args...) - if err != nil { - return nil, err - } - - // Strip the null terminator, the rest is a prefix - n := len(val) - if n > 0 { - return val[:n-1], nil - } - return val, nil -} - -func init() { - registerSchema(nodesTableSchema) - registerSchema(servicesTableSchema) - registerSchema(checksTableSchema) - registerSchema(gatewayServicesTableNameSchema) - registerSchema(topologyTableNameSchema) -} - const ( // minUUIDLookupLen is used as a minimum length of a node name required before // we test to see if the name is actually a UUID and perform an ID-based node diff --git a/agent/consul/state/catalog_oss.go b/agent/consul/state/catalog_oss.go index 05e59427b8..ed92e34df9 100644 --- a/agent/consul/state/catalog_oss.go +++ b/agent/consul/state/catalog_oss.go @@ -5,154 +5,12 @@ package state import ( "fmt" - "github.com/hashicorp/consul/agent/structs" memdb "github.com/hashicorp/go-memdb" + + "github.com/hashicorp/consul/agent/structs" ) -// servicesTableSchema returns a new table schema used to store information -// about services. -func servicesTableSchema() *memdb.TableSchema { - return &memdb.TableSchema{ - Name: "services", - Indexes: map[string]*memdb.IndexSchema{ - "id": { - Name: "id", - AllowMissing: false, - Unique: true, - Indexer: &memdb.CompoundIndex{ - Indexes: []memdb.Indexer{ - &memdb.StringFieldIndex{ - Field: "Node", - Lowercase: true, - }, - &memdb.StringFieldIndex{ - Field: "ServiceID", - Lowercase: true, - }, - }, - }, - }, - "node": { - Name: "node", - AllowMissing: false, - Unique: false, - Indexer: &memdb.StringFieldIndex{ - Field: "Node", - Lowercase: true, - }, - }, - "service": { - Name: "service", - AllowMissing: true, - Unique: false, - Indexer: &memdb.StringFieldIndex{ - Field: "ServiceName", - Lowercase: true, - }, - }, - "connect": { - Name: "connect", - AllowMissing: true, - Unique: false, - Indexer: &IndexConnectService{}, - }, - "kind": { - Name: "kind", - AllowMissing: false, - Unique: false, - Indexer: &IndexServiceKind{}, - }, - }, - } -} - -// checksTableSchema returns a new table schema used for storing and indexing -// health check information. Health checks have a number of different attributes -// we want to filter by, so this table is a bit more complex. -func checksTableSchema() *memdb.TableSchema { - return &memdb.TableSchema{ - Name: "checks", - Indexes: map[string]*memdb.IndexSchema{ - "id": { - Name: "id", - AllowMissing: false, - Unique: true, - Indexer: &memdb.CompoundIndex{ - Indexes: []memdb.Indexer{ - &memdb.StringFieldIndex{ - Field: "Node", - Lowercase: true, - }, - &memdb.StringFieldIndex{ - Field: "CheckID", - Lowercase: true, - }, - }, - }, - }, - "status": { - Name: "status", - AllowMissing: false, - Unique: false, - Indexer: &memdb.StringFieldIndex{ - Field: "Status", - Lowercase: false, - }, - }, - "service": { - Name: "service", - AllowMissing: true, - Unique: false, - Indexer: &memdb.StringFieldIndex{ - Field: "ServiceName", - Lowercase: true, - }, - }, - "node": { - Name: "node", - AllowMissing: true, - Unique: false, - Indexer: &memdb.StringFieldIndex{ - Field: "Node", - Lowercase: true, - }, - }, - "node_service_check": { - Name: "node_service_check", - AllowMissing: true, - Unique: false, - Indexer: &memdb.CompoundIndex{ - Indexes: []memdb.Indexer{ - &memdb.StringFieldIndex{ - Field: "Node", - Lowercase: true, - }, - &memdb.FieldSetIndex{ - Field: "ServiceID", - }, - }, - }, - }, - "node_service": { - Name: "node_service", - AllowMissing: true, - Unique: false, - Indexer: &memdb.CompoundIndex{ - Indexes: []memdb.Indexer{ - &memdb.StringFieldIndex{ - Field: "Node", - Lowercase: true, - }, - &memdb.StringFieldIndex{ - Field: "ServiceID", - Lowercase: true, - }, - }, - }, - }, - }, - } -} +func withEnterpriseSchema(_ *memdb.DBSchema) {} func serviceIndexName(name string, _ *structs.EnterpriseMeta) string { return fmt.Sprintf("service.%s", name) diff --git a/agent/consul/state/catalog_schema.go b/agent/consul/state/catalog_schema.go new file mode 100644 index 0000000000..a38ae349b9 --- /dev/null +++ b/agent/consul/state/catalog_schema.go @@ -0,0 +1,355 @@ +package state + +import ( + "fmt" + "reflect" + "strings" + + "github.com/hashicorp/go-memdb" + + "github.com/hashicorp/consul/agent/structs" +) + +const ( + tableNodes = "nodes" + tableServices = "services" + tableChecks = "checks" + + indexID = "id" + indexServiceName = "service" + indexConnect = "connect" + indexKind = "kind" + indexStatus = "status" + indexNodeServiceCheck = "node_service_check" + indexNodeService = "node_service" +) + +// nodesTableSchema returns a new table schema used for storing node +// information. +func nodesTableSchema() *memdb.TableSchema { + return &memdb.TableSchema{ + Name: tableNodes, + Indexes: map[string]*memdb.IndexSchema{ + "id": { + Name: "id", + AllowMissing: false, + Unique: true, + Indexer: &memdb.StringFieldIndex{ + Field: "Node", + Lowercase: true, + }, + }, + "uuid": { + Name: "uuid", + AllowMissing: true, + Unique: true, + Indexer: &memdb.UUIDFieldIndex{ + Field: "ID", + }, + }, + "meta": { + Name: "meta", + AllowMissing: true, + Unique: false, + Indexer: &memdb.StringMapFieldIndex{ + Field: "Meta", + Lowercase: false, + }, + }, + }, + } +} + +// servicesTableSchema returns a new table schema used to store information +// about services. +func servicesTableSchema() *memdb.TableSchema { + return &memdb.TableSchema{ + Name: tableServices, + Indexes: map[string]*memdb.IndexSchema{ + indexID: { + Name: indexID, + AllowMissing: false, + Unique: true, + Indexer: &memdb.CompoundIndex{ + Indexes: []memdb.Indexer{ + &memdb.StringFieldIndex{ + Field: "Node", + Lowercase: true, + }, + &memdb.StringFieldIndex{ + Field: "ServiceID", + Lowercase: true, + }, + }, + }, + }, + "node": { + Name: "node", + AllowMissing: false, + Unique: false, + Indexer: &memdb.StringFieldIndex{ + Field: "Node", + Lowercase: true, + }, + }, + indexServiceName: { + Name: indexServiceName, + AllowMissing: true, + Unique: false, + Indexer: &memdb.StringFieldIndex{ + Field: "ServiceName", + Lowercase: true, + }, + }, + indexConnect: { + Name: indexConnect, + AllowMissing: true, + Unique: false, + Indexer: &IndexConnectService{}, + }, + indexKind: { + Name: indexKind, + AllowMissing: false, + Unique: false, + Indexer: &IndexServiceKind{}, + }, + }, + } +} + +// checksTableSchema returns a new table schema used for storing and indexing +// health check information. Health checks have a number of different attributes +// we want to filter by, so this table is a bit more complex. +func checksTableSchema() *memdb.TableSchema { + return &memdb.TableSchema{ + Name: tableChecks, + Indexes: map[string]*memdb.IndexSchema{ + indexID: { + Name: indexID, + AllowMissing: false, + Unique: true, + Indexer: &memdb.CompoundIndex{ + Indexes: []memdb.Indexer{ + &memdb.StringFieldIndex{ + Field: "Node", + Lowercase: true, + }, + &memdb.StringFieldIndex{ + Field: "CheckID", + Lowercase: true, + }, + }, + }, + }, + indexStatus: { + Name: indexStatus, + AllowMissing: false, + Unique: false, + Indexer: &memdb.StringFieldIndex{ + Field: "Status", + Lowercase: false, + }, + }, + indexServiceName: { + Name: indexServiceName, + AllowMissing: true, + Unique: false, + Indexer: &memdb.StringFieldIndex{ + Field: "ServiceName", + Lowercase: true, + }, + }, + "node": { + Name: "node", + AllowMissing: true, + Unique: false, + Indexer: &memdb.StringFieldIndex{ + Field: "Node", + Lowercase: true, + }, + }, + indexNodeServiceCheck: { + Name: indexNodeServiceCheck, + AllowMissing: true, + Unique: false, + Indexer: &memdb.CompoundIndex{ + Indexes: []memdb.Indexer{ + &memdb.StringFieldIndex{ + Field: "Node", + Lowercase: true, + }, + &memdb.FieldSetIndex{ + Field: "ServiceID", + }, + }, + }, + }, + indexNodeService: { + Name: indexNodeService, + AllowMissing: true, + Unique: false, + Indexer: &memdb.CompoundIndex{ + Indexes: []memdb.Indexer{ + &memdb.StringFieldIndex{ + Field: "Node", + Lowercase: true, + }, + &memdb.StringFieldIndex{ + Field: "ServiceID", + Lowercase: true, + }, + }, + }, + }, + }, + } +} + +// gatewayServicesTableNameSchema returns a new table schema used to store information +// about services associated with terminating gateways. +func gatewayServicesTableNameSchema() *memdb.TableSchema { + return &memdb.TableSchema{ + Name: gatewayServicesTableName, + Indexes: map[string]*memdb.IndexSchema{ + "id": { + Name: "id", + AllowMissing: false, + Unique: true, + Indexer: &memdb.CompoundIndex{ + Indexes: []memdb.Indexer{ + &ServiceNameIndex{ + Field: "Gateway", + }, + &ServiceNameIndex{ + Field: "Service", + }, + &memdb.IntFieldIndex{ + Field: "Port", + }, + }, + }, + }, + "gateway": { + Name: "gateway", + AllowMissing: false, + Unique: false, + Indexer: &ServiceNameIndex{ + Field: "Gateway", + }, + }, + "service": { + Name: "service", + AllowMissing: true, + Unique: false, + Indexer: &ServiceNameIndex{ + Field: "Service", + }, + }, + }, + } +} + +// topologyTableNameSchema returns a new table schema used to store information +// relating upstream and downstream services +func topologyTableNameSchema() *memdb.TableSchema { + return &memdb.TableSchema{ + Name: topologyTableName, + Indexes: map[string]*memdb.IndexSchema{ + "id": { + Name: "id", + AllowMissing: false, + Unique: true, + Indexer: &memdb.CompoundIndex{ + Indexes: []memdb.Indexer{ + &ServiceNameIndex{ + Field: "Upstream", + }, + &ServiceNameIndex{ + Field: "Downstream", + }, + }, + }, + }, + "upstream": { + Name: "upstream", + AllowMissing: true, + Unique: false, + Indexer: &ServiceNameIndex{ + Field: "Upstream", + }, + }, + "downstream": { + Name: "downstream", + AllowMissing: false, + Unique: false, + Indexer: &ServiceNameIndex{ + Field: "Downstream", + }, + }, + }, + } +} + +type ServiceNameIndex struct { + Field string +} + +func (index *ServiceNameIndex) FromObject(obj interface{}) (bool, []byte, error) { + v := reflect.ValueOf(obj) + v = reflect.Indirect(v) // Dereference the pointer if any + + fv := v.FieldByName(index.Field) + isPtr := fv.Kind() == reflect.Ptr + fv = reflect.Indirect(fv) + if !isPtr && !fv.IsValid() || !fv.CanInterface() { + return false, nil, + fmt.Errorf("field '%s' for %#v is invalid %v ", index.Field, obj, isPtr) + } + + name, ok := fv.Interface().(structs.ServiceName) + if !ok { + return false, nil, fmt.Errorf("Field 'ServiceName' is not of type structs.ServiceName") + } + + // Enforce lowercase and add null character as terminator + id := strings.ToLower(name.String()) + "\x00" + + return true, []byte(id), nil +} + +func (index *ServiceNameIndex) FromArgs(args ...interface{}) ([]byte, error) { + if len(args) != 1 { + return nil, fmt.Errorf("must provide only a single argument") + } + name, ok := args[0].(structs.ServiceName) + if !ok { + return nil, fmt.Errorf("argument must be of type structs.ServiceName: %#v", args[0]) + } + + // Enforce lowercase and add null character as terminator + id := strings.ToLower(name.String()) + "\x00" + + return []byte(strings.ToLower(id)), nil +} + +func (index *ServiceNameIndex) PrefixFromArgs(args ...interface{}) ([]byte, error) { + val, err := index.FromArgs(args...) + if err != nil { + return nil, err + } + + // Strip the null terminator, the rest is a prefix + n := len(val) + if n > 0 { + return val[:n-1], nil + } + return val, nil +} + +func init() { + registerSchema(nodesTableSchema) + registerSchema(servicesTableSchema) + registerSchema(checksTableSchema) + registerSchema(gatewayServicesTableNameSchema) + registerSchema(topologyTableNameSchema) +} diff --git a/agent/consul/state/schema.go b/agent/consul/state/schema.go index 8ab6565a84..9cb0bb93d6 100644 --- a/agent/consul/state/schema.go +++ b/agent/consul/state/schema.go @@ -35,6 +35,7 @@ func stateStoreSchema() *memdb.DBSchema { } db.Tables[schema.Name] = schema } + withEnterpriseSchema(db) return db } diff --git a/agent/consul/state/schema_oss_test.go b/agent/consul/state/schema_oss_test.go new file mode 100644 index 0000000000..120d49603e --- /dev/null +++ b/agent/consul/state/schema_oss_test.go @@ -0,0 +1,5 @@ +// +build !consulent + +package state + +var stateStoreSchemaExpected = "TestStateStoreSchema.golden" diff --git a/agent/consul/state/schema_test.go b/agent/consul/state/schema_test.go index b96a027c2e..0d9a47838f 100644 --- a/agent/consul/state/schema_test.go +++ b/agent/consul/state/schema_test.go @@ -1,17 +1,100 @@ package state import ( + "bytes" + "fmt" + "reflect" + "runtime" + "sort" "testing" "github.com/hashicorp/go-memdb" + "github.com/stretchr/testify/require" + + "github.com/hashicorp/consul/internal/testing/golden" ) -func TestStateStore_Schema(t *testing.T) { - // First call the schema creation +func TestStateStoreSchema(t *testing.T) { schema := stateStoreSchema() + require.NoError(t, schema.Validate()) - // Try to initialize a new memdb using the schema - if _, err := memdb.NewMemDB(schema); err != nil { - t.Fatalf("err: %s", err) + _, err := memdb.NewMemDB(schema) + require.NoError(t, err) + + actual, err := repr(schema) + require.NoError(t, err) + + expected := golden.Get(t, actual, stateStoreSchemaExpected) + require.Equal(t, expected, actual) +} + +func repr(schema *memdb.DBSchema) (string, error) { + tables := make([]string, 0, len(schema.Tables)) + for name := range schema.Tables { + tables = append(tables, name) + } + sort.Strings(tables) + + buf := new(bytes.Buffer) + for _, name := range tables { + fmt.Fprintf(buf, "table=%v\n", name) + + indexes := indexNames(schema.Tables[name]) + for _, i := range indexes { + index := schema.Tables[name].Indexes[i] + fmt.Fprintf(buf, " index=%v", i) + if index.Unique { + buf.WriteString(" unique") + } + if index.AllowMissing { + buf.WriteString(" allow-missing") + } + buf.WriteString("\n") + buf.WriteString(" indexer=") + formatIndexer(buf, index.Indexer) + buf.WriteString("\n") + } + buf.WriteString("\n") + } + + return buf.String(), nil +} + +func formatIndexer(buf *bytes.Buffer, indexer memdb.Indexer) { + v := reflect.Indirect(reflect.ValueOf(indexer)) + typ := v.Type() + buf.WriteString(typ.PkgPath() + "." + typ.Name()) + for i := 0; i < typ.NumField(); i++ { + fmt.Fprintf(buf, " %v=", typ.Field(i).Name) + + field := v.Field(i) + switch typ.Field(i).Type.Kind() { + case reflect.Slice: + buf.WriteString("[") + for j := 0; j < field.Len(); j++ { + if j != 0 { + buf.WriteString(", ") + } + // TODO: handle other types of slices + formatIndexer(buf, v.Field(i).Index(j).Interface().(memdb.Indexer)) + } + buf.WriteString("]") + case reflect.Func: + // Functions are printed as pointer addresses, which change frequently. + // Instead use the name. + buf.WriteString(runtime.FuncForPC(field.Pointer()).Name()) + default: + fmt.Fprintf(buf, "%v", field) + } } } + +func indexNames(table *memdb.TableSchema) []string { + indexes := make([]string, 0, len(table.Indexes)) + for name := range table.Indexes { + indexes = append(indexes, name) + } + + sort.Strings(indexes) + return indexes +} diff --git a/agent/consul/state/testdata/TestStateStoreSchema.golden b/agent/consul/state/testdata/TestStateStoreSchema.golden new file mode 100644 index 0000000000..2b817d0290 --- /dev/null +++ b/agent/consul/state/testdata/TestStateStoreSchema.golden @@ -0,0 +1,188 @@ +table=acl-auth-methods + index=id unique + indexer=github.com/hashicorp/go-memdb.StringFieldIndex Field=Name Lowercase=true + +table=acl-binding-rules + index=authmethod + indexer=github.com/hashicorp/go-memdb.StringFieldIndex Field=AuthMethod Lowercase=true + index=id unique + indexer=github.com/hashicorp/go-memdb.UUIDFieldIndex Field=ID + +table=acl-policies + index=id unique + indexer=github.com/hashicorp/go-memdb.UUIDFieldIndex Field=ID + index=name unique + indexer=github.com/hashicorp/go-memdb.StringFieldIndex Field=Name Lowercase=true + +table=acl-roles + index=id unique + indexer=github.com/hashicorp/go-memdb.UUIDFieldIndex Field=ID + index=name unique + indexer=github.com/hashicorp/go-memdb.StringFieldIndex Field=Name Lowercase=true + index=policies allow-missing + indexer=github.com/hashicorp/consul/agent/consul/state.RolePoliciesIndex + +table=acl-tokens + index=accessor unique allow-missing + indexer=github.com/hashicorp/go-memdb.UUIDFieldIndex Field=AccessorID + index=authmethod allow-missing + indexer=github.com/hashicorp/go-memdb.StringFieldIndex Field=AuthMethod Lowercase=false + index=expires-global allow-missing + indexer=github.com/hashicorp/consul/agent/consul/state.TokenExpirationIndex LocalFilter=false + index=expires-local allow-missing + indexer=github.com/hashicorp/consul/agent/consul/state.TokenExpirationIndex LocalFilter=true + index=id unique + indexer=github.com/hashicorp/go-memdb.StringFieldIndex Field=SecretID Lowercase=false + index=local + indexer=github.com/hashicorp/go-memdb.ConditionalIndex Conditional=github.com/hashicorp/consul/agent/consul/state.tokensTableSchema.func1 + index=needs-upgrade + indexer=github.com/hashicorp/go-memdb.ConditionalIndex Conditional=github.com/hashicorp/consul/agent/consul/state.tokensTableSchema.func2 + index=policies allow-missing + indexer=github.com/hashicorp/consul/agent/consul/state.TokenPoliciesIndex + index=roles allow-missing + indexer=github.com/hashicorp/consul/agent/consul/state.TokenRolesIndex + +table=autopilot-config + index=id unique allow-missing + indexer=github.com/hashicorp/go-memdb.ConditionalIndex Conditional=github.com/hashicorp/consul/agent/consul/state.autopilotConfigTableSchema.func1 + +table=checks + index=id unique + indexer=github.com/hashicorp/go-memdb.CompoundIndex Indexes=[github.com/hashicorp/go-memdb.StringFieldIndex Field=Node Lowercase=true, github.com/hashicorp/go-memdb.StringFieldIndex Field=CheckID Lowercase=true] AllowMissing=false + index=node allow-missing + indexer=github.com/hashicorp/go-memdb.StringFieldIndex Field=Node Lowercase=true + index=node_service allow-missing + indexer=github.com/hashicorp/go-memdb.CompoundIndex Indexes=[github.com/hashicorp/go-memdb.StringFieldIndex Field=Node Lowercase=true, github.com/hashicorp/go-memdb.StringFieldIndex Field=ServiceID Lowercase=true] AllowMissing=false + index=node_service_check allow-missing + indexer=github.com/hashicorp/go-memdb.CompoundIndex Indexes=[github.com/hashicorp/go-memdb.StringFieldIndex Field=Node Lowercase=true, github.com/hashicorp/go-memdb.FieldSetIndex Field=ServiceID] AllowMissing=false + index=service allow-missing + indexer=github.com/hashicorp/go-memdb.StringFieldIndex Field=ServiceName Lowercase=true + index=status + indexer=github.com/hashicorp/go-memdb.StringFieldIndex Field=Status Lowercase=false + +table=config-entries + index=id unique + indexer=github.com/hashicorp/go-memdb.CompoundIndex Indexes=[github.com/hashicorp/go-memdb.StringFieldIndex Field=Kind Lowercase=true, github.com/hashicorp/go-memdb.StringFieldIndex Field=Name Lowercase=true] AllowMissing=false + index=intention-legacy-id unique allow-missing + indexer=github.com/hashicorp/consul/agent/consul/state.ServiceIntentionLegacyIDIndex uuidFieldIndex={} + index=intention-source allow-missing + indexer=github.com/hashicorp/consul/agent/consul/state.ServiceIntentionSourceIndex + index=kind + indexer=github.com/hashicorp/go-memdb.StringFieldIndex Field=Kind Lowercase=true + index=link allow-missing + indexer=github.com/hashicorp/consul/agent/consul/state.ConfigEntryLinkIndex + +table=connect-ca-builtin + index=id unique + indexer=github.com/hashicorp/go-memdb.StringFieldIndex Field=ID Lowercase=false + +table=connect-ca-config + index=id unique allow-missing + indexer=github.com/hashicorp/go-memdb.ConditionalIndex Conditional=github.com/hashicorp/consul/agent/consul/state.caConfigTableSchema.func1 + +table=connect-ca-roots + index=id unique + indexer=github.com/hashicorp/go-memdb.StringFieldIndex Field=ID Lowercase=false + +table=connect-intentions + index=destination allow-missing + indexer=github.com/hashicorp/go-memdb.CompoundIndex Indexes=[github.com/hashicorp/go-memdb.StringFieldIndex Field=DestinationNS Lowercase=true, github.com/hashicorp/go-memdb.StringFieldIndex Field=DestinationName Lowercase=true] AllowMissing=false + index=id unique + indexer=github.com/hashicorp/go-memdb.UUIDFieldIndex Field=ID + index=source allow-missing + indexer=github.com/hashicorp/go-memdb.CompoundIndex Indexes=[github.com/hashicorp/go-memdb.StringFieldIndex Field=SourceNS Lowercase=true, github.com/hashicorp/go-memdb.StringFieldIndex Field=SourceName Lowercase=true] AllowMissing=false + index=source_destination unique allow-missing + indexer=github.com/hashicorp/go-memdb.CompoundIndex Indexes=[github.com/hashicorp/go-memdb.StringFieldIndex Field=SourceNS Lowercase=true, github.com/hashicorp/go-memdb.StringFieldIndex Field=SourceName Lowercase=true, github.com/hashicorp/go-memdb.StringFieldIndex Field=DestinationNS Lowercase=true, github.com/hashicorp/go-memdb.StringFieldIndex Field=DestinationName Lowercase=true] AllowMissing=false + +table=coordinates + index=id unique + indexer=github.com/hashicorp/go-memdb.CompoundIndex Indexes=[github.com/hashicorp/go-memdb.StringFieldIndex Field=Node Lowercase=true, github.com/hashicorp/go-memdb.StringFieldIndex Field=Segment Lowercase=true] AllowMissing=true + index=node + indexer=github.com/hashicorp/go-memdb.StringFieldIndex Field=Node Lowercase=true + +table=federation-states + index=id unique + indexer=github.com/hashicorp/go-memdb.StringFieldIndex Field=Datacenter Lowercase=true + +table=gateway-services + index=gateway + indexer=github.com/hashicorp/consul/agent/consul/state.ServiceNameIndex Field=Gateway + index=id unique + indexer=github.com/hashicorp/go-memdb.CompoundIndex Indexes=[github.com/hashicorp/consul/agent/consul/state.ServiceNameIndex Field=Gateway, github.com/hashicorp/consul/agent/consul/state.ServiceNameIndex Field=Service, github.com/hashicorp/go-memdb.IntFieldIndex Field=Port] AllowMissing=false + index=service allow-missing + indexer=github.com/hashicorp/consul/agent/consul/state.ServiceNameIndex Field=Service + +table=index + index=id unique + indexer=github.com/hashicorp/go-memdb.StringFieldIndex Field=Key Lowercase=true + +table=kvs + index=id unique + indexer=github.com/hashicorp/go-memdb.StringFieldIndex Field=Key Lowercase=false + index=session allow-missing + indexer=github.com/hashicorp/go-memdb.UUIDFieldIndex Field=Session + +table=mesh-topology + index=downstream + indexer=github.com/hashicorp/consul/agent/consul/state.ServiceNameIndex Field=Downstream + index=id unique + indexer=github.com/hashicorp/go-memdb.CompoundIndex Indexes=[github.com/hashicorp/consul/agent/consul/state.ServiceNameIndex Field=Upstream, github.com/hashicorp/consul/agent/consul/state.ServiceNameIndex Field=Downstream] AllowMissing=false + index=upstream allow-missing + indexer=github.com/hashicorp/consul/agent/consul/state.ServiceNameIndex Field=Upstream + +table=nodes + index=id unique + indexer=github.com/hashicorp/go-memdb.StringFieldIndex Field=Node Lowercase=true + index=meta allow-missing + indexer=github.com/hashicorp/go-memdb.StringMapFieldIndex Field=Meta Lowercase=false + index=uuid unique allow-missing + indexer=github.com/hashicorp/go-memdb.UUIDFieldIndex Field=ID + +table=prepared-queries + index=id unique + indexer=github.com/hashicorp/go-memdb.UUIDFieldIndex Field=ID + index=name unique allow-missing + indexer=github.com/hashicorp/go-memdb.StringFieldIndex Field=Name Lowercase=true + index=session allow-missing + indexer=github.com/hashicorp/go-memdb.UUIDFieldIndex Field=Session + index=template unique allow-missing + indexer=github.com/hashicorp/consul/agent/consul/state.PreparedQueryIndex + +table=services + index=connect allow-missing + indexer=github.com/hashicorp/consul/agent/consul/state.IndexConnectService + index=id unique + indexer=github.com/hashicorp/go-memdb.CompoundIndex Indexes=[github.com/hashicorp/go-memdb.StringFieldIndex Field=Node Lowercase=true, github.com/hashicorp/go-memdb.StringFieldIndex Field=ServiceID Lowercase=true] AllowMissing=false + index=kind + indexer=github.com/hashicorp/consul/agent/consul/state.IndexServiceKind + index=node + indexer=github.com/hashicorp/go-memdb.StringFieldIndex Field=Node Lowercase=true + index=service allow-missing + indexer=github.com/hashicorp/go-memdb.StringFieldIndex Field=ServiceName Lowercase=true + +table=session_checks + index=id unique + indexer=github.com/hashicorp/go-memdb.CompoundIndex Indexes=[github.com/hashicorp/go-memdb.StringFieldIndex Field=Node Lowercase=true, github.com/hashicorp/consul/agent/consul/state.CheckIDIndex, github.com/hashicorp/go-memdb.UUIDFieldIndex Field=Session] AllowMissing=false + index=node_check + indexer=github.com/hashicorp/go-memdb.CompoundIndex Indexes=[github.com/hashicorp/go-memdb.StringFieldIndex Field=Node Lowercase=true, github.com/hashicorp/consul/agent/consul/state.CheckIDIndex] AllowMissing=false + index=session + indexer=github.com/hashicorp/go-memdb.UUIDFieldIndex Field=Session + +table=sessions + index=id unique + indexer=github.com/hashicorp/go-memdb.UUIDFieldIndex Field=ID + index=node + indexer=github.com/hashicorp/go-memdb.StringFieldIndex Field=Node Lowercase=true + +table=system-metadata + index=id unique + indexer=github.com/hashicorp/go-memdb.StringFieldIndex Field=Key Lowercase=true + +table=tombstones + index=id unique + indexer=github.com/hashicorp/go-memdb.StringFieldIndex Field=Key Lowercase=false + +table=usage + index=id unique + indexer=github.com/hashicorp/go-memdb.StringFieldIndex Field=ID Lowercase=true + diff --git a/internal/testing/golden/golden.go b/internal/testing/golden/golden.go new file mode 100644 index 0000000000..66ec2a0ffa --- /dev/null +++ b/internal/testing/golden/golden.go @@ -0,0 +1,36 @@ +package golden + +import ( + "flag" + "io/ioutil" + "os" + "path/filepath" + "testing" + + "github.com/stretchr/testify/require" +) + +// update allows golden files to be updated based on the current output. +var update = flag.Bool("update", false, "update golden files") + +// Get reads the expected value from the file at filename and returns the value. +// filename is relative to the ./testdata directory. +// +// If the `-update` flag is used with `go test`, the golden file will be updated +// to the value of actual. +func Get(t *testing.T, actual, filename string) string { + t.Helper() + + path := filepath.Join("testdata", filename) + if *update { + if dir := filepath.Dir(path); dir != "." { + require.NoError(t, os.MkdirAll(dir, 0755)) + } + err := ioutil.WriteFile(path, []byte(actual), 0644) + require.NoError(t, err) + } + + expected, err := ioutil.ReadFile(path) + require.NoError(t, err) + return string(expected) +}