Merge pull request #5531 from stack72/bump-triton-dependencies
Bump Joyent/triton-go to modern version of the SDK
This commit is contained in:
commit
a495948dc5
|
@ -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 {
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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)
|
||||
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("NewClient: %s", err)
|
||||
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 .....",
|
||||
})
|
||||
```go
|
||||
c, err := storage.NewClient(config)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
log.Fatalf("storage.NewClient: %s", err)
|
||||
}
|
||||
|
||||
// Key contains the return value.
|
||||
```
|
||||
|
||||
## 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
|
||||
|
|
|
@ -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
|
||||
}
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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}
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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
|
File diff suppressed because it is too large
Load Diff
|
@ -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
|
|
@ -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))
|
|
@ -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
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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, ",")
|
||||
}
|
||||
}
|
|
@ -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}
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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()
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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=",
|
||||
|
|
Loading…
Reference in New Issue