Connect: allow configuring Envoy for L7 Observability (#5558)
* Add support for HTTP proxy listeners * Add customizable bootstrap configuration options * Debug logging for xDS AuthZ * Add Envoy Integration test suite with basic test coverage * Add envoy command tests to cover new cases * Add tracing integration test * Add gRPC support WIP * Merged changes from master Docker. get CI integration to work with same Dockerfile now * Make docker build optional for integration * Enable integration tests again! * http2 and grpc integration tests and fixes * Fix up command config tests * Store all container logs as artifacts in circle on fail * Add retries to outer part of stats measurements as we keep missing them in CI * Only dump logs on failing cases * Fix typos from code review * Review tidying and make tests pass again * Add debug logs to exec test. * Fix legit test failure caused by upstream rename in envoy config * Attempt to reduce cases of bad TLS handshake in CI integration tests * bring up the right service * Add prometheus integration test * Add test for denied AuthZ both HTTP and TCP * Try ANSI term for Circle
This commit is contained in:
parent
e0778eeee9
commit
d6c0557e86
|
@ -313,6 +313,41 @@ jobs:
|
||||||
- store_test_results:
|
- store_test_results:
|
||||||
path: ui-v2/test-results
|
path: ui-v2/test-results
|
||||||
|
|
||||||
|
# Envoy integration tests. Require docker dev binary to be built already
|
||||||
|
envoy-integration-test-1.8.0:
|
||||||
|
docker:
|
||||||
|
# We only really need bash and docker-compose which is installed on all
|
||||||
|
# Circle images but pick Go since we have to pick one of them.
|
||||||
|
- image: *GOLANG_IMAGE
|
||||||
|
environment:
|
||||||
|
ENVOY_VERSIONS: "1.8.0"
|
||||||
|
steps:
|
||||||
|
&ENVOY_INTEGRATION_TEST_STEPS
|
||||||
|
- checkout
|
||||||
|
# Get go binary from workspace
|
||||||
|
- attach_workspace:
|
||||||
|
at: .
|
||||||
|
- setup_remote_docker:
|
||||||
|
docker_layer_caching: true
|
||||||
|
# Build the consul-dev image from the already built binary
|
||||||
|
- run: docker build -t consul-dev -f ./build-support/docker/Consul-Dev.dockerfile .
|
||||||
|
- run:
|
||||||
|
name: Envoy Integration Tests
|
||||||
|
command: make test-envoy-integ SKIP_DOCKER_BUILD=1
|
||||||
|
environment:
|
||||||
|
# tput complains if this isn't set to something.
|
||||||
|
TERM: ansi
|
||||||
|
- store_artifacts:
|
||||||
|
path: ./test/integration/connect/envoy/workdir/logs
|
||||||
|
destination: container-logs
|
||||||
|
|
||||||
|
envoy-integration-test-1.9.1:
|
||||||
|
docker:
|
||||||
|
- image: *GOLANG_IMAGE
|
||||||
|
environment:
|
||||||
|
ENVOY_VERSIONS: "1.9.1"
|
||||||
|
steps: *ENVOY_INTEGRATION_TEST_STEPS
|
||||||
|
|
||||||
workflows:
|
workflows:
|
||||||
version: 2
|
version: 2
|
||||||
build-distros:
|
build-distros:
|
||||||
|
@ -339,6 +374,12 @@ workflows:
|
||||||
- nomad-integration-0_8:
|
- nomad-integration-0_8:
|
||||||
requires:
|
requires:
|
||||||
- dev-build
|
- dev-build
|
||||||
|
- envoy-integration-test-1.8.0:
|
||||||
|
requires:
|
||||||
|
- dev-build
|
||||||
|
- envoy-integration-test-1.9.1:
|
||||||
|
requires:
|
||||||
|
- dev-build
|
||||||
website:
|
website:
|
||||||
jobs:
|
jobs:
|
||||||
- build-website
|
- build-website
|
||||||
|
|
14
GNUmakefile
14
GNUmakefile
|
@ -96,6 +96,12 @@ export GIT_DESCRIBE
|
||||||
export GOTAGS
|
export GOTAGS
|
||||||
export GOLDFLAGS
|
export GOLDFLAGS
|
||||||
|
|
||||||
|
# Allow skipping docker build during integration tests in CI since we already
|
||||||
|
# have a built binary
|
||||||
|
ENVOY_INTEG_DEPS?=dev-docker
|
||||||
|
ifdef SKIP_DOCKER_BUILD
|
||||||
|
ENVOY_INTEG_DEPS=noop
|
||||||
|
endif
|
||||||
|
|
||||||
DEV_PUSH?=0
|
DEV_PUSH?=0
|
||||||
ifeq ($(DEV_PUSH),1)
|
ifeq ($(DEV_PUSH),1)
|
||||||
|
@ -107,6 +113,9 @@ endif
|
||||||
# all builds binaries for all targets
|
# all builds binaries for all targets
|
||||||
all: bin
|
all: bin
|
||||||
|
|
||||||
|
# used to make integration dependencies conditional
|
||||||
|
noop: ;
|
||||||
|
|
||||||
bin: tools
|
bin: tools
|
||||||
@$(SHELL) $(CURDIR)/build-support/scripts/build-local.sh
|
@$(SHELL) $(CURDIR)/build-support/scripts/build-local.sh
|
||||||
|
|
||||||
|
@ -269,9 +278,12 @@ consul-docker: go-build-image
|
||||||
ui-docker: ui-build-image
|
ui-docker: ui-build-image
|
||||||
@$(SHELL) $(CURDIR)/build-support/scripts/build-docker.sh ui
|
@$(SHELL) $(CURDIR)/build-support/scripts/build-docker.sh ui
|
||||||
|
|
||||||
|
test-envoy-integ: $(ENVOY_INTEG_DEPS)
|
||||||
|
@$(SHELL) $(CURDIR)/test/integration/connect/envoy/run-tests.sh
|
||||||
|
|
||||||
proto:
|
proto:
|
||||||
protoc agent/connect/ca/plugin/*.proto --gofast_out=plugins=grpc:../../..
|
protoc agent/connect/ca/plugin/*.proto --gofast_out=plugins=grpc:../../..
|
||||||
|
|
||||||
.PHONY: all ci bin dev dist cov test test-ci test-internal test-install-deps cover format vet ui static-assets tools
|
.PHONY: all ci bin dev dist cov test test-ci test-internal test-install-deps cover format vet ui static-assets tools
|
||||||
.PHONY: docker-images go-build-image ui-build-image static-assets-docker consul-docker ui-docker
|
.PHONY: docker-images go-build-image ui-build-image static-assets-docker consul-docker ui-docker
|
||||||
.PHONY: version proto
|
.PHONY: version proto test-envoy-integ
|
||||||
|
|
|
@ -113,7 +113,7 @@ func TestManager_BasicLifecycle(t *testing.T) {
|
||||||
Roots: roots,
|
Roots: roots,
|
||||||
Leaf: leaf,
|
Leaf: leaf,
|
||||||
UpstreamEndpoints: map[string]structs.CheckServiceNodes{
|
UpstreamEndpoints: map[string]structs.CheckServiceNodes{
|
||||||
"service:db": TestUpstreamNodes(t),
|
"db": TestUpstreamNodes(t),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
start := time.Now()
|
start := time.Now()
|
||||||
|
|
|
@ -170,7 +170,7 @@ func (s *state) initWatches() error {
|
||||||
QueryOptions: structs.QueryOptions{Token: s.token, MaxAge: defaultPreparedQueryPollInterval},
|
QueryOptions: structs.QueryOptions{Token: s.token, MaxAge: defaultPreparedQueryPollInterval},
|
||||||
QueryIDOrName: u.DestinationName,
|
QueryIDOrName: u.DestinationName,
|
||||||
Connect: true,
|
Connect: true,
|
||||||
}, u.Identifier(), s.ch)
|
}, "upstream:"+u.Identifier(), s.ch)
|
||||||
case structs.UpstreamDestTypeService:
|
case structs.UpstreamDestTypeService:
|
||||||
fallthrough
|
fallthrough
|
||||||
case "": // Treat unset as the default Service type
|
case "": // Treat unset as the default Service type
|
||||||
|
@ -179,7 +179,10 @@ func (s *state) initWatches() error {
|
||||||
QueryOptions: structs.QueryOptions{Token: s.token},
|
QueryOptions: structs.QueryOptions{Token: s.token},
|
||||||
ServiceName: u.DestinationName,
|
ServiceName: u.DestinationName,
|
||||||
Connect: true,
|
Connect: true,
|
||||||
}, u.Identifier(), s.ch)
|
// 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
|
||||||
|
// simpler for us if we have the type to make things unambiguous.
|
||||||
|
}, "upstream:"+serviceIDPrefix+u.Identifier(), s.ch)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -297,19 +300,21 @@ func (s *state) handleUpdate(u cache.UpdateEvent, snap *ConfigSnapshot) error {
|
||||||
default:
|
default:
|
||||||
// Service discovery result, figure out which type
|
// Service discovery result, figure out which type
|
||||||
switch {
|
switch {
|
||||||
case strings.HasPrefix(u.CorrelationID, serviceIDPrefix):
|
case strings.HasPrefix(u.CorrelationID, "upstream:"+serviceIDPrefix):
|
||||||
resp, ok := u.Result.(*structs.IndexedCheckServiceNodes)
|
resp, ok := u.Result.(*structs.IndexedCheckServiceNodes)
|
||||||
if !ok {
|
if !ok {
|
||||||
return fmt.Errorf("invalid type for service response: %T", u.Result)
|
return fmt.Errorf("invalid type for service response: %T", u.Result)
|
||||||
}
|
}
|
||||||
snap.UpstreamEndpoints[u.CorrelationID] = resp.Nodes
|
svc := strings.TrimPrefix(u.CorrelationID, "upstream:"+serviceIDPrefix)
|
||||||
|
snap.UpstreamEndpoints[svc] = resp.Nodes
|
||||||
|
|
||||||
case strings.HasPrefix(u.CorrelationID, 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 prepared query response: %T", u.Result)
|
return fmt.Errorf("invalid type for prepared query response: %T", u.Result)
|
||||||
}
|
}
|
||||||
snap.UpstreamEndpoints[u.CorrelationID] = resp.Nodes
|
pq := strings.TrimPrefix(u.CorrelationID, "upstream:")
|
||||||
|
snap.UpstreamEndpoints[pq] = resp.Nodes
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return errors.New("unknown correlation ID")
|
return errors.New("unknown correlation ID")
|
||||||
|
|
|
@ -165,7 +165,7 @@ func TestConfigSnapshot(t testing.T) *ConfigSnapshot {
|
||||||
Roots: roots,
|
Roots: roots,
|
||||||
Leaf: leaf,
|
Leaf: leaf,
|
||||||
UpstreamEndpoints: map[string]structs.CheckServiceNodes{
|
UpstreamEndpoints: map[string]structs.CheckServiceNodes{
|
||||||
"service:db": TestUpstreamNodes(t),
|
"db": TestUpstreamNodes(t),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -168,8 +168,11 @@ func (u *Upstream) Identifier() string {
|
||||||
name += "?dc=" + u.Datacenter
|
name += "?dc=" + u.Datacenter
|
||||||
}
|
}
|
||||||
typ := u.DestinationType
|
typ := u.DestinationType
|
||||||
if typ == "" {
|
// Service is default type so never prefix it. This is more readable and long
|
||||||
typ = UpstreamDestTypeService
|
// term it is the only type that matters so we can drop the prefix and have
|
||||||
|
// nicer naming in metrics etc.
|
||||||
|
if typ == "" || typ == UpstreamDestTypeService {
|
||||||
|
return name
|
||||||
}
|
}
|
||||||
return typ + ":" + name
|
return typ + ":" + name
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,13 +4,13 @@ import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"strconv"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
envoy "github.com/envoyproxy/go-control-plane/envoy/api/v2"
|
envoy "github.com/envoyproxy/go-control-plane/envoy/api/v2"
|
||||||
envoyauth "github.com/envoyproxy/go-control-plane/envoy/api/v2/auth"
|
envoyauth "github.com/envoyproxy/go-control-plane/envoy/api/v2/auth"
|
||||||
envoycluster "github.com/envoyproxy/go-control-plane/envoy/api/v2/cluster"
|
envoycluster "github.com/envoyproxy/go-control-plane/envoy/api/v2/cluster"
|
||||||
envoycore "github.com/envoyproxy/go-control-plane/envoy/api/v2/core"
|
envoycore "github.com/envoyproxy/go-control-plane/envoy/api/v2/core"
|
||||||
|
envoyendpoint "github.com/envoyproxy/go-control-plane/envoy/api/v2/endpoint"
|
||||||
"github.com/gogo/protobuf/jsonpb"
|
"github.com/gogo/protobuf/jsonpb"
|
||||||
"github.com/gogo/protobuf/proto"
|
"github.com/gogo/protobuf/proto"
|
||||||
"github.com/gogo/protobuf/types"
|
"github.com/gogo/protobuf/types"
|
||||||
|
@ -21,7 +21,7 @@ import (
|
||||||
|
|
||||||
// clustersFromSnapshot returns the xDS API representation of the "clusters"
|
// clustersFromSnapshot returns the xDS API representation of the "clusters"
|
||||||
// (upstreams) in the snapshot.
|
// (upstreams) in the snapshot.
|
||||||
func clustersFromSnapshot(cfgSnap *proxycfg.ConfigSnapshot, token string) ([]proto.Message, error) {
|
func (s *Server) clustersFromSnapshot(cfgSnap *proxycfg.ConfigSnapshot, token string) ([]proto.Message, error) {
|
||||||
if cfgSnap == nil {
|
if cfgSnap == nil {
|
||||||
return nil, errors.New("nil config given")
|
return nil, errors.New("nil config given")
|
||||||
}
|
}
|
||||||
|
@ -29,13 +29,13 @@ func clustersFromSnapshot(cfgSnap *proxycfg.ConfigSnapshot, token string) ([]pro
|
||||||
clusters := make([]proto.Message, len(cfgSnap.Proxy.Upstreams)+1)
|
clusters := make([]proto.Message, len(cfgSnap.Proxy.Upstreams)+1)
|
||||||
|
|
||||||
var err error
|
var err error
|
||||||
clusters[0], err = makeAppCluster(cfgSnap)
|
clusters[0], err = s.makeAppCluster(cfgSnap)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
for idx, upstream := range cfgSnap.Proxy.Upstreams {
|
for idx, upstream := range cfgSnap.Proxy.Upstreams {
|
||||||
clusters[idx+1], err = makeUpstreamCluster(upstream, cfgSnap)
|
clusters[idx+1], err = s.makeUpstreamCluster(upstream, cfgSnap)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -44,18 +44,20 @@ func clustersFromSnapshot(cfgSnap *proxycfg.ConfigSnapshot, token string) ([]pro
|
||||||
return clusters, nil
|
return clusters, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func makeAppCluster(cfgSnap *proxycfg.ConfigSnapshot) (*envoy.Cluster, error) {
|
func (s *Server) makeAppCluster(cfgSnap *proxycfg.ConfigSnapshot) (*envoy.Cluster, error) {
|
||||||
var c *envoy.Cluster
|
var c *envoy.Cluster
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
|
cfg, err := ParseProxyConfig(cfgSnap.Proxy.Config)
|
||||||
|
if err != nil {
|
||||||
|
// Don't hard fail on a config typo, just warn. The parse func returns
|
||||||
|
// default config if there is an error so it's safe to continue.
|
||||||
|
s.Logger.Printf("[WARN] envoy: failed to parse Connect.Proxy.Config: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
// If we have overridden local cluster config try to parse it into an Envoy cluster
|
// If we have overridden local cluster config try to parse it into an Envoy cluster
|
||||||
if clusterJSONRaw, ok := cfgSnap.Proxy.Config["envoy_local_cluster_json"]; ok {
|
if cfg.LocalClusterJSON != "" {
|
||||||
if clusterJSON, ok := clusterJSONRaw.(string); ok {
|
return makeClusterFromUserConfig(cfg.LocalClusterJSON)
|
||||||
c, err = makeClusterFromUserConfig(clusterJSON)
|
|
||||||
if err != nil {
|
|
||||||
return c, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if c == nil {
|
if c == nil {
|
||||||
|
@ -65,72 +67,52 @@ func makeAppCluster(cfgSnap *proxycfg.ConfigSnapshot) (*envoy.Cluster, error) {
|
||||||
}
|
}
|
||||||
c = &envoy.Cluster{
|
c = &envoy.Cluster{
|
||||||
Name: LocalAppClusterName,
|
Name: LocalAppClusterName,
|
||||||
ConnectTimeout: 5 * time.Second,
|
ConnectTimeout: time.Duration(cfg.LocalConnectTimeoutMs) * time.Millisecond,
|
||||||
Type: envoy.Cluster_STATIC,
|
Type: envoy.Cluster_STATIC,
|
||||||
// API v2 docs say hosts is deprecated and should use LoadAssignment as
|
LoadAssignment: &envoy.ClusterLoadAssignment{
|
||||||
// below.. but it doesn't work for tcp_proxy target for some reason.
|
ClusterName: LocalAppClusterName,
|
||||||
Hosts: []*envoycore.Address{makeAddressPtr(addr, cfgSnap.Proxy.LocalServicePort)},
|
Endpoints: []envoyendpoint.LocalityLbEndpoints{
|
||||||
// LoadAssignment: &envoy.ClusterLoadAssignment{
|
{
|
||||||
// ClusterName: LocalAppClusterName,
|
LbEndpoints: []envoyendpoint.LbEndpoint{
|
||||||
// Endpoints: []endpoint.LocalityLbEndpoints{
|
makeEndpoint(LocalAppClusterName,
|
||||||
// {
|
addr,
|
||||||
// LbEndpoints: []endpoint.LbEndpoint{
|
cfgSnap.Proxy.LocalServicePort),
|
||||||
// makeEndpoint(LocalAppClusterName,
|
},
|
||||||
// addr,
|
},
|
||||||
// cfgSnap.Proxy.LocalServicePort),
|
},
|
||||||
// },
|
},
|
||||||
// },
|
}
|
||||||
// },
|
if cfg.Protocol == "http2" || cfg.Protocol == "grpc" {
|
||||||
// },
|
c.Http2ProtocolOptions = &envoycore.Http2ProtocolOptions{}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return c, err
|
return c, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func parseTimeMillis(ms interface{}) (time.Duration, error) {
|
func (s *Server) makeUpstreamCluster(upstream structs.Upstream, cfgSnap *proxycfg.ConfigSnapshot) (*envoy.Cluster, error) {
|
||||||
switch v := ms.(type) {
|
|
||||||
case string:
|
|
||||||
ms, err := strconv.Atoi(v)
|
|
||||||
if err != nil {
|
|
||||||
return 0, err
|
|
||||||
}
|
|
||||||
return time.Duration(ms) * time.Millisecond, nil
|
|
||||||
|
|
||||||
case float64: // This is what parsing from JSON results in
|
|
||||||
return time.Duration(v) * time.Millisecond, nil
|
|
||||||
// Not sure if this can ever really happen but just in case it does in
|
|
||||||
// some test code...
|
|
||||||
case int:
|
|
||||||
return time.Duration(v) * time.Millisecond, nil
|
|
||||||
}
|
|
||||||
return 0, errors.New("invalid type for millisecond duration")
|
|
||||||
}
|
|
||||||
|
|
||||||
func makeUpstreamCluster(upstream structs.Upstream, cfgSnap *proxycfg.ConfigSnapshot) (*envoy.Cluster, error) {
|
|
||||||
var c *envoy.Cluster
|
var c *envoy.Cluster
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
// If we have overridden cluster config attempt to parse it into an Envoy cluster
|
cfg, err := ParseUpstreamConfig(upstream.Config)
|
||||||
if clusterJSONRaw, ok := upstream.Config["envoy_cluster_json"]; ok {
|
if err != nil {
|
||||||
if clusterJSON, ok := clusterJSONRaw.(string); ok {
|
// Don't hard fail on a config typo, just warn. The parse func returns
|
||||||
c, err = makeClusterFromUserConfig(clusterJSON)
|
// default config if there is an error so it's safe to continue.
|
||||||
if err != nil {
|
s.Logger.Printf("[WARN] envoy: failed to parse Upstream[%s].Config: %s",
|
||||||
return c, err
|
upstream.Identifier(), err)
|
||||||
}
|
}
|
||||||
|
if cfg.ClusterJSON != "" {
|
||||||
|
c, err = makeClusterFromUserConfig(cfg.ClusterJSON)
|
||||||
|
if err != nil {
|
||||||
|
return c, err
|
||||||
}
|
}
|
||||||
|
// In the happy path don't return yet as we need to inject TLS config still.
|
||||||
}
|
}
|
||||||
|
|
||||||
if c == nil {
|
if c == nil {
|
||||||
conTimeout := 5 * time.Second
|
|
||||||
if toRaw, ok := upstream.Config["connect_timeout_ms"]; ok {
|
|
||||||
if ms, err := parseTimeMillis(toRaw); err == nil {
|
|
||||||
conTimeout = ms
|
|
||||||
}
|
|
||||||
}
|
|
||||||
c = &envoy.Cluster{
|
c = &envoy.Cluster{
|
||||||
Name: upstream.Identifier(),
|
Name: upstream.Identifier(),
|
||||||
ConnectTimeout: conTimeout,
|
ConnectTimeout: time.Duration(cfg.ConnectTimeoutMs) * time.Millisecond,
|
||||||
Type: envoy.Cluster_EDS,
|
Type: envoy.Cluster_EDS,
|
||||||
EdsClusterConfig: &envoy.Cluster_EdsClusterConfig{
|
EdsClusterConfig: &envoy.Cluster_EdsClusterConfig{
|
||||||
EdsConfig: &envoycore.ConfigSource{
|
EdsConfig: &envoycore.ConfigSource{
|
||||||
|
@ -142,6 +124,9 @@ func makeUpstreamCluster(upstream structs.Upstream, cfgSnap *proxycfg.ConfigSnap
|
||||||
// Having an empty config enables outlier detection with default config.
|
// Having an empty config enables outlier detection with default config.
|
||||||
OutlierDetection: &envoycluster.OutlierDetection{},
|
OutlierDetection: &envoycluster.OutlierDetection{},
|
||||||
}
|
}
|
||||||
|
if cfg.Protocol == "http2" || cfg.Protocol == "grpc" {
|
||||||
|
c.Http2ProtocolOptions = &envoycore.Http2ProtocolOptions{}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Enable TLS upstream with the configured client certificate.
|
// Enable TLS upstream with the configured client certificate.
|
||||||
|
@ -188,8 +173,7 @@ func makeClusterFromUserConfig(configJSON string) (*envoy.Cluster, error) {
|
||||||
// And then unmarshal the listener again...
|
// And then unmarshal the listener again...
|
||||||
err = proto.Unmarshal(any.Value, &c)
|
err = proto.Unmarshal(any.Value, &c)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
return nil, err
|
||||||
//return nil, err
|
|
||||||
}
|
}
|
||||||
return &c, err
|
return &c, err
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,76 +1,292 @@
|
||||||
package xds
|
package xds
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
"path"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"text/template"
|
||||||
|
|
||||||
envoy "github.com/envoyproxy/go-control-plane/envoy/api/v2"
|
|
||||||
envoyauth "github.com/envoyproxy/go-control-plane/envoy/api/v2/auth"
|
|
||||||
"github.com/envoyproxy/go-control-plane/envoy/api/v2/cluster"
|
|
||||||
envoycore "github.com/envoyproxy/go-control-plane/envoy/api/v2/core"
|
|
||||||
"github.com/stretchr/testify/require"
|
|
||||||
|
|
||||||
"github.com/hashicorp/consul/agent/proxycfg"
|
"github.com/hashicorp/consul/agent/proxycfg"
|
||||||
"github.com/hashicorp/consul/agent/structs"
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
|
||||||
func Test_makeUpstreamCluster(t *testing.T) {
|
func TestClustersFromSnapshot(t *testing.T) {
|
||||||
|
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
name string
|
name string
|
||||||
snap proxycfg.ConfigSnapshot
|
// Setup is called before the test starts. It is passed the snapshot from
|
||||||
upstream structs.Upstream
|
// TestConfigSnapshot and is allowed to modify it in any way to setup the
|
||||||
want *envoy.Cluster
|
// test input.
|
||||||
|
setup func(snap *proxycfg.ConfigSnapshot)
|
||||||
|
overrideGoldenName string
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
name: "timeout override",
|
name: "defaults",
|
||||||
snap: proxycfg.ConfigSnapshot{},
|
setup: nil, // Default snapshot
|
||||||
upstream: structs.TestUpstreams(t)[0],
|
},
|
||||||
want: &envoy.Cluster{
|
{
|
||||||
Name: "service:db",
|
name: "custom-local-app",
|
||||||
Type: envoy.Cluster_EDS,
|
setup: func(snap *proxycfg.ConfigSnapshot) {
|
||||||
EdsClusterConfig: &envoy.Cluster_EdsClusterConfig{
|
snap.Proxy.Config["envoy_local_cluster_json"] =
|
||||||
EdsConfig: &envoycore.ConfigSource{
|
customAppClusterJSON(t, customClusterJSONOptions{
|
||||||
ConfigSourceSpecifier: &envoycore.ConfigSource_Ads{
|
Name: "mylocal",
|
||||||
Ads: &envoycore.AggregatedConfigSource{},
|
IncludeType: false,
|
||||||
},
|
})
|
||||||
},
|
|
||||||
},
|
|
||||||
ConnectTimeout: 1 * time.Second, // TestUpstreams overrides to 1000ms
|
|
||||||
OutlierDetection: &cluster.OutlierDetection{},
|
|
||||||
TlsContext: &envoyauth.UpstreamTlsContext{
|
|
||||||
CommonTlsContext: makeCommonTLSContext(&proxycfg.ConfigSnapshot{}),
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "timeout default",
|
name: "custom-local-app-typed",
|
||||||
snap: proxycfg.ConfigSnapshot{},
|
overrideGoldenName: "custom-local-app",
|
||||||
upstream: structs.TestUpstreams(t)[1],
|
setup: func(snap *proxycfg.ConfigSnapshot) {
|
||||||
want: &envoy.Cluster{
|
snap.Proxy.Config["envoy_local_cluster_json"] =
|
||||||
Name: "prepared_query:geo-cache",
|
customAppClusterJSON(t, customClusterJSONOptions{
|
||||||
Type: envoy.Cluster_EDS,
|
Name: "mylocal",
|
||||||
EdsClusterConfig: &envoy.Cluster_EdsClusterConfig{
|
IncludeType: true,
|
||||||
EdsConfig: &envoycore.ConfigSource{
|
})
|
||||||
ConfigSourceSpecifier: &envoycore.ConfigSource_Ads{
|
},
|
||||||
Ads: &envoycore.AggregatedConfigSource{},
|
},
|
||||||
},
|
{
|
||||||
},
|
name: "custom-upstream",
|
||||||
},
|
setup: func(snap *proxycfg.ConfigSnapshot) {
|
||||||
ConnectTimeout: 5 * time.Second, // Default
|
snap.Proxy.Upstreams[0].Config["envoy_cluster_json"] =
|
||||||
OutlierDetection: &cluster.OutlierDetection{},
|
customAppClusterJSON(t, customClusterJSONOptions{
|
||||||
TlsContext: &envoyauth.UpstreamTlsContext{
|
Name: "myservice",
|
||||||
CommonTlsContext: makeCommonTLSContext(&proxycfg.ConfigSnapshot{}),
|
IncludeType: false,
|
||||||
},
|
})
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "custom-upstream-typed",
|
||||||
|
overrideGoldenName: "custom-upstream",
|
||||||
|
setup: func(snap *proxycfg.ConfigSnapshot) {
|
||||||
|
snap.Proxy.Upstreams[0].Config["envoy_cluster_json"] =
|
||||||
|
customAppClusterJSON(t, customClusterJSONOptions{
|
||||||
|
Name: "myservice",
|
||||||
|
IncludeType: true,
|
||||||
|
})
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "custom-upstream-ignores-tls",
|
||||||
|
overrideGoldenName: "custom-upstream", // should be the same
|
||||||
|
setup: func(snap *proxycfg.ConfigSnapshot) {
|
||||||
|
snap.Proxy.Upstreams[0].Config["envoy_cluster_json"] =
|
||||||
|
customAppClusterJSON(t, customClusterJSONOptions{
|
||||||
|
Name: "myservice",
|
||||||
|
IncludeType: true,
|
||||||
|
// Attempt to override the TLS context should be ignored
|
||||||
|
TLSContext: `{"commonTlsContext": {}}`,
|
||||||
|
})
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "custom-timeouts",
|
||||||
|
setup: func(snap *proxycfg.ConfigSnapshot) {
|
||||||
|
snap.Proxy.Config["local_connect_timeout_ms"] = 1234
|
||||||
|
snap.Proxy.Upstreams[0].Config["connect_timeout_ms"] = 2345
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
require := require.New(t)
|
require := require.New(t)
|
||||||
got, err := makeUpstreamCluster(tt.upstream, &tt.snap)
|
|
||||||
|
// Sanity check default with no overrides first
|
||||||
|
snap := proxycfg.TestConfigSnapshot(t)
|
||||||
|
|
||||||
|
// We need to replace the TLS certs with deterministic ones to make golden
|
||||||
|
// files workable. Note we don't update these otherwise they'd change
|
||||||
|
// golder files for every test case and so not be any use!
|
||||||
|
snap.Leaf.CertPEM = golden(t, "test-leaf-cert", "")
|
||||||
|
snap.Leaf.PrivateKeyPEM = golden(t, "test-leaf-key", "")
|
||||||
|
snap.Roots.Roots[0].RootCert = golden(t, "test-root-cert", "")
|
||||||
|
|
||||||
|
if tt.setup != nil {
|
||||||
|
tt.setup(snap)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Need server just for logger dependency
|
||||||
|
s := Server{Logger: log.New(os.Stderr, "", log.LstdFlags)}
|
||||||
|
|
||||||
|
clusters, err := s.clustersFromSnapshot(snap, "my-token")
|
||||||
|
require.NoError(err)
|
||||||
|
r, err := createResponse(ClusterType, "00000001", "00000001", clusters)
|
||||||
require.NoError(err)
|
require.NoError(err)
|
||||||
|
|
||||||
require.Equal(tt.want, got)
|
gotJSON := responseToJSON(t, r)
|
||||||
|
|
||||||
|
gName := tt.name
|
||||||
|
if tt.overrideGoldenName != "" {
|
||||||
|
gName = tt.overrideGoldenName
|
||||||
|
}
|
||||||
|
|
||||||
|
require.JSONEq(golden(t, path.Join("clusters", gName), gotJSON), gotJSON)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func expectClustersJSONResources(t *testing.T, snap *proxycfg.ConfigSnapshot, token string, v, n uint64) map[string]string {
|
||||||
|
return map[string]string{
|
||||||
|
"local_app": `
|
||||||
|
{
|
||||||
|
"@type": "type.googleapis.com/envoy.api.v2.Cluster",
|
||||||
|
"name": "local_app",
|
||||||
|
"connectTimeout": "5s",
|
||||||
|
"loadAssignment": {
|
||||||
|
"clusterName": "local_app",
|
||||||
|
"endpoints": [
|
||||||
|
{
|
||||||
|
"lbEndpoints": [
|
||||||
|
{
|
||||||
|
"endpoint": {
|
||||||
|
"address": {
|
||||||
|
"socketAddress": {
|
||||||
|
"address": "127.0.0.1",
|
||||||
|
"portValue": 8080
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}`,
|
||||||
|
"db": `
|
||||||
|
{
|
||||||
|
"@type": "type.googleapis.com/envoy.api.v2.Cluster",
|
||||||
|
"name": "db",
|
||||||
|
"type": "EDS",
|
||||||
|
"edsClusterConfig": {
|
||||||
|
"edsConfig": {
|
||||||
|
"ads": {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"outlierDetection": {
|
||||||
|
|
||||||
|
},
|
||||||
|
"connectTimeout": "1s",
|
||||||
|
"tlsContext": ` + expectedUpstreamTLSContextJSON(t, snap) + `
|
||||||
|
}`,
|
||||||
|
"prepared_query:geo-cache": `
|
||||||
|
{
|
||||||
|
"@type": "type.googleapis.com/envoy.api.v2.Cluster",
|
||||||
|
"name": "prepared_query:geo-cache",
|
||||||
|
"type": "EDS",
|
||||||
|
"edsClusterConfig": {
|
||||||
|
"edsConfig": {
|
||||||
|
"ads": {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"outlierDetection": {
|
||||||
|
|
||||||
|
},
|
||||||
|
"connectTimeout": "5s",
|
||||||
|
"tlsContext": ` + expectedUpstreamTLSContextJSON(t, snap) + `
|
||||||
|
}`,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func expectClustersJSONFromResources(t *testing.T, snap *proxycfg.ConfigSnapshot, token string, v, n uint64, resourcesJSON map[string]string) string {
|
||||||
|
resJSON := ""
|
||||||
|
|
||||||
|
// Sort resources into specific order because that matters in JSONEq
|
||||||
|
// comparison later.
|
||||||
|
keyOrder := []string{"local_app"}
|
||||||
|
for _, u := range snap.Proxy.Upstreams {
|
||||||
|
keyOrder = append(keyOrder, u.Identifier())
|
||||||
|
}
|
||||||
|
for _, k := range keyOrder {
|
||||||
|
j, ok := resourcesJSON[k]
|
||||||
|
if !ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if resJSON != "" {
|
||||||
|
resJSON += ",\n"
|
||||||
|
}
|
||||||
|
resJSON += j
|
||||||
|
}
|
||||||
|
|
||||||
|
return `{
|
||||||
|
"versionInfo": "` + hexString(v) + `",
|
||||||
|
"resources": [` + resJSON + `],
|
||||||
|
"typeUrl": "type.googleapis.com/envoy.api.v2.Cluster",
|
||||||
|
"nonce": "` + hexString(n) + `"
|
||||||
|
}`
|
||||||
|
}
|
||||||
|
|
||||||
|
func expectClustersJSON(t *testing.T, snap *proxycfg.ConfigSnapshot, token string, v, n uint64) string {
|
||||||
|
return expectClustersJSONFromResources(t, snap, token, v, n,
|
||||||
|
expectClustersJSONResources(t, snap, token, v, n))
|
||||||
|
}
|
||||||
|
|
||||||
|
type customClusterJSONOptions struct {
|
||||||
|
Name string
|
||||||
|
IncludeType bool
|
||||||
|
TLSContext string
|
||||||
|
}
|
||||||
|
|
||||||
|
var customEDSClusterJSONTpl = `{
|
||||||
|
{{ if .IncludeType -}}
|
||||||
|
"@type": "type.googleapis.com/envoy.api.v2.Cluster",
|
||||||
|
{{- end }}
|
||||||
|
{{ if .TLSContext -}}
|
||||||
|
"tlsContext": {{ .TLSContext }},
|
||||||
|
{{- end }}
|
||||||
|
"name": "{{ .Name }}",
|
||||||
|
"type": "EDS",
|
||||||
|
"edsClusterConfig": {
|
||||||
|
"edsConfig": {
|
||||||
|
"ads": {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"connectTimeout": "5s"
|
||||||
|
}`
|
||||||
|
|
||||||
|
var customEDSClusterJSONTemplate = template.Must(template.New("").Parse(customEDSClusterJSONTpl))
|
||||||
|
|
||||||
|
func customEDSClusterJSON(t *testing.T, opts customClusterJSONOptions) string {
|
||||||
|
t.Helper()
|
||||||
|
var buf bytes.Buffer
|
||||||
|
err := customEDSClusterJSONTemplate.Execute(&buf, opts)
|
||||||
|
require.NoError(t, err)
|
||||||
|
return buf.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
var customAppClusterJSONTpl = `{
|
||||||
|
{{ if .IncludeType -}}
|
||||||
|
"@type": "type.googleapis.com/envoy.api.v2.Cluster",
|
||||||
|
{{- end }}
|
||||||
|
{{ if .TLSContext -}}
|
||||||
|
"tlsContext": {{ .TLSContext }},
|
||||||
|
{{- end }}
|
||||||
|
"name": "{{ .Name }}",
|
||||||
|
"connectTimeout": "5s",
|
||||||
|
"hosts": [
|
||||||
|
{
|
||||||
|
"socketAddress": {
|
||||||
|
"address": "127.0.0.1",
|
||||||
|
"portValue": 8080
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}`
|
||||||
|
|
||||||
|
var customAppClusterJSONTemplate = template.Must(template.New("").Parse(customAppClusterJSONTpl))
|
||||||
|
|
||||||
|
func customAppClusterJSON(t *testing.T, opts customClusterJSONOptions) string {
|
||||||
|
t.Helper()
|
||||||
|
var buf bytes.Buffer
|
||||||
|
err := customAppClusterJSONTemplate.Execute(&buf, opts)
|
||||||
|
require.NoError(t, err)
|
||||||
|
return buf.String()
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,94 @@
|
||||||
|
package xds
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/mitchellh/mapstructure"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ProxyConfig describes the keys we understand from Connect.Proxy.Config. Note
|
||||||
|
// that this only includes config keys that affects runtime config delivered by
|
||||||
|
// xDS. For Envoy config keys that affect bootstrap generation see
|
||||||
|
// command/connect/envoy/bootstrap_config.go.
|
||||||
|
type ProxyConfig struct {
|
||||||
|
// PublicListenerJSON is a complete override ("escape hatch") for the
|
||||||
|
// upstream's public listener. The Connect server TLS certificate and
|
||||||
|
// validation context will be injected overriding any TLS settings present. An
|
||||||
|
// AuthZ filter will also be prepended to each filterChain provided to enforce
|
||||||
|
// Connect's access control.
|
||||||
|
PublicListenerJSON string `mapstructure:"envoy_public_listener_json"`
|
||||||
|
|
||||||
|
// LocalClusterJSON is a complete override ("escape hatch") for the
|
||||||
|
// local application cluster.
|
||||||
|
LocalClusterJSON string `mapstructure:"envoy_local_cluster_json"`
|
||||||
|
|
||||||
|
// LocalConnectTimeoutMs is the number of milliseconds to timeout making a new
|
||||||
|
// connection to the local app instance. Defaults to 5000 (5 seconds) if not
|
||||||
|
// set.
|
||||||
|
LocalConnectTimeoutMs int `mapstructure:"local_connect_timeout_ms"`
|
||||||
|
|
||||||
|
// Protocol describes the service's protocol. Valid values are "tcp",
|
||||||
|
// "http" and "grpc". Anything else is treated as tcp. The enables protocol
|
||||||
|
// aware features like per-request metrics and connection pooling, tracing,
|
||||||
|
// routing etc.
|
||||||
|
Protocol string `mapstructure:"protocol"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ParseProxyConfig returns the ProxyConfig parsed from the an opaque map. If an
|
||||||
|
// error occurs during parsing it is returned along with the default config this
|
||||||
|
// allows caller to choose whether and how to report the error.
|
||||||
|
func ParseProxyConfig(m map[string]interface{}) (ProxyConfig, error) {
|
||||||
|
var cfg ProxyConfig
|
||||||
|
err := mapstructure.WeakDecode(m, &cfg)
|
||||||
|
// Set defaults (even if error is returned)
|
||||||
|
if cfg.Protocol == "" {
|
||||||
|
cfg.Protocol = "tcp"
|
||||||
|
} else {
|
||||||
|
cfg.Protocol = strings.ToLower(cfg.Protocol)
|
||||||
|
}
|
||||||
|
if cfg.LocalConnectTimeoutMs < 1 {
|
||||||
|
cfg.LocalConnectTimeoutMs = 5000
|
||||||
|
}
|
||||||
|
return cfg, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpstreamConfig describes the keys we understand from
|
||||||
|
// Connect.Proxy.Upstream[*].Config.
|
||||||
|
type UpstreamConfig struct {
|
||||||
|
// ListenerJSON is a complete override ("escape hatch") for the upstream's
|
||||||
|
// listener.
|
||||||
|
ListenerJSON string `mapstructure:"envoy_listener_json"`
|
||||||
|
|
||||||
|
// ClusterJSON is a complete override ("escape hatch") for the upstream's
|
||||||
|
// cluster. The Connect client TLS certificate and context will be injected
|
||||||
|
// overriding any TLS settings present.
|
||||||
|
ClusterJSON string `mapstructure:"envoy_cluster_json"`
|
||||||
|
|
||||||
|
// Protocol describes the upstream's service protocol. Valid values are "tcp",
|
||||||
|
// "http" and "grpc". Anything else is treated as tcp. The enables protocol
|
||||||
|
// aware features like per-request metrics and connection pooling, tracing,
|
||||||
|
// routing etc.
|
||||||
|
Protocol string `mapstructure:"protocol"`
|
||||||
|
|
||||||
|
// ConnectTimeoutMs is the number of milliseconds to timeout making a new
|
||||||
|
// connection to this upstream. Defaults to 5000 (5 seconds) if not set.
|
||||||
|
ConnectTimeoutMs int `mapstructure:"connect_timeout_ms"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ParseUpstreamConfig returns the UpstreamConfig parsed from the an opaque map.
|
||||||
|
// If an error occurs during parsing it is returned along with the default
|
||||||
|
// config this allows caller to choose whether and how to report the error.
|
||||||
|
func ParseUpstreamConfig(m map[string]interface{}) (UpstreamConfig, error) {
|
||||||
|
var cfg UpstreamConfig
|
||||||
|
err := mapstructure.WeakDecode(m, &cfg)
|
||||||
|
// Set defaults (even if error is returned)
|
||||||
|
if cfg.Protocol == "" {
|
||||||
|
cfg.Protocol = "tcp"
|
||||||
|
} else {
|
||||||
|
cfg.Protocol = strings.ToLower(cfg.Protocol)
|
||||||
|
}
|
||||||
|
if cfg.ConnectTimeoutMs < 1 {
|
||||||
|
cfg.ConnectTimeoutMs = 5000
|
||||||
|
}
|
||||||
|
return cfg, err
|
||||||
|
}
|
|
@ -0,0 +1,183 @@
|
||||||
|
package xds
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestParseProxyConfig(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
input map[string]interface{}
|
||||||
|
want ProxyConfig
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "defaults - nil",
|
||||||
|
input: nil,
|
||||||
|
want: ProxyConfig{
|
||||||
|
Protocol: "tcp",
|
||||||
|
LocalConnectTimeoutMs: 5000,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "defaults - empty",
|
||||||
|
input: map[string]interface{}{},
|
||||||
|
want: ProxyConfig{
|
||||||
|
Protocol: "tcp",
|
||||||
|
LocalConnectTimeoutMs: 5000,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "defaults - other stuff",
|
||||||
|
input: map[string]interface{}{
|
||||||
|
"foo": "bar",
|
||||||
|
"envoy_foo": "envoy_bar",
|
||||||
|
},
|
||||||
|
want: ProxyConfig{
|
||||||
|
Protocol: "tcp",
|
||||||
|
LocalConnectTimeoutMs: 5000,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "protocol override",
|
||||||
|
input: map[string]interface{}{
|
||||||
|
"protocol": "http",
|
||||||
|
},
|
||||||
|
want: ProxyConfig{
|
||||||
|
Protocol: "http",
|
||||||
|
LocalConnectTimeoutMs: 5000,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "protocol uppercase override",
|
||||||
|
input: map[string]interface{}{
|
||||||
|
"protocol": "HTTP",
|
||||||
|
},
|
||||||
|
want: ProxyConfig{
|
||||||
|
Protocol: "http",
|
||||||
|
LocalConnectTimeoutMs: 5000,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "local connect timeout override, string",
|
||||||
|
input: map[string]interface{}{
|
||||||
|
"local_connect_timeout_ms": "1000",
|
||||||
|
},
|
||||||
|
want: ProxyConfig{
|
||||||
|
LocalConnectTimeoutMs: 1000,
|
||||||
|
Protocol: "tcp",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "local connect timeout override, float ",
|
||||||
|
input: map[string]interface{}{
|
||||||
|
"local_connect_timeout_ms": float64(1000.0),
|
||||||
|
},
|
||||||
|
want: ProxyConfig{
|
||||||
|
LocalConnectTimeoutMs: 1000,
|
||||||
|
Protocol: "tcp",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "local connect timeout override, int ",
|
||||||
|
input: map[string]interface{}{
|
||||||
|
"local_connect_timeout_ms": 1000,
|
||||||
|
},
|
||||||
|
want: ProxyConfig{
|
||||||
|
LocalConnectTimeoutMs: 1000,
|
||||||
|
Protocol: "tcp",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
got, err := ParseProxyConfig(tt.input)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, tt.want, got)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestParseUpstreamConfig(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
input map[string]interface{}
|
||||||
|
want UpstreamConfig
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "defaults - nil",
|
||||||
|
input: nil,
|
||||||
|
want: UpstreamConfig{
|
||||||
|
ConnectTimeoutMs: 5000,
|
||||||
|
Protocol: "tcp",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "defaults - empty",
|
||||||
|
input: map[string]interface{}{},
|
||||||
|
want: UpstreamConfig{
|
||||||
|
ConnectTimeoutMs: 5000,
|
||||||
|
Protocol: "tcp",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "defaults - other stuff",
|
||||||
|
input: map[string]interface{}{
|
||||||
|
"foo": "bar",
|
||||||
|
"envoy_foo": "envoy_bar",
|
||||||
|
},
|
||||||
|
want: UpstreamConfig{
|
||||||
|
ConnectTimeoutMs: 5000,
|
||||||
|
Protocol: "tcp",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "protocol override",
|
||||||
|
input: map[string]interface{}{
|
||||||
|
"protocol": "http",
|
||||||
|
},
|
||||||
|
want: UpstreamConfig{
|
||||||
|
ConnectTimeoutMs: 5000,
|
||||||
|
Protocol: "http",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "connect timeout override, string",
|
||||||
|
input: map[string]interface{}{
|
||||||
|
"connect_timeout_ms": "1000",
|
||||||
|
},
|
||||||
|
want: UpstreamConfig{
|
||||||
|
ConnectTimeoutMs: 1000,
|
||||||
|
Protocol: "tcp",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "connect timeout override, float ",
|
||||||
|
input: map[string]interface{}{
|
||||||
|
"connect_timeout_ms": float64(1000.0),
|
||||||
|
},
|
||||||
|
want: UpstreamConfig{
|
||||||
|
ConnectTimeoutMs: 1000,
|
||||||
|
Protocol: "tcp",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "connect timeout override, int ",
|
||||||
|
input: map[string]interface{}{
|
||||||
|
"connect_timeout_ms": 1000,
|
||||||
|
},
|
||||||
|
want: UpstreamConfig{
|
||||||
|
ConnectTimeoutMs: 1000,
|
||||||
|
Protocol: "tcp",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
got, err := ParseUpstreamConfig(tt.input)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, tt.want, got)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,43 @@
|
||||||
|
package xds
|
||||||
|
|
||||||
|
import (
|
||||||
|
"flag"
|
||||||
|
"io/ioutil"
|
||||||
|
"path/filepath"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
envoy "github.com/envoyproxy/go-control-plane/envoy/api/v2"
|
||||||
|
"github.com/gogo/protobuf/jsonpb"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
// update allows golden files to be updated based on the current output.
|
||||||
|
var update = flag.Bool("update", false, "update golden files")
|
||||||
|
|
||||||
|
// golden reads and optionally writes the expected data to the golden file,
|
||||||
|
// returning the contents as a string.
|
||||||
|
func golden(t *testing.T, name, got string) string {
|
||||||
|
t.Helper()
|
||||||
|
|
||||||
|
golden := filepath.Join("testdata", name+".golden")
|
||||||
|
if *update && got != "" {
|
||||||
|
err := ioutil.WriteFile(golden, []byte(got), 0644)
|
||||||
|
require.NoError(t, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
expected, err := ioutil.ReadFile(golden)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
return string(expected)
|
||||||
|
}
|
||||||
|
|
||||||
|
func responseToJSON(t *testing.T, r *envoy.DiscoveryResponse) string {
|
||||||
|
t.Helper()
|
||||||
|
m := jsonpb.Marshaler{
|
||||||
|
Indent: " ",
|
||||||
|
}
|
||||||
|
gotJSON, err := m.MarshalToString(r)
|
||||||
|
require.NoError(t, err)
|
||||||
|
return gotJSON
|
||||||
|
}
|
|
@ -4,13 +4,17 @@ import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
|
||||||
envoy "github.com/envoyproxy/go-control-plane/envoy/api/v2"
|
envoy "github.com/envoyproxy/go-control-plane/envoy/api/v2"
|
||||||
envoyauth "github.com/envoyproxy/go-control-plane/envoy/api/v2/auth"
|
envoyauth "github.com/envoyproxy/go-control-plane/envoy/api/v2/auth"
|
||||||
envoycore "github.com/envoyproxy/go-control-plane/envoy/api/v2/core"
|
envoycore "github.com/envoyproxy/go-control-plane/envoy/api/v2/core"
|
||||||
envoylistener "github.com/envoyproxy/go-control-plane/envoy/api/v2/listener"
|
envoylistener "github.com/envoyproxy/go-control-plane/envoy/api/v2/listener"
|
||||||
|
envoyroute "github.com/envoyproxy/go-control-plane/envoy/api/v2/route"
|
||||||
extauthz "github.com/envoyproxy/go-control-plane/envoy/config/filter/network/ext_authz/v2"
|
extauthz "github.com/envoyproxy/go-control-plane/envoy/config/filter/network/ext_authz/v2"
|
||||||
|
envoyhttp "github.com/envoyproxy/go-control-plane/envoy/config/filter/network/http_connection_manager/v2"
|
||||||
envoytcp "github.com/envoyproxy/go-control-plane/envoy/config/filter/network/tcp_proxy/v2"
|
envoytcp "github.com/envoyproxy/go-control-plane/envoy/config/filter/network/tcp_proxy/v2"
|
||||||
|
envoytype "github.com/envoyproxy/go-control-plane/envoy/type"
|
||||||
"github.com/envoyproxy/go-control-plane/pkg/util"
|
"github.com/envoyproxy/go-control-plane/pkg/util"
|
||||||
"github.com/gogo/protobuf/jsonpb"
|
"github.com/gogo/protobuf/jsonpb"
|
||||||
"github.com/gogo/protobuf/proto"
|
"github.com/gogo/protobuf/proto"
|
||||||
|
@ -22,7 +26,7 @@ import (
|
||||||
|
|
||||||
// listenersFromSnapshot returns the xDS API representation of the "listeners"
|
// listenersFromSnapshot returns the xDS API representation of the "listeners"
|
||||||
// in the snapshot.
|
// in the snapshot.
|
||||||
func listenersFromSnapshot(cfgSnap *proxycfg.ConfigSnapshot, token string) ([]proto.Message, error) {
|
func (s *Server) listenersFromSnapshot(cfgSnap *proxycfg.ConfigSnapshot, token string) ([]proto.Message, error) {
|
||||||
if cfgSnap == nil {
|
if cfgSnap == nil {
|
||||||
return nil, errors.New("nil config given")
|
return nil, errors.New("nil config given")
|
||||||
}
|
}
|
||||||
|
@ -32,12 +36,12 @@ func listenersFromSnapshot(cfgSnap *proxycfg.ConfigSnapshot, token string) ([]pr
|
||||||
|
|
||||||
// Configure public listener
|
// Configure public listener
|
||||||
var err error
|
var err error
|
||||||
resources[0], err = makePublicListener(cfgSnap, token)
|
resources[0], err = s.makePublicListener(cfgSnap, token)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
for i, u := range cfgSnap.Proxy.Upstreams {
|
for i, u := range cfgSnap.Proxy.Upstreams {
|
||||||
resources[i+1], err = makeUpstreamListener(&u)
|
resources[i+1], err = s.makeUpstreamListener(&u)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -137,17 +141,23 @@ func injectConnectFilters(cfgSnap *proxycfg.ConfigSnapshot, token string, listen
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func makePublicListener(cfgSnap *proxycfg.ConfigSnapshot, token string) (proto.Message, error) {
|
func (s *Server) makePublicListener(cfgSnap *proxycfg.ConfigSnapshot, token string) (proto.Message, error) {
|
||||||
var l *envoy.Listener
|
var l *envoy.Listener
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
if listenerJSONRaw, ok := cfgSnap.Proxy.Config["envoy_public_listener_json"]; ok {
|
cfg, err := ParseProxyConfig(cfgSnap.Proxy.Config)
|
||||||
if listenerJSON, ok := listenerJSONRaw.(string); ok {
|
if err != nil {
|
||||||
l, err = makeListenerFromUserConfig(listenerJSON)
|
// Don't hard fail on a config typo, just warn. The parse func returns
|
||||||
if err != nil {
|
// default config if there is an error so it's safe to continue.
|
||||||
return l, err
|
s.Logger.Printf("[WARN] envoy: failed to parse Connect.Proxy.Config: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if cfg.PublicListenerJSON != "" {
|
||||||
|
l, err = makeListenerFromUserConfig(cfg.PublicListenerJSON)
|
||||||
|
if err != nil {
|
||||||
|
return l, err
|
||||||
}
|
}
|
||||||
|
// In the happy path don't return yet as we need to inject TLS config still.
|
||||||
}
|
}
|
||||||
|
|
||||||
if l == nil {
|
if l == nil {
|
||||||
|
@ -157,16 +167,15 @@ func makePublicListener(cfgSnap *proxycfg.ConfigSnapshot, token string) (proto.M
|
||||||
addr = "0.0.0.0"
|
addr = "0.0.0.0"
|
||||||
}
|
}
|
||||||
l = makeListener(PublicListenerName, addr, cfgSnap.Port)
|
l = makeListener(PublicListenerName, addr, cfgSnap.Port)
|
||||||
tcpProxy, err := makeTCPProxyFilter("public_listener", LocalAppClusterName)
|
|
||||||
|
filter, err := makeListenerFilter(cfg.Protocol, "public_listener", LocalAppClusterName, "", true)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return l, err
|
return nil, err
|
||||||
}
|
}
|
||||||
// Setup TCP proxy for now. We inject TLS and authz below for both default
|
|
||||||
// and custom config cases.
|
|
||||||
l.FilterChains = []envoylistener.FilterChain{
|
l.FilterChains = []envoylistener.FilterChain{
|
||||||
{
|
{
|
||||||
Filters: []envoylistener.Filter{
|
Filters: []envoylistener.Filter{
|
||||||
tcpProxy,
|
filter,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -176,39 +185,141 @@ func makePublicListener(cfgSnap *proxycfg.ConfigSnapshot, token string) (proto.M
|
||||||
return l, err
|
return l, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func makeUpstreamListener(u *structs.Upstream) (proto.Message, error) {
|
func (s *Server) makeUpstreamListener(u *structs.Upstream) (proto.Message, error) {
|
||||||
if listenerJSONRaw, ok := u.Config["envoy_listener_json"]; ok {
|
cfg, err := ParseUpstreamConfig(u.Config)
|
||||||
if listenerJSON, ok := listenerJSONRaw.(string); ok {
|
if err != nil {
|
||||||
return makeListenerFromUserConfig(listenerJSON)
|
// Don't hard fail on a config typo, just warn. The parse func returns
|
||||||
}
|
// default config if there is an error so it's safe to continue.
|
||||||
|
s.Logger.Printf("[WARN] envoy: failed to parse Upstream[%s].Config: %s",
|
||||||
|
u.Identifier(), err)
|
||||||
}
|
}
|
||||||
|
if cfg.ListenerJSON != "" {
|
||||||
|
return makeListenerFromUserConfig(cfg.ListenerJSON)
|
||||||
|
}
|
||||||
|
|
||||||
addr := u.LocalBindAddress
|
addr := u.LocalBindAddress
|
||||||
if addr == "" {
|
if addr == "" {
|
||||||
addr = "127.0.0.1"
|
addr = "127.0.0.1"
|
||||||
}
|
}
|
||||||
|
|
||||||
l := makeListener(u.Identifier(), addr, u.LocalBindPort)
|
l := makeListener(u.Identifier(), addr, u.LocalBindPort)
|
||||||
tcpProxy, err := makeTCPProxyFilter(u.Identifier(), u.Identifier())
|
filter, err := makeListenerFilter(cfg.Protocol, u.Identifier(), u.Identifier(), "upstream_", false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return l, err
|
return nil, err
|
||||||
}
|
}
|
||||||
l.FilterChains = []envoylistener.FilterChain{
|
l.FilterChains = []envoylistener.FilterChain{
|
||||||
{
|
{
|
||||||
Filters: []envoylistener.Filter{
|
Filters: []envoylistener.Filter{
|
||||||
tcpProxy,
|
filter,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
return l, nil
|
return l, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func makeTCPProxyFilter(name, cluster string) (envoylistener.Filter, error) {
|
func makeListenerFilter(protocol, filterName, cluster, statPrefix string, ingress bool) (envoylistener.Filter, error) {
|
||||||
|
switch protocol {
|
||||||
|
case "grpc":
|
||||||
|
return makeHTTPFilter(filterName, cluster, statPrefix, ingress, true, true)
|
||||||
|
case "http2":
|
||||||
|
return makeHTTPFilter(filterName, cluster, statPrefix, ingress, false, true)
|
||||||
|
case "http":
|
||||||
|
return makeHTTPFilter(filterName, cluster, statPrefix, ingress, false, false)
|
||||||
|
case "tcp":
|
||||||
|
fallthrough
|
||||||
|
default:
|
||||||
|
return makeTCPProxyFilter(filterName, cluster, statPrefix)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func makeTCPProxyFilter(filterName, cluster, statPrefix string) (envoylistener.Filter, error) {
|
||||||
cfg := &envoytcp.TcpProxy{
|
cfg := &envoytcp.TcpProxy{
|
||||||
StatPrefix: name,
|
StatPrefix: makeStatPrefix("tcp", statPrefix, filterName),
|
||||||
Cluster: cluster,
|
Cluster: cluster,
|
||||||
}
|
}
|
||||||
return makeFilter("envoy.tcp_proxy", cfg)
|
return makeFilter("envoy.tcp_proxy", cfg)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func makeStatPrefix(protocol, prefix, filterName string) string {
|
||||||
|
// Replace colons here because Envoy does that in the metrics for the actual
|
||||||
|
// clusters but doesn't in the stat prefix here while dashboards assume they
|
||||||
|
// will match.
|
||||||
|
return fmt.Sprintf("%s%s_%s", prefix, strings.Replace(filterName, ":", "_", -1), protocol)
|
||||||
|
}
|
||||||
|
|
||||||
|
func makeHTTPFilter(filterName, cluster, statPrefix string, ingress, grpc, http2 bool) (envoylistener.Filter, error) {
|
||||||
|
op := envoyhttp.INGRESS
|
||||||
|
if !ingress {
|
||||||
|
op = envoyhttp.EGRESS
|
||||||
|
}
|
||||||
|
proto := "http"
|
||||||
|
if grpc {
|
||||||
|
proto = "grpc"
|
||||||
|
}
|
||||||
|
cfg := &envoyhttp.HttpConnectionManager{
|
||||||
|
StatPrefix: makeStatPrefix(proto, statPrefix, filterName),
|
||||||
|
CodecType: envoyhttp.AUTO,
|
||||||
|
RouteSpecifier: &envoyhttp.HttpConnectionManager_RouteConfig{
|
||||||
|
RouteConfig: &envoy.RouteConfiguration{
|
||||||
|
Name: filterName,
|
||||||
|
VirtualHosts: []envoyroute.VirtualHost{
|
||||||
|
envoyroute.VirtualHost{
|
||||||
|
Name: filterName,
|
||||||
|
Domains: []string{"*"},
|
||||||
|
Routes: []envoyroute.Route{
|
||||||
|
envoyroute.Route{
|
||||||
|
Match: envoyroute.RouteMatch{
|
||||||
|
PathSpecifier: &envoyroute.RouteMatch_Prefix{
|
||||||
|
Prefix: "/",
|
||||||
|
},
|
||||||
|
// TODO(banks) Envoy supports matching only valid GRPC
|
||||||
|
// requests which might be nice to add here for gRPC services
|
||||||
|
// but it's not supported in our current envoy SDK version
|
||||||
|
// although docs say it was supported by 1.8.0. Going to defer
|
||||||
|
// that until we've updated the deps.
|
||||||
|
},
|
||||||
|
Action: &envoyroute.Route_Route{
|
||||||
|
Route: &envoyroute.RouteAction{
|
||||||
|
ClusterSpecifier: &envoyroute.RouteAction_Cluster{
|
||||||
|
Cluster: cluster,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
HttpFilters: []*envoyhttp.HttpFilter{
|
||||||
|
&envoyhttp.HttpFilter{
|
||||||
|
Name: "envoy.router",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Tracing: &envoyhttp.HttpConnectionManager_Tracing{
|
||||||
|
OperationName: op,
|
||||||
|
// Don't trace any requests by default unless the client application
|
||||||
|
// explicitly propagates trace headers that indicate this should be
|
||||||
|
// sampled.
|
||||||
|
RandomSampling: &envoytype.Percent{Value: 0.0},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
if http2 {
|
||||||
|
cfg.Http2ProtocolOptions = &envoycore.Http2ProtocolOptions{}
|
||||||
|
}
|
||||||
|
|
||||||
|
if grpc {
|
||||||
|
// Add grpc bridge before router
|
||||||
|
cfg.HttpFilters = append([]*envoyhttp.HttpFilter{&envoyhttp.HttpFilter{
|
||||||
|
Name: "envoy.grpc_http1_bridge",
|
||||||
|
Config: &types.Struct{},
|
||||||
|
}}, cfg.HttpFilters...)
|
||||||
|
}
|
||||||
|
|
||||||
|
return makeFilter("envoy.http_connection_manager", cfg)
|
||||||
|
}
|
||||||
|
|
||||||
func makeExtAuthFilter(token string) (envoylistener.Filter, error) {
|
func makeExtAuthFilter(token string) (envoylistener.Filter, error) {
|
||||||
cfg := &extauthz.ExtAuthz{
|
cfg := &extauthz.ExtAuthz{
|
||||||
StatPrefix: "connect_authz",
|
StatPrefix: "connect_authz",
|
||||||
|
|
|
@ -0,0 +1,327 @@
|
||||||
|
package xds
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
"path"
|
||||||
|
"testing"
|
||||||
|
"text/template"
|
||||||
|
|
||||||
|
"github.com/hashicorp/consul/agent/proxycfg"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestListenersFromSnapshot(t *testing.T) {
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
// Setup is called before the test starts. It is passed the snapshot from
|
||||||
|
// TestConfigSnapshot and is allowed to modify it in any way to setup the
|
||||||
|
// test input.
|
||||||
|
setup func(snap *proxycfg.ConfigSnapshot)
|
||||||
|
overrideGoldenName string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "defaults",
|
||||||
|
setup: nil, // Default snapshot
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "http-public-listener",
|
||||||
|
setup: func(snap *proxycfg.ConfigSnapshot) {
|
||||||
|
snap.Proxy.Config["protocol"] = "http"
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "http-upstream",
|
||||||
|
setup: func(snap *proxycfg.ConfigSnapshot) {
|
||||||
|
snap.Proxy.Upstreams[0].Config["protocol"] = "http"
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "custom-public-listener",
|
||||||
|
setup: func(snap *proxycfg.ConfigSnapshot) {
|
||||||
|
snap.Proxy.Config["envoy_public_listener_json"] =
|
||||||
|
customListenerJSON(t, customListenerJSONOptions{
|
||||||
|
Name: "custom-public-listen",
|
||||||
|
IncludeType: false,
|
||||||
|
})
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "custom-public-listener-typed",
|
||||||
|
overrideGoldenName: "custom-public-listener", // should be the same
|
||||||
|
setup: func(snap *proxycfg.ConfigSnapshot) {
|
||||||
|
snap.Proxy.Config["envoy_public_listener_json"] =
|
||||||
|
customListenerJSON(t, customListenerJSONOptions{
|
||||||
|
Name: "custom-public-listen",
|
||||||
|
IncludeType: true,
|
||||||
|
})
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "custom-public-listener-ignores-tls",
|
||||||
|
overrideGoldenName: "custom-public-listener", // should be the same
|
||||||
|
setup: func(snap *proxycfg.ConfigSnapshot) {
|
||||||
|
snap.Proxy.Config["envoy_public_listener_json"] =
|
||||||
|
customListenerJSON(t, customListenerJSONOptions{
|
||||||
|
Name: "custom-public-listen",
|
||||||
|
IncludeType: true,
|
||||||
|
// Attempt to override the TLS context should be ignored
|
||||||
|
TLSContext: `{"requireClientCertificate": false}`,
|
||||||
|
})
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "custom-upstream",
|
||||||
|
setup: func(snap *proxycfg.ConfigSnapshot) {
|
||||||
|
snap.Proxy.Upstreams[0].Config["envoy_listener_json"] =
|
||||||
|
customListenerJSON(t, customListenerJSONOptions{
|
||||||
|
Name: "custom-upstream",
|
||||||
|
IncludeType: false,
|
||||||
|
})
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "custom-upstream-typed",
|
||||||
|
overrideGoldenName: "custom-upstream", // should be the same
|
||||||
|
setup: func(snap *proxycfg.ConfigSnapshot) {
|
||||||
|
snap.Proxy.Upstreams[0].Config["envoy_listener_json"] =
|
||||||
|
customListenerJSON(t, customListenerJSONOptions{
|
||||||
|
Name: "custom-upstream",
|
||||||
|
IncludeType: true,
|
||||||
|
})
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
require := require.New(t)
|
||||||
|
|
||||||
|
// Sanity check default with no overrides first
|
||||||
|
snap := proxycfg.TestConfigSnapshot(t)
|
||||||
|
|
||||||
|
// We need to replace the TLS certs with deterministic ones to make golden
|
||||||
|
// files workable. Note we don't update these otherwise they'd change
|
||||||
|
// golder files for every test case and so not be any use!
|
||||||
|
snap.Leaf.CertPEM = golden(t, "test-leaf-cert", "")
|
||||||
|
snap.Leaf.PrivateKeyPEM = golden(t, "test-leaf-key", "")
|
||||||
|
snap.Roots.Roots[0].RootCert = golden(t, "test-root-cert", "")
|
||||||
|
|
||||||
|
if tt.setup != nil {
|
||||||
|
tt.setup(snap)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Need server just for logger dependency
|
||||||
|
s := Server{Logger: log.New(os.Stderr, "", log.LstdFlags)}
|
||||||
|
|
||||||
|
listeners, err := s.listenersFromSnapshot(snap, "my-token")
|
||||||
|
require.NoError(err)
|
||||||
|
r, err := createResponse(ListenerType, "00000001", "00000001", listeners)
|
||||||
|
require.NoError(err)
|
||||||
|
|
||||||
|
gotJSON := responseToJSON(t, r)
|
||||||
|
|
||||||
|
gName := tt.name
|
||||||
|
if tt.overrideGoldenName != "" {
|
||||||
|
gName = tt.overrideGoldenName
|
||||||
|
}
|
||||||
|
|
||||||
|
require.JSONEq(golden(t, path.Join("listeners", gName), gotJSON), gotJSON)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func expectListenerJSONResources(t *testing.T, snap *proxycfg.ConfigSnapshot, token string, v, n uint64) map[string]string {
|
||||||
|
tokenVal := ""
|
||||||
|
if token != "" {
|
||||||
|
tokenVal = fmt.Sprintf(",\n"+`"value": "%s"`, token)
|
||||||
|
}
|
||||||
|
return map[string]string{
|
||||||
|
"public_listener": `{
|
||||||
|
"@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": [
|
||||||
|
{
|
||||||
|
"tlsContext": ` + expectedPublicTLSContextJSON(t, snap) + `,
|
||||||
|
"filters": [
|
||||||
|
{
|
||||||
|
"name": "envoy.ext_authz",
|
||||||
|
"config": {
|
||||||
|
"grpc_service": {
|
||||||
|
"envoy_grpc": {
|
||||||
|
"cluster_name": "local_agent"
|
||||||
|
},
|
||||||
|
"initial_metadata": [
|
||||||
|
{
|
||||||
|
"key": "x-consul-token"
|
||||||
|
` + tokenVal + `
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"stat_prefix": "connect_authz"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "envoy.tcp_proxy",
|
||||||
|
"config": {
|
||||||
|
"cluster": "local_app",
|
||||||
|
"stat_prefix": "public_listener_tcp"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}`,
|
||||||
|
"db": `{
|
||||||
|
"@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.tcp_proxy",
|
||||||
|
"config": {
|
||||||
|
"cluster": "db",
|
||||||
|
"stat_prefix": "upstream_db_tcp"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}`,
|
||||||
|
"prepared_query:geo-cache": `{
|
||||||
|
"@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.tcp_proxy",
|
||||||
|
"config": {
|
||||||
|
"cluster": "prepared_query:geo-cache",
|
||||||
|
"stat_prefix": "upstream_prepared_query_geo-cache_tcp"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}`,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func expectListenerJSONFromResources(t *testing.T, snap *proxycfg.ConfigSnapshot, token string, v, n uint64, resourcesJSON map[string]string) string {
|
||||||
|
resJSON := ""
|
||||||
|
// Sort resources into specific order because that matters in JSONEq
|
||||||
|
// comparison later.
|
||||||
|
keyOrder := []string{"public_listener"}
|
||||||
|
for _, u := range snap.Proxy.Upstreams {
|
||||||
|
keyOrder = append(keyOrder, u.Identifier())
|
||||||
|
}
|
||||||
|
for _, k := range keyOrder {
|
||||||
|
j, ok := resourcesJSON[k]
|
||||||
|
if !ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if resJSON != "" {
|
||||||
|
resJSON += ",\n"
|
||||||
|
}
|
||||||
|
resJSON += j
|
||||||
|
}
|
||||||
|
return `{
|
||||||
|
"versionInfo": "` + hexString(v) + `",
|
||||||
|
"resources": [` + resJSON + `],
|
||||||
|
"typeUrl": "type.googleapis.com/envoy.api.v2.Listener",
|
||||||
|
"nonce": "` + hexString(n) + `"
|
||||||
|
}`
|
||||||
|
}
|
||||||
|
|
||||||
|
func expectListenerJSON(t *testing.T, snap *proxycfg.ConfigSnapshot, token string, v, n uint64) string {
|
||||||
|
return expectListenerJSONFromResources(t, snap, token, v, n,
|
||||||
|
expectListenerJSONResources(t, snap, token, v, n))
|
||||||
|
}
|
||||||
|
|
||||||
|
type customListenerJSONOptions struct {
|
||||||
|
Name string
|
||||||
|
IncludeType bool
|
||||||
|
OverrideAuthz bool
|
||||||
|
TLSContext string
|
||||||
|
}
|
||||||
|
|
||||||
|
const customListenerJSONTpl = `{
|
||||||
|
{{ if .IncludeType -}}
|
||||||
|
"@type": "type.googleapis.com/envoy.api.v2.Listener",
|
||||||
|
{{- end }}
|
||||||
|
"name": "{{ .Name }}",
|
||||||
|
"address": {
|
||||||
|
"socketAddress": {
|
||||||
|
"address": "11.11.11.11",
|
||||||
|
"portValue": 11111
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"filterChains": [
|
||||||
|
{
|
||||||
|
{{ if .TLSContext -}}
|
||||||
|
"tlsContext": {{ .TLSContext }},
|
||||||
|
{{- end }}
|
||||||
|
"filters": [
|
||||||
|
{{ if .OverrideAuthz -}}
|
||||||
|
{
|
||||||
|
"name": "envoy.ext_authz",
|
||||||
|
"config": {
|
||||||
|
"grpc_service": {
|
||||||
|
"envoy_grpc": {
|
||||||
|
"cluster_name": "local_agent"
|
||||||
|
},
|
||||||
|
"initial_metadata": [
|
||||||
|
{
|
||||||
|
"key": "x-consul-token",
|
||||||
|
"value": "my-token"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"stat_prefix": "connect_authz"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{{- end }}
|
||||||
|
{
|
||||||
|
"name": "envoy.tcp_proxy",
|
||||||
|
"config": {
|
||||||
|
"cluster": "random-cluster",
|
||||||
|
"stat_prefix": "foo-stats"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}`
|
||||||
|
|
||||||
|
var customListenerJSONTemplate = template.Must(template.New("").Parse(customListenerJSONTpl))
|
||||||
|
|
||||||
|
func customListenerJSON(t *testing.T, opts customListenerJSONOptions) string {
|
||||||
|
t.Helper()
|
||||||
|
var buf bytes.Buffer
|
||||||
|
err := customListenerJSONTemplate.Execute(&buf, opts)
|
||||||
|
require.NoError(t, err)
|
||||||
|
return buf.String()
|
||||||
|
}
|
|
@ -51,11 +51,31 @@ const (
|
||||||
PublicListenerName = "public_listener"
|
PublicListenerName = "public_listener"
|
||||||
|
|
||||||
// LocalAppClusterName is the name we give the local application "cluster" in
|
// LocalAppClusterName is the name we give the local application "cluster" in
|
||||||
// Envoy config.
|
// Envoy config. Note that all cluster names may collide with service names
|
||||||
|
// since we want cluster names and service names to match to enable nice
|
||||||
|
// metrics correlation without massaging prefixes on cluster names.
|
||||||
|
//
|
||||||
|
// We should probably make this more unlikely to collide however changing it
|
||||||
|
// potentially breaks upgrade compatibility without restarting all Envoy's as
|
||||||
|
// it will no longer match their existing cluster name. Changing this will
|
||||||
|
// affect metrics output so could break dashboards (for local app traffic).
|
||||||
|
//
|
||||||
|
// We should probably just make it configurable if anyone actually has
|
||||||
|
// services named "local_app" in the future.
|
||||||
LocalAppClusterName = "local_app"
|
LocalAppClusterName = "local_app"
|
||||||
|
|
||||||
// LocalAgentClusterName is the name we give the local agent "cluster" in
|
// LocalAgentClusterName is the name we give the local agent "cluster" in
|
||||||
// Envoy config.
|
// Envoy config. Note that all cluster names may collide with service names
|
||||||
|
// since we want cluster names and service names to match to enable nice
|
||||||
|
// metrics correlation without massaging prefixes on cluster names.
|
||||||
|
//
|
||||||
|
// We should probably make this more unlikely to collied however changing it
|
||||||
|
// potentially breaks upgrade compatibility without restarting all Envoy's as
|
||||||
|
// it will no longer match their existing cluster name. Changing this will
|
||||||
|
// affect metrics output so could break dashboards (for local agent traffic).
|
||||||
|
//
|
||||||
|
// We should probably just make it configurable if anyone actually has
|
||||||
|
// services named "local_agent" in the future.
|
||||||
LocalAgentClusterName = "local_agent"
|
LocalAgentClusterName = "local_agent"
|
||||||
|
|
||||||
// DefaultAuthCheckFrequency is the default value for
|
// DefaultAuthCheckFrequency is the default value for
|
||||||
|
@ -178,7 +198,7 @@ func (s *Server) process(stream ADSStream, reqCh <-chan *envoy.DiscoveryRequest)
|
||||||
},
|
},
|
||||||
ClusterType: &xDSType{
|
ClusterType: &xDSType{
|
||||||
typeURL: ClusterType,
|
typeURL: ClusterType,
|
||||||
resources: clustersFromSnapshot,
|
resources: s.clustersFromSnapshot,
|
||||||
stream: stream,
|
stream: stream,
|
||||||
},
|
},
|
||||||
RouteType: &xDSType{
|
RouteType: &xDSType{
|
||||||
|
@ -188,7 +208,7 @@ func (s *Server) process(stream ADSStream, reqCh <-chan *envoy.DiscoveryRequest)
|
||||||
},
|
},
|
||||||
ListenerType: &xDSType{
|
ListenerType: &xDSType{
|
||||||
typeURL: ListenerType,
|
typeURL: ListenerType,
|
||||||
resources: listenersFromSnapshot,
|
resources: s.listenersFromSnapshot,
|
||||||
stream: stream,
|
stream: stream,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -421,6 +441,8 @@ func (s *Server) Check(ctx context.Context, r *envoyauthz.CheckRequest) (*envoya
|
||||||
// Parse destination to know the target service
|
// Parse destination to know the target service
|
||||||
dest, err := connect.ParseCertURIFromString(r.Attributes.Destination.Principal)
|
dest, err := connect.ParseCertURIFromString(r.Attributes.Destination.Principal)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
s.Logger.Printf("[DEBUG] grpc: Connect AuthZ DENIED: bad destination URI: src=%s dest=%s",
|
||||||
|
r.Attributes.Source.Principal, r.Attributes.Destination.Principal)
|
||||||
// Treat this as an auth error since Envoy has sent something it considers
|
// Treat this as an auth error since Envoy has sent something it considers
|
||||||
// valid, it's just not an identity we trust.
|
// valid, it's just not an identity we trust.
|
||||||
return deniedResponse("Destination Principal is not a valid Connect identity")
|
return deniedResponse("Destination Principal is not a valid Connect identity")
|
||||||
|
@ -428,6 +450,8 @@ func (s *Server) Check(ctx context.Context, r *envoyauthz.CheckRequest) (*envoya
|
||||||
|
|
||||||
destID, ok := dest.(*connect.SpiffeIDService)
|
destID, ok := dest.(*connect.SpiffeIDService)
|
||||||
if !ok {
|
if !ok {
|
||||||
|
s.Logger.Printf("[DEBUG] grpc: Connect AuthZ DENIED: bad destination service ID: src=%s dest=%s",
|
||||||
|
r.Attributes.Source.Principal, r.Attributes.Destination.Principal)
|
||||||
return deniedResponse("Destination Principal is not a valid Service identity")
|
return deniedResponse("Destination Principal is not a valid Service identity")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -452,14 +476,22 @@ func (s *Server) Check(ctx context.Context, r *envoyauthz.CheckRequest) (*envoya
|
||||||
authed, reason, _, err := s.Authz.ConnectAuthorize(token, req)
|
authed, reason, _, err := s.Authz.ConnectAuthorize(token, req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if err == acl.ErrPermissionDenied {
|
if err == acl.ErrPermissionDenied {
|
||||||
|
s.Logger.Printf("[DEBUG] grpc: Connect AuthZ failed ACL check: %s: src=%s dest=%s",
|
||||||
|
err, r.Attributes.Source.Principal, r.Attributes.Destination.Principal)
|
||||||
return nil, status.Error(codes.PermissionDenied, err.Error())
|
return nil, status.Error(codes.PermissionDenied, err.Error())
|
||||||
}
|
}
|
||||||
|
s.Logger.Printf("[DEBUG] grpc: Connect AuthZ failed: %s: src=%s dest=%s",
|
||||||
|
err, r.Attributes.Source.Principal, r.Attributes.Destination.Principal)
|
||||||
return nil, status.Error(codes.Internal, err.Error())
|
return nil, status.Error(codes.Internal, err.Error())
|
||||||
}
|
}
|
||||||
if !authed {
|
if !authed {
|
||||||
|
s.Logger.Printf("[DEBUG] grpc: Connect AuthZ DENIED: src=%s dest=%s reason=%s",
|
||||||
|
r.Attributes.Source.Principal, r.Attributes.Destination.Principal, reason)
|
||||||
return deniedResponse(reason)
|
return deniedResponse(reason)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
s.Logger.Printf("[DEBUG] grpc: Connect AuthZ ALLOWED: src=%s dest=%s reason=%s",
|
||||||
|
r.Attributes.Source.Principal, r.Attributes.Destination.Principal, reason)
|
||||||
return &envoyauthz.CheckResponse{
|
return &envoyauthz.CheckResponse{
|
||||||
Status: &rpc.Status{
|
Status: &rpc.Status{
|
||||||
Code: int32(rpc.OK),
|
Code: int32(rpc.OK),
|
||||||
|
|
|
@ -1,21 +1,17 @@
|
||||||
package xds
|
package xds
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
|
||||||
"context"
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
|
||||||
"log"
|
"log"
|
||||||
"os"
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
"testing"
|
"testing"
|
||||||
"text/template"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
envoy "github.com/envoyproxy/go-control-plane/envoy/api/v2"
|
envoy "github.com/envoyproxy/go-control-plane/envoy/api/v2"
|
||||||
"github.com/gogo/protobuf/jsonpb"
|
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
"google.golang.org/grpc/codes"
|
"google.golang.org/grpc/codes"
|
||||||
"google.golang.org/grpc/metadata"
|
"google.golang.org/grpc/metadata"
|
||||||
|
@ -234,228 +230,13 @@ func TestServer_StreamAggregatedResources_BasicProtocol(t *testing.T) {
|
||||||
assertResponseSent(t, envoy.stream.sendCh, expectListenerJSON(t, snap, "", 3, 9))
|
assertResponseSent(t, envoy.stream.sendCh, expectListenerJSON(t, snap, "", 3, 9))
|
||||||
}
|
}
|
||||||
|
|
||||||
func expectListenerJSONResources(t *testing.T, snap *proxycfg.ConfigSnapshot, token string, v, n uint64) map[string]string {
|
|
||||||
tokenVal := ""
|
|
||||||
if token != "" {
|
|
||||||
tokenVal = fmt.Sprintf(",\n"+`"value": "%s"`, token)
|
|
||||||
}
|
|
||||||
return map[string]string{
|
|
||||||
"public_listener": `{
|
|
||||||
"@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": [
|
|
||||||
{
|
|
||||||
"tlsContext": ` + expectedPublicTLSContextJSON(t, snap) + `,
|
|
||||||
"filters": [
|
|
||||||
{
|
|
||||||
"name": "envoy.ext_authz",
|
|
||||||
"config": {
|
|
||||||
"grpc_service": {
|
|
||||||
"envoy_grpc": {
|
|
||||||
"cluster_name": "local_agent"
|
|
||||||
},
|
|
||||||
"initial_metadata": [
|
|
||||||
{
|
|
||||||
"key": "x-consul-token"
|
|
||||||
` + tokenVal + `
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"stat_prefix": "connect_authz"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "envoy.tcp_proxy",
|
|
||||||
"config": {
|
|
||||||
"cluster": "local_app",
|
|
||||||
"stat_prefix": "public_listener"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}`,
|
|
||||||
"service:db": `{
|
|
||||||
"@type": "type.googleapis.com/envoy.api.v2.Listener",
|
|
||||||
"name": "service:db:127.0.0.1:9191",
|
|
||||||
"address": {
|
|
||||||
"socketAddress": {
|
|
||||||
"address": "127.0.0.1",
|
|
||||||
"portValue": 9191
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"filterChains": [
|
|
||||||
{
|
|
||||||
"filters": [
|
|
||||||
{
|
|
||||||
"name": "envoy.tcp_proxy",
|
|
||||||
"config": {
|
|
||||||
"cluster": "service:db",
|
|
||||||
"stat_prefix": "service:db"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}`,
|
|
||||||
"prepared_query:geo-cache": `{
|
|
||||||
"@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.tcp_proxy",
|
|
||||||
"config": {
|
|
||||||
"cluster": "prepared_query:geo-cache",
|
|
||||||
"stat_prefix": "prepared_query:geo-cache"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}`,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func expectListenerJSONFromResources(t *testing.T, snap *proxycfg.ConfigSnapshot, token string, v, n uint64, resourcesJSON map[string]string) string {
|
|
||||||
resJSON := ""
|
|
||||||
// Sort resources into specific order because that matters in JSONEq
|
|
||||||
// comparison later.
|
|
||||||
keyOrder := []string{"public_listener"}
|
|
||||||
for _, u := range snap.Proxy.Upstreams {
|
|
||||||
keyOrder = append(keyOrder, u.Identifier())
|
|
||||||
}
|
|
||||||
for _, k := range keyOrder {
|
|
||||||
j, ok := resourcesJSON[k]
|
|
||||||
if !ok {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if resJSON != "" {
|
|
||||||
resJSON += ",\n"
|
|
||||||
}
|
|
||||||
resJSON += j
|
|
||||||
}
|
|
||||||
return `{
|
|
||||||
"versionInfo": "` + hexString(v) + `",
|
|
||||||
"resources": [` + resJSON + `],
|
|
||||||
"typeUrl": "type.googleapis.com/envoy.api.v2.Listener",
|
|
||||||
"nonce": "` + hexString(n) + `"
|
|
||||||
}`
|
|
||||||
}
|
|
||||||
|
|
||||||
func expectListenerJSON(t *testing.T, snap *proxycfg.ConfigSnapshot, token string, v, n uint64) string {
|
|
||||||
return expectListenerJSONFromResources(t, snap, token, v, n,
|
|
||||||
expectListenerJSONResources(t, snap, token, v, n))
|
|
||||||
}
|
|
||||||
|
|
||||||
func expectClustersJSONResources(t *testing.T, snap *proxycfg.ConfigSnapshot, token string, v, n uint64) map[string]string {
|
|
||||||
return map[string]string{
|
|
||||||
"local_app": `
|
|
||||||
{
|
|
||||||
"@type": "type.googleapis.com/envoy.api.v2.Cluster",
|
|
||||||
"name": "local_app",
|
|
||||||
"connectTimeout": "5s",
|
|
||||||
"hosts": [
|
|
||||||
{
|
|
||||||
"socketAddress": {
|
|
||||||
"address": "127.0.0.1",
|
|
||||||
"portValue": 8080
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}`,
|
|
||||||
"service:db": `
|
|
||||||
{
|
|
||||||
"@type": "type.googleapis.com/envoy.api.v2.Cluster",
|
|
||||||
"name": "service:db",
|
|
||||||
"type": "EDS",
|
|
||||||
"edsClusterConfig": {
|
|
||||||
"edsConfig": {
|
|
||||||
"ads": {
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"outlierDetection": {
|
|
||||||
|
|
||||||
},
|
|
||||||
"connectTimeout": "1s",
|
|
||||||
"tlsContext": ` + expectedUpstreamTLSContextJSON(t, snap) + `
|
|
||||||
}`,
|
|
||||||
"prepared_query:geo-cache": `
|
|
||||||
{
|
|
||||||
"@type": "type.googleapis.com/envoy.api.v2.Cluster",
|
|
||||||
"name": "prepared_query:geo-cache",
|
|
||||||
"type": "EDS",
|
|
||||||
"edsClusterConfig": {
|
|
||||||
"edsConfig": {
|
|
||||||
"ads": {
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"outlierDetection": {
|
|
||||||
|
|
||||||
},
|
|
||||||
"connectTimeout": "5s",
|
|
||||||
"tlsContext": ` + expectedUpstreamTLSContextJSON(t, snap) + `
|
|
||||||
}`,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func expectClustersJSONFromResources(t *testing.T, snap *proxycfg.ConfigSnapshot, token string, v, n uint64, resourcesJSON map[string]string) string {
|
|
||||||
resJSON := ""
|
|
||||||
|
|
||||||
// Sort resources into specific order because that matters in JSONEq
|
|
||||||
// comparison later.
|
|
||||||
keyOrder := []string{"local_app"}
|
|
||||||
for _, u := range snap.Proxy.Upstreams {
|
|
||||||
keyOrder = append(keyOrder, u.Identifier())
|
|
||||||
}
|
|
||||||
for _, k := range keyOrder {
|
|
||||||
j, ok := resourcesJSON[k]
|
|
||||||
if !ok {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if resJSON != "" {
|
|
||||||
resJSON += ",\n"
|
|
||||||
}
|
|
||||||
resJSON += j
|
|
||||||
}
|
|
||||||
|
|
||||||
return `{
|
|
||||||
"versionInfo": "` + hexString(v) + `",
|
|
||||||
"resources": [` + resJSON + `],
|
|
||||||
"typeUrl": "type.googleapis.com/envoy.api.v2.Cluster",
|
|
||||||
"nonce": "` + hexString(n) + `"
|
|
||||||
}`
|
|
||||||
}
|
|
||||||
|
|
||||||
func expectClustersJSON(t *testing.T, snap *proxycfg.ConfigSnapshot, token string, v, n uint64) string {
|
|
||||||
return expectClustersJSONFromResources(t, snap, token, v, n,
|
|
||||||
expectClustersJSONResources(t, snap, token, v, n))
|
|
||||||
}
|
|
||||||
|
|
||||||
func expectEndpointsJSON(t *testing.T, snap *proxycfg.ConfigSnapshot, token string, v, n uint64) string {
|
func expectEndpointsJSON(t *testing.T, snap *proxycfg.ConfigSnapshot, token string, v, n uint64) string {
|
||||||
return `{
|
return `{
|
||||||
"versionInfo": "` + hexString(v) + `",
|
"versionInfo": "` + hexString(v) + `",
|
||||||
"resources": [
|
"resources": [
|
||||||
{
|
{
|
||||||
"@type": "type.googleapis.com/envoy.api.v2.ClusterLoadAssignment",
|
"@type": "type.googleapis.com/envoy.api.v2.ClusterLoadAssignment",
|
||||||
"clusterName": "service:db",
|
"clusterName": "db",
|
||||||
"endpoints": [
|
"endpoints": [
|
||||||
{
|
{
|
||||||
"lbEndpoints": [
|
"lbEndpoints": [
|
||||||
|
@ -558,11 +339,7 @@ func assertResponseSent(t *testing.T, ch chan *envoy.DiscoveryResponse, wantJSON
|
||||||
// expected structs require the same code that is under test!
|
// expected structs require the same code that is under test!
|
||||||
func assertResponse(t *testing.T, r *envoy.DiscoveryResponse, wantJSON string) {
|
func assertResponse(t *testing.T, r *envoy.DiscoveryResponse, wantJSON string) {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
m := jsonpb.Marshaler{
|
gotJSON := responseToJSON(t, r)
|
||||||
Indent: " ",
|
|
||||||
}
|
|
||||||
gotJSON, err := m.MarshalToString(r)
|
|
||||||
require.NoError(t, err)
|
|
||||||
require.JSONEqf(t, wantJSON, gotJSON, "got:\n%s", gotJSON)
|
require.JSONEqf(t, wantJSON, gotJSON, "got:\n%s", gotJSON)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -591,21 +368,21 @@ func TestServer_StreamAggregatedResources_ACLEnforcement(t *testing.T) {
|
||||||
wantDenied: true,
|
wantDenied: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "default deny, service:write token",
|
name: "default deny, write token",
|
||||||
defaultDeny: true,
|
defaultDeny: true,
|
||||||
acl: `service "web" { policy = "write" }`,
|
acl: `service "web" { policy = "write" }`,
|
||||||
token: "service-write-on-web",
|
token: "service-write-on-web",
|
||||||
wantDenied: false,
|
wantDenied: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "default deny, service:read token",
|
name: "default deny, read token",
|
||||||
defaultDeny: true,
|
defaultDeny: true,
|
||||||
acl: `service "web" { policy = "read" }`,
|
acl: `service "web" { policy = "read" }`,
|
||||||
token: "service-write-on-web",
|
token: "service-write-on-web",
|
||||||
wantDenied: true,
|
wantDenied: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "default deny, service:write token on different service",
|
name: "default deny, write token on different service",
|
||||||
defaultDeny: true,
|
defaultDeny: true,
|
||||||
acl: `service "not-web" { policy = "write" }`,
|
acl: `service "not-web" { policy = "write" }`,
|
||||||
token: "service-write-on-not-web",
|
token: "service-write-on-not-web",
|
||||||
|
@ -1012,394 +789,3 @@ func TestServer_Check(t *testing.T) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestServer_ConfigOverridesListeners(t *testing.T) {
|
|
||||||
|
|
||||||
tests := []struct {
|
|
||||||
name string
|
|
||||||
setup func(snap *proxycfg.ConfigSnapshot) string
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
name: "sanity check no custom",
|
|
||||||
setup: func(snap *proxycfg.ConfigSnapshot) string {
|
|
||||||
// Default snap and expectation
|
|
||||||
return expectListenerJSON(t, snap, "my-token", 1, 1)
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "custom public_listener no type",
|
|
||||||
setup: func(snap *proxycfg.ConfigSnapshot) string {
|
|
||||||
snap.Proxy.Config["envoy_public_listener_json"] =
|
|
||||||
customListenerJSON(t, customListenerJSONOptions{
|
|
||||||
Name: "custom-public-listen",
|
|
||||||
IncludeType: false,
|
|
||||||
})
|
|
||||||
resources := expectListenerJSONResources(t, snap, "my-token", 1, 1)
|
|
||||||
|
|
||||||
// Replace the public listener with the custom one WITH type since
|
|
||||||
// that's how it comes out the other end, and with TLS and authz
|
|
||||||
// overridden.
|
|
||||||
resources["public_listener"] = customListenerJSON(t, customListenerJSONOptions{
|
|
||||||
Name: "custom-public-listen",
|
|
||||||
// We should add type, TLS and authz
|
|
||||||
IncludeType: true,
|
|
||||||
OverrideAuthz: true,
|
|
||||||
TLSContext: expectedPublicTLSContextJSON(t, snap),
|
|
||||||
})
|
|
||||||
return expectListenerJSONFromResources(t, snap, "my-token", 1, 1, resources)
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "custom public_listener with type",
|
|
||||||
setup: func(snap *proxycfg.ConfigSnapshot) string {
|
|
||||||
snap.Proxy.Config["envoy_public_listener_json"] =
|
|
||||||
customListenerJSON(t, customListenerJSONOptions{
|
|
||||||
Name: "custom-public-listen",
|
|
||||||
IncludeType: true,
|
|
||||||
})
|
|
||||||
resources := expectListenerJSONResources(t, snap, "my-token", 1, 1)
|
|
||||||
|
|
||||||
// Replace the public listener with the custom one WITH type since
|
|
||||||
// that's how it comes out the other end, and with TLS and authz
|
|
||||||
// overridden.
|
|
||||||
resources["public_listener"] = customListenerJSON(t, customListenerJSONOptions{
|
|
||||||
Name: "custom-public-listen",
|
|
||||||
// We should add type, TLS and authz
|
|
||||||
IncludeType: true,
|
|
||||||
OverrideAuthz: true,
|
|
||||||
TLSContext: expectedPublicTLSContextJSON(t, snap),
|
|
||||||
})
|
|
||||||
return expectListenerJSONFromResources(t, snap, "my-token", 1, 1, resources)
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "custom public_listener with TLS should be overridden",
|
|
||||||
setup: func(snap *proxycfg.ConfigSnapshot) string {
|
|
||||||
snap.Proxy.Config["envoy_public_listener_json"] =
|
|
||||||
customListenerJSON(t, customListenerJSONOptions{
|
|
||||||
Name: "custom-public-listen",
|
|
||||||
IncludeType: true,
|
|
||||||
TLSContext: `{"requireClientCertificate": false}`,
|
|
||||||
})
|
|
||||||
resources := expectListenerJSONResources(t, snap, "my-token", 1, 1)
|
|
||||||
|
|
||||||
// Replace the public listener with the custom one WITH type since
|
|
||||||
// that's how it comes out the other end, and with TLS and authz
|
|
||||||
// overridden.
|
|
||||||
resources["public_listener"] = customListenerJSON(t, customListenerJSONOptions{
|
|
||||||
Name: "custom-public-listen",
|
|
||||||
// We should add type, TLS and authz
|
|
||||||
IncludeType: true,
|
|
||||||
OverrideAuthz: true,
|
|
||||||
TLSContext: expectedPublicTLSContextJSON(t, snap),
|
|
||||||
})
|
|
||||||
return expectListenerJSONFromResources(t, snap, "my-token", 1, 1, resources)
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "custom upstream no type",
|
|
||||||
setup: func(snap *proxycfg.ConfigSnapshot) string {
|
|
||||||
snap.Proxy.Upstreams[0].Config["envoy_listener_json"] =
|
|
||||||
customListenerJSON(t, customListenerJSONOptions{
|
|
||||||
Name: "custom-upstream",
|
|
||||||
IncludeType: false,
|
|
||||||
})
|
|
||||||
resources := expectListenerJSONResources(t, snap, "my-token", 1, 1)
|
|
||||||
|
|
||||||
// Replace an upstream listener with the custom one WITH type since
|
|
||||||
// that's how it comes out the other end. Note we do override TLS
|
|
||||||
resources["service:db"] =
|
|
||||||
customListenerJSON(t, customListenerJSONOptions{
|
|
||||||
Name: "custom-upstream",
|
|
||||||
// We should add type
|
|
||||||
IncludeType: true,
|
|
||||||
})
|
|
||||||
return expectListenerJSONFromResources(t, snap, "my-token", 1, 1, resources)
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "custom upstream with type",
|
|
||||||
setup: func(snap *proxycfg.ConfigSnapshot) string {
|
|
||||||
snap.Proxy.Upstreams[0].Config["envoy_listener_json"] =
|
|
||||||
customListenerJSON(t, customListenerJSONOptions{
|
|
||||||
Name: "custom-upstream",
|
|
||||||
IncludeType: true,
|
|
||||||
})
|
|
||||||
resources := expectListenerJSONResources(t, snap, "my-token", 1, 1)
|
|
||||||
|
|
||||||
// Replace an upstream listener with the custom one WITH type since
|
|
||||||
// that's how it comes out the other end.
|
|
||||||
resources["service:db"] =
|
|
||||||
customListenerJSON(t, customListenerJSONOptions{
|
|
||||||
Name: "custom-upstream",
|
|
||||||
// We should add type
|
|
||||||
IncludeType: true,
|
|
||||||
})
|
|
||||||
return expectListenerJSONFromResources(t, snap, "my-token", 1, 1, resources)
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, tt := range tests {
|
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
|
||||||
require := require.New(t)
|
|
||||||
|
|
||||||
// Sanity check default with no overrides first
|
|
||||||
snap := proxycfg.TestConfigSnapshot(t)
|
|
||||||
expect := tt.setup(snap)
|
|
||||||
|
|
||||||
listeners, err := listenersFromSnapshot(snap, "my-token")
|
|
||||||
require.NoError(err)
|
|
||||||
r, err := createResponse(ListenerType, "00000001", "00000001", listeners)
|
|
||||||
require.NoError(err)
|
|
||||||
|
|
||||||
assertResponse(t, r, expect)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestServer_ConfigOverridesClusters(t *testing.T) {
|
|
||||||
|
|
||||||
tests := []struct {
|
|
||||||
name string
|
|
||||||
setup func(snap *proxycfg.ConfigSnapshot) string
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
name: "sanity check no custom",
|
|
||||||
setup: func(snap *proxycfg.ConfigSnapshot) string {
|
|
||||||
// Default snap and expectation
|
|
||||||
return expectClustersJSON(t, snap, "my-token", 1, 1)
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "custom public with no type",
|
|
||||||
setup: func(snap *proxycfg.ConfigSnapshot) string {
|
|
||||||
snap.Proxy.Config["envoy_local_cluster_json"] =
|
|
||||||
customAppClusterJSON(t, customClusterJSONOptions{
|
|
||||||
Name: "mylocal",
|
|
||||||
IncludeType: false,
|
|
||||||
})
|
|
||||||
resources := expectClustersJSONResources(t, snap, "my-token", 1, 1)
|
|
||||||
|
|
||||||
// Replace an upstream listener with the custom one WITH type since
|
|
||||||
// that's how it comes out the other end.
|
|
||||||
resources["local_app"] =
|
|
||||||
customAppClusterJSON(t, customClusterJSONOptions{
|
|
||||||
Name: "mylocal",
|
|
||||||
IncludeType: true,
|
|
||||||
})
|
|
||||||
return expectClustersJSONFromResources(t, snap, "my-token", 1, 1, resources)
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "custom public with type",
|
|
||||||
setup: func(snap *proxycfg.ConfigSnapshot) string {
|
|
||||||
snap.Proxy.Config["envoy_local_cluster_json"] =
|
|
||||||
customAppClusterJSON(t, customClusterJSONOptions{
|
|
||||||
Name: "mylocal",
|
|
||||||
IncludeType: true,
|
|
||||||
})
|
|
||||||
resources := expectClustersJSONResources(t, snap, "my-token", 1, 1)
|
|
||||||
|
|
||||||
// Replace an upstream listener with the custom one WITH type since
|
|
||||||
// that's how it comes out the other end.
|
|
||||||
resources["local_app"] =
|
|
||||||
customAppClusterJSON(t, customClusterJSONOptions{
|
|
||||||
Name: "mylocal",
|
|
||||||
IncludeType: true,
|
|
||||||
})
|
|
||||||
return expectClustersJSONFromResources(t, snap, "my-token", 1, 1, resources)
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "custom upstream with no type",
|
|
||||||
setup: func(snap *proxycfg.ConfigSnapshot) string {
|
|
||||||
snap.Proxy.Upstreams[0].Config["envoy_cluster_json"] =
|
|
||||||
customEDSClusterJSON(t, customClusterJSONOptions{
|
|
||||||
Name: "myservice",
|
|
||||||
IncludeType: false,
|
|
||||||
})
|
|
||||||
resources := expectClustersJSONResources(t, snap, "my-token", 1, 1)
|
|
||||||
|
|
||||||
// Replace an upstream listener with the custom one WITH type since
|
|
||||||
// that's how it comes out the other end.
|
|
||||||
resources["service:db"] =
|
|
||||||
customEDSClusterJSON(t, customClusterJSONOptions{
|
|
||||||
Name: "myservice",
|
|
||||||
IncludeType: true,
|
|
||||||
TLSContext: expectedUpstreamTLSContextJSON(t, snap),
|
|
||||||
})
|
|
||||||
return expectClustersJSONFromResources(t, snap, "my-token", 1, 1, resources)
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "custom upstream with type",
|
|
||||||
setup: func(snap *proxycfg.ConfigSnapshot) string {
|
|
||||||
snap.Proxy.Upstreams[0].Config["envoy_cluster_json"] =
|
|
||||||
customEDSClusterJSON(t, customClusterJSONOptions{
|
|
||||||
Name: "myservice",
|
|
||||||
IncludeType: true,
|
|
||||||
})
|
|
||||||
resources := expectClustersJSONResources(t, snap, "my-token", 1, 1)
|
|
||||||
|
|
||||||
// Replace an upstream listener with the custom one WITH type since
|
|
||||||
// that's how it comes out the other end.
|
|
||||||
resources["service:db"] =
|
|
||||||
customEDSClusterJSON(t, customClusterJSONOptions{
|
|
||||||
Name: "myservice",
|
|
||||||
IncludeType: true,
|
|
||||||
TLSContext: expectedUpstreamTLSContextJSON(t, snap),
|
|
||||||
})
|
|
||||||
return expectClustersJSONFromResources(t, snap, "my-token", 1, 1, resources)
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, tt := range tests {
|
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
|
||||||
require := require.New(t)
|
|
||||||
|
|
||||||
// Sanity check default with no overrides first
|
|
||||||
snap := proxycfg.TestConfigSnapshot(t)
|
|
||||||
expect := tt.setup(snap)
|
|
||||||
|
|
||||||
clusters, err := clustersFromSnapshot(snap, "my-token")
|
|
||||||
require.NoError(err)
|
|
||||||
r, err := createResponse(ClusterType, "00000001", "00000001", clusters)
|
|
||||||
require.NoError(err)
|
|
||||||
|
|
||||||
fmt.Println(r)
|
|
||||||
|
|
||||||
assertResponse(t, r, expect)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type customListenerJSONOptions struct {
|
|
||||||
Name string
|
|
||||||
IncludeType bool
|
|
||||||
OverrideAuthz bool
|
|
||||||
TLSContext string
|
|
||||||
}
|
|
||||||
|
|
||||||
const customListenerJSONTpl = `{
|
|
||||||
{{ if .IncludeType -}}
|
|
||||||
"@type": "type.googleapis.com/envoy.api.v2.Listener",
|
|
||||||
{{- end }}
|
|
||||||
"name": "{{ .Name }}",
|
|
||||||
"address": {
|
|
||||||
"socketAddress": {
|
|
||||||
"address": "11.11.11.11",
|
|
||||||
"portValue": 11111
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"filterChains": [
|
|
||||||
{
|
|
||||||
{{ if .TLSContext -}}
|
|
||||||
"tlsContext": {{ .TLSContext }},
|
|
||||||
{{- end }}
|
|
||||||
"filters": [
|
|
||||||
{{ if .OverrideAuthz -}}
|
|
||||||
{
|
|
||||||
"name": "envoy.ext_authz",
|
|
||||||
"config": {
|
|
||||||
"grpc_service": {
|
|
||||||
"envoy_grpc": {
|
|
||||||
"cluster_name": "local_agent"
|
|
||||||
},
|
|
||||||
"initial_metadata": [
|
|
||||||
{
|
|
||||||
"key": "x-consul-token",
|
|
||||||
"value": "my-token"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"stat_prefix": "connect_authz"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{{- end }}
|
|
||||||
{
|
|
||||||
"name": "envoy.tcp_proxy",
|
|
||||||
"config": {
|
|
||||||
"cluster": "random-cluster",
|
|
||||||
"stat_prefix": "foo-stats"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}`
|
|
||||||
|
|
||||||
var customListenerJSONTemplate = template.Must(template.New("").Parse(customListenerJSONTpl))
|
|
||||||
|
|
||||||
func customListenerJSON(t *testing.T, opts customListenerJSONOptions) string {
|
|
||||||
t.Helper()
|
|
||||||
var buf bytes.Buffer
|
|
||||||
err := customListenerJSONTemplate.Execute(&buf, opts)
|
|
||||||
require.NoError(t, err)
|
|
||||||
return buf.String()
|
|
||||||
}
|
|
||||||
|
|
||||||
type customClusterJSONOptions struct {
|
|
||||||
Name string
|
|
||||||
IncludeType bool
|
|
||||||
TLSContext string
|
|
||||||
}
|
|
||||||
|
|
||||||
var customEDSClusterJSONTpl = `{
|
|
||||||
{{ if .IncludeType -}}
|
|
||||||
"@type": "type.googleapis.com/envoy.api.v2.Cluster",
|
|
||||||
{{- end }}
|
|
||||||
{{ if .TLSContext -}}
|
|
||||||
"tlsContext": {{ .TLSContext }},
|
|
||||||
{{- end }}
|
|
||||||
"name": "{{ .Name }}",
|
|
||||||
"type": "EDS",
|
|
||||||
"edsClusterConfig": {
|
|
||||||
"edsConfig": {
|
|
||||||
"ads": {
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"connectTimeout": "5s"
|
|
||||||
}`
|
|
||||||
|
|
||||||
var customEDSClusterJSONTemplate = template.Must(template.New("").Parse(customEDSClusterJSONTpl))
|
|
||||||
|
|
||||||
func customEDSClusterJSON(t *testing.T, opts customClusterJSONOptions) string {
|
|
||||||
t.Helper()
|
|
||||||
var buf bytes.Buffer
|
|
||||||
err := customEDSClusterJSONTemplate.Execute(&buf, opts)
|
|
||||||
require.NoError(t, err)
|
|
||||||
return buf.String()
|
|
||||||
}
|
|
||||||
|
|
||||||
var customAppClusterJSONTpl = `{
|
|
||||||
{{ if .IncludeType -}}
|
|
||||||
"@type": "type.googleapis.com/envoy.api.v2.Cluster",
|
|
||||||
{{- end }}
|
|
||||||
{{ if .TLSContext -}}
|
|
||||||
"tlsContext": {{ .TLSContext }},
|
|
||||||
{{- end }}
|
|
||||||
"name": "{{ .Name }}",
|
|
||||||
"connectTimeout": "5s",
|
|
||||||
"hosts": [
|
|
||||||
{
|
|
||||||
"socketAddress": {
|
|
||||||
"address": "127.0.0.1",
|
|
||||||
"portValue": 8080
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}`
|
|
||||||
|
|
||||||
var customAppClusterJSONTemplate = template.Must(template.New("").Parse(customAppClusterJSONTpl))
|
|
||||||
|
|
||||||
func customAppClusterJSON(t *testing.T, opts customClusterJSONOptions) string {
|
|
||||||
t.Helper()
|
|
||||||
var buf bytes.Buffer
|
|
||||||
err := customAppClusterJSONTemplate.Execute(&buf, opts)
|
|
||||||
require.NoError(t, err)
|
|
||||||
return buf.String()
|
|
||||||
}
|
|
||||||
|
|
|
@ -0,0 +1,96 @@
|
||||||
|
{
|
||||||
|
"versionInfo": "00000001",
|
||||||
|
"resources": [
|
||||||
|
{
|
||||||
|
"@type": "type.googleapis.com/envoy.api.v2.Cluster",
|
||||||
|
"name": "mylocal",
|
||||||
|
"connectTimeout": "5s",
|
||||||
|
"hosts": [
|
||||||
|
{
|
||||||
|
"socketAddress": {
|
||||||
|
"address": "127.0.0.1",
|
||||||
|
"portValue": 8080
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"@type": "type.googleapis.com/envoy.api.v2.Cluster",
|
||||||
|
"name": "db",
|
||||||
|
"type": "EDS",
|
||||||
|
"edsClusterConfig": {
|
||||||
|
"edsConfig": {
|
||||||
|
"ads": {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"connectTimeout": "1s",
|
||||||
|
"tlsContext": {
|
||||||
|
"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"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"outlierDetection": {
|
||||||
|
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"@type": "type.googleapis.com/envoy.api.v2.Cluster",
|
||||||
|
"name": "prepared_query:geo-cache",
|
||||||
|
"type": "EDS",
|
||||||
|
"edsClusterConfig": {
|
||||||
|
"edsConfig": {
|
||||||
|
"ads": {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"connectTimeout": "5s",
|
||||||
|
"tlsContext": {
|
||||||
|
"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"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"outlierDetection": {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"typeUrl": "type.googleapis.com/envoy.api.v2.Cluster",
|
||||||
|
"nonce": "00000001"
|
||||||
|
}
|
|
@ -0,0 +1,107 @@
|
||||||
|
{
|
||||||
|
"versionInfo": "00000001",
|
||||||
|
"resources": [
|
||||||
|
{
|
||||||
|
"@type": "type.googleapis.com/envoy.api.v2.Cluster",
|
||||||
|
"name": "local_app",
|
||||||
|
"connectTimeout": "1.234s",
|
||||||
|
"loadAssignment": {
|
||||||
|
"clusterName": "local_app",
|
||||||
|
"endpoints": [
|
||||||
|
{
|
||||||
|
"lbEndpoints": [
|
||||||
|
{
|
||||||
|
"endpoint": {
|
||||||
|
"address": {
|
||||||
|
"socketAddress": {
|
||||||
|
"address": "127.0.0.1",
|
||||||
|
"portValue": 8080
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"@type": "type.googleapis.com/envoy.api.v2.Cluster",
|
||||||
|
"name": "db",
|
||||||
|
"type": "EDS",
|
||||||
|
"edsClusterConfig": {
|
||||||
|
"edsConfig": {
|
||||||
|
"ads": {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"connectTimeout": "2.345s",
|
||||||
|
"tlsContext": {
|
||||||
|
"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"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"outlierDetection": {
|
||||||
|
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"@type": "type.googleapis.com/envoy.api.v2.Cluster",
|
||||||
|
"name": "prepared_query:geo-cache",
|
||||||
|
"type": "EDS",
|
||||||
|
"edsClusterConfig": {
|
||||||
|
"edsConfig": {
|
||||||
|
"ads": {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"connectTimeout": "5s",
|
||||||
|
"tlsContext": {
|
||||||
|
"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"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"outlierDetection": {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"typeUrl": "type.googleapis.com/envoy.api.v2.Cluster",
|
||||||
|
"nonce": "00000001"
|
||||||
|
}
|
|
@ -0,0 +1,104 @@
|
||||||
|
{
|
||||||
|
"versionInfo": "00000001",
|
||||||
|
"resources": [
|
||||||
|
{
|
||||||
|
"@type": "type.googleapis.com/envoy.api.v2.Cluster",
|
||||||
|
"name": "local_app",
|
||||||
|
"connectTimeout": "5s",
|
||||||
|
"loadAssignment": {
|
||||||
|
"clusterName": "local_app",
|
||||||
|
"endpoints": [
|
||||||
|
{
|
||||||
|
"lbEndpoints": [
|
||||||
|
{
|
||||||
|
"endpoint": {
|
||||||
|
"address": {
|
||||||
|
"socketAddress": {
|
||||||
|
"address": "127.0.0.1",
|
||||||
|
"portValue": 8080
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"@type": "type.googleapis.com/envoy.api.v2.Cluster",
|
||||||
|
"name": "myservice",
|
||||||
|
"connectTimeout": "5s",
|
||||||
|
"hosts": [
|
||||||
|
{
|
||||||
|
"socketAddress": {
|
||||||
|
"address": "127.0.0.1",
|
||||||
|
"portValue": 8080
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"tlsContext": {
|
||||||
|
"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"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"@type": "type.googleapis.com/envoy.api.v2.Cluster",
|
||||||
|
"name": "prepared_query:geo-cache",
|
||||||
|
"type": "EDS",
|
||||||
|
"edsClusterConfig": {
|
||||||
|
"edsConfig": {
|
||||||
|
"ads": {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"connectTimeout": "5s",
|
||||||
|
"tlsContext": {
|
||||||
|
"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"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"outlierDetection": {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"typeUrl": "type.googleapis.com/envoy.api.v2.Cluster",
|
||||||
|
"nonce": "00000001"
|
||||||
|
}
|
|
@ -0,0 +1,107 @@
|
||||||
|
{
|
||||||
|
"versionInfo": "00000001",
|
||||||
|
"resources": [
|
||||||
|
{
|
||||||
|
"@type": "type.googleapis.com/envoy.api.v2.Cluster",
|
||||||
|
"name": "local_app",
|
||||||
|
"connectTimeout": "5s",
|
||||||
|
"loadAssignment": {
|
||||||
|
"clusterName": "local_app",
|
||||||
|
"endpoints": [
|
||||||
|
{
|
||||||
|
"lbEndpoints": [
|
||||||
|
{
|
||||||
|
"endpoint": {
|
||||||
|
"address": {
|
||||||
|
"socketAddress": {
|
||||||
|
"address": "127.0.0.1",
|
||||||
|
"portValue": 8080
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"@type": "type.googleapis.com/envoy.api.v2.Cluster",
|
||||||
|
"name": "db",
|
||||||
|
"type": "EDS",
|
||||||
|
"edsClusterConfig": {
|
||||||
|
"edsConfig": {
|
||||||
|
"ads": {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"connectTimeout": "1s",
|
||||||
|
"tlsContext": {
|
||||||
|
"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"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"outlierDetection": {
|
||||||
|
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"@type": "type.googleapis.com/envoy.api.v2.Cluster",
|
||||||
|
"name": "prepared_query:geo-cache",
|
||||||
|
"type": "EDS",
|
||||||
|
"edsClusterConfig": {
|
||||||
|
"edsConfig": {
|
||||||
|
"ads": {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"connectTimeout": "5s",
|
||||||
|
"tlsContext": {
|
||||||
|
"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"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"outlierDetection": {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"typeUrl": "type.googleapis.com/envoy.api.v2.Cluster",
|
||||||
|
"nonce": "00000001"
|
||||||
|
}
|
|
@ -0,0 +1,116 @@
|
||||||
|
{
|
||||||
|
"versionInfo": "00000001",
|
||||||
|
"resources": [
|
||||||
|
{
|
||||||
|
"@type": "type.googleapis.com/envoy.api.v2.Listener",
|
||||||
|
"name": "custom-public-listen",
|
||||||
|
"address": {
|
||||||
|
"socketAddress": {
|
||||||
|
"address": "11.11.11.11",
|
||||||
|
"portValue": 11111
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"filterChains": [
|
||||||
|
{
|
||||||
|
"tlsContext": {
|
||||||
|
"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
|
||||||
|
},
|
||||||
|
"filters": [
|
||||||
|
{
|
||||||
|
"name": "envoy.ext_authz",
|
||||||
|
"config": {
|
||||||
|
"grpc_service": {
|
||||||
|
"envoy_grpc": {
|
||||||
|
"cluster_name": "local_agent"
|
||||||
|
},
|
||||||
|
"initial_metadata": [
|
||||||
|
{
|
||||||
|
"key": "x-consul-token",
|
||||||
|
"value": "my-token"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"stat_prefix": "connect_authz"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "envoy.tcp_proxy",
|
||||||
|
"config": {
|
||||||
|
"cluster": "random-cluster",
|
||||||
|
"stat_prefix": "foo-stats"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"@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.tcp_proxy",
|
||||||
|
"config": {
|
||||||
|
"cluster": "db",
|
||||||
|
"stat_prefix": "upstream_db_tcp"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"@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.tcp_proxy",
|
||||||
|
"config": {
|
||||||
|
"cluster": "prepared_query:geo-cache",
|
||||||
|
"stat_prefix": "upstream_prepared_query_geo-cache_tcp"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"typeUrl": "type.googleapis.com/envoy.api.v2.Listener",
|
||||||
|
"nonce": "00000001"
|
||||||
|
}
|
|
@ -0,0 +1,116 @@
|
||||||
|
{
|
||||||
|
"versionInfo": "00000001",
|
||||||
|
"resources": [
|
||||||
|
{
|
||||||
|
"@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": [
|
||||||
|
{
|
||||||
|
"tlsContext": {
|
||||||
|
"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
|
||||||
|
},
|
||||||
|
"filters": [
|
||||||
|
{
|
||||||
|
"name": "envoy.ext_authz",
|
||||||
|
"config": {
|
||||||
|
"grpc_service": {
|
||||||
|
"envoy_grpc": {
|
||||||
|
"cluster_name": "local_agent"
|
||||||
|
},
|
||||||
|
"initial_metadata": [
|
||||||
|
{
|
||||||
|
"key": "x-consul-token",
|
||||||
|
"value": "my-token"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"stat_prefix": "connect_authz"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "envoy.tcp_proxy",
|
||||||
|
"config": {
|
||||||
|
"cluster": "local_app",
|
||||||
|
"stat_prefix": "public_listener_tcp"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"@type": "type.googleapis.com/envoy.api.v2.Listener",
|
||||||
|
"name": "custom-upstream",
|
||||||
|
"address": {
|
||||||
|
"socketAddress": {
|
||||||
|
"address": "11.11.11.11",
|
||||||
|
"portValue": 11111
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"filterChains": [
|
||||||
|
{
|
||||||
|
"filters": [
|
||||||
|
{
|
||||||
|
"name": "envoy.tcp_proxy",
|
||||||
|
"config": {
|
||||||
|
"cluster": "random-cluster",
|
||||||
|
"stat_prefix": "foo-stats"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"@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.tcp_proxy",
|
||||||
|
"config": {
|
||||||
|
"cluster": "prepared_query:geo-cache",
|
||||||
|
"stat_prefix": "upstream_prepared_query_geo-cache_tcp"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"typeUrl": "type.googleapis.com/envoy.api.v2.Listener",
|
||||||
|
"nonce": "00000001"
|
||||||
|
}
|
|
@ -0,0 +1,116 @@
|
||||||
|
{
|
||||||
|
"versionInfo": "00000001",
|
||||||
|
"resources": [
|
||||||
|
{
|
||||||
|
"@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": [
|
||||||
|
{
|
||||||
|
"tlsContext": {
|
||||||
|
"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
|
||||||
|
},
|
||||||
|
"filters": [
|
||||||
|
{
|
||||||
|
"name": "envoy.ext_authz",
|
||||||
|
"config": {
|
||||||
|
"grpc_service": {
|
||||||
|
"envoy_grpc": {
|
||||||
|
"cluster_name": "local_agent"
|
||||||
|
},
|
||||||
|
"initial_metadata": [
|
||||||
|
{
|
||||||
|
"key": "x-consul-token",
|
||||||
|
"value": "my-token"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"stat_prefix": "connect_authz"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "envoy.tcp_proxy",
|
||||||
|
"config": {
|
||||||
|
"cluster": "local_app",
|
||||||
|
"stat_prefix": "public_listener_tcp"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"@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.tcp_proxy",
|
||||||
|
"config": {
|
||||||
|
"cluster": "db",
|
||||||
|
"stat_prefix": "upstream_db_tcp"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"@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.tcp_proxy",
|
||||||
|
"config": {
|
||||||
|
"cluster": "prepared_query:geo-cache",
|
||||||
|
"stat_prefix": "upstream_prepared_query_geo-cache_tcp"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"typeUrl": "type.googleapis.com/envoy.api.v2.Listener",
|
||||||
|
"nonce": "00000001"
|
||||||
|
}
|
|
@ -0,0 +1,145 @@
|
||||||
|
{
|
||||||
|
"versionInfo": "00000001",
|
||||||
|
"resources": [
|
||||||
|
{
|
||||||
|
"@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": [
|
||||||
|
{
|
||||||
|
"tlsContext": {
|
||||||
|
"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
|
||||||
|
},
|
||||||
|
"filters": [
|
||||||
|
{
|
||||||
|
"name": "envoy.ext_authz",
|
||||||
|
"config": {
|
||||||
|
"grpc_service": {
|
||||||
|
"envoy_grpc": {
|
||||||
|
"cluster_name": "local_agent"
|
||||||
|
},
|
||||||
|
"initial_metadata": [
|
||||||
|
{
|
||||||
|
"key": "x-consul-token",
|
||||||
|
"value": "my-token"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"stat_prefix": "connect_authz"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "envoy.http_connection_manager",
|
||||||
|
"config": {
|
||||||
|
"http_filters": [
|
||||||
|
{
|
||||||
|
"name": "envoy.router"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"route_config": {
|
||||||
|
"name": "public_listener",
|
||||||
|
"virtual_hosts": [
|
||||||
|
{
|
||||||
|
"domains": [
|
||||||
|
"*"
|
||||||
|
],
|
||||||
|
"name": "public_listener",
|
||||||
|
"routes": [
|
||||||
|
{
|
||||||
|
"match": {
|
||||||
|
"prefix": "/"
|
||||||
|
},
|
||||||
|
"route": {
|
||||||
|
"cluster": "local_app"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"stat_prefix": "public_listener_http",
|
||||||
|
"tracing": {
|
||||||
|
"random_sampling": {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"@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.tcp_proxy",
|
||||||
|
"config": {
|
||||||
|
"cluster": "db",
|
||||||
|
"stat_prefix": "upstream_db_tcp"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"@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.tcp_proxy",
|
||||||
|
"config": {
|
||||||
|
"cluster": "prepared_query:geo-cache",
|
||||||
|
"stat_prefix": "upstream_prepared_query_geo-cache_tcp"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"typeUrl": "type.googleapis.com/envoy.api.v2.Listener",
|
||||||
|
"nonce": "00000001"
|
||||||
|
}
|
|
@ -0,0 +1,146 @@
|
||||||
|
{
|
||||||
|
"versionInfo": "00000001",
|
||||||
|
"resources": [
|
||||||
|
{
|
||||||
|
"@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": [
|
||||||
|
{
|
||||||
|
"tlsContext": {
|
||||||
|
"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
|
||||||
|
},
|
||||||
|
"filters": [
|
||||||
|
{
|
||||||
|
"name": "envoy.ext_authz",
|
||||||
|
"config": {
|
||||||
|
"grpc_service": {
|
||||||
|
"envoy_grpc": {
|
||||||
|
"cluster_name": "local_agent"
|
||||||
|
},
|
||||||
|
"initial_metadata": [
|
||||||
|
{
|
||||||
|
"key": "x-consul-token",
|
||||||
|
"value": "my-token"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"stat_prefix": "connect_authz"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "envoy.tcp_proxy",
|
||||||
|
"config": {
|
||||||
|
"cluster": "local_app",
|
||||||
|
"stat_prefix": "public_listener_tcp"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"@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.http_connection_manager",
|
||||||
|
"config": {
|
||||||
|
"http_filters": [
|
||||||
|
{
|
||||||
|
"name": "envoy.router"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"route_config": {
|
||||||
|
"name": "db",
|
||||||
|
"virtual_hosts": [
|
||||||
|
{
|
||||||
|
"domains": [
|
||||||
|
"*"
|
||||||
|
],
|
||||||
|
"name": "db",
|
||||||
|
"routes": [
|
||||||
|
{
|
||||||
|
"match": {
|
||||||
|
"prefix": "/"
|
||||||
|
},
|
||||||
|
"route": {
|
||||||
|
"cluster": "db"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"stat_prefix": "upstream_db_http",
|
||||||
|
"tracing": {
|
||||||
|
"operation_name": "EGRESS",
|
||||||
|
"random_sampling": {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"@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.tcp_proxy",
|
||||||
|
"config": {
|
||||||
|
"cluster": "prepared_query:geo-cache",
|
||||||
|
"stat_prefix": "upstream_prepared_query_geo-cache_tcp"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"typeUrl": "type.googleapis.com/envoy.api.v2.Listener",
|
||||||
|
"nonce": "00000001"
|
||||||
|
}
|
|
@ -0,0 +1,16 @@
|
||||||
|
-----BEGIN CERTIFICATE-----
|
||||||
|
MIICjDCCAjKgAwIBAgIIC5llxGV1gB8wCgYIKoZIzj0EAwIwFDESMBAGA1UEAxMJ
|
||||||
|
VGVzdCBDQSAyMB4XDTE5MDMyMjEzNTgyNloXDTI5MDMyMjEzNTgyNlowDjEMMAoG
|
||||||
|
A1UEAxMDd2ViMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEADPv1RHVNRfa2VKR
|
||||||
|
AB16b6rZnEt7tuhaxCFpQXPj7M2omb0B9Favq5E0ivpNtv1QnFhxtPd7d5k4e+T7
|
||||||
|
SkW1TaOCAXIwggFuMA4GA1UdDwEB/wQEAwIDuDAdBgNVHSUEFjAUBggrBgEFBQcD
|
||||||
|
AgYIKwYBBQUHAwEwDAYDVR0TAQH/BAIwADBoBgNVHQ4EYQRfN2Q6MDc6ODc6M2E6
|
||||||
|
NDA6MTk6NDc6YzM6NWE6YzA6YmE6NjI6ZGY6YWY6NGI6ZDQ6MDU6MjU6NzY6M2Q6
|
||||||
|
NWE6OGQ6MTY6OGQ6Njc6NWU6MmU6YTA6MzQ6N2Q6ZGM6ZmYwagYDVR0jBGMwYYBf
|
||||||
|
ZDE6MTE6MTE6YWM6MmE6YmE6OTc6YjI6M2Y6YWM6N2I6YmQ6ZGE6YmU6YjE6OGE6
|
||||||
|
ZmM6OWE6YmE6YjU6YmM6ODM6ZTc6NWU6NDE6NmY6ZjI6NzM6OTU6NTg6MGM6ZGIw
|
||||||
|
WQYDVR0RBFIwUIZOc3BpZmZlOi8vMTExMTExMTEtMjIyMi0zMzMzLTQ0NDQtNTU1
|
||||||
|
NTU1NTU1NTU1LmNvbnN1bC9ucy9kZWZhdWx0L2RjL2RjMS9zdmMvd2ViMAoGCCqG
|
||||||
|
SM49BAMCA0gAMEUCIGC3TTvvjj76KMrguVyFf4tjOqaSCRie3nmHMRNNRav7AiEA
|
||||||
|
pY0heYeK9A6iOLrzqxSerkXXQyj5e9bE4VgUnxgPU6g=
|
||||||
|
-----END CERTIFICATE-----
|
|
@ -0,0 +1,5 @@
|
||||||
|
-----BEGIN EC PRIVATE KEY-----
|
||||||
|
MHcCAQEEIMoTkpRggp3fqZzFKh82yS4LjtJI+XY+qX/7DefHFrtdoAoGCCqGSM49
|
||||||
|
AwEHoUQDQgAEADPv1RHVNRfa2VKRAB16b6rZnEt7tuhaxCFpQXPj7M2omb0B9Fav
|
||||||
|
q5E0ivpNtv1QnFhxtPd7d5k4e+T7SkW1TQ==
|
||||||
|
-----END EC PRIVATE KEY-----
|
|
@ -0,0 +1,15 @@
|
||||||
|
-----BEGIN CERTIFICATE-----
|
||||||
|
MIICXDCCAgKgAwIBAgIICpZq70Z9LyUwCgYIKoZIzj0EAwIwFDESMBAGA1UEAxMJ
|
||||||
|
VGVzdCBDQSAyMB4XDTE5MDMyMjEzNTgyNloXDTI5MDMyMjEzNTgyNlowFDESMBAG
|
||||||
|
A1UEAxMJVGVzdCBDQSAyMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEIhywH1gx
|
||||||
|
AsMwuF3ukAI5YL2jFxH6Usnma1HFSfVyxbXX1/uoZEYrj8yCAtdU2yoHETyd+Zx2
|
||||||
|
ThhRLP79pYegCaOCATwwggE4MA4GA1UdDwEB/wQEAwIBhjAPBgNVHRMBAf8EBTAD
|
||||||
|
AQH/MGgGA1UdDgRhBF9kMToxMToxMTphYzoyYTpiYTo5NzpiMjozZjphYzo3Yjpi
|
||||||
|
ZDpkYTpiZTpiMTo4YTpmYzo5YTpiYTpiNTpiYzo4MzplNzo1ZTo0MTo2ZjpmMjo3
|
||||||
|
Mzo5NTo1ODowYzpkYjBqBgNVHSMEYzBhgF9kMToxMToxMTphYzoyYTpiYTo5Nzpi
|
||||||
|
MjozZjphYzo3YjpiZDpkYTpiZTpiMTo4YTpmYzo5YTpiYTpiNTpiYzo4MzplNzo1
|
||||||
|
ZTo0MTo2ZjpmMjo3Mzo5NTo1ODowYzpkYjA/BgNVHREEODA2hjRzcGlmZmU6Ly8x
|
||||||
|
MTExMTExMS0yMjIyLTMzMzMtNDQ0NC01NTU1NTU1NTU1NTUuY29uc3VsMAoGCCqG
|
||||||
|
SM49BAMCA0gAMEUCICOY0i246rQHJt8o8Oya0D5PLL1FnmsQmQqIGCi31RwnAiEA
|
||||||
|
oR5f6Ku+cig2Il8T8LJujOp2/2A72QcHZA57B13y+8o=
|
||||||
|
-----END CERTIFICATE-----
|
|
@ -0,0 +1,407 @@
|
||||||
|
package envoy
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
"net"
|
||||||
|
"net/url"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
"text/template"
|
||||||
|
)
|
||||||
|
|
||||||
|
// BootstrapConfig is the set of keys we care about in a Connect.Proxy.Config
|
||||||
|
// map. Note that this only includes config keys that affects Envoy bootstrap
|
||||||
|
// generation. For Envoy config keys that affect runtime xDS behavior see
|
||||||
|
// agent/xds/config.go.
|
||||||
|
type BootstrapConfig struct {
|
||||||
|
// StatsdURL allows simple configuration of the statsd metrics sink. If
|
||||||
|
// tagging is required, use DogstatsdURL instead. The URL must be in one of
|
||||||
|
// the following forms:
|
||||||
|
// - udp://<ip>:<port>
|
||||||
|
// - $ENV_VAR_NAME in this case the ENV var named will have it's
|
||||||
|
// value taken and is expected to contain a URL in
|
||||||
|
// one of the supported forms above.
|
||||||
|
StatsdURL string `mapstructure:"envoy_statsd_url"`
|
||||||
|
|
||||||
|
// DogstatsdURL allows simple configuration of the dogstatsd metrics sink
|
||||||
|
// which allows tags and Unix domain sockets. The URL must be in one of the
|
||||||
|
// following forms:
|
||||||
|
// - udp://<ip>:<port>
|
||||||
|
// - unix:///full/path/to/unix.sock
|
||||||
|
// - $ENV_VAR_NAME in this case the ENV var named will have it's
|
||||||
|
// value taken and is expected to contain a URL in
|
||||||
|
// one of the supported forms above.
|
||||||
|
DogstatsdURL string `mapstructure:"envoy_dogstatsd_url"`
|
||||||
|
|
||||||
|
// StatsTags is a slice of string values that will be added as tags to
|
||||||
|
// metrics. They are used to configure
|
||||||
|
// https://www.envoyproxy.io/docs/envoy/v1.9.0/api-v2/config/metrics/v2/stats.proto#envoy-api-msg-config-metrics-v2-statsconfig
|
||||||
|
// and add to the basic tags Consul adds by default like the local_cluster
|
||||||
|
// name. Only exact values are supported here. Full configuration of
|
||||||
|
// stats_config.stats_tags can be made by overriding envoy_stats_config_json.
|
||||||
|
StatsTags []string `mapstructure:"envoy_stats_tags"`
|
||||||
|
|
||||||
|
// PrometheusBindAddr configures an <ip>:<port> on which the Envoy will listen
|
||||||
|
// and expose a single /metrics HTTP endpoint for Prometheus to scrape. It
|
||||||
|
// does this by proxying that URL to the internal admin server's prometheus
|
||||||
|
// endpoint which allows exposing metrics on the network without the security
|
||||||
|
// risk of exposing the full admin server API. Any other URL requested will be
|
||||||
|
// a 404.
|
||||||
|
//
|
||||||
|
// Note that as of Envoy 1.9.0, the built in Prometheus endpoint only exports
|
||||||
|
// counters and gauges but not timing information via histograms. This is
|
||||||
|
// fixed in 1.10-dev currently in Envoy master. Other changes since 1.9.0 make
|
||||||
|
// master incompatible with the current release of Consul Connect. This will
|
||||||
|
// be fixed in a future Consul version as Envoy 1.10 reaches stable release.
|
||||||
|
PrometheusBindAddr string `mapstructure:"envoy_prometheus_bind_addr"`
|
||||||
|
|
||||||
|
// OverrideJSONTpl allows replacing the base template used to render the
|
||||||
|
// bootstrap. This is an "escape hatch" allowing arbitrary control over the
|
||||||
|
// proxy's configuration but will the most effort to maintain and correctly
|
||||||
|
// configure the aspects that Connect relies upon to work. It's recommended
|
||||||
|
// that this only be used if necessary, and that it be based on the default
|
||||||
|
// template in
|
||||||
|
// https://github.com/hashicorp/consul/blob/master/command/connect/envoy/bootstrap_tpl.go
|
||||||
|
// for the correct version of Consul and Envoy being used.
|
||||||
|
OverrideJSONTpl string `mapstructure:"envoy_bootstrap_json_tpl"`
|
||||||
|
|
||||||
|
// StaticClustersJSON is a JSON string containing zero or more Cluster
|
||||||
|
// definitions. They are appended to the "static_resources.clusters" list. A
|
||||||
|
// single cluster should be given as a plain object, if more than one is to be
|
||||||
|
// added, they should be separated by a comma suitable for direct injection
|
||||||
|
// into a JSON array.
|
||||||
|
//
|
||||||
|
// Note that cluster names should be chosen in such a way that they won't
|
||||||
|
// collide with service names since we use plain service names as cluster
|
||||||
|
// names in xDS to make metrics population simpler and cluster names mush be
|
||||||
|
// unique.
|
||||||
|
//
|
||||||
|
// This is mostly intended for providing clusters for tracing or metrics
|
||||||
|
// services.
|
||||||
|
//
|
||||||
|
// See https://www.envoyproxy.io/docs/envoy/v1.9.0/api-v2/api/v2/cds.proto.
|
||||||
|
StaticClustersJSON string `mapstructure:"envoy_extra_static_clusters_json"`
|
||||||
|
|
||||||
|
// StaticListenersJSON is a JSON string containing zero or more Listener
|
||||||
|
// definitions. They are appended to the "static_resources.listeners" list. A
|
||||||
|
// single listener should be given as a plain object, if more than one is to
|
||||||
|
// be added, they should be separated by a comma suitable for direct injection
|
||||||
|
// into a JSON array.
|
||||||
|
//
|
||||||
|
// See https://www.envoyproxy.io/docs/envoy/v1.9.0/api-v2/api/v2/lds.proto.
|
||||||
|
StaticListenersJSON string `mapstructure:"envoy_extra_static_listeners_json"`
|
||||||
|
|
||||||
|
// StatsSinksJSON is a JSON string containing zero or more StatsSink
|
||||||
|
// definititions. They are appended to the `stats_sinks` array at the top
|
||||||
|
// level of the bootstrap config. A single sink should be given as a plain
|
||||||
|
// object, if more than one is to be added, they should be separated by a
|
||||||
|
// comma suitable for direct injection into a JSON array.
|
||||||
|
//
|
||||||
|
// See
|
||||||
|
// https://www.envoyproxy.io/docs/envoy/v1.9.0/api-v2/config/metrics/v2/stats.proto#config-metrics-v2-statssink.
|
||||||
|
//
|
||||||
|
// If this is non-empty then it will override anything configured in
|
||||||
|
// StatsTags.
|
||||||
|
StatsSinksJSON string `mapstructure:"envoy_extra_stats_sinks_json"`
|
||||||
|
|
||||||
|
// StatsConfigJSON is a JSON string containing an object in the right format
|
||||||
|
// to be rendered as the body of the `stats_config` field at the top level of
|
||||||
|
// 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/metrics/v2/stats.proto#envoy-api-msg-config-metrics-v2-statsconfig.
|
||||||
|
//
|
||||||
|
// If this is non-empty then it will override anything configured in
|
||||||
|
// StatsdURL or DogstatsdURL.
|
||||||
|
StatsConfigJSON string `mapstructure:"envoy_stats_config_json"`
|
||||||
|
|
||||||
|
// StatsFlushInterval is the time duration between Envoy stats flushes. It is
|
||||||
|
// in proto3 "duration" string format for example "1.12s" See
|
||||||
|
// https://developers.google.com/protocol-buffers/docs/proto3#json and
|
||||||
|
// https://www.envoyproxy.io/docs/envoy/v1.9.0/api-v2/config/bootstrap/v2/bootstrap.proto#bootstrap
|
||||||
|
StatsFlushInterval string `mapstructure:"envoy_stats_flush_interval"`
|
||||||
|
|
||||||
|
// TracingConfigJSON is a JSON string containing an object in the right format
|
||||||
|
// to be rendered as the body of the `tracing` field at the top level of
|
||||||
|
// 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.
|
||||||
|
TracingConfigJSON string `mapstructure:"envoy_tracing_json"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Template returns the bootstrap template to use as a base.
|
||||||
|
func (c *BootstrapConfig) Template() string {
|
||||||
|
if c.OverrideJSONTpl != "" {
|
||||||
|
return c.OverrideJSONTpl
|
||||||
|
}
|
||||||
|
return bootstrapTemplate
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *BootstrapConfig) GenerateJSON(args *BootstrapTplArgs) ([]byte, error) {
|
||||||
|
if err := c.ConfigureArgs(args); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
t, err := template.New("bootstrap").Parse(c.Template())
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
var buf bytes.Buffer
|
||||||
|
err = t.Execute(&buf, args)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return buf.Bytes(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ConfigureArgs takes the basic template arguments generated from the command
|
||||||
|
// arguments and environment and modifies them according to the BootstrapConfig.
|
||||||
|
func (c *BootstrapConfig) ConfigureArgs(args *BootstrapTplArgs) error {
|
||||||
|
|
||||||
|
// Attempt to setup sink(s) from high-level config. Note the args are passed
|
||||||
|
// by ref and modified in place.
|
||||||
|
if err := c.generateStatsSinks(args); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if c.StatsConfigJSON != "" {
|
||||||
|
// StatsConfig overridden explicitly
|
||||||
|
args.StatsConfigJSON = c.StatsConfigJSON
|
||||||
|
} else {
|
||||||
|
// Attempt to setup tags from high-level config. Note the args are passed by
|
||||||
|
// ref and modified in place.
|
||||||
|
if err := c.generateStatsConfig(args); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if c.StaticClustersJSON != "" {
|
||||||
|
args.StaticClustersJSON = c.StaticClustersJSON
|
||||||
|
}
|
||||||
|
if c.StaticListenersJSON != "" {
|
||||||
|
args.StaticListenersJSON = c.StaticListenersJSON
|
||||||
|
}
|
||||||
|
// Setup prometheus if needed. This MUST happen after the Static*JSON is set above
|
||||||
|
if c.PrometheusBindAddr != "" {
|
||||||
|
if err := c.generatePrometheusConfig(args); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if c.TracingConfigJSON != "" {
|
||||||
|
args.TracingConfigJSON = c.TracingConfigJSON
|
||||||
|
}
|
||||||
|
|
||||||
|
if c.StatsFlushInterval != "" {
|
||||||
|
args.StatsFlushInterval = c.StatsFlushInterval
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *BootstrapConfig) generateStatsSinks(args *BootstrapTplArgs) error {
|
||||||
|
var stats_sinks []string
|
||||||
|
|
||||||
|
if c.StatsdURL != "" {
|
||||||
|
sinkJSON, err := c.generateStatsSinkJSON("envoy.statsd", c.StatsdURL)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
stats_sinks = append(stats_sinks, sinkJSON)
|
||||||
|
}
|
||||||
|
if c.DogstatsdURL != "" {
|
||||||
|
sinkJSON, err := c.generateStatsSinkJSON("envoy.dog_statsd", c.DogstatsdURL)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
stats_sinks = append(stats_sinks, sinkJSON)
|
||||||
|
}
|
||||||
|
if c.StatsSinksJSON != "" {
|
||||||
|
stats_sinks = append(stats_sinks, c.StatsSinksJSON)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(stats_sinks) > 0 {
|
||||||
|
args.StatsSinksJSON = "[\n" + strings.Join(stats_sinks, ",\n") + "\n]"
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *BootstrapConfig) generateStatsSinkJSON(name string, addr string) (string, error) {
|
||||||
|
// Resolve address ENV var
|
||||||
|
if len(addr) > 2 && addr[0] == '$' {
|
||||||
|
addr = os.Getenv(addr[1:])
|
||||||
|
}
|
||||||
|
|
||||||
|
u, err := url.Parse(addr)
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("failed to parse %s sink address %q", name, addr)
|
||||||
|
}
|
||||||
|
|
||||||
|
var addrJSON string
|
||||||
|
switch u.Scheme {
|
||||||
|
case "udp":
|
||||||
|
addrJSON = `
|
||||||
|
"socket_address": {
|
||||||
|
"address": "` + u.Hostname() + `",
|
||||||
|
"port_value": ` + u.Port() + `
|
||||||
|
}
|
||||||
|
`
|
||||||
|
case "unix":
|
||||||
|
addrJSON = `
|
||||||
|
"pipe": {
|
||||||
|
"path": "` + u.Path + `"
|
||||||
|
}
|
||||||
|
`
|
||||||
|
default:
|
||||||
|
return "", fmt.Errorf("unsupported address protocol %q for %s sink",
|
||||||
|
u.Scheme, name)
|
||||||
|
}
|
||||||
|
|
||||||
|
return `{
|
||||||
|
"name": "` + name + `",
|
||||||
|
"config": {
|
||||||
|
"address": {
|
||||||
|
` + addrJSON + `
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}`, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *BootstrapConfig) generateStatsConfig(args *BootstrapTplArgs) error {
|
||||||
|
var tagJSONs []string
|
||||||
|
|
||||||
|
// Add some default tags if not already overridden
|
||||||
|
defaults := map[string]string{
|
||||||
|
"local_cluster": args.ProxyCluster,
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tag := range c.StatsTags {
|
||||||
|
parts := strings.SplitN(tag, "=", 2)
|
||||||
|
// If there is no equals, treat it as a boolean tag and just assign value of
|
||||||
|
// 1 e.g. "canary" will out put the tag "canary: 1"
|
||||||
|
v := "1"
|
||||||
|
if len(parts) == 2 {
|
||||||
|
v = parts[1]
|
||||||
|
}
|
||||||
|
k := strings.ToLower(parts[0])
|
||||||
|
tagJSON := `{
|
||||||
|
"tag_name": "` + k + `",
|
||||||
|
"fixed_value": "` + v + `"
|
||||||
|
}`
|
||||||
|
tagJSONs = append(tagJSONs, tagJSON)
|
||||||
|
// Remove this in case we override a default
|
||||||
|
delete(defaults, k)
|
||||||
|
}
|
||||||
|
|
||||||
|
for k, v := range defaults {
|
||||||
|
if v == "" {
|
||||||
|
// Skip stuff we just didn't have data for, this is only really the case
|
||||||
|
// in tests currently.
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
tagJSON := `{
|
||||||
|
"tag_name": "` + k + `",
|
||||||
|
"fixed_value": "` + v + `"
|
||||||
|
}`
|
||||||
|
tagJSONs = append(tagJSONs, tagJSON)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(tagJSONs) > 0 {
|
||||||
|
// use_all_default_tags is true by default but we'll make it explicit!
|
||||||
|
args.StatsConfigJSON = `{
|
||||||
|
"stats_tags": [
|
||||||
|
` + strings.Join(tagJSONs, ",\n") + `
|
||||||
|
],
|
||||||
|
"use_all_default_tags": true
|
||||||
|
}`
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *BootstrapConfig) generatePrometheusConfig(args *BootstrapTplArgs) error {
|
||||||
|
host, port, err := net.SplitHostPort(c.PrometheusBindAddr)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("invalid prometheus_bind_addr: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
clusterJSON := `{
|
||||||
|
"name": "self_admin",
|
||||||
|
"connect_timeout": "5s",
|
||||||
|
"type": "STATIC",
|
||||||
|
"http_protocol_options": {},
|
||||||
|
"hosts": [
|
||||||
|
{
|
||||||
|
"socket_address": {
|
||||||
|
"address": "127.0.0.1",
|
||||||
|
"port_value": ` + args.AdminBindPort + `
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}`
|
||||||
|
listenerJSON := `{
|
||||||
|
"name": "envoy_prometheus_metrics_listener",
|
||||||
|
"address": {
|
||||||
|
"socket_address": {
|
||||||
|
"address": "` + host + `",
|
||||||
|
"port_value": ` + port + `
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"filter_chains": [
|
||||||
|
{
|
||||||
|
"filters": [
|
||||||
|
{
|
||||||
|
"name": "envoy.http_connection_manager",
|
||||||
|
"config": {
|
||||||
|
"stat_prefix": "envoy_prometheus_metrics",
|
||||||
|
"codec_type": "HTTP1",
|
||||||
|
"route_config": {
|
||||||
|
"name": "self_admin_route",
|
||||||
|
"virtual_hosts": [
|
||||||
|
{
|
||||||
|
"name": "self_admin",
|
||||||
|
"domains": [
|
||||||
|
"*"
|
||||||
|
],
|
||||||
|
"routes": [
|
||||||
|
{
|
||||||
|
"match": {
|
||||||
|
"path": "/metrics"
|
||||||
|
},
|
||||||
|
"route": {
|
||||||
|
"cluster": "self_admin",
|
||||||
|
"prefix_rewrite": "/stats/prometheus"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"match": {
|
||||||
|
"prefix": "/"
|
||||||
|
},
|
||||||
|
"direct_response": {
|
||||||
|
"status": 404
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"http_filters": [
|
||||||
|
{
|
||||||
|
"name": "envoy.router"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}`
|
||||||
|
|
||||||
|
if args.StaticClustersJSON != "" {
|
||||||
|
clusterJSON = ",\n" + clusterJSON
|
||||||
|
}
|
||||||
|
args.StaticClustersJSON += clusterJSON
|
||||||
|
|
||||||
|
if args.StaticListenersJSON != "" {
|
||||||
|
listenerJSON = ",\n" + listenerJSON
|
||||||
|
}
|
||||||
|
args.StaticListenersJSON += listenerJSON
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -0,0 +1,410 @@
|
||||||
|
package envoy
|
||||||
|
|
||||||
|
import (
|
||||||
|
"reflect"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
expectedPromListener = `{
|
||||||
|
"name": "envoy_prometheus_metrics_listener",
|
||||||
|
"address": {
|
||||||
|
"socket_address": {
|
||||||
|
"address": "0.0.0.0",
|
||||||
|
"port_value": 9000
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"filter_chains": [
|
||||||
|
{
|
||||||
|
"filters": [
|
||||||
|
{
|
||||||
|
"name": "envoy.http_connection_manager",
|
||||||
|
"config": {
|
||||||
|
"stat_prefix": "envoy_prometheus_metrics",
|
||||||
|
"codec_type": "HTTP1",
|
||||||
|
"route_config": {
|
||||||
|
"name": "self_admin_route",
|
||||||
|
"virtual_hosts": [
|
||||||
|
{
|
||||||
|
"name": "self_admin",
|
||||||
|
"domains": [
|
||||||
|
"*"
|
||||||
|
],
|
||||||
|
"routes": [
|
||||||
|
{
|
||||||
|
"match": {
|
||||||
|
"path": "/metrics"
|
||||||
|
},
|
||||||
|
"route": {
|
||||||
|
"cluster": "self_admin",
|
||||||
|
"prefix_rewrite": "/stats/prometheus"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"match": {
|
||||||
|
"prefix": "/"
|
||||||
|
},
|
||||||
|
"direct_response": {
|
||||||
|
"status": 404
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"http_filters": [
|
||||||
|
{
|
||||||
|
"name": "envoy.router"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}`
|
||||||
|
expectedPromCluster = `{
|
||||||
|
"name": "self_admin",
|
||||||
|
"connect_timeout": "5s",
|
||||||
|
"type": "STATIC",
|
||||||
|
"http_protocol_options": {},
|
||||||
|
"hosts": [
|
||||||
|
{
|
||||||
|
"socket_address": {
|
||||||
|
"address": "127.0.0.1",
|
||||||
|
"port_value": 19000
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}`
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestBootstrapConfig_ConfigureArgs(t *testing.T) {
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
input BootstrapConfig
|
||||||
|
env []string
|
||||||
|
baseArgs BootstrapTplArgs
|
||||||
|
wantArgs BootstrapTplArgs
|
||||||
|
wantErr bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "defaults",
|
||||||
|
input: BootstrapConfig{},
|
||||||
|
wantArgs: BootstrapTplArgs{},
|
||||||
|
wantErr: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "extra-stats-sinks",
|
||||||
|
input: BootstrapConfig{
|
||||||
|
StatsSinksJSON: `{
|
||||||
|
"name": "envoy.custom_exciting_sink",
|
||||||
|
"config": {
|
||||||
|
"foo": "bar"
|
||||||
|
}
|
||||||
|
}`,
|
||||||
|
},
|
||||||
|
wantArgs: BootstrapTplArgs{
|
||||||
|
StatsSinksJSON: `[{
|
||||||
|
"name": "envoy.custom_exciting_sink",
|
||||||
|
"config": {
|
||||||
|
"foo": "bar"
|
||||||
|
}
|
||||||
|
}]`,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "simple-statsd-sink",
|
||||||
|
input: BootstrapConfig{
|
||||||
|
StatsdURL: "udp://127.0.0.1:9125",
|
||||||
|
},
|
||||||
|
wantArgs: BootstrapTplArgs{
|
||||||
|
StatsSinksJSON: `[{
|
||||||
|
"name": "envoy.statsd",
|
||||||
|
"config": {
|
||||||
|
"address": {
|
||||||
|
"socket_address": {
|
||||||
|
"address": "127.0.0.1",
|
||||||
|
"port_value": 9125
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}]`,
|
||||||
|
},
|
||||||
|
wantErr: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "simple-statsd-sink-plus-extra",
|
||||||
|
input: BootstrapConfig{
|
||||||
|
StatsdURL: "udp://127.0.0.1:9125",
|
||||||
|
StatsSinksJSON: `{
|
||||||
|
"name": "envoy.custom_exciting_sink",
|
||||||
|
"config": {
|
||||||
|
"foo": "bar"
|
||||||
|
}
|
||||||
|
}`,
|
||||||
|
},
|
||||||
|
wantArgs: BootstrapTplArgs{
|
||||||
|
StatsSinksJSON: `[{
|
||||||
|
"name": "envoy.statsd",
|
||||||
|
"config": {
|
||||||
|
"address": {
|
||||||
|
"socket_address": {
|
||||||
|
"address": "127.0.0.1",
|
||||||
|
"port_value": 9125
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "envoy.custom_exciting_sink",
|
||||||
|
"config": {
|
||||||
|
"foo": "bar"
|
||||||
|
}
|
||||||
|
}]`,
|
||||||
|
},
|
||||||
|
wantErr: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "simple-statsd-sink-env",
|
||||||
|
input: BootstrapConfig{
|
||||||
|
StatsdURL: "$MY_STATSD_URL",
|
||||||
|
},
|
||||||
|
env: []string{"MY_STATSD_URL=udp://127.0.0.1:9125"},
|
||||||
|
wantArgs: BootstrapTplArgs{
|
||||||
|
StatsSinksJSON: `[{
|
||||||
|
"name": "envoy.statsd",
|
||||||
|
"config": {
|
||||||
|
"address": {
|
||||||
|
"socket_address": {
|
||||||
|
"address": "127.0.0.1",
|
||||||
|
"port_value": 9125
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}]`,
|
||||||
|
},
|
||||||
|
wantErr: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "simple-dogstatsd-sink",
|
||||||
|
input: BootstrapConfig{
|
||||||
|
DogstatsdURL: "udp://127.0.0.1:9125",
|
||||||
|
},
|
||||||
|
wantArgs: BootstrapTplArgs{
|
||||||
|
StatsSinksJSON: `[{
|
||||||
|
"name": "envoy.dog_statsd",
|
||||||
|
"config": {
|
||||||
|
"address": {
|
||||||
|
"socket_address": {
|
||||||
|
"address": "127.0.0.1",
|
||||||
|
"port_value": 9125
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}]`,
|
||||||
|
},
|
||||||
|
wantErr: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "simple-dogstatsd-unix-sink",
|
||||||
|
input: BootstrapConfig{
|
||||||
|
DogstatsdURL: "unix:///var/run/dogstatsd.sock",
|
||||||
|
},
|
||||||
|
wantArgs: BootstrapTplArgs{
|
||||||
|
StatsSinksJSON: `[{
|
||||||
|
"name": "envoy.dog_statsd",
|
||||||
|
"config": {
|
||||||
|
"address": {
|
||||||
|
"pipe": {
|
||||||
|
"path": "/var/run/dogstatsd.sock"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}]`,
|
||||||
|
},
|
||||||
|
wantErr: false,
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
name: "simple-dogstatsd-sink-env",
|
||||||
|
input: BootstrapConfig{
|
||||||
|
DogstatsdURL: "$MY_STATSD_URL",
|
||||||
|
},
|
||||||
|
env: []string{"MY_STATSD_URL=udp://127.0.0.1:9125"},
|
||||||
|
wantArgs: BootstrapTplArgs{
|
||||||
|
StatsSinksJSON: `[{
|
||||||
|
"name": "envoy.dog_statsd",
|
||||||
|
"config": {
|
||||||
|
"address": {
|
||||||
|
"socket_address": {
|
||||||
|
"address": "127.0.0.1",
|
||||||
|
"port_value": 9125
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}]`,
|
||||||
|
},
|
||||||
|
wantErr: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "stats-config-override",
|
||||||
|
input: BootstrapConfig{
|
||||||
|
StatsConfigJSON: `{
|
||||||
|
"use_all_default_tags": true
|
||||||
|
}`,
|
||||||
|
},
|
||||||
|
wantArgs: BootstrapTplArgs{
|
||||||
|
StatsConfigJSON: `{
|
||||||
|
"use_all_default_tags": true
|
||||||
|
}`,
|
||||||
|
},
|
||||||
|
wantErr: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "simple-tags",
|
||||||
|
input: BootstrapConfig{
|
||||||
|
StatsTags: []string{"canary", "foo=bar", "baz=2"},
|
||||||
|
},
|
||||||
|
wantArgs: BootstrapTplArgs{
|
||||||
|
StatsConfigJSON: `{
|
||||||
|
"stats_tags": [
|
||||||
|
{
|
||||||
|
"tag_name": "canary",
|
||||||
|
"fixed_value": "1"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tag_name": "foo",
|
||||||
|
"fixed_value": "bar"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tag_name": "baz",
|
||||||
|
"fixed_value": "2"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"use_all_default_tags": true
|
||||||
|
}`,
|
||||||
|
},
|
||||||
|
wantErr: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "prometheus-bind-addr",
|
||||||
|
input: BootstrapConfig{
|
||||||
|
PrometheusBindAddr: "0.0.0.0:9000",
|
||||||
|
},
|
||||||
|
baseArgs: BootstrapTplArgs{
|
||||||
|
AdminBindAddress: "127.0.0.1",
|
||||||
|
AdminBindPort: "19000",
|
||||||
|
},
|
||||||
|
wantArgs: BootstrapTplArgs{
|
||||||
|
AdminBindAddress: "127.0.0.1",
|
||||||
|
AdminBindPort: "19000",
|
||||||
|
// Should add a static cluster for the self-proxy to admin
|
||||||
|
StaticClustersJSON: expectedPromCluster,
|
||||||
|
// Should add a static http listener too
|
||||||
|
StaticListenersJSON: expectedPromListener,
|
||||||
|
},
|
||||||
|
wantErr: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "prometheus-bind-addr-with-overrides",
|
||||||
|
input: BootstrapConfig{
|
||||||
|
PrometheusBindAddr: "0.0.0.0:9000",
|
||||||
|
StaticClustersJSON: `{"foo":"bar"}`,
|
||||||
|
StaticListenersJSON: `{"baz":"qux"}`,
|
||||||
|
},
|
||||||
|
baseArgs: BootstrapTplArgs{
|
||||||
|
AdminBindAddress: "127.0.0.1",
|
||||||
|
AdminBindPort: "19000",
|
||||||
|
},
|
||||||
|
wantArgs: BootstrapTplArgs{
|
||||||
|
AdminBindAddress: "127.0.0.1",
|
||||||
|
AdminBindPort: "19000",
|
||||||
|
// Should add a static cluster for the self-proxy to admin
|
||||||
|
StaticClustersJSON: `{"foo":"bar"},` + expectedPromCluster,
|
||||||
|
// Should add a static http listener too
|
||||||
|
StaticListenersJSON: `{"baz":"qux"},` + expectedPromListener,
|
||||||
|
},
|
||||||
|
wantErr: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "stats-flush-interval",
|
||||||
|
input: BootstrapConfig{
|
||||||
|
StatsFlushInterval: `10s`,
|
||||||
|
},
|
||||||
|
wantArgs: BootstrapTplArgs{
|
||||||
|
StatsFlushInterval: `10s`,
|
||||||
|
},
|
||||||
|
wantErr: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "override-tracing",
|
||||||
|
input: BootstrapConfig{
|
||||||
|
TracingConfigJSON: `{"foo": "bar"}`,
|
||||||
|
},
|
||||||
|
wantArgs: BootstrapTplArgs{
|
||||||
|
TracingConfigJSON: `{"foo": "bar"}`,
|
||||||
|
},
|
||||||
|
wantErr: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "err-bad-prometheus-addr",
|
||||||
|
input: BootstrapConfig{
|
||||||
|
PrometheusBindAddr: "asdasdsad",
|
||||||
|
},
|
||||||
|
wantErr: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "err-bad-statsd-addr",
|
||||||
|
input: BootstrapConfig{
|
||||||
|
StatsdURL: "asdasdsad",
|
||||||
|
},
|
||||||
|
wantErr: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "err-bad-dogstatsd-addr",
|
||||||
|
input: BootstrapConfig{
|
||||||
|
DogstatsdURL: "asdasdsad",
|
||||||
|
},
|
||||||
|
wantErr: true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
args := tt.baseArgs
|
||||||
|
|
||||||
|
defer testSetAndResetEnv(t, tt.env)()
|
||||||
|
|
||||||
|
err := tt.input.ConfigureArgs(&args)
|
||||||
|
if tt.wantErr {
|
||||||
|
require.Error(t, err)
|
||||||
|
} else {
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
// Want to compare JSON fields with JSONEq
|
||||||
|
argV := reflect.ValueOf(args)
|
||||||
|
wantV := reflect.ValueOf(tt.wantArgs)
|
||||||
|
argT := reflect.TypeOf(args)
|
||||||
|
for i := 0; i < argT.NumField(); i++ {
|
||||||
|
f := argT.Field(i)
|
||||||
|
if strings.HasSuffix(f.Name, "JSON") && wantV.Field(i).String() != "" {
|
||||||
|
// Some of our JSON strings are comma separated objects to be
|
||||||
|
// insertedinto an array which is not valid JSON on it's own so wrap
|
||||||
|
// them all in an array. For simple values this is still valid JSON
|
||||||
|
// too.
|
||||||
|
want := "[" + wantV.Field(i).String() + "]"
|
||||||
|
got := "[" + argV.Field(i).String() + "]"
|
||||||
|
require.JSONEq(t, want, got, "field %s should be equivalent JSON", f.Name)
|
||||||
|
} else {
|
||||||
|
require.Equalf(t, wantV.Field(i).Interface(),
|
||||||
|
argV.Field(i).Interface(), "field %s should be equal", f.Name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,15 +1,87 @@
|
||||||
package envoy
|
package envoy
|
||||||
|
|
||||||
type templateArgs struct {
|
// BootstrapTplArgs is the set of arguments that may be interpolated into the
|
||||||
ProxyCluster, ProxyID string
|
// Envoy bootstrap template.
|
||||||
AgentAddress string
|
type BootstrapTplArgs struct {
|
||||||
AgentPort string
|
// ProxyCluster is the cluster name for the the Envoy `node` specification and
|
||||||
AgentTLS bool
|
// is typically the same as the ProxyID.
|
||||||
AgentCAFile string
|
ProxyCluster string
|
||||||
AdminBindAddress string
|
|
||||||
AdminBindPort string
|
// ProxyID is the ID of the proxy service instance as registered with the
|
||||||
|
// local Consul agent. This must be used as the Envoy `node.id` in order for
|
||||||
|
// the agent to deliver the correct configuration.
|
||||||
|
ProxyID string
|
||||||
|
|
||||||
|
// AgentAddress is the IP address of the local agent where the proxy instance
|
||||||
|
// is registered.
|
||||||
|
AgentAddress string
|
||||||
|
|
||||||
|
// AgentPort is the gRPC port exposed on the local agent.
|
||||||
|
AgentPort string
|
||||||
|
|
||||||
|
// AgentTLS is true of the local agent gRPC service should be accessed over
|
||||||
|
// TLS.
|
||||||
|
AgentTLS bool
|
||||||
|
|
||||||
|
// AgentCAFile is the CA file to use to verify the local agent gRPC service if
|
||||||
|
// TLS is enabled.
|
||||||
|
AgentCAFile string
|
||||||
|
|
||||||
|
// AdminBindAddress is the address the Envoy admin server should bind to.
|
||||||
|
AdminBindAddress string
|
||||||
|
|
||||||
|
// AdminBindPort is the port the Envoy admin server should bind to.
|
||||||
|
AdminBindPort string
|
||||||
|
|
||||||
|
// LocalAgentClusterName is the name reserved for the local Consul agent gRPC
|
||||||
|
// service and is expected to be used for that purpose.
|
||||||
LocalAgentClusterName string
|
LocalAgentClusterName string
|
||||||
Token string
|
|
||||||
|
// Token is the Consul ACL token provided which is required to make gRPC
|
||||||
|
// discovery requests. If non-empty, this must be configured as the gRPC
|
||||||
|
// service "initial_metadata" with the key "x-consul-token" in order to
|
||||||
|
// authorize the discovery streaming RPCs.
|
||||||
|
Token string
|
||||||
|
|
||||||
|
// StaticClustersJSON is JSON string, each is expected to be a valid Cluster
|
||||||
|
// definition. They are appended to the "static_resources.clusters" list. Note
|
||||||
|
// that cluster names should be chosen in such a way that they won't collide
|
||||||
|
// with service names since we use plain service names as cluster names in xDS
|
||||||
|
// to make metrics population simpler and cluster names mush be unique. See
|
||||||
|
// https://www.envoyproxy.io/docs/envoy/v1.9.0/api-v2/api/v2/cds.proto.
|
||||||
|
StaticClustersJSON string
|
||||||
|
|
||||||
|
// StaticListenersJSON is a JSON string containing zero or more Listener
|
||||||
|
// definitions. They are appended to the "static_resources.listeners" list. A
|
||||||
|
// single listener should be given as a plain object, if more than one is to
|
||||||
|
// be added, they should be separated by a comma suitable for direct injection
|
||||||
|
// into a JSON array.
|
||||||
|
// See https://www.envoyproxy.io/docs/envoy/v1.9.0/api-v2/api/v2/lds.proto.
|
||||||
|
StaticListenersJSON string
|
||||||
|
|
||||||
|
// StatsSinksJSON is a JSON string containing an array in the right format
|
||||||
|
// to be rendered as the body of the `stats_sinks` field at the top level of
|
||||||
|
// 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/metrics/v2/stats.proto#config-metrics-v2-statssink.
|
||||||
|
StatsSinksJSON string
|
||||||
|
|
||||||
|
// StatsConfigJSON is a JSON string containing an object in the right format
|
||||||
|
// to be rendered as the body of the `stats_config` field at the top level of
|
||||||
|
// 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/metrics/v2/stats.proto#envoy-api-msg-config-metrics-v2-statsconfig.
|
||||||
|
StatsConfigJSON string
|
||||||
|
|
||||||
|
// StatsFlushInterval is the time duration between Envoy stats flushes. It is
|
||||||
|
// in proto3 "duration" string format for example "1.12s" See
|
||||||
|
// https://developers.google.com/protocol-buffers/docs/proto3#json and
|
||||||
|
// https://www.envoyproxy.io/docs/envoy/v1.9.0/api-v2/config/bootstrap/v2/bootstrap.proto#bootstrap
|
||||||
|
StatsFlushInterval string
|
||||||
|
|
||||||
|
// TracingConfigJSON is a JSON string containing an object in the right format
|
||||||
|
// to be rendered as the body of the `tracing` field at the top level of
|
||||||
|
// 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.
|
||||||
|
TracingConfigJSON string
|
||||||
}
|
}
|
||||||
|
|
||||||
const bootstrapTemplate = `{
|
const bootstrapTemplate = `{
|
||||||
|
@ -53,8 +125,29 @@ const bootstrapTemplate = `{
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
{{- if .StaticClustersJSON -}}
|
||||||
|
,
|
||||||
|
{{ .StaticClustersJSON }}
|
||||||
|
{{- end }}
|
||||||
|
]{{- if .StaticListenersJSON -}}
|
||||||
|
,
|
||||||
|
"listeners": [
|
||||||
|
{{ .StaticListenersJSON }}
|
||||||
]
|
]
|
||||||
|
{{- end }}
|
||||||
},
|
},
|
||||||
|
{{- if .StatsSinksJSON }}
|
||||||
|
"stats_sinks": {{ .StatsSinksJSON }},
|
||||||
|
{{- end }}
|
||||||
|
{{- if .StatsConfigJSON }}
|
||||||
|
"stats_config": {{ .StatsConfigJSON }},
|
||||||
|
{{- end }}
|
||||||
|
{{- if .StatsFlushInterval }}
|
||||||
|
"stats_flush_interval": "{{ .StatsFlushInterval }}",
|
||||||
|
{{- end }}
|
||||||
|
{{- if .TracingConfigJSON }}
|
||||||
|
"tracing": {{ .TracingConfigJSON }},
|
||||||
|
{{- end }}
|
||||||
"dynamic_resources": {
|
"dynamic_resources": {
|
||||||
"lds_config": { "ads": {} },
|
"lds_config": { "ads": {} },
|
||||||
"cds_config": { "ads": {} },
|
"cds_config": { "ads": {} },
|
||||||
|
|
|
@ -1,17 +1,17 @@
|
||||||
package envoy
|
package envoy
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
|
||||||
"errors"
|
"errors"
|
||||||
"flag"
|
"flag"
|
||||||
"fmt"
|
"fmt"
|
||||||
"html/template"
|
|
||||||
"net"
|
"net"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/mitchellh/mapstructure"
|
||||||
|
|
||||||
proxyAgent "github.com/hashicorp/consul/agent/proxyprocess"
|
proxyAgent "github.com/hashicorp/consul/agent/proxyprocess"
|
||||||
"github.com/hashicorp/consul/agent/xds"
|
"github.com/hashicorp/consul/agent/xds"
|
||||||
"github.com/hashicorp/consul/api"
|
"github.com/hashicorp/consul/api"
|
||||||
|
@ -42,12 +42,13 @@ type cmd struct {
|
||||||
client *api.Client
|
client *api.Client
|
||||||
|
|
||||||
// flags
|
// flags
|
||||||
proxyID string
|
proxyID string
|
||||||
sidecarFor string
|
sidecarFor string
|
||||||
adminBind string
|
adminBind string
|
||||||
envoyBin string
|
envoyBin string
|
||||||
bootstrap bool
|
bootstrap bool
|
||||||
grpcAddr string
|
disableCentralConfig bool
|
||||||
|
grpcAddr string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *cmd) init() {
|
func (c *cmd) init() {
|
||||||
|
@ -68,12 +69,19 @@ func (c *cmd) init() {
|
||||||
|
|
||||||
c.flags.StringVar(&c.adminBind, "admin-bind", "localhost:19000",
|
c.flags.StringVar(&c.adminBind, "admin-bind", "localhost:19000",
|
||||||
"The address:port to start envoy's admin server on. Envoy requires this "+
|
"The address:port to start envoy's admin server on. Envoy requires this "+
|
||||||
"but care must be taked to ensure it's not exposed to untrusted network "+
|
"but care must be taken to ensure it's not exposed to an untrusted network "+
|
||||||
"as it has full control over the secrets and config of the proxy.")
|
"as it has full control over the secrets and config of the proxy.")
|
||||||
|
|
||||||
c.flags.BoolVar(&c.bootstrap, "bootstrap", false,
|
c.flags.BoolVar(&c.bootstrap, "bootstrap", false,
|
||||||
"Generate the bootstrap.json but don't exec envoy")
|
"Generate the bootstrap.json but don't exec envoy")
|
||||||
|
|
||||||
|
c.flags.BoolVar(&c.disableCentralConfig, "no-central-config", false,
|
||||||
|
"By default the proxy's bootstrap configuration can be customized "+
|
||||||
|
"centrally. This requires that the command run on the same agent as the "+
|
||||||
|
"proxy will and that the agent is reachable when the command is run. In "+
|
||||||
|
"cases where either assumption is violated this flag will prevent the "+
|
||||||
|
"command attempting to resolve config from the local agent.")
|
||||||
|
|
||||||
c.flags.StringVar(&c.grpcAddr, "grpc-addr", "",
|
c.flags.StringVar(&c.grpcAddr, "grpc-addr", "",
|
||||||
"Set the agent's gRPC address and port (in http(s)://host:port format). "+
|
"Set the agent's gRPC address and port (in http(s)://host:port format). "+
|
||||||
"Alternatively, you can specify CONSUL_GRPC_ADDR in ENV.")
|
"Alternatively, you can specify CONSUL_GRPC_ADDR in ENV.")
|
||||||
|
@ -179,7 +187,7 @@ func (c *cmd) findBinary() (string, error) {
|
||||||
return exec.LookPath("envoy")
|
return exec.LookPath("envoy")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *cmd) templateArgs() (*templateArgs, error) {
|
func (c *cmd) templateArgs() (*BootstrapTplArgs, error) {
|
||||||
httpCfg := api.DefaultConfig()
|
httpCfg := api.DefaultConfig()
|
||||||
c.http.MergeOntoConfig(httpCfg)
|
c.http.MergeOntoConfig(httpCfg)
|
||||||
|
|
||||||
|
@ -235,8 +243,18 @@ func (c *cmd) templateArgs() (*templateArgs, error) {
|
||||||
return nil, fmt.Errorf("Failed to resolve admin bind address: %s", err)
|
return nil, fmt.Errorf("Failed to resolve admin bind address: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return &templateArgs{
|
// Ideally the cluster should be the service name. We may or may not have that
|
||||||
ProxyCluster: c.proxyID,
|
// yet depending on the arguments used so make a best effort here. In the
|
||||||
|
// common case, even if the command was invoked with proxy-id and we don't
|
||||||
|
// know service name yet, we will after we resolve the proxy's config in a bit
|
||||||
|
// and will update this then.
|
||||||
|
cluster := c.proxyID
|
||||||
|
if c.sidecarFor != "" {
|
||||||
|
cluster = c.sidecarFor
|
||||||
|
}
|
||||||
|
|
||||||
|
return &BootstrapTplArgs{
|
||||||
|
ProxyCluster: cluster,
|
||||||
ProxyID: c.proxyID,
|
ProxyID: c.proxyID,
|
||||||
AgentAddress: agentIP.String(),
|
AgentAddress: agentIP.String(),
|
||||||
AgentPort: agentPort,
|
AgentPort: agentPort,
|
||||||
|
@ -254,13 +272,30 @@ func (c *cmd) generateConfig() ([]byte, error) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
var t = template.Must(template.New("bootstrap").Parse(bootstrapTemplate))
|
|
||||||
var buf bytes.Buffer
|
var bsCfg BootstrapConfig
|
||||||
err = t.Execute(&buf, args)
|
|
||||||
if err != nil {
|
if !c.disableCentralConfig {
|
||||||
return nil, err
|
// Fetch any customization from the registration
|
||||||
|
svc, _, err := c.client.Agent().Service(c.proxyID, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed fetch proxy config from local agent: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if svc.Proxy == nil {
|
||||||
|
return nil, errors.New("service is not a Connect proxy")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse the bootstrap config
|
||||||
|
if err := mapstructure.WeakDecode(svc.Proxy.Config, &bsCfg); err != nil {
|
||||||
|
return nil, fmt.Errorf("failed parsing Proxy.Config: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Override cluster now we know the actual service name
|
||||||
|
args.ProxyCluster = svc.Proxy.DestinationServiceName
|
||||||
}
|
}
|
||||||
return buf.Bytes(), nil
|
|
||||||
|
return bsCfg.GenerateJSON(args)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *cmd) lookupProxyIDForSidecar() (string, error) {
|
func (c *cmd) lookupProxyIDForSidecar() (string, error) {
|
||||||
|
|
|
@ -1,16 +1,21 @@
|
||||||
package envoy
|
package envoy
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/json"
|
||||||
"flag"
|
"flag"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
|
"net/http"
|
||||||
|
"net/http/httptest"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/hashicorp/consul/agent/xds"
|
|
||||||
"github.com/mitchellh/cli"
|
"github.com/mitchellh/cli"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
|
|
||||||
|
"github.com/hashicorp/consul/agent/xds"
|
||||||
|
"github.com/hashicorp/consul/api"
|
||||||
)
|
)
|
||||||
|
|
||||||
var update = flag.Bool("update", false, "update golden files")
|
var update = flag.Bool("update", false, "update golden files")
|
||||||
|
@ -56,11 +61,12 @@ func testSetAndResetEnv(t *testing.T, env []string) func() {
|
||||||
// pass the test of having their template args generated as expected.
|
// pass the test of having their template args generated as expected.
|
||||||
func TestGenerateConfig(t *testing.T) {
|
func TestGenerateConfig(t *testing.T) {
|
||||||
cases := []struct {
|
cases := []struct {
|
||||||
Name string
|
Name string
|
||||||
Flags []string
|
Flags []string
|
||||||
Env []string
|
Env []string
|
||||||
WantArgs templateArgs
|
ProxyConfig map[string]interface{}
|
||||||
WantErr string
|
WantArgs BootstrapTplArgs
|
||||||
|
WantErr string
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
Name: "no-args",
|
Name: "no-args",
|
||||||
|
@ -72,7 +78,7 @@ func TestGenerateConfig(t *testing.T) {
|
||||||
Name: "defaults",
|
Name: "defaults",
|
||||||
Flags: []string{"-proxy-id", "test-proxy"},
|
Flags: []string{"-proxy-id", "test-proxy"},
|
||||||
Env: []string{},
|
Env: []string{},
|
||||||
WantArgs: templateArgs{
|
WantArgs: BootstrapTplArgs{
|
||||||
ProxyCluster: "test-proxy",
|
ProxyCluster: "test-proxy",
|
||||||
ProxyID: "test-proxy",
|
ProxyID: "test-proxy",
|
||||||
AgentAddress: "127.0.0.1",
|
AgentAddress: "127.0.0.1",
|
||||||
|
@ -87,7 +93,7 @@ func TestGenerateConfig(t *testing.T) {
|
||||||
Flags: []string{"-proxy-id", "test-proxy",
|
Flags: []string{"-proxy-id", "test-proxy",
|
||||||
"-grpc-addr", "localhost:9999"},
|
"-grpc-addr", "localhost:9999"},
|
||||||
Env: []string{},
|
Env: []string{},
|
||||||
WantArgs: templateArgs{
|
WantArgs: BootstrapTplArgs{
|
||||||
ProxyCluster: "test-proxy",
|
ProxyCluster: "test-proxy",
|
||||||
ProxyID: "test-proxy",
|
ProxyID: "test-proxy",
|
||||||
// Should resolve IP, note this might not resolve the same way
|
// Should resolve IP, note this might not resolve the same way
|
||||||
|
@ -106,7 +112,7 @@ func TestGenerateConfig(t *testing.T) {
|
||||||
Env: []string{
|
Env: []string{
|
||||||
"CONSUL_GRPC_ADDR=localhost:9999",
|
"CONSUL_GRPC_ADDR=localhost:9999",
|
||||||
},
|
},
|
||||||
WantArgs: templateArgs{
|
WantArgs: BootstrapTplArgs{
|
||||||
ProxyCluster: "test-proxy",
|
ProxyCluster: "test-proxy",
|
||||||
ProxyID: "test-proxy",
|
ProxyID: "test-proxy",
|
||||||
// Should resolve IP, note this might not resolve the same way
|
// Should resolve IP, note this might not resolve the same way
|
||||||
|
@ -119,7 +125,185 @@ func TestGenerateConfig(t *testing.T) {
|
||||||
LocalAgentClusterName: xds.LocalAgentClusterName,
|
LocalAgentClusterName: xds.LocalAgentClusterName,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
// TODO(banks): all the flags/env manipulation cases
|
{
|
||||||
|
Name: "custom-bootstrap",
|
||||||
|
Flags: []string{"-proxy-id", "test-proxy"},
|
||||||
|
Env: []string{},
|
||||||
|
ProxyConfig: map[string]interface{}{
|
||||||
|
// Add a completely custom bootstrap template. Never mind if this is
|
||||||
|
// invalid envoy config just as long as it works and gets the variables
|
||||||
|
// interplated.
|
||||||
|
"envoy_bootstrap_json_tpl": `
|
||||||
|
{
|
||||||
|
"admin": {
|
||||||
|
"access_log_path": "/dev/null",
|
||||||
|
"address": {
|
||||||
|
"socket_address": {
|
||||||
|
"address": "{{ .AdminBindAddress }}",
|
||||||
|
"port_value": {{ .AdminBindPort }}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node": {
|
||||||
|
"cluster": "{{ .ProxyCluster }}",
|
||||||
|
"id": "{{ .ProxyID }}"
|
||||||
|
},
|
||||||
|
custom_field = "foo"
|
||||||
|
}`,
|
||||||
|
},
|
||||||
|
WantArgs: BootstrapTplArgs{
|
||||||
|
ProxyCluster: "test-proxy",
|
||||||
|
ProxyID: "test-proxy",
|
||||||
|
AgentAddress: "127.0.0.1",
|
||||||
|
AgentPort: "8502",
|
||||||
|
AdminBindAddress: "127.0.0.1",
|
||||||
|
AdminBindPort: "19000",
|
||||||
|
LocalAgentClusterName: xds.LocalAgentClusterName,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "extra_-single",
|
||||||
|
Flags: []string{"-proxy-id", "test-proxy"},
|
||||||
|
Env: []string{},
|
||||||
|
ProxyConfig: map[string]interface{}{
|
||||||
|
// Add a custom sections with interpolated variables. These are all
|
||||||
|
// invalid config syntax too but we are just testing they have the right
|
||||||
|
// effect.
|
||||||
|
"envoy_extra_static_clusters_json": `
|
||||||
|
{
|
||||||
|
"name": "fake_cluster_1"
|
||||||
|
}`,
|
||||||
|
"envoy_extra_static_listeners_json": `
|
||||||
|
{
|
||||||
|
"name": "fake_listener_1"
|
||||||
|
}`,
|
||||||
|
"envoy_extra_stats_sinks_json": `
|
||||||
|
{
|
||||||
|
"name": "fake_sink_1"
|
||||||
|
}`,
|
||||||
|
},
|
||||||
|
WantArgs: BootstrapTplArgs{
|
||||||
|
ProxyCluster: "test-proxy",
|
||||||
|
ProxyID: "test-proxy",
|
||||||
|
AgentAddress: "127.0.0.1",
|
||||||
|
AgentPort: "8502",
|
||||||
|
AdminBindAddress: "127.0.0.1",
|
||||||
|
AdminBindPort: "19000",
|
||||||
|
LocalAgentClusterName: xds.LocalAgentClusterName,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "extra_-multiple",
|
||||||
|
Flags: []string{"-proxy-id", "test-proxy"},
|
||||||
|
Env: []string{},
|
||||||
|
ProxyConfig: map[string]interface{}{
|
||||||
|
// Add a custom sections with interpolated variables. These are all
|
||||||
|
// invalid config syntax too but we are just testing they have the right
|
||||||
|
// effect.
|
||||||
|
"envoy_extra_static_clusters_json": `
|
||||||
|
{
|
||||||
|
"name": "fake_cluster_1"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "fake_cluster_2"
|
||||||
|
}`,
|
||||||
|
"envoy_extra_static_listeners_json": `
|
||||||
|
{
|
||||||
|
"name": "fake_listener_1"
|
||||||
|
},{
|
||||||
|
"name": "fake_listener_2"
|
||||||
|
}`,
|
||||||
|
"envoy_extra_stats_sinks_json": `
|
||||||
|
{
|
||||||
|
"name": "fake_sink_1"
|
||||||
|
} , { "name": "fake_sink_2" }`,
|
||||||
|
},
|
||||||
|
WantArgs: BootstrapTplArgs{
|
||||||
|
ProxyCluster: "test-proxy",
|
||||||
|
ProxyID: "test-proxy",
|
||||||
|
AgentAddress: "127.0.0.1",
|
||||||
|
AgentPort: "8502",
|
||||||
|
AdminBindAddress: "127.0.0.1",
|
||||||
|
AdminBindPort: "19000",
|
||||||
|
LocalAgentClusterName: xds.LocalAgentClusterName,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "stats-config-override",
|
||||||
|
Flags: []string{"-proxy-id", "test-proxy"},
|
||||||
|
Env: []string{},
|
||||||
|
ProxyConfig: map[string]interface{}{
|
||||||
|
// Add a custom sections with interpolated variables. These are all
|
||||||
|
// invalid config syntax too but we are just testing they have the right
|
||||||
|
// effect.
|
||||||
|
"envoy_stats_config_json": `
|
||||||
|
{
|
||||||
|
"name": "fake_config"
|
||||||
|
}`,
|
||||||
|
},
|
||||||
|
WantArgs: BootstrapTplArgs{
|
||||||
|
ProxyCluster: "test-proxy",
|
||||||
|
ProxyID: "test-proxy",
|
||||||
|
AgentAddress: "127.0.0.1",
|
||||||
|
AgentPort: "8502",
|
||||||
|
AdminBindAddress: "127.0.0.1",
|
||||||
|
AdminBindPort: "19000",
|
||||||
|
LocalAgentClusterName: xds.LocalAgentClusterName,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "zipkin-tracing-config",
|
||||||
|
Flags: []string{"-proxy-id", "test-proxy"},
|
||||||
|
Env: []string{},
|
||||||
|
ProxyConfig: map[string]interface{}{
|
||||||
|
// Add a custom sections with interpolated variables. These are all
|
||||||
|
// invalid config syntax too but we are just testing they have the right
|
||||||
|
// effect.
|
||||||
|
"envoy_tracing_json": `{
|
||||||
|
"http": {
|
||||||
|
"name": "envoy.zipkin",
|
||||||
|
"config": {
|
||||||
|
"collector_cluster": "zipkin",
|
||||||
|
"collector_endpoint": "/api/v1/spans"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}`,
|
||||||
|
// Need to setup the cluster to send that too as well
|
||||||
|
"envoy_extra_static_clusters_json": `{
|
||||||
|
"name": "zipkin",
|
||||||
|
"type": "STRICT_DNS",
|
||||||
|
"connect_timeout": "5s",
|
||||||
|
"load_assignment": {
|
||||||
|
"cluster_name": "zipkin",
|
||||||
|
"endpoints": [
|
||||||
|
{
|
||||||
|
"lb_endpoints": [
|
||||||
|
{
|
||||||
|
"endpoint": {
|
||||||
|
"address": {
|
||||||
|
"socket_address": {
|
||||||
|
"address": "zipkin.service.consul",
|
||||||
|
"port_value": 9411
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}`,
|
||||||
|
},
|
||||||
|
WantArgs: BootstrapTplArgs{
|
||||||
|
ProxyCluster: "test-proxy",
|
||||||
|
ProxyID: "test-proxy",
|
||||||
|
AgentAddress: "127.0.0.1",
|
||||||
|
AgentPort: "8502",
|
||||||
|
AdminBindAddress: "127.0.0.1",
|
||||||
|
AdminBindPort: "19000",
|
||||||
|
LocalAgentClusterName: xds.LocalAgentClusterName,
|
||||||
|
},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, tc := range cases {
|
for _, tc := range cases {
|
||||||
|
@ -129,6 +313,14 @@ func TestGenerateConfig(t *testing.T) {
|
||||||
ui := cli.NewMockUi()
|
ui := cli.NewMockUi()
|
||||||
c := New(ui)
|
c := New(ui)
|
||||||
|
|
||||||
|
// Run a mock agent API that just always returns the proxy config in the
|
||||||
|
// test.
|
||||||
|
srv := httptest.NewServer(testMockAgentProxyConfig(tc.ProxyConfig))
|
||||||
|
defer srv.Close()
|
||||||
|
|
||||||
|
// Set the agent HTTP address in ENV to be our mock
|
||||||
|
tc.Env = append(tc.Env, "CONSUL_HTTP_ADDR="+srv.URL)
|
||||||
|
|
||||||
defer testSetAndResetEnv(t, tc.Env)()
|
defer testSetAndResetEnv(t, tc.Env)()
|
||||||
|
|
||||||
// Run the command
|
// Run the command
|
||||||
|
@ -165,3 +357,31 @@ func TestGenerateConfig(t *testing.T) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func testMockAgentProxyConfig(cfg map[string]interface{}) http.HandlerFunc {
|
||||||
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
// Parse the proxy-id from the end of the URL (blindly assuming it's correct
|
||||||
|
// format)
|
||||||
|
proxyID := strings.TrimPrefix(r.URL.Path, "/v1/agent/service/")
|
||||||
|
serviceID := strings.TrimSuffix(proxyID, "-sidecar-proxy")
|
||||||
|
|
||||||
|
svc := api.AgentService{
|
||||||
|
Kind: api.ServiceKindConnectProxy,
|
||||||
|
ID: proxyID,
|
||||||
|
Service: proxyID,
|
||||||
|
Proxy: &api.AgentServiceConnectProxyConfig{
|
||||||
|
DestinationServiceName: serviceID,
|
||||||
|
DestinationServiceID: serviceID,
|
||||||
|
Config: cfg,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
cfgJSON, err := json.Marshal(svc)
|
||||||
|
if err != nil {
|
||||||
|
w.WriteHeader(500)
|
||||||
|
w.Write([]byte(err.Error()))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
w.Write(cfgJSON)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,17 @@
|
||||||
|
|
||||||
|
{
|
||||||
|
"admin": {
|
||||||
|
"access_log_path": "/dev/null",
|
||||||
|
"address": {
|
||||||
|
"socket_address": {
|
||||||
|
"address": "127.0.0.1",
|
||||||
|
"port_value": 19000
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node": {
|
||||||
|
"cluster": "test-proxy",
|
||||||
|
"id": "test-proxy"
|
||||||
|
},
|
||||||
|
custom_field = "foo"
|
||||||
|
}
|
|
@ -30,6 +30,15 @@
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
"stats_config": {
|
||||||
|
"stats_tags": [
|
||||||
|
{
|
||||||
|
"tag_name": "local_cluster",
|
||||||
|
"fixed_value": "test-proxy"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"use_all_default_tags": true
|
||||||
|
},
|
||||||
"dynamic_resources": {
|
"dynamic_resources": {
|
||||||
"lds_config": { "ads": {} },
|
"lds_config": { "ads": {} },
|
||||||
"cds_config": { "ads": {} },
|
"cds_config": { "ads": {} },
|
||||||
|
|
|
@ -0,0 +1,81 @@
|
||||||
|
{
|
||||||
|
"admin": {
|
||||||
|
"access_log_path": "/dev/null",
|
||||||
|
"address": {
|
||||||
|
"socket_address": {
|
||||||
|
"address": "127.0.0.1",
|
||||||
|
"port_value": 19000
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node": {
|
||||||
|
"cluster": "test-proxy",
|
||||||
|
"id": "test-proxy"
|
||||||
|
},
|
||||||
|
"static_resources": {
|
||||||
|
"clusters": [
|
||||||
|
{
|
||||||
|
"name": "local_agent",
|
||||||
|
"connect_timeout": "1s",
|
||||||
|
"type": "STATIC",
|
||||||
|
"http2_protocol_options": {},
|
||||||
|
"hosts": [
|
||||||
|
{
|
||||||
|
"socket_address": {
|
||||||
|
"address": "127.0.0.1",
|
||||||
|
"port_value": 8502
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
"name": "fake_cluster_1"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "fake_cluster_2"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"listeners": [
|
||||||
|
|
||||||
|
{
|
||||||
|
"name": "fake_listener_1"
|
||||||
|
},{
|
||||||
|
"name": "fake_listener_2"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"stats_sinks": [
|
||||||
|
|
||||||
|
{
|
||||||
|
"name": "fake_sink_1"
|
||||||
|
} , { "name": "fake_sink_2" }
|
||||||
|
],
|
||||||
|
"stats_config": {
|
||||||
|
"stats_tags": [
|
||||||
|
{
|
||||||
|
"tag_name": "local_cluster",
|
||||||
|
"fixed_value": "test-proxy"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"use_all_default_tags": true
|
||||||
|
},
|
||||||
|
"dynamic_resources": {
|
||||||
|
"lds_config": { "ads": {} },
|
||||||
|
"cds_config": { "ads": {} },
|
||||||
|
"ads_config": {
|
||||||
|
"api_type": "GRPC",
|
||||||
|
"grpc_services": {
|
||||||
|
"initial_metadata": [
|
||||||
|
{
|
||||||
|
"key": "x-consul-token",
|
||||||
|
"value": ""
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"envoy_grpc": {
|
||||||
|
"cluster_name": "local_agent"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,76 @@
|
||||||
|
{
|
||||||
|
"admin": {
|
||||||
|
"access_log_path": "/dev/null",
|
||||||
|
"address": {
|
||||||
|
"socket_address": {
|
||||||
|
"address": "127.0.0.1",
|
||||||
|
"port_value": 19000
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node": {
|
||||||
|
"cluster": "test-proxy",
|
||||||
|
"id": "test-proxy"
|
||||||
|
},
|
||||||
|
"static_resources": {
|
||||||
|
"clusters": [
|
||||||
|
{
|
||||||
|
"name": "local_agent",
|
||||||
|
"connect_timeout": "1s",
|
||||||
|
"type": "STATIC",
|
||||||
|
"http2_protocol_options": {},
|
||||||
|
"hosts": [
|
||||||
|
{
|
||||||
|
"socket_address": {
|
||||||
|
"address": "127.0.0.1",
|
||||||
|
"port_value": 8502
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
"name": "fake_cluster_1"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"listeners": [
|
||||||
|
|
||||||
|
{
|
||||||
|
"name": "fake_listener_1"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"stats_sinks": [
|
||||||
|
|
||||||
|
{
|
||||||
|
"name": "fake_sink_1"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"stats_config": {
|
||||||
|
"stats_tags": [
|
||||||
|
{
|
||||||
|
"tag_name": "local_cluster",
|
||||||
|
"fixed_value": "test-proxy"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"use_all_default_tags": true
|
||||||
|
},
|
||||||
|
"dynamic_resources": {
|
||||||
|
"lds_config": { "ads": {} },
|
||||||
|
"cds_config": { "ads": {} },
|
||||||
|
"ads_config": {
|
||||||
|
"api_type": "GRPC",
|
||||||
|
"grpc_services": {
|
||||||
|
"initial_metadata": [
|
||||||
|
{
|
||||||
|
"key": "x-consul-token",
|
||||||
|
"value": ""
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"envoy_grpc": {
|
||||||
|
"cluster_name": "local_agent"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -30,6 +30,15 @@
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
"stats_config": {
|
||||||
|
"stats_tags": [
|
||||||
|
{
|
||||||
|
"tag_name": "local_cluster",
|
||||||
|
"fixed_value": "test-proxy"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"use_all_default_tags": true
|
||||||
|
},
|
||||||
"dynamic_resources": {
|
"dynamic_resources": {
|
||||||
"lds_config": { "ads": {} },
|
"lds_config": { "ads": {} },
|
||||||
"cds_config": { "ads": {} },
|
"cds_config": { "ads": {} },
|
||||||
|
|
|
@ -30,6 +30,15 @@
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
"stats_config": {
|
||||||
|
"stats_tags": [
|
||||||
|
{
|
||||||
|
"tag_name": "local_cluster",
|
||||||
|
"fixed_value": "test-proxy"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"use_all_default_tags": true
|
||||||
|
},
|
||||||
"dynamic_resources": {
|
"dynamic_resources": {
|
||||||
"lds_config": { "ads": {} },
|
"lds_config": { "ads": {} },
|
||||||
"cds_config": { "ads": {} },
|
"cds_config": { "ads": {} },
|
||||||
|
|
|
@ -0,0 +1,55 @@
|
||||||
|
{
|
||||||
|
"admin": {
|
||||||
|
"access_log_path": "/dev/null",
|
||||||
|
"address": {
|
||||||
|
"socket_address": {
|
||||||
|
"address": "127.0.0.1",
|
||||||
|
"port_value": 19000
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node": {
|
||||||
|
"cluster": "test-proxy",
|
||||||
|
"id": "test-proxy"
|
||||||
|
},
|
||||||
|
"static_resources": {
|
||||||
|
"clusters": [
|
||||||
|
{
|
||||||
|
"name": "local_agent",
|
||||||
|
"connect_timeout": "1s",
|
||||||
|
"type": "STATIC",
|
||||||
|
"http2_protocol_options": {},
|
||||||
|
"hosts": [
|
||||||
|
{
|
||||||
|
"socket_address": {
|
||||||
|
"address": "127.0.0.1",
|
||||||
|
"port_value": 8502
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"stats_config":
|
||||||
|
{
|
||||||
|
"name": "fake_config"
|
||||||
|
},
|
||||||
|
"dynamic_resources": {
|
||||||
|
"lds_config": { "ads": {} },
|
||||||
|
"cds_config": { "ads": {} },
|
||||||
|
"ads_config": {
|
||||||
|
"api_type": "GRPC",
|
||||||
|
"grpc_services": {
|
||||||
|
"initial_metadata": [
|
||||||
|
{
|
||||||
|
"key": "x-consul-token",
|
||||||
|
"value": ""
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"envoy_grpc": {
|
||||||
|
"cluster_name": "local_agent"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,93 @@
|
||||||
|
{
|
||||||
|
"admin": {
|
||||||
|
"access_log_path": "/dev/null",
|
||||||
|
"address": {
|
||||||
|
"socket_address": {
|
||||||
|
"address": "127.0.0.1",
|
||||||
|
"port_value": 19000
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node": {
|
||||||
|
"cluster": "test-proxy",
|
||||||
|
"id": "test-proxy"
|
||||||
|
},
|
||||||
|
"static_resources": {
|
||||||
|
"clusters": [
|
||||||
|
{
|
||||||
|
"name": "local_agent",
|
||||||
|
"connect_timeout": "1s",
|
||||||
|
"type": "STATIC",
|
||||||
|
"http2_protocol_options": {},
|
||||||
|
"hosts": [
|
||||||
|
{
|
||||||
|
"socket_address": {
|
||||||
|
"address": "127.0.0.1",
|
||||||
|
"port_value": 8502
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "zipkin",
|
||||||
|
"type": "STRICT_DNS",
|
||||||
|
"connect_timeout": "5s",
|
||||||
|
"load_assignment": {
|
||||||
|
"cluster_name": "zipkin",
|
||||||
|
"endpoints": [
|
||||||
|
{
|
||||||
|
"lb_endpoints": [
|
||||||
|
{
|
||||||
|
"endpoint": {
|
||||||
|
"address": {
|
||||||
|
"socket_address": {
|
||||||
|
"address": "zipkin.service.consul",
|
||||||
|
"port_value": 9411
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"stats_config": {
|
||||||
|
"stats_tags": [
|
||||||
|
{
|
||||||
|
"tag_name": "local_cluster",
|
||||||
|
"fixed_value": "test-proxy"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"use_all_default_tags": true
|
||||||
|
},
|
||||||
|
"tracing": {
|
||||||
|
"http": {
|
||||||
|
"name": "envoy.zipkin",
|
||||||
|
"config": {
|
||||||
|
"collector_cluster": "zipkin",
|
||||||
|
"collector_endpoint": "/api/v1/spans"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"dynamic_resources": {
|
||||||
|
"lds_config": { "ads": {} },
|
||||||
|
"cds_config": { "ads": {} },
|
||||||
|
"ads_config": {
|
||||||
|
"api_type": "GRPC",
|
||||||
|
"grpc_services": {
|
||||||
|
"initial_metadata": [
|
||||||
|
{
|
||||||
|
"key": "x-consul-token",
|
||||||
|
"value": ""
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"envoy_grpc": {
|
||||||
|
"cluster_name": "local_agent"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
11
go.mod
11
go.mod
|
@ -16,7 +16,7 @@ require (
|
||||||
github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5 // indirect
|
github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5 // indirect
|
||||||
github.com/SAP/go-hdb v0.12.0 // indirect
|
github.com/SAP/go-hdb v0.12.0 // indirect
|
||||||
github.com/SermoDigital/jose v0.0.0-20180104203859-803625baeddc // indirect
|
github.com/SermoDigital/jose v0.0.0-20180104203859-803625baeddc // indirect
|
||||||
github.com/StackExchange/wmi v0.0.0-20160811214555-e54cbda6595d // indirect
|
github.com/StackExchange/wmi v0.0.0-20180116203802-5d049714c4a6 // indirect
|
||||||
github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e
|
github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e
|
||||||
github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da
|
github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da
|
||||||
github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310
|
github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310
|
||||||
|
@ -39,7 +39,7 @@ require (
|
||||||
github.com/envoyproxy/go-control-plane v0.0.0-20180919002855-2137d9196328
|
github.com/envoyproxy/go-control-plane v0.0.0-20180919002855-2137d9196328
|
||||||
github.com/fatih/structs v0.0.0-20180123065059-ebf56d35bba7 // indirect
|
github.com/fatih/structs v0.0.0-20180123065059-ebf56d35bba7 // indirect
|
||||||
github.com/go-ldap/ldap v3.0.2+incompatible // indirect
|
github.com/go-ldap/ldap v3.0.2+incompatible // indirect
|
||||||
github.com/go-ole/go-ole v0.0.0-20170601135611-02d3668a0cf0 // indirect
|
github.com/go-ole/go-ole v1.2.1 // indirect
|
||||||
github.com/go-sql-driver/mysql v0.0.0-20180618115901-749ddf1598b4 // indirect
|
github.com/go-sql-driver/mysql v0.0.0-20180618115901-749ddf1598b4 // indirect
|
||||||
github.com/go-test/deep v1.0.1 // indirect
|
github.com/go-test/deep v1.0.1 // indirect
|
||||||
github.com/gocql/gocql v0.0.0-20180617115710-e06f8c1bcd78 // indirect
|
github.com/gocql/gocql v0.0.0-20180617115710-e06f8c1bcd78 // indirect
|
||||||
|
@ -96,6 +96,7 @@ require (
|
||||||
github.com/mitchellh/mapstructure v1.1.2
|
github.com/mitchellh/mapstructure v1.1.2
|
||||||
github.com/mitchellh/reflectwalk v0.0.0-20170726202117-63d60e9d0dbc
|
github.com/mitchellh/reflectwalk v0.0.0-20170726202117-63d60e9d0dbc
|
||||||
github.com/oklog/run v0.0.0-20180308005104-6934b124db28 // indirect
|
github.com/oklog/run v0.0.0-20180308005104-6934b124db28 // indirect
|
||||||
|
github.com/onsi/gomega v1.4.2 // indirect
|
||||||
github.com/opencontainers/go-digest v1.0.0-rc1 // indirect
|
github.com/opencontainers/go-digest v1.0.0-rc1 // indirect
|
||||||
github.com/opencontainers/image-spec v1.0.1 // indirect
|
github.com/opencontainers/image-spec v1.0.1 // indirect
|
||||||
github.com/opencontainers/runc v0.1.1 // indirect
|
github.com/opencontainers/runc v0.1.1 // indirect
|
||||||
|
@ -114,10 +115,10 @@ require (
|
||||||
github.com/spf13/pflag v1.0.3 // indirect
|
github.com/spf13/pflag v1.0.3 // indirect
|
||||||
github.com/stretchr/objx v0.1.1 // indirect
|
github.com/stretchr/objx v0.1.1 // indirect
|
||||||
github.com/stretchr/testify v1.3.0
|
github.com/stretchr/testify v1.3.0
|
||||||
golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3
|
golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c
|
||||||
golang.org/x/net v0.0.0-20181201002055-351d144fa1fc
|
golang.org/x/net v0.0.0-20190403144856-b630fd6fe46b
|
||||||
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6
|
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6
|
||||||
golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5
|
golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e
|
||||||
golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2
|
golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2
|
||||||
google.golang.org/grpc v0.0.0-20180920234847-8997b5fa0873
|
google.golang.org/grpc v0.0.0-20180920234847-8997b5fa0873
|
||||||
gopkg.in/asn1-ber.v1 v1.0.0-20181015200546-f715ec2f112d // indirect
|
gopkg.in/asn1-ber.v1 v1.0.0-20181015200546-f715ec2f112d // indirect
|
||||||
|
|
22
go.sum
22
go.sum
|
@ -21,8 +21,8 @@ github.com/SAP/go-hdb v0.12.0 h1:5hBQZ2jjyZ268qjDmoDZJuCyLzR6oRLI60eYzmTW9m4=
|
||||||
github.com/SAP/go-hdb v0.12.0/go.mod h1:etBT+FAi1t5k3K3tf5vQTnosgYmhDkRi8jEnQqCnxF0=
|
github.com/SAP/go-hdb v0.12.0/go.mod h1:etBT+FAi1t5k3K3tf5vQTnosgYmhDkRi8jEnQqCnxF0=
|
||||||
github.com/SermoDigital/jose v0.0.0-20180104203859-803625baeddc h1:LkkwnbY+S8WmwkWq1SVyRWMH9nYWO1P5XN3OD1tts/w=
|
github.com/SermoDigital/jose v0.0.0-20180104203859-803625baeddc h1:LkkwnbY+S8WmwkWq1SVyRWMH9nYWO1P5XN3OD1tts/w=
|
||||||
github.com/SermoDigital/jose v0.0.0-20180104203859-803625baeddc/go.mod h1:ARgCUhI1MHQH+ONky/PAtmVHQrP5JlGY0F3poXOp/fA=
|
github.com/SermoDigital/jose v0.0.0-20180104203859-803625baeddc/go.mod h1:ARgCUhI1MHQH+ONky/PAtmVHQrP5JlGY0F3poXOp/fA=
|
||||||
github.com/StackExchange/wmi v0.0.0-20160811214555-e54cbda6595d h1:L9ZGqHf7d+ZeQDjVT/qv9WukazHxT+folVGy/xgaIc8=
|
github.com/StackExchange/wmi v0.0.0-20180116203802-5d049714c4a6 h1:fLjPD/aNc3UIOA6tDi6QXUemppXK3P9BI7mr2hd6gx8=
|
||||||
github.com/StackExchange/wmi v0.0.0-20160811214555-e54cbda6595d/go.mod h1:3eOhrUMpNV+6aFIbp5/iudMxNCF27Vw2OZgy4xEx0Fg=
|
github.com/StackExchange/wmi v0.0.0-20180116203802-5d049714c4a6/go.mod h1:3eOhrUMpNV+6aFIbp5/iudMxNCF27Vw2OZgy4xEx0Fg=
|
||||||
github.com/abdullin/seq v0.0.0-20160510034733-d5467c17e7af h1:DBNMBMuMiWYu0b+8KMJuWmfCkcxl09JwdlqwDZZ6U14=
|
github.com/abdullin/seq v0.0.0-20160510034733-d5467c17e7af h1:DBNMBMuMiWYu0b+8KMJuWmfCkcxl09JwdlqwDZZ6U14=
|
||||||
github.com/abdullin/seq v0.0.0-20160510034733-d5467c17e7af/go.mod h1:5Jv4cbFiHJMsVxt52+i0Ha45fjshj6wxYr1r19tB9bw=
|
github.com/abdullin/seq v0.0.0-20160510034733-d5467c17e7af/go.mod h1:5Jv4cbFiHJMsVxt52+i0Ha45fjshj6wxYr1r19tB9bw=
|
||||||
github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e h1:QEF07wC0T1rKkctt1RINW/+RMTVmiwxETico2l3gxJA=
|
github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e h1:QEF07wC0T1rKkctt1RINW/+RMTVmiwxETico2l3gxJA=
|
||||||
|
@ -91,8 +91,8 @@ github.com/go-ini/ini v1.25.4 h1:Mujh4R/dH6YL8bxuISne3xX2+qcQ9p0IxKAP6ExWoUo=
|
||||||
github.com/go-ini/ini v1.25.4/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8=
|
github.com/go-ini/ini v1.25.4/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8=
|
||||||
github.com/go-ldap/ldap v3.0.2+incompatible h1:kD5HQcAzlQ7yrhfn+h+MSABeAy/jAJhvIJ/QDllP44g=
|
github.com/go-ldap/ldap v3.0.2+incompatible h1:kD5HQcAzlQ7yrhfn+h+MSABeAy/jAJhvIJ/QDllP44g=
|
||||||
github.com/go-ldap/ldap v3.0.2+incompatible/go.mod h1:qfd9rJvER9Q0/D/Sqn1DfHRoBp40uXYvFoEVrNEPqRc=
|
github.com/go-ldap/ldap v3.0.2+incompatible/go.mod h1:qfd9rJvER9Q0/D/Sqn1DfHRoBp40uXYvFoEVrNEPqRc=
|
||||||
github.com/go-ole/go-ole v0.0.0-20170601135611-02d3668a0cf0 h1:2GWhNNlPnb5RHKNpnYXabh75bic5xflcnWslARlSGMA=
|
github.com/go-ole/go-ole v1.2.1 h1:2lOsA72HgjxAuMlKpFiCbHTvu44PIVkZ5hqm3RSdI/E=
|
||||||
github.com/go-ole/go-ole v0.0.0-20170601135611-02d3668a0cf0/go.mod h1:7FAglXiTm7HKlQRDeOQ6ZNUHidzCWXuZWq/1dTyBNF8=
|
github.com/go-ole/go-ole v1.2.1/go.mod h1:7FAglXiTm7HKlQRDeOQ6ZNUHidzCWXuZWq/1dTyBNF8=
|
||||||
github.com/go-sql-driver/mysql v0.0.0-20180618115901-749ddf1598b4 h1:1LlmVz15APoKz9dnm5j2ePptburJlwEH+/v/pUuoxck=
|
github.com/go-sql-driver/mysql v0.0.0-20180618115901-749ddf1598b4 h1:1LlmVz15APoKz9dnm5j2ePptburJlwEH+/v/pUuoxck=
|
||||||
github.com/go-sql-driver/mysql v0.0.0-20180618115901-749ddf1598b4/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
|
github.com/go-sql-driver/mysql v0.0.0-20180618115901-749ddf1598b4/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
|
||||||
github.com/go-test/deep v1.0.1 h1:UQhStjbkDClarlmv0am7OXXO4/GaPdCGiUiMTvi28sg=
|
github.com/go-test/deep v1.0.1 h1:UQhStjbkDClarlmv0am7OXXO4/GaPdCGiUiMTvi28sg=
|
||||||
|
@ -106,6 +106,7 @@ github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7a
|
||||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58=
|
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58=
|
||||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||||
github.com/golang/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:tluoj9z5200jBnyusfRPU2LqT6J+DAorxEvtC7LHB+E=
|
github.com/golang/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:tluoj9z5200jBnyusfRPU2LqT6J+DAorxEvtC7LHB+E=
|
||||||
|
github.com/golang/mock v1.1.1 h1:G5FRp8JnTd7RQH5kemVNlMeyXQAztQ3mOWV95KxsXH8=
|
||||||
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||||
github.com/golang/protobuf v1.2.0 h1:P3YflyNX/ehuJFLhxviNdFxQPkGK5cDcApsge1SqnvM=
|
github.com/golang/protobuf v1.2.0 h1:P3YflyNX/ehuJFLhxviNdFxQPkGK5cDcApsge1SqnvM=
|
||||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||||
|
@ -221,6 +222,7 @@ github.com/jtolds/gls v4.2.1+incompatible h1:fSuqC+Gmlu6l/ZYAoZzx2pyucC8Xza35fpR
|
||||||
github.com/jtolds/gls v4.2.1+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
|
github.com/jtolds/gls v4.2.1+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
|
||||||
github.com/keybase/go-crypto v0.0.0-20180614160407-5114a9a81e1b h1:VE6r2OwP5gj+Z9aCkSKl3MlmnZbfMAjhvR5T7abKHEo=
|
github.com/keybase/go-crypto v0.0.0-20180614160407-5114a9a81e1b h1:VE6r2OwP5gj+Z9aCkSKl3MlmnZbfMAjhvR5T7abKHEo=
|
||||||
github.com/keybase/go-crypto v0.0.0-20180614160407-5114a9a81e1b/go.mod h1:ghbZscTyKdM07+Fw3KSi0hcJm+AlEUWj8QLlPtijN/M=
|
github.com/keybase/go-crypto v0.0.0-20180614160407-5114a9a81e1b/go.mod h1:ghbZscTyKdM07+Fw3KSi0hcJm+AlEUWj8QLlPtijN/M=
|
||||||
|
github.com/kisielk/gotool v1.0.0 h1:AV2c/EiW3KqPNT9ZKl07ehoAGi4C5/01Cfbblndcapg=
|
||||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||||
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
|
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
|
||||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||||
|
@ -268,6 +270,8 @@ github.com/onsi/ginkgo v1.6.0 h1:Ix8l273rp3QzYgXSR+c8d1fTG7UPgYkOSELPhiY/YGw=
|
||||||
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||||
github.com/onsi/gomega v1.4.1 h1:PZSj/UFNaVp3KxrzHOcS7oyuWA7LoOY/77yCTEFu21U=
|
github.com/onsi/gomega v1.4.1 h1:PZSj/UFNaVp3KxrzHOcS7oyuWA7LoOY/77yCTEFu21U=
|
||||||
github.com/onsi/gomega v1.4.1/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA=
|
github.com/onsi/gomega v1.4.1/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA=
|
||||||
|
github.com/onsi/gomega v1.4.2 h1:3mYCb7aPxS/RU7TI1y4rkEn1oKmPRjNJLNEXgw7MH2I=
|
||||||
|
github.com/onsi/gomega v1.4.2/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
|
||||||
github.com/opencontainers/go-digest v1.0.0-rc1 h1:WzifXhOVOEOuFYOJAW6aQqW0TooG2iki3E3Ii+WN7gQ=
|
github.com/opencontainers/go-digest v1.0.0-rc1 h1:WzifXhOVOEOuFYOJAW6aQqW0TooG2iki3E3Ii+WN7gQ=
|
||||||
github.com/opencontainers/go-digest v1.0.0-rc1/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s=
|
github.com/opencontainers/go-digest v1.0.0-rc1/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s=
|
||||||
github.com/opencontainers/image-spec v1.0.1 h1:JMemWkRwHx4Zj+fVxWoMCFm/8sYGGrUVojFA6h/TRcI=
|
github.com/opencontainers/image-spec v1.0.1 h1:JMemWkRwHx4Zj+fVxWoMCFm/8sYGGrUVojFA6h/TRcI=
|
||||||
|
@ -337,12 +341,18 @@ github.com/vmware/govmomi v0.18.0 h1:f7QxSmP7meCtoAmiKZogvVbLInT+CZx6Px6K5rYsJZo
|
||||||
github.com/vmware/govmomi v0.18.0/go.mod h1:URlwyTFZX72RmxtxuaFL2Uj3fD1JTvZdx59bHWk6aFU=
|
github.com/vmware/govmomi v0.18.0/go.mod h1:URlwyTFZX72RmxtxuaFL2Uj3fD1JTvZdx59bHWk6aFU=
|
||||||
golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3 h1:KYQXGkl6vs02hK7pK4eIbw0NpNPedieTSTEiJ//bwGs=
|
golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3 h1:KYQXGkl6vs02hK7pK4eIbw0NpNPedieTSTEiJ//bwGs=
|
||||||
golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||||
|
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||||
|
golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c h1:Vj5n4GlwjmQteupaxJ9+0FNOmBrHfq7vN4btdGoDZgI=
|
||||||
|
golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||||
golang.org/x/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
golang.org/x/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
|
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519 h1:x6rhz8Y9CjbgQkccRGmELH6K+LJj7tOoh3XWeC1yaQM=
|
golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519 h1:x6rhz8Y9CjbgQkccRGmELH6K+LJj7tOoh3XWeC1yaQM=
|
||||||
golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
golang.org/x/net v0.0.0-20181201002055-351d144fa1fc h1:a3CU5tJYVj92DY2LaA1kUkrsqD5/3mLDhx2NcNqyW+0=
|
golang.org/x/net v0.0.0-20181201002055-351d144fa1fc h1:a3CU5tJYVj92DY2LaA1kUkrsqD5/3mLDhx2NcNqyW+0=
|
||||||
golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
|
golang.org/x/net v0.0.0-20190403144856-b630fd6fe46b h1:/zjbcJPEGAyu6Is/VBOALsgdi4z9+kz/Vtdm6S+beD0=
|
||||||
|
golang.org/x/net v0.0.0-20190403144856-b630fd6fe46b/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
golang.org/x/oauth2 v0.0.0-20170807180024-9a379c6b3e95/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
golang.org/x/oauth2 v0.0.0-20170807180024-9a379c6b3e95/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be h1:vEDujvNQGv4jgYKudGeI/+DAX4Jffq6hpD55MmoEvKs=
|
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be h1:vEDujvNQGv4jgYKudGeI/+DAX4Jffq6hpD55MmoEvKs=
|
||||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||||
|
@ -352,8 +362,12 @@ golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6 h1:bjcUS9ztw9kFmmIxJInhon/0
|
||||||
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
|
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5 h1:x6r4Jo0KNzOOzYd8lbcRsqjuqEASK6ob3auvWYM4/8U=
|
golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5 h1:x6r4Jo0KNzOOzYd8lbcRsqjuqEASK6ob3auvWYM4/8U=
|
||||||
golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
|
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
|
golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e h1:nFYrTHrdrAOpShe27kaFHjsqYSEQ0KWqdWLu3xuZJts=
|
||||||
|
golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
|
golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
|
||||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2 h1:+DCIGbF/swA92ohVg0//6X2IVY3KZs6p9mix0ziNYJM=
|
golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2 h1:+DCIGbF/swA92ohVg0//6X2IVY3KZs6p9mix0ziNYJM=
|
||||||
|
|
|
@ -0,0 +1,8 @@
|
||||||
|
FROM fortio/fortio AS fortio
|
||||||
|
|
||||||
|
FROM bats/bats:latest
|
||||||
|
|
||||||
|
RUN apk add curl
|
||||||
|
RUN apk add openssl
|
||||||
|
RUN apk add jq
|
||||||
|
COPY --from=fortio /usr/bin/fortio /usr/sbin/fortio
|
|
@ -0,0 +1,7 @@
|
||||||
|
# Note this arg has to be before the first FROM
|
||||||
|
ARG ENVOY_VERSION
|
||||||
|
|
||||||
|
FROM consul-dev as consul
|
||||||
|
|
||||||
|
FROM envoyproxy/envoy:v${ENVOY_VERSION}
|
||||||
|
COPY --from=consul /bin/consul /bin/consul
|
|
@ -0,0 +1,17 @@
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
# Setup deny intention
|
||||||
|
docker_consul intention create -deny s1 s2
|
||||||
|
|
||||||
|
docker_consul connect envoy -bootstrap \
|
||||||
|
-proxy-id s1-sidecar-proxy \
|
||||||
|
> workdir/envoy/s1-bootstrap.json
|
||||||
|
|
||||||
|
docker_consul connect envoy -bootstrap \
|
||||||
|
-proxy-id s2-sidecar-proxy \
|
||||||
|
-admin-bind 127.0.0.1:19001 \
|
||||||
|
> workdir/envoy/s2-bootstrap.json
|
||||||
|
|
||||||
|
export REQUIRED_SERVICES="s1 s1-sidecar-proxy s2 s2-sidecar-proxy"
|
|
@ -0,0 +1,6 @@
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
# Remove deny intention
|
||||||
|
docker_consul intention delete s1 s2
|
|
@ -0,0 +1,27 @@
|
||||||
|
#!/usr/bin/env bats
|
||||||
|
|
||||||
|
load helpers
|
||||||
|
|
||||||
|
@test "s1 proxy admin is up on :19000" {
|
||||||
|
retry_default curl -f -s localhost:19000/stats -o /dev/null
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "s2 proxy admin is up on :19001" {
|
||||||
|
retry_default curl -f -s localhost:19001/stats -o /dev/null
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "s1 proxy listener should be up and have right cert" {
|
||||||
|
assert_proxy_presents_cert_uri localhost:21000 s1
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "s2 proxy listener should be up and have right cert" {
|
||||||
|
assert_proxy_presents_cert_uri localhost:21001 s2
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "s1 upstream should NOT be able to connect to s2" {
|
||||||
|
run retry_default must_fail_tcp_connection localhost:5000
|
||||||
|
|
||||||
|
echo "OUTPUT $output"
|
||||||
|
|
||||||
|
[ "$status" == "0" ]
|
||||||
|
}
|
|
@ -0,0 +1,14 @@
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
docker_consul connect envoy -bootstrap \
|
||||||
|
-proxy-id s1-sidecar-proxy \
|
||||||
|
> workdir/envoy/s1-bootstrap.json
|
||||||
|
|
||||||
|
docker_consul connect envoy -bootstrap \
|
||||||
|
-proxy-id s2-sidecar-proxy \
|
||||||
|
-admin-bind 127.0.0.1:19001 \
|
||||||
|
> workdir/envoy/s2-bootstrap.json
|
||||||
|
|
||||||
|
export REQUIRED_SERVICES="s1 s1-sidecar-proxy s2 s2-sidecar-proxy"
|
|
@ -0,0 +1,25 @@
|
||||||
|
#!/usr/bin/env bats
|
||||||
|
|
||||||
|
load helpers
|
||||||
|
|
||||||
|
@test "s1 proxy admin is up on :19000" {
|
||||||
|
retry_default curl -f -s localhost:19000/stats -o /dev/null
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "s2 proxy admin is up on :19001" {
|
||||||
|
retry_default curl -f -s localhost:19001/stats -o /dev/null
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "s1 proxy listener should be up and have right cert" {
|
||||||
|
assert_proxy_presents_cert_uri localhost:21000 s1
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "s2 proxy listener should be up and have right cert" {
|
||||||
|
assert_proxy_presents_cert_uri localhost:21001 s2
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "s1 upstream should be able to connect to s2" {
|
||||||
|
run retry_default curl -s -f -d hello localhost:5000
|
||||||
|
[ "$status" -eq 0 ]
|
||||||
|
[ "$output" = "hello" ]
|
||||||
|
}
|
|
@ -0,0 +1,11 @@
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
# Bring up s1 and it's proxy as well because the check that it has a cert causes
|
||||||
|
# a proxy connection to be opened and having the backend not be available seems
|
||||||
|
# to cause Envoy to fail non-deterministically in CI (rarely on local machine).
|
||||||
|
# It might be related to this know issue
|
||||||
|
# https://github.com/envoyproxy/envoy/issues/2800 where TcpProxy will error if
|
||||||
|
# the backend is down sometimes part way through the handshake.
|
||||||
|
export REQUIRED_SERVICES="s1 s1-sidecar-proxy-consul-exec"
|
|
@ -0,0 +1,14 @@
|
||||||
|
#!/usr/bin/env bats
|
||||||
|
|
||||||
|
load helpers
|
||||||
|
|
||||||
|
# This test case really only validates the exec mechanism worked and brought
|
||||||
|
# envoy up.
|
||||||
|
|
||||||
|
@test "s1 proxy admin is up on :19000" {
|
||||||
|
retry_default curl -f -s localhost:19000/stats -o /dev/null
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "s1 proxy listener should be up and have right cert" {
|
||||||
|
assert_proxy_presents_cert_uri localhost:21000 s1
|
||||||
|
}
|
|
@ -0,0 +1,25 @@
|
||||||
|
services {
|
||||||
|
name = "s1"
|
||||||
|
port = 8080
|
||||||
|
connect {
|
||||||
|
sidecar_service {
|
||||||
|
proxy {
|
||||||
|
upstreams = [
|
||||||
|
{
|
||||||
|
destination_name = "s2"
|
||||||
|
local_bind_port = 5000
|
||||||
|
config {
|
||||||
|
protocol = "http"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
config {
|
||||||
|
protocol = "http"
|
||||||
|
envoy_dogstatsd_url = "udp://127.0.0.1:8125"
|
||||||
|
envoy_stats_tags = ["foo=bar"]
|
||||||
|
envoy_stats_flush_interval = "1s"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,14 @@
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
docker_consul connect envoy -bootstrap \
|
||||||
|
-proxy-id s1-sidecar-proxy \
|
||||||
|
> workdir/envoy/s1-bootstrap.json
|
||||||
|
|
||||||
|
docker_consul connect envoy -bootstrap \
|
||||||
|
-proxy-id s2-sidecar-proxy \
|
||||||
|
-admin-bind 127.0.0.1:19001 \
|
||||||
|
> workdir/envoy/s2-bootstrap.json
|
||||||
|
|
||||||
|
export REQUIRED_SERVICES="s1 s1-sidecar-proxy s2 s2-sidecar-proxy fake-statsd"
|
|
@ -0,0 +1,63 @@
|
||||||
|
#!/usr/bin/env bats
|
||||||
|
|
||||||
|
load helpers
|
||||||
|
|
||||||
|
@test "s1 proxy admin is up on :19000" {
|
||||||
|
retry_default curl -f -s localhost:19000/stats -o /dev/null
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "s2 proxy admin is up on :19001" {
|
||||||
|
retry_default curl -f -s localhost:19001/stats -o /dev/null
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "s1 upstream should be able to connect to s2" {
|
||||||
|
run retry_default curl -s -f -d hello localhost:5000
|
||||||
|
|
||||||
|
echo "OUTPUT: $output"
|
||||||
|
|
||||||
|
[ "$status" == 0 ]
|
||||||
|
[ "$output" == "hello" ]
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "s1 proxy should be sending metrics to statsd" {
|
||||||
|
run retry_default cat /workdir/statsd/statsd.log
|
||||||
|
|
||||||
|
echo "METRICS:"
|
||||||
|
echo "$output"
|
||||||
|
echo "COUNT: $(echo "$output" | grep -Ec '^envoy\.')"
|
||||||
|
|
||||||
|
[ "$status" == 0 ]
|
||||||
|
[ $(echo $output | grep -Ec '^envoy\.') -gt "0" ]
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "s1 proxy should be sending dogstatsd tagged metrics" {
|
||||||
|
run retry_default must_match_in_statsd_logs '[#,]local_cluster:s1(,|$)'
|
||||||
|
|
||||||
|
echo "OUTPUT: $output"
|
||||||
|
|
||||||
|
[ "$status" == 0 ]
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "s1 proxy should be adding cluster name as a tag" {
|
||||||
|
run retry_default must_match_in_statsd_logs '[#,]envoy.cluster_name:s2(,|$)'
|
||||||
|
|
||||||
|
echo "OUTPUT: $output"
|
||||||
|
|
||||||
|
[ "$status" == 0 ]
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "s1 proxy should be sending additional configured tags" {
|
||||||
|
run retry_default must_match_in_statsd_logs '[#,]foo:bar(,|$)'
|
||||||
|
|
||||||
|
echo "OUTPUT: $output"
|
||||||
|
|
||||||
|
[ "$status" == 0 ]
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "s1 proxy should have custom stats flush interval" {
|
||||||
|
INTERVAL=$(get_envoy_stats_flush_interval localhost:19000)
|
||||||
|
|
||||||
|
echo "INTERVAL = $INTERVAL"
|
||||||
|
|
||||||
|
[ "$INTERVAL" == "1s" ]
|
||||||
|
}
|
|
@ -0,0 +1,25 @@
|
||||||
|
services {
|
||||||
|
name = "s1"
|
||||||
|
port = 8079
|
||||||
|
connect {
|
||||||
|
sidecar_service {
|
||||||
|
proxy {
|
||||||
|
upstreams = [
|
||||||
|
{
|
||||||
|
destination_name = "s2"
|
||||||
|
local_bind_port = 5000
|
||||||
|
config {
|
||||||
|
protocol = "grpc"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
config {
|
||||||
|
protocol = "grpc"
|
||||||
|
envoy_dogstatsd_url = "udp://127.0.0.1:8125"
|
||||||
|
envoy_stats_tags = ["foo=bar"]
|
||||||
|
envoy_stats_flush_interval = "1s"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,14 @@
|
||||||
|
services {
|
||||||
|
name = "s2"
|
||||||
|
# Advertise gRPC port
|
||||||
|
port = 8179
|
||||||
|
connect {
|
||||||
|
sidecar_service {
|
||||||
|
proxy {
|
||||||
|
config {
|
||||||
|
protocol = "grpc"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,14 @@
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
docker_consul connect envoy -bootstrap \
|
||||||
|
-proxy-id s1-sidecar-proxy \
|
||||||
|
> workdir/envoy/s1-bootstrap.json
|
||||||
|
|
||||||
|
docker_consul connect envoy -bootstrap \
|
||||||
|
-proxy-id s2-sidecar-proxy \
|
||||||
|
-admin-bind 127.0.0.1:19001 \
|
||||||
|
> workdir/envoy/s2-bootstrap.json
|
||||||
|
|
||||||
|
export REQUIRED_SERVICES="s1 s1-sidecar-proxy s2 s2-sidecar-proxy fake-statsd"
|
|
@ -0,0 +1,27 @@
|
||||||
|
#!/usr/bin/env bats
|
||||||
|
|
||||||
|
load helpers
|
||||||
|
|
||||||
|
@test "s1 proxy admin is up on :19000" {
|
||||||
|
retry_default curl -f -s localhost:19000/stats -o /dev/null
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "s2 proxy admin is up on :19001" {
|
||||||
|
retry_default curl -f -s localhost:19001/stats -o /dev/null
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "s1 upstream should be able to connect to s2 via grpc" {
|
||||||
|
run fortio grpcping localhost:5000
|
||||||
|
|
||||||
|
echo "OUTPUT: $output"
|
||||||
|
|
||||||
|
[ "$status" == 0 ]
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "s1 proxy should be sending gRPC metrics to statsd" {
|
||||||
|
run retry_default must_match_in_statsd_logs 'envoy.cluster.grpc.PingServer.total'
|
||||||
|
|
||||||
|
echo "OUTPUT: $output"
|
||||||
|
|
||||||
|
[ "$status" == 0 ]
|
||||||
|
}
|
|
@ -0,0 +1,22 @@
|
||||||
|
services {
|
||||||
|
name = "s1"
|
||||||
|
port = 8080
|
||||||
|
connect {
|
||||||
|
sidecar_service {
|
||||||
|
proxy {
|
||||||
|
upstreams = [
|
||||||
|
{
|
||||||
|
destination_name = "s2"
|
||||||
|
local_bind_port = 5000
|
||||||
|
config {
|
||||||
|
protocol = "http"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
config {
|
||||||
|
protocol = "http"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,13 @@
|
||||||
|
services {
|
||||||
|
name = "s2"
|
||||||
|
port = 8181
|
||||||
|
connect {
|
||||||
|
sidecar_service {
|
||||||
|
proxy {
|
||||||
|
config {
|
||||||
|
protocol = "http"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,17 @@
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
# Setup deny intention
|
||||||
|
docker_consul intention create -deny s1 s2
|
||||||
|
|
||||||
|
docker_consul connect envoy -bootstrap \
|
||||||
|
-proxy-id s1-sidecar-proxy \
|
||||||
|
> workdir/envoy/s1-bootstrap.json
|
||||||
|
|
||||||
|
docker_consul connect envoy -bootstrap \
|
||||||
|
-proxy-id s2-sidecar-proxy \
|
||||||
|
-admin-bind 127.0.0.1:19001 \
|
||||||
|
> workdir/envoy/s2-bootstrap.json
|
||||||
|
|
||||||
|
export REQUIRED_SERVICES="s1 s1-sidecar-proxy s2 s2-sidecar-proxy"
|
|
@ -0,0 +1,6 @@
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
# Remove deny intention
|
||||||
|
docker_consul intention delete s1 s2
|
|
@ -0,0 +1,27 @@
|
||||||
|
#!/usr/bin/env bats
|
||||||
|
|
||||||
|
load helpers
|
||||||
|
|
||||||
|
@test "s1 proxy admin is up on :19000" {
|
||||||
|
retry_default curl -f -s localhost:19000/stats -o /dev/null
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "s2 proxy admin is up on :19001" {
|
||||||
|
retry_default curl -f -s localhost:19001/stats -o /dev/null
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "s1 proxy listener should be up and have right cert" {
|
||||||
|
assert_proxy_presents_cert_uri localhost:21000 s1
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "s2 proxy listener should be up and have right cert" {
|
||||||
|
assert_proxy_presents_cert_uri localhost:21001 s2
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "s1 upstream should NOT be able to connect to s2" {
|
||||||
|
run retry_default must_fail_http_connection localhost:5000
|
||||||
|
|
||||||
|
echo "OUTPUT $output"
|
||||||
|
|
||||||
|
[ "$status" == "0" ]
|
||||||
|
}
|
|
@ -0,0 +1,22 @@
|
||||||
|
services {
|
||||||
|
name = "s1"
|
||||||
|
port = 8080
|
||||||
|
connect {
|
||||||
|
sidecar_service {
|
||||||
|
proxy {
|
||||||
|
upstreams = [
|
||||||
|
{
|
||||||
|
destination_name = "s2"
|
||||||
|
local_bind_port = 5000
|
||||||
|
config {
|
||||||
|
protocol = "http"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
config {
|
||||||
|
protocol = "http"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,13 @@
|
||||||
|
services {
|
||||||
|
name = "s2"
|
||||||
|
port = 8181
|
||||||
|
connect {
|
||||||
|
sidecar_service {
|
||||||
|
proxy {
|
||||||
|
config {
|
||||||
|
protocol = "http"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,14 @@
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
docker_consul connect envoy -bootstrap \
|
||||||
|
-proxy-id s1-sidecar-proxy \
|
||||||
|
> workdir/envoy/s1-bootstrap.json
|
||||||
|
|
||||||
|
docker_consul connect envoy -bootstrap \
|
||||||
|
-proxy-id s2-sidecar-proxy \
|
||||||
|
-admin-bind 127.0.0.1:19001 \
|
||||||
|
> workdir/envoy/s2-bootstrap.json
|
||||||
|
|
||||||
|
export REQUIRED_SERVICES="s1 s1-sidecar-proxy s2 s2-sidecar-proxy"
|
|
@ -0,0 +1,39 @@
|
||||||
|
#!/usr/bin/env bats
|
||||||
|
|
||||||
|
load helpers
|
||||||
|
|
||||||
|
@test "s1 proxy admin is up on :19000" {
|
||||||
|
retry_default curl -f -s localhost:19000/stats -o /dev/null
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "s2 proxy admin is up on :19001" {
|
||||||
|
retry_default curl -f -s localhost:19001/stats -o /dev/null
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "s1 proxy listener should be up and have right cert" {
|
||||||
|
assert_proxy_presents_cert_uri localhost:21000 s1
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "s2 proxy listener should be up and have right cert" {
|
||||||
|
assert_proxy_presents_cert_uri localhost:21001 s2
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "s1 upstream should be able to connect to s2 with http/1.1" {
|
||||||
|
run retry_default curl --http1.1 -s -f -d hello localhost:5000
|
||||||
|
[ "$status" -eq 0 ]
|
||||||
|
[ "$output" = "hello" ]
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "s1 proxy should have been configured with http connection managers" {
|
||||||
|
LISTEN_FILTERS=$(get_envoy_listener_filters localhost:19000)
|
||||||
|
PUB=$(echo "$LISTEN_FILTERS" | grep -E "^public_listener:" | cut -f 2 -d ' ' )
|
||||||
|
UPS=$(echo "$LISTEN_FILTERS" | grep -E "^s2:" | cut -f 2 -d ' ' )
|
||||||
|
|
||||||
|
echo "LISTEN_FILTERS = $LISTEN_FILTERS"
|
||||||
|
echo "PUB = $PUB"
|
||||||
|
echo "UPS = $UPS"
|
||||||
|
|
||||||
|
[ "$PUB" = "envoy.ext_authz,envoy.http_connection_manager" ]
|
||||||
|
[ "$UPS" = "envoy.http_connection_manager" ]
|
||||||
|
}
|
|
@ -0,0 +1,22 @@
|
||||||
|
services {
|
||||||
|
name = "s1"
|
||||||
|
port = 8080
|
||||||
|
connect {
|
||||||
|
sidecar_service {
|
||||||
|
proxy {
|
||||||
|
upstreams = [
|
||||||
|
{
|
||||||
|
destination_name = "s2"
|
||||||
|
local_bind_port = 5000
|
||||||
|
config {
|
||||||
|
protocol = "http2"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
config {
|
||||||
|
protocol = "http2"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,14 @@
|
||||||
|
services {
|
||||||
|
name = "s2"
|
||||||
|
# Advertise gRPC (http2) port
|
||||||
|
port = 8179
|
||||||
|
connect {
|
||||||
|
sidecar_service {
|
||||||
|
proxy {
|
||||||
|
config {
|
||||||
|
protocol = "http2"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,14 @@
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
docker_consul connect envoy -bootstrap \
|
||||||
|
-proxy-id s1-sidecar-proxy \
|
||||||
|
> workdir/envoy/s1-bootstrap.json
|
||||||
|
|
||||||
|
docker_consul connect envoy -bootstrap \
|
||||||
|
-proxy-id s2-sidecar-proxy \
|
||||||
|
-admin-bind 127.0.0.1:19001 \
|
||||||
|
> workdir/envoy/s2-bootstrap.json
|
||||||
|
|
||||||
|
export REQUIRED_SERVICES="s1 s1-sidecar-proxy s2 s2-sidecar-proxy"
|
|
@ -0,0 +1,31 @@
|
||||||
|
#!/usr/bin/env bats
|
||||||
|
|
||||||
|
load helpers
|
||||||
|
|
||||||
|
@test "s1 proxy admin is up on :19000" {
|
||||||
|
retry_default curl -f -s localhost:19000/stats -o /dev/null
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "s2 proxy admin is up on :19001" {
|
||||||
|
retry_default curl -f -s localhost:19001/stats -o /dev/null
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "s1 proxy listener should be up and have right cert" {
|
||||||
|
assert_proxy_presents_cert_uri localhost:21000 s1
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "s2 proxy listener should be up and have right cert" {
|
||||||
|
assert_proxy_presents_cert_uri localhost:21001 s2
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "s1 upstream should be able to connect to s2 via http2" {
|
||||||
|
# We use grpc here because it's the easiest way to test http2. The server
|
||||||
|
# needs to support h2c since the proxy doesn't talk TLS to the local app.
|
||||||
|
# Most http2 servers don't support that but gRPC does. We could use curl
|
||||||
|
run curl -f -s -X POST localhost:5000/PingServer.Ping/
|
||||||
|
|
||||||
|
echo "OUTPUT: $output"
|
||||||
|
|
||||||
|
[ "$status" == 0 ]
|
||||||
|
}
|
|
@ -0,0 +1,23 @@
|
||||||
|
services {
|
||||||
|
name = "s1"
|
||||||
|
port = 8080
|
||||||
|
connect {
|
||||||
|
sidecar_service {
|
||||||
|
proxy {
|
||||||
|
upstreams = [
|
||||||
|
{
|
||||||
|
destination_name = "s2"
|
||||||
|
local_bind_port = 5000
|
||||||
|
config {
|
||||||
|
protocol = "http"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
config {
|
||||||
|
protocol = "http"
|
||||||
|
envoy_prometheus_bind_addr = "0.0.0.0:1234"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,13 @@
|
||||||
|
services {
|
||||||
|
name = "s2"
|
||||||
|
port = 8181
|
||||||
|
connect {
|
||||||
|
sidecar_service {
|
||||||
|
proxy {
|
||||||
|
config {
|
||||||
|
protocol = "http"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,14 @@
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
docker_consul connect envoy -bootstrap \
|
||||||
|
-proxy-id s1-sidecar-proxy \
|
||||||
|
> workdir/envoy/s1-bootstrap.json
|
||||||
|
|
||||||
|
docker_consul connect envoy -bootstrap \
|
||||||
|
-proxy-id s2-sidecar-proxy \
|
||||||
|
-admin-bind 127.0.0.1:19001 \
|
||||||
|
> workdir/envoy/s2-bootstrap.json
|
||||||
|
|
||||||
|
export REQUIRED_SERVICES="s1 s1-sidecar-proxy s2 s2-sidecar-proxy"
|
|
@ -0,0 +1,44 @@
|
||||||
|
#!/usr/bin/env bats
|
||||||
|
|
||||||
|
load helpers
|
||||||
|
|
||||||
|
@test "s1 proxy admin is up on :19000" {
|
||||||
|
retry_default curl -f -s localhost:19000/stats -o /dev/null
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "s2 proxy admin is up on :19001" {
|
||||||
|
retry_default curl -f -s localhost:19001/stats -o /dev/null
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "s1 proxy listener should be up and have right cert" {
|
||||||
|
assert_proxy_presents_cert_uri localhost:21000 s1
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "s2 proxy listener should be up and have right cert" {
|
||||||
|
assert_proxy_presents_cert_uri localhost:21001 s2
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "s1 upstream should be able to connect to s2 with http/1.1" {
|
||||||
|
run retry_default curl --http1.1 -s -f -d hello localhost:5000
|
||||||
|
[ "$status" -eq 0 ]
|
||||||
|
[ "$output" = "hello" ]
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "s1 proxy should be exposing metrics to prometheus" {
|
||||||
|
# Should have http metrics. This is just a sample one. Require the metric to
|
||||||
|
# be present not just found in a comment (anchor the regexp).
|
||||||
|
run retry_defaults \
|
||||||
|
must_match_in_prometheus_response localhost:1234 \
|
||||||
|
'^envoy_http_downstream_rq_active'
|
||||||
|
|
||||||
|
# Should be labelling with local_cluster.
|
||||||
|
run retry_defaults \
|
||||||
|
must_match_in_prometheus_response localhost:1234 \
|
||||||
|
'[\{,]local_cluster="s1"[,}] '
|
||||||
|
|
||||||
|
# Should be labelling with http listener prefix.
|
||||||
|
run retry_defaults \
|
||||||
|
must_match_in_prometheus_response localhost:1234 \
|
||||||
|
'[\{,]envoy_http_conn_manager_prefix="public_listener_http"[,}]'
|
||||||
|
}
|
|
@ -0,0 +1,23 @@
|
||||||
|
services {
|
||||||
|
name = "s1"
|
||||||
|
port = 8080
|
||||||
|
connect {
|
||||||
|
sidecar_service {
|
||||||
|
proxy {
|
||||||
|
upstreams = [
|
||||||
|
{
|
||||||
|
destination_name = "s2"
|
||||||
|
local_bind_port = 5000
|
||||||
|
config {
|
||||||
|
protocol = "http"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
config {
|
||||||
|
protocol = "http"
|
||||||
|
envoy_statsd_url = "udp://127.0.0.1:8125"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,14 @@
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
docker_consul connect envoy -bootstrap \
|
||||||
|
-proxy-id s1-sidecar-proxy \
|
||||||
|
> workdir/envoy/s1-bootstrap.json
|
||||||
|
|
||||||
|
docker_consul connect envoy -bootstrap \
|
||||||
|
-proxy-id s2-sidecar-proxy \
|
||||||
|
-admin-bind 127.0.0.1:19001 \
|
||||||
|
> workdir/envoy/s2-bootstrap.json
|
||||||
|
|
||||||
|
export REQUIRED_SERVICES="s1 s1-sidecar-proxy s2 s2-sidecar-proxy fake-statsd"
|
|
@ -0,0 +1,25 @@
|
||||||
|
#!/usr/bin/env bats
|
||||||
|
|
||||||
|
load helpers
|
||||||
|
|
||||||
|
@test "s1 proxy admin is up on :19000" {
|
||||||
|
retry_default curl -f -s localhost:19000/stats -o /dev/null
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "s2 proxy admin is up on :19001" {
|
||||||
|
retry_default curl -f -s localhost:19001/stats -o /dev/null
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "s1 upstream should be able to connect to s2" {
|
||||||
|
run retry_default curl -s -f -d hello localhost:5000
|
||||||
|
[ "$status" == 0 ]
|
||||||
|
[ "$output" == "hello" ]
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "s1 proxy should be sending metrics to statsd" {
|
||||||
|
run retry_default must_match_in_statsd_logs '^envoy\.'
|
||||||
|
|
||||||
|
echo "OUTPUT: $output"
|
||||||
|
|
||||||
|
[ "$status" == 0 ]
|
||||||
|
}
|
|
@ -0,0 +1,60 @@
|
||||||
|
services {
|
||||||
|
name = "s1"
|
||||||
|
port = 8080
|
||||||
|
connect {
|
||||||
|
sidecar_service {
|
||||||
|
proxy {
|
||||||
|
upstreams = [
|
||||||
|
{
|
||||||
|
destination_name = "s2"
|
||||||
|
local_bind_port = 5000
|
||||||
|
config {
|
||||||
|
protocol = "http"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
config {
|
||||||
|
protocol = "http"
|
||||||
|
envoy_tracing_json = <<EOF
|
||||||
|
{
|
||||||
|
"http": {
|
||||||
|
"name": "envoy.zipkin",
|
||||||
|
"config": {
|
||||||
|
"collector_cluster": "zipkin",
|
||||||
|
"collector_endpoint": "/api/v1/spans",
|
||||||
|
"shared_span_context": false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
EOF
|
||||||
|
envoy_extra_static_clusters_json = <<EOF2
|
||||||
|
{
|
||||||
|
"name": "zipkin",
|
||||||
|
"type": "STRICT_DNS",
|
||||||
|
"connect_timeout": "5s",
|
||||||
|
"load_assignment": {
|
||||||
|
"cluster_name": "zipkin",
|
||||||
|
"endpoints": [
|
||||||
|
{
|
||||||
|
"lb_endpoints": [
|
||||||
|
{
|
||||||
|
"endpoint": {
|
||||||
|
"address": {
|
||||||
|
"socket_address": {
|
||||||
|
"address": "127.0.0.1",
|
||||||
|
"port_value": 9411
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
EOF2
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,51 @@
|
||||||
|
services {
|
||||||
|
name = "s2"
|
||||||
|
port = 8181
|
||||||
|
connect {
|
||||||
|
sidecar_service {
|
||||||
|
proxy {
|
||||||
|
config {
|
||||||
|
protocol = "http"
|
||||||
|
envoy_tracing_json = <<EOF
|
||||||
|
{
|
||||||
|
"http": {
|
||||||
|
"name": "envoy.zipkin",
|
||||||
|
"config": {
|
||||||
|
"collector_cluster": "zipkin",
|
||||||
|
"collector_endpoint": "/api/v1/spans",
|
||||||
|
"shared_span_context": false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
EOF
|
||||||
|
envoy_extra_static_clusters_json = <<EOF2
|
||||||
|
{
|
||||||
|
"name": "zipkin",
|
||||||
|
"type": "STRICT_DNS",
|
||||||
|
"connect_timeout": "5s",
|
||||||
|
"load_assignment": {
|
||||||
|
"cluster_name": "zipkin",
|
||||||
|
"endpoints": [
|
||||||
|
{
|
||||||
|
"lb_endpoints": [
|
||||||
|
{
|
||||||
|
"endpoint": {
|
||||||
|
"address": {
|
||||||
|
"socket_address": {
|
||||||
|
"address": "127.0.0.1",
|
||||||
|
"port_value": 9411
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
EOF2
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,14 @@
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
docker_consul connect envoy -bootstrap \
|
||||||
|
-proxy-id s1-sidecar-proxy \
|
||||||
|
> workdir/envoy/s1-bootstrap.json
|
||||||
|
|
||||||
|
docker_consul connect envoy -bootstrap \
|
||||||
|
-proxy-id s2-sidecar-proxy \
|
||||||
|
-admin-bind 127.0.0.1:19001 \
|
||||||
|
> workdir/envoy/s2-bootstrap.json
|
||||||
|
|
||||||
|
export REQUIRED_SERVICES="s1 s1-sidecar-proxy s2 s2-sidecar-proxy jaeger"
|
|
@ -0,0 +1,44 @@
|
||||||
|
#!/usr/bin/env bats
|
||||||
|
|
||||||
|
load helpers
|
||||||
|
|
||||||
|
@test "s1 proxy admin is up on :19000" {
|
||||||
|
retry_default curl -f -s localhost:19000/stats -o /dev/null
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "s2 proxy admin is up on :19001" {
|
||||||
|
retry_default curl -f -s localhost:19001/stats -o /dev/null
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "s1 proxy listener should be up and have right cert" {
|
||||||
|
assert_proxy_presents_cert_uri localhost:21000 s1
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "s2 proxy listener should be up and have right cert" {
|
||||||
|
assert_proxy_presents_cert_uri localhost:21001 s2
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "s1 upstream should be able to connect to s2" {
|
||||||
|
run retry_default curl -s -f -d hello localhost:5000
|
||||||
|
[ "$status" == "0" ]
|
||||||
|
[ "$output" == "hello" ]
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "s1 proxy should send trace spans to zipkin/jaeger" {
|
||||||
|
# Send traced request through upstream. Debug echoes headers back which we can
|
||||||
|
# use to get the traceID generated (no way to force one I can find with Envoy
|
||||||
|
# currently?)
|
||||||
|
run curl -s -f -H 'x-client-trace-id:test-sentinel' localhost:5000/Debug
|
||||||
|
|
||||||
|
echo "OUTPUT $output"
|
||||||
|
|
||||||
|
[ "$status" == "0" ]
|
||||||
|
|
||||||
|
# Get the traceID from the output
|
||||||
|
TRACEID=$(echo $output | grep 'X-B3-Traceid:' | cut -c 15-)
|
||||||
|
|
||||||
|
# Get the trace from Jaeger. Won't bother parsing it just seeing it show up
|
||||||
|
# there is enough to know that the tracing config worked.
|
||||||
|
run retry_default curl -s -f "localhost:16686/api/traces/$TRACEID"
|
||||||
|
}
|
|
@ -0,0 +1,16 @@
|
||||||
|
services {
|
||||||
|
name = "s1"
|
||||||
|
port = 8080
|
||||||
|
connect {
|
||||||
|
sidecar_service {
|
||||||
|
proxy {
|
||||||
|
upstreams = [
|
||||||
|
{
|
||||||
|
destination_name = "s2"
|
||||||
|
local_bind_port = 5000
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,5 @@
|
||||||
|
services {
|
||||||
|
name = "s2"
|
||||||
|
port = 8181
|
||||||
|
connect { sidecar_service {} }
|
||||||
|
}
|
|
@ -0,0 +1,193 @@
|
||||||
|
version: '3.4'
|
||||||
|
|
||||||
|
x-workdir:
|
||||||
|
&workdir-volume
|
||||||
|
type: volume
|
||||||
|
source: workdir
|
||||||
|
target: /workdir
|
||||||
|
volume:
|
||||||
|
nocopy: true
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
workdir:
|
||||||
|
|
||||||
|
services:
|
||||||
|
# This is a dummy container that we use to create volume and keep it
|
||||||
|
# accessible while other containers are down.
|
||||||
|
workdir:
|
||||||
|
image: alpine
|
||||||
|
volumes:
|
||||||
|
- *workdir-volume
|
||||||
|
command:
|
||||||
|
- sleep
|
||||||
|
- "86400"
|
||||||
|
|
||||||
|
consul:
|
||||||
|
image: "consul-dev"
|
||||||
|
command:
|
||||||
|
- "agent"
|
||||||
|
- "-dev"
|
||||||
|
- "-config-dir"
|
||||||
|
- "/workdir/consul"
|
||||||
|
- "-client"
|
||||||
|
- "0.0.0.0"
|
||||||
|
volumes:
|
||||||
|
- *workdir-volume
|
||||||
|
ports:
|
||||||
|
# Exposing to host makes debugging locally a bit easier
|
||||||
|
- "8500:8500"
|
||||||
|
- "8502:8502"
|
||||||
|
# For zipkin which uses this containers network
|
||||||
|
- 9411:9411
|
||||||
|
# Jaeger UI
|
||||||
|
- 16686:16686
|
||||||
|
|
||||||
|
s1:
|
||||||
|
depends_on:
|
||||||
|
- consul
|
||||||
|
image: "fortio/fortio"
|
||||||
|
command:
|
||||||
|
- "server"
|
||||||
|
- "-http-port"
|
||||||
|
- ":8080"
|
||||||
|
- "-grpc-port"
|
||||||
|
- ":8079"
|
||||||
|
network_mode: service:consul
|
||||||
|
|
||||||
|
s2:
|
||||||
|
depends_on:
|
||||||
|
- consul
|
||||||
|
image: "fortio/fortio"
|
||||||
|
command:
|
||||||
|
- "server"
|
||||||
|
- "-http-port"
|
||||||
|
- ":8181"
|
||||||
|
- "-grpc-port"
|
||||||
|
- ":8179"
|
||||||
|
network_mode: service:consul
|
||||||
|
|
||||||
|
s1-sidecar-proxy:
|
||||||
|
depends_on:
|
||||||
|
- consul
|
||||||
|
image: "envoyproxy/envoy:v${ENVOY_VERSION:-1.8.0}"
|
||||||
|
command:
|
||||||
|
- "envoy"
|
||||||
|
- "-c"
|
||||||
|
- "/workdir/envoy/s1-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
|
||||||
|
|
||||||
|
s2-sidecar-proxy:
|
||||||
|
depends_on:
|
||||||
|
- consul
|
||||||
|
image: "envoyproxy/envoy:v${ENVOY_VERSION:-1.8.0}"
|
||||||
|
command:
|
||||||
|
- "envoy"
|
||||||
|
- "-c"
|
||||||
|
- "/workdir/envoy/s2-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
|
||||||
|
|
||||||
|
verify:
|
||||||
|
depends_on:
|
||||||
|
- consul
|
||||||
|
build:
|
||||||
|
context: .
|
||||||
|
dockerfile: Dockerfile-bats
|
||||||
|
tty: true
|
||||||
|
command:
|
||||||
|
- "--pretty"
|
||||||
|
- "/workdir/bats"
|
||||||
|
volumes:
|
||||||
|
- *workdir-volume
|
||||||
|
network_mode: service:consul
|
||||||
|
|
||||||
|
s1-sidecar-proxy-consul-exec:
|
||||||
|
depends_on:
|
||||||
|
- consul
|
||||||
|
build:
|
||||||
|
context: .
|
||||||
|
dockerfile: Dockerfile-consul-envoy
|
||||||
|
args:
|
||||||
|
ENVOY_VERSION: ${ENVOY_VERSION:-1.8.0}
|
||||||
|
command:
|
||||||
|
- "consul"
|
||||||
|
- "connect"
|
||||||
|
- "envoy"
|
||||||
|
- "-sidecar-for"
|
||||||
|
- "s1"
|
||||||
|
- "--"
|
||||||
|
- "-l"
|
||||||
|
- "debug"
|
||||||
|
network_mode: service:consul
|
||||||
|
|
||||||
|
fake-statsd:
|
||||||
|
depends_on:
|
||||||
|
- consul
|
||||||
|
image: "alpine/socat"
|
||||||
|
command:
|
||||||
|
- -u
|
||||||
|
- UDP-RECVFROM:8125,fork,reuseaddr
|
||||||
|
# This magic incantation is needed since Envoy doesn't add newlines and so
|
||||||
|
# we need each packet to be passed to echo to add a new line before
|
||||||
|
# appending.
|
||||||
|
- SYSTEM:'xargs -0 echo >> /workdir/statsd/statsd.log'
|
||||||
|
volumes:
|
||||||
|
- *workdir-volume
|
||||||
|
network_mode: service:consul
|
||||||
|
|
||||||
|
wipe-volumes:
|
||||||
|
volumes:
|
||||||
|
- *workdir-volume
|
||||||
|
image: alpine
|
||||||
|
command:
|
||||||
|
- sh
|
||||||
|
- -c
|
||||||
|
- 'rm -rf /workdir/*'
|
||||||
|
|
||||||
|
# This is a debugging tool run docker-compose up dump-volumes to see the
|
||||||
|
# current state.
|
||||||
|
dump-volumes:
|
||||||
|
volumes:
|
||||||
|
- *workdir-volume
|
||||||
|
- ./:/cwd
|
||||||
|
image: alpine
|
||||||
|
command:
|
||||||
|
- cp
|
||||||
|
- -r
|
||||||
|
- /workdir/.
|
||||||
|
- /cwd/workdir/
|
||||||
|
|
||||||
|
zipkin:
|
||||||
|
volumes:
|
||||||
|
- *workdir-volume
|
||||||
|
image: openzipkin/zipkin
|
||||||
|
network_mode: service:consul
|
||||||
|
|
||||||
|
jaeger:
|
||||||
|
volumes:
|
||||||
|
- *workdir-volume
|
||||||
|
image: jaegertracing/all-in-one:1.11
|
||||||
|
network_mode: service:consul
|
||||||
|
command:
|
||||||
|
- --collector.zipkin.http-port=9411
|
|
@ -0,0 +1,132 @@
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# retry based on
|
||||||
|
# https://github.com/fernandoacorreia/azure-docker-registry/blob/master/tools/scripts/create-registry-server
|
||||||
|
# under MIT license.
|
||||||
|
function retry {
|
||||||
|
local n=1
|
||||||
|
local max=$1
|
||||||
|
shift
|
||||||
|
local delay=$1
|
||||||
|
shift
|
||||||
|
while true; do
|
||||||
|
"$@" && break || {
|
||||||
|
exit=$?
|
||||||
|
if [[ $n -lt $max ]]; then
|
||||||
|
((n++))
|
||||||
|
echo "Command failed. Attempt $n/$max:"
|
||||||
|
sleep $delay;
|
||||||
|
else
|
||||||
|
echo "The command has failed after $n attempts." >&2
|
||||||
|
return $exit
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
function retry_default {
|
||||||
|
retry 5 1 $@
|
||||||
|
}
|
||||||
|
|
||||||
|
function echored {
|
||||||
|
tput setaf 1
|
||||||
|
tput bold
|
||||||
|
echo $@
|
||||||
|
tput sgr0
|
||||||
|
}
|
||||||
|
|
||||||
|
function echogreen {
|
||||||
|
tput setaf 2
|
||||||
|
tput bold
|
||||||
|
echo $@
|
||||||
|
tput sgr0
|
||||||
|
}
|
||||||
|
|
||||||
|
function get_cert {
|
||||||
|
local HOSTPORT=$1
|
||||||
|
openssl s_client -connect $HOSTPORT \
|
||||||
|
-showcerts 2>/dev/null \
|
||||||
|
| openssl x509 -noout -text
|
||||||
|
}
|
||||||
|
|
||||||
|
function assert_proxy_presents_cert_uri {
|
||||||
|
local HOSTPORT=$1
|
||||||
|
local SERVICENAME=$2
|
||||||
|
|
||||||
|
CERT=$(retry_default get_cert $HOSTPORT)
|
||||||
|
|
||||||
|
echo "WANT SERVICE: $SERVICENAME"
|
||||||
|
echo "GOT CERT:"
|
||||||
|
echo "$CERT"
|
||||||
|
|
||||||
|
echo "$CERT" | grep -Eo "URI:spiffe://([a-zA-Z0-9-]+).consul/ns/default/dc/dc1/svc/$SERVICENAME"
|
||||||
|
}
|
||||||
|
|
||||||
|
function get_envoy_listener_filters {
|
||||||
|
local HOSTPORT=$1
|
||||||
|
run retry_default curl -s -f $HOSTPORT/config_dump
|
||||||
|
[ "$status" -eq 0 ]
|
||||||
|
echo "$output" | jq --raw-output '.configs[2].dynamic_active_listeners[].listener | "\(.name) \( .filter_chains[0].filters | map(.name) | join(","))"'
|
||||||
|
}
|
||||||
|
|
||||||
|
function get_envoy_stats_flush_interval {
|
||||||
|
local HOSTPORT=$1
|
||||||
|
run retry_default curl -s -f $HOSTPORT/config_dump
|
||||||
|
[ "$status" -eq 0 ]
|
||||||
|
#echo "$output" > /workdir/s1_envoy_dump.json
|
||||||
|
echo "$output" | jq --raw-output '.configs[0].bootstrap.stats_flush_interval'
|
||||||
|
}
|
||||||
|
|
||||||
|
function docker_consul {
|
||||||
|
docker run -ti --network container:envoy_consul_1 consul-dev $@
|
||||||
|
}
|
||||||
|
|
||||||
|
function must_match_in_statsd_logs {
|
||||||
|
run cat /workdir/statsd/statsd.log
|
||||||
|
COUNT=$( echo "$output" | grep -Ec $1 )
|
||||||
|
|
||||||
|
echo "COUNT of '$1' matches: $COUNT"
|
||||||
|
|
||||||
|
[ "$status" == 0 ]
|
||||||
|
[ "$COUNT" -gt "0" ]
|
||||||
|
}
|
||||||
|
|
||||||
|
function must_match_in_prometheus_response {
|
||||||
|
run curl -f -s $1/metrics
|
||||||
|
COUNT=$( echo "$output" | grep -Ec $2 )
|
||||||
|
|
||||||
|
echo "COUNT of '$2' matches: $COUNT"
|
||||||
|
|
||||||
|
[ "$status" == 0 ]
|
||||||
|
[ "$COUNT" -gt "0" ]
|
||||||
|
}
|
||||||
|
|
||||||
|
# must_fail_tcp_connection checks that a request made through an upstream fails,
|
||||||
|
# probably due to authz being denied if all other tests passed already. Although
|
||||||
|
# we are using curl, this only works as expected for TCP upstreams as we are
|
||||||
|
# checking TCP-level errors. HTTP upstreams will return a valid 503 generated by
|
||||||
|
# Envoy rather than a connection-level error.
|
||||||
|
function must_fail_tcp_connection {
|
||||||
|
# Attempt to curl through upstream
|
||||||
|
run curl -s -v -f -d hello $1
|
||||||
|
|
||||||
|
echo "OUTPUT $output"
|
||||||
|
|
||||||
|
# Should fail during handshake and return "got nothing" error
|
||||||
|
[ "$status" == "52" ]
|
||||||
|
|
||||||
|
# Verbose output should enclude empty reply
|
||||||
|
echo "$output" | grep 'Empty reply from server'
|
||||||
|
}
|
||||||
|
|
||||||
|
# must_fail_http_connection see must_fail_tcp_connection but this expects Envoy
|
||||||
|
# to generate a 503 response since the upstreams have refused connection.
|
||||||
|
function must_fail_http_connection {
|
||||||
|
# Attempt to curl through upstream
|
||||||
|
run curl -s -i -d hello $1
|
||||||
|
|
||||||
|
echo "OUTPUT $output"
|
||||||
|
|
||||||
|
# Should fail request with 503
|
||||||
|
echo "$output" | grep '503 Service Unavailable'
|
||||||
|
}
|
|
@ -0,0 +1,158 @@
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
# DEBUG=1 enables set -x for this script so echos every command run
|
||||||
|
DEBUG=${DEBUG:-}
|
||||||
|
|
||||||
|
# FILTER_TESTS="<pattern>" skips any test whose CASENAME doesn't match the
|
||||||
|
# pattern. CASENAME is combination of the name from the case-<name> dir and the
|
||||||
|
# envoy version for example: "http, envoy 1.8.0". The pattern is passed to grep
|
||||||
|
# over that string.
|
||||||
|
FILTER_TESTS=${FILTER_TESTS:-}
|
||||||
|
|
||||||
|
# LEAVE_CONSUL_UP=1 leaves the consul container running at the end which can be
|
||||||
|
# useful for debugging.
|
||||||
|
LEAVE_CONSUL_UP=${LEAVE_CONSUL_UP:-}
|
||||||
|
|
||||||
|
# QUIESCE_SECS=1 will cause the runner to sleep for 1 second after setup but
|
||||||
|
# before verify container is run this is useful for CI which seems to pass more
|
||||||
|
# reliably with this even though docker-compose up waits for containers to
|
||||||
|
# start, and our tests retry.
|
||||||
|
QUIESCE_SECS=${QUIESCE_SECS:-}
|
||||||
|
|
||||||
|
# ENVOY_VERSIONS is the list of envoy versions to run each test against
|
||||||
|
ENVOY_VERSIONS=${ENVOY_VERSIONS:-"1.8.0 1.9.1"}
|
||||||
|
|
||||||
|
if [ ! -z "$DEBUG" ] ; then
|
||||||
|
set -x
|
||||||
|
fi
|
||||||
|
|
||||||
|
DIR=$(cd -P -- "$(dirname -- "$0")" && pwd -P)
|
||||||
|
|
||||||
|
cd $DIR
|
||||||
|
|
||||||
|
FILTER_TESTS=${FILTER_TESTS:-}
|
||||||
|
LEAVE_CONSUL_UP=${LEAVE_CONSUL_UP:-}
|
||||||
|
PROXY_LOGS_ON_FAIL=${PROXY_LOGS_ON_FAIL:-}
|
||||||
|
|
||||||
|
mkdir -p workdir/{consul,envoy,bats,statsd,logs}
|
||||||
|
|
||||||
|
source helpers.bash
|
||||||
|
|
||||||
|
RESULT=1
|
||||||
|
CLEANED_UP=0
|
||||||
|
|
||||||
|
function cleanup {
|
||||||
|
if [ "$CLEANED_UP" != 0 ] ; then
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
CLEANED_UP=1
|
||||||
|
|
||||||
|
if [ -z "$LEAVE_CONSUL_UP" ] ; then
|
||||||
|
docker-compose down
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
trap cleanup EXIT
|
||||||
|
|
||||||
|
# Start the volume container
|
||||||
|
docker-compose up -d workdir
|
||||||
|
|
||||||
|
for c in ./case-*/ ; do
|
||||||
|
for ev in $ENVOY_VERSIONS ; do
|
||||||
|
CASENAME="$( basename $c | cut -c6- ), envoy $ev"
|
||||||
|
echo ""
|
||||||
|
echo "==> CASE $CASENAME"
|
||||||
|
|
||||||
|
export ENVOY_VERSION=$ev
|
||||||
|
|
||||||
|
if [ ! -z "$FILTER_TESTS" ] && echo "$CASENAME" | grep -v "$FILTER_TESTS" > /dev/null ; then
|
||||||
|
echo " SKIPPED: doesn't match FILTER_TESTS=$FILTER_TESTS"
|
||||||
|
continue 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Wipe state
|
||||||
|
docker-compose up wipe-volumes
|
||||||
|
|
||||||
|
# Reload consul config from defaults
|
||||||
|
cp consul-base-cfg/* workdir/consul
|
||||||
|
|
||||||
|
# Add any overrides if there are any (no op if not)
|
||||||
|
cp -f ${c}*.hcl workdir/consul 2>/dev/null || :
|
||||||
|
|
||||||
|
# Push the state to the shared docker volume (note this is because CircleCI
|
||||||
|
# can't use shared volumes)
|
||||||
|
docker cp workdir/. envoy_workdir_1:/workdir
|
||||||
|
|
||||||
|
# Start Consul first we do this here even though typically nothing stopped
|
||||||
|
# it because it sometimes seems to be killed by something else (OOM killer)?
|
||||||
|
docker-compose up -d consul
|
||||||
|
|
||||||
|
# Reload consul
|
||||||
|
echo "Reloading Consul config"
|
||||||
|
if ! retry 10 2 docker_consul reload ; then
|
||||||
|
# Clean up everything before we abort
|
||||||
|
#docker-compose down
|
||||||
|
echored "⨯ FAIL - couldn't reload consul config"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Copy all the test files
|
||||||
|
cp ${c}*.bats workdir/bats
|
||||||
|
cp helpers.bash workdir/bats
|
||||||
|
|
||||||
|
# Run test case setup (e.g. generating Envoy bootstrap, starting containers)
|
||||||
|
source ${c}setup.sh
|
||||||
|
|
||||||
|
# Push the state to the shared docker volume (note this is because CircleCI
|
||||||
|
# can't use shared volumes)
|
||||||
|
docker cp workdir/. envoy_workdir_1:/workdir
|
||||||
|
|
||||||
|
# Start containers required
|
||||||
|
if [ ! -z "$REQUIRED_SERVICES" ] ; then
|
||||||
|
docker-compose up -d $REQUIRED_SERVICES
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ ! -z "$QUIESCE_SECS" ] ; then
|
||||||
|
echo "Sleeping for $QUIESCE_SECS seconds"
|
||||||
|
sleep $QUIESCE_SECS
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Execute tests
|
||||||
|
THISRESULT=1
|
||||||
|
if docker-compose up --build --abort-on-container-exit --exit-code-from verify verify ; then
|
||||||
|
echo -n "==> CASE $CASENAME: "
|
||||||
|
echogreen "✓ PASS"
|
||||||
|
else
|
||||||
|
echo -n "==> CASE $CASENAME: "
|
||||||
|
echored "⨯ FAIL"
|
||||||
|
if [ $RESULT -eq 1 ] ; then
|
||||||
|
RESULT=0
|
||||||
|
fi
|
||||||
|
THISRESULT=0
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Teardown
|
||||||
|
if [ -f "${c}teardown.sh" ] ; then
|
||||||
|
source "${c}teardown.sh"
|
||||||
|
fi
|
||||||
|
if [ ! -z "$REQUIRED_SERVICES" ] ; then
|
||||||
|
if [[ "$THISRESULT" == 0 ]] ; then
|
||||||
|
mkdir -p workdir/logs/$c/$ENVOY_VERSION
|
||||||
|
for cont in $REQUIRED_SERVICES; do
|
||||||
|
docker-compose logs --no-color $cont 2>&1 > workdir/logs/$c/$ENVOY_VERSION/$cont.log
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
docker-compose stop $REQUIRED_SERVICES
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
done
|
||||||
|
|
||||||
|
cleanup
|
||||||
|
|
||||||
|
if [ $RESULT -eq 1 ] ; then
|
||||||
|
echogreen "✓ PASS"
|
||||||
|
else
|
||||||
|
echored "⨯ FAIL"
|
||||||
|
exit 1
|
||||||
|
fi
|
|
@ -1,4 +1,6 @@
|
||||||
wmi
|
wmi
|
||||||
===
|
===
|
||||||
|
|
||||||
Package wmi provides a WQL interface for WMI on Windows.
|
Package wmi provides a WQL interface to Windows WMI.
|
||||||
|
|
||||||
|
Note: It interfaces with WMI on the local machine, therefore it only runs on Windows.
|
||||||
|
|
|
@ -0,0 +1,260 @@
|
||||||
|
// +build windows
|
||||||
|
|
||||||
|
package wmi
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"reflect"
|
||||||
|
"runtime"
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"github.com/go-ole/go-ole"
|
||||||
|
"github.com/go-ole/go-ole/oleutil"
|
||||||
|
)
|
||||||
|
|
||||||
|
// SWbemServices is used to access wmi. See https://msdn.microsoft.com/en-us/library/aa393719(v=vs.85).aspx
|
||||||
|
type SWbemServices struct {
|
||||||
|
//TODO: track namespace. Not sure if we can re connect to a different namespace using the same instance
|
||||||
|
cWMIClient *Client //This could also be an embedded struct, but then we would need to branch on Client vs SWbemServices in the Query method
|
||||||
|
sWbemLocatorIUnknown *ole.IUnknown
|
||||||
|
sWbemLocatorIDispatch *ole.IDispatch
|
||||||
|
queries chan *queryRequest
|
||||||
|
closeError chan error
|
||||||
|
lQueryorClose sync.Mutex
|
||||||
|
}
|
||||||
|
|
||||||
|
type queryRequest struct {
|
||||||
|
query string
|
||||||
|
dst interface{}
|
||||||
|
args []interface{}
|
||||||
|
finished chan error
|
||||||
|
}
|
||||||
|
|
||||||
|
// InitializeSWbemServices will return a new SWbemServices object that can be used to query WMI
|
||||||
|
func InitializeSWbemServices(c *Client, connectServerArgs ...interface{}) (*SWbemServices, error) {
|
||||||
|
//fmt.Println("InitializeSWbemServices: Starting")
|
||||||
|
//TODO: implement connectServerArgs as optional argument for init with connectServer call
|
||||||
|
s := new(SWbemServices)
|
||||||
|
s.cWMIClient = c
|
||||||
|
s.queries = make(chan *queryRequest)
|
||||||
|
initError := make(chan error)
|
||||||
|
go s.process(initError)
|
||||||
|
|
||||||
|
err, ok := <-initError
|
||||||
|
if ok {
|
||||||
|
return nil, err //Send error to caller
|
||||||
|
}
|
||||||
|
//fmt.Println("InitializeSWbemServices: Finished")
|
||||||
|
return s, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close will clear and release all of the SWbemServices resources
|
||||||
|
func (s *SWbemServices) Close() error {
|
||||||
|
s.lQueryorClose.Lock()
|
||||||
|
if s == nil || s.sWbemLocatorIDispatch == nil {
|
||||||
|
s.lQueryorClose.Unlock()
|
||||||
|
return fmt.Errorf("SWbemServices is not Initialized")
|
||||||
|
}
|
||||||
|
if s.queries == nil {
|
||||||
|
s.lQueryorClose.Unlock()
|
||||||
|
return fmt.Errorf("SWbemServices has been closed")
|
||||||
|
}
|
||||||
|
//fmt.Println("Close: sending close request")
|
||||||
|
var result error
|
||||||
|
ce := make(chan error)
|
||||||
|
s.closeError = ce //Race condition if multiple callers to close. May need to lock here
|
||||||
|
close(s.queries) //Tell background to shut things down
|
||||||
|
s.lQueryorClose.Unlock()
|
||||||
|
err, ok := <-ce
|
||||||
|
if ok {
|
||||||
|
result = err
|
||||||
|
}
|
||||||
|
//fmt.Println("Close: finished")
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *SWbemServices) process(initError chan error) {
|
||||||
|
//fmt.Println("process: starting background thread initialization")
|
||||||
|
//All OLE/WMI calls must happen on the same initialized thead, so lock this goroutine
|
||||||
|
runtime.LockOSThread()
|
||||||
|
defer runtime.LockOSThread()
|
||||||
|
|
||||||
|
err := ole.CoInitializeEx(0, ole.COINIT_MULTITHREADED)
|
||||||
|
if err != nil {
|
||||||
|
oleCode := err.(*ole.OleError).Code()
|
||||||
|
if oleCode != ole.S_OK && oleCode != S_FALSE {
|
||||||
|
initError <- fmt.Errorf("ole.CoInitializeEx error: %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
defer ole.CoUninitialize()
|
||||||
|
|
||||||
|
unknown, err := oleutil.CreateObject("WbemScripting.SWbemLocator")
|
||||||
|
if err != nil {
|
||||||
|
initError <- fmt.Errorf("CreateObject SWbemLocator error: %v", err)
|
||||||
|
return
|
||||||
|
} else if unknown == nil {
|
||||||
|
initError <- ErrNilCreateObject
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer unknown.Release()
|
||||||
|
s.sWbemLocatorIUnknown = unknown
|
||||||
|
|
||||||
|
dispatch, err := s.sWbemLocatorIUnknown.QueryInterface(ole.IID_IDispatch)
|
||||||
|
if err != nil {
|
||||||
|
initError <- fmt.Errorf("SWbemLocator QueryInterface error: %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer dispatch.Release()
|
||||||
|
s.sWbemLocatorIDispatch = dispatch
|
||||||
|
|
||||||
|
// we can't do the ConnectServer call outside the loop unless we find a way to track and re-init the connectServerArgs
|
||||||
|
//fmt.Println("process: initialized. closing initError")
|
||||||
|
close(initError)
|
||||||
|
//fmt.Println("process: waiting for queries")
|
||||||
|
for q := range s.queries {
|
||||||
|
//fmt.Printf("process: new query: len(query)=%d\n", len(q.query))
|
||||||
|
errQuery := s.queryBackground(q)
|
||||||
|
//fmt.Println("process: s.queryBackground finished")
|
||||||
|
if errQuery != nil {
|
||||||
|
q.finished <- errQuery
|
||||||
|
}
|
||||||
|
close(q.finished)
|
||||||
|
}
|
||||||
|
//fmt.Println("process: queries channel closed")
|
||||||
|
s.queries = nil //set channel to nil so we know it is closed
|
||||||
|
//TODO: I think the Release/Clear calls can panic if things are in a bad state.
|
||||||
|
//TODO: May need to recover from panics and send error to method caller instead.
|
||||||
|
close(s.closeError)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Query runs the WQL query using a SWbemServices instance and appends the values to dst.
|
||||||
|
//
|
||||||
|
// dst must have type *[]S or *[]*S, for some struct type S. Fields selected in
|
||||||
|
// the query must have the same name in dst. Supported types are all signed and
|
||||||
|
// unsigned integers, time.Time, string, bool, or a pointer to one of those.
|
||||||
|
// Array types are not supported.
|
||||||
|
//
|
||||||
|
// By default, the local machine and default namespace are used. These can be
|
||||||
|
// changed using connectServerArgs. See
|
||||||
|
// http://msdn.microsoft.com/en-us/library/aa393720.aspx for details.
|
||||||
|
func (s *SWbemServices) Query(query string, dst interface{}, connectServerArgs ...interface{}) error {
|
||||||
|
s.lQueryorClose.Lock()
|
||||||
|
if s == nil || s.sWbemLocatorIDispatch == nil {
|
||||||
|
s.lQueryorClose.Unlock()
|
||||||
|
return fmt.Errorf("SWbemServices is not Initialized")
|
||||||
|
}
|
||||||
|
if s.queries == nil {
|
||||||
|
s.lQueryorClose.Unlock()
|
||||||
|
return fmt.Errorf("SWbemServices has been closed")
|
||||||
|
}
|
||||||
|
|
||||||
|
//fmt.Println("Query: Sending query request")
|
||||||
|
qr := queryRequest{
|
||||||
|
query: query,
|
||||||
|
dst: dst,
|
||||||
|
args: connectServerArgs,
|
||||||
|
finished: make(chan error),
|
||||||
|
}
|
||||||
|
s.queries <- &qr
|
||||||
|
s.lQueryorClose.Unlock()
|
||||||
|
err, ok := <-qr.finished
|
||||||
|
if ok {
|
||||||
|
//fmt.Println("Query: Finished with error")
|
||||||
|
return err //Send error to caller
|
||||||
|
}
|
||||||
|
//fmt.Println("Query: Finished")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *SWbemServices) queryBackground(q *queryRequest) error {
|
||||||
|
if s == nil || s.sWbemLocatorIDispatch == nil {
|
||||||
|
return fmt.Errorf("SWbemServices is not Initialized")
|
||||||
|
}
|
||||||
|
wmi := s.sWbemLocatorIDispatch //Should just rename in the code, but this will help as we break things apart
|
||||||
|
//fmt.Println("queryBackground: Starting")
|
||||||
|
|
||||||
|
dv := reflect.ValueOf(q.dst)
|
||||||
|
if dv.Kind() != reflect.Ptr || dv.IsNil() {
|
||||||
|
return ErrInvalidEntityType
|
||||||
|
}
|
||||||
|
dv = dv.Elem()
|
||||||
|
mat, elemType := checkMultiArg(dv)
|
||||||
|
if mat == multiArgTypeInvalid {
|
||||||
|
return ErrInvalidEntityType
|
||||||
|
}
|
||||||
|
|
||||||
|
// service is a SWbemServices
|
||||||
|
serviceRaw, err := oleutil.CallMethod(wmi, "ConnectServer", q.args...)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
service := serviceRaw.ToIDispatch()
|
||||||
|
defer serviceRaw.Clear()
|
||||||
|
|
||||||
|
// result is a SWBemObjectSet
|
||||||
|
resultRaw, err := oleutil.CallMethod(service, "ExecQuery", q.query)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
result := resultRaw.ToIDispatch()
|
||||||
|
defer resultRaw.Clear()
|
||||||
|
|
||||||
|
count, err := oleInt64(result, "Count")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
enumProperty, err := result.GetProperty("_NewEnum")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer enumProperty.Clear()
|
||||||
|
|
||||||
|
enum, err := enumProperty.ToIUnknown().IEnumVARIANT(ole.IID_IEnumVariant)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if enum == nil {
|
||||||
|
return fmt.Errorf("can't get IEnumVARIANT, enum is nil")
|
||||||
|
}
|
||||||
|
defer enum.Release()
|
||||||
|
|
||||||
|
// Initialize a slice with Count capacity
|
||||||
|
dv.Set(reflect.MakeSlice(dv.Type(), 0, int(count)))
|
||||||
|
|
||||||
|
var errFieldMismatch error
|
||||||
|
for itemRaw, length, err := enum.Next(1); length > 0; itemRaw, length, err = enum.Next(1) {
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err := func() error {
|
||||||
|
// item is a SWbemObject, but really a Win32_Process
|
||||||
|
item := itemRaw.ToIDispatch()
|
||||||
|
defer item.Release()
|
||||||
|
|
||||||
|
ev := reflect.New(elemType)
|
||||||
|
if err = s.cWMIClient.loadEntity(ev.Interface(), item); err != nil {
|
||||||
|
if _, ok := err.(*ErrFieldMismatch); ok {
|
||||||
|
// We continue loading entities even in the face of field mismatch errors.
|
||||||
|
// If we encounter any other error, that other error is returned. Otherwise,
|
||||||
|
// an ErrFieldMismatch is returned.
|
||||||
|
errFieldMismatch = err
|
||||||
|
} else {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if mat != multiArgTypeStructPtr {
|
||||||
|
ev = ev.Elem()
|
||||||
|
}
|
||||||
|
dv.Set(reflect.Append(dv, ev))
|
||||||
|
return nil
|
||||||
|
}()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//fmt.Println("queryBackground: Finished")
|
||||||
|
return errFieldMismatch
|
||||||
|
}
|
|
@ -72,7 +72,10 @@ func QueryNamespace(query string, dst interface{}, namespace string) error {
|
||||||
//
|
//
|
||||||
// Query is a wrapper around DefaultClient.Query.
|
// Query is a wrapper around DefaultClient.Query.
|
||||||
func Query(query string, dst interface{}, connectServerArgs ...interface{}) error {
|
func Query(query string, dst interface{}, connectServerArgs ...interface{}) error {
|
||||||
return DefaultClient.Query(query, dst, connectServerArgs...)
|
if DefaultClient.SWbemServicesClient == nil {
|
||||||
|
return DefaultClient.Query(query, dst, connectServerArgs...)
|
||||||
|
}
|
||||||
|
return DefaultClient.SWbemServicesClient.Query(query, dst, connectServerArgs...)
|
||||||
}
|
}
|
||||||
|
|
||||||
// A Client is an WMI query client.
|
// A Client is an WMI query client.
|
||||||
|
@ -99,6 +102,11 @@ type Client struct {
|
||||||
// Setting this to true allows custom queries to be used with full
|
// Setting this to true allows custom queries to be used with full
|
||||||
// struct definitions instead of having to define multiple structs.
|
// struct definitions instead of having to define multiple structs.
|
||||||
AllowMissingFields bool
|
AllowMissingFields bool
|
||||||
|
|
||||||
|
// SWbemServiceClient is an optional SWbemServices object that can be
|
||||||
|
// initialized and then reused across multiple queries. If it is null
|
||||||
|
// then the method will initialize a new temporary client each time.
|
||||||
|
SWbemServicesClient *SWbemServices
|
||||||
}
|
}
|
||||||
|
|
||||||
// DefaultClient is the default Client and is used by Query, QueryNamespace
|
// DefaultClient is the default Client and is used by Query, QueryNamespace
|
||||||
|
@ -362,17 +370,50 @@ func (c *Client) loadEntity(dst interface{}, src *ole.IDispatch) (errFieldMismat
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
typeof := reflect.TypeOf(val)
|
if f.Kind() == reflect.Slice {
|
||||||
if typeof == nil && (isPtr || c.NonePtrZero) {
|
switch f.Type().Elem().Kind() {
|
||||||
if (isPtr && c.PtrNil) || (!isPtr && c.NonePtrZero) {
|
case reflect.String:
|
||||||
of.Set(reflect.Zero(of.Type()))
|
safeArray := prop.ToArray()
|
||||||
|
if safeArray != nil {
|
||||||
|
arr := safeArray.ToValueArray()
|
||||||
|
fArr := reflect.MakeSlice(f.Type(), len(arr), len(arr))
|
||||||
|
for i, v := range arr {
|
||||||
|
s := fArr.Index(i)
|
||||||
|
s.SetString(v.(string))
|
||||||
|
}
|
||||||
|
f.Set(fArr)
|
||||||
|
}
|
||||||
|
case reflect.Uint8:
|
||||||
|
safeArray := prop.ToArray()
|
||||||
|
if safeArray != nil {
|
||||||
|
arr := safeArray.ToValueArray()
|
||||||
|
fArr := reflect.MakeSlice(f.Type(), len(arr), len(arr))
|
||||||
|
for i, v := range arr {
|
||||||
|
s := fArr.Index(i)
|
||||||
|
s.SetUint(reflect.ValueOf(v).Uint())
|
||||||
|
}
|
||||||
|
f.Set(fArr)
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return &ErrFieldMismatch{
|
||||||
|
StructType: of.Type(),
|
||||||
|
FieldName: n,
|
||||||
|
Reason: fmt.Sprintf("unsupported slice type (%T)", val),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
typeof := reflect.TypeOf(val)
|
||||||
|
if typeof == nil && (isPtr || c.NonePtrZero) {
|
||||||
|
if (isPtr && c.PtrNil) || (!isPtr && c.NonePtrZero) {
|
||||||
|
of.Set(reflect.Zero(of.Type()))
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
return &ErrFieldMismatch{
|
||||||
|
StructType: of.Type(),
|
||||||
|
FieldName: n,
|
||||||
|
Reason: fmt.Sprintf("unsupported type (%T)", val),
|
||||||
}
|
}
|
||||||
break
|
|
||||||
}
|
|
||||||
return &ErrFieldMismatch{
|
|
||||||
StructType: of.Type(),
|
|
||||||
FieldName: n,
|
|
||||||
Reason: fmt.Sprintf("unsupported type (%T)", val),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
3598
vendor/github.com/envoyproxy/go-control-plane/envoy/config/filter/network/http_connection_manager/v2/http_connection_manager.pb.go
generated
vendored
Normal file
3598
vendor/github.com/envoyproxy/go-control-plane/envoy/config/filter/network/http_connection_manager/v2/http_connection_manager.pb.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
660
vendor/github.com/envoyproxy/go-control-plane/envoy/config/filter/network/http_connection_manager/v2/http_connection_manager.pb.validate.go
generated
vendored
Normal file
660
vendor/github.com/envoyproxy/go-control-plane/envoy/config/filter/network/http_connection_manager/v2/http_connection_manager.pb.validate.go
generated
vendored
Normal file
|
@ -0,0 +1,660 @@
|
||||||
|
// Code generated by protoc-gen-validate
|
||||||
|
// source: envoy/config/filter/network/http_connection_manager/v2/http_connection_manager.proto
|
||||||
|
// DO NOT EDIT!!!
|
||||||
|
|
||||||
|
package v2
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"net"
|
||||||
|
"net/mail"
|
||||||
|
"net/url"
|
||||||
|
"regexp"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
"unicode/utf8"
|
||||||
|
|
||||||
|
"github.com/gogo/protobuf/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ensure the imports are used
|
||||||
|
var (
|
||||||
|
_ = bytes.MinRead
|
||||||
|
_ = errors.New("")
|
||||||
|
_ = fmt.Print
|
||||||
|
_ = utf8.UTFMax
|
||||||
|
_ = (*regexp.Regexp)(nil)
|
||||||
|
_ = (*strings.Reader)(nil)
|
||||||
|
_ = net.IPv4len
|
||||||
|
_ = time.Duration(0)
|
||||||
|
_ = (*url.URL)(nil)
|
||||||
|
_ = (*mail.Address)(nil)
|
||||||
|
_ = types.DynamicAny{}
|
||||||
|
)
|
||||||
|
|
||||||
|
// Validate checks the field values on HttpConnectionManager with the rules
|
||||||
|
// defined in the proto definition for this message. If any rules are
|
||||||
|
// violated, an error is returned.
|
||||||
|
func (m *HttpConnectionManager) Validate() error {
|
||||||
|
if m == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, ok := HttpConnectionManager_CodecType_name[int32(m.GetCodecType())]; !ok {
|
||||||
|
return HttpConnectionManagerValidationError{
|
||||||
|
Field: "CodecType",
|
||||||
|
Reason: "value must be one of the defined enum values",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(m.GetStatPrefix()) < 1 {
|
||||||
|
return HttpConnectionManagerValidationError{
|
||||||
|
Field: "StatPrefix",
|
||||||
|
Reason: "value length must be at least 1 bytes",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for idx, item := range m.GetHttpFilters() {
|
||||||
|
_, _ = idx, item
|
||||||
|
|
||||||
|
if v, ok := interface{}(item).(interface{ Validate() error }); ok {
|
||||||
|
if err := v.Validate(); err != nil {
|
||||||
|
return HttpConnectionManagerValidationError{
|
||||||
|
Field: fmt.Sprintf("HttpFilters[%v]", idx),
|
||||||
|
Reason: "embedded message failed validation",
|
||||||
|
Cause: err,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if v, ok := interface{}(m.GetAddUserAgent()).(interface{ Validate() error }); ok {
|
||||||
|
if err := v.Validate(); err != nil {
|
||||||
|
return HttpConnectionManagerValidationError{
|
||||||
|
Field: "AddUserAgent",
|
||||||
|
Reason: "embedded message failed validation",
|
||||||
|
Cause: err,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if v, ok := interface{}(m.GetTracing()).(interface{ Validate() error }); ok {
|
||||||
|
if err := v.Validate(); err != nil {
|
||||||
|
return HttpConnectionManagerValidationError{
|
||||||
|
Field: "Tracing",
|
||||||
|
Reason: "embedded message failed validation",
|
||||||
|
Cause: err,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if v, ok := interface{}(m.GetHttpProtocolOptions()).(interface{ Validate() error }); ok {
|
||||||
|
if err := v.Validate(); err != nil {
|
||||||
|
return HttpConnectionManagerValidationError{
|
||||||
|
Field: "HttpProtocolOptions",
|
||||||
|
Reason: "embedded message failed validation",
|
||||||
|
Cause: err,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if v, ok := interface{}(m.GetHttp2ProtocolOptions()).(interface{ Validate() error }); ok {
|
||||||
|
if err := v.Validate(); err != nil {
|
||||||
|
return HttpConnectionManagerValidationError{
|
||||||
|
Field: "Http2ProtocolOptions",
|
||||||
|
Reason: "embedded message failed validation",
|
||||||
|
Cause: err,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// no validation rules for ServerName
|
||||||
|
|
||||||
|
if v, ok := interface{}(m.GetIdleTimeout()).(interface{ Validate() error }); ok {
|
||||||
|
if err := v.Validate(); err != nil {
|
||||||
|
return HttpConnectionManagerValidationError{
|
||||||
|
Field: "IdleTimeout",
|
||||||
|
Reason: "embedded message failed validation",
|
||||||
|
Cause: err,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if v, ok := interface{}(m.GetStreamIdleTimeout()).(interface{ Validate() error }); ok {
|
||||||
|
if err := v.Validate(); err != nil {
|
||||||
|
return HttpConnectionManagerValidationError{
|
||||||
|
Field: "StreamIdleTimeout",
|
||||||
|
Reason: "embedded message failed validation",
|
||||||
|
Cause: err,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if v, ok := interface{}(m.GetDrainTimeout()).(interface{ Validate() error }); ok {
|
||||||
|
if err := v.Validate(); err != nil {
|
||||||
|
return HttpConnectionManagerValidationError{
|
||||||
|
Field: "DrainTimeout",
|
||||||
|
Reason: "embedded message failed validation",
|
||||||
|
Cause: err,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for idx, item := range m.GetAccessLog() {
|
||||||
|
_, _ = idx, item
|
||||||
|
|
||||||
|
if v, ok := interface{}(item).(interface{ Validate() error }); ok {
|
||||||
|
if err := v.Validate(); err != nil {
|
||||||
|
return HttpConnectionManagerValidationError{
|
||||||
|
Field: fmt.Sprintf("AccessLog[%v]", idx),
|
||||||
|
Reason: "embedded message failed validation",
|
||||||
|
Cause: err,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if v, ok := interface{}(m.GetUseRemoteAddress()).(interface{ Validate() error }); ok {
|
||||||
|
if err := v.Validate(); err != nil {
|
||||||
|
return HttpConnectionManagerValidationError{
|
||||||
|
Field: "UseRemoteAddress",
|
||||||
|
Reason: "embedded message failed validation",
|
||||||
|
Cause: err,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// no validation rules for XffNumTrustedHops
|
||||||
|
|
||||||
|
// no validation rules for SkipXffAppend
|
||||||
|
|
||||||
|
// no validation rules for Via
|
||||||
|
|
||||||
|
if v, ok := interface{}(m.GetGenerateRequestId()).(interface{ Validate() error }); ok {
|
||||||
|
if err := v.Validate(); err != nil {
|
||||||
|
return HttpConnectionManagerValidationError{
|
||||||
|
Field: "GenerateRequestId",
|
||||||
|
Reason: "embedded message failed validation",
|
||||||
|
Cause: err,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, ok := HttpConnectionManager_ForwardClientCertDetails_name[int32(m.GetForwardClientCertDetails())]; !ok {
|
||||||
|
return HttpConnectionManagerValidationError{
|
||||||
|
Field: "ForwardClientCertDetails",
|
||||||
|
Reason: "value must be one of the defined enum values",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if v, ok := interface{}(m.GetSetCurrentClientCertDetails()).(interface{ Validate() error }); ok {
|
||||||
|
if err := v.Validate(); err != nil {
|
||||||
|
return HttpConnectionManagerValidationError{
|
||||||
|
Field: "SetCurrentClientCertDetails",
|
||||||
|
Reason: "embedded message failed validation",
|
||||||
|
Cause: err,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// no validation rules for Proxy_100Continue
|
||||||
|
|
||||||
|
// no validation rules for RepresentIpv4RemoteAddressAsIpv4MappedIpv6
|
||||||
|
|
||||||
|
for idx, item := range m.GetUpgradeConfigs() {
|
||||||
|
_, _ = idx, item
|
||||||
|
|
||||||
|
if v, ok := interface{}(item).(interface{ Validate() error }); ok {
|
||||||
|
if err := v.Validate(); err != nil {
|
||||||
|
return HttpConnectionManagerValidationError{
|
||||||
|
Field: fmt.Sprintf("UpgradeConfigs[%v]", idx),
|
||||||
|
Reason: "embedded message failed validation",
|
||||||
|
Cause: err,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
switch m.RouteSpecifier.(type) {
|
||||||
|
|
||||||
|
case *HttpConnectionManager_Rds:
|
||||||
|
|
||||||
|
if v, ok := interface{}(m.GetRds()).(interface{ Validate() error }); ok {
|
||||||
|
if err := v.Validate(); err != nil {
|
||||||
|
return HttpConnectionManagerValidationError{
|
||||||
|
Field: "Rds",
|
||||||
|
Reason: "embedded message failed validation",
|
||||||
|
Cause: err,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
case *HttpConnectionManager_RouteConfig:
|
||||||
|
|
||||||
|
if v, ok := interface{}(m.GetRouteConfig()).(interface{ Validate() error }); ok {
|
||||||
|
if err := v.Validate(); err != nil {
|
||||||
|
return HttpConnectionManagerValidationError{
|
||||||
|
Field: "RouteConfig",
|
||||||
|
Reason: "embedded message failed validation",
|
||||||
|
Cause: err,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
return HttpConnectionManagerValidationError{
|
||||||
|
Field: "RouteSpecifier",
|
||||||
|
Reason: "value is required",
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// HttpConnectionManagerValidationError is the validation error returned by
|
||||||
|
// HttpConnectionManager.Validate if the designated constraints aren't met.
|
||||||
|
type HttpConnectionManagerValidationError struct {
|
||||||
|
Field string
|
||||||
|
Reason string
|
||||||
|
Cause error
|
||||||
|
Key bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// Error satisfies the builtin error interface
|
||||||
|
func (e HttpConnectionManagerValidationError) Error() string {
|
||||||
|
cause := ""
|
||||||
|
if e.Cause != nil {
|
||||||
|
cause = fmt.Sprintf(" | caused by: %v", e.Cause)
|
||||||
|
}
|
||||||
|
|
||||||
|
key := ""
|
||||||
|
if e.Key {
|
||||||
|
key = "key for "
|
||||||
|
}
|
||||||
|
|
||||||
|
return fmt.Sprintf(
|
||||||
|
"invalid %sHttpConnectionManager.%s: %s%s",
|
||||||
|
key,
|
||||||
|
e.Field,
|
||||||
|
e.Reason,
|
||||||
|
cause)
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ error = HttpConnectionManagerValidationError{}
|
||||||
|
|
||||||
|
// Validate checks the field values on Rds with the rules defined in the proto
|
||||||
|
// definition for this message. If any rules are violated, an error is returned.
|
||||||
|
func (m *Rds) Validate() error {
|
||||||
|
if m == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if v, ok := interface{}(m.GetConfigSource()).(interface{ Validate() error }); ok {
|
||||||
|
if err := v.Validate(); err != nil {
|
||||||
|
return RdsValidationError{
|
||||||
|
Field: "ConfigSource",
|
||||||
|
Reason: "embedded message failed validation",
|
||||||
|
Cause: err,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(m.GetRouteConfigName()) < 1 {
|
||||||
|
return RdsValidationError{
|
||||||
|
Field: "RouteConfigName",
|
||||||
|
Reason: "value length must be at least 1 bytes",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// RdsValidationError is the validation error returned by Rds.Validate if the
|
||||||
|
// designated constraints aren't met.
|
||||||
|
type RdsValidationError struct {
|
||||||
|
Field string
|
||||||
|
Reason string
|
||||||
|
Cause error
|
||||||
|
Key bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// Error satisfies the builtin error interface
|
||||||
|
func (e RdsValidationError) Error() string {
|
||||||
|
cause := ""
|
||||||
|
if e.Cause != nil {
|
||||||
|
cause = fmt.Sprintf(" | caused by: %v", e.Cause)
|
||||||
|
}
|
||||||
|
|
||||||
|
key := ""
|
||||||
|
if e.Key {
|
||||||
|
key = "key for "
|
||||||
|
}
|
||||||
|
|
||||||
|
return fmt.Sprintf(
|
||||||
|
"invalid %sRds.%s: %s%s",
|
||||||
|
key,
|
||||||
|
e.Field,
|
||||||
|
e.Reason,
|
||||||
|
cause)
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ error = RdsValidationError{}
|
||||||
|
|
||||||
|
// Validate checks the field values on HttpFilter with the rules defined in the
|
||||||
|
// proto definition for this message. If any rules are violated, an error is returned.
|
||||||
|
func (m *HttpFilter) Validate() error {
|
||||||
|
if m == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(m.GetName()) < 1 {
|
||||||
|
return HttpFilterValidationError{
|
||||||
|
Field: "Name",
|
||||||
|
Reason: "value length must be at least 1 bytes",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if v, ok := interface{}(m.GetConfig()).(interface{ Validate() error }); ok {
|
||||||
|
if err := v.Validate(); err != nil {
|
||||||
|
return HttpFilterValidationError{
|
||||||
|
Field: "Config",
|
||||||
|
Reason: "embedded message failed validation",
|
||||||
|
Cause: err,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if v, ok := interface{}(m.GetDeprecatedV1()).(interface{ Validate() error }); ok {
|
||||||
|
if err := v.Validate(); err != nil {
|
||||||
|
return HttpFilterValidationError{
|
||||||
|
Field: "DeprecatedV1",
|
||||||
|
Reason: "embedded message failed validation",
|
||||||
|
Cause: err,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// HttpFilterValidationError is the validation error returned by
|
||||||
|
// HttpFilter.Validate if the designated constraints aren't met.
|
||||||
|
type HttpFilterValidationError struct {
|
||||||
|
Field string
|
||||||
|
Reason string
|
||||||
|
Cause error
|
||||||
|
Key bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// Error satisfies the builtin error interface
|
||||||
|
func (e HttpFilterValidationError) Error() string {
|
||||||
|
cause := ""
|
||||||
|
if e.Cause != nil {
|
||||||
|
cause = fmt.Sprintf(" | caused by: %v", e.Cause)
|
||||||
|
}
|
||||||
|
|
||||||
|
key := ""
|
||||||
|
if e.Key {
|
||||||
|
key = "key for "
|
||||||
|
}
|
||||||
|
|
||||||
|
return fmt.Sprintf(
|
||||||
|
"invalid %sHttpFilter.%s: %s%s",
|
||||||
|
key,
|
||||||
|
e.Field,
|
||||||
|
e.Reason,
|
||||||
|
cause)
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ error = HttpFilterValidationError{}
|
||||||
|
|
||||||
|
// Validate checks the field values on HttpConnectionManager_Tracing with the
|
||||||
|
// rules defined in the proto definition for this message. If any rules are
|
||||||
|
// violated, an error is returned.
|
||||||
|
func (m *HttpConnectionManager_Tracing) Validate() error {
|
||||||
|
if m == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, ok := HttpConnectionManager_Tracing_OperationName_name[int32(m.GetOperationName())]; !ok {
|
||||||
|
return HttpConnectionManager_TracingValidationError{
|
||||||
|
Field: "OperationName",
|
||||||
|
Reason: "value must be one of the defined enum values",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if v, ok := interface{}(m.GetClientSampling()).(interface{ Validate() error }); ok {
|
||||||
|
if err := v.Validate(); err != nil {
|
||||||
|
return HttpConnectionManager_TracingValidationError{
|
||||||
|
Field: "ClientSampling",
|
||||||
|
Reason: "embedded message failed validation",
|
||||||
|
Cause: err,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if v, ok := interface{}(m.GetRandomSampling()).(interface{ Validate() error }); ok {
|
||||||
|
if err := v.Validate(); err != nil {
|
||||||
|
return HttpConnectionManager_TracingValidationError{
|
||||||
|
Field: "RandomSampling",
|
||||||
|
Reason: "embedded message failed validation",
|
||||||
|
Cause: err,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if v, ok := interface{}(m.GetOverallSampling()).(interface{ Validate() error }); ok {
|
||||||
|
if err := v.Validate(); err != nil {
|
||||||
|
return HttpConnectionManager_TracingValidationError{
|
||||||
|
Field: "OverallSampling",
|
||||||
|
Reason: "embedded message failed validation",
|
||||||
|
Cause: err,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// HttpConnectionManager_TracingValidationError is the validation error
|
||||||
|
// returned by HttpConnectionManager_Tracing.Validate if the designated
|
||||||
|
// constraints aren't met.
|
||||||
|
type HttpConnectionManager_TracingValidationError struct {
|
||||||
|
Field string
|
||||||
|
Reason string
|
||||||
|
Cause error
|
||||||
|
Key bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// Error satisfies the builtin error interface
|
||||||
|
func (e HttpConnectionManager_TracingValidationError) Error() string {
|
||||||
|
cause := ""
|
||||||
|
if e.Cause != nil {
|
||||||
|
cause = fmt.Sprintf(" | caused by: %v", e.Cause)
|
||||||
|
}
|
||||||
|
|
||||||
|
key := ""
|
||||||
|
if e.Key {
|
||||||
|
key = "key for "
|
||||||
|
}
|
||||||
|
|
||||||
|
return fmt.Sprintf(
|
||||||
|
"invalid %sHttpConnectionManager_Tracing.%s: %s%s",
|
||||||
|
key,
|
||||||
|
e.Field,
|
||||||
|
e.Reason,
|
||||||
|
cause)
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ error = HttpConnectionManager_TracingValidationError{}
|
||||||
|
|
||||||
|
// Validate checks the field values on
|
||||||
|
// HttpConnectionManager_SetCurrentClientCertDetails with the rules defined in
|
||||||
|
// the proto definition for this message. If any rules are violated, an error
|
||||||
|
// is returned.
|
||||||
|
func (m *HttpConnectionManager_SetCurrentClientCertDetails) Validate() error {
|
||||||
|
if m == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if v, ok := interface{}(m.GetSubject()).(interface{ Validate() error }); ok {
|
||||||
|
if err := v.Validate(); err != nil {
|
||||||
|
return HttpConnectionManager_SetCurrentClientCertDetailsValidationError{
|
||||||
|
Field: "Subject",
|
||||||
|
Reason: "embedded message failed validation",
|
||||||
|
Cause: err,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// no validation rules for Cert
|
||||||
|
|
||||||
|
// no validation rules for Dns
|
||||||
|
|
||||||
|
// no validation rules for Uri
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// HttpConnectionManager_SetCurrentClientCertDetailsValidationError is the
|
||||||
|
// validation error returned by
|
||||||
|
// HttpConnectionManager_SetCurrentClientCertDetails.Validate if the
|
||||||
|
// designated constraints aren't met.
|
||||||
|
type HttpConnectionManager_SetCurrentClientCertDetailsValidationError struct {
|
||||||
|
Field string
|
||||||
|
Reason string
|
||||||
|
Cause error
|
||||||
|
Key bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// Error satisfies the builtin error interface
|
||||||
|
func (e HttpConnectionManager_SetCurrentClientCertDetailsValidationError) Error() string {
|
||||||
|
cause := ""
|
||||||
|
if e.Cause != nil {
|
||||||
|
cause = fmt.Sprintf(" | caused by: %v", e.Cause)
|
||||||
|
}
|
||||||
|
|
||||||
|
key := ""
|
||||||
|
if e.Key {
|
||||||
|
key = "key for "
|
||||||
|
}
|
||||||
|
|
||||||
|
return fmt.Sprintf(
|
||||||
|
"invalid %sHttpConnectionManager_SetCurrentClientCertDetails.%s: %s%s",
|
||||||
|
key,
|
||||||
|
e.Field,
|
||||||
|
e.Reason,
|
||||||
|
cause)
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ error = HttpConnectionManager_SetCurrentClientCertDetailsValidationError{}
|
||||||
|
|
||||||
|
// Validate checks the field values on HttpConnectionManager_UpgradeConfig with
|
||||||
|
// the rules defined in the proto definition for this message. If any rules
|
||||||
|
// are violated, an error is returned.
|
||||||
|
func (m *HttpConnectionManager_UpgradeConfig) Validate() error {
|
||||||
|
if m == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// no validation rules for UpgradeType
|
||||||
|
|
||||||
|
for idx, item := range m.GetFilters() {
|
||||||
|
_, _ = idx, item
|
||||||
|
|
||||||
|
if v, ok := interface{}(item).(interface{ Validate() error }); ok {
|
||||||
|
if err := v.Validate(); err != nil {
|
||||||
|
return HttpConnectionManager_UpgradeConfigValidationError{
|
||||||
|
Field: fmt.Sprintf("Filters[%v]", idx),
|
||||||
|
Reason: "embedded message failed validation",
|
||||||
|
Cause: err,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// HttpConnectionManager_UpgradeConfigValidationError is the validation error
|
||||||
|
// returned by HttpConnectionManager_UpgradeConfig.Validate if the designated
|
||||||
|
// constraints aren't met.
|
||||||
|
type HttpConnectionManager_UpgradeConfigValidationError struct {
|
||||||
|
Field string
|
||||||
|
Reason string
|
||||||
|
Cause error
|
||||||
|
Key bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// Error satisfies the builtin error interface
|
||||||
|
func (e HttpConnectionManager_UpgradeConfigValidationError) Error() string {
|
||||||
|
cause := ""
|
||||||
|
if e.Cause != nil {
|
||||||
|
cause = fmt.Sprintf(" | caused by: %v", e.Cause)
|
||||||
|
}
|
||||||
|
|
||||||
|
key := ""
|
||||||
|
if e.Key {
|
||||||
|
key = "key for "
|
||||||
|
}
|
||||||
|
|
||||||
|
return fmt.Sprintf(
|
||||||
|
"invalid %sHttpConnectionManager_UpgradeConfig.%s: %s%s",
|
||||||
|
key,
|
||||||
|
e.Field,
|
||||||
|
e.Reason,
|
||||||
|
cause)
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ error = HttpConnectionManager_UpgradeConfigValidationError{}
|
||||||
|
|
||||||
|
// Validate checks the field values on HttpFilter_DeprecatedV1 with the rules
|
||||||
|
// defined in the proto definition for this message. If any rules are
|
||||||
|
// violated, an error is returned.
|
||||||
|
func (m *HttpFilter_DeprecatedV1) Validate() error {
|
||||||
|
if m == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// no validation rules for Type
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// HttpFilter_DeprecatedV1ValidationError is the validation error returned by
|
||||||
|
// HttpFilter_DeprecatedV1.Validate if the designated constraints aren't met.
|
||||||
|
type HttpFilter_DeprecatedV1ValidationError struct {
|
||||||
|
Field string
|
||||||
|
Reason string
|
||||||
|
Cause error
|
||||||
|
Key bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// Error satisfies the builtin error interface
|
||||||
|
func (e HttpFilter_DeprecatedV1ValidationError) Error() string {
|
||||||
|
cause := ""
|
||||||
|
if e.Cause != nil {
|
||||||
|
cause = fmt.Sprintf(" | caused by: %v", e.Cause)
|
||||||
|
}
|
||||||
|
|
||||||
|
key := ""
|
||||||
|
if e.Key {
|
||||||
|
key = "key for "
|
||||||
|
}
|
||||||
|
|
||||||
|
return fmt.Sprintf(
|
||||||
|
"invalid %sHttpFilter_DeprecatedV1.%s: %s%s",
|
||||||
|
key,
|
||||||
|
e.Field,
|
||||||
|
e.Reason,
|
||||||
|
cause)
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ error = HttpFilter_DeprecatedV1ValidationError{}
|
|
@ -0,0 +1,21 @@
|
||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
Copyright © 2013-2017 Yasuhiro Matsumoto, <mattn.jp@gmail.com>
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
|
this software and associated documentation files (the “Software”), to deal in
|
||||||
|
the Software without restriction, including without limitation the rights to
|
||||||
|
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||||
|
of the Software, and to permit persons to whom the Software is furnished to do
|
||||||
|
so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
|
@ -23,7 +23,7 @@ environment:
|
||||||
GOPATH: c:\gopath
|
GOPATH: c:\gopath
|
||||||
matrix:
|
matrix:
|
||||||
- GOARCH: amd64
|
- GOARCH: amd64
|
||||||
GOVERSION: 1.4
|
GOVERSION: 1.5
|
||||||
GOROOT: c:\go
|
GOROOT: c:\go
|
||||||
DOWNLOADPLATFORM: "x64"
|
DOWNLOADPLATFORM: "x64"
|
||||||
|
|
||||||
|
@ -37,15 +37,6 @@ install:
|
||||||
# - set
|
# - set
|
||||||
- go version
|
- go version
|
||||||
- go env
|
- go env
|
||||||
- c:\gopath\src\github.com\go-ole\go-ole\build\compile-go.bat
|
|
||||||
- go tool dist install -v cmd/8a
|
|
||||||
- go tool dist install -v cmd/8c
|
|
||||||
- go tool dist install -v cmd/8g
|
|
||||||
- go tool dist install -v cmd/8l
|
|
||||||
- go tool dist install -v cmd/6a
|
|
||||||
- go tool dist install -v cmd/6c
|
|
||||||
- go tool dist install -v cmd/6g
|
|
||||||
- go tool dist install -v cmd/6l
|
|
||||||
- go get -u golang.org/x/tools/cmd/cover
|
- go get -u golang.org/x/tools/cmd/cover
|
||||||
- go get -u golang.org/x/tools/cmd/godoc
|
- go get -u golang.org/x/tools/cmd/godoc
|
||||||
- go get -u golang.org/x/tools/cmd/stringer
|
- go get -u golang.org/x/tools/cmd/stringer
|
||||||
|
|
|
@ -321,9 +321,9 @@ func DispatchMessage(msg *Msg) (ret int32) {
|
||||||
// GetVariantDate converts COM Variant Time value to Go time.Time.
|
// GetVariantDate converts COM Variant Time value to Go time.Time.
|
||||||
func GetVariantDate(value float64) (time.Time, error) {
|
func GetVariantDate(value float64) (time.Time, error) {
|
||||||
var st syscall.Systemtime
|
var st syscall.Systemtime
|
||||||
r, _, _ := procVariantTimeToSystemTime.Call(uintptr(unsafe.Pointer(&value)), uintptr(unsafe.Pointer(&st)))
|
r, _, _ := procVariantTimeToSystemTime.Call(uintptr(value), uintptr(unsafe.Pointer(&st)))
|
||||||
if r != 0 {
|
if r != 0 {
|
||||||
return time.Date(int(st.Year), time.Month(st.Month), int(st.Day), int(st.Hour), int(st.Minute), int(st.Second), int(st.Milliseconds/1000), nil), nil
|
return time.Date(int(st.Year), time.Month(st.Month), int(st.Day), int(st.Hour), int(st.Minute), int(st.Second), int(st.Milliseconds/1000), time.UTC), nil
|
||||||
}
|
}
|
||||||
return time.Now(), errors.New("Could not convert to time, passing current time.")
|
return time.Now(), errors.New("Could not convert to time, passing current time.")
|
||||||
}
|
}
|
||||||
|
|
|
@ -63,6 +63,10 @@ func invoke(disp *IDispatch, dispid int32, dispatch int16, params ...interface{}
|
||||||
dispnames := [1]int32{DISPID_PROPERTYPUT}
|
dispnames := [1]int32{DISPID_PROPERTYPUT}
|
||||||
dispparams.rgdispidNamedArgs = uintptr(unsafe.Pointer(&dispnames[0]))
|
dispparams.rgdispidNamedArgs = uintptr(unsafe.Pointer(&dispnames[0]))
|
||||||
dispparams.cNamedArgs = 1
|
dispparams.cNamedArgs = 1
|
||||||
|
} else if dispatch&DISPATCH_PROPERTYPUTREF != 0 {
|
||||||
|
dispnames := [1]int32{DISPID_PROPERTYPUT}
|
||||||
|
dispparams.rgdispidNamedArgs = uintptr(unsafe.Pointer(&dispnames[0]))
|
||||||
|
dispparams.cNamedArgs = 1
|
||||||
}
|
}
|
||||||
var vargs []VARIANT
|
var vargs []VARIANT
|
||||||
if len(params) > 0 {
|
if len(params) > 0 {
|
||||||
|
|
|
@ -26,6 +26,16 @@ type EXCEPINFO struct {
|
||||||
scode uint32
|
scode uint32
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// WCode return wCode in EXCEPINFO.
|
||||||
|
func (e EXCEPINFO) WCode() uint16 {
|
||||||
|
return e.wCode
|
||||||
|
}
|
||||||
|
|
||||||
|
// SCODE return scode in EXCEPINFO.
|
||||||
|
func (e EXCEPINFO) SCODE() uint32 {
|
||||||
|
return e.scode
|
||||||
|
}
|
||||||
|
|
||||||
// String convert EXCEPINFO to string.
|
// String convert EXCEPINFO to string.
|
||||||
func (e EXCEPINFO) String() string {
|
func (e EXCEPINFO) String() string {
|
||||||
var src, desc, hlp string
|
var src, desc, hlp string
|
||||||
|
|
|
@ -88,6 +88,20 @@ func MustPutProperty(disp *ole.IDispatch, name string, params ...interface{}) (r
|
||||||
return r
|
return r
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// PutPropertyRef mutates property reference.
|
||||||
|
func PutPropertyRef(disp *ole.IDispatch, name string, params ...interface{}) (result *ole.VARIANT, err error) {
|
||||||
|
return disp.InvokeWithOptionalArgs(name, ole.DISPATCH_PROPERTYPUTREF, params)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MustPutPropertyRef mutates property reference or panics.
|
||||||
|
func MustPutPropertyRef(disp *ole.IDispatch, name string, params ...interface{}) (result *ole.VARIANT) {
|
||||||
|
r, err := PutPropertyRef(disp, name, params...)
|
||||||
|
if err != nil {
|
||||||
|
panic(err.Error())
|
||||||
|
}
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
|
||||||
func ForEach(disp *ole.IDispatch, f func(v *ole.VARIANT) error) error {
|
func ForEach(disp *ole.IDispatch, f func(v *ole.VARIANT) error) error {
|
||||||
newEnum, err := disp.GetProperty("_NewEnum")
|
newEnum, err := disp.GetProperty("_NewEnum")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -75,19 +75,19 @@ func Sum256(data []byte) [Size256]byte {
|
||||||
}
|
}
|
||||||
|
|
||||||
// New512 returns a new hash.Hash computing the BLAKE2b-512 checksum. A non-nil
|
// New512 returns a new hash.Hash computing the BLAKE2b-512 checksum. A non-nil
|
||||||
// key turns the hash into a MAC. The key must between zero and 64 bytes long.
|
// key turns the hash into a MAC. The key must be between zero and 64 bytes long.
|
||||||
func New512(key []byte) (hash.Hash, error) { return newDigest(Size, key) }
|
func New512(key []byte) (hash.Hash, error) { return newDigest(Size, key) }
|
||||||
|
|
||||||
// New384 returns a new hash.Hash computing the BLAKE2b-384 checksum. A non-nil
|
// New384 returns a new hash.Hash computing the BLAKE2b-384 checksum. A non-nil
|
||||||
// key turns the hash into a MAC. The key must between zero and 64 bytes long.
|
// key turns the hash into a MAC. The key must be between zero and 64 bytes long.
|
||||||
func New384(key []byte) (hash.Hash, error) { return newDigest(Size384, key) }
|
func New384(key []byte) (hash.Hash, error) { return newDigest(Size384, key) }
|
||||||
|
|
||||||
// New256 returns a new hash.Hash computing the BLAKE2b-256 checksum. A non-nil
|
// New256 returns a new hash.Hash computing the BLAKE2b-256 checksum. A non-nil
|
||||||
// key turns the hash into a MAC. The key must between zero and 64 bytes long.
|
// key turns the hash into a MAC. The key must be between zero and 64 bytes long.
|
||||||
func New256(key []byte) (hash.Hash, error) { return newDigest(Size256, key) }
|
func New256(key []byte) (hash.Hash, error) { return newDigest(Size256, key) }
|
||||||
|
|
||||||
// New returns a new hash.Hash computing the BLAKE2b checksum with a custom length.
|
// New returns a new hash.Hash computing the BLAKE2b checksum with a custom length.
|
||||||
// A non-nil key turns the hash into a MAC. The key must between zero and 64 bytes long.
|
// A non-nil key turns the hash into a MAC. The key must be between zero and 64 bytes long.
|
||||||
// The hash size can be a value between 1 and 64 but it is highly recommended to use
|
// The hash size can be a value between 1 and 64 but it is highly recommended to use
|
||||||
// values equal or greater than:
|
// values equal or greater than:
|
||||||
// - 32 if BLAKE2b is used as a hash function (The key is zero bytes long).
|
// - 32 if BLAKE2b is used as a hash function (The key is zero bytes long).
|
||||||
|
|
|
@ -50,6 +50,12 @@ func NewFixedBuilder(buffer []byte) *Builder {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SetError sets the value to be returned as the error from Bytes. Writes
|
||||||
|
// performed after calling SetError are ignored.
|
||||||
|
func (b *Builder) SetError(err error) {
|
||||||
|
b.err = err
|
||||||
|
}
|
||||||
|
|
||||||
// Bytes returns the bytes written by the builder or an error if one has
|
// Bytes returns the bytes written by the builder or an error if one has
|
||||||
// occurred during building.
|
// occurred during building.
|
||||||
func (b *Builder) Bytes() ([]byte, error) {
|
func (b *Builder) Bytes() ([]byte, error) {
|
||||||
|
@ -94,7 +100,7 @@ func (b *Builder) AddBytes(v []byte) {
|
||||||
b.add(v...)
|
b.add(v...)
|
||||||
}
|
}
|
||||||
|
|
||||||
// BuilderContinuation is continuation-passing interface for building
|
// BuilderContinuation is a continuation-passing interface for building
|
||||||
// length-prefixed byte sequences. Builder methods for length-prefixed
|
// length-prefixed byte sequences. Builder methods for length-prefixed
|
||||||
// sequences (AddUint8LengthPrefixed etc) will invoke the BuilderContinuation
|
// sequences (AddUint8LengthPrefixed etc) will invoke the BuilderContinuation
|
||||||
// supplied to them. The child builder passed to the continuation can be used
|
// supplied to them. The child builder passed to the continuation can be used
|
||||||
|
@ -268,9 +274,11 @@ func (b *Builder) flushChild() {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if !b.fixedSize {
|
if b.fixedSize && &b.result[0] != &child.result[0] {
|
||||||
b.result = child.result // In case child reallocated result.
|
panic("cryptobyte: BuilderContinuation reallocated a fixed-size buffer")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
b.result = child.result
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *Builder) add(bytes ...byte) {
|
func (b *Builder) add(bytes ...byte) {
|
||||||
|
@ -278,7 +286,7 @@ func (b *Builder) add(bytes ...byte) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if b.child != nil {
|
if b.child != nil {
|
||||||
panic("attempted write while child is pending")
|
panic("cryptobyte: attempted write while child is pending")
|
||||||
}
|
}
|
||||||
if len(b.result)+len(bytes) < len(bytes) {
|
if len(b.result)+len(bytes) < len(bytes) {
|
||||||
b.err = errors.New("cryptobyte: length overflow")
|
b.err = errors.New("cryptobyte: length overflow")
|
||||||
|
@ -290,6 +298,26 @@ func (b *Builder) add(bytes ...byte) {
|
||||||
b.result = append(b.result, bytes...)
|
b.result = append(b.result, bytes...)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Unwrite rolls back n bytes written directly to the Builder. An attempt by a
|
||||||
|
// child builder passed to a continuation to unwrite bytes from its parent will
|
||||||
|
// panic.
|
||||||
|
func (b *Builder) Unwrite(n int) {
|
||||||
|
if b.err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if b.child != nil {
|
||||||
|
panic("cryptobyte: attempted unwrite while child is pending")
|
||||||
|
}
|
||||||
|
length := len(b.result) - b.pendingLenLen - b.offset
|
||||||
|
if length < 0 {
|
||||||
|
panic("cryptobyte: internal error")
|
||||||
|
}
|
||||||
|
if n > length {
|
||||||
|
panic("cryptobyte: attempted to unwrite more than was written")
|
||||||
|
}
|
||||||
|
b.result = b.result[:len(b.result)-n]
|
||||||
|
}
|
||||||
|
|
||||||
// A MarshalingValue marshals itself into a Builder.
|
// A MarshalingValue marshals itself into a Builder.
|
||||||
type MarshalingValue interface {
|
type MarshalingValue interface {
|
||||||
// Marshal is called by Builder.AddValue. It receives a pointer to a builder
|
// Marshal is called by Builder.AddValue. It receives a pointer to a builder
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue