github.com/dcarley/cf-cli@v6.24.1-0.20170220111324-4225ff346898+incompatible/api/cloudcontroller/ccv2/job.go (about)

     1  package ccv2
     2  
     3  import (
     4  	"encoding/json"
     5  	"fmt"
     6  	"net/url"
     7  	"time"
     8  
     9  	"code.cloudfoundry.org/cli/api/cloudcontroller"
    10  	"code.cloudfoundry.org/cli/api/cloudcontroller/ccv2/internal"
    11  )
    12  
    13  // JobFailedError represents a failed Cloud Controller Job. It wraps the error
    14  // returned back from the Cloud Controller.
    15  type JobFailedError struct {
    16  	JobGUID string
    17  	Message string
    18  }
    19  
    20  func (e JobFailedError) Error() string {
    21  	return fmt.Sprintf("Job (%s) failed: %s", e.JobGUID, e.Message)
    22  }
    23  
    24  // JobTimeoutError is returned from PollJob when the OverallPollingTimeout has
    25  // been reached.
    26  type JobTimeoutError struct {
    27  	JobGUID string
    28  	Timeout time.Duration
    29  }
    30  
    31  func (e JobTimeoutError) Error() string {
    32  	return fmt.Sprintf("Job (%s) polling has reached the maximum timeout of %s seconds", e.JobGUID, e.Timeout)
    33  }
    34  
    35  // JobStatus is the current state of a job.
    36  type JobStatus string
    37  
    38  const (
    39  	// JobStatusFailed is when the job is no longer running due to a failure.
    40  	JobStatusFailed JobStatus = "failed"
    41  
    42  	// JobStatusFinished is when the job is no longer and it was successful.
    43  	JobStatusFinished JobStatus = "finished"
    44  
    45  	// JobStatusQueued is when the job is waiting to be run.
    46  	JobStatusQueued JobStatus = "queued"
    47  
    48  	// JobStatusRunning is when the job is running.
    49  	JobStatusRunning JobStatus = "running"
    50  )
    51  
    52  // Job represents a Cloud Controller Job.
    53  type Job struct {
    54  	Error  string
    55  	GUID   string
    56  	Status JobStatus
    57  }
    58  
    59  // UnmarshalJSON helps unmarshal a Cloud Controller Job response.
    60  func (job *Job) UnmarshalJSON(data []byte) error {
    61  	var ccJob struct {
    62  		Entity struct {
    63  			Error  string `json:"error"`
    64  			GUID   string `json:"guid"`
    65  			Status string `json:"status"`
    66  		} `json:"entity"`
    67  		Metadata internal.Metadata `json:"metadata"`
    68  	}
    69  	if err := json.Unmarshal(data, &ccJob); err != nil {
    70  		return err
    71  	}
    72  
    73  	job.Error = ccJob.Entity.Error
    74  	job.GUID = ccJob.Entity.GUID
    75  	job.Status = JobStatus(ccJob.Entity.Status)
    76  	return nil
    77  }
    78  
    79  // Finished returns true when the job has completed successfully.
    80  func (job Job) Finished() bool {
    81  	return job.Status == JobStatusFinished
    82  }
    83  
    84  // Failed returns true when the job has completed with an error/failure.
    85  func (job Job) Failed() bool {
    86  	return job.Status == JobStatusFailed
    87  }
    88  
    89  // GetJob returns a job for the provided GUID.
    90  func (client *Client) GetJob(jobGUID string) (Job, Warnings, error) {
    91  	request, err := client.newHTTPRequest(requestOptions{
    92  		RequestName: internal.JobRequest,
    93  		URIParams:   map[string]string{"job_guid": jobGUID},
    94  	})
    95  	if err != nil {
    96  		return Job{}, nil, err
    97  	}
    98  
    99  	var job Job
   100  	response := cloudcontroller.Response{
   101  		Result: &job,
   102  	}
   103  
   104  	err = client.connection.Make(request, &response)
   105  	return job, response.Warnings, err
   106  }
   107  
   108  // PollJob will keep polling the given job until the job has terminated, an
   109  // error is encountered, or config.OverallPollingTimeout is reached. In the
   110  // last case, a JobTimeoutError is returned.
   111  func (client *Client) PollJob(job Job) (Warnings, error) {
   112  	originalJobGUID := job.GUID
   113  
   114  	var (
   115  		err         error
   116  		warnings    Warnings
   117  		allWarnings Warnings
   118  	)
   119  
   120  	startTime := time.Now()
   121  	for time.Now().Sub(startTime) < client.jobPollingTimeout {
   122  		job, warnings, err = client.GetJob(job.GUID)
   123  		allWarnings = append(allWarnings, Warnings(warnings)...)
   124  		if err != nil {
   125  			return allWarnings, err
   126  		}
   127  
   128  		if job.Failed() {
   129  			return allWarnings, JobFailedError{
   130  				JobGUID: originalJobGUID,
   131  				Message: job.Error,
   132  			}
   133  		}
   134  
   135  		if job.Finished() {
   136  			return allWarnings, nil
   137  		}
   138  
   139  		time.Sleep(client.jobPollingInterval)
   140  	}
   141  
   142  	return allWarnings, JobTimeoutError{
   143  		JobGUID: originalJobGUID,
   144  		Timeout: client.jobPollingTimeout,
   145  	}
   146  }
   147  
   148  // DeleteOrganization deletes the Organization associated with the provided
   149  // GUID. It will return the Cloud Controller job that is assigned to the
   150  // organization deletion.
   151  func (client *Client) DeleteOrganization(orgGUID string) (Job, Warnings, error) {
   152  	request, err := client.newHTTPRequest(requestOptions{
   153  		RequestName: internal.DeleteOrganizationRequest,
   154  		URIParams:   map[string]string{"organization_guid": orgGUID},
   155  		Query: url.Values{
   156  			"recursive": {"true"},
   157  			"async":     {"true"},
   158  		},
   159  	})
   160  	if err != nil {
   161  		return Job{}, nil, err
   162  	}
   163  
   164  	var job Job
   165  	response := cloudcontroller.Response{
   166  		Result: &job,
   167  	}
   168  
   169  	err = client.connection.Make(request, &response)
   170  	return job, response.Warnings, err
   171  }