terraform/internal/backend/remote-state/manta/backend.go

207 lines
5.1 KiB
Go

package manta
import (
"context"
"encoding/pem"
"errors"
"fmt"
"io/ioutil"
"os"
"github.com/hashicorp/errwrap"
"github.com/hashicorp/go-multierror"
"github.com/hashicorp/terraform/internal/backend"
"github.com/hashicorp/terraform/internal/legacy/helper/schema"
triton "github.com/joyent/triton-go"
"github.com/joyent/triton-go/authentication"
"github.com/joyent/triton-go/storage"
)
func New() backend.Backend {
s := &schema.Backend{
Schema: map[string]*schema.Schema{
"account": {
Type: schema.TypeString,
Required: true,
DefaultFunc: schema.MultiEnvDefaultFunc([]string{"TRITON_ACCOUNT", "SDC_ACCOUNT"}, ""),
},
"user": {
Type: schema.TypeString,
Optional: true,
DefaultFunc: schema.MultiEnvDefaultFunc([]string{"TRITON_USER", "SDC_USER"}, ""),
},
"url": {
Type: schema.TypeString,
Optional: true,
DefaultFunc: schema.MultiEnvDefaultFunc([]string{"MANTA_URL"}, "https://us-east.manta.joyent.com"),
},
"key_material": {
Type: schema.TypeString,
Optional: true,
DefaultFunc: schema.MultiEnvDefaultFunc([]string{"TRITON_KEY_MATERIAL", "SDC_KEY_MATERIAL"}, ""),
},
"key_id": {
Type: schema.TypeString,
Required: true,
DefaultFunc: schema.MultiEnvDefaultFunc([]string{"TRITON_KEY_ID", "SDC_KEY_ID"}, ""),
},
"insecure_skip_tls_verify": {
Type: schema.TypeBool,
Optional: true,
DefaultFunc: schema.EnvDefaultFunc("TRITON_SKIP_TLS_VERIFY", false),
},
"path": {
Type: schema.TypeString,
Required: true,
},
"object_name": {
Type: schema.TypeString,
Optional: true,
Default: "terraform.tfstate",
},
},
}
result := &Backend{Backend: s}
result.Backend.ConfigureFunc = result.configure
return result
}
type Backend struct {
*schema.Backend
data *schema.ResourceData
// The fields below are set from configure
storageClient *storage.StorageClient
path string
objectName string
}
type BackendConfig struct {
AccountId string
Username string
KeyId string
AccountUrl string
KeyMaterial string
SkipTls bool
}
func (b *Backend) configure(ctx context.Context) error {
if b.path != "" {
return nil
}
data := schema.FromContextBackendConfig(ctx)
config := &BackendConfig{
AccountId: data.Get("account").(string),
AccountUrl: data.Get("url").(string),
KeyId: data.Get("key_id").(string),
SkipTls: data.Get("insecure_skip_tls_verify").(bool),
}
if v, ok := data.GetOk("user"); ok {
config.Username = v.(string)
}
if v, ok := data.GetOk("key_material"); ok {
config.KeyMaterial = v.(string)
}
b.path = data.Get("path").(string)
b.objectName = data.Get("object_name").(string)
// If object_name is not set, try the deprecated objectName.
if b.objectName == "" {
b.objectName = data.Get("objectName").(string)
}
var validationError *multierror.Error
if data.Get("account").(string) == "" {
validationError = multierror.Append(validationError, errors.New("`Account` must be configured for the Triton provider"))
}
if data.Get("key_id").(string) == "" {
validationError = multierror.Append(validationError, errors.New("`Key ID` must be configured for the Triton provider"))
}
if b.path == "" {
validationError = multierror.Append(validationError, errors.New("`Path` must be configured for the Triton provider"))
}
if validationError != nil {
return validationError
}
var signer authentication.Signer
var err error
if config.KeyMaterial == "" {
input := authentication.SSHAgentSignerInput{
KeyID: config.KeyId,
AccountName: config.AccountId,
Username: config.Username,
}
signer, err = authentication.NewSSHAgentSigner(input)
if err != nil {
return errwrap.Wrapf("Error Creating SSH Agent Signer: {{err}}", err)
}
} else {
var keyBytes []byte
if _, err = os.Stat(config.KeyMaterial); err == nil {
keyBytes, err = ioutil.ReadFile(config.KeyMaterial)
if err != nil {
return fmt.Errorf("Error reading key material from %s: %s",
config.KeyMaterial, err)
}
block, _ := pem.Decode(keyBytes)
if block == nil {
return fmt.Errorf(
"Failed to read key material '%s': no key found", config.KeyMaterial)
}
if block.Headers["Proc-Type"] == "4,ENCRYPTED" {
return fmt.Errorf(
"Failed to read key '%s': password protected keys are\n"+
"not currently supported. Please decrypt the key prior to use.", config.KeyMaterial)
}
} else {
keyBytes = []byte(config.KeyMaterial)
}
input := authentication.PrivateKeySignerInput{
KeyID: config.KeyId,
PrivateKeyMaterial: keyBytes,
AccountName: config.AccountId,
Username: config.Username,
}
signer, err = authentication.NewPrivateKeySigner(input)
if err != nil {
return errwrap.Wrapf("Error Creating SSH Private Key Signer: {{err}}", err)
}
}
clientConfig := &triton.ClientConfig{
MantaURL: config.AccountUrl,
AccountName: config.AccountId,
Username: config.Username,
Signers: []authentication.Signer{signer},
}
triton, err := storage.NewClient(clientConfig)
if err != nil {
return err
}
b.storageClient = triton
return nil
}