Skip to content

Commit

Permalink
Merge pull request #463 from njhale/test-ownerref-gc
Browse files Browse the repository at this point in the history
test(e2e): add OwnerReference GC behavior test
  • Loading branch information
njhale authored Sep 15, 2018
2 parents fb69cf3 + 215f526 commit a6f305d
Show file tree
Hide file tree
Showing 4 changed files with 119 additions and 6 deletions.
22 changes: 22 additions & 0 deletions pkg/lib/ownerutil/util.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,28 @@ func AddNonBlockingOwner(object metav1.Object, owner Owner) {
object.SetOwnerReferences(ownerRefs)
}

// AddOwner adds an owner to the ownerref list.
func AddOwner(object metav1.Object, owner Owner, blockOwnerDeletion, isController bool) {
// Most of the time we won't have TypeMeta on the object, so we infer it for types we know about
inferGroupVersionKind(owner)

ownerRefs := object.GetOwnerReferences()
if ownerRefs == nil {
ownerRefs = []metav1.OwnerReference{}
}
gvk := owner.GroupVersionKind()
apiVersion, kind := gvk.ToAPIVersionAndKind()
ownerRefs = append(ownerRefs, metav1.OwnerReference{
APIVersion: apiVersion,
Kind: kind,
Name: owner.GetName(),
UID: owner.GetUID(),
BlockOwnerDeletion: &blockOwnerDeletion,
Controller: &isController,
})
object.SetOwnerReferences(ownerRefs)
}

// inferGroupVersionKind adds TypeMeta to an owner so that it can be written to an ownerref.
// TypeMeta is generally only known at serialization time, so we often won't know what GVK an owner has.
// For the types we know about, we can add the GVK of the apis that we're using the interact with the object.
Expand Down
84 changes: 84 additions & 0 deletions test/e2e/gc_e2e_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
package e2e

import (
"testing"

"github.com/coreos/go-semver/semver"
"github.com/stretchr/testify/require"
corev1 "k8s.io/api/core/v1"
k8serrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"

"github.com/operator-framework/operator-lifecycle-manager/pkg/lib/ownerutil"
)

