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 }