opentofu/internal/backend/remote-state/consul/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

181 lines
4.6 KiB
Go

package consul
import (
"context"
"net"
"strings"
"time"
consulapi "github.com/hashicorp/consul/api"
"github.com/hashicorp/terraform/internal/backend"
"github.com/hashicorp/terraform/internal/legacy/helper/schema"
)
// New creates a new backend for Consul remote state.
func New() backend.Backend {
s := &schema.Backend{
Schema: map[string]*schema.Schema{
"path": &schema.Schema{
Type: schema.TypeString,
Required: true,
Description: "Path to store state in Consul",
},
"access_token": &schema.Schema{
Type: schema.TypeString,
Optional: true,
Description: "Access token for a Consul ACL",
Default: "", // To prevent input
},
"address": &schema.Schema{
Type: schema.TypeString,
Optional: true,
Description: "Address to the Consul Cluster",
Default: "", // To prevent input
},
"scheme": &schema.Schema{
Type: schema.TypeString,
Optional: true,
Description: "Scheme to communicate to Consul with",
Default: "", // To prevent input
},
"datacenter": &schema.Schema{
Type: schema.TypeString,
Optional: true,
Description: "Datacenter to communicate with",
Default: "", // To prevent input
},
"http_auth": &schema.Schema{
Type: schema.TypeString,
Optional: true,
Description: "HTTP Auth in the format of 'username:password'",
Default: "", // To prevent input
},
"gzip": &schema.Schema{
Type: schema.TypeBool,
Optional: true,
Description: "Compress the state data using gzip",
Default: false,
},
"lock": &schema.Schema{
Type: schema.TypeBool,
Optional: true,
Description: "Lock state access",
Default: true,
},
"ca_file": &schema.Schema{
Type: schema.TypeString,
Optional: true,
Description: "A path to a PEM-encoded certificate authority used to verify the remote agent's certificate.",
DefaultFunc: schema.EnvDefaultFunc("CONSUL_CACERT", ""),
},
"cert_file": &schema.Schema{
Type: schema.TypeString,
Optional: true,
Description: "A path to a PEM-encoded certificate provided to the remote agent; requires use of key_file.",
DefaultFunc: schema.EnvDefaultFunc("CONSUL_CLIENT_CERT", ""),
},
"key_file": &schema.Schema{
Type: schema.TypeString,
Optional: true,
Description: "A path to a PEM-encoded private key, required if cert_file is specified.",
DefaultFunc: schema.EnvDefaultFunc("CONSUL_CLIENT_KEY", ""),
},
},
}
result := &Backend{Backend: s}
result.Backend.ConfigureFunc = result.configure
return result
}
type Backend struct {
*schema.Backend
// The fields below are set from configure
client *consulapi.Client
configData *schema.ResourceData
lock bool
}
func (b *Backend) configure(ctx context.Context) error {
// Grab the resource data
b.configData = schema.FromContextBackendConfig(ctx)
// Store the lock information
b.lock = b.configData.Get("lock").(bool)
data := b.configData
// Configure the client
config := consulapi.DefaultConfig()
// replace the default Transport Dialer to reduce the KeepAlive
config.Transport.DialContext = dialContext
if v, ok := data.GetOk("access_token"); ok && v.(string) != "" {
config.Token = v.(string)
}
if v, ok := data.GetOk("address"); ok && v.(string) != "" {
config.Address = v.(string)
}
if v, ok := data.GetOk("scheme"); ok && v.(string) != "" {
config.Scheme = v.(string)
}
if v, ok := data.GetOk("datacenter"); ok && v.(string) != "" {
config.Datacenter = v.(string)
}
if v, ok := data.GetOk("ca_file"); ok && v.(string) != "" {
config.TLSConfig.CAFile = v.(string)
}
if v, ok := data.GetOk("cert_file"); ok && v.(string) != "" {
config.TLSConfig.CertFile = v.(string)
}
if v, ok := data.GetOk("key_file"); ok && v.(string) != "" {
config.TLSConfig.KeyFile = v.(string)
}
if v, ok := data.GetOk("http_auth"); ok && v.(string) != "" {
auth := v.(string)
var username, password string
if strings.Contains(auth, ":") {
split := strings.SplitN(auth, ":", 2)
username = split[0]
password = split[1]
} else {
username = auth
}
config.HttpAuth = &consulapi.HttpBasicAuth{
Username: username,
Password: password,
}
}
client, err := consulapi.NewClient(config)
if err != nil {
return err
}
b.client = client
return nil
}
// dialContext is the DialContext function for the consul client transport.
// This is stored in a package var to inject a different dialer for tests.
var dialContext = (&net.Dialer{
Timeout: 30 * time.Second,
KeepAlive: 17 * time.Second,
}).DialContext