github.com/verrazzano/verrazzano@v1.7.1/cluster-operator/apis/clusters/v1alpha1/verrazzanomanagedcluster_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 v1alpha1
     5  
     6  import (
     7  	"testing"
     8  
     9  	"github.com/stretchr/testify/assert"
    10  	"github.com/verrazzano/verrazzano/platform-operator/apis/verrazzano/v1beta1"
    11  	"github.com/verrazzano/verrazzano/platform-operator/constants"
    12  	corev1 "k8s.io/api/core/v1"
    13  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    14  	"sigs.k8s.io/controller-runtime/pkg/client"
    15  	"sigs.k8s.io/controller-runtime/pkg/client/fake"
    16  )
    17  
    18  const testNameDefaultVZ = "defaultVz"
    19  const testNameRancherEnabled = "rancher explicitly enabled in VZ"
    20  const testNameRancherDisabled = "rancher disabled in VZ"
    21  
    22  var falseValue = false
    23  var trueValue = true
    24  
    25  // List of Verrazzano resources that have status InstallComplete
    26  
    27  // verrazzanoList contains Verrazzano with defaults and InstallComplete status condition
    28  var verrazzanoList = &v1beta1.VerrazzanoList{
    29  	Items: []v1beta1.Verrazzano{
    30  		{
    31  			ObjectMeta: metav1.ObjectMeta{
    32  				Name:      "my-verrazzano",
    33  				Namespace: "default",
    34  			},
    35  			Status: v1beta1.VerrazzanoStatus{
    36  				Conditions: []v1beta1.Condition{
    37  					{
    38  						Type: v1beta1.CondInstallComplete,
    39  					},
    40  				},
    41  			},
    42  		},
    43  	},
    44  }
    45  
    46  // verrazzanoListRancherDisabled contains Verrazzano with Rancher disabled and InstallComplete status condition
    47  var verrazzanoListRancherDisabled = &v1beta1.VerrazzanoList{
    48  	Items: []v1beta1.Verrazzano{
    49  		{
    50  			ObjectMeta: verrazzanoList.Items[0].ObjectMeta,
    51  			Status:     verrazzanoList.Items[0].Status,
    52  			Spec: v1beta1.VerrazzanoSpec{
    53  				Components: v1beta1.ComponentSpec{
    54  					Rancher: &v1beta1.RancherComponent{Enabled: &falseValue},
    55  				},
    56  			},
    57  		},
    58  	},
    59  }
    60  
    61  // verrazzanoListRancherEnabled contains Verrazzano with Rancher explicitly enabled and InstallComplete status condition
    62  var verrazzanoListRancherEnabled = &v1beta1.VerrazzanoList{
    63  	Items: []v1beta1.Verrazzano{
    64  		{
    65  			ObjectMeta: verrazzanoList.Items[0].ObjectMeta,
    66  			Status:     verrazzanoList.Items[0].Status,
    67  			Spec: v1beta1.VerrazzanoSpec{
    68  				Components: v1beta1.ComponentSpec{
    69  					Rancher: &v1beta1.RancherComponent{Enabled: &trueValue},
    70  				},
    71  			},
    72  		},
    73  	},
    74  }
    75  
    76  // TestCreateWithSecretAndConfigMap tests the validation of a valid VerrazzanoManagedCluster secret and valid verrazzano-admin-cluster configmap
    77  // GIVEN a call validate VerrazzanoManagedCluster
    78  // WHEN the VerrazzanoManagedCluster has valid secret specified and verrazzano-admin-cluster configmap is valid
    79  // THEN the validation should succeed in all cases
    80  func TestCreateWithSecretAndConfigMap(t *testing.T) {
    81  	const secretName = "mySecret"
    82  	tests := []struct {
    83  		name        string
    84  		verrazzanos *v1beta1.VerrazzanoList
    85  	}{
    86  		{testNameDefaultVZ, verrazzanoList},
    87  		{testNameRancherDisabled, verrazzanoListRancherDisabled},
    88  		{testNameRancherEnabled, verrazzanoListRancherEnabled},
    89  	}
    90  	for _, test := range tests {
    91  		t.Run(test.name, func(t *testing.T) {
    92  
    93  			// fake client needed to get secret
    94  			getClientFunc = func() (client.Client, error) {
    95  				return fake.NewClientBuilder().WithScheme(newScheme()).WithObjects(
    96  					&corev1.Secret{
    97  						ObjectMeta: metav1.ObjectMeta{
    98  							Name:      secretName,
    99  							Namespace: constants.VerrazzanoMultiClusterNamespace,
   100  						},
   101  					},
   102  					&corev1.ConfigMap{
   103  						ObjectMeta: metav1.ObjectMeta{
   104  							Name:      constants.AdminClusterConfigMapName,
   105  							Namespace: constants.VerrazzanoMultiClusterNamespace,
   106  						},
   107  						Data: map[string]string{
   108  							constants.ServerDataKey: "https://testUrl",
   109  						},
   110  					}).WithLists(verrazzanoList).Build(), nil
   111  			}
   112  			defer func() { getClientFunc = getClient }()
   113  
   114  			// VMC to be validated
   115  			vz := VerrazzanoManagedCluster{
   116  				TypeMeta: metav1.TypeMeta{},
   117  				ObjectMeta: metav1.ObjectMeta{
   118  					Name:      "testMC",
   119  					Namespace: constants.VerrazzanoMultiClusterNamespace,
   120  				},
   121  				Spec: VerrazzanoManagedClusterSpec{
   122  					CASecret: secretName,
   123  				},
   124  			}
   125  			err := vz.ValidateCreate()
   126  			assert.NoError(t, err, "Error validating VerrazzanoMultiCluster resource")
   127  		})
   128  	}
   129  }
   130  
   131  // TestCreateNoConfigMap tests the validation of missing verrazzano-admin-cluster configmap
   132  // GIVEN a call validate VerrazzanoManagedCluster
   133  // WHEN the verrazzano-admin-cluster configmap doesn't exist
   134  // THEN the validation should fail if Rancher is disabled, succeed otherwise
   135  func TestCreateNoConfigMap(t *testing.T) {
   136  	const secretName = "mySecret"
   137  
   138  	tests := []struct {
   139  		name           string
   140  		verrazzanos    *v1beta1.VerrazzanoList
   141  		errorExpected  bool
   142  		errMsgExpected string
   143  	}{
   144  		{testNameDefaultVZ, verrazzanoList, false, ""},
   145  		{testNameRancherDisabled, verrazzanoListRancherDisabled, true, "The ConfigMap verrazzano-admin-cluster does not exist in namespace verrazzano-mc"},
   146  		{testNameRancherEnabled, verrazzanoListRancherEnabled, false, ""},
   147  	}
   148  	for _, test := range tests {
   149  		t.Run(test.name, func(t *testing.T) {
   150  			// fake client needed to get secret
   151  			getClientFunc = func() (client.Client, error) {
   152  				return fake.NewClientBuilder().WithScheme(newScheme()).WithObjects(
   153  					&corev1.Secret{
   154  						ObjectMeta: metav1.ObjectMeta{
   155  							Name:      secretName,
   156  							Namespace: constants.VerrazzanoMultiClusterNamespace,
   157  						},
   158  					}).WithLists(test.verrazzanos).Build(), nil
   159  			}
   160  			defer func() { getClientFunc = getClient }()
   161  
   162  			// VMC to be validated
   163  			vz := VerrazzanoManagedCluster{
   164  				TypeMeta: metav1.TypeMeta{},
   165  				ObjectMeta: metav1.ObjectMeta{
   166  					Name:      "testMC",
   167  					Namespace: constants.VerrazzanoMultiClusterNamespace,
   168  				},
   169  				Spec: VerrazzanoManagedClusterSpec{
   170  					CASecret: secretName,
   171  				},
   172  			}
   173  			err := vz.ValidateCreate()
   174  			if test.errorExpected {
   175  				assert.EqualError(t, err, test.errMsgExpected, "Expected correct error message")
   176  			} else {
   177  				assert.NoError(t, err, "Error validating VerrazzanoMultiCluster resource - should be able to create VMC without verrazzano-admin-cluster existing")
   178  			}
   179  		})
   180  	}
   181  }
   182  
   183  // TestCreateWithSecretConfigMapMissingServer tests the validation of verrazzano-admin-cluster configmap with missing server data
   184  // GIVEN a call validate VerrazzanoManagedCluster
   185  // WHEN the verrazzano-admin-cluster configmap is missing server data
   186  // THEN the validation should fail if Rancher is disabled, succeed otherwise
   187  func TestCreateWithSecretConfigMapMissingServer(t *testing.T) {
   188  	const secretName = "mySecret"
   189  
   190  	tests := []struct {
   191  		name           string
   192  		verrazzanos    *v1beta1.VerrazzanoList
   193  		errorExpected  bool
   194  		errMsgExpected string
   195  	}{
   196  		{testNameDefaultVZ, verrazzanoList, false, ""},
   197  		{testNameRancherDisabled, verrazzanoListRancherDisabled, true, "Data with key \"server\" contains invalid url \"\" in the ConfigMap verrazzano-admin-cluster namespace verrazzano-mc"},
   198  		{testNameRancherEnabled, verrazzanoListRancherEnabled, false, ""},
   199  	}
   200  	for _, test := range tests {
   201  		t.Run(test.name, func(t *testing.T) {
   202  			// fake client needed to get secret
   203  			getClientFunc = func() (client.Client, error) {
   204  				return fake.NewClientBuilder().WithScheme(newScheme()).WithObjects(
   205  					&corev1.Secret{
   206  						ObjectMeta: metav1.ObjectMeta{
   207  							Name:      secretName,
   208  							Namespace: constants.VerrazzanoMultiClusterNamespace,
   209  						},
   210  					},
   211  					&corev1.ConfigMap{
   212  						ObjectMeta: metav1.ObjectMeta{
   213  							Name:      constants.AdminClusterConfigMapName,
   214  							Namespace: constants.VerrazzanoMultiClusterNamespace,
   215  						},
   216  					}).WithLists(test.verrazzanos).Build(), nil
   217  			}
   218  			defer func() { getClientFunc = getClient }()
   219  
   220  			// VMC to be validated
   221  			vz := VerrazzanoManagedCluster{
   222  				TypeMeta: metav1.TypeMeta{},
   223  				ObjectMeta: metav1.ObjectMeta{
   224  					Name:      "testMC",
   225  					Namespace: constants.VerrazzanoMultiClusterNamespace,
   226  				},
   227  				Spec: VerrazzanoManagedClusterSpec{
   228  					CASecret: secretName,
   229  				},
   230  			}
   231  			err := vz.ValidateCreate()
   232  			if test.errorExpected {
   233  				assert.EqualError(t, err, test.errMsgExpected,
   234  					"Expected correct error message")
   235  			} else {
   236  				assert.NoError(t, err)
   237  			}
   238  		})
   239  	}
   240  }
   241  
   242  // TestCreateMissingSecretName tests the validation of a VerrazzanoManagedCluster with a missing secret name
   243  // GIVEN a call validate VerrazzanoManagedCluster
   244  // WHEN the VerrazzanoManagedCluster is missing the secret name
   245  // THEN the validation should succeed and default to a well-known CA
   246  func TestCreateMissingSecretName(t *testing.T) {
   247  	getClientFunc = func() (client.Client, error) {
   248  		return fake.NewClientBuilder().WithScheme(newScheme()).WithObjects(
   249  			&corev1.ConfigMap{
   250  				ObjectMeta: metav1.ObjectMeta{
   251  					Name:      constants.AdminClusterConfigMapName,
   252  					Namespace: constants.VerrazzanoMultiClusterNamespace,
   253  				},
   254  				Data: map[string]string{
   255  					constants.ServerDataKey: "https://testUrl",
   256  				},
   257  			}).WithLists(verrazzanoList).Build(), nil
   258  	}
   259  	defer func() { getClientFunc = getClient }()
   260  	vz := VerrazzanoManagedCluster{
   261  		TypeMeta: metav1.TypeMeta{},
   262  		ObjectMeta: metav1.ObjectMeta{
   263  			Name:      "test",
   264  			Namespace: constants.VerrazzanoMultiClusterNamespace,
   265  		},
   266  	}
   267  	err := vz.ValidateCreate()
   268  	assert.NoError(t, err, "Error validating VerrazzanoMultiCluster resource with well-known CA")
   269  }
   270  
   271  // TestCreateMissingSecret tests the validation of a missing Prometheus secret in the MC namespace
   272  // GIVEN a call validate VerrazzanoManagedCluster
   273  // WHEN the multi-cluster namespace is missing the secret
   274  // THEN the validation should fail
   275  func TestCreateMissingSecret(t *testing.T) {
   276  	const secretName = "mySecret"
   277  	tests := []struct {
   278  		name           string
   279  		verrazzanos    *v1beta1.VerrazzanoList
   280  		errorExpected  bool
   281  		errMsgExpected string
   282  	}{
   283  		{testNameDefaultVZ, verrazzanoList, false, ""},
   284  		{testNameRancherDisabled, verrazzanoListRancherDisabled, true, "The CA secret mySecret does not exist in namespace verrazzano-mc"},
   285  		{testNameRancherEnabled, verrazzanoListRancherEnabled, false, ""},
   286  	}
   287  	for _, test := range tests {
   288  		t.Run(test.name, func(t *testing.T) {
   289  			getClientFunc = func() (client.Client, error) {
   290  				return fake.NewClientBuilder().WithScheme(newScheme()).WithLists(test.verrazzanos).Build(), nil
   291  			}
   292  			defer func() { getClientFunc = getClient }()
   293  
   294  			vz := VerrazzanoManagedCluster{
   295  				TypeMeta: metav1.TypeMeta{},
   296  				ObjectMeta: metav1.ObjectMeta{
   297  					Name:      "testMC",
   298  					Namespace: constants.VerrazzanoMultiClusterNamespace,
   299  				},
   300  				Spec: VerrazzanoManagedClusterSpec{
   301  					CASecret: secretName,
   302  				},
   303  			}
   304  			err := vz.ValidateCreate()
   305  			if test.errorExpected {
   306  				assert.EqualError(t, err, test.errMsgExpected,
   307  					"Expected correct error message for missing secret")
   308  			} else {
   309  				assert.NoError(t, err)
   310  			}
   311  		})
   312  	}
   313  }
   314  
   315  // TestCreateVerrazzanoNotInstalled tests the validation of a Verrazzano being installed
   316  // GIVEN a call validate VerrazzanoManagedCluster
   317  // WHEN the a Verrazzano install has not completed
   318  // THEN the validation should fail
   319  func TestCreateVerrazzanoNotInstalled(t *testing.T) {
   320  	const secretName = "mySecret"
   321  
   322  	var notInstalledList = &v1beta1.VerrazzanoList{
   323  		Items: []v1beta1.Verrazzano{
   324  			{
   325  				ObjectMeta: metav1.ObjectMeta{
   326  					Name:      "my-verrazzano",
   327  					Namespace: "default",
   328  				},
   329  				Status: v1beta1.VerrazzanoStatus{
   330  					Conditions: []v1beta1.Condition{
   331  						{
   332  							Type: v1beta1.CondInstallStarted,
   333  						},
   334  					},
   335  				},
   336  			},
   337  		},
   338  	}
   339  
   340  	// fake client needed to validate create
   341  	getClientFunc = func() (client.Client, error) {
   342  		return fake.NewClientBuilder().WithScheme(newScheme()).WithLists(notInstalledList).Build(), nil
   343  	}
   344  	defer func() { getClientFunc = getClient }()
   345  
   346  	// VMC to be validated
   347  	vz := VerrazzanoManagedCluster{
   348  		TypeMeta: metav1.TypeMeta{},
   349  		ObjectMeta: metav1.ObjectMeta{
   350  			Name:      "testMC",
   351  			Namespace: constants.VerrazzanoMultiClusterNamespace,
   352  		},
   353  		Spec: VerrazzanoManagedClusterSpec{
   354  			CASecret: secretName,
   355  		},
   356  	}
   357  	err := vz.ValidateCreate()
   358  	assert.Error(t, err)
   359  	assert.Contains(t, err.Error(), "the Verrazzano install must successfully complete")
   360  }