221 lines
6.4 KiB
Go
221 lines
6.4 KiB
Go
package state
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/binary"
|
|
"errors"
|
|
"fmt"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/hashicorp/consul/acl"
|
|
"github.com/hashicorp/consul/agent/structs"
|
|
)
|
|
|
|
// 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 {
|
|
// readIndex is used by memdb for Txn.Get, Txn.First, and other operations
|
|
// that read data.
|
|
readIndex
|
|
// writeIndex is used by memdb for Txn.Insert, Txn.Delete, for operations
|
|
// that write data to the index.
|
|
writeIndex
|
|
}
|
|
|
|
// 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 {
|
|
// readIndex is used by memdb for Txn.Get, Txn.First, and other operations
|
|
// that read data.
|
|
readIndex
|
|
// writeIndexMulti is used by memdb for Txn.Insert, Txn.Delete, for operations
|
|
// that write data to the index.
|
|
writeIndexMulti
|
|
}
|
|
|
|
// indexerSingleWithPrefix is a indexerSingle which also supports prefix queries.
|
|
type indexerSingleWithPrefix struct {
|
|
readIndex
|
|
writeIndex
|
|
prefixIndex
|
|
}
|
|
|
|
// readIndex implements memdb.Indexer. It exists so that a function can be used
|
|
// to provide the interface.
|
|
//
|
|
// 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)
|
|
|
|
func (f readIndex) FromArgs(args ...interface{}) ([]byte, error) {
|
|
if len(args) != 1 {
|
|
return nil, fmt.Errorf("index supports only a single arg")
|
|
}
|
|
return f(args[0])
|
|
}
|
|
|
|
var errMissingValueForIndex = fmt.Errorf("object is missing a value for this index")
|
|
|
|
// writeIndex implements memdb.SingleIndexer. It exists so that a function
|
|
// can be used to provide this interface.
|
|
//
|
|
// 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)
|
|
|
|
func (f writeIndex) FromObject(raw interface{}) (bool, []byte, error) {
|
|
v, err := f(raw)
|
|
if errors.Is(err, errMissingValueForIndex) {
|
|
return false, nil, nil
|
|
}
|
|
return err == nil, v, err
|
|
}
|
|
|
|
// writeIndexMulti implements memdb.MultiIndexer. It exists so that a function
|
|
// can be used to provide this interface.
|
|
//
|
|
// 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)
|
|
|
|
func (f writeIndexMulti) FromObject(raw interface{}) (bool, [][]byte, error) {
|
|
v, err := f(raw)
|
|
if errors.Is(err, errMissingValueForIndex) {
|
|
return false, nil, nil
|
|
}
|
|
return err == nil, v, err
|
|
}
|
|
|
|
// prefixIndex implements memdb.PrefixIndexer. It exists so that a function
|
|
// can be used to provide this interface.
|
|
type prefixIndex func(args interface{}) ([]byte, error)
|
|
|
|
func (f prefixIndex) PrefixFromArgs(args ...interface{}) ([]byte, error) {
|
|
if len(args) != 1 {
|
|
return nil, fmt.Errorf("index supports only a single arg")
|
|
}
|
|
return f(args[0])
|
|
}
|
|
|
|
const null = "\x00"
|
|
|
|
// indexBuilder is a buffer used to construct memdb index values.
|
|
type indexBuilder bytes.Buffer
|
|
|
|
func newIndexBuilder(cap int) *indexBuilder {
|
|
buff := make([]byte, 0, cap)
|
|
b := bytes.NewBuffer(buff)
|
|
return (*indexBuilder)(b)
|
|
}
|
|
|
|
// String appends the string and a null terminator to the buffer.
|
|
func (b *indexBuilder) String(v string) {
|
|
(*bytes.Buffer)(b).WriteString(v)
|
|
(*bytes.Buffer)(b).WriteString(null)
|
|
}
|
|
|
|
func (b *indexBuilder) Int64(v int64) {
|
|
const size = binary.MaxVarintLen64
|
|
|
|
// Get the value and encode it
|
|
buf := make([]byte, size)
|
|
binary.PutVarint(buf, v)
|
|
b.Raw(buf)
|
|
}
|
|
|
|
// Raw appends the bytes without a null terminator to the buffer. Raw should
|
|
// only be used when v has a fixed length, or when building the last segment of
|
|
// a prefix index.
|
|
func (b *indexBuilder) Raw(v []byte) {
|
|
(*bytes.Buffer)(b).Write(v)
|
|
}
|
|
|
|
func (b *indexBuilder) Bytes() []byte {
|
|
return (*bytes.Buffer)(b).Bytes()
|
|
}
|
|
|
|
// singleValueID is an interface that may be implemented by any type that should
|
|
// be indexed by a single ID and a structs.EnterpriseMeta to scope the ID.
|
|
type singleValueID interface {
|
|
IDValue() string
|
|
PartitionOrDefault() string
|
|
NamespaceOrDefault() string
|
|
}
|
|
|
|
type multiValueID interface {
|
|
IDValue() []string
|
|
PartitionOrDefault() string
|
|
NamespaceOrDefault() string
|
|
}
|
|
|
|
var _ singleValueID = (*structs.DirEntry)(nil)
|
|
var _ singleValueID = (*Tombstone)(nil)
|
|
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)
|
|
}
|
|
|
|
v := strings.ToLower(e.IDValue())
|
|
if v == "" {
|
|
return nil, errMissingValueForIndex
|
|
}
|
|
|
|
var b indexBuilder
|
|
b.String(v)
|
|
return b.Bytes(), nil
|
|
}
|
|
|
|
// 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)
|
|
}
|
|
var b indexBuilder
|
|
for _, v := range e.IDValue() {
|
|
if v == "" {
|
|
return nil, errMissingValueForIndex
|
|
}
|
|
b.String(strings.ToLower(v))
|
|
}
|
|
return b.Bytes(), nil
|
|
}
|
|
|
|
func (b *indexBuilder) Bool(v bool) {
|
|
b.Raw([]byte{intFromBool(v)})
|
|
}
|
|
|
|
type TimeQuery struct {
|
|
Value time.Time
|
|
acl.EnterpriseMeta
|
|
}
|
|
|
|
// NamespaceOrDefault exists because structs.EnterpriseMeta uses a pointer
|
|
// receiver for this method. Remove once that is fixed.
|
|
func (q TimeQuery) NamespaceOrDefault() string {
|
|
return q.EnterpriseMeta.NamespaceOrDefault()
|
|
}
|
|
|
|
// PartitionOrDefault exists because structs.EnterpriseMeta uses a pointer
|
|
// receiver for this method. Remove once that is fixed.
|
|
func (q TimeQuery) PartitionOrDefault() string {
|
|
return q.EnterpriseMeta.PartitionOrDefault()
|
|
}
|
|
|
|
func (b *indexBuilder) Time(t time.Time) {
|
|
val := t.Unix()
|
|
buf := make([]byte, 8)
|
|
binary.BigEndian.PutUint64(buf, uint64(val))
|
|
(*bytes.Buffer)(b).Write(buf)
|
|
}
|