diff --git a/Godeps/Godeps.json b/Godeps/Godeps.json
index afd346bd6..544a4988f 100644
--- a/Godeps/Godeps.json
+++ b/Godeps/Godeps.json
@@ -740,6 +740,22 @@
"Comment": "0.2.2-2-gc01cf91",
"Rev": "c01cf91b011868172fdcd9f41838e80c9d716264"
},
+ {
+ "ImportPath": "github.com/joyent/gocommon",
+ "Rev": "40c7818502f7c1ebbb13dab185a26e77b746ff40"
+ },
+ {
+ "ImportPath": "github.com/joyent/gosdc/cloudapi",
+ "Rev": "e5af42dc255d490169eaea98c610cf7660913844"
+ },
+ {
+ "ImportPath": "github.com/joyent/gosign/auth",
+ "Rev": "0da0d5f1342065321c97812b1f4ac0c2b0bab56c"
+ },
+ {
+ "ImportPath": "github.com/juju/loggo",
+ "Rev": "8477fc936adf0e382d680310047ca27e128a309a"
+ },
{
"ImportPath": "github.com/kardianos/osext",
"Rev": "29ae4ffbc9a6fe9fb2bc5029050ce6996ea1d3bc"
diff --git a/builtin/bins/provider-triton/main.go b/builtin/bins/provider-triton/main.go
new file mode 100644
index 000000000..d9e40fad6
--- /dev/null
+++ b/builtin/bins/provider-triton/main.go
@@ -0,0 +1,12 @@
+package main
+
+import (
+ "github.com/hashicorp/terraform/builtin/providers/triton"
+ "github.com/hashicorp/terraform/plugin"
+)
+
+func main() {
+ plugin.Serve(&plugin.ServeOpts{
+ ProviderFunc: triton.Provider,
+ })
+}
diff --git a/builtin/providers/triton/provider.go b/builtin/providers/triton/provider.go
new file mode 100644
index 000000000..b776f81c2
--- /dev/null
+++ b/builtin/providers/triton/provider.go
@@ -0,0 +1,118 @@
+package triton
+
+import (
+ "fmt"
+
+ "github.com/hashicorp/go-multierror"
+ "github.com/hashicorp/terraform/helper/schema"
+ "github.com/hashicorp/terraform/terraform"
+ "github.com/joyent/gocommon/client"
+ "github.com/joyent/gosdc/cloudapi"
+ "github.com/joyent/gosign/auth"
+)
+
+// Provider returns a terraform.ResourceProvider.
+func Provider() terraform.ResourceProvider {
+ return &schema.Provider{
+ Schema: map[string]*schema.Schema{
+ "account": &schema.Schema{
+ Type: schema.TypeString,
+ Required: true,
+ DefaultFunc: schema.EnvDefaultFunc("SDC_ACCOUNT", ""),
+ },
+
+ "url": &schema.Schema{
+ Type: schema.TypeString,
+ Required: true,
+ DefaultFunc: schema.EnvDefaultFunc("SDC_URL", "https://us-west-1.api.joyentcloud.com"),
+ },
+
+ "key_material": &schema.Schema{
+ Type: schema.TypeString,
+ Required: true,
+ DefaultFunc: schema.EnvDefaultFunc("SDC_KEY_MATERIAL", ""),
+ },
+
+ "key_id": &schema.Schema{
+ Type: schema.TypeString,
+ Required: true,
+ DefaultFunc: schema.EnvDefaultFunc("SDC_KEY_ID", ""),
+ },
+ },
+
+ ResourcesMap: map[string]*schema.Resource{
+ "triton_firewall_rule": resourceFirewallRule(),
+ "triton_machine": resourceMachine(),
+ "triton_key": resourceKey(),
+ },
+ ConfigureFunc: providerConfigure,
+ }
+}
+
+type SDCConfig struct {
+ Account string
+ KeyMaterial string
+ KeyID string
+ URL string
+}
+
+func (c SDCConfig) validate() error {
+ var err *multierror.Error
+
+ if c.URL == "" {
+ err = multierror.Append(err, fmt.Errorf("URL must be configured for the Triton provider"))
+ }
+ if c.KeyMaterial == "" {
+ err = multierror.Append(err, fmt.Errorf("Key Material must be configured for the Triton provider"))
+ }
+ if c.KeyID == "" {
+ err = multierror.Append(err, fmt.Errorf("Key ID must be configured for the Triton provider"))
+ }
+ if c.Account == "" {
+ err = multierror.Append(err, fmt.Errorf("Account must be configured for the Triton provider"))
+ }
+
+ return err.ErrorOrNil()
+}
+
+func (c SDCConfig) getSDCClient() (*cloudapi.Client, error) {
+ userauth, err := auth.NewAuth(c.Account, c.KeyMaterial, "rsa-sha256")
+ if err != nil {
+ return nil, err
+ }
+
+ creds := &auth.Credentials{
+ UserAuthentication: userauth,
+ SdcKeyId: c.KeyID,
+ SdcEndpoint: auth.Endpoint{URL: c.URL},
+ }
+
+ client := cloudapi.New(client.NewClient(
+ c.URL,
+ cloudapi.DefaultAPIVersion,
+ creds,
+ &cloudapi.Logger,
+ ))
+
+ return client, nil
+}
+
+func providerConfigure(d *schema.ResourceData) (interface{}, error) {
+ config := SDCConfig{
+ Account: d.Get("account").(string),
+ URL: d.Get("url").(string),
+ KeyMaterial: d.Get("key_material").(string),
+ KeyID: d.Get("key_id").(string),
+ }
+
+ if err := config.validate(); err != nil {
+ return nil, err
+ }
+
+ client, err := config.getSDCClient()
+ if err != nil {
+ return nil, err
+ }
+
+ return client, nil
+}
diff --git a/builtin/providers/triton/provider_test.go b/builtin/providers/triton/provider_test.go
new file mode 100644
index 000000000..2aa283ec3
--- /dev/null
+++ b/builtin/providers/triton/provider_test.go
@@ -0,0 +1,44 @@
+package triton
+
+import (
+ "os"
+ "testing"
+
+ "github.com/hashicorp/terraform/helper/schema"
+ "github.com/hashicorp/terraform/terraform"
+)
+
+var testAccProviders map[string]terraform.ResourceProvider
+var testAccProvider *schema.Provider
+
+func init() {
+ testAccProvider = Provider().(*schema.Provider)
+ testAccProviders = map[string]terraform.ResourceProvider{
+ "triton": testAccProvider,
+ }
+}
+
+func TestProvider(t *testing.T) {
+ if err := Provider().(*schema.Provider).InternalValidate(); err != nil {
+ t.Fatalf("err: %s", err)
+ }
+}
+
+func TestProvider_impl(t *testing.T) {
+ var _ terraform.ResourceProvider = Provider()
+}
+
+func testAccPreCheck(t *testing.T) {
+ sdcURL := os.Getenv("SDC_URL")
+ account := os.Getenv("SDC_ACCOUNT")
+ keyID := os.Getenv("SDC_KEY_ID")
+ keyMaterial := os.Getenv("SDC_KEY_MATERIAL")
+
+ if sdcURL == "" {
+ sdcURL = "https://us-west-1.api.joyentcloud.com"
+ }
+
+ if sdcURL == "" || account == "" || keyID == "" || keyMaterial == "" {
+ t.Fatal("SDC_ACCOUNT, SDC_KEY_ID and SDC_KEY_MATERIAL must be set for acceptance tests")
+ }
+}
diff --git a/builtin/providers/triton/resource_firewall_rule.go b/builtin/providers/triton/resource_firewall_rule.go
new file mode 100644
index 000000000..dc45236b0
--- /dev/null
+++ b/builtin/providers/triton/resource_firewall_rule.go
@@ -0,0 +1,101 @@
+package triton
+
+import (
+ "github.com/hashicorp/terraform/helper/schema"
+ "github.com/joyent/gosdc/cloudapi"
+)
+
+func resourceFirewallRule() *schema.Resource {
+ return &schema.Resource{
+ Create: resourceFirewallRuleCreate,
+ Exists: resourceFirewallRuleExists,
+ Read: resourceFirewallRuleRead,
+ Update: resourceFirewallRuleUpdate,
+ Delete: resourceFirewallRuleDelete,
+
+ Schema: map[string]*schema.Schema{
+ "rule": {
+ Description: "firewall rule text",
+ Type: schema.TypeString,
+ Required: true,
+ },
+ "enabled": {
+ Description: "Indicates if the rule is enabled",
+ Type: schema.TypeBool,
+ Optional: true,
+ Default: false,
+ },
+ },
+ }
+}
+
+func resourceFirewallRuleCreate(d *schema.ResourceData, meta interface{}) error {
+ client := meta.(*cloudapi.Client)
+
+ rule, err := client.CreateFirewallRule(cloudapi.CreateFwRuleOpts{
+ Rule: d.Get("rule").(string),
+ Enabled: d.Get("enabled").(bool),
+ })
+ if err != nil {
+ return err
+ }
+
+ d.SetId(rule.Id)
+
+ err = resourceFirewallRuleRead(d, meta)
+ if err != nil {
+ return err
+ }
+
+ return nil
+}
+
+func resourceFirewallRuleExists(d *schema.ResourceData, meta interface{}) (bool, error) {
+ client := meta.(*cloudapi.Client)
+
+ rule, err := client.GetFirewallRule(d.Id())
+
+ return rule != nil && err == nil, err
+}
+
+func resourceFirewallRuleRead(d *schema.ResourceData, meta interface{}) error {
+ client := meta.(*cloudapi.Client)
+
+ rule, err := client.GetFirewallRule(d.Id())
+ if err != nil {
+ return err
+ }
+
+ d.SetId(rule.Id)
+ d.Set("rule", rule.Rule)
+ d.Set("enabled", rule.Enabled)
+
+ return nil
+}
+
+func resourceFirewallRuleUpdate(d *schema.ResourceData, meta interface{}) error {
+ client := meta.(*cloudapi.Client)
+
+ _, err := client.UpdateFirewallRule(
+ d.Id(),
+ cloudapi.CreateFwRuleOpts{
+ Rule: d.Get("rule").(string),
+ Enabled: d.Get("enabled").(bool),
+ },
+ )
+ if err != nil {
+ return err
+ }
+
+ return resourceFirewallRuleRead(d, meta)
+}
+
+func resourceFirewallRuleDelete(d *schema.ResourceData, meta interface{}) error {
+ client := meta.(*cloudapi.Client)
+
+ if err := client.DeleteFirewallRule(d.Id()); err != nil {
+ return err
+ }
+
+ return nil
+}
diff --git a/builtin/providers/triton/resource_firewall_rule_test.go b/builtin/providers/triton/resource_firewall_rule_test.go
new file mode 100644
index 000000000..2c3c8cdde
--- /dev/null
+++ b/builtin/providers/triton/resource_firewall_rule_test.go
@@ -0,0 +1,152 @@
+package triton
+
+import (
+ "fmt"
+ "testing"
+
+ "github.com/hashicorp/terraform/helper/resource"
+ "github.com/hashicorp/terraform/terraform"
+ "github.com/joyent/gosdc/cloudapi"
+)
+
+func TestAccTritonFirewallRule_basic(t *testing.T) {
+ config := testAccTritonFirewallRule_basic
+
+ resource.Test(t, resource.TestCase{
+ PreCheck: func() { testAccPreCheck(t) },
+ Providers: testAccProviders,
+ CheckDestroy: testCheckTritonFirewallRuleDestroy,
+ Steps: []resource.TestStep{
+ resource.TestStep{
+ Config: config,
+ Check: resource.ComposeTestCheckFunc(
+ testCheckTritonFirewallRuleExists("triton_firewall_rule.test"),
+ ),
+ },
+ },
+ })
+}
+
+func TestAccTritonFirewallRule_update(t *testing.T) {
+ preConfig := testAccTritonFirewallRule_basic
+ postConfig := testAccTritonFirewallRule_update
+
+ resource.Test(t, resource.TestCase{
+ PreCheck: func() { testAccPreCheck(t) },
+ Providers: testAccProviders,
+ CheckDestroy: testCheckTritonFirewallRuleDestroy,
+ Steps: []resource.TestStep{
+ resource.TestStep{
+ Config: preConfig,
+ Check: resource.ComposeTestCheckFunc(
+ testCheckTritonFirewallRuleExists("triton_firewall_rule.test"),
+ resource.TestCheckResourceAttr("triton_firewall_rule.test", "rule", "FROM any TO tag www ALLOW tcp PORT 80"),
+ resource.TestCheckResourceAttr("triton_firewall_rule.test", "enabled", "false"),
+ ),
+ },
+
+ resource.TestStep{
+ Config: postConfig,
+ Check: resource.ComposeTestCheckFunc(
+ testCheckTritonFirewallRuleExists("triton_firewall_rule.test"),
+ resource.TestCheckResourceAttr("triton_firewall_rule.test", "rule", "FROM any TO tag www BLOCK tcp PORT 80"),
+ resource.TestCheckResourceAttr("triton_firewall_rule.test", "enabled", "true"),
+ ),
+ },
+ },
+ })
+}
+
+func TestAccTritonFirewallRule_enable(t *testing.T) {
+ preConfig := testAccTritonFirewallRule_basic
+ postConfig := testAccTritonFirewallRule_enable
+
+ resource.Test(t, resource.TestCase{
+ PreCheck: func() { testAccPreCheck(t) },
+ Providers: testAccProviders,
+ CheckDestroy: testCheckTritonFirewallRuleDestroy,
+ Steps: []resource.TestStep{
+ resource.TestStep{
+ Config: preConfig,
+ Check: resource.ComposeTestCheckFunc(
+ testCheckTritonFirewallRuleExists("triton_firewall_rule.test"),
+ resource.TestCheckResourceAttr("triton_firewall_rule.test", "rule", "FROM any TO tag www ALLOW tcp PORT 80"),
+ resource.TestCheckResourceAttr("triton_firewall_rule.test", "enabled", "false"),
+ ),
+ },
+
+ resource.TestStep{
+ Config: postConfig,
+ Check: resource.ComposeTestCheckFunc(
+ testCheckTritonFirewallRuleExists("triton_firewall_rule.test"),
+ resource.TestCheckResourceAttr("triton_firewall_rule.test", "rule", "FROM any TO tag www ALLOW tcp PORT 80"),
+ resource.TestCheckResourceAttr("triton_firewall_rule.test", "enabled", "true"),
+ ),
+ },
+ },
+ })
+}
+
+func testCheckTritonFirewallRuleExists(name string) resource.TestCheckFunc {
+ return func(s *terraform.State) error {
+ // Ensure we have enough information in state to look up in API
+ rs, ok := s.RootModule().Resources[name]
+ if !ok {
+ return fmt.Errorf("Not found: %s", name)
+ }
+ conn := testAccProvider.Meta().(*cloudapi.Client)
+
+ rule, err := conn.GetFirewallRule(rs.Primary.ID)
+ if err != nil {
+ return fmt.Errorf("Bad: Check Firewall Rule Exists: %s", err)
+ }
+
+ if rule == nil {
+ return fmt.Errorf("Bad: Firewall rule %q does not exist", rs.Primary.ID)
+ }
+
+ return nil
+ }
+}
+
+func testCheckTritonFirewallRuleDestroy(s *terraform.State) error {
+ conn := testAccProvider.Meta().(*cloudapi.Client)
+
+ for _, rs := range s.RootModule().Resources {
+ if rs.Type != "triton_firewall_rule" {
+ continue
+ }
+
+ resp, err := conn.GetFirewallRule(rs.Primary.ID)
+ if err != nil {
+ return nil
+ }
+
+ if resp != nil {
+ return fmt.Errorf("Bad: Firewall rule %q still exists", rs.Primary.ID)
+ }
+ }
+
+ return nil
+}
+
+var testAccTritonFirewallRule_basic = `
+resource "triton_firewall_rule" "test" {
+ rule = "FROM any TO tag www ALLOW tcp PORT 80"
+ enabled = false
+}
+`
+
+var testAccTritonFirewallRule_update = `
+resource "triton_firewall_rule" "test" {
+ rule = "FROM any TO tag www BLOCK tcp PORT 80"
+ enabled = true
+}
+`
+
+var testAccTritonFirewallRule_enable = `
+resource "triton_firewall_rule" "test" {
+ rule = "FROM any TO tag www ALLOW tcp PORT 80"
+ enabled = true
+}
+`
diff --git a/builtin/providers/triton/resource_key.go b/builtin/providers/triton/resource_key.go
new file mode 100644
index 000000000..b941cb0bf
--- /dev/null
+++ b/builtin/providers/triton/resource_key.go
@@ -0,0 +1,110 @@
+package triton
+
+import (
+ "errors"
+ "strings"
+
+ "github.com/hashicorp/terraform/helper/schema"
+ "github.com/joyent/gosdc/cloudapi"
+)
+
+var (
+ // ErrNoKeyComment will be returned when the key name cannot be generated from
+ // the key comment and is not otherwise specified.
+ ErrNoKeyComment = errors.New("no key comment found to use as a name (and none specified)")
+)
+
+func resourceKey() *schema.Resource {
+ return &schema.Resource{
+ Create: resourceKeyCreate,
+ Exists: resourceKeyExists,
+ Read: resourceKeyRead,
+ Delete: resourceKeyDelete,
+
+ Schema: map[string]*schema.Schema{
+ "name": &schema.Schema{
+ Description: "name of this key (will be generated from the key comment, if not set and comment present)",
+ Type: schema.TypeString,
+ Optional: true,
+ Computed: true,
+ ForceNew: true,
+ },
+ "key": &schema.Schema{
+ Description: "content of public key from disk",
+ Type: schema.TypeString,
+ Required: true,
+ ForceNew: true,
+ },
+ },
+ }
+}
+
+func resourceKeyCreate(d *schema.ResourceData, meta interface{}) error {
+ client := meta.(*cloudapi.Client)
+
+ if d.Get("name").(string) == "" {
+ parts := strings.SplitN(d.Get("key").(string), " ", 3)
+ if len(parts) == 3 {
+ d.Set("name", parts[2])
+ } else {
+ return ErrNoKeyComment
+ }
+ }
+
+ _, err := client.CreateKey(cloudapi.CreateKeyOpts{
+ Name: d.Get("name").(string),
+ Key: d.Get("key").(string),
+ })
+ if err != nil {
+ return err
+ }
+
+ err = resourceKeyRead(d, meta)
+ if err != nil {
+ return err
+ }
+
+ return nil
+}
+
+func resourceKeyExists(d *schema.ResourceData, meta interface{}) (bool, error) {
+ client := meta.(*cloudapi.Client)
+
+ keys, err := client.ListKeys()
+ if err != nil {
+ return false, err
+ }
+
+ for _, key := range keys {
+ if key.Name == d.Id() {
+ return true, nil
+ }
+ }
+
+ return false, nil
+}
+
+func resourceKeyRead(d *schema.ResourceData, meta interface{}) error {
+ client := meta.(*cloudapi.Client)
+
+ key, err := client.GetKey(d.Get("name").(string))
+ if err != nil {
+ return err
+ }
+
+ d.SetId(key.Name)
+ d.Set("name", key.Name)
+ d.Set("key", key.Key)
+
+ return nil
+}
+
+func resourceKeyDelete(d *schema.ResourceData, meta interface{}) error {
+ client := meta.(*cloudapi.Client)
+
+ if err := client.DeleteKey(d.Get("name").(string)); err != nil {
+ return err
+ }
+
+ return nil
+}
diff --git a/builtin/providers/triton/resource_key_test.go b/builtin/providers/triton/resource_key_test.go
new file mode 100644
index 000000000..1d1bf79a2
--- /dev/null
+++ b/builtin/providers/triton/resource_key_test.go
@@ -0,0 +1,89 @@
+package triton
+
+import (
+ "fmt"
+ "testing"
+ "time"
+
+ "github.com/hashicorp/terraform/helper/acctest"
+ "github.com/hashicorp/terraform/helper/resource"
+ "github.com/hashicorp/terraform/terraform"
+ "github.com/joyent/gosdc/cloudapi"
+)
+
+func TestAccTritonKey_basic(t *testing.T) {
+ keyName := fmt.Sprintf("acctest-%d", acctest.RandInt())
+ config := fmt.Sprintf(testAccTritonKey_basic, keyName, testAccTritonKey_basicMaterial)
+
+ resource.Test(t, resource.TestCase{
+ PreCheck: func() { testAccPreCheck(t) },
+ Providers: testAccProviders,
+ CheckDestroy: testCheckTritonKeyDestroy,
+ Steps: []resource.TestStep{
+ resource.TestStep{
+ Config: config,
+ Check: resource.ComposeTestCheckFunc(
+ testCheckTritonKeyExists("triton_key.test"),
+ func(*terraform.State) error {
+ time.Sleep(10 * time.Second)
+ return nil
+ },
+ ),
+ },
+ },
+ })
+}
+
+func testCheckTritonKeyExists(name string) resource.TestCheckFunc {
+ return func(s *terraform.State) error {
+ // Ensure we have enough information in state to look up in API
+ rs, ok := s.RootModule().Resources[name]
+ if !ok {
+ return fmt.Errorf("Not found: %s", name)
+ }
+ conn := testAccProvider.Meta().(*cloudapi.Client)
+
+ rule, err := conn.GetKey(rs.Primary.ID)
+ if err != nil {
+ return fmt.Errorf("Bad: Check Key Exists: %s", err)
+ }
+
+ if rule == nil {
+ return fmt.Errorf("Bad: Key %q does not exist", rs.Primary.ID)
+ }
+
+ return nil
+ }
+}
+
+func testCheckTritonKeyDestroy(s *terraform.State) error {
+ conn := testAccProvider.Meta().(*cloudapi.Client)
+
+ return resource.Retry(1*time.Minute, func() *resource.RetryError {
+ for _, rs := range s.RootModule().Resources {
+ if rs.Type != "triton_key" {
+ continue
+ }
+
+ resp, err := conn.GetKey(rs.Primary.ID)
+ if err != nil {
+ return nil
+ }
+
+ if resp != nil {
+ return resource.RetryableError(fmt.Errorf("Bad: Key %q still exists", rs.Primary.ID))
+ }
+ }
+
+ return nil
+ })
+}
+
+var testAccTritonKey_basic = `
+resource "triton_key" "test" {
+ name = "%s"
+ key = "%s"
+}
+`
+
+const testAccTritonKey_basicMaterial = `ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDL18KJIe8N7FxcgOMtabo10qZEDyYUSlOpsh/EYrugQCQHMKuNytog1lhFNZNk4LGNAz5L8/btG9+/axY/PfundbjR3SXt0hupAGQIVHuygWTr7foj5iGhckrEM+r3eMCXqoCnIFLhDZLDcq/zN2MxNbqDKcWSYmc8ul9dZWuiQpKOL+0nNXjhYA8Ewu+07kVAtsZD0WfvnAUjxmYb3rB15eBWk7gLxHrOPfZpeDSvOOX2bmzikpLn+L5NKrJsLrzO6hU/rpxD4OTHLULcsnIts3lYH8hShU8uY5ry94PBzdix++se3pUGvNSe967fKlHw3Ymh9nE/LJDQnzTNyFMj James@jn-mpb13`
diff --git a/builtin/providers/triton/resource_machine.go b/builtin/providers/triton/resource_machine.go
new file mode 100644
index 000000000..a16c9a964
--- /dev/null
+++ b/builtin/providers/triton/resource_machine.go
@@ -0,0 +1,436 @@
+package triton
+
+import (
+ "fmt"
+ "reflect"
+ "regexp"
+ "time"
+
+ "github.com/hashicorp/terraform/helper/schema"
+ "github.com/joyent/gosdc/cloudapi"
+)
+
+var (
+ machineStateRunning = "running"
+ machineStateStopped = "stopped"
+ machineStateDeleted = "deleted"
+
+ machineStateChangeTimeout = 10 * time.Minute
+ machineStateChangeCheckInterval = 10 * time.Second
+
+ resourceMachineMetadataKeys = map[string]string{
+ // semantics: "schema_name": "metadata_name"
+ "root_authorized_keys": "root_authorized_keys",
+ "user_script": "user-script",
+ "user_data": "user-data",
+ "administrator_pw": "administrator-pw",
+ }
+)
+
+func resourceMachine() *schema.Resource {
+ return &schema.Resource{
+ Create: resourceMachineCreate,
+ Exists: resourceMachineExists,
+ Read: resourceMachineRead,
+ Update: resourceMachineUpdate,
+ Delete: resourceMachineDelete,
+
+ Schema: map[string]*schema.Schema{
+ "name": {
+ Description: "friendly name",
+ Type: schema.TypeString,
+ Optional: true,
+ Computed: true,
+ ValidateFunc: resourceMachineValidateName,
+ },
+ "type": {
+ Description: "machine type (smartmachine or virtualmachine)",
+ Type: schema.TypeString,
+ Computed: true,
+ },
+ "state": {
+ Description: "current state of the machine",
+ Type: schema.TypeString,
+ Computed: true,
+ },
+ "dataset": {
+ Description: "dataset URN the machine was provisioned with",
+ Type: schema.TypeString,
+ Computed: true,
+ },
+ "memory": {
+ Description: "amount of memory the machine has (in Mb)",
+ Type: schema.TypeInt,
+ Computed: true,
+ },
+ "disk": {
+ Description: "amount of disk the machine has (in Gb)",
+ Type: schema.TypeInt,
+ Computed: true,
+ },
+ "ips": {
+ Description: "IP addresses the machine has",
+ Type: schema.TypeList,
+ Computed: true,
+ Elem: &schema.Schema{
+ Type: schema.TypeString,
+ },
+ },
+ "tags": {
+ Description: "machine tags",
+ Type: schema.TypeMap,
+ Optional: true,
+ },
+ "created": {
+ Description: "when the machine was created",
+ Type: schema.TypeString,
+ Computed: true,
+ },
+ "updated": {
+ Description: "when the machine was update",
+ Type: schema.TypeString,
+ Computed: true,
+ },
+ "package": {
+ Description: "name of the package to use on provisioning",
+ Type: schema.TypeString,
+ Required: true,
+ },
+ "image": {
+ Description: "image UUID",
+ Type: schema.TypeString,
+ Required: true,
+ ForceNew: true,
+ // TODO: validate that the UUID is valid
+ },
+ "primaryip": {
+ Description: "the primary (public) IP address for the machine",
+ Type: schema.TypeString,
+ Computed: true,
+ },
+ "networks": {
+ Description: "desired network IDs",
+ Type: schema.TypeList,
+ Optional: true,
+ Computed: true,
+ // TODO: this really should ForceNew but the Network IDs don't seem to
+ // be returned by the API, meaning if we track them here TF will replace
+ // the resource on every run.
+ // ForceNew: true,
+ Elem: &schema.Schema{
+ Type: schema.TypeString,
+ },
+ },
+ "firewall_enabled": {
+ Description: "enable firewall for this machine",
+ Type: schema.TypeBool,
+ Optional: true,
+ Default: false,
+ },
+
+ // computed resources from metadata
+ "root_authorized_keys": {
+ Description: "authorized keys for the root user on this machine",
+ Type: schema.TypeString,
+ Optional: true,
+ Computed: true,
+ },
+ "user_script": {
+ Description: "user script to run on boot (every boot on SmartMachines)",
+ Type: schema.TypeString,
+ Optional: true,
+ Computed: true,
+ },
+ "user_data": {
+ Description: "copied to machine on boot",
+ Type: schema.TypeString,
+ Optional: true,
+ Computed: true,
+ },
+ "administrator_pw": {
+ Description: "administrator's initial password (Windows only)",
+ Type: schema.TypeString,
+ Optional: true,
+ Computed: true,
+ },
+ },
+ }
+}
+
+func resourceMachineCreate(d *schema.ResourceData, meta interface{}) error {
+ client := meta.(*cloudapi.Client)
+
+ var networks []string
+ for _, network := range d.Get("networks").([]interface{}) {
+ networks = append(networks, network.(string))
+ }
+
+ metadata := map[string]string{}
+ for schemaName, metadataKey := range resourceMachineMetadataKeys {
+ if v, ok := d.GetOk(schemaName); ok {
+ metadata[metadataKey] = v.(string)
+ }
+ }
+
+ tags := map[string]string{}
+ for k, v := range d.Get("tags").(map[string]interface{}) {
+ tags[k] = v.(string)
+ }
+
+ machine, err := client.CreateMachine(cloudapi.CreateMachineOpts{
+ Name: d.Get("name").(string),
+ Package: d.Get("package").(string),
+ Image: d.Get("image").(string),
+ Networks: networks,
+ Metadata: metadata,
+ Tags: tags,
+ FirewallEnabled: d.Get("firewall_enabled").(bool),
+ })
+ if err != nil {
+ return err
+ }
+
+ err = waitForMachineState(client, machine.Id, machineStateRunning, machineStateChangeTimeout)
+ if err != nil {
+ return err
+ }
+
+ // refresh state after it provisions
+ d.SetId(machine.Id)
+ err = resourceMachineRead(d, meta)
+ if err != nil {
+ return err
+ }
+
+ return nil
+}
+
+func resourceMachineExists(d *schema.ResourceData, meta interface{}) (bool, error) {
+ client := meta.(*cloudapi.Client)
+
+ machine, err := client.GetMachine(d.Id())
+
+ return machine != nil && err == nil, err
+}
+
+func resourceMachineRead(d *schema.ResourceData, meta interface{}) error {
+ client := meta.(*cloudapi.Client)
+
+ machine, err := client.GetMachine(d.Id())
+ if err != nil {
+ return err
+ }
+
+ d.SetId(machine.Id)
+ d.Set("name", machine.Name)
+ d.Set("type", machine.Type)
+ d.Set("state", machine.State)
+ d.Set("dataset", machine.Dataset)
+ d.Set("memory", machine.Memory)
+ d.Set("disk", machine.Disk)
+ d.Set("ips", machine.IPs)
+ d.Set("tags", machine.Tags)
+ d.Set("created", machine.Created)
+ d.Set("updated", machine.Updated)
+ d.Set("package", machine.Package)
+ d.Set("image", machine.Image)
+ d.Set("primaryip", machine.PrimaryIP)
+ d.Set("networks", machine.Networks)
+ d.Set("firewall_enabled", machine.FirewallEnabled)
+
+ // computed attributes from metadata
+ for schemaName, metadataKey := range resourceMachineMetadataKeys {
+ d.Set(schemaName, machine.Metadata[metadataKey])
+ }
+
+ return nil
+}
+
+func resourceMachineUpdate(d *schema.ResourceData, meta interface{}) error {
+ client := meta.(*cloudapi.Client)
+
+ d.Partial(true)
+
+ if d.HasChange("name") {
+ if err := client.RenameMachine(d.Id(), d.Get("name").(string)); err != nil {
+ return err
+ }
+
+ err := waitFor(
+ func() (bool, error) {
+ machine, err := client.GetMachine(d.Id())
+ return machine.Name == d.Get("name").(string), err
+ },
+ machineStateChangeCheckInterval,
+ 1*time.Minute,
+ )
+ if err != nil {
+ return err
+ }
+
+ d.SetPartial("name")
+ }
+
+ if d.HasChange("tags") {
+ tags := map[string]string{}
+ for k, v := range d.Get("tags").(map[string]interface{}) {
+ tags[k] = v.(string)
+ }
+
+ var err error
+ if len(tags) == 0 {
+ err = client.DeleteMachineTags(d.Id())
+ } else {
+ _, err = client.ReplaceMachineTags(d.Id(), tags)
+ }
+ if err != nil {
+ return err
+ }
+
+ err = waitFor(
+ func() (bool, error) {
+ machine, err := client.GetMachine(d.Id())
+ return reflect.DeepEqual(machine.Tags, tags), err
+ },
+ machineStateChangeCheckInterval,
+ 1*time.Minute,
+ )
+ if err != nil {
+ return err
+ }
+
+ d.SetPartial("tags")
+ }
+
+ if d.HasChange("package") {
+ if err := client.ResizeMachine(d.Id(), d.Get("package").(string)); err != nil {
+ return err
+ }
+
+ err := waitFor(
+ func() (bool, error) {
+ machine, err := client.GetMachine(d.Id())
+ return machine.Package == d.Get("package").(string) && machine.State == machineStateRunning, err
+ },
+ machineStateChangeCheckInterval,
+ machineStateChangeTimeout,
+ )
+ if err != nil {
+ return err
+ }
+
+ d.SetPartial("package")
+ }
+
+ if d.HasChange("firewall_enabled") {
+ var err error
+ if d.Get("firewall_enabled").(bool) {
+ err = client.EnableFirewallMachine(d.Id())
+ } else {
+ err = client.DisableFirewallMachine(d.Id())
+ }
+ if err != nil {
+ return err
+ }
+
+ d.SetPartial("firewall_enabled")
+ }
+
+ // metadata stuff
+ metadata := map[string]string{}
+ for schemaName, metadataKey := range resourceMachineMetadataKeys {
+ if d.HasChange(schemaName) {
+ metadata[metadataKey] = d.Get(schemaName).(string)
+ }
+ }
+ if len(metadata) > 0 {
+ _, err := client.UpdateMachineMetadata(d.Id(), metadata)
+ if err != nil {
+ return err
+ }
+
+ err = waitFor(
+ func() (bool, error) {
+ machine, err := client.GetMachine(d.Id())
+ return reflect.DeepEqual(machine.Metadata, metadata), err
+ },
+ machineStateChangeCheckInterval,
+ 1*time.Minute,
+ )
+ if err != nil {
+ return err
+ }
+
+ for schemaName := range resourceMachineMetadataKeys {
+ if d.HasChange(schemaName) {
+ d.SetPartial(schemaName)
+ }
+ }
+ }
+
+ d.Partial(false)
+
+ err := resourceMachineRead(d, meta)
+ if err != nil {
+ return err
+ }
+
+ return nil
+}
+
+func resourceMachineDelete(d *schema.ResourceData, meta interface{}) error {
+ client := meta.(*cloudapi.Client)
+
+ state, err := readMachineState(client, d.Id())
+ if state != machineStateStopped {
+ err = client.StopMachine(d.Id())
+ if err != nil {
+ return err
+ }
+
+ waitForMachineState(client, d.Id(), machineStateStopped, machineStateChangeTimeout)
+ }
+
+ err = client.DeleteMachine(d.Id())
+ if err != nil {
+ return err
+ }
+
+ waitForMachineState(client, d.Id(), machineStateDeleted, machineStateChangeTimeout)
+ return nil
+}
+
+func readMachineState(api *cloudapi.Client, id string) (string, error) {
+ machine, err := api.GetMachine(id)
+ if err != nil {
+ return "", err
+ }
+
+ return machine.State, nil
+}
+
+// waitForMachineState waits for a machine to be in the desired state (waiting
+// some seconds between each poll). If it doesn't reach the state within the
+// duration specified in `timeout`, it returns ErrMachineStateTimeout.
+func waitForMachineState(api *cloudapi.Client, id, state string, timeout time.Duration) error {
+ return waitFor(
+ func() (bool, error) {
+ currentState, err := readMachineState(api, id)
+ return currentState == state, err
+ },
+ machineStateChangeCheckInterval,
+ machineStateChangeTimeout,
+ )
+}
+
+func resourceMachineValidateName(value interface{}, name string) (warnings []string, errors []error) {
+ warnings = []string{}
+ errors = []error{}
+
+ r := regexp.MustCompile(`^[a-zA-Z0-9][a-zA-Z0-9\_\.\-]*$`)
+ if !r.Match([]byte(value.(string))) {
+ errors = append(errors, fmt.Errorf(`"%s" is not a valid %s`, value.(string), name))
+ }
+
+ return warnings, errors
+}
diff --git a/builtin/providers/triton/resource_machine_test.go b/builtin/providers/triton/resource_machine_test.go
new file mode 100644
index 000000000..2fd13afca
--- /dev/null
+++ b/builtin/providers/triton/resource_machine_test.go
@@ -0,0 +1,90 @@
+package triton
+
+import (
+ "fmt"
+ "testing"
+ "time"
+
+ "github.com/hashicorp/terraform/helper/acctest"
+ "github.com/hashicorp/terraform/helper/resource"
+ "github.com/hashicorp/terraform/terraform"
+ "github.com/joyent/gosdc/cloudapi"
+)
+
+func TestAccTritonMachine_basic(t *testing.T) {
+ machineName := fmt.Sprintf("acctest-%d", acctest.RandInt())
+ config := fmt.Sprintf(testAccTritonMachine_basic, machineName)
+
+ resource.Test(t, resource.TestCase{
+ PreCheck: func() { testAccPreCheck(t) },
+ Providers: testAccProviders,
+ CheckDestroy: testCheckTritonMachineDestroy,
+ Steps: []resource.TestStep{
+ resource.TestStep{
+ Config: config,
+ Check: resource.ComposeTestCheckFunc(
+ testCheckTritonMachineExists("triton_machine.test"),
+ func(*terraform.State) error {
+ time.Sleep(10 * time.Second)
+ return nil
+ },
+ ),
+ },
+ },
+ })
+}
+
+func testCheckTritonMachineExists(name string) resource.TestCheckFunc {
+ return func(s *terraform.State) error {
+ // Ensure we have enough information in state to look up in API
+ rs, ok := s.RootModule().Resources[name]
+ if !ok {
+ return fmt.Errorf("Not found: %s", name)
+ }
+ conn := testAccProvider.Meta().(*cloudapi.Client)
+
+ rule, err := conn.GetMachine(rs.Primary.ID)
+ if err != nil {
+ return fmt.Errorf("Bad: Check Machine Exists: %s", err)
+ }
+
+ if rule == nil {
+ return fmt.Errorf("Bad: Machine %q does not exist", rs.Primary.ID)
+ }
+
+ return nil
+ }
+}
+
+func testCheckTritonMachineDestroy(s *terraform.State) error {
+ conn := testAccProvider.Meta().(*cloudapi.Client)
+
+ for _, rs := range s.RootModule().Resources {
+ if rs.Type != "triton_machine" {
+ continue
+ }
+
+ resp, err := conn.GetMachine(rs.Primary.ID)
+ if err != nil {
+ return nil
+ }
+
+ if resp != nil {
+ return fmt.Errorf("Bad: Machine %q still exists", rs.Primary.ID)
+ }
+ }
+
+ return nil
+}
+
+var testAccTritonMachine_basic = `
+resource "triton_machine" "test" {
+ name = "%s"
+ package = "g3-standard-0.25-smartos"
+ image = "842e6fa6-6e9b-11e5-8402-1b490459e334"
+
+ tags = {
+ test = "hello!"
+ }
+}
+`
diff --git a/builtin/providers/triton/utils.go b/builtin/providers/triton/utils.go
new file mode 100644
index 000000000..ef8ae23cb
--- /dev/null
+++ b/builtin/providers/triton/utils.go
@@ -0,0 +1,30 @@
+package triton
+
+import (
+ "errors"
+ "time"
+)
+
+var (
+ // ErrTimeout is returned when waiting for state change
+ ErrTimeout = errors.New("timed out while waiting for resource change")
+)
+
+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 ErrTimeout
+}
diff --git a/vendor/github.com/joyent/gocommon/.gitignore b/vendor/github.com/joyent/gocommon/.gitignore
new file mode 100644
index 000000000..8fde1319c
--- /dev/null
+++ b/vendor/github.com/joyent/gocommon/.gitignore
@@ -0,0 +1,26 @@
+# Compiled Object files, Static and Dynamic libs (Shared Objects)
+*.o
+*.a
+*.so
+
+# Folders
+_obj
+_test
+
+# Architecture specific extensions/prefixes
+*.[568vq]
+[568vq].out
+
+*.cgo1.go
+*.cgo2.c
+_cgo_defun.c
+_cgo_gotypes.go
+_cgo_export.*
+
+_testmain.go
+
+*.exe
+
+# IntelliJ files
+.idea
+*.iml
\ No newline at end of file
diff --git a/vendor/github.com/joyent/gocommon/COPYING b/vendor/github.com/joyent/gocommon/COPYING
new file mode 100644
index 000000000..94a9ed024
--- /dev/null
+++ b/vendor/github.com/joyent/gocommon/COPYING
@@ -0,0 +1,674 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc.
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The GNU General Public License is a free, copyleft license for
+software and other kinds of works.
+
+ The licenses for most software and other practical works are designed
+to take away your freedom to share and change the works. By contrast,
+the GNU General Public License is intended to guarantee your freedom to
+share and change all versions of a program--to make sure it remains free
+software for all its users. We, the Free Software Foundation, use the
+GNU General Public License for most of our software; it applies also to
+any other work released this way by its authors. You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+them if you wish), that you receive source code or can get it if you
+want it, that you can change the software or use pieces of it in new
+free programs, and that you know you can do these things.
+
+ To protect your rights, we need to prevent others from denying you
+these rights or asking you to surrender the rights. Therefore, you have
+certain responsibilities if you distribute copies of the software, or if
+you modify it: responsibilities to respect the freedom of others.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must pass on to the recipients the same
+freedoms that you received. You must make sure that they, too, receive
+or can get the source code. And you must show them these terms so they
+know their rights.
+
+ Developers that use the GNU GPL protect your rights with two steps:
+(1) assert copyright on the software, and (2) offer you this License
+giving you legal permission to copy, distribute and/or modify it.
+
+ For the developers' and authors' protection, the GPL clearly explains
+that there is no warranty for this free software. For both users' and
+authors' sake, the GPL requires that modified versions be marked as
+changed, so that their problems will not be attributed erroneously to
+authors of previous versions.
+
+ Some devices are designed to deny users access to install or run
+modified versions of the software inside them, although the manufacturer
+can do so. This is fundamentally incompatible with the aim of
+protecting users' freedom to change the software. The systematic
+pattern of such abuse occurs in the area of products for individuals to
+use, which is precisely where it is most unacceptable. Therefore, we
+have designed this version of the GPL to prohibit the practice for those
+products. If such problems arise substantially in other domains, we
+stand ready to extend this provision to those domains in future versions
+of the GPL, as needed to protect the freedom of users.
+
+ Finally, every program is threatened constantly by software patents.
+States should not allow patents to restrict development and use of
+software on general-purpose computers, but in those that do, we wish to
+avoid the special danger that patents applied to a free program could
+make it effectively proprietary. To prevent this, the GPL assures that
+patents cannot be used to render the program non-free.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ TERMS AND CONDITIONS
+
+ 0. Definitions.
+
+ "This License" refers to version 3 of the GNU General Public License.
+
+ "Copyright" also means copyright-like laws that apply to other kinds of
+works, such as semiconductor masks.
+
+ "The Program" refers to any copyrightable work licensed under this
+License. Each licensee is addressed as "you". "Licensees" and
+"recipients" may be individuals or organizations.
+
+ To "modify" a work means to copy from or adapt all or part of the work
+in a fashion requiring copyright permission, other than the making of an
+exact copy. The resulting work is called a "modified version" of the
+earlier work or a work "based on" the earlier work.
+
+ A "covered work" means either the unmodified Program or a work based
+on the Program.
+
+ To "propagate" a work means to do anything with it that, without
+permission, would make you directly or secondarily liable for
+infringement under applicable copyright law, except executing it on a
+computer or modifying a private copy. Propagation includes copying,
+distribution (with or without modification), making available to the
+public, and in some countries other activities as well.
+
+ To "convey" a work means any kind of propagation that enables other
+parties to make or receive copies. Mere interaction with a user through
+a computer network, with no transfer of a copy, is not conveying.
+
+ An interactive user interface displays "Appropriate Legal Notices"
+to the extent that it includes a convenient and prominently visible
+feature that (1) displays an appropriate copyright notice, and (2)
+tells the user that there is no warranty for the work (except to the
+extent that warranties are provided), that licensees may convey the
+work under this License, and how to view a copy of this License. If
+the interface presents a list of user commands or options, such as a
+menu, a prominent item in the list meets this criterion.
+
+ 1. Source Code.
+
+ The "source code" for a work means the preferred form of the work
+for making modifications to it. "Object code" means any non-source
+form of a work.
+
+ A "Standard Interface" means an interface that either is an official
+standard defined by a recognized standards body, or, in the case of
+interfaces specified for a particular programming language, one that
+is widely used among developers working in that language.
+
+ The "System Libraries" of an executable work include anything, other
+than the work as a whole, that (a) is included in the normal form of
+packaging a Major Component, but which is not part of that Major
+Component, and (b) serves only to enable use of the work with that
+Major Component, or to implement a Standard Interface for which an
+implementation is available to the public in source code form. A
+"Major Component", in this context, means a major essential component
+(kernel, window system, and so on) of the specific operating system
+(if any) on which the executable work runs, or a compiler used to
+produce the work, or an object code interpreter used to run it.
+
+ The "Corresponding Source" for a work in object code form means all
+the source code needed to generate, install, and (for an executable
+work) run the object code and to modify the work, including scripts to
+control those activities. However, it does not include the work's
+System Libraries, or general-purpose tools or generally available free
+programs which are used unmodified in performing those activities but
+which are not part of the work. For example, Corresponding Source
+includes interface definition files associated with source files for
+the work, and the source code for shared libraries and dynamically
+linked subprograms that the work is specifically designed to require,
+such as by intimate data communication or control flow between those
+subprograms and other parts of the work.
+
+ The Corresponding Source need not include anything that users
+can regenerate automatically from other parts of the Corresponding
+Source.
+
+ The Corresponding Source for a work in source code form is that
+same work.
+
+ 2. Basic Permissions.
+
+ All rights granted under this License are granted for the term of
+copyright on the Program, and are irrevocable provided the stated
+conditions are met. This License explicitly affirms your unlimited
+permission to run the unmodified Program. The output from running a
+covered work is covered by this License only if the output, given its
+content, constitutes a covered work. This License acknowledges your
+rights of fair use or other equivalent, as provided by copyright law.
+
+ You may make, run and propagate covered works that you do not
+convey, without conditions so long as your license otherwise remains
+in force. You may convey covered works to others for the sole purpose
+of having them make modifications exclusively for you, or provide you
+with facilities for running those works, provided that you comply with
+the terms of this License in conveying all material for which you do
+not control copyright. Those thus making or running the covered works
+for you must do so exclusively on your behalf, under your direction
+and control, on terms that prohibit them from making any copies of
+your copyrighted material outside their relationship with you.
+
+ Conveying under any other circumstances is permitted solely under
+the conditions stated below. Sublicensing is not allowed; section 10
+makes it unnecessary.
+
+ 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
+
+ No covered work shall be deemed part of an effective technological
+measure under any applicable law fulfilling obligations under article
+11 of the WIPO copyright treaty adopted on 20 December 1996, or
+similar laws prohibiting or restricting circumvention of such
+measures.
+
+ When you convey a covered work, you waive any legal power to forbid
+circumvention of technological measures to the extent such circumvention
+is effected by exercising rights under this License with respect to
+the covered work, and you disclaim any intention to limit operation or
+modification of the work as a means of enforcing, against the work's
+users, your or third parties' legal rights to forbid circumvention of
+technological measures.
+
+ 4. Conveying Verbatim Copies.
+
+ You may convey verbatim copies of the Program's source code as you
+receive it, in any medium, provided that you conspicuously and
+appropriately publish on each copy an appropriate copyright notice;
+keep intact all notices stating that this License and any
+non-permissive terms added in accord with section 7 apply to the code;
+keep intact all notices of the absence of any warranty; and give all
+recipients a copy of this License along with the Program.
+
+ You may charge any price or no price for each copy that you convey,
+and you may offer support or warranty protection for a fee.
+
+ 5. Conveying Modified Source Versions.
+
+ You may convey a work based on the Program, or the modifications to
+produce it from the Program, in the form of source code under the
+terms of section 4, provided that you also meet all of these conditions:
+
+ a) The work must carry prominent notices stating that you modified
+ it, and giving a relevant date.
+
+ b) The work must carry prominent notices stating that it is
+ released under this License and any conditions added under section
+ 7. This requirement modifies the requirement in section 4 to
+ "keep intact all notices".
+
+ c) You must license the entire work, as a whole, under this
+ License to anyone who comes into possession of a copy. This
+ License will therefore apply, along with any applicable section 7
+ additional terms, to the whole of the work, and all its parts,
+ regardless of how they are packaged. This License gives no
+ permission to license the work in any other way, but it does not
+ invalidate such permission if you have separately received it.
+
+ d) If the work has interactive user interfaces, each must display
+ Appropriate Legal Notices; however, if the Program has interactive
+ interfaces that do not display Appropriate Legal Notices, your
+ work need not make them do so.
+
+ A compilation of a covered work with other separate and independent
+works, which are not by their nature extensions of the covered work,
+and which are not combined with it such as to form a larger program,
+in or on a volume of a storage or distribution medium, is called an
+"aggregate" if the compilation and its resulting copyright are not
+used to limit the access or legal rights of the compilation's users
+beyond what the individual works permit. Inclusion of a covered work
+in an aggregate does not cause this License to apply to the other
+parts of the aggregate.
+
+ 6. Conveying Non-Source Forms.
+
+ You may convey a covered work in object code form under the terms
+of sections 4 and 5, provided that you also convey the
+machine-readable Corresponding Source under the terms of this License,
+in one of these ways:
+
+ a) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by the
+ Corresponding Source fixed on a durable physical medium
+ customarily used for software interchange.
+
+ b) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by a
+ written offer, valid for at least three years and valid for as
+ long as you offer spare parts or customer support for that product
+ model, to give anyone who possesses the object code either (1) a
+ copy of the Corresponding Source for all the software in the
+ product that is covered by this License, on a durable physical
+ medium customarily used for software interchange, for a price no
+ more than your reasonable cost of physically performing this
+ conveying of source, or (2) access to copy the
+ Corresponding Source from a network server at no charge.
+
+ c) Convey individual copies of the object code with a copy of the
+ written offer to provide the Corresponding Source. This
+ alternative is allowed only occasionally and noncommercially, and
+ only if you received the object code with such an offer, in accord
+ with subsection 6b.
+
+ d) Convey the object code by offering access from a designated
+ place (gratis or for a charge), and offer equivalent access to the
+ Corresponding Source in the same way through the same place at no
+ further charge. You need not require recipients to copy the
+ Corresponding Source along with the object code. If the place to
+ copy the object code is a network server, the Corresponding Source
+ may be on a different server (operated by you or a third party)
+ that supports equivalent copying facilities, provided you maintain
+ clear directions next to the object code saying where to find the
+ Corresponding Source. Regardless of what server hosts the
+ Corresponding Source, you remain obligated to ensure that it is
+ available for as long as needed to satisfy these requirements.
+
+ e) Convey the object code using peer-to-peer transmission, provided
+ you inform other peers where the object code and Corresponding
+ Source of the work are being offered to the general public at no
+ charge under subsection 6d.
+
+ A separable portion of the object code, whose source code is excluded
+from the Corresponding Source as a System Library, need not be
+included in conveying the object code work.
+
+ A "User Product" is either (1) a "consumer product", which means any
+tangible personal property which is normally used for personal, family,
+or household purposes, or (2) anything designed or sold for incorporation
+into a dwelling. In determining whether a product is a consumer product,
+doubtful cases shall be resolved in favor of coverage. For a particular
+product received by a particular user, "normally used" refers to a
+typical or common use of that class of product, regardless of the status
+of the particular user or of the way in which the particular user
+actually uses, or expects or is expected to use, the product. A product
+is a consumer product regardless of whether the product has substantial
+commercial, industrial or non-consumer uses, unless such uses represent
+the only significant mode of use of the product.
+
+ "Installation Information" for a User Product means any methods,
+procedures, authorization keys, or other information required to install
+and execute modified versions of a covered work in that User Product from
+a modified version of its Corresponding Source. The information must
+suffice to ensure that the continued functioning of the modified object
+code is in no case prevented or interfered with solely because
+modification has been made.
+
+ If you convey an object code work under this section in, or with, or
+specifically for use in, a User Product, and the conveying occurs as
+part of a transaction in which the right of possession and use of the
+User Product is transferred to the recipient in perpetuity or for a
+fixed term (regardless of how the transaction is characterized), the
+Corresponding Source conveyed under this section must be accompanied
+by the Installation Information. But this requirement does not apply
+if neither you nor any third party retains the ability to install
+modified object code on the User Product (for example, the work has
+been installed in ROM).
+
+ The requirement to provide Installation Information does not include a
+requirement to continue to provide support service, warranty, or updates
+for a work that has been modified or installed by the recipient, or for
+the User Product in which it has been modified or installed. Access to a
+network may be denied when the modification itself materially and
+adversely affects the operation of the network or violates the rules and
+protocols for communication across the network.
+
+ Corresponding Source conveyed, and Installation Information provided,
+in accord with this section must be in a format that is publicly
+documented (and with an implementation available to the public in
+source code form), and must require no special password or key for
+unpacking, reading or copying.
+
+ 7. Additional Terms.
+
+ "Additional permissions" are terms that supplement the terms of this
+License by making exceptions from one or more of its conditions.
+Additional permissions that are applicable to the entire Program shall
+be treated as though they were included in this License, to the extent
+that they are valid under applicable law. If additional permissions
+apply only to part of the Program, that part may be used separately
+under those permissions, but the entire Program remains governed by
+this License without regard to the additional permissions.
+
+ When you convey a copy of a covered work, you may at your option
+remove any additional permissions from that copy, or from any part of
+it. (Additional permissions may be written to require their own
+removal in certain cases when you modify the work.) You may place
+additional permissions on material, added by you to a covered work,
+for which you have or can give appropriate copyright permission.
+
+ Notwithstanding any other provision of this License, for material you
+add to a covered work, you may (if authorized by the copyright holders of
+that material) supplement the terms of this License with terms:
+
+ a) Disclaiming warranty or limiting liability differently from the
+ terms of sections 15 and 16 of this License; or
+
+ b) Requiring preservation of specified reasonable legal notices or
+ author attributions in that material or in the Appropriate Legal
+ Notices displayed by works containing it; or
+
+ c) Prohibiting misrepresentation of the origin of that material, or
+ requiring that modified versions of such material be marked in
+ reasonable ways as different from the original version; or
+
+ d) Limiting the use for publicity purposes of names of licensors or
+ authors of the material; or
+
+ e) Declining to grant rights under trademark law for use of some
+ trade names, trademarks, or service marks; or
+
+ f) Requiring indemnification of licensors and authors of that
+ material by anyone who conveys the material (or modified versions of
+ it) with contractual assumptions of liability to the recipient, for
+ any liability that these contractual assumptions directly impose on
+ those licensors and authors.
+
+ All other non-permissive additional terms are considered "further
+restrictions" within the meaning of section 10. If the Program as you
+received it, or any part of it, contains a notice stating that it is
+governed by this License along with a term that is a further
+restriction, you may remove that term. If a license document contains
+a further restriction but permits relicensing or conveying under this
+License, you may add to a covered work material governed by the terms
+of that license document, provided that the further restriction does
+not survive such relicensing or conveying.
+
+ If you add terms to a covered work in accord with this section, you
+must place, in the relevant source files, a statement of the
+additional terms that apply to those files, or a notice indicating
+where to find the applicable terms.
+
+ Additional terms, permissive or non-permissive, may be stated in the
+form of a separately written license, or stated as exceptions;
+the above requirements apply either way.
+
+ 8. Termination.
+
+ You may not propagate or modify a covered work except as expressly
+provided under this License. Any attempt otherwise to propagate or
+modify it is void, and will automatically terminate your rights under
+this License (including any patent licenses granted under the third
+paragraph of section 11).
+
+ However, if you cease all violation of this License, then your
+license from a particular copyright holder is reinstated (a)
+provisionally, unless and until the copyright holder explicitly and
+finally terminates your license, and (b) permanently, if the copyright
+holder fails to notify you of the violation by some reasonable means
+prior to 60 days after the cessation.
+
+ Moreover, your license from a particular copyright holder is
+reinstated permanently if the copyright holder notifies you of the
+violation by some reasonable means, this is the first time you have
+received notice of violation of this License (for any work) from that
+copyright holder, and you cure the violation prior to 30 days after
+your receipt of the notice.
+
+ Termination of your rights under this section does not terminate the
+licenses of parties who have received copies or rights from you under
+this License. If your rights have been terminated and not permanently
+reinstated, you do not qualify to receive new licenses for the same
+material under section 10.
+
+ 9. Acceptance Not Required for Having Copies.
+
+ You are not required to accept this License in order to receive or
+run a copy of the Program. Ancillary propagation of a covered work
+occurring solely as a consequence of using peer-to-peer transmission
+to receive a copy likewise does not require acceptance. However,
+nothing other than this License grants you permission to propagate or
+modify any covered work. These actions infringe copyright if you do
+not accept this License. Therefore, by modifying or propagating a
+covered work, you indicate your acceptance of this License to do so.
+
+ 10. Automatic Licensing of Downstream Recipients.
+
+ Each time you convey a covered work, the recipient automatically
+receives a license from the original licensors, to run, modify and
+propagate that work, subject to this License. You are not responsible
+for enforcing compliance by third parties with this License.
+
+ An "entity transaction" is a transaction transferring control of an
+organization, or substantially all assets of one, or subdividing an
+organization, or merging organizations. If propagation of a covered
+work results from an entity transaction, each party to that
+transaction who receives a copy of the work also receives whatever
+licenses to the work the party's predecessor in interest had or could
+give under the previous paragraph, plus a right to possession of the
+Corresponding Source of the work from the predecessor in interest, if
+the predecessor has it or can get it with reasonable efforts.
+
+ You may not impose any further restrictions on the exercise of the
+rights granted or affirmed under this License. For example, you may
+not impose a license fee, royalty, or other charge for exercise of
+rights granted under this License, and you may not initiate litigation
+(including a cross-claim or counterclaim in a lawsuit) alleging that
+any patent claim is infringed by making, using, selling, offering for
+sale, or importing the Program or any portion of it.
+
+ 11. Patents.
+
+ A "contributor" is a copyright holder who authorizes use under this
+License of the Program or a work on which the Program is based. The
+work thus licensed is called the contributor's "contributor version".
+
+ A contributor's "essential patent claims" are all patent claims
+owned or controlled by the contributor, whether already acquired or
+hereafter acquired, that would be infringed by some manner, permitted
+by this License, of making, using, or selling its contributor version,
+but do not include claims that would be infringed only as a
+consequence of further modification of the contributor version. For
+purposes of this definition, "control" includes the right to grant
+patent sublicenses in a manner consistent with the requirements of
+this License.
+
+ Each contributor grants you a non-exclusive, worldwide, royalty-free
+patent license under the contributor's essential patent claims, to
+make, use, sell, offer for sale, import and otherwise run, modify and
+propagate the contents of its contributor version.
+
+ In the following three paragraphs, a "patent license" is any express
+agreement or commitment, however denominated, not to enforce a patent
+(such as an express permission to practice a patent or covenant not to
+sue for patent infringement). To "grant" such a patent license to a
+party means to make such an agreement or commitment not to enforce a
+patent against the party.
+
+ If you convey a covered work, knowingly relying on a patent license,
+and the Corresponding Source of the work is not available for anyone
+to copy, free of charge and under the terms of this License, through a
+publicly available network server or other readily accessible means,
+then you must either (1) cause the Corresponding Source to be so
+available, or (2) arrange to deprive yourself of the benefit of the
+patent license for this particular work, or (3) arrange, in a manner
+consistent with the requirements of this License, to extend the patent
+license to downstream recipients. "Knowingly relying" means you have
+actual knowledge that, but for the patent license, your conveying the
+covered work in a country, or your recipient's use of the covered work
+in a country, would infringe one or more identifiable patents in that
+country that you have reason to believe are valid.
+
+ If, pursuant to or in connection with a single transaction or
+arrangement, you convey, or propagate by procuring conveyance of, a
+covered work, and grant a patent license to some of the parties
+receiving the covered work authorizing them to use, propagate, modify
+or convey a specific copy of the covered work, then the patent license
+you grant is automatically extended to all recipients of the covered
+work and works based on it.
+
+ A patent license is "discriminatory" if it does not include within
+the scope of its coverage, prohibits the exercise of, or is
+conditioned on the non-exercise of one or more of the rights that are
+specifically granted under this License. You may not convey a covered
+work if you are a party to an arrangement with a third party that is
+in the business of distributing software, under which you make payment
+to the third party based on the extent of your activity of conveying
+the work, and under which the third party grants, to any of the
+parties who would receive the covered work from you, a discriminatory
+patent license (a) in connection with copies of the covered work
+conveyed by you (or copies made from those copies), or (b) primarily
+for and in connection with specific products or compilations that
+contain the covered work, unless you entered into that arrangement,
+or that patent license was granted, prior to 28 March 2007.
+
+ Nothing in this License shall be construed as excluding or limiting
+any implied license or other defenses to infringement that may
+otherwise be available to you under applicable patent law.
+
+ 12. No Surrender of Others' Freedom.
+
+ If conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot convey a
+covered work so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you may
+not convey it at all. For example, if you agree to terms that obligate you
+to collect a royalty for further conveying from those to whom you convey
+the Program, the only way you could satisfy both those terms and this
+License would be to refrain entirely from conveying the Program.
+
+ 13. Use with the GNU Affero General Public License.
+
+ Notwithstanding any other provision of this License, you have
+permission to link or combine any covered work with a work licensed
+under version 3 of the GNU Affero General Public License into a single
+combined work, and to convey the resulting work. The terms of this
+License will continue to apply to the part which is the covered work,
+but the special requirements of the GNU Affero General Public License,
+section 13, concerning interaction through a network will apply to the
+combination as such.
+
+ 14. Revised Versions of this License.
+
+ The Free Software Foundation may publish revised and/or new versions of
+the GNU General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+ Each version is given a distinguishing version number. If the
+Program specifies that a certain numbered version of the GNU General
+Public License "or any later version" applies to it, you have the
+option of following the terms and conditions either of that numbered
+version or of any later version published by the Free Software
+Foundation. If the Program does not specify a version number of the
+GNU General Public License, you may choose any version ever published
+by the Free Software Foundation.
+
+ If the Program specifies that a proxy can decide which future
+versions of the GNU General Public License can be used, that proxy's
+public statement of acceptance of a version permanently authorizes you
+to choose that version for the Program.
+
+ Later license versions may give you additional or different
+permissions. However, no additional obligations are imposed on any
+author or copyright holder as a result of your choosing to follow a
+later version.
+
+ 15. Disclaimer of Warranty.
+
+ THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
+APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
+HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
+OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
+IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
+ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+ 16. Limitation of Liability.
+
+ IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
+THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
+GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
+USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
+DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
+PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
+EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGES.
+
+ 17. Interpretation of Sections 15 and 16.
+
+ If the disclaimer of warranty and limitation of liability provided
+above cannot be given local legal effect according to their terms,
+reviewing courts shall apply local law that most closely approximates
+an absolute waiver of all civil liability in connection with the
+Program, unless a warranty or assumption of liability accompanies a
+copy of the Program in return for a fee.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+state the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+
+ Copyright (C)
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see .
+
+Also add information on how to contact you by electronic and paper mail.
+
+ If the program does terminal interaction, make it output a short
+notice like this when it starts in an interactive mode:
+
+ Copyright (C)
+ This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, your program's commands
+might be different; for a GUI interface, you would use an "about box".
+
+ You should also get your employer (if you work as a programmer) or school,
+if any, to sign a "copyright disclaimer" for the program, if necessary.
+For more information on this, and how to apply and follow the GNU GPL, see
+.
+
+ The GNU General Public License does not permit incorporating your program
+into proprietary programs. If your program is a subroutine library, you
+may consider it more useful to permit linking proprietary applications with
+the library. If this is what you want to do, use the GNU Lesser General
+Public License instead of this License. But first, please read
+.
diff --git a/vendor/github.com/joyent/gocommon/COPYING.LESSER b/vendor/github.com/joyent/gocommon/COPYING.LESSER
new file mode 100644
index 000000000..65c5ca88a
--- /dev/null
+++ b/vendor/github.com/joyent/gocommon/COPYING.LESSER
@@ -0,0 +1,165 @@
+ GNU LESSER GENERAL PUBLIC LICENSE
+ Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc.
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+
+ This version of the GNU Lesser General Public License incorporates
+the terms and conditions of version 3 of the GNU General Public
+License, supplemented by the additional permissions listed below.
+
+ 0. Additional Definitions.
+
+ As used herein, "this License" refers to version 3 of the GNU Lesser
+General Public License, and the "GNU GPL" refers to version 3 of the GNU
+General Public License.
+
+ "The Library" refers to a covered work governed by this License,
+other than an Application or a Combined Work as defined below.
+
+ An "Application" is any work that makes use of an interface provided
+by the Library, but which is not otherwise based on the Library.
+Defining a subclass of a class defined by the Library is deemed a mode
+of using an interface provided by the Library.
+
+ A "Combined Work" is a work produced by combining or linking an
+Application with the Library. The particular version of the Library
+with which the Combined Work was made is also called the "Linked
+Version".
+
+ The "Minimal Corresponding Source" for a Combined Work means the
+Corresponding Source for the Combined Work, excluding any source code
+for portions of the Combined Work that, considered in isolation, are
+based on the Application, and not on the Linked Version.
+
+ The "Corresponding Application Code" for a Combined Work means the
+object code and/or source code for the Application, including any data
+and utility programs needed for reproducing the Combined Work from the
+Application, but excluding the System Libraries of the Combined Work.
+
+ 1. Exception to Section 3 of the GNU GPL.
+
+ You may convey a covered work under sections 3 and 4 of this License
+without being bound by section 3 of the GNU GPL.
+
+ 2. Conveying Modified Versions.
+
+ If you modify a copy of the Library, and, in your modifications, a
+facility refers to a function or data to be supplied by an Application
+that uses the facility (other than as an argument passed when the
+facility is invoked), then you may convey a copy of the modified
+version:
+
+ a) under this License, provided that you make a good faith effort to
+ ensure that, in the event an Application does not supply the
+ function or data, the facility still operates, and performs
+ whatever part of its purpose remains meaningful, or
+
+ b) under the GNU GPL, with none of the additional permissions of
+ this License applicable to that copy.
+
+ 3. Object Code Incorporating Material from Library Header Files.
+
+ The object code form of an Application may incorporate material from
+a header file that is part of the Library. You may convey such object
+code under terms of your choice, provided that, if the incorporated
+material is not limited to numerical parameters, data structure
+layouts and accessors, or small macros, inline functions and templates
+(ten or fewer lines in length), you do both of the following:
+
+ a) Give prominent notice with each copy of the object code that the
+ Library is used in it and that the Library and its use are
+ covered by this License.
+
+ b) Accompany the object code with a copy of the GNU GPL and this license
+ document.
+
+ 4. Combined Works.
+
+ You may convey a Combined Work under terms of your choice that,
+taken together, effectively do not restrict modification of the
+portions of the Library contained in the Combined Work and reverse
+engineering for debugging such modifications, if you also do each of
+the following:
+
+ a) Give prominent notice with each copy of the Combined Work that
+ the Library is used in it and that the Library and its use are
+ covered by this License.
+
+ b) Accompany the Combined Work with a copy of the GNU GPL and this license
+ document.
+
+ c) For a Combined Work that displays copyright notices during
+ execution, include the copyright notice for the Library among
+ these notices, as well as a reference directing the user to the
+ copies of the GNU GPL and this license document.
+
+ d) Do one of the following:
+
+ 0) Convey the Minimal Corresponding Source under the terms of this
+ License, and the Corresponding Application Code in a form
+ suitable for, and under terms that permit, the user to
+ recombine or relink the Application with a modified version of
+ the Linked Version to produce a modified Combined Work, in the
+ manner specified by section 6 of the GNU GPL for conveying
+ Corresponding Source.
+
+ 1) Use a suitable shared library mechanism for linking with the
+ Library. A suitable mechanism is one that (a) uses at run time
+ a copy of the Library already present on the user's computer
+ system, and (b) will operate properly with a modified version
+ of the Library that is interface-compatible with the Linked
+ Version.
+
+ e) Provide Installation Information, but only if you would otherwise
+ be required to provide such information under section 6 of the
+ GNU GPL, and only to the extent that such information is
+ necessary to install and execute a modified version of the
+ Combined Work produced by recombining or relinking the
+ Application with a modified version of the Linked Version. (If
+ you use option 4d0, the Installation Information must accompany
+ the Minimal Corresponding Source and Corresponding Application
+ Code. If you use option 4d1, you must provide the Installation
+ Information in the manner specified by section 6 of the GNU GPL
+ for conveying Corresponding Source.)
+
+ 5. Combined Libraries.
+
+ You may place library facilities that are a work based on the
+Library side by side in a single library together with other library
+facilities that are not Applications and are not covered by this
+License, and convey such a combined library under terms of your
+choice, if you do both of the following:
+
+ a) Accompany the combined library with a copy of the same work based
+ on the Library, uncombined with any other library facilities,
+ conveyed under the terms of this License.
+
+ b) Give prominent notice with the combined library that part of it
+ is a work based on the Library, and explaining where to find the
+ accompanying uncombined form of the same work.
+
+ 6. Revised Versions of the GNU Lesser General Public License.
+
+ The Free Software Foundation may publish revised and/or new versions
+of the GNU Lesser General Public License from time to time. Such new
+versions will be similar in spirit to the present version, but may
+differ in detail to address new problems or concerns.
+
+ Each version is given a distinguishing version number. If the
+Library as you received it specifies that a certain numbered version
+of the GNU Lesser General Public License "or any later version"
+applies to it, you have the option of following the terms and
+conditions either of that published version or of any later version
+published by the Free Software Foundation. If the Library as you
+received it does not specify a version number of the GNU Lesser
+General Public License, you may choose any version of the GNU Lesser
+General Public License ever published by the Free Software Foundation.
+
+ If the Library as you received it specifies that a proxy can decide
+whether future versions of the GNU Lesser General Public License shall
+apply, that proxy's public statement of acceptance of any version is
+permanent authorization for you to choose that version for the
+Library.
diff --git a/vendor/github.com/joyent/gocommon/LICENSE b/vendor/github.com/joyent/gocommon/LICENSE
new file mode 100644
index 000000000..c967dfa82
--- /dev/null
+++ b/vendor/github.com/joyent/gocommon/LICENSE
@@ -0,0 +1,15 @@
+GoCommon - Go Common Library for the Joyent Public Cloud and Joyent Manta
+
+Copyright (c) 2013, Joyent Inc.
+
+This program is free software: you can redistribute it and/or modify it under
+the terms of the GNU Lesser General Public License as published by the Free
+Software Foundation, either version 3 of the License, or (at your option) any
+later version.
+
+This program is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
+
+See both COPYING and COPYING.LESSER for the full terms of the GNU Lesser
+General Public License.
diff --git a/vendor/github.com/joyent/gocommon/README.md b/vendor/github.com/joyent/gocommon/README.md
new file mode 100644
index 000000000..113d10f19
--- /dev/null
+++ b/vendor/github.com/joyent/gocommon/README.md
@@ -0,0 +1,2 @@
+gocommon
+========
diff --git a/vendor/github.com/joyent/gocommon/client/client.go b/vendor/github.com/joyent/gocommon/client/client.go
new file mode 100644
index 000000000..b616625a6
--- /dev/null
+++ b/vendor/github.com/joyent/gocommon/client/client.go
@@ -0,0 +1,111 @@
+//
+// gocommon - Go library to interact with the JoyentCloud
+//
+//
+// Copyright (c) 2013 Joyent Inc.
+//
+// Written by Daniele Stroppa
+//
+
+package client
+
+import (
+ "fmt"
+ "net/url"
+ "strings"
+ "sync"
+ "time"
+
+ "github.com/juju/loggo"
+
+ 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 *loggo.Logger
+ baseURL string
+ creds *auth.Credentials
+ httpClient *joyenthttp.Client
+}
+
+var _ Client = (*client)(nil)
+
+func newClient(baseURL string, credentials *auth.Credentials, httpClient *joyenthttp.Client, logger *loggo.Logger) Client {
+ client := client{baseURL: baseURL, logger: logger, creds: credentials, httpClient: httpClient}
+ return &client
+}
+
+func NewClient(baseURL, apiVersion string, credentials *auth.Credentials, logger *loggo.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
+}
diff --git a/vendor/github.com/joyent/gocommon/errors/errors.go b/vendor/github.com/joyent/gocommon/errors/errors.go
new file mode 100644
index 000000000..f906e2c1a
--- /dev/null
+++ b/vendor/github.com/joyent/gocommon/errors/errors.go
@@ -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
+//
+
+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...)
+}
diff --git a/vendor/github.com/joyent/gocommon/gocommon.go b/vendor/github.com/joyent/gocommon/gocommon.go
new file mode 100644
index 000000000..1ffd7061c
--- /dev/null
+++ b/vendor/github.com/joyent/gocommon/gocommon.go
@@ -0,0 +1,18 @@
+/*
+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.
+
+Licensed under LGPL v3.
+
+Copyright (c) 2013 Joyent Inc.
+Written by Daniele Stroppa
+
+*/
+package gocommon
diff --git a/vendor/github.com/joyent/gocommon/http/client.go b/vendor/github.com/joyent/gocommon/http/client.go
new file mode 100644
index 000000000..1ac994287
--- /dev/null
+++ b/vendor/github.com/joyent/gocommon/http/client.go
@@ -0,0 +1,420 @@
+//
+// 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
+//
+
+package http
+
+import (
+ "bytes"
+ "encoding/json"
+ "fmt"
+ "io"
+ "io/ioutil"
+ "net/http"
+ "net/url"
+ "reflect"
+ "strconv"
+ "strings"
+ "time"
+
+ "github.com/juju/loggo"
+
+ "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 *loggo.Logger
+}
+
+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 *loggo.Logger) *Client {
+ return &Client{*http.DefaultClient, MaxSendAttempts, credentials, apiVersion, logger}
+}
+
+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 _, ok := response.RespValue.(*[]byte); ok {
+ response.RespValue = 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 *loggo.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 && logger.IsTraceEnabled() {
+ logger.Tracef("Request: %s %s\n", method, URL)
+ logger.Tracef("Request header: %s\n", headers)
+ logger.Tracef("Request body: %s\n", reqData)
+ logger.Tracef("Response: %s\n", rawResp.Status)
+ logger.Tracef("Response header: %s\n", rawResp.Header)
+ logger.Tracef("Response body: %s\n", rawResp.Body)
+ logger.Tracef("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 *loggo.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.Warningf("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")
+}
diff --git a/vendor/github.com/joyent/gocommon/jpc/jpc.go b/vendor/github.com/joyent/gocommon/jpc/jpc.go
new file mode 100644
index 000000000..775aa8dea
--- /dev/null
+++ b/vendor/github.com/joyent/gocommon/jpc/jpc.go
@@ -0,0 +1,105 @@
+//
+// gocommon - Go library to interact with the JoyentCloud
+//
+//
+// Copyright (c) 2013 Joyent Inc.
+//
+// Written by Daniele Stroppa
+//
+
+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
+}
diff --git a/vendor/github.com/joyent/gocommon/testing/httpsuite.go b/vendor/github.com/joyent/gocommon/testing/httpsuite.go
new file mode 100644
index 000000000..0580d9fdb
--- /dev/null
+++ b/vendor/github.com/joyent/gocommon/testing/httpsuite.go
@@ -0,0 +1,46 @@
+package testing
+
+// This package provides an HTTPSuite infrastructure that lets you bring up an
+// HTTP server. The server will handle requests based on whatever Handlers are
+// attached to HTTPSuite.Mux. This Mux is reset after every test case, and the
+// server is shut down at the end of the test suite.
+
+import (
+ gc "launchpad.net/gocheck"
+ "net/http"
+ "net/http/httptest"
+)
+
+var _ = gc.Suite(&HTTPSuite{})
+
+type HTTPSuite struct {
+ Server *httptest.Server
+ Mux *http.ServeMux
+ oldHandler http.Handler
+ UseTLS bool
+}
+
+func (s *HTTPSuite) SetUpSuite(c *gc.C) {
+ if s.UseTLS {
+ s.Server = httptest.NewTLSServer(nil)
+ } else {
+ s.Server = httptest.NewServer(nil)
+ }
+}
+
+func (s *HTTPSuite) SetUpTest(c *gc.C) {
+ s.oldHandler = s.Server.Config.Handler
+ s.Mux = http.NewServeMux()
+ s.Server.Config.Handler = s.Mux
+}
+
+func (s *HTTPSuite) TearDownTest(c *gc.C) {
+ s.Mux = nil
+ s.Server.Config.Handler = s.oldHandler
+}
+
+func (s *HTTPSuite) TearDownSuite(c *gc.C) {
+ if s.Server != nil {
+ s.Server.Close()
+ }
+}
diff --git a/vendor/github.com/joyent/gocommon/version.go b/vendor/github.com/joyent/gocommon/version.go
new file mode 100644
index 000000000..a8173be76
--- /dev/null
+++ b/vendor/github.com/joyent/gocommon/version.go
@@ -0,0 +1,32 @@
+//
+// gocommon - Go library to interact with the JoyentCloud
+//
+//
+// Copyright (c) 2013 Joyent Inc.
+//
+// Written by Daniele Stroppa
+//
+
+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()
diff --git a/vendor/github.com/joyent/gosdc/LICENSE b/vendor/github.com/joyent/gosdc/LICENSE
new file mode 100644
index 000000000..14e2f777f
--- /dev/null
+++ b/vendor/github.com/joyent/gosdc/LICENSE
@@ -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.
diff --git a/vendor/github.com/joyent/gosdc/cloudapi/cloudapi.go b/vendor/github.com/joyent/gosdc/cloudapi/cloudapi.go
new file mode 100644
index 000000000..c3f2dc7de
--- /dev/null
+++ b/vendor/github.com/joyent/gosdc/cloudapi/cloudapi.go
@@ -0,0 +1,130 @@
+/*
+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"
+ "github.com/juju/loggo"
+)
+
+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"
+
+ // CloudAPI actions
+ actionExport = "export"
+ actionStop = "stop"
+ actionStart = "start"
+ actionReboot = "reboot"
+ actionResize = "resize"
+ actionRename = "rename"
+ actionEnableFw = "enable_firewall"
+ actionDisableFw = "disable_firewall"
+)
+
+// Logger for this package
+var Logger = loggo.GetLogger("gosdc.cloudapi")
+
+// 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...)
+}
diff --git a/vendor/github.com/joyent/gosdc/cloudapi/datacenters.go b/vendor/github.com/joyent/gosdc/cloudapi/datacenters.go
new file mode 100644
index 000000000..e2bddf954
--- /dev/null
+++ b/vendor/github.com/joyent/gosdc/cloudapi/datacenters.go
@@ -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
+}
diff --git a/vendor/github.com/joyent/gosdc/cloudapi/fabrics.go b/vendor/github.com/joyent/gosdc/cloudapi/fabrics.go
new file mode 100644
index 000000000..cc36a7b3d
--- /dev/null
+++ b/vendor/github.com/joyent/gosdc/cloudapi/fabrics.go
@@ -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
+}
diff --git a/vendor/github.com/joyent/gosdc/cloudapi/firewalls.go b/vendor/github.com/joyent/gosdc/cloudapi/firewalls.go
new file mode 100644
index 000000000..a7763a668
--- /dev/null
+++ b/vendor/github.com/joyent/gosdc/cloudapi/firewalls.go
@@ -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 TO '
+}
+
+// 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 TO '
+}
+
+// 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
+}
diff --git a/vendor/github.com/joyent/gosdc/cloudapi/images.go b/vendor/github.com/joyent/gosdc/cloudapi/images.go
new file mode 100644
index 000000000..e3299d522
--- /dev/null
+++ b/vendor/github.com/joyent/gosdc/cloudapi/images.go
@@ -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 string // 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"` // Image description
+ Homepage string `json:"homepage"` // URL for a web page including detailed information for this image
+ EULA string `json:"eula"` // URL of the End User License Agreement (EULA) for the image
+ ACL []string `json:"acl"` // 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"` // 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
+}
diff --git a/vendor/github.com/joyent/gosdc/cloudapi/instrumentations.go b/vendor/github.com/joyent/gosdc/cloudapi/instrumentations.go
new file mode 100644
index 000000000..1dcd32777
--- /dev/null
+++ b/vendor/github.com/joyent/gosdc/cloudapi/instrumentations.go
@@ -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
+}
diff --git a/vendor/github.com/joyent/gosdc/cloudapi/keys.go b/vendor/github.com/joyent/gosdc/cloudapi/keys.go
new file mode 100644
index 000000000..fd9fd91b3
--- /dev/null
+++ b/vendor/github.com/joyent/gosdc/cloudapi/keys.go
@@ -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
+}
diff --git a/vendor/github.com/joyent/gosdc/cloudapi/machine_firewall.go b/vendor/github.com/joyent/gosdc/cloudapi/machine_firewall.go
new file mode 100644
index 000000000..60471e72e
--- /dev/null
+++ b/vendor/github.com/joyent/gosdc/cloudapi/machine_firewall.go
@@ -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
+}
diff --git a/vendor/github.com/joyent/gosdc/cloudapi/machine_metadata.go b/vendor/github.com/joyent/gosdc/cloudapi/machine_metadata.go
new file mode 100644
index 000000000..ca8d83ca9
--- /dev/null
+++ b/vendor/github.com/joyent/gosdc/cloudapi/machine_metadata.go
@@ -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
+}
diff --git a/vendor/github.com/joyent/gosdc/cloudapi/machine_nics.go b/vendor/github.com/joyent/gosdc/cloudapi/machine_nics.go
new file mode 100644
index 000000000..aa007d2f9
--- /dev/null
+++ b/vendor/github.com/joyent/gosdc/cloudapi/machine_nics.go
@@ -0,0 +1,92 @@
+package cloudapi
+
+import (
+ "net/http"
+
+ "github.com/joyent/gocommon/client"
+ "github.com/joyent/gocommon/errors"
+)
+
+type NICState string
+
+var (
+ NICStateProvisioning NICState = "provisioning"
+ NICStateRunning NICState = "running"
+ NICStateStopped NICState = "stopped"
+)
+
+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)
+}
+
+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) {
+ resp := make([]NIC, 0)
+ 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
+}
diff --git a/vendor/github.com/joyent/gosdc/cloudapi/machine_snapshots.go b/vendor/github.com/joyent/gosdc/cloudapi/machine_snapshots.go
new file mode 100644
index 000000000..0497a0fe5
--- /dev/null
+++ b/vendor/github.com/joyent/gosdc/cloudapi/machine_snapshots.go
@@ -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
+}
diff --git a/vendor/github.com/joyent/gosdc/cloudapi/machine_tags.go b/vendor/github.com/joyent/gosdc/cloudapi/machine_tags.go
new file mode 100644
index 000000000..9a5242bdb
--- /dev/null
+++ b/vendor/github.com/joyent/gosdc/cloudapi/machine_tags.go
@@ -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
+}
diff --git a/vendor/github.com/joyent/gosdc/cloudapi/machines.go b/vendor/github.com/joyent/gosdc/cloudapi/machines.go
new file mode 100644
index 000000000..073afb061
--- /dev/null
+++ b/vendor/github.com/joyent/gosdc/cloudapi/machines.go
@@ -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 // 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
+}
diff --git a/vendor/github.com/joyent/gosdc/cloudapi/networks.go b/vendor/github.com/joyent/gosdc/cloudapi/networks.go
new file mode 100644
index 000000000..18d828999
--- /dev/null
+++ b/vendor/github.com/joyent/gosdc/cloudapi/networks.go
@@ -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
+}
diff --git a/vendor/github.com/joyent/gosdc/cloudapi/packages.go b/vendor/github.com/joyent/gosdc/cloudapi/packages.go
new file mode 100644
index 000000000..9b3399916
--- /dev/null
+++ b/vendor/github.com/joyent/gosdc/cloudapi/packages.go
@@ -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
+}
diff --git a/vendor/github.com/joyent/gosign/COPYING b/vendor/github.com/joyent/gosign/COPYING
new file mode 100644
index 000000000..94a9ed024
--- /dev/null
+++ b/vendor/github.com/joyent/gosign/COPYING
@@ -0,0 +1,674 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc.
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The GNU General Public License is a free, copyleft license for
+software and other kinds of works.
+
+ The licenses for most software and other practical works are designed
+to take away your freedom to share and change the works. By contrast,
+the GNU General Public License is intended to guarantee your freedom to
+share and change all versions of a program--to make sure it remains free
+software for all its users. We, the Free Software Foundation, use the
+GNU General Public License for most of our software; it applies also to
+any other work released this way by its authors. You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+them if you wish), that you receive source code or can get it if you
+want it, that you can change the software or use pieces of it in new
+free programs, and that you know you can do these things.
+
+ To protect your rights, we need to prevent others from denying you
+these rights or asking you to surrender the rights. Therefore, you have
+certain responsibilities if you distribute copies of the software, or if
+you modify it: responsibilities to respect the freedom of others.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must pass on to the recipients the same
+freedoms that you received. You must make sure that they, too, receive
+or can get the source code. And you must show them these terms so they
+know their rights.
+
+ Developers that use the GNU GPL protect your rights with two steps:
+(1) assert copyright on the software, and (2) offer you this License
+giving you legal permission to copy, distribute and/or modify it.
+
+ For the developers' and authors' protection, the GPL clearly explains
+that there is no warranty for this free software. For both users' and
+authors' sake, the GPL requires that modified versions be marked as
+changed, so that their problems will not be attributed erroneously to
+authors of previous versions.
+
+ Some devices are designed to deny users access to install or run
+modified versions of the software inside them, although the manufacturer
+can do so. This is fundamentally incompatible with the aim of
+protecting users' freedom to change the software. The systematic
+pattern of such abuse occurs in the area of products for individuals to
+use, which is precisely where it is most unacceptable. Therefore, we
+have designed this version of the GPL to prohibit the practice for those
+products. If such problems arise substantially in other domains, we
+stand ready to extend this provision to those domains in future versions
+of the GPL, as needed to protect the freedom of users.
+
+ Finally, every program is threatened constantly by software patents.
+States should not allow patents to restrict development and use of
+software on general-purpose computers, but in those that do, we wish to
+avoid the special danger that patents applied to a free program could
+make it effectively proprietary. To prevent this, the GPL assures that
+patents cannot be used to render the program non-free.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ TERMS AND CONDITIONS
+
+ 0. Definitions.
+
+ "This License" refers to version 3 of the GNU General Public License.
+
+ "Copyright" also means copyright-like laws that apply to other kinds of
+works, such as semiconductor masks.
+
+ "The Program" refers to any copyrightable work licensed under this
+License. Each licensee is addressed as "you". "Licensees" and
+"recipients" may be individuals or organizations.
+
+ To "modify" a work means to copy from or adapt all or part of the work
+in a fashion requiring copyright permission, other than the making of an
+exact copy. The resulting work is called a "modified version" of the
+earlier work or a work "based on" the earlier work.
+
+ A "covered work" means either the unmodified Program or a work based
+on the Program.
+
+ To "propagate" a work means to do anything with it that, without
+permission, would make you directly or secondarily liable for
+infringement under applicable copyright law, except executing it on a
+computer or modifying a private copy. Propagation includes copying,
+distribution (with or without modification), making available to the
+public, and in some countries other activities as well.
+
+ To "convey" a work means any kind of propagation that enables other
+parties to make or receive copies. Mere interaction with a user through
+a computer network, with no transfer of a copy, is not conveying.
+
+ An interactive user interface displays "Appropriate Legal Notices"
+to the extent that it includes a convenient and prominently visible
+feature that (1) displays an appropriate copyright notice, and (2)
+tells the user that there is no warranty for the work (except to the
+extent that warranties are provided), that licensees may convey the
+work under this License, and how to view a copy of this License. If
+the interface presents a list of user commands or options, such as a
+menu, a prominent item in the list meets this criterion.
+
+ 1. Source Code.
+
+ The "source code" for a work means the preferred form of the work
+for making modifications to it. "Object code" means any non-source
+form of a work.
+
+ A "Standard Interface" means an interface that either is an official
+standard defined by a recognized standards body, or, in the case of
+interfaces specified for a particular programming language, one that
+is widely used among developers working in that language.
+
+ The "System Libraries" of an executable work include anything, other
+than the work as a whole, that (a) is included in the normal form of
+packaging a Major Component, but which is not part of that Major
+Component, and (b) serves only to enable use of the work with that
+Major Component, or to implement a Standard Interface for which an
+implementation is available to the public in source code form. A
+"Major Component", in this context, means a major essential component
+(kernel, window system, and so on) of the specific operating system
+(if any) on which the executable work runs, or a compiler used to
+produce the work, or an object code interpreter used to run it.
+
+ The "Corresponding Source" for a work in object code form means all
+the source code needed to generate, install, and (for an executable
+work) run the object code and to modify the work, including scripts to
+control those activities. However, it does not include the work's
+System Libraries, or general-purpose tools or generally available free
+programs which are used unmodified in performing those activities but
+which are not part of the work. For example, Corresponding Source
+includes interface definition files associated with source files for
+the work, and the source code for shared libraries and dynamically
+linked subprograms that the work is specifically designed to require,
+such as by intimate data communication or control flow between those
+subprograms and other parts of the work.
+
+ The Corresponding Source need not include anything that users
+can regenerate automatically from other parts of the Corresponding
+Source.
+
+ The Corresponding Source for a work in source code form is that
+same work.
+
+ 2. Basic Permissions.
+
+ All rights granted under this License are granted for the term of
+copyright on the Program, and are irrevocable provided the stated
+conditions are met. This License explicitly affirms your unlimited
+permission to run the unmodified Program. The output from running a
+covered work is covered by this License only if the output, given its
+content, constitutes a covered work. This License acknowledges your
+rights of fair use or other equivalent, as provided by copyright law.
+
+ You may make, run and propagate covered works that you do not
+convey, without conditions so long as your license otherwise remains
+in force. You may convey covered works to others for the sole purpose
+of having them make modifications exclusively for you, or provide you
+with facilities for running those works, provided that you comply with
+the terms of this License in conveying all material for which you do
+not control copyright. Those thus making or running the covered works
+for you must do so exclusively on your behalf, under your direction
+and control, on terms that prohibit them from making any copies of
+your copyrighted material outside their relationship with you.
+
+ Conveying under any other circumstances is permitted solely under
+the conditions stated below. Sublicensing is not allowed; section 10
+makes it unnecessary.
+
+ 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
+
+ No covered work shall be deemed part of an effective technological
+measure under any applicable law fulfilling obligations under article
+11 of the WIPO copyright treaty adopted on 20 December 1996, or
+similar laws prohibiting or restricting circumvention of such
+measures.
+
+ When you convey a covered work, you waive any legal power to forbid
+circumvention of technological measures to the extent such circumvention
+is effected by exercising rights under this License with respect to
+the covered work, and you disclaim any intention to limit operation or
+modification of the work as a means of enforcing, against the work's
+users, your or third parties' legal rights to forbid circumvention of
+technological measures.
+
+ 4. Conveying Verbatim Copies.
+
+ You may convey verbatim copies of the Program's source code as you
+receive it, in any medium, provided that you conspicuously and
+appropriately publish on each copy an appropriate copyright notice;
+keep intact all notices stating that this License and any
+non-permissive terms added in accord with section 7 apply to the code;
+keep intact all notices of the absence of any warranty; and give all
+recipients a copy of this License along with the Program.
+
+ You may charge any price or no price for each copy that you convey,
+and you may offer support or warranty protection for a fee.
+
+ 5. Conveying Modified Source Versions.
+
+ You may convey a work based on the Program, or the modifications to
+produce it from the Program, in the form of source code under the
+terms of section 4, provided that you also meet all of these conditions:
+
+ a) The work must carry prominent notices stating that you modified
+ it, and giving a relevant date.
+
+ b) The work must carry prominent notices stating that it is
+ released under this License and any conditions added under section
+ 7. This requirement modifies the requirement in section 4 to
+ "keep intact all notices".
+
+ c) You must license the entire work, as a whole, under this
+ License to anyone who comes into possession of a copy. This
+ License will therefore apply, along with any applicable section 7
+ additional terms, to the whole of the work, and all its parts,
+ regardless of how they are packaged. This License gives no
+ permission to license the work in any other way, but it does not
+ invalidate such permission if you have separately received it.
+
+ d) If the work has interactive user interfaces, each must display
+ Appropriate Legal Notices; however, if the Program has interactive
+ interfaces that do not display Appropriate Legal Notices, your
+ work need not make them do so.
+
+ A compilation of a covered work with other separate and independent
+works, which are not by their nature extensions of the covered work,
+and which are not combined with it such as to form a larger program,
+in or on a volume of a storage or distribution medium, is called an
+"aggregate" if the compilation and its resulting copyright are not
+used to limit the access or legal rights of the compilation's users
+beyond what the individual works permit. Inclusion of a covered work
+in an aggregate does not cause this License to apply to the other
+parts of the aggregate.
+
+ 6. Conveying Non-Source Forms.
+
+ You may convey a covered work in object code form under the terms
+of sections 4 and 5, provided that you also convey the
+machine-readable Corresponding Source under the terms of this License,
+in one of these ways:
+
+ a) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by the
+ Corresponding Source fixed on a durable physical medium
+ customarily used for software interchange.
+
+ b) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by a
+ written offer, valid for at least three years and valid for as
+ long as you offer spare parts or customer support for that product
+ model, to give anyone who possesses the object code either (1) a
+ copy of the Corresponding Source for all the software in the
+ product that is covered by this License, on a durable physical
+ medium customarily used for software interchange, for a price no
+ more than your reasonable cost of physically performing this
+ conveying of source, or (2) access to copy the
+ Corresponding Source from a network server at no charge.
+
+ c) Convey individual copies of the object code with a copy of the
+ written offer to provide the Corresponding Source. This
+ alternative is allowed only occasionally and noncommercially, and
+ only if you received the object code with such an offer, in accord
+ with subsection 6b.
+
+ d) Convey the object code by offering access from a designated
+ place (gratis or for a charge), and offer equivalent access to the
+ Corresponding Source in the same way through the same place at no
+ further charge. You need not require recipients to copy the
+ Corresponding Source along with the object code. If the place to
+ copy the object code is a network server, the Corresponding Source
+ may be on a different server (operated by you or a third party)
+ that supports equivalent copying facilities, provided you maintain
+ clear directions next to the object code saying where to find the
+ Corresponding Source. Regardless of what server hosts the
+ Corresponding Source, you remain obligated to ensure that it is
+ available for as long as needed to satisfy these requirements.
+
+ e) Convey the object code using peer-to-peer transmission, provided
+ you inform other peers where the object code and Corresponding
+ Source of the work are being offered to the general public at no
+ charge under subsection 6d.
+
+ A separable portion of the object code, whose source code is excluded
+from the Corresponding Source as a System Library, need not be
+included in conveying the object code work.
+
+ A "User Product" is either (1) a "consumer product", which means any
+tangible personal property which is normally used for personal, family,
+or household purposes, or (2) anything designed or sold for incorporation
+into a dwelling. In determining whether a product is a consumer product,
+doubtful cases shall be resolved in favor of coverage. For a particular
+product received by a particular user, "normally used" refers to a
+typical or common use of that class of product, regardless of the status
+of the particular user or of the way in which the particular user
+actually uses, or expects or is expected to use, the product. A product
+is a consumer product regardless of whether the product has substantial
+commercial, industrial or non-consumer uses, unless such uses represent
+the only significant mode of use of the product.
+
+ "Installation Information" for a User Product means any methods,
+procedures, authorization keys, or other information required to install
+and execute modified versions of a covered work in that User Product from
+a modified version of its Corresponding Source. The information must
+suffice to ensure that the continued functioning of the modified object
+code is in no case prevented or interfered with solely because
+modification has been made.
+
+ If you convey an object code work under this section in, or with, or
+specifically for use in, a User Product, and the conveying occurs as
+part of a transaction in which the right of possession and use of the
+User Product is transferred to the recipient in perpetuity or for a
+fixed term (regardless of how the transaction is characterized), the
+Corresponding Source conveyed under this section must be accompanied
+by the Installation Information. But this requirement does not apply
+if neither you nor any third party retains the ability to install
+modified object code on the User Product (for example, the work has
+been installed in ROM).
+
+ The requirement to provide Installation Information does not include a
+requirement to continue to provide support service, warranty, or updates
+for a work that has been modified or installed by the recipient, or for
+the User Product in which it has been modified or installed. Access to a
+network may be denied when the modification itself materially and
+adversely affects the operation of the network or violates the rules and
+protocols for communication across the network.
+
+ Corresponding Source conveyed, and Installation Information provided,
+in accord with this section must be in a format that is publicly
+documented (and with an implementation available to the public in
+source code form), and must require no special password or key for
+unpacking, reading or copying.
+
+ 7. Additional Terms.
+
+ "Additional permissions" are terms that supplement the terms of this
+License by making exceptions from one or more of its conditions.
+Additional permissions that are applicable to the entire Program shall
+be treated as though they were included in this License, to the extent
+that they are valid under applicable law. If additional permissions
+apply only to part of the Program, that part may be used separately
+under those permissions, but the entire Program remains governed by
+this License without regard to the additional permissions.
+
+ When you convey a copy of a covered work, you may at your option
+remove any additional permissions from that copy, or from any part of
+it. (Additional permissions may be written to require their own
+removal in certain cases when you modify the work.) You may place
+additional permissions on material, added by you to a covered work,
+for which you have or can give appropriate copyright permission.
+
+ Notwithstanding any other provision of this License, for material you
+add to a covered work, you may (if authorized by the copyright holders of
+that material) supplement the terms of this License with terms:
+
+ a) Disclaiming warranty or limiting liability differently from the
+ terms of sections 15 and 16 of this License; or
+
+ b) Requiring preservation of specified reasonable legal notices or
+ author attributions in that material or in the Appropriate Legal
+ Notices displayed by works containing it; or
+
+ c) Prohibiting misrepresentation of the origin of that material, or
+ requiring that modified versions of such material be marked in
+ reasonable ways as different from the original version; or
+
+ d) Limiting the use for publicity purposes of names of licensors or
+ authors of the material; or
+
+ e) Declining to grant rights under trademark law for use of some
+ trade names, trademarks, or service marks; or
+
+ f) Requiring indemnification of licensors and authors of that
+ material by anyone who conveys the material (or modified versions of
+ it) with contractual assumptions of liability to the recipient, for
+ any liability that these contractual assumptions directly impose on
+ those licensors and authors.
+
+ All other non-permissive additional terms are considered "further
+restrictions" within the meaning of section 10. If the Program as you
+received it, or any part of it, contains a notice stating that it is
+governed by this License along with a term that is a further
+restriction, you may remove that term. If a license document contains
+a further restriction but permits relicensing or conveying under this
+License, you may add to a covered work material governed by the terms
+of that license document, provided that the further restriction does
+not survive such relicensing or conveying.
+
+ If you add terms to a covered work in accord with this section, you
+must place, in the relevant source files, a statement of the
+additional terms that apply to those files, or a notice indicating
+where to find the applicable terms.
+
+ Additional terms, permissive or non-permissive, may be stated in the
+form of a separately written license, or stated as exceptions;
+the above requirements apply either way.
+
+ 8. Termination.
+
+ You may not propagate or modify a covered work except as expressly
+provided under this License. Any attempt otherwise to propagate or
+modify it is void, and will automatically terminate your rights under
+this License (including any patent licenses granted under the third
+paragraph of section 11).
+
+ However, if you cease all violation of this License, then your
+license from a particular copyright holder is reinstated (a)
+provisionally, unless and until the copyright holder explicitly and
+finally terminates your license, and (b) permanently, if the copyright
+holder fails to notify you of the violation by some reasonable means
+prior to 60 days after the cessation.
+
+ Moreover, your license from a particular copyright holder is
+reinstated permanently if the copyright holder notifies you of the
+violation by some reasonable means, this is the first time you have
+received notice of violation of this License (for any work) from that
+copyright holder, and you cure the violation prior to 30 days after
+your receipt of the notice.
+
+ Termination of your rights under this section does not terminate the
+licenses of parties who have received copies or rights from you under
+this License. If your rights have been terminated and not permanently
+reinstated, you do not qualify to receive new licenses for the same
+material under section 10.
+
+ 9. Acceptance Not Required for Having Copies.
+
+ You are not required to accept this License in order to receive or
+run a copy of the Program. Ancillary propagation of a covered work
+occurring solely as a consequence of using peer-to-peer transmission
+to receive a copy likewise does not require acceptance. However,
+nothing other than this License grants you permission to propagate or
+modify any covered work. These actions infringe copyright if you do
+not accept this License. Therefore, by modifying or propagating a
+covered work, you indicate your acceptance of this License to do so.
+
+ 10. Automatic Licensing of Downstream Recipients.
+
+ Each time you convey a covered work, the recipient automatically
+receives a license from the original licensors, to run, modify and
+propagate that work, subject to this License. You are not responsible
+for enforcing compliance by third parties with this License.
+
+ An "entity transaction" is a transaction transferring control of an
+organization, or substantially all assets of one, or subdividing an
+organization, or merging organizations. If propagation of a covered
+work results from an entity transaction, each party to that
+transaction who receives a copy of the work also receives whatever
+licenses to the work the party's predecessor in interest had or could
+give under the previous paragraph, plus a right to possession of the
+Corresponding Source of the work from the predecessor in interest, if
+the predecessor has it or can get it with reasonable efforts.
+
+ You may not impose any further restrictions on the exercise of the
+rights granted or affirmed under this License. For example, you may
+not impose a license fee, royalty, or other charge for exercise of
+rights granted under this License, and you may not initiate litigation
+(including a cross-claim or counterclaim in a lawsuit) alleging that
+any patent claim is infringed by making, using, selling, offering for
+sale, or importing the Program or any portion of it.
+
+ 11. Patents.
+
+ A "contributor" is a copyright holder who authorizes use under this
+License of the Program or a work on which the Program is based. The
+work thus licensed is called the contributor's "contributor version".
+
+ A contributor's "essential patent claims" are all patent claims
+owned or controlled by the contributor, whether already acquired or
+hereafter acquired, that would be infringed by some manner, permitted
+by this License, of making, using, or selling its contributor version,
+but do not include claims that would be infringed only as a
+consequence of further modification of the contributor version. For
+purposes of this definition, "control" includes the right to grant
+patent sublicenses in a manner consistent with the requirements of
+this License.
+
+ Each contributor grants you a non-exclusive, worldwide, royalty-free
+patent license under the contributor's essential patent claims, to
+make, use, sell, offer for sale, import and otherwise run, modify and
+propagate the contents of its contributor version.
+
+ In the following three paragraphs, a "patent license" is any express
+agreement or commitment, however denominated, not to enforce a patent
+(such as an express permission to practice a patent or covenant not to
+sue for patent infringement). To "grant" such a patent license to a
+party means to make such an agreement or commitment not to enforce a
+patent against the party.
+
+ If you convey a covered work, knowingly relying on a patent license,
+and the Corresponding Source of the work is not available for anyone
+to copy, free of charge and under the terms of this License, through a
+publicly available network server or other readily accessible means,
+then you must either (1) cause the Corresponding Source to be so
+available, or (2) arrange to deprive yourself of the benefit of the
+patent license for this particular work, or (3) arrange, in a manner
+consistent with the requirements of this License, to extend the patent
+license to downstream recipients. "Knowingly relying" means you have
+actual knowledge that, but for the patent license, your conveying the
+covered work in a country, or your recipient's use of the covered work
+in a country, would infringe one or more identifiable patents in that
+country that you have reason to believe are valid.
+
+ If, pursuant to or in connection with a single transaction or
+arrangement, you convey, or propagate by procuring conveyance of, a
+covered work, and grant a patent license to some of the parties
+receiving the covered work authorizing them to use, propagate, modify
+or convey a specific copy of the covered work, then the patent license
+you grant is automatically extended to all recipients of the covered
+work and works based on it.
+
+ A patent license is "discriminatory" if it does not include within
+the scope of its coverage, prohibits the exercise of, or is
+conditioned on the non-exercise of one or more of the rights that are
+specifically granted under this License. You may not convey a covered
+work if you are a party to an arrangement with a third party that is
+in the business of distributing software, under which you make payment
+to the third party based on the extent of your activity of conveying
+the work, and under which the third party grants, to any of the
+parties who would receive the covered work from you, a discriminatory
+patent license (a) in connection with copies of the covered work
+conveyed by you (or copies made from those copies), or (b) primarily
+for and in connection with specific products or compilations that
+contain the covered work, unless you entered into that arrangement,
+or that patent license was granted, prior to 28 March 2007.
+
+ Nothing in this License shall be construed as excluding or limiting
+any implied license or other defenses to infringement that may
+otherwise be available to you under applicable patent law.
+
+ 12. No Surrender of Others' Freedom.
+
+ If conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot convey a
+covered work so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you may
+not convey it at all. For example, if you agree to terms that obligate you
+to collect a royalty for further conveying from those to whom you convey
+the Program, the only way you could satisfy both those terms and this
+License would be to refrain entirely from conveying the Program.
+
+ 13. Use with the GNU Affero General Public License.
+
+ Notwithstanding any other provision of this License, you have
+permission to link or combine any covered work with a work licensed
+under version 3 of the GNU Affero General Public License into a single
+combined work, and to convey the resulting work. The terms of this
+License will continue to apply to the part which is the covered work,
+but the special requirements of the GNU Affero General Public License,
+section 13, concerning interaction through a network will apply to the
+combination as such.
+
+ 14. Revised Versions of this License.
+
+ The Free Software Foundation may publish revised and/or new versions of
+the GNU General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+ Each version is given a distinguishing version number. If the
+Program specifies that a certain numbered version of the GNU General
+Public License "or any later version" applies to it, you have the
+option of following the terms and conditions either of that numbered
+version or of any later version published by the Free Software
+Foundation. If the Program does not specify a version number of the
+GNU General Public License, you may choose any version ever published
+by the Free Software Foundation.
+
+ If the Program specifies that a proxy can decide which future
+versions of the GNU General Public License can be used, that proxy's
+public statement of acceptance of a version permanently authorizes you
+to choose that version for the Program.
+
+ Later license versions may give you additional or different
+permissions. However, no additional obligations are imposed on any
+author or copyright holder as a result of your choosing to follow a
+later version.
+
+ 15. Disclaimer of Warranty.
+
+ THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
+APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
+HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
+OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
+IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
+ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+ 16. Limitation of Liability.
+
+ IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
+THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
+GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
+USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
+DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
+PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
+EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGES.
+
+ 17. Interpretation of Sections 15 and 16.
+
+ If the disclaimer of warranty and limitation of liability provided
+above cannot be given local legal effect according to their terms,
+reviewing courts shall apply local law that most closely approximates
+an absolute waiver of all civil liability in connection with the
+Program, unless a warranty or assumption of liability accompanies a
+copy of the Program in return for a fee.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+state the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+
+ Copyright (C)
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see .
+
+Also add information on how to contact you by electronic and paper mail.
+
+ If the program does terminal interaction, make it output a short
+notice like this when it starts in an interactive mode:
+
+ Copyright (C)
+ This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, your program's commands
+might be different; for a GUI interface, you would use an "about box".
+
+ You should also get your employer (if you work as a programmer) or school,
+if any, to sign a "copyright disclaimer" for the program, if necessary.
+For more information on this, and how to apply and follow the GNU GPL, see
+.
+
+ The GNU General Public License does not permit incorporating your program
+into proprietary programs. If your program is a subroutine library, you
+may consider it more useful to permit linking proprietary applications with
+the library. If this is what you want to do, use the GNU Lesser General
+Public License instead of this License. But first, please read
+.
diff --git a/vendor/github.com/joyent/gosign/COPYING.LESSER b/vendor/github.com/joyent/gosign/COPYING.LESSER
new file mode 100644
index 000000000..65c5ca88a
--- /dev/null
+++ b/vendor/github.com/joyent/gosign/COPYING.LESSER
@@ -0,0 +1,165 @@
+ GNU LESSER GENERAL PUBLIC LICENSE
+ Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc.
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+
+ This version of the GNU Lesser General Public License incorporates
+the terms and conditions of version 3 of the GNU General Public
+License, supplemented by the additional permissions listed below.
+
+ 0. Additional Definitions.
+
+ As used herein, "this License" refers to version 3 of the GNU Lesser
+General Public License, and the "GNU GPL" refers to version 3 of the GNU
+General Public License.
+
+ "The Library" refers to a covered work governed by this License,
+other than an Application or a Combined Work as defined below.
+
+ An "Application" is any work that makes use of an interface provided
+by the Library, but which is not otherwise based on the Library.
+Defining a subclass of a class defined by the Library is deemed a mode
+of using an interface provided by the Library.
+
+ A "Combined Work" is a work produced by combining or linking an
+Application with the Library. The particular version of the Library
+with which the Combined Work was made is also called the "Linked
+Version".
+
+ The "Minimal Corresponding Source" for a Combined Work means the
+Corresponding Source for the Combined Work, excluding any source code
+for portions of the Combined Work that, considered in isolation, are
+based on the Application, and not on the Linked Version.
+
+ The "Corresponding Application Code" for a Combined Work means the
+object code and/or source code for the Application, including any data
+and utility programs needed for reproducing the Combined Work from the
+Application, but excluding the System Libraries of the Combined Work.
+
+ 1. Exception to Section 3 of the GNU GPL.
+
+ You may convey a covered work under sections 3 and 4 of this License
+without being bound by section 3 of the GNU GPL.
+
+ 2. Conveying Modified Versions.
+
+ If you modify a copy of the Library, and, in your modifications, a
+facility refers to a function or data to be supplied by an Application
+that uses the facility (other than as an argument passed when the
+facility is invoked), then you may convey a copy of the modified
+version:
+
+ a) under this License, provided that you make a good faith effort to
+ ensure that, in the event an Application does not supply the
+ function or data, the facility still operates, and performs
+ whatever part of its purpose remains meaningful, or
+
+ b) under the GNU GPL, with none of the additional permissions of
+ this License applicable to that copy.
+
+ 3. Object Code Incorporating Material from Library Header Files.
+
+ The object code form of an Application may incorporate material from
+a header file that is part of the Library. You may convey such object
+code under terms of your choice, provided that, if the incorporated
+material is not limited to numerical parameters, data structure
+layouts and accessors, or small macros, inline functions and templates
+(ten or fewer lines in length), you do both of the following:
+
+ a) Give prominent notice with each copy of the object code that the
+ Library is used in it and that the Library and its use are
+ covered by this License.
+
+ b) Accompany the object code with a copy of the GNU GPL and this license
+ document.
+
+ 4. Combined Works.
+
+ You may convey a Combined Work under terms of your choice that,
+taken together, effectively do not restrict modification of the
+portions of the Library contained in the Combined Work and reverse
+engineering for debugging such modifications, if you also do each of
+the following:
+
+ a) Give prominent notice with each copy of the Combined Work that
+ the Library is used in it and that the Library and its use are
+ covered by this License.
+
+ b) Accompany the Combined Work with a copy of the GNU GPL and this license
+ document.
+
+ c) For a Combined Work that displays copyright notices during
+ execution, include the copyright notice for the Library among
+ these notices, as well as a reference directing the user to the
+ copies of the GNU GPL and this license document.
+
+ d) Do one of the following:
+
+ 0) Convey the Minimal Corresponding Source under the terms of this
+ License, and the Corresponding Application Code in a form
+ suitable for, and under terms that permit, the user to
+ recombine or relink the Application with a modified version of
+ the Linked Version to produce a modified Combined Work, in the
+ manner specified by section 6 of the GNU GPL for conveying
+ Corresponding Source.
+
+ 1) Use a suitable shared library mechanism for linking with the
+ Library. A suitable mechanism is one that (a) uses at run time
+ a copy of the Library already present on the user's computer
+ system, and (b) will operate properly with a modified version
+ of the Library that is interface-compatible with the Linked
+ Version.
+
+ e) Provide Installation Information, but only if you would otherwise
+ be required to provide such information under section 6 of the
+ GNU GPL, and only to the extent that such information is
+ necessary to install and execute a modified version of the
+ Combined Work produced by recombining or relinking the
+ Application with a modified version of the Linked Version. (If
+ you use option 4d0, the Installation Information must accompany
+ the Minimal Corresponding Source and Corresponding Application
+ Code. If you use option 4d1, you must provide the Installation
+ Information in the manner specified by section 6 of the GNU GPL
+ for conveying Corresponding Source.)
+
+ 5. Combined Libraries.
+
+ You may place library facilities that are a work based on the
+Library side by side in a single library together with other library
+facilities that are not Applications and are not covered by this
+License, and convey such a combined library under terms of your
+choice, if you do both of the following:
+
+ a) Accompany the combined library with a copy of the same work based
+ on the Library, uncombined with any other library facilities,
+ conveyed under the terms of this License.
+
+ b) Give prominent notice with the combined library that part of it
+ is a work based on the Library, and explaining where to find the
+ accompanying uncombined form of the same work.
+
+ 6. Revised Versions of the GNU Lesser General Public License.
+
+ The Free Software Foundation may publish revised and/or new versions
+of the GNU Lesser General Public License from time to time. Such new
+versions will be similar in spirit to the present version, but may
+differ in detail to address new problems or concerns.
+
+ Each version is given a distinguishing version number. If the
+Library as you received it specifies that a certain numbered version
+of the GNU Lesser General Public License "or any later version"
+applies to it, you have the option of following the terms and
+conditions either of that published version or of any later version
+published by the Free Software Foundation. If the Library as you
+received it does not specify a version number of the GNU Lesser
+General Public License, you may choose any version of the GNU Lesser
+General Public License ever published by the Free Software Foundation.
+
+ If the Library as you received it specifies that a proxy can decide
+whether future versions of the GNU Lesser General Public License shall
+apply, that proxy's public statement of acceptance of any version is
+permanent authorization for you to choose that version for the
+Library.
diff --git a/vendor/github.com/joyent/gosign/LICENSE b/vendor/github.com/joyent/gosign/LICENSE
new file mode 100644
index 000000000..28eb66890
--- /dev/null
+++ b/vendor/github.com/joyent/gosign/LICENSE
@@ -0,0 +1,15 @@
+GoSign - Go HTTP signing library for the Joyent Public Cloud
+
+Copyright (c) 2013, Joyent Inc.
+
+This program is free software: you can redistribute it and/or modify it under
+the terms of the GNU Lesser General Public License as published by the Free
+Software Foundation, either version 3 of the License, or (at your option) any
+later version.
+
+This program is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
+
+See both COPYING and COPYING.LESSER for the full terms of the GNU Lesser
+General Public License.
diff --git a/vendor/github.com/joyent/gosign/auth/auth.go b/vendor/github.com/joyent/gosign/auth/auth.go
new file mode 100644
index 000000000..a49cb0852
--- /dev/null
+++ b/vendor/github.com/joyent/gosign/auth/auth.go
@@ -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
+//
+
+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
+}
diff --git a/vendor/github.com/juju/loggo/.gitignore b/vendor/github.com/juju/loggo/.gitignore
new file mode 100644
index 000000000..528cd5b39
--- /dev/null
+++ b/vendor/github.com/juju/loggo/.gitignore
@@ -0,0 +1 @@
+example/example
diff --git a/vendor/github.com/juju/loggo/LICENSE b/vendor/github.com/juju/loggo/LICENSE
new file mode 100644
index 000000000..ade9307b3
--- /dev/null
+++ b/vendor/github.com/juju/loggo/LICENSE
@@ -0,0 +1,191 @@
+All files in this repository are licensed as follows. If you contribute
+to this repository, it is assumed that you license your contribution
+under the same license unless you state otherwise.
+
+All files Copyright (C) 2015 Canonical Ltd. unless otherwise specified in the file.
+
+This software is licensed under the LGPLv3, included below.
+
+As a special exception to the GNU Lesser General Public License version 3
+("LGPL3"), the copyright holders of this Library give you permission to
+convey to a third party a Combined Work that links statically or dynamically
+to this Library without providing any Minimal Corresponding Source or
+Minimal Application Code as set out in 4d or providing the installation
+information set out in section 4e, provided that you comply with the other
+provisions of LGPL3 and provided that you meet, for the Application the
+terms and conditions of the license(s) which apply to the Application.
+
+Except as stated in this special exception, the provisions of LGPL3 will
+continue to comply in full to this Library. If you modify this Library, you
+may apply this exception to your version of this Library, but you are not
+obliged to do so. If you do not wish to do so, delete this exception
+statement from your version. This exception does not (and cannot) modify any
+license terms which apply to the Application, with which you must still
+comply.
+
+
+ GNU LESSER GENERAL PUBLIC LICENSE
+ Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc.
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+
+ This version of the GNU Lesser General Public License incorporates
+the terms and conditions of version 3 of the GNU General Public
+License, supplemented by the additional permissions listed below.
+
+ 0. Additional Definitions.
+
+ As used herein, "this License" refers to version 3 of the GNU Lesser
+General Public License, and the "GNU GPL" refers to version 3 of the GNU
+General Public License.
+
+ "The Library" refers to a covered work governed by this License,
+other than an Application or a Combined Work as defined below.
+
+ An "Application" is any work that makes use of an interface provided
+by the Library, but which is not otherwise based on the Library.
+Defining a subclass of a class defined by the Library is deemed a mode
+of using an interface provided by the Library.
+
+ A "Combined Work" is a work produced by combining or linking an
+Application with the Library. The particular version of the Library
+with which the Combined Work was made is also called the "Linked
+Version".
+
+ The "Minimal Corresponding Source" for a Combined Work means the
+Corresponding Source for the Combined Work, excluding any source code
+for portions of the Combined Work that, considered in isolation, are
+based on the Application, and not on the Linked Version.
+
+ The "Corresponding Application Code" for a Combined Work means the
+object code and/or source code for the Application, including any data
+and utility programs needed for reproducing the Combined Work from the
+Application, but excluding the System Libraries of the Combined Work.
+
+ 1. Exception to Section 3 of the GNU GPL.
+
+ You may convey a covered work under sections 3 and 4 of this License
+without being bound by section 3 of the GNU GPL.
+
+ 2. Conveying Modified Versions.
+
+ If you modify a copy of the Library, and, in your modifications, a
+facility refers to a function or data to be supplied by an Application
+that uses the facility (other than as an argument passed when the
+facility is invoked), then you may convey a copy of the modified
+version:
+
+ a) under this License, provided that you make a good faith effort to
+ ensure that, in the event an Application does not supply the
+ function or data, the facility still operates, and performs
+ whatever part of its purpose remains meaningful, or
+
+ b) under the GNU GPL, with none of the additional permissions of
+ this License applicable to that copy.
+
+ 3. Object Code Incorporating Material from Library Header Files.
+
+ The object code form of an Application may incorporate material from
+a header file that is part of the Library. You may convey such object
+code under terms of your choice, provided that, if the incorporated
+material is not limited to numerical parameters, data structure
+layouts and accessors, or small macros, inline functions and templates
+(ten or fewer lines in length), you do both of the following:
+
+ a) Give prominent notice with each copy of the object code that the
+ Library is used in it and that the Library and its use are
+ covered by this License.
+
+ b) Accompany the object code with a copy of the GNU GPL and this license
+ document.
+
+ 4. Combined Works.
+
+ You may convey a Combined Work under terms of your choice that,
+taken together, effectively do not restrict modification of the
+portions of the Library contained in the Combined Work and reverse
+engineering for debugging such modifications, if you also do each of
+the following:
+
+ a) Give prominent notice with each copy of the Combined Work that
+ the Library is used in it and that the Library and its use are
+ covered by this License.
+
+ b) Accompany the Combined Work with a copy of the GNU GPL and this license
+ document.
+
+ c) For a Combined Work that displays copyright notices during
+ execution, include the copyright notice for the Library among
+ these notices, as well as a reference directing the user to the
+ copies of the GNU GPL and this license document.
+
+ d) Do one of the following:
+
+ 0) Convey the Minimal Corresponding Source under the terms of this
+ License, and the Corresponding Application Code in a form
+ suitable for, and under terms that permit, the user to
+ recombine or relink the Application with a modified version of
+ the Linked Version to produce a modified Combined Work, in the
+ manner specified by section 6 of the GNU GPL for conveying
+ Corresponding Source.
+
+ 1) Use a suitable shared library mechanism for linking with the
+ Library. A suitable mechanism is one that (a) uses at run time
+ a copy of the Library already present on the user's computer
+ system, and (b) will operate properly with a modified version
+ of the Library that is interface-compatible with the Linked
+ Version.
+
+ e) Provide Installation Information, but only if you would otherwise
+ be required to provide such information under section 6 of the
+ GNU GPL, and only to the extent that such information is
+ necessary to install and execute a modified version of the
+ Combined Work produced by recombining or relinking the
+ Application with a modified version of the Linked Version. (If
+ you use option 4d0, the Installation Information must accompany
+ the Minimal Corresponding Source and Corresponding Application
+ Code. If you use option 4d1, you must provide the Installation
+ Information in the manner specified by section 6 of the GNU GPL
+ for conveying Corresponding Source.)
+
+ 5. Combined Libraries.
+
+ You may place library facilities that are a work based on the
+Library side by side in a single library together with other library
+facilities that are not Applications and are not covered by this
+License, and convey such a combined library under terms of your
+choice, if you do both of the following:
+
+ a) Accompany the combined library with a copy of the same work based
+ on the Library, uncombined with any other library facilities,
+ conveyed under the terms of this License.
+
+ b) Give prominent notice with the combined library that part of it
+ is a work based on the Library, and explaining where to find the
+ accompanying uncombined form of the same work.
+
+ 6. Revised Versions of the GNU Lesser General Public License.
+
+ The Free Software Foundation may publish revised and/or new versions
+of the GNU Lesser General Public License from time to time. Such new
+versions will be similar in spirit to the present version, but may
+differ in detail to address new problems or concerns.
+
+ Each version is given a distinguishing version number. If the
+Library as you received it specifies that a certain numbered version
+of the GNU Lesser General Public License "or any later version"
+applies to it, you have the option of following the terms and
+conditions either of that published version or of any later version
+published by the Free Software Foundation. If the Library as you
+received it does not specify a version number of the GNU Lesser
+General Public License, you may choose any version of the GNU Lesser
+General Public License ever published by the Free Software Foundation.
+
+ If the Library as you received it specifies that a proxy can decide
+whether future versions of the GNU Lesser General Public License shall
+apply, that proxy's public statement of acceptance of any version is
+permanent authorization for you to choose that version for the
+Library.
diff --git a/vendor/github.com/juju/loggo/Makefile b/vendor/github.com/juju/loggo/Makefile
new file mode 100644
index 000000000..8d379b36e
--- /dev/null
+++ b/vendor/github.com/juju/loggo/Makefile
@@ -0,0 +1,11 @@
+default: check
+
+check:
+ go test && go test -compiler gccgo
+
+docs:
+ godoc2md github.com/juju/loggo > README.md
+ sed -i 's|\[godoc-link-here\]|[![GoDoc](https://godoc.org/github.com/juju/loggo?status.svg)](https://godoc.org/github.com/juju/loggo)|' README.md
+
+
+.PHONY: default check docs
diff --git a/vendor/github.com/juju/loggo/README.md b/vendor/github.com/juju/loggo/README.md
new file mode 100644
index 000000000..b8db6556d
--- /dev/null
+++ b/vendor/github.com/juju/loggo/README.md
@@ -0,0 +1,548 @@
+
+# loggo
+ import "github.com/juju/loggo"
+
+[![GoDoc](https://godoc.org/github.com/juju/loggo?status.svg)](https://godoc.org/github.com/juju/loggo)
+
+### Module level logging for Go
+This package provides an alternative to the standard library log package.
+
+The actual logging functions never return errors. If you are logging
+something, you really don't want to be worried about the logging
+having trouble.
+
+Modules have names that are defined by dotted strings.
+
+
+ "first.second.third"
+
+There is a root module that has the name `""`. Each module
+(except the root module) has a parent, identified by the part of
+the name without the last dotted value.
+* the parent of "first.second.third" is "first.second"
+* the parent of "first.second" is "first"
+* the parent of "first" is "" (the root module)
+
+Each module can specify its own severity level. Logging calls that are of
+a lower severity than the module's effective severity level are not written
+out.
+
+Loggers are created using the GetLogger function.
+
+
+ logger := loggo.GetLogger("foo.bar")
+
+By default there is one writer registered, which will write to Stderr,
+and the root module, which will only emit warnings and above.
+If you want to continue using the default
+logger, but have it emit all logging levels you need to do the following.
+
+
+ writer, _, err := loggo.RemoveWriter("default")
+ // err is non-nil if and only if the name isn't found.
+ loggo.RegisterWriter("default", writer, loggo.TRACE)
+
+
+
+
+
+
+## func ConfigureLoggers
+``` go
+func ConfigureLoggers(specification string) error
+```
+ConfigureLoggers configures loggers according to the given string
+specification, which specifies a set of modules and their associated
+logging levels. Loggers are colon- or semicolon-separated; each
+module is specified as =. White space outside of
+module names and levels is ignored. The root module is specified
+with the name "".
+
+An example specification:
+
+
+ `=ERROR; foo.bar=WARNING`
+
+
+## func LoggerInfo
+``` go
+func LoggerInfo() string
+```
+LoggerInfo returns information about the configured loggers and their logging
+levels. The information is returned in the format expected by
+ConfigureModules. Loggers with UNSPECIFIED level will not
+be included.
+
+
+## func ParseConfigurationString
+``` go
+func ParseConfigurationString(specification string) (map[string]Level, error)
+```
+ParseConfigurationString parses a logger configuration string into a map of
+logger names and their associated log level. This method is provided to
+allow other programs to pre-validate a configuration string rather than
+just calling ConfigureLoggers.
+
+Loggers are colon- or semicolon-separated; each module is specified as
+=. White space outside of module names and levels is
+ignored. The root module is specified with the name "".
+
+As a special case, a log level may be specified on its own.
+This is equivalent to specifying the level of the root module,
+so "DEBUG" is equivalent to `=DEBUG`
+
+An example specification:
+
+
+ `=ERROR; foo.bar=WARNING`
+
+
+## func RegisterWriter
+``` go
+func RegisterWriter(name string, writer Writer, minLevel Level) error
+```
+RegisterWriter adds the writer to the list of writers that get notified
+when logging. When registering, the caller specifies the minimum logging
+level that will be written, and a name for the writer. If there is already
+a registered writer with that name, an error is returned.
+
+
+## func ResetLoggers
+``` go
+func ResetLoggers()
+```
+ResetLogging iterates through the known modules and sets the levels of all
+to UNSPECIFIED, except for which is set to WARNING.
+
+
+## func ResetWriters
+``` go
+func ResetWriters()
+```
+ResetWriters puts the list of writers back into the initial state.
+
+
+## func WillWrite
+``` go
+func WillWrite(level Level) bool
+```
+WillWrite returns whether there are any writers registered
+at or above the given severity level. If it returns
+false, a log message at the given level will be discarded.
+
+
+
+## type DefaultFormatter
+``` go
+type DefaultFormatter struct{}
+```
+DefaultFormatter provides a simple concatenation of all the components.
+
+
+
+
+
+
+
+
+
+
+
+### func (\*DefaultFormatter) Format
+``` go
+func (*DefaultFormatter) Format(level Level, module, filename string, line int, timestamp time.Time, message string) string
+```
+Format returns the parameters separated by spaces except for filename and
+line which are separated by a colon. The timestamp is shown to second
+resolution in UTC.
+
+
+
+## type Formatter
+``` go
+type Formatter interface {
+ Format(level Level, module, filename string, line int, timestamp time.Time, message string) string
+}
+```
+Formatter defines the single method Format, which takes the logging
+information, and converts it to a string.
+
+
+
+
+
+
+
+
+
+
+
+## type Level
+``` go
+type Level uint32
+```
+Level holds a severity level.
+
+
+
+``` go
+const (
+ UNSPECIFIED Level = iota
+ TRACE
+ DEBUG
+ INFO
+ WARNING
+ ERROR
+ CRITICAL
+)
+```
+The severity levels. Higher values are more considered more
+important.
+
+
+
+
+
+
+
+### func ParseLevel
+``` go
+func ParseLevel(level string) (Level, bool)
+```
+ParseLevel converts a string representation of a logging level to a
+Level. It returns the level and whether it was valid or not.
+
+
+
+
+### func (Level) String
+``` go
+func (level Level) String() string
+```
+
+
+## type Logger
+``` go
+type Logger struct {
+ // contains filtered or unexported fields
+}
+```
+A Logger represents a logging module. It has an associated logging
+level which can be changed; messages of lesser severity will
+be dropped. Loggers have a hierarchical relationship - see
+the package documentation.
+
+The zero Logger value is usable - any messages logged
+to it will be sent to the root Logger.
+
+
+
+
+
+
+
+
+
+### func GetLogger
+``` go
+func GetLogger(name string) Logger
+```
+GetLogger returns a Logger for the given module name,
+creating it and its parents if necessary.
+
+
+
+
+### func (Logger) Criticalf
+``` go
+func (logger Logger) Criticalf(message string, args ...interface{})
+```
+Criticalf logs the printf-formatted message at critical level.
+
+
+
+### func (Logger) Debugf
+``` go
+func (logger Logger) Debugf(message string, args ...interface{})
+```
+Debugf logs the printf-formatted message at debug level.
+
+
+
+### func (Logger) EffectiveLogLevel
+``` go
+func (logger Logger) EffectiveLogLevel() Level
+```
+EffectiveLogLevel returns the effective log level of
+the receiver - that is, messages with a lesser severity
+level will be discarded.
+
+If the log level of the receiver is unspecified,
+it will be taken from the effective log level of its
+parent.
+
+
+
+### func (Logger) Errorf
+``` go
+func (logger Logger) Errorf(message string, args ...interface{})
+```
+Errorf logs the printf-formatted message at error level.
+
+
+
+### func (Logger) Infof
+``` go
+func (logger Logger) Infof(message string, args ...interface{})
+```
+Infof logs the printf-formatted message at info level.
+
+
+
+### func (Logger) IsDebugEnabled
+``` go
+func (logger Logger) IsDebugEnabled() bool
+```
+IsDebugEnabled returns whether debugging is enabled
+at debug level.
+
+
+
+### func (Logger) IsErrorEnabled
+``` go
+func (logger Logger) IsErrorEnabled() bool
+```
+IsErrorEnabled returns whether debugging is enabled
+at error level.
+
+
+
+### func (Logger) IsInfoEnabled
+``` go
+func (logger Logger) IsInfoEnabled() bool
+```
+IsInfoEnabled returns whether debugging is enabled
+at info level.
+
+
+
+### func (Logger) IsLevelEnabled
+``` go
+func (logger Logger) IsLevelEnabled(level Level) bool
+```
+IsLevelEnabled returns whether debugging is enabled
+for the given log level.
+
+
+
+### func (Logger) IsTraceEnabled
+``` go
+func (logger Logger) IsTraceEnabled() bool
+```
+IsTraceEnabled returns whether debugging is enabled
+at trace level.
+
+
+
+### func (Logger) IsWarningEnabled
+``` go
+func (logger Logger) IsWarningEnabled() bool
+```
+IsWarningEnabled returns whether debugging is enabled
+at warning level.
+
+
+
+### func (Logger) LogCallf
+``` go
+func (logger Logger) LogCallf(calldepth int, level Level, message string, args ...interface{})
+```
+LogCallf logs a printf-formatted message at the given level.
+The location of the call is indicated by the calldepth argument.
+A calldepth of 1 means the function that called this function.
+A message will be discarded if level is less than the
+the effective log level of the logger.
+Note that the writers may also filter out messages that
+are less than their registered minimum severity level.
+
+
+
+### func (Logger) LogLevel
+``` go
+func (logger Logger) LogLevel() Level
+```
+LogLevel returns the configured log level of the logger.
+
+
+
+### func (Logger) Logf
+``` go
+func (logger Logger) Logf(level Level, message string, args ...interface{})
+```
+Logf logs a printf-formatted message at the given level.
+A message will be discarded if level is less than the
+the effective log level of the logger.
+Note that the writers may also filter out messages that
+are less than their registered minimum severity level.
+
+
+
+### func (Logger) Name
+``` go
+func (logger Logger) Name() string
+```
+Name returns the logger's module name.
+
+
+
+### func (Logger) SetLogLevel
+``` go
+func (logger Logger) SetLogLevel(level Level)
+```
+SetLogLevel sets the severity level of the given logger.
+The root logger cannot be set to UNSPECIFIED level.
+See EffectiveLogLevel for how this affects the
+actual messages logged.
+
+
+
+### func (Logger) Tracef
+``` go
+func (logger Logger) Tracef(message string, args ...interface{})
+```
+Tracef logs the printf-formatted message at trace level.
+
+
+
+### func (Logger) Warningf
+``` go
+func (logger Logger) Warningf(message string, args ...interface{})
+```
+Warningf logs the printf-formatted message at warning level.
+
+
+
+## type TestLogValues
+``` go
+type TestLogValues struct {
+ Level Level
+ Module string
+ Filename string
+ Line int
+ Timestamp time.Time
+ Message string
+}
+```
+TestLogValues represents a single logging call.
+
+
+
+
+
+
+
+
+
+
+
+## type TestWriter
+``` go
+type TestWriter struct {
+ // contains filtered or unexported fields
+}
+```
+TestWriter is a useful Writer for testing purposes. Each component of the
+logging message is stored in the Log array.
+
+
+
+
+
+
+
+
+
+
+
+### func (\*TestWriter) Clear
+``` go
+func (writer *TestWriter) Clear()
+```
+Clear removes any saved log messages.
+
+
+
+### func (\*TestWriter) Log
+``` go
+func (writer *TestWriter) Log() []TestLogValues
+```
+Log returns a copy of the current logged values.
+
+
+
+### func (\*TestWriter) Write
+``` go
+func (writer *TestWriter) Write(level Level, module, filename string, line int, timestamp time.Time, message string)
+```
+Write saves the params as members in the TestLogValues struct appended to the Log array.
+
+
+
+## type Writer
+``` go
+type Writer interface {
+ // Write writes a message to the Writer with the given
+ // level and module name. The filename and line hold
+ // the file name and line number of the code that is
+ // generating the log message; the time stamp holds
+ // the time the log message was generated, and
+ // message holds the log message itself.
+ Write(level Level, name, filename string, line int, timestamp time.Time, message string)
+}
+```
+Writer is implemented by any recipient of log messages.
+
+
+
+
+
+
+
+
+
+### func NewSimpleWriter
+``` go
+func NewSimpleWriter(writer io.Writer, formatter Formatter) Writer
+```
+NewSimpleWriter returns a new writer that writes
+log messages to the given io.Writer formatting the
+messages with the given formatter.
+
+
+### func RemoveWriter
+``` go
+func RemoveWriter(name string) (Writer, Level, error)
+```
+RemoveWriter removes the Writer identified by 'name' and returns it.
+If the Writer is not found, an error is returned.
+
+
+### func ReplaceDefaultWriter
+``` go
+func ReplaceDefaultWriter(writer Writer) (Writer, error)
+```
+ReplaceDefaultWriter is a convenience method that does the equivalent of
+RemoveWriter and then RegisterWriter with the name "default". The previous
+default writer, if any is returned.
+
+
+
+
+
+
+
+
+
+
+- - -
+Generated by [godoc2md](http://godoc.org/github.com/davecheney/godoc2md)
diff --git a/vendor/github.com/juju/loggo/doc.go b/vendor/github.com/juju/loggo/doc.go
new file mode 100644
index 000000000..bb19cd574
--- /dev/null
+++ b/vendor/github.com/juju/loggo/doc.go
@@ -0,0 +1,42 @@
+// Copyright 2014 Canonical Ltd.
+// Licensed under the LGPLv3, see LICENCE file for details.
+
+/*
+[godoc-link-here]
+
+Module level logging for Go
+
+This package provides an alternative to the standard library log package.
+
+The actual logging functions never return errors. If you are logging
+something, you really don't want to be worried about the logging
+having trouble.
+
+Modules have names that are defined by dotted strings.
+ "first.second.third"
+
+There is a root module that has the name `""`. Each module
+(except the root module) has a parent, identified by the part of
+the name without the last dotted value.
+* the parent of "first.second.third" is "first.second"
+* the parent of "first.second" is "first"
+* the parent of "first" is "" (the root module)
+
+Each module can specify its own severity level. Logging calls that are of
+a lower severity than the module's effective severity level are not written
+out.
+
+Loggers are created using the GetLogger function.
+ logger := loggo.GetLogger("foo.bar")
+
+By default there is one writer registered, which will write to Stderr,
+and the root module, which will only emit warnings and above.
+If you want to continue using the default
+logger, but have it emit all logging levels you need to do the following.
+
+ writer, _, err := loggo.RemoveWriter("default")
+ // err is non-nil if and only if the name isn't found.
+ loggo.RegisterWriter("default", writer, loggo.TRACE)
+
+*/
+package loggo
diff --git a/vendor/github.com/juju/loggo/example/first.go b/vendor/github.com/juju/loggo/example/first.go
new file mode 100644
index 000000000..558f906d3
--- /dev/null
+++ b/vendor/github.com/juju/loggo/example/first.go
@@ -0,0 +1,27 @@
+package main
+
+import (
+ "github.com/juju/loggo"
+)
+
+var first = loggo.GetLogger("first")
+
+func FirstCritical(message string) {
+ first.Criticalf(message)
+}
+
+func FirstError(message string) {
+ first.Errorf(message)
+}
+
+func FirstWarning(message string) {
+ first.Warningf(message)
+}
+
+func FirstInfo(message string) {
+ first.Infof(message)
+}
+
+func FirstTrace(message string) {
+ first.Tracef(message)
+}
diff --git a/vendor/github.com/juju/loggo/example/main.go b/vendor/github.com/juju/loggo/example/main.go
new file mode 100644
index 000000000..29f361f39
--- /dev/null
+++ b/vendor/github.com/juju/loggo/example/main.go
@@ -0,0 +1,39 @@
+package main
+
+import (
+ "fmt"
+ "os"
+
+ "github.com/juju/loggo"
+)
+
+var logger = loggo.GetLogger("main")
+var rootLogger = loggo.GetLogger("")
+
+func main() {
+ args := os.Args
+ if len(args) > 1 {
+ loggo.ConfigureLoggers(args[1])
+ } else {
+ fmt.Println("Add a parameter to configure the logging:")
+ fmt.Println("E.g. \"=INFO;first=TRACE\"")
+ }
+ fmt.Println("\nCurrent logging levels:")
+ fmt.Println(loggo.LoggerInfo())
+ fmt.Println("")
+
+ rootLogger.Infof("Start of test.")
+
+ FirstCritical("first critical")
+ FirstError("first error")
+ FirstWarning("first warning")
+ FirstInfo("first info")
+ FirstTrace("first trace")
+
+ SecondCritical("first critical")
+ SecondError("first error")
+ SecondWarning("first warning")
+ SecondInfo("first info")
+ SecondTrace("first trace")
+
+}
diff --git a/vendor/github.com/juju/loggo/example/second.go b/vendor/github.com/juju/loggo/example/second.go
new file mode 100644
index 000000000..4a198adfe
--- /dev/null
+++ b/vendor/github.com/juju/loggo/example/second.go
@@ -0,0 +1,27 @@
+package main
+
+import (
+ "github.com/juju/loggo"
+)
+
+var second = loggo.GetLogger("second")
+
+func SecondCritical(message string) {
+ second.Criticalf(message)
+}
+
+func SecondError(message string) {
+ second.Errorf(message)
+}
+
+func SecondWarning(message string) {
+ second.Warningf(message)
+}
+
+func SecondInfo(message string) {
+ second.Infof(message)
+}
+
+func SecondTrace(message string) {
+ second.Tracef(message)
+}
diff --git a/vendor/github.com/juju/loggo/formatter.go b/vendor/github.com/juju/loggo/formatter.go
new file mode 100644
index 000000000..7fdc4efc1
--- /dev/null
+++ b/vendor/github.com/juju/loggo/formatter.go
@@ -0,0 +1,29 @@
+// Copyright 2014 Canonical Ltd.
+// Licensed under the LGPLv3, see LICENCE file for details.
+
+package loggo
+
+import (
+ "fmt"
+ "path/filepath"
+ "time"
+)
+
+// Formatter defines the single method Format, which takes the logging
+// information, and converts it to a string.
+type Formatter interface {
+ Format(level Level, module, filename string, line int, timestamp time.Time, message string) string
+}
+
+// DefaultFormatter provides a simple concatenation of all the components.
+type DefaultFormatter struct{}
+
+// Format returns the parameters separated by spaces except for filename and
+// line which are separated by a colon. The timestamp is shown to second
+// resolution in UTC.
+func (*DefaultFormatter) Format(level Level, module, filename string, line int, timestamp time.Time, message string) string {
+ ts := timestamp.In(time.UTC).Format("2006-01-02 15:04:05")
+ // Just get the basename from the filename
+ filename = filepath.Base(filename)
+ return fmt.Sprintf("%s %s %s %s:%d %s", ts, level, module, filename, line, message)
+}
diff --git a/vendor/github.com/juju/loggo/logger.go b/vendor/github.com/juju/loggo/logger.go
new file mode 100644
index 000000000..b5d83d466
--- /dev/null
+++ b/vendor/github.com/juju/loggo/logger.go
@@ -0,0 +1,417 @@
+// Copyright 2014 Canonical Ltd.
+// Licensed under the LGPLv3, see LICENCE file for details.
+
+package loggo
+
+import (
+ "fmt"
+ "runtime"
+ "sort"
+ "strings"
+ "sync"
+ "sync/atomic"
+ "time"
+)
+
+// Level holds a severity level.
+type Level uint32
+
+// The severity levels. Higher values are more considered more
+// important.
+const (
+ UNSPECIFIED Level = iota
+ TRACE
+ DEBUG
+ INFO
+ WARNING
+ ERROR
+ CRITICAL
+)
+
+// A Logger represents a logging module. It has an associated logging
+// level which can be changed; messages of lesser severity will
+// be dropped. Loggers have a hierarchical relationship - see
+// the package documentation.
+//
+// The zero Logger value is usable - any messages logged
+// to it will be sent to the root Logger.
+type Logger struct {
+ impl *module
+}
+
+type module struct {
+ name string
+ level Level
+ parent *module
+}
+
+// Initially the modules map only contains the root module.
+var (
+ root = &module{level: WARNING}
+ modulesMutex sync.Mutex
+ modules = map[string]*module{
+ "": root,
+ }
+)
+
+func (level Level) String() string {
+ switch level {
+ case UNSPECIFIED:
+ return "UNSPECIFIED"
+ case TRACE:
+ return "TRACE"
+ case DEBUG:
+ return "DEBUG"
+ case INFO:
+ return "INFO"
+ case WARNING:
+ return "WARNING"
+ case ERROR:
+ return "ERROR"
+ case CRITICAL:
+ return "CRITICAL"
+ }
+ return ""
+}
+
+// get atomically gets the value of the given level.
+func (level *Level) get() Level {
+ return Level(atomic.LoadUint32((*uint32)(level)))
+}
+
+// set atomically sets the value of the receiver
+// to the given level.
+func (level *Level) set(newLevel Level) {
+ atomic.StoreUint32((*uint32)(level), uint32(newLevel))
+}
+
+// getLoggerInternal assumes that the modulesMutex is locked.
+func getLoggerInternal(name string) Logger {
+ impl, found := modules[name]
+ if found {
+ return Logger{impl}
+ }
+ parentName := ""
+ if i := strings.LastIndex(name, "."); i >= 0 {
+ parentName = name[0:i]
+ }
+ parent := getLoggerInternal(parentName).impl
+ impl = &module{name, UNSPECIFIED, parent}
+ modules[name] = impl
+ return Logger{impl}
+}
+
+// GetLogger returns a Logger for the given module name,
+// creating it and its parents if necessary.
+func GetLogger(name string) Logger {
+ // Lowercase the module name, and look for it in the modules map.
+ name = strings.ToLower(name)
+ modulesMutex.Lock()
+ defer modulesMutex.Unlock()
+ return getLoggerInternal(name)
+}
+
+// LoggerInfo returns information about the configured loggers and their logging
+// levels. The information is returned in the format expected by
+// ConfigureModules. Loggers with UNSPECIFIED level will not
+// be included.
+func LoggerInfo() string {
+ output := []string{}
+ // output in alphabetical order.
+ keys := []string{}
+ modulesMutex.Lock()
+ defer modulesMutex.Unlock()
+ for key := range modules {
+ keys = append(keys, key)
+ }
+ sort.Strings(keys)
+ for _, name := range keys {
+ mod := modules[name]
+ severity := mod.level.get()
+ if severity == UNSPECIFIED {
+ continue
+ }
+ output = append(output, fmt.Sprintf("%s=%s", mod.Name(), severity))
+ }
+ return strings.Join(output, ";")
+}
+
+// ParseConfigurationString parses a logger configuration string into a map of
+// logger names and their associated log level. This method is provided to
+// allow other programs to pre-validate a configuration string rather than
+// just calling ConfigureLoggers.
+//
+// Loggers are colon- or semicolon-separated; each module is specified as
+// =. White space outside of module names and levels is
+// ignored. The root module is specified with the name "".
+//
+// As a special case, a log level may be specified on its own.
+// This is equivalent to specifying the level of the root module,
+// so "DEBUG" is equivalent to `=DEBUG`
+//
+// An example specification:
+// `=ERROR; foo.bar=WARNING`
+func ParseConfigurationString(specification string) (map[string]Level, error) {
+ levels := make(map[string]Level)
+ if level, ok := ParseLevel(specification); ok {
+ levels[""] = level
+ return levels, nil
+ }
+ values := strings.FieldsFunc(specification, func(r rune) bool { return r == ';' || r == ':' })
+ for _, value := range values {
+ s := strings.SplitN(value, "=", 2)
+ if len(s) < 2 {
+ return nil, fmt.Errorf("logger specification expected '=', found %q", value)
+ }
+ name := strings.TrimSpace(s[0])
+ levelStr := strings.TrimSpace(s[1])
+ if name == "" || levelStr == "" {
+ return nil, fmt.Errorf("logger specification %q has blank name or level", value)
+ }
+ if name == "" {
+ name = ""
+ }
+ level, ok := ParseLevel(levelStr)
+ if !ok {
+ return nil, fmt.Errorf("unknown severity level %q", levelStr)
+ }
+ levels[name] = level
+ }
+ return levels, nil
+}
+
+// ConfigureLoggers configures loggers according to the given string
+// specification, which specifies a set of modules and their associated
+// logging levels. Loggers are colon- or semicolon-separated; each
+// module is specified as =. White space outside of
+// module names and levels is ignored. The root module is specified
+// with the name "".
+//
+// An example specification:
+// `=ERROR; foo.bar=WARNING`
+func ConfigureLoggers(specification string) error {
+ if specification == "" {
+ return nil
+ }
+ levels, err := ParseConfigurationString(specification)
+ if err != nil {
+ return err
+ }
+ for name, level := range levels {
+ GetLogger(name).SetLogLevel(level)
+ }
+ return nil
+}
+
+// ResetLogging iterates through the known modules and sets the levels of all
+// to UNSPECIFIED, except for which is set to WARNING.
+func ResetLoggers() {
+ modulesMutex.Lock()
+ defer modulesMutex.Unlock()
+ for name, module := range modules {
+ if name == "" {
+ module.level.set(WARNING)
+ } else {
+ module.level.set(UNSPECIFIED)
+ }
+ }
+}
+
+// ParseLevel converts a string representation of a logging level to a
+// Level. It returns the level and whether it was valid or not.
+func ParseLevel(level string) (Level, bool) {
+ level = strings.ToUpper(level)
+ switch level {
+ case "UNSPECIFIED":
+ return UNSPECIFIED, true
+ case "TRACE":
+ return TRACE, true
+ case "DEBUG":
+ return DEBUG, true
+ case "INFO":
+ return INFO, true
+ case "WARN", "WARNING":
+ return WARNING, true
+ case "ERROR":
+ return ERROR, true
+ case "CRITICAL":
+ return CRITICAL, true
+ }
+ return UNSPECIFIED, false
+}
+
+func (logger Logger) getModule() *module {
+ if logger.impl == nil {
+ return root
+ }
+ return logger.impl
+}
+
+// Name returns the logger's module name.
+func (logger Logger) Name() string {
+ return logger.getModule().Name()
+}
+
+// LogLevel returns the configured log level of the logger.
+func (logger Logger) LogLevel() Level {
+ return logger.getModule().level.get()
+}
+
+func (module *module) getEffectiveLogLevel() Level {
+ // Note: the root module is guaranteed to have a
+ // specified logging level, so acts as a suitable sentinel
+ // for this loop.
+ for {
+ if level := module.level.get(); level != UNSPECIFIED {
+ return level
+ }
+ module = module.parent
+ }
+ panic("unreachable")
+}
+
+func (module *module) Name() string {
+ if module.name == "" {
+ return ""
+ }
+ return module.name
+}
+
+// EffectiveLogLevel returns the effective log level of
+// the receiver - that is, messages with a lesser severity
+// level will be discarded.
+//
+// If the log level of the receiver is unspecified,
+// it will be taken from the effective log level of its
+// parent.
+func (logger Logger) EffectiveLogLevel() Level {
+ return logger.getModule().getEffectiveLogLevel()
+}
+
+// SetLogLevel sets the severity level of the given logger.
+// The root logger cannot be set to UNSPECIFIED level.
+// See EffectiveLogLevel for how this affects the
+// actual messages logged.
+func (logger Logger) SetLogLevel(level Level) {
+ module := logger.getModule()
+ // The root module can't be unspecified.
+ if module.name == "" && level == UNSPECIFIED {
+ level = WARNING
+ }
+ module.level.set(level)
+}
+
+// Logf logs a printf-formatted message at the given level.
+// A message will be discarded if level is less than the
+// the effective log level of the logger.
+// Note that the writers may also filter out messages that
+// are less than their registered minimum severity level.
+func (logger Logger) Logf(level Level, message string, args ...interface{}) {
+ logger.LogCallf(2, level, message, args...)
+}
+
+// LogCallf logs a printf-formatted message at the given level.
+// The location of the call is indicated by the calldepth argument.
+// A calldepth of 1 means the function that called this function.
+// A message will be discarded if level is less than the
+// the effective log level of the logger.
+// Note that the writers may also filter out messages that
+// are less than their registered minimum severity level.
+func (logger Logger) LogCallf(calldepth int, level Level, message string, args ...interface{}) {
+ if logger.getModule().getEffectiveLogLevel() > level ||
+ !WillWrite(level) ||
+ level < TRACE ||
+ level > CRITICAL {
+ return
+ }
+ // Gather time, and filename, line number.
+ now := time.Now() // get this early.
+ // Param to Caller is the call depth. Since this method is called from
+ // the Logger methods, we want the place that those were called from.
+ _, file, line, ok := runtime.Caller(calldepth + 1)
+ if !ok {
+ file = "???"
+ line = 0
+ }
+ // Trim newline off format string, following usual
+ // Go logging conventions.
+ if len(message) > 0 && message[len(message)-1] == '\n' {
+ message = message[0 : len(message)-1]
+ }
+
+ // To avoid having a proliferation of Info/Infof methods,
+ // only use Sprintf if there are any args, and rely on the
+ // `go vet` tool for the obvious cases where someone has forgotten
+ // to provide an arg.
+ formattedMessage := message
+ if len(args) > 0 {
+ formattedMessage = fmt.Sprintf(message, args...)
+ }
+ writeToWriters(level, logger.impl.name, file, line, now, formattedMessage)
+}
+
+// Criticalf logs the printf-formatted message at critical level.
+func (logger Logger) Criticalf(message string, args ...interface{}) {
+ logger.Logf(CRITICAL, message, args...)
+}
+
+// Errorf logs the printf-formatted message at error level.
+func (logger Logger) Errorf(message string, args ...interface{}) {
+ logger.Logf(ERROR, message, args...)
+}
+
+// Warningf logs the printf-formatted message at warning level.
+func (logger Logger) Warningf(message string, args ...interface{}) {
+ logger.Logf(WARNING, message, args...)
+}
+
+// Infof logs the printf-formatted message at info level.
+func (logger Logger) Infof(message string, args ...interface{}) {
+ logger.Logf(INFO, message, args...)
+}
+
+// Debugf logs the printf-formatted message at debug level.
+func (logger Logger) Debugf(message string, args ...interface{}) {
+ logger.Logf(DEBUG, message, args...)
+}
+
+// Tracef logs the printf-formatted message at trace level.
+func (logger Logger) Tracef(message string, args ...interface{}) {
+ logger.Logf(TRACE, message, args...)
+}
+
+// IsLevelEnabled returns whether debugging is enabled
+// for the given log level.
+func (logger Logger) IsLevelEnabled(level Level) bool {
+ return logger.getModule().getEffectiveLogLevel() <= level
+}
+
+// IsErrorEnabled returns whether debugging is enabled
+// at error level.
+func (logger Logger) IsErrorEnabled() bool {
+ return logger.IsLevelEnabled(ERROR)
+}
+
+// IsWarningEnabled returns whether debugging is enabled
+// at warning level.
+func (logger Logger) IsWarningEnabled() bool {
+ return logger.IsLevelEnabled(WARNING)
+}
+
+// IsInfoEnabled returns whether debugging is enabled
+// at info level.
+func (logger Logger) IsInfoEnabled() bool {
+ return logger.IsLevelEnabled(INFO)
+}
+
+// IsDebugEnabled returns whether debugging is enabled
+// at debug level.
+func (logger Logger) IsDebugEnabled() bool {
+ return logger.IsLevelEnabled(DEBUG)
+}
+
+// IsTraceEnabled returns whether debugging is enabled
+// at trace level.
+func (logger Logger) IsTraceEnabled() bool {
+ return logger.IsLevelEnabled(TRACE)
+}
diff --git a/vendor/github.com/juju/loggo/testwriter.go b/vendor/github.com/juju/loggo/testwriter.go
new file mode 100644
index 000000000..814a1ec97
--- /dev/null
+++ b/vendor/github.com/juju/loggo/testwriter.go
@@ -0,0 +1,51 @@
+// Copyright 2014 Canonical Ltd.
+// Licensed under the LGPLv3, see LICENCE file for details.
+
+package loggo
+
+import (
+ "path"
+ "sync"
+ "time"
+)
+
+// TestLogValues represents a single logging call.
+type TestLogValues struct {
+ Level Level
+ Module string
+ Filename string
+ Line int
+ Timestamp time.Time
+ Message string
+}
+
+// TestWriter is a useful Writer for testing purposes. Each component of the
+// logging message is stored in the Log array.
+type TestWriter struct {
+ mu sync.Mutex
+ log []TestLogValues
+}
+
+// Write saves the params as members in the TestLogValues struct appended to the Log array.
+func (writer *TestWriter) Write(level Level, module, filename string, line int, timestamp time.Time, message string) {
+ writer.mu.Lock()
+ defer writer.mu.Unlock()
+ writer.log = append(writer.log,
+ TestLogValues{level, module, path.Base(filename), line, timestamp, message})
+}
+
+// Clear removes any saved log messages.
+func (writer *TestWriter) Clear() {
+ writer.mu.Lock()
+ defer writer.mu.Unlock()
+ writer.log = nil
+}
+
+// Log returns a copy of the current logged values.
+func (writer *TestWriter) Log() []TestLogValues {
+ writer.mu.Lock()
+ defer writer.mu.Unlock()
+ v := make([]TestLogValues, len(writer.log))
+ copy(v, writer.log)
+ return v
+}
diff --git a/vendor/github.com/juju/loggo/writer.go b/vendor/github.com/juju/loggo/writer.go
new file mode 100644
index 000000000..09f88fab0
--- /dev/null
+++ b/vendor/github.com/juju/loggo/writer.go
@@ -0,0 +1,152 @@
+// Copyright 2014 Canonical Ltd.
+// Licensed under the LGPLv3, see LICENCE file for details.
+
+package loggo
+
+import (
+ "fmt"
+ "io"
+ "os"
+ "sync"
+ "time"
+)
+
+// Writer is implemented by any recipient of log messages.
+type Writer interface {
+ // Write writes a message to the Writer with the given
+ // level and module name. The filename and line hold
+ // the file name and line number of the code that is
+ // generating the log message; the time stamp holds
+ // the time the log message was generated, and
+ // message holds the log message itself.
+ Write(level Level, name, filename string, line int, timestamp time.Time, message string)
+}
+
+type registeredWriter struct {
+ writer Writer
+ level Level
+}
+
+// defaultName is the name of a writer that is registered
+// by default that writes to stderr.
+const defaultName = "default"
+
+var (
+ writerMutex sync.Mutex
+ writers = map[string]*registeredWriter{
+ defaultName: ®isteredWriter{
+ writer: NewSimpleWriter(os.Stderr, &DefaultFormatter{}),
+ level: TRACE,
+ },
+ }
+ globalMinLevel = TRACE
+)
+
+// ResetWriters puts the list of writers back into the initial state.
+func ResetWriters() {
+ writerMutex.Lock()
+ defer writerMutex.Unlock()
+ writers = map[string]*registeredWriter{
+ "default": ®isteredWriter{
+ writer: NewSimpleWriter(os.Stderr, &DefaultFormatter{}),
+ level: TRACE,
+ },
+ }
+ findMinLevel()
+}
+
+// ReplaceDefaultWriter is a convenience method that does the equivalent of
+// RemoveWriter and then RegisterWriter with the name "default". The previous
+// default writer, if any is returned.
+func ReplaceDefaultWriter(writer Writer) (Writer, error) {
+ if writer == nil {
+ return nil, fmt.Errorf("Writer cannot be nil")
+ }
+ writerMutex.Lock()
+ defer writerMutex.Unlock()
+ reg, found := writers[defaultName]
+ if !found {
+ return nil, fmt.Errorf("there is no %q writer", defaultName)
+ }
+ oldWriter := reg.writer
+ reg.writer = writer
+ return oldWriter, nil
+
+}
+
+// RegisterWriter adds the writer to the list of writers that get notified
+// when logging. When registering, the caller specifies the minimum logging
+// level that will be written, and a name for the writer. If there is already
+// a registered writer with that name, an error is returned.
+func RegisterWriter(name string, writer Writer, minLevel Level) error {
+ if writer == nil {
+ return fmt.Errorf("Writer cannot be nil")
+ }
+ writerMutex.Lock()
+ defer writerMutex.Unlock()
+ if _, found := writers[name]; found {
+ return fmt.Errorf("there is already a Writer registered with the name %q", name)
+ }
+ writers[name] = ®isteredWriter{writer: writer, level: minLevel}
+ findMinLevel()
+ return nil
+}
+
+// RemoveWriter removes the Writer identified by 'name' and returns it.
+// If the Writer is not found, an error is returned.
+func RemoveWriter(name string) (Writer, Level, error) {
+ writerMutex.Lock()
+ defer writerMutex.Unlock()
+ registered, found := writers[name]
+ if !found {
+ return nil, UNSPECIFIED, fmt.Errorf("Writer %q is not registered", name)
+ }
+ delete(writers, name)
+ findMinLevel()
+ return registered.writer, registered.level, nil
+}
+
+func findMinLevel() {
+ // We assume the lock is already held
+ minLevel := CRITICAL
+ for _, registered := range writers {
+ if registered.level < minLevel {
+ minLevel = registered.level
+ }
+ }
+ globalMinLevel.set(minLevel)
+}
+
+// WillWrite returns whether there are any writers registered
+// at or above the given severity level. If it returns
+// false, a log message at the given level will be discarded.
+func WillWrite(level Level) bool {
+ return level >= globalMinLevel.get()
+}
+
+func writeToWriters(level Level, module, filename string, line int, timestamp time.Time, message string) {
+ writerMutex.Lock()
+ defer writerMutex.Unlock()
+ for _, registered := range writers {
+ if level >= registered.level {
+ registered.writer.Write(level, module, filename, line, timestamp, message)
+ }
+ }
+}
+
+type simpleWriter struct {
+ writer io.Writer
+ formatter Formatter
+}
+
+// NewSimpleWriter returns a new writer that writes
+// log messages to the given io.Writer formatting the
+// messages with the given formatter.
+func NewSimpleWriter(writer io.Writer, formatter Formatter) Writer {
+ return &simpleWriter{writer, formatter}
+}
+
+func (simple *simpleWriter) Write(level Level, module, filename string, line int, timestamp time.Time, message string) {
+ logLine := simple.formatter.Format(level, module, filename, line, timestamp, message)
+ fmt.Fprintln(simple.writer, logLine)
+}
diff --git a/website/source/assets/stylesheets/_docs.scss b/website/source/assets/stylesheets/_docs.scss
index 83c8f2314..1b27d41c3 100755
--- a/website/source/assets/stylesheets/_docs.scss
+++ b/website/source/assets/stylesheets/_docs.scss
@@ -33,6 +33,7 @@ body.layout-rundeck,
body.layout-statuscake,
body.layout-template,
body.layout-tls,
+body.layout-triton,
body.layout-vcd,
body.layout-vsphere,
body.layout-docs,
diff --git a/website/source/docs/providers/triton/index.html.markdown b/website/source/docs/providers/triton/index.html.markdown
new file mode 100644
index 000000000..d24fe50bf
--- /dev/null
+++ b/website/source/docs/providers/triton/index.html.markdown
@@ -0,0 +1,35 @@
+---
+layout: "triton"
+page_title: "Provider: Triton"
+sidebar_current: "docs-triton-index"
+description: |-
+ Used to provision infrastructure in Joyent's Triton public or on-premise clouds.
+---
+
+# Triton Provider
+
+The Triton provider is used to interact with resources in Joyent's Triton cloud. It is compatible with both public- and on-premise installations of Triton. The provider needs to be configured with the proper credentials before it can be used.
+
+Use the navigation to the left to read about the available resources.
+
+## Example Usage
+
+```
+provider "triton" {
+ account = "AccountName"
+ key_path = "~/.ssh/id_rsa"
+ key_id = "25:d4:a9:fe:ef:e6:c0:bf:b4:4b:4b:d4:a8:8f:01:0f"
+
+ # If using a private installation of Triton, specify the URL
+ url = "https://us-west-1.api.joyentcloud.com"
+}
+```
+
+## Argument Reference
+
+The following arguments are supported in the `provider` block:
+
+* `account` - (Required) This is the name of the Triton account. It can also be provided via the `SDC_ACCOUNT` environment variable.
+* `key_path` - (Required) This is the path to the private key of an SSH key associated with the Triton account to be used.
+* `key_id` - (Required) This is the fingerprint of the public key matching the key specified in `key_path`. It can be obtained via the command `ssh-keygen -l -E md5 -f /path/to/key`
+* `url` - (Optional) This is the URL to the Triton API endpoint. It is required if using a private installation of Triton. The default is to use the Joyent public cloud.
diff --git a/website/source/docs/providers/triton/r/triton_firewall_rule.html.markdown b/website/source/docs/providers/triton/r/triton_firewall_rule.html.markdown
new file mode 100644
index 000000000..1de023a63
--- /dev/null
+++ b/website/source/docs/providers/triton/r/triton_firewall_rule.html.markdown
@@ -0,0 +1,50 @@
+---
+layout: "triton"
+page_title: "Triton: triton_firewall_rule"
+sidebar_current: "docs-triton-firewall"
+description: |-
+ The `triton_firewall_rule` resource represents a rule for the Triton cloud firewall.
+---
+
+# triton\_firewall\_rule
+
+The `triton_firewall_rule` resource represents a rule for the Triton cloud firewall.
+
+## Example Usages
+
+Allow traffic on ports tcp/80 and tcp/443 to machines with the 'www' tag from any source
+
+
+```
+resource "triton_firewall_rule" "www" {
+ rule = "FROM any TO tag www ALLOW tcp (PORT 80 AND PORT 443)"
+ enabled = true
+}
+
+```
+Block traffic on port tcp/143 to all machines
+
+
+```
+resource "triton_firewall_rule" "imap" {
+ rule = "FROM any TO all vms BLOCK tcp port 143"
+ enabled = true
+}
+
+```
+
+## Argument Reference
+
+The following arguments are supported:
+
+* `rule` - (string, Required)
+ The firewall rule described using the Cloud API rule syntax defined at https://docs.joyent.com/public-cloud/network/firewall/cloud-firewall-rules-reference.
+
+* `enabled` - (boolean) Default: `false`
+ Whether the rule should be effective.
+
+## Attribute Reference
+
+The following attributes are exported:
+
+* `id` - (string) - The identifier representing the firewall rule in Triton.
diff --git a/website/source/docs/providers/triton/r/triton_key.html.markdown b/website/source/docs/providers/triton/r/triton_key.html.markdown
new file mode 100644
index 000000000..b1cc2e5fd
--- /dev/null
+++ b/website/source/docs/providers/triton/r/triton_key.html.markdown
@@ -0,0 +1,35 @@
+---
+layout: "triton"
+page_title: "Triton: triton_key"
+sidebar_current: "docs-triton-firewall"
+description: |-
+ The `triton_key` resource represents an SSH key for a Triton account.
+---
+
+# triton\_key
+
+The `triton_key` resource represents an SSH key for a Triton account.
+
+## Example Usages
+
+Create a key
+
+
+```
+resource "triton_key" "example" {
+ name = "Example Key"
+ key = "${file("keys/id_rsa")}"
+}
+
+```
+
+## Argument Reference
+
+The following arguments are supported:
+
+* `name` - (string, Change forces new resource)
+ The name of the key. If this is left empty, the name is inferred from the comment in the SSH key material.
+
+* `key` - (string, Required, Change forces new resource)
+ The SSH key material. In order to read this from a file, use the `file` interpolation.
+
diff --git a/website/source/docs/providers/triton/r/triton_machine.html.markdown b/website/source/docs/providers/triton/r/triton_machine.html.markdown
new file mode 100644
index 000000000..6164250d6
--- /dev/null
+++ b/website/source/docs/providers/triton/r/triton_machine.html.markdown
@@ -0,0 +1,78 @@
+---
+layout: "triton"
+page_title: "Triton: triton_machine"
+sidebar_current: "docs-triton-firewall"
+description: |-
+ The `triton_machine` resource represents a virtual machine or infrastructure container running in Triton.
+---
+
+# triton\_machine
+
+The `triton_machine` resource represents a virtual machine or infrastructure container running in Triton.
+
+## Example Usages
+
+Run a SmartOS base-64 machine.
+
+
+```
+resource "triton_machine" "test" {
+ name = "example-machine"
+ package = "g3-standard-0.25-smartos"
+ image = "842e6fa6-6e9b-11e5-8402-1b490459e334"
+
+ tags = {
+ hello = "world"
+ }
+}
+
+```
+
+## Argument Reference
+
+The following arguments are supported:
+
+* `name` - (string)
+ The friendly name for the machine. Triton will generate a name if one is not specified.
+
+* `tags` - (map)
+ A mapping of tags to apply to the machine.
+
+* `package` - (string, Required)
+ The name of the package to use for provisioning.
+
+* `image` - (string, Required)
+ The UUID of the image to provision.
+
+* `networks` - (list of string)
+ A list of the IDs of the desired networks for the machine.
+
+* `firewall_enabled` - (boolean) Default: `false`
+ Whether the cloud firewall should be enabled for this machine.
+
+* `root_authorized_keys` - (string)
+ The public keys authorized for root access via SSH to the machine.
+
+* `user_data` - (string)
+ Data to be copied to the machine on boot.
+
+* `user_script` - (string)
+ The user script to run on boot (every boot on SmartMachines).
+
+* `administrator_pw` - (string)
+ The initial password for the Administrator user. Only used for Windows virtual machines.
+
+## Attribute Reference
+
+The following attributes are exported:
+
+* `id` - (string) - The identifier representing the firewall rule in Triton.
+* `type` - (string) - The type of the machine (`smartmachine` or `virtualmachine`).
+* `state` - (string) - The current state of the machine.
+* `dataset` - (string) - The dataset URN with which the machine was provisioned.
+* `memory` - (int) - The amount of memory the machine has (in Mb).
+* `disk` - (int) - The amount of disk the machine has (in Gb).
+* `ips` - (list of strings) - IP addresses of the machine.
+* `primaryip` - (string) - The primary (public) IP address for the machine.
+* `created` - (string) - The time at which the machine was created.
+* `updated` - (string) - The time at which the machine was last updated.
diff --git a/website/source/layouts/docs.erb b/website/source/layouts/docs.erb
index aec52d682..f4d53ca04 100644
--- a/website/source/layouts/docs.erb
+++ b/website/source/layouts/docs.erb
@@ -225,9 +225,9 @@
Rundeck
- >
- StatusCake
-
+ >
+ StatusCake
+
>
Template
@@ -240,6 +240,10 @@
>
TLS
+
+ >
+ Triton
+
>
VMware vCloud Director
diff --git a/website/source/layouts/triton.erb b/website/source/layouts/triton.erb
new file mode 100644
index 000000000..716d174ef
--- /dev/null
+++ b/website/source/layouts/triton.erb
@@ -0,0 +1,39 @@
+<% wrap_layout :inner do %>
+ <% content_for :sidebar do %>
+
+ <% end %>
+
+ <%= yield %>
+<% end %>