1641 lines
40 KiB
Go
1641 lines
40 KiB
Go
// Copyright (c) HashiCorp, Inc.
|
|
// SPDX-License-Identifier: MPL-2.0
|
|
|
|
package state
|
|
|
|
import (
|
|
"fmt"
|
|
"sync"
|
|
|
|
"github.com/hashicorp/go-memdb"
|
|
"github.com/hashicorp/nomad/nomad/state/indexer"
|
|
"github.com/hashicorp/nomad/nomad/structs"
|
|
)
|
|
|
|
const (
|
|
tableIndex = "index"
|
|
|
|
TableNamespaces = "namespaces"
|
|
TableNodePools = "node_pools"
|
|
TableServiceRegistrations = "service_registrations"
|
|
TableVariables = "variables"
|
|
TableVariablesQuotas = "variables_quota"
|
|
TableRootKeyMeta = "root_key_meta"
|
|
TableACLRoles = "acl_roles"
|
|
TableACLAuthMethods = "acl_auth_methods"
|
|
TableACLBindingRules = "acl_binding_rules"
|
|
TableAllocs = "allocs"
|
|
)
|
|
|
|
const (
|
|
indexID = "id"
|
|
indexJob = "job"
|
|
indexNodeID = "node_id"
|
|
indexAllocID = "alloc_id"
|
|
indexServiceName = "service_name"
|
|
indexExpiresGlobal = "expires-global"
|
|
indexExpiresLocal = "expires-local"
|
|
indexKeyID = "key_id"
|
|
indexPath = "path"
|
|
indexName = "name"
|
|
indexSigningKey = "signing_key"
|
|
indexAuthMethod = "auth_method"
|
|
)
|
|
|
|
var (
|
|
schemaFactories SchemaFactories
|
|
factoriesLock sync.Mutex
|
|
)
|
|
|
|
// SchemaFactory is the factory method for returning a TableSchema
|
|
type SchemaFactory func() *memdb.TableSchema
|
|
type SchemaFactories []SchemaFactory
|
|
|
|
// RegisterSchemaFactories is used to register a table schema.
|
|
func RegisterSchemaFactories(factories ...SchemaFactory) {
|
|
factoriesLock.Lock()
|
|
defer factoriesLock.Unlock()
|
|
schemaFactories = append(schemaFactories, factories...)
|
|
}
|
|
|
|
func GetFactories() SchemaFactories {
|
|
return schemaFactories
|
|
}
|
|
|
|
func init() {
|
|
// Register all schemas
|
|
RegisterSchemaFactories([]SchemaFactory{
|
|
indexTableSchema,
|
|
nodeTableSchema,
|
|
nodePoolTableSchema,
|
|
jobTableSchema,
|
|
jobSummarySchema,
|
|
jobVersionSchema,
|
|
jobSubmissionSchema,
|
|
deploymentSchema,
|
|
periodicLaunchTableSchema,
|
|
evalTableSchema,
|
|
allocTableSchema,
|
|
vaultAccessorTableSchema,
|
|
siTokenAccessorTableSchema,
|
|
aclPolicyTableSchema,
|
|
aclTokenTableSchema,
|
|
oneTimeTokenTableSchema,
|
|
autopilotConfigTableSchema,
|
|
schedulerConfigTableSchema,
|
|
clusterMetaTableSchema,
|
|
csiVolumeTableSchema,
|
|
csiPluginTableSchema,
|
|
scalingPolicyTableSchema,
|
|
scalingEventTableSchema,
|
|
namespaceTableSchema,
|
|
serviceRegistrationsTableSchema,
|
|
variablesTableSchema,
|
|
variablesQuotasTableSchema,
|
|
variablesRootKeyMetaSchema,
|
|
aclRolesTableSchema,
|
|
aclAuthMethodsTableSchema,
|
|
bindingRulesTableSchema,
|
|
}...)
|
|
}
|
|
|
|
// stateStoreSchema is used to return the schema for the state store
|
|
func stateStoreSchema() *memdb.DBSchema {
|
|
// Create the root DB schema
|
|
db := &memdb.DBSchema{
|
|
Tables: make(map[string]*memdb.TableSchema),
|
|
}
|
|
|
|
// Add each of the tables
|
|
for _, schemaFn := range GetFactories() {
|
|
schema := schemaFn()
|
|
if _, ok := db.Tables[schema.Name]; ok {
|
|
panic(fmt.Sprintf("duplicate table name: %s", schema.Name))
|
|
}
|
|
db.Tables[schema.Name] = schema
|
|
}
|
|
return db
|
|
}
|
|
|
|
// indexTableSchema is used for tracking the most recent index used for each table.
|
|
func indexTableSchema() *memdb.TableSchema {
|
|
return &memdb.TableSchema{
|
|
Name: "index",
|
|
Indexes: map[string]*memdb.IndexSchema{
|
|
"id": {
|
|
Name: "id",
|
|
AllowMissing: false,
|
|
Unique: true,
|
|
Indexer: &memdb.StringFieldIndex{
|
|
Field: "Key",
|
|
Lowercase: true,
|
|
},
|
|
},
|
|
},
|
|
}
|
|
}
|
|
|
|
// nodeTableSchema returns the MemDB schema for the nodes table.
|
|
// This table is used to store all the client nodes that are registered.
|
|
func nodeTableSchema() *memdb.TableSchema {
|
|
return &memdb.TableSchema{
|
|
Name: "nodes",
|
|
Indexes: map[string]*memdb.IndexSchema{
|
|
// Primary index is used for node management
|
|
// and simple direct lookup. ID is required to be
|
|
// unique.
|
|
"id": {
|
|
Name: "id",
|
|
AllowMissing: false,
|
|
Unique: true,
|
|
Indexer: &memdb.UUIDFieldIndex{
|
|
Field: "ID",
|
|
},
|
|
},
|
|
"secret_id": {
|
|
Name: "secret_id",
|
|
AllowMissing: false,
|
|
Unique: true,
|
|
Indexer: &memdb.UUIDFieldIndex{
|
|
Field: "SecretID",
|
|
},
|
|
},
|
|
"node_pool": {
|
|
Name: "node_pool",
|
|
AllowMissing: false,
|
|
Unique: false,
|
|
Indexer: &memdb.StringFieldIndex{
|
|
Field: "NodePool",
|
|
},
|
|
},
|
|
},
|
|
}
|
|
}
|
|
|
|
// nodePoolTableSchema returns the MemDB schema for the node pools table.
|
|
// This table is used to store all the node pools registered in the cluster.
|
|
func nodePoolTableSchema() *memdb.TableSchema {
|
|
return &memdb.TableSchema{
|
|
Name: TableNodePools,
|
|
Indexes: map[string]*memdb.IndexSchema{
|
|
// Name is the primary index used for lookup and is required to be
|
|
// unique.
|
|
"id": {
|
|
Name: "id",
|
|
AllowMissing: false,
|
|
Unique: true,
|
|
Indexer: &memdb.StringFieldIndex{
|
|
Field: "Name",
|
|
},
|
|
},
|
|
},
|
|
}
|
|
}
|
|
|
|
// jobTableSchema returns the MemDB schema for the jobs table.
|
|
// This table is used to store all the jobs that have been submitted.
|
|
func jobTableSchema() *memdb.TableSchema {
|
|
return &memdb.TableSchema{
|
|
Name: "jobs",
|
|
Indexes: map[string]*memdb.IndexSchema{
|
|
// Primary index is used for job management
|
|
// and simple direct lookup. ID is required to be
|
|
// unique within a namespace.
|
|
"id": {
|
|
Name: "id",
|
|
AllowMissing: false,
|
|
Unique: true,
|
|
|
|
// Use a compound index so the tuple of (Namespace, ID) is
|
|
// uniquely identifying
|
|
Indexer: &memdb.CompoundIndex{
|
|
Indexes: []memdb.Indexer{
|
|
&memdb.StringFieldIndex{
|
|
Field: "Namespace",
|
|
},
|
|
|
|
&memdb.StringFieldIndex{
|
|
Field: "ID",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
"type": {
|
|
Name: "type",
|
|
AllowMissing: false,
|
|
Unique: false,
|
|
Indexer: &memdb.StringFieldIndex{
|
|
Field: "Type",
|
|
Lowercase: false,
|
|
},
|
|
},
|
|
"gc": {
|
|
Name: "gc",
|
|
AllowMissing: false,
|
|
Unique: false,
|
|
Indexer: &memdb.ConditionalIndex{
|
|
Conditional: jobIsGCable,
|
|
},
|
|
},
|
|
"periodic": {
|
|
Name: "periodic",
|
|
AllowMissing: false,
|
|
Unique: false,
|
|
Indexer: &memdb.ConditionalIndex{
|
|
Conditional: jobIsPeriodic,
|
|
},
|
|
},
|
|
"pool": {
|
|
Name: "pool",
|
|
AllowMissing: false,
|
|
Unique: false,
|
|
Indexer: &memdb.StringFieldIndex{
|
|
Field: "NodePool",
|
|
},
|
|
},
|
|
},
|
|
}
|
|
}
|
|
|
|
// jobSummarySchema returns the memdb schema for the job summary table
|
|
func jobSummarySchema() *memdb.TableSchema {
|
|
return &memdb.TableSchema{
|
|
Name: "job_summary",
|
|
Indexes: map[string]*memdb.IndexSchema{
|
|
"id": {
|
|
Name: "id",
|
|
AllowMissing: false,
|
|
Unique: true,
|
|
|
|
// Use a compound index so the tuple of (Namespace, JobID) is
|
|
// uniquely identifying
|
|
Indexer: &memdb.CompoundIndex{
|
|
Indexes: []memdb.Indexer{
|
|
&memdb.StringFieldIndex{
|
|
Field: "Namespace",
|
|
},
|
|
|
|
&memdb.StringFieldIndex{
|
|
Field: "JobID",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
}
|
|
|
|
// jobVersionSchema returns the memdb schema for the job version table which
|
|
// keeps a historical view of job versions.
|
|
func jobVersionSchema() *memdb.TableSchema {
|
|
return &memdb.TableSchema{
|
|
Name: "job_version",
|
|
Indexes: map[string]*memdb.IndexSchema{
|
|
"id": {
|
|
Name: "id",
|
|
AllowMissing: false,
|
|
Unique: true,
|
|
|
|
// Use a compound index so the tuple of (Namespace, ID, Version) is
|
|
// uniquely identifying
|
|
Indexer: &memdb.CompoundIndex{
|
|
Indexes: []memdb.Indexer{
|
|
&memdb.StringFieldIndex{
|
|
Field: "Namespace",
|
|
},
|
|
|
|
&memdb.StringFieldIndex{
|
|
Field: "ID",
|
|
Lowercase: true,
|
|
},
|
|
|
|
&memdb.UintFieldIndex{
|
|
Field: "Version",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
}
|
|
|
|
// jobSubmissionSchema returns the memdb table schema of job submissions
|
|
// which contain the original source material of each job, per version.
|
|
func jobSubmissionSchema() *memdb.TableSchema {
|
|
return &memdb.TableSchema{
|
|
Name: "job_submission",
|
|
Indexes: map[string]*memdb.IndexSchema{
|
|
"id": {
|
|
Name: "id",
|
|
AllowMissing: false,
|
|
Unique: true,
|
|
// index by (Namespace, JobID, Version)
|
|
// note: uniqueness applies only at the moment of insertion,
|
|
// if anything modifies one of these fields (as the stored
|
|
// struct is a pointer, there is no consistency)
|
|
Indexer: &memdb.CompoundIndex{
|
|
Indexes: []memdb.Indexer{
|
|
&memdb.StringFieldIndex{
|
|
Field: "Namespace",
|
|
},
|
|
|
|
&memdb.StringFieldIndex{
|
|
Field: "JobID",
|
|
Lowercase: true,
|
|
},
|
|
|
|
&memdb.UintFieldIndex{
|
|
Field: "Version",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
}
|
|
|
|
// jobIsGCable satisfies the ConditionalIndexFunc interface and creates an index
|
|
// on whether a job is eligible for garbage collection.
|
|
func jobIsGCable(obj interface{}) (bool, error) {
|
|
j, ok := obj.(*structs.Job)
|
|
if !ok {
|
|
return false, fmt.Errorf("Unexpected type: %v", obj)
|
|
}
|
|
|
|
// If the job is periodic or parameterized it is only garbage collectable if
|
|
// it is stopped.
|
|
periodic := j.Periodic != nil && j.Periodic.Enabled
|
|
parameterized := j.IsParameterized()
|
|
if periodic || parameterized {
|
|
return j.Stop, nil
|
|
}
|
|
|
|
// If the job isn't dead it isn't eligible
|
|
if j.Status != structs.JobStatusDead {
|
|
return false, nil
|
|
}
|
|
|
|
// Any job that is stopped is eligible for garbage collection
|
|
if j.Stop {
|
|
return true, nil
|
|
}
|
|
|
|
switch j.Type {
|
|
// Otherwise, batch and sysbatch jobs are eligible because they complete on
|
|
// their own without a user stopping them.
|
|
case structs.JobTypeBatch, structs.JobTypeSysBatch:
|
|
return true, nil
|
|
|
|
default:
|
|
// other job types may not be GC until stopped
|
|
return false, nil
|
|
}
|
|
}
|
|
|
|
// jobIsPeriodic satisfies the ConditionalIndexFunc interface and creates an index
|
|
// on whether a job is periodic.
|
|
func jobIsPeriodic(obj interface{}) (bool, error) {
|
|
j, ok := obj.(*structs.Job)
|
|
if !ok {
|
|
return false, fmt.Errorf("Unexpected type: %v", obj)
|
|
}
|
|
|
|
if j.Periodic != nil && j.Periodic.Enabled {
|
|
return true, nil
|
|
}
|
|
|
|
return false, nil
|
|
}
|
|
|
|
// deploymentSchema returns the MemDB schema tracking a job's deployments
|
|
func deploymentSchema() *memdb.TableSchema {
|
|
return &memdb.TableSchema{
|
|
Name: "deployment",
|
|
Indexes: map[string]*memdb.IndexSchema{
|
|
// id index is used for direct lookup of an deployment by ID.
|
|
"id": {
|
|
Name: "id",
|
|
AllowMissing: false,
|
|
Unique: true,
|
|
Indexer: &memdb.UUIDFieldIndex{
|
|
Field: "ID",
|
|
},
|
|
},
|
|
|
|
// create index is used for listing deploy, ordering them by
|
|
// creation chronology. (Use a reverse iterator for newest first).
|
|
//
|
|
// There may be more than one deployment per CreateIndex.
|
|
"create": {
|
|
Name: "create",
|
|
AllowMissing: false,
|
|
Unique: true,
|
|
Indexer: &memdb.CompoundIndex{
|
|
Indexes: []memdb.Indexer{
|
|
&memdb.UintFieldIndex{
|
|
Field: "CreateIndex",
|
|
},
|
|
&memdb.StringFieldIndex{
|
|
Field: "ID",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
|
|
// namespace is used to lookup evaluations by namespace.
|
|
"namespace": {
|
|
Name: "namespace",
|
|
AllowMissing: false,
|
|
Unique: false,
|
|
Indexer: &memdb.StringFieldIndex{
|
|
Field: "Namespace",
|
|
},
|
|
},
|
|
|
|
// namespace_create index is used to lookup deployments by namespace
|
|
// in their original chronological order based on CreateIndex.
|
|
//
|
|
// Use a prefix iterator (namespace_create_prefix) to iterate deployments
|
|
// of a Namespace in order of CreateIndex.
|
|
//
|
|
// There may be more than one deployment per CreateIndex.
|
|
"namespace_create": {
|
|
Name: "namespace_create",
|
|
AllowMissing: false,
|
|
Unique: true,
|
|
Indexer: &memdb.CompoundIndex{
|
|
AllowMissing: false,
|
|
Indexes: []memdb.Indexer{
|
|
&memdb.StringFieldIndex{
|
|
Field: "Namespace",
|
|
},
|
|
&memdb.UintFieldIndex{
|
|
Field: "CreateIndex",
|
|
},
|
|
&memdb.StringFieldIndex{
|
|
Field: "ID",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
|
|
// job index is used to lookup deployments by job
|
|
"job": {
|
|
Name: "job",
|
|
AllowMissing: false,
|
|
Unique: false,
|
|
|
|
// Use a compound index so the tuple of (Namespace, JobID) is
|
|
// uniquely identifying
|
|
Indexer: &memdb.CompoundIndex{
|
|
Indexes: []memdb.Indexer{
|
|
&memdb.StringFieldIndex{
|
|
Field: "Namespace",
|
|
},
|
|
|
|
&memdb.StringFieldIndex{
|
|
Field: "JobID",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
}
|
|
|
|
// periodicLaunchTableSchema returns the MemDB schema tracking the most recent
|
|
// launch time for a periodic job.
|
|
func periodicLaunchTableSchema() *memdb.TableSchema {
|
|
return &memdb.TableSchema{
|
|
Name: "periodic_launch",
|
|
Indexes: map[string]*memdb.IndexSchema{
|
|
// Primary index is used for job management
|
|
// and simple direct lookup. ID is required to be
|
|
// unique.
|
|
"id": {
|
|
Name: "id",
|
|
AllowMissing: false,
|
|
Unique: true,
|
|
|
|
// Use a compound index so the tuple of (Namespace, JobID) is
|
|
// uniquely identifying
|
|
Indexer: &memdb.CompoundIndex{
|
|
Indexes: []memdb.Indexer{
|
|
&memdb.StringFieldIndex{
|
|
Field: "Namespace",
|
|
},
|
|
|
|
&memdb.StringFieldIndex{
|
|
Field: "ID",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
}
|
|
|
|
// evalTableSchema returns the MemDB schema for the eval table.
|
|
// This table is used to store all the evaluations that are pending
|
|
// or recently completed.
|
|
func evalTableSchema() *memdb.TableSchema {
|
|
return &memdb.TableSchema{
|
|
Name: "evals",
|
|
Indexes: map[string]*memdb.IndexSchema{
|
|
// id index is used for direct lookup of an evaluation by ID.
|
|
"id": {
|
|
Name: "id",
|
|
AllowMissing: false,
|
|
Unique: true,
|
|
Indexer: &memdb.UUIDFieldIndex{
|
|
Field: "ID",
|
|
},
|
|
},
|
|
|
|
// create index is used for listing evaluations, ordering them by
|
|
// creation chronology. (Use a reverse iterator for newest first).
|
|
"create": {
|
|
Name: "create",
|
|
AllowMissing: false,
|
|
Unique: true,
|
|
Indexer: &memdb.CompoundIndex{
|
|
Indexes: []memdb.Indexer{
|
|
&memdb.UintFieldIndex{
|
|
Field: "CreateIndex",
|
|
},
|
|
&memdb.StringFieldIndex{
|
|
Field: "ID",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
|
|
// job index is used to lookup evaluations by job ID.
|
|
"job": {
|
|
Name: "job",
|
|
AllowMissing: false,
|
|
Unique: false,
|
|
Indexer: &memdb.CompoundIndex{
|
|
Indexes: []memdb.Indexer{
|
|
&memdb.StringFieldIndex{
|
|
Field: "Namespace",
|
|
},
|
|
|
|
&memdb.StringFieldIndex{
|
|
Field: "JobID",
|
|
Lowercase: true,
|
|
},
|
|
|
|
&memdb.StringFieldIndex{
|
|
Field: "Status",
|
|
Lowercase: true,
|
|
},
|
|
},
|
|
},
|
|
},
|
|
|
|
// namespace is used to lookup evaluations by namespace.
|
|
"namespace": {
|
|
Name: "namespace",
|
|
AllowMissing: false,
|
|
Unique: false,
|
|
Indexer: &memdb.StringFieldIndex{
|
|
Field: "Namespace",
|
|
},
|
|
},
|
|
|
|
// namespace_create index is used to lookup evaluations by namespace
|
|
// in their original chronological order based on CreateIndex.
|
|
//
|
|
// Use a prefix iterator (namespace_prefix) on a Namespace to iterate
|
|
// those evaluations in order of CreateIndex.
|
|
"namespace_create": {
|
|
Name: "namespace_create",
|
|
AllowMissing: false,
|
|
Unique: true,
|
|
Indexer: &memdb.CompoundIndex{
|
|
AllowMissing: false,
|
|
Indexes: []memdb.Indexer{
|
|
&memdb.StringFieldIndex{
|
|
Field: "Namespace",
|
|
},
|
|
&memdb.UintFieldIndex{
|
|
Field: "CreateIndex",
|
|
},
|
|
&memdb.StringFieldIndex{
|
|
Field: "ID",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
}
|
|
|
|
// allocTableSchema returns the MemDB schema for the allocation table.
|
|
// This table is used to store all the task allocations between task groups
|
|
// and nodes.
|
|
func allocTableSchema() *memdb.TableSchema {
|
|
return &memdb.TableSchema{
|
|
Name: "allocs",
|
|
Indexes: map[string]*memdb.IndexSchema{
|
|
// id index is used for direct lookup of allocation by ID.
|
|
"id": {
|
|
Name: "id",
|
|
AllowMissing: false,
|
|
Unique: true,
|
|
Indexer: &memdb.UUIDFieldIndex{
|
|
Field: "ID",
|
|
},
|
|
},
|
|
|
|
// create index is used for listing allocations, ordering them by
|
|
// creation chronology. (Use a reverse iterator for newest first).
|
|
"create": {
|
|
Name: "create",
|
|
AllowMissing: false,
|
|
Unique: true,
|
|
Indexer: &memdb.CompoundIndex{
|
|
Indexes: []memdb.Indexer{
|
|
&memdb.UintFieldIndex{
|
|
Field: "CreateIndex",
|
|
},
|
|
&memdb.StringFieldIndex{
|
|
Field: "ID",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
|
|
// namespace is used to lookup evaluations by namespace.
|
|
// todo(shoenig): i think we can deprecate this and other like it
|
|
"namespace": {
|
|
Name: "namespace",
|
|
AllowMissing: false,
|
|
Unique: false,
|
|
Indexer: &memdb.StringFieldIndex{
|
|
Field: "Namespace",
|
|
},
|
|
},
|
|
|
|
// namespace_create index is used to lookup evaluations by namespace
|
|
// in their original chronological order based on CreateIndex.
|
|
//
|
|
// Use a prefix iterator (namespace_prefix) on a Namespace to iterate
|
|
// those evaluations in order of CreateIndex.
|
|
"namespace_create": {
|
|
Name: "namespace_create",
|
|
AllowMissing: false,
|
|
Unique: true,
|
|
Indexer: &memdb.CompoundIndex{
|
|
AllowMissing: false,
|
|
Indexes: []memdb.Indexer{
|
|
&memdb.StringFieldIndex{
|
|
Field: "Namespace",
|
|
},
|
|
&memdb.UintFieldIndex{
|
|
Field: "CreateIndex",
|
|
},
|
|
&memdb.StringFieldIndex{
|
|
Field: "ID",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
|
|
// Node index is used to lookup allocations by node
|
|
"node": {
|
|
Name: "node",
|
|
AllowMissing: true, // Missing is allow for failed allocations
|
|
Unique: false,
|
|
Indexer: &memdb.CompoundIndex{
|
|
Indexes: []memdb.Indexer{
|
|
&memdb.StringFieldIndex{
|
|
Field: "NodeID",
|
|
Lowercase: true,
|
|
},
|
|
|
|
// Conditional indexer on if allocation is terminal
|
|
&memdb.ConditionalIndex{
|
|
Conditional: func(obj interface{}) (bool, error) {
|
|
// Cast to allocation
|
|
alloc, ok := obj.(*structs.Allocation)
|
|
if !ok {
|
|
return false, fmt.Errorf("wrong type, got %t should be Allocation", obj)
|
|
}
|
|
|
|
// Check if the allocation is terminal
|
|
return alloc.TerminalStatus(), nil
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
|
|
// Job index is used to lookup allocations by job
|
|
"job": {
|
|
Name: "job",
|
|
AllowMissing: false,
|
|
Unique: false,
|
|
|
|
Indexer: &memdb.CompoundIndex{
|
|
Indexes: []memdb.Indexer{
|
|
&memdb.StringFieldIndex{
|
|
Field: "Namespace",
|
|
},
|
|
|
|
&memdb.StringFieldIndex{
|
|
Field: "JobID",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
|
|
// Eval index is used to lookup allocations by eval
|
|
"eval": {
|
|
Name: "eval",
|
|
AllowMissing: false,
|
|
Unique: false,
|
|
Indexer: &memdb.UUIDFieldIndex{
|
|
Field: "EvalID",
|
|
},
|
|
},
|
|
|
|
// Deployment index is used to lookup allocations by deployment
|
|
"deployment": {
|
|
Name: "deployment",
|
|
AllowMissing: true,
|
|
Unique: false,
|
|
Indexer: &memdb.UUIDFieldIndex{
|
|
Field: "DeploymentID",
|
|
},
|
|
},
|
|
|
|
// signing_key index is used to lookup live allocations by signing
|
|
// key ID
|
|
indexSigningKey: {
|
|
Name: indexSigningKey,
|
|
AllowMissing: true, // terminal allocations won't be indexed
|
|
Unique: false,
|
|
Indexer: &memdb.CompoundIndex{
|
|
Indexes: []memdb.Indexer{
|
|
&memdb.StringFieldIndex{
|
|
Field: "SigningKeyID",
|
|
},
|
|
&memdb.ConditionalIndex{
|
|
Conditional: func(obj interface{}) (bool, error) {
|
|
alloc, ok := obj.(*structs.Allocation)
|
|
if !ok {
|
|
return false, fmt.Errorf(
|
|
"wrong type, got %t should be Allocation", obj)
|
|
}
|
|
// note: this isn't alloc.TerminalStatus(),
|
|
// because we only want to consider the key
|
|
// unused if the allocation is terminal on both
|
|
// server and client
|
|
return !(alloc.ClientTerminalStatus() && alloc.ServerTerminalStatus()), nil
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
}
|
|
|
|
// vaultAccessorTableSchema returns the MemDB schema for the Vault Accessor
|
|
// Table. This table tracks Vault accessors for tokens created on behalf of
|
|
// allocations required Vault tokens.
|
|
func vaultAccessorTableSchema() *memdb.TableSchema {
|
|
return &memdb.TableSchema{
|
|
Name: "vault_accessors",
|
|
Indexes: map[string]*memdb.IndexSchema{
|
|
// The primary index is the accessor id
|
|
"id": {
|
|
Name: "id",
|
|
AllowMissing: false,
|
|
Unique: true,
|
|
Indexer: &memdb.StringFieldIndex{
|
|
Field: "Accessor",
|
|
},
|
|
},
|
|
|
|
"alloc_id": {
|
|
Name: "alloc_id",
|
|
AllowMissing: false,
|
|
Unique: false,
|
|
Indexer: &memdb.StringFieldIndex{
|
|
Field: "AllocID",
|
|
},
|
|
},
|
|
|
|
"node_id": {
|
|
Name: "node_id",
|
|
AllowMissing: false,
|
|
Unique: false,
|
|
Indexer: &memdb.StringFieldIndex{
|
|
Field: "NodeID",
|
|
},
|
|
},
|
|
},
|
|
}
|
|
}
|
|
|
|
// siTokenAccessorTableSchema returns the MemDB schema for the Service Identity
|
|
// token accessor table. This table tracks accessors for tokens created on behalf
|
|
// of allocations with Consul connect enabled tasks that need SI tokens.
|
|
func siTokenAccessorTableSchema() *memdb.TableSchema {
|
|
return &memdb.TableSchema{
|
|
Name: siTokenAccessorTable,
|
|
Indexes: map[string]*memdb.IndexSchema{
|
|
// The primary index is the accessor id
|
|
"id": {
|
|
Name: "id",
|
|
AllowMissing: false,
|
|
Unique: true,
|
|
Indexer: &memdb.StringFieldIndex{
|
|
Field: "AccessorID",
|
|
},
|
|
},
|
|
|
|
"alloc_id": {
|
|
Name: "alloc_id",
|
|
AllowMissing: false,
|
|
Unique: false,
|
|
Indexer: &memdb.StringFieldIndex{
|
|
Field: "AllocID",
|
|
},
|
|
},
|
|
|
|
"node_id": {
|
|
Name: "node_id",
|
|
AllowMissing: false,
|
|
Unique: false,
|
|
Indexer: &memdb.StringFieldIndex{
|
|
Field: "NodeID",
|
|
},
|
|
},
|
|
},
|
|
}
|
|
}
|
|
|
|
// aclPolicyTableSchema returns the MemDB schema for the policy table.
|
|
// This table is used to store the policies which are referenced by tokens
|
|
func aclPolicyTableSchema() *memdb.TableSchema {
|
|
return &memdb.TableSchema{
|
|
Name: "acl_policy",
|
|
Indexes: map[string]*memdb.IndexSchema{
|
|
"id": {
|
|
Name: "id",
|
|
AllowMissing: false,
|
|
Unique: true,
|
|
Indexer: &memdb.StringFieldIndex{
|
|
Field: "Name",
|
|
},
|
|
},
|
|
"job": {
|
|
Name: "job",
|
|
AllowMissing: true,
|
|
Unique: false,
|
|
Indexer: &ACLPolicyJobACLFieldIndex{},
|
|
},
|
|
},
|
|
}
|
|
}
|
|
|
|
// ACLPolicyJobACLFieldIndex is used to extract the policy's JobACL field and
|
|
// build an index on it.
|
|
type ACLPolicyJobACLFieldIndex struct{}
|
|
|
|
// FromObject is used to extract an index value from an
|
|
// object or to indicate that the index value is missing.
|
|
func (a *ACLPolicyJobACLFieldIndex) FromObject(obj interface{}) (bool, []byte, error) {
|
|
policy, ok := obj.(*structs.ACLPolicy)
|
|
if !ok {
|
|
return false, nil, fmt.Errorf("object %#v is not an ACLPolicy", obj)
|
|
}
|
|
|
|
if policy.JobACL == nil {
|
|
return false, nil, nil
|
|
}
|
|
|
|
ns := policy.JobACL.Namespace
|
|
if ns == "" {
|
|
return false, nil, nil
|
|
}
|
|
jobID := policy.JobACL.JobID
|
|
if jobID == "" {
|
|
return false, nil, fmt.Errorf(
|
|
"object %#v is not a valid ACLPolicy: JobACL.JobID without Namespace", obj)
|
|
}
|
|
|
|
val := ns + "\x00" + jobID + "\x00"
|
|
return true, []byte(val), nil
|
|
}
|
|
|
|
// FromArgs is used to build an exact index lookup based on arguments
|
|
func (a *ACLPolicyJobACLFieldIndex) FromArgs(args ...interface{}) ([]byte, error) {
|
|
if len(args) != 2 {
|
|
return nil, fmt.Errorf("must provide two arguments")
|
|
}
|
|
arg0, ok := args[0].(string)
|
|
if !ok {
|
|
return nil, fmt.Errorf("argument must be a string: %#v", args[0])
|
|
}
|
|
arg1, ok := args[1].(string)
|
|
if !ok {
|
|
return nil, fmt.Errorf("argument must be a string: %#v", args[0])
|
|
}
|
|
|
|
// Add the null character as a terminator
|
|
arg0 += "\x00" + arg1 + "\x00"
|
|
return []byte(arg0), nil
|
|
}
|
|
|
|
// PrefixFromArgs returns a prefix that should be used for scanning based on the arguments
|
|
func (a *ACLPolicyJobACLFieldIndex) PrefixFromArgs(args ...interface{}) ([]byte, error) {
|
|
val, err := a.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
|
|
}
|
|
|
|
// aclTokenTableSchema returns the MemDB schema for the tokens table.
|
|
// This table is used to store the bearer tokens which are used to authenticate
|
|
func aclTokenTableSchema() *memdb.TableSchema {
|
|
return &memdb.TableSchema{
|
|
Name: "acl_token",
|
|
Indexes: map[string]*memdb.IndexSchema{
|
|
"id": {
|
|
Name: "id",
|
|
AllowMissing: false,
|
|
Unique: true,
|
|
Indexer: &memdb.UUIDFieldIndex{
|
|
Field: "AccessorID",
|
|
},
|
|
},
|
|
"create": {
|
|
Name: "create",
|
|
AllowMissing: false,
|
|
Unique: true,
|
|
Indexer: &memdb.CompoundIndex{
|
|
Indexes: []memdb.Indexer{
|
|
&memdb.UintFieldIndex{
|
|
Field: "CreateIndex",
|
|
},
|
|
&memdb.StringFieldIndex{
|
|
Field: "AccessorID",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
"secret": {
|
|
Name: "secret",
|
|
AllowMissing: false,
|
|
Unique: true,
|
|
Indexer: &memdb.UUIDFieldIndex{
|
|
Field: "SecretID",
|
|
},
|
|
},
|
|
"global": {
|
|
Name: "global",
|
|
AllowMissing: false,
|
|
Unique: false,
|
|
Indexer: &memdb.FieldSetIndex{
|
|
Field: "Global",
|
|
},
|
|
},
|
|
indexExpiresGlobal: {
|
|
Name: indexExpiresGlobal,
|
|
AllowMissing: true,
|
|
Unique: false,
|
|
Indexer: indexer.SingleIndexer{
|
|
ReadIndex: indexer.ReadIndex(indexer.IndexFromTimeQuery),
|
|
WriteIndex: indexer.WriteIndex(indexExpiresGlobalFromACLToken),
|
|
},
|
|
},
|
|
indexExpiresLocal: {
|
|
Name: indexExpiresLocal,
|
|
AllowMissing: true,
|
|
Unique: false,
|
|
Indexer: indexer.SingleIndexer{
|
|
ReadIndex: indexer.ReadIndex(indexer.IndexFromTimeQuery),
|
|
WriteIndex: indexer.WriteIndex(indexExpiresLocalFromACLToken),
|
|
},
|
|
},
|
|
},
|
|
}
|
|
}
|
|
|
|
func indexExpiresLocalFromACLToken(raw interface{}) ([]byte, error) {
|
|
return indexExpiresFromACLToken(raw, false)
|
|
}
|
|
|
|
func indexExpiresGlobalFromACLToken(raw interface{}) ([]byte, error) {
|
|
return indexExpiresFromACLToken(raw, true)
|
|
}
|
|
|
|
// indexExpiresFromACLToken implements the indexer.WriteIndex interface and
|
|
// allows us to use an ACL tokens ExpirationTime as an index, if it is a
|
|
// non-default value. This allows for efficient lookups when trying to deal
|
|
// with removal of expired tokens from state.
|
|
func indexExpiresFromACLToken(raw interface{}, global bool) ([]byte, error) {
|
|
p, ok := raw.(*structs.ACLToken)
|
|
if !ok {
|
|
return nil, fmt.Errorf("unexpected type %T for structs.ACLToken index", raw)
|
|
}
|
|
if p.Global != global {
|
|
return nil, indexer.ErrMissingValueForIndex
|
|
}
|
|
if !p.HasExpirationTime() {
|
|
return nil, indexer.ErrMissingValueForIndex
|
|
}
|
|
if p.ExpirationTime.Unix() < 0 {
|
|
return nil, fmt.Errorf("token expiration time cannot be before the unix epoch: %s", p.ExpirationTime)
|
|
}
|
|
|
|
var b indexer.IndexBuilder
|
|
b.Time(*p.ExpirationTime)
|
|
return b.Bytes(), nil
|
|
}
|
|
|
|
// oneTimeTokenTableSchema returns the MemDB schema for the tokens table.
|
|
// This table is used to store one-time tokens for ACL tokens
|
|
func oneTimeTokenTableSchema() *memdb.TableSchema {
|
|
return &memdb.TableSchema{
|
|
Name: "one_time_token",
|
|
Indexes: map[string]*memdb.IndexSchema{
|
|
"secret": {
|
|
Name: "secret",
|
|
AllowMissing: false,
|
|
Unique: true,
|
|
Indexer: &memdb.UUIDFieldIndex{
|
|
Field: "OneTimeSecretID",
|
|
},
|
|
},
|
|
"id": {
|
|
Name: "id",
|
|
AllowMissing: false,
|
|
Unique: true,
|
|
Indexer: &memdb.UUIDFieldIndex{
|
|
Field: "AccessorID",
|
|
},
|
|
},
|
|
},
|
|
}
|
|
}
|
|
|
|
// singletonRecord can be used to describe tables which should contain only 1 entry.
|
|
// Example uses include storing node config or cluster metadata blobs.
|
|
var singletonRecord = &memdb.ConditionalIndex{
|
|
Conditional: func(interface{}) (bool, error) { return true, nil },
|
|
}
|
|
|
|
// schedulerConfigTableSchema returns the MemDB schema for the scheduler config table.
|
|
// This table is used to store configuration options for the scheduler
|
|
func schedulerConfigTableSchema() *memdb.TableSchema {
|
|
return &memdb.TableSchema{
|
|
Name: "scheduler_config",
|
|
Indexes: map[string]*memdb.IndexSchema{
|
|
"id": {
|
|
Name: "id",
|
|
AllowMissing: true,
|
|
Unique: true,
|
|
Indexer: singletonRecord, // we store only 1 scheduler config
|
|
},
|
|
},
|
|
}
|
|
}
|
|
|
|
// clusterMetaTableSchema returns the MemDB schema for the scheduler config table.
|
|
func clusterMetaTableSchema() *memdb.TableSchema {
|
|
return &memdb.TableSchema{
|
|
Name: "cluster_meta",
|
|
Indexes: map[string]*memdb.IndexSchema{
|
|
"id": {
|
|
Name: "id",
|
|
AllowMissing: false,
|
|
Unique: true,
|
|
Indexer: singletonRecord, // we store only 1 cluster metadata
|
|
},
|
|
},
|
|
}
|
|
}
|
|
|
|
// CSIVolumes are identified by id globally, and searchable by driver
|
|
func csiVolumeTableSchema() *memdb.TableSchema {
|
|
return &memdb.TableSchema{
|
|
Name: "csi_volumes",
|
|
Indexes: map[string]*memdb.IndexSchema{
|
|
"id": {
|
|
Name: "id",
|
|
AllowMissing: false,
|
|
Unique: true,
|
|
Indexer: &memdb.CompoundIndex{
|
|
Indexes: []memdb.Indexer{
|
|
&memdb.StringFieldIndex{
|
|
Field: "Namespace",
|
|
},
|
|
&memdb.StringFieldIndex{
|
|
Field: "ID",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
"plugin_id": {
|
|
Name: "plugin_id",
|
|
AllowMissing: false,
|
|
Unique: false,
|
|
Indexer: &memdb.StringFieldIndex{
|
|
Field: "PluginID",
|
|
},
|
|
},
|
|
},
|
|
}
|
|
}
|
|
|
|
// CSIPlugins are identified by id globally, and searchable by driver
|
|
func csiPluginTableSchema() *memdb.TableSchema {
|
|
return &memdb.TableSchema{
|
|
Name: "csi_plugins",
|
|
Indexes: map[string]*memdb.IndexSchema{
|
|
"id": {
|
|
Name: "id",
|
|
AllowMissing: false,
|
|
Unique: true,
|
|
Indexer: &memdb.StringFieldIndex{
|
|
Field: "ID",
|
|
},
|
|
},
|
|
},
|
|
}
|
|
}
|
|
|
|
// StringFieldIndex is used to extract a field from an object
|
|
// using reflection and builds an index on that field.
|
|
type ScalingPolicyTargetFieldIndex struct {
|
|
Field string
|
|
|
|
// AllowMissing controls if the field should be ignored if the field is
|
|
// not provided.
|
|
AllowMissing bool
|
|
}
|
|
|
|
// FromObject is used to extract an index value from an
|
|
// object or to indicate that the index value is missing.
|
|
func (s *ScalingPolicyTargetFieldIndex) FromObject(obj interface{}) (bool, []byte, error) {
|
|
policy, ok := obj.(*structs.ScalingPolicy)
|
|
if !ok {
|
|
return false, nil, fmt.Errorf("object %#v is not a ScalingPolicy", obj)
|
|
}
|
|
|
|
if policy.Target == nil {
|
|
return false, nil, nil
|
|
}
|
|
|
|
val, ok := policy.Target[s.Field]
|
|
if !ok && !s.AllowMissing {
|
|
return false, nil, nil
|
|
}
|
|
|
|
// Add the null character as a terminator
|
|
val += "\x00"
|
|
return true, []byte(val), nil
|
|
}
|
|
|
|
// FromArgs is used to build an exact index lookup based on arguments
|
|
func (s *ScalingPolicyTargetFieldIndex) FromArgs(args ...interface{}) ([]byte, error) {
|
|
if len(args) != 1 {
|
|
return nil, fmt.Errorf("must provide only a single argument")
|
|
}
|
|
arg, ok := args[0].(string)
|
|
if !ok {
|
|
return nil, fmt.Errorf("argument must be a string: %#v", args[0])
|
|
}
|
|
// Add the null character as a terminator
|
|
arg += "\x00"
|
|
return []byte(arg), nil
|
|
}
|
|
|
|
// PrefixFromArgs returns a prefix that should be used for scanning based on the arguments
|
|
func (s *ScalingPolicyTargetFieldIndex) PrefixFromArgs(args ...interface{}) ([]byte, error) {
|
|
val, err := s.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
|
|
}
|
|
|
|
// scalingPolicyTableSchema returns the MemDB schema for the policy table.
|
|
func scalingPolicyTableSchema() *memdb.TableSchema {
|
|
return &memdb.TableSchema{
|
|
Name: "scaling_policy",
|
|
Indexes: map[string]*memdb.IndexSchema{
|
|
// Primary index is used for simple direct lookup.
|
|
"id": {
|
|
Name: "id",
|
|
AllowMissing: false,
|
|
Unique: true,
|
|
|
|
// UUID is uniquely identifying
|
|
Indexer: &memdb.StringFieldIndex{
|
|
Field: "ID",
|
|
},
|
|
},
|
|
// Target index is used for listing by namespace or job, or looking up a specific target.
|
|
// A given task group can have only a single scaling policies, so this is guaranteed to be unique.
|
|
"target": {
|
|
Name: "target",
|
|
Unique: false,
|
|
|
|
// Use a compound index so the tuple of (Namespace, Job, Group, Task) is
|
|
// used when looking for a policy
|
|
Indexer: &memdb.CompoundIndex{
|
|
Indexes: []memdb.Indexer{
|
|
&ScalingPolicyTargetFieldIndex{
|
|
Field: "Namespace",
|
|
AllowMissing: true,
|
|
},
|
|
|
|
&ScalingPolicyTargetFieldIndex{
|
|
Field: "Job",
|
|
AllowMissing: true,
|
|
},
|
|
|
|
&ScalingPolicyTargetFieldIndex{
|
|
Field: "Group",
|
|
AllowMissing: true,
|
|
},
|
|
|
|
&ScalingPolicyTargetFieldIndex{
|
|
Field: "Task",
|
|
AllowMissing: true,
|
|
},
|
|
},
|
|
},
|
|
},
|
|
// Type index is used for listing by policy type
|
|
"type": {
|
|
Name: "type",
|
|
AllowMissing: false,
|
|
Unique: false,
|
|
Indexer: &memdb.StringFieldIndex{
|
|
Field: "Type",
|
|
},
|
|
},
|
|
// Used to filter by enabled
|
|
"enabled": {
|
|
Name: "enabled",
|
|
AllowMissing: false,
|
|
Unique: false,
|
|
Indexer: &memdb.FieldSetIndex{
|
|
Field: "Enabled",
|
|
},
|
|
},
|
|
},
|
|
}
|
|
}
|
|
|
|
// scalingEventTableSchema returns the memdb schema for job scaling events
|
|
func scalingEventTableSchema() *memdb.TableSchema {
|
|
return &memdb.TableSchema{
|
|
Name: "scaling_event",
|
|
Indexes: map[string]*memdb.IndexSchema{
|
|
"id": {
|
|
Name: "id",
|
|
AllowMissing: false,
|
|
Unique: true,
|
|
|
|
// Use a compound index so the tuple of (Namespace, JobID) is
|
|
// uniquely identifying
|
|
Indexer: &memdb.CompoundIndex{
|
|
Indexes: []memdb.Indexer{
|
|
&memdb.StringFieldIndex{
|
|
Field: "Namespace",
|
|
},
|
|
|
|
&memdb.StringFieldIndex{
|
|
Field: "JobID",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
|
|
// TODO: need to figure out whether we want to index these or the jobs or ...
|
|
// "error": {
|
|
// Name: "error",
|
|
// AllowMissing: false,
|
|
// Unique: false,
|
|
// Indexer: &memdb.FieldSetIndex{
|
|
// Field: "Error",
|
|
// },
|
|
// },
|
|
},
|
|
}
|
|
}
|
|
|
|
// namespaceTableSchema returns the MemDB schema for the namespace table.
|
|
func namespaceTableSchema() *memdb.TableSchema {
|
|
return &memdb.TableSchema{
|
|
Name: TableNamespaces,
|
|
Indexes: map[string]*memdb.IndexSchema{
|
|
"id": {
|
|
Name: "id",
|
|
AllowMissing: false,
|
|
Unique: true,
|
|
Indexer: &memdb.StringFieldIndex{
|
|
Field: "Name",
|
|
},
|
|
},
|
|
"quota": {
|
|
Name: "quota",
|
|
AllowMissing: true,
|
|
Unique: false,
|
|
Indexer: &memdb.StringFieldIndex{
|
|
Field: "Quota",
|
|
},
|
|
},
|
|
},
|
|
}
|
|
}
|
|
|
|
// serviceRegistrationsTableSchema returns the MemDB schema for Nomad native
|
|
// service registrations.
|
|
func serviceRegistrationsTableSchema() *memdb.TableSchema {
|
|
return &memdb.TableSchema{
|
|
Name: TableServiceRegistrations,
|
|
Indexes: map[string]*memdb.IndexSchema{
|
|
// The serviceID in combination with namespace forms a unique
|
|
// identifier for a service registration. This is used to look up
|
|
// and delete services in individual isolation.
|
|
indexID: {
|
|
Name: indexID,
|
|
AllowMissing: false,
|
|
Unique: true,
|
|
Indexer: &memdb.CompoundIndex{
|
|
Indexes: []memdb.Indexer{
|
|
&memdb.StringFieldIndex{
|
|
Field: "Namespace",
|
|
},
|
|
&memdb.StringFieldIndex{
|
|
Field: "ID",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
indexServiceName: {
|
|
Name: indexServiceName,
|
|
AllowMissing: false,
|
|
Unique: false,
|
|
Indexer: &memdb.CompoundIndex{
|
|
Indexes: []memdb.Indexer{
|
|
&memdb.StringFieldIndex{
|
|
Field: "Namespace",
|
|
},
|
|
&memdb.StringFieldIndex{
|
|
Field: "ServiceName",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
indexJob: {
|
|
Name: indexJob,
|
|
AllowMissing: false,
|
|
Unique: false,
|
|
Indexer: &memdb.CompoundIndex{
|
|
Indexes: []memdb.Indexer{
|
|
&memdb.StringFieldIndex{
|
|
Field: "Namespace",
|
|
},
|
|
&memdb.StringFieldIndex{
|
|
Field: "JobID",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
// The nodeID index allows lookups and deletions to be performed
|
|
// for an entire node. This is primarily used when a node becomes
|
|
// lost.
|
|
indexNodeID: {
|
|
Name: indexNodeID,
|
|
AllowMissing: false,
|
|
Unique: false,
|
|
Indexer: &memdb.StringFieldIndex{
|
|
Field: "NodeID",
|
|
},
|
|
},
|
|
indexAllocID: {
|
|
Name: indexAllocID,
|
|
AllowMissing: false,
|
|
Unique: false,
|
|
Indexer: &memdb.StringFieldIndex{
|
|
Field: "AllocID",
|
|
},
|
|
},
|
|
},
|
|
}
|
|
}
|
|
|
|
// variablesTableSchema returns the MemDB schema for Nomad variables.
|
|
func variablesTableSchema() *memdb.TableSchema {
|
|
return &memdb.TableSchema{
|
|
Name: TableVariables,
|
|
Indexes: map[string]*memdb.IndexSchema{
|
|
indexID: {
|
|
Name: indexID,
|
|
AllowMissing: false,
|
|
Unique: true,
|
|
Indexer: &memdb.CompoundIndex{
|
|
Indexes: []memdb.Indexer{
|
|
&memdb.StringFieldIndex{
|
|
Field: "Namespace",
|
|
},
|
|
&memdb.StringFieldIndex{
|
|
Field: "Path",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
indexKeyID: {
|
|
Name: indexKeyID,
|
|
AllowMissing: false,
|
|
Indexer: &variableKeyIDFieldIndexer{},
|
|
},
|
|
indexPath: {
|
|
Name: indexPath,
|
|
AllowMissing: false,
|
|
Unique: false,
|
|
Indexer: &memdb.StringFieldIndex{
|
|
Field: "Path",
|
|
},
|
|
},
|
|
},
|
|
}
|
|
}
|
|
|
|
type variableKeyIDFieldIndexer struct{}
|
|
|
|
// FromArgs implements go-memdb/Indexer and is used to build an exact
|
|
// index lookup based on arguments
|
|
func (s *variableKeyIDFieldIndexer) FromArgs(args ...interface{}) ([]byte, error) {
|
|
if len(args) != 1 {
|
|
return nil, fmt.Errorf("must provide only a single argument")
|
|
}
|
|
arg, ok := args[0].(string)
|
|
if !ok {
|
|
return nil, fmt.Errorf("argument must be a string: %#v", args[0])
|
|
}
|
|
// Add the null character as a terminator
|
|
arg += "\x00"
|
|
return []byte(arg), nil
|
|
}
|
|
|
|
// PrefixFromArgs implements go-memdb/PrefixIndexer and returns a
|
|
// prefix that should be used for scanning based on the arguments
|
|
func (s *variableKeyIDFieldIndexer) PrefixFromArgs(args ...interface{}) ([]byte, error) {
|
|
val, err := s.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
|
|
}
|
|
|
|
// FromObject implements go-memdb/SingleIndexer and is used to extract
|
|
// an index value from an object or to indicate that the index value
|
|
// is missing.
|
|
func (s *variableKeyIDFieldIndexer) FromObject(obj interface{}) (bool, []byte, error) {
|
|
variable, ok := obj.(*structs.VariableEncrypted)
|
|
if !ok {
|
|
return false, nil, fmt.Errorf("object %#v is not a Variable", obj)
|
|
}
|
|
|
|
keyID := variable.KeyID
|
|
if keyID == "" {
|
|
return false, nil, nil
|
|
}
|
|
|
|
// Add the null character as a terminator
|
|
keyID += "\x00"
|
|
return true, []byte(keyID), nil
|
|
}
|
|
|
|
// variablesQuotasTableSchema returns the MemDB schema for Nomad variables
|
|
// quotas tracking
|
|
func variablesQuotasTableSchema() *memdb.TableSchema {
|
|
return &memdb.TableSchema{
|
|
Name: TableVariablesQuotas,
|
|
Indexes: map[string]*memdb.IndexSchema{
|
|
indexID: {
|
|
Name: indexID,
|
|
AllowMissing: false,
|
|
Unique: true,
|
|
Indexer: &memdb.StringFieldIndex{
|
|
Field: "Namespace",
|
|
Lowercase: true,
|
|
},
|
|
},
|
|
},
|
|
}
|
|
}
|
|
|
|
// variablesRootKeyMetaSchema returns the MemDB schema for Nomad root keys
|
|
func variablesRootKeyMetaSchema() *memdb.TableSchema {
|
|
return &memdb.TableSchema{
|
|
Name: TableRootKeyMeta,
|
|
Indexes: map[string]*memdb.IndexSchema{
|
|
indexID: {
|
|
Name: indexID,
|
|
AllowMissing: false,
|
|
Unique: true,
|
|
Indexer: &memdb.StringFieldIndex{
|
|
Field: "KeyID",
|
|
Lowercase: true,
|
|
},
|
|
},
|
|
},
|
|
}
|
|
}
|
|
|
|
func aclRolesTableSchema() *memdb.TableSchema {
|
|
return &memdb.TableSchema{
|
|
Name: TableACLRoles,
|
|
Indexes: map[string]*memdb.IndexSchema{
|
|
indexID: {
|
|
Name: indexID,
|
|
AllowMissing: false,
|
|
Unique: true,
|
|
Indexer: &memdb.StringFieldIndex{
|
|
Field: "ID",
|
|
},
|
|
},
|
|
indexName: {
|
|
Name: indexName,
|
|
AllowMissing: false,
|
|
Unique: true,
|
|
Indexer: &memdb.StringFieldIndex{
|
|
Field: "Name",
|
|
},
|
|
},
|
|
},
|
|
}
|
|
}
|
|
|
|
func aclAuthMethodsTableSchema() *memdb.TableSchema {
|
|
return &memdb.TableSchema{
|
|
Name: TableACLAuthMethods,
|
|
Indexes: map[string]*memdb.IndexSchema{
|
|
indexID: {
|
|
Name: indexID,
|
|
AllowMissing: false,
|
|
Unique: true,
|
|
Indexer: &memdb.StringFieldIndex{
|
|
Field: "Name",
|
|
},
|
|
},
|
|
},
|
|
}
|
|
}
|
|
|
|
func bindingRulesTableSchema() *memdb.TableSchema {
|
|
return &memdb.TableSchema{
|
|
Name: TableACLBindingRules,
|
|
Indexes: map[string]*memdb.IndexSchema{
|
|
indexID: {
|
|
Name: indexID,
|
|
AllowMissing: false,
|
|
Unique: true,
|
|
Indexer: &memdb.StringFieldIndex{
|
|
Field: "ID",
|
|
},
|
|
},
|
|
indexAuthMethod: {
|
|
Name: indexAuthMethod,
|
|
AllowMissing: false,
|
|
Unique: false,
|
|
Indexer: &memdb.StringFieldIndex{
|
|
Field: "AuthMethod",
|
|
},
|
|
},
|
|
},
|
|
}
|
|
}
|