github.com/verrazzano/verrazzano@v1.7.0/application-operator/controllers/webhooks/istio_defaulter_test.go (about) 1 // Copyright (c) 2021, 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 "net/http" 10 "testing" 11 12 "github.com/prometheus/client_golang/prometheus/testutil" 13 "github.com/stretchr/testify/assert" 14 cluv1alpha1 "github.com/verrazzano/verrazzano/application-operator/apis/clusters/v1alpha1" 15 "github.com/verrazzano/verrazzano/application-operator/constants" 16 "github.com/verrazzano/verrazzano/application-operator/metricsexporter" 17 istiofake "istio.io/client-go/pkg/clientset/versioned/fake" 18 corev1 "k8s.io/api/core/v1" 19 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 20 "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" 21 "k8s.io/apimachinery/pkg/runtime" 22 "k8s.io/apimachinery/pkg/runtime/schema" 23 dynamicfake "k8s.io/client-go/dynamic/fake" 24 "k8s.io/client-go/kubernetes/fake" 25 ctrlfake "sigs.k8s.io/controller-runtime/pkg/client/fake" 26 "sigs.k8s.io/controller-runtime/pkg/webhook/admission" 27 ) 28 29 // TestHandleBadRequest tests handling an invalid admission.Request 30 // GIVEN an IstioWebhook and an admission.Request 31 // WHEN Handle is called with an invalid admission.Request containing no content 32 // THEN Handle should return an error with http.StatusBadRequest 33 func TestHandleBadRequest(t *testing.T) { 34 35 decoder := decoder() 36 defaulter := &IstioWebhook{} 37 err := defaulter.InjectDecoder(decoder) 38 assert.NoError(t, err, "Unexpected error injecting decoder") 39 req := admission.Request{} 40 res := defaulter.Handle(context.TODO(), req) 41 assert.False(t, res.Allowed) 42 assert.Equal(t, int32(http.StatusBadRequest), res.Result.Code) 43 } 44 45 // TestHandleIstioDisabled tests handling an admission.Request 46 // GIVEN a IstioWebhook and an admission.Request 47 // WHEN Handle is called with an admission.Request containing a pod resource with Istio disabled 48 // THEN Handle should return an Allowed response with no action required 49 func TestHandleIstioDisabled(t *testing.T) { 50 51 defaulter := &IstioWebhook{ 52 DynamicClient: dynamicfake.NewSimpleDynamicClient(runtime.NewScheme()), 53 KubeClient: fake.NewSimpleClientset(), 54 IstioClient: istiofake.NewSimpleClientset(), 55 } 56 // Create a pod with Istio injection disabled 57 p := &corev1.Pod{ 58 ObjectMeta: metav1.ObjectMeta{ 59 Name: "istio-disabled", 60 Namespace: "default", 61 Annotations: map[string]string{ 62 "sidecar.istio.io/inject": "false", 63 }, 64 }, 65 } 66 pod, err := defaulter.KubeClient.CoreV1().Pods("default").Create(context.TODO(), p, metav1.CreateOptions{}) 67 assert.NoError(t, err, "Unexpected error creating pod") 68 69 decoder := decoder() 70 err = defaulter.InjectDecoder(decoder) 71 assert.NoError(t, err, "Unexpected error injecting decoder") 72 req := admission.Request{} 73 req.Namespace = "default" 74 marshaledPod, err := json.Marshal(pod) 75 assert.NoError(t, err, "Unexpected error marshaling pod") 76 req.Object = runtime.RawExtension{Raw: marshaledPod} 77 res := defaulter.Handle(context.TODO(), req) 78 assert.True(t, res.Allowed) 79 assert.Equal(t, metav1.StatusReason("No action required, pod labeled with sidecar.istio.io/inject: false"), res.Result.Reason) 80 } 81 82 // TestHandleNoOnwerReference tests handling an admission.Request 83 // GIVEN a IstioWebhook and an admission.Request 84 // 85 // WHEN Handle is called with an admission.Request containing a pod resource with no owner references 86 // THEN Handle should return an Allowed response with no action required 87 func TestHandleNoOnwerReference(t *testing.T) { 88 89 defaulter := &IstioWebhook{ 90 DynamicClient: dynamicfake.NewSimpleDynamicClient(runtime.NewScheme()), 91 KubeClient: fake.NewSimpleClientset(), 92 IstioClient: istiofake.NewSimpleClientset(), 93 } 94 // Create a simple pod 95 p := &corev1.Pod{ 96 ObjectMeta: metav1.ObjectMeta{ 97 Name: "simple-pod", 98 Namespace: "default", 99 }, 100 } 101 pod, err := defaulter.KubeClient.CoreV1().Pods("default").Create(context.TODO(), p, metav1.CreateOptions{}) 102 assert.NoError(t, err, "Unexpected error creating pod") 103 104 decoder := decoder() 105 err = defaulter.InjectDecoder(decoder) 106 assert.NoError(t, err, "Unexpected error injecting decoder") 107 req := admission.Request{} 108 req.Namespace = "default" 109 marshaledPod, err := json.Marshal(pod) 110 assert.NoError(t, err, "Unexpected error marshaling pod") 111 req.Object = runtime.RawExtension{Raw: marshaledPod} 112 res := defaulter.Handle(context.TODO(), req) 113 assert.True(t, res.Allowed) 114 assert.Equal(t, metav1.StatusReason("No action required, pod is not a child of an ApplicationConfiguration resource"), res.Result.Reason) 115 } 116 117 // TestHandleNoAppConfigOnwerReference tests handling an admission.Request 118 // GIVEN a IstioWebhook and an admission.Request 119 // WHEN Handle is called with an admission.Request containing a pod resource with no parent appconfig owner references 120 // THEN Handle should return an Allowed response with no action required 121 func TestHandleNoAppConfigOnwerReference(t *testing.T) { 122 123 defaulter := &IstioWebhook{ 124 DynamicClient: dynamicfake.NewSimpleDynamicClient(runtime.NewScheme()), 125 KubeClient: fake.NewSimpleClientset(), 126 IstioClient: istiofake.NewSimpleClientset(), 127 } 128 129 u := newUnstructured("apps/v1", "Deployment", "test-deployment") 130 resource := schema.GroupVersionResource{ 131 Group: "apps", 132 Version: "v1", 133 Resource: "deployments", 134 } 135 _, err := defaulter.DynamicClient.Resource(resource).Namespace("default").Create(context.TODO(), u, metav1.CreateOptions{}) 136 assert.NoError(t, err, "Unexpected error creating deployment") 137 138 u = newUnstructured("apps/v1", "ReplicaSet", "test-replicaSet") 139 ownerReferences := []metav1.OwnerReference{ 140 { 141 Name: "test-deployment", 142 Kind: "Deployment", 143 APIVersion: "apps/v1", 144 }, 145 } 146 u.SetOwnerReferences(ownerReferences) 147 resource = schema.GroupVersionResource{ 148 Group: "apps", 149 Version: "v1", 150 Resource: "replicasets", 151 } 152 _, err = defaulter.DynamicClient.Resource(resource).Namespace("default").Create(context.TODO(), u, metav1.CreateOptions{}) 153 assert.NoError(t, err, "Unexpected error creating replica set") 154 155 u = newUnstructured("v1", "Pod", "test-pod") 156 ownerReferences = []metav1.OwnerReference{ 157 { 158 Name: "test-replicaSet", 159 Kind: "ReplicaSet", 160 APIVersion: "apps/v1", 161 }, 162 } 163 u.SetOwnerReferences(ownerReferences) 164 resource = schema.GroupVersionResource{ 165 Group: "", 166 Version: "v1", 167 Resource: "pods", 168 } 169 pod, err := defaulter.DynamicClient.Resource(resource).Namespace("default").Create(context.TODO(), u, metav1.CreateOptions{}) 170 assert.NoError(t, err, "Unexpected error creating pod") 171 172 decoder := decoder() 173 err = defaulter.InjectDecoder(decoder) 174 assert.NoError(t, err, "Unexpected error injecting decoder") 175 req := admission.Request{} 176 req.Namespace = "default" 177 marshaledPod, err := json.Marshal(pod) 178 assert.NoError(t, err, "Unexpected error marshaling pod") 179 req.Object = runtime.RawExtension{Raw: marshaledPod} 180 res := defaulter.Handle(context.TODO(), req) 181 assert.True(t, res.Allowed) 182 assert.Equal(t, metav1.StatusReason("No action required, pod is not a child of an ApplicationConfiguration resource"), res.Result.Reason) 183 } 184 185 // TestHandleAppConfigOnwerReference1 tests handling an admission.Request 186 // GIVEN a IstioWebhook and an admission.Request 187 // WHEN Handle is called with an admission.Request containing a pod resource with a parent appconfig owner reference 188 // 189 // and a default service account referenced by the pod 190 // 191 // THEN Handle should return an Allowed response with patch values 192 func TestHandleAppConfigOnwerReference1(t *testing.T) { 193 194 scheme := runtime.NewScheme() 195 err := cluv1alpha1.AddToScheme(scheme) 196 assert.NoError(t, err, "Unexpected error adding to scheme") 197 client := ctrlfake.NewClientBuilder().WithScheme(scheme).Build() 198 199 defaulter := &IstioWebhook{ 200 Client: client, 201 DynamicClient: dynamicfake.NewSimpleDynamicClient(runtime.NewScheme()), 202 KubeClient: fake.NewSimpleClientset(), 203 IstioClient: istiofake.NewSimpleClientset(), 204 } 205 206 // Create an applicationConfiguration resource 207 u := newUnstructured("core.oam.dev/v1alpha2", "ApplicationConfiguration", "test-appconfig") 208 resource := schema.GroupVersionResource{ 209 Group: "core.oam.dev", 210 Version: "v1alpha2", 211 Resource: "applicationconfigurations", 212 } 213 _, err = defaulter.DynamicClient.Resource(resource).Namespace("default").Create(context.TODO(), u, metav1.CreateOptions{}) 214 assert.NoError(t, err, "Unexpected error creating application config") 215 216 // Create a pod without specifying a service account 217 p := &corev1.Pod{ 218 ObjectMeta: metav1.ObjectMeta{ 219 Name: "test-pod", 220 Namespace: "default", 221 OwnerReferences: []metav1.OwnerReference{ 222 { 223 Name: "test-appconfig", 224 Kind: "ApplicationConfiguration", 225 APIVersion: "core.oam.dev/v1alpha2", 226 }, 227 }, 228 }, 229 } 230 pod, err := defaulter.KubeClient.CoreV1().Pods("default").Create(context.TODO(), p, metav1.CreateOptions{}) 231 assert.NoError(t, err, "Unexpected error creating pod") 232 233 decoder := decoder() 234 err = defaulter.InjectDecoder(decoder) 235 assert.NoError(t, err, "Unexpected error injecting decoder") 236 req := admission.Request{} 237 req.Namespace = "default" 238 marshaledPod, err := json.Marshal(pod) 239 assert.NoError(t, err, "Unexpected error marshaling pod") 240 req.Object = runtime.RawExtension{Raw: marshaledPod} 241 res := defaulter.Handle(context.TODO(), req) 242 assert.True(t, res.Allowed) 243 assert.NotEmpty(t, res.Patches) 244 245 // Get the authorization policy resource we created and do some validations 246 authPolicy, err := defaulter.IstioClient.SecurityV1beta1().AuthorizationPolicies("default").Get(context.TODO(), "test-appconfig", metav1.GetOptions{}) 247 assert.NoError(t, err, "Unexpected error getting authorization policy") 248 assert.Equal(t, authPolicy.Name, "test-appconfig") 249 assert.Equal(t, authPolicy.Namespace, "default") 250 assert.Contains(t, authPolicy.Labels, IstioAppLabel) 251 assert.Equal(t, authPolicy.GetOwnerReferences()[0].Name, "test-appconfig") 252 assert.Equal(t, authPolicy.GetOwnerReferences()[0].Kind, "ApplicationConfiguration") 253 assert.Equal(t, authPolicy.GetOwnerReferences()[0].APIVersion, "core.oam.dev/v1alpha2") 254 assert.Contains(t, authPolicy.Spec.Selector.MatchLabels, IstioAppLabel) 255 assert.Equal(t, len(authPolicy.Spec.GetRules()[0].From[0].Source.Principals), 4) 256 assert.Contains(t, authPolicy.Spec.GetRules()[0].From[0].Source.Principals, "cluster.local/ns/istio-system/sa/istio-ingressgateway-service-account") 257 assert.Contains(t, authPolicy.Spec.GetRules()[0].From[0].Source.Principals, "cluster.local/ns/verrazzano-system/sa/verrazzano-monitoring-operator") 258 assert.Contains(t, authPolicy.Spec.GetRules()[0].From[0].Source.Principals, "cluster.local/ns/default/sa/test-appconfig") 259 } 260 261 // TestHandleAppConfigOnwerReference2 tests handling an admission.Request 262 // GIVEN a IstioWebhook and an admission.Request 263 // WHEN Handle is called with an admission.Request containing a pod resource with a parent appconfig owner reference 264 // 265 // and a non-default service account referenced by the pod 266 // 267 // THEN Handle should return an Allowed response with patch values 268 func TestHandleAppConfigOnwerReference2(t *testing.T) { 269 270 scheme := runtime.NewScheme() 271 err := cluv1alpha1.AddToScheme(scheme) 272 assert.NoError(t, err, "Unexpected error adding to scheme") 273 client := ctrlfake.NewClientBuilder().WithScheme(scheme).Build() 274 275 defaulter := &IstioWebhook{ 276 Client: client, 277 DynamicClient: dynamicfake.NewSimpleDynamicClient(runtime.NewScheme()), 278 KubeClient: fake.NewSimpleClientset(), 279 IstioClient: istiofake.NewSimpleClientset(), 280 } 281 282 // Create a non-default service account 283 sa := &corev1.ServiceAccount{ 284 ObjectMeta: metav1.ObjectMeta{ 285 Name: "test-sa", 286 Namespace: "default"}, 287 } 288 serviceAccount, err := defaulter.KubeClient.CoreV1().ServiceAccounts("default").Create(context.TODO(), sa, metav1.CreateOptions{}) 289 assert.NoError(t, err, "Unexpected error creating service account") 290 291 // Create an applicationConfiguration resource 292 u := newUnstructured("core.oam.dev/v1alpha2", "ApplicationConfiguration", "test-appconfig") 293 resource := schema.GroupVersionResource{ 294 Group: "core.oam.dev", 295 Version: "v1alpha2", 296 Resource: "applicationconfigurations", 297 } 298 _, err = defaulter.DynamicClient.Resource(resource).Namespace("default").Create(context.TODO(), u, metav1.CreateOptions{}) 299 assert.NoError(t, err, "Unexpected error creating application config") 300 301 // Create a pod referencing the service account we created 302 p := &corev1.Pod{ 303 ObjectMeta: metav1.ObjectMeta{ 304 Name: "test-pod", 305 Namespace: "default", 306 OwnerReferences: []metav1.OwnerReference{ 307 { 308 Name: "test-appconfig", 309 Kind: "ApplicationConfiguration", 310 APIVersion: "core.oam.dev/v1alpha2", 311 }, 312 }, 313 }, 314 Spec: corev1.PodSpec{ 315 ServiceAccountName: serviceAccount.Name, 316 }, 317 } 318 pod, err := defaulter.KubeClient.CoreV1().Pods("default").Create(context.TODO(), p, metav1.CreateOptions{}) 319 assert.NoError(t, err, "Unexpected error creating pod") 320 321 decoder := decoder() 322 err = defaulter.InjectDecoder(decoder) 323 assert.NoError(t, err, "Unexpected error injecting decoder") 324 req := admission.Request{} 325 req.Namespace = "default" 326 marshaledPod, err := json.Marshal(pod) 327 assert.NoError(t, err, "Unexpected error marshaling pod") 328 req.Object = runtime.RawExtension{Raw: marshaledPod} 329 res := defaulter.Handle(context.TODO(), req) 330 assert.True(t, res.Allowed) 331 assert.NotEmpty(t, res.Patches) 332 333 // Get the authorization policy resource we created and do some validations 334 authPolicy, err := defaulter.IstioClient.SecurityV1beta1().AuthorizationPolicies("default").Get(context.TODO(), "test-appconfig", metav1.GetOptions{}) 335 assert.NoError(t, err, "Unexpected error getting authorization policy") 336 assert.Equal(t, authPolicy.Name, "test-appconfig") 337 assert.Equal(t, authPolicy.Namespace, "default") 338 assert.Contains(t, authPolicy.Labels, IstioAppLabel) 339 assert.Equal(t, authPolicy.GetOwnerReferences()[0].Name, "test-appconfig") 340 assert.Equal(t, authPolicy.GetOwnerReferences()[0].Kind, "ApplicationConfiguration") 341 assert.Equal(t, authPolicy.GetOwnerReferences()[0].APIVersion, "core.oam.dev/v1alpha2") 342 assert.Contains(t, authPolicy.Spec.Selector.MatchLabels, IstioAppLabel) 343 assert.Equal(t, len(authPolicy.Spec.GetRules()[0].From[0].Source.Principals), 4) 344 assert.Contains(t, authPolicy.Spec.GetRules()[0].From[0].Source.Principals, "cluster.local/ns/istio-system/sa/istio-ingressgateway-service-account") 345 assert.Contains(t, authPolicy.Spec.GetRules()[0].From[0].Source.Principals, "cluster.local/ns/verrazzano-system/sa/verrazzano-monitoring-operator") 346 assert.Contains(t, authPolicy.Spec.GetRules()[0].From[0].Source.Principals, "cluster.local/ns/default/sa/test-sa") 347 } 348 349 // TestHandleAppConfigOnwerReference3 tests handling an admission.Request 350 // GIVEN a IstioWebhook and an admission.Request 351 // WHEN Handle is called twice with an admission.Request containing a pod resource with a parent appconfig owner reference 352 // 353 // A different service account is used on each call. 354 // 355 // THEN Handle should return an Allowed response with patch values 356 func TestHandleAppConfigOnwerReference3(t *testing.T) { 357 358 scheme := runtime.NewScheme() 359 err := cluv1alpha1.AddToScheme(scheme) 360 assert.NoError(t, err, "Unexpected error adding to scheme") 361 client := ctrlfake.NewClientBuilder().WithScheme(scheme).Build() 362 363 defaulter := &IstioWebhook{ 364 Client: client, 365 DynamicClient: dynamicfake.NewSimpleDynamicClient(runtime.NewScheme()), 366 KubeClient: fake.NewSimpleClientset(), 367 IstioClient: istiofake.NewSimpleClientset(), 368 } 369 370 // Create a non-default service account 371 sa := &corev1.ServiceAccount{ 372 ObjectMeta: metav1.ObjectMeta{ 373 Name: "test-sa", 374 Namespace: "default"}, 375 } 376 serviceAccount, err := defaulter.KubeClient.CoreV1().ServiceAccounts("default").Create(context.TODO(), sa, metav1.CreateOptions{}) 377 assert.NoError(t, err, "Unexpected error creating service account") 378 379 // Create an applicationConfiguration resource 380 u := newUnstructured("core.oam.dev/v1alpha2", "ApplicationConfiguration", "test-appconfig") 381 resource := schema.GroupVersionResource{ 382 Group: "core.oam.dev", 383 Version: "v1alpha2", 384 Resource: "applicationconfigurations", 385 } 386 _, err = defaulter.DynamicClient.Resource(resource).Namespace("default").Create(context.TODO(), u, metav1.CreateOptions{}) 387 assert.NoError(t, err, "Unexpected error creating application config") 388 389 // Create a pod referencing the service account we created 390 p := &corev1.Pod{ 391 ObjectMeta: metav1.ObjectMeta{ 392 Name: "test-pod1", 393 Namespace: "default", 394 OwnerReferences: []metav1.OwnerReference{ 395 { 396 Name: "test-appconfig", 397 Kind: "ApplicationConfiguration", 398 APIVersion: "core.oam.dev/v1alpha2", 399 }, 400 }, 401 }, 402 Spec: corev1.PodSpec{ 403 ServiceAccountName: serviceAccount.Name, 404 }, 405 } 406 pod, err := defaulter.KubeClient.CoreV1().Pods("default").Create(context.TODO(), p, metav1.CreateOptions{}) 407 assert.NoError(t, err, "Unexpected error creating pod") 408 409 decoder := decoder() 410 err = defaulter.InjectDecoder(decoder) 411 assert.NoError(t, err, "Unexpected error injecting decoder") 412 req := admission.Request{} 413 req.Namespace = "default" 414 marshaledPod, err := json.Marshal(pod) 415 assert.NoError(t, err, "Unexpected error marshaling pod") 416 req.Object = runtime.RawExtension{Raw: marshaledPod} 417 res := defaulter.Handle(context.TODO(), req) 418 assert.True(t, res.Allowed) 419 assert.NotEmpty(t, res.Patches) 420 421 // Get the authorization policy resource we created and do some validations 422 authPolicy, err := defaulter.IstioClient.SecurityV1beta1().AuthorizationPolicies("default").Get(context.TODO(), "test-appconfig", metav1.GetOptions{}) 423 assert.NoError(t, err, "Unexpected error getting authorization policy") 424 assert.Equal(t, authPolicy.Name, "test-appconfig") 425 assert.Equal(t, authPolicy.Namespace, "default") 426 assert.Contains(t, authPolicy.Labels, IstioAppLabel) 427 assert.Equal(t, authPolicy.GetOwnerReferences()[0].Name, "test-appconfig") 428 assert.Equal(t, authPolicy.GetOwnerReferences()[0].Kind, "ApplicationConfiguration") 429 assert.Equal(t, authPolicy.GetOwnerReferences()[0].APIVersion, "core.oam.dev/v1alpha2") 430 assert.Contains(t, authPolicy.Spec.Selector.MatchLabels, IstioAppLabel) 431 assert.Equal(t, len(authPolicy.Spec.GetRules()[0].From[0].Source.Principals), 4) 432 assert.Contains(t, authPolicy.Spec.GetRules()[0].From[0].Source.Principals, "cluster.local/ns/istio-system/sa/istio-ingressgateway-service-account") 433 assert.Contains(t, authPolicy.Spec.GetRules()[0].From[0].Source.Principals, "cluster.local/ns/verrazzano-system/sa/verrazzano-monitoring-operator") 434 assert.Contains(t, authPolicy.Spec.GetRules()[0].From[0].Source.Principals, "cluster.local/ns/default/sa/test-sa") 435 436 // Create a non-default service account, different than first one we created 437 sa = &corev1.ServiceAccount{ 438 ObjectMeta: metav1.ObjectMeta{ 439 Name: "test-sa2", 440 Namespace: "default"}, 441 } 442 serviceAccount, err = defaulter.KubeClient.CoreV1().ServiceAccounts("default").Create(context.TODO(), sa, metav1.CreateOptions{}) 443 assert.NoError(t, err, "Unexpected error creating service account") 444 445 // Create a pod referencing the second service account we created 446 p = &corev1.Pod{ 447 ObjectMeta: metav1.ObjectMeta{ 448 Name: "test-pod2", 449 Namespace: "default", 450 OwnerReferences: []metav1.OwnerReference{ 451 { 452 Name: "test-appconfig", 453 Kind: "ApplicationConfiguration", 454 APIVersion: "core.oam.dev/v1alpha2", 455 }, 456 }, 457 }, 458 Spec: corev1.PodSpec{ 459 ServiceAccountName: serviceAccount.Name, 460 }, 461 } 462 pod, err = defaulter.KubeClient.CoreV1().Pods("default").Create(context.TODO(), p, metav1.CreateOptions{}) 463 assert.NoError(t, err, "Unexpected error creating pod") 464 465 err = defaulter.InjectDecoder(decoder) 466 assert.NoError(t, err, "Unexpected error injecting decoder") 467 req = admission.Request{} 468 req.Namespace = "default" 469 marshaledPod, err = json.Marshal(pod) 470 assert.NoError(t, err, "Unexpected error marshaling pod") 471 req.Object = runtime.RawExtension{Raw: marshaledPod} 472 res = defaulter.Handle(context.TODO(), req) 473 assert.True(t, res.Allowed) 474 assert.NotEmpty(t, res.Patches) 475 476 // Get the authorization policy resource we created and do some validations 477 authPolicy, err = defaulter.IstioClient.SecurityV1beta1().AuthorizationPolicies("default").Get(context.TODO(), "test-appconfig", metav1.GetOptions{}) 478 assert.NoError(t, err, "Unexpected error getting authorization policy") 479 assert.Equal(t, authPolicy.Name, "test-appconfig") 480 assert.Equal(t, authPolicy.Namespace, "default") 481 assert.Contains(t, authPolicy.Labels, IstioAppLabel) 482 assert.Equal(t, authPolicy.GetOwnerReferences()[0].Name, "test-appconfig") 483 assert.Equal(t, authPolicy.GetOwnerReferences()[0].Kind, "ApplicationConfiguration") 484 assert.Equal(t, authPolicy.GetOwnerReferences()[0].APIVersion, "core.oam.dev/v1alpha2") 485 assert.Contains(t, authPolicy.Spec.Selector.MatchLabels, IstioAppLabel) 486 assert.Equal(t, len(authPolicy.Spec.GetRules()[0].From[0].Source.Principals), 5) 487 assert.Contains(t, authPolicy.Spec.GetRules()[0].From[0].Source.Principals, "cluster.local/ns/istio-system/sa/istio-ingressgateway-service-account") 488 assert.Contains(t, authPolicy.Spec.GetRules()[0].From[0].Source.Principals, "cluster.local/ns/verrazzano-system/sa/verrazzano-monitoring-operator") 489 assert.Contains(t, authPolicy.Spec.GetRules()[0].From[0].Source.Principals, "cluster.local/ns/default/sa/test-sa") 490 assert.Contains(t, authPolicy.Spec.GetRules()[0].From[0].Source.Principals, "cluster.local/ns/default/sa/test-sa2") 491 } 492 493 // TestHandleAppConfigOnwerReference4 tests handling an admission.Request 494 // GIVEN a IstioWebhook and an admission.Request 495 // WHEN Handle is called twice with an admission.Request containing a pod resource with a parent appconfig owner reference 496 // 497 // The same service account is used on each call. 498 // 499 // THEN Handle should return an Allowed response with patch values 500 func TestHandleAppConfigOnwerReference4(t *testing.T) { 501 502 scheme := runtime.NewScheme() 503 err := cluv1alpha1.AddToScheme(scheme) 504 assert.NoError(t, err, "Unexpected error adding to scheme") 505 client := ctrlfake.NewClientBuilder().WithScheme(scheme).Build() 506 507 defaulter := &IstioWebhook{ 508 Client: client, 509 DynamicClient: dynamicfake.NewSimpleDynamicClient(runtime.NewScheme()), 510 KubeClient: fake.NewSimpleClientset(), 511 IstioClient: istiofake.NewSimpleClientset(), 512 } 513 514 // Create a non-default service account 515 sa := &corev1.ServiceAccount{ 516 ObjectMeta: metav1.ObjectMeta{ 517 Name: "test-sa", 518 Namespace: "default"}, 519 } 520 serviceAccount, err := defaulter.KubeClient.CoreV1().ServiceAccounts("default").Create(context.TODO(), sa, metav1.CreateOptions{}) 521 assert.NoError(t, err, "Unexpected error creating service account") 522 523 // Create an applicationConfiguration resource 524 u := newUnstructured("core.oam.dev/v1alpha2", "ApplicationConfiguration", "test-appconfig") 525 resource := schema.GroupVersionResource{ 526 Group: "core.oam.dev", 527 Version: "v1alpha2", 528 Resource: "applicationconfigurations", 529 } 530 _, err = defaulter.DynamicClient.Resource(resource).Namespace("default").Create(context.TODO(), u, metav1.CreateOptions{}) 531 assert.NoError(t, err, "Unexpected error creating application config") 532 533 // Create a pod referencing the service account we created 534 p := &corev1.Pod{ 535 ObjectMeta: metav1.ObjectMeta{ 536 Name: "test-pod1", 537 Namespace: "default", 538 OwnerReferences: []metav1.OwnerReference{ 539 { 540 Name: "test-appconfig", 541 Kind: "ApplicationConfiguration", 542 APIVersion: "core.oam.dev/v1alpha2", 543 }, 544 }, 545 }, 546 Spec: corev1.PodSpec{ 547 ServiceAccountName: serviceAccount.Name, 548 }, 549 } 550 pod, err := defaulter.KubeClient.CoreV1().Pods("default").Create(context.TODO(), p, metav1.CreateOptions{}) 551 assert.NoError(t, err, "Unexpected error creating pod") 552 553 decoder := decoder() 554 err = defaulter.InjectDecoder(decoder) 555 assert.NoError(t, err, "Unexpected error injecting decoder") 556 req := admission.Request{} 557 req.Namespace = "default" 558 marshaledPod, err := json.Marshal(pod) 559 assert.NoError(t, err, "Unexpected error marshaling pod") 560 req.Object = runtime.RawExtension{Raw: marshaledPod} 561 res := defaulter.Handle(context.TODO(), req) 562 assert.True(t, res.Allowed) 563 assert.NotEmpty(t, res.Patches) 564 565 // Get the authorization policy resource we created and do some validations 566 authPolicy, err := defaulter.IstioClient.SecurityV1beta1().AuthorizationPolicies("default").Get(context.TODO(), "test-appconfig", metav1.GetOptions{}) 567 assert.NoError(t, err, "Unexpected error getting authorization policy") 568 assert.Equal(t, authPolicy.Name, "test-appconfig") 569 assert.Equal(t, authPolicy.Namespace, "default") 570 assert.Contains(t, authPolicy.Labels, IstioAppLabel) 571 assert.Equal(t, authPolicy.GetOwnerReferences()[0].Name, "test-appconfig") 572 assert.Equal(t, authPolicy.GetOwnerReferences()[0].Kind, "ApplicationConfiguration") 573 assert.Equal(t, authPolicy.GetOwnerReferences()[0].APIVersion, "core.oam.dev/v1alpha2") 574 assert.Contains(t, authPolicy.Spec.Selector.MatchLabels, IstioAppLabel) 575 assert.Equal(t, len(authPolicy.Spec.GetRules()[0].From[0].Source.Principals), 4) 576 assert.Contains(t, authPolicy.Spec.GetRules()[0].From[0].Source.Principals, "cluster.local/ns/istio-system/sa/istio-ingressgateway-service-account") 577 assert.Contains(t, authPolicy.Spec.GetRules()[0].From[0].Source.Principals, "cluster.local/ns/verrazzano-system/sa/verrazzano-monitoring-operator") 578 assert.Contains(t, authPolicy.Spec.GetRules()[0].From[0].Source.Principals, "cluster.local/ns/default/sa/test-sa") 579 580 // Create a pod referencing the second service account we created 581 p = &corev1.Pod{ 582 ObjectMeta: metav1.ObjectMeta{ 583 Name: "test-pod2", 584 Namespace: "default", 585 OwnerReferences: []metav1.OwnerReference{ 586 { 587 Name: "test-appconfig", 588 Kind: "ApplicationConfiguration", 589 APIVersion: "core.oam.dev/v1alpha2", 590 }, 591 }, 592 }, 593 Spec: corev1.PodSpec{ 594 ServiceAccountName: serviceAccount.Name, 595 }, 596 } 597 pod, err = defaulter.KubeClient.CoreV1().Pods("default").Create(context.TODO(), p, metav1.CreateOptions{}) 598 assert.NoError(t, err, "Unexpected error creating pod") 599 600 err = defaulter.InjectDecoder(decoder) 601 assert.NoError(t, err, "Unexpected error injecting decoder") 602 req = admission.Request{} 603 req.Namespace = "default" 604 marshaledPod, err = json.Marshal(pod) 605 assert.NoError(t, err, "Unexpected error marshaling pod") 606 req.Object = runtime.RawExtension{Raw: marshaledPod} 607 res = defaulter.Handle(context.TODO(), req) 608 assert.True(t, res.Allowed) 609 assert.NotEmpty(t, res.Patches) 610 611 // Get the authorization policy resource we created and do some validations. 612 authPolicy, err = defaulter.IstioClient.SecurityV1beta1().AuthorizationPolicies("default").Get(context.TODO(), "test-appconfig", metav1.GetOptions{}) 613 assert.NoError(t, err, "Unexpected error getting authorization policy") 614 assert.Equal(t, authPolicy.Name, "test-appconfig") 615 assert.Equal(t, authPolicy.Namespace, "default") 616 assert.Contains(t, authPolicy.Labels, IstioAppLabel) 617 assert.Equal(t, authPolicy.GetOwnerReferences()[0].Name, "test-appconfig") 618 assert.Equal(t, authPolicy.GetOwnerReferences()[0].Kind, "ApplicationConfiguration") 619 assert.Equal(t, authPolicy.GetOwnerReferences()[0].APIVersion, "core.oam.dev/v1alpha2") 620 assert.Contains(t, authPolicy.Spec.Selector.MatchLabels, IstioAppLabel) 621 assert.Equal(t, len(authPolicy.Spec.GetRules()[0].From[0].Source.Principals), 4) 622 assert.Contains(t, authPolicy.Spec.GetRules()[0].From[0].Source.Principals, "cluster.local/ns/istio-system/sa/istio-ingressgateway-service-account") 623 assert.Contains(t, authPolicy.Spec.GetRules()[0].From[0].Source.Principals, "cluster.local/ns/verrazzano-system/sa/verrazzano-monitoring-operator") 624 assert.Contains(t, authPolicy.Spec.GetRules()[0].From[0].Source.Principals, "cluster.local/ns/default/sa/test-sa") 625 } 626 627 // TestHandleProject1 tests handling an admission.Request 628 // GIVEN a IstioWebhook and an admission.Request 629 // WHEN Handle is called with an admission.Request containing a pod resource with a parent appconfig owner reference 630 // 631 // and a project that matches the namespace of pod resource 632 // 633 // THEN Handle should return an Allowed response with patch values 634 func TestHandleProject1(t *testing.T) { 635 636 scheme := runtime.NewScheme() 637 err := cluv1alpha1.AddToScheme(scheme) 638 assert.NoError(t, err, "Unexpected error adding to scheme") 639 client := ctrlfake.NewClientBuilder().WithScheme(scheme).Build() 640 641 defaulter := &IstioWebhook{ 642 Client: client, 643 DynamicClient: dynamicfake.NewSimpleDynamicClient(runtime.NewScheme()), 644 KubeClient: fake.NewSimpleClientset(), 645 IstioClient: istiofake.NewSimpleClientset(), 646 } 647 648 // Create a project in the verrazzano-mc namespace 649 project := &cluv1alpha1.VerrazzanoProject{ 650 ObjectMeta: metav1.ObjectMeta{ 651 Name: "test-project", 652 Namespace: "verrazzano-mc", 653 }, 654 Spec: cluv1alpha1.VerrazzanoProjectSpec{ 655 Template: cluv1alpha1.ProjectTemplate{ 656 Namespaces: []cluv1alpha1.NamespaceTemplate{ 657 {Metadata: metav1.ObjectMeta{ 658 Name: "default", 659 }}, 660 }, 661 }, 662 Placement: cluv1alpha1.Placement{ 663 Clusters: []cluv1alpha1.Cluster{ 664 { 665 Name: constants.DefaultClusterName, 666 }, 667 }, 668 }, 669 }, 670 } 671 err = client.Create(context.TODO(), project) 672 assert.NoError(t, err, "Unexpected error creating Verrazzano project") 673 674 // Create an applicationConfiguration resource 675 u := newUnstructured("core.oam.dev/v1alpha2", "ApplicationConfiguration", "test-appconfig") 676 resource := schema.GroupVersionResource{ 677 Group: "core.oam.dev", 678 Version: "v1alpha2", 679 Resource: "applicationconfigurations", 680 } 681 682 _, err = defaulter.DynamicClient.Resource(resource).Namespace("default").Create(context.TODO(), u, metav1.CreateOptions{}) 683 assert.NoError(t, err, "Unexpected error creating application config") 684 685 // Create a pod without specifying a service account 686 p := &corev1.Pod{ 687 ObjectMeta: metav1.ObjectMeta{ 688 Name: "test-pod", 689 Namespace: "default", 690 OwnerReferences: []metav1.OwnerReference{ 691 { 692 Name: "test-appconfig", 693 Kind: "ApplicationConfiguration", 694 APIVersion: "core.oam.dev/v1alpha2", 695 }, 696 }, 697 }, 698 } 699 pod, err := defaulter.KubeClient.CoreV1().Pods("default").Create(context.TODO(), p, metav1.CreateOptions{}) 700 assert.NoError(t, err, "Unexpected error creating pod") 701 702 decoder := decoder() 703 err = defaulter.InjectDecoder(decoder) 704 assert.NoError(t, err, "Unexpected error injecting decoder") 705 req := admission.Request{} 706 req.Namespace = "default" 707 marshaledPod, err := json.Marshal(pod) 708 assert.NoError(t, err, "Unexpected error marshaling pod") 709 req.Object = runtime.RawExtension{Raw: marshaledPod} 710 res := defaulter.Handle(context.TODO(), req) 711 assert.True(t, res.Allowed) 712 assert.NotEmpty(t, res.Patches) 713 714 // Get the authorization policy resource we created and do some validations 715 authPolicy, err := defaulter.IstioClient.SecurityV1beta1().AuthorizationPolicies("default").Get(context.TODO(), "test-appconfig", metav1.GetOptions{}) 716 assert.NoError(t, err, "Unexpected error getting authorization policy") 717 assert.Equal(t, len(authPolicy.Spec.GetRules()[0].From[0].Source.Principals), 4) 718 assert.Contains(t, authPolicy.Spec.GetRules()[0].From[0].Source.Principals, "cluster.local/ns/istio-system/sa/istio-ingressgateway-service-account") 719 assert.Contains(t, authPolicy.Spec.GetRules()[0].From[0].Source.Principals, "cluster.local/ns/verrazzano-system/sa/verrazzano-monitoring-operator") 720 assert.Contains(t, authPolicy.Spec.GetRules()[0].From[0].Source.Principals, "cluster.local/ns/default/sa/test-appconfig") 721 } 722 723 // TestHandleProject2 tests handling an admission.Request 724 // GIVEN a IstioWebhook and an admission.Request 725 // WHEN Handle is called twice with an admission.Request containing a pod resource with a parent appconfig owner reference 726 // 727 // and a project that matches the namespace of pod resource. There are 2 different appconfigs. 728 // 729 // THEN Handle should return an Allowed response with patch values 730 func TestHandleProject2(t *testing.T) { 731 732 scheme := runtime.NewScheme() 733 err := cluv1alpha1.AddToScheme(scheme) 734 assert.NoError(t, err, "Unexpected error adding to scheme") 735 client := ctrlfake.NewClientBuilder().WithScheme(scheme).Build() 736 737 defaulter := &IstioWebhook{ 738 Client: client, 739 DynamicClient: dynamicfake.NewSimpleDynamicClient(runtime.NewScheme()), 740 KubeClient: fake.NewSimpleClientset(), 741 IstioClient: istiofake.NewSimpleClientset(), 742 } 743 744 // Create a project in the verrazzano-mc namespace 745 project := &cluv1alpha1.VerrazzanoProject{ 746 ObjectMeta: metav1.ObjectMeta{ 747 Name: "test-project", 748 Namespace: "verrazzano-mc", 749 }, 750 Spec: cluv1alpha1.VerrazzanoProjectSpec{ 751 Template: cluv1alpha1.ProjectTemplate{ 752 Namespaces: []cluv1alpha1.NamespaceTemplate{ 753 { 754 Metadata: metav1.ObjectMeta{ 755 Name: "app-namespace", 756 }, 757 }, 758 { 759 Metadata: metav1.ObjectMeta{ 760 Name: "default", 761 }, 762 }, 763 }, 764 }, 765 Placement: cluv1alpha1.Placement{ 766 Clusters: []cluv1alpha1.Cluster{ 767 { 768 Name: constants.DefaultClusterName, 769 }, 770 }, 771 }, 772 }, 773 } 774 err = client.Create(context.TODO(), project) 775 assert.NoError(t, err, "Unexpected error creating Verrazzano project") 776 777 // Create an applicationConfiguration resource 778 u := newUnstructured("core.oam.dev/v1alpha2", "ApplicationConfiguration", "test-appconfig") 779 resource := schema.GroupVersionResource{ 780 Group: "core.oam.dev", 781 Version: "v1alpha2", 782 Resource: "applicationconfigurations", 783 } 784 785 _, err = defaulter.DynamicClient.Resource(resource).Namespace("default").Create(context.TODO(), u, metav1.CreateOptions{}) 786 assert.NoError(t, err, "Unexpected error creating application config") 787 788 // Create a pod without specifying a service account 789 p := &corev1.Pod{ 790 ObjectMeta: metav1.ObjectMeta{ 791 Name: "test-pod", 792 Namespace: "default", 793 OwnerReferences: []metav1.OwnerReference{ 794 { 795 Name: "test-appconfig", 796 Kind: "ApplicationConfiguration", 797 APIVersion: "core.oam.dev/v1alpha2", 798 }, 799 }, 800 }, 801 } 802 pod, err := defaulter.KubeClient.CoreV1().Pods("default").Create(context.TODO(), p, metav1.CreateOptions{}) 803 assert.NoError(t, err, "Unexpected error creating pod") 804 805 decoder := decoder() 806 err = defaulter.InjectDecoder(decoder) 807 assert.NoError(t, err, "Unexpected error injecting decoder") 808 req := admission.Request{} 809 req.Namespace = "default" 810 marshaledPod, err := json.Marshal(pod) 811 assert.NoError(t, err, "Unexpected error marshaling pod") 812 req.Object = runtime.RawExtension{Raw: marshaledPod} 813 res := defaulter.Handle(context.TODO(), req) 814 assert.True(t, res.Allowed) 815 assert.NotEmpty(t, res.Patches) 816 817 // Get the authorization policy resource we created and do some validations 818 authPolicy, err := defaulter.IstioClient.SecurityV1beta1().AuthorizationPolicies("default").Get(context.TODO(), "test-appconfig", metav1.GetOptions{}) 819 assert.NoError(t, err, "Unexpected error getting authorization policy") 820 assert.Equal(t, len(authPolicy.Spec.GetRules()[0].From[0].Source.Principals), 4) 821 assert.Contains(t, authPolicy.Spec.GetRules()[0].From[0].Source.Principals, "cluster.local/ns/istio-system/sa/istio-ingressgateway-service-account") 822 assert.Contains(t, authPolicy.Spec.GetRules()[0].From[0].Source.Principals, "cluster.local/ns/verrazzano-system/sa/verrazzano-monitoring-operator") 823 assert.Contains(t, authPolicy.Spec.GetRules()[0].From[0].Source.Principals, "cluster.local/ns/default/sa/test-appconfig") 824 825 // Create a 2nd applicationConfiguration resource 826 u = newUnstructured("core.oam.dev/v1alpha2", "ApplicationConfiguration", "test-appconfig2") 827 resource = schema.GroupVersionResource{ 828 Group: "core.oam.dev", 829 Version: "v1alpha2", 830 Resource: "applicationconfigurations", 831 } 832 833 _, err = defaulter.DynamicClient.Resource(resource).Namespace("default").Create(context.TODO(), u, metav1.CreateOptions{}) 834 assert.NoError(t, err, "Unexpected error creating application config") 835 836 // Create a 2nd pod without specifying a service account and referencing the 2nd applicationConfiguration resource 837 p = &corev1.Pod{ 838 ObjectMeta: metav1.ObjectMeta{ 839 Name: "test-pod2", 840 Namespace: "default", 841 OwnerReferences: []metav1.OwnerReference{ 842 { 843 Name: "test-appconfig2", 844 Kind: "ApplicationConfiguration", 845 APIVersion: "core.oam.dev/v1alpha2", 846 }, 847 }, 848 }, 849 } 850 pod, err = defaulter.KubeClient.CoreV1().Pods("default").Create(context.TODO(), p, metav1.CreateOptions{}) 851 assert.NoError(t, err, "Unexpected error creating pod") 852 853 // err = defaulter.InjectDecoder(decoder) 854 // assert.NoError(t, err, "Unexpected error injecting decoder") 855 req = admission.Request{} 856 req.Namespace = "default" 857 marshaledPod, err = json.Marshal(pod) 858 assert.NoError(t, err, "Unexpected error marshaling pod") 859 req.Object = runtime.RawExtension{Raw: marshaledPod} 860 res = defaulter.Handle(context.TODO(), req) 861 assert.True(t, res.Allowed) 862 assert.NotEmpty(t, res.Patches) 863 864 // Get the test-appconfig authorization policy resource we created and do some validations 865 authPolicy, err = defaulter.IstioClient.SecurityV1beta1().AuthorizationPolicies("default").Get(context.TODO(), "test-appconfig", metav1.GetOptions{}) 866 assert.NoError(t, err, "Unexpected error getting authorization policy") 867 assert.Equal(t, len(authPolicy.Spec.GetRules()[0].From[0].Source.Principals), 5) 868 assert.Contains(t, authPolicy.Spec.GetRules()[0].From[0].Source.Principals, "cluster.local/ns/istio-system/sa/istio-ingressgateway-service-account") 869 assert.Contains(t, authPolicy.Spec.GetRules()[0].From[0].Source.Principals, "cluster.local/ns/verrazzano-system/sa/verrazzano-monitoring-operator") 870 assert.Contains(t, authPolicy.Spec.GetRules()[0].From[0].Source.Principals, "cluster.local/ns/default/sa/test-appconfig") 871 assert.Contains(t, authPolicy.Spec.GetRules()[0].From[0].Source.Principals, "cluster.local/ns/default/sa/test-appconfig2") 872 873 // Get the test-appconfig2 authorization policy resource we created and do some validations 874 authPolicy, err = defaulter.IstioClient.SecurityV1beta1().AuthorizationPolicies("default").Get(context.TODO(), "test-appconfig2", metav1.GetOptions{}) 875 assert.NoError(t, err, "Unexpected error getting authorization policy") 876 assert.Equal(t, len(authPolicy.Spec.GetRules()[0].From[0].Source.Principals), 5) 877 assert.Contains(t, authPolicy.Spec.GetRules()[0].From[0].Source.Principals, "cluster.local/ns/istio-system/sa/istio-ingressgateway-service-account") 878 assert.Contains(t, authPolicy.Spec.GetRules()[0].From[0].Source.Principals, "cluster.local/ns/verrazzano-system/sa/verrazzano-monitoring-operator") 879 assert.Contains(t, authPolicy.Spec.GetRules()[0].From[0].Source.Principals, "cluster.local/ns/default/sa/test-appconfig") 880 assert.Contains(t, authPolicy.Spec.GetRules()[0].From[0].Source.Principals, "cluster.local/ns/default/sa/test-appconfig2") 881 } 882 883 // TestHandleProject3 tests handling an admission.Request 884 // GIVEN a IstioWebhook and an admission.Request 885 // WHEN Handle is called twice with an admission.Request containing a pod resource with a parent appconfig owner reference 886 // 887 // and a project that does not match the namespace of pod resource. There are 2 different appconfigs. 888 // 889 // THEN Handle should return an Allowed response with patch values 890 func TestHandleProject3(t *testing.T) { 891 892 scheme := runtime.NewScheme() 893 err := cluv1alpha1.AddToScheme(scheme) 894 assert.NoError(t, err, "Unexpected error adding to scheme") 895 client := ctrlfake.NewClientBuilder().WithScheme(scheme).Build() 896 897 defaulter := &IstioWebhook{ 898 Client: client, 899 DynamicClient: dynamicfake.NewSimpleDynamicClient(runtime.NewScheme()), 900 KubeClient: fake.NewSimpleClientset(), 901 IstioClient: istiofake.NewSimpleClientset(), 902 } 903 904 // Create a project in the verrazzano-mc namespace 905 project := &cluv1alpha1.VerrazzanoProject{ 906 ObjectMeta: metav1.ObjectMeta{ 907 Name: "test-project", 908 Namespace: "verrazzano-mc", 909 }, 910 Spec: cluv1alpha1.VerrazzanoProjectSpec{ 911 Template: cluv1alpha1.ProjectTemplate{ 912 Namespaces: []cluv1alpha1.NamespaceTemplate{ 913 { 914 Metadata: metav1.ObjectMeta{ 915 Name: "app-namespace", 916 }, 917 }, 918 { 919 Metadata: metav1.ObjectMeta{ 920 Name: "app-namespace2", 921 }, 922 }, 923 }, 924 }, 925 Placement: cluv1alpha1.Placement{ 926 Clusters: []cluv1alpha1.Cluster{ 927 { 928 Name: constants.DefaultClusterName, 929 }, 930 }, 931 }, 932 }, 933 } 934 err = client.Create(context.TODO(), project) 935 assert.NoError(t, err, "Unexpected error creating Verrazzano project") 936 937 // Create an applicationConfiguration resource 938 u := newUnstructured("core.oam.dev/v1alpha2", "ApplicationConfiguration", "test-appconfig") 939 resource := schema.GroupVersionResource{ 940 Group: "core.oam.dev", 941 Version: "v1alpha2", 942 Resource: "applicationconfigurations", 943 } 944 945 _, err = defaulter.DynamicClient.Resource(resource).Namespace("default").Create(context.TODO(), u, metav1.CreateOptions{}) 946 assert.NoError(t, err, "Unexpected error creating application config") 947 948 // Create a pod without specifying a service account 949 p := &corev1.Pod{ 950 ObjectMeta: metav1.ObjectMeta{ 951 Name: "test-pod", 952 Namespace: "default", 953 OwnerReferences: []metav1.OwnerReference{ 954 { 955 Name: "test-appconfig", 956 Kind: "ApplicationConfiguration", 957 APIVersion: "core.oam.dev/v1alpha2", 958 }, 959 }, 960 }, 961 } 962 pod, err := defaulter.KubeClient.CoreV1().Pods("default").Create(context.TODO(), p, metav1.CreateOptions{}) 963 assert.NoError(t, err, "Unexpected error creating pod") 964 965 decoder := decoder() 966 err = defaulter.InjectDecoder(decoder) 967 assert.NoError(t, err, "Unexpected error injecting decoder") 968 req := admission.Request{} 969 req.Namespace = "default" 970 marshaledPod, err := json.Marshal(pod) 971 assert.NoError(t, err, "Unexpected error marshaling pod") 972 req.Object = runtime.RawExtension{Raw: marshaledPod} 973 res := defaulter.Handle(context.TODO(), req) 974 assert.True(t, res.Allowed) 975 assert.NotEmpty(t, res.Patches) 976 977 // Get the authorization policy resource we created and do some validations 978 authPolicy, err := defaulter.IstioClient.SecurityV1beta1().AuthorizationPolicies("default").Get(context.TODO(), "test-appconfig", metav1.GetOptions{}) 979 assert.NoError(t, err, "Unexpected error getting authorization policy") 980 assert.Equal(t, len(authPolicy.Spec.GetRules()[0].From[0].Source.Principals), 4) 981 assert.Contains(t, authPolicy.Spec.GetRules()[0].From[0].Source.Principals, "cluster.local/ns/istio-system/sa/istio-ingressgateway-service-account") 982 assert.Contains(t, authPolicy.Spec.GetRules()[0].From[0].Source.Principals, "cluster.local/ns/verrazzano-system/sa/verrazzano-monitoring-operator") 983 assert.Contains(t, authPolicy.Spec.GetRules()[0].From[0].Source.Principals, "cluster.local/ns/default/sa/test-appconfig") 984 985 // Create a 2nd applicationConfiguration resource 986 u = newUnstructured("core.oam.dev/v1alpha2", "ApplicationConfiguration", "test-appconfig2") 987 resource = schema.GroupVersionResource{ 988 Group: "core.oam.dev", 989 Version: "v1alpha2", 990 Resource: "applicationconfigurations", 991 } 992 993 _, err = defaulter.DynamicClient.Resource(resource).Namespace("default").Create(context.TODO(), u, metav1.CreateOptions{}) 994 assert.NoError(t, err, "Unexpected error creating application config") 995 996 // Create a 2nd pod without specifying a service account and referencing the 2nd applicationConfiguration resource 997 p = &corev1.Pod{ 998 ObjectMeta: metav1.ObjectMeta{ 999 Name: "test-pod2", 1000 Namespace: "default", 1001 OwnerReferences: []metav1.OwnerReference{ 1002 { 1003 Name: "test-appconfig2", 1004 Kind: "ApplicationConfiguration", 1005 APIVersion: "core.oam.dev/v1alpha2", 1006 }, 1007 }, 1008 }, 1009 } 1010 pod, err = defaulter.KubeClient.CoreV1().Pods("default").Create(context.TODO(), p, metav1.CreateOptions{}) 1011 assert.NoError(t, err, "Unexpected error creating pod") 1012 1013 // err = defaulter.InjectDecoder(decoder) 1014 // assert.NoError(t, err, "Unexpected error injecting decoder") 1015 req = admission.Request{} 1016 req.Namespace = "default" 1017 marshaledPod, err = json.Marshal(pod) 1018 assert.NoError(t, err, "Unexpected error marshaling pod") 1019 req.Object = runtime.RawExtension{Raw: marshaledPod} 1020 res = defaulter.Handle(context.TODO(), req) 1021 assert.True(t, res.Allowed) 1022 assert.NotEmpty(t, res.Patches) 1023 1024 // Get the test-appconfig authorization policy resource we created and do some validations 1025 authPolicy, err = defaulter.IstioClient.SecurityV1beta1().AuthorizationPolicies("default").Get(context.TODO(), "test-appconfig", metav1.GetOptions{}) 1026 assert.NoError(t, err, "Unexpected error getting authorization policy") 1027 assert.Equal(t, len(authPolicy.Spec.GetRules()[0].From[0].Source.Principals), 4) 1028 assert.Contains(t, authPolicy.Spec.GetRules()[0].From[0].Source.Principals, "cluster.local/ns/istio-system/sa/istio-ingressgateway-service-account") 1029 assert.Contains(t, authPolicy.Spec.GetRules()[0].From[0].Source.Principals, "cluster.local/ns/verrazzano-system/sa/verrazzano-monitoring-operator") 1030 assert.Contains(t, authPolicy.Spec.GetRules()[0].From[0].Source.Principals, "cluster.local/ns/default/sa/test-appconfig") 1031 1032 // Get the test-appconfig2 authorization policy resource we created and do some validations 1033 authPolicy, err = defaulter.IstioClient.SecurityV1beta1().AuthorizationPolicies("default").Get(context.TODO(), "test-appconfig2", metav1.GetOptions{}) 1034 assert.NoError(t, err, "Unexpected error getting authorization policy") 1035 assert.Equal(t, len(authPolicy.Spec.GetRules()[0].From[0].Source.Principals), 4) 1036 assert.Contains(t, authPolicy.Spec.GetRules()[0].From[0].Source.Principals, "cluster.local/ns/istio-system/sa/istio-ingressgateway-service-account") 1037 assert.Contains(t, authPolicy.Spec.GetRules()[0].From[0].Source.Principals, "cluster.local/ns/verrazzano-system/sa/verrazzano-monitoring-operator") 1038 assert.Contains(t, authPolicy.Spec.GetRules()[0].From[0].Source.Principals, "cluster.local/ns/default/sa/test-appconfig2") 1039 } 1040 1041 // TestHandleFailed tests to make sure the failure metric is being exposed 1042 func TestIstioHandleFailed(t *testing.T) { 1043 1044 assert := assert.New(t) 1045 // Create a request and decode(Handle) it 1046 decoder := decoder() 1047 defaulter := &IstioWebhook{} 1048 _ = defaulter.InjectDecoder(decoder) 1049 req := admission.Request{} 1050 defaulter.Handle(context.TODO(), req) 1051 reconcileerrorCounterObject, err := metricsexporter.GetSimpleCounterMetric(metricsexporter.IstioHandleError) 1052 assert.NoError(err) 1053 // Expect a call to fetch the error 1054 reconcileFailedCounterBefore := testutil.ToFloat64(reconcileerrorCounterObject.Get()) 1055 reconcileerrorCounterObject.Get().Inc() 1056 reconcileFailedCounterAfter := testutil.ToFloat64(reconcileerrorCounterObject.Get()) 1057 assert.Equal(reconcileFailedCounterBefore, reconcileFailedCounterAfter-1) 1058 } 1059 func newUnstructured(apiVersion string, kind string, name string) *unstructured.Unstructured { 1060 return &unstructured.Unstructured{ 1061 Object: map[string]interface{}{ 1062 "apiVersion": apiVersion, 1063 "kind": kind, 1064 "metadata": map[string]interface{}{ 1065 "namespace": "default", 1066 "name": name, 1067 }, 1068 }, 1069 } 1070 }