func()

in pkg/providers/v1/aws.go [2780:2876]


func (c *Cloud) CreateDisk(volumeOptions *VolumeOptions) (KubernetesVolumeID, error) {
	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. AWS will throw an error if IOPS per GB gets out
		// of supported bounds, no need to check it here.
		createType = volumeOptions.VolumeType
		iops = int64(volumeOptions.CapacityGB * volumeOptions.IOPSPerGB)

		// Cap at min/max total IOPS, AWS would throw an error if it gets too
		// low/high.
		if iops < MinTotalIOPS {
			iops = MinTotalIOPS
		}
		if iops > MaxTotalIOPS {
			iops = MaxTotalIOPS
		}

	case "":
		createType = DefaultVolumeType

	default:
		return KubernetesVolumeID(""), fmt.Errorf("invalid AWS VolumeType %q", volumeOptions.VolumeType)
	}

	request := &ec2.CreateVolumeInput{}
	request.AvailabilityZone = aws.String(volumeOptions.AvailabilityZone)
	request.Size = aws.Int64(int64(volumeOptions.CapacityGB))
	request.VolumeType = aws.String(createType)
	request.Encrypted = aws.Bool(volumeOptions.Encrypted)
	if len(volumeOptions.KmsKeyID) > 0 {
		request.KmsKeyId = aws.String(volumeOptions.KmsKeyID)
		request.Encrypted = aws.Bool(true)
	}
	if iops > 0 {
		request.Iops = aws.Int64(iops)
	}

	tags := volumeOptions.Tags
	tags = c.tagging.buildTags(ResourceLifecycleOwned, tags)

	var tagList []*ec2.Tag
	for k, v := range tags {
		tagList = append(tagList, &ec2.Tag{
			Key: aws.String(k), Value: aws.String(v),
		})
	}
	request.TagSpecifications = append(request.TagSpecifications, &ec2.TagSpecification{
		Tags:         tagList,
		ResourceType: aws.String(ec2.ResourceTypeVolume),
	})

	response, err := c.ec2.CreateVolume(request)
	if err != nil {
		return KubernetesVolumeID(""), err
	}

	awsID := EBSVolumeID(aws.StringValue(response.VolumeId))
	if awsID == "" {
		return KubernetesVolumeID(""), fmt.Errorf("VolumeID was not returned by CreateVolume")
	}
	volumeName := KubernetesVolumeID("aws://" + aws.StringValue(response.AvailabilityZone) + "/" + string(awsID))

	err = c.waitUntilVolumeAvailable(volumeName)
	if err != nil {
		// AWS has a bad habbit of reporting success when creating a volume with
		// encryption keys that either don't exists or have wrong permissions.
		// Such volume lives for couple of seconds and then it's silently deleted
		// by AWS. There is no other check to ensure that given KMS key is correct,
		// because Kubernetes may have limited permissions to the key.
		if isAWSErrorVolumeNotFound(err) {
			err = fmt.Errorf("failed to create encrypted volume: the volume disappeared after creation, most likely due to inaccessible KMS encryption key")
		} else {
			// When DescribeVolumes api failed, plugin will lose track on the volumes' state
			// driver should be able to clean up these kind of volumes to make sure they are not leaked on customers' account
			klog.V(5).Infof("Failed to create the volume %v due to %v. Will try to delete it.", volumeName, err)
			awsDisk, newDiskError := newAWSDisk(c, volumeName)
			if newDiskError != nil {
				klog.Errorf("Failed to delete the volume %v due to error: %v", volumeName, newDiskError)
			} else {
				if _, deleteVolumeError := awsDisk.deleteVolume(); deleteVolumeError != nil {
					klog.Errorf("Failed to delete the volume %v due to error: %v", volumeName, deleteVolumeError)
				} else {
					klog.V(5).Infof("%v is deleted because it is not in desired state after waiting", volumeName)
				}
			}
		}
		return KubernetesVolumeID(""), err
	}

	return volumeName, nil
}