2019-03-19 22:56:17 +00:00
package state
import (
"fmt"
2020-04-16 21:00:48 +00:00
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-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.
2020-01-24 15:04:58 +00:00
ListRelatedServices ( ) [ ] structs . ServiceID
2019-07-01 20:23:36 +00:00
}
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 {
2020-01-24 15:04:58 +00:00
vals = append ( vals , [ ] byte ( linkedService . String ( ) + "\x00" ) )
2019-07-01 20:23:36 +00:00
}
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" )
}
2020-01-24 15:04:58 +00:00
arg , ok := args [ 0 ] . ( structs . ServiceID )
2019-07-01 20:23:36 +00:00
if ! ok {
2020-01-24 15:04:58 +00:00
return nil , fmt . Errorf ( "argument must be a structs.ServiceID: %#v" , args [ 0 ] )
2019-07-01 20:23:36 +00:00
}
// Add the null character as a terminator
2020-01-24 15:04:58 +00:00
return [ ] byte ( arg . String ( ) + "\x00" ) , nil
2019-07-01 20:23:36 +00:00
}
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.
2020-01-24 15:04:58 +00:00
func ( s * Store ) ConfigEntry ( ws memdb . WatchSet , kind , name string , entMeta * structs . EnterpriseMeta ) ( 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 ( )
2020-01-24 15:04:58 +00:00
return s . configEntryTxn ( tx , ws , kind , name , entMeta )
2019-06-27 17:37:43 +00:00
}
2019-03-19 22:56:17 +00:00
2020-01-24 15:04:58 +00:00
func ( s * Store ) configEntryTxn ( tx * memdb . Txn , ws memdb . WatchSet , kind , name string , entMeta * structs . EnterpriseMeta ) ( uint64 , structs . ConfigEntry , error ) {
2019-03-19 22:56:17 +00:00
// Get the index
idx := maxIndexTxn ( tx , configTableName )
// Get the existing config entry.
2020-01-24 15:04:58 +00:00
watchCh , existing , err := s . firstWatchConfigEntryWithTxn ( tx , kind , name , entMeta )
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.
2020-01-24 15:04:58 +00:00
func ( s * Store ) ConfigEntries ( ws memdb . WatchSet , entMeta * structs . EnterpriseMeta ) ( uint64 , [ ] structs . ConfigEntry , error ) {
return s . ConfigEntriesByKind ( ws , "" , entMeta )
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.
2020-01-24 15:04:58 +00:00
func ( s * Store ) ConfigEntriesByKind ( ws memdb . WatchSet , kind string , entMeta * structs . EnterpriseMeta ) ( 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 ( )
2020-01-24 15:04:58 +00:00
return s . configEntriesByKindTxn ( tx , ws , kind , entMeta )
2019-07-02 16:01:17 +00:00
}
2019-03-20 23:13:13 +00:00
2020-01-24 15:04:58 +00:00
func ( s * Store ) configEntriesByKindTxn ( tx * memdb . Txn , ws memdb . WatchSet , kind string , entMeta * structs . EnterpriseMeta ) ( 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 != "" {
2020-01-24 15:04:58 +00:00
iter , err = getConfigEntryKindsWithTxn ( tx , kind , entMeta )
2019-03-27 23:52:38 +00:00
} else {
2020-01-24 15:04:58 +00:00
iter , err = getAllConfigEntriesWithTxn ( tx , entMeta )
2019-03-27 23:52:38 +00:00
}
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.
2020-01-24 15:04:58 +00:00
func ( s * Store ) EnsureConfigEntry ( idx uint64 , conf structs . ConfigEntry , entMeta * structs . EnterpriseMeta ) error {
2019-03-19 22:56:17 +00:00
tx := s . db . Txn ( true )
defer tx . Abort ( )
2020-01-24 15:04:58 +00:00
if err := s . ensureConfigEntryTxn ( tx , idx , conf , entMeta ) ; err != nil {
2019-03-27 23:52:38 +00:00
return err
}
tx . Commit ( )
return nil
}
// ensureConfigEntryTxn upserts a config entry inside of a transaction.
2020-01-24 15:04:58 +00:00
func ( s * Store ) ensureConfigEntryTxn ( tx * memdb . Txn , idx uint64 , conf structs . ConfigEntry , entMeta * structs . EnterpriseMeta ) error {
2019-03-19 22:56:17 +00:00
// Check for existing configuration.
2020-01-24 15:04:58 +00:00
existing , err := s . firstConfigEntryWithTxn ( tx , conf . GetKind ( ) , conf . GetName ( ) , entMeta )
2019-03-19 22:56:17 +00:00
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 ,
2020-01-24 15:04:58 +00:00
entMeta ,
2019-07-01 20:23:36 +00:00
)
if err != nil {
return err // Err is already sufficiently decorated.
}
2020-04-16 21:00:48 +00:00
// If the config entry is for a terminating or ingress gateway we update the memdb table
2020-04-08 18:37:24 +00:00
// that associates gateways <-> services.
2020-04-16 21:00:48 +00:00
if conf . GetKind ( ) == structs . TerminatingGateway || conf . GetKind ( ) == structs . IngressGateway {
err = s . updateGatewayServices ( tx , idx , conf , entMeta )
2020-04-08 18:37:24 +00:00
if err != nil {
return fmt . Errorf ( "failed to associate services to gateway: %v" , err )
}
}
2019-03-19 22:56:17 +00:00
// Insert the config entry and update the index
2020-01-24 15:04:58 +00:00
if err := s . insertConfigEntryWithTxn ( tx , conf ) ; err != nil {
2019-03-19 22:56:17 +00:00
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.
2020-01-24 15:04:58 +00:00
func ( s * Store ) EnsureConfigEntryCAS ( idx , cidx uint64 , conf structs . ConfigEntry , entMeta * structs . EnterpriseMeta ) ( 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.
2020-01-24 15:04:58 +00:00
existing , err := s . firstConfigEntryWithTxn ( tx , conf . GetKind ( ) , conf . GetName ( ) , entMeta )
2019-03-27 23:52:38 +00:00
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
}
2020-01-24 15:04:58 +00:00
if err := s . ensureConfigEntryTxn ( tx , idx , conf , entMeta ) ; err != nil {
2019-03-27 23:52:38 +00:00
return false , err
}
tx . Commit ( )
return true , nil
}
2020-01-24 15:04:58 +00:00
func ( s * Store ) DeleteConfigEntry ( idx uint64 , kind , name string , entMeta * structs . EnterpriseMeta ) error {
2019-03-27 23:52:38 +00:00
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.
2020-01-24 15:04:58 +00:00
existing , err := s . firstConfigEntryWithTxn ( tx , kind , name , entMeta )
2019-03-19 22:56:17 +00:00
if err != nil {
return fmt . Errorf ( "failed config entry lookup: %s" , err )
}
if existing == nil {
return nil
}
2020-04-16 21:00:48 +00:00
// If the config entry is for terminating or ingress gateways we delete entries from the memdb table
2020-04-08 18:37:24 +00:00
// that associates gateways <-> services.
2020-04-16 21:00:48 +00:00
if kind == structs . TerminatingGateway || kind == structs . IngressGateway {
if _ , err := tx . DeleteAll ( gatewayServicesTableName , "gateway" , structs . NewServiceID ( name , entMeta ) ) ; err != nil {
2020-04-08 18:37:24 +00:00
return fmt . Errorf ( "failed to truncate gateway services table: %v" , err )
}
2020-04-16 21:00:48 +00:00
if err := indexUpdateMaxTxn ( tx , idx , gatewayServicesTableName ) ; err != nil {
return fmt . Errorf ( "failed updating gateway-services index: %v" , err )
2020-04-08 18:37:24 +00:00
}
}
2019-07-01 20:23:36 +00:00
err = s . validateProposedConfigEntryInGraph (
tx ,
idx ,
kind ,
name ,
nil ,
2020-01-24 15:04:58 +00:00
entMeta ,
2019-07-01 20:23:36 +00:00
)
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 ,
2020-01-24 15:04:58 +00:00
entMeta * structs . EnterpriseMeta ,
2019-07-01 20:23:36 +00:00
) error {
2020-01-24 15:04:58 +00:00
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 :
2020-03-31 16:59:10 +00:00
case structs . IngressGateway :
2020-04-16 21:00:48 +00:00
err := s . checkGatewayClash ( tx , name , structs . IngressGateway , structs . TerminatingGateway , entMeta )
if err != nil {
return err
}
2020-03-31 19:27:32 +00:00
case structs . TerminatingGateway :
2020-04-16 21:00:48 +00:00
err := s . checkGatewayClash ( tx , name , structs . TerminatingGateway , structs . IngressGateway , entMeta )
if err != nil {
return err
}
2019-07-01 20:23:36 +00:00
default :
return fmt . Errorf ( "unhandled kind %q during validation of %q" , kind , name )
}
2019-07-02 16:01:17 +00:00
2020-01-24 15:04:58 +00:00
return s . validateProposedConfigEntryInServiceGraph ( tx , idx , kind , name , next , validateAllChains , entMeta )
2019-07-02 16:01:17 +00:00
}
2020-04-16 21:00:48 +00:00
func ( s * Store ) checkGatewayClash (
tx * memdb . Txn ,
name , selfKind , otherKind string ,
entMeta * structs . EnterpriseMeta ,
) error {
_ , entry , err := s . configEntryTxn ( tx , nil , otherKind , name , entMeta )
if err != nil {
return err
}
if entry != nil {
return fmt . Errorf ( "cannot create a %q config entry with name %q, " +
"a %q config entry with that name already exists" , selfKind , name , otherKind )
}
return nil
}
2019-07-02 16:01:17 +00:00
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 ,
2020-01-24 15:04:58 +00:00
entMeta * structs . EnterpriseMeta ,
2019-07-01 20:23:36 +00:00
) error {
// Collect all of the chains that could be affected by this change
// including our own.
2020-01-24 15:04:58 +00:00
checkChains := make ( map [ structs . ServiceID ] struct { } )
2019-07-02 16:01:17 +00:00
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 {
2020-02-05 15:06:27 +00:00
_ , entries , err := s . configEntriesByKindTxn ( tx , nil , kind , structs . WildcardEnterpriseMeta ( ) )
2019-07-02 16:01:17 +00:00
if err != nil {
return err
}
for _ , entry := range entries {
2020-01-24 15:04:58 +00:00
checkChains [ structs . NewServiceID ( entry . GetName ( ) , entry . GetEnterpriseMeta ( ) ) ] = struct { } { }
2019-07-02 16:01:17 +00:00
}
}
} else {
// Must be a single chain.
2019-07-01 20:23:36 +00:00
2020-01-24 15:04:58 +00:00
sid := structs . NewServiceID ( name , entMeta )
checkChains [ sid ] = struct { } { }
2019-07-02 16:01:17 +00:00
2020-01-24 15:04:58 +00:00
iter , err := tx . Get ( configTableName , "link" , sid )
2019-07-02 16:01:17 +00:00
for raw := iter . Next ( ) ; raw != nil ; raw = iter . Next ( ) {
entry := raw . ( structs . ConfigEntry )
2020-01-24 15:04:58 +00:00
checkChains [ structs . NewServiceID ( entry . GetName ( ) , entry . GetEnterpriseMeta ( ) ) ] = struct { } { }
2019-07-02 16:01:17 +00:00
}
if err != nil {
return err
}
2019-07-01 20:23:36 +00:00
}
overrides := map [ structs . ConfigEntryKindName ] structs . ConfigEntry {
{ Kind : kind , Name : name } : next ,
}
2020-01-24 15:04:58 +00:00
for chain , _ := range checkChains {
if err := s . testCompileDiscoveryChain ( tx , nil , chain . ID , overrides , & chain . EnterpriseMeta ) ; 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 ,
2020-01-24 15:04:58 +00:00
entMeta * structs . EnterpriseMeta ,
2019-07-02 16:01:17 +00:00
) error {
2020-01-24 15:04:58 +00:00
_ , speculativeEntries , err := s . readDiscoveryChainConfigEntriesTxn ( tx , nil , chainName , overrides , entMeta )
2019-07-02 16:01:17 +00:00
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 ,
2020-01-24 15:04:58 +00:00
EvaluateInNamespace : entMeta . NamespaceOrDefault ( ) ,
2019-08-19 18:03:03 +00:00
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 ,
2020-01-24 15:04:58 +00:00
entMeta * structs . EnterpriseMeta ,
2019-07-01 20:23:36 +00:00
) ( uint64 , * structs . DiscoveryChainConfigEntries , error ) {
2020-01-24 15:04:58 +00:00
return s . readDiscoveryChainConfigEntries ( ws , serviceName , nil , entMeta )
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.
//
// 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 ,
2020-01-24 15:04:58 +00:00
entMeta * structs . EnterpriseMeta ,
2019-07-01 20:23:36 +00:00
) ( uint64 , * structs . DiscoveryChainConfigEntries , error ) {
tx := s . db . Txn ( false )
defer tx . Abort ( )
2020-01-24 15:04:58 +00:00
return s . readDiscoveryChainConfigEntriesTxn ( tx , ws , serviceName , overrides , entMeta )
2019-07-01 20:23:36 +00:00
}
func ( s * Store ) readDiscoveryChainConfigEntriesTxn (
tx * memdb . Txn ,
ws memdb . WatchSet ,
serviceName string ,
overrides map [ structs . ConfigEntryKindName ] structs . ConfigEntry ,
2020-01-24 15:04:58 +00:00
entMeta * structs . EnterpriseMeta ,
2019-07-01 20:23:36 +00:00
) ( 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 (
2020-01-24 15:04:58 +00:00
todoSplitters = make ( map [ structs . ServiceID ] struct { } )
todoResolvers = make ( map [ structs . ServiceID ] struct { } )
todoDefaults = make ( map [ structs . ServiceID ] struct { } )
2019-07-01 20:23:36 +00:00
)
2020-01-24 15:04:58 +00:00
sid := structs . NewServiceID ( serviceName , entMeta )
2019-07-02 16:01:17 +00:00
// Grab the proxy defaults if they exist.
2020-01-24 15:04:58 +00:00
idx , proxy , err := s . getProxyConfigEntryTxn ( tx , ws , structs . ProxyConfigGlobal , overrides , structs . DefaultEnterpriseMeta ( ) )
2019-07-02 16:01:17 +00:00
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.
2020-01-24 15:04:58 +00:00
todoDefaults [ sid ] = struct { } { }
2019-07-01 20:23:36 +00:00
// first fetch the router, of which we only collect 1 per chain eval
2020-01-24 15:04:58 +00:00
_ , router , err := s . getRouterConfigEntryTxn ( tx , ws , serviceName , overrides , entMeta )
2019-07-01 20:23:36 +00:00
if err != nil {
return 0 , nil , err
} else if router != nil {
2020-01-24 15:04:58 +00:00
res . Routers [ sid ] = router
2019-07-01 20:23:36 +00:00
}
if router != nil {
for _ , svc := range router . ListRelatedServices ( ) {
todoSplitters [ svc ] = struct { } { }
}
} else {
// Next hop in the chain is the splitter.
2020-01-24 15:04:58 +00:00
todoSplitters [ sid ] = struct { } { }
2019-07-01 20:23:36 +00:00
}
for {
2020-01-24 15:04:58 +00:00
splitID , ok := anyKey ( todoSplitters )
2019-07-01 20:23:36 +00:00
if ! ok {
break
}
2020-01-24 15:04:58 +00:00
delete ( todoSplitters , splitID )
2019-07-01 20:23:36 +00:00
2020-01-24 15:04:58 +00:00
if _ , ok := res . Splitters [ splitID ] ; ok {
2019-07-01 20:23:36 +00:00
continue // already fetched
}
// Yes, even for splitters.
2020-01-24 15:04:58 +00:00
todoDefaults [ splitID ] = struct { } { }
2019-07-01 20:23:36 +00:00
2020-01-24 15:04:58 +00:00
_ , splitter , err := s . getSplitterConfigEntryTxn ( tx , ws , splitID . ID , overrides , & splitID . EnterpriseMeta )
2019-07-01 20:23:36 +00:00
if err != nil {
return 0 , nil , err
}
if splitter == nil {
2020-01-24 15:04:58 +00:00
res . Splitters [ splitID ] = nil
2019-07-01 20:23:36 +00:00
// Next hop in the chain is the resolver.
2020-01-24 15:04:58 +00:00
todoResolvers [ splitID ] = struct { } { }
2019-07-01 20:23:36 +00:00
continue
}
2020-01-24 15:04:58 +00:00
res . Splitters [ splitID ] = splitter
2019-07-01 20:23:36 +00:00
2020-01-24 15:04:58 +00:00
todoResolvers [ splitID ] = struct { } { }
2019-07-01 20:23:36 +00:00
for _ , svc := range splitter . ListRelatedServices ( ) {
// If there is no splitter, this will end up adding a resolver
// after another iteration.
todoSplitters [ svc ] = struct { } { }
}
}
for {
2020-01-24 15:04:58 +00:00
resolverID , ok := anyKey ( todoResolvers )
2019-07-01 20:23:36 +00:00
if ! ok {
break
}
2020-01-24 15:04:58 +00:00
delete ( todoResolvers , resolverID )
2019-07-01 20:23:36 +00:00
2020-01-24 15:04:58 +00:00
if _ , ok := res . Resolvers [ resolverID ] ; ok {
2019-07-01 20:23:36 +00:00
continue // already fetched
}
// And resolvers, too.
2020-01-24 15:04:58 +00:00
todoDefaults [ resolverID ] = struct { } { }
2019-07-01 20:23:36 +00:00
2020-01-24 15:04:58 +00:00
_ , resolver , err := s . getResolverConfigEntryTxn ( tx , ws , resolverID . ID , overrides , & resolverID . EnterpriseMeta )
2019-07-01 20:23:36 +00:00
if err != nil {
return 0 , nil , err
}
if resolver == nil {
2020-01-24 15:04:58 +00:00
res . Resolvers [ resolverID ] = nil
2019-07-01 20:23:36 +00:00
continue
}
2020-01-24 15:04:58 +00:00
res . Resolvers [ resolverID ] = resolver
2019-07-01 20:23:36 +00:00
for _ , svc := range resolver . ListRelatedServices ( ) {
todoResolvers [ svc ] = struct { } { }
}
}
for {
2020-01-24 15:04:58 +00:00
svcID , ok := anyKey ( todoDefaults )
2019-07-01 20:23:36 +00:00
if ! ok {
break
}
2020-01-24 15:04:58 +00:00
delete ( todoDefaults , svcID )
2019-07-01 20:23:36 +00:00
2020-01-24 15:04:58 +00:00
if _ , ok := res . Services [ svcID ] ; ok {
2019-07-01 20:23:36 +00:00
continue // already fetched
}
2020-01-24 15:04:58 +00:00
_ , entry , err := s . getServiceConfigEntryTxn ( tx , ws , svcID . ID , overrides , & svcID . EnterpriseMeta )
2019-07-01 20:23:36 +00:00
if err != nil {
return 0 , nil , err
}
if entry == nil {
2020-01-24 15:04:58 +00:00
res . Services [ svcID ] = nil
2019-07-01 20:23:36 +00:00
continue
}
2020-01-24 15:04:58 +00:00
res . Services [ svcID ] = entry
2019-07-01 20:23:36 +00:00
}
// Strip nils now that they are no longer necessary.
2020-01-24 15:04:58 +00:00
for sid , entry := range res . Routers {
2019-07-01 20:23:36 +00:00
if entry == nil {
2020-01-24 15:04:58 +00:00
delete ( res . Routers , sid )
2019-07-01 20:23:36 +00:00
}
}
2020-01-24 15:04:58 +00:00
for sid , entry := range res . Splitters {
2019-07-01 20:23:36 +00:00
if entry == nil {
2020-01-24 15:04:58 +00:00
delete ( res . Splitters , sid )
2019-07-01 20:23:36 +00:00
}
}
2020-01-24 15:04:58 +00:00
for sid , entry := range res . Resolvers {
2019-07-01 20:23:36 +00:00
if entry == nil {
2020-01-24 15:04:58 +00:00
delete ( res . Resolvers , sid )
2019-07-01 20:23:36 +00:00
}
}
2020-01-24 15:04:58 +00:00
for sid , entry := range res . Services {
2019-07-01 20:23:36 +00:00
if entry == nil {
2020-01-24 15:04:58 +00:00
delete ( res . Services , sid )
2019-07-01 20:23:36 +00:00
}
}
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.
2020-01-24 15:04:58 +00:00
func anyKey ( m map [ structs . ServiceID ] struct { } ) ( structs . ServiceID , bool ) {
2019-07-01 20:23:36 +00:00
if len ( m ) == 0 {
2020-01-24 15:04:58 +00:00
return structs . ServiceID { } , false
2019-07-01 20:23:36 +00:00
}
for k , _ := range m {
return k , true
}
2020-01-24 15:04:58 +00:00
return structs . ServiceID { } , false
2019-07-01 20:23:36 +00:00
}
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 ,
2020-01-24 15:04:58 +00:00
entMeta * structs . EnterpriseMeta ,
2019-07-02 16:01:17 +00:00
) ( uint64 , * structs . ProxyConfigEntry , error ) {
2020-01-24 15:04:58 +00:00
idx , entry , err := s . configEntryWithOverridesTxn ( tx , ws , structs . ProxyDefaults , name , overrides , entMeta )
2019-07-02 16:01:17 +00:00
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 ,
2020-01-24 15:04:58 +00:00
entMeta * structs . EnterpriseMeta ,
2019-07-01 20:23:36 +00:00
) ( uint64 , * structs . ServiceConfigEntry , error ) {
2020-01-24 15:04:58 +00:00
idx , entry , err := s . configEntryWithOverridesTxn ( tx , ws , structs . ServiceDefaults , serviceName , overrides , entMeta )
2019-07-01 20:23:36 +00:00
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 ,
2020-01-24 15:04:58 +00:00
entMeta * structs . EnterpriseMeta ,
2019-07-01 20:23:36 +00:00
) ( uint64 , * structs . ServiceRouterConfigEntry , error ) {
2020-01-24 15:04:58 +00:00
idx , entry , err := s . configEntryWithOverridesTxn ( tx , ws , structs . ServiceRouter , serviceName , overrides , entMeta )
2019-07-01 20:23:36 +00:00
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 ,
2020-01-24 15:04:58 +00:00
entMeta * structs . EnterpriseMeta ,
2019-07-01 20:23:36 +00:00
) ( uint64 , * structs . ServiceSplitterConfigEntry , error ) {
2020-01-24 15:04:58 +00:00
idx , entry , err := s . configEntryWithOverridesTxn ( tx , ws , structs . ServiceSplitter , serviceName , overrides , entMeta )
2019-07-01 20:23:36 +00:00
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 ,
2020-01-24 15:04:58 +00:00
entMeta * structs . EnterpriseMeta ,
2019-07-01 20:23:36 +00:00
) ( uint64 , * structs . ServiceResolverConfigEntry , error ) {
2020-01-24 15:04:58 +00:00
idx , entry , err := s . configEntryWithOverridesTxn ( tx , ws , structs . ServiceResolver , serviceName , overrides , entMeta )
2019-07-01 20:23:36 +00:00
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 ,
2020-01-24 15:04:58 +00:00
entMeta * structs . EnterpriseMeta ,
2019-07-01 20:23:36 +00:00
) ( 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
}
}
2020-01-24 15:04:58 +00:00
return s . configEntryTxn ( tx , ws , kind , name , entMeta )
2019-07-01 20:23:36 +00:00
}