@ -1,16 +1,15 @@
package triton
import (
"errors"
"fmt"
"io/ioutil"
"log"
"os"
"github.com/hashicorp/packer/helper/communicator"
"github.com/hashicorp/packer/template/interpolate"
"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"
)
// AccessConfig is for common configuration related to Triton access
@ -19,29 +18,40 @@ type AccessConfig struct {
Account string ` mapstructure:"triton_account" `
KeyID string ` mapstructure:"triton_key_id" `
KeyMaterial string ` mapstructure:"triton_key_material" `
signer authentication . Signer
}
// Prepare performs basic validation on the AccessConfig
// Prepare performs basic validation on the AccessConfig and ensures we can sign
// a request.
func ( c * AccessConfig ) Prepare ( ctx * interpolate . Context ) [ ] error {
var errs [ ] error
if c . Endpoint == "" {
// Use Joyent public cloud as the default endpoint if none is in environment
// Use Joyent public cloud as the default endpoint if none is specified
c . Endpoint = "https://us-east-1.api.joyent.com"
}
if c . Account == "" {
errs = append ( errs , fmt. Errorf ( "triton_account is required to use the triton builder" ) )
errs = append ( errs , errors. New ( "triton_account is required to use the triton builder" ) )
}
if c . KeyID == "" {
errs = append ( errs , fmt. Errorf ( "triton_key_id is required to use the triton builder" ) )
errs = append ( errs , errors. New ( "triton_key_id is required to use the triton builder" ) )
}
var err error
c . KeyMaterial , err = processKeyMaterial ( c . KeyMaterial )
if c . KeyMaterial == "" || err != nil {
errs = append ( errs , fmt . Errorf ( "valid triton_key_material is required to use the triton builder" ) )
if c . KeyMaterial == "" {
signer , err := c . createSSHAgentSigner ( )
if err != nil {
errs = append ( errs , err )
}
c . signer = signer
} else {
signer , err := c . createPrivateKeySigner ( )
if err != nil {
errs = append ( errs , err )
}
c . signer = signer
}
if len ( errs ) > 0 {
@ -51,49 +61,55 @@ func (c *AccessConfig) Prepare(ctx *interpolate.Context) []error {
return nil
}
// CreateTritonClient returns an SDC client configured with the appropriate client credentials
// or an error if creating the client fails.
func ( c * AccessConfig ) CreateTritonClient ( ) ( * cloudapi . Client , error ) {
keyData , err := processKeyMaterial ( c . KeyMaterial )
func ( c * AccessConfig ) createSSHAgentSigner ( ) ( authentication . Signer , error ) {
signer , err := authentication . NewSSHAgentSigner ( c . KeyID , c . Account )
if err != nil {
return nil , err
return nil , fmt . Errorf ( "Error creating Triton request signer: %s" , err )
}
userauth , err := auth . NewAuth ( c . Account , keyData , "rsa-sha256" )
// Ensure we can sign a request
_ , err = signer . Sign ( "Wed, 26 Apr 2017 16:01:11 UTC" )
if err != nil {
return nil , err
return nil , fmt. Errorf ( "Error signing test request: %s" , err)
}
creds := & auth . Credentials {
UserAuthentication : userauth ,
SdcKeyId : c . KeyID ,
SdcEndpoint : auth . Endpoint { URL : c . Endpoint } ,
}
return cloudapi . New ( client . NewClient (
c . Endpoint ,
cloudapi . DefaultAPIVersion ,
creds ,
log . New ( os . Stdout , "" , log . Flags ( ) ) ,
) ) , nil
return signer , nil
}
func ( c * AccessConfig ) Comm( ) communicator . Config {
return communicator . Config { }
}
func ( c * AccessConfig ) createPrivateKeySigner ( ) ( authentication . Signer , error ) {
var privateKeyMaterial [ ] byte
var err error
func processKeyMaterial ( keyMaterial string ) ( string , error ) {
// Check for keyMaterial being a file path
if _ , err := os . Stat ( keyMaterial ) ; err != nil {
// Not a valid file. Assume that keyMaterial is the key data
return keyMaterial , nil
if _ , err = os . Stat ( c . KeyMaterial ) ; err != nil {
privateKeyMaterial = [ ] byte ( c . KeyMaterial )
} else {
privateKeyMaterial , err = ioutil . ReadFile ( c . KeyMaterial )
if err != nil {
return nil , fmt . Errorf ( "Error reading key material from path '%s': %s" ,
c . KeyMaterial , err )
}
}
b , err := ioutil . ReadFile ( keyMaterial )
// Create signer
signer , err := authentication . NewPrivateKeySigner ( c . KeyID , privateKeyMaterial , c . Account )
if err != nil {
return "" , fmt . Errorf ( "Error reading key_material from path '%s': %s" ,
keyMaterial , err )
return nil , fmt . Errorf ( "Error creating Triton request signer: %s" , err )
}
return string ( b ) , nil
// Ensure we can sign a request
_ , err = signer . Sign ( "Wed, 26 Apr 2017 16:01:11 UTC" )
if err != nil {
return nil , fmt . Errorf ( "Error signing test request: %s" , err )
}
return signer , nil
}
func ( c * AccessConfig ) CreateTritonClient ( ) ( * triton . Client , error ) {
return triton . NewClient ( c . Endpoint , c . Account , c . signer )
}
func ( c * AccessConfig ) Comm ( ) communicator . Config {
return communicator . Config { }
}