Merge pull request #5531 from stack72/bump-triton-dependencies

Bump Joyent/triton-go to modern version of the SDK
This commit is contained in:
James Nugent 2017-10-31 11:21:08 -05:00 committed by GitHub
commit a495948dc5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
30 changed files with 2852 additions and 2184 deletions

View File

@ -6,10 +6,13 @@ import (
"io/ioutil"
"os"
"github.com/hashicorp/errwrap"
"github.com/hashicorp/packer/helper/communicator"
"github.com/hashicorp/packer/template/interpolate"
"github.com/joyent/triton-go"
tgo "github.com/joyent/triton-go"
"github.com/joyent/triton-go/authentication"
"github.com/joyent/triton-go/compute"
"github.com/joyent/triton-go/network"
)
// AccessConfig is for common configuration related to Triton access
@ -106,8 +109,39 @@ func (c *AccessConfig) createPrivateKeySigner() (authentication.Signer, error) {
return signer, nil
}
func (c *AccessConfig) CreateTritonClient() (*triton.Client, error) {
return triton.NewClient(c.Endpoint, c.Account, c.signer)
func (c *AccessConfig) CreateTritonClient() (*Client, error) {
config := &tgo.ClientConfig{
AccountName: c.Account,
TritonURL: c.Endpoint,
Signers: []authentication.Signer{c.signer},
}
return &Client{
config: config,
}, nil
}
type Client struct {
config *tgo.ClientConfig
}
func (c *Client) Compute() (*compute.ComputeClient, error) {
computeClient, err := compute.NewClient(c.config)
if err != nil {
return nil, errwrap.Wrapf("Error Creating Triton Compute Client: {{err}}", err)
}
return computeClient, nil
}
func (c *Client) Network() (*network.NetworkClient, error) {
networkClient, err := network.NewClient(c.config)
if err != nil {
return nil, errwrap.Wrapf("Error Creating Triton Network Client: {{err}}", err)
}
return networkClient, nil
}
func (c *AccessConfig) Comm() communicator.Config {

View File

@ -7,11 +7,12 @@ import (
"time"
"github.com/hashicorp/packer/packer"
"github.com/joyent/triton-go"
"github.com/joyent/triton-go/client"
"github.com/joyent/triton-go/compute"
)
type driverTriton struct {
client *triton.Client
client *Client
ui packer.Ui
}
@ -28,7 +29,8 @@ func NewDriverTriton(ui packer.Ui, config Config) (Driver, error) {
}
func (d *driverTriton) CreateImageFromMachine(machineId string, config Config) (string, error) {
image, err := d.client.Images().CreateImageFromMachine(context.Background(), &triton.CreateImageFromMachineInput{
computeClient, _ := d.client.Compute()
image, err := computeClient.Images().CreateFromMachine(context.Background(), &compute.CreateImageFromMachineInput{
MachineID: machineId,
Name: config.ImageName,
Version: config.ImageVersion,
@ -46,7 +48,8 @@ func (d *driverTriton) CreateImageFromMachine(machineId string, config Config) (
}
func (d *driverTriton) CreateMachine(config Config) (string, error) {
input := &triton.CreateMachineInput{
computeClient, _ := d.client.Compute()
input := &compute.CreateInstanceInput{
Package: config.MachinePackage,
Image: config.MachineImage,
Metadata: config.MachineMetadata,
@ -66,7 +69,7 @@ func (d *driverTriton) CreateMachine(config Config) (string, error) {
input.Networks = config.MachineNetworks
}
machine, err := d.client.Machines().CreateMachine(context.Background(), input)
machine, err := computeClient.Instances().Create(context.Background(), input)
if err != nil {
return "", err
}
@ -75,19 +78,22 @@ func (d *driverTriton) CreateMachine(config Config) (string, error) {
}
func (d *driverTriton) DeleteImage(imageId string) error {
return d.client.Images().DeleteImage(context.Background(), &triton.DeleteImageInput{
computeClient, _ := d.client.Compute()
return computeClient.Images().Delete(context.Background(), &compute.DeleteImageInput{
ImageID: imageId,
})
}
func (d *driverTriton) DeleteMachine(machineId string) error {
return d.client.Machines().DeleteMachine(context.Background(), &triton.DeleteMachineInput{
computeClient, _ := d.client.Compute()
return computeClient.Instances().Delete(context.Background(), &compute.DeleteInstanceInput{
ID: machineId,
})
}
func (d *driverTriton) GetMachineIP(machineId string) (string, error) {
machine, err := d.client.Machines().GetMachine(context.Background(), &triton.GetMachineInput{
computeClient, _ := d.client.Compute()
machine, err := computeClient.Instances().Get(context.Background(), &compute.GetInstanceInput{
ID: machineId,
})
if err != nil {
@ -98,8 +104,9 @@ func (d *driverTriton) GetMachineIP(machineId string) (string, error) {
}
func (d *driverTriton) StopMachine(machineId string) error {
return d.client.Machines().StopMachine(context.Background(), &triton.StopMachineInput{
MachineID: machineId,
computeClient, _ := d.client.Compute()
return computeClient.Instances().Stop(context.Background(), &compute.StopInstanceInput{
InstanceID: machineId,
})
}
@ -111,7 +118,8 @@ func (d *driverTriton) StopMachine(machineId string) error {
func (d *driverTriton) WaitForMachineState(machineId string, state string, timeout time.Duration) error {
return waitFor(
func() (bool, error) {
machine, err := d.client.Machines().GetMachine(context.Background(), &triton.GetMachineInput{
computeClient, _ := d.client.Compute()
machine, err := computeClient.Instances().Get(context.Background(), &compute.GetInstanceInput{
ID: machineId,
})
if machine == nil {
@ -130,14 +138,15 @@ func (d *driverTriton) WaitForMachineState(machineId string, state string, timeo
func (d *driverTriton) WaitForMachineDeletion(machineId string, timeout time.Duration) error {
return waitFor(
func() (bool, error) {
_, err := d.client.Machines().GetMachine(context.Background(), &triton.GetMachineInput{
computeClient, _ := d.client.Compute()
_, err := computeClient.Instances().Get(context.Background(), &compute.GetInstanceInput{
ID: machineId,
})
if err != nil {
// 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.(*triton.TritonError); ok && triErr.StatusCode == http.StatusGone {
if triErr, ok := err.(*client.TritonError); ok && triErr.StatusCode == http.StatusGone {
return true, nil
}
}
@ -152,7 +161,8 @@ func (d *driverTriton) WaitForMachineDeletion(machineId string, timeout time.Dur
func (d *driverTriton) WaitForImageCreation(imageId string, timeout time.Duration) error {
return waitFor(
func() (bool, error) {
image, err := d.client.Images().GetImage(context.Background(), &triton.GetImageInput{
computeClient, _ := d.client.Compute()
image, err := computeClient.Images().Get(context.Background(), &compute.GetImageInput{
ImageID: imageId,
})
if image == nil {

View File

@ -1,12 +1,19 @@
# triton-go
`go-triton` is an idiomatic library exposing a client SDK for Go applications using the Joyent Triton API.
`triton-go` is an idiomatic library exposing a client SDK for Go applications
using Joyent's Triton Compute and Storage (Manta) APIs.
## Usage
Triton uses [HTTP Signature][4] to sign the Date header in each HTTP request made to the Triton API. Currently, requests can be signed using either a private key file loaded from disk (using an [`authentication.PrivateKeySigner`][5]), or using a key stored with the local SSH Agent (using an [`SSHAgentSigner`][6].
Triton uses [HTTP Signature][4] to sign the Date header in each HTTP request
made to the Triton API. Currently, requests can be signed using either a private
key file loaded from disk (using an [`authentication.PrivateKeySigner`][5]), or
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:
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:
```
const fingerprint := "a4:c6:f3:75:80:27:e0:03:a9:98:79:ef:c5:0a:06:11"
@ -16,178 +23,69 @@ if err != nil {
}
```
An appropriate key fingerprint can be generated using `ssh-keygen`:
An appropriate key fingerprint can be generated using `ssh-keygen`.
```
ssh-keygen -Emd5 -lf ~/.ssh/id_rsa.pub | cut -d " " -f 2 | sed 's/MD5://'
```
To construct a Client, use the `NewClient` function, passing in the endpoint, account name and constructed signer:
Each top level package, `account`, `compute`, `identity`, `network`, all have
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
client, err := triton.NewClient("https://us-sw-1.api.joyent.com/", "AccountName", sshKeySigner)
if err != nil {
log.Fatalf("NewClient: %s", err)
}
config := &triton.ClientConfig{
TritonURL: os.Getenv("SDC_URL"),
MantaURL: os.Getenv("MANTA_URL"),
AccountName: accountName,
Signers: []authentication.Signer{sshKeySigner},
}
c, err := compute.NewClient(config)
if err != nil {
log.Fatalf("compute.NewClient: %s", err)
}
```
Having constructed a `triton.Client`, use the methods available to access functionality by functional grouping. For example, for access to operations on SSH keys, use the `Keys()` method to obtain a client which has access to the `CreateKey`, `ListKeys` and `DeleteKey` operations. For access to operations on Machines, use the `Machines()` method to obtain a client which has access to the `RenameMachine`, `GetMachineMetadata`, `GetMachineTag`, and other operations.
Constructing `compute.Client` returns an interface which exposes `compute` API
resources. The same goes for all other packages. Reference their unique
documentation for more information.
Operation methods take their formal parameters via a struct named `OperationInput` - for example when creating an SSH key, the `CreateKeyInput` struct is used with the `func CreateKey(*CreateKeyInput) (*Key, error)` method. This allows specification of named parameters:
The same `triton.ClientConfig` will initialize the Manta `storage` client as
well...
```
client := state.Client().Keys()
key, err := client.CreateKey(&CreateKeyInput{
Name: "tempKey",
Key: "ssh-rsa .....",
})
if err != nil {
panic(err)
}
// Key contains the return value.
```go
c, err := storage.NewClient(config)
if err != nil {
log.Fatalf("storage.NewClient: %s", err)
}
```
## Error Handling
If an error is returned by the HTTP API, the `error` returned from the function will contain an instance of `triton.TritonError` in the chain. Error wrapping is performed using the [errwrap][7] library from HashiCorp.
If an error is returned by the HTTP API, the `error` returned from the function
will contain an instance of `compute.TritonError` in the chain. Error wrapping
is performed using the [errwrap][7] library from HashiCorp.
## Completeness
## Acceptance Tests
The following list is updated as new functionality is added. The complete list of operations is taken from the [CloudAPI documentation](https://apidocs.joyent.com/cloudapi).
Acceptance Tests run directly against the Triton API, so you will need either a
local installation of Triton or an account with Joyent's Public Cloud offering
in order to run them. The tests create real resources (and thus cost real
money)!
- Accounts
- [x] GetAccount
- [x] UpdateAccount
- Keys
- [x] ListKeys
- [x] GetKey
- [x] CreateKey
- [x] DeleteKey
- Users
- [ ] ListUsers
- [ ] GetUser
- [ ] CreateUser
- [ ] UpdateUser
- [ ] ChangeUserPassword
- [ ] DeleteUser
- Roles
- [x] ListRoles
- [x] GetRole
- [x] CreateRole
- [x] UpdateRole
- [x] DeleteRole
- Role Tags
- [ ] SetRoleTags
- Policies
- [ ] ListPolicies
- [ ] GetPolicy
- [ ] CreatePolicy
- [ ] UpdatePolicy
- [ ] DeletePolicy
- User SSH Keys
- [x] ListUserKeys
- [x] GetUserKey
- [x] CreateUserKey
- [x] DeleteUserKey
- Config
- [x] GetConfig
- [x] UpdateConfig
- Datacenters
- [x] ListDatacenters
- [x] GetDatacenter
- Services
- [x] ListServices
- Images
- [x] ListImages
- [x] GetImage
- [x] DeleteImage
- [x] ExportImage
- [x] CreateImageFromMachine
- [x] UpdateImage
- Packages
- [x] ListPackages
- [x] GetPackage
- Instances
- [ ] ListMachines
- [x] GetMachine
- [x] CreateMachine
- [ ] StopMachine
- [ ] StartMachine
- [ ] RebootMachine
- [x] ResizeMachine
- [x] RenameMachine
- [x] EnableMachineFirewall
- [x] DisableMachineFirewall
- [ ] CreateMachineSnapshot
- [ ] StartMachineFromSnapshot
- [ ] ListMachineSnapshots
- [ ] GetMachineSnapshot
- [ ] DeleteMachineSnapshot
- [x] UpdateMachineMetadata
- [ ] ListMachineMetadata
- [ ] GetMachineMetadata
- [ ] DeleteMachineMetadata
- [ ] DeleteAllMachineMetadata
- [x] AddMachineTags
- [x] ReplaceMachineTags
- [x] ListMachineTags
- [x] GetMachineTag
- [x] DeleteMachineTag
- [x] DeleteMachineTags
- [x] DeleteMachine
- [ ] MachineAudit
- Analytics
- [ ] DescribeAnalytics
- [ ] ListInstrumentations
- [ ] GetInstrumentation
- [ ] GetInstrumentationValue
- [ ] GetInstrumentationHeatmap
- [ ] GetInstrumentationHeatmapDetails
- [ ] CreateInstrumentation
- [ ] DeleteInstrumentation
- Firewall Rules
- [x] ListFirewallRules
- [x] GetFirewallRule
- [x] CreateFirewallRule
- [x] UpdateFirewallRule
- [x] EnableFirewallRule
- [x] DisableFirewallRule
- [x] DeleteFirewallRule
- [ ] ListMachineFirewallRules
- [x] ListFirewallRuleMachines
- Fabrics
- [x] ListFabricVLANs
- [x] CreateFabricVLAN
- [x] GetFabricVLAN
- [x] UpdateFabricVLAN
- [x] DeleteFabricVLAN
- [x] ListFabricNetworks
- [x] CreateFabricNetwork
- [x] GetFabricNetwork
- [x] DeleteFabricNetwork
- Networks
- [x] ListNetworks
- [x] GetNetwork
- Nics
- [ ] ListNics
- [ ] GetNic
- [x] AddNic
- [x] RemoveNic
In order to run acceptance tests, the following environment variables must be
set:
## Running Acceptance Tests
Acceptance Tests run directly against the Triton API, so you will need either a local installation or Triton or an account with Joyent in order to run them. The tests create real resources (and thus cost real money!)
In order to run acceptance tests, the following environment variables must be set:
- `TRITON_TEST` - must be set to any value in order to indicate desire to create resources
- `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
Additionally, you may set `SDC_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.
Additionally, you may set `SDC_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.
### Example Run
@ -207,10 +105,111 @@ $ HTTP_PROXY=http://localhost:8888 \
=== RUN TestAccKey_Delete
--- PASS: TestAccKey_Delete (15.08s)
PASS
ok github.com/jen20/triton-go 31.861s
ok github.com/joyent/triton-go 31.861s
```
## Example API
There's an `examples/` directory available with sample code setup for many of
the APIs within this library. Most of these can be run using `go run` and
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
```
The following is a complete example of how to initialize the `compute` package
client and list all instances under an account. More detailed usage of this
library follows.
```go
package main
import (
"context"
"fmt"
"io/ioutil"
"log"
"os"
"time"
triton "github.com/joyent/triton-go"
"github.com/joyent/triton-go/authentication"
"github.com/joyent/triton-go/compute"
)
func main() {
keyID := os.Getenv("SDC_KEY_ID")
accountName := os.Getenv("SDC_ACCOUNT")
keyMaterial := os.Getenv("SDC_KEY_MATERIAL")
var signer authentication.Signer
var err error
if keyMaterial == "" {
signer, err = authentication.NewSSHAgentSigner(keyID, accountName)
if err != nil {
log.Fatalf("Error Creating SSH Agent Signer: {{err}}", err)
}
} else {
var keyBytes []byte
if _, err = os.Stat(keyMaterial); err == nil {
keyBytes, err = ioutil.ReadFile(keyMaterial)
if err != nil {
log.Fatalf("Error reading key material from %s: %s",
keyMaterial, err)
}
block, _ := pem.Decode(keyBytes)
if block == nil {
log.Fatalf(
"Failed to read key material '%s': no key found", keyMaterial)
}
if block.Headers["Proc-Type"] == "4,ENCRYPTED" {
log.Fatalf(
"Failed to read key '%s': password protected keys are\n"+
"not currently supported. Please decrypt the key prior to use.", keyMaterial)
}
} else {
keyBytes = []byte(keyMaterial)
}
signer, err = authentication.NewPrivateKeySigner(keyID, []byte(keyMaterial), accountName)
if err != nil {
log.Fatalf("Error Creating SSH Private Key Signer: {{err}}", err)
}
}
config := &triton.ClientConfig{
TritonURL: os.Getenv("SDC_URL"),
AccountName: accountName,
Signers: []authentication.Signer{signer},
}
c, err := compute.NewClient(config)
if err != nil {
log.Fatalf("compute.NewClient: %s", err)
}
listInput := &compute.ListInstancesInput{}
instances, err := c.Instances().List(context.Background(), listInput)
if err != nil {
log.Fatalf("compute.Instances.List: %v", err)
}
numInstances := 0
for _, instance := range instances {
numInstances++
fmt.Println(fmt.Sprintf("-- Instance: %v", instance.Name))
}
}
```
[4]: https://github.com/joyent/node-http-signature/blob/master/http_signing.md
[5]: https://godoc.org/github.com/joyent/go-triton/authentication
[6]: https://godoc.org/github.com/joyent/go-triton/authentication
[5]: https://godoc.org/github.com/joyent/triton-go/authentication
[6]: https://godoc.org/github.com/joyent/triton-go/authentication
[7]: https://github.com/hashicorp/go-errwrap

View File

@ -1,95 +0,0 @@
package triton
import (
"context"
"encoding/json"
"fmt"
"net/http"
"time"
"github.com/hashicorp/errwrap"
)
type AccountsClient struct {
*Client
}
// Accounts returns a c used for accessing functions pertaining
// to Account functionality in the Triton API.
func (c *Client) Accounts() *AccountsClient {
return &AccountsClient{c}
}
type Account struct {
ID string `json:"id"`
Login string `json:"login"`
Email string `json:"email"`
CompanyName string `json:"companyName"`
FirstName string `json:"firstName"`
LastName string `json:"lastName"`
Address string `json:"address"`
PostalCode string `json:"postalCode"`
City string `json:"city"`
State string `json:"state"`
Country string `json:"country"`
Phone string `json:"phone"`
Created time.Time `json:"created"`
Updated time.Time `json:"updated"`
TritonCNSEnabled bool `json:"triton_cns_enabled"`
}
type GetAccountInput struct{}
func (client *AccountsClient) GetAccount(ctx context.Context, input *GetAccountInput) (*Account, error) {
path := fmt.Sprintf("/%s", client.accountName)
respReader, err := client.executeRequest(ctx, http.MethodGet, path, nil)
if respReader != nil {
defer respReader.Close()
}
if err != nil {
return nil, errwrap.Wrapf("Error executing GetAccount request: {{err}}", err)
}
var result *Account
decoder := json.NewDecoder(respReader)
if err = decoder.Decode(&result); err != nil {
return nil, errwrap.Wrapf("Error decoding GetAccount response: {{err}}", err)
}
return result, nil
}
type UpdateAccountInput struct {
Email string `json:"email,omitempty"`
CompanyName string `json:"companyName,omitempty"`
FirstName string `json:"firstName,omitempty"`
LastName string `json:"lastName,omitempty"`
Address string `json:"address,omitempty"`
PostalCode string `json:"postalCode,omitempty"`
City string `json:"city,omitempty"`
State string `json:"state,omitempty"`
Country string `json:"country,omitempty"`
Phone string `json:"phone,omitempty"`
TritonCNSEnabled bool `json:"triton_cns_enabled,omitempty"`
}
// UpdateAccount updates your account details with the given parameters.
// TODO(jen20) Work out a safe way to test this
func (client *AccountsClient) UpdateAccount(ctx context.Context, input *UpdateAccountInput) (*Account, error) {
path := fmt.Sprintf("/%s", client.accountName)
respReader, err := client.executeRequest(ctx, http.MethodPost, path, input)
if respReader != nil {
defer respReader.Close()
}
if err != nil {
return nil, errwrap.Wrapf("Error executing UpdateAccount request: {{err}}", err)
}
var result *Account
decoder := json.NewDecoder(respReader)
if err = decoder.Decode(&result); err != nil {
return nil, errwrap.Wrapf("Error decoding UpdateAccount response: {{err}}", err)
}
return result, nil
}

View File

@ -18,6 +18,7 @@ import (
type PrivateKeySigner struct {
formattedKeyFingerprint string
keyFingerprint string
algorithm string
accountName string
hashFunc crypto.Hash
@ -48,14 +49,22 @@ func NewPrivateKeySigner(keyFingerprint string, privateKeyMaterial []byte, accou
return nil, errors.New("Private key file does not match public key fingerprint")
}
return &PrivateKeySigner{
signer := &PrivateKeySigner{
formattedKeyFingerprint: displayKeyFingerprint,
keyFingerprint: keyFingerprint,
accountName: accountName,
hashFunc: crypto.SHA1,
privateKey: rsakey,
}, nil
}
_, 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 *PrivateKeySigner) Sign(dateHeader string) (string, error) {
@ -74,3 +83,24 @@ func (s *PrivateKeySigner) Sign(dateHeader string) (string, error) {
keyID := fmt.Sprintf("/%s/keys/%s", s.accountName, s.formattedKeyFingerprint)
return fmt.Sprintf(authorizationHeaderFormat, keyID, "rsa-sha1", headerName, signedBase64), nil
}
func (s *PrivateKeySigner) SignRaw(toSign string) (string, string, error) {
hash := s.hashFunc.New()
hash.Write([]byte(toSign))
digest := hash.Sum(nil)
signed, err := rsa.SignPKCS1v15(rand.Reader, s.privateKey, s.hashFunc, digest)
if err != nil {
return "", "", errwrap.Wrapf("Error signing date header: {{err}}", err)
}
signedBase64 := base64.StdEncoding.EncodeToString(signed)
return signedBase64, "rsa-sha1", nil
}
func (s *PrivateKeySigner) KeyFingerprint() string {
return s.formattedKeyFingerprint
}
func (s *PrivateKeySigner) DefaultAlgorithm() string {
return s.algorithm
}

View File

@ -3,5 +3,8 @@ package authentication
const authorizationHeaderFormat = `Signature keyId="%s",algorithm="%s",headers="%s",signature="%s"`
type Signer interface {
DefaultAlgorithm() string
KeyFingerprint() string
Sign(dateHeader string) (string, error)
SignRaw(toSign string) (string, string, error)
}

View File

@ -2,6 +2,8 @@ package authentication
import (
"crypto/md5"
"crypto/sha256"
"encoding/base64"
"errors"
"fmt"
"net"
@ -16,6 +18,7 @@ import (
type SSHAgentSigner struct {
formattedKeyFingerprint string
keyFingerprint string
algorithm string
accountName string
keyIdentifier string
@ -41,15 +44,21 @@ func NewSSHAgentSigner(keyFingerprint, accountName string) (*SSHAgentSigner, err
return nil, errwrap.Wrapf("Error listing keys in SSH Agent: %s", err)
}
keyFingerprintMD5 := strings.Replace(keyFingerprint, ":", "", -1)
keyFingerprintStripped := strings.TrimPrefix(keyFingerprint, "MD5:")
keyFingerprintStripped = strings.TrimPrefix(keyFingerprintStripped, "SHA256:")
keyFingerprintStripped = strings.Replace(keyFingerprintStripped, ":", "", -1)
var matchingKey ssh.PublicKey
for _, key := range keys {
h := md5.New()
h.Write(key.Marshal())
fp := fmt.Sprintf("%x", h.Sum(nil))
keyMD5 := md5.New()
keyMD5.Write(key.Marshal())
finalizedMD5 := fmt.Sprintf("%x", keyMD5.Sum(nil))
if fp == keyFingerprintMD5 {
keySHA256 := sha256.New()
keySHA256.Write(key.Marshal())
finalizedSHA256 := base64.RawStdEncoding.EncodeToString(keySHA256.Sum(nil))
if keyFingerprintStripped == finalizedMD5 || keyFingerprintStripped == finalizedSHA256 {
matchingKey = key
}
}
@ -60,14 +69,22 @@ func NewSSHAgentSigner(keyFingerprint, accountName string) (*SSHAgentSigner, err
formattedKeyFingerprint := formatPublicKeyFingerprint(matchingKey, true)
return &SSHAgentSigner{
signer := &SSHAgentSigner{
formattedKeyFingerprint: formattedKeyFingerprint,
keyFingerprint: keyFingerprint,
accountName: accountName,
agent: ag,
key: matchingKey,
keyIdentifier: fmt.Sprintf("/%s/keys/%s", accountName, formattedKeyFingerprint),
}, nil
}
_, 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) Sign(dateHeader string) (string, error) {
@ -102,3 +119,41 @@ func (s *SSHAgentSigner) Sign(dateHeader string) (string, error) {
return fmt.Sprintf(authorizationHeaderFormat, s.keyIdentifier,
authSignature.SignatureType(), headerName, authSignature.String()), nil
}
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)
}
keyFormat, err := keyFormatToKeyType(signature.Format)
if err != nil {
return "", "", errwrap.Wrapf("Error reading signature: {{err}}", err)
}
var authSignature httpAuthSignature
switch keyFormat {
case "rsa":
authSignature, err = newRSASignature(signature.Blob)
if err != nil {
return "", "", errwrap.Wrapf("Error reading signature: {{err}}", err)
}
case "ecdsa":
authSignature, err = newECDSASignature(signature.Blob)
if err != nil {
return "", "", errwrap.Wrapf("Error reading signature: {{err}}", err)
}
default:
return "", "", fmt.Errorf("Unsupported algorithm from SSH agent: %s", signature.Format)
}
return authSignature.String(), authSignature.SignatureType(), nil
}
func (s *SSHAgentSigner) KeyFingerprint() string {
return s.formattedKeyFingerprint
}
func (s *SSHAgentSigner) DefaultAlgorithm() string {
return s.algorithm
}

View File

@ -1,195 +0,0 @@
package triton
import (
"bytes"
"context"
"crypto/tls"
"encoding/json"
"errors"
"io"
"net"
"net/http"
"net/url"
"time"
"github.com/hashicorp/errwrap"
"github.com/joyent/triton-go/authentication"
)
const nilContext = "nil context"
// Client represents a connection to the Triton API.
type Client struct {
client *http.Client
authorizer []authentication.Signer
apiURL url.URL
accountName string
}
// NewClient is used to construct a Client in order to make API
// requests to the Triton API.
//
// At least one signer must be provided - example signers include
// authentication.PrivateKeySigner and authentication.SSHAgentSigner.
func NewClient(endpoint string, accountName string, signers ...authentication.Signer) (*Client, error) {
apiURL, err := url.Parse(endpoint)
if err != nil {
return nil, errwrap.Wrapf("invalid endpoint: {{err}}", err)
}
if accountName == "" {
return nil, errors.New("account name can not be empty")
}
httpClient := &http.Client{
Transport: httpTransport(false),
CheckRedirect: doNotFollowRedirects,
}
return &Client{
client: httpClient,
authorizer: signers,
apiURL: *apiURL,
accountName: accountName,
}, nil
}
// 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
// to temporary Triton installations such as Triton Cloud-On-A-Laptop.
func (c *Client) InsecureSkipTLSVerify() {
if c.client == nil {
return
}
c.client.Transport = httpTransport(true)
}
func httpTransport(insecureSkipTLSVerify bool) *http.Transport {
return &http.Transport{
Proxy: http.ProxyFromEnvironment,
Dial: (&net.Dialer{
Timeout: 30 * time.Second,
KeepAlive: 30 * time.Second,
}).Dial,
TLSHandshakeTimeout: 10 * time.Second,
DisableKeepAlives: true,
MaxIdleConnsPerHost: -1,
TLSClientConfig: &tls.Config{
InsecureSkipVerify: insecureSkipTLSVerify,
},
}
}
func doNotFollowRedirects(*http.Request, []*http.Request) error {
return http.ErrUseLastResponse
}
func (c *Client) executeRequestURIParams(ctx context.Context, method, path string, body interface{}, query *url.Values) (io.ReadCloser, error) {
var requestBody io.ReadSeeker
if body != nil {
marshaled, err := json.MarshalIndent(body, "", " ")
if err != nil {
return nil, err
}
requestBody = bytes.NewReader(marshaled)
}
endpoint := c.apiURL
endpoint.Path = path
if query != nil {
endpoint.RawQuery = query.Encode()
}
req, err := http.NewRequest(method, endpoint.String(), requestBody)
if err != nil {
return nil, errwrap.Wrapf("Error constructing HTTP request: {{err}}", err)
}
dateHeader := time.Now().UTC().Format(time.RFC1123)
req.Header.Set("date", dateHeader)
authHeader, err := c.authorizer[0].Sign(dateHeader)
if err != nil {
return nil, errwrap.Wrapf("Error signing HTTP request: {{err}}", err)
}
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")
if body != nil {
req.Header.Set("Content-Type", "application/json")
}
resp, err := c.client.Do(req.WithContext(ctx))
if err != nil {
return nil, errwrap.Wrapf("Error executing HTTP request: {{err}}", err)
}
if resp.StatusCode >= http.StatusOK && resp.StatusCode < http.StatusMultipleChoices {
return resp.Body, nil
}
return nil, c.decodeError(resp.StatusCode, resp.Body)
}
func (c *Client) decodeError(statusCode int, body io.Reader) error {
err := &TritonError{
StatusCode: statusCode,
}
errorDecoder := json.NewDecoder(body)
if err := errorDecoder.Decode(err); err != nil {
return errwrap.Wrapf("Error decoding error response: {{err}}", err)
}
return err
}
func (c *Client) executeRequest(ctx context.Context, method, path string, body interface{}) (io.ReadCloser, error) {
return c.executeRequestURIParams(ctx, method, path, body, nil)
}
func (c *Client) executeRequestRaw(ctx context.Context, method, path string, body interface{}) (*http.Response, error) {
var requestBody io.ReadSeeker
if body != nil {
marshaled, err := json.MarshalIndent(body, "", " ")
if err != nil {
return nil, err
}
requestBody = bytes.NewReader(marshaled)
}
endpoint := c.apiURL
endpoint.Path = path
req, err := http.NewRequest(method, endpoint.String(), requestBody)
if err != nil {
return nil, errwrap.Wrapf("Error constructing HTTP request: {{err}}", err)
}
dateHeader := time.Now().UTC().Format(time.RFC1123)
req.Header.Set("date", dateHeader)
authHeader, err := c.authorizer[0].Sign(dateHeader)
if err != nil {
return nil, errwrap.Wrapf("Error signing HTTP request: {{err}}", err)
}
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")
if body != nil {
req.Header.Set("Content-Type", "application/json")
}
resp, err := c.client.Do(req.WithContext(ctx))
if err != nil {
return nil, errwrap.Wrapf("Error executing HTTP request: {{err}}", err)
}
return resp, nil
}

397
vendor/github.com/joyent/triton-go/client/client.go generated vendored Normal file
View File

@ -0,0 +1,397 @@
package client
import (
"bytes"
"context"
"crypto/tls"
"encoding/json"
"errors"
"io"
"net"
"net/http"
"net/url"
"os"
"time"
"github.com/hashicorp/errwrap"
"github.com/joyent/triton-go/authentication"
)
const nilContext = "nil context"
var MissingKeyIdError = errors.New("Default SSH agent authentication requires SDC_KEY_ID")
// Client represents a connection to the Triton Compute or Object Storage APIs.
type Client struct {
HTTPClient *http.Client
Authorizers []authentication.Signer
TritonURL url.URL
MantaURL url.URL
AccountName string
Endpoint string
}
// New is used to construct a Client in order to make API
// requests to the Triton API.
//
// 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) {
cloudURL, err := url.Parse(tritonURL)
if err != nil {
return nil, errwrap.Wrapf("invalid endpoint URL: {{err}}", err)
}
storageURL, err := url.Parse(mantaURL)
if err != nil {
return nil, errwrap.Wrapf("invalid manta URL: {{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
for _, key := range signers {
if key != nil {
authorizers = append(authorizers, key)
}
}
// 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
}
}
return newClient, nil
}
// 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
// to temporary Triton installations such as Triton Cloud-On-A-Laptop.
func (c *Client) InsecureSkipTLSVerify() {
if c.HTTPClient == nil {
return
}
c.HTTPClient.Transport = httpTransport(true)
}
func httpTransport(insecureSkipTLSVerify bool) *http.Transport {
return &http.Transport{
Proxy: http.ProxyFromEnvironment,
Dial: (&net.Dialer{
Timeout: 30 * time.Second,
KeepAlive: 30 * time.Second,
}).Dial,
TLSHandshakeTimeout: 10 * time.Second,
DisableKeepAlives: true,
MaxIdleConnsPerHost: -1,
TLSClientConfig: &tls.Config{
InsecureSkipVerify: insecureSkipTLSVerify,
},
}
}
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,
}
errorDecoder := json.NewDecoder(body)
if err := errorDecoder.Decode(err); err != nil {
return errwrap.Wrapf("Error decoding error response: {{err}}", err)
}
return err
}
// -----------------------------------------------------------------------------
type RequestInput struct {
Method string
Path string
Query *url.Values
Headers *http.Header
Body interface{}
}
func (c *Client) ExecuteRequestURIParams(ctx context.Context, inputs RequestInput) (io.ReadCloser, error) {
method := inputs.Method
path := inputs.Path
body := inputs.Body
query := inputs.Query
var requestBody io.ReadSeeker
if body != nil {
marshaled, err := json.MarshalIndent(body, "", " ")
if err != nil {
return nil, err
}
requestBody = bytes.NewReader(marshaled)
}
endpoint := c.TritonURL
endpoint.Path = path
if query != nil {
endpoint.RawQuery = query.Encode()
}
req, err := http.NewRequest(method, endpoint.String(), requestBody)
if err != nil {
return nil, errwrap.Wrapf("Error constructing HTTP request: {{err}}", err)
}
dateHeader := time.Now().UTC().Format(time.RFC1123)
req.Header.Set("date", dateHeader)
// NewClient ensures there's always an authorizer (unless this is called
// outside that constructor).
authHeader, err := c.Authorizers[0].Sign(dateHeader)
if err != nil {
return nil, errwrap.Wrapf("Error signing HTTP request: {{err}}", err)
}
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")
if body != nil {
req.Header.Set("Content-Type", "application/json")
}
resp, err := c.HTTPClient.Do(req.WithContext(ctx))
if err != nil {
return nil, errwrap.Wrapf("Error executing HTTP request: {{err}}", err)
}
if resp.StatusCode >= http.StatusOK && resp.StatusCode < http.StatusMultipleChoices {
return resp.Body, nil
}
return nil, c.DecodeError(resp.StatusCode, resp.Body)
}
func (c *Client) ExecuteRequest(ctx context.Context, inputs RequestInput) (io.ReadCloser, error) {
return c.ExecuteRequestURIParams(ctx, inputs)
}
func (c *Client) ExecuteRequestRaw(ctx context.Context, inputs RequestInput) (*http.Response, error) {
method := inputs.Method
path := inputs.Path
body := inputs.Body
var requestBody io.ReadSeeker
if body != nil {
marshaled, err := json.MarshalIndent(body, "", " ")
if err != nil {
return nil, err
}
requestBody = bytes.NewReader(marshaled)
}
endpoint := c.TritonURL
endpoint.Path = path
req, err := http.NewRequest(method, endpoint.String(), requestBody)
if err != nil {
return nil, errwrap.Wrapf("Error constructing HTTP request: {{err}}", err)
}
dateHeader := time.Now().UTC().Format(time.RFC1123)
req.Header.Set("date", dateHeader)
// NewClient ensures there's always an authorizer (unless this is called
// outside that constructor).
authHeader, err := c.Authorizers[0].Sign(dateHeader)
if err != nil {
return nil, errwrap.Wrapf("Error signing HTTP request: {{err}}", err)
}
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")
if body != nil {
req.Header.Set("Content-Type", "application/json")
}
resp, err := c.HTTPClient.Do(req.WithContext(ctx))
if err != nil {
return nil, errwrap.Wrapf("Error executing HTTP request: {{err}}", err)
}
return resp, nil
}
func (c *Client) ExecuteRequestStorage(ctx context.Context, inputs RequestInput) (io.ReadCloser, http.Header, error) {
method := inputs.Method
path := inputs.Path
query := inputs.Query
headers := inputs.Headers
body := inputs.Body
endpoint := c.MantaURL
endpoint.Path = path
var requestBody io.ReadSeeker
if body != nil {
marshaled, err := json.MarshalIndent(body, "", " ")
if err != nil {
return nil, nil, err
}
requestBody = bytes.NewReader(marshaled)
}
req, err := http.NewRequest(method, endpoint.String(), requestBody)
if err != nil {
return nil, nil, errwrap.Wrapf("Error constructing HTTP request: {{err}}", err)
}
if body != nil && (headers == nil || headers.Get("Content-Type") == "") {
req.Header.Set("Content-Type", "application/json")
}
if headers != nil {
for key, values := range *headers {
for _, value := range values {
req.Header.Set(key, value)
}
}
}
dateHeader := time.Now().UTC().Format(time.RFC1123)
req.Header.Set("date", dateHeader)
authHeader, err := c.Authorizers[0].Sign(dateHeader)
if err != nil {
return nil, nil, errwrap.Wrapf("Error signing HTTP request: {{err}}", err)
}
req.Header.Set("Authorization", authHeader)
req.Header.Set("Accept", "*/*")
req.Header.Set("User-Agent", "manta-go client API")
if query != nil {
req.URL.RawQuery = query.Encode()
}
resp, err := c.HTTPClient.Do(req.WithContext(ctx))
if err != nil {
return nil, nil, errwrap.Wrapf("Error executing HTTP request: {{err}}", err)
}
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
}
type RequestNoEncodeInput struct {
Method string
Path string
Query *url.Values
Headers *http.Header
Body io.ReadSeeker
}
func (c *Client) ExecuteRequestNoEncode(ctx context.Context, inputs RequestNoEncodeInput) (io.ReadCloser, http.Header, error) {
method := inputs.Method
path := inputs.Path
query := inputs.Query
headers := inputs.Headers
body := inputs.Body
endpoint := c.MantaURL
endpoint.Path = path
req, err := http.NewRequest(method, endpoint.String(), body)
if err != nil {
return nil, nil, errwrap.Wrapf("Error constructing HTTP request: {{err}}", err)
}
if headers != nil {
for key, values := range *headers {
for _, value := range values {
req.Header.Set(key, value)
}
}
}
dateHeader := time.Now().UTC().Format(time.RFC1123)
req.Header.Set("date", dateHeader)
authHeader, err := c.Authorizers[0].Sign(dateHeader)
if err != nil {
return nil, nil, errwrap.Wrapf("Error signing HTTP request: {{err}}", err)
}
req.Header.Set("Authorization", authHeader)
req.Header.Set("Accept", "*/*")
req.Header.Set("User-Agent", "manta-go client API")
if query != nil {
req.URL.RawQuery = query.Encode()
}
resp, err := c.HTTPClient.Do(req.WithContext(ctx))
if err != nil {
return nil, nil, errwrap.Wrapf("Error executing HTTP request: {{err}}", err)
}
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
}

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

@ -0,0 +1,190 @@
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
}

57
vendor/github.com/joyent/triton-go/compute/client.go generated vendored Normal file
View File

@ -0,0 +1,57 @@
package compute
import (
triton "github.com/joyent/triton-go"
"github.com/joyent/triton-go/client"
)
type ComputeClient struct {
Client *client.Client
}
func newComputeClient(client *client.Client) *ComputeClient {
return &ComputeClient{
Client: client,
}
}
// NewClient returns a new client for working with Compute endpoints and
// resources within CloudAPI
func NewClient(config *triton.ClientConfig) (*ComputeClient, error) {
// TODO: Utilize config interface within the function itself
client, err := client.New(config.TritonURL, config.MantaURL, config.AccountName, config.Signers...)
if err != nil {
return nil, err
}
return newComputeClient(client), nil
}
// Datacenters returns a Compute client used for accessing functions pertaining
// to DataCenter functionality in the Triton API.
func (c *ComputeClient) Datacenters() *DataCentersClient {
return &DataCentersClient{c.Client}
}
// Images returns a Compute client used for accessing functions pertaining to
// Images functionality in the Triton API.
func (c *ComputeClient) Images() *ImagesClient {
return &ImagesClient{c.Client}
}
// Machines 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}
}
// Packages returns a Compute client used for accessing functions pertaining to
// Packages functionality in the Triton API.
func (c *ComputeClient) Packages() *PackagesClient {
return &PackagesClient{c.Client}
}
// Services returns a Compute client used for accessing functions pertaining to
// Services functionality in the Triton API.
func (c *ComputeClient) Services() *ServicesClient {
return &ServicesClient{c.Client}
}

View File

@ -0,0 +1,97 @@
package compute
import (
"encoding/json"
"errors"
"fmt"
"net/http"
"sort"
"context"
"github.com/hashicorp/errwrap"
"github.com/joyent/triton-go/client"
)
type DataCentersClient struct {
client *client.Client
}
type DataCenter struct {
Name string `json:"name"`
URL string `json:"url"`
}
type ListDataCentersInput struct{}
func (c *DataCentersClient) List(ctx context.Context, _ *ListDataCentersInput) ([]*DataCenter, error) {
path := fmt.Sprintf("/%s/datacenters", c.client.AccountName)
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 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)
}
keys := make([]string, len(intermediate))
i := 0
for k := range intermediate {
keys[i] = k
i++
}
sort.Strings(keys)
result := make([]*DataCenter, len(intermediate))
i = 0
for _, key := range keys {
result[i] = &DataCenter{
Name: key,
URL: intermediate[key],
}
i++
}
return result, nil
}
type GetDataCenterInput struct {
Name string
}
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)
if err != nil {
return nil, errwrap.Wrapf("Error executing Get request: {{err}}", err)
}
if resp.StatusCode != http.StatusFound {
return nil, fmt.Errorf("Error executing Get request: expected status code 302, got %s",
resp.StatusCode)
}
location := resp.Header.Get("Location")
if location == "" {
return nil, errors.New("Error decoding Get response: no Location header")
}
return &DataCenter{
Name: input.Name,
URL: location,
}, nil
}

View File

@ -1,123 +1,112 @@
package triton
package compute
import (
"fmt"
"github.com/hashicorp/errwrap"
"github.com/joyent/triton-go/client"
)
// 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)
}
// IsBadRequest tests whether err wraps a TritonError with
// 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 TritonError with
// 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 TritonError with
// 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 TritonError with
// 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 TritonError with
// 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 TritonError with
// 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 TritonError with
// 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 TritonError with
// 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 TritonError with
// 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 TritonError with
// 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 TritonError with
// 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 TritonError with
// IsRequestMoved tests whether err wraps a client.TritonError with
// code RequestMoved
func IsRequestMoved(err error) bool {
return isSpecificError(err, "RequestMoved")
}
// IsResourceNotFound tests whether err wraps a TritonError with
// 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 TritonError with
// IsUnknownError tests whether err wraps a client.TritonError with
// code UnknownError
func IsUnknownError(err error) bool {
return isSpecificError(err, "UnknownError")
}
// isSpecificError checks whether the error represented by err wraps
// an underlying TritonError with code errorCode.
// an underlying client.TritonError with code errorCode.
func isSpecificError(err error, errorCode string) bool {
if err == nil {
return false
}
tritonErrorInterface := errwrap.GetType(err.(error), &TritonError{})
tritonErrorInterface := errwrap.GetType(err.(error), &client.TritonError{})
if tritonErrorInterface == nil {
return false
}
tritonErr := tritonErrorInterface.(*TritonError)
tritonErr := tritonErrorInterface.(*client.TritonError)
if tritonErr.Code == errorCode {
return true
}

View File

@ -1,4 +1,4 @@
package triton
package compute
import (
"context"
@ -9,16 +9,11 @@ import (
"time"
"github.com/hashicorp/errwrap"
"github.com/joyent/triton-go/client"
)
type ImagesClient struct {
*Client
}
// Images returns a c used for accessing functions pertaining to
// Images functionality in the Triton API.
func (c *Client) Images() *ImagesClient {
return &ImagesClient{c}
client *client.Client
}
type ImageFile struct {
@ -44,25 +39,62 @@ type Image struct {
Tags map[string]string `json:"tags"`
EULA string `json:"eula"`
ACL []string `json:"acl"`
Error TritonError `json:"error"`
Error client.TritonError `json:"error"`
}
type ListImagesInput struct{}
type ListImagesInput struct {
Name string
OS string
Version string
Public bool
State string
Owner string
Type string
}
func (client *ImagesClient) ListImages(ctx context.Context, _ *ListImagesInput) ([]*Image, error) {
path := fmt.Sprintf("/%s/images", client.accountName)
respReader, err := client.executeRequest(ctx, http.MethodGet, path, nil)
func (c *ImagesClient) List(ctx context.Context, input *ListImagesInput) ([]*Image, error) {
path := fmt.Sprintf("/%s/images", c.client.AccountName)
query := &url.Values{}
if input.Name != "" {
query.Set("name", input.Name)
}
if input.OS != "" {
query.Set("os", input.OS)
}
if input.Version != "" {
query.Set("version", input.Version)
}
if input.Public {
query.Set("public", "true")
}
if input.State != "" {
query.Set("state", input.State)
}
if input.Owner != "" {
query.Set("owner", input.Owner)
}
if input.Type != "" {
query.Set("type", input.Type)
}
reqInputs := client.RequestInput{
Method: http.MethodGet,
Path: path,
Query: query,
}
respReader, err := c.client.ExecuteRequestURIParams(ctx, reqInputs)
if respReader != nil {
defer respReader.Close()
}
if err != nil {
return nil, errwrap.Wrapf("Error executing ListImages request: {{err}}", err)
return nil, errwrap.Wrapf("Error executing List request: {{err}}", err)
}
var result []*Image
decoder := json.NewDecoder(respReader)
if err = decoder.Decode(&result); err != nil {
return nil, errwrap.Wrapf("Error decoding ListImages response: {{err}}", err)
return nil, errwrap.Wrapf("Error decoding List response: {{err}}", err)
}
return result, nil
@ -72,20 +104,24 @@ type GetImageInput struct {
ImageID string
}
func (client *ImagesClient) GetImage(ctx context.Context, input *GetImageInput) (*Image, error) {
path := fmt.Sprintf("/%s/images/%s", client.accountName, input.ImageID)
respReader, err := client.executeRequest(ctx, http.MethodGet, path, nil)
func (c *ImagesClient) Get(ctx context.Context, input *GetImageInput) (*Image, error) {
path := fmt.Sprintf("/%s/images/%s", c.client.AccountName, input.ImageID)
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 GetImage request: {{err}}", err)
return nil, errwrap.Wrapf("Error executing Get request: {{err}}", err)
}
var result *Image
decoder := json.NewDecoder(respReader)
if err = decoder.Decode(&result); err != nil {
return nil, errwrap.Wrapf("Error decoding GetImage response: {{err}}", err)
return nil, errwrap.Wrapf("Error decoding Get response: {{err}}", err)
}
return result, nil
@ -95,14 +131,18 @@ type DeleteImageInput struct {
ImageID string
}
func (client *ImagesClient) DeleteImage(ctx context.Context, input *DeleteImageInput) error {
path := fmt.Sprintf("/%s/images/%s", client.accountName, input.ImageID)
respReader, err := client.executeRequest(ctx, http.MethodDelete, path, nil)
func (c *ImagesClient) Delete(ctx context.Context, input *DeleteImageInput) error {
path := fmt.Sprintf("/%s/images/%s", c.client.AccountName, input.ImageID)
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 DeleteKey request: {{err}}", err)
return errwrap.Wrapf("Error executing Delete request: {{err}}", err)
}
return nil
@ -119,24 +159,29 @@ type MantaLocation struct {
ManifestPath string `json:"manifest_path"`
}
func (client *ImagesClient) ExportImage(ctx context.Context, input *ExportImageInput) (*MantaLocation, error) {
path := fmt.Sprintf("/%s/images/%s", client.accountName, input.ImageID)
func (c *ImagesClient) Export(ctx context.Context, input *ExportImageInput) (*MantaLocation, error) {
path := fmt.Sprintf("/%s/images/%s", c.client.AccountName, input.ImageID)
query := &url.Values{}
query.Set("action", "export")
query.Set("manta_path", input.MantaPath)
respReader, err := client.executeRequestURIParams(ctx, http.MethodGet, path, nil, query)
reqInputs := client.RequestInput{
Method: http.MethodGet,
Path: path,
Query: query,
}
respReader, err := c.client.ExecuteRequestURIParams(ctx, reqInputs)
if respReader != nil {
defer respReader.Close()
}
if err != nil {
return nil, errwrap.Wrapf("Error executing GetImage request: {{err}}", err)
return nil, errwrap.Wrapf("Error executing Get request: {{err}}", err)
}
var result *MantaLocation
decoder := json.NewDecoder(respReader)
if err = decoder.Decode(&result); err != nil {
return nil, errwrap.Wrapf("Error decoding GetImage response: {{err}}", err)
return nil, errwrap.Wrapf("Error decoding Get response: {{err}}", err)
}
return result, nil
@ -153,20 +198,25 @@ type CreateImageFromMachineInput struct {
Tags map[string]string `json:"tags,omitempty"`
}
func (client *ImagesClient) CreateImageFromMachine(ctx context.Context, input *CreateImageFromMachineInput) (*Image, error) {
path := fmt.Sprintf("/%s/images", client.accountName)
respReader, err := client.executeRequest(ctx, http.MethodPost, path, input)
func (c *ImagesClient) CreateFromMachine(ctx context.Context, input *CreateImageFromMachineInput) (*Image, error) {
path := fmt.Sprintf("/%s/images", c.client.AccountName)
reqInputs := client.RequestInput{
Method: http.MethodPost,
Path: path,
Body: input,
}
respReader, err := c.client.ExecuteRequest(ctx, reqInputs)
if respReader != nil {
defer respReader.Close()
}
if err != nil {
return nil, errwrap.Wrapf("Error executing CreateImageFromMachine request: {{err}}", err)
return nil, errwrap.Wrapf("Error executing CreateFromMachine request: {{err}}", err)
}
var result *Image
decoder := json.NewDecoder(respReader)
if err = decoder.Decode(&result); err != nil {
return nil, errwrap.Wrapf("Error decoding CreateImageFromMachine response: {{err}}", err)
return nil, errwrap.Wrapf("Error decoding CreateFromMachine response: {{err}}", err)
}
return result, nil
@ -183,23 +233,29 @@ type UpdateImageInput struct {
Tags map[string]string `json:"tags,omitempty"`
}
func (client *ImagesClient) UpdateImage(ctx context.Context, input *UpdateImageInput) (*Image, error) {
path := fmt.Sprintf("/%s/images/%s", client.accountName, input.ImageID)
func (c *ImagesClient) Update(ctx context.Context, input *UpdateImageInput) (*Image, error) {
path := fmt.Sprintf("/%s/images/%s", c.client.AccountName, input.ImageID)
query := &url.Values{}
query.Set("action", "update")
respReader, err := client.executeRequestURIParams(ctx, http.MethodPost, path, input, query)
reqInputs := client.RequestInput{
Method: http.MethodPost,
Path: path,
Query: query,
Body: input,
}
respReader, err := c.client.ExecuteRequestURIParams(ctx, reqInputs)
if respReader != nil {
defer respReader.Close()
}
if err != nil {
return nil, errwrap.Wrapf("Error executing UpdateImage request: {{err}}", err)
return nil, errwrap.Wrapf("Error executing Update request: {{err}}", err)
}
var result *Image
decoder := json.NewDecoder(respReader)
if err = decoder.Decode(&result); err != nil {
return nil, errwrap.Wrapf("Error decoding UpdateImage response: {{err}}", err)
return nil, errwrap.Wrapf("Error decoding Update response: {{err}}", err)
}
return result, nil

1020
vendor/github.com/joyent/triton-go/compute/instances.go generated vendored Normal file

File diff suppressed because it is too large Load Diff

View File

@ -1,4 +1,4 @@
package triton
package compute
import (
"context"
@ -7,16 +7,11 @@ import (
"net/http"
"github.com/hashicorp/errwrap"
"github.com/joyent/triton-go/client"
)
type PackagesClient struct {
*Client
}
// Packages returns a c used for accessing functions pertaining
// to Packages functionality in the Triton API.
func (c *Client) Packages() *PackagesClient {
return &PackagesClient{c}
client *client.Client
}
type Package struct {
@ -44,20 +39,25 @@ type ListPackagesInput struct {
Group string `json:"group"`
}
func (client *PackagesClient) ListPackages(ctx context.Context, input *ListPackagesInput) ([]*Package, error) {
path := fmt.Sprintf("/%s/packages", client.accountName)
respReader, err := client.executeRequest(ctx, http.MethodGet, path, input)
func (c *PackagesClient) List(ctx context.Context, input *ListPackagesInput) ([]*Package, error) {
path := fmt.Sprintf("/%s/packages", c.client.AccountName)
reqInputs := client.RequestInput{
Method: http.MethodGet,
Path: path,
Body: input,
}
respReader, err := c.client.ExecuteRequest(ctx, reqInputs)
if respReader != nil {
defer respReader.Close()
}
if err != nil {
return nil, errwrap.Wrapf("Error executing ListPackages request: {{err}}", err)
return nil, errwrap.Wrapf("Error executing List request: {{err}}", err)
}
var result []*Package
decoder := json.NewDecoder(respReader)
if err = decoder.Decode(&result); err != nil {
return nil, errwrap.Wrapf("Error decoding ListPackages response: {{err}}", err)
return nil, errwrap.Wrapf("Error decoding List response: {{err}}", err)
}
return result, nil
@ -67,20 +67,24 @@ type GetPackageInput struct {
ID string
}
func (client *PackagesClient) GetPackage(ctx context.Context, input *GetPackageInput) (*Package, error) {
path := fmt.Sprintf("/%s/packages/%s", client.accountName, input.ID)
respReader, err := client.executeRequest(ctx, http.MethodGet, path, nil)
func (c *PackagesClient) Get(ctx context.Context, input *GetPackageInput) (*Package, error) {
path := fmt.Sprintf("/%s/packages/%s", 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 GetPackage request: {{err}}", err)
return nil, errwrap.Wrapf("Error executing Get request: {{err}}", err)
}
var result *Package
decoder := json.NewDecoder(respReader)
if err = decoder.Decode(&result); err != nil {
return nil, errwrap.Wrapf("Error decoding GetPackage response: {{err}}", err)
return nil, errwrap.Wrapf("Error decoding Get response: {{err}}", err)
}
return result, nil

View File

@ -1,4 +1,4 @@
package triton
package compute
import (
"context"
@ -8,16 +8,11 @@ import (
"sort"
"github.com/hashicorp/errwrap"
"github.com/joyent/triton-go/client"
)
type ServicesClient struct {
*Client
}
// Services returns a c used for accessing functions pertaining
// to Services functionality in the Triton API.
func (c *Client) Services() *ServicesClient {
return &ServicesClient{c}
client *client.Client
}
type Service struct {
@ -27,20 +22,24 @@ type Service struct {
type ListServicesInput struct{}
func (client *ServicesClient) ListServices(ctx context.Context, _ *ListServicesInput) ([]*Service, error) {
path := fmt.Sprintf("/%s/services", client.accountName)
respReader, err := client.executeRequest(ctx, http.MethodGet, path, nil)
func (c *ServicesClient) List(ctx context.Context, _ *ListServicesInput) ([]*Service, error) {
path := fmt.Sprintf("/%s/services", c.client.AccountName)
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 ListServices request: {{err}}", err)
return nil, errwrap.Wrapf("Error executing List request: {{err}}", err)
}
var intermediate map[string]string
decoder := json.NewDecoder(respReader)
if err = decoder.Decode(&intermediate); err != nil {
return nil, errwrap.Wrapf("Error decoding ListServices response: {{err}}", err)
return nil, errwrap.Wrapf("Error decoding List response: {{err}}", err)
}
keys := make([]string, len(intermediate))

View File

@ -1,73 +0,0 @@
package triton
import (
"context"
"encoding/json"
"fmt"
"net/http"
"github.com/hashicorp/errwrap"
)
type ConfigClient struct {
*Client
}
// Config returns a c used for accessing functions pertaining
// to Config functionality in the Triton API.
func (c *Client) Config() *ConfigClient {
return &ConfigClient{c}
}
// Config represents configuration for your account.
type Config struct {
// DefaultNetwork is the network that docker containers are provisioned on.
DefaultNetwork string `json:"default_network"`
}
type GetConfigInput struct{}
// GetConfig outputs configuration for your account.
func (client *ConfigClient) GetConfig(ctx context.Context, input *GetConfigInput) (*Config, error) {
path := fmt.Sprintf("/%s/config", client.accountName)
respReader, err := client.executeRequest(ctx, http.MethodGet, path, nil)
if respReader != nil {
defer respReader.Close()
}
if err != nil {
return nil, errwrap.Wrapf("Error executing GetConfig request: {{err}}", err)
}
var result *Config
decoder := json.NewDecoder(respReader)
if err = decoder.Decode(&result); err != nil {
return nil, errwrap.Wrapf("Error decoding GetConfig response: {{err}}", err)
}
return result, nil
}
type UpdateConfigInput struct {
// DefaultNetwork is the network that docker containers are provisioned on.
DefaultNetwork string `json:"default_network"`
}
// UpdateConfig updates configuration values for your account.
func (client *ConfigClient) UpdateConfig(ctx context.Context, input *UpdateConfigInput) (*Config, error) {
path := fmt.Sprintf("/%s/config", client.accountName)
respReader, err := client.executeRequest(ctx, http.MethodPut, path, input)
if respReader != nil {
defer respReader.Close()
}
if err != nil {
return nil, errwrap.Wrapf("Error executing UpdateConfig request: {{err}}", err)
}
var result *Config
decoder := json.NewDecoder(respReader)
if err = decoder.Decode(&result); err != nil {
return nil, errwrap.Wrapf("Error decoding UpdateConfig response: {{err}}", err)
}
return result, nil
}

View File

@ -1,93 +0,0 @@
package triton
import (
"encoding/json"
"errors"
"fmt"
"net/http"
"sort"
"context"
"github.com/hashicorp/errwrap"
)
type DataCentersClient struct {
*Client
}
// DataCenters returns a c used for accessing functions pertaining
// to Datacenter functionality in the Triton API.
func (c *Client) Datacenters() *DataCentersClient {
return &DataCentersClient{c}
}
type DataCenter struct {
Name string `json:"name"`
URL string `json:"url"`
}
type ListDataCentersInput struct{}
func (client *DataCentersClient) ListDataCenters(ctx context.Context, _ *ListDataCentersInput) ([]*DataCenter, error) {
path := fmt.Sprintf("/%s/datacenters", client.accountName)
respReader, err := client.executeRequest(ctx, http.MethodGet, path, nil)
if respReader != nil {
defer respReader.Close()
}
if err != nil {
return nil, errwrap.Wrapf("Error executing ListDatacenters request: {{err}}", err)
}
var intermediate map[string]string
decoder := json.NewDecoder(respReader)
if err = decoder.Decode(&intermediate); err != nil {
return nil, errwrap.Wrapf("Error decoding ListDatacenters response: {{err}}", err)
}
keys := make([]string, len(intermediate))
i := 0
for k := range intermediate {
keys[i] = k
i++
}
sort.Strings(keys)
result := make([]*DataCenter, len(intermediate))
i = 0
for _, key := range keys {
result[i] = &DataCenter{
Name: key,
URL: intermediate[key],
}
i++
}
return result, nil
}
type GetDataCenterInput struct {
Name string
}
func (client *DataCentersClient) GetDataCenter(ctx context.Context, input *GetDataCenterInput) (*DataCenter, error) {
path := fmt.Sprintf("/%s/datacenters/%s", client.accountName, input.Name)
resp, err := client.executeRequestRaw(ctx, http.MethodGet, path, nil)
if err != nil {
return nil, errwrap.Wrapf("Error executing GetDatacenter request: {{err}}", err)
}
if resp.StatusCode != http.StatusFound {
return nil, fmt.Errorf("Error executing GetDatacenter request: expected status code 302, got %s",
resp.StatusCode)
}
location := resp.Header.Get("Location")
if location == "" {
return nil, errors.New("Error decoding GetDatacenter response: no Location header")
}
return &DataCenter{
Name: input.Name,
URL: location,
}, nil
}

View File

@ -1,234 +0,0 @@
package triton
import (
"encoding/json"
"fmt"
"net/http"
"context"
"github.com/hashicorp/errwrap"
)
type FabricsClient struct {
*Client
}
// Fabrics returns a client used for accessing functions pertaining to
// Fabric functionality in the Triton API.
func (c *Client) Fabrics() *FabricsClient {
return &FabricsClient{c}
}
type FabricVLAN struct {
Name string `json:"name"`
ID int `json:"vlan_id"`
Description string `json:"description"`
}
type ListFabricVLANsInput struct{}
func (client *FabricsClient) ListFabricVLANs(ctx context.Context, _ *ListFabricVLANsInput) ([]*FabricVLAN, error) {
path := fmt.Sprintf("/%s/fabrics/default/vlans", client.accountName)
respReader, err := client.executeRequest(ctx, http.MethodGet, path, nil)
if respReader != nil {
defer respReader.Close()
}
if err != nil {
return nil, errwrap.Wrapf("Error executing ListFabricVLANs request: {{err}}", err)
}
var result []*FabricVLAN
decoder := json.NewDecoder(respReader)
if err = decoder.Decode(&result); err != nil {
return nil, errwrap.Wrapf("Error decoding ListFabricVLANs response: {{err}}", err)
}
return result, nil
}
type CreateFabricVLANInput struct {
Name string `json:"name"`
ID int `json:"vlan_id"`
Description string `json:"description"`
}
func (client *FabricsClient) CreateFabricVLAN(ctx context.Context, input *CreateFabricVLANInput) (*FabricVLAN, error) {
path := fmt.Sprintf("/%s/fabrics/default/vlans", client.accountName)
respReader, err := client.executeRequest(ctx, http.MethodPost, path, input)
if respReader != nil {
defer respReader.Close()
}
if err != nil {
return nil, errwrap.Wrapf("Error executing CreateFabricVLAN request: {{err}}", err)
}
var result *FabricVLAN
decoder := json.NewDecoder(respReader)
if err = decoder.Decode(&result); err != nil {
return nil, errwrap.Wrapf("Error decoding CreateFabricVLAN response: {{err}}", err)
}
return result, nil
}
type UpdateFabricVLANInput struct {
ID int `json:"-"`
Name string `json:"name"`
Description string `json:"description"`
}
func (client *FabricsClient) UpdateFabricVLAN(ctx context.Context, input *UpdateFabricVLANInput) (*FabricVLAN, error) {
path := fmt.Sprintf("/%s/fabrics/default/vlans/%d", client.accountName, input.ID)
respReader, err := client.executeRequest(ctx, http.MethodPut, path, input)
if respReader != nil {
defer respReader.Close()
}
if err != nil {
return nil, errwrap.Wrapf("Error executing UpdateFabricVLAN request: {{err}}", err)
}
var result *FabricVLAN
decoder := json.NewDecoder(respReader)
if err = decoder.Decode(&result); err != nil {
return nil, errwrap.Wrapf("Error decoding UpdateFabricVLAN response: {{err}}", err)
}
return result, nil
}
type GetFabricVLANInput struct {
ID int `json:"-"`
}
func (client *FabricsClient) GetFabricVLAN(ctx context.Context, input *GetFabricVLANInput) (*FabricVLAN, error) {
path := fmt.Sprintf("/%s/fabrics/default/vlans/%d", client.accountName, input.ID)
respReader, err := client.executeRequest(ctx, http.MethodGet, path, nil)
if respReader != nil {
defer respReader.Close()
}
if err != nil {
return nil, errwrap.Wrapf("Error executing GetFabricVLAN request: {{err}}", err)
}
var result *FabricVLAN
decoder := json.NewDecoder(respReader)
if err = decoder.Decode(&result); err != nil {
return nil, errwrap.Wrapf("Error decoding GetFabricVLAN response: {{err}}", err)
}
return result, nil
}
type DeleteFabricVLANInput struct {
ID int `json:"-"`
}
func (client *FabricsClient) DeleteFabricVLAN(ctx context.Context, input *DeleteFabricVLANInput) error {
path := fmt.Sprintf("/%s/fabrics/default/vlans/%d", client.accountName, input.ID)
respReader, err := client.executeRequest(ctx, http.MethodDelete, path, nil)
if respReader != nil {
defer respReader.Close()
}
if err != nil {
return errwrap.Wrapf("Error executing DeleteFabricVLAN request: {{err}}", err)
}
return nil
}
type ListFabricNetworksInput struct {
FabricVLANID int `json:"-"`
}
func (client *FabricsClient) ListFabricNetworks(ctx context.Context, input *ListFabricNetworksInput) ([]*Network, error) {
path := fmt.Sprintf("/%s/fabrics/default/vlans/%d/networks", client.accountName, input.FabricVLANID)
respReader, err := client.executeRequest(ctx, http.MethodGet, path, nil)
if respReader != nil {
defer respReader.Close()
}
if err != nil {
return nil, errwrap.Wrapf("Error executing ListFabricNetworks request: {{err}}", err)
}
var result []*Network
decoder := json.NewDecoder(respReader)
if err = decoder.Decode(&result); err != nil {
return nil, errwrap.Wrapf("Error decoding ListFabricNetworks response: {{err}}", err)
}
return result, nil
}
type CreateFabricNetworkInput struct {
FabricVLANID int `json:"-"`
Name string `json:"name"`
Description string `json:"description"`
Subnet string `json:"subnet"`
ProvisionStartIP string `json:"provision_start_ip"`
ProvisionEndIP string `json:"provision_end_ip"`
Gateway string `json:"gateway"`
Resolvers []string `json:"resolvers"`
Routes map[string]string `json:"routes"`
InternetNAT bool `json:"internet_nat"`
}
func (client *FabricsClient) CreateFabricNetwork(ctx context.Context, input *CreateFabricNetworkInput) (*Network, error) {
path := fmt.Sprintf("/%s/fabrics/default/vlans/%d/networks", client.accountName, input.FabricVLANID)
respReader, err := client.executeRequest(ctx, http.MethodPost, path, input)
if respReader != nil {
defer respReader.Close()
}
if err != nil {
return nil, errwrap.Wrapf("Error executing CreateFabricNetwork request: {{err}}", err)
}
var result *Network
decoder := json.NewDecoder(respReader)
if err = decoder.Decode(&result); err != nil {
return nil, errwrap.Wrapf("Error decoding CreateFabricNetwork response: {{err}}", err)
}
return result, nil
}
type GetFabricNetworkInput struct {
FabricVLANID int `json:"-"`
NetworkID string `json:"-"`
}
func (client *FabricsClient) GetFabricNetwork(ctx context.Context, input *GetFabricNetworkInput) (*Network, error) {
path := fmt.Sprintf("/%s/fabrics/default/vlans/%d/networks/%s", client.accountName, input.FabricVLANID, input.NetworkID)
respReader, err := client.executeRequest(ctx, http.MethodGet, path, nil)
if respReader != nil {
defer respReader.Close()
}
if err != nil {
return nil, errwrap.Wrapf("Error executing GetFabricNetwork request: {{err}}", err)
}
var result *Network
decoder := json.NewDecoder(respReader)
if err = decoder.Decode(&result); err != nil {
return nil, errwrap.Wrapf("Error decoding GetFabricNetwork response: {{err}}", err)
}
return result, nil
}
type DeleteFabricNetworkInput struct {
FabricVLANID int `json:"-"`
NetworkID string `json:"-"`
}
func (client *FabricsClient) DeleteFabricNetwork(ctx context.Context, input *DeleteFabricNetworkInput) error {
path := fmt.Sprintf("/%s/fabrics/default/vlans/%d/networks/%s", client.accountName, input.FabricVLANID, input.NetworkID)
respReader, err := client.executeRequest(ctx, http.MethodDelete, path, nil)
if respReader != nil {
defer respReader.Close()
}
if err != nil {
return errwrap.Wrapf("Error executing DeleteFabricNetwork request: {{err}}", err)
}
return nil
}

View File

@ -1,219 +0,0 @@
package triton
import (
"context"
"encoding/json"
"fmt"
"net/http"
"github.com/hashicorp/errwrap"
)
type FirewallClient struct {
*Client
}
// Firewall returns a client used for accessing functions pertaining to
// firewall functionality in the Triton API.
func (c *Client) Firewall() *FirewallClient {
return &FirewallClient{c}
}
// FirewallRule represents a firewall rule
type FirewallRule struct {
// ID is a unique identifier for this rule
ID string `json:"id"`
// Enabled indicates if the rule is enabled
Enabled bool `json:"enabled"`
// Rule is the firewall rule text
Rule string `json:"rule"`
// Global indicates if the rule is global. Optional.
Global bool `json:"global"`
// Description is a human-readable description for the rule. Optional
Description string `json:"description"`
}
type ListFirewallRulesInput struct{}
func (client *FirewallClient) ListFirewallRules(ctx context.Context, _ *ListFirewallRulesInput) ([]*FirewallRule, error) {
path := fmt.Sprintf("/%s/fwrules", client.accountName)
respReader, err := client.executeRequest(ctx, http.MethodGet, path, nil)
if respReader != nil {
defer respReader.Close()
}
if err != nil {
return nil, errwrap.Wrapf("Error executing ListFirewallRules request: {{err}}", err)
}
var result []*FirewallRule
decoder := json.NewDecoder(respReader)
if err = decoder.Decode(&result); err != nil {
return nil, errwrap.Wrapf("Error decoding ListFirewallRules response: {{err}}", err)
}
return result, nil
}
type GetFirewallRuleInput struct {
ID string
}
func (client *FirewallClient) GetFirewallRule(ctx context.Context, input *GetFirewallRuleInput) (*FirewallRule, error) {
path := fmt.Sprintf("/%s/fwrules/%s", client.accountName, input.ID)
respReader, err := client.executeRequest(ctx, http.MethodGet, path, nil)
if respReader != nil {
defer respReader.Close()
}
if err != nil {
return nil, errwrap.Wrapf("Error executing GetFirewallRule request: {{err}}", err)
}
var result *FirewallRule
decoder := json.NewDecoder(respReader)
if err = decoder.Decode(&result); err != nil {
return nil, errwrap.Wrapf("Error decoding GetFirewallRule response: {{err}}", err)
}
return result, nil
}
type CreateFirewallRuleInput struct {
Enabled bool `json:"enabled"`
Rule string `json:"rule"`
Description string `json:"description"`
}
func (client *FirewallClient) CreateFirewallRule(ctx context.Context, input *CreateFirewallRuleInput) (*FirewallRule, error) {
path := fmt.Sprintf("/%s/fwrules", client.accountName)
respReader, err := client.executeRequest(ctx, http.MethodPost, path, input)
if respReader != nil {
defer respReader.Close()
}
if err != nil {
return nil, errwrap.Wrapf("Error executing CreateFirewallRule request: {{err}}", err)
}
var result *FirewallRule
decoder := json.NewDecoder(respReader)
if err = decoder.Decode(&result); err != nil {
return nil, errwrap.Wrapf("Error decoding CreateFirewallRule response: {{err}}", err)
}
return result, nil
}
type UpdateFirewallRuleInput struct {
ID string `json:"-"`
Enabled bool `json:"enabled"`
Rule string `json:"rule"`
Description string `json:"description"`
}
func (client *FirewallClient) UpdateFirewallRule(ctx context.Context, input *UpdateFirewallRuleInput) (*FirewallRule, error) {
path := fmt.Sprintf("/%s/fwrules/%s", client.accountName, input.ID)
respReader, err := client.executeRequest(ctx, http.MethodPost, path, input)
if respReader != nil {
defer respReader.Close()
}
if err != nil {
return nil, errwrap.Wrapf("Error executing UpdateFirewallRule request: {{err}}", err)
}
var result *FirewallRule
decoder := json.NewDecoder(respReader)
if err = decoder.Decode(&result); err != nil {
return nil, errwrap.Wrapf("Error decoding UpdateFirewallRule response: {{err}}", err)
}
return result, nil
}
type EnableFirewallRuleInput struct {
ID string `json:"-"`
}
func (client *FirewallClient) EnableFirewallRule(ctx context.Context, input *EnableFirewallRuleInput) (*FirewallRule, error) {
path := fmt.Sprintf("/%s/fwrules/%s/enable", client.accountName, input.ID)
respReader, err := client.executeRequest(ctx, http.MethodPost, path, input)
if respReader != nil {
defer respReader.Close()
}
if err != nil {
return nil, errwrap.Wrapf("Error executing EnableFirewallRule request: {{err}}", err)
}
var result *FirewallRule
decoder := json.NewDecoder(respReader)
if err = decoder.Decode(&result); err != nil {
return nil, errwrap.Wrapf("Error decoding EnableFirewallRule response: {{err}}", err)
}
return result, nil
}
type DisableFirewallRuleInput struct {
ID string `json:"-"`
}
func (client *FirewallClient) DisableFirewallRule(ctx context.Context, input *DisableFirewallRuleInput) (*FirewallRule, error) {
path := fmt.Sprintf("/%s/fwrules/%s/disable", client.accountName, input.ID)
respReader, err := client.executeRequest(ctx, http.MethodPost, path, input)
if respReader != nil {
defer respReader.Close()
}
if err != nil {
return nil, errwrap.Wrapf("Error executing DisableFirewallRule request: {{err}}", err)
}
var result *FirewallRule
decoder := json.NewDecoder(respReader)
if err = decoder.Decode(&result); err != nil {
return nil, errwrap.Wrapf("Error decoding DisableFirewallRule response: {{err}}", err)
}
return result, nil
}
type DeleteFirewallRuleInput struct {
ID string
}
func (client *FirewallClient) DeleteFirewallRule(ctx context.Context, input *DeleteFirewallRuleInput) error {
path := fmt.Sprintf("/%s/fwrules/%s", client.accountName, input.ID)
respReader, err := client.executeRequest(ctx, http.MethodDelete, path, nil)
if respReader != nil {
defer respReader.Close()
}
if err != nil {
return errwrap.Wrapf("Error executing DeleteFirewallRule request: {{err}}", err)
}
return nil
}
type ListMachineFirewallRulesInput struct {
MachineID string
}
func (client *FirewallClient) ListMachineFirewallRules(ctx context.Context, input *ListMachineFirewallRulesInput) ([]*FirewallRule, error) {
path := fmt.Sprintf("/%s/machines/%s/firewallrules", client.accountName, input.MachineID)
respReader, err := client.executeRequest(ctx, http.MethodGet, path, nil)
if respReader != nil {
defer respReader.Close()
}
if err != nil {
return nil, errwrap.Wrapf("Error executing ListMachineFirewallRules request: {{err}}", err)
}
var result []*FirewallRule
decoder := json.NewDecoder(respReader)
if err = decoder.Decode(&result); err != nil {
return nil, errwrap.Wrapf("Error decoding ListFirewallRules response: {{err}}", err)
}
return result, nil
}

View File

@ -1,125 +0,0 @@
package triton
import (
"context"
"encoding/json"
"fmt"
"net/http"
"github.com/hashicorp/errwrap"
)
type KeysClient struct {
*Client
}
// Keys returns a c used for accessing functions pertaining to
// SSH key functionality in the Triton API.
func (c *Client) Keys() *KeysClient {
return &KeysClient{c}
}
// Key represents a public key
type Key struct {
// Name of the key
Name string `json:"name"`
// Key fingerprint
Fingerprint string `json:"fingerprint"`
// OpenSSH-formatted public key
Key string `json:"key"`
}
type ListKeysInput struct{}
// ListKeys lists all public keys we have on record for the specified
// account.
func (client *KeysClient) ListKeys(ctx context.Context, _ *ListKeysInput) ([]*Key, error) {
path := fmt.Sprintf("/%s/keys", client.accountName)
respReader, err := client.executeRequest(ctx, http.MethodGet, path, nil)
if respReader != nil {
defer respReader.Close()
}
if err != nil {
return nil, errwrap.Wrapf("Error executing ListKeys request: {{err}}", err)
}
var result []*Key
decoder := json.NewDecoder(respReader)
if err = decoder.Decode(&result); err != nil {
return nil, errwrap.Wrapf("Error decoding ListKeys response: {{err}}", err)
}
return result, nil
}
type GetKeyInput struct {
KeyName string
}
func (client *KeysClient) GetKey(ctx context.Context, input *GetKeyInput) (*Key, error) {
path := fmt.Sprintf("/%s/keys/%s", client.accountName, input.KeyName)
respReader, err := client.executeRequest(ctx, http.MethodGet, path, nil)
if respReader != nil {
defer respReader.Close()
}
if err != nil {
return nil, errwrap.Wrapf("Error executing GetKey request: {{err}}", err)
}
var result *Key
decoder := json.NewDecoder(respReader)
if err = decoder.Decode(&result); err != nil {
return nil, errwrap.Wrapf("Error decoding GetKey response: {{err}}", err)
}
return result, nil
}
type DeleteKeyInput struct {
KeyName string
}
func (client *KeysClient) DeleteKey(ctx context.Context, input *DeleteKeyInput) error {
path := fmt.Sprintf("/%s/keys/%s", client.accountName, input.KeyName)
respReader, err := client.executeRequest(ctx, http.MethodDelete, path, nil)
if respReader != nil {
defer respReader.Close()
}
if err != nil {
return errwrap.Wrapf("Error executing DeleteKey request: {{err}}", err)
}
return nil
}
// CreateKeyInput represents the option that can be specified
// when creating a new key.
type CreateKeyInput struct {
// Name of the key. Optional.
Name string `json:"name,omitempty"`
// OpenSSH-formatted public key.
Key string `json:"key"`
}
// CreateKey uploads a new OpenSSH key to Triton for use in HTTP signing and SSH.
func (client *KeysClient) CreateKey(ctx context.Context, input *CreateKeyInput) (*Key, error) {
path := fmt.Sprintf("/%s/keys", client.accountName)
respReader, err := client.executeRequest(ctx, http.MethodPost, path, input)
if respReader != nil {
defer respReader.Close()
}
if err != nil {
return nil, errwrap.Wrapf("Error executing CreateKey request: {{err}}", err)
}
var result *Key
decoder := json.NewDecoder(respReader)
if err = decoder.Decode(&result); err != nil {
return nil, errwrap.Wrapf("Error decoding CreateKey response: {{err}}", err)
}
return result, nil
}

View File

@ -1,667 +0,0 @@
package triton
import (
"context"
"encoding/json"
"fmt"
"net/http"
"net/url"
"strings"
"time"
"github.com/hashicorp/errwrap"
)
type MachinesClient struct {
*Client
}
// Machines returns a client used for accessing functions pertaining to
// machine functionality in the Triton API.
func (c *Client) Machines() *MachinesClient {
return &MachinesClient{c}
}
const (
machineCNSTagDisable = "triton.cns.disable"
machineCNSTagReversePTR = "triton.cns.reverse_ptr"
machineCNSTagServices = "triton.cns.services"
)
// MachineCNS is a container for the CNS-specific attributes. In the API these
// values are embedded within a Machine's Tags attribute, however they are
// exposed to the caller as their native types.
type MachineCNS struct {
Disable *bool
ReversePTR *string
Services []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]string `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"`
DomainNames []string `json:"dns_names"`
CNS MachineCNS
}
// _Machine is a private facade over Machine that handles the necessary API
// overrides from VMAPI's machine endpoint(s).
type _Machine struct {
Machine
Tags map[string]interface{} `json:"tags"`
}
type NIC struct {
IP string `json:"ip"`
MAC string `json:"mac"`
Primary bool `json:"primary"`
Netmask string `json:"netmask"`
Gateway string `json:"gateway"`
State string `json:"state"`
Network string `json:"network"`
}
type GetMachineInput struct {
ID string
}
func (gmi *GetMachineInput) Validate() error {
if gmi.ID == "" {
return fmt.Errorf("machine ID can not be empty")
}
return nil
}
func (client *MachinesClient) GetMachine(ctx context.Context, input *GetMachineInput) (*Machine, error) {
if err := input.Validate(); err != nil {
return nil, errwrap.Wrapf("unable to get machine: {{err}}", err)
}
path := fmt.Sprintf("/%s/machines/%s", client.accountName, input.ID)
response, err := client.executeRequestRaw(ctx, http.MethodGet, path, nil)
if response != nil {
defer response.Body.Close()
}
if response.StatusCode == http.StatusNotFound || response.StatusCode == http.StatusGone {
return nil, &TritonError{
StatusCode: response.StatusCode,
Code: "ResourceNotFound",
}
}
if err != nil {
return nil, errwrap.Wrapf("Error executing GetMachine request: {{err}}",
client.decodeError(response.StatusCode, response.Body))
}
var result *_Machine
decoder := json.NewDecoder(response.Body)
if err = decoder.Decode(&result); err != nil {
return nil, errwrap.Wrapf("Error decoding GetMachine response: {{err}}", err)
}
native, err := result.toNative()
if err != nil {
return nil, errwrap.Wrapf("unable to convert API response for machines to native type: {{err}}", err)
}
return native, nil
}
type ListMachinesInput struct{}
func (client *MachinesClient) ListMachines(ctx context.Context, _ *ListMachinesInput) ([]*Machine, error) {
path := fmt.Sprintf("/%s/machines", client.accountName)
response, err := client.executeRequestRaw(ctx, http.MethodGet, path, nil)
if response != nil {
defer response.Body.Close()
}
if response.StatusCode == http.StatusNotFound {
return nil, &TritonError{
StatusCode: response.StatusCode,
Code: "ResourceNotFound",
}
}
if err != nil {
return nil, errwrap.Wrapf("Error executing ListMachines request: {{err}}",
client.decodeError(response.StatusCode, response.Body))
}
var results []*_Machine
decoder := json.NewDecoder(response.Body)
if err = decoder.Decode(&results); err != nil {
return nil, errwrap.Wrapf("Error decoding ListMachines response: {{err}}", err)
}
machines := make([]*Machine, 0, len(results))
for _, machineAPI := range results {
native, err := machineAPI.toNative()
if err != nil {
return nil, errwrap.Wrapf("unable to convert API response for machines to native type: {{err}}", err)
}
machines = append(machines, native)
}
return machines, nil
}
type CreateMachineInput struct {
Name string
Package string
Image string
Networks []string
LocalityStrict bool
LocalityNear []string
LocalityFar []string
Metadata map[string]string
Tags map[string]string
FirewallEnabled bool
CNS MachineCNS
}
func (input *CreateMachineInput) toAPI() map[string]interface{} {
const numExtraParams = 8
result := make(map[string]interface{}, numExtraParams+len(input.Metadata)+len(input.Tags))
result["firewall_enabled"] = input.FirewallEnabled
if input.Name != "" {
result["name"] = input.Name
}
if input.Package != "" {
result["package"] = input.Package
}
if input.Image != "" {
result["image"] = input.Image
}
if len(input.Networks) > 0 {
result["networks"] = input.Networks
}
locality := struct {
Strict bool `json:"strict"`
Near []string `json:"near,omitempty"`
Far []string `json:"far,omitempty"`
}{
Strict: input.LocalityStrict,
Near: input.LocalityNear,
Far: input.LocalityFar,
}
result["locality"] = locality
for key, value := range input.Tags {
result[fmt.Sprintf("tag.%s", key)] = value
}
// Deliberately clobber any user-specified Tags with the attributes from the
// CNS struct.
input.CNS.toTags(result)
for key, value := range input.Metadata {
result[fmt.Sprintf("metadata.%s", key)] = value
}
return result
}
func (client *MachinesClient) CreateMachine(ctx context.Context, input *CreateMachineInput) (*Machine, error) {
path := fmt.Sprintf("/%s/machines", client.accountName)
respReader, err := client.executeRequest(ctx, http.MethodPost, path, input.toAPI())
if respReader != nil {
defer respReader.Close()
}
if err != nil {
return nil, errwrap.Wrapf("Error executing CreateMachine request: {{err}}", err)
}
var result *Machine
decoder := json.NewDecoder(respReader)
if err = decoder.Decode(&result); err != nil {
return nil, errwrap.Wrapf("Error decoding CreateMachine response: {{err}}", err)
}
return result, nil
}
type DeleteMachineInput struct {
ID string
}
func (client *MachinesClient) DeleteMachine(ctx context.Context, input *DeleteMachineInput) error {
path := fmt.Sprintf("/%s/machines/%s", client.accountName, input.ID)
response, err := client.executeRequestRaw(ctx, http.MethodDelete, path, nil)
if response.Body != nil {
defer response.Body.Close()
}
if response.StatusCode == http.StatusNotFound || response.StatusCode == http.StatusGone {
return nil
}
if err != nil {
return errwrap.Wrapf("Error executing DeleteMachine request: {{err}}",
client.decodeError(response.StatusCode, response.Body))
}
return nil
}
type DeleteMachineTagsInput struct {
ID string
}
func (client *MachinesClient) DeleteMachineTags(ctx context.Context, input *DeleteMachineTagsInput) error {
path := fmt.Sprintf("/%s/machines/%s/tags", client.accountName, input.ID)
response, err := client.executeRequestRaw(ctx, http.MethodDelete, path, nil)
if response.Body != nil {
defer response.Body.Close()
}
if response.StatusCode == http.StatusNotFound {
return nil
}
if err != nil {
return errwrap.Wrapf("Error executing DeleteMachineTags request: {{err}}",
client.decodeError(response.StatusCode, response.Body))
}
return nil
}
type DeleteMachineTagInput struct {
ID string
Key string
}
func (client *MachinesClient) DeleteMachineTag(ctx context.Context, input *DeleteMachineTagInput) error {
path := fmt.Sprintf("/%s/machines/%s/tags/%s", client.accountName, input.ID, input.Key)
response, err := client.executeRequestRaw(ctx, http.MethodDelete, path, nil)
if response.Body != nil {
defer response.Body.Close()
}
if response.StatusCode == http.StatusNotFound {
return nil
}
if err != nil {
return errwrap.Wrapf("Error executing DeleteMachineTag request: {{err}}",
client.decodeError(response.StatusCode, response.Body))
}
return nil
}
type RenameMachineInput struct {
ID string
Name string
}
func (client *MachinesClient) RenameMachine(ctx context.Context, input *RenameMachineInput) error {
path := fmt.Sprintf("/%s/machines/%s", client.accountName, input.ID)
params := &url.Values{}
params.Set("action", "rename")
params.Set("name", input.Name)
respReader, err := client.executeRequestURIParams(ctx, http.MethodPost, path, nil, params)
if respReader != nil {
defer respReader.Close()
}
if err != nil {
return errwrap.Wrapf("Error executing RenameMachine request: {{err}}", err)
}
return nil
}
type ReplaceMachineTagsInput struct {
ID string
Tags map[string]string
}
func (client *MachinesClient) ReplaceMachineTags(ctx context.Context, input *ReplaceMachineTagsInput) error {
path := fmt.Sprintf("/%s/machines/%s/tags", client.accountName, input.ID)
respReader, err := client.executeRequest(ctx, http.MethodPut, path, input.Tags)
if respReader != nil {
defer respReader.Close()
}
if err != nil {
return errwrap.Wrapf("Error executing ReplaceMachineTags request: {{err}}", err)
}
return nil
}
type AddMachineTagsInput struct {
ID string
Tags map[string]string
}
func (client *MachinesClient) AddMachineTags(ctx context.Context, input *AddMachineTagsInput) error {
path := fmt.Sprintf("/%s/machines/%s/tags", client.accountName, input.ID)
respReader, err := client.executeRequest(ctx, http.MethodPost, path, input.Tags)
if respReader != nil {
defer respReader.Close()
}
if err != nil {
return errwrap.Wrapf("Error executing AddMachineTags request: {{err}}", err)
}
return nil
}
type GetMachineTagInput struct {
ID string
Key string
}
func (client *MachinesClient) GetMachineTag(ctx context.Context, input *GetMachineTagInput) (string, error) {
path := fmt.Sprintf("/%s/machines/%s/tags/%s", client.accountName, input.ID, input.Key)
respReader, err := client.executeRequest(ctx, http.MethodGet, path, nil)
if respReader != nil {
defer respReader.Close()
}
if err != nil {
return "", errwrap.Wrapf("Error executing GetMachineTag request: {{err}}", err)
}
var result string
decoder := json.NewDecoder(respReader)
if err = decoder.Decode(&result); err != nil {
return "", errwrap.Wrapf("Error decoding GetMachineTag response: {{err}}", err)
}
return result, nil
}
type ListMachineTagsInput struct {
ID string
}
func (client *MachinesClient) ListMachineTags(ctx context.Context, input *ListMachineTagsInput) (map[string]string, error) {
path := fmt.Sprintf("/%s/machines/%s/tags", client.accountName, input.ID)
respReader, err := client.executeRequest(ctx, http.MethodGet, path, nil)
if respReader != nil {
defer respReader.Close()
}
if err != nil {
return nil, errwrap.Wrapf("Error executing ListMachineTags request: {{err}}", err)
}
var result map[string]interface{}
decoder := json.NewDecoder(respReader)
if err = decoder.Decode(&result); err != nil {
return nil, errwrap.Wrapf("Error decoding ListMachineTags response: {{err}}", err)
}
_, tags := machineTagsExtractMeta(result)
return tags, nil
}
type UpdateMachineMetadataInput struct {
ID string
Metadata map[string]string
}
func (client *MachinesClient) UpdateMachineMetadata(ctx context.Context, input *UpdateMachineMetadataInput) (map[string]string, error) {
path := fmt.Sprintf("/%s/machines/%s/tags", client.accountName, input.ID)
respReader, err := client.executeRequest(ctx, http.MethodPost, path, input.Metadata)
if respReader != nil {
defer respReader.Close()
}
if err != nil {
return nil, errwrap.Wrapf("Error executing UpdateMachineMetadata request: {{err}}", err)
}
var result map[string]string
decoder := json.NewDecoder(respReader)
if err = decoder.Decode(&result); err != nil {
return nil, errwrap.Wrapf("Error decoding UpdateMachineMetadata response: {{err}}", err)
}
return result, nil
}
type ResizeMachineInput struct {
ID string
Package string
}
func (client *MachinesClient) ResizeMachine(ctx context.Context, input *ResizeMachineInput) error {
path := fmt.Sprintf("/%s/machines/%s", client.accountName, input.ID)
params := &url.Values{}
params.Set("action", "resize")
params.Set("package", input.Package)
respReader, err := client.executeRequestURIParams(ctx, http.MethodPost, path, nil, params)
if respReader != nil {
defer respReader.Close()
}
if err != nil {
return errwrap.Wrapf("Error executing ResizeMachine request: {{err}}", err)
}
return nil
}
type EnableMachineFirewallInput struct {
ID string
}
func (client *MachinesClient) EnableMachineFirewall(ctx context.Context, input *EnableMachineFirewallInput) error {
path := fmt.Sprintf("/%s/machines/%s", client.accountName, input.ID)
params := &url.Values{}
params.Set("action", "enable_firewall")
respReader, err := client.executeRequestURIParams(ctx, http.MethodPost, path, nil, params)
if respReader != nil {
defer respReader.Close()
}
if err != nil {
return errwrap.Wrapf("Error executing EnableMachineFirewall request: {{err}}", err)
}
return nil
}
type DisableMachineFirewallInput struct {
ID string
}
func (client *MachinesClient) DisableMachineFirewall(ctx context.Context, input *DisableMachineFirewallInput) error {
path := fmt.Sprintf("/%s/machines/%s", client.accountName, input.ID)
params := &url.Values{}
params.Set("action", "disable_firewall")
respReader, err := client.executeRequestURIParams(ctx, http.MethodPost, path, nil, params)
if respReader != nil {
defer respReader.Close()
}
if err != nil {
return errwrap.Wrapf("Error executing DisableMachineFirewall request: {{err}}", err)
}
return nil
}
type ListNICsInput struct {
MachineID string
}
func (client *MachinesClient) ListNICs(ctx context.Context, input *ListNICsInput) ([]*NIC, error) {
path := fmt.Sprintf("/%s/machines/%s/nics", client.accountName, input.MachineID)
respReader, err := client.executeRequest(ctx, http.MethodGet, path, nil)
if respReader != nil {
defer respReader.Close()
}
if err != nil {
return nil, errwrap.Wrapf("Error executing ListNICs request: {{err}}", err)
}
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 result, nil
}
type AddNICInput struct {
MachineID string `json:"-"`
Network string `json:"network"`
}
func (client *MachinesClient) AddNIC(ctx context.Context, input *AddNICInput) (*NIC, error) {
path := fmt.Sprintf("/%s/machines/%s/nics", client.accountName, input.MachineID)
respReader, err := client.executeRequest(ctx, http.MethodPost, path, input)
if respReader != nil {
defer respReader.Close()
}
if err != nil {
return nil, errwrap.Wrapf("Error executing AddNIC request: {{err}}", err)
}
var result *NIC
decoder := json.NewDecoder(respReader)
if err = decoder.Decode(&result); err != nil {
return nil, errwrap.Wrapf("Error decoding AddNIC response: {{err}}", err)
}
return result, nil
}
type RemoveNICInput struct {
MachineID string
MAC string
}
func (client *MachinesClient) RemoveNIC(ctx context.Context, input *RemoveNICInput) error {
path := fmt.Sprintf("/%s/machines/%s/nics/%s", client.accountName, input.MachineID, input.MAC)
respReader, err := client.executeRequest(ctx, http.MethodDelete, path, nil)
if respReader != nil {
defer respReader.Close()
}
if err != nil {
return errwrap.Wrapf("Error executing RemoveNIC request: {{err}}", err)
}
return nil
}
type StopMachineInput struct {
MachineID string
}
func (client *MachinesClient) StopMachine(ctx context.Context, input *StopMachineInput) error {
path := fmt.Sprintf("/%s/machines/%s", client.accountName, input.MachineID)
params := &url.Values{}
params.Set("action", "stop")
respReader, err := client.executeRequestURIParams(ctx, http.MethodPost, path, nil, params)
if respReader != nil {
defer respReader.Close()
}
if err != nil {
return errwrap.Wrapf("Error executing StopMachine request: {{err}}", err)
}
return nil
}
type StartMachineInput struct {
MachineID string
}
func (client *MachinesClient) StartMachine(ctx context.Context, input *StartMachineInput) error {
path := fmt.Sprintf("/%s/machines/%s", client.accountName, input.MachineID)
params := &url.Values{}
params.Set("action", "start")
respReader, err := client.executeRequestURIParams(ctx, http.MethodPost, path, nil, params)
if respReader != nil {
defer respReader.Close()
}
if err != nil {
return errwrap.Wrapf("Error executing StartMachine request: {{err}}", err)
}
return nil
}
var reservedMachineCNSTags = map[string]struct{}{
machineCNSTagDisable: {},
machineCNSTagReversePTR: {},
machineCNSTagServices: {},
}
// machineTagsExtractMeta() extracts all of the misc parameters from Tags and
// returns a clean CNS and Tags struct.
func machineTagsExtractMeta(tags map[string]interface{}) (MachineCNS, map[string]string) {
nativeCNS := MachineCNS{}
nativeTags := make(map[string]string, len(tags))
for k, raw := range tags {
if _, found := reservedMachineCNSTags[k]; found {
switch k {
case machineCNSTagDisable:
b := raw.(bool)
nativeCNS.Disable = &b
case machineCNSTagReversePTR:
s := raw.(string)
nativeCNS.ReversePTR = &s
case machineCNSTagServices:
nativeCNS.Services = strings.Split(raw.(string), ",")
default:
// TODO(seanc@): should assert, logic fail
}
} else {
nativeTags[k] = raw.(string)
}
}
return nativeCNS, nativeTags
}
// toNative() exports a given _Machine (API representation) to its native object
// format.
func (api *_Machine) toNative() (*Machine, error) {
m := Machine(api.Machine)
m.CNS, m.Tags = machineTagsExtractMeta(api.Tags)
return &m, nil
}
// toTags() injects its state information into a Tags map suitable for use to
// submit an API call to the vmapi machine endpoint
func (mcns *MachineCNS) toTags(m map[string]interface{}) {
if mcns.Disable != nil {
s := fmt.Sprintf("%t", mcns.Disable)
m[machineCNSTagDisable] = &s
}
if mcns.ReversePTR != nil {
m[machineCNSTagReversePTR] = &mcns.ReversePTR
}
if len(mcns.Services) > 0 {
m[machineCNSTagServices] = strings.Join(mcns.Services, ",")
}
}

39
vendor/github.com/joyent/triton-go/network/client.go generated vendored Normal file
View File

@ -0,0 +1,39 @@
package network
import (
triton "github.com/joyent/triton-go"
"github.com/joyent/triton-go/client"
)
type NetworkClient struct {
Client *client.Client
}
func newNetworkClient(client *client.Client) *NetworkClient {
return &NetworkClient{
Client: client,
}
}
// NewClient returns a new client for working with Network endpoints and
// resources within CloudAPI
func NewClient(config *triton.ClientConfig) (*NetworkClient, error) {
// TODO: Utilize config interface within the function itself
client, err := client.New(config.TritonURL, config.MantaURL, config.AccountName, config.Signers...)
if err != nil {
return nil, err
}
return newNetworkClient(client), nil
}
// Fabrics returns a FabricsClient used for accessing functions pertaining to
// Fabric functionality in the Triton API.
func (c *NetworkClient) Fabrics() *FabricsClient {
return &FabricsClient{c.Client}
}
// Firewall returns a FirewallClient client used for accessing functions
// pertaining to firewall functionality in the Triton API.
func (c *NetworkClient) Firewall() *FirewallClient {
return &FirewallClient{c.Client}
}

269
vendor/github.com/joyent/triton-go/network/fabrics.go generated vendored Normal file
View File

@ -0,0 +1,269 @@
package network
import (
"encoding/json"
"fmt"
"net/http"
"context"
"github.com/hashicorp/errwrap"
"github.com/joyent/triton-go/client"
)
type FabricsClient struct {
client *client.Client
}
type FabricVLAN struct {
Name string `json:"name"`
ID int `json:"vlan_id"`
Description string `json:"description"`
}
type ListVLANsInput struct{}
func (c *FabricsClient) ListVLANs(ctx context.Context, _ *ListVLANsInput) ([]*FabricVLAN, error) {
path := fmt.Sprintf("/%s/fabrics/default/vlans", c.client.AccountName)
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 ListVLANs request: {{err}}", err)
}
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 result, nil
}
type CreateVLANInput struct {
Name string `json:"name"`
ID int `json:"vlan_id"`
Description string `json:"description,omitempty"`
}
func (c *FabricsClient) CreateVLAN(ctx context.Context, input *CreateVLANInput) (*FabricVLAN, error) {
path := fmt.Sprintf("/%s/fabrics/default/vlans", c.client.AccountName)
reqInputs := client.RequestInput{
Method: http.MethodPost,
Path: path,
Body: input,
}
respReader, err := c.client.ExecuteRequest(ctx, reqInputs)
if respReader != nil {
defer respReader.Close()
}
if err != nil {
return nil, errwrap.Wrapf("Error executing CreateVLAN request: {{err}}", err)
}
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 result, nil
}
type UpdateVLANInput struct {
ID int `json:"-"`
Name string `json:"name"`
Description string `json:"description"`
}
func (c *FabricsClient) UpdateVLAN(ctx context.Context, input *UpdateVLANInput) (*FabricVLAN, error) {
path := fmt.Sprintf("/%s/fabrics/default/vlans/%d", c.client.AccountName, input.ID)
reqInputs := client.RequestInput{
Method: http.MethodPut,
Path: path,
Body: input,
}
respReader, err := c.client.ExecuteRequest(ctx, reqInputs)
if respReader != nil {
defer respReader.Close()
}
if err != nil {
return nil, errwrap.Wrapf("Error executing UpdateVLAN request: {{err}}", err)
}
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 result, nil
}
type GetVLANInput struct {
ID int `json:"-"`
}
func (c *FabricsClient) GetVLAN(ctx context.Context, input *GetVLANInput) (*FabricVLAN, error) {
path := fmt.Sprintf("/%s/fabrics/default/vlans/%d", 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 GetVLAN request: {{err}}", err)
}
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 result, nil
}
type DeleteVLANInput struct {
ID int `json:"-"`
}
func (c *FabricsClient) DeleteVLAN(ctx context.Context, input *DeleteVLANInput) error {
path := fmt.Sprintf("/%s/fabrics/default/vlans/%d", c.client.AccountName, input.ID)
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 DeleteVLAN request: {{err}}", err)
}
return nil
}
type ListFabricsInput struct {
FabricVLANID int `json:"-"`
}
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)
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 ListFabrics request: {{err}}", err)
}
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 result, nil
}
type CreateFabricInput struct {
FabricVLANID int `json:"-"`
Name string `json:"name"`
Description string `json:"description,omitempty"`
Subnet string `json:"subnet"`
ProvisionStartIP string `json:"provision_start_ip"`
ProvisionEndIP string `json:"provision_end_ip"`
Gateway string `json:"gateway,omitempty"`
Resolvers []string `json:"resolvers,omitempty"`
Routes map[string]string `json:"routes,omitempty"`
InternetNAT bool `json:"internet_nat"`
}
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)
reqInputs := client.RequestInput{
Method: http.MethodPost,
Path: path,
Body: input,
}
respReader, err := c.client.ExecuteRequest(ctx, reqInputs)
if respReader != nil {
defer respReader.Close()
}
if err != nil {
return nil, errwrap.Wrapf("Error executing CreateFabric request: {{err}}", err)
}
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 result, nil
}
type GetFabricInput struct {
FabricVLANID int `json:"-"`
NetworkID string `json:"-"`
}
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)
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 GetFabric request: {{err}}", err)
}
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 result, nil
}
type DeleteFabricInput struct {
FabricVLANID int `json:"-"`
NetworkID string `json:"-"`
}
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)
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 DeleteFabric request: {{err}}", err)
}
return nil
}

250
vendor/github.com/joyent/triton-go/network/firewall.go generated vendored Normal file
View File

@ -0,0 +1,250 @@
package network
import (
"context"
"encoding/json"
"fmt"
"net/http"
"github.com/hashicorp/errwrap"
"github.com/joyent/triton-go/client"
)
type FirewallClient struct {
client *client.Client
}
// FirewallRule represents a firewall rule
type FirewallRule struct {
// ID is a unique identifier for this rule
ID string `json:"id"`
// Enabled indicates if the rule is enabled
Enabled bool `json:"enabled"`
// Rule is the firewall rule text
Rule string `json:"rule"`
// Global indicates if the rule is global. Optional.
Global bool `json:"global"`
// Description is a human-readable description for the rule. Optional
Description string `json:"description"`
}
type ListRulesInput struct{}
func (c *FirewallClient) ListRules(ctx context.Context, _ *ListRulesInput) ([]*FirewallRule, error) {
path := fmt.Sprintf("/%s/fwrules", c.client.AccountName)
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 ListRules request: {{err}}", err)
}
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 result, nil
}
type GetRuleInput struct {
ID string
}
func (c *FirewallClient) GetRule(ctx context.Context, input *GetRuleInput) (*FirewallRule, error) {
path := fmt.Sprintf("/%s/fwrules/%s", 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 GetRule request: {{err}}", err)
}
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 result, nil
}
type CreateRuleInput struct {
Enabled bool `json:"enabled"`
Rule string `json:"rule"`
Description string `json:"description,omitempty"`
}
func (c *FirewallClient) CreateRule(ctx context.Context, input *CreateRuleInput) (*FirewallRule, error) {
path := fmt.Sprintf("/%s/fwrules", c.client.AccountName)
reqInputs := client.RequestInput{
Method: http.MethodPost,
Path: path,
Body: input,
}
respReader, err := c.client.ExecuteRequest(ctx, reqInputs)
if respReader != nil {
defer respReader.Close()
}
if err != nil {
return nil, errwrap.Wrapf("Error executing CreateRule request: {{err}}", err)
}
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 result, nil
}
type UpdateRuleInput struct {
ID string `json:"-"`
Enabled bool `json:"enabled"`
Rule string `json:"rule"`
Description string `json:"description,omitempty"`
}
func (c *FirewallClient) UpdateRule(ctx context.Context, input *UpdateRuleInput) (*FirewallRule, error) {
path := fmt.Sprintf("/%s/fwrules/%s", c.client.AccountName, input.ID)
reqInputs := client.RequestInput{
Method: http.MethodPost,
Path: path,
Body: input,
}
respReader, err := c.client.ExecuteRequest(ctx, reqInputs)
if respReader != nil {
defer respReader.Close()
}
if err != nil {
return nil, errwrap.Wrapf("Error executing UpdateRule request: {{err}}", err)
}
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 result, nil
}
type EnableRuleInput struct {
ID string `json:"-"`
}
func (c *FirewallClient) EnableRule(ctx context.Context, input *EnableRuleInput) (*FirewallRule, error) {
path := fmt.Sprintf("/%s/fwrules/%s/enable", c.client.AccountName, input.ID)
reqInputs := client.RequestInput{
Method: http.MethodPost,
Path: path,
Body: input,
}
respReader, err := c.client.ExecuteRequest(ctx, reqInputs)
if respReader != nil {
defer respReader.Close()
}
if err != nil {
return nil, errwrap.Wrapf("Error executing EnableRule request: {{err}}", err)
}
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 result, nil
}
type DisableRuleInput struct {
ID string `json:"-"`
}
func (c *FirewallClient) DisableRule(ctx context.Context, input *DisableRuleInput) (*FirewallRule, error) {
path := fmt.Sprintf("/%s/fwrules/%s/disable", c.client.AccountName, input.ID)
reqInputs := client.RequestInput{
Method: http.MethodPost,
Path: path,
Body: input,
}
respReader, err := c.client.ExecuteRequest(ctx, reqInputs)
if respReader != nil {
defer respReader.Close()
}
if err != nil {
return nil, errwrap.Wrapf("Error executing DisableRule request: {{err}}", err)
}
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 result, nil
}
type DeleteRuleInput struct {
ID string
}
func (c *FirewallClient) DeleteRule(ctx context.Context, input *DeleteRuleInput) error {
path := fmt.Sprintf("/%s/fwrules/%s", c.client.AccountName, input.ID)
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 DeleteRule request: {{err}}", err)
}
return nil
}
type ListMachineRulesInput struct {
MachineID string
}
func (c *FirewallClient) ListMachineRules(ctx context.Context, input *ListMachineRulesInput) ([]*FirewallRule, error) {
path := fmt.Sprintf("/%s/machines/%s/firewallrules", 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 ListMachineRules request: {{err}}", err)
}
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 result, nil
}

View File

@ -1,24 +1,15 @@
package triton
package network
import (
"context"
"encoding/json"
"fmt"
"net/http"
"context"
"github.com/hashicorp/errwrap"
"github.com/joyent/triton-go/client"
)
type NetworksClient struct {
*Client
}
// Networks returns a c used for accessing functions pertaining to
// Network functionality in the Triton API.
func (c *Client) Networks() *NetworksClient {
return &NetworksClient{c}
}
type Network struct {
Id string `json:"id"`
Name string `json:"name"`
@ -34,11 +25,15 @@ type Network struct {
InternetNAT bool `json:"internet_nat"`
}
type ListNetworksInput struct{}
type ListInput struct{}
func (client *NetworksClient) ListNetworks(ctx context.Context, _ *ListNetworksInput) ([]*Network, error) {
path := fmt.Sprintf("/%s/networks", client.accountName)
respReader, err := client.executeRequest(ctx, http.MethodGet, path, nil)
func (c *NetworkClient) List(ctx context.Context, _ *ListInput) ([]*Network, error) {
path := fmt.Sprintf("/%s/networks", c.Client.AccountName)
reqInputs := client.RequestInput{
Method: http.MethodGet,
Path: path,
}
respReader, err := c.Client.ExecuteRequest(ctx, reqInputs)
if respReader != nil {
defer respReader.Close()
}
@ -55,13 +50,17 @@ func (client *NetworksClient) ListNetworks(ctx context.Context, _ *ListNetworksI
return result, nil
}
type GetNetworkInput struct {
type GetInput struct {
ID string
}
func (client *NetworksClient) GetNetwork(ctx context.Context, input *GetNetworkInput) (*Network, error) {
path := fmt.Sprintf("/%s/networks/%s", client.accountName, input.ID)
respReader, err := client.executeRequest(ctx, http.MethodGet, path, nil)
func (c *NetworkClient) Get(ctx context.Context, input *GetInput) (*Network, error) {
path := fmt.Sprintf("/%s/networks/%s", 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()
}

View File

@ -1,164 +0,0 @@
package triton
import (
"context"
"encoding/json"
"fmt"
"net/http"
"github.com/hashicorp/errwrap"
)
type RolesClient struct {
*Client
}
// Roles returns a c used for accessing functions pertaining
// to Role functionality in the Triton API.
func (c *Client) Roles() *RolesClient {
return &RolesClient{c}
}
type Role struct {
ID string `json:"id"`
Name string `json:"name"`
Policies []string `json:"policies"`
Members []string `json:"policies"`
DefaultMembers []string `json:"default_members"`
}
type ListRolesInput struct{}
func (client *RolesClient) ListRoles(ctx context.Context, _ *ListRolesInput) ([]*Role, error) {
path := fmt.Sprintf("/%s/roles", client.accountName)
respReader, err := client.executeRequest(ctx, http.MethodGet, path, nil)
if respReader != nil {
defer respReader.Close()
}
if err != nil {
return nil, errwrap.Wrapf("Error executing ListRoles request: {{err}}", err)
}
var result []*Role
decoder := json.NewDecoder(respReader)
if err = decoder.Decode(&result); err != nil {
return nil, errwrap.Wrapf("Error decoding ListRoles response: {{err}}", err)
}
return result, nil
}
type GetRoleInput struct {
RoleID string
}
func (client *RolesClient) GetRole(ctx context.Context, input *GetRoleInput) (*Role, error) {
path := fmt.Sprintf("/%s/roles/%s", client.accountName, input.RoleID)
respReader, err := client.executeRequest(ctx, http.MethodGet, path, nil)
if respReader != nil {
defer respReader.Close()
}
if err != nil {
return nil, errwrap.Wrapf("Error executing GetRole request: {{err}}", err)
}
var result *Role
decoder := json.NewDecoder(respReader)
if err = decoder.Decode(&result); err != nil {
return nil, errwrap.Wrapf("Error decoding GetRole response: {{err}}", err)
}
return result, nil
}
// CreateRoleInput represents the options that can be specified
// when creating a new role.
type CreateRoleInput struct {
// Name of the role. Required.
Name string `json:"name"`
// This account's policies to be given to this role. Optional.
Policies []string `json:"policies,omitempty"`
// This account's user logins to be added to this role. Optional.
Members []string `json:"members,omitempty"`
// This account's user logins to be added to this role and have
// it enabled by default. Optional.
DefaultMembers []string `json:"default_members,omitempty"`
}
func (client *RolesClient) CreateRole(ctx context.Context, input *CreateRoleInput) (*Role, error) {
path := fmt.Sprintf("/%s/roles", client.accountName)
respReader, err := client.executeRequest(ctx, http.MethodPost, path, input)
if respReader != nil {
defer respReader.Close()
}
if err != nil {
return nil, errwrap.Wrapf("Error executing CreateRole request: {{err}}", err)
}
var result *Role
decoder := json.NewDecoder(respReader)
if err = decoder.Decode(&result); err != nil {
return nil, errwrap.Wrapf("Error decoding CreateRole response: {{err}}", err)
}
return result, nil
}
// UpdateRoleInput represents the options that can be specified
// when updating a role. Anything but ID can be modified.
type UpdateRoleInput struct {
// ID of the role to modify. Required.
RoleID string `json:"id"`
// Name of the role. Required.
Name string `json:"name"`
// This account's policies to be given to this role. Optional.
Policies []string `json:"policies,omitempty"`
// This account's user logins to be added to this role. Optional.
Members []string `json:"members,omitempty"`
// This account's user logins to be added to this role and have
// it enabled by default. Optional.
DefaultMembers []string `json:"default_members,omitempty"`
}
func (client *RolesClient) UpdateRole(ctx context.Context, input *UpdateRoleInput) (*Role, error) {
path := fmt.Sprintf("/%s/roles/%s", client.accountName, input.RoleID)
respReader, err := client.executeRequest(ctx, http.MethodPost, path, input)
if respReader != nil {
defer respReader.Close()
}
if err != nil {
return nil, errwrap.Wrapf("Error executing UpdateRole request: {{err}}", err)
}
var result *Role
decoder := json.NewDecoder(respReader)
if err = decoder.Decode(&result); err != nil {
return nil, errwrap.Wrapf("Error decoding UpdateRole response: {{err}}", err)
}
return result, nil
}
type DeleteRoleInput struct {
RoleID string
}
func (client *RolesClient) DeleteRoles(ctx context.Context, input *DeleteRoleInput) error {
path := fmt.Sprintf("/%s/roles/%s", client.accountName, input.RoleID)
respReader, err := client.executeRequest(ctx, http.MethodDelete, path, nil)
if respReader != nil {
defer respReader.Close()
}
if err != nil {
return errwrap.Wrapf("Error executing DeleteRole request: {{err}}", err)
}
return nil
}

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

@ -0,0 +1,18 @@
package triton
import (
"github.com/joyent/triton-go/authentication"
)
// Universal package used for defining configuration used across all client
// constructors.
// ClientConfig is a placeholder/input struct around the behavior of configuring
// a client constructor through the implementation's runtime environment
// (SDC/MANTA env vars).
type ClientConfig struct {
TritonURL string
MantaURL string
AccountName string
Signers []authentication.Signer
}

30
vendor/vendor.json vendored
View File

@ -817,16 +817,34 @@
"revision": "c01cf91b011868172fdcd9f41838e80c9d716264"
},
{
"checksumSHA1": "o8jaSD36Zq42PMnmUaiB+vq+QNA=",
"checksumSHA1": "EqvUu0Ku0Ec5Tk6yhGNOuOr8yeA=",
"path": "github.com/joyent/triton-go",
"revision": "97ccd9f6c0c0652cf87997bcb01955e0329cd37e",
"revisionTime": "2017-05-09T20:29:43Z"
"revision": "5a58ad2cdec95cddd1e0a2e56f559341044b04f0",
"revisionTime": "2017-10-17T16:55:58Z"
},
{
"checksumSHA1": "QzUqkCSn/ZHyIK346xb9V6EBw9U=",
"checksumSHA1": "JKf97EAAAZFQ6Wf8qN9X7TWqNBY=",
"path": "github.com/joyent/triton-go/authentication",
"revision": "16cef4c2d78ba1d3bf89af75e93ae2dec6e56634",
"revisionTime": "2017-05-04T20:45:05Z"
"revision": "5a58ad2cdec95cddd1e0a2e56f559341044b04f0",
"revisionTime": "2017-10-17T16:55:58Z"
},
{
"checksumSHA1": "dlO1or0cyVMAmZzyLcBuoy+M0xU=",
"path": "github.com/joyent/triton-go/client",
"revision": "5a58ad2cdec95cddd1e0a2e56f559341044b04f0",
"revisionTime": "2017-10-17T16:55:58Z"
},
{
"checksumSHA1": "O/y7BfKJFUf3A8TCRMXgo9HSb1w=",
"path": "github.com/joyent/triton-go/compute",
"revision": "5a58ad2cdec95cddd1e0a2e56f559341044b04f0",
"revisionTime": "2017-10-17T16:55:58Z"
},
{
"checksumSHA1": "gyLtPyKlcumRSkrAH+SsDQo1GnY=",
"path": "github.com/joyent/triton-go/network",
"revision": "5a58ad2cdec95cddd1e0a2e56f559341044b04f0",
"revisionTime": "2017-10-17T16:55:58Z"
},
{
"checksumSHA1": "gEjGS03N1eysvpQ+FCHTxPcbxXc=",