github.com/Kevinklinger/open_terraform@v0.11.12-beta1/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  			"user": {
    30  				Type:        schema.TypeString,
    31  				Optional:    true,
    32  				DefaultFunc: schema.MultiEnvDefaultFunc([]string{"TRITON_USER", "SDC_USER"}, ""),
    33  			},
    34  
    35  			"url": {
    36  				Type:        schema.TypeString,
    37  				Optional:    true,
    38  				DefaultFunc: schema.MultiEnvDefaultFunc([]string{"MANTA_URL"}, "https://us-east.manta.joyent.com"),
    39  			},
    40  
    41  			"key_material": {
    42  				Type:        schema.TypeString,
    43  				Optional:    true,
    44  				DefaultFunc: schema.MultiEnvDefaultFunc([]string{"TRITON_KEY_MATERIAL", "SDC_KEY_MATERIAL"}, ""),
    45  			},
    46  
    47  			"key_id": {
    48  				Type:        schema.TypeString,
    49  				Required:    true,
    50  				DefaultFunc: schema.MultiEnvDefaultFunc([]string{"TRITON_KEY_ID", "SDC_KEY_ID"}, ""),
    51  			},
    52  
    53  			"insecure_skip_tls_verify": {
    54  				Type:        schema.TypeBool,
    55  				Optional:    true,
    56  				DefaultFunc: schema.EnvDefaultFunc("TRITON_SKIP_TLS_VERIFY", ""),
    57  			},
    58  
    59  			"path": {
    60  				Type:     schema.TypeString,
    61  				Required: true,
    62  			},
    63  
    64  			"objectName": {
    65  				Type:       schema.TypeString,
    66  				Optional:   true,
    67  				Default:    "terraform.tfstate",
    68  				Deprecated: "please use the object_name attribute",
    69  			},
    70  
    71  			"object_name": {
    72  				Type:     schema.TypeString,
    73  				Optional: true,
    74  				// Set this default once the objectName attribute is removed!
    75  				// Default:  "terraform.tfstate",
    76  			},
    77  		},
    78  	}
    79  
    80  	result := &Backend{Backend: s}
    81  	result.Backend.ConfigureFunc = result.configure
    82  	return result
    83  }
    84  
    85  type Backend struct {
    86  	*schema.Backend
    87  	data *schema.ResourceData
    88  
    89  	// The fields below are set from configure
    90  	storageClient *storage.StorageClient
    91  	path          string
    92  	objectName    string
    93  }
    94  
    95  type BackendConfig struct {
    96  	AccountId   string
    97  	Username    string
    98  	KeyId       string
    99  	AccountUrl  string
   100  	KeyMaterial string
   101  	SkipTls     bool
   102  }
   103  
   104  func (b *Backend) configure(ctx context.Context) error {
   105  	if b.path != "" {
   106  		return nil
   107  	}
   108  
   109  	data := schema.FromContextBackendConfig(ctx)
   110  
   111  	config := &BackendConfig{
   112  		AccountId:  data.Get("account").(string),
   113  		AccountUrl: data.Get("url").(string),
   114  		KeyId:      data.Get("key_id").(string),
   115  		SkipTls:    data.Get("insecure_skip_tls_verify").(bool),
   116  	}
   117  
   118  	if v, ok := data.GetOk("user"); ok {
   119  		config.Username = v.(string)
   120  	}
   121  
   122  	if v, ok := data.GetOk("key_material"); ok {
   123  		config.KeyMaterial = v.(string)
   124  	}
   125  
   126  	b.path = data.Get("path").(string)
   127  	b.objectName = data.Get("object_name").(string)
   128  
   129  	// If object_name is not set, try the deprecated objectName.
   130  	if b.objectName == "" {
   131  		b.objectName = data.Get("objectName").(string)
   132  	}
   133  
   134  	var validationError *multierror.Error
   135  
   136  	if data.Get("account").(string) == "" {
   137  		validationError = multierror.Append(validationError, errors.New("`Account` must be configured for the Triton provider"))
   138  	}
   139  	if data.Get("key_id").(string) == "" {
   140  		validationError = multierror.Append(validationError, errors.New("`Key ID` must be configured for the Triton provider"))
   141  	}
   142  	if b.path == "" {
   143  		validationError = multierror.Append(validationError, errors.New("`Path` must be configured for the Triton provider"))
   144  	}
   145  
   146  	if validationError != nil {
   147  		return validationError
   148  	}
   149  
   150  	var signer authentication.Signer
   151  	var err error
   152  
   153  	if config.KeyMaterial == "" {
   154  		input := authentication.SSHAgentSignerInput{
   155  			KeyID:       config.KeyId,
   156  			AccountName: config.AccountId,
   157  			Username:    config.Username,
   158  		}
   159  		signer, err = authentication.NewSSHAgentSigner(input)
   160  		if err != nil {
   161  			return errwrap.Wrapf("Error Creating SSH Agent Signer: {{err}}", err)
   162  		}
   163  	} else {
   164  		var keyBytes []byte
   165  		if _, err = os.Stat(config.KeyMaterial); err == nil {
   166  			keyBytes, err = ioutil.ReadFile(config.KeyMaterial)
   167  			if err != nil {
   168  				return fmt.Errorf("Error reading key material from %s: %s",
   169  					config.KeyMaterial, err)
   170  			}
   171  			block, _ := pem.Decode(keyBytes)
   172  			if block == nil {
   173  				return fmt.Errorf(
   174  					"Failed to read key material '%s': no key found", config.KeyMaterial)
   175  			}
   176  
   177  			if block.Headers["Proc-Type"] == "4,ENCRYPTED" {
   178  				return fmt.Errorf(
   179  					"Failed to read key '%s': password protected keys are\n"+
   180  						"not currently supported. Please decrypt the key prior to use.", config.KeyMaterial)
   181  			}
   182  
   183  		} else {
   184  			keyBytes = []byte(config.KeyMaterial)
   185  		}
   186  
   187  		input := authentication.PrivateKeySignerInput{
   188  			KeyID:              config.KeyId,
   189  			PrivateKeyMaterial: keyBytes,
   190  			AccountName:        config.AccountId,
   191  			Username:           config.Username,
   192  		}
   193  
   194  		signer, err = authentication.NewPrivateKeySigner(input)
   195  		if err != nil {
   196  			return errwrap.Wrapf("Error Creating SSH Private Key Signer: {{err}}", err)
   197  		}
   198  	}
   199  
   200  	clientConfig := &triton.ClientConfig{
   201  		MantaURL:    config.AccountUrl,
   202  		AccountName: config.AccountId,
   203  		Username:    config.Username,
   204  		Signers:     []authentication.Signer{signer},
   205  	}
   206  	triton, err := storage.NewClient(clientConfig)
   207  	if err != nil {
   208  		return err
   209  	}
   210  
   211  	b.storageClient = triton
   212  
   213  	return nil
   214  }