mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-27 05:27:21 +00:00
Merge pull request #25216 from rootfs/pv-integration-test
Automatic merge from submit-queue Add PV test to persistent volume integration test This is a follow-up to #25120 @kubernetes/sig-storage
This commit is contained in:
commit
e484f142d6
@ -21,6 +21,8 @@ package integration
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"math/rand"
|
"math/rand"
|
||||||
|
"net/http/httptest"
|
||||||
|
"strconv"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@ -47,36 +49,15 @@ func TestPersistentVolumeRecycler(t *testing.T) {
|
|||||||
defer s.Close()
|
defer s.Close()
|
||||||
|
|
||||||
deleteAllEtcdKeys()
|
deleteAllEtcdKeys()
|
||||||
// Use higher QPS and Burst, there is a test for race condition below, which
|
testClient, ctrl := createClients(s)
|
||||||
// creates many claims and default values were too low.
|
|
||||||
testClient := clientset.NewForConfigOrDie(&restclient.Config{Host: s.URL, ContentConfig: restclient.ContentConfig{GroupVersion: testapi.Default.GroupVersion()}, QPS: 1000, Burst: 100000})
|
|
||||||
host := volumetest.NewFakeVolumeHost("/tmp/fake", nil, nil)
|
|
||||||
|
|
||||||
plugins := []volume.VolumePlugin{&volumetest.FakeVolumePlugin{"plugin-name", host, volume.VolumeConfig{}, volume.VolumeOptions{}, 0, 0, nil, nil, nil, nil}}
|
|
||||||
cloud := &fake_cloud.FakeCloud{}
|
|
||||||
|
|
||||||
ctrl := persistentvolumecontroller.NewPersistentVolumeController(testClient, 10*time.Second, nil, plugins, cloud, "", nil, nil, nil)
|
|
||||||
ctrl.Run()
|
ctrl.Run()
|
||||||
defer ctrl.Stop()
|
defer ctrl.Stop()
|
||||||
|
|
||||||
// This PV will be claimed, released, and recycled.
|
// This PV will be claimed, released, and recycled.
|
||||||
pv := &api.PersistentVolume{
|
pv := createPV("fake-pv", "/tmp/foo", "10G", []api.PersistentVolumeAccessMode{api.ReadWriteOnce}, api.PersistentVolumeReclaimRecycle)
|
||||||
ObjectMeta: api.ObjectMeta{Name: "fake-pv"},
|
|
||||||
Spec: api.PersistentVolumeSpec{
|
|
||||||
PersistentVolumeSource: api.PersistentVolumeSource{HostPath: &api.HostPathVolumeSource{Path: "/tmp/foo"}},
|
|
||||||
Capacity: api.ResourceList{api.ResourceName(api.ResourceStorage): resource.MustParse("10G")},
|
|
||||||
AccessModes: []api.PersistentVolumeAccessMode{api.ReadWriteOnce},
|
|
||||||
PersistentVolumeReclaimPolicy: api.PersistentVolumeReclaimRecycle,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
pvc := &api.PersistentVolumeClaim{
|
pvc := createPVC("fake-pvc", "5G", []api.PersistentVolumeAccessMode{api.ReadWriteOnce})
|
||||||
ObjectMeta: api.ObjectMeta{Name: "fake-pvc"},
|
|
||||||
Spec: api.PersistentVolumeClaimSpec{
|
|
||||||
Resources: api.ResourceRequirements{Requests: api.ResourceList{api.ResourceName(api.ResourceStorage): resource.MustParse("5G")}},
|
|
||||||
AccessModes: []api.PersistentVolumeAccessMode{api.ReadWriteOnce},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
w, _ := testClient.PersistentVolumes().Watch(api.ListOptions{})
|
w, _ := testClient.PersistentVolumes().Watch(api.ListOptions{})
|
||||||
defer w.Stop()
|
defer w.Stop()
|
||||||
@ -84,7 +65,7 @@ func TestPersistentVolumeRecycler(t *testing.T) {
|
|||||||
_, _ = testClient.PersistentVolumes().Create(pv)
|
_, _ = testClient.PersistentVolumes().Create(pv)
|
||||||
_, _ = testClient.PersistentVolumeClaims(api.NamespaceDefault).Create(pvc)
|
_, _ = testClient.PersistentVolumeClaims(api.NamespaceDefault).Create(pvc)
|
||||||
|
|
||||||
// wait until the binder pairs the volume and claim
|
// wait until the controller pairs the volume and claim
|
||||||
waitForPersistentVolumePhase(w, api.VolumeBound)
|
waitForPersistentVolumePhase(w, api.VolumeBound)
|
||||||
|
|
||||||
// deleting a claim releases the volume, after which it can be recycled
|
// deleting a claim releases the volume, after which it can be recycled
|
||||||
@ -171,6 +152,130 @@ func TestPersistentVolumeRecycler(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestPersistentVolumeMultiPVs(t *testing.T) {
|
||||||
|
_, s := framework.RunAMaster(t)
|
||||||
|
defer s.Close()
|
||||||
|
|
||||||
|
deleteAllEtcdKeys()
|
||||||
|
testClient, controller := createClients(s)
|
||||||
|
controller.Run()
|
||||||
|
defer controller.Stop()
|
||||||
|
|
||||||
|
maxPVs := 100
|
||||||
|
pvs := make([]*api.PersistentVolume, maxPVs)
|
||||||
|
for i := 0; i < maxPVs; i++ {
|
||||||
|
// This PV will be claimed, released, and deleted
|
||||||
|
pvs[i] = createPV("pv-"+strconv.Itoa(i), "/tmp/foo"+strconv.Itoa(i), strconv.Itoa(i)+"G",
|
||||||
|
[]api.PersistentVolumeAccessMode{api.ReadWriteOnce}, api.PersistentVolumeReclaimDelete)
|
||||||
|
}
|
||||||
|
|
||||||
|
pvc := createPVC("pvc-2", strconv.Itoa(maxPVs/2)+"G", []api.PersistentVolumeAccessMode{api.ReadWriteOnce})
|
||||||
|
|
||||||
|
w, _ := testClient.PersistentVolumes().Watch(api.ListOptions{})
|
||||||
|
defer w.Stop()
|
||||||
|
|
||||||
|
for i := 0; i < maxPVs; i++ {
|
||||||
|
_, _ = testClient.PersistentVolumes().Create(pvs[i])
|
||||||
|
}
|
||||||
|
|
||||||
|
_, _ = testClient.PersistentVolumeClaims(api.NamespaceDefault).Create(pvc)
|
||||||
|
|
||||||
|
// wait until the controller pairs the volume and claim
|
||||||
|
waitForPersistentVolumePhase(w, api.VolumeBound)
|
||||||
|
|
||||||
|
// only one PV is bound
|
||||||
|
bound := 0
|
||||||
|
for i := 0; i < maxPVs; i++ {
|
||||||
|
pv, err := testClient.PersistentVolumes().Get(pvs[i].Name)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Unexpected error getting pv: %v", err)
|
||||||
|
}
|
||||||
|
if pv.Spec.ClaimRef == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
// found a bounded PV
|
||||||
|
p := pv.Spec.Capacity[api.ResourceStorage]
|
||||||
|
pvCap := p.Value()
|
||||||
|
expectedCap := resource.MustParse(strconv.Itoa(maxPVs/2) + "G")
|
||||||
|
expectedCapVal := expectedCap.Value()
|
||||||
|
if pv.Spec.ClaimRef.Name != pvc.Name || pvCap != expectedCapVal {
|
||||||
|
t.Fatalf("Bind mismatch! Expected %s capacity %d but got %s capacity %d", pvc.Name, expectedCapVal, pv.Spec.ClaimRef.Name, pvCap)
|
||||||
|
}
|
||||||
|
t.Logf("claim bounded to %s capacity %v", pv.Name, pv.Spec.Capacity[api.ResourceStorage])
|
||||||
|
bound += 1
|
||||||
|
}
|
||||||
|
|
||||||
|
if bound != 1 {
|
||||||
|
t.Fatalf("Only 1 PV should be bound but got %d", bound)
|
||||||
|
}
|
||||||
|
|
||||||
|
// deleting a claim releases the volume
|
||||||
|
if err := testClient.PersistentVolumeClaims(api.NamespaceDefault).Delete(pvc.Name, nil); err != nil {
|
||||||
|
t.Errorf("error deleting claim %s", pvc.Name)
|
||||||
|
}
|
||||||
|
|
||||||
|
waitForPersistentVolumePhase(w, api.VolumeReleased)
|
||||||
|
|
||||||
|
deleteAllEtcdKeys()
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPersistentVolumeMultiPVsDiffAccessModes(t *testing.T) {
|
||||||
|
_, s := framework.RunAMaster(t)
|
||||||
|
defer s.Close()
|
||||||
|
|
||||||
|
deleteAllEtcdKeys()
|
||||||
|
testClient, controller := createClients(s)
|
||||||
|
controller.Run()
|
||||||
|
defer controller.Stop()
|
||||||
|
|
||||||
|
// This PV will be claimed, released, and deleted
|
||||||
|
pv_rwo := createPV("pv-rwo", "/tmp/foo", "10G",
|
||||||
|
[]api.PersistentVolumeAccessMode{api.ReadWriteOnce}, api.PersistentVolumeReclaimDelete)
|
||||||
|
pv_rwm := createPV("pv-rwm", "/tmp/bar", "10G",
|
||||||
|
[]api.PersistentVolumeAccessMode{api.ReadWriteMany}, api.PersistentVolumeReclaimDelete)
|
||||||
|
|
||||||
|
pvc := createPVC("pvc-rwm", "5G", []api.PersistentVolumeAccessMode{api.ReadWriteMany})
|
||||||
|
|
||||||
|
w, _ := testClient.PersistentVolumes().Watch(api.ListOptions{})
|
||||||
|
defer w.Stop()
|
||||||
|
|
||||||
|
_, _ = testClient.PersistentVolumes().Create(pv_rwm)
|
||||||
|
_, _ = testClient.PersistentVolumes().Create(pv_rwo)
|
||||||
|
|
||||||
|
_, _ = testClient.PersistentVolumeClaims(api.NamespaceDefault).Create(pvc)
|
||||||
|
|
||||||
|
// wait until the controller pairs the volume and claim
|
||||||
|
waitForPersistentVolumePhase(w, api.VolumeBound)
|
||||||
|
|
||||||
|
// only RWM PV is bound
|
||||||
|
pv, err := testClient.PersistentVolumes().Get("pv-rwo")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Unexpected error getting pv: %v", err)
|
||||||
|
}
|
||||||
|
if pv.Spec.ClaimRef != nil {
|
||||||
|
t.Fatalf("ReadWriteOnce PV shouldn't be bound")
|
||||||
|
}
|
||||||
|
pv, err = testClient.PersistentVolumes().Get("pv-rwm")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Unexpected error getting pv: %v", err)
|
||||||
|
}
|
||||||
|
if pv.Spec.ClaimRef == nil {
|
||||||
|
t.Fatalf("ReadWriteMany PV should be bound")
|
||||||
|
}
|
||||||
|
if pv.Spec.ClaimRef.Name != pvc.Name {
|
||||||
|
t.Fatalf("Bind mismatch! Expected %s but got %s", pvc.Name, pv.Spec.ClaimRef.Name)
|
||||||
|
}
|
||||||
|
|
||||||
|
// deleting a claim releases the volume
|
||||||
|
if err := testClient.PersistentVolumeClaims(api.NamespaceDefault).Delete(pvc.Name, nil); err != nil {
|
||||||
|
t.Errorf("error deleting claim %s", pvc.Name)
|
||||||
|
}
|
||||||
|
|
||||||
|
waitForPersistentVolumePhase(w, api.VolumeReleased)
|
||||||
|
|
||||||
|
deleteAllEtcdKeys()
|
||||||
|
}
|
||||||
|
|
||||||
func waitForPersistentVolumePhase(w watch.Interface, phase api.PersistentVolumePhase) {
|
func waitForPersistentVolumePhase(w watch.Interface, phase api.PersistentVolumePhase) {
|
||||||
for {
|
for {
|
||||||
event := <-w.ResultChan()
|
event := <-w.ResultChan()
|
||||||
@ -180,3 +285,36 @@ func waitForPersistentVolumePhase(w watch.Interface, phase api.PersistentVolumeP
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func createClients(s *httptest.Server) (*clientset.Clientset, *persistentvolumecontroller.PersistentVolumeController) {
|
||||||
|
// Use higher QPS and Burst, there is a test for race condition below, which
|
||||||
|
// creates many claims and default values were too low.
|
||||||
|
testClient := clientset.NewForConfigOrDie(&restclient.Config{Host: s.URL, ContentConfig: restclient.ContentConfig{GroupVersion: testapi.Default.GroupVersion()}, QPS: 1000, Burst: 100000})
|
||||||
|
host := volumetest.NewFakeVolumeHost("/tmp/fake", nil, nil)
|
||||||
|
plugins := []volume.VolumePlugin{&volumetest.FakeVolumePlugin{"plugin-name", host, volume.VolumeConfig{}, volume.VolumeOptions{}, 0, 0, nil, nil, nil, nil}}
|
||||||
|
cloud := &fake_cloud.FakeCloud{}
|
||||||
|
ctrl := persistentvolumecontroller.NewPersistentVolumeController(testClient, 10*time.Second, nil, plugins, cloud, "", nil, nil, nil)
|
||||||
|
return testClient, ctrl
|
||||||
|
}
|
||||||
|
|
||||||
|
func createPV(name, path, cap string, mode []api.PersistentVolumeAccessMode, reclaim api.PersistentVolumeReclaimPolicy) *api.PersistentVolume {
|
||||||
|
return &api.PersistentVolume{
|
||||||
|
ObjectMeta: api.ObjectMeta{Name: name},
|
||||||
|
Spec: api.PersistentVolumeSpec{
|
||||||
|
PersistentVolumeSource: api.PersistentVolumeSource{HostPath: &api.HostPathVolumeSource{Path: path}},
|
||||||
|
Capacity: api.ResourceList{api.ResourceName(api.ResourceStorage): resource.MustParse(cap)},
|
||||||
|
AccessModes: mode,
|
||||||
|
PersistentVolumeReclaimPolicy: reclaim,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func createPVC(name, cap string, mode []api.PersistentVolumeAccessMode) *api.PersistentVolumeClaim {
|
||||||
|
return &api.PersistentVolumeClaim{
|
||||||
|
ObjectMeta: api.ObjectMeta{Name: name},
|
||||||
|
Spec: api.PersistentVolumeClaimSpec{
|
||||||
|
Resources: api.ResourceRequirements{Requests: api.ResourceList{api.ResourceName(api.ResourceStorage): resource.MustParse(cap)}},
|
||||||
|
AccessModes: mode,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user