2017-08-03 19:10:50 +02:00
|
|
|
package etcd
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
|
|
|
|
|
|
|
etcdv3 "github.com/coreos/etcd/clientv3"
|
2017-09-09 09:03:59 +02:00
|
|
|
"github.com/coreos/etcd/pkg/transport"
|
2021-05-17 17:42:17 +02:00
|
|
|
"github.com/hashicorp/terraform/internal/backend"
|
2020-11-18 16:07:30 +01:00
|
|
|
"github.com/hashicorp/terraform/internal/legacy/helper/schema"
|
2017-08-03 19:10:50 +02:00
|
|
|
)
|
|
|
|
|
2017-09-09 09:03:59 +02:00
|
|
|
const (
|
|
|
|
endpointsKey = "endpoints"
|
|
|
|
usernameKey = "username"
|
|
|
|
usernameEnvVarName = "ETCDV3_USERNAME"
|
|
|
|
passwordKey = "password"
|
|
|
|
passwordEnvVarName = "ETCDV3_PASSWORD"
|
|
|
|
prefixKey = "prefix"
|
|
|
|
lockKey = "lock"
|
|
|
|
cacertPathKey = "cacert_path"
|
|
|
|
certPathKey = "cert_path"
|
|
|
|
keyPathKey = "key_path"
|
|
|
|
)
|
|
|
|
|
2017-08-03 19:10:50 +02:00
|
|
|
func New() backend.Backend {
|
|
|
|
s := &schema.Backend{
|
|
|
|
Schema: map[string]*schema.Schema{
|
2017-09-09 09:03:59 +02:00
|
|
|
endpointsKey: &schema.Schema{
|
2017-09-08 13:16:00 +02:00
|
|
|
Type: schema.TypeList,
|
|
|
|
Elem: &schema.Schema{
|
|
|
|
Type: schema.TypeString,
|
|
|
|
},
|
|
|
|
MinItems: 1,
|
2017-08-03 19:10:50 +02:00
|
|
|
Required: true,
|
2017-09-08 13:16:00 +02:00
|
|
|
Description: "Endpoints for the etcd cluster.",
|
2017-08-03 19:10:50 +02:00
|
|
|
},
|
|
|
|
|
2017-09-09 09:03:59 +02:00
|
|
|
usernameKey: &schema.Schema{
|
2017-08-03 19:10:50 +02:00
|
|
|
Type: schema.TypeString,
|
|
|
|
Optional: true,
|
|
|
|
Description: "Username used to connect to the etcd cluster.",
|
2017-09-09 09:03:59 +02:00
|
|
|
DefaultFunc: schema.EnvDefaultFunc(usernameEnvVarName, ""),
|
2017-08-03 19:10:50 +02:00
|
|
|
},
|
|
|
|
|
2017-09-09 09:03:59 +02:00
|
|
|
passwordKey: &schema.Schema{
|
2017-08-03 19:10:50 +02:00
|
|
|
Type: schema.TypeString,
|
|
|
|
Optional: true,
|
|
|
|
Description: "Password used to connect to the etcd cluster.",
|
2017-09-09 09:03:59 +02:00
|
|
|
DefaultFunc: schema.EnvDefaultFunc(passwordEnvVarName, ""),
|
2017-08-03 19:10:50 +02:00
|
|
|
},
|
|
|
|
|
2017-09-09 09:03:59 +02:00
|
|
|
prefixKey: &schema.Schema{
|
2017-08-03 19:10:50 +02:00
|
|
|
Type: schema.TypeString,
|
2017-09-08 23:34:15 +02:00
|
|
|
Optional: true,
|
|
|
|
Description: "An optional prefix to be added to keys when to storing state in etcd.",
|
|
|
|
Default: "",
|
2017-08-03 19:10:50 +02:00
|
|
|
},
|
|
|
|
|
2017-09-09 09:03:59 +02:00
|
|
|
lockKey: &schema.Schema{
|
2017-08-03 19:10:50 +02:00
|
|
|
Type: schema.TypeBool,
|
|
|
|
Optional: true,
|
2017-09-09 00:40:05 +02:00
|
|
|
Description: "Whether to lock state access.",
|
2017-08-03 19:10:50 +02:00
|
|
|
Default: true,
|
|
|
|
},
|
2017-09-09 09:03:59 +02:00
|
|
|
|
|
|
|
cacertPathKey: &schema.Schema{
|
|
|
|
Type: schema.TypeString,
|
|
|
|
Optional: true,
|
|
|
|
Description: "The path to a PEM-encoded CA bundle with which to verify certificates of TLS-enabled etcd servers.",
|
|
|
|
Default: "",
|
|
|
|
},
|
|
|
|
|
|
|
|
certPathKey: &schema.Schema{
|
|
|
|
Type: schema.TypeString,
|
|
|
|
Optional: true,
|
|
|
|
Description: "The path to a PEM-encoded certificate to provide to etcd for secure client identification.",
|
|
|
|
Default: "",
|
|
|
|
},
|
|
|
|
|
|
|
|
keyPathKey: &schema.Schema{
|
|
|
|
Type: schema.TypeString,
|
|
|
|
Optional: true,
|
|
|
|
Description: "The path to a PEM-encoded key to provide to etcd for secure client identification.",
|
|
|
|
Default: "",
|
|
|
|
},
|
2017-08-03 19:10:50 +02:00
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
result := &Backend{Backend: s}
|
|
|
|
result.Backend.ConfigureFunc = result.configure
|
|
|
|
return result
|
|
|
|
}
|
|
|
|
|
|
|
|
type Backend struct {
|
|
|
|
*schema.Backend
|
|
|
|
|
|
|
|
// The fields below are set from configure.
|
2017-09-09 01:21:23 +02:00
|
|
|
client *etcdv3.Client
|
2017-08-03 19:10:50 +02:00
|
|
|
data *schema.ResourceData
|
|
|
|
lock bool
|
|
|
|
prefix string
|
|
|
|
}
|
|
|
|
|
|
|
|
func (b *Backend) configure(ctx context.Context) error {
|
2017-09-09 01:21:23 +02:00
|
|
|
var err error
|
2017-08-03 19:10:50 +02:00
|
|
|
// Grab the resource data.
|
|
|
|
b.data = schema.FromContextBackendConfig(ctx)
|
|
|
|
// Store the lock information.
|
2017-09-09 09:03:59 +02:00
|
|
|
b.lock = b.data.Get(lockKey).(bool)
|
2017-08-03 19:10:50 +02:00
|
|
|
// Store the prefix information.
|
2017-09-09 09:03:59 +02:00
|
|
|
b.prefix = b.data.Get(prefixKey).(string)
|
2017-08-03 19:10:50 +02:00
|
|
|
// Initialize a client to test config.
|
2017-09-09 01:21:23 +02:00
|
|
|
b.client, err = b.rawClient()
|
2017-08-03 19:10:50 +02:00
|
|
|
// Return err, if any.
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
func (b *Backend) rawClient() (*etcdv3.Client, error) {
|
|
|
|
config := etcdv3.Config{}
|
2017-09-09 09:03:59 +02:00
|
|
|
tlsInfo := transport.TLSInfo{}
|
2017-08-03 19:10:50 +02:00
|
|
|
|
2017-09-09 09:03:59 +02:00
|
|
|
if v, ok := b.data.GetOk(endpointsKey); ok {
|
2017-09-08 13:16:00 +02:00
|
|
|
config.Endpoints = retrieveEndpoints(v)
|
2017-08-03 19:10:50 +02:00
|
|
|
}
|
2017-09-09 09:03:59 +02:00
|
|
|
if v, ok := b.data.GetOk(usernameKey); ok && v.(string) != "" {
|
2017-08-03 19:10:50 +02:00
|
|
|
config.Username = v.(string)
|
|
|
|
}
|
2017-09-09 09:03:59 +02:00
|
|
|
if v, ok := b.data.GetOk(passwordKey); ok && v.(string) != "" {
|
2017-08-03 19:10:50 +02:00
|
|
|
config.Password = v.(string)
|
|
|
|
}
|
2017-09-09 09:03:59 +02:00
|
|
|
if v, ok := b.data.GetOk(cacertPathKey); ok && v.(string) != "" {
|
|
|
|
tlsInfo.TrustedCAFile = v.(string)
|
|
|
|
}
|
|
|
|
if v, ok := b.data.GetOk(certPathKey); ok && v.(string) != "" {
|
|
|
|
tlsInfo.CertFile = v.(string)
|
|
|
|
}
|
|
|
|
if v, ok := b.data.GetOk(keyPathKey); ok && v.(string) != "" {
|
|
|
|
tlsInfo.KeyFile = v.(string)
|
|
|
|
}
|
|
|
|
|
|
|
|
if tlsCfg, err := tlsInfo.ClientConfig(); err != nil {
|
|
|
|
return nil, err
|
|
|
|
} else if !tlsInfo.Empty() {
|
|
|
|
config.TLS = tlsCfg // Assign TLS configuration only if it valid and non-empty.
|
|
|
|
}
|
2017-08-03 19:10:50 +02:00
|
|
|
|
|
|
|
return etcdv3.New(config)
|
|
|
|
}
|
2017-09-08 13:16:00 +02:00
|
|
|
|
|
|
|
func retrieveEndpoints(v interface{}) []string {
|
|
|
|
var endpoints []string
|
|
|
|
list := v.([]interface{})
|
|
|
|
for _, ep := range list {
|
|
|
|
endpoints = append(endpoints, ep.(string))
|
|
|
|
}
|
|
|
|
return endpoints
|
|
|
|
}
|