Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .changelog/2743.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
```release-note:improvement
control-plane: Changed the container ordering in connect-inject to insert consul-dataplane container first if lifecycle is enabled. Container ordering is unchanged if lifecycle is disabled.
```
4 changes: 2 additions & 2 deletions acceptance/framework/connhelper/connect_helper.go
Original file line number Diff line number Diff line change
Expand Up @@ -265,7 +265,7 @@ func (c *ConnectHelper) TestConnectionFailureWhenUnhealthy(t *testing.T) {
opts := c.KubectlOptsForApp(t)

logger.Log(t, "testing k8s -> consul health checks sync by making the static-server unhealthy")
k8s.RunKubectl(t, opts, "exec", "deploy/"+StaticServerName, "--", "touch", "/tmp/unhealthy")
k8s.RunKubectl(t, opts, "exec", "deploy/"+StaticServerName, "-c", "static-server", "--", "touch", "/tmp/unhealthy")

// The readiness probe should take a moment to be reflected in Consul,
// CheckStaticServerConnection will retry until Consul marks the service
Expand All @@ -290,7 +290,7 @@ func (c *ConnectHelper) TestConnectionFailureWhenUnhealthy(t *testing.T) {
}

// Return the static-server to a "healthy state".
k8s.RunKubectl(t, opts, "exec", "deploy/"+StaticServerName, "--", "rm", "/tmp/unhealthy")
k8s.RunKubectl(t, opts, "exec", "deploy/"+StaticServerName, "-c", "static-server", "--", "rm", "/tmp/unhealthy")
}

