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