195 lines
5.5 KiB
Go
195 lines
5.5 KiB
Go
/*
|
|
Copyright 2017 Luis Pabón luis@portworx.com
|
|
|
|
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 sanity
|
|
|
|
import (
|
|
"crypto/rand"
|
|
"fmt"
|
|
"io/ioutil"
|
|
"os"
|
|
"testing"
|
|
|
|
"github.com/kubernetes-csi/csi-test/utils"
|
|
yaml "gopkg.in/yaml.v2"
|
|
|
|
"google.golang.org/grpc"
|
|
|
|
. "github.com/onsi/ginkgo"
|
|
. "github.com/onsi/gomega"
|
|
)
|
|
|
|
// CSISecrets consists of secrets used in CSI credentials.
|
|
type CSISecrets struct {
|
|
CreateVolumeSecret map[string]string `yaml:"CreateVolumeSecret"`
|
|
DeleteVolumeSecret map[string]string `yaml:"DeleteVolumeSecret"`
|
|
ControllerPublishVolumeSecret map[string]string `yaml:"ControllerPublishVolumeSecret"`
|
|
ControllerUnpublishVolumeSecret map[string]string `yaml:"ControllerUnpublishVolumeSecret"`
|
|
NodeStageVolumeSecret map[string]string `yaml:"NodeStageVolumeSecret"`
|
|
NodePublishVolumeSecret map[string]string `yaml:"NodePublishVolumeSecret"`
|
|
CreateSnapshotSecret map[string]string `yaml:"CreateSnapshotSecret"`
|
|
DeleteSnapshotSecret map[string]string `yaml:"DeleteSnapshotSecret"`
|
|
}
|
|
|
|
// Config provides the configuration for the sanity tests. It
|
|
// needs to be initialized by the user of the sanity package.
|
|
type Config struct {
|
|
TargetPath string
|
|
StagingPath string
|
|
Address string
|
|
SecretsFile string
|
|
|
|
TestVolumeSize int64
|
|
TestVolumeParametersFile string
|
|
TestVolumeParameters map[string]string
|
|
}
|
|
|
|
// SanityContext holds the variables that each test can depend on. It
|
|
// gets initialized before each test block runs.
|
|
type SanityContext struct {
|
|
Config *Config
|
|
Conn *grpc.ClientConn
|
|
Secrets *CSISecrets
|
|
|
|
connAddress string
|
|
}
|
|
|
|
// Test will test the CSI driver at the specified address by
|
|
// setting up a Ginkgo suite and running it.
|
|
func Test(t *testing.T, reqConfig *Config) {
|
|
path := reqConfig.TestVolumeParametersFile
|
|
if len(path) != 0 {
|
|
yamlFile, err := ioutil.ReadFile(path)
|
|
if err != nil {
|
|
panic(fmt.Sprintf("failed to read file %q: %v", path, err))
|
|
}
|
|
err = yaml.Unmarshal(yamlFile, &reqConfig.TestVolumeParameters)
|
|
if err != nil {
|
|
panic(fmt.Sprintf("error unmarshaling yaml: %v", err))
|
|
}
|
|
}
|
|
|
|
sc := &SanityContext{
|
|
Config: reqConfig,
|
|
}
|
|
|
|
registerTestsInGinkgo(sc)
|
|
RegisterFailHandler(Fail)
|
|
RunSpecs(t, "CSI Driver Test Suite")
|
|
}
|
|
|
|
func GinkgoTest(reqConfig *Config) {
|
|
sc := &SanityContext{
|
|
Config: reqConfig,
|
|
}
|
|
|
|
registerTestsInGinkgo(sc)
|
|
}
|
|
|
|
func (sc *SanityContext) setup() {
|
|
var err error
|
|
|
|
if len(sc.Config.SecretsFile) > 0 {
|
|
sc.Secrets, err = loadSecrets(sc.Config.SecretsFile)
|
|
Expect(err).NotTo(HaveOccurred())
|
|
} else {
|
|
sc.Secrets = &CSISecrets{}
|
|
}
|
|
|
|
// It is possible that a test sets sc.Config.Address
|
|
// dynamically (and differently!) in a BeforeEach, so only
|
|
// reuse the connection if the address is still the same.
|
|
if sc.Conn == nil || sc.connAddress != sc.Config.Address {
|
|
By("connecting to CSI driver")
|
|
sc.Conn, err = utils.Connect(sc.Config.Address)
|
|
Expect(err).NotTo(HaveOccurred())
|
|
sc.connAddress = sc.Config.Address
|
|
} else {
|
|
By(fmt.Sprintf("reusing connection to CSI driver at %s", sc.connAddress))
|
|
}
|
|
|
|
By("creating mount and staging directories")
|
|
err = createMountTargetLocation(sc.Config.TargetPath)
|
|
Expect(err).NotTo(HaveOccurred())
|
|
if len(sc.Config.StagingPath) > 0 {
|
|
err = createMountTargetLocation(sc.Config.StagingPath)
|
|
Expect(err).NotTo(HaveOccurred())
|
|
}
|
|
}
|
|
|
|
func (sc *SanityContext) teardown() {
|
|
// We intentionally do not close the connection to the CSI
|
|
// driver here because the large amount of connection attempts
|
|
// caused test failures
|
|
// (https://github.com/kubernetes-csi/csi-test/issues/101). We
|
|
// could fix this with retries
|
|
// (https://github.com/kubernetes-csi/csi-test/pull/97) but
|
|
// that requires more discussion, so instead we just connect
|
|
// once per process instead of once per test case. This was
|
|
// also said to be faster
|
|
// (https://github.com/kubernetes-csi/csi-test/pull/98).
|
|
}
|
|
|
|
func createMountTargetLocation(targetPath string) error {
|
|
fileInfo, err := os.Stat(targetPath)
|
|
if err != nil && os.IsNotExist(err) {
|
|
return os.MkdirAll(targetPath, 0755)
|
|
} else if err != nil {
|
|
return err
|
|
}
|
|
if !fileInfo.IsDir() {
|
|
return fmt.Errorf("Target location %s is not a directory", targetPath)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func loadSecrets(path string) (*CSISecrets, error) {
|
|
var creds CSISecrets
|
|
|
|
yamlFile, err := ioutil.ReadFile(path)
|
|
if err != nil {
|
|
return &creds, fmt.Errorf("failed to read file %q: #%v", path, err)
|
|
}
|
|
|
|
err = yaml.Unmarshal(yamlFile, &creds)
|
|
if err != nil {
|
|
return &creds, fmt.Errorf("error unmarshaling yaml: #%v", err)
|
|
}
|
|
|
|
return &creds, nil
|
|
}
|
|
|
|
var uniqueSuffix = "-" + pseudoUUID()
|
|
|
|
// pseudoUUID returns a unique string generated from random
|
|
// bytes, empty string in case of error.
|
|
func pseudoUUID() string {
|
|
b := make([]byte, 8)
|
|
if _, err := rand.Read(b); err != nil {
|
|
// Shouldn't happen?!
|
|
return ""
|
|
}
|
|
return fmt.Sprintf("%08X-%08X", b[0:4], b[4:8])
|
|
}
|
|
|
|
// uniqueString returns a unique string by appending a random
|
|
// number. In case of an error, just the prefix is returned, so it
|
|
// alone should already be fairly unique.
|
|
func uniqueString(prefix string) string {
|
|
return prefix + uniqueSuffix
|
|
}
|