github.com/mook-as/cf-cli@v7.0.0-beta.28.0.20200120190804-b91c115fae48+incompatible/api/cloudcontroller/ccv3/job.go (about)

     1  package ccv3
     2  
     3  import (
     4  	"time"
     5  
     6  	"code.cloudfoundry.org/cli/api/cloudcontroller"
     7  	"code.cloudfoundry.org/cli/api/cloudcontroller/ccerror"
     8  	"code.cloudfoundry.org/cli/api/cloudcontroller/ccv3/constant"
     9  )
    10  
    11  // Job represents a Cloud Controller Job.
    12  type Job struct {
    13  	// RawErrors is a list of errors that occurred while processing the job.
    14  	RawErrors []JobErrorDetails `json:"errors"`
    15  	// GUID is a unique identifier for the job.
    16  	GUID string `json:"guid"`
    17  	// State is the state of the job.
    18  	State constant.JobState `json:"state"`
    19  	// Warnings are the warnings emitted by the job during its processing.
    20  	Warnings []jobWarning `json:"warnings"`
    21  }
    22  
    23  // Errors returns back a list of
    24  func (job Job) Errors() []error {
    25  	var errs []error
    26  	for _, errDetails := range job.RawErrors {
    27  		switch errDetails.Code {
    28  		case constant.JobErrorCodeBuildpackAlreadyExistsForStack:
    29  			errs = append(errs, ccerror.BuildpackAlreadyExistsForStackError{Message: errDetails.Detail})
    30  		case constant.JobErrorCodeBuildpackInvalid:
    31  			errs = append(errs, ccerror.BuildpackInvalidError{Message: errDetails.Detail})
    32  		case constant.JobErrorCodeBuildpackStacksDontMatch:
    33  			errs = append(errs, ccerror.BuildpackStacksDontMatchError{Message: errDetails.Detail})
    34  		case constant.JobErrorCodeBuildpackStackDoesNotExist:
    35  			errs = append(errs, ccerror.BuildpackStackDoesNotExistError{Message: errDetails.Detail})
    36  		case constant.JobErrorCodeBuildpackZipInvalid:
    37  			errs = append(errs, ccerror.BuildpackZipInvalidError{Message: errDetails.Detail})
    38  		default:
    39  			errs = append(errs, ccerror.V3JobFailedError{
    40  				JobGUID: job.GUID,
    41  				Code:    errDetails.Code,
    42  				Detail:  errDetails.Detail,
    43  				Title:   errDetails.Title,
    44  			})
    45  		}
    46  	}
    47  	return errs
    48  }
    49  
    50  // HasFailed returns true when the job has completed with an error/failure.
    51  func (job Job) HasFailed() bool {
    52  	return job.State == constant.JobFailed
    53  }
    54  
    55  // IsComplete returns true when the job has completed successfully.
    56  func (job Job) IsComplete() bool {
    57  	return job.State == constant.JobComplete
    58  }
    59  
    60  type jobWarning struct {
    61  	Detail string `json:"detail"`
    62  }
    63  
    64  // JobErrorDetails provides information regarding a job's error.
    65  type JobErrorDetails struct {
    66  	// Code is a numeric code for this error.
    67  	Code constant.JobErrorCode `json:"code"`
    68  	// Detail is a verbose description of the error.
    69  	Detail string `json:"detail"`
    70  	// Title is a short description of the error.
    71  	Title string `json:"title"`
    72  }
    73  
    74  // GetJob returns a job for the provided GUID.
    75  func (client *Client) GetJob(jobURL JobURL) (Job, Warnings, error) {
    76  	request, err := client.newHTTPRequest(requestOptions{URL: string(jobURL)})
    77  	if err != nil {
    78  		return Job{}, nil, err
    79  	}
    80  
    81  	var job Job
    82  	response := cloudcontroller.Response{
    83  		DecodeJSONResponseInto: &job,
    84  	}
    85  
    86  	err = client.connection.Make(request, &response)
    87  	warnings := response.Warnings
    88  
    89  	for _, jobWarning := range job.Warnings {
    90  		warnings = append(warnings, jobWarning.Detail)
    91  	}
    92  
    93  	return job, warnings, err
    94  }
    95  
    96  // PollJob will keep polling the given job until the job has terminated, an
    97  // error is encountered, or config.OverallPollingTimeout is reached. In the
    98  // last case, a JobTimeoutError is returned.
    99  func (client *Client) PollJob(jobURL JobURL) (Warnings, error) {
   100  	var (
   101  		err         error
   102  		warnings    Warnings
   103  		allWarnings Warnings
   104  		job         Job
   105  	)
   106  
   107  	startTime := client.clock.Now()
   108  	for client.clock.Now().Sub(startTime) < client.jobPollingTimeout {
   109  		job, warnings, err = client.GetJob(jobURL)
   110  		allWarnings = append(allWarnings, warnings...)
   111  		if err != nil {
   112  			return allWarnings, err
   113  		}
   114  
   115  		if job.HasFailed() {
   116  			firstError := job.Errors()[0]
   117  			return allWarnings, firstError
   118  		}
   119  
   120  		if job.IsComplete() {
   121  			return allWarnings, nil
   122  		}
   123  
   124  		time.Sleep(client.jobPollingInterval)
   125  	}
   126  
   127  	return allWarnings, ccerror.JobTimeoutError{
   128  		JobGUID: job.GUID,
   129  		Timeout: client.jobPollingTimeout,
   130  	}
   131  }