Merge pull request #4325 from siepkes/f-triton-builder-2

Add Joyent Triton Builder
This commit is contained in:
James Nugent 2017-01-01 15:40:11 -06:00 committed by GitHub
commit 01c09aa7f7
61 changed files with 6150 additions and 0 deletions

View File

@ -0,0 +1,99 @@
package triton
import (
"fmt"
"io/ioutil"
"log"
"os"
"github.com/joyent/gocommon/client"
"github.com/joyent/gosdc/cloudapi"
"github.com/joyent/gosign/auth"
"github.com/mitchellh/packer/helper/communicator"
"github.com/mitchellh/packer/template/interpolate"
)
// AccessConfig is for common configuration related to Triton access
type AccessConfig struct {
Endpoint string `mapstructure:"triton_url"`
Account string `mapstructure:"triton_account"`
KeyID string `mapstructure:"triton_key_id"`
KeyMaterial string `mapstructure:"triton_key_material"`
}
// Prepare performs basic validation on the AccessConfig
func (c *AccessConfig) Prepare(ctx *interpolate.Context) []error {
var errs []error
if c.Endpoint == "" {
// Use Joyent public cloud as the default endpoint if none is in environment
c.Endpoint = "https://us-east-1.api.joyent.com"
}
if c.Account == "" {
errs = append(errs, fmt.Errorf("triton_account is required to use the triton builder"))
}
if c.KeyID == "" {
errs = append(errs, fmt.Errorf("triton_key_id is required to use the triton builder"))
}
var err error
c.KeyMaterial, err = processKeyMaterial(c.KeyMaterial)
if c.KeyMaterial == "" || err != nil {
errs = append(errs, fmt.Errorf("valid triton_key_material is required to use the triton builder"))
}
if len(errs) > 0 {
return errs
}
return nil
}
// CreateTritonClient returns an SDC client configured with the appropriate client credentials
// or an error if creating the client fails.
func (c *AccessConfig) CreateTritonClient() (*cloudapi.Client, error) {
keyData, err := processKeyMaterial(c.KeyMaterial)
if err != nil {
return nil, err
}
userauth, err := auth.NewAuth(c.Account, string(keyData), "rsa-sha256")
if err != nil {
return nil, err
}
creds := &auth.Credentials{
UserAuthentication: userauth,
SdcKeyId: c.KeyID,
SdcEndpoint: auth.Endpoint{URL: c.Endpoint},
}
return cloudapi.New(client.NewClient(
c.Endpoint,
cloudapi.DefaultAPIVersion,
creds,
log.New(os.Stdout, "", log.Flags()),
)), nil
}
func (c *AccessConfig) Comm() communicator.Config {
return communicator.Config{}
}
func processKeyMaterial(keyMaterial string) (string, error) {
// Check for keyMaterial being a file path
if _, err := os.Stat(keyMaterial); err != nil {
// Not a valid file. Assume that keyMaterial is the key data
return keyMaterial, nil
}
b, err := ioutil.ReadFile(keyMaterial)
if err != nil {
return "", fmt.Errorf("Error reading key_material from path '%s': %s",
keyMaterial, err)
}
return string(b), nil
}

View File

@ -0,0 +1,43 @@
package triton
import (
"testing"
)
func TestAccessConfig_Prepare(t *testing.T) {
ac := testAccessConfig(t)
errs := ac.Prepare(nil)
if errs != nil {
t.Fatal("should not error")
}
ac = testAccessConfig(t)
ac.Account = ""
errs = ac.Prepare(nil)
if errs == nil {
t.Fatal("should error")
}
ac = testAccessConfig(t)
ac.KeyID = ""
errs = ac.Prepare(nil)
if errs == nil {
t.Fatal("should error")
}
ac = testAccessConfig(t)
ac.KeyMaterial = ""
errs = ac.Prepare(nil)
if errs == nil {
t.Fatal("should error")
}
}
func testAccessConfig(t *testing.T) AccessConfig {
return AccessConfig{
Endpoint: "test-endpoint",
Account: "test-account",
KeyID: "test-id",
KeyMaterial: "test-private-key",
}
}

View File

@ -0,0 +1,49 @@
package triton
import (
"fmt"
"log"
)
// Artifact is an artifact implementation that contains built Triton images.
type Artifact struct {
// ImageID is the image ID of the artifact
ImageID string
// BuilderIDValue is the unique ID for the builder that created this Image
BuilderIDValue string
// SDC connection for cleanup etc
Driver Driver
}
func (a *Artifact) BuilderId() string {
return a.BuilderIDValue
}
func (*Artifact) Files() []string {
return nil
}
func (a *Artifact) Id() string {
return a.ImageID
}
func (a *Artifact) String() string {
return fmt.Sprintf("Image was created: %s", a.ImageID)
}
func (a *Artifact) State(name string) interface{} {
//TODO(jen20): Figure out how to make this work with Atlas
return nil
}
func (a *Artifact) Destroy() error {
log.Printf("Deleting image ID (%s)", a.ImageID)
err := a.Driver.DeleteImage(a.ImageID)
if err != nil {
return err
}
return nil
}

103
builder/triton/builder.go Normal file
View File

@ -0,0 +1,103 @@
package triton
import (
"log"
"github.com/hashicorp/go-multierror"
"github.com/mitchellh/multistep"
"github.com/mitchellh/packer/common"
"github.com/mitchellh/packer/helper/communicator"
"github.com/mitchellh/packer/helper/config"
"github.com/mitchellh/packer/packer"
)
const (
BuilderId = "joyent.triton"
)
type Builder struct {
config Config
runner multistep.Runner
}
func (b *Builder) Prepare(raws ...interface{}) ([]string, error) {
errs := &multierror.Error{}
err := config.Decode(&b.config, &config.DecodeOpts{
Interpolate: true,
InterpolateContext: &b.config.ctx,
}, raws...)
if err != nil {
errs = multierror.Append(errs, err)
}
errs = multierror.Append(errs, b.config.AccessConfig.Prepare(&b.config.ctx)...)
errs = multierror.Append(errs, b.config.SourceMachineConfig.Prepare(&b.config.ctx)...)
errs = multierror.Append(errs, b.config.Comm.Prepare(&b.config.ctx)...)
errs = multierror.Append(errs, b.config.TargetImageConfig.Prepare(&b.config.ctx)...)
return nil, errs.ErrorOrNil()
}
func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packer.Artifact, error) {
config := b.config
driver, err := NewDriverTriton(ui, config)
if err != nil {
return nil, err
}
state := new(multistep.BasicStateBag)
state.Put("config", b.config)
state.Put("debug", b.config.PackerDebug)
state.Put("driver", driver)
state.Put("hook", hook)
state.Put("ui", ui)
steps := []multistep.Step{
&StepCreateSourceMachine{},
&communicator.StepConnect{
Config: &config.Comm,
Host: commHost,
SSHConfig: sshConfig(
b.config.Comm.SSHAgentAuth,
b.config.Comm.SSHUsername,
b.config.Comm.SSHPrivateKey,
b.config.Comm.SSHPassword),
},
&common.StepProvision{},
&StepStopMachine{},
&StepCreateImageFromMachine{},
&StepDeleteMachine{},
}
b.runner = common.NewRunnerWithPauseFn(steps, b.config.PackerConfig, ui, state)
b.runner.Run(state)
// If there was an error, return that
if rawErr, ok := state.GetOk("error"); ok {
return nil, rawErr.(error)
}
// If there is no image, just return
if _, ok := state.GetOk("image"); !ok {
return nil, nil
}
artifact := &Artifact{
ImageID: state.Get("image").(string),
BuilderIDValue: BuilderId,
Driver: driver,
}
return artifact, nil
}
// Cancel cancels a possibly running Builder. This should block until
// the builder actually cancels and cleans up after itself.
func (b *Builder) Cancel() {
if b.runner != nil {
log.Println("Cancelling the step runner...")
b.runner.Cancel()
}
}

18
builder/triton/config.go Normal file
View File

@ -0,0 +1,18 @@
package triton
import (
"github.com/mitchellh/packer/common"
"github.com/mitchellh/packer/helper/communicator"
"github.com/mitchellh/packer/template/interpolate"
)
type Config struct {
common.PackerConfig `mapstructure:",squash"`
AccessConfig `mapstructure:",squash"`
SourceMachineConfig `mapstructure:",squash"`
TargetImageConfig `mapstructure:",squash"`
Comm communicator.Config `mapstructure:",squash"`
ctx interpolate.Context
}

View File

@ -0,0 +1,13 @@
package triton
import (
"testing"
)
func testConfig(t *testing.T) Config {
return Config{
AccessConfig: testAccessConfig(t),
SourceMachineConfig: testSourceMachineConfig(t),
TargetImageConfig: testTargetImageConfig(t),
}
}

17
builder/triton/driver.go Normal file
View File

@ -0,0 +1,17 @@
package triton
import (
"time"
)
type Driver interface {
CreateImageFromMachine(machineId string, config Config) (string, error)
CreateMachine(config Config) (string, error)
DeleteImage(imageId string) error
DeleteMachine(machineId string) error
GetMachine(machineId string) (string, error)
StopMachine(machineId string) error
WaitForImageCreation(imageId string, timeout time.Duration) error
WaitForMachineDeletion(machineId string, timeout time.Duration) error
WaitForMachineState(machineId string, state string, timeout time.Duration) error
}

View File

@ -0,0 +1,96 @@
package triton
import (
"time"
)
type DriverMock struct {
CreateImageFromMachineId string
CreateImageFromMachineErr error
CreateMachineId string
CreateMachineErr error
DeleteImageId string
DeleteImageErr error
DeleteMachineId string
DeleteMachineErr error
GetMachineErr error
StopMachineId string
StopMachineErr error
WaitForImageCreationErr error
WaitForMachineDeletionErr error
WaitForMachineStateErr error
}
func (d *DriverMock) CreateImageFromMachine(machineId string, config Config) (string, error) {
if d.CreateImageFromMachineErr != nil {
return "", d.CreateImageFromMachineErr
}
d.CreateImageFromMachineId = config.ImageName
return d.CreateImageFromMachineId, nil
}
func (d *DriverMock) CreateMachine(config Config) (string, error) {
if d.CreateMachineErr != nil {
return "", d.CreateMachineErr
}
d.CreateMachineId = config.MachineName
return d.CreateMachineId, nil
}
func (d *DriverMock) DeleteImage(imageId string) error {
if d.DeleteImageErr != nil {
return d.DeleteImageErr
}
d.DeleteImageId = imageId
return nil
}
func (d *DriverMock) DeleteMachine(machineId string) error {
if d.DeleteMachineErr != nil {
return d.DeleteMachineErr
}
d.DeleteMachineId = machineId
return nil
}
func (d *DriverMock) GetMachine(machineId string) (string, error) {
if d.GetMachineErr != nil {
return "", d.GetMachineErr
}
return "ip", nil
}
func (d *DriverMock) StopMachine(machineId string) error {
d.StopMachineId = machineId
return d.StopMachineErr
}
func (d *DriverMock) WaitForImageCreation(machineId string, timeout time.Duration) error {
return d.WaitForImageCreationErr
}
func (d *DriverMock) WaitForMachineDeletion(machineId string, timeout time.Duration) error {
return d.WaitForMachineDeletionErr
}
func (d *DriverMock) WaitForMachineState(machineId string, state string, timeout time.Duration) error {
return d.WaitForMachineStateErr
}

View File

@ -0,0 +1,171 @@
package triton
import (
"errors"
"strings"
"time"
"github.com/joyent/gosdc/cloudapi"
"github.com/mitchellh/packer/packer"
)
type driverTriton struct {
client *cloudapi.Client
ui packer.Ui
}
func NewDriverTriton(ui packer.Ui, config Config) (Driver, error) {
client, err := config.AccessConfig.CreateTritonClient()
if err != nil {
return nil, err
}
return &driverTriton{
client: client,
ui: ui,
}, nil
}
func (d *driverTriton) CreateImageFromMachine(machineId string, config Config) (string, error) {
opts := cloudapi.CreateImageFromMachineOpts{
Machine: machineId,
Name: config.ImageName,
Version: config.ImageVersion,
Description: config.ImageDescription,
Homepage: config.ImageHomepage,
EULA: config.ImageEULA,
ACL: config.ImageACL,
Tags: config.ImageTags,
}
image, err := d.client.CreateImageFromMachine(opts)
if err != nil {
return "", err
}
return image.Id, err
}
func (d *driverTriton) CreateMachine(config Config) (string, error) {
opts := cloudapi.CreateMachineOpts{
Package: config.MachinePackage,
Image: config.MachineImage,
Networks: config.MachineNetworks,
Metadata: config.MachineMetadata,
Tags: config.MachineTags,
FirewallEnabled: config.MachineFirewallEnabled,
}
if config.MachineName == "" {
// If not supplied generate a name for the source VM: "packer-builder-[image_name]".
// The version is not used because it can contain characters invalid for a VM name.
opts.Name = "packer-builder-" + config.ImageName
} else {
opts.Name = config.MachineName
}
machine, err := d.client.CreateMachine(opts)
if err != nil {
return "", err
}
return machine.Id, nil
}
func (d *driverTriton) DeleteImage(imageId string) error {
return d.client.DeleteImage(imageId)
}
func (d *driverTriton) DeleteMachine(machineId string) error {
return d.client.DeleteMachine(machineId)
}
func (d *driverTriton) GetMachine(machineId string) (string, error) {
machine, err := d.client.GetMachine(machineId)
if err != nil {
return "", err
}
return machine.PrimaryIP, nil
}
func (d *driverTriton) StopMachine(machineId string) error {
return d.client.StopMachine(machineId)
}
// waitForMachineState uses the supplied client to wait for the state of
// the machine with the given ID to reach the state described in state.
// If timeout is reached before the machine reaches the required state, an
// error is returned. If the machine reaches the target state within the
// timeout, nil is returned.
func (d *driverTriton) WaitForMachineState(machineId string, state string, timeout time.Duration) error {
return waitFor(
func() (bool, error) {
machine, err := d.client.GetMachine(machineId)
if machine == nil {
return false, err
}
return machine.State == state, err
},
3*time.Second,
timeout,
)
}
// waitForMachineDeletion uses the supplied client to wait for the machine
// with the given ID to be deleted. It is expected that the API call to delete
// the machine has already been issued at this point.
func (d *driverTriton) WaitForMachineDeletion(machineId string, timeout time.Duration) error {
return waitFor(
func() (bool, error) {
machine, err := d.client.GetMachine(machineId)
if err != nil {
//TODO(jen20): is there a better way here than searching strings?
if strings.Contains(err.Error(), "410") || strings.Contains(err.Error(), "404") {
return true, nil
}
}
if machine != nil {
return false, nil
}
return false, err
},
3*time.Second,
timeout,
)
}
func (d *driverTriton) WaitForImageCreation(imageId string, timeout time.Duration) error {
return waitFor(
func() (bool, error) {
image, err := d.client.GetImage(imageId)
if image == nil {
return false, err
}
return image.OS != "", err
},
3*time.Second,
timeout,
)
}
func waitFor(f func() (bool, error), every, timeout time.Duration) error {
start := time.Now()
for time.Since(start) <= timeout {
stop, err := f()
if err != nil {
return err
}
if stop {
return nil
}
time.Sleep(every)
}
return errors.New("Timed out while waiting for resource change")
}

View File

@ -0,0 +1,50 @@
package triton
import (
"fmt"
"github.com/mitchellh/packer/template/interpolate"
)
// SourceMachineConfig represents the configuration to run a machine using
// the SDC API in order for provisioning to take place.
type SourceMachineConfig struct {
MachineName string `mapstructure:"source_machine_name"`
MachinePackage string `mapstructure:"source_machine_package"`
MachineImage string `mapstructure:"source_machine_image"`
MachineNetworks []string `mapstructure:"source_machine_networks"`
MachineMetadata map[string]string `mapstructure:"source_machine_metadata"`
MachineTags map[string]string `mapstructure:"source_machine_tags"`
MachineFirewallEnabled bool `mapstructure:"source_machine_firewall_enabled"`
}
// Prepare performs basic validation on a SourceMachineConfig struct.
func (c *SourceMachineConfig) Prepare(ctx *interpolate.Context) []error {
var errs []error
if c.MachinePackage == "" {
errs = append(errs, fmt.Errorf("A source_machine_package must be specified"))
}
if c.MachineImage == "" {
errs = append(errs, fmt.Errorf("A source_machine_image must be specified"))
}
if c.MachineNetworks == nil {
c.MachineNetworks = []string{}
}
if c.MachineMetadata == nil {
c.MachineMetadata = make(map[string]string)
}
if c.MachineTags == nil {
c.MachineTags = make(map[string]string)
}
if len(errs) > 0 {
return errs
}
return nil
}

View File

@ -0,0 +1,57 @@
package triton
import (
"testing"
)
func TestSourceMachineConfig_Prepare(t *testing.T) {
sc := testSourceMachineConfig(t)
errs := sc.Prepare(nil)
if errs != nil {
t.Fatalf("should not error: %#v", sc)
}
sc = testSourceMachineConfig(t)
sc.MachineName = ""
errs = sc.Prepare(nil)
if errs != nil {
t.Fatalf("should not error: %#v", sc)
}
sc = testSourceMachineConfig(t)
sc.MachinePackage = ""
errs = sc.Prepare(nil)
if errs == nil {
t.Fatalf("should error: %#v", sc)
}
sc = testSourceMachineConfig(t)
sc.MachineImage = ""
errs = sc.Prepare(nil)
if errs == nil {
t.Fatalf("should error: %#v", sc)
}
}
func testSourceMachineConfig(t *testing.T) SourceMachineConfig {
return SourceMachineConfig{
MachineName: "test-machine",
MachinePackage: "test-package",
MachineImage: "test-image",
MachineNetworks: []string{
"test-network-1",
"test-network-2",
},
MachineMetadata: map[string]string{
"test-metadata-key1": "test-metadata-value1",
"test-metadata-key2": "test-metadata-value2",
"test-metadata-key3": "test-metadata-value3",
},
MachineTags: map[string]string{
"test-tags-key1": "test-tags-value1",
"test-tags-key2": "test-tags-value2",
"test-tags-key3": "test-tags-value3",
},
MachineFirewallEnabled: false,
}
}

88
builder/triton/ssh.go Normal file
View File

@ -0,0 +1,88 @@
package triton
import (
"fmt"
"github.com/mitchellh/multistep"
packerssh "github.com/mitchellh/packer/communicator/ssh"
"golang.org/x/crypto/ssh"
"golang.org/x/crypto/ssh/agent"
"io/ioutil"
"log"
"net"
"os"
)
func commHost(state multistep.StateBag) (string, error) {
driver := state.Get("driver").(Driver)
machineID := state.Get("machine").(string)
machine, err := driver.GetMachine(machineID)
if err != nil {
return "", err
}
return machine, nil
}
// SSHConfig returns a function that can be used for the SSH communicator
// config for connecting to the instance created over SSH using the private key
// or password.
func sshConfig(useAgent bool, username, privateKeyPath, password string) func(multistep.StateBag) (*ssh.ClientConfig, error) {
return func(state multistep.StateBag) (*ssh.ClientConfig, error) {
if useAgent {
log.Println("Configuring SSH agent.")
authSock := os.Getenv("SSH_AUTH_SOCK")
if authSock == "" {
return nil, fmt.Errorf("SSH_AUTH_SOCK is not set")
}
sshAgent, err := net.Dial("unix", authSock)
if err != nil {
return nil, fmt.Errorf("Cannot connect to SSH Agent socket %q: %s", authSock, err)
}
return &ssh.ClientConfig{
User: username,
Auth: []ssh.AuthMethod{
ssh.PublicKeysCallback(agent.NewClient(sshAgent).Signers),
},
}, nil
}
hasKey := privateKeyPath != ""
if hasKey {
log.Printf("Configuring SSH private key '%s'.", privateKeyPath)
privateKeyBytes, err := ioutil.ReadFile(privateKeyPath)
if err != nil {
return nil, fmt.Errorf("Unable to read SSH private key: %s", err)
}
signer, err := ssh.ParsePrivateKey(privateKeyBytes)
if err != nil {
return nil, fmt.Errorf("Error setting up SSH config: %s", err)
}
return &ssh.ClientConfig{
User: username,
Auth: []ssh.AuthMethod{
ssh.PublicKeys(signer),
},
}, nil
} else {
log.Println("Configuring SSH keyboard interactive.")
return &ssh.ClientConfig{
User: username,
Auth: []ssh.AuthMethod{
ssh.Password(password),
ssh.KeyboardInteractive(
packerssh.PasswordKeyboardInteractive(password)),
}}, nil
}
}
}

View File

@ -0,0 +1,45 @@
package triton
import (
"fmt"
"time"
"github.com/mitchellh/multistep"
"github.com/mitchellh/packer/packer"
)
// StepCreateImageFromMachine creates an image with the specified attributes
// from the machine with the given ID, and waits for the image to be created.
// The machine must be in the "stopped" state prior to this step being run.
type StepCreateImageFromMachine struct{}
func (s *StepCreateImageFromMachine) Run(state multistep.StateBag) multistep.StepAction {
config := state.Get("config").(Config)
driver := state.Get("driver").(Driver)
ui := state.Get("ui").(packer.Ui)
machineId := state.Get("machine").(string)
ui.Say("Creating image from source machine...")
imageId, err := driver.CreateImageFromMachine(machineId, config)
if err != nil {
state.Put("error", fmt.Errorf("Problem creating image from machine: %s", err))
return multistep.ActionHalt
}
ui.Say("Waiting for image to become available...")
err = driver.WaitForImageCreation(imageId, 10*time.Minute)
if err != nil {
state.Put("error", fmt.Errorf("Problem waiting for image to become available: %s", err))
return multistep.ActionHalt
}
state.Put("image", imageId)
return multistep.ActionContinue
}
func (s *StepCreateImageFromMachine) Cleanup(state multistep.StateBag) {
// No cleanup
}

View File

@ -0,0 +1,73 @@
package triton
import (
"errors"
"testing"
"github.com/mitchellh/multistep"
)
func TestStepCreateImageFromMachine(t *testing.T) {
state := testState(t)
step := new(StepCreateImageFromMachine)
defer step.Cleanup(state)
state.Put("machine", "test-machine-id")
if action := step.Run(state); action != multistep.ActionContinue {
t.Fatalf("bad action: %#v", action)
}
_, ok := state.GetOk("image")
if !ok {
t.Fatalf("should have image")
}
step.Cleanup(state)
}
func TestStepCreateImageFromMachine_CreateImageFromMachineError(t *testing.T) {
state := testState(t)
step := new(StepCreateImageFromMachine)
defer step.Cleanup(state)
driver := state.Get("driver").(*DriverMock)
state.Put("machine", "test-machine-id")
driver.CreateImageFromMachineErr = errors.New("error")
if action := step.Run(state); action != multistep.ActionHalt {
t.Fatalf("bad action: %#v", action)
}
if _, ok := state.GetOk("error"); !ok {
t.Fatalf("should have error")
}
if _, ok := state.GetOk("image"); ok {
t.Fatalf("should NOT have image")
}
}
func TestStepCreateImageFromMachine_WaitForImageCreationError(t *testing.T) {
state := testState(t)
step := new(StepCreateImageFromMachine)
defer step.Cleanup(state)
driver := state.Get("driver").(*DriverMock)
state.Put("machine", "test-machine-id")
driver.WaitForImageCreationErr = errors.New("error")
if action := step.Run(state); action != multistep.ActionHalt {
t.Fatalf("bad action: %#v", action)
}
if _, ok := state.GetOk("error"); !ok {
t.Fatalf("should have error")
}
if _, ok := state.GetOk("image"); ok {
t.Fatalf("should NOT have image")
}
}

View File

@ -0,0 +1,68 @@
package triton
import (
"fmt"
"time"
"github.com/mitchellh/multistep"
"github.com/mitchellh/packer/packer"
)
// StepCreateSourceMachine creates an machine with the specified attributes
// and waits for it to become available for provisioners.
type StepCreateSourceMachine struct{}
func (s *StepCreateSourceMachine) Run(state multistep.StateBag) multistep.StepAction {
config := state.Get("config").(Config)
driver := state.Get("driver").(Driver)
ui := state.Get("ui").(packer.Ui)
ui.Say("Creating source machine...")
machineId, err := driver.CreateMachine(config)
if err != nil {
state.Put("error", fmt.Errorf("Problem creating source machine: %s", err))
return multistep.ActionHalt
}
ui.Say("Waiting for source machine to become available...")
err = driver.WaitForMachineState(machineId, "running", 10*time.Minute)
if err != nil {
state.Put("error", fmt.Errorf("Problem waiting for source machine to become available: %s", err))
return multistep.ActionHalt
}
state.Put("machine", machineId)
return multistep.ActionContinue
}
func (s *StepCreateSourceMachine) Cleanup(state multistep.StateBag) {
driver := state.Get("driver").(Driver)
ui := state.Get("ui").(packer.Ui)
machineIdRaw, ok := state.GetOk("machine")
if ok && machineIdRaw.(string) != "" {
machineId := machineIdRaw.(string)
ui.Say(fmt.Sprintf("Stopping source machine (%s)...", machineId))
err := driver.StopMachine(machineId)
if err != nil {
state.Put("error", fmt.Errorf("Problem stopping source machine: %s", err))
return
}
ui.Say(fmt.Sprintf("Waiting for source machine to stop (%s)...", machineId))
err = driver.WaitForMachineState(machineId, "stopped", 10*time.Minute)
if err != nil {
state.Put("error", fmt.Errorf("Problem waiting for source machine to stop: %s", err))
return
}
ui.Say(fmt.Sprintf("Deleting source machine (%s)...", machineId))
err = driver.DeleteMachine(machineId)
if err != nil {
state.Put("error", fmt.Errorf("Problem deleting source machine: %s", err))
return
}
}
}

