Skip to content

Commit

Permalink
provider/triton: Move to joyent/triton-go (#13225)
Browse files Browse the repository at this point in the history
* provider/triton: Move to joyent/triton-go

This commit moves the Triton provider to the new joyent/triton-go
library from gosdc. This has a number of advantages - not least that
requests can be signed using an SSH agent without having to keep
unencrypted key material in memory.

Schema has been maintained for all resources, and several tests have
been added and acceptance tests repaired - in some cases by fixing bugs
in the underlying resources.

After applying this patch, all acceptance tests pass:

```
go generate $(go list ./... | grep -v /terraform/vendor/)
2017/03/30 13:48:33 Generated command/internal_plugin_list.go
TF_ACC=1 go test ./builtin/providers/triton -v  -timeout 120m
=== RUN   TestProvider
--- PASS: TestProvider (0.00s)
=== RUN   TestProvider_impl
--- PASS: TestProvider_impl (0.00s)
=== RUN   TestAccTritonFabric_basic
--- PASS: TestAccTritonFabric_basic (15.11s)
=== RUN   TestAccTritonFirewallRule_basic
--- PASS: TestAccTritonFirewallRule_basic (1.48s)
=== RUN   TestAccTritonFirewallRule_update
--- PASS: TestAccTritonFirewallRule_update (1.55s)
=== RUN   TestAccTritonFirewallRule_enable
--- PASS: TestAccTritonFirewallRule_enable (1.52s)
=== RUN   TestAccTritonKey_basic
--- PASS: TestAccTritonKey_basic (11.76s)
=== RUN   TestAccTritonKey_noKeyName
--- PASS: TestAccTritonKey_noKeyName (11.20s)
=== RUN   TestAccTritonMachine_basic
--- PASS: TestAccTritonMachine_basic (82.19s)
=== RUN   TestAccTritonMachine_dns
--- PASS: TestAccTritonMachine_dns (173.36s)
=== RUN   TestAccTritonMachine_nic
--- PASS: TestAccTritonMachine_nic (167.82s)
=== RUN   TestAccTritonMachine_addNIC
--- PASS: TestAccTritonMachine_addNIC (192.11s)
=== RUN   TestAccTritonMachine_firewall
--- PASS: TestAccTritonMachine_firewall (188.53s)
=== RUN   TestAccTritonMachine_metadata
--- PASS: TestAccTritonMachine_metadata (614.57s)
=== RUN   TestAccTritonVLAN_basic
--- PASS: TestAccTritonVLAN_basic (0.93s)
=== RUN   TestAccTritonVLAN_update
--- PASS: TestAccTritonVLAN_update (1.50s)
PASS
ok  	github.com/hashicorp/terraform/builtin/providers/triton	1463.621s
```

* provider/triton: Update docs for provider config

* deps: Vendor github.com/joyent/triton-go/...

* deps: Remove github.com/joyent/gosdc
  • Loading branch information
jen20 authored and stack72 committed Mar 30, 2017
1 parent 90b73d4 commit a0568e5
Show file tree
Hide file tree
Showing 55 changed files with 3,496 additions and 2,384 deletions.
125 changes: 85 additions & 40 deletions builtin/providers/triton/provider.go
Original file line number Diff line number Diff line change
@@ -1,41 +1,43 @@
package triton

import (
"fmt"
"log"
"os"
"crypto/md5"
"encoding/base64"
"errors"
"sort"
"time"

"github.com/hashicorp/errwrap"
"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"
"github.com/joyent/triton-go"
"github.com/joyent/triton-go/authentication"
)

// Provider returns a terraform.ResourceProvider.
func Provider() terraform.ResourceProvider {
return &schema.Provider{
Schema: map[string]*schema.Schema{
"account": &schema.Schema{
"account": {
Type: schema.TypeString,
Required: true,
DefaultFunc: schema.EnvDefaultFunc("SDC_ACCOUNT", ""),
},

"url": &schema.Schema{
"url": {
Type: schema.TypeString,
Required: true,
DefaultFunc: schema.EnvDefaultFunc("SDC_URL", "https://us-west-1.api.joyentcloud.com"),
},

"key_material": &schema.Schema{
"key_material": {
Type: schema.TypeString,
Required: true,
Optional: true,
DefaultFunc: schema.EnvDefaultFunc("SDC_KEY_MATERIAL", ""),
},

"key_id": &schema.Schema{
"key_id": {
Type: schema.TypeString,
Required: true,
DefaultFunc: schema.EnvDefaultFunc("SDC_KEY_ID", ""),
Expand All @@ -53,70 +55,113 @@ func Provider() terraform.ResourceProvider {
}
}

type SDCConfig struct {
type Config struct {
Account string
KeyMaterial string
KeyID string
URL string
}

func (c SDCConfig) validate() error {
func (c Config) 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"))
err = multierror.Append(err, errors.New("URL 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"))
err = multierror.Append(err, errors.New("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"))
err = multierror.Append(err, errors.New("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
func (c Config) getTritonClient() (*triton.Client, error) {
var signer authentication.Signer
var err error
if c.KeyMaterial == "" {
signer, err = authentication.NewSSHAgentSigner(c.KeyID, c.Account)
if err != nil {
return nil, errwrap.Wrapf("Error Creating SSH Agent Signer: {{err}}", err)
}
} else {
signer, err = authentication.NewPrivateKeySigner(c.KeyID, []byte(c.KeyMaterial), c.Account)
if err != nil {
return nil, errwrap.Wrapf("Error Creating SSH Private Key Signer: {{err}}", err)
}
}

creds := &auth.Credentials{
UserAuthentication: userauth,
SdcKeyId: c.KeyID,
SdcEndpoint: auth.Endpoint{URL: c.URL},
client, err := triton.NewClient(c.URL, c.Account, signer)
if err != nil {
return nil, errwrap.Wrapf("Error Creating Triton Client: {{err}}", err)
}

client := cloudapi.New(client.NewClient(
c.URL,
cloudapi.DefaultAPIVersion,
creds,
log.New(os.Stderr, "", log.LstdFlags),
))

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),
config := Config{
Account: d.Get("account").(string),
URL: d.Get("url").(string),
KeyID: d.Get("key_id").(string),
}

if keyMaterial, ok := d.GetOk("key_material"); ok {
config.KeyMaterial = keyMaterial.(string)
}

if err := config.validate(); err != nil {
return nil, err
}

client, err := config.getSDCClient()
client, err := config.getTritonClient()
if err != nil {
return nil, err
}

return client, nil
}

func resourceExists(resource interface{}, err error) (bool, error) {
if err != nil {
if triton.IsResourceNotFound(err) {
return false, nil
}

return false, err
}

return resource != nil, nil
}

func stableMapHash(input map[string]string) string {
keys := make([]string, 0, len(input))
for k := range input {
keys = append(keys, k)
}
sort.Strings(keys)

hash := md5.New()
for _, key := range keys {
hash.Write([]byte(key))
hash.Write([]byte(input[key]))
}

return base64.StdEncoding.EncodeToString(hash.Sum([]byte{}))
}

var fastResourceTimeout = &schema.ResourceTimeout{
Create: schema.DefaultTimeout(1 * time.Minute),
Read: schema.DefaultTimeout(30 * time.Second),
Update: schema.DefaultTimeout(1 * time.Minute),
Delete: schema.DefaultTimeout(1 * time.Minute),
}

var slowResourceTimeout = &schema.ResourceTimeout{
Create: schema.DefaultTimeout(10 * time.Minute),
Read: schema.DefaultTimeout(30 * time.Second),
Update: schema.DefaultTimeout(10 * time.Minute),
Delete: schema.DefaultTimeout(10 * time.Minute),
}
6 changes: 3 additions & 3 deletions builtin/providers/triton/provider_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,13 +32,13 @@ 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")
if sdcURL == "" || account == "" || keyID == "" {
t.Fatal("SDC_ACCOUNT and SDC_KEY_ID must be set for acceptance tests. To test with the SSH" +
" private key signer, SDC_KEY_MATERIAL must also be set.")
}
}
Loading

0 comments on commit a0568e5

Please sign in to comment.