Add support for windows to AWS EBS

This commit is contained in:
Matthew Wong 2019-06-28 20:01:33 +00:00
parent f3a03f71af
commit 51615f691d
6 changed files with 151 additions and 9 deletions

View File

@ -10,6 +10,9 @@ go_library(
name = "go_default_library",
srcs = [
"attacher.go",
"attacher_linux.go",
"attacher_unsupported.go",
"attacher_windows.go",
"aws_ebs.go",
"aws_ebs_block.go",
"aws_util.go",

View File

@ -20,6 +20,8 @@ import (
"fmt"
"os"
"path"
"path/filepath"
"runtime"
"strconv"
"time"
@ -175,15 +177,14 @@ func (attacher *awsElasticBlockStoreAttacher) WaitForAttach(spec *volume.Spec, d
for {
select {
case <-ticker.C:
klog.V(5).Infof("Checking AWS Volume %q is attached.", volumeID)
devicePaths := getDiskByIDPaths(aws.KubernetesVolumeID(volumeSource.VolumeID), partition, devicePath)
path, err := verifyDevicePath(devicePaths)
klog.V(5).Infof("Checking AWS Volume %q is attached at devicePath %q.", volumeID, devicePath)
path, err := attacher.getDevicePath(volumeSource.VolumeID, partition, devicePath)
if err != nil {
// Log error, if any, and continue checking periodically. See issue #11321
klog.Errorf("Error verifying AWS Volume (%q) is attached: %v", volumeID, err)
klog.Errorf("Error verifying AWS Volume (%q) is attached at devicePath %q: %v", volumeID, devicePath, err)
} else if path != "" {
// A device path has successfully been created for the PD
klog.Infof("Successfully found attached AWS Volume %q.", volumeID)
klog.Infof("Successfully found attached AWS Volume %q at path %q.", volumeID, path)
return path, nil
}
case <-timer.C:
@ -208,8 +209,17 @@ func (attacher *awsElasticBlockStoreAttacher) MountDevice(spec *volume.Spec, dev
notMnt, err := mounter.IsLikelyNotMountPoint(deviceMountPath)
if err != nil {
if os.IsNotExist(err) {
if err := os.MkdirAll(deviceMountPath, 0750); err != nil {
return err
dir := deviceMountPath
if runtime.GOOS == "windows" {
// On Windows, FormatAndMount will mklink (create a symbolic link) at deviceMountPath later, so don't create a
// directory at deviceMountPath now. Otherwise mklink will error: "Cannot create a file when that file already exists".
// Instead, create the parent of deviceMountPath. For example when deviceMountPath is:
// C:\var\lib\kubelet\plugins\kubernetes.io\aws-ebs\mounts\aws\us-west-2b\vol-xxx
// create us-west-2b. FormatAndMount will make vol-xxx a symlink to the drive (e.g. D:\)
dir = filepath.Dir(deviceMountPath)
}
if err := os.MkdirAll(dir, 0750); err != nil {
return fmt.Errorf("making dir %s failed with %s", dir, err)
}
notMnt = true
} else {

View File

@ -0,0 +1,28 @@
// +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 awsebs
import (
"k8s.io/legacy-cloud-providers/aws"
)
func (attacher *awsElasticBlockStoreAttacher) getDevicePath(volumeID, partition, devicePath string) (string, error) {
devicePaths := getDiskByIDPaths(aws.KubernetesVolumeID(volumeID), partition, devicePath)
return verifyDevicePath(devicePaths)
}

View File

@ -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 awsebs
import "errors"
func (attacher *awsElasticBlockStoreAttacher) getDevicePath(volumeID, partition, devicePath string) (string, error) {
return "", errors.New("unsupported")
}

View File

@ -0,0 +1,68 @@
// +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 awsebs
import (
"fmt"
"regexp"
"strings"
)
// ebsnvme-id is present on AWS-provided Windows Server AMIs
// https://docs.aws.amazon.com/AWSEC2/latest/WindowsGuide/nvme-ebs-volumes.html#identify-nvme-ebs-device
const ebsnvmeID = `C:\ProgramData\Amazon\Tools\ebsnvme-id.exe`
func (attacher *awsElasticBlockStoreAttacher) getDevicePath(volumeID, partition, devicePath string) (string, error) {
return attacher.getDiskNumber(volumeID)
}
// getDiskNumber gets the Windows disk number for a given volume ID. The disk number is needed for mounting.
// TODO This only works for Nitro-based instances
// TODO fallback to Get-Disk
func (attacher *awsElasticBlockStoreAttacher) getDiskNumber(volumeID string) (string, error) {
// Split the ID from zone: aws://us-west-2b/vol-06d0909eb358b05f9
split := strings.Split(volumeID, "/")
volumeID = split[len(split)-1]
exec := attacher.host.GetExec(awsElasticBlockStorePluginName)
output, err := exec.Run(ebsnvmeID)
if err != nil {
return "", fmt.Errorf("error calling ebsnvme-id.exe: %v", err)
}
// ebsnvme-id.exe will output a list of disks in this format:
// ```
// Disk Number: 1
// Volume ID: vol-06d0909eb358b05f9
// Device Name: /dev/xvdch
// ```
// Don't try to match devicePath against "Device Name" not only because volume ID is sufficient,
// but because devicePath may change between Linux & Windows formats between WaitForAttach calls.
// The first attach and mount, WaitForAttach gets devicePath as the Linux format /dev/xvdch. Then
// WaitForAttach returns the disk number as the "right" devicePath and that is persisted to ASW.
// In subsequent mounts of the same disk, WaitForAttach gets devicePath as the Windows format it
// returned the first time.
diskRe := regexp.MustCompile(
`Disk Number: (\d+)\s*` +
`Volume ID: ` + volumeID + `\s*`)
matches := diskRe.FindStringSubmatch(string(output))
if len(matches) != 2 {
return "", fmt.Errorf("disk not found in ebsnvme-id.exe output: %q", string(output))
}
return matches[1], nil
}

View File

@ -22,6 +22,7 @@ import (
"os"
"path/filepath"
"regexp"
"runtime"
"strconv"
"strings"
@ -401,8 +402,15 @@ func (b *awsElasticBlockStoreMounter) SetUpAt(dir string, mounterArgs volume.Mou
globalPDPath := makeGlobalPDPath(b.plugin.host, b.volumeID)
if err := os.MkdirAll(dir, 0750); err != nil {
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".
// Instead, do nothing. For example when dir is:
// C:\var\lib\kubelet\pods\xxx\volumes\kubernetes.io~aws-ebs\pvc-xxx
// do nothing. Mount will make pvc-xxx a symlink to the global mount path (e.g. C:\var\lib\kubelet\plugins\kubernetes.io\aws-ebs\mounts\aws\us-west-2b\vol-xxx)
if err := os.MkdirAll(dir, 0750); err != nil {
return err
}
}
// Perform a bind mount to the full path to allow duplicate mounts of the same PD.