44 "context"
55 "fmt"
66 "strings"
7- "time"
87
98 v1 "github.com/operator-framework/api/pkg/operators/v1"
109 "github.com/operator-framework/api/pkg/operators/v1alpha1"
@@ -13,7 +12,6 @@ import (
1312 "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
1413 "k8s.io/apimachinery/pkg/runtime/schema"
1514 "k8s.io/apimachinery/pkg/types"
16- "k8s.io/apimachinery/pkg/util/wait"
1715 "sigs.k8s.io/controller-runtime/pkg/client"
1816 "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
1917 "sigs.k8s.io/yaml"
@@ -22,10 +20,11 @@ import (
2220type OperatorUninstall struct {
2321 config * Configuration
2422
25- Package string
26- DeleteOperatorGroup bool
27- DeleteCRDs bool
28- DeleteAll bool
23+ Package string
24+ DeleteAll bool
25+ DeleteCRDs bool
26+ DeleteOperatorGroups bool
27+ DeleteOperatorGroupNames []string
2928
3029 Logf func (string , ... interface {})
3130}
@@ -38,15 +37,16 @@ func NewOperatorUninstall(cfg *Configuration) *OperatorUninstall {
3837}
3938
4039func (u * OperatorUninstall ) BindFlags (fs * pflag.FlagSet ) {
41- fs .BoolVar (& u .DeleteOperatorGroup , "delete-operator-group " , false , "delete operator group if no other operators remain " )
40+ fs .BoolVarP (& u .DeleteAll , "delete-all " , "X" , false , "enable all delete flags " )
4241 fs .BoolVar (& u .DeleteCRDs , "delete-crds" , false , "delete all owned CRDs and all CRs" )
43- fs .BoolVarP (& u .DeleteAll , "delete-add" , "X" , false , "enable all delete flags" )
42+ fs .BoolVar (& u .DeleteOperatorGroups , "delete-operator-groups" , false , "delete operator group if no other operators remain" )
43+ fs .StringSliceVar (& u .DeleteOperatorGroupNames , "delete-operator-group-names" , nil , "delete operator group if no other operators remain" )
4444}
4545
4646func (u * OperatorUninstall ) Run (ctx context.Context ) error {
4747 if u .DeleteAll {
4848 u .DeleteCRDs = true
49- u .DeleteOperatorGroup = true
49+ u .DeleteOperatorGroups = true
5050 }
5151
5252 subs := v1alpha1.SubscriptionList {}
@@ -66,6 +66,9 @@ func (u *OperatorUninstall) Run(ctx context.Context) error {
6666 return fmt .Errorf ("operator package %q not found" , u .Package )
6767 }
6868
69+ // Since the install plan is owned by the subscription, we need to
70+ // read all of the resource references from the install plan before
71+ // deleting the subscription.
6972 var crds , csvs , others []controllerutil.Object
7073 if sub .Status .InstallPlanRef != nil {
7174 ipKey := types.NamespacedName {
@@ -79,56 +82,52 @@ func (u *OperatorUninstall) Run(ctx context.Context) error {
7982 }
8083 }
8184
82- if err := u .config .Client .Delete (ctx , sub ); err != nil {
83- return fmt .Errorf ("delete subscription %q: %v" , sub .Name , err )
85+ // Delete the subscription first, so that no further installs or upgrades
86+ // of the operator occur while we're cleaning up.
87+ if err := u .deleteObjects (ctx , sub ); err != nil {
88+ return err
8489 }
85- u .Logf ("subscription %q deleted" , sub .Name )
8690
8791 if u .DeleteCRDs {
88- if err := u .deleteCRDs (ctx , crds ); err != nil {
92+ // Ensure CustomResourceDefinitions are deleted next, so that the operator
93+ // has a chance to handle CRs that have finalizers.
94+ if err := u .deleteObjects (ctx , crds ... ); err != nil {
8995 return err
9096 }
9197 }
9298
93- if err := u .deleteObjects (ctx , false , csvs ); err != nil {
94- return err
95- }
96-
97- if err := u .deleteObjects (ctx , false , others ); err != nil {
99+ // Delete CSVs and all other objects created by the install plan.
100+ objects := append (csvs , others ... )
101+ if err := u .deleteObjects (ctx , objects ... ); err != nil {
98102 return err
99103 }
100104
101- if u .DeleteOperatorGroup {
105+ if u .DeleteOperatorGroups {
102106 subs := v1alpha1.SubscriptionList {}
103107 if err := u .config .Client .List (ctx , & subs , client .InNamespace (u .config .Namespace )); err != nil {
104- return fmt .Errorf ("list clusterserviceversions : %v" , err )
108+ return fmt .Errorf ("list subscriptions : %v" , err )
105109 }
110+ // If there are no subscriptions left, delete the operator group(s).
106111 if len (subs .Items ) == 0 {
107112 ogs := v1.OperatorGroupList {}
108113 if err := u .config .Client .List (ctx , & ogs , client .InNamespace (u .config .Namespace )); err != nil {
109114 return fmt .Errorf ("list operatorgroups: %v" , err )
110115 }
111116 for _ , og := range ogs .Items {
112117 og := og
113- if err := u .config .Client .Delete (ctx , & og ); err != nil {
114- return fmt .Errorf ("delete operatorgroup %q: %v" , og .Name , err )
118+ if len (u .DeleteOperatorGroupNames ) == 0 || contains (u .DeleteOperatorGroupNames , og .GetName ()) {
119+ if err := u .deleteObjects (ctx , & og ); err != nil {
120+ return err
121+ }
115122 }
116- u .Logf ("operatorgroup %q deleted" , og .Name )
117123 }
118124 }
119125 }
120126
121127 return nil
122128}
123129
124- func (u * OperatorUninstall ) deleteCRDs (ctx context.Context , crds []controllerutil.Object ) error {
125- if err := u .deleteObjects (ctx , true , crds ); err != nil {
126- return err
127- }
128- return nil
129- }
130-
131- func (u * OperatorUninstall ) deleteObjects (ctx context.Context , waitForDelete bool , objs []controllerutil.Object ) error {
130+ func (u * OperatorUninstall ) deleteObjects (ctx context.Context , objs ... controllerutil.Object ) error {
132131 for _ , obj := range objs {
133132 obj := obj
134133 lowerKind := strings .ToLower (obj .GetObjectKind ().GroupVersionKind ().Kind )
@@ -137,24 +136,8 @@ func (u *OperatorUninstall) deleteObjects(ctx context.Context, waitForDelete boo
137136 } else if err == nil {
138137 u .Logf ("%s %q deleted" , lowerKind , obj .GetName ())
139138 }
140- if waitForDelete {
141- key , err := client .ObjectKeyFromObject (obj )
142- if err != nil {
143- return fmt .Errorf ("get %s key: %v" , lowerKind , err )
144- }
145- if err := wait .PollImmediateUntil (250 * time .Millisecond , func () (bool , error ) {
146- if err := u .config .Client .Get (ctx , key , obj ); apierrors .IsNotFound (err ) {
147- return true , nil
148- } else if err != nil {
149- return false , err
150- }
151- return false , nil
152- }, ctx .Done ()); err != nil {
153- return fmt .Errorf ("wait for %s deleted: %v" , lowerKind , err )
154- }
155- }
156139 }
157- return nil
140+ return waitForDeletion ( ctx , u . config . Client , objs ... )
158141}
159142
160143func (u * OperatorUninstall ) getInstallPlanResources (ctx context.Context , installPlanKey types.NamespacedName ) (crds , csvs , others []controllerutil.Object , err error ) {
@@ -164,11 +147,8 @@ func (u *OperatorUninstall) getInstallPlanResources(ctx context.Context, install
164147 }
165148
166149 for _ , step := range installPlan .Status .Plan {
167- if step .Status != v1alpha1 .StepStatusCreated {
168- continue
169- }
170- obj := & unstructured.Unstructured {Object : map [string ]interface {}{}}
171150 lowerKind := strings .ToLower (step .Resource .Kind )
151+ obj := & unstructured.Unstructured {Object : map [string ]interface {}{}}
172152 if err := yaml .Unmarshal ([]byte (step .Resource .Manifest ), & obj .Object ); err != nil {
173153 return nil , nil , nil , fmt .Errorf ("parse %s manifest %q: %v" , lowerKind , step .Resource .Name , err )
174154 }
@@ -191,8 +171,22 @@ func (u *OperatorUninstall) getInstallPlanResources(ctx context.Context, install
191171 case csvKind :
192172 csvs = append (csvs , obj )
193173 default :
174+ // Skip non-CRD/non-CSV resources in the install plan that were not created by the install plan.
175+ // This means we avoid deleting things like the default service account.
176+ if step .Status != v1alpha1 .StepStatusCreated {
177+ continue
178+ }
194179 others = append (others , obj )
195180 }
196181 }
197182 return crds , csvs , others , nil
198183}
184+
185+ func contains (haystack []string , needle string ) bool {
186+ for _ , n := range haystack {
187+ if n == needle {
188+ return true
189+ }
190+ }
191+ return false
192+ }
0 commit comments