github.com/hooklift/terraform@v0.11.0-beta1.0.20171117000744-6786c1361ffe/backend/remote-state/manta/backend.go (about)

     1  package manta
     2  
     3  import (
     4  	"context"
     5  	"encoding/pem"
     6  	"errors"
     7  	"fmt"
     8  	"io/ioutil"
     9  	"os"
    10  
    11  	"github.com/hashicorp/errwrap"
    12  	"github.com/hashicorp/go-multierror"
    13  	"github.com/hashicorp/terraform/backend"
    14  	"github.com/hashicorp/terraform/helper/schema"
    15  	triton "github.com/joyent/triton-go"
    16  	"github.com/joyent/triton-go/authentication"
    17  	"github.com/joyent/triton-go/storage"
    18  )
    19  
    20  func New() backend.Backend {
    21  	s := &schema.Backend{
    22  		Schema: map[string]*schema.Schema{
    23  			"account": {
    24  				Type:        schema.TypeString,
    25  				Required:    true,
    26  				DefaultFunc: schema.MultiEnvDefaultFunc([]string{"TRITON_ACCOUNT", "SDC_ACCOUNT"}, ""),
    27  			},
    28  
    29  			"url": {
    30  				Type:        schema.TypeString,
    31  				Optional:    true,
    32  				DefaultFunc: schema.MultiEnvDefaultFunc([]string{"MANTA_URL"}, "https://us-east.manta.joyent.com"),
    33  			},
    34  
    35  			"key_material": {
    36  				Type:        schema.TypeString,
    37  				Optional:    true,
    38  				DefaultFunc: schema.MultiEnvDefaultFunc([]string{"TRITON_KEY_MATERIAL", "SDC_KEY_MATERIAL"}, ""),
    39  			},
    40  
    41  			"key_id": {
    42  				Type:        schema.TypeString,
    43  				Required:    true,
    44  				DefaultFunc: schema.MultiEnvDefaultFunc([]string{"TRITON_KEY_ID", "SDC_KEY_ID"}, ""),
    45  			},
    46  
    47  			"insecure_skip_tls_verify": {
    48  				Type:        schema.TypeBool,
    49  				Optional:    true,
    50  				DefaultFunc: schema.EnvDefaultFunc("TRITON_SKIP_TLS_VERIFY", ""),
    51  			},
    52  
    53  			"path": {
    54  				Type:     schema.TypeString,
    55  				Required: true,
    56  			},
    57  
    58  			"objectName": {
    59  				Type:     schema.TypeString,
    60  				Optional: true,
    61  				Default:  "terraform.tfstate",
    62  			},
    63  		},
    64  	}
    65  
    66  	result := &Backend{Backend: s}
    67  	result.Backend.ConfigureFunc = result.configure
    68  	return result
    69  }
    70  
    71  type Backend struct {
    72  	*schema.Backend
    73  	data *schema.ResourceData
    74  
    75  	// The fields below are set from configure
    76  	storageClient *storage.StorageClient
    77  	path          string
    78  	objectName    string
    79  }
    80  
    81  type BackendConfig struct {
    82  	AccountId   string
    83  	KeyId       string
    84  	AccountUrl  string
    85  	KeyMaterial string
    86  	SkipTls     bool
    87  }
    88  
    89  func (b *Backend) configure(ctx context.Context) error {
    90  	if b.path != "" {
    91  		return nil
    92  	}
    93  
    94  	data := schema.FromContextBackendConfig(ctx)
    95  
    96  	config := &BackendConfig{
    97  		AccountId:  data.Get("account").(string),
    98  		AccountUrl: data.Get("url").(string),
    99  		KeyId:      data.Get("key_id").(string),
   100  		SkipTls:    data.Get("insecure_skip_tls_verify").(bool),
   101  	}
   102  
   103  	if v, ok := data.GetOk("key_material"); ok {
   104  		config.KeyMaterial = v.(string)
   105  	}
   106  
   107  	b.path = data.Get("path").(string)
   108  	b.objectName = data.Get("objectName").(string)
   109  
   110  	var validationError *multierror.Error
   111  
   112  	if data.Get("account").(string) == "" {
   113  		validationError = multierror.Append(validationError, errors.New("`Account` must be configured for the Triton provider"))
   114  	}
   115  	if data.Get("key_id").(string) == "" {
   116  		validationError = multierror.Append(validationError, errors.New("`Key ID` must be configured for the Triton provider"))
   117  	}
   118  	if b.path == "" {
   119  		validationError = multierror.Append(validationError, errors.New("`Path` must be configured for the Triton provider"))
   120  	}
   121  
   122  	if validationError != nil {
   123  		return validationError
   124  	}
   125  
   126  	var signer authentication.Signer
   127  	var err error
   128  
   129  	if config.KeyMaterial == "" {
   130  		signer, err = authentication.NewSSHAgentSigner(config.KeyId, config.AccountId)
   131  		if err != nil {
   132  			return errwrap.Wrapf("Error Creating SSH Agent Signer: {{err}}", err)
   133  		}
   134  	} else {
   135  		var keyBytes []byte
   136  		if _, err = os.Stat(config.KeyMaterial); err == nil {
   137  			keyBytes, err = ioutil.ReadFile(config.KeyMaterial)
   138  			if err != nil {
   139  				return fmt.Errorf("Error reading key material from %s: %s",
   140  					config.KeyMaterial, err)
   141  			}
   142  			block, _ := pem.Decode(keyBytes)
   143  			if block == nil {
   144  				return fmt.Errorf(
   145  					"Failed to read key material '%s': no key found", config.KeyMaterial)
   146  			}
   147  
   148  			if block.Headers["Proc-Type"] == "4,ENCRYPTED" {
   149  				return fmt.Errorf(
   150  					"Failed to read key '%s': password protected keys are\n"+
   151  						"not currently supported. Please decrypt the key prior to use.", config.KeyMaterial)
   152  			}
   153  
   154  		} else {
   155  			keyBytes = []byte(config.KeyMaterial)
   156  		}
   157  
   158  		signer, err = authentication.NewPrivateKeySigner(config.KeyId, keyBytes, config.AccountId)
   159  		if err != nil {
   160  			return errwrap.Wrapf("Error Creating SSH Private Key Signer: {{err}}", err)
   161  		}
   162  	}
   163  
   164  	clientConfig := &triton.ClientConfig{
   165  		MantaURL:    config.AccountUrl,
   166  		AccountName: config.AccountId,
   167  		Signers:     []authentication.Signer{signer},
   168  	}
   169  	triton, err := storage.NewClient(clientConfig)
   170  	if err != nil {
   171  		return err
   172  	}
   173  
   174  	b.storageClient = triton
   175  
   176  	return nil
   177  }