Skip to content

Commit

Permalink
Consider all namespaceselectors in the Pod webhook (#3199)
Browse files Browse the repository at this point in the history
Co-authored-by: Marcell Sevcsik <31651557+0sewa0@users.noreply.github.com>
  • Loading branch information
waodim and 0sewa0 committed May 31, 2024
1 parent 767ffbb commit d2dc4b7
Show file tree
Hide file tree
Showing 5 changed files with 165 additions and 30 deletions.
6 changes: 3 additions & 3 deletions pkg/webhook/mutation/pod/metadata/containers_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ func TestMutateUserContainers(t *testing.T) {
annotations := map[string]string{"container.inject.dyantrace/container": "false"}

t.Run("Add volume mounts to containers", func(t *testing.T) {
request := createTestMutationRequest(getTestDynakube(), nil)
request := createTestMutationRequest(getTestDynakube(), nil, false)
mutateUserContainers(request.BaseRequest)

for _, container := range request.Pod.Spec.Containers {
Expand All @@ -23,7 +23,7 @@ func TestMutateUserContainers(t *testing.T) {
t.Run("Do not inject container if excluded in dynkube", func(t *testing.T) {
dynakube.Annotations = annotations

request := createTestMutationRequest(dynakube, nil)
request := createTestMutationRequest(dynakube, nil, false)
mutateUserContainers(request.BaseRequest)

for _, container := range request.Pod.Spec.Containers {
Expand All @@ -32,7 +32,7 @@ func TestMutateUserContainers(t *testing.T) {
})

t.Run("Do not inject container if excluded in pod", func(t *testing.T) {
request := createTestMutationRequest(dynakube, annotations)
request := createTestMutationRequest(dynakube, annotations, false)
mutateUserContainers(request.BaseRequest)

for _, container := range request.Pod.Spec.Containers {
Expand Down
12 changes: 11 additions & 1 deletion pkg/webhook/mutation/pod/metadata/mutator.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ import (
webhookotel "github.com/Dynatrace/dynatrace-operator/pkg/webhook/internal/otel"
corev1 "k8s.io/api/core/v1"
k8serrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/labels"
"sigs.k8s.io/controller-runtime/pkg/client"
)

Expand All @@ -36,7 +38,15 @@ func (mut *Mutator) Enabled(request *dtwebhook.BaseRequest) bool {
request.DynaKube.FeatureAutomaticInjection())
enabledOnDynakube := request.DynaKube.MetadataEnrichmentEnabled()

return enabledOnPod && enabledOnDynakube
matchesNamespace := true // if no namespace selector is configured, we just pass set this to true

if request.DynaKube.MetadataEnrichmentNamespaceSelector().Size() > 0 {
selector, _ := metav1.LabelSelectorAsSelector(request.DynaKube.MetadataEnrichmentNamespaceSelector())

matchesNamespace = selector.Matches(labels.Set(request.Namespace.Labels))
}

return matchesNamespace && enabledOnPod && enabledOnDynakube
}

func (mut *Mutator) Injected(request *dtwebhook.BaseRequest) bool {
Expand Down
102 changes: 82 additions & 20 deletions pkg/webhook/mutation/pod/metadata/mutator_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,26 +18,29 @@ import (
)

const (
testPodName = "test-pod"
testNamespaceName = "test-namespace"
testDynakubeName = "test-dynakube"
testApiUrl = "http://test-endpoint/api"
testWorkloadInfoName = "test-name"
testWorkloadInfoKind = "test-kind"
testPodName = "test-pod"
testNamespaceName = "test-namespace"
testDynakubeName = "test-dynakube"
testApiUrl = "http://test-endpoint/api"
testWorkloadInfoName = "test-name"
testWorkloadInfoKind = "test-kind"
testLabelKeyMatching = "inject"
testLabelKeyNotMatching = "do-not-inject"
testLabelValue = "into-this-ns"
)

func TestEnabled(t *testing.T) {
t.Run("turned off", func(t *testing.T) {
mutator := createTestPodMutator(nil)
request := createTestMutationRequest(nil, map[string]string{dtwebhook.AnnotationMetadataEnrichmentInject: "false"})
request := createTestMutationRequest(nil, map[string]string{dtwebhook.AnnotationMetadataEnrichmentInject: "false"}, false)

enabled := mutator.Enabled(request.BaseRequest)

require.False(t, enabled)
})
t.Run("off by feature flag", func(t *testing.T) {
mutator := createTestPodMutator(nil)
request := createTestMutationRequest(nil, nil)
request := createTestMutationRequest(nil, nil, false)
request.DynaKube.Spec.MetadataEnrichment.Enabled = true
request.DynaKube.Annotations = map[string]string{dynatracev1beta2.AnnotationFeatureAutomaticInjection: "false"}

Expand All @@ -52,27 +55,69 @@ func TestEnabled(t *testing.T) {
MetadataEnrichment: dynatracev1beta2.MetadataEnrichment{Enabled: true},
},
}
request := createTestMutationRequest(&dynakube, nil)
request := createTestMutationRequest(&dynakube, nil, false)
request.DynaKube.Annotations = map[string]string{dynatracev1beta2.AnnotationFeatureAutomaticInjection: "true"}

enabled := mutator.Enabled(request.BaseRequest)

require.True(t, enabled)
})
t.Run("on with namespaceselector", func(t *testing.T) {
mutator := createTestPodMutator(nil)
dynakube := dynatracev1beta2.DynaKube{
Spec: dynatracev1beta2.DynaKubeSpec{
MetadataEnrichment: dynatracev1beta2.MetadataEnrichment{
Enabled: true,
NamespaceSelector: metav1.LabelSelector{
MatchLabels: map[string]string{
testLabelKeyMatching: testLabelValue,
},
},
},
},
}
request := createTestMutationRequest(&dynakube, nil, true)
request.DynaKube.Annotations = map[string]string{dynatracev1beta2.AnnotationFeatureAutomaticInjection: "true"}

enabled := mutator.Enabled(request.BaseRequest)

require.True(t, enabled)
})
t.Run("off due to mismatching namespaceselector", func(t *testing.T) {
mutator := createTestPodMutator(nil)
dynakube := dynatracev1beta2.DynaKube{
Spec: dynatracev1beta2.DynaKubeSpec{
MetadataEnrichment: dynatracev1beta2.MetadataEnrichment{
Enabled: true,
NamespaceSelector: metav1.LabelSelector{
MatchLabels: map[string]string{
testLabelKeyNotMatching: testLabelValue,
},
},
},
},
}
request := createTestMutationRequest(&dynakube, nil, true)
request.DynaKube.Annotations = map[string]string{dynatracev1beta2.AnnotationFeatureAutomaticInjection: "true"}

enabled := mutator.Enabled(request.BaseRequest)

require.False(t, enabled)
})
}

func TestInjected(t *testing.T) {
t.Run("already marked", func(t *testing.T) {
mutator := createTestPodMutator(nil)
request := createTestMutationRequest(nil, map[string]string{dtwebhook.AnnotationMetadataEnrichmentInjected: "true"})
request := createTestMutationRequest(nil, map[string]string{dtwebhook.AnnotationMetadataEnrichmentInjected: "true"}, false)

enabled := mutator.Injected(request.BaseRequest)

require.True(t, enabled)
})
t.Run("fresh", func(t *testing.T) {
mutator := createTestPodMutator(nil)
request := createTestMutationRequest(nil, nil)
request := createTestMutationRequest(nil, nil, false)

enabled := mutator.Injected(request.BaseRequest)

Expand All @@ -83,7 +128,7 @@ func TestInjected(t *testing.T) {
func TestMutate(t *testing.T) {
t.Run("should mutate the pod and init container in the request", func(t *testing.T) {
mutator := createTestPodMutator([]client.Object{getTestInitSecret()})
request := createTestMutationRequest(getTestDynakube(), nil)
request := createTestMutationRequest(getTestDynakube(), nil, false)
initialNumberOfVolumesLen := len(request.Pod.Spec.Volumes)
initialContainerVolumeMountsLen := len(request.Pod.Spec.Containers[0].VolumeMounts)
initialAnnotationsLen := len(request.Pod.Annotations)
Expand Down Expand Up @@ -132,15 +177,15 @@ func TestReinvoke(t *testing.T) {
func TestIngestEndpointSecret(t *testing.T) {
t.Run("shouldn't create ingest secret if already there", func(t *testing.T) {
mutator := createTestPodMutator([]client.Object{getTestInitSecret()})
request := createTestMutationRequest(getTestDynakube(), nil)
request := createTestMutationRequest(getTestDynakube(), nil, false)

err := mutator.ensureIngestEndpointSecret(request)
require.NoError(t, err)
})

t.Run("should create ingest secret", func(t *testing.T) {
mutator := createTestPodMutator([]client.Object{getTestDynakube(), getTestTokensSecret()})
request := createTestMutationRequest(getTestDynakube(), nil)
request := createTestMutationRequest(getTestDynakube(), nil, false)

err := mutator.ensureIngestEndpointSecret(request)
require.NoError(t, err)
Expand All @@ -153,7 +198,7 @@ func TestIngestEndpointSecret(t *testing.T) {

func TestSetInjectedAnnotation(t *testing.T) {
t.Run("should add annotation to nil map", func(t *testing.T) {
request := createTestMutationRequest(nil, nil)
request := createTestMutationRequest(nil, nil, false)
mutator := createTestPodMutator(nil)

require.False(t, mutator.Injected(request.BaseRequest))
Expand All @@ -165,7 +210,7 @@ func TestSetInjectedAnnotation(t *testing.T) {

func TestWorkloadAnnotations(t *testing.T) {
t.Run("should add annotation to nil map", func(t *testing.T) {
request := createTestMutationRequest(nil, nil)
request := createTestMutationRequest(nil, nil, false)

require.Equal(t, "not-found", maputils.GetField(request.Pod.Annotations, dtwebhook.AnnotationWorkloadName, "not-found"))
setWorkloadAnnotations(request.Pod, &workloadInfo{name: testWorkloadInfoName, kind: testWorkloadInfoKind})
Expand All @@ -174,7 +219,7 @@ func TestWorkloadAnnotations(t *testing.T) {
assert.Equal(t, testWorkloadInfoKind, maputils.GetField(request.Pod.Annotations, dtwebhook.AnnotationWorkloadKind, "not-found"))
})
t.Run("should lower case kind annotation", func(t *testing.T) {
request := createTestMutationRequest(nil, nil)
request := createTestMutationRequest(nil, nil, false)
setWorkloadAnnotations(request.Pod, &workloadInfo{name: testWorkloadInfoName, kind: "SuperWorkload"})
assert.Contains(t, request.Pod.Annotations, dtwebhook.AnnotationWorkloadKind)
assert.Equal(t, "superworkload", request.Pod.Annotations[dtwebhook.AnnotationWorkloadKind])
Expand Down Expand Up @@ -204,14 +249,19 @@ func TestContainerIsInjected(t *testing.T) {
})
}

func createTestMutationRequest(dynakube *dynatracev1beta2.DynaKube, annotations map[string]string) *dtwebhook.MutationRequest {
func createTestMutationRequest(dynakube *dynatracev1beta2.DynaKube, annotations map[string]string, withLabelSelector bool) *dtwebhook.MutationRequest {
if dynakube == nil {
dynakube = &dynatracev1beta2.DynaKube{}
}

namespace := getTestNamespace()
if withLabelSelector {
namespace = getTestNamespaceWithMatchingLabel()
}

return dtwebhook.NewMutationRequest(
context.Background(),
*getTestNamespace(),
*namespace,
&corev1.Container{
Name: dtwebhook.InstallContainerName,
},
Expand All @@ -221,7 +271,7 @@ func createTestMutationRequest(dynakube *dynatracev1beta2.DynaKube, annotations
}

func createTestReinvocationRequest(dynakube *dynatracev1beta2.DynaKube, annotations map[string]string) *dtwebhook.ReinvocationRequest {
request := createTestMutationRequest(dynakube, annotations).ToReinvocationRequest()
request := createTestMutationRequest(dynakube, annotations, false).ToReinvocationRequest()
request.Pod.Spec.InitContainers = append(request.Pod.Spec.InitContainers, corev1.Container{Name: dtwebhook.InstallContainerName})

return request
Expand Down Expand Up @@ -325,3 +375,15 @@ func getTestNamespace() *corev1.Namespace {
},
}
}

func getTestNamespaceWithMatchingLabel() *corev1.Namespace {
return &corev1.Namespace{
ObjectMeta: metav1.ObjectMeta{
Name: testNamespaceName,
Labels: map[string]string{
dtwebhook.InjectionInstanceLabel: testDynakubeName,
testLabelKeyMatching: testLabelValue,
},
},
}
}
15 changes: 14 additions & 1 deletion pkg/webhook/mutation/pod/oneagent/mutator.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ import (
"github.com/pkg/errors"
corev1 "k8s.io/api/core/v1"
k8serrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/labels"
"sigs.k8s.io/controller-runtime/pkg/client"
)

Expand All @@ -36,7 +38,18 @@ func NewMutator(image, clusterID, webhookNamespace string, client client.Client,
}

func (mut *Mutator) Enabled(request *dtwebhook.BaseRequest) bool {
return maputils.GetFieldBool(request.Pod.Annotations, dtwebhook.AnnotationOneAgentInject, request.DynaKube.FeatureAutomaticInjection())
enabledOnPod := maputils.GetFieldBool(request.Pod.Annotations, dtwebhook.AnnotationOneAgentInject, request.DynaKube.FeatureAutomaticInjection())
enabledOnDynakube := request.DynaKube.OneAgentNamespaceSelector() != nil

matchesNamespaceSelector := true // if no namespace selector is configured, we just pass set this to true

if request.DynaKube.OneAgentNamespaceSelector().Size() > 0 {
selector, _ := metav1.LabelSelectorAsSelector(request.DynaKube.OneAgentNamespaceSelector())

matchesNamespaceSelector = selector.Matches(labels.Set(request.Namespace.Labels))
}

return matchesNamespaceSelector && enabledOnPod && enabledOnDynakube
}

func (mut *Mutator) Injected(request *dtwebhook.BaseRequest) bool {
Expand Down
60 changes: 55 additions & 5 deletions pkg/webhook/mutation/pod/oneagent/mutator_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,14 @@ import (
)

const (
testImage = "test-image"
testClusterID = "test-cluster-id"
testPodName = "test-pod"
testNamespaceName = "test-namespace"
testDynakubeName = "test-dynakube"
testImage = "test-image"
testClusterID = "test-cluster-id"
testPodName = "test-pod"
testNamespaceName = "test-namespace"
testDynakubeName = "test-dynakube"
testLabelKeyMatching = "inject"
testLabelKeyNotMatching = "do-not-inject"
testLabelValue = "into-this-ns"
)

func TestEnabled(t *testing.T) {
Expand All @@ -35,6 +38,7 @@ func TestEnabled(t *testing.T) {
t.Run("on by default", func(t *testing.T) {
mutator := createTestPodMutator(nil)
request := createTestMutationRequest(nil, nil, getTestNamespace(nil))
request.DynaKube.Spec.OneAgent.ApplicationMonitoring = &dynatracev1beta2.ApplicationMonitoringSpec{}

enabled := mutator.Enabled(request.BaseRequest)

Expand All @@ -52,12 +56,33 @@ func TestEnabled(t *testing.T) {
t.Run("on with feature flag", func(t *testing.T) {
mutator := createTestPodMutator(nil)
request := createTestMutationRequest(nil, nil, getTestNamespace(nil))
request.DynaKube.Spec.OneAgent.ApplicationMonitoring = &dynatracev1beta2.ApplicationMonitoringSpec{}
request.DynaKube.Annotations = map[string]string{dynatracev1beta2.AnnotationFeatureAutomaticInjection: "true"}

enabled := mutator.Enabled(request.BaseRequest)

require.True(t, enabled)
})
t.Run("on with namespaceselector", func(t *testing.T) {
mutator := createTestPodMutator(nil)
request := createTestMutationRequest(nil, nil, getTestNamespaceWithMatchingLabel(nil, testLabelKeyMatching, testLabelValue))
request.DynaKube.Annotations = map[string]string{dynatracev1beta2.AnnotationFeatureAutomaticInjection: "true"}
request.DynaKube = *addNamespaceSelector(&request.DynaKube)

enabled := mutator.Enabled(request.BaseRequest)

require.True(t, enabled)
})
t.Run("off due to not matching namespaceselector", func(t *testing.T) {
mutator := createTestPodMutator(nil)
request := createTestMutationRequest(nil, nil, getTestNamespaceWithMatchingLabel(nil, testLabelKeyNotMatching, testLabelValue))
request.DynaKube.Annotations = map[string]string{dynatracev1beta2.AnnotationFeatureAutomaticInjection: "true"}
request.DynaKube = *addNamespaceSelector(&request.DynaKube)

enabled := mutator.Enabled(request.BaseRequest)

require.False(t, enabled)
})
}

func TestInjected(t *testing.T) {
Expand Down Expand Up @@ -277,6 +302,18 @@ func getTestInitSecret() *corev1.Secret {
}
}

func addNamespaceSelector(dynakube *dynatracev1beta2.DynaKube) *dynatracev1beta2.DynaKube {
dynakube.Spec.OneAgent.ApplicationMonitoring = &dynatracev1beta2.ApplicationMonitoringSpec{}

dynakube.Spec.OneAgent.ApplicationMonitoring.NamespaceSelector = metav1.LabelSelector{
MatchLabels: map[string]string{
testLabelKeyMatching: testLabelValue,
},
}

return dynakube
}

func createTestMutationRequest(dynakube *dynatracev1beta2.DynaKube, podAnnotations map[string]string, namespace corev1.Namespace) *dtwebhook.MutationRequest {
if dynakube == nil {
dynakube = &dynatracev1beta2.DynaKube{}
Expand Down Expand Up @@ -452,3 +489,16 @@ func getTestNamespace(annotations map[string]string) corev1.Namespace {
},
}
}

func getTestNamespaceWithMatchingLabel(annotations map[string]string, labelKey, labelValue string) corev1.Namespace {
return corev1.Namespace{
ObjectMeta: metav1.ObjectMeta{
Name: testNamespaceName,
Labels: map[string]string{
dtwebhook.InjectionInstanceLabel: testDynakubeName,
labelKey: labelValue,
},
Annotations: annotations,
},
}
}

0 comments on commit d2dc4b7

Please sign in to comment.