Skip to content

Commit 67283da

Browse files
test/e2e: Delete test namespaces asynchronously
Now that namespace deletion is reliable, use the suite tear down to catch non terminated namespaces and stop waiting within each test for deletion. Serial tests that need a clean set of namespaces must use the appropriate flag to control whether they wait for clean namespaces on startup.
1 parent a5135ec commit 67283da

File tree

3 files changed

+1
-216
lines changed

3 files changed

+1
-216
lines changed

test/e2e/framework/BUILD

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,6 @@ go_library(
8787
"//test/e2e/system:go_default_library",
8888
"//test/utils:go_default_library",
8989
"//test/utils/image:go_default_library",
90-
"//vendor/github.com/davecgh/go-spew/spew:go_default_library",
9190
"//vendor/github.com/onsi/ginkgo:go_default_library",
9291
"//vendor/github.com/onsi/ginkgo/config:go_default_library",
9392
"//vendor/github.com/onsi/gomega:go_default_library",

test/e2e/framework/framework.go

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -303,11 +303,7 @@ func (f *Framework) AfterEach() {
303303
if TestContext.DeleteNamespace && (TestContext.DeleteNamespaceOnFailure || !ginkgo.CurrentGinkgoTestDescription().Failed) {
304304
for _, ns := range f.namespacesToDelete {
305305
ginkgo.By(fmt.Sprintf("Destroying namespace %q for this suite.", ns.Name))
306-
timeout := DefaultNamespaceDeletionTimeout
307-
if f.NamespaceDeletionTimeout != 0 {
308-
timeout = f.NamespaceDeletionTimeout
309-
}
310-
if err := deleteNS(f.ClientSet, f.DynamicClient, ns.Name, timeout); err != nil {
306+
if err := f.ClientSet.CoreV1().Namespaces().Delete(ns.Name, nil); err != nil {
311307
if !apierrors.IsNotFound(err) {
312308
nsDeletionErrors[ns.Name] = err
313309
} else {

test/e2e/framework/util.go

Lines changed: 0 additions & 210 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,6 @@ import (
4040
"syscall"
4141
"time"
4242

43-
"github.com/davecgh/go-spew/spew"
4443
"golang.org/x/net/websocket"
4544
"k8s.io/klog"
4645

@@ -795,193 +794,6 @@ func CheckTestingNSDeletedExcept(c clientset.Interface, skip string) error {
795794
return fmt.Errorf("Waiting for terminating namespaces to be deleted timed out")
796795
}
797796

798-
// deleteNS deletes the provided namespace, waits for it to be completely deleted, and then checks
799-
// whether there are any pods remaining in a non-terminating state.
800-
func deleteNS(c clientset.Interface, dynamicClient dynamic.Interface, namespace string, timeout time.Duration) error {
801-
startTime := time.Now()
802-
if err := c.CoreV1().Namespaces().Delete(namespace, nil); err != nil {
803-
return err
804-
}
805-
806-
// wait for namespace to delete or timeout.
807-
var lastNamespace *v1.Namespace
808-
err := wait.PollImmediate(2*time.Second, timeout, func() (bool, error) {
809-
var err error
810-
lastNamespace, err = c.CoreV1().Namespaces().Get(namespace, metav1.GetOptions{})
811-
if err != nil {
812-
if apierrs.IsNotFound(err) {
813-
return true, nil
814-
}
815-
Logf("Error while waiting for namespace to be terminated: %v", err)
816-
return false, nil
817-
}
818-
return false, nil
819-
})
820-
821-
// verify there is no more remaining content in the namespace
822-
remainingContent, cerr := hasRemainingContent(c, dynamicClient, namespace)
823-
if cerr != nil {
824-
return cerr
825-
}
826-
827-
// if content remains, let's dump information about the namespace, and system for flake debugging.
828-
remainingPods := 0
829-
missingTimestamp := 0
830-
if remainingContent {
831-
// log information about namespace, and set of namespaces in api server to help flake detection
832-
logNamespace(c, namespace)
833-
logNamespaces(c, namespace)
834-
835-
// if we can, check if there were pods remaining with no timestamp.
836-
remainingPods, missingTimestamp, _ = e2epod.CountRemainingPods(c, namespace)
837-
}
838-
839-
// a timeout waiting for namespace deletion happened!
840-
if err != nil {
841-
// namespaces now have conditions that are useful for debugging generic resources and finalizers
842-
Logf("namespace did not cleanup: %s", spew.Sdump(lastNamespace))
843-
844-
// some content remains in the namespace
845-
if remainingContent {
846-
// pods remain
847-
if remainingPods > 0 {
848-
if missingTimestamp != 0 {
849-
// pods remained, but were not undergoing deletion (namespace controller is probably culprit)
850-
return fmt.Errorf("namespace %v was not deleted with limit: %v, pods remaining: %v, pods missing deletion timestamp: %v", namespace, err, remainingPods, missingTimestamp)
851-
}
852-
// but they were all undergoing deletion (kubelet is probably culprit, check NodeLost)
853-
return fmt.Errorf("namespace %v was not deleted with limit: %v, pods remaining: %v", namespace, err, remainingPods)
854-
}
855-
// other content remains (namespace controller is probably screwed up)
856-
return fmt.Errorf("namespace %v was not deleted with limit: %v, namespaced content other than pods remain", namespace, err)
857-
}
858-
// no remaining content, but namespace was not deleted (namespace controller is probably wedged)
859-
return fmt.Errorf("namespace %v was not deleted with limit: %v, namespace is empty but is not yet removed", namespace, err)
860-
}
861-
Logf("namespace %v deletion completed in %s", namespace, time.Since(startTime))
862-
return nil
863-
}
864-
865-
// logNamespaces logs the number of namespaces by phase
866-
// namespace is the namespace the test was operating against that failed to delete so it can be grepped in logs
867-
func logNamespaces(c clientset.Interface, namespace string) {
868-
namespaceList, err := c.CoreV1().Namespaces().List(metav1.ListOptions{})
869-
if err != nil {
870-
Logf("namespace: %v, unable to list namespaces: %v", namespace, err)
871-
return
872-
}
873-
874-
numActive := 0
875-
numTerminating := 0
876-
for _, namespace := range namespaceList.Items {
877-
if namespace.Status.Phase == v1.NamespaceActive {
878-
numActive++
879-
} else {
880-
numTerminating++
881-
}
882-
}
883-
Logf("namespace: %v, total namespaces: %v, active: %v, terminating: %v", namespace, len(namespaceList.Items), numActive, numTerminating)
884-
}
885-
886-
// logNamespace logs detail about a namespace
887-
func logNamespace(c clientset.Interface, namespace string) {
888-
ns, err := c.CoreV1().Namespaces().Get(namespace, metav1.GetOptions{})
889-
if err != nil {
890-
if apierrs.IsNotFound(err) {
891-
Logf("namespace: %v no longer exists", namespace)
892-
return
893-
}
894-
Logf("namespace: %v, unable to get namespace due to error: %v", namespace, err)
895-
return
896-
}
897-
Logf("namespace: %v, DeletionTimetamp: %v, Finalizers: %v, Phase: %v", ns.Name, ns.DeletionTimestamp, ns.Spec.Finalizers, ns.Status.Phase)
898-
}
899-
900-
// isDynamicDiscoveryError returns true if the error is a group discovery error
901-
// only for groups expected to be created/deleted dynamically during e2e tests
902-
func isDynamicDiscoveryError(err error) bool {
903-
if !discovery.IsGroupDiscoveryFailedError(err) {
904-
return false
905-
}
906-
discoveryErr := err.(*discovery.ErrGroupDiscoveryFailed)
907-
for gv := range discoveryErr.Groups {
908-
switch gv.Group {
909-
case "mygroup.example.com":
910-
// custom_resource_definition
911-
// garbage_collector
912-
case "wardle.k8s.io":
913-
// aggregator
914-
case "metrics.k8s.io":
915-
// aggregated metrics server add-on, no persisted resources
916-
default:
917-
Logf("discovery error for unexpected group: %#v", gv)
918-
return false
919-
}
920-
}
921-
return true
922-
}
923-
924-
// hasRemainingContent checks if there is remaining content in the namespace via API discovery
925-
func hasRemainingContent(c clientset.Interface, dynamicClient dynamic.Interface, namespace string) (bool, error) {
926-
// some tests generate their own framework.Client rather than the default
927-
// TODO: ensure every test call has a configured dynamicClient
928-
if dynamicClient == nil {
929-
return false, nil
930-
}
931-
932-
// find out what content is supported on the server
933-
// Since extension apiserver is not always available, e.g. metrics server sometimes goes down,
934-
// add retry here.
935-
resources, err := waitForServerPreferredNamespacedResources(c.Discovery(), 30*time.Second)
936-
if err != nil {
937-
return false, err
938-
}
939-
resources = discovery.FilteredBy(discovery.SupportsAllVerbs{Verbs: []string{"list", "delete"}}, resources)
940-
groupVersionResources, err := discovery.GroupVersionResources(resources)
941-
if err != nil {
942-
return false, err
943-
}
944-
945-
// TODO: temporary hack for https://github.com/kubernetes/kubernetes/issues/31798
946-
ignoredResources := sets.NewString("bindings")
947-
948-
contentRemaining := false
949-
950-
// dump how many of resource type is on the server in a log.
951-
for gvr := range groupVersionResources {
952-
// get a client for this group version...
953-
dynamicClient := dynamicClient.Resource(gvr).Namespace(namespace)
954-
if err != nil {
955-
// not all resource types support list, so some errors here are normal depending on the resource type.
956-
Logf("namespace: %s, unable to get client - gvr: %v, error: %v", namespace, gvr, err)
957-
continue
958-
}
959-
// get the api resource
960-
apiResource := metav1.APIResource{Name: gvr.Resource, Namespaced: true}
961-
if ignoredResources.Has(gvr.Resource) {
962-
Logf("namespace: %s, resource: %s, ignored listing per whitelist", namespace, apiResource.Name)
963-
continue
964-
}
965-
unstructuredList, err := dynamicClient.List(metav1.ListOptions{})
966-
if err != nil {
967-
// not all resources support list, so we ignore those
968-
if apierrs.IsMethodNotSupported(err) || apierrs.IsNotFound(err) || apierrs.IsForbidden(err) {
969-
continue
970-
}
971-
// skip unavailable servers
972-
if apierrs.IsServiceUnavailable(err) {
973-
continue
974-
}
975-
return false, err
976-
}
977-
if len(unstructuredList.Items) > 0 {
978-
Logf("namespace: %s, resource: %s, items remaining: %v", namespace, apiResource.Name, len(unstructuredList.Items))
979-
contentRemaining = true
980-
}
981-
}
982-
return contentRemaining, nil
983-
}
984-
985797
// ContainerInitInvariant checks for an init containers are initialized and invariant on both older and newer.
986798
func ContainerInitInvariant(older, newer runtime.Object) error {
987799
oldPod := older.(*v1.Pod)
@@ -3180,28 +2992,6 @@ func DsFromManifest(url string) (*appsv1.DaemonSet, error) {
31802992
return &ds, nil
31812993
}
31822994

3183-
// waitForServerPreferredNamespacedResources waits until server preferred namespaced resources could be successfully discovered.
3184-
// TODO: Fix https://github.com/kubernetes/kubernetes/issues/55768 and remove the following retry.
3185-
func waitForServerPreferredNamespacedResources(d discovery.DiscoveryInterface, timeout time.Duration) ([]*metav1.APIResourceList, error) {
3186-
Logf("Waiting up to %v for server preferred namespaced resources to be successfully discovered", timeout)
3187-
var resources []*metav1.APIResourceList
3188-
if err := wait.PollImmediate(Poll, timeout, func() (bool, error) {
3189-
var err error
3190-
resources, err = d.ServerPreferredNamespacedResources()
3191-
if err == nil || isDynamicDiscoveryError(err) {
3192-
return true, nil
3193-
}
3194-
if !discovery.IsGroupDiscoveryFailedError(err) {
3195-
return false, err
3196-
}
3197-
Logf("Error discoverying server preferred namespaced resources: %v, retrying in %v.", err, Poll)
3198-
return false, nil
3199-
}); err != nil {
3200-
return nil, err
3201-
}
3202-
return resources, nil
3203-
}
3204-
32052995
// WaitForPersistentVolumeClaimDeleted waits for a PersistentVolumeClaim to be removed from the system until timeout occurs, whichever comes first.
32062996
func WaitForPersistentVolumeClaimDeleted(c clientset.Interface, ns string, pvcName string, Poll, timeout time.Duration) error {
32072997
Logf("Waiting up to %v for PersistentVolumeClaim %s to be removed", timeout, pvcName)

0 commit comments

Comments
 (0)