mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-25 20:53:33 +00:00
Merge pull request #35883 from justinsb/aws_strong_volumetype
Automatic merge from submit-queue AWS: strong-typing for k8s vs aws volume ids
This commit is contained in:
commit
f4738ff575
@ -21,6 +21,7 @@ go_library(
|
|||||||
"log_handler.go",
|
"log_handler.go",
|
||||||
"retry_handler.go",
|
"retry_handler.go",
|
||||||
"sets_ippermissions.go",
|
"sets_ippermissions.go",
|
||||||
|
"volumes.go",
|
||||||
],
|
],
|
||||||
tags = ["automanaged"],
|
tags = ["automanaged"],
|
||||||
deps = [
|
deps = [
|
||||||
|
@ -21,7 +21,6 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"net"
|
"net"
|
||||||
"net/url"
|
|
||||||
"regexp"
|
"regexp"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
@ -299,31 +298,31 @@ type Volumes interface {
|
|||||||
// Attach the disk to the node with the specified NodeName
|
// Attach the disk to the node with the specified NodeName
|
||||||
// nodeName can be empty to mean "the instance on which we are running"
|
// nodeName can be empty to mean "the instance on which we are running"
|
||||||
// Returns the device (e.g. /dev/xvdf) where we attached the volume
|
// Returns the device (e.g. /dev/xvdf) where we attached the volume
|
||||||
AttachDisk(diskName string, nodeName types.NodeName, readOnly bool) (string, error)
|
AttachDisk(diskName KubernetesVolumeID, nodeName types.NodeName, readOnly bool) (string, error)
|
||||||
// Detach the disk from the node with the specified NodeName
|
// Detach the disk from the node with the specified NodeName
|
||||||
// nodeName can be empty to mean "the instance on which we are running"
|
// nodeName can be empty to mean "the instance on which we are running"
|
||||||
// Returns the device where the volume was attached
|
// Returns the device where the volume was attached
|
||||||
DetachDisk(diskName string, nodeName types.NodeName) (string, error)
|
DetachDisk(diskName KubernetesVolumeID, nodeName types.NodeName) (string, error)
|
||||||
|
|
||||||
// Create a volume with the specified options
|
// Create a volume with the specified options
|
||||||
CreateDisk(volumeOptions *VolumeOptions) (volumeName string, err error)
|
CreateDisk(volumeOptions *VolumeOptions) (volumeName KubernetesVolumeID, err error)
|
||||||
// Delete the specified volume
|
// Delete the specified volume
|
||||||
// Returns true iff the volume was deleted
|
// Returns true iff the volume was deleted
|
||||||
// If the was not found, returns (false, nil)
|
// If the was not found, returns (false, nil)
|
||||||
DeleteDisk(volumeName string) (bool, error)
|
DeleteDisk(volumeName KubernetesVolumeID) (bool, error)
|
||||||
|
|
||||||
// Get labels to apply to volume on creation
|
// Get labels to apply to volume on creation
|
||||||
GetVolumeLabels(volumeName string) (map[string]string, error)
|
GetVolumeLabels(volumeName KubernetesVolumeID) (map[string]string, error)
|
||||||
|
|
||||||
// Get volume's disk path from volume name
|
// Get volume's disk path from volume name
|
||||||
// return the device path where the volume is attached
|
// return the device path where the volume is attached
|
||||||
GetDiskPath(volumeName string) (string, error)
|
GetDiskPath(volumeName KubernetesVolumeID) (string, error)
|
||||||
|
|
||||||
// Check if the volume is already attached to the node with the specified NodeName
|
// Check if the volume is already attached to the node with the specified NodeName
|
||||||
DiskIsAttached(diskName string, nodeName types.NodeName) (bool, error)
|
DiskIsAttached(diskName KubernetesVolumeID, nodeName types.NodeName) (bool, error)
|
||||||
|
|
||||||
// Check if a list of volumes are attached to the node with the specified NodeName
|
// Check if a list of volumes are attached to the node with the specified NodeName
|
||||||
DisksAreAttached(diskNames []string, nodeName types.NodeName) (map[string]bool, error)
|
DisksAreAttached(diskNames []KubernetesVolumeID, nodeName types.NodeName) (map[KubernetesVolumeID]bool, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
// InstanceGroups is an interface for managing cloud-managed instance groups / autoscaling instance groups
|
// InstanceGroups is an interface for managing cloud-managed instance groups / autoscaling instance groups
|
||||||
@ -365,7 +364,7 @@ type Cloud struct {
|
|||||||
// attached, to avoid a race condition where we assign a device mapping
|
// attached, to avoid a race condition where we assign a device mapping
|
||||||
// and then get a second request before we attach the volume
|
// and then get a second request before we attach the volume
|
||||||
attachingMutex sync.Mutex
|
attachingMutex sync.Mutex
|
||||||
attaching map[types.NodeName]map[mountDevice]string
|
attaching map[types.NodeName]map[mountDevice]awsVolumeID
|
||||||
}
|
}
|
||||||
|
|
||||||
var _ Volumes = &Cloud{}
|
var _ Volumes = &Cloud{}
|
||||||
@ -797,7 +796,7 @@ func newAWSCloud(config io.Reader, awsServices Services) (*Cloud, error) {
|
|||||||
cfg: cfg,
|
cfg: cfg,
|
||||||
region: regionName,
|
region: regionName,
|
||||||
|
|
||||||
attaching: make(map[types.NodeName]map[mountDevice]string),
|
attaching: make(map[types.NodeName]map[mountDevice]awsVolumeID),
|
||||||
}
|
}
|
||||||
|
|
||||||
selfAWSInstance, err := awsCloud.buildSelfAWSInstance()
|
selfAWSInstance, err := awsCloud.buildSelfAWSInstance()
|
||||||
@ -1168,7 +1167,7 @@ func (i *awsInstance) describeInstance() (*ec2.Instance, error) {
|
|||||||
// Gets the mountDevice already assigned to the volume, or assigns an unused mountDevice.
|
// Gets the mountDevice already assigned to the volume, or assigns an unused mountDevice.
|
||||||
// If the volume is already assigned, this will return the existing mountDevice with alreadyAttached=true.
|
// If the volume is already assigned, this will return the existing mountDevice with alreadyAttached=true.
|
||||||
// Otherwise the mountDevice is assigned by finding the first available mountDevice, and it is returned with alreadyAttached=false.
|
// Otherwise the mountDevice is assigned by finding the first available mountDevice, and it is returned with alreadyAttached=false.
|
||||||
func (c *Cloud) getMountDevice(i *awsInstance, volumeID string, assign bool) (assigned mountDevice, alreadyAttached bool, err error) {
|
func (c *Cloud) getMountDevice(i *awsInstance, volumeID awsVolumeID, assign bool) (assigned mountDevice, alreadyAttached bool, err error) {
|
||||||
instanceType := i.getInstanceType()
|
instanceType := i.getInstanceType()
|
||||||
if instanceType == nil {
|
if instanceType == nil {
|
||||||
return "", false, fmt.Errorf("could not get instance type for instance: %s", i.awsID)
|
return "", false, fmt.Errorf("could not get instance type for instance: %s", i.awsID)
|
||||||
@ -1178,7 +1177,7 @@ func (c *Cloud) getMountDevice(i *awsInstance, volumeID string, assign bool) (as
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return "", false, err
|
return "", false, err
|
||||||
}
|
}
|
||||||
deviceMappings := map[mountDevice]string{}
|
deviceMappings := map[mountDevice]awsVolumeID{}
|
||||||
for _, blockDevice := range info.BlockDeviceMappings {
|
for _, blockDevice := range info.BlockDeviceMappings {
|
||||||
name := aws.StringValue(blockDevice.DeviceName)
|
name := aws.StringValue(blockDevice.DeviceName)
|
||||||
if strings.HasPrefix(name, "/dev/sd") {
|
if strings.HasPrefix(name, "/dev/sd") {
|
||||||
@ -1190,7 +1189,7 @@ func (c *Cloud) getMountDevice(i *awsInstance, volumeID string, assign bool) (as
|
|||||||
if len(name) < 1 || len(name) > 2 {
|
if len(name) < 1 || len(name) > 2 {
|
||||||
glog.Warningf("Unexpected EBS DeviceName: %q", aws.StringValue(blockDevice.DeviceName))
|
glog.Warningf("Unexpected EBS DeviceName: %q", aws.StringValue(blockDevice.DeviceName))
|
||||||
}
|
}
|
||||||
deviceMappings[mountDevice(name)] = aws.StringValue(blockDevice.Ebs.VolumeId)
|
deviceMappings[mountDevice(name)] = awsVolumeID(aws.StringValue(blockDevice.Ebs.VolumeId))
|
||||||
}
|
}
|
||||||
|
|
||||||
// We lock to prevent concurrent mounts from conflicting
|
// We lock to prevent concurrent mounts from conflicting
|
||||||
@ -1236,7 +1235,7 @@ func (c *Cloud) getMountDevice(i *awsInstance, volumeID string, assign bool) (as
|
|||||||
|
|
||||||
attaching := c.attaching[i.nodeName]
|
attaching := c.attaching[i.nodeName]
|
||||||
if attaching == nil {
|
if attaching == nil {
|
||||||
attaching = make(map[mountDevice]string)
|
attaching = make(map[mountDevice]awsVolumeID)
|
||||||
c.attaching[i.nodeName] = attaching
|
c.attaching[i.nodeName] = attaching
|
||||||
}
|
}
|
||||||
attaching[chosen] = volumeID
|
attaching[chosen] = volumeID
|
||||||
@ -1247,7 +1246,7 @@ func (c *Cloud) getMountDevice(i *awsInstance, volumeID string, assign bool) (as
|
|||||||
|
|
||||||
// endAttaching removes the entry from the "attachments in progress" map
|
// endAttaching removes the entry from the "attachments in progress" map
|
||||||
// It returns true if it was found (and removed), false otherwise
|
// It returns true if it was found (and removed), false otherwise
|
||||||
func (c *Cloud) endAttaching(i *awsInstance, volumeID string, mountDevice mountDevice) bool {
|
func (c *Cloud) endAttaching(i *awsInstance, volumeID awsVolumeID, mountDevice mountDevice) bool {
|
||||||
c.attachingMutex.Lock()
|
c.attachingMutex.Lock()
|
||||||
defer c.attachingMutex.Unlock()
|
defer c.attachingMutex.Unlock()
|
||||||
|
|
||||||
@ -1272,44 +1271,16 @@ type awsDisk struct {
|
|||||||
ec2 EC2
|
ec2 EC2
|
||||||
|
|
||||||
// Name in k8s
|
// Name in k8s
|
||||||
name string
|
name KubernetesVolumeID
|
||||||
// id in AWS
|
// id in AWS
|
||||||
awsID string
|
awsID awsVolumeID
|
||||||
}
|
}
|
||||||
|
|
||||||
func newAWSDisk(aws *Cloud, name string) (*awsDisk, error) {
|
func newAWSDisk(aws *Cloud, name KubernetesVolumeID) (*awsDisk, error) {
|
||||||
// name looks like aws://availability-zone/id
|
awsID, err := name.mapToAWSVolumeID()
|
||||||
|
|
||||||
// The original idea of the URL-style name was to put the AZ into the
|
|
||||||
// host, so we could find the AZ immediately from the name without
|
|
||||||
// querying the API. But it turns out we don't actually need it for
|
|
||||||
// multi-AZ clusters, as we put the AZ into the labels on the PV instead.
|
|
||||||
// However, if in future we want to support multi-AZ cluster
|
|
||||||
// volume-awareness without using PersistentVolumes, we likely will
|
|
||||||
// want the AZ in the host.
|
|
||||||
|
|
||||||
if !strings.HasPrefix(name, "aws://") {
|
|
||||||
name = "aws://" + "" + "/" + name
|
|
||||||
}
|
|
||||||
url, err := url.Parse(name)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// TODO: Maybe we should pass a URL into the Volume functions
|
return nil, err
|
||||||
return nil, fmt.Errorf("Invalid disk name (%s): %v", name, err)
|
|
||||||
}
|
}
|
||||||
if url.Scheme != "aws" {
|
|
||||||
return nil, fmt.Errorf("Invalid scheme for AWS volume (%s)", name)
|
|
||||||
}
|
|
||||||
|
|
||||||
awsID := url.Path
|
|
||||||
if len(awsID) > 1 && awsID[0] == '/' {
|
|
||||||
awsID = awsID[1:]
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: Regex match?
|
|
||||||
if strings.Contains(awsID, "/") || !strings.HasPrefix(awsID, "vol-") {
|
|
||||||
return nil, fmt.Errorf("Invalid format for AWS volume (%s)", name)
|
|
||||||
}
|
|
||||||
|
|
||||||
disk := &awsDisk{ec2: aws.ec2, name: name, awsID: awsID}
|
disk := &awsDisk{ec2: aws.ec2, name: name, awsID: awsID}
|
||||||
return disk, nil
|
return disk, nil
|
||||||
}
|
}
|
||||||
@ -1319,7 +1290,7 @@ func (d *awsDisk) describeVolume() (*ec2.Volume, error) {
|
|||||||
volumeID := d.awsID
|
volumeID := d.awsID
|
||||||
|
|
||||||
request := &ec2.DescribeVolumesInput{
|
request := &ec2.DescribeVolumesInput{
|
||||||
VolumeIds: []*string{&volumeID},
|
VolumeIds: []*string{volumeID.awsString()},
|
||||||
}
|
}
|
||||||
|
|
||||||
volumes, err := d.ec2.DescribeVolumes(request)
|
volumes, err := d.ec2.DescribeVolumes(request)
|
||||||
@ -1400,7 +1371,7 @@ func (d *awsDisk) waitForAttachmentStatus(status string) (*ec2.VolumeAttachment,
|
|||||||
|
|
||||||
// Deletes the EBS disk
|
// Deletes the EBS disk
|
||||||
func (d *awsDisk) deleteVolume() (bool, error) {
|
func (d *awsDisk) deleteVolume() (bool, error) {
|
||||||
request := &ec2.DeleteVolumeInput{VolumeId: aws.String(d.awsID)}
|
request := &ec2.DeleteVolumeInput{VolumeId: d.awsID.awsString()}
|
||||||
_, err := d.ec2.DeleteVolume(request)
|
_, err := d.ec2.DeleteVolume(request)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if awsError, ok := err.(awserr.Error); ok {
|
if awsError, ok := err.(awserr.Error); ok {
|
||||||
@ -1460,7 +1431,7 @@ func (c *Cloud) getAwsInstance(nodeName types.NodeName) (*awsInstance, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// AttachDisk implements Volumes.AttachDisk
|
// AttachDisk implements Volumes.AttachDisk
|
||||||
func (c *Cloud) AttachDisk(diskName string, nodeName types.NodeName, readOnly bool) (string, error) {
|
func (c *Cloud) AttachDisk(diskName KubernetesVolumeID, nodeName types.NodeName, readOnly bool) (string, error) {
|
||||||
disk, err := newAWSDisk(c, diskName)
|
disk, err := newAWSDisk(c, diskName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
@ -1508,7 +1479,7 @@ func (c *Cloud) AttachDisk(diskName string, nodeName types.NodeName, readOnly bo
|
|||||||
request := &ec2.AttachVolumeInput{
|
request := &ec2.AttachVolumeInput{
|
||||||
Device: aws.String(ec2Device),
|
Device: aws.String(ec2Device),
|
||||||
InstanceId: aws.String(awsInstance.awsID),
|
InstanceId: aws.String(awsInstance.awsID),
|
||||||
VolumeId: aws.String(disk.awsID),
|
VolumeId: disk.awsID.awsString(),
|
||||||
}
|
}
|
||||||
|
|
||||||
attachResponse, err := c.ec2.AttachVolume(request)
|
attachResponse, err := c.ec2.AttachVolume(request)
|
||||||
@ -1547,7 +1518,7 @@ func (c *Cloud) AttachDisk(diskName string, nodeName types.NodeName, readOnly bo
|
|||||||
}
|
}
|
||||||
|
|
||||||
// DetachDisk implements Volumes.DetachDisk
|
// DetachDisk implements Volumes.DetachDisk
|
||||||
func (c *Cloud) DetachDisk(diskName string, nodeName types.NodeName) (string, error) {
|
func (c *Cloud) DetachDisk(diskName KubernetesVolumeID, nodeName types.NodeName) (string, error) {
|
||||||
disk, err := newAWSDisk(c, diskName)
|
disk, err := newAWSDisk(c, diskName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
@ -1579,7 +1550,7 @@ func (c *Cloud) DetachDisk(diskName string, nodeName types.NodeName) (string, er
|
|||||||
|
|
||||||
request := ec2.DetachVolumeInput{
|
request := ec2.DetachVolumeInput{
|
||||||
InstanceId: &awsInstance.awsID,
|
InstanceId: &awsInstance.awsID,
|
||||||
VolumeId: &disk.awsID,
|
VolumeId: disk.awsID.awsString(),
|
||||||
}
|
}
|
||||||
|
|
||||||
response, err := c.ec2.DetachVolume(&request)
|
response, err := c.ec2.DetachVolume(&request)
|
||||||
@ -1610,7 +1581,7 @@ func (c *Cloud) DetachDisk(diskName string, nodeName types.NodeName) (string, er
|
|||||||
}
|
}
|
||||||
|
|
||||||
// CreateDisk implements Volumes.CreateDisk
|
// CreateDisk implements Volumes.CreateDisk
|
||||||
func (c *Cloud) CreateDisk(volumeOptions *VolumeOptions) (string, error) {
|
func (c *Cloud) CreateDisk(volumeOptions *VolumeOptions) (KubernetesVolumeID, error) {
|
||||||
allZones, err := c.getAllZones()
|
allZones, err := c.getAllZones()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", fmt.Errorf("error querying for all zones: %v", err)
|
return "", fmt.Errorf("error querying for all zones: %v", err)
|
||||||
@ -1668,10 +1639,11 @@ func (c *Cloud) CreateDisk(volumeOptions *VolumeOptions) (string, error) {
|
|||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
az := orEmpty(response.AvailabilityZone)
|
awsID := awsVolumeID(aws.StringValue(response.VolumeId))
|
||||||
awsID := orEmpty(response.VolumeId)
|
if awsID == "" {
|
||||||
|
return "", fmt.Errorf("VolumeID was not returned by CreateVolume")
|
||||||
volumeName := "aws://" + az + "/" + awsID
|
}
|
||||||
|
volumeName := KubernetesVolumeID("aws://" + aws.StringValue(response.AvailabilityZone) + "/" + string(awsID))
|
||||||
|
|
||||||
// apply tags
|
// apply tags
|
||||||
tags := make(map[string]string)
|
tags := make(map[string]string)
|
||||||
@ -1684,7 +1656,7 @@ func (c *Cloud) CreateDisk(volumeOptions *VolumeOptions) (string, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if len(tags) != 0 {
|
if len(tags) != 0 {
|
||||||
if err := c.createTags(awsID, tags); err != nil {
|
if err := c.createTags(string(awsID), tags); err != nil {
|
||||||
// delete the volume and hope it succeeds
|
// delete the volume and hope it succeeds
|
||||||
_, delerr := c.DeleteDisk(volumeName)
|
_, delerr := c.DeleteDisk(volumeName)
|
||||||
if delerr != nil {
|
if delerr != nil {
|
||||||
@ -1698,7 +1670,7 @@ func (c *Cloud) CreateDisk(volumeOptions *VolumeOptions) (string, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// DeleteDisk implements Volumes.DeleteDisk
|
// DeleteDisk implements Volumes.DeleteDisk
|
||||||
func (c *Cloud) DeleteDisk(volumeName string) (bool, error) {
|
func (c *Cloud) DeleteDisk(volumeName KubernetesVolumeID) (bool, error) {
|
||||||
awsDisk, err := newAWSDisk(c, volumeName)
|
awsDisk, err := newAWSDisk(c, volumeName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, err
|
return false, err
|
||||||
@ -1707,7 +1679,7 @@ func (c *Cloud) DeleteDisk(volumeName string) (bool, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// GetVolumeLabels implements Volumes.GetVolumeLabels
|
// GetVolumeLabels implements Volumes.GetVolumeLabels
|
||||||
func (c *Cloud) GetVolumeLabels(volumeName string) (map[string]string, error) {
|
func (c *Cloud) GetVolumeLabels(volumeName KubernetesVolumeID) (map[string]string, error) {
|
||||||
awsDisk, err := newAWSDisk(c, volumeName)
|
awsDisk, err := newAWSDisk(c, volumeName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -1733,7 +1705,7 @@ func (c *Cloud) GetVolumeLabels(volumeName string) (map[string]string, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// GetDiskPath implements Volumes.GetDiskPath
|
// GetDiskPath implements Volumes.GetDiskPath
|
||||||
func (c *Cloud) GetDiskPath(volumeName string) (string, error) {
|
func (c *Cloud) GetDiskPath(volumeName KubernetesVolumeID) (string, error) {
|
||||||
awsDisk, err := newAWSDisk(c, volumeName)
|
awsDisk, err := newAWSDisk(c, volumeName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
@ -1749,7 +1721,7 @@ func (c *Cloud) GetDiskPath(volumeName string) (string, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// DiskIsAttached implements Volumes.DiskIsAttached
|
// DiskIsAttached implements Volumes.DiskIsAttached
|
||||||
func (c *Cloud) DiskIsAttached(diskName string, nodeName types.NodeName) (bool, error) {
|
func (c *Cloud) DiskIsAttached(diskName KubernetesVolumeID, nodeName types.NodeName) (bool, error) {
|
||||||
awsInstance, err := c.getAwsInstance(nodeName)
|
awsInstance, err := c.getAwsInstance(nodeName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if err == cloudprovider.InstanceNotFound {
|
if err == cloudprovider.InstanceNotFound {
|
||||||
@ -1764,22 +1736,33 @@ func (c *Cloud) DiskIsAttached(diskName string, nodeName types.NodeName) (bool,
|
|||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
diskID, err := diskName.mapToAWSVolumeID()
|
||||||
|
if err != nil {
|
||||||
|
return false, fmt.Errorf("error mapping volume spec %q to aws id: %v", diskName, err)
|
||||||
|
}
|
||||||
|
|
||||||
info, err := awsInstance.describeInstance()
|
info, err := awsInstance.describeInstance()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
for _, blockDevice := range info.BlockDeviceMappings {
|
for _, blockDevice := range info.BlockDeviceMappings {
|
||||||
name := aws.StringValue(blockDevice.Ebs.VolumeId)
|
id := awsVolumeID(aws.StringValue(blockDevice.Ebs.VolumeId))
|
||||||
if name == diskName {
|
if id == diskID {
|
||||||
return true, nil
|
return true, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return false, nil
|
return false, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Cloud) DisksAreAttached(diskNames []string, nodeName types.NodeName) (map[string]bool, error) {
|
func (c *Cloud) DisksAreAttached(diskNames []KubernetesVolumeID, nodeName types.NodeName) (map[KubernetesVolumeID]bool, error) {
|
||||||
attached := make(map[string]bool)
|
idToDiskName := make(map[awsVolumeID]KubernetesVolumeID)
|
||||||
|
attached := make(map[KubernetesVolumeID]bool)
|
||||||
for _, diskName := range diskNames {
|
for _, diskName := range diskNames {
|
||||||
|
volumeID, err := diskName.mapToAWSVolumeID()
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("error mapping volume spec %q to aws id: %v", diskName, err)
|
||||||
|
}
|
||||||
|
idToDiskName[volumeID] = diskName
|
||||||
attached[diskName] = false
|
attached[diskName] = false
|
||||||
}
|
}
|
||||||
awsInstance, err := c.getAwsInstance(nodeName)
|
awsInstance, err := c.getAwsInstance(nodeName)
|
||||||
@ -1800,12 +1783,11 @@ func (c *Cloud) DisksAreAttached(diskNames []string, nodeName types.NodeName) (m
|
|||||||
return attached, err
|
return attached, err
|
||||||
}
|
}
|
||||||
for _, blockDevice := range info.BlockDeviceMappings {
|
for _, blockDevice := range info.BlockDeviceMappings {
|
||||||
volumeId := aws.StringValue(blockDevice.Ebs.VolumeId)
|
volumeID := awsVolumeID(aws.StringValue(blockDevice.Ebs.VolumeId))
|
||||||
for _, diskName := range diskNames {
|
diskName, found := idToDiskName[volumeID]
|
||||||
if volumeId == diskName {
|
if found {
|
||||||
// Disk is still attached to node
|
// Disk is still attached to node
|
||||||
attached[diskName] = true
|
attached[diskName] = true
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1163,16 +1163,16 @@ func TestGetVolumeLabels(t *testing.T) {
|
|||||||
awsServices := NewFakeAWSServices()
|
awsServices := NewFakeAWSServices()
|
||||||
c, err := newAWSCloud(strings.NewReader("[global]"), awsServices)
|
c, err := newAWSCloud(strings.NewReader("[global]"), awsServices)
|
||||||
assert.Nil(t, err, "Error building aws cloud: %v", err)
|
assert.Nil(t, err, "Error building aws cloud: %v", err)
|
||||||
volumeId := aws.String("vol-VolumeId")
|
volumeId := awsVolumeID("vol-VolumeId")
|
||||||
expectedVolumeRequest := &ec2.DescribeVolumesInput{VolumeIds: []*string{volumeId}}
|
expectedVolumeRequest := &ec2.DescribeVolumesInput{VolumeIds: []*string{volumeId.awsString()}}
|
||||||
awsServices.ec2.On("DescribeVolumes", expectedVolumeRequest).Return([]*ec2.Volume{
|
awsServices.ec2.On("DescribeVolumes", expectedVolumeRequest).Return([]*ec2.Volume{
|
||||||
{
|
{
|
||||||
VolumeId: volumeId,
|
VolumeId: volumeId.awsString(),
|
||||||
AvailabilityZone: aws.String("us-east-1a"),
|
AvailabilityZone: aws.String("us-east-1a"),
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
labels, err := c.GetVolumeLabels(*volumeId)
|
labels, err := c.GetVolumeLabels(KubernetesVolumeID("aws:///" + string(volumeId)))
|
||||||
|
|
||||||
assert.Nil(t, err, "Error creating Volume %v", err)
|
assert.Nil(t, err, "Error creating Volume %v", err)
|
||||||
assert.Equal(t, map[string]string{
|
assert.Equal(t, map[string]string{
|
||||||
|
83
pkg/cloudprovider/providers/aws/volumes.go
Normal file
83
pkg/cloudprovider/providers/aws/volumes.go
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
/*
|
||||||
|
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 aws
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/aws/aws-sdk-go/aws"
|
||||||
|
"net/url"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// awsVolumeID represents the ID of the volume in the AWS API, e.g. vol-12345678a
|
||||||
|
// The "traditional" format is "vol-12345678"
|
||||||
|
// A new longer format is also being introduced: "vol-12345678abcdef01"
|
||||||
|
// We should not assume anything about the length or format, though it seems
|
||||||
|
// reasonable to assume that volumes will continue to start with "vol-".
|
||||||
|
type awsVolumeID string
|
||||||
|
|
||||||
|
func (i awsVolumeID) awsString() *string {
|
||||||
|
return aws.String(string(i))
|
||||||
|
}
|
||||||
|
|
||||||
|
// KubernetesVolumeID represents the id for a volume in the kubernetes API;
|
||||||
|
// a few forms are recognized:
|
||||||
|
// * aws://<zone>/<awsVolumeId>
|
||||||
|
// * aws:///<awsVolumeId>
|
||||||
|
// * <awsVolumeId>
|
||||||
|
type KubernetesVolumeID string
|
||||||
|
|
||||||
|
// mapToAWSVolumeID extracts the awsVolumeID from the KubernetesVolumeID
|
||||||
|
func (name KubernetesVolumeID) mapToAWSVolumeID() (awsVolumeID, error) {
|
||||||
|
// name looks like aws://availability-zone/awsVolumeId
|
||||||
|
|
||||||
|
// The original idea of the URL-style name was to put the AZ into the
|
||||||
|
// host, so we could find the AZ immediately from the name without
|
||||||
|
// querying the API. But it turns out we don't actually need it for
|
||||||
|
// multi-AZ clusters, as we put the AZ into the labels on the PV instead.
|
||||||
|
// However, if in future we want to support multi-AZ cluster
|
||||||
|
// volume-awareness without using PersistentVolumes, we likely will
|
||||||
|
// want the AZ in the host.
|
||||||
|
|
||||||
|
s := string(name)
|
||||||
|
|
||||||
|
if !strings.HasPrefix(s, "aws://") {
|
||||||
|
// Assume a bare aws volume id (vol-1234...)
|
||||||
|
// Build a URL with an empty host (AZ)
|
||||||
|
s = "aws://" + "" + "/" + s
|
||||||
|
}
|
||||||
|
url, err := url.Parse(s)
|
||||||
|
if err != nil {
|
||||||
|
// TODO: Maybe we should pass a URL into the Volume functions
|
||||||
|
return "", fmt.Errorf("Invalid disk name (%s): %v", name, err)
|
||||||
|
}
|
||||||
|
if url.Scheme != "aws" {
|
||||||
|
return "", fmt.Errorf("Invalid scheme for AWS volume (%s)", name)
|
||||||
|
}
|
||||||
|
|
||||||
|
awsID := url.Path
|
||||||
|
awsID = strings.Trim(awsID, "/")
|
||||||
|
|
||||||
|
// We sanity check the resulting volume; the two known formats are
|
||||||
|
// vol-12345678 and vol-12345678abcdef01
|
||||||
|
// TODO: Regex match?
|
||||||
|
if strings.Contains(awsID, "/") || !strings.HasPrefix(awsID, "vol-") {
|
||||||
|
return "", fmt.Errorf("Invalid format for AWS volume (%s)", name)
|
||||||
|
}
|
||||||
|
|
||||||
|
return awsVolumeID(awsID), nil
|
||||||
|
}
|
@ -64,7 +64,7 @@ func (attacher *awsElasticBlockStoreAttacher) Attach(spec *volume.Spec, nodeName
|
|||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
volumeID := volumeSource.VolumeID
|
volumeID := aws.KubernetesVolumeID(volumeSource.VolumeID)
|
||||||
|
|
||||||
// awsCloud.AttachDisk checks if disk is already attached to node and
|
// awsCloud.AttachDisk checks if disk is already attached to node and
|
||||||
// succeeds in that case, so no need to do that separately.
|
// succeeds in that case, so no need to do that separately.
|
||||||
@ -79,8 +79,8 @@ func (attacher *awsElasticBlockStoreAttacher) Attach(spec *volume.Spec, nodeName
|
|||||||
|
|
||||||
func (attacher *awsElasticBlockStoreAttacher) VolumesAreAttached(specs []*volume.Spec, nodeName types.NodeName) (map[*volume.Spec]bool, error) {
|
func (attacher *awsElasticBlockStoreAttacher) VolumesAreAttached(specs []*volume.Spec, nodeName types.NodeName) (map[*volume.Spec]bool, error) {
|
||||||
volumesAttachedCheck := make(map[*volume.Spec]bool)
|
volumesAttachedCheck := make(map[*volume.Spec]bool)
|
||||||
volumeSpecMap := make(map[string]*volume.Spec)
|
volumeSpecMap := make(map[aws.KubernetesVolumeID]*volume.Spec)
|
||||||
volumeIDList := []string{}
|
volumeIDList := []aws.KubernetesVolumeID{}
|
||||||
for _, spec := range specs {
|
for _, spec := range specs {
|
||||||
volumeSource, _, err := getVolumeSource(spec)
|
volumeSource, _, err := getVolumeSource(spec)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -88,9 +88,10 @@ func (attacher *awsElasticBlockStoreAttacher) VolumesAreAttached(specs []*volume
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
volumeIDList = append(volumeIDList, volumeSource.VolumeID)
|
name := aws.KubernetesVolumeID(volumeSource.VolumeID)
|
||||||
|
volumeIDList = append(volumeIDList, name)
|
||||||
volumesAttachedCheck[spec] = true
|
volumesAttachedCheck[spec] = true
|
||||||
volumeSpecMap[volumeSource.VolumeID] = spec
|
volumeSpecMap[name] = spec
|
||||||
}
|
}
|
||||||
attachedResult, err := attacher.awsVolumes.DisksAreAttached(volumeIDList, nodeName)
|
attachedResult, err := attacher.awsVolumes.DisksAreAttached(volumeIDList, nodeName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -163,7 +164,7 @@ func (attacher *awsElasticBlockStoreAttacher) GetDeviceMountPath(
|
|||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
return makeGlobalPDPath(attacher.host, volumeSource.VolumeID), nil
|
return makeGlobalPDPath(attacher.host, aws.KubernetesVolumeID(volumeSource.VolumeID)), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// FIXME: this method can be further pruned.
|
// FIXME: this method can be further pruned.
|
||||||
@ -221,7 +222,7 @@ func (plugin *awsElasticBlockStorePlugin) NewDetacher() (volume.Detacher, error)
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (detacher *awsElasticBlockStoreDetacher) Detach(deviceMountPath string, nodeName types.NodeName) error {
|
func (detacher *awsElasticBlockStoreDetacher) Detach(deviceMountPath string, nodeName types.NodeName) error {
|
||||||
volumeID := path.Base(deviceMountPath)
|
volumeID := aws.KubernetesVolumeID(path.Base(deviceMountPath))
|
||||||
|
|
||||||
attached, err := detacher.awsVolumes.DiskIsAttached(volumeID, nodeName)
|
attached, err := detacher.awsVolumes.DiskIsAttached(volumeID, nodeName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -29,37 +29,37 @@ import (
|
|||||||
"k8s.io/kubernetes/pkg/types"
|
"k8s.io/kubernetes/pkg/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestGetDeviceName_Volume(t *testing.T) {
|
func TestGetVolumeName_Volume(t *testing.T) {
|
||||||
plugin := newPlugin()
|
plugin := newPlugin()
|
||||||
name := "my-aws-volume"
|
name := aws.KubernetesVolumeID("my-aws-volume")
|
||||||
spec := createVolSpec(name, false)
|
spec := createVolSpec(name, false)
|
||||||
|
|
||||||
deviceName, err := plugin.GetVolumeName(spec)
|
volumeName, err := plugin.GetVolumeName(spec)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("GetDeviceName error: %v", err)
|
t.Errorf("GetVolumeName error: %v", err)
|
||||||
}
|
}
|
||||||
if deviceName != name {
|
if volumeName != string(name) {
|
||||||
t.Errorf("GetDeviceName error: expected %s, got %s", name, deviceName)
|
t.Errorf("GetVolumeName error: expected %s, got %s", name, volumeName)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestGetDeviceName_PersistentVolume(t *testing.T) {
|
func TestGetVolumeName_PersistentVolume(t *testing.T) {
|
||||||
plugin := newPlugin()
|
plugin := newPlugin()
|
||||||
name := "my-aws-pv"
|
name := aws.KubernetesVolumeID("my-aws-pv")
|
||||||
spec := createPVSpec(name, true)
|
spec := createPVSpec(name, true)
|
||||||
|
|
||||||
deviceName, err := plugin.GetVolumeName(spec)
|
volumeName, err := plugin.GetVolumeName(spec)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("GetDeviceName error: %v", err)
|
t.Errorf("GetVolumeName error: %v", err)
|
||||||
}
|
}
|
||||||
if deviceName != name {
|
if volumeName != string(name) {
|
||||||
t.Errorf("GetDeviceName error: expected %s, got %s", name, deviceName)
|
t.Errorf("GetVolumeName error: expected %s, got %s", name, volumeName)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// One testcase for TestAttachDetach table test below
|
// One testcase for TestAttachDetach table test below
|
||||||
type testcase struct {
|
type testcase struct {
|
||||||
name string
|
name aws.KubernetesVolumeID
|
||||||
// For fake AWS:
|
// For fake AWS:
|
||||||
attach attachCall
|
attach attachCall
|
||||||
detach detachCall
|
detach detachCall
|
||||||
@ -74,7 +74,7 @@ type testcase struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestAttachDetach(t *testing.T) {
|
func TestAttachDetach(t *testing.T) {
|
||||||
diskName := "disk"
|
diskName := aws.KubernetesVolumeID("disk")
|
||||||
nodeName := types.NodeName("instance")
|
nodeName := types.NodeName("instance")
|
||||||
readOnly := false
|
readOnly := false
|
||||||
spec := createVolSpec(diskName, readOnly)
|
spec := createVolSpec(diskName, readOnly)
|
||||||
@ -111,7 +111,8 @@ func TestAttachDetach(t *testing.T) {
|
|||||||
detach: detachCall{diskName, nodeName, "/dev/sda", nil},
|
detach: detachCall{diskName, nodeName, "/dev/sda", nil},
|
||||||
test: func(testcase *testcase) (string, error) {
|
test: func(testcase *testcase) (string, error) {
|
||||||
detacher := newDetacher(testcase)
|
detacher := newDetacher(testcase)
|
||||||
return "", detacher.Detach(diskName, nodeName)
|
mountPath := "/mnt/" + string(diskName)
|
||||||
|
return "", detacher.Detach(mountPath, nodeName)
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -121,7 +122,8 @@ func TestAttachDetach(t *testing.T) {
|
|||||||
diskIsAttached: diskIsAttachedCall{diskName, nodeName, false, nil},
|
diskIsAttached: diskIsAttachedCall{diskName, nodeName, false, nil},
|
||||||
test: func(testcase *testcase) (string, error) {
|
test: func(testcase *testcase) (string, error) {
|
||||||
detacher := newDetacher(testcase)
|
detacher := newDetacher(testcase)
|
||||||
return "", detacher.Detach(diskName, nodeName)
|
mountPath := "/mnt/" + string(diskName)
|
||||||
|
return "", detacher.Detach(mountPath, nodeName)
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -132,7 +134,8 @@ func TestAttachDetach(t *testing.T) {
|
|||||||
detach: detachCall{diskName, nodeName, "/dev/sda", nil},
|
detach: detachCall{diskName, nodeName, "/dev/sda", nil},
|
||||||
test: func(testcase *testcase) (string, error) {
|
test: func(testcase *testcase) (string, error) {
|
||||||
detacher := newDetacher(testcase)
|
detacher := newDetacher(testcase)
|
||||||
return "", detacher.Detach(diskName, nodeName)
|
mountPath := "/mnt/" + string(diskName)
|
||||||
|
return "", detacher.Detach(mountPath, nodeName)
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -143,7 +146,8 @@ func TestAttachDetach(t *testing.T) {
|
|||||||
detach: detachCall{diskName, nodeName, "", detachError},
|
detach: detachCall{diskName, nodeName, "", detachError},
|
||||||
test: func(testcase *testcase) (string, error) {
|
test: func(testcase *testcase) (string, error) {
|
||||||
detacher := newDetacher(testcase)
|
detacher := newDetacher(testcase)
|
||||||
return "", detacher.Detach(diskName, nodeName)
|
mountPath := "/mnt/" + string(diskName)
|
||||||
|
return "", detacher.Detach(mountPath, nodeName)
|
||||||
},
|
},
|
||||||
expectedError: detachError,
|
expectedError: detachError,
|
||||||
},
|
},
|
||||||
@ -185,12 +189,12 @@ func newDetacher(testcase *testcase) *awsElasticBlockStoreDetacher {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func createVolSpec(name string, readOnly bool) *volume.Spec {
|
func createVolSpec(name aws.KubernetesVolumeID, readOnly bool) *volume.Spec {
|
||||||
return &volume.Spec{
|
return &volume.Spec{
|
||||||
Volume: &api.Volume{
|
Volume: &api.Volume{
|
||||||
VolumeSource: api.VolumeSource{
|
VolumeSource: api.VolumeSource{
|
||||||
AWSElasticBlockStore: &api.AWSElasticBlockStoreVolumeSource{
|
AWSElasticBlockStore: &api.AWSElasticBlockStoreVolumeSource{
|
||||||
VolumeID: name,
|
VolumeID: string(name),
|
||||||
ReadOnly: readOnly,
|
ReadOnly: readOnly,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -198,13 +202,13 @@ func createVolSpec(name string, readOnly bool) *volume.Spec {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func createPVSpec(name string, readOnly bool) *volume.Spec {
|
func createPVSpec(name aws.KubernetesVolumeID, readOnly bool) *volume.Spec {
|
||||||
return &volume.Spec{
|
return &volume.Spec{
|
||||||
PersistentVolume: &api.PersistentVolume{
|
PersistentVolume: &api.PersistentVolume{
|
||||||
Spec: api.PersistentVolumeSpec{
|
Spec: api.PersistentVolumeSpec{
|
||||||
PersistentVolumeSource: api.PersistentVolumeSource{
|
PersistentVolumeSource: api.PersistentVolumeSource{
|
||||||
AWSElasticBlockStore: &api.AWSElasticBlockStoreVolumeSource{
|
AWSElasticBlockStore: &api.AWSElasticBlockStoreVolumeSource{
|
||||||
VolumeID: name,
|
VolumeID: string(name),
|
||||||
ReadOnly: readOnly,
|
ReadOnly: readOnly,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -216,7 +220,7 @@ func createPVSpec(name string, readOnly bool) *volume.Spec {
|
|||||||
// Fake AWS implementation
|
// Fake AWS implementation
|
||||||
|
|
||||||
type attachCall struct {
|
type attachCall struct {
|
||||||
diskName string
|
diskName aws.KubernetesVolumeID
|
||||||
nodeName types.NodeName
|
nodeName types.NodeName
|
||||||
readOnly bool
|
readOnly bool
|
||||||
retDeviceName string
|
retDeviceName string
|
||||||
@ -224,20 +228,20 @@ type attachCall struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type detachCall struct {
|
type detachCall struct {
|
||||||
diskName string
|
diskName aws.KubernetesVolumeID
|
||||||
nodeName types.NodeName
|
nodeName types.NodeName
|
||||||
retDeviceName string
|
retDeviceName string
|
||||||
ret error
|
ret error
|
||||||
}
|
}
|
||||||
|
|
||||||
type diskIsAttachedCall struct {
|
type diskIsAttachedCall struct {
|
||||||
diskName string
|
diskName aws.KubernetesVolumeID
|
||||||
nodeName types.NodeName
|
nodeName types.NodeName
|
||||||
isAttached bool
|
isAttached bool
|
||||||
ret error
|
ret error
|
||||||
}
|
}
|
||||||
|
|
||||||
func (testcase *testcase) AttachDisk(diskName string, nodeName types.NodeName, readOnly bool) (string, error) {
|
func (testcase *testcase) AttachDisk(diskName aws.KubernetesVolumeID, nodeName types.NodeName, readOnly bool) (string, error) {
|
||||||
expected := &testcase.attach
|
expected := &testcase.attach
|
||||||
|
|
||||||
if expected.diskName == "" && expected.nodeName == "" {
|
if expected.diskName == "" && expected.nodeName == "" {
|
||||||
@ -267,7 +271,7 @@ func (testcase *testcase) AttachDisk(diskName string, nodeName types.NodeName, r
|
|||||||
return expected.retDeviceName, expected.ret
|
return expected.retDeviceName, expected.ret
|
||||||
}
|
}
|
||||||
|
|
||||||
func (testcase *testcase) DetachDisk(diskName string, nodeName types.NodeName) (string, error) {
|
func (testcase *testcase) DetachDisk(diskName aws.KubernetesVolumeID, nodeName types.NodeName) (string, error) {
|
||||||
expected := &testcase.detach
|
expected := &testcase.detach
|
||||||
|
|
||||||
if expected.diskName == "" && expected.nodeName == "" {
|
if expected.diskName == "" && expected.nodeName == "" {
|
||||||
@ -292,7 +296,7 @@ func (testcase *testcase) DetachDisk(diskName string, nodeName types.NodeName) (
|
|||||||
return expected.retDeviceName, expected.ret
|
return expected.retDeviceName, expected.ret
|
||||||
}
|
}
|
||||||
|
|
||||||
func (testcase *testcase) DiskIsAttached(diskName string, nodeName types.NodeName) (bool, error) {
|
func (testcase *testcase) DiskIsAttached(diskName aws.KubernetesVolumeID, nodeName types.NodeName) (bool, error) {
|
||||||
expected := &testcase.diskIsAttached
|
expected := &testcase.diskIsAttached
|
||||||
|
|
||||||
if expected.diskName == "" && expected.nodeName == "" {
|
if expected.diskName == "" && expected.nodeName == "" {
|
||||||
@ -317,22 +321,22 @@ func (testcase *testcase) DiskIsAttached(diskName string, nodeName types.NodeNam
|
|||||||
return expected.isAttached, expected.ret
|
return expected.isAttached, expected.ret
|
||||||
}
|
}
|
||||||
|
|
||||||
func (testcase *testcase) DisksAreAttached(diskNames []string, nodeName types.NodeName) (map[string]bool, error) {
|
func (testcase *testcase) DisksAreAttached(diskNames []aws.KubernetesVolumeID, nodeName types.NodeName) (map[aws.KubernetesVolumeID]bool, error) {
|
||||||
return nil, errors.New("Not implemented")
|
return nil, errors.New("Not implemented")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (testcase *testcase) CreateDisk(volumeOptions *aws.VolumeOptions) (volumeName string, err error) {
|
func (testcase *testcase) CreateDisk(volumeOptions *aws.VolumeOptions) (volumeName aws.KubernetesVolumeID, err error) {
|
||||||
return "", errors.New("Not implemented")
|
return "", errors.New("Not implemented")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (testcase *testcase) DeleteDisk(volumeName string) (bool, error) {
|
func (testcase *testcase) DeleteDisk(volumeName aws.KubernetesVolumeID) (bool, error) {
|
||||||
return false, errors.New("Not implemented")
|
return false, errors.New("Not implemented")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (testcase *testcase) GetVolumeLabels(volumeName string) (map[string]string, error) {
|
func (testcase *testcase) GetVolumeLabels(volumeName aws.KubernetesVolumeID) (map[string]string, error) {
|
||||||
return map[string]string{}, errors.New("Not implemented")
|
return map[string]string{}, errors.New("Not implemented")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (testcase *testcase) GetDiskPath(volumeName string) (string, error) {
|
func (testcase *testcase) GetDiskPath(volumeName aws.KubernetesVolumeID) (string, error) {
|
||||||
return "", errors.New("Not implemented")
|
return "", errors.New("Not implemented")
|
||||||
}
|
}
|
||||||
|
@ -27,6 +27,7 @@ import (
|
|||||||
"github.com/golang/glog"
|
"github.com/golang/glog"
|
||||||
"k8s.io/kubernetes/pkg/api"
|
"k8s.io/kubernetes/pkg/api"
|
||||||
"k8s.io/kubernetes/pkg/api/resource"
|
"k8s.io/kubernetes/pkg/api/resource"
|
||||||
|
"k8s.io/kubernetes/pkg/cloudprovider/providers/aws"
|
||||||
"k8s.io/kubernetes/pkg/types"
|
"k8s.io/kubernetes/pkg/types"
|
||||||
"k8s.io/kubernetes/pkg/util/exec"
|
"k8s.io/kubernetes/pkg/util/exec"
|
||||||
"k8s.io/kubernetes/pkg/util/mount"
|
"k8s.io/kubernetes/pkg/util/mount"
|
||||||
@ -102,7 +103,7 @@ func (plugin *awsElasticBlockStorePlugin) newMounterInternal(spec *volume.Spec,
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
volumeID := ebs.VolumeID
|
volumeID := aws.KubernetesVolumeID(ebs.VolumeID)
|
||||||
fsType := ebs.FSType
|
fsType := ebs.FSType
|
||||||
partition := ""
|
partition := ""
|
||||||
if ebs.Partition != 0 {
|
if ebs.Partition != 0 {
|
||||||
@ -153,7 +154,7 @@ func (plugin *awsElasticBlockStorePlugin) newDeleterInternal(spec *volume.Spec,
|
|||||||
return &awsElasticBlockStoreDeleter{
|
return &awsElasticBlockStoreDeleter{
|
||||||
awsElasticBlockStore: &awsElasticBlockStore{
|
awsElasticBlockStore: &awsElasticBlockStore{
|
||||||
volName: spec.Name(),
|
volName: spec.Name(),
|
||||||
volumeID: spec.PersistentVolume.Spec.AWSElasticBlockStore.VolumeID,
|
volumeID: aws.KubernetesVolumeID(spec.PersistentVolume.Spec.AWSElasticBlockStore.VolumeID),
|
||||||
manager: manager,
|
manager: manager,
|
||||||
plugin: plugin,
|
plugin: plugin,
|
||||||
}}, nil
|
}}, nil
|
||||||
@ -205,7 +206,7 @@ func (plugin *awsElasticBlockStorePlugin) ConstructVolumeSpec(volName, mountPath
|
|||||||
|
|
||||||
// Abstract interface to PD operations.
|
// Abstract interface to PD operations.
|
||||||
type ebsManager interface {
|
type ebsManager interface {
|
||||||
CreateVolume(provisioner *awsElasticBlockStoreProvisioner) (volumeID string, volumeSizeGB int, labels map[string]string, err error)
|
CreateVolume(provisioner *awsElasticBlockStoreProvisioner) (volumeID aws.KubernetesVolumeID, volumeSizeGB int, labels map[string]string, err error)
|
||||||
// Deletes a volume
|
// Deletes a volume
|
||||||
DeleteVolume(deleter *awsElasticBlockStoreDeleter) error
|
DeleteVolume(deleter *awsElasticBlockStoreDeleter) error
|
||||||
}
|
}
|
||||||
@ -216,7 +217,7 @@ type awsElasticBlockStore struct {
|
|||||||
volName string
|
volName string
|
||||||
podUID types.UID
|
podUID types.UID
|
||||||
// Unique id of the PD, used to find the disk resource in the provider.
|
// Unique id of the PD, used to find the disk resource in the provider.
|
||||||
volumeID string
|
volumeID aws.KubernetesVolumeID
|
||||||
// Specifies the partition to mount
|
// Specifies the partition to mount
|
||||||
partition string
|
partition string
|
||||||
// Utility interface that provides API calls to the provider to attach/detach disks.
|
// Utility interface that provides API calls to the provider to attach/detach disks.
|
||||||
@ -312,9 +313,9 @@ func (b *awsElasticBlockStoreMounter) SetUpAt(dir string, fsGroup *int64) error
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func makeGlobalPDPath(host volume.VolumeHost, volumeID string) string {
|
func makeGlobalPDPath(host volume.VolumeHost, volumeID aws.KubernetesVolumeID) string {
|
||||||
// Clean up the URI to be more fs-friendly
|
// Clean up the URI to be more fs-friendly
|
||||||
name := volumeID
|
name := string(volumeID)
|
||||||
name = strings.Replace(name, "://", "/", -1)
|
name = strings.Replace(name, "://", "/", -1)
|
||||||
return path.Join(host.GetPluginDir(awsElasticBlockStorePluginName), "mounts", name)
|
return path.Join(host.GetPluginDir(awsElasticBlockStorePluginName), "mounts", name)
|
||||||
}
|
}
|
||||||
@ -432,7 +433,7 @@ func (c *awsElasticBlockStoreProvisioner) Provision() (*api.PersistentVolume, er
|
|||||||
},
|
},
|
||||||
PersistentVolumeSource: api.PersistentVolumeSource{
|
PersistentVolumeSource: api.PersistentVolumeSource{
|
||||||
AWSElasticBlockStore: &api.AWSElasticBlockStoreVolumeSource{
|
AWSElasticBlockStore: &api.AWSElasticBlockStoreVolumeSource{
|
||||||
VolumeID: volumeID,
|
VolumeID: string(volumeID),
|
||||||
FSType: "ext4",
|
FSType: "ext4",
|
||||||
Partition: 0,
|
Partition: 0,
|
||||||
ReadOnly: false,
|
ReadOnly: false,
|
||||||
|
@ -24,6 +24,7 @@ import (
|
|||||||
|
|
||||||
"k8s.io/kubernetes/pkg/api"
|
"k8s.io/kubernetes/pkg/api"
|
||||||
"k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/fake"
|
"k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/fake"
|
||||||
|
"k8s.io/kubernetes/pkg/cloudprovider/providers/aws"
|
||||||
"k8s.io/kubernetes/pkg/types"
|
"k8s.io/kubernetes/pkg/types"
|
||||||
"k8s.io/kubernetes/pkg/util/mount"
|
"k8s.io/kubernetes/pkg/util/mount"
|
||||||
utiltesting "k8s.io/kubernetes/pkg/util/testing"
|
utiltesting "k8s.io/kubernetes/pkg/util/testing"
|
||||||
@ -91,7 +92,7 @@ type fakePDManager struct {
|
|||||||
|
|
||||||
// TODO(jonesdl) To fully test this, we could create a loopback device
|
// TODO(jonesdl) To fully test this, we could create a loopback device
|
||||||
// and mount that instead.
|
// and mount that instead.
|
||||||
func (fake *fakePDManager) CreateVolume(c *awsElasticBlockStoreProvisioner) (volumeID string, volumeSizeGB int, labels map[string]string, err error) {
|
func (fake *fakePDManager) CreateVolume(c *awsElasticBlockStoreProvisioner) (volumeID aws.KubernetesVolumeID, volumeSizeGB int, labels map[string]string, err error) {
|
||||||
labels = make(map[string]string)
|
labels = make(map[string]string)
|
||||||
labels["fakepdmanager"] = "yes"
|
labels["fakepdmanager"] = "yes"
|
||||||
return "test-aws-volume-name", 100, labels, nil
|
return "test-aws-volume-name", 100, labels, nil
|
||||||
|
@ -65,7 +65,7 @@ func (util *AWSDiskUtil) DeleteVolume(d *awsElasticBlockStoreDeleter) error {
|
|||||||
|
|
||||||
// CreateVolume creates an AWS EBS volume.
|
// CreateVolume creates an AWS EBS volume.
|
||||||
// Returns: volumeID, volumeSizeGB, labels, error
|
// Returns: volumeID, volumeSizeGB, labels, error
|
||||||
func (util *AWSDiskUtil) CreateVolume(c *awsElasticBlockStoreProvisioner) (string, int, map[string]string, error) {
|
func (util *AWSDiskUtil) CreateVolume(c *awsElasticBlockStoreProvisioner) (aws.KubernetesVolumeID, int, map[string]string, error) {
|
||||||
cloud, err := getCloudProvider(c.awsElasticBlockStore.plugin.host.GetCloudProvider())
|
cloud, err := getCloudProvider(c.awsElasticBlockStore.plugin.host.GetCloudProvider())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", 0, nil, err
|
return "", 0, nil, err
|
||||||
|
@ -242,7 +242,7 @@ func (plugin *gcePersistentDiskPlugin) NewDetacher() (volume.Detacher, error) {
|
|||||||
// attached to the specified node. If the volume is not attached, it succeeds
|
// attached to the specified node. If the volume is not attached, it succeeds
|
||||||
// (returns nil). If it is attached, Detach issues a call to the GCE cloud
|
// (returns nil). If it is attached, Detach issues a call to the GCE cloud
|
||||||
// provider to attach it.
|
// provider to attach it.
|
||||||
// Callers are responsible for retryinging on failure.
|
// Callers are responsible for retrying on failure.
|
||||||
// Callers are responsible for thread safety between concurrent attach and detach
|
// Callers are responsible for thread safety between concurrent attach and detach
|
||||||
// operations.
|
// operations.
|
||||||
func (detacher *gcePersistentDiskDetacher) Detach(deviceMountPath string, nodeName types.NodeName) error {
|
func (detacher *gcePersistentDiskDetacher) Detach(deviceMountPath string, nodeName types.NodeName) error {
|
||||||
|
@ -118,7 +118,8 @@ func (l *persistentVolumeLabel) findAWSEBSLabels(volume *api.PersistentVolume) (
|
|||||||
|
|
||||||
// TODO: GetVolumeLabels is actually a method on the Volumes interface
|
// TODO: GetVolumeLabels is actually a method on the Volumes interface
|
||||||
// If that gets standardized we can refactor to reduce code duplication
|
// If that gets standardized we can refactor to reduce code duplication
|
||||||
labels, err := ebsVolumes.GetVolumeLabels(volume.Spec.AWSElasticBlockStore.VolumeID)
|
spec := aws.KubernetesVolumeID(volume.Spec.AWSElasticBlockStore.VolumeID)
|
||||||
|
labels, err := ebsVolumes.GetVolumeLabels(spec)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -34,35 +34,35 @@ type mockVolumes struct {
|
|||||||
|
|
||||||
var _ aws.Volumes = &mockVolumes{}
|
var _ aws.Volumes = &mockVolumes{}
|
||||||
|
|
||||||
func (v *mockVolumes) AttachDisk(diskName string, nodeName types.NodeName, readOnly bool) (string, error) {
|
func (v *mockVolumes) AttachDisk(diskName aws.KubernetesVolumeID, nodeName types.NodeName, readOnly bool) (string, error) {
|
||||||
return "", fmt.Errorf("not implemented")
|
return "", fmt.Errorf("not implemented")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *mockVolumes) DetachDisk(diskName string, nodeName types.NodeName) (string, error) {
|
func (v *mockVolumes) DetachDisk(diskName aws.KubernetesVolumeID, nodeName types.NodeName) (string, error) {
|
||||||
return "", fmt.Errorf("not implemented")
|
return "", fmt.Errorf("not implemented")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *mockVolumes) CreateDisk(volumeOptions *aws.VolumeOptions) (volumeName string, err error) {
|
func (v *mockVolumes) CreateDisk(volumeOptions *aws.VolumeOptions) (volumeName aws.KubernetesVolumeID, err error) {
|
||||||
return "", fmt.Errorf("not implemented")
|
return "", fmt.Errorf("not implemented")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *mockVolumes) DeleteDisk(volumeName string) (bool, error) {
|
func (v *mockVolumes) DeleteDisk(volumeName aws.KubernetesVolumeID) (bool, error) {
|
||||||
return false, fmt.Errorf("not implemented")
|
return false, fmt.Errorf("not implemented")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *mockVolumes) GetVolumeLabels(volumeName string) (map[string]string, error) {
|
func (v *mockVolumes) GetVolumeLabels(volumeName aws.KubernetesVolumeID) (map[string]string, error) {
|
||||||
return v.volumeLabels, v.volumeLabelsError
|
return v.volumeLabels, v.volumeLabelsError
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *mockVolumes) GetDiskPath(volumeName string) (string, error) {
|
func (c *mockVolumes) GetDiskPath(volumeName aws.KubernetesVolumeID) (string, error) {
|
||||||
return "", fmt.Errorf("not implemented")
|
return "", fmt.Errorf("not implemented")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *mockVolumes) DiskIsAttached(volumeName string, nodeName types.NodeName) (bool, error) {
|
func (c *mockVolumes) DiskIsAttached(volumeName aws.KubernetesVolumeID, nodeName types.NodeName) (bool, error) {
|
||||||
return false, fmt.Errorf("not implemented")
|
return false, fmt.Errorf("not implemented")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *mockVolumes) DisksAreAttached(diskNames []string, nodeName types.NodeName) (map[string]bool, error) {
|
func (c *mockVolumes) DisksAreAttached(diskNames []aws.KubernetesVolumeID, nodeName types.NodeName) (map[aws.KubernetesVolumeID]bool, error) {
|
||||||
return nil, fmt.Errorf("not implemented")
|
return nil, fmt.Errorf("not implemented")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user