opentofu/internal/backend/remote-state/manta/backend.go
Martin Atkins 73dda868cc Move backend/ to internal/backend/
This is part of a general effort to move all of Terraform's non-library
package surface under internal in order to reinforce that these are for
internal use within Terraform only.

If you were previously importing packages under this prefix into an
external codebase, you could pin to an earlier release tag as an interim
solution until you've make a plan to achieve the same functionality some
other way.
2021-05-17 14:09:07 -07:00

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
}