github.com/openshift/installer@v1.4.17/pkg/destroy/ibmcloud/cloudobjectstorage.go (about)

     1  package ibmcloud
     2  
     3  import (
     4  	"fmt"
     5  	"net/http"
     6  
     7  	"github.com/IBM/platform-services-go-sdk/resourcecontrollerv2"
     8  	"github.com/pkg/errors"
     9  )
    10  
    11  const (
    12  	cosTypeName = "cos instance"
    13  	// reclamationReclaim will delete the resource, reclaim it.
    14  	reclamationReclaim = "reclaim"
    15  )
    16  
    17  // Resource ID collected via following command using IBM Cloud CLI:
    18  // $ ibmcloud catalog service cloud-object-storage --output json | jq -r '.[].id' .
    19  const cosResourceID = "dff97f5c-bc5e-4455-b470-411c3edbe49c"
    20  
    21  // findCOSInstanceReclamation Checks current reclamations for one that matches the COS instance name.
    22  func (o *ClusterUninstaller) findCOSInstanceReclamation(instance cloudResource) (*resourcecontrollerv2.Reclamation, error) {
    23  	o.Logger.Debugf("Searching for COS reclamations for instance %s", instance.name)
    24  	ctx, cancel := o.contextWithTimeout()
    25  	defer cancel()
    26  
    27  	reclamationOptions := o.controllerSvc.NewListReclamationsOptions()
    28  	resources, _, err := o.controllerSvc.ListReclamationsWithContext(ctx, reclamationOptions)
    29  	if err != nil {
    30  		return nil, errors.Wrapf(err, "Failed listing reclamations for instance %s", instance.name)
    31  	}
    32  
    33  	o.Logger.Debugf("Checking reclamations that match instance %s", instance.name)
    34  	for _, reclamation := range resources.Resources {
    35  		getOptions := o.controllerSvc.NewGetResourceInstanceOptions(*reclamation.ResourceInstanceID)
    36  		cosInstance, _, err := o.controllerSvc.GetResourceInstanceWithContext(ctx, getOptions)
    37  		if err != nil {
    38  			return nil, errors.Wrapf(err, "Failed checking reclamation %s", *reclamation.ResourceInstanceID)
    39  		}
    40  		if *cosInstance.Name == instance.name {
    41  			o.Logger.Debugf("Found COS instance reclamation %s - %s", instance.name, *reclamation.ID)
    42  			return &reclamation, nil
    43  		}
    44  	}
    45  
    46  	return nil, nil
    47  }
    48  
    49  // reclaimCOSInstanceReclamation reclaims (deletes) a reclamation from a deleted COS instance.
    50  func (o *ClusterUninstaller) reclaimCOSInstanceReclamation(reclamationID string) error {
    51  	o.Logger.Debugf("Reclaming COS instance reclamation %s", reclamationID)
    52  	ctx, cancel := o.contextWithTimeout()
    53  	defer cancel()
    54  
    55  	options := o.controllerSvc.NewRunReclamationActionOptions(reclamationID, reclamationReclaim)
    56  	_, response, err := o.controllerSvc.RunReclamationActionWithContext(ctx, options)
    57  	if err != nil {
    58  		// If reclaim attempt failed because the reclamation doesn't exist (404) don't return an error
    59  		if response != nil && response.StatusCode == http.StatusNotFound {
    60  			o.Logger.Debugf("Reclamation not found, it has likely already been reclaimed %s", reclamationID)
    61  			return nil
    62  		}
    63  		return errors.Wrapf(err, "Failed to reclaim COS instance reclamation %s", reclamationID)
    64  	}
    65  
    66  	o.Logger.Infof("Reclaimed %s", reclamationID)
    67  	return nil
    68  }
    69  
    70  // listCOSInstances lists COS service instances.
    71  func (o *ClusterUninstaller) listCOSInstances() (cloudResources, error) {
    72  	o.Logger.Debugf("Listing COS instances")
    73  	ctx, cancel := o.contextWithTimeout()
    74  	defer cancel()
    75  
    76  	resourceGroupID, err := o.ResourceGroupID()
    77  	if err != nil {
    78  		return nil, err
    79  	}
    80  
    81  	options := o.controllerSvc.NewListResourceInstancesOptions()
    82  	options.SetResourceGroupID(resourceGroupID)
    83  	options.SetResourceID(cosResourceID)
    84  	options.SetType("service_instance")
    85  	resources, _, err := o.controllerSvc.ListResourceInstancesWithContext(ctx, options)
    86  	if err != nil {
    87  		return nil, errors.Wrapf(err, "Failed to list COS instances")
    88  	}
    89  
    90  	result := []cloudResource{}
    91  	for _, instance := range resources.Resources {
    92  		// Match the COS instances created by both the installer and the
    93  		// cluster-image-registry-operator.
    94  		if fmt.Sprintf("%s-cos", o.InfraID) == *instance.Name ||
    95  			fmt.Sprintf("%s-image-registry", o.InfraID) == *instance.Name {
    96  			result = append(result, cloudResource{
    97  				key:      *instance.ID,
    98  				name:     *instance.Name,
    99  				status:   *instance.State,
   100  				typeName: cosTypeName,
   101  				id:       *instance.ID,
   102  			})
   103  		}
   104  	}
   105  
   106  	return cloudResources{}.insert(result...), nil
   107  }
   108  
   109  func (o *ClusterUninstaller) deleteCOSInstance(item cloudResource) error {
   110  	o.Logger.Debugf("Deleting COS instance %s", item.name)
   111  	ctx, cancel := o.contextWithTimeout()
   112  	defer cancel()
   113  
   114  	options := o.controllerSvc.NewDeleteResourceInstanceOptions(item.id)
   115  	options.SetRecursive(true)
   116  	details, err := o.controllerSvc.DeleteResourceInstanceWithContext(ctx, options)
   117  	if err != nil {
   118  		// If the deletion attempt failed because the COS instance doesn't exist (404) don't return an error
   119  		if details != nil && details.StatusCode == http.StatusNotFound {
   120  			return nil
   121  		}
   122  		return errors.Wrapf(err, "Failed to delete COS Instance %s", item.name)
   123  	}
   124  
   125  	return nil
   126  }
   127  
   128  // destroyCOSInstances removes the COS service instance resources that have a
   129  // name prefixed with the cluster's infra ID.
   130  func (o *ClusterUninstaller) destroyCOSInstances() error {
   131  	found, err := o.listCOSInstances()
   132  	if err != nil {
   133  		return err
   134  	}
   135  
   136  	items := o.insertPendingItems(cosTypeName, found.list())
   137  
   138  	for _, item := range items {
   139  		if _, ok := found[item.key]; !ok {
   140  			// Check if a COS reclamation was created for the deleted instance
   141  			reclamation, err := o.findCOSInstanceReclamation(item)
   142  			if err != nil {
   143  				return err
   144  			}
   145  			if reclamation != nil {
   146  				err = o.reclaimCOSInstanceReclamation(*reclamation.ID)
   147  				if err != nil {
   148  					return err
   149  				}
   150  				continue
   151  			}
   152  			o.Logger.Debugf("No reclamations found for COS instance %s", item.name)
   153  			// This item has finished deletion.
   154  			o.deletePendingItems(item.typeName, []cloudResource{item})
   155  			o.Logger.Infof("Deleted COS instance %s", item.name)
   156  			continue
   157  		}
   158  		err = o.deleteCOSInstance(item)
   159  		if err != nil {
   160  			o.errorTracker.suppressWarning(item.key, err, o.Logger)
   161  		}
   162  	}
   163  
   164  	if items = o.getPendingItems(cosTypeName); len(items) > 0 {
   165  		return errors.Errorf("%d items pending", len(items))
   166  	}
   167  	return nil
   168  }
   169  
   170  // COSInstanceID returns the ID of the Cloud Object Storage service instance
   171  // created by the installer during installation.
   172  func (o *ClusterUninstaller) COSInstanceID() (string, error) {
   173  	if o.cosInstanceID != "" {
   174  		return o.cosInstanceID, nil
   175  	}
   176  	cosInstances, err := o.listCOSInstances()
   177  	if err != nil {
   178  		return "", err
   179  	}
   180  	instanceList := cosInstances.list()
   181  	if len(instanceList) == 0 {
   182  		return "", errors.Errorf("COS instance not found")
   183  	}
   184  
   185  	// Locate the installer's COS instance by name.
   186  	for _, instance := range instanceList {
   187  		if instance.name == fmt.Sprintf("%s-cos", o.InfraID) {
   188  			o.cosInstanceID = instance.id
   189  			return instance.id, nil
   190  		}
   191  	}
   192  	return "", errors.Errorf("COS instance not found")
   193  }