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"
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
|
"github.com/hashicorp/errwrap"
|
||||||
"github.com/hashicorp/packer/helper/communicator"
|
"github.com/hashicorp/packer/helper/communicator"
|
||||||
"github.com/hashicorp/packer/template/interpolate"
|
"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/authentication"
|
||||||
|
"github.com/joyent/triton-go/compute"
|
||||||
|
"github.com/joyent/triton-go/network"
|
||||||
)
|
)
|
||||||
|
|
||||||
// AccessConfig is for common configuration related to Triton access
|
// AccessConfig is for common configuration related to Triton access
|
||||||
|
@ -106,8 +109,39 @@ func (c *AccessConfig) createPrivateKeySigner() (authentication.Signer, error) {
|
||||||
return signer, nil
|
return signer, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *AccessConfig) CreateTritonClient() (*triton.Client, error) {
|
func (c *AccessConfig) CreateTritonClient() (*Client, error) {
|
||||||
return triton.NewClient(c.Endpoint, c.Account, c.signer)
|
|
||||||
|
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 {
|
func (c *AccessConfig) Comm() communicator.Config {
|
||||||
|
|
|
@ -7,11 +7,12 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/hashicorp/packer/packer"
|
"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 {
|
type driverTriton struct {
|
||||||
client *triton.Client
|
client *Client
|
||||||
ui packer.Ui
|
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) {
|
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,
|
MachineID: machineId,
|
||||||
Name: config.ImageName,
|
Name: config.ImageName,
|
||||||
Version: config.ImageVersion,
|
Version: config.ImageVersion,
|
||||||
|
@ -46,7 +48,8 @@ func (d *driverTriton) CreateImageFromMachine(machineId string, config Config) (
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *driverTriton) CreateMachine(config Config) (string, error) {
|
func (d *driverTriton) CreateMachine(config Config) (string, error) {
|
||||||
input := &triton.CreateMachineInput{
|
computeClient, _ := d.client.Compute()
|
||||||
|
input := &compute.CreateInstanceInput{
|
||||||
Package: config.MachinePackage,
|
Package: config.MachinePackage,
|
||||||
Image: config.MachineImage,
|
Image: config.MachineImage,
|
||||||
Metadata: config.MachineMetadata,
|
Metadata: config.MachineMetadata,
|
||||||
|
@ -66,7 +69,7 @@ func (d *driverTriton) CreateMachine(config Config) (string, error) {
|
||||||
input.Networks = config.MachineNetworks
|
input.Networks = config.MachineNetworks
|
||||||
}
|
}
|
||||||
|
|
||||||
machine, err := d.client.Machines().CreateMachine(context.Background(), input)
|
machine, err := computeClient.Instances().Create(context.Background(), input)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
@ -75,19 +78,22 @@ func (d *driverTriton) CreateMachine(config Config) (string, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *driverTriton) DeleteImage(imageId 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,
|
ImageID: imageId,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *driverTriton) DeleteMachine(machineId string) error {
|
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,
|
ID: machineId,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *driverTriton) GetMachineIP(machineId string) (string, error) {
|
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,
|
ID: machineId,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -98,8 +104,9 @@ func (d *driverTriton) GetMachineIP(machineId string) (string, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *driverTriton) StopMachine(machineId string) error {
|
func (d *driverTriton) StopMachine(machineId string) error {
|
||||||
return d.client.Machines().StopMachine(context.Background(), &triton.StopMachineInput{
|
computeClient, _ := d.client.Compute()
|
||||||
MachineID: machineId,
|
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 {
|
func (d *driverTriton) WaitForMachineState(machineId string, state string, timeout time.Duration) error {
|
||||||
return waitFor(
|
return waitFor(
|
||||||
func() (bool, error) {
|
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,
|
ID: machineId,
|
||||||
})
|
})
|
||||||
if machine == nil {
|
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 {
|
func (d *driverTriton) WaitForMachineDeletion(machineId string, timeout time.Duration) error {
|
||||||
return waitFor(
|
return waitFor(
|
||||||
func() (bool, error) {
|
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,
|
ID: machineId,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// Return true only when we receive a 410 (Gone) response. A 404
|
// Return true only when we receive a 410 (Gone) response. A 404
|
||||||
// indicates that the machine is being deleted whereas a 410 indicates
|
// indicates that the machine is being deleted whereas a 410 indicates
|
||||||
// that this process has completed.
|
// 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
|
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 {
|
func (d *driverTriton) WaitForImageCreation(imageId string, timeout time.Duration) error {
|
||||||
return waitFor(
|
return waitFor(
|
||||||
func() (bool, error) {
|
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,
|
ImageID: imageId,
|
||||||
})
|
})
|
||||||
if image == nil {
|
if image == nil {
|
||||||
|
|
|
@ -1,12 +1,19 @@
|
||||||
# triton-go
|
# 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
|
## 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"
|
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://'
|
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
|
```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 {
|
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...
|
||||||
|
|
||||||
```
|
```go
|
||||||
client := state.Client().Keys()
|
c, err := storage.NewClient(config)
|
||||||
|
|
||||||
key, err := client.CreateKey(&CreateKeyInput{
|
|
||||||
Name: "tempKey",
|
|
||||||
Key: "ssh-rsa .....",
|
|
||||||
})
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
log.Fatalf("storage.NewClient: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Key contains the return value.
|
|
||||||
```
|
```
|
||||||
|
|
||||||
## Error Handling
|
## 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
|
In order to run acceptance tests, the following environment variables must be
|
||||||
- [x] GetAccount
|
set:
|
||||||
- [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
|
|
||||||
|
|
||||||
## Running Acceptance Tests
|
- `TRITON_TEST` - must be set to any value in order to indicate desire to create
|
||||||
|
resources
|
||||||
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
|
|
||||||
- `SDC_URL` - the base endpoint for the Triton API
|
- `SDC_URL` - the base endpoint for the Triton API
|
||||||
- `SDC_ACCOUNT` - the account name 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
|
- `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
|
### Example Run
|
||||||
|
|
||||||
|
@ -207,10 +105,111 @@ $ HTTP_PROXY=http://localhost:8888 \
|
||||||
=== RUN TestAccKey_Delete
|
=== RUN TestAccKey_Delete
|
||||||
--- PASS: TestAccKey_Delete (15.08s)
|
--- PASS: TestAccKey_Delete (15.08s)
|
||||||
PASS
|
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
|
[4]: https://github.com/joyent/node-http-signature/blob/master/http_signing.md
|
||||||
[5]: 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/go-triton/authentication
|
[6]: https://godoc.org/github.com/joyent/triton-go/authentication
|
||||||
[7]: https://github.com/hashicorp/go-errwrap
|
[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 {
|
type PrivateKeySigner struct {
|
||||||
formattedKeyFingerprint string
|
formattedKeyFingerprint string
|
||||||
keyFingerprint string
|
keyFingerprint string
|
||||||
|
algorithm string
|
||||||
accountName string
|
accountName string
|
||||||
hashFunc crypto.Hash
|
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 nil, errors.New("Private key file does not match public key fingerprint")
|
||||||
}
|
}
|
||||||
|
|
||||||
return &PrivateKeySigner{
|
signer := &PrivateKeySigner{
|
||||||
formattedKeyFingerprint: displayKeyFingerprint,
|
formattedKeyFingerprint: displayKeyFingerprint,
|
||||||
keyFingerprint: keyFingerprint,
|
keyFingerprint: keyFingerprint,
|
||||||
accountName: accountName,
|
accountName: accountName,
|
||||||
|
|
||||||
hashFunc: crypto.SHA1,
|
hashFunc: crypto.SHA1,
|
||||||
privateKey: rsakey,
|
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) {
|
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)
|
keyID := fmt.Sprintf("/%s/keys/%s", s.accountName, s.formattedKeyFingerprint)
|
||||||
return fmt.Sprintf(authorizationHeaderFormat, keyID, "rsa-sha1", headerName, signedBase64), nil
|
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"`
|
const authorizationHeaderFormat = `Signature keyId="%s",algorithm="%s",headers="%s",signature="%s"`
|
||||||
|
|
||||||
type Signer interface {
|
type Signer interface {
|
||||||
|
DefaultAlgorithm() string
|
||||||
|
KeyFingerprint() string
|
||||||
Sign(dateHeader string) (string, error)
|
Sign(dateHeader string) (string, error)
|
||||||
|
SignRaw(toSign string) (string, string, error)
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,8 @@ package authentication
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto/md5"
|
"crypto/md5"
|
||||||
|
"crypto/sha256"
|
||||||
|
"encoding/base64"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
|
@ -16,6 +18,7 @@ import (
|
||||||
type SSHAgentSigner struct {
|
type SSHAgentSigner struct {
|
||||||
formattedKeyFingerprint string
|
formattedKeyFingerprint string
|
||||||
keyFingerprint string
|
keyFingerprint string
|
||||||
|
algorithm string
|
||||||
accountName string
|
accountName string
|
||||||
keyIdentifier 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)
|
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
|
var matchingKey ssh.PublicKey
|
||||||
for _, key := range keys {
|
for _, key := range keys {
|
||||||
h := md5.New()
|
keyMD5 := md5.New()
|
||||||
h.Write(key.Marshal())
|
keyMD5.Write(key.Marshal())
|
||||||
fp := fmt.Sprintf("%x", h.Sum(nil))
|
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
|
matchingKey = key
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -60,14 +69,22 @@ func NewSSHAgentSigner(keyFingerprint, accountName string) (*SSHAgentSigner, err
|
||||||
|
|
||||||
formattedKeyFingerprint := formatPublicKeyFingerprint(matchingKey, true)
|
formattedKeyFingerprint := formatPublicKeyFingerprint(matchingKey, true)
|
||||||
|
|
||||||
return &SSHAgentSigner{
|
signer := &SSHAgentSigner{
|
||||||
formattedKeyFingerprint: formattedKeyFingerprint,
|
formattedKeyFingerprint: formattedKeyFingerprint,
|
||||||
keyFingerprint: keyFingerprint,
|
keyFingerprint: keyFingerprint,
|
||||||
accountName: accountName,
|
accountName: accountName,
|
||||||
agent: ag,
|
agent: ag,
|
||||||
key: matchingKey,
|
key: matchingKey,
|
||||||
keyIdentifier: fmt.Sprintf("/%s/keys/%s", accountName, formattedKeyFingerprint),
|
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) {
|
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,
|
return fmt.Sprintf(authorizationHeaderFormat, s.keyIdentifier,
|
||||||
authSignature.SignatureType(), headerName, authSignature.String()), nil
|
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 (
|
import (
|
||||||
"fmt"
|
|
||||||
|
|
||||||
"github.com/hashicorp/errwrap"
|
"github.com/hashicorp/errwrap"
|
||||||
|
"github.com/joyent/triton-go/client"
|
||||||
)
|
)
|
||||||
|
|
||||||
// TritonError represents an error code and message along with
|
// IsBadRequest tests whether err wraps a client.TritonError 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
|
|
||||||
// code BadRequest
|
// code BadRequest
|
||||||
func IsBadRequest(err error) bool {
|
func IsBadRequest(err error) bool {
|
||||||
return isSpecificError(err, "BadRequest")
|
return isSpecificError(err, "BadRequest")
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsInternalError tests whether err wraps a TritonError with
|
// IsInternalError tests whether err wraps a client.TritonError with
|
||||||
// code InternalError
|
// code InternalError
|
||||||
func IsInternalError(err error) bool {
|
func IsInternalError(err error) bool {
|
||||||
return isSpecificError(err, "InternalError")
|
return isSpecificError(err, "InternalError")
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsInUseError tests whether err wraps a TritonError with
|
// IsInUseError tests whether err wraps a client.TritonError with
|
||||||
// code InUseError
|
// code InUseError
|
||||||
func IsInUseError(err error) bool {
|
func IsInUseError(err error) bool {
|
||||||
return isSpecificError(err, "InUseError")
|
return isSpecificError(err, "InUseError")
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsInvalidArgument tests whether err wraps a TritonError with
|
// IsInvalidArgument tests whether err wraps a client.TritonError with
|
||||||
// code InvalidArgument
|
// code InvalidArgument
|
||||||
func IsInvalidArgument(err error) bool {
|
func IsInvalidArgument(err error) bool {
|
||||||
return isSpecificError(err, "InvalidArgument")
|
return isSpecificError(err, "InvalidArgument")
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsInvalidCredentials tests whether err wraps a TritonError with
|
// IsInvalidCredentials tests whether err wraps a client.TritonError with
|
||||||
// code InvalidCredentials
|
// code InvalidCredentials
|
||||||
func IsInvalidCredentials(err error) bool {
|
func IsInvalidCredentials(err error) bool {
|
||||||
return isSpecificError(err, "InvalidCredentials")
|
return isSpecificError(err, "InvalidCredentials")
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsInvalidHeader tests whether err wraps a TritonError with
|
// IsInvalidHeader tests whether err wraps a client.TritonError with
|
||||||
// code InvalidHeader
|
// code InvalidHeader
|
||||||
func IsInvalidHeader(err error) bool {
|
func IsInvalidHeader(err error) bool {
|
||||||
return isSpecificError(err, "InvalidHeader")
|
return isSpecificError(err, "InvalidHeader")
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsInvalidVersion tests whether err wraps a TritonError with
|
// IsInvalidVersion tests whether err wraps a client.TritonError with
|
||||||
// code InvalidVersion
|
// code InvalidVersion
|
||||||
func IsInvalidVersion(err error) bool {
|
func IsInvalidVersion(err error) bool {
|
||||||
return isSpecificError(err, "InvalidVersion")
|
return isSpecificError(err, "InvalidVersion")
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsMissingParameter tests whether err wraps a TritonError with
|
// IsMissingParameter tests whether err wraps a client.TritonError with
|
||||||
// code MissingParameter
|
// code MissingParameter
|
||||||
func IsMissingParameter(err error) bool {
|
func IsMissingParameter(err error) bool {
|
||||||
return isSpecificError(err, "MissingParameter")
|
return isSpecificError(err, "MissingParameter")
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsNotAuthorized tests whether err wraps a TritonError with
|
// IsNotAuthorized tests whether err wraps a client.TritonError with
|
||||||
// code NotAuthorized
|
// code NotAuthorized
|
||||||
func IsNotAuthorized(err error) bool {
|
func IsNotAuthorized(err error) bool {
|
||||||
return isSpecificError(err, "NotAuthorized")
|
return isSpecificError(err, "NotAuthorized")
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsRequestThrottled tests whether err wraps a TritonError with
|
// IsRequestThrottled tests whether err wraps a client.TritonError with
|
||||||
// code RequestThrottled
|
// code RequestThrottled
|
||||||
func IsRequestThrottled(err error) bool {
|
func IsRequestThrottled(err error) bool {
|
||||||
return isSpecificError(err, "RequestThrottled")
|
return isSpecificError(err, "RequestThrottled")
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsRequestTooLarge tests whether err wraps a TritonError with
|
// IsRequestTooLarge tests whether err wraps a client.TritonError with
|
||||||
// code RequestTooLarge
|
// code RequestTooLarge
|
||||||
func IsRequestTooLarge(err error) bool {
|
func IsRequestTooLarge(err error) bool {
|
||||||
return isSpecificError(err, "RequestTooLarge")
|
return isSpecificError(err, "RequestTooLarge")
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsRequestMoved tests whether err wraps a TritonError with
|
// IsRequestMoved tests whether err wraps a client.TritonError with
|
||||||
// code RequestMoved
|
// code RequestMoved
|
||||||
func IsRequestMoved(err error) bool {
|
func IsRequestMoved(err error) bool {
|
||||||
return isSpecificError(err, "RequestMoved")
|
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
|
// code ResourceNotFound
|
||||||
func IsResourceNotFound(err error) bool {
|
func IsResourceNotFound(err error) bool {
|
||||||
return isSpecificError(err, "ResourceNotFound")
|
return isSpecificError(err, "ResourceNotFound")
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsUnknownError tests whether err wraps a TritonError with
|
// IsUnknownError tests whether err wraps a client.TritonError with
|
||||||
// code UnknownError
|
// code UnknownError
|
||||||
func IsUnknownError(err error) bool {
|
func IsUnknownError(err error) bool {
|
||||||
return isSpecificError(err, "UnknownError")
|
return isSpecificError(err, "UnknownError")
|
||||||
}
|
}
|
||||||
|
|
||||||
// isSpecificError checks whether the error represented by err wraps
|
// 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 {
|
func isSpecificError(err error, errorCode string) bool {
|
||||||
if err == nil {
|
if err == nil {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
tritonErrorInterface := errwrap.GetType(err.(error), &TritonError{})
|
tritonErrorInterface := errwrap.GetType(err.(error), &client.TritonError{})
|
||||||
if tritonErrorInterface == nil {
|
if tritonErrorInterface == nil {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
tritonErr := tritonErrorInterface.(*TritonError)
|
tritonErr := tritonErrorInterface.(*client.TritonError)
|
||||||
if tritonErr.Code == errorCode {
|
if tritonErr.Code == errorCode {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
package triton
|
package compute
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
@ -9,16 +9,11 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/hashicorp/errwrap"
|
"github.com/hashicorp/errwrap"
|
||||||
|
"github.com/joyent/triton-go/client"
|
||||||
)
|
)
|
||||||
|
|
||||||
type ImagesClient struct {
|
type ImagesClient struct {
|
||||||
*Client
|
client *client.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}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type ImageFile struct {
|
type ImageFile struct {
|
||||||
|
@ -44,25 +39,62 @@ type Image struct {
|
||||||
Tags map[string]string `json:"tags"`
|
Tags map[string]string `json:"tags"`
|
||||||
EULA string `json:"eula"`
|
EULA string `json:"eula"`
|
||||||
ACL []string `json:"acl"`
|
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) {
|
func (c *ImagesClient) List(ctx context.Context, input *ListImagesInput) ([]*Image, error) {
|
||||||
path := fmt.Sprintf("/%s/images", client.accountName)
|
path := fmt.Sprintf("/%s/images", c.client.AccountName)
|
||||||
respReader, err := client.executeRequest(ctx, http.MethodGet, path, nil)
|
|
||||||
|
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 {
|
if respReader != nil {
|
||||||
defer respReader.Close()
|
defer respReader.Close()
|
||||||
}
|
}
|
||||||
if err != nil {
|
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
|
var result []*Image
|
||||||
decoder := json.NewDecoder(respReader)
|
decoder := json.NewDecoder(respReader)
|
||||||
if err = decoder.Decode(&result); err != nil {
|
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
|
return result, nil
|
||||||
|
@ -72,20 +104,24 @@ type GetImageInput struct {
|
||||||
ImageID string
|
ImageID string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (client *ImagesClient) GetImage(ctx context.Context, input *GetImageInput) (*Image, error) {
|
func (c *ImagesClient) Get(ctx context.Context, input *GetImageInput) (*Image, error) {
|
||||||
path := fmt.Sprintf("/%s/images/%s", client.accountName, input.ImageID)
|
path := fmt.Sprintf("/%s/images/%s", c.client.AccountName, input.ImageID)
|
||||||
respReader, err := client.executeRequest(ctx, http.MethodGet, path, nil)
|
reqInputs := client.RequestInput{
|
||||||
|
Method: http.MethodGet,
|
||||||
|
Path: path,
|
||||||
|
}
|
||||||
|
respReader, err := c.client.ExecuteRequest(ctx, reqInputs)
|
||||||
if respReader != nil {
|
if respReader != nil {
|
||||||
defer respReader.Close()
|
defer respReader.Close()
|
||||||
}
|
}
|
||||||
if err != nil {
|
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
|
var result *Image
|
||||||
decoder := json.NewDecoder(respReader)
|
decoder := json.NewDecoder(respReader)
|
||||||
if err = decoder.Decode(&result); err != nil {
|
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
|
return result, nil
|
||||||
|
@ -95,14 +131,18 @@ type DeleteImageInput struct {
|
||||||
ImageID string
|
ImageID string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (client *ImagesClient) DeleteImage(ctx context.Context, input *DeleteImageInput) error {
|
func (c *ImagesClient) Delete(ctx context.Context, input *DeleteImageInput) error {
|
||||||
path := fmt.Sprintf("/%s/images/%s", client.accountName, input.ImageID)
|
path := fmt.Sprintf("/%s/images/%s", c.client.AccountName, input.ImageID)
|
||||||
respReader, err := client.executeRequest(ctx, http.MethodDelete, path, nil)
|
reqInputs := client.RequestInput{
|
||||||
|
Method: http.MethodDelete,
|
||||||
|
Path: path,
|
||||||
|
}
|
||||||
|
respReader, err := c.client.ExecuteRequest(ctx, reqInputs)
|
||||||
if respReader != nil {
|
if respReader != nil {
|
||||||
defer respReader.Close()
|
defer respReader.Close()
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errwrap.Wrapf("Error executing DeleteKey request: {{err}}", err)
|
return errwrap.Wrapf("Error executing Delete request: {{err}}", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
@ -119,24 +159,29 @@ type MantaLocation struct {
|
||||||
ManifestPath string `json:"manifest_path"`
|
ManifestPath string `json:"manifest_path"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (client *ImagesClient) ExportImage(ctx context.Context, input *ExportImageInput) (*MantaLocation, error) {
|
func (c *ImagesClient) Export(ctx context.Context, input *ExportImageInput) (*MantaLocation, error) {
|
||||||
path := fmt.Sprintf("/%s/images/%s", client.accountName, input.ImageID)
|
path := fmt.Sprintf("/%s/images/%s", c.client.AccountName, input.ImageID)
|
||||||
query := &url.Values{}
|
query := &url.Values{}
|
||||||
query.Set("action", "export")
|
query.Set("action", "export")
|
||||||
query.Set("manta_path", input.MantaPath)
|
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 {
|
if respReader != nil {
|
||||||
defer respReader.Close()
|
defer respReader.Close()
|
||||||
}
|
}
|
||||||
if err != nil {
|
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
|
var result *MantaLocation
|
||||||
decoder := json.NewDecoder(respReader)
|
decoder := json.NewDecoder(respReader)
|
||||||
if err = decoder.Decode(&result); err != nil {
|
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
|
return result, nil
|
||||||
|
@ -153,20 +198,25 @@ type CreateImageFromMachineInput struct {
|
||||||
Tags map[string]string `json:"tags,omitempty"`
|
Tags map[string]string `json:"tags,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (client *ImagesClient) CreateImageFromMachine(ctx context.Context, input *CreateImageFromMachineInput) (*Image, error) {
|
func (c *ImagesClient) CreateFromMachine(ctx context.Context, input *CreateImageFromMachineInput) (*Image, error) {
|
||||||
path := fmt.Sprintf("/%s/images", client.accountName)
|
path := fmt.Sprintf("/%s/images", c.client.AccountName)
|
||||||
respReader, err := client.executeRequest(ctx, http.MethodPost, path, input)
|
reqInputs := client.RequestInput{
|
||||||
|
Method: http.MethodPost,
|
||||||
|
Path: path,
|
||||||
|
Body: input,
|
||||||
|
}
|
||||||
|
respReader, err := c.client.ExecuteRequest(ctx, reqInputs)
|
||||||
if respReader != nil {
|
if respReader != nil {
|
||||||
defer respReader.Close()
|
defer respReader.Close()
|
||||||
}
|
}
|
||||||
if err != nil {
|
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
|
var result *Image
|
||||||
decoder := json.NewDecoder(respReader)
|
decoder := json.NewDecoder(respReader)
|
||||||
if err = decoder.Decode(&result); err != nil {
|
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
|
return result, nil
|
||||||
|
@ -183,23 +233,29 @@ type UpdateImageInput struct {
|
||||||
Tags map[string]string `json:"tags,omitempty"`
|
Tags map[string]string `json:"tags,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (client *ImagesClient) UpdateImage(ctx context.Context, input *UpdateImageInput) (*Image, error) {
|
func (c *ImagesClient) Update(ctx context.Context, input *UpdateImageInput) (*Image, error) {
|
||||||
path := fmt.Sprintf("/%s/images/%s", client.accountName, input.ImageID)
|
path := fmt.Sprintf("/%s/images/%s", c.client.AccountName, input.ImageID)
|
||||||
query := &url.Values{}
|
query := &url.Values{}
|
||||||
query.Set("action", "update")
|
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 {
|
if respReader != nil {
|
||||||
defer respReader.Close()
|
defer respReader.Close()
|
||||||
}
|
}
|
||||||
if err != nil {
|
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
|
var result *Image
|
||||||
decoder := json.NewDecoder(respReader)
|
decoder := json.NewDecoder(respReader)
|
||||||
if err = decoder.Decode(&result); err != nil {
|
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
|
return result, nil
|
File diff suppressed because it is too large
Load Diff
|
@ -1,4 +1,4 @@
|
||||||
package triton
|
package compute
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
@ -7,16 +7,11 @@ import (
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
"github.com/hashicorp/errwrap"
|
"github.com/hashicorp/errwrap"
|
||||||
|
"github.com/joyent/triton-go/client"
|
||||||
)
|
)
|
||||||
|
|
||||||
type PackagesClient struct {
|
type PackagesClient struct {
|
||||||
*Client
|
client *client.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}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type Package struct {
|
type Package struct {
|
||||||
|
@ -44,20 +39,25 @@ type ListPackagesInput struct {
|
||||||
Group string `json:"group"`
|
Group string `json:"group"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (client *PackagesClient) ListPackages(ctx context.Context, input *ListPackagesInput) ([]*Package, error) {
|
func (c *PackagesClient) List(ctx context.Context, input *ListPackagesInput) ([]*Package, error) {
|
||||||
path := fmt.Sprintf("/%s/packages", client.accountName)
|
path := fmt.Sprintf("/%s/packages", c.client.AccountName)
|
||||||
respReader, err := client.executeRequest(ctx, http.MethodGet, path, input)
|
reqInputs := client.RequestInput{
|
||||||
|
Method: http.MethodGet,
|
||||||
|
Path: path,
|
||||||
|
Body: input,
|
||||||
|
}
|
||||||
|
respReader, err := c.client.ExecuteRequest(ctx, reqInputs)
|
||||||
if respReader != nil {
|
if respReader != nil {
|
||||||
defer respReader.Close()
|
defer respReader.Close()
|
||||||
}
|
}
|
||||||
if err != nil {
|
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
|
var result []*Package
|
||||||
decoder := json.NewDecoder(respReader)
|
decoder := json.NewDecoder(respReader)
|
||||||
if err = decoder.Decode(&result); err != nil {
|
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
|
return result, nil
|
||||||
|
@ -67,20 +67,24 @@ type GetPackageInput struct {
|
||||||
ID string
|
ID string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (client *PackagesClient) GetPackage(ctx context.Context, input *GetPackageInput) (*Package, error) {
|
func (c *PackagesClient) Get(ctx context.Context, input *GetPackageInput) (*Package, error) {
|
||||||
path := fmt.Sprintf("/%s/packages/%s", client.accountName, input.ID)
|
path := fmt.Sprintf("/%s/packages/%s", c.client.AccountName, input.ID)
|
||||||
respReader, err := client.executeRequest(ctx, http.MethodGet, path, nil)
|
reqInputs := client.RequestInput{
|
||||||
|
Method: http.MethodGet,
|
||||||
|
Path: path,
|
||||||
|
}
|
||||||
|
respReader, err := c.client.ExecuteRequest(ctx, reqInputs)
|
||||||
if respReader != nil {
|
if respReader != nil {
|
||||||
defer respReader.Close()
|
defer respReader.Close()
|
||||||
}
|
}
|
||||||
if err != nil {
|
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
|
var result *Package
|
||||||
decoder := json.NewDecoder(respReader)
|
decoder := json.NewDecoder(respReader)
|
||||||
if err = decoder.Decode(&result); err != nil {
|
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
|
return result, nil
|
|
@ -1,4 +1,4 @@
|
||||||
package triton
|
package compute
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
@ -8,16 +8,11 @@ import (
|
||||||
"sort"
|
"sort"
|
||||||
|
|
||||||
"github.com/hashicorp/errwrap"
|
"github.com/hashicorp/errwrap"
|
||||||
|
"github.com/joyent/triton-go/client"
|
||||||
)
|
)
|
||||||
|
|
||||||
type ServicesClient struct {
|
type ServicesClient struct {
|
||||||
*Client
|
client *client.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}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type Service struct {
|
type Service struct {
|
||||||
|
@ -27,20 +22,24 @@ type Service struct {
|
||||||
|
|
||||||
type ListServicesInput struct{}
|
type ListServicesInput struct{}
|
||||||
|
|
||||||
func (client *ServicesClient) ListServices(ctx context.Context, _ *ListServicesInput) ([]*Service, error) {
|
func (c *ServicesClient) List(ctx context.Context, _ *ListServicesInput) ([]*Service, error) {
|
||||||
path := fmt.Sprintf("/%s/services", client.accountName)
|
path := fmt.Sprintf("/%s/services", c.client.AccountName)
|
||||||
respReader, err := client.executeRequest(ctx, http.MethodGet, path, nil)
|
reqInputs := client.RequestInput{
|
||||||
|
Method: http.MethodGet,
|
||||||
|
Path: path,
|
||||||
|
}
|
||||||
|
respReader, err := c.client.ExecuteRequest(ctx, reqInputs)
|
||||||
if respReader != nil {
|
if respReader != nil {
|
||||||
defer respReader.Close()
|
defer respReader.Close()
|
||||||
}
|
}
|
||||||
if err != nil {
|
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
|
var intermediate map[string]string
|
||||||
decoder := json.NewDecoder(respReader)
|
decoder := json.NewDecoder(respReader)
|
||||||
if err = decoder.Decode(&intermediate); err != nil {
|
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))
|
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 (
|
import (
|
||||||
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
"context"
|
|
||||||
"github.com/hashicorp/errwrap"
|
"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 {
|
type Network struct {
|
||||||
Id string `json:"id"`
|
Id string `json:"id"`
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
|
@ -34,11 +25,15 @@ type Network struct {
|
||||||
InternetNAT bool `json:"internet_nat"`
|
InternetNAT bool `json:"internet_nat"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type ListNetworksInput struct{}
|
type ListInput struct{}
|
||||||
|
|
||||||
func (client *NetworksClient) ListNetworks(ctx context.Context, _ *ListNetworksInput) ([]*Network, error) {
|
func (c *NetworkClient) List(ctx context.Context, _ *ListInput) ([]*Network, error) {
|
||||||
path := fmt.Sprintf("/%s/networks", client.accountName)
|
path := fmt.Sprintf("/%s/networks", c.Client.AccountName)
|
||||||
respReader, err := client.executeRequest(ctx, http.MethodGet, path, nil)
|
reqInputs := client.RequestInput{
|
||||||
|
Method: http.MethodGet,
|
||||||
|
Path: path,
|
||||||
|
}
|
||||||
|
respReader, err := c.Client.ExecuteRequest(ctx, reqInputs)
|
||||||
if respReader != nil {
|
if respReader != nil {
|
||||||
defer respReader.Close()
|
defer respReader.Close()
|
||||||
}
|
}
|
||||||
|
@ -55,13 +50,17 @@ func (client *NetworksClient) ListNetworks(ctx context.Context, _ *ListNetworksI
|
||||||
return result, nil
|
return result, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
type GetNetworkInput struct {
|
type GetInput struct {
|
||||||
ID string
|
ID string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (client *NetworksClient) GetNetwork(ctx context.Context, input *GetNetworkInput) (*Network, error) {
|
func (c *NetworkClient) Get(ctx context.Context, input *GetInput) (*Network, error) {
|
||||||
path := fmt.Sprintf("/%s/networks/%s", client.accountName, input.ID)
|
path := fmt.Sprintf("/%s/networks/%s", c.Client.AccountName, input.ID)
|
||||||
respReader, err := client.executeRequest(ctx, http.MethodGet, path, nil)
|
reqInputs := client.RequestInput{
|
||||||
|
Method: http.MethodGet,
|
||||||
|
Path: path,
|
||||||
|
}
|
||||||
|
respReader, err := c.Client.ExecuteRequest(ctx, reqInputs)
|
||||||
if respReader != nil {
|
if respReader != nil {
|
||||||
defer respReader.Close()
|
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"
|
"revision": "c01cf91b011868172fdcd9f41838e80c9d716264"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"checksumSHA1": "o8jaSD36Zq42PMnmUaiB+vq+QNA=",
|
"checksumSHA1": "EqvUu0Ku0Ec5Tk6yhGNOuOr8yeA=",
|
||||||
"path": "github.com/joyent/triton-go",
|
"path": "github.com/joyent/triton-go",
|
||||||
"revision": "97ccd9f6c0c0652cf87997bcb01955e0329cd37e",
|
"revision": "5a58ad2cdec95cddd1e0a2e56f559341044b04f0",
|
||||||
"revisionTime": "2017-05-09T20:29:43Z"
|
"revisionTime": "2017-10-17T16:55:58Z"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"checksumSHA1": "QzUqkCSn/ZHyIK346xb9V6EBw9U=",
|
"checksumSHA1": "JKf97EAAAZFQ6Wf8qN9X7TWqNBY=",
|
||||||
"path": "github.com/joyent/triton-go/authentication",
|
"path": "github.com/joyent/triton-go/authentication",
|
||||||
"revision": "16cef4c2d78ba1d3bf89af75e93ae2dec6e56634",
|
"revision": "5a58ad2cdec95cddd1e0a2e56f559341044b04f0",
|
||||||
"revisionTime": "2017-05-04T20:45:05Z"
|
"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=",
|
"checksumSHA1": "gEjGS03N1eysvpQ+FCHTxPcbxXc=",
|
||||||
|
|
Loading…
Reference in New Issue