2015-03-06 14:26:39 +00:00
|
|
|
/*
|
2016-06-03 00:25:58 +00:00
|
|
|
Copyright 2014 The Kubernetes Authors.
|
2015-03-06 14:26:39 +00:00
|
|
|
|
|
|
|
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.
|
|
|
|
*/
|
|
|
|
|
2015-04-09 13:40:48 +00:00
|
|
|
package aws_ebs
|
2015-03-06 14:26:39 +00:00
|
|
|
|
|
|
|
import (
|
2016-01-19 15:45:30 +00:00
|
|
|
"fmt"
|
2016-08-18 08:36:49 +00:00
|
|
|
"strconv"
|
|
|
|
"strings"
|
2015-03-06 14:26:39 +00:00
|
|
|
"time"
|
|
|
|
|
2015-08-05 22:05:17 +00:00
|
|
|
"github.com/golang/glog"
|
2016-05-10 19:02:41 +00:00
|
|
|
"k8s.io/kubernetes/pkg/cloudprovider"
|
2016-01-19 15:45:30 +00:00
|
|
|
"k8s.io/kubernetes/pkg/cloudprovider/providers/aws"
|
2015-12-15 09:22:49 +00:00
|
|
|
"k8s.io/kubernetes/pkg/volume"
|
2016-08-15 23:29:21 +00:00
|
|
|
volumeutil "k8s.io/kubernetes/pkg/volume/util"
|
2015-03-06 14:26:39 +00:00
|
|
|
)
|
|
|
|
|
2016-01-19 15:45:30 +00:00
|
|
|
const (
|
|
|
|
diskPartitionSuffix = ""
|
|
|
|
diskXVDPath = "/dev/xvd"
|
|
|
|
diskXVDPattern = "/dev/xvd*"
|
|
|
|
maxChecks = 60
|
|
|
|
maxRetries = 10
|
|
|
|
checkSleepDuration = time.Second
|
|
|
|
errorSleepDuration = 5 * time.Second
|
|
|
|
)
|
|
|
|
|
2015-03-06 14:26:39 +00:00
|
|
|
type AWSDiskUtil struct{}
|
|
|
|
|
2015-12-15 09:22:49 +00:00
|
|
|
func (util *AWSDiskUtil) DeleteVolume(d *awsElasticBlockStoreDeleter) error {
|
2016-05-10 19:02:41 +00:00
|
|
|
cloud, err := getCloudProvider(d.awsElasticBlockStore.plugin.host.GetCloudProvider())
|
2015-12-15 09:22:49 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2015-10-20 02:06:33 +00:00
|
|
|
deleted, err := cloud.DeleteDisk(d.volumeID)
|
|
|
|
if err != nil {
|
2016-01-19 15:45:30 +00:00
|
|
|
glog.V(2).Infof("Error deleting EBS Disk volume %s: %v", d.volumeID, err)
|
2015-12-15 09:22:49 +00:00
|
|
|
return err
|
|
|
|
}
|
2015-10-20 02:06:33 +00:00
|
|
|
if deleted {
|
|
|
|
glog.V(2).Infof("Successfully deleted EBS Disk volume %s", d.volumeID)
|
|
|
|
} else {
|
|
|
|
glog.V(2).Infof("Successfully deleted EBS Disk volume %s (actually already deleted)", d.volumeID)
|
|
|
|
}
|
2015-12-15 09:22:49 +00:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2016-03-05 23:11:17 +00:00
|
|
|
// CreateVolume creates an AWS EBS volume.
|
|
|
|
// Returns: volumeID, volumeSizeGB, labels, error
|
|
|
|
func (util *AWSDiskUtil) CreateVolume(c *awsElasticBlockStoreProvisioner) (string, int, map[string]string, error) {
|
2016-05-10 19:02:41 +00:00
|
|
|
cloud, err := getCloudProvider(c.awsElasticBlockStore.plugin.host.GetCloudProvider())
|
2015-12-15 09:22:49 +00:00
|
|
|
if err != nil {
|
2016-03-05 23:11:17 +00:00
|
|
|
return "", 0, nil, err
|
2015-12-15 09:22:49 +00:00
|
|
|
}
|
|
|
|
|
2016-02-12 08:46:59 +00:00
|
|
|
// AWS volumes don't have Name field, store the name in Name tag
|
|
|
|
var tags map[string]string
|
|
|
|
if c.options.CloudTags == nil {
|
|
|
|
tags = make(map[string]string)
|
|
|
|
} else {
|
|
|
|
tags = *c.options.CloudTags
|
|
|
|
}
|
|
|
|
tags["Name"] = volume.GenerateVolumeName(c.options.ClusterName, c.options.PVName, 255) // AWS tags can have 255 characters
|
2015-12-15 09:22:49 +00:00
|
|
|
|
2016-02-12 08:46:59 +00:00
|
|
|
requestBytes := c.options.Capacity.Value()
|
|
|
|
// AWS works with gigabytes, convert to GiB with rounding up
|
|
|
|
requestGB := int(volume.RoundUpSize(requestBytes, 1024*1024*1024))
|
|
|
|
volumeOptions := &aws.VolumeOptions{
|
|
|
|
CapacityGB: requestGB,
|
2016-03-10 13:12:17 +00:00
|
|
|
Tags: tags,
|
2016-06-16 17:36:55 +00:00
|
|
|
PVCName: c.options.PVCName,
|
2016-02-12 08:46:59 +00:00
|
|
|
}
|
2016-08-18 08:36:49 +00:00
|
|
|
// Apply Parameters (case-insensitive). We leave validation of
|
|
|
|
// the values to the cloud provider.
|
|
|
|
for k, v := range c.options.Parameters {
|
|
|
|
switch strings.ToLower(k) {
|
|
|
|
case "type":
|
|
|
|
volumeOptions.VolumeType = v
|
|
|
|
case "zone":
|
|
|
|
volumeOptions.AvailabilityZone = v
|
|
|
|
case "iopspergb":
|
|
|
|
volumeOptions.IOPSPerGB, err = strconv.Atoi(v)
|
|
|
|
if err != nil {
|
|
|
|
return "", 0, nil, fmt.Errorf("invalid iopsPerGB value %q, must be integer between 1 and 30: %v", v, err)
|
|
|
|
}
|
|
|
|
default:
|
|
|
|
return "", 0, nil, fmt.Errorf("invalid option %q for volume plugin %s", k, c.plugin.GetPluginName())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// TODO: implement c.options.ProvisionerSelector parsing
|
|
|
|
if c.options.Selector != nil {
|
|
|
|
return "", 0, nil, fmt.Errorf("claim.Spec.Selector is not supported for dynamic provisioning on AWS")
|
|
|
|
}
|
2016-01-19 15:45:30 +00:00
|
|
|
|
|
|
|
name, err := cloud.CreateDisk(volumeOptions)
|
2015-12-15 09:22:49 +00:00
|
|
|
if err != nil {
|
2016-01-19 15:45:30 +00:00
|
|
|
glog.V(2).Infof("Error creating EBS Disk volume: %v", err)
|
2016-03-05 23:11:17 +00:00
|
|
|
return "", 0, nil, err
|
2015-12-15 09:22:49 +00:00
|
|
|
}
|
2016-01-19 15:45:30 +00:00
|
|
|
glog.V(2).Infof("Successfully created EBS Disk volume %s", name)
|
2016-03-05 23:11:17 +00:00
|
|
|
|
|
|
|
labels, err := cloud.GetVolumeLabels(name)
|
|
|
|
if err != nil {
|
|
|
|
// We don't really want to leak the volume here...
|
|
|
|
glog.Errorf("error building labels for new EBS volume %q: %v", name, err)
|
|
|
|
}
|
|
|
|
|
|
|
|
return name, int(requestGB), labels, nil
|
2016-01-19 15:45:30 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Returns the first path that exists, or empty string if none exist.
|
2016-02-03 20:48:53 +00:00
|
|
|
func verifyDevicePath(devicePaths []string) (string, error) {
|
2016-01-19 15:45:30 +00:00
|
|
|
for _, path := range devicePaths {
|
2016-08-15 23:29:21 +00:00
|
|
|
if pathExists, err := volumeutil.PathExists(path); err != nil {
|
2016-01-19 15:45:30 +00:00
|
|
|
return "", fmt.Errorf("Error checking if path exists: %v", err)
|
|
|
|
} else if pathExists {
|
|
|
|
return path, nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return "", nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// Returns the first path that exists, or empty string if none exist.
|
|
|
|
func verifyAllPathsRemoved(devicePaths []string) (bool, error) {
|
|
|
|
allPathsRemoved := true
|
|
|
|
for _, path := range devicePaths {
|
2016-08-15 23:29:21 +00:00
|
|
|
if exists, err := volumeutil.PathExists(path); err != nil {
|
2016-01-19 15:45:30 +00:00
|
|
|
return false, fmt.Errorf("Error checking if path exists: %v", err)
|
|
|
|
} else {
|
|
|
|
allPathsRemoved = allPathsRemoved && !exists
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return allPathsRemoved, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// Returns list of all paths for given EBS mount
|
|
|
|
// This is more interesting on GCE (where we are able to identify volumes under /dev/disk-by-id)
|
|
|
|
// Here it is mostly about applying the partition path
|
2016-05-10 19:02:41 +00:00
|
|
|
func getDiskByIdPaths(partition string, devicePath string) []string {
|
2016-01-19 15:45:30 +00:00
|
|
|
devicePaths := []string{}
|
|
|
|
if devicePath != "" {
|
|
|
|
devicePaths = append(devicePaths, devicePath)
|
|
|
|
}
|
|
|
|
|
2016-05-10 19:02:41 +00:00
|
|
|
if partition != "" {
|
2016-01-19 15:45:30 +00:00
|
|
|
for i, path := range devicePaths {
|
2016-05-10 19:02:41 +00:00
|
|
|
devicePaths[i] = path + diskPartitionSuffix + partition
|
2016-01-19 15:45:30 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return devicePaths
|
|
|
|
}
|
|
|
|
|
|
|
|
// Return cloud provider
|
2016-04-17 20:31:02 +00:00
|
|
|
func getCloudProvider(cloudProvider cloudprovider.Interface) (*aws.Cloud, error) {
|
|
|
|
awsCloudProvider, ok := cloudProvider.(*aws.Cloud)
|
2016-04-01 23:40:36 +00:00
|
|
|
if !ok || awsCloudProvider == nil {
|
2016-05-10 19:02:41 +00:00
|
|
|
return nil, fmt.Errorf("Failed to get AWS Cloud Provider. GetCloudProvider returned %v instead", cloudProvider)
|
2016-01-19 15:45:30 +00:00
|
|
|
}
|
|
|
|
|
2016-04-01 23:40:36 +00:00
|
|
|
return awsCloudProvider, nil
|
2016-01-19 15:45:30 +00:00
|
|
|
}
|