View File

@ -0,0 +1,159 @@
package triton
import (
"errors"
"testing"
"github.com/mitchellh/multistep"
)
func TestStepCreateSourceMachine(t *testing.T) {
state := testState(t)
step := new(StepCreateSourceMachine)
defer step.Cleanup(state)
driver := state.Get("driver").(*DriverMock)
if action := step.Run(state); action != multistep.ActionContinue {
t.Fatalf("bad action: %#v", action)
}
machineIdRaw, ok := state.GetOk("machine")
if !ok {
t.Fatalf("should have machine")
}
step.Cleanup(state)
if driver.DeleteMachineId != machineIdRaw.(string) {
t.Fatalf("should've deleted machine (%s != %s)", driver.DeleteMachineId, machineIdRaw.(string))
}
}
func TestStepCreateSourceMachine_CreateMachineError(t *testing.T) {
state := testState(t)
step := new(StepCreateSourceMachine)
defer step.Cleanup(state)
driver := state.Get("driver").(*DriverMock)
driver.CreateMachineErr = errors.New("error")
if action := step.Run(state); action != multistep.ActionHalt {
t.Fatalf("bad action: %#v", action)
}
if _, ok := state.GetOk("error"); !ok {
t.Fatalf("should have error")
}
if _, ok := state.GetOk("machine"); ok {
t.Fatalf("should NOT have machine")
}
}
func TestStepCreateSourceMachine_WaitForMachineStateError(t *testing.T) {
state := testState(t)
step := new(StepCreateSourceMachine)
defer step.Cleanup(state)
driver := state.Get("driver").(*DriverMock)
driver.WaitForMachineStateErr = errors.New("error")
if action := step.Run(state); action != multistep.ActionHalt {
t.Fatalf("bad action: %#v", action)
}
if _, ok := state.GetOk("error"); !ok {
t.Fatalf("should have error")
}
if _, ok := state.GetOk("machine"); ok {
t.Fatalf("should NOT have machine")
}
}
func TestStepCreateSourceMachine_StopMachineError(t *testing.T) {
state := testState(t)
step := new(StepCreateSourceMachine)
defer step.Cleanup(state)
driver := state.Get("driver").(*DriverMock)
if action := step.Run(state); action != multistep.ActionContinue {
t.Fatalf("bad action: %#v", action)
}
_, ok := state.GetOk("machine")
if !ok {
t.Fatalf("should have machine")
}
driver.StopMachineErr = errors.New("error")
step.Cleanup(state)
if _, ok := state.GetOk("error"); !ok {
t.Fatalf("should have error")
}
if _, ok := state.GetOk("machine"); !ok {
t.Fatalf("should have machine")
}
}
func TestStepCreateSourceMachine_WaitForMachineStoppedError(t *testing.T) {
state := testState(t)
step := new(StepCreateSourceMachine)
defer step.Cleanup(state)
driver := state.Get("driver").(*DriverMock)
if action := step.Run(state); action != multistep.ActionContinue {
t.Fatalf("bad action: %#v", action)
}
_, ok := state.GetOk("machine")
if !ok {
t.Fatalf("should have machine")
}
driver.WaitForMachineStateErr = errors.New("error")
step.Cleanup(state)
if _, ok := state.GetOk("error"); !ok {
t.Fatalf("should have error")
}
if _, ok := state.GetOk("machine"); !ok {
t.Fatalf("should have machine")
}
}
func TestStepCreateSourceMachine_DeleteMachineError(t *testing.T) {
state := testState(t)
step := new(StepCreateSourceMachine)
defer step.Cleanup(state)
driver := state.Get("driver").(*DriverMock)
if action := step.Run(state); action != multistep.ActionContinue {
t.Fatalf("bad action: %#v", action)
}
_, ok := state.GetOk("machine")
if !ok {
t.Fatalf("should have machine")
}
driver.DeleteMachineErr = errors.New("error")
step.Cleanup(state)
if _, ok := state.GetOk("error"); !ok {
t.Fatalf("should have error")
}
if _, ok := state.GetOk("machine"); !ok {
t.Fatalf("should have machine")
}
}

View File

@ -0,0 +1,41 @@
package triton
import (
"fmt"
"time"
"github.com/mitchellh/multistep"
"github.com/mitchellh/packer/packer"
)
// StepDeleteMachine deletes the machine with the ID specified in state["machine"]
type StepDeleteMachine struct{}
func (s *StepDeleteMachine) Run(state multistep.StateBag) multistep.StepAction {
driver := state.Get("driver").(Driver)
ui := state.Get("ui").(packer.Ui)
machineId := state.Get("machine").(string)
ui.Say("Deleting source machine...")
err := driver.DeleteMachine(machineId)
if err != nil {
state.Put("error", fmt.Errorf("Problem deleting source machine: %s", err))
return multistep.ActionHalt
}
ui.Say("Waiting for source machine to be deleted...")
err = driver.WaitForMachineDeletion(machineId, 10*time.Minute)
if err != nil {
state.Put("error", fmt.Errorf("Problem waiting for source machine to be deleted: %s", err))
return multistep.ActionHalt
}
state.Put("machine", "")
return multistep.ActionContinue
}
func (s *StepDeleteMachine) Cleanup(state multistep.StateBag) {
// No clean up to do here...
}

View File

@ -0,0 +1,79 @@
package triton
import (
"errors"
"testing"
"github.com/mitchellh/multistep"
)
func TestStepDeleteMachine(t *testing.T) {
state := testState(t)
step := new(StepDeleteMachine)
defer step.Cleanup(state)
driver := state.Get("driver").(*DriverMock)
machineId := "test-machine-id"
state.Put("machine", machineId)
if action := step.Run(state); action != multistep.ActionContinue {
t.Fatalf("bad action: %#v", action)
}
step.Cleanup(state)
if driver.DeleteMachineId != machineId {
t.Fatalf("should've deleted machine (%s != %s)", driver.DeleteMachineId, machineId)
}
}
func TestStepDeleteMachine_DeleteMachineError(t *testing.T) {
state := testState(t)
step := new(StepDeleteMachine)
defer step.Cleanup(state)
driver := state.Get("driver").(*DriverMock)
machineId := "test-machine-id"
state.Put("machine", machineId)
driver.DeleteMachineErr = errors.New("error")
if action := step.Run(state); action != multistep.ActionHalt {
t.Fatalf("bad action: %#v", action)
}
if _, ok := state.GetOk("error"); !ok {
t.Fatalf("should have error")
}
if _, ok := state.GetOk("machine"); !ok {
t.Fatalf("should have machine")
}
}
func TestStepDeleteMachine_WaitForMachineDeletionError(t *testing.T) {
state := testState(t)
step := new(StepDeleteMachine)
defer step.Cleanup(state)
driver := state.Get("driver").(*DriverMock)
machineId := "test-machine-id"
state.Put("machine", machineId)
driver.WaitForMachineDeletionErr = errors.New("error")
if action := step.Run(state); action != multistep.ActionHalt {
t.Fatalf("bad action: %#v", action)
}
if _, ok := state.GetOk("error"); !ok {
t.Fatalf("should have error")
}
if _, ok := state.GetOk("machine"); !ok {
t.Fatalf("should have machine")
}
}

View File

@ -0,0 +1,41 @@
package triton
import (
"fmt"
"time"
"github.com/mitchellh/multistep"
"github.com/mitchellh/packer/packer"
)
// StepStopMachine stops the machine with the given Machine ID, and waits
// for it to reach the stopped state.
type StepStopMachine struct{}
func (s *StepStopMachine) Run(state multistep.StateBag) multistep.StepAction {
driver := state.Get("driver").(Driver)
ui := state.Get("ui").(packer.Ui)
machineId := state.Get("machine").(string)
ui.Say(fmt.Sprintf("Stopping source machine (%s)...", machineId))
err := driver.StopMachine(machineId)
if err != nil {
state.Put("error", fmt.Errorf("Problem stopping source machine: %s", err))
return multistep.ActionHalt
}
ui.Say(fmt.Sprintf("Waiting for source machine to stop (%s)...", machineId))
err = driver.WaitForMachineState(machineId, "stopped", 10*time.Minute)
if err != nil {
state.Put("error", fmt.Errorf("Problem waiting for source machine to stop: %s", err))
return multistep.ActionHalt
}
return multistep.ActionContinue
}
func (s *StepStopMachine) Cleanup(state multistep.StateBag) {
// Explicitly don't clean up here as StepCreateSourceMachine will do it if necessary
// and there is no real meaning to cleaning this up.
}

View File

@ -0,0 +1,71 @@
package triton
import (
"errors"
"testing"
"github.com/mitchellh/multistep"
)
func TestStepStopMachine(t *testing.T) {
state := testState(t)
step := new(StepStopMachine)
defer step.Cleanup(state)
driver := state.Get("driver").(*DriverMock)
machineId := "test-machine-id"
state.Put("machine", machineId)
if action := step.Run(state); action != multistep.ActionContinue {
t.Fatalf("bad action: %#v", action)
}
step.Cleanup(state)
if driver.StopMachineId != machineId {
t.Fatalf("should've stopped machine (%s != %s)", driver.StopMachineId, machineId)
}
}
func TestStepStopMachine_StopMachineError(t *testing.T) {
state := testState(t)
step := new(StepStopMachine)
defer step.Cleanup(state)
driver := state.Get("driver").(*DriverMock)
machineId := "test-machine-id"
state.Put("machine", machineId)
driver.StopMachineErr = errors.New("error")
if action := step.Run(state); action != multistep.ActionHalt {
t.Fatalf("bad action: %#v", action)
}
if _, ok := state.GetOk("error"); !ok {
t.Fatalf("should have error")
}
}
func TestStepStopMachine_WaitForMachineStoppedError(t *testing.T) {
state := testState(t)
step := new(StepStopMachine)
defer step.Cleanup(state)
driver := state.Get("driver").(*DriverMock)
machineId := "test-machine-id"
state.Put("machine", machineId)
driver.WaitForMachineStateErr = errors.New("error")
if action := step.Run(state); action != multistep.ActionHalt {
t.Fatalf("bad action: %#v", action)
}
if _, ok := state.GetOk("error"); !ok {
t.Fatalf("should have error")
}
}

View File

@ -0,0 +1,20 @@
package triton
import (
"bytes"
"github.com/mitchellh/multistep"
"github.com/mitchellh/packer/packer"
"testing"
)
func testState(t *testing.T) multistep.StateBag {
state := new(multistep.BasicStateBag)
state.Put("config", testConfig(t))
state.Put("driver", &DriverMock{})
state.Put("hook", &packer.MockHook{})
state.Put("ui", &packer.BasicUi{
Reader: new(bytes.Buffer),
Writer: new(bytes.Buffer),
})
return state
}

View File

@ -0,0 +1,24 @@
package triton
import (
"time"
"github.com/mitchellh/multistep"
"github.com/mitchellh/packer/packer"
)
// StepWaitForStopNotToFail waits for 10 seconds before returning with continue
// in order to prevent an observed issue where machines stopped immediately after
// they are started never actually stop.
type StepWaitForStopNotToFail struct{}
func (s *StepWaitForStopNotToFail) Run(state multistep.StateBag) multistep.StepAction {
ui := state.Get("ui").(packer.Ui)
ui.Say("Waiting 10 seconds to avoid potential SDC bug...")
time.Sleep(10 * time.Second)
return multistep.ActionContinue
}
func (s *StepWaitForStopNotToFail) Cleanup(state multistep.StateBag) {
// No clean up required...
}

View File

@ -0,0 +1,38 @@
package triton
import (
"fmt"
"github.com/mitchellh/packer/template/interpolate"
)
// TargetImageConfig represents the configuration for the image to be created
// from the source machine.
type TargetImageConfig struct {
ImageName string `mapstructure:"image_name"`
ImageVersion string `mapstructure:"image_version"`
ImageDescription string `mapstructure:"image_description"`
ImageHomepage string `mapstructure:"image_homepage"`
ImageEULA string `mapstructure:"image_eula_url"`
ImageACL []string `mapstructure:"image_acls"`
ImageTags map[string]string `mapstructure:"image_tags"`
}
// Prepare performs basic validation on a TargetImageConfig struct.
func (c *TargetImageConfig) Prepare(ctx *interpolate.Context) []error {
var errs []error
if c.ImageName == "" {
errs = append(errs, fmt.Errorf("An image_name must be specified"))
}
if c.ImageVersion == "" {
errs = append(errs, fmt.Errorf("An image_version must be specified"))
}
if len(errs) > 0 {
return errs
}
return nil
}

View File

@ -0,0 +1,46 @@
package triton
import (
"testing"
)
func TestTargetImageConfig_Prepare(t *testing.T) {
tic := testTargetImageConfig(t)
errs := tic.Prepare(nil)
if errs != nil {
t.Fatalf("should not error: %#v", tic)
}
tic = testTargetImageConfig(t)
tic.ImageName = ""
errs = tic.Prepare(nil)
if errs == nil {
t.Fatalf("should error: %#v", tic)
}
tic = testTargetImageConfig(t)
tic.ImageVersion = ""
errs = tic.Prepare(nil)
if errs == nil {
t.Fatalf("should error: %#v", tic)
}
}
func testTargetImageConfig(t *testing.T) TargetImageConfig {
return TargetImageConfig{
ImageName: "test-image",
ImageVersion: "test-version",
ImageDescription: "test-description",
ImageHomepage: "test-homepage",
ImageEULA: "test-eula",
ImageACL: []string{
"test-acl-1",
"test-acl-2",
},
ImageTags: map[string]string{
"test-tags-key1": "test-tags-value1",
"test-tags-key2": "test-tags-value2",
"test-tags-key3": "test-tags-value3",
},
}
}

View File

