From c7c755a0d1cb58f1f4d5796c360c8d7e46c406a0 Mon Sep 17 00:00:00 2001 From: Gab Satchi Date: Fri, 2 Aug 2019 09:52:00 -0400 Subject: [PATCH] Add support for vSphere volume mount/attach on Windows Signed-off-by: Ben Moss --- pkg/volume/vsphere_volume/BUILD | 3 + pkg/volume/vsphere_volume/attacher.go | 10 ++- pkg/volume/vsphere_volume/vsphere_volume.go | 11 +++- .../vsphere_volume/vsphere_volume_util.go | 11 ---- .../vsphere_volume_util_linux.go | 35 +++++++++++ .../vsphere_volume_util_unsupported.go | 25 ++++++++ .../vsphere_volume_util_windows.go | 63 +++++++++++++++++++ 7 files changed, 142 insertions(+), 16 deletions(-) create mode 100644 pkg/volume/vsphere_volume/vsphere_volume_util_linux.go create mode 100644 pkg/volume/vsphere_volume/vsphere_volume_util_unsupported.go create mode 100644 pkg/volume/vsphere_volume/vsphere_volume_util_windows.go diff --git a/pkg/volume/vsphere_volume/BUILD b/pkg/volume/vsphere_volume/BUILD index 1263202752d..bbfc95c05c2 100644 --- a/pkg/volume/vsphere_volume/BUILD +++ b/pkg/volume/vsphere_volume/BUILD @@ -13,6 +13,9 @@ go_library( "vsphere_volume.go", "vsphere_volume_block.go", "vsphere_volume_util.go", + "vsphere_volume_util_linux.go", + "vsphere_volume_util_unsupported.go", + "vsphere_volume_util_windows.go", ], importpath = "k8s.io/kubernetes/pkg/volume/vsphere_volume", deps = [ diff --git a/pkg/volume/vsphere_volume/attacher.go b/pkg/volume/vsphere_volume/attacher.go index 8a9363253f9..49715d5f5a9 100644 --- a/pkg/volume/vsphere_volume/attacher.go +++ b/pkg/volume/vsphere_volume/attacher.go @@ -20,6 +20,7 @@ import ( "fmt" "os" "path/filepath" + "runtime" "time" "k8s.io/api/core/v1" @@ -205,12 +206,17 @@ func (plugin *vsphereVolumePlugin) GetDeviceMountRefs(deviceMountPath string) ([ // MountDevice mounts device to global mount point. func (attacher *vsphereVMDKAttacher) MountDevice(spec *volume.Spec, devicePath string, deviceMountPath string) error { + klog.Info("vsphere MountDevice", devicePath, deviceMountPath) mounter := attacher.host.GetMounter(vsphereVolumePluginName) notMnt, err := mounter.IsLikelyNotMountPoint(deviceMountPath) if err != nil { if os.IsNotExist(err) { - if err := os.MkdirAll(deviceMountPath, 0750); err != nil { - klog.Errorf("Failed to create directory at %#v. err: %s", deviceMountPath, err) + dir := deviceMountPath + if runtime.GOOS == "windows" { + dir = filepath.Dir(deviceMountPath) + } + if err := os.MkdirAll(dir, 0750); err != nil { + klog.Errorf("Failed to create directory at %#v. err: %s", dir, err) return err } notMnt = true diff --git a/pkg/volume/vsphere_volume/vsphere_volume.go b/pkg/volume/vsphere_volume/vsphere_volume.go index 8dc860b9c0e..d8254f0d76d 100644 --- a/pkg/volume/vsphere_volume/vsphere_volume.go +++ b/pkg/volume/vsphere_volume/vsphere_volume.go @@ -20,6 +20,7 @@ import ( "fmt" "os" "path/filepath" + "runtime" "strings" "k8s.io/api/core/v1" @@ -239,9 +240,13 @@ func (b *vsphereVolumeMounter) SetUpAt(dir string, mounterArgs volume.MounterArg return nil } - if err := os.MkdirAll(dir, 0750); err != nil { - klog.V(4).Infof("Could not create directory %s: %v", dir, err) - return err + if runtime.GOOS != "windows" { + // On Windows, Mount will create the parent of dir and mklink (create a symbolic link) at dir later, so don't create a + // directory at dir now. Otherwise mklink will error: "Cannot create a file when that file already exists". + if err := os.MkdirAll(dir, 0750); err != nil { + klog.Errorf("Could not create directory %s: %v", dir, err) + return err + } } options := []string{"bind"} diff --git a/pkg/volume/vsphere_volume/vsphere_volume_util.go b/pkg/volume/vsphere_volume/vsphere_volume_util.go index fbb21bf2154..a2db21ec629 100644 --- a/pkg/volume/vsphere_volume/vsphere_volume_util.go +++ b/pkg/volume/vsphere_volume/vsphere_volume_util.go @@ -27,7 +27,6 @@ import ( cloudprovider "k8s.io/cloud-provider" volumehelpers "k8s.io/cloud-provider/volume/helpers" "k8s.io/klog" - "k8s.io/kubernetes/pkg/util/mount" "k8s.io/kubernetes/pkg/volume" volumeutil "k8s.io/kubernetes/pkg/volume/util" "k8s.io/legacy-cloud-providers/vsphere" @@ -75,16 +74,6 @@ type VolumeSpec struct { Labels map[string]string } -func verifyDevicePath(path string) (string, error) { - if pathExists, err := mount.PathExists(path); err != nil { - return "", fmt.Errorf("Error checking if path exists: %v", err) - } else if pathExists { - return path, nil - } - - return "", nil -} - // CreateVolume creates a vSphere volume. func (util *VsphereDiskUtil) CreateVolume(v *vsphereVolumeProvisioner, selectedZone []string) (volSpec *VolumeSpec, err error) { var fstype string diff --git a/pkg/volume/vsphere_volume/vsphere_volume_util_linux.go b/pkg/volume/vsphere_volume/vsphere_volume_util_linux.go new file mode 100644 index 00000000000..9e30e1c1442 --- /dev/null +++ b/pkg/volume/vsphere_volume/vsphere_volume_util_linux.go @@ -0,0 +1,35 @@ +// +build linux + +/* +Copyright 2019 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 vsphere_volume + +import ( + "fmt" + + "k8s.io/kubernetes/pkg/util/mount" +) + +func verifyDevicePath(path string) (string, error) { + if pathExists, err := mount.PathExists(path); err != nil { + return "", fmt.Errorf("Error checking if path exists: %v", err) + } else if pathExists { + return path, nil + } + + return "", nil +} diff --git a/pkg/volume/vsphere_volume/vsphere_volume_util_unsupported.go b/pkg/volume/vsphere_volume/vsphere_volume_util_unsupported.go new file mode 100644 index 00000000000..2f77a1bc64d --- /dev/null +++ b/pkg/volume/vsphere_volume/vsphere_volume_util_unsupported.go @@ -0,0 +1,25 @@ +// +build !linux,!windows + +/* +Copyright 2019 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 vsphere_volume + +import "errors" + +func verifyDevicePath(path string) (string, error) { + return "", errors.New("unsupported") +} diff --git a/pkg/volume/vsphere_volume/vsphere_volume_util_windows.go b/pkg/volume/vsphere_volume/vsphere_volume_util_windows.go new file mode 100644 index 00000000000..5b075482563 --- /dev/null +++ b/pkg/volume/vsphere_volume/vsphere_volume_util_windows.go @@ -0,0 +1,63 @@ +// +build windows + +/* +Copyright 2019 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 vsphere_volume + +import ( + "encoding/json" + "fmt" + "os/exec" + "strings" + + "k8s.io/klog" +) + +type diskInfoResult struct { + Number json.Number + SerialNumber string +} + +func verifyDevicePath(path string) (string, error) { + if !strings.Contains(path, diskByIDPath) { + // If this volume has already been mounted then + // its devicePath will have already been converted to a disk number + klog.V(4).Infof("Found vSphere disk attached with disk number %v", path) + return path, nil + } + cmd := exec.Command("powershell", "/c", "Get-Disk | Select Number, SerialNumber | ConvertTo-JSON") + output, err := cmd.Output() + if err != nil { + klog.Errorf("Get-Disk failed, error: %v, output: %q", err, string(output)) + return "", err + } + + var results []diskInfoResult + if err = json.Unmarshal(output, &results); err != nil { + klog.Errorf("Failed to unmarshal Get-Disk json, output: %q", string(output)) + return "", err + } + serialNumber := strings.TrimPrefix(path, diskByIDPath+diskSCSIPrefix) + for _, v := range results { + if v.SerialNumber == serialNumber { + klog.V(4).Infof("Found vSphere disk attached with serial %v", serialNumber) + return v.Number.String(), nil + } + } + + return "", fmt.Errorf("unable to find vSphere disk with serial %v", serialNumber) +}