github.com/verrazzano/verrazzano@v1.7.0/application-operator/controllers/webhooks/multiclustersecret_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  // newMultiClusterSecretValidator creates a new MultiClusterSecretValidator
    24  func newMultiClusterSecretValidator() MultiClusterSecretValidator {
    25  	scheme := newScheme()
    26  	decoder, _ := admission.NewDecoder(scheme)
    27  	cli := fake.NewClientBuilder().WithScheme(scheme).Build()
    28  	v := MultiClusterSecretValidator{client: cli, decoder: decoder}
    29  	return v
    30  }
    31  
    32  // TestValidationFailureForMultiClusterSecretCreationWithoutTargetClusters tests preventing the creation
    33  // of a MultiClusterSecret resources that is missing Placement information.
    34  // GIVEN a call to validate a MultiClusterSecret resource
    35  // WHEN the MultiClusterSecret resource is missing Placement information
    36  // THEN the validation should fail.
    37  func TestValidationFailureForMultiClusterSecretCreationWithoutTargetClusters(t *testing.T) {
    38  	asrt := assert.New(t)
    39  	v := newMultiClusterSecretValidator()
    40  	p := v1alpha12.MultiClusterSecret{
    41  		ObjectMeta: metav1.ObjectMeta{
    42  			Name:      "test-mcsecret-name",
    43  			Namespace: constants.VerrazzanoMultiClusterNamespace,
    44  		},
    45  		Spec: v1alpha12.MultiClusterSecretSpec{},
    46  	}
    47  
    48  	req := newAdmissionRequest(admissionv1.Create, p)
    49  	res := v.Handle(context.TODO(), req)
    50  	asrt.False(res.Allowed, "Expected multi-cluster secret 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 secret validation to fail due to missing placement information.")
    56  	asrt.Contains(res.Result.Reason, "target cluster")
    57  }
    58  
    59  // TestValidationFailureForMultiClusterSecretCreationTargetingMissingManagedCluster tests preventing the creation
    60  // of a MultiClusterSecret resources that references a non-existent managed cluster.
    61  // GIVEN a call to validate a MultiClusterSecret resource
    62  // WHEN the MultiClusterSecret resource references a VerrazzanoManagedCluster that does not exist
    63  // THEN the validation should fail.
    64  func TestValidationFailureForMultiClusterSecretCreationTargetingMissingManagedCluster(t *testing.T) {
    65  	asrt := assert.New(t)
    66  	v := newMultiClusterSecretValidator()
    67  	p := v1alpha12.MultiClusterSecret{
    68  		ObjectMeta: metav1.ObjectMeta{
    69  			Name:      "test-mcsecret-name",
    70  			Namespace: constants.VerrazzanoMultiClusterNamespace,
    71  		},
    72  		Spec: v1alpha12.MultiClusterSecretSpec{
    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 secret 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 secret validation to fail due to missing placement information.")
    87  	asrt.Contains(res.Result.Reason, "invalid-cluster-name")
    88  }
    89  
    90  // TestValidationSuccessForMultiClusterSecretCreationTargetingExistingManagedCluster tests allowing the creation
    91  // of a MultiClusterSecret resources that references an existent managed cluster.
    92  // GIVEN a call to validate a MultiClusterSecret resource
    93  // WHEN the MultiClusterSecret resource references a VerrazzanoManagedCluster that does exist
    94  // THEN the validation should pass.
    95  func TestValidationSuccessForMultiClusterSecretCreationTargetingExistingManagedCluster(t *testing.T) {
    96  	asrt := assert.New(t)
    97  	v := newMultiClusterSecretValidator()
    98  	c := 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  	p := v1alpha12.MultiClusterSecret{
   110  		ObjectMeta: metav1.ObjectMeta{
   111  			Name:      "test-mcsecret-name",
   112  			Namespace: "application-ns",
   113  		},
   114  		Spec: v1alpha12.MultiClusterSecretSpec{
   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(), &c))
   139  	asrt.NoError(v.client.Create(context.TODO(), &vp))
   140  
   141  	req := newAdmissionRequest(admissionv1.Create, p)
   142  	res := v.Handle(context.TODO(), req)
   143  	asrt.True(res.Allowed, "Expected multi-cluster secret create validation to succeed.")
   144  
   145  	req = newAdmissionRequest(admissionv1.Update, p)
   146  	res = v.Handle(context.TODO(), req)
   147  	asrt.True(res.Allowed, "Expected multi-cluster secret update validation to succeed.")
   148  }
   149  
   150  // TestValidationSuccessForMultiClusterSecretCreationWithoutTargetClustersOnManagedCluster tests allowing the creation
   151  // of a MultiClusterSecret resources that is missing target cluster information when on managed cluster.
   152  // GIVEN a call to validate a MultiClusterSecret resource
   153  // WHEN the MultiClusterSecret resource is missing Placement information
   154  // AND the validation is being done on a managed cluster
   155  // THEN the validation should succeed.
   156  func TestValidationSuccessForMultiClusterSecretCreationWithoutTargetClustersOnManagedCluster(t *testing.T) {
   157  	asrt := assert.New(t)
   158  	v := newMultiClusterSecretValidator()
   159  	s := corev1.Secret{
   160  		ObjectMeta: metav1.ObjectMeta{
   161  			Name:      constants.MCRegistrationSecret,
   162  			Namespace: constants.VerrazzanoSystemNamespace,
   163  		},
   164  	}
   165  	p := v1alpha12.MultiClusterSecret{
   166  		ObjectMeta: metav1.ObjectMeta{
   167  			Name:      "test-mcsecret-name",
   168  			Namespace: "application-ns",
   169  		},
   170  		Spec: v1alpha12.MultiClusterSecretSpec{
   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, p)
   198  	res := v.Handle(context.TODO(), req)
   199  	asrt.True(res.Allowed, "Expected multi-cluster secret validation to succeed with missing placement information on managed cluster.")
   200  
   201  	req = newAdmissionRequest(admissionv1.Update, p)
   202  	res = v.Handle(context.TODO(), req)
   203  	asrt.True(res.Allowed, "Expected multi-cluster secret validation to succeed with missing placement information on managed cluster.")
   204  }
   205  
   206  // TestMultiClusterSecretHandleFailed tests to make sure the failure metric is being exposed
   207  // GIVEN a call to validate a MultiClusterConfigMap resource
   208  // WHEN the MultiClusterConfigMap resource is failing
   209  // THEN the validation should fail.
   210  func TestMultiClusterSecretHandleFailed(t *testing.T) {
   211  	assert := assert.New(t)
   212  	mcc := v1alpha12.MultiClusterComponent{
   213  		ObjectMeta: metav1.ObjectMeta{
   214  			Name:      "test-mccomponent-error",
   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.MultiClusterSecretHandleError)
   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  }