github.com/openshift/installer@v1.4.17/pkg/destroy/gcp/disk.go (about)

     1  package gcp
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  
     7  	"github.com/pkg/errors"
     8  	"google.golang.org/api/compute/v1"
     9  	"google.golang.org/api/googleapi"
    10  
    11  	gcpconsts "github.com/openshift/installer/pkg/constants/gcp"
    12  	"github.com/openshift/installer/pkg/types/gcp"
    13  )
    14  
    15  const (
    16  	maxGCEPDNameLength    = 63
    17  	estimatedPVNameLength = 40
    18  	// Removing an extra value (-1) for the "-" separated between the storage name and the pv name
    19  	storageNameLength = maxGCEPDNameLength - estimatedPVNameLength - 1
    20  )
    21  
    22  // formatClusterIDForStorage will format the Cluster ID as it will be used for destroying
    23  // GCE PDs. The maximum length is 63 characters, and can end with "-dynamic".
    24  // https://github.com/kubernetes/kubernetes/blob/master/pkg/volume/util/util.go, GenerateVolumeName()
    25  func (o *ClusterUninstaller) formatClusterIDForStorage() string {
    26  	storageName := o.ClusterID + "-dynamic"
    27  	slicedLength := storageNameLength
    28  	if len(storageName) < slicedLength {
    29  		slicedLength = len(storageName)
    30  	}
    31  	return storageName[:slicedLength]
    32  }
    33  
    34  func (o *ClusterUninstaller) storageIDFilter() string {
    35  	return fmt.Sprintf("name : \"%s-*\"", o.formatClusterIDForStorage())
    36  }
    37  
    38  func (o *ClusterUninstaller) storageLabelFilter() string {
    39  	return fmt.Sprintf("labels.%s = \"owned\"", fmt.Sprintf(gcpconsts.ClusterIDLabelFmt, o.formatClusterIDForStorage()))
    40  }
    41  
    42  // storageLabelOrClusterIDFilter will perform the search for resources with the ClusterID, but
    43  // it will also search for specific disk name formats.
    44  func (o *ClusterUninstaller) storageLabelOrClusterIDFilter() string {
    45  	return fmt.Sprintf("%s OR (%s) OR (%s)", o.clusterLabelOrClusterIDFilter(), o.storageIDFilter(), o.storageLabelFilter())
    46  }
    47  
    48  func (o *ClusterUninstaller) listDisks(ctx context.Context) ([]cloudResource, error) {
    49  	return o.listDisksWithFilter(ctx, "items/*/disks(name,zone,type,sizeGb),nextPageToken", o.storageLabelOrClusterIDFilter(), nil)
    50  }
    51  
    52  // listDisksWithFilter lists disks in the project that satisfy the filter criteria.
    53  // The fields parameter specifies which fields should be returned in the result, the filter string contains
    54  // a filter string passed to the API to filter results. The filterFunc is a client-side filtering function
    55  // that determines whether a particular result should be returned or not.
    56  func (o *ClusterUninstaller) listDisksWithFilter(ctx context.Context, fields string, filter string, filterFunc func(*compute.Disk) bool) ([]cloudResource, error) {
    57  	o.Logger.Debug("Listing disks")
    58  	ctx, cancel := context.WithTimeout(ctx, defaultTimeout)
    59  	defer cancel()
    60  	result := []cloudResource{}
    61  	req := o.computeSvc.Disks.AggregatedList(o.ProjectID).Fields(googleapi.Field(fields))
    62  	if len(filter) > 0 {
    63  		req = req.Filter(filter)
    64  	}
    65  	err := req.Pages(ctx, func(list *compute.DiskAggregatedList) error {
    66  		for _, scopedList := range list.Items {
    67  			for _, item := range scopedList.Disks {
    68  				if filterFunc == nil || filterFunc != nil && filterFunc(item) {
    69  					// Regional disks are replicated in multiple zones, so we
    70  					// need to destroy all the replicas
    71  					zoneUrls := item.ReplicaZones
    72  					if len(item.Zone) > 0 {
    73  						zoneUrls = append(zoneUrls, item.Zone)
    74  					}
    75  					for _, url := range zoneUrls {
    76  						zone := o.getZoneName(url)
    77  						o.Logger.Debugf("Found disk: %s in zone %s", item.Name, zone)
    78  						result = append(result, cloudResource{
    79  							key:      fmt.Sprintf("%s/%s", zone, item.Name),
    80  							name:     item.Name,
    81  							typeName: "disk",
    82  							zone:     zone,
    83  							quota: []gcp.QuotaUsage{{
    84  								Metric: &gcp.Metric{
    85  									Service: gcp.ServiceComputeEngineAPI,
    86  									Limit:   getDiskLimit(item.Type),
    87  									Dimensions: map[string]string{
    88  										"region": getRegionFromZone(zone),
    89  									},
    90  								},
    91  								Amount: item.SizeGb,
    92  							}},
    93  						})
    94  					}
    95  				}
    96  			}
    97  		}
    98  		return nil
    99  	})
   100  	if err != nil {
   101  		return nil, errors.Wrapf(err, "failed to fetch disks")
   102  	}
   103  	return result, nil
   104  }
   105  
   106  func (o *ClusterUninstaller) deleteDisk(ctx context.Context, item cloudResource) error {
   107  	o.Logger.Debugf("Deleting disk %s in zone %s", item.name, item.zone)
   108  	ctx, cancel := context.WithTimeout(ctx, defaultTimeout)
   109  	defer cancel()
   110  	op, err := o.computeSvc.Disks.Delete(o.ProjectID, item.zone, item.name).RequestId(o.requestID(item.typeName, item.zone, item.name)).Context(ctx).Do()
   111  	if err != nil && !isNoOp(err) {
   112  		o.resetRequestID(item.typeName, item.zone, item.name)
   113  		return errors.Wrapf(err, "failed to delete disk %s in zone %s", item.name, item.zone)
   114  	}
   115  	if op != nil && op.Status == "DONE" && isErrorStatus(op.HttpErrorStatusCode) {
   116  		o.resetRequestID(item.typeName, item.zone, item.name)
   117  		return errors.Errorf("failed to delete disk %s in zone %s with error: %s", item.name, item.zone, operationErrorMessage(op))
   118  	}
   119  	if (err != nil && isNoOp(err)) || (op != nil && op.Status == "DONE") {
   120  		o.resetRequestID(item.typeName, item.name)
   121  		o.deletePendingItems(item.typeName, []cloudResource{item})
   122  		o.Logger.Infof("Deleted disk %s", item.name)
   123  	}
   124  	return nil
   125  }
   126  
   127  // destroyDisks removes all disk resources that have a name prefixed
   128  // with the cluster's infra ID.
   129  func (o *ClusterUninstaller) destroyDisks(ctx context.Context) error {
   130  	found, err := o.listDisks(ctx)
   131  	if err != nil {
   132  		return err
   133  	}
   134  	items := o.insertPendingItems("disk", found)
   135  	for _, item := range items {
   136  		err := o.deleteDisk(ctx, item)
   137  		if err != nil {
   138  			o.errorTracker.suppressWarning(item.key, err, o.Logger)
   139  		}
   140  	}
   141  	if items = o.getPendingItems("disk"); len(items) > 0 {
   142  		return errors.Errorf("%d items pending", len(items))
   143  	}
   144  	return nil
   145  }