// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: BUSL-1.1
package proxycfgglue
import (
"context"
"github.com/hashicorp/go-memdb"
"github.com/hashicorp/consul/acl"
"github.com/hashicorp/consul/agent/cache"
cachetype "github.com/hashicorp/consul/agent/cache-types"
"github.com/hashicorp/consul/agent/consul/discoverychain"
"github.com/hashicorp/consul/agent/consul/watch"
"github.com/hashicorp/consul/agent/proxycfg"
"github.com/hashicorp/consul/agent/structs"
)
// CacheCompiledDiscoveryChain satisfies the proxycfg.CompiledDiscoveryChain
// interface by sourcing data from the agent cache.
func CacheCompiledDiscoveryChain ( c * cache . Cache ) proxycfg . CompiledDiscoveryChain {
return & cacheProxyDataSource [ * structs . DiscoveryChainRequest ] { c , cachetype . CompiledDiscoveryChainName }
}
// ServerCompiledDiscoveryChain satisfies the proxycfg.CompiledDiscoveryChain
// interface by sourcing data from a blocking query against the server's state
// store.
//
// Requests for services in remote datacenters will be delegated to the given
// remoteSource (i.e. CacheCompiledDiscoveryChain).
func ServerCompiledDiscoveryChain ( deps ServerDataSourceDeps , remoteSource proxycfg . CompiledDiscoveryChain ) proxycfg . CompiledDiscoveryChain {
return & serverCompiledDiscoveryChain { deps , remoteSource }
}
type serverCompiledDiscoveryChain struct {
deps ServerDataSourceDeps
remoteSource proxycfg . CompiledDiscoveryChain
}
func ( s serverCompiledDiscoveryChain ) Notify ( ctx context . Context , req * structs . DiscoveryChainRequest , correlationID string , ch chan <- proxycfg . UpdateEvent ) error {
if req . Datacenter != s . deps . Datacenter {
return s . remoteSource . Notify ( ctx , req , correlationID , ch )
}
entMeta := req . GetEnterpriseMeta ( )
evalDC := req . EvaluateInDatacenter
if evalDC == "" {
evalDC = s . deps . Datacenter
}
compileReq := discoverychain . CompileRequest {
ServiceName : req . Name ,
EvaluateInNamespace : entMeta . NamespaceOrDefault ( ) ,
EvaluateInPartition : entMeta . PartitionOrDefault ( ) ,
EvaluateInDatacenter : evalDC ,
OverrideMeshGateway : req . OverrideMeshGateway ,
OverrideProtocol : req . OverrideProtocol ,
OverrideConnectTimeout : req . OverrideConnectTimeout ,
}
return watch . ServerLocalNotify ( ctx , correlationID , s . deps . GetStore ,
func ( ws memdb . WatchSet , store Store ) ( uint64 , * structs . DiscoveryChainResponse , error ) {
var authzContext acl . AuthorizerContext
authz , err := s . deps . ACLResolver . ResolveTokenAndDefaultMeta ( req . Token , req . GetEnterpriseMeta ( ) , & authzContext )
if err != nil {
return 0 , nil , err
}
if err := authz . ToAllowAuthorizer ( ) . ServiceReadAllowed ( req . Name , & authzContext ) ; err != nil {
// TODO(agentless): the agent cache handles acl.IsErrNotFound specially to
// prevent endlessly retrying if an ACL token is deleted. We should probably
// do this in watch.ServerLocalNotify too.
return 0 , nil , err
}
index , chain , entries , err := store . ServiceDiscoveryChain ( ws , req . Name , entMeta , compileReq )
if err != nil {
return 0 , nil , err
}
rsp := & structs . DiscoveryChainResponse {
Chain : chain ,
QueryMeta : structs . QueryMeta {
Backend : structs . QueryBackendBlocking ,
Index : index ,
} ,
}
// TODO(boxofrad): Check with @mkeeler that this is the correct thing to do.
if entries . IsEmpty ( ) {
return index , rsp , watch . ErrorNotFound
}
return index , rsp , nil
} ,
dispatchBlockingQueryUpdate [ * structs . DiscoveryChainResponse ] ( ch ) ,
)
}