Skip to content

Commit e132b77

Browse files
committed
Add stress test to repeatedly restart Pods with PVCs in parallel
Change-Id: I499571cc86b1058d0e16d79e5e998d1dedfd9a4a
1 parent b35fdbc commit e132b77

File tree

7 files changed

+223
-49
lines changed

7 files changed

+223
-49
lines changed

test/e2e/framework/pod/create.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -177,7 +177,7 @@ func MakeSecPod(podConfig *Config) (*v1.Pod, error) {
177177
if len(podConfig.Command) == 0 {
178178
podConfig.Command = "trap exit TERM; while true; do sleep 1; done"
179179
}
180-
podName := "security-context-" + string(uuid.NewUUID())
180+
podName := "pod-" + string(uuid.NewUUID())
181181
if podConfig.FsGroup == nil {
182182
podConfig.FsGroup = func(i int64) *int64 {
183183
return &i

test/e2e/storage/csi_volumes.go

Lines changed: 1 addition & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -31,29 +31,13 @@ var csiTestDrivers = []func() testsuites.TestDriver{
3131
// Don't run tests with mock driver (drivers.InitMockCSIDriver), it does not provide persistent storage.
3232
}
3333

34-
// List of testSuites to be executed in below loop
35-
var csiTestSuites = []func() testsuites.TestSuite{
36-
testsuites.InitEphemeralTestSuite,
37-
testsuites.InitVolumesTestSuite,
38-
testsuites.InitVolumeIOTestSuite,
39-
testsuites.InitVolumeModeTestSuite,
40-
testsuites.InitSubPathTestSuite,
41-
testsuites.InitProvisioningTestSuite,
42-
testsuites.InitSnapshottableTestSuite,
43-
testsuites.InitMultiVolumeTestSuite,
44-
testsuites.InitDisruptiveTestSuite,
45-
testsuites.InitVolumeExpandTestSuite,
46-
testsuites.InitVolumeLimitsTestSuite,
47-
testsuites.InitTopologyTestSuite,
48-
}
49-
5034
// This executes testSuites for csi volumes.
5135
var _ = utils.SIGDescribe("CSI Volumes", func() {
5236
for _, initDriver := range csiTestDrivers {
5337
curDriver := initDriver()
5438

5539
ginkgo.Context(testsuites.GetDriverNameWithFeatureTags(curDriver), func() {
56-
testsuites.DefineTestSuite(curDriver, csiTestSuites)
40+
testsuites.DefineTestSuite(curDriver, testsuites.CSISuites)
5741
})
5842
}
5943
})

test/e2e/storage/external/external.go

Lines changed: 1 addition & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -130,21 +130,6 @@ type driverDefinition struct {
130130
ClientNodeName string
131131
}
132132

133-
// List of testSuites to be executed for each external driver.
134-
var csiTestSuites = []func() testsuites.TestSuite{
135-
testsuites.InitEphemeralTestSuite,
136-
testsuites.InitMultiVolumeTestSuite,
137-
testsuites.InitProvisioningTestSuite,
138-
testsuites.InitSnapshottableTestSuite,
139-
testsuites.InitSubPathTestSuite,
140-
testsuites.InitVolumeIOTestSuite,
141-
testsuites.InitVolumeModeTestSuite,
142-
testsuites.InitVolumesTestSuite,
143-
testsuites.InitVolumeExpandTestSuite,
144-
testsuites.InitDisruptiveTestSuite,
145-
testsuites.InitVolumeLimitsTestSuite,
146-
}
147-
148133
func init() {
149134
e2econfig.Flags.Var(testDriverParameter{}, "storage.testdriver", "name of a .yaml or .json file that defines a driver for storage testing, can be used more than once")
150135
}
@@ -182,7 +167,7 @@ func AddDriverDefinition(filename string) error {
182167

183168
description := "External Storage " + testsuites.GetDriverNameWithFeatureTags(driver)
184169
ginkgo.Describe(description, func() {
185-
testsuites.DefineTestSuite(driver, csiTestSuites)
170+
testsuites.DefineTestSuite(driver, testsuites.CSISuites)
186171
})
187172

188173
return nil

test/e2e/storage/in_tree_volumes.go

Lines changed: 1 addition & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -48,27 +48,13 @@ var testDrivers = []func() testsuites.TestDriver{
4848
drivers.InitLocalDriverWithVolumeType(utils.LocalVolumeGCELocalSSD),
4949
}
5050

51-
// List of testSuites to be executed in below loop
52-
var testSuites = []func() testsuites.TestSuite{
53-
testsuites.InitVolumesTestSuite,
54-
testsuites.InitVolumeIOTestSuite,
55-
testsuites.InitVolumeModeTestSuite,
56-
testsuites.InitSubPathTestSuite,
57-
testsuites.InitProvisioningTestSuite,
58-
testsuites.InitMultiVolumeTestSuite,
59-
testsuites.InitVolumeExpandTestSuite,
60-
testsuites.InitDisruptiveTestSuite,
61-
testsuites.InitVolumeLimitsTestSuite,
62-
testsuites.InitTopologyTestSuite,
63-
}
64-
6551
// This executes testSuites for in-tree volumes.
6652
var _ = utils.SIGDescribe("In-tree Volumes", func() {
6753
for _, initDriver := range testDrivers {
6854
curDriver := initDriver()
6955

7056
ginkgo.Context(testsuites.GetDriverNameWithFeatureTags(curDriver), func() {
71-
testsuites.DefineTestSuite(curDriver, testSuites)
57+
testsuites.DefineTestSuite(curDriver, testsuites.BaseSuites)
7258
})
7359
}
7460
})

test/e2e/storage/testsuites/BUILD

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ go_library(
1010
"multivolume.go",
1111
"provisioning.go",
1212
"snapshottable.go",
13+
"stress.go",
1314
"subpath.go",
1415
"testdriver.go",
1516
"topology.go",

test/e2e/storage/testsuites/base.go

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,27 @@ func init() {
6060

6161
type opCounts map[string]int64
6262

63+
// BaseSuites is a list of storage test suites that work for in-tree and CSI drivers
64+
var BaseSuites = []func() TestSuite{
65+
InitVolumesTestSuite,
66+
InitVolumeIOTestSuite,
67+
InitVolumeModeTestSuite,
68+
InitSubPathTestSuite,
69+
InitProvisioningTestSuite,
70+
InitMultiVolumeTestSuite,
71+
InitVolumeExpandTestSuite,
72+
InitDisruptiveTestSuite,
73+
InitVolumeLimitsTestSuite,
74+
InitTopologyTestSuite,
75+
InitStressTestSuite,
76+
}
77+
78+
// CSISuites is a list of storage test suites that work only for CSI drivers
79+
var CSISuites = append(BaseSuites,
80+
InitEphemeralTestSuite,
81+
InitSnapshottableTestSuite,
82+
)
83+
6384
// TestSuite represents an interface for a set of tests which works with TestDriver
6485
type TestSuite interface {
6586
// GetTestSuiteInfo returns the TestSuiteInfo for this TestSuite
Lines changed: 197 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,197 @@
1+
/*
2+
Copyright 2020 The Kubernetes Authors.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
// This suite tests volumes under stress conditions
18+
19+
package testsuites
20+
21+
import (
22+
"context"
23+
"sync"
24+
25+
"github.com/onsi/ginkgo"
26+
27+
v1 "k8s.io/api/core/v1"
28+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
29+
errors "k8s.io/apimachinery/pkg/util/errors"
30+
clientset "k8s.io/client-go/kubernetes"
31+
"k8s.io/kubernetes/test/e2e/framework"
32+
e2epod "k8s.io/kubernetes/test/e2e/framework/pod"
33+
e2epv "k8s.io/kubernetes/test/e2e/framework/pv"
34+
e2eskipper "k8s.io/kubernetes/test/e2e/framework/skipper"
35+
"k8s.io/kubernetes/test/e2e/storage/testpatterns"
36+
)
37+
38+
type stressTestSuite struct {
39+
tsInfo TestSuiteInfo
40+
}
41+
42+
type stressTest struct {
43+
config *PerTestConfig
44+
driverCleanup func()
45+
46+
intreeOps opCounts
47+
migratedOps opCounts
48+
49+
resources []*VolumeResource
50+
pods []*v1.Pod
51+
// stop and wait for any async routines
52+
wg sync.WaitGroup
53+
stopChs []chan struct{}
54+
}
55+
56+
var _ TestSuite = &stressTestSuite{}
57+
58+
// InitStressTestSuite returns stressTestSuite that implements TestSuite interface
59+
func InitStressTestSuite() TestSuite {
60+
return &stressTestSuite{
61+
tsInfo: TestSuiteInfo{
62+
Name: "stress",
63+
FeatureTag: "[Feature: VolumeStress]",
64+
TestPatterns: []testpatterns.TestPattern{
65+
testpatterns.DefaultFsDynamicPV,
66+
testpatterns.BlockVolModeDynamicPV,
67+
},
68+
},
69+
}
70+
}
71+
72+
func (t *stressTestSuite) GetTestSuiteInfo() TestSuiteInfo {
73+
return t.tsInfo
74+
}
75+
76+
func (t *stressTestSuite) SkipRedundantSuite(driver TestDriver, pattern testpatterns.TestPattern) {
77+
}
78+
79+
func (t *stressTestSuite) DefineTests(driver TestDriver, pattern testpatterns.TestPattern) {
80+
var (
81+
dInfo = driver.GetDriverInfo()
82+
cs clientset.Interface
83+
)
84+
85+
ginkgo.BeforeEach(func() {
86+
// Check preconditions.
87+
ok := false
88+
_, ok = driver.(DynamicPVTestDriver)
89+
if !ok {
90+
e2eskipper.Skipf("Driver %s doesn't support %v -- skipping", dInfo.Name, pattern.VolType)
91+
}
92+
93+
if !driver.GetDriverInfo().Capabilities[CapBlock] && pattern.VolMode == v1.PersistentVolumeBlock {
94+
e2eskipper.Skipf("Driver %q does not support block volume mode - skipping", driver.GetDriverInfo().Name)
95+
}
96+
})
97+
98+
// This intentionally comes after checking the preconditions because it
99+
// registers its own BeforeEach which creates the namespace. Beware that it
100+
// also registers an AfterEach which renders f unusable. Any code using
101+
// f must run inside an It or Context callback.
102+
f := framework.NewDefaultFramework("stress")
103+
104+
init := func() *stressTest {
105+
cs = f.ClientSet
106+
l := &stressTest{}
107+
108+
// Now do the more expensive test initialization.
109+
l.config, l.driverCleanup = driver.PrepareTest(f)
110+
l.intreeOps, l.migratedOps = getMigrationVolumeOpCounts(f.ClientSet, dInfo.InTreePluginName)
111+
l.resources = []*VolumeResource{}
112+
l.pods = []*v1.Pod{}
113+
l.stopChs = []chan struct{}{}
114+
115+
return l
116+
}
117+
118+
cleanup := func(l *stressTest) {
119+
var errs []error
120+
121+
framework.Logf("Stopping and waiting for all test routines to finish")
122+
for _, stopCh := range l.stopChs {
123+
close(stopCh)
124+
}
125+
l.wg.Wait()
126+
127+
for _, pod := range l.pods {
128+
framework.Logf("Deleting pod %v", pod.Name)
129+
err := e2epod.DeletePodWithWait(cs, pod)
130+
errs = append(errs, err)
131+
}
132+
133+
for _, resource := range l.resources {
134+
errs = append(errs, resource.CleanupResource())
135+
}
136+
137+
errs = append(errs, tryFunc(l.driverCleanup))
138+
framework.ExpectNoError(errors.NewAggregate(errs), "while cleaning up resource")
139+
validateMigrationVolumeOpCounts(f.ClientSet, dInfo.InTreePluginName, l.intreeOps, l.migratedOps)
140+
}
141+
142+
ginkgo.It("multiple pods should access different volumes repeatedly [Slow] [Serial]", func() {
143+
const (
144+
numPods = 10
145+
// number of times each Pod should start
146+
numPodStarts = 10
147+
)
148+
149+
var err error
150+
151+
l := init()
152+
defer func() {
153+
cleanup(l)
154+
}()
155+
156+
for i := 0; i < numPods; i++ {
157+
framework.Logf("Creating resources for pod %v/%v", i, numPods)
158+
r := CreateVolumeResource(driver, l.config, pattern, t.GetTestSuiteInfo().SupportedSizeRange)
159+
l.resources = append(l.resources, r)
160+
l.pods = append(l.pods, e2epod.MakeSecPod(f.Namespace.Name,
161+
[]*v1.PersistentVolumeClaim{r.Pvc},
162+
nil, false, "", false, false, e2epv.SELinuxLabel, nil))
163+
l.stopChs = append(l.stopChs, make(chan struct{}))
164+
}
165+
166+
// Restart pod repeatedly
167+
for i := 0; i < numPods; i++ {
168+
podIndex := i
169+
l.wg.Add(1)
170+
go func() {
171+
defer ginkgo.GinkgoRecover()
172+
defer l.wg.Done()
173+
for j := 0; j < numPodStarts; j++ {
174+
select {
175+
case <-l.stopChs[podIndex]:
176+
return
177+
default:
178+
pod := l.pods[podIndex]
179+
framework.Logf("Pod %v, Iteration %v/%v", podIndex, j, numPodStarts)
180+
_, err = cs.CoreV1().Pods(pod.Namespace).Create(context.TODO(), pod, metav1.CreateOptions{})
181+
framework.ExpectNoError(err)
182+
183+
err = e2epod.WaitForPodRunningInNamespace(cs, pod)
184+
framework.ExpectNoError(err)
185+
186+
// TODO: write data per pod and validate it everytime
187+
188+
err = e2epod.DeletePodWithWait(f.ClientSet, pod)
189+
framework.ExpectNoError(err)
190+
}
191+
}
192+
}()
193+
}
194+
195+
l.wg.Wait()
196+
})
197+
}

0 commit comments

Comments
 (0)