builder/triton: bump triton-go dependencies
This introduces a new triton-go errors package so we can error handle our code in a better way
This commit is contained in:
parent
e222d60b5a
commit
20f9ef3445
|
@ -8,8 +8,8 @@ import (
|
|||
"time"
|
||||
|
||||
"github.com/hashicorp/packer/packer"
|
||||
"github.com/joyent/triton-go/client"
|
||||
"github.com/joyent/triton-go/compute"
|
||||
terrors "github.com/joyent/triton-go/errors"
|
||||
)
|
||||
|
||||
type driverTriton struct {
|
||||
|
@ -177,7 +177,7 @@ func (d *driverTriton) WaitForMachineDeletion(machineId string, timeout time.Dur
|
|||
// Return true only when we receive a 410 (Gone) response. A 404
|
||||
// indicates that the machine is being deleted whereas a 410 indicates
|
||||
// that this process has completed.
|
||||
if triErr, ok := err.(*client.TritonError); ok && triErr.StatusCode == http.StatusGone {
|
||||
if terrors.IsSpecificStatusCode(err, http.StatusGone) {
|
||||
return true, nil
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 (
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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"`
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 (
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
|
@ -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 compute
|
||||
|
||||
import (
|
||||
|
@ -61,3 +69,9 @@ func (c *ComputeClient) Services() *ServicesClient {
|
|||
func (c *ComputeClient) Snapshots() *SnapshotsClient {
|
||||
return &SnapshotsClient{c.Client}
|
||||
}
|
||||
|
||||
// Snapshots returns a Compute client used for accessing functions pertaining to
|
||||
// Snapshots functionality in the Triton API.
|
||||
func (c *ComputeClient) Volumes() *VolumesClient {
|
||||
return &VolumesClient{c.Client}
|
||||
}
|
||||
|
|
|
@ -1,16 +1,24 @@
|
|||
//
|
||||
// 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 compute
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"path"
|
||||
"sort"
|
||||
|
||||
"context"
|
||||
|
||||
"github.com/hashicorp/errwrap"
|
||||
"github.com/joyent/triton-go/client"
|
||||
"github.com/joyent/triton-go/errors"
|
||||
pkgerrors "github.com/pkg/errors"
|
||||
)
|
||||
|
||||
type DataCentersClient struct {
|
||||
|
@ -25,23 +33,24 @@ type DataCenter struct {
|
|||
type ListDataCentersInput struct{}
|
||||
|
||||
func (c *DataCentersClient) List(ctx context.Context, _ *ListDataCentersInput) ([]*DataCenter, error) {
|
||||
path := fmt.Sprintf("/%s/datacenters", c.client.AccountName)
|
||||
fullPath := path.Join("/", c.client.AccountName, "datacenters")
|
||||
|
||||
reqInputs := client.RequestInput{
|
||||
Method: http.MethodGet,
|
||||
Path: path,
|
||||
Path: fullPath,
|
||||
}
|
||||
respReader, err := c.client.ExecuteRequest(ctx, reqInputs)
|
||||
if respReader != nil {
|
||||
defer respReader.Close()
|
||||
}
|
||||
if err != nil {
|
||||
return nil, errwrap.Wrapf("Error executing List request: {{err}}", err)
|
||||
return nil, pkgerrors.Wrap(err, "unable to list data centers")
|
||||
}
|
||||
|
||||
var intermediate map[string]string
|
||||
decoder := json.NewDecoder(respReader)
|
||||
if err = decoder.Decode(&intermediate); err != nil {
|
||||
return nil, errwrap.Wrapf("Error decoding List response: {{err}}", err)
|
||||
return nil, pkgerrors.Wrap(err, "unable to decode list data centers response")
|
||||
}
|
||||
|
||||
keys := make([]string, len(intermediate))
|
||||
|
@ -70,28 +79,23 @@ type GetDataCenterInput struct {
|
|||
}
|
||||
|
||||
func (c *DataCentersClient) Get(ctx context.Context, input *GetDataCenterInput) (*DataCenter, error) {
|
||||
path := fmt.Sprintf("/%s/datacenters/%s", c.client.AccountName, input.Name)
|
||||
reqInputs := client.RequestInput{
|
||||
Method: http.MethodGet,
|
||||
Path: path,
|
||||
}
|
||||
resp, err := c.client.ExecuteRequestRaw(ctx, reqInputs)
|
||||
dcs, err := c.List(ctx, &ListDataCentersInput{})
|
||||
if err != nil {
|
||||
return nil, errwrap.Wrapf("Error executing Get request: {{err}}", err)
|
||||
return nil, pkgerrors.Wrap(err, "unable to get data center")
|
||||
}
|
||||
|
||||
if resp.StatusCode != http.StatusFound {
|
||||
return nil, fmt.Errorf("Error executing Get request: expected status code 302, got %d",
|
||||
resp.StatusCode)
|
||||
for _, dc := range dcs {
|
||||
if dc.Name == input.Name {
|
||||
return &DataCenter{
|
||||
Name: input.Name,
|
||||
URL: dc.URL,
|
||||
}, nil
|
||||
}
|
||||
}
|
||||
|
||||
location := resp.Header.Get("Location")
|
||||
if location == "" {
|
||||
return nil, errors.New("Error decoding Get response: no Location header")
|
||||
return nil, &errors.APIError{
|
||||
StatusCode: http.StatusNotFound,
|
||||
Code: "ResourceNotFound",
|
||||
Message: fmt.Sprintf("data center %q not found", input.Name),
|
||||
}
|
||||
|
||||
return &DataCenter{
|
||||
Name: input.Name,
|
||||
URL: location,
|
||||
}, nil
|
||||
}
|
||||
|
|
|
@ -1,121 +0,0 @@
|
|||
package compute
|
||||
|
||||
import (
|
||||
"github.com/hashicorp/errwrap"
|
||||
"github.com/joyent/triton-go/client"
|
||||
)
|
||||
|
||||
// IsBadRequest tests whether err wraps a client.TritonError with
|
||||
// code BadRequest
|
||||
func IsBadRequest(err error) bool {
|
||||
return isSpecificError(err, "BadRequest")
|
||||
}
|
||||
|
||||
// IsInternalError tests whether err wraps a client.TritonError with
|
||||
// code InternalError
|
||||
func IsInternalError(err error) bool {
|
||||
return isSpecificError(err, "InternalError")
|
||||
}
|
||||
|
||||
// IsInUseError tests whether err wraps a client.TritonError with
|
||||
// code InUseError
|
||||
func IsInUseError(err error) bool {
|
||||
return isSpecificError(err, "InUseError")
|
||||
}
|
||||
|
||||
// IsInvalidArgument tests whether err wraps a client.TritonError with
|
||||
// code InvalidArgument
|
||||
func IsInvalidArgument(err error) bool {
|
||||
return isSpecificError(err, "InvalidArgument")
|
||||
}
|
||||
|
||||
// IsInvalidCredentials tests whether err wraps a client.TritonError with
|
||||
// code InvalidCredentials
|
||||
func IsInvalidCredentials(err error) bool {
|
||||
return isSpecificError(err, "InvalidCredentials")
|
||||
}
|
||||
|
||||
// IsInvalidHeader tests whether err wraps a client.TritonError with
|
||||
// code InvalidHeader
|
||||
func IsInvalidHeader(err error) bool {
|
||||
return isSpecificError(err, "InvalidHeader")
|
||||
}
|
||||
|
||||
// IsInvalidVersion tests whether err wraps a client.TritonError with
|
||||
// code InvalidVersion
|
||||
func IsInvalidVersion(err error) bool {
|
||||
return isSpecificError(err, "InvalidVersion")
|
||||
}
|
||||
|
||||
// IsMissingParameter tests whether err wraps a client.TritonError with
|
||||
// code MissingParameter
|
||||
func IsMissingParameter(err error) bool {
|
||||
return isSpecificError(err, "MissingParameter")
|
||||
}
|
||||
|
||||
// IsNotAuthorized tests whether err wraps a client.TritonError with
|
||||
// code NotAuthorized
|
||||
func IsNotAuthorized(err error) bool {
|
||||
return isSpecificError(err, "NotAuthorized")
|
||||
}
|
||||
|
||||
// IsRequestThrottled tests whether err wraps a client.TritonError with
|
||||
// code RequestThrottled
|
||||
func IsRequestThrottled(err error) bool {
|
||||
return isSpecificError(err, "RequestThrottled")
|
||||
}
|
||||
|
||||
// IsRequestTooLarge tests whether err wraps a client.TritonError with
|
||||
// code RequestTooLarge
|
||||
func IsRequestTooLarge(err error) bool {
|
||||
return isSpecificError(err, "RequestTooLarge")
|
||||
}
|
||||
|
||||
// IsRequestMoved tests whether err wraps a client.TritonError with
|
||||
// code RequestMoved
|
||||
func IsRequestMoved(err error) bool {
|
||||
return isSpecificError(err, "RequestMoved")
|
||||
}
|
||||
|
||||
// IsResourceFound tests whether err wraps a client.TritonError with code ResourceFound
|
||||
func IsResourceFound(err error) bool {
|
||||
return isSpecificError(err, "ResourceFound")
|
||||
}
|
||||
|
||||
// IsResourceNotFound tests whether err wraps a client.TritonError with
|
||||
// code ResourceNotFound
|
||||
func IsResourceNotFound(err error) bool {
|
||||
return isSpecificError(err, "ResourceNotFound")
|
||||
}
|
||||
|
||||
// IsUnknownError tests whether err wraps a client.TritonError with
|
||||
// code UnknownError
|
||||
func IsUnknownError(err error) bool {
|
||||
return isSpecificError(err, "UnknownError")
|
||||
}
|
||||
|
||||
// IsEmptyResponse tests whether err wraps a client.TritonError with code
|
||||
// EmptyResponse
|
||||
func IsEmptyResponse(err error) bool {
|
||||
return isSpecificError(err, "EmptyResponse")
|
||||
}
|
||||
|
||||
// isSpecificError checks whether the error represented by err wraps
|
||||
// an underlying client.TritonError with code errorCode.
|
||||
func isSpecificError(err error, errorCode string) bool {
|
||||
if err == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
tritonErrorInterface := errwrap.GetType(err.(error), &client.TritonError{})
|
||||
if tritonErrorInterface == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
tritonErr := tritonErrorInterface.(*client.TritonError)
|
||||
if tritonErr.Code == errorCode {
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
|
@ -1,15 +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 compute
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"path"
|
||||
"time"
|
||||
|
||||
"github.com/hashicorp/errwrap"
|
||||
"github.com/joyent/triton-go/client"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
type ImagesClient struct {
|
||||
|
@ -39,7 +47,6 @@ type Image struct {
|
|||
Tags map[string]string `json:"tags"`
|
||||
EULA string `json:"eula"`
|
||||
ACL []string `json:"acl"`
|
||||
Error client.TritonError `json:"error"`
|
||||
}
|
||||
|
||||
type ListImagesInput struct {
|
||||
|
@ -53,7 +60,7 @@ type ListImagesInput struct {
|
|||
}
|
||||
|
||||
func (c *ImagesClient) List(ctx context.Context, input *ListImagesInput) ([]*Image, error) {
|
||||
path := fmt.Sprintf("/%s/images", c.client.AccountName)
|
||||
fullPath := path.Join("/", c.client.AccountName, "images")
|
||||
|
||||
query := &url.Values{}
|
||||
if input.Name != "" {
|
||||
|
@ -80,7 +87,7 @@ func (c *ImagesClient) List(ctx context.Context, input *ListImagesInput) ([]*Ima
|
|||
|
||||
reqInputs := client.RequestInput{
|
||||
Method: http.MethodGet,
|
||||
Path: path,
|
||||
Path: fullPath,
|
||||
Query: query,
|
||||
}
|
||||
respReader, err := c.client.ExecuteRequestURIParams(ctx, reqInputs)
|
||||
|
@ -88,13 +95,13 @@ func (c *ImagesClient) List(ctx context.Context, input *ListImagesInput) ([]*Ima
|
|||
defer respReader.Close()
|
||||
}
|
||||
if err != nil {
|
||||
return nil, errwrap.Wrapf("Error executing List request: {{err}}", err)
|
||||
return nil, errors.Wrap(err, "unable to list images")
|
||||
}
|
||||
|
||||
var result []*Image
|
||||
decoder := json.NewDecoder(respReader)
|
||||
if err = decoder.Decode(&result); err != nil {
|
||||
return nil, errwrap.Wrapf("Error decoding List response: {{err}}", err)
|
||||
return nil, errors.Wrap(err, "unable to decode list images response")
|
||||
}
|
||||
|
||||
return result, nil
|
||||
|
@ -105,23 +112,23 @@ type GetImageInput struct {
|
|||
}
|
||||
|
||||
func (c *ImagesClient) Get(ctx context.Context, input *GetImageInput) (*Image, error) {
|
||||
path := fmt.Sprintf("/%s/images/%s", c.client.AccountName, input.ImageID)
|
||||
fullPath := path.Join("/", c.client.AccountName, "images", input.ImageID)
|
||||
reqInputs := client.RequestInput{
|
||||
Method: http.MethodGet,
|
||||
Path: path,
|
||||
Path: fullPath,
|
||||
}
|
||||
respReader, err := c.client.ExecuteRequest(ctx, reqInputs)
|
||||
if respReader != nil {
|
||||
defer respReader.Close()
|
||||
}
|
||||
if err != nil {
|
||||
return nil, errwrap.Wrapf("Error executing Get request: {{err}}", err)
|
||||
return nil, errors.Wrap(err, "unable to get image")
|
||||
}
|
||||
|
||||
var result *Image
|
||||
decoder := json.NewDecoder(respReader)
|
||||
if err = decoder.Decode(&result); err != nil {
|
||||
return nil, errwrap.Wrapf("Error decoding Get response: {{err}}", err)
|
||||
return nil, errors.Wrap(err, "unable to decode get image response")
|
||||
}
|
||||
|
||||
return result, nil
|
||||
|
@ -132,17 +139,17 @@ type DeleteImageInput struct {
|
|||
}
|
||||
|
||||
func (c *ImagesClient) Delete(ctx context.Context, input *DeleteImageInput) error {
|
||||
path := fmt.Sprintf("/%s/images/%s", c.client.AccountName, input.ImageID)
|
||||
fullPath := path.Join("/", c.client.AccountName, "images", input.ImageID)
|
||||
reqInputs := client.RequestInput{
|
||||
Method: http.MethodDelete,
|
||||
Path: path,
|
||||
Path: fullPath,
|
||||
}
|
||||
respReader, err := c.client.ExecuteRequest(ctx, reqInputs)
|
||||
if respReader != nil {
|
||||
defer respReader.Close()
|
||||
}
|
||||
if err != nil {
|
||||
return errwrap.Wrapf("Error executing Delete request: {{err}}", err)
|
||||
return errors.Wrap(err, "unable to delete image")
|
||||
}
|
||||
|
||||
return nil
|
||||
|
@ -160,14 +167,13 @@ type MantaLocation struct {
|
|||
}
|
||||
|
||||
func (c *ImagesClient) Export(ctx context.Context, input *ExportImageInput) (*MantaLocation, error) {
|
||||
path := fmt.Sprintf("/%s/images/%s", c.client.AccountName, input.ImageID)
|
||||
fullPath := path.Join("/", c.client.AccountName, "images", input.ImageID)
|
||||
query := &url.Values{}
|
||||
query.Set("action", "export")
|
||||
query.Set("manta_path", input.MantaPath)
|
||||
|
||||
reqInputs := client.RequestInput{
|
||||
Method: http.MethodGet,
|
||||
Path: path,
|
||||
Method: http.MethodPost,
|
||||
Path: fullPath,
|
||||
Query: query,
|
||||
}
|
||||
respReader, err := c.client.ExecuteRequestURIParams(ctx, reqInputs)
|
||||
|
@ -175,13 +181,13 @@ func (c *ImagesClient) Export(ctx context.Context, input *ExportImageInput) (*Ma
|
|||
defer respReader.Close()
|
||||
}
|
||||
if err != nil {
|
||||
return nil, errwrap.Wrapf("Error executing Get request: {{err}}", err)
|
||||
return nil, errors.Wrap(err, "unable to export image")
|
||||
}
|
||||
|
||||
var result *MantaLocation
|
||||
decoder := json.NewDecoder(respReader)
|
||||
if err = decoder.Decode(&result); err != nil {
|
||||
return nil, errwrap.Wrapf("Error decoding Get response: {{err}}", err)
|
||||
return nil, errors.Wrap(err, "unable to decode export image response")
|
||||
}
|
||||
|
||||
return result, nil
|
||||
|
@ -199,10 +205,10 @@ type CreateImageFromMachineInput struct {
|
|||
}
|
||||
|
||||
func (c *ImagesClient) CreateFromMachine(ctx context.Context, input *CreateImageFromMachineInput) (*Image, error) {
|
||||
path := fmt.Sprintf("/%s/images", c.client.AccountName)
|
||||
fullPath := path.Join("/", c.client.AccountName, "images")
|
||||
reqInputs := client.RequestInput{
|
||||
Method: http.MethodPost,
|
||||
Path: path,
|
||||
Path: fullPath,
|
||||
Body: input,
|
||||
}
|
||||
respReader, err := c.client.ExecuteRequest(ctx, reqInputs)
|
||||
|
@ -210,13 +216,13 @@ func (c *ImagesClient) CreateFromMachine(ctx context.Context, input *CreateImage
|
|||
defer respReader.Close()
|
||||
}
|
||||
if err != nil {
|
||||
return nil, errwrap.Wrapf("Error executing CreateFromMachine request: {{err}}", err)
|
||||
return nil, errors.Wrap(err, "unable to create machine from image")
|
||||
}
|
||||
|
||||
var result *Image
|
||||
decoder := json.NewDecoder(respReader)
|
||||
if err = decoder.Decode(&result); err != nil {
|
||||
return nil, errwrap.Wrapf("Error decoding CreateFromMachine response: {{err}}", err)
|
||||
return nil, errors.Wrap(err, "unable to decode create machine from image response")
|
||||
}
|
||||
|
||||
return result, nil
|
||||
|
@ -224,7 +230,7 @@ func (c *ImagesClient) CreateFromMachine(ctx context.Context, input *CreateImage
|
|||
|
||||
type UpdateImageInput struct {
|
||||
ImageID string `json:"-"`
|
||||
Name string `json:"name"`
|
||||
Name string `json:"name,omitempty"`
|
||||
Version string `json:"version,omitempty"`
|
||||
Description string `json:"description,omitempty"`
|
||||
HomePage string `json:"homepage,omitempty"`
|
||||
|
@ -234,13 +240,13 @@ type UpdateImageInput struct {
|
|||
}
|
||||
|
||||
func (c *ImagesClient) Update(ctx context.Context, input *UpdateImageInput) (*Image, error) {
|
||||
path := fmt.Sprintf("/%s/images/%s", c.client.AccountName, input.ImageID)
|
||||
fullPath := path.Join("/", c.client.AccountName, "images", input.ImageID)
|
||||
query := &url.Values{}
|
||||
query.Set("action", "update")
|
||||
|
||||
reqInputs := client.RequestInput{
|
||||
Method: http.MethodPost,
|
||||
Path: path,
|
||||
Path: fullPath,
|
||||
Query: query,
|
||||
Body: input,
|
||||
}
|
||||
|
@ -249,13 +255,13 @@ func (c *ImagesClient) Update(ctx context.Context, input *UpdateImageInput) (*Im
|
|||
defer respReader.Close()
|
||||
}
|
||||
if err != nil {
|
||||
return nil, errwrap.Wrapf("Error executing Update request: {{err}}", err)
|
||||
return nil, errors.Wrap(err, "unable to update image")
|
||||
}
|
||||
|
||||
var result *Image
|
||||
decoder := json.NewDecoder(respReader)
|
||||
if err = decoder.Decode(&result); err != nil {
|
||||
return nil, errwrap.Wrapf("Error decoding Update response: {{err}}", err)
|
||||
return nil, errors.Wrap(err, "unable to decode update image response")
|
||||
}
|
||||
|
||||
return result, nil
|
||||
|
|
|
@ -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 compute
|
||||
|
||||
import (
|
||||
|
@ -7,11 +15,13 @@ import (
|
|||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"path"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/hashicorp/errwrap"
|
||||
"github.com/joyent/triton-go/client"
|
||||
"github.com/joyent/triton-go/errors"
|
||||
pkgerrors "github.com/pkg/errors"
|
||||
)
|
||||
|
||||
type InstancesClient struct {
|
||||
|
@ -33,6 +43,13 @@ type InstanceCNS struct {
|
|||
Services []string
|
||||
}
|
||||
|
||||
type InstanceVolume struct {
|
||||
Name string `json:"name,omitempty"`
|
||||
Type string `json:"type,omitempty"`
|
||||
Mode string `json:"mode,omitempty"`
|
||||
Mountpoint string `json:"mountpoint,omitempty"`
|
||||
}
|
||||
|
||||
type Instance struct {
|
||||
ID string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
|
@ -88,41 +105,40 @@ func (gmi *GetInstanceInput) Validate() error {
|
|||
|
||||
func (c *InstancesClient) Get(ctx context.Context, input *GetInstanceInput) (*Instance, error) {
|
||||
if err := input.Validate(); err != nil {
|
||||
return nil, errwrap.Wrapf("unable to get machine: {{err}}", err)
|
||||
return nil, pkgerrors.Wrap(err, "unable to get machine")
|
||||
}
|
||||
|
||||
path := fmt.Sprintf("/%s/machines/%s", c.client.AccountName, input.ID)
|
||||
fullPath := path.Join("/", c.client.AccountName, "machines", input.ID)
|
||||
reqInputs := client.RequestInput{
|
||||
Method: http.MethodGet,
|
||||
Path: path,
|
||||
Path: fullPath,
|
||||
}
|
||||
response, err := c.client.ExecuteRequestRaw(ctx, reqInputs)
|
||||
if response == nil {
|
||||
return nil, errwrap.Wrapf("Error executing Get request: {{err}}", err)
|
||||
return nil, pkgerrors.Wrap(err, "unable to get machine")
|
||||
}
|
||||
if response.Body != nil {
|
||||
defer response.Body.Close()
|
||||
}
|
||||
if response.StatusCode == http.StatusNotFound || response.StatusCode == http.StatusGone {
|
||||
return nil, &client.TritonError{
|
||||
return nil, &errors.APIError{
|
||||
StatusCode: response.StatusCode,
|
||||
Code: "ResourceNotFound",
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
return nil, errwrap.Wrapf("Error executing Get request: {{err}}",
|
||||
c.client.DecodeError(response.StatusCode, response.Body))
|
||||
return nil, pkgerrors.Wrap(err, "unable to get machine")
|
||||
}
|
||||
|
||||
var result *_Instance
|
||||
decoder := json.NewDecoder(response.Body)
|
||||
if err = decoder.Decode(&result); err != nil {
|
||||
return nil, errwrap.Wrapf("Error decoding Get response: {{err}}", err)
|
||||
return nil, pkgerrors.Wrap(err, "unable to decode get machine response")
|
||||
}
|
||||
|
||||
native, err := result.toNative()
|
||||
if err != nil {
|
||||
return nil, errwrap.Wrapf("unable to convert API response for instances to native type: {{err}}", err)
|
||||
return nil, pkgerrors.Wrap(err, "unable to decode get machine response")
|
||||
}
|
||||
|
||||
return native, nil
|
||||
|
@ -144,7 +160,7 @@ type ListInstancesInput struct {
|
|||
}
|
||||
|
||||
func (c *InstancesClient) List(ctx context.Context, input *ListInstancesInput) ([]*Instance, error) {
|
||||
path := fmt.Sprintf("/%s/machines", c.client.AccountName)
|
||||
fullPath := path.Join("/", c.client.AccountName, "machines")
|
||||
|
||||
query := &url.Values{}
|
||||
if input.Brand != "" {
|
||||
|
@ -180,25 +196,25 @@ func (c *InstancesClient) List(ctx context.Context, input *ListInstancesInput) (
|
|||
|
||||
reqInputs := client.RequestInput{
|
||||
Method: http.MethodGet,
|
||||
Path: path,
|
||||
Path: fullPath,
|
||||
Query: query,
|
||||
}
|
||||
respReader, err := c.client.ExecuteRequest(ctx, reqInputs)
|
||||
if err != nil {
|
||||
return nil, errwrap.Wrapf("Error executing List request: {{err}}", err)
|
||||
return nil, pkgerrors.Wrap(err, "unable to list machines")
|
||||
}
|
||||
|
||||
var results []*_Instance
|
||||
decoder := json.NewDecoder(respReader)
|
||||
if err = decoder.Decode(&results); err != nil {
|
||||
return nil, errwrap.Wrapf("Error decoding List response: {{err}}", err)
|
||||
return nil, pkgerrors.Wrap(err, "unable to decode list machines response")
|
||||
}
|
||||
|
||||
machines := make([]*Instance, 0, len(results))
|
||||
for _, machineAPI := range results {
|
||||
native, err := machineAPI.toNative()
|
||||
if err != nil {
|
||||
return nil, errwrap.Wrapf("unable to convert API response for instances to native type: {{err}}", err)
|
||||
return nil, pkgerrors.Wrap(err, "unable to decode list machines response")
|
||||
}
|
||||
machines = append(machines, native)
|
||||
}
|
||||
|
@ -219,6 +235,7 @@ type CreateInstanceInput struct {
|
|||
Tags map[string]string
|
||||
FirewallEnabled bool
|
||||
CNS InstanceCNS
|
||||
Volumes []InstanceVolume
|
||||
}
|
||||
|
||||
func (input *CreateInstanceInput) toAPI() (map[string]interface{}, error) {
|
||||
|
@ -243,6 +260,10 @@ func (input *CreateInstanceInput) toAPI() (map[string]interface{}, error) {
|
|||
result["networks"] = input.Networks
|
||||
}
|
||||
|
||||
if len(input.Volumes) > 0 {
|
||||
result["volumes"] = input.Volumes
|
||||
}
|
||||
|
||||
// validate that affinity and locality are not included together
|
||||
hasAffinity := len(input.Affinity) > 0
|
||||
hasLocality := len(input.LocalityNear) > 0 || len(input.LocalityFar) > 0
|
||||
|
@ -286,15 +307,15 @@ func (input *CreateInstanceInput) toAPI() (map[string]interface{}, error) {
|
|||
}
|
||||
|
||||
func (c *InstancesClient) Create(ctx context.Context, input *CreateInstanceInput) (*Instance, error) {
|
||||
path := fmt.Sprintf("/%s/machines", c.client.AccountName)
|
||||
fullPath := path.Join("/", c.client.AccountName, "machines")
|
||||
body, err := input.toAPI()
|
||||
if err != nil {
|
||||
return nil, errwrap.Wrapf("Error preparing Create request: {{err}}", err)
|
||||
return nil, pkgerrors.Wrap(err, "unable to prepare for machine creation")
|
||||
}
|
||||
|
||||
reqInputs := client.RequestInput{
|
||||
Method: http.MethodPost,
|
||||
Path: path,
|
||||
Path: fullPath,
|
||||
Body: body,
|
||||
}
|
||||
respReader, err := c.client.ExecuteRequest(ctx, reqInputs)
|
||||
|
@ -302,13 +323,13 @@ func (c *InstancesClient) Create(ctx context.Context, input *CreateInstanceInput
|
|||
defer respReader.Close()
|
||||
}
|
||||
if err != nil {
|
||||
return nil, errwrap.Wrapf("Error executing Create request: {{err}}", err)
|
||||
return nil, pkgerrors.Wrap(err, "unable to create machine")
|
||||
}
|
||||
|
||||
var result *Instance
|
||||
decoder := json.NewDecoder(respReader)
|
||||
if err = decoder.Decode(&result); err != nil {
|
||||
return nil, errwrap.Wrapf("Error decoding Create response: {{err}}", err)
|
||||
return nil, pkgerrors.Wrap(err, "unable to decode create machine response")
|
||||
}
|
||||
|
||||
return result, nil
|
||||
|
@ -319,14 +340,14 @@ type DeleteInstanceInput struct {
|
|||
}
|
||||
|
||||
func (c *InstancesClient) Delete(ctx context.Context, input *DeleteInstanceInput) error {
|
||||
path := fmt.Sprintf("/%s/machines/%s", c.client.AccountName, input.ID)
|
||||
fullPath := path.Join("/", c.client.AccountName, "machines", input.ID)
|
||||
reqInputs := client.RequestInput{
|
||||
Method: http.MethodDelete,
|
||||
Path: path,
|
||||
Path: fullPath,
|
||||
}
|
||||
response, err := c.client.ExecuteRequestRaw(ctx, reqInputs)
|
||||
if response == nil {
|
||||
return fmt.Errorf("Delete request has empty response")
|
||||
return pkgerrors.Wrap(err, "unable to delete machine")
|
||||
}
|
||||
if response.Body != nil {
|
||||
defer response.Body.Close()
|
||||
|
@ -335,8 +356,7 @@ func (c *InstancesClient) Delete(ctx context.Context, input *DeleteInstanceInput
|
|||
return nil
|
||||
}
|
||||
if err != nil {
|
||||
return errwrap.Wrapf("Error executing Delete request: {{err}}",
|
||||
c.client.DecodeError(response.StatusCode, response.Body))
|
||||
return pkgerrors.Wrap(err, "unable to decode delete machine response")
|
||||
}
|
||||
|
||||
return nil
|
||||
|
@ -347,12 +367,15 @@ type DeleteTagsInput struct {
|
|||
}
|
||||
|
||||
func (c *InstancesClient) DeleteTags(ctx context.Context, input *DeleteTagsInput) error {
|
||||
path := fmt.Sprintf("/%s/machines/%s/tags", c.client.AccountName, input.ID)
|
||||
fullPath := path.Join("/", c.client.AccountName, "machines", input.ID, "tags")
|
||||
reqInputs := client.RequestInput{
|
||||
Method: http.MethodDelete,
|
||||
Path: path,
|
||||
Path: fullPath,
|
||||
}
|
||||
response, err := c.client.ExecuteRequestRaw(ctx, reqInputs)
|
||||
if err != nil {
|
||||
return pkgerrors.Wrap(err, "unable to delete tags from machine")
|
||||
}
|
||||
if response == nil {
|
||||
return fmt.Errorf("DeleteTags request has empty response")
|
||||
}
|
||||
|
@ -362,10 +385,6 @@ func (c *InstancesClient) DeleteTags(ctx context.Context, input *DeleteTagsInput
|
|||
if response.StatusCode == http.StatusNotFound {
|
||||
return nil
|
||||
}
|
||||
if err != nil {
|
||||
return errwrap.Wrapf("Error executing DeleteTags request: {{err}}",
|
||||
c.client.DecodeError(response.StatusCode, response.Body))
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
@ -376,12 +395,15 @@ type DeleteTagInput struct {
|
|||
}
|
||||
|
||||
func (c *InstancesClient) DeleteTag(ctx context.Context, input *DeleteTagInput) error {
|
||||
path := fmt.Sprintf("/%s/machines/%s/tags/%s", c.client.AccountName, input.ID, input.Key)
|
||||
fullPath := path.Join("/", c.client.AccountName, "machines", input.ID, "tags", input.Key)
|
||||
reqInputs := client.RequestInput{
|
||||
Method: http.MethodDelete,
|
||||
Path: path,
|
||||
Path: fullPath,
|
||||
}
|
||||
response, err := c.client.ExecuteRequestRaw(ctx, reqInputs)
|
||||
if err != nil {
|
||||
return pkgerrors.Wrap(err, "unable to delete tag from machine")
|
||||
}
|
||||
if response == nil {
|
||||
return fmt.Errorf("DeleteTag request has empty response")
|
||||
}
|
||||
|
@ -391,10 +413,6 @@ func (c *InstancesClient) DeleteTag(ctx context.Context, input *DeleteTagInput)
|
|||
if response.StatusCode == http.StatusNotFound {
|
||||
return nil
|
||||
}
|
||||
if err != nil {
|
||||
return errwrap.Wrapf("Error executing DeleteTag request: {{err}}",
|
||||
c.client.DecodeError(response.StatusCode, response.Body))
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
@ -405,7 +423,7 @@ type RenameInstanceInput struct {
|
|||
}
|
||||
|
||||
func (c *InstancesClient) Rename(ctx context.Context, input *RenameInstanceInput) error {
|
||||
path := fmt.Sprintf("/%s/machines/%s", c.client.AccountName, input.ID)
|
||||
fullPath := path.Join("/", c.client.AccountName, "machines", input.ID)
|
||||
|
||||
params := &url.Values{}
|
||||
params.Set("action", "rename")
|
||||
|
@ -413,7 +431,7 @@ func (c *InstancesClient) Rename(ctx context.Context, input *RenameInstanceInput
|
|||
|
||||
reqInputs := client.RequestInput{
|
||||
Method: http.MethodPost,
|
||||
Path: path,
|
||||
Path: fullPath,
|
||||
Query: params,
|
||||
}
|
||||
respReader, err := c.client.ExecuteRequestURIParams(ctx, reqInputs)
|
||||
|
@ -421,7 +439,7 @@ func (c *InstancesClient) Rename(ctx context.Context, input *RenameInstanceInput
|
|||
defer respReader.Close()
|
||||
}
|
||||
if err != nil {
|
||||
return errwrap.Wrapf("Error executing Rename request: {{err}}", err)
|
||||
return pkgerrors.Wrap(err, "unable to rename machine")
|
||||
}
|
||||
|
||||
return nil
|
||||
|
@ -445,10 +463,10 @@ func (input ReplaceTagsInput) toAPI() map[string]interface{} {
|
|||
}
|
||||
|
||||
func (c *InstancesClient) ReplaceTags(ctx context.Context, input *ReplaceTagsInput) error {
|
||||
path := fmt.Sprintf("/%s/machines/%s/tags", c.client.AccountName, input.ID)
|
||||
fullPath := path.Join("/", c.client.AccountName, "machines", input.ID, "tags")
|
||||
reqInputs := client.RequestInput{
|
||||
Method: http.MethodPut,
|
||||
Path: path,
|
||||
Path: fullPath,
|
||||
Body: input.toAPI(),
|
||||
}
|
||||
respReader, err := c.client.ExecuteRequest(ctx, reqInputs)
|
||||
|
@ -456,7 +474,7 @@ func (c *InstancesClient) ReplaceTags(ctx context.Context, input *ReplaceTagsInp
|
|||
defer respReader.Close()
|
||||
}
|
||||
if err != nil {
|
||||
return errwrap.Wrapf("Error executing ReplaceTags request: {{err}}", err)
|
||||
return pkgerrors.Wrap(err, "unable to replace machine tags")
|
||||
}
|
||||
|
||||
return nil
|
||||
|
@ -468,10 +486,10 @@ type AddTagsInput struct {
|
|||
}
|
||||
|
||||
func (c *InstancesClient) AddTags(ctx context.Context, input *AddTagsInput) error {
|
||||
path := fmt.Sprintf("/%s/machines/%s/tags", c.client.AccountName, input.ID)
|
||||
fullPath := path.Join("/", c.client.AccountName, "machines", input.ID, "tags")
|
||||
reqInputs := client.RequestInput{
|
||||
Method: http.MethodPost,
|
||||
Path: path,
|
||||
Path: fullPath,
|
||||
Body: input.Tags,
|
||||
}
|
||||
respReader, err := c.client.ExecuteRequest(ctx, reqInputs)
|
||||
|
@ -479,7 +497,7 @@ func (c *InstancesClient) AddTags(ctx context.Context, input *AddTagsInput) erro
|
|||
defer respReader.Close()
|
||||
}
|
||||
if err != nil {
|
||||
return errwrap.Wrapf("Error executing AddTags request: {{err}}", err)
|
||||
return pkgerrors.Wrap(err, "unable to add tags to machine")
|
||||
}
|
||||
|
||||
return nil
|
||||
|
@ -491,23 +509,23 @@ type GetTagInput struct {
|
|||
}
|
||||
|
||||
func (c *InstancesClient) GetTag(ctx context.Context, input *GetTagInput) (string, error) {
|
||||
path := fmt.Sprintf("/%s/machines/%s/tags/%s", c.client.AccountName, input.ID, input.Key)
|
||||
fullPath := path.Join("/", c.client.AccountName, "machines", input.ID, "tags", input.Key)
|
||||
reqInputs := client.RequestInput{
|
||||
Method: http.MethodGet,
|
||||
Path: path,
|
||||
Path: fullPath,
|
||||
}
|
||||
respReader, err := c.client.ExecuteRequest(ctx, reqInputs)
|
||||
if err != nil {
|
||||
return "", pkgerrors.Wrap(err, "unable to get tag")
|
||||
}
|
||||
if respReader != nil {
|
||||
defer respReader.Close()
|
||||
}
|
||||
if err != nil {
|
||||
return "", errwrap.Wrapf("Error executing GetTag request: {{err}}", err)
|
||||
}
|
||||
|
||||
var result string
|
||||
decoder := json.NewDecoder(respReader)
|
||||
if err = decoder.Decode(&result); err != nil {
|
||||
return "", errwrap.Wrapf("Error decoding GetTag response: {{err}}", err)
|
||||
return "", pkgerrors.Wrap(err, "unable to decode get tag response")
|
||||
}
|
||||
|
||||
return result, nil
|
||||
|
@ -518,23 +536,23 @@ type ListTagsInput struct {
|
|||
}
|
||||
|
||||
func (c *InstancesClient) ListTags(ctx context.Context, input *ListTagsInput) (map[string]interface{}, error) {
|
||||
path := fmt.Sprintf("/%s/machines/%s/tags", c.client.AccountName, input.ID)
|
||||
fullPath := path.Join("/", c.client.AccountName, "machines", input.ID, "tags")
|
||||
reqInputs := client.RequestInput{
|
||||
Method: http.MethodGet,
|
||||
Path: path,
|
||||
Path: fullPath,
|
||||
}
|
||||
respReader, err := c.client.ExecuteRequest(ctx, reqInputs)
|
||||
if respReader != nil {
|
||||
defer respReader.Close()
|
||||
}
|
||||
if err != nil {
|
||||
return nil, errwrap.Wrapf("Error executing ListTags request: {{err}}", err)
|
||||
return nil, pkgerrors.Wrap(err, "unable to list machine tags")
|
||||
}
|
||||
|
||||
var result map[string]interface{}
|
||||
decoder := json.NewDecoder(respReader)
|
||||
if err = decoder.Decode(&result); err != nil {
|
||||
return nil, errwrap.Wrapf("Error decoding ListTags response: {{err}}", err)
|
||||
return nil, pkgerrors.Wrap(err, "unable decode list machine tags response")
|
||||
}
|
||||
|
||||
_, tags := tagsExtractMeta(result)
|
||||
|
@ -552,30 +570,28 @@ func (c *InstancesClient) GetMetadata(ctx context.Context, input *GetMetadataInp
|
|||
return "", fmt.Errorf("Missing metadata Key from input: %s", input.Key)
|
||||
}
|
||||
|
||||
path := fmt.Sprintf("/%s/machines/%s/metadata/%s", c.client.AccountName, input.ID, input.Key)
|
||||
fullPath := path.Join("/", c.client.AccountName, "machines", input.ID, "metadata", input.Key)
|
||||
reqInputs := client.RequestInput{
|
||||
Method: http.MethodGet,
|
||||
Path: path,
|
||||
Path: fullPath,
|
||||
}
|
||||
response, err := c.client.ExecuteRequestRaw(ctx, reqInputs)
|
||||
if err != nil {
|
||||
return "", pkgerrors.Wrap(err, "unable to get machine metadata")
|
||||
}
|
||||
if response != nil {
|
||||
defer response.Body.Close()
|
||||
}
|
||||
if response.StatusCode == http.StatusNotFound || response.StatusCode == http.StatusGone {
|
||||
return "", &client.TritonError{
|
||||
return "", &errors.APIError{
|
||||
StatusCode: response.StatusCode,
|
||||
Code: "ResourceNotFound",
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
return "", errwrap.Wrapf("Error executing Get request: {{err}}",
|
||||
c.client.DecodeError(response.StatusCode, response.Body))
|
||||
}
|
||||
|
||||
body, err := ioutil.ReadAll(response.Body)
|
||||
if err != nil {
|
||||
return "", errwrap.Wrapf("Error unwrapping request body: {{err}}",
|
||||
c.client.DecodeError(response.StatusCode, response.Body))
|
||||
return "", pkgerrors.Wrap(err, "unable to decode get machine metadata response")
|
||||
}
|
||||
|
||||
return fmt.Sprintf("%s", body), nil
|
||||
|
@ -587,7 +603,7 @@ type ListMetadataInput struct {
|
|||
}
|
||||
|
||||
func (c *InstancesClient) ListMetadata(ctx context.Context, input *ListMetadataInput) (map[string]string, error) {
|
||||
path := fmt.Sprintf("/%s/machines/%s/metadata", c.client.AccountName, input.ID)
|
||||
fullPath := path.Join("/", c.client.AccountName, "machines", input.ID, "metadata")
|
||||
|
||||
query := &url.Values{}
|
||||
if input.Credentials {
|
||||
|
@ -596,7 +612,7 @@ func (c *InstancesClient) ListMetadata(ctx context.Context, input *ListMetadataI
|
|||
|
||||
reqInputs := client.RequestInput{
|
||||
Method: http.MethodGet,
|
||||
Path: path,
|
||||
Path: fullPath,
|
||||
Query: query,
|
||||
}
|
||||
respReader, err := c.client.ExecuteRequest(ctx, reqInputs)
|
||||
|
@ -604,13 +620,13 @@ func (c *InstancesClient) ListMetadata(ctx context.Context, input *ListMetadataI
|
|||
defer respReader.Close()
|
||||
}
|
||||
if err != nil {
|
||||
return nil, errwrap.Wrapf("Error executing ListMetadata request: {{err}}", err)
|
||||
return nil, pkgerrors.Wrap(err, "unable to list machine metadata")
|
||||
}
|
||||
|
||||
var result map[string]string
|
||||
decoder := json.NewDecoder(respReader)
|
||||
if err = decoder.Decode(&result); err != nil {
|
||||
return nil, errwrap.Wrapf("Error decoding ListMetadata response: {{err}}", err)
|
||||
return nil, pkgerrors.Wrap(err, "unable to decode list machine metadata response")
|
||||
}
|
||||
|
||||
return result, nil
|
||||
|
@ -622,10 +638,10 @@ type UpdateMetadataInput struct {
|
|||
}
|
||||
|
||||
func (c *InstancesClient) UpdateMetadata(ctx context.Context, input *UpdateMetadataInput) (map[string]string, error) {
|
||||
path := fmt.Sprintf("/%s/machines/%s/metadata", c.client.AccountName, input.ID)
|
||||
fullPath := path.Join("/", c.client.AccountName, "machines", input.ID, "metadata")
|
||||
reqInputs := client.RequestInput{
|
||||
Method: http.MethodPost,
|
||||
Path: path,
|
||||
Path: fullPath,
|
||||
Body: input.Metadata,
|
||||
}
|
||||
respReader, err := c.client.ExecuteRequest(ctx, reqInputs)
|
||||
|
@ -633,13 +649,13 @@ func (c *InstancesClient) UpdateMetadata(ctx context.Context, input *UpdateMetad
|
|||
defer respReader.Close()
|
||||
}
|
||||
if err != nil {
|
||||
return nil, errwrap.Wrapf("Error executing UpdateMetadata request: {{err}}", err)
|
||||
return nil, pkgerrors.Wrap(err, "unable to update machine metadata")
|
||||
}
|
||||
|
||||
var result map[string]string
|
||||
decoder := json.NewDecoder(respReader)
|
||||
if err = decoder.Decode(&result); err != nil {
|
||||
return nil, errwrap.Wrapf("Error decoding UpdateMetadata response: {{err}}", err)
|
||||
return nil, pkgerrors.Wrap(err, "unable to decode update machine metadata response")
|
||||
}
|
||||
|
||||
return result, nil
|
||||
|
@ -656,18 +672,18 @@ func (c *InstancesClient) DeleteMetadata(ctx context.Context, input *DeleteMetad
|
|||
return fmt.Errorf("Missing metadata Key from input: %s", input.Key)
|
||||
}
|
||||
|
||||
path := fmt.Sprintf("/%s/machines/%s/metadata/%s", c.client.AccountName, input.ID, input.Key)
|
||||
fullPath := path.Join("/", c.client.AccountName, "machines", input.ID, "metadata", input.Key)
|
||||
reqInputs := client.RequestInput{
|
||||
Method: http.MethodDelete,
|
||||
Path: path,
|
||||
Path: fullPath,
|
||||
}
|
||||
respReader, err := c.client.ExecuteRequest(ctx, reqInputs)
|
||||
if err != nil {
|
||||
return pkgerrors.Wrap(err, "unable to delete machine metadata")
|
||||
}
|
||||
if respReader != nil {
|
||||
defer respReader.Close()
|
||||
}
|
||||
if err != nil {
|
||||
return errwrap.Wrapf("Error executing DeleteMetadata request: {{err}}", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
@ -678,18 +694,18 @@ type DeleteAllMetadataInput struct {
|
|||
|
||||
// DeleteAllMetadata deletes all metadata keys from this instance
|
||||
func (c *InstancesClient) DeleteAllMetadata(ctx context.Context, input *DeleteAllMetadataInput) error {
|
||||
path := fmt.Sprintf("/%s/machines/%s/metadata", c.client.AccountName, input.ID)
|
||||
fullPath := path.Join("/", c.client.AccountName, "machines", input.ID, "metadata")
|
||||
reqInputs := client.RequestInput{
|
||||
Method: http.MethodDelete,
|
||||
Path: path,
|
||||
Path: fullPath,
|
||||
}
|
||||
respReader, err := c.client.ExecuteRequest(ctx, reqInputs)
|
||||
if err != nil {
|
||||
return pkgerrors.Wrap(err, "unable to delete all machine metadata")
|
||||
}
|
||||
if respReader != nil {
|
||||
defer respReader.Close()
|
||||
}
|
||||
if err != nil {
|
||||
return errwrap.Wrapf("Error executing DeleteAllMetadata request: {{err}}", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
@ -700,7 +716,7 @@ type ResizeInstanceInput struct {
|
|||
}
|
||||
|
||||
func (c *InstancesClient) Resize(ctx context.Context, input *ResizeInstanceInput) error {
|
||||
path := fmt.Sprintf("/%s/machines/%s", c.client.AccountName, input.ID)
|
||||
fullPath := path.Join("/", c.client.AccountName, "machines", input.ID)
|
||||
|
||||
params := &url.Values{}
|
||||
params.Set("action", "resize")
|
||||
|
@ -708,7 +724,7 @@ func (c *InstancesClient) Resize(ctx context.Context, input *ResizeInstanceInput
|
|||
|
||||
reqInputs := client.RequestInput{
|
||||
Method: http.MethodPost,
|
||||
Path: path,
|
||||
Path: fullPath,
|
||||
Query: params,
|
||||
}
|
||||
respReader, err := c.client.ExecuteRequestURIParams(ctx, reqInputs)
|
||||
|
@ -716,7 +732,7 @@ func (c *InstancesClient) Resize(ctx context.Context, input *ResizeInstanceInput
|
|||
defer respReader.Close()
|
||||
}
|
||||
if err != nil {
|
||||
return errwrap.Wrapf("Error executing Resize request: {{err}}", err)
|
||||
return pkgerrors.Wrap(err, "unable to resize machine")
|
||||
}
|
||||
|
||||
return nil
|
||||
|
@ -727,14 +743,14 @@ type EnableFirewallInput struct {
|
|||
}
|
||||
|
||||
func (c *InstancesClient) EnableFirewall(ctx context.Context, input *EnableFirewallInput) error {
|
||||
path := fmt.Sprintf("/%s/machines/%s", c.client.AccountName, input.ID)
|
||||
fullPath := path.Join("/", c.client.AccountName, "machines", input.ID)
|
||||
|
||||
params := &url.Values{}
|
||||
params.Set("action", "enable_firewall")
|
||||
|
||||
reqInputs := client.RequestInput{
|
||||
Method: http.MethodPost,
|
||||
Path: path,
|
||||
Path: fullPath,
|
||||
Query: params,
|
||||
}
|
||||
respReader, err := c.client.ExecuteRequestURIParams(ctx, reqInputs)
|
||||
|
@ -742,7 +758,7 @@ func (c *InstancesClient) EnableFirewall(ctx context.Context, input *EnableFirew
|
|||
defer respReader.Close()
|
||||
}
|
||||
if err != nil {
|
||||
return errwrap.Wrapf("Error executing EnableFirewall request: {{err}}", err)
|
||||
return pkgerrors.Wrap(err, "unable to enable machine firewall")
|
||||
}
|
||||
|
||||
return nil
|
||||
|
@ -753,14 +769,14 @@ type DisableFirewallInput struct {
|
|||
}
|
||||
|
||||
func (c *InstancesClient) DisableFirewall(ctx context.Context, input *DisableFirewallInput) error {
|
||||
path := fmt.Sprintf("/%s/machines/%s", c.client.AccountName, input.ID)
|
||||
fullPath := path.Join("/", c.client.AccountName, "machines", input.ID)
|
||||
|
||||
params := &url.Values{}
|
||||
params.Set("action", "disable_firewall")
|
||||
|
||||
reqInputs := client.RequestInput{
|
||||
Method: http.MethodPost,
|
||||
Path: path,
|
||||
Path: fullPath,
|
||||
Query: params,
|
||||
}
|
||||
respReader, err := c.client.ExecuteRequestURIParams(ctx, reqInputs)
|
||||
|
@ -768,7 +784,7 @@ func (c *InstancesClient) DisableFirewall(ctx context.Context, input *DisableFir
|
|||
defer respReader.Close()
|
||||
}
|
||||
if err != nil {
|
||||
return errwrap.Wrapf("Error executing DisableFirewall request: {{err}}", err)
|
||||
return pkgerrors.Wrap(err, "unable to disable machine firewall")
|
||||
}
|
||||
|
||||
return nil
|
||||
|
@ -779,23 +795,23 @@ type ListNICsInput struct {
|
|||
}
|
||||
|
||||
func (c *InstancesClient) ListNICs(ctx context.Context, input *ListNICsInput) ([]*NIC, error) {
|
||||
path := fmt.Sprintf("/%s/machines/%s/nics", c.client.AccountName, input.InstanceID)
|
||||
fullPath := path.Join("/", c.client.AccountName, "machines", input.InstanceID, "nics")
|
||||
reqInputs := client.RequestInput{
|
||||
Method: http.MethodGet,
|
||||
Path: path,
|
||||
Path: fullPath,
|
||||
}
|
||||
respReader, err := c.client.ExecuteRequest(ctx, reqInputs)
|
||||
if respReader != nil {
|
||||
defer respReader.Close()
|
||||
}
|
||||
if err != nil {
|
||||
return nil, errwrap.Wrapf("Error executing ListNICs request: {{err}}", err)
|
||||
return nil, pkgerrors.Wrap(err, "unable to list machine NICs")
|
||||
}
|
||||
|
||||
var result []*NIC
|
||||
decoder := json.NewDecoder(respReader)
|
||||
if err = decoder.Decode(&result); err != nil {
|
||||
return nil, errwrap.Wrapf("Error decoding ListNICs response: {{err}}", err)
|
||||
return nil, pkgerrors.Wrap(err, "unable to decode list machine NICs response")
|
||||
}
|
||||
|
||||
return result, nil
|
||||
|
@ -808,30 +824,30 @@ type GetNICInput struct {
|
|||
|
||||
func (c *InstancesClient) GetNIC(ctx context.Context, input *GetNICInput) (*NIC, error) {
|
||||
mac := strings.Replace(input.MAC, ":", "", -1)
|
||||
path := fmt.Sprintf("/%s/machines/%s/nics/%s", c.client.AccountName, input.InstanceID, mac)
|
||||
fullPath := path.Join("/", c.client.AccountName, "machines", input.InstanceID, "nics", mac)
|
||||
reqInputs := client.RequestInput{
|
||||
Method: http.MethodGet,
|
||||
Path: path,
|
||||
Path: fullPath,
|
||||
}
|
||||
response, err := c.client.ExecuteRequestRaw(ctx, reqInputs)
|
||||
if err != nil {
|
||||
return nil, pkgerrors.Wrap(err, "unable to get machine NIC")
|
||||
}
|
||||
if response != nil {
|
||||
defer response.Body.Close()
|
||||
}
|
||||
switch response.StatusCode {
|
||||
case http.StatusNotFound:
|
||||
return nil, &client.TritonError{
|
||||
return nil, &errors.APIError{
|
||||
StatusCode: response.StatusCode,
|
||||
Code: "ResourceNotFound",
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
return nil, errwrap.Wrapf("Error executing GetNIC request: {{err}}", err)
|
||||
}
|
||||
|
||||
var result *NIC
|
||||
decoder := json.NewDecoder(response.Body)
|
||||
if err = decoder.Decode(&result); err != nil {
|
||||
return nil, errwrap.Wrapf("Error decoding ListNICs response: {{err}}", err)
|
||||
return nil, pkgerrors.Wrap(err, "unable to decode get machine NIC response")
|
||||
}
|
||||
|
||||
return result, nil
|
||||
|
@ -848,32 +864,32 @@ type AddNICInput struct {
|
|||
// until its state is set to "running". Only one NIC per network may exist.
|
||||
// Warning: this operation causes the instance to restart.
|
||||
func (c *InstancesClient) AddNIC(ctx context.Context, input *AddNICInput) (*NIC, error) {
|
||||
path := fmt.Sprintf("/%s/machines/%s/nics", c.client.AccountName, input.InstanceID)
|
||||
fullPath := path.Join("/", c.client.AccountName, "machines", input.InstanceID, "nics")
|
||||
reqInputs := client.RequestInput{
|
||||
Method: http.MethodPost,
|
||||
Path: path,
|
||||
Path: fullPath,
|
||||
Body: input,
|
||||
}
|
||||
response, err := c.client.ExecuteRequestRaw(ctx, reqInputs)
|
||||
if err != nil {
|
||||
return nil, pkgerrors.Wrap(err, "unable to add NIC to machine")
|
||||
}
|
||||
if response != nil {
|
||||
defer response.Body.Close()
|
||||
}
|
||||
switch response.StatusCode {
|
||||
case http.StatusFound:
|
||||
return nil, &client.TritonError{
|
||||
return nil, &errors.APIError{
|
||||
StatusCode: response.StatusCode,
|
||||
Code: "ResourceFound",
|
||||
Message: response.Header.Get("Location"),
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
return nil, errwrap.Wrapf("Error executing AddNIC request: {{err}}", err)
|
||||
}
|
||||
|
||||
var result *NIC
|
||||
decoder := json.NewDecoder(response.Body)
|
||||
if err = decoder.Decode(&result); err != nil {
|
||||
return nil, errwrap.Wrapf("Error decoding AddNIC response: {{err}}", err)
|
||||
return nil, pkgerrors.Wrap(err, "unable to decode add NIC to machine response")
|
||||
}
|
||||
|
||||
return result, nil
|
||||
|
@ -890,25 +906,28 @@ type RemoveNICInput struct {
|
|||
// machine to restart.
|
||||
func (c *InstancesClient) RemoveNIC(ctx context.Context, input *RemoveNICInput) error {
|
||||
mac := strings.Replace(input.MAC, ":", "", -1)
|
||||
path := fmt.Sprintf("/%s/machines/%s/nics/%s", c.client.AccountName, input.InstanceID, mac)
|
||||
fullPath := path.Join("/", c.client.AccountName, "machines", input.InstanceID, "nics", mac)
|
||||
reqInputs := client.RequestInput{
|
||||
Method: http.MethodDelete,
|
||||
Path: path,
|
||||
Path: fullPath,
|
||||
}
|
||||
response, err := c.client.ExecuteRequestRaw(ctx, reqInputs)
|
||||
if response != nil {
|
||||
if err != nil {
|
||||
return pkgerrors.Wrap(err, "unable to remove NIC from machine")
|
||||
}
|
||||
if response == nil {
|
||||
return pkgerrors.Wrap(err, "unable to remove NIC from machine")
|
||||
}
|
||||
if response.Body != nil {
|
||||
defer response.Body.Close()
|
||||
}
|
||||
switch response.StatusCode {
|
||||
case http.StatusNotFound:
|
||||
return &client.TritonError{
|
||||
return &errors.APIError{
|
||||
StatusCode: response.StatusCode,
|
||||
Code: "ResourceNotFound",
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
return errwrap.Wrapf("Error executing RemoveNIC request: {{err}}", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
@ -918,14 +937,14 @@ type StopInstanceInput struct {
|
|||
}
|
||||
|
||||
func (c *InstancesClient) Stop(ctx context.Context, input *StopInstanceInput) error {
|
||||
path := fmt.Sprintf("/%s/machines/%s", c.client.AccountName, input.InstanceID)
|
||||
fullPath := path.Join("/", c.client.AccountName, "machines", input.InstanceID)
|
||||
|
||||
params := &url.Values{}
|
||||
params.Set("action", "stop")
|
||||
|
||||
reqInputs := client.RequestInput{
|
||||
Method: http.MethodPost,
|
||||
Path: path,
|
||||
Path: fullPath,
|
||||
Query: params,
|
||||
}
|
||||
respReader, err := c.client.ExecuteRequestURIParams(ctx, reqInputs)
|
||||
|
@ -933,7 +952,7 @@ func (c *InstancesClient) Stop(ctx context.Context, input *StopInstanceInput) er
|
|||
defer respReader.Close()
|
||||
}
|
||||
if err != nil {
|
||||
return errwrap.Wrapf("Error executing Stop request: {{err}}", err)
|
||||
return pkgerrors.Wrap(err, "unable to stop machine")
|
||||
}
|
||||
|
||||
return nil
|
||||
|
@ -944,14 +963,14 @@ type StartInstanceInput struct {
|
|||
}
|
||||
|
||||
func (c *InstancesClient) Start(ctx context.Context, input *StartInstanceInput) error {
|
||||
path := fmt.Sprintf("/%s/machines/%s", c.client.AccountName, input.InstanceID)
|
||||
fullPath := path.Join("/", c.client.AccountName, "machines", input.InstanceID)
|
||||
|
||||
params := &url.Values{}
|
||||
params.Set("action", "start")
|
||||
|
||||
reqInputs := client.RequestInput{
|
||||
Method: http.MethodPost,
|
||||
Path: path,
|
||||
Path: fullPath,
|
||||
Query: params,
|
||||
}
|
||||
respReader, err := c.client.ExecuteRequestURIParams(ctx, reqInputs)
|
||||
|
@ -959,7 +978,7 @@ func (c *InstancesClient) Start(ctx context.Context, input *StartInstanceInput)
|
|||
defer respReader.Close()
|
||||
}
|
||||
if err != nil {
|
||||
return errwrap.Wrapf("Error executing Start request: {{err}}", err)
|
||||
return pkgerrors.Wrap(err, "unable to start machine")
|
||||
}
|
||||
|
||||
return nil
|
||||
|
@ -970,14 +989,14 @@ type RebootInstanceInput struct {
|
|||
}
|
||||
|
||||
func (c *InstancesClient) Reboot(ctx context.Context, input *RebootInstanceInput) error {
|
||||
path := fmt.Sprintf("/%s/machines/%s", c.client.AccountName, input.InstanceID)
|
||||
fullPath := path.Join("/", c.client.AccountName, "machines", input.InstanceID)
|
||||
|
||||
params := &url.Values{}
|
||||
params.Set("action", "reboot")
|
||||
|
||||
reqInputs := client.RequestInput{
|
||||
Method: http.MethodPost,
|
||||
Path: path,
|
||||
Path: fullPath,
|
||||
Query: params,
|
||||
}
|
||||
respReader, err := c.client.ExecuteRequestURIParams(ctx, reqInputs)
|
||||
|
@ -985,7 +1004,7 @@ func (c *InstancesClient) Reboot(ctx context.Context, input *RebootInstanceInput
|
|||
defer respReader.Close()
|
||||
}
|
||||
if err != nil {
|
||||
return errwrap.Wrapf("Error executing Start request: {{err}}", err)
|
||||
return pkgerrors.Wrap(err, "unable to reboot machine")
|
||||
}
|
||||
|
||||
return nil
|
||||
|
|
|
@ -1,13 +1,21 @@
|
|||
//
|
||||
// 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 compute
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"path"
|
||||
|
||||
"github.com/hashicorp/errwrap"
|
||||
"github.com/joyent/triton-go/client"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
type PackagesClient struct {
|
||||
|
@ -40,10 +48,10 @@ type ListPackagesInput struct {
|
|||
}
|
||||
|
||||
func (c *PackagesClient) List(ctx context.Context, input *ListPackagesInput) ([]*Package, error) {
|
||||
path := fmt.Sprintf("/%s/packages", c.client.AccountName)
|
||||
fullPath := path.Join("/", c.client.AccountName, "packages")
|
||||
reqInputs := client.RequestInput{
|
||||
Method: http.MethodGet,
|
||||
Path: path,
|
||||
Path: fullPath,
|
||||
Body: input,
|
||||
}
|
||||
respReader, err := c.client.ExecuteRequest(ctx, reqInputs)
|
||||
|
@ -51,13 +59,13 @@ func (c *PackagesClient) List(ctx context.Context, input *ListPackagesInput) ([]
|
|||
defer respReader.Close()
|
||||
}
|
||||
if err != nil {
|
||||
return nil, errwrap.Wrapf("Error executing List request: {{err}}", err)
|
||||
return nil, errors.Wrap(err, "unable to list packages")
|
||||
}
|
||||
|
||||
var result []*Package
|
||||
decoder := json.NewDecoder(respReader)
|
||||
if err = decoder.Decode(&result); err != nil {
|
||||
return nil, errwrap.Wrapf("Error decoding List response: {{err}}", err)
|
||||
return nil, errors.Wrap(err, "unable to decode list packages response")
|
||||
}
|
||||
|
||||
return result, nil
|
||||
|
@ -68,23 +76,23 @@ type GetPackageInput struct {
|
|||
}
|
||||
|
||||
func (c *PackagesClient) Get(ctx context.Context, input *GetPackageInput) (*Package, error) {
|
||||
path := fmt.Sprintf("/%s/packages/%s", c.client.AccountName, input.ID)
|
||||
fullPath := path.Join("/", c.client.AccountName, "packages", input.ID)
|
||||
reqInputs := client.RequestInput{
|
||||
Method: http.MethodGet,
|
||||
Path: path,
|
||||
Path: fullPath,
|
||||
}
|
||||
respReader, err := c.client.ExecuteRequest(ctx, reqInputs)
|
||||
if respReader != nil {
|
||||
defer respReader.Close()
|
||||
}
|
||||
if err != nil {
|
||||
return nil, errwrap.Wrapf("Error executing Get request: {{err}}", err)
|
||||
return nil, errors.Wrap(err, "unable to get package")
|
||||
}
|
||||
|
||||
var result *Package
|
||||
decoder := json.NewDecoder(respReader)
|
||||
if err = decoder.Decode(&result); err != nil {
|
||||
return nil, errwrap.Wrapf("Error decoding Get response: {{err}}", err)
|
||||
return nil, errors.Wrap(err, "unable to decode get package response")
|
||||
}
|
||||
|
||||
return result, nil
|
||||
|
|
|
@ -1,13 +1,20 @@
|
|||
//
|
||||
// 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 compute
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
"github.com/hashicorp/errwrap"
|
||||
"github.com/joyent/triton-go/client"
|
||||
pkgerrors "github.com/pkg/errors"
|
||||
)
|
||||
|
||||
const pingEndpoint = "/--ping"
|
||||
|
@ -29,27 +36,22 @@ func (c *ComputeClient) Ping(ctx context.Context) (*PingOutput, error) {
|
|||
Path: pingEndpoint,
|
||||
}
|
||||
response, err := c.Client.ExecuteRequestRaw(ctx, reqInputs)
|
||||
if err != nil {
|
||||
return nil, pkgerrors.Wrap(err, "unable to ping")
|
||||
}
|
||||
if response == nil {
|
||||
return nil, fmt.Errorf("Ping request has empty response")
|
||||
return nil, pkgerrors.Wrap(err, "unable to ping")
|
||||
}
|
||||
if response.Body != nil {
|
||||
defer response.Body.Close()
|
||||
}
|
||||
if response.StatusCode == http.StatusNotFound || response.StatusCode == http.StatusGone {
|
||||
return nil, &client.TritonError{
|
||||
StatusCode: response.StatusCode,
|
||||
Code: "ResourceNotFound",
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
return nil, errwrap.Wrapf("Error executing Get request: {{err}}",
|
||||
c.Client.DecodeError(response.StatusCode, response.Body))
|
||||
}
|
||||
|
||||
var result *PingOutput
|
||||
decoder := json.NewDecoder(response.Body)
|
||||
if err = decoder.Decode(&result); err != nil {
|
||||
return nil, errwrap.Wrapf("Error decoding Get response: {{err}}", err)
|
||||
if err != nil {
|
||||
return nil, pkgerrors.Wrap(err, "unable to decode ping response")
|
||||
}
|
||||
}
|
||||
|
||||
return result, nil
|
||||
|
|
|
@ -1,14 +1,22 @@
|
|||
//
|
||||
// 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 compute
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"path"
|
||||
"sort"
|
||||
|
||||
"github.com/hashicorp/errwrap"
|
||||
"github.com/joyent/triton-go/client"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
type ServicesClient struct {
|
||||
|
@ -23,23 +31,23 @@ type Service struct {
|
|||
type ListServicesInput struct{}
|
||||
|
||||
func (c *ServicesClient) List(ctx context.Context, _ *ListServicesInput) ([]*Service, error) {
|
||||
path := fmt.Sprintf("/%s/services", c.client.AccountName)
|
||||
fullPath := path.Join("/", c.client.AccountName, "services")
|
||||
reqInputs := client.RequestInput{
|
||||
Method: http.MethodGet,
|
||||
Path: path,
|
||||
Path: fullPath,
|
||||
}
|
||||
respReader, err := c.client.ExecuteRequest(ctx, reqInputs)
|
||||
if respReader != nil {
|
||||
defer respReader.Close()
|
||||
}
|
||||
if err != nil {
|
||||
return nil, errwrap.Wrapf("Error executing List request: {{err}}", err)
|
||||
return nil, errors.Wrap(err, "unable to list services")
|
||||
}
|
||||
|
||||
var intermediate map[string]string
|
||||
decoder := json.NewDecoder(respReader)
|
||||
if err = decoder.Decode(&intermediate); err != nil {
|
||||
return nil, errwrap.Wrapf("Error decoding List response: {{err}}", err)
|
||||
return nil, errors.Wrap(err, "unable to decode list services response")
|
||||
}
|
||||
|
||||
keys := make([]string, len(intermediate))
|
||||
|
|
|
@ -1,15 +1,22 @@
|
|||
//
|
||||
// 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 compute
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
"path"
|
||||
"time"
|
||||
|
||||
"github.com/hashicorp/errwrap"
|
||||
"github.com/joyent/triton-go/client"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
type SnapshotsClient struct {
|
||||
|
@ -28,23 +35,23 @@ type ListSnapshotsInput struct {
|
|||
}
|
||||
|
||||
func (c *SnapshotsClient) List(ctx context.Context, input *ListSnapshotsInput) ([]*Snapshot, error) {
|
||||
path := fmt.Sprintf("/%s/machines/%s/snapshots", c.client.AccountName, input.MachineID)
|
||||
fullPath := path.Join("/", c.client.AccountName, "machines", input.MachineID, "snapshots")
|
||||
reqInputs := client.RequestInput{
|
||||
Method: http.MethodGet,
|
||||
Path: path,
|
||||
Path: fullPath,
|
||||
}
|
||||
respReader, err := c.client.ExecuteRequest(ctx, reqInputs)
|
||||
if respReader != nil {
|
||||
defer respReader.Close()
|
||||
}
|
||||
if err != nil {
|
||||
return nil, errwrap.Wrapf("Error executing List request: {{err}}", err)
|
||||
return nil, errors.Wrap(err, "unable to list snapshots")
|
||||
}
|
||||
|
||||
var result []*Snapshot
|
||||
decoder := json.NewDecoder(respReader)
|
||||
if err = decoder.Decode(&result); err != nil {
|
||||
return nil, errwrap.Wrapf("Error decoding List response: {{err}}", err)
|
||||
return nil, errors.Wrap(err, "unable to decode list snapshots response")
|
||||
}
|
||||
|
||||
return result, nil
|
||||
|
@ -56,23 +63,23 @@ type GetSnapshotInput struct {
|
|||
}
|
||||
|
||||
func (c *SnapshotsClient) Get(ctx context.Context, input *GetSnapshotInput) (*Snapshot, error) {
|
||||
path := fmt.Sprintf("/%s/machines/%s/snapshots/%s", c.client.AccountName, input.MachineID, input.Name)
|
||||
fullPath := path.Join("/", c.client.AccountName, "machines", input.MachineID, "snapshots", input.Name)
|
||||
reqInputs := client.RequestInput{
|
||||
Method: http.MethodGet,
|
||||
Path: path,
|
||||
Path: fullPath,
|
||||
}
|
||||
respReader, err := c.client.ExecuteRequest(ctx, reqInputs)
|
||||
if respReader != nil {
|
||||
defer respReader.Close()
|
||||
}
|
||||
if err != nil {
|
||||
return nil, errwrap.Wrapf("Error executing Get request: {{err}}", err)
|
||||
return nil, errors.Wrap(err, "unable to get snapshot")
|
||||
}
|
||||
|
||||
var result *Snapshot
|
||||
decoder := json.NewDecoder(respReader)
|
||||
if err = decoder.Decode(&result); err != nil {
|
||||
return nil, errwrap.Wrapf("Error decoding Get response: {{err}}", err)
|
||||
return nil, errors.Wrap(err, "unable to decode get snapshot response")
|
||||
}
|
||||
|
||||
return result, nil
|
||||
|
@ -84,17 +91,17 @@ type DeleteSnapshotInput struct {
|
|||
}
|
||||
|
||||
func (c *SnapshotsClient) Delete(ctx context.Context, input *DeleteSnapshotInput) error {
|
||||
path := fmt.Sprintf("/%s/machines/%s/snapshots/%s", c.client.AccountName, input.MachineID, input.Name)
|
||||
fullPath := path.Join("/", c.client.AccountName, "machines", input.MachineID, "snapshots", input.Name)
|
||||
reqInputs := client.RequestInput{
|
||||
Method: http.MethodDelete,
|
||||
Path: path,
|
||||
Path: fullPath,
|
||||
}
|
||||
respReader, err := c.client.ExecuteRequest(ctx, reqInputs)
|
||||
if respReader != nil {
|
||||
defer respReader.Close()
|
||||
}
|
||||
if err != nil {
|
||||
return errwrap.Wrapf("Error executing Delete request: {{err}}", err)
|
||||
return errors.Wrap(err, "unable to delete snapshot")
|
||||
}
|
||||
|
||||
return nil
|
||||
|
@ -106,17 +113,17 @@ type StartMachineFromSnapshotInput struct {
|
|||
}
|
||||
|
||||
func (c *SnapshotsClient) StartMachine(ctx context.Context, input *StartMachineFromSnapshotInput) error {
|
||||
path := fmt.Sprintf("/%s/machines/%s/snapshots/%s", c.client.AccountName, input.MachineID, input.Name)
|
||||
fullPath := path.Join("/", c.client.AccountName, "machines", input.MachineID, "snapshots", input.Name)
|
||||
reqInputs := client.RequestInput{
|
||||
Method: http.MethodPost,
|
||||
Path: path,
|
||||
Path: fullPath,
|
||||
}
|
||||
respReader, err := c.client.ExecuteRequest(ctx, reqInputs)
|
||||
if respReader != nil {
|
||||
defer respReader.Close()
|
||||
}
|
||||
if err != nil {
|
||||
return errwrap.Wrapf("Error executing StartMachine request: {{err}}", err)
|
||||
return errors.Wrap(err, "unable to start machine")
|
||||
}
|
||||
|
||||
return nil
|
||||
|
@ -128,14 +135,14 @@ type CreateSnapshotInput struct {
|
|||
}
|
||||
|
||||
func (c *SnapshotsClient) Create(ctx context.Context, input *CreateSnapshotInput) (*Snapshot, error) {
|
||||
path := fmt.Sprintf("/%s/machines/%s/snapshots", c.client.AccountName, input.MachineID)
|
||||
fullPath := path.Join("/", c.client.AccountName, "machines", input.MachineID, "snapshots")
|
||||
|
||||
data := make(map[string]interface{})
|
||||
data["name"] = input.Name
|
||||
|
||||
reqInputs := client.RequestInput{
|
||||
Method: http.MethodPost,
|
||||
Path: path,
|
||||
Path: fullPath,
|
||||
Body: data,
|
||||
}
|
||||
|
||||
|
@ -144,13 +151,13 @@ func (c *SnapshotsClient) Create(ctx context.Context, input *CreateSnapshotInput
|
|||
defer respReader.Close()
|
||||
}
|
||||
if err != nil {
|
||||
return nil, errwrap.Wrapf("Error executing Create request: {{err}}", err)
|
||||
return nil, errors.Wrap(err, "unable to create snapshot")
|
||||
}
|
||||
|
||||
var result *Snapshot
|
||||
decoder := json.NewDecoder(respReader)
|
||||
if err = decoder.Decode(&result); err != nil {
|
||||
return nil, errwrap.Wrapf("Error decoding Create response: {{err}}", err)
|
||||
return nil, errors.Wrap(err, "unable to decode create snapshot response")
|
||||
}
|
||||
|
||||
return result, nil
|
||||
|
|
|
@ -0,0 +1,217 @@
|
|||
//
|
||||
// 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 compute
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"path"
|
||||
|
||||
"github.com/joyent/triton-go/client"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
type VolumesClient struct {
|
||||
client *client.Client
|
||||
}
|
||||
|
||||
type Volume struct {
|
||||
ID string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Owner string `json:"owner_uuid"`
|
||||
Type string `json:"type"`
|
||||
FileSystemPath string `json:"filesystem_path"`
|
||||
Size int64 `json:"size"`
|
||||
State string `json:"state"`
|
||||
Networks []string `json:"networks"`
|
||||
Refs []string `json:"refs"`
|
||||
}
|
||||
|
||||
type ListVolumesInput struct {
|
||||
Name string
|
||||
Size string
|
||||
State string
|
||||
Type string
|
||||
}
|
||||
|
||||
func (c *VolumesClient) List(ctx context.Context, input *ListVolumesInput) ([]*Volume, error) {
|
||||
fullPath := path.Join("/", c.client.AccountName, "volumes")
|
||||
|
||||
query := &url.Values{}
|
||||
if input.Name != "" {
|
||||
query.Set("name", input.Name)
|
||||
}
|
||||
if input.Size != "" {
|
||||
query.Set("size", input.Size)
|
||||
}
|
||||
if input.State != "" {
|
||||
query.Set("state", input.State)
|
||||
}
|
||||
if input.Type != "" {
|
||||
query.Set("type", input.Type)
|
||||
}
|
||||
|
||||
reqInputs := client.RequestInput{
|
||||
Method: http.MethodGet,
|
||||
Path: fullPath,
|
||||
Query: query,
|
||||
}
|
||||
resp, err := c.client.ExecuteRequest(ctx, reqInputs)
|
||||
if resp != nil {
|
||||
defer resp.Close()
|
||||
}
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "unable to list volumes")
|
||||
}
|
||||
|
||||
var result []*Volume
|
||||
decoder := json.NewDecoder(resp)
|
||||
if err = decoder.Decode(&result); err != nil {
|
||||
return nil, errors.Wrap(err, "unable to decode list volumes response")
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
type CreateVolumeInput struct {
|
||||
Name string
|
||||
Size int64
|
||||
Networks []string
|
||||
Type string
|
||||
}
|
||||
|
||||
func (input *CreateVolumeInput) toAPI() map[string]interface{} {
|
||||
result := make(map[string]interface{}, 0)
|
||||
|
||||
if input.Name != "" {
|
||||
result["name"] = input.Name
|
||||
}
|
||||
|
||||
if input.Size != 0 {
|
||||
result["size"] = input.Size
|
||||
}
|
||||
|
||||
if input.Type != "" {
|
||||
result["type"] = input.Type
|
||||
}
|
||||
|
||||
if len(input.Networks) > 0 {
|
||||
result["networks"] = input.Networks
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
func (c *VolumesClient) Create(ctx context.Context, input *CreateVolumeInput) (*Volume, error) {
|
||||
fullPath := path.Join("/", c.client.AccountName, "volumes")
|
||||
|
||||
reqInputs := client.RequestInput{
|
||||
Method: http.MethodPost,
|
||||
Path: fullPath,
|
||||
Body: input.toAPI(),
|
||||
}
|
||||
resp, err := c.client.ExecuteRequest(ctx, reqInputs)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "unable to create volume")
|
||||
}
|
||||
if resp != nil {
|
||||
defer resp.Close()
|
||||
}
|
||||
|
||||
var result *Volume
|
||||
decoder := json.NewDecoder(resp)
|
||||
if err = decoder.Decode(&result); err != nil {
|
||||
return nil, errors.Wrap(err, "unable to decode create volume response")
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
type DeleteVolumeInput struct {
|
||||
ID string
|
||||
}
|
||||
|
||||
func (c *VolumesClient) Delete(ctx context.Context, input *DeleteVolumeInput) error {
|
||||
fullPath := path.Join("/", c.client.AccountName, "volumes", input.ID)
|
||||
reqInputs := client.RequestInput{
|
||||
Method: http.MethodDelete,
|
||||
Path: fullPath,
|
||||
}
|
||||
resp, err := c.client.ExecuteRequestRaw(ctx, reqInputs)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "unable to delete volume")
|
||||
}
|
||||
if resp == nil {
|
||||
return errors.Wrap(err, "unable to delete volume")
|
||||
}
|
||||
if resp.Body != nil {
|
||||
defer resp.Body.Close()
|
||||
}
|
||||
if resp.StatusCode == http.StatusNotFound || resp.StatusCode == http.StatusGone {
|
||||
return nil
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type GetVolumeInput struct {
|
||||
ID string
|
||||
}
|
||||
|
||||
func (c *VolumesClient) Get(ctx context.Context, input *GetVolumeInput) (*Volume, error) {
|
||||
fullPath := path.Join("/", c.client.AccountName, "volumes", input.ID)
|
||||
reqInputs := client.RequestInput{
|
||||
Method: http.MethodGet,
|
||||
Path: fullPath,
|
||||
}
|
||||
resp, err := c.client.ExecuteRequestRaw(ctx, reqInputs)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "unable to get volume")
|
||||
}
|
||||
if resp == nil {
|
||||
return nil, errors.Wrap(err, "unable to get volume")
|
||||
}
|
||||
if resp.Body != nil {
|
||||
defer resp.Body.Close()
|
||||
}
|
||||
|
||||
var result *Volume
|
||||
decoder := json.NewDecoder(resp.Body)
|
||||
if err = decoder.Decode(&result); err != nil {
|
||||
return nil, errors.Wrap(err, "unable to decode get volume volume")
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
type UpdateVolumeInput struct {
|
||||
ID string `json:"-"`
|
||||
Name string `json:"name"`
|
||||
}
|
||||
|
||||
func (c *VolumesClient) Update(ctx context.Context, input *UpdateVolumeInput) error {
|
||||
fullPath := path.Join("/", c.client.AccountName, "volumes", input.ID)
|
||||
|
||||
reqInputs := client.RequestInput{
|
||||
Method: http.MethodPost,
|
||||
Path: fullPath,
|
||||
Body: input,
|
||||
}
|
||||
resp, err := c.client.ExecuteRequest(ctx, reqInputs)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "unable to update volume")
|
||||
}
|
||||
if resp != nil {
|
||||
defer resp.Close()
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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 network
|
||||
|
||||
import (
|
||||
|
|
|
@ -1,14 +1,22 @@
|
|||
//
|
||||
// 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 network
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
"context"
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
"path"
|
||||
"strconv"
|
||||
|
||||
"github.com/hashicorp/errwrap"
|
||||
"github.com/joyent/triton-go/client"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
type FabricsClient struct {
|
||||
|
@ -24,23 +32,23 @@ type FabricVLAN struct {
|
|||
type ListVLANsInput struct{}
|
||||
|
||||
func (c *FabricsClient) ListVLANs(ctx context.Context, _ *ListVLANsInput) ([]*FabricVLAN, error) {
|
||||
path := fmt.Sprintf("/%s/fabrics/default/vlans", c.client.AccountName)
|
||||
fullPath := path.Join("/", c.client.AccountName, "fabrics", "default", "vlans")
|
||||
reqInputs := client.RequestInput{
|
||||
Method: http.MethodGet,
|
||||
Path: path,
|
||||
Path: fullPath,
|
||||
}
|
||||
respReader, err := c.client.ExecuteRequest(ctx, reqInputs)
|
||||
if respReader != nil {
|
||||
defer respReader.Close()
|
||||
}
|
||||
if err != nil {
|
||||
return nil, errwrap.Wrapf("Error executing ListVLANs request: {{err}}", err)
|
||||
return nil, errors.Wrap(err, "unable to list VLANs")
|
||||
}
|
||||
|
||||
var result []*FabricVLAN
|
||||
decoder := json.NewDecoder(respReader)
|
||||
if err = decoder.Decode(&result); err != nil {
|
||||
return nil, errwrap.Wrapf("Error decoding ListVLANs response: {{err}}", err)
|
||||
return nil, errors.Wrap(err, "unable to decode list VLANs response")
|
||||
}
|
||||
|
||||
return result, nil
|
||||
|
@ -53,10 +61,10 @@ type CreateVLANInput struct {
|
|||
}
|
||||
|
||||
func (c *FabricsClient) CreateVLAN(ctx context.Context, input *CreateVLANInput) (*FabricVLAN, error) {
|
||||
path := fmt.Sprintf("/%s/fabrics/default/vlans", c.client.AccountName)
|
||||
fullPath := path.Join("/", c.client.AccountName, "fabrics", "default", "vlans")
|
||||
reqInputs := client.RequestInput{
|
||||
Method: http.MethodPost,
|
||||
Path: path,
|
||||
Path: fullPath,
|
||||
Body: input,
|
||||
}
|
||||
respReader, err := c.client.ExecuteRequest(ctx, reqInputs)
|
||||
|
@ -64,13 +72,13 @@ func (c *FabricsClient) CreateVLAN(ctx context.Context, input *CreateVLANInput)
|
|||
defer respReader.Close()
|
||||
}
|
||||
if err != nil {
|
||||
return nil, errwrap.Wrapf("Error executing CreateVLAN request: {{err}}", err)
|
||||
return nil, errors.Wrap(err, "unable to create VLAN")
|
||||
}
|
||||
|
||||
var result *FabricVLAN
|
||||
decoder := json.NewDecoder(respReader)
|
||||
if err = decoder.Decode(&result); err != nil {
|
||||
return nil, errwrap.Wrapf("Error decoding CreateVLAN response: {{err}}", err)
|
||||
return nil, errors.Wrap(err, "unable to decode create VLAN response")
|
||||
}
|
||||
|
||||
return result, nil
|
||||
|
@ -83,10 +91,10 @@ type UpdateVLANInput struct {
|
|||
}
|
||||
|
||||
func (c *FabricsClient) UpdateVLAN(ctx context.Context, input *UpdateVLANInput) (*FabricVLAN, error) {
|
||||
path := fmt.Sprintf("/%s/fabrics/default/vlans/%d", c.client.AccountName, input.ID)
|
||||
fullPath := path.Join("/", c.client.AccountName, "fabrics", "default", "vlans", strconv.Itoa(input.ID))
|
||||
reqInputs := client.RequestInput{
|
||||
Method: http.MethodPut,
|
||||
Path: path,
|
||||
Path: fullPath,
|
||||
Body: input,
|
||||
}
|
||||
respReader, err := c.client.ExecuteRequest(ctx, reqInputs)
|
||||
|
@ -94,13 +102,13 @@ func (c *FabricsClient) UpdateVLAN(ctx context.Context, input *UpdateVLANInput)
|
|||
defer respReader.Close()
|
||||
}
|
||||
if err != nil {
|
||||
return nil, errwrap.Wrapf("Error executing UpdateVLAN request: {{err}}", err)
|
||||
return nil, errors.Wrap(err, "unable to update VLAN")
|
||||
}
|
||||
|
||||
var result *FabricVLAN
|
||||
decoder := json.NewDecoder(respReader)
|
||||
if err = decoder.Decode(&result); err != nil {
|
||||
return nil, errwrap.Wrapf("Error decoding UpdateVLAN response: {{err}}", err)
|
||||
return nil, errors.Wrap(err, "unable to decode update VLAN response")
|
||||
}
|
||||
|
||||
return result, nil
|
||||
|
@ -111,23 +119,23 @@ type GetVLANInput struct {
|
|||
}
|
||||
|
||||
func (c *FabricsClient) GetVLAN(ctx context.Context, input *GetVLANInput) (*FabricVLAN, error) {
|
||||
path := fmt.Sprintf("/%s/fabrics/default/vlans/%d", c.client.AccountName, input.ID)
|
||||
fullPath := path.Join("/", c.client.AccountName, "fabrics", "default", "vlans", strconv.Itoa(input.ID))
|
||||
reqInputs := client.RequestInput{
|
||||
Method: http.MethodGet,
|
||||
Path: path,
|
||||
Path: fullPath,
|
||||
}
|
||||
respReader, err := c.client.ExecuteRequest(ctx, reqInputs)
|
||||
if respReader != nil {
|
||||
defer respReader.Close()
|
||||
}
|
||||
if err != nil {
|
||||
return nil, errwrap.Wrapf("Error executing GetVLAN request: {{err}}", err)
|
||||
return nil, errors.Wrap(err, "unable to get VLAN")
|
||||
}
|
||||
|
||||
var result *FabricVLAN
|
||||
decoder := json.NewDecoder(respReader)
|
||||
if err = decoder.Decode(&result); err != nil {
|
||||
return nil, errwrap.Wrapf("Error decoding GetVLAN response: {{err}}", err)
|
||||
return nil, errors.Wrap(err, "unable to decode get VLAN response")
|
||||
}
|
||||
|
||||
return result, nil
|
||||
|
@ -138,17 +146,17 @@ type DeleteVLANInput struct {
|
|||
}
|
||||
|
||||
func (c *FabricsClient) DeleteVLAN(ctx context.Context, input *DeleteVLANInput) error {
|
||||
path := fmt.Sprintf("/%s/fabrics/default/vlans/%d", c.client.AccountName, input.ID)
|
||||
fullPath := path.Join("/", c.client.AccountName, "fabrics", "default", "vlans", strconv.Itoa(input.ID))
|
||||
reqInputs := client.RequestInput{
|
||||
Method: http.MethodDelete,
|
||||
Path: path,
|
||||
Path: fullPath,
|
||||
}
|
||||
respReader, err := c.client.ExecuteRequest(ctx, reqInputs)
|
||||
if respReader != nil {
|
||||
defer respReader.Close()
|
||||
}
|
||||
if err != nil {
|
||||
return errwrap.Wrapf("Error executing DeleteVLAN request: {{err}}", err)
|
||||
return errors.Wrap(err, "unable to delete VLAN")
|
||||
}
|
||||
|
||||
return nil
|
||||
|
@ -159,23 +167,23 @@ type ListFabricsInput struct {
|
|||
}
|
||||
|
||||
func (c *FabricsClient) List(ctx context.Context, input *ListFabricsInput) ([]*Network, error) {
|
||||
path := fmt.Sprintf("/%s/fabrics/default/vlans/%d/networks", c.client.AccountName, input.FabricVLANID)
|
||||
fullPath := path.Join("/", c.client.AccountName, "fabrics", "default", "vlans", strconv.Itoa(input.FabricVLANID), "networks")
|
||||
reqInputs := client.RequestInput{
|
||||
Method: http.MethodGet,
|
||||
Path: path,
|
||||
Path: fullPath,
|
||||
}
|
||||
respReader, err := c.client.ExecuteRequest(ctx, reqInputs)
|
||||
if respReader != nil {
|
||||
defer respReader.Close()
|
||||
}
|
||||
if err != nil {
|
||||
return nil, errwrap.Wrapf("Error executing ListFabrics request: {{err}}", err)
|
||||
return nil, errors.Wrap(err, "unable to list fabrics")
|
||||
}
|
||||
|
||||
var result []*Network
|
||||
decoder := json.NewDecoder(respReader)
|
||||
if err = decoder.Decode(&result); err != nil {
|
||||
return nil, errwrap.Wrapf("Error decoding ListFabrics response: {{err}}", err)
|
||||
return nil, errors.Wrap(err, "unable to decode list fabrics response")
|
||||
}
|
||||
|
||||
return result, nil
|
||||
|
@ -195,10 +203,10 @@ type CreateFabricInput struct {
|
|||
}
|
||||
|
||||
func (c *FabricsClient) Create(ctx context.Context, input *CreateFabricInput) (*Network, error) {
|
||||
path := fmt.Sprintf("/%s/fabrics/default/vlans/%d/networks", c.client.AccountName, input.FabricVLANID)
|
||||
fullPath := path.Join("/", c.client.AccountName, "fabrics", "default", "vlans", strconv.Itoa(input.FabricVLANID), "networks")
|
||||
reqInputs := client.RequestInput{
|
||||
Method: http.MethodPost,
|
||||
Path: path,
|
||||
Path: fullPath,
|
||||
Body: input,
|
||||
}
|
||||
respReader, err := c.client.ExecuteRequest(ctx, reqInputs)
|
||||
|
@ -206,13 +214,13 @@ func (c *FabricsClient) Create(ctx context.Context, input *CreateFabricInput) (*
|
|||
defer respReader.Close()
|
||||
}
|
||||
if err != nil {
|
||||
return nil, errwrap.Wrapf("Error executing CreateFabric request: {{err}}", err)
|
||||
return nil, errors.Wrap(err, "unable to create fabric")
|
||||
}
|
||||
|
||||
var result *Network
|
||||
decoder := json.NewDecoder(respReader)
|
||||
if err = decoder.Decode(&result); err != nil {
|
||||
return nil, errwrap.Wrapf("Error decoding CreateFabric response: {{err}}", err)
|
||||
return nil, errors.Wrap(err, "unable to decode create fabric response")
|
||||
}
|
||||
|
||||
return result, nil
|
||||
|
@ -224,23 +232,23 @@ type GetFabricInput struct {
|
|||
}
|
||||
|
||||
func (c *FabricsClient) Get(ctx context.Context, input *GetFabricInput) (*Network, error) {
|
||||
path := fmt.Sprintf("/%s/fabrics/default/vlans/%d/networks/%s", c.client.AccountName, input.FabricVLANID, input.NetworkID)
|
||||
fullPath := path.Join("/", c.client.AccountName, "fabrics", "default", "vlans", strconv.Itoa(input.FabricVLANID), "networks", input.NetworkID)
|
||||
reqInputs := client.RequestInput{
|
||||
Method: http.MethodGet,
|
||||
Path: path,
|
||||
Path: fullPath,
|
||||
}
|
||||
respReader, err := c.client.ExecuteRequest(ctx, reqInputs)
|
||||
if respReader != nil {
|
||||
defer respReader.Close()
|
||||
}
|
||||
if err != nil {
|
||||
return nil, errwrap.Wrapf("Error executing GetFabric request: {{err}}", err)
|
||||
return nil, errors.Wrap(err, "unable to get fabric")
|
||||
}
|
||||
|
||||
var result *Network
|
||||
decoder := json.NewDecoder(respReader)
|
||||
if err = decoder.Decode(&result); err != nil {
|
||||
return nil, errwrap.Wrapf("Error decoding GetFabric response: {{err}}", err)
|
||||
return nil, errors.Wrap(err, "unable to decode get fabric response")
|
||||
}
|
||||
|
||||
return result, nil
|
||||
|
@ -252,17 +260,17 @@ type DeleteFabricInput struct {
|
|||
}
|
||||
|
||||
func (c *FabricsClient) Delete(ctx context.Context, input *DeleteFabricInput) error {
|
||||
path := fmt.Sprintf("/%s/fabrics/default/vlans/%d/networks/%s", c.client.AccountName, input.FabricVLANID, input.NetworkID)
|
||||
fullPath := path.Join("/", c.client.AccountName, "fabrics", "default", "vlans", strconv.Itoa(input.FabricVLANID), "networks", input.NetworkID)
|
||||
reqInputs := client.RequestInput{
|
||||
Method: http.MethodDelete,
|
||||
Path: path,
|
||||
Path: fullPath,
|
||||
}
|
||||
respReader, err := c.client.ExecuteRequest(ctx, reqInputs)
|
||||
if respReader != nil {
|
||||
defer respReader.Close()
|
||||
}
|
||||
if err != nil {
|
||||
return errwrap.Wrapf("Error executing DeleteFabric request: {{err}}", err)
|
||||
return errors.Wrap(err, "unable to delete fabric")
|
||||
}
|
||||
|
||||
return nil
|
||||
|
|
|
@ -1,15 +1,22 @@
|
|||
//
|
||||
// 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 network
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
"path"
|
||||
"time"
|
||||
|
||||
"github.com/hashicorp/errwrap"
|
||||
"github.com/joyent/triton-go/client"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
type FirewallClient struct {
|
||||
|
@ -37,23 +44,23 @@ type FirewallRule struct {
|
|||
type ListRulesInput struct{}
|
||||
|
||||
func (c *FirewallClient) ListRules(ctx context.Context, _ *ListRulesInput) ([]*FirewallRule, error) {
|
||||
path := fmt.Sprintf("/%s/fwrules", c.client.AccountName)
|
||||
fullPath := path.Join("/", c.client.AccountName, "fwrules")
|
||||
reqInputs := client.RequestInput{
|
||||
Method: http.MethodGet,
|
||||
Path: path,
|
||||
Path: fullPath,
|
||||
}
|
||||
respReader, err := c.client.ExecuteRequest(ctx, reqInputs)
|
||||
if respReader != nil {
|
||||
defer respReader.Close()
|
||||
}
|
||||
if err != nil {
|
||||
return nil, errwrap.Wrapf("Error executing ListRules request: {{err}}", err)
|
||||
return nil, errors.Wrap(err, "unable to list firewall rules")
|
||||
}
|
||||
|
||||
var result []*FirewallRule
|
||||
decoder := json.NewDecoder(respReader)
|
||||
if err = decoder.Decode(&result); err != nil {
|
||||
return nil, errwrap.Wrapf("Error decoding ListRules response: {{err}}", err)
|
||||
return nil, errors.Wrap(err, "unable to decode list firewall rules response")
|
||||
}
|
||||
|
||||
return result, nil
|
||||
|
@ -64,23 +71,23 @@ type GetRuleInput struct {
|
|||
}
|
||||
|
||||
func (c *FirewallClient) GetRule(ctx context.Context, input *GetRuleInput) (*FirewallRule, error) {
|
||||
path := fmt.Sprintf("/%s/fwrules/%s", c.client.AccountName, input.ID)
|
||||
fullPath := path.Join("/", c.client.AccountName, "fwrules", input.ID)
|
||||
reqInputs := client.RequestInput{
|
||||
Method: http.MethodGet,
|
||||
Path: path,
|
||||
Path: fullPath,
|
||||
}
|
||||
respReader, err := c.client.ExecuteRequest(ctx, reqInputs)
|
||||
if respReader != nil {
|
||||
defer respReader.Close()
|
||||
}
|
||||
if err != nil {
|
||||
return nil, errwrap.Wrapf("Error executing GetRule request: {{err}}", err)
|
||||
return nil, errors.Wrap(err, "unable to get firewall rule")
|
||||
}
|
||||
|
||||
var result *FirewallRule
|
||||
decoder := json.NewDecoder(respReader)
|
||||
if err = decoder.Decode(&result); err != nil {
|
||||
return nil, errwrap.Wrapf("Error decoding GetRule response: {{err}}", err)
|
||||
return nil, errors.Wrap(err, "unable to decode get firewall rule response")
|
||||
}
|
||||
|
||||
return result, nil
|
||||
|
@ -93,10 +100,10 @@ type CreateRuleInput struct {
|
|||
}
|
||||
|
||||
func (c *FirewallClient) CreateRule(ctx context.Context, input *CreateRuleInput) (*FirewallRule, error) {
|
||||
path := fmt.Sprintf("/%s/fwrules", c.client.AccountName)
|
||||
fullPath := path.Join("/", c.client.AccountName, "fwrules")
|
||||
reqInputs := client.RequestInput{
|
||||
Method: http.MethodPost,
|
||||
Path: path,
|
||||
Path: fullPath,
|
||||
Body: input,
|
||||
}
|
||||
respReader, err := c.client.ExecuteRequest(ctx, reqInputs)
|
||||
|
@ -104,13 +111,13 @@ func (c *FirewallClient) CreateRule(ctx context.Context, input *CreateRuleInput)
|
|||
defer respReader.Close()
|
||||
}
|
||||
if err != nil {
|
||||
return nil, errwrap.Wrapf("Error executing CreateRule request: {{err}}", err)
|
||||
return nil, errors.Wrap(err, "unable to create firewall rule")
|
||||
}
|
||||
|
||||
var result *FirewallRule
|
||||
decoder := json.NewDecoder(respReader)
|
||||
if err = decoder.Decode(&result); err != nil {
|
||||
return nil, errwrap.Wrapf("Error decoding CreateRule response: {{err}}", err)
|
||||
return nil, errors.Wrap(err, "unable to decode create firewall rule response")
|
||||
}
|
||||
|
||||
return result, nil
|
||||
|
@ -124,10 +131,10 @@ type UpdateRuleInput struct {
|
|||
}
|
||||
|
||||
func (c *FirewallClient) UpdateRule(ctx context.Context, input *UpdateRuleInput) (*FirewallRule, error) {
|
||||
path := fmt.Sprintf("/%s/fwrules/%s", c.client.AccountName, input.ID)
|
||||
fullPath := path.Join("/", c.client.AccountName, "fwrules", input.ID)
|
||||
reqInputs := client.RequestInput{
|
||||
Method: http.MethodPost,
|
||||
Path: path,
|
||||
Path: fullPath,
|
||||
Body: input,
|
||||
}
|
||||
respReader, err := c.client.ExecuteRequest(ctx, reqInputs)
|
||||
|
@ -135,13 +142,13 @@ func (c *FirewallClient) UpdateRule(ctx context.Context, input *UpdateRuleInput)
|
|||
defer respReader.Close()
|
||||
}
|
||||
if err != nil {
|
||||
return nil, errwrap.Wrapf("Error executing UpdateRule request: {{err}}", err)
|
||||
return nil, errors.Wrap(err, "unable to update firewall rule")
|
||||
}
|
||||
|
||||
var result *FirewallRule
|
||||
decoder := json.NewDecoder(respReader)
|
||||
if err = decoder.Decode(&result); err != nil {
|
||||
return nil, errwrap.Wrapf("Error decoding UpdateRule response: {{err}}", err)
|
||||
return nil, errors.Wrap(err, "unable to decode update firewall rule response")
|
||||
}
|
||||
|
||||
return result, nil
|
||||
|
@ -152,10 +159,10 @@ type EnableRuleInput struct {
|
|||
}
|
||||
|
||||
func (c *FirewallClient) EnableRule(ctx context.Context, input *EnableRuleInput) (*FirewallRule, error) {
|
||||
path := fmt.Sprintf("/%s/fwrules/%s/enable", c.client.AccountName, input.ID)
|
||||
fullPath := path.Join("/", c.client.AccountName, "fwrules", input.ID, "enable")
|
||||
reqInputs := client.RequestInput{
|
||||
Method: http.MethodPost,
|
||||
Path: path,
|
||||
Path: fullPath,
|
||||
Body: input,
|
||||
}
|
||||
respReader, err := c.client.ExecuteRequest(ctx, reqInputs)
|
||||
|
@ -163,13 +170,13 @@ func (c *FirewallClient) EnableRule(ctx context.Context, input *EnableRuleInput)
|
|||
defer respReader.Close()
|
||||
}
|
||||
if err != nil {
|
||||
return nil, errwrap.Wrapf("Error executing EnableRule request: {{err}}", err)
|
||||
return nil, errors.Wrap(err, "unable to enable firewall rule")
|
||||
}
|
||||
|
||||
var result *FirewallRule
|
||||
decoder := json.NewDecoder(respReader)
|
||||
if err = decoder.Decode(&result); err != nil {
|
||||
return nil, errwrap.Wrapf("Error decoding EnableRule response: {{err}}", err)
|
||||
return nil, errors.Wrap(err, "unable to decode enable firewall rule response")
|
||||
}
|
||||
|
||||
return result, nil
|
||||
|
@ -180,10 +187,10 @@ type DisableRuleInput struct {
|
|||
}
|
||||
|
||||
func (c *FirewallClient) DisableRule(ctx context.Context, input *DisableRuleInput) (*FirewallRule, error) {
|
||||
path := fmt.Sprintf("/%s/fwrules/%s/disable", c.client.AccountName, input.ID)
|
||||
fullPath := path.Join("/", c.client.AccountName, "fwrules", input.ID, "disable")
|
||||
reqInputs := client.RequestInput{
|
||||
Method: http.MethodPost,
|
||||
Path: path,
|
||||
Path: fullPath,
|
||||
Body: input,
|
||||
}
|
||||
respReader, err := c.client.ExecuteRequest(ctx, reqInputs)
|
||||
|
@ -191,13 +198,13 @@ func (c *FirewallClient) DisableRule(ctx context.Context, input *DisableRuleInpu
|
|||
defer respReader.Close()
|
||||
}
|
||||
if err != nil {
|
||||
return nil, errwrap.Wrapf("Error executing DisableRule request: {{err}}", err)
|
||||
return nil, errors.Wrap(err, "unable to disable firewall rule")
|
||||
}
|
||||
|
||||
var result *FirewallRule
|
||||
decoder := json.NewDecoder(respReader)
|
||||
if err = decoder.Decode(&result); err != nil {
|
||||
return nil, errwrap.Wrapf("Error decoding DisableRule response: {{err}}", err)
|
||||
return nil, errors.Wrap(err, "unable to decode disable firewall rule response")
|
||||
}
|
||||
|
||||
return result, nil
|
||||
|
@ -208,17 +215,17 @@ type DeleteRuleInput struct {
|
|||
}
|
||||
|
||||
func (c *FirewallClient) DeleteRule(ctx context.Context, input *DeleteRuleInput) error {
|
||||
path := fmt.Sprintf("/%s/fwrules/%s", c.client.AccountName, input.ID)
|
||||
fullPath := path.Join("/", c.client.AccountName, "fwrules", input.ID)
|
||||
reqInputs := client.RequestInput{
|
||||
Method: http.MethodDelete,
|
||||
Path: path,
|
||||
Path: fullPath,
|
||||
}
|
||||
respReader, err := c.client.ExecuteRequest(ctx, reqInputs)
|
||||
if respReader != nil {
|
||||
defer respReader.Close()
|
||||
}
|
||||
if err != nil {
|
||||
return errwrap.Wrapf("Error executing DeleteRule request: {{err}}", err)
|
||||
return errors.Wrap(err, "unable to delete firewall rule")
|
||||
}
|
||||
|
||||
return nil
|
||||
|
@ -229,23 +236,23 @@ type ListMachineRulesInput struct {
|
|||
}
|
||||
|
||||
func (c *FirewallClient) ListMachineRules(ctx context.Context, input *ListMachineRulesInput) ([]*FirewallRule, error) {
|
||||
path := fmt.Sprintf("/%s/machines/%s/fwrules", c.client.AccountName, input.MachineID)
|
||||
fullPath := path.Join("/", c.client.AccountName, "machines", input.MachineID, "fwrules")
|
||||
reqInputs := client.RequestInput{
|
||||
Method: http.MethodGet,
|
||||
Path: path,
|
||||
Path: fullPath,
|
||||
}
|
||||
respReader, err := c.client.ExecuteRequest(ctx, reqInputs)
|
||||
if respReader != nil {
|
||||
defer respReader.Close()
|
||||
}
|
||||
if err != nil {
|
||||
return nil, errwrap.Wrapf("Error executing ListMachineRules request: {{err}}", err)
|
||||
return nil, errors.Wrap(err, "unable to list machine firewall rules")
|
||||
}
|
||||
|
||||
var result []*FirewallRule
|
||||
decoder := json.NewDecoder(respReader)
|
||||
if err = decoder.Decode(&result); err != nil {
|
||||
return nil, errwrap.Wrapf("Error decoding ListMachineRules response: {{err}}", err)
|
||||
return nil, errors.Wrap(err, "unable to decode list machine firewall rules response")
|
||||
}
|
||||
|
||||
return result, nil
|
||||
|
@ -278,23 +285,23 @@ type Machine struct {
|
|||
}
|
||||
|
||||
func (c *FirewallClient) ListRuleMachines(ctx context.Context, input *ListRuleMachinesInput) ([]*Machine, error) {
|
||||
path := fmt.Sprintf("/%s/fwrules/%s/machines", c.client.AccountName, input.ID)
|
||||
fullPath := path.Join("/", c.client.AccountName, "fwrules", input.ID, "machines")
|
||||
reqInputs := client.RequestInput{
|
||||
Method: http.MethodGet,
|
||||
Path: path,
|
||||
Path: fullPath,
|
||||
}
|
||||
respReader, err := c.client.ExecuteRequest(ctx, reqInputs)
|
||||
if respReader != nil {
|
||||
defer respReader.Close()
|
||||
}
|
||||
if err != nil {
|
||||
return nil, errwrap.Wrapf("Error executing ListRuleMachines request: {{err}}", err)
|
||||
return nil, errors.Wrap(err, "unable to list firewall rule machines")
|
||||
}
|
||||
|
||||
var result []*Machine
|
||||
decoder := json.NewDecoder(respReader)
|
||||
if err = decoder.Decode(&result); err != nil {
|
||||
return nil, errwrap.Wrapf("Error decoding ListRuleMachines response: {{err}}", err)
|
||||
return nil, errors.Wrap(err, "unable to decode list firewall rule machines response")
|
||||
}
|
||||
|
||||
return result, nil
|
||||
|
|
|
@ -1,13 +1,21 @@
|
|||
//
|
||||
// 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 network
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"path"
|
||||
|
||||
"github.com/hashicorp/errwrap"
|
||||
"github.com/joyent/triton-go/client"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
type Network struct {
|
||||
|
@ -28,23 +36,23 @@ type Network struct {
|
|||
type ListInput struct{}
|
||||
|
||||
func (c *NetworkClient) List(ctx context.Context, _ *ListInput) ([]*Network, error) {
|
||||
path := fmt.Sprintf("/%s/networks", c.Client.AccountName)
|
||||
fullPath := path.Join("/", c.Client.AccountName, "networks")
|
||||
reqInputs := client.RequestInput{
|
||||
Method: http.MethodGet,
|
||||
Path: path,
|
||||
Path: fullPath,
|
||||
}
|
||||
respReader, err := c.Client.ExecuteRequest(ctx, reqInputs)
|
||||
if respReader != nil {
|
||||
defer respReader.Close()
|
||||
}
|
||||
if err != nil {
|
||||
return nil, errwrap.Wrapf("Error executing ListNetworks request: {{err}}", err)
|
||||
return nil, errors.Wrap(err, "unable to list networks")
|
||||
}
|
||||
|
||||
var result []*Network
|
||||
decoder := json.NewDecoder(respReader)
|
||||
if err = decoder.Decode(&result); err != nil {
|
||||
return nil, errwrap.Wrapf("Error decoding ListNetworks response: {{err}}", err)
|
||||
return nil, errors.Wrap(err, "unable to decode list networks response")
|
||||
}
|
||||
|
||||
return result, nil
|
||||
|
@ -55,23 +63,23 @@ type GetInput struct {
|
|||
}
|
||||
|
||||
func (c *NetworkClient) Get(ctx context.Context, input *GetInput) (*Network, error) {
|
||||
path := fmt.Sprintf("/%s/networks/%s", c.Client.AccountName, input.ID)
|
||||
fullPath := path.Join("/", c.Client.AccountName, "networks", input.ID)
|
||||
reqInputs := client.RequestInput{
|
||||
Method: http.MethodGet,
|
||||
Path: path,
|
||||
Path: fullPath,
|
||||
}
|
||||
respReader, err := c.Client.ExecuteRequest(ctx, reqInputs)
|
||||
if respReader != nil {
|
||||
defer respReader.Close()
|
||||
}
|
||||
if err != nil {
|
||||
return nil, errwrap.Wrapf("Error executing GetNetwork request: {{err}}", err)
|
||||
return nil, errors.Wrap(err, "unable to get network")
|
||||
}
|
||||
|
||||
var result *Network
|
||||
decoder := json.NewDecoder(respReader)
|
||||
if err = decoder.Decode(&result); err != nil {
|
||||
return nil, errwrap.Wrapf("Error decoding GetNetwork response: {{err}}", err)
|
||||
return nil, errors.Wrap(err, "unable to decode get network response")
|
||||
}
|
||||
|
||||
return result, nil
|
||||
|
|
|
@ -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 (
|
||||
|
|
|
@ -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"
|
|
@ -0,0 +1,23 @@
|
|||
Copyright (c) 2015, Dave Cheney <dave@cheney.net>
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
@ -0,0 +1,52 @@
|
|||
# errors [![Travis-CI](https://travis-ci.org/pkg/errors.svg)](https://travis-ci.org/pkg/errors) [![AppVeyor](https://ci.appveyor.com/api/projects/status/b98mptawhudj53ep/branch/master?svg=true)](https://ci.appveyor.com/project/davecheney/errors/branch/master) [![GoDoc](https://godoc.org/github.com/pkg/errors?status.svg)](http://godoc.org/github.com/pkg/errors) [![Report card](https://goreportcard.com/badge/github.com/pkg/errors)](https://goreportcard.com/report/github.com/pkg/errors) [![Sourcegraph](https://sourcegraph.com/github.com/pkg/errors/-/badge.svg)](https://sourcegraph.com/github.com/pkg/errors?badge)
|
||||
|
||||
Package errors provides simple error handling primitives.
|
||||
|
||||
`go get github.com/pkg/errors`
|
||||
|
||||
The traditional error handling idiom in Go is roughly akin to
|
||||
```go
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
```
|
||||
which applied recursively up the call stack results in error reports without context or debugging information. The errors package allows programmers to add context to the failure path in their code in a way that does not destroy the original value of the error.
|
||||
|
||||
## Adding context to an error
|
||||
|
||||
The errors.Wrap function returns a new error that adds context to the original error. For example
|
||||
```go
|
||||
_, err := ioutil.ReadAll(r)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "read failed")
|
||||
}
|
||||
```
|
||||
## Retrieving the cause of an error
|
||||
|
||||
Using `errors.Wrap` constructs a stack of errors, adding context to the preceding error. Depending on the nature of the error it may be necessary to reverse the operation of errors.Wrap to retrieve the original error for inspection. Any error value which implements this interface can be inspected by `errors.Cause`.
|
||||
```go
|
||||
type causer interface {
|
||||
Cause() error
|
||||
}
|
||||
```
|
||||
`errors.Cause` will recursively retrieve the topmost error which does not implement `causer`, which is assumed to be the original cause. For example:
|
||||
```go
|
||||
switch err := errors.Cause(err).(type) {
|
||||
case *MyError:
|
||||
// handle specifically
|
||||
default:
|
||||
// unknown error
|
||||
}
|
||||
```
|
||||
|
||||
[Read the package documentation for more information](https://godoc.org/github.com/pkg/errors).
|
||||
|
||||
## Contributing
|
||||
|
||||
We welcome pull requests, bug fixes and issue reports. With that said, the bar for adding new symbols to this package is intentionally set high.
|
||||
|
||||
Before proposing a change, please discuss your change by raising an issue.
|
||||
|
||||
## License
|
||||
|
||||
BSD-2-Clause
|
|
@ -0,0 +1,32 @@
|
|||
version: build-{build}.{branch}
|
||||
|
||||
clone_folder: C:\gopath\src\github.com\pkg\errors
|
||||
shallow_clone: true # for startup speed
|
||||
|
||||
environment:
|
||||
GOPATH: C:\gopath
|
||||
|
||||
platform:
|
||||
- x64
|
||||
|
||||
# http://www.appveyor.com/docs/installed-software
|
||||
install:
|
||||
# some helpful output for debugging builds
|
||||
- go version
|
||||
- go env
|
||||
# pre-installed MinGW at C:\MinGW is 32bit only
|
||||
# but MSYS2 at C:\msys64 has mingw64
|
||||
- set PATH=C:\msys64\mingw64\bin;%PATH%
|
||||
- gcc --version
|
||||
- g++ --version
|
||||
|
||||
build_script:
|
||||
- go install -v ./...
|
||||
|
||||
test_script:
|
||||
- set PATH=C:\gopath\bin;%PATH%
|
||||
- go test -v ./...
|
||||
|
||||
#artifacts:
|
||||
# - path: '%GOPATH%\bin\*.exe'
|
||||
deploy: off
|
|
@ -0,0 +1,269 @@
|
|||
// Package errors provides simple error handling primitives.
|
||||
//
|
||||
// The traditional error handling idiom in Go is roughly akin to
|
||||
//
|
||||
// if err != nil {
|
||||
// return err
|
||||
// }
|
||||
//
|
||||
// which applied recursively up the call stack results in error reports
|
||||
// without context or debugging information. The errors package allows
|
||||
// programmers to add context to the failure path in their code in a way
|
||||
// that does not destroy the original value of the error.
|
||||
//
|
||||
// Adding context to an error
|
||||
//
|
||||
// The errors.Wrap function returns a new error that adds context to the
|
||||
// original error by recording a stack trace at the point Wrap is called,
|
||||
// and the supplied message. For example
|
||||
//
|
||||
// _, err := ioutil.ReadAll(r)
|
||||
// if err != nil {
|
||||
// return errors.Wrap(err, "read failed")
|
||||
// }
|
||||
//
|
||||
// If additional control is required the errors.WithStack and errors.WithMessage
|
||||
// functions destructure errors.Wrap into its component operations of annotating
|
||||
// an error with a stack trace and an a message, respectively.
|
||||
//
|
||||
// Retrieving the cause of an error
|
||||
//
|
||||
// Using errors.Wrap constructs a stack of errors, adding context to the
|
||||
// preceding error. Depending on the nature of the error it may be necessary
|
||||
// to reverse the operation of errors.Wrap to retrieve the original error
|
||||
// for inspection. Any error value which implements this interface
|
||||
//
|
||||
// type causer interface {
|
||||
// Cause() error
|
||||
// }
|
||||
//
|
||||
// can be inspected by errors.Cause. errors.Cause will recursively retrieve
|
||||
// the topmost error which does not implement causer, which is assumed to be
|
||||
// the original cause. For example:
|
||||
//
|
||||
// switch err := errors.Cause(err).(type) {
|
||||
// case *MyError:
|
||||
// // handle specifically
|
||||
// default:
|
||||
// // unknown error
|
||||
// }
|
||||
//
|
||||
// causer interface is not exported by this package, but is considered a part
|
||||
// of stable public API.
|
||||
//
|
||||
// Formatted printing of errors
|
||||
//
|
||||
// All error values returned from this package implement fmt.Formatter and can
|
||||
// be formatted by the fmt package. The following verbs are supported
|
||||
//
|
||||
// %s print the error. If the error has a Cause it will be
|
||||
// printed recursively
|
||||
// %v see %s
|
||||
// %+v extended format. Each Frame of the error's StackTrace will
|
||||
// be printed in detail.
|
||||
//
|
||||
// Retrieving the stack trace of an error or wrapper
|
||||
//
|
||||
// New, Errorf, Wrap, and Wrapf record a stack trace at the point they are
|
||||
// invoked. This information can be retrieved with the following interface.
|
||||
//
|
||||
// type stackTracer interface {
|
||||
// StackTrace() errors.StackTrace
|
||||
// }
|
||||
//
|
||||
// Where errors.StackTrace is defined as
|
||||
//
|
||||
// type StackTrace []Frame
|
||||
//
|
||||
// The Frame type represents a call site in the stack trace. Frame supports
|
||||
// the fmt.Formatter interface that can be used for printing information about
|
||||
// the stack trace of this error. For example:
|
||||
//
|
||||
// if err, ok := err.(stackTracer); ok {
|
||||
// for _, f := range err.StackTrace() {
|
||||
// fmt.Printf("%+s:%d", f)
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// stackTracer interface is not exported by this package, but is considered a part
|
||||
// of stable public API.
|
||||
//
|
||||
// See the documentation for Frame.Format for more details.
|
||||
package errors
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
)
|
||||
|
||||
// New returns an error with the supplied message.
|
||||
// New also records the stack trace at the point it was called.
|
||||
func New(message string) error {
|
||||
return &fundamental{
|
||||
msg: message,
|
||||
stack: callers(),
|
||||
}
|
||||
}
|
||||
|
||||
// Errorf formats according to a format specifier and returns the string
|
||||
// as a value that satisfies error.
|
||||
// Errorf also records the stack trace at the point it was called.
|
||||
func Errorf(format string, args ...interface{}) error {
|
||||
return &fundamental{
|
||||
msg: fmt.Sprintf(format, args...),
|
||||
stack: callers(),
|
||||
}
|
||||
}
|
||||
|
||||
// fundamental is an error that has a message and a stack, but no caller.
|
||||
type fundamental struct {
|
||||
msg string
|
||||
*stack
|
||||
}
|
||||
|
||||
func (f *fundamental) Error() string { return f.msg }
|
||||
|
||||
func (f *fundamental) Format(s fmt.State, verb rune) {
|
||||
switch verb {
|
||||
case 'v':
|
||||
if s.Flag('+') {
|
||||
io.WriteString(s, f.msg)
|
||||
f.stack.Format(s, verb)
|
||||
return
|
||||
}
|
||||
fallthrough
|
||||
case 's':
|
||||
io.WriteString(s, f.msg)
|
||||
case 'q':
|
||||
fmt.Fprintf(s, "%q", f.msg)
|
||||
}
|
||||
}
|
||||
|
||||
// WithStack annotates err with a stack trace at the point WithStack was called.
|
||||
// If err is nil, WithStack returns nil.
|
||||
func WithStack(err error) error {
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
return &withStack{
|
||||
err,
|
||||
callers(),
|
||||
}
|
||||
}
|
||||
|
||||
type withStack struct {
|
||||
error
|
||||
*stack
|
||||
}
|
||||
|
||||
func (w *withStack) Cause() error { return w.error }
|
||||
|
||||
func (w *withStack) Format(s fmt.State, verb rune) {
|
||||
switch verb {
|
||||
case 'v':
|
||||
if s.Flag('+') {
|
||||
fmt.Fprintf(s, "%+v", w.Cause())
|
||||
w.stack.Format(s, verb)
|
||||
return
|
||||
}
|
||||
fallthrough
|
||||
case 's':
|
||||
io.WriteString(s, w.Error())
|
||||
case 'q':
|
||||
fmt.Fprintf(s, "%q", w.Error())
|
||||
}
|
||||
}
|
||||
|
||||
// Wrap returns an error annotating err with a stack trace
|
||||
// at the point Wrap is called, and the supplied message.
|
||||
// If err is nil, Wrap returns nil.
|
||||
func Wrap(err error, message string) error {
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
err = &withMessage{
|
||||
cause: err,
|
||||
msg: message,
|
||||
}
|
||||
return &withStack{
|
||||
err,
|
||||
callers(),
|
||||
}
|
||||
}
|
||||
|
||||
// Wrapf returns an error annotating err with a stack trace
|
||||
// at the point Wrapf is call, and the format specifier.
|
||||
// If err is nil, Wrapf returns nil.
|
||||
func Wrapf(err error, format string, args ...interface{}) error {
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
err = &withMessage{
|
||||
cause: err,
|
||||
msg: fmt.Sprintf(format, args...),
|
||||
}
|
||||
return &withStack{
|
||||
err,
|
||||
callers(),
|
||||
}
|
||||
}
|
||||
|
||||
// WithMessage annotates err with a new message.
|
||||
// If err is nil, WithMessage returns nil.
|
||||
func WithMessage(err error, message string) error {
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
return &withMessage{
|
||||
cause: err,
|
||||
msg: message,
|
||||
}
|
||||
}
|
||||
|
||||
type withMessage struct {
|
||||
cause error
|
||||
msg string
|
||||
}
|
||||
|
||||
func (w *withMessage) Error() string { return w.msg + ": " + w.cause.Error() }
|
||||
func (w *withMessage) Cause() error { return w.cause }
|
||||
|
||||
func (w *withMessage) Format(s fmt.State, verb rune) {
|
||||
switch verb {
|
||||
case 'v':
|
||||
if s.Flag('+') {
|
||||
fmt.Fprintf(s, "%+v\n", w.Cause())
|
||||
io.WriteString(s, w.msg)
|
||||
return
|
||||
}
|
||||
fallthrough
|
||||
case 's', 'q':
|
||||
io.WriteString(s, w.Error())
|
||||
}
|
||||
}
|
||||
|
||||
// Cause returns the underlying cause of the error, if possible.
|
||||
// An error value has a cause if it implements the following
|
||||
// interface:
|
||||
//
|
||||
// type causer interface {
|
||||
// Cause() error
|
||||
// }
|
||||
//
|
||||
// If the error does not implement Cause, the original error will
|
||||
// be returned. If the error is nil, nil will be returned without further
|
||||
// investigation.
|
||||
func Cause(err error) error {
|
||||
type causer interface {
|
||||
Cause() error
|
||||
}
|
||||
|
||||
for err != nil {
|
||||
cause, ok := err.(causer)
|
||||
if !ok {
|
||||
break
|
||||
}
|
||||
err = cause.Cause()
|
||||
}
|
||||
return err
|
||||
}
|
|
@ -0,0 +1,187 @@
|
|||
package errors
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"path"
|
||||
"runtime"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Frame represents a program counter inside a stack frame.
|
||||
type Frame uintptr
|
||||
|
||||
// pc returns the program counter for this frame;
|
||||
// multiple frames may have the same PC value.
|
||||
func (f Frame) pc() uintptr { return uintptr(f) - 1 }
|
||||
|
||||
// file returns the full path to the file that contains the
|
||||
// function for this Frame's pc.
|
||||
func (f Frame) file() string {
|
||||
fn := runtime.FuncForPC(f.pc())
|
||||
if fn == nil {
|
||||
return "unknown"
|
||||
}
|
||||
file, _ := fn.FileLine(f.pc())
|
||||
return file
|
||||
}
|
||||
|
||||
// line returns the line number of source code of the
|
||||
// function for this Frame's pc.
|
||||
func (f Frame) line() int {
|
||||
fn := runtime.FuncForPC(f.pc())
|
||||
if fn == nil {
|
||||
return 0
|
||||
}
|
||||
_, line := fn.FileLine(f.pc())
|
||||
return line
|
||||
}
|
||||
|
||||
// Format formats the frame according to the fmt.Formatter interface.
|
||||
//
|
||||
// %s source file
|
||||
// %d source line
|
||||
// %n function name
|
||||
// %v equivalent to %s:%d
|
||||
//
|
||||
// Format accepts flags that alter the printing of some verbs, as follows:
|
||||
//
|
||||
// %+s function name and path of source file relative to the compile time
|
||||
// GOPATH separated by \n\t (<funcname>\n\t<path>)
|
||||
// %+v equivalent to %+s:%d
|
||||
func (f Frame) Format(s fmt.State, verb rune) {
|
||||
switch verb {
|
||||
case 's':
|
||||
switch {
|
||||
case s.Flag('+'):
|
||||
pc := f.pc()
|
||||
fn := runtime.FuncForPC(pc)
|
||||
if fn == nil {
|
||||
io.WriteString(s, "unknown")
|
||||
} else {
|
||||
file, _ := fn.FileLine(pc)
|
||||
fmt.Fprintf(s, "%s\n\t%s", fn.Name(), file)
|
||||
}
|
||||
default:
|
||||
io.WriteString(s, path.Base(f.file()))
|
||||
}
|
||||
case 'd':
|
||||
fmt.Fprintf(s, "%d", f.line())
|
||||
case 'n':
|
||||
name := runtime.FuncForPC(f.pc()).Name()
|
||||
io.WriteString(s, funcname(name))
|
||||
case 'v':
|
||||
f.Format(s, 's')
|
||||
io.WriteString(s, ":")
|
||||
f.Format(s, 'd')
|
||||
}
|
||||
}
|
||||
|
||||
// StackTrace is stack of Frames from innermost (newest) to outermost (oldest).
|
||||
type StackTrace []Frame
|
||||
|
||||
// Format formats the stack of Frames according to the fmt.Formatter interface.
|
||||
//
|
||||
// %s lists source files for each Frame in the stack
|
||||
// %v lists the source file and line number for each Frame in the stack
|
||||
//
|
||||
// Format accepts flags that alter the printing of some verbs, as follows:
|
||||
//
|
||||
// %+v Prints filename, function, and line number for each Frame in the stack.
|
||||
func (st StackTrace) Format(s fmt.State, verb rune) {
|
||||
switch verb {
|
||||
case 'v':
|
||||
switch {
|
||||
case s.Flag('+'):
|
||||
for _, f := range st {
|
||||
fmt.Fprintf(s, "\n%+v", f)
|
||||
}
|
||||
case s.Flag('#'):
|
||||
fmt.Fprintf(s, "%#v", []Frame(st))
|
||||
default:
|
||||
fmt.Fprintf(s, "%v", []Frame(st))
|
||||
}
|
||||
case 's':
|
||||
fmt.Fprintf(s, "%s", []Frame(st))
|
||||
}
|
||||
}
|
||||
|
||||
// stack represents a stack of program counters.
|
||||
type stack []uintptr
|
||||
|
||||
func (s *stack) Format(st fmt.State, verb rune) {
|
||||
switch verb {
|
||||
case 'v':
|
||||
switch {
|
||||
case st.Flag('+'):
|
||||
for _, pc := range *s {
|
||||
f := Frame(pc)
|
||||
fmt.Fprintf(st, "\n%+v", f)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (s *stack) StackTrace() StackTrace {
|
||||
f := make([]Frame, len(*s))
|
||||
for i := 0; i < len(f); i++ {
|
||||
f[i] = Frame((*s)[i])
|
||||
}
|
||||
return f
|
||||
}
|
||||
|
||||
func callers() *stack {
|
||||
const depth = 32
|
||||
var pcs [depth]uintptr
|
||||
n := runtime.Callers(3, pcs[:])
|
||||
var st stack = pcs[0:n]
|
||||
return &st
|
||||
}
|
||||
|
||||
// funcname removes the path prefix component of a function's name reported by func.Name().
|
||||
func funcname(name string) string {
|
||||
i := strings.LastIndex(name, "/")
|
||||
name = name[i+1:]
|
||||
i = strings.Index(name, ".")
|
||||
return name[i+1:]
|
||||
}
|
||||
|
||||
func trimGOPATH(name, file string) string {
|
||||
// Here we want to get the source file path relative to the compile time
|
||||
// GOPATH. As of Go 1.6.x there is no direct way to know the compiled
|
||||
// GOPATH at runtime, but we can infer the number of path segments in the
|
||||
// GOPATH. We note that fn.Name() returns the function name qualified by
|
||||
// the import path, which does not include the GOPATH. Thus we can trim
|
||||
// segments from the beginning of the file path until the number of path
|
||||
// separators remaining is one more than the number of path separators in
|
||||
// the function name. For example, given:
|
||||
//
|
||||
// GOPATH /home/user
|
||||
// file /home/user/src/pkg/sub/file.go
|
||||
// fn.Name() pkg/sub.Type.Method
|
||||
//
|
||||
// We want to produce:
|
||||
//
|
||||
// pkg/sub/file.go
|
||||
//
|
||||
// From this we can easily see that fn.Name() has one less path separator
|
||||
// than our desired output. We count separators from the end of the file
|
||||
// path until it finds two more than in the function name and then move
|
||||
// one character forward to preserve the initial path segment without a
|
||||
// leading separator.
|
||||
const sep = "/"
|
||||
goal := strings.Count(name, sep) + 2
|
||||
i := len(file)
|
||||
for n := 0; n < goal; n++ {
|
||||
i = strings.LastIndex(file[:i], sep)
|
||||
if i == -1 {
|
||||
// not enough separators found, set i so that the slice expression
|
||||
// below leaves file unmodified
|
||||
i = -len(sep)
|
||||
break
|
||||
}
|
||||
}
|
||||
// get back to 0 or trim the leading separator
|
||||
file = file[i+len(sep):]
|
||||
return file
|
||||
}
|
|
@ -823,34 +823,40 @@
|
|||
"revision": "c01cf91b011868172fdcd9f41838e80c9d716264"
|
||||
},
|
||||
{
|
||||
"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": "U9D/fCNr+1uD1p/O0PW0yD/izqc=",
|
||||
"checksumSHA1": "A+YGcdf/qfac1gqK8QY1F5+pYGA=",
|
||||
"path": "github.com/joyent/triton-go/compute",
|
||||
"revision": "86ba9699869b6cd5ea3290faad7be659efc7d6ce",
|
||||
"revisionTime": "2017-12-28T20:20:46Z"
|
||||
"revision": "545edbe0d564f075ac576f1ad177f4ff39c9adaf",
|
||||
"revisionTime": "2018-01-16T16:57:42Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "9OdR/eI3qbmADruKn6yE1Dbx3LE=",
|
||||
"checksumSHA1": "d/Py6j/uMgOAFNFGpsQrNnSsO+k=",
|
||||
"path": "github.com/joyent/triton-go/errors",
|
||||
"revision": "545edbe0d564f075ac576f1ad177f4ff39c9adaf",
|
||||
"revisionTime": "2018-01-16T16:57:42Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "MIrIeqiPmw66cko+gzk7Cht2SLE=",
|
||||
"path": "github.com/joyent/triton-go/network",
|
||||
"revision": "86ba9699869b6cd5ea3290faad7be659efc7d6ce",
|
||||
"revisionTime": "2017-12-28T20:20:46Z"
|
||||
"revision": "545edbe0d564f075ac576f1ad177f4ff39c9adaf",
|
||||
"revisionTime": "2018-01-16T16:57:42Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "gEjGS03N1eysvpQ+FCHTxPcbxXc=",
|
||||
|
@ -1003,6 +1009,12 @@
|
|||
"path": "github.com/pierrec/xxHash/xxHash32",
|
||||
"revision": "5a004441f897722c627870a981d02b29924215fa"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "xCv4GBFyw07vZkVtKF/XrUnkHRk=",
|
||||
"path": "github.com/pkg/errors",
|
||||
"revision": "e881fd58d78e04cf6d0de1217f8707c8cc2249bc",
|
||||
"revisionTime": "2017-12-16T07:03:16Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "Mud+5WNq90BFb4gTeM5AByN8f2Y=",
|
||||
"path": "github.com/pkg/sftp",
|
||||
|
|
Loading…
Reference in New Issue