Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

test/integration: rewrite in BDD style, fixup cleanup command #4303

Merged
merged 2 commits into from
Dec 11, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -169,7 +169,7 @@ test-e2e-ansible-molecule:: image/ansible-operator ## Run molecule-based Ansible
test-e2e-helm:: image/helm-operator ## Run Helm e2e tests
go test ./test/e2e-helm -v -ginkgo.v
test-e2e-integration:: ## Run integration tests
./hack/tests/integration.sh
go test ./test/integration -v -ginkgo.v
./hack/tests/subcommand-olm-install.sh

.DEFAULT_GOAL := help
Expand Down
13 changes: 13 additions & 0 deletions changelog/fragments/cleanup-all-resources.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
entries:
- description: >
Made the `cleanup` command's error handling more robust on deletion and "not found" events.
kind: bugfix
- description: >
Fixed the documented `packagemanifests` make recipe.
kind: bugfix
migration:
header: Update `packagemanifests` make recipe variable `PKG_MAN_OPTS`
body: >
If your project uses the `packagemanifests` make recipe, update your `PKG_MAN_OPTS`
variable to include `PKG_FROM_VERSION` instead of `FROM_VERSION`, ex.
`PKG_MAN_OPTS ?= $(PKG_FROM_VERSION) $(PKG_CHANNELS) $(PKG_IS_DEFAULT_CHANNEL)`.
46 changes: 0 additions & 46 deletions hack/tests/integration.sh

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -160,7 +160,7 @@ func (g *Generator) generate() (base *operatorsv1alpha1.ClusterServiceVersion, e
}
}
if g.FromVersion != "" {
base.Spec.Replaces = genutil.MakeCSVName(g.OperatorName, g.Version)
base.Spec.Replaces = genutil.MakeCSVName(g.OperatorName, g.FromVersion)
}

