github.com/argoproj-labs/argocd-operator@v0.10.0/api/v1alpha1/argocd_conversion_test.go (about)

     1  package v1alpha1
     2  
     3  import (
     4  	"testing"
     5  
     6  	"github.com/stretchr/testify/assert"
     7  	corev1 "k8s.io/api/core/v1"
     8  	v1 "k8s.io/api/networking/v1"
     9  	resourcev1 "k8s.io/apimachinery/pkg/api/resource"
    10  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    11  	"sigs.k8s.io/controller-runtime/pkg/conversion"
    12  
    13  	v1beta1 "github.com/argoproj-labs/argocd-operator/api/v1beta1"
    14  )
    15  
    16  type argoCDAlphaOpt func(*ArgoCD)
    17  
    18  func makeTestArgoCDAlpha(opts ...argoCDAlphaOpt) *ArgoCD {
    19  	a := &ArgoCD{
    20  		ObjectMeta: metav1.ObjectMeta{
    21  			Name:      "test-argocd",
    22  			Namespace: "default",
    23  			Labels: map[string]string{
    24  				"example": "conversion",
    25  			},
    26  		},
    27  	}
    28  	for _, o := range opts {
    29  		o(a)
    30  	}
    31  	return a
    32  }
    33  
    34  type argoCDBetaOpt func(*v1beta1.ArgoCD)
    35  
    36  func makeTestArgoCDBeta(opts ...argoCDBetaOpt) *v1beta1.ArgoCD {
    37  	a := &v1beta1.ArgoCD{
    38  		ObjectMeta: metav1.ObjectMeta{
    39  			Name:      "test-argocd",
    40  			Namespace: "default",
    41  			Labels: map[string]string{
    42  				"example": "conversion",
    43  			},
    44  		},
    45  	}
    46  	for _, o := range opts {
    47  		o(a)
    48  	}
    49  	return a
    50  }
    51  
    52  // in case of conflict, deprecated fields will have more priority during conversion to beta
    53  func TestAlphaToBetaConversion(t *testing.T) {
    54  	tests := []struct {
    55  		name           string
    56  		input          *ArgoCD
    57  		expectedOutput *v1beta1.ArgoCD
    58  	}{
    59  		// dex conversion
    60  		{
    61  			name: ".dex -> .sso.dex",
    62  			input: makeTestArgoCDAlpha(func(cr *ArgoCD) {
    63  				cr.Spec.Dex = &ArgoCDDexSpec{
    64  					OpenShiftOAuth: true,
    65  					Image:          "test",
    66  					Version:        "latest",
    67  				}
    68  			}),
    69  			expectedOutput: makeTestArgoCDBeta(func(cr *v1beta1.ArgoCD) {
    70  				cr.Spec.SSO = &v1beta1.ArgoCDSSOSpec{
    71  					Provider: "dex",
    72  					Dex: &v1beta1.ArgoCDDexSpec{
    73  						OpenShiftOAuth: true,
    74  						Image:          "test",
    75  						Version:        "latest",
    76  					},
    77  				}
    78  			}),
    79  		},
    80  		{
    81  			name: "Conflict: .dex & .sso.dex -> .sso.dex (values from v1alpha1.spec.dex)",
    82  			input: makeTestArgoCDAlpha(func(cr *ArgoCD) {
    83  				cr.Spec.Dex = &ArgoCDDexSpec{
    84  					OpenShiftOAuth: true,
    85  					Resources: &corev1.ResourceRequirements{
    86  						Limits: corev1.ResourceList{
    87  							corev1.ResourceMemory: resourcev1.MustParse("2048Mi"),
    88  							corev1.ResourceCPU:    resourcev1.MustParse("2000m"),
    89  						},
    90  					},
    91  				}
    92  				cr.Spec.SSO = &ArgoCDSSOSpec{
    93  					Provider: SSOProviderTypeDex,
    94  					Dex: &ArgoCDDexSpec{
    95  						Config: "test-config",
    96  						Image:  "test-image",
    97  					},
    98  				}
    99  			}),
   100  			expectedOutput: makeTestArgoCDBeta(func(cr *v1beta1.ArgoCD) {
   101  				cr.Spec.SSO = &v1beta1.ArgoCDSSOSpec{
   102  					Provider: v1beta1.SSOProviderTypeDex,
   103  					Dex: &v1beta1.ArgoCDDexSpec{
   104  						OpenShiftOAuth: true,
   105  						Resources: &corev1.ResourceRequirements{
   106  							Limits: corev1.ResourceList{
   107  								corev1.ResourceMemory: resourcev1.MustParse("2048Mi"),
   108  								corev1.ResourceCPU:    resourcev1.MustParse("2000m"),
   109  							},
   110  						},
   111  					},
   112  				}
   113  			}),
   114  		},
   115  		{
   116  			name: "Missing dex provider: .dex & .sso.dex -> .spec.sso(values from v1alpha1.spec.dex with dex provider set)",
   117  			input: makeTestArgoCDAlpha(func(cr *ArgoCD) {
   118  				cr.Spec.Dex = &ArgoCDDexSpec{
   119  					Config: "test-config",
   120  				}
   121  				cr.Spec.SSO = &ArgoCDSSOSpec{
   122  					Dex: &ArgoCDDexSpec{
   123  						OpenShiftOAuth: false,
   124  					},
   125  				}
   126  			}),
   127  			expectedOutput: makeTestArgoCDBeta(func(cr *v1beta1.ArgoCD) {
   128  				cr.Spec.SSO = &v1beta1.ArgoCDSSOSpec{
   129  					Provider: v1beta1.SSOProviderTypeDex,
   130  					Dex: &v1beta1.ArgoCDDexSpec{
   131  						Config: "test-config",
   132  					},
   133  				}
   134  			}),
   135  		},
   136  		{
   137  			name: "Missing dex provider without deprecated dex: .sso.dex -> .sso(values from v1alpha1.spec.sso)",
   138  			input: makeTestArgoCDAlpha(func(cr *ArgoCD) {
   139  				cr.Spec.SSO = &ArgoCDSSOSpec{
   140  					Dex: &ArgoCDDexSpec{
   141  						OpenShiftOAuth: false,
   142  					},
   143  				}
   144  			}),
   145  			expectedOutput: makeTestArgoCDBeta(func(cr *v1beta1.ArgoCD) {
   146  				cr.Spec.SSO = &v1beta1.ArgoCDSSOSpec{
   147  					Dex: &v1beta1.ArgoCDDexSpec{
   148  						OpenShiftOAuth: false,
   149  					},
   150  				}
   151  			}),
   152  		},
   153  
   154  		// dex + keycloak - .spec.dex has more priority
   155  		{
   156  			name: "Conflict: .dex & .sso.keycloak provider -> .sso.dex + .sso.keycloak with dex provider",
   157  			input: makeTestArgoCDAlpha(func(cr *ArgoCD) {
   158  				cr.Spec.Dex = &ArgoCDDexSpec{
   159  					OpenShiftOAuth: true,
   160  				}
   161  				cr.Spec.SSO = &ArgoCDSSOSpec{
   162  					Provider: SSOProviderTypeKeycloak,
   163  					Keycloak: &ArgoCDKeycloakSpec{
   164  						Image: "keycloak",
   165  					},
   166  				}
   167  			}),
   168  			expectedOutput: makeTestArgoCDBeta(func(cr *v1beta1.ArgoCD) {
   169  				cr.Spec.SSO = &v1beta1.ArgoCDSSOSpec{
   170  					Provider: v1beta1.SSOProviderTypeDex,
   171  					Dex: &v1beta1.ArgoCDDexSpec{
   172  						OpenShiftOAuth: true,
   173  					},
   174  					Keycloak: &v1beta1.ArgoCDKeycloakSpec{
   175  						Image: "keycloak",
   176  					},
   177  				}
   178  			}),
   179  		},
   180  
   181  		// keycloak conversion
   182  		{
   183  			name: ".sso.VerifyTLS -> .sso.Keycloak.VerifyTLS",
   184  			input: makeTestArgoCDAlpha(func(cr *ArgoCD) {
   185  				tls := new(bool)
   186  				*tls = false
   187  				cr.Spec.SSO = &ArgoCDSSOSpec{
   188  					Provider: SSOProviderTypeKeycloak,
   189  					Keycloak: &ArgoCDKeycloakSpec{
   190  						RootCA: "__CA__",
   191  					},
   192  					VerifyTLS: tls,
   193  				}
   194  			}),
   195  			expectedOutput: makeTestArgoCDBeta(func(cr *v1beta1.ArgoCD) {
   196  				tls := new(bool)
   197  				*tls = false
   198  				cr.Spec.SSO = &v1beta1.ArgoCDSSOSpec{
   199  					Provider: v1beta1.SSOProviderTypeKeycloak,
   200  					Keycloak: &v1beta1.ArgoCDKeycloakSpec{
   201  						RootCA:    "__CA__",
   202  						VerifyTLS: tls,
   203  					},
   204  				}
   205  			}),
   206  		},
   207  		{
   208  			name: ".sso.Image without provider -> .sso.Keycloak.Image without provider",
   209  			input: makeTestArgoCDAlpha(func(cr *ArgoCD) {
   210  				cr.Spec.SSO = &ArgoCDSSOSpec{
   211  					Image: "test-image",
   212  				}
   213  			}),
   214  			expectedOutput: makeTestArgoCDBeta(func(cr *v1beta1.ArgoCD) {
   215  				cr.Spec.SSO = &v1beta1.ArgoCDSSOSpec{
   216  					Keycloak: &v1beta1.ArgoCDKeycloakSpec{
   217  						Image: "test-image",
   218  					},
   219  				}
   220  			}),
   221  		},
   222  
   223  		// other fields
   224  		{
   225  			name:           "ArgoCD Example - Empty",
   226  			input:          makeTestArgoCDAlpha(func(cr *ArgoCD) {}),
   227  			expectedOutput: makeTestArgoCDBeta(func(cr *v1beta1.ArgoCD) {}),
   228  		},
   229  		{
   230  			name: "ArgoCD Example - Dex + RBAC",
   231  			input: makeTestArgoCDAlpha(func(cr *ArgoCD) {
   232  				cr.Spec.Dex = &ArgoCDDexSpec{
   233  					OpenShiftOAuth: true,
   234  				}
   235  
   236  				defaultPolicy := "role:readonly"
   237  				policy := "g, system:cluster-admins, role:admin"
   238  				scope := "[groups]"
   239  				cr.Spec.RBAC = ArgoCDRBACSpec{
   240  					DefaultPolicy: &defaultPolicy,
   241  					Policy:        &policy,
   242  					Scopes:        &scope,
   243  				}
   244  
   245  				cr.Spec.Server = ArgoCDServerSpec{
   246  					Route: ArgoCDRouteSpec{
   247  						Enabled: true,
   248  					},
   249  				}
   250  			}),
   251  			expectedOutput: makeTestArgoCDBeta(func(cr *v1beta1.ArgoCD) {
   252  				cr.Spec.SSO = &v1beta1.ArgoCDSSOSpec{
   253  					Provider: v1beta1.SSOProviderTypeDex,
   254  					Dex: &v1beta1.ArgoCDDexSpec{
   255  						OpenShiftOAuth: true,
   256  					},
   257  				}
   258  
   259  				defaultPolicy := "role:readonly"
   260  				policy := "g, system:cluster-admins, role:admin"
   261  				scope := "[groups]"
   262  				cr.Spec.RBAC = v1beta1.ArgoCDRBACSpec{
   263  					DefaultPolicy: &defaultPolicy,
   264  					Policy:        &policy,
   265  					Scopes:        &scope,
   266  				}
   267  
   268  				cr.Spec.Server = v1beta1.ArgoCDServerSpec{
   269  					Route: v1beta1.ArgoCDRouteSpec{
   270  						Enabled: true,
   271  					},
   272  				}
   273  			}),
   274  		},
   275  		{
   276  			name: "ArgoCD Example - ResourceCustomizations",
   277  			input: makeTestArgoCDAlpha(func(cr *ArgoCD) {
   278  				cr.Spec.ResourceIgnoreDifferences = &ResourceIgnoreDifference{
   279  					All: &IgnoreDifferenceCustomization{
   280  						JsonPointers: []string{
   281  							"/spec/replicas",
   282  						},
   283  						ManagedFieldsManagers: []string{
   284  							"kube-controller-manager",
   285  						},
   286  					},
   287  					ResourceIdentifiers: []ResourceIdentifiers{
   288  						{
   289  							Group: "admissionregistration.k8s.io",
   290  							Kind:  "MutatingWebhookConfiguration",
   291  							Customization: IgnoreDifferenceCustomization{
   292  								JqPathExpressions: []string{
   293  									"'.webhooks[]?.clientConfig.caBundle'",
   294  								},
   295  							},
   296  						},
   297  						{
   298  							Group: "apps",
   299  							Kind:  "Deployment",
   300  							Customization: IgnoreDifferenceCustomization{
   301  								ManagedFieldsManagers: []string{
   302  									"kube-controller-manager",
   303  								},
   304  								JsonPointers: []string{
   305  									"/spec/replicas",
   306  								},
   307  							},
   308  						},
   309  					},
   310  				}
   311  				cr.Spec.ResourceHealthChecks = []ResourceHealthCheck{
   312  					{
   313  						Group: "certmanager.k8s.io",
   314  						Kind:  "Certificate",
   315  					},
   316  				}
   317  				cr.Spec.ResourceActions = []ResourceAction{
   318  					{
   319  						Group: "apps",
   320  						Kind:  "Deployment",
   321  					},
   322  				}
   323  			}),
   324  			expectedOutput: makeTestArgoCDBeta(func(cr *v1beta1.ArgoCD) {
   325  				cr.Spec.ResourceIgnoreDifferences = &v1beta1.ResourceIgnoreDifference{
   326  					All: &v1beta1.IgnoreDifferenceCustomization{
   327  						JsonPointers: []string{
   328  							"/spec/replicas",
   329  						},
   330  						ManagedFieldsManagers: []string{
   331  							"kube-controller-manager",
   332  						},
   333  					},
   334  					ResourceIdentifiers: []v1beta1.ResourceIdentifiers{
   335  						{
   336  							Group: "admissionregistration.k8s.io",
   337  							Kind:  "MutatingWebhookConfiguration",
   338  							Customization: v1beta1.IgnoreDifferenceCustomization{
   339  								JqPathExpressions: []string{
   340  									"'.webhooks[]?.clientConfig.caBundle'",
   341  								},
   342  							},
   343  						},
   344  						{
   345  							Group: "apps",
   346  							Kind:  "Deployment",
   347  							Customization: v1beta1.IgnoreDifferenceCustomization{
   348  								ManagedFieldsManagers: []string{
   349  									"kube-controller-manager",
   350  								},
   351  								JsonPointers: []string{
   352  									"/spec/replicas",
   353  								},
   354  							},
   355  						},
   356  					},
   357  				}
   358  				cr.Spec.ResourceHealthChecks = []v1beta1.ResourceHealthCheck{
   359  					{
   360  						Group: "certmanager.k8s.io",
   361  						Kind:  "Certificate",
   362  					},
   363  				}
   364  				cr.Spec.ResourceActions = []v1beta1.ResourceAction{
   365  					{
   366  						Group: "apps",
   367  						Kind:  "Deployment",
   368  					},
   369  				}
   370  			}),
   371  		},
   372  		{
   373  			name: "ArgoCD Example - Image + ExtraConfig",
   374  			input: makeTestArgoCDAlpha(func(cr *ArgoCD) {
   375  				cr.Spec.Image = "test-image"
   376  				cr.Spec.ExtraConfig = map[string]string{
   377  					"ping": "pong",
   378  				}
   379  			}),
   380  			expectedOutput: makeTestArgoCDBeta(func(cr *v1beta1.ArgoCD) {
   381  				cr.Spec.Image = "test-image"
   382  				cr.Spec.ExtraConfig = map[string]string{
   383  					"ping": "pong",
   384  				}
   385  			}),
   386  		},
   387  		{
   388  			name: "ArgoCD Example - Sever + Import",
   389  			input: makeTestArgoCDAlpha(func(cr *ArgoCD) {
   390  				cr.Spec.Server.Autoscale = ArgoCDServerAutoscaleSpec{
   391  					Enabled: true,
   392  				}
   393  				cr.Spec.Import = &ArgoCDImportSpec{
   394  					Name: "test-name",
   395  				}
   396  				cr.Spec.Server = ArgoCDServerSpec{
   397  					Host: "test-host.argocd.org",
   398  					GRPC: ArgoCDServerGRPCSpec{
   399  						Ingress: ArgoCDIngressSpec{
   400  							Enabled: false,
   401  						},
   402  					},
   403  					Ingress: ArgoCDIngressSpec{
   404  						Enabled: true,
   405  						TLS: []v1.IngressTLS{
   406  							{Hosts: []string{
   407  								"test-tls",
   408  							}},
   409  						},
   410  					},
   411  					Insecure: true,
   412  				}
   413  			}),
   414  			expectedOutput: makeTestArgoCDBeta(func(cr *v1beta1.ArgoCD) {
   415  				cr.Spec.Server.Autoscale = v1beta1.ArgoCDServerAutoscaleSpec{
   416  					Enabled: true,
   417  				}
   418  				cr.Spec.Import = &v1beta1.ArgoCDImportSpec{
   419  					Name: "test-name",
   420  				}
   421  				cr.Spec.Server = v1beta1.ArgoCDServerSpec{
   422  					Host: "test-host.argocd.org",
   423  					GRPC: v1beta1.ArgoCDServerGRPCSpec{
   424  						Ingress: v1beta1.ArgoCDIngressSpec{
   425  							Enabled: false,
   426  						},
   427  					},
   428  					Ingress: v1beta1.ArgoCDIngressSpec{
   429  						Enabled: true,
   430  						TLS: []v1.IngressTLS{
   431  							{Hosts: []string{
   432  								"test-tls",
   433  							}},
   434  						},
   435  					},
   436  					Insecure: true,
   437  				}
   438  			}),
   439  		},
   440  	}
   441  
   442  	for _, test := range tests {
   443  		t.Run(test.name, func(t *testing.T) {
   444  
   445  			// Set v1beta1 object in Hub, converted values will be set in this object.
   446  			var hub conversion.Hub = &v1beta1.ArgoCD{}
   447  
   448  			// Call ConvertTo function to convert v1alpha1 version to v1beta1
   449  			test.input.ConvertTo(hub)
   450  
   451  			// Fetch the converted object
   452  			result := hub.(*v1beta1.ArgoCD)
   453  
   454  			// Compare converted object with expected.
   455  			assert.Equal(t, test.expectedOutput, result)
   456  		})
   457  	}
   458  }
   459  
   460  // During beta to alpha conversion, converting sso fields back to deprecated fields is ignored as
   461  // there is no data loss since the new fields in v1beta1 are also present in v1alpha1
   462  func TestBetaToAlphaConversion(t *testing.T) {
   463  	tests := []struct {
   464  		name           string
   465  		input          *v1beta1.ArgoCD
   466  		expectedOutput *ArgoCD
   467  	}{
   468  		{
   469  			name:           "ArgoCD Example - Empty",
   470  			input:          makeTestArgoCDBeta(func(cr *v1beta1.ArgoCD) {}),
   471  			expectedOutput: makeTestArgoCDAlpha(func(cr *ArgoCD) {}),
   472  		},
   473  		{
   474  			name: "ArgoCD Example - Image + ExtraConfig",
   475  			input: makeTestArgoCDBeta(func(cr *v1beta1.ArgoCD) {
   476  				cr.Spec.Image = "test-image"
   477  				cr.Spec.ExtraConfig = map[string]string{
   478  					"ping": "pong",
   479  				}
   480  			}),
   481  			expectedOutput: makeTestArgoCDAlpha(func(cr *ArgoCD) {
   482  				cr.Spec.Image = "test-image"
   483  				cr.Spec.ExtraConfig = map[string]string{
   484  					"ping": "pong",
   485  				}
   486  			}),
   487  		},
   488  		{
   489  			name: "ArgoCD Example - Dex + RBAC",
   490  			input: makeTestArgoCDBeta(func(cr *v1beta1.ArgoCD) {
   491  				cr.Spec.SSO = &v1beta1.ArgoCDSSOSpec{
   492  					Provider: v1beta1.SSOProviderTypeDex,
   493  					Dex: &v1beta1.ArgoCDDexSpec{
   494  						OpenShiftOAuth: true,
   495  					},
   496  				}
   497  
   498  				defaultPolicy := "role:readonly"
   499  				policy := "g, system:cluster-admins, role:admin"
   500  				scope := "[groups]"
   501  				cr.Spec.RBAC = v1beta1.ArgoCDRBACSpec{
   502  					DefaultPolicy: &defaultPolicy,
   503  					Policy:        &policy,
   504  					Scopes:        &scope,
   505  				}
   506  
   507  				cr.Spec.Server = v1beta1.ArgoCDServerSpec{
   508  					Route: v1beta1.ArgoCDRouteSpec{
   509  						Enabled: true,
   510  					},
   511  				}
   512  			}),
   513  			expectedOutput: makeTestArgoCDAlpha(func(cr *ArgoCD) {
   514  				cr.Spec.SSO = &ArgoCDSSOSpec{
   515  					Provider: SSOProviderTypeDex,
   516  					Dex: &ArgoCDDexSpec{
   517  						OpenShiftOAuth: true,
   518  					},
   519  				}
   520  
   521  				defaultPolicy := "role:readonly"
   522  				policy := "g, system:cluster-admins, role:admin"
   523  				scope := "[groups]"
   524  				cr.Spec.RBAC = ArgoCDRBACSpec{
   525  					DefaultPolicy: &defaultPolicy,
   526  					Policy:        &policy,
   527  					Scopes:        &scope,
   528  				}
   529  
   530  				cr.Spec.Server = ArgoCDServerSpec{
   531  					Route: ArgoCDRouteSpec{
   532  						Enabled: true,
   533  					},
   534  				}
   535  			}),
   536  		},
   537  	}
   538  	for _, test := range tests {
   539  		t.Run(test.name, func(t *testing.T) {
   540  
   541  			// Add input v1beta1 object in Hub
   542  			var hub conversion.Hub = test.input
   543  
   544  			result := &ArgoCD{}
   545  			// Call ConvertFrom function to convert v1beta1 version to v1alpha
   546  			result.ConvertFrom(hub)
   547  
   548  			// Compare converted object with expected.
   549  			assert.Equal(t, test.expectedOutput, result)
   550  		})
   551  	}
   552  }