mirror of https://github.com/hashicorp/consul
Updates to Config Entries and Connect for Namespaces (#7116)
parent
47ac0bcd01
commit
c09693e545
|
@ -4024,6 +4024,14 @@ func (a *Agent) registerCache() {
|
||||||
RefreshTimeout: 10 * time.Minute,
|
RefreshTimeout: 10 * time.Minute,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
a.cache.RegisterType(cachetype.CatalogServiceListName, &cachetype.CatalogServiceList{
|
||||||
|
RPC: a,
|
||||||
|
}, &cache.RegisterOptions{
|
||||||
|
Refresh: true,
|
||||||
|
RefreshTimer: 0 * time.Second,
|
||||||
|
RefreshTimeout: 10 * time.Minute,
|
||||||
|
})
|
||||||
|
|
||||||
a.cache.RegisterType(cachetype.CatalogDatacentersName, &cachetype.CatalogDatacenters{
|
a.cache.RegisterType(cachetype.CatalogDatacentersName, &cachetype.CatalogDatacenters{
|
||||||
RPC: a,
|
RPC: a,
|
||||||
}, &cache.RegisterOptions{
|
}, &cache.RegisterOptions{
|
||||||
|
|
|
@ -292,7 +292,7 @@ func (s *HTTPServer) AgentService(resp http.ResponseWriter, req *http.Request) (
|
||||||
svcState := s.agent.State.ServiceState(sid)
|
svcState := s.agent.State.ServiceState(sid)
|
||||||
if svcState == nil {
|
if svcState == nil {
|
||||||
resp.WriteHeader(http.StatusNotFound)
|
resp.WriteHeader(http.StatusNotFound)
|
||||||
fmt.Fprintf(resp, "unknown service ID: %s", id)
|
fmt.Fprintf(resp, "unknown service ID: %s", sid.String())
|
||||||
return "", nil, nil
|
return "", nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,55 @@
|
||||||
|
package cachetype
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/hashicorp/consul/agent/cache"
|
||||||
|
"github.com/hashicorp/consul/agent/structs"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Recommended name for registration.
|
||||||
|
const CatalogServiceListName = "catalog-services-list"
|
||||||
|
|
||||||
|
// CatalogServiceList supports fetching service names via the catalog.
|
||||||
|
type CatalogServiceList struct {
|
||||||
|
RPC RPC
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *CatalogServiceList) Fetch(opts cache.FetchOptions, req cache.Request) (cache.FetchResult, error) {
|
||||||
|
var result cache.FetchResult
|
||||||
|
|
||||||
|
// The request should be a DCSpecificRequest.
|
||||||
|
reqReal, ok := req.(*structs.DCSpecificRequest)
|
||||||
|
if !ok {
|
||||||
|
return result, fmt.Errorf(
|
||||||
|
"Internal cache failure: request wrong type: %T", req)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Lightweight copy this object so that manipulating QueryOptions doesn't race.
|
||||||
|
dup := *reqReal
|
||||||
|
reqReal = &dup
|
||||||
|
|
||||||
|
// Set the minimum query index to our current index so we block
|
||||||
|
reqReal.QueryOptions.MinQueryIndex = opts.MinIndex
|
||||||
|
reqReal.QueryOptions.MaxQueryTime = opts.Timeout
|
||||||
|
|
||||||
|
// Always allow stale - there's no point in hitting leader if the request is
|
||||||
|
// going to be served from cache and end up arbitrarily stale anyway. This
|
||||||
|
// allows cached service-discover to automatically read scale across all
|
||||||
|
// servers too.
|
||||||
|
reqReal.AllowStale = true
|
||||||
|
|
||||||
|
// Fetch
|
||||||
|
var reply structs.IndexedServiceList
|
||||||
|
if err := c.RPC.RPC("Catalog.ServiceList", reqReal, &reply); err != nil {
|
||||||
|
return result, err
|
||||||
|
}
|
||||||
|
|
||||||
|
result.Value = &reply
|
||||||
|
result.Index = reply.QueryMeta.Index
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *CatalogServiceList) SupportsBlocking() bool {
|
||||||
|
return true
|
||||||
|
}
|
|
@ -0,0 +1,66 @@
|
||||||
|
package cachetype
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/hashicorp/consul/agent/cache"
|
||||||
|
"github.com/hashicorp/consul/agent/structs"
|
||||||
|
"github.com/stretchr/testify/mock"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestCatalogServiceList(t *testing.T) {
|
||||||
|
rpc := TestRPC(t)
|
||||||
|
typ := &CatalogServiceList{RPC: rpc}
|
||||||
|
|
||||||
|
// Expect the proper RPC call. This also sets the expected value
|
||||||
|
// since that is return-by-pointer in the arguments.
|
||||||
|
var resp *structs.IndexedServiceList
|
||||||
|
rpc.On("RPC", "Catalog.ServiceList", mock.Anything, mock.Anything).Return(nil).
|
||||||
|
Run(func(args mock.Arguments) {
|
||||||
|
req := args.Get(1).(*structs.DCSpecificRequest)
|
||||||
|
require.Equal(t, uint64(24), req.QueryOptions.MinQueryIndex)
|
||||||
|
require.Equal(t, 1*time.Second, req.QueryOptions.MaxQueryTime)
|
||||||
|
require.True(t, req.AllowStale)
|
||||||
|
|
||||||
|
reply := args.Get(2).(*structs.IndexedServiceList)
|
||||||
|
reply.Services = structs.ServiceList{
|
||||||
|
structs.ServiceInfo{
|
||||||
|
Name: "foo",
|
||||||
|
},
|
||||||
|
structs.ServiceInfo{
|
||||||
|
Name: "bar",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
reply.QueryMeta.Index = 48
|
||||||
|
resp = reply
|
||||||
|
})
|
||||||
|
|
||||||
|
// Fetch
|
||||||
|
resultA, err := typ.Fetch(cache.FetchOptions{
|
||||||
|
MinIndex: 24,
|
||||||
|
Timeout: 1 * time.Second,
|
||||||
|
}, &structs.DCSpecificRequest{
|
||||||
|
Datacenter: "dc1",
|
||||||
|
})
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, cache.FetchResult{
|
||||||
|
Value: resp,
|
||||||
|
Index: 48,
|
||||||
|
}, resultA)
|
||||||
|
|
||||||
|
rpc.AssertExpectations(t)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCatalogServiceList_badReqType(t *testing.T) {
|
||||||
|
rpc := TestRPC(t)
|
||||||
|
typ := &CatalogServiceList{RPC: rpc}
|
||||||
|
|
||||||
|
// Fetch
|
||||||
|
_, err := typ.Fetch(cache.FetchOptions{}, cache.TestRequest(
|
||||||
|
t, cache.RequestInfo{Key: "foo", MinIndex: 64}))
|
||||||
|
require.Error(t, err)
|
||||||
|
require.Contains(t, err.Error(), "wrong type")
|
||||||
|
rpc.AssertExpectations(t)
|
||||||
|
}
|
|
@ -515,17 +515,17 @@ func (c *ConnectCALeaf) generateNewLeaf(req *ConnectCALeafRequest,
|
||||||
id = &connect.SpiffeIDService{
|
id = &connect.SpiffeIDService{
|
||||||
Host: roots.TrustDomain,
|
Host: roots.TrustDomain,
|
||||||
Datacenter: req.Datacenter,
|
Datacenter: req.Datacenter,
|
||||||
Namespace: "default",
|
Namespace: req.TargetNamespace(),
|
||||||
Service: req.Service,
|
Service: req.Service,
|
||||||
}
|
}
|
||||||
commonName = connect.ServiceCN(req.Service, roots.TrustDomain)
|
commonName = connect.ServiceCN(req.Service, req.TargetNamespace(), roots.TrustDomain)
|
||||||
} else if req.Agent != "" {
|
} else if req.Agent != "" {
|
||||||
id = &connect.SpiffeIDAgent{
|
id = &connect.SpiffeIDAgent{
|
||||||
Host: roots.TrustDomain,
|
Host: roots.TrustDomain,
|
||||||
Datacenter: req.Datacenter,
|
Datacenter: req.Datacenter,
|
||||||
Agent: req.Agent,
|
Agent: req.Agent,
|
||||||
}
|
}
|
||||||
commonName = connect.ServiceCN(req.Agent, roots.TrustDomain)
|
commonName = connect.AgentCN(req.Agent, roots.TrustDomain)
|
||||||
dnsNames = append([]string{"localhost"}, req.DNSSAN...)
|
dnsNames = append([]string{"localhost"}, req.DNSSAN...)
|
||||||
ipAddresses = append([]net.IP{net.ParseIP("127.0.0.1"), net.ParseIP("::")}, req.IPSAN...)
|
ipAddresses = append([]net.IP{net.ParseIP("127.0.0.1"), net.ParseIP("::")}, req.IPSAN...)
|
||||||
} else {
|
} else {
|
||||||
|
@ -645,6 +645,8 @@ type ConnectCALeafRequest struct {
|
||||||
IPSAN []net.IP
|
IPSAN []net.IP
|
||||||
MinQueryIndex uint64
|
MinQueryIndex uint64
|
||||||
MaxQueryTime time.Duration
|
MaxQueryTime time.Duration
|
||||||
|
|
||||||
|
structs.EnterpriseMeta
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *ConnectCALeafRequest) Key() string {
|
func (r *ConnectCALeafRequest) Key() string {
|
||||||
|
|
|
@ -0,0 +1,7 @@
|
||||||
|
// +build !consulent
|
||||||
|
|
||||||
|
package cachetype
|
||||||
|
|
||||||
|
func (req *ConnectCALeafRequest) TargetNamespace() string {
|
||||||
|
return "default"
|
||||||
|
}
|
|
@ -2,6 +2,7 @@ package cachetype
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"strconv"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/hashicorp/consul/agent/cache"
|
"github.com/hashicorp/consul/agent/cache"
|
||||||
|
@ -55,8 +56,8 @@ func (c *ServiceHTTPChecks) Fetch(opts cache.FetchOptions, req cache.Request) (c
|
||||||
|
|
||||||
hash, resp, err := c.Agent.LocalBlockingQuery(true, lastHash, reqReal.MaxQueryTime,
|
hash, resp, err := c.Agent.LocalBlockingQuery(true, lastHash, reqReal.MaxQueryTime,
|
||||||
func(ws memdb.WatchSet) (string, interface{}, error) {
|
func(ws memdb.WatchSet) (string, interface{}, error) {
|
||||||
// TODO (namespaces) update with the real ent meta once thats plumbed through
|
sid := structs.NewServiceID(reqReal.ServiceID, &reqReal.EnterpriseMeta)
|
||||||
svcState := c.Agent.LocalState().ServiceState(structs.NewServiceID(reqReal.ServiceID, nil))
|
svcState := c.Agent.LocalState().ServiceState(sid)
|
||||||
if svcState == nil {
|
if svcState == nil {
|
||||||
return "", result, fmt.Errorf("Internal cache failure: service '%s' not in agent state", reqReal.ServiceID)
|
return "", result, fmt.Errorf("Internal cache failure: service '%s' not in agent state", reqReal.ServiceID)
|
||||||
}
|
}
|
||||||
|
@ -64,8 +65,7 @@ func (c *ServiceHTTPChecks) Fetch(opts cache.FetchOptions, req cache.Request) (c
|
||||||
// WatchCh will receive updates on service (de)registrations and check (de)registrations
|
// WatchCh will receive updates on service (de)registrations and check (de)registrations
|
||||||
ws.Add(svcState.WatchCh)
|
ws.Add(svcState.WatchCh)
|
||||||
|
|
||||||
// TODO (namespaces) update with a real entMeta
|
reply := c.Agent.ServiceHTTPBasedChecks(sid)
|
||||||
reply := c.Agent.ServiceHTTPBasedChecks(structs.NewServiceID(reqReal.ServiceID, nil))
|
|
||||||
|
|
||||||
hash, err := hashChecks(reply)
|
hash, err := hashChecks(reply)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -103,16 +103,28 @@ type ServiceHTTPChecksRequest struct {
|
||||||
ServiceID string
|
ServiceID string
|
||||||
MinQueryIndex uint64
|
MinQueryIndex uint64
|
||||||
MaxQueryTime time.Duration
|
MaxQueryTime time.Duration
|
||||||
|
structs.EnterpriseMeta
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *ServiceHTTPChecksRequest) CacheInfo() cache.RequestInfo {
|
func (s *ServiceHTTPChecksRequest) CacheInfo() cache.RequestInfo {
|
||||||
return cache.RequestInfo{
|
info := cache.RequestInfo{
|
||||||
Token: "",
|
Token: "",
|
||||||
Key: ServiceHTTPChecksName + ":" + s.ServiceID,
|
|
||||||
Datacenter: "",
|
Datacenter: "",
|
||||||
MinIndex: s.MinQueryIndex,
|
MinIndex: s.MinQueryIndex,
|
||||||
Timeout: s.MaxQueryTime,
|
Timeout: s.MaxQueryTime,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
v, err := hashstructure.Hash([]interface{}{
|
||||||
|
s.ServiceID,
|
||||||
|
s.EnterpriseMeta,
|
||||||
|
}, nil)
|
||||||
|
if err == nil {
|
||||||
|
// If there is an error, we don't set the key. A blank key forces
|
||||||
|
// no cache for this request.
|
||||||
|
info.Key = strconv.FormatUint(v, 10)
|
||||||
|
}
|
||||||
|
|
||||||
|
return info
|
||||||
}
|
}
|
||||||
|
|
||||||
func hashChecks(checks []structs.CheckType) (string, error) {
|
func hashChecks(checks []structs.CheckType) (string, error) {
|
||||||
|
|
|
@ -32,6 +32,14 @@ func (s *HTTPServer) configGet(resp http.ResponseWriter, req *http.Request) (int
|
||||||
}
|
}
|
||||||
pathArgs := strings.SplitN(strings.TrimPrefix(req.URL.Path, "/v1/config/"), "/", 2)
|
pathArgs := strings.SplitN(strings.TrimPrefix(req.URL.Path, "/v1/config/"), "/", 2)
|
||||||
|
|
||||||
|
if len(pathArgs) == 2 {
|
||||||
|
if err := s.parseEntMetaNoWildcard(req, &args.EnterpriseMeta); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
} else if err := s.parseEntMeta(req, &args.EnterpriseMeta); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
switch len(pathArgs) {
|
switch len(pathArgs) {
|
||||||
case 2:
|
case 2:
|
||||||
// Both kind/name provided.
|
// Both kind/name provided.
|
||||||
|
@ -85,6 +93,11 @@ func (s *HTTPServer) configDelete(resp http.ResponseWriter, req *http.Request) (
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
args.Entry = entry
|
args.Entry = entry
|
||||||
|
// Parse enterprise meta.
|
||||||
|
meta := args.Entry.GetEnterpriseMeta()
|
||||||
|
if err := s.parseEntMetaNoWildcard(req, meta); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
var reply struct{}
|
var reply struct{}
|
||||||
if err := s.agent.RPC("ConfigEntry.Delete", &args, &reply); err != nil {
|
if err := s.agent.RPC("ConfigEntry.Delete", &args, &reply); err != nil {
|
||||||
|
@ -113,6 +126,13 @@ func (s *HTTPServer) ConfigApply(resp http.ResponseWriter, req *http.Request) (i
|
||||||
return nil, BadRequestError{Reason: fmt.Sprintf("Request decoding failed: %v", err)}
|
return nil, BadRequestError{Reason: fmt.Sprintf("Request decoding failed: %v", err)}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Parse enterprise meta.
|
||||||
|
var meta structs.EnterpriseMeta
|
||||||
|
if err := s.parseEntMetaNoWildcard(req, &meta); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
args.Entry.GetEnterpriseMeta().Merge(&meta)
|
||||||
|
|
||||||
// Check for cas value
|
// Check for cas value
|
||||||
if casStr := req.URL.Query().Get("cas"); casStr != "" {
|
if casStr := req.URL.Query().Get("cas"); casStr != "" {
|
||||||
casVal, err := strconv.ParseUint(casStr, 10, 64)
|
casVal, err := strconv.ParseUint(casStr, 10, 64)
|
||||||
|
|
|
@ -174,7 +174,7 @@ func TestConsulCAProvider_SignLeaf(t *testing.T) {
|
||||||
parsed, err := connect.ParseCert(cert)
|
parsed, err := connect.ParseCert(cert)
|
||||||
require.NoError(err)
|
require.NoError(err)
|
||||||
require.Equal(spiffeService.URI(), parsed.URIs[0])
|
require.Equal(spiffeService.URI(), parsed.URIs[0])
|
||||||
require.Equal(connect.ServiceCN("foo", connect.TestClusterID), parsed.Subject.CommonName)
|
require.Equal(connect.ServiceCN("foo", "default", connect.TestClusterID), parsed.Subject.CommonName)
|
||||||
require.Equal(uint64(2), parsed.SerialNumber.Uint64())
|
require.Equal(uint64(2), parsed.SerialNumber.Uint64())
|
||||||
subjectKeyID, err := connect.KeyId(csr.PublicKey)
|
subjectKeyID, err := connect.KeyId(csr.PublicKey)
|
||||||
require.NoError(err)
|
require.NoError(err)
|
||||||
|
@ -203,7 +203,7 @@ func TestConsulCAProvider_SignLeaf(t *testing.T) {
|
||||||
parsed, err := connect.ParseCert(cert)
|
parsed, err := connect.ParseCert(cert)
|
||||||
require.NoError(err)
|
require.NoError(err)
|
||||||
require.Equal(spiffeService.URI(), parsed.URIs[0])
|
require.Equal(spiffeService.URI(), parsed.URIs[0])
|
||||||
require.Equal(connect.ServiceCN("bar", connect.TestClusterID), parsed.Subject.CommonName)
|
require.Equal(connect.ServiceCN("bar", "default", connect.TestClusterID), parsed.Subject.CommonName)
|
||||||
require.Equal(parsed.SerialNumber.Uint64(), uint64(2))
|
require.Equal(parsed.SerialNumber.Uint64(), uint64(2))
|
||||||
requireNotEncoded(t, parsed.SubjectKeyId)
|
requireNotEncoded(t, parsed.SubjectKeyId)
|
||||||
requireNotEncoded(t, parsed.AuthorityKeyId)
|
requireNotEncoded(t, parsed.AuthorityKeyId)
|
||||||
|
|
|
@ -11,6 +11,56 @@ import (
|
||||||
|
|
||||||
var invalidDNSNameChars = regexp.MustCompile(`[^a-z0-9]`)
|
var invalidDNSNameChars = regexp.MustCompile(`[^a-z0-9]`)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// 64 = max length of a certificate common name
|
||||||
|
// 21 = 7 bytes for ".consul", 9 bytes for .<trust domain> and 5 bytes for ".svc."
|
||||||
|
// This ends up being 43 bytes
|
||||||
|
maxServiceAndNamespaceLen = 64 - 21
|
||||||
|
minServiceNameLen = 15
|
||||||
|
minNamespaceNameLen = 15
|
||||||
|
)
|
||||||
|
|
||||||
|
// trucateServiceAndNamespace will take a service name and namespace name and truncate
|
||||||
|
// them appropriately so that they would fit within the space alloted for them in the
|
||||||
|
// Common Name field of the x509 certificate. That field is capped at 64 characters
|
||||||
|
// in length and there is other data that must be a part of the name too. This function
|
||||||
|
// takes all of that into account.
|
||||||
|
func truncateServiceAndNamespace(serviceName, namespace string) (string, string) {
|
||||||
|
svcLen := len(serviceName)
|
||||||
|
nsLen := len(namespace)
|
||||||
|
totalLen := svcLen + nsLen
|
||||||
|
|
||||||
|
// quick exit when the entirety of both can fit
|
||||||
|
if totalLen <= maxServiceAndNamespaceLen {
|
||||||
|
return serviceName, namespace
|
||||||
|
}
|
||||||
|
|
||||||
|
toRemove := totalLen - maxServiceAndNamespaceLen
|
||||||
|
// now we must figure out how to truncate each one, we need to ensure we don't remove all of either one.
|
||||||
|
if svcLen <= minServiceNameLen {
|
||||||
|
// only remove bytes from the namespace
|
||||||
|
return serviceName, truncateTo(namespace, nsLen-toRemove)
|
||||||
|
} else if nsLen <= minNamespaceNameLen {
|
||||||
|
// only remove bytes from the service name
|
||||||
|
return truncateTo(serviceName, svcLen-toRemove), namespace
|
||||||
|
} else {
|
||||||
|
// we can remove an "equal" amount from each. If the number of bytes to remove is odd we give it to the namespace
|
||||||
|
svcTruncate := svcLen - (toRemove / 2) - (toRemove % 2)
|
||||||
|
nsTruncate := nsLen - (toRemove / 2)
|
||||||
|
|
||||||
|
// checks to ensure we don't reduce one side too much when they are not roughly balanced in length.
|
||||||
|
if svcTruncate <= minServiceNameLen {
|
||||||
|
svcTruncate = minServiceNameLen
|
||||||
|
nsTruncate = maxServiceAndNamespaceLen - minServiceNameLen
|
||||||
|
} else if nsTruncate <= minNamespaceNameLen {
|
||||||
|
svcTruncate = maxServiceAndNamespaceLen - minNamespaceNameLen
|
||||||
|
nsTruncate = minNamespaceNameLen
|
||||||
|
}
|
||||||
|
|
||||||
|
return truncateTo(serviceName, svcTruncate), truncateTo(namespace, nsTruncate)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// ServiceCN returns the common name for a service's certificate. We can't use
|
// ServiceCN returns the common name for a service's certificate. We can't use
|
||||||
// SPIFFE URIs because some CAs require valid FQDN format. We can't use SNI
|
// SPIFFE URIs because some CAs require valid FQDN format. We can't use SNI
|
||||||
// values because they are often too long than the 64 bytes allowed by
|
// values because they are often too long than the 64 bytes allowed by
|
||||||
|
@ -31,11 +81,12 @@ var invalidDNSNameChars = regexp.MustCompile(`[^a-z0-9]`)
|
||||||
// total at 64.
|
// total at 64.
|
||||||
//
|
//
|
||||||
// trust domain is truncated to keep the whole name short
|
// trust domain is truncated to keep the whole name short
|
||||||
func ServiceCN(serviceName, trustDomain string) string {
|
func ServiceCN(serviceName, namespace, trustDomain string) string {
|
||||||
svc := invalidDNSNameChars.ReplaceAllString(strings.ToLower(serviceName), "")
|
svc := invalidDNSNameChars.ReplaceAllString(strings.ToLower(serviceName), "")
|
||||||
// 20 = 7 bytes for ".consul", 8 bytes for trust domain, 5 bytes for ".svc."
|
|
||||||
return fmt.Sprintf("%s.svc.%s.consul",
|
svc, namespace = truncateServiceAndNamespace(svc, namespace)
|
||||||
truncateTo(svc, 64-20), truncateTo(trustDomain, 8))
|
return fmt.Sprintf("%s.svc.%s.%s.consul",
|
||||||
|
svc, namespace, truncateTo(trustDomain, 8))
|
||||||
}
|
}
|
||||||
|
|
||||||
// AgentCN returns the common name for an agent certificate. See ServiceCN for
|
// AgentCN returns the common name for an agent certificate. See ServiceCN for
|
||||||
|
@ -122,7 +173,7 @@ func CNForCertURI(uri CertURI) (string, error) {
|
||||||
// didn't include the Common Name in the CSR.
|
// didn't include the Common Name in the CSR.
|
||||||
switch id := uri.(type) {
|
switch id := uri.(type) {
|
||||||
case *SpiffeIDService:
|
case *SpiffeIDService:
|
||||||
return ServiceCN(id.Service, id.Host), nil
|
return ServiceCN(id.Service, id.Namespace, id.Host), nil
|
||||||
case *SpiffeIDAgent:
|
case *SpiffeIDAgent:
|
||||||
return AgentCN(id.Agent, id.Host), nil
|
return AgentCN(id.Agent, id.Host), nil
|
||||||
case *SpiffeIDSigning:
|
case *SpiffeIDSigning:
|
||||||
|
|
|
@ -0,0 +1,115 @@
|
||||||
|
package connect
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestServiceAndNamespaceTruncation(t *testing.T) {
|
||||||
|
type tcase struct {
|
||||||
|
service string
|
||||||
|
namespace string
|
||||||
|
// if left as "" then its expected to not be truncated
|
||||||
|
expectedService string
|
||||||
|
// if left as "" then its expected to not be truncated
|
||||||
|
expectedNamespace string
|
||||||
|
}
|
||||||
|
|
||||||
|
cases := map[string]tcase{
|
||||||
|
"short-no-truncation": tcase{
|
||||||
|
service: "foo",
|
||||||
|
namespace: "bar",
|
||||||
|
},
|
||||||
|
"long-service-no-truncation": tcase{
|
||||||
|
// -3 because thats the length of the namespace
|
||||||
|
service: strings.Repeat("a", maxServiceAndNamespaceLen-3),
|
||||||
|
namespace: "bar",
|
||||||
|
},
|
||||||
|
"long-namespace-no-truncation": tcase{
|
||||||
|
service: "foo",
|
||||||
|
// -3 because thats the length of the service name
|
||||||
|
namespace: strings.Repeat("b", maxServiceAndNamespaceLen-3),
|
||||||
|
},
|
||||||
|
"truncate-service-only": tcase{
|
||||||
|
// this should force the service name to be truncated
|
||||||
|
service: strings.Repeat("a", maxServiceAndNamespaceLen-minNamespaceNameLen+5),
|
||||||
|
expectedService: strings.Repeat("a", maxServiceAndNamespaceLen-minNamespaceNameLen),
|
||||||
|
// this is the maximum length that will never be truncated for a namespace
|
||||||
|
namespace: strings.Repeat("b", minNamespaceNameLen),
|
||||||
|
},
|
||||||
|
"truncate-namespace-only": tcase{
|
||||||
|
// this is the maximum length that will never be truncated for a service name
|
||||||
|
service: strings.Repeat("a", minServiceNameLen),
|
||||||
|
// this should force the namespace name to be truncated
|
||||||
|
namespace: strings.Repeat("b", maxServiceAndNamespaceLen-minServiceNameLen+5),
|
||||||
|
expectedNamespace: strings.Repeat("b", maxServiceAndNamespaceLen-minServiceNameLen),
|
||||||
|
},
|
||||||
|
"truncate-both-even": tcase{
|
||||||
|
// this test would need to be update if the maxServiceAndNamespaceLen variable is updated
|
||||||
|
// I could put some more complex logic into here to prevent that but it would be mostly
|
||||||
|
// duplicating the logic in the function itself and thus not really be testing anything
|
||||||
|
//
|
||||||
|
// The original lengths of 50 / 51 were chose when maxServiceAndNamespaceLen would be 43
|
||||||
|
// Therefore a total of 58 characters must be removed. This was chose so that the value
|
||||||
|
// could be evenly split between the two strings.
|
||||||
|
service: strings.Repeat("a", 50),
|
||||||
|
expectedService: strings.Repeat("a", 21),
|
||||||
|
namespace: strings.Repeat("b", 51),
|
||||||
|
expectedNamespace: strings.Repeat("b", 22),
|
||||||
|
},
|
||||||
|
"truncate-both-odd": tcase{
|
||||||
|
// this test would need to be update if the maxServiceAndNamespaceLen variable is updated
|
||||||
|
// I could put some more complex logic into here to prevent that but it would be mostly
|
||||||
|
// duplicating the logic in the function itself and thus not really be testing anything
|
||||||
|
//
|
||||||
|
// The original lengths of 50 / 57 were chose when maxServiceAndNamespaceLen would be 43
|
||||||
|
// Therefore a total of 57 characters must be removed. This was chose so that the value
|
||||||
|
// could not be evenly removed from both so the namespace should be truncated to a length
|
||||||
|
// 1 character more than the service.
|
||||||
|
service: strings.Repeat("a", 50),
|
||||||
|
expectedService: strings.Repeat("a", 21),
|
||||||
|
namespace: strings.Repeat("b", 50),
|
||||||
|
expectedNamespace: strings.Repeat("b", 22),
|
||||||
|
},
|
||||||
|
"truncate-both-min-svc": tcase{
|
||||||
|
service: strings.Repeat("a", minServiceNameLen+1),
|
||||||
|
expectedService: strings.Repeat("a", minServiceNameLen),
|
||||||
|
namespace: strings.Repeat("b", maxServiceAndNamespaceLen),
|
||||||
|
expectedNamespace: strings.Repeat("b", maxServiceAndNamespaceLen-minServiceNameLen),
|
||||||
|
},
|
||||||
|
"truncate-both-min-ns": tcase{
|
||||||
|
service: strings.Repeat("a", maxServiceAndNamespaceLen),
|
||||||
|
expectedService: strings.Repeat("a", maxServiceAndNamespaceLen-minNamespaceNameLen),
|
||||||
|
namespace: strings.Repeat("b", minNamespaceNameLen+1),
|
||||||
|
expectedNamespace: strings.Repeat("b", minNamespaceNameLen),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for name, tc := range cases {
|
||||||
|
t.Run(name, func(t *testing.T) {
|
||||||
|
actualSvc, actualNamespace := truncateServiceAndNamespace(tc.service, tc.namespace)
|
||||||
|
|
||||||
|
expectedLen := len(tc.service) + len(tc.namespace)
|
||||||
|
if tc.expectedService != "" || tc.expectedNamespace != "" {
|
||||||
|
expectedLen = maxServiceAndNamespaceLen
|
||||||
|
}
|
||||||
|
|
||||||
|
actualLen := len(actualSvc) + len(actualNamespace)
|
||||||
|
|
||||||
|
require.Equal(t, expectedLen, actualLen, "Combined length of %d (svc: %d, ns: %d) does not match expected value of %d", actualLen, len(actualSvc), len(actualNamespace), expectedLen)
|
||||||
|
|
||||||
|
if tc.expectedService != "" {
|
||||||
|
require.Equal(t, tc.expectedService, actualSvc)
|
||||||
|
} else {
|
||||||
|
require.Equal(t, tc.service, actualSvc)
|
||||||
|
}
|
||||||
|
if tc.expectedNamespace != "" {
|
||||||
|
require.Equal(t, tc.expectedNamespace, actualNamespace)
|
||||||
|
} else {
|
||||||
|
require.Equal(t, tc.namespace, actualNamespace)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
|
@ -211,7 +211,7 @@ func testLeaf(t testing.T, service string, root *structs.CARoot, keyType string,
|
||||||
// Cert template for generation
|
// Cert template for generation
|
||||||
template := x509.Certificate{
|
template := x509.Certificate{
|
||||||
SerialNumber: sn,
|
SerialNumber: sn,
|
||||||
Subject: pkix.Name{CommonName: ServiceCN(service, TestClusterID)},
|
Subject: pkix.Name{CommonName: ServiceCN(service, "default", TestClusterID)},
|
||||||
URIs: []*url.URL{spiffeId.URI()},
|
URIs: []*url.URL{spiffeId.URI()},
|
||||||
SignatureAlgorithm: SigAlgoForKeyType(rootKeyType),
|
SignatureAlgorithm: SigAlgoForKeyType(rootKeyType),
|
||||||
BasicConstraintsValid: true,
|
BasicConstraintsValid: true,
|
||||||
|
|
|
@ -1647,6 +1647,25 @@ func (f *aclFilter) filterAuthMethods(methods *structs.ACLAuthMethods) {
|
||||||
*methods = ret
|
*methods = ret
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (f *aclFilter) filterServiceList(services *structs.ServiceList) {
|
||||||
|
ret := make(structs.ServiceList, 0, len(*services))
|
||||||
|
for _, svc := range *services {
|
||||||
|
var authzContext acl.AuthorizerContext
|
||||||
|
|
||||||
|
svc.FillAuthzContext(&authzContext)
|
||||||
|
|
||||||
|
if f.authorizer.ServiceRead(svc.Name, &authzContext) != acl.Allow {
|
||||||
|
sid := structs.NewServiceID(svc.Name, &svc.EnterpriseMeta)
|
||||||
|
f.logger.Printf("[DEBUG] consul: dropping service %q from result due to ACLs", sid.String())
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = append(ret, svc)
|
||||||
|
}
|
||||||
|
|
||||||
|
*services = ret
|
||||||
|
}
|
||||||
|
|
||||||
func (r *ACLResolver) filterACLWithAuthorizer(authorizer acl.Authorizer, subj interface{}) error {
|
func (r *ACLResolver) filterACLWithAuthorizer(authorizer acl.Authorizer, subj interface{}) error {
|
||||||
if authorizer == nil {
|
if authorizer == nil {
|
||||||
return nil
|
return nil
|
||||||
|
@ -1726,6 +1745,8 @@ func (r *ACLResolver) filterACLWithAuthorizer(authorizer acl.Authorizer, subj in
|
||||||
case **structs.ACLAuthMethod:
|
case **structs.ACLAuthMethod:
|
||||||
filt.filterAuthMethod(v)
|
filt.filterAuthMethod(v)
|
||||||
|
|
||||||
|
case *structs.IndexedServiceList:
|
||||||
|
filt.filterServiceList(&v.Services)
|
||||||
default:
|
default:
|
||||||
panic(fmt.Errorf("Unhandled type passed to ACL filter: %T %#v", subj, subj))
|
panic(fmt.Errorf("Unhandled type passed to ACL filter: %T %#v", subj, subj))
|
||||||
}
|
}
|
||||||
|
|
|
@ -821,17 +821,26 @@ func (a *ACL) TokenList(args *structs.ACLTokenListRequest, reply *structs.ACLTok
|
||||||
}
|
}
|
||||||
|
|
||||||
var authzContext acl.AuthorizerContext
|
var authzContext acl.AuthorizerContext
|
||||||
authz, err := a.srv.ResolveTokenAndDefaultMeta(args.Token, &args.EnterpriseMeta, &authzContext)
|
var requestMeta structs.EnterpriseMeta
|
||||||
|
authz, err := a.srv.ResolveTokenAndDefaultMeta(args.Token, &requestMeta, &authzContext)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
} else if authz == nil || authz.ACLRead(&authzContext) != acl.Allow {
|
}
|
||||||
|
// merge the token default meta into the requests meta
|
||||||
|
args.EnterpriseMeta.Merge(&requestMeta)
|
||||||
|
args.EnterpriseMeta.FillAuthzContext(&authzContext)
|
||||||
|
if authz == nil || authz.ACLRead(&authzContext) != acl.Allow {
|
||||||
return acl.ErrPermissionDenied
|
return acl.ErrPermissionDenied
|
||||||
}
|
}
|
||||||
|
|
||||||
var methodMeta *structs.EnterpriseMeta
|
var methodMeta *structs.EnterpriseMeta
|
||||||
if args.AuthMethod != "" {
|
if args.AuthMethod != "" {
|
||||||
methodMeta = args.ACLAuthMethodEnterpriseMeta.ToEnterpriseMeta()
|
methodMeta = args.ACLAuthMethodEnterpriseMeta.ToEnterpriseMeta()
|
||||||
methodMeta.Merge(&args.EnterpriseMeta)
|
// attempt to merge in the overall meta, wildcards will not be merged
|
||||||
|
methodMeta.MergeNoWildcard(&args.EnterpriseMeta)
|
||||||
|
// in the event that the meta above didn't merge due to being a wildcard
|
||||||
|
// we ensure that proper token based meta inference occurs
|
||||||
|
methodMeta.Merge(&requestMeta)
|
||||||
}
|
}
|
||||||
|
|
||||||
return a.srv.blockingQuery(&args.QueryOptions, &reply.QueryMeta,
|
return a.srv.blockingQuery(&args.QueryOptions, &reply.QueryMeta,
|
||||||
|
|
|
@ -320,6 +320,34 @@ func (c *Catalog) ListServices(args *structs.DCSpecificRequest, reply *structs.I
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *Catalog) ServiceList(args *structs.DCSpecificRequest, reply *structs.IndexedServiceList) error {
|
||||||
|
if done, err := c.srv.forward("Catalog.ServiceList", args, args, reply); done {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
authz, err := c.srv.ResolveTokenAndDefaultMeta(args.Token, &args.EnterpriseMeta, nil)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := c.srv.validateEnterpriseRequest(&args.EnterpriseMeta, false); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.srv.blockingQuery(
|
||||||
|
&args.QueryOptions,
|
||||||
|
&reply.QueryMeta,
|
||||||
|
func(ws memdb.WatchSet, state *state.Store) error {
|
||||||
|
index, services, err := state.ServiceList(ws, &args.EnterpriseMeta)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
reply.Index, reply.Services = index, services
|
||||||
|
return c.srv.filterACLWithAuthorizer(authz, reply)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
// ServiceNodes returns all the nodes registered as part of a service
|
// ServiceNodes returns all the nodes registered as part of a service
|
||||||
func (c *Catalog) ServiceNodes(args *structs.ServiceSpecificRequest, reply *structs.IndexedServiceNodes) error {
|
func (c *Catalog) ServiceNodes(args *structs.ServiceSpecificRequest, reply *structs.IndexedServiceNodes) error {
|
||||||
if done, err := c.srv.forward("Catalog.ServiceNodes", args, args, reply); done {
|
if done, err := c.srv.forward("Catalog.ServiceNodes", args, args, reply); done {
|
||||||
|
|
|
@ -349,6 +349,9 @@ type Config struct {
|
||||||
// a Consul server is now up and known about.
|
// a Consul server is now up and known about.
|
||||||
ServerUp func()
|
ServerUp func()
|
||||||
|
|
||||||
|
// Shutdown callback is used to trigger a full Consul shutdown
|
||||||
|
Shutdown func()
|
||||||
|
|
||||||
// UserEventHandler callback can be used to handle incoming
|
// UserEventHandler callback can be used to handle incoming
|
||||||
// user events. This function should not block.
|
// user events. This function should not block.
|
||||||
UserEventHandler func(serf.UserEvent)
|
UserEventHandler func(serf.UserEvent)
|
||||||
|
|
|
@ -19,6 +19,10 @@ type ConfigEntry struct {
|
||||||
|
|
||||||
// Apply does an upsert of the given config entry.
|
// Apply does an upsert of the given config entry.
|
||||||
func (c *ConfigEntry) Apply(args *structs.ConfigEntryRequest, reply *bool) error {
|
func (c *ConfigEntry) Apply(args *structs.ConfigEntryRequest, reply *bool) error {
|
||||||
|
if err := c.srv.validateEnterpriseRequest(args.Entry.GetEnterpriseMeta(), true); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
// Ensure that all config entry writes go to the primary datacenter. These will then
|
// Ensure that all config entry writes go to the primary datacenter. These will then
|
||||||
// be replicated to all the other datacenters.
|
// be replicated to all the other datacenters.
|
||||||
args.Datacenter = c.srv.config.PrimaryDatacenter
|
args.Datacenter = c.srv.config.PrimaryDatacenter
|
||||||
|
@ -28,6 +32,12 @@ func (c *ConfigEntry) Apply(args *structs.ConfigEntryRequest, reply *bool) error
|
||||||
}
|
}
|
||||||
defer metrics.MeasureSince([]string{"config_entry", "apply"}, time.Now())
|
defer metrics.MeasureSince([]string{"config_entry", "apply"}, time.Now())
|
||||||
|
|
||||||
|
entMeta := args.Entry.GetEnterpriseMeta()
|
||||||
|
authz, err := c.srv.ResolveTokenAndDefaultMeta(args.Token, entMeta, nil)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
// Normalize and validate the incoming config entry.
|
// Normalize and validate the incoming config entry.
|
||||||
if err := args.Entry.Normalize(); err != nil {
|
if err := args.Entry.Normalize(); err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -36,12 +46,7 @@ func (c *ConfigEntry) Apply(args *structs.ConfigEntryRequest, reply *bool) error
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fetch the ACL token, if any.
|
if authz != nil && !args.Entry.CanWrite(authz) {
|
||||||
rule, err := c.srv.ResolveToken(args.Token)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if rule != nil && !args.Entry.CanWrite(rule) {
|
|
||||||
return acl.ErrPermissionDenied
|
return acl.ErrPermissionDenied
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -64,13 +69,16 @@ func (c *ConfigEntry) Apply(args *structs.ConfigEntryRequest, reply *bool) error
|
||||||
|
|
||||||
// Get returns a single config entry by Kind/Name.
|
// Get returns a single config entry by Kind/Name.
|
||||||
func (c *ConfigEntry) Get(args *structs.ConfigEntryQuery, reply *structs.ConfigEntryResponse) error {
|
func (c *ConfigEntry) Get(args *structs.ConfigEntryQuery, reply *structs.ConfigEntryResponse) error {
|
||||||
|
if err := c.srv.validateEnterpriseRequest(&args.EnterpriseMeta, false); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
if done, err := c.srv.forward("ConfigEntry.Get", args, args, reply); done {
|
if done, err := c.srv.forward("ConfigEntry.Get", args, args, reply); done {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
defer metrics.MeasureSince([]string{"config_entry", "get"}, time.Now())
|
defer metrics.MeasureSince([]string{"config_entry", "get"}, time.Now())
|
||||||
|
|
||||||
// Fetch the ACL token, if any.
|
authz, err := c.srv.ResolveTokenAndDefaultMeta(args.Token, &args.EnterpriseMeta, nil)
|
||||||
rule, err := c.srv.ResolveToken(args.Token)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -80,7 +88,7 @@ func (c *ConfigEntry) Get(args *structs.ConfigEntryQuery, reply *structs.ConfigE
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if rule != nil && !lookupEntry.CanRead(rule) {
|
if authz != nil && !lookupEntry.CanRead(authz) {
|
||||||
return acl.ErrPermissionDenied
|
return acl.ErrPermissionDenied
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -88,7 +96,7 @@ func (c *ConfigEntry) Get(args *structs.ConfigEntryQuery, reply *structs.ConfigE
|
||||||
&args.QueryOptions,
|
&args.QueryOptions,
|
||||||
&reply.QueryMeta,
|
&reply.QueryMeta,
|
||||||
func(ws memdb.WatchSet, state *state.Store) error {
|
func(ws memdb.WatchSet, state *state.Store) error {
|
||||||
index, entry, err := state.ConfigEntry(ws, args.Kind, args.Name)
|
index, entry, err := state.ConfigEntry(ws, args.Kind, args.Name, &args.EnterpriseMeta)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -106,13 +114,16 @@ func (c *ConfigEntry) Get(args *structs.ConfigEntryQuery, reply *structs.ConfigE
|
||||||
// List returns all the config entries of the given kind. If Kind is blank,
|
// List returns all the config entries of the given kind. If Kind is blank,
|
||||||
// all existing config entries will be returned.
|
// all existing config entries will be returned.
|
||||||
func (c *ConfigEntry) List(args *structs.ConfigEntryQuery, reply *structs.IndexedConfigEntries) error {
|
func (c *ConfigEntry) List(args *structs.ConfigEntryQuery, reply *structs.IndexedConfigEntries) error {
|
||||||
|
if err := c.srv.validateEnterpriseRequest(&args.EnterpriseMeta, false); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
if done, err := c.srv.forward("ConfigEntry.List", args, args, reply); done {
|
if done, err := c.srv.forward("ConfigEntry.List", args, args, reply); done {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
defer metrics.MeasureSince([]string{"config_entry", "list"}, time.Now())
|
defer metrics.MeasureSince([]string{"config_entry", "list"}, time.Now())
|
||||||
|
|
||||||
// Fetch the ACL token, if any.
|
authz, err := c.srv.ResolveTokenAndDefaultMeta(args.Token, &args.EnterpriseMeta, nil)
|
||||||
rule, err := c.srv.ResolveToken(args.Token)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -125,7 +136,7 @@ func (c *ConfigEntry) List(args *structs.ConfigEntryQuery, reply *structs.Indexe
|
||||||
&args.QueryOptions,
|
&args.QueryOptions,
|
||||||
&reply.QueryMeta,
|
&reply.QueryMeta,
|
||||||
func(ws memdb.WatchSet, state *state.Store) error {
|
func(ws memdb.WatchSet, state *state.Store) error {
|
||||||
index, entries, err := state.ConfigEntriesByKind(ws, args.Kind)
|
index, entries, err := state.ConfigEntriesByKind(ws, args.Kind, &args.EnterpriseMeta)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -133,7 +144,7 @@ func (c *ConfigEntry) List(args *structs.ConfigEntryQuery, reply *structs.Indexe
|
||||||
// Filter the entries returned by ACL permissions.
|
// Filter the entries returned by ACL permissions.
|
||||||
filteredEntries := make([]structs.ConfigEntry, 0, len(entries))
|
filteredEntries := make([]structs.ConfigEntry, 0, len(entries))
|
||||||
for _, entry := range entries {
|
for _, entry := range entries {
|
||||||
if rule != nil && !entry.CanRead(rule) {
|
if authz != nil && !entry.CanRead(authz) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
filteredEntries = append(filteredEntries, entry)
|
filteredEntries = append(filteredEntries, entry)
|
||||||
|
@ -148,13 +159,16 @@ func (c *ConfigEntry) List(args *structs.ConfigEntryQuery, reply *structs.Indexe
|
||||||
|
|
||||||
// ListAll returns all the known configuration entries
|
// ListAll returns all the known configuration entries
|
||||||
func (c *ConfigEntry) ListAll(args *structs.DCSpecificRequest, reply *structs.IndexedGenericConfigEntries) error {
|
func (c *ConfigEntry) ListAll(args *structs.DCSpecificRequest, reply *structs.IndexedGenericConfigEntries) error {
|
||||||
|
if err := c.srv.validateEnterpriseRequest(&args.EnterpriseMeta, false); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
if done, err := c.srv.forward("ConfigEntry.ListAll", args, args, reply); done {
|
if done, err := c.srv.forward("ConfigEntry.ListAll", args, args, reply); done {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
defer metrics.MeasureSince([]string{"config_entry", "listAll"}, time.Now())
|
defer metrics.MeasureSince([]string{"config_entry", "listAll"}, time.Now())
|
||||||
|
|
||||||
// Fetch the ACL token, if any.
|
authz, err := c.srv.ResolveTokenAndDefaultMeta(args.Token, &args.EnterpriseMeta, nil)
|
||||||
rule, err := c.srv.ResolveToken(args.Token)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -163,7 +177,7 @@ func (c *ConfigEntry) ListAll(args *structs.DCSpecificRequest, reply *structs.In
|
||||||
&args.QueryOptions,
|
&args.QueryOptions,
|
||||||
&reply.QueryMeta,
|
&reply.QueryMeta,
|
||||||
func(ws memdb.WatchSet, state *state.Store) error {
|
func(ws memdb.WatchSet, state *state.Store) error {
|
||||||
index, entries, err := state.ConfigEntries(ws)
|
index, entries, err := state.ConfigEntries(ws, &args.EnterpriseMeta)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -171,7 +185,7 @@ func (c *ConfigEntry) ListAll(args *structs.DCSpecificRequest, reply *structs.In
|
||||||
// Filter the entries returned by ACL permissions.
|
// Filter the entries returned by ACL permissions.
|
||||||
filteredEntries := make([]structs.ConfigEntry, 0, len(entries))
|
filteredEntries := make([]structs.ConfigEntry, 0, len(entries))
|
||||||
for _, entry := range entries {
|
for _, entry := range entries {
|
||||||
if rule != nil && !entry.CanRead(rule) {
|
if authz != nil && !entry.CanRead(authz) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
filteredEntries = append(filteredEntries, entry)
|
filteredEntries = append(filteredEntries, entry)
|
||||||
|
@ -185,6 +199,10 @@ func (c *ConfigEntry) ListAll(args *structs.DCSpecificRequest, reply *structs.In
|
||||||
|
|
||||||
// Delete deletes a config entry.
|
// Delete deletes a config entry.
|
||||||
func (c *ConfigEntry) Delete(args *structs.ConfigEntryRequest, reply *struct{}) error {
|
func (c *ConfigEntry) Delete(args *structs.ConfigEntryRequest, reply *struct{}) error {
|
||||||
|
if err := c.srv.validateEnterpriseRequest(args.Entry.GetEnterpriseMeta(), true); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
// Ensure that all config entry writes go to the primary datacenter. These will then
|
// Ensure that all config entry writes go to the primary datacenter. These will then
|
||||||
// be replicated to all the other datacenters.
|
// be replicated to all the other datacenters.
|
||||||
args.Datacenter = c.srv.config.PrimaryDatacenter
|
args.Datacenter = c.srv.config.PrimaryDatacenter
|
||||||
|
@ -194,17 +212,17 @@ func (c *ConfigEntry) Delete(args *structs.ConfigEntryRequest, reply *struct{})
|
||||||
}
|
}
|
||||||
defer metrics.MeasureSince([]string{"config_entry", "delete"}, time.Now())
|
defer metrics.MeasureSince([]string{"config_entry", "delete"}, time.Now())
|
||||||
|
|
||||||
|
authz, err := c.srv.ResolveTokenAndDefaultMeta(args.Token, args.Entry.GetEnterpriseMeta(), nil)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
// Normalize the incoming entry.
|
// Normalize the incoming entry.
|
||||||
if err := args.Entry.Normalize(); err != nil {
|
if err := args.Entry.Normalize(); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fetch the ACL token, if any.
|
if authz != nil && !args.Entry.CanWrite(authz) {
|
||||||
rule, err := c.srv.ResolveToken(args.Token)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if rule != nil && !args.Entry.CanWrite(rule) {
|
|
||||||
return acl.ErrPermissionDenied
|
return acl.ErrPermissionDenied
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -221,18 +239,21 @@ func (c *ConfigEntry) Delete(args *structs.ConfigEntryRequest, reply *struct{})
|
||||||
|
|
||||||
// ResolveServiceConfig
|
// ResolveServiceConfig
|
||||||
func (c *ConfigEntry) ResolveServiceConfig(args *structs.ServiceConfigRequest, reply *structs.ServiceConfigResponse) error {
|
func (c *ConfigEntry) ResolveServiceConfig(args *structs.ServiceConfigRequest, reply *structs.ServiceConfigResponse) error {
|
||||||
|
if err := c.srv.validateEnterpriseRequest(&args.EnterpriseMeta, false); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
if done, err := c.srv.forward("ConfigEntry.ResolveServiceConfig", args, args, reply); done {
|
if done, err := c.srv.forward("ConfigEntry.ResolveServiceConfig", args, args, reply); done {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
defer metrics.MeasureSince([]string{"config_entry", "resolve_service_config"}, time.Now())
|
defer metrics.MeasureSince([]string{"config_entry", "resolve_service_config"}, time.Now())
|
||||||
|
|
||||||
// Fetch the ACL token, if any.
|
var authzContext acl.AuthorizerContext
|
||||||
rule, err := c.srv.ResolveToken(args.Token)
|
authz, err := c.srv.ResolveTokenAndDefaultMeta(args.Token, &args.EnterpriseMeta, &authzContext)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
// TODO (namespaces) use actual ent authz context
|
if authz != nil && authz.ServiceRead(args.Name, &authzContext) != acl.Allow {
|
||||||
if rule != nil && rule.ServiceRead(args.Name, nil) != acl.Allow {
|
|
||||||
return acl.ErrPermissionDenied
|
return acl.ErrPermissionDenied
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -246,7 +267,7 @@ func (c *ConfigEntry) ResolveServiceConfig(args *structs.ServiceConfigRequest, r
|
||||||
// Pass the WatchSet to both the service and proxy config lookups. If either is updated
|
// Pass the WatchSet to both the service and proxy config lookups. If either is updated
|
||||||
// during the blocking query, this function will be rerun and these state store lookups
|
// during the blocking query, this function will be rerun and these state store lookups
|
||||||
// will both be current.
|
// will both be current.
|
||||||
index, serviceEntry, err := state.ConfigEntry(ws, structs.ServiceDefaults, args.Name)
|
index, serviceEntry, err := state.ConfigEntry(ws, structs.ServiceDefaults, args.Name, &args.EnterpriseMeta)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -259,7 +280,9 @@ func (c *ConfigEntry) ResolveServiceConfig(args *structs.ServiceConfigRequest, r
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_, proxyEntry, err := state.ConfigEntry(ws, structs.ProxyDefaults, structs.ProxyConfigGlobal)
|
// Use the default enterprise meta to look up the global proxy defaults. In the future we may allow per-namespace proxy-defaults
|
||||||
|
// but not yet.
|
||||||
|
_, proxyEntry, err := state.ConfigEntry(ws, structs.ProxyDefaults, structs.ProxyConfigGlobal, structs.DefaultEnterpriseMeta())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -305,9 +328,24 @@ func (c *ConfigEntry) ResolveServiceConfig(args *structs.ServiceConfigRequest, r
|
||||||
proxyConfGlobalProtocol = proxyConf.Config["protocol"]
|
proxyConfGlobalProtocol = proxyConf.Config["protocol"]
|
||||||
}
|
}
|
||||||
|
|
||||||
// Apply the upstream protocols to the upstream configs
|
// map the legacy request structure using only service names
|
||||||
for _, upstream := range args.Upstreams {
|
// to the new ServiceID type.
|
||||||
_, upstreamEntry, err := state.ConfigEntry(ws, structs.ServiceDefaults, upstream)
|
upstreamIDs := args.UpstreamIDs
|
||||||
|
legacyUpstreams := false
|
||||||
|
|
||||||
|
if len(upstreamIDs) == 0 {
|
||||||
|
legacyUpstreams = true
|
||||||
|
|
||||||
|
upstreamIDs = make([]structs.ServiceID, 0)
|
||||||
|
for _, upstream := range args.Upstreams {
|
||||||
|
upstreamIDs = append(upstreamIDs, structs.NewServiceID(upstream, &args.EnterpriseMeta))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
usConfigs := make(map[structs.ServiceID]map[string]interface{})
|
||||||
|
|
||||||
|
for _, upstream := range upstreamIDs {
|
||||||
|
_, upstreamEntry, err := state.ConfigEntry(ws, structs.ServiceDefaults, upstream.ID, &upstream.EnterpriseMeta)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -336,11 +374,30 @@ func (c *ConfigEntry) ResolveServiceConfig(args *structs.ServiceConfigRequest, r
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
usConfigs[upstream] = map[string]interface{}{
|
||||||
|
"protocol": protocol,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// don't allocate the slices just to not fill them
|
||||||
|
if len(usConfigs) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if legacyUpstreams {
|
||||||
if reply.UpstreamConfigs == nil {
|
if reply.UpstreamConfigs == nil {
|
||||||
reply.UpstreamConfigs = make(map[string]map[string]interface{})
|
reply.UpstreamConfigs = make(map[string]map[string]interface{})
|
||||||
}
|
}
|
||||||
reply.UpstreamConfigs[upstream] = map[string]interface{}{
|
for us, conf := range usConfigs {
|
||||||
"protocol": protocol,
|
reply.UpstreamConfigs[us.ID] = conf
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if reply.UpstreamIDConfigs == nil {
|
||||||
|
reply.UpstreamIDConfigs = make(structs.UpstreamConfigs, 0, len(usConfigs))
|
||||||
|
}
|
||||||
|
|
||||||
|
for us, conf := range usConfigs {
|
||||||
|
reply.UpstreamIDConfigs = append(reply.UpstreamIDConfigs, structs.UpstreamConfig{Upstream: us, Config: conf})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -53,7 +53,7 @@ func TestConfigEntry_Apply(t *testing.T) {
|
||||||
// the previous RPC should not return until the primary has been updated but will return
|
// the previous RPC should not return until the primary has been updated but will return
|
||||||
// before the secondary has the data.
|
// before the secondary has the data.
|
||||||
state := s1.fsm.State()
|
state := s1.fsm.State()
|
||||||
_, entry, err := state.ConfigEntry(nil, structs.ServiceDefaults, "foo")
|
_, entry, err := state.ConfigEntry(nil, structs.ServiceDefaults, "foo", nil)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
serviceConf, ok := entry.(*structs.ServiceConfigEntry)
|
serviceConf, ok := entry.(*structs.ServiceConfigEntry)
|
||||||
|
@ -64,7 +64,7 @@ func TestConfigEntry_Apply(t *testing.T) {
|
||||||
retry.Run(t, func(r *retry.R) {
|
retry.Run(t, func(r *retry.R) {
|
||||||
// wait for replication to happen
|
// wait for replication to happen
|
||||||
state := s2.fsm.State()
|
state := s2.fsm.State()
|
||||||
_, entry, err := state.ConfigEntry(nil, structs.ServiceDefaults, "foo")
|
_, entry, err := state.ConfigEntry(nil, structs.ServiceDefaults, "foo", nil)
|
||||||
require.NoError(r, err)
|
require.NoError(r, err)
|
||||||
require.NotNil(r, entry)
|
require.NotNil(r, entry)
|
||||||
// this test is not testing that the config entries that are replicated are correct as thats done elsewhere.
|
// this test is not testing that the config entries that are replicated are correct as thats done elsewhere.
|
||||||
|
@ -91,7 +91,7 @@ func TestConfigEntry_Apply(t *testing.T) {
|
||||||
require.True(t, out)
|
require.True(t, out)
|
||||||
|
|
||||||
state = s1.fsm.State()
|
state = s1.fsm.State()
|
||||||
_, entry, err = state.ConfigEntry(nil, structs.ServiceDefaults, "foo")
|
_, entry, err = state.ConfigEntry(nil, structs.ServiceDefaults, "foo", nil)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
serviceConf, ok = entry.(*structs.ServiceConfigEntry)
|
serviceConf, ok = entry.(*structs.ServiceConfigEntry)
|
||||||
|
@ -124,7 +124,7 @@ func TestConfigEntry_ProxyDefaultsMeshGateway(t *testing.T) {
|
||||||
require.True(t, out)
|
require.True(t, out)
|
||||||
|
|
||||||
state := s1.fsm.State()
|
state := s1.fsm.State()
|
||||||
_, entry, err := state.ConfigEntry(nil, structs.ProxyDefaults, "global")
|
_, entry, err := state.ConfigEntry(nil, structs.ProxyDefaults, "global", nil)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
proxyConf, ok := entry.(*structs.ProxyConfigEntry)
|
proxyConf, ok := entry.(*structs.ProxyConfigEntry)
|
||||||
|
@ -192,7 +192,7 @@ operator = "write"
|
||||||
require.NoError(err)
|
require.NoError(err)
|
||||||
|
|
||||||
state := s1.fsm.State()
|
state := s1.fsm.State()
|
||||||
_, entry, err := state.ConfigEntry(nil, structs.ServiceDefaults, "foo")
|
_, entry, err := state.ConfigEntry(nil, structs.ServiceDefaults, "foo", nil)
|
||||||
require.NoError(err)
|
require.NoError(err)
|
||||||
|
|
||||||
serviceConf, ok := entry.(*structs.ServiceConfigEntry)
|
serviceConf, ok := entry.(*structs.ServiceConfigEntry)
|
||||||
|
@ -237,7 +237,7 @@ func TestConfigEntry_Get(t *testing.T) {
|
||||||
Name: "foo",
|
Name: "foo",
|
||||||
}
|
}
|
||||||
state := s1.fsm.State()
|
state := s1.fsm.State()
|
||||||
require.NoError(state.EnsureConfigEntry(1, entry))
|
require.NoError(state.EnsureConfigEntry(1, entry, nil))
|
||||||
|
|
||||||
args := structs.ConfigEntryQuery{
|
args := structs.ConfigEntryQuery{
|
||||||
Kind: structs.ServiceDefaults,
|
Kind: structs.ServiceDefaults,
|
||||||
|
@ -296,11 +296,11 @@ operator = "read"
|
||||||
require.NoError(state.EnsureConfigEntry(1, &structs.ProxyConfigEntry{
|
require.NoError(state.EnsureConfigEntry(1, &structs.ProxyConfigEntry{
|
||||||
Kind: structs.ProxyDefaults,
|
Kind: structs.ProxyDefaults,
|
||||||
Name: structs.ProxyConfigGlobal,
|
Name: structs.ProxyConfigGlobal,
|
||||||
}))
|
}, nil))
|
||||||
require.NoError(state.EnsureConfigEntry(2, &structs.ServiceConfigEntry{
|
require.NoError(state.EnsureConfigEntry(2, &structs.ServiceConfigEntry{
|
||||||
Kind: structs.ServiceDefaults,
|
Kind: structs.ServiceDefaults,
|
||||||
Name: "foo",
|
Name: "foo",
|
||||||
}))
|
}, nil))
|
||||||
|
|
||||||
// This should fail since we don't have write perms for the "db" service.
|
// This should fail since we don't have write perms for the "db" service.
|
||||||
args := structs.ConfigEntryQuery{
|
args := structs.ConfigEntryQuery{
|
||||||
|
@ -350,8 +350,8 @@ func TestConfigEntry_List(t *testing.T) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
require.NoError(state.EnsureConfigEntry(1, expected.Entries[0]))
|
require.NoError(state.EnsureConfigEntry(1, expected.Entries[0], nil))
|
||||||
require.NoError(state.EnsureConfigEntry(2, expected.Entries[1]))
|
require.NoError(state.EnsureConfigEntry(2, expected.Entries[1], nil))
|
||||||
|
|
||||||
args := structs.ConfigEntryQuery{
|
args := structs.ConfigEntryQuery{
|
||||||
Kind: structs.ServiceDefaults,
|
Kind: structs.ServiceDefaults,
|
||||||
|
@ -394,9 +394,9 @@ func TestConfigEntry_ListAll(t *testing.T) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
require.NoError(state.EnsureConfigEntry(1, expected.Entries[0]))
|
require.NoError(state.EnsureConfigEntry(1, expected.Entries[0], nil))
|
||||||
require.NoError(state.EnsureConfigEntry(2, expected.Entries[1]))
|
require.NoError(state.EnsureConfigEntry(2, expected.Entries[1], nil))
|
||||||
require.NoError(state.EnsureConfigEntry(3, expected.Entries[2]))
|
require.NoError(state.EnsureConfigEntry(3, expected.Entries[2], nil))
|
||||||
|
|
||||||
args := structs.DCSpecificRequest{
|
args := structs.DCSpecificRequest{
|
||||||
Datacenter: "dc1",
|
Datacenter: "dc1",
|
||||||
|
@ -451,15 +451,15 @@ operator = "read"
|
||||||
require.NoError(state.EnsureConfigEntry(1, &structs.ProxyConfigEntry{
|
require.NoError(state.EnsureConfigEntry(1, &structs.ProxyConfigEntry{
|
||||||
Kind: structs.ProxyDefaults,
|
Kind: structs.ProxyDefaults,
|
||||||
Name: structs.ProxyConfigGlobal,
|
Name: structs.ProxyConfigGlobal,
|
||||||
}))
|
}, nil))
|
||||||
require.NoError(state.EnsureConfigEntry(2, &structs.ServiceConfigEntry{
|
require.NoError(state.EnsureConfigEntry(2, &structs.ServiceConfigEntry{
|
||||||
Kind: structs.ServiceDefaults,
|
Kind: structs.ServiceDefaults,
|
||||||
Name: "foo",
|
Name: "foo",
|
||||||
}))
|
}, nil))
|
||||||
require.NoError(state.EnsureConfigEntry(3, &structs.ServiceConfigEntry{
|
require.NoError(state.EnsureConfigEntry(3, &structs.ServiceConfigEntry{
|
||||||
Kind: structs.ServiceDefaults,
|
Kind: structs.ServiceDefaults,
|
||||||
Name: "db",
|
Name: "db",
|
||||||
}))
|
}, nil))
|
||||||
|
|
||||||
// This should filter out the "db" service since we don't have permissions for it.
|
// This should filter out the "db" service since we don't have permissions for it.
|
||||||
args := structs.ConfigEntryQuery{
|
args := structs.ConfigEntryQuery{
|
||||||
|
@ -532,15 +532,15 @@ operator = "read"
|
||||||
require.NoError(state.EnsureConfigEntry(1, &structs.ProxyConfigEntry{
|
require.NoError(state.EnsureConfigEntry(1, &structs.ProxyConfigEntry{
|
||||||
Kind: structs.ProxyDefaults,
|
Kind: structs.ProxyDefaults,
|
||||||
Name: structs.ProxyConfigGlobal,
|
Name: structs.ProxyConfigGlobal,
|
||||||
}))
|
}, nil))
|
||||||
require.NoError(state.EnsureConfigEntry(2, &structs.ServiceConfigEntry{
|
require.NoError(state.EnsureConfigEntry(2, &structs.ServiceConfigEntry{
|
||||||
Kind: structs.ServiceDefaults,
|
Kind: structs.ServiceDefaults,
|
||||||
Name: "foo",
|
Name: "foo",
|
||||||
}))
|
}, nil))
|
||||||
require.NoError(state.EnsureConfigEntry(3, &structs.ServiceConfigEntry{
|
require.NoError(state.EnsureConfigEntry(3, &structs.ServiceConfigEntry{
|
||||||
Kind: structs.ServiceDefaults,
|
Kind: structs.ServiceDefaults,
|
||||||
Name: "db",
|
Name: "db",
|
||||||
}))
|
}, nil))
|
||||||
|
|
||||||
// This should filter out the "db" service since we don't have permissions for it.
|
// This should filter out the "db" service since we don't have permissions for it.
|
||||||
args := structs.ConfigEntryQuery{
|
args := structs.ConfigEntryQuery{
|
||||||
|
@ -600,10 +600,10 @@ func TestConfigEntry_Delete(t *testing.T) {
|
||||||
Name: "foo",
|
Name: "foo",
|
||||||
}
|
}
|
||||||
state := s1.fsm.State()
|
state := s1.fsm.State()
|
||||||
require.NoError(t, state.EnsureConfigEntry(1, entry))
|
require.NoError(t, state.EnsureConfigEntry(1, entry, nil))
|
||||||
|
|
||||||
// Verify it's there.
|
// Verify it's there.
|
||||||
_, existing, err := state.ConfigEntry(nil, structs.ServiceDefaults, "foo")
|
_, existing, err := state.ConfigEntry(nil, structs.ServiceDefaults, "foo", nil)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
serviceConf, ok := existing.(*structs.ServiceConfigEntry)
|
serviceConf, ok := existing.(*structs.ServiceConfigEntry)
|
||||||
|
@ -613,7 +613,7 @@ func TestConfigEntry_Delete(t *testing.T) {
|
||||||
|
|
||||||
retry.Run(t, func(r *retry.R) {
|
retry.Run(t, func(r *retry.R) {
|
||||||
// wait for it to be replicated into the secondary dc
|
// wait for it to be replicated into the secondary dc
|
||||||
_, existing, err := s2.fsm.State().ConfigEntry(nil, structs.ServiceDefaults, "foo")
|
_, existing, err := s2.fsm.State().ConfigEntry(nil, structs.ServiceDefaults, "foo", nil)
|
||||||
require.NoError(r, err)
|
require.NoError(r, err)
|
||||||
require.NotNil(r, existing)
|
require.NotNil(r, existing)
|
||||||
})
|
})
|
||||||
|
@ -627,13 +627,13 @@ func TestConfigEntry_Delete(t *testing.T) {
|
||||||
require.NoError(t, msgpackrpc.CallWithCodec(codec2, "ConfigEntry.Delete", &args, &out))
|
require.NoError(t, msgpackrpc.CallWithCodec(codec2, "ConfigEntry.Delete", &args, &out))
|
||||||
|
|
||||||
// Verify the entry was deleted.
|
// Verify the entry was deleted.
|
||||||
_, existing, err = s1.fsm.State().ConfigEntry(nil, structs.ServiceDefaults, "foo")
|
_, existing, err = s1.fsm.State().ConfigEntry(nil, structs.ServiceDefaults, "foo", nil)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Nil(t, existing)
|
require.Nil(t, existing)
|
||||||
|
|
||||||
// verify it gets deleted from the secondary too
|
// verify it gets deleted from the secondary too
|
||||||
retry.Run(t, func(r *retry.R) {
|
retry.Run(t, func(r *retry.R) {
|
||||||
_, existing, err := s2.fsm.State().ConfigEntry(nil, structs.ServiceDefaults, "foo")
|
_, existing, err := s2.fsm.State().ConfigEntry(nil, structs.ServiceDefaults, "foo", nil)
|
||||||
require.NoError(r, err)
|
require.NoError(r, err)
|
||||||
require.Nil(r, existing)
|
require.Nil(r, existing)
|
||||||
})
|
})
|
||||||
|
@ -682,11 +682,11 @@ operator = "write"
|
||||||
require.NoError(state.EnsureConfigEntry(1, &structs.ProxyConfigEntry{
|
require.NoError(state.EnsureConfigEntry(1, &structs.ProxyConfigEntry{
|
||||||
Kind: structs.ProxyDefaults,
|
Kind: structs.ProxyDefaults,
|
||||||
Name: structs.ProxyConfigGlobal,
|
Name: structs.ProxyConfigGlobal,
|
||||||
}))
|
}, nil))
|
||||||
require.NoError(state.EnsureConfigEntry(2, &structs.ServiceConfigEntry{
|
require.NoError(state.EnsureConfigEntry(2, &structs.ServiceConfigEntry{
|
||||||
Kind: structs.ServiceDefaults,
|
Kind: structs.ServiceDefaults,
|
||||||
Name: "foo",
|
Name: "foo",
|
||||||
}))
|
}, nil))
|
||||||
|
|
||||||
// This should fail since we don't have write perms for the "db" service.
|
// This should fail since we don't have write perms for the "db" service.
|
||||||
args := structs.ConfigEntryRequest{
|
args := structs.ConfigEntryRequest{
|
||||||
|
@ -709,7 +709,7 @@ operator = "write"
|
||||||
require.NoError(msgpackrpc.CallWithCodec(codec, "ConfigEntry.Delete", &args, &out))
|
require.NoError(msgpackrpc.CallWithCodec(codec, "ConfigEntry.Delete", &args, &out))
|
||||||
|
|
||||||
// Verify the entry was deleted.
|
// Verify the entry was deleted.
|
||||||
_, existing, err := state.ConfigEntry(nil, structs.ServiceDefaults, "foo")
|
_, existing, err := state.ConfigEntry(nil, structs.ServiceDefaults, "foo", nil)
|
||||||
require.NoError(err)
|
require.NoError(err)
|
||||||
require.Nil(existing)
|
require.Nil(existing)
|
||||||
|
|
||||||
|
@ -729,7 +729,7 @@ operator = "write"
|
||||||
args.WriteRequest.Token = id
|
args.WriteRequest.Token = id
|
||||||
require.NoError(msgpackrpc.CallWithCodec(codec, "ConfigEntry.Delete", &args, &out))
|
require.NoError(msgpackrpc.CallWithCodec(codec, "ConfigEntry.Delete", &args, &out))
|
||||||
|
|
||||||
_, existing, err = state.ConfigEntry(nil, structs.ServiceDefaults, "foo")
|
_, existing, err = state.ConfigEntry(nil, structs.ServiceDefaults, "foo", nil)
|
||||||
require.NoError(err)
|
require.NoError(err)
|
||||||
require.Nil(existing)
|
require.Nil(existing)
|
||||||
}
|
}
|
||||||
|
@ -753,17 +753,17 @@ func TestConfigEntry_ResolveServiceConfig(t *testing.T) {
|
||||||
Config: map[string]interface{}{
|
Config: map[string]interface{}{
|
||||||
"foo": 1,
|
"foo": 1,
|
||||||
},
|
},
|
||||||
}))
|
}, nil))
|
||||||
require.NoError(state.EnsureConfigEntry(2, &structs.ServiceConfigEntry{
|
require.NoError(state.EnsureConfigEntry(2, &structs.ServiceConfigEntry{
|
||||||
Kind: structs.ServiceDefaults,
|
Kind: structs.ServiceDefaults,
|
||||||
Name: "foo",
|
Name: "foo",
|
||||||
Protocol: "http",
|
Protocol: "http",
|
||||||
}))
|
}, nil))
|
||||||
require.NoError(state.EnsureConfigEntry(2, &structs.ServiceConfigEntry{
|
require.NoError(state.EnsureConfigEntry(2, &structs.ServiceConfigEntry{
|
||||||
Kind: structs.ServiceDefaults,
|
Kind: structs.ServiceDefaults,
|
||||||
Name: "bar",
|
Name: "bar",
|
||||||
Protocol: "grpc",
|
Protocol: "grpc",
|
||||||
}))
|
}, nil))
|
||||||
|
|
||||||
args := structs.ServiceConfigRequest{
|
args := structs.ServiceConfigRequest{
|
||||||
Name: "foo",
|
Name: "foo",
|
||||||
|
@ -788,7 +788,7 @@ func TestConfigEntry_ResolveServiceConfig(t *testing.T) {
|
||||||
}
|
}
|
||||||
require.Equal(expected, out)
|
require.Equal(expected, out)
|
||||||
|
|
||||||
_, entry, err := s1.fsm.State().ConfigEntry(nil, structs.ProxyDefaults, structs.ProxyConfigGlobal)
|
_, entry, err := s1.fsm.State().ConfigEntry(nil, structs.ProxyDefaults, structs.ProxyConfigGlobal, nil)
|
||||||
require.NoError(err)
|
require.NoError(err)
|
||||||
require.NotNil(entry)
|
require.NotNil(entry)
|
||||||
proxyConf, ok := entry.(*structs.ProxyConfigEntry)
|
proxyConf, ok := entry.(*structs.ProxyConfigEntry)
|
||||||
|
@ -819,17 +819,17 @@ func TestConfigEntry_ResolveServiceConfig_Blocking(t *testing.T) {
|
||||||
Config: map[string]interface{}{
|
Config: map[string]interface{}{
|
||||||
"global": 1,
|
"global": 1,
|
||||||
},
|
},
|
||||||
}))
|
}, nil))
|
||||||
require.NoError(state.EnsureConfigEntry(2, &structs.ServiceConfigEntry{
|
require.NoError(state.EnsureConfigEntry(2, &structs.ServiceConfigEntry{
|
||||||
Kind: structs.ServiceDefaults,
|
Kind: structs.ServiceDefaults,
|
||||||
Name: "foo",
|
Name: "foo",
|
||||||
Protocol: "grpc",
|
Protocol: "grpc",
|
||||||
}))
|
}, nil))
|
||||||
require.NoError(state.EnsureConfigEntry(3, &structs.ServiceConfigEntry{
|
require.NoError(state.EnsureConfigEntry(3, &structs.ServiceConfigEntry{
|
||||||
Kind: structs.ServiceDefaults,
|
Kind: structs.ServiceDefaults,
|
||||||
Name: "bar",
|
Name: "bar",
|
||||||
Protocol: "http",
|
Protocol: "http",
|
||||||
}))
|
}, nil))
|
||||||
|
|
||||||
var index uint64
|
var index uint64
|
||||||
|
|
||||||
|
@ -863,6 +863,7 @@ func TestConfigEntry_ResolveServiceConfig_Blocking(t *testing.T) {
|
||||||
require.NoError(state.DeleteConfigEntry(index+1,
|
require.NoError(state.DeleteConfigEntry(index+1,
|
||||||
structs.ServiceDefaults,
|
structs.ServiceDefaults,
|
||||||
"foo",
|
"foo",
|
||||||
|
nil,
|
||||||
))
|
))
|
||||||
}()
|
}()
|
||||||
|
|
||||||
|
@ -927,6 +928,7 @@ func TestConfigEntry_ResolveServiceConfig_Blocking(t *testing.T) {
|
||||||
require.NoError(state.DeleteConfigEntry(index+1,
|
require.NoError(state.DeleteConfigEntry(index+1,
|
||||||
structs.ProxyDefaults,
|
structs.ProxyDefaults,
|
||||||
structs.ProxyConfigGlobal,
|
structs.ProxyConfigGlobal,
|
||||||
|
nil,
|
||||||
))
|
))
|
||||||
}()
|
}()
|
||||||
|
|
||||||
|
@ -979,24 +981,24 @@ func TestConfigEntry_ResolveServiceConfig_UpstreamProxyDefaultsProtocol(t *testi
|
||||||
Config: map[string]interface{}{
|
Config: map[string]interface{}{
|
||||||
"protocol": "http",
|
"protocol": "http",
|
||||||
},
|
},
|
||||||
}))
|
}, nil))
|
||||||
require.NoError(state.EnsureConfigEntry(2, &structs.ServiceConfigEntry{
|
require.NoError(state.EnsureConfigEntry(2, &structs.ServiceConfigEntry{
|
||||||
Kind: structs.ServiceDefaults,
|
Kind: structs.ServiceDefaults,
|
||||||
Name: "foo",
|
Name: "foo",
|
||||||
}))
|
}, nil))
|
||||||
require.NoError(state.EnsureConfigEntry(2, &structs.ServiceConfigEntry{
|
require.NoError(state.EnsureConfigEntry(2, &structs.ServiceConfigEntry{
|
||||||
Kind: structs.ServiceDefaults,
|
Kind: structs.ServiceDefaults,
|
||||||
Name: "bar",
|
Name: "bar",
|
||||||
}))
|
}, nil))
|
||||||
require.NoError(state.EnsureConfigEntry(2, &structs.ServiceConfigEntry{
|
require.NoError(state.EnsureConfigEntry(2, &structs.ServiceConfigEntry{
|
||||||
Kind: structs.ServiceDefaults,
|
Kind: structs.ServiceDefaults,
|
||||||
Name: "other",
|
Name: "other",
|
||||||
}))
|
}, nil))
|
||||||
require.NoError(state.EnsureConfigEntry(2, &structs.ServiceConfigEntry{
|
require.NoError(state.EnsureConfigEntry(2, &structs.ServiceConfigEntry{
|
||||||
Kind: structs.ServiceDefaults,
|
Kind: structs.ServiceDefaults,
|
||||||
Name: "alreadyprotocol",
|
Name: "alreadyprotocol",
|
||||||
Protocol: "grpc",
|
Protocol: "grpc",
|
||||||
}))
|
}, nil))
|
||||||
|
|
||||||
args := structs.ServiceConfigRequest{
|
args := structs.ServiceConfigRequest{
|
||||||
Name: "foo",
|
Name: "foo",
|
||||||
|
@ -1100,15 +1102,15 @@ operator = "write"
|
||||||
require.NoError(state.EnsureConfigEntry(1, &structs.ProxyConfigEntry{
|
require.NoError(state.EnsureConfigEntry(1, &structs.ProxyConfigEntry{
|
||||||
Kind: structs.ProxyDefaults,
|
Kind: structs.ProxyDefaults,
|
||||||
Name: structs.ProxyConfigGlobal,
|
Name: structs.ProxyConfigGlobal,
|
||||||
}))
|
}, nil))
|
||||||
require.NoError(state.EnsureConfigEntry(2, &structs.ServiceConfigEntry{
|
require.NoError(state.EnsureConfigEntry(2, &structs.ServiceConfigEntry{
|
||||||
Kind: structs.ServiceDefaults,
|
Kind: structs.ServiceDefaults,
|
||||||
Name: "foo",
|
Name: "foo",
|
||||||
}))
|
}, nil))
|
||||||
require.NoError(state.EnsureConfigEntry(3, &structs.ServiceConfigEntry{
|
require.NoError(state.EnsureConfigEntry(3, &structs.ServiceConfigEntry{
|
||||||
Kind: structs.ServiceDefaults,
|
Kind: structs.ServiceDefaults,
|
||||||
Name: "db",
|
Name: "db",
|
||||||
}))
|
}, nil))
|
||||||
|
|
||||||
// This should fail since we don't have write perms for the "db" service.
|
// This should fail since we don't have write perms for the "db" service.
|
||||||
args := structs.ServiceConfigRequest{
|
args := structs.ServiceConfigRequest{
|
||||||
|
@ -1163,7 +1165,7 @@ func TestConfigEntry_ProxyDefaultsExposeConfig(t *testing.T) {
|
||||||
require.True(t, out)
|
require.True(t, out)
|
||||||
|
|
||||||
state := s1.fsm.State()
|
state := s1.fsm.State()
|
||||||
_, entry, err := state.ConfigEntry(nil, structs.ProxyDefaults, "global")
|
_, entry, err := state.ConfigEntry(nil, structs.ProxyDefaults, "global", nil)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
proxyConf, ok := entry.(*structs.ProxyConfigEntry)
|
proxyConf, ok := entry.(*structs.ProxyConfigEntry)
|
||||||
|
|
|
@ -105,6 +105,7 @@ func (s *Server) fetchConfigEntries(lastRemoteIndex uint64) (*structs.IndexedGen
|
||||||
MinQueryIndex: lastRemoteIndex,
|
MinQueryIndex: lastRemoteIndex,
|
||||||
Token: s.tokens.ReplicationToken(),
|
Token: s.tokens.ReplicationToken(),
|
||||||
},
|
},
|
||||||
|
EnterpriseMeta: *s.replicationEnterpriseMeta(),
|
||||||
}
|
}
|
||||||
|
|
||||||
var response structs.IndexedGenericConfigEntries
|
var response structs.IndexedGenericConfigEntries
|
||||||
|
@ -138,7 +139,7 @@ func (s *Server) replicateConfig(ctx context.Context, lastRemoteIndex uint64) (u
|
||||||
// replication process is.
|
// replication process is.
|
||||||
defer metrics.MeasureSince([]string{"leader", "replication", "config", "apply"}, time.Now())
|
defer metrics.MeasureSince([]string{"leader", "replication", "config", "apply"}, time.Now())
|
||||||
|
|
||||||
_, local, err := s.fsm.State().ConfigEntries(nil)
|
_, local, err := s.fsm.State().ConfigEntries(nil, s.replicationEnterpriseMeta())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, false, fmt.Errorf("failed to retrieve local config entries: %v", err)
|
return 0, false, fmt.Errorf("failed to retrieve local config entries: %v", err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -74,9 +74,9 @@ func TestReplication_ConfigEntries(t *testing.T) {
|
||||||
entries = append(entries, arg.Entry)
|
entries = append(entries, arg.Entry)
|
||||||
|
|
||||||
checkSame := func(t *retry.R) error {
|
checkSame := func(t *retry.R) error {
|
||||||
_, remote, err := s1.fsm.State().ConfigEntries(nil)
|
_, remote, err := s1.fsm.State().ConfigEntries(nil, structs.ReplicationEnterpriseMeta())
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
_, local, err := s2.fsm.State().ConfigEntries(nil)
|
_, local, err := s2.fsm.State().ConfigEntries(nil, structs.ReplicationEnterpriseMeta())
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
require.Len(t, local, len(remote))
|
require.Len(t, local, len(remote))
|
||||||
|
|
|
@ -30,12 +30,13 @@ func (c *DiscoveryChain) Get(args *structs.DiscoveryChainRequest, reply *structs
|
||||||
defer metrics.MeasureSince([]string{"discovery_chain", "get"}, time.Now())
|
defer metrics.MeasureSince([]string{"discovery_chain", "get"}, time.Now())
|
||||||
|
|
||||||
// Fetch the ACL token, if any.
|
// Fetch the ACL token, if any.
|
||||||
rule, err := c.srv.ResolveToken(args.Token)
|
entMeta := args.GetEnterpriseMeta()
|
||||||
|
var authzContext acl.AuthorizerContext
|
||||||
|
rule, err := c.srv.ResolveTokenAndDefaultMeta(args.Token, entMeta, &authzContext)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
// TODO (namespaces) use actual ent authz context
|
if rule != nil && rule.ServiceRead(args.Name, &authzContext) != acl.Allow {
|
||||||
if rule != nil && rule.ServiceRead(args.Name, nil) != acl.Allow {
|
|
||||||
return acl.ErrPermissionDenied
|
return acl.ErrPermissionDenied
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -48,17 +49,11 @@ func (c *DiscoveryChain) Get(args *structs.DiscoveryChainRequest, reply *structs
|
||||||
evalDC = c.srv.config.Datacenter
|
evalDC = c.srv.config.Datacenter
|
||||||
}
|
}
|
||||||
|
|
||||||
evalNS := args.EvaluateInNamespace
|
|
||||||
if evalNS == "" {
|
|
||||||
// TODO(namespaces) pull from something else?
|
|
||||||
evalNS = "default"
|
|
||||||
}
|
|
||||||
|
|
||||||
return c.srv.blockingQuery(
|
return c.srv.blockingQuery(
|
||||||
&args.QueryOptions,
|
&args.QueryOptions,
|
||||||
&reply.QueryMeta,
|
&reply.QueryMeta,
|
||||||
func(ws memdb.WatchSet, state *state.Store) error {
|
func(ws memdb.WatchSet, state *state.Store) error {
|
||||||
index, entries, err := state.ReadDiscoveryChainConfigEntries(ws, args.Name)
|
index, entries, err := state.ReadDiscoveryChainConfigEntries(ws, args.Name, entMeta)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -82,7 +77,7 @@ func (c *DiscoveryChain) Get(args *structs.DiscoveryChainRequest, reply *structs
|
||||||
// Then we compile it into something useful.
|
// Then we compile it into something useful.
|
||||||
chain, err := discoverychain.Compile(discoverychain.CompileRequest{
|
chain, err := discoverychain.Compile(discoverychain.CompileRequest{
|
||||||
ServiceName: args.Name,
|
ServiceName: args.Name,
|
||||||
EvaluateInNamespace: evalNS,
|
EvaluateInNamespace: entMeta.NamespaceOrDefault(),
|
||||||
EvaluateInDatacenter: evalDC,
|
EvaluateInDatacenter: evalDC,
|
||||||
EvaluateInTrustDomain: currentTrustDomain,
|
EvaluateInTrustDomain: currentTrustDomain,
|
||||||
UseInDatacenter: c.srv.config.Datacenter,
|
UseInDatacenter: c.srv.config.Datacenter,
|
||||||
|
|
|
@ -92,7 +92,7 @@ func Compile(req CompileRequest) (*structs.CompiledDiscoveryChain, error) {
|
||||||
overrideConnectTimeout: req.OverrideConnectTimeout,
|
overrideConnectTimeout: req.OverrideConnectTimeout,
|
||||||
entries: entries,
|
entries: entries,
|
||||||
|
|
||||||
resolvers: make(map[string]*structs.ServiceResolverConfigEntry),
|
resolvers: make(map[structs.ServiceID]*structs.ServiceResolverConfigEntry),
|
||||||
splitterNodes: make(map[string]*structs.DiscoveryGraphNode),
|
splitterNodes: make(map[string]*structs.DiscoveryGraphNode),
|
||||||
resolveNodes: make(map[string]*structs.DiscoveryGraphNode),
|
resolveNodes: make(map[string]*structs.DiscoveryGraphNode),
|
||||||
|
|
||||||
|
@ -134,7 +134,7 @@ type compiler struct {
|
||||||
|
|
||||||
// resolvers is initially seeded by copying the provided entries.Resolvers
|
// resolvers is initially seeded by copying the provided entries.Resolvers
|
||||||
// map and default resolvers are added as they are needed.
|
// map and default resolvers are added as they are needed.
|
||||||
resolvers map[string]*structs.ServiceResolverConfigEntry
|
resolvers map[structs.ServiceID]*structs.ServiceResolverConfigEntry
|
||||||
|
|
||||||
// cached nodes
|
// cached nodes
|
||||||
splitterNodes map[string]*structs.DiscoveryGraphNode
|
splitterNodes map[string]*structs.DiscoveryGraphNode
|
||||||
|
@ -180,6 +180,13 @@ type customizationMarkers struct {
|
||||||
ConnectTimeout bool
|
ConnectTimeout bool
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// serviceIDString deviates from the standard formatting you would get with
|
||||||
|
// the String() method on the type itself. It is this way to be more
|
||||||
|
// consistent with other string ids within the discovery chain.
|
||||||
|
func serviceIDString(sid structs.ServiceID) string {
|
||||||
|
return fmt.Sprintf("%s.%s", sid.ID, sid.NamespaceOrDefault())
|
||||||
|
}
|
||||||
|
|
||||||
func (m *customizationMarkers) IsZero() bool {
|
func (m *customizationMarkers) IsZero() bool {
|
||||||
return !m.MeshGateway && !m.Protocol && !m.ConnectTimeout
|
return !m.MeshGateway && !m.Protocol && !m.ConnectTimeout
|
||||||
}
|
}
|
||||||
|
@ -201,19 +208,19 @@ func (c *compiler) recordNode(node *structs.DiscoveryGraphNode) {
|
||||||
c.nodes[node.MapKey()] = node
|
c.nodes[node.MapKey()] = node
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *compiler) recordServiceProtocol(serviceName string) error {
|
func (c *compiler) recordServiceProtocol(sid structs.ServiceID) error {
|
||||||
if serviceDefault := c.entries.GetService(serviceName); serviceDefault != nil {
|
if serviceDefault := c.entries.GetService(sid); serviceDefault != nil {
|
||||||
return c.recordProtocol(serviceName, serviceDefault.Protocol)
|
return c.recordProtocol(sid, serviceDefault.Protocol)
|
||||||
}
|
}
|
||||||
if c.entries.GlobalProxy != nil {
|
if c.entries.GlobalProxy != nil {
|
||||||
var cfg proxyConfig
|
var cfg proxyConfig
|
||||||
// Ignore errors and fallback on defaults if it does happen.
|
// Ignore errors and fallback on defaults if it does happen.
|
||||||
_ = mapstructure.WeakDecode(c.entries.GlobalProxy.Config, &cfg)
|
_ = mapstructure.WeakDecode(c.entries.GlobalProxy.Config, &cfg)
|
||||||
if cfg.Protocol != "" {
|
if cfg.Protocol != "" {
|
||||||
return c.recordProtocol(serviceName, cfg.Protocol)
|
return c.recordProtocol(sid, cfg.Protocol)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return c.recordProtocol(serviceName, "")
|
return c.recordProtocol(sid, "")
|
||||||
}
|
}
|
||||||
|
|
||||||
// proxyConfig is a snippet from agent/xds/config.go:ProxyConfig
|
// proxyConfig is a snippet from agent/xds/config.go:ProxyConfig
|
||||||
|
@ -221,7 +228,7 @@ type proxyConfig struct {
|
||||||
Protocol string `mapstructure:"protocol"`
|
Protocol string `mapstructure:"protocol"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *compiler) recordProtocol(fromService, protocol string) error {
|
func (c *compiler) recordProtocol(fromService structs.ServiceID, protocol string) error {
|
||||||
if protocol == "" {
|
if protocol == "" {
|
||||||
protocol = "tcp"
|
protocol = "tcp"
|
||||||
} else {
|
} else {
|
||||||
|
@ -234,7 +241,7 @@ func (c *compiler) recordProtocol(fromService, protocol string) error {
|
||||||
return &structs.ConfigEntryGraphError{
|
return &structs.ConfigEntryGraphError{
|
||||||
Message: fmt.Sprintf(
|
Message: fmt.Sprintf(
|
||||||
"discovery chain %q uses inconsistent protocols; service %q has %q which is not %q",
|
"discovery chain %q uses inconsistent protocols; service %q has %q which is not %q",
|
||||||
c.serviceName, fromService, protocol, c.protocol,
|
c.serviceName, fromService.String(), protocol, c.protocol,
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -494,15 +501,17 @@ func (c *compiler) assembleChain() error {
|
||||||
return fmt.Errorf("assembleChain should only be called once")
|
return fmt.Errorf("assembleChain should only be called once")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sid := structs.NewServiceID(c.serviceName, c.GetEnterpriseMeta())
|
||||||
|
|
||||||
// Check for short circuit path.
|
// Check for short circuit path.
|
||||||
if len(c.resolvers) == 0 && c.entries.IsChainEmpty() {
|
if len(c.resolvers) == 0 && c.entries.IsChainEmpty() {
|
||||||
// Materialize defaults and cache.
|
// Materialize defaults and cache.
|
||||||
c.resolvers[c.serviceName] = newDefaultServiceResolver(c.serviceName)
|
c.resolvers[sid] = newDefaultServiceResolver(sid)
|
||||||
}
|
}
|
||||||
|
|
||||||
// The only router we consult is the one for the service name at the top of
|
// The only router we consult is the one for the service name at the top of
|
||||||
// the chain.
|
// the chain.
|
||||||
router := c.entries.GetRouter(c.serviceName)
|
router := c.entries.GetRouter(sid)
|
||||||
if router != nil && c.disableAdvancedRoutingFeatures {
|
if router != nil && c.disableAdvancedRoutingFeatures {
|
||||||
router = nil
|
router = nil
|
||||||
c.customizedBy.Protocol = true
|
c.customizedBy.Protocol = true
|
||||||
|
@ -520,13 +529,15 @@ func (c *compiler) assembleChain() error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
routerID := structs.NewServiceID(router.Name, router.GetEnterpriseMeta())
|
||||||
|
|
||||||
routeNode := &structs.DiscoveryGraphNode{
|
routeNode := &structs.DiscoveryGraphNode{
|
||||||
Type: structs.DiscoveryGraphNodeTypeRouter,
|
Type: structs.DiscoveryGraphNodeTypeRouter,
|
||||||
Name: router.Name,
|
Name: serviceIDString(routerID),
|
||||||
Routes: make([]*structs.DiscoveryRoute, 0, len(router.Routes)+1),
|
Routes: make([]*structs.DiscoveryRoute, 0, len(router.Routes)+1),
|
||||||
}
|
}
|
||||||
c.usesAdvancedRoutingFeatures = true
|
c.usesAdvancedRoutingFeatures = true
|
||||||
if err := c.recordServiceProtocol(router.Name); err != nil {
|
if err := c.recordServiceProtocol(routerID); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -543,19 +554,20 @@ func (c *compiler) assembleChain() error {
|
||||||
dest := route.Destination
|
dest := route.Destination
|
||||||
|
|
||||||
svc := defaultIfEmpty(dest.Service, c.serviceName)
|
svc := defaultIfEmpty(dest.Service, c.serviceName)
|
||||||
|
destNamespace := defaultIfEmpty(dest.Namespace, c.evaluateInNamespace)
|
||||||
|
|
||||||
// Check to see if the destination is eligible for splitting.
|
// Check to see if the destination is eligible for splitting.
|
||||||
var (
|
var (
|
||||||
node *structs.DiscoveryGraphNode
|
node *structs.DiscoveryGraphNode
|
||||||
err error
|
err error
|
||||||
)
|
)
|
||||||
if dest.ServiceSubset == "" && dest.Namespace == "" {
|
if dest.ServiceSubset == "" {
|
||||||
node, err = c.getSplitterOrResolverNode(
|
node, err = c.getSplitterOrResolverNode(
|
||||||
c.newTarget(svc, dest.ServiceSubset, dest.Namespace, ""),
|
c.newTarget(svc, "", destNamespace, ""),
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
node, err = c.getResolverNode(
|
node, err = c.getResolverNode(
|
||||||
c.newTarget(svc, dest.ServiceSubset, dest.Namespace, ""),
|
c.newTarget(svc, dest.ServiceSubset, destNamespace, ""),
|
||||||
false,
|
false,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -573,7 +585,7 @@ func (c *compiler) assembleChain() error {
|
||||||
}
|
}
|
||||||
|
|
||||||
defaultRoute := &structs.DiscoveryRoute{
|
defaultRoute := &structs.DiscoveryRoute{
|
||||||
Definition: newDefaultServiceRoute(c.serviceName),
|
Definition: newDefaultServiceRoute(c.serviceName, c.evaluateInNamespace),
|
||||||
NextNode: defaultDestinationNode.MapKey(),
|
NextNode: defaultDestinationNode.MapKey(),
|
||||||
}
|
}
|
||||||
routeNode.Routes = append(routeNode.Routes, defaultRoute)
|
routeNode.Routes = append(routeNode.Routes, defaultRoute)
|
||||||
|
@ -584,7 +596,7 @@ func (c *compiler) assembleChain() error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func newDefaultServiceRoute(serviceName string) *structs.ServiceRoute {
|
func newDefaultServiceRoute(serviceName string, namespace string) *structs.ServiceRoute {
|
||||||
return &structs.ServiceRoute{
|
return &structs.ServiceRoute{
|
||||||
Match: &structs.ServiceRouteMatch{
|
Match: &structs.ServiceRouteMatch{
|
||||||
HTTP: &structs.ServiceRouteHTTPMatch{
|
HTTP: &structs.ServiceRouteHTTPMatch{
|
||||||
|
@ -592,7 +604,8 @@ func newDefaultServiceRoute(serviceName string) *structs.ServiceRoute {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Destination: &structs.ServiceRouteDestination{
|
Destination: &structs.ServiceRouteDestination{
|
||||||
Service: serviceName,
|
Service: serviceName,
|
||||||
|
Namespace: namespace,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -652,7 +665,7 @@ func (c *compiler) rewriteTarget(t *structs.DiscoveryTarget, service, serviceSub
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *compiler) getSplitterOrResolverNode(target *structs.DiscoveryTarget) (*structs.DiscoveryGraphNode, error) {
|
func (c *compiler) getSplitterOrResolverNode(target *structs.DiscoveryTarget) (*structs.DiscoveryGraphNode, error) {
|
||||||
nextNode, err := c.getSplitterNode(target.Service)
|
nextNode, err := c.getSplitterNode(target.ServiceID())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
} else if nextNode != nil {
|
} else if nextNode != nil {
|
||||||
|
@ -661,14 +674,15 @@ func (c *compiler) getSplitterOrResolverNode(target *structs.DiscoveryTarget) (*
|
||||||
return c.getResolverNode(target, false)
|
return c.getResolverNode(target, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *compiler) getSplitterNode(name string) (*structs.DiscoveryGraphNode, error) {
|
func (c *compiler) getSplitterNode(sid structs.ServiceID) (*structs.DiscoveryGraphNode, error) {
|
||||||
|
name := serviceIDString(sid)
|
||||||
// Do we already have the node?
|
// Do we already have the node?
|
||||||
if prev, ok := c.splitterNodes[name]; ok {
|
if prev, ok := c.splitterNodes[name]; ok {
|
||||||
return prev, nil
|
return prev, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fetch the config entry.
|
// Fetch the config entry.
|
||||||
splitter := c.entries.GetSplitter(name)
|
splitter := c.entries.GetSplitter(sid)
|
||||||
if splitter != nil && c.disableAdvancedRoutingFeatures {
|
if splitter != nil && c.disableAdvancedRoutingFeatures {
|
||||||
splitter = nil
|
splitter = nil
|
||||||
c.customizedBy.Protocol = true
|
c.customizedBy.Protocol = true
|
||||||
|
@ -694,10 +708,15 @@ func (c *compiler) getSplitterNode(name string) (*structs.DiscoveryGraphNode, er
|
||||||
}
|
}
|
||||||
splitNode.Splits = append(splitNode.Splits, compiledSplit)
|
splitNode.Splits = append(splitNode.Splits, compiledSplit)
|
||||||
|
|
||||||
svc := defaultIfEmpty(split.Service, name)
|
svc := defaultIfEmpty(split.Service, sid.ID)
|
||||||
|
splitID := structs.ServiceID{
|
||||||
|
ID: svc,
|
||||||
|
EnterpriseMeta: *split.GetEnterpriseMeta(&sid.EnterpriseMeta),
|
||||||
|
}
|
||||||
|
|
||||||
// Check to see if the split is eligible for additional splitting.
|
// Check to see if the split is eligible for additional splitting.
|
||||||
if svc != name && split.ServiceSubset == "" && split.Namespace == "" {
|
if !splitID.Matches(&sid) && split.ServiceSubset == "" {
|
||||||
nextNode, err := c.getSplitterNode(svc)
|
nextNode, err := c.getSplitterNode(splitID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
} else if nextNode != nil {
|
} else if nextNode != nil {
|
||||||
|
@ -708,7 +727,7 @@ func (c *compiler) getSplitterNode(name string) (*structs.DiscoveryGraphNode, er
|
||||||
}
|
}
|
||||||
|
|
||||||
node, err := c.getResolverNode(
|
node, err := c.getResolverNode(
|
||||||
c.newTarget(svc, split.ServiceSubset, split.Namespace, ""),
|
c.newTarget(splitID.ID, split.ServiceSubset, splitID.NamespaceOrDefault(), ""),
|
||||||
false,
|
false,
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -738,16 +757,18 @@ RESOLVE_AGAIN:
|
||||||
return prev, nil
|
return prev, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := c.recordServiceProtocol(target.Service); err != nil {
|
targetID := target.ServiceID()
|
||||||
|
|
||||||
|
if err := c.recordServiceProtocol(targetID); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fetch the config entry.
|
// Fetch the config entry.
|
||||||
resolver, ok := c.resolvers[target.Service]
|
resolver, ok := c.resolvers[targetID]
|
||||||
if !ok {
|
if !ok {
|
||||||
// Materialize defaults and cache.
|
// Materialize defaults and cache.
|
||||||
resolver = newDefaultServiceResolver(target.Service)
|
resolver = newDefaultServiceResolver(targetID)
|
||||||
c.resolvers[target.Service] = resolver
|
c.resolvers[targetID] = resolver
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, ok := redirectHistory[target.ID]; ok {
|
if _, ok := redirectHistory[target.ID]; ok {
|
||||||
|
@ -829,7 +850,7 @@ RESOLVE_AGAIN:
|
||||||
|
|
||||||
target.Subset = resolver.Subsets[target.ServiceSubset]
|
target.Subset = resolver.Subsets[target.ServiceSubset]
|
||||||
|
|
||||||
if serviceDefault := c.entries.GetService(target.Service); serviceDefault != nil && serviceDefault.ExternalSNI != "" {
|
if serviceDefault := c.entries.GetService(targetID); serviceDefault != nil && serviceDefault.ExternalSNI != "" {
|
||||||
// Override the default SNI value.
|
// Override the default SNI value.
|
||||||
target.SNI = serviceDefault.ExternalSNI
|
target.SNI = serviceDefault.ExternalSNI
|
||||||
target.External = true
|
target.External = true
|
||||||
|
@ -873,7 +894,7 @@ RESOLVE_AGAIN:
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
// Default mesh gateway settings
|
// Default mesh gateway settings
|
||||||
if serviceDefault := c.entries.GetService(target.Service); serviceDefault != nil {
|
if serviceDefault := c.entries.GetService(targetID); serviceDefault != nil {
|
||||||
target.MeshGateway = serviceDefault.MeshGateway
|
target.MeshGateway = serviceDefault.MeshGateway
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -967,10 +988,11 @@ RESOLVE_AGAIN:
|
||||||
return node, nil
|
return node, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func newDefaultServiceResolver(serviceName string) *structs.ServiceResolverConfigEntry {
|
func newDefaultServiceResolver(sid structs.ServiceID) *structs.ServiceResolverConfigEntry {
|
||||||
return &structs.ServiceResolverConfigEntry{
|
return &structs.ServiceResolverConfigEntry{
|
||||||
Kind: structs.ServiceResolver,
|
Kind: structs.ServiceResolver,
|
||||||
Name: serviceName,
|
Name: sid.ID,
|
||||||
|
EnterpriseMeta: sid.EnterpriseMeta,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,9 @@
|
||||||
|
// +build !consulent
|
||||||
|
|
||||||
|
package discoverychain
|
||||||
|
|
||||||
|
import "github.com/hashicorp/consul/agent/structs"
|
||||||
|
|
||||||
|
func (c *compiler) GetEnterpriseMeta() *structs.EnterpriseMeta {
|
||||||
|
return structs.DefaultEnterpriseMeta()
|
||||||
|
}
|
|
@ -154,14 +154,14 @@ func testcase_JustRouterWithDefaults() compileTestCase {
|
||||||
|
|
||||||
expect := &structs.CompiledDiscoveryChain{
|
expect := &structs.CompiledDiscoveryChain{
|
||||||
Protocol: "http",
|
Protocol: "http",
|
||||||
StartNode: "router:main",
|
StartNode: "router:main.default",
|
||||||
Nodes: map[string]*structs.DiscoveryGraphNode{
|
Nodes: map[string]*structs.DiscoveryGraphNode{
|
||||||
"router:main": &structs.DiscoveryGraphNode{
|
"router:main.default": &structs.DiscoveryGraphNode{
|
||||||
Type: structs.DiscoveryGraphNodeTypeRouter,
|
Type: structs.DiscoveryGraphNodeTypeRouter,
|
||||||
Name: "main",
|
Name: "main.default",
|
||||||
Routes: []*structs.DiscoveryRoute{
|
Routes: []*structs.DiscoveryRoute{
|
||||||
{
|
{
|
||||||
Definition: newDefaultServiceRoute("main"),
|
Definition: newDefaultServiceRoute("main", "default"),
|
||||||
NextNode: "resolver:main.default.dc1",
|
NextNode: "resolver:main.default.dc1",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -204,14 +204,14 @@ func testcase_RouterWithDefaults_NoSplit_WithResolver() compileTestCase {
|
||||||
|
|
||||||
expect := &structs.CompiledDiscoveryChain{
|
expect := &structs.CompiledDiscoveryChain{
|
||||||
Protocol: "http",
|
Protocol: "http",
|
||||||
StartNode: "router:main",
|
StartNode: "router:main.default",
|
||||||
Nodes: map[string]*structs.DiscoveryGraphNode{
|
Nodes: map[string]*structs.DiscoveryGraphNode{
|
||||||
"router:main": &structs.DiscoveryGraphNode{
|
"router:main.default": &structs.DiscoveryGraphNode{
|
||||||
Type: structs.DiscoveryGraphNodeTypeRouter,
|
Type: structs.DiscoveryGraphNodeTypeRouter,
|
||||||
Name: "main",
|
Name: "main.default",
|
||||||
Routes: []*structs.DiscoveryRoute{
|
Routes: []*structs.DiscoveryRoute{
|
||||||
{
|
{
|
||||||
Definition: newDefaultServiceRoute("main"),
|
Definition: newDefaultServiceRoute("main", "default"),
|
||||||
NextNode: "resolver:main.default.dc1",
|
NextNode: "resolver:main.default.dc1",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -255,21 +255,21 @@ func testcase_RouterWithDefaults_WithNoopSplit_DefaultResolver() compileTestCase
|
||||||
|
|
||||||
expect := &structs.CompiledDiscoveryChain{
|
expect := &structs.CompiledDiscoveryChain{
|
||||||
Protocol: "http",
|
Protocol: "http",
|
||||||
StartNode: "router:main",
|
StartNode: "router:main.default",
|
||||||
Nodes: map[string]*structs.DiscoveryGraphNode{
|
Nodes: map[string]*structs.DiscoveryGraphNode{
|
||||||
"router:main": &structs.DiscoveryGraphNode{
|
"router:main.default": &structs.DiscoveryGraphNode{
|
||||||
Type: structs.DiscoveryGraphNodeTypeRouter,
|
Type: structs.DiscoveryGraphNodeTypeRouter,
|
||||||
Name: "main",
|
Name: "main.default",
|
||||||
Routes: []*structs.DiscoveryRoute{
|
Routes: []*structs.DiscoveryRoute{
|
||||||
{
|
{
|
||||||
Definition: newDefaultServiceRoute("main"),
|
Definition: newDefaultServiceRoute("main", "default"),
|
||||||
NextNode: "splitter:main",
|
NextNode: "splitter:main.default",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"splitter:main": &structs.DiscoveryGraphNode{
|
"splitter:main.default": &structs.DiscoveryGraphNode{
|
||||||
Type: structs.DiscoveryGraphNodeTypeSplitter,
|
Type: structs.DiscoveryGraphNodeTypeSplitter,
|
||||||
Name: "main",
|
Name: "main.default",
|
||||||
Splits: []*structs.DiscoverySplit{
|
Splits: []*structs.DiscoverySplit{
|
||||||
{
|
{
|
||||||
Weight: 100,
|
Weight: 100,
|
||||||
|
@ -317,21 +317,21 @@ func testcase_NoopSplit_DefaultResolver_ProtocolFromProxyDefaults() compileTestC
|
||||||
|
|
||||||
expect := &structs.CompiledDiscoveryChain{
|
expect := &structs.CompiledDiscoveryChain{
|
||||||
Protocol: "http",
|
Protocol: "http",
|
||||||
StartNode: "router:main",
|
StartNode: "router:main.default",
|
||||||
Nodes: map[string]*structs.DiscoveryGraphNode{
|
Nodes: map[string]*structs.DiscoveryGraphNode{
|
||||||
"router:main": &structs.DiscoveryGraphNode{
|
"router:main.default": &structs.DiscoveryGraphNode{
|
||||||
Type: structs.DiscoveryGraphNodeTypeRouter,
|
Type: structs.DiscoveryGraphNodeTypeRouter,
|
||||||
Name: "main",
|
Name: "main.default",
|
||||||
Routes: []*structs.DiscoveryRoute{
|
Routes: []*structs.DiscoveryRoute{
|
||||||
{
|
{
|
||||||
Definition: newDefaultServiceRoute("main"),
|
Definition: newDefaultServiceRoute("main", "default"),
|
||||||
NextNode: "splitter:main",
|
NextNode: "splitter:main.default",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"splitter:main": &structs.DiscoveryGraphNode{
|
"splitter:main.default": &structs.DiscoveryGraphNode{
|
||||||
Type: structs.DiscoveryGraphNodeTypeSplitter,
|
Type: structs.DiscoveryGraphNodeTypeSplitter,
|
||||||
Name: "main",
|
Name: "main.default",
|
||||||
Splits: []*structs.DiscoverySplit{
|
Splits: []*structs.DiscoverySplit{
|
||||||
{
|
{
|
||||||
Weight: 100,
|
Weight: 100,
|
||||||
|
@ -386,21 +386,21 @@ func testcase_RouterWithDefaults_WithNoopSplit_WithResolver() compileTestCase {
|
||||||
|
|
||||||
expect := &structs.CompiledDiscoveryChain{
|
expect := &structs.CompiledDiscoveryChain{
|
||||||
Protocol: "http",
|
Protocol: "http",
|
||||||
StartNode: "router:main",
|
StartNode: "router:main.default",
|
||||||
Nodes: map[string]*structs.DiscoveryGraphNode{
|
Nodes: map[string]*structs.DiscoveryGraphNode{
|
||||||
"router:main": &structs.DiscoveryGraphNode{
|
"router:main.default": &structs.DiscoveryGraphNode{
|
||||||
Type: structs.DiscoveryGraphNodeTypeRouter,
|
Type: structs.DiscoveryGraphNodeTypeRouter,
|
||||||
Name: "main",
|
Name: "main.default",
|
||||||
Routes: []*structs.DiscoveryRoute{
|
Routes: []*structs.DiscoveryRoute{
|
||||||
{
|
{
|
||||||
Definition: newDefaultServiceRoute("main"),
|
Definition: newDefaultServiceRoute("main", "default"),
|
||||||
NextNode: "splitter:main",
|
NextNode: "splitter:main.default",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"splitter:main": &structs.DiscoveryGraphNode{
|
"splitter:main.default": &structs.DiscoveryGraphNode{
|
||||||
Type: structs.DiscoveryGraphNodeTypeSplitter,
|
Type: structs.DiscoveryGraphNodeTypeSplitter,
|
||||||
Name: "main",
|
Name: "main.default",
|
||||||
Splits: []*structs.DiscoverySplit{
|
Splits: []*structs.DiscoverySplit{
|
||||||
{
|
{
|
||||||
Weight: 100,
|
Weight: 100,
|
||||||
|
@ -463,22 +463,22 @@ func testcase_RouteBypassesSplit() compileTestCase {
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
router := entries.GetRouter("main")
|
router := entries.GetRouter(structs.NewServiceID("main", nil))
|
||||||
|
|
||||||
expect := &structs.CompiledDiscoveryChain{
|
expect := &structs.CompiledDiscoveryChain{
|
||||||
Protocol: "http",
|
Protocol: "http",
|
||||||
StartNode: "router:main",
|
StartNode: "router:main.default",
|
||||||
Nodes: map[string]*structs.DiscoveryGraphNode{
|
Nodes: map[string]*structs.DiscoveryGraphNode{
|
||||||
"router:main": &structs.DiscoveryGraphNode{
|
"router:main.default": &structs.DiscoveryGraphNode{
|
||||||
Type: structs.DiscoveryGraphNodeTypeRouter,
|
Type: structs.DiscoveryGraphNodeTypeRouter,
|
||||||
Name: "main",
|
Name: "main.default",
|
||||||
Routes: []*structs.DiscoveryRoute{
|
Routes: []*structs.DiscoveryRoute{
|
||||||
{
|
{
|
||||||
Definition: &router.Routes[0],
|
Definition: &router.Routes[0],
|
||||||
NextNode: "resolver:bypass.other.default.dc1",
|
NextNode: "resolver:bypass.other.default.dc1",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Definition: newDefaultServiceRoute("main"),
|
Definition: newDefaultServiceRoute("main", "default"),
|
||||||
NextNode: "resolver:main.default.dc1",
|
NextNode: "resolver:main.default.dc1",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -530,11 +530,11 @@ func testcase_NoopSplit_DefaultResolver() compileTestCase {
|
||||||
|
|
||||||
expect := &structs.CompiledDiscoveryChain{
|
expect := &structs.CompiledDiscoveryChain{
|
||||||
Protocol: "http",
|
Protocol: "http",
|
||||||
StartNode: "splitter:main",
|
StartNode: "splitter:main.default",
|
||||||
Nodes: map[string]*structs.DiscoveryGraphNode{
|
Nodes: map[string]*structs.DiscoveryGraphNode{
|
||||||
"splitter:main": &structs.DiscoveryGraphNode{
|
"splitter:main.default": &structs.DiscoveryGraphNode{
|
||||||
Type: structs.DiscoveryGraphNodeTypeSplitter,
|
Type: structs.DiscoveryGraphNodeTypeSplitter,
|
||||||
Name: "main",
|
Name: "main.default",
|
||||||
Splits: []*structs.DiscoverySplit{
|
Splits: []*structs.DiscoverySplit{
|
||||||
{
|
{
|
||||||
Weight: 100,
|
Weight: 100,
|
||||||
|
@ -583,11 +583,11 @@ func testcase_NoopSplit_WithResolver() compileTestCase {
|
||||||
|
|
||||||
expect := &structs.CompiledDiscoveryChain{
|
expect := &structs.CompiledDiscoveryChain{
|
||||||
Protocol: "http",
|
Protocol: "http",
|
||||||
StartNode: "splitter:main",
|
StartNode: "splitter:main.default",
|
||||||
Nodes: map[string]*structs.DiscoveryGraphNode{
|
Nodes: map[string]*structs.DiscoveryGraphNode{
|
||||||
"splitter:main": &structs.DiscoveryGraphNode{
|
"splitter:main.default": &structs.DiscoveryGraphNode{
|
||||||
Type: structs.DiscoveryGraphNodeTypeSplitter,
|
Type: structs.DiscoveryGraphNodeTypeSplitter,
|
||||||
Name: "main",
|
Name: "main.default",
|
||||||
Splits: []*structs.DiscoverySplit{
|
Splits: []*structs.DiscoverySplit{
|
||||||
{
|
{
|
||||||
Weight: 100,
|
Weight: 100,
|
||||||
|
@ -643,11 +643,11 @@ func testcase_SubsetSplit() compileTestCase {
|
||||||
|
|
||||||
expect := &structs.CompiledDiscoveryChain{
|
expect := &structs.CompiledDiscoveryChain{
|
||||||
Protocol: "http",
|
Protocol: "http",
|
||||||
StartNode: "splitter:main",
|
StartNode: "splitter:main.default",
|
||||||
Nodes: map[string]*structs.DiscoveryGraphNode{
|
Nodes: map[string]*structs.DiscoveryGraphNode{
|
||||||
"splitter:main": &structs.DiscoveryGraphNode{
|
"splitter:main.default": &structs.DiscoveryGraphNode{
|
||||||
Type: structs.DiscoveryGraphNodeTypeSplitter,
|
Type: structs.DiscoveryGraphNodeTypeSplitter,
|
||||||
Name: "main",
|
Name: "main.default",
|
||||||
Splits: []*structs.DiscoverySplit{
|
Splits: []*structs.DiscoverySplit{
|
||||||
{
|
{
|
||||||
Weight: 60,
|
Weight: 60,
|
||||||
|
@ -712,11 +712,11 @@ func testcase_ServiceSplit() compileTestCase {
|
||||||
|
|
||||||
expect := &structs.CompiledDiscoveryChain{
|
expect := &structs.CompiledDiscoveryChain{
|
||||||
Protocol: "http",
|
Protocol: "http",
|
||||||
StartNode: "splitter:main",
|
StartNode: "splitter:main.default",
|
||||||
Nodes: map[string]*structs.DiscoveryGraphNode{
|
Nodes: map[string]*structs.DiscoveryGraphNode{
|
||||||
"splitter:main": &structs.DiscoveryGraphNode{
|
"splitter:main.default": &structs.DiscoveryGraphNode{
|
||||||
Type: structs.DiscoveryGraphNodeTypeSplitter,
|
Type: structs.DiscoveryGraphNodeTypeSplitter,
|
||||||
Name: "main",
|
Name: "main.default",
|
||||||
Splits: []*structs.DiscoverySplit{
|
Splits: []*structs.DiscoverySplit{
|
||||||
{
|
{
|
||||||
Weight: 60,
|
Weight: 60,
|
||||||
|
@ -801,11 +801,11 @@ func testcase_SplitBypassesSplit() compileTestCase {
|
||||||
|
|
||||||
expect := &structs.CompiledDiscoveryChain{
|
expect := &structs.CompiledDiscoveryChain{
|
||||||
Protocol: "http",
|
Protocol: "http",
|
||||||
StartNode: "splitter:main",
|
StartNode: "splitter:main.default",
|
||||||
Nodes: map[string]*structs.DiscoveryGraphNode{
|
Nodes: map[string]*structs.DiscoveryGraphNode{
|
||||||
"splitter:main": &structs.DiscoveryGraphNode{
|
"splitter:main.default": &structs.DiscoveryGraphNode{
|
||||||
Type: structs.DiscoveryGraphNodeTypeSplitter,
|
Type: structs.DiscoveryGraphNodeTypeSplitter,
|
||||||
Name: "main",
|
Name: "main.default",
|
||||||
Splits: []*structs.DiscoverySplit{
|
Splits: []*structs.DiscoverySplit{
|
||||||
{
|
{
|
||||||
Weight: 100,
|
Weight: 100,
|
||||||
|
@ -1278,11 +1278,11 @@ func testcase_NoopSplit_WithDefaultSubset() compileTestCase {
|
||||||
|
|
||||||
expect := &structs.CompiledDiscoveryChain{
|
expect := &structs.CompiledDiscoveryChain{
|
||||||
Protocol: "http",
|
Protocol: "http",
|
||||||
StartNode: "splitter:main",
|
StartNode: "splitter:main.default",
|
||||||
Nodes: map[string]*structs.DiscoveryGraphNode{
|
Nodes: map[string]*structs.DiscoveryGraphNode{
|
||||||
"splitter:main": &structs.DiscoveryGraphNode{
|
"splitter:main.default": &structs.DiscoveryGraphNode{
|
||||||
Type: structs.DiscoveryGraphNodeTypeSplitter,
|
Type: structs.DiscoveryGraphNodeTypeSplitter,
|
||||||
Name: "main",
|
Name: "main.default",
|
||||||
Splits: []*structs.DiscoverySplit{
|
Splits: []*structs.DiscoverySplit{
|
||||||
{
|
{
|
||||||
Weight: 100,
|
Weight: 100,
|
||||||
|
@ -1586,11 +1586,11 @@ func testcase_MultiDatacenterCanary() compileTestCase {
|
||||||
|
|
||||||
expect := &structs.CompiledDiscoveryChain{
|
expect := &structs.CompiledDiscoveryChain{
|
||||||
Protocol: "http",
|
Protocol: "http",
|
||||||
StartNode: "splitter:main",
|
StartNode: "splitter:main.default",
|
||||||
Nodes: map[string]*structs.DiscoveryGraphNode{
|
Nodes: map[string]*structs.DiscoveryGraphNode{
|
||||||
"splitter:main": &structs.DiscoveryGraphNode{
|
"splitter:main.default": &structs.DiscoveryGraphNode{
|
||||||
Type: structs.DiscoveryGraphNodeTypeSplitter,
|
Type: structs.DiscoveryGraphNodeTypeSplitter,
|
||||||
Name: "main",
|
Name: "main.default",
|
||||||
Splits: []*structs.DiscoverySplit{
|
Splits: []*structs.DiscoverySplit{
|
||||||
{
|
{
|
||||||
Weight: 60,
|
Weight: 60,
|
||||||
|
@ -1712,16 +1712,16 @@ func testcase_AllBellsAndWhistles() compileTestCase {
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
router = entries.GetRouter("main")
|
router = entries.GetRouter(structs.NewServiceID("main", nil))
|
||||||
)
|
)
|
||||||
|
|
||||||
expect := &structs.CompiledDiscoveryChain{
|
expect := &structs.CompiledDiscoveryChain{
|
||||||
Protocol: "http",
|
Protocol: "http",
|
||||||
StartNode: "router:main",
|
StartNode: "router:main.default",
|
||||||
Nodes: map[string]*structs.DiscoveryGraphNode{
|
Nodes: map[string]*structs.DiscoveryGraphNode{
|
||||||
"router:main": &structs.DiscoveryGraphNode{
|
"router:main.default": &structs.DiscoveryGraphNode{
|
||||||
Type: structs.DiscoveryGraphNodeTypeRouter,
|
Type: structs.DiscoveryGraphNodeTypeRouter,
|
||||||
Name: "main",
|
Name: "main.default",
|
||||||
Routes: []*structs.DiscoveryRoute{
|
Routes: []*structs.DiscoveryRoute{
|
||||||
{
|
{
|
||||||
Definition: &router.Routes[0],
|
Definition: &router.Routes[0],
|
||||||
|
@ -1729,17 +1729,17 @@ func testcase_AllBellsAndWhistles() compileTestCase {
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Definition: &router.Routes[1],
|
Definition: &router.Routes[1],
|
||||||
NextNode: "splitter:svc-split",
|
NextNode: "splitter:svc-split.default",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Definition: newDefaultServiceRoute("main"),
|
Definition: newDefaultServiceRoute("main", "default"),
|
||||||
NextNode: "resolver:default-subset.main.default.dc1",
|
NextNode: "resolver:default-subset.main.default.dc1",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"splitter:svc-split": &structs.DiscoveryGraphNode{
|
"splitter:svc-split.default": &structs.DiscoveryGraphNode{
|
||||||
Type: structs.DiscoveryGraphNodeTypeSplitter,
|
Type: structs.DiscoveryGraphNodeTypeSplitter,
|
||||||
Name: "svc-split",
|
Name: "svc-split.default",
|
||||||
Splits: []*structs.DiscoverySplit{
|
Splits: []*structs.DiscoverySplit{
|
||||||
{
|
{
|
||||||
Weight: 60,
|
Weight: 60,
|
||||||
|
@ -2191,9 +2191,9 @@ func setServiceProtocol(entries *structs.DiscoveryChainConfigEntries, name, prot
|
||||||
|
|
||||||
func newEntries() *structs.DiscoveryChainConfigEntries {
|
func newEntries() *structs.DiscoveryChainConfigEntries {
|
||||||
return &structs.DiscoveryChainConfigEntries{
|
return &structs.DiscoveryChainConfigEntries{
|
||||||
Routers: make(map[string]*structs.ServiceRouterConfigEntry),
|
Routers: make(map[structs.ServiceID]*structs.ServiceRouterConfigEntry),
|
||||||
Splitters: make(map[string]*structs.ServiceSplitterConfigEntry),
|
Splitters: make(map[structs.ServiceID]*structs.ServiceSplitterConfigEntry),
|
||||||
Resolvers: make(map[string]*structs.ServiceResolverConfigEntry),
|
Resolvers: make(map[structs.ServiceID]*structs.ServiceResolverConfigEntry),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -456,7 +456,7 @@ func (c *FSM) applyConfigEntryOperation(buf []byte, index uint64) interface{} {
|
||||||
case structs.ConfigEntryUpsertCAS:
|
case structs.ConfigEntryUpsertCAS:
|
||||||
defer metrics.MeasureSinceWithLabels([]string{"fsm", "config_entry", req.Entry.GetKind()}, time.Now(),
|
defer metrics.MeasureSinceWithLabels([]string{"fsm", "config_entry", req.Entry.GetKind()}, time.Now(),
|
||||||
[]metrics.Label{{Name: "op", Value: "upsert"}})
|
[]metrics.Label{{Name: "op", Value: "upsert"}})
|
||||||
updated, err := c.state.EnsureConfigEntryCAS(index, req.Entry.GetRaftIndex().ModifyIndex, req.Entry)
|
updated, err := c.state.EnsureConfigEntryCAS(index, req.Entry.GetRaftIndex().ModifyIndex, req.Entry, req.Entry.GetEnterpriseMeta())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -464,14 +464,14 @@ func (c *FSM) applyConfigEntryOperation(buf []byte, index uint64) interface{} {
|
||||||
case structs.ConfigEntryUpsert:
|
case structs.ConfigEntryUpsert:
|
||||||
defer metrics.MeasureSinceWithLabels([]string{"fsm", "config_entry", req.Entry.GetKind()}, time.Now(),
|
defer metrics.MeasureSinceWithLabels([]string{"fsm", "config_entry", req.Entry.GetKind()}, time.Now(),
|
||||||
[]metrics.Label{{Name: "op", Value: "upsert"}})
|
[]metrics.Label{{Name: "op", Value: "upsert"}})
|
||||||
if err := c.state.EnsureConfigEntry(index, req.Entry); err != nil {
|
if err := c.state.EnsureConfigEntry(index, req.Entry, req.Entry.GetEnterpriseMeta()); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
case structs.ConfigEntryDelete:
|
case structs.ConfigEntryDelete:
|
||||||
defer metrics.MeasureSinceWithLabels([]string{"fsm", "config_entry", req.Entry.GetKind()}, time.Now(),
|
defer metrics.MeasureSinceWithLabels([]string{"fsm", "config_entry", req.Entry.GetKind()}, time.Now(),
|
||||||
[]metrics.Label{{Name: "op", Value: "delete"}})
|
[]metrics.Label{{Name: "op", Value: "delete"}})
|
||||||
return c.state.DeleteConfigEntry(index, req.Entry.GetKind(), req.Entry.GetName())
|
return c.state.DeleteConfigEntry(index, req.Entry.GetKind(), req.Entry.GetName(), req.Entry.GetEnterpriseMeta())
|
||||||
default:
|
default:
|
||||||
return fmt.Errorf("invalid config entry operation type: %v", req.Op)
|
return fmt.Errorf("invalid config entry operation type: %v", req.Op)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1422,6 +1422,7 @@ func TestFSM_ConfigEntry(t *testing.T) {
|
||||||
Config: map[string]interface{}{
|
Config: map[string]interface{}{
|
||||||
"foo": "bar",
|
"foo": "bar",
|
||||||
},
|
},
|
||||||
|
EnterpriseMeta: *structs.DefaultEnterpriseMeta(),
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create a new request.
|
// Create a new request.
|
||||||
|
@ -1441,7 +1442,7 @@ func TestFSM_ConfigEntry(t *testing.T) {
|
||||||
|
|
||||||
// Verify it's in the state store.
|
// Verify it's in the state store.
|
||||||
{
|
{
|
||||||
_, config, err := fsm.state.ConfigEntry(nil, structs.ProxyDefaults, "global")
|
_, config, err := fsm.state.ConfigEntry(nil, structs.ProxyDefaults, "global", nil)
|
||||||
require.NoError(err)
|
require.NoError(err)
|
||||||
entry.RaftIndex.CreateIndex = 1
|
entry.RaftIndex.CreateIndex = 1
|
||||||
entry.RaftIndex.ModifyIndex = 1
|
entry.RaftIndex.ModifyIndex = 1
|
||||||
|
|
|
@ -240,8 +240,8 @@ func TestFSM_SnapshotRestore_OSS(t *testing.T) {
|
||||||
Kind: structs.ProxyDefaults,
|
Kind: structs.ProxyDefaults,
|
||||||
Name: "global",
|
Name: "global",
|
||||||
}
|
}
|
||||||
require.NoError(fsm.state.EnsureConfigEntry(18, serviceConfig))
|
require.NoError(fsm.state.EnsureConfigEntry(18, serviceConfig, structs.DefaultEnterpriseMeta()))
|
||||||
require.NoError(fsm.state.EnsureConfigEntry(19, proxyConfig))
|
require.NoError(fsm.state.EnsureConfigEntry(19, proxyConfig, structs.DefaultEnterpriseMeta()))
|
||||||
|
|
||||||
chunkState := &raftchunking.State{
|
chunkState := &raftchunking.State{
|
||||||
ChunkMap: make(raftchunking.ChunkMap),
|
ChunkMap: make(raftchunking.ChunkMap),
|
||||||
|
@ -479,11 +479,11 @@ func TestFSM_SnapshotRestore_OSS(t *testing.T) {
|
||||||
assert.Equal(caConfig, caConf)
|
assert.Equal(caConfig, caConf)
|
||||||
|
|
||||||
// Verify config entries are restored
|
// Verify config entries are restored
|
||||||
_, serviceConfEntry, err := fsm2.state.ConfigEntry(nil, structs.ServiceDefaults, "foo")
|
_, serviceConfEntry, err := fsm2.state.ConfigEntry(nil, structs.ServiceDefaults, "foo", structs.DefaultEnterpriseMeta())
|
||||||
require.NoError(err)
|
require.NoError(err)
|
||||||
assert.Equal(serviceConfig, serviceConfEntry)
|
assert.Equal(serviceConfig, serviceConfEntry)
|
||||||
|
|
||||||
_, proxyConfEntry, err := fsm2.state.ConfigEntry(nil, structs.ProxyDefaults, "global")
|
_, proxyConfEntry, err := fsm2.state.ConfigEntry(nil, structs.ProxyDefaults, "global", structs.DefaultEnterpriseMeta())
|
||||||
require.NoError(err)
|
require.NoError(err)
|
||||||
assert.Equal(proxyConfig, proxyConfEntry)
|
assert.Equal(proxyConfig, proxyConfEntry)
|
||||||
|
|
||||||
|
|
|
@ -1105,6 +1105,8 @@ service "foo" {
|
||||||
Op: structs.IntentionOpCreate,
|
Op: structs.IntentionOpCreate,
|
||||||
Intention: structs.TestIntention(t),
|
Intention: structs.TestIntention(t),
|
||||||
}
|
}
|
||||||
|
ixn.Intention.SourceNS = "default"
|
||||||
|
ixn.Intention.DestinationNS = "default"
|
||||||
ixn.Intention.DestinationName = name
|
ixn.Intention.DestinationName = name
|
||||||
ixn.WriteRequest.Token = "root"
|
ixn.WriteRequest.Token = "root"
|
||||||
|
|
||||||
|
@ -1230,13 +1232,7 @@ func TestIntentionMatch_good(t *testing.T) {
|
||||||
func TestIntentionMatch_acl(t *testing.T) {
|
func TestIntentionMatch_acl(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
assert := assert.New(t)
|
dir1, s1 := testACLServerWithConfig(t, nil, false)
|
||||||
dir1, s1 := testServerWithConfig(t, func(c *Config) {
|
|
||||||
c.ACLDatacenter = "dc1"
|
|
||||||
c.ACLsEnabled = true
|
|
||||||
c.ACLMasterToken = "root"
|
|
||||||
c.ACLDefaultPolicy = "deny"
|
|
||||||
})
|
|
||||||
defer os.RemoveAll(dir1)
|
defer os.RemoveAll(dir1)
|
||||||
defer s1.Shutdown()
|
defer s1.Shutdown()
|
||||||
codec := rpcClient(t, s1)
|
codec := rpcClient(t, s1)
|
||||||
|
@ -1244,37 +1240,15 @@ func TestIntentionMatch_acl(t *testing.T) {
|
||||||
|
|
||||||
testrpc.WaitForLeader(t, s1.RPC, "dc1")
|
testrpc.WaitForLeader(t, s1.RPC, "dc1")
|
||||||
|
|
||||||
// Create an ACL with service write permissions. This will grant
|
token, err := upsertTestTokenWithPolicyRules(codec, TestDefaultMasterToken, "dc1", `service "bar" { policy = "write" }`)
|
||||||
// intentions read.
|
require.NoError(t, err)
|
||||||
var token string
|
|
||||||
{
|
|
||||||
var rules = `
|
|
||||||
service "bar" {
|
|
||||||
policy = "write"
|
|
||||||
}`
|
|
||||||
|
|
||||||
req := structs.ACLRequest{
|
|
||||||
Datacenter: "dc1",
|
|
||||||
Op: structs.ACLSet,
|
|
||||||
ACL: structs.ACL{
|
|
||||||
Name: "User token",
|
|
||||||
Type: structs.ACLTokenTypeClient,
|
|
||||||
Rules: rules,
|
|
||||||
},
|
|
||||||
WriteRequest: structs.WriteRequest{Token: "root"},
|
|
||||||
}
|
|
||||||
assert.Nil(msgpackrpc.CallWithCodec(codec, "ACL.Apply", &req, &token))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create some records
|
// Create some records
|
||||||
{
|
{
|
||||||
insert := [][]string{
|
insert := []string{
|
||||||
{"foo", "*"},
|
"*",
|
||||||
{"foo", "bar"},
|
"bar",
|
||||||
{"foo", "baz"}, // shouldn't match
|
"baz",
|
||||||
{"bar", "bar"}, // shouldn't match
|
|
||||||
{"bar", "*"}, // shouldn't match
|
|
||||||
{"*", "*"},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, v := range insert {
|
for _, v := range insert {
|
||||||
|
@ -1283,13 +1257,12 @@ service "bar" {
|
||||||
Op: structs.IntentionOpCreate,
|
Op: structs.IntentionOpCreate,
|
||||||
Intention: structs.TestIntention(t),
|
Intention: structs.TestIntention(t),
|
||||||
}
|
}
|
||||||
ixn.Intention.DestinationNS = v[0]
|
ixn.Intention.DestinationName = v
|
||||||
ixn.Intention.DestinationName = v[1]
|
ixn.WriteRequest.Token = TestDefaultMasterToken
|
||||||
ixn.WriteRequest.Token = "root"
|
|
||||||
|
|
||||||
// Create
|
// Create
|
||||||
var reply string
|
var reply string
|
||||||
assert.Nil(msgpackrpc.CallWithCodec(codec, "Intention.Apply", &ixn, &reply))
|
require.Nil(t, msgpackrpc.CallWithCodec(codec, "Intention.Apply", &ixn, &reply))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1301,7 +1274,7 @@ service "bar" {
|
||||||
Type: structs.IntentionMatchDestination,
|
Type: structs.IntentionMatchDestination,
|
||||||
Entries: []structs.IntentionMatchEntry{
|
Entries: []structs.IntentionMatchEntry{
|
||||||
{
|
{
|
||||||
Namespace: "foo",
|
Namespace: "default",
|
||||||
Name: "bar",
|
Name: "bar",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -1309,8 +1282,8 @@ service "bar" {
|
||||||
}
|
}
|
||||||
var resp structs.IndexedIntentionMatches
|
var resp structs.IndexedIntentionMatches
|
||||||
err := msgpackrpc.CallWithCodec(codec, "Intention.Match", req, &resp)
|
err := msgpackrpc.CallWithCodec(codec, "Intention.Match", req, &resp)
|
||||||
assert.True(acl.IsErrPermissionDenied(err))
|
require.True(t, acl.IsErrPermissionDenied(err))
|
||||||
assert.Len(resp.Matches, 0)
|
require.Len(t, resp.Matches, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test with proper token
|
// Test with proper token
|
||||||
|
@ -1321,24 +1294,24 @@ service "bar" {
|
||||||
Type: structs.IntentionMatchDestination,
|
Type: structs.IntentionMatchDestination,
|
||||||
Entries: []structs.IntentionMatchEntry{
|
Entries: []structs.IntentionMatchEntry{
|
||||||
{
|
{
|
||||||
Namespace: "foo",
|
Namespace: "default",
|
||||||
Name: "bar",
|
Name: "bar",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
QueryOptions: structs.QueryOptions{Token: token},
|
QueryOptions: structs.QueryOptions{Token: token.SecretID},
|
||||||
}
|
}
|
||||||
var resp structs.IndexedIntentionMatches
|
var resp structs.IndexedIntentionMatches
|
||||||
assert.Nil(msgpackrpc.CallWithCodec(codec, "Intention.Match", req, &resp))
|
require.NoError(t, msgpackrpc.CallWithCodec(codec, "Intention.Match", req, &resp))
|
||||||
assert.Len(resp.Matches, 1)
|
require.Len(t, resp.Matches, 1)
|
||||||
|
|
||||||
expected := [][]string{{"foo", "bar"}, {"foo", "*"}, {"*", "*"}}
|
expected := []string{"bar", "*"}
|
||||||
var actual [][]string
|
var actual []string
|
||||||
for _, ixn := range resp.Matches[0] {
|
for _, ixn := range resp.Matches[0] {
|
||||||
actual = append(actual, []string{ixn.DestinationNS, ixn.DestinationName})
|
actual = append(actual, ixn.DestinationName)
|
||||||
}
|
}
|
||||||
|
|
||||||
assert.Equal(expected, actual)
|
require.ElementsMatch(t, expected, actual)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -957,7 +957,7 @@ func (s *Server) bootstrapConfigEntries(entries []structs.ConfigEntry) error {
|
||||||
state := s.fsm.State()
|
state := s.fsm.State()
|
||||||
for _, entry := range entries {
|
for _, entry := range entries {
|
||||||
// avoid a round trip through Raft if we know the CAS is going to fail
|
// avoid a round trip through Raft if we know the CAS is going to fail
|
||||||
_, existing, err := state.ConfigEntry(nil, entry.GetKind(), entry.GetName())
|
_, existing, err := state.ConfigEntry(nil, entry.GetKind(), entry.GetName(), entry.GetEnterpriseMeta())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("Failed to determine whether the configuration for %q / %q already exists: %v", entry.GetKind(), entry.GetName(), err)
|
return fmt.Errorf("Failed to determine whether the configuration for %q / %q already exists: %v", entry.GetKind(), entry.GetName(), err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1153,7 +1153,7 @@ func TestLeader_ConfigEntryBootstrap(t *testing.T) {
|
||||||
testrpc.WaitForTestAgent(t, s1.RPC, "dc1")
|
testrpc.WaitForTestAgent(t, s1.RPC, "dc1")
|
||||||
|
|
||||||
retry.Run(t, func(t *retry.R) {
|
retry.Run(t, func(t *retry.R) {
|
||||||
_, entry, err := s1.fsm.State().ConfigEntry(nil, structs.ProxyDefaults, structs.ProxyConfigGlobal)
|
_, entry, err := s1.fsm.State().ConfigEntry(nil, structs.ProxyDefaults, structs.ProxyConfigGlobal, structs.DefaultEnterpriseMeta())
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NotNil(t, entry)
|
require.NotNil(t, entry)
|
||||||
global, ok := entry.(*structs.ProxyConfigEntry)
|
global, ok := entry.(*structs.ProxyConfigEntry)
|
||||||
|
|
|
@ -1135,7 +1135,7 @@ func TestServer_Reload(t *testing.T) {
|
||||||
|
|
||||||
s.ReloadConfig(s.config)
|
s.ReloadConfig(s.config)
|
||||||
|
|
||||||
_, entry, err := s.fsm.State().ConfigEntry(nil, structs.ProxyDefaults, structs.ProxyConfigGlobal)
|
_, entry, err := s.fsm.State().ConfigEntry(nil, structs.ProxyDefaults, structs.ProxyConfigGlobal, structs.DefaultEnterpriseMeta())
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NotNil(t, entry)
|
require.NotNil(t, entry)
|
||||||
global, ok := entry.(*structs.ProxyConfigEntry)
|
global, ok := entry.(*structs.ProxyConfigEntry)
|
||||||
|
|
|
@ -748,6 +748,32 @@ func (s *Store) Services(ws memdb.WatchSet, entMeta *structs.EnterpriseMeta) (ui
|
||||||
return idx, results, nil
|
return idx, results, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *Store) ServiceList(ws memdb.WatchSet, entMeta *structs.EnterpriseMeta) (uint64, structs.ServiceList, error) {
|
||||||
|
tx := s.db.Txn(false)
|
||||||
|
defer tx.Abort()
|
||||||
|
|
||||||
|
idx := s.catalogServicesMaxIndex(tx, entMeta)
|
||||||
|
|
||||||
|
services, err := s.catalogServiceList(tx, entMeta, true)
|
||||||
|
if err != nil {
|
||||||
|
return 0, nil, fmt.Errorf("failed querying services: %s", err)
|
||||||
|
}
|
||||||
|
ws.Add(services.WatchCh())
|
||||||
|
|
||||||
|
unique := make(map[structs.ServiceID]struct{})
|
||||||
|
for service := services.Next(); service != nil; service = services.Next() {
|
||||||
|
svc := service.(*structs.ServiceNode)
|
||||||
|
unique[svc.CompoundServiceName()] = struct{}{}
|
||||||
|
}
|
||||||
|
|
||||||
|
results := make(structs.ServiceList, 0, len(unique))
|
||||||
|
for sid, _ := range unique {
|
||||||
|
results = append(results, structs.ServiceInfo{Name: sid.ID, EnterpriseMeta: sid.EnterpriseMeta})
|
||||||
|
}
|
||||||
|
|
||||||
|
return idx, results, nil
|
||||||
|
}
|
||||||
|
|
||||||
// ServicesByNodeMeta returns all services, filtered by the given node metadata.
|
// ServicesByNodeMeta returns all services, filtered by the given node metadata.
|
||||||
func (s *Store) ServicesByNodeMeta(ws memdb.WatchSet, filters map[string]string, entMeta *structs.EnterpriseMeta) (uint64, structs.Services, error) {
|
func (s *Store) ServicesByNodeMeta(ws memdb.WatchSet, filters map[string]string, entMeta *structs.EnterpriseMeta) (uint64, structs.Services, error) {
|
||||||
tx := s.db.Txn(false)
|
tx := s.db.Txn(false)
|
||||||
|
|
|
@ -12,48 +12,6 @@ const (
|
||||||
configTableName = "config-entries"
|
configTableName = "config-entries"
|
||||||
)
|
)
|
||||||
|
|
||||||
// configTableSchema returns a new table schema used to store global
|
|
||||||
// config entries.
|
|
||||||
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,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
"kind": &memdb.IndexSchema{
|
|
||||||
Name: "kind",
|
|
||||||
AllowMissing: false,
|
|
||||||
Unique: false,
|
|
||||||
Indexer: &memdb.StringFieldIndex{
|
|
||||||
Field: "Kind",
|
|
||||||
Lowercase: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
"link": &memdb.IndexSchema{
|
|
||||||
Name: "link",
|
|
||||||
AllowMissing: true,
|
|
||||||
Unique: false,
|
|
||||||
Indexer: &ConfigEntryLinkIndex{},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type ConfigEntryLinkIndex struct {
|
type ConfigEntryLinkIndex struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -61,7 +19,7 @@ type discoveryChainConfigEntry interface {
|
||||||
structs.ConfigEntry
|
structs.ConfigEntry
|
||||||
// ListRelatedServices returns a list of other names of services referenced
|
// ListRelatedServices returns a list of other names of services referenced
|
||||||
// in this config entry.
|
// in this config entry.
|
||||||
ListRelatedServices() []string
|
ListRelatedServices() []structs.ServiceID
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *ConfigEntryLinkIndex) FromObject(obj interface{}) (bool, [][]byte, error) {
|
func (s *ConfigEntryLinkIndex) FromObject(obj interface{}) (bool, [][]byte, error) {
|
||||||
|
@ -84,7 +42,7 @@ func (s *ConfigEntryLinkIndex) FromObject(obj interface{}) (bool, [][]byte, erro
|
||||||
|
|
||||||
vals := make([][]byte, 0, numLinks)
|
vals := make([][]byte, 0, numLinks)
|
||||||
for _, linkedService := range linkedServices {
|
for _, linkedService := range linkedServices {
|
||||||
vals = append(vals, []byte(linkedService+"\x00"))
|
vals = append(vals, []byte(linkedService.String()+"\x00"))
|
||||||
}
|
}
|
||||||
|
|
||||||
return true, vals, nil
|
return true, vals, nil
|
||||||
|
@ -94,13 +52,12 @@ func (s *ConfigEntryLinkIndex) FromArgs(args ...interface{}) ([]byte, error) {
|
||||||
if len(args) != 1 {
|
if len(args) != 1 {
|
||||||
return nil, fmt.Errorf("must provide only a single argument")
|
return nil, fmt.Errorf("must provide only a single argument")
|
||||||
}
|
}
|
||||||
arg, ok := args[0].(string)
|
arg, ok := args[0].(structs.ServiceID)
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil, fmt.Errorf("argument must be a string: %#v", args[0])
|
return nil, fmt.Errorf("argument must be a structs.ServiceID: %#v", args[0])
|
||||||
}
|
}
|
||||||
// Add the null character as a terminator
|
// Add the null character as a terminator
|
||||||
arg += "\x00"
|
return []byte(arg.String() + "\x00"), nil
|
||||||
return []byte(arg), nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *ConfigEntryLinkIndex) PrefixFromArgs(args ...interface{}) ([]byte, error) {
|
func (s *ConfigEntryLinkIndex) PrefixFromArgs(args ...interface{}) ([]byte, error) {
|
||||||
|
@ -150,18 +107,18 @@ func (s *Restore) ConfigEntry(c structs.ConfigEntry) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// ConfigEntry is called to get a given config entry.
|
// ConfigEntry is called to get a given config entry.
|
||||||
func (s *Store) ConfigEntry(ws memdb.WatchSet, kind, name string) (uint64, structs.ConfigEntry, error) {
|
func (s *Store) ConfigEntry(ws memdb.WatchSet, kind, name string, entMeta *structs.EnterpriseMeta) (uint64, structs.ConfigEntry, error) {
|
||||||
tx := s.db.Txn(false)
|
tx := s.db.Txn(false)
|
||||||
defer tx.Abort()
|
defer tx.Abort()
|
||||||
return s.configEntryTxn(tx, ws, kind, name)
|
return s.configEntryTxn(tx, ws, kind, name, entMeta)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Store) configEntryTxn(tx *memdb.Txn, ws memdb.WatchSet, kind, name string) (uint64, structs.ConfigEntry, error) {
|
func (s *Store) configEntryTxn(tx *memdb.Txn, ws memdb.WatchSet, kind, name string, entMeta *structs.EnterpriseMeta) (uint64, structs.ConfigEntry, error) {
|
||||||
// Get the index
|
// Get the index
|
||||||
idx := maxIndexTxn(tx, configTableName)
|
idx := maxIndexTxn(tx, configTableName)
|
||||||
|
|
||||||
// Get the existing config entry.
|
// Get the existing config entry.
|
||||||
watchCh, existing, err := tx.FirstWatch(configTableName, "id", kind, name)
|
watchCh, existing, err := s.firstWatchConfigEntryWithTxn(tx, kind, name, entMeta)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, nil, fmt.Errorf("failed config entry lookup: %s", err)
|
return 0, nil, fmt.Errorf("failed config entry lookup: %s", err)
|
||||||
}
|
}
|
||||||
|
@ -179,19 +136,19 @@ func (s *Store) configEntryTxn(tx *memdb.Txn, ws memdb.WatchSet, kind, name stri
|
||||||
}
|
}
|
||||||
|
|
||||||
// ConfigEntries is called to get all config entry objects.
|
// ConfigEntries is called to get all config entry objects.
|
||||||
func (s *Store) ConfigEntries(ws memdb.WatchSet) (uint64, []structs.ConfigEntry, error) {
|
func (s *Store) ConfigEntries(ws memdb.WatchSet, entMeta *structs.EnterpriseMeta) (uint64, []structs.ConfigEntry, error) {
|
||||||
return s.ConfigEntriesByKind(ws, "")
|
return s.ConfigEntriesByKind(ws, "", entMeta)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ConfigEntriesByKind is called to get all config entry objects with the given kind.
|
// ConfigEntriesByKind is called to get all config entry objects with the given kind.
|
||||||
// If kind is empty, all config entries will be returned.
|
// If kind is empty, all config entries will be returned.
|
||||||
func (s *Store) ConfigEntriesByKind(ws memdb.WatchSet, kind string) (uint64, []structs.ConfigEntry, error) {
|
func (s *Store) ConfigEntriesByKind(ws memdb.WatchSet, kind string, entMeta *structs.EnterpriseMeta) (uint64, []structs.ConfigEntry, error) {
|
||||||
tx := s.db.Txn(false)
|
tx := s.db.Txn(false)
|
||||||
defer tx.Abort()
|
defer tx.Abort()
|
||||||
return s.configEntriesByKindTxn(tx, ws, kind)
|
return s.configEntriesByKindTxn(tx, ws, kind, entMeta)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Store) configEntriesByKindTxn(tx *memdb.Txn, ws memdb.WatchSet, kind string) (uint64, []structs.ConfigEntry, error) {
|
func (s *Store) configEntriesByKindTxn(tx *memdb.Txn, ws memdb.WatchSet, kind string, entMeta *structs.EnterpriseMeta) (uint64, []structs.ConfigEntry, error) {
|
||||||
// Get the index
|
// Get the index
|
||||||
idx := maxIndexTxn(tx, configTableName)
|
idx := maxIndexTxn(tx, configTableName)
|
||||||
|
|
||||||
|
@ -199,9 +156,9 @@ func (s *Store) configEntriesByKindTxn(tx *memdb.Txn, ws memdb.WatchSet, kind st
|
||||||
var iter memdb.ResultIterator
|
var iter memdb.ResultIterator
|
||||||
var err error
|
var err error
|
||||||
if kind != "" {
|
if kind != "" {
|
||||||
iter, err = tx.Get(configTableName, "kind", kind)
|
iter, err = getConfigEntryKindsWithTxn(tx, kind, entMeta)
|
||||||
} else {
|
} else {
|
||||||
iter, err = tx.Get(configTableName, "id")
|
iter, err = getAllConfigEntriesWithTxn(tx, entMeta)
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, nil, fmt.Errorf("failed config entry lookup: %s", err)
|
return 0, nil, fmt.Errorf("failed config entry lookup: %s", err)
|
||||||
|
@ -216,11 +173,11 @@ func (s *Store) configEntriesByKindTxn(tx *memdb.Txn, ws memdb.WatchSet, kind st
|
||||||
}
|
}
|
||||||
|
|
||||||
// EnsureConfigEntry is called to do an upsert of a given config entry.
|
// EnsureConfigEntry is called to do an upsert of a given config entry.
|
||||||
func (s *Store) EnsureConfigEntry(idx uint64, conf structs.ConfigEntry) error {
|
func (s *Store) EnsureConfigEntry(idx uint64, conf structs.ConfigEntry, entMeta *structs.EnterpriseMeta) error {
|
||||||
tx := s.db.Txn(true)
|
tx := s.db.Txn(true)
|
||||||
defer tx.Abort()
|
defer tx.Abort()
|
||||||
|
|
||||||
if err := s.ensureConfigEntryTxn(tx, idx, conf); err != nil {
|
if err := s.ensureConfigEntryTxn(tx, idx, conf, entMeta); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -229,9 +186,9 @@ func (s *Store) EnsureConfigEntry(idx uint64, conf structs.ConfigEntry) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// ensureConfigEntryTxn upserts a config entry inside of a transaction.
|
// ensureConfigEntryTxn upserts a config entry inside of a transaction.
|
||||||
func (s *Store) ensureConfigEntryTxn(tx *memdb.Txn, idx uint64, conf structs.ConfigEntry) error {
|
func (s *Store) ensureConfigEntryTxn(tx *memdb.Txn, idx uint64, conf structs.ConfigEntry, entMeta *structs.EnterpriseMeta) error {
|
||||||
// Check for existing configuration.
|
// Check for existing configuration.
|
||||||
existing, err := tx.First(configTableName, "id", conf.GetKind(), conf.GetName())
|
existing, err := s.firstConfigEntryWithTxn(tx, conf.GetKind(), conf.GetName(), entMeta)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed configuration lookup: %s", err)
|
return fmt.Errorf("failed configuration lookup: %s", err)
|
||||||
}
|
}
|
||||||
|
@ -252,13 +209,14 @@ func (s *Store) ensureConfigEntryTxn(tx *memdb.Txn, idx uint64, conf structs.Con
|
||||||
conf.GetKind(),
|
conf.GetKind(),
|
||||||
conf.GetName(),
|
conf.GetName(),
|
||||||
conf,
|
conf,
|
||||||
|
entMeta,
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err // Err is already sufficiently decorated.
|
return err // Err is already sufficiently decorated.
|
||||||
}
|
}
|
||||||
|
|
||||||
// Insert the config entry and update the index
|
// Insert the config entry and update the index
|
||||||
if err := tx.Insert(configTableName, conf); err != nil {
|
if err := s.insertConfigEntryWithTxn(tx, conf); err != nil {
|
||||||
return fmt.Errorf("failed inserting config entry: %s", err)
|
return fmt.Errorf("failed inserting config entry: %s", err)
|
||||||
}
|
}
|
||||||
if err := indexUpdateMaxTxn(tx, idx, configTableName); err != nil {
|
if err := indexUpdateMaxTxn(tx, idx, configTableName); err != nil {
|
||||||
|
@ -269,12 +227,12 @@ func (s *Store) ensureConfigEntryTxn(tx *memdb.Txn, idx uint64, conf structs.Con
|
||||||
}
|
}
|
||||||
|
|
||||||
// EnsureConfigEntryCAS is called to do a check-and-set upsert of a given config entry.
|
// 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) {
|
func (s *Store) EnsureConfigEntryCAS(idx, cidx uint64, conf structs.ConfigEntry, entMeta *structs.EnterpriseMeta) (bool, error) {
|
||||||
tx := s.db.Txn(true)
|
tx := s.db.Txn(true)
|
||||||
defer tx.Abort()
|
defer tx.Abort()
|
||||||
|
|
||||||
// Check for existing configuration.
|
// Check for existing configuration.
|
||||||
existing, err := tx.First(configTableName, "id", conf.GetKind(), conf.GetName())
|
existing, err := s.firstConfigEntryWithTxn(tx, conf.GetKind(), conf.GetName(), entMeta)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, fmt.Errorf("failed configuration lookup: %s", err)
|
return false, fmt.Errorf("failed configuration lookup: %s", err)
|
||||||
}
|
}
|
||||||
|
@ -295,7 +253,7 @@ func (s *Store) EnsureConfigEntryCAS(idx, cidx uint64, conf structs.ConfigEntry)
|
||||||
return false, nil
|
return false, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := s.ensureConfigEntryTxn(tx, idx, conf); err != nil {
|
if err := s.ensureConfigEntryTxn(tx, idx, conf, entMeta); err != nil {
|
||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -303,12 +261,12 @@ func (s *Store) EnsureConfigEntryCAS(idx, cidx uint64, conf structs.ConfigEntry)
|
||||||
return true, nil
|
return true, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Store) DeleteConfigEntry(idx uint64, kind, name string) error {
|
func (s *Store) DeleteConfigEntry(idx uint64, kind, name string, entMeta *structs.EnterpriseMeta) error {
|
||||||
tx := s.db.Txn(true)
|
tx := s.db.Txn(true)
|
||||||
defer tx.Abort()
|
defer tx.Abort()
|
||||||
|
|
||||||
// Try to retrieve the existing config entry.
|
// Try to retrieve the existing config entry.
|
||||||
existing, err := tx.First(configTableName, "id", kind, name)
|
existing, err := s.firstConfigEntryWithTxn(tx, kind, name, entMeta)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed config entry lookup: %s", err)
|
return fmt.Errorf("failed config entry lookup: %s", err)
|
||||||
}
|
}
|
||||||
|
@ -322,6 +280,7 @@ func (s *Store) DeleteConfigEntry(idx uint64, kind, name string) error {
|
||||||
kind,
|
kind,
|
||||||
name,
|
name,
|
||||||
nil,
|
nil,
|
||||||
|
entMeta,
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err // Err is already sufficiently decorated.
|
return err // Err is already sufficiently decorated.
|
||||||
|
@ -351,7 +310,9 @@ func (s *Store) validateProposedConfigEntryInGraph(
|
||||||
idx uint64,
|
idx uint64,
|
||||||
kind, name string,
|
kind, name string,
|
||||||
next structs.ConfigEntry,
|
next structs.ConfigEntry,
|
||||||
|
entMeta *structs.EnterpriseMeta,
|
||||||
) error {
|
) error {
|
||||||
|
|
||||||
validateAllChains := false
|
validateAllChains := false
|
||||||
|
|
||||||
switch kind {
|
switch kind {
|
||||||
|
@ -368,7 +329,7 @@ func (s *Store) validateProposedConfigEntryInGraph(
|
||||||
return fmt.Errorf("unhandled kind %q during validation of %q", kind, name)
|
return fmt.Errorf("unhandled kind %q during validation of %q", kind, name)
|
||||||
}
|
}
|
||||||
|
|
||||||
return s.validateProposedConfigEntryInServiceGraph(tx, idx, kind, name, next, validateAllChains)
|
return s.validateProposedConfigEntryInServiceGraph(tx, idx, kind, name, next, validateAllChains, entMeta)
|
||||||
}
|
}
|
||||||
|
|
||||||
var serviceGraphKinds = []string{
|
var serviceGraphKinds = []string{
|
||||||
|
@ -383,10 +344,11 @@ func (s *Store) validateProposedConfigEntryInServiceGraph(
|
||||||
kind, name string,
|
kind, name string,
|
||||||
next structs.ConfigEntry,
|
next structs.ConfigEntry,
|
||||||
validateAllChains bool,
|
validateAllChains bool,
|
||||||
|
entMeta *structs.EnterpriseMeta,
|
||||||
) error {
|
) error {
|
||||||
// Collect all of the chains that could be affected by this change
|
// Collect all of the chains that could be affected by this change
|
||||||
// including our own.
|
// including our own.
|
||||||
checkChains := make(map[string]struct{})
|
checkChains := make(map[structs.ServiceID]struct{})
|
||||||
|
|
||||||
if validateAllChains {
|
if validateAllChains {
|
||||||
// Must be proxy-defaults/global.
|
// Must be proxy-defaults/global.
|
||||||
|
@ -395,23 +357,24 @@ func (s *Store) validateProposedConfigEntryInServiceGraph(
|
||||||
// somehow omit the ones that have a default protocol configured.
|
// somehow omit the ones that have a default protocol configured.
|
||||||
|
|
||||||
for _, kind := range serviceGraphKinds {
|
for _, kind := range serviceGraphKinds {
|
||||||
_, entries, err := s.configEntriesByKindTxn(tx, nil, kind)
|
_, entries, err := s.configEntriesByKindTxn(tx, nil, kind, entMeta)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
for _, entry := range entries {
|
for _, entry := range entries {
|
||||||
checkChains[entry.GetName()] = struct{}{}
|
checkChains[structs.NewServiceID(entry.GetName(), entry.GetEnterpriseMeta())] = struct{}{}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Must be a single chain.
|
// Must be a single chain.
|
||||||
|
|
||||||
checkChains[name] = struct{}{}
|
sid := structs.NewServiceID(name, entMeta)
|
||||||
|
checkChains[sid] = struct{}{}
|
||||||
|
|
||||||
iter, err := tx.Get(configTableName, "link", name)
|
iter, err := tx.Get(configTableName, "link", sid)
|
||||||
for raw := iter.Next(); raw != nil; raw = iter.Next() {
|
for raw := iter.Next(); raw != nil; raw = iter.Next() {
|
||||||
entry := raw.(structs.ConfigEntry)
|
entry := raw.(structs.ConfigEntry)
|
||||||
checkChains[entry.GetName()] = struct{}{}
|
checkChains[structs.NewServiceID(entry.GetName(), entry.GetEnterpriseMeta())] = struct{}{}
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -422,8 +385,8 @@ func (s *Store) validateProposedConfigEntryInServiceGraph(
|
||||||
{Kind: kind, Name: name}: next,
|
{Kind: kind, Name: name}: next,
|
||||||
}
|
}
|
||||||
|
|
||||||
for chainName, _ := range checkChains {
|
for chain, _ := range checkChains {
|
||||||
if err := s.testCompileDiscoveryChain(tx, nil, chainName, overrides); err != nil {
|
if err := s.testCompileDiscoveryChain(tx, nil, chain.ID, overrides, &chain.EnterpriseMeta); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -436,8 +399,9 @@ func (s *Store) testCompileDiscoveryChain(
|
||||||
ws memdb.WatchSet,
|
ws memdb.WatchSet,
|
||||||
chainName string,
|
chainName string,
|
||||||
overrides map[structs.ConfigEntryKindName]structs.ConfigEntry,
|
overrides map[structs.ConfigEntryKindName]structs.ConfigEntry,
|
||||||
|
entMeta *structs.EnterpriseMeta,
|
||||||
) error {
|
) error {
|
||||||
_, speculativeEntries, err := s.readDiscoveryChainConfigEntriesTxn(tx, nil, chainName, overrides)
|
_, speculativeEntries, err := s.readDiscoveryChainConfigEntriesTxn(tx, nil, chainName, overrides, entMeta)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -448,7 +412,7 @@ func (s *Store) testCompileDiscoveryChain(
|
||||||
// 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
|
// 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
|
||||||
req := discoverychain.CompileRequest{
|
req := discoverychain.CompileRequest{
|
||||||
ServiceName: chainName,
|
ServiceName: chainName,
|
||||||
EvaluateInNamespace: "default",
|
EvaluateInNamespace: entMeta.NamespaceOrDefault(),
|
||||||
EvaluateInDatacenter: "dc1",
|
EvaluateInDatacenter: "dc1",
|
||||||
EvaluateInTrustDomain: "b6fc9da3-03d4-4b5a-9134-c045e9b20152.consul",
|
EvaluateInTrustDomain: "b6fc9da3-03d4-4b5a-9134-c045e9b20152.consul",
|
||||||
UseInDatacenter: "dc1",
|
UseInDatacenter: "dc1",
|
||||||
|
@ -467,8 +431,9 @@ func (s *Store) testCompileDiscoveryChain(
|
||||||
func (s *Store) ReadDiscoveryChainConfigEntries(
|
func (s *Store) ReadDiscoveryChainConfigEntries(
|
||||||
ws memdb.WatchSet,
|
ws memdb.WatchSet,
|
||||||
serviceName string,
|
serviceName string,
|
||||||
|
entMeta *structs.EnterpriseMeta,
|
||||||
) (uint64, *structs.DiscoveryChainConfigEntries, error) {
|
) (uint64, *structs.DiscoveryChainConfigEntries, error) {
|
||||||
return s.readDiscoveryChainConfigEntries(ws, serviceName, nil)
|
return s.readDiscoveryChainConfigEntries(ws, serviceName, nil, entMeta)
|
||||||
}
|
}
|
||||||
|
|
||||||
// readDiscoveryChainConfigEntries will query for the full discovery chain for
|
// readDiscoveryChainConfigEntries will query for the full discovery chain for
|
||||||
|
@ -485,10 +450,11 @@ func (s *Store) readDiscoveryChainConfigEntries(
|
||||||
ws memdb.WatchSet,
|
ws memdb.WatchSet,
|
||||||
serviceName string,
|
serviceName string,
|
||||||
overrides map[structs.ConfigEntryKindName]structs.ConfigEntry,
|
overrides map[structs.ConfigEntryKindName]structs.ConfigEntry,
|
||||||
|
entMeta *structs.EnterpriseMeta,
|
||||||
) (uint64, *structs.DiscoveryChainConfigEntries, error) {
|
) (uint64, *structs.DiscoveryChainConfigEntries, error) {
|
||||||
tx := s.db.Txn(false)
|
tx := s.db.Txn(false)
|
||||||
defer tx.Abort()
|
defer tx.Abort()
|
||||||
return s.readDiscoveryChainConfigEntriesTxn(tx, ws, serviceName, overrides)
|
return s.readDiscoveryChainConfigEntriesTxn(tx, ws, serviceName, overrides, entMeta)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Store) readDiscoveryChainConfigEntriesTxn(
|
func (s *Store) readDiscoveryChainConfigEntriesTxn(
|
||||||
|
@ -496,6 +462,7 @@ func (s *Store) readDiscoveryChainConfigEntriesTxn(
|
||||||
ws memdb.WatchSet,
|
ws memdb.WatchSet,
|
||||||
serviceName string,
|
serviceName string,
|
||||||
overrides map[structs.ConfigEntryKindName]structs.ConfigEntry,
|
overrides map[structs.ConfigEntryKindName]structs.ConfigEntry,
|
||||||
|
entMeta *structs.EnterpriseMeta,
|
||||||
) (uint64, *structs.DiscoveryChainConfigEntries, error) {
|
) (uint64, *structs.DiscoveryChainConfigEntries, error) {
|
||||||
res := structs.NewDiscoveryChainConfigEntries()
|
res := structs.NewDiscoveryChainConfigEntries()
|
||||||
|
|
||||||
|
@ -511,13 +478,15 @@ func (s *Store) readDiscoveryChainConfigEntriesTxn(
|
||||||
// the end of this function to indicate "no such entry".
|
// the end of this function to indicate "no such entry".
|
||||||
|
|
||||||
var (
|
var (
|
||||||
todoSplitters = make(map[string]struct{})
|
todoSplitters = make(map[structs.ServiceID]struct{})
|
||||||
todoResolvers = make(map[string]struct{})
|
todoResolvers = make(map[structs.ServiceID]struct{})
|
||||||
todoDefaults = make(map[string]struct{})
|
todoDefaults = make(map[structs.ServiceID]struct{})
|
||||||
)
|
)
|
||||||
|
|
||||||
|
sid := structs.NewServiceID(serviceName, entMeta)
|
||||||
|
|
||||||
// Grab the proxy defaults if they exist.
|
// Grab the proxy defaults if they exist.
|
||||||
idx, proxy, err := s.getProxyConfigEntryTxn(tx, ws, structs.ProxyConfigGlobal, overrides)
|
idx, proxy, err := s.getProxyConfigEntryTxn(tx, ws, structs.ProxyConfigGlobal, overrides, structs.DefaultEnterpriseMeta())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, nil, err
|
return 0, nil, err
|
||||||
} else if proxy != nil {
|
} else if proxy != nil {
|
||||||
|
@ -525,14 +494,14 @@ func (s *Store) readDiscoveryChainConfigEntriesTxn(
|
||||||
}
|
}
|
||||||
|
|
||||||
// At every step we'll need service defaults.
|
// At every step we'll need service defaults.
|
||||||
todoDefaults[serviceName] = struct{}{}
|
todoDefaults[sid] = struct{}{}
|
||||||
|
|
||||||
// first fetch the router, of which we only collect 1 per chain eval
|
// first fetch the router, of which we only collect 1 per chain eval
|
||||||
_, router, err := s.getRouterConfigEntryTxn(tx, ws, serviceName, overrides)
|
_, router, err := s.getRouterConfigEntryTxn(tx, ws, serviceName, overrides, entMeta)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, nil, err
|
return 0, nil, err
|
||||||
} else if router != nil {
|
} else if router != nil {
|
||||||
res.Routers[serviceName] = router
|
res.Routers[sid] = router
|
||||||
}
|
}
|
||||||
|
|
||||||
if router != nil {
|
if router != nil {
|
||||||
|
@ -541,39 +510,39 @@ func (s *Store) readDiscoveryChainConfigEntriesTxn(
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Next hop in the chain is the splitter.
|
// Next hop in the chain is the splitter.
|
||||||
todoSplitters[serviceName] = struct{}{}
|
todoSplitters[sid] = struct{}{}
|
||||||
}
|
}
|
||||||
|
|
||||||
for {
|
for {
|
||||||
name, ok := anyKey(todoSplitters)
|
splitID, ok := anyKey(todoSplitters)
|
||||||
if !ok {
|
if !ok {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
delete(todoSplitters, name)
|
delete(todoSplitters, splitID)
|
||||||
|
|
||||||
if _, ok := res.Splitters[name]; ok {
|
if _, ok := res.Splitters[splitID]; ok {
|
||||||
continue // already fetched
|
continue // already fetched
|
||||||
}
|
}
|
||||||
|
|
||||||
// Yes, even for splitters.
|
// Yes, even for splitters.
|
||||||
todoDefaults[name] = struct{}{}
|
todoDefaults[splitID] = struct{}{}
|
||||||
|
|
||||||
_, splitter, err := s.getSplitterConfigEntryTxn(tx, ws, name, overrides)
|
_, splitter, err := s.getSplitterConfigEntryTxn(tx, ws, splitID.ID, overrides, &splitID.EnterpriseMeta)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, nil, err
|
return 0, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if splitter == nil {
|
if splitter == nil {
|
||||||
res.Splitters[name] = nil
|
res.Splitters[splitID] = nil
|
||||||
|
|
||||||
// Next hop in the chain is the resolver.
|
// Next hop in the chain is the resolver.
|
||||||
todoResolvers[name] = struct{}{}
|
todoResolvers[splitID] = struct{}{}
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
res.Splitters[name] = splitter
|
res.Splitters[splitID] = splitter
|
||||||
|
|
||||||
todoResolvers[name] = struct{}{}
|
todoResolvers[splitID] = struct{}{}
|
||||||
for _, svc := range splitter.ListRelatedServices() {
|
for _, svc := range splitter.ListRelatedServices() {
|
||||||
// If there is no splitter, this will end up adding a resolver
|
// If there is no splitter, this will end up adding a resolver
|
||||||
// after another iteration.
|
// after another iteration.
|
||||||
|
@ -582,30 +551,30 @@ func (s *Store) readDiscoveryChainConfigEntriesTxn(
|
||||||
}
|
}
|
||||||
|
|
||||||
for {
|
for {
|
||||||
name, ok := anyKey(todoResolvers)
|
resolverID, ok := anyKey(todoResolvers)
|
||||||
if !ok {
|
if !ok {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
delete(todoResolvers, name)
|
delete(todoResolvers, resolverID)
|
||||||
|
|
||||||
if _, ok := res.Resolvers[name]; ok {
|
if _, ok := res.Resolvers[resolverID]; ok {
|
||||||
continue // already fetched
|
continue // already fetched
|
||||||
}
|
}
|
||||||
|
|
||||||
// And resolvers, too.
|
// And resolvers, too.
|
||||||
todoDefaults[name] = struct{}{}
|
todoDefaults[resolverID] = struct{}{}
|
||||||
|
|
||||||
_, resolver, err := s.getResolverConfigEntryTxn(tx, ws, name, overrides)
|
_, resolver, err := s.getResolverConfigEntryTxn(tx, ws, resolverID.ID, overrides, &resolverID.EnterpriseMeta)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, nil, err
|
return 0, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if resolver == nil {
|
if resolver == nil {
|
||||||
res.Resolvers[name] = nil
|
res.Resolvers[resolverID] = nil
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
res.Resolvers[name] = resolver
|
res.Resolvers[resolverID] = resolver
|
||||||
|
|
||||||
for _, svc := range resolver.ListRelatedServices() {
|
for _, svc := range resolver.ListRelatedServices() {
|
||||||
todoResolvers[svc] = struct{}{}
|
todoResolvers[svc] = struct{}{}
|
||||||
|
@ -613,48 +582,48 @@ func (s *Store) readDiscoveryChainConfigEntriesTxn(
|
||||||
}
|
}
|
||||||
|
|
||||||
for {
|
for {
|
||||||
name, ok := anyKey(todoDefaults)
|
svcID, ok := anyKey(todoDefaults)
|
||||||
if !ok {
|
if !ok {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
delete(todoDefaults, name)
|
delete(todoDefaults, svcID)
|
||||||
|
|
||||||
if _, ok := res.Services[name]; ok {
|
if _, ok := res.Services[svcID]; ok {
|
||||||
continue // already fetched
|
continue // already fetched
|
||||||
}
|
}
|
||||||
|
|
||||||
_, entry, err := s.getServiceConfigEntryTxn(tx, ws, name, overrides)
|
_, entry, err := s.getServiceConfigEntryTxn(tx, ws, svcID.ID, overrides, &svcID.EnterpriseMeta)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, nil, err
|
return 0, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if entry == nil {
|
if entry == nil {
|
||||||
res.Services[name] = nil
|
res.Services[svcID] = nil
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
res.Services[name] = entry
|
res.Services[svcID] = entry
|
||||||
}
|
}
|
||||||
|
|
||||||
// Strip nils now that they are no longer necessary.
|
// Strip nils now that they are no longer necessary.
|
||||||
for name, entry := range res.Routers {
|
for sid, entry := range res.Routers {
|
||||||
if entry == nil {
|
if entry == nil {
|
||||||
delete(res.Routers, name)
|
delete(res.Routers, sid)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for name, entry := range res.Splitters {
|
for sid, entry := range res.Splitters {
|
||||||
if entry == nil {
|
if entry == nil {
|
||||||
delete(res.Splitters, name)
|
delete(res.Splitters, sid)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for name, entry := range res.Resolvers {
|
for sid, entry := range res.Resolvers {
|
||||||
if entry == nil {
|
if entry == nil {
|
||||||
delete(res.Resolvers, name)
|
delete(res.Resolvers, sid)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for name, entry := range res.Services {
|
for sid, entry := range res.Services {
|
||||||
if entry == nil {
|
if entry == nil {
|
||||||
delete(res.Services, name)
|
delete(res.Services, sid)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -663,14 +632,14 @@ func (s *Store) readDiscoveryChainConfigEntriesTxn(
|
||||||
|
|
||||||
// anyKey returns any key from the provided map if any exist. Useful for using
|
// anyKey returns any key from the provided map if any exist. Useful for using
|
||||||
// a map as a simple work queue of sorts.
|
// a map as a simple work queue of sorts.
|
||||||
func anyKey(m map[string]struct{}) (string, bool) {
|
func anyKey(m map[structs.ServiceID]struct{}) (structs.ServiceID, bool) {
|
||||||
if len(m) == 0 {
|
if len(m) == 0 {
|
||||||
return "", false
|
return structs.ServiceID{}, false
|
||||||
}
|
}
|
||||||
for k, _ := range m {
|
for k, _ := range m {
|
||||||
return k, true
|
return k, true
|
||||||
}
|
}
|
||||||
return "", false
|
return structs.ServiceID{}, false
|
||||||
}
|
}
|
||||||
|
|
||||||
// getProxyConfigEntryTxn is a convenience method for fetching a
|
// getProxyConfigEntryTxn is a convenience method for fetching a
|
||||||
|
@ -682,8 +651,9 @@ func (s *Store) getProxyConfigEntryTxn(
|
||||||
ws memdb.WatchSet,
|
ws memdb.WatchSet,
|
||||||
name string,
|
name string,
|
||||||
overrides map[structs.ConfigEntryKindName]structs.ConfigEntry,
|
overrides map[structs.ConfigEntryKindName]structs.ConfigEntry,
|
||||||
|
entMeta *structs.EnterpriseMeta,
|
||||||
) (uint64, *structs.ProxyConfigEntry, error) {
|
) (uint64, *structs.ProxyConfigEntry, error) {
|
||||||
idx, entry, err := s.configEntryWithOverridesTxn(tx, ws, structs.ProxyDefaults, name, overrides)
|
idx, entry, err := s.configEntryWithOverridesTxn(tx, ws, structs.ProxyDefaults, name, overrides, entMeta)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, nil, err
|
return 0, nil, err
|
||||||
} else if entry == nil {
|
} else if entry == nil {
|
||||||
|
@ -706,8 +676,9 @@ func (s *Store) getServiceConfigEntryTxn(
|
||||||
ws memdb.WatchSet,
|
ws memdb.WatchSet,
|
||||||
serviceName string,
|
serviceName string,
|
||||||
overrides map[structs.ConfigEntryKindName]structs.ConfigEntry,
|
overrides map[structs.ConfigEntryKindName]structs.ConfigEntry,
|
||||||
|
entMeta *structs.EnterpriseMeta,
|
||||||
) (uint64, *structs.ServiceConfigEntry, error) {
|
) (uint64, *structs.ServiceConfigEntry, error) {
|
||||||
idx, entry, err := s.configEntryWithOverridesTxn(tx, ws, structs.ServiceDefaults, serviceName, overrides)
|
idx, entry, err := s.configEntryWithOverridesTxn(tx, ws, structs.ServiceDefaults, serviceName, overrides, entMeta)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, nil, err
|
return 0, nil, err
|
||||||
} else if entry == nil {
|
} else if entry == nil {
|
||||||
|
@ -730,8 +701,9 @@ func (s *Store) getRouterConfigEntryTxn(
|
||||||
ws memdb.WatchSet,
|
ws memdb.WatchSet,
|
||||||
serviceName string,
|
serviceName string,
|
||||||
overrides map[structs.ConfigEntryKindName]structs.ConfigEntry,
|
overrides map[structs.ConfigEntryKindName]structs.ConfigEntry,
|
||||||
|
entMeta *structs.EnterpriseMeta,
|
||||||
) (uint64, *structs.ServiceRouterConfigEntry, error) {
|
) (uint64, *structs.ServiceRouterConfigEntry, error) {
|
||||||
idx, entry, err := s.configEntryWithOverridesTxn(tx, ws, structs.ServiceRouter, serviceName, overrides)
|
idx, entry, err := s.configEntryWithOverridesTxn(tx, ws, structs.ServiceRouter, serviceName, overrides, entMeta)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, nil, err
|
return 0, nil, err
|
||||||
} else if entry == nil {
|
} else if entry == nil {
|
||||||
|
@ -754,8 +726,9 @@ func (s *Store) getSplitterConfigEntryTxn(
|
||||||
ws memdb.WatchSet,
|
ws memdb.WatchSet,
|
||||||
serviceName string,
|
serviceName string,
|
||||||
overrides map[structs.ConfigEntryKindName]structs.ConfigEntry,
|
overrides map[structs.ConfigEntryKindName]structs.ConfigEntry,
|
||||||
|
entMeta *structs.EnterpriseMeta,
|
||||||
) (uint64, *structs.ServiceSplitterConfigEntry, error) {
|
) (uint64, *structs.ServiceSplitterConfigEntry, error) {
|
||||||
idx, entry, err := s.configEntryWithOverridesTxn(tx, ws, structs.ServiceSplitter, serviceName, overrides)
|
idx, entry, err := s.configEntryWithOverridesTxn(tx, ws, structs.ServiceSplitter, serviceName, overrides, entMeta)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, nil, err
|
return 0, nil, err
|
||||||
} else if entry == nil {
|
} else if entry == nil {
|
||||||
|
@ -778,8 +751,9 @@ func (s *Store) getResolverConfigEntryTxn(
|
||||||
ws memdb.WatchSet,
|
ws memdb.WatchSet,
|
||||||
serviceName string,
|
serviceName string,
|
||||||
overrides map[structs.ConfigEntryKindName]structs.ConfigEntry,
|
overrides map[structs.ConfigEntryKindName]structs.ConfigEntry,
|
||||||
|
entMeta *structs.EnterpriseMeta,
|
||||||
) (uint64, *structs.ServiceResolverConfigEntry, error) {
|
) (uint64, *structs.ServiceResolverConfigEntry, error) {
|
||||||
idx, entry, err := s.configEntryWithOverridesTxn(tx, ws, structs.ServiceResolver, serviceName, overrides)
|
idx, entry, err := s.configEntryWithOverridesTxn(tx, ws, structs.ServiceResolver, serviceName, overrides, entMeta)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, nil, err
|
return 0, nil, err
|
||||||
} else if entry == nil {
|
} else if entry == nil {
|
||||||
|
@ -799,6 +773,7 @@ func (s *Store) configEntryWithOverridesTxn(
|
||||||
kind string,
|
kind string,
|
||||||
name string,
|
name string,
|
||||||
overrides map[structs.ConfigEntryKindName]structs.ConfigEntry,
|
overrides map[structs.ConfigEntryKindName]structs.ConfigEntry,
|
||||||
|
entMeta *structs.EnterpriseMeta,
|
||||||
) (uint64, structs.ConfigEntry, error) {
|
) (uint64, structs.ConfigEntry, error) {
|
||||||
if len(overrides) > 0 {
|
if len(overrides) > 0 {
|
||||||
entry, ok := overrides[structs.ConfigEntryKindName{
|
entry, ok := overrides[structs.ConfigEntryKindName{
|
||||||
|
@ -809,5 +784,5 @@ func (s *Store) configEntryWithOverridesTxn(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return s.configEntryTxn(tx, ws, kind, name)
|
return s.configEntryTxn(tx, ws, kind, name, entMeta)
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,73 @@
|
||||||
|
// +build !consulent
|
||||||
|
|
||||||
|
package state
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/hashicorp/consul/agent/structs"
|
||||||
|
memdb "github.com/hashicorp/go-memdb"
|
||||||
|
)
|
||||||
|
|
||||||
|
// configTableSchema returns a new table schema used to store global
|
||||||
|
// config entries.
|
||||||
|
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,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"kind": &memdb.IndexSchema{
|
||||||
|
Name: "kind",
|
||||||
|
AllowMissing: false,
|
||||||
|
Unique: false,
|
||||||
|
Indexer: &memdb.StringFieldIndex{
|
||||||
|
Field: "Kind",
|
||||||
|
Lowercase: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"link": &memdb.IndexSchema{
|
||||||
|
Name: "link",
|
||||||
|
AllowMissing: true,
|
||||||
|
Unique: false,
|
||||||
|
Indexer: &ConfigEntryLinkIndex{},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Store) firstConfigEntryWithTxn(tx *memdb.Txn,
|
||||||
|
kind, name string, entMeta *structs.EnterpriseMeta) (interface{}, error) {
|
||||||
|
return tx.First(configTableName, "id", kind, name)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Store) firstWatchConfigEntryWithTxn(tx *memdb.Txn,
|
||||||
|
kind, name string, entMeta *structs.EnterpriseMeta) (<-chan struct{}, interface{}, error) {
|
||||||
|
return tx.FirstWatch(configTableName, "id", kind, name)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Store) insertConfigEntryWithTxn(tx *memdb.Txn, conf structs.ConfigEntry) error {
|
||||||
|
return tx.Insert(configTableName, conf)
|
||||||
|
}
|
||||||
|
|
||||||
|
func getAllConfigEntriesWithTxn(tx *memdb.Txn, entMeta *structs.EnterpriseMeta) (memdb.ResultIterator, error) {
|
||||||
|
return tx.Get(configTableName, "id")
|
||||||
|
}
|
||||||
|
|
||||||
|
func getConfigEntryKindsWithTxn(tx *memdb.Txn,
|
||||||
|
kind string, entMeta *structs.EnterpriseMeta) (memdb.ResultIterator, error) {
|
||||||
|
return tx.Get(configTableName, "kind", kind)
|
||||||
|
}
|
|
@ -22,9 +22,9 @@ func TestStore_ConfigEntry(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create
|
// Create
|
||||||
require.NoError(s.EnsureConfigEntry(0, expected))
|
require.NoError(s.EnsureConfigEntry(0, expected, nil))
|
||||||
|
|
||||||
idx, config, err := s.ConfigEntry(nil, structs.ProxyDefaults, "global")
|
idx, config, err := s.ConfigEntry(nil, structs.ProxyDefaults, "global", nil)
|
||||||
require.NoError(err)
|
require.NoError(err)
|
||||||
require.Equal(uint64(0), idx)
|
require.Equal(uint64(0), idx)
|
||||||
require.Equal(expected, config)
|
require.Equal(expected, config)
|
||||||
|
@ -37,17 +37,17 @@ func TestStore_ConfigEntry(t *testing.T) {
|
||||||
"DestinationServiceName": "bar",
|
"DestinationServiceName": "bar",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
require.NoError(s.EnsureConfigEntry(1, updated))
|
require.NoError(s.EnsureConfigEntry(1, updated, nil))
|
||||||
|
|
||||||
idx, config, err = s.ConfigEntry(nil, structs.ProxyDefaults, "global")
|
idx, config, err = s.ConfigEntry(nil, structs.ProxyDefaults, "global", nil)
|
||||||
require.NoError(err)
|
require.NoError(err)
|
||||||
require.Equal(uint64(1), idx)
|
require.Equal(uint64(1), idx)
|
||||||
require.Equal(updated, config)
|
require.Equal(updated, config)
|
||||||
|
|
||||||
// Delete
|
// Delete
|
||||||
require.NoError(s.DeleteConfigEntry(2, structs.ProxyDefaults, "global"))
|
require.NoError(s.DeleteConfigEntry(2, structs.ProxyDefaults, "global", nil))
|
||||||
|
|
||||||
idx, config, err = s.ConfigEntry(nil, structs.ProxyDefaults, "global")
|
idx, config, err = s.ConfigEntry(nil, structs.ProxyDefaults, "global", nil)
|
||||||
require.NoError(err)
|
require.NoError(err)
|
||||||
require.Equal(uint64(2), idx)
|
require.Equal(uint64(2), idx)
|
||||||
require.Nil(config)
|
require.Nil(config)
|
||||||
|
@ -57,19 +57,19 @@ func TestStore_ConfigEntry(t *testing.T) {
|
||||||
Kind: structs.ServiceDefaults,
|
Kind: structs.ServiceDefaults,
|
||||||
Name: "foo",
|
Name: "foo",
|
||||||
}
|
}
|
||||||
require.NoError(s.EnsureConfigEntry(3, serviceConf))
|
require.NoError(s.EnsureConfigEntry(3, serviceConf, nil))
|
||||||
|
|
||||||
ws := memdb.NewWatchSet()
|
ws := memdb.NewWatchSet()
|
||||||
_, _, err = s.ConfigEntry(ws, structs.ServiceDefaults, "foo")
|
_, _, err = s.ConfigEntry(ws, structs.ServiceDefaults, "foo", nil)
|
||||||
require.NoError(err)
|
require.NoError(err)
|
||||||
|
|
||||||
// Make an unrelated modification and make sure the watch doesn't fire.
|
// Make an unrelated modification and make sure the watch doesn't fire.
|
||||||
require.NoError(s.EnsureConfigEntry(4, updated))
|
require.NoError(s.EnsureConfigEntry(4, updated, nil))
|
||||||
require.False(watchFired(ws))
|
require.False(watchFired(ws))
|
||||||
|
|
||||||
// Update the watched config and make sure it fires.
|
// Update the watched config and make sure it fires.
|
||||||
serviceConf.Protocol = "http"
|
serviceConf.Protocol = "http"
|
||||||
require.NoError(s.EnsureConfigEntry(5, serviceConf))
|
require.NoError(s.EnsureConfigEntry(5, serviceConf, nil))
|
||||||
require.True(watchFired(ws))
|
require.True(watchFired(ws))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -86,9 +86,9 @@ func TestStore_ConfigEntryCAS(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create
|
// Create
|
||||||
require.NoError(s.EnsureConfigEntry(1, expected))
|
require.NoError(s.EnsureConfigEntry(1, expected, nil))
|
||||||
|
|
||||||
idx, config, err := s.ConfigEntry(nil, structs.ProxyDefaults, "global")
|
idx, config, err := s.ConfigEntry(nil, structs.ProxyDefaults, "global", nil)
|
||||||
require.NoError(err)
|
require.NoError(err)
|
||||||
require.Equal(uint64(1), idx)
|
require.Equal(uint64(1), idx)
|
||||||
require.Equal(expected, config)
|
require.Equal(expected, config)
|
||||||
|
@ -101,23 +101,23 @@ func TestStore_ConfigEntryCAS(t *testing.T) {
|
||||||
"DestinationServiceName": "bar",
|
"DestinationServiceName": "bar",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
ok, err := s.EnsureConfigEntryCAS(2, 99, updated)
|
ok, err := s.EnsureConfigEntryCAS(2, 99, updated, nil)
|
||||||
require.False(ok)
|
require.False(ok)
|
||||||
require.NoError(err)
|
require.NoError(err)
|
||||||
|
|
||||||
// Entry should not be changed
|
// Entry should not be changed
|
||||||
idx, config, err = s.ConfigEntry(nil, structs.ProxyDefaults, "global")
|
idx, config, err = s.ConfigEntry(nil, structs.ProxyDefaults, "global", nil)
|
||||||
require.NoError(err)
|
require.NoError(err)
|
||||||
require.Equal(uint64(1), idx)
|
require.Equal(uint64(1), idx)
|
||||||
require.Equal(expected, config)
|
require.Equal(expected, config)
|
||||||
|
|
||||||
// Update with a valid index
|
// Update with a valid index
|
||||||
ok, err = s.EnsureConfigEntryCAS(2, 1, updated)
|
ok, err = s.EnsureConfigEntryCAS(2, 1, updated, nil)
|
||||||
require.True(ok)
|
require.True(ok)
|
||||||
require.NoError(err)
|
require.NoError(err)
|
||||||
|
|
||||||
// Entry should be updated
|
// Entry should be updated
|
||||||
idx, config, err = s.ConfigEntry(nil, structs.ProxyDefaults, "global")
|
idx, config, err = s.ConfigEntry(nil, structs.ProxyDefaults, "global", nil)
|
||||||
require.NoError(err)
|
require.NoError(err)
|
||||||
require.Equal(uint64(2), idx)
|
require.Equal(uint64(2), idx)
|
||||||
require.Equal(updated, config)
|
require.Equal(updated, config)
|
||||||
|
@ -141,25 +141,25 @@ func TestStore_ConfigEntries(t *testing.T) {
|
||||||
Name: "test3",
|
Name: "test3",
|
||||||
}
|
}
|
||||||
|
|
||||||
require.NoError(s.EnsureConfigEntry(0, entry1))
|
require.NoError(s.EnsureConfigEntry(0, entry1, nil))
|
||||||
require.NoError(s.EnsureConfigEntry(1, entry2))
|
require.NoError(s.EnsureConfigEntry(1, entry2, nil))
|
||||||
require.NoError(s.EnsureConfigEntry(2, entry3))
|
require.NoError(s.EnsureConfigEntry(2, entry3, nil))
|
||||||
|
|
||||||
// Get all entries
|
// Get all entries
|
||||||
idx, entries, err := s.ConfigEntries(nil)
|
idx, entries, err := s.ConfigEntries(nil, nil)
|
||||||
require.NoError(err)
|
require.NoError(err)
|
||||||
require.Equal(uint64(2), idx)
|
require.Equal(uint64(2), idx)
|
||||||
require.Equal([]structs.ConfigEntry{entry1, entry2, entry3}, entries)
|
require.Equal([]structs.ConfigEntry{entry1, entry2, entry3}, entries)
|
||||||
|
|
||||||
// Get all proxy entries
|
// Get all proxy entries
|
||||||
idx, entries, err = s.ConfigEntriesByKind(nil, structs.ProxyDefaults)
|
idx, entries, err = s.ConfigEntriesByKind(nil, structs.ProxyDefaults, nil)
|
||||||
require.NoError(err)
|
require.NoError(err)
|
||||||
require.Equal(uint64(2), idx)
|
require.Equal(uint64(2), idx)
|
||||||
require.Equal([]structs.ConfigEntry{entry1}, entries)
|
require.Equal([]structs.ConfigEntry{entry1}, entries)
|
||||||
|
|
||||||
// Get all service entries
|
// Get all service entries
|
||||||
ws := memdb.NewWatchSet()
|
ws := memdb.NewWatchSet()
|
||||||
idx, entries, err = s.ConfigEntriesByKind(ws, structs.ServiceDefaults)
|
idx, entries, err = s.ConfigEntriesByKind(ws, structs.ServiceDefaults, nil)
|
||||||
require.NoError(err)
|
require.NoError(err)
|
||||||
require.Equal(uint64(2), idx)
|
require.Equal(uint64(2), idx)
|
||||||
require.Equal([]structs.ConfigEntry{entry2, entry3}, entries)
|
require.Equal([]structs.ConfigEntry{entry2, entry3}, entries)
|
||||||
|
@ -172,20 +172,19 @@ func TestStore_ConfigEntries(t *testing.T) {
|
||||||
Kind: structs.ServiceDefaults,
|
Kind: structs.ServiceDefaults,
|
||||||
Name: "test2",
|
Name: "test2",
|
||||||
Protocol: "tcp",
|
Protocol: "tcp",
|
||||||
}))
|
}, nil))
|
||||||
require.True(watchFired(ws))
|
require.True(watchFired(ws))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestStore_ConfigEntry_GraphValidation(t *testing.T) {
|
func TestStore_ConfigEntry_GraphValidation(t *testing.T) {
|
||||||
for _, tc := range []struct {
|
type tcase struct {
|
||||||
name string
|
|
||||||
entries []structs.ConfigEntry
|
entries []structs.ConfigEntry
|
||||||
op func(t *testing.T, s *Store) error
|
op func(t *testing.T, s *Store) error
|
||||||
expectErr string
|
expectErr string
|
||||||
expectGraphErr bool
|
expectGraphErr bool
|
||||||
}{
|
}
|
||||||
{
|
cases := map[string]tcase{
|
||||||
name: "splitter fails without default protocol",
|
"splitter fails without default protocol": tcase{
|
||||||
entries: []structs.ConfigEntry{},
|
entries: []structs.ConfigEntry{},
|
||||||
op: func(t *testing.T, s *Store) error {
|
op: func(t *testing.T, s *Store) error {
|
||||||
entry := &structs.ServiceSplitterConfigEntry{
|
entry := &structs.ServiceSplitterConfigEntry{
|
||||||
|
@ -196,13 +195,12 @@ func TestStore_ConfigEntry_GraphValidation(t *testing.T) {
|
||||||
{Weight: 10, Namespace: "v2"},
|
{Weight: 10, Namespace: "v2"},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
return s.EnsureConfigEntry(0, entry)
|
return s.EnsureConfigEntry(0, entry, nil)
|
||||||
},
|
},
|
||||||
expectErr: "does not permit advanced routing or splitting behavior",
|
expectErr: "does not permit advanced routing or splitting behavior",
|
||||||
expectGraphErr: true,
|
expectGraphErr: true,
|
||||||
},
|
},
|
||||||
{
|
"splitter fails with tcp protocol": tcase{
|
||||||
name: "splitter fails with tcp protocol",
|
|
||||||
entries: []structs.ConfigEntry{
|
entries: []structs.ConfigEntry{
|
||||||
&structs.ServiceConfigEntry{
|
&structs.ServiceConfigEntry{
|
||||||
Kind: structs.ServiceDefaults,
|
Kind: structs.ServiceDefaults,
|
||||||
|
@ -219,13 +217,12 @@ func TestStore_ConfigEntry_GraphValidation(t *testing.T) {
|
||||||
{Weight: 10, Namespace: "v2"},
|
{Weight: 10, Namespace: "v2"},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
return s.EnsureConfigEntry(0, entry)
|
return s.EnsureConfigEntry(0, entry, nil)
|
||||||
},
|
},
|
||||||
expectErr: "does not permit advanced routing or splitting behavior",
|
expectErr: "does not permit advanced routing or splitting behavior",
|
||||||
expectGraphErr: true,
|
expectGraphErr: true,
|
||||||
},
|
},
|
||||||
{
|
"splitter works with http protocol": tcase{
|
||||||
name: "splitter works with http protocol",
|
|
||||||
entries: []structs.ConfigEntry{
|
entries: []structs.ConfigEntry{
|
||||||
&structs.ProxyConfigEntry{
|
&structs.ProxyConfigEntry{
|
||||||
Kind: structs.ProxyDefaults,
|
Kind: structs.ProxyDefaults,
|
||||||
|
@ -235,9 +232,22 @@ func TestStore_ConfigEntry_GraphValidation(t *testing.T) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
&structs.ServiceConfigEntry{
|
&structs.ServiceConfigEntry{
|
||||||
Kind: structs.ServiceDefaults,
|
Kind: structs.ServiceDefaults,
|
||||||
Name: "main",
|
Name: "main",
|
||||||
Protocol: "http",
|
Protocol: "http",
|
||||||
|
EnterpriseMeta: *structs.DefaultEnterpriseMeta(),
|
||||||
|
},
|
||||||
|
&structs.ServiceResolverConfigEntry{
|
||||||
|
Kind: structs.ServiceResolver,
|
||||||
|
Name: "main",
|
||||||
|
Subsets: map[string]structs.ServiceResolverSubset{
|
||||||
|
"v1": structs.ServiceResolverSubset{
|
||||||
|
Filter: "Service.Meta.version == v1",
|
||||||
|
},
|
||||||
|
"v2": structs.ServiceResolverSubset{
|
||||||
|
Filter: "Service.Meta.version == v2",
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
op: func(t *testing.T, s *Store) error {
|
op: func(t *testing.T, s *Store) error {
|
||||||
|
@ -245,15 +255,15 @@ func TestStore_ConfigEntry_GraphValidation(t *testing.T) {
|
||||||
Kind: structs.ServiceSplitter,
|
Kind: structs.ServiceSplitter,
|
||||||
Name: "main",
|
Name: "main",
|
||||||
Splits: []structs.ServiceSplit{
|
Splits: []structs.ServiceSplit{
|
||||||
{Weight: 90, Namespace: "v1"},
|
{Weight: 90, ServiceSubset: "v1"},
|
||||||
{Weight: 10, Namespace: "v2"},
|
{Weight: 10, ServiceSubset: "v2"},
|
||||||
},
|
},
|
||||||
|
EnterpriseMeta: *structs.DefaultEnterpriseMeta(),
|
||||||
}
|
}
|
||||||
return s.EnsureConfigEntry(0, entry)
|
return s.EnsureConfigEntry(0, entry, nil)
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
"splitter works with http protocol (from proxy-defaults)": tcase{
|
||||||
name: "splitter works with http protocol (from proxy-defaults)",
|
|
||||||
entries: []structs.ConfigEntry{
|
entries: []structs.ConfigEntry{
|
||||||
&structs.ProxyConfigEntry{
|
&structs.ProxyConfigEntry{
|
||||||
Kind: structs.ProxyDefaults,
|
Kind: structs.ProxyDefaults,
|
||||||
|
@ -272,11 +282,10 @@ func TestStore_ConfigEntry_GraphValidation(t *testing.T) {
|
||||||
{Weight: 10, Namespace: "v2"},
|
{Weight: 10, Namespace: "v2"},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
return s.EnsureConfigEntry(0, entry)
|
return s.EnsureConfigEntry(0, entry, nil)
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
"router fails with tcp protocol": tcase{
|
||||||
name: "router fails with tcp protocol",
|
|
||||||
entries: []structs.ConfigEntry{
|
entries: []structs.ConfigEntry{
|
||||||
&structs.ServiceConfigEntry{
|
&structs.ServiceConfigEntry{
|
||||||
Kind: structs.ServiceDefaults,
|
Kind: structs.ServiceDefaults,
|
||||||
|
@ -301,13 +310,12 @@ func TestStore_ConfigEntry_GraphValidation(t *testing.T) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
return s.EnsureConfigEntry(0, entry)
|
return s.EnsureConfigEntry(0, entry, nil)
|
||||||
},
|
},
|
||||||
expectErr: "does not permit advanced routing or splitting behavior",
|
expectErr: "does not permit advanced routing or splitting behavior",
|
||||||
expectGraphErr: true,
|
expectGraphErr: true,
|
||||||
},
|
},
|
||||||
{
|
"router fails without default protocol": tcase{
|
||||||
name: "router fails without default protocol",
|
|
||||||
entries: []structs.ConfigEntry{},
|
entries: []structs.ConfigEntry{},
|
||||||
op: func(t *testing.T, s *Store) error {
|
op: func(t *testing.T, s *Store) error {
|
||||||
entry := &structs.ServiceRouterConfigEntry{
|
entry := &structs.ServiceRouterConfigEntry{
|
||||||
|
@ -326,37 +334,47 @@ func TestStore_ConfigEntry_GraphValidation(t *testing.T) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
return s.EnsureConfigEntry(0, entry)
|
return s.EnsureConfigEntry(0, entry, nil)
|
||||||
},
|
},
|
||||||
expectErr: "does not permit advanced routing or splitting behavior",
|
expectErr: "does not permit advanced routing or splitting behavior",
|
||||||
expectGraphErr: true,
|
expectGraphErr: true,
|
||||||
},
|
},
|
||||||
/////////////////////////////////////////////////
|
/////////////////////////////////////////////////
|
||||||
{
|
"cannot remove default protocol after splitter created": tcase{
|
||||||
name: "cannot remove default protocol after splitter created",
|
|
||||||
entries: []structs.ConfigEntry{
|
entries: []structs.ConfigEntry{
|
||||||
&structs.ServiceConfigEntry{
|
&structs.ServiceConfigEntry{
|
||||||
Kind: structs.ServiceDefaults,
|
Kind: structs.ServiceDefaults,
|
||||||
Name: "main",
|
Name: "main",
|
||||||
Protocol: "http",
|
Protocol: "http",
|
||||||
},
|
},
|
||||||
|
&structs.ServiceResolverConfigEntry{
|
||||||
|
Kind: structs.ServiceResolver,
|
||||||
|
Name: "main",
|
||||||
|
Subsets: map[string]structs.ServiceResolverSubset{
|
||||||
|
"v1": structs.ServiceResolverSubset{
|
||||||
|
Filter: "Service.Meta.version == v1",
|
||||||
|
},
|
||||||
|
"v2": structs.ServiceResolverSubset{
|
||||||
|
Filter: "Service.Meta.version == v2",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
&structs.ServiceSplitterConfigEntry{
|
&structs.ServiceSplitterConfigEntry{
|
||||||
Kind: structs.ServiceSplitter,
|
Kind: structs.ServiceSplitter,
|
||||||
Name: "main",
|
Name: "main",
|
||||||
Splits: []structs.ServiceSplit{
|
Splits: []structs.ServiceSplit{
|
||||||
{Weight: 90, Namespace: "v1"},
|
{Weight: 90, ServiceSubset: "v1"},
|
||||||
{Weight: 10, Namespace: "v2"},
|
{Weight: 10, ServiceSubset: "v2"},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
op: func(t *testing.T, s *Store) error {
|
op: func(t *testing.T, s *Store) error {
|
||||||
return s.DeleteConfigEntry(0, structs.ServiceDefaults, "main")
|
return s.DeleteConfigEntry(0, structs.ServiceDefaults, "main", nil)
|
||||||
},
|
},
|
||||||
expectErr: "does not permit advanced routing or splitting behavior",
|
expectErr: "does not permit advanced routing or splitting behavior",
|
||||||
expectGraphErr: true,
|
expectGraphErr: true,
|
||||||
},
|
},
|
||||||
{
|
"cannot remove global default protocol after splitter created": tcase{
|
||||||
name: "cannot remove global default protocol after splitter created",
|
|
||||||
entries: []structs.ConfigEntry{
|
entries: []structs.ConfigEntry{
|
||||||
&structs.ProxyConfigEntry{
|
&structs.ProxyConfigEntry{
|
||||||
Kind: structs.ProxyDefaults,
|
Kind: structs.ProxyDefaults,
|
||||||
|
@ -375,13 +393,12 @@ func TestStore_ConfigEntry_GraphValidation(t *testing.T) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
op: func(t *testing.T, s *Store) error {
|
op: func(t *testing.T, s *Store) error {
|
||||||
return s.DeleteConfigEntry(0, structs.ProxyDefaults, structs.ProxyConfigGlobal)
|
return s.DeleteConfigEntry(0, structs.ProxyDefaults, structs.ProxyConfigGlobal, nil)
|
||||||
},
|
},
|
||||||
expectErr: "does not permit advanced routing or splitting behavior",
|
expectErr: "does not permit advanced routing or splitting behavior",
|
||||||
expectGraphErr: true,
|
expectGraphErr: true,
|
||||||
},
|
},
|
||||||
{
|
"can remove global default protocol after splitter created if service default overrides it": tcase{
|
||||||
name: "can remove global default protocol after splitter created if service default overrides it",
|
|
||||||
entries: []structs.ConfigEntry{
|
entries: []structs.ConfigEntry{
|
||||||
&structs.ProxyConfigEntry{
|
&structs.ProxyConfigEntry{
|
||||||
Kind: structs.ProxyDefaults,
|
Kind: structs.ProxyDefaults,
|
||||||
|
@ -395,33 +412,56 @@ func TestStore_ConfigEntry_GraphValidation(t *testing.T) {
|
||||||
Name: "main",
|
Name: "main",
|
||||||
Protocol: "http",
|
Protocol: "http",
|
||||||
},
|
},
|
||||||
|
&structs.ServiceResolverConfigEntry{
|
||||||
|
Kind: structs.ServiceResolver,
|
||||||
|
Name: "main",
|
||||||
|
Subsets: map[string]structs.ServiceResolverSubset{
|
||||||
|
"v1": structs.ServiceResolverSubset{
|
||||||
|
Filter: "Service.Meta.version == v1",
|
||||||
|
},
|
||||||
|
"v2": structs.ServiceResolverSubset{
|
||||||
|
Filter: "Service.Meta.version == v2",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
&structs.ServiceSplitterConfigEntry{
|
&structs.ServiceSplitterConfigEntry{
|
||||||
Kind: structs.ServiceSplitter,
|
Kind: structs.ServiceSplitter,
|
||||||
Name: "main",
|
Name: "main",
|
||||||
Splits: []structs.ServiceSplit{
|
Splits: []structs.ServiceSplit{
|
||||||
{Weight: 90, Namespace: "v1"},
|
{Weight: 90, ServiceSubset: "v1"},
|
||||||
{Weight: 10, Namespace: "v2"},
|
{Weight: 10, ServiceSubset: "v2"},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
op: func(t *testing.T, s *Store) error {
|
op: func(t *testing.T, s *Store) error {
|
||||||
return s.DeleteConfigEntry(0, structs.ProxyDefaults, structs.ProxyConfigGlobal)
|
return s.DeleteConfigEntry(0, structs.ProxyDefaults, structs.ProxyConfigGlobal, nil)
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
"cannot change to tcp protocol after splitter created": tcase{
|
||||||
name: "cannot change to tcp protocol after splitter created",
|
|
||||||
entries: []structs.ConfigEntry{
|
entries: []structs.ConfigEntry{
|
||||||
&structs.ServiceConfigEntry{
|
&structs.ServiceConfigEntry{
|
||||||
Kind: structs.ServiceDefaults,
|
Kind: structs.ServiceDefaults,
|
||||||
Name: "main",
|
Name: "main",
|
||||||
Protocol: "http",
|
Protocol: "http",
|
||||||
},
|
},
|
||||||
|
&structs.ServiceResolverConfigEntry{
|
||||||
|
Kind: structs.ServiceResolver,
|
||||||
|
Name: "main",
|
||||||
|
Subsets: map[string]structs.ServiceResolverSubset{
|
||||||
|
"v1": structs.ServiceResolverSubset{
|
||||||
|
Filter: "Service.Meta.version == v1",
|
||||||
|
},
|
||||||
|
"v2": structs.ServiceResolverSubset{
|
||||||
|
Filter: "Service.Meta.version == v2",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
&structs.ServiceSplitterConfigEntry{
|
&structs.ServiceSplitterConfigEntry{
|
||||||
Kind: structs.ServiceSplitter,
|
Kind: structs.ServiceSplitter,
|
||||||
Name: "main",
|
Name: "main",
|
||||||
Splits: []structs.ServiceSplit{
|
Splits: []structs.ServiceSplit{
|
||||||
{Weight: 90, Namespace: "v1"},
|
{Weight: 90, ServiceSubset: "v1"},
|
||||||
{Weight: 10, Namespace: "v2"},
|
{Weight: 10, ServiceSubset: "v2"},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -431,19 +471,27 @@ func TestStore_ConfigEntry_GraphValidation(t *testing.T) {
|
||||||
Name: "main",
|
Name: "main",
|
||||||
Protocol: "tcp",
|
Protocol: "tcp",
|
||||||
}
|
}
|
||||||
return s.EnsureConfigEntry(0, entry)
|
return s.EnsureConfigEntry(0, entry, nil)
|
||||||
},
|
},
|
||||||
expectErr: "does not permit advanced routing or splitting behavior",
|
expectErr: "does not permit advanced routing or splitting behavior",
|
||||||
expectGraphErr: true,
|
expectGraphErr: true,
|
||||||
},
|
},
|
||||||
{
|
"cannot remove default protocol after router created": tcase{
|
||||||
name: "cannot remove default protocol after router created",
|
|
||||||
entries: []structs.ConfigEntry{
|
entries: []structs.ConfigEntry{
|
||||||
&structs.ServiceConfigEntry{
|
&structs.ServiceConfigEntry{
|
||||||
Kind: structs.ServiceDefaults,
|
Kind: structs.ServiceDefaults,
|
||||||
Name: "main",
|
Name: "main",
|
||||||
Protocol: "http",
|
Protocol: "http",
|
||||||
},
|
},
|
||||||
|
&structs.ServiceResolverConfigEntry{
|
||||||
|
Kind: structs.ServiceResolver,
|
||||||
|
Name: "main",
|
||||||
|
Subsets: map[string]structs.ServiceResolverSubset{
|
||||||
|
"other": structs.ServiceResolverSubset{
|
||||||
|
Filter: "Service.Meta.version == other",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
&structs.ServiceRouterConfigEntry{
|
&structs.ServiceRouterConfigEntry{
|
||||||
Kind: structs.ServiceRouter,
|
Kind: structs.ServiceRouter,
|
||||||
Name: "main",
|
Name: "main",
|
||||||
|
@ -455,26 +503,34 @@ func TestStore_ConfigEntry_GraphValidation(t *testing.T) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Destination: &structs.ServiceRouteDestination{
|
Destination: &structs.ServiceRouteDestination{
|
||||||
Namespace: "other",
|
ServiceSubset: "other",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
op: func(t *testing.T, s *Store) error {
|
op: func(t *testing.T, s *Store) error {
|
||||||
return s.DeleteConfigEntry(0, structs.ServiceDefaults, "main")
|
return s.DeleteConfigEntry(0, structs.ServiceDefaults, "main", nil)
|
||||||
},
|
},
|
||||||
expectErr: "does not permit advanced routing or splitting behavior",
|
expectErr: "does not permit advanced routing or splitting behavior",
|
||||||
expectGraphErr: true,
|
expectGraphErr: true,
|
||||||
},
|
},
|
||||||
{
|
"cannot change to tcp protocol after router created": tcase{
|
||||||
name: "cannot change to tcp protocol after router created",
|
|
||||||
entries: []structs.ConfigEntry{
|
entries: []structs.ConfigEntry{
|
||||||
&structs.ServiceConfigEntry{
|
&structs.ServiceConfigEntry{
|
||||||
Kind: structs.ServiceDefaults,
|
Kind: structs.ServiceDefaults,
|
||||||
Name: "main",
|
Name: "main",
|
||||||
Protocol: "http",
|
Protocol: "http",
|
||||||
},
|
},
|
||||||
|
&structs.ServiceResolverConfigEntry{
|
||||||
|
Kind: structs.ServiceResolver,
|
||||||
|
Name: "main",
|
||||||
|
Subsets: map[string]structs.ServiceResolverSubset{
|
||||||
|
"other": structs.ServiceResolverSubset{
|
||||||
|
Filter: "Service.Meta.version == other",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
&structs.ServiceRouterConfigEntry{
|
&structs.ServiceRouterConfigEntry{
|
||||||
Kind: structs.ServiceRouter,
|
Kind: structs.ServiceRouter,
|
||||||
Name: "main",
|
Name: "main",
|
||||||
|
@ -486,7 +542,7 @@ func TestStore_ConfigEntry_GraphValidation(t *testing.T) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Destination: &structs.ServiceRouteDestination{
|
Destination: &structs.ServiceRouteDestination{
|
||||||
Namespace: "other",
|
ServiceSubset: "other",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -498,14 +554,13 @@ func TestStore_ConfigEntry_GraphValidation(t *testing.T) {
|
||||||
Name: "main",
|
Name: "main",
|
||||||
Protocol: "tcp",
|
Protocol: "tcp",
|
||||||
}
|
}
|
||||||
return s.EnsureConfigEntry(0, entry)
|
return s.EnsureConfigEntry(0, entry, nil)
|
||||||
},
|
},
|
||||||
expectErr: "does not permit advanced routing or splitting behavior",
|
expectErr: "does not permit advanced routing or splitting behavior",
|
||||||
expectGraphErr: true,
|
expectGraphErr: true,
|
||||||
},
|
},
|
||||||
/////////////////////////////////////////////////
|
/////////////////////////////////////////////////
|
||||||
{
|
"cannot split to a service using tcp": tcase{
|
||||||
name: "cannot split to a service using tcp",
|
|
||||||
entries: []structs.ConfigEntry{
|
entries: []structs.ConfigEntry{
|
||||||
&structs.ServiceConfigEntry{
|
&structs.ServiceConfigEntry{
|
||||||
Kind: structs.ServiceDefaults,
|
Kind: structs.ServiceDefaults,
|
||||||
|
@ -527,13 +582,12 @@ func TestStore_ConfigEntry_GraphValidation(t *testing.T) {
|
||||||
{Weight: 10, Service: "other"},
|
{Weight: 10, Service: "other"},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
return s.EnsureConfigEntry(0, entry)
|
return s.EnsureConfigEntry(0, entry, nil)
|
||||||
},
|
},
|
||||||
expectErr: "uses inconsistent protocols",
|
expectErr: "uses inconsistent protocols",
|
||||||
expectGraphErr: true,
|
expectGraphErr: true,
|
||||||
},
|
},
|
||||||
{
|
"cannot route to a service using tcp": tcase{
|
||||||
name: "cannot route to a service using tcp",
|
|
||||||
entries: []structs.ConfigEntry{
|
entries: []structs.ConfigEntry{
|
||||||
&structs.ServiceConfigEntry{
|
&structs.ServiceConfigEntry{
|
||||||
Kind: structs.ServiceDefaults,
|
Kind: structs.ServiceDefaults,
|
||||||
|
@ -563,14 +617,13 @@ func TestStore_ConfigEntry_GraphValidation(t *testing.T) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
return s.EnsureConfigEntry(0, entry)
|
return s.EnsureConfigEntry(0, entry, nil)
|
||||||
},
|
},
|
||||||
expectErr: "uses inconsistent protocols",
|
expectErr: "uses inconsistent protocols",
|
||||||
expectGraphErr: true,
|
expectGraphErr: true,
|
||||||
},
|
},
|
||||||
/////////////////////////////////////////////////
|
/////////////////////////////////////////////////
|
||||||
{
|
"cannot failover to a service using a different protocol": tcase{
|
||||||
name: "cannot failover to a service using a different protocol",
|
|
||||||
entries: []structs.ConfigEntry{
|
entries: []structs.ConfigEntry{
|
||||||
&structs.ServiceConfigEntry{
|
&structs.ServiceConfigEntry{
|
||||||
Kind: structs.ServiceDefaults,
|
Kind: structs.ServiceDefaults,
|
||||||
|
@ -598,13 +651,12 @@ func TestStore_ConfigEntry_GraphValidation(t *testing.T) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
return s.EnsureConfigEntry(0, entry)
|
return s.EnsureConfigEntry(0, entry, nil)
|
||||||
},
|
},
|
||||||
expectErr: "uses inconsistent protocols",
|
expectErr: "uses inconsistent protocols",
|
||||||
expectGraphErr: true,
|
expectGraphErr: true,
|
||||||
},
|
},
|
||||||
{
|
"cannot redirect to a service using a different protocol": tcase{
|
||||||
name: "cannot redirect to a service using a different protocol",
|
|
||||||
entries: []structs.ConfigEntry{
|
entries: []structs.ConfigEntry{
|
||||||
&structs.ServiceConfigEntry{
|
&structs.ServiceConfigEntry{
|
||||||
Kind: structs.ServiceDefaults,
|
Kind: structs.ServiceDefaults,
|
||||||
|
@ -630,14 +682,13 @@ func TestStore_ConfigEntry_GraphValidation(t *testing.T) {
|
||||||
Service: "other",
|
Service: "other",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
return s.EnsureConfigEntry(0, entry)
|
return s.EnsureConfigEntry(0, entry, nil)
|
||||||
},
|
},
|
||||||
expectErr: "uses inconsistent protocols",
|
expectErr: "uses inconsistent protocols",
|
||||||
expectGraphErr: true,
|
expectGraphErr: true,
|
||||||
},
|
},
|
||||||
/////////////////////////////////////////////////
|
/////////////////////////////////////////////////
|
||||||
{
|
"redirect to a subset that does exist is fine": tcase{
|
||||||
name: "redirect to a subset that does exist is fine",
|
|
||||||
entries: []structs.ConfigEntry{
|
entries: []structs.ConfigEntry{
|
||||||
&structs.ServiceResolverConfigEntry{
|
&structs.ServiceResolverConfigEntry{
|
||||||
Kind: structs.ServiceResolver,
|
Kind: structs.ServiceResolver,
|
||||||
|
@ -659,11 +710,10 @@ func TestStore_ConfigEntry_GraphValidation(t *testing.T) {
|
||||||
ServiceSubset: "v1",
|
ServiceSubset: "v1",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
return s.EnsureConfigEntry(0, entry)
|
return s.EnsureConfigEntry(0, entry, nil)
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
"cannot redirect to a subset that does not exist": tcase{
|
||||||
name: "cannot redirect to a subset that does not exist",
|
|
||||||
entries: []structs.ConfigEntry{
|
entries: []structs.ConfigEntry{
|
||||||
&structs.ServiceResolverConfigEntry{
|
&structs.ServiceResolverConfigEntry{
|
||||||
Kind: structs.ServiceResolver,
|
Kind: structs.ServiceResolver,
|
||||||
|
@ -680,14 +730,13 @@ func TestStore_ConfigEntry_GraphValidation(t *testing.T) {
|
||||||
ServiceSubset: "v1",
|
ServiceSubset: "v1",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
return s.EnsureConfigEntry(0, entry)
|
return s.EnsureConfigEntry(0, entry, nil)
|
||||||
},
|
},
|
||||||
expectErr: `does not have a subset named "v1"`,
|
expectErr: `does not have a subset named "v1"`,
|
||||||
expectGraphErr: true,
|
expectGraphErr: true,
|
||||||
},
|
},
|
||||||
/////////////////////////////////////////////////
|
/////////////////////////////////////////////////
|
||||||
{
|
"cannot introduce circular resolver redirect": tcase{
|
||||||
name: "cannot introduce circular resolver redirect",
|
|
||||||
entries: []structs.ConfigEntry{
|
entries: []structs.ConfigEntry{
|
||||||
&structs.ServiceResolverConfigEntry{
|
&structs.ServiceResolverConfigEntry{
|
||||||
Kind: structs.ServiceResolver,
|
Kind: structs.ServiceResolver,
|
||||||
|
@ -705,13 +754,12 @@ func TestStore_ConfigEntry_GraphValidation(t *testing.T) {
|
||||||
Service: "other",
|
Service: "other",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
return s.EnsureConfigEntry(0, entry)
|
return s.EnsureConfigEntry(0, entry, nil)
|
||||||
},
|
},
|
||||||
expectErr: `detected circular resolver redirect`,
|
expectErr: `detected circular resolver redirect`,
|
||||||
expectGraphErr: true,
|
expectGraphErr: true,
|
||||||
},
|
},
|
||||||
{
|
"cannot introduce circular split": tcase{
|
||||||
name: "cannot introduce circular split",
|
|
||||||
entries: []structs.ConfigEntry{
|
entries: []structs.ConfigEntry{
|
||||||
&structs.ProxyConfigEntry{
|
&structs.ProxyConfigEntry{
|
||||||
Kind: structs.ProxyDefaults,
|
Kind: structs.ProxyDefaults,
|
||||||
|
@ -736,18 +784,21 @@ func TestStore_ConfigEntry_GraphValidation(t *testing.T) {
|
||||||
{Weight: 100, Service: "other"},
|
{Weight: 100, Service: "other"},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
return s.EnsureConfigEntry(0, entry)
|
return s.EnsureConfigEntry(0, entry, nil)
|
||||||
},
|
},
|
||||||
expectErr: `detected circular reference`,
|
expectErr: `detected circular reference`,
|
||||||
expectGraphErr: true,
|
expectGraphErr: true,
|
||||||
},
|
},
|
||||||
} {
|
}
|
||||||
|
|
||||||
|
for name, tc := range cases {
|
||||||
|
name := name
|
||||||
tc := tc
|
tc := tc
|
||||||
|
|
||||||
t.Run(tc.name, func(t *testing.T) {
|
t.Run(name, func(t *testing.T) {
|
||||||
s := testStateStore(t)
|
s := testStateStore(t)
|
||||||
for _, entry := range tc.entries {
|
for _, entry := range tc.entries {
|
||||||
require.NoError(t, s.EnsureConfigEntry(0, entry))
|
require.NoError(t, s.EnsureConfigEntry(0, entry, nil))
|
||||||
}
|
}
|
||||||
|
|
||||||
err := tc.op(t, s)
|
err := tc.op(t, s)
|
||||||
|
@ -819,7 +870,7 @@ func TestStore_ReadDiscoveryChainConfigEntries_Overrides(t *testing.T) {
|
||||||
{Kind: structs.ServiceDefaults, Name: "main"},
|
{Kind: structs.ServiceDefaults, Name: "main"},
|
||||||
},
|
},
|
||||||
checkAfter: func(t *testing.T, entrySet *structs.DiscoveryChainConfigEntries) {
|
checkAfter: func(t *testing.T, entrySet *structs.DiscoveryChainConfigEntries) {
|
||||||
defaults := entrySet.GetService("main")
|
defaults := entrySet.GetService(structs.NewServiceID("main", nil))
|
||||||
require.NotNil(t, defaults)
|
require.NotNil(t, defaults)
|
||||||
require.Equal(t, "grpc", defaults.Protocol)
|
require.Equal(t, "grpc", defaults.Protocol)
|
||||||
},
|
},
|
||||||
|
@ -912,7 +963,7 @@ func TestStore_ReadDiscoveryChainConfigEntries_Overrides(t *testing.T) {
|
||||||
{Kind: structs.ServiceRouter, Name: "main"},
|
{Kind: structs.ServiceRouter, Name: "main"},
|
||||||
},
|
},
|
||||||
checkAfter: func(t *testing.T, entrySet *structs.DiscoveryChainConfigEntries) {
|
checkAfter: func(t *testing.T, entrySet *structs.DiscoveryChainConfigEntries) {
|
||||||
router := entrySet.GetRouter("main")
|
router := entrySet.GetRouter(structs.NewServiceID("main", nil))
|
||||||
require.NotNil(t, router)
|
require.NotNil(t, router)
|
||||||
require.Len(t, router.Routes, 1)
|
require.Len(t, router.Routes, 1)
|
||||||
|
|
||||||
|
@ -992,7 +1043,7 @@ func TestStore_ReadDiscoveryChainConfigEntries_Overrides(t *testing.T) {
|
||||||
{Kind: structs.ServiceSplitter, Name: "main"},
|
{Kind: structs.ServiceSplitter, Name: "main"},
|
||||||
},
|
},
|
||||||
checkAfter: func(t *testing.T, entrySet *structs.DiscoveryChainConfigEntries) {
|
checkAfter: func(t *testing.T, entrySet *structs.DiscoveryChainConfigEntries) {
|
||||||
splitter := entrySet.GetSplitter("main")
|
splitter := entrySet.GetSplitter(structs.NewServiceID("main", nil))
|
||||||
require.NotNil(t, splitter)
|
require.NotNil(t, splitter)
|
||||||
require.Len(t, splitter.Splits, 2)
|
require.Len(t, splitter.Splits, 2)
|
||||||
|
|
||||||
|
@ -1044,7 +1095,7 @@ func TestStore_ReadDiscoveryChainConfigEntries_Overrides(t *testing.T) {
|
||||||
{Kind: structs.ServiceResolver, Name: "main"},
|
{Kind: structs.ServiceResolver, Name: "main"},
|
||||||
},
|
},
|
||||||
checkAfter: func(t *testing.T, entrySet *structs.DiscoveryChainConfigEntries) {
|
checkAfter: func(t *testing.T, entrySet *structs.DiscoveryChainConfigEntries) {
|
||||||
resolver := entrySet.GetResolver("main")
|
resolver := entrySet.GetResolver(structs.NewServiceID("main", nil))
|
||||||
require.NotNil(t, resolver)
|
require.NotNil(t, resolver)
|
||||||
require.Equal(t, 33*time.Second, resolver.ConnectTimeout)
|
require.Equal(t, 33*time.Second, resolver.ConnectTimeout)
|
||||||
},
|
},
|
||||||
|
@ -1055,18 +1106,18 @@ func TestStore_ReadDiscoveryChainConfigEntries_Overrides(t *testing.T) {
|
||||||
t.Run(tc.name, func(t *testing.T) {
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
s := testStateStore(t)
|
s := testStateStore(t)
|
||||||
for _, entry := range tc.entries {
|
for _, entry := range tc.entries {
|
||||||
require.NoError(t, s.EnsureConfigEntry(0, entry))
|
require.NoError(t, s.EnsureConfigEntry(0, entry, nil))
|
||||||
}
|
}
|
||||||
|
|
||||||
t.Run("without override", func(t *testing.T) {
|
t.Run("without override", func(t *testing.T) {
|
||||||
_, entrySet, err := s.readDiscoveryChainConfigEntries(nil, "main", nil)
|
_, entrySet, err := s.readDiscoveryChainConfigEntries(nil, "main", nil, nil)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
got := entrySetToKindNames(entrySet)
|
got := entrySetToKindNames(entrySet)
|
||||||
require.ElementsMatch(t, tc.expectBefore, got)
|
require.ElementsMatch(t, tc.expectBefore, got)
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("with override", func(t *testing.T) {
|
t.Run("with override", func(t *testing.T) {
|
||||||
_, entrySet, err := s.readDiscoveryChainConfigEntries(nil, "main", tc.overrides)
|
_, entrySet, err := s.readDiscoveryChainConfigEntries(nil, "main", tc.overrides, nil)
|
||||||
|
|
||||||
if tc.expectAfterErr != "" {
|
if tc.expectAfterErr != "" {
|
||||||
require.Error(t, err)
|
require.Error(t, err)
|
||||||
|
@ -1146,10 +1197,10 @@ func TestStore_ReadDiscoveryChainConfigEntries_SubsetSplit(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, entry := range entries {
|
for _, entry := range entries {
|
||||||
require.NoError(t, s.EnsureConfigEntry(0, entry))
|
require.NoError(t, s.EnsureConfigEntry(0, entry, nil))
|
||||||
}
|
}
|
||||||
|
|
||||||
_, entrySet, err := s.ReadDiscoveryChainConfigEntries(nil, "main")
|
_, entrySet, err := s.ReadDiscoveryChainConfigEntries(nil, "main", nil)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
require.Len(t, entrySet.Routers, 0)
|
require.Len(t, entrySet.Routers, 0)
|
||||||
|
|
|
@ -0,0 +1,7 @@
|
||||||
|
// +build !consulent
|
||||||
|
|
||||||
|
package state
|
||||||
|
|
||||||
|
func (s *Store) setupDefaultTestEntMeta() error {
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -24,7 +24,11 @@ func (s *HTTPServer) DiscoveryChainRead(resp http.ResponseWriter, req *http.Requ
|
||||||
}
|
}
|
||||||
|
|
||||||
args.EvaluateInDatacenter = req.URL.Query().Get("compile-dc")
|
args.EvaluateInDatacenter = req.URL.Query().Get("compile-dc")
|
||||||
// TODO(namespaces): args.EvaluateInNamespace = req.URL.Query().Get("compile-namespace")
|
var entMeta structs.EnterpriseMeta
|
||||||
|
if err := s.parseEntMetaNoWildcard(req, &entMeta); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
args.WithEnterpriseMeta(&entMeta)
|
||||||
|
|
||||||
if req.Method == "POST" {
|
if req.Method == "POST" {
|
||||||
var raw map[string]interface{}
|
var raw map[string]interface{}
|
||||||
|
|
|
@ -45,8 +45,8 @@ type Manager struct {
|
||||||
|
|
||||||
mu sync.Mutex
|
mu sync.Mutex
|
||||||
started bool
|
started bool
|
||||||
proxies map[string]*state
|
proxies map[structs.ServiceID]*state
|
||||||
watchers map[string]map[uint64]chan *ConfigSnapshot
|
watchers map[structs.ServiceID]map[uint64]chan *ConfigSnapshot
|
||||||
}
|
}
|
||||||
|
|
||||||
// ManagerConfig holds the required external dependencies for a Manager
|
// ManagerConfig holds the required external dependencies for a Manager
|
||||||
|
@ -79,8 +79,8 @@ func NewManager(cfg ManagerConfig) (*Manager, error) {
|
||||||
// Single item buffer is enough since there is no data transferred so this
|
// Single item buffer is enough since there is no data transferred so this
|
||||||
// is "level triggering" and we can't miss actual data.
|
// is "level triggering" and we can't miss actual data.
|
||||||
stateCh: make(chan struct{}, 1),
|
stateCh: make(chan struct{}, 1),
|
||||||
proxies: make(map[string]*state),
|
proxies: make(map[structs.ServiceID]*state),
|
||||||
watchers: make(map[string]map[uint64]chan *ConfigSnapshot),
|
watchers: make(map[structs.ServiceID]map[uint64]chan *ConfigSnapshot),
|
||||||
}
|
}
|
||||||
return m, nil
|
return m, nil
|
||||||
}
|
}
|
||||||
|
@ -130,7 +130,7 @@ func (m *Manager) syncState() {
|
||||||
|
|
||||||
// Traverse the local state and ensure all proxy services are registered
|
// Traverse the local state and ensure all proxy services are registered
|
||||||
services := m.State.Services(structs.WildcardEnterpriseMeta())
|
services := m.State.Services(structs.WildcardEnterpriseMeta())
|
||||||
for _, svc := range services {
|
for sid, svc := range services {
|
||||||
if svc.Kind != structs.ServiceKindConnectProxy && svc.Kind != structs.ServiceKindMeshGateway {
|
if svc.Kind != structs.ServiceKindConnectProxy && svc.Kind != structs.ServiceKindMeshGateway {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
@ -141,20 +141,16 @@ func (m *Manager) syncState() {
|
||||||
// know that so we'd need to set it here if not during registration of the
|
// know that so we'd need to set it here if not during registration of the
|
||||||
// proxy service. Sidecar Service in the interim can do that, but we should
|
// proxy service. Sidecar Service in the interim can do that, but we should
|
||||||
// validate more generally that that is always true.
|
// validate more generally that that is always true.
|
||||||
err := m.ensureProxyServiceLocked(svc, m.State.ServiceToken(svc.CompoundServiceID()))
|
err := m.ensureProxyServiceLocked(svc, m.State.ServiceToken(sid))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
m.Logger.Printf("[ERR] failed to watch proxy service %s: %s", svc.ID,
|
m.Logger.Printf("[ERR] failed to watch proxy service %s: %s", sid.String(),
|
||||||
err)
|
err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Now see if any proxies were removed
|
// Now see if any proxies were removed
|
||||||
for proxyID := range m.proxies {
|
for proxyID := range m.proxies {
|
||||||
var key structs.ServiceID
|
if _, ok := services[proxyID]; !ok {
|
||||||
// TODO (namespaces) pass through some real enterprise meta that probably needs to come from the proxy tracking
|
|
||||||
key.Init(proxyID, nil)
|
|
||||||
|
|
||||||
if _, ok := services[key]; !ok {
|
|
||||||
// Remove them
|
// Remove them
|
||||||
m.removeProxyServiceLocked(proxyID)
|
m.removeProxyServiceLocked(proxyID)
|
||||||
}
|
}
|
||||||
|
@ -163,7 +159,8 @@ func (m *Manager) syncState() {
|
||||||
|
|
||||||
// ensureProxyServiceLocked adds or changes the proxy to our state.
|
// ensureProxyServiceLocked adds or changes the proxy to our state.
|
||||||
func (m *Manager) ensureProxyServiceLocked(ns *structs.NodeService, token string) error {
|
func (m *Manager) ensureProxyServiceLocked(ns *structs.NodeService, token string) error {
|
||||||
state, ok := m.proxies[ns.ID]
|
sid := ns.CompoundServiceID()
|
||||||
|
state, ok := m.proxies[sid]
|
||||||
|
|
||||||
if ok {
|
if ok {
|
||||||
if !state.Changed(ns, token) {
|
if !state.Changed(ns, token) {
|
||||||
|
@ -190,7 +187,7 @@ func (m *Manager) ensureProxyServiceLocked(ns *structs.NodeService, token string
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
m.proxies[ns.ID] = state
|
m.proxies[sid] = state
|
||||||
|
|
||||||
// Start a goroutine that will wait for changes and broadcast them to watchers.
|
// Start a goroutine that will wait for changes and broadcast them to watchers.
|
||||||
go func(ch <-chan ConfigSnapshot) {
|
go func(ch <-chan ConfigSnapshot) {
|
||||||
|
@ -205,7 +202,7 @@ func (m *Manager) ensureProxyServiceLocked(ns *structs.NodeService, token string
|
||||||
|
|
||||||
// removeProxyService is called when a service deregisters and frees all
|
// removeProxyService is called when a service deregisters and frees all
|
||||||
// resources for that service.
|
// resources for that service.
|
||||||
func (m *Manager) removeProxyServiceLocked(proxyID string) {
|
func (m *Manager) removeProxyServiceLocked(proxyID structs.ServiceID) {
|
||||||
state, ok := m.proxies[proxyID]
|
state, ok := m.proxies[proxyID]
|
||||||
if !ok {
|
if !ok {
|
||||||
return
|
return
|
||||||
|
@ -269,7 +266,7 @@ OUTER:
|
||||||
// This should not be possible since we should be the only sender, enforced
|
// This should not be possible since we should be the only sender, enforced
|
||||||
// by m.mu but error and drop the update rather than panic.
|
// by m.mu but error and drop the update rather than panic.
|
||||||
m.Logger.Printf("[ERR] proxycfg: failed to deliver ConfigSnapshot to %q",
|
m.Logger.Printf("[ERR] proxycfg: failed to deliver ConfigSnapshot to %q",
|
||||||
snap.ProxyID)
|
snap.ProxyID.String())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -277,7 +274,7 @@ OUTER:
|
||||||
// will not fail, but no updates will be delivered until the proxy is
|
// will not fail, but no updates will be delivered until the proxy is
|
||||||
// registered. If there is already a valid snapshot in memory, it will be
|
// registered. If there is already a valid snapshot in memory, it will be
|
||||||
// delivered immediately.
|
// delivered immediately.
|
||||||
func (m *Manager) Watch(proxyID string) (<-chan *ConfigSnapshot, CancelFunc) {
|
func (m *Manager) Watch(proxyID structs.ServiceID) (<-chan *ConfigSnapshot, CancelFunc) {
|
||||||
m.mu.Lock()
|
m.mu.Lock()
|
||||||
defer m.mu.Unlock()
|
defer m.mu.Unlock()
|
||||||
|
|
||||||
|
@ -311,7 +308,7 @@ func (m *Manager) Watch(proxyID string) (<-chan *ConfigSnapshot, CancelFunc) {
|
||||||
|
|
||||||
// closeWatchLocked cleans up state related to a single watcher. It assumes the
|
// closeWatchLocked cleans up state related to a single watcher. It assumes the
|
||||||
// lock is held.
|
// lock is held.
|
||||||
func (m *Manager) closeWatchLocked(proxyID string, watchIdx uint64) {
|
func (m *Manager) closeWatchLocked(proxyID structs.ServiceID, watchIdx uint64) {
|
||||||
if watchers, ok := m.watchers[proxyID]; ok {
|
if watchers, ok := m.watchers[proxyID]; ok {
|
||||||
if ch, ok := watchers[watchIdx]; ok {
|
if ch, ok := watchers[watchIdx]; ok {
|
||||||
delete(watchers, watchIdx)
|
delete(watchers, watchIdx)
|
||||||
|
|
|
@ -138,7 +138,7 @@ func TestManager_BasicLifecycle(t *testing.T) {
|
||||||
dbChainCacheKey := testGenCacheKey(&structs.DiscoveryChainRequest{
|
dbChainCacheKey := testGenCacheKey(&structs.DiscoveryChainRequest{
|
||||||
Name: "db",
|
Name: "db",
|
||||||
EvaluateInDatacenter: "dc1",
|
EvaluateInDatacenter: "dc1",
|
||||||
EvaluateInNamespace: "default",
|
EvaluateInNamespace: "",
|
||||||
// This is because structs.TestUpstreams uses an opaque config
|
// This is because structs.TestUpstreams uses an opaque config
|
||||||
// to override connect timeouts.
|
// to override connect timeouts.
|
||||||
OverrideConnectTimeout: 1 * time.Second,
|
OverrideConnectTimeout: 1 * time.Second,
|
||||||
|
@ -147,26 +147,29 @@ func TestManager_BasicLifecycle(t *testing.T) {
|
||||||
})
|
})
|
||||||
|
|
||||||
dbHealthCacheKey := testGenCacheKey(&structs.ServiceSpecificRequest{
|
dbHealthCacheKey := testGenCacheKey(&structs.ServiceSpecificRequest{
|
||||||
Datacenter: "dc1",
|
Datacenter: "dc1",
|
||||||
QueryOptions: structs.QueryOptions{Token: "my-token", Filter: ""},
|
QueryOptions: structs.QueryOptions{Token: "my-token", Filter: ""},
|
||||||
ServiceName: "db",
|
ServiceName: "db",
|
||||||
Connect: true,
|
Connect: true,
|
||||||
|
EnterpriseMeta: *structs.DefaultEnterpriseMeta(),
|
||||||
})
|
})
|
||||||
db_v1_HealthCacheKey := testGenCacheKey(&structs.ServiceSpecificRequest{
|
db_v1_HealthCacheKey := testGenCacheKey(&structs.ServiceSpecificRequest{
|
||||||
Datacenter: "dc1",
|
Datacenter: "dc1",
|
||||||
QueryOptions: structs.QueryOptions{Token: "my-token",
|
QueryOptions: structs.QueryOptions{Token: "my-token",
|
||||||
Filter: "Service.Meta.version == v1",
|
Filter: "Service.Meta.version == v1",
|
||||||
},
|
},
|
||||||
ServiceName: "db",
|
ServiceName: "db",
|
||||||
Connect: true,
|
Connect: true,
|
||||||
|
EnterpriseMeta: *structs.DefaultEnterpriseMeta(),
|
||||||
})
|
})
|
||||||
db_v2_HealthCacheKey := testGenCacheKey(&structs.ServiceSpecificRequest{
|
db_v2_HealthCacheKey := testGenCacheKey(&structs.ServiceSpecificRequest{
|
||||||
Datacenter: "dc1",
|
Datacenter: "dc1",
|
||||||
QueryOptions: structs.QueryOptions{Token: "my-token",
|
QueryOptions: structs.QueryOptions{Token: "my-token",
|
||||||
Filter: "Service.Meta.version == v2",
|
Filter: "Service.Meta.version == v2",
|
||||||
},
|
},
|
||||||
ServiceName: "db",
|
ServiceName: "db",
|
||||||
Connect: true,
|
Connect: true,
|
||||||
|
EnterpriseMeta: *structs.DefaultEnterpriseMeta(),
|
||||||
})
|
})
|
||||||
|
|
||||||
// Create test cases using some of the common data above.
|
// Create test cases using some of the common data above.
|
||||||
|
@ -185,7 +188,7 @@ func TestManager_BasicLifecycle(t *testing.T) {
|
||||||
expectSnap: &ConfigSnapshot{
|
expectSnap: &ConfigSnapshot{
|
||||||
Kind: structs.ServiceKindConnectProxy,
|
Kind: structs.ServiceKindConnectProxy,
|
||||||
Service: webProxy.Service,
|
Service: webProxy.Service,
|
||||||
ProxyID: webProxy.ID,
|
ProxyID: webProxy.CompoundServiceID(),
|
||||||
Address: webProxy.Address,
|
Address: webProxy.Address,
|
||||||
Port: webProxy.Port,
|
Port: webProxy.Port,
|
||||||
Proxy: webProxy.Proxy,
|
Proxy: webProxy.Proxy,
|
||||||
|
@ -206,8 +209,8 @@ func TestManager_BasicLifecycle(t *testing.T) {
|
||||||
WatchedGatewayEndpoints: map[string]map[string]structs.CheckServiceNodes{
|
WatchedGatewayEndpoints: map[string]map[string]structs.CheckServiceNodes{
|
||||||
"db": {},
|
"db": {},
|
||||||
},
|
},
|
||||||
UpstreamEndpoints: map[string]structs.CheckServiceNodes{},
|
PreparedQueryEndpoints: map[string]structs.CheckServiceNodes{},
|
||||||
WatchedServiceChecks: map[string][]structs.CheckType{},
|
WatchedServiceChecks: map[structs.ServiceID][]structs.CheckType{},
|
||||||
},
|
},
|
||||||
Datacenter: "dc1",
|
Datacenter: "dc1",
|
||||||
},
|
},
|
||||||
|
@ -229,7 +232,7 @@ func TestManager_BasicLifecycle(t *testing.T) {
|
||||||
expectSnap: &ConfigSnapshot{
|
expectSnap: &ConfigSnapshot{
|
||||||
Kind: structs.ServiceKindConnectProxy,
|
Kind: structs.ServiceKindConnectProxy,
|
||||||
Service: webProxy.Service,
|
Service: webProxy.Service,
|
||||||
ProxyID: webProxy.ID,
|
ProxyID: webProxy.CompoundServiceID(),
|
||||||
Address: webProxy.Address,
|
Address: webProxy.Address,
|
||||||
Port: webProxy.Port,
|
Port: webProxy.Port,
|
||||||
Proxy: webProxy.Proxy,
|
Proxy: webProxy.Proxy,
|
||||||
|
@ -251,8 +254,8 @@ func TestManager_BasicLifecycle(t *testing.T) {
|
||||||
WatchedGatewayEndpoints: map[string]map[string]structs.CheckServiceNodes{
|
WatchedGatewayEndpoints: map[string]map[string]structs.CheckServiceNodes{
|
||||||
"db": {},
|
"db": {},
|
||||||
},
|
},
|
||||||
UpstreamEndpoints: map[string]structs.CheckServiceNodes{},
|
PreparedQueryEndpoints: map[string]structs.CheckServiceNodes{},
|
||||||
WatchedServiceChecks: map[string][]structs.CheckType{},
|
WatchedServiceChecks: map[structs.ServiceID][]structs.CheckType{},
|
||||||
},
|
},
|
||||||
Datacenter: "dc1",
|
Datacenter: "dc1",
|
||||||
},
|
},
|
||||||
|
@ -331,7 +334,7 @@ func testManager_BasicLifecycle(
|
||||||
}()
|
}()
|
||||||
|
|
||||||
// BEFORE we register, we should be able to get a watch channel
|
// BEFORE we register, we should be able to get a watch channel
|
||||||
wCh, cancel := m.Watch(webProxy.ID)
|
wCh, cancel := m.Watch(webProxy.CompoundServiceID())
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
// And it should block with nothing sent on it yet
|
// And it should block with nothing sent on it yet
|
||||||
|
@ -355,7 +358,7 @@ func testManager_BasicLifecycle(
|
||||||
assertWatchChanRecvs(t, wCh, expectSnap)
|
assertWatchChanRecvs(t, wCh, expectSnap)
|
||||||
|
|
||||||
// Register a second watcher
|
// Register a second watcher
|
||||||
wCh2, cancel2 := m.Watch(webProxy.ID)
|
wCh2, cancel2 := m.Watch(webProxy.CompoundServiceID())
|
||||||
defer cancel2()
|
defer cancel2()
|
||||||
|
|
||||||
// New watcher should immediately receive the current state
|
// New watcher should immediately receive the current state
|
||||||
|
@ -463,11 +466,11 @@ func TestManager_deliverLatest(t *testing.T) {
|
||||||
require.NoError(err)
|
require.NoError(err)
|
||||||
|
|
||||||
snap1 := &ConfigSnapshot{
|
snap1 := &ConfigSnapshot{
|
||||||
ProxyID: "test-proxy",
|
ProxyID: structs.NewServiceID("test-proxy", nil),
|
||||||
Port: 1111,
|
Port: 1111,
|
||||||
}
|
}
|
||||||
snap2 := &ConfigSnapshot{
|
snap2 := &ConfigSnapshot{
|
||||||
ProxyID: "test-proxy",
|
ProxyID: structs.NewServiceID("test-proxy", nil),
|
||||||
Port: 2222,
|
Port: 2222,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -14,9 +14,9 @@ type configSnapshotConnectProxy struct {
|
||||||
WatchedUpstreamEndpoints map[string]map[string]structs.CheckServiceNodes
|
WatchedUpstreamEndpoints map[string]map[string]structs.CheckServiceNodes
|
||||||
WatchedGateways map[string]map[string]context.CancelFunc
|
WatchedGateways map[string]map[string]context.CancelFunc
|
||||||
WatchedGatewayEndpoints map[string]map[string]structs.CheckServiceNodes
|
WatchedGatewayEndpoints map[string]map[string]structs.CheckServiceNodes
|
||||||
WatchedServiceChecks map[string][]structs.CheckType // TODO: missing garbage collection
|
WatchedServiceChecks map[structs.ServiceID][]structs.CheckType // TODO: missing garbage collection
|
||||||
|
|
||||||
UpstreamEndpoints map[string]structs.CheckServiceNodes // DEPRECATED:see:WatchedUpstreamEndpoints
|
PreparedQueryEndpoints map[string]structs.CheckServiceNodes // DEPRECATED:see:WatchedUpstreamEndpoints
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *configSnapshotConnectProxy) IsEmpty() bool {
|
func (c *configSnapshotConnectProxy) IsEmpty() bool {
|
||||||
|
@ -30,15 +30,15 @@ func (c *configSnapshotConnectProxy) IsEmpty() bool {
|
||||||
len(c.WatchedGateways) == 0 &&
|
len(c.WatchedGateways) == 0 &&
|
||||||
len(c.WatchedGatewayEndpoints) == 0 &&
|
len(c.WatchedGatewayEndpoints) == 0 &&
|
||||||
len(c.WatchedServiceChecks) == 0 &&
|
len(c.WatchedServiceChecks) == 0 &&
|
||||||
len(c.UpstreamEndpoints) == 0
|
len(c.PreparedQueryEndpoints) == 0
|
||||||
}
|
}
|
||||||
|
|
||||||
type configSnapshotMeshGateway struct {
|
type configSnapshotMeshGateway struct {
|
||||||
WatchedServices map[string]context.CancelFunc
|
WatchedServices map[structs.ServiceID]context.CancelFunc
|
||||||
WatchedServicesSet bool
|
WatchedServicesSet bool
|
||||||
WatchedDatacenters map[string]context.CancelFunc
|
WatchedDatacenters map[string]context.CancelFunc
|
||||||
ServiceGroups map[string]structs.CheckServiceNodes
|
ServiceGroups map[structs.ServiceID]structs.CheckServiceNodes
|
||||||
ServiceResolvers map[string]*structs.ServiceResolverConfigEntry
|
ServiceResolvers map[structs.ServiceID]*structs.ServiceResolverConfigEntry
|
||||||
GatewayGroups map[string]structs.CheckServiceNodes
|
GatewayGroups map[string]structs.CheckServiceNodes
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -60,13 +60,14 @@ func (c *configSnapshotMeshGateway) IsEmpty() bool {
|
||||||
type ConfigSnapshot struct {
|
type ConfigSnapshot struct {
|
||||||
Kind structs.ServiceKind
|
Kind structs.ServiceKind
|
||||||
Service string
|
Service string
|
||||||
ProxyID string
|
ProxyID structs.ServiceID
|
||||||
Address string
|
Address string
|
||||||
Port int
|
Port int
|
||||||
TaggedAddresses map[string]structs.ServiceAddress
|
TaggedAddresses map[string]structs.ServiceAddress
|
||||||
Proxy structs.ConnectProxyConfig
|
Proxy structs.ConnectProxyConfig
|
||||||
Datacenter string
|
Datacenter string
|
||||||
Roots *structs.IndexedCARoots
|
|
||||||
|
Roots *structs.IndexedCARoots
|
||||||
|
|
||||||
// connect-proxy specific
|
// connect-proxy specific
|
||||||
ConnectProxy configSnapshotConnectProxy
|
ConnectProxy configSnapshotConnectProxy
|
||||||
|
|
|
@ -50,7 +50,7 @@ type state struct {
|
||||||
|
|
||||||
kind structs.ServiceKind
|
kind structs.ServiceKind
|
||||||
service string
|
service string
|
||||||
proxyID string
|
proxyID structs.ServiceID
|
||||||
address string
|
address string
|
||||||
port int
|
port int
|
||||||
taggedAddresses map[string]structs.ServiceAddress
|
taggedAddresses map[string]structs.ServiceAddress
|
||||||
|
@ -92,7 +92,7 @@ func newState(ns *structs.NodeService, token string) (*state, error) {
|
||||||
return &state{
|
return &state{
|
||||||
kind: ns.Kind,
|
kind: ns.Kind,
|
||||||
service: ns.Service,
|
service: ns.Service,
|
||||||
proxyID: ns.ID,
|
proxyID: ns.CompoundServiceID(),
|
||||||
address: ns.Address,
|
address: ns.Address,
|
||||||
port: ns.Port,
|
port: ns.Port,
|
||||||
taggedAddresses: taggedAddresses,
|
taggedAddresses: taggedAddresses,
|
||||||
|
@ -156,10 +156,14 @@ func (s *state) watchMeshGateway(ctx context.Context, dc string, upstreamID stri
|
||||||
ServiceKind: structs.ServiceKindMeshGateway,
|
ServiceKind: structs.ServiceKindMeshGateway,
|
||||||
UseServiceKind: true,
|
UseServiceKind: true,
|
||||||
Source: *s.source,
|
Source: *s.source,
|
||||||
|
EnterpriseMeta: *structs.DefaultEnterpriseMeta(),
|
||||||
}, "mesh-gateway:"+dc+":"+upstreamID, s.ch)
|
}, "mesh-gateway:"+dc+":"+upstreamID, s.ch)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *state) watchConnectProxyService(ctx context.Context, correlationId string, service string, dc string, filter string) error {
|
func (s *state) watchConnectProxyService(ctx context.Context, correlationId string, service string, dc string, filter string, entMeta *structs.EnterpriseMeta) error {
|
||||||
|
var finalMeta structs.EnterpriseMeta
|
||||||
|
finalMeta.Merge(entMeta)
|
||||||
|
|
||||||
return s.cache.Notify(ctx, cachetype.HealthServicesName, &structs.ServiceSpecificRequest{
|
return s.cache.Notify(ctx, cachetype.HealthServicesName, &structs.ServiceSpecificRequest{
|
||||||
Datacenter: dc,
|
Datacenter: dc,
|
||||||
QueryOptions: structs.QueryOptions{
|
QueryOptions: structs.QueryOptions{
|
||||||
|
@ -171,7 +175,8 @@ func (s *state) watchConnectProxyService(ctx context.Context, correlationId stri
|
||||||
// Note that Identifier doesn't type-prefix for service any more as it's
|
// Note that Identifier doesn't type-prefix for service any more as it's
|
||||||
// the default and makes metrics and other things much cleaner. It's
|
// the default and makes metrics and other things much cleaner. It's
|
||||||
// simpler for us if we have the type to make things unambiguous.
|
// simpler for us if we have the type to make things unambiguous.
|
||||||
Source: *s.source,
|
Source: *s.source,
|
||||||
|
EnterpriseMeta: finalMeta,
|
||||||
}, correlationId, s.ch)
|
}, correlationId, s.ch)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -190,9 +195,10 @@ func (s *state) initWatchesConnectProxy() error {
|
||||||
|
|
||||||
// Watch the leaf cert
|
// Watch the leaf cert
|
||||||
err = s.cache.Notify(s.ctx, cachetype.ConnectCALeafName, &cachetype.ConnectCALeafRequest{
|
err = s.cache.Notify(s.ctx, cachetype.ConnectCALeafName, &cachetype.ConnectCALeafRequest{
|
||||||
Datacenter: s.source.Datacenter,
|
Datacenter: s.source.Datacenter,
|
||||||
Token: s.token,
|
Token: s.token,
|
||||||
Service: s.proxyCfg.DestinationServiceName,
|
Service: s.proxyCfg.DestinationServiceName,
|
||||||
|
EnterpriseMeta: s.proxyID.EnterpriseMeta,
|
||||||
}, leafWatchID, s.ch)
|
}, leafWatchID, s.ch)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -206,7 +212,7 @@ func (s *state) initWatchesConnectProxy() error {
|
||||||
Type: structs.IntentionMatchDestination,
|
Type: structs.IntentionMatchDestination,
|
||||||
Entries: []structs.IntentionMatchEntry{
|
Entries: []structs.IntentionMatchEntry{
|
||||||
{
|
{
|
||||||
Namespace: structs.IntentionDefaultNamespace,
|
Namespace: s.proxyID.NamespaceOrDefault(),
|
||||||
Name: s.proxyCfg.DestinationServiceName,
|
Name: s.proxyCfg.DestinationServiceName,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -218,14 +224,15 @@ func (s *state) initWatchesConnectProxy() error {
|
||||||
|
|
||||||
// Watch for service check updates
|
// Watch for service check updates
|
||||||
err = s.cache.Notify(s.ctx, cachetype.ServiceHTTPChecksName, &cachetype.ServiceHTTPChecksRequest{
|
err = s.cache.Notify(s.ctx, cachetype.ServiceHTTPChecksName, &cachetype.ServiceHTTPChecksRequest{
|
||||||
ServiceID: s.proxyCfg.DestinationServiceID,
|
ServiceID: s.proxyCfg.DestinationServiceID,
|
||||||
}, svcChecksWatchIDPrefix+s.proxyCfg.DestinationServiceID, s.ch)
|
EnterpriseMeta: s.proxyID.EnterpriseMeta,
|
||||||
|
}, svcChecksWatchIDPrefix+structs.ServiceIDString(s.proxyCfg.DestinationServiceID, &s.proxyID.EnterpriseMeta), s.ch)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO(namespaces): pull this from something like s.source.Namespace?
|
// let namespace inference happen server side
|
||||||
currentNamespace := "default"
|
currentNamespace := ""
|
||||||
|
|
||||||
// Watch for updates to service endpoints for all upstreams
|
// Watch for updates to service endpoints for all upstreams
|
||||||
for _, u := range s.proxyCfg.Upstreams {
|
for _, u := range s.proxyCfg.Upstreams {
|
||||||
|
@ -317,10 +324,11 @@ func (s *state) initWatchesMeshGateway() error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Watch for all services
|
// Watch for all services
|
||||||
err = s.cache.Notify(s.ctx, cachetype.CatalogListServicesName, &structs.DCSpecificRequest{
|
err = s.cache.Notify(s.ctx, cachetype.CatalogServiceListName, &structs.DCSpecificRequest{
|
||||||
Datacenter: s.source.Datacenter,
|
Datacenter: s.source.Datacenter,
|
||||||
QueryOptions: structs.QueryOptions{Token: s.token},
|
QueryOptions: structs.QueryOptions{Token: s.token},
|
||||||
Source: *s.source,
|
Source: *s.source,
|
||||||
|
EnterpriseMeta: *structs.WildcardEnterpriseMeta(),
|
||||||
}, serviceListWatchID, s.ch)
|
}, serviceListWatchID, s.ch)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -342,9 +350,10 @@ func (s *state) initWatchesMeshGateway() error {
|
||||||
|
|
||||||
// Watch service-resolvers so we can setup service subset clusters
|
// Watch service-resolvers so we can setup service subset clusters
|
||||||
err = s.cache.Notify(s.ctx, cachetype.ConfigEntriesName, &structs.ConfigEntryQuery{
|
err = s.cache.Notify(s.ctx, cachetype.ConfigEntriesName, &structs.ConfigEntryQuery{
|
||||||
Datacenter: s.source.Datacenter,
|
Datacenter: s.source.Datacenter,
|
||||||
QueryOptions: structs.QueryOptions{Token: s.token},
|
QueryOptions: structs.QueryOptions{Token: s.token},
|
||||||
Kind: structs.ServiceResolver,
|
Kind: structs.ServiceResolver,
|
||||||
|
EnterpriseMeta: *structs.WildcardEnterpriseMeta(),
|
||||||
}, serviceResolversWatchID, s.ch)
|
}, serviceResolversWatchID, s.ch)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -374,14 +383,15 @@ func (s *state) initialConfigSnapshot() ConfigSnapshot {
|
||||||
snap.ConnectProxy.WatchedUpstreamEndpoints = make(map[string]map[string]structs.CheckServiceNodes)
|
snap.ConnectProxy.WatchedUpstreamEndpoints = make(map[string]map[string]structs.CheckServiceNodes)
|
||||||
snap.ConnectProxy.WatchedGateways = make(map[string]map[string]context.CancelFunc)
|
snap.ConnectProxy.WatchedGateways = make(map[string]map[string]context.CancelFunc)
|
||||||
snap.ConnectProxy.WatchedGatewayEndpoints = make(map[string]map[string]structs.CheckServiceNodes)
|
snap.ConnectProxy.WatchedGatewayEndpoints = make(map[string]map[string]structs.CheckServiceNodes)
|
||||||
snap.ConnectProxy.WatchedServiceChecks = make(map[string][]structs.CheckType)
|
snap.ConnectProxy.WatchedServiceChecks = make(map[structs.ServiceID][]structs.CheckType)
|
||||||
|
|
||||||
snap.ConnectProxy.UpstreamEndpoints = make(map[string]structs.CheckServiceNodes) // TODO(rb): deprecated
|
snap.ConnectProxy.PreparedQueryEndpoints = make(map[string]structs.CheckServiceNodes) // TODO(rb): deprecated
|
||||||
case structs.ServiceKindMeshGateway:
|
case structs.ServiceKindMeshGateway:
|
||||||
snap.MeshGateway.WatchedServices = make(map[string]context.CancelFunc)
|
snap.MeshGateway.WatchedServices = make(map[structs.ServiceID]context.CancelFunc)
|
||||||
snap.MeshGateway.WatchedDatacenters = make(map[string]context.CancelFunc)
|
snap.MeshGateway.WatchedDatacenters = make(map[string]context.CancelFunc)
|
||||||
snap.MeshGateway.ServiceGroups = make(map[string]structs.CheckServiceNodes)
|
snap.MeshGateway.ServiceGroups = make(map[structs.ServiceID]structs.CheckServiceNodes)
|
||||||
snap.MeshGateway.GatewayGroups = make(map[string]structs.CheckServiceNodes)
|
snap.MeshGateway.GatewayGroups = make(map[string]structs.CheckServiceNodes)
|
||||||
|
snap.MeshGateway.ServiceResolvers = make(map[structs.ServiceID]*structs.ServiceResolverConfigEntry)
|
||||||
// there is no need to initialize the map of service resolvers as we
|
// there is no need to initialize the map of service resolvers as we
|
||||||
// fully rebuild it every time we get updates
|
// fully rebuild it every time we get updates
|
||||||
}
|
}
|
||||||
|
@ -551,28 +561,20 @@ func (s *state) handleUpdateConnectProxy(u cache.UpdateEvent, snap *ConfigSnapsh
|
||||||
}
|
}
|
||||||
snap.ConnectProxy.WatchedGatewayEndpoints[svc][dc] = resp.Nodes
|
snap.ConnectProxy.WatchedGatewayEndpoints[svc][dc] = resp.Nodes
|
||||||
|
|
||||||
case strings.HasPrefix(u.CorrelationID, "upstream:"+serviceIDPrefix):
|
|
||||||
resp, ok := u.Result.(*structs.IndexedCheckServiceNodes)
|
|
||||||
if !ok {
|
|
||||||
return fmt.Errorf("invalid type for response: %T", u.Result)
|
|
||||||
}
|
|
||||||
svc := strings.TrimPrefix(u.CorrelationID, "upstream:"+serviceIDPrefix)
|
|
||||||
snap.ConnectProxy.UpstreamEndpoints[svc] = resp.Nodes
|
|
||||||
|
|
||||||
case strings.HasPrefix(u.CorrelationID, "upstream:"+preparedQueryIDPrefix):
|
case strings.HasPrefix(u.CorrelationID, "upstream:"+preparedQueryIDPrefix):
|
||||||
resp, ok := u.Result.(*structs.PreparedQueryExecuteResponse)
|
resp, ok := u.Result.(*structs.PreparedQueryExecuteResponse)
|
||||||
if !ok {
|
if !ok {
|
||||||
return fmt.Errorf("invalid type for response: %T", u.Result)
|
return fmt.Errorf("invalid type for response: %T", u.Result)
|
||||||
}
|
}
|
||||||
pq := strings.TrimPrefix(u.CorrelationID, "upstream:")
|
pq := strings.TrimPrefix(u.CorrelationID, "upstream:")
|
||||||
snap.ConnectProxy.UpstreamEndpoints[pq] = resp.Nodes
|
snap.ConnectProxy.PreparedQueryEndpoints[pq] = resp.Nodes
|
||||||
|
|
||||||
case strings.HasPrefix(u.CorrelationID, svcChecksWatchIDPrefix):
|
case strings.HasPrefix(u.CorrelationID, svcChecksWatchIDPrefix):
|
||||||
resp, ok := u.Result.([]structs.CheckType)
|
resp, ok := u.Result.([]structs.CheckType)
|
||||||
if !ok {
|
if !ok {
|
||||||
return fmt.Errorf("invalid type for service checks response: %T, want: []structs.CheckType", u.Result)
|
return fmt.Errorf("invalid type for service checks response: %T, want: []structs.CheckType", u.Result)
|
||||||
}
|
}
|
||||||
svcID := strings.TrimPrefix(u.CorrelationID, svcChecksWatchIDPrefix)
|
svcID := structs.ServiceIDFromString(strings.TrimPrefix(u.CorrelationID, svcChecksWatchIDPrefix))
|
||||||
snap.ConnectProxy.WatchedServiceChecks[svcID] = resp
|
snap.ConnectProxy.WatchedServiceChecks[svcID] = resp
|
||||||
|
|
||||||
default:
|
default:
|
||||||
|
@ -644,6 +646,7 @@ func (s *state) resetWatchesFromChain(
|
||||||
target.Service,
|
target.Service,
|
||||||
target.Datacenter,
|
target.Datacenter,
|
||||||
target.Subset.Filter,
|
target.Subset.Filter,
|
||||||
|
target.GetEnterpriseMetadata(),
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
cancel()
|
cancel()
|
||||||
|
@ -696,33 +699,37 @@ func (s *state) handleUpdateMeshGateway(u cache.UpdateEvent, snap *ConfigSnapsho
|
||||||
}
|
}
|
||||||
snap.Roots = roots
|
snap.Roots = roots
|
||||||
case serviceListWatchID:
|
case serviceListWatchID:
|
||||||
services, ok := u.Result.(*structs.IndexedServices)
|
services, ok := u.Result.(*structs.IndexedServiceList)
|
||||||
if !ok {
|
if !ok {
|
||||||
return fmt.Errorf("invalid type for response: %T", u.Result)
|
return fmt.Errorf("invalid type for response: %T", u.Result)
|
||||||
}
|
}
|
||||||
|
|
||||||
for svcName := range services.Services {
|
svcMap := make(map[structs.ServiceID]struct{})
|
||||||
if _, ok := snap.MeshGateway.WatchedServices[svcName]; !ok {
|
for _, svc := range services.Services {
|
||||||
|
sid := svc.ToServiceID()
|
||||||
|
if _, ok := snap.MeshGateway.WatchedServices[sid]; !ok {
|
||||||
ctx, cancel := context.WithCancel(s.ctx)
|
ctx, cancel := context.WithCancel(s.ctx)
|
||||||
err := s.cache.Notify(ctx, cachetype.HealthServicesName, &structs.ServiceSpecificRequest{
|
err := s.cache.Notify(ctx, cachetype.HealthServicesName, &structs.ServiceSpecificRequest{
|
||||||
Datacenter: s.source.Datacenter,
|
Datacenter: s.source.Datacenter,
|
||||||
QueryOptions: structs.QueryOptions{Token: s.token},
|
QueryOptions: structs.QueryOptions{Token: s.token},
|
||||||
ServiceName: svcName,
|
ServiceName: svc.Name,
|
||||||
Connect: true,
|
Connect: true,
|
||||||
}, fmt.Sprintf("connect-service:%s", svcName), s.ch)
|
EnterpriseMeta: sid.EnterpriseMeta,
|
||||||
|
}, fmt.Sprintf("connect-service:%s", sid.String()), s.ch)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
s.logger.Printf("[ERR] mesh-gateway: failed to register watch for connect-service:%s", svcName)
|
s.logger.Printf("[ERR] mesh-gateway: failed to register watch for connect-service:%s", sid.String())
|
||||||
cancel()
|
cancel()
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
snap.MeshGateway.WatchedServices[svcName] = cancel
|
snap.MeshGateway.WatchedServices[sid] = cancel
|
||||||
|
svcMap[sid] = struct{}{}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for svcName, cancelFn := range snap.MeshGateway.WatchedServices {
|
for sid, cancelFn := range snap.MeshGateway.WatchedServices {
|
||||||
if _, ok := services.Services[svcName]; !ok {
|
if _, ok := svcMap[sid]; !ok {
|
||||||
delete(snap.MeshGateway.WatchedServices, svcName)
|
delete(snap.MeshGateway.WatchedServices, sid)
|
||||||
cancelFn()
|
cancelFn()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -752,6 +759,7 @@ func (s *state) handleUpdateMeshGateway(u cache.UpdateEvent, snap *ConfigSnapsho
|
||||||
ServiceKind: structs.ServiceKindMeshGateway,
|
ServiceKind: structs.ServiceKindMeshGateway,
|
||||||
UseServiceKind: true,
|
UseServiceKind: true,
|
||||||
Source: *s.source,
|
Source: *s.source,
|
||||||
|
EnterpriseMeta: *structs.DefaultEnterpriseMeta(),
|
||||||
}, fmt.Sprintf("mesh-gateway:%s", dc), s.ch)
|
}, fmt.Sprintf("mesh-gateway:%s", dc), s.ch)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -784,10 +792,10 @@ func (s *state) handleUpdateMeshGateway(u cache.UpdateEvent, snap *ConfigSnapsho
|
||||||
return fmt.Errorf("invalid type for response: %T", u.Result)
|
return fmt.Errorf("invalid type for response: %T", u.Result)
|
||||||
}
|
}
|
||||||
|
|
||||||
resolvers := make(map[string]*structs.ServiceResolverConfigEntry)
|
resolvers := make(map[structs.ServiceID]*structs.ServiceResolverConfigEntry)
|
||||||
for _, entry := range configEntries.Entries {
|
for _, entry := range configEntries.Entries {
|
||||||
if resolver, ok := entry.(*structs.ServiceResolverConfigEntry); ok {
|
if resolver, ok := entry.(*structs.ServiceResolverConfigEntry); ok {
|
||||||
resolvers[resolver.Name] = resolver
|
resolvers[structs.NewServiceID(resolver.Name, &resolver.EnterpriseMeta)] = resolver
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
snap.MeshGateway.ServiceResolvers = resolvers
|
snap.MeshGateway.ServiceResolvers = resolvers
|
||||||
|
@ -799,12 +807,12 @@ func (s *state) handleUpdateMeshGateway(u cache.UpdateEvent, snap *ConfigSnapsho
|
||||||
return fmt.Errorf("invalid type for response: %T", u.Result)
|
return fmt.Errorf("invalid type for response: %T", u.Result)
|
||||||
}
|
}
|
||||||
|
|
||||||
svc := strings.TrimPrefix(u.CorrelationID, "connect-service:")
|
sid := structs.ServiceIDFromString(strings.TrimPrefix(u.CorrelationID, "connect-service:"))
|
||||||
|
|
||||||
if len(resp.Nodes) > 0 {
|
if len(resp.Nodes) > 0 {
|
||||||
snap.MeshGateway.ServiceGroups[svc] = resp.Nodes
|
snap.MeshGateway.ServiceGroups[sid] = resp.Nodes
|
||||||
} else if _, ok := snap.MeshGateway.ServiceGroups[svc]; ok {
|
} else if _, ok := snap.MeshGateway.ServiceGroups[sid]; ok {
|
||||||
delete(snap.MeshGateway.ServiceGroups, svc)
|
delete(snap.MeshGateway.ServiceGroups, sid)
|
||||||
}
|
}
|
||||||
case strings.HasPrefix(u.CorrelationID, "mesh-gateway:"):
|
case strings.HasPrefix(u.CorrelationID, "mesh-gateway:"):
|
||||||
resp, ok := u.Result.(*structs.IndexedCheckServiceNodes)
|
resp, ok := u.Result.(*structs.IndexedCheckServiceNodes)
|
||||||
|
@ -845,7 +853,7 @@ func (s *state) Changed(ns *structs.NodeService, token string) bool {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
return ns.Kind != s.kind ||
|
return ns.Kind != s.kind ||
|
||||||
s.proxyID != ns.ID ||
|
s.proxyID != ns.CompoundServiceID() ||
|
||||||
s.address != ns.Address ||
|
s.address != ns.Address ||
|
||||||
s.port != ns.Port ||
|
s.port != ns.Port ||
|
||||||
!reflect.DeepEqual(s.proxyCfg, ns.Proxy) ||
|
!reflect.DeepEqual(s.proxyCfg, ns.Proxy) ||
|
||||||
|
|
|
@ -185,7 +185,7 @@ func genVerifyRootsWatch(expectedDatacenter string) verifyWatchRequest {
|
||||||
}
|
}
|
||||||
|
|
||||||
func genVerifyListServicesWatch(expectedDatacenter string) verifyWatchRequest {
|
func genVerifyListServicesWatch(expectedDatacenter string) verifyWatchRequest {
|
||||||
return genVerifyDCSpecificWatch(cachetype.CatalogListServicesName, expectedDatacenter)
|
return genVerifyDCSpecificWatch(cachetype.CatalogServiceListName, expectedDatacenter)
|
||||||
}
|
}
|
||||||
|
|
||||||
func verifyDatacentersWatch(t testing.TB, cacheType string, request cache.Request) {
|
func verifyDatacentersWatch(t testing.TB, cacheType string, request cache.Request) {
|
||||||
|
@ -383,7 +383,7 @@ func TestState_WatchesAndUpdates(t *testing.T) {
|
||||||
"discovery-chain:api": genVerifyDiscoveryChainWatch(&structs.DiscoveryChainRequest{
|
"discovery-chain:api": genVerifyDiscoveryChainWatch(&structs.DiscoveryChainRequest{
|
||||||
Name: "api",
|
Name: "api",
|
||||||
EvaluateInDatacenter: "dc1",
|
EvaluateInDatacenter: "dc1",
|
||||||
EvaluateInNamespace: "default",
|
EvaluateInNamespace: "",
|
||||||
Datacenter: "dc1",
|
Datacenter: "dc1",
|
||||||
OverrideMeshGateway: structs.MeshGatewayConfig{
|
OverrideMeshGateway: structs.MeshGatewayConfig{
|
||||||
Mode: meshGatewayProxyConfigValue,
|
Mode: meshGatewayProxyConfigValue,
|
||||||
|
@ -392,7 +392,7 @@ func TestState_WatchesAndUpdates(t *testing.T) {
|
||||||
"discovery-chain:api-failover-remote?dc=dc2": genVerifyDiscoveryChainWatch(&structs.DiscoveryChainRequest{
|
"discovery-chain:api-failover-remote?dc=dc2": genVerifyDiscoveryChainWatch(&structs.DiscoveryChainRequest{
|
||||||
Name: "api-failover-remote",
|
Name: "api-failover-remote",
|
||||||
EvaluateInDatacenter: "dc2",
|
EvaluateInDatacenter: "dc2",
|
||||||
EvaluateInNamespace: "default",
|
EvaluateInNamespace: "",
|
||||||
Datacenter: "dc1",
|
Datacenter: "dc1",
|
||||||
OverrideMeshGateway: structs.MeshGatewayConfig{
|
OverrideMeshGateway: structs.MeshGatewayConfig{
|
||||||
Mode: structs.MeshGatewayModeRemote,
|
Mode: structs.MeshGatewayModeRemote,
|
||||||
|
@ -401,7 +401,7 @@ func TestState_WatchesAndUpdates(t *testing.T) {
|
||||||
"discovery-chain:api-failover-local?dc=dc2": genVerifyDiscoveryChainWatch(&structs.DiscoveryChainRequest{
|
"discovery-chain:api-failover-local?dc=dc2": genVerifyDiscoveryChainWatch(&structs.DiscoveryChainRequest{
|
||||||
Name: "api-failover-local",
|
Name: "api-failover-local",
|
||||||
EvaluateInDatacenter: "dc2",
|
EvaluateInDatacenter: "dc2",
|
||||||
EvaluateInNamespace: "default",
|
EvaluateInNamespace: "",
|
||||||
Datacenter: "dc1",
|
Datacenter: "dc1",
|
||||||
OverrideMeshGateway: structs.MeshGatewayConfig{
|
OverrideMeshGateway: structs.MeshGatewayConfig{
|
||||||
Mode: structs.MeshGatewayModeLocal,
|
Mode: structs.MeshGatewayModeLocal,
|
||||||
|
@ -410,7 +410,7 @@ func TestState_WatchesAndUpdates(t *testing.T) {
|
||||||
"discovery-chain:api-failover-direct?dc=dc2": genVerifyDiscoveryChainWatch(&structs.DiscoveryChainRequest{
|
"discovery-chain:api-failover-direct?dc=dc2": genVerifyDiscoveryChainWatch(&structs.DiscoveryChainRequest{
|
||||||
Name: "api-failover-direct",
|
Name: "api-failover-direct",
|
||||||
EvaluateInDatacenter: "dc2",
|
EvaluateInDatacenter: "dc2",
|
||||||
EvaluateInNamespace: "default",
|
EvaluateInNamespace: "",
|
||||||
Datacenter: "dc1",
|
Datacenter: "dc1",
|
||||||
OverrideMeshGateway: structs.MeshGatewayConfig{
|
OverrideMeshGateway: structs.MeshGatewayConfig{
|
||||||
Mode: structs.MeshGatewayModeNone,
|
Mode: structs.MeshGatewayModeNone,
|
||||||
|
@ -419,7 +419,7 @@ func TestState_WatchesAndUpdates(t *testing.T) {
|
||||||
"discovery-chain:api-dc2": genVerifyDiscoveryChainWatch(&structs.DiscoveryChainRequest{
|
"discovery-chain:api-dc2": genVerifyDiscoveryChainWatch(&structs.DiscoveryChainRequest{
|
||||||
Name: "api-dc2",
|
Name: "api-dc2",
|
||||||
EvaluateInDatacenter: "dc1",
|
EvaluateInDatacenter: "dc1",
|
||||||
EvaluateInNamespace: "default",
|
EvaluateInNamespace: "",
|
||||||
Datacenter: "dc1",
|
Datacenter: "dc1",
|
||||||
OverrideMeshGateway: structs.MeshGatewayConfig{
|
OverrideMeshGateway: structs.MeshGatewayConfig{
|
||||||
Mode: meshGatewayProxyConfigValue,
|
Mode: meshGatewayProxyConfigValue,
|
||||||
|
@ -506,7 +506,7 @@ func TestState_WatchesAndUpdates(t *testing.T) {
|
||||||
require.Len(t, snap.ConnectProxy.WatchedGatewayEndpoints, 5, "%+v", snap.ConnectProxy.WatchedGatewayEndpoints)
|
require.Len(t, snap.ConnectProxy.WatchedGatewayEndpoints, 5, "%+v", snap.ConnectProxy.WatchedGatewayEndpoints)
|
||||||
|
|
||||||
require.Len(t, snap.ConnectProxy.WatchedServiceChecks, 0, "%+v", snap.ConnectProxy.WatchedServiceChecks)
|
require.Len(t, snap.ConnectProxy.WatchedServiceChecks, 0, "%+v", snap.ConnectProxy.WatchedServiceChecks)
|
||||||
require.Len(t, snap.ConnectProxy.UpstreamEndpoints, 0, "%+v", snap.ConnectProxy.UpstreamEndpoints)
|
require.Len(t, snap.ConnectProxy.PreparedQueryEndpoints, 0, "%+v", snap.ConnectProxy.PreparedQueryEndpoints)
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -532,7 +532,7 @@ func TestState_WatchesAndUpdates(t *testing.T) {
|
||||||
require.Len(t, snap.ConnectProxy.WatchedGatewayEndpoints, 5, "%+v", snap.ConnectProxy.WatchedGatewayEndpoints)
|
require.Len(t, snap.ConnectProxy.WatchedGatewayEndpoints, 5, "%+v", snap.ConnectProxy.WatchedGatewayEndpoints)
|
||||||
|
|
||||||
require.Len(t, snap.ConnectProxy.WatchedServiceChecks, 0, "%+v", snap.ConnectProxy.WatchedServiceChecks)
|
require.Len(t, snap.ConnectProxy.WatchedServiceChecks, 0, "%+v", snap.ConnectProxy.WatchedServiceChecks)
|
||||||
require.Len(t, snap.ConnectProxy.UpstreamEndpoints, 0, "%+v", snap.ConnectProxy.UpstreamEndpoints)
|
require.Len(t, snap.ConnectProxy.PreparedQueryEndpoints, 0, "%+v", snap.ConnectProxy.PreparedQueryEndpoints)
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -589,8 +589,8 @@ func TestState_WatchesAndUpdates(t *testing.T) {
|
||||||
events: []cache.UpdateEvent{
|
events: []cache.UpdateEvent{
|
||||||
cache.UpdateEvent{
|
cache.UpdateEvent{
|
||||||
CorrelationID: serviceListWatchID,
|
CorrelationID: serviceListWatchID,
|
||||||
Result: &structs.IndexedServices{
|
Result: &structs.IndexedServiceList{
|
||||||
Services: make(structs.Services),
|
Services: make(structs.ServiceList, 0),
|
||||||
},
|
},
|
||||||
Err: nil,
|
Err: nil,
|
||||||
},
|
},
|
||||||
|
|
|
@ -571,7 +571,7 @@ func TestConfigSnapshot(t testing.T) *ConfigSnapshot {
|
||||||
return &ConfigSnapshot{
|
return &ConfigSnapshot{
|
||||||
Kind: structs.ServiceKindConnectProxy,
|
Kind: structs.ServiceKindConnectProxy,
|
||||||
Service: "web-sidecar-proxy",
|
Service: "web-sidecar-proxy",
|
||||||
ProxyID: "web-sidecar-proxy",
|
ProxyID: structs.NewServiceID("web-sidecar-proxy", nil),
|
||||||
Address: "0.0.0.0",
|
Address: "0.0.0.0",
|
||||||
Port: 9999,
|
Port: 9999,
|
||||||
Proxy: structs.ConnectProxyConfig{
|
Proxy: structs.ConnectProxyConfig{
|
||||||
|
@ -590,7 +590,7 @@ func TestConfigSnapshot(t testing.T) *ConfigSnapshot {
|
||||||
DiscoveryChain: map[string]*structs.CompiledDiscoveryChain{
|
DiscoveryChain: map[string]*structs.CompiledDiscoveryChain{
|
||||||
"db": dbChain,
|
"db": dbChain,
|
||||||
},
|
},
|
||||||
UpstreamEndpoints: map[string]structs.CheckServiceNodes{
|
PreparedQueryEndpoints: map[string]structs.CheckServiceNodes{
|
||||||
"prepared_query:geo-cache": TestUpstreamNodes(t),
|
"prepared_query:geo-cache": TestUpstreamNodes(t),
|
||||||
},
|
},
|
||||||
WatchedUpstreamEndpoints: map[string]map[string]structs.CheckServiceNodes{
|
WatchedUpstreamEndpoints: map[string]map[string]structs.CheckServiceNodes{
|
||||||
|
@ -866,7 +866,7 @@ func testConfigSnapshotDiscoveryChain(t testing.T, variation string, additionalE
|
||||||
snap := &ConfigSnapshot{
|
snap := &ConfigSnapshot{
|
||||||
Kind: structs.ServiceKindConnectProxy,
|
Kind: structs.ServiceKindConnectProxy,
|
||||||
Service: "web-sidecar-proxy",
|
Service: "web-sidecar-proxy",
|
||||||
ProxyID: "web-sidecar-proxy",
|
ProxyID: structs.NewServiceID("web-sidecar-proxy", nil),
|
||||||
Address: "0.0.0.0",
|
Address: "0.0.0.0",
|
||||||
Port: 9999,
|
Port: 9999,
|
||||||
Proxy: structs.ConnectProxyConfig{
|
Proxy: structs.ConnectProxyConfig{
|
||||||
|
@ -979,7 +979,7 @@ func testConfigSnapshotMeshGateway(t testing.T, populateServices bool) *ConfigSn
|
||||||
snap := &ConfigSnapshot{
|
snap := &ConfigSnapshot{
|
||||||
Kind: structs.ServiceKindMeshGateway,
|
Kind: structs.ServiceKindMeshGateway,
|
||||||
Service: "mesh-gateway",
|
Service: "mesh-gateway",
|
||||||
ProxyID: "mesh-gateway",
|
ProxyID: structs.NewServiceID("mesh-gateway", nil),
|
||||||
Address: "1.2.3.4",
|
Address: "1.2.3.4",
|
||||||
Port: 8443,
|
Port: 8443,
|
||||||
Proxy: structs.ConnectProxyConfig{
|
Proxy: structs.ConnectProxyConfig{
|
||||||
|
@ -1004,17 +1004,17 @@ func testConfigSnapshotMeshGateway(t testing.T, populateServices bool) *ConfigSn
|
||||||
|
|
||||||
if populateServices {
|
if populateServices {
|
||||||
snap.MeshGateway = configSnapshotMeshGateway{
|
snap.MeshGateway = configSnapshotMeshGateway{
|
||||||
WatchedServices: map[string]context.CancelFunc{
|
WatchedServices: map[structs.ServiceID]context.CancelFunc{
|
||||||
"foo": nil,
|
structs.NewServiceID("foo", nil): nil,
|
||||||
"bar": nil,
|
structs.NewServiceID("bar", nil): nil,
|
||||||
},
|
},
|
||||||
WatchedServicesSet: true,
|
WatchedServicesSet: true,
|
||||||
WatchedDatacenters: map[string]context.CancelFunc{
|
WatchedDatacenters: map[string]context.CancelFunc{
|
||||||
"dc2": nil,
|
"dc2": nil,
|
||||||
},
|
},
|
||||||
ServiceGroups: map[string]structs.CheckServiceNodes{
|
ServiceGroups: map[structs.ServiceID]structs.CheckServiceNodes{
|
||||||
"foo": TestGatewayServiceGroupFooDC1(t),
|
structs.NewServiceID("foo", nil): TestGatewayServiceGroupFooDC1(t),
|
||||||
"bar": TestGatewayServiceGroupBarDC1(t),
|
structs.NewServiceID("bar", nil): TestGatewayServiceGroupBarDC1(t),
|
||||||
},
|
},
|
||||||
GatewayGroups: map[string]structs.CheckServiceNodes{
|
GatewayGroups: map[string]structs.CheckServiceNodes{
|
||||||
"dc2": TestGatewayNodesDC2(t),
|
"dc2": TestGatewayNodesDC2(t),
|
||||||
|
@ -1029,7 +1029,7 @@ func TestConfigSnapshotExposeConfig(t testing.T) *ConfigSnapshot {
|
||||||
return &ConfigSnapshot{
|
return &ConfigSnapshot{
|
||||||
Kind: structs.ServiceKindConnectProxy,
|
Kind: structs.ServiceKindConnectProxy,
|
||||||
Service: "web-proxy",
|
Service: "web-proxy",
|
||||||
ProxyID: "web-proxy",
|
ProxyID: structs.NewServiceID("web-proxy", nil),
|
||||||
Address: "1.2.3.4",
|
Address: "1.2.3.4",
|
||||||
Port: 8080,
|
Port: 8080,
|
||||||
Proxy: structs.ConnectProxyConfig{
|
Proxy: structs.ConnectProxyConfig{
|
||||||
|
|
|
@ -452,7 +452,7 @@ type asyncRegisterRequest struct {
|
||||||
func makeConfigRequest(agent *Agent, registration *serviceRegistration) *structs.ServiceConfigRequest {
|
func makeConfigRequest(agent *Agent, registration *serviceRegistration) *structs.ServiceConfigRequest {
|
||||||
ns := registration.service
|
ns := registration.service
|
||||||
name := ns.Service
|
name := ns.Service
|
||||||
var upstreams []string
|
var upstreams []structs.ServiceID
|
||||||
|
|
||||||
// Note that only sidecar proxies should even make it here for now although
|
// Note that only sidecar proxies should even make it here for now although
|
||||||
// later that will change to add the condition.
|
// later that will change to add the condition.
|
||||||
|
@ -465,16 +465,19 @@ func makeConfigRequest(agent *Agent, registration *serviceRegistration) *structs
|
||||||
// learn about their configs.
|
// learn about their configs.
|
||||||
for _, us := range ns.Proxy.Upstreams {
|
for _, us := range ns.Proxy.Upstreams {
|
||||||
if us.DestinationType == "" || us.DestinationType == structs.UpstreamDestTypeService {
|
if us.DestinationType == "" || us.DestinationType == structs.UpstreamDestTypeService {
|
||||||
upstreams = append(upstreams, us.DestinationName)
|
sid := us.DestinationID()
|
||||||
|
sid.EnterpriseMeta.Merge(&ns.EnterpriseMeta)
|
||||||
|
upstreams = append(upstreams, sid)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
req := &structs.ServiceConfigRequest{
|
req := &structs.ServiceConfigRequest{
|
||||||
Name: name,
|
Name: name,
|
||||||
Datacenter: agent.config.Datacenter,
|
Datacenter: agent.config.Datacenter,
|
||||||
QueryOptions: structs.QueryOptions{Token: agent.tokens.AgentToken()},
|
QueryOptions: structs.QueryOptions{Token: agent.tokens.AgentToken()},
|
||||||
Upstreams: upstreams,
|
UpstreamIDs: upstreams,
|
||||||
|
EnterpriseMeta: ns.EnterpriseMeta,
|
||||||
}
|
}
|
||||||
if registration.token != "" {
|
if registration.token != "" {
|
||||||
req.QueryOptions.Token = registration.token
|
req.QueryOptions.Token = registration.token
|
||||||
|
@ -527,7 +530,7 @@ func (w *serviceConfigWatch) mergeServiceConfig() (*structs.NodeService, error)
|
||||||
us.MeshGateway.Mode = ns.Proxy.MeshGateway.Mode
|
us.MeshGateway.Mode = ns.Proxy.MeshGateway.Mode
|
||||||
}
|
}
|
||||||
|
|
||||||
usCfg, ok := w.defaults.UpstreamConfigs[us.DestinationName]
|
usCfg, ok := w.defaults.UpstreamIDConfigs.GetUpstreamConfig(us.DestinationID())
|
||||||
if !ok {
|
if !ok {
|
||||||
// No config defaults to merge
|
// No config defaults to merge
|
||||||
continue
|
continue
|
||||||
|
|
|
@ -338,9 +338,12 @@ func TestServiceManager_PersistService_API(t *testing.T) {
|
||||||
"foo": 1,
|
"foo": 1,
|
||||||
"protocol": "http",
|
"protocol": "http",
|
||||||
},
|
},
|
||||||
UpstreamConfigs: map[string]map[string]interface{}{
|
UpstreamIDConfigs: structs.UpstreamConfigs{
|
||||||
"redis": map[string]interface{}{
|
structs.UpstreamConfig{
|
||||||
"protocol": "tcp",
|
Upstream: structs.NewServiceID("redis", nil),
|
||||||
|
Config: map[string]interface{}{
|
||||||
|
"protocol": "tcp",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -376,9 +379,12 @@ func TestServiceManager_PersistService_API(t *testing.T) {
|
||||||
"foo": 1,
|
"foo": 1,
|
||||||
"protocol": "http",
|
"protocol": "http",
|
||||||
},
|
},
|
||||||
UpstreamConfigs: map[string]map[string]interface{}{
|
UpstreamIDConfigs: structs.UpstreamConfigs{
|
||||||
"redis": map[string]interface{}{
|
structs.UpstreamConfig{
|
||||||
"protocol": "tcp",
|
Upstream: structs.NewServiceID("redis", nil),
|
||||||
|
Config: map[string]interface{}{
|
||||||
|
"protocol": "tcp",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -549,9 +555,12 @@ func TestServiceManager_PersistService_ConfigFiles(t *testing.T) {
|
||||||
"foo": 1,
|
"foo": 1,
|
||||||
"protocol": "http",
|
"protocol": "http",
|
||||||
},
|
},
|
||||||
UpstreamConfigs: map[string]map[string]interface{}{
|
UpstreamIDConfigs: structs.UpstreamConfigs{
|
||||||
"redis": map[string]interface{}{
|
structs.UpstreamConfig{
|
||||||
"protocol": "tcp",
|
Upstream: structs.NewServiceID("redis", nil),
|
||||||
|
Config: map[string]interface{}{
|
||||||
|
"protocol": "tcp",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
|
@ -41,6 +41,7 @@ type ConfigEntry interface {
|
||||||
CanRead(acl.Authorizer) bool
|
CanRead(acl.Authorizer) bool
|
||||||
CanWrite(acl.Authorizer) bool
|
CanWrite(acl.Authorizer) bool
|
||||||
|
|
||||||
|
GetEnterpriseMeta() *EnterpriseMeta
|
||||||
GetRaftIndex() *RaftIndex
|
GetRaftIndex() *RaftIndex
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -61,6 +62,7 @@ type ServiceConfigEntry struct {
|
||||||
//
|
//
|
||||||
// Connect ConnectConfiguration
|
// Connect ConnectConfiguration
|
||||||
|
|
||||||
|
EnterpriseMeta `hcl:",squash" mapstructure:",squash"`
|
||||||
RaftIndex
|
RaftIndex
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -84,6 +86,8 @@ func (e *ServiceConfigEntry) Normalize() error {
|
||||||
e.Kind = ServiceDefaults
|
e.Kind = ServiceDefaults
|
||||||
e.Protocol = strings.ToLower(e.Protocol)
|
e.Protocol = strings.ToLower(e.Protocol)
|
||||||
|
|
||||||
|
e.EnterpriseMeta.Normalize()
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -91,12 +95,16 @@ func (e *ServiceConfigEntry) Validate() error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *ServiceConfigEntry) CanRead(rule acl.Authorizer) bool {
|
func (e *ServiceConfigEntry) CanRead(authz acl.Authorizer) bool {
|
||||||
return rule.ServiceRead(e.Name, nil) == acl.Allow
|
var authzContext acl.AuthorizerContext
|
||||||
|
e.FillAuthzContext(&authzContext)
|
||||||
|
return authz.ServiceRead(e.Name, &authzContext) == acl.Allow
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *ServiceConfigEntry) CanWrite(rule acl.Authorizer) bool {
|
func (e *ServiceConfigEntry) CanWrite(authz acl.Authorizer) bool {
|
||||||
return rule.ServiceWrite(e.Name, nil) == acl.Allow
|
var authzContext acl.AuthorizerContext
|
||||||
|
e.FillAuthzContext(&authzContext)
|
||||||
|
return authz.ServiceWrite(e.Name, &authzContext) == acl.Allow
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *ServiceConfigEntry) GetRaftIndex() *RaftIndex {
|
func (e *ServiceConfigEntry) GetRaftIndex() *RaftIndex {
|
||||||
|
@ -107,6 +115,14 @@ func (e *ServiceConfigEntry) GetRaftIndex() *RaftIndex {
|
||||||
return &e.RaftIndex
|
return &e.RaftIndex
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (e *ServiceConfigEntry) GetEnterpriseMeta() *EnterpriseMeta {
|
||||||
|
if e == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return &e.EnterpriseMeta
|
||||||
|
}
|
||||||
|
|
||||||
type ConnectConfiguration struct {
|
type ConnectConfiguration struct {
|
||||||
SidecarProxy bool
|
SidecarProxy bool
|
||||||
}
|
}
|
||||||
|
@ -119,6 +135,7 @@ type ProxyConfigEntry struct {
|
||||||
MeshGateway MeshGatewayConfig `json:",omitempty"`
|
MeshGateway MeshGatewayConfig `json:",omitempty"`
|
||||||
Expose ExposeConfig `json:",omitempty"`
|
Expose ExposeConfig `json:",omitempty"`
|
||||||
|
|
||||||
|
EnterpriseMeta `hcl:",squash" mapstructure:",squash"`
|
||||||
RaftIndex
|
RaftIndex
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -142,6 +159,8 @@ func (e *ProxyConfigEntry) Normalize() error {
|
||||||
e.Kind = ProxyDefaults
|
e.Kind = ProxyDefaults
|
||||||
e.Name = ProxyConfigGlobal
|
e.Name = ProxyConfigGlobal
|
||||||
|
|
||||||
|
e.EnterpriseMeta.Normalize()
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -154,15 +173,17 @@ func (e *ProxyConfigEntry) Validate() error {
|
||||||
return fmt.Errorf("invalid name (%q), only %q is supported", e.Name, ProxyConfigGlobal)
|
return fmt.Errorf("invalid name (%q), only %q is supported", e.Name, ProxyConfigGlobal)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return e.validateEnterpriseMeta()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *ProxyConfigEntry) CanRead(rule acl.Authorizer) bool {
|
func (e *ProxyConfigEntry) CanRead(authz acl.Authorizer) bool {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *ProxyConfigEntry) CanWrite(rule acl.Authorizer) bool {
|
func (e *ProxyConfigEntry) CanWrite(authz acl.Authorizer) bool {
|
||||||
return rule.OperatorWrite(nil) == acl.Allow
|
var authzContext acl.AuthorizerContext
|
||||||
|
e.FillAuthzContext(&authzContext)
|
||||||
|
return authz.OperatorWrite(&authzContext) == acl.Allow
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *ProxyConfigEntry) GetRaftIndex() *RaftIndex {
|
func (e *ProxyConfigEntry) GetRaftIndex() *RaftIndex {
|
||||||
|
@ -173,6 +194,14 @@ func (e *ProxyConfigEntry) GetRaftIndex() *RaftIndex {
|
||||||
return &e.RaftIndex
|
return &e.RaftIndex
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (e *ProxyConfigEntry) GetEnterpriseMeta() *EnterpriseMeta {
|
||||||
|
if e == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return &e.EnterpriseMeta
|
||||||
|
}
|
||||||
|
|
||||||
func (e *ProxyConfigEntry) MarshalBinary() (data []byte, err error) {
|
func (e *ProxyConfigEntry) MarshalBinary() (data []byte, err error) {
|
||||||
// We mainly want to implement the BinaryMarshaller interface so that
|
// We mainly want to implement the BinaryMarshaller interface so that
|
||||||
// we can fixup some msgpack types to coerce them into JSON compatible
|
// we can fixup some msgpack types to coerce them into JSON compatible
|
||||||
|
@ -459,6 +488,7 @@ type ConfigEntryQuery struct {
|
||||||
Name string
|
Name string
|
||||||
Datacenter string
|
Datacenter string
|
||||||
|
|
||||||
|
EnterpriseMeta `hcl:",squash" mapstructure:",squash"`
|
||||||
QueryOptions
|
QueryOptions
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -496,7 +526,12 @@ func (r *ConfigEntryQuery) CacheInfo() cache.RequestInfo {
|
||||||
type ServiceConfigRequest struct {
|
type ServiceConfigRequest struct {
|
||||||
Name string
|
Name string
|
||||||
Datacenter string
|
Datacenter string
|
||||||
Upstreams []string
|
// DEPRECATED
|
||||||
|
// Upstreams is a list of upstream service names to use for resolving the service config
|
||||||
|
// UpstreamIDs should be used instead which can encode more than just the name to
|
||||||
|
// uniquely identify a service.
|
||||||
|
Upstreams []string
|
||||||
|
UpstreamIDs []ServiceID
|
||||||
|
|
||||||
EnterpriseMeta `hcl:",squash" mapstructure:",squash"`
|
EnterpriseMeta `hcl:",squash" mapstructure:",squash"`
|
||||||
QueryOptions
|
QueryOptions
|
||||||
|
@ -538,11 +573,29 @@ func (r *ServiceConfigRequest) CacheInfo() cache.RequestInfo {
|
||||||
return info
|
return info
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type UpstreamConfig struct {
|
||||||
|
Upstream ServiceID
|
||||||
|
Config map[string]interface{}
|
||||||
|
}
|
||||||
|
|
||||||
|
type UpstreamConfigs []UpstreamConfig
|
||||||
|
|
||||||
|
func (configs UpstreamConfigs) GetUpstreamConfig(sid ServiceID) (config map[string]interface{}, found bool) {
|
||||||
|
for _, usconf := range configs {
|
||||||
|
if usconf.Upstream.Matches(&sid) {
|
||||||
|
return usconf.Config, true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, false
|
||||||
|
}
|
||||||
|
|
||||||
type ServiceConfigResponse struct {
|
type ServiceConfigResponse struct {
|
||||||
ProxyConfig map[string]interface{}
|
ProxyConfig map[string]interface{}
|
||||||
UpstreamConfigs map[string]map[string]interface{}
|
UpstreamConfigs map[string]map[string]interface{}
|
||||||
MeshGateway MeshGatewayConfig `json:",omitempty"`
|
UpstreamIDConfigs UpstreamConfigs
|
||||||
Expose ExposeConfig `json:",omitempty"`
|
MeshGateway MeshGatewayConfig `json:",omitempty"`
|
||||||
|
Expose ExposeConfig `json:",omitempty"`
|
||||||
QueryMeta
|
QueryMeta
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -597,6 +650,13 @@ func (r *ServiceConfigResponse) UnmarshalBinary(data []byte) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for k := range r.UpstreamIDConfigs {
|
||||||
|
r.UpstreamIDConfigs[k].Config, err = lib.MapWalk(r.UpstreamIDConfigs[k].Config)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -38,6 +38,7 @@ type ServiceRouterConfigEntry struct {
|
||||||
// the default service.
|
// the default service.
|
||||||
Routes []ServiceRoute
|
Routes []ServiceRoute
|
||||||
|
|
||||||
|
EnterpriseMeta `hcl:",squash" mapstructure:",squash"`
|
||||||
RaftIndex
|
RaftIndex
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -60,6 +61,8 @@ func (e *ServiceRouterConfigEntry) Normalize() error {
|
||||||
|
|
||||||
e.Kind = ServiceRouter
|
e.Kind = ServiceRouter
|
||||||
|
|
||||||
|
e.EnterpriseMeta.Normalize()
|
||||||
|
|
||||||
for _, route := range e.Routes {
|
for _, route := range e.Routes {
|
||||||
if route.Match == nil || route.Match.HTTP == nil {
|
if route.Match == nil || route.Match.HTTP == nil {
|
||||||
continue
|
continue
|
||||||
|
@ -73,6 +76,9 @@ func (e *ServiceRouterConfigEntry) Normalize() error {
|
||||||
for j := 0; j < len(httpMatch.Methods); j++ {
|
for j := 0; j < len(httpMatch.Methods); j++ {
|
||||||
httpMatch.Methods[j] = strings.ToUpper(httpMatch.Methods[j])
|
httpMatch.Methods[j] = strings.ToUpper(httpMatch.Methods[j])
|
||||||
}
|
}
|
||||||
|
if route.Destination != nil && route.Destination.Namespace == "" {
|
||||||
|
route.Destination.Namespace = e.EnterpriseMeta.NamespaceOrDefault()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
@ -194,15 +200,15 @@ func (e *ServiceRouterConfigEntry) GetRaftIndex() *RaftIndex {
|
||||||
return &e.RaftIndex
|
return &e.RaftIndex
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *ServiceRouterConfigEntry) ListRelatedServices() []string {
|
func (e *ServiceRouterConfigEntry) ListRelatedServices() []ServiceID {
|
||||||
found := make(map[string]struct{})
|
found := make(map[ServiceID]struct{})
|
||||||
|
|
||||||
// We always inject a default catch-all route to the same service as the router.
|
// We always inject a default catch-all route to the same service as the router.
|
||||||
found[e.Name] = struct{}{}
|
found[NewServiceID(e.Name, &e.EnterpriseMeta)] = struct{}{}
|
||||||
|
|
||||||
for _, route := range e.Routes {
|
for _, route := range e.Routes {
|
||||||
if route.Destination != nil && route.Destination.Service != "" {
|
if route.Destination != nil && route.Destination.Service != "" {
|
||||||
found[route.Destination.Service] = struct{}{}
|
found[NewServiceID(route.Destination.Service, route.Destination.GetEnterpriseMeta(&e.EnterpriseMeta))] = struct{}{}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -210,14 +216,25 @@ func (e *ServiceRouterConfigEntry) ListRelatedServices() []string {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
out := make([]string, 0, len(found))
|
out := make([]ServiceID, 0, len(found))
|
||||||
for svc, _ := range found {
|
for svc, _ := range found {
|
||||||
out = append(out, svc)
|
out = append(out, svc)
|
||||||
}
|
}
|
||||||
sort.Strings(out)
|
sort.Slice(out, func(i, j int) bool {
|
||||||
|
return out[i].EnterpriseMeta.LessThan(&out[j].EnterpriseMeta) ||
|
||||||
|
out[i].ID < out[j].ID
|
||||||
|
})
|
||||||
return out
|
return out
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (e *ServiceRouterConfigEntry) GetEnterpriseMeta() *EnterpriseMeta {
|
||||||
|
if e == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return &e.EnterpriseMeta
|
||||||
|
}
|
||||||
|
|
||||||
// ServiceRoute is a single routing rule that routes traffic to the destination
|
// ServiceRoute is a single routing rule that routes traffic to the destination
|
||||||
// when the match criteria applies.
|
// when the match criteria applies.
|
||||||
type ServiceRoute struct {
|
type ServiceRoute struct {
|
||||||
|
@ -386,6 +403,7 @@ type ServiceSplitterConfigEntry struct {
|
||||||
// to the FIRST split.
|
// to the FIRST split.
|
||||||
Splits []ServiceSplit
|
Splits []ServiceSplit
|
||||||
|
|
||||||
|
EnterpriseMeta `hcl:",squash" mapstructure:",squash"`
|
||||||
RaftIndex
|
RaftIndex
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -411,8 +429,13 @@ func (e *ServiceSplitterConfigEntry) Normalize() error {
|
||||||
// This slightly massages inputs to enforce that the smallest representable
|
// This slightly massages inputs to enforce that the smallest representable
|
||||||
// weight is 1/10000 or .01%
|
// weight is 1/10000 or .01%
|
||||||
|
|
||||||
|
e.EnterpriseMeta.Normalize()
|
||||||
|
|
||||||
if len(e.Splits) > 0 {
|
if len(e.Splits) > 0 {
|
||||||
for i, split := range e.Splits {
|
for i, split := range e.Splits {
|
||||||
|
if split.Namespace == "" {
|
||||||
|
split.Namespace = e.EnterpriseMeta.NamespaceOrDefault()
|
||||||
|
}
|
||||||
e.Splits[i].Weight = NormalizeServiceSplitWeight(split.Weight)
|
e.Splits[i].Weight = NormalizeServiceSplitWeight(split.Weight)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -493,12 +516,20 @@ func (e *ServiceSplitterConfigEntry) GetRaftIndex() *RaftIndex {
|
||||||
return &e.RaftIndex
|
return &e.RaftIndex
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *ServiceSplitterConfigEntry) ListRelatedServices() []string {
|
func (e *ServiceSplitterConfigEntry) GetEnterpriseMeta() *EnterpriseMeta {
|
||||||
found := make(map[string]struct{})
|
if e == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return &e.EnterpriseMeta
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *ServiceSplitterConfigEntry) ListRelatedServices() []ServiceID {
|
||||||
|
found := make(map[ServiceID]struct{})
|
||||||
|
|
||||||
for _, split := range e.Splits {
|
for _, split := range e.Splits {
|
||||||
if split.Service != "" {
|
if split.Service != "" {
|
||||||
found[split.Service] = struct{}{}
|
found[NewServiceID(split.Service, split.GetEnterpriseMeta(&e.EnterpriseMeta))] = struct{}{}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -506,11 +537,14 @@ func (e *ServiceSplitterConfigEntry) ListRelatedServices() []string {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
out := make([]string, 0, len(found))
|
out := make([]ServiceID, 0, len(found))
|
||||||
for svc, _ := range found {
|
for svc, _ := range found {
|
||||||
out = append(out, svc)
|
out = append(out, svc)
|
||||||
}
|
}
|
||||||
sort.Strings(out)
|
sort.Slice(out, func(i, j int) bool {
|
||||||
|
return out[i].EnterpriseMeta.LessThan(&out[j].EnterpriseMeta) ||
|
||||||
|
out[i].ID < out[j].ID
|
||||||
|
})
|
||||||
return out
|
return out
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -598,6 +632,7 @@ type ServiceResolverConfigEntry struct {
|
||||||
// to this service.
|
// to this service.
|
||||||
ConnectTimeout time.Duration `json:",omitempty"`
|
ConnectTimeout time.Duration `json:",omitempty"`
|
||||||
|
|
||||||
|
EnterpriseMeta `hcl:",squash" mapstructure:",squash"`
|
||||||
RaftIndex
|
RaftIndex
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -675,6 +710,8 @@ func (e *ServiceResolverConfigEntry) Normalize() error {
|
||||||
|
|
||||||
e.Kind = ServiceResolver
|
e.Kind = ServiceResolver
|
||||||
|
|
||||||
|
e.EnterpriseMeta.Normalize()
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -782,19 +819,27 @@ func (e *ServiceResolverConfigEntry) GetRaftIndex() *RaftIndex {
|
||||||
return &e.RaftIndex
|
return &e.RaftIndex
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *ServiceResolverConfigEntry) ListRelatedServices() []string {
|
func (e *ServiceResolverConfigEntry) GetEnterpriseMeta() *EnterpriseMeta {
|
||||||
found := make(map[string]struct{})
|
if e == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return &e.EnterpriseMeta
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *ServiceResolverConfigEntry) ListRelatedServices() []ServiceID {
|
||||||
|
found := make(map[ServiceID]struct{})
|
||||||
|
|
||||||
if e.Redirect != nil {
|
if e.Redirect != nil {
|
||||||
if e.Redirect.Service != "" {
|
if e.Redirect.Service != "" {
|
||||||
found[e.Redirect.Service] = struct{}{}
|
found[NewServiceID(e.Redirect.Service, e.Redirect.GetEnterpriseMeta(&e.EnterpriseMeta))] = struct{}{}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(e.Failover) > 0 {
|
if len(e.Failover) > 0 {
|
||||||
for _, failover := range e.Failover {
|
for _, failover := range e.Failover {
|
||||||
if failover.Service != "" {
|
if failover.Service != "" {
|
||||||
found[failover.Service] = struct{}{}
|
found[NewServiceID(failover.Service, failover.GetEnterpriseMeta(&e.EnterpriseMeta))] = struct{}{}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -803,11 +848,14 @@ func (e *ServiceResolverConfigEntry) ListRelatedServices() []string {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
out := make([]string, 0, len(found))
|
out := make([]ServiceID, 0, len(found))
|
||||||
for svc, _ := range found {
|
for svc, _ := range found {
|
||||||
out = append(out, svc)
|
out = append(out, svc)
|
||||||
}
|
}
|
||||||
sort.Strings(out)
|
sort.Slice(out, func(i, j int) bool {
|
||||||
|
return out[i].EnterpriseMeta.LessThan(&out[j].EnterpriseMeta) ||
|
||||||
|
out[i].ID < out[j].ID
|
||||||
|
})
|
||||||
return out
|
return out
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -889,28 +937,34 @@ type discoveryChainConfigEntry interface {
|
||||||
ConfigEntry
|
ConfigEntry
|
||||||
// ListRelatedServices returns a list of other names of services referenced
|
// ListRelatedServices returns a list of other names of services referenced
|
||||||
// in this config entry.
|
// in this config entry.
|
||||||
ListRelatedServices() []string
|
ListRelatedServices() []ServiceID
|
||||||
}
|
}
|
||||||
|
|
||||||
func canReadDiscoveryChain(entry discoveryChainConfigEntry, rule acl.Authorizer) bool {
|
func canReadDiscoveryChain(entry discoveryChainConfigEntry, authz acl.Authorizer) bool {
|
||||||
return rule.ServiceRead(entry.GetName(), nil) == acl.Allow
|
var authzContext acl.AuthorizerContext
|
||||||
|
entry.GetEnterpriseMeta().FillAuthzContext(&authzContext)
|
||||||
|
return authz.ServiceRead(entry.GetName(), &authzContext) == acl.Allow
|
||||||
}
|
}
|
||||||
|
|
||||||
func canWriteDiscoveryChain(entry discoveryChainConfigEntry, rule acl.Authorizer) bool {
|
func canWriteDiscoveryChain(entry discoveryChainConfigEntry, rule acl.Authorizer) bool {
|
||||||
|
var authzContext acl.AuthorizerContext
|
||||||
|
entry.GetEnterpriseMeta().FillAuthzContext(&authzContext)
|
||||||
|
|
||||||
name := entry.GetName()
|
name := entry.GetName()
|
||||||
|
|
||||||
if rule.ServiceWrite(name, nil) != acl.Allow {
|
if rule.ServiceWrite(name, &authzContext) != acl.Allow {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, svc := range entry.ListRelatedServices() {
|
for _, svc := range entry.ListRelatedServices() {
|
||||||
if svc == name {
|
if svc.ID == name {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
svc.FillAuthzContext(&authzContext)
|
||||||
// You only need read on related services to redirect traffic flow for
|
// You only need read on related services to redirect traffic flow for
|
||||||
// your own service.
|
// your own service.
|
||||||
if rule.ServiceRead(svc, nil) != acl.Allow {
|
if rule.ServiceRead(svc.ID, &authzContext) != acl.Allow {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -920,46 +974,46 @@ func canWriteDiscoveryChain(entry discoveryChainConfigEntry, rule acl.Authorizer
|
||||||
// DiscoveryChainConfigEntries wraps just the raw cross-referenced config
|
// DiscoveryChainConfigEntries wraps just the raw cross-referenced config
|
||||||
// entries. None of these are defaulted.
|
// entries. None of these are defaulted.
|
||||||
type DiscoveryChainConfigEntries struct {
|
type DiscoveryChainConfigEntries struct {
|
||||||
Routers map[string]*ServiceRouterConfigEntry
|
Routers map[ServiceID]*ServiceRouterConfigEntry
|
||||||
Splitters map[string]*ServiceSplitterConfigEntry
|
Splitters map[ServiceID]*ServiceSplitterConfigEntry
|
||||||
Resolvers map[string]*ServiceResolverConfigEntry
|
Resolvers map[ServiceID]*ServiceResolverConfigEntry
|
||||||
Services map[string]*ServiceConfigEntry
|
Services map[ServiceID]*ServiceConfigEntry
|
||||||
GlobalProxy *ProxyConfigEntry
|
GlobalProxy *ProxyConfigEntry
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewDiscoveryChainConfigEntries() *DiscoveryChainConfigEntries {
|
func NewDiscoveryChainConfigEntries() *DiscoveryChainConfigEntries {
|
||||||
return &DiscoveryChainConfigEntries{
|
return &DiscoveryChainConfigEntries{
|
||||||
Routers: make(map[string]*ServiceRouterConfigEntry),
|
Routers: make(map[ServiceID]*ServiceRouterConfigEntry),
|
||||||
Splitters: make(map[string]*ServiceSplitterConfigEntry),
|
Splitters: make(map[ServiceID]*ServiceSplitterConfigEntry),
|
||||||
Resolvers: make(map[string]*ServiceResolverConfigEntry),
|
Resolvers: make(map[ServiceID]*ServiceResolverConfigEntry),
|
||||||
Services: make(map[string]*ServiceConfigEntry),
|
Services: make(map[ServiceID]*ServiceConfigEntry),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *DiscoveryChainConfigEntries) GetRouter(name string) *ServiceRouterConfigEntry {
|
func (e *DiscoveryChainConfigEntries) GetRouter(sid ServiceID) *ServiceRouterConfigEntry {
|
||||||
if e.Routers != nil {
|
if e.Routers != nil {
|
||||||
return e.Routers[name]
|
return e.Routers[sid]
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *DiscoveryChainConfigEntries) GetSplitter(name string) *ServiceSplitterConfigEntry {
|
func (e *DiscoveryChainConfigEntries) GetSplitter(sid ServiceID) *ServiceSplitterConfigEntry {
|
||||||
if e.Splitters != nil {
|
if e.Splitters != nil {
|
||||||
return e.Splitters[name]
|
return e.Splitters[sid]
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *DiscoveryChainConfigEntries) GetResolver(name string) *ServiceResolverConfigEntry {
|
func (e *DiscoveryChainConfigEntries) GetResolver(sid ServiceID) *ServiceResolverConfigEntry {
|
||||||
if e.Resolvers != nil {
|
if e.Resolvers != nil {
|
||||||
return e.Resolvers[name]
|
return e.Resolvers[sid]
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *DiscoveryChainConfigEntries) GetService(name string) *ServiceConfigEntry {
|
func (e *DiscoveryChainConfigEntries) GetService(sid ServiceID) *ServiceConfigEntry {
|
||||||
if e.Services != nil {
|
if e.Services != nil {
|
||||||
return e.Services[name]
|
return e.Services[sid]
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -967,40 +1021,40 @@ func (e *DiscoveryChainConfigEntries) GetService(name string) *ServiceConfigEntr
|
||||||
// AddRouters adds router configs. Convenience function for testing.
|
// AddRouters adds router configs. Convenience function for testing.
|
||||||
func (e *DiscoveryChainConfigEntries) AddRouters(entries ...*ServiceRouterConfigEntry) {
|
func (e *DiscoveryChainConfigEntries) AddRouters(entries ...*ServiceRouterConfigEntry) {
|
||||||
if e.Routers == nil {
|
if e.Routers == nil {
|
||||||
e.Routers = make(map[string]*ServiceRouterConfigEntry)
|
e.Routers = make(map[ServiceID]*ServiceRouterConfigEntry)
|
||||||
}
|
}
|
||||||
for _, entry := range entries {
|
for _, entry := range entries {
|
||||||
e.Routers[entry.Name] = entry
|
e.Routers[NewServiceID(entry.Name, &entry.EnterpriseMeta)] = entry
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// AddSplitters adds splitter configs. Convenience function for testing.
|
// AddSplitters adds splitter configs. Convenience function for testing.
|
||||||
func (e *DiscoveryChainConfigEntries) AddSplitters(entries ...*ServiceSplitterConfigEntry) {
|
func (e *DiscoveryChainConfigEntries) AddSplitters(entries ...*ServiceSplitterConfigEntry) {
|
||||||
if e.Splitters == nil {
|
if e.Splitters == nil {
|
||||||
e.Splitters = make(map[string]*ServiceSplitterConfigEntry)
|
e.Splitters = make(map[ServiceID]*ServiceSplitterConfigEntry)
|
||||||
}
|
}
|
||||||
for _, entry := range entries {
|
for _, entry := range entries {
|
||||||
e.Splitters[entry.Name] = entry
|
e.Splitters[NewServiceID(entry.Name, entry.GetEnterpriseMeta())] = entry
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// AddResolvers adds resolver configs. Convenience function for testing.
|
// AddResolvers adds resolver configs. Convenience function for testing.
|
||||||
func (e *DiscoveryChainConfigEntries) AddResolvers(entries ...*ServiceResolverConfigEntry) {
|
func (e *DiscoveryChainConfigEntries) AddResolvers(entries ...*ServiceResolverConfigEntry) {
|
||||||
if e.Resolvers == nil {
|
if e.Resolvers == nil {
|
||||||
e.Resolvers = make(map[string]*ServiceResolverConfigEntry)
|
e.Resolvers = make(map[ServiceID]*ServiceResolverConfigEntry)
|
||||||
}
|
}
|
||||||
for _, entry := range entries {
|
for _, entry := range entries {
|
||||||
e.Resolvers[entry.Name] = entry
|
e.Resolvers[NewServiceID(entry.Name, entry.GetEnterpriseMeta())] = entry
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// AddServices adds service configs. Convenience function for testing.
|
// AddServices adds service configs. Convenience function for testing.
|
||||||
func (e *DiscoveryChainConfigEntries) AddServices(entries ...*ServiceConfigEntry) {
|
func (e *DiscoveryChainConfigEntries) AddServices(entries ...*ServiceConfigEntry) {
|
||||||
if e.Services == nil {
|
if e.Services == nil {
|
||||||
e.Services = make(map[string]*ServiceConfigEntry)
|
e.Services = make(map[ServiceID]*ServiceConfigEntry)
|
||||||
}
|
}
|
||||||
for _, entry := range entries {
|
for _, entry := range entries {
|
||||||
e.Services[entry.Name] = entry
|
e.Services[NewServiceID(entry.Name, entry.GetEnterpriseMeta())] = entry
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,39 @@
|
||||||
|
// +build !consulent
|
||||||
|
|
||||||
|
package structs
|
||||||
|
|
||||||
|
// GetEnterpriseMeta is used to synthesize the EnterpriseMeta struct from
|
||||||
|
// fields in the ServiceRouteDestination
|
||||||
|
func (dest *ServiceRouteDestination) GetEnterpriseMeta(_ *EnterpriseMeta) *EnterpriseMeta {
|
||||||
|
return DefaultEnterpriseMeta()
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetEnterpriseMeta is used to synthesize the EnterpriseMeta struct from
|
||||||
|
// fields in the ServiceSplit
|
||||||
|
func (split *ServiceSplit) GetEnterpriseMeta(_ *EnterpriseMeta) *EnterpriseMeta {
|
||||||
|
return DefaultEnterpriseMeta()
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetEnterpriseMeta is used to synthesize the EnterpriseMeta struct from
|
||||||
|
// fields in the ServiceResolverRedirect
|
||||||
|
func (redir *ServiceResolverRedirect) GetEnterpriseMeta(_ *EnterpriseMeta) *EnterpriseMeta {
|
||||||
|
return DefaultEnterpriseMeta()
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetEnterpriseMeta is used to synthesize the EnterpriseMeta struct from
|
||||||
|
// fields in the ServiceResolverFailover
|
||||||
|
func (failover *ServiceResolverFailover) GetEnterpriseMeta(_ *EnterpriseMeta) *EnterpriseMeta {
|
||||||
|
return DefaultEnterpriseMeta()
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetEnterpriseMeta is used to synthesize the EnterpriseMeta struct from
|
||||||
|
// fields in the DiscoveryChainRequest
|
||||||
|
func (req *DiscoveryChainRequest) GetEnterpriseMeta() *EnterpriseMeta {
|
||||||
|
return DefaultEnterpriseMeta()
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithEnterpriseMeta will populate the corresponding fields in the
|
||||||
|
// DiscoveryChainRequest from the EnterpriseMeta struct
|
||||||
|
func (req *DiscoveryChainRequest) WithEnterpriseMeta(_ *EnterpriseMeta) {
|
||||||
|
// do nothing
|
||||||
|
}
|
|
@ -67,7 +67,7 @@ func TestConfigEntries_ListRelatedServices_AndACLs(t *testing.T) {
|
||||||
for _, tc := range []struct {
|
for _, tc := range []struct {
|
||||||
name string
|
name string
|
||||||
entry discoveryChainConfigEntry
|
entry discoveryChainConfigEntry
|
||||||
expectServices []string
|
expectServices []ServiceID
|
||||||
expectACLs []testACL
|
expectACLs []testACL
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
|
@ -92,7 +92,7 @@ func TestConfigEntries_ListRelatedServices_AndACLs(t *testing.T) {
|
||||||
Service: "other",
|
Service: "other",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
expectServices: []string{"other"},
|
expectServices: []ServiceID{NewServiceID("other", nil)},
|
||||||
expectACLs: []testACL{
|
expectACLs: []testACL{
|
||||||
defaultDenyCase,
|
defaultDenyCase,
|
||||||
readTestCase,
|
readTestCase,
|
||||||
|
@ -123,7 +123,7 @@ func TestConfigEntries_ListRelatedServices_AndACLs(t *testing.T) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
expectServices: []string{"other1", "other2"},
|
expectServices: []ServiceID{NewServiceID("other1", nil), NewServiceID("other2", nil)},
|
||||||
expectACLs: []testACL{
|
expectACLs: []testACL{
|
||||||
defaultDenyCase,
|
defaultDenyCase,
|
||||||
readTestCase,
|
readTestCase,
|
||||||
|
@ -163,7 +163,7 @@ func TestConfigEntries_ListRelatedServices_AndACLs(t *testing.T) {
|
||||||
{Weight: 50, Service: "c"},
|
{Weight: 50, Service: "c"},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
expectServices: []string{"a", "b", "c"},
|
expectServices: []ServiceID{NewServiceID("a", nil), NewServiceID("b", nil), NewServiceID("c", nil)},
|
||||||
expectACLs: []testACL{
|
expectACLs: []testACL{
|
||||||
defaultDenyCase,
|
defaultDenyCase,
|
||||||
readTestCase,
|
readTestCase,
|
||||||
|
@ -182,7 +182,7 @@ func TestConfigEntries_ListRelatedServices_AndACLs(t *testing.T) {
|
||||||
Kind: ServiceRouter,
|
Kind: ServiceRouter,
|
||||||
Name: "test",
|
Name: "test",
|
||||||
},
|
},
|
||||||
expectServices: []string{"test"},
|
expectServices: []ServiceID{NewServiceID("test", nil)},
|
||||||
expectACLs: []testACL{
|
expectACLs: []testACL{
|
||||||
defaultDenyCase,
|
defaultDenyCase,
|
||||||
readTestCase,
|
readTestCase,
|
||||||
|
@ -213,7 +213,7 @@ func TestConfigEntries_ListRelatedServices_AndACLs(t *testing.T) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
expectServices: []string{"bar", "foo", "test"},
|
expectServices: []ServiceID{NewServiceID("bar", nil), NewServiceID("foo", nil), NewServiceID("test", nil)},
|
||||||
expectACLs: []testACL{
|
expectACLs: []testACL{
|
||||||
defaultDenyCase,
|
defaultDenyCase,
|
||||||
readTestCase,
|
readTestCase,
|
||||||
|
|
|
@ -0,0 +1,7 @@
|
||||||
|
// +build !consulent
|
||||||
|
|
||||||
|
package structs
|
||||||
|
|
||||||
|
func (e *ProxyConfigEntry) validateEnterpriseMeta() error {
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -0,0 +1,13 @@
|
||||||
|
// +build !consulent
|
||||||
|
|
||||||
|
package structs
|
||||||
|
|
||||||
|
func (us *Upstream) GetEnterpriseMeta() *EnterpriseMeta {
|
||||||
|
return DefaultEnterpriseMeta()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (us *Upstream) DestinationID() ServiceID {
|
||||||
|
return ServiceID{
|
||||||
|
ID: us.DestinationName,
|
||||||
|
}
|
||||||
|
}
|
|
@ -238,3 +238,7 @@ func (t *DiscoveryTarget) setID() {
|
||||||
func (t *DiscoveryTarget) String() string {
|
func (t *DiscoveryTarget) String() string {
|
||||||
return t.ID
|
return t.ID
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (t *DiscoveryTarget) ServiceID() ServiceID {
|
||||||
|
return NewServiceID(t.Service, t.GetEnterpriseMetadata())
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,7 @@
|
||||||
|
// +build !consulent
|
||||||
|
|
||||||
|
package structs
|
||||||
|
|
||||||
|
func (t *DiscoveryTarget) GetEnterpriseMetadata() *EnterpriseMeta {
|
||||||
|
return DefaultEnterpriseMeta()
|
||||||
|
}
|
|
@ -803,13 +803,16 @@ func (s *ServiceNode) ToNodeService() *NodeService {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *ServiceNode) CompoundServiceID() ServiceID {
|
func (sn *ServiceNode) compoundID(preferName bool) ServiceID {
|
||||||
id := s.ServiceID
|
var id string
|
||||||
if id == "" {
|
if sn.ServiceID == "" || (preferName && sn.ServiceName != "") {
|
||||||
id = s.ServiceName
|
id = sn.ServiceName
|
||||||
|
} else {
|
||||||
|
id = sn.ServiceID
|
||||||
}
|
}
|
||||||
|
|
||||||
entMeta := s.EnterpriseMeta
|
// copy the ent meta and normalize it
|
||||||
|
entMeta := sn.EnterpriseMeta
|
||||||
entMeta.Normalize()
|
entMeta.Normalize()
|
||||||
|
|
||||||
return ServiceID{
|
return ServiceID{
|
||||||
|
@ -818,6 +821,14 @@ func (s *ServiceNode) CompoundServiceID() ServiceID {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (sn *ServiceNode) CompoundServiceID() ServiceID {
|
||||||
|
return sn.compoundID(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (sn *ServiceNode) CompoundServiceName() ServiceID {
|
||||||
|
return sn.compoundID(true)
|
||||||
|
}
|
||||||
|
|
||||||
// Weights represent the weight used by DNS for a given status
|
// Weights represent the weight used by DNS for a given status
|
||||||
type Weights struct {
|
type Weights struct {
|
||||||
Passing int
|
Passing int
|
||||||
|
@ -938,11 +949,12 @@ func (ns *NodeService) BestAddress(wan bool) (string, int) {
|
||||||
return addr, port
|
return addr, port
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ns *NodeService) CompoundServiceID() ServiceID {
|
func (ns *NodeService) compoundID(preferName bool) ServiceID {
|
||||||
id := ns.ID
|
var id string
|
||||||
|
if ns.ID == "" || (preferName && ns.Service != "") {
|
||||||
if id == "" {
|
|
||||||
id = ns.Service
|
id = ns.Service
|
||||||
|
} else {
|
||||||
|
id = ns.ID
|
||||||
}
|
}
|
||||||
|
|
||||||
// copy the ent meta and normalize it
|
// copy the ent meta and normalize it
|
||||||
|
@ -955,6 +967,14 @@ func (ns *NodeService) CompoundServiceID() ServiceID {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (ns *NodeService) CompoundServiceID() ServiceID {
|
||||||
|
return ns.compoundID(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ns *NodeService) CompoundServiceName() ServiceID {
|
||||||
|
return ns.compoundID(true)
|
||||||
|
}
|
||||||
|
|
||||||
// ServiceConnect are the shared Connect settings between all service
|
// ServiceConnect are the shared Connect settings between all service
|
||||||
// definitions from the agent to the state store.
|
// definitions from the agent to the state store.
|
||||||
type ServiceConnect struct {
|
type ServiceConnect struct {
|
||||||
|
@ -1645,6 +1665,22 @@ type IndexedServices struct {
|
||||||
QueryMeta
|
QueryMeta
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type ServiceInfo struct {
|
||||||
|
Name string
|
||||||
|
EnterpriseMeta
|
||||||
|
}
|
||||||
|
|
||||||
|
func (si *ServiceInfo) ToServiceID() ServiceID {
|
||||||
|
return ServiceID{ID: si.Name, EnterpriseMeta: si.EnterpriseMeta}
|
||||||
|
}
|
||||||
|
|
||||||
|
type ServiceList []ServiceInfo
|
||||||
|
|
||||||
|
type IndexedServiceList struct {
|
||||||
|
Services ServiceList
|
||||||
|
QueryMeta
|
||||||
|
}
|
||||||
|
|
||||||
type IndexedServiceNodes struct {
|
type IndexedServiceNodes struct {
|
||||||
ServiceNodes ServiceNodes
|
ServiceNodes ServiceNodes
|
||||||
QueryMeta
|
QueryMeta
|
||||||
|
|
|
@ -26,6 +26,10 @@ func (m *EnterpriseMeta) Merge(_ *EnterpriseMeta) {
|
||||||
// do nothing
|
// do nothing
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (m *EnterpriseMeta) MergeNoWildcard(_ *EnterpriseMeta) {
|
||||||
|
// do nothing
|
||||||
|
}
|
||||||
|
|
||||||
func (m *EnterpriseMeta) Matches(_ *EnterpriseMeta) bool {
|
func (m *EnterpriseMeta) Matches(_ *EnterpriseMeta) bool {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
@ -38,6 +42,10 @@ func (m *EnterpriseMeta) LessThan(_ *EnterpriseMeta) bool {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (m *EnterpriseMeta) NamespaceOrDefault() string {
|
||||||
|
return "default"
|
||||||
|
}
|
||||||
|
|
||||||
// ReplicationEnterpriseMeta stub
|
// ReplicationEnterpriseMeta stub
|
||||||
func ReplicationEnterpriseMeta() *EnterpriseMeta {
|
func ReplicationEnterpriseMeta() *EnterpriseMeta {
|
||||||
return &emptyEnterpriseMeta
|
return &emptyEnterpriseMeta
|
||||||
|
@ -81,10 +89,19 @@ func ServiceIDString(id string, _ *EnterpriseMeta) string {
|
||||||
return id
|
return id
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func ParseServiceIDString(input string) (string, *EnterpriseMeta) {
|
||||||
|
return input, DefaultEnterpriseMeta()
|
||||||
|
}
|
||||||
|
|
||||||
func (sid *ServiceID) String() string {
|
func (sid *ServiceID) String() string {
|
||||||
return sid.ID
|
return sid.ID
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func ServiceIDFromString(input string) ServiceID {
|
||||||
|
id, _ := ParseServiceIDString(input)
|
||||||
|
return ServiceID{ID: id}
|
||||||
|
}
|
||||||
|
|
||||||
func (cid *CheckID) String() string {
|
func (cid *CheckID) String() string {
|
||||||
return string(cid.ID)
|
return string(cid.ID)
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,9 +7,9 @@ import (
|
||||||
// TestIntention returns a valid, uninserted (no ID set) intention.
|
// TestIntention returns a valid, uninserted (no ID set) intention.
|
||||||
func TestIntention(t testing.T) *Intention {
|
func TestIntention(t testing.T) *Intention {
|
||||||
return &Intention{
|
return &Intention{
|
||||||
SourceNS: "eng",
|
SourceNS: IntentionDefaultNamespace,
|
||||||
SourceName: "api",
|
SourceName: "api",
|
||||||
DestinationNS: "eng",
|
DestinationNS: IntentionDefaultNamespace,
|
||||||
DestinationName: "db",
|
DestinationName: "db",
|
||||||
Action: IntentionActionAllow,
|
Action: IntentionActionAllow,
|
||||||
SourceType: IntentionSourceConsul,
|
SourceType: IntentionSourceConsul,
|
||||||
|
|
|
@ -79,9 +79,7 @@ func (s *Server) clustersFromSnapshotConnectProxy(cfgSnap *proxycfg.ConfigSnapsh
|
||||||
|
|
||||||
// Add service health checks to the list of paths to create clusters for if needed
|
// Add service health checks to the list of paths to create clusters for if needed
|
||||||
if cfgSnap.Proxy.Expose.Checks {
|
if cfgSnap.Proxy.Expose.Checks {
|
||||||
// TODO (namespaces) update with real entmeta
|
psid := structs.NewServiceID(cfgSnap.Proxy.DestinationServiceID, &cfgSnap.ProxyID.EnterpriseMeta)
|
||||||
var psid structs.ServiceID
|
|
||||||
psid.Init(cfgSnap.Proxy.DestinationServiceID, structs.DefaultEnterpriseMeta())
|
|
||||||
for _, check := range s.CheckFetcher.ServiceHTTPBasedChecks(psid) {
|
for _, check := range s.CheckFetcher.ServiceHTTPBasedChecks(psid) {
|
||||||
p, err := parseCheckPath(check)
|
p, err := parseCheckPath(check)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -131,7 +129,7 @@ func (s *Server) clustersFromSnapshotMeshGateway(cfgSnap *proxycfg.ConfigSnapsho
|
||||||
|
|
||||||
// generate the per-service clusters
|
// generate the per-service clusters
|
||||||
for svc, _ := range cfgSnap.MeshGateway.ServiceGroups {
|
for svc, _ := range cfgSnap.MeshGateway.ServiceGroups {
|
||||||
clusterName := connect.ServiceSNI(svc, "", "default", cfgSnap.Datacenter, cfgSnap.Roots.TrustDomain)
|
clusterName := connect.ServiceSNI(svc.ID, "", svc.NamespaceOrDefault(), cfgSnap.Datacenter, cfgSnap.Roots.TrustDomain)
|
||||||
|
|
||||||
cluster, err := s.makeMeshGatewayCluster(clusterName, cfgSnap)
|
cluster, err := s.makeMeshGatewayCluster(clusterName, cfgSnap)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -143,7 +141,7 @@ func (s *Server) clustersFromSnapshotMeshGateway(cfgSnap *proxycfg.ConfigSnapsho
|
||||||
// generate the service subset clusters
|
// generate the service subset clusters
|
||||||
for svc, resolver := range cfgSnap.MeshGateway.ServiceResolvers {
|
for svc, resolver := range cfgSnap.MeshGateway.ServiceResolvers {
|
||||||
for subsetName, _ := range resolver.Subsets {
|
for subsetName, _ := range resolver.Subsets {
|
||||||
clusterName := connect.ServiceSNI(svc, subsetName, "default", cfgSnap.Datacenter, cfgSnap.Roots.TrustDomain)
|
clusterName := connect.ServiceSNI(svc.ID, subsetName, svc.NamespaceOrDefault(), cfgSnap.Datacenter, cfgSnap.Roots.TrustDomain)
|
||||||
|
|
||||||
cluster, err := s.makeMeshGatewayCluster(clusterName, cfgSnap)
|
cluster, err := s.makeMeshGatewayCluster(clusterName, cfgSnap)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -259,8 +259,8 @@ func TestClustersFromSnapshot(t *testing.T) {
|
||||||
name: "mesh-gateway-service-subsets",
|
name: "mesh-gateway-service-subsets",
|
||||||
create: proxycfg.TestConfigSnapshotMeshGateway,
|
create: proxycfg.TestConfigSnapshotMeshGateway,
|
||||||
setup: func(snap *proxycfg.ConfigSnapshot) {
|
setup: func(snap *proxycfg.ConfigSnapshot) {
|
||||||
snap.MeshGateway.ServiceResolvers = map[string]*structs.ServiceResolverConfigEntry{
|
snap.MeshGateway.ServiceResolvers = map[structs.ServiceID]*structs.ServiceResolverConfigEntry{
|
||||||
"bar": &structs.ServiceResolverConfigEntry{
|
structs.NewServiceID("bar", nil): &structs.ServiceResolverConfigEntry{
|
||||||
Kind: structs.ServiceResolver,
|
Kind: structs.ServiceResolver,
|
||||||
Name: "bar",
|
Name: "bar",
|
||||||
Subsets: map[string]structs.ServiceResolverSubset{
|
Subsets: map[string]structs.ServiceResolverSubset{
|
||||||
|
|
|
@ -37,7 +37,7 @@ func (s *Server) endpointsFromSnapshot(cfgSnap *proxycfg.ConfigSnapshot, token s
|
||||||
// (upstream instances) in the snapshot.
|
// (upstream instances) in the snapshot.
|
||||||
func (s *Server) endpointsFromSnapshotConnectProxy(cfgSnap *proxycfg.ConfigSnapshot, token string) ([]proto.Message, error) {
|
func (s *Server) endpointsFromSnapshotConnectProxy(cfgSnap *proxycfg.ConfigSnapshot, token string) ([]proto.Message, error) {
|
||||||
resources := make([]proto.Message, 0,
|
resources := make([]proto.Message, 0,
|
||||||
len(cfgSnap.ConnectProxy.UpstreamEndpoints)+len(cfgSnap.ConnectProxy.WatchedUpstreamEndpoints))
|
len(cfgSnap.ConnectProxy.PreparedQueryEndpoints)+len(cfgSnap.ConnectProxy.WatchedUpstreamEndpoints))
|
||||||
|
|
||||||
for _, u := range cfgSnap.Proxy.Upstreams {
|
for _, u := range cfgSnap.Proxy.Upstreams {
|
||||||
id := u.Identifier()
|
id := u.Identifier()
|
||||||
|
@ -56,7 +56,7 @@ func (s *Server) endpointsFromSnapshotConnectProxy(cfgSnap *proxycfg.ConfigSnaps
|
||||||
}
|
}
|
||||||
clusterName := connect.UpstreamSNI(&u, "", dc, cfgSnap.Roots.TrustDomain)
|
clusterName := connect.UpstreamSNI(&u, "", dc, cfgSnap.Roots.TrustDomain)
|
||||||
|
|
||||||
endpoints, ok := cfgSnap.ConnectProxy.UpstreamEndpoints[id]
|
endpoints, ok := cfgSnap.ConnectProxy.PreparedQueryEndpoints[id]
|
||||||
if ok {
|
if ok {
|
||||||
la := makeLoadAssignment(
|
la := makeLoadAssignment(
|
||||||
clusterName,
|
clusterName,
|
||||||
|
@ -167,7 +167,7 @@ func (s *Server) endpointsFromSnapshotMeshGateway(cfgSnap *proxycfg.ConfigSnapsh
|
||||||
|
|
||||||
// generate the endpoints for the local service groups
|
// generate the endpoints for the local service groups
|
||||||
for svc, endpoints := range cfgSnap.MeshGateway.ServiceGroups {
|
for svc, endpoints := range cfgSnap.MeshGateway.ServiceGroups {
|
||||||
clusterName := connect.ServiceSNI(svc, "", "default", cfgSnap.Datacenter, cfgSnap.Roots.TrustDomain)
|
clusterName := connect.ServiceSNI(svc.ID, "", svc.NamespaceOrDefault(), cfgSnap.Datacenter, cfgSnap.Roots.TrustDomain)
|
||||||
la := makeLoadAssignment(
|
la := makeLoadAssignment(
|
||||||
clusterName,
|
clusterName,
|
||||||
[]loadAssignmentEndpointGroup{
|
[]loadAssignmentEndpointGroup{
|
||||||
|
@ -181,7 +181,7 @@ func (s *Server) endpointsFromSnapshotMeshGateway(cfgSnap *proxycfg.ConfigSnapsh
|
||||||
// generate the endpoints for the service subsets
|
// generate the endpoints for the service subsets
|
||||||
for svc, resolver := range cfgSnap.MeshGateway.ServiceResolvers {
|
for svc, resolver := range cfgSnap.MeshGateway.ServiceResolvers {
|
||||||
for subsetName, subset := range resolver.Subsets {
|
for subsetName, subset := range resolver.Subsets {
|
||||||
clusterName := connect.ServiceSNI(svc, subsetName, "default", cfgSnap.Datacenter, cfgSnap.Roots.TrustDomain)
|
clusterName := connect.ServiceSNI(svc.ID, subsetName, svc.NamespaceOrDefault(), cfgSnap.Datacenter, cfgSnap.Roots.TrustDomain)
|
||||||
|
|
||||||
endpoints := cfgSnap.MeshGateway.ServiceGroups[svc]
|
endpoints := cfgSnap.MeshGateway.ServiceGroups[svc]
|
||||||
|
|
||||||
|
|
|
@ -311,8 +311,8 @@ func Test_endpointsFromSnapshot(t *testing.T) {
|
||||||
name: "mesh-gateway-service-subsets",
|
name: "mesh-gateway-service-subsets",
|
||||||
create: proxycfg.TestConfigSnapshotMeshGateway,
|
create: proxycfg.TestConfigSnapshotMeshGateway,
|
||||||
setup: func(snap *proxycfg.ConfigSnapshot) {
|
setup: func(snap *proxycfg.ConfigSnapshot) {
|
||||||
snap.MeshGateway.ServiceResolvers = map[string]*structs.ServiceResolverConfigEntry{
|
snap.MeshGateway.ServiceResolvers = map[structs.ServiceID]*structs.ServiceResolverConfigEntry{
|
||||||
"bar": &structs.ServiceResolverConfigEntry{
|
structs.NewServiceID("bar", nil): &structs.ServiceResolverConfigEntry{
|
||||||
Kind: structs.ServiceResolver,
|
Kind: structs.ServiceResolver,
|
||||||
Name: "bar",
|
Name: "bar",
|
||||||
Subsets: map[string]structs.ServiceResolverSubset{
|
Subsets: map[string]structs.ServiceResolverSubset{
|
||||||
|
@ -325,7 +325,7 @@ func Test_endpointsFromSnapshot(t *testing.T) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"foo": &structs.ServiceResolverConfigEntry{
|
structs.NewServiceID("foo", nil): &structs.ServiceResolverConfigEntry{
|
||||||
Kind: structs.ServiceResolver,
|
Kind: structs.ServiceResolver,
|
||||||
Name: "foo",
|
Name: "foo",
|
||||||
Subsets: map[string]structs.ServiceResolverSubset{
|
Subsets: map[string]structs.ServiceResolverSubset{
|
||||||
|
|
|
@ -81,9 +81,7 @@ func (s *Server) listenersFromSnapshotConnectProxy(cfgSnap *proxycfg.ConfigSnaps
|
||||||
|
|
||||||
// Add service health checks to the list of paths to create listeners for if needed
|
// Add service health checks to the list of paths to create listeners for if needed
|
||||||
if cfgSnap.Proxy.Expose.Checks {
|
if cfgSnap.Proxy.Expose.Checks {
|
||||||
// TODO (namespaces) update with real ent meta
|
psid := structs.NewServiceID(cfgSnap.Proxy.DestinationServiceID, &cfgSnap.ProxyID.EnterpriseMeta)
|
||||||
var psid structs.ServiceID
|
|
||||||
psid.Init(cfgSnap.Proxy.DestinationServiceID, structs.DefaultEnterpriseMeta())
|
|
||||||
for _, check := range s.CheckFetcher.ServiceHTTPBasedChecks(psid) {
|
for _, check := range s.CheckFetcher.ServiceHTTPBasedChecks(psid) {
|
||||||
p, err := parseCheckPath(check)
|
p, err := parseCheckPath(check)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -115,7 +115,7 @@ type ConfigFetcher interface {
|
||||||
// easier testing without several layers of mocked cache, local state and
|
// easier testing without several layers of mocked cache, local state and
|
||||||
// proxycfg.Manager.
|
// proxycfg.Manager.
|
||||||
type ConfigManager interface {
|
type ConfigManager interface {
|
||||||
Watch(proxyID string) (<-chan *proxycfg.ConfigSnapshot, proxycfg.CancelFunc)
|
Watch(proxyID structs.ServiceID) (<-chan *proxycfg.ConfigSnapshot, proxycfg.CancelFunc)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Server represents a gRPC server that can handle both XDS and ext_authz
|
// Server represents a gRPC server that can handle both XDS and ext_authz
|
||||||
|
@ -200,7 +200,7 @@ func (s *Server) process(stream ADSStream, reqCh <-chan *envoy.DiscoveryRequest)
|
||||||
var ok bool
|
var ok bool
|
||||||
var stateCh <-chan *proxycfg.ConfigSnapshot
|
var stateCh <-chan *proxycfg.ConfigSnapshot
|
||||||
var watchCancel func()
|
var watchCancel func()
|
||||||
var proxyID string
|
var proxyID structs.ServiceID
|
||||||
|
|
||||||
// need to run a small state machine to get through initial authentication.
|
// need to run a small state machine to get through initial authentication.
|
||||||
var state = stateInit
|
var state = stateInit
|
||||||
|
@ -254,15 +254,16 @@ func (s *Server) process(stream ADSStream, reqCh <-chan *envoy.DiscoveryRequest)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var authzContext acl.AuthorizerContext
|
||||||
switch cfgSnap.Kind {
|
switch cfgSnap.Kind {
|
||||||
case structs.ServiceKindConnectProxy:
|
case structs.ServiceKindConnectProxy:
|
||||||
// TODO (namespaces) - pass through a real ent authz ctx
|
cfgSnap.ProxyID.EnterpriseMeta.FillAuthzContext(&authzContext)
|
||||||
if rule != nil && rule.ServiceWrite(cfgSnap.Proxy.DestinationServiceName, nil) != acl.Allow {
|
if rule != nil && rule.ServiceWrite(cfgSnap.Proxy.DestinationServiceName, &authzContext) != acl.Allow {
|
||||||
return status.Errorf(codes.PermissionDenied, "permission denied")
|
return status.Errorf(codes.PermissionDenied, "permission denied")
|
||||||
}
|
}
|
||||||
case structs.ServiceKindMeshGateway:
|
case structs.ServiceKindMeshGateway:
|
||||||
// TODO (namespaces) - pass through a real ent authz ctx
|
cfgSnap.ProxyID.EnterpriseMeta.FillAuthzContext(&authzContext)
|
||||||
if rule != nil && rule.ServiceWrite(cfgSnap.Service, nil) != acl.Allow {
|
if rule != nil && rule.ServiceWrite(cfgSnap.Service, &authzContext) != acl.Allow {
|
||||||
return status.Errorf(codes.PermissionDenied, "permission denied")
|
return status.Errorf(codes.PermissionDenied, "permission denied")
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
|
@ -310,7 +311,7 @@ func (s *Server) process(stream ADSStream, reqCh <-chan *envoy.DiscoveryRequest)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
// Start authentication process, we need the proxyID
|
// Start authentication process, we need the proxyID
|
||||||
proxyID = req.Node.Id
|
proxyID = structs.NewServiceID(req.Node.Id, parseEnterpriseMeta(req.Node))
|
||||||
|
|
||||||
// Start watching config for that proxy
|
// Start watching config for that proxy
|
||||||
stateCh, watchCancel = s.CfgMgr.Watch(proxyID)
|
stateCh, watchCancel = s.CfgMgr.Watch(proxyID)
|
||||||
|
|
|
@ -0,0 +1,12 @@
|
||||||
|
// +build !consulent
|
||||||
|
|
||||||
|
package xds
|
||||||
|
|
||||||
|
import (
|
||||||
|
envoycore "github.com/envoyproxy/go-control-plane/envoy/api/v2/core"
|
||||||
|
"github.com/hashicorp/consul/agent/structs"
|
||||||
|
)
|
||||||
|
|
||||||
|
func parseEnterpriseMeta(node *envoycore.Node) *structs.EnterpriseMeta {
|
||||||
|
return structs.DefaultEnterpriseMeta()
|
||||||
|
}
|
|
@ -27,8 +27,8 @@ import (
|
||||||
// testing. It also implements ConnectAuthz to allow control over authorization.
|
// testing. It also implements ConnectAuthz to allow control over authorization.
|
||||||
type testManager struct {
|
type testManager struct {
|
||||||
sync.Mutex
|
sync.Mutex
|
||||||
chans map[string]chan *proxycfg.ConfigSnapshot
|
chans map[structs.ServiceID]chan *proxycfg.ConfigSnapshot
|
||||||
cancels chan string
|
cancels chan structs.ServiceID
|
||||||
authz map[string]connectAuthzResult
|
authz map[string]connectAuthzResult
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -41,21 +41,21 @@ type connectAuthzResult struct {
|
||||||
|
|
||||||
func newTestManager(t *testing.T) *testManager {
|
func newTestManager(t *testing.T) *testManager {
|
||||||
return &testManager{
|
return &testManager{
|
||||||
chans: map[string]chan *proxycfg.ConfigSnapshot{},
|
chans: map[structs.ServiceID]chan *proxycfg.ConfigSnapshot{},
|
||||||
cancels: make(chan string, 10),
|
cancels: make(chan structs.ServiceID, 10),
|
||||||
authz: make(map[string]connectAuthzResult),
|
authz: make(map[string]connectAuthzResult),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// RegisterProxy simulates a proxy registration
|
// RegisterProxy simulates a proxy registration
|
||||||
func (m *testManager) RegisterProxy(t *testing.T, proxyID string) {
|
func (m *testManager) RegisterProxy(t *testing.T, proxyID structs.ServiceID) {
|
||||||
m.Lock()
|
m.Lock()
|
||||||
defer m.Unlock()
|
defer m.Unlock()
|
||||||
m.chans[proxyID] = make(chan *proxycfg.ConfigSnapshot, 1)
|
m.chans[proxyID] = make(chan *proxycfg.ConfigSnapshot, 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Deliver simulates a proxy registration
|
// Deliver simulates a proxy registration
|
||||||
func (m *testManager) DeliverConfig(t *testing.T, proxyID string, cfg *proxycfg.ConfigSnapshot) {
|
func (m *testManager) DeliverConfig(t *testing.T, proxyID structs.ServiceID, cfg *proxycfg.ConfigSnapshot) {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
m.Lock()
|
m.Lock()
|
||||||
defer m.Unlock()
|
defer m.Unlock()
|
||||||
|
@ -67,7 +67,7 @@ func (m *testManager) DeliverConfig(t *testing.T, proxyID string, cfg *proxycfg.
|
||||||
}
|
}
|
||||||
|
|
||||||
// Watch implements ConfigManager
|
// Watch implements ConfigManager
|
||||||
func (m *testManager) Watch(proxyID string) (<-chan *proxycfg.ConfigSnapshot, proxycfg.CancelFunc) {
|
func (m *testManager) Watch(proxyID structs.ServiceID) (<-chan *proxycfg.ConfigSnapshot, proxycfg.CancelFunc) {
|
||||||
m.Lock()
|
m.Lock()
|
||||||
defer m.Unlock()
|
defer m.Unlock()
|
||||||
// ch might be nil but then it will just block forever
|
// ch might be nil but then it will just block forever
|
||||||
|
@ -81,7 +81,7 @@ func (m *testManager) Watch(proxyID string) (<-chan *proxycfg.ConfigSnapshot, pr
|
||||||
// probably won't work if you are running multiple Watches in parallel on
|
// probably won't work if you are running multiple Watches in parallel on
|
||||||
// multiple proxyIDS due to timing/ordering issues but I don't think we need to
|
// multiple proxyIDS due to timing/ordering issues but I don't think we need to
|
||||||
// do that.
|
// do that.
|
||||||
func (m *testManager) AssertWatchCancelled(t *testing.T, proxyID string) {
|
func (m *testManager) AssertWatchCancelled(t *testing.T, proxyID structs.ServiceID) {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
select {
|
select {
|
||||||
case got := <-m.cancels:
|
case got := <-m.cancels:
|
||||||
|
@ -120,13 +120,15 @@ func TestServer_StreamAggregatedResources_BasicProtocol(t *testing.T) {
|
||||||
}
|
}
|
||||||
s.Initialize()
|
s.Initialize()
|
||||||
|
|
||||||
|
sid := structs.NewServiceID("web-sidecar-proxy", nil)
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
err := s.StreamAggregatedResources(envoy.stream)
|
err := s.StreamAggregatedResources(envoy.stream)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
}()
|
}()
|
||||||
|
|
||||||
// Register the proxy to create state needed to Watch() on
|
// Register the proxy to create state needed to Watch() on
|
||||||
mgr.RegisterProxy(t, "web-sidecar-proxy")
|
mgr.RegisterProxy(t, sid)
|
||||||
|
|
||||||
// Send initial cluster discover
|
// Send initial cluster discover
|
||||||
envoy.SendReq(t, ClusterType, 0, 0)
|
envoy.SendReq(t, ClusterType, 0, 0)
|
||||||
|
@ -136,7 +138,7 @@ func TestServer_StreamAggregatedResources_BasicProtocol(t *testing.T) {
|
||||||
|
|
||||||
// Deliver a new snapshot
|
// Deliver a new snapshot
|
||||||
snap := proxycfg.TestConfigSnapshot(t)
|
snap := proxycfg.TestConfigSnapshot(t)
|
||||||
mgr.DeliverConfig(t, "web-sidecar-proxy", snap)
|
mgr.DeliverConfig(t, sid, snap)
|
||||||
|
|
||||||
assertResponseSent(t, envoy.stream.sendCh, expectClustersJSON(t, snap, "", 1, 1))
|
assertResponseSent(t, envoy.stream.sendCh, expectClustersJSON(t, snap, "", 1, 1))
|
||||||
|
|
||||||
|
@ -179,7 +181,7 @@ func TestServer_StreamAggregatedResources_BasicProtocol(t *testing.T) {
|
||||||
// doesn't know _what_ changed. We could do something trivial but let's
|
// doesn't know _what_ changed. We could do something trivial but let's
|
||||||
// simulate a leaf cert expiring and being rotated.
|
// simulate a leaf cert expiring and being rotated.
|
||||||
snap.ConnectProxy.Leaf = proxycfg.TestLeafForCA(t, snap.Roots.Roots[0])
|
snap.ConnectProxy.Leaf = proxycfg.TestLeafForCA(t, snap.Roots.Roots[0])
|
||||||
mgr.DeliverConfig(t, "web-sidecar-proxy", snap)
|
mgr.DeliverConfig(t, sid, snap)
|
||||||
|
|
||||||
// All 3 response that have something to return should return with new version
|
// All 3 response that have something to return should return with new version
|
||||||
// note that the ordering is not deterministic in general. Trying to make this
|
// note that the ordering is not deterministic in general. Trying to make this
|
||||||
|
@ -223,7 +225,7 @@ func TestServer_StreamAggregatedResources_BasicProtocol(t *testing.T) {
|
||||||
|
|
||||||
// Change config again and make sure it's delivered to everyone!
|
// Change config again and make sure it's delivered to everyone!
|
||||||
snap.ConnectProxy.Leaf = proxycfg.TestLeafForCA(t, snap.Roots.Roots[0])
|
snap.ConnectProxy.Leaf = proxycfg.TestLeafForCA(t, snap.Roots.Roots[0])
|
||||||
mgr.DeliverConfig(t, "web-sidecar-proxy", snap)
|
mgr.DeliverConfig(t, sid, snap)
|
||||||
|
|
||||||
assertResponseSent(t, envoy.stream.sendCh, expectClustersJSON(t, snap, "", 3, 7))
|
assertResponseSent(t, envoy.stream.sendCh, expectClustersJSON(t, snap, "", 3, 7))
|
||||||
assertResponseSent(t, envoy.stream.sendCh, expectEndpointsJSON(t, snap, "", 3, 8))
|
assertResponseSent(t, envoy.stream.sendCh, expectEndpointsJSON(t, snap, "", 3, 8))
|
||||||
|
@ -468,12 +470,13 @@ func TestServer_StreamAggregatedResources_ACLEnforcement(t *testing.T) {
|
||||||
errCh <- s.StreamAggregatedResources(envoy.stream)
|
errCh <- s.StreamAggregatedResources(envoy.stream)
|
||||||
}()
|
}()
|
||||||
|
|
||||||
|
sid := structs.NewServiceID("web-sidecar-proxy", nil)
|
||||||
// Register the proxy to create state needed to Watch() on
|
// Register the proxy to create state needed to Watch() on
|
||||||
mgr.RegisterProxy(t, "web-sidecar-proxy")
|
mgr.RegisterProxy(t, sid)
|
||||||
|
|
||||||
// Deliver a new snapshot
|
// Deliver a new snapshot
|
||||||
snap := proxycfg.TestConfigSnapshot(t)
|
snap := proxycfg.TestConfigSnapshot(t)
|
||||||
mgr.DeliverConfig(t, "web-sidecar-proxy", snap)
|
mgr.DeliverConfig(t, sid, snap)
|
||||||
|
|
||||||
// Send initial listener discover, in real life Envoy always sends cluster
|
// Send initial listener discover, in real life Envoy always sends cluster
|
||||||
// first but it doesn't really matter and listener has a response that
|
// first but it doesn't really matter and listener has a response that
|
||||||
|
@ -493,7 +496,7 @@ func TestServer_StreamAggregatedResources_ACLEnforcement(t *testing.T) {
|
||||||
if tt.wantDenied {
|
if tt.wantDenied {
|
||||||
require.Error(t, err)
|
require.Error(t, err)
|
||||||
require.Contains(t, err.Error(), "permission denied")
|
require.Contains(t, err.Error(), "permission denied")
|
||||||
mgr.AssertWatchCancelled(t, "web-sidecar-proxy")
|
mgr.AssertWatchCancelled(t, sid)
|
||||||
} else {
|
} else {
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
}
|
}
|
||||||
|
@ -549,8 +552,9 @@ func TestServer_StreamAggregatedResources_ACLTokenDeleted_StreamTerminatedDuring
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sid := structs.NewServiceID("web-sidecar-proxy", nil)
|
||||||
// Register the proxy to create state needed to Watch() on
|
// Register the proxy to create state needed to Watch() on
|
||||||
mgr.RegisterProxy(t, "web-sidecar-proxy")
|
mgr.RegisterProxy(t, sid)
|
||||||
|
|
||||||
// Send initial cluster discover (OK)
|
// Send initial cluster discover (OK)
|
||||||
envoy.SendReq(t, ClusterType, 0, 0)
|
envoy.SendReq(t, ClusterType, 0, 0)
|
||||||
|
@ -570,7 +574,7 @@ func TestServer_StreamAggregatedResources_ACLTokenDeleted_StreamTerminatedDuring
|
||||||
|
|
||||||
// Deliver a new snapshot
|
// Deliver a new snapshot
|
||||||
snap := proxycfg.TestConfigSnapshot(t)
|
snap := proxycfg.TestConfigSnapshot(t)
|
||||||
mgr.DeliverConfig(t, "web-sidecar-proxy", snap)
|
mgr.DeliverConfig(t, sid, snap)
|
||||||
|
|
||||||
assertResponseSent(t, envoy.stream.sendCh, expectClustersJSON(t, snap, token, 1, 1))
|
assertResponseSent(t, envoy.stream.sendCh, expectClustersJSON(t, snap, token, 1, 1))
|
||||||
|
|
||||||
|
@ -589,7 +593,7 @@ func TestServer_StreamAggregatedResources_ACLTokenDeleted_StreamTerminatedDuring
|
||||||
require.Equal(t, codes.Unauthenticated, gerr.Code())
|
require.Equal(t, codes.Unauthenticated, gerr.Code())
|
||||||
require.Equal(t, "unauthenticated: ACL not found", gerr.Message())
|
require.Equal(t, "unauthenticated: ACL not found", gerr.Message())
|
||||||
|
|
||||||
mgr.AssertWatchCancelled(t, "web-sidecar-proxy")
|
mgr.AssertWatchCancelled(t, sid)
|
||||||
case <-time.After(50 * time.Millisecond):
|
case <-time.After(50 * time.Millisecond):
|
||||||
t.Fatalf("timed out waiting for handler to finish")
|
t.Fatalf("timed out waiting for handler to finish")
|
||||||
}
|
}
|
||||||
|
@ -640,8 +644,9 @@ func TestServer_StreamAggregatedResources_ACLTokenDeleted_StreamTerminatedInBack
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sid := structs.NewServiceID("web-sidecar-proxy", nil)
|
||||||
// Register the proxy to create state needed to Watch() on
|
// Register the proxy to create state needed to Watch() on
|
||||||
mgr.RegisterProxy(t, "web-sidecar-proxy")
|
mgr.RegisterProxy(t, sid)
|
||||||
|
|
||||||
// Send initial cluster discover (OK)
|
// Send initial cluster discover (OK)
|
||||||
envoy.SendReq(t, ClusterType, 0, 0)
|
envoy.SendReq(t, ClusterType, 0, 0)
|
||||||
|
@ -661,7 +666,7 @@ func TestServer_StreamAggregatedResources_ACLTokenDeleted_StreamTerminatedInBack
|
||||||
|
|
||||||
// Deliver a new snapshot
|
// Deliver a new snapshot
|
||||||
snap := proxycfg.TestConfigSnapshot(t)
|
snap := proxycfg.TestConfigSnapshot(t)
|
||||||
mgr.DeliverConfig(t, "web-sidecar-proxy", snap)
|
mgr.DeliverConfig(t, sid, snap)
|
||||||
|
|
||||||
assertResponseSent(t, envoy.stream.sendCh, expectClustersJSON(t, snap, token, 1, 1))
|
assertResponseSent(t, envoy.stream.sendCh, expectClustersJSON(t, snap, token, 1, 1))
|
||||||
|
|
||||||
|
@ -688,7 +693,7 @@ func TestServer_StreamAggregatedResources_ACLTokenDeleted_StreamTerminatedInBack
|
||||||
require.Equal(t, codes.Unauthenticated, gerr.Code())
|
require.Equal(t, codes.Unauthenticated, gerr.Code())
|
||||||
require.Equal(t, "unauthenticated: ACL not found", gerr.Message())
|
require.Equal(t, "unauthenticated: ACL not found", gerr.Message())
|
||||||
|
|
||||||
mgr.AssertWatchCancelled(t, "web-sidecar-proxy")
|
mgr.AssertWatchCancelled(t, sid)
|
||||||
case <-time.After(200 * time.Millisecond):
|
case <-time.After(200 * time.Millisecond):
|
||||||
t.Fatalf("timed out waiting for handler to finish")
|
t.Fatalf("timed out waiting for handler to finish")
|
||||||
}
|
}
|
||||||
|
|
|
@ -88,6 +88,7 @@ type ExposePath struct {
|
||||||
type ServiceConfigEntry struct {
|
type ServiceConfigEntry struct {
|
||||||
Kind string
|
Kind string
|
||||||
Name string
|
Name string
|
||||||
|
Namespace string `json:",omitempty"`
|
||||||
Protocol string `json:",omitempty"`
|
Protocol string `json:",omitempty"`
|
||||||
MeshGateway MeshGatewayConfig `json:",omitempty"`
|
MeshGateway MeshGatewayConfig `json:",omitempty"`
|
||||||
Expose ExposeConfig `json:",omitempty"`
|
Expose ExposeConfig `json:",omitempty"`
|
||||||
|
@ -115,6 +116,7 @@ func (s *ServiceConfigEntry) GetModifyIndex() uint64 {
|
||||||
type ProxyConfigEntry struct {
|
type ProxyConfigEntry struct {
|
||||||
Kind string
|
Kind string
|
||||||
Name string
|
Name string
|
||||||
|
Namespace string `json:",omitempty"`
|
||||||
Config map[string]interface{} `json:",omitempty"`
|
Config map[string]interface{} `json:",omitempty"`
|
||||||
MeshGateway MeshGatewayConfig `json:",omitempty"`
|
MeshGateway MeshGatewayConfig `json:",omitempty"`
|
||||||
Expose ExposeConfig `json:",omitempty"`
|
Expose ExposeConfig `json:",omitempty"`
|
||||||
|
|
|
@ -6,8 +6,9 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
type ServiceRouterConfigEntry struct {
|
type ServiceRouterConfigEntry struct {
|
||||||
Kind string
|
Kind string
|
||||||
Name string
|
Name string
|
||||||
|
Namespace string `json:",omitempty"`
|
||||||
|
|
||||||
Routes []ServiceRoute `json:",omitempty"`
|
Routes []ServiceRoute `json:",omitempty"`
|
||||||
|
|
||||||
|
@ -104,8 +105,9 @@ func (e *ServiceRouteDestination) UnmarshalJSON(data []byte) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
type ServiceSplitterConfigEntry struct {
|
type ServiceSplitterConfigEntry struct {
|
||||||
Kind string
|
Kind string
|
||||||
Name string
|
Name string
|
||||||
|
Namespace string `json:",omitempty"`
|
||||||
|
|
||||||
Splits []ServiceSplit `json:",omitempty"`
|
Splits []ServiceSplit `json:",omitempty"`
|
||||||
|
|
||||||
|
@ -126,8 +128,9 @@ type ServiceSplit struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
type ServiceResolverConfigEntry struct {
|
type ServiceResolverConfigEntry struct {
|
||||||
Kind string
|
Kind string
|
||||||
Name string
|
Name string
|
||||||
|
Namespace string `json:",omitempty"`
|
||||||
|
|
||||||
DefaultSubset string `json:",omitempty"`
|
DefaultSubset string `json:",omitempty"`
|
||||||
Subsets map[string]ServiceResolverSubset `json:",omitempty"`
|
Subsets map[string]ServiceResolverSubset `json:",omitempty"`
|
||||||
|
|
|
@ -130,6 +130,7 @@ func TestAPI_ConfigEntry_DiscoveryChain(t *testing.T) {
|
||||||
entry: &ServiceResolverConfigEntry{
|
entry: &ServiceResolverConfigEntry{
|
||||||
Kind: ServiceResolver,
|
Kind: ServiceResolver,
|
||||||
Name: "test-failover",
|
Name: "test-failover",
|
||||||
|
Namespace: defaultNamespace,
|
||||||
DefaultSubset: "v1",
|
DefaultSubset: "v1",
|
||||||
Subsets: map[string]ServiceResolverSubset{
|
Subsets: map[string]ServiceResolverSubset{
|
||||||
"v1": ServiceResolverSubset{
|
"v1": ServiceResolverSubset{
|
||||||
|
@ -144,7 +145,8 @@ func TestAPI_ConfigEntry_DiscoveryChain(t *testing.T) {
|
||||||
Datacenters: []string{"dc2"},
|
Datacenters: []string{"dc2"},
|
||||||
},
|
},
|
||||||
"v1": ServiceResolverFailover{
|
"v1": ServiceResolverFailover{
|
||||||
Service: "alternate",
|
Service: "alternate",
|
||||||
|
Namespace: defaultNamespace,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
ConnectTimeout: 5 * time.Second,
|
ConnectTimeout: 5 * time.Second,
|
||||||
|
@ -154,12 +156,13 @@ func TestAPI_ConfigEntry_DiscoveryChain(t *testing.T) {
|
||||||
{
|
{
|
||||||
name: "redirect",
|
name: "redirect",
|
||||||
entry: &ServiceResolverConfigEntry{
|
entry: &ServiceResolverConfigEntry{
|
||||||
Kind: ServiceResolver,
|
Kind: ServiceResolver,
|
||||||
Name: "test-redirect",
|
Name: "test-redirect",
|
||||||
|
Namespace: defaultNamespace,
|
||||||
Redirect: &ServiceResolverRedirect{
|
Redirect: &ServiceResolverRedirect{
|
||||||
Service: "test-failover",
|
Service: "test-failover",
|
||||||
ServiceSubset: "v2",
|
ServiceSubset: "v2",
|
||||||
Namespace: "c",
|
Namespace: defaultNamespace,
|
||||||
Datacenter: "d",
|
Datacenter: "d",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -168,19 +171,20 @@ func TestAPI_ConfigEntry_DiscoveryChain(t *testing.T) {
|
||||||
{
|
{
|
||||||
name: "mega splitter", // use one mega object to avoid multiple trips
|
name: "mega splitter", // use one mega object to avoid multiple trips
|
||||||
entry: &ServiceSplitterConfigEntry{
|
entry: &ServiceSplitterConfigEntry{
|
||||||
Kind: ServiceSplitter,
|
Kind: ServiceSplitter,
|
||||||
Name: "test-split",
|
Name: "test-split",
|
||||||
|
Namespace: defaultNamespace,
|
||||||
Splits: []ServiceSplit{
|
Splits: []ServiceSplit{
|
||||||
{
|
{
|
||||||
Weight: 90,
|
Weight: 90,
|
||||||
Service: "test-failover",
|
Service: "test-failover",
|
||||||
ServiceSubset: "v1",
|
ServiceSubset: "v1",
|
||||||
Namespace: "c",
|
Namespace: defaultNamespace,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Weight: 10,
|
Weight: 10,
|
||||||
Service: "test-redirect",
|
Service: "test-redirect",
|
||||||
Namespace: "z",
|
Namespace: defaultNamespace,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -189,8 +193,9 @@ func TestAPI_ConfigEntry_DiscoveryChain(t *testing.T) {
|
||||||
{
|
{
|
||||||
name: "mega router", // use one mega object to avoid multiple trips
|
name: "mega router", // use one mega object to avoid multiple trips
|
||||||
entry: &ServiceRouterConfigEntry{
|
entry: &ServiceRouterConfigEntry{
|
||||||
Kind: ServiceRouter,
|
Kind: ServiceRouter,
|
||||||
Name: "test-route",
|
Name: "test-route",
|
||||||
|
Namespace: defaultNamespace,
|
||||||
Routes: []ServiceRoute{
|
Routes: []ServiceRoute{
|
||||||
{
|
{
|
||||||
Match: &ServiceRouteMatch{
|
Match: &ServiceRouteMatch{
|
||||||
|
@ -207,7 +212,7 @@ func TestAPI_ConfigEntry_DiscoveryChain(t *testing.T) {
|
||||||
Destination: &ServiceRouteDestination{
|
Destination: &ServiceRouteDestination{
|
||||||
Service: "test-failover",
|
Service: "test-failover",
|
||||||
ServiceSubset: "v2",
|
ServiceSubset: "v2",
|
||||||
Namespace: "sec",
|
Namespace: defaultNamespace,
|
||||||
PrefixRewrite: "/",
|
PrefixRewrite: "/",
|
||||||
RequestTimeout: 5 * time.Second,
|
RequestTimeout: 5 * time.Second,
|
||||||
NumRetries: 5,
|
NumRetries: 5,
|
||||||
|
|
|
@ -31,6 +31,7 @@ func (c *cmd) init() {
|
||||||
c.http = &flags.HTTPFlags{}
|
c.http = &flags.HTTPFlags{}
|
||||||
flags.Merge(c.flags, c.http.ClientFlags())
|
flags.Merge(c.flags, c.http.ClientFlags())
|
||||||
flags.Merge(c.flags, c.http.ServerFlags())
|
flags.Merge(c.flags, c.http.ServerFlags())
|
||||||
|
flags.Merge(c.flags, c.http.NamespaceFlags())
|
||||||
c.help = flags.Usage(help, c.flags)
|
c.help = flags.Usage(help, c.flags)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -29,6 +29,7 @@ func (c *cmd) init() {
|
||||||
c.http = &flags.HTTPFlags{}
|
c.http = &flags.HTTPFlags{}
|
||||||
flags.Merge(c.flags, c.http.ClientFlags())
|
flags.Merge(c.flags, c.http.ClientFlags())
|
||||||
flags.Merge(c.flags, c.http.ServerFlags())
|
flags.Merge(c.flags, c.http.ServerFlags())
|
||||||
|
flags.Merge(c.flags, c.http.NamespaceFlags())
|
||||||
c.help = flags.Usage(help, c.flags)
|
c.help = flags.Usage(help, c.flags)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -34,6 +34,7 @@ func (c *cmd) init() {
|
||||||
c.http = &flags.HTTPFlags{}
|
c.http = &flags.HTTPFlags{}
|
||||||
flags.Merge(c.flags, c.http.ClientFlags())
|
flags.Merge(c.flags, c.http.ClientFlags())
|
||||||
flags.Merge(c.flags, c.http.ServerFlags())
|
flags.Merge(c.flags, c.http.ServerFlags())
|
||||||
|
flags.Merge(c.flags, c.http.NamespaceFlags())
|
||||||
c.help = flags.Usage(help, c.flags)
|
c.help = flags.Usage(help, c.flags)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -44,6 +44,7 @@ func (c *cmd) init() {
|
||||||
"This is used in combination with the -cas flag.")
|
"This is used in combination with the -cas flag.")
|
||||||
flags.Merge(c.flags, c.http.ClientFlags())
|
flags.Merge(c.flags, c.http.ClientFlags())
|
||||||
flags.Merge(c.flags, c.http.ServerFlags())
|
flags.Merge(c.flags, c.http.ServerFlags())
|
||||||
|
flags.Merge(c.flags, c.http.NamespaceFlags())
|
||||||
c.help = flags.Usage(help, c.flags)
|
c.help = flags.Usage(help, c.flags)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -92,6 +92,10 @@ type BootstrapTplArgs struct {
|
||||||
// the bootstrap config. It's format may vary based on Envoy version used.
|
// the bootstrap config. It's format may vary based on Envoy version used.
|
||||||
// See https://www.envoyproxy.io/docs/envoy/v1.9.0/api-v2/config/trace/v2/trace.proto.
|
// See https://www.envoyproxy.io/docs/envoy/v1.9.0/api-v2/config/trace/v2/trace.proto.
|
||||||
TracingConfigJSON string
|
TracingConfigJSON string
|
||||||
|
|
||||||
|
// Namespace is the Consul Enterprise Namespace of the proxy service instance as
|
||||||
|
// registered with the Consul agent.
|
||||||
|
Namespace string
|
||||||
}
|
}
|
||||||
|
|
||||||
const bootstrapTemplate = `{
|
const bootstrapTemplate = `{
|
||||||
|
@ -106,7 +110,10 @@ const bootstrapTemplate = `{
|
||||||
},
|
},
|
||||||
"node": {
|
"node": {
|
||||||
"cluster": "{{ .ProxyCluster }}",
|
"cluster": "{{ .ProxyCluster }}",
|
||||||
"id": "{{ .ProxyID }}"
|
"id": "{{ .ProxyID }}",
|
||||||
|
"metadata": {
|
||||||
|
"namespace": "{{if ne .Namespace ""}}{{ .Namespace }}{{else}}default{{end}}"
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"static_resources": {
|
"static_resources": {
|
||||||
"clusters": [
|
"clusters": [
|
||||||
|
|
|
@ -131,6 +131,7 @@ func (c *cmd) init() {
|
||||||
|
|
||||||
c.http = &flags.HTTPFlags{}
|
c.http = &flags.HTTPFlags{}
|
||||||
flags.Merge(c.flags, c.http.ClientFlags())
|
flags.Merge(c.flags, c.http.ClientFlags())
|
||||||
|
flags.Merge(c.flags, c.http.NamespaceFlags())
|
||||||
c.help = flags.Usage(help, c.flags)
|
c.help = flags.Usage(help, c.flags)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -517,6 +518,7 @@ func (c *cmd) templateArgs() (*BootstrapTplArgs, error) {
|
||||||
AdminBindPort: adminPort,
|
AdminBindPort: adminPort,
|
||||||
Token: httpCfg.Token,
|
Token: httpCfg.Token,
|
||||||
LocalAgentClusterName: xds.LocalAgentClusterName,
|
LocalAgentClusterName: xds.LocalAgentClusterName,
|
||||||
|
Namespace: httpCfg.Namespace,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -10,7 +10,10 @@
|
||||||
},
|
},
|
||||||
"node": {
|
"node": {
|
||||||
"cluster": "test-proxy",
|
"cluster": "test-proxy",
|
||||||
"id": "test-proxy"
|
"id": "test-proxy",
|
||||||
|
"metadata": {
|
||||||
|
"namespace": "default"
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"static_resources": {
|
"static_resources": {
|
||||||
"clusters": [
|
"clusters": [
|
||||||
|
|
|
@ -10,7 +10,10 @@
|
||||||
},
|
},
|
||||||
"node": {
|
"node": {
|
||||||
"cluster": "test-proxy",
|
"cluster": "test-proxy",
|
||||||
"id": "test-proxy"
|
"id": "test-proxy",
|
||||||
|
"metadata": {
|
||||||
|
"namespace": "default"
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"static_resources": {
|
"static_resources": {
|
||||||
"clusters": [
|
"clusters": [
|
||||||
|
|
|
@ -10,7 +10,10 @@
|
||||||
},
|
},
|
||||||
"node": {
|
"node": {
|
||||||
"cluster": "test-proxy",
|
"cluster": "test-proxy",
|
||||||
"id": "test-proxy"
|
"id": "test-proxy",
|
||||||
|
"metadata": {
|
||||||
|
"namespace": "default"
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"static_resources": {
|
"static_resources": {
|
||||||
"clusters": [
|
"clusters": [
|
||||||
|
|
|
@ -10,7 +10,10 @@
|
||||||
},
|
},
|
||||||
"node": {
|
"node": {
|
||||||
"cluster": "test-proxy",
|
"cluster": "test-proxy",
|
||||||
"id": "test-proxy"
|
"id": "test-proxy",
|
||||||
|
"metadata": {
|
||||||
|
"namespace": "default"
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"static_resources": {
|
"static_resources": {
|
||||||
"clusters": [
|
"clusters": [
|
||||||
|
|
|
@ -10,7 +10,10 @@
|
||||||
},
|
},
|
||||||
"node": {
|
"node": {
|
||||||
"cluster": "test-proxy",
|
"cluster": "test-proxy",
|
||||||
"id": "test-proxy"
|
"id": "test-proxy",
|
||||||
|
"metadata": {
|
||||||
|
"namespace": "default"
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"static_resources": {
|
"static_resources": {
|
||||||
"clusters": [
|
"clusters": [
|
||||||
|
|
|
@ -10,7 +10,10 @@
|
||||||
},
|
},
|
||||||
"node": {
|
"node": {
|
||||||
"cluster": "test-proxy",
|
"cluster": "test-proxy",
|
||||||
"id": "test-proxy"
|
"id": "test-proxy",
|
||||||
|
"metadata": {
|
||||||
|
"namespace": "default"
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"static_resources": {
|
"static_resources": {
|
||||||
"clusters": [
|
"clusters": [
|
||||||
|
|
|
@ -10,7 +10,10 @@
|
||||||
},
|
},
|
||||||
"node": {
|
"node": {
|
||||||
"cluster": "test-proxy",
|
"cluster": "test-proxy",
|
||||||
"id": "test-proxy"
|
"id": "test-proxy",
|
||||||
|
"metadata": {
|
||||||
|
"namespace": "default"
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"static_resources": {
|
"static_resources": {
|
||||||
"clusters": [
|
"clusters": [
|
||||||
|
|
|
@ -10,7 +10,10 @@
|
||||||
},
|
},
|
||||||
"node": {
|
"node": {
|
||||||
"cluster": "test-proxy",
|
"cluster": "test-proxy",
|
||||||
"id": "test-proxy"
|
"id": "test-proxy",
|
||||||
|
"metadata": {
|
||||||
|
"namespace": "default"
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"static_resources": {
|
"static_resources": {
|
||||||
"clusters": [
|
"clusters": [
|
||||||
|
|
|
@ -10,7 +10,10 @@
|
||||||
},
|
},
|
||||||
"node": {
|
"node": {
|
||||||
"cluster": "test-proxy",
|
"cluster": "test-proxy",
|
||||||
"id": "test-proxy"
|
"id": "test-proxy",
|
||||||
|
"metadata": {
|
||||||
|
"namespace": "default"
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"static_resources": {
|
"static_resources": {
|
||||||
"clusters": [
|
"clusters": [
|
||||||
|
|
|
@ -10,7 +10,10 @@
|
||||||
},
|
},
|
||||||
"node": {
|
"node": {
|
||||||
"cluster": "test-proxy",
|
"cluster": "test-proxy",
|
||||||
"id": "test-proxy"
|
"id": "test-proxy",
|
||||||
|
"metadata": {
|
||||||
|
"namespace": "default"
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"static_resources": {
|
"static_resources": {
|
||||||
"clusters": [
|
"clusters": [
|
||||||
|
|
|
@ -10,7 +10,10 @@
|
||||||
},
|
},
|
||||||
"node": {
|
"node": {
|
||||||
"cluster": "test-proxy",
|
"cluster": "test-proxy",
|
||||||
"id": "test-proxy"
|
"id": "test-proxy",
|
||||||
|
"metadata": {
|
||||||
|
"namespace": "default"
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"static_resources": {
|
"static_resources": {
|
||||||
"clusters": [
|
"clusters": [
|
||||||
|
|
|
@ -10,7 +10,10 @@
|
||||||
},
|
},
|
||||||
"node": {
|
"node": {
|
||||||
"cluster": "test-proxy",
|
"cluster": "test-proxy",
|
||||||
"id": "test-proxy"
|
"id": "test-proxy",
|
||||||
|
"metadata": {
|
||||||
|
"namespace": "default"
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"static_resources": {
|
"static_resources": {
|
||||||
"clusters": [
|
"clusters": [
|
||||||
|
|
|
@ -10,7 +10,10 @@
|
||||||
},
|
},
|
||||||
"node": {
|
"node": {
|
||||||
"cluster": "test-proxy",
|
"cluster": "test-proxy",
|
||||||
"id": "test-proxy"
|
"id": "test-proxy",
|
||||||
|
"metadata": {
|
||||||
|
"namespace": "default"
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"static_resources": {
|
"static_resources": {
|
||||||
"clusters": [
|
"clusters": [
|
||||||
|
|
|
@ -10,7 +10,10 @@
|
||||||
},
|
},
|
||||||
"node": {
|
"node": {
|
||||||
"cluster": "test-proxy",
|
"cluster": "test-proxy",
|
||||||
"id": "test-proxy"
|
"id": "test-proxy",
|
||||||
|
"metadata": {
|
||||||
|
"namespace": "default"
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"static_resources": {
|
"static_resources": {
|
||||||
"clusters": [
|
"clusters": [
|
||||||
|
|
|
@ -10,7 +10,10 @@
|
||||||
},
|
},
|
||||||
"node": {
|
"node": {
|
||||||
"cluster": "test-proxy",
|
"cluster": "test-proxy",
|
||||||
"id": "test-proxy"
|
"id": "test-proxy",
|
||||||
|
"metadata": {
|
||||||
|
"namespace": "default"
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"static_resources": {
|
"static_resources": {
|
||||||
"clusters": [
|
"clusters": [
|
||||||
|
|
|
@ -0,0 +1,7 @@
|
||||||
|
FROM alpine:latest
|
||||||
|
|
||||||
|
RUN apk add --no-cache tcpdump
|
||||||
|
VOLUME [ "/data" ]
|
||||||
|
|
||||||
|
CMD [ "-w", "/data/all.pcap" ]
|
||||||
|
ENTRYPOINT [ "/usr/sbin/tcpdump" ]
|
|
@ -155,6 +155,22 @@ services:
|
||||||
- "-redirect-port"
|
- "-redirect-port"
|
||||||
- "disabled"
|
- "disabled"
|
||||||
network_mode: service:consul-primary
|
network_mode: service:consul-primary
|
||||||
|
|
||||||
|
s3-alt:
|
||||||
|
depends_on:
|
||||||
|
- consul-primary
|
||||||
|
image: "fortio/fortio"
|
||||||
|
environment:
|
||||||
|
- "FORTIO_NAME=s3-alt"
|
||||||
|
command:
|
||||||
|
- "server"
|
||||||
|
- "-http-port"
|
||||||
|
- ":8286"
|
||||||
|
- "-grpc-port"
|
||||||
|
- ":8280"
|
||||||
|
- "-redirect-port"
|
||||||
|
- "disabled"
|
||||||
|
network_mode: service:consul-primary
|
||||||
|
|
||||||
s1-sidecar-proxy:
|
s1-sidecar-proxy:
|
||||||
depends_on:
|
depends_on:
|
||||||
|
@ -302,6 +318,27 @@ services:
|
||||||
volumes:
|
volumes:
|
||||||
- *workdir-volume
|
- *workdir-volume
|
||||||
network_mode: service:consul-primary
|
network_mode: service:consul-primary
|
||||||
|
|
||||||
|
s3-alt-sidecar-proxy:
|
||||||
|
depends_on:
|
||||||
|
- consul-primary
|
||||||
|
image: "envoyproxy/envoy:v${ENVOY_VERSION:-1.8.0}"
|
||||||
|
command:
|
||||||
|
- "envoy"
|
||||||
|
- "-c"
|
||||||
|
- "/workdir/primary/envoy/s3-alt-bootstrap.json"
|
||||||
|
- "-l"
|
||||||
|
- "debug"
|
||||||
|
# Hot restart breaks since both envoys seem to interact with each other
|
||||||
|
# despite separate containers that don't share IPC namespace. Not quite
|
||||||
|
# sure how this happens but may be due to unix socket being in some shared
|
||||||
|
# location?
|
||||||
|
- "--disable-hot-restart"
|
||||||
|
- "--drain-time-s"
|
||||||
|
- "1"
|
||||||
|
volumes:
|
||||||
|
- *workdir-volume
|
||||||
|
network_mode: service:consul-primary
|
||||||
|
|
||||||
verify-primary:
|
verify-primary:
|
||||||
depends_on:
|
depends_on:
|
||||||
|
@ -502,7 +539,7 @@ services:
|
||||||
volumes:
|
volumes:
|
||||||
- *workdir-volume
|
- *workdir-volume
|
||||||
network_mode: service:consul-primary
|
network_mode: service:consul-primary
|
||||||
|
|
||||||
gateway-secondary:
|
gateway-secondary:
|
||||||
depends_on:
|
depends_on:
|
||||||
- consul-secondary
|
- consul-secondary
|
||||||
|
@ -557,3 +594,35 @@ services:
|
||||||
- *workdir-volume
|
- *workdir-volume
|
||||||
network_mode: service:consul-secondary
|
network_mode: service:consul-secondary
|
||||||
pid: host
|
pid: host
|
||||||
|
|
||||||
|
tcpdump-primary:
|
||||||
|
depends_on:
|
||||||
|
- consul-primary
|
||||||
|
build:
|
||||||
|
context: .
|
||||||
|
dockerfile: Dockerfile-tcpdump
|
||||||
|
|
||||||
|
# we cant do this in circle but its only here to temporarily enable.
|
||||||
|
volumes:
|
||||||
|
- type: bind
|
||||||
|
source: ./${LOG_DIR}
|
||||||
|
target: /data
|
||||||
|
command: -v -i any -w /data/primary.pcap
|
||||||
|
network_mode: service:consul-primary
|
||||||
|
|
||||||
|
tcpdump-secondary:
|
||||||
|
depends_on:
|
||||||
|
- consul-secondary
|
||||||
|
build:
|
||||||
|
context: .
|
||||||
|
dockerfile: Dockerfile-tcpdump
|
||||||
|
|
||||||
|
# we cant do this in circle but its only here to temporarily enable.
|
||||||
|
volumes:
|
||||||
|
- type: bind
|
||||||
|
source: ./${LOG_DIR}
|
||||||
|
target: /data
|
||||||
|
command: -v -i any -w /data/secondary.pcap
|
||||||
|
network_mode: service:consul-secondary
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -19,7 +19,7 @@ function retry {
|
||||||
|
|
||||||
for ((i=1;i<=$max;i++))
|
for ((i=1;i<=$max;i++))
|
||||||
do
|
do
|
||||||
if $@
|
if "$@"
|
||||||
then
|
then
|
||||||
if test $errtrace -eq 1
|
if test $errtrace -eq 1
|
||||||
then
|
then
|
||||||
|
@ -42,13 +42,13 @@ function retry {
|
||||||
function retry_default {
|
function retry_default {
|
||||||
set +E
|
set +E
|
||||||
ret=0
|
ret=0
|
||||||
retry 5 1 $@ || ret=1
|
retry 5 1 "$@" || ret=1
|
||||||
set -E
|
set -E
|
||||||
return $ret
|
return $ret
|
||||||
}
|
}
|
||||||
|
|
||||||
function retry_long {
|
function retry_long {
|
||||||
retry 30 1 $@
|
retry 30 1 "$@"
|
||||||
}
|
}
|
||||||
|
|
||||||
function echored {
|
function echored {
|
||||||
|
@ -108,15 +108,16 @@ function assert_proxy_presents_cert_uri {
|
||||||
local HOSTPORT=$1
|
local HOSTPORT=$1
|
||||||
local SERVICENAME=$2
|
local SERVICENAME=$2
|
||||||
local DC=${3:-primary}
|
local DC=${3:-primary}
|
||||||
|
local NS=${4:-default}
|
||||||
|
|
||||||
|
|
||||||
CERT=$(retry_default get_cert $HOSTPORT)
|
CERT=$(retry_default get_cert $HOSTPORT)
|
||||||
|
|
||||||
echo "WANT SERVICE: $SERVICENAME"
|
echo "WANT SERVICE: ${NS}/${SERVICENAME}"
|
||||||
echo "GOT CERT:"
|
echo "GOT CERT:"
|
||||||
echo "$CERT"
|
echo "$CERT"
|
||||||
|
|
||||||
echo "$CERT" | grep -Eo "URI:spiffe://([a-zA-Z0-9-]+).consul/ns/default/dc/${DC}/svc/$SERVICENAME"
|
echo "$CERT" | grep -Eo "URI:spiffe://([a-zA-Z0-9-]+).consul/ns/${NS}/dc/${DC}/svc/$SERVICENAME"
|
||||||
}
|
}
|
||||||
|
|
||||||
function assert_envoy_version {
|
function assert_envoy_version {
|
||||||
|
@ -290,10 +291,41 @@ function assert_envoy_metric_at_least {
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function assert_envoy_aggregate_metric_at_least {
|
||||||
|
set -eEuo pipefail
|
||||||
|
local HOSTPORT=$1
|
||||||
|
local METRIC=$2
|
||||||
|
local EXPECT_COUNT=$3
|
||||||
|
|
||||||
|
METRICS=$(get_envoy_metrics $HOSTPORT "$METRIC")
|
||||||
|
|
||||||
|
if [ -z "${METRICS}" ]
|
||||||
|
then
|
||||||
|
echo "Metric not found" 1>&2
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
GOT_COUNT=$(awk '{ sum += $2 } END { print sum }' <<< "$METRICS")
|
||||||
|
|
||||||
|
if [ -z "$GOT_COUNT" ]
|
||||||
|
then
|
||||||
|
echo "Couldn't parse metric count" 1>&2
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ $EXPECT_COUNT -gt $GOT_COUNT ]
|
||||||
|
then
|
||||||
|
echo "$METRIC - expected >= count: $EXPECT_COUNT, actual count: $GOT_COUNT" 1>&2
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
function get_healthy_service_count {
|
function get_healthy_service_count {
|
||||||
local SERVICE_NAME=$1
|
local SERVICE_NAME=$1
|
||||||
local DC=$2
|
local DC=$2
|
||||||
run retry_default curl -s -f "127.0.0.1:8500/v1/health/connect/${SERVICE_NAME}?dc=${DC}&passing"
|
local NS=$3
|
||||||
|
|
||||||
|
run retry_default curl -s -f ${HEADERS} "127.0.0.1:8500/v1/health/connect/${SERVICE_NAME}?dc=${DC}&passing&ns=${NS}"
|
||||||
[ "$status" -eq 0 ]
|
[ "$status" -eq 0 ]
|
||||||
echo "$output" | jq --raw-output '. | length'
|
echo "$output" | jq --raw-output '. | length'
|
||||||
}
|
}
|
||||||
|
@ -302,8 +334,9 @@ function assert_service_has_healthy_instances_once {
|
||||||
local SERVICE_NAME=$1
|
local SERVICE_NAME=$1
|
||||||
local EXPECT_COUNT=$2
|
local EXPECT_COUNT=$2
|
||||||
local DC=${3:-primary}
|
local DC=${3:-primary}
|
||||||
|
local NS=$4
|
||||||
|
|
||||||
GOT_COUNT=$(get_healthy_service_count $SERVICE_NAME $DC)
|
GOT_COUNT=$(get_healthy_service_count "$SERVICE_NAME" "$DC" "$NS")
|
||||||
|
|
||||||
[ "$GOT_COUNT" -eq $EXPECT_COUNT ]
|
[ "$GOT_COUNT" -eq $EXPECT_COUNT ]
|
||||||
}
|
}
|
||||||
|
@ -312,8 +345,9 @@ function assert_service_has_healthy_instances {
|
||||||
local SERVICE_NAME=$1
|
local SERVICE_NAME=$1
|
||||||
local EXPECT_COUNT=$2
|
local EXPECT_COUNT=$2
|
||||||
local DC=${3:-primary}
|
local DC=${3:-primary}
|
||||||
|
local NS=$4
|
||||||
|
|
||||||
run retry_long assert_service_has_healthy_instances_once $SERVICE_NAME $EXPECT_COUNT $DC
|
run retry_long assert_service_has_healthy_instances_once "$SERVICE_NAME" "$EXPECT_COUNT" "$DC" "$NS"
|
||||||
[ "$status" -eq 0 ]
|
[ "$status" -eq 0 ]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -335,6 +369,20 @@ function docker_curl {
|
||||||
docker run -ti --rm --network container:envoy_consul-${DC}_1 --entrypoint curl consul-dev "$@"
|
docker run -ti --rm --network container:envoy_consul-${DC}_1 --entrypoint curl consul-dev "$@"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function docker_exec {
|
||||||
|
if ! docker exec -i "$@"
|
||||||
|
then
|
||||||
|
echo "Failed to execute: docker exec -i $@" 1>&2
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
function docker_consul_exec {
|
||||||
|
local DC=$1
|
||||||
|
shift 1
|
||||||
|
docker_exec envoy_consul-${DC}_1 "$@"
|
||||||
|
}
|
||||||
|
|
||||||
function get_envoy_pid {
|
function get_envoy_pid {
|
||||||
local BOOTSTRAP_NAME=$1
|
local BOOTSTRAP_NAME=$1
|
||||||
local DC=${2:-primary}
|
local DC=${2:-primary}
|
||||||
|
@ -417,6 +465,7 @@ function gen_envoy_bootstrap {
|
||||||
ADMIN_PORT=$2
|
ADMIN_PORT=$2
|
||||||
DC=${3:-primary}
|
DC=${3:-primary}
|
||||||
IS_MGW=${4:-0}
|
IS_MGW=${4:-0}
|
||||||
|
EXTRA_ENVOY_BS_ARGS="${5-}"
|
||||||
|
|
||||||
PROXY_ID="$SERVICE"
|
PROXY_ID="$SERVICE"
|
||||||
if ! is_set "$IS_MGW"
|
if ! is_set "$IS_MGW"
|
||||||
|
@ -426,7 +475,7 @@ function gen_envoy_bootstrap {
|
||||||
|
|
||||||
if output=$(docker_consul "$DC" connect envoy -bootstrap \
|
if output=$(docker_consul "$DC" connect envoy -bootstrap \
|
||||||
-proxy-id $PROXY_ID \
|
-proxy-id $PROXY_ID \
|
||||||
-admin-bind 0.0.0.0:$ADMIN_PORT 2>&1); then
|
-admin-bind 0.0.0.0:$ADMIN_PORT ${EXTRA_ENVOY_BS_ARGS} 2>&1); then
|
||||||
|
|
||||||
# All OK, write config to file
|
# All OK, write config to file
|
||||||
echo "$output" > workdir/${DC}/envoy/$SERVICE-bootstrap.json
|
echo "$output" > workdir/${DC}/envoy/$SERVICE-bootstrap.json
|
||||||
|
|
|
@ -54,11 +54,11 @@ function cleanup {
|
||||||
trap cleanup EXIT
|
trap cleanup EXIT
|
||||||
|
|
||||||
function command_error {
|
function command_error {
|
||||||
echo "ERR: command exited with status $1"
|
echo "ERR: command exited with status $1" 1>&2
|
||||||
echo " command: $2"
|
echo " command: $2" 1>&2
|
||||||
echo " line: $3"
|
echo " line: $3" 1>&2
|
||||||
echo " function: $4"
|
echo " function: $4" 1>&2
|
||||||
echo " called at: $5"
|
echo " called at: $5" 1>&2
|
||||||
# printf '%s\n' "${FUNCNAME[@]}"
|
# printf '%s\n' "${FUNCNAME[@]}"
|
||||||
# printf '%s\n' "${BASH_SOURCE[@]}"
|
# printf '%s\n' "${BASH_SOURCE[@]}"
|
||||||
# printf '%s\n' "${BASH_LINENO[@]}"
|
# printf '%s\n' "${BASH_LINENO[@]}"
|
||||||
|
@ -84,7 +84,7 @@ function init_workdir {
|
||||||
# don't wipe logs between runs as they are already split and we need them to
|
# don't wipe logs between runs as they are already split and we need them to
|
||||||
# upload as artifacts later.
|
# upload as artifacts later.
|
||||||
rm -rf workdir/${DC}
|
rm -rf workdir/${DC}
|
||||||
mkdir -p workdir/${DC}/{consul,envoy,bats,statsd}
|
mkdir -p workdir/${DC}/{consul,envoy,bats,statsd,data}
|
||||||
|
|
||||||
# Reload consul config from defaults
|
# Reload consul config from defaults
|
||||||
cp consul-base-cfg/* workdir/${DC}/consul/
|
cp consul-base-cfg/* workdir/${DC}/consul/
|
||||||
|
@ -103,7 +103,12 @@ function init_workdir {
|
||||||
find ${CASE_DIR}/${DC} -type f -name '*.hcl' -exec cp -f {} workdir/${DC}/consul \;
|
find ${CASE_DIR}/${DC} -type f -name '*.hcl' -exec cp -f {} workdir/${DC}/consul \;
|
||||||
find ${CASE_DIR}/${DC} -type f -name '*.bats' -exec cp -f {} workdir/${DC}/bats \;
|
find ${CASE_DIR}/${DC} -type f -name '*.bats' -exec cp -f {} workdir/${DC}/bats \;
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
if test -d "${CASE_DIR}/data"
|
||||||
|
then
|
||||||
|
cp -r ${CASE_DIR}/data/* workdir/${DC}/data
|
||||||
|
fi
|
||||||
|
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -131,7 +136,7 @@ function start_services {
|
||||||
# Push the state to the shared docker volume (note this is because CircleCI
|
# Push the state to the shared docker volume (note this is because CircleCI
|
||||||
# can't use shared volumes)
|
# can't use shared volumes)
|
||||||
docker cp workdir/. envoy_workdir_1:/workdir
|
docker cp workdir/. envoy_workdir_1:/workdir
|
||||||
|
|
||||||
# Start containers required
|
# Start containers required
|
||||||
if [ ! -z "$REQUIRED_SERVICES" ] ; then
|
if [ ! -z "$REQUIRED_SERVICES" ] ; then
|
||||||
docker-compose rm -s -v -f $REQUIRED_SERVICES || true
|
docker-compose rm -s -v -f $REQUIRED_SERVICES || true
|
||||||
|
@ -243,23 +248,28 @@ function runTest {
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
echo "Setting up the primary datacenter"
|
||||||
pre_service_setup primary
|
pre_service_setup primary
|
||||||
if [ $? -ne 0 ]
|
if [ $? -ne 0 ]
|
||||||
then
|
then
|
||||||
|
echo "Setting up the primary datacenter failed"
|
||||||
capture_logs
|
capture_logs
|
||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if is_set $REQUIRE_SECONDARY
|
if is_set $REQUIRE_SECONDARY
|
||||||
then
|
then
|
||||||
|
echo "Setting up the secondary datacenter"
|
||||||
pre_service_setup secondary
|
pre_service_setup secondary
|
||||||
if [ $? -ne 0 ]
|
if [ $? -ne 0 ]
|
||||||
then
|
then
|
||||||
|
echo "Setting up the secondary datacenter failed"
|
||||||
capture_logs
|
capture_logs
|
||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
echo "Starting services"
|
||||||
start_services
|
start_services
|
||||||
if [ $? -ne 0 ]
|
if [ $? -ne 0 ]
|
||||||
then
|
then
|
||||||
|
|
Loading…
Reference in New Issue