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  }