Skip to content
This repository was archived by the owner on Apr 25, 2023. It is now read-only.

Commit 9ec6713

Browse files
authored
Merge pull request #1499 from jonathanbeber/fix-deletion-non-ready
fix: ignore non-targeted clusters during deletion
2 parents f5d5f20 + d19b80c commit 9ec6713

File tree

11 files changed

+349
-53
lines changed

11 files changed

+349
-53
lines changed

pkg/controller/sync/controller.go

Lines changed: 52 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -268,7 +268,19 @@ func (s *KubeFedSyncController) reconcile(qualifiedName util.QualifiedName) util
268268
apiResource := s.typeConfig.GetTargetType()
269269
gvk := apiResourceToGVK(&apiResource)
270270
klog.V(2).Infof("Ensuring the removal of the label %q from %s %q in member clusters.", util.ManagedByKubeFedLabelKey, gvk.Kind, qualifiedName)
271-
err = s.removeManagedLabel(gvk, qualifiedName)
271+
// We can't compute resource placement, therefore we try to
272+
// remove it from all member clusters.
273+
clusters, err := s.informer.GetClusters()
274+
if err != nil {
275+
wrappedErr := errors.Wrap(err, "failed to get member clusters")
276+
runtime.HandleError(wrappedErr)
277+
return util.StatusError
278+
}
279+
clusterNames := sets.NewString()
280+
for _, cluster := range clusters {
281+
clusterNames = clusterNames.Insert(cluster.Name)
282+
}
283+
err = s.removeManagedLabel(gvk, qualifiedName, clusterNames)
272284
if err != nil {
273285
wrappedErr := errors.Wrapf(err, "failed to remove the label %q from %s %q in member clusters", util.ManagedByKubeFedLabelKey, gvk.Kind, qualifiedName)
274286
runtime.HandleError(wrappedErr)
@@ -501,7 +513,19 @@ func (s *KubeFedSyncController) ensureDeletion(fedResource FederatedResource) ut
501513
return util.StatusError
502514
}
503515
klog.V(2).Infof("Initiating the removal of the label %q from resources previously managed by %s %q.", util.ManagedByKubeFedLabelKey, kind, key)
504-
err = s.removeManagedLabel(fedResource.TargetGVK(), fedResource.TargetName())
516+
clusters, err := s.informer.GetClusters()
517+
if err != nil {
518+
wrappedErr := errors.Wrap(err, "failed to get member clusters")
519+
runtime.HandleError(wrappedErr)
520+
return util.StatusError
521+
}
522+
targetClusters, err := fedResource.ComputePlacement(clusters)
523+
if err != nil {
524+
wrappedErr := errors.Wrapf(err, "failed to compute placement for %s %q", kind, key)
525+
runtime.HandleError(wrappedErr)
526+
return util.StatusError
527+
}
528+
err = s.removeManagedLabel(fedResource.TargetGVK(), fedResource.TargetName(), targetClusters)
505529
if err != nil {
506530
wrappedErr := errors.Wrapf(err, "failed to remove the label %q from all resources previously managed by %s %q", util.ManagedByKubeFedLabelKey, kind, key)
507531
runtime.HandleError(wrappedErr)
@@ -533,8 +557,8 @@ func (s *KubeFedSyncController) ensureDeletion(fedResource FederatedResource) ut
533557

534558
// removeManagedLabel attempts to remove the managed label from
535559
// resources with the given name in member clusters.
536-
func (s *KubeFedSyncController) removeManagedLabel(gvk schema.GroupVersionKind, qualifiedName util.QualifiedName) error {
537-
ok, err := s.handleDeletionInClusters(gvk, qualifiedName, func(dispatcher dispatch.UnmanagedDispatcher, clusterName string, clusterObj *unstructured.Unstructured) {
560+
func (s *KubeFedSyncController) removeManagedLabel(gvk schema.GroupVersionKind, qualifiedName util.QualifiedName, clusters sets.String) error {
561+
ok, err := s.handleDeletionInClusters(gvk, qualifiedName, clusters, func(dispatcher dispatch.UnmanagedDispatcher, clusterName string, clusterObj *unstructured.Unstructured) {
538562
if clusterObj.GetDeletionTimestamp() != nil {
539563
return
540564
}
@@ -554,8 +578,17 @@ func (s *KubeFedSyncController) deleteFromClusters(fedResource FederatedResource
554578
gvk := fedResource.TargetGVK()
555579
qualifiedName := fedResource.TargetName()
556580

581+
clusters, err := s.informer.GetClusters()
582+
if err != nil {
583+
return false, err
584+
}
585+
targetClusters, err := fedResource.ComputePlacement(clusters)
586+
if err != nil {
587+
return false, err
588+
}
589+
557590
remainingClusters := []string{}
558-
ok, err := s.handleDeletionInClusters(gvk, qualifiedName, func(dispatcher dispatch.UnmanagedDispatcher, clusterName string, clusterObj *unstructured.Unstructured) {
591+
ok, err := s.handleDeletionInClusters(gvk, qualifiedName, targetClusters, func(dispatcher dispatch.UnmanagedDispatcher, clusterName string, clusterObj *unstructured.Unstructured) {
559592
// If the containing namespace of a FederatedNamespace is
560593
// marked for deletion, it is impossible to require the
561594
// removal of the namespace in advance of removal of the sync
@@ -615,9 +648,17 @@ func (s *KubeFedSyncController) ensureRemovedOrUnmanaged(fedResource FederatedRe
615648
return errors.Wrap(err, "failed to get a list of clusters")
616649
}
617650

651+
targetClusters, err := fedResource.ComputePlacement(clusters)
652+
if err != nil {
653+
return errors.Wrapf(err, "failed to compute placement for %s %q", fedResource.FederatedKind(), fedResource.FederatedName().Name)
654+
}
655+
618656
dispatcher := dispatch.NewCheckUnmanagedDispatcher(s.informer.GetClientForCluster, fedResource.TargetGVK(), fedResource.TargetName())
619657
unreadyClusters := []string{}
620658
for _, cluster := range clusters {
659+
if !targetClusters.Has(cluster.Name) {
660+
continue
661+
}
621662
if !util.IsClusterReady(&cluster.Status) {
622663
unreadyClusters = append(unreadyClusters, cluster.Name)
623664
continue
@@ -639,18 +680,21 @@ func (s *KubeFedSyncController) ensureRemovedOrUnmanaged(fedResource FederatedRe
639680

640681
// handleDeletionInClusters invokes the provided deletion handler for
641682
// each managed resource in member clusters.
642-
func (s *KubeFedSyncController) handleDeletionInClusters(gvk schema.GroupVersionKind, qualifiedName util.QualifiedName,
683+
func (s *KubeFedSyncController) handleDeletionInClusters(gvk schema.GroupVersionKind, qualifiedName util.QualifiedName, clusters sets.String,
643684
deletionFunc func(dispatcher dispatch.UnmanagedDispatcher, clusterName string, clusterObj *unstructured.Unstructured)) (bool, error) {
644-
clusters, err := s.informer.GetClusters()
685+
memberClusters, err := s.informer.GetClusters()
645686
if err != nil {
646687
return false, errors.Wrap(err, "failed to get a list of clusters")
647688
}
648689

649690
dispatcher := dispatch.NewUnmanagedDispatcher(s.informer.GetClientForCluster, gvk, qualifiedName)
650691
retrievalFailureClusters := []string{}
651692
unreadyClusters := []string{}
652-
for _, cluster := range clusters {
693+
for _, cluster := range memberClusters {
653694
clusterName := cluster.Name
695+
if !clusters.Has(clusterName) {
696+
continue
697+
}
654698

655699
if !util.IsClusterReady(&cluster.Status) {
656700
unreadyClusters = append(unreadyClusters, clusterName)

scripts/pre-commit.sh

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,19 @@ function run-e2e-tests-with-in-memory-controllers() {
105105
${IN_MEMORY_E2E_TEST_CMD}
106106
}
107107

108+
function run-e2e-tests-with-not-ready-clusters() {
109+
# Run the tests without any verbosity. The unhealthy nodes generate
110+
# too much logs.
111+
go test -timeout 900s ./test/e2e \
112+
-args -kubeconfig=${HOME}/.kube/config \
113+
-single-call-timeout=2m \
114+
-ginkgo.randomizeAllSpecs \
115+
-limited-scope=true \
116+
-in-memory-controllers=true \
117+
-simulate-federation=true \
118+
-ginkgo.focus='\[NOT_READY\]'
119+
}
120+
108121
function run-namespaced-e2e-tests() {
109122
local namespaced_e2e_test_cmd="${E2E_TEST_CMD} -kubefed-namespace=foo -limited-scope=true"
110123
# Run the placement test separately to avoid crud failures if
@@ -200,6 +213,9 @@ kubectl scale deployments kubefed-controller-manager -n kube-federation-system -
200213
echo "Running e2e tests with race detector against cluster-scoped kubefed with in-memory controllers"
201214
run-e2e-tests-with-in-memory-controllers
202215

216+
echo "Running e2e tests with not-ready clusters"
217+
run-e2e-tests-with-not-ready-clusters
218+
203219
# FederatedTypeConfig controller is needed to remove finalizers from
204220
# FederatedTypeConfigs in order to successfully delete the KubeFed
205221
# control plane in the next step.

test/common/crudtester.go

Lines changed: 51 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ import (
4141
"sigs.k8s.io/kubefed/pkg/apis/core/common"
4242
"sigs.k8s.io/kubefed/pkg/apis/core/typeconfig"
4343
fedv1a1 "sigs.k8s.io/kubefed/pkg/apis/core/v1alpha1"
44+
"sigs.k8s.io/kubefed/pkg/apis/core/v1beta1"
4445
genericclient "sigs.k8s.io/kubefed/pkg/client/generic"
4546
"sigs.k8s.io/kubefed/pkg/controller/sync"
4647
"sigs.k8s.io/kubefed/pkg/controller/sync/status"
@@ -65,6 +66,7 @@ type FederatedTypeCrudTester struct {
6566
// operation that involves member clusters may take longer due to
6667
// propagation latency.
6768
clusterWaitTimeout time.Duration
69+
clustersNamespace string
6870
}
6971

7072
type TestClusterConfig struct {
@@ -77,7 +79,7 @@ type TestCluster struct {
7779
Client util.ResourceClient
7880
}
7981

80-
func NewFederatedTypeCrudTester(testLogger TestLogger, typeConfig typeconfig.Interface, kubeConfig *rest.Config, testClusters map[string]TestCluster, waitInterval, clusterWaitTimeout time.Duration) (*FederatedTypeCrudTester, error) {
82+
func NewFederatedTypeCrudTester(testLogger TestLogger, typeConfig typeconfig.Interface, kubeConfig *rest.Config, testClusters map[string]TestCluster, clustersNamespace string, waitInterval, clusterWaitTimeout time.Duration) (*FederatedTypeCrudTester, error) {
8183
return &FederatedTypeCrudTester{
8284
tl: testLogger,
8385
typeConfig: typeConfig,
@@ -87,11 +89,12 @@ func NewFederatedTypeCrudTester(testLogger TestLogger, typeConfig typeconfig.Int
8789
testClusters: testClusters,
8890
waitInterval: waitInterval,
8991
clusterWaitTimeout: clusterWaitTimeout,
92+
clustersNamespace: clustersNamespace,
9093
}, nil
9194
}
9295

93-
func (c *FederatedTypeCrudTester) CheckLifecycle(targetObject *unstructured.Unstructured, overrides []interface{}) {
94-
fedObject := c.CheckCreate(targetObject, overrides)
96+
func (c *FederatedTypeCrudTester) CheckLifecycle(targetObject *unstructured.Unstructured, overrides []interface{}, selectors map[string]string) {
97+
fedObject := c.CheckCreate(targetObject, overrides, selectors)
9598

9699
c.CheckStatusCreated(util.NewQualifiedName(fedObject))
97100

@@ -104,7 +107,7 @@ func (c *FederatedTypeCrudTester) CheckLifecycle(targetObject *unstructured.Unst
104107
c.CheckDelete(fedObject, false)
105108
}
106109

107-
func (c *FederatedTypeCrudTester) Create(targetObject *unstructured.Unstructured, overrides []interface{}) *unstructured.Unstructured {
110+
func (c *FederatedTypeCrudTester) Create(targetObject *unstructured.Unstructured, overrides []interface{}, selectors map[string]string) *unstructured.Unstructured {
108111
qualifiedName := util.NewQualifiedName(targetObject)
109112
kind := c.typeConfig.GetTargetType().Kind
110113
fedKind := c.typeConfig.GetFederatedType().Kind
@@ -113,10 +116,7 @@ func (c *FederatedTypeCrudTester) Create(targetObject *unstructured.Unstructured
113116
c.tl.Fatalf("Error obtaining %s from %s %q: %v", fedKind, kind, qualifiedName, err)
114117
}
115118

116-
fedObject, err = c.setAdditionalTestData(fedObject, overrides, targetObject.GetGenerateName())
117-
if err != nil {
118-
c.tl.Fatalf("Error setting overrides and placement on %s %q: %v", fedKind, qualifiedName, err)
119-
}
119+
fedObject = c.setAdditionalTestData(fedObject, overrides, selectors, targetObject.GetGenerateName())
120120

121121
return c.createResource(c.typeConfig.GetFederatedType(), fedObject)
122122
}
@@ -141,15 +141,15 @@ func (c *FederatedTypeCrudTester) resourceClient(apiResource metav1.APIResource)
141141
return client
142142
}
143143

144-
func (c *FederatedTypeCrudTester) CheckCreate(targetObject *unstructured.Unstructured, overrides []interface{}) *unstructured.Unstructured {
145-
fedObject := c.Create(targetObject, overrides)
144+
func (c *FederatedTypeCrudTester) CheckCreate(targetObject *unstructured.Unstructured, overrides []interface{}, selectors map[string]string) *unstructured.Unstructured {
145+
fedObject := c.Create(targetObject, overrides, selectors)
146146

147147
c.CheckPropagation(fedObject)
148148
return fedObject
149149
}
150150

151151
// AdditionalTestData additionally sets fixture overrides and placement clusternames into federated object
152-
func (c *FederatedTypeCrudTester) setAdditionalTestData(fedObject *unstructured.Unstructured, overrides []interface{}, generateName string) (*unstructured.Unstructured, error) {
152+
func (c *FederatedTypeCrudTester) setAdditionalTestData(fedObject *unstructured.Unstructured, overrides []interface{}, selectors map[string]string, generateName string) *unstructured.Unstructured {
153153
fedKind := c.typeConfig.GetFederatedType().Kind
154154
qualifiedName := util.NewQualifiedName(fedObject)
155155

@@ -159,17 +159,23 @@ func (c *FederatedTypeCrudTester) setAdditionalTestData(fedObject *unstructured.
159159
c.tl.Fatalf("Error updating overrides in %s %q: %v", fedKind, qualifiedName, err)
160160
}
161161
}
162-
clusterNames := []string{}
163-
for name := range c.testClusters {
164-
clusterNames = append(clusterNames, name)
165-
}
166-
err := util.SetClusterNames(fedObject, clusterNames)
167-
if err != nil {
168-
c.tl.Fatalf("Error setting cluster names in %s %q: %v", fedKind, qualifiedName, err)
162+
if selectors != nil {
163+
if err := util.SetClusterSelector(fedObject, selectors); err != nil {
164+
c.tl.Fatalf("Error setting cluster selectors for %s/%s: %v", fedObject.GetKind(), fedObject.GetName(), err)
165+
}
166+
} else {
167+
clusterNames := []string{}
168+
for name := range c.testClusters {
169+
clusterNames = append(clusterNames, name)
170+
}
171+
err := util.SetClusterNames(fedObject, clusterNames)
172+
if err != nil {
173+
c.tl.Fatalf("Error setting cluster names in %s %q: %v", fedKind, qualifiedName, err)
174+
}
169175
}
170176
fedObject.SetGenerateName(generateName)
171177

172-
return fedObject, err
178+
return fedObject
173179
}
174180

175181
func (c *FederatedTypeCrudTester) CheckUpdate(fedObject *unstructured.Unstructured) {
@@ -334,7 +340,14 @@ func (c *FederatedTypeCrudTester) CheckDelete(fedObject *unstructured.Unstructur
334340
if deletingInCluster {
335341
stateMsg = "not present"
336342
}
343+
clusters, err := util.ComputePlacement(fedObject, c.getClusters(), false)
344+
if err != nil {
345+
c.tl.Fatalf("Couldn't retrieve clusters for %s/%s: %v", federatedKind, name, err)
346+
}
337347
for clusterName, testCluster := range c.testClusters {
348+
if !clusters.Has(clusterName) {
349+
continue
350+
}
338351
namespace = util.QualifiedNameForCluster(clusterName, qualifiedName).Namespace
339352
err = wait.PollImmediate(c.waitInterval, waitTimeout, func() (bool, error) {
340353
obj, err := testCluster.Client.Resources(namespace).Get(context.Background(), name, metav1.GetOptions{})
@@ -425,16 +438,33 @@ func (c *FederatedTypeCrudTester) CheckReplicaSet(fedObject *unstructured.Unstru
425438
}
426439
}
427440

441+
func (c *FederatedTypeCrudTester) getClusters() []*v1beta1.KubeFedCluster {
442+
client, err := genericclient.New(c.kubeConfig)
443+
if err != nil {
444+
c.tl.Fatalf("Failed to get kubefed clientset: %v", err)
445+
}
446+
447+
fedClusters := []*v1beta1.KubeFedCluster{}
448+
for cluster := range c.testClusters {
449+
clusterResource := &v1beta1.KubeFedCluster{}
450+
err = client.Get(context.Background(), clusterResource, c.clustersNamespace, cluster)
451+
if err != nil {
452+
c.tl.Fatalf("Cannot get cluster %s: %v", cluster, err)
453+
}
454+
fedClusters = append(fedClusters, clusterResource)
455+
}
456+
return fedClusters
457+
}
458+
428459
// CheckPropagation checks propagation for the crud tester's clients
429460
func (c *FederatedTypeCrudTester) CheckPropagation(fedObject *unstructured.Unstructured) {
430461
federatedKind := c.typeConfig.GetFederatedType().Kind
431462
qualifiedName := util.NewQualifiedName(fedObject)
432463

433-
clusterNames, err := util.GetClusterNames(fedObject)
464+
selectedClusters, err := util.ComputePlacement(fedObject, c.getClusters(), false)
434465
if err != nil {
435466
c.tl.Fatalf("Error retrieving cluster names for %s %q: %v", federatedKind, qualifiedName, err)
436467
}
437-
selectedClusters := sets.NewString(clusterNames...)
438468

439469
templateVersion, err := sync.GetTemplateHash(fedObject.Object)
440470
if err != nil {

test/e2e/crd.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -215,10 +215,10 @@ overrides:
215215
return targetObj, overrides, nil
216216
}
217217

218-
crudTester, targetObject, overrides := initCrudTest(f, tl, typeConfig, testObjectsFunc)
218+
crudTester, targetObject, overrides := initCrudTest(f, tl, f.KubeFedSystemNamespace(), typeConfig, testObjectsFunc)
219219
// Make a copy for use in the orphan check.
220220
deletionTargetObject := targetObject.DeepCopy()
221-
crudTester.CheckLifecycle(targetObject, overrides)
221+
crudTester.CheckLifecycle(targetObject, overrides, nil)
222222

223223
if namespaced {
224224
// This check should not fail so long as the main test loop
@@ -228,7 +228,7 @@ overrides:
228228
tl.Fatalf("Test of orphaned deletion assumes deletion of the containing namespace")
229229
}
230230
// Perform a check of orphan deletion.
231-
fedObject := crudTester.CheckCreate(deletionTargetObject, nil)
231+
fedObject := crudTester.CheckCreate(deletionTargetObject, nil, nil)
232232
orphanDeletion := true
233233
crudTester.CheckDelete(fedObject, orphanDeletion)
234234
}

0 commit comments

Comments
 (0)