mirror of
https://github.com/ceph/ceph-csi.git
synced 2025-06-13 18:43:34 +00:00
vendor files
This commit is contained in:
72
vendor/k8s.io/kubernetes/pkg/volume/scaleio/BUILD
generated
vendored
Normal file
72
vendor/k8s.io/kubernetes/pkg/volume/scaleio/BUILD
generated
vendored
Normal file
@ -0,0 +1,72 @@
|
||||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
load(
|
||||
"@io_bazel_rules_go//go:def.bzl",
|
||||
"go_library",
|
||||
"go_test",
|
||||
)
|
||||
|
||||
go_test(
|
||||
name = "go_default_test",
|
||||
srcs = [
|
||||
"sio_mgr_test.go",
|
||||
"sio_util_test.go",
|
||||
"sio_volume_test.go",
|
||||
],
|
||||
importpath = "k8s.io/kubernetes/pkg/volume/scaleio",
|
||||
library = ":go_default_library",
|
||||
deps = [
|
||||
"//pkg/util/mount:go_default_library",
|
||||
"//pkg/volume:go_default_library",
|
||||
"//pkg/volume/testing:go_default_library",
|
||||
"//vendor/github.com/codedellemc/goscaleio/types/v1:go_default_library",
|
||||
"//vendor/github.com/golang/glog:go_default_library",
|
||||
"//vendor/k8s.io/api/core/v1:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/types:go_default_library",
|
||||
"//vendor/k8s.io/client-go/kubernetes/fake:go_default_library",
|
||||
"//vendor/k8s.io/client-go/util/testing:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = [
|
||||
"sio_client.go",
|
||||
"sio_mgr.go",
|
||||
"sio_plugin.go",
|
||||
"sio_util.go",
|
||||
"sio_volume.go",
|
||||
],
|
||||
importpath = "k8s.io/kubernetes/pkg/volume/scaleio",
|
||||
deps = [
|
||||
"//pkg/util/keymutex:go_default_library",
|
||||
"//pkg/util/mount:go_default_library",
|
||||
"//pkg/util/strings:go_default_library",
|
||||
"//pkg/volume:go_default_library",
|
||||
"//pkg/volume/util:go_default_library",
|
||||
"//pkg/volume/util/volumehelper:go_default_library",
|
||||
"//vendor/github.com/codedellemc/goscaleio:go_default_library",
|
||||
"//vendor/github.com/codedellemc/goscaleio/types/v1:go_default_library",
|
||||
"//vendor/github.com/golang/glog:go_default_library",
|
||||
"//vendor/k8s.io/api/core/v1:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/api/resource:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/types:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/util/uuid:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "package-srcs",
|
||||
srcs = glob(["**"]),
|
||||
tags = ["automanaged"],
|
||||
visibility = ["//visibility:private"],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "all-srcs",
|
||||
srcs = [":package-srcs"],
|
||||
tags = ["automanaged"],
|
||||
)
|
535
vendor/k8s.io/kubernetes/pkg/volume/scaleio/sio_client.go
generated
vendored
Normal file
535
vendor/k8s.io/kubernetes/pkg/volume/scaleio/sio_client.go
generated
vendored
Normal file
@ -0,0 +1,535 @@
|
||||
/*
|
||||
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 scaleio
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"k8s.io/kubernetes/pkg/util/mount"
|
||||
|
||||
sio "github.com/codedellemc/goscaleio"
|
||||
siotypes "github.com/codedellemc/goscaleio/types/v1"
|
||||
"github.com/golang/glog"
|
||||
)
|
||||
|
||||
var (
|
||||
sioDiskIDPath = "/dev/disk/by-id"
|
||||
)
|
||||
|
||||
type sioVolumeID string
|
||||
|
||||
type sioInterface interface {
|
||||
FindVolume(name string) (*siotypes.Volume, error)
|
||||
Volume(sioVolumeID) (*siotypes.Volume, error)
|
||||
CreateVolume(name string, sizeGB int64) (*siotypes.Volume, error)
|
||||
AttachVolume(sioVolumeID, bool) error
|
||||
DetachVolume(sioVolumeID) error
|
||||
DeleteVolume(sioVolumeID) error
|
||||
IID() (string, error)
|
||||
Devs() (map[string]string, error)
|
||||
WaitForAttachedDevice(token string) (string, error)
|
||||
WaitForDetachedDevice(token string) error
|
||||
GetVolumeRefs(sioVolumeID) (int, error)
|
||||
}
|
||||
|
||||
type sioClient struct {
|
||||
client *sio.Client
|
||||
gateway string
|
||||
username string
|
||||
password string
|
||||
insecure bool
|
||||
certsEnabled bool
|
||||
system *siotypes.System
|
||||
sysName string
|
||||
sysClient *sio.System
|
||||
protectionDomain *siotypes.ProtectionDomain
|
||||
pdName string
|
||||
pdClient *sio.ProtectionDomain
|
||||
storagePool *siotypes.StoragePool
|
||||
spName string
|
||||
spClient *sio.StoragePool
|
||||
provisionMode string
|
||||
sdcPath string
|
||||
sdcGuid string
|
||||
instanceID string
|
||||
inited bool
|
||||
diskRegex *regexp.Regexp
|
||||
mtx sync.Mutex
|
||||
exec mount.Exec
|
||||
}
|
||||
|
||||
func newSioClient(gateway, username, password string, sslEnabled bool, exec mount.Exec) (*sioClient, error) {
|
||||
client := new(sioClient)
|
||||
client.gateway = gateway
|
||||
client.username = username
|
||||
client.password = password
|
||||
client.exec = exec
|
||||
if sslEnabled {
|
||||
client.insecure = false
|
||||
client.certsEnabled = true
|
||||
} else {
|
||||
client.insecure = true
|
||||
client.certsEnabled = false
|
||||
}
|
||||
r, err := regexp.Compile(`^emc-vol-\w*-\w*$`)
|
||||
if err != nil {
|
||||
glog.Error(log("failed to compile regex: %v", err))
|
||||
return nil, err
|
||||
}
|
||||
client.diskRegex = r
|
||||
|
||||
// delay client setup/login until init()
|
||||
return client, nil
|
||||
}
|
||||
|
||||
// init setups client and authenticate
|
||||
func (c *sioClient) init() error {
|
||||
c.mtx.Lock()
|
||||
defer c.mtx.Unlock()
|
||||
if c.inited {
|
||||
return nil
|
||||
}
|
||||
glog.V(4).Infoln(log("initializing scaleio client"))
|
||||
client, err := sio.NewClientWithArgs(c.gateway, "", c.insecure, c.certsEnabled)
|
||||
if err != nil {
|
||||
glog.Error(log("failed to create client: %v", err))
|
||||
return err
|
||||
}
|
||||
c.client = client
|
||||
if _, err = c.client.Authenticate(
|
||||
&sio.ConfigConnect{
|
||||
Endpoint: c.gateway,
|
||||
Version: "",
|
||||
Username: c.username,
|
||||
Password: c.password},
|
||||
); err != nil {
|
||||
glog.Error(log("client authentication failed: %v", err))
|
||||
return err
|
||||
}
|
||||
|
||||
// retrieve system
|
||||
if c.system, err = c.findSystem(c.sysName); err != nil {
|
||||
glog.Error(log("unable to find system %s: %v", c.sysName, err))
|
||||
return err
|
||||
}
|
||||
|
||||
// retrieve protection domain
|
||||
if c.protectionDomain, err = c.findProtectionDomain(c.pdName); err != nil {
|
||||
glog.Error(log("unable to find protection domain %s: %v", c.protectionDomain, err))
|
||||
return err
|
||||
}
|
||||
// retrieve storage pool
|
||||
if c.storagePool, err = c.findStoragePool(c.spName); err != nil {
|
||||
glog.Error(log("unable to find storage pool %s: %v", c.storagePool, err))
|
||||
return err
|
||||
}
|
||||
c.inited = true
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *sioClient) Volumes() ([]*siotypes.Volume, error) {
|
||||
if err := c.init(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
vols, err := c.getVolumes()
|
||||
if err != nil {
|
||||
glog.Error(log("failed to retrieve volumes: %v", err))
|
||||
return nil, err
|
||||
}
|
||||
return vols, nil
|
||||
}
|
||||
|
||||
func (c *sioClient) Volume(id sioVolumeID) (*siotypes.Volume, error) {
|
||||
if err := c.init(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
vols, err := c.getVolumesByID(id)
|
||||
if err != nil {
|
||||
glog.Error(log("failed to retrieve volume by id: %v", err))
|
||||
return nil, err
|
||||
}
|
||||
vol := vols[0]
|
||||
if vol == nil {
|
||||
glog.V(4).Info(log("volume not found, id %s", id))
|
||||
return nil, errors.New("volume not found")
|
||||
}
|
||||
return vol, nil
|
||||
}
|
||||
|
||||
func (c *sioClient) FindVolume(name string) (*siotypes.Volume, error) {
|
||||
if err := c.init(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
glog.V(4).Info(log("searching for volume %s", name))
|
||||
volumes, err := c.getVolumesByName(name)
|
||||
if err != nil {
|
||||
glog.Error(log("failed to find volume by name %v", err))
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, volume := range volumes {
|
||||
if volume.Name == name {
|
||||
glog.V(4).Info(log("found volume %s", name))
|
||||
return volume, nil
|
||||
}
|
||||
}
|
||||
glog.V(4).Info(log("volume not found, name %s", name))
|
||||
return nil, errors.New("volume not found")
|
||||
}
|
||||
|
||||
func (c *sioClient) CreateVolume(name string, sizeGB int64) (*siotypes.Volume, error) {
|
||||
if err := c.init(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
params := &siotypes.VolumeParam{
|
||||
Name: name,
|
||||
VolumeSizeInKb: strconv.Itoa(int(sizeGB) * 1024 * 1024),
|
||||
VolumeType: c.provisionMode,
|
||||
}
|
||||
createResponse, err := c.client.CreateVolume(params, c.storagePool.Name)
|
||||
if err != nil {
|
||||
glog.Error(log("failed to create volume %s: %v", name, err))
|
||||
return nil, err
|
||||
}
|
||||
return c.Volume(sioVolumeID(createResponse.ID))
|
||||
}
|
||||
|
||||
// AttachVolume maps the scaleio volume to an sdc node. If the multipleMappings flag
|
||||
// is true, ScaleIO will allow other SDC to map to that volume.
|
||||
func (c *sioClient) AttachVolume(id sioVolumeID, multipleMappings bool) error {
|
||||
if err := c.init(); err != nil {
|
||||
glog.Error(log("failed to init'd client in attach volume: %v", err))
|
||||
return err
|
||||
}
|
||||
|
||||
iid, err := c.IID()
|
||||
if err != nil {
|
||||
glog.Error(log("failed to get instanceIID for attach volume: %v", err))
|
||||
return err
|
||||
}
|
||||
|
||||
params := &siotypes.MapVolumeSdcParam{
|
||||
SdcID: iid,
|
||||
AllowMultipleMappings: strconv.FormatBool(multipleMappings),
|
||||
AllSdcs: "",
|
||||
}
|
||||
volClient := sio.NewVolume(c.client)
|
||||
volClient.Volume = &siotypes.Volume{ID: string(id)}
|
||||
|
||||
if err := volClient.MapVolumeSdc(params); err != nil {
|
||||
glog.Error(log("failed to attach volume id %s: %v", id, err))
|
||||
return err
|
||||
}
|
||||
|
||||
glog.V(4).Info(log("volume %s attached successfully", id))
|
||||
return nil
|
||||
}
|
||||
|
||||
// DetachVolume detaches the volume with specified id.
|
||||
func (c *sioClient) DetachVolume(id sioVolumeID) error {
|
||||
if err := c.init(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
iid, err := c.IID()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
params := &siotypes.UnmapVolumeSdcParam{
|
||||
SdcID: "",
|
||||
IgnoreScsiInitiators: "true",
|
||||
AllSdcs: iid,
|
||||
}
|
||||
volClient := sio.NewVolume(c.client)
|
||||
volClient.Volume = &siotypes.Volume{ID: string(id)}
|
||||
if err := volClient.UnmapVolumeSdc(params); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeleteVolume deletes the volume with the specified id
|
||||
func (c *sioClient) DeleteVolume(id sioVolumeID) error {
|
||||
if err := c.init(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
vol, err := c.Volume(id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
volClient := sio.NewVolume(c.client)
|
||||
volClient.Volume = vol
|
||||
if err := volClient.RemoveVolume("ONLY_ME"); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// IID returns the scaleio instance id for node
|
||||
func (c *sioClient) IID() (string, error) {
|
||||
if err := c.init(); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
// if instanceID not set, retrieve it
|
||||
if c.instanceID == "" {
|
||||
guid, err := c.getGuid()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
sdc, err := c.sysClient.FindSdc("SdcGuid", guid)
|
||||
if err != nil {
|
||||
glog.Error(log("failed to retrieve sdc info %s", err))
|
||||
return "", err
|
||||
}
|
||||
c.instanceID = sdc.Sdc.ID
|
||||
glog.V(4).Info(log("retrieved instanceID %s", c.instanceID))
|
||||
}
|
||||
return c.instanceID, nil
|
||||
}
|
||||
|
||||
// getGuid returns instance GUID, if not set using resource labels
|
||||
// it attemps to fallback to using drv_cfg binary
|
||||
func (c *sioClient) getGuid() (string, error) {
|
||||
if c.sdcGuid == "" {
|
||||
glog.V(4).Info(log("sdc guid label not set, falling back to using drv_cfg"))
|
||||
cmd := c.getSdcCmd()
|
||||
output, err := c.exec.Run(cmd, "--query_guid")
|
||||
if err != nil {
|
||||
glog.Error(log("drv_cfg --query_guid failed: %v", err))
|
||||
return "", err
|
||||
}
|
||||
c.sdcGuid = strings.TrimSpace(string(output))
|
||||
}
|
||||
return c.sdcGuid, nil
|
||||
}
|
||||
|
||||
// getSioDiskPaths traverse local disk devices to retrieve device path
|
||||
// The path is extracted from /dev/disk/by-id; each sio device path has format:
|
||||
// emc-vol-<mdmID-volID> e.g.:
|
||||
// emc-vol-788d9efb0a8f20cb-a2b8419300000000
|
||||
func (c *sioClient) getSioDiskPaths() ([]os.FileInfo, error) {
|
||||
files, err := ioutil.ReadDir(sioDiskIDPath)
|
||||
if err != nil {
|
||||
glog.Error(log("failed to ReadDir %s: %v", sioDiskIDPath, err))
|
||||
return nil, err
|
||||
}
|
||||
|
||||
result := []os.FileInfo{}
|
||||
for _, file := range files {
|
||||
if c.diskRegex.MatchString(file.Name()) {
|
||||
result = append(result, file)
|
||||
}
|
||||
}
|
||||
|
||||
return result, nil
|
||||
|
||||
}
|
||||
|
||||
// GetVolumeRefs counts the number of references an SIO volume has a disk device.
|
||||
// This is useful in preventing premature detach.
|
||||
func (c *sioClient) GetVolumeRefs(volId sioVolumeID) (refs int, err error) {
|
||||
files, err := c.getSioDiskPaths()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
for _, file := range files {
|
||||
if strings.Contains(file.Name(), string(volId)) {
|
||||
refs++
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Devs returns a map of local devices as map[<volume.id>]<deviceName>
|
||||
func (c *sioClient) Devs() (map[string]string, error) {
|
||||
volumeMap := make(map[string]string)
|
||||
|
||||
files, err := c.getSioDiskPaths()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, f := range files {
|
||||
// split emc-vol-<mdmID>-<volumeID> to pull out volumeID
|
||||
parts := strings.Split(f.Name(), "-")
|
||||
if len(parts) != 4 {
|
||||
return nil, errors.New("unexpected ScaleIO device name format")
|
||||
}
|
||||
volumeID := parts[3]
|
||||
devPath, err := filepath.EvalSymlinks(fmt.Sprintf("%s/%s", sioDiskIDPath, f.Name()))
|
||||
if err != nil {
|
||||
glog.Error(log("devicepath-to-volID mapping error: %v", err))
|
||||
return nil, err
|
||||
}
|
||||
// map volumeID to devicePath
|
||||
volumeMap[volumeID] = devPath
|
||||
}
|
||||
return volumeMap, nil
|
||||
}
|
||||
|
||||
// WaitForAttachedDevice sets up a timer to wait for an attached device to appear in the instance's list.
|
||||
func (c *sioClient) WaitForAttachedDevice(token string) (string, error) {
|
||||
if token == "" {
|
||||
return "", fmt.Errorf("invalid attach token")
|
||||
}
|
||||
|
||||
// wait for device to show up in local device list
|
||||
ticker := time.NewTicker(time.Second)
|
||||
defer ticker.Stop()
|
||||
timer := time.NewTimer(30 * time.Second)
|
||||
defer timer.Stop()
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-ticker.C:
|
||||
devMap, err := c.Devs()
|
||||
if err != nil {
|
||||
glog.Error(log("failed while waiting for volume to attach: %v", err))
|
||||
return "", err
|
||||
}
|
||||
go func() {
|
||||
glog.V(4).Infof(log("waiting for volume %s to be mapped/attached", token))
|
||||
}()
|
||||
if path, ok := devMap[token]; ok {
|
||||
glog.V(4).Info(log("device %s mapped to vol %s", path, token))
|
||||
return path, nil
|
||||
}
|
||||
case <-timer.C:
|
||||
glog.Error(log("timed out while waiting for volume to be mapped to a device"))
|
||||
return "", fmt.Errorf("volume attach timeout")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// waitForDetachedDevice waits for device to be detached
|
||||
func (c *sioClient) WaitForDetachedDevice(token string) error {
|
||||
if token == "" {
|
||||
return fmt.Errorf("invalid detach token")
|
||||
}
|
||||
|
||||
// wait for attach.Token to show up in local device list
|
||||
ticker := time.NewTicker(time.Second)
|
||||
defer ticker.Stop()
|
||||
timer := time.NewTimer(30 * time.Second)
|
||||
defer timer.Stop()
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-ticker.C:
|
||||
devMap, err := c.Devs()
|
||||
if err != nil {
|
||||
glog.Error(log("failed while waiting for volume to unmap/detach: %v", err))
|
||||
return err
|
||||
}
|
||||
go func() {
|
||||
glog.V(4).Infof(log("waiting for volume %s to be unmapped/detached", token))
|
||||
}()
|
||||
// cant find vol id, then ok.
|
||||
if _, ok := devMap[token]; !ok {
|
||||
return nil
|
||||
}
|
||||
case <-timer.C:
|
||||
glog.Error(log("timed out while waiting for volume %s to be unmapped/detached", token))
|
||||
return fmt.Errorf("volume detach timeout")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ***********************************************************************
|
||||
// Little Helpers!
|
||||
// ***********************************************************************
|
||||
func (c *sioClient) findSystem(sysname string) (sys *siotypes.System, err error) {
|
||||
if c.sysClient, err = c.client.FindSystem("", sysname, ""); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
systems, err := c.client.GetInstance("")
|
||||
if err != nil {
|
||||
glog.Error(log("failed to retrieve instances: %v", err))
|
||||
return nil, err
|
||||
}
|
||||
for _, sys = range systems {
|
||||
if sys.Name == sysname {
|
||||
return sys, nil
|
||||
}
|
||||
}
|
||||
glog.Error(log("system %s not found", sysname))
|
||||
return nil, errors.New("system not found")
|
||||
}
|
||||
|
||||
func (c *sioClient) findProtectionDomain(pdname string) (*siotypes.ProtectionDomain, error) {
|
||||
c.pdClient = sio.NewProtectionDomain(c.client)
|
||||
if c.sysClient != nil {
|
||||
protectionDomain, err := c.sysClient.FindProtectionDomain("", pdname, "")
|
||||
if err != nil {
|
||||
glog.Error(log("failed to retrieve protection domains: %v", err))
|
||||
return nil, err
|
||||
}
|
||||
c.pdClient.ProtectionDomain = protectionDomain
|
||||
return protectionDomain, nil
|
||||
}
|
||||
glog.Error(log("protection domain %s not set", pdname))
|
||||
return nil, errors.New("protection domain not set")
|
||||
}
|
||||
|
||||
func (c *sioClient) findStoragePool(spname string) (*siotypes.StoragePool, error) {
|
||||
c.spClient = sio.NewStoragePool(c.client)
|
||||
if c.pdClient != nil {
|
||||
sp, err := c.pdClient.FindStoragePool("", spname, "")
|
||||
if err != nil {
|
||||
glog.Error(log("failed to retrieve storage pool: %v", err))
|
||||
return nil, err
|
||||
}
|
||||
c.spClient.StoragePool = sp
|
||||
return sp, nil
|
||||
}
|
||||
glog.Error(log("storage pool %s not set", spname))
|
||||
return nil, errors.New("storage pool not set")
|
||||
}
|
||||
|
||||
func (c *sioClient) getVolumes() ([]*siotypes.Volume, error) {
|
||||
return c.client.GetVolume("", "", "", "", true)
|
||||
}
|
||||
func (c *sioClient) getVolumesByID(id sioVolumeID) ([]*siotypes.Volume, error) {
|
||||
return c.client.GetVolume("", string(id), "", "", true)
|
||||
}
|
||||
|
||||
func (c *sioClient) getVolumesByName(name string) ([]*siotypes.Volume, error) {
|
||||
return c.client.GetVolume("", "", "", name, true)
|
||||
}
|
||||
|
||||
func (c *sioClient) getSdcPath() string {
|
||||
return sdcRootPath
|
||||
}
|
||||
|
||||
func (c *sioClient) getSdcCmd() string {
|
||||
return path.Join(c.getSdcPath(), "drv_cfg")
|
||||
}
|
248
vendor/k8s.io/kubernetes/pkg/volume/scaleio/sio_mgr.go
generated
vendored
Normal file
248
vendor/k8s.io/kubernetes/pkg/volume/scaleio/sio_mgr.go
generated
vendored
Normal file
@ -0,0 +1,248 @@
|
||||
/*
|
||||
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 scaleio
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"strconv"
|
||||
|
||||
"k8s.io/kubernetes/pkg/util/mount"
|
||||
|
||||
"github.com/golang/glog"
|
||||
|
||||
siotypes "github.com/codedellemc/goscaleio/types/v1"
|
||||
)
|
||||
|
||||
type storageInterface interface {
|
||||
CreateVolume(string, int64) (*siotypes.Volume, error)
|
||||
AttachVolume(string, bool) (string, error)
|
||||
IsAttached(string) (bool, error)
|
||||
DetachVolume(string) error
|
||||
DeleteVolume(string) error
|
||||
}
|
||||
|
||||
type sioMgr struct {
|
||||
client sioInterface
|
||||
configData map[string]string
|
||||
exec mount.Exec
|
||||
}
|
||||
|
||||
func newSioMgr(configs map[string]string, exec mount.Exec) (*sioMgr, error) {
|
||||
if configs == nil {
|
||||
return nil, errors.New("missing configuration data")
|
||||
}
|
||||
configs[confKey.protectionDomain] = defaultString(configs[confKey.protectionDomain], "default")
|
||||
configs[confKey.storagePool] = defaultString(configs[confKey.storagePool], "default")
|
||||
configs[confKey.sdcRootPath] = defaultString(configs[confKey.sdcRootPath], sdcRootPath)
|
||||
configs[confKey.storageMode] = defaultString(configs[confKey.storageMode], "ThinProvisioned")
|
||||
|
||||
mgr := &sioMgr{configData: configs, exec: exec}
|
||||
return mgr, nil
|
||||
}
|
||||
|
||||
// getClient safely returns an sioInterface
|
||||
func (m *sioMgr) getClient() (sioInterface, error) {
|
||||
if m.client == nil {
|
||||
glog.V(4).Info(log("creating scaleio client"))
|
||||
configs := m.configData
|
||||
username := configs[confKey.username]
|
||||
password := configs[confKey.password]
|
||||
gateway := configs[confKey.gateway]
|
||||
b, err := strconv.ParseBool(configs[confKey.sslEnabled])
|
||||
if err != nil {
|
||||
glog.Error(log("failed to parse sslEnabled, must be either \"true\" or \"false\""))
|
||||
return nil, err
|
||||
}
|
||||
certsEnabled := b
|
||||
|
||||
glog.V(4).Info(log("creating new client for gateway %s", gateway))
|
||||
client, err := newSioClient(gateway, username, password, certsEnabled, m.exec)
|
||||
if err != nil {
|
||||
glog.Error(log("failed to create scaleio client: %v", err))
|
||||
return nil, err
|
||||
}
|
||||
|
||||
client.sysName = configs[confKey.system]
|
||||
client.pdName = configs[confKey.protectionDomain]
|
||||
client.spName = configs[confKey.storagePool]
|
||||
client.sdcPath = configs[confKey.sdcRootPath]
|
||||
client.provisionMode = configs[confKey.storageMode]
|
||||
client.sdcGuid = configs[confKey.sdcGuid]
|
||||
|
||||
m.client = client
|
||||
|
||||
glog.V(4).Info(log("client created successfully [gateway=%s]", gateway))
|
||||
}
|
||||
return m.client, nil
|
||||
}
|
||||
|
||||
// CreateVolume creates a new ScaleIO volume
|
||||
func (m *sioMgr) CreateVolume(volName string, sizeGB int64) (*siotypes.Volume, error) {
|
||||
client, err := m.getClient()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
glog.V(4).Infof("scaleio: creating volume %s", volName)
|
||||
vol, err := client.CreateVolume(volName, sizeGB)
|
||||
if err != nil {
|
||||
glog.V(4).Infof("scaleio: failed creating volume %s: %v", volName, err)
|
||||
return nil, err
|
||||
}
|
||||
glog.V(4).Infof("scaleio: created volume %s successfully", volName)
|
||||
return vol, nil
|
||||
}
|
||||
|
||||
// AttachVolume maps a ScaleIO volume to the running node. If flag multiMaps,
|
||||
// ScaleIO will allow other SDC to map to volume.
|
||||
func (m *sioMgr) AttachVolume(volName string, multipleMappings bool) (string, error) {
|
||||
client, err := m.getClient()
|
||||
if err != nil {
|
||||
glog.Error(log("attach volume failed: %v", err))
|
||||
return "", err
|
||||
}
|
||||
|
||||
glog.V(4).Infoln(log("attaching volume %s", volName))
|
||||
iid, err := client.IID()
|
||||
if err != nil {
|
||||
glog.Error(log("failed to get instanceID"))
|
||||
return "", err
|
||||
}
|
||||
glog.V(4).Info(log("attaching volume %s to host instance %s", volName, iid))
|
||||
|
||||
devs, err := client.Devs()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
vol, err := client.FindVolume(volName)
|
||||
if err != nil {
|
||||
glog.Error(log("failed to find volume %s: %v", volName, err))
|
||||
return "", err
|
||||
}
|
||||
|
||||
// handle vol if already attached
|
||||
if len(vol.MappedSdcInfo) > 0 {
|
||||
if m.isSdcMappedToVol(iid, vol) {
|
||||
glog.V(4).Info(log("skippping attachment, volume %s already attached to sdc %s", volName, iid))
|
||||
return devs[vol.ID], nil
|
||||
}
|
||||
}
|
||||
|
||||
// attach volume, get deviceName
|
||||
if err := client.AttachVolume(sioVolumeID(vol.ID), multipleMappings); err != nil {
|
||||
glog.Error(log("attachment for volume %s failed :%v", volName, err))
|
||||
return "", err
|
||||
}
|
||||
device, err := client.WaitForAttachedDevice(vol.ID)
|
||||
if err != nil {
|
||||
glog.Error(log("failed while waiting for device to attach: %v", err))
|
||||
return "", err
|
||||
}
|
||||
glog.V(4).Info(log("volume %s attached successfully as %s to instance %s", volName, device, iid))
|
||||
return device, nil
|
||||
}
|
||||
|
||||
// IsAttached verifies that the named ScaleIO volume is still attached
|
||||
func (m *sioMgr) IsAttached(volName string) (bool, error) {
|
||||
client, err := m.getClient()
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
iid, err := client.IID()
|
||||
if err != nil {
|
||||
glog.Error("scaleio: failed to get instanceID")
|
||||
return false, err
|
||||
}
|
||||
|
||||
vol, err := client.FindVolume(volName)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
return m.isSdcMappedToVol(iid, vol), nil
|
||||
}
|
||||
|
||||
// DetachVolume detaches the name ScaleIO volume from an instance
|
||||
func (m *sioMgr) DetachVolume(volName string) error {
|
||||
client, err := m.getClient()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
iid, err := client.IID()
|
||||
if err != nil {
|
||||
glog.Error(log("failed to get instanceID: %v", err))
|
||||
return err
|
||||
}
|
||||
|
||||
vol, err := client.FindVolume(volName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !m.isSdcMappedToVol(iid, vol) {
|
||||
glog.Warning(log(
|
||||
"skipping detached, vol %s not attached to instance %s",
|
||||
volName, iid,
|
||||
))
|
||||
return nil
|
||||
}
|
||||
|
||||
if err := client.DetachVolume(sioVolumeID(vol.ID)); err != nil {
|
||||
glog.Error(log("failed to detach vol %s: %v", volName, err))
|
||||
return err
|
||||
}
|
||||
|
||||
glog.V(4).Info(log("volume %s detached successfully", volName))
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeleteVolumes removes the ScaleIO volume
|
||||
func (m *sioMgr) DeleteVolume(volName string) error {
|
||||
client, err := m.getClient()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
vol, err := client.FindVolume(volName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := client.DeleteVolume(sioVolumeID(vol.ID)); err != nil {
|
||||
glog.Error(log("failed to delete volume %s: %v", volName, err))
|
||||
return err
|
||||
}
|
||||
|
||||
glog.V(4).Info(log("deleted volume %s successfully", volName))
|
||||
return nil
|
||||
|
||||
}
|
||||
|
||||
// isSdcMappedToVol returns true if the sdc is mapped to the volume
|
||||
func (m *sioMgr) isSdcMappedToVol(sdcID string, vol *siotypes.Volume) bool {
|
||||
if len(vol.MappedSdcInfo) == 0 {
|
||||
glog.V(4).Info(log("no attachment found"))
|
||||
return false
|
||||
}
|
||||
|
||||
for _, sdcInfo := range vol.MappedSdcInfo {
|
||||
if sdcInfo.SdcID == sdcID {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
338
vendor/k8s.io/kubernetes/pkg/volume/scaleio/sio_mgr_test.go
generated
vendored
Normal file
338
vendor/k8s.io/kubernetes/pkg/volume/scaleio/sio_mgr_test.go
generated
vendored
Normal file
@ -0,0 +1,338 @@
|
||||
/*
|
||||
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 scaleio
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"k8s.io/kubernetes/pkg/util/mount"
|
||||
|
||||
siotypes "github.com/codedellemc/goscaleio/types/v1"
|
||||
)
|
||||
|
||||
var (
|
||||
fakeSdcID = "test-sdc-123456789"
|
||||
fakeVolumeName = "test-vol-0001"
|
||||
fakeVolumeID = "1234567890"
|
||||
fakeDev = "/dev/testABC"
|
||||
|
||||
fakeConfig = map[string]string{
|
||||
confKey.gateway: "http://sio.gateway:1234",
|
||||
confKey.sslEnabled: "false",
|
||||
confKey.system: "scaleio",
|
||||
confKey.volumeName: "sio-0001",
|
||||
confKey.secretName: "sio-secret",
|
||||
confKey.username: "c2lvdXNlcgo=", // siouser
|
||||
confKey.password: "c2lvcGFzc3dvcmQK", // siopassword
|
||||
}
|
||||
)
|
||||
|
||||
func newTestMgr(t *testing.T) *sioMgr {
|
||||
mgr, err := newSioMgr(fakeConfig, mount.NewFakeExec(nil))
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
mgr.client = newFakeSio()
|
||||
return mgr
|
||||
}
|
||||
|
||||
func TestMgrNew(t *testing.T) {
|
||||
mgr, err := newSioMgr(fakeConfig, mount.NewFakeExec(nil))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if mgr.configData == nil {
|
||||
t.Fatal("configuration data not set")
|
||||
}
|
||||
if mgr.configData[confKey.volumeName] != "sio-0001" {
|
||||
t.Errorf("expecting %s, got %s", "sio-0001", mgr.configData[confKey.volumeName])
|
||||
}
|
||||
|
||||
// check defaults
|
||||
if mgr.configData[confKey.protectionDomain] != "default" {
|
||||
t.Errorf("unexpected value for confData[protectionDomain] %s", mgr.configData[confKey.protectionDomain])
|
||||
}
|
||||
if mgr.configData[confKey.storagePool] != "default" {
|
||||
t.Errorf("unexpected value for confData[storagePool] %s", mgr.configData[confKey.storagePool])
|
||||
}
|
||||
if mgr.configData[confKey.storageMode] != "ThinProvisioned" {
|
||||
t.Errorf("unexpected value for confData[storageMode] %s", mgr.configData[confKey.storageMode])
|
||||
}
|
||||
}
|
||||
|
||||
func TestMgrGetClient(t *testing.T) {
|
||||
mgr := newTestMgr(t)
|
||||
_, err := mgr.getClient()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if mgr.client == nil {
|
||||
t.Fatal("mgr.client not set")
|
||||
}
|
||||
}
|
||||
|
||||
func TestMgrCreateVolume(t *testing.T) {
|
||||
mgr := newTestMgr(t)
|
||||
vol, err := mgr.CreateVolume("test-vol-0001", 8*1024*1024)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if vol.Name != "test-vol-0001" {
|
||||
t.Errorf("unexpected vol.Name %s", vol.Name)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMgrAttachVolume(t *testing.T) {
|
||||
mgr := newTestMgr(t)
|
||||
mgr.CreateVolume("test-vol-0001", 8*1024*1024)
|
||||
device, err := mgr.AttachVolume("test-vol-0001", false)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if device != "/dev/testABC" {
|
||||
t.Errorf("unexpected value for mapped device %s", device)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMgrAttachVolume_AlreadyAttached(t *testing.T) {
|
||||
mgr := newTestMgr(t)
|
||||
mgr.CreateVolume("test-vol-0001", 8*1024*1024)
|
||||
mgr.AttachVolume("test-vol-0001", false)
|
||||
dev, err := mgr.AttachVolume("test-vol-0001", false)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
if dev != "/dev/testABC" {
|
||||
t.Errorf("unexpected value for mapped device %s", dev)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMgrAttachVolume_VolumeNotFoundError(t *testing.T) {
|
||||
mgr := newTestMgr(t)
|
||||
mgr.CreateVolume("test-vol-0001", 8*1024*1024)
|
||||
_, err := mgr.AttachVolume("test-vol-0002", false)
|
||||
|
||||
if err == nil {
|
||||
t.Error("attachVolume should fail with volume not found error")
|
||||
}
|
||||
}
|
||||
|
||||
func TestMgrAttachVolume_WaitForAttachError(t *testing.T) {
|
||||
mgr := newTestMgr(t)
|
||||
mgr.CreateVolume("test-vol-0001", 8*1024*1024)
|
||||
go func() {
|
||||
c := mgr.client.(*fakeSio)
|
||||
close(c.waitAttachCtrl)
|
||||
}()
|
||||
_, err := mgr.AttachVolume("test-vol-0001", false)
|
||||
if err == nil {
|
||||
t.Error("attachVolume should fail with attach timeout error")
|
||||
}
|
||||
}
|
||||
|
||||
func TestMgrDetachVolume(t *testing.T) {
|
||||
mgr := newTestMgr(t)
|
||||
mgr.CreateVolume("test-vol-0001", 8*1024*1024)
|
||||
mgr.AttachVolume("test-vol-0001", false)
|
||||
if err := mgr.DetachVolume("test-vol-0001"); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
fakeSio := mgr.client.(*fakeSio)
|
||||
if len(fakeSio.volume.MappedSdcInfo) != 0 {
|
||||
t.Errorf("expecting attached sdc to 0, got %d", len(fakeSio.volume.MappedSdcInfo))
|
||||
}
|
||||
if len(fakeSio.devs) != 0 {
|
||||
t.Errorf("expecting local devs to be 0, got %d", len(fakeSio.devs))
|
||||
}
|
||||
|
||||
}
|
||||
func TestMgrDetachVolume_VolumeNotFound(t *testing.T) {
|
||||
mgr := newTestMgr(t)
|
||||
mgr.CreateVolume("test-vol-0001", 8*1024*1024)
|
||||
mgr.AttachVolume("test-vol-0001", false)
|
||||
err := mgr.DetachVolume("test-vol-0002")
|
||||
if err == nil {
|
||||
t.Fatal("expected a volume not found failure")
|
||||
}
|
||||
}
|
||||
|
||||
func TestMgrDetachVolume_VolumeNotAttached(t *testing.T) {
|
||||
mgr := newTestMgr(t)
|
||||
mgr.CreateVolume("test-vol-0001", 8*1024*1024)
|
||||
err := mgr.DetachVolume("test-vol-0001")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMgrDetachVolume_VolumeAlreadyDetached(t *testing.T) {
|
||||
mgr := newTestMgr(t)
|
||||
mgr.CreateVolume("test-vol-0001", 8*1024*1024)
|
||||
mgr.AttachVolume("test-vol-0001", false)
|
||||
mgr.DetachVolume("test-vol-0001")
|
||||
err := mgr.DetachVolume("test-vol-0001")
|
||||
if err != nil {
|
||||
t.Fatal("failed detaching a volume already detached")
|
||||
}
|
||||
}
|
||||
|
||||
func TestMgrDetachVolume_WaitForDetachError(t *testing.T) {
|
||||
mgr := newTestMgr(t)
|
||||
mgr.CreateVolume("test-vol-0001", 8*1024*1024)
|
||||
mgr.AttachVolume("test-vol-0001", false)
|
||||
err := mgr.DetachVolume("test-vol-0001")
|
||||
if err != nil {
|
||||
t.Error("detachVolume failed")
|
||||
}
|
||||
}
|
||||
func TestMgrDeleteVolume(t *testing.T) {
|
||||
mgr := newTestMgr(t)
|
||||
mgr.CreateVolume("test-vol-0001", 8*1024*1024)
|
||||
err := mgr.DeleteVolume("test-vol-0001")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
sio := mgr.client.(*fakeSio)
|
||||
if sio.volume != nil {
|
||||
t.Errorf("volume not nil after delete operation")
|
||||
}
|
||||
}
|
||||
func TestMgrDeleteVolume_VolumeNotFound(t *testing.T) {
|
||||
mgr := newTestMgr(t)
|
||||
mgr.CreateVolume("test-vol-0001", 8*1024*1024)
|
||||
err := mgr.DeleteVolume("test-vol-0002")
|
||||
if err == nil {
|
||||
t.Fatal("expected volume not found error")
|
||||
}
|
||||
}
|
||||
|
||||
// ************************************************************
|
||||
// Helper Test Types
|
||||
// ************************************************************
|
||||
type fakeSio struct {
|
||||
volume *siotypes.Volume
|
||||
waitAttachCtrl chan struct{}
|
||||
waitDetachCtrl chan struct{}
|
||||
devs map[string]string
|
||||
isMultiMap bool
|
||||
}
|
||||
|
||||
func newFakeSio() *fakeSio {
|
||||
return &fakeSio{
|
||||
waitAttachCtrl: make(chan struct{}),
|
||||
waitDetachCtrl: make(chan struct{}),
|
||||
}
|
||||
}
|
||||
|
||||
func (f *fakeSio) FindVolume(volumeName string) (*siotypes.Volume, error) {
|
||||
if f.volume == nil || f.volume.Name != volumeName {
|
||||
return nil, errors.New("volume not found")
|
||||
}
|
||||
return f.volume, nil
|
||||
}
|
||||
|
||||
func (f *fakeSio) Volume(id sioVolumeID) (*siotypes.Volume, error) {
|
||||
if f.volume == nil || f.volume.ID != string(id) {
|
||||
return nil, errors.New("volume not found")
|
||||
}
|
||||
return f.volume, nil
|
||||
}
|
||||
|
||||
func (f *fakeSio) CreateVolume(volName string, sizeGB int64) (*siotypes.Volume, error) {
|
||||
f.volume = &siotypes.Volume{
|
||||
ID: fakeVolumeID,
|
||||
Name: volName,
|
||||
SizeInKb: int(sizeGB),
|
||||
VolumeType: "test",
|
||||
}
|
||||
|
||||
return f.volume, nil
|
||||
}
|
||||
|
||||
func (f *fakeSio) AttachVolume(id sioVolumeID, multiMaps bool) error {
|
||||
f.isMultiMap = multiMaps
|
||||
_, err := f.Volume(id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
f.volume.MappedSdcInfo = []*siotypes.MappedSdcInfo{
|
||||
{SdcID: fakeSdcID},
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f *fakeSio) DetachVolume(id sioVolumeID) error {
|
||||
if _, err := f.Volume(id); err != nil {
|
||||
return err
|
||||
}
|
||||
f.volume.MappedSdcInfo = nil
|
||||
delete(f.devs, f.volume.ID)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f *fakeSio) DeleteVolume(id sioVolumeID) error {
|
||||
if _, err := f.Volume(id); err != nil {
|
||||
return err
|
||||
}
|
||||
f.volume = nil
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f *fakeSio) IID() (string, error) {
|
||||
return fakeSdcID, nil
|
||||
}
|
||||
|
||||
func (f *fakeSio) Devs() (map[string]string, error) {
|
||||
if f.volume == nil {
|
||||
return nil, errors.New("volume not found")
|
||||
}
|
||||
f.devs = map[string]string{
|
||||
f.volume.ID: fakeDev,
|
||||
}
|
||||
|
||||
return f.devs, nil
|
||||
}
|
||||
|
||||
func (f *fakeSio) GetVolumeRefs(volId sioVolumeID) (int, error) {
|
||||
if f.volume == nil {
|
||||
return 0, nil
|
||||
}
|
||||
return 1, nil
|
||||
}
|
||||
|
||||
func (f *fakeSio) WaitForAttachedDevice(token string) (string, error) {
|
||||
select {
|
||||
case <-time.After(500 * time.Millisecond):
|
||||
return fakeDev, nil
|
||||
case <-f.waitAttachCtrl:
|
||||
return "", errors.New("attached device timeout")
|
||||
}
|
||||
}
|
||||
|
||||
func (f *fakeSio) WaitForDetachedDevice(token string) error {
|
||||
select {
|
||||
case <-time.After(500 * time.Millisecond):
|
||||
delete(f.devs, f.volume.ID)
|
||||
return nil
|
||||
case <-f.waitDetachCtrl:
|
||||
return errors.New("detach device timeout")
|
||||
}
|
||||
}
|
220
vendor/k8s.io/kubernetes/pkg/volume/scaleio/sio_plugin.go
generated
vendored
Normal file
220
vendor/k8s.io/kubernetes/pkg/volume/scaleio/sio_plugin.go
generated
vendored
Normal file
@ -0,0 +1,220 @@
|
||||
/*
|
||||
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 scaleio
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"github.com/golang/glog"
|
||||
api "k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
"k8s.io/kubernetes/pkg/util/keymutex"
|
||||
"k8s.io/kubernetes/pkg/volume"
|
||||
)
|
||||
|
||||
const (
|
||||
sioName = "scaleio"
|
||||
sioPluginName = "kubernetes.io/scaleio"
|
||||
sioConfigFileName = "sioconf.dat"
|
||||
)
|
||||
|
||||
type sioPlugin struct {
|
||||
host volume.VolumeHost
|
||||
volumeMtx keymutex.KeyMutex
|
||||
}
|
||||
|
||||
func ProbeVolumePlugins() []volume.VolumePlugin {
|
||||
p := &sioPlugin{
|
||||
host: nil,
|
||||
}
|
||||
return []volume.VolumePlugin{p}
|
||||
}
|
||||
|
||||
// *******************
|
||||
// VolumePlugin Impl
|
||||
// *******************
|
||||
var _ volume.VolumePlugin = &sioPlugin{}
|
||||
|
||||
func (p *sioPlugin) Init(host volume.VolumeHost) error {
|
||||
p.host = host
|
||||
p.volumeMtx = keymutex.NewKeyMutex()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *sioPlugin) GetPluginName() string {
|
||||
return sioPluginName
|
||||
}
|
||||
|
||||
func (p *sioPlugin) GetVolumeName(spec *volume.Spec) (string, error) {
|
||||
attribs, err := getVolumeSourceAttribs(spec)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return attribs.volName, nil
|
||||
}
|
||||
|
||||
func (p *sioPlugin) CanSupport(spec *volume.Spec) bool {
|
||||
return (spec.PersistentVolume != nil && spec.PersistentVolume.Spec.ScaleIO != nil) ||
|
||||
(spec.Volume != nil && spec.Volume.ScaleIO != nil)
|
||||
}
|
||||
|
||||
func (p *sioPlugin) RequiresRemount() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (p *sioPlugin) NewMounter(
|
||||
spec *volume.Spec,
|
||||
pod *api.Pod,
|
||||
_ volume.VolumeOptions) (volume.Mounter, error) {
|
||||
|
||||
// extract source info from either ScaleIOVolumeSource or ScaleIOPersistentVolumeSource type
|
||||
attribs, err := getVolumeSourceAttribs(spec)
|
||||
if err != nil {
|
||||
return nil, errors.New(log("mounter failed to extract volume attributes from spec: %v", err))
|
||||
}
|
||||
|
||||
secretName, secretNS, err := getSecretAndNamespaceFromSpec(spec, pod)
|
||||
if err != nil {
|
||||
return nil, errors.New(log("failed to get secret name or secretNamespace: %v", err))
|
||||
}
|
||||
|
||||
return &sioVolume{
|
||||
pod: pod,
|
||||
spec: spec,
|
||||
secretName: secretName,
|
||||
secretNamespace: secretNS,
|
||||
volSpecName: spec.Name(),
|
||||
volName: attribs.volName,
|
||||
podUID: pod.UID,
|
||||
readOnly: attribs.readOnly,
|
||||
fsType: attribs.fsType,
|
||||
plugin: p,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// NewUnmounter creates a representation of the volume to unmount
|
||||
func (p *sioPlugin) NewUnmounter(specName string, podUID types.UID) (volume.Unmounter, error) {
|
||||
glog.V(4).Info(log("Unmounter for %s", specName))
|
||||
|
||||
return &sioVolume{
|
||||
podUID: podUID,
|
||||
volSpecName: specName,
|
||||
plugin: p,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (p *sioPlugin) ConstructVolumeSpec(volumeName, mountPath string) (*volume.Spec, error) {
|
||||
sioVol := &api.Volume{
|
||||
Name: volumeName,
|
||||
VolumeSource: api.VolumeSource{
|
||||
ScaleIO: &api.ScaleIOVolumeSource{},
|
||||
},
|
||||
}
|
||||
return volume.NewSpecFromVolume(sioVol), nil
|
||||
}
|
||||
|
||||
// SupportsMountOption returns true if volume plugins supports Mount options
|
||||
// Specifying mount options in a volume plugin that doesn't support
|
||||
// user specified mount options will result in error creating persistent volumes
|
||||
func (p *sioPlugin) SupportsMountOption() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// SupportsBulkVolumeVerification checks if volume plugin type is capable
|
||||
// of enabling bulk polling of all nodes. This can speed up verification of
|
||||
// attached volumes by quite a bit, but underlying pluging must support it.
|
||||
func (p *sioPlugin) SupportsBulkVolumeVerification() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
//******************************
|
||||
// PersistentVolumePlugin Impl
|
||||
// *****************************
|
||||
var _ volume.PersistentVolumePlugin = &sioPlugin{}
|
||||
|
||||
func (p *sioPlugin) GetAccessModes() []api.PersistentVolumeAccessMode {
|
||||
return []api.PersistentVolumeAccessMode{
|
||||
api.ReadWriteOnce,
|
||||
api.ReadOnlyMany,
|
||||
}
|
||||
}
|
||||
|
||||
// ***************************
|
||||
// DeletableVolumePlugin Impl
|
||||
//****************************
|
||||
var _ volume.DeletableVolumePlugin = &sioPlugin{}
|
||||
|
||||
func (p *sioPlugin) NewDeleter(spec *volume.Spec) (volume.Deleter, error) {
|
||||
attribs, err := getVolumeSourceAttribs(spec)
|
||||
if err != nil {
|
||||
glog.Error(log("deleter failed to extract volume attributes from spec: %v", err))
|
||||
return nil, err
|
||||
}
|
||||
|
||||
secretName, secretNS, err := getSecretAndNamespaceFromSpec(spec, nil)
|
||||
if err != nil {
|
||||
return nil, errors.New(log("failed to get secret name or secretNamespace: %v", err))
|
||||
}
|
||||
|
||||
return &sioVolume{
|
||||
spec: spec,
|
||||
secretName: secretName,
|
||||
secretNamespace: secretNS,
|
||||
volSpecName: spec.Name(),
|
||||
volName: attribs.volName,
|
||||
plugin: p,
|
||||
readOnly: attribs.readOnly,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// *********************************
|
||||
// ProvisionableVolumePlugin Impl
|
||||
// *********************************
|
||||
var _ volume.ProvisionableVolumePlugin = &sioPlugin{}
|
||||
|
||||
func (p *sioPlugin) NewProvisioner(options volume.VolumeOptions) (volume.Provisioner, error) {
|
||||
glog.V(4).Info(log("creating Provisioner"))
|
||||
|
||||
configData := options.Parameters
|
||||
if configData == nil {
|
||||
glog.Error(log("provisioner missing parameters, unable to continue"))
|
||||
return nil, errors.New("option parameters missing")
|
||||
}
|
||||
|
||||
// Supports ref of name of secret a couple of ways:
|
||||
// options.Parameters["secretRef"] for backward compat, or
|
||||
// options.Parameters["secretName"]
|
||||
secretName := configData[confKey.secretName]
|
||||
if secretName == "" {
|
||||
secretName = configData["secretName"]
|
||||
configData[confKey.secretName] = secretName
|
||||
}
|
||||
|
||||
secretNS := configData[confKey.secretNamespace]
|
||||
if secretNS == "" {
|
||||
secretNS = options.PVC.Namespace
|
||||
}
|
||||
|
||||
return &sioVolume{
|
||||
configData: configData,
|
||||
plugin: p,
|
||||
options: options,
|
||||
secretName: secretName,
|
||||
secretNamespace: secretNS,
|
||||
volSpecName: options.PVName,
|
||||
}, nil
|
||||
}
|
337
vendor/k8s.io/kubernetes/pkg/volume/scaleio/sio_util.go
generated
vendored
Normal file
337
vendor/k8s.io/kubernetes/pkg/volume/scaleio/sio_util.go
generated
vendored
Normal file
@ -0,0 +1,337 @@
|
||||
/*
|
||||
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 scaleio
|
||||
|
||||
import (
|
||||
"encoding/gob"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"path"
|
||||
"strconv"
|
||||
|
||||
"github.com/golang/glog"
|
||||
|
||||
api "k8s.io/api/core/v1"
|
||||
"k8s.io/kubernetes/pkg/volume"
|
||||
volutil "k8s.io/kubernetes/pkg/volume/util"
|
||||
)
|
||||
|
||||
type volSourceAttribs struct {
|
||||
volName,
|
||||
fsType string
|
||||
readOnly bool
|
||||
}
|
||||
|
||||
var (
|
||||
confKey = struct {
|
||||
gateway,
|
||||
sslEnabled,
|
||||
secretName,
|
||||
system,
|
||||
protectionDomain,
|
||||
storagePool,
|
||||
storageMode,
|
||||
sdcRootPath,
|
||||
volumeName,
|
||||
volSpecName,
|
||||
fsType,
|
||||
readOnly,
|
||||
username,
|
||||
password,
|
||||
secretNamespace,
|
||||
sdcGuid string
|
||||
}{
|
||||
gateway: "gateway",
|
||||
sslEnabled: "sslEnabled",
|
||||
secretName: "secretRef",
|
||||
secretNamespace: "secretNamespace",
|
||||
system: "system",
|
||||
protectionDomain: "protectionDomain",
|
||||
storagePool: "storagePool",
|
||||
storageMode: "storageMode",
|
||||
sdcRootPath: "sdcRootPath",
|
||||
volumeName: "volumeName",
|
||||
volSpecName: "volSpecName",
|
||||
fsType: "fsType",
|
||||
readOnly: "readOnly",
|
||||
username: "username",
|
||||
password: "password",
|
||||
sdcGuid: "sdcGuid",
|
||||
}
|
||||
sdcGuidLabelName = "scaleio.sdcGuid"
|
||||
sdcRootPath = "/opt/emc/scaleio/sdc/bin"
|
||||
|
||||
secretNotFoundErr = errors.New("secret not found")
|
||||
configMapNotFoundErr = errors.New("configMap not found")
|
||||
gatewayNotProvidedErr = errors.New("ScaleIO gateway not provided")
|
||||
secretRefNotProvidedErr = errors.New("secret ref not provided")
|
||||
systemNotProvidedErr = errors.New("ScaleIO system not provided")
|
||||
storagePoolNotProvidedErr = errors.New("ScaleIO storage pool not provided")
|
||||
protectionDomainNotProvidedErr = errors.New("ScaleIO protection domain not provided")
|
||||
)
|
||||
|
||||
// mapVolumeSpec maps attributes from either ScaleIOVolumeSource or ScaleIOPersistentVolumeSource to config
|
||||
func mapVolumeSpec(config map[string]string, spec *volume.Spec) {
|
||||
|
||||
if source, err := getScaleIOPersistentVolumeSourceFromSpec(spec); err == nil {
|
||||
config[confKey.gateway] = source.Gateway
|
||||
config[confKey.system] = source.System
|
||||
config[confKey.volumeName] = source.VolumeName
|
||||
config[confKey.sslEnabled] = strconv.FormatBool(source.SSLEnabled)
|
||||
config[confKey.protectionDomain] = source.ProtectionDomain
|
||||
config[confKey.storagePool] = source.StoragePool
|
||||
config[confKey.storageMode] = source.StorageMode
|
||||
config[confKey.fsType] = source.FSType
|
||||
config[confKey.readOnly] = strconv.FormatBool(source.ReadOnly)
|
||||
}
|
||||
|
||||
if source, err := getScaleIOVolumeSourceFromSpec(spec); err == nil {
|
||||
config[confKey.gateway] = source.Gateway
|
||||
config[confKey.system] = source.System
|
||||
config[confKey.volumeName] = source.VolumeName
|
||||
config[confKey.sslEnabled] = strconv.FormatBool(source.SSLEnabled)
|
||||
config[confKey.protectionDomain] = source.ProtectionDomain
|
||||
config[confKey.storagePool] = source.StoragePool
|
||||
config[confKey.storageMode] = source.StorageMode
|
||||
config[confKey.fsType] = source.FSType
|
||||
config[confKey.readOnly] = strconv.FormatBool(source.ReadOnly)
|
||||
}
|
||||
|
||||
//optionals
|
||||
applyConfigDefaults(config)
|
||||
}
|
||||
|
||||
func validateConfigs(config map[string]string) error {
|
||||
if config[confKey.gateway] == "" {
|
||||
return gatewayNotProvidedErr
|
||||
}
|
||||
if config[confKey.secretName] == "" {
|
||||
return secretRefNotProvidedErr
|
||||
}
|
||||
if config[confKey.system] == "" {
|
||||
return systemNotProvidedErr
|
||||
}
|
||||
if config[confKey.storagePool] == "" {
|
||||
return storagePoolNotProvidedErr
|
||||
}
|
||||
if config[confKey.protectionDomain] == "" {
|
||||
return protectionDomainNotProvidedErr
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// applyConfigDefaults apply known defaults to incoming spec for dynamic PVCs.
|
||||
func applyConfigDefaults(config map[string]string) {
|
||||
b, err := strconv.ParseBool(config[confKey.sslEnabled])
|
||||
if err != nil {
|
||||
glog.Warning(log("failed to parse param sslEnabled, setting it to false"))
|
||||
b = false
|
||||
}
|
||||
config[confKey.sslEnabled] = strconv.FormatBool(b)
|
||||
config[confKey.storageMode] = defaultString(config[confKey.storageMode], "ThinProvisioned")
|
||||
config[confKey.fsType] = defaultString(config[confKey.fsType], "xfs")
|
||||
b, err = strconv.ParseBool(config[confKey.readOnly])
|
||||
if err != nil {
|
||||
glog.Warning(log("failed to parse param readOnly, setting it to false"))
|
||||
b = false
|
||||
}
|
||||
config[confKey.readOnly] = strconv.FormatBool(b)
|
||||
}
|
||||
|
||||
func defaultString(val, defVal string) string {
|
||||
if val == "" {
|
||||
return defVal
|
||||
}
|
||||
return val
|
||||
}
|
||||
|
||||
// loadConfig loads configuration data from a file on disk
|
||||
func loadConfig(configName string) (map[string]string, error) {
|
||||
glog.V(4).Info(log("loading config file %s", configName))
|
||||
file, err := os.Open(configName)
|
||||
if err != nil {
|
||||
glog.Error(log("failed to open config file %s: %v", configName, err))
|
||||
return nil, err
|
||||
}
|
||||
defer file.Close()
|
||||
data := map[string]string{}
|
||||
if err := gob.NewDecoder(file).Decode(&data); err != nil {
|
||||
glog.Error(log("failed to parse config data %s: %v", configName, err))
|
||||
return nil, err
|
||||
}
|
||||
applyConfigDefaults(data)
|
||||
if err := validateConfigs(data); err != nil {
|
||||
glog.Error(log("failed to load ConfigMap %s: %v", err))
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return data, nil
|
||||
}
|
||||
|
||||
// saveConfig saves the configuration data to local disk
|
||||
func saveConfig(configName string, data map[string]string) error {
|
||||
glog.V(4).Info(log("saving config file %s", configName))
|
||||
|
||||
dir := path.Dir(configName)
|
||||
if _, err := os.Stat(dir); err != nil {
|
||||
if !os.IsNotExist(err) {
|
||||
return err
|
||||
}
|
||||
glog.V(4).Info(log("creating config dir for config data: %s", dir))
|
||||
if err := os.MkdirAll(dir, 0750); err != nil {
|
||||
glog.Error(log("failed to create config data dir %v", err))
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
file, err := os.Create(configName)
|
||||
if err != nil {
|
||||
glog.V(4).Info(log("failed to save config data file %s: %v", configName, err))
|
||||
return err
|
||||
}
|
||||
defer file.Close()
|
||||
if err := gob.NewEncoder(file).Encode(data); err != nil {
|
||||
glog.Error(log("failed to save config %s: %v", configName, err))
|
||||
return err
|
||||
}
|
||||
glog.V(4).Info(log("config data file saved successfully as %s", configName))
|
||||
return nil
|
||||
}
|
||||
|
||||
// attachSecret loads secret object and attaches to configData
|
||||
func attachSecret(plug *sioPlugin, namespace string, configData map[string]string) error {
|
||||
// load secret
|
||||
secretRefName := configData[confKey.secretName]
|
||||
kubeClient := plug.host.GetKubeClient()
|
||||
secretMap, err := volutil.GetSecretForPV(namespace, secretRefName, sioPluginName, kubeClient)
|
||||
if err != nil {
|
||||
glog.Error(log("failed to get secret: %v", err))
|
||||
return secretNotFoundErr
|
||||
}
|
||||
// merge secret data
|
||||
for key, val := range secretMap {
|
||||
configData[key] = val
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// attachSdcGuid injects the sdc guid node label value into config
|
||||
func attachSdcGuid(plug *sioPlugin, conf map[string]string) error {
|
||||
guid, err := getSdcGuidLabel(plug)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
conf[confKey.sdcGuid] = guid
|
||||
return nil
|
||||
}
|
||||
|
||||
// getSdcGuidLabel fetches the scaleio.sdcGuid node label
|
||||
// associated with the node executing this code.
|
||||
func getSdcGuidLabel(plug *sioPlugin) (string, error) {
|
||||
nodeLabels, err := plug.host.GetNodeLabels()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
label, ok := nodeLabels[sdcGuidLabelName]
|
||||
if !ok {
|
||||
glog.V(4).Info(log("node label %s not found", sdcGuidLabelName))
|
||||
return "", nil
|
||||
}
|
||||
|
||||
glog.V(4).Info(log("found node label %s=%s", sdcGuidLabelName, label))
|
||||
return label, nil
|
||||
}
|
||||
|
||||
// getVolumeSourceFromSpec safely extracts ScaleIOVolumeSource or ScaleIOPersistentVolumeSource from spec
|
||||
func getVolumeSourceFromSpec(spec *volume.Spec) (interface{}, error) {
|
||||
if spec.Volume != nil && spec.Volume.ScaleIO != nil {
|
||||
return spec.Volume.ScaleIO, nil
|
||||
}
|
||||
if spec.PersistentVolume != nil &&
|
||||
spec.PersistentVolume.Spec.ScaleIO != nil {
|
||||
return spec.PersistentVolume.Spec.ScaleIO, nil
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("ScaleIO not defined in spec")
|
||||
}
|
||||
|
||||
func getVolumeSourceAttribs(spec *volume.Spec) (*volSourceAttribs, error) {
|
||||
attribs := new(volSourceAttribs)
|
||||
if pvSource, err := getScaleIOPersistentVolumeSourceFromSpec(spec); err == nil {
|
||||
attribs.volName = pvSource.VolumeName
|
||||
attribs.fsType = pvSource.FSType
|
||||
attribs.readOnly = pvSource.ReadOnly
|
||||
} else if pSource, err := getScaleIOVolumeSourceFromSpec(spec); err == nil {
|
||||
attribs.volName = pSource.VolumeName
|
||||
attribs.fsType = pSource.FSType
|
||||
attribs.readOnly = pSource.ReadOnly
|
||||
} else {
|
||||
msg := log("failed to get ScaleIOVolumeSource or ScaleIOPersistentVolumeSource from spec")
|
||||
glog.Error(msg)
|
||||
return nil, errors.New(msg)
|
||||
}
|
||||
return attribs, nil
|
||||
}
|
||||
|
||||
func getScaleIOPersistentVolumeSourceFromSpec(spec *volume.Spec) (*api.ScaleIOPersistentVolumeSource, error) {
|
||||
source, err := getVolumeSourceFromSpec(spec)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if val, ok := source.(*api.ScaleIOPersistentVolumeSource); ok {
|
||||
return val, nil
|
||||
}
|
||||
return nil, fmt.Errorf("spec is not a valid ScaleIOPersistentVolume type")
|
||||
}
|
||||
|
||||
func getScaleIOVolumeSourceFromSpec(spec *volume.Spec) (*api.ScaleIOVolumeSource, error) {
|
||||
source, err := getVolumeSourceFromSpec(spec)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if val, ok := source.(*api.ScaleIOVolumeSource); ok {
|
||||
return val, nil
|
||||
}
|
||||
return nil, fmt.Errorf("spec is not a valid ScaleIOVolume type")
|
||||
}
|
||||
|
||||
func getSecretAndNamespaceFromSpec(spec *volume.Spec, pod *api.Pod) (secretName string, secretNS string, err error) {
|
||||
if source, err := getScaleIOVolumeSourceFromSpec(spec); err == nil {
|
||||
secretName = source.SecretRef.Name
|
||||
if pod != nil {
|
||||
secretNS = pod.Namespace
|
||||
}
|
||||
} else if source, err := getScaleIOPersistentVolumeSourceFromSpec(spec); err == nil {
|
||||
if source.SecretRef != nil {
|
||||
secretName = source.SecretRef.Name
|
||||
secretNS = source.SecretRef.Namespace
|
||||
if secretNS == "" && pod != nil {
|
||||
secretNS = pod.Namespace
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return "", "", errors.New("failed to get ScaleIOVolumeSource or ScaleIOPersistentVolumeSource")
|
||||
}
|
||||
return secretName, secretNS, nil
|
||||
}
|
||||
|
||||
func log(msg string, parts ...interface{}) string {
|
||||
return fmt.Sprintf(fmt.Sprintf("scaleio: %s", msg), parts...)
|
||||
}
|
224
vendor/k8s.io/kubernetes/pkg/volume/scaleio/sio_util_test.go
generated
vendored
Normal file
224
vendor/k8s.io/kubernetes/pkg/volume/scaleio/sio_util_test.go
generated
vendored
Normal file
@ -0,0 +1,224 @@
|
||||
/*
|
||||
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 scaleio
|
||||
|
||||
import (
|
||||
"encoding/gob"
|
||||
"os"
|
||||
"path"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
api "k8s.io/api/core/v1"
|
||||
utiltesting "k8s.io/client-go/util/testing"
|
||||
"k8s.io/kubernetes/pkg/volume"
|
||||
)
|
||||
|
||||
var (
|
||||
vol = &api.Volume{
|
||||
Name: testSioVolName,
|
||||
VolumeSource: api.VolumeSource{
|
||||
ScaleIO: &api.ScaleIOVolumeSource{
|
||||
Gateway: "http://test.scaleio:1111",
|
||||
System: "sio",
|
||||
ProtectionDomain: "defaultPD",
|
||||
StoragePool: "defaultSP",
|
||||
VolumeName: "test-vol",
|
||||
FSType: "ext4",
|
||||
SecretRef: &api.LocalObjectReference{Name: "test-secret"},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
config = map[string]string{
|
||||
confKey.system: "sio",
|
||||
confKey.gateway: "http://sio/",
|
||||
confKey.volSpecName: testSioVolName,
|
||||
confKey.volumeName: "sio-vol",
|
||||
confKey.secretName: "sio-secret",
|
||||
confKey.protectionDomain: "defaultPD",
|
||||
confKey.storagePool: "deraultSP",
|
||||
confKey.fsType: "xfs",
|
||||
confKey.readOnly: "true",
|
||||
}
|
||||
|
||||
testConfigFile = "conf.dat"
|
||||
)
|
||||
|
||||
func TestUtilMapVolumeSource(t *testing.T) {
|
||||
data := make(map[string]string)
|
||||
mapVolumeSpec(data, volume.NewSpecFromVolume(vol))
|
||||
if data[confKey.gateway] != "http://test.scaleio:1111" {
|
||||
t.Error("Unexpected gateway value")
|
||||
}
|
||||
if data[confKey.system] != "sio" {
|
||||
t.Error("Unexpected system value")
|
||||
}
|
||||
if data[confKey.protectionDomain] != "defaultPD" {
|
||||
t.Error("Unexpected protection domain value")
|
||||
}
|
||||
if data[confKey.storagePool] != "defaultSP" {
|
||||
t.Error("Unexpected storage pool value")
|
||||
}
|
||||
if data[confKey.volumeName] != "test-vol" {
|
||||
t.Error("Unexpected volume name value")
|
||||
}
|
||||
if data[confKey.fsType] != "ext4" {
|
||||
t.Error("Unexpected fstype value")
|
||||
}
|
||||
if data[confKey.sslEnabled] != "false" {
|
||||
t.Error("Unexpected sslEnabled value")
|
||||
}
|
||||
if data[confKey.readOnly] != "false" {
|
||||
t.Error("Unexpected readOnly value: ", data[confKey.readOnly])
|
||||
}
|
||||
}
|
||||
|
||||
func TestUtilValidateConfigs(t *testing.T) {
|
||||
data := map[string]string{
|
||||
confKey.secretName: "sio-secret",
|
||||
confKey.system: "sio",
|
||||
}
|
||||
if err := validateConfigs(data); err != gatewayNotProvidedErr {
|
||||
t.Error("Expecting error for missing gateway, but did not get it")
|
||||
}
|
||||
}
|
||||
|
||||
func TestUtilApplyConfigDefaults(t *testing.T) {
|
||||
data := map[string]string{
|
||||
confKey.system: "sio",
|
||||
confKey.gateway: "http://sio/",
|
||||
confKey.volumeName: "sio-vol",
|
||||
confKey.secretName: "test-secret",
|
||||
}
|
||||
applyConfigDefaults(data)
|
||||
|
||||
if data[confKey.gateway] != "http://sio/" {
|
||||
t.Error("Unexpected gateway value")
|
||||
}
|
||||
if data[confKey.system] != "sio" {
|
||||
t.Error("Unexpected system value")
|
||||
}
|
||||
if data[confKey.protectionDomain] != "" {
|
||||
t.Error("Unexpected protection domain value")
|
||||
}
|
||||
if data[confKey.storagePool] != "" {
|
||||
t.Error("Unexpected storage pool value")
|
||||
}
|
||||
if data[confKey.volumeName] != "sio-vol" {
|
||||
t.Error("Unexpected volume name value")
|
||||
}
|
||||
if data[confKey.fsType] != "xfs" {
|
||||
t.Error("Unexpected fstype value")
|
||||
}
|
||||
if data[confKey.storageMode] != "ThinProvisioned" {
|
||||
t.Error("Unexpected storage mode value")
|
||||
}
|
||||
if data[confKey.secretName] != "test-secret" {
|
||||
t.Error("Unexpected secret ref value")
|
||||
}
|
||||
if data[confKey.sslEnabled] != "false" {
|
||||
t.Error("Unexpected sslEnabled value")
|
||||
}
|
||||
if data[confKey.readOnly] != "false" {
|
||||
t.Error("Unexpected readOnly value: ", data[confKey.readOnly])
|
||||
}
|
||||
}
|
||||
|
||||
func TestUtilDefaultString(t *testing.T) {
|
||||
if defaultString("", "foo") != "foo" {
|
||||
t.Error("Unexpected value for default value")
|
||||
}
|
||||
}
|
||||
|
||||
func TestUtilSaveConfig(t *testing.T) {
|
||||
tmpDir, err := utiltesting.MkTmpdir("scaleio-test")
|
||||
if err != nil {
|
||||
t.Fatalf("can't make a temp dir: %v", err)
|
||||
}
|
||||
defer os.RemoveAll(tmpDir)
|
||||
|
||||
config := path.Join(tmpDir, testConfigFile)
|
||||
data := map[string]string{
|
||||
confKey.gateway: "https://test-gateway/",
|
||||
confKey.secretName: "sio-secret",
|
||||
confKey.sslEnabled: "false",
|
||||
}
|
||||
if err := saveConfig(config, data); err != nil {
|
||||
t.Fatal("failed while saving data", err)
|
||||
}
|
||||
file, err := os.Open(config)
|
||||
if err != nil {
|
||||
t.Fatal("failed to open conf file: ", file)
|
||||
}
|
||||
defer file.Close()
|
||||
dataRcvd := map[string]string{}
|
||||
if err := gob.NewDecoder(file).Decode(&dataRcvd); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !reflect.DeepEqual(data, dataRcvd) {
|
||||
t.Error("we got problem, config data not the same")
|
||||
}
|
||||
}
|
||||
|
||||
func TestUtilAttachSecret(t *testing.T) {
|
||||
plugMgr, tmpDir := newPluginMgr(t, makeScaleIOSecret(testSecret, testns))
|
||||
defer os.RemoveAll(tmpDir)
|
||||
|
||||
plug, err := plugMgr.FindPluginByName(sioPluginName)
|
||||
if err != nil {
|
||||
t.Errorf("Can't find the plugin %v", sioPluginName)
|
||||
}
|
||||
sioPlug, ok := plug.(*sioPlugin)
|
||||
if !ok {
|
||||
t.Errorf("Cannot assert plugin to be type sioPlugin")
|
||||
}
|
||||
|
||||
data := make(map[string]string)
|
||||
for k, v := range config {
|
||||
data[k] = v
|
||||
}
|
||||
if err := attachSecret(sioPlug, "default", data); err != nil {
|
||||
t.Errorf("failed to setupConfigData %v", err)
|
||||
}
|
||||
if data[confKey.username] == "" {
|
||||
t.Errorf("failed to merge secret")
|
||||
}
|
||||
}
|
||||
|
||||
func TestUtilLoadConfig(t *testing.T) {
|
||||
tmpDir, err := utiltesting.MkTmpdir("scaleio-test")
|
||||
if err != nil {
|
||||
t.Fatalf("can't make a temp dir: %v", err)
|
||||
}
|
||||
defer os.RemoveAll(tmpDir)
|
||||
|
||||
configFile := path.Join(tmpDir, sioConfigFileName)
|
||||
|
||||
if err := saveConfig(configFile, config); err != nil {
|
||||
t.Fatalf("failed to save configFile %s error:%v", configFile, err)
|
||||
}
|
||||
|
||||
dataRcvd, err := loadConfig(configFile)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to load configFile %s error:%v", configFile, err)
|
||||
}
|
||||
if dataRcvd[confKey.gateway] != config[confKey.gateway] ||
|
||||
dataRcvd[confKey.system] != config[confKey.system] {
|
||||
t.Fatal("loaded config data not matching saved config data")
|
||||
}
|
||||
}
|
522
vendor/k8s.io/kubernetes/pkg/volume/scaleio/sio_volume.go
generated
vendored
Normal file
522
vendor/k8s.io/kubernetes/pkg/volume/scaleio/sio_volume.go
generated
vendored
Normal file
@ -0,0 +1,522 @@
|
||||
/*
|
||||
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 scaleio
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/golang/glog"
|
||||
api "k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/api/resource"
|
||||
meta "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
"k8s.io/apimachinery/pkg/util/uuid"
|
||||
"k8s.io/kubernetes/pkg/util/mount"
|
||||
kstrings "k8s.io/kubernetes/pkg/util/strings"
|
||||
"k8s.io/kubernetes/pkg/volume"
|
||||
"k8s.io/kubernetes/pkg/volume/util"
|
||||
"k8s.io/kubernetes/pkg/volume/util/volumehelper"
|
||||
)
|
||||
|
||||
type sioVolume struct {
|
||||
sioMgr *sioMgr
|
||||
plugin *sioPlugin
|
||||
pod *api.Pod
|
||||
podUID types.UID
|
||||
spec *volume.Spec
|
||||
secretName string
|
||||
secretNamespace string
|
||||
volSpecName string
|
||||
volName string
|
||||
readOnly bool
|
||||
fsType string
|
||||
options volume.VolumeOptions
|
||||
configData map[string]string
|
||||
|
||||
volume.MetricsNil
|
||||
}
|
||||
|
||||
// *******************
|
||||
// volume.Volume Impl
|
||||
var _ volume.Volume = &sioVolume{}
|
||||
|
||||
// GetPath returns the path where the volume will be mounted.
|
||||
func (v *sioVolume) GetPath() string {
|
||||
return v.plugin.host.GetPodVolumeDir(
|
||||
v.podUID,
|
||||
kstrings.EscapeQualifiedNameForDisk(sioPluginName),
|
||||
v.volSpecName)
|
||||
}
|
||||
|
||||
// *************
|
||||
// Mounter Impl
|
||||
// *************
|
||||
var _ volume.Mounter = &sioVolume{}
|
||||
|
||||
// CanMount checks to verify that the volume can be mounted prior to Setup.
|
||||
// A nil error indicates that the volume is ready for mounitnig.
|
||||
func (v *sioVolume) CanMount() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (v *sioVolume) SetUp(fsGroup *int64) error {
|
||||
return v.SetUpAt(v.GetPath(), fsGroup)
|
||||
}
|
||||
|
||||
// SetUp bind mounts the disk global mount to the volume path.
|
||||
func (v *sioVolume) SetUpAt(dir string, fsGroup *int64) error {
|
||||
v.plugin.volumeMtx.LockKey(v.volSpecName)
|
||||
defer v.plugin.volumeMtx.UnlockKey(v.volSpecName)
|
||||
|
||||
glog.V(4).Info(log("setting up volume for PV.spec %s", v.volSpecName))
|
||||
if err := v.setSioMgr(); err != nil {
|
||||
glog.Error(log("setup failed to create scalio manager: %v", err))
|
||||
return err
|
||||
}
|
||||
|
||||
mounter := v.plugin.host.GetMounter(v.plugin.GetPluginName())
|
||||
notDevMnt, err := mounter.IsLikelyNotMountPoint(dir)
|
||||
if err != nil && !os.IsNotExist(err) {
|
||||
glog.Error(log("IsLikelyNotMountPoint test failed for dir %v", dir))
|
||||
return err
|
||||
}
|
||||
if !notDevMnt {
|
||||
glog.V(4).Info(log("skipping setup, dir %s already a mount point", v.volName))
|
||||
return nil
|
||||
}
|
||||
|
||||
// should multiple-mapping be enabled
|
||||
enableMultiMaps := false
|
||||
isROM := false
|
||||
if v.spec.PersistentVolume != nil {
|
||||
ams := v.spec.PersistentVolume.Spec.AccessModes
|
||||
for _, am := range ams {
|
||||
if am == api.ReadOnlyMany {
|
||||
enableMultiMaps = true
|
||||
isROM = true
|
||||
}
|
||||
}
|
||||
}
|
||||
glog.V(4).Info(log("multiple mapping enabled = %v", enableMultiMaps))
|
||||
|
||||
volName := v.volName
|
||||
devicePath, err := v.sioMgr.AttachVolume(volName, enableMultiMaps)
|
||||
if err != nil {
|
||||
glog.Error(log("setup of volume %v: %v", v.volSpecName, err))
|
||||
return err
|
||||
}
|
||||
options := []string{}
|
||||
switch {
|
||||
default:
|
||||
options = append(options, "rw")
|
||||
case isROM && !v.readOnly:
|
||||
options = append(options, "rw")
|
||||
case isROM:
|
||||
options = append(options, "ro")
|
||||
case v.readOnly:
|
||||
options = append(options, "ro")
|
||||
}
|
||||
|
||||
glog.V(4).Info(log("mounting device %s -> %s", devicePath, dir))
|
||||
if err := os.MkdirAll(dir, 0750); err != nil {
|
||||
glog.Error(log("failed to create dir %#v: %v", dir, err))
|
||||
return err
|
||||
}
|
||||
glog.V(4).Info(log("setup created mount point directory %s", dir))
|
||||
|
||||
diskMounter := volumehelper.NewSafeFormatAndMountFromHost(v.plugin.GetPluginName(), v.plugin.host)
|
||||
err = diskMounter.FormatAndMount(devicePath, dir, v.fsType, options)
|
||||
|
||||
if err != nil {
|
||||
glog.Error(log("mount operation failed during setup: %v", err))
|
||||
if err := os.Remove(dir); err != nil && !os.IsNotExist(err) {
|
||||
glog.Error(log("failed to remove dir %s during a failed mount at setup: %v", dir, err))
|
||||
return err
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
if !v.readOnly && fsGroup != nil {
|
||||
glog.V(4).Info(log("applying value FSGroup ownership"))
|
||||
volume.SetVolumeOwnership(v, fsGroup)
|
||||
}
|
||||
|
||||
glog.V(4).Info(log("successfully setup PV %s: volume %s mapped as %s mounted at %s", v.volSpecName, v.volName, devicePath, dir))
|
||||
return nil
|
||||
}
|
||||
|
||||
func (v *sioVolume) GetAttributes() volume.Attributes {
|
||||
return volume.Attributes{
|
||||
ReadOnly: v.readOnly,
|
||||
Managed: !v.readOnly,
|
||||
SupportsSELinux: true,
|
||||
}
|
||||
}
|
||||
|
||||
// **********************
|
||||
// volume.Unmounter Impl
|
||||
// *********************
|
||||
var _ volume.Unmounter = &sioVolume{}
|
||||
|
||||
// TearDownAt unmounts the bind mount
|
||||
func (v *sioVolume) TearDown() error {
|
||||
return v.TearDownAt(v.GetPath())
|
||||
}
|
||||
|
||||
// TearDown unmounts and remove the volume
|
||||
func (v *sioVolume) TearDownAt(dir string) error {
|
||||
v.plugin.volumeMtx.LockKey(v.volSpecName)
|
||||
defer v.plugin.volumeMtx.UnlockKey(v.volSpecName)
|
||||
|
||||
mounter := v.plugin.host.GetMounter(v.plugin.GetPluginName())
|
||||
dev, _, err := mount.GetDeviceNameFromMount(mounter, dir)
|
||||
if err != nil {
|
||||
glog.Errorf(log("failed to get reference count for volume: %s", dir))
|
||||
return err
|
||||
}
|
||||
|
||||
glog.V(4).Info(log("attempting to unmount %s", dir))
|
||||
if err := util.UnmountPath(dir, mounter); err != nil {
|
||||
glog.Error(log("teardown failed while unmounting dir %s: %v ", dir, err))
|
||||
return err
|
||||
}
|
||||
glog.V(4).Info(log("dir %s unmounted successfully", dir))
|
||||
|
||||
// detach/unmap
|
||||
deviceBusy, err := mounter.DeviceOpened(dev)
|
||||
if err != nil {
|
||||
glog.Error(log("teardown unable to get status for device %s: %v", dev, err))
|
||||
return err
|
||||
}
|
||||
|
||||
// Detach volume from node:
|
||||
// use "last attempt wins" strategy to detach volume from node
|
||||
// only allow volume to detach when it is not busy (not being used by other pods)
|
||||
if !deviceBusy {
|
||||
glog.V(4).Info(log("teardown is attempting to detach/unmap volume for PV %s", v.volSpecName))
|
||||
if err := v.resetSioMgr(); err != nil {
|
||||
glog.Error(log("teardown failed, unable to reset scalio mgr: %v", err))
|
||||
}
|
||||
volName := v.volName
|
||||
if err := v.sioMgr.DetachVolume(volName); err != nil {
|
||||
glog.Warning(log("warning: detaching failed for volume %s: %v", volName, err))
|
||||
return nil
|
||||
}
|
||||
glog.V(4).Infof(log("teardown of volume %v detached successfully", volName))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// ********************
|
||||
// volume.Deleter Impl
|
||||
// ********************
|
||||
var _ volume.Deleter = &sioVolume{}
|
||||
|
||||
func (v *sioVolume) Delete() error {
|
||||
glog.V(4).Info(log("deleting pvc %s", v.volSpecName))
|
||||
|
||||
if err := v.setSioMgrFromSpec(); err != nil {
|
||||
glog.Error(log("delete failed while setting sio manager: %v", err))
|
||||
return err
|
||||
}
|
||||
|
||||
err := v.sioMgr.DeleteVolume(v.volName)
|
||||
if err != nil {
|
||||
glog.Error(log("failed to delete volume %s: %v", v.volName, err))
|
||||
return err
|
||||
}
|
||||
|
||||
glog.V(4).Info(log("successfully deleted PV %s with volume %s", v.volSpecName, v.volName))
|
||||
return nil
|
||||
}
|
||||
|
||||
// ************************
|
||||
// volume.Provisioner Impl
|
||||
// ************************
|
||||
var _ volume.Provisioner = &sioVolume{}
|
||||
|
||||
func (v *sioVolume) Provision() (*api.PersistentVolume, error) {
|
||||
glog.V(4).Info(log("attempting to dynamically provision pvc %v", v.options.PVC.Name))
|
||||
|
||||
if !volume.AccessModesContainedInAll(v.plugin.GetAccessModes(), v.options.PVC.Spec.AccessModes) {
|
||||
return nil, fmt.Errorf("invalid AccessModes %v: only AccessModes %v are supported", v.options.PVC.Spec.AccessModes, v.plugin.GetAccessModes())
|
||||
}
|
||||
|
||||
// setup volume attrributes
|
||||
genName := v.generateName("k8svol", 11)
|
||||
var oneGig int64 = 1024 * 1024 * 1024
|
||||
var eightGig int64 = 8 * oneGig
|
||||
|
||||
capacity := v.options.PVC.Spec.Resources.Requests[api.ResourceName(api.ResourceStorage)]
|
||||
volSizeBytes := capacity.Value()
|
||||
volSizeGB := int64(volume.RoundUpSize(volSizeBytes, oneGig))
|
||||
|
||||
if volSizeBytes == 0 {
|
||||
return nil, fmt.Errorf("invalid volume size of 0 specified")
|
||||
}
|
||||
|
||||
if volSizeBytes < eightGig {
|
||||
volSizeGB = int64(volume.RoundUpSize(eightGig, oneGig))
|
||||
glog.V(4).Info(log("capacity less than 8Gi found, adjusted to %dGi", volSizeGB))
|
||||
|
||||
}
|
||||
|
||||
// create sio manager
|
||||
if err := v.setSioMgrFromConfig(); err != nil {
|
||||
glog.Error(log("provision failed while setting up sio mgr: %v", err))
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// create volume
|
||||
volName := genName
|
||||
vol, err := v.sioMgr.CreateVolume(volName, volSizeGB)
|
||||
if err != nil {
|
||||
glog.Error(log("provision failed while creating volume: %v", err))
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// prepare data for pv
|
||||
v.configData[confKey.volumeName] = volName
|
||||
sslEnabled, err := strconv.ParseBool(v.configData[confKey.sslEnabled])
|
||||
if err != nil {
|
||||
glog.Warning(log("failed to parse parameter sslEnabled, setting to false"))
|
||||
sslEnabled = false
|
||||
}
|
||||
readOnly, err := strconv.ParseBool(v.configData[confKey.readOnly])
|
||||
if err != nil {
|
||||
glog.Warning(log("failed to parse parameter readOnly, setting it to false"))
|
||||
readOnly = false
|
||||
}
|
||||
|
||||
// describe created pv
|
||||
pvName := genName
|
||||
pv := &api.PersistentVolume{
|
||||
ObjectMeta: meta.ObjectMeta{
|
||||
Name: pvName,
|
||||
Namespace: v.options.PVC.Namespace,
|
||||
Labels: map[string]string{},
|
||||
Annotations: map[string]string{
|
||||
volumehelper.VolumeDynamicallyCreatedByKey: "scaleio-dynamic-provisioner",
|
||||
},
|
||||
},
|
||||
Spec: api.PersistentVolumeSpec{
|
||||
PersistentVolumeReclaimPolicy: v.options.PersistentVolumeReclaimPolicy,
|
||||
AccessModes: v.options.PVC.Spec.AccessModes,
|
||||
Capacity: api.ResourceList{
|
||||
api.ResourceName(api.ResourceStorage): resource.MustParse(
|
||||
fmt.Sprintf("%dGi", volSizeGB),
|
||||
),
|
||||
},
|
||||
PersistentVolumeSource: api.PersistentVolumeSource{
|
||||
ScaleIO: &api.ScaleIOPersistentVolumeSource{
|
||||
Gateway: v.configData[confKey.gateway],
|
||||
SSLEnabled: sslEnabled,
|
||||
SecretRef: &api.SecretReference{Name: v.secretName, Namespace: v.secretNamespace},
|
||||
System: v.configData[confKey.system],
|
||||
ProtectionDomain: v.configData[confKey.protectionDomain],
|
||||
StoragePool: v.configData[confKey.storagePool],
|
||||
StorageMode: v.configData[confKey.storageMode],
|
||||
VolumeName: volName,
|
||||
FSType: v.configData[confKey.fsType],
|
||||
ReadOnly: readOnly,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
if len(v.options.PVC.Spec.AccessModes) == 0 {
|
||||
pv.Spec.AccessModes = v.plugin.GetAccessModes()
|
||||
}
|
||||
|
||||
glog.V(4).Info(log("provisioner created pv %v and volume %s successfully", pvName, vol.Name))
|
||||
return pv, nil
|
||||
}
|
||||
|
||||
// setSioMgr creates scaleio mgr from cached config data if found
|
||||
// otherwise, setups new config data and create mgr
|
||||
func (v *sioVolume) setSioMgr() error {
|
||||
glog.V(4).Info(log("setting up sio mgr for spec %s", v.volSpecName))
|
||||
podDir := v.plugin.host.GetPodPluginDir(v.podUID, sioPluginName)
|
||||
configName := path.Join(podDir, sioConfigFileName)
|
||||
if v.sioMgr == nil {
|
||||
configData, err := loadConfig(configName) // try to load config if exist
|
||||
if err != nil {
|
||||
if !os.IsNotExist(err) {
|
||||
glog.Error(log("failed to load config %s : %v", configName, err))
|
||||
return err
|
||||
}
|
||||
glog.V(4).Info(log("previous config file not found, creating new one"))
|
||||
// prepare config data
|
||||
configData = make(map[string]string)
|
||||
mapVolumeSpec(configData, v.spec)
|
||||
|
||||
// additional config data
|
||||
configData[confKey.secretNamespace] = v.secretNamespace
|
||||
configData[confKey.secretName] = v.secretName
|
||||
configData[confKey.volSpecName] = v.volSpecName
|
||||
|
||||
if err := validateConfigs(configData); err != nil {
|
||||
glog.Error(log("config setup failed: %s", err))
|
||||
return err
|
||||
}
|
||||
|
||||
// persist config
|
||||
if err := saveConfig(configName, configData); err != nil {
|
||||
glog.Error(log("failed to save config data: %v", err))
|
||||
return err
|
||||
}
|
||||
}
|
||||
// merge in secret
|
||||
if err := attachSecret(v.plugin, v.secretNamespace, configData); err != nil {
|
||||
glog.Error(log("failed to load secret: %v", err))
|
||||
return err
|
||||
}
|
||||
|
||||
// merge in Sdc Guid label value
|
||||
if err := attachSdcGuid(v.plugin, configData); err != nil {
|
||||
glog.Error(log("failed to retrieve sdc guid: %v", err))
|
||||
return err
|
||||
}
|
||||
mgr, err := newSioMgr(configData, v.plugin.host.GetExec(v.plugin.GetPluginName()))
|
||||
|
||||
if err != nil {
|
||||
glog.Error(log("failed to reset sio manager: %v", err))
|
||||
return err
|
||||
}
|
||||
|
||||
v.sioMgr = mgr
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// resetSioMgr creates scaleio manager from existing (cached) config data
|
||||
func (v *sioVolume) resetSioMgr() error {
|
||||
podDir := v.plugin.host.GetPodPluginDir(v.podUID, sioPluginName)
|
||||
configName := path.Join(podDir, sioConfigFileName)
|
||||
if v.sioMgr == nil {
|
||||
// load config data from disk
|
||||
configData, err := loadConfig(configName)
|
||||
if err != nil {
|
||||
glog.Error(log("failed to load config data: %v", err))
|
||||
return err
|
||||
}
|
||||
v.secretName = configData[confKey.secretName]
|
||||
v.secretNamespace = configData[confKey.secretNamespace]
|
||||
v.volName = configData[confKey.volumeName]
|
||||
v.volSpecName = configData[confKey.volSpecName]
|
||||
|
||||
// attach secret
|
||||
if err := attachSecret(v.plugin, v.secretNamespace, configData); err != nil {
|
||||
glog.Error(log("failed to load secret: %v", err))
|
||||
return err
|
||||
}
|
||||
|
||||
// merge in Sdc Guid label value
|
||||
if err := attachSdcGuid(v.plugin, configData); err != nil {
|
||||
glog.Error(log("failed to retrieve sdc guid: %v", err))
|
||||
return err
|
||||
}
|
||||
|
||||
mgr, err := newSioMgr(configData, v.plugin.host.GetExec(v.plugin.GetPluginName()))
|
||||
|
||||
if err != nil {
|
||||
glog.Error(log("failed to reset scaleio mgr: %v", err))
|
||||
return err
|
||||
}
|
||||
v.sioMgr = mgr
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// setSioFromConfig sets up scaleio mgr from an available config data map
|
||||
// designed to be called from dynamic provisioner
|
||||
func (v *sioVolume) setSioMgrFromConfig() error {
|
||||
glog.V(4).Info(log("setting scaleio mgr from available config"))
|
||||
if v.sioMgr == nil {
|
||||
applyConfigDefaults(v.configData)
|
||||
|
||||
v.configData[confKey.volSpecName] = v.volSpecName
|
||||
|
||||
if err := validateConfigs(v.configData); err != nil {
|
||||
glog.Error(log("config data setup failed: %s", err))
|
||||
return err
|
||||
}
|
||||
|
||||
// copy config and attach secret
|
||||
data := map[string]string{}
|
||||
for k, v := range v.configData {
|
||||
data[k] = v
|
||||
}
|
||||
|
||||
if err := attachSecret(v.plugin, v.secretNamespace, data); err != nil {
|
||||
glog.Error(log("failed to load secret: %v", err))
|
||||
return err
|
||||
}
|
||||
|
||||
mgr, err := newSioMgr(data, v.plugin.host.GetExec(v.plugin.GetPluginName()))
|
||||
|
||||
if err != nil {
|
||||
glog.Error(log("failed while setting scaleio mgr from config: %v", err))
|
||||
return err
|
||||
}
|
||||
v.sioMgr = mgr
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// setSioMgrFromSpec sets the scaleio manager from a spec object.
|
||||
// The spec may be complete or incomplete depending on lifecycle phase.
|
||||
func (v *sioVolume) setSioMgrFromSpec() error {
|
||||
glog.V(4).Info(log("setting sio manager from spec"))
|
||||
if v.sioMgr == nil {
|
||||
// get config data form spec volume source
|
||||
configData := map[string]string{}
|
||||
mapVolumeSpec(configData, v.spec)
|
||||
|
||||
// additional config
|
||||
configData[confKey.secretNamespace] = v.secretNamespace
|
||||
configData[confKey.secretName] = v.secretName
|
||||
configData[confKey.volSpecName] = v.volSpecName
|
||||
|
||||
if err := validateConfigs(configData); err != nil {
|
||||
glog.Error(log("config setup failed: %s", err))
|
||||
return err
|
||||
}
|
||||
|
||||
// attach secret object to config data
|
||||
if err := attachSecret(v.plugin, v.secretNamespace, configData); err != nil {
|
||||
glog.Error(log("failed to load secret: %v", err))
|
||||
return err
|
||||
}
|
||||
|
||||
mgr, err := newSioMgr(configData, v.plugin.host.GetExec(v.plugin.GetPluginName()))
|
||||
|
||||
if err != nil {
|
||||
glog.Error(log("failed to reset sio manager: %v", err))
|
||||
return err
|
||||
}
|
||||
v.sioMgr = mgr
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (v *sioVolume) generateName(prefix string, size int) string {
|
||||
return fmt.Sprintf("%s-%s", prefix, strings.Replace(string(uuid.NewUUID()), "-", "", -1)[0:size])
|
||||
}
|
535
vendor/k8s.io/kubernetes/pkg/volume/scaleio/sio_volume_test.go
generated
vendored
Normal file
535
vendor/k8s.io/kubernetes/pkg/volume/scaleio/sio_volume_test.go
generated
vendored
Normal file
@ -0,0 +1,535 @@
|
||||
/*
|
||||
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 scaleio
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/golang/glog"
|
||||
|
||||
api "k8s.io/api/core/v1"
|
||||
meta "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
fakeclient "k8s.io/client-go/kubernetes/fake"
|
||||
utiltesting "k8s.io/client-go/util/testing"
|
||||
"k8s.io/kubernetes/pkg/volume"
|
||||
volumetest "k8s.io/kubernetes/pkg/volume/testing"
|
||||
)
|
||||
|
||||
var (
|
||||
testSioSystem = "sio"
|
||||
testSioPD = "default"
|
||||
testSioVol = "vol-0001"
|
||||
testns = "default"
|
||||
testSecret = "sio-secret"
|
||||
testSioVolName = fmt.Sprintf("%s%s%s", testns, "-", testSioVol)
|
||||
podUID = types.UID("sio-pod")
|
||||
)
|
||||
|
||||
func newPluginMgr(t *testing.T, apiObject runtime.Object) (*volume.VolumePluginMgr, string) {
|
||||
tmpDir, err := utiltesting.MkTmpdir("scaleio-test")
|
||||
if err != nil {
|
||||
t.Fatalf("can't make a temp dir: %v", err)
|
||||
}
|
||||
|
||||
fakeClient := fakeclient.NewSimpleClientset(apiObject)
|
||||
host := volumetest.NewFakeVolumeHostWithNodeLabels(
|
||||
tmpDir,
|
||||
fakeClient,
|
||||
nil,
|
||||
map[string]string{sdcGuidLabelName: "abc-123"},
|
||||
)
|
||||
plugMgr := &volume.VolumePluginMgr{}
|
||||
plugMgr.InitPlugins(ProbeVolumePlugins(), nil /* prober */, host)
|
||||
|
||||
return plugMgr, tmpDir
|
||||
}
|
||||
|
||||
func makeScaleIOSecret(name, namespace string) *api.Secret {
|
||||
return &api.Secret{
|
||||
ObjectMeta: meta.ObjectMeta{
|
||||
Name: name,
|
||||
Namespace: namespace,
|
||||
UID: "1234567890",
|
||||
},
|
||||
Type: api.SecretType("kubernetes.io/scaleio"),
|
||||
Data: map[string][]byte{
|
||||
"username": []byte("username"),
|
||||
"password": []byte("password"),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func TestVolumeCanSupport(t *testing.T) {
|
||||
plugMgr, tmpDir := newPluginMgr(t, makeScaleIOSecret(testSecret, testns))
|
||||
defer os.RemoveAll(tmpDir)
|
||||
plug, err := plugMgr.FindPluginByName(sioPluginName)
|
||||
if err != nil {
|
||||
t.Errorf("Can't find the plugin %s by name", sioPluginName)
|
||||
}
|
||||
if plug.GetPluginName() != "kubernetes.io/scaleio" {
|
||||
t.Errorf("Wrong name: %s", plug.GetPluginName())
|
||||
}
|
||||
if !plug.CanSupport(
|
||||
&volume.Spec{
|
||||
Volume: &api.Volume{
|
||||
VolumeSource: api.VolumeSource{
|
||||
ScaleIO: &api.ScaleIOVolumeSource{},
|
||||
},
|
||||
},
|
||||
},
|
||||
) {
|
||||
t.Errorf("Expected true for CanSupport LibStorage VolumeSource")
|
||||
}
|
||||
if !plug.CanSupport(
|
||||
&volume.Spec{
|
||||
PersistentVolume: &api.PersistentVolume{
|
||||
Spec: api.PersistentVolumeSpec{
|
||||
PersistentVolumeSource: api.PersistentVolumeSource{
|
||||
ScaleIO: &api.ScaleIOPersistentVolumeSource{},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
) {
|
||||
t.Errorf("Expected true for CanSupport LibStorage PersistentVolumeSource")
|
||||
}
|
||||
}
|
||||
|
||||
func TestVolumeGetAccessModes(t *testing.T) {
|
||||
plugMgr, tmpDir := newPluginMgr(t, makeScaleIOSecret(testSecret, testns))
|
||||
defer os.RemoveAll(tmpDir)
|
||||
plug, err := plugMgr.FindPersistentPluginByName(sioPluginName)
|
||||
if err != nil {
|
||||
t.Errorf("Can't find the plugin %v", sioPluginName)
|
||||
}
|
||||
if !containsMode(plug.GetAccessModes(), api.ReadWriteOnce) {
|
||||
t.Errorf("Expected two AccessModeTypes: %s or %s", api.ReadWriteOnce, api.ReadOnlyMany)
|
||||
}
|
||||
}
|
||||
func containsMode(modes []api.PersistentVolumeAccessMode, mode api.PersistentVolumeAccessMode) bool {
|
||||
for _, m := range modes {
|
||||
if m == mode {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func TestVolumeMounterUnmounter(t *testing.T) {
|
||||
plugMgr, tmpDir := newPluginMgr(t, makeScaleIOSecret(testSecret, testns))
|
||||
defer os.RemoveAll(tmpDir)
|
||||
|
||||
plug, err := plugMgr.FindPluginByName(sioPluginName)
|
||||
if err != nil {
|
||||
t.Errorf("Can't find the plugin %v", sioPluginName)
|
||||
}
|
||||
sioPlug, ok := plug.(*sioPlugin)
|
||||
if !ok {
|
||||
t.Errorf("Cannot assert plugin to be type sioPlugin")
|
||||
}
|
||||
|
||||
vol := &api.Volume{
|
||||
Name: testSioVolName,
|
||||
VolumeSource: api.VolumeSource{
|
||||
ScaleIO: &api.ScaleIOVolumeSource{
|
||||
Gateway: "http://test.scaleio:1111",
|
||||
System: testSioSystem,
|
||||
ProtectionDomain: testSioPD,
|
||||
StoragePool: "default",
|
||||
VolumeName: testSioVol,
|
||||
FSType: "ext4",
|
||||
SecretRef: &api.LocalObjectReference{Name: testSecret},
|
||||
ReadOnly: false,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
sioMounter, err := sioPlug.NewMounter(
|
||||
volume.NewSpecFromVolume(vol),
|
||||
&api.Pod{ObjectMeta: meta.ObjectMeta{UID: podUID, Namespace: testns}},
|
||||
volume.VolumeOptions{},
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to make a new Mounter: %v", err)
|
||||
}
|
||||
|
||||
if sioMounter == nil {
|
||||
t.Fatal("Got a nil Mounter")
|
||||
}
|
||||
|
||||
sio := newFakeSio()
|
||||
sioVol := sioMounter.(*sioVolume)
|
||||
if err := sioVol.setSioMgr(); err != nil {
|
||||
t.Fatalf("failed to create sio mgr: %v", err)
|
||||
}
|
||||
sioVol.sioMgr.client = sio
|
||||
sioVol.sioMgr.CreateVolume(testSioVol, 8) //create vol ahead of time
|
||||
|
||||
volPath := path.Join(tmpDir, fmt.Sprintf("pods/%s/volumes/kubernetes.io~scaleio/%s", podUID, testSioVolName))
|
||||
path := sioMounter.GetPath()
|
||||
if path != volPath {
|
||||
t.Errorf("Got unexpected path: %s", path)
|
||||
}
|
||||
|
||||
if err := sioMounter.SetUp(nil); err != nil {
|
||||
t.Errorf("Expected success, got: %v", err)
|
||||
}
|
||||
if _, err := os.Stat(path); err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
t.Errorf("SetUp() failed, volume path not created: %s", path)
|
||||
} else {
|
||||
t.Errorf("SetUp() failed: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
if sio.isMultiMap {
|
||||
t.Errorf("SetUp() - expecting multiple volume disabled by default")
|
||||
}
|
||||
|
||||
// did we read sdcGuid label
|
||||
if _, ok := sioVol.sioMgr.configData[confKey.sdcGuid]; !ok {
|
||||
t.Errorf("Expected to find node label scaleio.sdcGuid, but did not find it")
|
||||
}
|
||||
|
||||
// rebuild spec
|
||||
builtSpec, err := sioPlug.ConstructVolumeSpec(volume.NewSpecFromVolume(vol).Name(), path)
|
||||
if err != nil {
|
||||
t.Errorf("ConstructVolumeSpec failed %v", err)
|
||||
}
|
||||
if builtSpec.Name() != vol.Name {
|
||||
t.Errorf("Unexpected spec name %s", builtSpec.Name())
|
||||
}
|
||||
|
||||
// unmount
|
||||
sioUnmounter, err := sioPlug.NewUnmounter(volume.NewSpecFromVolume(vol).Name(), podUID)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to make a new Unmounter: %v", err)
|
||||
}
|
||||
if sioUnmounter == nil {
|
||||
t.Fatal("Got a nil Unmounter")
|
||||
}
|
||||
sioVol = sioUnmounter.(*sioVolume)
|
||||
if err := sioVol.resetSioMgr(); err != nil {
|
||||
t.Fatalf("failed to reset sio mgr: %v", err)
|
||||
}
|
||||
sioVol.sioMgr.client = sio
|
||||
|
||||
if err := sioUnmounter.TearDown(); err != nil {
|
||||
t.Errorf("Expected success, got: %v", err)
|
||||
}
|
||||
// is mount point gone ?
|
||||
if _, err := os.Stat(path); err == nil {
|
||||
t.Errorf("TearDown() failed, volume path still exists: %s", path)
|
||||
} else if !os.IsNotExist(err) {
|
||||
t.Errorf("TearDown() failed: %v", err)
|
||||
}
|
||||
// are we still mapped
|
||||
if sio.volume.MappedSdcInfo != nil {
|
||||
t.Errorf("expected SdcMappedInfo to be nil, volume may still be mapped")
|
||||
}
|
||||
}
|
||||
|
||||
func TestVolumeProvisioner(t *testing.T) {
|
||||
plugMgr, tmpDir := newPluginMgr(t, makeScaleIOSecret(testSecret, testns))
|
||||
defer os.RemoveAll(tmpDir)
|
||||
|
||||
plug, err := plugMgr.FindPluginByName(sioPluginName)
|
||||
if err != nil {
|
||||
t.Fatalf("Can't find the plugin %v", sioPluginName)
|
||||
}
|
||||
sioPlug, ok := plug.(*sioPlugin)
|
||||
if !ok {
|
||||
t.Fatal("Cannot assert plugin to be type sioPlugin")
|
||||
}
|
||||
|
||||
options := volume.VolumeOptions{
|
||||
ClusterName: "testcluster",
|
||||
PVC: volumetest.CreateTestPVC("100Mi", []api.PersistentVolumeAccessMode{api.ReadWriteOnce}),
|
||||
PersistentVolumeReclaimPolicy: api.PersistentVolumeReclaimDelete,
|
||||
}
|
||||
options.PVC.Name = "testpvc"
|
||||
options.PVC.Namespace = testns
|
||||
|
||||
options.PVC.Spec.AccessModes = []api.PersistentVolumeAccessMode{
|
||||
api.ReadOnlyMany,
|
||||
}
|
||||
|
||||
options.Parameters = map[string]string{
|
||||
confKey.gateway: "http://test.scaleio:11111",
|
||||
confKey.system: "sio",
|
||||
confKey.protectionDomain: testSioPD,
|
||||
confKey.storagePool: "default",
|
||||
confKey.secretName: testSecret,
|
||||
}
|
||||
|
||||
provisioner, err := sioPlug.NewProvisioner(options)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create new provisioner: %v", err)
|
||||
}
|
||||
if provisioner == nil {
|
||||
t.Fatal("got a nil provisioner")
|
||||
}
|
||||
sio := newFakeSio()
|
||||
sioVol := provisioner.(*sioVolume)
|
||||
if err := sioVol.setSioMgrFromConfig(); err != nil {
|
||||
t.Fatalf("failed to create scaleio mgr from config: %v", err)
|
||||
}
|
||||
sioVol.sioMgr.client = sio
|
||||
|
||||
spec, err := provisioner.Provision()
|
||||
if err != nil {
|
||||
t.Fatalf("call to Provision() failed: %v", err)
|
||||
}
|
||||
|
||||
if spec.Namespace != testns {
|
||||
t.Fatalf("unexpected namespace %v", spec.Namespace)
|
||||
}
|
||||
if spec.Spec.ScaleIO.SecretRef == nil {
|
||||
t.Fatalf("unexpected nil value for spec.SecretRef")
|
||||
}
|
||||
if spec.Spec.ScaleIO.SecretRef.Name != testSecret ||
|
||||
spec.Spec.ScaleIO.SecretRef.Namespace != testns {
|
||||
t.Fatalf("spec.SecretRef is not being set properly")
|
||||
}
|
||||
|
||||
spec.Spec.ClaimRef = &api.ObjectReference{Namespace: testns}
|
||||
|
||||
// validate provision
|
||||
actualSpecName := spec.Name
|
||||
actualVolName := spec.Spec.PersistentVolumeSource.ScaleIO.VolumeName
|
||||
if !strings.HasPrefix(actualSpecName, "k8svol-") {
|
||||
t.Errorf("expecting volume name to start with k8svol-, got %s", actualSpecName)
|
||||
}
|
||||
vol, err := sio.FindVolume(actualVolName)
|
||||
if err != nil {
|
||||
t.Fatalf("failed getting volume %v: %v", actualVolName, err)
|
||||
}
|
||||
if vol.Name != actualVolName {
|
||||
t.Errorf("expected volume name to be %s, got %s", actualVolName, vol.Name)
|
||||
}
|
||||
if vol.SizeInKb != 8*1024*1024 {
|
||||
glog.V(4).Info(log("unexpected volume size"))
|
||||
}
|
||||
|
||||
// mount dynamic vol
|
||||
sioMounter, err := sioPlug.NewMounter(
|
||||
volume.NewSpecFromPersistentVolume(spec, false),
|
||||
&api.Pod{ObjectMeta: meta.ObjectMeta{UID: podUID, Namespace: testns}},
|
||||
volume.VolumeOptions{},
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to make a new Mounter: %v", err)
|
||||
}
|
||||
sioVol = sioMounter.(*sioVolume)
|
||||
if err := sioVol.setSioMgr(); err != nil {
|
||||
t.Fatalf("failed to create sio mgr: %v", err)
|
||||
}
|
||||
sioVol.sioMgr.client = sio
|
||||
if err := sioMounter.SetUp(nil); err != nil {
|
||||
t.Fatalf("Expected success, got: %v", err)
|
||||
}
|
||||
|
||||
// did we read sdcGuid label
|
||||
if _, ok := sioVol.sioMgr.configData[confKey.sdcGuid]; !ok {
|
||||
t.Errorf("Expected to find node label scaleio.sdcGuid, but did not find it")
|
||||
}
|
||||
|
||||
// isMultiMap applied
|
||||
if !sio.isMultiMap {
|
||||
t.Errorf("SetUp() expecting attached volume with multi-mapping")
|
||||
}
|
||||
|
||||
// teardown dynamic vol
|
||||
sioUnmounter, err := sioPlug.NewUnmounter(spec.Name, podUID)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to make a new Unmounter: %v", err)
|
||||
}
|
||||
sioVol = sioUnmounter.(*sioVolume)
|
||||
if err := sioVol.resetSioMgr(); err != nil {
|
||||
t.Fatalf("failed to reset sio mgr: %v", err)
|
||||
}
|
||||
sioVol.sioMgr.client = sio
|
||||
if err := sioUnmounter.TearDown(); err != nil {
|
||||
t.Errorf("Expected success, got: %v", err)
|
||||
}
|
||||
|
||||
// test deleter
|
||||
deleter, err := sioPlug.NewDeleter(volume.NewSpecFromPersistentVolume(spec, false))
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create a deleter %v", err)
|
||||
}
|
||||
sioVol = deleter.(*sioVolume)
|
||||
if err := sioVol.setSioMgrFromSpec(); err != nil {
|
||||
t.Fatalf("failed to set sio mgr: %v", err)
|
||||
}
|
||||
sioVol.sioMgr.client = sio
|
||||
if err := deleter.Delete(); err != nil {
|
||||
t.Fatalf("failed while deleteing vol: %v", err)
|
||||
}
|
||||
path := deleter.GetPath()
|
||||
if _, err := os.Stat(path); err == nil {
|
||||
t.Errorf("TearDown() failed, volume path still exists: %s", path)
|
||||
} else if !os.IsNotExist(err) {
|
||||
t.Errorf("Deleter did not delete path %v: %v", path, err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestVolumeProvisionerWithIncompleteConfig(t *testing.T) {
|
||||
plugMgr, tmpDir := newPluginMgr(t, makeScaleIOSecret(testSecret, testns))
|
||||
defer os.RemoveAll(tmpDir)
|
||||
|
||||
plug, err := plugMgr.FindPluginByName(sioPluginName)
|
||||
if err != nil {
|
||||
t.Fatalf("Can't find the plugin %v", sioPluginName)
|
||||
}
|
||||
sioPlug, ok := plug.(*sioPlugin)
|
||||
if !ok {
|
||||
t.Fatal("Cannot assert plugin to be type sioPlugin")
|
||||
}
|
||||
|
||||
options := volume.VolumeOptions{
|
||||
ClusterName: "testcluster",
|
||||
PVName: "pvc-sio-dynamic-vol",
|
||||
PVC: volumetest.CreateTestPVC("100Mi", []api.PersistentVolumeAccessMode{api.ReadWriteOnce}),
|
||||
PersistentVolumeReclaimPolicy: api.PersistentVolumeReclaimDelete,
|
||||
}
|
||||
options.PVC.Namespace = testns
|
||||
|
||||
options.PVC.Spec.AccessModes = []api.PersistentVolumeAccessMode{
|
||||
api.ReadWriteOnce,
|
||||
}
|
||||
|
||||
// incomplete options, test should fail
|
||||
_, err = sioPlug.NewProvisioner(options)
|
||||
if err == nil {
|
||||
t.Fatal("expected failure due to incomplete options")
|
||||
}
|
||||
}
|
||||
|
||||
func TestVolumeProvisionerWithZeroCapacity(t *testing.T) {
|
||||
plugMgr, tmpDir := newPluginMgr(t, makeScaleIOSecret(testSecret, testns))
|
||||
defer os.RemoveAll(tmpDir)
|
||||
|
||||
plug, err := plugMgr.FindPluginByName(sioPluginName)
|
||||
if err != nil {
|
||||
t.Fatalf("Can't find the plugin %v", sioPluginName)
|
||||
}
|
||||
sioPlug, ok := plug.(*sioPlugin)
|
||||
if !ok {
|
||||
t.Fatal("Cannot assert plugin to be type sioPlugin")
|
||||
}
|
||||
|
||||
options := volume.VolumeOptions{
|
||||
ClusterName: "testcluster",
|
||||
PVName: "pvc-sio-dynamic-vol",
|
||||
PVC: volumetest.CreateTestPVC("0Mi", []api.PersistentVolumeAccessMode{api.ReadWriteOnce}),
|
||||
PersistentVolumeReclaimPolicy: api.PersistentVolumeReclaimDelete,
|
||||
}
|
||||
options.PVC.Namespace = testns
|
||||
|
||||
options.PVC.Spec.AccessModes = []api.PersistentVolumeAccessMode{
|
||||
api.ReadWriteOnce,
|
||||
}
|
||||
|
||||
options.Parameters = map[string]string{
|
||||
confKey.gateway: "http://test.scaleio:11111",
|
||||
confKey.system: "sio",
|
||||
confKey.protectionDomain: testSioPD,
|
||||
confKey.storagePool: "default",
|
||||
confKey.secretName: "sio-secret",
|
||||
}
|
||||
|
||||
provisioner, _ := sioPlug.NewProvisioner(options)
|
||||
sio := newFakeSio()
|
||||
sioVol := provisioner.(*sioVolume)
|
||||
if err := sioVol.setSioMgrFromConfig(); err != nil {
|
||||
t.Fatalf("failed to create scaleio mgr from config: %v", err)
|
||||
}
|
||||
sioVol.sioMgr.client = sio
|
||||
|
||||
_, err = provisioner.Provision()
|
||||
if err == nil {
|
||||
t.Fatalf("call to Provision() should fail with invalid capacity")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestVolumeProvisionerWithSecretNamespace(t *testing.T) {
|
||||
plugMgr, tmpDir := newPluginMgr(t, makeScaleIOSecret("sio-sec", "sio-ns"))
|
||||
defer os.RemoveAll(tmpDir)
|
||||
|
||||
plug, err := plugMgr.FindPluginByName(sioPluginName)
|
||||
if err != nil {
|
||||
t.Fatalf("Can't find the plugin %v", sioPluginName)
|
||||
}
|
||||
sioPlug, ok := plug.(*sioPlugin)
|
||||
if !ok {
|
||||
t.Fatal("Cannot assert plugin to be type sioPlugin")
|
||||
}
|
||||
|
||||
options := volume.VolumeOptions{
|
||||
ClusterName: "testcluster",
|
||||
PVName: "pvc-sio-dynamic-vol",
|
||||
PVC: volumetest.CreateTestPVC("100Mi", []api.PersistentVolumeAccessMode{api.ReadWriteOnce}),
|
||||
PersistentVolumeReclaimPolicy: api.PersistentVolumeReclaimDelete,
|
||||
}
|
||||
|
||||
options.PVC.Spec.AccessModes = []api.PersistentVolumeAccessMode{
|
||||
api.ReadWriteOnce,
|
||||
}
|
||||
|
||||
options.PVC.Namespace = "pvc-ns"
|
||||
options.Parameters = map[string]string{
|
||||
confKey.gateway: "http://test.scaleio:11111",
|
||||
confKey.system: "sio",
|
||||
confKey.protectionDomain: testSioPD,
|
||||
confKey.storagePool: "default",
|
||||
confKey.secretName: "sio-sec",
|
||||
confKey.secretNamespace: "sio-ns",
|
||||
}
|
||||
|
||||
provisioner, _ := sioPlug.NewProvisioner(options)
|
||||
sio := newFakeSio()
|
||||
sioVol := provisioner.(*sioVolume)
|
||||
if err := sioVol.setSioMgrFromConfig(); err != nil {
|
||||
t.Fatalf("failed to create scaleio mgr from config: %v", err)
|
||||
}
|
||||
sioVol.sioMgr.client = sio
|
||||
|
||||
spec, err := sioVol.Provision()
|
||||
if err != nil {
|
||||
t.Fatalf("call to Provision() failed: %v", err)
|
||||
}
|
||||
|
||||
if spec.GetObjectMeta().GetNamespace() != "pvc-ns" {
|
||||
t.Fatalf("unexpected spec.namespace %s", spec.GetObjectMeta().GetNamespace())
|
||||
}
|
||||
|
||||
if spec.Spec.ScaleIO.SecretRef.Name != "sio-sec" {
|
||||
t.Fatalf("unexpected spec.ScaleIOPersistentVolume.SecretRef.Name %v", spec.Spec.ScaleIO.SecretRef.Name)
|
||||
}
|
||||
|
||||
if spec.Spec.ScaleIO.SecretRef.Namespace != "sio-ns" {
|
||||
t.Fatalf("unexpected spec.ScaleIOPersistentVolume.SecretRef.Namespace %v", spec.Spec.ScaleIO.SecretRef.Namespace)
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user