github.com/openshift/installer@v1.4.17/pkg/destroy/powervs/job.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 jobTypeName = "job"
    15  
    16  // listJobs lists jobs in the vpc.
    17  func (o *ClusterUninstaller) listJobs() (cloudResources, error) {
    18  	var jobs *models.Jobs
    19  	var job *models.Job
    20  	var err error
    21  
    22  	o.Logger.Debugf("Listing jobs")
    23  
    24  	if o.jobClient == nil {
    25  		result := []cloudResource{}
    26  		return cloudResources{}.insert(result...), nil
    27  	}
    28  
    29  	ctx, cancel := o.contextWithTimeout()
    30  	defer cancel()
    31  
    32  	select {
    33  	case <-ctx.Done():
    34  		o.Logger.Debugf("listJobs: case <-ctx.Done()")
    35  		return nil, o.Context.Err() // we're cancelled, abort
    36  	default:
    37  	}
    38  
    39  	jobs, err = o.jobClient.GetAll()
    40  	if err != nil {
    41  		return nil, fmt.Errorf("failed to list jobs: %w", err)
    42  	}
    43  
    44  	result := []cloudResource{}
    45  	for _, job = range jobs.Jobs {
    46  		// https://github.com/IBM-Cloud/power-go-client/blob/master/power/models/job.go
    47  		if strings.Contains(*job.Operation.ID, o.InfraID) {
    48  			if *job.Status.State == "completed" {
    49  				continue
    50  			}
    51  			o.Logger.Debugf("listJobs: FOUND: %s (%s) (%s)", *job.Operation.ID, *job.ID, *job.Status.State)
    52  			result = append(result, cloudResource{
    53  				key:      *job.Operation.ID,
    54  				name:     *job.Operation.ID,
    55  				status:   *job.Status.State,
    56  				typeName: jobTypeName,
    57  				id:       *job.ID,
    58  			})
    59  		}
    60  	}
    61  
    62  	return cloudResources{}.insert(result...), nil
    63  }
    64  
    65  // DeleteJobResult The different states deleting a job can take.
    66  type DeleteJobResult int
    67  
    68  const (
    69  	// DeleteJobSuccess A job has finished successfully.
    70  	DeleteJobSuccess DeleteJobResult = iota
    71  
    72  	// DeleteJobRunning A job is currently running.
    73  	DeleteJobRunning
    74  
    75  	// DeleteJobError A job has resulted in an error.
    76  	DeleteJobError
    77  )
    78  
    79  func (o *ClusterUninstaller) deleteJob(item cloudResource) (DeleteJobResult, error) {
    80  	var job *models.Job
    81  	var err error
    82  
    83  	ctx, cancel := o.contextWithTimeout()
    84  	defer cancel()
    85  
    86  	select {
    87  	case <-ctx.Done():
    88  		o.Logger.Debugf("deleteJob: case <-ctx.Done()")
    89  		return DeleteJobError, o.Context.Err() // we're cancelled, abort
    90  	default:
    91  	}
    92  
    93  	job, err = o.jobClient.Get(item.id)
    94  	if err != nil {
    95  		o.Logger.Debugf("listJobs: deleteJob: job %q no longer exists", item.name)
    96  		o.deletePendingItems(item.typeName, []cloudResource{item})
    97  		o.Logger.Infof("Deleted Job %q", item.name)
    98  		return DeleteJobSuccess, nil
    99  	}
   100  
   101  	switch *job.Status.State {
   102  	case "completed":
   103  		//		err = o.jobClient.Delete(item.id)
   104  		//		if err != nil {
   105  		//			return DeleteJobError, fmt.Errorf("failed to delete job %s: %w", item.name, err)
   106  		//		}
   107  
   108  		o.deletePendingItems(item.typeName, []cloudResource{item})
   109  		o.Logger.Debugf("Deleting job %q", item.name)
   110  
   111  		return DeleteJobSuccess, nil
   112  
   113  	case "active":
   114  		o.Logger.Debugf("Waiting for job %q to delete (status is %q)", item.name, *job.Status.State)
   115  		return DeleteJobRunning, nil
   116  
   117  	case "failed":
   118  		err = fmt.Errorf("@TODO we cannot query error message inside the job")
   119  		return DeleteJobError, fmt.Errorf("job %v has failed: %w", item.id, err)
   120  
   121  	default:
   122  		o.Logger.Debugf("Default waiting for job %q to delete (status is %q)", item.name, *job.Status.State)
   123  		return DeleteJobRunning, nil
   124  	}
   125  }
   126  
   127  // destroyJobs removes all job resources that have a name prefixed
   128  // with the cluster's infra ID.
   129  func (o *ClusterUninstaller) destroyJobs() error {
   130  	firstPassList, err := o.listJobs()
   131  	if err != nil {
   132  		return err
   133  	}
   134  
   135  	if len(firstPassList.list()) == 0 {
   136  		return nil
   137  	}
   138  
   139  	items := o.insertPendingItems(jobTypeName, firstPassList.list())
   140  
   141  	ctx, cancel := o.contextWithTimeout()
   142  	defer cancel()
   143  
   144  	for _, item := range items {
   145  		select {
   146  		case <-ctx.Done():
   147  			o.Logger.Debugf("destroyJobs: case <-ctx.Done()")
   148  			return o.Context.Err() // we're cancelled, abort
   149  		default:
   150  		}
   151  
   152  		backoff := wait.Backoff{
   153  			Duration: 15 * time.Second,
   154  			Factor:   1.1,
   155  			Cap:      leftInContext(ctx),
   156  			Steps:    math.MaxInt32}
   157  		err = wait.ExponentialBackoffWithContext(ctx, backoff, func(context.Context) (bool, error) {
   158  			result, err2 := o.deleteJob(item)
   159  			switch result {
   160  			case DeleteJobSuccess:
   161  				o.Logger.Debugf("destroyJobs: deleteJob returns DeleteJobSuccess")
   162  				return true, nil
   163  			case DeleteJobRunning:
   164  				o.Logger.Debugf("destroyJobs: deleteJob returns DeleteJobRunning")
   165  				return false, nil
   166  			case DeleteJobError:
   167  				o.Logger.Debugf("destroyJobs: deleteJob returns DeleteJobError: %v", err2)
   168  				return false, err2
   169  			default:
   170  				return false, fmt.Errorf("destroyJobs: deleteJob unknown result enum %v", result)
   171  			}
   172  		})
   173  		if err != nil {
   174  			o.Logger.Fatal("destroyJobs: ExponentialBackoffWithContext (destroy) returns ", err)
   175  		}
   176  	}
   177  
   178  	if items = o.getPendingItems(jobTypeName); len(items) > 0 {
   179  		for _, item := range items {
   180  			o.Logger.Debugf("destroyJobs: found %s in pending items", item.name)
   181  		}
   182  		return fmt.Errorf("destroyJobs: %d undeleted items pending", len(items))
   183  	}
   184  
   185  	backoff := wait.Backoff{
   186  		Duration: 15 * time.Second,
   187  		Factor:   1.1,
   188  		Cap:      leftInContext(ctx),
   189  		Steps:    math.MaxInt32}
   190  	err = wait.ExponentialBackoffWithContext(ctx, backoff, func(context.Context) (bool, error) {
   191  		secondPassList, err2 := o.listJobs()
   192  		if err2 != nil {
   193  			return false, err2
   194  		}
   195  		if len(secondPassList) == 0 {
   196  			// We finally don't see any remaining instances!
   197  			return true, nil
   198  		}
   199  		for _, item := range secondPassList {
   200  			o.Logger.Debugf("destroyJobs: found %s in second pass", item.name)
   201  		}
   202  		return false, nil
   203  	})
   204  	if err != nil {
   205  		o.Logger.Fatal("destroyJobs: ExponentialBackoffWithContext (list) returns ", err)
   206  	}
   207  
   208  	return nil
   209  }