consul/command/connect/envoy/envoy_test.go

1921 lines
57 KiB
Go
Raw Normal View History

// Copyright (c) HashiCorp, Inc.
[COMPLIANCE] License changes (#18443) * Adding explicit MPL license for sub-package This directory and its subdirectories (packages) contain files licensed with the MPLv2 `LICENSE` file in this directory and are intentionally licensed separately from the BSL `LICENSE` file at the root of this repository. * Adding explicit MPL license for sub-package This directory and its subdirectories (packages) contain files licensed with the MPLv2 `LICENSE` file in this directory and are intentionally licensed separately from the BSL `LICENSE` file at the root of this repository. * Updating the license from MPL to Business Source License Going forward, this project will be licensed under the Business Source License v1.1. Please see our blog post for more details at <Blog URL>, FAQ at www.hashicorp.com/licensing-faq, and details of the license at www.hashicorp.com/bsl. * add missing license headers * Update copyright file headers to BUSL-1.1 * Update copyright file headers to BUSL-1.1 * Update copyright file headers to BUSL-1.1 * Update copyright file headers to BUSL-1.1 * Update copyright file headers to BUSL-1.1 * Update copyright file headers to BUSL-1.1 * Update copyright file headers to BUSL-1.1 * Update copyright file headers to BUSL-1.1 * Update copyright file headers to BUSL-1.1 * Update copyright file headers to BUSL-1.1 * Update copyright file headers to BUSL-1.1 * Update copyright file headers to BUSL-1.1 * Update copyright file headers to BUSL-1.1 * Update copyright file headers to BUSL-1.1 * Update copyright file headers to BUSL-1.1 --------- Co-authored-by: hashicorp-copywrite[bot] <110428419+hashicorp-copywrite[bot]@users.noreply.github.com>
2023-08-11 13:12:13 +00:00
// SPDX-License-Identifier: BUSL-1.1
package envoy
import (
"encoding/json"
"flag"
"fmt"
"net"
"net/http"
"net/http/httptest"
"os"
"path/filepath"
"strconv"
"strings"
"testing"
"github.com/mitchellh/cli"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/hashicorp/consul/acl"
"github.com/hashicorp/consul/agent"
"github.com/hashicorp/consul/agent/xds"
"github.com/hashicorp/consul/api"
"github.com/hashicorp/consul/envoyextensions/xdscommon"
"github.com/hashicorp/consul/sdk/testutil"
)
var update = flag.Bool("update", false, "update golden files")
func TestEnvoyCommand_noTabs(t *testing.T) {
t.Parallel()
if strings.ContainsRune(New(nil).Help(), '\t') {
t.Fatal("help has tabs")
}
}
func TestEnvoyGateway_Validation(t *testing.T) {
t.Parallel()
cases := []struct {
name string
args []string
output string
}{
{
"-register for non-gateway",
[]string{"-register", "-proxy-id", "not-a-gateway"},
"Auto-Registration can only be used for gateways",
},
{
"-mesh-gateway and -gateway cannot be combined",
[]string{"-register", "-mesh-gateway", "-gateway", "mesh"},
"The mesh-gateway flag is deprecated and cannot be used alongside the gateway flag",
},
{
"no proxy registration specified nor discovered",
[]string{""},
"No proxy ID specified",
},
{
"-register with nodename",
[]string{"-register", "-proxy-id", "gw-svc-id", "-node-name", "gw-node"},
"'-register' cannot be used with '-node-name'",
},
}
for _, tc := range cases {
t.Run(tc.name, func(t *testing.T) {
ui := cli.NewMockUi()
c := New(ui)
c.init()
code := c.Run(tc.args)
if code == 0 {
t.Errorf("%s: expected non-zero exit", tc.name)
}
output := ui.ErrorWriter.String()
if !strings.Contains(output, tc.output) {
t.Errorf("expected %q to contain %q", output, tc.output)
}
})
}
}
// testSetAndResetEnv sets the env vars passed as KEY=value strings in the
// current ENV and returns a func() that will undo it's work at the end of the
// test for use with defer.
func testSetAndResetEnv(t *testing.T, env []string) func() {
old := make(map[string]*string)
for _, e := range env {
pair := strings.SplitN(e, "=", 2)
current := os.Getenv(pair[0])
if current != "" {
old[pair[0]] = &current
} else {
// save it as a nil so we know to remove again
old[pair[0]] = nil
}
2021-09-15 23:22:22 +00:00
require.NoError(t, os.Setenv(pair[0], pair[1]))
}
// Return a func that will reset to old values
return func() {
for k, v := range old {
if v == nil {
os.Unsetenv(k)
} else {
os.Setenv(k, *v)
}
}
}
}
type generateConfigTestCase struct {
Name string
2021-09-15 23:22:22 +00:00
TLSServer bool
ACLEnabled bool
Flags []string
Env []string
Files map[string]string
ProxyConfig map[string]interface{}
ProxyDefaults api.ProxyConfigEntry
NamespacesEnabled bool
XDSPorts agent.GRPCPorts // used to mock an agent's configured gRPC ports. Plaintext defaults to 8502 and TLS defaults to 8503.
AgentSelf110 bool // fake the agent API from versions v1.10 and earlier
GRPCDisabled bool
WantArgs BootstrapTplArgs
WantErr string
WantWarn string
}
// This tests the args we use to generate the template directly because they
// encapsulate all the argument and default handling code which is where most of
// the logic is. We also allow generating golden files but only for cases that
// pass the test of having their template args generated as expected.
func TestGenerateConfig(t *testing.T) {
b, err := os.ReadFile("../../../test/ca/root.cer")
require.NoError(t, err)
rootPEM := string(b)
rootPEM = strings.Replace(rootPEM, "\n", "\\n", -1)
b, err = os.ReadFile("../../../test/ca_path/cert1.crt")
require.NoError(t, err)
pathPEM := string(b)
b, err = os.ReadFile("../../../test/ca_path/cert2.crt")
require.NoError(t, err)
pathPEM += string(b)
pathPEM = strings.Replace(pathPEM, "\n", "\\n", -1)
cases := []generateConfigTestCase{
{
Name: "no-args",
Flags: []string{},
Env: []string{},
WantErr: "No proxy ID specified",
},
{
Name: "node-name without proxy-id",
Flags: []string{"-node-name", "test-node"},
WantErr: "'-node-name' requires '-proxy-id'",
},
{
Name: "gRPC disabled",
Flags: []string{"-proxy-id", "test-proxy"},
GRPCDisabled: true,
WantErr: "agent has grpc disabled",
},
{
Name: "defaults",
Flags: []string{"-proxy-id", "test-proxy"},
WantArgs: BootstrapTplArgs{
ProxyCluster: "test-proxy",
ProxyID: "test-proxy",
// We don't know this til after the lookup so it will be empty in the
// initial args call we are testing here.
ProxySourceService: "",
GRPC: GRPC{
AgentAddress: "127.0.0.1",
AgentPort: "8502", // Note this is the gRPC port
},
AdminAccessLogPath: "/dev/null",
AdminBindAddress: "127.0.0.1",
AdminBindPort: "19000",
LocalAgentClusterName: xds.LocalAgentClusterName,
PrometheusBackendPort: "",
PrometheusScrapePath: "/metrics",
},
},
{
Name: "defaults-nodemeta",
Flags: []string{"-proxy-id", "test-proxy", "-node-name", "test-node"},
WantArgs: BootstrapTplArgs{
ProxyCluster: "test-proxy",
ProxyID: "test-proxy",
NodeName: "test-node",
// We don't know this til after the lookup so it will be empty in the
// initial args call we are testing here.
ProxySourceService: "",
GRPC: GRPC{
AgentAddress: "127.0.0.1",
AgentPort: "8502", // Note this is the gRPC port
},
AdminAccessLogPath: "/dev/null",
AdminBindAddress: "127.0.0.1",
AdminBindPort: "19000",
LocalAgentClusterName: xds.LocalAgentClusterName,
PrometheusBackendPort: "",
PrometheusScrapePath: "/metrics",
},
},
Allow HCP metrics collection for Envoy proxies Co-authored-by: Ashvitha Sridharan <ashvitha.sridharan@hashicorp.com> Co-authored-by: Freddy <freddygv@users.noreply.github.com> Add a new envoy flag: "envoy_hcp_metrics_bind_socket_dir", a directory where a unix socket will be created with the name `<namespace>_<proxy_id>.sock` to forward Envoy metrics. If set, this will configure: - In bootstrap configuration a local stats_sink and static cluster. These will forward metrics to a loopback listener sent over xDS. - A dynamic listener listening at the socket path that the previously defined static cluster is sending metrics to. - A dynamic cluster that will forward traffic received at this listener to the hcp-metrics-collector service. Reasons for having a static cluster pointing at a dynamic listener: - We want to secure the metrics stream using TLS, but the stats sink can only be defined in bootstrap config. With dynamic listeners/clusters we can use the proxy's leaf certificate issued by the Connect CA, which isn't available at bootstrap time. - We want to intelligently route to the HCP collector. Configuring its addreess at bootstrap time limits our flexibility routing-wise. More on this below. Reasons for defining the collector as an upstream in `proxycfg`: - The HCP collector will be deployed as a mesh service. - Certificate management is taken care of, as mentioned above. - Service discovery and routing logic is automatically taken care of, meaning that no code changes are required in the xds package. - Custom routing rules can be added for the collector using discovery chain config entries. Initially the collector is expected to be deployed to each admin partition, but in the future could be deployed centrally in the default partition. These config entries could even be managed by HCP itself.
2023-03-10 20:52:54 +00:00
{
Name: "telemetry-collector",
Allow HCP metrics collection for Envoy proxies Co-authored-by: Ashvitha Sridharan <ashvitha.sridharan@hashicorp.com> Co-authored-by: Freddy <freddygv@users.noreply.github.com> Add a new envoy flag: "envoy_hcp_metrics_bind_socket_dir", a directory where a unix socket will be created with the name `<namespace>_<proxy_id>.sock` to forward Envoy metrics. If set, this will configure: - In bootstrap configuration a local stats_sink and static cluster. These will forward metrics to a loopback listener sent over xDS. - A dynamic listener listening at the socket path that the previously defined static cluster is sending metrics to. - A dynamic cluster that will forward traffic received at this listener to the hcp-metrics-collector service. Reasons for having a static cluster pointing at a dynamic listener: - We want to secure the metrics stream using TLS, but the stats sink can only be defined in bootstrap config. With dynamic listeners/clusters we can use the proxy's leaf certificate issued by the Connect CA, which isn't available at bootstrap time. - We want to intelligently route to the HCP collector. Configuring its addreess at bootstrap time limits our flexibility routing-wise. More on this below. Reasons for defining the collector as an upstream in `proxycfg`: - The HCP collector will be deployed as a mesh service. - Certificate management is taken care of, as mentioned above. - Service discovery and routing logic is automatically taken care of, meaning that no code changes are required in the xds package. - Custom routing rules can be added for the collector using discovery chain config entries. Initially the collector is expected to be deployed to each admin partition, but in the future could be deployed centrally in the default partition. These config entries could even be managed by HCP itself.
2023-03-10 20:52:54 +00:00
Flags: []string{"-proxy-id", "test-proxy"},
ProxyConfig: map[string]interface{}{
"envoy_telemetry_collector_bind_socket_dir": "/tmp/consul/telemetry-collector",
Allow HCP metrics collection for Envoy proxies Co-authored-by: Ashvitha Sridharan <ashvitha.sridharan@hashicorp.com> Co-authored-by: Freddy <freddygv@users.noreply.github.com> Add a new envoy flag: "envoy_hcp_metrics_bind_socket_dir", a directory where a unix socket will be created with the name `<namespace>_<proxy_id>.sock` to forward Envoy metrics. If set, this will configure: - In bootstrap configuration a local stats_sink and static cluster. These will forward metrics to a loopback listener sent over xDS. - A dynamic listener listening at the socket path that the previously defined static cluster is sending metrics to. - A dynamic cluster that will forward traffic received at this listener to the hcp-metrics-collector service. Reasons for having a static cluster pointing at a dynamic listener: - We want to secure the metrics stream using TLS, but the stats sink can only be defined in bootstrap config. With dynamic listeners/clusters we can use the proxy's leaf certificate issued by the Connect CA, which isn't available at bootstrap time. - We want to intelligently route to the HCP collector. Configuring its addreess at bootstrap time limits our flexibility routing-wise. More on this below. Reasons for defining the collector as an upstream in `proxycfg`: - The HCP collector will be deployed as a mesh service. - Certificate management is taken care of, as mentioned above. - Service discovery and routing logic is automatically taken care of, meaning that no code changes are required in the xds package. - Custom routing rules can be added for the collector using discovery chain config entries. Initially the collector is expected to be deployed to each admin partition, but in the future could be deployed centrally in the default partition. These config entries could even be managed by HCP itself.
2023-03-10 20:52:54 +00:00
},
WantArgs: BootstrapTplArgs{
ProxyCluster: "test-proxy",
ProxyID: "test-proxy",
// We don't know this til after the lookup so it will be empty in the
// initial args call we are testing here.
ProxySourceService: "",
GRPC: GRPC{
AgentAddress: "127.0.0.1",
AgentPort: "8502",
},
AdminAccessLogPath: "/dev/null",
AdminBindAddress: "127.0.0.1",
AdminBindPort: "19000",
LocalAgentClusterName: xds.LocalAgentClusterName,
PrometheusScrapePath: "/metrics",
},
},
{
Name: "prometheus-metrics",
Flags: []string{"-proxy-id", "test-proxy",
"-prometheus-backend-port", "20100", "-prometheus-scrape-path", "/scrape-path"},
ProxyConfig: map[string]interface{}{
// When envoy_prometheus_bind_addr is set, if
// PrometheusBackendPort is set, there will be a
// "prometheus_backend" cluster in the Envoy configuration.
"envoy_prometheus_bind_addr": "0.0.0.0:9000",
},
WantArgs: BootstrapTplArgs{
ProxyCluster: "test-proxy",
ProxyID: "test-proxy",
// We don't know this til after the lookup so it will be empty in the
// initial args call we are testing here.
ProxySourceService: "",
GRPC: GRPC{
AgentAddress: "127.0.0.1",
AgentPort: "8502", // Note this is the gRPC port
},
AdminAccessLogPath: "/dev/null",
AdminBindAddress: "127.0.0.1",
AdminBindPort: "19000",
LocalAgentClusterName: xds.LocalAgentClusterName,
PrometheusBackendPort: "20100",
PrometheusScrapePath: "/scrape-path",
},
},
{
Name: "prometheus-metrics-tls-ca-file",
Flags: []string{"-proxy-id", "test-proxy",
"-prometheus-backend-port", "20100", "-prometheus-scrape-path", "/scrape-path",
"-prometheus-ca-file", "../../../test/key/ourdomain.cer", "-prometheus-cert-file", "../../../test/key/ourdomain_server.cer",
"-prometheus-key-file", "../../../test/key/ourdomain_server.key"},
ProxyConfig: map[string]interface{}{
// When envoy_prometheus_bind_addr is set, if
// PrometheusBackendPort is set, there will be a
// "prometheus_backend" cluster in the Envoy configuration.
"envoy_prometheus_bind_addr": "0.0.0.0:9000",
},
WantArgs: BootstrapTplArgs{
ProxyCluster: "test-proxy",
ProxyID: "test-proxy",
// We don't know this til after the lookup so it will be empty in the
// initial args call we are testing here.
ProxySourceService: "",
GRPC: GRPC{
AgentAddress: "127.0.0.1",
AgentPort: "8502", // Note this is the gRPC port
},
AdminAccessLogPath: "/dev/null",
AdminBindAddress: "127.0.0.1",
AdminBindPort: "19000",
LocalAgentClusterName: xds.LocalAgentClusterName,
PrometheusBackendPort: "20100",
PrometheusScrapePath: "/scrape-path",
PrometheusCAFile: "../../../test/key/ourdomain.cer",
PrometheusCertFile: "../../../test/key/ourdomain_server.cer",
PrometheusKeyFile: "../../../test/key/ourdomain_server.key",
},
},
{
Name: "prometheus-metrics-tls-ca-path",
Flags: []string{"-proxy-id", "test-proxy",
"-prometheus-backend-port", "20100", "-prometheus-scrape-path", "/scrape-path",
"-prometheus-ca-path", "../../../test/ca_path", "-prometheus-cert-file", "../../../test/key/ourdomain_server.cer",
"-prometheus-key-file", "../../../test/key/ourdomain_server.key"},
ProxyConfig: map[string]interface{}{
// When envoy_prometheus_bind_addr is set, if
// PrometheusBackendPort is set, there will be a
// "prometheus_backend" cluster in the Envoy configuration.
"envoy_prometheus_bind_addr": "0.0.0.0:9000",
},
WantArgs: BootstrapTplArgs{
ProxyCluster: "test-proxy",
ProxyID: "test-proxy",
// We don't know this til after the lookup so it will be empty in the
// initial args call we are testing here.
ProxySourceService: "",
GRPC: GRPC{
AgentAddress: "127.0.0.1",
AgentPort: "8502", // Note this is the gRPC port
},
AdminAccessLogPath: "/dev/null",
AdminBindAddress: "127.0.0.1",
AdminBindPort: "19000",
LocalAgentClusterName: xds.LocalAgentClusterName,
PrometheusBackendPort: "20100",
PrometheusScrapePath: "/scrape-path",
PrometheusCAPath: "../../../test/ca_path",
PrometheusCertFile: "../../../test/key/ourdomain_server.cer",
PrometheusKeyFile: "../../../test/key/ourdomain_server.key",
},
},
{
Name: "token-arg",
Flags: []string{"-proxy-id", "test-proxy",
"-token", "c9a52720-bf6c-4aa6-b8bc-66881a5ade95"},
WantArgs: BootstrapTplArgs{
ProxyCluster: "test-proxy",
ProxyID: "test-proxy",
// We don't know this til after the lookup so it will be empty in the
// initial args call we are testing here.
ProxySourceService: "",
GRPC: GRPC{
AgentAddress: "127.0.0.1",
AgentPort: "8502", // Note this is the gRPC port
},
AdminAccessLogPath: "/dev/null",
AdminBindAddress: "127.0.0.1",
AdminBindPort: "19000",
LocalAgentClusterName: xds.LocalAgentClusterName,
Token: "c9a52720-bf6c-4aa6-b8bc-66881a5ade95",
PrometheusScrapePath: "/metrics",
},
},
{
Name: "token-env",
Flags: []string{"-proxy-id", "test-proxy"},
Env: []string{
"CONSUL_HTTP_TOKEN=c9a52720-bf6c-4aa6-b8bc-66881a5ade95",
},
WantArgs: BootstrapTplArgs{
ProxyCluster: "test-proxy",
ProxyID: "test-proxy",
// We don't know this til after the lookup so it will be empty in the
// initial args call we are testing here.
ProxySourceService: "",
GRPC: GRPC{
AgentAddress: "127.0.0.1",
AgentPort: "8502", // Note this is the gRPC port
},
AdminAccessLogPath: "/dev/null",
AdminBindAddress: "127.0.0.1",
AdminBindPort: "19000",
LocalAgentClusterName: xds.LocalAgentClusterName,
Token: "c9a52720-bf6c-4aa6-b8bc-66881a5ade95",
PrometheusScrapePath: "/metrics",
},
},
{
Name: "token-file-arg",
Flags: []string{"-proxy-id", "test-proxy",
"-token-file", "@@TEMPDIR@@token.txt",
},
Files: map[string]string{
"token.txt": "c9a52720-bf6c-4aa6-b8bc-66881a5ade95",
},
WantArgs: BootstrapTplArgs{
ProxyCluster: "test-proxy",
ProxyID: "test-proxy",
// We don't know this til after the lookup so it will be empty in the
// initial args call we are testing here.
ProxySourceService: "",
GRPC: GRPC{
AgentAddress: "127.0.0.1",
AgentPort: "8502", // Note this is the gRPC port
},
AdminAccessLogPath: "/dev/null",
AdminBindAddress: "127.0.0.1",
AdminBindPort: "19000",
LocalAgentClusterName: xds.LocalAgentClusterName,
Token: "c9a52720-bf6c-4aa6-b8bc-66881a5ade95",
PrometheusScrapePath: "/metrics",
},
},
{
Name: "token-file-env",
Flags: []string{"-proxy-id", "test-proxy"},
Env: []string{
"CONSUL_HTTP_TOKEN_FILE=@@TEMPDIR@@token.txt",
},
Files: map[string]string{
"token.txt": "c9a52720-bf6c-4aa6-b8bc-66881a5ade95",
},
WantArgs: BootstrapTplArgs{
ProxyCluster: "test-proxy",
ProxyID: "test-proxy",
// We don't know this til after the lookup so it will be empty in the
// initial args call we are testing here.
ProxySourceService: "",
GRPC: GRPC{
AgentAddress: "127.0.0.1",
AgentPort: "8502", // Note this is the gRPC port
},
AdminAccessLogPath: "/dev/null",
AdminBindAddress: "127.0.0.1",
AdminBindPort: "19000",
LocalAgentClusterName: xds.LocalAgentClusterName,
Token: "c9a52720-bf6c-4aa6-b8bc-66881a5ade95",
PrometheusScrapePath: "/metrics",
},
},
{
Name: "grpc-addr-flag",
Flags: []string{"-proxy-id", "test-proxy",
"-grpc-addr", "localhost:9999"},
WantArgs: BootstrapTplArgs{
ProxyCluster: "test-proxy",
ProxyID: "test-proxy",
// We don't know this til after the lookup so it will be empty in the
// initial args call we are testing here.
ProxySourceService: "",
// Should resolve IP, note this might not resolve the same way
// everywhere which might make this test brittle but not sure what else
// to do.
GRPC: GRPC{
AgentAddress: "127.0.0.1",
AgentPort: "9999",
},
AdminAccessLogPath: "/dev/null",
AdminBindAddress: "127.0.0.1",
AdminBindPort: "19000",
LocalAgentClusterName: xds.LocalAgentClusterName,
PrometheusScrapePath: "/metrics",
},
},
{
Name: "grpc-addr-env",
Flags: []string{"-proxy-id", "test-proxy"},
Env: []string{
"CONSUL_GRPC_ADDR=localhost:9999",
},
WantArgs: BootstrapTplArgs{
ProxyCluster: "test-proxy",
ProxyID: "test-proxy",
// We don't know this til after the lookup so it will be empty in the
// initial args call we are testing here.
ProxySourceService: "",
// Should resolve IP, note this might not resolve the same way
// everywhere which might make this test brittle but not sure what else
// to do.
GRPC: GRPC{
AgentAddress: "127.0.0.1",
AgentPort: "9999",
},
AdminAccessLogPath: "/dev/null",
AdminBindAddress: "127.0.0.1",
AdminBindPort: "19000",
LocalAgentClusterName: xds.LocalAgentClusterName,
PrometheusScrapePath: "/metrics",
},
},
{
Name: "grpc-addr-unix",
Flags: []string{"-proxy-id", "test-proxy",
"-grpc-addr", "unix:///var/run/consul.sock"},
WantArgs: BootstrapTplArgs{
ProxyCluster: "test-proxy",
ProxyID: "test-proxy",
// We don't know this til after the lookup so it will be empty in the
// initial args call we are testing here.
ProxySourceService: "",
GRPC: GRPC{
AgentSocket: "/var/run/consul.sock",
},
AdminAccessLogPath: "/dev/null",
AdminBindAddress: "127.0.0.1",
AdminBindPort: "19000",
LocalAgentClusterName: xds.LocalAgentClusterName,
PrometheusScrapePath: "/metrics",
},
},
{
Name: "grpc-addr-unix-with-tls",
Flags: []string{"-proxy-id", "test-proxy",
"-grpc-ca-file", "../../../test/ca/root.cer",
"-grpc-addr", "unix:///var/run/consul.sock"},
WantArgs: BootstrapTplArgs{
ProxyCluster: "test-proxy",
ProxyID: "test-proxy",
GRPC: GRPC{
AgentSocket: "/var/run/consul.sock",
AgentTLS: true,
},
AdminAccessLogPath: "/dev/null",
AdminBindAddress: "127.0.0.1",
AdminBindPort: "19000",
AgentCAPEM: rootPEM,
LocalAgentClusterName: xds.LocalAgentClusterName,
PrometheusScrapePath: "/metrics",
},
},
{
2022-09-01 17:32:11 +00:00
Name: "xds-addr-config",
Flags: []string{"-proxy-id", "test-proxy"},
XDSPorts: agent.GRPCPorts{Plaintext: 9999, TLS: 0},
WantArgs: BootstrapTplArgs{
ProxyCluster: "test-proxy",
ProxyID: "test-proxy",
// We don't know this til after the lookup so it will be empty in the
// initial args call we are testing here.
ProxySourceService: "",
// Should resolve IP, note this might not resolve the same way
// everywhere which might make this test brittle but not sure what else
// to do.
GRPC: GRPC{
AgentAddress: "127.0.0.1",
AgentPort: "9999",
},
AdminAccessLogPath: "/dev/null",
AdminBindAddress: "127.0.0.1",
AdminBindPort: "19000",
LocalAgentClusterName: xds.LocalAgentClusterName,
PrometheusScrapePath: "/metrics",
},
},
2022-09-01 17:32:11 +00:00
{
Name: "grpc-tls-addr-config",
Flags: []string{"-proxy-id", "test-proxy"},
XDSPorts: agent.GRPCPorts{Plaintext: 9997, TLS: 9998},
AgentSelf110: false,
WantArgs: BootstrapTplArgs{
ProxyCluster: "test-proxy",
ProxyID: "test-proxy",
// We don't know this til after the lookup so it will be empty in the
// initial args call we are testing here.
ProxySourceService: "",
// Should resolve IP, note this might not resolve the same way
// everywhere which might make this test brittle but not sure what else
// to do.
GRPC: GRPC{
AgentAddress: "127.0.0.1",
AgentPort: "9998",
AgentTLS: true,
},
AdminAccessLogPath: "/dev/null",
AdminBindAddress: "127.0.0.1",
AdminBindPort: "19000",
LocalAgentClusterName: xds.LocalAgentClusterName,
PrometheusScrapePath: "/metrics",
},
},
{
Name: "deprecated-grpc-addr-config",
Flags: []string{"-proxy-id", "test-proxy"},
2022-09-01 17:32:11 +00:00
XDSPorts: agent.GRPCPorts{Plaintext: 9999, TLS: 0},
AgentSelf110: true,
WantArgs: BootstrapTplArgs{
ProxyCluster: "test-proxy",
ProxyID: "test-proxy",
// We don't know this til after the lookup so it will be empty in the
// initial args call we are testing here.
ProxySourceService: "",
// Should resolve IP, note this might not resolve the same way
// everywhere which might make this test brittle but not sure what else
// to do.
GRPC: GRPC{
AgentAddress: "127.0.0.1",
AgentPort: "9999",
},
AdminAccessLogPath: "/dev/null",
AdminBindAddress: "127.0.0.1",
AdminBindPort: "19000",
LocalAgentClusterName: xds.LocalAgentClusterName,
PrometheusScrapePath: "/metrics",
},
},
{
Name: "access-log-path",
Flags: []string{"-proxy-id", "test-proxy", "-admin-access-log-path", "/some/path/access.log"},
WantWarn: "-admin-access-log-path is deprecated",
WantArgs: BootstrapTplArgs{
ProxyCluster: "test-proxy",
ProxyID: "test-proxy",
// We don't know this til after the lookup so it will be empty in the
// initial args call we are testing here.
ProxySourceService: "",
// Should resolve IP, note this might not resolve the same way
// everywhere which might make this test brittle but not sure what else
// to do.
GRPC: GRPC{
AgentAddress: "127.0.0.1",
AgentPort: "8502",
},
AdminAccessLogPath: "/some/path/access.log",
AdminBindAddress: "127.0.0.1",
AdminBindPort: "19000",
LocalAgentClusterName: xds.LocalAgentClusterName,
PrometheusScrapePath: "/metrics",
},
},
{
Name: "missing-ca-file",
Flags: []string{"-proxy-id", "test-proxy", "-ca-file", "some/path"},
WantErr: "Error loading CA File: open some/path: no such file or directory",
},
{
2021-09-15 23:22:22 +00:00
Name: "existing-ca-file",
TLSServer: true,
Flags: []string{"-proxy-id", "test-proxy", "-grpc-ca-file", "../../../test/ca/root.cer"},
Env: []string{"CONSUL_GRPC_ADDR=https://127.0.0.1:8502"},
WantArgs: BootstrapTplArgs{
ProxyCluster: "test-proxy",
ProxyID: "test-proxy",
// We don't know this til after the lookup so it will be empty in the
// initial args call we are testing here.
ProxySourceService: "",
// Should resolve IP, note this might not resolve the same way
// everywhere which might make this test brittle but not sure what else
// to do.
GRPC: GRPC{
AgentAddress: "127.0.0.1",
AgentPort: "8502",
AgentTLS: true,
},
AgentCAPEM: rootPEM,
AdminAccessLogPath: "/dev/null",
AdminBindAddress: "127.0.0.1",
AdminBindPort: "19000",
LocalAgentClusterName: xds.LocalAgentClusterName,
PrometheusScrapePath: "/metrics",
},
},
{
Name: "missing-ca-path",
Flags: []string{"-proxy-id", "test-proxy", "-ca-path", "some/path"},
WantErr: "lstat some/path: no such file or directory",
},
{
2021-09-15 23:22:22 +00:00
Name: "existing-ca-path",
TLSServer: true,
Flags: []string{"-proxy-id", "test-proxy", "-grpc-ca-path", "../../../test/ca_path/"},
Env: []string{"CONSUL_GRPC_ADDR=https://127.0.0.1:8502"},
WantArgs: BootstrapTplArgs{
ProxyCluster: "test-proxy",
ProxyID: "test-proxy",
// We don't know this til after the lookup so it will be empty in the
// initial args call we are testing here.
ProxySourceService: "",
// Should resolve IP, note this might not resolve the same way
// everywhere which might make this test brittle but not sure what else
// to do.
GRPC: GRPC{
AgentAddress: "127.0.0.1",
AgentPort: "8502",
AgentTLS: true,
},
AgentCAPEM: pathPEM,
AdminAccessLogPath: "/dev/null",
AdminBindAddress: "127.0.0.1",
AdminBindPort: "19000",
LocalAgentClusterName: xds.LocalAgentClusterName,
PrometheusScrapePath: "/metrics",
},
},
{
Name: "custom-bootstrap",
Flags: []string{"-proxy-id", "test-proxy"},
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",
// We don't know this til after the lookup so it will be empty in the
// initial args call we are testing here.
ProxySourceService: "",
GRPC: GRPC{
AgentAddress: "127.0.0.1",
AgentPort: "8502",
},
AdminAccessLogPath: "/dev/null",
AdminBindAddress: "127.0.0.1",
AdminBindPort: "19000",
LocalAgentClusterName: xds.LocalAgentClusterName,
PrometheusScrapePath: "/metrics",
},
},
{
Name: "extra_-single",
Flags: []string{"-proxy-id", "test-proxy"},
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",
// We don't know this til after the lookup so it will be empty in the
// initial args call we are testing here.
ProxySourceService: "",
GRPC: GRPC{
AgentAddress: "127.0.0.1",
AgentPort: "8502",
},
AdminAccessLogPath: "/dev/null",
AdminBindAddress: "127.0.0.1",
AdminBindPort: "19000",
LocalAgentClusterName: xds.LocalAgentClusterName,
PrometheusScrapePath: "/metrics",
},
},
{
Name: "extra_-multiple",
Flags: []string{"-proxy-id", "test-proxy"},
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",
// We don't know this til after the lookup so it will be empty in the
// initial args call we are testing here.
ProxySourceService: "",
GRPC: GRPC{
AgentAddress: "127.0.0.1",
AgentPort: "8502",
},
AdminAccessLogPath: "/dev/null",
AdminBindAddress: "127.0.0.1",
AdminBindPort: "19000",
LocalAgentClusterName: xds.LocalAgentClusterName,
PrometheusScrapePath: "/metrics",
},
},
{
Name: "stats-config-override",
Flags: []string{"-proxy-id", "test-proxy"},
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",
// We don't know this til after the lookup so it will be empty in the
// initial args call we are testing here.
ProxySourceService: "",
GRPC: GRPC{
AgentAddress: "127.0.0.1",
AgentPort: "8502",
},
AdminAccessLogPath: "/dev/null",
AdminBindAddress: "127.0.0.1",
AdminBindPort: "19000",
LocalAgentClusterName: xds.LocalAgentClusterName,
PrometheusScrapePath: "/metrics",
},
},
{
Name: "zipkin-tracing-config",
Flags: []string{"-proxy-id", "test-proxy"},
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",
// We don't know this til after the lookup so it will be empty in the
// initial args call we are testing here.
ProxySourceService: "",
GRPC: GRPC{
AgentAddress: "127.0.0.1",
AgentPort: "8502",
},
AdminAccessLogPath: "/dev/null",
AdminBindAddress: "127.0.0.1",
AdminBindPort: "19000",
LocalAgentClusterName: xds.LocalAgentClusterName,
PrometheusScrapePath: "/metrics",
},
},
{
Name: "CONSUL_HTTP_ADDR-with-https-scheme-does-not-affect-grpc-tls",
Flags: []string{"-proxy-id", "test-proxy", "-ca-file", "../../../test/ca/root.cer"},
Env: []string{"CONSUL_HTTP_ADDR=https://127.0.0.1:8500"},
WantArgs: BootstrapTplArgs{
ProxyCluster: "test-proxy",
ProxyID: "test-proxy",
// We don't know this til after the lookup so it will be empty in the
// initial args call we are testing here.
ProxySourceService: "",
// Should resolve IP, note this might not resolve the same way
// everywhere which might make this test brittle but not sure what else
// to do.
GRPC: GRPC{
AgentAddress: "127.0.0.1",
AgentPort: "8502",
AgentTLS: false,
},
AdminAccessLogPath: "/dev/null",
AdminBindAddress: "127.0.0.1",
AdminBindPort: "19000",
LocalAgentClusterName: xds.LocalAgentClusterName,
PrometheusScrapePath: "/metrics",
},
},
{
Name: "CONSUL_GRPC_ADDR-with-https-scheme-enables-tls",
Flags: []string{"-proxy-id", "test-proxy", "-ca-file", "../../../test/ca/root.cer"},
Env: []string{"CONSUL_GRPC_ADDR=https://127.0.0.1:8502"},
WantArgs: BootstrapTplArgs{
ProxyCluster: "test-proxy",
ProxyID: "test-proxy",
// We don't know this til after the lookup so it will be empty in the
// initial args call we are testing here.
ProxySourceService: "",
// Should resolve IP, note this might not resolve the same way
// everywhere which might make this test brittle but not sure what else
// to do.
GRPC: GRPC{
AgentAddress: "127.0.0.1",
AgentPort: "8502",
AgentTLS: true,
},
AgentCAPEM: rootPEM,
AdminAccessLogPath: "/dev/null",
AdminBindAddress: "127.0.0.1",
AdminBindPort: "19000",
LocalAgentClusterName: xds.LocalAgentClusterName,
PrometheusScrapePath: "/metrics",
},
},
{
Name: "both-CONSUL_HTTP_ADDR-TLS-and-CONSUL_GRPC_ADDR-PLAIN-is-plain",
Flags: []string{"-proxy-id", "test-proxy", "-ca-file", "../../../test/ca/root.cer"},
Env: []string{
"CONSUL_HTTP_ADDR=https://127.0.0.1:8500",
"CONSUL_GRPC_ADDR=http://127.0.0.1:8502",
},
WantArgs: BootstrapTplArgs{
ProxyCluster: "test-proxy",
ProxyID: "test-proxy",
// We don't know this til after the lookup so it will be empty in the
// initial args call we are testing here.
ProxySourceService: "",
// Should resolve IP, note this might not resolve the same way
// everywhere which might make this test brittle but not sure what else
// to do.
GRPC: GRPC{
AgentAddress: "127.0.0.1",
AgentPort: "8502",
AgentTLS: false,
},
AdminAccessLogPath: "/dev/null",
AdminBindAddress: "127.0.0.1",
AdminBindPort: "19000",
LocalAgentClusterName: xds.LocalAgentClusterName,
PrometheusScrapePath: "/metrics",
},
},
{
Name: "both-CONSUL_HTTP_ADDR-PLAIN-and-CONSUL_GRPC_ADDR-TLS-is-tls",
Flags: []string{"-proxy-id", "test-proxy", "-ca-file", "../../../test/ca/root.cer"},
Env: []string{
"CONSUL_HTTP_ADDR=http://127.0.0.1:8500",
"CONSUL_GRPC_ADDR=https://127.0.0.1:8502",
},
WantArgs: BootstrapTplArgs{
ProxyCluster: "test-proxy",
ProxyID: "test-proxy",
// We don't know this til after the lookup so it will be empty in the
// initial args call we are testing here.
ProxySourceService: "",
// Should resolve IP, note this might not resolve the same way
// everywhere which might make this test brittle but not sure what else
// to do.
GRPC: GRPC{
AgentAddress: "127.0.0.1",
AgentPort: "8502",
AgentTLS: true,
},
AgentCAPEM: rootPEM,
AdminAccessLogPath: "/dev/null",
AdminBindAddress: "127.0.0.1",
AdminBindPort: "19000",
LocalAgentClusterName: xds.LocalAgentClusterName,
PrometheusScrapePath: "/metrics",
},
},
{
Name: "ingress-gateway",
Flags: []string{"-proxy-id", "ingress-gateway-1", "-gateway", "ingress"},
WantArgs: BootstrapTplArgs{
ProxyCluster: "ingress-gateway",
ProxyID: "ingress-gateway-1",
ProxySourceService: "ingress-gateway",
GRPC: GRPC{
AgentAddress: "127.0.0.1",
AgentPort: "8502",
},
AdminAccessLogPath: "/dev/null",
AdminBindAddress: "127.0.0.1",
AdminBindPort: "19000",
LocalAgentClusterName: xds.LocalAgentClusterName,
PrometheusScrapePath: "/metrics",
},
},
{
Name: "ingress-gateway-nodemeta",
Flags: []string{"-proxy-id", "ingress-gateway-1", "-node-name", "test-node"},
WantArgs: BootstrapTplArgs{
ProxyCluster: "ingress-gateway-1",
ProxyID: "ingress-gateway-1",
NodeName: "test-node",
GRPC: GRPC{
AgentAddress: "127.0.0.1",
AgentPort: "8502",
},
AdminAccessLogPath: "/dev/null",
AdminBindAddress: "127.0.0.1",
AdminBindPort: "19000",
LocalAgentClusterName: xds.LocalAgentClusterName,
PrometheusScrapePath: "/metrics",
},
},
{
Name: "envoy-readiness-probe",
Flags: []string{"-proxy-id", "test-proxy",
"-envoy-ready-bind-address", "127.0.0.1", "-envoy-ready-bind-port", "21000"},
WantArgs: BootstrapTplArgs{
ProxyCluster: "test-proxy",
ProxyID: "test-proxy",
// We don't know this til after the lookup so it will be empty in the
// initial args call we are testing here.
ProxySourceService: "",
GRPC: GRPC{
AgentAddress: "127.0.0.1",
AgentPort: "8502", // Note this is the gRPC port
},
AdminAccessLogPath: "/dev/null",
AdminBindAddress: "127.0.0.1",
AdminBindPort: "19000",
LocalAgentClusterName: xds.LocalAgentClusterName,
PrometheusBackendPort: "",
PrometheusScrapePath: "/metrics",
},
},
{
Name: "ingress-gateway-address-specified",
Flags: []string{"-proxy-id", "ingress-gateway", "-gateway", "ingress", "-address", "1.2.3.4:7777"},
WantArgs: BootstrapTplArgs{
ProxyCluster: "ingress-gateway",
ProxyID: "ingress-gateway",
ProxySourceService: "ingress-gateway",
GRPC: GRPC{
AgentAddress: "127.0.0.1",
AgentPort: "8502",
},
AdminAccessLogPath: "/dev/null",
AdminBindAddress: "127.0.0.1",
AdminBindPort: "19000",
LocalAgentClusterName: xds.LocalAgentClusterName,
PrometheusScrapePath: "/metrics",
},
},
{
Name: "ingress-gateway-register-with-service-without-proxy-id",
Flags: []string{"-gateway", "ingress", "-register", "-service", "my-gateway", "-address", "127.0.0.1:7777"},
WantArgs: BootstrapTplArgs{
ProxyCluster: "my-gateway",
ProxyID: "my-gateway",
ProxySourceService: "my-gateway",
GRPC: GRPC{
AgentAddress: "127.0.0.1",
AgentPort: "8502",
},
AdminAccessLogPath: "/dev/null",
AdminBindAddress: "127.0.0.1",
AdminBindPort: "19000",
LocalAgentClusterName: xds.LocalAgentClusterName,
PrometheusScrapePath: "/metrics",
},
},
{
Name: "ingress-gateway-register-with-service-and-proxy-id",
Flags: []string{"-gateway", "ingress", "-register", "-service", "my-gateway", "-proxy-id", "my-gateway-123", "-address", "127.0.0.1:7777"},
WantArgs: BootstrapTplArgs{
ProxyCluster: "my-gateway",
ProxyID: "my-gateway-123",
ProxySourceService: "my-gateway",
GRPC: GRPC{
AgentAddress: "127.0.0.1",
AgentPort: "8502",
},
AdminAccessLogPath: "/dev/null",
AdminBindAddress: "127.0.0.1",
AdminBindPort: "19000",
LocalAgentClusterName: xds.LocalAgentClusterName,
PrometheusScrapePath: "/metrics",
},
},
{
Name: "ingress-gateway-no-auto-register",
Flags: []string{"-gateway", "ingress", "-address", "127.0.0.1:7777"},
WantArgs: BootstrapTplArgs{
ProxyCluster: "ingress-gateway",
ProxyID: "ingress-gateway",
ProxySourceService: "ingress-gateway",
GRPC: GRPC{
AgentAddress: "127.0.0.1",
AgentPort: "8502",
},
AdminAccessLogPath: "/dev/null",
AdminBindAddress: "127.0.0.1",
AdminBindPort: "19000",
LocalAgentClusterName: xds.LocalAgentClusterName,
PrometheusScrapePath: "/metrics",
},
},
{
Name: "access-logs-enabled",
Flags: []string{"-proxy-id", "test-proxy"},
WantArgs: BootstrapTplArgs{
ProxyCluster: "test-proxy",
ProxyID: "test-proxy",
ProxySourceService: "",
GRPC: GRPC{
AgentAddress: "127.0.0.1",
AgentPort: "8502",
},
AdminAccessLogPath: "/dev/null",
AdminBindAddress: "127.0.0.1",
AdminBindPort: "19000",
LocalAgentClusterName: xds.LocalAgentClusterName,
PrometheusBackendPort: "",
PrometheusScrapePath: "/metrics",
},
ProxyDefaults: api.ProxyConfigEntry{
AccessLogs: &api.AccessLogsConfig{
Enabled: true,
},
},
},
{
Name: "access-logs-enabled-custom",
Flags: []string{"-proxy-id", "test-proxy"},
WantArgs: BootstrapTplArgs{
ProxyCluster: "test-proxy",
ProxyID: "test-proxy",
ProxySourceService: "",
GRPC: GRPC{
AgentAddress: "127.0.0.1",
AgentPort: "8502",
},
AdminAccessLogPath: "/dev/null",
AdminBindAddress: "127.0.0.1",
AdminBindPort: "19000",
LocalAgentClusterName: xds.LocalAgentClusterName,
PrometheusBackendPort: "",
PrometheusScrapePath: "/metrics",
},
ProxyDefaults: api.ProxyConfigEntry{
AccessLogs: &api.AccessLogsConfig{
Enabled: true,
DisableListenerLogs: true, // Should have no effect here
Type: api.FileLogSinkType,
Path: "/var/log/consul.log",
TextFormat: "MY START TIME %START_TIME%",
},
},
},
{
Name: "acl-enabled-but-no-token",
Flags: []string{"-proxy-id", "test-proxy"},
ACLEnabled: true,
WantWarn: "No ACL token was provided to Envoy.",
WantArgs: BootstrapTplArgs{
ProxyCluster: "test-proxy",
ProxyID: "test-proxy",
// We don't know this til after the lookup so it will be empty in the
// initial args call we are testing here.
ProxySourceService: "",
GRPC: GRPC{
AgentAddress: "127.0.0.1",
AgentPort: "8502", // Note this is the gRPC port
},
AdminAccessLogPath: "/dev/null",
AdminBindAddress: "127.0.0.1",
AdminBindPort: "19000",
LocalAgentClusterName: xds.LocalAgentClusterName,
PrometheusBackendPort: "",
PrometheusScrapePath: "/metrics",
},
},
{
Name: "acl-enabled-and-token",
Flags: []string{"-proxy-id", "test-proxy", "-token", "foo"},
ACLEnabled: true,
WantArgs: BootstrapTplArgs{
ProxyCluster: "test-proxy",
ProxyID: "test-proxy",
// We don't know this til after the lookup so it will be empty in the
// initial args call we are testing here.
ProxySourceService: "",
GRPC: GRPC{
AgentAddress: "127.0.0.1",
AgentPort: "8502", // Note this is the gRPC port
},
AdminAccessLogPath: "/dev/null",
AdminBindAddress: "127.0.0.1",
AdminBindPort: "19000",
Token: "foo",
LocalAgentClusterName: xds.LocalAgentClusterName,
PrometheusBackendPort: "",
PrometheusScrapePath: "/metrics",
},
},
}
cases = append(cases, enterpriseGenerateConfigTestCases()...)
copyAndReplaceAll := func(s []string, old, new string) []string {
out := make([]string, len(s))
for i, v := range s {
out[i] = strings.ReplaceAll(v, old, new)
}
return out
}
for _, tc := range cases {
t.Run(tc.Name, func(t *testing.T) {
testDir := testutil.TempDir(t, "envoytest")
if len(tc.Files) > 0 {
for fn, fv := range tc.Files {
fullname := filepath.Join(testDir, fn)
require.NoError(t, os.WriteFile(fullname, []byte(fv), 0600))
}
}
// Default the ports
if tc.XDSPorts.TLS == 0 && tc.XDSPorts.Plaintext == 0 {
tc.XDSPorts.Plaintext = 8502
}
// Run a mock agent API that just always returns the proxy config in the
// test.
2021-09-15 23:22:22 +00:00
var srv *httptest.Server
if tc.TLSServer {
srv = httptest.NewTLSServer(testMockAgent(tc))
} else {
srv = httptest.NewServer(testMockAgent(tc))
}
defer srv.Close()
testDirPrefix := testDir + string(filepath.Separator)
myEnv := copyAndReplaceAll(tc.Env, "@@TEMPDIR@@", testDirPrefix)
defer testSetAndResetEnv(t, myEnv)()
2021-09-15 23:22:22 +00:00
client, err := api.NewClient(&api.Config{Address: srv.URL, TLSConfig: api.TLSConfig{InsecureSkipVerify: true}})
require.NoError(t, err)
2021-09-15 23:22:22 +00:00
ui := cli.NewMockUi()
c := New(ui)
// explicitly set the client to one which can connect to the httptest.Server
c.client = client
c.dialFunc = func(_, _ string) (net.Conn, error) {
return nil, nil
}
// Run the command
myFlags := copyAndReplaceAll(tc.Flags, "@@TEMPDIR@@", testDirPrefix)
args := append([]string{"-bootstrap"}, myFlags...)
require.NoError(t, c.flags.Parse(args))
code := c.run(c.flags.Args())
if tc.WantErr != "" {
require.Equal(t, 1, code, ui.ErrorWriter.String())
require.Contains(t, ui.ErrorWriter.String(), tc.WantErr)
return
} else if tc.WantWarn != "" {
require.Equal(t, 0, code, ui.ErrorWriter.String())
require.Contains(t, ui.ErrorWriter.String(), tc.WantWarn)
} else {
require.Equal(t, 0, code, ui.ErrorWriter.String())
require.Empty(t, ui.ErrorWriter.String())
}
// Verify we handled the env and flags right first to get correct template
// args.
got, err := c.templateArgs()
require.NoError(t, err) // Error cases should have returned above
require.Equal(t, &tc.WantArgs, got)
actual := ui.OutputWriter.Bytes()
// If we got the arg handling write, verify output
golden := filepath.Join("testdata", tc.Name+".golden")
if *update {
os.WriteFile(golden, actual, 0644)
}
expected, err := os.ReadFile(golden)
require.NoError(t, err)
require.Equal(t, string(expected), string(actual))
})
}
}
func TestEnvoy_GatewayRegistration(t *testing.T) {
if testing.Short() {
t.Skip("too slow for testing.Short")
}
t.Parallel()
a := agent.NewTestAgent(t, ``)
defer a.Shutdown()
client := a.Client()
tt := []struct {
name string
args []string
kind api.ServiceKind
id string
service string
}{
{
name: "register gateway with proxy-id and name",
args: []string{
"-http-addr=" + a.HTTPAddr(),
"-register",
"-bootstrap",
"-gateway", "ingress",
"-service", "us-ingress",
"-proxy-id", "us-ingress-1",
},
kind: api.ServiceKindIngressGateway,
id: "us-ingress-1",
service: "us-ingress",
},
{
name: "register gateway without proxy-id with name",
args: []string{
"-http-addr=" + a.HTTPAddr(),
"-register",
"-bootstrap",
"-gateway", "ingress",
"-service", "us-ingress",
},
kind: api.ServiceKindIngressGateway,
id: "us-ingress",
service: "us-ingress",
},
{
name: "register gateway without proxy-id and without name",
args: []string{
"-http-addr=" + a.HTTPAddr(),
"-register",
"-bootstrap",
"-gateway", "ingress",
},
kind: api.ServiceKindIngressGateway,
id: "ingress-gateway",
service: "ingress-gateway",
},
{
name: "register gateway with proxy-id without name",
args: []string{
"-http-addr=" + a.HTTPAddr(),
"-register",
"-bootstrap",
"-gateway", "ingress",
"-proxy-id", "us-ingress-1",
},
kind: api.ServiceKindIngressGateway,
id: "us-ingress-1",
service: "ingress-gateway",
},
}
for _, tc := range tt {
t.Run(tc.name, func(t *testing.T) {
ui := cli.NewMockUi()
c := New(ui)
code := c.Run(tc.args)
if code != 0 {
t.Fatalf("bad exit code: %d. %#v", code, ui.ErrorWriter.String())
}
data, _, err := client.Agent().Service(tc.id, nil)
assert.NoError(t, err)
assert.NotNil(t, data)
assert.Equal(t, tc.kind, data.Kind)
assert.Equal(t, tc.id, data.ID)
assert.Equal(t, tc.service, data.Service)
assert.Equal(t, defaultGatewayPort, data.Port)
})
}
}
func TestEnvoy_proxyRegistration(t *testing.T) {
t.Parallel()
type args struct {
svcForProxy api.AgentService
cmdFn func(*cmd)
}
cases := []struct {
name string
args args
testFn func(*testing.T, args, *api.AgentServiceRegistration)
}{
{
"locality is inherited from proxied service if configured and using sidecarFor",
args{
svcForProxy: api.AgentService{
ID: "my-svc",
Locality: &api.Locality{
Region: "us-east-1",
Zone: "us-east-1a",
},
},
cmdFn: func(c *cmd) {
c.sidecarFor = "my-svc"
},
},
func(t *testing.T, args args, r *api.AgentServiceRegistration) {
assert.NotNil(t, r.Locality)
assert.Equal(t, args.svcForProxy.Locality, r.Locality)
},
},
{
"locality is not inherited if not using sidecarFor",
args{
svcForProxy: api.AgentService{
ID: "my-svc",
Locality: &api.Locality{
Region: "us-east-1",
Zone: "us-east-1a",
},
},
},
func(t *testing.T, args args, r *api.AgentServiceRegistration) {
assert.Nil(t, r.Locality)
},
},
{
"locality is not set if not configured for proxied service",
args{
svcForProxy: api.AgentService{},
cmdFn: func(c *cmd) {
c.sidecarFor = "my-svc"
},
},
func(t *testing.T, args args, r *api.AgentServiceRegistration) {
assert.Nil(t, r.Locality)
},
},
}
for _, tc := range cases {
t.Run(tc.name, func(t *testing.T) {
ui := cli.NewMockUi()
c := New(ui)
if tc.args.cmdFn != nil {
tc.args.cmdFn(c)
}
result, err := c.proxyRegistration(&tc.args.svcForProxy)
assert.NoError(t, err)
tc.testFn(t, tc.args, result)
})
}
}
// testMockAgent combines testMockAgentProxyConfig and testMockAgentSelf,
// routing /agent/service/... requests to testMockAgentProxyConfig,
// routing /catalog/node-services/... requests to testMockCatalogNodeServiceList
// routing /agent/self requests to testMockAgentSelf.
func testMockAgent(tc generateConfigTestCase) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
switch {
case strings.Contains(r.URL.Path, "/agent/services"):
testMockAgentGatewayConfig(tc.NamespacesEnabled)(w, r)
case strings.Contains(r.URL.Path, "/agent/service"):
testMockAgentProxyConfig(tc.ProxyConfig, tc.NamespacesEnabled)(w, r)
case strings.Contains(r.URL.Path, "/agent/self"):
testMockAgentSelf(tc.XDSPorts, tc.AgentSelf110, tc.GRPCDisabled)(w, r)
case strings.Contains(r.URL.Path, "/catalog/node-services"):
testMockCatalogNodeServiceList()(w, r)
case strings.Contains(r.URL.Path, "/config/proxy-defaults/global"):
testMockConfigProxyDefaults(tc.ProxyDefaults)(w, r)
case strings.Contains(r.URL.Path, "/acl/token/self"):
testMockTokenReadSelf(tc.ACLEnabled, tc.Flags)(w, r)
default:
http.NotFound(w, r)
}
}
}
func testMockAgentGatewayConfig(namespacesEnabled bool) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
// Parse the proxy-id from the end of the URL (blindly assuming it's correct
// format)
params := r.URL.Query()
filter := params["filter"][0]
var kind api.ServiceKind
switch {
case strings.Contains(filter, string(api.ServiceKindTerminatingGateway)):
kind = api.ServiceKindTerminatingGateway
case strings.Contains(filter, string(api.ServiceKindIngressGateway)):
kind = api.ServiceKindIngressGateway
}
svc := map[string]*api.AgentService{
string(kind): {
Kind: kind,
ID: string(kind),
Service: string(kind),
Datacenter: "dc1",
},
}
if namespacesEnabled {
svc[string(kind)].Namespace = namespaceFromQuery(r)
2021-09-15 23:22:22 +00:00
svc[string(kind)].Partition = partitionFromQuery(r)
}
cfgJSON, err := json.Marshal(svc)
if err != nil {
w.WriteHeader(500)
w.Write([]byte(err.Error()))
return
}
w.Write(cfgJSON)
}
}
func namespaceFromQuery(r *http.Request) string {
// Use the namespace in the request if there is one, otherwise
// use-default.
if queryNamespace := r.URL.Query().Get("namespace"); queryNamespace != "" {
return queryNamespace
}
if queryNs := r.URL.Query().Get("ns"); queryNs != "" {
return queryNs
}
return "default"
}
2021-09-15 23:22:22 +00:00
func partitionFromQuery(r *http.Request) string {
// Use the partition in the request if there is one, otherwise
// use-default.
if queryPartition := r.URL.Query().Get("partition"); queryPartition != "" {
return queryPartition
}
if queryAp := r.URL.Query().Get("ap"); queryAp != "" {
return queryAp
2021-09-15 23:22:22 +00:00
}
return "default"
}
func testMockAgentProxyConfig(cfg map[string]interface{}, namespacesEnabled bool) http.HandlerFunc {
return 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, "-proxy")
svc := api.AgentService{
Kind: api.ServiceKindConnectProxy,
ID: proxyID,
Service: proxyID,
Proxy: &api.AgentServiceConnectProxyConfig{
DestinationServiceName: serviceID,
DestinationServiceID: serviceID,
Config: cfg,
},
Datacenter: "dc1",
}
if namespacesEnabled {
svc.Namespace = namespaceFromQuery(r)
2021-09-15 23:22:22 +00:00
svc.Partition = partitionFromQuery(r)
}
cfgJSON, err := json.Marshal(svc)
if err != nil {
w.WriteHeader(500)
w.Write([]byte(err.Error()))
return
}
w.Write(cfgJSON)
}
}
func testMockCatalogNodeServiceList() http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
quotedProxyID := strings.TrimPrefix(r.URL.Query().Get("filter"), "ID == ")
proxyID := quotedProxyID[1 : len(quotedProxyID)-1]
serviceID := strings.TrimSuffix(proxyID, "-proxy")
var svcKind api.ServiceKind
if strings.Contains(proxyID, "ingress-gateway") {
svcKind = api.ServiceKindIngressGateway
} else {
svcKind = api.ServiceKindConnectProxy
}
var svcProxy api.AgentServiceConnectProxyConfig
if svcKind == api.ServiceKindConnectProxy {
svcProxy = api.AgentServiceConnectProxyConfig{
DestinationServiceName: serviceID,
DestinationServiceID: serviceID,
}
}
svc := api.AgentService{
Kind: svcKind,
ID: proxyID,
Service: proxyID,
Proxy: &svcProxy,
}
nodeSvc := api.CatalogNodeServiceList{
Node: &api.Node{Datacenter: "dc1"},
Services: []*api.AgentService{&svc},
}
cfgJSON, err := json.Marshal(nodeSvc)
if err != nil {
w.WriteHeader(500)
w.Write([]byte(err.Error()))
return
}
w.Write(cfgJSON)
}
}
func testMockConfigProxyDefaults(entry api.ProxyConfigEntry) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
cfgJSON, err := json.Marshal(entry)
if err != nil {
w.WriteHeader(500)
w.Write([]byte(err.Error()))
return
}
w.Write(cfgJSON)
}
}
func testMockTokenReadSelf(aclEnabled bool, flags []string) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
if aclEnabled {
for _, f := range flags {
if f == "-token" {
w.WriteHeader(200)
return
}
}
w.WriteHeader(403)
w.Write([]byte(acl.ErrNotFound.Error()))
return
}
}
}
func TestEnvoyCommand_canBindInternal(t *testing.T) {
t.Parallel()
type testCheck struct {
expected bool
addr string
}
type testCase struct {
ifAddrs []net.Addr
checks map[string]testCheck
}
parseIPNets := func(t *testing.T, in ...string) []net.Addr {
var out []net.Addr
for _, addr := range in {
ip := net.ParseIP(addr)
require.NotNil(t, ip)
out = append(out, &net.IPNet{IP: ip})
}
return out
}
parseIPs := func(t *testing.T, in ...string) []net.Addr {
var out []net.Addr
for _, addr := range in {
ip := net.ParseIP(addr)
require.NotNil(t, ip)
out = append(out, &net.IPAddr{IP: ip})
}
return out
}
cases := map[string]testCase{
"IPNet": {
parseIPNets(t, "10.3.0.2", "198.18.0.1", "2001:db8:a0b:12f0::1"),
map[string]testCheck{
"ipv4": {
true,
"10.3.0.2",
},
"secondary ipv4": {
true,
"198.18.0.1",
},
"ipv6": {
true,
"2001:db8:a0b:12f0::1",
},
"ipv4 not found": {
false,
"1.2.3.4",
},
"ipv6 not found": {
false,
"::ffff:192.168.0.1",
},
},
},
"IPAddr": {
parseIPs(t, "10.3.0.2", "198.18.0.1", "2001:db8:a0b:12f0::1"),
map[string]testCheck{
"ipv4": {
true,
"10.3.0.2",
},
"secondary ipv4": {
true,
"198.18.0.1",
},
"ipv6": {
true,
"2001:db8:a0b:12f0::1",
},
"ipv4 not found": {
false,
"1.2.3.4",
},
"ipv6 not found": {
false,
"::ffff:192.168.0.1",
},
},
},
}
for name, tcase := range cases {
t.Run(name, func(t *testing.T) {
for checkName, check := range tcase.checks {
t.Run(checkName, func(t *testing.T) {
require.Equal(t, check.expected, canBindInternal(check.addr, tcase.ifAddrs))
})
}
})
}
}
// testMockAgentSelf returns an empty /v1/agent/self response except GRPC
// port is filled in to match the given wantXDSPort argument.
func testMockAgentSelf(
wantXDSPorts agent.GRPCPorts,
agentSelf110 bool,
grpcDisabled bool,
) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
resp := agent.Self{
Config: map[string]interface{}{
"Datacenter": "dc1",
},
}
if agentSelf110 {
resp.DebugConfig = map[string]interface{}{
2022-09-01 17:32:11 +00:00
"GRPCPort": wantXDSPorts.Plaintext,
}
} else if grpcDisabled {
resp.DebugConfig = map[string]interface{}{
"GRPCPort": -1,
}
// the real agent does not populate XDS if grpc or
// grpc-tls ports are < 0
} else {
2022-09-01 17:32:11 +00:00
resp.XDS = &agent.XDSSelf{
// The deprecated Port field should default to TLS if it's available.
Port: wantXDSPorts.TLS,
Ports: wantXDSPorts,
}
if wantXDSPorts.TLS <= 0 {
resp.XDS.Port = wantXDSPorts.Plaintext
}
}
selfJSON, err := json.Marshal(resp)
if err != nil {
w.WriteHeader(500)
w.Write([]byte(err.Error()))
return
}
w.Write(selfJSON)
}
}
func TestCheckEnvoyVersionCompatibility(t *testing.T) {
tests := []struct {
name string
envoyVersion string
unsupportedList []string
expectedCompat envoyCompat
isErrorExpected bool
}{
{
name: "supported-using-proxy-support-defined",
envoyVersion: xdscommon.EnvoyVersions[1],
unsupportedList: xdscommon.UnsupportedEnvoyVersions,
expectedCompat: envoyCompat{
isCompatible: true,
},
},
{
name: "supported-at-max",
envoyVersion: xdscommon.GetMaxEnvoyMinorVersion(),
unsupportedList: xdscommon.UnsupportedEnvoyVersions,
expectedCompat: envoyCompat{
isCompatible: true,
},
},
{
name: "supported-patch-higher",
envoyVersion: addNPatchVersion(xdscommon.EnvoyVersions[0], 1),
unsupportedList: xdscommon.UnsupportedEnvoyVersions,
expectedCompat: envoyCompat{
isCompatible: true,
},
},
{
name: "not-supported-minor-higher",
envoyVersion: addNMinorVersion(xdscommon.EnvoyVersions[0], 1),
unsupportedList: xdscommon.UnsupportedEnvoyVersions,
expectedCompat: envoyCompat{
isCompatible: false,
versionIncompatible: replacePatchVersionWithX(addNMinorVersion(xdscommon.EnvoyVersions[0], 1)),
},
},
{
name: "not-supported-minor-lower",
envoyVersion: addNMinorVersion(xdscommon.EnvoyVersions[len(xdscommon.EnvoyVersions)-1], -1),
unsupportedList: xdscommon.UnsupportedEnvoyVersions,
expectedCompat: envoyCompat{
isCompatible: false,
versionIncompatible: replacePatchVersionWithX(addNMinorVersion(xdscommon.EnvoyVersions[len(xdscommon.EnvoyVersions)-1], -1)),
},
},
{
name: "not-supported-explicitly-unsupported-version",
envoyVersion: addNPatchVersion(xdscommon.EnvoyVersions[0], 1),
unsupportedList: []string{"1.23.1", addNPatchVersion(xdscommon.EnvoyVersions[0], 1)},
expectedCompat: envoyCompat{
isCompatible: false,
versionIncompatible: addNPatchVersion(xdscommon.EnvoyVersions[0], 1),
},
},
{
name: "error-bad-input",
envoyVersion: "1.abc.3",
unsupportedList: xdscommon.UnsupportedEnvoyVersions,
expectedCompat: envoyCompat{},
isErrorExpected: true,
},
}
for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
actual, err := checkEnvoyVersionCompatibility(tc.envoyVersion, tc.unsupportedList)
if tc.isErrorExpected {
assert.Error(t, err)
} else {
assert.NoError(t, err)
}
assert.Equal(t, tc.expectedCompat, actual)
})
}
}
func addNPatchVersion(s string, n int) string {
splitS := strings.Split(s, ".")
minor, _ := strconv.Atoi(splitS[2])
minor += n
return fmt.Sprintf("%s.%s.%d", splitS[0], splitS[1], minor)
}
func addNMinorVersion(s string, n int) string {
splitS := strings.Split(s, ".")
major, _ := strconv.Atoi(splitS[1])
major += n
return fmt.Sprintf("%s.%d.%s", splitS[0], major, splitS[2])
}