diff --git a/builder/triton/access_config.go b/builder/triton/access_config.go index 2df0ed150..3212c37a1 100644 --- a/builder/triton/access_config.go +++ b/builder/triton/access_config.go @@ -19,6 +19,7 @@ import ( type AccessConfig struct { Endpoint string `mapstructure:"triton_url"` Account string `mapstructure:"triton_account"` + Username string `mapstructure:"triton_user"` KeyID string `mapstructure:"triton_key_id"` KeyMaterial string `mapstructure:"triton_key_material"` @@ -65,7 +66,12 @@ func (c *AccessConfig) Prepare(ctx *interpolate.Context) []error { } func (c *AccessConfig) createSSHAgentSigner() (authentication.Signer, error) { - signer, err := authentication.NewSSHAgentSigner(c.KeyID, c.Account) + input := authentication.SSHAgentSignerInput{ + KeyID: c.KeyID, + AccountName: c.Account, + Username: c.Username, + } + signer, err := authentication.NewSSHAgentSigner(input) if err != nil { return nil, fmt.Errorf("Error creating Triton request signer: %s", err) } @@ -94,8 +100,14 @@ func (c *AccessConfig) createPrivateKeySigner() (authentication.Signer, error) { } } - // Create signer - signer, err := authentication.NewPrivateKeySigner(c.KeyID, privateKeyMaterial, c.Account) + input := authentication.PrivateKeySignerInput{ + KeyID: c.KeyID, + AccountName: c.Account, + Username: c.Username, + PrivateKeyMaterial: privateKeyMaterial, + } + + signer, err := authentication.NewPrivateKeySigner(input) if err != nil { return nil, fmt.Errorf("Error creating Triton request signer: %s", err) } @@ -114,6 +126,7 @@ func (c *AccessConfig) CreateTritonClient() (*Client, error) { config := &tgo.ClientConfig{ AccountName: c.Account, TritonURL: c.Endpoint, + Username: c.Username, Signers: []authentication.Signer{c.signer}, } diff --git a/vendor/github.com/joyent/triton-go/CHANGELOG.md b/vendor/github.com/joyent/triton-go/CHANGELOG.md new file mode 100644 index 000000000..c0d740998 --- /dev/null +++ b/vendor/github.com/joyent/triton-go/CHANGELOG.md @@ -0,0 +1,58 @@ +## Unreleased + +## 0.5.2 (December 28) + +- Standardise the API SSH Signers input casing and naming + +## 0.5.1 (December 28) + +- Include leading '/' when working with SSH Agent signers + +## 0.5.0 (December 28) + +- Add support for RBAC in triton-go [#82] +This is a breaking change. No longer do we pass individual parameters to the SSH Signer funcs, but we now pass an input Struct. This will guard from from additional parameter changes in the future. +We also now add support for using `SDC_*` and `TRITON_*` env vars when working with the Default agent signer + +## 0.4.2 (December 22) + +- Fixing a panic when the user loses network connectivity when making a GET request to instance [#81] + +## 0.4.1 (December 15) + +- Clean up the handling of directory sanitization. Use abs paths everywhere [#79] + +## 0.4.0 (December 15) + +- Fix an issue where Manta HEAD requests do not return an error resp body [#77] +- Add support for recursively creating child directories [#78] + +## 0.3.0 (December 14) + +- Introduce CloudAPI's ListRulesMachines under networking +- Enable HTTP KeepAlives by default in the client. 15s idle timeout, 2x + connections per host, total of 10x connections per client. +- Expose an optional Headers attribute to clients to allow them to customize + HTTP headers when making Object requests. +- Fix a bug in Directory ListIndex [#69](https://github.com/joyent/issues/69) +- Inputs to Object inputs have been relaxed to `io.Reader` (formerly a + `io.ReadSeeker`) [#73](https://github.com/joyent/issues/73). +- Add support for ForceDelete of all children of a directory [#71](https://github.com/joyent/issues/71) +- storage: Introduce `Objects.GetInfo` and `Objects.IsDir` using HEAD requests [#74](https://github.com/joyent/triton-go/issues/74) + +## 0.2.1 (November 8) + +- Fixing a bug where CreateUser and UpdateUser didn't return the UserID + +## 0.2.0 (November 7) + +- Introduce CloudAPI's Ping under compute +- Introduce CloudAPI's RebootMachine under compute instances +- Introduce CloudAPI's ListUsers, GetUser, CreateUser, UpdateUser and DeleteUser under identity package +- Introduce CloudAPI's ListMachineSnapshots, GetMachineSnapshot, CreateSnapshot, DeleteMachineSnapshot and StartMachineFromSnapshot under compute package +- tools: Introduce unit testing and scripts for linting, etc. +- bug: Fix the `compute.ListMachineRules` endpoint + +## 0.1.0 (November 2) + +- Initial release of a versioned SDK diff --git a/vendor/github.com/joyent/triton-go/GNUmakefile b/vendor/github.com/joyent/triton-go/GNUmakefile new file mode 100644 index 000000000..cca7f6759 --- /dev/null +++ b/vendor/github.com/joyent/triton-go/GNUmakefile @@ -0,0 +1,47 @@ +TEST?=$$(go list ./... |grep -Ev 'vendor|examples|testutils') +GOFMT_FILES?=$$(find . -name '*.go' |grep -v vendor) + +default: vet errcheck test + +tools:: ## Download and install all dev/code tools + @echo "==> Installing dev tools" + go get -u github.com/golang/dep/cmd/dep + go get -u github.com/golang/lint/golint + go get -u github.com/kisielk/errcheck + @echo "==> Installing test package dependencies" + 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' + +testacc:: ## Run acceptance tests + @echo "==> Running acceptance tests" + TRITON_TEST=1 go test $(TEST) -v $(TESTARGS) -run -timeout 120m + +vet:: ## Check for unwanted code constructs + @echo "go vet ." + @go vet $$(go list ./... | grep -v vendor/) ; if [ $$? -eq 1 ]; then \ + echo ""; \ + echo "Vet found suspicious constructs. Please check the reported constructs"; \ + echo "and fix them if necessary before submitting the code for review."; \ + exit 1; \ + fi + +lint:: ## Lint and vet code by common Go standards + @bash $(CURDIR)/scripts/lint.sh + +fmt:: ## Format as canonical Go code + gofmt -w $(GOFMT_FILES) + +fmtcheck:: ## Check if code format is canonical Go + @bash $(CURDIR)/scripts/gofmtcheck.sh + +errcheck:: ## Check for unhandled errors + @bash $(CURDIR)/scripts/errcheck.sh + +.PHONY: help +help:: ## Display this help message + @echo "GNU make(1) targets:" + @grep -E '^[a-zA-Z_.-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-15s\033[0m %s\n", $$1, $$2}' diff --git a/vendor/github.com/joyent/triton-go/Gopkg.lock b/vendor/github.com/joyent/triton-go/Gopkg.lock new file mode 100644 index 000000000..b61936ad3 --- /dev/null +++ b/vendor/github.com/joyent/triton-go/Gopkg.lock @@ -0,0 +1,39 @@ +# This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'. + + +[[projects]] + branch = "master" + name = "github.com/abdullin/seq" + 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" + packages = ["."] + revision = "7554cd9344cec97297fa6649b055a8c98c2a1e55" + +[[projects]] + branch = "master" + name = "github.com/sean-/seed" + packages = ["."] + revision = "e2103e2c35297fb7e17febb81e49b312087a2372" + +[[projects]] + branch = "master" + name = "golang.org/x/crypto" + packages = ["curve25519","ed25519","ed25519/internal/edwards25519","ssh","ssh/agent"] + revision = "bd6f299fb381e4c3393d1c4b1f0b94f5e77650c8" + +[solve-meta] + analyzer-name = "dep" + analyzer-version = 1 + inputs-digest = "28853a8970ee33112a9e7998b18e658bed04d177537ec69db678189f0b8a9a7d" + solver-name = "gps-cdcl" + solver-version = 1 diff --git a/vendor/github.com/joyent/triton-go/Gopkg.toml b/vendor/github.com/joyent/triton-go/Gopkg.toml new file mode 100644 index 000000000..3b85ddf9b --- /dev/null +++ b/vendor/github.com/joyent/triton-go/Gopkg.toml @@ -0,0 +1,42 @@ + +# Gopkg.toml example +# +# Refer to https://github.com/golang/dep/blob/master/docs/Gopkg.toml.md +# for detailed Gopkg.toml documentation. +# +# required = ["github.com/user/thing/cmd/thing"] +# ignored = ["github.com/user/project/pkgX", "bitbucket.org/user/project/pkgA/pkgY"] +# +# [[constraint]] +# name = "github.com/user/project" +# version = "1.0.0" +# +# [[constraint]] +# name = "github.com/user/project2" +# branch = "dev" +# source = "github.com/myfork/project2" +# +# [[override]] +# name = "github.com/x/y" +# version = "2.4.0" + + +[[constraint]] + 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" + +[[constraint]] + branch = "master" + name = "golang.org/x/crypto" diff --git a/vendor/github.com/joyent/triton-go/README.md b/vendor/github.com/joyent/triton-go/README.md index 1089c72da..5dbd5de0f 100644 --- a/vendor/github.com/joyent/triton-go/README.md +++ b/vendor/github.com/joyent/triton-go/README.md @@ -3,6 +3,8 @@ `triton-go` is an idiomatic library exposing a client SDK for Go applications using Joyent's Triton Compute and Storage (Manta) APIs. +[![Build Status](https://travis-ci.org/joyent/triton-go.svg?branch=master)](https://travis-ci.org/joyent/triton-go) [![Go Report Card](https://goreportcard.com/badge/github.com/joyent/triton-go)](https://goreportcard.com/report/github.com/joyent/triton-go) + ## Usage Triton uses [HTTP Signature][4] to sign the Date header in each HTTP request @@ -13,11 +15,17 @@ using a key stored with the local SSH Agent (using an [`SSHAgentSigner`][6]. To construct a Signer, use the `New*` range of methods in the `authentication` package. In the case of `authentication.NewSSHAgentSigner`, the parameters are the fingerprint of the key with which to sign, and the account name (normally -stored in the `SDC_ACCOUNT` environment variable). For example: +stored in the `TRITON_ACCOUNT` environment variable). There is also support for +passing in a username, this will allow you to use an account other than the main +Triton account. For example: -``` -const fingerprint := "a4:c6:f3:75:80:27:e0:03:a9:98:79:ef:c5:0a:06:11" -sshKeySigner, err := authentication.NewSSHAgentSigner(fingerprint, "AccountName") +```go +input := authentication.SSHAgentSignerInput{ + KeyID: "a4:c6:f3:75:80:27:e0:03:a9:98:79:ef:c5:0a:06:11", + AccountName: "AccountName", + Username: "Username", +} +sshKeySigner, err := authentication.NewSSHAgentSigner(input) if err != nil { log.Fatalf("NewSSHAgentSigner: %s", err) } @@ -34,17 +42,18 @@ their own seperate client. In order to initialize a package client, simply pass the global `triton.ClientConfig` struct into the client's constructor function. ```go - config := &triton.ClientConfig{ - TritonURL: os.Getenv("SDC_URL"), - MantaURL: os.Getenv("MANTA_URL"), - AccountName: accountName, - Signers: []authentication.Signer{sshKeySigner}, - } +config := &triton.ClientConfig{ + TritonURL: os.Getenv("TRITON_URL"), + MantaURL: os.Getenv("MANTA_URL"), + AccountName: accountName, + Username: os.Getenv("TRITON_USER"), + Signers: []authentication.Signer{sshKeySigner}, +} - c, err := compute.NewClient(config) - if err != nil { - log.Fatalf("compute.NewClient: %s", err) - } +c, err := compute.NewClient(config) +if err != nil { + log.Fatalf("compute.NewClient: %s", err) +} ``` Constructing `compute.Client` returns an interface which exposes `compute` API @@ -55,10 +64,10 @@ The same `triton.ClientConfig` will initialize the Manta `storage` client as well... ```go - c, err := storage.NewClient(config) - if err != nil { - log.Fatalf("storage.NewClient: %s", err) - } +c, err := storage.NewClient(config) +if err != nil { + log.Fatalf("storage.NewClient: %s", err) +} ``` ## Error Handling @@ -79,13 +88,14 @@ set: - `TRITON_TEST` - must be set to any value in order to indicate desire to create resources -- `SDC_URL` - the base endpoint for the Triton API -- `SDC_ACCOUNT` - the account name for the Triton API -- `SDC_KEY_ID` - the fingerprint of the SSH key identifying the key +- `TRITON_URL` - the base endpoint for the Triton API +- `TRITON_ACCOUNT` - the account name for the Triton API +- `TRITON_KEY_ID` - the fingerprint of the SSH key identifying the key -Additionally, you may set `SDC_KEY_MATERIAL` to the contents of an unencrypted +Additionally, you may set `TRITON_KEY_MATERIAL` to the contents of an unencrypted private key. If this is set, the PrivateKeySigner (see above) will be used - if -not the SSHAgentSigner will be used. +not the SSHAgentSigner will be used. You can also set `TRITON_USER` to run the tests +against an account other than the main Triton account ### Example Run @@ -94,9 +104,9 @@ The verbose output has been removed for brevity here. ``` $ HTTP_PROXY=http://localhost:8888 \ TRITON_TEST=1 \ - SDC_URL=https://us-sw-1.api.joyent.com \ - SDC_ACCOUNT=AccountName \ - SDC_KEY_ID=a4:c6:f3:75:80:27:e0:03:a9:98:79:ef:c5:0a:06:11 \ + TRITON_URL=https://us-sw-1.api.joyent.com \ + TRITON_ACCOUNT=AccountName \ + TRITON_KEY_ID=a4:c6:f3:75:80:27:e0:03:a9:98:79:ef:c5:0a:06:11 \ go test -v -run "TestAccKey" === RUN TestAccKey_Create --- PASS: TestAccKey_Create (12.46s) @@ -116,7 +126,7 @@ referencing your SSH key file use by your active `triton` CLI profile. ```sh $ eval "$(triton env us-sw-1)" -$ SDC_KEY_FILE=~/.ssh/triton-id_rsa go run examples/compute/instances.go +$ TRITON_KEY_FILE=~/.ssh/triton-id_rsa go run examples/compute/instances.go ``` The following is a complete example of how to initialize the `compute` package @@ -142,15 +152,21 @@ import ( ) func main() { - keyID := os.Getenv("SDC_KEY_ID") - accountName := os.Getenv("SDC_ACCOUNT") - keyMaterial := os.Getenv("SDC_KEY_MATERIAL") + keyID := os.Getenv("TRITON_KEY_ID") + accountName := os.Getenv("TRITON_ACCOUNT") + keyMaterial := os.Getenv("TRITON_KEY_MATERIAL") + userName := os.Getenv("TRITON_USER") var signer authentication.Signer var err error if keyMaterial == "" { - signer, err = authentication.NewSSHAgentSigner(keyID, accountName) + input := authentication.SSHAgentSignerInput{ + KeyID: keyID, + AccountName: accountName, + Username: userName, + } + signer, err = authentication.NewSSHAgentSigner(input) if err != nil { log.Fatalf("Error Creating SSH Agent Signer: {{err}}", err) } @@ -178,15 +194,22 @@ func main() { keyBytes = []byte(keyMaterial) } - signer, err = authentication.NewPrivateKeySigner(keyID, []byte(keyMaterial), accountName) + input := authentication.PrivateKeySignerInput{ + KeyID: keyID, + PrivateKeyMaterial: keyBytes, + AccountName: accountName, + Username: userName, + } + signer, err = authentication.NewPrivateKeySigner(input) if err != nil { log.Fatalf("Error Creating SSH Private Key Signer: {{err}}", err) } } config := &triton.ClientConfig{ - TritonURL: os.Getenv("SDC_URL"), + TritonURL: os.Getenv("TRITON_URL"), AccountName: accountName, + Username: userName, Signers: []authentication.Signer{signer}, } diff --git a/vendor/github.com/joyent/triton-go/authentication/dummy.go b/vendor/github.com/joyent/triton-go/authentication/dummy.go new file mode 100644 index 000000000..cd16273b6 --- /dev/null +++ b/vendor/github.com/joyent/triton-go/authentication/dummy.go @@ -0,0 +1,72 @@ +package authentication + +// DON'T USE THIS OUTSIDE TESTING ~ This key was only created to use for +// internal unit testing. It should never be used for acceptance testing either. +// +// This is just a randomly generated key pair. +var Dummy = struct { + Fingerprint string + PrivateKey []byte + PublicKey []byte + Signer Signer +}{ + "9f:d6:65:fc:d6:60:dc:d0:4e:db:2d:75:f7:92:8c:31", + []byte(`-----BEGIN RSA PRIVATE KEY----- +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 +-----END RSA PRIVATE KEY-----`), + []byte(`ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQC6L2U2MIlqEd4VIULodeL8I1fnzwL8vaAlTHrluc2mFUL3yFbXDOhG89cfHlpadvuURuk2E/oHRSgIENNo+C32Ed8P+g1D7odPVCkMgZ9mZcj3X+1yMZVIFvE3tZpi38ew+saNtqXC9A8KE3newxWYWQxMQNHkbfM4E4Aas6KaUJPzTAjZLleDSyl3yOMozkjWzANHTG24NacdNHU6GKf6kRAgTqCUtN9iruhDHqeYxdcE2aT5Jo8PUUDKOQX/zWKbYQdcHKA0TCtpWsNwDEraYqVTZcwmKiBAo1haWbdSRs0HpY8t7Xb4prTauYhWxKoilz9kIGtoCUJpJaVgKRT68naEf/7KnLltc2VtpOJZkr6+4js8LztPnTIs15dW41kDKfMOkEK+yOKIesH9kVF9V8Xo++JXwJX6bsZPN8DAmFV5JuWtHqe2zXorQdMPjy/nsNTRvS3lU68LtxY9KGz5p91IvdnxF4vkRYyBTXB++3JxEp6GINUaPcyzZx/zdEgA2vfzsb7UtxNBg/skm4HtK/LmP5Hr4eMjRyC7gQxQ91Z8I0YbKuXHAk20+ZRJ9qwfXFQqmqvt0L7Sw3j+B8nDvlZMBisBOQ/N7Ghg1oywYN+G6iGRhrdOfarWDeSqGxf9HQV3l3x3mpzXfupxELGJljP1/dtWRdBuFiY711rcqw== test-dummy-20171002140848`), + nil, +} + +func init() { + testSigner, _ := NewTestSigner() + Dummy.Signer = testSigner +} diff --git a/vendor/github.com/joyent/triton-go/authentication/private_key_signer.go b/vendor/github.com/joyent/triton-go/authentication/private_key_signer.go index 43bc286f0..35abff0f6 100644 --- a/vendor/github.com/joyent/triton-go/authentication/private_key_signer.go +++ b/vendor/github.com/joyent/triton-go/authentication/private_key_signer.go @@ -9,6 +9,7 @@ import ( "encoding/pem" "errors" "fmt" + "path" "strings" "github.com/hashicorp/errwrap" @@ -20,15 +21,23 @@ type PrivateKeySigner struct { keyFingerprint string algorithm string accountName string + userName string hashFunc crypto.Hash privateKey *rsa.PrivateKey } -func NewPrivateKeySigner(keyFingerprint string, privateKeyMaterial []byte, accountName string) (*PrivateKeySigner, error) { - keyFingerprintMD5 := strings.Replace(keyFingerprint, ":", "", -1) +type PrivateKeySignerInput struct { + KeyID string + PrivateKeyMaterial []byte + AccountName string + Username string +} - block, _ := pem.Decode(privateKeyMaterial) +func NewPrivateKeySigner(input PrivateKeySignerInput) (*PrivateKeySigner, error) { + keyFingerprintMD5 := strings.Replace(input.KeyID, ":", "", -1) + + block, _ := pem.Decode(input.PrivateKeyMaterial) if block == nil { return nil, errors.New("Error PEM-decoding private key material: nil block received") } @@ -51,13 +60,17 @@ func NewPrivateKeySigner(keyFingerprint string, privateKeyMaterial []byte, accou signer := &PrivateKeySigner{ formattedKeyFingerprint: displayKeyFingerprint, - keyFingerprint: keyFingerprint, - accountName: accountName, + keyFingerprint: input.KeyID, + accountName: input.AccountName, hashFunc: crypto.SHA1, privateKey: rsakey, } + if input.Username != "" { + signer.userName = input.Username + } + _, algorithm, err := signer.SignRaw("HelloWorld") if err != nil { return nil, fmt.Errorf("Cannot sign using ssh agent: %s", err) @@ -80,7 +93,13 @@ func (s *PrivateKeySigner) Sign(dateHeader string) (string, error) { } signedBase64 := base64.StdEncoding.EncodeToString(signed) - keyID := fmt.Sprintf("/%s/keys/%s", s.accountName, s.formattedKeyFingerprint) + var keyID string + if s.userName != "" { + + keyID = path.Join("/", s.accountName, "users", s.userName, "keys", s.formattedKeyFingerprint) + } else { + keyID = path.Join("/", s.accountName, "keys", s.formattedKeyFingerprint) + } return fmt.Sprintf(authorizationHeaderFormat, keyID, "rsa-sha1", headerName, signedBase64), nil } diff --git a/vendor/github.com/joyent/triton-go/authentication/ssh_agent_signer.go b/vendor/github.com/joyent/triton-go/authentication/ssh_agent_signer.go index ea84c5070..c068dae27 100644 --- a/vendor/github.com/joyent/triton-go/authentication/ssh_agent_signer.go +++ b/vendor/github.com/joyent/triton-go/authentication/ssh_agent_signer.go @@ -8,6 +8,7 @@ import ( "fmt" "net" "os" + "path" "strings" "github.com/hashicorp/errwrap" @@ -15,21 +16,32 @@ import ( "golang.org/x/crypto/ssh/agent" ) +var ( + ErrUnsetEnvVar = errors.New("SSH_AUTH_SOCK is not set") +) + type SSHAgentSigner struct { formattedKeyFingerprint string keyFingerprint string algorithm string accountName string + userName string keyIdentifier string agent agent.Agent key ssh.PublicKey } -func NewSSHAgentSigner(keyFingerprint, accountName string) (*SSHAgentSigner, error) { - sshAgentAddress := os.Getenv("SSH_AUTH_SOCK") - if sshAgentAddress == "" { - return nil, errors.New("SSH_AUTH_SOCK is not set") +type SSHAgentSignerInput struct { + KeyID string + AccountName string + Username string +} + +func NewSSHAgentSigner(input SSHAgentSignerInput) (*SSHAgentSigner, error) { + sshAgentAddress, agentOk := os.LookupEnv("SSH_AUTH_SOCK") + if !agentOk { + return nil, ErrUnsetEnvVar } conn, err := net.Dial("unix", sshAgentAddress) @@ -39,12 +51,41 @@ func NewSSHAgentSigner(keyFingerprint, accountName string) (*SSHAgentSigner, err ag := agent.NewClient(conn) - keys, err := ag.List() + signer := &SSHAgentSigner{ + keyFingerprint: input.KeyID, + accountName: input.AccountName, + agent: ag, + } + + matchingKey, err := signer.MatchKey() + if err != nil { + return nil, err + } + signer.key = matchingKey + signer.formattedKeyFingerprint = formatPublicKeyFingerprint(signer.key, true) + if input.Username != "" { + signer.userName = input.Username + signer.keyIdentifier = path.Join("/", signer.accountName, "users", input.Username, "keys", signer.formattedKeyFingerprint) + } else { + signer.keyIdentifier = path.Join("/", signer.accountName, "keys", signer.formattedKeyFingerprint) + } + + _, algorithm, err := signer.SignRaw("HelloWorld") + if err != nil { + return nil, fmt.Errorf("Cannot sign using ssh agent: %s", err) + } + signer.algorithm = algorithm + + return signer, nil +} + +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) } - keyFingerprintStripped := strings.TrimPrefix(keyFingerprint, "MD5:") + keyFingerprintStripped := strings.TrimPrefix(s.keyFingerprint, "MD5:") keyFingerprintStripped = strings.TrimPrefix(keyFingerprintStripped, "SHA256:") keyFingerprintStripped = strings.Replace(keyFingerprintStripped, ":", "", -1) @@ -64,27 +105,10 @@ func NewSSHAgentSigner(keyFingerprint, accountName string) (*SSHAgentSigner, err } if matchingKey == nil { - return nil, fmt.Errorf("No key in the SSH Agent matches fingerprint: %s", keyFingerprint) + return nil, fmt.Errorf("No key in the SSH Agent matches fingerprint: %s", s.keyFingerprint) } - formattedKeyFingerprint := formatPublicKeyFingerprint(matchingKey, true) - - signer := &SSHAgentSigner{ - formattedKeyFingerprint: formattedKeyFingerprint, - keyFingerprint: keyFingerprint, - accountName: accountName, - agent: ag, - key: matchingKey, - keyIdentifier: fmt.Sprintf("/%s/keys/%s", accountName, formattedKeyFingerprint), - } - - _, algorithm, err := signer.SignRaw("HelloWorld") - if err != nil { - return nil, fmt.Errorf("Cannot sign using ssh agent: %s", err) - } - signer.algorithm = algorithm - - return signer, nil + return matchingKey, nil } func (s *SSHAgentSigner) Sign(dateHeader string) (string, error) { diff --git a/vendor/github.com/joyent/triton-go/authentication/test_signer.go b/vendor/github.com/joyent/triton-go/authentication/test_signer.go new file mode 100644 index 000000000..a9c2c82d5 --- /dev/null +++ b/vendor/github.com/joyent/triton-go/authentication/test_signer.go @@ -0,0 +1,27 @@ +package authentication + +// TestSigner represents an authentication key signer which we can use for +// testing purposes only. This will largely be a stub to send through client +// unit tests. +type TestSigner struct{} + +// NewTestSigner constructs a new instance of test signer +func NewTestSigner() (Signer, error) { + return &TestSigner{}, nil +} + +func (s *TestSigner) DefaultAlgorithm() string { + return "" +} + +func (s *TestSigner) KeyFingerprint() string { + return "" +} + +func (s *TestSigner) Sign(dateHeader string) (string, error) { + return "", nil +} + +func (s *TestSigner) SignRaw(toSign string) (string, string, error) { + return "", "", nil +} diff --git a/vendor/github.com/joyent/triton-go/client/client.go b/vendor/github.com/joyent/triton-go/client/client.go index b01f86baf..cccf3a25d 100644 --- a/vendor/github.com/joyent/triton-go/client/client.go +++ b/vendor/github.com/joyent/triton-go/client/client.go @@ -6,6 +6,7 @@ import ( "crypto/tls" "encoding/json" "errors" + "fmt" "io" "net" "net/http" @@ -19,7 +20,14 @@ import ( const nilContext = "nil context" -var MissingKeyIdError = errors.New("Default SSH agent authentication requires SDC_KEY_ID") +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") + + BadTritonURL = "invalid format of triton URL" + BadMantaURL = "invalid format of manta URL" +) // Client represents a connection to the Triton Compute or Object Storage APIs. type Client struct { @@ -28,7 +36,7 @@ type Client struct { TritonURL url.URL MantaURL url.URL AccountName string - Endpoint string + Username string } // New is used to construct a Client in order to make API @@ -37,61 +45,93 @@ type Client struct { // At least one signer must be provided - example signers include // authentication.PrivateKeySigner and authentication.SSHAgentSigner. func New(tritonURL string, mantaURL string, accountName string, signers ...authentication.Signer) (*Client, error) { + if accountName == "" { + return nil, ErrAccountName + } + + if tritonURL == "" && mantaURL == "" { + return nil, ErrMissingURL + } + cloudURL, err := url.Parse(tritonURL) if err != nil { - return nil, errwrap.Wrapf("invalid endpoint URL: {{err}}", err) + return nil, errwrap.Wrapf(BadTritonURL+": {{err}}", err) } storageURL, err := url.Parse(mantaURL) if err != nil { - return nil, errwrap.Wrapf("invalid manta URL: {{err}}", err) + return nil, errwrap.Wrapf(BadMantaURL+": {{err}}", err) } - if accountName == "" { - return nil, errors.New("account name can not be empty") - } - - httpClient := &http.Client{ - Transport: httpTransport(false), - CheckRedirect: doNotFollowRedirects, - } - - newClient := &Client{ - HTTPClient: httpClient, - Authorizers: signers, - TritonURL: *cloudURL, - MantaURL: *storageURL, - AccountName: accountName, - // TODO(justinwr): Deprecated? - // Endpoint: tritonURL, - } - - var authorizers []authentication.Signer + authorizers := make([]authentication.Signer, 0) for _, key := range signers { if key != nil { authorizers = append(authorizers, key) } } + newClient := &Client{ + HTTPClient: &http.Client{ + Transport: httpTransport(false), + CheckRedirect: doNotFollowRedirects, + }, + Authorizers: authorizers, + TritonURL: *cloudURL, + MantaURL: *storageURL, + AccountName: accountName, + } + // Default to constructing an SSHAgentSigner if there are no other signers - // passed into NewClient and there's an SDC_KEY_ID value available in the - // user environ. - if len(authorizers) == 0 { - keyID := os.Getenv("SDC_KEY_ID") - if len(keyID) != 0 { - keySigner, err := authentication.NewSSHAgentSigner(keyID, accountName) - if err != nil { - return nil, errwrap.Wrapf("Problem initializing NewSSHAgentSigner: {{err}}", err) - } - newClient.Authorizers = append(authorizers, keySigner) - } else { - return nil, MissingKeyIdError + // passed into NewClient and there's an TRITON_KEY_ID and SSH_AUTH_SOCK + // available in the user's environ(7). + if len(newClient.Authorizers) == 0 { + if err := newClient.DefaultAuth(); err != nil { + return nil, err } } return newClient, nil } +var envPrefixes = []string{"TRITON", "SDC"} + +// GetTritonEnv looks up environment variables using the preferred "TRITON" +// prefix, but falls back to the SDC prefix. For example, looking up "USER" +// will search for "TRITON_USER" followed by "SDC_USER". If the environment +// variable is not set, an empty string is returned. GetTritonEnv() is used to +// aid in the transition and deprecation of the SDC_* environment variables. +func GetTritonEnv(name string) string { + for _, prefix := range envPrefixes { + if val, found := os.LookupEnv(prefix + "_" + name); found { + return val + } + } + + return "" +} + +// initDefaultAuth provides a default key signer for a client. This should only +// be used internally if the client has no other key signer for authenticating +// with Triton. We first look for both `SDC_KEY_ID` and `SSH_AUTH_SOCK` in the +// user's environ(7). If so we default to the SSH agent key signer. +func (c *Client) DefaultAuth() error { + tritonKeyId := GetTritonEnv("KEY_ID") + if tritonKeyId != "" { + input := authentication.SSHAgentSignerInput{ + KeyID: tritonKeyId, + AccountName: c.AccountName, + Username: c.Username, + } + defaultSigner, err := authentication.NewSSHAgentSigner(input) + if err != nil { + return errwrap.Wrapf("problem initializing NewSSHAgentSigner: {{err}}", err) + } + c.Authorizers = append(c.Authorizers, defaultSigner) + } + + return ErrDefaultAuth +} + // InsecureSkipTLSVerify turns off TLS verification for the client connection. This // allows connection to an endpoint with a certificate which was signed by a non- // trusted CA, such as self-signed certificates. This can be useful when connecting @@ -112,8 +152,8 @@ func httpTransport(insecureSkipTLSVerify bool) *http.Transport { KeepAlive: 30 * time.Second, }).Dial, TLSHandshakeTimeout: 10 * time.Second, - DisableKeepAlives: true, - MaxIdleConnsPerHost: -1, + MaxIdleConns: 10, + IdleConnTimeout: 15 * time.Second, TLSClientConfig: &tls.Config{ InsecureSkipVerify: insecureSkipTLSVerify, }, @@ -158,7 +198,7 @@ func (c *Client) ExecuteRequestURIParams(ctx context.Context, inputs RequestInpu body := inputs.Body query := inputs.Query - var requestBody io.ReadSeeker + var requestBody io.Reader if body != nil { marshaled, err := json.MarshalIndent(body, "", " ") if err != nil { @@ -217,7 +257,7 @@ func (c *Client) ExecuteRequestRaw(ctx context.Context, inputs RequestInput) (*h path := inputs.Path body := inputs.Body - var requestBody io.ReadSeeker + var requestBody io.Reader if body != nil { marshaled, err := json.MarshalIndent(body, "", " ") if err != nil { @@ -270,7 +310,7 @@ func (c *Client) ExecuteRequestStorage(ctx context.Context, inputs RequestInput) endpoint := c.MantaURL endpoint.Path = path - var requestBody io.ReadSeeker + var requestBody io.Reader if body != nil { marshaled, err := json.MarshalIndent(body, "", " ") if err != nil { @@ -323,10 +363,17 @@ func (c *Client) ExecuteRequestStorage(ctx context.Context, inputs RequestInput) 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) + 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 } @@ -335,7 +382,7 @@ type RequestNoEncodeInput struct { Path string Query *url.Values Headers *http.Header - Body io.ReadSeeker + Body io.Reader } func (c *Client) ExecuteRequestNoEncode(ctx context.Context, inputs RequestNoEncodeInput) (io.ReadCloser, http.Header, error) { diff --git a/vendor/github.com/joyent/triton-go/compute/client.go b/vendor/github.com/joyent/triton-go/compute/client.go index 8ce726cb1..73582e2ae 100644 --- a/vendor/github.com/joyent/triton-go/compute/client.go +++ b/vendor/github.com/joyent/triton-go/compute/client.go @@ -38,7 +38,7 @@ func (c *ComputeClient) Images() *ImagesClient { return &ImagesClient{c.Client} } -// Machines returns a Compute client used for accessing functions pertaining to +// Machine returns a Compute client used for accessing functions pertaining to // machine functionality in the Triton API. func (c *ComputeClient) Instances() *InstancesClient { return &InstancesClient{c.Client} @@ -55,3 +55,9 @@ func (c *ComputeClient) Packages() *PackagesClient { func (c *ComputeClient) Services() *ServicesClient { return &ServicesClient{c.Client} } + +// Snapshots returns a Compute client used for accessing functions pertaining to +// Snapshots functionality in the Triton API. +func (c *ComputeClient) Snapshots() *SnapshotsClient { + return &SnapshotsClient{c.Client} +} diff --git a/vendor/github.com/joyent/triton-go/compute/datacenters.go b/vendor/github.com/joyent/triton-go/compute/datacenters.go index 7acaf20a1..365474e17 100644 --- a/vendor/github.com/joyent/triton-go/compute/datacenters.go +++ b/vendor/github.com/joyent/triton-go/compute/datacenters.go @@ -81,7 +81,7 @@ func (c *DataCentersClient) Get(ctx context.Context, input *GetDataCenterInput) } if resp.StatusCode != http.StatusFound { - return nil, fmt.Errorf("Error executing Get request: expected status code 302, got %s", + return nil, fmt.Errorf("Error executing Get request: expected status code 302, got %d", resp.StatusCode) } diff --git a/vendor/github.com/joyent/triton-go/compute/errors.go b/vendor/github.com/joyent/triton-go/compute/errors.go index ae2a4bf5c..db31472ff 100644 --- a/vendor/github.com/joyent/triton-go/compute/errors.go +++ b/vendor/github.com/joyent/triton-go/compute/errors.go @@ -94,6 +94,12 @@ 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 { diff --git a/vendor/github.com/joyent/triton-go/compute/instances.go b/vendor/github.com/joyent/triton-go/compute/instances.go index 337e6a482..9be14bb21 100644 --- a/vendor/github.com/joyent/triton-go/compute/instances.go +++ b/vendor/github.com/joyent/triton-go/compute/instances.go @@ -97,10 +97,13 @@ func (c *InstancesClient) Get(ctx context.Context, input *GetInstanceInput) (*In Path: path, } response, err := c.client.ExecuteRequestRaw(ctx, reqInputs) - if response != nil { + if response == nil { + return nil, errwrap.Wrapf("Error executing Get request: {{err}}", err) + } + if response.Body != nil { defer response.Body.Close() } - if response == nil || response.StatusCode == http.StatusNotFound || response.StatusCode == http.StatusGone { + if response.StatusCode == http.StatusNotFound || response.StatusCode == http.StatusGone { return nil, &client.TritonError{ StatusCode: response.StatusCode, Code: "ResourceNotFound", @@ -962,6 +965,32 @@ func (c *InstancesClient) Start(ctx context.Context, input *StartInstanceInput) return nil } +type RebootInstanceInput struct { + InstanceID string +} + +func (c *InstancesClient) Reboot(ctx context.Context, input *RebootInstanceInput) error { + path := fmt.Sprintf("/%s/machines/%s", c.client.AccountName, input.InstanceID) + + params := &url.Values{} + params.Set("action", "reboot") + + reqInputs := client.RequestInput{ + Method: http.MethodPost, + Path: path, + Query: params, + } + respReader, err := c.client.ExecuteRequestURIParams(ctx, reqInputs) + if respReader != nil { + defer respReader.Close() + } + if err != nil { + return errwrap.Wrapf("Error executing Start request: {{err}}", err) + } + + return nil +} + var reservedInstanceCNSTags = map[string]struct{}{ CNSTagDisable: {}, CNSTagReversePTR: {}, diff --git a/vendor/github.com/joyent/triton-go/compute/ping.go b/vendor/github.com/joyent/triton-go/compute/ping.go new file mode 100644 index 000000000..c0f62dfaa --- /dev/null +++ b/vendor/github.com/joyent/triton-go/compute/ping.go @@ -0,0 +1,56 @@ +package compute + +import ( + "context" + "encoding/json" + "fmt" + "net/http" + + "github.com/hashicorp/errwrap" + "github.com/joyent/triton-go/client" +) + +const pingEndpoint = "/--ping" + +type CloudAPI struct { + Versions []string `json:"versions"` +} + +type PingOutput struct { + Ping string `json:"ping"` + CloudAPI CloudAPI `json:"cloudapi"` +} + +// Ping sends a request to the '/--ping' endpoint and returns a `pong` as well +// as a list of API version numbers your instance of CloudAPI is presenting. +func (c *ComputeClient) Ping(ctx context.Context) (*PingOutput, error) { + reqInputs := client.RequestInput{ + Method: http.MethodGet, + Path: pingEndpoint, + } + response, err := c.Client.ExecuteRequestRaw(ctx, reqInputs) + if response == nil { + return nil, fmt.Errorf("Ping request has empty response") + } + 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) + } + + return result, nil +} diff --git a/vendor/github.com/joyent/triton-go/compute/snapshots.go b/vendor/github.com/joyent/triton-go/compute/snapshots.go new file mode 100644 index 000000000..72873ad39 --- /dev/null +++ b/vendor/github.com/joyent/triton-go/compute/snapshots.go @@ -0,0 +1,157 @@ +package compute + +import ( + "context" + "encoding/json" + "fmt" + "net/http" + + "time" + + "github.com/hashicorp/errwrap" + "github.com/joyent/triton-go/client" +) + +type SnapshotsClient struct { + client *client.Client +} + +type Snapshot struct { + Name string + State string + Created time.Time + Updated time.Time +} + +type ListSnapshotsInput struct { + MachineID string +} + +func (c *SnapshotsClient) List(ctx context.Context, input *ListSnapshotsInput) ([]*Snapshot, error) { + path := fmt.Sprintf("/%s/machines/%s/snapshots", c.client.AccountName, input.MachineID) + reqInputs := client.RequestInput{ + Method: http.MethodGet, + Path: path, + } + 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) + } + + 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 result, nil +} + +type GetSnapshotInput struct { + MachineID string + Name string +} + +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) + reqInputs := client.RequestInput{ + Method: http.MethodGet, + Path: path, + } + 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) + } + + 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 result, nil +} + +type DeleteSnapshotInput struct { + MachineID string + Name string +} + +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) + reqInputs := client.RequestInput{ + Method: http.MethodDelete, + Path: path, + } + 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 nil +} + +type StartMachineFromSnapshotInput struct { + MachineID string + Name string +} + +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) + reqInputs := client.RequestInput{ + Method: http.MethodPost, + Path: path, + } + 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 nil +} + +type CreateSnapshotInput struct { + MachineID string + Name string +} + +func (c *SnapshotsClient) Create(ctx context.Context, input *CreateSnapshotInput) (*Snapshot, error) { + path := fmt.Sprintf("/%s/machines/%s/snapshots", c.client.AccountName, input.MachineID) + + data := make(map[string]interface{}) + data["name"] = input.Name + + reqInputs := client.RequestInput{ + Method: http.MethodPost, + Path: path, + Body: data, + } + + respReader, err := c.client.ExecuteRequest(ctx, reqInputs) + if respReader != nil { + defer respReader.Close() + } + if err != nil { + return nil, errwrap.Wrapf("Error executing Create request: {{err}}", err) + } + + 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 result, nil +} diff --git a/vendor/github.com/joyent/triton-go/network/firewall.go b/vendor/github.com/joyent/triton-go/network/firewall.go index 60054702a..8dbd969f3 100644 --- a/vendor/github.com/joyent/triton-go/network/firewall.go +++ b/vendor/github.com/joyent/triton-go/network/firewall.go @@ -6,6 +6,8 @@ import ( "fmt" "net/http" + "time" + "github.com/hashicorp/errwrap" "github.com/joyent/triton-go/client" ) @@ -227,7 +229,7 @@ type ListMachineRulesInput struct { } func (c *FirewallClient) ListMachineRules(ctx context.Context, input *ListMachineRulesInput) ([]*FirewallRule, error) { - path := fmt.Sprintf("/%s/machines/%s/firewallrules", c.client.AccountName, input.MachineID) + path := fmt.Sprintf("/%s/machines/%s/fwrules", c.client.AccountName, input.MachineID) reqInputs := client.RequestInput{ Method: http.MethodGet, Path: path, @@ -243,7 +245,56 @@ func (c *FirewallClient) ListMachineRules(ctx context.Context, input *ListMachin 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, errwrap.Wrapf("Error decoding ListMachineRules response: {{err}}", err) + } + + return result, nil +} + +type ListRuleMachinesInput struct { + ID string +} + +type Machine struct { + ID string `json:"id"` + Name string `json:"name"` + Type string `json:"type"` + Brand string `json:"brand"` + State string `json:"state"` + Image string `json:"image"` + Memory int `json:"memory"` + Disk int `json:"disk"` + Metadata map[string]string `json:"metadata"` + Tags map[string]interface{} `json:"tags"` + Created time.Time `json:"created"` + Updated time.Time `json:"updated"` + Docker bool `json:"docker"` + IPs []string `json:"ips"` + Networks []string `json:"networks"` + PrimaryIP string `json:"primaryIp"` + FirewallEnabled bool `json:"firewall_enabled"` + ComputeNode string `json:"compute_node"` + Package string `json:"package"` +} + +func (c *FirewallClient) ListRuleMachines(ctx context.Context, input *ListRuleMachinesInput) ([]*Machine, error) { + path := fmt.Sprintf("/%s/fwrules/%s/machines", c.client.AccountName, input.ID) + reqInputs := client.RequestInput{ + Method: http.MethodGet, + Path: path, + } + 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) + } + + 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 result, nil diff --git a/vendor/github.com/joyent/triton-go/triton.go b/vendor/github.com/joyent/triton-go/triton.go index b5bacd255..f9202984e 100644 --- a/vendor/github.com/joyent/triton-go/triton.go +++ b/vendor/github.com/joyent/triton-go/triton.go @@ -14,5 +14,6 @@ type ClientConfig struct { TritonURL string MantaURL string AccountName string + Username string Signers []authentication.Signer } diff --git a/vendor/vendor.json b/vendor/vendor.json index 35704c419..a229cabce 100644 --- a/vendor/vendor.json +++ b/vendor/vendor.json @@ -823,34 +823,34 @@ "revision": "c01cf91b011868172fdcd9f41838e80c9d716264" }, { - "checksumSHA1": "EqvUu0Ku0Ec5Tk6yhGNOuOr8yeA=", + "checksumSHA1": "oINoQSRkPinChzwEHr3VatB9++Y=", "path": "github.com/joyent/triton-go", - "revision": "5a58ad2cdec95cddd1e0a2e56f559341044b04f0", - "revisionTime": "2017-10-17T16:55:58Z" + "revision": "86ba9699869b6cd5ea3290faad7be659efc7d6ce", + "revisionTime": "2017-12-28T20:20:46Z" }, { - "checksumSHA1": "JKf97EAAAZFQ6Wf8qN9X7TWqNBY=", + "checksumSHA1": "d6pxw8DLxYehLr92fWZTLEWVws8=", "path": "github.com/joyent/triton-go/authentication", - "revision": "5a58ad2cdec95cddd1e0a2e56f559341044b04f0", - "revisionTime": "2017-10-17T16:55:58Z" + "revision": "86ba9699869b6cd5ea3290faad7be659efc7d6ce", + "revisionTime": "2017-12-28T20:20:46Z" }, { - "checksumSHA1": "dlO1or0cyVMAmZzyLcBuoy+M0xU=", + "checksumSHA1": "GCHfn8d1Mhswm7n7IRnT0n/w+dw=", "path": "github.com/joyent/triton-go/client", - "revision": "5a58ad2cdec95cddd1e0a2e56f559341044b04f0", - "revisionTime": "2017-10-17T16:55:58Z" + "revision": "86ba9699869b6cd5ea3290faad7be659efc7d6ce", + "revisionTime": "2017-12-28T20:20:46Z" }, { - "checksumSHA1": "O/y7BfKJFUf3A8TCRMXgo9HSb1w=", + "checksumSHA1": "U9D/fCNr+1uD1p/O0PW0yD/izqc=", "path": "github.com/joyent/triton-go/compute", - "revision": "5a58ad2cdec95cddd1e0a2e56f559341044b04f0", - "revisionTime": "2017-10-17T16:55:58Z" + "revision": "86ba9699869b6cd5ea3290faad7be659efc7d6ce", + "revisionTime": "2017-12-28T20:20:46Z" }, { - "checksumSHA1": "gyLtPyKlcumRSkrAH+SsDQo1GnY=", + "checksumSHA1": "9OdR/eI3qbmADruKn6yE1Dbx3LE=", "path": "github.com/joyent/triton-go/network", - "revision": "5a58ad2cdec95cddd1e0a2e56f559341044b04f0", - "revisionTime": "2017-10-17T16:55:58Z" + "revision": "86ba9699869b6cd5ea3290faad7be659efc7d6ce", + "revisionTime": "2017-12-28T20:20:46Z" }, { "checksumSHA1": "gEjGS03N1eysvpQ+FCHTxPcbxXc=", diff --git a/website/source/docs/builders/triton.html.md b/website/source/docs/builders/triton.html.md index 80c54a5b4..2a62dbe12 100644 --- a/website/source/docs/builders/triton.html.md +++ b/website/source/docs/builders/triton.html.md @@ -92,6 +92,9 @@ builder. of `triton_key_id` is stored. For example `/home/soandso/.ssh/id_rsa`. If this is not specified, the SSH agent is used to sign requests with the `triton_key_id` specified. + +- `triton_user` (string) - The username of a user who has access to your Triton + account. - `source_machine_firewall_enabled` (boolean) - Whether or not the firewall of the VM used to create an image of is enabled. The Triton firewall only @@ -149,7 +152,7 @@ builder. ## Basic Example -Below is a minimal example to create an joyent-brand image on the Joyent public +Below is a minimal example to create an image on the Joyent public cloud: ``` json