diff --git a/connect-inject/envoy_sidecar.go b/connect-inject/envoy_sidecar.go index 2fcbdfc6f4..ee1cef2a55 100644 --- a/connect-inject/envoy_sidecar.go +++ b/connect-inject/envoy_sidecar.go @@ -3,9 +3,11 @@ package connectinject import ( "bytes" "fmt" + "strconv" "strings" "text/template" + "github.com/google/shlex" corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/resource" ) @@ -35,6 +37,11 @@ func (h *Handler) envoySidecar(pod *corev1.Pod, k8sNamespace string) (corev1.Con return corev1.Container{}, err } + cmd, err := h.getContainerSidecarCommand() + if err != nil { + return corev1.Container{}, err + } + container := corev1.Container{ Name: "consul-connect-envoy-sidecar", Image: h.ImageEnvoy, @@ -64,10 +71,7 @@ func (h *Handler) envoySidecar(pod *corev1.Pod, k8sNamespace string) (corev1.Con }, }, }, - Command: []string{ - "envoy", - "--config-path", "/consul/connect-inject/envoy-bootstrap.yaml", - }, + Command: cmd, } if h.ConsulCACert != "" { caCertEnvVar := corev1.EnvVar{ @@ -88,6 +92,27 @@ func (h *Handler) envoySidecar(pod *corev1.Pod, k8sNamespace string) (corev1.Con return container, nil } +func (h *Handler) getContainerSidecarCommand() ([]string, error) { + cmd := []string{ + "envoy", + "--config-path", "/consul/connect-inject/envoy-bootstrap.yaml", + } + + if h.ExtraEnvoyOpts != "" { + tokens, err := shlex.Split(h.ExtraEnvoyOpts) + if err != nil { + return []string{}, err + } + for _, t := range tokens { + if strings.Contains(t, " ") { + t = strconv.Quote(t) + } + cmd = append(cmd, t) + } + } + return cmd, nil +} + func (h *Handler) envoySidecarResources(pod *corev1.Pod) (corev1.ResourceRequirements, error) { resources := corev1.ResourceRequirements{ Limits: corev1.ResourceList{}, diff --git a/connect-inject/envoy_sidecar_test.go b/connect-inject/envoy_sidecar_test.go index ed775abd48..9ff50dc41b 100644 --- a/connect-inject/envoy_sidecar_test.go +++ b/connect-inject/envoy_sidecar_test.go @@ -60,6 +60,57 @@ func TestHandlerEnvoySidecar(t *testing.T) { }) } +// Test that we can pass extra flags to envoy. +func TestHandlerEnvoySidecar_ExtraEnvoyFlags(t *testing.T) { + cases := []struct { + name string + extraEnvoyOpts string + expectedContainerCommand []string + }{ + { + name: "no extra options provided", + extraEnvoyOpts: "", + expectedContainerCommand: []string{ + "envoy", + "--config-path", "/consul/connect-inject/envoy-bootstrap.yaml", + }, + }, + { + name: "extra log-level option", + extraEnvoyOpts: "--log-level debug", + expectedContainerCommand: []string{ + "envoy", + "--config-path", "/consul/connect-inject/envoy-bootstrap.yaml", + "--log-level", "debug", + }, + }, + { + name: "extraEnvoyOpts with quotes inside", + extraEnvoyOpts: "--log-level debug --admin-address-path \"/tmp/consul/foo bar\"", + expectedContainerCommand: []string{ + "envoy", + "--config-path", "/consul/connect-inject/envoy-bootstrap.yaml", + "--log-level", "debug", + "--admin-address-path", "\"/tmp/consul/foo bar\"", + }, + }, + } + + for _, tc := range cases { + t.Run(tc.name, func(t *testing.T) { + h := Handler{ + ImageConsul: "hashicorp/consul:latest", + ImageEnvoy: "hashicorp/consul-k8s:latest", + ExtraEnvoyOpts: tc.extraEnvoyOpts, + } + + c, err := h.envoySidecar(&corev1.Pod{}, "") + require.NoError(t, err) + require.Equal(t, tc.expectedContainerCommand, c.Command) + }) + } +} + // Test that if AuthMethod is set // the preStop command includes a token func TestHandlerEnvoySidecar_AuthMethod(t *testing.T) { diff --git a/connect-inject/handler.go b/connect-inject/handler.go index d1c010f249..59d3a01eba 100644 --- a/connect-inject/handler.go +++ b/connect-inject/handler.go @@ -113,6 +113,10 @@ type Handler struct { // This image is used for the lifecycle-sidecar container. ImageConsulK8S string + // Optional: set when you need extra options to be set when running envoy + // See a list of args here: https://www.envoyproxy.io/docs/envoy/latest/operations/cli + ExtraEnvoyOpts string + // RequireAnnotation means that the annotation must be given to inject. // If this is false, injection is default. RequireAnnotation bool diff --git a/go.mod b/go.mod index e90903a575..1cce26878e 100644 --- a/go.mod +++ b/go.mod @@ -8,6 +8,7 @@ require ( github.com/go-logr/logr v0.1.0 github.com/google/go-cmp v0.4.0 github.com/google/go-querystring v1.0.0 // indirect + github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 github.com/hashicorp/consul/api v1.4.1-0.20201007080954-aa0f5ff839c5 github.com/hashicorp/consul/sdk v0.6.0 github.com/hashicorp/go-discover v0.0.0-20200812215701-c4b85f6ed31f diff --git a/go.sum b/go.sum index 55cdff5c07..f3cc30c2df 100644 --- a/go.sum +++ b/go.sum @@ -229,6 +229,8 @@ github.com/google/gofuzz v1.1.0 h1:Hsa8mG0dQ46ij8Sl2AYJDUv1oA9/d6Vk+3LG99Oe02g= github.com/google/gofuzz v1.1.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= +github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4= +github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ= github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY= github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= diff --git a/subcommand/inject-connect/command.go b/subcommand/inject-connect/command.go index 41f50517ab..0a329f5af3 100644 --- a/subcommand/inject-connect/command.go +++ b/subcommand/inject-connect/command.go @@ -47,6 +47,7 @@ type Command struct { flagWriteServiceDefaults bool // True to enable central config injection flagDefaultProtocol string // Default protocol for use with central config flagConsulCACert string // [Deprecated] Path to CA Certificate to use when communicating with Consul clients + flagEnvoyExtraArgs string // Extra envoy args when starting envoy // Flags to support namespaces flagEnableNamespaces bool // Use namespacing on all components @@ -109,6 +110,8 @@ func (c *Command) init() { "Docker image for Envoy. Defaults to envoyproxy/envoy-alpine:v1.13.0.") c.flagSet.StringVar(&c.flagConsulK8sImage, "consul-k8s-image", "", "Docker image for consul-k8s. Used for the connect sidecar.") + c.flagSet.StringVar(&c.flagEnvoyExtraArgs, "envoy-extra-args", "", + "Extra envoy command line args to be set when starting envoy (e.g \"--log-level debug\").") c.flagSet.StringVar(&c.flagACLAuthMethod, "acl-auth-method", "", "The name of the Kubernetes Auth Method to use for connectInjection if ACLs are enabled.") c.flagSet.BoolVar(&c.flagWriteServiceDefaults, "enable-central-config", false, @@ -305,6 +308,7 @@ func (c *Command) Run(args []string) int { ConsulClient: c.consulClient, ImageConsul: c.flagConsulImage, ImageEnvoy: c.flagEnvoyImage, + ExtraEnvoyOpts: c.flagEnvoyExtraArgs, ImageConsulK8S: c.flagConsulK8sImage, RequireAnnotation: !c.flagDefaultInject, AuthMethod: c.flagACLAuthMethod,