diff --git a/CHANGELOG.md b/CHANGELOG.md index c59aba6938..ed550dc4f9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -45,6 +45,7 @@ FEATURES: IMPROVEMENTS: * Control Plane * Upgrade Docker image Alpine version from 3.14 to 3.15. [[GH-1058](https://github.com/hashicorp/consul-k8s/pull/1058)] + * Support new annotation for mounting connect-inject volume to other containers. [[GH-1111](https://github.com/hashicorp/consul-k8s/pull/1111)] * Helm * API Gateway: Allow controller to read Kubernetes namespaces in order to determine if route is allowed for gateway. [[GH-1092](https://github.com/hashicorp/consul-k8s/pull/1092)] * Support a pre-configured bootstrap ACL token. [[GH-1125](https://github.com/hashicorp/consul-k8s/pull/1125)] diff --git a/control-plane/connect-inject/annotations.go b/control-plane/connect-inject/annotations.go index 5d47e69eb6..d1c59d19dc 100644 --- a/control-plane/connect-inject/annotations.go +++ b/control-plane/connect-inject/annotations.go @@ -16,6 +16,12 @@ const ( // be set to a truthy or falsy value, as parseable by strconv.ParseBool. annotationInject = "consul.hashicorp.com/connect-inject" + // annotationInjectMountVolumes is the key of the annotation that controls whether + // the data volume that connect inject uses to store data including the Consul ACL token + // is mounted to other containers in the pod. It is a comma-separated list of container names + // to mount the volume on. It will be mounted at the path `/consul/connect-inject`. + annotationInjectMountVolumes = "consul.hashicorp.com/connect-inject-mount-volume" + // annotationService is the name of the service to proxy. // This defaults to the name of the Kubernetes service associated with the pod. annotationService = "consul.hashicorp.com/connect-service" diff --git a/control-plane/connect-inject/handler.go b/control-plane/connect-inject/handler.go index 6e454557c0..00f3918f5e 100644 --- a/control-plane/connect-inject/handler.go +++ b/control-plane/connect-inject/handler.go @@ -209,6 +209,9 @@ func (h *Handler) Handle(ctx context.Context, req admission.Request) admission.R // the sidecar for passing data in the pod. pod.Spec.Volumes = append(pod.Spec.Volumes, h.containerVolume()) + // Optionally mount data volume to other containers + h.injectVolumeMount(pod) + // Add the upstream services as environment variables for easy // service discovery. containerEnvVars := h.containerEnvVars(pod) @@ -441,6 +444,19 @@ func (h *Handler) overwriteProbes(ns corev1.Namespace, pod *corev1.Pod) error { return nil } +func (h *Handler) injectVolumeMount(pod corev1.Pod) { + containersToInject := splitCommaSeparatedItemsFromAnnotation(annotationInjectMountVolumes, pod) + + for index, container := range pod.Spec.Containers { + if sliceContains(containersToInject, container.Name) { + pod.Spec.Containers[index].VolumeMounts = append(pod.Spec.Containers[index].VolumeMounts, corev1.VolumeMount{ + Name: volumeName, + MountPath: "/consul/connect-inject", + }) + } + } +} + func (h *Handler) shouldInject(pod corev1.Pod, namespace string) (bool, error) { // Don't inject in the Kubernetes system namespaces if kubeSystemNamespaces.Contains(namespace) { @@ -625,3 +641,12 @@ func (h *Handler) InjectDecoder(d *admission.Decoder) error { h.decoder = d return nil } + +func sliceContains(slice []string, entry string) bool { + for _, s := range slice { + if entry == s { + return true + } + } + return false +} diff --git a/control-plane/connect-inject/handler_test.go b/control-plane/connect-inject/handler_test.go index 0ae01fd93c..9eaf5d89d9 100644 --- a/control-plane/connect-inject/handler_test.go +++ b/control-plane/connect-inject/handler_test.go @@ -264,6 +264,127 @@ func TestHandlerHandle(t *testing.T) { }, }, + { + "pod with empty volume mount annotation", + Handler{ + Log: logrtest.TestLogger{T: t}, + AllowK8sNamespacesSet: mapset.NewSetWith("*"), + DenyK8sNamespacesSet: mapset.NewSet(), + decoder: decoder, + Clientset: defaultTestClientWithNamespace(), + }, + admission.Request{ + AdmissionRequest: admissionv1.AdmissionRequest{ + Namespace: namespaces.DefaultNamespace, + Object: encodeRaw(t, &corev1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Annotations: map[string]string{ + annotationInjectMountVolumes: "", + }, + }, + Spec: basicSpec, + }), + }, + }, + "", + []jsonpatch.Operation{ + { + Operation: "add", + Path: "/spec/volumes", + }, + { + Operation: "add", + Path: "/spec/initContainers", + }, + { + Operation: "add", + Path: "/spec/containers/1", + }, + { + Operation: "add", + Path: "/metadata/annotations/" + escapeJSONPointer(keyInjectStatus), + }, + { + Operation: "add", + Path: "/metadata/annotations/" + escapeJSONPointer(annotationOriginalPod), + }, + { + Operation: "add", + Path: "/metadata/labels", + }, + }, + }, + { + "pod with volume mount annotation", + Handler{ + Log: logrtest.TestLogger{T: t}, + AllowK8sNamespacesSet: mapset.NewSetWith("*"), + DenyK8sNamespacesSet: mapset.NewSet(), + decoder: decoder, + Clientset: defaultTestClientWithNamespace(), + }, + admission.Request{ + AdmissionRequest: admissionv1.AdmissionRequest{ + Namespace: namespaces.DefaultNamespace, + Object: encodeRaw(t, &corev1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Annotations: map[string]string{ + annotationInjectMountVolumes: "web,unknown,web_three_point_oh", + }, + }, + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + { + Name: "web", + }, + { + Name: "web_two_point_oh", + }, + { + Name: "web_three_point_oh", + }, + }, + }, + }), + }, + }, + "", + []jsonpatch.Operation{ + { + Operation: "add", + Path: "/spec/volumes", + }, + { + Operation: "add", + Path: "/spec/containers/0/volumeMounts", + }, + { + Operation: "add", + Path: "/spec/containers/2/volumeMounts", + }, + { + Operation: "add", + Path: "/spec/initContainers", + }, + { + Operation: "add", + Path: "/spec/containers/3", + }, + { + Operation: "add", + Path: "/metadata/annotations/" + escapeJSONPointer(keyInjectStatus), + }, + { + Operation: "add", + Path: "/metadata/annotations/" + escapeJSONPointer(annotationOriginalPod), + }, + { + Operation: "add", + Path: "/metadata/labels", + }, + }, + }, + { "pod with service annotation", Handler{