volume_binding_test.go 45 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251
  1. /*
  2. Copyright 2017 The Kubernetes Authors.
  3. Licensed under the Apache License, Version 2.0 (the "License");
  4. you may not use this file except in compliance with the License.
  5. You may obtain a copy of the License at
  6. http://www.apache.org/licenses/LICENSE-2.0
  7. Unless required by applicable law or agreed to in writing, software
  8. distributed under the License is distributed on an "AS IS" BASIS,
  9. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  10. See the License for the specific language governing permissions and
  11. limitations under the License.
  12. */
  13. package volumescheduling
  14. // This file tests the VolumeScheduling feature.
  15. import (
  16. "context"
  17. "fmt"
  18. "os"
  19. "strconv"
  20. "strings"
  21. "testing"
  22. "time"
  23. "k8s.io/klog"
  24. v1 "k8s.io/api/core/v1"
  25. storagev1 "k8s.io/api/storage/v1"
  26. "k8s.io/apimachinery/pkg/api/resource"
  27. metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
  28. "k8s.io/apimachinery/pkg/util/rand"
  29. "k8s.io/apimachinery/pkg/util/sets"
  30. "k8s.io/apimachinery/pkg/util/wait"
  31. "k8s.io/client-go/informers"
  32. clientset "k8s.io/client-go/kubernetes"
  33. "k8s.io/client-go/util/workqueue"
  34. "k8s.io/kubernetes/pkg/controller/volume/persistentvolume"
  35. "k8s.io/kubernetes/pkg/scheduler/framework/plugins/nodevolumelimits"
  36. "k8s.io/kubernetes/pkg/volume"
  37. volumetest "k8s.io/kubernetes/pkg/volume/testing"
  38. imageutils "k8s.io/kubernetes/test/utils/image"
  39. )
  40. type testConfig struct {
  41. client clientset.Interface
  42. ns string
  43. stop <-chan struct{}
  44. teardown func()
  45. }
  46. var (
  47. // Delete API objects immediately
  48. deletePeriod = int64(0)
  49. deleteOption = &metav1.DeleteOptions{GracePeriodSeconds: &deletePeriod}
  50. modeWait = storagev1.VolumeBindingWaitForFirstConsumer
  51. modeImmediate = storagev1.VolumeBindingImmediate
  52. classWait = "wait"
  53. classImmediate = "immediate"
  54. classDynamic = "dynamic"
  55. classTopoMismatch = "topomismatch"
  56. sharedClasses = map[string]*storagev1.StorageClass{
  57. classImmediate: makeStorageClass(classImmediate, &modeImmediate),
  58. classWait: makeStorageClass(classWait, &modeWait),
  59. }
  60. )
  61. const (
  62. node1 = "node-1"
  63. node2 = "node-2"
  64. podLimit = 50
  65. volsPerPod = 3
  66. nodeAffinityLabelKey = "kubernetes.io/hostname"
  67. provisionerPluginName = "kubernetes.io/mock-provisioner"
  68. )
  69. type testPV struct {
  70. name string
  71. scName string
  72. preboundPVC string
  73. node string
  74. }
  75. type testPVC struct {
  76. name string
  77. scName string
  78. preboundPV string
  79. }
  80. func TestVolumeBinding(t *testing.T) {
  81. config := setupCluster(t, "volume-scheduling-", 2, 0, 0)
  82. defer config.teardown()
  83. cases := map[string]struct {
  84. pod *v1.Pod
  85. pvs []*testPV
  86. pvcs []*testPVC
  87. // Create these, but they should not be bound in the end
  88. unboundPvcs []*testPVC
  89. unboundPvs []*testPV
  90. shouldFail bool
  91. }{
  92. "immediate can bind": {
  93. pod: makePod("pod-i-canbind", config.ns, []string{"pvc-i-canbind"}),
  94. pvs: []*testPV{{"pv-i-canbind", classImmediate, "", node1}},
  95. pvcs: []*testPVC{{"pvc-i-canbind", classImmediate, ""}},
  96. },
  97. "immediate cannot bind": {
  98. pod: makePod("pod-i-cannotbind", config.ns, []string{"pvc-i-cannotbind"}),
  99. unboundPvcs: []*testPVC{{"pvc-i-cannotbind", classImmediate, ""}},
  100. shouldFail: true,
  101. },
  102. "immediate pvc prebound": {
  103. pod: makePod("pod-i-pvc-prebound", config.ns, []string{"pvc-i-prebound"}),
  104. pvs: []*testPV{{"pv-i-pvc-prebound", classImmediate, "", node1}},
  105. pvcs: []*testPVC{{"pvc-i-prebound", classImmediate, "pv-i-pvc-prebound"}},
  106. },
  107. "immediate pv prebound": {
  108. pod: makePod("pod-i-pv-prebound", config.ns, []string{"pvc-i-pv-prebound"}),
  109. pvs: []*testPV{{"pv-i-prebound", classImmediate, "pvc-i-pv-prebound", node1}},
  110. pvcs: []*testPVC{{"pvc-i-pv-prebound", classImmediate, ""}},
  111. },
  112. "wait can bind": {
  113. pod: makePod("pod-w-canbind", config.ns, []string{"pvc-w-canbind"}),
  114. pvs: []*testPV{{"pv-w-canbind", classWait, "", node1}},
  115. pvcs: []*testPVC{{"pvc-w-canbind", classWait, ""}},
  116. },
  117. "wait cannot bind": {
  118. pod: makePod("pod-w-cannotbind", config.ns, []string{"pvc-w-cannotbind"}),
  119. unboundPvcs: []*testPVC{{"pvc-w-cannotbind", classWait, ""}},
  120. shouldFail: true,
  121. },
  122. "wait pvc prebound": {
  123. pod: makePod("pod-w-pvc-prebound", config.ns, []string{"pvc-w-prebound"}),
  124. pvs: []*testPV{{"pv-w-pvc-prebound", classWait, "", node1}},
  125. pvcs: []*testPVC{{"pvc-w-prebound", classWait, "pv-w-pvc-prebound"}},
  126. },
  127. "wait pv prebound": {
  128. pod: makePod("pod-w-pv-prebound", config.ns, []string{"pvc-w-pv-prebound"}),
  129. pvs: []*testPV{{"pv-w-prebound", classWait, "pvc-w-pv-prebound", node1}},
  130. pvcs: []*testPVC{{"pvc-w-pv-prebound", classWait, ""}},
  131. },
  132. "wait can bind two": {
  133. pod: makePod("pod-w-canbind-2", config.ns, []string{"pvc-w-canbind-2", "pvc-w-canbind-3"}),
  134. pvs: []*testPV{
  135. {"pv-w-canbind-2", classWait, "", node2},
  136. {"pv-w-canbind-3", classWait, "", node2},
  137. },
  138. pvcs: []*testPVC{
  139. {"pvc-w-canbind-2", classWait, ""},
  140. {"pvc-w-canbind-3", classWait, ""},
  141. },
  142. unboundPvs: []*testPV{
  143. {"pv-w-canbind-5", classWait, "", node1},
  144. },
  145. },
  146. "wait cannot bind two": {
  147. pod: makePod("pod-w-cannotbind-2", config.ns, []string{"pvc-w-cannotbind-1", "pvc-w-cannotbind-2"}),
  148. unboundPvcs: []*testPVC{
  149. {"pvc-w-cannotbind-1", classWait, ""},
  150. {"pvc-w-cannotbind-2", classWait, ""},
  151. },
  152. unboundPvs: []*testPV{
  153. {"pv-w-cannotbind-1", classWait, "", node2},
  154. {"pv-w-cannotbind-2", classWait, "", node1},
  155. },
  156. shouldFail: true,
  157. },
  158. "mix immediate and wait": {
  159. pod: makePod("pod-mix-bound", config.ns, []string{"pvc-w-canbind-4", "pvc-i-canbind-2"}),
  160. pvs: []*testPV{
  161. {"pv-w-canbind-4", classWait, "", node1},
  162. {"pv-i-canbind-2", classImmediate, "", node1},
  163. },
  164. pvcs: []*testPVC{
  165. {"pvc-w-canbind-4", classWait, ""},
  166. {"pvc-i-canbind-2", classImmediate, ""},
  167. },
  168. },
  169. }
  170. for name, test := range cases {
  171. klog.Infof("Running test %v", name)
  172. // Create two StorageClasses
  173. suffix := rand.String(4)
  174. classes := map[string]*storagev1.StorageClass{}
  175. classes[classImmediate] = makeStorageClass(fmt.Sprintf("immediate-%v", suffix), &modeImmediate)
  176. classes[classWait] = makeStorageClass(fmt.Sprintf("wait-%v", suffix), &modeWait)
  177. for _, sc := range classes {
  178. if _, err := config.client.StorageV1().StorageClasses().Create(context.TODO(), sc, metav1.CreateOptions{}); err != nil {
  179. t.Fatalf("Failed to create StorageClass %q: %v", sc.Name, err)
  180. }
  181. }
  182. // Create PVs
  183. for _, pvConfig := range test.pvs {
  184. pv := makePV(pvConfig.name, classes[pvConfig.scName].Name, pvConfig.preboundPVC, config.ns, pvConfig.node)
  185. if _, err := config.client.CoreV1().PersistentVolumes().Create(context.TODO(), pv, metav1.CreateOptions{}); err != nil {
  186. t.Fatalf("Failed to create PersistentVolume %q: %v", pv.Name, err)
  187. }
  188. }
  189. for _, pvConfig := range test.unboundPvs {
  190. pv := makePV(pvConfig.name, classes[pvConfig.scName].Name, pvConfig.preboundPVC, config.ns, pvConfig.node)
  191. if _, err := config.client.CoreV1().PersistentVolumes().Create(context.TODO(), pv, metav1.CreateOptions{}); err != nil {
  192. t.Fatalf("Failed to create PersistentVolume %q: %v", pv.Name, err)
  193. }
  194. }
  195. // Wait for PVs to become available to avoid race condition in PV controller
  196. // https://github.com/kubernetes/kubernetes/issues/85320
  197. for _, pvConfig := range test.pvs {
  198. if err := waitForPVPhase(config.client, pvConfig.name, v1.VolumeAvailable); err != nil {
  199. t.Fatalf("PersistentVolume %q failed to become available: %v", pvConfig.name, err)
  200. }
  201. }
  202. for _, pvConfig := range test.unboundPvs {
  203. if err := waitForPVPhase(config.client, pvConfig.name, v1.VolumeAvailable); err != nil {
  204. t.Fatalf("PersistentVolume %q failed to become available: %v", pvConfig.name, err)
  205. }
  206. }
  207. // Create PVCs
  208. for _, pvcConfig := range test.pvcs {
  209. pvc := makePVC(pvcConfig.name, config.ns, &classes[pvcConfig.scName].Name, pvcConfig.preboundPV)
  210. if _, err := config.client.CoreV1().PersistentVolumeClaims(config.ns).Create(context.TODO(), pvc, metav1.CreateOptions{}); err != nil {
  211. t.Fatalf("Failed to create PersistentVolumeClaim %q: %v", pvc.Name, err)
  212. }
  213. }
  214. for _, pvcConfig := range test.unboundPvcs {
  215. pvc := makePVC(pvcConfig.name, config.ns, &classes[pvcConfig.scName].Name, pvcConfig.preboundPV)
  216. if _, err := config.client.CoreV1().PersistentVolumeClaims(config.ns).Create(context.TODO(), pvc, metav1.CreateOptions{}); err != nil {
  217. t.Fatalf("Failed to create PersistentVolumeClaim %q: %v", pvc.Name, err)
  218. }
  219. }
  220. // Create Pod
  221. if _, err := config.client.CoreV1().Pods(config.ns).Create(context.TODO(), test.pod, metav1.CreateOptions{}); err != nil {
  222. t.Fatalf("Failed to create Pod %q: %v", test.pod.Name, err)
  223. }
  224. if test.shouldFail {
  225. if err := waitForPodUnschedulable(config.client, test.pod); err != nil {
  226. t.Errorf("Pod %q was not unschedulable: %v", test.pod.Name, err)
  227. }
  228. } else {
  229. if err := waitForPodToSchedule(config.client, test.pod); err != nil {
  230. t.Errorf("Failed to schedule Pod %q: %v", test.pod.Name, err)
  231. }
  232. }
  233. // Validate PVC/PV binding
  234. for _, pvc := range test.pvcs {
  235. validatePVCPhase(t, config.client, pvc.name, config.ns, v1.ClaimBound, false)
  236. }
  237. for _, pvc := range test.unboundPvcs {
  238. validatePVCPhase(t, config.client, pvc.name, config.ns, v1.ClaimPending, false)
  239. }
  240. for _, pv := range test.pvs {
  241. validatePVPhase(t, config.client, pv.name, v1.VolumeBound)
  242. }
  243. for _, pv := range test.unboundPvs {
  244. validatePVPhase(t, config.client, pv.name, v1.VolumeAvailable)
  245. }
  246. // Force delete objects, but they still may not be immediately removed
  247. deleteTestObjects(config.client, config.ns, deleteOption)
  248. }
  249. }
  250. // TestVolumeBindingRescheduling tests scheduler will retry scheduling when needed.
  251. func TestVolumeBindingRescheduling(t *testing.T) {
  252. config := setupCluster(t, "volume-scheduling-", 2, 0, 0)
  253. defer config.teardown()
  254. storageClassName := "local-storage"
  255. cases := map[string]struct {
  256. pod *v1.Pod
  257. pvcs []*testPVC
  258. pvs []*testPV
  259. trigger func(config *testConfig)
  260. shouldFail bool
  261. }{
  262. "reschedule on WaitForFirstConsumer dynamic storage class add": {
  263. pod: makePod("pod-reschedule-onclassadd-dynamic", config.ns, []string{"pvc-reschedule-onclassadd-dynamic"}),
  264. pvcs: []*testPVC{
  265. {"pvc-reschedule-onclassadd-dynamic", "", ""},
  266. },
  267. trigger: func(config *testConfig) {
  268. sc := makeDynamicProvisionerStorageClass(storageClassName, &modeWait, nil)
  269. if _, err := config.client.StorageV1().StorageClasses().Create(context.TODO(), sc, metav1.CreateOptions{}); err != nil {
  270. t.Fatalf("Failed to create StorageClass %q: %v", sc.Name, err)
  271. }
  272. },
  273. shouldFail: false,
  274. },
  275. "reschedule on WaitForFirstConsumer static storage class add": {
  276. pod: makePod("pod-reschedule-onclassadd-static", config.ns, []string{"pvc-reschedule-onclassadd-static"}),
  277. pvcs: []*testPVC{
  278. {"pvc-reschedule-onclassadd-static", "", ""},
  279. },
  280. trigger: func(config *testConfig) {
  281. sc := makeStorageClass(storageClassName, &modeWait)
  282. if _, err := config.client.StorageV1().StorageClasses().Create(context.TODO(), sc, metav1.CreateOptions{}); err != nil {
  283. t.Fatalf("Failed to create StorageClass %q: %v", sc.Name, err)
  284. }
  285. // Create pv for this class to mock static provisioner behavior.
  286. pv := makePV("pv-reschedule-onclassadd-static", storageClassName, "", "", node1)
  287. if pv, err := config.client.CoreV1().PersistentVolumes().Create(context.TODO(), pv, metav1.CreateOptions{}); err != nil {
  288. t.Fatalf("Failed to create PersistentVolume %q: %v", pv.Name, err)
  289. }
  290. },
  291. shouldFail: false,
  292. },
  293. "reschedule on delay binding PVC add": {
  294. pod: makePod("pod-reschedule-onpvcadd", config.ns, []string{"pvc-reschedule-onpvcadd"}),
  295. pvs: []*testPV{
  296. {
  297. name: "pv-reschedule-onpvcadd",
  298. scName: classWait,
  299. node: node1,
  300. },
  301. },
  302. trigger: func(config *testConfig) {
  303. pvc := makePVC("pvc-reschedule-onpvcadd", config.ns, &classWait, "")
  304. if _, err := config.client.CoreV1().PersistentVolumeClaims(config.ns).Create(context.TODO(), pvc, metav1.CreateOptions{}); err != nil {
  305. t.Fatalf("Failed to create PersistentVolumeClaim %q: %v", pvc.Name, err)
  306. }
  307. },
  308. shouldFail: false,
  309. },
  310. }
  311. for name, test := range cases {
  312. klog.Infof("Running test %v", name)
  313. if test.pod == nil {
  314. t.Fatal("pod is required for this test")
  315. }
  316. // Create unbound pvc
  317. for _, pvcConfig := range test.pvcs {
  318. pvc := makePVC(pvcConfig.name, config.ns, &storageClassName, "")
  319. if _, err := config.client.CoreV1().PersistentVolumeClaims(config.ns).Create(context.TODO(), pvc, metav1.CreateOptions{}); err != nil {
  320. t.Fatalf("Failed to create PersistentVolumeClaim %q: %v", pvc.Name, err)
  321. }
  322. }
  323. // Create PVs
  324. for _, pvConfig := range test.pvs {
  325. pv := makePV(pvConfig.name, sharedClasses[pvConfig.scName].Name, pvConfig.preboundPVC, config.ns, pvConfig.node)
  326. if _, err := config.client.CoreV1().PersistentVolumes().Create(context.TODO(), pv, metav1.CreateOptions{}); err != nil {
  327. t.Fatalf("Failed to create PersistentVolume %q: %v", pv.Name, err)
  328. }
  329. }
  330. // Create pod
  331. if _, err := config.client.CoreV1().Pods(config.ns).Create(context.TODO(), test.pod, metav1.CreateOptions{}); err != nil {
  332. t.Fatalf("Failed to create Pod %q: %v", test.pod.Name, err)
  333. }
  334. // Wait for pod is unschedulable.
  335. klog.Infof("Waiting for pod is unschedulable")
  336. if err := waitForPodUnschedulable(config.client, test.pod); err != nil {
  337. t.Errorf("Failed as Pod %s was not unschedulable: %v", test.pod.Name, err)
  338. }
  339. // Trigger
  340. test.trigger(config)
  341. // Wait for pod is scheduled or unscheduable.
  342. if !test.shouldFail {
  343. klog.Infof("Waiting for pod is scheduled")
  344. if err := waitForPodToSchedule(config.client, test.pod); err != nil {
  345. t.Errorf("Failed to schedule Pod %q: %v", test.pod.Name, err)
  346. }
  347. } else {
  348. klog.Infof("Waiting for pod is unschedulable")
  349. if err := waitForPodUnschedulable(config.client, test.pod); err != nil {
  350. t.Errorf("Failed as Pod %s was not unschedulable: %v", test.pod.Name, err)
  351. }
  352. }
  353. // Force delete objects, but they still may not be immediately removed
  354. deleteTestObjects(config.client, config.ns, deleteOption)
  355. }
  356. }
  357. // TestVolumeBindingStress creates <podLimit> pods, each with <volsPerPod> unbound or prebound PVCs.
  358. // PVs are precreated.
  359. func TestVolumeBindingStress(t *testing.T) {
  360. testVolumeBindingStress(t, 0, false, 0)
  361. }
  362. // Like TestVolumeBindingStress but with scheduler resync. In real cluster,
  363. // scheduler will schedule failed pod frequently due to various events, e.g.
  364. // service/node update events.
  365. // This is useful to detect possible race conditions.
  366. func TestVolumeBindingStressWithSchedulerResync(t *testing.T) {
  367. testVolumeBindingStress(t, time.Second, false, 0)
  368. }
  369. // Like TestVolumeBindingStress but with fast dynamic provisioning
  370. func TestVolumeBindingDynamicStressFast(t *testing.T) {
  371. testVolumeBindingStress(t, 0, true, 0)
  372. }
  373. // Like TestVolumeBindingStress but with slow dynamic provisioning
  374. func TestVolumeBindingDynamicStressSlow(t *testing.T) {
  375. testVolumeBindingStress(t, 0, true, 10)
  376. }
  377. func testVolumeBindingStress(t *testing.T, schedulerResyncPeriod time.Duration, dynamic bool, provisionDelaySeconds int) {
  378. config := setupCluster(t, "volume-binding-stress-", 1, schedulerResyncPeriod, provisionDelaySeconds)
  379. defer config.teardown()
  380. // Set max volume limit to the number of PVCs the test will create
  381. // TODO: remove when max volume limit allows setting through storageclass
  382. if err := os.Setenv(nodevolumelimits.KubeMaxPDVols, fmt.Sprintf("%v", podLimit*volsPerPod)); err != nil {
  383. t.Fatalf("failed to set max pd limit: %v", err)
  384. }
  385. defer os.Unsetenv(nodevolumelimits.KubeMaxPDVols)
  386. scName := &classWait
  387. if dynamic {
  388. scName = &classDynamic
  389. sc := makeDynamicProvisionerStorageClass(*scName, &modeWait, nil)
  390. if _, err := config.client.StorageV1().StorageClasses().Create(context.TODO(), sc, metav1.CreateOptions{}); err != nil {
  391. t.Fatalf("Failed to create StorageClass %q: %v", sc.Name, err)
  392. }
  393. }
  394. klog.Infof("Start creating PVs and PVCs")
  395. // Create enough PVs and PVCs for all the pods
  396. podVolumesCount := podLimit * volsPerPod
  397. pvs := make([]*v1.PersistentVolume, podVolumesCount)
  398. pvcs := make([]*v1.PersistentVolumeClaim, podVolumesCount)
  399. workqueue.ParallelizeUntil(context.TODO(), 16, podVolumesCount, func(i int) {
  400. var (
  401. pv *v1.PersistentVolume
  402. pvc *v1.PersistentVolumeClaim
  403. pvName = fmt.Sprintf("pv-stress-%v", i)
  404. pvcName = fmt.Sprintf("pvc-stress-%v", i)
  405. )
  406. // Don't create pvs for dynamic provisioning test
  407. if !dynamic {
  408. if rand.Int()%2 == 0 {
  409. // static unbound pvs
  410. pv = makePV(pvName, *scName, "", "", node1)
  411. } else {
  412. // static prebound pvs
  413. pv = makePV(pvName, classImmediate, pvcName, config.ns, node1)
  414. }
  415. if pv, err := config.client.CoreV1().PersistentVolumes().Create(context.TODO(), pv, metav1.CreateOptions{}); err != nil {
  416. t.Fatalf("Failed to create PersistentVolume %q: %v", pv.Name, err)
  417. }
  418. pvs[i] = pv
  419. }
  420. if pv != nil && pv.Spec.ClaimRef != nil && pv.Spec.ClaimRef.Name == pvcName {
  421. pvc = makePVC(pvcName, config.ns, &classImmediate, pv.Name)
  422. } else {
  423. pvc = makePVC(pvcName, config.ns, scName, "")
  424. }
  425. if pvc, err := config.client.CoreV1().PersistentVolumeClaims(config.ns).Create(context.TODO(), pvc, metav1.CreateOptions{}); err != nil {
  426. t.Fatalf("Failed to create PersistentVolumeClaim %q: %v", pvc.Name, err)
  427. }
  428. pvcs[i] = pvc
  429. })
  430. klog.Infof("Start creating Pods")
  431. pods := make([]*v1.Pod, podLimit)
  432. workqueue.ParallelizeUntil(context.TODO(), 16, podLimit, func(i int) {
  433. // Generate string of all the PVCs for the pod
  434. podPvcs := []string{}
  435. for j := i * volsPerPod; j < (i+1)*volsPerPod; j++ {
  436. podPvcs = append(podPvcs, pvcs[j].Name)
  437. }
  438. pod := makePod(fmt.Sprintf("pod%03d", i), config.ns, podPvcs)
  439. if pod, err := config.client.CoreV1().Pods(config.ns).Create(context.TODO(), pod, metav1.CreateOptions{}); err != nil {
  440. t.Fatalf("Failed to create Pod %q: %v", pod.Name, err)
  441. }
  442. pods[i] = pod
  443. })
  444. klog.Infof("Start validating pod scheduled")
  445. // Validate Pods scheduled
  446. workqueue.ParallelizeUntil(context.TODO(), 16, len(pods), func(i int) {
  447. pod := pods[i]
  448. // Use increased timeout for stress test because there is a higher chance of
  449. // PV sync error
  450. if err := waitForPodToScheduleWithTimeout(config.client, pod, 2*time.Minute); err != nil {
  451. t.Errorf("Failed to schedule Pod %q: %v", pod.Name, err)
  452. }
  453. })
  454. klog.Infof("Start validating PVCs scheduled")
  455. // Validate PVC/PV binding
  456. workqueue.ParallelizeUntil(context.TODO(), 16, len(pvcs), func(i int) {
  457. validatePVCPhase(t, config.client, pvcs[i].Name, config.ns, v1.ClaimBound, dynamic)
  458. })
  459. // Don't validate pv for dynamic provisioning test
  460. if !dynamic {
  461. klog.Infof("Start validating PVs scheduled")
  462. workqueue.ParallelizeUntil(context.TODO(), 16, len(pvs), func(i int) {
  463. validatePVPhase(t, config.client, pvs[i].Name, v1.VolumeBound)
  464. })
  465. }
  466. }
  467. func testVolumeBindingWithAffinity(t *testing.T, anti bool, numNodes, numPods, numPVsFirstNode int) {
  468. config := setupCluster(t, "volume-pod-affinity-", numNodes, 0, 0)
  469. defer config.teardown()
  470. pods := []*v1.Pod{}
  471. pvcs := []*v1.PersistentVolumeClaim{}
  472. // Create PVs for the first node
  473. for i := 0; i < numPVsFirstNode; i++ {
  474. pv := makePV(fmt.Sprintf("pv-node1-%v", i), classWait, "", "", node1)
  475. if pv, err := config.client.CoreV1().PersistentVolumes().Create(context.TODO(), pv, metav1.CreateOptions{}); err != nil {
  476. t.Fatalf("Failed to create PersistentVolume %q: %v", pv.Name, err)
  477. }
  478. }
  479. // Create 1 PV per Node for the remaining nodes
  480. for i := 2; i <= numNodes; i++ {
  481. pv := makePV(fmt.Sprintf("pv-node%v-0", i), classWait, "", "", fmt.Sprintf("node-%v", i))
  482. if pv, err := config.client.CoreV1().PersistentVolumes().Create(context.TODO(), pv, metav1.CreateOptions{}); err != nil {
  483. t.Fatalf("Failed to create PersistentVolume %q: %v", pv.Name, err)
  484. }
  485. }
  486. // Create pods
  487. for i := 0; i < numPods; i++ {
  488. // Create one pvc per pod
  489. pvc := makePVC(fmt.Sprintf("pvc-%v", i), config.ns, &classWait, "")
  490. if pvc, err := config.client.CoreV1().PersistentVolumeClaims(config.ns).Create(context.TODO(), pvc, metav1.CreateOptions{}); err != nil {
  491. t.Fatalf("Failed to create PersistentVolumeClaim %q: %v", pvc.Name, err)
  492. }
  493. pvcs = append(pvcs, pvc)
  494. // Create pod with pod affinity
  495. pod := makePod(fmt.Sprintf("pod%03d", i), config.ns, []string{pvc.Name})
  496. pod.Spec.Affinity = &v1.Affinity{}
  497. affinityTerms := []v1.PodAffinityTerm{
  498. {
  499. LabelSelector: &metav1.LabelSelector{
  500. MatchExpressions: []metav1.LabelSelectorRequirement{
  501. {
  502. Key: "app",
  503. Operator: metav1.LabelSelectorOpIn,
  504. Values: []string{"volume-binding-test"},
  505. },
  506. },
  507. },
  508. TopologyKey: nodeAffinityLabelKey,
  509. },
  510. }
  511. if anti {
  512. pod.Spec.Affinity.PodAntiAffinity = &v1.PodAntiAffinity{
  513. RequiredDuringSchedulingIgnoredDuringExecution: affinityTerms,
  514. }
  515. } else {
  516. pod.Spec.Affinity.PodAffinity = &v1.PodAffinity{
  517. RequiredDuringSchedulingIgnoredDuringExecution: affinityTerms,
  518. }
  519. }
  520. if pod, err := config.client.CoreV1().Pods(config.ns).Create(context.TODO(), pod, metav1.CreateOptions{}); err != nil {
  521. t.Fatalf("Failed to create Pod %q: %v", pod.Name, err)
  522. }
  523. pods = append(pods, pod)
  524. }
  525. // Validate Pods scheduled
  526. scheduledNodes := sets.NewString()
  527. for _, pod := range pods {
  528. if err := waitForPodToSchedule(config.client, pod); err != nil {
  529. t.Errorf("Failed to schedule Pod %q: %v", pod.Name, err)
  530. } else {
  531. // Keep track of all the nodes that the Pods were scheduled on
  532. pod, err = config.client.CoreV1().Pods(config.ns).Get(context.TODO(), pod.Name, metav1.GetOptions{})
  533. if err != nil {
  534. t.Fatalf("Failed to get Pod %q: %v", pod.Name, err)
  535. }
  536. if pod.Spec.NodeName == "" {
  537. t.Fatalf("Pod %q node name unset after scheduling", pod.Name)
  538. }
  539. scheduledNodes.Insert(pod.Spec.NodeName)
  540. }
  541. }
  542. // Validate the affinity policy
  543. if anti {
  544. // The pods should have been spread across different nodes
  545. if scheduledNodes.Len() != numPods {
  546. t.Errorf("Pods were scheduled across %v nodes instead of %v", scheduledNodes.Len(), numPods)
  547. }
  548. } else {
  549. // The pods should have been scheduled on 1 node
  550. if scheduledNodes.Len() != 1 {
  551. t.Errorf("Pods were scheduled across %v nodes instead of %v", scheduledNodes.Len(), 1)
  552. }
  553. }
  554. // Validate PVC binding
  555. for _, pvc := range pvcs {
  556. validatePVCPhase(t, config.client, pvc.Name, config.ns, v1.ClaimBound, false)
  557. }
  558. }
  559. func TestVolumeBindingWithAntiAffinity(t *testing.T) {
  560. numNodes := 10
  561. // Create as many pods as number of nodes
  562. numPods := numNodes
  563. // Create many more PVs on node1 to increase chance of selecting node1
  564. numPVsFirstNode := 10 * numNodes
  565. testVolumeBindingWithAffinity(t, true, numNodes, numPods, numPVsFirstNode)
  566. }
  567. func TestVolumeBindingWithAffinity(t *testing.T) {
  568. numPods := 10
  569. // Create many more nodes to increase chance of selecting a PV on a different node than node1
  570. numNodes := 10 * numPods
  571. // Create numPods PVs on the first node
  572. numPVsFirstNode := numPods
  573. testVolumeBindingWithAffinity(t, true, numNodes, numPods, numPVsFirstNode)
  574. }
  575. func TestPVAffinityConflict(t *testing.T) {
  576. config := setupCluster(t, "volume-scheduling-", 3, 0, 0)
  577. defer config.teardown()
  578. pv := makePV("local-pv", classImmediate, "", "", node1)
  579. pvc := makePVC("local-pvc", config.ns, &classImmediate, "")
  580. // Create PV
  581. if _, err := config.client.CoreV1().PersistentVolumes().Create(context.TODO(), pv, metav1.CreateOptions{}); err != nil {
  582. t.Fatalf("Failed to create PersistentVolume %q: %v", pv.Name, err)
  583. }
  584. // Create PVC
  585. if _, err := config.client.CoreV1().PersistentVolumeClaims(config.ns).Create(context.TODO(), pvc, metav1.CreateOptions{}); err != nil {
  586. t.Fatalf("Failed to create PersistentVolumeClaim %q: %v", pvc.Name, err)
  587. }
  588. // Wait for PVC bound
  589. if err := waitForPVCBound(config.client, pvc); err != nil {
  590. t.Fatalf("PVC %q failed to bind: %v", pvc.Name, err)
  591. }
  592. nodeMarkers := []interface{}{
  593. markNodeAffinity,
  594. markNodeSelector,
  595. }
  596. for i := 0; i < len(nodeMarkers); i++ {
  597. podName := "local-pod-" + strconv.Itoa(i+1)
  598. pod := makePod(podName, config.ns, []string{"local-pvc"})
  599. nodeMarkers[i].(func(*v1.Pod, string))(pod, "node-2")
  600. // Create Pod
  601. if _, err := config.client.CoreV1().Pods(config.ns).Create(context.TODO(), pod, metav1.CreateOptions{}); err != nil {
  602. t.Fatalf("Failed to create Pod %q: %v", pod.Name, err)
  603. }
  604. // Give time to shceduler to attempt to schedule pod
  605. if err := waitForPodUnschedulable(config.client, pod); err != nil {
  606. t.Errorf("Failed as Pod %s was not unschedulable: %v", pod.Name, err)
  607. }
  608. // Check pod conditions
  609. p, err := config.client.CoreV1().Pods(config.ns).Get(context.TODO(), podName, metav1.GetOptions{})
  610. if err != nil {
  611. t.Fatalf("Failed to access Pod %s status: %v", podName, err)
  612. }
  613. if strings.Compare(string(p.Status.Phase), "Pending") != 0 {
  614. t.Fatalf("Failed as Pod %s was in: %s state and not in expected: Pending state", podName, p.Status.Phase)
  615. }
  616. if strings.Compare(p.Status.Conditions[0].Reason, "Unschedulable") != 0 {
  617. t.Fatalf("Failed as Pod %s reason was: %s but expected: Unschedulable", podName, p.Status.Conditions[0].Reason)
  618. }
  619. if !strings.Contains(p.Status.Conditions[0].Message, "node(s) didn't match node selector") || !strings.Contains(p.Status.Conditions[0].Message, "node(s) had volume node affinity conflict") {
  620. t.Fatalf("Failed as Pod's %s failure message does not contain expected message: node(s) didn't match node selector, node(s) had volume node affinity conflict. Got message %q", podName, p.Status.Conditions[0].Message)
  621. }
  622. // Deleting test pod
  623. if err := config.client.CoreV1().Pods(config.ns).Delete(context.TODO(), podName, &metav1.DeleteOptions{}); err != nil {
  624. t.Fatalf("Failed to delete Pod %s: %v", podName, err)
  625. }
  626. }
  627. }
  628. func TestVolumeProvision(t *testing.T) {
  629. config := setupCluster(t, "volume-scheduling", 1, 0, 0)
  630. defer config.teardown()
  631. cases := map[string]struct {
  632. pod *v1.Pod
  633. pvs []*testPV
  634. boundPvcs []*testPVC
  635. provisionedPvcs []*testPVC
  636. // Create these, but they should not be bound in the end
  637. unboundPvcs []*testPVC
  638. shouldFail bool
  639. }{
  640. "wait provisioned": {
  641. pod: makePod("pod-pvc-canprovision", config.ns, []string{"pvc-canprovision"}),
  642. provisionedPvcs: []*testPVC{{"pvc-canprovision", classWait, ""}},
  643. },
  644. "topolgy unsatisfied": {
  645. pod: makePod("pod-pvc-topomismatch", config.ns, []string{"pvc-topomismatch"}),
  646. unboundPvcs: []*testPVC{{"pvc-topomismatch", classTopoMismatch, ""}},
  647. shouldFail: true,
  648. },
  649. "wait one bound, one provisioned": {
  650. pod: makePod("pod-pvc-canbind-or-provision", config.ns, []string{"pvc-w-canbind", "pvc-canprovision"}),
  651. pvs: []*testPV{{"pv-w-canbind", classWait, "", node1}},
  652. boundPvcs: []*testPVC{{"pvc-w-canbind", classWait, ""}},
  653. provisionedPvcs: []*testPVC{{"pvc-canprovision", classWait, ""}},
  654. },
  655. "one immediate pv prebound, one wait provisioned": {
  656. pod: makePod("pod-i-pv-prebound-w-provisioned", config.ns, []string{"pvc-i-pv-prebound", "pvc-canprovision"}),
  657. pvs: []*testPV{{"pv-i-prebound", classImmediate, "pvc-i-pv-prebound", node1}},
  658. boundPvcs: []*testPVC{{"pvc-i-pv-prebound", classImmediate, ""}},
  659. provisionedPvcs: []*testPVC{{"pvc-canprovision", classWait, ""}},
  660. },
  661. "wait one pv prebound, one provisioned": {
  662. pod: makePod("pod-w-pv-prebound-w-provisioned", config.ns, []string{"pvc-w-pv-prebound", "pvc-canprovision"}),
  663. pvs: []*testPV{{"pv-w-prebound", classWait, "pvc-w-pv-prebound", node1}},
  664. boundPvcs: []*testPVC{{"pvc-w-pv-prebound", classWait, ""}},
  665. provisionedPvcs: []*testPVC{{"pvc-canprovision", classWait, ""}},
  666. },
  667. "immediate provisioned by controller": {
  668. pod: makePod("pod-i-unbound", config.ns, []string{"pvc-controller-provisioned"}),
  669. // A pvc of immediate binding mode is expected to be provisioned by controller,
  670. // we treat it as "bound" here because it is supposed to be in same state
  671. // with bound claims, i.e. in bound status and has no selectedNode annotation.
  672. boundPvcs: []*testPVC{{"pvc-controller-provisioned", classImmediate, ""}},
  673. },
  674. }
  675. for name, test := range cases {
  676. klog.Infof("Running test %v", name)
  677. // Create StorageClasses
  678. suffix := rand.String(4)
  679. classes := map[string]*storagev1.StorageClass{}
  680. classes[classImmediate] = makeDynamicProvisionerStorageClass(fmt.Sprintf("immediate-%v", suffix), &modeImmediate, nil)
  681. classes[classWait] = makeDynamicProvisionerStorageClass(fmt.Sprintf("wait-%v", suffix), &modeWait, nil)
  682. topo := []v1.TopologySelectorTerm{
  683. {
  684. MatchLabelExpressions: []v1.TopologySelectorLabelRequirement{
  685. {
  686. Key: nodeAffinityLabelKey,
  687. Values: []string{node2},
  688. },
  689. },
  690. },
  691. }
  692. classes[classTopoMismatch] = makeDynamicProvisionerStorageClass(fmt.Sprintf("topomismatch-%v", suffix), &modeWait, topo)
  693. for _, sc := range classes {
  694. if _, err := config.client.StorageV1().StorageClasses().Create(context.TODO(), sc, metav1.CreateOptions{}); err != nil {
  695. t.Fatalf("Failed to create StorageClass %q: %v", sc.Name, err)
  696. }
  697. }
  698. // Create PVs
  699. for _, pvConfig := range test.pvs {
  700. pv := makePV(pvConfig.name, classes[pvConfig.scName].Name, pvConfig.preboundPVC, config.ns, pvConfig.node)
  701. if _, err := config.client.CoreV1().PersistentVolumes().Create(context.TODO(), pv, metav1.CreateOptions{}); err != nil {
  702. t.Fatalf("Failed to create PersistentVolume %q: %v", pv.Name, err)
  703. }
  704. }
  705. // Create PVCs
  706. for _, pvcConfig := range test.boundPvcs {
  707. pvc := makePVC(pvcConfig.name, config.ns, &classes[pvcConfig.scName].Name, pvcConfig.preboundPV)
  708. if _, err := config.client.CoreV1().PersistentVolumeClaims(config.ns).Create(context.TODO(), pvc, metav1.CreateOptions{}); err != nil {
  709. t.Fatalf("Failed to create PersistentVolumeClaim %q: %v", pvc.Name, err)
  710. }
  711. }
  712. for _, pvcConfig := range test.unboundPvcs {
  713. pvc := makePVC(pvcConfig.name, config.ns, &classes[pvcConfig.scName].Name, pvcConfig.preboundPV)
  714. if _, err := config.client.CoreV1().PersistentVolumeClaims(config.ns).Create(context.TODO(), pvc, metav1.CreateOptions{}); err != nil {
  715. t.Fatalf("Failed to create PersistentVolumeClaim %q: %v", pvc.Name, err)
  716. }
  717. }
  718. for _, pvcConfig := range test.provisionedPvcs {
  719. pvc := makePVC(pvcConfig.name, config.ns, &classes[pvcConfig.scName].Name, pvcConfig.preboundPV)
  720. if _, err := config.client.CoreV1().PersistentVolumeClaims(config.ns).Create(context.TODO(), pvc, metav1.CreateOptions{}); err != nil {
  721. t.Fatalf("Failed to create PersistentVolumeClaim %q: %v", pvc.Name, err)
  722. }
  723. }
  724. // Create Pod
  725. if _, err := config.client.CoreV1().Pods(config.ns).Create(context.TODO(), test.pod, metav1.CreateOptions{}); err != nil {
  726. t.Fatalf("Failed to create Pod %q: %v", test.pod.Name, err)
  727. }
  728. if test.shouldFail {
  729. if err := waitForPodUnschedulable(config.client, test.pod); err != nil {
  730. t.Errorf("Pod %q was not unschedulable: %v", test.pod.Name, err)
  731. }
  732. } else {
  733. if err := waitForPodToSchedule(config.client, test.pod); err != nil {
  734. t.Errorf("Failed to schedule Pod %q: %v", test.pod.Name, err)
  735. }
  736. }
  737. // Validate PVC/PV binding
  738. for _, pvc := range test.boundPvcs {
  739. validatePVCPhase(t, config.client, pvc.name, config.ns, v1.ClaimBound, false)
  740. }
  741. for _, pvc := range test.unboundPvcs {
  742. validatePVCPhase(t, config.client, pvc.name, config.ns, v1.ClaimPending, false)
  743. }
  744. for _, pvc := range test.provisionedPvcs {
  745. validatePVCPhase(t, config.client, pvc.name, config.ns, v1.ClaimBound, true)
  746. }
  747. for _, pv := range test.pvs {
  748. validatePVPhase(t, config.client, pv.name, v1.VolumeBound)
  749. }
  750. // Force delete objects, but they still may not be immediately removed
  751. deleteTestObjects(config.client, config.ns, deleteOption)
  752. }
  753. }
  754. // TestRescheduleProvisioning validate that PV controller will remove
  755. // selectedNode annotation from a claim to reschedule volume provision
  756. // on provision failure.
  757. func TestRescheduleProvisioning(t *testing.T) {
  758. // Set feature gates
  759. controllerCh := make(chan struct{})
  760. testCtx := initTestMaster(t, "reschedule-volume-provision", nil)
  761. clientset := testCtx.clientSet
  762. ns := testCtx.ns.Name
  763. defer func() {
  764. close(controllerCh)
  765. deleteTestObjects(clientset, ns, nil)
  766. testCtx.clientSet.CoreV1().Nodes().DeleteCollection(context.TODO(), nil, metav1.ListOptions{})
  767. testCtx.closeFn()
  768. }()
  769. ctrl, informerFactory, err := initPVController(t, testCtx, 0)
  770. if err != nil {
  771. t.Fatalf("Failed to create PV controller: %v", err)
  772. }
  773. // Prepare node and storage class.
  774. testNode := makeNode(0)
  775. if _, err := clientset.CoreV1().Nodes().Create(context.TODO(), testNode, metav1.CreateOptions{}); err != nil {
  776. t.Fatalf("Failed to create Node %q: %v", testNode.Name, err)
  777. }
  778. scName := "fail-provision"
  779. sc := makeDynamicProvisionerStorageClass(scName, &modeWait, nil)
  780. // Expect the storage class fail to provision.
  781. sc.Parameters[volumetest.ExpectProvisionFailureKey] = ""
  782. if _, err := clientset.StorageV1().StorageClasses().Create(context.TODO(), sc, metav1.CreateOptions{}); err != nil {
  783. t.Fatalf("Failed to create StorageClass %q: %v", sc.Name, err)
  784. }
  785. // Create a pvc with selected node annotation.
  786. pvcName := "pvc-fail-to-provision"
  787. pvc := makePVC(pvcName, ns, &scName, "")
  788. pvc.Annotations = map[string]string{"volume.kubernetes.io/selected-node": node1}
  789. pvc, err = clientset.CoreV1().PersistentVolumeClaims(ns).Create(context.TODO(), pvc, metav1.CreateOptions{})
  790. if err != nil {
  791. t.Fatalf("Failed to create PersistentVolumeClaim %q: %v", pvc.Name, err)
  792. }
  793. // Validate selectedNode annotation exists on created claim.
  794. selectedNodeAnn, exist := pvc.Annotations["volume.kubernetes.io/selected-node"]
  795. if !exist || selectedNodeAnn != node1 {
  796. t.Fatalf("Created pvc is not annotated as expected")
  797. }
  798. // Start controller.
  799. go ctrl.Run(controllerCh)
  800. informerFactory.Start(controllerCh)
  801. informerFactory.WaitForCacheSync(controllerCh)
  802. // Validate that the annotation is removed by controller for provision reschedule.
  803. if err := waitForProvisionAnn(clientset, pvc, false); err != nil {
  804. t.Errorf("Expect to reschedule provision for PVC %v/%v, but still found selected-node annotation on it", ns, pvcName)
  805. }
  806. }
  807. func setupCluster(t *testing.T, nsName string, numberOfNodes int, resyncPeriod time.Duration, provisionDelaySeconds int) *testConfig {
  808. textCtx := initTestSchedulerWithOptions(t, initTestMaster(t, nsName, nil), resyncPeriod)
  809. clientset := textCtx.clientSet
  810. ns := textCtx.ns.Name
  811. ctrl, informerFactory, err := initPVController(t, textCtx, provisionDelaySeconds)
  812. if err != nil {
  813. t.Fatalf("Failed to create PV controller: %v", err)
  814. }
  815. go ctrl.Run(textCtx.ctx.Done())
  816. // Start informer factory after all controllers are configured and running.
  817. informerFactory.Start(textCtx.ctx.Done())
  818. informerFactory.WaitForCacheSync(textCtx.ctx.Done())
  819. // Create shared objects
  820. // Create nodes
  821. for i := 0; i < numberOfNodes; i++ {
  822. testNode := makeNode(i)
  823. if _, err := clientset.CoreV1().Nodes().Create(context.TODO(), testNode, metav1.CreateOptions{}); err != nil {
  824. t.Fatalf("Failed to create Node %q: %v", testNode.Name, err)
  825. }
  826. }
  827. // Create SCs
  828. for _, sc := range sharedClasses {
  829. if _, err := clientset.StorageV1().StorageClasses().Create(context.TODO(), sc, metav1.CreateOptions{}); err != nil {
  830. t.Fatalf("Failed to create StorageClass %q: %v", sc.Name, err)
  831. }
  832. }
  833. return &testConfig{
  834. client: clientset,
  835. ns: ns,
  836. stop: textCtx.ctx.Done(),
  837. teardown: func() {
  838. klog.Infof("test cluster %q start to tear down", ns)
  839. deleteTestObjects(clientset, ns, nil)
  840. cleanupTest(t, textCtx)
  841. },
  842. }
  843. }
  844. func initPVController(t *testing.T, testCtx *testContext, provisionDelaySeconds int) (*persistentvolume.PersistentVolumeController, informers.SharedInformerFactory, error) {
  845. clientset := testCtx.clientSet
  846. // Informers factory for controllers
  847. informerFactory := informers.NewSharedInformerFactory(clientset, 0)
  848. // Start PV controller for volume binding.
  849. host := volumetest.NewFakeVolumeHost(t, "/tmp/fake", nil, nil)
  850. plugin := &volumetest.FakeVolumePlugin{
  851. PluginName: provisionerPluginName,
  852. Host: host,
  853. Config: volume.VolumeConfig{},
  854. LastProvisionerOptions: volume.VolumeOptions{},
  855. ProvisionDelaySeconds: provisionDelaySeconds,
  856. NewAttacherCallCount: 0,
  857. NewDetacherCallCount: 0,
  858. Mounters: nil,
  859. Unmounters: nil,
  860. Attachers: nil,
  861. Detachers: nil,
  862. }
  863. plugins := []volume.VolumePlugin{plugin}
  864. params := persistentvolume.ControllerParameters{
  865. KubeClient: clientset,
  866. // Use a frequent resync period to retry API update conflicts due to
  867. // https://github.com/kubernetes/kubernetes/issues/85320
  868. SyncPeriod: 5 * time.Second,
  869. VolumePlugins: plugins,
  870. Cloud: nil,
  871. ClusterName: "volume-test-cluster",
  872. VolumeInformer: informerFactory.Core().V1().PersistentVolumes(),
  873. ClaimInformer: informerFactory.Core().V1().PersistentVolumeClaims(),
  874. ClassInformer: informerFactory.Storage().V1().StorageClasses(),
  875. PodInformer: informerFactory.Core().V1().Pods(),
  876. NodeInformer: informerFactory.Core().V1().Nodes(),
  877. EnableDynamicProvisioning: true,
  878. }
  879. ctrl, err := persistentvolume.NewController(params)
  880. if err != nil {
  881. return nil, nil, err
  882. }
  883. return ctrl, informerFactory, nil
  884. }
  885. func deleteTestObjects(client clientset.Interface, ns string, option *metav1.DeleteOptions) {
  886. client.CoreV1().Pods(ns).DeleteCollection(context.TODO(), option, metav1.ListOptions{})
  887. client.CoreV1().PersistentVolumeClaims(ns).DeleteCollection(context.TODO(), option, metav1.ListOptions{})
  888. client.CoreV1().PersistentVolumes().DeleteCollection(context.TODO(), option, metav1.ListOptions{})
  889. client.StorageV1().StorageClasses().DeleteCollection(context.TODO(), option, metav1.ListOptions{})
  890. }
  891. func makeStorageClass(name string, mode *storagev1.VolumeBindingMode) *storagev1.StorageClass {
  892. return &storagev1.StorageClass{
  893. ObjectMeta: metav1.ObjectMeta{
  894. Name: name,
  895. },
  896. Provisioner: "kubernetes.io/no-provisioner",
  897. VolumeBindingMode: mode,
  898. }
  899. }
  900. func makeDynamicProvisionerStorageClass(name string, mode *storagev1.VolumeBindingMode, allowedTopologies []v1.TopologySelectorTerm) *storagev1.StorageClass {
  901. return &storagev1.StorageClass{
  902. ObjectMeta: metav1.ObjectMeta{
  903. Name: name,
  904. },
  905. Provisioner: provisionerPluginName,
  906. VolumeBindingMode: mode,
  907. AllowedTopologies: allowedTopologies,
  908. Parameters: map[string]string{},
  909. }
  910. }
  911. func makePV(name, scName, pvcName, ns, node string) *v1.PersistentVolume {
  912. pv := &v1.PersistentVolume{
  913. ObjectMeta: metav1.ObjectMeta{
  914. Name: name,
  915. Annotations: map[string]string{},
  916. },
  917. Spec: v1.PersistentVolumeSpec{
  918. Capacity: v1.ResourceList{
  919. v1.ResourceName(v1.ResourceStorage): resource.MustParse("5Gi"),
  920. },
  921. AccessModes: []v1.PersistentVolumeAccessMode{
  922. v1.ReadWriteOnce,
  923. },
  924. StorageClassName: scName,
  925. PersistentVolumeSource: v1.PersistentVolumeSource{
  926. Local: &v1.LocalVolumeSource{
  927. Path: "/test-path",
  928. },
  929. },
  930. NodeAffinity: &v1.VolumeNodeAffinity{
  931. Required: &v1.NodeSelector{
  932. NodeSelectorTerms: []v1.NodeSelectorTerm{
  933. {
  934. MatchExpressions: []v1.NodeSelectorRequirement{
  935. {
  936. Key: nodeAffinityLabelKey,
  937. Operator: v1.NodeSelectorOpIn,
  938. Values: []string{node},
  939. },
  940. },
  941. },
  942. },
  943. },
  944. },
  945. },
  946. }
  947. if pvcName != "" {
  948. pv.Spec.ClaimRef = &v1.ObjectReference{Name: pvcName, Namespace: ns}
  949. }
  950. return pv
  951. }
  952. func makePVC(name, ns string, scName *string, volumeName string) *v1.PersistentVolumeClaim {
  953. return &v1.PersistentVolumeClaim{
  954. ObjectMeta: metav1.ObjectMeta{
  955. Name: name,
  956. Namespace: ns,
  957. },
  958. Spec: v1.PersistentVolumeClaimSpec{
  959. AccessModes: []v1.PersistentVolumeAccessMode{
  960. v1.ReadWriteOnce,
  961. },
  962. Resources: v1.ResourceRequirements{
  963. Requests: v1.ResourceList{
  964. v1.ResourceName(v1.ResourceStorage): resource.MustParse("5Gi"),
  965. },
  966. },
  967. StorageClassName: scName,
  968. VolumeName: volumeName,
  969. },
  970. }
  971. }
  972. func makePod(name, ns string, pvcs []string) *v1.Pod {
  973. volumes := []v1.Volume{}
  974. for i, pvc := range pvcs {
  975. volumes = append(volumes, v1.Volume{
  976. Name: fmt.Sprintf("vol%v", i),
  977. VolumeSource: v1.VolumeSource{
  978. PersistentVolumeClaim: &v1.PersistentVolumeClaimVolumeSource{
  979. ClaimName: pvc,
  980. },
  981. },
  982. })
  983. }
  984. return &v1.Pod{
  985. ObjectMeta: metav1.ObjectMeta{
  986. Name: name,
  987. Namespace: ns,
  988. Labels: map[string]string{
  989. "app": "volume-binding-test",
  990. },
  991. },
  992. Spec: v1.PodSpec{
  993. Containers: []v1.Container{
  994. {
  995. Name: "write-pod",
  996. Image: imageutils.GetE2EImage(imageutils.BusyBox),
  997. Command: []string{"/bin/sh"},
  998. Args: []string{"-c", "while true; do sleep 1; done"},
  999. },
  1000. },
  1001. Volumes: volumes,
  1002. },
  1003. }
  1004. }
  1005. func makeNode(index int) *v1.Node {
  1006. return &v1.Node{
  1007. ObjectMeta: metav1.ObjectMeta{
  1008. Name: fmt.Sprintf("node-%d", index+1),
  1009. Labels: map[string]string{nodeAffinityLabelKey: fmt.Sprintf("node-%d", index+1)},
  1010. },
  1011. Spec: v1.NodeSpec{Unschedulable: false},
  1012. Status: v1.NodeStatus{
  1013. Capacity: v1.ResourceList{
  1014. v1.ResourcePods: *resource.NewQuantity(podLimit, resource.DecimalSI),
  1015. },
  1016. Conditions: []v1.NodeCondition{
  1017. {
  1018. Type: v1.NodeReady,
  1019. Status: v1.ConditionTrue,
  1020. Reason: fmt.Sprintf("schedulable condition"),
  1021. LastHeartbeatTime: metav1.Time{Time: time.Now()},
  1022. },
  1023. },
  1024. },
  1025. }
  1026. }
  1027. func validatePVCPhase(t *testing.T, client clientset.Interface, pvcName string, ns string, phase v1.PersistentVolumeClaimPhase, isProvisioned bool) {
  1028. claim, err := client.CoreV1().PersistentVolumeClaims(ns).Get(context.TODO(), pvcName, metav1.GetOptions{})
  1029. if err != nil {
  1030. t.Errorf("Failed to get PVC %v/%v: %v", ns, pvcName, err)
  1031. }
  1032. if claim.Status.Phase != phase {
  1033. t.Errorf("PVC %v/%v phase not %v, got %v", ns, pvcName, phase, claim.Status.Phase)
  1034. }
  1035. // Check whether the bound claim is provisioned/bound as expect.
  1036. if phase == v1.ClaimBound {
  1037. if err := validateProvisionAnn(claim, isProvisioned); err != nil {
  1038. t.Errorf("Provisoning annotaion on PVC %v/%v not bahaviors as expected: %v", ns, pvcName, err)
  1039. }
  1040. }
  1041. }
  1042. func validateProvisionAnn(claim *v1.PersistentVolumeClaim, volIsProvisioned bool) error {
  1043. selectedNode, provisionAnnoExist := claim.Annotations["volume.kubernetes.io/selected-node"]
  1044. if volIsProvisioned {
  1045. if !provisionAnnoExist {
  1046. return fmt.Errorf("PVC %v/%v expected to be provisioned, but no selected-node annotation found", claim.Namespace, claim.Name)
  1047. }
  1048. if selectedNode != node1 {
  1049. return fmt.Errorf("PVC %v/%v expected to be annotated as %v, but got %v", claim.Namespace, claim.Name, node1, selectedNode)
  1050. }
  1051. }
  1052. if !volIsProvisioned && provisionAnnoExist {
  1053. return fmt.Errorf("PVC %v/%v not expected to be provisioned, but found selected-node annotation", claim.Namespace, claim.Name)
  1054. }
  1055. return nil
  1056. }
  1057. func waitForProvisionAnn(client clientset.Interface, pvc *v1.PersistentVolumeClaim, annShouldExist bool) error {
  1058. return wait.Poll(time.Second, 30*time.Second, func() (bool, error) {
  1059. claim, err := client.CoreV1().PersistentVolumeClaims(pvc.Namespace).Get(context.TODO(), pvc.Name, metav1.GetOptions{})
  1060. if err != nil {
  1061. return false, err
  1062. }
  1063. if err := validateProvisionAnn(claim, annShouldExist); err == nil {
  1064. return true, nil
  1065. }
  1066. return false, nil
  1067. })
  1068. }
  1069. func validatePVPhase(t *testing.T, client clientset.Interface, pvName string, phase v1.PersistentVolumePhase) {
  1070. pv, err := client.CoreV1().PersistentVolumes().Get(context.TODO(), pvName, metav1.GetOptions{})
  1071. if err != nil {
  1072. t.Errorf("Failed to get PV %v: %v", pvName, err)
  1073. }
  1074. if pv.Status.Phase != phase {
  1075. t.Errorf("PV %v phase not %v, got %v", pvName, phase, pv.Status.Phase)
  1076. }
  1077. }
  1078. func waitForPVPhase(client clientset.Interface, pvName string, phase v1.PersistentVolumePhase) error {
  1079. return wait.PollImmediate(time.Second, 30*time.Second, func() (bool, error) {
  1080. pv, err := client.CoreV1().PersistentVolumes().Get(context.TODO(), pvName, metav1.GetOptions{})
  1081. if err != nil {
  1082. return false, err
  1083. }
  1084. if pv.Status.Phase == phase {
  1085. return true, nil
  1086. }
  1087. return false, nil
  1088. })
  1089. }
  1090. func waitForPVCBound(client clientset.Interface, pvc *v1.PersistentVolumeClaim) error {
  1091. return wait.Poll(time.Second, 30*time.Second, func() (bool, error) {
  1092. claim, err := client.CoreV1().PersistentVolumeClaims(pvc.Namespace).Get(context.TODO(), pvc.Name, metav1.GetOptions{})
  1093. if err != nil {
  1094. return false, err
  1095. }
  1096. if claim.Status.Phase == v1.ClaimBound {
  1097. return true, nil
  1098. }
  1099. return false, nil
  1100. })
  1101. }
  1102. func markNodeAffinity(pod *v1.Pod, node string) {
  1103. affinity := &v1.Affinity{
  1104. NodeAffinity: &v1.NodeAffinity{
  1105. RequiredDuringSchedulingIgnoredDuringExecution: &v1.NodeSelector{
  1106. NodeSelectorTerms: []v1.NodeSelectorTerm{
  1107. {
  1108. MatchExpressions: []v1.NodeSelectorRequirement{
  1109. {
  1110. Key: nodeAffinityLabelKey,
  1111. Operator: v1.NodeSelectorOpIn,
  1112. Values: []string{node},
  1113. },
  1114. },
  1115. },
  1116. },
  1117. },
  1118. },
  1119. }
  1120. pod.Spec.Affinity = affinity
  1121. }
  1122. func markNodeSelector(pod *v1.Pod, node string) {
  1123. ns := map[string]string{
  1124. nodeAffinityLabelKey: node,
  1125. }
  1126. pod.Spec.NodeSelector = ns
  1127. }