github.com/verrazzano/verrazzano@v1.7.0/application-operator/controllers/webhooks/metrics-binding-labeler-pod_test.go (about) 1 // Copyright (c) 2022, Oracle and/or its affiliates. 2 // Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. 3 4 package webhooks 5 6 import ( 7 "context" 8 "encoding/json" 9 "testing" 10 11 "github.com/stretchr/testify/assert" 12 vzapp "github.com/verrazzano/verrazzano/application-operator/apis/app/v1alpha1" 13 "github.com/verrazzano/verrazzano/application-operator/constants" 14 appsv1 "k8s.io/api/apps/v1" 15 corev1 "k8s.io/api/core/v1" 16 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 17 "k8s.io/apimachinery/pkg/runtime" 18 "k8s.io/apimachinery/pkg/runtime/schema" 19 "k8s.io/client-go/dynamic/fake" 20 ctrlfake "sigs.k8s.io/controller-runtime/pkg/client/fake" 21 "sigs.k8s.io/controller-runtime/pkg/webhook/admission" 22 ) 23 24 // newLabelerPodWebhook creates a new LabelerPodWebhook 25 func newLabelerPodWebhook() LabelerPodWebhook { 26 scheme := newScheme() 27 scheme.AddKnownTypes(schema.GroupVersion{ 28 Version: "v1", 29 }, &corev1.Namespace{}, &corev1.Pod{}, &appsv1.Deployment{}, &appsv1.ReplicaSet{}, &appsv1.StatefulSet{}) 30 _ = vzapp.AddToScheme(scheme) 31 decoder, _ := admission.NewDecoder(scheme) 32 cli := ctrlfake.NewClientBuilder().WithScheme(scheme).Build() 33 v := LabelerPodWebhook{ 34 Client: cli, 35 DynamicClient: fake.NewSimpleDynamicClient(runtime.NewScheme()), 36 } 37 _ = v.InjectDecoder(decoder) 38 return v 39 } 40 41 // TestNoOwnerReferences tests the handling of a Pod resource 42 // GIVEN a call to the webhook Handle function 43 // WHEN the pod resource has no owner references 44 // THEN the Handle function should succeed and the pod is mutated 45 func TestNoOwnerReferences(t *testing.T) { 46 a := newLabelerPodWebhook() 47 48 // Test data 49 pod := corev1.Pod{ 50 ObjectMeta: metav1.ObjectMeta{ 51 Name: "test", 52 Namespace: "default", 53 }, 54 } 55 assert.NoError(t, a.Client.Create(context.TODO(), &pod)) 56 57 req := admission.Request{} 58 req.Namespace = "default" 59 marshaledPod, err := json.Marshal(pod) 60 assert.NoError(t, err, "Unexpected error marshaling pod") 61 req.Object = runtime.RawExtension{Raw: marshaledPod} 62 res := a.Handle(context.TODO(), req) 63 64 verifyResponse(t, res, 2) 65 } 66 67 // TestOwnerReference tests the handling of a Pod resource 68 // GIVEN a call to the webhook Handle function 69 // WHEN the pod resource has one owner reference 70 // THEN the Handle function should succeed and the pod is mutated 71 func TestOwnerReference(t *testing.T) { 72 a := newLabelerPodWebhook() 73 74 // Create a replica set with no owner reference 75 u := newUnstructured("apps/v1", "ReplicaSet", "test-replicaSet") 76 resource := schema.GroupVersionResource{ 77 Group: "apps", 78 Version: "v1", 79 Resource: "replicasets", 80 } 81 u.SetLabels(map[string]string{constants.MetricsWorkloadLabel: "testValue"}) 82 _, err := a.DynamicClient.Resource(resource).Namespace("default").Create(context.TODO(), u, metav1.CreateOptions{}) 83 assert.NoError(t, err, "Unexpected error creating replica set") 84 85 // Create the pod with an owner reference 86 pod := corev1.Pod{ 87 ObjectMeta: metav1.ObjectMeta{ 88 Name: "test", 89 Namespace: "default", 90 OwnerReferences: []metav1.OwnerReference{ 91 { 92 Name: "test-replicaSet", 93 Kind: "ReplicaSet", 94 APIVersion: "apps/v1", 95 }, 96 }, 97 }, 98 } 99 assert.NoError(t, a.Client.Create(context.TODO(), &pod)) 100 101 req := admission.Request{} 102 req.Namespace = "default" 103 marshaledPod, err := json.Marshal(pod) 104 assert.NoError(t, err, "Unexpected error marshaling pod") 105 req.Object = runtime.RawExtension{Raw: marshaledPod} 106 res := a.Handle(context.TODO(), req) 107 108 verifyResponse(t, res, 2) 109 } 110 111 // TestMultipleOwnerReference tests the handling of a Pod resource 112 // GIVEN a call to the webhook Handle function 113 // WHEN the pod resource has nested owner references and the 2nd owner reference 114 // is the workload resource 115 // THEN the Handle function should succeed and the pod is mutated 116 func TestMultipleOwnerReference(t *testing.T) { 117 a := newLabelerPodWebhook() 118 119 // Create a deployment with no owner reference 120 u := newUnstructured("apps/v1", "Deployment", "test-deployment") 121 resource := schema.GroupVersionResource{ 122 Group: "apps", 123 Version: "v1", 124 Resource: "deployments", 125 } 126 u.SetLabels(map[string]string{constants.MetricsWorkloadLabel: "testValue"}) 127 _, err := a.DynamicClient.Resource(resource).Namespace("default").Create(context.TODO(), u, metav1.CreateOptions{}) 128 assert.NoError(t, err, "Unexpected error creating deployment") 129 130 // Create a replica set with an owner reference 131 u = newUnstructured("apps/v1", "ReplicaSet", "test-replicaSet") 132 resource = schema.GroupVersionResource{ 133 Group: "apps", 134 Version: "v1", 135 Resource: "replicasets", 136 } 137 ownerReferences := []metav1.OwnerReference{ 138 { 139 Name: "test-deployment", 140 Kind: "Deployment", 141 APIVersion: "apps/v1", 142 }, 143 } 144 u.SetOwnerReferences(ownerReferences) 145 _, err = a.DynamicClient.Resource(resource).Namespace("default").Create(context.TODO(), u, metav1.CreateOptions{}) 146 assert.NoError(t, err, "Unexpected error creating replica set") 147 148 // Create the pod with an owner reference 149 pod := corev1.Pod{ 150 ObjectMeta: metav1.ObjectMeta{ 151 Name: "test", 152 Namespace: "default", 153 OwnerReferences: []metav1.OwnerReference{ 154 { 155 Name: "test-replicaSet", 156 Kind: "ReplicaSet", 157 APIVersion: "apps/v1", 158 }, 159 }, 160 }, 161 } 162 assert.NoError(t, a.Client.Create(context.TODO(), &pod)) 163 164 req := admission.Request{} 165 req.Namespace = "default" 166 marshaledPod, err := json.Marshal(pod) 167 assert.NoError(t, err, "Unexpected error marshaling pod") 168 req.Object = runtime.RawExtension{Raw: marshaledPod} 169 res := a.Handle(context.TODO(), req) 170 171 verifyResponse(t, res, 2) 172 } 173 174 // TestMultipleOwnerReferenceAndWorkloadResources tests the handling of a Pod resource 175 // GIVEN a call to the webhook Handle function 176 // WHEN the pod resource has nested owner references and two owner references are found to be 177 // a workload resource 178 // THEN the Handle function should fail and return an error 179 func TestMultipleOwnerReferenceAndWorkloadResources(t *testing.T) { 180 a := newLabelerPodWebhook() 181 182 // Create a deployment with no owner reference 183 u := newUnstructured("apps/v1", "Deployment", "test-deployment") 184 resource := schema.GroupVersionResource{ 185 Group: "apps", 186 Version: "v1", 187 Resource: "deployments", 188 } 189 u.SetLabels(map[string]string{constants.MetricsWorkloadLabel: "testValue"}) 190 _, err := a.DynamicClient.Resource(resource).Namespace("default").Create(context.TODO(), u, metav1.CreateOptions{}) 191 assert.NoError(t, err, "Unexpected error creating deployment") 192 193 // Create another deployment with no owner reference 194 u = newUnstructured("apps/v1", "Deployment", "test-deployment2") 195 resource = schema.GroupVersionResource{ 196 Group: "apps", 197 Version: "v1", 198 Resource: "deployments", 199 } 200 u.SetLabels(map[string]string{constants.MetricsWorkloadLabel: "testValue"}) 201 _, err = a.DynamicClient.Resource(resource).Namespace("default").Create(context.TODO(), u, metav1.CreateOptions{}) 202 assert.NoError(t, err, "Unexpected error creating deployment") 203 204 // Create a replica set with two owner references 205 u = newUnstructured("apps/v1", "ReplicaSet", "test-replicaSet") 206 resource = schema.GroupVersionResource{ 207 Group: "apps", 208 Version: "v1", 209 Resource: "replicasets", 210 } 211 ownerReferences := []metav1.OwnerReference{ 212 { 213 Name: "test-deployment", 214 Kind: "Deployment", 215 APIVersion: "apps/v1", 216 }, 217 { 218 Name: "test-deployment2", 219 Kind: "Deployment", 220 APIVersion: "apps/v1", 221 }, 222 } 223 u.SetOwnerReferences(ownerReferences) 224 _, err = a.DynamicClient.Resource(resource).Namespace("default").Create(context.TODO(), u, metav1.CreateOptions{}) 225 assert.NoError(t, err, "Unexpected error creating replica set") 226 227 // Create the pod with an owner reference 228 pod := corev1.Pod{ 229 ObjectMeta: metav1.ObjectMeta{ 230 Name: "test", 231 Namespace: "default", 232 OwnerReferences: []metav1.OwnerReference{ 233 { 234 Name: "test-replicaSet", 235 Kind: "ReplicaSet", 236 APIVersion: "apps/v1", 237 }, 238 }, 239 }, 240 } 241 assert.NoError(t, a.Client.Create(context.TODO(), &pod)) 242 243 req := admission.Request{} 244 req.Namespace = "default" 245 marshaledPod, err := json.Marshal(pod) 246 assert.NoError(t, err, "Unexpected error marshaling pod") 247 req.Object = runtime.RawExtension{Raw: marshaledPod} 248 res := a.Handle(context.TODO(), req) 249 250 assert.False(t, res.Allowed) 251 assert.Equal(t, "multiple workload resources found for test, Verrazzano metrics cannot be enabled", res.Result.Message) 252 } 253 254 // TestPodPrometheusAnnotations tests the annotation of a Pod resource 255 // GIVEN a call to the webhook Handle function 256 // WHEN the pod has Prometheus annotations 257 // THEN the Handle function should not overwrite those annotations 258 func TestPodPrometheusAnnotations(t *testing.T) { 259 a := newLabelerPodWebhook() 260 261 // Test data 262 pod := corev1.Pod{ 263 ObjectMeta: metav1.ObjectMeta{ 264 Name: "test", 265 Namespace: "default", 266 Annotations: map[string]string{ 267 PrometheusPortAnnotation: PrometheusPortDefault, 268 PrometheusPathAnnotation: PrometheusPathAnnotation, 269 PrometheusScrapeAnnotation: PrometheusScrapeAnnotation, 270 }, 271 }, 272 } 273 assert.NoError(t, a.Client.Create(context.TODO(), &pod)) 274 275 req := admission.Request{} 276 req.Namespace = "default" 277 marshaledPod, err := json.Marshal(pod) 278 assert.NoError(t, err, "Unexpected error marshaling pod") 279 req.Object = runtime.RawExtension{Raw: marshaledPod} 280 res := a.Handle(context.TODO(), req) 281 282 verifyResponse(t, res, 1) 283 } 284 285 func verifyResponse(t *testing.T, res admission.Response, len int) { 286 assert.True(t, res.Allowed) 287 assert.Len(t, res.Patches, len) 288 for _, patch := range res.Patches { 289 assert.Equal(t, "add", patch.Operation) 290 assert.True(t, patch.Path == "/metadata/labels" || patch.Path == "/metadata/annotations") 291 } 292 }