@@ -34,6 +34,14 @@ func NewOperatorUninstall(cfg *Configuration) *OperatorUninstall {
3434 }
3535}
3636
37+ type ErrPackageNotFound struct {
38+ PackageName string
39+ }
40+
41+ func (e ErrPackageNotFound ) Error () string {
42+ return fmt .Sprintf ("package %q not found" , e .PackageName )
43+ }
44+
3745func (u * OperatorUninstall ) Run (ctx context.Context ) error {
3846 if u .DeleteAll {
3947 u .DeleteCRDs = true
@@ -57,16 +65,41 @@ func (u *OperatorUninstall) Run(ctx context.Context) error {
5765 return fmt .Errorf ("operator package %q not found" , u .Package )
5866 }
5967
60- csv , err := u .getInstalledCSV (ctx , sub )
61- if err != nil {
62- return fmt .Errorf ("get installed CSV %q: %v" , sub .Status .InstalledCSV , err )
68+ var subObj , csvObj controllerutil.Object
69+ var crds []controllerutil.Object
70+ if sub != nil {
71+ subObj = sub
72+ // CSV name may either be the installed or current name in a subscription's status,
73+ // depending on installation state.
74+ csvKey := types.NamespacedName {
75+ Name : sub .Status .InstalledCSV ,
76+ Namespace : u .config .Namespace ,
77+ }
78+ if csvKey .Name == "" {
79+ csvKey .Name = sub .Status .CurrentCSV
80+ }
81+
82+ // This value can be empty which will cause errors.
83+ if csvKey .Name != "" {
84+ csv := & v1alpha1.ClusterServiceVersion {}
85+ if err := u .config .Client .Get (ctx , csvKey , csv ); err != nil && ! apierrors .IsNotFound (err ) {
86+ return fmt .Errorf ("error getting installed CSV %q: %v" , csvKey .Name , err )
87+ } else if err == nil {
88+ crds = getCRDs (csv )
89+ }
90+ csvObj = csv
91+ }
6392 }
6493
65- crds := getCRDs (csv )
94+ // Deletion order:
95+ //
96+ // 1. Subscription to prevent further installs or upgrades of the operator while cleaning up.
97+ // 2. CustomResourceDefinitions so the operator has a chance to handle CRs that have finalizers.
98+ // 3. ClusterServiceVersion. OLM puts an ownerref on every namespaced resource to the CSV,
99+ // and an owner label on every cluster scoped resource so they get gc'd on deletion.
66100
67- // Delete the subscription first, so that no further installs or upgrades
68- // of the operator occur while we're cleaning up.
69- if err := u .deleteObjects (ctx , sub ); err != nil {
101+ // Subscriptions can be deleted asynchronously.
102+ if err := u .deleteObjects (ctx , subObj ); err != nil {
70103 return err
71104 }
72105
@@ -81,7 +114,7 @@ func (u *OperatorUninstall) Run(ctx context.Context) error {
81114 // OLM puts an ownerref on every namespaced resource to the CSV,
82115 // and an owner label on every cluster scoped resource. When CSV is deleted
83116 // kube and olm gc will remove all the referenced resources.
84- if err := u .deleteObjects (ctx , csv ); err != nil {
117+ if err := u .deleteObjects (ctx , csvObj ); err != nil {
85118 return err
86119 }
87120
@@ -107,6 +140,11 @@ func (u *OperatorUninstall) Run(ctx context.Context) error {
107140 }
108141 }
109142
143+ // If no objects were cleaned up, it means the package was not found
144+ if subObj == nil && csvObj == nil && len (crds ) == 0 {
145+ return & ErrPackageNotFound {u .Package }
146+ }
147+
110148 return nil
111149}
112150
@@ -123,22 +161,6 @@ func (u *OperatorUninstall) deleteObjects(ctx context.Context, objs ...controlle
123161 return waitForDeletion (ctx , u .config .Client , objs ... )
124162}
125163
126- // getInstalledCSV looks up the installed CSV name from the provided subscription and fetches it.
127- func (u * OperatorUninstall ) getInstalledCSV (ctx context.Context , subscription * v1alpha1.Subscription ) (* v1alpha1.ClusterServiceVersion , error ) {
128- key := types.NamespacedName {
129- Name : subscription .Status .InstalledCSV ,
130- Namespace : subscription .GetNamespace (),
131- }
132-
133- installedCSV := & v1alpha1.ClusterServiceVersion {}
134- if err := u .config .Client .Get (ctx , key , installedCSV ); err != nil {
135- return nil , err
136- }
137-
138- installedCSV .SetGroupVersionKind (v1alpha1 .SchemeGroupVersion .WithKind (csvKind ))
139- return installedCSV , nil
140- }
141-
142164// getCRDs returns the list of CRDs required by a CSV.
143165func getCRDs (csv * v1alpha1.ClusterServiceVersion ) (crds []controllerutil.Object ) {
144166 for _ , resource := range csv .Status .RequirementStatus {
0 commit comments