181 lines
4.6 KiB
Go
181 lines
4.6 KiB
Go
package consul
|
|
|
|
import (
|
|
"context"
|
|
"net"
|
|
"strings"
|
|
"time"
|
|
|
|
consulapi "github.com/hashicorp/consul/api"
|
|
"github.com/hashicorp/terraform/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
|