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

Commit d19b80c

Browse files
committed
Add not-ready clusters E2E test
The e2e tests do not consider any unhealthy cluster. This commit adds a test case where the cluster federates itself twice and makes one the virtual federation not-ready by changing the API endpoint to an invalid address. The ready cluster is labelled and a complete CRUD test guarantees that the not-ready cluster does not impact operations if the the cluster is not targeted.
1 parent 3157e45 commit d19b80c

File tree

10 files changed

+297
-45
lines changed

10 files changed

+297
-45
lines changed

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
}

test/e2e/crud.go

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -61,8 +61,8 @@ var _ = Describe("Federated", func() {
6161
Describe(fmt.Sprintf("%q", typeConfigName), func() {
6262
It("should be created, read, updated and deleted successfully", func() {
6363
typeConfig, testObjectsFunc := getCrudTestInput(f, tl, typeConfigName, fixture)
64-
crudTester, targetObject, overrides := initCrudTest(f, tl, typeConfig, testObjectsFunc)
65-
crudTester.CheckLifecycle(targetObject, overrides)
64+
crudTester, targetObject, overrides := initCrudTest(f, tl, f.KubeFedSystemNamespace(), typeConfig, testObjectsFunc)
65+
crudTester.CheckLifecycle(targetObject, overrides, nil)
6666
})
6767

6868
for _, remoteStatusTypeName := range containedTypeNames {
@@ -78,8 +78,8 @@ var _ = Describe("Federated", func() {
7878
tl.Logf("Show the content of the kubefedconfig file: '%v'", kubeFedConfig)
7979

8080
typeConfig, testObjectsFunc := getCrudTestInput(f, tl, typeConfigName, fixture)
81-
crudTester, targetObject, overrides := initCrudTest(f, tl, typeConfig, testObjectsFunc)
82-
fedObject := crudTester.CheckCreate(targetObject, overrides)
81+
crudTester, targetObject, overrides := initCrudTest(f, tl, f.KubeFedSystemNamespace(), typeConfig, testObjectsFunc)
82+
fedObject := crudTester.CheckCreate(targetObject, overrides, nil)
8383

8484
By("Checking the remote status filled for each federated resource for every cluster")
8585
tl.Logf("Checking the existence of a remote status for each fedObj in every cluster: %v", fedObject)
@@ -105,12 +105,12 @@ var _ = Describe("Federated", func() {
105105

106106
typeConfig, testObjectsFunc := getCrudTestInput(f, tl, typeConfigName, fixture)
107107
// Initialize the test without creating a federated namespace.
108-
crudTester, targetObject, overrides := initCrudTestWithPropagation(f, tl, typeConfig, testObjectsFunc, false)
108+
crudTester, targetObject, overrides := initCrudTestWithPropagation(f, tl, f.KubeFedSystemNamespace(), typeConfig, testObjectsFunc, false)
109109

110110
kind := typeConfig.GetFederatedType().Kind
111111

112112
By(fmt.Sprintf("Creating a %s whose containing namespace is not federated", kind))
113-
fedObject := crudTester.Create(targetObject, overrides)
113+
fedObject := crudTester.Create(targetObject, overrides, nil)
114114

115115
qualifiedName := util.NewQualifiedName(fedObject)
116116

@@ -143,7 +143,7 @@ var _ = Describe("Federated", func() {
143143

144144
It("should have the managed label removed if not managed", func() {
145145
typeConfig, testObjectsFunc := getCrudTestInput(f, tl, typeConfigName, fixture)
146-
crudTester, targetObject, _ := initCrudTest(f, tl, typeConfig, testObjectsFunc)
146+
crudTester, targetObject, _ := initCrudTest(f, tl, f.KubeFedSystemNamespace(), typeConfig, testObjectsFunc)
147147

148148
testClusters := crudTester.TestClusters()
149149

@@ -192,7 +192,7 @@ var _ = Describe("Federated", func() {
192192

193193
It("should not be deleted if unlabeled", func() {
194194
typeConfig, testObjectsFunc := getCrudTestInput(f, tl, typeConfigName, fixture)
195-
crudTester, targetObject, _ := initCrudTest(f, tl, typeConfig, testObjectsFunc)
195+
crudTester, targetObject, _ := initCrudTest(f, tl, f.KubeFedSystemNamespace(), typeConfig, testObjectsFunc)
196196

197197
testClusters := crudTester.TestClusters()
198198

@@ -315,13 +315,13 @@ func getCrudTestInput(f framework.KubeFedFramework, tl common.TestLogger,
315315
return typeConfig, testObjectsFunc
316316
}
317317

318-
func initCrudTest(f framework.KubeFedFramework, tl common.TestLogger,
318+
func initCrudTest(f framework.KubeFedFramework, tl common.TestLogger, clustersNamespace string,
319319
typeConfig typeconfig.Interface, testObjectsFunc testObjectsAccessor) (
320320
*common.FederatedTypeCrudTester, *unstructured.Unstructured, []interface{}) {
321-
return initCrudTestWithPropagation(f, tl, typeConfig, testObjectsFunc, true)
321+
return initCrudTestWithPropagation(f, tl, clustersNamespace, typeConfig, testObjectsFunc, true)
322322
}
323323

324-
func initCrudTestWithPropagation(f framework.KubeFedFramework, tl common.TestLogger,
324+
func initCrudTestWithPropagation(f framework.KubeFedFramework, tl common.TestLogger, clustersNamespace string,
325325
typeConfig typeconfig.Interface, testObjectsFunc testObjectsAccessor,
326326
ensureNamespacePropagation bool) (
327327
*common.FederatedTypeCrudTester, *unstructured.Unstructured, []interface{}) {
@@ -343,7 +343,7 @@ func initCrudTestWithPropagation(f framework.KubeFedFramework, tl common.TestLog
343343
targetAPIResource := typeConfig.GetTargetType()
344344

345345
testClusters := f.ClusterDynamicClients(&targetAPIResource, userAgent)
346-
crudTester, err := common.NewFederatedTypeCrudTester(tl, typeConfig, kubeConfig, testClusters, framework.PollInterval, framework.TestContext.SingleCallTimeout)
346+
crudTester, err := common.NewFederatedTypeCrudTester(tl, typeConfig, kubeConfig, testClusters, clustersNamespace, framework.PollInterval, framework.TestContext.SingleCallTimeout)
347347
if err != nil {
348348
tl.Fatalf("Error creating crudtester for %q: %v", federatedKind, err)
349349
}

test/e2e/deleteoptions.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,8 +39,8 @@ var _ = Describe("DeleteOptions", func() {
3939
It("Deployment should be created and deleted successfully, but ReplicaSet that created by Deployment won't be deleted", func() {
4040

4141
typeConfig, testObjectsFunc := getCrudTestInput(f, tl, typeConfigName, fixture)
42-
crudTester, targetObject, overrides := initCrudTest(f, tl, typeConfig, testObjectsFunc)
43-
fedObject := crudTester.CheckCreate(targetObject, overrides)
42+
crudTester, targetObject, overrides := initCrudTest(f, tl, f.KubeFedSystemNamespace(), typeConfig, testObjectsFunc)
43+
fedObject := crudTester.CheckCreate(targetObject, overrides, nil)
4444

4545
By("Set PropagationPolicy property as 'Orphan' on the DeleteOptions for Federated Deployment")
4646
orphan := metav1.DeletePropagationOrphan

test/e2e/e2e.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -49,9 +49,9 @@ func RunE2ETests(t *testing.T) {
4949
var _ = ginkgo.SynchronizedBeforeSuite(func() []byte {
5050
// Run only on Ginkgo node 1
5151

52-
if framework.TestContext.ScaleTest {
53-
// Scale testing will initialize an in-memory control plane
54-
// after the creation of a simulated federation.
52+
// Some tests require simulated federation and will initialize an
53+
// in-memory control plane.
54+
if framework.TestContext.SimulateFederation {
5555
return nil
5656
}
5757

test/e2e/framework/test_context.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ type TestContextType struct {
3838
WaitForFinalization bool
3939
ScaleTest bool
4040
ScaleClusterCount int
41+
SimulateFederation bool
4142
}
4243

4344
func (t *TestContextType) RunControllers() bool {
@@ -72,6 +73,7 @@ func registerFlags(t *TestContextType) {
7273
flag.BoolVar(&t.WaitForFinalization, "wait-for-finalization", true,
7374
"Whether the test suite should wait for finalization before stopping fixtures or exiting. Setting this to false will speed up test execution but likely result in wedged namespaces and is only recommended for disposeable clusters.")
7475
flag.BoolVar(&t.ScaleTest, "scale-test", false, "Whether the test suite should be configured for scale testing. Not compatible with most tests.")
76+
flag.BoolVar(&t.SimulateFederation, "simulate-federation", false, "Whether the tests require a simulated federation.")
7577
flag.IntVar(&t.ScaleClusterCount, "scale-cluster-count", 1, "How many member clusters to simulate when scale testing.")
7678
}
7779

@@ -83,6 +85,9 @@ func validateFlags(t *TestContextType) {
8385
if t.ScaleTest {
8486
t.InMemoryControllers = true
8587
t.LimitedScope = true
88+
// Scale testing will initialize an in-memory control plane
89+
// after the creation of a simulated federation.
90+
t.SimulateFederation = true
8691
// Scale testing will create a namespace per simulated cluster
8792
// and for large numbers of such namespaces the finalization
8893
// wait could be considerable.

0 commit comments

Comments
 (0)