// helmValues uses the Secure and AutoEncrypt fields to set values for the Helm
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ func TestConnectInject_ExternalServers(t *testing.T) {
// Test that kubernetes readiness status is synced to Consul.
// Create the file so that the readiness probe of the static-server pod fails.
logger.Log(t, "testing k8s -> consul health checks sync by making the static-server unhealthy")
k8s.RunKubectl(t, ctx.KubectlOptions(t), "exec", "deploy/"+connhelper.StaticServerName, "--", "touch", "/tmp/unhealthy")
k8s.RunKubectl(t, ctx.KubectlOptions(t), "exec", "deploy/"+connhelper.StaticServerName, "-c", "static-server", "--", "touch", "/tmp/unhealthy")

// The readiness probe should take a moment to be reflected in Consul, CheckStaticServerConnection will retry
// until Consul marks the service instance unavailable for mesh traffic, causing the connection to fail.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -221,7 +221,7 @@ func TestConnectInjectNamespaces(t *testing.T) {
// Test that kubernetes readiness status is synced to Consul.
// Create the file so that the readiness probe of the static-server pod fails.
logger.Log(t, "testing k8s -> consul health checks sync by making the static-server unhealthy")
k8s.RunKubectl(t, staticServerOpts, "exec", "deploy/"+connhelper.StaticServerName, "--", "touch", "/tmp/unhealthy")
k8s.RunKubectl(t, staticServerOpts, "exec", "deploy/"+connhelper.StaticServerName, "-c", "static-server", "--", "touch", "/tmp/unhealthy")

// The readiness probe should take a moment to be reflected in Consul, CheckStaticServerConnection will retry
// until Consul marks the service instance unavailable for mesh traffic, causing the connection to fail.
Expand Down
4 changes: 2 additions & 2 deletions acceptance/tests/connect/connect_inject_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -298,9 +298,9 @@ func TestConnectInject_MultiportServices(t *testing.T) {
// and check inbound connections to the multi port pods' services.
// Create the files so that the readiness probes of the multi port pod fails.
logger.Log(t, "testing k8s -> consul health checks sync by making the multiport unhealthy")
k8s.RunKubectl(t, ctx.KubectlOptions(t), "exec", "deploy/"+multiport, "--", "touch", "/tmp/unhealthy-multiport")
k8s.RunKubectl(t, ctx.KubectlOptions(t), "exec", "deploy/"+multiport, "-c", "multiport", "--", "touch", "/tmp/unhealthy-multiport")
logger.Log(t, "testing k8s -> consul health checks sync by making the multiport-admin unhealthy")
k8s.RunKubectl(t, ctx.KubectlOptions(t), "exec", "deploy/"+multiport, "--", "touch", "/tmp/unhealthy-multiport-admin")
k8s.RunKubectl(t, ctx.KubectlOptions(t), "exec", "deploy/"+multiport, "-c", "multiport-admin", "--", "touch", "/tmp/unhealthy-multiport-admin")

// The readiness probe should take a moment to be reflected in Consul, CheckStaticServerConnection will retry
// until Consul marks the service instance unavailable for mesh traffic, causing the connection to fail.
Expand Down
8 changes: 4 additions & 4 deletions acceptance/tests/metrics/metrics_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -74,12 +74,12 @@ func TestComponentMetrics(t *testing.T) {
k8s.DeployKustomize(t, ctx.KubectlOptions(t), cfg.NoCleanupOnFailure, cfg.NoCleanup, cfg.DebugDirectory, "../fixtures/bases/static-client")

// Server Metrics
metricsOutput, err := k8s.RunKubectlAndGetOutputE(t, ctx.KubectlOptions(t), "exec", "deploy/"+StaticClientName, "--", "curl", "--silent", "--show-error", fmt.Sprintf("http://%s:8500/v1/agent/metrics?format=prometheus", fmt.Sprintf("%s-consul-server.%s.svc", releaseName, ns)))
metricsOutput, err := k8s.RunKubectlAndGetOutputE(t, ctx.KubectlOptions(t), "exec", "deploy/"+StaticClientName, "-c", "static-client", "--", "curl", "--silent", "--show-error", fmt.Sprintf("http://%s:8500/v1/agent/metrics?format=prometheus", fmt.Sprintf("%s-consul-server.%s.svc", releaseName, ns)))
require.NoError(t, err)
require.Contains(t, metricsOutput, `consul_acl_ResolveToken{quantile="0.5"}`)

// Client Metrics
metricsOutput, err = k8s.RunKubectlAndGetOutputE(t, ctx.KubectlOptions(t), "exec", "deploy/"+StaticClientName, "--", "sh", "-c", "curl --silent --show-error http://$HOST_IP:8500/v1/agent/metrics?format=prometheus")
metricsOutput, err = k8s.RunKubectlAndGetOutputE(t, ctx.KubectlOptions(t), "exec", "deploy/"+StaticClientName, "-c", "static-client", "--", "sh", "-c", "curl --silent --show-error http://$HOST_IP:8500/v1/agent/metrics?format=prometheus")
require.NoError(t, err)
require.Contains(t, metricsOutput, `consul_acl_ResolveToken{quantile="0.5"}`)

Expand Down Expand Up @@ -133,7 +133,7 @@ func TestAppMetrics(t *testing.T) {
// Retry because sometimes the merged metrics server takes a couple hundred milliseconds
// to start.
retry.RunWith(&retry.Counter{Count: 20, Wait: 2 * time.Second}, t, func(r *retry.R) {
metricsOutput, err := k8s.RunKubectlAndGetOutputE(t, ctx.KubectlOptions(t), "exec", "deploy/"+StaticClientName, "--", "curl", "--silent", "--show-error", fmt.Sprintf("http://%s:20200/metrics", podIP))
metricsOutput, err := k8s.RunKubectlAndGetOutputE(t, ctx.KubectlOptions(t), "exec", "deploy/"+StaticClientName, "-c", "static-client", "--", "curl", "--silent", "--show-error", fmt.Sprintf("http://%s:20200/metrics", podIP))
require.NoError(r, err)
// This assertion represents the metrics from the envoy sidecar.
require.Contains(r, metricsOutput, `envoy_cluster_assignment_stale{local_cluster="server",consul_source_service="server"`)
Expand All @@ -147,7 +147,7 @@ func assertGatewayMetricsEnabled(t *testing.T, ctx environment.TestContext, ns,
require.NoError(t, err)
for _, pod := range pods.Items {
podIP := pod.Status.PodIP
metricsOutput, err := k8s.RunKubectlAndGetOutputE(t, ctx.KubectlOptions(t), "exec", "deploy/"+StaticClientName, "--", "curl", "--silent", "--show-error", fmt.Sprintf("http://%s:20200/metrics", podIP))
metricsOutput, err := k8s.RunKubectlAndGetOutputE(t, ctx.KubectlOptions(t), "exec", "deploy/"+StaticClientName, "-c", "static-client", "--", "curl", "--silent", "--show-error", fmt.Sprintf("http://%s:20200/metrics", podIP))
require.NoError(t, err)
require.Contains(t, metricsOutput, metricsAssertion)
}
Expand Down
8 changes: 4 additions & 4 deletions acceptance/tests/partitions/partitions_connect_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -405,8 +405,8 @@ func TestPartitions_Connect(t *testing.T) {
// Test that kubernetes readiness status is synced to Consul.
// Create the file so that the readiness probe of the static-server pod fails.
logger.Log(t, "testing k8s -> consul health checks sync by making the static-server unhealthy")
k8s.RunKubectl(t, defaultPartitionClusterStaticServerOpts, "exec", "deploy/"+staticServerName, "--", "touch", "/tmp/unhealthy")
k8s.RunKubectl(t, secondaryPartitionClusterStaticServerOpts, "exec", "deploy/"+staticServerName, "--", "touch", "/tmp/unhealthy")
k8s.RunKubectl(t, defaultPartitionClusterStaticServerOpts, "exec", "deploy/"+staticServerName, "-c", "static-server", "--", "touch", "/tmp/unhealthy")
k8s.RunKubectl(t, secondaryPartitionClusterStaticServerOpts, "exec", "deploy/"+staticServerName, "-c", "static-server", "--", "touch", "/tmp/unhealthy")

// The readiness probe should take a moment to be reflected in Consul, CheckStaticServerConnection will retry
// until Consul marks the service instance unavailable for mesh traffic, causing the connection to fail.
Expand Down Expand Up @@ -578,8 +578,8 @@ func TestPartitions_Connect(t *testing.T) {
// Test that kubernetes readiness status is synced to Consul.
// Create the file so that the readiness probe of the static-server pod fails.
logger.Log(t, "testing k8s -> consul health checks sync by making the static-server unhealthy")
k8s.RunKubectl(t, defaultPartitionClusterStaticServerOpts, "exec", "deploy/"+staticServerName, "--", "touch", "/tmp/unhealthy")
k8s.RunKubectl(t, secondaryPartitionClusterStaticServerOpts, "exec", "deploy/"+staticServerName, "--", "touch", "/tmp/unhealthy")
k8s.RunKubectl(t, defaultPartitionClusterStaticServerOpts, "exec", "deploy/"+staticServerName, "-c", "static-server", "--", "touch", "/tmp/unhealthy")
k8s.RunKubectl(t, secondaryPartitionClusterStaticServerOpts, "exec", "deploy/"+staticServerName, "-c", "static-server", "--", "touch", "/tmp/unhealthy")

// The readiness probe should take a moment to be reflected in Consul, CheckStaticServerConnection will retry
// until Consul marks the service instance unavailable for mesh traffic, causing the connection to fail.
Expand Down
36 changes: 30 additions & 6 deletions control-plane/connect-inject/webhook/mesh_webhook.go
Original file line number Diff line number Diff line change
Expand Up @@ -294,7 +294,10 @@ func (w *MeshWebhook) Handle(ctx context.Context, req admission.Request) admissi
// port.
annotatedSvcNames := w.annotatedServiceNames(pod)
multiPort := len(annotatedSvcNames) > 1

lifecycleEnabled, ok := w.LifecycleConfig.EnableProxyLifecycle(pod)
if ok != nil {
w.Log.Error(err, "unable to get lifecycle enabled status")
}
// For single port pods, add the single init container and envoy sidecar.
if !multiPort {
// Add the init container that registers the service and sets up the Envoy configuration.
Expand All @@ -311,8 +314,14 @@ func (w *MeshWebhook) Handle(ctx context.Context, req admission.Request) admissi
w.Log.Error(err, "error configuring injection sidecar container", "request name", req.Name)
return admission.Errored(http.StatusInternalServerError, fmt.Errorf("error configuring injection sidecar container: %s", err))
}
// TODO: invert to start the Envoy sidecar before the application container
pod.Spec.Containers = append(pod.Spec.Containers, envoySidecar)
//Append the Envoy sidecar before the application container only if lifecycle enabled.

if lifecycleEnabled && ok == nil {
pod.Spec.Containers = append([]corev1.Container{envoySidecar}, pod.Spec.Containers...)
} else {
pod.Spec.Containers = append(pod.Spec.Containers, envoySidecar)
}

} else {
// For multi port pods, check for unsupported cases, mount all relevant service account tokens, and mount an init
// container and envoy sidecar per port. Tproxy, metrics, and metrics merging are not supported for multi port pods.
Expand All @@ -327,6 +336,10 @@ func (w *MeshWebhook) Handle(ctx context.Context, req admission.Request) admissi
w.Log.Error(err, "checking unsupported cases for multi port pods")
return admission.Errored(http.StatusInternalServerError, err)
}

//List of sidecar containers for each service. Build as a list to preserve correct ordering in relation
//to services.
sidecarContainers := []corev1.Container{}
for i, svc := range annotatedSvcNames {
w.Log.Info(fmt.Sprintf("service: %s", svc))
if w.AuthMethod != "" {
Expand Down Expand Up @@ -382,9 +395,20 @@ func (w *MeshWebhook) Handle(ctx context.Context, req admission.Request) admissi
w.Log.Error(err, "error configuring injection sidecar container", "request name", req.Name)
return admission.Errored(http.StatusInternalServerError, fmt.Errorf("error configuring injection sidecar container: %s", err))
}
// TODO: invert to start the Envoy sidecar container before the
// application container
pod.Spec.Containers = append(pod.Spec.Containers, envoySidecar)
// If Lifecycle is enabled, add to the list of sidecar containers to be added
// to pod containers at the end in order to preserve relative ordering.
if lifecycleEnabled {
sidecarContainers = append(sidecarContainers, envoySidecar)
} else {
pod.Spec.Containers = append(pod.Spec.Containers, envoySidecar)

}

}

//Add sidecar containers first if lifecycle enabled.
if lifecycleEnabled {
pod.Spec.Containers = append(sidecarContainers, pod.Spec.Containers...)
}
}

Expand Down
153 changes: 153 additions & 0 deletions control-plane/connect-inject/webhook/mesh_webhook_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import (
mapset "github.com/deckarep/golang-set"
logrtest "github.com/go-logr/logr/testr"
"github.com/hashicorp/consul-k8s/control-plane/connect-inject/constants"
"github.com/hashicorp/consul-k8s/control-plane/connect-inject/lifecycle"
Comment thread
t-eckert marked this conversation as resolved.
"github.com/hashicorp/consul-k8s/control-plane/connect-inject/metrics"
"github.com/hashicorp/consul-k8s/control-plane/consul"
"github.com/hashicorp/consul-k8s/control-plane/namespaces"
Expand Down Expand Up @@ -138,6 +139,73 @@ func TestHandlerHandle(t *testing.T) {
},
},
},
{
"empty pod basic with lifecycle",
MeshWebhook{
Log: logrtest.New(t),
AllowK8sNamespacesSet: mapset.NewSetWith("*"),
DenyK8sNamespacesSet: mapset.NewSet(),
decoder: decoder,
Clientset: defaultTestClientWithNamespace(),
LifecycleConfig: lifecycle.Config{DefaultEnableProxyLifecycle: true},
},
admission.Request{
AdmissionRequest: admissionv1.AdmissionRequest{
Namespace: namespaces.DefaultNamespace,
Object: encodeRaw(t, &corev1.Pod{
Spec: basicSpec,
}),
},
},
"",
[]jsonpatch.Operation{
{
Operation: "add",
Path: "/metadata/labels",
},
{
Operation: "add",
Path: "/metadata/annotations",
},
{
Operation: "add",
Path: "/spec/volumes",
},
{
Operation: "add",
Path: "/spec/initContainers",
},
{
Operation: "add",
Path: "/spec/containers/1",
},

{
Operation: "add",
Path: "/spec/containers/0/readinessProbe",
},
{
Operation: "add",
Path: "/spec/containers/0/securityContext",
},
{
Operation: "replace",
Path: "/spec/containers/0/name",
},
{
Operation: "add",
Path: "/spec/containers/0/args",
},
{
Operation: "add",
Path: "/spec/containers/0/env",
},
{
Operation: "add",
Path: "/spec/containers/0/volumeMounts",
},
},
},

{
"pod with upstreams specified",
Expand Down Expand Up @@ -811,6 +879,91 @@ func TestHandlerHandle(t *testing.T) {
},
},
},
{
"multiport pod kube < 1.24 with AuthMethod, serviceaccount has secret ref, lifecycle enabled",
MeshWebhook{
Log: logrtest.New(t),
AllowK8sNamespacesSet: mapset.NewSetWith("*"),
DenyK8sNamespacesSet: mapset.NewSet(),
decoder: decoder,
Clientset: testClientWithServiceAccountAndSecretRefs(),
AuthMethod: "k8s",
LifecycleConfig: lifecycle.Config{DefaultEnableProxyLifecycle: true},
},
admission.Request{
AdmissionRequest: admissionv1.AdmissionRequest{
Namespace: namespaces.DefaultNamespace,
Object: encodeRaw(t, &corev1.Pod{
Spec: basicSpec,
ObjectMeta: metav1.ObjectMeta{
Annotations: map[string]string{
constants.AnnotationService: "web,web-admin",
},
},
}),
},
},
"",
[]jsonpatch.Operation{
{
Operation: "add",
Path: "/spec/containers/0/env",
},
{
Operation: "add",
Path: "/spec/containers/0/volumeMounts",
},
{
Operation: "add",
Path: "/spec/containers/0/readinessProbe",
},
{
Operation: "add",
Path: "/spec/containers/0/securityContext",
},
{
Operation: "replace",
Path: "/spec/containers/0/name",
},
{
Operation: "add",
Path: "/spec/containers/0/args",
},

{
Operation: "add",
Path: "/spec/volumes",
},
{
Operation: "add",
Path: "/spec/initContainers",
},
{
Operation: "add",
Path: "/spec/containers/1",
},
{
Operation: "add",
Path: "/spec/containers/2",
},
{
Operation: "add",
Path: "/metadata/annotations/" + escapeJSONPointer(constants.KeyInjectStatus),
},
{
Operation: "add",
Path: "/metadata/annotations/" + escapeJSONPointer(constants.AnnotationOriginalPod),
},
{
Operation: "add",
Path: "/metadata/annotations/" + escapeJSONPointer(constants.AnnotationConsulK8sVersion),
},
{
Operation: "add",
Path: "/metadata/labels",
},
},
},
{
"dns redirection enabled",
MeshWebhook{
Expand Down