2024-01-10 11:19:20 -05:00
|
|
|
// Copyright (c) HashiCorp, Inc.
|
|
|
|
// SPDX-License-Identifier: BUSL-1.1
|
|
|
|
|
|
|
|
package discovery
|
|
|
|
|
|
|
|
import (
|
2024-01-17 16:46:18 -07:00
|
|
|
"context"
|
2024-01-29 11:40:10 -05:00
|
|
|
"fmt"
|
2024-01-10 11:19:20 -05:00
|
|
|
"net"
|
|
|
|
"sync/atomic"
|
|
|
|
"time"
|
|
|
|
|
2024-01-17 16:46:18 -07:00
|
|
|
"github.com/hashicorp/go-hclog"
|
|
|
|
|
2024-01-29 11:40:10 -05:00
|
|
|
"github.com/hashicorp/consul/acl"
|
2024-01-10 11:19:20 -05:00
|
|
|
"github.com/hashicorp/consul/agent/config"
|
2024-01-17 16:46:18 -07:00
|
|
|
"github.com/hashicorp/consul/agent/structs"
|
2024-01-10 11:19:20 -05:00
|
|
|
)
|
|
|
|
|
|
|
|
const (
|
|
|
|
// TODO (v2-dns): can we move the recursion into the data fetcher?
|
|
|
|
maxRecursionLevelDefault = 3 // This field comes from the V1 DNS server and affects V1 catalog lookups
|
|
|
|
maxRecurseRecords = 5
|
|
|
|
)
|
|
|
|
|
2024-01-17 16:46:18 -07:00
|
|
|
// v1DataFetcherDynamicConfig is used to store the dynamic configuration of the V1 data fetcher.
|
2024-01-10 11:19:20 -05:00
|
|
|
type v1DataFetcherDynamicConfig struct {
|
2024-01-29 11:40:10 -05:00
|
|
|
// Default request tenancy
|
|
|
|
datacenter string
|
|
|
|
|
|
|
|
// Catalog configuration
|
2024-01-10 11:19:20 -05:00
|
|
|
allowStale bool
|
|
|
|
maxStale time.Duration
|
|
|
|
useCache bool
|
|
|
|
cacheMaxAge time.Duration
|
|
|
|
onlyPassing bool
|
|
|
|
}
|
|
|
|
|
2024-01-17 16:46:18 -07:00
|
|
|
// V1DataFetcher is used to fetch data from the V1 catalog.
|
2024-01-10 11:19:20 -05:00
|
|
|
type V1DataFetcher struct {
|
2024-01-29 11:40:10 -05:00
|
|
|
defaultEnterpriseMeta acl.EnterpriseMeta
|
|
|
|
dynamicConfig atomic.Value
|
|
|
|
logger hclog.Logger
|
2024-01-17 16:46:18 -07:00
|
|
|
|
|
|
|
rpcFunc func(ctx context.Context, method string, args interface{}, reply interface{}) error
|
2024-01-10 11:19:20 -05:00
|
|
|
}
|
|
|
|
|
2024-01-17 16:46:18 -07:00
|
|
|
// NewV1DataFetcher creates a new V1 data fetcher.
|
|
|
|
func NewV1DataFetcher(config *config.RuntimeConfig,
|
2024-01-29 11:40:10 -05:00
|
|
|
entMeta *acl.EnterpriseMeta,
|
2024-01-17 16:46:18 -07:00
|
|
|
rpcFunc func(ctx context.Context, method string, args interface{}, reply interface{}) error,
|
|
|
|
logger hclog.Logger) *V1DataFetcher {
|
|
|
|
f := &V1DataFetcher{
|
2024-01-29 11:40:10 -05:00
|
|
|
defaultEnterpriseMeta: *entMeta,
|
|
|
|
rpcFunc: rpcFunc,
|
|
|
|
logger: logger,
|
2024-01-17 16:46:18 -07:00
|
|
|
}
|
2024-01-10 11:19:20 -05:00
|
|
|
f.LoadConfig(config)
|
|
|
|
return f
|
|
|
|
}
|
|
|
|
|
2024-01-17 16:46:18 -07:00
|
|
|
// LoadConfig loads the configuration for the V1 data fetcher.
|
2024-01-10 11:19:20 -05:00
|
|
|
func (f *V1DataFetcher) LoadConfig(config *config.RuntimeConfig) {
|
|
|
|
dynamicConfig := &v1DataFetcherDynamicConfig{
|
2024-01-29 11:40:10 -05:00
|
|
|
datacenter: config.Datacenter,
|
2024-01-10 11:19:20 -05:00
|
|
|
allowStale: config.DNSAllowStale,
|
|
|
|
maxStale: config.DNSMaxStale,
|
|
|
|
useCache: config.DNSUseCache,
|
|
|
|
cacheMaxAge: config.DNSCacheMaxAge,
|
|
|
|
onlyPassing: config.DNSOnlyPassing,
|
|
|
|
}
|
|
|
|
f.dynamicConfig.Store(dynamicConfig)
|
|
|
|
}
|
|
|
|
|
|
|
|
// TODO (v2-dns): Implementation of the V1 data fetcher
|
|
|
|
|
2024-01-17 16:46:18 -07:00
|
|
|
// FetchNodes fetches A/AAAA/CNAME
|
2024-01-10 11:19:20 -05:00
|
|
|
func (f *V1DataFetcher) FetchNodes(ctx Context, req *QueryPayload) ([]*Result, error) {
|
|
|
|
return nil, nil
|
|
|
|
}
|
|
|
|
|
2024-01-17 16:46:18 -07:00
|
|
|
// FetchEndpoints fetches records for A/AAAA/CNAME or SRV requests for services
|
2024-01-10 11:19:20 -05:00
|
|
|
func (f *V1DataFetcher) FetchEndpoints(ctx Context, req *QueryPayload, lookupType LookupType) ([]*Result, error) {
|
|
|
|
return nil, nil
|
|
|
|
}
|
|
|
|
|
2024-01-17 16:46:18 -07:00
|
|
|
// FetchVirtualIP fetches A/AAAA records for virtual IPs
|
2024-01-10 11:19:20 -05:00
|
|
|
func (f *V1DataFetcher) FetchVirtualIP(ctx Context, req *QueryPayload) (*Result, error) {
|
2024-01-17 16:46:18 -07:00
|
|
|
args := structs.ServiceSpecificRequest{
|
|
|
|
// The datacenter of the request is not specified because cross-datacenter virtual IP
|
|
|
|
// queries are not supported. This guard rail is in place because virtual IPs are allocated
|
|
|
|
// within a DC, therefore their uniqueness is not guaranteed globally.
|
|
|
|
PeerName: req.Tenancy.Peer,
|
|
|
|
ServiceName: req.Name,
|
|
|
|
EnterpriseMeta: req.Tenancy.EnterpriseMeta,
|
|
|
|
QueryOptions: structs.QueryOptions{
|
|
|
|
Token: ctx.Token,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
var out string
|
|
|
|
if err := f.rpcFunc(context.Background(), "Catalog.VirtualIPForService", &args, &out); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
result := &Result{
|
|
|
|
Address: out,
|
|
|
|
Type: ResultTypeVirtual,
|
|
|
|
}
|
|
|
|
return result, nil
|
2024-01-10 11:19:20 -05:00
|
|
|
}
|
|
|
|
|
2024-01-17 16:46:18 -07:00
|
|
|
// FetchRecordsByIp is used for PTR requests to look up a service/node from an IP.
|
2024-01-29 11:40:10 -05:00
|
|
|
// The search is performed in the agent's partition and over all namespaces (or those allowed by the ACL token).
|
|
|
|
func (f *V1DataFetcher) FetchRecordsByIp(reqCtx Context, ip net.IP) ([]*Result, error) {
|
|
|
|
configCtx := f.dynamicConfig.Load().(*v1DataFetcherDynamicConfig)
|
|
|
|
targetIP := ip.String()
|
|
|
|
|
|
|
|
var results []*Result
|
|
|
|
|
|
|
|
args := structs.DCSpecificRequest{
|
|
|
|
Datacenter: configCtx.datacenter,
|
|
|
|
QueryOptions: structs.QueryOptions{
|
|
|
|
Token: reqCtx.Token,
|
|
|
|
AllowStale: configCtx.allowStale,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
var out structs.IndexedNodes
|
|
|
|
|
|
|
|
// TODO: Replace ListNodes with an internal RPC that can do the filter
|
|
|
|
// server side to avoid transferring the entire node list.
|
|
|
|
if err := f.rpcFunc(context.Background(), "Catalog.ListNodes", &args, &out); err == nil {
|
|
|
|
for _, n := range out.Nodes {
|
|
|
|
if targetIP == n.Address {
|
|
|
|
results = append(results, &Result{
|
|
|
|
Address: n.Address,
|
|
|
|
Type: ResultTypeNode,
|
|
|
|
Target: n.Node,
|
|
|
|
Tenancy: ResultTenancy{
|
|
|
|
EnterpriseMeta: f.defaultEnterpriseMeta,
|
|
|
|
Datacenter: configCtx.datacenter,
|
|
|
|
},
|
|
|
|
})
|
|
|
|
return results, nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// only look into the services if we didn't find a node
|
|
|
|
sargs := structs.ServiceSpecificRequest{
|
|
|
|
Datacenter: configCtx.datacenter,
|
|
|
|
QueryOptions: structs.QueryOptions{
|
|
|
|
Token: reqCtx.Token,
|
|
|
|
AllowStale: configCtx.allowStale,
|
|
|
|
},
|
|
|
|
ServiceAddress: targetIP,
|
|
|
|
EnterpriseMeta: *f.defaultEnterpriseMeta.WithWildcardNamespace(),
|
|
|
|
}
|
|
|
|
|
|
|
|
var sout structs.IndexedServiceNodes
|
|
|
|
if err := f.rpcFunc(context.Background(), "Catalog.ServiceNodes", &sargs, &sout); err == nil {
|
|
|
|
for _, n := range sout.ServiceNodes {
|
|
|
|
if n.ServiceAddress == targetIP {
|
|
|
|
results = append(results, &Result{
|
|
|
|
Address: n.ServiceAddress,
|
|
|
|
Type: ResultTypeService,
|
|
|
|
Target: n.ServiceName,
|
|
|
|
Tenancy: ResultTenancy{
|
|
|
|
EnterpriseMeta: f.defaultEnterpriseMeta,
|
|
|
|
Datacenter: configCtx.datacenter,
|
|
|
|
},
|
|
|
|
})
|
|
|
|
return results, nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// nothing found locally, recurse
|
|
|
|
// TODO: (v2-dns) implement recursion
|
|
|
|
//d.handleRecurse(resp, req)
|
|
|
|
|
|
|
|
return nil, fmt.Errorf("unhandled error in FetchRecordsByIp")
|
2024-01-10 11:19:20 -05:00
|
|
|
}
|
|
|
|
|
2024-01-17 16:46:18 -07:00
|
|
|
// FetchWorkload fetches a single Result associated with
|
|
|
|
// V2 Workload. V2-only.
|
2024-01-10 11:19:20 -05:00
|
|
|
func (f *V1DataFetcher) FetchWorkload(ctx Context, req *QueryPayload) (*Result, error) {
|
2024-01-29 11:40:10 -05:00
|
|
|
return nil, ErrNotSupported
|
2024-01-10 11:19:20 -05:00
|
|
|
}
|
|
|
|
|
2024-01-17 16:46:18 -07:00
|
|
|
// FetchPreparedQuery evaluates the results of a prepared query.
|
|
|
|
// deprecated in V2
|
2024-01-10 11:19:20 -05:00
|
|
|
func (f *V1DataFetcher) FetchPreparedQuery(ctx Context, req *QueryPayload) ([]*Result, error) {
|
|
|
|
return nil, nil
|
|
|
|
}
|