Add generated file

This PR adds generated files under pkg/client and vendor folder.
This commit is contained in:
xing-yang
2018-07-12 10:55:15 -07:00
parent 36b1de0341
commit e213d1890d
17729 changed files with 5090889 additions and 0 deletions

62
vendor/k8s.io/kubernetes/pkg/volume/photon_pd/BUILD generated vendored Normal file
View File

@@ -0,0 +1,62 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
"go_test",
)
go_library(
name = "go_default_library",
srcs = [
"attacher.go",
"photon_pd.go",
"photon_util.go",
],
importpath = "k8s.io/kubernetes/pkg/volume/photon_pd",
deps = [
"//pkg/cloudprovider:go_default_library",
"//pkg/cloudprovider/providers/photon: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",
"//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",
],
)
go_test(
name = "go_default_test",
srcs = [
"attacher_test.go",
"photon_pd_test.go",
],
embed = [":go_default_library"],
deps = [
"//pkg/cloudprovider/providers/photon:go_default_library",
"//pkg/util/mount:go_default_library",
"//pkg/volume:go_default_library",
"//pkg/volume/testing: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/types:go_default_library",
"//vendor/k8s.io/client-go/util/testing:go_default_library",
],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [":package-srcs"],
tags = ["automanaged"],
)

4
vendor/k8s.io/kubernetes/pkg/volume/photon_pd/OWNERS generated vendored Normal file
View File

@@ -0,0 +1,4 @@
maintainers:
- luomiao
- kerneltime
- abrarshivani

View File

