k8s.io/kubernetes@v1.29.3/test/e2e/storage/flexvolume.go (about) 1 /* 2 Copyright 2017 The Kubernetes Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package storage 18 19 import ( 20 "context" 21 "fmt" 22 "net" 23 "path" 24 25 "time" 26 27 "github.com/onsi/ginkgo/v2" 28 v1 "k8s.io/api/core/v1" 29 clientset "k8s.io/client-go/kubernetes" 30 "k8s.io/kubernetes/test/e2e/feature" 31 "k8s.io/kubernetes/test/e2e/framework" 32 e2enode "k8s.io/kubernetes/test/e2e/framework/node" 33 e2epod "k8s.io/kubernetes/test/e2e/framework/pod" 34 e2eskipper "k8s.io/kubernetes/test/e2e/framework/skipper" 35 e2essh "k8s.io/kubernetes/test/e2e/framework/ssh" 36 e2etestfiles "k8s.io/kubernetes/test/e2e/framework/testfiles" 37 e2evolume "k8s.io/kubernetes/test/e2e/framework/volume" 38 "k8s.io/kubernetes/test/e2e/storage/utils" 39 admissionapi "k8s.io/pod-security-admission/api" 40 ) 41 42 const ( 43 driverDir = "test/e2e/testing-manifests/flexvolume/" 44 defaultVolumePluginDir = "/usr/libexec/kubernetes/kubelet-plugins/volume/exec" 45 // TODO: change this and config-test.sh when default flex volume install path is changed for GCI 46 // On gci, root is read-only and controller-manager containerized. Assume 47 // controller-manager has started with --flex-volume-plugin-dir equal to this 48 // (see cluster/gce/config-test.sh) 49 gciVolumePluginDir = "/home/kubernetes/flexvolume" 50 detachTimeout = 10 * time.Second 51 ) 52 53 // testFlexVolume tests that a client pod using a given flexvolume driver 54 // successfully mounts it and runs 55 func testFlexVolume(ctx context.Context, driver string, config e2evolume.TestConfig, f *framework.Framework) { 56 tests := []e2evolume.Test{ 57 { 58 Volume: v1.VolumeSource{ 59 FlexVolume: &v1.FlexVolumeSource{ 60 Driver: "k8s/" + driver, 61 }, 62 }, 63 File: "index.html", 64 // Must match content of examples/volumes/flexvolume/dummy(-attachable) domount 65 ExpectedContent: "Hello from flexvolume!", 66 }, 67 } 68 e2evolume.TestVolumeClient(ctx, f, config, nil, "" /* fsType */, tests) 69 } 70 71 // installFlex installs the driver found at filePath on the node, and restarts 72 // kubelet if 'restart' is true. If node is nil, installs on the master, and restarts 73 // controller-manager if 'restart' is true. 74 func installFlex(ctx context.Context, c clientset.Interface, node *v1.Node, vendor, driver, filePath string) { 75 flexDir := getFlexDir(c, node, vendor, driver) 76 flexFile := path.Join(flexDir, driver) 77 78 host := "" 79 var err error 80 if node != nil { 81 host, err = e2enode.GetSSHExternalIP(node) 82 if err != nil { 83 host, err = e2enode.GetSSHInternalIP(node) 84 } 85 } else { 86 instanceWithPort := framework.APIAddress() 87 hostName := getHostFromHostPort(instanceWithPort) 88 host = net.JoinHostPort(hostName, e2essh.SSHPort) 89 } 90 91 framework.ExpectNoError(err) 92 93 cmd := fmt.Sprintf("sudo mkdir -p %s", flexDir) 94 sshAndLog(ctx, cmd, host, true /*failOnError*/) 95 96 data, err := e2etestfiles.Read(filePath) 97 if err != nil { 98 framework.Fail(err.Error()) 99 } 100 cmd = fmt.Sprintf("sudo tee <<'EOF' %s\n%s\nEOF", flexFile, string(data)) 101 sshAndLog(ctx, cmd, host, true /*failOnError*/) 102 103 cmd = fmt.Sprintf("sudo chmod +x %s", flexFile) 104 sshAndLog(ctx, cmd, host, true /*failOnError*/) 105 } 106 107 func uninstallFlex(ctx context.Context, c clientset.Interface, node *v1.Node, vendor, driver string) { 108 flexDir := getFlexDir(c, node, vendor, driver) 109 110 host := "" 111 var err error 112 if node != nil { 113 host, err = e2enode.GetSSHExternalIP(node) 114 if err != nil { 115 host, err = e2enode.GetSSHInternalIP(node) 116 } 117 } else { 118 instanceWithPort := framework.APIAddress() 119 hostName := getHostFromHostPort(instanceWithPort) 120 host = net.JoinHostPort(hostName, e2essh.SSHPort) 121 } 122 123 if host == "" { 124 framework.Failf("Error getting node ip : %v", err) 125 } 126 127 cmd := fmt.Sprintf("sudo rm -r %s", flexDir) 128 sshAndLog(ctx, cmd, host, false /*failOnError*/) 129 } 130 131 func getFlexDir(c clientset.Interface, node *v1.Node, vendor, driver string) string { 132 volumePluginDir := defaultVolumePluginDir 133 if framework.ProviderIs("gce") { 134 volumePluginDir = gciVolumePluginDir 135 } 136 flexDir := path.Join(volumePluginDir, fmt.Sprintf("/%s~%s/", vendor, driver)) 137 return flexDir 138 } 139 140 func sshAndLog(ctx context.Context, cmd, host string, failOnError bool) { 141 result, err := e2essh.SSH(ctx, cmd, host, framework.TestContext.Provider) 142 e2essh.LogResult(result) 143 framework.ExpectNoError(err) 144 if result.Code != 0 && failOnError { 145 framework.Failf("%s returned non-zero, stderr: %s", cmd, result.Stderr) 146 } 147 } 148 149 func getHostFromHostPort(hostPort string) string { 150 // try to split host and port 151 var host string 152 var err error 153 if host, _, err = net.SplitHostPort(hostPort); err != nil { 154 // if SplitHostPort returns an error, the entire hostport is considered as host 155 host = hostPort 156 } 157 return host 158 } 159 160 var _ = utils.SIGDescribe("Flexvolumes", func() { 161 f := framework.NewDefaultFramework("flexvolume") 162 f.NamespacePodSecurityLevel = admissionapi.LevelBaseline 163 164 // note that namespace deletion is handled by delete-namespace flag 165 166 var cs clientset.Interface 167 var ns *v1.Namespace 168 var node *v1.Node 169 var config e2evolume.TestConfig 170 var suffix string 171 172 ginkgo.BeforeEach(func(ctx context.Context) { 173 e2eskipper.SkipUnlessProviderIs("gce", "local") 174 e2eskipper.SkipUnlessMasterOSDistroIs("debian", "ubuntu", "gci", "custom") 175 e2eskipper.SkipUnlessNodeOSDistroIs("debian", "ubuntu", "gci", "custom") 176 e2eskipper.SkipUnlessSSHKeyPresent() 177 178 cs = f.ClientSet 179 ns = f.Namespace 180 var err error 181 node, err = e2enode.GetRandomReadySchedulableNode(ctx, f.ClientSet) 182 framework.ExpectNoError(err) 183 config = e2evolume.TestConfig{ 184 Namespace: ns.Name, 185 Prefix: "flex", 186 ClientNodeSelection: e2epod.NodeSelection{Name: node.Name}, 187 } 188 suffix = ns.Name 189 }) 190 191 ginkgo.It("should be mountable when non-attachable", func(ctx context.Context) { 192 driver := "dummy" 193 driverInstallAs := driver + "-" + suffix 194 195 ginkgo.By(fmt.Sprintf("installing flexvolume %s on node %s as %s", path.Join(driverDir, driver), node.Name, driverInstallAs)) 196 installFlex(ctx, cs, node, "k8s", driverInstallAs, path.Join(driverDir, driver)) 197 198 testFlexVolume(ctx, driverInstallAs, config, f) 199 200 ginkgo.By(fmt.Sprintf("uninstalling flexvolume %s from node %s", driverInstallAs, node.Name)) 201 uninstallFlex(ctx, cs, node, "k8s", driverInstallAs) 202 }) 203 204 f.It("should be mountable when attachable", feature.Flexvolumes, func(ctx context.Context) { 205 driver := "dummy-attachable" 206 driverInstallAs := driver + "-" + suffix 207 208 ginkgo.By(fmt.Sprintf("installing flexvolume %s on node %s as %s", path.Join(driverDir, driver), node.Name, driverInstallAs)) 209 installFlex(ctx, cs, node, "k8s", driverInstallAs, path.Join(driverDir, driver)) 210 ginkgo.By(fmt.Sprintf("installing flexvolume %s on master as %s", path.Join(driverDir, driver), driverInstallAs)) 211 installFlex(ctx, cs, nil, "k8s", driverInstallAs, path.Join(driverDir, driver)) 212 213 testFlexVolume(ctx, driverInstallAs, config, f) 214 215 // Detach might occur after pod deletion. Wait before deleting driver. 216 time.Sleep(detachTimeout) 217 218 ginkgo.By(fmt.Sprintf("uninstalling flexvolume %s from node %s", driverInstallAs, node.Name)) 219 uninstallFlex(ctx, cs, node, "k8s", driverInstallAs) 220 ginkgo.By(fmt.Sprintf("uninstalling flexvolume %s from master", driverInstallAs)) 221 uninstallFlex(ctx, cs, nil, "k8s", driverInstallAs) 222 }) 223 })