github.com/verrazzano/verrazzano@v1.7.0/application-operator/controllers/webhooks/multiclustercomponent_webhook_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 "testing" 9 10 "github.com/prometheus/client_golang/prometheus/testutil" 11 "github.com/stretchr/testify/assert" 12 v1alpha12 "github.com/verrazzano/verrazzano/application-operator/apis/clusters/v1alpha1" 13 "github.com/verrazzano/verrazzano/application-operator/constants" 14 "github.com/verrazzano/verrazzano/application-operator/metricsexporter" 15 "github.com/verrazzano/verrazzano/cluster-operator/apis/clusters/v1alpha1" 16 admissionv1 "k8s.io/api/admission/v1" 17 corev1 "k8s.io/api/core/v1" 18 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 19 "sigs.k8s.io/controller-runtime/pkg/client/fake" 20 "sigs.k8s.io/controller-runtime/pkg/webhook/admission" 21 ) 22 23 // newMultiClusterComponentValidator creates a new MultiClusterComponentValidator 24 func newMultiClusterComponentValidator() MultiClusterComponentValidator { 25 scheme := newScheme() 26 decoder, _ := admission.NewDecoder(scheme) 27 cli := fake.NewClientBuilder().WithScheme(scheme).Build() 28 v := MultiClusterComponentValidator{client: cli, decoder: decoder} 29 return v 30 } 31 32 // TestValidationFailureForMultiClusterComponentCreationWithoutTargetClusters tests preventing the creation 33 // of a MultiClusterComponent resources that is missing Placement information. 34 // GIVEN a call to validate a MultiClusterComponent resource 35 // WHEN the MultiClusterComponent resource is missing Placement information 36 // THEN the validation should fail. 37 func TestValidationFailureForMultiClusterComponentCreationWithoutTargetClusters(t *testing.T) { 38 asrt := assert.New(t) 39 v := newMultiClusterComponentValidator() 40 p := v1alpha12.MultiClusterComponent{ 41 ObjectMeta: metav1.ObjectMeta{ 42 Name: "test-mccomponent-name", 43 Namespace: constants.VerrazzanoMultiClusterNamespace, 44 }, 45 Spec: v1alpha12.MultiClusterComponentSpec{}, 46 } 47 48 req := newAdmissionRequest(admissionv1.Create, p) 49 res := v.Handle(context.TODO(), req) 50 asrt.False(res.Allowed, "Expected multi-cluster component validation to fail due to missing placement information.") 51 asrt.Contains(res.Result.Reason, "target cluster") 52 53 req = newAdmissionRequest(admissionv1.Update, p) 54 res = v.Handle(context.TODO(), req) 55 asrt.False(res.Allowed, "Expected multi-cluster component validation to fail due to missing placement information.") 56 asrt.Contains(res.Result.Reason, "target cluster") 57 } 58 59 // TestValidationFailureForMultiClusterComponentCreationTargetingMissingManagedCluster tests preventing the creation 60 // of a MultiClusterComponent resources that references a non-existent managed cluster. 61 // GIVEN a call to validate a MultiClusterComponent resource 62 // WHEN the MultiClusterComponent resource references a VerrazzanoManagedCluster that does not exist 63 // THEN the validation should fail. 64 func TestValidationFailureForMultiClusterComponentCreationTargetingMissingManagedCluster(t *testing.T) { 65 asrt := assert.New(t) 66 v := newMultiClusterComponentValidator() 67 p := v1alpha12.MultiClusterComponent{ 68 ObjectMeta: metav1.ObjectMeta{ 69 Name: "test-mccomponent-name", 70 Namespace: constants.VerrazzanoMultiClusterNamespace, 71 }, 72 Spec: v1alpha12.MultiClusterComponentSpec{ 73 Placement: v1alpha12.Placement{ 74 Clusters: []v1alpha12.Cluster{{Name: "invalid-cluster-name"}}, 75 }, 76 }, 77 } 78 79 req := newAdmissionRequest(admissionv1.Create, p) 80 res := v.Handle(context.TODO(), req) 81 asrt.False(res.Allowed, "Expected multi-cluster component validation to fail due to missing placement information.") 82 asrt.Contains(res.Result.Reason, "invalid-cluster-name") 83 84 req = newAdmissionRequest(admissionv1.Update, p) 85 res = v.Handle(context.TODO(), req) 86 asrt.False(res.Allowed, "Expected multi-cluster component validation to fail due to missing placement information.") 87 asrt.Contains(res.Result.Reason, "invalid-cluster-name") 88 } 89 90 // TestValidationSuccessForMultiClusterComponentCreationTargetingExistingManagedCluster tests allowing the creation 91 // of a MultiClusterComponent resources that references an existent managed cluster. 92 // GIVEN a call to validate a MultiClusterComponent resource 93 // WHEN the MultiClusterComponent resource references a VerrazzanoManagedCluster that does exist 94 // THEN the validation should pass. 95 func TestValidationSuccessForMultiClusterComponentCreationTargetingExistingManagedCluster(t *testing.T) { 96 asrt := assert.New(t) 97 v := newMultiClusterComponentValidator() 98 mc := v1alpha1.VerrazzanoManagedCluster{ 99 ObjectMeta: metav1.ObjectMeta{ 100 Name: "valid-cluster-name", 101 Namespace: constants.VerrazzanoMultiClusterNamespace, 102 }, 103 Spec: v1alpha1.VerrazzanoManagedClusterSpec{ 104 CASecret: "test-secret", 105 ManagedClusterManifestSecret: "test-cluster-manifest-secret", 106 ServiceAccount: "test-service-account", 107 }, 108 } 109 mcc := v1alpha12.MultiClusterComponent{ 110 ObjectMeta: metav1.ObjectMeta{ 111 Name: "test-mccomponent-name", 112 Namespace: "application-ns", 113 }, 114 Spec: v1alpha12.MultiClusterComponentSpec{ 115 Placement: v1alpha12.Placement{ 116 Clusters: []v1alpha12.Cluster{{Name: "valid-cluster-name"}}, 117 }, 118 }, 119 } 120 vp := v1alpha12.VerrazzanoProject{ 121 ObjectMeta: metav1.ObjectMeta{ 122 Name: "test-verrazzanoproject-name", 123 Namespace: constants.VerrazzanoMultiClusterNamespace, 124 }, 125 Spec: v1alpha12.VerrazzanoProjectSpec{ 126 Template: v1alpha12.ProjectTemplate{ 127 Namespaces: []v1alpha12.NamespaceTemplate{ 128 { 129 Metadata: metav1.ObjectMeta{ 130 Name: "application-ns", 131 }, 132 }, 133 }, 134 }, 135 }, 136 } 137 138 asrt.NoError(v.client.Create(context.TODO(), &mc)) 139 asrt.NoError(v.client.Create(context.TODO(), &vp)) 140 141 req := newAdmissionRequest(admissionv1.Create, mcc) 142 res := v.Handle(context.TODO(), req) 143 asrt.True(res.Allowed, "Expected multi-cluster component create validation to succeed.") 144 145 req = newAdmissionRequest(admissionv1.Update, mcc) 146 res = v.Handle(context.TODO(), req) 147 asrt.True(res.Allowed, "Expected multi-cluster component create validation to succeed.") 148 } 149 150 // TestValidationSuccessForMultiClusterComponentCreationWithoutTargetClustersOnManagedCluster tests allowing the creation 151 // of a MultiClusterComponent resources that is missing target cluster information when on managed cluster. 152 // GIVEN a call to validate a MultiClusterComponent resource 153 // WHEN the MultiClusterComponent resource is missing Placement information 154 // AND the validation is being done on a managed cluster 155 // THEN the validation should succeed. 156 func TestValidationSuccessForMultiClusterComponentCreationWithoutTargetClustersOnManagedCluster(t *testing.T) { 157 asrt := assert.New(t) 158 v := newMultiClusterComponentValidator() 159 s := corev1.Secret{ 160 ObjectMeta: metav1.ObjectMeta{ 161 Name: constants.MCRegistrationSecret, 162 Namespace: constants.VerrazzanoSystemNamespace, 163 }, 164 } 165 mcc := v1alpha12.MultiClusterComponent{ 166 ObjectMeta: metav1.ObjectMeta{ 167 Name: "test-mccomponent-name", 168 Namespace: "application-ns", 169 }, 170 Spec: v1alpha12.MultiClusterComponentSpec{ 171 Placement: v1alpha12.Placement{ 172 Clusters: []v1alpha12.Cluster{{Name: "invalid-cluster-name"}}, 173 }, 174 }, 175 } 176 vp := v1alpha12.VerrazzanoProject{ 177 ObjectMeta: metav1.ObjectMeta{ 178 Name: "test-verrazzanoproject-name", 179 Namespace: constants.VerrazzanoMultiClusterNamespace, 180 }, 181 Spec: v1alpha12.VerrazzanoProjectSpec{ 182 Template: v1alpha12.ProjectTemplate{ 183 Namespaces: []v1alpha12.NamespaceTemplate{ 184 { 185 Metadata: metav1.ObjectMeta{ 186 Name: "application-ns", 187 }, 188 }, 189 }, 190 }, 191 }, 192 } 193 194 asrt.NoError(v.client.Create(context.TODO(), &s)) 195 asrt.NoError(v.client.Create(context.TODO(), &vp)) 196 197 req := newAdmissionRequest(admissionv1.Create, mcc) 198 res := v.Handle(context.TODO(), req) 199 asrt.True(res.Allowed, "Expected multi-cluster component validation to succeed with missing placement information on managed cluster.") 200 201 req = newAdmissionRequest(admissionv1.Update, mcc) 202 res = v.Handle(context.TODO(), req) 203 asrt.True(res.Allowed, "Expected multi-cluster component validation to succeed with missing placement information on managed cluster.") 204 } 205 206 // TestMultiClusterComponentHandleFailed tests to make sure the failure metric is being exposed 207 // GIVEN a call to validate a MultiClusterComponent resource 208 // WHEN the MultiClusterComponent resource references a VerrazzanoManagedCluster that does not exist 209 // THEN the validation should fail. 210 func TestMultiClusterComponentHandleFailed(t *testing.T) { 211 assert := assert.New(t) 212 mcc := v1alpha12.MultiClusterComponent{ 213 ObjectMeta: metav1.ObjectMeta{ 214 Name: "test-mccomponent-name", 215 Namespace: "application-ns", 216 }, 217 Spec: v1alpha12.MultiClusterComponentSpec{ 218 Placement: v1alpha12.Placement{ 219 Clusters: []v1alpha12.Cluster{{Name: "valid-cluster-name"}}, 220 }, 221 }, 222 } 223 // Create a request and Handle 224 v := newMultiClusterComponentValidator() 225 req := newAdmissionRequest(admissionv1.Create, mcc) 226 v.Handle(context.TODO(), req) 227 reconcileerrorCounterObject, err := metricsexporter.GetSimpleCounterMetric(metricsexporter.MultiClusterCompHandleError) 228 assert.NoError(err) 229 // Expect a call to fetch the error 230 reconcileFailedCounterBefore := testutil.ToFloat64(reconcileerrorCounterObject.Get()) 231 reconcileerrorCounterObject.Get().Inc() 232 reconcileFailedCounterAfter := testutil.ToFloat64(reconcileerrorCounterObject.Get()) 233 assert.Equal(reconcileFailedCounterBefore, reconcileFailedCounterAfter-1) 234 }