// TestOwnerReferenceGCBehavior runs a simple check on OwnerReference behavior to ensure
// a resource with multiple OwnerReferences will not be garbage collected when one of its
// owners has been deleted.
// Test Case:
// CSV-A CSV-B CSV-B
// \ / --Delete CSV-A--> |
// ConfigMap ConfigMap
func TestOwnerReferenceGCBehavior(t *testing.T) {
defer cleaner.NotifyTestComplete(t, true)

ownerA := newCSV("ownera", testNamespace, "", *semver.New("0.0.0"), nil, nil, newNginxInstallStrategy("dep-"))
ownerB := newCSV("ownerb", testNamespace, "", *semver.New("0.0.0"), nil, nil, newNginxInstallStrategy("dep-"))

// create all owners
c := newKubeClient(t)
crc := newCRClient(t)

fetchedA, err := crc.Operators().ClusterServiceVersions(testNamespace).Create(&ownerA)
require.NoError(t, err)
fetchedB, err := crc.Operators().ClusterServiceVersions(testNamespace).Create(&ownerB)
require.NoError(t, err)

dependent := &corev1.ConfigMap{
ObjectMeta: metav1.ObjectMeta{
Name: "dependent",
},
Data: map[string]string{},
}

// add owners
ownerutil.AddOwner(dependent, fetchedA, true, false)
ownerutil.AddOwner(dependent, fetchedB, true, false)

// create dependent
_, err = c.KubernetesInterface().CoreV1().ConfigMaps(testNamespace).Create(dependent)
require.NoError(t, err, "dependent could not be created")

// delete ownerA in the foreground (to ensure any "blocking" dependents are deleted before ownerA)
propagation := metav1.DeletionPropagation("Foreground")
options := metav1.DeleteOptions{PropagationPolicy: &propagation}
err = crc.Operators().ClusterServiceVersions(testNamespace).Delete(fetchedA.GetName(), &options)
require.NoError(t, err)

// wait for deletion of ownerA
waitForDelete(func() error {
_, err := crc.Operators().ClusterServiceVersions(testNamespace).Get(ownerA.GetName(), metav1.GetOptions{})
return err
})

// check for dependent (should still exist since it still has one owner present)
_, err = c.KubernetesInterface().CoreV1().ConfigMaps(testNamespace).Get(dependent.GetName(), metav1.GetOptions{})
require.NoError(t, err, "dependent deleted after one owner was deleted")
t.Log("dependent still exists after one owner was deleted")

// delete ownerB in the foreground (to ensure any "blocking" dependents are deleted before ownerB)
err = crc.Operators().ClusterServiceVersions(testNamespace).Delete(fetchedB.GetName(), &options)
require.NoError(t, err)

// wait for deletion of ownerB
waitForDelete(func() error {
_, err := crc.Operators().ClusterServiceVersions(testNamespace).Get(ownerB.GetName(), metav1.GetOptions{})
return err
})

// check for dependent (should be deleted since last blocking owner was deleted)
_, err = c.KubernetesInterface().CoreV1().ConfigMaps(testNamespace).Get(dependent.GetName(), metav1.GetOptions{})
require.Error(t, err)
require.True(t, k8serrors.IsNotFound(err))
t.Log("dependent successfully garbage collected after both owners were deleted")
}
10 changes: 5 additions & 5 deletions test/e2e/installplan_e2e_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,17 @@ import (
"testing"

"github.com/coreos/go-semver/semver"
"github.com/operator-framework/operator-lifecycle-manager/pkg/api/apis/operators/v1alpha1"
"github.com/operator-framework/operator-lifecycle-manager/pkg/controller/install"
"github.com/operator-framework/operator-lifecycle-manager/pkg/controller/registry"

"github.com/operator-framework/operator-lifecycle-manager/pkg/api/client/clientset/versioned"
"github.com/stretchr/testify/require"
appsv1 "k8s.io/api/apps/v1"
corev1 "k8s.io/api/core/v1"
extv1beta1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/util/wait"

"github.com/operator-framework/operator-lifecycle-manager/pkg/api/apis/operators/v1alpha1"
"github.com/operator-framework/operator-lifecycle-manager/pkg/api/client/clientset/versioned"
"github.com/operator-framework/operator-lifecycle-manager/pkg/controller/install"
"github.com/operator-framework/operator-lifecycle-manager/pkg/controller/registry"
)

type checkInstallPlanFunc func(fip *v1alpha1.InstallPlan) bool
Expand Down
9 changes: 8 additions & 1 deletion test/e2e/util_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -291,7 +291,7 @@ func cleanupOLM(t *testing.T, namespace string) {
c := newKubeClient(t)

// Cleanup non persistent OLM CRs
t.Log("Cleaning up any remaining non persistent resources...")
t.Log("cleaning up any remaining non persistent resources...")
deleteOptions := &metav1.DeleteOptions{GracePeriodSeconds: &immediate}
listOptions := metav1.ListOptions{}
require.NoError(t, crc.OperatorsV1alpha1().ClusterServiceVersions(namespace).DeleteCollection(deleteOptions, listOptions))
Expand All @@ -317,6 +317,13 @@ func buildConfigMapCleanupFunc(t *testing.T, c operatorclient.ClientInterface, n
}
}

func buildServiceAccountCleanupFunc(t *testing.T, c operatorclient.ClientInterface, namespace string, serviceAccount *corev1.ServiceAccount) cleanupFunc {
return func() {
t.Logf("Deleting service account %s...", serviceAccount.GetName())
require.NoError(t, c.KubernetesInterface().CoreV1().ServiceAccounts(namespace).Delete(serviceAccount.GetName(), &metav1.DeleteOptions{}))
}
}

func createInternalCatalogSource(t *testing.T, c operatorclient.ClientInterface, crc versioned.Interface, name, namespace string, manifests []registry.PackageManifest, crds []v1beta1.CustomResourceDefinition, csvs []v1alpha1.ClusterServiceVersion) (*v1alpha1.CatalogSource, cleanupFunc, error) {
// Create a config map containing the PackageManifests and CSVs
configMapName := fmt.Sprintf("%s-configmap", name)
Expand Down

0 comments on commit a6f305d

Please sign in to comment.