From 228ab0d8821d08f6480b73dfaa51f12565335500 Mon Sep 17 00:00:00 2001 From: Maru Newby Date: Wed, 21 Jun 2017 21:45:26 -0700 Subject: [PATCH] fed: Move namespace propagation to the sync controller --- .../federation-controller-manager/app/BUILD | 2 - .../app/controllermanager.go | 10 - federation/pkg/federatedtypes/BUILD | 5 + federation/pkg/federatedtypes/namespace.go | 215 ++++++++ federation/pkg/federation-controller/BUILD | 1 - .../pkg/federation-controller/namespace/BUILD | 75 --- .../namespace/namespace_controller.go | 460 ------------------ .../namespace/namespace_controller_test.go | 188 ------- .../federation-controller/sync/controller.go | 11 + test/e2e_federation/namespace.go | 33 -- test/test_owners.csv | 1 - 11 files changed, 231 insertions(+), 770 deletions(-) create mode 100644 federation/pkg/federatedtypes/namespace.go delete mode 100644 federation/pkg/federation-controller/namespace/BUILD delete mode 100644 federation/pkg/federation-controller/namespace/namespace_controller.go delete mode 100644 federation/pkg/federation-controller/namespace/namespace_controller_test.go diff --git a/federation/cmd/federation-controller-manager/app/BUILD b/federation/cmd/federation-controller-manager/app/BUILD index 1069570bbd2..a4fffac2a2b 100644 --- a/federation/cmd/federation-controller-manager/app/BUILD +++ b/federation/cmd/federation-controller-manager/app/BUILD @@ -24,7 +24,6 @@ go_library( "//federation/pkg/federatedtypes:go_default_library", "//federation/pkg/federation-controller/cluster:go_default_library", "//federation/pkg/federation-controller/ingress:go_default_library", - "//federation/pkg/federation-controller/namespace:go_default_library", "//federation/pkg/federation-controller/service:go_default_library", "//federation/pkg/federation-controller/service/dns:go_default_library", "//federation/pkg/federation-controller/sync:go_default_library", @@ -40,7 +39,6 @@ go_library( "//vendor/k8s.io/apiserver/pkg/server/healthz:go_default_library", "//vendor/k8s.io/apiserver/pkg/util/flag:go_default_library", "//vendor/k8s.io/client-go/discovery:go_default_library", - "//vendor/k8s.io/client-go/dynamic:go_default_library", "//vendor/k8s.io/client-go/rest:go_default_library", "//vendor/k8s.io/client-go/tools/clientcmd:go_default_library", ], diff --git a/federation/cmd/federation-controller-manager/app/controllermanager.go b/federation/cmd/federation-controller-manager/app/controllermanager.go index 1f8b41ec9b8..9144eca97f4 100644 --- a/federation/cmd/federation-controller-manager/app/controllermanager.go +++ b/federation/cmd/federation-controller-manager/app/controllermanager.go @@ -30,7 +30,6 @@ import ( "k8s.io/apimachinery/pkg/util/wait" "k8s.io/apiserver/pkg/server/healthz" utilflag "k8s.io/apiserver/pkg/util/flag" - "k8s.io/client-go/dynamic" restclient "k8s.io/client-go/rest" "k8s.io/client-go/tools/clientcmd" federationclientset "k8s.io/kubernetes/federation/client/clientset_generated/federation_clientset" @@ -38,7 +37,6 @@ import ( "k8s.io/kubernetes/federation/pkg/federatedtypes" clustercontroller "k8s.io/kubernetes/federation/pkg/federation-controller/cluster" ingresscontroller "k8s.io/kubernetes/federation/pkg/federation-controller/ingress" - namespacecontroller "k8s.io/kubernetes/federation/pkg/federation-controller/namespace" servicecontroller "k8s.io/kubernetes/federation/pkg/federation-controller/service" servicednscontroller "k8s.io/kubernetes/federation/pkg/federation-controller/service/dns" synccontroller "k8s.io/kubernetes/federation/pkg/federation-controller/sync" @@ -151,14 +149,6 @@ func StartControllers(s *options.CMServer, restClientCfg *restclient.Config) err go serviceController.Run(s.ConcurrentServiceSyncs, wait.NeverStop) } - if controllerEnabled(s.Controllers, serverResources, namespacecontroller.ControllerName, namespacecontroller.RequiredResources, true) { - glog.V(3).Infof("Loading client config for namespace controller %q", namespacecontroller.UserAgentName) - nsClientset := federationclientset.NewForConfigOrDie(restclient.AddUserAgent(restClientCfg, namespacecontroller.UserAgentName)) - namespaceController := namespacecontroller.NewNamespaceController(nsClientset, dynamic.NewDynamicClientPool(restclient.AddUserAgent(restClientCfg, namespacecontroller.UserAgentName))) - glog.V(3).Infof("Running namespace controller") - namespaceController.Run(wait.NeverStop) - } - for kind, federatedType := range federatedtypes.FederatedTypes() { if controllerEnabled(s.Controllers, serverResources, federatedType.ControllerName, federatedType.RequiredResources, true) { synccontroller.StartFederationSyncController(kind, federatedType.AdapterFactory, restClientCfg, stopChan, minimizeLatency) diff --git a/federation/pkg/federatedtypes/BUILD b/federation/pkg/federatedtypes/BUILD index 5d3eacf93d4..7bb01c29d64 100644 --- a/federation/pkg/federatedtypes/BUILD +++ b/federation/pkg/federatedtypes/BUILD @@ -15,6 +15,7 @@ go_library( "configmap.go", "daemonset.go", "deployment.go", + "namespace.go", "qualifiedname.go", "registry.go", "replicaset.go", @@ -30,7 +31,9 @@ go_library( "//federation/pkg/federation-controller/util/planner:go_default_library", "//federation/pkg/federation-controller/util/podanalyzer:go_default_library", "//federation/pkg/federation-controller/util/replicapreferences:go_default_library", + "//pkg/api:go_default_library", "//pkg/client/clientset_generated/clientset:go_default_library", + "//pkg/controller/namespace/deletion:go_default_library", "//vendor/github.com/golang/glog:go_default_library", "//vendor/k8s.io/api/core/v1:go_default_library", "//vendor/k8s.io/api/extensions/v1beta1:go_default_library", @@ -39,7 +42,9 @@ go_library( "//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library", "//vendor/k8s.io/apimachinery/pkg/runtime/schema:go_default_library", "//vendor/k8s.io/apimachinery/pkg/watch:go_default_library", + "//vendor/k8s.io/client-go/dynamic:go_default_library", "//vendor/k8s.io/client-go/rest:go_default_library", + "//vendor/k8s.io/client-go/tools/record:go_default_library", ], ) diff --git a/federation/pkg/federatedtypes/namespace.go b/federation/pkg/federatedtypes/namespace.go new file mode 100644 index 00000000000..43eadfac145 --- /dev/null +++ b/federation/pkg/federatedtypes/namespace.go @@ -0,0 +1,215 @@ +/* +Copyright 2017 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package federatedtypes + +import ( + "fmt" + + apiv1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + pkgruntime "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/apimachinery/pkg/watch" + "k8s.io/client-go/dynamic" + restclient "k8s.io/client-go/rest" + "k8s.io/client-go/tools/record" + federationclientset "k8s.io/kubernetes/federation/client/clientset_generated/federation_clientset" + "k8s.io/kubernetes/federation/pkg/federation-controller/util" + "k8s.io/kubernetes/pkg/api" + kubeclientset "k8s.io/kubernetes/pkg/client/clientset_generated/clientset" + "k8s.io/kubernetes/pkg/controller/namespace/deletion" + + "github.com/golang/glog" +) + +const ( + NamespaceKind = "namespace" + NamespaceControllerName = "namespaces" +) + +func init() { + RegisterFederatedType(NamespaceKind, NamespaceControllerName, []schema.GroupVersionResource{apiv1.SchemeGroupVersion.WithResource(NamespaceControllerName)}, NewNamespaceAdapter) +} + +type NamespaceAdapter struct { + client federationclientset.Interface + deleter deletion.NamespacedResourcesDeleterInterface +} + +func NewNamespaceAdapter(client federationclientset.Interface, config *restclient.Config) FederatedTypeAdapter { + dynamicClientPool := dynamic.NewDynamicClientPool(config) + discoverResourcesFunc := client.Discovery().ServerPreferredNamespacedResources + deleter := deletion.NewNamespacedResourcesDeleter( + client.Core().Namespaces(), + dynamicClientPool, + nil, + discoverResourcesFunc, + apiv1.FinalizerKubernetes, + false) + return &NamespaceAdapter{client: client, deleter: deleter} +} + +func (a *NamespaceAdapter) Kind() string { + return NamespaceKind +} + +func (a *NamespaceAdapter) ObjectType() pkgruntime.Object { + return &apiv1.Namespace{} +} + +func (a *NamespaceAdapter) IsExpectedType(obj interface{}) bool { + _, ok := obj.(*apiv1.Namespace) + return ok +} + +func (a *NamespaceAdapter) Copy(obj pkgruntime.Object) pkgruntime.Object { + namespace := obj.(*apiv1.Namespace) + return &apiv1.Namespace{ + ObjectMeta: util.DeepCopyRelevantObjectMeta(namespace.ObjectMeta), + Spec: *(util.DeepCopyApiTypeOrPanic(&namespace.Spec).(*apiv1.NamespaceSpec)), + } +} + +func (a *NamespaceAdapter) Equivalent(obj1, obj2 pkgruntime.Object) bool { + return util.ObjectMetaAndSpecEquivalent(obj1, obj2) +} + +func (a *NamespaceAdapter) QualifiedName(obj pkgruntime.Object) QualifiedName { + namespace := obj.(*apiv1.Namespace) + return QualifiedName{Name: namespace.Name} +} + +func (a *NamespaceAdapter) ObjectMeta(obj pkgruntime.Object) *metav1.ObjectMeta { + return &obj.(*apiv1.Namespace).ObjectMeta +} + +func (a *NamespaceAdapter) FedCreate(obj pkgruntime.Object) (pkgruntime.Object, error) { + namespace := obj.(*apiv1.Namespace) + return a.client.CoreV1().Namespaces().Create(namespace) +} + +func (a *NamespaceAdapter) FedDelete(qualifiedName QualifiedName, options *metav1.DeleteOptions) error { + return a.client.CoreV1().Namespaces().Delete(qualifiedName.Name, options) +} + +func (a *NamespaceAdapter) FedGet(qualifiedName QualifiedName) (pkgruntime.Object, error) { + return a.client.CoreV1().Namespaces().Get(qualifiedName.Name, metav1.GetOptions{}) +} + +func (a *NamespaceAdapter) FedList(namespace string, options metav1.ListOptions) (pkgruntime.Object, error) { + return a.client.CoreV1().Namespaces().List(options) +} + +func (a *NamespaceAdapter) FedUpdate(obj pkgruntime.Object) (pkgruntime.Object, error) { + namespace := obj.(*apiv1.Namespace) + return a.client.CoreV1().Namespaces().Update(namespace) +} + +func (a *NamespaceAdapter) FedWatch(namespace string, options metav1.ListOptions) (watch.Interface, error) { + return a.client.CoreV1().Namespaces().Watch(options) +} + +func (a *NamespaceAdapter) ClusterCreate(client kubeclientset.Interface, obj pkgruntime.Object) (pkgruntime.Object, error) { + namespace := obj.(*apiv1.Namespace) + return client.CoreV1().Namespaces().Create(namespace) +} + +func (a *NamespaceAdapter) ClusterDelete(client kubeclientset.Interface, qualifiedName QualifiedName, options *metav1.DeleteOptions) error { + return client.CoreV1().Namespaces().Delete(qualifiedName.Name, options) +} + +func (a *NamespaceAdapter) ClusterGet(client kubeclientset.Interface, qualifiedName QualifiedName) (pkgruntime.Object, error) { + return client.CoreV1().Namespaces().Get(qualifiedName.Name, metav1.GetOptions{}) +} + +func (a *NamespaceAdapter) ClusterList(client kubeclientset.Interface, namespace string, options metav1.ListOptions) (pkgruntime.Object, error) { + return client.CoreV1().Namespaces().List(options) +} + +func (a *NamespaceAdapter) ClusterUpdate(client kubeclientset.Interface, obj pkgruntime.Object) (pkgruntime.Object, error) { + namespace := obj.(*apiv1.Namespace) + return client.CoreV1().Namespaces().Update(namespace) +} + +func (a *NamespaceAdapter) ClusterWatch(client kubeclientset.Interface, namespace string, options metav1.ListOptions) (watch.Interface, error) { + return client.CoreV1().Namespaces().Watch(options) +} + +func (a *NamespaceAdapter) IsSchedulingAdapter() bool { + return false +} + +func (a *NamespaceAdapter) NewTestObject(namespace string) pkgruntime.Object { + return &apiv1.Namespace{ + ObjectMeta: metav1.ObjectMeta{ + GenerateName: "test-namespace-", + }, + Spec: apiv1.NamespaceSpec{ + Finalizers: []apiv1.FinalizerName{apiv1.FinalizerKubernetes}, + }, + } +} + +// CleanUpNamespace deletes all resources in a given namespace. +func (a *NamespaceAdapter) CleanUpNamespace(obj pkgruntime.Object, eventRecorder record.EventRecorder) (pkgruntime.Object, error) { + namespace := obj.(*apiv1.Namespace) + name := namespace.Name + + // Set Terminating status. + updatedNamespace := &apiv1.Namespace{ + ObjectMeta: namespace.ObjectMeta, + Spec: namespace.Spec, + Status: apiv1.NamespaceStatus{ + Phase: apiv1.NamespaceTerminating, + }, + } + var err error + if namespace.Status.Phase != apiv1.NamespaceTerminating { + glog.V(2).Infof("Marking ns %s as terminating", name) + eventRecorder.Event(namespace, api.EventTypeNormal, "DeleteNamespace", fmt.Sprintf("Marking for deletion")) + _, err = a.FedUpdate(updatedNamespace) + if err != nil { + return nil, fmt.Errorf("failed to update namespace: %v", err) + } + } + + if hasFinalizerInSpec(updatedNamespace, apiv1.FinalizerKubernetes) { + // Delete resources in this namespace. + err = a.deleter.Delete(name) + if err != nil { + return nil, fmt.Errorf("error in deleting resources in namespace %s: %v", name, err) + } + glog.V(2).Infof("Removed kubernetes finalizer from ns %s", name) + // Fetch the updated Namespace. + obj, err = a.FedGet(QualifiedName{Name: name}) + updatedNamespace = obj.(*apiv1.Namespace) + if err != nil { + return nil, fmt.Errorf("error in fetching updated namespace %s: %s", name, err) + } + } + + return updatedNamespace, nil +} + +func hasFinalizerInSpec(namespace *apiv1.Namespace, finalizer apiv1.FinalizerName) bool { + for i := range namespace.Spec.Finalizers { + if namespace.Spec.Finalizers[i] == finalizer { + return true + } + } + return false +} diff --git a/federation/pkg/federation-controller/BUILD b/federation/pkg/federation-controller/BUILD index 1c5fd2d39bd..28057fc52ea 100644 --- a/federation/pkg/federation-controller/BUILD +++ b/federation/pkg/federation-controller/BUILD @@ -26,7 +26,6 @@ filegroup( ":package-srcs", "//federation/pkg/federation-controller/cluster:all-srcs", "//federation/pkg/federation-controller/ingress:all-srcs", - "//federation/pkg/federation-controller/namespace:all-srcs", "//federation/pkg/federation-controller/service:all-srcs", "//federation/pkg/federation-controller/sync:all-srcs", "//federation/pkg/federation-controller/util:all-srcs", diff --git a/federation/pkg/federation-controller/namespace/BUILD b/federation/pkg/federation-controller/namespace/BUILD deleted file mode 100644 index 1216d707201..00000000000 --- a/federation/pkg/federation-controller/namespace/BUILD +++ /dev/null @@ -1,75 +0,0 @@ -package(default_visibility = ["//visibility:public"]) - -licenses(["notice"]) - -load( - "@io_bazel_rules_go//go:def.bzl", - "go_library", - "go_test", -) - -go_library( - name = "go_default_library", - srcs = ["namespace_controller.go"], - tags = ["automanaged"], - deps = [ - "//federation/apis/federation/v1beta1:go_default_library", - "//federation/client/clientset_generated/federation_clientset:go_default_library", - "//federation/pkg/federation-controller/util:go_default_library", - "//federation/pkg/federation-controller/util/deletionhelper:go_default_library", - "//federation/pkg/federation-controller/util/eventsink:go_default_library", - "//pkg/api:go_default_library", - "//pkg/client/clientset_generated/clientset:go_default_library", - "//pkg/controller:go_default_library", - "//pkg/controller/namespace/deletion:go_default_library", - "//vendor/github.com/golang/glog:go_default_library", - "//vendor/k8s.io/api/core/v1:go_default_library", - "//vendor/k8s.io/apimachinery/pkg/api/errors:go_default_library", - "//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library", - "//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library", - "//vendor/k8s.io/apimachinery/pkg/runtime/schema:go_default_library", - "//vendor/k8s.io/apimachinery/pkg/watch:go_default_library", - "//vendor/k8s.io/client-go/dynamic:go_default_library", - "//vendor/k8s.io/client-go/tools/cache:go_default_library", - "//vendor/k8s.io/client-go/tools/record:go_default_library", - "//vendor/k8s.io/client-go/util/flowcontrol:go_default_library", - ], -) - -go_test( - name = "go_default_test", - srcs = ["namespace_controller_test.go"], - library = ":go_default_library", - tags = ["automanaged"], - deps = [ - "//federation/apis/federation/v1beta1:go_default_library", - "//federation/client/clientset_generated/federation_clientset/fake:go_default_library", - "//federation/pkg/federation-controller/util:go_default_library", - "//federation/pkg/federation-controller/util/deletionhelper:go_default_library", - "//federation/pkg/federation-controller/util/test:go_default_library", - "//pkg/client/clientset_generated/clientset:go_default_library", - "//pkg/client/clientset_generated/clientset/fake:go_default_library", - "//vendor/github.com/stretchr/testify/assert:go_default_library", - "//vendor/github.com/stretchr/testify/require:go_default_library", - "//vendor/k8s.io/api/core/v1:go_default_library", - "//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library", - "//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library", - "//vendor/k8s.io/apimachinery/pkg/util/wait:go_default_library", - "//vendor/k8s.io/client-go/dynamic:go_default_library", - "//vendor/k8s.io/client-go/rest:go_default_library", - "//vendor/k8s.io/client-go/testing:go_default_library", - ], -) - -filegroup( - name = "package-srcs", - srcs = glob(["**"]), - tags = ["automanaged"], - visibility = ["//visibility:private"], -) - -filegroup( - name = "all-srcs", - srcs = [":package-srcs"], - tags = ["automanaged"], -) diff --git a/federation/pkg/federation-controller/namespace/namespace_controller.go b/federation/pkg/federation-controller/namespace/namespace_controller.go deleted file mode 100644 index 9979ac8c1df..00000000000 --- a/federation/pkg/federation-controller/namespace/namespace_controller.go +++ /dev/null @@ -1,460 +0,0 @@ -/* -Copyright 2016 The Kubernetes Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package namespace - -import ( - "fmt" - "time" - - apiv1 "k8s.io/api/core/v1" - clientv1 "k8s.io/api/core/v1" - "k8s.io/apimachinery/pkg/api/errors" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/runtime" - "k8s.io/apimachinery/pkg/runtime/schema" - "k8s.io/apimachinery/pkg/watch" - "k8s.io/client-go/dynamic" - "k8s.io/client-go/tools/cache" - "k8s.io/client-go/tools/record" - "k8s.io/client-go/util/flowcontrol" - federationapi "k8s.io/kubernetes/federation/apis/federation/v1beta1" - federationclientset "k8s.io/kubernetes/federation/client/clientset_generated/federation_clientset" - "k8s.io/kubernetes/federation/pkg/federation-controller/util" - "k8s.io/kubernetes/federation/pkg/federation-controller/util/deletionhelper" - "k8s.io/kubernetes/federation/pkg/federation-controller/util/eventsink" - "k8s.io/kubernetes/pkg/api" - kubeclientset "k8s.io/kubernetes/pkg/client/clientset_generated/clientset" - "k8s.io/kubernetes/pkg/controller" - "k8s.io/kubernetes/pkg/controller/namespace/deletion" - - "github.com/golang/glog" -) - -const ( - allClustersKey = "ALL_CLUSTERS" - ControllerName = "namespaces" - UserAgentName = "federation-namespace-controller" -) - -var ( - RequiredResources = []schema.GroupVersionResource{apiv1.SchemeGroupVersion.WithResource("namespaces")} -) - -type NamespaceController struct { - // For triggering single namespace reconciliation. This is used when there is an - // add/update/delete operation on a namespace in either federated API server or - // in some member of the federation. - namespaceDeliverer *util.DelayingDeliverer - - // For triggering all namespaces reconciliation. This is used when - // a new cluster becomes available. - clusterDeliverer *util.DelayingDeliverer - - // Contains namespaces present in members of federation. - namespaceFederatedInformer util.FederatedInformer - // For updating members of federation. - federatedUpdater util.FederatedUpdater - // Definitions of namespaces that should be federated. - namespaceInformerStore cache.Store - // Informer controller for namespaces that should be federated. - namespaceInformerController cache.Controller - - // Client to federated api server. - federatedApiClient federationclientset.Interface - - // Backoff manager for namespaces - namespaceBackoff *flowcontrol.Backoff - - // For events - eventRecorder record.EventRecorder - - deletionHelper *deletionhelper.DeletionHelper - - // Helper to delete all resources in a namespace. - namespacedResourcesDeleter deletion.NamespacedResourcesDeleterInterface - - namespaceReviewDelay time.Duration - clusterAvailableDelay time.Duration - smallDelay time.Duration - updateTimeout time.Duration -} - -// NewNamespaceController returns a new namespace controller -func NewNamespaceController(client federationclientset.Interface, dynamicClientPool dynamic.ClientPool) *NamespaceController { - broadcaster := record.NewBroadcaster() - broadcaster.StartRecordingToSink(eventsink.NewFederatedEventSink(client)) - recorder := broadcaster.NewRecorder(api.Scheme, clientv1.EventSource{Component: UserAgentName}) - - nc := &NamespaceController{ - federatedApiClient: client, - namespaceReviewDelay: time.Second * 10, - clusterAvailableDelay: time.Second * 20, - smallDelay: time.Second * 3, - updateTimeout: time.Second * 30, - namespaceBackoff: flowcontrol.NewBackOff(5*time.Second, time.Minute), - eventRecorder: recorder, - } - - // Build deliverers for triggering reconciliations. - nc.namespaceDeliverer = util.NewDelayingDeliverer() - nc.clusterDeliverer = util.NewDelayingDeliverer() - - // Start informer in federated API servers on namespaces that should be federated. - nc.namespaceInformerStore, nc.namespaceInformerController = cache.NewInformer( - &cache.ListWatch{ - ListFunc: func(options metav1.ListOptions) (runtime.Object, error) { - return client.Core().Namespaces().List(options) - }, - WatchFunc: func(options metav1.ListOptions) (watch.Interface, error) { - return client.Core().Namespaces().Watch(options) - }, - }, - &apiv1.Namespace{}, - controller.NoResyncPeriodFunc(), - util.NewTriggerOnAllChanges(func(obj runtime.Object) { nc.deliverNamespaceObj(obj, 0, false) })) - - // Federated informer on namespaces in members of federation. - nc.namespaceFederatedInformer = util.NewFederatedInformer( - client, - func(cluster *federationapi.Cluster, targetClient kubeclientset.Interface) (cache.Store, cache.Controller) { - return cache.NewInformer( - &cache.ListWatch{ - ListFunc: func(options metav1.ListOptions) (runtime.Object, error) { - return targetClient.Core().Namespaces().List(options) - }, - WatchFunc: func(options metav1.ListOptions) (watch.Interface, error) { - return targetClient.Core().Namespaces().Watch(options) - }, - }, - &apiv1.Namespace{}, - controller.NoResyncPeriodFunc(), - // Trigger reconciliation whenever something in federated cluster is changed. In most cases it - // would be just confirmation that some namespace operation succeeded. - util.NewTriggerOnMetaAndSpecChanges( - func(obj runtime.Object) { nc.deliverNamespaceObj(obj, nc.namespaceReviewDelay, false) }, - )) - }, - &util.ClusterLifecycleHandlerFuncs{ - ClusterAvailable: func(cluster *federationapi.Cluster) { - // When new cluster becomes available process all the namespaces again. - nc.clusterDeliverer.DeliverAfter(allClustersKey, nil, nc.clusterAvailableDelay) - }, - }, - ) - - // Federated updater along with Create/Update/Delete operations. - nc.federatedUpdater = util.NewFederatedUpdater(nc.namespaceFederatedInformer, "namespace", nc.updateTimeout, nc.eventRecorder, - func(client kubeclientset.Interface, obj runtime.Object) error { - namespace := obj.(*apiv1.Namespace) - _, err := client.Core().Namespaces().Create(namespace) - return err - }, - func(client kubeclientset.Interface, obj runtime.Object) error { - namespace := obj.(*apiv1.Namespace) - _, err := client.Core().Namespaces().Update(namespace) - return err - }, - func(client kubeclientset.Interface, obj runtime.Object) error { - namespace := obj.(*apiv1.Namespace) - orphanDependents := false - err := client.Core().Namespaces().Delete(namespace.Name, &metav1.DeleteOptions{OrphanDependents: &orphanDependents}) - // IsNotFound error is fine since that means the object is deleted already. - if errors.IsNotFound(err) { - return nil - } - return err - }) - - nc.deletionHelper = deletionhelper.NewDeletionHelper( - nc.updateNamespace, - // objNameFunc - func(obj runtime.Object) string { - namespace := obj.(*apiv1.Namespace) - return fmt.Sprintf("%s/%s", namespace.Namespace, namespace.Name) - }, - nc.namespaceFederatedInformer, - nc.federatedUpdater, - ) - - discoverResourcesFn := nc.federatedApiClient.Discovery().ServerPreferredNamespacedResources - nc.namespacedResourcesDeleter = deletion.NewNamespacedResourcesDeleter( - client.Core().Namespaces(), dynamicClientPool, nil, - discoverResourcesFn, apiv1.FinalizerKubernetes, false) - return nc -} - -// Sends the given update object to apiserver. -// Assumes that the given object is a namespace. -func (nc *NamespaceController) updateNamespace(obj runtime.Object) (runtime.Object, error) { - namespace := obj.(*apiv1.Namespace) - return nc.federatedApiClient.Core().Namespaces().Update(namespace) -} - -// Returns true if the given object has the given finalizer in its NamespaceSpec. -func (nc *NamespaceController) hasFinalizerFuncInSpec(obj runtime.Object, finalizer apiv1.FinalizerName) bool { - namespace := obj.(*apiv1.Namespace) - for i := range namespace.Spec.Finalizers { - if namespace.Spec.Finalizers[i] == finalizer { - return true - } - } - return false -} - -// Removes the finalizer from the given objects NamespaceSpec. -func (nc *NamespaceController) removeFinalizerFromSpec(namespace *apiv1.Namespace, finalizer apiv1.FinalizerName) (*apiv1.Namespace, error) { - updatedFinalizers := []apiv1.FinalizerName{} - for i := range namespace.Spec.Finalizers { - if namespace.Spec.Finalizers[i] != finalizer { - updatedFinalizers = append(updatedFinalizers, namespace.Spec.Finalizers[i]) - } - } - namespace.Spec.Finalizers = updatedFinalizers - updatedNamespace, err := nc.federatedApiClient.Core().Namespaces().Finalize(namespace) - if err != nil { - return nil, fmt.Errorf("failed to remove finalizer %s from namespace %s: %v", string(finalizer), namespace.Name, err) - } - return updatedNamespace, nil -} - -func (nc *NamespaceController) Run(stopChan <-chan struct{}) { - go nc.namespaceInformerController.Run(stopChan) - nc.namespaceFederatedInformer.Start() - go func() { - <-stopChan - nc.namespaceFederatedInformer.Stop() - }() - nc.namespaceDeliverer.StartWithHandler(func(item *util.DelayingDelivererItem) { - namespace := item.Value.(string) - nc.reconcileNamespace(namespace) - }) - nc.clusterDeliverer.StartWithHandler(func(_ *util.DelayingDelivererItem) { - nc.reconcileNamespacesOnClusterChange() - }) - util.StartBackoffGC(nc.namespaceBackoff, stopChan) -} - -func (nc *NamespaceController) deliverNamespaceObj(obj interface{}, delay time.Duration, failed bool) { - namespace := obj.(*apiv1.Namespace) - nc.deliverNamespace(namespace.Name, delay, failed) -} - -// Adds backoff to delay if this delivery is related to some failure. Resets backoff if there was no failure. -func (nc *NamespaceController) deliverNamespace(namespace string, delay time.Duration, failed bool) { - if failed { - nc.namespaceBackoff.Next(namespace, time.Now()) - delay = delay + nc.namespaceBackoff.Get(namespace) - } else { - nc.namespaceBackoff.Reset(namespace) - } - nc.namespaceDeliverer.DeliverAfter(namespace, namespace, delay) -} - -// Check whether all data stores are in sync. False is returned if any of the informer/stores is not yet -// synced with the corresponding api server. -func (nc *NamespaceController) isSynced() bool { - if !nc.namespaceFederatedInformer.ClustersSynced() { - glog.V(2).Infof("Cluster list not synced") - return false - } - clusters, err := nc.namespaceFederatedInformer.GetReadyClusters() - if err != nil { - glog.Errorf("Failed to get ready clusters: %v", err) - return false - } - if !nc.namespaceFederatedInformer.GetTargetStore().ClustersSynced(clusters) { - return false - } - return true -} - -// The function triggers reconciliation of all federated namespaces. -func (nc *NamespaceController) reconcileNamespacesOnClusterChange() { - if !nc.isSynced() { - nc.clusterDeliverer.DeliverAfter(allClustersKey, nil, nc.clusterAvailableDelay) - } - for _, obj := range nc.namespaceInformerStore.List() { - namespace := obj.(*apiv1.Namespace) - nc.deliverNamespace(namespace.Name, nc.smallDelay, false) - } -} - -func (nc *NamespaceController) reconcileNamespace(namespace string) { - if !nc.isSynced() { - nc.deliverNamespace(namespace, nc.clusterAvailableDelay, false) - return - } - - namespaceObjFromStore, exist, err := nc.namespaceInformerStore.GetByKey(namespace) - if err != nil { - glog.Errorf("Failed to query main namespace store for %v: %v", namespace, err) - nc.deliverNamespace(namespace, 0, true) - return - } - - if !exist { - // Not federated namespace, ignoring. - return - } - // Create a copy before modifying the namespace to prevent race condition with - // other readers of namespace from store. - namespaceObj, err := api.Scheme.DeepCopy(namespaceObjFromStore) - baseNamespace, ok := namespaceObj.(*apiv1.Namespace) - if err != nil || !ok { - glog.Errorf("Error in retrieving obj from store: %v, %v", ok, err) - nc.deliverNamespace(namespace, 0, true) - return - } - if baseNamespace.DeletionTimestamp != nil { - if err := nc.delete(baseNamespace); err != nil { - glog.Errorf("Failed to delete %s: %v", namespace, err) - nc.eventRecorder.Eventf(baseNamespace, api.EventTypeWarning, "DeleteFailed", - "Namespace delete failed: %v", err) - nc.deliverNamespace(namespace, 0, true) - } - return - } - - glog.V(3).Infof("Ensuring delete object from underlying clusters finalizer for namespace: %s", - baseNamespace.Name) - // Add the required finalizers before creating a namespace in - // underlying clusters. - // This ensures that the dependent namespaces are deleted in underlying - // clusters when the federated namespace is deleted. - updatedNamespaceObj, err := nc.deletionHelper.EnsureFinalizers(baseNamespace) - if err != nil { - glog.Errorf("Failed to ensure delete object from underlying clusters finalizer in namespace %s: %v", - baseNamespace.Name, err) - nc.deliverNamespace(namespace, 0, false) - return - } - baseNamespace = updatedNamespaceObj.(*apiv1.Namespace) - - glog.V(3).Infof("Syncing namespace %s in underlying clusters", baseNamespace.Name) - // Sync the namespace in all underlying clusters. - clusters, err := nc.namespaceFederatedInformer.GetReadyClusters() - if err != nil { - glog.Errorf("Failed to get cluster list: %v", err) - nc.deliverNamespace(namespace, nc.clusterAvailableDelay, false) - return - } - - operations := make([]util.FederatedOperation, 0) - for _, cluster := range clusters { - clusterNamespaceObj, found, err := nc.namespaceFederatedInformer.GetTargetStore().GetByKey(cluster.Name, namespace) - if err != nil { - glog.Errorf("Failed to get %s from %s: %v", namespace, cluster.Name, err) - nc.deliverNamespace(namespace, 0, true) - return - } - // The object should not be modified. - desiredNamespace := &apiv1.Namespace{ - ObjectMeta: util.DeepCopyRelevantObjectMeta(baseNamespace.ObjectMeta), - Spec: *(util.DeepCopyApiTypeOrPanic(&baseNamespace.Spec).(*apiv1.NamespaceSpec)), - } - glog.V(5).Infof("Desired namespace in underlying clusters: %+v", desiredNamespace) - - if !found { - operations = append(operations, util.FederatedOperation{ - Type: util.OperationTypeAdd, - Obj: desiredNamespace, - ClusterName: cluster.Name, - Key: namespace, - }) - } else { - clusterNamespace := clusterNamespaceObj.(*apiv1.Namespace) - - // Update existing namespace, if needed. - if !util.ObjectMetaAndSpecEquivalent(desiredNamespace, clusterNamespace) { - operations = append(operations, util.FederatedOperation{ - Type: util.OperationTypeUpdate, - Obj: desiredNamespace, - ClusterName: cluster.Name, - Key: namespace, - }) - } - } - } - - if len(operations) == 0 { - // Everything is in order - return - } - glog.V(2).Infof("Updating namespace %s in underlying clusters. Operations: %d", baseNamespace.Name, len(operations)) - - err = nc.federatedUpdater.Update(operations) - if err != nil { - glog.Errorf("Failed to execute updates for %s: %v", namespace, err) - nc.deliverNamespace(namespace, 0, true) - return - } - - // Everything is in order but lets be double sure - nc.deliverNamespace(namespace, nc.namespaceReviewDelay, false) -} - -// delete deletes the given namespace or returns error if the deletion was not complete. -func (nc *NamespaceController) delete(namespace *apiv1.Namespace) error { - // Set Terminating status. - updatedNamespace := &apiv1.Namespace{ - ObjectMeta: namespace.ObjectMeta, - Spec: namespace.Spec, - Status: apiv1.NamespaceStatus{ - Phase: apiv1.NamespaceTerminating, - }, - } - var err error - if namespace.Status.Phase != apiv1.NamespaceTerminating { - glog.V(2).Infof("Marking ns %s as terminating", namespace.Name) - nc.eventRecorder.Event(namespace, api.EventTypeNormal, "DeleteNamespace", fmt.Sprintf("Marking for deletion")) - _, err = nc.federatedApiClient.Core().Namespaces().Update(updatedNamespace) - if err != nil { - return fmt.Errorf("failed to update namespace: %v", err) - } - } - - if nc.hasFinalizerFuncInSpec(updatedNamespace, apiv1.FinalizerKubernetes) { - // Delete resources in this namespace. - err = nc.namespacedResourcesDeleter.Delete(updatedNamespace.Name) - if err != nil { - return fmt.Errorf("error in deleting resources in namespace %s: %v", namespace.Name, err) - } - glog.V(2).Infof("Removed kubernetes finalizer from ns %s", namespace.Name) - // Fetch the updated Namespace. - updatedNamespace, err = nc.federatedApiClient.Core().Namespaces().Get(updatedNamespace.Name, metav1.GetOptions{}) - if err != nil { - return fmt.Errorf("error in fetching updated namespace %s: %s", updatedNamespace.Name, err) - } - } - - // Delete the namespace from all underlying clusters. - _, err = nc.deletionHelper.HandleObjectInUnderlyingClusters(updatedNamespace) - if err != nil { - return err - } - - err = nc.federatedApiClient.Core().Namespaces().Delete(namespace.Name, nil) - if err != nil { - // Its all good if the error is not found error. That means it is deleted already and we do not have to do anything. - // This is expected when we are processing an update as a result of namespace finalizer deletion. - // The process that deleted the last finalizer is also going to delete the namespace and we do not have to do anything. - if !errors.IsNotFound(err) { - return fmt.Errorf("failed to delete namespace: %v", err) - } - } - return nil -} diff --git a/federation/pkg/federation-controller/namespace/namespace_controller_test.go b/federation/pkg/federation-controller/namespace/namespace_controller_test.go deleted file mode 100644 index 461f84c65c4..00000000000 --- a/federation/pkg/federation-controller/namespace/namespace_controller_test.go +++ /dev/null @@ -1,188 +0,0 @@ -/* -Copyright 2016 The Kubernetes Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package namespace - -import ( - "fmt" - "testing" - "time" - - apiv1 "k8s.io/api/core/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/runtime" - "k8s.io/apimachinery/pkg/util/wait" - "k8s.io/client-go/dynamic" - restclient "k8s.io/client-go/rest" - core "k8s.io/client-go/testing" - federationapi "k8s.io/kubernetes/federation/apis/federation/v1beta1" - fakefedclientset "k8s.io/kubernetes/federation/client/clientset_generated/federation_clientset/fake" - "k8s.io/kubernetes/federation/pkg/federation-controller/util" - "k8s.io/kubernetes/federation/pkg/federation-controller/util/deletionhelper" - . "k8s.io/kubernetes/federation/pkg/federation-controller/util/test" - kubeclientset "k8s.io/kubernetes/pkg/client/clientset_generated/clientset" - fakekubeclientset "k8s.io/kubernetes/pkg/client/clientset_generated/clientset/fake" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" -) - -const ( - namespaces string = "namespaces" - clusters string = "clusters" -) - -func TestNamespaceController(t *testing.T) { - cluster1 := NewCluster("cluster1", apiv1.ConditionTrue) - cluster2 := NewCluster("cluster2", apiv1.ConditionTrue) - ns1 := apiv1.Namespace{ - ObjectMeta: metav1.ObjectMeta{ - Name: "test-namespace", - SelfLink: "/api/v1/namespaces/test-namespace", - }, - Spec: apiv1.NamespaceSpec{ - Finalizers: []apiv1.FinalizerName{apiv1.FinalizerKubernetes}, - }, - } - - fakeClient := &fakefedclientset.Clientset{} - RegisterFakeList(clusters, &fakeClient.Fake, &federationapi.ClusterList{Items: []federationapi.Cluster{*cluster1}}) - RegisterFakeList(namespaces, &fakeClient.Fake, &apiv1.NamespaceList{Items: []apiv1.Namespace{}}) - namespaceWatch := RegisterFakeWatch(namespaces, &fakeClient.Fake) - namespaceUpdateChan := RegisterFakeCopyOnUpdate(namespaces, &fakeClient.Fake, namespaceWatch) - clusterWatch := RegisterFakeWatch(clusters, &fakeClient.Fake) - - cluster1Client := &fakekubeclientset.Clientset{} - cluster1Watch := RegisterFakeWatch(namespaces, &cluster1Client.Fake) - RegisterFakeList(namespaces, &cluster1Client.Fake, &apiv1.NamespaceList{Items: []apiv1.Namespace{}}) - cluster1CreateChan := RegisterFakeCopyOnCreate(namespaces, &cluster1Client.Fake, cluster1Watch) - cluster1UpdateChan := RegisterFakeCopyOnUpdate(namespaces, &cluster1Client.Fake, cluster1Watch) - - cluster2Client := &fakekubeclientset.Clientset{} - cluster2Watch := RegisterFakeWatch(namespaces, &cluster2Client.Fake) - RegisterFakeList(namespaces, &cluster2Client.Fake, &apiv1.NamespaceList{Items: []apiv1.Namespace{}}) - cluster2CreateChan := RegisterFakeCopyOnCreate(namespaces, &cluster2Client.Fake, cluster2Watch) - - nsDeleteChan := RegisterDelete(&fakeClient.Fake, namespaces) - namespaceController := NewNamespaceController(fakeClient, dynamic.NewDynamicClientPool(&restclient.Config{})) - informerClientFactory := func(cluster *federationapi.Cluster) (kubeclientset.Interface, error) { - switch cluster.Name { - case cluster1.Name: - return cluster1Client, nil - case cluster2.Name: - return cluster2Client, nil - default: - return nil, fmt.Errorf("Unknown cluster") - } - } - setClientFactory(namespaceController.namespaceFederatedInformer, informerClientFactory) - namespaceController.clusterAvailableDelay = time.Second - namespaceController.namespaceReviewDelay = 50 * time.Millisecond - namespaceController.smallDelay = 20 * time.Millisecond - namespaceController.updateTimeout = 5 * time.Second - - stop := make(chan struct{}) - namespaceController.Run(stop) - - // Test add federated namespace. - namespaceWatch.Add(&ns1) - // Verify that the DeleteFromUnderlyingClusters finalizer is added to the namespace. - updatedNamespace := GetNamespaceFromChan(namespaceUpdateChan) - require.NotNil(t, updatedNamespace) - AssertHasFinalizer(t, updatedNamespace, deletionhelper.FinalizerDeleteFromUnderlyingClusters) - ns1 = *updatedNamespace - - // Verify that the namespace is created in underlying cluster1. - createdNamespace := GetNamespaceFromChan(cluster1CreateChan) - require.NotNil(t, createdNamespace) - assert.Equal(t, ns1.Name, createdNamespace.Name) - - // Wait for the namespace to appear in the informer store - err := WaitForStoreUpdate( - namespaceController.namespaceFederatedInformer.GetTargetStore(), - cluster1.Name, ns1.Name, wait.ForeverTestTimeout) - assert.Nil(t, err, "namespace should have appeared in the informer store") - - // Test update federated namespace. - ns1.Annotations = map[string]string{ - "A": "B", - } - namespaceWatch.Modify(&ns1) - assert.NoError(t, CheckObjectFromChan(cluster1UpdateChan, MetaAndSpecCheckingFunction(&ns1))) - - // Test add cluster - clusterWatch.Add(cluster2) - createdNamespace2 := GetNamespaceFromChan(cluster2CreateChan) - require.NotNil(t, createdNamespace2) - assert.Equal(t, ns1.Name, createdNamespace2.Name) - assert.Contains(t, createdNamespace2.Annotations, "A") - - // Delete the namespace with orphan finalizer (let namespaces - // in underlying clusters be as is). - // TODO: Add a test without orphan finalizer. - ns1.ObjectMeta.Finalizers = append(ns1.ObjectMeta.Finalizers, metav1.FinalizerOrphanDependents) - ns1.DeletionTimestamp = &metav1.Time{Time: time.Now()} - namespaceWatch.Modify(&ns1) - assert.Equal(t, ns1.Name, GetStringFromChan(nsDeleteChan)) - // TODO: Add a test for verifying that resources in the namespace are deleted - // when the namespace is deleted. - // Need a fake dynamic client to mock list and delete actions to be able to test this. - // TODO: Add a fake dynamic client and test this. - // In the meantime, e2e test verify that the resources in a namespace are - // deleted when the namespace is deleted. - close(stop) -} - -func setClientFactory(informer util.FederatedInformer, informerClientFactory func(*federationapi.Cluster) (kubeclientset.Interface, error)) { - testInformer := ToFederatedInformerForTestOnly(informer) - testInformer.SetClientFactory(informerClientFactory) -} - -func RegisterDeleteCollection(client *core.Fake, resource string) chan string { - deleteChan := make(chan string, 100) - client.AddReactor("delete-collection", resource, func(action core.Action) (bool, runtime.Object, error) { - deleteChan <- "all" - return true, nil, nil - }) - return deleteChan -} - -func RegisterDelete(client *core.Fake, resource string) chan string { - deleteChan := make(chan string, 100) - client.AddReactor("delete", resource, func(action core.Action) (bool, runtime.Object, error) { - deleteAction := action.(core.DeleteAction) - deleteChan <- deleteAction.GetName() - return true, nil, nil - }) - return deleteChan -} - -func GetStringFromChan(c chan string) string { - select { - case str := <-c: - return str - case <-time.After(5 * time.Second): - return "timedout" - } -} - -func GetNamespaceFromChan(c chan runtime.Object) *apiv1.Namespace { - if namespace := GetObjectFromChan(c); namespace == nil { - return nil - } else { - return namespace.(*apiv1.Namespace) - } -} diff --git a/federation/pkg/federation-controller/sync/controller.go b/federation/pkg/federation-controller/sync/controller.go index 9b7b3ca66b8..f5a8331bfb5 100644 --- a/federation/pkg/federation-controller/sync/controller.go +++ b/federation/pkg/federation-controller/sync/controller.go @@ -416,6 +416,17 @@ func (s *FederationSyncController) objFromCache(kind, key string) (pkgruntime.Ob // delete deletes the given resource or returns error if the deletion was not complete. func (s *FederationSyncController) delete(obj pkgruntime.Object, kind string, qualifiedName federatedtypes.QualifiedName) error { glog.V(3).Infof("Handling deletion of %s %q", kind, qualifiedName) + + // Perform pre-deletion cleanup for the namespace adapter + namespaceAdapter, ok := s.adapter.(*federatedtypes.NamespaceAdapter) + if ok { + var err error + obj, err = namespaceAdapter.CleanUpNamespace(obj, s.eventRecorder) + if err != nil { + return err + } + } + _, err := s.deletionHelper.HandleObjectInUnderlyingClusters(obj) if err != nil { return err diff --git a/test/e2e_federation/namespace.go b/test/e2e_federation/namespace.go index 01a17bc6639..0a98185193c 100644 --- a/test/e2e_federation/namespace.go +++ b/test/e2e_federation/namespace.go @@ -66,39 +66,6 @@ var _ = framework.KubeDescribe("Federation namespace [Feature:Federation]", func } }) - It("should be created and deleted successfully", func() { - fedframework.SkipUnlessFederated(f.ClientSet) - - nsName = createNamespace(f.FederationClientset.Core().Namespaces()) - - By(fmt.Sprintf("Deleting namespace %s", nsName)) - deleteNamespace(nil, nsName, - f.FederationClientset.Core().Namespaces().Get, - f.FederationClientset.Core().Namespaces().Delete) - By(fmt.Sprintf("Verified that deletion succeeded")) - }) - - It("should be deleted from underlying clusters when OrphanDependents is false", func() { - fedframework.SkipUnlessFederated(f.ClientSet) - orphanDependents := false - nsName = verifyNsCascadingDeletion(f.FederationClientset.Core().Namespaces(), clusters, &orphanDependents) - By(fmt.Sprintf("Verified that namespaces were deleted from underlying clusters")) - }) - - It("should not be deleted from underlying clusters when OrphanDependents is true", func() { - fedframework.SkipUnlessFederated(f.ClientSet) - orphanDependents := true - nsName = verifyNsCascadingDeletion(f.FederationClientset.Core().Namespaces(), clusters, &orphanDependents) - By(fmt.Sprintf("Verified that namespaces were not deleted from underlying clusters")) - }) - - It("should not be deleted from underlying clusters when OrphanDependents is nil", func() { - fedframework.SkipUnlessFederated(f.ClientSet) - - nsName = verifyNsCascadingDeletion(f.FederationClientset.Core().Namespaces(), clusters, nil) - By(fmt.Sprintf("Verified that namespaces were not deleted from underlying clusters")) - }) - // See https://github.com/kubernetes/kubernetes/issues/38225 It("deletes replicasets in the namespace when the namespace is deleted", func() { fedframework.SkipUnlessFederated(f.ClientSet) diff --git a/test/test_owners.csv b/test/test_owners.csv index 674a8dac03f..09be7003a12 100644 --- a/test/test_owners.csv +++ b/test/test_owners.csv @@ -599,7 +599,6 @@ k8s.io/kubernetes/federation/pkg/dnsprovider/providers/google/clouddns,madhusuda k8s.io/kubernetes/federation/pkg/federation-controller/cluster,nikhiljindal,0, k8s.io/kubernetes/federation/pkg/federation-controller/deployment,zmerlynn,1, k8s.io/kubernetes/federation/pkg/federation-controller/ingress,vishh,1, -k8s.io/kubernetes/federation/pkg/federation-controller/namespace,rrati,0, k8s.io/kubernetes/federation/pkg/federation-controller/replicaset,roberthbailey,1, k8s.io/kubernetes/federation/pkg/federation-controller/secret,apelisse,1, k8s.io/kubernetes/federation/pkg/federation-controller/service,pmorie,1,