| package etcd |
| |
| import ( |
| "context" |
| |
| "github.com/hashicorp/terraform/internal/backend" |
| "github.com/hashicorp/terraform/internal/legacy/helper/schema" |
| etcdv3 "go.etcd.io/etcd/clientv3" |
| "go.etcd.io/etcd/pkg/transport" |
| ) |
| |
| const ( |
| endpointsKey = "endpoints" |
| usernameKey = "username" |
| usernameEnvVarName = "ETCDV3_USERNAME" |
| passwordKey = "password" |
| passwordEnvVarName = "ETCDV3_PASSWORD" |
| maxRequestBytesKey = "max_request_bytes" |
| prefixKey = "prefix" |
| lockKey = "lock" |
| cacertPathKey = "cacert_path" |
| certPathKey = "cert_path" |
| keyPathKey = "key_path" |
| ) |
| |
| func New() backend.Backend { |
| s := &schema.Backend{ |
| Schema: map[string]*schema.Schema{ |
| endpointsKey: &schema.Schema{ |
| Type: schema.TypeList, |
| Elem: &schema.Schema{ |
| Type: schema.TypeString, |
| }, |
| MinItems: 1, |
| Required: true, |
| Description: "Endpoints for the etcd cluster.", |
| }, |
| |
| usernameKey: &schema.Schema{ |
| Type: schema.TypeString, |
| Optional: true, |
| Description: "Username used to connect to the etcd cluster.", |
| DefaultFunc: schema.EnvDefaultFunc(usernameEnvVarName, ""), |
| }, |
| |
| passwordKey: &schema.Schema{ |
| Type: schema.TypeString, |
| Optional: true, |
| Description: "Password used to connect to the etcd cluster.", |
| DefaultFunc: schema.EnvDefaultFunc(passwordEnvVarName, ""), |
| }, |
| |
| maxRequestBytesKey: &schema.Schema{ |
| Type: schema.TypeInt, |
| Optional: true, |
| Description: "The max request size to send to etcd.", |
| Default: 0, |
| }, |
| |
| prefixKey: &schema.Schema{ |
| Type: schema.TypeString, |
| Optional: true, |
| Description: "An optional prefix to be added to keys when to storing state in etcd.", |
| Default: "", |
| }, |
| |
| lockKey: &schema.Schema{ |
| Type: schema.TypeBool, |
| Optional: true, |
| Description: "Whether to lock state access.", |
| Default: true, |
| }, |
| |
| 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: "", |
| }, |
| }, |
| } |
| |
| result := &Backend{Backend: s} |
| result.Backend.ConfigureFunc = result.configure |
| return result |
| } |
| |
| type Backend struct { |
| *schema.Backend |
| |
| // The fields below are set from configure. |
| client *etcdv3.Client |
| data *schema.ResourceData |
| lock bool |
| prefix string |
| } |
| |
| func (b *Backend) configure(ctx context.Context) error { |
| var err error |
| // Grab the resource data. |
| b.data = schema.FromContextBackendConfig(ctx) |
| // Store the lock information. |
| b.lock = b.data.Get(lockKey).(bool) |
| // Store the prefix information. |
| b.prefix = b.data.Get(prefixKey).(string) |
| // Initialize a client to test config. |
| b.client, err = b.rawClient() |
| // Return err, if any. |
| return err |
| } |
| |
| func (b *Backend) rawClient() (*etcdv3.Client, error) { |
| config := etcdv3.Config{} |
| tlsInfo := transport.TLSInfo{} |
| |
| if v, ok := b.data.GetOk(endpointsKey); ok { |
| config.Endpoints = retrieveEndpoints(v) |
| } |
| if v, ok := b.data.GetOk(usernameKey); ok && v.(string) != "" { |
| config.Username = v.(string) |
| } |
| if v, ok := b.data.GetOk(passwordKey); ok && v.(string) != "" { |
| config.Password = v.(string) |
| } |
| if v, ok := b.data.GetOk(maxRequestBytesKey); ok && v.(int) != 0 { |
| config.MaxCallSendMsgSize = v.(int) |
| } |
| 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. |
| } |
| |
| return etcdv3.New(config) |
| } |
| |
| func retrieveEndpoints(v interface{}) []string { |
| var endpoints []string |
| list := v.([]interface{}) |
| for _, ep := range list { |
| endpoints = append(endpoints, ep.(string)) |
| } |
| return endpoints |
| } |