if err := ApplyTo(g.Collector, base); err != nil {
Expand Down
165 changes: 92 additions & 73 deletions internal/olm/operator/uninstall.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,6 @@ import (
)

const (
csvKind = "ClusterServiceVersion"
crdKind = "CustomResourceDefinition"
)

Expand Down Expand Up @@ -73,7 +72,15 @@ func (u *Uninstall) Run(ctx context.Context) error {
return fmt.Errorf("list subscriptions: %v", err)
}

// Use nil objects to determine if the underlying was found.
// If it was, the object will != nil.
var subObj, csvObj, csObj client.Object
var sub *v1alpha1.Subscription
var crds []client.Object
catsrc := &v1alpha1.CatalogSource{}
catsrc.SetNamespace(u.config.Namespace)
catsrc.SetName(CatalogNameForPackage(u.Package))

for i := range subs.Items {
s := subs.Items[i]
if u.Package == s.Spec.Package {
Expand All @@ -82,88 +89,116 @@ func (u *Uninstall) Run(ctx context.Context) error {
}
}

catsrc := &v1alpha1.CatalogSource{}
catsrcKey := client.ObjectKeyFromObject(catsrc)
if sub != nil {
catsrcKey := types.NamespacedName{
subObj = sub
// Use the subscription's catalog source data only if available.
keyFromSpec := types.NamespacedName{
Namespace: sub.Spec.CatalogSourceNamespace,
Name: sub.Spec.CatalogSource,
}
if err := u.config.Client.Get(ctx, catsrcKey, catsrc); err != nil {
return fmt.Errorf("get catalog source: %v", err)
if catsrcKey.Name != "" && catsrcKey.Namespace != "" {
catsrcKey = keyFromSpec
}

csv, err := u.getInstalledCSV(ctx, sub)
if err != nil {
return fmt.Errorf("get installed CSV %q: %v", sub.Status.InstalledCSV, err)
// CSV name may either be the installed or current name in a subscription's status,
// depending on installation state.
varshaprasad96 marked this conversation as resolved.
Show resolved Hide resolved
csvKey := types.NamespacedName{
Name: sub.Status.InstalledCSV,
Namespace: u.config.Namespace,
}

crds := getCRDs(csv)

// Delete the subscription first, so that no further installs or upgrades
// of the operator occur while we're cleaning up.
if err := u.deleteObjects(ctx, false, sub); err != nil {
return err
if csvKey.Name == "" {
csvKey.Name = sub.Status.CurrentCSV
}

if u.DeleteCRDs {
// Ensure CustomResourceDefinitions are deleted next, so that the operator
// has a chance to handle CRs that have finalizers.
if err := u.deleteObjects(ctx, true, crds...); err != nil {
return err
// This value can be empty which will cause errors.
if csvKey.Name != "" {
csv := &v1alpha1.ClusterServiceVersion{}
if err := u.config.Client.Get(ctx, csvKey, csv); err != nil && !apierrors.IsNotFound(err) {
return fmt.Errorf("error getting installed CSV %q: %v", csvKey.Name, err)
} else if err == nil {
crds = getCRDs(csv)
}
csvObj = csv
}
}

// OLM puts an ownerref on every namespaced resource to the CSV,
// and an owner label on every cluster scoped resource. When CSV is deleted
// kube and olm gc will remove all the referenced resources.
if err := u.deleteObjects(ctx, true, csv); err != nil {
return err
}

} else {
catsrc.SetNamespace(u.config.Namespace)
catsrc.SetName(CatalogNameForPackage(u.Package))
// Get the catalog source to make sure the correct error is returned.
if err := u.config.Client.Get(ctx, catsrcKey, catsrc); err == nil {
csObj = catsrc
} else if !apierrors.IsNotFound(err) {
return fmt.Errorf("error get catalog source: %v", err)
}

// Delete the catalog source. This assumes that all underlying resources related
// to this catalog source have an owner reference to this catalog source so that
// they are automatically garbage-collected.
catsrc.SetGroupVersionKind(v1alpha1.SchemeGroupVersion.WithKind(v1alpha1.CatalogSourceKind))
if err := u.deleteObjects(ctx, true, catsrc); err != nil {
// Deletion order:
//
// 1. Subscription to prevent further installs or upgrades of the operator while cleaning up.
// 2. CustomResourceDefinitions so the operator has a chance to handle CRs that have finalizers.
// 3. ClusterServiceVersion. OLM puts an ownerref on every namespaced resource to the CSV,
// and an owner label on every cluster scoped resource so they get gc'd on deletion.
// 4. CatalogSource. All other resources installed by OLM or operator-sdk related to this
// package will be gc'd.

// Subscriptions can be deleted asynchronously.
if err := u.deleteObjects(ctx, false, subObj); err != nil {
return err
}
var objs []client.Object
if u.DeleteCRDs {
objs = append(objs, crds...)
}
objs = append(objs, csvObj, csObj)
// These objects may have owned resources/finalizers, so block on deletion.
if err := u.deleteObjects(ctx, true, objs...); err != nil {
return err
}

// If this was the last subscription in the namespace and the operator group is
// the one we created, delete it
// If the last subscription in the namespace was deleted and the operator group is
// the one operator-sdk created, delete it.
if u.DeleteOperatorGroups {
if err := u.config.Client.List(ctx, &subs, client.InNamespace(u.config.Namespace)); err != nil {
return fmt.Errorf("list subscriptions: %v", err)
}
if len(subs.Items) == 0 {
ogs := v1.OperatorGroupList{}
if err := u.config.Client.List(ctx, &ogs, client.InNamespace(u.config.Namespace)); err != nil {
return fmt.Errorf("list operatorgroups: %v", err)
}
for _, og := range ogs.Items {
og := og
if len(u.DeleteOperatorGroupNames) == 0 || slice.ContainsString(u.DeleteOperatorGroupNames, og.GetName(), nil) {
if err := u.deleteObjects(ctx, false, &og); err != nil {
return err
}
}
}
if err := u.deleteOperatorGroup(ctx); err != nil {
return err
}
}
if sub == nil {

// If no objects were cleaned up, the package was not found.
if subObj == nil && csObj == nil && csvObj == nil && len(crds) == 0 {
return &ErrPackageNotFound{u.Package}
}
return nil
}

func (u *Uninstall) deleteOperatorGroup(ctx context.Context) error {
subs := v1alpha1.SubscriptionList{}
if err := u.config.Client.List(ctx, &subs, client.InNamespace(u.config.Namespace)); err != nil {
return fmt.Errorf("list subscriptions: %v", err)
}
if len(subs.Items) != 0 {
return nil
}
ogs := v1.OperatorGroupList{}
if err := u.config.Client.List(ctx, &ogs, client.InNamespace(u.config.Namespace)); err != nil {
return fmt.Errorf("list operatorgroups: %v", err)
}
for _, og := range ogs.Items {
if len(u.DeleteOperatorGroupNames) == 0 || slice.ContainsString(u.DeleteOperatorGroupNames, og.GetName(), nil) {
if err := u.deleteObjects(ctx, false, &og); err != nil {
return err
}
}
}
return nil
}

func (u *Uninstall) deleteObjects(ctx context.Context, waitForDelete bool, objs ...client.Object) error {
for _, obj := range objs {
obj := obj
lowerKind := strings.ToLower(obj.GetObjectKind().GroupVersionKind().Kind)
estroz marked this conversation as resolved.
Show resolved Hide resolved
if obj == nil {
continue
}
gvks, _, err := u.config.Scheme.ObjectKinds(obj)
if err != nil {
return err
}
lowerKind := strings.ToLower(gvks[0].Kind)
if err := u.config.Client.Delete(ctx, obj); err != nil && !apierrors.IsNotFound(err) {
return fmt.Errorf("delete %s %q: %v", lowerKind, obj.GetName(), err)
} else if err == nil {
Expand All @@ -186,22 +221,6 @@ func (u *Uninstall) deleteObjects(ctx context.Context, waitForDelete bool, objs
return nil
}

// getInstalledCSV looks up the installed CSV name from the provided subscription and fetches it.
func (u *Uninstall) getInstalledCSV(ctx context.Context, subscription *v1alpha1.Subscription) (*v1alpha1.ClusterServiceVersion, error) {
key := types.NamespacedName{
Name: subscription.Status.InstalledCSV,
Namespace: subscription.GetNamespace(),
}

installedCSV := &v1alpha1.ClusterServiceVersion{}
if err := u.config.Client.Get(ctx, key, installedCSV); err != nil {
return nil, err
}

installedCSV.SetGroupVersionKind(v1alpha1.SchemeGroupVersion.WithKind(csvKind))
return installedCSV, nil
}

// getCRDs returns the list of CRDs required by a CSV.
func getCRDs(csv *v1alpha1.ClusterServiceVersion) (crds []client.Object) {
for _, resource := range csv.Status.RequirementStatus {
Expand Down
2 changes: 1 addition & 1 deletion internal/testutils/olm.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ endif
ifeq ($(IS_CHANNEL_DEFAULT), 1)
PKG_IS_DEFAULT_CHANNEL := --default-channel
endif
PKG_MAN_OPTS ?= $(FROM_VERSION) $(PKG_CHANNELS) $(PKG_IS_DEFAULT_CHANNEL)
PKG_MAN_OPTS ?= $(PKG_FROM_VERSION) $(PKG_CHANNELS) $(PKG_IS_DEFAULT_CHANNEL)
estroz marked this conversation as resolved.
Show resolved Hide resolved
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It shows required an entry + migration step for who is using this pkg manifests scaffold on the projects.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

They aren't scaffolded at all, the user has to add this themselves, so I don't think this is necessary. However it doesn't hurt to add one.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

They are not scaffolded. However, the user might have followed the docs and added these steps.
Then, we should provide the guidance to migrate it in the changelog.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.


# Generate package manifests.
packagemanifests: kustomize %s
Expand Down