connect: add toggle to globally disable wildcard outbound network access when transparent proxy is enabled (#9973)
This adds a new config entry kind "cluster" with a single special name "cluster" where this can be controlled.
This commit is contained in:
parent
a711e119e7
commit
82245585c6
|
@ -0,0 +1,3 @@
|
|||
```release-note:feature
|
||||
connect: add toggle to globally disable wildcard outbound network access when transparent proxy is enabled
|
||||
```
|
|
@ -4095,6 +4095,116 @@ func TestLoad_IntegrationWithFlags(t *testing.T) {
|
|||
}
|
||||
},
|
||||
})
|
||||
run(t, testCase{
|
||||
desc: "ConfigEntry bootstrap cluster (snake-case)",
|
||||
args: []string{`-data-dir=` + dataDir},
|
||||
json: []string{`{
|
||||
"config_entries": {
|
||||
"bootstrap": [
|
||||
{
|
||||
"kind": "cluster",
|
||||
"name": "cluster",
|
||||
"meta" : {
|
||||
"foo": "bar",
|
||||
"gir": "zim"
|
||||
},
|
||||
"transparent_proxy": {
|
||||
"catalog_destinations_only": true
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}`,
|
||||
},
|
||||
hcl: []string{`
|
||||
config_entries {
|
||||
bootstrap {
|
||||
kind = "cluster"
|
||||
name = "cluster"
|
||||
meta {
|
||||
"foo" = "bar"
|
||||
"gir" = "zim"
|
||||
}
|
||||
transparent_proxy {
|
||||
catalog_destinations_only = true
|
||||
}
|
||||
}
|
||||
}
|
||||
`,
|
||||
},
|
||||
expected: func(rt *RuntimeConfig) {
|
||||
rt.DataDir = dataDir
|
||||
rt.ConfigEntryBootstrap = []structs.ConfigEntry{
|
||||
&structs.ClusterConfigEntry{
|
||||
Kind: "cluster",
|
||||
Name: "cluster",
|
||||
Meta: map[string]string{
|
||||
"foo": "bar",
|
||||
"gir": "zim",
|
||||
},
|
||||
EnterpriseMeta: *defaultEntMeta,
|
||||
TransparentProxy: structs.TransparentProxyClusterConfig{
|
||||
CatalogDestinationsOnly: true,
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
})
|
||||
run(t, testCase{
|
||||
desc: "ConfigEntry bootstrap cluster (camel-case)",
|
||||
args: []string{`-data-dir=` + dataDir},
|
||||
json: []string{`{
|
||||
"config_entries": {
|
||||
"bootstrap": [
|
||||
{
|
||||
"Kind": "cluster",
|
||||
"Name": "cluster",
|
||||
"Meta" : {
|
||||
"foo": "bar",
|
||||
"gir": "zim"
|
||||
},
|
||||
"TransparentProxy": {
|
||||
"CatalogDestinationsOnly": true
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}`,
|
||||
},
|
||||
hcl: []string{`
|
||||
config_entries {
|
||||
bootstrap {
|
||||
Kind = "cluster"
|
||||
Name = "cluster"
|
||||
Meta {
|
||||
"foo" = "bar"
|
||||
"gir" = "zim"
|
||||
}
|
||||
TransparentProxy {
|
||||
CatalogDestinationsOnly = true
|
||||
}
|
||||
}
|
||||
}
|
||||
`,
|
||||
},
|
||||
expected: func(rt *RuntimeConfig) {
|
||||
rt.DataDir = dataDir
|
||||
rt.ConfigEntryBootstrap = []structs.ConfigEntry{
|
||||
&structs.ClusterConfigEntry{
|
||||
Kind: "cluster",
|
||||
Name: "cluster",
|
||||
Meta: map[string]string{
|
||||
"foo": "bar",
|
||||
"gir": "zim",
|
||||
},
|
||||
EnterpriseMeta: *defaultEntMeta,
|
||||
TransparentProxy: structs.TransparentProxyClusterConfig{
|
||||
CatalogDestinationsOnly: true,
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
})
|
||||
|
||||
///////////////////////////////////
|
||||
// Defaults sanity checks
|
||||
|
|
|
@ -419,6 +419,16 @@ func TestFSM_SnapshotRestore_OSS(t *testing.T) {
|
|||
}
|
||||
require.NoError(t, fsm.state.EnsureConfigEntry(26, serviceIxn))
|
||||
|
||||
// cluster config entry
|
||||
clusterConfig := &structs.ClusterConfigEntry{
|
||||
Kind: structs.ClusterConfig,
|
||||
Name: structs.ClusterConfigCluster,
|
||||
TransparentProxy: structs.TransparentProxyClusterConfig{
|
||||
CatalogDestinationsOnly: true,
|
||||
},
|
||||
}
|
||||
require.NoError(t, fsm.state.EnsureConfigEntry(27, clusterConfig))
|
||||
|
||||
// Snapshot
|
||||
snap, err := fsm.Snapshot()
|
||||
require.NoError(t, err)
|
||||
|
@ -691,6 +701,11 @@ func TestFSM_SnapshotRestore_OSS(t *testing.T) {
|
|||
require.NoError(t, err)
|
||||
require.Equal(t, serviceIxn, serviceIxnEntry)
|
||||
|
||||
// Verify cluster config entry is restored
|
||||
_, clusterConfigEntry, err := fsm2.state.ConfigEntry(nil, structs.ClusterConfig, structs.ClusterConfigCluster, structs.DefaultEnterpriseMeta())
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, clusterConfig, clusterConfigEntry)
|
||||
|
||||
// Snapshot
|
||||
snap, err = fsm2.Snapshot()
|
||||
require.NoError(t, err)
|
||||
|
|
|
@ -362,6 +362,7 @@ func validateProposedConfigEntryInGraph(
|
|||
return err
|
||||
}
|
||||
case structs.ServiceIntentions:
|
||||
case structs.ClusterConfig:
|
||||
default:
|
||||
return fmt.Errorf("unhandled kind %q during validation of %q", kind, name)
|
||||
}
|
||||
|
|
|
@ -5,8 +5,9 @@ import (
|
|||
"fmt"
|
||||
"sort"
|
||||
|
||||
"github.com/hashicorp/consul/agent/structs"
|
||||
"github.com/mitchellh/copystructure"
|
||||
|
||||
"github.com/hashicorp/consul/agent/structs"
|
||||
)
|
||||
|
||||
// TODO(ingress): Can we think of a better for this bag of data?
|
||||
|
@ -59,6 +60,9 @@ type configSnapshotConnectProxy struct {
|
|||
// intentions.
|
||||
Intentions structs.Intentions
|
||||
IntentionsSet bool
|
||||
|
||||
ClusterConfig *structs.ClusterConfigEntry
|
||||
ClusterConfigSet bool
|
||||
}
|
||||
|
||||
func (c *configSnapshotConnectProxy) IsEmpty() bool {
|
||||
|
@ -75,7 +79,8 @@ func (c *configSnapshotConnectProxy) IsEmpty() bool {
|
|||
len(c.WatchedGatewayEndpoints) == 0 &&
|
||||
len(c.WatchedServiceChecks) == 0 &&
|
||||
len(c.PreparedQueryEndpoints) == 0 &&
|
||||
len(c.UpstreamConfig) == 0
|
||||
len(c.UpstreamConfig) == 0 &&
|
||||
!c.ClusterConfigSet
|
||||
}
|
||||
|
||||
type configSnapshotTerminatingGateway struct {
|
||||
|
@ -355,6 +360,9 @@ type ConfigSnapshot struct {
|
|||
func (s *ConfigSnapshot) Valid() bool {
|
||||
switch s.Kind {
|
||||
case structs.ServiceKindConnectProxy:
|
||||
if s.Proxy.TransparentProxy && !s.ConnectProxy.ClusterConfigSet {
|
||||
return false
|
||||
}
|
||||
return s.Roots != nil &&
|
||||
s.ConnectProxy.Leaf != nil &&
|
||||
s.ConnectProxy.IntentionsSet
|
||||
|
|
|
@ -46,6 +46,7 @@ const (
|
|||
serviceResolverIDPrefix = "service-resolver:"
|
||||
serviceIntentionsIDPrefix = "service-intentions:"
|
||||
intentionUpstreamsID = "intention-upstreams"
|
||||
clusterConfigEntryID = "cluster-config"
|
||||
svcChecksWatchIDPrefix = cachetype.ServiceHTTPChecksName + ":"
|
||||
serviceIDPrefix = string(structs.UpstreamDestTypeService) + ":"
|
||||
preparedQueryIDPrefix = string(structs.UpstreamDestTypePreparedQuery) + ":"
|
||||
|
@ -315,6 +316,17 @@ func (s *state) initWatchesConnectProxy(snap *ConfigSnapshot) error {
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = s.cache.Notify(s.ctx, cachetype.ConfigEntryName, &structs.ConfigEntryQuery{
|
||||
Kind: structs.ClusterConfig,
|
||||
Name: structs.ClusterConfigCluster,
|
||||
Datacenter: s.source.Datacenter,
|
||||
QueryOptions: structs.QueryOptions{Token: s.token},
|
||||
EnterpriseMeta: *structs.DefaultEnterpriseMeta(),
|
||||
}, clusterConfigEntryID, s.ch)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// Watch for updates to service endpoints for all upstreams
|
||||
|
@ -846,6 +858,24 @@ func (s *state) handleUpdateConnectProxy(u cache.UpdateEvent, snap *ConfigSnapsh
|
|||
}
|
||||
svcID := structs.ServiceIDFromString(strings.TrimPrefix(u.CorrelationID, svcChecksWatchIDPrefix))
|
||||
snap.ConnectProxy.WatchedServiceChecks[svcID] = resp
|
||||
|
||||
case u.CorrelationID == clusterConfigEntryID:
|
||||
resp, ok := u.Result.(*structs.ConfigEntryResponse)
|
||||
if !ok {
|
||||
return fmt.Errorf("invalid type for response: %T", u.Result)
|
||||
}
|
||||
|
||||
if resp.Entry != nil {
|
||||
clusterConf, ok := resp.Entry.(*structs.ClusterConfigEntry)
|
||||
if !ok {
|
||||
return fmt.Errorf("invalid type for config entry: %T", resp.Entry)
|
||||
}
|
||||
snap.ConnectProxy.ClusterConfig = clusterConf
|
||||
} else {
|
||||
snap.ConnectProxy.ClusterConfig = nil
|
||||
}
|
||||
snap.ConnectProxy.ClusterConfigSet = true
|
||||
|
||||
default:
|
||||
return s.handleUpdateUpstreams(u, &snap.ConnectProxy.ConfigSnapshotUpstreams)
|
||||
}
|
||||
|
|
|
@ -288,6 +288,18 @@ func genVerifyDiscoveryChainWatch(expected *structs.DiscoveryChainRequest) verif
|
|||
}
|
||||
}
|
||||
|
||||
func genVerifyClusterConfigWatch(expectedDatacenter string) verifyWatchRequest {
|
||||
return func(t testing.TB, cacheType string, request cache.Request) {
|
||||
require.Equal(t, cachetype.ConfigEntryName, cacheType)
|
||||
|
||||
reqReal, ok := request.(*structs.ConfigEntryQuery)
|
||||
require.True(t, ok)
|
||||
require.Equal(t, expectedDatacenter, reqReal.Datacenter)
|
||||
require.Equal(t, structs.ClusterConfigCluster, reqReal.Name)
|
||||
require.Equal(t, structs.ClusterConfig, reqReal.Kind)
|
||||
}
|
||||
}
|
||||
|
||||
func genVerifyGatewayWatch(expectedDatacenter string) verifyWatchRequest {
|
||||
return func(t testing.TB, cacheType string, request cache.Request) {
|
||||
require.Equal(t, cachetype.InternalServiceDumpName, cacheType)
|
||||
|
@ -1540,6 +1552,7 @@ func TestState_WatchesAndUpdates(t *testing.T) {
|
|||
"api", "", "dc1", false),
|
||||
leafWatchID: genVerifyLeafWatch("api", "dc1"),
|
||||
intentionsWatchID: genVerifyIntentionWatch("api", "dc1"),
|
||||
clusterConfigEntryID: genVerifyClusterConfigWatch("dc1"),
|
||||
},
|
||||
verifySnapshot: func(t testing.TB, snap *ConfigSnapshot) {
|
||||
require.False(t, snap.Valid(), "proxy without roots/leaf/intentions is not valid")
|
||||
|
@ -1562,6 +1575,13 @@ func TestState_WatchesAndUpdates(t *testing.T) {
|
|||
Result: TestIntentions(),
|
||||
Err: nil,
|
||||
},
|
||||
{
|
||||
CorrelationID: clusterConfigEntryID,
|
||||
Result: &structs.ConfigEntryResponse{
|
||||
Entry: nil, // no explicit config
|
||||
},
|
||||
Err: nil,
|
||||
},
|
||||
},
|
||||
verifySnapshot: func(t testing.TB, snap *ConfigSnapshot) {
|
||||
require.True(t, snap.Valid(), "proxy with roots/leaf/intentions is valid")
|
||||
|
@ -1571,6 +1591,8 @@ func TestState_WatchesAndUpdates(t *testing.T) {
|
|||
require.True(t, snap.MeshGateway.IsEmpty())
|
||||
require.True(t, snap.IngressGateway.IsEmpty())
|
||||
require.True(t, snap.TerminatingGateway.IsEmpty())
|
||||
require.True(t, snap.ConnectProxy.ClusterConfigSet)
|
||||
require.Nil(t, snap.ConnectProxy.ClusterConfig)
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -1596,6 +1618,7 @@ func TestState_WatchesAndUpdates(t *testing.T) {
|
|||
"api", "", "dc1", false),
|
||||
leafWatchID: genVerifyLeafWatch("api", "dc1"),
|
||||
intentionsWatchID: genVerifyIntentionWatch("api", "dc1"),
|
||||
clusterConfigEntryID: genVerifyClusterConfigWatch("dc1"),
|
||||
},
|
||||
verifySnapshot: func(t testing.TB, snap *ConfigSnapshot) {
|
||||
require.False(t, snap.Valid(), "proxy without roots/leaf/intentions is not valid")
|
||||
|
@ -1619,6 +1642,17 @@ func TestState_WatchesAndUpdates(t *testing.T) {
|
|||
Result: TestIntentions(),
|
||||
Err: nil,
|
||||
},
|
||||
{
|
||||
CorrelationID: clusterConfigEntryID,
|
||||
Result: &structs.ConfigEntryResponse{
|
||||
Entry: &structs.ClusterConfigEntry{
|
||||
Kind: structs.ClusterConfig,
|
||||
Name: structs.ClusterConfigCluster,
|
||||
TransparentProxy: structs.TransparentProxyClusterConfig{},
|
||||
},
|
||||
},
|
||||
Err: nil,
|
||||
},
|
||||
},
|
||||
verifySnapshot: func(t testing.TB, snap *ConfigSnapshot) {
|
||||
require.True(t, snap.Valid(), "proxy with roots/leaf/intentions is valid")
|
||||
|
@ -1628,6 +1662,8 @@ func TestState_WatchesAndUpdates(t *testing.T) {
|
|||
require.True(t, snap.MeshGateway.IsEmpty())
|
||||
require.True(t, snap.IngressGateway.IsEmpty())
|
||||
require.True(t, snap.TerminatingGateway.IsEmpty())
|
||||
require.True(t, snap.ConnectProxy.ClusterConfigSet)
|
||||
require.NotNil(t, snap.ConnectProxy.ClusterConfig)
|
||||
},
|
||||
},
|
||||
// Receiving an intention should lead to spinning up a discovery chain watch
|
||||
|
|
|
@ -26,8 +26,10 @@ const (
|
|||
IngressGateway string = "ingress-gateway"
|
||||
TerminatingGateway string = "terminating-gateway"
|
||||
ServiceIntentions string = "service-intentions"
|
||||
ClusterConfig string = "cluster"
|
||||
|
||||
ProxyConfigGlobal string = "global"
|
||||
ClusterConfigCluster string = "cluster"
|
||||
|
||||
DefaultServiceProtocol = "tcp"
|
||||
)
|
||||
|
@ -41,6 +43,7 @@ var AllConfigEntryKinds = []string{
|
|||
IngressGateway,
|
||||
TerminatingGateway,
|
||||
ServiceIntentions,
|
||||
ClusterConfig,
|
||||
}
|
||||
|
||||
// ConfigEntry is the interface for centralized configuration stored in Raft.
|
||||
|
@ -496,6 +499,8 @@ func MakeConfigEntry(kind, name string) (ConfigEntry, error) {
|
|||
return &TerminatingGatewayConfigEntry{Name: name}, nil
|
||||
case ServiceIntentions:
|
||||
return &ServiceIntentionsConfigEntry{Name: name}, nil
|
||||
case ClusterConfig:
|
||||
return &ClusterConfigEntry{Name: name}, nil
|
||||
default:
|
||||
return nil, fmt.Errorf("invalid config entry kind: %s", kind)
|
||||
}
|
||||
|
|
|
@ -0,0 +1,102 @@
|
|||
package structs
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/hashicorp/consul/acl"
|
||||
)
|
||||
|
||||
type ClusterConfigEntry struct {
|
||||
Kind string
|
||||
Name string
|
||||
|
||||
// TransparentProxy contains cluster-wide options pertaining to TPROXY mode
|
||||
// when enabled.
|
||||
TransparentProxy TransparentProxyClusterConfig `alias:"transparent_proxy"`
|
||||
|
||||
Meta map[string]string `json:",omitempty"`
|
||||
EnterpriseMeta `hcl:",squash" mapstructure:",squash"`
|
||||
RaftIndex
|
||||
}
|
||||
|
||||
// TransparentProxyClusterConfig contains cluster-wide options pertaining to
|
||||
// TPROXY mode when enabled.
|
||||
type TransparentProxyClusterConfig struct {
|
||||
// CatalogDestinationsOnly can be used to disable the pass-through that
|
||||
// allows traffic to destinations outside of the mesh.
|
||||
CatalogDestinationsOnly bool `alias:"catalog_destinations_only"`
|
||||
}
|
||||
|
||||
func (e *ClusterConfigEntry) GetKind() string {
|
||||
return ClusterConfig
|
||||
}
|
||||
|
||||
func (e *ClusterConfigEntry) GetName() string {
|
||||
if e == nil {
|
||||
return ""
|
||||
}
|
||||
|
||||
return e.Name
|
||||
}
|
||||
|
||||
func (e *ClusterConfigEntry) GetMeta() map[string]string {
|
||||
if e == nil {
|
||||
return nil
|
||||
}
|
||||
return e.Meta
|
||||
}
|
||||
|
||||
func (e *ClusterConfigEntry) Normalize() error {
|
||||
if e == nil {
|
||||
return fmt.Errorf("config entry is nil")
|
||||
}
|
||||
|
||||
e.Kind = ClusterConfig
|
||||
e.Name = ClusterConfigCluster
|
||||
|
||||
e.EnterpriseMeta.Normalize()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (e *ClusterConfigEntry) Validate() error {
|
||||
if e == nil {
|
||||
return fmt.Errorf("config entry is nil")
|
||||
}
|
||||
|
||||
if e.Name != ClusterConfigCluster {
|
||||
return fmt.Errorf("invalid name (%q), only %q is supported", e.Name, ClusterConfigCluster)
|
||||
}
|
||||
|
||||
if err := validateConfigEntryMeta(e.Meta); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return e.validateEnterpriseMeta()
|
||||
}
|
||||
|
||||
func (e *ClusterConfigEntry) CanRead(authz acl.Authorizer) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (e *ClusterConfigEntry) CanWrite(authz acl.Authorizer) bool {
|
||||
var authzContext acl.AuthorizerContext
|
||||
e.FillAuthzContext(&authzContext)
|
||||
return authz.OperatorWrite(&authzContext) == acl.Allow
|
||||
}
|
||||
|
||||
func (e *ClusterConfigEntry) GetRaftIndex() *RaftIndex {
|
||||
if e == nil {
|
||||
return &RaftIndex{}
|
||||
}
|
||||
|
||||
return &e.RaftIndex
|
||||
}
|
||||
|
||||
func (e *ClusterConfigEntry) GetEnterpriseMeta() *EnterpriseMeta {
|
||||
if e == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
return &e.EnterpriseMeta
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
package structs
|
||||
|
||||
func (e *ClusterConfigEntry) validateEnterpriseMeta() error {
|
||||
return nil
|
||||
}
|
|
@ -1300,6 +1300,42 @@ func TestDecodeConfigEntry(t *testing.T) {
|
|||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "cluster",
|
||||
snake: `
|
||||
kind = "cluster"
|
||||
name = "cluster"
|
||||
meta {
|
||||
"foo" = "bar"
|
||||
"gir" = "zim"
|
||||
}
|
||||
transparent_proxy {
|
||||
catalog_destinations_only = true
|
||||
}
|
||||
`,
|
||||
camel: `
|
||||
Kind = "cluster"
|
||||
Name = "cluster"
|
||||
Meta {
|
||||
"foo" = "bar"
|
||||
"gir" = "zim"
|
||||
}
|
||||
TransparentProxy {
|
||||
CatalogDestinationsOnly = true
|
||||
}
|
||||
`,
|
||||
expect: &ClusterConfigEntry{
|
||||
Kind: "cluster",
|
||||
Name: "cluster",
|
||||
Meta: map[string]string{
|
||||
"foo": "bar",
|
||||
"gir": "zim",
|
||||
},
|
||||
TransparentProxy: TransparentProxyClusterConfig{
|
||||
CatalogDestinationsOnly: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
} {
|
||||
tc := tc
|
||||
|
||||
|
|
|
@ -223,6 +223,9 @@ func (s *Server) listenersFromSnapshotConnectProxy(cInfo connectionInfo, cfgSnap
|
|||
})
|
||||
|
||||
// Add a catch-all filter chain that acts as a TCP proxy to non-catalog destinations
|
||||
if cfgSnap.ConnectProxy.ClusterConfig == nil ||
|
||||
!cfgSnap.ConnectProxy.ClusterConfig.TransparentProxy.CatalogDestinationsOnly {
|
||||
|
||||
filterChain, err := s.makeUpstreamFilterChainForDiscoveryChain(
|
||||
"passthrough",
|
||||
OriginalDestinationClusterName,
|
||||
|
@ -236,6 +239,7 @@ func (s *Server) listenersFromSnapshotConnectProxy(cInfo connectionInfo, cfgSnap
|
|||
return nil, err
|
||||
}
|
||||
outboundListener.FilterChains = append(outboundListener.FilterChains, filterChain)
|
||||
}
|
||||
|
||||
resources = append(resources, outboundListener)
|
||||
}
|
||||
|
|
|
@ -2,7 +2,16 @@ package xds
|
|||
|
||||
import (
|
||||
"bytes"
|
||||
"path/filepath"
|
||||
"sort"
|
||||
"testing"
|
||||
"text/template"
|
||||
"time"
|
||||
|
||||
envoy_listener_v3 "github.com/envoyproxy/go-control-plane/envoy/config/listener/v3"
|
||||
testinf "github.com/mitchellh/go-testing-interface"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/hashicorp/consul/agent/connect"
|
||||
"github.com/hashicorp/consul/agent/consul/discoverychain"
|
||||
"github.com/hashicorp/consul/agent/proxycfg"
|
||||
|
@ -10,13 +19,6 @@ import (
|
|||
"github.com/hashicorp/consul/agent/xds/proxysupport"
|
||||
"github.com/hashicorp/consul/sdk/testutil"
|
||||
"github.com/hashicorp/consul/types"
|
||||
testinf "github.com/mitchellh/go-testing-interface"
|
||||
"github.com/stretchr/testify/require"
|
||||
"path/filepath"
|
||||
"sort"
|
||||
"testing"
|
||||
"text/template"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestListenersFromSnapshot(t *testing.T) {
|
||||
|
@ -481,6 +483,48 @@ func TestListenersFromSnapshot(t *testing.T) {
|
|||
setup: func(snap *proxycfg.ConfigSnapshot) {
|
||||
snap.Proxy.TransparentProxy = true
|
||||
|
||||
snap.ConnectProxy.ClusterConfigSet = true
|
||||
|
||||
// DiscoveryChain without an UpstreamConfig should yield a filter chain when in TransparentProxy mode
|
||||
snap.ConnectProxy.DiscoveryChain["google"] = discoverychain.TestCompileConfigEntries(
|
||||
t, "google", "default", "dc1",
|
||||
connect.TestClusterID+".consul", "dc1", nil)
|
||||
snap.ConnectProxy.WatchedUpstreamEndpoints["google"] = map[string]structs.CheckServiceNodes{
|
||||
"google.default.dc1": {
|
||||
structs.CheckServiceNode{
|
||||
Node: &structs.Node{
|
||||
Address: "8.8.8.8",
|
||||
Datacenter: "dc1",
|
||||
},
|
||||
Service: &structs.NodeService{
|
||||
Service: "google",
|
||||
Port: 9090,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
// DiscoveryChains without endpoints do not get a filter chain because there are no addresses to match on.
|
||||
snap.ConnectProxy.DiscoveryChain["no-endpoints"] = discoverychain.TestCompileConfigEntries(
|
||||
t, "no-endpoints", "default", "dc1",
|
||||
connect.TestClusterID+".consul", "dc1", nil)
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "transparent-proxy-catalog-destinations-only",
|
||||
create: proxycfg.TestConfigSnapshot,
|
||||
setup: func(snap *proxycfg.ConfigSnapshot) {
|
||||
snap.Proxy.TransparentProxy = true
|
||||
|
||||
snap.ConnectProxy.ClusterConfigSet = true
|
||||
snap.ConnectProxy.ClusterConfig = &structs.ClusterConfigEntry{
|
||||
Kind: structs.ClusterConfig,
|
||||
Name: structs.ClusterConfigCluster,
|
||||
TransparentProxy: structs.TransparentProxyClusterConfig{
|
||||
CatalogDestinationsOnly: true,
|
||||
},
|
||||
}
|
||||
|
||||
// DiscoveryChain without an UpstreamConfig should yield a filter chain when in TransparentProxy mode
|
||||
snap.ConnectProxy.DiscoveryChain["google"] = discoverychain.TestCompileConfigEntries(
|
||||
t, "google", "default", "dc1",
|
||||
|
|
157
agent/xds/testdata/listeners/transparent-proxy-catalog-destinations-only.envoy-1-17-x.golden
vendored
Normal file
157
agent/xds/testdata/listeners/transparent-proxy-catalog-destinations-only.envoy-1-17-x.golden
vendored
Normal file
|
@ -0,0 +1,157 @@
|
|||
{
|
||||
"versionInfo": "00000001",
|
||||
"resources": [
|
||||
{
|
||||
"@type": "type.googleapis.com/envoy.config.listener.v3.Listener",
|
||||
"name": "db:127.0.0.1:9191",
|
||||
"address": {
|
||||
"socketAddress": {
|
||||
"address": "127.0.0.1",
|
||||
"portValue": 9191
|
||||
}
|
||||
},
|
||||
"filterChains": [
|
||||
{
|
||||
"filters": [
|
||||
{
|
||||
"name": "envoy.filters.network.tcp_proxy",
|
||||
"typedConfig": {
|
||||
"@type": "type.googleapis.com/envoy.extensions.filters.network.tcp_proxy.v3.TcpProxy",
|
||||
"statPrefix": "upstream.db.default.dc1",
|
||||
"cluster": "db.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"trafficDirection": "OUTBOUND"
|
||||
},
|
||||
{
|
||||
"@type": "type.googleapis.com/envoy.config.listener.v3.Listener",
|
||||
"name": "outbound_listener:127.0.0.1:15001",
|
||||
"address": {
|
||||
"socketAddress": {
|
||||
"address": "127.0.0.1",
|
||||
"portValue": 15001
|
||||
}
|
||||
},
|
||||
"filterChains": [
|
||||
{
|
||||
"filterChainMatch": {
|
||||
"prefixRanges": [
|
||||
{
|
||||
"addressPrefix": "8.8.8.8",
|
||||
"prefixLen": 32
|
||||
}
|
||||
]
|
||||
},
|
||||
"filters": [
|
||||
{
|
||||
"name": "envoy.filters.network.tcp_proxy",
|
||||
"typedConfig": {
|
||||
"@type": "type.googleapis.com/envoy.extensions.filters.network.tcp_proxy.v3.TcpProxy",
|
||||
"statPrefix": "upstream.google.default.dc1",
|
||||
"cluster": "google.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"listenerFilters": [
|
||||
{
|
||||
"name": "envoy.filters.listener.original_dst"
|
||||
}
|
||||
],
|
||||
"trafficDirection": "OUTBOUND"
|
||||
},
|
||||
{
|
||||
"@type": "type.googleapis.com/envoy.config.listener.v3.Listener",
|
||||
"name": "prepared_query:geo-cache:127.10.10.10:8181",
|
||||
"address": {
|
||||
"socketAddress": {
|
||||
"address": "127.10.10.10",
|
||||
"portValue": 8181
|
||||
}
|
||||
},
|
||||
"filterChains": [
|
||||
{
|
||||
"filters": [
|
||||
{
|
||||
"name": "envoy.filters.network.tcp_proxy",
|
||||
"typedConfig": {
|
||||
"@type": "type.googleapis.com/envoy.extensions.filters.network.tcp_proxy.v3.TcpProxy",
|
||||
"statPrefix": "upstream.prepared_query_geo-cache",
|
||||
"cluster": "geo-cache.default.dc1.query.11111111-2222-3333-4444-555555555555.consul"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"trafficDirection": "OUTBOUND"
|
||||
},
|
||||
{
|
||||
"@type": "type.googleapis.com/envoy.config.listener.v3.Listener",
|
||||
"name": "public_listener:0.0.0.0:9999",
|
||||
"address": {
|
||||
"socketAddress": {
|
||||
"address": "0.0.0.0",
|
||||
"portValue": 9999
|
||||
}
|
||||
},
|
||||
"filterChains": [
|
||||
{
|
||||
"filters": [
|
||||
{
|
||||
"name": "envoy.filters.network.rbac",
|
||||
"typedConfig": {
|
||||
"@type": "type.googleapis.com/envoy.extensions.filters.network.rbac.v3.RBAC",
|
||||
"rules": {
|
||||
|
||||
},
|
||||
"statPrefix": "connect_authz"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "envoy.filters.network.tcp_proxy",
|
||||
"typedConfig": {
|
||||
"@type": "type.googleapis.com/envoy.extensions.filters.network.tcp_proxy.v3.TcpProxy",
|
||||
"statPrefix": "public_listener",
|
||||
"cluster": "local_app"
|
||||
}
|
||||
}
|
||||
],
|
||||
"transportSocket": {
|
||||
"name": "tls",
|
||||
"typedConfig": {
|
||||
"@type": "type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.DownstreamTlsContext",
|
||||
"commonTlsContext": {
|
||||
"tlsParams": {
|
||||
|
||||
},
|
||||
"tlsCertificates": [
|
||||
{
|
||||
"certificateChain": {
|
||||
"inlineString": "-----BEGIN CERTIFICATE-----\nMIICjDCCAjKgAwIBAgIIC5llxGV1gB8wCgYIKoZIzj0EAwIwFDESMBAGA1UEAxMJ\nVGVzdCBDQSAyMB4XDTE5MDMyMjEzNTgyNloXDTI5MDMyMjEzNTgyNlowDjEMMAoG\nA1UEAxMDd2ViMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEADPv1RHVNRfa2VKR\nAB16b6rZnEt7tuhaxCFpQXPj7M2omb0B9Favq5E0ivpNtv1QnFhxtPd7d5k4e+T7\nSkW1TaOCAXIwggFuMA4GA1UdDwEB/wQEAwIDuDAdBgNVHSUEFjAUBggrBgEFBQcD\nAgYIKwYBBQUHAwEwDAYDVR0TAQH/BAIwADBoBgNVHQ4EYQRfN2Q6MDc6ODc6M2E6\nNDA6MTk6NDc6YzM6NWE6YzA6YmE6NjI6ZGY6YWY6NGI6ZDQ6MDU6MjU6NzY6M2Q6\nNWE6OGQ6MTY6OGQ6Njc6NWU6MmU6YTA6MzQ6N2Q6ZGM6ZmYwagYDVR0jBGMwYYBf\nZDE6MTE6MTE6YWM6MmE6YmE6OTc6YjI6M2Y6YWM6N2I6YmQ6ZGE6YmU6YjE6OGE6\nZmM6OWE6YmE6YjU6YmM6ODM6ZTc6NWU6NDE6NmY6ZjI6NzM6OTU6NTg6MGM6ZGIw\nWQYDVR0RBFIwUIZOc3BpZmZlOi8vMTExMTExMTEtMjIyMi0zMzMzLTQ0NDQtNTU1\nNTU1NTU1NTU1LmNvbnN1bC9ucy9kZWZhdWx0L2RjL2RjMS9zdmMvd2ViMAoGCCqG\nSM49BAMCA0gAMEUCIGC3TTvvjj76KMrguVyFf4tjOqaSCRie3nmHMRNNRav7AiEA\npY0heYeK9A6iOLrzqxSerkXXQyj5e9bE4VgUnxgPU6g=\n-----END CERTIFICATE-----\n"
|
||||
},
|
||||
"privateKey": {
|
||||
"inlineString": "-----BEGIN EC PRIVATE KEY-----\nMHcCAQEEIMoTkpRggp3fqZzFKh82yS4LjtJI+XY+qX/7DefHFrtdoAoGCCqGSM49\nAwEHoUQDQgAEADPv1RHVNRfa2VKRAB16b6rZnEt7tuhaxCFpQXPj7M2omb0B9Fav\nq5E0ivpNtv1QnFhxtPd7d5k4e+T7SkW1TQ==\n-----END EC PRIVATE KEY-----\n"
|
||||
}
|
||||
}
|
||||
],
|
||||
"validationContext": {
|
||||
"trustedCa": {
|
||||
"inlineString": "-----BEGIN CERTIFICATE-----\nMIICXDCCAgKgAwIBAgIICpZq70Z9LyUwCgYIKoZIzj0EAwIwFDESMBAGA1UEAxMJ\nVGVzdCBDQSAyMB4XDTE5MDMyMjEzNTgyNloXDTI5MDMyMjEzNTgyNlowFDESMBAG\nA1UEAxMJVGVzdCBDQSAyMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEIhywH1gx\nAsMwuF3ukAI5YL2jFxH6Usnma1HFSfVyxbXX1/uoZEYrj8yCAtdU2yoHETyd+Zx2\nThhRLP79pYegCaOCATwwggE4MA4GA1UdDwEB/wQEAwIBhjAPBgNVHRMBAf8EBTAD\nAQH/MGgGA1UdDgRhBF9kMToxMToxMTphYzoyYTpiYTo5NzpiMjozZjphYzo3Yjpi\nZDpkYTpiZTpiMTo4YTpmYzo5YTpiYTpiNTpiYzo4MzplNzo1ZTo0MTo2ZjpmMjo3\nMzo5NTo1ODowYzpkYjBqBgNVHSMEYzBhgF9kMToxMToxMTphYzoyYTpiYTo5Nzpi\nMjozZjphYzo3YjpiZDpkYTpiZTpiMTo4YTpmYzo5YTpiYTpiNTpiYzo4MzplNzo1\nZTo0MTo2ZjpmMjo3Mzo5NTo1ODowYzpkYjA/BgNVHREEODA2hjRzcGlmZmU6Ly8x\nMTExMTExMS0yMjIyLTMzMzMtNDQ0NC01NTU1NTU1NTU1NTUuY29uc3VsMAoGCCqG\nSM49BAMCA0gAMEUCICOY0i246rQHJt8o8Oya0D5PLL1FnmsQmQqIGCi31RwnAiEA\noR5f6Ku+cig2Il8T8LJujOp2/2A72QcHZA57B13y+8o=\n-----END CERTIFICATE-----\n"
|
||||
}
|
||||
}
|
||||
},
|
||||
"requireClientCertificate": true
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"trafficDirection": "INBOUND"
|
||||
}
|
||||
],
|
||||
"typeUrl": "type.googleapis.com/envoy.config.listener.v3.Listener",
|
||||
"nonce": "00000001"
|
||||
}
|
|
@ -0,0 +1,157 @@
|
|||
{
|
||||
"versionInfo": "00000001",
|
||||
"resources": [
|
||||
{
|
||||
"@type": "type.googleapis.com/envoy.api.v2.Listener",
|
||||
"name": "db:127.0.0.1:9191",
|
||||
"address": {
|
||||
"socketAddress": {
|
||||
"address": "127.0.0.1",
|
||||
"portValue": 9191
|
||||
}
|
||||
},
|
||||
"filterChains": [
|
||||
{
|
||||
"filters": [
|
||||
{
|
||||
"name": "envoy.filters.network.tcp_proxy",
|
||||
"typedConfig": {
|
||||
"@type": "type.googleapis.com/envoy.config.filter.network.tcp_proxy.v2.TcpProxy",
|
||||
"statPrefix": "upstream.db.default.dc1",
|
||||
"cluster": "db.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"trafficDirection": "OUTBOUND"
|
||||
},
|
||||
{
|
||||
"@type": "type.googleapis.com/envoy.api.v2.Listener",
|
||||
"name": "outbound_listener:127.0.0.1:15001",
|
||||
"address": {
|
||||
"socketAddress": {
|
||||
"address": "127.0.0.1",
|
||||
"portValue": 15001
|
||||
}
|
||||
},
|
||||
"filterChains": [
|
||||
{
|
||||
"filterChainMatch": {
|
||||
"prefixRanges": [
|
||||
{
|
||||
"addressPrefix": "8.8.8.8",
|
||||
"prefixLen": 32
|
||||
}
|
||||
]
|
||||
},
|
||||
"filters": [
|
||||
{
|
||||
"name": "envoy.filters.network.tcp_proxy",
|
||||
"typedConfig": {
|
||||
"@type": "type.googleapis.com/envoy.config.filter.network.tcp_proxy.v2.TcpProxy",
|
||||
"statPrefix": "upstream.google.default.dc1",
|
||||
"cluster": "google.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"listenerFilters": [
|
||||
{
|
||||
"name": "envoy.filters.listener.original_dst"
|
||||
}
|
||||
],
|
||||
"trafficDirection": "OUTBOUND"
|
||||
},
|
||||
{
|
||||
"@type": "type.googleapis.com/envoy.api.v2.Listener",
|
||||
"name": "prepared_query:geo-cache:127.10.10.10:8181",
|
||||
"address": {
|
||||
"socketAddress": {
|
||||
"address": "127.10.10.10",
|
||||
"portValue": 8181
|
||||
}
|
||||
},
|
||||
"filterChains": [
|
||||
{
|
||||
"filters": [
|
||||
{
|
||||
"name": "envoy.filters.network.tcp_proxy",
|
||||
"typedConfig": {
|
||||
"@type": "type.googleapis.com/envoy.config.filter.network.tcp_proxy.v2.TcpProxy",
|
||||
"statPrefix": "upstream.prepared_query_geo-cache",
|
||||
"cluster": "geo-cache.default.dc1.query.11111111-2222-3333-4444-555555555555.consul"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"trafficDirection": "OUTBOUND"
|
||||
},
|
||||
{
|
||||
"@type": "type.googleapis.com/envoy.api.v2.Listener",
|
||||
"name": "public_listener:0.0.0.0:9999",
|
||||
"address": {
|
||||
"socketAddress": {
|
||||
"address": "0.0.0.0",
|
||||
"portValue": 9999
|
||||
}
|
||||
},
|
||||
"filterChains": [
|
||||
{
|
||||
"filters": [
|
||||
{
|
||||
"name": "envoy.filters.network.rbac",
|
||||
"typedConfig": {
|
||||
"@type": "type.googleapis.com/envoy.config.filter.network.rbac.v2.RBAC",
|
||||
"rules": {
|
||||
|
||||
},
|
||||
"statPrefix": "connect_authz"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "envoy.filters.network.tcp_proxy",
|
||||
"typedConfig": {
|
||||
"@type": "type.googleapis.com/envoy.config.filter.network.tcp_proxy.v2.TcpProxy",
|
||||
"statPrefix": "public_listener",
|
||||
"cluster": "local_app"
|
||||
}
|
||||
}
|
||||
],
|
||||
"transportSocket": {
|
||||
"name": "tls",
|
||||
"typedConfig": {
|
||||
"@type": "type.googleapis.com/envoy.api.v2.auth.DownstreamTlsContext",
|
||||
"commonTlsContext": {
|
||||
"tlsParams": {
|
||||
|
||||
},
|
||||
"tlsCertificates": [
|
||||
{
|
||||
"certificateChain": {
|
||||
"inlineString": "-----BEGIN CERTIFICATE-----\nMIICjDCCAjKgAwIBAgIIC5llxGV1gB8wCgYIKoZIzj0EAwIwFDESMBAGA1UEAxMJ\nVGVzdCBDQSAyMB4XDTE5MDMyMjEzNTgyNloXDTI5MDMyMjEzNTgyNlowDjEMMAoG\nA1UEAxMDd2ViMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEADPv1RHVNRfa2VKR\nAB16b6rZnEt7tuhaxCFpQXPj7M2omb0B9Favq5E0ivpNtv1QnFhxtPd7d5k4e+T7\nSkW1TaOCAXIwggFuMA4GA1UdDwEB/wQEAwIDuDAdBgNVHSUEFjAUBggrBgEFBQcD\nAgYIKwYBBQUHAwEwDAYDVR0TAQH/BAIwADBoBgNVHQ4EYQRfN2Q6MDc6ODc6M2E6\nNDA6MTk6NDc6YzM6NWE6YzA6YmE6NjI6ZGY6YWY6NGI6ZDQ6MDU6MjU6NzY6M2Q6\nNWE6OGQ6MTY6OGQ6Njc6NWU6MmU6YTA6MzQ6N2Q6ZGM6ZmYwagYDVR0jBGMwYYBf\nZDE6MTE6MTE6YWM6MmE6YmE6OTc6YjI6M2Y6YWM6N2I6YmQ6ZGE6YmU6YjE6OGE6\nZmM6OWE6YmE6YjU6YmM6ODM6ZTc6NWU6NDE6NmY6ZjI6NzM6OTU6NTg6MGM6ZGIw\nWQYDVR0RBFIwUIZOc3BpZmZlOi8vMTExMTExMTEtMjIyMi0zMzMzLTQ0NDQtNTU1\nNTU1NTU1NTU1LmNvbnN1bC9ucy9kZWZhdWx0L2RjL2RjMS9zdmMvd2ViMAoGCCqG\nSM49BAMCA0gAMEUCIGC3TTvvjj76KMrguVyFf4tjOqaSCRie3nmHMRNNRav7AiEA\npY0heYeK9A6iOLrzqxSerkXXQyj5e9bE4VgUnxgPU6g=\n-----END CERTIFICATE-----\n"
|
||||
},
|
||||
"privateKey": {
|
||||
"inlineString": "-----BEGIN EC PRIVATE KEY-----\nMHcCAQEEIMoTkpRggp3fqZzFKh82yS4LjtJI+XY+qX/7DefHFrtdoAoGCCqGSM49\nAwEHoUQDQgAEADPv1RHVNRfa2VKRAB16b6rZnEt7tuhaxCFpQXPj7M2omb0B9Fav\nq5E0ivpNtv1QnFhxtPd7d5k4e+T7SkW1TQ==\n-----END EC PRIVATE KEY-----\n"
|
||||
}
|
||||
}
|
||||
],
|
||||
"validationContext": {
|
||||
"trustedCa": {
|
||||
"inlineString": "-----BEGIN CERTIFICATE-----\nMIICXDCCAgKgAwIBAgIICpZq70Z9LyUwCgYIKoZIzj0EAwIwFDESMBAGA1UEAxMJ\nVGVzdCBDQSAyMB4XDTE5MDMyMjEzNTgyNloXDTI5MDMyMjEzNTgyNlowFDESMBAG\nA1UEAxMJVGVzdCBDQSAyMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEIhywH1gx\nAsMwuF3ukAI5YL2jFxH6Usnma1HFSfVyxbXX1/uoZEYrj8yCAtdU2yoHETyd+Zx2\nThhRLP79pYegCaOCATwwggE4MA4GA1UdDwEB/wQEAwIBhjAPBgNVHRMBAf8EBTAD\nAQH/MGgGA1UdDgRhBF9kMToxMToxMTphYzoyYTpiYTo5NzpiMjozZjphYzo3Yjpi\nZDpkYTpiZTpiMTo4YTpmYzo5YTpiYTpiNTpiYzo4MzplNzo1ZTo0MTo2ZjpmMjo3\nMzo5NTo1ODowYzpkYjBqBgNVHSMEYzBhgF9kMToxMToxMTphYzoyYTpiYTo5Nzpi\nMjozZjphYzo3YjpiZDpkYTpiZTpiMTo4YTpmYzo5YTpiYTpiNTpiYzo4MzplNzo1\nZTo0MTo2ZjpmMjo3Mzo5NTo1ODowYzpkYjA/BgNVHREEODA2hjRzcGlmZmU6Ly8x\nMTExMTExMS0yMjIyLTMzMzMtNDQ0NC01NTU1NTU1NTU1NTUuY29uc3VsMAoGCCqG\nSM49BAMCA0gAMEUCICOY0i246rQHJt8o8Oya0D5PLL1FnmsQmQqIGCi31RwnAiEA\noR5f6Ku+cig2Il8T8LJujOp2/2A72QcHZA57B13y+8o=\n-----END CERTIFICATE-----\n"
|
||||
}
|
||||
}
|
||||
},
|
||||
"requireClientCertificate": true
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"trafficDirection": "INBOUND"
|
||||
}
|
||||
],
|
||||
"typeUrl": "type.googleapis.com/envoy.api.v2.Listener",
|
||||
"nonce": "00000001"
|
||||
}
|
|
@ -21,8 +21,10 @@ const (
|
|||
IngressGateway string = "ingress-gateway"
|
||||
TerminatingGateway string = "terminating-gateway"
|
||||
ServiceIntentions string = "service-intentions"
|
||||
ClusterConfig string = "cluster"
|
||||
|
||||
ProxyConfigGlobal string = "global"
|
||||
ClusterConfigCluster string = "cluster"
|
||||
)
|
||||
|
||||
type ConfigEntry interface {
|
||||
|
@ -260,6 +262,8 @@ func makeConfigEntry(kind, name string) (ConfigEntry, error) {
|
|||
return &TerminatingGatewayConfigEntry{Kind: kind, Name: name}, nil
|
||||
case ServiceIntentions:
|
||||
return &ServiceIntentionsConfigEntry{Kind: kind, Name: name}, nil
|
||||
case ClusterConfig:
|
||||
return &ClusterConfigEntry{Kind: kind, Name: name}, nil
|
||||
default:
|
||||
return nil, fmt.Errorf("invalid config entry kind: %s", kind)
|
||||
}
|
||||
|
|
|
@ -0,0 +1,39 @@
|
|||
package api
|
||||
|
||||
type ClusterConfigEntry struct {
|
||||
Kind string
|
||||
Name string
|
||||
Namespace string `json:",omitempty"`
|
||||
TransparentProxy TransparentProxyClusterConfig `alias:"transparent_proxy"`
|
||||
Meta map[string]string `json:",omitempty"`
|
||||
CreateIndex uint64
|
||||
ModifyIndex uint64
|
||||
}
|
||||
|
||||
type TransparentProxyClusterConfig struct {
|
||||
CatalogDestinationsOnly bool `alias:"catalog_destinations_only"`
|
||||
}
|
||||
|
||||
func (e *ClusterConfigEntry) GetKind() string {
|
||||
return e.Kind
|
||||
}
|
||||
|
||||
func (e *ClusterConfigEntry) GetName() string {
|
||||
return e.Name
|
||||
}
|
||||
|
||||
func (e *ClusterConfigEntry) GetNamespace() string {
|
||||
return e.Namespace
|
||||
}
|
||||
|
||||
func (e *ClusterConfigEntry) GetMeta() map[string]string {
|
||||
return e.Meta
|
||||
}
|
||||
|
||||
func (e *ClusterConfigEntry) GetCreateIndex() uint64 {
|
||||
return e.CreateIndex
|
||||
}
|
||||
|
||||
func (e *ClusterConfigEntry) GetModifyIndex() uint64 {
|
||||
return e.ModifyIndex
|
||||
}
|
|
@ -1124,6 +1124,33 @@ func TestDecodeConfigEntry(t *testing.T) {
|
|||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "cluster",
|
||||
body: `
|
||||
{
|
||||
"Kind": "cluster",
|
||||
"Name": "cluster",
|
||||
"Meta" : {
|
||||
"foo": "bar",
|
||||
"gir": "zim"
|
||||
},
|
||||
"TransparentProxy": {
|
||||
"CatalogDestinationsOnly": true
|
||||
}
|
||||
}
|
||||
`,
|
||||
expect: &ClusterConfigEntry{
|
||||
Kind: "cluster",
|
||||
Name: "cluster",
|
||||
Meta: map[string]string{
|
||||
"foo": "bar",
|
||||
"gir": "zim",
|
||||
},
|
||||
TransparentProxy: TransparentProxyClusterConfig{
|
||||
CatalogDestinationsOnly: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
} {
|
||||
tc := tc
|
||||
|
||||
|
|
|
@ -8,11 +8,12 @@ import (
|
|||
|
||||
"github.com/hashicorp/consul/agent/structs"
|
||||
|
||||
"github.com/mitchellh/cli"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/hashicorp/consul/agent"
|
||||
"github.com/hashicorp/consul/api"
|
||||
"github.com/hashicorp/consul/sdk/testutil"
|
||||
"github.com/mitchellh/cli"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestConfigWrite_noTabs(t *testing.T) {
|
||||
|
@ -2534,6 +2535,68 @@ func TestParseConfigEntry(t *testing.T) {
|
|||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "cluster",
|
||||
snake: `
|
||||
kind = "cluster"
|
||||
name = "cluster"
|
||||
meta {
|
||||
"foo" = "bar"
|
||||
"gir" = "zim"
|
||||
}
|
||||
transparent_proxy {
|
||||
catalog_destinations_only = true
|
||||
}
|
||||
`,
|
||||
camel: `
|
||||
Kind = "cluster"
|
||||
Name = "cluster"
|
||||
Meta {
|
||||
"foo" = "bar"
|
||||
"gir" = "zim"
|
||||
}
|
||||
TransparentProxy {
|
||||
CatalogDestinationsOnly = true
|
||||
}
|
||||
`,
|
||||
snakeJSON: `
|
||||
{
|
||||
"kind": "cluster",
|
||||
"name": "cluster",
|
||||
"meta" : {
|
||||
"foo": "bar",
|
||||
"gir": "zim"
|
||||
},
|
||||
"transparent_proxy": {
|
||||
"catalog_destinations_only": true
|
||||
}
|
||||
}
|
||||
`,
|
||||
camelJSON: `
|
||||
{
|
||||
"Kind": "cluster",
|
||||
"Name": "cluster",
|
||||
"Meta" : {
|
||||
"foo": "bar",
|
||||
"gir": "zim"
|
||||
},
|
||||
"TransparentProxy": {
|
||||
"CatalogDestinationsOnly": true
|
||||
}
|
||||
}
|
||||
`,
|
||||
expect: &api.ClusterConfigEntry{
|
||||
Kind: "cluster",
|
||||
Name: "cluster",
|
||||
Meta: map[string]string{
|
||||
"foo": "bar",
|
||||
"gir": "zim",
|
||||
},
|
||||
TransparentProxy: api.TransparentProxyClusterConfig{
|
||||
CatalogDestinationsOnly: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
} {
|
||||
tc := tc
|
||||
|
||||
|
|
Loading…
Reference in New Issue