Skip to content

Commit 8af1d91

Browse files
committed
test(sub): add subscription installplan condition e2e test
1 parent b936e0b commit 8af1d91

File tree

2 files changed

+204
-4
lines changed

2 files changed

+204
-4
lines changed

test/e2e/operator_groups_e2e_test.go

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,13 +20,12 @@ import (
2020
"k8s.io/client-go/informers"
2121
"k8s.io/client-go/tools/cache"
2222

23-
"github.com/operator-framework/operator-lifecycle-manager/pkg/lib/ownerutil"
24-
2523
v1 "github.com/operator-framework/operator-lifecycle-manager/pkg/api/apis/operators/v1"
2624
"github.com/operator-framework/operator-lifecycle-manager/pkg/api/apis/operators/v1alpha1"
2725
"github.com/operator-framework/operator-lifecycle-manager/pkg/controller/install"
2826
"github.com/operator-framework/operator-lifecycle-manager/pkg/controller/registry"
2927
"github.com/operator-framework/operator-lifecycle-manager/pkg/lib/operatorclient"
28+
"github.com/operator-framework/operator-lifecycle-manager/pkg/lib/ownerutil"
3029
)
3130

3231
func checkOperatorGroupAnnotations(obj metav1.Object, op *v1.OperatorGroup, checkTargetNamespaces bool, targetNamespaces string) error {
@@ -1246,8 +1245,7 @@ func TestStaticProviderOperatorGroup(t *testing.T) {
12461245
}
12471246

12481247
// Create subscription for csvA in namespaceB
1249-
subAName := genName("a-" +
1250-
"")
1248+
subAName := genName("a-")
12511249
cleanupSubA := createSubscriptionForCatalog(t, crc, nsB, subAName, catalog, pkgA, stableChannel, pkgAStable, v1alpha1.ApprovalAutomatic)
12521250
defer cleanupSubA()
12531251
subA, err := fetchSubscription(t, crc, nsB, subAName, subscriptionHasInstallPlanChecker)

test/e2e/subscription_e2e_test.go

Lines changed: 202 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import (
2323
"github.com/operator-framework/operator-lifecycle-manager/pkg/controller/install"
2424
"github.com/operator-framework/operator-lifecycle-manager/pkg/controller/registry"
2525
"github.com/operator-framework/operator-lifecycle-manager/pkg/controller/registry/resolver"
26+
"github.com/operator-framework/operator-lifecycle-manager/pkg/lib/comparison"
2627
"github.com/operator-framework/operator-lifecycle-manager/pkg/lib/operatorclient"
2728
"github.com/operator-framework/operator-lifecycle-manager/pkg/lib/version"
2829
)
@@ -897,6 +898,207 @@ func TestSubscriptionStatusMissingTargetCatalogSource(t *testing.T) {
897898
require.NoError(t, err)
898899
}
899900

901+
// TestSubscriptionInstallPlanStatus ensures that a Subscription has the appropriate status conditions for possible referenced
902+
// InstallPlan states.
903+
//
904+
// Steps:
905+
// 1. Create namespace, ns
906+
// 2. Create CatalogSource, cs, in ns
907+
// 3. Create OperatorGroup, og, in ns selecting its own namespace
908+
// 4. Create Subscription to a package of cs in ns, sub
909+
// 5. Wait for the package from sub to install successfully with no remaining InstallPlan status conditions
910+
// 6. Store conditions for later comparision
911+
// 7. Get the InstallPlan
912+
// 8. Set the InstallPlan's approval mode to Manual
913+
// 9. Set the InstallPlan's phase to None
914+
// 10. Wait for sub to have status condition SubscriptionInstallPlanPending true and reason InstallPlanNotYetReconciled
915+
// 11. Get the latest IntallPlan and set the phase to InstallPlanPhaseRequiresApproval
916+
// 12. Wait for sub to have status condition SubscriptionInstallPlanPending true and reason RequiresApproval
917+
// 13. Get the latest InstallPlan and set the phase to InstallPlanPhaseInstalling
918+
// 14. Wait for sub to have status condition SubscriptionInstallPlanPending true and reason Installing
919+
// 15. Get the latest InstallPlan and set the phase to InstallPlanPhaseFailed and remove all status conditions
920+
// 16. Wait for sub to have status condition SubscriptionInstallPlanFailed true and reason InstallPlanFailed
921+
// 17. Get the latest InstallPlan and set status condition of type Installed to false with reason InstallComponentFailed
922+
// 18. Wait for sub to have status condition SubscriptionInstallPlanFailed true and reason InstallComponentFailed
923+
// 19. Delete the referenced InstallPlan
924+
// 20. Wait for sub to have status condition SubscriptionInstallPlanMissing true
925+
// 21. Ensure original non-InstallPlan status conditions remain after InstallPlan transitions
926+
func TestSubscriptionInstallPlanStatus(t *testing.T) {
927+
defer cleaner.NotifyTestComplete(t, true)
928+
929+
c := newKubeClient(t)
930+
crc := newCRClient(t)
931+
932+
// Create namespace ns
933+
ns := &corev1.Namespace{
934+
ObjectMeta: metav1.ObjectMeta{
935+
Name: genName("ns-"),
936+
},
937+
}
938+
_, err := c.KubernetesInterface().CoreV1().Namespaces().Create(ns)
939+
require.NoError(t, err)
940+
defer func() {
941+
require.NoError(t, c.KubernetesInterface().CoreV1().Namespaces().Delete(ns.GetName(), &metav1.DeleteOptions{}))
942+
}()
943+
944+
// Create CatalogSource, cs, in ns
945+
pkgName := genName("pkg-")
946+
channelName := genName("channel-")
947+
strategy := newNginxInstallStrategy(pkgName, nil, nil)
948+
crd := newCRD(pkgName)
949+
csv := newCSV(pkgName, ns.GetName(), "", semver.MustParse("0.1.0"), []apiextensions.CustomResourceDefinition{crd}, nil, strategy)
950+
manifests := []registry.PackageManifest{
951+
{
952+
PackageName: pkgName,
953+
Channels: []registry.PackageChannel{
954+
{Name: channelName, CurrentCSVName: csv.GetName()},
955+
},
956+
DefaultChannelName: channelName,
957+
},
958+
}
959+
catalogName := genName("catalog-")
960+
_, cleanupCatalogSource := createInternalCatalogSource(t, c, crc, catalogName, ns.GetName(), manifests, []apiextensions.CustomResourceDefinition{crd}, []v1alpha1.ClusterServiceVersion{csv})
961+
defer cleanupCatalogSource()
962+
_, err = fetchCatalogSource(t, crc, catalogName, ns.GetName(), catalogSourceRegistryPodSynced)
963+
require.NoError(t, err)
964+
965+
// Create OperatorGroup, og, in ns selecting its own namespace
966+
og := newOperatorGroup(ns.GetName(), genName("og-"), nil, nil, []string{ns.GetName()}, false)
967+
_, err = crc.OperatorsV1().OperatorGroups(og.GetNamespace()).Create(og)
968+
require.NoError(t, err)
969+
defer func() {
970+
require.NoError(t, crc.OperatorsV1().OperatorGroups(og.GetNamespace()).Delete(og.GetName(), &metav1.DeleteOptions{}))
971+
}()
972+
973+
// Create Subscription to a package of cs in ns, sub
974+
subName := genName("sub-")
975+
defer createSubscriptionForCatalog(t, crc, ns.GetName(), subName, catalogName, pkgName, channelName, pkgName, v1alpha1.ApprovalAutomatic)()
976+
977+
// Wait for the package from sub to install successfully with no remaining InstallPlan status conditions
978+
sub, err := fetchSubscription(t, crc, ns.GetName(), subName, func(s *v1alpha1.Subscription) bool {
979+
for _, cond := range s.Status.Conditions {
980+
switch cond.Type {
981+
case v1alpha1.SubscriptionInstallPlanMissing, v1alpha1.SubscriptionInstallPlanPending, v1alpha1.SubscriptionInstallPlanFailed:
982+
return false
983+
}
984+
}
985+
return subscriptionStateAtLatestChecker(s)
986+
})
987+
require.NoError(t, err)
988+
require.NotNil(t, sub)
989+
990+
// Store conditions for later comparision
991+
conds := sub.Status.Conditions
992+
993+
// Get the InstallPlan
994+
ref := sub.Status.InstallPlanRef
995+
require.NotNil(t, ref)
996+
plan, err := crc.OperatorsV1alpha1().InstallPlans(ref.Namespace).Get(ref.Name, metav1.GetOptions{})
997+
require.NoError(t, err)
998+
999+
// Set the InstallPlan's approval mode to Manual
1000+
plan.Spec.Approval = v1alpha1.ApprovalManual
1001+
plan.Spec.Approved = false
1002+
plan, err = crc.OperatorsV1alpha1().InstallPlans(ref.Namespace).Update(plan)
1003+
require.NoError(t, err)
1004+
1005+
// Set the InstallPlan's phase to None
1006+
plan.Status.Phase = v1alpha1.InstallPlanPhaseNone
1007+
plan, err = crc.OperatorsV1alpha1().InstallPlans(ref.Namespace).UpdateStatus(plan)
1008+
require.NoError(t, err)
1009+
1010+
// Wait for sub to have status condition SubscriptionInstallPlanPending true and reason InstallPlanNotYetReconciled
1011+
sub, err = fetchSubscription(t, crc, ns.GetName(), subName, func(s *v1alpha1.Subscription) bool {
1012+
cond := s.Status.GetCondition(v1alpha1.SubscriptionInstallPlanPending)
1013+
return cond.Status == corev1.ConditionTrue && cond.Reason == v1alpha1.InstallPlanNotYetReconciled
1014+
})
1015+
require.NoError(t, err)
1016+
1017+
// Get the latest InstallPlan and set the phase to InstallPlanPhaseRequiresApproval
1018+
plan, err = crc.OperatorsV1alpha1().InstallPlans(ref.Namespace).Get(ref.Name, metav1.GetOptions{})
1019+
require.NoError(t, err)
1020+
plan.Status.Phase = v1alpha1.InstallPlanPhaseRequiresApproval
1021+
plan, err = crc.OperatorsV1alpha1().InstallPlans(ref.Namespace).UpdateStatus(plan)
1022+
require.NoError(t, err)
1023+
1024+
// Wait for sub to have status condition SubscriptionInstallPlanPending true and reason RequiresApproval
1025+
sub, err = fetchSubscription(t, crc, ns.GetName(), subName, func(s *v1alpha1.Subscription) bool {
1026+
cond := s.Status.GetCondition(v1alpha1.SubscriptionInstallPlanPending)
1027+
return cond.Status == corev1.ConditionTrue && cond.Reason == string(v1alpha1.InstallPlanPhaseRequiresApproval)
1028+
})
1029+
require.NoError(t, err)
1030+
1031+
// Get the latest InstallPlan and set the phase to InstallPlanPhaseInstalling
1032+
plan, err = crc.OperatorsV1alpha1().InstallPlans(ref.Namespace).Get(ref.Name, metav1.GetOptions{})
1033+
require.NoError(t, err)
1034+
plan.Status.Phase = v1alpha1.InstallPlanPhaseInstalling
1035+
plan, err = crc.OperatorsV1alpha1().InstallPlans(ref.Namespace).UpdateStatus(plan)
1036+
require.NoError(t, err)
1037+
1038+
// Wait for sub to have status condition SubscriptionInstallPlanPending true and reason Installing
1039+
sub, err = fetchSubscription(t, crc, ns.GetName(), subName, func(s *v1alpha1.Subscription) bool {
1040+
cond := s.Status.GetCondition(v1alpha1.SubscriptionInstallPlanPending)
1041+
return cond.Status == corev1.ConditionTrue && cond.Reason == string(v1alpha1.InstallPlanPhaseInstalling)
1042+
})
1043+
require.NoError(t, err)
1044+
1045+
// Get the latest InstallPlan and set the phase to InstallPlanPhaseFailed and remove all status conditions
1046+
plan, err = crc.OperatorsV1alpha1().InstallPlans(ref.Namespace).Get(ref.Name, metav1.GetOptions{})
1047+
require.NoError(t, err)
1048+
plan.Status.Phase = v1alpha1.InstallPlanPhaseFailed
1049+
plan.Status.Conditions = nil
1050+
plan, err = crc.OperatorsV1alpha1().InstallPlans(ref.Namespace).UpdateStatus(plan)
1051+
require.NoError(t, err)
1052+
1053+
// Wait for sub to have status condition SubscriptionInstallPlanFailed true and reason InstallPlanFailed
1054+
sub, err = fetchSubscription(t, crc, ns.GetName(), subName, func(s *v1alpha1.Subscription) bool {
1055+
cond := s.Status.GetCondition(v1alpha1.SubscriptionInstallPlanFailed)
1056+
return cond.Status == corev1.ConditionTrue && cond.Reason == v1alpha1.InstallPlanFailed
1057+
})
1058+
require.NoError(t, err)
1059+
1060+
// Get the latest InstallPlan and set status condition of type Installed to false with reason InstallComponentFailed
1061+
plan, err = crc.OperatorsV1alpha1().InstallPlans(ref.Namespace).Get(ref.Name, metav1.GetOptions{})
1062+
require.NoError(t, err)
1063+
plan.Status.Phase = v1alpha1.InstallPlanPhaseFailed
1064+
failedCond := plan.Status.GetCondition(v1alpha1.InstallPlanInstalled)
1065+
failedCond.Status = corev1.ConditionFalse
1066+
failedCond.Reason = v1alpha1.InstallPlanReasonComponentFailed
1067+
plan.Status.SetCondition(failedCond)
1068+
plan, err = crc.OperatorsV1alpha1().InstallPlans(ref.Namespace).UpdateStatus(plan)
1069+
require.NoError(t, err)
1070+
1071+
// Wait for sub to have status condition SubscriptionInstallPlanFailed true and reason InstallComponentFailed
1072+
sub, err = fetchSubscription(t, crc, ns.GetName(), subName, func(s *v1alpha1.Subscription) bool {
1073+
cond := s.Status.GetCondition(v1alpha1.SubscriptionInstallPlanFailed)
1074+
return cond.Status == corev1.ConditionTrue && cond.Reason == string(v1alpha1.InstallPlanReasonComponentFailed)
1075+
})
1076+
require.NoError(t, err)
1077+
1078+
// Delete the referenced InstallPlan
1079+
require.NoError(t, crc.OperatorsV1alpha1().InstallPlans(ref.Namespace).Delete(ref.Name, &metav1.DeleteOptions{}))
1080+
1081+
// Wait for sub to have status condition SubscriptionInstallPlanMissing true
1082+
sub, err = fetchSubscription(t, crc, ns.GetName(), subName, func(s *v1alpha1.Subscription) bool {
1083+
return s.Status.GetCondition(v1alpha1.SubscriptionInstallPlanMissing).Status == corev1.ConditionTrue
1084+
})
1085+
require.NoError(t, err)
1086+
require.NotNil(t, sub)
1087+
1088+
// Ensure original non-InstallPlan status conditions remain after InstallPlan transitions
1089+
hashEqual := comparison.NewHashEqualitor()
1090+
for _, cond := range conds {
1091+
switch condType := cond.Type; condType {
1092+
case v1alpha1.SubscriptionInstallPlanPending, v1alpha1.SubscriptionInstallPlanFailed:
1093+
require.FailNowf(t, "failed", "subscription contains unexpected installplan condition: %v", cond)
1094+
case v1alpha1.SubscriptionInstallPlanMissing:
1095+
require.Equal(t, v1alpha1.ReferencedInstallPlanNotFound, cond.Reason)
1096+
default:
1097+
require.True(t, hashEqual(cond, sub.Status.GetCondition(condType)), "non-installplan status condition changed")
1098+
}
1099+
}
1100+
}
1101+
9001102
func updateInternalCatalog(t *testing.T, c operatorclient.ClientInterface, crc versioned.Interface, catalogSourceName, namespace string, crds []apiextensions.CustomResourceDefinition, csvs []v1alpha1.ClusterServiceVersion, packages []registry.PackageManifest) {
9011103
fetchedInitialCatalog, err := fetchCatalogSource(t, crc, catalogSourceName, namespace, catalogSourceRegistryPodSynced)
9021104
require.NoError(t, err)

0 commit comments

Comments
 (0)