From a113d8ac4170245e5a3347318532a50eeabb552b Mon Sep 17 00:00:00 2001 From: jeff vance Date: Thu, 1 Jun 2017 19:21:14 -0700 Subject: [PATCH] volume i/o tests for storage plugins --- test/e2e/common/volumes.go | 86 +--- test/e2e/framework/volume_util.go | 111 +++++ test/e2e/kubelet.go | 22 +- test/e2e/storage/BUILD | 1 + test/e2e/storage/empty_dir_wrapper.go | 6 +- .../storage/persistent_volumes-disruptive.go | 16 +- test/e2e/storage/persistent_volumes.go | 21 +- test/e2e/storage/volume_io.go | 436 ++++++++++++++++++ test/e2e/storage/volumes.go | 114 +---- test/e2e_node/summary_test.go | 53 +-- 10 files changed, 613 insertions(+), 253 deletions(-) create mode 100644 test/e2e/storage/volume_io.go diff --git a/test/e2e/common/volumes.go b/test/e2e/common/volumes.go index c1ad60d1080..e3aedccf2c9 100644 --- a/test/e2e/common/volumes.go +++ b/test/e2e/common/volumes.go @@ -44,11 +44,11 @@ package common import ( "k8s.io/api/core/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" clientset "k8s.io/client-go/kubernetes" "k8s.io/kubernetes/test/e2e/framework" . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" ) // These tests need privileged containers, which are disabled by default. Run @@ -73,27 +73,15 @@ var _ = framework.KubeDescribe("GCP Volumes", func() { //////////////////////////////////////////////////////////////////////// // NFS //////////////////////////////////////////////////////////////////////// - framework.KubeDescribe("NFSv4", func() { It("should be mountable for NFSv4 [sig-storage]", func() { - - config := framework.VolumeTestConfig{ - Namespace: namespace.Name, - Prefix: "nfs", - ServerImage: framework.NfsServerImage, - ServerPorts: []int{2049}, - } - + config, _, serverIP := framework.NewNFSServer(c, namespace.Name, []string{}) defer func() { if clean { framework.VolumeTestCleanup(f, config) } }() - pod := framework.StartVolumeServer(c, config) - serverIP := pod.Status.PodIP - framework.Logf("NFS server IP address: %v", serverIP) - tests := []framework.VolumeTest{ { Volume: v1.VolumeSource{ @@ -115,21 +103,13 @@ var _ = framework.KubeDescribe("GCP Volumes", func() { framework.KubeDescribe("NFSv3", func() { It("should be mountable for NFSv3 [sig-storage]", func() { - config := framework.VolumeTestConfig{ - Namespace: namespace.Name, - Prefix: "nfs", - ServerImage: framework.NfsServerImage, - ServerPorts: []int{2049}, - } - + config, _, serverIP := framework.NewNFSServer(c, namespace.Name, []string{}) defer func() { if clean { framework.VolumeTestCleanup(f, config) } }() - pod := framework.StartVolumeServer(c, config) - serverIP := pod.Status.PodIP - framework.Logf("NFS server IP address: %v", serverIP) + tests := []framework.VolumeTest{ { Volume: v1.VolumeSource{ @@ -151,70 +131,24 @@ var _ = framework.KubeDescribe("GCP Volumes", func() { //////////////////////////////////////////////////////////////////////// // Gluster //////////////////////////////////////////////////////////////////////// - framework.KubeDescribe("GlusterFS", func() { It("should be mountable [sig-storage]", func() { - config := framework.VolumeTestConfig{ - Namespace: namespace.Name, - Prefix: "gluster", - ServerImage: framework.GlusterfsServerImage, - ServerPorts: []int{24007, 24008, 49152}, - } - + // create gluster server and endpoints + config, _, _ := framework.NewGlusterfsServer(c, namespace.Name) + name := config.Prefix + "-server" defer func() { if clean { framework.VolumeTestCleanup(f, config) + err := c.Core().Endpoints(namespace.Name).Delete(name, nil) + Expect(err).NotTo(HaveOccurred(), "defer: Gluster delete endpoints failed") } }() - pod := framework.StartVolumeServer(c, config) - serverIP := pod.Status.PodIP - framework.Logf("Gluster server IP address: %v", serverIP) - - // create Endpoints for the server - endpoints := v1.Endpoints{ - TypeMeta: metav1.TypeMeta{ - Kind: "Endpoints", - APIVersion: "v1", - }, - ObjectMeta: metav1.ObjectMeta{ - Name: config.Prefix + "-server", - }, - Subsets: []v1.EndpointSubset{ - { - Addresses: []v1.EndpointAddress{ - { - IP: serverIP, - }, - }, - Ports: []v1.EndpointPort{ - { - Name: "gluster", - Port: 24007, - Protocol: v1.ProtocolTCP, - }, - }, - }, - }, - } - - endClient := f.ClientSet.CoreV1().Endpoints(config.Namespace) - - defer func() { - if clean { - endClient.Delete(config.Prefix+"-server", nil) - } - }() - - if _, err := endClient.Create(&endpoints); err != nil { - framework.Failf("Failed to create endpoints for Gluster server: %v", err) - } - tests := []framework.VolumeTest{ { Volume: v1.VolumeSource{ Glusterfs: &v1.GlusterfsVolumeSource{ - EndpointsName: config.Prefix + "-server", + EndpointsName: name, // 'test_vol' comes from test/images/volumes-tester/gluster/run_gluster.sh Path: "test_vol", ReadOnly: true, diff --git a/test/e2e/framework/volume_util.go b/test/e2e/framework/volume_util.go index bb04989e1d1..9ff2a8bf4ac 100644 --- a/test/e2e/framework/volume_util.go +++ b/test/e2e/framework/volume_util.go @@ -64,6 +64,17 @@ const ( BusyBoxImage string = "gcr.io/google_containers/busybox:1.24" ) +const ( + Kb int64 = 1000 + Mb int64 = 1000 * Kb + Gb int64 = 1000 * Mb + Tb int64 = 1000 * Gb + KiB int64 = 1024 + MiB int64 = 1024 * KiB + GiB int64 = 1024 * MiB + TiB int64 = 1024 * GiB +) + // Configuration of one tests. The test consist of: // - server pod - runs serverImage, exports ports[] // - client pod - does not need any special configuration @@ -97,6 +108,106 @@ type VolumeTest struct { ExpectedContent string } +// NFS-specific wrapper for CreateStorageServer. +func NewNFSServer(cs clientset.Interface, namespace string, args []string) (config VolumeTestConfig, pod *v1.Pod, ip string) { + config = VolumeTestConfig{ + Namespace: namespace, + Prefix: "nfs", + ServerImage: NfsServerImage, + ServerPorts: []int{2049}, + } + if len(args) > 0 { + config.ServerArgs = args + } + pod, ip = CreateStorageServer(cs, config) + return config, pod, ip +} + +// GlusterFS-specific wrapper for CreateStorageServer. Also creates the gluster endpoints object. +func NewGlusterfsServer(cs clientset.Interface, namespace string) (config VolumeTestConfig, pod *v1.Pod, ip string) { + config = VolumeTestConfig{ + Namespace: namespace, + Prefix: "gluster", + ServerImage: GlusterfsServerImage, + ServerPorts: []int{24007, 24008, 49152}, + } + pod, ip = CreateStorageServer(cs, config) + + By("creating Gluster endpoints") + endpoints := &v1.Endpoints{ + TypeMeta: metav1.TypeMeta{ + Kind: "Endpoints", + APIVersion: "v1", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: config.Prefix + "-server", + }, + Subsets: []v1.EndpointSubset{ + { + Addresses: []v1.EndpointAddress{ + { + IP: ip, + }, + }, + Ports: []v1.EndpointPort{ + { + Name: "gluster", + Port: 24007, + Protocol: v1.ProtocolTCP, + }, + }, + }, + }, + } + endpoints, err := cs.Core().Endpoints(namespace).Create(endpoints) + Expect(err).NotTo(HaveOccurred(), "failed to create endpoints for Gluster server") + + return config, pod, ip +} + +// iSCSI-specific wrapper for CreateStorageServer. +func NewISCSIServer(cs clientset.Interface, namespace string) (config VolumeTestConfig, pod *v1.Pod, ip string) { + config = VolumeTestConfig{ + Namespace: namespace, + Prefix: "iscsi", + ServerImage: IscsiServerImage, + ServerPorts: []int{3260}, + ServerVolumes: map[string]string{ + // iSCSI container needs to insert modules from the host + "/lib/modules": "/lib/modules", + }, + } + pod, ip = CreateStorageServer(cs, config) + return config, pod, ip +} + +// CephRBD-specific wrapper for CreateStorageServer. +func NewRBDServer(cs clientset.Interface, namespace string) (config VolumeTestConfig, pod *v1.Pod, ip string) { + config = VolumeTestConfig{ + Namespace: namespace, + Prefix: "rbd", + ServerImage: RbdServerImage, + ServerPorts: []int{6789}, + ServerVolumes: map[string]string{ + "/lib/modules": "/lib/modules", + }, + } + pod, ip = CreateStorageServer(cs, config) + return config, pod, ip +} + +// Wrapper for StartVolumeServer(). A storage server config is passed in, and a pod pointer +// and ip address string are returned. +// Note: Expect() is called so no error is returned. +func CreateStorageServer(cs clientset.Interface, config VolumeTestConfig) (pod *v1.Pod, ip string) { + pod = StartVolumeServer(cs, config) + Expect(pod).NotTo(BeNil(), "storage server pod should not be nil") + ip = pod.Status.PodIP + Expect(len(ip)).NotTo(BeZero(), fmt.Sprintf("pod %s's IP should not be empty", pod.Name)) + Logf("%s server pod IP address: %s", config.Prefix, ip) + return pod, ip +} + // Starts a container specified by config.serverImage and exports all // config.serverPorts from it. The returned pod should be used to get the server // IP address and create appropriate VolumeSource. diff --git a/test/e2e/kubelet.go b/test/e2e/kubelet.go index 582700022b0..37b05d81b31 100644 --- a/test/e2e/kubelet.go +++ b/test/e2e/kubelet.go @@ -130,19 +130,6 @@ func updateNodeLabels(c clientset.Interface, nodeNames sets.String, toAdd, toRem } } -// Calls startVolumeServer to create and run a nfs-server pod. Returns server pod and its -// ip address. -// Note: startVolumeServer() waits for the nfs-server pod to be Running and sleeps some -// so that the nfs server can start up. -func createNfsServerPod(c clientset.Interface, config framework.VolumeTestConfig) (*v1.Pod, string) { - pod := framework.StartVolumeServer(c, config) - Expect(pod).NotTo(BeNil()) - ip := pod.Status.PodIP - Expect(len(ip)).NotTo(BeZero()) - framework.Logf("NFS server IP address: %v", ip) - return pod, ip -} - // Restart the passed-in nfs-server by issuing a `/usr/sbin/rpc.nfsd 1` command in the // pod's (only) container. This command changes the number of nfs server threads from // (presumably) zero back to 1, and therefore allows nfs to open connections again. @@ -431,14 +418,7 @@ var _ = framework.KubeDescribe("kubelet", func() { BeforeEach(func() { framework.SkipUnlessProviderIs(framework.ProvidersWithSSH...) - NFSconfig = framework.VolumeTestConfig{ - Namespace: ns, - Prefix: "nfs", - ServerImage: framework.NfsServerImage, - ServerPorts: []int{2049}, - ServerArgs: []string{"-G", "777", "/exports"}, - } - nfsServerPod, nfsIP = createNfsServerPod(c, NFSconfig) + NFSconfig, nfsServerPod, nfsIP = framework.NewNFSServer(c, ns, []string{"-G", "777", "/exports"}) }) AfterEach(func() { diff --git a/test/e2e/storage/BUILD b/test/e2e/storage/BUILD index 03d915f9299..0de56a0211d 100644 --- a/test/e2e/storage/BUILD +++ b/test/e2e/storage/BUILD @@ -20,6 +20,7 @@ go_library( "persistent_volumes-vsphere.go", "pv_reclaimpolicy.go", "pvc_label_selector.go", + "volume_io.go", "volume_provisioning.go", "volumes.go", "vsphere_utils.go", diff --git a/test/e2e/storage/empty_dir_wrapper.go b/test/e2e/storage/empty_dir_wrapper.go index c8c2045523b..f61d42168ce 100644 --- a/test/e2e/storage/empty_dir_wrapper.go +++ b/test/e2e/storage/empty_dir_wrapper.go @@ -53,7 +53,7 @@ const ( var _ = SIGDescribe("EmptyDir wrapper volumes", func() { f := framework.NewDefaultFramework("emptydir-wrapper") - It("should not conflict [Volume]", func() { + It("should not conflict", func() { name := "emptydir-wrapper-test-" + string(uuid.NewUUID()) volumeName := "secret-volume" volumeMountPath := "/etc/secret-volume" @@ -152,7 +152,7 @@ var _ = SIGDescribe("EmptyDir wrapper volumes", func() { // but these cases are harder because tmpfs-based emptyDir // appears to be less prone to the race problem. - It("should not cause race condition when used for configmaps [Serial] [Slow] [Volume]", func() { + It("should not cause race condition when used for configmaps [Serial] [Slow]", func() { configMapNames := createConfigmapsForRace(f) defer deleteConfigMaps(f, configMapNames) volumes, volumeMounts := makeConfigMapVolumes(configMapNames) @@ -161,7 +161,7 @@ var _ = SIGDescribe("EmptyDir wrapper volumes", func() { } }) - It("should not cause race condition when used for git_repo [Serial] [Slow] [Volume]", func() { + It("should not cause race condition when used for git_repo [Serial] [Slow]", func() { gitURL, gitRepo, cleanup := createGitServer(f) defer cleanup() volumes, volumeMounts := makeGitRepoVolumes(gitURL, gitRepo) diff --git a/test/e2e/storage/persistent_volumes-disruptive.go b/test/e2e/storage/persistent_volumes-disruptive.go index 55bf7a508df..a91c3489bfa 100644 --- a/test/e2e/storage/persistent_volumes-disruptive.go +++ b/test/e2e/storage/persistent_volumes-disruptive.go @@ -60,6 +60,7 @@ var _ = SIGDescribe("PersistentVolumes[Disruptive][Flaky]", func() { volLabel labels.Set selector *metav1.LabelSelector ) + BeforeEach(func() { // To protect the NFS volume pod from the kubelet restart, we isolate it on its own node. framework.SkipUnlessNodeCountIsAtLeast(MinNodes) @@ -69,14 +70,8 @@ var _ = SIGDescribe("PersistentVolumes[Disruptive][Flaky]", func() { ns = f.Namespace.Name volLabel = labels.Set{framework.VolumeSelectorKey: ns} selector = metav1.SetAsLabelSelector(volLabel) - // Start the NFS server pod. - framework.Logf("[BeforeEach] Creating NFS Server Pod") - nfsServerPod = initNFSserverPod(c, ns) - framework.Logf("NFS server Pod %q created on Node %q", nfsServerPod.Name, nfsServerPod.Spec.NodeName) - framework.Logf("[BeforeEach] Configuring PersistentVolume") - nfsServerIP = nfsServerPod.Status.PodIP - Expect(nfsServerIP).NotTo(BeEmpty()) + _, nfsServerPod, nfsServerIP = framework.NewNFSServer(c, ns, []string{"-G", "777", "/exports"}) nfsPVconfig = framework.PersistentVolumeConfig{ NamePrefix: "nfs-", Labels: volLabel, @@ -108,25 +103,29 @@ var _ = SIGDescribe("PersistentVolumes[Disruptive][Flaky]", func() { Expect(clientNodeIP).NotTo(BeEmpty()) } }) + AfterEach(func() { framework.DeletePodWithWait(f, c, nfsServerPod) }) - Context("when kubelet restarts", func() { + Context("when kubelet restarts", func() { var ( clientPod *v1.Pod pv *v1.PersistentVolume pvc *v1.PersistentVolumeClaim ) + BeforeEach(func() { framework.Logf("Initializing test spec") clientPod, pv, pvc = initTestCase(f, c, nfsPVconfig, pvcConfig, ns, clientNode.Name) }) + AfterEach(func() { framework.Logf("Tearing down test spec") tearDownTestCase(c, f, ns, clientPod, pvc, pv) pv, pvc, clientPod = nil, nil, nil }) + // Test table housing the It() title string and test spec. runTest is type testBody, defined at // the start of this file. To add tests, define a function mirroring the testBody signature and assign // to runTest. @@ -140,6 +139,7 @@ var _ = SIGDescribe("PersistentVolumes[Disruptive][Flaky]", func() { runTest: testVolumeUnmountsFromDeletedPod, }, } + // Test loop executes each disruptiveTest iteratively. for _, test := range disruptiveTestTable { func(t disruptiveTest) { diff --git a/test/e2e/storage/persistent_volumes.go b/test/e2e/storage/persistent_volumes.go index 38b80eb1655..36dd8fff561 100644 --- a/test/e2e/storage/persistent_volumes.go +++ b/test/e2e/storage/persistent_volumes.go @@ -85,18 +85,6 @@ func completeMultiTest(f *framework.Framework, c clientset.Interface, ns string, return nil } -// initNFSserverPod wraps volumes.go's startVolumeServer to return a running nfs host pod -// commonly used by persistent volume testing -func initNFSserverPod(c clientset.Interface, ns string) *v1.Pod { - return framework.StartVolumeServer(c, framework.VolumeTestConfig{ - Namespace: ns, - Prefix: "nfs", - ServerImage: framework.NfsServerImage, - ServerPorts: []int{2049}, - ServerArgs: []string{"-G", "777", "/exports"}, - }) -} - var _ = SIGDescribe("PersistentVolumes", func() { // global vars for the Context()s and It()'s below @@ -131,10 +119,7 @@ var _ = SIGDescribe("PersistentVolumes", func() { ) BeforeEach(func() { - framework.Logf("[BeforeEach] Creating NFS Server Pod") - nfsServerPod = initNFSserverPod(c, ns) - serverIP = nfsServerPod.Status.PodIP - framework.Logf("[BeforeEach] Configuring PersistentVolume") + _, nfsServerPod, serverIP = framework.NewNFSServer(c, ns, []string{"-G", "777", "/exports"}) pvConfig = framework.PersistentVolumeConfig{ NamePrefix: "nfs-", Labels: volLabel, @@ -218,6 +203,10 @@ var _ = SIGDescribe("PersistentVolumes", func() { // a) pre-binding, b) create pvcs before pvs, c) create pvcs and pods // in different namespaces. Context("with multiple PVs and PVCs all in same ns", func() { + + // define the maximum number of PVs and PVCs supported by these tests + const maxNumPVs = 10 + const maxNumPVCs = 10 // scope the pv and pvc maps to be available in the AfterEach // note: these maps are created fresh in CreatePVsPVCs() var pvols framework.PVMap diff --git a/test/e2e/storage/volume_io.go b/test/e2e/storage/volume_io.go new file mode 100644 index 00000000000..a05c69df586 --- /dev/null +++ b/test/e2e/storage/volume_io.go @@ -0,0 +1,436 @@ +/* +Copyright 2017 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +/* + * This test checks that the plugin VolumeSources are working when pseudo-streaming + * various write sizes to mounted files. Note that the plugin is defined inline in + * the pod spec, not via a persistent volume and claim. + * + * These tests work only when privileged containers are allowed, exporting various + * filesystems (NFS, GlusterFS, ...) usually needs some mounting or other privileged + * magic in the server pod. Note that the server containers are for testing purposes + * only and should not be used in production. + */ + +package storage + +import ( + "fmt" + "math" + "path" + "strconv" + "strings" + + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" + "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + clientset "k8s.io/client-go/kubernetes" + "k8s.io/kubernetes/test/e2e/framework" +) + +const minFileSize = 1 * framework.MiB + +// Return the plugin's client pod spec. Use an InitContainer to setup the file i/o test env. +func makePodSpec(config framework.VolumeTestConfig, dir, initCmd string, volsrc v1.VolumeSource, podSecContext *v1.PodSecurityContext) *v1.Pod { + volName := fmt.Sprintf("%s-%s", config.Prefix, "io-volume") + + return &v1.Pod{ + TypeMeta: metav1.TypeMeta{ + Kind: "Pod", + APIVersion: "v1", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: config.Prefix + "-io-client", + Labels: map[string]string{ + "role": config.Prefix + "-io-client", + }, + }, + Spec: v1.PodSpec{ + InitContainers: []v1.Container{ + { + Name: config.Prefix + "-io-init", + Image: framework.BusyBoxImage, + Command: []string{ + "/bin/sh", + "-c", + initCmd, + }, + VolumeMounts: []v1.VolumeMount{ + { + Name: volName, + MountPath: dir, + }, + }, + }, + }, + Containers: []v1.Container{ + { + Name: config.Prefix + "-io-client", + Image: framework.BusyBoxImage, + Command: []string{ + "/bin/sh", + "-c", + "sleep 3600", // keep pod alive until explicitly deleted + }, + VolumeMounts: []v1.VolumeMount{ + { + Name: volName, + MountPath: dir, + }, + }, + }, + }, + SecurityContext: podSecContext, + Volumes: []v1.Volume{ + { + Name: volName, + VolumeSource: volsrc, + }, + }, + RestartPolicy: v1.RestartPolicyNever, // want pod to fail if init container fails + }, + } +} + +// Write `fsize` bytes to `fpath` in the pod, using dd and the `dd_input` file. +func writeToFile(pod *v1.Pod, fpath, dd_input string, fsize int64) error { + By(fmt.Sprintf("writing %d bytes to test file %s", fsize, fpath)) + loopCnt := fsize / minFileSize + writeCmd := fmt.Sprintf("i=0; while [ $i -lt %d ]; do dd if=%s bs=%d >>%s 2>/dev/null; let i+=1; done", loopCnt, dd_input, minFileSize, fpath) + _, err := podExec(pod, writeCmd) + + return err +} + +// Verify that the test file is the expected size and contains the expected content. +func verifyFile(pod *v1.Pod, fpath string, expectSize int64, dd_input string) error { + By("verifying file size") + rtnstr, err := podExec(pod, fmt.Sprintf("stat -c %%s %s", fpath)) + if err != nil || rtnstr == "" { + return fmt.Errorf("unable to get file size via `stat %s`: %v", fpath, err) + } + size, err := strconv.Atoi(strings.TrimSuffix(rtnstr, "\n")) + if err != nil { + return fmt.Errorf("unable to convert string %q to int: %v", rtnstr, err) + } + if int64(size) != expectSize { + return fmt.Errorf("size of file %s is %d, expected %d", fpath, size, expectSize) + } + + By("verifying file content") + // use `grep ... -f` rather than the expected content in a variable to reduce logging + rtnstr, err = podExec(pod, fmt.Sprintf("grep -c -m1 -f %s %s", dd_input, fpath)) + if err != nil { + return fmt.Errorf("unable to test file content via `grep %s`: %v", fpath, err) + } + foundCnt, err := strconv.Atoi(strings.TrimSuffix(rtnstr, "\n")) + if err != nil { + return fmt.Errorf("unable to convert string %q to int: %v", rtnstr, err) + } + if foundCnt == 0 { + rtnstr, err = podExec(pod, fmt.Sprintf("cat %s", dd_input)) + if err != nil || len(rtnstr) == 0 { + return fmt.Errorf("string not found in file %s and unable to read dd's input file %s: %v", fpath, dd_input, err) + } + return fmt.Errorf("string %q not found in file %s", rtnstr, fpath) + } + + return nil +} + +// Delete `fpath` to save some disk space on host. Delete errors are logged but ignored. +func deleteFile(pod *v1.Pod, fpath string) { + By(fmt.Sprintf("deleting test file %s...", fpath)) + _, err := podExec(pod, fmt.Sprintf("rm -f %s", fpath)) + if err != nil { + // keep going, the test dir will be deleted when the volume is unmounted + framework.Logf("unable to delete test file %s: %v\nerror ignored, continuing test", fpath, err) + } +} + +// Create the client pod and create files of the sizes passed in by the `fsizes` parameter. Delete the +// client pod and the new files when done. +// Note: the file name is appended to "/opt//", eg. "/opt/nfs/e2e-.../". +// Note: nil can be passed for the podSecContext parm, in which case it is ignored. +// Note: `fsizes` values are enforced to each be at least `minFileSize` and a multiple of `minFileSize` +// bytes. +func testVolumeIO(f *framework.Framework, cs clientset.Interface, config framework.VolumeTestConfig, volsrc v1.VolumeSource, podSecContext *v1.PodSecurityContext, file string, fsizes []int64) (err error) { + dir := path.Join("/opt", config.Prefix, config.Namespace) + dd_input := path.Join(dir, "dd_if") + writeBlk := strings.Repeat("abcdefghijklmnopqrstuvwxyz123456", 32) // 1KiB value + loopCnt := minFileSize / int64(len(writeBlk)) + // initContainer cmd to create and fill dd's input file. The initContainer is used to create + // the `dd` input file which is currently 1MiB. Rather than store a 1MiB go value, a loop is + // used to create a 1MiB file in the target directory. + initCmd := fmt.Sprintf("i=0; while [ $i -lt %d ]; do echo -n %s >>%s; let i+=1; done", loopCnt, writeBlk, dd_input) + + clientPod := makePodSpec(config, dir, initCmd, volsrc, podSecContext) + + By(fmt.Sprintf("starting %s", clientPod.Name)) + podsNamespacer := cs.CoreV1().Pods(config.Namespace) + clientPod, err = podsNamespacer.Create(clientPod) + if err != nil { + return fmt.Errorf("failed to create client pod %q: %v", clientPod.Name, err) + } + defer func() { + // note the test dir will be removed when the kubelet unmounts it + By(fmt.Sprintf("deleting client pod %q...", clientPod.Name)) + e := framework.DeletePodWithWait(f, cs, clientPod) + if e != nil { + framework.Logf("client pod failed to delete: %v", e) + if err == nil { // delete err is returned if err is not set + err = e + } + } + }() + + err = framework.WaitForPodRunningInNamespace(cs, clientPod) + if err != nil { + return fmt.Errorf("client pod %q not running: %v", clientPod.Name, err) + } + + // create files of the passed-in file sizes and verify test file size and content + for _, fsize := range fsizes { + // file sizes must be a multiple of `minFileSize` + if math.Mod(float64(fsize), float64(minFileSize)) != 0 { + fsize = fsize/minFileSize + minFileSize + } + fpath := path.Join(dir, fmt.Sprintf("%s-%d", file, fsize)) + if err = writeToFile(clientPod, fpath, dd_input, fsize); err != nil { + return err + } + if err = verifyFile(clientPod, fpath, fsize, dd_input); err != nil { + return err + } + deleteFile(clientPod, fpath) + } + + return +} + +// These tests need privileged containers which are disabled by default. +// TODO: support all of the plugins tested in storage/volumes.go +var _ = SIGDescribe("Volume plugin streaming [Slow]", func() { + f := framework.NewDefaultFramework("volume-io") + var ( + config framework.VolumeTestConfig + cs clientset.Interface + ns string + serverIP string + serverPod *v1.Pod + volSource v1.VolumeSource + ) + + BeforeEach(func() { + cs = f.ClientSet + ns = f.Namespace.Name + }) + + //////////////////////////////////////////////////////////////////////// + // NFS + //////////////////////////////////////////////////////////////////////// + SIGDescribe("NFS", func() { + testFile := "nfs_io_test" + // client pod uses selinux + podSec := v1.PodSecurityContext{ + SELinuxOptions: &v1.SELinuxOptions{ + Level: "s0:c0,c1", + }, + } + + BeforeEach(func() { + config, serverPod, serverIP = framework.NewNFSServer(cs, ns, []string{}) + volSource = v1.VolumeSource{ + NFS: &v1.NFSVolumeSource{ + Server: serverIP, + Path: "/", + ReadOnly: false, + }, + } + }) + + AfterEach(func() { + framework.Logf("AfterEach: deleting NFS server pod %q...", serverPod.Name) + err := framework.DeletePodWithWait(f, cs, serverPod) + Expect(err).NotTo(HaveOccurred(), "AfterEach: NFS server pod failed to delete") + }) + + It("should write files of various sizes, verify size, validate content", func() { + fileSizes := []int64{1 * framework.MiB, 100 * framework.MiB, 1 * framework.GiB} + err := testVolumeIO(f, cs, config, volSource, &podSec, testFile, fileSizes) + Expect(err).NotTo(HaveOccurred()) + }) + }) + + //////////////////////////////////////////////////////////////////////// + // Gluster + //////////////////////////////////////////////////////////////////////// + SIGDescribe("GlusterFS", func() { + var name string + testFile := "gluster_io_test" + + BeforeEach(func() { + framework.SkipUnlessNodeOSDistroIs("gci") + // create gluster server and endpoints + config, serverPod, serverIP = framework.NewGlusterfsServer(cs, ns) + name = config.Prefix + "-server" + volSource = v1.VolumeSource{ + Glusterfs: &v1.GlusterfsVolumeSource{ + EndpointsName: name, + // 'test_vol' comes from test/images/volumes-tester/gluster/run_gluster.sh + Path: "test_vol", + ReadOnly: false, + }, + } + }) + + AfterEach(func() { + framework.Logf("AfterEach: deleting Gluster endpoints %q...", name) + epErr := cs.Core().Endpoints(ns).Delete(name, nil) + framework.Logf("AfterEach: deleting Gluster server pod %q...", serverPod.Name) + err := framework.DeletePodWithWait(f, cs, serverPod) + if epErr != nil || err != nil { + if epErr != nil { + framework.Logf("AfterEach: Gluster delete endpoints failed: %v", err) + } + if err != nil { + framework.Logf("AfterEach: Gluster server pod delete failed: %v", err) + } + framework.Failf("AfterEach: cleanup failed") + } + }) + + It("should write files of various sizes, verify size, validate content", func() { + fileSizes := []int64{1 * framework.MiB, 100 * framework.MiB} + err := testVolumeIO(f, cs, config, volSource, nil /*no secContext*/, testFile, fileSizes) + Expect(err).NotTo(HaveOccurred()) + }) + }) + + //////////////////////////////////////////////////////////////////////// + // iSCSI + // The iscsiadm utility and iscsi target kernel modules must be installed on all nodes. + //////////////////////////////////////////////////////////////////////// + SIGDescribe("iSCSI [Feature:Volumes]", func() { + testFile := "iscsi_io_test" + + BeforeEach(func() { + config, serverPod, serverIP = framework.NewISCSIServer(cs, ns) + volSource = v1.VolumeSource{ + ISCSI: &v1.ISCSIVolumeSource{ + TargetPortal: serverIP + ":3260", + // from test/images/volumes-tester/iscsi/initiatorname.iscsi + IQN: "iqn.2003-01.org.linux-iscsi.f21.x8664:sn.4b0aae584f7c", + Lun: 0, + FSType: "ext2", + ReadOnly: false, + }, + } + }) + + AfterEach(func() { + framework.Logf("AfterEach: deleting iSCSI server pod %q...", serverPod.Name) + err := framework.DeletePodWithWait(f, cs, serverPod) + Expect(err).NotTo(HaveOccurred(), "AfterEach: iSCSI server pod failed to delete") + }) + + It("should write files of various sizes, verify size, validate content", func() { + fileSizes := []int64{1 * framework.MiB, 100 * framework.MiB} + fsGroup := int64(1234) + podSec := v1.PodSecurityContext{ + FSGroup: &fsGroup, + } + err := testVolumeIO(f, cs, config, volSource, &podSec, testFile, fileSizes) + Expect(err).NotTo(HaveOccurred()) + }) + }) + + //////////////////////////////////////////////////////////////////////// + // Ceph RBD + //////////////////////////////////////////////////////////////////////// + SIGDescribe("Ceph-RBD [Feature:Volumes]", func() { + var ( + secret *v1.Secret + name string + ) + testFile := "ceph-rbd_io_test" + + BeforeEach(func() { + config, serverPod, serverIP = framework.NewRBDServer(cs, ns) + name = config.Prefix + "-server" + + // create server secret + secret = &v1.Secret{ + TypeMeta: metav1.TypeMeta{ + Kind: "Secret", + APIVersion: "v1", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: name, + }, + Data: map[string][]byte{ + // from test/images/volumes-tester/rbd/keyring + "key": []byte("AQDRrKNVbEevChAAEmRC+pW/KBVHxa0w/POILA=="), + }, + Type: "kubernetes.io/rbd", + } + var err error + secret, err = cs.Core().Secrets(ns).Create(secret) + Expect(err).NotTo(HaveOccurred(), fmt.Sprintf("BeforeEach: failed to create secret %q for Ceph-RBD: %v", name, err)) + + volSource = v1.VolumeSource{ + RBD: &v1.RBDVolumeSource{ + CephMonitors: []string{serverIP}, + RBDPool: "rbd", + RBDImage: "foo", + RadosUser: "admin", + SecretRef: &v1.LocalObjectReference{ + Name: name, + }, + FSType: "ext2", + ReadOnly: true, + }, + } + }) + + AfterEach(func() { + framework.Logf("AfterEach: deleting Ceph-RDB server secret %q...", name) + secErr := cs.Core().Secrets(ns).Delete(name, &metav1.DeleteOptions{}) + framework.Logf("AfterEach: deleting Ceph-RDB server pod %q...", serverPod.Name) + err := framework.DeletePodWithWait(f, cs, serverPod) + if secErr != nil || err != nil { + if secErr != nil { + framework.Logf("AfterEach: Ceph-RDB delete secret failed: %v", err) + } + if err != nil { + framework.Logf("AfterEach: Ceph-RDB server pod delete failed: %v", err) + } + framework.Failf("AfterEach: cleanup failed") + } + }) + + It("should write files of various sizes, verify size, validate content", func() { + fileSizes := []int64{1 * framework.MiB, 100 * framework.MiB} + fsGroup := int64(1234) + podSec := v1.PodSecurityContext{ + FSGroup: &fsGroup, + } + err := testVolumeIO(f, cs, config, volSource, &podSec, testFile, fileSizes) + Expect(err).NotTo(HaveOccurred()) + }) + }) +}) diff --git a/test/e2e/storage/volumes.go b/test/e2e/storage/volumes.go index 8a7bd08588d..4a58ed17509 100644 --- a/test/e2e/storage/volumes.go +++ b/test/e2e/storage/volumes.go @@ -85,7 +85,7 @@ var _ = SIGDescribe("Volumes", func() { // If 'false', the test won't clear its volumes upon completion. Useful for debugging, // note that namespace deletion is handled by delete-namespace flag clean := true - // filled in BeforeEach + // filled inside BeforeEach var cs clientset.Interface var namespace *v1.Namespace @@ -100,21 +100,12 @@ var _ = SIGDescribe("Volumes", func() { SIGDescribe("NFS", func() { It("should be mountable", func() { - config := framework.VolumeTestConfig{ - Namespace: namespace.Name, - Prefix: "nfs", - ServerImage: framework.NfsServerImage, - ServerPorts: []int{2049}, - } - + config, _, serverIP := framework.NewNFSServer(cs, namespace.Name, []string{}) defer func() { if clean { framework.VolumeTestCleanup(f, config) } }() - pod := framework.StartVolumeServer(cs, config) - serverIP := pod.Status.PodIP - framework.Logf("NFS server IP address: %v", serverIP) tests := []framework.VolumeTest{ { @@ -138,71 +129,26 @@ var _ = SIGDescribe("Volumes", func() { // Gluster //////////////////////////////////////////////////////////////////////// - SIGDescribe("GlusterFS [Feature:Volumes]", func() { + SIGDescribe("GlusterFS", func() { It("should be mountable", func() { //TODO (copejon) GFS is not supported on debian image. framework.SkipUnlessNodeOSDistroIs("gci") - - config := framework.VolumeTestConfig{ - Namespace: namespace.Name, - Prefix: "gluster", - ServerImage: framework.GlusterfsServerImage, - ServerPorts: []int{24007, 24008, 49152}, - } - + // create gluster server and endpoints + config, _, _ := framework.NewGlusterfsServer(cs, namespace.Name) + name := config.Prefix + "-server" defer func() { if clean { framework.VolumeTestCleanup(f, config) + err := cs.Core().Endpoints(namespace.Name).Delete(name, nil) + Expect(err).NotTo(HaveOccurred(), "defer: Gluster delete endpoints failed") } }() - pod := framework.StartVolumeServer(cs, config) - serverIP := pod.Status.PodIP - framework.Logf("Gluster server IP address: %v", serverIP) - - // create Endpoints for the server - endpoints := v1.Endpoints{ - TypeMeta: metav1.TypeMeta{ - Kind: "Endpoints", - APIVersion: "v1", - }, - ObjectMeta: metav1.ObjectMeta{ - Name: config.Prefix + "-server", - }, - Subsets: []v1.EndpointSubset{ - { - Addresses: []v1.EndpointAddress{ - { - IP: serverIP, - }, - }, - Ports: []v1.EndpointPort{ - { - Name: "gluster", - Port: 24007, - Protocol: v1.ProtocolTCP, - }, - }, - }, - }, - } - - endClient := cs.Core().Endpoints(config.Namespace) - - defer func() { - if clean { - endClient.Delete(config.Prefix+"-server", nil) - } - }() - - if _, err := endClient.Create(&endpoints); err != nil { - framework.Failf("Failed to create endpoints for Gluster server: %v", err) - } tests := []framework.VolumeTest{ { Volume: v1.VolumeSource{ Glusterfs: &v1.GlusterfsVolumeSource{ - EndpointsName: config.Prefix + "-server", + EndpointsName: name, // 'test_vol' comes from test/images/volumes-tester/gluster/run_gluster.sh Path: "test_vol", ReadOnly: true, @@ -228,25 +174,12 @@ var _ = SIGDescribe("Volumes", func() { SIGDescribe("iSCSI [Feature:Volumes]", func() { It("should be mountable", func() { - config := framework.VolumeTestConfig{ - Namespace: namespace.Name, - Prefix: "iscsi", - ServerImage: framework.IscsiServerImage, - ServerPorts: []int{3260}, - ServerVolumes: map[string]string{ - // iSCSI container needs to insert modules from the host - "/lib/modules": "/lib/modules", - }, - } - + config, _, serverIP := framework.NewISCSIServer(cs, namespace.Name) defer func() { if clean { framework.VolumeTestCleanup(f, config) } }() - pod := framework.StartVolumeServer(cs, config) - serverIP := pod.Status.PodIP - framework.Logf("iSCSI server IP address: %v", serverIP) tests := []framework.VolumeTest{ { @@ -275,26 +208,12 @@ var _ = SIGDescribe("Volumes", func() { SIGDescribe("Ceph RBD [Feature:Volumes]", func() { It("should be mountable", func() { - config := framework.VolumeTestConfig{ - Namespace: namespace.Name, - Prefix: "rbd", - ServerImage: framework.RbdServerImage, - ServerPorts: []int{6789}, - ServerVolumes: map[string]string{ - // iSCSI container needs to insert modules from the host - "/lib/modules": "/lib/modules", - "/sys": "/sys", - }, - } - + config, _, serverIP := framework.NewRBDServer(cs, namespace.Name) defer func() { if clean { framework.VolumeTestCleanup(f, config) } }() - pod := framework.StartVolumeServer(cs, config) - serverIP := pod.Status.PodIP - framework.Logf("Ceph server IP address: %v", serverIP) // create secrets for the server secret := v1.Secret{ @@ -347,10 +266,10 @@ var _ = SIGDescribe("Volumes", func() { framework.TestVolumeClient(cs, config, &fsGroup, tests) }) }) + //////////////////////////////////////////////////////////////////////// // Ceph //////////////////////////////////////////////////////////////////////// - SIGDescribe("CephFS [Feature:Volumes]", func() { It("should be mountable", func() { config := framework.VolumeTestConfig{ @@ -365,9 +284,7 @@ var _ = SIGDescribe("Volumes", func() { framework.VolumeTestCleanup(f, config) } }() - pod := framework.StartVolumeServer(cs, config) - serverIP := pod.Status.PodIP - framework.Logf("Ceph server IP address: %v", serverIP) + _, serverIP := framework.CreateStorageServer(cs, config) By("sleeping a bit to give ceph server time to initialize") time.Sleep(20 * time.Second) @@ -428,7 +345,6 @@ var _ = SIGDescribe("Volumes", func() { // (/usr/bin/nova, /usr/bin/cinder and /usr/bin/keystone) // and that the usual OpenStack authentication env. variables are set // (OS_USERNAME, OS_PASSWORD, OS_TENANT_NAME at least). - SIGDescribe("Cinder [Feature:Volumes]", func() { It("should be mountable", func() { framework.SkipUnlessProviderIs("openstack") @@ -504,7 +420,6 @@ var _ = SIGDescribe("Volumes", func() { //////////////////////////////////////////////////////////////////////// // GCE PD //////////////////////////////////////////////////////////////////////// - SIGDescribe("PD", func() { // Flaky issue: #43977 It("should be mountable [Flaky]", func() { @@ -558,7 +473,6 @@ var _ = SIGDescribe("Volumes", func() { //////////////////////////////////////////////////////////////////////// // ConfigMap //////////////////////////////////////////////////////////////////////// - SIGDescribe("ConfigMap", func() { It("should be mountable", func() { config := framework.VolumeTestConfig{ @@ -636,7 +550,6 @@ var _ = SIGDescribe("Volumes", func() { //////////////////////////////////////////////////////////////////////// // vSphere //////////////////////////////////////////////////////////////////////// - SIGDescribe("vsphere [Feature:Volumes]", func() { It("should be mountable", func() { framework.SkipUnlessProviderIs("vsphere") @@ -686,6 +599,7 @@ var _ = SIGDescribe("Volumes", func() { framework.TestVolumeClient(cs, config, &fsGroup, tests) }) }) + //////////////////////////////////////////////////////////////////////// // Azure Disk //////////////////////////////////////////////////////////////////////// diff --git a/test/e2e_node/summary_test.go b/test/e2e_node/summary_test.go index 6f12ff07f83..4b5084b7091 100644 --- a/test/e2e_node/summary_test.go +++ b/test/e2e_node/summary_test.go @@ -72,15 +72,10 @@ var _ = framework.KubeDescribe("Summary API", func() { // Setup expectations. const ( - kb int64 = 1000 - mb int64 = 1000 * kb - gb int64 = 1000 * mb - tb int64 = 1000 * gb - maxStartAge = time.Hour * 24 * 365 // 1 year maxStatsAge = time.Minute ) - fsCapacityBounds := bounded(100*mb, 100*gb) + fsCapacityBounds := bounded(100*framework.Mb, 100*framework.Gb) // Expectations for system containers. sysContExpectations := func() types.GomegaMatcher { return gstruct.MatchAllFields(gstruct.Fields{ @@ -95,8 +90,8 @@ var _ = framework.KubeDescribe("Summary API", func() { "Time": recent(maxStatsAge), // We don't limit system container memory. "AvailableBytes": BeNil(), - "UsageBytes": bounded(1*mb, 10*gb), - "WorkingSetBytes": bounded(1*mb, 10*gb), + "UsageBytes": bounded(1*framework.Mb, 10*framework.Gb), + "WorkingSetBytes": bounded(1*framework.Mb, 10*framework.Gb), // today, this returns the value reported // in /sys/fs/cgroup/memory.stat for rss // this value should really return /sys/fs/cgroup/memory.stat total_rss @@ -104,7 +99,7 @@ var _ = framework.KubeDescribe("Summary API", func() { // for now, i am updating the bounding box to the value as coded, but the // value reported needs to change. // rss only makes sense if you are leaf cgroup - "RSSBytes": bounded(0, 1*gb), + "RSSBytes": bounded(0, 1*framework.Gb), "PageFaults": bounded(1000, 1E9), "MajorPageFaults": bounded(0, 100000), }), @@ -126,9 +121,9 @@ var _ = framework.KubeDescribe("Summary API", func() { "Time": recent(maxStatsAge), // We don't limit system container memory. "AvailableBytes": BeNil(), - "UsageBytes": bounded(100*kb, 10*gb), - "WorkingSetBytes": bounded(100*kb, 10*gb), - "RSSBytes": bounded(100*kb, 1*gb), + "UsageBytes": bounded(100*framework.Kb, 10*framework.Gb), + "WorkingSetBytes": bounded(100*framework.Kb, 10*framework.Gb), + "RSSBytes": bounded(100*framework.Kb, 1*framework.Gb), "PageFaults": bounded(1000, 1E9), "MajorPageFaults": bounded(0, 100000), }) @@ -149,10 +144,10 @@ var _ = framework.KubeDescribe("Summary API", func() { }), "Memory": ptrMatchAllFields(gstruct.Fields{ "Time": recent(maxStatsAge), - "AvailableBytes": bounded(10*kb, 10*mb), - "UsageBytes": bounded(10*kb, 20*mb), - "WorkingSetBytes": bounded(10*kb, 20*mb), - "RSSBytes": bounded(1*kb, mb), + "AvailableBytes": bounded(1*framework.Kb, 10*framework.Mb), + "UsageBytes": bounded(10*framework.Kb, 20*framework.Mb), + "WorkingSetBytes": bounded(10*framework.Kb, 20*framework.Mb), + "RSSBytes": bounded(1*framework.Kb, framework.Mb), "PageFaults": bounded(100, 1000000), "MajorPageFaults": bounded(0, 10), }), @@ -160,7 +155,7 @@ var _ = framework.KubeDescribe("Summary API", func() { "Time": recent(maxStatsAge), "AvailableBytes": fsCapacityBounds, "CapacityBytes": fsCapacityBounds, - "UsedBytes": bounded(kb, 10*mb), + "UsedBytes": bounded(framework.Kb, 10*framework.Mb), "InodesFree": bounded(1E4, 1E8), "Inodes": bounded(1E4, 1E8), "InodesUsed": bounded(0, 1E8), @@ -169,7 +164,7 @@ var _ = framework.KubeDescribe("Summary API", func() { "Time": recent(maxStatsAge), "AvailableBytes": fsCapacityBounds, "CapacityBytes": fsCapacityBounds, - "UsedBytes": bounded(kb, 10*mb), + "UsedBytes": bounded(framework.Kb, 10*framework.Mb), "InodesFree": bounded(1E4, 1E8), "Inodes": bounded(1E4, 1E8), "InodesUsed": bounded(0, 1E8), @@ -179,9 +174,9 @@ var _ = framework.KubeDescribe("Summary API", func() { }), "Network": ptrMatchAllFields(gstruct.Fields{ "Time": recent(maxStatsAge), - "RxBytes": bounded(10, 10*mb), + "RxBytes": bounded(10, 10*framework.Mb), "RxErrors": bounded(0, 1000), - "TxBytes": bounded(10, 10*mb), + "TxBytes": bounded(10, 10*framework.Mb), "TxErrors": bounded(0, 1000), }), "VolumeStats": gstruct.MatchAllElements(summaryObjectID, gstruct.Elements{ @@ -191,7 +186,7 @@ var _ = framework.KubeDescribe("Summary API", func() { "Time": recent(maxStatsAge), "AvailableBytes": fsCapacityBounds, "CapacityBytes": fsCapacityBounds, - "UsedBytes": bounded(kb, 1*mb), + "UsedBytes": bounded(framework.Kb, 1*framework.Mb), "InodesFree": bounded(1E4, 1E8), "Inodes": bounded(1E4, 1E8), "InodesUsed": bounded(0, 1E8), @@ -211,9 +206,9 @@ var _ = framework.KubeDescribe("Summary API", func() { }), "Memory": ptrMatchAllFields(gstruct.Fields{ "Time": recent(maxStatsAge), - "AvailableBytes": bounded(100*mb, 100*gb), - "UsageBytes": bounded(10*mb, 10*gb), - "WorkingSetBytes": bounded(10*mb, 10*gb), + "AvailableBytes": bounded(100*framework.Mb, 100*framework.Gb), + "UsageBytes": bounded(10*framework.Mb, 10*framework.Gb), + "WorkingSetBytes": bounded(10*framework.Mb, 10*framework.Gb), // today, this returns the value reported // in /sys/fs/cgroup/memory.stat for rss // this value should really return /sys/fs/cgroup/memory.stat total_rss @@ -221,16 +216,16 @@ var _ = framework.KubeDescribe("Summary API", func() { // for now, i am updating the bounding box to the value as coded, but the // value reported needs to change. // rss only makes sense if you are leaf cgroup - "RSSBytes": bounded(0, 1*gb), + "RSSBytes": bounded(0, 1*framework.Gb), "PageFaults": bounded(1000, 1E9), "MajorPageFaults": bounded(0, 100000), }), // TODO(#28407): Handle non-eth0 network interface names. "Network": Or(BeNil(), ptrMatchAllFields(gstruct.Fields{ "Time": recent(maxStatsAge), - "RxBytes": bounded(1*mb, 100*gb), + "RxBytes": bounded(1*framework.Mb, 100*framework.Gb), "RxErrors": bounded(0, 100000), - "TxBytes": bounded(10*kb, 10*gb), + "TxBytes": bounded(10*framework.Kb, 10*framework.Gb), "TxErrors": bounded(0, 100000), })), "Fs": ptrMatchAllFields(gstruct.Fields{ @@ -238,7 +233,7 @@ var _ = framework.KubeDescribe("Summary API", func() { "AvailableBytes": fsCapacityBounds, "CapacityBytes": fsCapacityBounds, // we assume we are not running tests on machines < 10tb of disk - "UsedBytes": bounded(kb, 10*tb), + "UsedBytes": bounded(framework.Kb, 10*framework.Tb), "InodesFree": bounded(1E4, 1E8), "Inodes": bounded(1E4, 1E8), "InodesUsed": bounded(0, 1E8), @@ -249,7 +244,7 @@ var _ = framework.KubeDescribe("Summary API", func() { "AvailableBytes": fsCapacityBounds, "CapacityBytes": fsCapacityBounds, // we assume we are not running tests on machines < 10tb of disk - "UsedBytes": bounded(kb, 10*tb), + "UsedBytes": bounded(framework.Kb, 10*framework.Tb), "InodesFree": bounded(1E4, 1E8), "Inodes": bounded(1E4, 1E8), "InodesUsed": bounded(0, 1E8),