k3s/test/e2e/storage/flexvolume.go

228 lines
7.5 KiB
Go
Raw Normal View History

/*
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.
*/
package storage
import (
"fmt"
"math/rand"
"net"
"path"
"time"
2019-05-10 05:56:26 +00:00
"github.com/onsi/ginkgo"
v1 "k8s.io/api/core/v1"
apierrs "k8s.io/apimachinery/pkg/api/errors"
clientset "k8s.io/client-go/kubernetes"
"k8s.io/kubernetes/test/e2e/framework"
e2essh "k8s.io/kubernetes/test/e2e/framework/ssh"
e2e: abstract access to additional files The new test/e2e/framework/testfiles package makes it possible to write tests that do not depend on a specific way of providing additional test files at runtime. Such tests and the framework are then more easily reused in other test suites. In the test/e2e suite file access is enabled based on the existing "repo-root" command line parameter and the built-in bindata. Tests using the new API will first check for files under "repo-root" and then fall back to the builtin data. This way, users of a test binary can modify those files without having to rebuild the binary. "repo-root" is still needed because at least some tests check for additional files (secret.yaml, via ingress_utils.go) that are not part of the upstream source code and thus may or may not be built into a test binary. Tests using bindata or repo-root directly get modified to use the new API, or removed when they are obsolete: test/e2e/examples.go depended on files that were removed in https://github.com/kubernetes/kubernetes/pull/61246 and thus can no longer be run in Kubernetes. Moving the tests to kubernetes/examples is tracked in https://github.com/kubernetes/examples/issues/214. The file removal did not break the automated E2E testing probably because the tests are under the Feature:Example tag and thus not enabled during normal CI runs. Removing also the obsolete tests makes it simpler to rework the "repo-root" setting because less code uses it. Related-to: #66649 and #23987
2018-08-31 14:43:15 +00:00
"k8s.io/kubernetes/test/e2e/framework/testfiles"
"k8s.io/kubernetes/test/e2e/framework/volume"
"k8s.io/kubernetes/test/e2e/storage/utils"
)
const (
sshPort = "22"
driverDir = "test/e2e/testing-manifests/flexvolume/"
defaultVolumePluginDir = "/usr/libexec/kubernetes/kubelet-plugins/volume/exec"
// TODO: change this and config-test.sh when default flex volume install path is changed for GCI
// On gci, root is read-only and controller-manager containerized. Assume
// controller-manager has started with --flex-volume-plugin-dir equal to this
// (see cluster/gce/config-test.sh)
gciVolumePluginDir = "/home/kubernetes/flexvolume"
detachTimeout = 10 * time.Second
)
// testFlexVolume tests that a client pod using a given flexvolume driver
// successfully mounts it and runs
func testFlexVolume(driver string, cs clientset.Interface, config volume.TestConfig, f *framework.Framework) {
tests := []volume.Test{
{
Volume: v1.VolumeSource{
FlexVolume: &v1.FlexVolumeSource{
Driver: "k8s/" + driver,
},
},
File: "index.html",
// Must match content of examples/volumes/flexvolume/dummy(-attachable) domount
ExpectedContent: "Hello from flexvolume!",
},
}
volume.TestVolumeClient(cs, config, nil, "" /* fsType */, tests)
volume.TestCleanup(f, config)
}
// installFlex installs the driver found at filePath on the node, and restarts
// kubelet if 'restart' is true. If node is nil, installs on the master, and restarts
// controller-manager if 'restart' is true.
func installFlex(c clientset.Interface, node *v1.Node, vendor, driver, filePath string) {
flexDir := getFlexDir(c, node, vendor, driver)
flexFile := path.Join(flexDir, driver)
host := ""
var err error
if node != nil {
host, err = framework.GetNodeExternalIP(node)
if err != nil {
host, err = framework.GetNodeInternalIP(node)
}
} else {
masterHostWithPort := framework.GetMasterHost()
hostName := getHostFromHostPort(masterHostWithPort)
host = net.JoinHostPort(hostName, sshPort)
}
framework.ExpectNoError(err)
cmd := fmt.Sprintf("sudo mkdir -p %s", flexDir)
sshAndLog(cmd, host, true /*failOnError*/)
2019-05-10 05:56:26 +00:00
data := testfiles.ReadOrDie(filePath, ginkgo.Fail)
cmd = fmt.Sprintf("sudo tee <<'EOF' %s\n%s\nEOF", flexFile, string(data))
sshAndLog(cmd, host, true /*failOnError*/)
cmd = fmt.Sprintf("sudo chmod +x %s", flexFile)
sshAndLog(cmd, host, true /*failOnError*/)
}
func uninstallFlex(c clientset.Interface, node *v1.Node, vendor, driver string) {
flexDir := getFlexDir(c, node, vendor, driver)
host := ""
var err error
if node != nil {
host, err = framework.GetNodeExternalIP(node)
if err != nil {
host, err = framework.GetNodeInternalIP(node)
}
} else {
masterHostWithPort := framework.GetMasterHost()
hostName := getHostFromHostPort(masterHostWithPort)
host = net.JoinHostPort(hostName, sshPort)
}
if host == "" {
framework.Failf("Error getting node ip : %v", err)
}
cmd := fmt.Sprintf("sudo rm -r %s", flexDir)
sshAndLog(cmd, host, false /*failOnError*/)
}
func getFlexDir(c clientset.Interface, node *v1.Node, vendor, driver string) string {
volumePluginDir := defaultVolumePluginDir
if framework.ProviderIs("gce") {
volumePluginDir = gciVolumePluginDir
}
flexDir := path.Join(volumePluginDir, fmt.Sprintf("/%s~%s/", vendor, driver))
return flexDir
}
func sshAndLog(cmd, host string, failOnError bool) {
result, err := e2essh.SSH(cmd, host, framework.TestContext.Provider)
e2essh.LogResult(result)
framework.ExpectNoError(err)
if result.Code != 0 && failOnError {
framework.Failf("%s returned non-zero, stderr: %s", cmd, result.Stderr)
}
}
func getHostFromHostPort(hostPort string) string {
// try to split host and port
var host string
var err error
if host, _, err = net.SplitHostPort(hostPort); err != nil {
// if SplitHostPort returns an error, the entire hostport is considered as host
host = hostPort
}
return host
}
var _ = utils.SIGDescribe("Flexvolumes", func() {
f := framework.NewDefaultFramework("flexvolume")
// note that namespace deletion is handled by delete-namespace flag
var cs clientset.Interface
var ns *v1.Namespace
var node v1.Node
var config volume.TestConfig
var suffix string
2019-05-10 05:56:26 +00:00
ginkgo.BeforeEach(func() {
framework.SkipUnlessProviderIs("gce", "local")
framework.SkipUnlessMasterOSDistroIs("debian", "ubuntu", "gci", "custom")
framework.SkipUnlessNodeOSDistroIs("debian", "ubuntu", "gci", "custom")
framework.SkipUnlessSSHKeyPresent()
cs = f.ClientSet
ns = f.Namespace
nodes := framework.GetReadySchedulableNodesOrDie(f.ClientSet)
node = nodes.Items[rand.Intn(len(nodes.Items))]
config = volume.TestConfig{
Namespace: ns.Name,
Prefix: "flex",
ClientNodeName: node.Name,
}
suffix = ns.Name
})
2019-05-10 05:56:26 +00:00
ginkgo.It("should be mountable when non-attachable", func() {
driver := "dummy"
driverInstallAs := driver + "-" + suffix
2019-05-10 05:56:26 +00:00
ginkgo.By(fmt.Sprintf("installing flexvolume %s on node %s as %s", path.Join(driverDir, driver), node.Name, driverInstallAs))
installFlex(cs, &node, "k8s", driverInstallAs, path.Join(driverDir, driver))
2017-10-30 00:55:51 +00:00
testFlexVolume(driverInstallAs, cs, config, f)
2019-05-10 05:56:26 +00:00
ginkgo.By("waiting for flex client pod to terminate")
if err := f.WaitForPodTerminated(config.Prefix+"-client", ""); !apierrs.IsNotFound(err) {
framework.ExpectNoError(err, "Failed to wait client pod terminated: %v", err)
}
2019-05-10 05:56:26 +00:00
ginkgo.By(fmt.Sprintf("uninstalling flexvolume %s from node %s", driverInstallAs, node.Name))
uninstallFlex(cs, &node, "k8s", driverInstallAs)
})
2019-05-10 05:56:26 +00:00
ginkgo.It("should be mountable when attachable", func() {
driver := "dummy-attachable"
driverInstallAs := driver + "-" + suffix
2019-05-10 05:56:26 +00:00
ginkgo.By(fmt.Sprintf("installing flexvolume %s on node %s as %s", path.Join(driverDir, driver), node.Name, driverInstallAs))
installFlex(cs, &node, "k8s", driverInstallAs, path.Join(driverDir, driver))
2019-05-10 05:56:26 +00:00
ginkgo.By(fmt.Sprintf("installing flexvolume %s on master as %s", path.Join(driverDir, driver), driverInstallAs))
installFlex(cs, nil, "k8s", driverInstallAs, path.Join(driverDir, driver))
2017-10-30 00:55:51 +00:00
testFlexVolume(driverInstallAs, cs, config, f)
2019-05-10 05:56:26 +00:00
ginkgo.By("waiting for flex client pod to terminate")
if err := f.WaitForPodTerminated(config.Prefix+"-client", ""); !apierrs.IsNotFound(err) {
framework.ExpectNoError(err, "Failed to wait client pod terminated: %v", err)
}
// Detach might occur after pod deletion. Wait before deleting driver.
time.Sleep(detachTimeout)
2019-05-10 05:56:26 +00:00
ginkgo.By(fmt.Sprintf("uninstalling flexvolume %s from node %s", driverInstallAs, node.Name))
uninstallFlex(cs, &node, "k8s", driverInstallAs)
2019-05-10 05:56:26 +00:00
ginkgo.By(fmt.Sprintf("uninstalling flexvolume %s from master", driverInstallAs))
uninstallFlex(cs, nil, "k8s", driverInstallAs)
})
})