Merge pull request #79552 from wongma7/windows-ebs

Add support for AWS EBS on windows
This commit is contained in:
Kubernetes Prow Robot 2019-07-23 17:14:28 -07:00 committed by GitHub
commit 60c2d44129
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 160 additions and 11 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"
@ -406,9 +407,16 @@ func (b *awsElasticBlockStoreMounter) SetUpAt(dir string, mounterArgs volume.Mou
globalPDPath := makeGlobalPDPath(b.plugin.host, b.volumeID)
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.
options := []string{"bind"}

View File

@ -1526,6 +1526,7 @@ func InitAwsDriver() testsuites.TestDriver {
SupportedFsType: sets.NewString(
"", // Default fsType
"ext3",
"ntfs",
),
SupportedMountOption: sets.NewString("debug", "nouid32"),
Capabilities: map[testsuites.Capability]bool{
@ -1595,11 +1596,17 @@ func (a *awsDriver) GetClaimSize() string {
}
func (a *awsDriver) PrepareTest(f *framework.Framework) (*testsuites.PerTestConfig, func()) {
return &testsuites.PerTestConfig{
config := &testsuites.PerTestConfig{
Driver: a,
Prefix: "aws",
Framework: f,
}, func() {}
}
if framework.NodeOSDistroIs("windows") {
config.ClientNodeSelector = map[string]string{
"beta.kubernetes.io/os": "windows",
}
}
return config, func() {}
}
// TODO: Fix authorization error in attach operation and uncomment below