@ -31,6 +31,7 @@ import (
parallelspvmbuilder "github.com/mitchellh/packer/builder/parallels/pvm"
profitbricksbuilder "github.com/mitchellh/packer/builder/profitbricks"
qemubuilder "github.com/mitchellh/packer/builder/qemu"
tritonbuilder "github.com/mitchellh/packer/builder/triton"
virtualboxisobuilder "github.com/mitchellh/packer/builder/virtualbox/iso"
virtualboxovfbuilder "github.com/mitchellh/packer/builder/virtualbox/ovf"
vmwareisobuilder "github.com/mitchellh/packer/builder/vmware/iso"
@ -90,6 +91,7 @@ var Builders = map[string]packer.Builder{
"parallels-pvm": new(parallelspvmbuilder.Builder),
"profitbricks": new(profitbricksbuilder.Builder),
"qemu": new(qemubuilder.Builder),
"triton": new(tritonbuilder.Builder),
"virtualbox-iso": new(virtualboxisobuilder.Builder),
"virtualbox-ovf": new(virtualboxovfbuilder.Builder),
"vmware-iso": new(vmwareisobuilder.Builder),

373
vendor/github.com/joyent/gocommon/LICENSE generated vendored Normal file
View File

@ -0,0 +1,373 @@
Mozilla Public License Version 2.0
==================================
1. Definitions
--------------
1.1. "Contributor"
means each individual or legal entity that creates, contributes to
the creation of, or owns Covered Software.
1.2. "Contributor Version"
means the combination of the Contributions of others (if any) used
by a Contributor and that particular Contributor's Contribution.
1.3. "Contribution"
means Covered Software of a particular Contributor.
1.4. "Covered Software"
means Source Code Form to which the initial Contributor has attached
the notice in Exhibit A, the Executable Form of such Source Code
Form, and Modifications of such Source Code Form, in each case
including portions thereof.
1.5. "Incompatible With Secondary Licenses"
means
(a) that the initial Contributor has attached the notice described
in Exhibit B to the Covered Software; or
(b) that the Covered Software was made available under the terms of
version 1.1 or earlier of the License, but not also under the
terms of a Secondary License.
1.6. "Executable Form"
means any form of the work other than Source Code Form.
1.7. "Larger Work"
means a work that combines Covered Software with other material, in
a separate file or files, that is not Covered Software.
1.8. "License"
means this document.
1.9. "Licensable"
means having the right to grant, to the maximum extent possible,
whether at the time of the initial grant or subsequently, any and
all of the rights conveyed by this License.
1.10. "Modifications"
means any of the following:
(a) any file in Source Code Form that results from an addition to,
deletion from, or modification of the contents of Covered
Software; or
(b) any new file in Source Code Form that contains any Covered
Software.
1.11. "Patent Claims" of a Contributor
means any patent claim(s), including without limitation, method,
process, and apparatus claims, in any patent Licensable by such
Contributor that would be infringed, but for the grant of the
License, by the making, using, selling, offering for sale, having
made, import, or transfer of either its Contributions or its
Contributor Version.
1.12. "Secondary License"
means either the GNU General Public License, Version 2.0, the GNU
Lesser General Public License, Version 2.1, the GNU Affero General
Public License, Version 3.0, or any later versions of those
licenses.
1.13. "Source Code Form"
means the form of the work preferred for making modifications.
1.14. "You" (or "Your")
means an individual or a legal entity exercising rights under this
License. For legal entities, "You" includes any entity that
controls, is controlled by, or is under common control with You. For
purposes of this definition, "control" means (a) the power, direct
or indirect, to cause the direction or management of such entity,
whether by contract or otherwise, or (b) ownership of more than
fifty percent (50%) of the outstanding shares or beneficial
ownership of such entity.
2. License Grants and Conditions
--------------------------------
2.1. Grants
Each Contributor hereby grants You a world-wide, royalty-free,
non-exclusive license:
(a) under intellectual property rights (other than patent or trademark)
Licensable by such Contributor to use, reproduce, make available,
modify, display, perform, distribute, and otherwise exploit its
Contributions, either on an unmodified basis, with Modifications, or
as part of a Larger Work; and
(b) under Patent Claims of such Contributor to make, use, sell, offer
for sale, have made, import, and otherwise transfer either its
Contributions or its Contributor Version.
2.2. Effective Date
The licenses granted in Section 2.1 with respect to any Contribution
become effective for each Contribution on the date the Contributor first
distributes such Contribution.
2.3. Limitations on Grant Scope
The licenses granted in this Section 2 are the only rights granted under
this License. No additional rights or licenses will be implied from the
distribution or licensing of Covered Software under this License.
Notwithstanding Section 2.1(b) above, no patent license is granted by a
Contributor:
(a) for any code that a Contributor has removed from Covered Software;
or
(b) for infringements caused by: (i) Your and any other third party's
modifications of Covered Software, or (ii) the combination of its
Contributions with other software (except as part of its Contributor
Version); or
(c) under Patent Claims infringed by Covered Software in the absence of
its Contributions.
This License does not grant any rights in the trademarks, service marks,
or logos of any Contributor (except as may be necessary to comply with
the notice requirements in Section 3.4).
2.4. Subsequent Licenses
No Contributor makes additional grants as a result of Your choice to
distribute the Covered Software under a subsequent version of this
License (see Section 10.2) or under the terms of a Secondary License (if
permitted under the terms of Section 3.3).
2.5. Representation
Each Contributor represents that the Contributor believes its
Contributions are its original creation(s) or it has sufficient rights
to grant the rights to its Contributions conveyed by this License.
2.6. Fair Use
This License is not intended to limit any rights You have under
applicable copyright doctrines of fair use, fair dealing, or other
equivalents.
2.7. Conditions
Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted
in Section 2.1.
3. Responsibilities
-------------------
3.1. Distribution of Source Form
All distribution of Covered Software in Source Code Form, including any
Modifications that You create or to which You contribute, must be under
the terms of this License. You must inform recipients that the Source
Code Form of the Covered Software is governed by the terms of this
License, and how they can obtain a copy of this License. You may not
attempt to alter or restrict the recipients' rights in the Source Code
Form.
3.2. Distribution of Executable Form
If You distribute Covered Software in Executable Form then:
(a) such Covered Software must also be made available in Source Code
Form, as described in Section 3.1, and You must inform recipients of
the Executable Form how they can obtain a copy of such Source Code
Form by reasonable means in a timely manner, at a charge no more
than the cost of distribution to the recipient; and
(b) You may distribute such Executable Form under the terms of this
License, or sublicense it under different terms, provided that the
license for the Executable Form does not attempt to limit or alter
the recipients' rights in the Source Code Form under this License.
3.3. Distribution of a Larger Work
You may create and distribute a Larger Work under terms of Your choice,
provided that You also comply with the requirements of this License for
the Covered Software. If the Larger Work is a combination of Covered
Software with a work governed by one or more Secondary Licenses, and the
Covered Software is not Incompatible With Secondary Licenses, this
License permits You to additionally distribute such Covered Software
under the terms of such Secondary License(s), so that the recipient of
the Larger Work may, at their option, further distribute the Covered
Software under the terms of either this License or such Secondary
License(s).
3.4. Notices
You may not remove or alter the substance of any license notices
(including copyright notices, patent notices, disclaimers of warranty,
or limitations of liability) contained within the Source Code Form of
the Covered Software, except that You may alter any license notices to
the extent required to remedy known factual inaccuracies.
3.5. Application of Additional Terms
You may choose to offer, and to charge a fee for, warranty, support,
indemnity or liability obligations to one or more recipients of Covered
Software. However, You may do so only on Your own behalf, and not on
behalf of any Contributor. You must make it absolutely clear that any
such warranty, support, indemnity, or liability obligation is offered by
You alone, and You hereby agree to indemnify every Contributor for any
liability incurred by such Contributor as a result of warranty, support,
indemnity or liability terms You offer. You may include additional
disclaimers of warranty and limitations of liability specific to any
jurisdiction.
4. Inability to Comply Due to Statute or Regulation
---------------------------------------------------
If it is impossible for You to comply with any of the terms of this
License with respect to some or all of the Covered Software due to
statute, judicial order, or regulation then You must: (a) comply with
the terms of this License to the maximum extent possible; and (b)
describe the limitations and the code they affect. Such description must
be placed in a text file included with all distributions of the Covered
Software under this License. Except to the extent prohibited by statute
or regulation, such description must be sufficiently detailed for a
recipient of ordinary skill to be able to understand it.
5. Termination
--------------
5.1. The rights granted under this License will terminate automatically
if You fail to comply with any of its terms. However, if You become
compliant, then the rights granted under this License from a particular
Contributor are reinstated (a) provisionally, unless and until such
Contributor explicitly and finally terminates Your grants, and (b) on an
ongoing basis, if such Contributor fails to notify You of the
non-compliance by some reasonable means prior to 60 days after You have
come back into compliance. Moreover, Your grants from a particular
Contributor are reinstated on an ongoing basis if such Contributor
notifies You of the non-compliance by some reasonable means, this is the
first time You have received notice of non-compliance with this License
from such Contributor, and You become compliant prior to 30 days after
Your receipt of the notice.
5.2. If You initiate litigation against any entity by asserting a patent
infringement claim (excluding declaratory judgment actions,
counter-claims, and cross-claims) alleging that a Contributor Version
directly or indirectly infringes any patent, then the rights granted to
You by any and all Contributors for the Covered Software under Section
2.1 of this License shall terminate.
5.3. In the event of termination under Sections 5.1 or 5.2 above, all
end user license agreements (excluding distributors and resellers) which
have been validly granted by You or Your distributors under this License
prior to termination shall survive termination.
************************************************************************
* *
* 6. Disclaimer of Warranty *
* ------------------------- *
* *
* Covered Software is provided under this License on an "as is" *
* basis, without warranty of any kind, either expressed, implied, or *
* statutory, including, without limitation, warranties that the *
* Covered Software is free of defects, merchantable, fit for a *
* particular purpose or non-infringing. The entire risk as to the *
* quality and performance of the Covered Software is with You. *
* Should any Covered Software prove defective in any respect, You *
* (not any Contributor) assume the cost of any necessary servicing, *
* repair, or correction. This disclaimer of warranty constitutes an *
* essential part of this License. No use of any Covered Software is *
* authorized under this License except under this disclaimer. *
* *
************************************************************************
************************************************************************
* *
* 7. Limitation of Liability *
* -------------------------- *
* *
* Under no circumstances and under no legal theory, whether tort *
* (including negligence), contract, or otherwise, shall any *
* Contributor, or anyone who distributes Covered Software as *
* permitted above, be liable to You for any direct, indirect, *
* special, incidental, or consequential damages of any character *
* including, without limitation, damages for lost profits, loss of *
* goodwill, work stoppage, computer failure or malfunction, or any *
* and all other commercial damages or losses, even if such party *
* shall have been informed of the possibility of such damages. This *
* limitation of liability shall not apply to liability for death or *
* personal injury resulting from such party's negligence to the *
* extent applicable law prohibits such limitation. Some *
* jurisdictions do not allow the exclusion or limitation of *
* incidental or consequential damages, so this exclusion and *
* limitation may not apply to You. *
* *
************************************************************************
8. Litigation
-------------
Any litigation relating to this License may be brought only in the
courts of a jurisdiction where the defendant maintains its principal
place of business and such litigation shall be governed by laws of that
jurisdiction, without reference to its conflict-of-law provisions.
Nothing in this Section shall prevent a party's ability to bring
cross-claims or counter-claims.
9. Miscellaneous
----------------
This License represents the complete agreement concerning the subject
matter hereof. If any provision of this License is held to be
unenforceable, such provision shall be reformed only to the extent
necessary to make it enforceable. Any law or regulation which provides
that the language of a contract shall be construed against the drafter
shall not be used to construe this License against a Contributor.
10. Versions of the License
---------------------------
10.1. New Versions
Mozilla Foundation is the license steward. Except as provided in Section
10.3, no one other than the license steward has the right to modify or
publish new versions of this License. Each version will be given a
distinguishing version number.
10.2. Effect of New Versions
You may distribute the Covered Software under the terms of the version
of the License under which You originally received the Covered Software,
or under the terms of any subsequent version published by the license
steward.
10.3. Modified Versions
If you create software not governed by this License, and you want to
create a new license for such software, you may create and use a
modified version of this License if you rename the license and remove
any references to the name of the license steward (except to note that
such modified license differs from this License).
10.4. Distributing Source Code Form that is Incompatible With Secondary
Licenses
If You choose to distribute Source Code Form that is Incompatible With
Secondary Licenses under the terms of this version of the License, the
notice described in Exhibit B of this License must be attached.
Exhibit A - Source Code Form License Notice
-------------------------------------------
This Source Code Form is subject to the terms of the Mozilla Public
License, v. 2.0. If a copy of the MPL was not distributed with this
file, You can obtain one at http://mozilla.org/MPL/2.0/.
If it is not possible or desirable to put the notice in a particular
file, then You may include the notice in a location (such as a LICENSE
file in a relevant directory) where a recipient would be likely to look
for such a notice.
You may add additional accurate notices of copyright ownership.
Exhibit B - "Incompatible With Secondary Licenses" Notice
---------------------------------------------------------
This Source Code Form is "Incompatible With Secondary Licenses", as
defined by the Mozilla Public License, v. 2.0.

98
vendor/github.com/joyent/gocommon/README.md generated vendored Normal file
View File

@ -0,0 +1,98 @@
gocommon
========
Common Go library for Joyent's Triton and Manta.
[![wercker status](https://app.wercker.com/status/2f63bf7f68bfdd46b979abad19c0bee0/s/master "wercker status")](https://app.wercker.com/project/byKey/2f63bf7f68bfdd46b979abad19c0bee0)
## Installation
Use `go-get` to install gocommon.
```
go get github.com/joyent/gocommon
```
## Documentation
Auto-generated documentation can be found on godoc.
- [github.com/joyent/gocommon](http://godoc.org/github.com/joyent/gocommon)
- [github.com/joyent/gocommon/client](http://godoc.org/github.com/joyent/client)
- [github.com/joyent/gocommon/errors](http://godoc.org/github.com/joyent/gocommon/errors)
- [github.com/joyent/gocommon/http](http://godoc.org/github.com/joyent/gocommon/http)
- [github.com/joyent/gocommon/jpc](http://godoc.org/github.com/joyent/gocommon/jpc)
- [github.com/joyent/gocommon/testing](http://godoc.org/github.com/joyent/gocommon/testing)
## Contributing
Report bugs and request features using [GitHub Issues](https://github.com/joyent/gocommon/issues), or contribute code via a [GitHub Pull Request](https://github.com/joyent/gocommon/pulls). Changes will be code reviewed before merging. In the near future, automated tests will be run, but in the meantime please `go fmt`, `go lint`, and test all contributions.
## Developing
This library assumes a Go development environment setup based on [How to Write Go Code](https://golang.org/doc/code.html). Your GOPATH environment variable should be pointed at your workspace directory.
You can now use `go get github.com/joyent/gocommon` to install the repository to the correct location, but if you are intending on contributing back a change you may want to consider cloning the repository via git yourself. This way you can have a single source tree for all Joyent Go projects with each repo having two remotes -- your own fork on GitHub and the upstream origin.
For example if your GOPATH is `~/src/joyent/go` and you're working on multiple repos then that directory tree might look like:
```
~/src/joyent/go/
|_ pkg/
|_ src/
|_ github.com
|_ joyent
|_ gocommon
|_ gomanta
|_ gosdc
|_ gosign
```
### Recommended Setup
```
$ mkdir -p ${GOPATH}/src/github.com/joyent
$ cd ${GOPATH}/src/github.com/joyent
$ git clone git@github.com:<yourname>/gocommon.git
# fetch dependencies
$ git clone git@github.com:<yourname>/gosign.git
$ go get -v -t ./...
# add upstream remote
$ cd gocommon
$ git remote add upstream git@github.com:joyent/gocommon.git
$ git remote -v
origin git@github.com:<yourname>/gocommon.git (fetch)
origin git@github.com:<yourname>/gocommon.git (push)
upstream git@github.com:joyent/gocommon.git (fetch)
upstream git@github.com:joyent/gocommon.git (push)
```
### Run Tests
The library needs values for the `SDC_URL`, `MANTA_URL`, `MANTA_KEY_ID` and `SDC_KEY_ID` environment variables even though the tests are run locally. You can generate a temporary key and use its fingerprint for tests without adding the key to your Triton Cloud account.
```
# create a temporary key
ssh-keygen -b 2048 -C "Testing Key" -f /tmp/id_rsa -t rsa -P ""
# set up environment
# note: leave the -E md5 argument off on older ssh-keygen
export KEY_ID=$(ssh-keygen -E md5 -lf /tmp/id_rsa | awk -F' ' '{print $2}' | cut -d':' -f2-)
export SDC_KEY_ID=${KEY_ID}
export MANTA_KEY_ID=${KEY_ID}
export SDC_URL=https://us-east-1.api.joyent.com
export MANTA_URL=https://us-east.manta.joyent.com
cd ${GOPATH}/src/github.com/joyent/gocommon
go test ./...
```
### Build the Library
```
cd ${GOPATH}/src/github.com/joyent/gocommon
go build ./...
```

110
vendor/github.com/joyent/gocommon/client/client.go generated vendored Normal file
View File

@ -0,0 +1,110 @@
//
// gocommon - Go library to interact with the JoyentCloud
//
//
// Copyright (c) 2013 Joyent Inc.
//
// Written by Daniele Stroppa <daniele.stroppa@joyent.com>
//
package client
import (
"fmt"
"log"
"net/url"
"strings"
"sync"
"time"
joyenthttp "github.com/joyent/gocommon/http"
"github.com/joyent/gosign/auth"
)
const (
// The HTTP request methods.
GET = "GET"
POST = "POST"
PUT = "PUT"
DELETE = "DELETE"
HEAD = "HEAD"
COPY = "COPY"
)
// Client implementations sends service requests to the JoyentCloud.
type Client interface {
SendRequest(method, apiCall, rfc1123Date string, request *joyenthttp.RequestData, response *joyenthttp.ResponseData) (err error)
// MakeServiceURL prepares a full URL to a service endpoint, with optional
// URL parts. It uses the first endpoint it can find for the given service type.
MakeServiceURL(parts []string) string
SignURL(path string, expires time.Time) (string, error)
}
// This client sends requests without authenticating.
type client struct {
mu sync.Mutex
logger *log.Logger
baseURL string
creds *auth.Credentials
httpClient *joyenthttp.Client
}
var _ Client = (*client)(nil)
func newClient(baseURL string, credentials *auth.Credentials, httpClient *joyenthttp.Client, logger *log.Logger) Client {
client := client{baseURL: baseURL, logger: logger, creds: credentials, httpClient: httpClient}
return &client
}
func NewClient(baseURL, apiVersion string, credentials *auth.Credentials, logger *log.Logger) Client {
sharedHttpClient := joyenthttp.New(credentials, apiVersion, logger)
return newClient(baseURL, credentials, sharedHttpClient, logger)
}
func (c *client) sendRequest(method, url, rfc1123Date string, request *joyenthttp.RequestData, response *joyenthttp.ResponseData) (err error) {
if request.ReqValue != nil || response.RespValue != nil {
err = c.httpClient.JsonRequest(method, url, rfc1123Date, request, response)
} else {
err = c.httpClient.BinaryRequest(method, url, rfc1123Date, request, response)
}
return
}
func (c *client) SendRequest(method, apiCall, rfc1123Date string, request *joyenthttp.RequestData, response *joyenthttp.ResponseData) (err error) {
url := c.MakeServiceURL([]string{c.creds.UserAuthentication.User, apiCall})
err = c.sendRequest(method, url, rfc1123Date, request, response)
return
}
func makeURL(base string, parts []string) string {
if !strings.HasSuffix(base, "/") && len(parts) > 0 {
base += "/"
}
if parts[1] == "" {
return base + parts[0]
}
return base + strings.Join(parts, "/")
}
func (c *client) MakeServiceURL(parts []string) string {
return makeURL(c.baseURL, parts)
}
func (c *client) SignURL(path string, expires time.Time) (string, error) {
parsedURL, err := url.Parse(c.baseURL)
if err != nil {
return "", fmt.Errorf("bad Manta endpoint URL %q: %v", c.baseURL, err)
}
userAuthentication := c.creds.UserAuthentication
userAuthentication.Algorithm = "RSA-SHA1"
keyId := url.QueryEscape(fmt.Sprintf("/%s/keys/%s", userAuthentication.User, c.creds.MantaKeyId))
params := fmt.Sprintf("algorithm=%s&expires=%d&keyId=%s", userAuthentication.Algorithm, expires.Unix(), keyId)
signingLine := fmt.Sprintf("GET\n%s\n%s\n%s", parsedURL.Host, path, params)
signature, err := auth.GetSignature(userAuthentication, signingLine)
if err != nil {
return "", fmt.Errorf("cannot generate URL signature: %v", err)
}
signedURL := fmt.Sprintf("%s%s?%s&signature=%s", c.baseURL, path, params, url.QueryEscape(signature))
return signedURL, nil
}

292
vendor/github.com/joyent/gocommon/errors/errors.go generated vendored Normal file
View File

@ -0,0 +1,292 @@
//
// gocommon - Go library to interact with the JoyentCloud
// This package provides an Error implementation which knows about types of error, and which has support
// for error causes.
//
// Copyright (c) 2013 Joyent Inc.
//
// Written by Daniele Stroppa <daniele.stroppa@joyent.com>
//
package errors
import "fmt"
type Code string
const (
// Public available error types.
// These errors are provided because they are specifically required by business logic in the callers.
BadRequestError = Code("BadRequest")
InternalErrorError = Code("InternalError")
InvalidArgumentError = Code("InvalidArgument")
InvalidCredentialsError = Code("InvalidCredentials")
InvalidHeaderError = Code("InvalidHeader")
InvalidVersionError = Code("InvalidVersion")
MissingParameterError = Code("MissinParameter")
NotAuthorizedError = Code("NotAuthorized")
RequestThrottledError = Code("RequestThrottled")
RequestTooLargeError = Code("RequestTooLarge")
RequestMovedError = Code("RequestMoved")
ResourceNotFoundError = Code("ResourceNotFound")
UnknownErrorError = Code("UnkownError")
)
// Error instances store an optional error cause.
type Error interface {
error
Cause() error
}
type gojoyentError struct {
error
errcode Code
cause error
}
// Type checks.
var _ Error = (*gojoyentError)(nil)
// Code returns the error code.
func (err *gojoyentError) code() Code {
if err.errcode != UnknownErrorError {
return err.errcode
}
if e, ok := err.cause.(*gojoyentError); ok {
return e.code()
}
return UnknownErrorError
}
// Cause returns the error cause.
func (err *gojoyentError) Cause() error {
return err.cause
}
// CausedBy returns true if this error or its cause are of the specified error code.
func (err *gojoyentError) causedBy(code Code) bool {
if err.code() == code {
return true
}
if cause, ok := err.cause.(*gojoyentError); ok {
return cause.code() == code
}
return false
}
// Error fulfills the error interface, taking account of any caused by error.
func (err *gojoyentError) Error() string {
if err.cause != nil {
return fmt.Sprintf("%v\ncaused by: %v", err.error, err.cause)
}
return err.error.Error()
}
func IsBadRequest(err error) bool {
if e, ok := err.(*gojoyentError); ok {
return e.causedBy(BadRequestError)
}
return false
}
func IsInternalError(err error) bool {
if e, ok := err.(*gojoyentError); ok {
return e.causedBy(InternalErrorError)
}
return false
}
func IsInvalidArgument(err error) bool {
if e, ok := err.(*gojoyentError); ok {
return e.causedBy(InvalidArgumentError)
}
return false
}
func IsInvalidCredentials(err error) bool {
if e, ok := err.(*gojoyentError); ok {
return e.causedBy(InvalidCredentialsError)
}
return false
}
func IsInvalidHeader(err error) bool {
if e, ok := err.(*gojoyentError); ok {
return e.causedBy(InvalidHeaderError)
}
return false
}
func IsInvalidVersion(err error) bool {
if e, ok := err.(*gojoyentError); ok {
return e.causedBy(InvalidVersionError)
}
return false
}
func IsMissingParameter(err error) bool {
if e, ok := err.(*gojoyentError); ok {
return e.causedBy(MissingParameterError)
}
return false
}
func IsNotAuthorized(err error) bool {
if e, ok := err.(*gojoyentError); ok {
return e.causedBy(NotAuthorizedError)
}
return false
}
func IsRequestThrottled(err error) bool {
if e, ok := err.(*gojoyentError); ok {
return e.causedBy(RequestThrottledError)
}
return false
}
func IsRequestTooLarge(err error) bool {
if e, ok := err.(*gojoyentError); ok {
return e.causedBy(RequestTooLargeError)
}
return false
}
func IsRequestMoved(err error) bool {
if e, ok := err.(*gojoyentError); ok {
return e.causedBy(RequestMovedError)
}
return false
}
func IsResourceNotFound(err error) bool {
if e, ok := err.(*gojoyentError); ok {
return e.causedBy(ResourceNotFoundError)
}
return false
}
func IsUnknownError(err error) bool {
if e, ok := err.(*gojoyentError); ok {
return e.causedBy(UnknownErrorError)
}
return false
}
// New creates a new Error instance with the specified cause.
func makeErrorf(code Code, cause error, format string, args ...interface{}) Error {
return &gojoyentError{
errcode: code,
error: fmt.Errorf(format, args...),
cause: cause,
}
}
// New creates a new UnknownError Error instance with the specified cause.
func Newf(cause error, format string, args ...interface{}) Error {
return makeErrorf(UnknownErrorError, cause, format, args...)
}
// New creates a new BadRequest Error instance with the specified cause.
func NewBadRequestf(cause error, context interface{}, format string, args ...interface{}) Error {
if format == "" {
format = fmt.Sprintf("Bad Request: %s", context)
}
return makeErrorf(BadRequestError, cause, format, args...)
}
// New creates a new InternalError Error instance with the specified cause.
func NewInternalErrorf(cause error, context interface{}, format string, args ...interface{}) Error {
if format == "" {
format = fmt.Sprintf("Internal Error: %s", context)
}
return makeErrorf(InternalErrorError, cause, format, args...)
}
// New creates a new InvalidArgument Error instance with the specified cause.
func NewInvalidArgumentf(cause error, context interface{}, format string, args ...interface{}) Error {
if format == "" {
format = fmt.Sprintf("Invalid Argument: %s", context)
}
return makeErrorf(InvalidArgumentError, cause, format, args...)
}
// New creates a new InvalidCredentials Error instance with the specified cause.
func NewInvalidCredentialsf(cause error, context interface{}, format string, args ...interface{}) Error {
if format == "" {
format = fmt.Sprintf("Invalid Credentials: %s", context)
}
return makeErrorf(InvalidCredentialsError, cause, format, args...)
}
// New creates a new InvalidHeader Error instance with the specified cause.
func NewInvalidHeaderf(cause error, context interface{}, format string, args ...interface{}) Error {
if format == "" {
format = fmt.Sprintf("Invalid Header: %s", context)
}
return makeErrorf(InvalidHeaderError, cause, format, args...)
}
// New creates a new InvalidVersion Error instance with the specified cause.
func NewInvalidVersionf(cause error, context interface{}, format string, args ...interface{}) Error {
if format == "" {
format = fmt.Sprintf("Invalid Version: %s", context)
}
return makeErrorf(InvalidVersionError, cause, format, args...)
}
// New creates a new MissingParameter Error instance with the specified cause.
func NewMissingParameterf(cause error, context interface{}, format string, args ...interface{}) Error {
if format == "" {
format = fmt.Sprintf("Missing Parameter: %s", context)
}
return makeErrorf(MissingParameterError, cause, format, args...)
}
// New creates a new NotAuthorized Error instance with the specified cause.
func NewNotAuthorizedf(cause error, context interface{}, format string, args ...interface{}) Error {
if format == "" {
format = fmt.Sprintf("Not Authorized: %s", context)
}
return makeErrorf(NotAuthorizedError, cause, format, args...)
}
// New creates a new RequestThrottled Error instance with the specified cause.
func NewRequestThrottledf(cause error, context interface{}, format string, args ...interface{}) Error {
if format == "" {
format = fmt.Sprintf("Request Throttled: %s", context)
}
return makeErrorf(RequestThrottledError, cause, format, args...)
}
// New creates a new RequestTooLarge Error instance with the specified cause.
func NewRequestTooLargef(cause error, context interface{}, format string, args ...interface{}) Error {
if format == "" {
format = fmt.Sprintf("Request Too Large: %s", context)
}
return makeErrorf(RequestTooLargeError, cause, format, args...)
}
// New creates a new RequestMoved Error instance with the specified cause.
func NewRequestMovedf(cause error, context interface{}, format string, args ...interface{}) Error {
if format == "" {
format = fmt.Sprintf("Request Moved: %s", context)
}
return makeErrorf(RequestMovedError, cause, format, args...)
}
// New creates a new ResourceNotFound Error instance with the specified cause.
func NewResourceNotFoundf(cause error, context interface{}, format string, args ...interface{}) Error {
if format == "" {
format = fmt.Sprintf("Resource Not Found: %s", context)
}
return makeErrorf(ResourceNotFoundError, cause, format, args...)
}
// New creates a new UnknownError Error instance with the specified cause.
func NewUnknownErrorf(cause error, context interface{}, format string, args ...interface{}) Error {
if format == "" {
format = fmt.Sprintf("Unknown Error: %s", context)
}
return makeErrorf(UnknownErrorError, cause, format, args...)
}

21
vendor/github.com/joyent/gocommon/gocommon.go generated vendored Normal file
View File

@ -0,0 +1,21 @@
/*
* The gocommon package collects common packages to interact with the Joyent Public Cloud and Joyent Manta services.
*
* The gocommon package is structured as follow:
*
* - gocommon/client. Client for sending requests.
* - gocommon/errors. Joyent specific errors.
* - gocommon/http. HTTP client for sending requests.
* - gocommon/jpc. This package provides common structures and functions across packages.
* - gocommon/testing. Testing Suite for local testing.
*
* Copyright (c) 2016 Joyent Inc.
* Written by Daniele Stroppa <daniele.stroppa@joyent.com>
*
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package gocommon

427
vendor/github.com/joyent/gocommon/http/client.go generated vendored Normal file
View File

@ -0,0 +1,427 @@
//
// gocommon - Go library to interact with the JoyentCloud
// An HTTP Client which sends json and binary requests, handling data marshalling and response processing.
//
// Copyright (c) 2013 Joyent Inc.
//
// Written by Daniele Stroppa <daniele.stroppa@joyent.com>
//
package http
import (
"bytes"
"encoding/json"
"fmt"
"io"
"io/ioutil"
"log"
"net/http"
"net/url"
"reflect"
"strconv"
"strings"
"time"
"github.com/joyent/gocommon"
"github.com/joyent/gocommon/errors"
"github.com/joyent/gocommon/jpc"
"github.com/joyent/gosign/auth"
)
const (
contentTypeJSON = "application/json"
contentTypeOctetStream = "application/octet-stream"
)
type Client struct {
http.Client
maxSendAttempts int
credentials *auth.Credentials
apiVersion string
logger *log.Logger
trace bool
}
type ErrorResponse struct {
Message string `json:"message"`
Code int `json:"code"`
}
func (e *ErrorResponse) Error() string {
return fmt.Sprintf("Failed: %d: %s", e.Code, e.Message)
}
type ErrorWrapper struct {
Error ErrorResponse `json:"error"`
}
type RequestData struct {
ReqHeaders http.Header
Params *url.Values
ReqValue interface{}
ReqReader io.Reader
ReqLength int
}
type ResponseData struct {
ExpectedStatus []int
RespHeaders *http.Header
RespValue interface{}
RespReader io.ReadCloser
}
const (
// The maximum number of times to try sending a request before we give up
// (assuming any unsuccessful attempts can be sensibly tried again).
MaxSendAttempts = 3
)
// New returns a new http *Client using the default net/http client.
func New(credentials *auth.Credentials, apiVersion string, logger *log.Logger) *Client {
return &Client{*http.DefaultClient, MaxSendAttempts, credentials, apiVersion, logger, false}
}
// SetTrace allows control over whether requests will write their
// contents to the logger supplied during construction. Note that this
// is not safe to call from multiple go-routines.
func (client *Client) SetTrace(traceEnabled bool) {
client.trace = traceEnabled
}
func gojoyentAgent() string {
return fmt.Sprintf("gocommon (%s)", gocommon.Version)
}
func createHeaders(extraHeaders http.Header, credentials *auth.Credentials, contentType, rfc1123Date,
apiVersion string, isMantaRequest bool) (http.Header, error) {
headers := make(http.Header)
if extraHeaders != nil {
for header, values := range extraHeaders {
for _, value := range values {
headers.Add(header, value)
}
}
}
if extraHeaders.Get("Content-Type") == "" {
headers.Add("Content-Type", contentType)
}
if extraHeaders.Get("Accept") == "" {
headers.Add("Accept", contentType)
}
if rfc1123Date != "" {
headers.Set("Date", rfc1123Date)
} else {
headers.Set("Date", getDateForRegion(credentials, isMantaRequest))
}
authHeaders, err := auth.CreateAuthorizationHeader(headers, credentials, isMantaRequest)
if err != nil {
return http.Header{}, err
}
headers.Set("Authorization", authHeaders)
if apiVersion != "" {
headers.Set("X-Api-Version", apiVersion)
}
headers.Add("User-Agent", gojoyentAgent())
return headers, nil
}
func getDateForRegion(credentials *auth.Credentials, isManta bool) string {
if isManta {
location, _ := time.LoadLocation(jpc.Locations["us-east-1"])
return time.Now().In(location).Format(time.RFC1123)
} else {
location, _ := time.LoadLocation(jpc.Locations[credentials.Region()])
return time.Now().In(location).Format(time.RFC1123)
}
}
// JsonRequest JSON encodes and sends the object in reqData.ReqValue (if any) to the specified URL.
// Optional method arguments are passed using the RequestData object.
// Relevant RequestData fields:
// ReqHeaders: additional HTTP header values to add to the request.
// ExpectedStatus: the allowed HTTP response status values, else an error is returned.
// ReqValue: the data object to send.
// RespValue: the data object to decode the result into.
func (c *Client) JsonRequest(method, url, rfc1123Date string, request *RequestData, response *ResponseData) (err error) {
err = nil
var body []byte
if request.Params != nil {
url += "?" + request.Params.Encode()
}
if request.ReqValue != nil {
body, err = json.Marshal(request.ReqValue)
if err != nil {
err = errors.Newf(err, "failed marshalling the request body")
return
}
}
headers, err := createHeaders(request.ReqHeaders, c.credentials, contentTypeJSON, rfc1123Date, c.apiVersion,
isMantaRequest(url, c.credentials.UserAuthentication.User))
if err != nil {
return err
}
respBody, respHeader, err := c.sendRequest(
method, url, bytes.NewReader(body), len(body), headers, response.ExpectedStatus, c.logger)
if err != nil {
return
}
defer respBody.Close()
respData, err := ioutil.ReadAll(respBody)
if err != nil {
err = errors.Newf(err, "failed reading the response body")
return
}
if len(respData) > 0 {
if response.RespValue != nil {
if dest, ok := response.RespValue.(*[]byte); ok {
*dest = respData
//err = decodeJSON(bytes.NewReader(respData), false, response.RespValue)
//if err != nil {
// err = errors.Newf(err, "failed unmarshaling/decoding the response body: %s", respData)
//}
} else {
err = json.Unmarshal(respData, response.RespValue)
if err != nil {
err = decodeJSON(bytes.NewReader(respData), true, response.RespValue)
if err != nil {
err = errors.Newf(err, "failed unmarshaling/decoding the response body: %s", respData)
}
}
}
}
}
if respHeader != nil {
response.RespHeaders = respHeader
}
return
}
func decodeJSON(r io.Reader, multiple bool, into interface{}) error {
d := json.NewDecoder(r)
if multiple {
return decodeStream(d, into)
}
return d.Decode(into)
}
func decodeStream(d *json.Decoder, into interface{}) error {
t := reflect.TypeOf(into)
if t.Kind() != reflect.Ptr || t.Elem().Kind() != reflect.Slice {
return fmt.Errorf("unexpected type %s", t)
}
elemType := t.Elem().Elem()
slice := reflect.ValueOf(into).Elem()
for {
val := reflect.New(elemType)
if err := d.Decode(val.Interface()); err != nil {
if err == io.EOF {
break
}
return err
}
slice.Set(reflect.Append(slice, val.Elem()))
}
return nil
}
// Sends the byte array in reqData.ReqValue (if any) to the specified URL.
// Optional method arguments are passed using the RequestData object.
// Relevant RequestData fields:
// ReqHeaders: additional HTTP header values to add to the request.
// ExpectedStatus: the allowed HTTP response status values, else an error is returned.
// ReqReader: an io.Reader providing the bytes to send.
// RespReader: assigned an io.ReadCloser instance used to read the returned data..
func (c *Client) BinaryRequest(method, url, rfc1123Date string, request *RequestData, response *ResponseData) (err error) {
err = nil
if request.Params != nil {
url += "?" + request.Params.Encode()
}
headers, err := createHeaders(request.ReqHeaders, c.credentials, contentTypeOctetStream, rfc1123Date,
c.apiVersion, isMantaRequest(url, c.credentials.UserAuthentication.User))
if err != nil {
return err
}
respBody, respHeader, err := c.sendRequest(
method, url, request.ReqReader, request.ReqLength, headers, response.ExpectedStatus, c.logger)
if err != nil {
return
}
if response.RespReader != nil {
response.RespReader = respBody
}
if respHeader != nil {
response.RespHeaders = respHeader
}
return
}
// Sends the specified request to URL and checks that the HTTP response status is as expected.
// reqReader: a reader returning the data to send.
// length: the number of bytes to send.
// headers: HTTP headers to include with the request.
// expectedStatus: a slice of allowed response status codes.
func (c *Client) sendRequest(method, URL string, reqReader io.Reader, length int, headers http.Header,
expectedStatus []int, logger *log.Logger) (rc io.ReadCloser, respHeader *http.Header, err error) {
reqData := make([]byte, length)
if reqReader != nil {
nrRead, err := io.ReadFull(reqReader, reqData)
if err != nil {
err = errors.Newf(err, "failed reading the request data, read %v of %v bytes", nrRead, length)
return rc, respHeader, err
}
}
rawResp, err := c.sendRateLimitedRequest(method, URL, headers, reqData, logger)
if err != nil {
return
}
if logger != nil && c.trace {
logger.Printf("Request: %s %s\n", method, URL)
logger.Printf("Request header: %s\n", headers)
logger.Printf("Request body: %s\n", reqData)
logger.Printf("Response: %s\n", rawResp.Status)
logger.Printf("Response header: %s\n", rawResp.Header)
logger.Printf("Response body: %s\n", rawResp.Body)
logger.Printf("Response error: %s\n", err)
}
foundStatus := false
if len(expectedStatus) == 0 {
expectedStatus = []int{http.StatusOK}
}
for _, status := range expectedStatus {
if rawResp.StatusCode == status {
foundStatus = true
break
}
}
if !foundStatus && len(expectedStatus) > 0 {
err = handleError(URL, rawResp)
rawResp.Body.Close()
return
}
return rawResp.Body, &rawResp.Header, err
}
func (c *Client) sendRateLimitedRequest(method, URL string, headers http.Header, reqData []byte,
logger *log.Logger) (resp *http.Response, err error) {
for i := 0; i < c.maxSendAttempts; i++ {
var reqReader io.Reader
if reqData != nil {
reqReader = bytes.NewReader(reqData)
}
req, err := http.NewRequest(method, URL, reqReader)
if err != nil {
err = errors.Newf(err, "failed creating the request %s", URL)
return nil, err
}
// Setting req.Close to true to avoid malformed HTTP version "nullHTTP/1.1" error
// See http://stackoverflow.com/questions/17714494/golang-http-request-results-in-eof-errors-when-making-multiple-requests-successi
req.Close = true
for header, values := range headers {
for _, value := range values {
req.Header.Add(header, value)
}
}
req.ContentLength = int64(len(reqData))
resp, err = c.Do(req)
if err != nil {
return nil, errors.Newf(err, "failed executing the request %s", URL)
}
if resp.StatusCode != http.StatusRequestEntityTooLarge || resp.Header.Get("Retry-After") == "" {
return resp, nil
}
resp.Body.Close()
retryAfter, err := strconv.ParseFloat(resp.Header.Get("Retry-After"), 64)
if err != nil {
return nil, errors.Newf(err, "Invalid Retry-After header %s", URL)
}
if retryAfter == 0 {
return nil, errors.Newf(err, "Resource limit exeeded at URL %s", URL)
}
if logger != nil {
logger.Println("Too many requests, retrying in %dms.", int(retryAfter*1000))
}
time.Sleep(time.Duration(retryAfter) * time.Second)
}
return nil, errors.Newf(err, "Maximum number of attempts (%d) reached sending request to %s", c.maxSendAttempts, URL)
}
type HttpError struct {
StatusCode int
Data map[string][]string
Url string
ResponseMessage string
}
func (e *HttpError) Error() string {
return fmt.Sprintf("request %q returned unexpected status %d with body %q",
e.Url,
e.StatusCode,
e.ResponseMessage,
)
}
// The HTTP response status code was not one of those expected, so we construct an error.
// NotFound (404) codes have their own NotFound error type.
// We also make a guess at duplicate value errors.
func handleError(URL string, resp *http.Response) error {
errBytes, _ := ioutil.ReadAll(resp.Body)
errInfo := string(errBytes)
// Check if we have a JSON representation of the failure, if so decode it.
if resp.Header.Get("Content-Type") == contentTypeJSON {
var errResponse ErrorResponse
if err := json.Unmarshal(errBytes, &errResponse); err == nil {
errInfo = errResponse.Message
}
}
httpError := &HttpError{
resp.StatusCode, map[string][]string(resp.Header), URL, errInfo,
}
switch resp.StatusCode {
case http.StatusBadRequest:
return errors.NewBadRequestf(httpError, "", "Bad request %s", URL)
case http.StatusUnauthorized:
return errors.NewNotAuthorizedf(httpError, "", "Unauthorised URL %s", URL)
//return errors.NewInvalidCredentialsf(httpError, "", "Unauthorised URL %s", URL)
case http.StatusForbidden:
//return errors.
case http.StatusNotFound:
return errors.NewResourceNotFoundf(httpError, "", "Resource not found %s", URL)
case http.StatusMethodNotAllowed:
//return errors.
case http.StatusNotAcceptable:
return errors.NewInvalidHeaderf(httpError, "", "Invalid Header %s", URL)
case http.StatusConflict:
return errors.NewMissingParameterf(httpError, "", "Missing parameters %s", URL)
//return errors.NewInvalidArgumentf(httpError, "", "Invalid parameter %s", URL)
case http.StatusRequestEntityTooLarge:
return errors.NewRequestTooLargef(httpError, "", "Request too large %s", URL)
case http.StatusUnsupportedMediaType:
//return errors.
case http.StatusServiceUnavailable:
return errors.NewInternalErrorf(httpError, "", "Internal error %s", URL)
case 420:
// SlowDown
return errors.NewRequestThrottledf(httpError, "", "Request throttled %s", URL)
case 422:
// Unprocessable Entity
return errors.NewInvalidArgumentf(httpError, "", "Invalid parameters %s", URL)
case 449:
// RetryWith
return errors.NewInvalidVersionf(httpError, "", "Invalid version %s", URL)
//RequestMovedError -> ?
}
return errors.NewUnknownErrorf(httpError, "", "Unknown error %s", URL)
}
func isMantaRequest(url, user string) bool {
return strings.Contains(url, "/"+user+"/stor") || strings.Contains(url, "/"+user+"/jobs") || strings.Contains(url, "/"+user+"/public")
}

110
vendor/github.com/joyent/gocommon/jpc/jpc.go generated vendored Normal file
View File

@ -0,0 +1,110 @@
/*
*
* gocommon - Go library to interact with the JoyentCloud
*
*
* Copyright (c) 2016 Joyent Inc.
*
* Written by Daniele Stroppa <daniele.stroppa@joyent.com>
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package jpc
import (
"fmt"
"io/ioutil"
"os"
"reflect"
"runtime"
"github.com/joyent/gosign/auth"
)
const (
// Environment variables
SdcAccount = "SDC_ACCOUNT"
SdcKeyId = "SDC_KEY_ID"
SdcUrl = "SDC_URL"
MantaUser = "MANTA_USER"
MantaKeyId = "MANTA_KEY_ID"
MantaUrl = "MANTA_URL"
)
var Locations = map[string]string{
"us-east-1": "America/New_York",
"us-west-1": "America/Los_Angeles",
"us-sw-1": "America/Los_Angeles",
"eu-ams-1": "Europe/Amsterdam",
}
// getConfig returns the value of the first available environment
// variable, among the given ones.
func getConfig(envVars ...string) (value string) {
value = ""
for _, v := range envVars {
value = os.Getenv(v)
if value != "" {
break
}
}
return
}
// getUserHome returns the value of HOME environment
// variable for the user environment.
func getUserHome() string {
if runtime.GOOS == "windows" {
return os.Getenv("APPDATA")
} else {
return os.Getenv("HOME")
}
}
// credentialsFromEnv creates and initializes the credentials from the
// environment variables.
func credentialsFromEnv(key string) (*auth.Credentials, error) {
var keyName string
if key == "" {
keyName = getUserHome() + "/.ssh/id_rsa"
} else {
keyName = key
}
privateKey, err := ioutil.ReadFile(keyName)
if err != nil {
return nil, err
}
authentication, err := auth.NewAuth(getConfig(SdcAccount, MantaUser), string(privateKey), "rsa-sha256")
if err != nil {
return nil, err
}
return &auth.Credentials{
UserAuthentication: authentication,
SdcKeyId: getConfig(SdcKeyId),
SdcEndpoint: auth.Endpoint{URL: getConfig(SdcUrl)},
MantaKeyId: getConfig(MantaKeyId),
MantaEndpoint: auth.Endpoint{URL: getConfig(MantaUrl)},
}, nil
}
// CompleteCredentialsFromEnv gets and verifies all the required
// authentication parameters have values in the environment.
func CompleteCredentialsFromEnv(keyName string) (cred *auth.Credentials, err error) {
cred, err = credentialsFromEnv(keyName)
if err != nil {
return nil, err
}
v := reflect.ValueOf(cred).Elem()
t := v.Type()
for i := 0; i < v.NumField(); i++ {
f := v.Field(i)
if f.String() == "" {
return nil, fmt.Errorf("Required environment variable not set for credentials attribute: %s", t.Field(i).Name)
}
}
return cred, nil
}

37
vendor/github.com/joyent/gocommon/version.go generated vendored Normal file
View File

@ -0,0 +1,37 @@
/*
*
* gocommon - Go library to interact with the JoyentCloud
*
*
* Copyright (c) 2016 Joyent Inc.
*
* Written by Daniele Stroppa <daniele.stroppa@joyent.com>
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package gocommon
import (
"fmt"
)
type VersionNum struct {
Major int
Minor int
Micro int
}
func (v *VersionNum) String() string {
return fmt.Sprintf("%d.%d.%d", v.Major, v.Minor, v.Micro)
}
var VersionNumber = VersionNum{
Major: 0,
Minor: 1,
Micro: 0,
}
var Version = VersionNumber.String()

40
vendor/github.com/joyent/gocommon/wercker.yml generated vendored Normal file
View File

@ -0,0 +1,40 @@
box: golang
build:
steps:
# Sets the go workspace and places you package
# at the right place in the workspace tree
- setup-go-workspace:
package-dir: github.com/joyent/gocommon
# Gets the dependencies
- script:
name: go get
code: |
go get -v -t ./...
# Build the project
- script:
name: go build
code: |
go build ./...
- script:
name: make a new key for testing
code: |
ssh-keygen -b 2048 \
-C "Testing Key" \
-f /root/.ssh/id_rsa \
-t rsa \
-P ""
# Test the project
- script:
name: go test
code: |
export KEY_ID=$(ssh-keygen -lf /root/.ssh/id_rsa | awk -F' ' '{print $2}' | cut -d':' -f2-)
export SDC_KEY_ID=${KEY_ID}
export MANTA_KEY_ID=${KEY_ID}
export SDC_URL=https://us-east-1.api.joyent.com
export MANTA_URL=https://us-east.manta.joyent.com
go test ./...

373
vendor/github.com/joyent/gosdc/LICENSE generated vendored Normal file
View File

@ -0,0 +1,373 @@
Mozilla Public License Version 2.0
==================================
1. Definitions
--------------
1.1. "Contributor"
means each individual or legal entity that creates, contributes to
the creation of, or owns Covered Software.
1.2. "Contributor Version"
means the combination of the Contributions of others (if any) used
by a Contributor and that particular Contributor's Contribution.
1.3. "Contribution"
means Covered Software of a particular Contributor.
1.4. "Covered Software"
means Source Code Form to which the initial Contributor has attached
the notice in Exhibit A, the Executable Form of such Source Code
Form, and Modifications of such Source Code Form, in each case
including portions thereof.
1.5. "Incompatible With Secondary Licenses"
means
(a) that the initial Contributor has attached the notice described
in Exhibit B to the Covered Software; or
(b) that the Covered Software was made available under the terms of
version 1.1 or earlier of the License, but not also under the
terms of a Secondary License.
1.6. "Executable Form"
means any form of the work other than Source Code Form.
1.7. "Larger Work"
means a work that combines Covered Software with other material, in
a separate file or files, that is not Covered Software.
1.8. "License"
means this document.
1.9. "Licensable"
means having the right to grant, to the maximum extent possible,
whether at the time of the initial grant or subsequently, any and
all of the rights conveyed by this License.
1.10. "Modifications"
means any of the following:
(a) any file in Source Code Form that results from an addition to,
deletion from, or modification of the contents of Covered
Software; or
(b) any new file in Source Code Form that contains any Covered
Software.
1.11. "Patent Claims" of a Contributor
means any patent claim(s), including without limitation, method,
process, and apparatus claims, in any patent Licensable by such
Contributor that would be infringed, but for the grant of the
License, by the making, using, selling, offering for sale, having
made, import, or transfer of either its Contributions or its
Contributor Version.
1.12. "Secondary License"
means either the GNU General Public License, Version 2.0, the GNU
Lesser General Public License, Version 2.1, the GNU Affero General
Public License, Version 3.0, or any later versions of those
licenses.
1.13. "Source Code Form"
means the form of the work preferred for making modifications.
1.14. "You" (or "Your")
means an individual or a legal entity exercising rights under this
License. For legal entities, "You" includes any entity that
controls, is controlled by, or is under common control with You. For
purposes of this definition, "control" means (a) the power, direct
or indirect, to cause the direction or management of such entity,
whether by contract or otherwise, or (b) ownership of more than
fifty percent (50%) of the outstanding shares or beneficial
ownership of such entity.
2. License Grants and Conditions
--------------------------------
2.1. Grants
Each Contributor hereby grants You a world-wide, royalty-free,
non-exclusive license:
(a) under intellectual property rights (other than patent or trademark)
Licensable by such Contributor to use, reproduce, make available,
modify, display, perform, distribute, and otherwise exploit its
Contributions, either on an unmodified basis, with Modifications, or
as part of a Larger Work; and
(b) under Patent Claims of such Contributor to make, use, sell, offer
for sale, have made, import, and otherwise transfer either its
Contributions or its Contributor Version.
2.2. Effective Date
The licenses granted in Section 2.1 with respect to any Contribution
become effective for each Contribution on the date the Contributor first
distributes such Contribution.
2.3. Limitations on Grant Scope
The licenses granted in this Section 2 are the only rights granted under
this License. No additional rights or licenses will be implied from the
distribution or licensing of Covered Software under this License.
Notwithstanding Section 2.1(b) above, no patent license is granted by a
Contributor:
(a) for any code that a Contributor has removed from Covered Software;
or
(b) for infringements caused by: (i) Your and any other third party's
modifications of Covered Software, or (ii) the combination of its
Contributions with other software (except as part of its Contributor
Version); or
(c) under Patent Claims infringed by Covered Software in the absence of
its Contributions.
This License does not grant any rights in the trademarks, service marks,
or logos of any Contributor (except as may be necessary to comply with
the notice requirements in Section 3.4).
2.4. Subsequent Licenses
No Contributor makes additional grants as a result of Your choice to
distribute the Covered Software under a subsequent version of this
License (see Section 10.2) or under the terms of a Secondary License (if
permitted under the terms of Section 3.3).
2.5. Representation
Each Contributor represents that the Contributor believes its
Contributions are its original creation(s) or it has sufficient rights
to grant the rights to its Contributions conveyed by this License.
2.6. Fair Use
This License is not intended to limit any rights You have under
applicable copyright doctrines of fair use, fair dealing, or other
equivalents.
2.7. Conditions
Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted
in Section 2.1.
3. Responsibilities
-------------------
3.1. Distribution of Source Form
All distribution of Covered Software in Source Code Form, including any
Modifications that You create or to which You contribute, must be under
the terms of this License. You must inform recipients that the Source
Code Form of the Covered Software is governed by the terms of this
License, and how they can obtain a copy of this License. You may not
attempt to alter or restrict the recipients' rights in the Source Code
Form.
3.2. Distribution of Executable Form
If You distribute Covered Software in Executable Form then:
(a) such Covered Software must also be made available in Source Code
Form, as described in Section 3.1, and You must inform recipients of
the Executable Form how they can obtain a copy of such Source Code
Form by reasonable means in a timely manner, at a charge no more
than the cost of distribution to the recipient; and
(b) You may distribute such Executable Form under the terms of this
License, or sublicense it under different terms, provided that the
license for the Executable Form does not attempt to limit or alter
the recipients' rights in the Source Code Form under this License.
3.3. Distribution of a Larger Work
You may create and distribute a Larger Work under terms of Your choice,
provided that You also comply with the requirements of this License for
the Covered Software. If the Larger Work is a combination of Covered
Software with a work governed by one or more Secondary Licenses, and the
Covered Software is not Incompatible With Secondary Licenses, this
License permits You to additionally distribute such Covered Software
under the terms of such Secondary License(s), so that the recipient of
the Larger Work may, at their option, further distribute the Covered
Software under the terms of either this License or such Secondary
License(s).
3.4. Notices
You may not remove or alter the substance of any license notices
(including copyright notices, patent notices, disclaimers of warranty,
or limitations of liability) contained within the Source Code Form of
the Covered Software, except that You may alter any license notices to
the extent required to remedy known factual inaccuracies.
3.5. Application of Additional Terms
You may choose to offer, and to charge a fee for, warranty, support,
indemnity or liability obligations to one or more recipients of Covered
Software. However, You may do so only on Your own behalf, and not on
behalf of any Contributor. You must make it absolutely clear that any
such warranty, support, indemnity, or liability obligation is offered by
You alone, and You hereby agree to indemnify every Contributor for any
liability incurred by such Contributor as a result of warranty, support,
indemnity or liability terms You offer. You may include additional
disclaimers of warranty and limitations of liability specific to any
jurisdiction.
4. Inability to Comply Due to Statute or Regulation
---------------------------------------------------
If it is impossible for You to comply with any of the terms of this
License with respect to some or all of the Covered Software due to
statute, judicial order, or regulation then You must: (a) comply with
the terms of this License to the maximum extent possible; and (b)
describe the limitations and the code they affect. Such description must
be placed in a text file included with all distributions of the Covered
Software under this License. Except to the extent prohibited by statute
or regulation, such description must be sufficiently detailed for a
recipient of ordinary skill to be able to understand it.
5. Termination
--------------
5.1. The rights granted under this License will terminate automatically
if You fail to comply with any of its terms. However, if You become
compliant, then the rights granted under this License from a particular
Contributor are reinstated (a) provisionally, unless and until such
Contributor explicitly and finally terminates Your grants, and (b) on an
ongoing basis, if such Contributor fails to notify You of the
non-compliance by some reasonable means prior to 60 days after You have
come back into compliance. Moreover, Your grants from a particular
Contributor are reinstated on an ongoing basis if such Contributor
notifies You of the non-compliance by some reasonable means, this is the
first time You have received notice of non-compliance with this License
from such Contributor, and You become compliant prior to 30 days after
Your receipt of the notice.
5.2. If You initiate litigation against any entity by asserting a patent
infringement claim (excluding declaratory judgment actions,
counter-claims, and cross-claims) alleging that a Contributor Version
directly or indirectly infringes any patent, then the rights granted to
You by any and all Contributors for the Covered Software under Section
2.1 of this License shall terminate.
5.3. In the event of termination under Sections 5.1 or 5.2 above, all
end user license agreements (excluding distributors and resellers) which
have been validly granted by You or Your distributors under this License
prior to termination shall survive termination.
************************************************************************
* *
* 6. Disclaimer of Warranty *
* ------------------------- *
* *
* Covered Software is provided under this License on an "as is" *
* basis, without warranty of any kind, either expressed, implied, or *
* statutory, including, without limitation, warranties that the *
* Covered Software is free of defects, merchantable, fit for a *
* particular purpose or non-infringing. The entire risk as to the *
* quality and performance of the Covered Software is with You. *
* Should any Covered Software prove defective in any respect, You *
* (not any Contributor) assume the cost of any necessary servicing, *
* repair, or correction. This disclaimer of warranty constitutes an *
* essential part of this License. No use of any Covered Software is *
* authorized under this License except under this disclaimer. *
* *
************************************************************************
************************************************************************
* *
* 7. Limitation of Liability *
* -------------------------- *
* *
* Under no circumstances and under no legal theory, whether tort *
* (including negligence), contract, or otherwise, shall any *
* Contributor, or anyone who distributes Covered Software as *
* permitted above, be liable to You for any direct, indirect, *
* special, incidental, or consequential damages of any character *
* including, without limitation, damages for lost profits, loss of *
* goodwill, work stoppage, computer failure or malfunction, or any *
* and all other commercial damages or losses, even if such party *
* shall have been informed of the possibility of such damages. This *
* limitation of liability shall not apply to liability for death or *
* personal injury resulting from such party's negligence to the *
* extent applicable law prohibits such limitation. Some *
* jurisdictions do not allow the exclusion or limitation of *
* incidental or consequential damages, so this exclusion and *
* limitation may not apply to You. *
* *
************************************************************************
8. Litigation
-------------
Any litigation relating to this License may be brought only in the
courts of a jurisdiction where the defendant maintains its principal
place of business and such litigation shall be governed by laws of that
jurisdiction, without reference to its conflict-of-law provisions.
Nothing in this Section shall prevent a party's ability to bring
cross-claims or counter-claims.
9. Miscellaneous
----------------
This License represents the complete agreement concerning the subject
matter hereof. If any provision of this License is held to be
unenforceable, such provision shall be reformed only to the extent
necessary to make it enforceable. Any law or regulation which provides
that the language of a contract shall be construed against the drafter
shall not be used to construe this License against a Contributor.
10. Versions of the License
---------------------------
10.1. New Versions
Mozilla Foundation is the license steward. Except as provided in Section
10.3, no one other than the license steward has the right to modify or
publish new versions of this License. Each version will be given a
distinguishing version number.
10.2. Effect of New Versions
You may distribute the Covered Software under the terms of the version
of the License under which You originally received the Covered Software,
or under the terms of any subsequent version published by the license
steward.
10.3. Modified Versions
If you create software not governed by this License, and you want to
create a new license for such software, you may create and use a
modified version of this License if you rename the license and remove
any references to the name of the license steward (except to note that
such modified license differs from this License).
10.4. Distributing Source Code Form that is Incompatible With Secondary
Licenses
If You choose to distribute Source Code Form that is Incompatible With
Secondary Licenses under the terms of this version of the License, the
notice described in Exhibit B of this License must be attached.
Exhibit A - Source Code Form License Notice
-------------------------------------------
This Source Code Form is subject to the terms of the Mozilla Public
License, v. 2.0. If a copy of the MPL was not distributed with this
file, You can obtain one at http://mozilla.org/MPL/2.0/.
If it is not possible or desirable to put the notice in a particular
file, then You may include the notice in a location (such as a LICENSE
file in a relevant directory) where a recipient would be likely to look
for such a notice.
You may add additional accurate notices of copyright ownership.
Exhibit B - "Incompatible With Secondary Licenses" Notice
---------------------------------------------------------
This Source Code Form is "Incompatible With Secondary Licenses", as
defined by the Mozilla Public License, v. 2.0.

163
vendor/github.com/joyent/gosdc/README.md generated vendored Normal file
View File

@ -0,0 +1,163 @@
# gosdc
[![wercker status](https://app.wercker.com/status/349ee60ed0afffd99d2b2b354ada5938/s/master "wercker status")](https://app.wercker.com/project/bykey/349ee60ed0afffd99d2b2b354ada5938)
`gosdc` is a Go client for Joyent's SmartDataCenter
<!-- markdown-toc start - Don't edit this section. Run M-x markdown-toc-generate-toc again -->
**Table of Contents**
- [gosdc](#gosdc)
- [Usage](#usage)
- [Examples](#examples)
- [Resources](#resources)
- [License](#license)
<!-- markdown-toc end -->
## Usage
To create a client
([`*cloudapi.Client`](https://godoc.org/github.com/joyent/gosdc/cloudapi#Client)),
you'll need a few things:
1. your account ID
2. the ID of the key associated with your account
3. your private key material
4. the cloud endpoint you want to use (for example
`https://us-east-1.api.joyentcloud.com`)
Given these four pieces of information, you can initialize a client with the
following:
```go
package main
import (
"io/ioutil"
"log"
"os"
"github.com/joyent/gocommon/client"
"github.com/joyent/gosdc/cloudapi"
"github.com/joyent/gosign/auth"
)
func client(key, keyId, account, endpoint string) (*cloudapi.Client, error) {
keyData, err := ioutil.ReadFile(key)
if err != nil {
return nil, err
}
userAuth, err := auth.NewAuth(account, string(keyData), "rsa-sha256")
if err != nil {
return nil, err
}
creds := &auth.Credentials{
UserAuthentication: auth,
SdcKeyId: keyId,
SdcEndpoint: auth.Endpoint{URL: endpoint},
}
return cloudapi.New(client.NewClient(
creds.SdcEndpoint.URL,
cloudapi.DefaultAPIVersion,
creds,
log.New(os.Stderr, "", log.LstdFlags),
)), nil
}
```
### Examples
Projects using the gosdc API:
- [triton-terraform](https://github.com/joyent/triton-terraform)
## Resources
After creating a client, you can manipulate resources in the following ways:
| Resource | Create | Read | Update | Delete | Extra |
|----------|--------|------|--------|--------|-------|
| Datacenters | | [GetDatacenter](https://godoc.org/github.com/joyent/gosdc/cloudapi#Client.GetDatacenter), [ListDatacenters](https://godoc.org/github.com/joyent/gosdc/cloudapi#Client.ListDatacenters) | | | |
| Firewall Rules | [CreateFirewallRule](https://godoc.org/github.com/joyent/gosdc/cloudapi#Client.CreateFirewallRule) | [GetFirewallRule](https://godoc.org/github.com/joyent/gosdc/cloudapi#Client.GetFirewallRule), [ListFirewallRules](https://godoc.org/github.com/joyent/gosdc/cloudapi#Client.ListFirewallRules), [ListmachineFirewallRules](https://godoc.org/github.com/joyent/gosdc/cloudapi#Client.ListMachineFirewallRules) | [UpdateFirewallRule](https://godoc.org/github.com/joyent/gosdc/cloudapi#Client.UpdateFirewallRule), [EnableFirewallRule](https://godoc.org/github.com/joyent/gosdc/cloudapi#Client.EnableFirewallRule), [DisableFirewallRule](https://godoc.org/github.com/joyent/gosdc/cloudapi#Client.DisableFirewallRule) | [DeleteFirewallRule](https://godoc.org/github.com/joyent/gosdc/cloudapi#Client.DeleteFirewallRule) | |
| Instrumentations | [CreateInstrumentation](https://godoc.org/github.com/joyent/gosdc/cloudapi#Client.CreateInstrumentation) | [GetInstrumentation](https://godoc.org/github.com/joyent/gosdc/cloudapi#Client.GetInstrumentation), [ListInstrumentations](https://godoc.org/github.com/joyent/gosdc/cloudapi#Client.ListInstrumentations), [GetInstrumentationHeatmap](https://godoc.org/github.com/joyent/gosdc/cloudapi#Client.GetInstrumentationHeatmap), [GetInstrumentationHeatmapDetails](https://godoc.org/github.com/joyent/gosdc/cloudapi#Client.GetInstrumentationHeatmapDetails), [GetInstrumentationValue](https://godoc.org/github.com/joyent/gosdc/cloudapi#Client.GetInstrumentationValue) | | [DeleteInstrumentation](https://godoc.org/github.com/joyent/gosdc/cloudapi#Client.DeleteInstrumentation) | [DescribeAnalytics](https://godoc.org/github.com/joyent/gosdc/cloudapi#Client.DescribeAnalytics) |
| Keys | [CreateKey](https://godoc.org/github.com/joyent/gosdc/cloudapi#Client.CreateKey) | [GetKey](https://godoc.org/github.com/joyent/gosdc/cloudapi#Client.GetKey), [ListKeys](https://godoc.org/github.com/joyent/gosdc/cloudapi#Client.ListKeys) | | [DeleteKey](https://godoc.org/github.com/joyent/gosdc/cloudapi#Client.DeleteKey) | |
| Machines | [CreateMachine](https://godoc.org/github.com/joyent/gosdc/cloudapi#Client.CreateMachine) | [GetMachine](https://godoc.org/github.com/joyent/gosdc/cloudapi#Client.GetMachine), [ListMachines](https://godoc.org/github.com/joyent/gosdc/cloudapi#Client.ListMachines), [ListFirewallRuleMachines](https://godoc.org/github.com/joyent/gosdc/cloudapi#Client.ListFirewallRuleMachines) | [RenameMachine](https://godoc.org/github.com/joyent/gosdc/cloudapi#Client.RenameMachine), [ResizeMachine](https://godoc.org/github.com/joyent/gosdc/cloudapi#Client.ResizeMachine) | [DeleteMachine](https://godoc.org/github.com/joyent/gosdc/cloudapi#Client.DeleteMachine) | [CountMachines](https://godoc.org/github.com/joyent/gosdc/cloudapi#Client.CountMachines), [MachineAudit](https://godoc.org/github.com/joyent/gosdc/cloudapi#Client.MachineAudit), [StartMachine](https://godoc.org/github.com/joyent/gosdc/cloudapi#Client.StartMachine), [StartMachineFromSnapshot](https://godoc.org/github.com/joyent/gosdc/cloudapi#Client.StartMachineFromSnapshot), [StopMachine](https://godoc.org/github.com/joyent/gosdc/cloudapi#Client.StopMachine), [RebootMachine](https://godoc.org/github.com/joyent/gosdc/cloudapi#Client.RebootMachine) |
| Machine (Images) | [CreateImageFromMachine](https://godoc.org/github.com/joyent/gosdc/cloudapi#Client.CreateImageFromMachine) | [GetImage](https://godoc.org/github.com/joyent/gosdc/cloudapi#Client.GetImage), [ListImages](https://godoc.org/github.com/joyent/gosdc/cloudapi#Client.ListImages) | | [DeleteImage](https://godoc.org/github.com/joyent/gosdc/cloudapi#Client.DeleteImage) | [ExportImage](https://godoc.org/github.com/joyent/gosdc/cloudapi#Client.ExportImage) |
| Machine (Metadata) | | [GetMachineMetadata](https://godoc.org/github.com/joyent/gosdc/cloudapi#Client.GetMachineMetadata) | [UpdateMachineMetadata](https://godoc.org/github.com/joyent/gosdc/cloudapi#Client.UpdateMachineMetadata) | [DeleteMachineMetadata](https://godoc.org/github.com/joyent/gosdc/cloudapi#Client.DeleteMachineMetadata), [DeleteAllMachineMetadata](https://godoc.org/github.com/joyent/gosdc/cloudapi#Client.DeleteAllMachineMetadata) | |
| Machine (Snapshots) | [CreateMachineSnapshot](https://godoc.org/github.com/joyent/gosdc/cloudapi#Client.CreateMachineSnapshot) | [GetMachineSnapshot](https://godoc.org/github.com/joyent/gosdc/cloudapi#Client.GetMachineSnapshot), [ListMachineSnapshots](https://godoc.org/github.com/joyent/gosdc/cloudapi#Client.ListMachineSnapshots) | | [DeleteMachineSnapshot](https://godoc.org/github.com/joyent/gosdc/cloudapi#Client.DeleteMachineSnapshot) | |
| Machine (Tags) | | [GetMachineTag](https://godoc.org/github.com/joyent/gosdc/cloudapi#Client.GetMachineTag), [ListMachineTags](https://godoc.org/github.com/joyent/gosdc/cloudapi#Client.ListMachineTags) | [AddMachineTags](https://godoc.org/github.com/joyent/gosdc/cloudapi#Client.AddMachineTags), [ReplaceMachineTags](https://godoc.org/github.com/joyent/gosdc/cloudapi#Client.ReplaceMachineTags) | [DeleteMachineTag](https://godoc.org/github.com/joyent/gosdc/cloudapi#Client.DeleteMachineTag), [DeleteMachineTags](https://godoc.org/github.com/joyent/gosdc/cloudapi#Client.DeleteMachineTags) | [EnableFirewallMachine](https://godoc.org/github.com/joyent/gosdc/cloudapi#Client.EnableFirewallMachine), [DisableFirewallMachine](https://godoc.org/github.com/joyent/gosdc/cloudapi#Client.DisableFirewallMachine) |
| Networks | | [GetNetwork](https://godoc.org/github.com/joyent/gosdc/cloudapi#Client.GetNetwork), [ListNetworks](https://godoc.org/github.com/joyent/gosdc/cloudapi#Client.ListNetworks) | | | |
| Packages | | [GetPackage](https://godoc.org/github.com/joyent/gosdc/cloudapi#Client.GetPackage), [ListPackages](https://godoc.org/github.com/joyent/gosdc/cloudapi#Client.ListPackages) | | | |
## Contributing
Report bugs and request features using [GitHub Issues](https://github.com/joyent/gosdc/issues), or contribute code via a [GitHub Pull Request](https://github.com/joyent/gosdc/pulls). Changes will be code reviewed before merging. In the near future, automated tests will be run, but in the meantime please `go fmt`, `go lint`, and test all contributions.
## Developing
This library assumes a Go development environment setup based on [How to Write Go Code](https://golang.org/doc/code.html). Your GOPATH environment variable should be pointed at your workspace directory.
You can now use `go get github.com/joyent/gosdc` to install the repository to the correct location, but if you are intending on contributing back a change you may want to consider cloning the repository via git yourself. This way you can have a single source tree for all Joyent Go projects with each repo having two remotes -- your own fork on GitHub and the upstream origin.
For example if your GOPATH is `~/src/joyent/go` and you're working on multiple repos then that directory tree might look like:
```
~/src/joyent/go/
|_ pkg/
|_ src/
|_ github.com
|_ joyent
|_ gocommon
|_ gomanta
|_ gosdc
|_ gosign
```
### Recommended Setup
```
$ mkdir -p ${GOPATH}/src/github.com/joyent
$ cd ${GOPATH}/src/github.com/joyent
$ git clone git@github.com:<yourname>/gosdc.git
# fetch dependencies
$ git clone git@github.com:<yourname>/gocommon.git
$ git clone git@github.com:<yourname>/gosign.git
$ go get -v -t ./...
# add upstream remote
$ cd gosdc
$ git remote add upstream git@github.com:joyent/gosdc.git
$ git remote -v
origin git@github.com:<yourname>/gosdc.git (fetch)
origin git@github.com:<yourname>/gosdc.git (push)
upstream git@github.com:joyent/gosdc.git (fetch)
upstream git@github.com:joyent/gosdc.git (push)
```
### Run Tests
You can run the tests either locally or against live Triton. If you want to run the tests locally you'll want to generate an SSH key and pass the appropriate flags to the test harness as shown below.
```
cd ${GOPATH}/src/github.com/joyent/gosdc
ssh-keygen -b 2048 -C "Testing Key" -f test_key.id_rsa -t rsa -P ""
env KEY_NAME=`pwd`/test_key.id_rsa LIVE=false go test ./...
```
### Build the Library
```
cd ${GOPATH}/src/github.com/joyent/gosdc
go build ./...
```
## License
gosdc is licensed under the Mozilla Public License Version 2.0, a copy of which
is available at [LICENSE](LICENSE)

127
vendor/github.com/joyent/gosdc/cloudapi/cloudapi.go generated vendored Normal file
View File

@ -0,0 +1,127 @@
/*
Package cloudapi interacts with the Cloud API (http://apidocs.joyent.com/cloudapi/).
Licensed under the Mozilla Public License version 2.0
Copyright (c) Joyent Inc.
*/
package cloudapi
import (
"net/http"
"net/url"
"path"
"github.com/joyent/gocommon/client"
jh "github.com/joyent/gocommon/http"
)
const (
// DefaultAPIVersion defines the default version of the Cloud API to use
DefaultAPIVersion = "~7.3"
// CloudAPI URL parts
apiKeys = "keys"
apiPackages = "packages"
apiImages = "images"
apiDatacenters = "datacenters"
apiMachines = "machines"
apiMetadata = "metadata"
apiSnapshots = "snapshots"
apiTags = "tags"
apiAnalytics = "analytics"
apiInstrumentations = "instrumentations"
apiInstrumentationsValue = "value"
apiInstrumentationsRaw = "raw"
apiInstrumentationsHeatmap = "heatmap"
apiInstrumentationsImage = "image"
apiInstrumentationsDetails = "details"
apiUsage = "usage"
apiAudit = "audit"
apiFirewallRules = "fwrules"
apiFirewallRulesEnable = "enable"
apiFirewallRulesDisable = "disable"
apiNetworks = "networks"
apiFabricVLANs = "fabrics/default/vlans"
apiFabricNetworks = "networks"
apiNICs = "nics"
apiServices = "services"
// CloudAPI actions
actionExport = "export"
actionStop = "stop"
actionStart = "start"
actionReboot = "reboot"
actionResize = "resize"
actionRename = "rename"
actionEnableFw = "enable_firewall"
actionDisableFw = "disable_firewall"
)
// Client provides a means to access the Joyent CloudAPI
type Client struct {
client client.Client
}
// New creates a new Client.
func New(client client.Client) *Client {
return &Client{client}
}
// Filter represents a filter that can be applied to an API request.
type Filter struct {
v url.Values
}
// NewFilter creates a new Filter.
func NewFilter() *Filter {
return &Filter{make(url.Values)}
}
// Set a value for the specified filter.
func (f *Filter) Set(filter, value string) {
f.v.Set(filter, value)
}
// Add a value for the specified filter.
func (f *Filter) Add(filter, value string) {
f.v.Add(filter, value)
}
// request represents an API request
type request struct {
method string
url string
filter *Filter
reqValue interface{}
reqHeader http.Header
resp interface{}
respHeader *http.Header
expectedStatus int
}
// Helper method to send an API request
func (c *Client) sendRequest(req request) (*jh.ResponseData, error) {
request := jh.RequestData{
ReqValue: req.reqValue,
ReqHeaders: req.reqHeader,
}
if req.filter != nil {
request.Params = &req.filter.v
}
if req.expectedStatus == 0 {
req.expectedStatus = http.StatusOK
}
respData := jh.ResponseData{
RespValue: req.resp,
RespHeaders: req.respHeader,
ExpectedStatus: []int{req.expectedStatus},
}
err := c.client.SendRequest(req.method, req.url, "", &request, &respData)
return &respData, err
}
// Helper method to create the API URL
func makeURL(parts ...string) string {
return path.Join(parts...)
}

41
vendor/github.com/joyent/gosdc/cloudapi/datacenters.go generated vendored Normal file
View File

@ -0,0 +1,41 @@
package cloudapi
import (
"net/http"
"github.com/joyent/gocommon/client"
"github.com/joyent/gocommon/errors"
)
// ListDatacenters provides a list of all datacenters this cloud is aware of.
// See API docs: http://apidocs.joyent.com/cloudapi/#ListDatacenters
func (c *Client) ListDatacenters() (map[string]interface{}, error) {
var resp map[string]interface{}
req := request{
method: client.GET,
url: apiDatacenters,
resp: &resp,
}
if _, err := c.sendRequest(req); err != nil {
return nil, errors.Newf(err, "failed to get list of datcenters")
}
return resp, nil
}
// GetDatacenter gets an individual datacenter by name. Returns an HTTP redirect
// to your client, the datacenter URL is in the Location header.
// See API docs: http://apidocs.joyent.com/cloudapi/#GetDatacenter
func (c *Client) GetDatacenter(datacenterName string) (string, error) {
var respHeader http.Header
req := request{
method: client.GET,
url: makeURL(apiDatacenters, datacenterName),
respHeader: &respHeader,
expectedStatus: http.StatusFound,
}
respData, err := c.sendRequest(req)
if err != nil {
return "", errors.Newf(err, "failed to get datacenter with name: %s", datacenterName)
}
return respData.RespHeaders.Get("Location"), nil
}

182
vendor/github.com/joyent/gosdc/cloudapi/fabrics.go generated vendored Normal file
View File

@ -0,0 +1,182 @@
package cloudapi
import (
"net/http"
"strconv"
"github.com/joyent/gocommon/client"
"github.com/joyent/gocommon/errors"
)
type FabricVLAN struct {
Id int16 `json:"vlan_id"` // Number between 0-4095 indicating VLAN Id
Name string `json:"name"` // Unique name to identify VLAN
Description string `json:"description,omitempty"` // Optional description of the VLAN
}
type FabricNetwork struct {
Id string `json:"id"` // Unique identifier for network
Name string `json:"name"` // Network name
Public bool `json:"public"` // Whether or not this is an RFC1918 network
Fabric bool `json:"fabric"` // Whether this network is on a fabric
Description string `json:"description"` // Optional description of network
Subnet string `json:"subnet"` // CIDR formatted string describing network
ProvisionStartIp string `json:"provision_start_ip"` // First IP on the network that can be assigned
ProvisionEndIp string `json:"provision_end_ip"` // Last assignable IP on the network
Gateway string `json:"gateway"` // Optional Gateway IP
Resolvers []string `json:"resolvers,omitempty"` // Array of IP addresses for resolvers
Routes map[string]string `json:"routes,omitempty"` // Map of CIDR block to Gateway IP Address
InternetNAT bool `json:"internet_nat"` // If a NAT zone is provisioned at Gateway IP Address
VLANId int16 `json:"vlan_id"` // VLAN network is on
}
type CreateFabricNetworkOpts struct {
Name string `json:"name"` // Network name
Description string `json:"description,omitempty"` // Optional description of network
Subnet string `json:"subnet"` // CIDR formatted string describing network
ProvisionStartIp string `json:"provision_start_ip"` // First IP on the network that can be assigned
ProvisionEndIp string `json:"provision_end_ip"` // Last assignable IP on the network
Gateway string `json:"gateway,omitempty"` // Optional Gateway IP
Resolvers []string `json:"resolvers,omitempty"` // Array of IP addresses for resolvers
Routes map[string]string `json:"routes,omitempty"` // Map of CIDR block to Gateway IP Address
InternetNAT bool `json:"internet_nat"` // If a NAT zone is provisioned at Gateway IP Address
}
// ListFabricVLANs lists VLANs
// See API docs: https://apidocs.joyent.com/cloudapi/#ListFabricVLANs
func (c *Client) ListFabricVLANs() ([]FabricVLAN, error) {
var resp []FabricVLAN
req := request{
method: client.GET,
url: apiFabricVLANs,
resp: &resp,
}
if _, err := c.sendRequest(req); err != nil {
return nil, errors.Newf(err, "failed to get list of fabric VLANs")
}
return resp, nil
}
// GetFabricLAN retrieves a single VLAN by ID
// See API docs: https://apidocs.joyent.com/cloudapi/#GetFabricVLAN
func (c *Client) GetFabricVLAN(vlanID int16) (*FabricVLAN, error) {
var resp FabricVLAN
req := request{
method: client.GET,
url: makeURL(apiFabricVLANs, strconv.Itoa(int(vlanID))),
resp: &resp,
}
if _, err := c.sendRequest(req); err != nil {
return nil, errors.Newf(err, "failed to get fabric VLAN with id %d", vlanID)
}
return &resp, nil
}
// CreateFabricVLAN creates a new VLAN with the specified options
// See API docs: https://apidocs.joyent.com/cloudapi/#CreateFabricVLAN
func (c *Client) CreateFabricVLAN(vlan FabricVLAN) (*FabricVLAN, error) {
var resp FabricVLAN
req := request{
method: client.POST,
url: apiFabricVLANs,
reqValue: vlan,
resp: &resp,
expectedStatus: http.StatusCreated,
}
if _, err := c.sendRequest(req); err != nil {
return nil, errors.Newf(err, "failed to create fabric VLAN: %d - %s", vlan.Id, vlan.Name)
}
return &resp, nil
}
// UpdateFabricVLAN updates a given VLAN with new fields
// See API docs: https://apidocs.joyent.com/cloudapi/#UpdateFabricVLAN
func (c *Client) UpdateFabricVLAN(vlan FabricVLAN) (*FabricVLAN, error) {
var resp FabricVLAN
req := request{
method: client.PUT,
url: makeURL(apiFabricVLANs, strconv.Itoa(int(vlan.Id))),
reqValue: vlan,
resp: &resp,
expectedStatus: http.StatusAccepted,
}
if _, err := c.sendRequest(req); err != nil {
return nil, errors.Newf(err, "failed to update fabric VLAN with id %d to %s - %s", vlan.Id, vlan.Name, vlan.Description)
}
return &resp, nil
}
// DeleteFabricVLAN delets a given VLAN as specified by ID
// See API docs: https://apidocs.joyent.com/cloudapi/#DeleteFabricVLAN
func (c *Client) DeleteFabricVLAN(vlanID int16) error {
req := request{
method: client.DELETE,
url: makeURL(apiFabricVLANs, strconv.Itoa(int(vlanID))),
expectedStatus: http.StatusNoContent,
}
if _, err := c.sendRequest(req); err != nil {
return errors.Newf(err, "failed to delete fabric VLAN with id %d", vlanID)
}
return nil
}
// ListFabricNetworks lists the networks inside the given VLAN
// See API docs: https://apidocs.joyent.com/cloudapi/#ListFabricNetworks
func (c *Client) ListFabricNetworks(vlanID int16) ([]FabricNetwork, error) {
var resp []FabricNetwork
req := request{
method: client.GET,
url: makeURL(apiFabricVLANs, strconv.Itoa(int(vlanID)), apiFabricNetworks),
resp: &resp,
}
if _, err := c.sendRequest(req); err != nil {
return nil, errors.Newf(err, "failed to get list of networks on fabric %d", vlanID)
}
return resp, nil
}
// GetFabricNetwork gets a single network by VLAN and Network IDs
// See API docs: https://apidocs.joyent.com/cloudapi/#GetFabricNetwork
func (c *Client) GetFabricNetwork(vlanID int16, networkID string) (*FabricNetwork, error) {
var resp FabricNetwork
req := request{
method: client.GET,
url: makeURL(apiFabricVLANs, strconv.Itoa(int(vlanID)), apiFabricNetworks, networkID),
resp: &resp,
}
if _, err := c.sendRequest(req); err != nil {
return nil, errors.Newf(err, "failed to get fabric network %s on vlan %d", networkID, vlanID)
}
return &resp, nil
}
// CreateFabricNetwork creates a new fabric network
// See API docs: https://apidocs.joyent.com/cloudapi/#CreateFabricNetwork
func (c *Client) CreateFabricNetwork(vlanID int16, opts CreateFabricNetworkOpts) (*FabricNetwork, error) {
var resp FabricNetwork
req := request{
method: client.POST,
url: makeURL(apiFabricVLANs, strconv.Itoa(int(vlanID)), apiFabricNetworks),
reqValue: opts,
resp: &resp,
expectedStatus: http.StatusCreated,
}
if _, err := c.sendRequest(req); err != nil {
return nil, errors.Newf(err, "failed to create fabric network %s on vlan %d", opts.Name, vlanID)
}
return &resp, nil
}
// DeleteFabricNetwork deletes an existing fabric network
// See API docs: https://apidocs.joyent.com/cloudapi/#DeleteFabricNetwork
func (c *Client) DeleteFabricNetwork(vlanID int16, networkID string) error {
req := request{
method: client.DELETE,
url: makeURL(apiFabricVLANs, strconv.Itoa(int(vlanID)), apiFabricNetworks, networkID),
expectedStatus: http.StatusNoContent,
}
if _, err := c.sendRequest(req); err != nil {
return errors.Newf(err, "failed to delete fabric network %s on vlan %d", networkID, vlanID)
}
return nil
}

144
vendor/github.com/joyent/gosdc/cloudapi/firewalls.go generated vendored Normal file
View File

@ -0,0 +1,144 @@
package cloudapi
import (
"net/http"
"github.com/joyent/gocommon/client"
"github.com/joyent/gocommon/errors"
)
// FirewallRule represent a firewall rule that can be specifed for a machine.
type FirewallRule struct {
Id string // Unique identifier for the rule
Enabled bool // Whether the rule is enabled or not
Rule string // Firewall rule in the form 'FROM <target a> TO <target b> <action> <protocol> <port>'
}
// CreateFwRuleOpts represent the option that can be specified
// when creating a new firewall rule.
type CreateFwRuleOpts struct {
Enabled bool `json:"enabled"` // Whether to enable the rule or not
Rule string `json:"rule"` // Firewall rule in the form 'FROM <target a> TO <target b> <action> <protocol> <port>'
}
// ListFirewallRules lists all the firewall rules on record for a specified account.
// See API docs: http://apidocs.joyent.com/cloudapi/#ListFirewallRules
func (c *Client) ListFirewallRules() ([]FirewallRule, error) {
var resp []FirewallRule
req := request{
method: client.GET,
url: apiFirewallRules,
resp: &resp,
}
if _, err := c.sendRequest(req); err != nil {
return nil, errors.Newf(err, "failed to get list of firewall rules")
}
return resp, nil
}
// GetFirewallRule returns the specified firewall rule.
// See API docs: http://apidocs.joyent.com/cloudapi/#GetFirewallRule
func (c *Client) GetFirewallRule(fwRuleID string) (*FirewallRule, error) {
var resp FirewallRule
req := request{
method: client.GET,
url: makeURL(apiFirewallRules, fwRuleID),
resp: &resp,
}
if _, err := c.sendRequest(req); err != nil {
return nil, errors.Newf(err, "failed to get firewall rule with id %s", fwRuleID)
}
return &resp, nil
}
// CreateFirewallRule creates the firewall rule with the specified options.
// See API docs: http://apidocs.joyent.com/cloudapi/#CreateFirewallRule
func (c *Client) CreateFirewallRule(opts CreateFwRuleOpts) (*FirewallRule, error) {
var resp FirewallRule
req := request{
method: client.POST,
url: apiFirewallRules,
reqValue: opts,
resp: &resp,
expectedStatus: http.StatusCreated,
}
if _, err := c.sendRequest(req); err != nil {
return nil, errors.Newf(err, "failed to create firewall rule: %s", opts.Rule)
}
return &resp, nil
}
// UpdateFirewallRule updates the specified firewall rule.
// See API docs: http://apidocs.joyent.com/cloudapi/#UpdateFirewallRule
func (c *Client) UpdateFirewallRule(fwRuleID string, opts CreateFwRuleOpts) (*FirewallRule, error) {
var resp FirewallRule
req := request{
method: client.POST,
url: makeURL(apiFirewallRules, fwRuleID),
reqValue: opts,
resp: &resp,
}
if _, err := c.sendRequest(req); err != nil {
return nil, errors.Newf(err, "failed to update firewall rule with id %s to %s", fwRuleID, opts.Rule)
}
return &resp, nil
}
// EnableFirewallRule enables the given firewall rule record if it is disabled.
// See API docs: http://apidocs.joyent.com/cloudapi/#EnableFirewallRule
func (c *Client) EnableFirewallRule(fwRuleID string) (*FirewallRule, error) {
var resp FirewallRule
req := request{
method: client.POST,
url: makeURL(apiFirewallRules, fwRuleID, apiFirewallRulesEnable),
resp: &resp,
}
if _, err := c.sendRequest(req); err != nil {
return nil, errors.Newf(err, "failed to enable firewall rule with id %s", fwRuleID)
}
return &resp, nil
}
// DisableFirewallRule disables the given firewall rule record if it is enabled.
// See API docs: http://apidocs.joyent.com/cloudapi/#DisableFirewallRule
func (c *Client) DisableFirewallRule(fwRuleID string) (*FirewallRule, error) {
var resp FirewallRule
req := request{
method: client.POST,
url: makeURL(apiFirewallRules, fwRuleID, apiFirewallRulesDisable),
resp: &resp,
}
if _, err := c.sendRequest(req); err != nil {
return nil, errors.Newf(err, "failed to disable firewall rule with id %s", fwRuleID)
}
return &resp, nil
}
// DeleteFirewallRule removes the given firewall rule record from all the required account machines.
// See API docs: http://apidocs.joyent.com/cloudapi/#DeleteFirewallRule
func (c *Client) DeleteFirewallRule(fwRuleID string) error {
req := request{
method: client.DELETE,
url: makeURL(apiFirewallRules, fwRuleID),
expectedStatus: http.StatusNoContent,
}
if _, err := c.sendRequest(req); err != nil {
return errors.Newf(err, "failed to delete firewall rule with id %s", fwRuleID)
}
return nil
}
// ListFirewallRuleMachines return the list of machines affected by the given firewall rule.
// See API docs: http://apidocs.joyent.com/cloudapi/#ListFirewallRuleMachines
func (c *Client) ListFirewallRuleMachines(fwRuleID string) ([]Machine, error) {
var resp []Machine
req := request{
method: client.GET,
url: makeURL(apiFirewallRules, fwRuleID, apiMachines),
resp: &resp,
}
if _, err := c.sendRequest(req); err != nil {
return nil, errors.Newf(err, "failed to get list of machines affected by firewall rule wit id %s", fwRuleID)
}
return resp, nil
}

133
vendor/github.com/joyent/gosdc/cloudapi/images.go generated vendored Normal file
View File

@ -0,0 +1,133 @@
package cloudapi
import (
"fmt"
"net/http"
"github.com/joyent/gocommon/client"
"github.com/joyent/gocommon/errors"
)
// Image represent the software packages that will be available on newly provisioned machines
type Image struct {
Id string // Unique identifier for the image
Name string // Image friendly name
OS string // Underlying operating system
Version string // Image version
Type string // Image type, one of 'smartmachine' or 'virtualmachine'
Description string // Image description
Requirements map[string]interface{} // Minimum requirements for provisioning a machine with this image, e.g. 'password' indicates that a password must be provided
Homepage string // URL for a web page including detailed information for this image (new in API version 7.0)
PublishedAt string `json:"published_at"` // Time this image has been made publicly available (new in API version 7.0)
Public bool // Indicates if the image is publicly available (new in API version 7.1)
State string // Current image state. One of 'active', 'unactivated', 'disabled', 'creating', 'failed' (new in API version 7.1)
Tags map[string]string // A map of key/value pairs that allows clients to categorize images by any given criteria (new in API version 7.1)
EULA string // URL of the End User License Agreement (EULA) for the image (new in API version 7.1)
ACL []string // An array of account UUIDs given access to a private image. The field is only relevant to private images (new in API version 7.1)
Owner string // The UUID of the user owning the image
}
// ExportImageOpts represent the option that can be specified
// when exporting an image.
type ExportImageOpts struct {
MantaPath string `json:"manta_path"` // The Manta path prefix to use when exporting the image
}
// MantaLocation represent the properties that allow a user
// to retrieve the image file and manifest from Manta
type MantaLocation struct {
MantaURL string `json:"manta_url"` // Manta datacenter URL
ImagePath string `json:"image_path"` // Path to the image
ManifestPath string `json:"manifest_path"` // Path to the image manifest
}
// CreateImageFromMachineOpts represent the option that can be specified
// when creating a new image from an existing machine.
type CreateImageFromMachineOpts struct {
Machine string `json:"machine"` // The machine UUID from which the image is to be created
Name string `json:"name"` // Image name
Version string `json:"version"` // Image version
Description string `json:"description,omitempty"` // Image description
Homepage string `json:"homepage,omitempty"` // URL for a web page including detailed information for this image
EULA string `json:"eula,omitempty"` // URL of the End User License Agreement (EULA) for the image
ACL []string `json:"acl,omitempty"` // An array of account UUIDs given access to a private image. The field is only relevant to private images
Tags map[string]string `json:"tags,omitempty"` // A map of key/value pairs that allows clients to categorize images by any given criteria
}
// ListImages provides a list of images available in the datacenter.
// See API docs: http://apidocs.joyent.com/cloudapi/#ListImages
func (c *Client) ListImages(filter *Filter) ([]Image, error) {
var resp []Image
req := request{
method: client.GET,
url: apiImages,
filter: filter,
resp: &resp,
}
if _, err := c.sendRequest(req); err != nil {
return nil, errors.Newf(err, "failed to get list of images")
}
return resp, nil
}
// GetImage returns the image specified by imageId.
// See API docs: http://apidocs.joyent.com/cloudapi/#GetImage
func (c *Client) GetImage(imageID string) (*Image, error) {
var resp Image
req := request{
method: client.GET,
url: makeURL(apiImages, imageID),
resp: &resp,
}
if _, err := c.sendRequest(req); err != nil {
return nil, errors.Newf(err, "failed to get image with id: %s", imageID)
}
return &resp, nil
}
// DeleteImage (Beta) Delete the image specified by imageId. Must be image owner to do so.
// See API docs: http://apidocs.joyent.com/cloudapi/#DeleteImage
func (c *Client) DeleteImage(imageID string) error {
req := request{
method: client.DELETE,
url: makeURL(apiImages, imageID),
expectedStatus: http.StatusNoContent,
}
if _, err := c.sendRequest(req); err != nil {
return errors.Newf(err, "failed to delete image with id: %s", imageID)
}
return nil
}
// ExportImage (Beta) Exports an image to the specified Manta path.
// See API docs: http://apidocs.joyent.com/cloudapi/#ListImages
func (c *Client) ExportImage(imageID string, opts ExportImageOpts) (*MantaLocation, error) {
var resp MantaLocation
req := request{
method: client.POST,
url: fmt.Sprintf("%s/%s?action=%s", apiImages, imageID, actionExport),
reqValue: opts,
resp: &resp,
}
if _, err := c.sendRequest(req); err != nil {
return nil, errors.Newf(err, "failed to export image %s to %s", imageID, opts.MantaPath)
}
return &resp, nil
}
// CreateImageFromMachine (Beta) Create a new custom image from a machine.
// See API docs: http://apidocs.joyent.com/cloudapi/#ListImages
func (c *Client) CreateImageFromMachine(opts CreateImageFromMachineOpts) (*Image, error) {
var resp Image
req := request{
method: client.POST,
url: apiImages,
reqValue: opts,
resp: &resp,
expectedStatus: http.StatusCreated,
}
if _, err := c.sendRequest(req); err != nil {
return nil, errors.Newf(err, "failed to create image from machine %s", opts.Machine)
}
return &resp, nil
}

View File

@ -0,0 +1,216 @@
package cloudapi
import (
"net/http"
"github.com/joyent/gocommon/client"
"github.com/joyent/gocommon/errors"
)
// Analytics represents the available analytics
type Analytics struct {
Modules map[string]interface{} // Namespace to organize metrics
Fields map[string]interface{} // Fields represent metadata by which data points can be filtered or decomposed
Types map[string]interface{} // Types are used with both metrics and fields for two purposes: to hint to clients at how to best label values, and to distinguish between numeric and discrete quantities.
Metrics map[string]interface{} // Metrics describe quantities which can be measured by the system
Transformations map[string]interface{} // Transformations are post-processing functions that can be applied to data when it's retrieved.
}
// Instrumentation specify which metric to collect, how frequently to aggregate data (e.g., every second, every hour, etc.)
// how much data to keep (e.g., 10 minutes' worth, 6 months' worth, etc.) and other configuration options
type Instrumentation struct {
Module string `json:"module"`
Stat string `json:"stat"`
Predicate string `json:"predicate"`
Decomposition []string `json:"decomposition"`
ValueDimension int `json:"value-dimenstion"`
ValueArity string `json:"value-arity"`
RetentionTime int `json:"retention-time"`
Granularity int `json:"granularitiy"`
IdleMax int `json:"idle-max"`
Transformations []string `json:"transformations"`
PersistData bool `json:"persist-data"`
Crtime int `json:"crtime"`
ValueScope string `json:"value-scope"`
Id string `json:"id"`
Uris []Uri `json:"uris"`
}
// Uri represents a Universal Resource Identifier
type Uri struct {
Uri string // Resource identifier
Name string // URI name
}
// InstrumentationValue represents the data associated to an instrumentation for a point in time
type InstrumentationValue struct {
Value interface{}
Transformations map[string]interface{}
StartTime int
Duration int
}
// HeatmapOpts represent the option that can be specified
// when retrieving an instrumentation.'s heatmap
type HeatmapOpts struct {
Height int `json:"height"` // Height of the image in pixels
Width int `json:"width"` // Width of the image in pixels
Ymin int `json:"ymin"` // Y-Axis value for the bottom of the image (default: 0)
Ymax int `json:"ymax"` // Y-Axis value for the top of the image (default: auto)
Nbuckets int `json:"nbuckets"` // Number of buckets in the vertical dimension
Selected []string `json:"selected"` // Array of field values to highlight, isolate or exclude
Isolate bool `json:"isolate"` // If true, only draw selected values
Exclude bool `json:"exclude"` // If true, don't draw selected values at all
Hues []string `json:"hues"` // Array of colors for highlighting selected field values
DecomposeAll bool `json:"decompose_all"` // Highlight all field values
X int `json:"x"`
Y int `json:"y"`
}
// Heatmap represents an instrumentation's heatmap
type Heatmap struct {
BucketTime int `json:"bucket_time"` // Time corresponding to the bucket (Unix seconds)
BucketYmin int `json:"bucket_ymin"` // Minimum y-axis value for the bucket
BucketYmax int `json:"bucket_ymax"` // Maximum y-axis value for the bucket
Present map[string]interface{} `json:"present"` // If the instrumentation defines a discrete decomposition, this property's value is an object whose keys are values of that field and whose values are the number of data points in that bucket for that key
Total int `json:"total"` // The total number of data points in the bucket
}
// CreateInstrumentationOpts represent the option that can be specified
// when creating a new instrumentation.
type CreateInstrumentationOpts struct {
Clone int `json:"clone"` // An existing instrumentation ID to be cloned
Module string `json:"module"` // Analytics module
Stat string `json:"stat"` // Analytics stat
Predicate string `json:"predicate"` // Instrumentation predicate, must be JSON string
Decomposition string `json:"decomposition"`
Granularity int `json:"granularity"` // Number of seconds between data points (default is 1)
RetentionTime int `json:"retention-time"` // How long to keep this instrumentation data for
PersistData bool `json:"persist-data"` // Whether or not to store this for historical analysis
IdleMax int `json:"idle-max"` // Number of seconds after which if the instrumentation or its data has not been accessed via the API the service may delete the instrumentation and its data
}
// DescribeAnalytics retrieves the "schema" for instrumentations that can be created.
// See API docs: http://apidocs.joyent.com/cloudapi/#DescribeAnalytics
func (c *Client) DescribeAnalytics() (*Analytics, error) {
var resp Analytics
req := request{
method: client.GET,
url: apiAnalytics,
resp: &resp,
}
if _, err := c.sendRequest(req); err != nil {
return nil, errors.Newf(err, "failed to get analytics")
}
return &resp, nil
}
// ListInstrumentations retrieves all currently created instrumentations.
// See API docs: http://apidocs.joyent.com/cloudapi/#ListInstrumentations
func (c *Client) ListInstrumentations() ([]Instrumentation, error) {
var resp []Instrumentation
req := request{
method: client.GET,
url: makeURL(apiAnalytics, apiInstrumentations),
resp: &resp,
}
if _, err := c.sendRequest(req); err != nil {
return nil, errors.Newf(err, "failed to get instrumentations")
}
return resp, nil
}
// GetInstrumentation retrieves the configuration for the specified instrumentation.
// See API docs: http://apidocs.joyent.com/cloudapi/#GetInstrumentation
func (c *Client) GetInstrumentation(instrumentationID string) (*Instrumentation, error) {
var resp Instrumentation
req := request{
method: client.GET,
url: makeURL(apiAnalytics, apiInstrumentations, instrumentationID),
resp: &resp,
}
if _, err := c.sendRequest(req); err != nil {
return nil, errors.Newf(err, "failed to get instrumentation with id %s", instrumentationID)
}
return &resp, nil
}
// GetInstrumentationValue retrieves the data associated to an instrumentation
// for a point in time.
// See API docs: http://apidocs.joyent.com/cloudapi/#GetInstrumentationValue
func (c *Client) GetInstrumentationValue(instrumentationID string) (*InstrumentationValue, error) {
var resp InstrumentationValue
req := request{
method: client.GET,
url: makeURL(apiAnalytics, apiInstrumentations, instrumentationID, apiInstrumentationsValue, apiInstrumentationsRaw),
resp: &resp,
}
if _, err := c.sendRequest(req); err != nil {
return nil, errors.Newf(err, "failed to get value for instrumentation with id %s", instrumentationID)
}
return &resp, nil
}
// GetInstrumentationHeatmap retrieves the specified instrumentation's heatmap.
// See API docs: http://apidocs.joyent.com/cloudapi/#GetInstrumentationHeatmap
func (c *Client) GetInstrumentationHeatmap(instrumentationID string) (*Heatmap, error) {
var resp Heatmap
req := request{
method: client.GET,
url: makeURL(apiAnalytics, apiInstrumentations, instrumentationID, apiInstrumentationsValue, apiInstrumentationsHeatmap, apiInstrumentationsImage),
resp: &resp,
}
if _, err := c.sendRequest(req); err != nil {
return nil, errors.Newf(err, "failed to get heatmap image for instrumentation with id %s", instrumentationID)
}
return &resp, nil
}
// GetInstrumentationHeatmapDetails allows you to retrieve the bucket details
// for a heatmap.
// See API docs: http://apidocs.joyent.com/cloudapi/#GetInstrumentationHeatmapDetails
func (c *Client) GetInstrumentationHeatmapDetails(instrumentationID string) (*Heatmap, error) {
var resp Heatmap
req := request{
method: client.GET,
url: makeURL(apiAnalytics, apiInstrumentations, instrumentationID, apiInstrumentationsValue, apiInstrumentationsHeatmap, apiInstrumentationsDetails),
resp: &resp,
}
if _, err := c.sendRequest(req); err != nil {
return nil, errors.Newf(err, "failed to get heatmap details for instrumentation with id %s", instrumentationID)
}
return &resp, nil
}
// CreateInstrumentation Creates an instrumentation. You can clone an existing
// instrumentation by passing in the parameter clone, which should be a numeric id
// of an existing instrumentation.
// See API docs: http://apidocs.joyent.com/cloudapi/#CreateInstrumentation
func (c *Client) CreateInstrumentation(opts CreateInstrumentationOpts) (*Instrumentation, error) {
var resp Instrumentation
req := request{
method: client.POST,
url: makeURL(apiAnalytics, apiInstrumentations),
reqValue: opts,
resp: &resp,
expectedStatus: http.StatusCreated,
}
if _, err := c.sendRequest(req); err != nil {
return nil, errors.Newf(err, "failed to create instrumentation")
}
return &resp, nil
}
// DeleteInstrumentation destroys an instrumentation.
// See API docs: http://apidocs.joyent.com/cloudapi/#DeleteInstrumentation
func (c *Client) DeleteInstrumentation(instrumentationID string) error {
req := request{
method: client.DELETE,
url: makeURL(apiAnalytics, apiInstrumentations, instrumentationID),
expectedStatus: http.StatusNoContent,
}
if _, err := c.sendRequest(req); err != nil {
return errors.Newf(err, "failed to delete instrumentation with id %s", instrumentationID)
}
return nil
}

90
vendor/github.com/joyent/gosdc/cloudapi/keys.go generated vendored Normal file
View File

@ -0,0 +1,90 @@
package cloudapi
import (
"net/http"
"github.com/joyent/gocommon/client"
"github.com/joyent/gocommon/errors"
)
// Key represent a public key
type Key struct {
Name string // Name for the key
Fingerprint string // Key Fingerprint
Key string // OpenSSH formatted public key
}
/*func (k Key) Equals(other Key) bool {
if k.Name == other.Name && k.Fingerprint == other.Fingerprint && k.Key == other.Key {
return true
}
return false
}*/
// CreateKeyOpts represent the option that can be specified
// when creating a new key.
type CreateKeyOpts struct {
Name string `json:"name"` // Name for the key, optional
Key string `json:"key"` // OpenSSH formatted public key
}
// ListKeys returns a list of public keys registered with a specific account.
// See API docs: http://apidocs.joyent.com/cloudapi/#ListKeys
func (c *Client) ListKeys() ([]Key, error) {
var resp []Key
req := request{
method: client.GET,
url: apiKeys,
resp: &resp,
}
if _, err := c.sendRequest(req); err != nil {
return nil, errors.Newf(err, "failed to get list of keys")
}
return resp, nil
}
// GetKey returns the key identified by keyName.
// See API docs: http://apidocs.joyent.com/cloudapi/#GetKey
func (c *Client) GetKey(keyName string) (*Key, error) {
var resp Key
req := request{
method: client.GET,
url: makeURL(apiKeys, keyName),
resp: &resp,
}
if _, err := c.sendRequest(req); err != nil {
return nil, errors.Newf(err, "failed to get key with name: %s", keyName)
}
return &resp, nil
}
// CreateKey creates a new key with the specified options.
// See API docs: http://apidocs.joyent.com/cloudapi/#CreateKey
func (c *Client) CreateKey(opts CreateKeyOpts) (*Key, error) {
var resp Key
req := request{
method: client.POST,
url: apiKeys,
reqValue: opts,
resp: &resp,
expectedStatus: http.StatusCreated,
}
if _, err := c.sendRequest(req); err != nil {
return nil, errors.Newf(err, "failed to create key with name: %s", opts.Name)
}
return &resp, nil
}
// DeleteKey deletes the key identified by keyName.
// See API docs: http://apidocs.joyent.com/cloudapi/#DeleteKey
func (c *Client) DeleteKey(keyName string) error {
req := request{
method: client.DELETE,
url: makeURL(apiKeys, keyName),
expectedStatus: http.StatusNoContent,
}
if _, err := c.sendRequest(req); err != nil {
return errors.Newf(err, "failed to delete key with name: %s", keyName)
}
return nil
}

View File

@ -0,0 +1,52 @@
package cloudapi
import (
"fmt"
"net/http"
"github.com/joyent/gocommon/client"
"github.com/joyent/gocommon/errors"
)
// ListMachineFirewallRules lists all the firewall rules for the specified machine.
// See API docs: http://apidocs.joyent.com/cloudapi/#ListMachineFirewallRules
func (c *Client) ListMachineFirewallRules(machineID string) ([]FirewallRule, error) {
var resp []FirewallRule
req := request{
method: client.GET,
url: makeURL(apiMachines, machineID, apiFirewallRules),
resp: &resp,
}
if _, err := c.sendRequest(req); err != nil {
return nil, errors.Newf(err, "failed to get list of firewall rules for machine with id %s", machineID)
}
return resp, nil
}
// EnableFirewallMachine enables the firewall for the specified machine.
// See API docs: http://apidocs.joyent.com/cloudapi/#EnableMachineFirewall
func (c *Client) EnableFirewallMachine(machineID string) error {
req := request{
method: client.POST,
url: fmt.Sprintf("%s/%s?action=%s", apiMachines, machineID, actionEnableFw),
expectedStatus: http.StatusAccepted,
}
if _, err := c.sendRequest(req); err != nil {
return errors.Newf(err, "failed to enable firewall on machine with id: %s", machineID)
}
return nil
}
// DisableFirewallMachine disables the firewall for the specified machine.
// See API docs: http://apidocs.joyent.com/cloudapi/#DisableMachineFirewall
func (c *Client) DisableFirewallMachine(machineID string) error {
req := request{
method: client.POST,
url: fmt.Sprintf("%s/%s?action=%s", apiMachines, machineID, actionDisableFw),
expectedStatus: http.StatusAccepted,
}
if _, err := c.sendRequest(req); err != nil {
return errors.Newf(err, "failed to disable firewall on machine with id: %s", machineID)
}
return nil
}

View File

@ -0,0 +1,70 @@
package cloudapi
import (
"net/http"
"github.com/joyent/gocommon/client"
"github.com/joyent/gocommon/errors"
)
// UpdateMachineMetadata updates the metadata for a given machine.
// Any metadata keys passed in here are created if they do not exist, and
// overwritten if they do.
// See API docs: http://apidocs.joyent.com/cloudapi/#UpdateMachineMetadata
func (c *Client) UpdateMachineMetadata(machineID string, metadata map[string]string) (map[string]interface{}, error) {
var resp map[string]interface{}
req := request{
method: client.POST,
url: makeURL(apiMachines, machineID, apiMetadata),
reqValue: metadata,
resp: &resp,
}
if _, err := c.sendRequest(req); err != nil {
return nil, errors.Newf(err, "failed to update metadata for machine with id %s", machineID)
}
return resp, nil
}
// GetMachineMetadata returns the complete set of metadata associated with the
// specified machine.
// See API docs: http://apidocs.joyent.com/cloudapi/#GetMachineMetadata
func (c *Client) GetMachineMetadata(machineID string) (map[string]interface{}, error) {
var resp map[string]interface{}
req := request{
method: client.GET,
url: makeURL(apiMachines, machineID, apiMetadata),
resp: &resp,
}
if _, err := c.sendRequest(req); err != nil {
return nil, errors.Newf(err, "failed to get list of metadata for machine with id %s", machineID)
}
return resp, nil
}
// DeleteMachineMetadata deletes a single metadata key from the specified machine.
// See API docs: http://apidocs.joyent.com/cloudapi/#DeleteMachineMetadata
func (c *Client) DeleteMachineMetadata(machineID, metadataKey string) error {
req := request{
method: client.DELETE,
url: makeURL(apiMachines, machineID, apiMetadata, metadataKey),
expectedStatus: http.StatusNoContent,
}
if _, err := c.sendRequest(req); err != nil {
return errors.Newf(err, "failed to delete metadata with key %s for machine with id %s", metadataKey, machineID)
}
return nil
}
// DeleteAllMachineMetadata deletes all metadata keys from the specified machine.
// See API docs: http://apidocs.joyent.com/cloudapi/#DeleteAllMachineMetadata
func (c *Client) DeleteAllMachineMetadata(machineID string) error {
req := request{
method: client.DELETE,
url: makeURL(apiMachines, machineID, apiMetadata),
expectedStatus: http.StatusNoContent,
}
if _, err := c.sendRequest(req); err != nil {
return errors.Newf(err, "failed to delete metadata for machine with id %s", machineID)
}
return nil
}

View File

@ -0,0 +1,95 @@
package cloudapi
import (
"net/http"
"github.com/joyent/gocommon/client"
"github.com/joyent/gocommon/errors"
)
// NICState represents the state of a NIC
type NICState string
var (
NICStateProvisioning NICState = "provisioning"
NICStateRunning NICState = "running"
NICStateStopped NICState = "stopped"
)
// NIC represents a NIC on a machine
type NIC struct {
IP string `json:"ip"` // NIC's IPv4 Address
MAC string `json:"mac"` // NIC's MAC address
Primary bool `json:"primary"` // Whether this is the machine's primary NIC
Netmask string `json:"netmask"` // IPv4 netmask
Gateway string `json:"gateway"` // IPv4 gateway
State NICState `json:"state"` // Describes the state of the NIC (e.g. provisioning, running, or stopped)
Network string `json:"network"` // Network ID this NIC is attached to
}
type addNICOptions struct {
Network string `json:"network"` // UUID of network this NIC should attach to
}
// ListNICs lists all the NICs on a machine belonging to a given account
// See API docs: https://apidocs.joyent.com/cloudapi/#ListNics
func (c *Client) ListNICs(machineID string) ([]NIC, error) {
var resp []NIC
req := request{
method: client.GET,
url: makeURL(apiMachines, machineID, apiNICs),
resp: &resp,
}
if _, err := c.sendRequest(req); err != nil {
return nil, errors.Newf(err, "failed to list NICs")
}
return resp, nil
}
// GetNIC gets a specific NIC on a machine belonging to a given account
// See API docs: https://apidocs.joyent.com/cloudapi/#GetNic
func (c *Client) GetNIC(machineID, MAC string) (*NIC, error) {
resp := new(NIC)
req := request{
method: client.GET,
url: makeURL(apiMachines, machineID, apiNICs, MAC),
resp: resp,
}
if _, err := c.sendRequest(req); err != nil {
return nil, errors.Newf(err, "failed to get NIC with MAC: %s", MAC)
}
return resp, nil
}
// AddNIC creates a new NIC on a machine belonging to a given account.
// *WARNING*: this causes the machine to reboot while adding the NIC.
// See API docs: https://apidocs.joyent.com/cloudapi/#AddNic
func (c *Client) AddNIC(machineID, networkID string) (*NIC, error) {
resp := new(NIC)
req := request{
method: client.POST,
url: makeURL(apiMachines, machineID, apiNICs),
reqValue: addNICOptions{networkID},
resp: resp,
expectedStatus: http.StatusCreated,
}
if _, err := c.sendRequest(req); err != nil {
return nil, errors.Newf(err, "failed to add NIC to machine %s on network: %s", machineID, networkID)
}
return resp, nil
}
// RemoveNIC removes a NIC on a machine belonging to a given account.
// *WARNING*: this causes the machine to reboot while removing the NIC.
// See API docs: https://apidocs.joyent.com/cloudapi/#RemoveNic
func (c *Client) RemoveNIC(machineID, MAC string) error {
req := request{
method: client.DELETE,
url: makeURL(apiMachines, machineID, apiNICs, MAC),
expectedStatus: http.StatusNoContent,
}
if _, err := c.sendRequest(req); err != nil {
return errors.Newf(err, "failed to remove NIC: %s", MAC)
}
return nil
}

View File

@ -0,0 +1,96 @@
package cloudapi
import (
"net/http"
"github.com/joyent/gocommon/client"
"github.com/joyent/gocommon/errors"
)
// Snapshot represent a point in time state of a machine.
type Snapshot struct {
Name string // Snapshot name
State string // Snapshot state
}
// SnapshotOpts represent the option that can be specified
// when creating a new machine snapshot.
type SnapshotOpts struct {
Name string `json:"name"` // Snapshot name
}
// CreateMachineSnapshot creates a new snapshot for the machine with the options specified.
// See API docs: http://apidocs.joyent.com/cloudapi/#CreateMachineSnapshot
func (c *Client) CreateMachineSnapshot(machineID string, opts SnapshotOpts) (*Snapshot, error) {
var resp Snapshot
req := request{
method: client.POST,
url: makeURL(apiMachines, machineID, apiSnapshots),
reqValue: opts,
resp: &resp,
expectedStatus: http.StatusCreated,
}
if _, err := c.sendRequest(req); err != nil {
return nil, errors.Newf(err, "failed to create snapshot %s from machine with id %s", opts.Name, machineID)
}
return &resp, nil
}
// StartMachineFromSnapshot starts the machine from the specified snapshot.
// Machine must be in 'stopped' state.
// See API docs: http://apidocs.joyent.com/cloudapi/#StartMachineFromSnapshot
func (c *Client) StartMachineFromSnapshot(machineID, snapshotName string) error {
req := request{
method: client.POST,
url: makeURL(apiMachines, machineID, apiSnapshots, snapshotName),
expectedStatus: http.StatusAccepted,
}
if _, err := c.sendRequest(req); err != nil {
return errors.Newf(err, "failed to start machine with id %s from snapshot %s", machineID, snapshotName)
}
return nil
}
// ListMachineSnapshots lists all snapshots for the specified machine.
// See API docs: http://apidocs.joyent.com/cloudapi/#ListMachineSnapshots
func (c *Client) ListMachineSnapshots(machineID string) ([]Snapshot, error) {
var resp []Snapshot
req := request{
method: client.GET,
url: makeURL(apiMachines, machineID, apiSnapshots),
resp: &resp,
}
if _, err := c.sendRequest(req); err != nil {
return nil, errors.Newf(err, "failed to get list of snapshots for machine with id %s", machineID)
}
return resp, nil
}
// GetMachineSnapshot returns the state of the specified snapshot.
// See API docs: http://apidocs.joyent.com/cloudapi/#GetMachineSnapshot
func (c *Client) GetMachineSnapshot(machineID, snapshotName string) (*Snapshot, error) {
var resp Snapshot
req := request{
method: client.GET,
url: makeURL(apiMachines, machineID, apiSnapshots, snapshotName),
resp: &resp,
}
if _, err := c.sendRequest(req); err != nil {
return nil, errors.Newf(err, "failed to get snapshot %s for machine with id %s", snapshotName, machineID)
}
return &resp, nil
}
// DeleteMachineSnapshot deletes the specified snapshot.
// See API docs: http://apidocs.joyent.com/cloudapi/#DeleteMachineSnapshot
func (c *Client) DeleteMachineSnapshot(machineID, snapshotName string) error {
req := request{
method: client.DELETE,
url: makeURL(apiMachines, machineID, apiSnapshots, snapshotName),
expectedStatus: http.StatusNoContent,
}
if _, err := c.sendRequest(req); err != nil {
return errors.Newf(err, "failed to delete snapshot %s for machine with id %s", snapshotName, machineID)
}
return nil
}

103
vendor/github.com/joyent/gosdc/cloudapi/machine_tags.go generated vendored Normal file
View File

@ -0,0 +1,103 @@
package cloudapi
import (
"net/http"
"github.com/joyent/gocommon/client"
"github.com/joyent/gocommon/errors"
)
// AddMachineTags adds additional tags to the specified machine.
// This API lets you append new tags, not overwrite existing tags.
// See API docs: http://apidocs.joyent.com/cloudapi/#AddMachineTags
func (c *Client) AddMachineTags(machineID string, tags map[string]string) (map[string]string, error) {
var resp map[string]string
req := request{
method: client.POST,
url: makeURL(apiMachines, machineID, apiTags),
reqValue: tags,
resp: &resp,
}
if _, err := c.sendRequest(req); err != nil {
return nil, errors.Newf(err, "failed to add tags for machine with id %s", machineID)
}
return resp, nil
}
// ReplaceMachineTags replaces existing tags for the specified machine.
// This API lets you overwrite existing tags, not append to existing tags.
// See API docs: http://apidocs.joyent.com/cloudapi/#ReplaceMachineTags
func (c *Client) ReplaceMachineTags(machineID string, tags map[string]string) (map[string]string, error) {
var resp map[string]string
req := request{
method: client.PUT,
url: makeURL(apiMachines, machineID, apiTags),
reqValue: tags,
resp: &resp,
}
if _, err := c.sendRequest(req); err != nil {
return nil, errors.Newf(err, "failed to replace tags for machine with id %s", machineID)
}
return resp, nil
}
// ListMachineTags returns the complete set of tags associated with the specified machine.
// See API docs: http://apidocs.joyent.com/cloudapi/#ListMachineTags
func (c *Client) ListMachineTags(machineID string) (map[string]string, error) {
var resp map[string]string
req := request{
method: client.GET,
url: makeURL(apiMachines, machineID, apiTags),
resp: &resp,
}
if _, err := c.sendRequest(req); err != nil {
return nil, errors.Newf(err, "failed to get list of tags for machine with id %s", machineID)
}
return resp, nil
}
// GetMachineTag returns the value for a single tag on the specified machine.
// See API docs: http://apidocs.joyent.com/cloudapi/#GetMachineTag
func (c *Client) GetMachineTag(machineID, tagKey string) (string, error) {
var resp []byte
requestHeaders := make(http.Header)
requestHeaders.Set("Accept", "text/plain")
req := request{
method: client.GET,
url: makeURL(apiMachines, machineID, apiTags, tagKey),
resp: &resp,
reqHeader: requestHeaders,
}
if _, err := c.sendRequest(req); err != nil {
return "", errors.Newf(err, "failed to get tag %s for machine with id %s", tagKey, machineID)
}
return string(resp), nil
}
// DeleteMachineTag deletes a single tag from the specified machine.
// See API docs: http://apidocs.joyent.com/cloudapi/#DeleteMachineTag
func (c *Client) DeleteMachineTag(machineID, tagKey string) error {
req := request{
method: client.DELETE,
url: makeURL(apiMachines, machineID, apiTags, tagKey),
expectedStatus: http.StatusNoContent,
}
if _, err := c.sendRequest(req); err != nil {
return errors.Newf(err, "failed to delete tag with key %s for machine with id %s", tagKey, machineID)
}
return nil
}
// DeleteMachineTags deletes all tags from the specified machine.
// See API docs: http://apidocs.joyent.com/cloudapi/#DeleteMachineTags
func (c *Client) DeleteMachineTags(machineID string) error {
req := request{
method: client.DELETE,
url: makeURL(apiMachines, machineID, apiTags),
expectedStatus: http.StatusNoContent,
}
if _, err := c.sendRequest(req); err != nil {
return errors.Newf(err, "failed to delete tags for machine with id %s", machineID)
}
return nil
}

306
vendor/github.com/joyent/gosdc/cloudapi/machines.go generated vendored Normal file
View File

@ -0,0 +1,306 @@
package cloudapi
import (
"encoding/json"
"fmt"
"net/http"
"strings"
"github.com/joyent/gocommon/client"
"github.com/joyent/gocommon/errors"
)
// Machine represent a provisioned virtual machines
type Machine struct {
Id string // Unique identifier for the image
Name string // Machine friendly name
Type string // Machine type, one of 'smartmachine' or 'virtualmachine'
State string // Current state of the machine
Dataset string // The dataset URN the machine was provisioned with. For new images/datasets this value will be the dataset id, i.e, same value than the image attribute
Memory int // The amount of memory the machine has (in Mb)
Disk int // The amount of disk the machine has (in Gb)
IPs []string // The IP addresses the machine has
Metadata map[string]string // Map of the machine metadata, e.g. authorized-keys
Tags map[string]string // Map of the machine tags
Created string // When the machine was created
Updated string // When the machine was updated
Package string // The name of the package used to create the machine
Image string // The image id the machine was provisioned with
PrimaryIP string // The primary (public) IP address for the machine
Networks []string // The network IDs for the machine
FirewallEnabled bool `json:"firewall_enabled"` // whether or not the firewall is enabled
}
// Equals compares two machines. Ignores state and timestamps.
func (m Machine) Equals(other Machine) bool {
if m.Id == other.Id && m.Name == other.Name && m.Type == other.Type && m.Dataset == other.Dataset &&
m.Memory == other.Memory && m.Disk == other.Disk && m.Package == other.Package && m.Image == other.Image &&
m.compareIPs(other) && m.compareMetadata(other) {
return true
}
return false
}
// Helper method to compare two machines IPs
func (m Machine) compareIPs(other Machine) bool {
if len(m.IPs) != len(other.IPs) {
return false
}
for i, v := range m.IPs {
if v != other.IPs[i] {
return false
}
}
return true
}
// Helper method to compare two machines metadata
func (m Machine) compareMetadata(other Machine) bool {
if len(m.Metadata) != len(other.Metadata) {
return false
}
for k, v := range m.Metadata {
if v != other.Metadata[k] {
return false
}
}
return true
}
// CreateMachineOpts represent the option that can be specified
// when creating a new machine.
type CreateMachineOpts struct {
Name string `json:"name"` // Machine friendly name, default is a randomly generated name
Package string `json:"package"` // Name of the package to use on provisioning
Image string `json:"image"` // The image UUID
Networks []string `json:"networks"` // Desired networks IDs
Metadata map[string]string `json:"-"` // An arbitrary set of metadata key/value pairs can be set at provision time
Tags map[string]string `json:"-"` // An arbitrary set of tags can be set at provision time
FirewallEnabled bool `json:"firewall_enabled"` // Completely enable or disable firewall for this machine (new in API version 7.0)
}
// AuditAction represents an action/event accomplished by a machine.
type AuditAction struct {
Action string // Action name
Parameters map[string]interface{} // Original set of parameters sent when the action was requested
Time string // When the action finished
Success string // Either 'yes' or 'no', depending on the action successfulness
Caller Caller // Account requesting the action
}
// Caller represents an account requesting an action.
type Caller struct {
Type string // Authentication type for the action request. One of 'basic', 'operator', 'signature' or 'token'
User string // When the authentication type is 'basic', this member will be present and include user login
IP string // The IP addresses this from which the action was requested. Not present if type is 'operator'
KeyId string // When authentication type is either 'signature' or 'token', SSH key identifier
}
// appendJSON marshals the given attribute value and appends it as an encoded value to the given json data.
// The newly encode (attr, value) is inserted just before the closing "}" in the json data.
func appendJSON(data []byte, attr string, value interface{}) ([]byte, error) {
newData, err := json.Marshal(&value)
if err != nil {
return nil, err
}
strData := string(data)
result := fmt.Sprintf(`%s, "%s":%s}`, strData[:len(strData)-1], attr, string(newData))
return []byte(result), nil
}
type jsonOpts CreateMachineOpts
// MarshalJSON turns the given CreateMachineOpts into JSON
func (opts CreateMachineOpts) MarshalJSON() ([]byte, error) {
jo := jsonOpts(opts)
data, err := json.Marshal(&jo)
if err != nil {
return nil, err
}
for k, v := range opts.Tags {
if !strings.HasPrefix(k, "tag.") {
k = "tag." + k
}
data, err = appendJSON(data, k, v)
if err != nil {
return nil, err
}
}
for k, v := range opts.Metadata {
if !strings.HasPrefix(k, "metadata.") {
k = "metadata." + k
}
data, err = appendJSON(data, k, v)
if err != nil {
return nil, err
}
}
return data, nil
}
// ListMachines lists all machines on record for an account.
// You can paginate this API by passing in offset, and limit
// See API docs: http://apidocs.joyent.com/cloudapi/#ListMachines
func (c *Client) ListMachines(filter *Filter) ([]Machine, error) {
var resp []Machine
req := request{
method: client.GET,
url: apiMachines,
filter: filter,
resp: &resp,
}
if _, err := c.sendRequest(req); err != nil {
return nil, errors.Newf(err, "failed to get list of machines")
}
return resp, nil
}
// CountMachines returns the number of machines on record for an account.
// See API docs: http://apidocs.joyent.com/cloudapi/#ListMachines
func (c *Client) CountMachines() (int, error) {
var resp int
req := request{
method: client.HEAD,
url: apiMachines,
resp: &resp,
}
if _, err := c.sendRequest(req); err != nil {
return -1, errors.Newf(err, "failed to get count of machines")
}
return resp, nil
}
// GetMachine returns the machine specified by machineId.
// See API docs: http://apidocs.joyent.com/cloudapi/#GetMachine
func (c *Client) GetMachine(machineID string) (*Machine, error) {
var resp Machine
req := request{
method: client.GET,
url: makeURL(apiMachines, machineID),
resp: &resp,
}
if _, err := c.sendRequest(req); err != nil {
return nil, errors.Newf(err, "failed to get machine with id: %s", machineID)
}
return &resp, nil
}
// CreateMachine creates a new machine with the options specified.
// See API docs: http://apidocs.joyent.com/cloudapi/#CreateMachine
func (c *Client) CreateMachine(opts CreateMachineOpts) (*Machine, error) {
var resp Machine
req := request{
method: client.POST,
url: apiMachines,
reqValue: opts,
resp: &resp,
expectedStatus: http.StatusCreated,
}
if _, err := c.sendRequest(req); err != nil {
return nil, errors.Newf(err, "failed to create machine with name: %s", opts.Name)
}
return &resp, nil
}
// StopMachine stops a running machine.
// See API docs: http://apidocs.joyent.com/cloudapi/#StopMachine
func (c *Client) StopMachine(machineID string) error {
req := request{
method: client.POST,
url: fmt.Sprintf("%s/%s?action=%s", apiMachines, machineID, actionStop),
expectedStatus: http.StatusAccepted,
}
if _, err := c.sendRequest(req); err != nil {
return errors.Newf(err, "failed to stop machine with id: %s", machineID)
}
return nil
}
// StartMachine starts a stopped machine.
// See API docs: http://apidocs.joyent.com/cloudapi/#StartMachine
func (c *Client) StartMachine(machineID string) error {
req := request{
method: client.POST,
url: fmt.Sprintf("%s/%s?action=%s", apiMachines, machineID, actionStart),
expectedStatus: http.StatusAccepted,
}
if _, err := c.sendRequest(req); err != nil {
return errors.Newf(err, "failed to start machine with id: %s", machineID)
}
return nil
}
// RebootMachine reboots (stop followed by a start) a machine.
// See API docs: http://apidocs.joyent.com/cloudapi/#RebootMachine
func (c *Client) RebootMachine(machineID string) error {
req := request{
method: client.POST,
url: fmt.Sprintf("%s/%s?action=%s", apiMachines, machineID, actionReboot),
expectedStatus: http.StatusAccepted,
}
if _, err := c.sendRequest(req); err != nil {
return errors.Newf(err, "failed to reboot machine with id: %s", machineID)
}
return nil
}
// ResizeMachine allows you to resize a SmartMachine. Virtual machines can also
// be resized, but only resizing virtual machines to a higher capacity package
// is supported.
// See API docs: http://apidocs.joyent.com/cloudapi/#ResizeMachine
func (c *Client) ResizeMachine(machineID, packageName string) error {
req := request{
method: client.POST,
url: fmt.Sprintf("%s/%s?action=%s&package=%s", apiMachines, machineID, actionResize, packageName),
expectedStatus: http.StatusAccepted,
}
if _, err := c.sendRequest(req); err != nil {
return errors.Newf(err, "failed to resize machine with id: %s", machineID)
}
return nil
}
// RenameMachine renames an existing machine.
// See API docs: http://apidocs.joyent.com/cloudapi/#RenameMachine
func (c *Client) RenameMachine(machineID, machineName string) error {
req := request{
method: client.POST,
url: fmt.Sprintf("%s/%s?action=%s&name=%s", apiMachines, machineID, actionRename, machineName),
expectedStatus: http.StatusAccepted,
}
if _, err := c.sendRequest(req); err != nil {
return errors.Newf(err, "failed to rename machine with id: %s", machineID)
}
return nil
}
// DeleteMachine allows you to completely destroy a machine. Machine must be in the 'stopped' state.
// See API docs: http://apidocs.joyent.com/cloudapi/#DeleteMachine
func (c *Client) DeleteMachine(machineID string) error {
req := request{
method: client.DELETE,
url: makeURL(apiMachines, machineID),
expectedStatus: http.StatusNoContent,
}
if _, err := c.sendRequest(req); err != nil {
return errors.Newf(err, "failed to delete machine with id %s", machineID)
}
return nil
}
// MachineAudit provides a list of machine's accomplished actions, (sorted from
// latest to older one).
// See API docs: http://apidocs.joyent.com/cloudapi/#MachineAudit
func (c *Client) MachineAudit(machineID string) ([]AuditAction, error) {
var resp []AuditAction
req := request{
method: client.GET,
url: makeURL(apiMachines, machineID, apiAudit),
resp: &resp,
}
if _, err := c.sendRequest(req); err != nil {
return nil, errors.Newf(err, "failed to get actions for machine with id %s", machineID)
}
return resp, nil
}

44
vendor/github.com/joyent/gosdc/cloudapi/networks.go generated vendored Normal file
View File

@ -0,0 +1,44 @@
package cloudapi
import (
"github.com/joyent/gocommon/client"
"github.com/joyent/gocommon/errors"
)
// Network represents a network available to a given account
type Network struct {
Id string // Unique identifier for the network
Name string // Network name
Public bool // Whether this a public or private (rfc1918) network
Description string // Optional description for this network, when name is not enough
}
// ListNetworks lists all the networks which can be used by the given account.
// See API docs: http://apidocs.joyent.com/cloudapi/#ListNetworks
func (c *Client) ListNetworks() ([]Network, error) {
var resp []Network
req := request{
method: client.GET,
url: apiNetworks,
resp: &resp,
}
if _, err := c.sendRequest(req); err != nil {
return nil, errors.Newf(err, "failed to get list of networks")
}
return resp, nil
}
// GetNetwork retrieves an individual network record.
// See API docs: http://apidocs.joyent.com/cloudapi/#GetNetwork
func (c *Client) GetNetwork(networkID string) (*Network, error) {
var resp Network
req := request{
method: client.GET,
url: makeURL(apiNetworks, networkID),
resp: &resp,
}
if _, err := c.sendRequest(req); err != nil {
return nil, errors.Newf(err, "failed to get network with id %s", networkID)
}
return &resp, nil
}

53
vendor/github.com/joyent/gosdc/cloudapi/packages.go generated vendored Normal file
View File

@ -0,0 +1,53 @@
package cloudapi
import (
"github.com/joyent/gocommon/client"
"github.com/joyent/gocommon/errors"
)
// Package represents a named collections of resources that are used to describe the 'sizes'
// of either a smart machine or a virtual machine.
type Package struct {
Name string // Name for the package
Memory int // Memory available (in Mb)
Disk int // Disk space available (in Gb)
Swap int // Swap memory available (in Mb)
VCPUs int // Number of VCPUs for the package
Default bool // Indicates whether this is the default package in the datacenter
Id string // Unique identifier for the package
Version string // Version for the package
Group string // Group this package belongs to
Description string // Human friendly description for the package
}
// ListPackages provides a list of packages available in the datacenter.
// See API docs: http://apidocs.joyent.com/cloudapi/#ListPackages
func (c *Client) ListPackages(filter *Filter) ([]Package, error) {
var resp []Package
req := request{
method: client.GET,
url: apiPackages,
filter: filter,
resp: &resp,
}
if _, err := c.sendRequest(req); err != nil {
return nil, errors.Newf(err, "failed to get list of packages")
}
return resp, nil
}
// GetPackage returns the package specified by packageName. NOTE: packageName can
// specify either the package name or package ID.
// See API docs: http://apidocs.joyent.com/cloudapi/#GetPackage
func (c *Client) GetPackage(packageName string) (*Package, error) {
var resp Package
req := request{
method: client.GET,
url: makeURL(apiPackages, packageName),
resp: &resp,
}
if _, err := c.sendRequest(req); err != nil {
return nil, errors.Newf(err, "failed to get package with name: %s", packageName)
}
return &resp, nil
}

20
vendor/github.com/joyent/gosdc/cloudapi/services.go generated vendored Normal file
View File

@ -0,0 +1,20 @@
package cloudapi
import (
"github.com/joyent/gocommon/client"
"github.com/joyent/gocommon/errors"
)
// list available services
func (c *Client) ListServices() (map[string]string, error) {
var resp map[string]string
req := request{
method: client.GET,
url: apiServices,
resp: &resp,
}
if _, err := c.sendRequest(req); err != nil {
return nil, errors.Newf(err, "failed to get list of services")
}
return resp, nil
}

14
vendor/github.com/joyent/gosdc/gosdc.go generated vendored Normal file
View File

@ -0,0 +1,14 @@
/*
Package gosdc enables Go programs to interact with the Joyent CloudAPI.
The gosdc package is structured as follow:
- gosdc/cloudapi. This package interacts with the Cloud API (http://apidocs.joyent.com/cloudapi/).
- gosdc/localservices. This package provides local services to be used for testing.
Licensed under the Mozilla Public License version 2.0
Copyright (c) Joyent Inc.
*/
package gosdc

46
vendor/github.com/joyent/gosdc/wercker.yml generated vendored Normal file
View File

@ -0,0 +1,46 @@
box: golang
# services:
# - postgres
# http://devcenter.wercker.com/docs/services/postgresql.html
# - mongodb
# http://devcenter.wercker.com/docs/services/mongodb.html
build:
# The steps that will be executed on build
# Steps make up the actions in your pipeline
# Read more about steps on our dev center:
# http://devcenter.wercker.com/docs/steps/index.html
steps:
# Sets the go workspace and places you package
# at the right place in the workspace tree
- setup-go-workspace:
package-dir: github.com/joyent/gosdc
# Gets the dependencies
- script:
name: go get
code: |
go get -v -t ./...
- script:
name: make a new key for testing
code: |
ssh-keygen -b 2048 \
-C "Testing Key" \
-f $WERCKER_SOURCE_DIR/test_key.id_rsa \
-t rsa \
-P ""
# Build the project
- script:
name: go build
code: |
go build ./...
# Test the project
- script:
name: go test
code: |
env KEY_NAME=$WERCKER_SOURCE_DIR/test_key.id_rsa LIVE=false go test ./...

373
vendor/github.com/joyent/gosign/LICENSE generated vendored Executable file
View File

@ -0,0 +1,373 @@
Mozilla Public License Version 2.0
==================================
1. Definitions
--------------
1.1. "Contributor"
means each individual or legal entity that creates, contributes to
the creation of, or owns Covered Software.
1.2. "Contributor Version"
means the combination of the Contributions of others (if any) used
by a Contributor and that particular Contributor's Contribution.
1.3. "Contribution"
means Covered Software of a particular Contributor.
1.4. "Covered Software"
means Source Code Form to which the initial Contributor has attached
the notice in Exhibit A, the Executable Form of such Source Code
Form, and Modifications of such Source Code Form, in each case
including portions thereof.
1.5. "Incompatible With Secondary Licenses"
means
(a) that the initial Contributor has attached the notice described
in Exhibit B to the Covered Software; or
(b) that the Covered Software was made available under the terms of
version 1.1 or earlier of the License, but not also under the
terms of a Secondary License.
1.6. "Executable Form"
means any form of the work other than Source Code Form.
1.7. "Larger Work"
means a work that combines Covered Software with other material, in
a separate file or files, that is not Covered Software.
1.8. "License"
means this document.
1.9. "Licensable"
means having the right to grant, to the maximum extent possible,
whether at the time of the initial grant or subsequently, any and
all of the rights conveyed by this License.
1.10. "Modifications"
means any of the following:
(a) any file in Source Code Form that results from an addition to,
deletion from, or modification of the contents of Covered
Software; or
(b) any new file in Source Code Form that contains any Covered
Software.
1.11. "Patent Claims" of a Contributor
means any patent claim(s), including without limitation, method,
process, and apparatus claims, in any patent Licensable by such
Contributor that would be infringed, but for the grant of the
License, by the making, using, selling, offering for sale, having
made, import, or transfer of either its Contributions or its
Contributor Version.
1.12. "Secondary License"
means either the GNU General Public License, Version 2.0, the GNU
Lesser General Public License, Version 2.1, the GNU Affero General
Public License, Version 3.0, or any later versions of those
licenses.
1.13. "Source Code Form"
means the form of the work preferred for making modifications.
1.14. "You" (or "Your")
means an individual or a legal entity exercising rights under this
License. For legal entities, "You" includes any entity that
controls, is controlled by, or is under common control with You. For
purposes of this definition, "control" means (a) the power, direct
or indirect, to cause the direction or management of such entity,
whether by contract or otherwise, or (b) ownership of more than
fifty percent (50%) of the outstanding shares or beneficial
ownership of such entity.
2. License Grants and Conditions
--------------------------------
2.1. Grants
Each Contributor hereby grants You a world-wide, royalty-free,
non-exclusive license:
(a) under intellectual property rights (other than patent or trademark)
Licensable by such Contributor to use, reproduce, make available,
modify, display, perform, distribute, and otherwise exploit its
Contributions, either on an unmodified basis, with Modifications, or
as part of a Larger Work; and
(b) under Patent Claims of such Contributor to make, use, sell, offer
for sale, have made, import, and otherwise transfer either its
Contributions or its Contributor Version.
2.2. Effective Date
The licenses granted in Section 2.1 with respect to any Contribution
become effective for each Contribution on the date the Contributor first
distributes such Contribution.
2.3. Limitations on Grant Scope
The licenses granted in this Section 2 are the only rights granted under
this License. No additional rights or licenses will be implied from the
distribution or licensing of Covered Software under this License.
Notwithstanding Section 2.1(b) above, no patent license is granted by a
Contributor:
(a) for any code that a Contributor has removed from Covered Software;
or
(b) for infringements caused by: (i) Your and any other third party's
modifications of Covered Software, or (ii) the combination of its
Contributions with other software (except as part of its Contributor
Version); or
(c) under Patent Claims infringed by Covered Software in the absence of
its Contributions.
This License does not grant any rights in the trademarks, service marks,
or logos of any Contributor (except as may be necessary to comply with
the notice requirements in Section 3.4).
2.4. Subsequent Licenses
No Contributor makes additional grants as a result of Your choice to
distribute the Covered Software under a subsequent version of this
License (see Section 10.2) or under the terms of a Secondary License (if
permitted under the terms of Section 3.3).
2.5. Representation
Each Contributor represents that the Contributor believes its
Contributions are its original creation(s) or it has sufficient rights
to grant the rights to its Contributions conveyed by this License.
2.6. Fair Use
This License is not intended to limit any rights You have under
applicable copyright doctrines of fair use, fair dealing, or other
equivalents.
2.7. Conditions
Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted
in Section 2.1.
3. Responsibilities
-------------------
3.1. Distribution of Source Form
All distribution of Covered Software in Source Code Form, including any
Modifications that You create or to which You contribute, must be under
the terms of this License. You must inform recipients that the Source
Code Form of the Covered Software is governed by the terms of this
License, and how they can obtain a copy of this License. You may not
attempt to alter or restrict the recipients' rights in the Source Code
Form.
3.2. Distribution of Executable Form
If You distribute Covered Software in Executable Form then:
(a) such Covered Software must also be made available in Source Code
Form, as described in Section 3.1, and You must inform recipients of
the Executable Form how they can obtain a copy of such Source Code
Form by reasonable means in a timely manner, at a charge no more
than the cost of distribution to the recipient; and
(b) You may distribute such Executable Form under the terms of this
License, or sublicense it under different terms, provided that the
license for the Executable Form does not attempt to limit or alter
the recipients' rights in the Source Code Form under this License.
3.3. Distribution of a Larger Work
You may create and distribute a Larger Work under terms of Your choice,
provided that You also comply with the requirements of this License for
the Covered Software. If the Larger Work is a combination of Covered
Software with a work governed by one or more Secondary Licenses, and the
Covered Software is not Incompatible With Secondary Licenses, this
License permits You to additionally distribute such Covered Software
under the terms of such Secondary License(s), so that the recipient of
the Larger Work may, at their option, further distribute the Covered
Software under the terms of either this License or such Secondary
License(s).
3.4. Notices
You may not remove or alter the substance of any license notices
(including copyright notices, patent notices, disclaimers of warranty,
or limitations of liability) contained within the Source Code Form of
the Covered Software, except that You may alter any license notices to
the extent required to remedy known factual inaccuracies.
3.5. Application of Additional Terms
You may choose to offer, and to charge a fee for, warranty, support,
indemnity or liability obligations to one or more recipients of Covered
Software. However, You may do so only on Your own behalf, and not on
behalf of any Contributor. You must make it absolutely clear that any
such warranty, support, indemnity, or liability obligation is offered by
You alone, and You hereby agree to indemnify every Contributor for any
liability incurred by such Contributor as a result of warranty, support,
indemnity or liability terms You offer. You may include additional
disclaimers of warranty and limitations of liability specific to any
jurisdiction.
4. Inability to Comply Due to Statute or Regulation
---------------------------------------------------
If it is impossible for You to comply with any of the terms of this
License with respect to some or all of the Covered Software due to
statute, judicial order, or regulation then You must: (a) comply with
the terms of this License to the maximum extent possible; and (b)
describe the limitations and the code they affect. Such description must
be placed in a text file included with all distributions of the Covered
Software under this License. Except to the extent prohibited by statute
or regulation, such description must be sufficiently detailed for a
recipient of ordinary skill to be able to understand it.
5. Termination
--------------
5.1. The rights granted under this License will terminate automatically
if You fail to comply with any of its terms. However, if You become
compliant, then the rights granted under this License from a particular
Contributor are reinstated (a) provisionally, unless and until such
Contributor explicitly and finally terminates Your grants, and (b) on an
ongoing basis, if such Contributor fails to notify You of the
non-compliance by some reasonable means prior to 60 days after You have
come back into compliance. Moreover, Your grants from a particular
Contributor are reinstated on an ongoing basis if such Contributor
notifies You of the non-compliance by some reasonable means, this is the
first time You have received notice of non-compliance with this License
from such Contributor, and You become compliant prior to 30 days after
Your receipt of the notice.
5.2. If You initiate litigation against any entity by asserting a patent
infringement claim (excluding declaratory judgment actions,
counter-claims, and cross-claims) alleging that a Contributor Version
directly or indirectly infringes any patent, then the rights granted to
You by any and all Contributors for the Covered Software under Section
2.1 of this License shall terminate.
5.3. In the event of termination under Sections 5.1 or 5.2 above, all
end user license agreements (excluding distributors and resellers) which
have been validly granted by You or Your distributors under this License
prior to termination shall survive termination.
************************************************************************
* *
* 6. Disclaimer of Warranty *
* ------------------------- *
* *
* Covered Software is provided under this License on an "as is" *
* basis, without warranty of any kind, either expressed, implied, or *
* statutory, including, without limitation, warranties that the *
* Covered Software is free of defects, merchantable, fit for a *
* particular purpose or non-infringing. The entire risk as to the *
* quality and performance of the Covered Software is with You. *
* Should any Covered Software prove defective in any respect, You *
* (not any Contributor) assume the cost of any necessary servicing, *
* repair, or correction. This disclaimer of warranty constitutes an *
* essential part of this License. No use of any Covered Software is *
* authorized under this License except under this disclaimer. *
* *
************************************************************************
************************************************************************
* *
* 7. Limitation of Liability *
* -------------------------- *
* *
* Under no circumstances and under no legal theory, whether tort *
* (including negligence), contract, or otherwise, shall any *
* Contributor, or anyone who distributes Covered Software as *
* permitted above, be liable to You for any direct, indirect, *
* special, incidental, or consequential damages of any character *
* including, without limitation, damages for lost profits, loss of *
* goodwill, work stoppage, computer failure or malfunction, or any *
* and all other commercial damages or losses, even if such party *
* shall have been informed of the possibility of such damages. This *
* limitation of liability shall not apply to liability for death or *
* personal injury resulting from such party's negligence to the *
* extent applicable law prohibits such limitation. Some *
* jurisdictions do not allow the exclusion or limitation of *
* incidental or consequential damages, so this exclusion and *
* limitation may not apply to You. *
* *
************************************************************************
8. Litigation
-------------
Any litigation relating to this License may be brought only in the
courts of a jurisdiction where the defendant maintains its principal
place of business and such litigation shall be governed by laws of that
jurisdiction, without reference to its conflict-of-law provisions.
Nothing in this Section shall prevent a party's ability to bring
cross-claims or counter-claims.
9. Miscellaneous
----------------
This License represents the complete agreement concerning the subject
matter hereof. If any provision of this License is held to be
unenforceable, such provision shall be reformed only to the extent
necessary to make it enforceable. Any law or regulation which provides
that the language of a contract shall be construed against the drafter
shall not be used to construe this License against a Contributor.
10. Versions of the License
---------------------------
10.1. New Versions
Mozilla Foundation is the license steward. Except as provided in Section
10.3, no one other than the license steward has the right to modify or
publish new versions of this License. Each version will be given a
distinguishing version number.
10.2. Effect of New Versions
You may distribute the Covered Software under the terms of the version
of the License under which You originally received the Covered Software,
or under the terms of any subsequent version published by the license
steward.
10.3. Modified Versions
If you create software not governed by this License, and you want to
create a new license for such software, you may create and use a
modified version of this License if you rename the license and remove
any references to the name of the license steward (except to note that
such modified license differs from this License).
10.4. Distributing Source Code Form that is Incompatible With Secondary
Licenses
If You choose to distribute Source Code Form that is Incompatible With
Secondary Licenses under the terms of this version of the License, the
notice described in Exhibit B of this License must be attached.
Exhibit A - Source Code Form License Notice
-------------------------------------------
This Source Code Form is subject to the terms of the Mozilla Public
License, v. 2.0. If a copy of the MPL was not distributed with this
file, You can obtain one at http://mozilla.org/MPL/2.0/.
If it is not possible or desirable to put the notice in a particular
file, then You may include the notice in a location (such as a LICENSE
file in a relevant directory) where a recipient would be likely to look
for such a notice.
You may add additional accurate notices of copyright ownership.
Exhibit B - "Incompatible With Secondary Licenses" Notice
---------------------------------------------------------
This Source Code Form is "Incompatible With Secondary Licenses", as
defined by the Mozilla Public License, v. 2.0.

73
vendor/github.com/joyent/gosign/README.md generated vendored Normal file
View File

@ -0,0 +1,73 @@
gosign
======
Go HTTP signing library for Joyent's Triton and Manta.
## Installation
Use `go-get` to install gosign
```
go get github.com/joyent/gosign
```
## Documentation
Documentation can be found on godoc.
- [github.com/joyent/gosign](http://godoc.org/github.com/joyent/gosign)
- [github.com/joyent/gosign/auth](http://godoc.org/github.com/joyent/gosign/auth)
## Contributing
Report bugs and request features using [GitHub Issues](https://github.com/joyent/gosign/issues), or contribute code via a [GitHub Pull Request](https://github.com/joyent/gosign/pulls). Changes will be code reviewed before merging. In the near future, automated tests will be run, but in the meantime please `go fmt`, `go lint`, and test all contributions.
## Developing
This library assumes a Go development environment setup based on [How to Write Go Code](https://golang.org/doc/code.html). Your GOPATH environment variable should be pointed at your workspace directory.
You can now use `go get github.com/joyent/gosign` to install the repository to the correct location, but if you are intending on contributing back a change you may want to consider cloning the repository via git yourself. This way you can have a single source tree for all Joyent Go projects with each repo having two remotes -- your own fork on GitHub and the upstream origin.
For example if your GOPATH is `~/src/joyent/go` and you're working on multiple repos then that directory tree might look like:
```
~/src/joyent/go/
|_ pkg/
|_ src/
|_ github.com
|_ joyent
|_ gocommon
|_ gomanta
|_ gosdc
|_ gosign
```
### Recommended Setup
```
$ mkdir -p ${GOPATH}/src/github.com/joyent
$ cd ${GOPATH}/src/github.com/joyent
$ git clone git@github.com:<yourname>/gosign.git
$ cd gosign
$ git remote add upstream git@github.com:joyent/gosign.git
$ git remote -v
origin git@github.com:<yourname>/gosign.git (fetch)
origin git@github.com:<yourname>/gosign.git (push)
upstream git@github.com:joyent/gosign.git (fetch)
upstream git@github.com:joyent/gosign.git (push)
```
### Run Tests
```
cd ${GOPATH}/src/github.com/joyent/gosign
go test ./...
```
### Build the Library
```
cd ${GOPATH}/src/github.com/joyent/gosign
go build ./...
```

135
vendor/github.com/joyent/gosign/auth/auth.go generated vendored Normal file
View File

@ -0,0 +1,135 @@
//
// gosign - Go HTTP signing library for the Joyent Public Cloud and Joyent Manta
//
//
// Copyright (c) 2013 Joyent Inc.
//
// Written by Daniele Stroppa <daniele.stroppa@joyent.com>
//
package auth
import (
"crypto"
"crypto/rand"
"crypto/rsa"
"crypto/x509"
"encoding/base64"
"encoding/pem"
"fmt"
"net/http"
"net/url"
"strings"
)
const (
// Authorization Headers
SdcSignature = "Signature keyId=\"/%s/keys/%s\",algorithm=\"%s\" %s"
MantaSignature = "Signature keyId=\"/%s/keys/%s\",algorithm=\"%s\",signature=\"%s\""
)
type Endpoint struct {
URL string
}
type Auth struct {
User string
PrivateKey PrivateKey
Algorithm string
}
type Credentials struct {
UserAuthentication *Auth
SdcKeyId string
SdcEndpoint Endpoint
MantaKeyId string
MantaEndpoint Endpoint
}
type PrivateKey struct {
key *rsa.PrivateKey
}
// NewAuth creates a new Auth.
func NewAuth(user, privateKey, algorithm string) (*Auth, error) {
block, _ := pem.Decode([]byte(privateKey))
if block == nil {
return nil, fmt.Errorf("invalid private key data: %s", privateKey)
}
rsakey, err := x509.ParsePKCS1PrivateKey(block.Bytes)
if err != nil {
return nil, fmt.Errorf("An error occurred while parsing the key: %s", err)
}
return &Auth{user, PrivateKey{rsakey}, algorithm}, nil
}
// The CreateAuthorizationHeader returns the Authorization header for the give request.
func CreateAuthorizationHeader(headers http.Header, credentials *Credentials, isMantaRequest bool) (string, error) {
if isMantaRequest {
signature, err := GetSignature(credentials.UserAuthentication, "date: "+headers.Get("Date"))
if err != nil {
return "", err
}
return fmt.Sprintf(MantaSignature, credentials.UserAuthentication.User, credentials.MantaKeyId,
credentials.UserAuthentication.Algorithm, signature), nil
}
signature, err := GetSignature(credentials.UserAuthentication, headers.Get("Date"))
if err != nil {
return "", err
}
return fmt.Sprintf(SdcSignature, credentials.UserAuthentication.User, credentials.SdcKeyId,
credentials.UserAuthentication.Algorithm, signature), nil
}
// The GetSignature method signs the specified key according to http://apidocs.joyent.com/cloudapi/#issuing-requests
// and http://apidocs.joyent.com/manta/api.html#authentication.
func GetSignature(auth *Auth, signing string) (string, error) {
hashFunc := getHashFunction(auth.Algorithm)
hash := hashFunc.New()
hash.Write([]byte(signing))
digest := hash.Sum(nil)
signed, err := rsa.SignPKCS1v15(rand.Reader, auth.PrivateKey.key, hashFunc, digest)
if err != nil {
return "", fmt.Errorf("An error occurred while signing the key: %s", err)
}
return base64.StdEncoding.EncodeToString(signed), nil
}
// Helper method to get the Hash function based on the algorithm
func getHashFunction(algorithm string) (hashFunc crypto.Hash) {
switch strings.ToLower(algorithm) {
case "rsa-sha1":
hashFunc = crypto.SHA1
case "rsa-sha224", "rsa-sha256":
hashFunc = crypto.SHA256
case "rsa-sha384", "rsa-sha512":
hashFunc = crypto.SHA512
default:
hashFunc = crypto.SHA256
}
return
}
func (cred *Credentials) Region() string {
sdcUrl := cred.SdcEndpoint.URL
if isLocalhost(sdcUrl) {
return "some-region"
}
return sdcUrl[strings.LastIndex(sdcUrl, "/")+1 : strings.Index(sdcUrl, ".")]
}
func isLocalhost(u string) bool {
parsedUrl, err := url.Parse(u)
if err != nil {
return false
}
if strings.HasPrefix(parsedUrl.Host, "localhost") || strings.HasPrefix(parsedUrl.Host, "127.0.0.1") {
return true
}
return false
}

16
vendor/github.com/joyent/gosign/gosign.go generated vendored Normal file
View File

@ -0,0 +1,16 @@
/*
* The sign package enables Go programs to create signed requests for
* the Joyent Public Cloud and Joyent Manta services.
*
* The sign package is structured as follow:
*
* - gosign/auth. This package deals with the authorization and signature of requests.
*
* Copyright (c) 2016 Joyent Inc.
* Written by Daniele Stroppa <daniele.stroppa@joyent.com>
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package gosign

15
vendor/vendor.json vendored
View File

@ -512,6 +512,21 @@
"path": "github.com/jmespath/go-jmespath",
"revision": "c01cf91b011868172fdcd9f41838e80c9d716264"
},
{
"checksumSHA1": "NOwNdnb70M6s9LvhaPFabBVwlBs=",
"path": "github.com/joyent/gocommon",
"revision": "b78708995d1c2ebdb64a3061b0bca5d8ccdf0fc2"
},
{
"checksumSHA1": "zg/V3yqR59RlBEhTqjW7AhEp16o=",
"path": "github.com/joyent/gosdc",
"revision": "ec8b3503a75edca0df26581b83807677b0240716"
},
{
"checksumSHA1": "xVAU7r1t8lxJ1AHvgNNXxwU+jIo=",
"path": "github.com/joyent/gosign",
"revision": "9abcee278795b82b36858cdfc857c8a0e7de797c"
},
{
"checksumSHA1": "6nmAJBw2phU9MUmkUnqFvbO5urg=",
"path": "github.com/kardianos/osext",

View File

@ -0,0 +1,150 @@
---
description: |
The `triton` Packer builder is able to create new images for use with Triton.
These images can be used with both the Joyent public cloud (which is powered by
Triton) as well with private Triton installations. This builder uses the Triton
Cloud API to create images. The builder creates and launches a temporary VM
based on a specified source image, runs any provisioning necessary, uses the
Triton "VM to image" functionality to create a reusable image and finally
destroys the temporary VM. This reusable image can then be used to launch new
VM's.
page_title: Triton Builder
...
# Triton Builder
Type: `triton`
The `triton` Packer builder is able to create new images for use with Triton.
These images can be used with both the [Joyent public
cloud](https://www.joyent.com/) (which is powered by Triton) as well with
private [Triton](https://github.com/joyent/triton) installations.
This builder uses the Triton Cloud API to create these images. Triton also
supports the Docker API however this builder does *not*. If you want to create
Docker images on Triton you should use the Packer Docker builder.
The builder creates and launches a temporary VM based on a specified source
image, runs any provisioning necessary, uses the Triton "VM to image"
functionality to create a reusable image and finally destroys the temporary VM.
This reusable image can then be used to launch new VM's.
The builder does *not* manage images. Once it creates an image, it is up to you
to use it or delete it.
## Configuration Reference
There are many configuration options available for the builder. They are
segmented below into two categories: required and optional parameters.
In addition to the options listed here, a
[communicator](/docs/templates/communicator.html) can be configured for this
builder.
### Required:
- `triton_account` (string) - The username of the Triton account to use when
using the Triton Cloud API.
- `triton_key_id` (string) - The fingerprint of the public key of the SSH key
pair to use for authentication with the Triton Cloud API.
- `triton_key_material` (string) - Path to the file in which the private key
of `triton_key_id` is stored. For example `~/.ssh/id_rsa`.
- `source_machine_image` (string) - The UUID of the image to base the new
image on. Triton supports multiple types of images, called 'brands' in
Triton / Joyent lingo, for contains and VM's. See the chapter [Containers
and virtual machines](https://docs.joyent.com/public-cloud/instances) in the
Joyent Triton documentation for detailed information. The following brands
are currently supported by this builder:`joyent` and`kvm`. The choice of
base image automatically decides the brand. On the Joyent public cloud a
valid `source_machine_image` could for example be
`70e3ae72-96b6-11e6-9056-9737fd4d0764` for version 16.3.1 of the 64bit
SmartOS base image (a 'joyent' brand image).
- `source_machine_package` (string) - The Triton package to use while building
the image. Does not affect (and does not have to be the same) as the package
which will be used for a VM instance running this image. On the Joyent
public cloud this could for example be `g3-standard-0.5-smartos`.
- `image_name` (string) - The name the finished image in Triton will be
assigned. Maximum 512 characters but should in practice be much shorter
(think between 5 and 20 characters). For example `postgresql-95-server` for
an image used as a PostgreSQL 9.5 server.
- `image_version` (string) - The version string for this image. Maximum 128
characters. Any string will do but a format of `Major.Minor.Patch` is
strongly advised by Joyent. See [Semantic Versioning](http://semver.org/)
for more information on the `Major.Minor.Patch` versioning format.
### Optional:
- `triton_url` (string) - The URL of the Triton cloud API to use. If omitted
it will default to the URL of the Joyent Public cloud. If you are using your
own private Triton installation you will have to supply the URL of the cloud
API of your own Triton installation.
- `source_machine_firewall_enabled` (boolean) - Whether or not the firewall of
the VM used to create an image of is enabled. The Triton firewall only
filters inbound traffic to the VM. All outbound traffic is always allowed.
Currently this builder does not provide an interface to add specific
firewall rules. Unless you have a global rule defined in Triton which allows
SSH traffic enabling the firewall will interfere with the SSH provisioner.
The default is `false`.
- `source_machine_metadata` (object of key/value strings) - Triton metadata
applied to the VM used to create the image. Metadata can be used to pass
configuration information to the VM without the need for networking. See
[Using the metadata
API](https://docs.joyent.com/private-cloud/instances/using-mdata) in the
Joyent documentation for more information. This can for example be used to
set the `user-script` metadata key to have Triton start a user supplied
script after the VM has booted.
- `source_machine_name` (string) - Name of the VM used for building the image.
Does not affect (and does not have to be the same) as the name for a VM
instance running this image. Maximum 512 characters but should in practice
be much shorter (think between 5 and 20 characters). For example
`mysql-64-server-image-builder`. When omitted defaults to
`packer-builder-[image_name]`.
- `source_machine_networks` (array of strings) - The UUID's of Triton networks
added to the source machine used for creating the image. For example if any
of the provisioners which are run need Internet access you will need to add
the UUID's of the appropriate networks here.
- `source_machine_tags` (object of key/value strings) - Tags applied to the VM
used to create the image.
- `image_acls` (array of strings) - The UUID's of the users which will have
access to this image. When omitted only the owner (the Triton user whose
credentials are used) will have access to the image.
- `image_description` (string) - Description of the image. Maximum 512
characters.
- `image_eula_url` (string) - URL of the End User License Agreement (EULA) for
the image. Maximum 128 characters.
- `image_homepage` (string) - URL of the homepage where users can find
information about the image. Maximum 128 characters.
- `image_tags` (object of key/value strings) - Tag applied to the image.
## Basic Example
Below is a minimal example to create an joyent-brand image on the Joyent public
cloud:
``` {.javascript}
"builders": [{
"type": "triton",
"triton_account": "triton_username",
"triton_key_id": "6b:95:03:3d:d3:6e:52:69:01:96:1a:46:4a:8d:c1:7e",
"triton_key_material": "~/.ssh/id_rsa",
"source_machine_name": "image-builder",
"source_machine_package": "g3-standard-0.5-smartos",
"source_machine_image": "70e3ae72-96b6-11e6-9056-9737fd4d0764",
"ssh_username": "root",
"ssh_private_key_file": "~/.ssh/id_rsa",
"image_name": "my_new_image",
"image_version": "1.0.0",
}],
```
In the above example the SSH key used for `triton_key_material` (connecting to
the Cloud API) and the `ssh_private_key_file` (connecting to the VM once it has
started) are the same. This is because Triton automatically configures the root
users to be able to login via SSH with the same key used to create the VM via
the Cloud API. In more advanced scenarios for example when using a
`source_machine_image` one might use different credentials.

View File

@ -49,6 +49,7 @@
<li><a href="/docs/builders/parallels.html">Parallels</a></li>
<li><a href="/docs/builders/profitbricks.html">ProfitBricks</a></li>
<li><a href="/docs/builders/qemu.html">QEMU</a></li>
<li><a href="/docs/builders/triton.html">Triton (Joyent Public Cloud)</a></li>
<li><a href="/docs/builders/virtualbox.html">VirtualBox</a></li>
<li><a href="/docs/builders/vmware.html">VMware</a></li>
<li><a href="/docs/builders/custom.html">Custom</a></li>