Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
154 changes: 75 additions & 79 deletions pkg/destroy/gcp/address.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,26 +4,54 @@ import (
"context"
"fmt"

"github.com/sirupsen/logrus"
"google.golang.org/api/compute/v1"
"google.golang.org/api/googleapi"

"github.com/openshift/installer/pkg/types/gcp"
)

func (o *ClusterUninstaller) listAddresses(ctx context.Context, scope resourceScope) ([]cloudResource, error) {
return o.listAddressesWithFilter(ctx, "items(name,region,addressType),nextPageToken", o.clusterIDFilter(), nil, scope)
const (
globalAddressResource = "address"
regionalAddressResource = "regionaddress"
)

func (o *ClusterUninstaller) listAddresses(ctx context.Context, typeName string) ([]cloudResource, error) {
return o.listAddressesWithFilter(ctx, typeName, "items(name,region,addressType),nextPageToken", o.clusterIDFilter())
}

func createAddressCloudResources(filterFunc func(address *compute.Address) bool, list *compute.AddressList) []cloudResource {
result := []cloudResource{}
// listAddressesWithFilter lists addresses in the project that satisfy the filter criteria.
// The fields parameter specifies which fields should be returned in the result, the filter string contains
// a filter string passed to the API to filter results.
func (o *ClusterUninstaller) listAddressesWithFilter(ctx context.Context, typeName, fields, filter string) ([]cloudResource, error) {
o.Logger.Debugf("Listing addresses")

ctx, cancel := context.WithTimeout(ctx, defaultTimeout)
defer cancel()

var err error
var list *compute.AddressList
switch typeName {
case globalAddressResource:
list, err = o.computeSvc.GlobalAddresses.List(o.ProjectID).Filter(filter).Fields(googleapi.Field(fields)).Context(ctx).Do()
case regionalAddressResource:
list, err = o.computeSvc.Addresses.List(o.ProjectID, o.Region).Filter(filter).Fields(googleapi.Field(fields)).Context(ctx).Do()
default:
return nil, fmt.Errorf("invalid address type %q", typeName)
}

if err != nil {
return nil, fmt.Errorf("failed to list addresses: %w", err)
}

result := []cloudResource{}
for _, item := range list.Items {
if filterFunc == nil || filterFunc(item) {
logrus.Debugf("Found address: %s", item.Name)
var quota []gcp.QuotaUsage
if item.AddressType == "INTERNAL" {
quota = []gcp.QuotaUsage{{
o.Logger.Debugf("Found address: %s", item.Name)
if item.AddressType == "INTERNAL" {
result = append(result, cloudResource{
key: item.Name,
name: item.Name,
typeName: typeName,
quota: []gcp.QuotaUsage{{
Metric: &gcp.Metric{
Service: gcp.ServiceComputeEngineAPI,
Limit: "internal_addresses",
Expand All @@ -32,77 +60,36 @@ func createAddressCloudResources(filterFunc func(address *compute.Address) bool,
},
},
Amount: 1,
}}
}
result = append(result, cloudResource{
key: item.Name,
name: item.Name,
typeName: "address",
quota: quota,
}},
})
}
}

return result
}

// listAddressesWithFilter lists addresses in the project that satisfy the filter criteria.
// The fields parameter specifies which fields should be returned in the result, the filter string contains
// a filter string passed to the API to filter results. The filterFunc is a client-side filtering function
// that determines whether a particular result should be returned or not.
func (o *ClusterUninstaller) listAddressesWithFilter(ctx context.Context, fields string, filter string, filterFunc func(*compute.Address) bool, scope resourceScope) ([]cloudResource, error) {
o.Logger.Debugf("Listing %s addresses", scope)
ctx, cancel := context.WithTimeout(ctx, defaultTimeout)
defer cancel()
result := []cloudResource{}

if scope == gcpGlobalResource {
req := o.computeSvc.GlobalAddresses.List(o.ProjectID).Fields(googleapi.Field(fields))
if len(filter) > 0 {
req = req.Filter(filter)
}
err := req.Pages(ctx, func(list *compute.AddressList) error {
result = append(result, createAddressCloudResources(filterFunc, list)...)
return nil
})
if err != nil {
return nil, fmt.Errorf("failed to list global addresses: %w", err)
}
return result, nil
}

// Regional addresses
req := o.computeSvc.Addresses.List(o.ProjectID, o.Region).Fields(googleapi.Field(fields))
if len(filter) > 0 {
req = req.Filter(filter)
}
err := req.Pages(ctx, func(list *compute.AddressList) error {
result = append(result, createAddressCloudResources(filterFunc, list)...)
return nil
})
if err != nil {
return nil, fmt.Errorf("failed to list regional addresses: %w", err)
}

return result, nil
}

func (o *ClusterUninstaller) deleteAddress(ctx context.Context, item cloudResource, scope resourceScope) error {
func (o *ClusterUninstaller) deleteAddress(ctx context.Context, item cloudResource) error {
o.Logger.Debugf("Deleting address %s", item.name)
ctx, cancel := context.WithTimeout(ctx, defaultTimeout)
defer cancel()

var op *compute.Operation
var err error
if scope == gcpGlobalResource {
var op *compute.Operation
switch item.typeName {
case globalAddressResource:
op, err = o.computeSvc.GlobalAddresses.Delete(o.ProjectID, item.name).RequestId(o.requestID(item.typeName, item.name)).Context(ctx).Do()
} else {
case regionalAddressResource:
op, err = o.computeSvc.Addresses.Delete(o.ProjectID, o.Region, item.name).RequestId(o.requestID(item.typeName, item.name)).Context(ctx).Do()
default:
return fmt.Errorf("invalid address type %q", item.typeName)
}

if err != nil && !isNoOp(err) {
o.resetRequestID(item.typeName, item.name)
return fmt.Errorf("failed to delete address %s: %w", item.name, err)
}
if op != nil && op.Status == "DONE" && isErrorStatus(op.HttpErrorStatusCode) {
o.resetRequestID(item.typeName, item.name)
return fmt.Errorf("failed to delete address %s with error: %s: %w", item.name, operationErrorMessage(op), err)
return fmt.Errorf("failed to delete address %s with error: %s", item.name, operationErrorMessage(op))
}
if (err != nil && isNoOp(err)) || (op != nil && op.Status == "DONE") {
o.resetRequestID(item.typeName, item.name)
Expand All @@ -115,23 +102,32 @@ func (o *ClusterUninstaller) deleteAddress(ctx context.Context, item cloudResour
// destroyAddresses removes all address resources that have a name prefixed
// with the cluster's infra ID.
func (o *ClusterUninstaller) destroyAddresses(ctx context.Context) error {
for _, scope := range []resourceScope{gcpGlobalResource, gcpRegionalResource} {
found, err := o.listAddresses(ctx, scope)
found, err := o.listAddresses(ctx, globalAddressResource)
if err != nil {
return err
}
items := o.insertPendingItems(globalAddressResource, found)

found, err = o.listAddresses(ctx, regionalAddressResource)
if err != nil {
return err
}
items = append(items, o.insertPendingItems(regionalAddressResource, found)...)

for _, item := range items {
err := o.deleteAddress(ctx, item)
if err != nil {
return fmt.Errorf("failed to list %s addresses: %w", scope, err)
}
items := o.insertPendingItems("address", found)
for _, item := range items {
err := o.deleteAddress(ctx, item, scope)
if err != nil {
o.errorTracker.suppressWarning(item.key, err, o.Logger)
}
}
for _, item := range o.getPendingItems("address") {
if err := o.deleteAddress(ctx, item, scope); err != nil {
return fmt.Errorf("error deleting pending address %s: %w", item.name, err)
}
o.errorTracker.suppressWarning(item.key, err, o.Logger)
}
}

if items = o.getPendingItems(globalAddressResource); len(items) > 0 {
return fmt.Errorf("%d global addresses pending", len(items))
}

if items = o.getPendingItems(regionalAddressResource); len(items) > 0 {
return fmt.Errorf("%d region addresses pending", len(items))
}

return nil
}
Loading