func (b *VolumeSnapshotter) SetVolumeID(unstructuredPV runtime.Unstructured, volumeID string) (runtime.Unstructured, error) {
	pv := new(v1.PersistentVolume)
	if err := runtime.DefaultUnstructuredConverter.FromUnstructured(unstructuredPV.UnstructuredContent(), pv); err != nil {
		return nil, errors.WithStack(err)
	}
	err := setEBSDiskID(pv, volumeID)
	if err != nil {
		return nil, err
	}
	res, err := runtime.DefaultUnstructuredConverter.ToUnstructured(pv)
	if err != nil {
		return nil, errors.WithStack(err)
	}
	return &unstructured.Unstructured{Object: res}, nil
}
SetVolumeID set the volume's id
package main
import (
	"fmt"
	"os"
	"regexp"
	"strings"
	"github.com/aliyun/alibaba-cloud-sdk-go/sdk/requests"
	"github.com/aliyun/alibaba-cloud-sdk-go/services/ecs"
	"github.com/pkg/errors"
	"github.com/sirupsen/logrus"
	"k8s.io/api/core/v1"
	"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
	"k8s.io/apimachinery/pkg/runtime"
	veleroplugin "github.com/vmware-tanzu/velero/pkg/plugin/framework"
)
const (
	ackClusterNameKey = "ACK_CLUSTER_NAME"
)
type VolumeSnapshotter struct {
	log logrus.FieldLogger
	ecs *ecs.Client
}
func newVolumeSnapshotter(logger logrus.FieldLogger) *VolumeSnapshotter {
	return &VolumeSnapshotter{log: logger}
}
func (b *VolumeSnapshotter) Init(config map[string]string) error {
	if err := veleroplugin.ValidateVolumeSnapshotterConfigKeys(config, regionConfigKey); err != nil {
		return err
	}
	if err := loadEnv(); err != nil {
		return err
	}
	regionID := getEcsRegionID(config)
	accessKeyID := os.Getenv("ALIBABA_CLOUD_ACCESS_KEY_ID")
	accessKeySecret := os.Getenv("ALIBABA_CLOUD_ACCESS_KEY_SECRET")
	stsToken := os.Getenv("ALIBABA_CLOUD_ACCESS_STS_TOKEN")
	if len(accessKeyID) == 0 {
		return errors.Errorf("ALIBABA_CLOUD_ACCESS_KEY_ID environment variable is not set")
	}
	if len(accessKeySecret) == 0 {
		return errors.Errorf("ALIBABA_CLOUD_ACCESS_KEY_SECRET environment variable is not set")
	}
	var client *ecs.Client
	var err error
	if len(stsToken) == 0 {
		client, err = ecs.NewClientWithAccessKey(regionID, accessKeyID, accessKeySecret)
	} else {
		client, err = ecs.NewClientWithStsToken(regionID, accessKeyID, accessKeySecret, stsToken)
	}
	b.ecs = client
	return err
}
func getJSONArrayString(id string) string {
	return fmt.Sprintf("[\"%s\"]", id)
}
func (b *VolumeSnapshotter) CreateVolumeFromSnapshot(snapshotID, volumeType, volumeAZ string, iops *int64) (volumeID string, err error) {
	snapReq := ecs.CreateDescribeSnapshotsRequest()
	snapReq.SnapshotIds = getJSONArrayString(snapshotID)
	snapRes, err := b.ecs.DescribeSnapshots(snapReq)
	if err != nil {
		return "", errors.WithStack(err)
	}
	if count := len(snapRes.Snapshots.Snapshot); count != 1 {
		return "", errors.Errorf("expected 1 snapshot from DescribeSnapshots for %s, got %v", snapshotID, count)
	}
	tags := getTagsForCluster(snapRes.Snapshots.Snapshot[0].Tags.Tag)
	volumeAZ, err = getMetaData(metadataZoneKey)
	if err != nil {
		return "", errors.Errorf("failed to get zone-id, got %v", err)
	}
	req := ecs.CreateCreateDiskRequest()
	req.SnapshotId = snapshotID
	req.ZoneId = volumeAZ
	req.DiskCategory = volumeType
	req.Encrypted = requests.NewBoolean(snapRes.Snapshots.Snapshot[0].Encrypted)
	req.Tag = &tags
	res, err := b.ecs.CreateDisk(req)
	if err != nil {
		return "", errors.WithStack(err)
	}
	return res.DiskId, nil
}
func (b *VolumeSnapshotter) GetVolumeInfo(volumeID, volumeAZ string) (string, *int64, error) {
	volumeInfo, err := b.describeVolume(volumeID, volumeAZ)
	if err != nil {
		return "", nil, err
	}
	iops := int64(volumeInfo.IOPS)
	return volumeInfo.Category, &iops, nil
}
func (b *VolumeSnapshotter) describeVolume(volumeID string, volumeAZ string) (*ecs.Disk, error) {
	req := ecs.CreateDescribeDisksRequest()
	req.DiskIds = getJSONArrayString(volumeID)
	req.ZoneId = volumeAZ
	res, err := b.ecs.DescribeDisks(req)
	if err != nil {
		return nil, errors.WithStack(err)
	}
	if count := len(res.Disks.Disk); count != 1 {
		return nil, errors.Errorf("Expected one volume from DescribeDisks for volume ID %v, got %v", volumeID, count)
	}
	return &res.Disks.Disk[0], nil
}
func (b *VolumeSnapshotter) CreateSnapshot(volumeID, volumeAZ string, tags map[string]string) (string, error) {
	volumeInfo, err := b.describeVolume(volumeID, volumeAZ)
	if err != nil {
		return "", err
	}
	req := ecs.CreateCreateSnapshotRequest()
	req.DiskId = volumeID
	newTags := getTags(tags, volumeInfo.Tags.Tag)
	if len(tags) > 0 {
		req.Tag = &newTags
	}
	res, err := b.ecs.CreateSnapshot(req)
	if err != nil {
		return "", errors.WithStack(err)
	}
	return res.SnapshotId, nil
}
func getTagsForCluster(snapshotTags []ecs.Tag) []ecs.CreateDiskTag {
	var result []ecs.CreateDiskTag
	clusterName, haveACKClusterNameEnvVar := os.LookupEnv(ackClusterNameKey)
	if haveACKClusterNameEnvVar {
		result = append(result, ecs.CreateDiskTag{
			Key:   "kubernetes.io/cluster/" + clusterName,
			Value: "owned",
		})
		result = append(result, ecs.CreateDiskTag{
			Key:   "KubernetesCluster",
			Value: clusterName,
		})
	}
	for _, tag := range snapshotTags {
		if haveACKClusterNameEnvVar && (strings.HasPrefix(tag.TagKey, "kubernetes.io/cluster/") || tag.TagKey == "KubernetesCluster") {
			continue
		}
		result = append(result, ecs.CreateDiskTag{
			Key:   tag.TagKey,
			Value: tag.TagValue,
		})
	}
	return result
}
func getTags(veleroTags map[string]string, volumeTags []ecs.Tag) []ecs.CreateSnapshotTag {
	var result []ecs.CreateSnapshotTag
	for k, v := range veleroTags {
		result = append(result, ecs.CreateSnapshotTag{
			Key:   k,
			Value: v,
		})
	}
	for _, tag := range volumeTags {
		if _, found := veleroTags[tag.TagKey]; found {
			continue
		}
		result = append(result, ecs.CreateSnapshotTag{
			Key:   tag.TagKey,
			Value: tag.TagValue,
		})
	}
	return result
}
func (b *VolumeSnapshotter) DeleteSnapshot(snapshotID string) error {
	req := ecs.CreateDeleteSnapshotRequest()
	req.SnapshotId = snapshotID
	_, err := b.ecs.DeleteSnapshot(req)
	if err != nil {
		return errors.WithStack(err)
	}
	return err
}
var ebsVolumeIDRegex = regexp.MustCompile("vol-.*")
func checkCSIVolumeDriver(driver string) error {
	if driver != "diskplugin.csi.alibabacloud.com" {
		return errors.New("unsupported CSI driver: " + driver)
	}
	return nil
}
func checkFlexVolumeDriver(driver string) error {
	if driver != "alicloud/disk" {
		return errors.New("unsupported FlexVolume driver: " + driver)
	}
	return nil
}
func getEBSDiskID(pv *v1.PersistentVolume) (string, error) {
	if pv.Spec.CSI != nil {
		err := checkCSIVolumeDriver(pv.Spec.CSI.Driver)
		if err != nil {
			return "", err
		}
		handle := pv.Spec.CSI.VolumeHandle
		if handle == "" {
			return "", errors.New("spec.CSI.VolumeHandle not found")
		}
		return handle, nil
	} else if pv.Spec.FlexVolume != nil {
		err := checkFlexVolumeDriver(pv.Spec.FlexVolume.Driver)
		if err != nil {
			return "", err
		}
		options := pv.Spec.FlexVolume.Options
		if options == nil || (options["VolumeId"] == "" && options["volumeId"] == "") {
			return "", errors.New("spec.FlexVolume.Options['VolumeId'] or spec.FlexVolume.Options['volumeId'] not found")
		} else if options["VolumeId"] != "" {
			return options["VolumeId"], nil
		} else {
			return options["volumeId"], nil
		}
	}
	return "", nil
}
func setEBSDiskID(pv *v1.PersistentVolume, diskID string) error {
	if pv.Spec.CSI != nil {
		err := checkCSIVolumeDriver(pv.Spec.CSI.Driver)
		if err != nil {
			return err
		}
		pv.Spec.CSI.VolumeHandle = diskID
		return nil
	} else if pv.Spec.FlexVolume != nil {
		err := checkFlexVolumeDriver(pv.Spec.FlexVolume.Driver)
		if err != nil {
			return err
		}
		options := pv.Spec.FlexVolume.Options
		if options == nil {
			options = map[string]string{}
			pv.Spec.FlexVolume.Options = options
		}
		options["VolumeId"] = diskID
		return nil
	}
	return errors.New("spec.CSI or spec.FlexVolume not found")
}
func (b *VolumeSnapshotter) GetVolumeID(unstructuredPV runtime.Unstructured) (string, error) {
	pv := new(v1.PersistentVolume)
	if err := runtime.DefaultUnstructuredConverter.FromUnstructured(unstructuredPV.UnstructuredContent(), pv); err != nil {
		return "", errors.WithStack(err)
	}
	volumeID, err := getEBSDiskID(pv)
	if err != nil {
		return "", err
	}
	return volumeID, nil
}