github.com/verrazzano/verrazzano@v1.7.0/application-operator/controllers/webhooks/multiclusterapplicationconfiguration_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/verrazzano/verrazzano/cluster-operator/apis/clusters/v1alpha1"
    11  	admissionv1 "k8s.io/api/admission/v1"
    12  
    13  	"github.com/prometheus/client_golang/prometheus/testutil"
    14  	"github.com/stretchr/testify/assert"
    15  	v1alpha12 "github.com/verrazzano/verrazzano/application-operator/apis/clusters/v1alpha1"
    16  	"github.com/verrazzano/verrazzano/application-operator/constants"
    17  	"github.com/verrazzano/verrazzano/application-operator/metricsexporter"
    18  	corev1 "k8s.io/api/core/v1"
    19  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    20  	"sigs.k8s.io/controller-runtime/pkg/client/fake"
    21  	"sigs.k8s.io/controller-runtime/pkg/webhook/admission"
    22  )
    23  
    24  // newMultiClusterApplicationConfigurationValidator creates a new MultiClusterApplicationConfigurationValidator
    25  func newMultiClusterApplicationConfigurationValidator() MultiClusterApplicationConfigurationValidator {
    26  	scheme := newScheme()
    27  	decoder, _ := admission.NewDecoder(scheme)
    28  	cli := fake.NewClientBuilder().WithScheme(scheme).Build()
    29  	v := MultiClusterApplicationConfigurationValidator{client: cli, decoder: decoder}
    30  	return v
    31  }
    32  
    33  // TestValidationFailureForMultiClusterApplicationConfigurationCreationWithoutTargetClusters tests preventing the creation
    34  // of a MultiClusterApplicationConfiguration resources that is missing Placement information.
    35  // GIVEN a call to validate a MultiClusterApplicationConfiguration resource
    36  // WHEN the MultiClusterApplicationConfiguration resource is missing Placement information
    37  // THEN the validation should fail.
    38  func TestValidationFailureForMultiClusterApplicationConfigurationCreationWithoutTargetClusters(t *testing.T) {
    39  	asrt := assert.New(t)
    40  	v := newMultiClusterApplicationConfigurationValidator()
    41  	p := v1alpha12.MultiClusterApplicationConfiguration{
    42  		ObjectMeta: metav1.ObjectMeta{
    43  			Name:      "test-mcapplicationconfiguration-name",
    44  			Namespace: constants.VerrazzanoMultiClusterNamespace,
    45  		},
    46  		Spec: v1alpha12.MultiClusterApplicationConfigurationSpec{},
    47  	}
    48  
    49  	req := newAdmissionRequest(admissionv1.Create, p)
    50  	res := v.Handle(context.TODO(), req)
    51  	asrt.False(res.Allowed, "Expected multi-cluster application configuration validation to fail due to missing placement information.")
    52  	asrt.Contains(res.Result.Reason, "target cluster")
    53  
    54  	req = newAdmissionRequest(admissionv1.Update, p)
    55  	res = v.Handle(context.TODO(), req)
    56  	asrt.False(res.Allowed, "Expected multi-cluster application configuration validation to fail due to missing placement information.")
    57  	asrt.Contains(res.Result.Reason, "target cluster")
    58  }
    59  
    60  // TestValidationFailureForMultiClusterApplicationConfigurationCreationTargetingMissingManagedCluster tests preventing the creation
    61  // of a MultiClusterApplicationConfiguration resources that references a non-existent managed cluster.
    62  // GIVEN a call to validate a MultiClusterApplicationConfiguration resource
    63  // WHEN the MultiClusterApplicationConfiguration resource references a VerrazzanoManagedCluster that does not exist
    64  // THEN the validation should fail.
    65  func TestValidationFailureForMultiClusterApplicationConfigurationCreationTargetingMissingManagedCluster(t *testing.T) {
    66  	asrt := assert.New(t)
    67  	v := newMultiClusterApplicationConfigurationValidator()
    68  	p := v1alpha12.MultiClusterApplicationConfiguration{
    69  		ObjectMeta: metav1.ObjectMeta{
    70  			Name:      "test-mcapplicationconfiguration-name",
    71  			Namespace: constants.VerrazzanoMultiClusterNamespace,
    72  		},
    73  		Spec: v1alpha12.MultiClusterApplicationConfigurationSpec{
    74  			Placement: v1alpha12.Placement{
    75  				Clusters: []v1alpha12.Cluster{{Name: "invalid-cluster-name"}},
    76  			},
    77  		},
    78  	}
    79  
    80  	req := newAdmissionRequest(admissionv1.Create, p)
    81  	res := v.Handle(context.TODO(), req)
    82  	asrt.False(res.Allowed, "Expected multi-cluster application configuration validation to fail due to missing placement information.")
    83  	asrt.Contains(res.Result.Reason, "invalid-cluster-name")
    84  
    85  	req = newAdmissionRequest(admissionv1.Update, p)
    86  	res = v.Handle(context.TODO(), req)
    87  	asrt.False(res.Allowed, "Expected multi-cluster application configuration validation to fail due to missing placement information.")
    88  	asrt.Contains(res.Result.Reason, "invalid-cluster-name")
    89  }
    90  
    91  // TestValidationSuccessForMultiClusterApplicationConfigurationCreationTargetingExistingManagedCluster tests allowing the creation
    92  // of a MultiClusterApplicationConfiguration resources that references an existent managed cluster.
    93  // GIVEN a call to validate a MultiClusterApplicationConfiguration resource
    94  // WHEN the MultiClusterApplicationConfiguration resource references a VerrazzanoManagedCluster that does exist
    95  // THEN the validation should pass.
    96  func TestValidationSuccessForMultiClusterApplicationConfigurationCreationTargetingExistingManagedCluster(t *testing.T) {
    97  	asrt := assert.New(t)
    98  	v := newMultiClusterApplicationConfigurationValidator()
    99  	mc := v1alpha1.VerrazzanoManagedCluster{
   100  		ObjectMeta: metav1.ObjectMeta{
   101  			Name:      "valid-cluster-name",
   102  			Namespace: constants.VerrazzanoMultiClusterNamespace,
   103  		},
   104  		Spec: v1alpha1.VerrazzanoManagedClusterSpec{
   105  			CASecret:                     "test-secret",
   106  			ManagedClusterManifestSecret: "test-cluster-manifest-secret",
   107  			ServiceAccount:               "test-service-account",
   108  		},
   109  	}
   110  	mcac := v1alpha12.MultiClusterApplicationConfiguration{
   111  		ObjectMeta: metav1.ObjectMeta{
   112  			Name:      "test-mcapplicationconfiguration-name",
   113  			Namespace: "application-ns",
   114  		},
   115  		Spec: v1alpha12.MultiClusterApplicationConfigurationSpec{
   116  			Placement: v1alpha12.Placement{
   117  				Clusters: []v1alpha12.Cluster{{Name: "valid-cluster-name"}},
   118  			},
   119  		},
   120  	}
   121  	vp := v1alpha12.VerrazzanoProject{
   122  		ObjectMeta: metav1.ObjectMeta{
   123  			Name:      "test-verrazzanoproject-name",
   124  			Namespace: constants.VerrazzanoMultiClusterNamespace,
   125  		},
   126  		Spec: v1alpha12.VerrazzanoProjectSpec{
   127  			Template: v1alpha12.ProjectTemplate{
   128  				Namespaces: []v1alpha12.NamespaceTemplate{
   129  					{
   130  						Metadata: metav1.ObjectMeta{
   131  							Name: "application-ns",
   132  						},
   133  					},
   134  				},
   135  			},
   136  		},
   137  	}
   138  
   139  	asrt.NoError(v.client.Create(context.TODO(), &mc))
   140  	asrt.NoError(v.client.Create(context.TODO(), &vp))
   141  
   142  	req := newAdmissionRequest(admissionv1.Create, mcac)
   143  	res := v.Handle(context.TODO(), req)
   144  	asrt.True(res.Allowed, "Expected multi-cluster application configuration create validation to succeed.")
   145  
   146  	req = newAdmissionRequest(admissionv1.Update, mcac)
   147  	res = v.Handle(context.TODO(), req)
   148  	asrt.True(res.Allowed, "Expected multi-cluster application configuration update validation to succeed.")
   149  }
   150  
   151  // TestValidationSuccessForMultiClusterApplicationConfigurationCreationWithoutTargetClustersOnManagedCluster tests allowing the creation
   152  // of a MultiClusterApplicationConfiguration resources that is missing target cluster information when on managed cluster.
   153  // GIVEN a call to validate a MultiClusterApplicationConfiguration resource
   154  // WHEN the MultiClusterApplicationConfiguration resource is missing Placement information
   155  // AND the validation is being done on a managed cluster
   156  // THEN the validation should succeed.
   157  func TestValidationSuccessForMultiClusterApplicationConfigurationCreationWithoutTargetClustersOnManagedCluster(t *testing.T) {
   158  	asrt := assert.New(t)
   159  	v := newMultiClusterApplicationConfigurationValidator()
   160  	s := corev1.Secret{
   161  		ObjectMeta: metav1.ObjectMeta{
   162  			Name:      constants.MCRegistrationSecret,
   163  			Namespace: constants.VerrazzanoSystemNamespace,
   164  		},
   165  	}
   166  	mcac := v1alpha12.MultiClusterApplicationConfiguration{
   167  		ObjectMeta: metav1.ObjectMeta{
   168  			Name:      "test-mcapplicationconfiguration-name",
   169  			Namespace: "application-ns",
   170  		},
   171  		Spec: v1alpha12.MultiClusterApplicationConfigurationSpec{
   172  			Placement: v1alpha12.Placement{
   173  				Clusters: []v1alpha12.Cluster{{Name: "invalid-cluster-name"}},
   174  			},
   175  		},
   176  	}
   177  	vp := v1alpha12.VerrazzanoProject{
   178  		ObjectMeta: metav1.ObjectMeta{
   179  			Name:      "test-verrazzanoproject-name",
   180  			Namespace: constants.VerrazzanoMultiClusterNamespace,
   181  		},
   182  		Spec: v1alpha12.VerrazzanoProjectSpec{
   183  			Template: v1alpha12.ProjectTemplate{
   184  				Namespaces: []v1alpha12.NamespaceTemplate{
   185  					{
   186  						Metadata: metav1.ObjectMeta{
   187  							Name: "application-ns",
   188  						},
   189  					},
   190  				},
   191  			},
   192  		},
   193  	}
   194  
   195  	asrt.NoError(v.client.Create(context.TODO(), &s))
   196  	asrt.NoError(v.client.Create(context.TODO(), &vp))
   197  
   198  	req := newAdmissionRequest(admissionv1.Create, mcac)
   199  	res := v.Handle(context.TODO(), req)
   200  	asrt.True(res.Allowed, "Expected multi-cluster application configuration validation to succeed with missing placement information on managed cluster.")
   201  
   202  	req = newAdmissionRequest(admissionv1.Update, mcac)
   203  	res = v.Handle(context.TODO(), req)
   204  	asrt.True(res.Allowed, "Expected multi-cluster application configuration validation to succeed with missing placement information on managed cluster.")
   205  }
   206  
   207  // TestValidateSecrets tests the function validateSecrets
   208  // GIVEN a call to validateSecrets
   209  // WHEN called with various MultiClusterApplicationConfiguration resources
   210  // THEN the validation should succeed or fail based on what secrets are specified in the
   211  // MultiClusterApplicationConfiguration resource
   212  func TestValidateSecrets(t *testing.T) {
   213  	asrt := assert.New(t)
   214  	v := newMultiClusterApplicationConfigurationValidator()
   215  
   216  	mcac := &v1alpha12.MultiClusterApplicationConfiguration{
   217  		ObjectMeta: metav1.ObjectMeta{
   218  			Name:      "test-mcapplicationconfiguration-name",
   219  			Namespace: constants.VerrazzanoMultiClusterNamespace,
   220  		},
   221  		Spec: v1alpha12.MultiClusterApplicationConfigurationSpec{},
   222  	}
   223  
   224  	// No secrets specified, so success is expected
   225  	asrt.NoError(v.validateSecrets(mcac))
   226  
   227  	mcac = &v1alpha12.MultiClusterApplicationConfiguration{
   228  		ObjectMeta: metav1.ObjectMeta{
   229  			Name:      "test-mcapplicationconfiguration-name",
   230  			Namespace: constants.VerrazzanoMultiClusterNamespace,
   231  		},
   232  		Spec: v1alpha12.MultiClusterApplicationConfigurationSpec{
   233  			Secrets: []string{
   234  				"secret1",
   235  			},
   236  		},
   237  	}
   238  
   239  	// Secret not found, so failure expected
   240  	err := v.validateSecrets(mcac)
   241  	asrt.EqualError(err, "secret(s) secret1 specified in MultiClusterApplicationConfiguration not found in namespace verrazzano-mc")
   242  
   243  	mcac = &v1alpha12.MultiClusterApplicationConfiguration{
   244  		ObjectMeta: metav1.ObjectMeta{
   245  			Name:      "test-mcapplicationconfiguration-name",
   246  			Namespace: constants.VerrazzanoMultiClusterNamespace,
   247  		},
   248  		Spec: v1alpha12.MultiClusterApplicationConfigurationSpec{
   249  			Secrets: []string{
   250  				"secret1",
   251  				"secret2",
   252  			},
   253  		},
   254  	}
   255  
   256  	// Secrets not found, so failure expected
   257  	err = v.validateSecrets(mcac)
   258  	asrt.EqualError(err, "secret(s) secret1,secret2 specified in MultiClusterApplicationConfiguration not found in namespace verrazzano-mc")
   259  
   260  	mcac = &v1alpha12.MultiClusterApplicationConfiguration{
   261  		ObjectMeta: metav1.ObjectMeta{
   262  			Name:      "test-mcapplicationconfiguration-name",
   263  			Namespace: constants.VerrazzanoMultiClusterNamespace,
   264  		},
   265  		Spec: v1alpha12.MultiClusterApplicationConfigurationSpec{
   266  			Secrets: []string{
   267  				"secret1",
   268  			},
   269  		},
   270  	}
   271  	secret1 := corev1.Secret{
   272  		ObjectMeta: metav1.ObjectMeta{
   273  			Name:      "secret1",
   274  			Namespace: constants.VerrazzanoMultiClusterNamespace,
   275  		},
   276  	}
   277  	asrt.NoError(v.client.Create(context.TODO(), &secret1))
   278  
   279  	// Secret should be found, so success is expected
   280  	asrt.NoError(v.validateSecrets(mcac))
   281  
   282  }
   283  
   284  // TestMultiClusterAppConfigHandleFailed tests to make sure the failure metric is being exposed
   285  func TestMultiClusterAppConfigHandleFailed(t *testing.T) {
   286  	assert := assert.New(t)
   287  	// Create a request and decode(Handle)
   288  	decoder := decoder()
   289  	defaulter := &IstioWebhook{}
   290  	_ = defaulter.InjectDecoder(decoder)
   291  	req := admission.Request{}
   292  	defaulter.Handle(context.TODO(), req)
   293  	reconcileerrorCounterObject, err := metricsexporter.GetSimpleCounterMetric(metricsexporter.MultiClusterAppconfigPodHandleError)
   294  	assert.NoError(err)
   295  	// Expect a call to fetch the error
   296  	reconcileFailedCounterBefore := testutil.ToFloat64(reconcileerrorCounterObject.Get())
   297  	reconcileerrorCounterObject.Get().Inc()
   298  	reconcileFailedCounterAfter := testutil.ToFloat64(reconcileerrorCounterObject.Get())
   299  	assert.Equal(reconcileFailedCounterBefore, reconcileFailedCounterAfter-1)
   300  }