github.com/argoproj/argo-cd/v2@v2.10.9/applicationset/generators/cluster_test.go (about)

     1  package generators
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"testing"
     7  
     8  	corev1 "k8s.io/api/core/v1"
     9  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    10  	"k8s.io/apimachinery/pkg/runtime"
    11  	"sigs.k8s.io/controller-runtime/pkg/client"
    12  	"sigs.k8s.io/controller-runtime/pkg/client/fake"
    13  
    14  	kubefake "k8s.io/client-go/kubernetes/fake"
    15  
    16  	"github.com/argoproj/argo-cd/v2/applicationset/utils"
    17  	argoprojiov1alpha1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
    18  
    19  	"github.com/stretchr/testify/assert"
    20  )
    21  
    22  type possiblyErroringFakeCtrlRuntimeClient struct {
    23  	client.Client
    24  	shouldError bool
    25  }
    26  
    27  func (p *possiblyErroringFakeCtrlRuntimeClient) List(ctx context.Context, secretList client.ObjectList, opts ...client.ListOption) error {
    28  	if p.shouldError {
    29  		return fmt.Errorf("could not list Secrets")
    30  	}
    31  	return p.Client.List(ctx, secretList, opts...)
    32  }
    33  
    34  func TestGenerateParams(t *testing.T) {
    35  	clusters := []client.Object{
    36  		&corev1.Secret{
    37  			TypeMeta: metav1.TypeMeta{
    38  				Kind:       "Secret",
    39  				APIVersion: "v1",
    40  			},
    41  			ObjectMeta: metav1.ObjectMeta{
    42  				Name:      "staging-01",
    43  				Namespace: "namespace",
    44  				Labels: map[string]string{
    45  					"argocd.argoproj.io/secret-type": "cluster",
    46  					"environment":                    "staging",
    47  					"org":                            "foo",
    48  				},
    49  				Annotations: map[string]string{
    50  					"foo.argoproj.io": "staging",
    51  				},
    52  			},
    53  			Data: map[string][]byte{
    54  				"config": []byte("{}"),
    55  				"name":   []byte("staging-01"),
    56  				"server": []byte("https://staging-01.example.com"),
    57  			},
    58  			Type: corev1.SecretType("Opaque"),
    59  		},
    60  		&corev1.Secret{
    61  			TypeMeta: metav1.TypeMeta{
    62  				Kind:       "Secret",
    63  				APIVersion: "v1",
    64  			},
    65  			ObjectMeta: metav1.ObjectMeta{
    66  				Name:      "production-01",
    67  				Namespace: "namespace",
    68  				Labels: map[string]string{
    69  					"argocd.argoproj.io/secret-type": "cluster",
    70  					"environment":                    "production",
    71  					"org":                            "bar",
    72  				},
    73  				Annotations: map[string]string{
    74  					"foo.argoproj.io": "production",
    75  				},
    76  			},
    77  			Data: map[string][]byte{
    78  				"config": []byte("{}"),
    79  				"name":   []byte("production_01/west"),
    80  				"server": []byte("https://production-01.example.com"),
    81  			},
    82  			Type: corev1.SecretType("Opaque"),
    83  		},
    84  	}
    85  	testCases := []struct {
    86  		name     string
    87  		selector metav1.LabelSelector
    88  		values   map[string]string
    89  		expected []map[string]interface{}
    90  		// clientError is true if a k8s client error should be simulated
    91  		clientError   bool
    92  		expectedError error
    93  	}{
    94  		{
    95  			name:     "no label selector",
    96  			selector: metav1.LabelSelector{},
    97  			values: map[string]string{
    98  				"lol1":  "lol",
    99  				"lol2":  "{{values.lol1}}{{values.lol1}}",
   100  				"lol3":  "{{values.lol2}}{{values.lol2}}{{values.lol2}}",
   101  				"foo":   "bar",
   102  				"bar":   "{{ metadata.annotations.foo.argoproj.io }}",
   103  				"bat":   "{{ metadata.labels.environment }}",
   104  				"aaa":   "{{ server }}",
   105  				"no-op": "{{ this-does-not-exist }}",
   106  			}, expected: []map[string]interface{}{
   107  				{"values.lol1": "lol", "values.lol2": "{{values.lol1}}{{values.lol1}}", "values.lol3": "{{values.lol2}}{{values.lol2}}{{values.lol2}}", "values.foo": "bar", "values.bar": "production", "values.no-op": "{{ this-does-not-exist }}", "values.bat": "production", "values.aaa": "https://production-01.example.com", "name": "production_01/west", "nameNormalized": "production-01-west", "server": "https://production-01.example.com", "metadata.labels.environment": "production", "metadata.labels.org": "bar",
   108  					"metadata.labels.argocd.argoproj.io/secret-type": "cluster", "metadata.annotations.foo.argoproj.io": "production"},
   109  
   110  				{"values.lol1": "lol", "values.lol2": "{{values.lol1}}{{values.lol1}}", "values.lol3": "{{values.lol2}}{{values.lol2}}{{values.lol2}}", "values.foo": "bar", "values.bar": "staging", "values.no-op": "{{ this-does-not-exist }}", "values.bat": "staging", "values.aaa": "https://staging-01.example.com", "name": "staging-01", "nameNormalized": "staging-01", "server": "https://staging-01.example.com", "metadata.labels.environment": "staging", "metadata.labels.org": "foo",
   111  					"metadata.labels.argocd.argoproj.io/secret-type": "cluster", "metadata.annotations.foo.argoproj.io": "staging"},
   112  
   113  				{"values.lol1": "lol", "values.lol2": "{{values.lol1}}{{values.lol1}}", "values.lol3": "{{values.lol2}}{{values.lol2}}{{values.lol2}}", "values.foo": "bar", "values.bar": "{{ metadata.annotations.foo.argoproj.io }}", "values.no-op": "{{ this-does-not-exist }}", "values.bat": "{{ metadata.labels.environment }}", "values.aaa": "https://kubernetes.default.svc", "nameNormalized": "in-cluster", "name": "in-cluster", "server": "https://kubernetes.default.svc"},
   114  			},
   115  			clientError:   false,
   116  			expectedError: nil,
   117  		},
   118  		{
   119  			name: "secret type label selector",
   120  			selector: metav1.LabelSelector{
   121  				MatchLabels: map[string]string{
   122  					"argocd.argoproj.io/secret-type": "cluster",
   123  				},
   124  			},
   125  			values: nil,
   126  			expected: []map[string]interface{}{
   127  				{"name": "production_01/west", "nameNormalized": "production-01-west", "server": "https://production-01.example.com", "metadata.labels.environment": "production", "metadata.labels.org": "bar",
   128  					"metadata.labels.argocd.argoproj.io/secret-type": "cluster", "metadata.annotations.foo.argoproj.io": "production"},
   129  
   130  				{"name": "staging-01", "nameNormalized": "staging-01", "server": "https://staging-01.example.com", "metadata.labels.environment": "staging", "metadata.labels.org": "foo",
   131  					"metadata.labels.argocd.argoproj.io/secret-type": "cluster", "metadata.annotations.foo.argoproj.io": "staging"},
   132  			},
   133  			clientError:   false,
   134  			expectedError: nil,
   135  		},
   136  		{
   137  			name: "production-only",
   138  			selector: metav1.LabelSelector{
   139  				MatchLabels: map[string]string{
   140  					"environment": "production",
   141  				},
   142  			},
   143  			values: map[string]string{
   144  				"foo": "bar",
   145  			},
   146  			expected: []map[string]interface{}{
   147  				{"values.foo": "bar", "name": "production_01/west", "nameNormalized": "production-01-west", "server": "https://production-01.example.com", "metadata.labels.environment": "production", "metadata.labels.org": "bar",
   148  					"metadata.labels.argocd.argoproj.io/secret-type": "cluster", "metadata.annotations.foo.argoproj.io": "production"},
   149  			},
   150  			clientError:   false,
   151  			expectedError: nil,
   152  		},
   153  		{
   154  			name: "production or staging",
   155  			selector: metav1.LabelSelector{
   156  				MatchExpressions: []metav1.LabelSelectorRequirement{
   157  					{
   158  						Key:      "environment",
   159  						Operator: "In",
   160  						Values: []string{
   161  							"production",
   162  							"staging",
   163  						},
   164  					},
   165  				},
   166  			},
   167  			values: map[string]string{
   168  				"foo": "bar",
   169  			},
   170  			expected: []map[string]interface{}{
   171  				{"values.foo": "bar", "name": "staging-01", "nameNormalized": "staging-01", "server": "https://staging-01.example.com", "metadata.labels.environment": "staging", "metadata.labels.org": "foo",
   172  					"metadata.labels.argocd.argoproj.io/secret-type": "cluster", "metadata.annotations.foo.argoproj.io": "staging"},
   173  				{"values.foo": "bar", "name": "production_01/west", "nameNormalized": "production-01-west", "server": "https://production-01.example.com", "metadata.labels.environment": "production", "metadata.labels.org": "bar",
   174  					"metadata.labels.argocd.argoproj.io/secret-type": "cluster", "metadata.annotations.foo.argoproj.io": "production"},
   175  			},
   176  			clientError:   false,
   177  			expectedError: nil,
   178  		},
   179  		{
   180  			name: "production or staging with match labels",
   181  			selector: metav1.LabelSelector{
   182  				MatchExpressions: []metav1.LabelSelectorRequirement{
   183  					{
   184  						Key:      "environment",
   185  						Operator: "In",
   186  						Values: []string{
   187  							"production",
   188  							"staging",
   189  						},
   190  					},
   191  				},
   192  				MatchLabels: map[string]string{
   193  					"org": "foo",
   194  				},
   195  			},
   196  			values: map[string]string{
   197  				"name": "baz",
   198  			},
   199  			expected: []map[string]interface{}{
   200  				{"values.name": "baz", "name": "staging-01", "nameNormalized": "staging-01", "server": "https://staging-01.example.com", "metadata.labels.environment": "staging", "metadata.labels.org": "foo",
   201  					"metadata.labels.argocd.argoproj.io/secret-type": "cluster", "metadata.annotations.foo.argoproj.io": "staging"},
   202  			},
   203  			clientError:   false,
   204  			expectedError: nil,
   205  		},
   206  		{
   207  			name:          "simulate client error",
   208  			selector:      metav1.LabelSelector{},
   209  			values:        nil,
   210  			expected:      nil,
   211  			clientError:   true,
   212  			expectedError: fmt.Errorf("could not list Secrets"),
   213  		},
   214  	}
   215  
   216  	// convert []client.Object to []runtime.Object, for use by kubefake package
   217  	runtimeClusters := []runtime.Object{}
   218  	for _, clientCluster := range clusters {
   219  		runtimeClusters = append(runtimeClusters, clientCluster)
   220  	}
   221  
   222  	for _, testCase := range testCases {
   223  
   224  		t.Run(testCase.name, func(t *testing.T) {
   225  
   226  			appClientset := kubefake.NewSimpleClientset(runtimeClusters...)
   227  
   228  			fakeClient := fake.NewClientBuilder().WithObjects(clusters...).Build()
   229  			cl := &possiblyErroringFakeCtrlRuntimeClient{
   230  				fakeClient,
   231  				testCase.clientError,
   232  			}
   233  
   234  			var clusterGenerator = NewClusterGenerator(cl, context.Background(), appClientset, "namespace")
   235  
   236  			applicationSetInfo := argoprojiov1alpha1.ApplicationSet{
   237  				ObjectMeta: metav1.ObjectMeta{
   238  					Name: "set",
   239  				},
   240  				Spec: argoprojiov1alpha1.ApplicationSetSpec{},
   241  			}
   242  
   243  			got, err := clusterGenerator.GenerateParams(&argoprojiov1alpha1.ApplicationSetGenerator{
   244  				Clusters: &argoprojiov1alpha1.ClusterGenerator{
   245  					Selector: testCase.selector,
   246  					Values:   testCase.values,
   247  				},
   248  			}, &applicationSetInfo)
   249  
   250  			if testCase.expectedError != nil {
   251  				assert.EqualError(t, err, testCase.expectedError.Error())
   252  			} else {
   253  				assert.NoError(t, err)
   254  				assert.ElementsMatch(t, testCase.expected, got)
   255  			}
   256  
   257  		})
   258  	}
   259  }
   260  
   261  func TestGenerateParamsGoTemplate(t *testing.T) {
   262  	clusters := []client.Object{
   263  		&corev1.Secret{
   264  			TypeMeta: metav1.TypeMeta{
   265  				Kind:       "Secret",
   266  				APIVersion: "v1",
   267  			},
   268  			ObjectMeta: metav1.ObjectMeta{
   269  				Name:      "staging-01",
   270  				Namespace: "namespace",
   271  				Labels: map[string]string{
   272  					"argocd.argoproj.io/secret-type": "cluster",
   273  					"environment":                    "staging",
   274  					"org":                            "foo",
   275  				},
   276  				Annotations: map[string]string{
   277  					"foo.argoproj.io": "staging",
   278  				},
   279  			},
   280  			Data: map[string][]byte{
   281  				"config": []byte("{}"),
   282  				"name":   []byte("staging-01"),
   283  				"server": []byte("https://staging-01.example.com"),
   284  			},
   285  			Type: corev1.SecretType("Opaque"),
   286  		},
   287  		&corev1.Secret{
   288  			TypeMeta: metav1.TypeMeta{
   289  				Kind:       "Secret",
   290  				APIVersion: "v1",
   291  			},
   292  			ObjectMeta: metav1.ObjectMeta{
   293  				Name:      "production-01",
   294  				Namespace: "namespace",
   295  				Labels: map[string]string{
   296  					"argocd.argoproj.io/secret-type": "cluster",
   297  					"environment":                    "production",
   298  					"org":                            "bar",
   299  				},
   300  				Annotations: map[string]string{
   301  					"foo.argoproj.io": "production",
   302  				},
   303  			},
   304  			Data: map[string][]byte{
   305  				"config": []byte("{}"),
   306  				"name":   []byte("production_01/west"),
   307  				"server": []byte("https://production-01.example.com"),
   308  			},
   309  			Type: corev1.SecretType("Opaque"),
   310  		},
   311  	}
   312  	testCases := []struct {
   313  		name     string
   314  		selector metav1.LabelSelector
   315  		values   map[string]string
   316  		expected []map[string]interface{}
   317  		// clientError is true if a k8s client error should be simulated
   318  		clientError   bool
   319  		expectedError error
   320  	}{
   321  		{
   322  			name:     "no label selector",
   323  			selector: metav1.LabelSelector{},
   324  			values: map[string]string{
   325  				"lol1":  "lol",
   326  				"lol2":  "{{ .values.lol1 }}{{ .values.lol1 }}",
   327  				"lol3":  "{{ .values.lol2 }}{{ .values.lol2 }}{{ .values.lol2 }}",
   328  				"foo":   "bar",
   329  				"bar":   "{{ if not (empty .metadata) }}{{index .metadata.annotations \"foo.argoproj.io\" }}{{ end }}",
   330  				"bat":   "{{ if not (empty .metadata) }}{{.metadata.labels.environment}}{{ end }}",
   331  				"aaa":   "{{ .server }}",
   332  				"no-op": "{{ .thisDoesNotExist }}",
   333  			}, expected: []map[string]interface{}{
   334  				{
   335  					"name":           "production_01/west",
   336  					"nameNormalized": "production-01-west",
   337  					"server":         "https://production-01.example.com",
   338  					"metadata": map[string]interface{}{
   339  						"labels": map[string]string{
   340  							"argocd.argoproj.io/secret-type": "cluster",
   341  							"environment":                    "production",
   342  							"org":                            "bar",
   343  						},
   344  						"annotations": map[string]string{
   345  							"foo.argoproj.io": "production",
   346  						},
   347  					},
   348  					"values": map[string]string{
   349  						"lol1":  "lol",
   350  						"lol2":  "<no value><no value>",
   351  						"lol3":  "<no value><no value><no value>",
   352  						"foo":   "bar",
   353  						"bar":   "production",
   354  						"bat":   "production",
   355  						"aaa":   "https://production-01.example.com",
   356  						"no-op": "<no value>",
   357  					},
   358  				},
   359  				{
   360  					"name":           "staging-01",
   361  					"nameNormalized": "staging-01",
   362  					"server":         "https://staging-01.example.com",
   363  					"metadata": map[string]interface{}{
   364  						"labels": map[string]string{
   365  							"argocd.argoproj.io/secret-type": "cluster",
   366  							"environment":                    "staging",
   367  							"org":                            "foo",
   368  						},
   369  						"annotations": map[string]string{
   370  							"foo.argoproj.io": "staging",
   371  						},
   372  					},
   373  					"values": map[string]string{
   374  						"lol1":  "lol",
   375  						"lol2":  "<no value><no value>",
   376  						"lol3":  "<no value><no value><no value>",
   377  						"foo":   "bar",
   378  						"bar":   "staging",
   379  						"bat":   "staging",
   380  						"aaa":   "https://staging-01.example.com",
   381  						"no-op": "<no value>",
   382  					},
   383  				},
   384  				{
   385  					"nameNormalized": "in-cluster",
   386  					"name":           "in-cluster",
   387  					"server":         "https://kubernetes.default.svc",
   388  					"values": map[string]string{
   389  						"lol1":  "lol",
   390  						"lol2":  "<no value><no value>",
   391  						"lol3":  "<no value><no value><no value>",
   392  						"foo":   "bar",
   393  						"bar":   "",
   394  						"bat":   "",
   395  						"aaa":   "https://kubernetes.default.svc",
   396  						"no-op": "<no value>",
   397  					},
   398  				},
   399  			},
   400  			clientError:   false,
   401  			expectedError: nil,
   402  		},
   403  		{
   404  			name: "secret type label selector",
   405  			selector: metav1.LabelSelector{
   406  				MatchLabels: map[string]string{
   407  					"argocd.argoproj.io/secret-type": "cluster",
   408  				},
   409  			},
   410  			values: nil,
   411  			expected: []map[string]interface{}{
   412  				{
   413  					"name":           "production_01/west",
   414  					"nameNormalized": "production-01-west",
   415  					"server":         "https://production-01.example.com",
   416  					"metadata": map[string]interface{}{
   417  						"labels": map[string]string{
   418  							"argocd.argoproj.io/secret-type": "cluster",
   419  							"environment":                    "production",
   420  							"org":                            "bar",
   421  						},
   422  						"annotations": map[string]string{
   423  							"foo.argoproj.io": "production",
   424  						},
   425  					},
   426  				},
   427  				{
   428  					"name":           "staging-01",
   429  					"nameNormalized": "staging-01",
   430  					"server":         "https://staging-01.example.com",
   431  					"metadata": map[string]interface{}{
   432  						"labels": map[string]string{
   433  							"argocd.argoproj.io/secret-type": "cluster",
   434  							"environment":                    "staging",
   435  							"org":                            "foo",
   436  						},
   437  						"annotations": map[string]string{
   438  							"foo.argoproj.io": "staging",
   439  						},
   440  					},
   441  				},
   442  			},
   443  			clientError:   false,
   444  			expectedError: nil,
   445  		},
   446  		{
   447  			name: "production-only",
   448  			selector: metav1.LabelSelector{
   449  				MatchLabels: map[string]string{
   450  					"environment": "production",
   451  				},
   452  			},
   453  			values: map[string]string{
   454  				"foo": "bar",
   455  			},
   456  			expected: []map[string]interface{}{
   457  				{
   458  					"name":           "production_01/west",
   459  					"nameNormalized": "production-01-west",
   460  					"server":         "https://production-01.example.com",
   461  					"metadata": map[string]interface{}{
   462  						"labels": map[string]string{
   463  							"argocd.argoproj.io/secret-type": "cluster",
   464  							"environment":                    "production",
   465  							"org":                            "bar",
   466  						},
   467  						"annotations": map[string]string{
   468  							"foo.argoproj.io": "production",
   469  						},
   470  					},
   471  					"values": map[string]string{
   472  						"foo": "bar",
   473  					},
   474  				},
   475  			},
   476  			clientError:   false,
   477  			expectedError: nil,
   478  		},
   479  		{
   480  			name: "production or staging",
   481  			selector: metav1.LabelSelector{
   482  				MatchExpressions: []metav1.LabelSelectorRequirement{
   483  					{
   484  						Key:      "environment",
   485  						Operator: "In",
   486  						Values: []string{
   487  							"production",
   488  							"staging",
   489  						},
   490  					},
   491  				},
   492  			},
   493  			values: map[string]string{
   494  				"foo": "bar",
   495  			},
   496  			expected: []map[string]interface{}{
   497  				{
   498  					"name":           "production_01/west",
   499  					"nameNormalized": "production-01-west",
   500  					"server":         "https://production-01.example.com",
   501  					"metadata": map[string]interface{}{
   502  						"labels": map[string]string{
   503  							"argocd.argoproj.io/secret-type": "cluster",
   504  							"environment":                    "production",
   505  							"org":                            "bar",
   506  						},
   507  						"annotations": map[string]string{
   508  							"foo.argoproj.io": "production",
   509  						},
   510  					},
   511  					"values": map[string]string{
   512  						"foo": "bar",
   513  					},
   514  				},
   515  				{
   516  					"name":           "staging-01",
   517  					"nameNormalized": "staging-01",
   518  					"server":         "https://staging-01.example.com",
   519  					"metadata": map[string]interface{}{
   520  						"labels": map[string]string{
   521  							"argocd.argoproj.io/secret-type": "cluster",
   522  							"environment":                    "staging",
   523  							"org":                            "foo",
   524  						},
   525  						"annotations": map[string]string{
   526  							"foo.argoproj.io": "staging",
   527  						},
   528  					},
   529  					"values": map[string]string{
   530  						"foo": "bar",
   531  					},
   532  				},
   533  			},
   534  			clientError:   false,
   535  			expectedError: nil,
   536  		},
   537  		{
   538  			name: "production or staging with match labels",
   539  			selector: metav1.LabelSelector{
   540  				MatchExpressions: []metav1.LabelSelectorRequirement{
   541  					{
   542  						Key:      "environment",
   543  						Operator: "In",
   544  						Values: []string{
   545  							"production",
   546  							"staging",
   547  						},
   548  					},
   549  				},
   550  				MatchLabels: map[string]string{
   551  					"org": "foo",
   552  				},
   553  			},
   554  			values: map[string]string{
   555  				"name": "baz",
   556  			},
   557  			expected: []map[string]interface{}{
   558  				{
   559  					"name":           "staging-01",
   560  					"nameNormalized": "staging-01",
   561  					"server":         "https://staging-01.example.com",
   562  					"metadata": map[string]interface{}{
   563  						"labels": map[string]string{
   564  							"argocd.argoproj.io/secret-type": "cluster",
   565  							"environment":                    "staging",
   566  							"org":                            "foo",
   567  						},
   568  						"annotations": map[string]string{
   569  							"foo.argoproj.io": "staging",
   570  						},
   571  					},
   572  					"values": map[string]string{
   573  						"name": "baz",
   574  					},
   575  				},
   576  			},
   577  			clientError:   false,
   578  			expectedError: nil,
   579  		},
   580  		{
   581  			name:          "simulate client error",
   582  			selector:      metav1.LabelSelector{},
   583  			values:        nil,
   584  			expected:      nil,
   585  			clientError:   true,
   586  			expectedError: fmt.Errorf("could not list Secrets"),
   587  		},
   588  	}
   589  
   590  	// convert []client.Object to []runtime.Object, for use by kubefake package
   591  	runtimeClusters := []runtime.Object{}
   592  	for _, clientCluster := range clusters {
   593  		runtimeClusters = append(runtimeClusters, clientCluster)
   594  	}
   595  
   596  	for _, testCase := range testCases {
   597  
   598  		t.Run(testCase.name, func(t *testing.T) {
   599  
   600  			appClientset := kubefake.NewSimpleClientset(runtimeClusters...)
   601  
   602  			fakeClient := fake.NewClientBuilder().WithObjects(clusters...).Build()
   603  			cl := &possiblyErroringFakeCtrlRuntimeClient{
   604  				fakeClient,
   605  				testCase.clientError,
   606  			}
   607  
   608  			var clusterGenerator = NewClusterGenerator(cl, context.Background(), appClientset, "namespace")
   609  
   610  			applicationSetInfo := argoprojiov1alpha1.ApplicationSet{
   611  				ObjectMeta: metav1.ObjectMeta{
   612  					Name: "set",
   613  				},
   614  				Spec: argoprojiov1alpha1.ApplicationSetSpec{
   615  					GoTemplate: true,
   616  				},
   617  			}
   618  
   619  			got, err := clusterGenerator.GenerateParams(&argoprojiov1alpha1.ApplicationSetGenerator{
   620  				Clusters: &argoprojiov1alpha1.ClusterGenerator{
   621  					Selector: testCase.selector,
   622  					Values:   testCase.values,
   623  				},
   624  			}, &applicationSetInfo)
   625  
   626  			if testCase.expectedError != nil {
   627  				assert.EqualError(t, err, testCase.expectedError.Error())
   628  			} else {
   629  				assert.NoError(t, err)
   630  				assert.ElementsMatch(t, testCase.expected, got)
   631  			}
   632  
   633  		})
   634  	}
   635  }
   636  
   637  func TestSanitizeClusterName(t *testing.T) {
   638  	t.Run("valid DNS-1123 subdomain name", func(t *testing.T) {
   639  		assert.Equal(t, "cluster-name", utils.SanitizeName("cluster-name"))
   640  	})
   641  	t.Run("invalid DNS-1123 subdomain name", func(t *testing.T) {
   642  		invalidName := "-.--CLUSTER/name  -./.-"
   643  		assert.Equal(t, "cluster-name", utils.SanitizeName(invalidName))
   644  	})
   645  }