mirror of
https://github.com/opentofu/opentofu.git
synced 2025-01-16 11:42:58 -06:00
25a8227291
Add a way to inject network errors by setting an immediate deadline on open consul connections. The consul client currently doesn't retry on some errors, and will force us to lose our lock. Once the consul api client is fixed, this test will fail.
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/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
|