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 }