state: add a regression test for state store schema

To allow the index to be refactored without accidental changes.

To update the expected value run: 'go test ./agent/consul/state -update'
This commit is contained in:
Daniel Nephin 2021-01-15 18:28:32 -05:00
parent aa21c1ea04
commit 52a1d78e39
4 changed files with 317 additions and 5 deletions

View File

@ -0,0 +1,5 @@
// +build !consulent
package state
var stateStoreSchemaExpected = "TestStateStoreSchema.golden"

View File

@ -1,17 +1,100 @@
package state package state
import ( import (
"bytes"
"fmt"
"reflect"
"runtime"
"sort"
"testing" "testing"
"github.com/hashicorp/go-memdb" "github.com/hashicorp/go-memdb"
"github.com/stretchr/testify/require"
"github.com/hashicorp/consul/internal/testing/golden"
) )
func TestStateStore_Schema(t *testing.T) { func TestStateStoreSchema(t *testing.T) {
// First call the schema creation
schema := stateStoreSchema() schema := stateStoreSchema()
require.NoError(t, schema.Validate())
// Try to initialize a new memdb using the schema _, err := memdb.NewMemDB(schema)
if _, err := memdb.NewMemDB(schema); err != nil { require.NoError(t, err)
t.Fatalf("err: %s", 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
}

View File

@ -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

View File

@ -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)
}