Add OpenStack Cinder E2E test.
The test follows the example of test/e2e/pd.go. It expects, that the machine that runs the test has OpenStack client tools installed and also that the usual OpenStack authentication env. variables are set [1] The test creates a new volume, inserts index.html there and checks, that another pod can read it. The volume is deleted afterwards. The test is disabled by default and it must be explicitly enabled. 1: http://docs.openstack.org/cli-reference/content/cli_openrc.html
This commit is contained in:
parent
7178921a96
commit
5678fa83d9
@ -15,8 +15,11 @@ limitations under the License.
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* This test checks that various VolumeSources are working. For each volume
|
* This test checks that various VolumeSources are working.
|
||||||
* type it creates a server pod, exporting simple 'index.html' file.
|
*
|
||||||
|
* There are two ways, how to test the volumes:
|
||||||
|
* 1) With containerized server (NFS, Ceph, Gluster, iSCSI, ...)
|
||||||
|
* The test creates a server pod, exporting simple 'index.html' file.
|
||||||
* Then it uses appropriate VolumeSource to import this file into a client pod
|
* Then it uses appropriate VolumeSource to import this file into a client pod
|
||||||
* and checks that the pod can see the file. It does so by importing the file
|
* and checks that the pod can see the file. It does so by importing the file
|
||||||
* into web server root and loadind the index.html from it.
|
* into web server root and loadind the index.html from it.
|
||||||
@ -27,18 +30,26 @@ limitations under the License.
|
|||||||
*
|
*
|
||||||
* Note that the server containers are for testing purposes only and should not
|
* Note that the server containers are for testing purposes only and should not
|
||||||
* be used in production.
|
* be used in production.
|
||||||
|
*
|
||||||
|
* 2) With server outside of Kubernetes (Cinder, ...)
|
||||||
|
* Appropriate server (e.g. OpenStack Cinder) must exist somewhere outside
|
||||||
|
* the tested Kubernetes cluster. The test itself creates a new volume,
|
||||||
|
* and checks, that Kubernetes can use it as a volume.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package e2e
|
package e2e
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"os/exec"
|
||||||
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"k8s.io/kubernetes/pkg/api"
|
"k8s.io/kubernetes/pkg/api"
|
||||||
"k8s.io/kubernetes/pkg/api/unversioned"
|
"k8s.io/kubernetes/pkg/api/unversioned"
|
||||||
client "k8s.io/kubernetes/pkg/client/unversioned"
|
client "k8s.io/kubernetes/pkg/client/unversioned"
|
||||||
|
|
||||||
|
"github.com/golang/glog"
|
||||||
. "github.com/onsi/ginkgo"
|
. "github.com/onsi/ginkgo"
|
||||||
. "github.com/onsi/gomega"
|
. "github.com/onsi/gomega"
|
||||||
)
|
)
|
||||||
@ -218,6 +229,80 @@ func testVolumeClient(client *client.Client, config VolumeTestConfig, volume api
|
|||||||
Expect(body).To(ContainSubstring(expectedContent))
|
Expect(body).To(ContainSubstring(expectedContent))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Insert index.html with given content into given volume. It does so by
|
||||||
|
// starting and auxiliary pod which writes the file there.
|
||||||
|
// The volume must be writable.
|
||||||
|
func injectHtml(client *client.Client, config VolumeTestConfig, volume api.VolumeSource, content string) {
|
||||||
|
By(fmt.Sprint("starting ", config.prefix, " injector"))
|
||||||
|
podClient := client.Pods(config.namespace)
|
||||||
|
|
||||||
|
injectPod := &api.Pod{
|
||||||
|
TypeMeta: unversioned.TypeMeta{
|
||||||
|
Kind: "Pod",
|
||||||
|
APIVersion: "v1",
|
||||||
|
},
|
||||||
|
ObjectMeta: api.ObjectMeta{
|
||||||
|
Name: config.prefix + "-injector",
|
||||||
|
Labels: map[string]string{
|
||||||
|
"role": config.prefix + "-injector",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Spec: api.PodSpec{
|
||||||
|
Containers: []api.Container{
|
||||||
|
{
|
||||||
|
Name: config.prefix + "-injector",
|
||||||
|
Image: "gcr.io/google_containers/busybox",
|
||||||
|
Command: []string{"/bin/sh"},
|
||||||
|
Args: []string{"-c", "echo '" + content + "' > /mnt/index.html && chmod o+rX /mnt /mnt/index.html"},
|
||||||
|
VolumeMounts: []api.VolumeMount{
|
||||||
|
{
|
||||||
|
Name: config.prefix + "-volume",
|
||||||
|
MountPath: "/mnt",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
RestartPolicy: api.RestartPolicyNever,
|
||||||
|
Volumes: []api.Volume{
|
||||||
|
{
|
||||||
|
Name: config.prefix + "-volume",
|
||||||
|
VolumeSource: volume,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
defer func() {
|
||||||
|
podClient.Delete(config.prefix+"-injector", nil)
|
||||||
|
}()
|
||||||
|
|
||||||
|
injectPod, err := podClient.Create(injectPod)
|
||||||
|
expectNoError(err, "Failed to create injector pod: %v", err)
|
||||||
|
err = waitForPodSuccessInNamespace(client, injectPod.Name, injectPod.Spec.Containers[0].Name, injectPod.Namespace)
|
||||||
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
}
|
||||||
|
|
||||||
|
func deleteCinderVolume(name string) error {
|
||||||
|
// Try to delete the volume for several seconds - it takes
|
||||||
|
// a while for the plugin to detach it.
|
||||||
|
var output []byte
|
||||||
|
var err error
|
||||||
|
timeout := time.Second * 120
|
||||||
|
|
||||||
|
Logf("Waiting up to %v for removal of cinder volume %s", timeout, name)
|
||||||
|
for start := time.Now(); time.Since(start) < timeout; time.Sleep(5 * time.Second) {
|
||||||
|
output, err = exec.Command("cinder", "delete", name).CombinedOutput()
|
||||||
|
if err == nil {
|
||||||
|
Logf("Cinder volume %s deleted", name)
|
||||||
|
return nil
|
||||||
|
} else {
|
||||||
|
Logf("Failed to delete volume %s: %v", name, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Logf("Giving up deleting volume %s: %v\n%s", name, err, string(output[:]))
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
var _ = Describe("Volumes", func() {
|
var _ = Describe("Volumes", func() {
|
||||||
clean := true // If 'false', the test won't clear its namespace (and pods and services) upon completion. Useful for debugging.
|
clean := true // If 'false', the test won't clear its namespace (and pods and services) upon completion. Useful for debugging.
|
||||||
|
|
||||||
@ -549,4 +634,77 @@ var _ = Describe("Volumes", func() {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////
|
||||||
|
// OpenStack Cinder
|
||||||
|
////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
// Marked with [Skipped] to skip the test by default (see driver.go),
|
||||||
|
// The test assumes that OpenStack client tools are installed
|
||||||
|
// (/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).
|
||||||
|
|
||||||
|
Describe("[Skipped] Cinder", func() {
|
||||||
|
It("should be mountable", func() {
|
||||||
|
config := VolumeTestConfig{
|
||||||
|
namespace: namespace.Name,
|
||||||
|
prefix: "cinder",
|
||||||
|
}
|
||||||
|
|
||||||
|
// We assume that namespace.Name is a random string
|
||||||
|
volumeName := namespace.Name
|
||||||
|
By("creating a test Cinder volume")
|
||||||
|
output, err := exec.Command("cinder", "create", "--display-name="+volumeName, "1").CombinedOutput()
|
||||||
|
outputString := string(output[:])
|
||||||
|
Logf("cinder output:\n%s", outputString)
|
||||||
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
|
||||||
|
defer func() {
|
||||||
|
// Ignore any cleanup errors, there is not much we can do about
|
||||||
|
// them. They were already logged.
|
||||||
|
deleteCinderVolume(volumeName)
|
||||||
|
}()
|
||||||
|
|
||||||
|
// Parse 'id'' from stdout. Expected format:
|
||||||
|
// | attachments | [] |
|
||||||
|
// | availability_zone | nova |
|
||||||
|
// ...
|
||||||
|
// | id | 1d6ff08f-5d1c-41a4-ad72-4ef872cae685 |
|
||||||
|
volumeID := ""
|
||||||
|
for _, line := range strings.Split(outputString, "\n") {
|
||||||
|
fields := strings.Fields(line)
|
||||||
|
if len(fields) != 5 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if fields[1] != "id" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
volumeID = fields[3]
|
||||||
|
break
|
||||||
|
}
|
||||||
|
Logf("Volume ID: %s", volumeID)
|
||||||
|
Expect(volumeID).NotTo(Equal(""))
|
||||||
|
|
||||||
|
defer func() {
|
||||||
|
if clean {
|
||||||
|
Logf("Running volumeTestCleanup")
|
||||||
|
volumeTestCleanup(c, config)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
volume := api.VolumeSource{
|
||||||
|
Cinder: &api.CinderVolumeSource{
|
||||||
|
VolumeID: volumeID,
|
||||||
|
FSType: "ext3",
|
||||||
|
ReadOnly: false,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
// Insert index.html into the test volume with some random content
|
||||||
|
// to make sure we don't see the content from previous test runs.
|
||||||
|
content := "Hello from Cinder from namespace " + volumeName
|
||||||
|
injectHtml(c, config, volume, content)
|
||||||
|
|
||||||
|
testVolumeClient(c, config, volume, content)
|
||||||
|
})
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
Loading…
Reference in New Issue
Block a user