@@ -23,6 +23,7 @@ import (
23
23
"github.com/operator-framework/operator-lifecycle-manager/pkg/controller/install"
24
24
"github.com/operator-framework/operator-lifecycle-manager/pkg/controller/registry"
25
25
"github.com/operator-framework/operator-lifecycle-manager/pkg/controller/registry/resolver"
26
+ "github.com/operator-framework/operator-lifecycle-manager/pkg/lib/comparison"
26
27
"github.com/operator-framework/operator-lifecycle-manager/pkg/lib/operatorclient"
27
28
"github.com/operator-framework/operator-lifecycle-manager/pkg/lib/version"
28
29
)
@@ -897,6 +898,207 @@ func TestSubscriptionStatusMissingTargetCatalogSource(t *testing.T) {
897
898
require .NoError (t , err )
898
899
}
899
900
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
+
900
1102
func updateInternalCatalog (t * testing.T , c operatorclient.ClientInterface , crc versioned.Interface , catalogSourceName , namespace string , crds []apiextensions.CustomResourceDefinition , csvs []v1alpha1.ClusterServiceVersion , packages []registry.PackageManifest ) {
901
1103
fetchedInitialCatalog , err := fetchCatalogSource (t , crc , catalogSourceName , namespace , catalogSourceRegistryPodSynced )
902
1104
require .NoError (t , err )
0 commit comments