// JsonRequest JSON encodes and sends the object in reqData.ReqValue (if any) to the specified URL. // Optional method arguments are passed using the RequestData object. // Relevant RequestData fields: // ReqHeaders: additional HTTP header values to add to the request. // ExpectedStatus: the allowed HTTP response status values, else an error is returned. // ReqValue: the data object to send. // RespValue: the data object to decode the result into. func (c *Client) JsonRequest(method, url, rfc1123Date string, request *RequestData, response *ResponseData) (err error) { err = nil var body []byte if request.Params != nil { url += "?" + request.Params.Encode() } if request.ReqValue != nil { body, err = json.Marshal(request.ReqValue) if err != nil { err = errors.Newf(err, "failed marshalling the request body") return } } headers, err := createHeaders(request.ReqHeaders, c.credentials, contentTypeJSON, rfc1123Date, c.apiVersion, isMantaRequest(url, c.credentials.UserAuthentication.User)) if err != nil { return err } respBody, respHeader, err := c.sendRequest( method, url, bytes.NewReader(body), len(body), headers, response.ExpectedStatus, c.logger) if err != nil { return } defer respBody.Close() respData, err := ioutil.ReadAll(respBody) if err != nil { err = errors.Newf(err, "failed reading the response body") return } if len(respData) > 0 { if response.RespValue != nil { if _, ok := response.RespValue.(*[]byte); ok { response.RespValue = respData //err = decodeJSON(bytes.NewReader(respData), false, response.RespValue) //if err != nil { // err = errors.Newf(err, "failed unmarshaling/decoding the response body: %s", respData) //} } else { err = json.Unmarshal(respData, response.RespValue) if err != nil { err = decodeJSON(bytes.NewReader(respData), true, response.RespValue) if err != nil { err = errors.Newf(err, "failed unmarshaling/decoding the response body: %s", respData) } } } } } if respHeader != nil { response.RespHeaders = respHeader } return }
func (s *ErrorsSuite) TestErrorCause(c *gc.C) { rootCause := errors.NewResourceNotFoundf(nil, "some value", "") // Construct a new error, based on a resource not found root cause. err := errors.Newf(rootCause, "an error occurred") c.Assert(err.Cause(), gc.Equals, rootCause) // Check the other error attributes. c.Assert(err.Error(), gc.Equals, "an error occurred\ncaused by: Resource Not Found: some value") }
func (s *ErrorsSuite) TestErrorIsType(c *gc.C) { rootCause := errors.NewBadRequestf(nil, "some value", "") // Construct a new error, based on a bad request root cause. err := errors.Newf(rootCause, "an error occurred") // Check that the error is not falsely identified as something it is not. c.Assert(errors.IsNotAuthorized(err), gc.Equals, false) // Check that the error is correctly identified as a not found error. c.Assert(errors.IsBadRequest(err), gc.Equals, true) }
func (c *Client) sendRateLimitedRequest(method, URL string, headers http.Header, reqData []byte, logger *loggo.Logger) (resp *http.Response, err error) { for i := 0; i < c.maxSendAttempts; i++ { var reqReader io.Reader if reqData != nil { reqReader = bytes.NewReader(reqData) } req, err := http.NewRequest(method, URL, reqReader) if err != nil { err = errors.Newf(err, "failed creating the request %s", URL) return nil, err } // Setting req.Close to true to avoid malformed HTTP version "nullHTTP/1.1" error // See http://stackoverflow.com/questions/17714494/golang-http-request-results-in-eof-errors-when-making-multiple-requests-successi req.Close = true for header, values := range headers { for _, value := range values { req.Header.Add(header, value) } } req.ContentLength = int64(len(reqData)) resp, err = c.Do(req) if err != nil { return nil, errors.Newf(err, "failed executing the request %s", URL) } if resp.StatusCode != http.StatusRequestEntityTooLarge || resp.Header.Get("Retry-After") == "" { return resp, nil } resp.Body.Close() retryAfter, err := strconv.ParseFloat(resp.Header.Get("Retry-After"), 64) if err != nil { return nil, errors.Newf(err, "Invalid Retry-After header %s", URL) } if retryAfter == 0 { return nil, errors.Newf(err, "Resource limit exeeded at URL %s", URL) } if logger != nil { logger.Warningf("Too many requests, retrying in %dms.", int(retryAfter*1000)) } time.Sleep(time.Duration(retryAfter) * time.Second) } return nil, errors.Newf(err, "Maximum number of attempts (%d) reached sending request to %s", c.maxSendAttempts, URL) }
// This cancels a job from doing any further work. // Cancellation is asynchronous and "best effort"; there is no guarantee the job will actually stop // See API docs: http://apidocs.joyent.com/manta/api.html#CancelJob func (c *Client) CancelJob(jobId string) error { req := request{ method: client.POST, url: makeURL(apiJobs, jobId, apiJobsLive, apiJobsCancel), expectedStatus: http.StatusAccepted, } if _, err := c.sendRequest(req); err != nil { return errors.Newf(err, "failed to cancel job %s", jobId) } return nil }
// This closes input for a job, and finalize the job. // See API docs: http://apidocs.joyent.com/manta/api.html#EndJobInput func (c *Client) EndJobInputs(jobId string) error { req := request{ method: client.POST, url: makeURL(apiJobs, jobId, apiJobsLive, apiJobsIn, apiJobsEnd), expectedStatus: http.StatusAccepted, } if _, err := c.sendRequest(req); err != nil { return errors.Newf(err, "failed to end inputs for job %s", jobId) } return nil }
// Submits inputs to an already created job. // See API docs: http://apidocs.joyent.com/manta/api.html#AddJobInputs func (c *Client) AddJobInputs(jobId string, jobInputs io.Reader) error { inputData, errI := ioutil.ReadAll(jobInputs) if errI != nil { return errors.Newf(errI, "failed to read inputs for job %s", jobId) } requestHeaders := make(http.Header) requestHeaders.Set("Accept", "*/*") requestHeaders.Set("Content-Type", "text/plain") req := request{ method: client.POST, url: makeURL(apiJobs, jobId, apiJobsLive, apiJobsIn), reqValue: string(inputData), reqHeader: requestHeaders, expectedStatus: http.StatusNoContent, } if _, err := c.sendRequest(req); err != nil { return errors.Newf(err, "failed to add inputs to job %s", jobId) } return nil }
// DeleteMachineTag deletes a single tag from the specified machine. // See API docs: http://apidocs.joyent.com/cloudapi/#DeleteMachineTag func (c *Client) DeleteMachineTag(machineID, tagKey string) error { req := request{ method: client.DELETE, url: makeURL(apiMachines, machineID, apiTags, tagKey), expectedStatus: http.StatusNoContent, } if _, err := c.sendRequest(req); err != nil { return errors.Newf(err, "failed to delete tag with key %s for machine with id %s", tagKey, machineID) } return nil }
// DeleteImage (Beta) Delete the image specified by imageId. Must be image owner to do so. // See API docs: http://apidocs.joyent.com/cloudapi/#DeleteImage func (c *Client) DeleteImage(imageID string) error { req := request{ method: client.DELETE, url: makeURL(apiImages, imageID), expectedStatus: http.StatusNoContent, } if _, err := c.sendRequest(req); err != nil { return errors.Newf(err, "failed to delete image with id: %s", imageID) } return nil }
// DeleteKey deletes the key identified by keyName. // See API docs: http://apidocs.joyent.com/cloudapi/#DeleteKey func (c *Client) DeleteKey(keyName string) error { req := request{ method: client.DELETE, url: makeURL(apiKeys, keyName), expectedStatus: http.StatusNoContent, } if _, err := c.sendRequest(req); err != nil { return errors.Newf(err, "failed to delete key with name: %s", keyName) } return nil }
// DeleteFirewallRule removes the given firewall rule record from all the required account machines. // See API docs: http://apidocs.joyent.com/cloudapi/#DeleteFirewallRule func (c *Client) DeleteFirewallRule(fwRuleID string) error { req := request{ method: client.DELETE, url: makeURL(apiFirewallRules, fwRuleID), expectedStatus: http.StatusNoContent, } if _, err := c.sendRequest(req); err != nil { return errors.Newf(err, "failed to delete firewall rule with id %s", fwRuleID) } return nil }
// DeleteMachineSnapshot deletes the specified snapshot. // See API docs: http://apidocs.joyent.com/cloudapi/#DeleteMachineSnapshot func (c *Client) DeleteMachineSnapshot(machineID, snapshotName string) error { req := request{ method: client.DELETE, url: makeURL(apiMachines, machineID, apiSnapshots, snapshotName), expectedStatus: http.StatusNoContent, } if _, err := c.sendRequest(req); err != nil { return errors.Newf(err, "failed to delete snapshot %s for machine with id %s", snapshotName, machineID) } return nil }
// StartMachineFromSnapshot starts the machine from the specified snapshot. // Machine must be in 'stopped' state. // See API docs: http://apidocs.joyent.com/cloudapi/#StartMachineFromSnapshot func (c *Client) StartMachineFromSnapshot(machineID, snapshotName string) error { req := request{ method: client.POST, url: makeURL(apiMachines, machineID, apiSnapshots, snapshotName), expectedStatus: http.StatusAccepted, } if _, err := c.sendRequest(req); err != nil { return errors.Newf(err, "failed to start machine with id %s from snapshot %s", machineID, snapshotName) } return nil }
// Starts a stopped machine. // See API docs: http://apidocs.joyent.com/cloudapi/#StartMachine func (c *Client) StartMachine(machineId string) error { req := request{ method: client.POST, url: fmt.Sprintf("%s/%s?action=%s", apiMachines, machineId, actionStart), expectedStatus: http.StatusAccepted, } if _, err := c.sendRequest(req); err != nil { return errors.Newf(err, "failed to start machine with id: %s", machineId) } return nil }
// Destroys an instrumentation. // See API docs: http://apidocs.joyent.com/cloudapi/#DeleteInstrumentation func (c *Client) DeleteInstrumentation(instrumentationId string) error { req := request{ method: client.DELETE, url: makeURL(apiAnalytics, apiInstrumentations, instrumentationId), expectedStatus: http.StatusNoContent, } if _, err := c.sendRequest(req); err != nil { return errors.Newf(err, "failed to delete instrumentation with id %s", instrumentationId) } return nil }
// DeleteFabricNetwork deletes an existing fabric network // See API docs: https://apidocs.joyent.com/cloudapi/#DeleteFabricNetwork func (c *Client) DeleteFabricNetwork(vlanID int16, networkID string) error { req := request{ method: client.DELETE, url: makeURL(apiFabricVLANs, strconv.Itoa(int(vlanID)), apiFabricNetworks, networkID), expectedStatus: http.StatusNoContent, } if _, err := c.sendRequest(req); err != nil { return errors.Newf(err, "failed to delete fabric network %s on vlan %d", networkID, vlanID) } return nil }
// Retrieves the specified object from the specified location. // See API docs: http://apidocs.joyent.com/manta/api.html#GetObject func (c *Client) GetObject(path, objectName string) ([]byte, error) { var resp []byte requestHeaders := make(http.Header) requestHeaders.Set("Accept", "*/*") req := request{ method: client.GET, url: makeURL(apiStorage, path, objectName), reqHeader: requestHeaders, resp: &resp, } respData, err := c.sendRequest(req) if err != nil { return nil, errors.Newf(err, "failed to get object %s/%s", path, objectName) } res, ok := respData.RespValue.(*[]byte) if !ok { return nil, errors.Newf(err, "failed to assert downloaded data as type *[]byte for object %s/%s", path, objectName) } return *res, nil }
// RenameMachine renames an existing machine. // See API docs: http://apidocs.joyent.com/cloudapi/#RenameMachine func (c *Client) RenameMachine(machineID, machineName string) error { req := request{ method: client.POST, url: fmt.Sprintf("%s/%s?action=%s&name=%s", apiMachines, machineID, actionRename, machineName), expectedStatus: http.StatusAccepted, } if _, err := c.sendRequest(req); err != nil { return errors.Newf(err, "failed to rename machine with id: %s", machineID) } return nil }
// Deletes the specified object from the specified location. // See API docs: http://apidocs.joyent.com/manta/api.html#DeleteObject func (c *Client) DeleteObject(path, objectName string) error { req := request{ method: client.DELETE, url: makeURL(apiStorage, path, objectName), expectedStatus: http.StatusNoContent, } if _, err := c.sendRequest(req); err != nil { return errors.Newf(err, "failed to delete object %s/%s", path, objectName) } return nil }
// DeleteFabricVLAN delets a given VLAN as specified by ID // See API docs: https://apidocs.joyent.com/cloudapi/#DeleteFabricVLAN func (c *Client) DeleteFabricVLAN(vlanID int16) error { req := request{ method: client.DELETE, url: makeURL(apiFabricVLANs, strconv.Itoa(int(vlanID))), expectedStatus: http.StatusNoContent, } if _, err := c.sendRequest(req); err != nil { return errors.Newf(err, "failed to delete fabric VLAN with id %d", vlanID) } return nil }
// RemoveNIC removes a NIC on a machine belonging to a given account. // *WARNING*: this causes the machine to reboot while removing the NIC. // See API docs: https://apidocs.joyent.com/cloudapi/#RemoveNic func (c *Client) RemoveNIC(machineID, MAC string) error { req := request{ method: client.DELETE, url: makeURL(apiMachines, machineID, apiNICs, MAC), expectedStatus: http.StatusNoContent, } if _, err := c.sendRequest(req); err != nil { return errors.Newf(err, "failed to remove NIC: %s", MAC) } return nil }
// DisableFirewallMachine disables the firewall for the specified machine. // See API docs: http://apidocs.joyent.com/cloudapi/#DisableMachineFirewall func (c *Client) DisableFirewallMachine(machineID string) error { req := request{ method: client.POST, url: fmt.Sprintf("%s/%s?action=%s", apiMachines, machineID, actionDisableFw), expectedStatus: http.StatusAccepted, } if _, err := c.sendRequest(req); err != nil { return errors.Newf(err, "failed to disable firewall on machine with id: %s", machineID) } return nil }
// DeleteAllMachineMetadata deletes all metadata keys from the specified machine. // See API docs: http://apidocs.joyent.com/cloudapi/#DeleteAllMachineMetadata func (c *Client) DeleteAllMachineMetadata(machineID string) error { req := request{ method: client.DELETE, url: makeURL(apiMachines, machineID, apiMetadata), expectedStatus: http.StatusNoContent, } if _, err := c.sendRequest(req); err != nil { return errors.Newf(err, "failed to delete metadata for machine with id %s", machineID) } return nil }
// MachineAudit provides a list of machine's accomplished actions, (sorted from // latest to older one). // See API docs: http://apidocs.joyent.com/cloudapi/#MachineAudit func (c *Client) MachineAudit(machineID string) ([]AuditAction, error) { var resp []AuditAction req := request{ method: client.GET, url: makeURL(apiMachines, machineID, apiAudit), resp: &resp, } if _, err := c.sendRequest(req); err != nil { return nil, errors.Newf(err, "failed to get actions for machine with id %s", machineID) } return resp, nil }
// ListDatacenters provides a list of all datacenters this cloud is aware of. // See API docs: http://apidocs.joyent.com/cloudapi/#ListDatacenters func (c *Client) ListDatacenters() (map[string]interface{}, error) { var resp map[string]interface{} req := request{ method: client.GET, url: apiDatacenters, resp: &resp, } if _, err := c.sendRequest(req); err != nil { return nil, errors.Newf(err, "failed to get list of datcenters") } return resp, nil }
// GetPackage returns the package specified by packageName. NOTE: packageName can // specify either the package name or package ID. // See API docs: http://apidocs.joyent.com/cloudapi/#GetPackage func (c *Client) GetPackage(packageName string) (*Package, error) { var resp Package req := request{ method: client.GET, url: makeURL(apiPackages, packageName), resp: &resp, } if _, err := c.sendRequest(req); err != nil { return nil, errors.Newf(err, "failed to get package with name: %s", packageName) } return &resp, nil }
// GetNetwork retrieves an individual network record. // See API docs: http://apidocs.joyent.com/cloudapi/#GetNetwork func (c *Client) GetNetwork(networkID string) (*Network, error) { var resp Network req := request{ method: client.GET, url: makeURL(apiNetworks, networkID), resp: &resp, } if _, err := c.sendRequest(req); err != nil { return nil, errors.Newf(err, "failed to get network with id %s", networkID) } return &resp, nil }
// ListNetworks lists all the networks which can be used by the given account. // See API docs: http://apidocs.joyent.com/cloudapi/#ListNetworks func (c *Client) ListNetworks() ([]Network, error) { var resp []Network req := request{ method: client.GET, url: apiNetworks, resp: &resp, } if _, err := c.sendRequest(req); err != nil { return nil, errors.Newf(err, "failed to get list of networks") } return resp, nil }
// ListMachineFirewallRules lists all the firewall rules for the specified machine. // See API docs: http://apidocs.joyent.com/cloudapi/#ListMachineFirewallRules func (c *Client) ListMachineFirewallRules(machineID string) ([]FirewallRule, error) { var resp []FirewallRule req := request{ method: client.GET, url: makeURL(apiMachines, machineID, apiFirewallRules), resp: &resp, } if _, err := c.sendRequest(req); err != nil { return nil, errors.Newf(err, "failed to get list of firewall rules for machine with id %s", machineID) } return resp, nil }
// GetMachineMetadata returns the complete set of metadata associated with the // specified machine. // See API docs: http://apidocs.joyent.com/cloudapi/#GetMachineMetadata func (c *Client) GetMachineMetadata(machineID string) (map[string]interface{}, error) { var resp map[string]interface{} req := request{ method: client.GET, url: makeURL(apiMachines, machineID, apiMetadata), resp: &resp, } if _, err := c.sendRequest(req); err != nil { return nil, errors.Newf(err, "failed to get list of metadata for machine with id %s", machineID) } return resp, nil }