205 lines
6.1 KiB
Go
205 lines
6.1 KiB
Go
package cfclient
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/json"
|
|
"fmt"
|
|
"io"
|
|
"io/ioutil"
|
|
"net/url"
|
|
"time"
|
|
|
|
"github.com/pkg/errors"
|
|
)
|
|
|
|
// TaskListResponse is the JSON response from the API.
|
|
type TaskListResponse struct {
|
|
Pagination Pagination `json:"pagination"`
|
|
Tasks []Task `json:"resources"`
|
|
}
|
|
|
|
// Task is a description of a task element.
|
|
type Task struct {
|
|
GUID string `json:"guid"`
|
|
SequenceID int `json:"sequence_id"`
|
|
Name string `json:"name"`
|
|
Command string `json:"command"`
|
|
State string `json:"state"`
|
|
MemoryInMb int `json:"memory_in_mb"`
|
|
DiskInMb int `json:"disk_in_mb"`
|
|
Result struct {
|
|
FailureReason string `json:"failure_reason"`
|
|
} `json:"result"`
|
|
CreatedAt time.Time `json:"created_at"`
|
|
UpdatedAt time.Time `json:"updated_at"`
|
|
DropletGUID string `json:"droplet_guid"`
|
|
Links struct {
|
|
Self Link `json:"self"`
|
|
App Link `json:"app"`
|
|
Droplet Link `json:"droplet"`
|
|
} `json:"links"`
|
|
}
|
|
|
|
// TaskRequest is a v3 JSON object as described in:
|
|
// http://v3-apidocs.cloudfoundry.org/version/3.0.0/index.html#create-a-task
|
|
type TaskRequest struct {
|
|
Command string `json:"command"`
|
|
Name string `json:"name"`
|
|
MemoryInMegabyte int `json:"memory_in_mb"`
|
|
DiskInMegabyte int `json:"disk_in_mb"`
|
|
DropletGUID string `json:"droplet_guid"`
|
|
}
|
|
|
|
func (c *Client) makeTaskListRequestWithParams(baseUrl string, query url.Values) ([]byte, error) {
|
|
requestUrl := baseUrl + "?" + query.Encode()
|
|
req := c.NewRequest("GET", requestUrl)
|
|
resp, err := c.DoRequest(req)
|
|
if err != nil {
|
|
return nil, errors.Wrap(err, "Error requesting tasks")
|
|
}
|
|
defer resp.Body.Close()
|
|
if resp.StatusCode != 200 {
|
|
return nil, errors.Wrapf(err, "Error requesting tasks: status code not 200, it was %d", resp.StatusCode)
|
|
}
|
|
return ioutil.ReadAll(resp.Body)
|
|
}
|
|
|
|
func parseTaskListRespones(answer []byte) (TaskListResponse, error) {
|
|
var response TaskListResponse
|
|
err := json.Unmarshal(answer, &response)
|
|
if err != nil {
|
|
return response, errors.Wrap(err, "Error unmarshaling response %v")
|
|
}
|
|
return response, nil
|
|
}
|
|
|
|
func (c *Client) handleTasksApiCall(apiUrl string, query url.Values) ([]Task, error) {
|
|
body, err := c.makeTaskListRequestWithParams(apiUrl, query)
|
|
if err != nil {
|
|
return nil, errors.Wrap(err, "Error requesting tasks")
|
|
}
|
|
response, err := parseTaskListRespones(body)
|
|
if err != nil {
|
|
return nil, errors.Wrap(err, "Error reading tasks")
|
|
}
|
|
return response.Tasks, nil
|
|
}
|
|
|
|
// ListTasks returns all tasks the user has access to.
|
|
// See http://v3-apidocs.cloudfoundry.org/version/3.12.0/index.html#list-tasks
|
|
func (c *Client) ListTasks() ([]Task, error) {
|
|
return c.handleTasksApiCall("/v3/tasks", url.Values{})
|
|
}
|
|
|
|
// ListTasksByQuery returns all tasks the user has access to, with query parameters.
|
|
// See http://v3-apidocs.cloudfoundry.org/version/3.12.0/index.html#list-tasks
|
|
func (c *Client) ListTasksByQuery(query url.Values) ([]Task, error) {
|
|
return c.handleTasksApiCall("/v3/tasks", query)
|
|
}
|
|
|
|
// TasksByApp returns task structures which aligned to an app identified by the given guid.
|
|
// See: http://v3-apidocs.cloudfoundry.org/version/3.12.0/index.html#list-tasks-for-an-app
|
|
func (c *Client) TasksByApp(guid string) ([]Task, error) {
|
|
return c.TasksByAppByQuery(guid, url.Values{})
|
|
}
|
|
|
|
// TasksByAppByQuery returns task structures which aligned to an app identified by the given guid
|
|
// and filtered by the given query parameters.
|
|
// See: http://v3-apidocs.cloudfoundry.org/version/3.12.0/index.html#list-tasks-for-an-app
|
|
func (c *Client) TasksByAppByQuery(guid string, query url.Values) ([]Task, error) {
|
|
uri := fmt.Sprintf("/v3/apps/%s/tasks", guid)
|
|
return c.handleTasksApiCall(uri, query)
|
|
}
|
|
|
|
func createReader(tr TaskRequest) (io.Reader, error) {
|
|
rmap := make(map[string]string)
|
|
rmap["command"] = tr.Command
|
|
if tr.Name != "" {
|
|
rmap["name"] = tr.Name
|
|
}
|
|
// setting droplet GUID causing issues
|
|
if tr.MemoryInMegabyte != 0 {
|
|
rmap["memory_in_mb"] = fmt.Sprintf("%d", tr.MemoryInMegabyte)
|
|
}
|
|
if tr.DiskInMegabyte != 0 {
|
|
rmap["disk_in_mb"] = fmt.Sprintf("%d", tr.DiskInMegabyte)
|
|
}
|
|
|
|
bodyReader := bytes.NewBuffer(nil)
|
|
enc := json.NewEncoder(bodyReader)
|
|
if err := enc.Encode(rmap); err != nil {
|
|
return nil, errors.Wrap(err, "Error during encoding task request")
|
|
}
|
|
return bodyReader, nil
|
|
}
|
|
|
|
// CreateTask creates a new task in CF system and returns its structure.
|
|
func (c *Client) CreateTask(tr TaskRequest) (task Task, err error) {
|
|
bodyReader, err := createReader(tr)
|
|
if err != nil {
|
|
return task, err
|
|
}
|
|
|
|
request := fmt.Sprintf("/v3/apps/%s/tasks", tr.DropletGUID)
|
|
req := c.NewRequestWithBody("POST", request, bodyReader)
|
|
|
|
resp, err := c.DoRequest(req)
|
|
if err != nil {
|
|
return task, errors.Wrap(err, "Error creating task")
|
|
}
|
|
defer resp.Body.Close()
|
|
|
|
body, err := ioutil.ReadAll(resp.Body)
|
|
if err != nil {
|
|
return task, errors.Wrap(err, "Error reading task after creation")
|
|
}
|
|
|
|
err = json.Unmarshal(body, &task)
|
|
if err != nil {
|
|
return task, errors.Wrap(err, "Error unmarshaling task")
|
|
}
|
|
return task, err
|
|
}
|
|
|
|
// GetTaskByGuid returns a task structure by requesting it with the tasks GUID.
|
|
func (c *Client) GetTaskByGuid(guid string) (task Task, err error) {
|
|
request := fmt.Sprintf("/v3/tasks/%s", guid)
|
|
req := c.NewRequest("GET", request)
|
|
|
|
resp, err := c.DoRequest(req)
|
|
if err != nil {
|
|
return task, errors.Wrap(err, "Error requesting task")
|
|
}
|
|
defer resp.Body.Close()
|
|
|
|
body, err := ioutil.ReadAll(resp.Body)
|
|
if err != nil {
|
|
return task, errors.Wrap(err, "Error reading task")
|
|
}
|
|
|
|
err = json.Unmarshal(body, &task)
|
|
if err != nil {
|
|
return task, errors.Wrap(err, "Error unmarshaling task")
|
|
}
|
|
return task, err
|
|
}
|
|
|
|
func (c *Client) TaskByGuid(guid string) (task Task, err error) {
|
|
return c.GetTaskByGuid(guid)
|
|
}
|
|
|
|
// TerminateTask cancels a task identified by its GUID.
|
|
func (c *Client) TerminateTask(guid string) error {
|
|
req := c.NewRequest("PUT", fmt.Sprintf("/v3/tasks/%s/cancel", guid))
|
|
resp, err := c.DoRequest(req)
|
|
if err != nil {
|
|
return errors.Wrap(err, "Error terminating task")
|
|
}
|
|
defer resp.Body.Close()
|
|
|
|
if resp.StatusCode != 202 {
|
|
return errors.Wrapf(err, "Failed terminating task, response status code %d", resp.StatusCode)
|
|
}
|
|
return nil
|
|
}
|