Skip to content

Commit fa4a6f1

Browse files
authored
delete everything from install plan, wait for CRD deletion (#9)
1 parent 039c26c commit fa4a6f1

File tree

6 files changed

+166
-39
lines changed

6 files changed

+166
-39
lines changed

go.mod

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,10 @@ require (
99
github.com/sirupsen/logrus v1.4.2
1010
github.com/spf13/cobra v1.0.0
1111
github.com/spf13/pflag v1.0.5
12-
k8s.io/api v0.18.4
13-
k8s.io/apiextensions-apiserver v0.18.2
14-
k8s.io/apimachinery v0.18.4
15-
k8s.io/client-go v0.18.4
16-
sigs.k8s.io/controller-runtime v0.6.0
12+
k8s.io/api v0.18.6
13+
k8s.io/apiextensions-apiserver v0.18.6
14+
k8s.io/apimachinery v0.18.6
15+
k8s.io/client-go v0.18.6
16+
sigs.k8s.io/controller-runtime v0.6.2
17+
sigs.k8s.io/yaml v1.2.0
1718
)

go.sum

Lines changed: 54 additions & 0 deletions
Large diffs are not rendered by default.

internal/pkg/action/constants.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
package action
2+
3+
const (
4+
csvKind = "ClusterServiceVersion"
5+
crdKind = "CustomResourceDefinition"
6+
)

internal/pkg/action/operator_install.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -379,7 +379,7 @@ func (i *OperatorInstall) getCSV(ctx context.Context, ip *v1alpha1.InstallPlan)
379379
Namespace: i.config.Namespace,
380380
}
381381
for _, s := range ip.Status.Plan {
382-
if s.Resource.Kind == "ClusterServiceVersion" {
382+
if s.Resource.Kind == csvKind {
383383
csvKey.Name = s.Resource.Name
384384
}
385385
}

internal/pkg/action/operator_uninstall.go

Lines changed: 98 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,20 @@ package action
33
import (
44
"context"
55
"fmt"
6+
"strings"
7+
"time"
68

79
v1 "github.com/operator-framework/api/pkg/operators/v1"
810
"github.com/operator-framework/api/pkg/operators/v1alpha1"
911
"github.com/spf13/pflag"
10-
apiextv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
1112
apierrors "k8s.io/apimachinery/pkg/api/errors"
13+
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
14+
"k8s.io/apimachinery/pkg/runtime/schema"
1215
"k8s.io/apimachinery/pkg/types"
16+
"k8s.io/apimachinery/pkg/util/wait"
1317
"sigs.k8s.io/controller-runtime/pkg/client"
18+
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
19+
"sigs.k8s.io/yaml"
1420

1521
"github.com/operator-framework/kubectl-operator/internal/pkg/log"
1622
)
@@ -59,28 +65,44 @@ func (u *OperatorUninstall) Run(ctx context.Context) error {
5965
return fmt.Errorf("operator package %q not found", u.Package)
6066
}
6167

68+
var crds, csvs, others []controllerutil.Object
69+
if sub.Status.InstallPlanRef != nil {
70+
ipKey := types.NamespacedName{
71+
Namespace: sub.Status.InstallPlanRef.Namespace,
72+
Name: sub.Status.InstallPlanRef.Name,
73+
}
74+
var err error
75+
crds, csvs, others, err = u.getInstallPlanResources(ctx, ipKey)
76+
if err != nil {
77+
return fmt.Errorf("get install plan resources: %v", err)
78+
}
79+
}
80+
6281
if err := u.config.Client.Delete(ctx, sub); err != nil {
6382
return fmt.Errorf("delete subscription %q: %v", sub.Name, err)
6483
}
6584
log.Printf("subscription %q deleted", sub.Name)
6685

67-
if sub.Status.CurrentCSV != "" && sub.Status.CurrentCSV != sub.Status.InstalledCSV {
68-
if err := u.deleteCSVandCRDs(ctx, sub.Status.CurrentCSV, true); err != nil {
86+
if u.DeleteCRDs {
87+
if err := u.deleteCRDs(ctx, crds); err != nil {
6988
return err
7089
}
7190
}
72-
if sub.Status.InstalledCSV != "" {
73-
if err := u.deleteCSVandCRDs(ctx, sub.Status.InstalledCSV, false); err != nil {
74-
return err
75-
}
91+
92+
if err := u.deleteObjects(ctx, false, csvs); err != nil {
93+
return err
94+
}
95+
96+
if err := u.deleteObjects(ctx, false, others); err != nil {
97+
return err
7698
}
7799

78100
if u.DeleteOperatorGroup {
79-
csvs := v1alpha1.ClusterServiceVersionList{}
80-
if err := u.config.Client.List(ctx, &csvs, client.InNamespace(u.config.Namespace)); err != nil {
101+
subs := v1alpha1.SubscriptionList{}
102+
if err := u.config.Client.List(ctx, &subs, client.InNamespace(u.config.Namespace)); err != nil {
81103
return fmt.Errorf("list clusterserviceversions: %v", err)
82104
}
83-
if len(csvs.Items) == 0 {
105+
if len(subs.Items) == 0 {
84106
ogs := v1.OperatorGroupList{}
85107
if err := u.config.Client.List(ctx, &ogs, client.InNamespace(u.config.Namespace)); err != nil {
86108
return fmt.Errorf("list operatorgroups: %v", err)
@@ -98,34 +120,78 @@ func (u *OperatorUninstall) Run(ctx context.Context) error {
98120
return nil
99121
}
100122

101-
func (u *OperatorUninstall) deleteCSVandCRDs(ctx context.Context, csvName string, ignoreNotFound bool) error {
102-
csvKey := types.NamespacedName{
103-
Name: csvName,
104-
Namespace: u.config.Namespace,
105-
}
106-
csv := v1alpha1.ClusterServiceVersion{}
107-
if err := u.config.Client.Get(ctx, csvKey, &csv); err != nil {
108-
if !apierrors.IsNotFound(err) || !ignoreNotFound {
109-
return fmt.Errorf("get clusterserviceversion %q: %v", csvName, err)
110-
}
123+
func (u *OperatorUninstall) deleteCRDs(ctx context.Context, crds []controllerutil.Object) error {
124+
if err := u.deleteObjects(ctx, true, crds); err != nil {
125+
return err
111126
}
127+
return nil
128+
}
112129

113-
if u.DeleteCRDs {
114-
ownedCRDs := csv.Spec.CustomResourceDefinitions.Owned
115-
for _, ownedCRD := range ownedCRDs {
116-
crd := apiextv1.CustomResourceDefinition{}
117-
crd.SetName(ownedCRD.Name)
118-
if err := u.config.Client.Delete(ctx, &crd); err != nil {
119-
return fmt.Errorf("delete crd %q: %v", ownedCRD.Name, err)
130+
func (u *OperatorUninstall) deleteObjects(ctx context.Context, waitForDelete bool, objs []controllerutil.Object) error {
131+
for _, obj := range objs {
132+
obj := obj
133+
lowerKind := strings.ToLower(obj.GetObjectKind().GroupVersionKind().Kind)
134+
if err := u.config.Client.Delete(ctx, obj); err != nil && !apierrors.IsNotFound(err) {
135+
return fmt.Errorf("delete %s %q: %v", lowerKind, obj.GetName(), err)
136+
} else if err == nil {
137+
log.Printf("%s %q deleted", lowerKind, obj.GetName())
138+
}
139+
if waitForDelete {
140+
key, err := client.ObjectKeyFromObject(obj)
141+
if err != nil {
142+
return fmt.Errorf("get %s key: %v", lowerKind, err)
143+
}
144+
if err := wait.PollImmediateUntil(250*time.Millisecond, func() (bool, error) {
145+
if err := u.config.Client.Get(ctx, key, obj); apierrors.IsNotFound(err) {
146+
return true, nil
147+
} else if err != nil {
148+
return false, err
149+
}
150+
return false, nil
151+
}, ctx.Done()); err != nil {
152+
return fmt.Errorf("wait for %s deleted: %v", lowerKind, err)
120153
}
121-
log.Printf("crd %q deleted", ownedCRD.Name)
122154
}
123155
}
156+
return nil
157+
}
124158

125-
if err := u.config.Client.Delete(ctx, &csv); err != nil {
126-
return fmt.Errorf("delete csv %q: %v", csvName, err)
159+
func (u *OperatorUninstall) getInstallPlanResources(ctx context.Context, installPlanKey types.NamespacedName) (crds, csvs, others []controllerutil.Object, err error) {
160+
installPlan := &v1alpha1.InstallPlan{}
161+
if err := u.config.Client.Get(ctx, installPlanKey, installPlan); err != nil {
162+
return nil, nil, nil, fmt.Errorf("get install plan: %v", err)
127163
}
128-
log.Printf("csv %q deleted", csvName)
129164

130-
return nil
165+
for _, step := range installPlan.Status.Plan {
166+
if step.Status != v1alpha1.StepStatusCreated {
167+
continue
168+
}
169+
obj := &unstructured.Unstructured{Object: map[string]interface{}{}}
170+
lowerKind := strings.ToLower(step.Resource.Kind)
171+
if err := yaml.Unmarshal([]byte(step.Resource.Manifest), &obj.Object); err != nil {
172+
return nil, nil, nil, fmt.Errorf("parse %s manifest %q: %v", lowerKind, step.Resource.Name, err)
173+
}
174+
obj.SetGroupVersionKind(schema.GroupVersionKind{
175+
Group: step.Resource.Group,
176+
Version: step.Resource.Version,
177+
Kind: step.Resource.Kind,
178+
})
179+
180+
// TODO(joelanford): This seems necessary for service accounts tied to
181+
// cluster roles and cluster role bindings because the SA namespace
182+
// is not set in the manifest in this case.
183+
// See: https://github.com/operator-framework/operator-lifecycle-manager/blob/c9405d035bc50d9aa290220cb8d75b0402e72707/pkg/controller/registry/resolver/rbac.go#L133
184+
if step.Resource.Kind == "ServiceAccount" && obj.GetNamespace() == "" {
185+
obj.SetNamespace(installPlanKey.Namespace)
186+
}
187+
switch step.Resource.Kind {
188+
case crdKind:
189+
crds = append(crds, obj)
190+
case csvKind:
191+
csvs = append(csvs, obj)
192+
default:
193+
others = append(others, obj)
194+
}
195+
}
196+
return crds, csvs, others, nil
131197
}

internal/pkg/action/operator_upgrade.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,7 @@ func (u *OperatorUpgrade) getCSV(ctx context.Context, ip *v1alpha1.InstallPlan)
113113
Namespace: u.config.Namespace,
114114
}
115115
for _, s := range ip.Status.Plan {
116-
if s.Resource.Kind == "ClusterServiceVersion" {
116+
if s.Resource.Kind == csvKind {
117117
csvKey.Name = s.Resource.Name
118118
}
119119
}

0 commit comments

Comments
 (0)