github.com/argoproj/argo-cd/v3@v3.2.1/applicationset/controllers/clustereventhandler_test.go (about)

     1  package controllers
     2  
     3  import (
     4  	"testing"
     5  
     6  	argocommon "github.com/argoproj/argo-cd/v3/common"
     7  
     8  	log "github.com/sirupsen/logrus"
     9  	"github.com/stretchr/testify/assert"
    10  	"github.com/stretchr/testify/require"
    11  	corev1 "k8s.io/api/core/v1"
    12  	apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
    13  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    14  	"k8s.io/apimachinery/pkg/runtime"
    15  	"k8s.io/apimachinery/pkg/types"
    16  	ctrl "sigs.k8s.io/controller-runtime"
    17  	"sigs.k8s.io/controller-runtime/pkg/client/fake"
    18  	"sigs.k8s.io/controller-runtime/pkg/reconcile"
    19  
    20  	argov1alpha1 "github.com/argoproj/argo-cd/v3/pkg/apis/application/v1alpha1"
    21  )
    22  
    23  type mockAddRateLimitingInterface struct {
    24  	addedItems []reconcile.Request
    25  }
    26  
    27  // Add checks the type, and adds it to the internal list of received additions
    28  func (obj *mockAddRateLimitingInterface) Add(item reconcile.Request) {
    29  	obj.addedItems = append(obj.addedItems, item)
    30  }
    31  
    32  func TestClusterEventHandler(t *testing.T) {
    33  	scheme := runtime.NewScheme()
    34  	err := argov1alpha1.AddToScheme(scheme)
    35  	require.NoError(t, err)
    36  
    37  	err = argov1alpha1.AddToScheme(scheme)
    38  	require.NoError(t, err)
    39  
    40  	tests := []struct {
    41  		name             string
    42  		items            []argov1alpha1.ApplicationSet
    43  		secret           corev1.Secret
    44  		expectedRequests []ctrl.Request
    45  	}{
    46  		{
    47  			name:  "no application sets should mean no requests",
    48  			items: []argov1alpha1.ApplicationSet{},
    49  			secret: corev1.Secret{
    50  				ObjectMeta: metav1.ObjectMeta{
    51  					Namespace: "argocd",
    52  					Name:      "my-secret",
    53  					Labels: map[string]string{
    54  						argocommon.LabelKeySecretType: argocommon.LabelValueSecretTypeCluster,
    55  					},
    56  				},
    57  			},
    58  			expectedRequests: []reconcile.Request{},
    59  		},
    60  		{
    61  			name: "a cluster generator should produce a request",
    62  			items: []argov1alpha1.ApplicationSet{
    63  				{
    64  					ObjectMeta: metav1.ObjectMeta{
    65  						Name:      "my-app-set",
    66  						Namespace: "argocd",
    67  					},
    68  					Spec: argov1alpha1.ApplicationSetSpec{
    69  						Generators: []argov1alpha1.ApplicationSetGenerator{
    70  							{
    71  								Clusters: &argov1alpha1.ClusterGenerator{},
    72  							},
    73  						},
    74  					},
    75  				},
    76  			},
    77  			secret: corev1.Secret{
    78  				ObjectMeta: metav1.ObjectMeta{
    79  					Namespace: "argocd",
    80  					Name:      "my-secret",
    81  					Labels: map[string]string{
    82  						argocommon.LabelKeySecretType: argocommon.LabelValueSecretTypeCluster,
    83  					},
    84  				},
    85  			},
    86  			expectedRequests: []reconcile.Request{{
    87  				NamespacedName: types.NamespacedName{Namespace: "argocd", Name: "my-app-set"},
    88  			}},
    89  		},
    90  		{
    91  			name: "multiple cluster generators should produce multiple requests",
    92  			items: []argov1alpha1.ApplicationSet{
    93  				{
    94  					ObjectMeta: metav1.ObjectMeta{
    95  						Name:      "my-app-set",
    96  						Namespace: "argocd",
    97  					},
    98  					Spec: argov1alpha1.ApplicationSetSpec{
    99  						Generators: []argov1alpha1.ApplicationSetGenerator{
   100  							{
   101  								Clusters: &argov1alpha1.ClusterGenerator{},
   102  							},
   103  						},
   104  					},
   105  				},
   106  				{
   107  					ObjectMeta: metav1.ObjectMeta{
   108  						Name:      "my-app-set2",
   109  						Namespace: "argocd",
   110  					},
   111  					Spec: argov1alpha1.ApplicationSetSpec{
   112  						Generators: []argov1alpha1.ApplicationSetGenerator{
   113  							{
   114  								Clusters: &argov1alpha1.ClusterGenerator{},
   115  							},
   116  						},
   117  					},
   118  				},
   119  			},
   120  			secret: corev1.Secret{
   121  				ObjectMeta: metav1.ObjectMeta{
   122  					Namespace: "argocd",
   123  					Name:      "my-secret",
   124  					Labels: map[string]string{
   125  						argocommon.LabelKeySecretType: argocommon.LabelValueSecretTypeCluster,
   126  					},
   127  				},
   128  			},
   129  			expectedRequests: []reconcile.Request{
   130  				{NamespacedName: types.NamespacedName{Namespace: "argocd", Name: "my-app-set"}},
   131  				{NamespacedName: types.NamespacedName{Namespace: "argocd", Name: "my-app-set2"}},
   132  			},
   133  		},
   134  		{
   135  			name: "non-cluster generator should not match",
   136  			items: []argov1alpha1.ApplicationSet{
   137  				{
   138  					ObjectMeta: metav1.ObjectMeta{
   139  						Name:      "my-app-set",
   140  						Namespace: "another-namespace",
   141  					},
   142  					Spec: argov1alpha1.ApplicationSetSpec{
   143  						Generators: []argov1alpha1.ApplicationSetGenerator{
   144  							{
   145  								Clusters: &argov1alpha1.ClusterGenerator{},
   146  							},
   147  						},
   148  					},
   149  				},
   150  				{
   151  					ObjectMeta: metav1.ObjectMeta{
   152  						Name:      "app-set-non-cluster",
   153  						Namespace: "argocd",
   154  					},
   155  					Spec: argov1alpha1.ApplicationSetSpec{
   156  						Generators: []argov1alpha1.ApplicationSetGenerator{
   157  							{
   158  								List: &argov1alpha1.ListGenerator{},
   159  							},
   160  						},
   161  					},
   162  				},
   163  			},
   164  			secret: corev1.Secret{
   165  				ObjectMeta: metav1.ObjectMeta{
   166  					Namespace: "argocd",
   167  					Name:      "my-secret",
   168  					Labels: map[string]string{
   169  						argocommon.LabelKeySecretType: argocommon.LabelValueSecretTypeCluster,
   170  					},
   171  				},
   172  			},
   173  			expectedRequests: []reconcile.Request{
   174  				{NamespacedName: types.NamespacedName{Namespace: "another-namespace", Name: "my-app-set"}},
   175  			},
   176  		},
   177  		{
   178  			name: "non-argo cd secret should not match",
   179  			items: []argov1alpha1.ApplicationSet{
   180  				{
   181  					ObjectMeta: metav1.ObjectMeta{
   182  						Name:      "my-app-set",
   183  						Namespace: "another-namespace",
   184  					},
   185  					Spec: argov1alpha1.ApplicationSetSpec{
   186  						Generators: []argov1alpha1.ApplicationSetGenerator{
   187  							{
   188  								Clusters: &argov1alpha1.ClusterGenerator{},
   189  							},
   190  						},
   191  					},
   192  				},
   193  			},
   194  			secret: corev1.Secret{
   195  				ObjectMeta: metav1.ObjectMeta{
   196  					Namespace: "argocd",
   197  					Name:      "my-non-argocd-secret",
   198  				},
   199  			},
   200  			expectedRequests: []reconcile.Request{},
   201  		},
   202  		{
   203  			name: "a matrix generator with a cluster generator should produce a request",
   204  			items: []argov1alpha1.ApplicationSet{
   205  				{
   206  					ObjectMeta: metav1.ObjectMeta{
   207  						Name:      "my-app-set",
   208  						Namespace: "argocd",
   209  					},
   210  					Spec: argov1alpha1.ApplicationSetSpec{
   211  						Generators: []argov1alpha1.ApplicationSetGenerator{
   212  							{
   213  								Matrix: &argov1alpha1.MatrixGenerator{
   214  									Generators: []argov1alpha1.ApplicationSetNestedGenerator{
   215  										{
   216  											Clusters: &argov1alpha1.ClusterGenerator{},
   217  										},
   218  									},
   219  								},
   220  							},
   221  						},
   222  					},
   223  				},
   224  			},
   225  			secret: corev1.Secret{
   226  				ObjectMeta: metav1.ObjectMeta{
   227  					Namespace: "argocd",
   228  					Name:      "my-secret",
   229  					Labels: map[string]string{
   230  						argocommon.LabelKeySecretType: argocommon.LabelValueSecretTypeCluster,
   231  					},
   232  				},
   233  			},
   234  			expectedRequests: []reconcile.Request{{
   235  				NamespacedName: types.NamespacedName{Namespace: "argocd", Name: "my-app-set"},
   236  			}},
   237  		},
   238  		{
   239  			name: "a matrix generator with non cluster generator should not match",
   240  			items: []argov1alpha1.ApplicationSet{
   241  				{
   242  					ObjectMeta: metav1.ObjectMeta{
   243  						Name:      "my-app-set",
   244  						Namespace: "argocd",
   245  					},
   246  					Spec: argov1alpha1.ApplicationSetSpec{
   247  						Generators: []argov1alpha1.ApplicationSetGenerator{
   248  							{
   249  								Matrix: &argov1alpha1.MatrixGenerator{
   250  									Generators: []argov1alpha1.ApplicationSetNestedGenerator{
   251  										{
   252  											List: &argov1alpha1.ListGenerator{},
   253  										},
   254  									},
   255  								},
   256  							},
   257  						},
   258  					},
   259  				},
   260  			},
   261  			secret: corev1.Secret{
   262  				ObjectMeta: metav1.ObjectMeta{
   263  					Namespace: "argocd",
   264  					Name:      "my-secret",
   265  					Labels: map[string]string{
   266  						argocommon.LabelKeySecretType: argocommon.LabelValueSecretTypeCluster,
   267  					},
   268  				},
   269  			},
   270  			expectedRequests: []reconcile.Request{},
   271  		},
   272  		{
   273  			name: "a matrix generator with a nested matrix generator containing a cluster generator should produce a request",
   274  			items: []argov1alpha1.ApplicationSet{
   275  				{
   276  					ObjectMeta: metav1.ObjectMeta{
   277  						Name:      "my-app-set",
   278  						Namespace: "argocd",
   279  					},
   280  					Spec: argov1alpha1.ApplicationSetSpec{
   281  						Generators: []argov1alpha1.ApplicationSetGenerator{
   282  							{
   283  								Matrix: &argov1alpha1.MatrixGenerator{
   284  									Generators: []argov1alpha1.ApplicationSetNestedGenerator{
   285  										{
   286  											Matrix: &apiextensionsv1.JSON{
   287  												Raw: []byte(
   288  													`{
   289  														"generators": [
   290  														  {
   291  															"clusters": {
   292  															  "selector": {
   293  																"matchLabels": {
   294  																  "argocd.argoproj.io/secret-type": "cluster"
   295  																}
   296  															  }
   297  															}
   298  														  }
   299  														]
   300  													  }`,
   301  												),
   302  											},
   303  										},
   304  									},
   305  								},
   306  							},
   307  						},
   308  					},
   309  				},
   310  			},
   311  			secret: corev1.Secret{
   312  				ObjectMeta: metav1.ObjectMeta{
   313  					Namespace: "argocd",
   314  					Name:      "my-secret",
   315  					Labels: map[string]string{
   316  						argocommon.LabelKeySecretType: argocommon.LabelValueSecretTypeCluster,
   317  					},
   318  				},
   319  			},
   320  			expectedRequests: []reconcile.Request{{
   321  				NamespacedName: types.NamespacedName{Namespace: "argocd", Name: "my-app-set"},
   322  			}},
   323  		},
   324  		{
   325  			name: "a matrix generator with a nested matrix generator containing non cluster generator should not match",
   326  			items: []argov1alpha1.ApplicationSet{
   327  				{
   328  					ObjectMeta: metav1.ObjectMeta{
   329  						Name:      "my-app-set",
   330  						Namespace: "argocd",
   331  					},
   332  					Spec: argov1alpha1.ApplicationSetSpec{
   333  						Generators: []argov1alpha1.ApplicationSetGenerator{
   334  							{
   335  								Matrix: &argov1alpha1.MatrixGenerator{
   336  									Generators: []argov1alpha1.ApplicationSetNestedGenerator{
   337  										{
   338  											Matrix: &apiextensionsv1.JSON{
   339  												Raw: []byte(
   340  													`{
   341  														"generators": [
   342  														  {
   343  															"list": {
   344  															  "elements": [
   345  																"a",
   346  																"b"
   347  															  ]
   348  															}
   349  														  }
   350  														]
   351  													  }`,
   352  												),
   353  											},
   354  										},
   355  									},
   356  								},
   357  							},
   358  						},
   359  					},
   360  				},
   361  			},
   362  			secret: corev1.Secret{
   363  				ObjectMeta: metav1.ObjectMeta{
   364  					Namespace: "argocd",
   365  					Name:      "my-secret",
   366  					Labels: map[string]string{
   367  						argocommon.LabelKeySecretType: argocommon.LabelValueSecretTypeCluster,
   368  					},
   369  				},
   370  			},
   371  			expectedRequests: []reconcile.Request{},
   372  		},
   373  		{
   374  			name: "a merge generator with a cluster generator should produce a request",
   375  			items: []argov1alpha1.ApplicationSet{
   376  				{
   377  					ObjectMeta: metav1.ObjectMeta{
   378  						Name:      "my-app-set",
   379  						Namespace: "argocd",
   380  					},
   381  					Spec: argov1alpha1.ApplicationSetSpec{
   382  						Generators: []argov1alpha1.ApplicationSetGenerator{
   383  							{
   384  								Merge: &argov1alpha1.MergeGenerator{
   385  									Generators: []argov1alpha1.ApplicationSetNestedGenerator{
   386  										{
   387  											Clusters: &argov1alpha1.ClusterGenerator{},
   388  										},
   389  									},
   390  								},
   391  							},
   392  						},
   393  					},
   394  				},
   395  			},
   396  			secret: corev1.Secret{
   397  				ObjectMeta: metav1.ObjectMeta{
   398  					Namespace: "argocd",
   399  					Name:      "my-secret",
   400  					Labels: map[string]string{
   401  						argocommon.LabelKeySecretType: argocommon.LabelValueSecretTypeCluster,
   402  					},
   403  				},
   404  			},
   405  			expectedRequests: []reconcile.Request{{
   406  				NamespacedName: types.NamespacedName{Namespace: "argocd", Name: "my-app-set"},
   407  			}},
   408  		},
   409  		{
   410  			name: "a matrix generator with non cluster generator should not match",
   411  			items: []argov1alpha1.ApplicationSet{
   412  				{
   413  					ObjectMeta: metav1.ObjectMeta{
   414  						Name:      "my-app-set",
   415  						Namespace: "argocd",
   416  					},
   417  					Spec: argov1alpha1.ApplicationSetSpec{
   418  						Generators: []argov1alpha1.ApplicationSetGenerator{
   419  							{
   420  								Merge: &argov1alpha1.MergeGenerator{
   421  									Generators: []argov1alpha1.ApplicationSetNestedGenerator{
   422  										{
   423  											List: &argov1alpha1.ListGenerator{},
   424  										},
   425  									},
   426  								},
   427  							},
   428  						},
   429  					},
   430  				},
   431  			},
   432  			secret: corev1.Secret{
   433  				ObjectMeta: metav1.ObjectMeta{
   434  					Namespace: "argocd",
   435  					Name:      "my-secret",
   436  					Labels: map[string]string{
   437  						argocommon.LabelKeySecretType: argocommon.LabelValueSecretTypeCluster,
   438  					},
   439  				},
   440  			},
   441  			expectedRequests: []reconcile.Request{},
   442  		},
   443  		{
   444  			name: "a merge generator with a nested merge generator containing a cluster generator should produce a request",
   445  			items: []argov1alpha1.ApplicationSet{
   446  				{
   447  					ObjectMeta: metav1.ObjectMeta{
   448  						Name:      "my-app-set",
   449  						Namespace: "argocd",
   450  					},
   451  					Spec: argov1alpha1.ApplicationSetSpec{
   452  						Generators: []argov1alpha1.ApplicationSetGenerator{
   453  							{
   454  								Merge: &argov1alpha1.MergeGenerator{
   455  									Generators: []argov1alpha1.ApplicationSetNestedGenerator{
   456  										{
   457  											Merge: &apiextensionsv1.JSON{
   458  												Raw: []byte(
   459  													`{
   460  														"generators": [
   461  														  {
   462  															"clusters": {
   463  															  "selector": {
   464  																"matchLabels": {
   465  																  "argocd.argoproj.io/secret-type": "cluster"
   466  																}
   467  															  }
   468  															}
   469  														  }
   470  														]
   471  													  }`,
   472  												),
   473  											},
   474  										},
   475  									},
   476  								},
   477  							},
   478  						},
   479  					},
   480  				},
   481  			},
   482  			secret: corev1.Secret{
   483  				ObjectMeta: metav1.ObjectMeta{
   484  					Namespace: "argocd",
   485  					Name:      "my-secret",
   486  					Labels: map[string]string{
   487  						argocommon.LabelKeySecretType: argocommon.LabelValueSecretTypeCluster,
   488  					},
   489  				},
   490  			},
   491  			expectedRequests: []reconcile.Request{{
   492  				NamespacedName: types.NamespacedName{Namespace: "argocd", Name: "my-app-set"},
   493  			}},
   494  		},
   495  		{
   496  			name: "a merge generator with a nested merge generator containing non cluster generator should not match",
   497  			items: []argov1alpha1.ApplicationSet{
   498  				{
   499  					ObjectMeta: metav1.ObjectMeta{
   500  						Name:      "my-app-set",
   501  						Namespace: "argocd",
   502  					},
   503  					Spec: argov1alpha1.ApplicationSetSpec{
   504  						Generators: []argov1alpha1.ApplicationSetGenerator{
   505  							{
   506  								Merge: &argov1alpha1.MergeGenerator{
   507  									Generators: []argov1alpha1.ApplicationSetNestedGenerator{
   508  										{
   509  											Merge: &apiextensionsv1.JSON{
   510  												Raw: []byte(
   511  													`{
   512  														"generators": [
   513  														  {
   514  															"list": {
   515  															  "elements": [
   516  																"a",
   517  																"b"
   518  															  ]
   519  															}
   520  														  }
   521  														]
   522  													  }`,
   523  												),
   524  											},
   525  										},
   526  									},
   527  								},
   528  							},
   529  						},
   530  					},
   531  				},
   532  			},
   533  			secret: corev1.Secret{
   534  				ObjectMeta: metav1.ObjectMeta{
   535  					Namespace: "argocd",
   536  					Name:      "my-secret",
   537  					Labels: map[string]string{
   538  						argocommon.LabelKeySecretType: argocommon.LabelValueSecretTypeCluster,
   539  					},
   540  				},
   541  			},
   542  			expectedRequests: []reconcile.Request{},
   543  		},
   544  	}
   545  
   546  	for _, test := range tests {
   547  		t.Run(test.name, func(t *testing.T) {
   548  			appSetList := argov1alpha1.ApplicationSetList{
   549  				Items: test.items,
   550  			}
   551  
   552  			fakeClient := fake.NewClientBuilder().WithScheme(scheme).WithLists(&appSetList).Build()
   553  
   554  			handler := &clusterSecretEventHandler{
   555  				Client: fakeClient,
   556  				Log:    log.WithField("type", "createSecretEventHandler"),
   557  			}
   558  
   559  			mockAddRateLimitingInterface := mockAddRateLimitingInterface{}
   560  
   561  			handler.queueRelatedAppGenerators(t.Context(), &mockAddRateLimitingInterface, &test.secret)
   562  
   563  			assert.ElementsMatch(t, mockAddRateLimitingInterface.addedItems, test.expectedRequests)
   564  		})
   565  	}
   566  }
   567  
   568  func TestNestedGeneratorHasClusterGenerator_NestedClusterGenerator(t *testing.T) {
   569  	nested := argov1alpha1.ApplicationSetNestedGenerator{
   570  		Clusters: &argov1alpha1.ClusterGenerator{},
   571  	}
   572  
   573  	hasClusterGenerator, err := nestedGeneratorHasClusterGenerator(nested)
   574  
   575  	require.NoError(t, err)
   576  	assert.True(t, hasClusterGenerator)
   577  }
   578  
   579  func TestNestedGeneratorHasClusterGenerator_NestedMergeGenerator(t *testing.T) {
   580  	nested := argov1alpha1.ApplicationSetNestedGenerator{
   581  		Merge: &apiextensionsv1.JSON{
   582  			Raw: []byte(
   583  				`{
   584  					"generators": [
   585  					  {
   586  						"clusters": {
   587  						  "selector": {
   588  							"matchLabels": {
   589  							  "argocd.argoproj.io/secret-type": "cluster"
   590  							}
   591  						  }
   592  						}
   593  					  }
   594  					]
   595  				  }`,
   596  			),
   597  		},
   598  	}
   599  
   600  	hasClusterGenerator, err := nestedGeneratorHasClusterGenerator(nested)
   601  
   602  	require.NoError(t, err)
   603  	assert.True(t, hasClusterGenerator)
   604  }
   605  
   606  func TestNestedGeneratorHasClusterGenerator_NestedMergeGeneratorWithInvalidJSON(t *testing.T) {
   607  	nested := argov1alpha1.ApplicationSetNestedGenerator{
   608  		Merge: &apiextensionsv1.JSON{
   609  			Raw: []byte(
   610  				`{
   611  					"generators": [
   612  					  {
   613  						"clusters": {
   614  						  "selector": {
   615  							"matchLabels": {
   616  							  "argocd.argoproj.io/secret-type": "cluster"
   617  							}
   618  						  }
   619  						}
   620  					  }
   621  					]
   622  				  `,
   623  			),
   624  		},
   625  	}
   626  
   627  	hasClusterGenerator, err := nestedGeneratorHasClusterGenerator(nested)
   628  
   629  	require.Error(t, err)
   630  	assert.False(t, hasClusterGenerator)
   631  }