diff --git a/connect-inject/container_env.go b/connect-inject/container_env.go new file mode 100644 index 000000000000..940f055bb6da --- /dev/null +++ b/connect-inject/container_env.go @@ -0,0 +1,37 @@ +package connectinject + +import ( + "fmt" + "strconv" + "strings" + + corev1 "k8s.io/api/core/v1" +) + +func (h *Handler) containerEnvVars(pod *corev1.Pod) []corev1.EnvVar { + raw, ok := pod.Annotations[annotationUpstreams] + if !ok || raw == "" { + return []corev1.EnvVar{} + } + + var result []corev1.EnvVar + for _, raw := range strings.Split(raw, ",") { + parts := strings.SplitN(raw, ":", 2) + port, _ := portValue(pod, strings.TrimSpace(parts[1])) + if port > 0 { + name := strings.TrimSpace(parts[0]) + name = strings.ToUpper(strings.Replace(name, "-", "_", -1)) + portStr := strconv.Itoa(int(port)) + + result = append(result, corev1.EnvVar{ + Name: fmt.Sprintf("%s_CONNECT_SERVICE_HOST", name), + Value: "127.0.0.1", + }, corev1.EnvVar{ + Name: fmt.Sprintf("%s_CONNECT_SERVICE_PORT", name), + Value: portStr, + }) + } + } + + return result +} diff --git a/connect-inject/container_init.go b/connect-inject/container_init.go new file mode 100644 index 000000000000..8b2e8cfb9be6 --- /dev/null +++ b/connect-inject/container_init.go @@ -0,0 +1,150 @@ +package connectinject + +import ( + "bytes" + "strings" + "text/template" + + corev1 "k8s.io/api/core/v1" +) + +type initContainerCommandData struct { + PodName string + ServiceName string + ServicePort int32 + Upstreams []initContainerCommandUpstreamData +} + +type initContainerCommandUpstreamData struct { + Name string + LocalPort int32 +} + +// containerInit returns the init container spec for registering the Consul +// service, setting up the Envoy bootstrap, etc. +func (h *Handler) containerInit(pod *corev1.Pod) (corev1.Container, error) { + data := initContainerCommandData{ + PodName: pod.Name, + ServiceName: pod.Annotations[annotationService], + } + if data.ServiceName == "" { + // Assertion, since we call defaultAnnotations above and do + // not mutate pods without a service specified. + panic("No service found. This should be impossible since we default it.") + } + + // If a port is specified, then we determine the value of that port + // and register that port for the host service. + if raw, ok := pod.Annotations[annotationPort]; ok && raw != "" { + if port, _ := portValue(pod, raw); port > 0 { + data.ServicePort = port + } + } + + // If upstreams are specified, configure those + if raw, ok := pod.Annotations[annotationUpstreams]; ok && raw != "" { + for _, raw := range strings.Split(raw, ",") { + parts := strings.SplitN(raw, ":", 2) + port, _ := portValue(pod, strings.TrimSpace(parts[1])) + if port > 0 { + data.Upstreams = append(data.Upstreams, initContainerCommandUpstreamData{ + Name: strings.TrimSpace(parts[0]), + LocalPort: port, + }) + } + } + } + + // Render the command + var buf bytes.Buffer + tpl := template.Must(template.New("root").Parse(strings.TrimSpace( + initContainerCommandTpl))) + err := tpl.Execute(&buf, &data) + if err != nil { + return corev1.Container{}, err + } + + return corev1.Container{ + Name: "consul-connect-inject-init", + Image: h.ImageConsul, + Env: []corev1.EnvVar{ + { + Name: "HOST_IP", + ValueFrom: &corev1.EnvVarSource{ + FieldRef: &corev1.ObjectFieldSelector{FieldPath: "status.hostIP"}, + }, + }, + { + Name: "POD_IP", + ValueFrom: &corev1.EnvVarSource{ + FieldRef: &corev1.ObjectFieldSelector{FieldPath: "status.podIP"}, + }, + }, + }, + VolumeMounts: []corev1.VolumeMount{ + corev1.VolumeMount{ + Name: volumeName, + MountPath: "/consul/connect-inject", + }, + }, + Command: []string{"/bin/sh", "-ec", buf.String()}, + }, nil +} + +// initContainerCommandTpl is the template for the command executed by +// the init container. +const initContainerCommandTpl = ` +export CONSUL_HTTP_ADDR="${HOST_IP}:8500" +export CONSUL_GRPC_ADDR="${HOST_IP}:8502" + +# Register the service. The HCL is stored in the volume so that +# the preStop hook can access it to deregister the service. +cat </consul/connect-inject/service.hcl +services { + id = "{{ .PodName }}-{{ .ServiceName }}-proxy" + name = "{{ .ServiceName }}-proxy" + kind = "connect-proxy" + address = "${POD_IP}" + port = 20000 + + proxy { + destination_service_name = "{{ .ServiceName }}" + destination_service_id = "{{ .ServiceName}}" + {{ if (gt .ServicePort 0) -}} + local_service_address = "127.0.0.1" + local_service_port = {{ .ServicePort }} + {{ end -}} + + + {{ range .Upstreams -}} + upstreams { + destination_name = "{{ .Name }}" + local_bind_port = {{ .LocalPort }} + } + {{ end }} + } + + checks { + name = "Proxy Public Listener" + tcp = "${POD_IP}:20000" + interval = "10s" + deregister_critical_service_after = "10m" + } + + checks { + name = "Destination Alias" + alias_service = "{{ .ServiceName }}" + } +} +EOF + +/bin/consul services register /consul/connect-inject/service.hcl + +# Generate the envoy bootstrap code +/bin/consul connect envoy \ + -proxy-id="{{ .PodName }}-{{ .ServiceName }}-proxy" \ + -bootstrap > /consul/connect-inject/envoy-bootstrap.yaml + +# Copy the Consul binary +cp /bin/consul /consul/connect-inject/consul +` diff --git a/connect-inject/container_init_test.go b/connect-inject/container_init_test.go new file mode 100644 index 000000000000..960ca0a58f77 --- /dev/null +++ b/connect-inject/container_init_test.go @@ -0,0 +1,88 @@ +package connectinject + +import ( + "strings" + "testing" + + "github.com/stretchr/testify/require" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +func TestHandlerContainerInit(t *testing.T) { + minimal := func() *corev1.Pod { + return &corev1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Annotations: map[string]string{ + annotationService: "foo", + }, + }, + + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + corev1.Container{ + Name: "web", + }, + + corev1.Container{ + Name: "web-side", + }, + }, + }, + } + } + + cases := []struct { + Name string + Pod func(*corev1.Pod) *corev1.Pod + Cmd string // Strings.Contains test + CmdNot string // Not contains + }{ + { + "Only service", + func(pod *corev1.Pod) *corev1.Pod { + pod.Annotations[annotationService] = "web" + return pod + }, + `service = "web"`, + `upstreams`, + }, + + { + "Service port specified", + func(pod *corev1.Pod) *corev1.Pod { + pod.Annotations[annotationService] = "web" + pod.Annotations[annotationPort] = "1234" + return pod + }, + "port = 1234", + "", + }, + + { + "Upstream", + func(pod *corev1.Pod) *corev1.Pod { + pod.Annotations[annotationService] = "web" + pod.Annotations[annotationUpstreams] = "db:1234" + return pod + }, + `destination_name = "db"`, + "", + }, + } + + for _, tt := range cases { + t.Run(tt.Name, func(t *testing.T) { + require := require.New(t) + + var h Handler + container, err := h.containerInit(tt.Pod(minimal())) + require.NoError(err) + actual := strings.Join(container.Command, " ") + require.Contains(actual, tt.Cmd) + if tt.CmdNot != "" { + require.NotContains(actual, tt.CmdNot) + } + }) + } +} diff --git a/connect-inject/container_sidecar.go b/connect-inject/container_sidecar.go new file mode 100644 index 000000000000..1dae08e88bf1 --- /dev/null +++ b/connect-inject/container_sidecar.go @@ -0,0 +1,49 @@ +package connectinject + +import ( + "strings" + + corev1 "k8s.io/api/core/v1" +) + +func (h *Handler) containerSidecar(pod *corev1.Pod) corev1.Container { + return corev1.Container{ + Name: "consul-connect-envoy-sidecar", + Image: h.ImageEnvoy, + Env: []corev1.EnvVar{ + { + Name: "HOST_IP", + ValueFrom: &corev1.EnvVarSource{ + FieldRef: &corev1.ObjectFieldSelector{FieldPath: "status.hostIP"}, + }, + }, + }, + VolumeMounts: []corev1.VolumeMount{ + corev1.VolumeMount{ + Name: volumeName, + MountPath: "/consul/connect-inject", + }, + }, + Lifecycle: &corev1.Lifecycle{ + PreStop: &corev1.Handler{ + Exec: &corev1.ExecAction{ + Command: []string{ + "/bin/sh", + "-ec", + strings.TrimSpace(sidecarPreStopCommand), + }, + }, + }, + }, + Command: []string{ + "envoy", + "--config-path", "/consul/connect-inject/envoy-bootstrap.yaml", + }, + } +} + +const sidecarPreStopCommand = ` +export CONSUL_HTTP_ADDR="${HOST_IP}:8500" +/consul/connect-inject/consul services deregister \ + /consul/connect-inject/service.hcl +` diff --git a/connect-inject/container_volume.go b/connect-inject/container_volume.go new file mode 100644 index 000000000000..e3a70676f129 --- /dev/null +++ b/connect-inject/container_volume.go @@ -0,0 +1,20 @@ +package connectinject + +import ( + corev1 "k8s.io/api/core/v1" +) + +// volumeName is the name of the volume that is created to store the +// Consul Connect injection data. +const volumeName = "consul-connect-inject-data" + +// containerVolume returns the volume data to add to the pod. This volume +// is used for shared data between containers. +func (h *Handler) containerVolume() corev1.Volume { + return corev1.Volume{ + Name: volumeName, + VolumeSource: corev1.VolumeSource{ + EmptyDir: &corev1.EmptyDirVolumeSource{}, + }, + } +} diff --git a/helper/connect-inject/handler.go b/connect-inject/handler.go similarity index 71% rename from helper/connect-inject/handler.go rename to connect-inject/handler.go index 56bb0d03bd3b..03b2d72a78f6 100644 --- a/helper/connect-inject/handler.go +++ b/connect-inject/handler.go @@ -7,7 +7,6 @@ import ( "log" "net/http" "strconv" - "strings" "github.com/mattbaird/jsonpatch" "k8s.io/api/admission/v1beta1" @@ -17,6 +16,11 @@ import ( "k8s.io/apimachinery/pkg/runtime/serializer" ) +const ( + DefaultConsulImage = "consul:1.3.0" + DefaultEnvoyImage = "envoyproxy/envoy-alpine:v1.8.0" +) + const ( // annotationStatus is the key of the annotation that is added to // a pod after an injection is done. @@ -57,6 +61,13 @@ var ( // Handler is the HTTP handler for admission webhooks. type Handler struct { + // ImageConsul is the container image for Consul to use. + // ImageEnvoy is the container image for Envoy to use. + // + // Both of these MUST be set. + ImageConsul string + ImageEnvoy string + // RequireAnnotation means that the annotation must be given to inject. // If this is false, injection is default. RequireAnnotation bool @@ -152,11 +163,50 @@ func (h *Handler) Mutate(req *v1beta1.AdmissionRequest) *v1beta1.AdmissionRespon // Accumulate any patches here var patches []jsonpatch.JsonPatchOperation - // Add a container to it + // Add our volume that will be shared by the init container and + // the sidecar for passing data in the pod. + patches = append(patches, addVolume( + pod.Spec.Volumes, + []corev1.Volume{h.containerVolume()}, + "/spec/volumes")...) + + // Add the upstream services as environment variables for easy + // service discovery. + for i, container := range pod.Spec.InitContainers { + patches = append(patches, addEnvVar( + container.Env, + h.containerEnvVars(&pod), + fmt.Sprintf("/spec/initContainers/%d/env", i))...) + } + for i, container := range pod.Spec.Containers { + patches = append(patches, addEnvVar( + container.Env, + h.containerEnvVars(&pod), + fmt.Sprintf("/spec/containers/%d/env", i))...) + } + + // Add the init container that registers the service and sets up + // the Envoy configuration. + container, err := h.containerInit(&pod) + if err != nil { + return &v1beta1.AdmissionResponse{ + Result: &metav1.Status{ + Message: fmt.Sprintf("Error configuring injection init container: %s", err), + }, + } + } + patches = append(patches, addContainer( + pod.Spec.InitContainers, + []corev1.Container{container}, + "/spec/initContainers")...) + + // Add the Envoy sidecar patches = append(patches, addContainer( pod.Spec.Containers, []corev1.Container{h.containerSidecar(&pod)}, "/spec/containers")...) + + // Add annotations so that we know we're injected patches = append(patches, updateAnnotation( pod.Annotations, map[string]string{annotationStatus: "injected"})...) @@ -237,66 +287,6 @@ func (h *Handler) defaultAnnotations(pod *corev1.Pod) error { return nil } -func (h *Handler) containerSidecar(pod *corev1.Pod) corev1.Container { - cmd := []string{ - "exec /bin/consul connect proxy", - "-http-addr=${HOST_IP}:8500", - } - - svc := pod.Annotations[annotationService] - if svc == "" { - // Assertion, since we call defaultAnnotations above and do - // not mutate pods without a service specified. - panic("No service found. This should be impossible since we default it.") - } - cmd = append(cmd, "-service="+svc) - - // If a port is specified, then we determine the value of that port - // and register this proxy as a listener. This enables the proxy to - // act as an inbound connection receiver. - if raw, ok := pod.Annotations[annotationPort]; ok && raw != "" { - if port, _ := portValue(pod, raw); port > 0 { - cmd = append(cmd, - fmt.Sprintf("-service-addr=127.0.0.1:%d", port), - "-listen=${POD_IP}:12500", - "-register", - ) - } - } - - // If upstreams are specified, configure those - if raw, ok := pod.Annotations[annotationUpstreams]; ok && raw != "" { - for _, raw := range strings.Split(raw, ",") { - parts := strings.SplitN(raw, ":", 2) - port, _ := portValue(pod, strings.TrimSpace(parts[1])) - if port > 0 { - cmd = append(cmd, fmt.Sprintf( - "-upstream=%s:%d", strings.TrimSpace(parts[0]), port)) - } - } - } - - return corev1.Container{ - Name: "consul-connect-proxy", - Image: "consul:1.2.2", - Env: []corev1.EnvVar{ - { - Name: "POD_IP", - ValueFrom: &corev1.EnvVarSource{ - FieldRef: &corev1.ObjectFieldSelector{FieldPath: "status.podIP"}, - }, - }, - { - Name: "HOST_IP", - ValueFrom: &corev1.EnvVarSource{ - FieldRef: &corev1.ObjectFieldSelector{FieldPath: "status.hostIP"}, - }, - }, - }, - Command: []string{"/bin/sh", "-ec", strings.Join(cmd, " ")}, - } -} - func portValue(pod *corev1.Pod, value string) (int32, error) { // First search for the named port for _, c := range pod.Spec.Containers { @@ -319,57 +309,3 @@ func admissionError(err error) *v1beta1.AdmissionResponse { }, } } - -func addContainer(target, add []corev1.Container, base string) []jsonpatch.JsonPatchOperation { - var result []jsonpatch.JsonPatchOperation - first := len(target) == 0 - var value interface{} - for _, container := range add { - value = container - path := base - if first { - first = false - value = []corev1.Container{container} - } else { - path = path + "/-" - } - - result = append(result, jsonpatch.JsonPatchOperation{ - Operation: "add", - Path: path, - Value: value, - }) - } - - return result -} - -func updateAnnotation(target, add map[string]string) []jsonpatch.JsonPatchOperation { - var result []jsonpatch.JsonPatchOperation - if len(target) == 0 { - result = append(result, jsonpatch.JsonPatchOperation{ - Operation: "add", - Path: "/metadata/annotations", - Value: add, - }) - - return result - } - - for key, value := range add { - result = append(result, jsonpatch.JsonPatchOperation{ - Operation: "add", - Path: "/metadata/annotations/" + escapeJSONPointer(key), - Value: value, - }) - } - - return result -} - -// https://tools.ietf.org/html/rfc6901 -func escapeJSONPointer(s string) string { - s = strings.Replace(s, "~", "~0", -1) - s = strings.Replace(s, "/", "~1", -1) - return s -} diff --git a/helper/connect-inject/handler_test.go b/connect-inject/handler_test.go similarity index 80% rename from helper/connect-inject/handler_test.go rename to connect-inject/handler_test.go index 6c8c84bba3bf..8bf1c6b1c4ac 100644 --- a/helper/connect-inject/handler_test.go +++ b/connect-inject/handler_test.go @@ -4,7 +4,6 @@ import ( "encoding/json" "net/http" "net/http/httptest" - "strings" "testing" "github.com/mattbaird/jsonpatch" @@ -64,7 +63,7 @@ func TestHandlerHandle(t *testing.T) { }, { - "empty pod", + "empty pod basic", Handler{}, v1beta1.AdmissionRequest{ Object: encodeRaw(t, &corev1.Pod{ @@ -73,6 +72,65 @@ func TestHandlerHandle(t *testing.T) { }, "", []jsonpatch.JsonPatchOperation{ + { + Operation: "add", + Path: "/spec/volumes", + }, + { + Operation: "add", + Path: "/spec/initContainers", + }, + { + Operation: "add", + Path: "/spec/containers/-", + }, + { + Operation: "add", + Path: "/metadata/annotations/" + escapeJSONPointer(annotationStatus), + }, + }, + }, + + { + "pod with upstreams specified", + Handler{}, + v1beta1.AdmissionRequest{ + Object: encodeRaw(t, &corev1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Annotations: map[string]string{ + annotationUpstreams: "echo:1234,db:1234", + }, + }, + + Spec: basicSpec, + }), + }, + "", + []jsonpatch.JsonPatchOperation{ + { + Operation: "add", + Path: "/spec/volumes", + }, + { + Operation: "add", + Path: "/spec/containers/0/env", + }, + { + Operation: "add", + Path: "/spec/containers/0/env/-", + }, + { + Operation: "add", + Path: "/spec/containers/0/env/-", + }, + { + Operation: "add", + Path: "/spec/containers/0/env/-", + }, + { + Operation: "add", + Path: "/spec/initContainers", + }, { Operation: "add", Path: "/spec/containers/-", @@ -118,6 +176,14 @@ func TestHandlerHandle(t *testing.T) { }, "", []jsonpatch.JsonPatchOperation{ + { + Operation: "add", + Path: "/spec/volumes", + }, + { + Operation: "add", + Path: "/spec/initContainers", + }, { Operation: "add", Path: "/spec/containers/-", @@ -294,83 +360,6 @@ func TestHandlerDefaultAnnotations(t *testing.T) { } } -func TestHandlerContainerSidecar(t *testing.T) { - minimal := func() *corev1.Pod { - return &corev1.Pod{ - ObjectMeta: metav1.ObjectMeta{ - Annotations: map[string]string{ - annotationService: "foo", - }, - }, - - Spec: corev1.PodSpec{ - Containers: []corev1.Container{ - corev1.Container{ - Name: "web", - }, - - corev1.Container{ - Name: "web-side", - }, - }, - }, - } - } - - cases := []struct { - Name string - Pod func(*corev1.Pod) *corev1.Pod - Cmd string // Strings.Contains test - CmdNot string // Not contains - }{ - { - "Only service", - func(pod *corev1.Pod) *corev1.Pod { - pod.Annotations[annotationService] = "web" - return pod - }, - "-service=web", - "-register", - }, - - { - "Service port specified", - func(pod *corev1.Pod) *corev1.Pod { - pod.Annotations[annotationService] = "web" - pod.Annotations[annotationPort] = "1234" - return pod - }, - "-service-addr=127.0.0.1:1234", - "", - }, - - { - "Upstream", - func(pod *corev1.Pod) *corev1.Pod { - pod.Annotations[annotationService] = "web" - pod.Annotations[annotationUpstreams] = "db:1234" - return pod - }, - "-upstream=db:1234", - "", - }, - } - - for _, tt := range cases { - t.Run(tt.Name, func(t *testing.T) { - require := require.New(t) - - var h Handler - container := h.containerSidecar(tt.Pod(minimal())) - actual := strings.Join(container.Command, " ") - require.Contains(actual, tt.Cmd) - if tt.CmdNot != "" { - require.NotContains(actual, tt.CmdNot) - } - }) - } -} - // encodeRaw is a helper to encode some data into a RawExtension. func encodeRaw(t *testing.T, input interface{}) runtime.RawExtension { data, err := json.Marshal(input) diff --git a/connect-inject/patch.go b/connect-inject/patch.go new file mode 100644 index 000000000000..665c18bccf1c --- /dev/null +++ b/connect-inject/patch.go @@ -0,0 +1,109 @@ +package connectinject + +import ( + "strings" + + "github.com/mattbaird/jsonpatch" + corev1 "k8s.io/api/core/v1" +) + +func addVolume(target, add []corev1.Volume, base string) []jsonpatch.JsonPatchOperation { + var result []jsonpatch.JsonPatchOperation + first := len(target) == 0 + var value interface{} + for _, v := range add { + value = v + path := base + if first { + first = false + value = []corev1.Volume{v} + } else { + path = path + "/-" + } + + result = append(result, jsonpatch.JsonPatchOperation{ + Operation: "add", + Path: path, + Value: value, + }) + } + return result +} + +func addContainer(target, add []corev1.Container, base string) []jsonpatch.JsonPatchOperation { + var result []jsonpatch.JsonPatchOperation + first := len(target) == 0 + var value interface{} + for _, container := range add { + value = container + path := base + if first { + first = false + value = []corev1.Container{container} + } else { + path = path + "/-" + } + + result = append(result, jsonpatch.JsonPatchOperation{ + Operation: "add", + Path: path, + Value: value, + }) + } + + return result +} + +func addEnvVar(target, add []corev1.EnvVar, base string) []jsonpatch.JsonPatchOperation { + var result []jsonpatch.JsonPatchOperation + first := len(target) == 0 + var value interface{} + for _, v := range add { + value = v + path := base + if first { + first = false + value = []corev1.EnvVar{v} + } else { + path = path + "/-" + } + + result = append(result, jsonpatch.JsonPatchOperation{ + Operation: "add", + Path: path, + Value: value, + }) + } + + return result +} + +func updateAnnotation(target, add map[string]string) []jsonpatch.JsonPatchOperation { + var result []jsonpatch.JsonPatchOperation + if len(target) == 0 { + result = append(result, jsonpatch.JsonPatchOperation{ + Operation: "add", + Path: "/metadata/annotations", + Value: add, + }) + + return result + } + + for key, value := range add { + result = append(result, jsonpatch.JsonPatchOperation{ + Operation: "add", + Path: "/metadata/annotations/" + escapeJSONPointer(key), + Value: value, + }) + } + + return result +} + +// https://tools.ietf.org/html/rfc6901 +func escapeJSONPointer(s string) string { + s = strings.Replace(s, "~", "~0", -1) + s = strings.Replace(s, "/", "~1", -1) + return s +} diff --git a/subcommand/inject-connect/command.go b/subcommand/inject-connect/command.go index c1411e5eaf5b..e97407808858 100644 --- a/subcommand/inject-connect/command.go +++ b/subcommand/inject-connect/command.go @@ -14,8 +14,8 @@ import ( "time" "github.com/gorilla/handlers" + "github.com/hashicorp/consul-k8s/connect-inject" "github.com/hashicorp/consul-k8s/helper/cert" - "github.com/hashicorp/consul-k8s/helper/connect-inject" "github.com/hashicorp/consul/command/flags" "github.com/mitchellh/cli" "k8s.io/apimachinery/pkg/types" @@ -32,6 +32,8 @@ type Command struct { flagCertFile string // TLS cert for listening (PEM) flagKeyFile string // TLS cert private key (PEM) flagDefaultInject bool // True to inject by default + flagConsulImage string // Docker image for Consul + flagEnvoyImage string // Docker image for Envoy flagSet *flag.FlagSet once sync.Once @@ -51,6 +53,10 @@ func (c *Command) init() { "PEM-encoded TLS certificate to serve. If blank, will generate random cert.") c.flagSet.StringVar(&c.flagKeyFile, "tls-key-file", "", "PEM-encoded TLS private key to serve. If blank, will generate random cert.") + c.flagSet.StringVar(&c.flagConsulImage, "consul-image", connectinject.DefaultConsulImage, + "Docker image for Consul. Defaults to an Consul 1.3.0.") + c.flagSet.StringVar(&c.flagEnvoyImage, "envoy-image", connectinject.DefaultEnvoyImage, + "Docker image for Envoy. Defaults to Envoy 1.8.0.") c.help = flags.Usage(help, c.flagSet) } @@ -95,7 +101,11 @@ func (c *Command) Run(args []string) int { go c.certWatcher(ctx, certCh, clientset) // Build the HTTP handler and server - injector := connectinject.Handler{RequireAnnotation: !c.flagDefaultInject} + injector := connectinject.Handler{ + ImageConsul: c.flagConsulImage, + ImageEnvoy: c.flagEnvoyImage, + RequireAnnotation: !c.flagDefaultInject, + } mux := http.NewServeMux() mux.HandleFunc("/mutate", injector.Handle) mux.HandleFunc("/health/ready", c.handleReady) @@ -191,9 +201,9 @@ func (c *Command) Help() string { const synopsis = "Inject Connect proxy sidecar." const help = ` -Usage: consul-k8s inject [options] +Usage: consul-k8s inject-connect [options] Run the admission webhook server for injecting the Consul Connect - proxy sidecar. + proxy sidecar. The sidecar uses Envoy by default. `