@@ -0,0 +1,294 @@
/*
Copyright 2016 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 photon_pd
import (
"context"
"fmt"
"os"
"path"
"path/filepath"
"strings"
"time"
"github.com/golang/glog"
"k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/types"
"k8s.io/kubernetes/pkg/cloudprovider/providers/photon"
"k8s.io/kubernetes/pkg/util/mount"
"k8s.io/kubernetes/pkg/volume"
volumeutil "k8s.io/kubernetes/pkg/volume/util"
)
type photonPersistentDiskAttacher struct {
host volume.VolumeHost
photonDisks photon.Disks
}
var _ volume.Attacher = &photonPersistentDiskAttacher{}
var _ volume.AttachableVolumePlugin = &photonPersistentDiskPlugin{}
func (plugin *photonPersistentDiskPlugin) NewAttacher() (volume.Attacher, error) {
photonCloud, err := getCloudProvider(plugin.host.GetCloudProvider())
if err != nil {
glog.Errorf("Photon Controller attacher: NewAttacher failed to get cloud provider")
return nil, err
}
return &photonPersistentDiskAttacher{
host: plugin.host,
photonDisks: photonCloud,
}, nil
}
// Attaches the volume specified by the given spec to the given host.
// On success, returns the device path where the device was attached on the
// node.
// Callers are responsible for retryinging on failure.
// Callers are responsible for thread safety between concurrent attach and
// detach operations.
func (attacher *photonPersistentDiskAttacher) Attach(spec *volume.Spec, nodeName types.NodeName) (string, error) {
hostName := string(nodeName)
volumeSource, _, err := getVolumeSource(spec)
if err != nil {
glog.Errorf("Photon Controller attacher: Attach failed to get volume source")
return "", err
}
attached, err := attacher.photonDisks.DiskIsAttached(context.TODO(), volumeSource.PdID, nodeName)
if err != nil {
glog.Warningf("Photon Controller: couldn't check if disk is Attached for host %s, will try attach disk: %+v", hostName, err)
attached = false
}
if !attached {
glog.V(4).Infof("Photon Controller: Attach disk called for host %s", hostName)
err = attacher.photonDisks.AttachDisk(context.TODO(), volumeSource.PdID, nodeName)
if err != nil {
glog.Errorf("Error attaching volume %q to node %q: %+v", volumeSource.PdID, nodeName, err)
return "", err
}
}
PdidWithNoHypens := strings.Replace(volumeSource.PdID, "-", "", -1)
return path.Join(diskByIDPath, diskPhotonPrefix+PdidWithNoHypens), nil
}
func (attacher *photonPersistentDiskAttacher) VolumesAreAttached(specs []*volume.Spec, nodeName types.NodeName) (map[*volume.Spec]bool, error) {
volumesAttachedCheck := make(map[*volume.Spec]bool)
volumeSpecMap := make(map[string]*volume.Spec)
pdIDList := []string{}
for _, spec := range specs {
volumeSource, _, err := getVolumeSource(spec)
if err != nil {
glog.Errorf("Error getting volume (%q) source : %v", spec.Name(), err)
continue
}
pdIDList = append(pdIDList, volumeSource.PdID)
volumesAttachedCheck[spec] = true
volumeSpecMap[volumeSource.PdID] = spec
}
attachedResult, err := attacher.photonDisks.DisksAreAttached(context.TODO(), pdIDList, nodeName)
if err != nil {
glog.Errorf(
"Error checking if volumes (%v) are attached to current node (%q). err=%v",
pdIDList, nodeName, err)
return volumesAttachedCheck, err
}
for pdID, attached := range attachedResult {
if !attached {
spec := volumeSpecMap[pdID]
volumesAttachedCheck[spec] = false
glog.V(2).Infof("VolumesAreAttached: check volume %q (specName: %q) is no longer attached", pdID, spec.Name())
}
}
return volumesAttachedCheck, nil
}
func (attacher *photonPersistentDiskAttacher) WaitForAttach(spec *volume.Spec, devicePath string, _ *v1.Pod, timeout time.Duration) (string, error) {
volumeSource, _, err := getVolumeSource(spec)
if err != nil {
glog.Errorf("Photon Controller attacher: WaitForAttach failed to get volume source")
return "", err
}
if devicePath == "" {
return "", fmt.Errorf("WaitForAttach failed for PD %s: devicePath is empty.", volumeSource.PdID)
}
// scan scsi path to discover the new disk
scsiHostScan()
ticker := time.NewTicker(checkSleepDuration)
defer ticker.Stop()
timer := time.NewTimer(timeout)
defer timer.Stop()
for {
select {
case <-ticker.C:
glog.V(4).Infof("Checking PD %s is attached", volumeSource.PdID)
checkPath, err := verifyDevicePath(devicePath)
if err != nil {
// Log error, if any, and continue checking periodically. See issue #11321
glog.Warningf("Photon Controller attacher: WaitForAttach with devicePath %s Checking PD %s Error verify path", devicePath, volumeSource.PdID)
} else if checkPath != "" {
// A device path has successfully been created for the VMDK
glog.V(4).Infof("Successfully found attached PD %s.", volumeSource.PdID)
// map path with spec.Name()
volName := spec.Name()
realPath, _ := filepath.EvalSymlinks(devicePath)
deviceName := path.Base(realPath)
volNameToDeviceName[volName] = deviceName
return devicePath, nil
}
case <-timer.C:
return "", fmt.Errorf("Could not find attached PD %s. Timeout waiting for mount paths to be created.", volumeSource.PdID)
}
}
}
// GetDeviceMountPath returns a path where the device should
// point which should be bind mounted for individual volumes.
func (attacher *photonPersistentDiskAttacher) GetDeviceMountPath(spec *volume.Spec) (string, error) {
volumeSource, _, err := getVolumeSource(spec)
if err != nil {
glog.Errorf("Photon Controller attacher: GetDeviceMountPath failed to get volume source")
return "", err
}
return makeGlobalPDPath(attacher.host, volumeSource.PdID), nil
}
// GetMountDeviceRefs finds all other references to the device referenced
// by deviceMountPath; returns a list of paths.
func (plugin *photonPersistentDiskPlugin) GetDeviceMountRefs(deviceMountPath string) ([]string, error) {
mounter := plugin.host.GetMounter(plugin.GetPluginName())
return mount.GetMountRefs(mounter, deviceMountPath)
}
// MountDevice mounts device to global mount point.
func (attacher *photonPersistentDiskAttacher) MountDevice(spec *volume.Spec, devicePath string, deviceMountPath string) error {
mounter := attacher.host.GetMounter(photonPersistentDiskPluginName)
notMnt, err := mounter.IsLikelyNotMountPoint(deviceMountPath)
if err != nil {
if os.IsNotExist(err) {
if err := os.MkdirAll(deviceMountPath, 0750); err != nil {
glog.Errorf("Failed to create directory at %#v. err: %s", deviceMountPath, err)
return err
}
notMnt = true
} else {
return err
}
}
volumeSource, _, err := getVolumeSource(spec)
if err != nil {
glog.Errorf("Photon Controller attacher: MountDevice failed to get volume source. err: %s", err)
return err
}
options := []string{}
if notMnt {
diskMounter := volumeutil.NewSafeFormatAndMountFromHost(photonPersistentDiskPluginName, attacher.host)
mountOptions := volumeutil.MountOptionFromSpec(spec)
err = diskMounter.FormatAndMount(devicePath, deviceMountPath, volumeSource.FSType, mountOptions)
if err != nil {
os.Remove(deviceMountPath)
return err
}
glog.V(4).Infof("formatting spec %v devicePath %v deviceMountPath %v fs %v with options %+v", spec.Name(), devicePath, deviceMountPath, volumeSource.FSType, options)
}
return nil
}
type photonPersistentDiskDetacher struct {
mounter mount.Interface
photonDisks photon.Disks
}
var _ volume.Detacher = &photonPersistentDiskDetacher{}
func (plugin *photonPersistentDiskPlugin) NewDetacher() (volume.Detacher, error) {
photonCloud, err := getCloudProvider(plugin.host.GetCloudProvider())
if err != nil {
glog.Errorf("Photon Controller attacher: NewDetacher failed to get cloud provider. err: %s", err)
return nil, err
}
return &photonPersistentDiskDetacher{
mounter: plugin.host.GetMounter(plugin.GetPluginName()),
photonDisks: photonCloud,
}, nil
}
// Detach the given device from the given host.
func (detacher *photonPersistentDiskDetacher) Detach(volumeName string, nodeName types.NodeName) error {
hostName := string(nodeName)
pdID := volumeName
attached, err := detacher.photonDisks.DiskIsAttached(context.TODO(), pdID, nodeName)
if err != nil {
// Log error and continue with detach
glog.Errorf(
"Error checking if persistent disk (%q) is already attached to current node (%q). Will continue and try detach anyway. err=%v",
pdID, hostName, err)
}
if err == nil && !attached {
// Volume is already detached from node.
glog.V(4).Infof("detach operation was successful. persistent disk %q is already detached from node %q.", pdID, hostName)
return nil
}
if err := detacher.photonDisks.DetachDisk(context.TODO(), pdID, nodeName); err != nil {
glog.Errorf("Error detaching volume %q: %v", pdID, err)
return err
}
return nil
}
func (detacher *photonPersistentDiskDetacher) WaitForDetach(devicePath string, timeout time.Duration) error {
ticker := time.NewTicker(checkSleepDuration)
defer ticker.Stop()
timer := time.NewTimer(timeout)
defer timer.Stop()
for {
select {
case <-ticker.C:
glog.V(4).Infof("Checking device %q is detached.", devicePath)
if pathExists, err := volumeutil.PathExists(devicePath); err != nil {
return fmt.Errorf("Error checking if device path exists: %v", err)
} else if !pathExists {
return nil
}
case <-timer.C:
return fmt.Errorf("Timeout reached; Device %v is still attached", devicePath)
}
}
}
func (detacher *photonPersistentDiskDetacher) UnmountDevice(deviceMountPath string) error {
return volumeutil.UnmountPath(deviceMountPath, detacher.mounter)
}

View File

@@ -0,0 +1,330 @@
/*
Copyright 2016 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 photon_pd
import (
"context"
"errors"
"testing"
"k8s.io/api/core/v1"
"k8s.io/kubernetes/pkg/cloudprovider/providers/photon"
"k8s.io/kubernetes/pkg/volume"
volumetest "k8s.io/kubernetes/pkg/volume/testing"
"github.com/golang/glog"
"k8s.io/apimachinery/pkg/types"
)
func TestGetDeviceName_Volume(t *testing.T) {
plugin := newPlugin()
name := "my-photon-volume"
spec := createVolSpec(name, false)
deviceName, err := plugin.GetVolumeName(spec)
if err != nil {
t.Errorf("GetDeviceName error: %v", err)
}
if deviceName != name {
t.Errorf("GetDeviceName error: expected %s, got %s", name, deviceName)
}
}
func TestGetDeviceName_PersistentVolume(t *testing.T) {
plugin := newPlugin()
name := "my-photon-pv"
spec := createPVSpec(name, true)
deviceName, err := plugin.GetVolumeName(spec)
if err != nil {
t.Errorf("GetDeviceName error: %v", err)
}
if deviceName != name {
t.Errorf("GetDeviceName error: expected %s, got %s", name, deviceName)
}
}
// One testcase for TestAttachDetach table test below
type testcase struct {
name string
// For fake Photon cloud provider:
attach attachCall
detach detachCall
diskIsAttached diskIsAttachedCall
t *testing.T
// Actual test to run
test func(test *testcase) (string, error)
// Expected return of the test
expectedDevice string
expectedError error
}
func TestAttachDetach(t *testing.T) {
diskName := "000-000-000"
nodeName := types.NodeName("instance")
readOnly := false
spec := createVolSpec(diskName, readOnly)
detachError := errors.New("Fake detach error")
diskCheckError := errors.New("Fake DiskIsAttached error")
tests := []testcase{
// Successful Attach call
{
name: "Attach_Positive",
diskIsAttached: diskIsAttachedCall{diskName, nodeName, false, diskCheckError},
attach: attachCall{diskName, nodeName, nil},
test: func(testcase *testcase) (string, error) {
attacher := newAttacher(testcase)
return attacher.Attach(spec, nodeName)
},
expectedDevice: "/dev/disk/by-id/wwn-0x000000000",
},
// Disk is already attached
{
name: "Attach_Positive_AlreadyAttached",
diskIsAttached: diskIsAttachedCall{diskName, nodeName, false, diskCheckError},
attach: attachCall{diskName, nodeName, nil},
test: func(testcase *testcase) (string, error) {
attacher := newAttacher(testcase)
return attacher.Attach(spec, nodeName)
},
expectedDevice: "/dev/disk/by-id/wwn-0x000000000",
},
// Detach succeeds
{
name: "Detach_Positive",
diskIsAttached: diskIsAttachedCall{diskName, nodeName, true, nil},
detach: detachCall{diskName, nodeName, nil},
test: func(testcase *testcase) (string, error) {
detacher := newDetacher(testcase)
return "", detacher.Detach(diskName, nodeName)
},
},
// Disk is already detached
{
name: "Detach_Positive_AlreadyDetached",
diskIsAttached: diskIsAttachedCall{diskName, nodeName, false, nil},
test: func(testcase *testcase) (string, error) {
detacher := newDetacher(testcase)
return "", detacher.Detach(diskName, nodeName)
},
},
// Detach succeeds when DiskIsAttached fails
{
name: "Detach_Positive_CheckFails",
diskIsAttached: diskIsAttachedCall{diskName, nodeName, false, diskCheckError},
detach: detachCall{diskName, nodeName, nil},
test: func(testcase *testcase) (string, error) {
detacher := newDetacher(testcase)
return "", detacher.Detach(diskName, nodeName)
},
},
// Detach fails
{
name: "Detach_Negative",
diskIsAttached: diskIsAttachedCall{diskName, nodeName, false, diskCheckError},
detach: detachCall{diskName, nodeName, detachError},
test: func(testcase *testcase) (string, error) {
detacher := newDetacher(testcase)
return "", detacher.Detach(diskName, nodeName)
},
expectedError: detachError,
},
}
for _, testcase := range tests {
testcase.t = t
device, err := testcase.test(&testcase)
if err != testcase.expectedError {
t.Errorf("%s failed: expected err=%q, got %q", testcase.name, testcase.expectedError.Error(), err.Error())
}
if device != testcase.expectedDevice {
t.Errorf("%s failed: expected device=%q, got %q", testcase.name, testcase.expectedDevice, device)
}
t.Logf("Test %q succeeded", testcase.name)
}
}
// newPlugin creates a new gcePersistentDiskPlugin with fake cloud, NewAttacher
// and NewDetacher won't work.
func newPlugin() *photonPersistentDiskPlugin {
host := volumetest.NewFakeVolumeHost("/tmp", nil, nil)
plugins := ProbeVolumePlugins()
plugin := plugins[0]
plugin.Init(host)
return plugin.(*photonPersistentDiskPlugin)
}
func newAttacher(testcase *testcase) *photonPersistentDiskAttacher {
return &photonPersistentDiskAttacher{
host: nil,
photonDisks: testcase,
}
}
func newDetacher(testcase *testcase) *photonPersistentDiskDetacher {
return &photonPersistentDiskDetacher{
photonDisks: testcase,
}
}
func createVolSpec(name string, readOnly bool) *volume.Spec {
return &volume.Spec{
Volume: &v1.Volume{
VolumeSource: v1.VolumeSource{
PhotonPersistentDisk: &v1.PhotonPersistentDiskVolumeSource{
PdID: name,
},
},
},
}
}
func createPVSpec(name string, readOnly bool) *volume.Spec {
return &volume.Spec{
PersistentVolume: &v1.PersistentVolume{
Spec: v1.PersistentVolumeSpec{
PersistentVolumeSource: v1.PersistentVolumeSource{
PhotonPersistentDisk: &v1.PhotonPersistentDiskVolumeSource{
PdID: name,
},
},
},
},
}
}
// Fake PhotonPD implementation
type attachCall struct {
diskName string
nodeName types.NodeName
ret error
}
type detachCall struct {
diskName string
nodeName types.NodeName
ret error
}
type diskIsAttachedCall struct {
diskName string
nodeName types.NodeName
isAttached bool
ret error
}
func (testcase *testcase) AttachDisk(ctx context.Context, diskName string, nodeName types.NodeName) error {
expected := &testcase.attach
if expected.diskName == "" && expected.nodeName == "" {
// testcase.attach looks uninitialized, test did not expect to call
// AttachDisk
testcase.t.Errorf("Unexpected AttachDisk call!")
return errors.New("Unexpected AttachDisk call!")
}
if expected.diskName != diskName {
testcase.t.Errorf("Unexpected AttachDisk call: expected diskName %s, got %s", expected.diskName, diskName)
return errors.New("Unexpected AttachDisk call: wrong diskName")
}
if expected.nodeName != nodeName {
testcase.t.Errorf("Unexpected AttachDisk call: expected nodeName %s, got %s", expected.nodeName, nodeName)
return errors.New("Unexpected AttachDisk call: wrong nodeName")
}
glog.V(4).Infof("AttachDisk call: %s, %s, returning %v", diskName, nodeName, expected.ret)
return expected.ret
}
func (testcase *testcase) DetachDisk(ctx context.Context, diskName string, nodeName types.NodeName) error {
expected := &testcase.detach
if expected.diskName == "" && expected.nodeName == "" {
// testcase.detach looks uninitialized, test did not expect to call
// DetachDisk
testcase.t.Errorf("Unexpected DetachDisk call!")
return errors.New("Unexpected DetachDisk call!")
}
if expected.diskName != diskName {
testcase.t.Errorf("Unexpected DetachDisk call: expected diskName %s, got %s", expected.diskName, diskName)
return errors.New("Unexpected DetachDisk call: wrong diskName")
}
if expected.nodeName != nodeName {
testcase.t.Errorf("Unexpected DetachDisk call: expected nodeName %s, got %s", expected.nodeName, nodeName)
return errors.New("Unexpected DetachDisk call: wrong nodeName")
}
glog.V(4).Infof("DetachDisk call: %s, %s, returning %v", diskName, nodeName, expected.ret)
return expected.ret
}
func (testcase *testcase) DiskIsAttached(ctx context.Context, diskName string, nodeName types.NodeName) (bool, error) {
expected := &testcase.diskIsAttached
if expected.diskName == "" && expected.nodeName == "" {
// testcase.diskIsAttached looks uninitialized, test did not expect to
// call DiskIsAttached
testcase.t.Errorf("Unexpected DiskIsAttached call!")
return false, errors.New("Unexpected DiskIsAttached call!")
}
if expected.diskName != diskName {
testcase.t.Errorf("Unexpected DiskIsAttached call: expected diskName %s, got %s", expected.diskName, diskName)
return false, errors.New("Unexpected DiskIsAttached call: wrong diskName")
}
if expected.nodeName != nodeName {
testcase.t.Errorf("Unexpected DiskIsAttached call: expected nodeName %s, got %s", expected.nodeName, nodeName)
return false, errors.New("Unexpected DiskIsAttached call: wrong nodeName")
}
glog.V(4).Infof("DiskIsAttached call: %s, %s, returning %v, %v", diskName, nodeName, expected.isAttached, expected.ret)
return expected.isAttached, expected.ret
}
func (testcase *testcase) DisksAreAttached(ctx context.Context, diskNames []string, nodeName types.NodeName) (map[string]bool, error) {
return nil, errors.New("Not implemented")
}
func (testcase *testcase) CreateDisk(volumeOptions *photon.VolumeOptions) (volumeName string, err error) {
return "", errors.New("Not implemented")
}
func (testcase *testcase) DeleteDisk(volumeName string) error {
return errors.New("Not implemented")
}
func (testcase *testcase) GetVolumeLabels(volumeName string) (map[string]string, error) {
return map[string]string{}, errors.New("Not implemented")
}
func (testcase *testcase) GetDiskPath(volumeName string) (string, error) {
return "", errors.New("Not implemented")
}

View File

@@ -0,0 +1,401 @@
/*
Copyright 2016 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 photon_pd
import (
"fmt"
"os"
"path"
"github.com/golang/glog"
"k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/resource"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/types"
"k8s.io/kubernetes/pkg/util/mount"
utilstrings "k8s.io/kubernetes/pkg/util/strings"
"k8s.io/kubernetes/pkg/volume"
"k8s.io/kubernetes/pkg/volume/util"
)
// This is the primary entrypoint for volume plugins.
func ProbeVolumePlugins() []volume.VolumePlugin {
return []volume.VolumePlugin{&photonPersistentDiskPlugin{}}
}
type photonPersistentDiskPlugin struct {
host volume.VolumeHost
}
var _ volume.VolumePlugin = &photonPersistentDiskPlugin{}
var _ volume.PersistentVolumePlugin = &photonPersistentDiskPlugin{}
var _ volume.DeletableVolumePlugin = &photonPersistentDiskPlugin{}
var _ volume.ProvisionableVolumePlugin = &photonPersistentDiskPlugin{}
const (
photonPersistentDiskPluginName = "kubernetes.io/photon-pd"
)
func (plugin *photonPersistentDiskPlugin) Init(host volume.VolumeHost) error {
plugin.host = host
return nil
}
func (plugin *photonPersistentDiskPlugin) GetPluginName() string {
return photonPersistentDiskPluginName
}
func (plugin *photonPersistentDiskPlugin) GetVolumeName(spec *volume.Spec) (string, error) {
volumeSource, _, err := getVolumeSource(spec)
if err != nil {
glog.Errorf("Photon volume plugin: GetVolumeName failed to get volume source")
return "", err
}
return volumeSource.PdID, nil
}
func (plugin *photonPersistentDiskPlugin) CanSupport(spec *volume.Spec) bool {
return (spec.PersistentVolume != nil && spec.PersistentVolume.Spec.PhotonPersistentDisk != nil) ||
(spec.Volume != nil && spec.Volume.PhotonPersistentDisk != nil)
}
func (plugin *photonPersistentDiskPlugin) RequiresRemount() bool {
return false
}
func (plugin *photonPersistentDiskPlugin) SupportsMountOption() bool {
return true
}
func (plugin *photonPersistentDiskPlugin) SupportsBulkVolumeVerification() bool {
return false
}
func (plugin *photonPersistentDiskPlugin) NewMounter(spec *volume.Spec, pod *v1.Pod, _ volume.VolumeOptions) (volume.Mounter, error) {
return plugin.newMounterInternal(spec, pod.UID, &PhotonDiskUtil{}, plugin.host.GetMounter(plugin.GetPluginName()))
}
func (plugin *photonPersistentDiskPlugin) NewUnmounter(volName string, podUID types.UID) (volume.Unmounter, error) {
return plugin.newUnmounterInternal(volName, podUID, &PhotonDiskUtil{}, plugin.host.GetMounter(plugin.GetPluginName()))
}
func (plugin *photonPersistentDiskPlugin) newMounterInternal(spec *volume.Spec, podUID types.UID, manager pdManager, mounter mount.Interface) (volume.Mounter, error) {
vvol, _, err := getVolumeSource(spec)
if err != nil {
glog.Errorf("Photon volume plugin: newMounterInternal failed to get volume source")
return nil, err
}
pdID := vvol.PdID
fsType := vvol.FSType
return &photonPersistentDiskMounter{
photonPersistentDisk: &photonPersistentDisk{
podUID: podUID,
volName: spec.Name(),
pdID: pdID,
manager: manager,
mounter: mounter,
plugin: plugin,
},
fsType: fsType,
diskMounter: util.NewSafeFormatAndMountFromHost(plugin.GetPluginName(), plugin.host)}, nil
}
func (plugin *photonPersistentDiskPlugin) newUnmounterInternal(volName string, podUID types.UID, manager pdManager, mounter mount.Interface) (volume.Unmounter, error) {
return &photonPersistentDiskUnmounter{
&photonPersistentDisk{
podUID: podUID,
volName: volName,
manager: manager,
mounter: mounter,
plugin: plugin,
}}, nil
}
func (plugin *photonPersistentDiskPlugin) ConstructVolumeSpec(volumeSpecName, mountPath string) (*volume.Spec, error) {
mounter := plugin.host.GetMounter(plugin.GetPluginName())
pluginDir := plugin.host.GetPluginDir(plugin.GetPluginName())
pdID, err := mounter.GetDeviceNameFromMount(mountPath, pluginDir)
if err != nil {
return nil, err
}
photonPersistentDisk := &v1.Volume{
Name: volumeSpecName,
VolumeSource: v1.VolumeSource{
PhotonPersistentDisk: &v1.PhotonPersistentDiskVolumeSource{
PdID: pdID,
},
},
}
return volume.NewSpecFromVolume(photonPersistentDisk), nil
}
// Abstract interface to disk operations.
type pdManager interface {
// Creates a volume
CreateVolume(provisioner *photonPersistentDiskProvisioner) (pdID string, volumeSizeGB int, fstype string, err error)
// Deletes a volume
DeleteVolume(deleter *photonPersistentDiskDeleter) error
}
// photonPersistentDisk volumes are disk resources are attached to the kubelet's host machine and exposed to the pod.
type photonPersistentDisk struct {
volName string
podUID types.UID
// Unique identifier of the volume, used to find the disk resource in the provider.
pdID string
// Filesystem type, optional.
fsType string
// Utility interface that provides API calls to the provider to attach/detach disks.
manager pdManager
// Mounter interface that provides system calls to mount the global path to the pod local path.
mounter mount.Interface
plugin *photonPersistentDiskPlugin
volume.MetricsNil
}
var _ volume.Mounter = &photonPersistentDiskMounter{}
type photonPersistentDiskMounter struct {
*photonPersistentDisk
fsType string
diskMounter *mount.SafeFormatAndMount
}
func (b *photonPersistentDiskMounter) GetAttributes() volume.Attributes {
return volume.Attributes{
SupportsSELinux: true,
}
}
// Checks prior to mount operations to verify that the required components (binaries, etc.)
// to mount the volume are available on the underlying node.
// If not, it returns an error
func (b *photonPersistentDiskMounter) CanMount() error {
return nil
}
// SetUp attaches the disk and bind mounts to the volume path.
func (b *photonPersistentDiskMounter) SetUp(fsGroup *int64) error {
return b.SetUpAt(b.GetPath(), fsGroup)
}
// SetUp attaches the disk and bind mounts to the volume path.
func (b *photonPersistentDiskMounter) SetUpAt(dir string, fsGroup *int64) error {
glog.V(4).Infof("Photon Persistent Disk setup %s to %s", b.pdID, dir)
// TODO: handle failed mounts here.
notmnt, err := b.mounter.IsLikelyNotMountPoint(dir)
if err != nil && !os.IsNotExist(err) {
glog.Errorf("cannot validate mount point: %s %v", dir, err)
return err
}
if !notmnt {
return nil
}
if err := os.MkdirAll(dir, 0750); err != nil {
glog.Errorf("mkdir failed on disk %s (%v)", dir, err)
return err
}
options := []string{"bind"}
// Perform a bind mount to the full path to allow duplicate mounts of the same PD.
globalPDPath := makeGlobalPDPath(b.plugin.host, b.pdID)
glog.V(4).Infof("attempting to mount %s", dir)
err = b.mounter.Mount(globalPDPath, dir, "", options)
if err != nil {
notmnt, mntErr := b.mounter.IsLikelyNotMountPoint(dir)
if mntErr != nil {
glog.Errorf("IsLikelyNotMountPoint check failed: %v", mntErr)
return err
}
if !notmnt {
if mntErr = b.mounter.Unmount(dir); mntErr != nil {
glog.Errorf("Failed to unmount: %v", mntErr)
return err
}
notmnt, mntErr := b.mounter.IsLikelyNotMountPoint(dir)
if mntErr != nil {
glog.Errorf("IsLikelyNotMountPoint check failed: %v", mntErr)
return err
}
if !notmnt {
glog.Errorf("%s is still mounted, despite call to unmount(). Will try again next sync loop.", b.GetPath())
return err
}
}
os.Remove(dir)
glog.Errorf("Mount of disk %s failed: %v", dir, err)
return err
}
return nil
}
var _ volume.Unmounter = &photonPersistentDiskUnmounter{}
type photonPersistentDiskUnmounter struct {
*photonPersistentDisk
}
// Unmounts the bind mount, and detaches the disk only if the PD
// resource was the last reference to that disk on the kubelet.
func (c *photonPersistentDiskUnmounter) TearDown() error {
err := c.TearDownAt(c.GetPath())
if err != nil {
return err
}
removeFromScsiSubsystem(c.volName)
return nil
}
// Unmounts the bind mount, and detaches the disk only if the PD
// resource was the last reference to that disk on the kubelet.
func (c *photonPersistentDiskUnmounter) TearDownAt(dir string) error {
return util.UnmountPath(dir, c.mounter)
}
func makeGlobalPDPath(host volume.VolumeHost, devName string) string {
return path.Join(host.GetPluginDir(photonPersistentDiskPluginName), mount.MountsInGlobalPDPath, devName)
}
func (ppd *photonPersistentDisk) GetPath() string {
name := photonPersistentDiskPluginName
return ppd.plugin.host.GetPodVolumeDir(ppd.podUID, utilstrings.EscapeQualifiedNameForDisk(name), ppd.volName)
}
// TODO: supporting more access mode for PhotonController persistent disk
func (plugin *photonPersistentDiskPlugin) GetAccessModes() []v1.PersistentVolumeAccessMode {
return []v1.PersistentVolumeAccessMode{
v1.ReadWriteOnce,
}
}
type photonPersistentDiskDeleter struct {
*photonPersistentDisk
}
var _ volume.Deleter = &photonPersistentDiskDeleter{}
func (plugin *photonPersistentDiskPlugin) NewDeleter(spec *volume.Spec) (volume.Deleter, error) {
return plugin.newDeleterInternal(spec, &PhotonDiskUtil{})
}
func (plugin *photonPersistentDiskPlugin) newDeleterInternal(spec *volume.Spec, manager pdManager) (volume.Deleter, error) {
if spec.PersistentVolume != nil && spec.PersistentVolume.Spec.PhotonPersistentDisk == nil {
return nil, fmt.Errorf("spec.PersistentVolumeSource.PhotonPersistentDisk is nil")
}
return &photonPersistentDiskDeleter{
&photonPersistentDisk{
volName: spec.Name(),
pdID: spec.PersistentVolume.Spec.PhotonPersistentDisk.PdID,
manager: manager,
plugin: plugin,
}}, nil
}
func (r *photonPersistentDiskDeleter) Delete() error {
return r.manager.DeleteVolume(r)
}
type photonPersistentDiskProvisioner struct {
*photonPersistentDisk
options volume.VolumeOptions
}
var _ volume.Provisioner = &photonPersistentDiskProvisioner{}
func (plugin *photonPersistentDiskPlugin) NewProvisioner(options volume.VolumeOptions) (volume.Provisioner, error) {
return plugin.newProvisionerInternal(options, &PhotonDiskUtil{})
}
func (plugin *photonPersistentDiskPlugin) newProvisionerInternal(options volume.VolumeOptions, manager pdManager) (volume.Provisioner, error) {
return &photonPersistentDiskProvisioner{
photonPersistentDisk: &photonPersistentDisk{
manager: manager,
plugin: plugin,
},
options: options,
}, nil
}
func (p *photonPersistentDiskProvisioner) Provision(selectedNode *v1.Node, allowedTopologies []v1.TopologySelectorTerm) (*v1.PersistentVolume, error) {
if !util.AccessModesContainedInAll(p.plugin.GetAccessModes(), p.options.PVC.Spec.AccessModes) {
return nil, fmt.Errorf("invalid AccessModes %v: only AccessModes %v are supported", p.options.PVC.Spec.AccessModes, p.plugin.GetAccessModes())
}
if util.CheckPersistentVolumeClaimModeBlock(p.options.PVC) {
return nil, fmt.Errorf("%s does not support block volume provisioning", p.plugin.GetPluginName())
}
pdID, sizeGB, fstype, err := p.manager.CreateVolume(p)
if err != nil {
return nil, err
}
if fstype == "" {
fstype = "ext4"
}
pv := &v1.PersistentVolume{
ObjectMeta: metav1.ObjectMeta{
Name: p.options.PVName,
Labels: map[string]string{},
Annotations: map[string]string{
util.VolumeDynamicallyCreatedByKey: "photon-volume-dynamic-provisioner",
},
},
Spec: v1.PersistentVolumeSpec{
PersistentVolumeReclaimPolicy: p.options.PersistentVolumeReclaimPolicy,
AccessModes: p.options.PVC.Spec.AccessModes,
Capacity: v1.ResourceList{
v1.ResourceName(v1.ResourceStorage): resource.MustParse(fmt.Sprintf("%dGi", sizeGB)),
},
PersistentVolumeSource: v1.PersistentVolumeSource{
PhotonPersistentDisk: &v1.PhotonPersistentDiskVolumeSource{
PdID: pdID,
FSType: fstype,
},
},
MountOptions: p.options.MountOptions,
},
}
if len(p.options.PVC.Spec.AccessModes) == 0 {
pv.Spec.AccessModes = p.plugin.GetAccessModes()
}
return pv, nil
}
func getVolumeSource(
spec *volume.Spec) (*v1.PhotonPersistentDiskVolumeSource, bool, error) {
if spec.Volume != nil && spec.Volume.PhotonPersistentDisk != nil {
return spec.Volume.PhotonPersistentDisk, spec.ReadOnly, nil
} else if spec.PersistentVolume != nil &&
spec.PersistentVolume.Spec.PhotonPersistentDisk != nil {
return spec.PersistentVolume.Spec.PhotonPersistentDisk, spec.ReadOnly, nil
}
return nil, false, fmt.Errorf("Spec does not reference a Photon Controller persistent disk type")
}

View File

@@ -0,0 +1,235 @@
/*
Copyright 2014 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 photon_pd
import (
"fmt"
"os"
"path"
"testing"
"k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/types"
utiltesting "k8s.io/client-go/util/testing"
"k8s.io/kubernetes/pkg/util/mount"
"k8s.io/kubernetes/pkg/volume"
volumetest "k8s.io/kubernetes/pkg/volume/testing"
)
func TestCanSupport(t *testing.T) {
tmpDir, err := utiltesting.MkTmpdir("photonpdTest")
if err != nil {
t.Fatalf("can't make a temp dir: %v", err)
}
defer os.RemoveAll(tmpDir)
plugMgr := volume.VolumePluginMgr{}
plugMgr.InitPlugins(ProbeVolumePlugins(), nil /* prober */, volumetest.NewFakeVolumeHost(tmpDir, nil, nil))
plug, err := plugMgr.FindPluginByName("kubernetes.io/photon-pd")
if err != nil {
t.Errorf("Can't find the plugin by name")
}
if plug.GetPluginName() != "kubernetes.io/photon-pd" {
t.Errorf("Wrong name: %s", plug.GetPluginName())
}
if !plug.CanSupport(&volume.Spec{Volume: &v1.Volume{VolumeSource: v1.VolumeSource{PhotonPersistentDisk: &v1.PhotonPersistentDiskVolumeSource{}}}}) {
t.Errorf("Expected true")
}
if !plug.CanSupport(&volume.Spec{PersistentVolume: &v1.PersistentVolume{Spec: v1.PersistentVolumeSpec{PersistentVolumeSource: v1.PersistentVolumeSource{PhotonPersistentDisk: &v1.PhotonPersistentDiskVolumeSource{}}}}}) {
t.Errorf("Expected true")
}
}
func TestGetAccessModes(t *testing.T) {
tmpDir, err := utiltesting.MkTmpdir("photonpdTest")
if err != nil {
t.Fatalf("can't make a temp dir: %v", err)
}
defer os.RemoveAll(tmpDir)
plugMgr := volume.VolumePluginMgr{}
plugMgr.InitPlugins(ProbeVolumePlugins(), nil /* prober */, volumetest.NewFakeVolumeHost(tmpDir, nil, nil))
plug, err := plugMgr.FindPersistentPluginByName("kubernetes.io/photon-pd")
if err != nil {
t.Errorf("Can't find the plugin by name")
}
if !volumetest.ContainsAccessMode(plug.GetAccessModes(), v1.ReadWriteOnce) {
t.Errorf("Expected to support AccessModeTypes: %s", v1.ReadWriteOnce)
}
if volumetest.ContainsAccessMode(plug.GetAccessModes(), v1.ReadOnlyMany) {
t.Errorf("Expected not to support AccessModeTypes: %s", v1.ReadOnlyMany)
}
}
type fakePDManager struct {
}
func (fake *fakePDManager) CreateVolume(c *photonPersistentDiskProvisioner) (pdID string, volumeSizeGB int, fstype string, err error) {
return "test-photon-pd-id", 10, "ext4", nil
}
func (fake *fakePDManager) DeleteVolume(cd *photonPersistentDiskDeleter) error {
if cd.pdID != "test-photon-pd-id" {
return fmt.Errorf("Deleter got unexpected volume name: %s", cd.pdID)
}
return nil
}
func TestPlugin(t *testing.T) {
tmpDir, err := utiltesting.MkTmpdir("photonpdTest")
if err != nil {
t.Fatalf("can't make a temp dir: %v", err)
}
defer os.RemoveAll(tmpDir)
plugMgr := volume.VolumePluginMgr{}
plugMgr.InitPlugins(ProbeVolumePlugins(), nil /* prober */, volumetest.NewFakeVolumeHost(tmpDir, nil, nil))
plug, err := plugMgr.FindPluginByName("kubernetes.io/photon-pd")
if err != nil {
t.Errorf("Can't find the plugin by name")
}
spec := &v1.Volume{
Name: "vol1",
VolumeSource: v1.VolumeSource{
PhotonPersistentDisk: &v1.PhotonPersistentDiskVolumeSource{
PdID: "pdid",
FSType: "ext4",
},
},
}
fakeManager := &fakePDManager{}
fakeMounter := &mount.FakeMounter{}
mounter, err := plug.(*photonPersistentDiskPlugin).newMounterInternal(volume.NewSpecFromVolume(spec), types.UID("poduid"), fakeManager, fakeMounter)
if err != nil {
t.Errorf("Failed to make a new Mounter: %v", err)
}
if mounter == nil {
t.Errorf("Got a nil Mounter")
}
volPath := path.Join(tmpDir, "pods/poduid/volumes/kubernetes.io~photon-pd/vol1")
path := mounter.GetPath()
if path != volPath {
t.Errorf("Got unexpected path: %s", path)
}
if err := mounter.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)
}
}
fakeManager = &fakePDManager{}
unmounter, err := plug.(*photonPersistentDiskPlugin).newUnmounterInternal("vol1", types.UID("poduid"), fakeManager, fakeMounter)
if err != nil {
t.Errorf("Failed to make a new Unmounter: %v", err)
}
if unmounter == nil {
t.Errorf("Got a nil Unmounter")
}
if err := unmounter.TearDown(); err != nil {
t.Errorf("Expected success, got: %v", err)
}
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)
}
// Test Provisioner
options := volume.VolumeOptions{
PVC: volumetest.CreateTestPVC("10Gi", []v1.PersistentVolumeAccessMode{v1.ReadWriteOnce}),
PersistentVolumeReclaimPolicy: v1.PersistentVolumeReclaimDelete,
}
provisioner, err := plug.(*photonPersistentDiskPlugin).newProvisionerInternal(options, &fakePDManager{})
if err != nil {
t.Fatalf("Error creating new provisioner:%v", err)
}
persistentSpec, err := provisioner.Provision(nil, nil)
if err != nil {
t.Errorf("Provision() failed: %v", err)
}
if persistentSpec.Spec.PersistentVolumeSource.PhotonPersistentDisk.PdID != "test-photon-pd-id" {
t.Errorf("Provision() returned unexpected persistent disk ID: %s", persistentSpec.Spec.PersistentVolumeSource.PhotonPersistentDisk.PdID)
}
cap := persistentSpec.Spec.Capacity[v1.ResourceStorage]
size := cap.Value()
if size != 10*1024*1024*1024 {
t.Errorf("Provision() returned unexpected volume size: %v", size)
}
// Test Deleter
volSpec := &volume.Spec{
PersistentVolume: persistentSpec,
}
deleter, err := plug.(*photonPersistentDiskPlugin).newDeleterInternal(volSpec, &fakePDManager{})
if err != nil {
t.Fatalf("Error creating new deleter:%v", err)
}
err = deleter.Delete()
if err != nil {
t.Errorf("Deleter() failed: %v", err)
}
}
func TestMounterAndUnmounterTypeAssert(t *testing.T) {
tmpDir, err := utiltesting.MkTmpdir("photonpdTest")
if err != nil {
t.Fatalf("can't make a temp dir: %v", err)
}
defer os.RemoveAll(tmpDir)
plugMgr := volume.VolumePluginMgr{}
plugMgr.InitPlugins(ProbeVolumePlugins(), nil /* prober */, volumetest.NewFakeVolumeHost(tmpDir, nil, nil))
plug, err := plugMgr.FindPluginByName("kubernetes.io/photon-pd")
if err != nil {
t.Errorf("Can't find the plugin by name")
}
spec := &v1.Volume{
Name: "vol1",
VolumeSource: v1.VolumeSource{
PhotonPersistentDisk: &v1.PhotonPersistentDiskVolumeSource{
PdID: "pdid",
FSType: "ext4",
},
},
}
mounter, err := plug.(*photonPersistentDiskPlugin).newMounterInternal(volume.NewSpecFromVolume(spec), types.UID("poduid"), &fakePDManager{}, &mount.FakeMounter{})
if err != nil {
t.Fatalf("Error creating new mounter:%v", err)
}
if _, ok := mounter.(volume.Unmounter); ok {
t.Errorf("Volume Mounter can be type-assert to Unmounter")
}
unmounter, err := plug.(*photonPersistentDiskPlugin).newUnmounterInternal("vol1", types.UID("poduid"), &fakePDManager{}, &mount.FakeMounter{})
if err != nil {
t.Fatalf("Error creating new unmounter:%v", err)
}
if _, ok := unmounter.(volume.Mounter); ok {
t.Errorf("Volume Unmounter can be type-assert to Mounter")
}
}

