func (controller *PacketControllerServer) GetCapacity(ctx context.Context, in *csi.GetCapacityRequest) (*csi.GetCapacityResponse, error) {
	return nil, status.Error(codes.Unimplemented, "")
}
GetCapacity get the available capacity
package driver
import (
	"fmt"
	"net/http"
	"strconv"
	"strings"
	"time"
	"github.com/packethost/packngo"
	"github.com/pkg/errors"
	csi "github.com/container-storage-interface/spec/lib/go/csi"
	"github.com/packethost/csi-packet/pkg/packet"
	log "github.com/sirupsen/logrus"
	"golang.org/x/net/context"
	"google.golang.org/grpc/codes"
	"google.golang.org/grpc/status"
)
const (
	VolumeMaxRetries = 10
	VolumeRetryInterval = 1  
	AttachMaxRetries = 5
	AttachRetryInterval = 1  
	DetachMaxRetries = 30
	DetachRetryInterval = 2  
)
var _ csi.ControllerServer = &PacketControllerServer{}
type PacketControllerServer struct {
	Provider packet.VolumeProvider
}
func NewPacketControllerServer(provider packet.VolumeProvider) *PacketControllerServer {
	return &PacketControllerServer{
		Provider: provider,
	}
}
func getSizeRequest(capacityRange *csi.CapacityRange) int {
	var sizeRequestGiB int
	if capacityRange == nil {
		sizeRequestGiB = packet.DefaultVolumeSizeGi
	} else {
		maxBytes := capacityRange.GetLimitBytes()
		if maxBytes != 0 {
			sizeRequestGiB = int(maxBytes / packet.Gibi)
		} else {
			minBytes := capacityRange.GetRequiredBytes()
			if minBytes != 0 {
				sizeRequestGiB = int(minBytes / packet.Gibi)
			}
		}
	}
	if sizeRequestGiB > packet.MaxVolumeSizeGi {
		sizeRequestGiB = packet.MaxVolumeSizeGi
	}
	if sizeRequestGiB < packet.MinVolumeSizeGi {
		sizeRequestGiB = packet.MinVolumeSizeGi
	}
	return sizeRequestGiB
}
func getPlanID(parameters map[string]string) string {
	var planID string
	volumePlanRequest := parameters["plan"]
	switch volumePlanRequest {
	case packet.VolumePlanPerformance:
		planID = packet.VolumePlanPerformanceID
	case packet.VolumePlanStandard:
		planID = packet.VolumePlanStandardID
	default:
		planID = packet.VolumePlanStandardID
	}
	return planID
}
func (controller *PacketControllerServer) CreateVolume(ctx context.Context, in *csi.CreateVolumeRequest) (*csi.CreateVolumeResponse, error) {
	if controller == nil || controller.Provider == nil {
		return nil, status.Error(codes.Internal, "controller not configured")
	}
	logger := log.WithFields(log.Fields{"volume_name": in.Name})
	logger.Info("CreateVolume called")
	if in.Name == "" {
		return nil, status.Error(codes.InvalidArgument, "Name unspecified for CreateVolume")
	}
	if in.VolumeCapabilities == nil {
		return nil, status.Error(codes.InvalidArgument, "VolumeCapabilities unspecified for CreateVolume")
	}
	sizeRequestGiB := getSizeRequest(in.CapacityRange)
	planID := getPlanID(in.Parameters)
	logger.WithFields(log.Fields{"planID": planID, "sizeRequestGiB": sizeRequestGiB}).Info("Volume requested")
	volumes, httpResponse, err := controller.Provider.ListVolumes(nil)
	if err != nil {
		return nil, errors.Wrap(err, httpResponse.Status)
	}
	if httpResponse.StatusCode != http.StatusOK {
		return nil, errors.Errorf("bad status from list volumes, %s", httpResponse.Status)
	}
	for _, volume := range volumes {
		description, err := packet.ReadDescription(volume.Description)
		if err == nil && description.Name == in.Name {
			logger.Infof("Volume already exists with id %s", volume.ID)
			if volume.Size != sizeRequestGiB {
				return nil, status.Errorf(codes.AlreadyExists, "mismatch with existing volume %s, size %d, requested %d", in.Name, volume.Size, sizeRequestGiB)
			}
			if volume.Plan.ID != planID {
				return nil, status.Errorf(codes.AlreadyExists, "mismatch with existing volume %s, plan %+v, requested %s", in.Name, volume.Plan, planID)
			}
			out := csi.CreateVolumeResponse{
				Volume: &csi.Volume{
					CapacityBytes: int64(volume.Size) * packet.Gibi,
					VolumeId:      volume.ID,
				},
			}
			return &out, nil
		}
	}
	description := packet.NewVolumeDescription(in.Name)
	volumeCreateRequest := packngo.VolumeCreateRequest{
		Size:         sizeRequestGiB,        
		BillingCycle: packet.BillingHourly,  
		PlanID:       planID,                
		Description:  description.String(),  
	}
	volume, httpResponse, err := controller.Provider.Create(&volumeCreateRequest)
	if err != nil {
		return nil, err
	}
	if httpResponse.StatusCode != http.StatusOK && httpResponse.StatusCode != http.StatusCreated {
		return nil, errors.Errorf("bad status from create volume, %s", httpResponse.Status)
	}
	description, err = packet.ReadDescription(volume.Description)
	if err != nil {
		return nil, errors.Wrap(err, "unable to read csi description from provider volume")
	}
	volReady := packet.VolumeReady(volume)
	for counter := 0; !volReady && counter < VolumeMaxRetries; counter++ {
		time.Sleep(VolumeRetryInterval * time.Second)
		volume, httpResponse, err := controller.Provider.Get(volume.ID)
		if err != nil {
			return nil, err
		}
		if httpResponse.StatusCode != http.StatusOK && httpResponse.StatusCode != http.StatusCreated {
			return nil, errors.Errorf("bad status from create volume, %s", httpResponse.Status)
		}
		volReady = packet.VolumeReady(volume)
	}
	if !volReady {
		return nil, errors.Errorf("volume %s not in ready state after %d seconds", volume.Name, VolumeMaxRetries*VolumeRetryInterval)
	}
	out := csi.CreateVolumeResponse{
		Volume: &csi.Volume{
			CapacityBytes: int64(volume.Size) * packet.Gibi,
			VolumeId:      volume.ID,
		},
	}
	return &out, nil
}
func (controller *PacketControllerServer) DeleteVolume(ctx context.Context, in *csi.DeleteVolumeRequest) (*csi.DeleteVolumeResponse, error) {
	if controller == nil || controller.Provider == nil {
		return nil, status.Error(codes.Internal, "controller not configured")
	}
	logger := log.WithFields(log.Fields{"volume_id": in.VolumeId})
	logger.Info("DeleteVolume called")
	if in.VolumeId == "" {
		return nil, status.Error(codes.InvalidArgument, "VolumeId unspecified for DeleteVolume")
	}
	httpResponse, err := controller.Provider.Delete(in.GetVolumeId())
	if err != nil {
		if httpResponse.StatusCode == http.StatusUnprocessableEntity {
			return nil, status.Errorf(codes.FailedPrecondition, "delete should retry, %v", err)
		}
		return nil, status.Errorf(codes.Unknown, "bad status from delete volumes, %s", httpResponse.Status)
	}
	switch httpResponse.StatusCode {
	case http.StatusOK, http.StatusNoContent, http.StatusNotFound:
		return &csi.DeleteVolumeResponse{}, nil
	case http.StatusUnprocessableEntity:
		return nil, status.Errorf(codes.FailedPrecondition, "code %d indicates retry condition", httpResponse.StatusCode)
	}
	return nil, status.Errorf(codes.Unknown, "bad status from delete volumes, %s", httpResponse.Status)
}
func (controller *PacketControllerServer) ControllerPublishVolume(ctx context.Context, in *csi.ControllerPublishVolumeRequest) (*csi.ControllerPublishVolumeResponse, error) {
	if controller == nil || controller.Provider == nil {
		return nil, status.Error(codes.Internal, "controller not configured")
	}
	if in.NodeId == "" {
		return nil, status.Error(codes.InvalidArgument, "NodeId unspecified for ControllerPublishVolume")
	}
	if in.VolumeId == "" {
		return nil, status.Error(codes.InvalidArgument, "VolumeId unspecified for ControllerPublishVolume")
	}
	if in.VolumeCapability == nil {
		return nil, status.Error(codes.InvalidArgument, "VolumeCapability unspecified for ControllerPublishVolume")
	}
	logger := log.WithFields(log.Fields{"volume_id": in.VolumeId})
	logger.Info("ControllerPublishVolume called")
	nodeID := in.NodeId
	volumeID := in.VolumeId
	volume, httpResponse, err := controller.Provider.Get(volumeID)
	returnError := processGetError(volumeID, httpResponse, err)
	if returnError != nil {
		return nil, returnError
	}
	count := 0
	var attachment *packngo.VolumeAttachment
forloop:
	for {
		attachment, httpResponse, err = controller.Provider.Attach(volumeID, nodeID)
		switch {
		case err != nil && httpResponse != nil && httpResponse.StatusCode == http.StatusNotFound:
			return nil, status.Errorf(codes.NotFound, "node or volume not found attempting to attach %s to %s", volumeID, nodeID)
		case err != nil && packet.IsWrongDeviceAttachment(err):
			if count > AttachMaxRetries {
				return nil, err
			}
			count++
			time.Sleep(AttachRetryInterval * time.Second)
			continue forloop
		case err != nil && packet.IsTooManyDevicesAttached(err):
			return nil, err
		case err != nil:
			return nil, status.Errorf(codes.Unknown, "error attempting to attach %s to %s, %v", volumeID, nodeID, err)
		case httpResponse.StatusCode != http.StatusOK && httpResponse.StatusCode != http.StatusCreated:
			return nil, status.Errorf(codes.Unknown, "bad status from attach volumes, %s", httpResponse.Status)
		default:
			break forloop
		}
	}
	metadata := make(map[string]string)
	metadata["AttachmentId"] = attachment.ID
	metadata["VolumeId"] = volumeID
	metadata["VolumeName"] = volume.Name
	response := &csi.ControllerPublishVolumeResponse{
		PublishContext: metadata,
	}
	return response, nil
}
func (controller *PacketControllerServer) ControllerUnpublishVolume(ctx context.Context, in *csi.ControllerUnpublishVolumeRequest) (*csi.ControllerUnpublishVolumeResponse, error) {
	logger := log.WithFields(log.Fields{"node_id": in.NodeId, "volume_id": in.VolumeId})
	logger.Info("UnpublishVolume called")
	if controller == nil || controller.Provider == nil {
		return nil, status.Error(codes.Internal, "controller not configured")
	}
	nodeID := in.GetNodeId()
	volumeID := in.GetVolumeId()
	if volumeID == "" {
		return nil, status.Error(codes.InvalidArgument, "VolumeId unspecified for ControllerUnpublishVolume")
	}
	volume, httpResponse, err := controller.Provider.Get(volumeID)
	if err != nil {
		if httpResponse != nil && httpResponse.StatusCode == http.StatusNotFound {
			logger.Infof("volumeId not found, Get() returned %d", httpResponse.StatusCode)
			return &csi.ControllerUnpublishVolumeResponse{}, nil
		}
		return nil, err
	}
	if httpResponse.StatusCode != http.StatusOK {
		return nil, status.Errorf(codes.Unknown, "bad status from get volume %s, %s", volumeID, httpResponse.Status)
	}
	attachments := volume.Attachments
	if attachments == nil {
		return nil, status.Errorf(codes.Unknown, "cannot detach unattached volume %s", volumeID)
	}
	attachmentIDs := []string{}
	for _, attachment := range attachments {
		switch {
		case nodeID == "":
			logger.Infof("empty nodeID; detaching from node %s", attachment.Device.ID)
			attachmentIDs = append(attachmentIDs, attachment.ID)
		case attachment.Volume.ID == volumeID && attachment.Device.ID == nodeID:
			logger.Debugf("matched node ID; detaching from node %s", attachment.Device.ID)
			attachmentIDs = append(attachmentIDs, attachment.ID)
		default:
			logger.Debugf("mismatched node ID; not detaching from node %s", attachment.Device.ID)
		}
	}
	if len(attachmentIDs) == 0 {
		return nil, status.Errorf(codes.Unknown, "no attachment ID found for volume %s", volumeID)
	}
	count := 0
	failed := []string{}
	retryIDs := []string{}
	for {
		for _, a := range attachmentIDs {
			httpResponse, err = controller.Provider.Detach(a)
			switch {
			case err != nil && httpResponse != nil && httpResponse.StatusCode == http.StatusNotFound:
				logger.WithFields(log.Fields{"attachmentID": a}).Infof("attachmentID not found, Detach() returned %d", httpResponse.StatusCode)
				failed = append(failed, fmt.Sprintf("%s: attachmentID not found, Detach() returned %d", a, httpResponse.StatusCode))
			case err != nil && packet.IsDeviceStillAttached(err):
				retryIDs = append(retryIDs, a)
			case err != nil:
				failed = append(failed, fmt.Sprintf(err.Error()))
			case httpResponse != nil && httpResponse.StatusCode != http.StatusOK && httpResponse.StatusCode != http.StatusNotFound:
				failed = append(failed, fmt.Sprintf("%s: bad status from detach volume, %s", a, httpResponse.Status))
			}
		}
		if len(retryIDs) <= 0 || count >= DetachMaxRetries {
			break
		}
		count++
		attachmentIDs = retryIDs[:]
		retryIDs = retryIDs[:0]
		time.Sleep(DetachRetryInterval * time.Second)
	}
	for _, a := range retryIDs {
		failed = append(failed, fmt.Sprintf("%s: could not detach after %d retries in %d seconds", a, DetachMaxRetries, DetachRetryInterval*DetachMaxRetries))
	}
	if len(failed) != 0 {
		return nil, fmt.Errorf(strings.Join(failed, ";"))
	}
	logger.Info("successful Detach()")
	return &csi.ControllerUnpublishVolumeResponse{}, nil
}
func (controller *PacketControllerServer) ValidateVolumeCapabilities(ctx context.Context, in *csi.ValidateVolumeCapabilitiesRequest) (*csi.ValidateVolumeCapabilitiesResponse, error) {
	if controller == nil || controller.Provider == nil {
		return nil, status.Error(codes.Internal, "controller not configured")
	}
	if in.VolumeCapabilities == nil {
		return nil, status.Error(codes.InvalidArgument, "VolumeCapability unspecified for ValidateVolumeCapabilities")
	}
	if in.VolumeId == "" {
		return nil, status.Error(codes.InvalidArgument, "VolumeId unspecified for ValidateVolumeCapabilities")
	}
	volumeID := in.VolumeId
	_, httpResponse, err := controller.Provider.Get(volumeID)
	returnError := processGetError(volumeID, httpResponse, err)
	if returnError != nil {
		return nil, returnError
	}
	supported := map[csi.VolumeCapability_AccessMode_Mode]bool{
		csi.VolumeCapability_AccessMode_SINGLE_NODE_WRITER:      true,
		csi.VolumeCapability_AccessMode_SINGLE_NODE_READER_ONLY: true,
	}
	for _, cap := range in.VolumeCapabilities {
		mode := cap.AccessMode.Mode
		if !supported[mode] {
			return &csi.ValidateVolumeCapabilitiesResponse{}, nil
		}
	}
	return &csi.ValidateVolumeCapabilitiesResponse{
		Confirmed: &csi.ValidateVolumeCapabilitiesResponse_Confirmed{
			VolumeCapabilities: in.VolumeCapabilities,
		},
	}, nil
}
func (controller *PacketControllerServer) ListVolumes(ctx context.Context, in *csi.ListVolumesRequest) (*csi.ListVolumesResponse, error) {
	if controller == nil || controller.Provider == nil {
		return nil, status.Error(codes.Internal, "controller not configured")
	}
	var listOptions *packngo.ListOptions
	if in != nil {
		listOptions = &packngo.ListOptions{
			PerPage: int(in.MaxEntries),
		}
		if in.StartingToken != "" {
			page, err := strconv.Atoi(in.StartingToken)
			if err != nil {
				return nil, status.Errorf(codes.Aborted, "starting token must be an integer to indicate which page, %s", in.StartingToken)
			}
			listOptions.Page = page
		}
	}
	volumes, httpResponse, err := controller.Provider.ListVolumes(listOptions)
	if err != nil {
		return nil, errors.Wrap(err, httpResponse.Status)
	}
	if httpResponse.StatusCode != http.StatusOK {
		return nil, status.Errorf(codes.Unknown, "bad status from list volumes, %s", httpResponse.Status)
	}
	entries := []*csi.ListVolumesResponse_Entry{}
	for _, volume := range volumes {
		entry := &csi.ListVolumesResponse_Entry{
			Volume: &csi.Volume{
				CapacityBytes: int64(volume.Size * 1024 * 1024 * 1024),
				VolumeId:      volume.ID,
			},
		}
		entries = append(entries, entry)
	}
	response := &csi.ListVolumesResponse{}
	response.Entries = entries
	return response, nil
}