From 4b97db202c5453ddcc15d4d0a862d8c9a8a03675 Mon Sep 17 00:00:00 2001 From: Jan Safranek Date: Thu, 18 Aug 2016 10:36:49 +0200 Subject: [PATCH] AWS changes for new provisioning model --- pkg/cloudprovider/providers/aws/aws.go | 61 +++++++++++++++++++++++--- pkg/volume/aws_ebs/aws_util.go | 24 ++++++++++ 2 files changed, 80 insertions(+), 5 deletions(-) diff --git a/pkg/cloudprovider/providers/aws/aws.go b/pkg/cloudprovider/providers/aws/aws.go index 37e8078e3eb..b736f52c225 100644 --- a/pkg/cloudprovider/providers/aws/aws.go +++ b/pkg/cloudprovider/providers/aws/aws.go @@ -209,11 +209,29 @@ type EC2Metadata interface { GetMetadata(path string) (string, error) } +// AWS volume types +const ( + // Provisioned IOPS SSD + VolumeTypeIO1 = "io1" + // General Purpose SSD + VolumeTypeGP2 = "gp2" + // Cold HDD (sc1) + VolumeTypeSC1 = "sc1" + // Throughput Optimized HDD + VolumeTypeST1 = "st1" +) + // VolumeOptions specifies capacity and tags for a volume. type VolumeOptions struct { - CapacityGB int - Tags map[string]string - PVCName string + CapacityGB int + Tags map[string]string + PVCName string + VolumeType string + AvailabilityZone string + // IOPSPerGB x CapacityGB will give total IOPS of the volume to create. + // IOPSPerGB must be bigger than zero and smaller or equal to 30. + // Calculated total IOPS will be capped at 20000 IOPS. + IOPSPerGB int } // Volumes is an interface for managing cloud-provisioned volumes @@ -1475,14 +1493,47 @@ func (c *Cloud) CreateDisk(volumeOptions *VolumeOptions) (string, error) { return "", fmt.Errorf("error querying for all zones: %v", err) } - createAZ := volume.ChooseZoneForVolume(allZones, volumeOptions.PVCName) + createAZ := volumeOptions.AvailabilityZone + if createAZ == "" { + createAZ = volume.ChooseZoneForVolume(allZones, volumeOptions.PVCName) + } + + var createType string + var iops int64 + switch volumeOptions.VolumeType { + case VolumeTypeGP2, VolumeTypeSC1, VolumeTypeST1: + createType = volumeOptions.VolumeType + + case VolumeTypeIO1: + // See http://docs.aws.amazon.com/AWSEC2/latest/APIReference/API_CreateVolume.html for IOPS constraints + if volumeOptions.IOPSPerGB <= 0 || volumeOptions.IOPSPerGB > 30 { + return "", fmt.Errorf("invalid iopsPerGB value %d, must be 0 < IOPSPerGB <= 30", volumeOptions.IOPSPerGB) + } + createType = volumeOptions.VolumeType + iops = int64(volumeOptions.CapacityGB * volumeOptions.IOPSPerGB) + if iops < 100 { + iops = 100 + } + if iops > 20000 { + iops = 20000 + } + + case "": + createType = DefaultVolumeType + + default: + return "", fmt.Errorf("invalid AWS VolumeType %q", volumeOptions.VolumeType) + } // TODO: Should we tag this with the cluster id (so it gets deleted when the cluster does?) request := &ec2.CreateVolumeInput{} request.AvailabilityZone = &createAZ volSize := int64(volumeOptions.CapacityGB) request.Size = &volSize - request.VolumeType = aws.String(DefaultVolumeType) + request.VolumeType = &createType + if iops > 0 { + request.Iops = &iops + } response, err := c.ec2.CreateVolume(request) if err != nil { return "", err diff --git a/pkg/volume/aws_ebs/aws_util.go b/pkg/volume/aws_ebs/aws_util.go index 487e263b5d5..9fc7d1d3696 100644 --- a/pkg/volume/aws_ebs/aws_util.go +++ b/pkg/volume/aws_ebs/aws_util.go @@ -18,6 +18,8 @@ package aws_ebs import ( "fmt" + "strconv" + "strings" "time" "github.com/golang/glog" @@ -83,6 +85,28 @@ func (util *AWSDiskUtil) CreateVolume(c *awsElasticBlockStoreProvisioner) (strin Tags: tags, PVCName: c.options.PVCName, } + // 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") + } name, err := cloud.CreateDisk(volumeOptions) if err != nil {