View File

@@ -0,0 +1,153 @@
/*
Copyright 2016 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 photon_pd
import (
"errors"
"fmt"
"io/ioutil"
"strings"
"time"
"github.com/golang/glog"
"k8s.io/api/core/v1"
"k8s.io/kubernetes/pkg/cloudprovider"
"k8s.io/kubernetes/pkg/cloudprovider/providers/photon"
"k8s.io/kubernetes/pkg/volume"
volumeutil "k8s.io/kubernetes/pkg/volume/util"
)
const (
maxRetries = 10
checkSleepDuration = time.Second
diskByIDPath = "/dev/disk/by-id/"
diskPhotonPrefix = "wwn-0x"
)
var ErrProbeVolume = errors.New("Error scanning attached volumes")
// volNameToDeviceName is a mapping between spec.Name from detacher
// and the device name inside scsi path. Once pvscsi controller is
// supported, this won't be needed.
var volNameToDeviceName = make(map[string]string)
type PhotonDiskUtil struct{}
func removeFromScsiSubsystem(volName string) {
// TODO: if using pvscsi controller, this won't be needed
deviceName := volNameToDeviceName[volName]
fileName := "/sys/block/" + deviceName + "/device/delete"
data := []byte("1")
ioutil.WriteFile(fileName, data, 0666)
}
func scsiHostScan() {
// TODO: if using pvscsi controller, this won't be needed
scsi_path := "/sys/class/scsi_host/"
if dirs, err := ioutil.ReadDir(scsi_path); err == nil {
for _, f := range dirs {
name := scsi_path + f.Name() + "/scan"
data := []byte("- - -")
ioutil.WriteFile(name, data, 0666)
glog.Errorf("scsiHostScan scan for %s", name)
}
}
}
func verifyDevicePath(path string) (string, error) {
if pathExists, err := volumeutil.PathExists(path); err != nil {
return "", fmt.Errorf("Error checking if path exists: %v", err)
} else if pathExists {
return path, nil
}
glog.V(4).Infof("verifyDevicePath: path not exists yet")
return "", nil
}
// CreateVolume creates a PhotonController persistent disk.
func (util *PhotonDiskUtil) CreateVolume(p *photonPersistentDiskProvisioner) (pdID string, capacityGB int, fstype string, err error) {
cloud, err := getCloudProvider(p.plugin.host.GetCloudProvider())
if err != nil {
glog.Errorf("Photon Controller Util: CreateVolume failed to get cloud provider. Error [%v]", err)
return "", 0, "", err
}
capacity := p.options.PVC.Spec.Resources.Requests[v1.ResourceName(v1.ResourceStorage)]
volSizeBytes := capacity.Value()
// PhotonController works with GB, convert to GB with rounding up
volSizeGB := int(volumeutil.RoundUpSize(volSizeBytes, 1024*1024*1024))
name := volumeutil.GenerateVolumeName(p.options.ClusterName, p.options.PVName, 255)
volumeOptions := &photon.VolumeOptions{
CapacityGB: volSizeGB,
Tags: *p.options.CloudTags,
Name: name,
}
for parameter, value := range p.options.Parameters {
switch strings.ToLower(parameter) {
case "flavor":
volumeOptions.Flavor = value
case volume.VolumeParameterFSType:
fstype = value
glog.V(4).Infof("Photon Controller Util: Setting fstype to %s", fstype)
default:
glog.Errorf("Photon Controller Util: invalid option %s for volume plugin %s.", parameter, p.plugin.GetPluginName())
return "", 0, "", fmt.Errorf("Photon Controller Util: invalid option %s for volume plugin %s.", parameter, p.plugin.GetPluginName())
}
}
pdID, err = cloud.CreateDisk(volumeOptions)
if err != nil {
glog.Errorf("Photon Controller Util: failed to CreateDisk. Error [%v]", err)
return "", 0, "", err
}
glog.V(4).Infof("Successfully created Photon Controller persistent disk %s", name)
return pdID, volSizeGB, "", nil
}
// DeleteVolume deletes a vSphere volume.
func (util *PhotonDiskUtil) DeleteVolume(pd *photonPersistentDiskDeleter) error {
cloud, err := getCloudProvider(pd.plugin.host.GetCloudProvider())
if err != nil {
glog.Errorf("Photon Controller Util: DeleteVolume failed to get cloud provider. Error [%v]", err)
return err
}
if err = cloud.DeleteDisk(pd.pdID); err != nil {
glog.Errorf("Photon Controller Util: failed to DeleteDisk for pdID %s. Error [%v]", pd.pdID, err)
return err
}
glog.V(4).Infof("Successfully deleted PhotonController persistent disk %s", pd.pdID)
return nil
}
func getCloudProvider(cloud cloudprovider.Interface) (*photon.PCCloud, error) {
if cloud == nil {
glog.Errorf("Photon Controller Util: Cloud provider not initialized properly")
return nil, fmt.Errorf("Photon Controller Util: Cloud provider not initialized properly")
}
pcc := cloud.(*photon.PCCloud)
if pcc == nil {
glog.Errorf("Invalid cloud provider: expected Photon Controller")
return nil, fmt.Errorf("Invalid cloud provider: expected Photon Controller")
}
return pcc, nil
}