github.com/openshift/installer@v1.4.17/pkg/destroy/powervs/image.go (about)

     1  package powervs
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"math"
     7  	"strings"
     8  	"time"
     9  
    10  	"github.com/IBM-Cloud/power-go-client/power/models"
    11  	"k8s.io/apimachinery/pkg/util/wait"
    12  )
    13  
    14  const imageTypeName = "image"
    15  
    16  // listImages lists images in the vpc.
    17  func (o *ClusterUninstaller) listImages() (cloudResources, error) {
    18  	o.Logger.Debugf("Listing images")
    19  
    20  	if o.imageClient == nil {
    21  		o.Logger.Infof("Skipping deleting images because no service instance was found")
    22  		result := []cloudResource{}
    23  		return cloudResources{}.insert(result...), nil
    24  	}
    25  
    26  	ctx, cancel := o.contextWithTimeout()
    27  	defer cancel()
    28  
    29  	select {
    30  	case <-ctx.Done():
    31  		o.Logger.Debugf("listImages: case <-ctx.Done()")
    32  		return nil, o.Context.Err() // we're cancelled, abort
    33  	default:
    34  	}
    35  
    36  	images, err := o.imageClient.GetAll()
    37  	if err != nil {
    38  		return nil, fmt.Errorf("failed to list images: %w", err)
    39  	}
    40  
    41  	var foundOne = false
    42  
    43  	result := []cloudResource{}
    44  	for _, image := range images.Images {
    45  		if strings.Contains(*image.Name, o.InfraID) {
    46  			foundOne = true
    47  			o.Logger.Debugf("listImages: FOUND: %s, %s, %s", *image.ImageID, *image.Name, *image.State)
    48  			result = append(result, cloudResource{
    49  				key:      *image.ImageID,
    50  				name:     *image.Name,
    51  				status:   *image.State,
    52  				typeName: imageTypeName,
    53  				id:       *image.ImageID,
    54  			})
    55  		}
    56  	}
    57  	if !foundOne {
    58  		o.Logger.Debugf("listImages: NO matching image against: %s", o.InfraID)
    59  		for _, image := range images.Images {
    60  			o.Logger.Debugf("listImages: image: %s", *image.Name)
    61  		}
    62  	}
    63  
    64  	return cloudResources{}.insert(result...), nil
    65  }
    66  
    67  func (o *ClusterUninstaller) deleteImage(item cloudResource) error {
    68  	var img *models.Image
    69  	var err error
    70  
    71  	ctx, cancel := o.contextWithTimeout()
    72  	defer cancel()
    73  
    74  	select {
    75  	case <-ctx.Done():
    76  		o.Logger.Debugf("deleteImage: case <-ctx.Done()")
    77  		return o.Context.Err() // we're cancelled, abort
    78  	default:
    79  	}
    80  
    81  	img, err = o.imageClient.Get(item.id)
    82  	if err != nil {
    83  		o.Logger.Debugf("listImages: deleteImage: image %q no longer exists", item.name)
    84  		o.deletePendingItems(item.typeName, []cloudResource{item})
    85  		o.Logger.Infof("Deleted Image %q", item.name)
    86  		return nil
    87  	}
    88  
    89  	if !strings.EqualFold(img.State, "active") {
    90  		o.Logger.Debugf("Waiting for image %q to delete", item.name)
    91  		return nil
    92  	}
    93  
    94  	err = o.imageClient.Delete(item.id)
    95  	if err != nil {
    96  		return fmt.Errorf("failed to delete image %s: %w", item.name, err)
    97  	}
    98  
    99  	o.Logger.Infof("Deleted Image %q", item.name)
   100  	o.deletePendingItems(item.typeName, []cloudResource{item})
   101  
   102  	return nil
   103  }
   104  
   105  // destroyImages removes all image resources that have a name prefixed
   106  // with the cluster's infra ID.
   107  func (o *ClusterUninstaller) destroyImages() error {
   108  	firstPassList, err := o.listImages()
   109  	if err != nil {
   110  		return err
   111  	}
   112  
   113  	if len(firstPassList.list()) == 0 {
   114  		return nil
   115  	}
   116  
   117  	items := o.insertPendingItems(imageTypeName, firstPassList.list())
   118  	for _, item := range items {
   119  		o.Logger.Debugf("destroyImages: firstPassList: %v / %v", item.name, item.id)
   120  	}
   121  
   122  	ctx, cancel := o.contextWithTimeout()
   123  	defer cancel()
   124  
   125  	for _, item := range items {
   126  		select {
   127  		case <-ctx.Done():
   128  			o.Logger.Debugf("destroyImages: case <-ctx.Done()")
   129  			return o.Context.Err() // we're cancelled, abort
   130  		default:
   131  		}
   132  
   133  		backoff := wait.Backoff{
   134  			Duration: 15 * time.Second,
   135  			Factor:   1.1,
   136  			Cap:      leftInContext(ctx),
   137  			Steps:    math.MaxInt32}
   138  		err = wait.ExponentialBackoffWithContext(ctx, backoff, func(context.Context) (bool, error) {
   139  			err2 := o.deleteImage(item)
   140  			if err2 == nil {
   141  				return true, err2
   142  			}
   143  			o.errorTracker.suppressWarning(item.key, err2, o.Logger)
   144  			return false, err2
   145  		})
   146  		if err != nil {
   147  			o.Logger.Fatal("destroyImages: ExponentialBackoffWithContext (destroy) returns ", err)
   148  		}
   149  	}
   150  
   151  	if items = o.getPendingItems(imageTypeName); len(items) > 0 {
   152  		for _, item := range items {
   153  			o.Logger.Debugf("destroyImages: found %s in pending items", item.name)
   154  		}
   155  		return fmt.Errorf("destroyImages: %d undeleted items pending", len(items))
   156  	}
   157  
   158  	backoff := wait.Backoff{
   159  		Duration: 15 * time.Second,
   160  		Factor:   1.1,
   161  		Cap:      leftInContext(ctx),
   162  		Steps:    math.MaxInt32}
   163  	err = wait.ExponentialBackoffWithContext(ctx, backoff, func(context.Context) (bool, error) {
   164  		secondPassList, err2 := o.listImages()
   165  		if err2 != nil {
   166  			return false, err2
   167  		}
   168  		if len(secondPassList) == 0 {
   169  			// We finally don't see any remaining instances!
   170  			return true, nil
   171  		}
   172  		for _, item := range secondPassList {
   173  			o.Logger.Debugf("destroyImages: found %s in second pass", item.name)
   174  		}
   175  		return false, nil
   176  	})
   177  	if err != nil {
   178  		o.Logger.Fatal("destroyImages: ExponentialBackoffWithContext (list) returns ", err)
   179  	}
   180  
   181  	return nil
   182  }