backend/manta: Update manta dependencies

Internally, triton-go has changed how it handles errors. We can now get rid of
checking strings for errors, and we have introduced an errors library that
wraps some of the major errors we encounter and test for
This commit is contained in:
stack72 2018-01-16 19:41:10 +02:00 committed by Martin Atkins
parent 0e93e0c2b0
commit e9476c6765
27 changed files with 634 additions and 371 deletions

View File

@ -12,6 +12,7 @@ import (
"github.com/hashicorp/terraform/state"
"github.com/hashicorp/terraform/state/remote"
"github.com/hashicorp/terraform/terraform"
tritonErrors "github.com/joyent/triton-go/errors"
"github.com/joyent/triton-go/storage"
)
@ -22,7 +23,7 @@ func (b *Backend) States() ([]string, error) {
DirectoryName: path.Join(mantaDefaultRootStore, b.path),
})
if err != nil {
if strings.Contains(err.Error(), "ResourceNotFound") {
if tritonErrors.IsResourceNotFound(err) {
return result, nil
}
return nil, err

View File

@ -9,11 +9,10 @@ import (
"log"
"path"
"strings"
uuid "github.com/hashicorp/go-uuid"
"github.com/hashicorp/terraform/state"
"github.com/hashicorp/terraform/state/remote"
tritonErrors "github.com/joyent/triton-go/errors"
"github.com/joyent/triton-go/storage"
)
@ -34,7 +33,7 @@ func (c *RemoteClient) Get() (*remote.Payload, error) {
ObjectPath: path.Join(mantaDefaultRootStore, c.directoryName, c.keyName),
})
if err != nil {
if strings.Contains(err.Error(), "ResourceNotFound") {
if tritonErrors.IsResourceNotFound(err) {
return nil, nil
}
return nil, err
@ -107,7 +106,7 @@ func (c *RemoteClient) Lock(info *state.LockInfo) (string, error) {
lockErr := &state.LockError{}
lockInfo, err := c.getLockInfo()
if err != nil {
if !strings.Contains(err.Error(), "ResourceNotFound") {
if tritonErrors.IsResourceNotFound(err) {
lockErr.Err = fmt.Errorf("failed to retrieve lock info: %s", err)
return "", lockErr
}

View File

@ -1,5 +1,11 @@
## Unreleased
- Add support for managing columes in Triton [#100]
- identity/policies: Add support for managing policies in Triton [#86]
- addition of triton-go errors package to expose unwraping of internal errors
- Migration from hashicorp/errwrap to pkg/errors
- Using path.Join() for URL structures rather than fmt.Sprintf()
## 0.5.2 (December 28)
- Standardise the API SSH Signers input casing and naming

View File

@ -12,9 +12,8 @@ tools:: ## Download and install all dev/code tools
go test -i $(TEST) || exit 1
test:: ## Run unit tests
@echo "==> Running unit tests"
@echo $(TEST) | \
xargs -t go test -v $(TESTARGS) -timeout=30s -parallel=1 | grep -Ev 'TRITON_TEST|TestAcc'
@echo "==> Running unit test with coverage"
@./scripts/go-test-with-coverage.sh
testacc:: ## Run acceptance tests
@echo "==> Running acceptance tests"

View File

@ -7,17 +7,11 @@
packages = ["."]
revision = "d5467c17e7afe8d8f08f556c6c811a50c3feb28d"
[[projects]]
name = "github.com/davecgh/go-spew"
packages = ["spew"]
revision = "346938d642f2ec3594ed81d874461961cd0faa76"
version = "v1.1.0"
[[projects]]
branch = "master"
name = "github.com/hashicorp/errwrap"
name = "github.com/pkg/errors"
packages = ["."]
revision = "7554cd9344cec97297fa6649b055a8c98c2a1e55"
revision = "e881fd58d78e04cf6d0de1217f8707c8cc2249bc"
[[projects]]
branch = "master"
@ -29,11 +23,11 @@
branch = "master"
name = "golang.org/x/crypto"
packages = ["curve25519","ed25519","ed25519/internal/edwards25519","ssh","ssh/agent"]
revision = "bd6f299fb381e4c3393d1c4b1f0b94f5e77650c8"
revision = "0fcca4842a8d74bfddc2c96a073bd2a4d2a7a2e8"
[solve-meta]
analyzer-name = "dep"
analyzer-version = 1
inputs-digest = "28853a8970ee33112a9e7998b18e658bed04d177537ec69db678189f0b8a9a7d"
inputs-digest = "f7efd974ae38e2ee077c4d2698df74128a04797460b5f9c833853ddfaa86a0a0"
solver-name = "gps-cdcl"
solver-version = 1

View File

@ -25,14 +25,6 @@
branch = "master"
name = "github.com/abdullin/seq"
[[constraint]]
name = "github.com/davecgh/go-spew"
version = "1.1.0"
[[constraint]]
branch = "master"
name = "github.com/hashicorp/errwrap"
[[constraint]]
branch = "master"
name = "github.com/sean-/seed"
@ -40,3 +32,7 @@
[[constraint]]
branch = "master"
name = "golang.org/x/crypto"
[[constraint]]
branch = "master"
name = "github.com/pkg/errors"

View File

@ -1,3 +1,11 @@
//
// Copyright (c) 2018, Joyent, Inc. All rights reserved.
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
//
package authentication
// DON'T USE THIS OUTSIDE TESTING ~ This key was only created to use for

View File

@ -1,3 +1,11 @@
//
// Copyright (c) 2018, Joyent, Inc. All rights reserved.
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
//
package authentication
import (
@ -6,7 +14,7 @@ import (
"fmt"
"math/big"
"github.com/hashicorp/errwrap"
"github.com/pkg/errors"
"golang.org/x/crypto/ssh"
)
@ -44,7 +52,7 @@ func newECDSASignature(signatureBlob []byte) (*ecdsaSignature, error) {
}
if err := ssh.Unmarshal(signatureBlob, &ecSig); err != nil {
return nil, errwrap.Wrapf("Error unmarshaling signature: {{err}}", err)
return nil, errors.Wrap(err, "unable to unmarshall signature")
}
rValue := ecSig.R.Bytes()

View File

@ -1,3 +1,11 @@
//
// Copyright (c) 2018, Joyent, Inc. All rights reserved.
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
//
package authentication
import (
@ -7,12 +15,11 @@ import (
"crypto/x509"
"encoding/base64"
"encoding/pem"
"errors"
"fmt"
"path"
"strings"
"github.com/hashicorp/errwrap"
"github.com/pkg/errors"
"golang.org/x/crypto/ssh"
)
@ -44,12 +51,12 @@ func NewPrivateKeySigner(input PrivateKeySignerInput) (*PrivateKeySigner, error)
rsakey, err := x509.ParsePKCS1PrivateKey(block.Bytes)
if err != nil {
return nil, errwrap.Wrapf("Error parsing private key: {{err}}", err)
return nil, errors.Wrap(err, "unable to parse private key")
}
sshPublicKey, err := ssh.NewPublicKey(rsakey.Public())
if err != nil {
return nil, errwrap.Wrapf("Error parsing SSH key from private key: {{err}}", err)
return nil, errors.Wrap(err, "unable to parse SSH key from private key")
}
matchKeyFingerprint := formatPublicKeyFingerprint(sshPublicKey, false)
@ -89,7 +96,7 @@ func (s *PrivateKeySigner) Sign(dateHeader string) (string, error) {
signed, err := rsa.SignPKCS1v15(rand.Reader, s.privateKey, s.hashFunc, digest)
if err != nil {
return "", errwrap.Wrapf("Error signing date header: {{err}}", err)
return "", errors.Wrap(err, "unable to sign date header")
}
signedBase64 := base64.StdEncoding.EncodeToString(signed)
@ -110,7 +117,7 @@ func (s *PrivateKeySigner) SignRaw(toSign string) (string, string, error) {
signed, err := rsa.SignPKCS1v15(rand.Reader, s.privateKey, s.hashFunc, digest)
if err != nil {
return "", "", errwrap.Wrapf("Error signing date header: {{err}}", err)
return "", "", errors.Wrap(err, "unable to sign date header")
}
signedBase64 := base64.StdEncoding.EncodeToString(signed)
return signedBase64, "rsa-sha1", nil

View File

@ -1,3 +1,11 @@
//
// Copyright (c) 2018, Joyent, Inc. All rights reserved.
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
//
package authentication
import (

View File

@ -1,8 +1,16 @@
//
// Copyright (c) 2018, Joyent, Inc. All rights reserved.
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
//
package authentication
import (
"regexp"
"fmt"
"regexp"
)
type httpAuthSignature interface {

View File

@ -1,3 +1,11 @@
//
// Copyright (c) 2018, Joyent, Inc. All rights reserved.
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
//
package authentication
const authorizationHeaderFormat = `Signature keyId="%s",algorithm="%s",headers="%s",signature="%s"`

View File

@ -1,23 +1,30 @@
//
// Copyright (c) 2018, Joyent, Inc. All rights reserved.
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
//
package authentication
import (
"crypto/md5"
"crypto/sha256"
"encoding/base64"
"errors"
"fmt"
"net"
"os"
"path"
"strings"
"github.com/hashicorp/errwrap"
pkgerrors "github.com/pkg/errors"
"golang.org/x/crypto/ssh"
"golang.org/x/crypto/ssh/agent"
)
var (
ErrUnsetEnvVar = errors.New("SSH_AUTH_SOCK is not set")
ErrUnsetEnvVar = pkgerrors.New("environment variable SSH_AUTH_SOCK not set")
)
type SSHAgentSigner struct {
@ -46,7 +53,7 @@ func NewSSHAgentSigner(input SSHAgentSignerInput) (*SSHAgentSigner, error) {
conn, err := net.Dial("unix", sshAgentAddress)
if err != nil {
return nil, errwrap.Wrapf("Error dialing SSH agent: {{err}}", err)
return nil, pkgerrors.Wrap(err, "unable to dial SSH agent")
}
ag := agent.NewClient(conn)
@ -82,7 +89,7 @@ func NewSSHAgentSigner(input SSHAgentSignerInput) (*SSHAgentSigner, error) {
func (s *SSHAgentSigner) MatchKey() (ssh.PublicKey, error) {
keys, err := s.agent.List()
if err != nil {
return nil, errwrap.Wrapf("Error listing keys in SSH Agent: %s", err)
return nil, pkgerrors.Wrap(err, "unable to list keys in SSH Agent")
}
keyFingerprintStripped := strings.TrimPrefix(s.keyFingerprint, "MD5:")
@ -116,12 +123,12 @@ func (s *SSHAgentSigner) Sign(dateHeader string) (string, error) {
signature, err := s.agent.Sign(s.key, []byte(fmt.Sprintf("%s: %s", headerName, dateHeader)))
if err != nil {
return "", errwrap.Wrapf("Error signing date header: {{err}}", err)
return "", pkgerrors.Wrap(err, "unable to sign date header")
}
keyFormat, err := keyFormatToKeyType(signature.Format)
if err != nil {
return "", errwrap.Wrapf("Error reading signature: {{err}}", err)
return "", pkgerrors.Wrap(err, "unable to format signature")
}
var authSignature httpAuthSignature
@ -129,12 +136,12 @@ func (s *SSHAgentSigner) Sign(dateHeader string) (string, error) {
case "rsa":
authSignature, err = newRSASignature(signature.Blob)
if err != nil {
return "", errwrap.Wrapf("Error reading signature: {{err}}", err)
return "", pkgerrors.Wrap(err, "unable to read RSA signature")
}
case "ecdsa":
authSignature, err = newECDSASignature(signature.Blob)
if err != nil {
return "", errwrap.Wrapf("Error reading signature: {{err}}", err)
return "", pkgerrors.Wrap(err, "unable to read ECDSA signature")
}
default:
return "", fmt.Errorf("Unsupported algorithm from SSH agent: %s", signature.Format)
@ -147,12 +154,12 @@ func (s *SSHAgentSigner) Sign(dateHeader string) (string, error) {
func (s *SSHAgentSigner) SignRaw(toSign string) (string, string, error) {
signature, err := s.agent.Sign(s.key, []byte(toSign))
if err != nil {
return "", "", errwrap.Wrapf("Error signing string: {{err}}", err)
return "", "", pkgerrors.Wrap(err, "unable to sign string")
}
keyFormat, err := keyFormatToKeyType(signature.Format)
if err != nil {
return "", "", errwrap.Wrapf("Error reading signature: {{err}}", err)
return "", "", pkgerrors.Wrap(err, "unable to format key")
}
var authSignature httpAuthSignature
@ -160,12 +167,12 @@ func (s *SSHAgentSigner) SignRaw(toSign string) (string, string, error) {
case "rsa":
authSignature, err = newRSASignature(signature.Blob)
if err != nil {
return "", "", errwrap.Wrapf("Error reading signature: {{err}}", err)
return "", "", pkgerrors.Wrap(err, "unable to read RSA signature")
}
case "ecdsa":
authSignature, err = newECDSASignature(signature.Blob)
if err != nil {
return "", "", errwrap.Wrapf("Error reading signature: {{err}}", err)
return "", "", pkgerrors.Wrap(err, "unable to read ECDSA signature")
}
default:
return "", "", fmt.Errorf("Unsupported algorithm from SSH agent: %s", signature.Format)

View File

@ -1,3 +1,11 @@
//
// Copyright (c) 2018, Joyent, Inc. All rights reserved.
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
//
package authentication
// TestSigner represents an authentication key signer which we can use for

View File

@ -1,3 +1,11 @@
//
// Copyright (c) 2018, Joyent, Inc. All rights reserved.
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
//
package authentication
import (

View File

@ -1,3 +1,11 @@
//
// Copyright (c) 2018, Joyent, Inc. All rights reserved.
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
//
package client
import (
@ -5,7 +13,6 @@ import (
"context"
"crypto/tls"
"encoding/json"
"errors"
"fmt"
"io"
"net"
@ -14,19 +21,21 @@ import (
"os"
"time"
"github.com/hashicorp/errwrap"
"github.com/joyent/triton-go"
"github.com/joyent/triton-go/authentication"
"github.com/joyent/triton-go/errors"
pkgerrors "github.com/pkg/errors"
)
const nilContext = "nil context"
var (
ErrDefaultAuth = errors.New("default SSH agent authentication requires SDC_KEY_ID / TRITON_KEY_ID and SSH_AUTH_SOCK")
ErrAccountName = errors.New("missing account name for Triton/Manta")
ErrMissingURL = errors.New("missing Triton and/or Manta URL")
ErrDefaultAuth = pkgerrors.New("default SSH agent authentication requires SDC_KEY_ID / TRITON_KEY_ID and SSH_AUTH_SOCK")
ErrAccountName = pkgerrors.New("missing account name")
ErrMissingURL = pkgerrors.New("missing API URL")
BadTritonURL = "invalid format of triton URL"
BadMantaURL = "invalid format of manta URL"
InvalidTritonURL = "invalid format of Triton URL"
InvalidMantaURL = "invalid format of Manta URL"
)
// Client represents a connection to the Triton Compute or Object Storage APIs.
@ -55,12 +64,12 @@ func New(tritonURL string, mantaURL string, accountName string, signers ...authe
cloudURL, err := url.Parse(tritonURL)
if err != nil {
return nil, errwrap.Wrapf(BadTritonURL+": {{err}}", err)
return nil, pkgerrors.Wrapf(err, InvalidTritonURL)
}
storageURL, err := url.Parse(mantaURL)
if err != nil {
return nil, errwrap.Wrapf(BadMantaURL+": {{err}}", err)
return nil, pkgerrors.Wrapf(err, InvalidMantaURL)
}
authorizers := make([]authentication.Signer, 0)
@ -124,7 +133,7 @@ func (c *Client) DefaultAuth() error {
}
defaultSigner, err := authentication.NewSSHAgentSigner(input)
if err != nil {
return errwrap.Wrapf("problem initializing NewSSHAgentSigner: {{err}}", err)
return pkgerrors.Wrapf(err, "unable to initialize NewSSHAgentSigner")
}
c.Authorizers = append(c.Authorizers, defaultSigner)
}
@ -164,19 +173,20 @@ func doNotFollowRedirects(*http.Request, []*http.Request) error {
return http.ErrUseLastResponse
}
// TODO(justinwr): Deprecated?
// func (c *Client) FormatURL(path string) string {
// return fmt.Sprintf("%s%s", c.Endpoint, path)
// }
func (c *Client) DecodeError(statusCode int, body io.Reader) error {
err := &TritonError{
StatusCode: statusCode,
func (c *Client) DecodeError(resp *http.Response, requestMethod string) error {
err := &errors.APIError{
StatusCode: resp.StatusCode,
}
errorDecoder := json.NewDecoder(body)
if err := errorDecoder.Decode(err); err != nil {
return errwrap.Wrapf("Error decoding error response: {{err}}", err)
if requestMethod != http.MethodHead && resp.Body != nil {
errorDecoder := json.NewDecoder(resp.Body)
if err := errorDecoder.Decode(err); err != nil {
return pkgerrors.Wrapf(err, "unable to decode error response")
}
}
if err.Message == "" {
err.Message = fmt.Sprintf("HTTP response returned status code %d", err.StatusCode)
}
return err
@ -215,7 +225,7 @@ func (c *Client) ExecuteRequestURIParams(ctx context.Context, inputs RequestInpu
req, err := http.NewRequest(method, endpoint.String(), requestBody)
if err != nil {
return nil, errwrap.Wrapf("Error constructing HTTP request: {{err}}", err)
return nil, pkgerrors.Wrapf(err, "unable to construct HTTP request")
}
dateHeader := time.Now().UTC().Format(time.RFC1123)
@ -225,12 +235,12 @@ func (c *Client) ExecuteRequestURIParams(ctx context.Context, inputs RequestInpu
// outside that constructor).
authHeader, err := c.Authorizers[0].Sign(dateHeader)
if err != nil {
return nil, errwrap.Wrapf("Error signing HTTP request: {{err}}", err)
return nil, pkgerrors.Wrapf(err, "unable to sign HTTP request")
}
req.Header.Set("Authorization", authHeader)
req.Header.Set("Accept", "application/json")
req.Header.Set("Accept-Version", "8")
req.Header.Set("User-Agent", "triton-go Client API")
req.Header.Set("Accept-Version", triton.CloudAPIMajorVersion)
req.Header.Set("User-Agent", triton.UserAgent())
if body != nil {
req.Header.Set("Content-Type", "application/json")
@ -238,14 +248,16 @@ func (c *Client) ExecuteRequestURIParams(ctx context.Context, inputs RequestInpu
resp, err := c.HTTPClient.Do(req.WithContext(ctx))
if err != nil {
return nil, errwrap.Wrapf("Error executing HTTP request: {{err}}", err)
return nil, pkgerrors.Wrapf(err, "unable to execute HTTP request")
}
// We will only return a response from the API it is in the HTTP StatusCode 2xx range
// StatusMultipleChoices is StatusCode 300
if resp.StatusCode >= http.StatusOK && resp.StatusCode < http.StatusMultipleChoices {
return resp.Body, nil
}
return nil, c.DecodeError(resp.StatusCode, resp.Body)
return nil, c.DecodeError(resp, req.Method)
}
func (c *Client) ExecuteRequest(ctx context.Context, inputs RequestInput) (io.ReadCloser, error) {
@ -271,7 +283,7 @@ func (c *Client) ExecuteRequestRaw(ctx context.Context, inputs RequestInput) (*h
req, err := http.NewRequest(method, endpoint.String(), requestBody)
if err != nil {
return nil, errwrap.Wrapf("Error constructing HTTP request: {{err}}", err)
return nil, pkgerrors.Wrapf(err, "unable to construct HTTP request")
}
dateHeader := time.Now().UTC().Format(time.RFC1123)
@ -281,12 +293,12 @@ func (c *Client) ExecuteRequestRaw(ctx context.Context, inputs RequestInput) (*h
// outside that constructor).
authHeader, err := c.Authorizers[0].Sign(dateHeader)
if err != nil {
return nil, errwrap.Wrapf("Error signing HTTP request: {{err}}", err)
return nil, pkgerrors.Wrapf(err, "unable to sign HTTP request")
}
req.Header.Set("Authorization", authHeader)
req.Header.Set("Accept", "application/json")
req.Header.Set("Accept-Version", "8")
req.Header.Set("User-Agent", "triton-go c API")
req.Header.Set("Accept-Version", triton.CloudAPIMajorVersion)
req.Header.Set("User-Agent", triton.UserAgent())
if body != nil {
req.Header.Set("Content-Type", "application/json")
@ -294,10 +306,16 @@ func (c *Client) ExecuteRequestRaw(ctx context.Context, inputs RequestInput) (*h
resp, err := c.HTTPClient.Do(req.WithContext(ctx))
if err != nil {
return nil, errwrap.Wrapf("Error executing HTTP request: {{err}}", err)
return nil, pkgerrors.Wrapf(err, "unable to execute HTTP request")
}
return resp, nil
// We will only return a response from the API it is in the HTTP StatusCode 2xx range
// StatusMultipleChoices is StatusCode 300
if resp.StatusCode >= http.StatusOK && resp.StatusCode < http.StatusMultipleChoices {
return resp, nil
}
return nil, c.DecodeError(resp, req.Method)
}
func (c *Client) ExecuteRequestStorage(ctx context.Context, inputs RequestInput) (io.ReadCloser, http.Header, error) {
@ -321,7 +339,7 @@ func (c *Client) ExecuteRequestStorage(ctx context.Context, inputs RequestInput)
req, err := http.NewRequest(method, endpoint.String(), requestBody)
if err != nil {
return nil, nil, errwrap.Wrapf("Error constructing HTTP request: {{err}}", err)
return nil, nil, pkgerrors.Wrapf(err, "unable to construct HTTP request")
}
if body != nil && (headers == nil || headers.Get("Content-Type") == "") {
@ -340,11 +358,11 @@ func (c *Client) ExecuteRequestStorage(ctx context.Context, inputs RequestInput)
authHeader, err := c.Authorizers[0].Sign(dateHeader)
if err != nil {
return nil, nil, errwrap.Wrapf("Error signing HTTP request: {{err}}", err)
return nil, nil, pkgerrors.Wrapf(err, "unable to sign HTTP request")
}
req.Header.Set("Authorization", authHeader)
req.Header.Set("Accept", "*/*")
req.Header.Set("User-Agent", "manta-go client API")
req.Header.Set("User-Agent", triton.UserAgent())
if query != nil {
req.URL.RawQuery = query.Encode()
@ -352,29 +370,16 @@ func (c *Client) ExecuteRequestStorage(ctx context.Context, inputs RequestInput)
resp, err := c.HTTPClient.Do(req.WithContext(ctx))
if err != nil {
return nil, nil, errwrap.Wrapf("Error executing HTTP request: {{err}}", err)
return nil, nil, pkgerrors.Wrapf(err, "unable to execute HTTP request")
}
// We will only return a response from the API it is in the HTTP StatusCode 2xx range
// StatusMultipleChoices is StatusCode 300
if resp.StatusCode >= http.StatusOK && resp.StatusCode < http.StatusMultipleChoices {
return resp.Body, resp.Header, nil
}
mantaError := &MantaError{
StatusCode: resp.StatusCode,
}
if req.Method != http.MethodHead {
errorDecoder := json.NewDecoder(resp.Body)
if err := errorDecoder.Decode(mantaError); err != nil {
return nil, nil, errwrap.Wrapf("Error decoding error response: {{err}}", err)
}
}
if mantaError.Message == "" {
mantaError.Message = fmt.Sprintf("HTTP response returned status code %d", resp.StatusCode)
}
return nil, nil, mantaError
return nil, nil, c.DecodeError(resp, req.Method)
}
type RequestNoEncodeInput struct {
@ -397,7 +402,7 @@ func (c *Client) ExecuteRequestNoEncode(ctx context.Context, inputs RequestNoEnc
req, err := http.NewRequest(method, endpoint.String(), body)
if err != nil {
return nil, nil, errwrap.Wrapf("Error constructing HTTP request: {{err}}", err)
return nil, nil, pkgerrors.Wrapf(err, "unable to construct HTTP request")
}
if headers != nil {
@ -413,11 +418,12 @@ func (c *Client) ExecuteRequestNoEncode(ctx context.Context, inputs RequestNoEnc
authHeader, err := c.Authorizers[0].Sign(dateHeader)
if err != nil {
return nil, nil, errwrap.Wrapf("Error signing HTTP request: {{err}}", err)
return nil, nil, pkgerrors.Wrapf(err, "unable to sign HTTP request")
}
req.Header.Set("Authorization", authHeader)
req.Header.Set("Accept", "*/*")
req.Header.Set("User-Agent", "manta-go client API")
req.Header.Set("Accept-Version", triton.CloudAPIMajorVersion)
req.Header.Set("User-Agent", triton.UserAgent())
if query != nil {
req.URL.RawQuery = query.Encode()
@ -425,20 +431,14 @@ func (c *Client) ExecuteRequestNoEncode(ctx context.Context, inputs RequestNoEnc
resp, err := c.HTTPClient.Do(req.WithContext(ctx))
if err != nil {
return nil, nil, errwrap.Wrapf("Error executing HTTP request: {{err}}", err)
return nil, nil, pkgerrors.Wrapf(err, "unable to execute HTTP request")
}
// We will only return a response from the API it is in the HTTP StatusCode 2xx range
// StatusMultipleChoices is StatusCode 300
if resp.StatusCode >= http.StatusOK && resp.StatusCode < http.StatusMultipleChoices {
return resp.Body, resp.Header, nil
}
mantaError := &MantaError{
StatusCode: resp.StatusCode,
}
errorDecoder := json.NewDecoder(resp.Body)
if err := errorDecoder.Decode(mantaError); err != nil {
return nil, nil, errwrap.Wrapf("Error decoding error response: {{err}}", err)
}
return nil, nil, mantaError
return nil, nil, c.DecodeError(resp, req.Method)
}

View File

@ -1,190 +0,0 @@
package client
import (
"fmt"
"github.com/hashicorp/errwrap"
)
// ClientError represents an error code and message along with the status code
// of the HTTP request which resulted in the error message.
type ClientError struct {
StatusCode int
Code string
Message string
}
// Error implements interface Error on the TritonError type.
func (e ClientError) Error() string {
return fmt.Sprintf("%s: %s", e.Code, e.Message)
}
// MantaError represents an error code and message along with
// the status code of the HTTP request which resulted in the error
// message. Error codes used by the Manta API are listed at
// https://apidocs.joyent.com/manta/api.html#errors
type MantaError struct {
StatusCode int
Code string `json:"code"`
Message string `json:"message"`
}
// Error implements interface Error on the MantaError type.
func (e MantaError) Error() string {
return fmt.Sprintf("%s: %s", e.Code, e.Message)
}
// TritonError represents an error code and message along with
// the status code of the HTTP request which resulted in the error
// message. Error codes used by the Triton API are listed at
// https://apidocs.joyent.com/cloudapi/#cloudapi-http-responses
type TritonError struct {
StatusCode int
Code string `json:"code"`
Message string `json:"message"`
}
// Error implements interface Error on the TritonError type.
func (e TritonError) Error() string {
return fmt.Sprintf("%s: %s", e.Code, e.Message)
}
func IsAuthSchemeError(err error) bool {
return isSpecificError(err, "AuthScheme")
}
func IsAuthorizationError(err error) bool {
return isSpecificError(err, "Authorization")
}
func IsBadRequestError(err error) bool {
return isSpecificError(err, "BadRequest")
}
func IsChecksumError(err error) bool {
return isSpecificError(err, "Checksum")
}
func IsConcurrentRequestError(err error) bool {
return isSpecificError(err, "ConcurrentRequest")
}
func IsContentLengthError(err error) bool {
return isSpecificError(err, "ContentLength")
}
func IsContentMD5MismatchError(err error) bool {
return isSpecificError(err, "ContentMD5Mismatch")
}
func IsEntityExistsError(err error) bool {
return isSpecificError(err, "EntityExists")
}
func IsInvalidArgumentError(err error) bool {
return isSpecificError(err, "InvalidArgument")
}
func IsInvalidAuthTokenError(err error) bool {
return isSpecificError(err, "InvalidAuthToken")
}
func IsInvalidCredentialsError(err error) bool {
return isSpecificError(err, "InvalidCredentials")
}
func IsInvalidDurabilityLevelError(err error) bool {
return isSpecificError(err, "InvalidDurabilityLevel")
}
func IsInvalidKeyIdError(err error) bool {
return isSpecificError(err, "InvalidKeyId")
}
func IsInvalidJobError(err error) bool {
return isSpecificError(err, "InvalidJob")
}
func IsInvalidLinkError(err error) bool {
return isSpecificError(err, "InvalidLink")
}
func IsInvalidLimitError(err error) bool {
return isSpecificError(err, "InvalidLimit")
}
func IsInvalidSignatureError(err error) bool {
return isSpecificError(err, "InvalidSignature")
}
func IsInvalidUpdateError(err error) bool {
return isSpecificError(err, "InvalidUpdate")
}
func IsDirectoryDoesNotExistError(err error) bool {
return isSpecificError(err, "DirectoryDoesNotExist")
}
func IsDirectoryExistsError(err error) bool {
return isSpecificError(err, "DirectoryExists")
}
func IsDirectoryNotEmptyError(err error) bool {
return isSpecificError(err, "DirectoryNotEmpty")
}
func IsDirectoryOperationError(err error) bool {
return isSpecificError(err, "DirectoryOperation")
}
func IsInternalError(err error) bool {
return isSpecificError(err, "Internal")
}
func IsJobNotFoundError(err error) bool {
return isSpecificError(err, "JobNotFound")
}
func IsJobStateError(err error) bool {
return isSpecificError(err, "JobState")
}
func IsKeyDoesNotExistError(err error) bool {
return isSpecificError(err, "KeyDoesNotExist")
}
func IsNotAcceptableError(err error) bool {
return isSpecificError(err, "NotAcceptable")
}
func IsNotEnoughSpaceError(err error) bool {
return isSpecificError(err, "NotEnoughSpace")
}
func IsLinkNotFoundError(err error) bool {
return isSpecificError(err, "LinkNotFound")
}
func IsLinkNotObjectError(err error) bool {
return isSpecificError(err, "LinkNotObject")
}
func IsLinkRequiredError(err error) bool {
return isSpecificError(err, "LinkRequired")
}
func IsParentNotDirectoryError(err error) bool {
return isSpecificError(err, "ParentNotDirectory")
}
func IsPreconditionFailedError(err error) bool {
return isSpecificError(err, "PreconditionFailed")
}
func IsPreSignedRequestError(err error) bool {
return isSpecificError(err, "PreSignedRequest")
}
func IsRequestEntityTooLargeError(err error) bool {
return isSpecificError(err, "RequestEntityTooLarge")
}
func IsResourceNotFoundError(err error) bool {
return isSpecificError(err, "ResourceNotFound")
}
func IsRootDirectoryError(err error) bool {
return isSpecificError(err, "RootDirectory")
}
func IsServiceUnavailableError(err error) bool {
return isSpecificError(err, "ServiceUnavailable")
}
func IsSSLRequiredError(err error) bool {
return isSpecificError(err, "SSLRequired")
}
func IsUploadTimeoutError(err error) bool {
return isSpecificError(err, "UploadTimeout")
}
func IsUserDoesNotExistError(err error) bool {
return isSpecificError(err, "UserDoesNotExist")
}
// isSpecificError checks whether the error represented by err wraps
// an underlying MantaError with code errorCode.
func isSpecificError(err error, errorCode string) bool {
tritonErrorInterface := errwrap.GetType(err.(error), &MantaError{})
if tritonErrorInterface == nil {
return false
}
tritonErr := tritonErrorInterface.(*MantaError)
if tritonErr.Code == errorCode {
return true
}
return false
}

297
vendor/github.com/joyent/triton-go/errors/errors.go generated vendored Normal file
View File

@ -0,0 +1,297 @@
//
// Copyright (c) 2018, Joyent, Inc. All rights reserved.
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
//
package errors
import (
"fmt"
"net/http"
"strings"
"github.com/pkg/errors"
)
// APIError represents an error code and message along with
// the status code of the HTTP request which resulted in the error
// message. Error codes used by the Triton API are listed at
// https://apidocs.joyent.com/cloudapi/#cloudapi-http-responses
// Error codes used by the Manta API are listed at
// https://apidocs.joyent.com/manta/api.html#errors
type APIError struct {
StatusCode int
Code string `json:"code"`
Message string `json:"message"`
}
// Error implements interface Error on the APIError type.
func (e APIError) Error() string {
return strings.Trim(fmt.Sprintf("%+q", e.Code), `"`) + ": " + strings.Trim(fmt.Sprintf("%+q", e.Message), `"`)
}
// ClientError represents an error code and message returned
// when connecting to the triton-go client
type ClientError struct {
StatusCode int
Code string `json:"code"`
Message string `json:"message"`
}
// Error implements interface Error on the ClientError type.
func (e ClientError) Error() string {
return strings.Trim(fmt.Sprintf("%+q", e.Code), `"`) + ": " + strings.Trim(fmt.Sprintf("%+q", e.Message), `"`)
}
func IsAuthSchemeError(err error) bool {
return IsSpecificError(err, "AuthScheme")
}
func IsAuthorizationError(err error) bool {
return IsSpecificError(err, "Authorization")
}
func IsBadRequestError(err error) bool {
return IsSpecificError(err, "BadRequest")
}
func IsChecksumError(err error) bool {
return IsSpecificError(err, "Checksum")
}
func IsConcurrentRequestError(err error) bool {
return IsSpecificError(err, "ConcurrentRequest")
}
func IsContentLengthError(err error) bool {
return IsSpecificError(err, "ContentLength")
}
func IsContentMD5MismatchError(err error) bool {
return IsSpecificError(err, "ContentMD5Mismatch")
}
func IsEntityExistsError(err error) bool {
return IsSpecificError(err, "EntityExists")
}
func IsInvalidArgumentError(err error) bool {
return IsSpecificError(err, "InvalidArgument")
}
func IsInvalidAuthTokenError(err error) bool {
return IsSpecificError(err, "InvalidAuthToken")
}
func IsInvalidCredentialsError(err error) bool {
return IsSpecificError(err, "InvalidCredentials")
}
func IsInvalidDurabilityLevelError(err error) bool {
return IsSpecificError(err, "InvalidDurabilityLevel")
}
func IsInvalidKeyIdError(err error) bool {
return IsSpecificError(err, "InvalidKeyId")
}
func IsInvalidJobError(err error) bool {
return IsSpecificError(err, "InvalidJob")
}
func IsInvalidLinkError(err error) bool {
return IsSpecificError(err, "InvalidLink")
}
func IsInvalidLimitError(err error) bool {
return IsSpecificError(err, "InvalidLimit")
}
func IsInvalidSignatureError(err error) bool {
return IsSpecificError(err, "InvalidSignature")
}
func IsInvalidUpdateError(err error) bool {
return IsSpecificError(err, "InvalidUpdate")
}
func IsDirectoryDoesNotExistError(err error) bool {
return IsSpecificError(err, "DirectoryDoesNotExist")
}
func IsDirectoryExistsError(err error) bool {
return IsSpecificError(err, "DirectoryExists")
}
func IsDirectoryNotEmptyError(err error) bool {
return IsSpecificError(err, "DirectoryNotEmpty")
}
func IsDirectoryOperationError(err error) bool {
return IsSpecificError(err, "DirectoryOperation")
}
func IsInternalError(err error) bool {
return IsSpecificError(err, "Internal")
}
func IsJobNotFoundError(err error) bool {
return IsSpecificError(err, "JobNotFound")
}
func IsJobStateError(err error) bool {
return IsSpecificError(err, "JobState")
}
func IsKeyDoesNotExistError(err error) bool {
return IsSpecificError(err, "KeyDoesNotExist")
}
func IsNotAcceptableError(err error) bool {
return IsSpecificError(err, "NotAcceptable")
}
func IsNotEnoughSpaceError(err error) bool {
return IsSpecificError(err, "NotEnoughSpace")
}
func IsLinkNotFoundError(err error) bool {
return IsSpecificError(err, "LinkNotFound")
}
func IsLinkNotObjectError(err error) bool {
return IsSpecificError(err, "LinkNotObject")
}
func IsLinkRequiredError(err error) bool {
return IsSpecificError(err, "LinkRequired")
}
func IsParentNotDirectoryError(err error) bool {
return IsSpecificError(err, "ParentNotDirectory")
}
func IsPreconditionFailedError(err error) bool {
return IsSpecificError(err, "PreconditionFailed")
}
func IsPreSignedRequestError(err error) bool {
return IsSpecificError(err, "PreSignedRequest")
}
func IsRequestEntityTooLargeError(err error) bool {
return IsSpecificError(err, "RequestEntityTooLarge")
}
func IsResourceNotFoundError(err error) bool {
return IsSpecificError(err, "ResourceNotFound")
}
func IsRootDirectoryError(err error) bool {
return IsSpecificError(err, "RootDirectory")
}
func IsServiceUnavailableError(err error) bool {
return IsSpecificError(err, "ServiceUnavailable")
}
func IsSSLRequiredError(err error) bool {
return IsSpecificError(err, "SSLRequired")
}
func IsUploadTimeoutError(err error) bool {
return IsSpecificError(err, "UploadTimeout")
}
func IsUserDoesNotExistError(err error) bool {
return IsSpecificError(err, "UserDoesNotExist")
}
func IsBadRequest(err error) bool {
return IsSpecificError(err, "BadRequest")
}
func IsInUseError(err error) bool {
return IsSpecificError(err, "InUseError")
}
func IsInvalidArgument(err error) bool {
return IsSpecificError(err, "InvalidArgument")
}
func IsInvalidCredentials(err error) bool {
return IsSpecificError(err, "InvalidCredentials")
}
func IsInvalidHeader(err error) bool {
return IsSpecificError(err, "InvalidHeader")
}
func IsInvalidVersion(err error) bool {
return IsSpecificError(err, "InvalidVersion")
}
func IsMissingParameter(err error) bool {
return IsSpecificError(err, "MissingParameter")
}
func IsNotAuthorized(err error) bool {
return IsSpecificError(err, "NotAuthorized")
}
func IsRequestThrottled(err error) bool {
return IsSpecificError(err, "RequestThrottled")
}
func IsRequestTooLarge(err error) bool {
return IsSpecificError(err, "RequestTooLarge")
}
func IsRequestMoved(err error) bool {
return IsSpecificError(err, "RequestMoved")
}
func IsResourceFound(err error) bool {
return IsSpecificError(err, "ResourceFound")
}
func IsResourceNotFound(err error) bool {
return IsSpecificError(err, "ResourceNotFound")
}
func IsUnknownError(err error) bool {
return IsSpecificError(err, "UnknownError")
}
func IsEmptyResponse(err error) bool {
return IsSpecificError(err, "EmptyResponse")
}
func IsStatusNotFoundCode(err error) bool {
return IsSpecificStatusCode(err, http.StatusNotFound)
}
func IsSpecificError(myError error, errorCode string) bool {
switch err := errors.Cause(myError).(type) {
case *APIError:
if err.Code == errorCode {
return true
}
}
return false
}
func IsSpecificStatusCode(myError error, statusCode int) bool {
switch err := errors.Cause(myError).(type) {
case *APIError:
if err.StatusCode == statusCode {
return true
}
}
return false
}

View File

@ -1,3 +1,11 @@
//
// Copyright (c) 2018, Joyent, Inc. All rights reserved.
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
//
package storage
import (

View File

@ -1,3 +1,11 @@
//
// Copyright (c) 2018, Joyent, Inc. All rights reserved.
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
//
package storage
import (
@ -10,8 +18,8 @@ import (
"strconv"
"time"
"github.com/hashicorp/errwrap"
"github.com/joyent/triton-go/client"
"github.com/pkg/errors"
)
type DirectoryClient struct {
@ -58,7 +66,7 @@ func (s *DirectoryClient) List(ctx context.Context, input *ListDirectoryInput) (
}
respBody, respHeader, err := s.client.ExecuteRequestStorage(ctx, reqInput)
if err != nil {
return nil, errwrap.Wrapf("Error executing List request: {{err}}", err)
return nil, errors.Wrap(err, "unable to list directory")
}
defer respBody.Close()
@ -67,14 +75,14 @@ func (s *DirectoryClient) List(ctx context.Context, input *ListDirectoryInput) (
for scanner.Scan() {
current := &DirectoryEntry{}
if err := json.Unmarshal(scanner.Bytes(), current); err != nil {
return nil, errwrap.Wrapf("error decoding list response: {{err}}", err)
return nil, errors.Wrap(err, "unable to decode list directories response")
}
results = append(results, current)
}
if err := scanner.Err(); err != nil {
return nil, errwrap.Wrapf("error decoding list responses: {{err}}", err)
return nil, errors.Wrap(err, "unable to decode list directories response")
}
output := &ListDirectoryOutput{
@ -113,7 +121,7 @@ func (s *DirectoryClient) Put(ctx context.Context, input *PutDirectoryInput) err
defer respBody.Close()
}
if err != nil {
return errwrap.Wrapf("Error executing Put request: {{err}}", err)
return errors.Wrap(err, "unable to put directory")
}
return nil
@ -177,7 +185,7 @@ func deleteDirectory(c DirectoryClient, ctx context.Context, directoryPath _AbsC
defer respBody.Close()
}
if err != nil {
return errwrap.Wrapf("Error executing DeleteDirectory request: {{err}}", err)
return errors.Wrap(err, "unable to delete directory")
}
return nil

View File

@ -1,18 +1,26 @@
//
// Copyright (c) 2018, Joyent, Inc. All rights reserved.
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
//
package storage
import (
"context"
"encoding/json"
"fmt"
"io"
"net/http"
"net/url"
"path"
"strconv"
"strings"
"time"
"github.com/hashicorp/errwrap"
"github.com/joyent/triton-go/client"
"github.com/pkg/errors"
)
type JobClient struct {
@ -99,11 +107,11 @@ type CreateJobOutput struct {
// CreateJob submits a new job to be executed. This call is not
// idempotent, so calling it twice will create two jobs.
func (s *JobClient) Create(ctx context.Context, input *CreateJobInput) (*CreateJobOutput, error) {
path := fmt.Sprintf("/%s/jobs", s.client.AccountName)
fullPath := path.Join("/", s.client.AccountName, "jobs")
reqInput := client.RequestInput{
Method: http.MethodPost,
Path: path,
Path: fullPath,
Body: input,
}
respBody, respHeaders, err := s.client.ExecuteRequestStorage(ctx, reqInput)
@ -111,7 +119,7 @@ func (s *JobClient) Create(ctx context.Context, input *CreateJobInput) (*CreateJ
defer respBody.Close()
}
if err != nil {
return nil, errwrap.Wrapf("Error executing CreateJob request: {{err}}", err)
return nil, errors.Wrap(err, "unable to create job")
}
jobURI := respHeaders.Get("Location")
@ -133,7 +141,7 @@ type AddJobInputsInput struct {
// AddJobInputs submits inputs to an already created job.
func (s *JobClient) AddInputs(ctx context.Context, input *AddJobInputsInput) error {
path := fmt.Sprintf("/%s/jobs/%s/live/in", s.client.AccountName, input.JobID)
fullPath := path.Join("/", s.client.AccountName, "jobs", input.JobID, "live", "in")
headers := &http.Header{}
headers.Set("Content-Type", "text/plain")
@ -141,7 +149,7 @@ func (s *JobClient) AddInputs(ctx context.Context, input *AddJobInputsInput) err
reqInput := client.RequestNoEncodeInput{
Method: http.MethodPost,
Path: path,
Path: fullPath,
Headers: headers,
Body: reader,
}
@ -150,7 +158,7 @@ func (s *JobClient) AddInputs(ctx context.Context, input *AddJobInputsInput) err
defer respBody.Close()
}
if err != nil {
return errwrap.Wrapf("Error executing AddJobInputs request: {{err}}", err)
return errors.Wrap(err, "unable to add job inputs")
}
return nil
@ -163,18 +171,18 @@ type EndJobInputInput struct {
// EndJobInput submits inputs to an already created job.
func (s *JobClient) EndInput(ctx context.Context, input *EndJobInputInput) error {
path := fmt.Sprintf("/%s/jobs/%s/live/in/end", s.client.AccountName, input.JobID)
fullPath := path.Join("/", s.client.AccountName, "jobs", input.JobID, "live", "in", "end")
reqInput := client.RequestNoEncodeInput{
Method: http.MethodPost,
Path: path,
Path: fullPath,
}
respBody, _, err := s.client.ExecuteRequestNoEncode(ctx, reqInput)
if respBody != nil {
defer respBody.Close()
}
if err != nil {
return errwrap.Wrapf("Error executing EndJobInput request: {{err}}", err)
return errors.Wrap(err, "unable to end job inputs")
}
return nil
@ -194,18 +202,18 @@ type CancelJobInput struct {
// - input is still open
// - you have a long-running job
func (s *JobClient) Cancel(ctx context.Context, input *CancelJobInput) error {
path := fmt.Sprintf("/%s/jobs/%s/live/cancel", s.client.AccountName, input.JobID)
fullPath := path.Join("/", s.client.AccountName, "jobs", input.JobID, "live", "cancel")
reqInput := client.RequestNoEncodeInput{
Method: http.MethodPost,
Path: path,
Path: fullPath,
}
respBody, _, err := s.client.ExecuteRequestNoEncode(ctx, reqInput)
if respBody != nil {
defer respBody.Close()
}
if err != nil {
return errwrap.Wrapf("Error executing CancelJob request: {{err}}", err)
return errors.Wrap(err, "unable to cancel job")
}
return nil
@ -226,7 +234,7 @@ type ListJobsOutput struct {
// ListJobs returns the list of jobs you currently have.
func (s *JobClient) List(ctx context.Context, input *ListJobsInput) (*ListJobsOutput, error) {
path := fmt.Sprintf("/%s/jobs", s.client.AccountName)
fullPath := path.Join("/", s.client.AccountName, "jobs")
query := &url.Values{}
if input.RunningOnly {
query.Set("state", "running")
@ -240,7 +248,7 @@ func (s *JobClient) List(ctx context.Context, input *ListJobsInput) (*ListJobsOu
reqInput := client.RequestInput{
Method: http.MethodGet,
Path: path,
Path: fullPath,
Query: query,
}
respBody, respHeader, err := s.client.ExecuteRequestStorage(ctx, reqInput)
@ -248,7 +256,7 @@ func (s *JobClient) List(ctx context.Context, input *ListJobsInput) (*ListJobsOu
defer respBody.Close()
}
if err != nil {
return nil, errwrap.Wrapf("Error executing ListJobs request: {{err}}", err)
return nil, errors.Wrap(err, "unable to list jobs")
}
var results []*JobSummary
@ -259,7 +267,7 @@ func (s *JobClient) List(ctx context.Context, input *ListJobsInput) (*ListJobsOu
if err == io.EOF {
break
}
return nil, errwrap.Wrapf("Error decoding ListJobs response: {{err}}", err)
return nil, errors.Wrap(err, "unable to decode list jobs response")
}
results = append(results, current)
}
@ -288,24 +296,24 @@ type GetJobOutput struct {
// GetJob returns the list of jobs you currently have.
func (s *JobClient) Get(ctx context.Context, input *GetJobInput) (*GetJobOutput, error) {
path := fmt.Sprintf("/%s/jobs/%s/live/status", s.client.AccountName, input.JobID)
fullPath := path.Join("/", s.client.AccountName, "jobs", input.JobID, "live", "status")
reqInput := client.RequestInput{
Method: http.MethodGet,
Path: path,
Path: fullPath,
}
respBody, _, err := s.client.ExecuteRequestStorage(ctx, reqInput)
if respBody != nil {
defer respBody.Close()
}
if err != nil {
return nil, errwrap.Wrapf("Error executing GetJob request: {{err}}", err)
return nil, errors.Wrap(err, "unable to get job")
}
job := &Job{}
decoder := json.NewDecoder(respBody)
if err = decoder.Decode(&job); err != nil {
return nil, errwrap.Wrapf("Error decoding GetJob response: {{err}}", err)
return nil, errors.Wrap(err, "unable to decode get job response")
}
return &GetJobOutput{
@ -329,18 +337,18 @@ type GetJobOutputOutput struct {
// this like `tail -f`. If error is nil (i.e. the operation is successful), it is
// your responsibility to close the io.ReadCloser named Items in the output.
func (s *JobClient) GetOutput(ctx context.Context, input *GetJobOutputInput) (*GetJobOutputOutput, error) {
path := fmt.Sprintf("/%s/jobs/%s/live/out", s.client.AccountName, input.JobID)
fullPath := path.Join("/", s.client.AccountName, "jobs", input.JobID, "live", "out")
reqInput := client.RequestInput{
Method: http.MethodGet,
Path: path,
Path: fullPath,
}
respBody, respHeader, err := s.client.ExecuteRequestStorage(ctx, reqInput)
if respBody != nil {
defer respBody.Close()
}
if err != nil {
return nil, errwrap.Wrapf("Error executing GetJobOutput request: {{err}}", err)
return nil, errors.Wrap(err, "unable to get job output")
}
output := &GetJobOutputOutput{
@ -371,18 +379,18 @@ type GetJobInputOutput struct {
// this like `tail -f`. If error is nil (i.e. the operation is successful), it is
// your responsibility to close the io.ReadCloser named Items in the output.
func (s *JobClient) GetInput(ctx context.Context, input *GetJobInputInput) (*GetJobInputOutput, error) {
path := fmt.Sprintf("/%s/jobs/%s/live/in", s.client.AccountName, input.JobID)
fullPath := path.Join("/", s.client.AccountName, "jobs", input.JobID, "live", "in")
reqInput := client.RequestInput{
Method: http.MethodGet,
Path: path,
Path: fullPath,
}
respBody, respHeader, err := s.client.ExecuteRequestStorage(ctx, reqInput)
if respBody != nil {
defer respBody.Close()
}
if err != nil {
return nil, errwrap.Wrapf("Error executing GetJobInput request: {{err}}", err)
return nil, errors.Wrap(err, "unable to get job input")
}
output := &GetJobInputOutput{
@ -413,18 +421,18 @@ type GetJobFailuresOutput struct {
// this like `tail -f`. If error is nil (i.e. the operation is successful), it is
// your responsibility to close the io.ReadCloser named Items in the output.
func (s *JobClient) GetFailures(ctx context.Context, input *GetJobFailuresInput) (*GetJobFailuresOutput, error) {
path := fmt.Sprintf("/%s/jobs/%s/live/fail", s.client.AccountName, input.JobID)
fullPath := path.Join("/", s.client.AccountName, "jobs", input.JobID, "live", "fail")
reqInput := client.RequestInput{
Method: http.MethodGet,
Path: path,
Path: fullPath,
}
respBody, respHeader, err := s.client.ExecuteRequestStorage(ctx, reqInput)
if respBody != nil {
defer respBody.Close()
}
if err != nil {
return nil, errwrap.Wrapf("Error executing GetJobFailures request: {{err}}", err)
return nil, errors.Wrap(err, "unable to get job failures")
}
output := &GetJobFailuresOutput{

View File

@ -1,8 +1,15 @@
//
// Copyright (c) 2018, Joyent, Inc. All rights reserved.
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
//
package storage
import (
"context"
"errors"
"io"
"net/http"
"net/url"
@ -11,8 +18,9 @@ import (
"strings"
"time"
"github.com/hashicorp/errwrap"
"github.com/joyent/triton-go/client"
tt "github.com/joyent/triton-go/errors"
"github.com/pkg/errors"
)
type ObjectsClient struct {
@ -53,7 +61,7 @@ func (s *ObjectsClient) GetInfo(ctx context.Context, input *GetInfoInput) (*GetI
}
_, respHeaders, err := s.client.ExecuteRequestStorage(ctx, reqInput)
if err != nil {
return nil, errwrap.Wrapf("Error executing get info request: {{err}}", err)
return nil, errors.Wrap(err, "unable to get info")
}
response := &GetInfoOutput{
@ -135,7 +143,7 @@ func (s *ObjectsClient) Get(ctx context.Context, input *GetObjectInput) (*GetObj
}
respBody, respHeaders, err := s.client.ExecuteRequestStorage(ctx, reqInput)
if err != nil {
return nil, errwrap.Wrapf("Error executing Get request: {{err}}", err)
return nil, errors.Wrap(err, "unable to get object")
}
response := &GetObjectOutput{
@ -191,7 +199,7 @@ func (s *ObjectsClient) Delete(ctx context.Context, input *DeleteObjectInput) er
defer respBody.Close()
}
if err != nil {
return errwrap.Wrapf("Error executing Delete request: {{err}}", err)
return errors.Wrap(err, "unable to delete object")
}
return nil
@ -235,7 +243,7 @@ func (s *ObjectsClient) PutMetadata(ctx context.Context, input *PutObjectMetadat
defer respBody.Close()
}
if err != nil {
return errwrap.Wrapf("Error executing PutMetadata request: {{err}}", err)
return errors.Wrap(err, "unable to put metadata")
}
return nil
@ -333,7 +341,7 @@ func putObject(c ObjectsClient, ctx context.Context, input *PutObjectInput, absP
defer respBody.Close()
}
if err != nil {
return errwrap.Wrapf("Error executing Put request: {{err}}", err)
return errors.Wrap(err, "unable to put object")
}
return nil
@ -370,12 +378,8 @@ func createDirectory(c ObjectsClient, ctx context.Context, absPath _AbsCleanPath
func checkDirectoryTreeExists(c ObjectsClient, ctx context.Context, absPath _AbsCleanPath) (bool, error) {
exists, err := c.IsDir(ctx, string(absPath))
if err != nil {
errType := &client.MantaError{}
if errwrap.ContainsType(err, errType) {
mantaErr := errwrap.GetType(err, errType).(*client.MantaError)
if mantaErr.StatusCode == http.StatusNotFound {
return false, nil
}
if tt.IsResourceNotFoundError(err) {
return false, nil
}
return false, err
}

View File

@ -1,14 +1,23 @@
//
// Copyright (c) 2018, Joyent, Inc. All rights reserved.
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
//
package storage
import (
"bytes"
"fmt"
"net/url"
"path"
"strconv"
"strings"
"time"
"github.com/hashicorp/errwrap"
"github.com/pkg/errors"
)
// SignURLInput represents parameters to a SignURL operation.
@ -57,7 +66,7 @@ func (s *StorageClient) SignURL(input *SignURLInput) (*SignURLOutput, error) {
Method: input.Method,
Algorithm: strings.ToUpper(s.Client.Authorizers[0].DefaultAlgorithm()),
Expires: strconv.FormatInt(time.Now().Add(input.ValidityPeriod).Unix(), 10),
KeyID: fmt.Sprintf("/%s/keys/%s", s.Client.AccountName, s.Client.Authorizers[0].KeyFingerprint()),
KeyID: path.Join("/", s.Client.AccountName, "keys", s.Client.Authorizers[0].KeyFingerprint()),
}
toSign := bytes.Buffer{}
@ -73,7 +82,7 @@ func (s *StorageClient) SignURL(input *SignURLInput) (*SignURLOutput, error) {
signature, _, err := s.Client.Authorizers[0].SignRaw(toSign.String())
if err != nil {
return nil, errwrap.Wrapf("Error signing string: {{err}}", err)
return nil, errors.Wrapf(err, "error signing string")
}
output.Signature = signature

View File

@ -1,3 +1,11 @@
//
// Copyright (c) 2018, Joyent, Inc. All rights reserved.
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
//
package storage
import (
@ -5,8 +13,8 @@ import (
"fmt"
"net/http"
"github.com/hashicorp/errwrap"
"github.com/joyent/triton-go/client"
"github.com/pkg/errors"
)
type SnapLinksClient struct {
@ -39,7 +47,7 @@ func (s *SnapLinksClient) Put(ctx context.Context, input *PutSnapLinkInput) erro
defer respBody.Close()
}
if err != nil {
return errwrap.Wrapf("Error executing PutSnapLink request: {{err}}", err)
return errors.Wrapf(err, "unable to put snaplink")
}
return nil

View File

@ -1,3 +1,11 @@
//
// Copyright (c) 2018, Joyent, Inc. All rights reserved.
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
//
package triton
import (

32
vendor/github.com/joyent/triton-go/version.go generated vendored Normal file
View File

@ -0,0 +1,32 @@
//
// Copyright (c) 2018, Joyent, Inc. All rights reserved.
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
//
package triton
import (
"fmt"
"runtime"
)
// The main version number of the current released Triton-go SDK.
const Version = "0.9.0"
// A pre-release marker for the version. If this is "" (empty string)
// then it means that it is a final release. Otherwise, this is a pre-release
// such as "dev" (in development), "beta", "rc1", etc.
var Prerelease = ""
func UserAgent() string {
if Prerelease != "" {
return fmt.Sprintf("triton-go/%s-%s (%s-%s; %s)", Version, Prerelease, runtime.GOARCH, runtime.GOOS, runtime.Version())
} else {
return fmt.Sprintf("triton-go/%s (%s-%s; %s)", Version, runtime.GOARCH, runtime.GOOS, runtime.Version())
}
}
const CloudAPIMajorVersion = "8"

30
vendor/vendor.json vendored
View File

@ -1802,28 +1802,34 @@
"revisionTime": "2016-06-16T18:50:15Z"
},
{
"checksumSHA1": "oINoQSRkPinChzwEHr3VatB9++Y=",
"checksumSHA1": "Lg8OHK87XRGCaipG+5+zFyN8OMw=",
"path": "github.com/joyent/triton-go",
"revision": "86ba9699869b6cd5ea3290faad7be659efc7d6ce",
"revisionTime": "2017-12-28T20:20:46Z"
"revision": "545edbe0d564f075ac576f1ad177f4ff39c9adaf",
"revisionTime": "2018-01-16T16:57:42Z"
},
{
"checksumSHA1": "d6pxw8DLxYehLr92fWZTLEWVws8=",
"checksumSHA1": "Y03+L+I0FVZ2bMGWt1MHTDEyWM4=",
"path": "github.com/joyent/triton-go/authentication",
"revision": "86ba9699869b6cd5ea3290faad7be659efc7d6ce",
"revisionTime": "2017-12-28T20:20:46Z"
"revision": "545edbe0d564f075ac576f1ad177f4ff39c9adaf",
"revisionTime": "2018-01-16T16:57:42Z"
},
{
"checksumSHA1": "GCHfn8d1Mhswm7n7IRnT0n/w+dw=",
"checksumSHA1": "MuJsGBr6HlXQYxZY9cM5rBk+Lns=",
"path": "github.com/joyent/triton-go/client",
"revision": "86ba9699869b6cd5ea3290faad7be659efc7d6ce",
"revisionTime": "2017-12-28T20:20:46Z"
"revision": "545edbe0d564f075ac576f1ad177f4ff39c9adaf",
"revisionTime": "2018-01-16T16:57:42Z"
},
{
"checksumSHA1": "PJe3Rs8H466xR8o5audO8oWk44Q=",
"checksumSHA1": "d/Py6j/uMgOAFNFGpsQrNnSsO+k=",
"path": "github.com/joyent/triton-go/errors",
"revision": "545edbe0d564f075ac576f1ad177f4ff39c9adaf",
"revisionTime": "2018-01-16T16:57:42Z"
},
{
"checksumSHA1": "5v533ELM047YOiwHsyMaVzITpR0=",
"path": "github.com/joyent/triton-go/storage",
"revision": "86ba9699869b6cd5ea3290faad7be659efc7d6ce",
"revisionTime": "2017-12-28T20:20:46Z"
"revision": "545edbe0d564f075ac576f1ad177f4ff39c9adaf",
"revisionTime": "2018-01-16T16:57:42Z"
},
{
"checksumSHA1": "g+afVQQVopBLiLB5pFZp/8s6aBs=",