github.com/operator-framework/operator-lifecycle-manager@v0.30.0/pkg/controller/operators/olm/requirements_test.go (about) 1 package olm 2 3 import ( 4 "context" 5 "fmt" 6 "testing" 7 8 "github.com/operator-framework/operator-lifecycle-manager/pkg/controller/install" 9 "github.com/stretchr/testify/require" 10 corev1 "k8s.io/api/core/v1" 11 rbacv1 "k8s.io/api/rbac/v1" 12 apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" 13 "k8s.io/apimachinery/pkg/api/errors" 14 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 15 "k8s.io/apimachinery/pkg/runtime" 16 "k8s.io/apimachinery/pkg/runtime/schema" 17 "k8s.io/apimachinery/pkg/types" 18 19 "github.com/operator-framework/api/pkg/operators/v1alpha1" 20 "github.com/operator-framework/operator-lifecycle-manager/pkg/controller/operators/internal/alongside" 21 "github.com/operator-framework/operator-lifecycle-manager/pkg/lib/operatorlister/operatorlisterfakes" 22 "github.com/stretchr/testify/assert" 23 ) 24 25 func TestRequirementAndPermissionStatus(t *testing.T) { 26 namespace := "ns" 27 type gvkn struct { 28 group string 29 version string 30 kind string 31 name string 32 } 33 tests := []struct { 34 description string 35 csv *v1alpha1.ClusterServiceVersion 36 existingObjs []runtime.Object 37 existingExtObjs []runtime.Object 38 met bool 39 expectedRequirementStatuses map[gvkn]v1alpha1.RequirementStatus 40 expectedError error 41 }{ 42 { 43 description: "AllPermissionsMet", 44 csv: csvWithUID(csv("csv1", 45 namespace, 46 "0.0.0", 47 "", 48 installStrategy( 49 "csv1-dep", 50 []v1alpha1.StrategyDeploymentPermissions{ 51 { 52 ServiceAccountName: "sa", 53 Rules: []rbacv1.PolicyRule{ 54 { 55 APIGroups: []string{""}, 56 Verbs: []string{"*"}, 57 Resources: []string{"donuts"}, 58 }, 59 }, 60 }, 61 }, 62 []v1alpha1.StrategyDeploymentPermissions{ 63 { 64 ServiceAccountName: "sa", 65 Rules: []rbacv1.PolicyRule{ 66 { 67 Verbs: []string{"get"}, 68 NonResourceURLs: []string{"/osbs"}, 69 }, 70 }, 71 }, 72 }, 73 ), 74 nil, 75 nil, 76 v1alpha1.CSVPhasePending, 77 ), types.UID("csv-uid")), 78 existingObjs: []runtime.Object{ 79 &corev1.ServiceAccount{ 80 ObjectMeta: metav1.ObjectMeta{ 81 Name: "sa", 82 Namespace: namespace, 83 UID: types.UID("sa"), 84 Labels: map[string]string{install.OLMManagedLabelKey: install.OLMManagedLabelValue}, 85 OwnerReferences: []metav1.OwnerReference{ 86 { 87 Kind: v1alpha1.ClusterServiceVersionKind, 88 UID: "csv-uid", 89 }, 90 }, 91 }, 92 }, 93 &rbacv1.Role{ 94 ObjectMeta: metav1.ObjectMeta{ 95 Name: "role", 96 Namespace: namespace, 97 Labels: map[string]string{install.OLMManagedLabelKey: install.OLMManagedLabelValue}, 98 }, 99 Rules: []rbacv1.PolicyRule{ 100 { 101 APIGroups: []string{""}, 102 Verbs: []string{"*"}, 103 Resources: []string{"donuts"}, 104 }, 105 }, 106 }, 107 &rbacv1.RoleBinding{ 108 ObjectMeta: metav1.ObjectMeta{ 109 Name: "roleBinding", 110 Namespace: namespace, 111 Labels: map[string]string{install.OLMManagedLabelKey: install.OLMManagedLabelValue}, 112 }, 113 Subjects: []rbacv1.Subject{ 114 { 115 Kind: "ServiceAccount", 116 APIGroup: "", 117 Name: "sa", 118 Namespace: namespace, 119 }, 120 }, 121 RoleRef: rbacv1.RoleRef{ 122 APIGroup: "rbac.authorization.k8s.io", 123 Kind: "Role", 124 Name: "role", 125 }, 126 }, 127 &rbacv1.ClusterRole{ 128 ObjectMeta: metav1.ObjectMeta{ 129 Name: "clusterRole", 130 Labels: map[string]string{install.OLMManagedLabelKey: install.OLMManagedLabelValue}, 131 }, 132 Rules: []rbacv1.PolicyRule{ 133 { 134 Verbs: []string{"get"}, 135 NonResourceURLs: []string{"/osbs"}, 136 }, 137 }, 138 }, 139 &rbacv1.ClusterRoleBinding{ 140 ObjectMeta: metav1.ObjectMeta{ 141 Name: "clusterRoleBinding", 142 Labels: map[string]string{install.OLMManagedLabelKey: install.OLMManagedLabelValue}, 143 }, 144 Subjects: []rbacv1.Subject{ 145 { 146 Kind: "ServiceAccount", 147 APIGroup: "", 148 Name: "sa", 149 Namespace: namespace, 150 }, 151 }, 152 RoleRef: rbacv1.RoleRef{ 153 APIGroup: "rbac.authorization.k8s.io", 154 Kind: "ClusterRole", 155 Name: "clusterRole", 156 }, 157 }, 158 }, 159 existingExtObjs: nil, 160 met: true, 161 expectedRequirementStatuses: map[gvkn]v1alpha1.RequirementStatus{ 162 {"", "v1", "ServiceAccount", "sa"}: { 163 Group: "", 164 Version: "v1", 165 Kind: "ServiceAccount", 166 Name: "sa", 167 Status: v1alpha1.RequirementStatusReasonPresent, 168 Dependents: []v1alpha1.DependentStatus{ 169 { 170 Group: "rbac.authorization.k8s.io", 171 Kind: "PolicyRule", 172 Version: "v1", 173 }, 174 { 175 Group: "rbac.authorization.k8s.io", 176 Kind: "PolicyRule", 177 Version: "v1", 178 }, 179 }, 180 }, 181 {"operators.coreos.com", "v1alpha1", "ClusterServiceVersion", "csv1"}: { 182 Group: "operators.coreos.com", 183 Version: "v1alpha1", 184 Kind: "ClusterServiceVersion", 185 Name: "csv1", 186 Status: v1alpha1.RequirementStatusReasonPresent, 187 }, 188 }, 189 expectedError: nil, 190 }, 191 { 192 description: "OnePermissionNotMet", 193 csv: csvWithUID(csv("csv1", 194 namespace, 195 "0.0.0", 196 "", 197 installStrategy( 198 "csv1-dep", 199 []v1alpha1.StrategyDeploymentPermissions{ 200 { 201 ServiceAccountName: "sa", 202 Rules: []rbacv1.PolicyRule{ 203 { 204 APIGroups: []string{""}, 205 Verbs: []string{"*"}, 206 Resources: []string{"donuts"}, 207 }, 208 }, 209 }, 210 }, 211 []v1alpha1.StrategyDeploymentPermissions{ 212 { 213 ServiceAccountName: "sa", 214 Rules: []rbacv1.PolicyRule{ 215 { 216 Verbs: []string{"get"}, 217 NonResourceURLs: []string{"/osbs"}, 218 }, 219 }, 220 }, 221 }, 222 ), 223 nil, 224 nil, 225 v1alpha1.CSVPhasePending, 226 ), types.UID("csv-uid")), 227 existingObjs: []runtime.Object{ 228 &corev1.ServiceAccount{ 229 ObjectMeta: metav1.ObjectMeta{ 230 Name: "sa", 231 Namespace: namespace, 232 UID: types.UID("sa"), 233 Labels: map[string]string{install.OLMManagedLabelKey: install.OLMManagedLabelValue}, 234 OwnerReferences: []metav1.OwnerReference{ 235 { 236 Kind: v1alpha1.ClusterServiceVersionKind, 237 UID: "csv-uid", 238 }, 239 }, 240 }, 241 }, 242 &rbacv1.Role{ 243 ObjectMeta: metav1.ObjectMeta{ 244 Name: "role", 245 Namespace: namespace, 246 Labels: map[string]string{install.OLMManagedLabelKey: install.OLMManagedLabelValue}, 247 }, 248 Rules: []rbacv1.PolicyRule{ 249 { 250 APIGroups: []string{""}, 251 Verbs: []string{"*"}, 252 Resources: []string{"donuts"}, 253 }, 254 }, 255 }, 256 &rbacv1.RoleBinding{ 257 ObjectMeta: metav1.ObjectMeta{ 258 Name: "roleBinding", 259 Namespace: namespace, 260 Labels: map[string]string{install.OLMManagedLabelKey: install.OLMManagedLabelValue}, 261 }, 262 Subjects: []rbacv1.Subject{ 263 { 264 Kind: "ServiceAccount", 265 APIGroup: "", 266 Name: "sa", 267 Namespace: namespace, 268 }, 269 }, 270 RoleRef: rbacv1.RoleRef{ 271 APIGroup: "rbac.authorization.k8s.io", 272 Kind: "Role", 273 Name: "role", 274 }, 275 }, 276 &rbacv1.ClusterRole{ 277 ObjectMeta: metav1.ObjectMeta{ 278 Name: "clusterRole", 279 Labels: map[string]string{install.OLMManagedLabelKey: install.OLMManagedLabelValue}, 280 }, 281 Rules: []rbacv1.PolicyRule{ 282 { 283 Verbs: []string{"get"}, 284 NonResourceURLs: []string{"/osbs/*"}, 285 }, 286 }, 287 }, 288 &rbacv1.ClusterRoleBinding{ 289 ObjectMeta: metav1.ObjectMeta{ 290 Name: "clusterRoleBinding", 291 Labels: map[string]string{install.OLMManagedLabelKey: install.OLMManagedLabelValue}, 292 }, 293 Subjects: []rbacv1.Subject{ 294 { 295 Kind: "ServiceAccount", 296 APIGroup: "", 297 Name: "sa", 298 Namespace: namespace, 299 }, 300 }, 301 RoleRef: rbacv1.RoleRef{ 302 APIGroup: "rbac.authorization.k8s.io", 303 Kind: "ClusterRole", 304 Name: "clusterRole", 305 }, 306 }, 307 }, 308 existingExtObjs: nil, 309 met: false, 310 expectedRequirementStatuses: map[gvkn]v1alpha1.RequirementStatus{ 311 {"", "v1", "ServiceAccount", "sa"}: { 312 Group: "", 313 Version: "v1", 314 Kind: "ServiceAccount", 315 Name: "sa", 316 Status: v1alpha1.RequirementStatusReasonPresentNotSatisfied, 317 Dependents: []v1alpha1.DependentStatus{ 318 { 319 Group: "rbac.authorization.k8s.io", 320 Kind: "PolicyRule", 321 Version: "v1", 322 }, 323 { 324 Group: "rbac.authorization.k8s.io", 325 Kind: "PolicyRule", 326 Version: "v1", 327 }, 328 }, 329 }, 330 {"operators.coreos.com", "v1alpha1", "ClusterServiceVersion", "csv1"}: { 331 Group: "operators.coreos.com", 332 Version: "v1alpha1", 333 Kind: "ClusterServiceVersion", 334 Name: "csv1", 335 Status: v1alpha1.RequirementStatusReasonPresent, 336 }, 337 }, 338 expectedError: nil, 339 }, 340 { 341 description: "RequirementNotMet/ServiceAccountOwnerConflict", 342 csv: csvWithUID(csv("csv1", 343 namespace, 344 "0.0.0", 345 "", 346 installStrategy( 347 "csv1-dep", 348 []v1alpha1.StrategyDeploymentPermissions{ 349 { 350 ServiceAccountName: "sa", 351 Rules: []rbacv1.PolicyRule{ 352 { 353 APIGroups: []string{""}, 354 Verbs: []string{"*"}, 355 Resources: []string{"donuts"}, 356 }, 357 }, 358 }, 359 }, 360 []v1alpha1.StrategyDeploymentPermissions{ 361 { 362 ServiceAccountName: "sa", 363 Rules: []rbacv1.PolicyRule{ 364 { 365 Verbs: []string{"get"}, 366 NonResourceURLs: []string{"/osbs"}, 367 }, 368 }, 369 }, 370 }, 371 ), 372 nil, 373 nil, 374 v1alpha1.CSVPhasePending, 375 ), types.UID("csv-uid")), 376 existingObjs: []runtime.Object{ 377 &corev1.ServiceAccount{ 378 ObjectMeta: metav1.ObjectMeta{ 379 Name: "sa", 380 Namespace: namespace, 381 UID: types.UID("sa"), 382 Labels: map[string]string{install.OLMManagedLabelKey: install.OLMManagedLabelValue}, 383 OwnerReferences: []metav1.OwnerReference{ 384 { 385 Kind: v1alpha1.ClusterServiceVersionKind, 386 UID: "csv-uid-other", 387 }, 388 }, 389 }, 390 }, 391 &rbacv1.Role{ 392 ObjectMeta: metav1.ObjectMeta{ 393 Name: "role", 394 Namespace: namespace, 395 Labels: map[string]string{install.OLMManagedLabelKey: install.OLMManagedLabelValue}, 396 }, 397 Rules: []rbacv1.PolicyRule{ 398 { 399 APIGroups: []string{""}, 400 Verbs: []string{"*"}, 401 Resources: []string{"donuts"}, 402 }, 403 }, 404 }, 405 &rbacv1.RoleBinding{ 406 ObjectMeta: metav1.ObjectMeta{ 407 Name: "roleBinding", 408 Namespace: namespace, 409 Labels: map[string]string{install.OLMManagedLabelKey: install.OLMManagedLabelValue}, 410 }, 411 Subjects: []rbacv1.Subject{ 412 { 413 Kind: "ServiceAccount", 414 APIGroup: "", 415 Name: "sa", 416 Namespace: namespace, 417 }, 418 }, 419 RoleRef: rbacv1.RoleRef{ 420 APIGroup: "rbac.authorization.k8s.io", 421 Kind: "Role", 422 Name: "role", 423 }, 424 }, 425 &rbacv1.ClusterRole{ 426 ObjectMeta: metav1.ObjectMeta{ 427 Name: "clusterRole", 428 Labels: map[string]string{install.OLMManagedLabelKey: install.OLMManagedLabelValue}, 429 }, 430 Rules: []rbacv1.PolicyRule{ 431 { 432 Verbs: []string{"get"}, 433 NonResourceURLs: []string{"/osbs"}, 434 }, 435 }, 436 }, 437 &rbacv1.ClusterRoleBinding{ 438 ObjectMeta: metav1.ObjectMeta{ 439 Name: "clusterRoleBinding", 440 Labels: map[string]string{install.OLMManagedLabelKey: install.OLMManagedLabelValue}, 441 }, 442 Subjects: []rbacv1.Subject{ 443 { 444 Kind: "ServiceAccount", 445 APIGroup: "", 446 Name: "sa", 447 Namespace: namespace, 448 }, 449 }, 450 RoleRef: rbacv1.RoleRef{ 451 APIGroup: "rbac.authorization.k8s.io", 452 Kind: "ClusterRole", 453 Name: "clusterRole", 454 }, 455 }, 456 }, 457 existingExtObjs: nil, 458 met: false, 459 expectedRequirementStatuses: map[gvkn]v1alpha1.RequirementStatus{ 460 {"", "v1", "ServiceAccount", "sa"}: { 461 Group: "", 462 Version: "v1", 463 Kind: "ServiceAccount", 464 Name: "sa", 465 Status: v1alpha1.RequirementStatusReasonPresentNotSatisfied, 466 Dependents: []v1alpha1.DependentStatus{}, 467 }, 468 {"operators.coreos.com", "v1alpha1", "ClusterServiceVersion", "csv1"}: { 469 Group: "operators.coreos.com", 470 Version: "v1alpha1", 471 Kind: "ClusterServiceVersion", 472 Name: "csv1", 473 Status: v1alpha1.RequirementStatusReasonPresent, 474 }, 475 }, 476 expectedError: nil, 477 }, 478 { 479 description: "AllRequirementsMet", 480 csv: csvWithUID(csv("csv1", 481 namespace, 482 "0.0.0", 483 "", 484 installStrategy( 485 "csv1-dep", 486 []v1alpha1.StrategyDeploymentPermissions{ 487 { 488 ServiceAccountName: "sa", 489 Rules: []rbacv1.PolicyRule{ 490 { 491 APIGroups: []string{""}, 492 Verbs: []string{"*"}, 493 Resources: []string{"donuts"}, 494 }, 495 }, 496 }, 497 }, 498 nil, 499 ), 500 []*apiextensionsv1.CustomResourceDefinition{crd("c1", "v1", "g1")}, 501 []*apiextensionsv1.CustomResourceDefinition{crd("c2", "v1", "g2")}, 502 v1alpha1.CSVPhasePending, 503 ), types.UID("csv-uid")), 504 existingObjs: []runtime.Object{ 505 &corev1.ServiceAccount{ 506 ObjectMeta: metav1.ObjectMeta{ 507 Name: "sa", 508 Namespace: namespace, 509 UID: types.UID("sa"), 510 Labels: map[string]string{install.OLMManagedLabelKey: install.OLMManagedLabelValue}, 511 OwnerReferences: []metav1.OwnerReference{ 512 { 513 Kind: v1alpha1.ClusterServiceVersionKind, 514 UID: "csv-uid", 515 }, 516 }, 517 }, 518 }, 519 &rbacv1.Role{ 520 ObjectMeta: metav1.ObjectMeta{ 521 Name: "role", 522 Namespace: namespace, 523 Labels: map[string]string{install.OLMManagedLabelKey: install.OLMManagedLabelValue}, 524 }, 525 Rules: []rbacv1.PolicyRule{ 526 { 527 APIGroups: []string{""}, 528 Verbs: []string{"*"}, 529 Resources: []string{"donuts"}, 530 }, 531 }, 532 }, 533 &rbacv1.RoleBinding{ 534 ObjectMeta: metav1.ObjectMeta{ 535 Name: "roleBinding", 536 Namespace: namespace, 537 Labels: map[string]string{install.OLMManagedLabelKey: install.OLMManagedLabelValue}, 538 }, 539 Subjects: []rbacv1.Subject{ 540 { 541 Kind: "ServiceAccount", 542 APIGroup: "", 543 Name: "sa", 544 Namespace: namespace, 545 }, 546 }, 547 RoleRef: rbacv1.RoleRef{ 548 APIGroup: "rbac.authorization.k8s.io", 549 Kind: "Role", 550 Name: "role", 551 }, 552 }, 553 }, 554 existingExtObjs: []runtime.Object{ 555 crd("c1", "v1", "g1"), 556 crd("c2", "v1", "g2"), 557 }, 558 met: true, 559 expectedRequirementStatuses: map[gvkn]v1alpha1.RequirementStatus{ 560 {"", "v1", "ServiceAccount", "sa"}: { 561 Group: "", 562 Version: "v1", 563 Kind: "ServiceAccount", 564 Name: "sa", 565 Status: v1alpha1.RequirementStatusReasonPresent, 566 Dependents: []v1alpha1.DependentStatus{ 567 { 568 Group: "rbac.authorization.k8s.io", 569 Kind: "PolicyRule", 570 Version: "v1", 571 }, 572 }, 573 }, 574 {"", "v1", "ServiceAccount", "sa"}: { 575 Group: "", 576 Version: "v1", 577 Kind: "ServiceAccount", 578 Name: "sa", 579 Status: v1alpha1.RequirementStatusReasonPresent, 580 Dependents: []v1alpha1.DependentStatus{ 581 { 582 Group: "rbac.authorization.k8s.io", 583 Kind: "PolicyRule", 584 Version: "v1", 585 }, 586 }, 587 }, 588 {"apiextensions.k8s.io", "v1", "CustomResourceDefinition", "c1.g1"}: { 589 Group: "apiextensions.k8s.io", 590 Version: "v1", 591 Kind: "CustomResourceDefinition", 592 Name: "c1.g1", 593 Status: v1alpha1.RequirementStatusReasonPresent, 594 }, 595 {"apiextensions.k8s.io", "v1", "CustomResourceDefinition", "c2.g2"}: { 596 Group: "apiextensions.k8s.io", 597 Version: "v1", 598 Kind: "CustomResourceDefinition", 599 Name: "c2.g2", 600 Status: v1alpha1.RequirementStatusReasonPresent, 601 }, 602 {"operators.coreos.com", "v1alpha1", "ClusterServiceVersion", "csv1"}: { 603 Group: "operators.coreos.com", 604 Version: "v1alpha1", 605 Kind: "ClusterServiceVersion", 606 Name: "csv1", 607 Status: v1alpha1.RequirementStatusReasonPresent, 608 }, 609 }, 610 expectedError: nil, 611 }, 612 { 613 description: "RequirementNotMet/NonServedCRDVersion", 614 csv: csv("csv1", 615 namespace, 616 "0.0.0", 617 "", 618 installStrategy("csv1-dep", nil, nil), 619 []*apiextensionsv1.CustomResourceDefinition{crd("c1", "v2", "g1")}, 620 nil, 621 v1alpha1.CSVPhasePending, 622 ), 623 existingObjs: nil, 624 existingExtObjs: []runtime.Object{ 625 crd("c1", "v1", "g1"), 626 }, 627 met: false, 628 expectedRequirementStatuses: map[gvkn]v1alpha1.RequirementStatus{ 629 {"apiextensions.k8s.io", "v1", "CustomResourceDefinition", "c1.g1"}: { 630 Group: "apiextensions.k8s.io", 631 Version: "v1", 632 Kind: "CustomResourceDefinition", 633 Name: "c1.g1", 634 Status: v1alpha1.RequirementStatusReasonNotPresent, 635 }, 636 {"operators.coreos.com", "v1alpha1", "ClusterServiceVersion", "csv1"}: { 637 Group: "operators.coreos.com", 638 Version: "v1alpha1", 639 Kind: "ClusterServiceVersion", 640 Name: "csv1", 641 Status: v1alpha1.RequirementStatusReasonPresent, 642 }, 643 }, 644 expectedError: nil, 645 }, 646 { 647 description: "RequirementNotMet/NotEstablishedCRDVersion", 648 csv: csv("csv1", 649 namespace, 650 "0.0.0", 651 "", 652 installStrategy("csv1-dep", nil, nil), 653 []*apiextensionsv1.CustomResourceDefinition{crd("c1", "version-not-found", "g1")}, 654 nil, 655 v1alpha1.CSVPhasePending, 656 ), 657 existingObjs: nil, 658 existingExtObjs: []runtime.Object{ 659 crd("c1", "v2", "g1"), 660 }, 661 met: false, 662 expectedRequirementStatuses: map[gvkn]v1alpha1.RequirementStatus{ 663 {"apiextensions.k8s.io", "v1", "CustomResourceDefinition", "c1.g1"}: { 664 Group: "apiextensions.k8s.io", 665 Version: "v1", 666 Kind: "CustomResourceDefinition", 667 Name: "c1.g1", 668 Status: v1alpha1.RequirementStatusReasonNotPresent, 669 }, 670 {"operators.coreos.com", "v1alpha1", "ClusterServiceVersion", "csv1"}: { 671 Group: "operators.coreos.com", 672 Version: "v1alpha1", 673 Kind: "ClusterServiceVersion", 674 Name: "csv1", 675 Status: v1alpha1.RequirementStatusReasonPresent, 676 }, 677 }, 678 expectedError: nil, 679 }, 680 { 681 description: "RequirementNotMet/NamesConflictedCRD", 682 csv: csv("csv1", 683 namespace, 684 "0.0.0", 685 "", 686 installStrategy("csv1-dep", nil, nil), 687 []*apiextensionsv1.CustomResourceDefinition{crd("c1", "v2", "g1")}, 688 nil, 689 v1alpha1.CSVPhasePending, 690 ), 691 existingObjs: nil, 692 existingExtObjs: []runtime.Object{ 693 func() *apiextensionsv1.CustomResourceDefinition { 694 newCRD := crd("c1", "v2", "g1") 695 // condition order: established, name accepted 696 newCRD.Status.Conditions[0].Status = apiextensionsv1.ConditionTrue 697 newCRD.Status.Conditions[1].Status = apiextensionsv1.ConditionFalse 698 return newCRD 699 }(), 700 }, 701 met: false, 702 expectedRequirementStatuses: map[gvkn]v1alpha1.RequirementStatus{ 703 {"apiextensions.k8s.io", "v1", "CustomResourceDefinition", "c1.g1"}: { 704 Group: "apiextensions.k8s.io", 705 Version: "v1", 706 Kind: "CustomResourceDefinition", 707 Name: "c1.g1", 708 Status: v1alpha1.RequirementStatusReasonNotAvailable, 709 }, 710 {"operators.coreos.com", "v1alpha1", "ClusterServiceVersion", "csv1"}: { 711 Group: "operators.coreos.com", 712 Version: "v1alpha1", 713 Kind: "ClusterServiceVersion", 714 Name: "csv1", 715 Status: v1alpha1.RequirementStatusReasonPresent, 716 }, 717 }, 718 expectedError: nil, 719 }, 720 { 721 description: "RequirementNotMet/CRDResourceInactive", 722 csv: csv("csv1", 723 namespace, 724 "0.0.0", 725 "", 726 installStrategy("csv1-dep", nil, nil), 727 []*apiextensionsv1.CustomResourceDefinition{crd("c1", "v2", "g1")}, 728 nil, 729 v1alpha1.CSVPhasePending, 730 ), 731 existingObjs: nil, 732 existingExtObjs: []runtime.Object{ 733 func() *apiextensionsv1.CustomResourceDefinition { 734 newCRD := crd("c1", "v2", "g1") 735 // condition order: established, name accepted 736 newCRD.Status.Conditions[0].Status = apiextensionsv1.ConditionFalse 737 newCRD.Status.Conditions[1].Status = apiextensionsv1.ConditionTrue 738 return newCRD 739 }(), 740 }, 741 met: false, 742 expectedRequirementStatuses: map[gvkn]v1alpha1.RequirementStatus{ 743 {"apiextensions.k8s.io", "v1", "CustomResourceDefinition", "c1.g1"}: { 744 Group: "apiextensions.k8s.io", 745 Version: "v1", 746 Kind: "CustomResourceDefinition", 747 Name: "c1.g1", 748 Status: v1alpha1.RequirementStatusReasonNotAvailable, 749 }, 750 {"operators.coreos.com", "v1alpha1", "ClusterServiceVersion", "csv1"}: { 751 Group: "operators.coreos.com", 752 Version: "v1alpha1", 753 Kind: "ClusterServiceVersion", 754 Name: "csv1", 755 Status: v1alpha1.RequirementStatusReasonPresent, 756 }, 757 }, 758 expectedError: nil, 759 }, 760 { 761 description: "RequirementNotMet/StaleServiceAccount", 762 csv: csvWithUID(csv("csv1", 763 namespace, 764 "0.0.0", 765 "", 766 installStrategy( 767 "csv1-dep", 768 []v1alpha1.StrategyDeploymentPermissions{ 769 { 770 ServiceAccountName: "sa", 771 Rules: []rbacv1.PolicyRule{ 772 { 773 APIGroups: []string{""}, 774 Verbs: []string{"*"}, 775 Resources: []string{"donuts"}, 776 }, 777 }, 778 }, 779 }, 780 nil, 781 ), 782 nil, 783 nil, 784 v1alpha1.CSVPhasePending, 785 ), types.UID("csv-uid")), 786 existingObjs: []runtime.Object{ 787 &corev1.ServiceAccount{ 788 ObjectMeta: metav1.ObjectMeta{ 789 Name: "sa", 790 Namespace: namespace, 791 UID: types.UID("sa"), 792 Labels: map[string]string{install.OLMManagedLabelKey: install.OLMManagedLabelValue}, 793 OwnerReferences: []metav1.OwnerReference{ 794 { 795 Kind: v1alpha1.ClusterServiceVersionKind, 796 UID: "csv-wrong", 797 }, 798 }, 799 }, 800 }, 801 }, 802 existingExtObjs: nil, 803 met: false, 804 expectedRequirementStatuses: map[gvkn]v1alpha1.RequirementStatus{ 805 {"", "v1", "ServiceAccount", "sa"}: { 806 Version: "v1", 807 Kind: "ServiceAccount", 808 Name: "sa", 809 Status: v1alpha1.RequirementStatusReasonPresentNotSatisfied, 810 Dependents: []v1alpha1.DependentStatus{}, 811 }, 812 {"operators.coreos.com", "v1alpha1", "ClusterServiceVersion", "csv1"}: { 813 Group: "operators.coreos.com", 814 Version: "v1alpha1", 815 Kind: "ClusterServiceVersion", 816 Name: "csv1", 817 Status: v1alpha1.RequirementStatusReasonPresent, 818 }, 819 }, 820 expectedError: nil, 821 }, 822 { 823 description: "RequirementMet/ServiceAccountOwnedByNonCSV", 824 csv: csvWithUID(csv("csv", 825 namespace, 826 "0.0.0", 827 "", 828 installStrategy( 829 "csv-dep", 830 []v1alpha1.StrategyDeploymentPermissions{ 831 { 832 ServiceAccountName: "sa", 833 }, 834 }, 835 nil, 836 ), 837 nil, 838 nil, 839 v1alpha1.CSVPhasePending, 840 ), types.UID("csv-uid")), 841 existingObjs: []runtime.Object{ 842 &corev1.ServiceAccount{ 843 ObjectMeta: metav1.ObjectMeta{ 844 Name: "sa", 845 Namespace: namespace, 846 UID: types.UID("sa"), 847 Labels: map[string]string{install.OLMManagedLabelKey: install.OLMManagedLabelValue}, 848 OwnerReferences: []metav1.OwnerReference{ 849 { 850 Kind: v1alpha1.SubscriptionKind, // arbitrary non-CSV kind 851 UID: "non-csv", 852 }, 853 }, 854 }, 855 }, 856 }, 857 existingExtObjs: nil, 858 met: true, 859 expectedRequirementStatuses: map[gvkn]v1alpha1.RequirementStatus{ 860 {"", "v1", "ServiceAccount", "sa"}: { 861 Version: "v1", 862 Kind: "ServiceAccount", 863 Name: "sa", 864 Status: v1alpha1.RequirementStatusReasonPresent, 865 Dependents: []v1alpha1.DependentStatus{}, 866 }, 867 {"operators.coreos.com", "v1alpha1", "ClusterServiceVersion", "csv"}: { 868 Group: "operators.coreos.com", 869 Version: "v1alpha1", 870 Kind: "ClusterServiceVersion", 871 Name: "csv", 872 Status: v1alpha1.RequirementStatusReasonPresent, 873 }, 874 }, 875 expectedError: nil, 876 }, 877 { 878 description: "RequirementMet/ServiceAccountHasNoOwner", 879 csv: csvWithUID(csv("csv", 880 namespace, 881 "0.0.0", 882 "", 883 installStrategy( 884 "csv-dep", 885 []v1alpha1.StrategyDeploymentPermissions{ 886 { 887 ServiceAccountName: "sa", 888 }, 889 }, 890 nil, 891 ), 892 nil, 893 nil, 894 v1alpha1.CSVPhasePending, 895 ), types.UID("csv-uid")), 896 existingObjs: []runtime.Object{ 897 &corev1.ServiceAccount{ 898 ObjectMeta: metav1.ObjectMeta{ 899 Name: "sa", 900 Namespace: namespace, 901 Labels: map[string]string{install.OLMManagedLabelKey: install.OLMManagedLabelValue}, 902 UID: types.UID("sa"), 903 }, 904 }, 905 }, 906 existingExtObjs: nil, 907 met: true, 908 expectedRequirementStatuses: map[gvkn]v1alpha1.RequirementStatus{ 909 {"", "v1", "ServiceAccount", "sa"}: { 910 Version: "v1", 911 Kind: "ServiceAccount", 912 Name: "sa", 913 Status: v1alpha1.RequirementStatusReasonPresent, 914 Dependents: []v1alpha1.DependentStatus{}, 915 }, 916 {"operators.coreos.com", "v1alpha1", "ClusterServiceVersion", "csv"}: { 917 Group: "operators.coreos.com", 918 Version: "v1alpha1", 919 Kind: "ClusterServiceVersion", 920 Name: "csv", 921 Status: v1alpha1.RequirementStatusReasonPresent, 922 }, 923 }, 924 expectedError: nil, 925 }, 926 } 927 928 for _, test := range tests { 929 t.Run(test.description, func(t *testing.T) { 930 ctx, cancel := context.WithCancel(context.TODO()) 931 defer cancel() 932 op, err := NewFakeOperator(ctx, withNamespaces(namespace), withOperatorNamespace(namespace), withClientObjs(test.csv), withK8sObjs(test.existingObjs...), withExtObjs(test.existingExtObjs...)) 933 require.NoError(t, err) 934 935 // Get the permission status 936 met, statuses, err := op.requirementAndPermissionStatus(test.csv) 937 938 if test.expectedError != nil { 939 require.Error(t, err) 940 require.EqualError(t, test.expectedError, err.Error()) 941 } 942 assert := assert.New(t) 943 assert.Equal(test.met, met) 944 945 for _, status := range statuses { 946 key := gvkn{ 947 group: status.Group, 948 version: status.Version, 949 kind: status.Kind, 950 name: status.Name, 951 } 952 953 expected, ok := test.expectedRequirementStatuses[key] 954 assert.True(ok, fmt.Sprintf("permission requirement status %+v found but not expected", key)) 955 assert.Equal(expected.Status, status.Status) 956 assert.Len(status.Dependents, len(expected.Dependents), "number of dependents is not what was expected") 957 958 // Delete the requirement status to mark as found 959 delete(test.expectedRequirementStatuses, key) 960 } 961 962 assert.Len(test.expectedRequirementStatuses, 0, "not all expected permission requirement statuses were found") 963 }) 964 } 965 } 966 967 func TestMinKubeVersionStatus(t *testing.T) { 968 namespace := "ns" 969 csv := csv("csv1", 970 namespace, 971 "0.0.0", 972 "", 973 v1alpha1.NamedInstallStrategy{StrategyName: "deployment", StrategySpec: v1alpha1.StrategyDetailsDeployment{}}, 974 nil, 975 nil, 976 v1alpha1.CSVPhasePending, 977 ) 978 979 tests := []struct { 980 description string 981 csvName string 982 minKubeVersion string 983 expectedMet bool 984 expectedRequirementStatuses []v1alpha1.RequirementStatus 985 }{ 986 { 987 description: "minKubeVersion is not specfied", 988 csvName: "test1", 989 minKubeVersion: "", 990 expectedMet: true, 991 expectedRequirementStatuses: []v1alpha1.RequirementStatus{}, 992 }, 993 { 994 description: "minKubeVersion is met", 995 csvName: "test2", 996 minKubeVersion: "0.0.0", 997 expectedMet: true, 998 expectedRequirementStatuses: []v1alpha1.RequirementStatus{ 999 { 1000 Status: v1alpha1.RequirementStatusReasonPresent, 1001 Message: fmt.Sprintf("CSV minKubeVersion (%s) less than server version", "0.0.0"), 1002 Group: "operators.coreos.com", 1003 Version: "v1alpha1", 1004 Kind: "ClusterServiceVersion", 1005 Name: "test2", 1006 }, 1007 }, 1008 }, 1009 { 1010 description: "minKubeVersion is unmet", 1011 csvName: "test3", 1012 minKubeVersion: "999.999.999", 1013 expectedMet: false, 1014 expectedRequirementStatuses: []v1alpha1.RequirementStatus{ 1015 { 1016 Status: v1alpha1.RequirementStatusReasonPresentNotSatisfied, 1017 Message: fmt.Sprintf("CSV version requirement not met: minKubeVersion (%s)", "999.999.999"), 1018 Group: "operators.coreos.com", 1019 Version: "v1alpha1", 1020 Kind: "ClusterServiceVersion", 1021 Name: "test3", 1022 }, 1023 }, 1024 }, 1025 { 1026 description: "minKubeVersion is invalid", 1027 csvName: "test4", 1028 minKubeVersion: "a.b.c", 1029 expectedMet: false, 1030 expectedRequirementStatuses: []v1alpha1.RequirementStatus{ 1031 { 1032 Status: v1alpha1.RequirementStatusReasonPresentNotSatisfied, 1033 Message: "CSV version parsing error", 1034 Group: "operators.coreos.com", 1035 Version: "v1alpha1", 1036 Kind: "ClusterServiceVersion", 1037 Name: "test4", 1038 }, 1039 }, 1040 }, 1041 } 1042 1043 for _, test := range tests { 1044 t.Run(test.description, func(t *testing.T) { 1045 ctx, cancel := context.WithCancel(context.TODO()) 1046 defer cancel() 1047 op, err := NewFakeOperator(ctx, withNamespaces(namespace), withOperatorNamespace(namespace), withClientObjs(csv)) 1048 require.NoError(t, err) 1049 1050 // Get the permission status 1051 met, status := op.minKubeVersionStatus(test.csvName, test.minKubeVersion) 1052 require.Equal(t, test.expectedMet, met) 1053 if len(test.expectedRequirementStatuses) > 0 { 1054 require.Equal(t, status[0].Status, test.expectedRequirementStatuses[0].Status) 1055 require.Equal(t, status[0].Kind, test.expectedRequirementStatuses[0].Kind) 1056 require.Equal(t, status[0].Name, test.expectedRequirementStatuses[0].Name) 1057 require.Contains(t, status[0].Message, test.expectedRequirementStatuses[0].Message) 1058 } else { 1059 require.Equal(t, status, []v1alpha1.RequirementStatus(nil)) 1060 } 1061 }) 1062 } 1063 } 1064 1065 func TestOthersInstalledAlongside(t *testing.T) { 1066 for _, tc := range []struct { 1067 Name string 1068 All []alongside.NamespacedName 1069 Target v1alpha1.ClusterServiceVersion 1070 InNamespace []v1alpha1.ClusterServiceVersion 1071 Expected []string 1072 }{ 1073 { 1074 Name: "csv in different namespace excluded", 1075 All: []alongside.NamespacedName{ 1076 {Namespace: "namespace-2", Name: "a"}, 1077 }, 1078 Target: v1alpha1.ClusterServiceVersion{ 1079 ObjectMeta: metav1.ObjectMeta{ 1080 Name: "b", 1081 Namespace: "namespace-1", 1082 }, 1083 }, 1084 InNamespace: []v1alpha1.ClusterServiceVersion{ 1085 { 1086 ObjectMeta: metav1.ObjectMeta{ 1087 Name: "a", 1088 }, 1089 }, 1090 }, 1091 Expected: nil, 1092 }, 1093 { 1094 Name: "given csv excluded", 1095 All: []alongside.NamespacedName{ 1096 {Namespace: "namespace", Name: "a"}, 1097 }, 1098 Target: v1alpha1.ClusterServiceVersion{ 1099 ObjectMeta: metav1.ObjectMeta{ 1100 Name: "a", 1101 Namespace: "namespace", 1102 }, 1103 }, 1104 Expected: nil, 1105 }, 1106 { 1107 Name: "returns nil if given csv is included", 1108 All: []alongside.NamespacedName{ 1109 {Namespace: "namespace", Name: "a"}, 1110 {Namespace: "namespace", Name: "b"}, 1111 }, 1112 Target: v1alpha1.ClusterServiceVersion{ 1113 ObjectMeta: metav1.ObjectMeta{ 1114 Name: "a", 1115 Namespace: "namespace", 1116 }, 1117 Spec: v1alpha1.ClusterServiceVersionSpec{ 1118 Replaces: "b", 1119 }, 1120 }, 1121 InNamespace: []v1alpha1.ClusterServiceVersion{ 1122 { 1123 ObjectMeta: metav1.ObjectMeta{ 1124 Name: "b", 1125 }, 1126 }, 1127 }, 1128 Expected: nil, 1129 }, 1130 { 1131 Name: "copied csv excluded", 1132 All: []alongside.NamespacedName{ 1133 {Namespace: "namespace", Name: "b"}, 1134 }, 1135 Target: v1alpha1.ClusterServiceVersion{ 1136 ObjectMeta: metav1.ObjectMeta{ 1137 Name: "a", 1138 Namespace: "namespace", 1139 }, 1140 }, 1141 InNamespace: []v1alpha1.ClusterServiceVersion{ 1142 { 1143 ObjectMeta: metav1.ObjectMeta{ 1144 Name: "b", 1145 }, 1146 Status: v1alpha1.ClusterServiceVersionStatus{ 1147 Reason: v1alpha1.CSVReasonCopied, 1148 }, 1149 }, 1150 }, 1151 Expected: nil, 1152 }, 1153 { 1154 Name: "non-ancestor csv excluded", 1155 All: []alongside.NamespacedName{ 1156 {Namespace: "namespace", Name: "b"}, 1157 }, 1158 Target: v1alpha1.ClusterServiceVersion{ 1159 ObjectMeta: metav1.ObjectMeta{ 1160 Name: "a", 1161 Namespace: "namespace", 1162 }, 1163 }, 1164 InNamespace: []v1alpha1.ClusterServiceVersion{ 1165 { 1166 ObjectMeta: metav1.ObjectMeta{ 1167 Name: "b", 1168 }, 1169 }, 1170 }, 1171 Expected: nil, 1172 }, 1173 { 1174 Name: "ancestor csvs included", 1175 All: []alongside.NamespacedName{ 1176 {Namespace: "namespace", Name: "b"}, 1177 {Namespace: "namespace", Name: "c"}, 1178 }, 1179 Target: v1alpha1.ClusterServiceVersion{ 1180 ObjectMeta: metav1.ObjectMeta{ 1181 Name: "a", 1182 Namespace: "namespace", 1183 }, 1184 Spec: v1alpha1.ClusterServiceVersionSpec{ 1185 Replaces: "b", 1186 }, 1187 }, 1188 InNamespace: []v1alpha1.ClusterServiceVersion{ 1189 { 1190 ObjectMeta: metav1.ObjectMeta{ 1191 Name: "b", 1192 }, 1193 Spec: v1alpha1.ClusterServiceVersionSpec{ 1194 Replaces: "c", 1195 }, 1196 }, 1197 { 1198 ObjectMeta: metav1.ObjectMeta{ 1199 Name: "c", 1200 }, 1201 }, 1202 }, 1203 Expected: []string{"b", "c"}, 1204 }, 1205 { 1206 Name: "descendant csvs excluded", 1207 All: []alongside.NamespacedName{ 1208 {Namespace: "namespace", Name: "b"}, 1209 {Namespace: "namespace", Name: "c"}, 1210 }, 1211 Target: v1alpha1.ClusterServiceVersion{ 1212 ObjectMeta: metav1.ObjectMeta{ 1213 Name: "a", 1214 Namespace: "namespace", 1215 }, 1216 }, 1217 InNamespace: []v1alpha1.ClusterServiceVersion{ 1218 { 1219 ObjectMeta: metav1.ObjectMeta{ 1220 Name: "c", 1221 }, 1222 Spec: v1alpha1.ClusterServiceVersionSpec{ 1223 Replaces: "b", 1224 }, 1225 }, 1226 { 1227 ObjectMeta: metav1.ObjectMeta{ 1228 Name: "b", 1229 }, 1230 Spec: v1alpha1.ClusterServiceVersionSpec{ 1231 Replaces: "a", 1232 }, 1233 }, 1234 }, 1235 Expected: nil, 1236 }, 1237 { 1238 Name: "ancestor csvs included with cycle", 1239 All: []alongside.NamespacedName{ 1240 {Namespace: "namespace", Name: "b"}, 1241 {Namespace: "namespace", Name: "c"}, 1242 }, 1243 Target: v1alpha1.ClusterServiceVersion{ 1244 ObjectMeta: metav1.ObjectMeta{ 1245 Name: "a", 1246 Namespace: "namespace", 1247 }, 1248 Spec: v1alpha1.ClusterServiceVersionSpec{ 1249 Replaces: "b", 1250 }, 1251 }, 1252 InNamespace: []v1alpha1.ClusterServiceVersion{ 1253 { 1254 ObjectMeta: metav1.ObjectMeta{ 1255 Name: "b", 1256 }, 1257 Spec: v1alpha1.ClusterServiceVersionSpec{ 1258 Replaces: "c", 1259 }, 1260 }, 1261 { 1262 ObjectMeta: metav1.ObjectMeta{ 1263 Name: "c", 1264 }, 1265 Spec: v1alpha1.ClusterServiceVersionSpec{ 1266 Replaces: "a", 1267 }, 1268 }, 1269 }, 1270 Expected: []string{"b", "c"}, 1271 }, 1272 } { 1273 t.Run(tc.Name, func(t *testing.T) { 1274 var ( 1275 o metav1.ObjectMeta 1276 a alongside.Annotator 1277 nslister operatorlisterfakes.FakeClusterServiceVersionNamespaceLister 1278 ) 1279 1280 nslister.GetCalls(func(name string) (*v1alpha1.ClusterServiceVersion, error) { 1281 if name == tc.Target.GetName() { 1282 return tc.Target.DeepCopy(), nil 1283 } 1284 1285 for _, csv := range tc.InNamespace { 1286 if csv.GetName() == name { 1287 return csv.DeepCopy(), nil 1288 } 1289 } 1290 return nil, errors.NewNotFound(schema.GroupResource{}, name) 1291 }) 1292 1293 a.ToObject(&o, tc.All) 1294 actual := othersInstalledAlongside(&o, tc.Target.DeepCopy(), &nslister) 1295 assert.ElementsMatch(t, actual, tc.Expected) 1296 }) 1297 } 1298 }