2019-03-19 22:56:17 +00:00
package state
import (
"fmt"
2019-07-01 20:23:36 +00:00
"github.com/hashicorp/consul/agent/consul/discoverychain"
2019-03-19 22:56:17 +00:00
"github.com/hashicorp/consul/agent/structs"
memdb "github.com/hashicorp/go-memdb"
)
const (
configTableName = "config-entries"
)
2019-03-27 23:52:38 +00:00
// configTableSchema returns a new table schema used to store global
// config entries.
2019-03-19 22:56:17 +00:00
func configTableSchema ( ) * memdb . TableSchema {
return & memdb . TableSchema {
Name : configTableName ,
Indexes : map [ string ] * memdb . IndexSchema {
"id" : & memdb . IndexSchema {
Name : "id" ,
AllowMissing : false ,
Unique : true ,
Indexer : & memdb . CompoundIndex {
Indexes : [ ] memdb . Indexer {
& memdb . StringFieldIndex {
Field : "Kind" ,
Lowercase : true ,
} ,
& memdb . StringFieldIndex {
Field : "Name" ,
Lowercase : true ,
} ,
} ,
} ,
} ,
2019-03-27 23:52:38 +00:00
"kind" : & memdb . IndexSchema {
Name : "kind" ,
AllowMissing : false ,
2019-04-07 06:38:08 +00:00
Unique : false ,
2019-03-27 23:52:38 +00:00
Indexer : & memdb . StringFieldIndex {
Field : "Kind" ,
Lowercase : true ,
} ,
} ,
2019-07-01 20:23:36 +00:00
"link" : & memdb . IndexSchema {
Name : "link" ,
AllowMissing : true ,
Unique : false ,
Indexer : & ConfigEntryLinkIndex { } ,
} ,
2019-03-19 22:56:17 +00:00
} ,
}
}
2019-07-01 20:23:36 +00:00
type ConfigEntryLinkIndex struct {
}
type discoveryChainConfigEntry interface {
structs . ConfigEntry
// ListRelatedServices returns a list of other names of services referenced
// in this config entry.
ListRelatedServices ( ) [ ] string
}
func ( s * ConfigEntryLinkIndex ) FromObject ( obj interface { } ) ( bool , [ ] [ ] byte , error ) {
entry , ok := obj . ( structs . ConfigEntry )
if ! ok {
return false , nil , fmt . Errorf ( "object is not a ConfigEntry" )
}
dcEntry , ok := entry . ( discoveryChainConfigEntry )
if ! ok {
return false , nil , nil
}
linkedServices := dcEntry . ListRelatedServices ( )
numLinks := len ( linkedServices )
if numLinks == 0 {
return false , nil , nil
}
vals := make ( [ ] [ ] byte , 0 , numLinks )
for _ , linkedService := range linkedServices {
vals = append ( vals , [ ] byte ( linkedService + "\x00" ) )
}
return true , vals , nil
}
func ( s * ConfigEntryLinkIndex ) 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
}
func ( s * ConfigEntryLinkIndex ) 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
}
2019-03-19 22:56:17 +00:00
func init ( ) {
registerSchema ( configTableSchema )
}
// ConfigEntries is used to pull all the config entries for the snapshot.
func ( s * Snapshot ) ConfigEntries ( ) ( [ ] structs . ConfigEntry , error ) {
2019-03-27 23:52:38 +00:00
entries , err := s . tx . Get ( configTableName , "id" )
2019-03-19 22:56:17 +00:00
if err != nil {
return nil , err
}
var ret [ ] structs . ConfigEntry
2019-03-27 23:52:38 +00:00
for wrapped := entries . Next ( ) ; wrapped != nil ; wrapped = entries . Next ( ) {
2019-03-19 22:56:17 +00:00
ret = append ( ret , wrapped . ( structs . ConfigEntry ) )
}
return ret , nil
}
2019-03-20 23:13:13 +00:00
// ConfigEntry is used when restoring from a snapshot.
2019-03-19 22:56:17 +00:00
func ( s * Restore ) ConfigEntry ( c structs . ConfigEntry ) error {
// Insert
if err := s . tx . Insert ( configTableName , c ) ; err != nil {
return fmt . Errorf ( "failed restoring config entry object: %s" , err )
}
if err := indexUpdateMaxTxn ( s . tx , c . GetRaftIndex ( ) . ModifyIndex , configTableName ) ; err != nil {
return fmt . Errorf ( "failed updating index: %s" , err )
}
return nil
}
2019-03-20 23:13:13 +00:00
// ConfigEntry is called to get a given config entry.
2019-04-07 06:38:08 +00:00
func ( s * Store ) ConfigEntry ( ws memdb . WatchSet , kind , name string ) ( uint64 , structs . ConfigEntry , error ) {
2019-03-27 23:52:38 +00:00
tx := s . db . Txn ( false )
2019-03-19 22:56:17 +00:00
defer tx . Abort ( )
2019-06-27 17:37:43 +00:00
return s . configEntryTxn ( tx , ws , kind , name )
}
2019-03-19 22:56:17 +00:00
2019-06-27 17:37:43 +00:00
func ( s * Store ) configEntryTxn ( tx * memdb . Txn , ws memdb . WatchSet , kind , name string ) ( uint64 , structs . ConfigEntry , error ) {
2019-03-19 22:56:17 +00:00
// Get the index
idx := maxIndexTxn ( tx , configTableName )
// Get the existing config entry.
2019-04-07 06:38:08 +00:00
watchCh , existing , err := tx . FirstWatch ( configTableName , "id" , kind , name )
2019-03-19 22:56:17 +00:00
if err != nil {
return 0 , nil , fmt . Errorf ( "failed config entry lookup: %s" , err )
}
2019-05-02 19:25:29 +00:00
ws . Add ( watchCh )
2019-03-19 22:56:17 +00:00
if existing == nil {
2019-03-27 23:52:38 +00:00
return idx , nil , nil
2019-03-19 22:56:17 +00:00
}
conf , ok := existing . ( structs . ConfigEntry )
if ! ok {
return 0 , nil , fmt . Errorf ( "config entry %q (%s) is an invalid type: %T" , name , kind , conf )
}
return idx , conf , nil
}
2019-03-20 23:13:13 +00:00
// ConfigEntries is called to get all config entry objects.
2019-04-07 06:38:08 +00:00
func ( s * Store ) ConfigEntries ( ws memdb . WatchSet ) ( uint64 , [ ] structs . ConfigEntry , error ) {
2019-04-26 17:38:39 +00:00
return s . ConfigEntriesByKind ( ws , "" )
2019-03-27 23:52:38 +00:00
}
// ConfigEntriesByKind is called to get all config entry objects with the given kind.
// If kind is empty, all config entries will be returned.
2019-04-07 06:38:08 +00:00
func ( s * Store ) ConfigEntriesByKind ( ws memdb . WatchSet , kind string ) ( uint64 , [ ] structs . ConfigEntry , error ) {
2019-03-27 23:52:38 +00:00
tx := s . db . Txn ( false )
2019-03-20 23:13:13 +00:00
defer tx . Abort ( )
2019-07-02 16:01:17 +00:00
return s . configEntriesByKindTxn ( tx , ws , kind )
}
2019-03-20 23:13:13 +00:00
2019-07-02 16:01:17 +00:00
func ( s * Store ) configEntriesByKindTxn ( tx * memdb . Txn , ws memdb . WatchSet , kind string ) ( uint64 , [ ] structs . ConfigEntry , error ) {
2019-03-20 23:13:13 +00:00
// Get the index
idx := maxIndexTxn ( tx , configTableName )
2019-03-27 23:52:38 +00:00
// Lookup by kind, or all if kind is empty
var iter memdb . ResultIterator
var err error
if kind != "" {
iter , err = tx . Get ( configTableName , "kind" , kind )
} else {
iter , err = tx . Get ( configTableName , "id" )
}
2019-03-20 23:13:13 +00:00
if err != nil {
return 0 , nil , fmt . Errorf ( "failed config entry lookup: %s" , err )
}
2019-04-07 06:38:08 +00:00
ws . Add ( iter . WatchCh ( ) )
2019-03-20 23:13:13 +00:00
var results [ ] structs . ConfigEntry
for v := iter . Next ( ) ; v != nil ; v = iter . Next ( ) {
results = append ( results , v . ( structs . ConfigEntry ) )
}
return idx , results , nil
}
2019-03-27 23:52:38 +00:00
// EnsureConfigEntry is called to do an upsert of a given config entry.
2019-03-19 22:56:17 +00:00
func ( s * Store ) EnsureConfigEntry ( idx uint64 , conf structs . ConfigEntry ) error {
tx := s . db . Txn ( true )
defer tx . Abort ( )
2019-03-27 23:52:38 +00:00
if err := s . ensureConfigEntryTxn ( tx , idx , conf ) ; err != nil {
return err
}
tx . Commit ( )
return nil
}
// ensureConfigEntryTxn upserts a config entry inside of a transaction.
func ( s * Store ) ensureConfigEntryTxn ( tx * memdb . Txn , idx uint64 , conf structs . ConfigEntry ) error {
2019-03-19 22:56:17 +00:00
// Check for existing configuration.
existing , err := tx . First ( configTableName , "id" , conf . GetKind ( ) , conf . GetName ( ) )
if err != nil {
return fmt . Errorf ( "failed configuration lookup: %s" , err )
}
raftIndex := conf . GetRaftIndex ( )
if existing != nil {
existingIdx := existing . ( structs . ConfigEntry ) . GetRaftIndex ( )
raftIndex . CreateIndex = existingIdx . CreateIndex
raftIndex . ModifyIndex = existingIdx . ModifyIndex
} else {
raftIndex . CreateIndex = idx
}
raftIndex . ModifyIndex = idx
2019-07-01 20:23:36 +00:00
err = s . validateProposedConfigEntryInGraph (
tx ,
idx ,
conf . GetKind ( ) ,
conf . GetName ( ) ,
conf ,
)
if err != nil {
return err // Err is already sufficiently decorated.
}
2019-03-19 22:56:17 +00:00
// Insert the config entry and update the index
if err := tx . Insert ( configTableName , conf ) ; err != nil {
return fmt . Errorf ( "failed inserting config entry: %s" , err )
}
2019-03-27 23:52:38 +00:00
if err := indexUpdateMaxTxn ( tx , idx , configTableName ) ; err != nil {
return fmt . Errorf ( "failed updating index: %v" , err )
2019-03-19 22:56:17 +00:00
}
return nil
}
2019-03-27 23:52:38 +00:00
// EnsureConfigEntryCAS is called to do a check-and-set upsert of a given config entry.
func ( s * Store ) EnsureConfigEntryCAS ( idx , cidx uint64 , conf structs . ConfigEntry ) ( bool , error ) {
2019-03-19 22:56:17 +00:00
tx := s . db . Txn ( true )
defer tx . Abort ( )
2019-03-27 23:52:38 +00:00
// Check for existing configuration.
existing , err := tx . First ( configTableName , "id" , conf . GetKind ( ) , conf . GetName ( ) )
if err != nil {
return false , fmt . Errorf ( "failed configuration lookup: %s" , err )
}
// Check if the we should do the set. A ModifyIndex of 0 means that
// we are doing a set-if-not-exists.
var existingIdx structs . RaftIndex
if existing != nil {
existingIdx = * existing . ( structs . ConfigEntry ) . GetRaftIndex ( )
}
if cidx == 0 && existing != nil {
return false , nil
}
if cidx != 0 && existing == nil {
return false , nil
}
if existing != nil && cidx != 0 && cidx != existingIdx . ModifyIndex {
return false , nil
}
if err := s . ensureConfigEntryTxn ( tx , idx , conf ) ; err != nil {
return false , err
}
tx . Commit ( )
return true , nil
}
func ( s * Store ) DeleteConfigEntry ( idx uint64 , kind , name string ) error {
tx := s . db . Txn ( true )
defer tx . Abort ( )
2019-03-19 22:56:17 +00:00
2019-04-07 06:38:08 +00:00
// Try to retrieve the existing config entry.
2019-03-19 22:56:17 +00:00
existing , err := tx . First ( configTableName , "id" , kind , name )
if err != nil {
return fmt . Errorf ( "failed config entry lookup: %s" , err )
}
if existing == nil {
return nil
}
2019-07-01 20:23:36 +00:00
err = s . validateProposedConfigEntryInGraph (
tx ,
idx ,
kind ,
name ,
nil ,
)
if err != nil {
return err // Err is already sufficiently decorated.
}
2019-03-19 22:56:17 +00:00
// Delete the config entry from the DB and update the index.
if err := tx . Delete ( configTableName , existing ) ; err != nil {
return fmt . Errorf ( "failed removing check: %s" , err )
}
if err := tx . Insert ( "index" , & IndexEntry { configTableName , idx } ) ; err != nil {
return fmt . Errorf ( "failed updating index: %s" , err )
}
tx . Commit ( )
return nil
}
2019-07-01 20:23:36 +00:00
// validateProposedConfigEntryInGraph can be used to verify graph integrity for
// a proposed graph create/update/delete.
//
// This must be called before any mutations occur on the config entries table!
//
// May return *ConfigEntryGraphValidationError if there is a concern to surface
// to the caller that they can correct.
func ( s * Store ) validateProposedConfigEntryInGraph (
tx * memdb . Txn ,
idx uint64 ,
kind , name string ,
2019-07-02 16:01:17 +00:00
next structs . ConfigEntry ,
2019-07-01 20:23:36 +00:00
) error {
2019-07-02 16:01:17 +00:00
validateAllChains := false
2019-07-01 20:23:36 +00:00
switch kind {
case structs . ProxyDefaults :
2019-07-02 16:01:17 +00:00
if name != structs . ProxyConfigGlobal {
return nil
}
validateAllChains = true
2019-07-01 20:23:36 +00:00
case structs . ServiceDefaults :
case structs . ServiceRouter :
case structs . ServiceSplitter :
case structs . ServiceResolver :
default :
return fmt . Errorf ( "unhandled kind %q during validation of %q" , kind , name )
}
2019-07-02 16:01:17 +00:00
return s . validateProposedConfigEntryInServiceGraph ( tx , idx , kind , name , next , validateAllChains )
}
var serviceGraphKinds = [ ] string {
structs . ServiceRouter ,
structs . ServiceSplitter ,
structs . ServiceResolver ,
2019-07-01 20:23:36 +00:00
}
func ( s * Store ) validateProposedConfigEntryInServiceGraph (
tx * memdb . Txn ,
idx uint64 ,
kind , name string ,
2019-07-02 16:01:17 +00:00
next structs . ConfigEntry ,
validateAllChains bool ,
2019-07-01 20:23:36 +00:00
) error {
// Collect all of the chains that could be affected by this change
// including our own.
2019-07-02 16:01:17 +00:00
checkChains := make ( map [ string ] struct { } )
if validateAllChains {
// Must be proxy-defaults/global.
// Check anything that has a discovery chain entry. In the future we could
// somehow omit the ones that have a default protocol configured.
for _ , kind := range serviceGraphKinds {
_ , entries , err := s . configEntriesByKindTxn ( tx , nil , kind )
if err != nil {
return err
}
for _ , entry := range entries {
checkChains [ entry . GetName ( ) ] = struct { } { }
}
}
} else {
// Must be a single chain.
2019-07-01 20:23:36 +00:00
2019-07-02 16:01:17 +00:00
checkChains [ name ] = struct { } { }
iter , err := tx . Get ( configTableName , "link" , name )
for raw := iter . Next ( ) ; raw != nil ; raw = iter . Next ( ) {
entry := raw . ( structs . ConfigEntry )
checkChains [ entry . GetName ( ) ] = struct { } { }
}
if err != nil {
return err
}
2019-07-01 20:23:36 +00:00
}
overrides := map [ structs . ConfigEntryKindName ] structs . ConfigEntry {
{ Kind : kind , Name : name } : next ,
}
for chainName , _ := range checkChains {
2019-07-02 16:01:17 +00:00
if err := s . testCompileDiscoveryChain ( tx , nil , chainName , overrides ) ; err != nil {
2019-07-01 20:23:36 +00:00
return err
}
}
return nil
}
2019-07-02 16:01:17 +00:00
func ( s * Store ) testCompileDiscoveryChain (
tx * memdb . Txn ,
ws memdb . WatchSet ,
chainName string ,
overrides map [ structs . ConfigEntryKindName ] structs . ConfigEntry ,
) error {
_ , speculativeEntries , err := s . readDiscoveryChainConfigEntriesTxn ( tx , nil , chainName , overrides )
if err != nil {
return err
}
// Note we use an arbitrary namespace and datacenter as those would not
// currently affect the graph compilation in ways that matter here.
2019-08-05 18:30:35 +00:00
//
2019-08-19 18:03:03 +00:00
// TODO(rb): we should thread a better value than "dc1" and the throwaway trust domain down here as that is going to sometimes show up in user facing errors
2019-07-02 16:01:17 +00:00
req := discoverychain . CompileRequest {
2019-08-19 18:03:03 +00:00
ServiceName : chainName ,
EvaluateInNamespace : "default" ,
EvaluateInDatacenter : "dc1" ,
EvaluateInTrustDomain : "b6fc9da3-03d4-4b5a-9134-c045e9b20152.consul" ,
UseInDatacenter : "dc1" ,
Entries : speculativeEntries ,
2019-07-02 16:01:17 +00:00
}
_ , err = discoverychain . Compile ( req )
return err
}
2019-07-01 20:23:36 +00:00
// ReadDiscoveryChainConfigEntries will query for the full discovery chain for
// the provided service name. All relevant config entries will be recursively
// fetched and included in the result.
//
// Once returned, the caller still needs to assemble these into a useful graph
// structure.
func ( s * Store ) ReadDiscoveryChainConfigEntries (
ws memdb . WatchSet ,
serviceName string ,
) ( uint64 , * structs . DiscoveryChainConfigEntries , error ) {
return s . readDiscoveryChainConfigEntries ( ws , serviceName , nil )
}
// readDiscoveryChainConfigEntries will query for the full discovery chain for
// the provided service name. All relevant config entries will be recursively
// fetched and included in the result.
//
// If 'overrides' is provided then it will use entries in that map instead of
// the database to simulate the entries that go into a modified discovery chain
// without actually modifying it yet. Nil values are tombstones to simulate
// deleting an entry.
//
// Overrides is not mutated.
func ( s * Store ) readDiscoveryChainConfigEntries (
ws memdb . WatchSet ,
serviceName string ,
overrides map [ structs . ConfigEntryKindName ] structs . ConfigEntry ,
) ( uint64 , * structs . DiscoveryChainConfigEntries , error ) {
tx := s . db . Txn ( false )
defer tx . Abort ( )
return s . readDiscoveryChainConfigEntriesTxn ( tx , ws , serviceName , overrides )
}
func ( s * Store ) readDiscoveryChainConfigEntriesTxn (
tx * memdb . Txn ,
ws memdb . WatchSet ,
serviceName string ,
overrides map [ structs . ConfigEntryKindName ] structs . ConfigEntry ,
) ( uint64 , * structs . DiscoveryChainConfigEntries , error ) {
2019-07-12 19:16:21 +00:00
res := structs . NewDiscoveryChainConfigEntries ( )
2019-07-01 20:23:36 +00:00
// Note that below we always look up splitters and resolvers in pairs, even
// in some circumstances where both are not strictly necessary.
//
// For now we'll just eat the cost of fetching pairs of splitter/resolver
// config entries even though we may not always need both. In the common
// case we will need the pair so there's not a big drive to optimize this
// here at this time.
// Both Splitters and Resolvers maps will contain placeholder nils until
// the end of this function to indicate "no such entry".
var (
todoSplitters = make ( map [ string ] struct { } )
todoResolvers = make ( map [ string ] struct { } )
todoDefaults = make ( map [ string ] struct { } )
)
2019-07-02 16:01:17 +00:00
// Grab the proxy defaults if they exist.
idx , proxy , err := s . getProxyConfigEntryTxn ( tx , ws , structs . ProxyConfigGlobal , overrides )
if err != nil {
return 0 , nil , err
} else if proxy != nil {
res . GlobalProxy = proxy
}
2019-07-01 20:23:36 +00:00
// At every step we'll need service defaults.
todoDefaults [ serviceName ] = struct { } { }
// first fetch the router, of which we only collect 1 per chain eval
_ , router , err := s . getRouterConfigEntryTxn ( tx , ws , serviceName , overrides )
if err != nil {
return 0 , nil , err
} else if router != nil {
res . Routers [ serviceName ] = router
}
if router != nil {
for _ , svc := range router . ListRelatedServices ( ) {
todoSplitters [ svc ] = struct { } { }
}
} else {
// Next hop in the chain is the splitter.
todoSplitters [ serviceName ] = struct { } { }
}
for {
name , ok := anyKey ( todoSplitters )
if ! ok {
break
}
delete ( todoSplitters , name )
if _ , ok := res . Splitters [ name ] ; ok {
continue // already fetched
}
// Yes, even for splitters.
todoDefaults [ name ] = struct { } { }
_ , splitter , err := s . getSplitterConfigEntryTxn ( tx , ws , name , overrides )
if err != nil {
return 0 , nil , err
}
if splitter == nil {
res . Splitters [ name ] = nil
// Next hop in the chain is the resolver.
todoResolvers [ name ] = struct { } { }
continue
}
res . Splitters [ name ] = splitter
todoResolvers [ name ] = struct { } { }
for _ , svc := range splitter . ListRelatedServices ( ) {
// If there is no splitter, this will end up adding a resolver
// after another iteration.
todoSplitters [ svc ] = struct { } { }
}
}
for {
name , ok := anyKey ( todoResolvers )
if ! ok {
break
}
delete ( todoResolvers , name )
if _ , ok := res . Resolvers [ name ] ; ok {
continue // already fetched
}
// And resolvers, too.
todoDefaults [ name ] = struct { } { }
_ , resolver , err := s . getResolverConfigEntryTxn ( tx , ws , name , overrides )
if err != nil {
return 0 , nil , err
}
if resolver == nil {
res . Resolvers [ name ] = nil
continue
}
res . Resolvers [ name ] = resolver
for _ , svc := range resolver . ListRelatedServices ( ) {
todoResolvers [ svc ] = struct { } { }
}
}
for {
name , ok := anyKey ( todoDefaults )
if ! ok {
break
}
delete ( todoDefaults , name )
if _ , ok := res . Services [ name ] ; ok {
continue // already fetched
}
_ , entry , err := s . getServiceConfigEntryTxn ( tx , ws , name , overrides )
if err != nil {
return 0 , nil , err
}
if entry == nil {
res . Services [ name ] = nil
continue
}
res . Services [ name ] = entry
}
// Strip nils now that they are no longer necessary.
for name , entry := range res . Routers {
if entry == nil {
delete ( res . Routers , name )
}
}
for name , entry := range res . Splitters {
if entry == nil {
delete ( res . Splitters , name )
}
}
for name , entry := range res . Resolvers {
if entry == nil {
delete ( res . Resolvers , name )
}
}
for name , entry := range res . Services {
if entry == nil {
delete ( res . Services , name )
}
}
return idx , res , nil
}
// anyKey returns any key from the provided map if any exist. Useful for using
// a map as a simple work queue of sorts.
func anyKey ( m map [ string ] struct { } ) ( string , bool ) {
if len ( m ) == 0 {
return "" , false
}
for k , _ := range m {
return k , true
}
return "" , false
}
2019-07-02 16:01:17 +00:00
// getProxyConfigEntryTxn is a convenience method for fetching a
// proxy-defaults kind of config entry.
//
// If an override is returned the index returned will be 0.
func ( s * Store ) getProxyConfigEntryTxn (
tx * memdb . Txn ,
ws memdb . WatchSet ,
name string ,
overrides map [ structs . ConfigEntryKindName ] structs . ConfigEntry ,
) ( uint64 , * structs . ProxyConfigEntry , error ) {
idx , entry , err := s . configEntryWithOverridesTxn ( tx , ws , structs . ProxyDefaults , name , overrides )
if err != nil {
return 0 , nil , err
} else if entry == nil {
return idx , nil , nil
}
proxy , ok := entry . ( * structs . ProxyConfigEntry )
if ! ok {
return 0 , nil , fmt . Errorf ( "invalid service config type %T" , entry )
}
return idx , proxy , nil
}
2019-07-01 20:23:36 +00:00
// getServiceConfigEntryTxn is a convenience method for fetching a
// service-defaults kind of config entry.
//
// If an override is returned the index returned will be 0.
func ( s * Store ) getServiceConfigEntryTxn (
tx * memdb . Txn ,
ws memdb . WatchSet ,
serviceName string ,
overrides map [ structs . ConfigEntryKindName ] structs . ConfigEntry ,
) ( uint64 , * structs . ServiceConfigEntry , error ) {
idx , entry , err := s . configEntryWithOverridesTxn ( tx , ws , structs . ServiceDefaults , serviceName , overrides )
if err != nil {
return 0 , nil , err
} else if entry == nil {
return idx , nil , nil
}
service , ok := entry . ( * structs . ServiceConfigEntry )
if ! ok {
return 0 , nil , fmt . Errorf ( "invalid service config type %T" , entry )
}
return idx , service , nil
}
// getRouterConfigEntryTxn is a convenience method for fetching a
// service-router kind of config entry.
//
// If an override is returned the index returned will be 0.
func ( s * Store ) getRouterConfigEntryTxn (
tx * memdb . Txn ,
ws memdb . WatchSet ,
serviceName string ,
overrides map [ structs . ConfigEntryKindName ] structs . ConfigEntry ,
) ( uint64 , * structs . ServiceRouterConfigEntry , error ) {
idx , entry , err := s . configEntryWithOverridesTxn ( tx , ws , structs . ServiceRouter , serviceName , overrides )
if err != nil {
return 0 , nil , err
} else if entry == nil {
return idx , nil , nil
}
router , ok := entry . ( * structs . ServiceRouterConfigEntry )
if ! ok {
return 0 , nil , fmt . Errorf ( "invalid service config type %T" , entry )
}
return idx , router , nil
}
// getSplitterConfigEntryTxn is a convenience method for fetching a
// service-splitter kind of config entry.
//
// If an override is returned the index returned will be 0.
func ( s * Store ) getSplitterConfigEntryTxn (
tx * memdb . Txn ,
ws memdb . WatchSet ,
serviceName string ,
overrides map [ structs . ConfigEntryKindName ] structs . ConfigEntry ,
) ( uint64 , * structs . ServiceSplitterConfigEntry , error ) {
idx , entry , err := s . configEntryWithOverridesTxn ( tx , ws , structs . ServiceSplitter , serviceName , overrides )
if err != nil {
return 0 , nil , err
} else if entry == nil {
return idx , nil , nil
}
splitter , ok := entry . ( * structs . ServiceSplitterConfigEntry )
if ! ok {
return 0 , nil , fmt . Errorf ( "invalid service config type %T" , entry )
}
return idx , splitter , nil
}
// getResolverConfigEntryTxn is a convenience method for fetching a
// service-resolver kind of config entry.
//
// If an override is returned the index returned will be 0.
func ( s * Store ) getResolverConfigEntryTxn (
tx * memdb . Txn ,
ws memdb . WatchSet ,
serviceName string ,
overrides map [ structs . ConfigEntryKindName ] structs . ConfigEntry ,
) ( uint64 , * structs . ServiceResolverConfigEntry , error ) {
idx , entry , err := s . configEntryWithOverridesTxn ( tx , ws , structs . ServiceResolver , serviceName , overrides )
if err != nil {
return 0 , nil , err
} else if entry == nil {
return idx , nil , nil
}
resolver , ok := entry . ( * structs . ServiceResolverConfigEntry )
if ! ok {
return 0 , nil , fmt . Errorf ( "invalid service config type %T" , entry )
}
return idx , resolver , nil
}
func ( s * Store ) configEntryWithOverridesTxn (
tx * memdb . Txn ,
ws memdb . WatchSet ,
kind string ,
name string ,
overrides map [ structs . ConfigEntryKindName ] structs . ConfigEntry ,
) ( uint64 , structs . ConfigEntry , error ) {
if len ( overrides ) > 0 {
entry , ok := overrides [ structs . ConfigEntryKindName {
Kind : kind , Name : name ,
} ]
if ok {
return 0 , entry , nil // a nil entry implies it should act like it is erased
}
}
return s . configEntryTxn ( tx , ws , kind , name )
}