internal/clients/config/elasticsearch.go (234 lines of code) (raw):

package config import ( "context" "crypto/tls" "fmt" "net/http" "os" "strconv" "strings" "github.com/elastic/go-elasticsearch/v8" fwdiags "github.com/hashicorp/terraform-plugin-framework/diag" "github.com/hashicorp/terraform-plugin-framework/types/basetypes" sdkdiags "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/logging" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" ) type elasticsearchConfig struct { config elasticsearch.Config bearerToken string esClientAuthentication string } func newElasticsearchConfigFromSDK(d *schema.ResourceData, base baseConfig, key string, useEnvAsDefault bool) (*elasticsearchConfig, sdkdiags.Diagnostics) { esConn, ok := d.GetOk(key) if !ok { return nil, nil } var diags sdkdiags.Diagnostics config := base.toElasticsearchConfig() // if defined, then we only have a single entry if es := esConn.([]interface{})[0]; es != nil { esConfig := es.(map[string]interface{}) if endpoints, ok := esConfig["endpoints"]; ok && len(endpoints.([]interface{})) > 0 { var addrs []string for _, e := range endpoints.([]interface{}) { addrs = append(addrs, e.(string)) } config.config.Addresses = addrs } if headers, ok := esConfig["headers"]; ok && len(headers.(map[string]interface{})) > 0 { headersMap := headers.(map[string]interface{}) for header, value := range headersMap { config.config.Header.Add(strings.TrimSpace(header), strings.TrimSpace(value.(string))) } } if bearer_token, ok := esConfig["bearer_token"].(string); ok && bearer_token != "" { config.bearerToken = bearer_token } if es_client_authentication, ok := esConfig["es_client_authentication"].(string); ok && es_client_authentication != "" { config.esClientAuthentication = es_client_authentication } if insecure, ok := esConfig["insecure"]; ok && insecure.(bool) { tlsClientConfig := config.ensureTLSClientConfig() tlsClientConfig.InsecureSkipVerify = true } if caFile, ok := esConfig["ca_file"]; ok && caFile.(string) != "" { caCert, err := os.ReadFile(caFile.(string)) if err != nil { diags = append(diags, sdkdiags.Diagnostic{ Severity: sdkdiags.Error, Summary: "Unable to read CA File", Detail: err.Error(), }) return nil, diags } config.config.CACert = caCert } if caData, ok := esConfig["ca_data"]; ok && caData.(string) != "" { config.config.CACert = []byte(caData.(string)) } if certFile, ok := esConfig["cert_file"]; ok && certFile.(string) != "" { if keyFile, ok := esConfig["key_file"]; ok && keyFile.(string) != "" { cert, err := tls.LoadX509KeyPair(certFile.(string), keyFile.(string)) if err != nil { diags = append(diags, sdkdiags.Diagnostic{ Severity: sdkdiags.Error, Summary: "Unable to read certificate or key file", Detail: err.Error(), }) return nil, diags } tlsClientConfig := config.ensureTLSClientConfig() tlsClientConfig.Certificates = []tls.Certificate{cert} } else { diags = append(diags, sdkdiags.Diagnostic{ Severity: sdkdiags.Error, Summary: "Unable to read key file", Detail: "Path to key file has not been configured or is empty", }) return nil, diags } } if certData, ok := esConfig["cert_data"]; ok && certData.(string) != "" { if keyData, ok := esConfig["key_data"]; ok && keyData.(string) != "" { cert, err := tls.X509KeyPair([]byte(certData.(string)), []byte(keyData.(string))) if err != nil { diags = append(diags, sdkdiags.Diagnostic{ Severity: sdkdiags.Error, Summary: "Unable to parse certificate or key", Detail: err.Error(), }) return nil, diags } tlsClientConfig := config.ensureTLSClientConfig() tlsClientConfig.Certificates = []tls.Certificate{cert} } else { diags = append(diags, sdkdiags.Diagnostic{ Severity: sdkdiags.Error, Summary: "Unable to parse key", Detail: "Key data has not been configured or is empty", }) return nil, diags } } } if logging.IsDebugOrHigher() { config.config.EnableDebugLogger = true config.config.Logger = &debugLogger{Name: "elasticsearch"} } config = config.withEnvironmentOverrides() return &config, nil } func newElasticsearchConfigFromFramework(ctx context.Context, cfg ProviderConfiguration, base baseConfig) (*elasticsearchConfig, fwdiags.Diagnostics) { if len(cfg.Elasticsearch) == 0 { return nil, nil } config := base.toElasticsearchConfig() esConfig := cfg.Elasticsearch[0] var endpoints []string diags := esConfig.Endpoints.ElementsAs(ctx, &endpoints, true) if diags.HasError() { return nil, diags } if len(endpoints) > 0 { config.config.Addresses = endpoints } for header, value := range esConfig.Headers.Elements() { strValue := value.(basetypes.StringValue) // trim the strings to remove any leading/trailing whitespace config.config.Header.Add(strings.TrimSpace(header), strings.TrimSpace(strValue.ValueString())) } if esConfig.BearerToken.ValueString() != "" { config.bearerToken = esConfig.BearerToken.ValueString() if esConfig.ESClientAuthentication.ValueString() != "" { config.esClientAuthentication = esConfig.ESClientAuthentication.ValueString() } } if esConfig.Insecure.ValueBool() { tlsClientConfig := config.ensureTLSClientConfig() tlsClientConfig.InsecureSkipVerify = true } if caFile := esConfig.CAFile.ValueString(); caFile != "" { caCert, err := os.ReadFile(caFile) if err != nil { diags.Append(fwdiags.NewErrorDiagnostic("Unable to read CA file", err.Error())) return nil, diags } config.config.CACert = caCert } if caData := esConfig.CAData.ValueString(); caData != "" { config.config.CACert = []byte(caData) } if certFile := esConfig.CertFile.ValueString(); certFile != "" { if keyFile := esConfig.KeyFile.ValueString(); keyFile != "" { cert, err := tls.LoadX509KeyPair(certFile, keyFile) if err != nil { diags.Append(fwdiags.NewErrorDiagnostic("Unable to read certificate or key file", err.Error())) return nil, diags } tlsClientConfig := config.ensureTLSClientConfig() tlsClientConfig.Certificates = []tls.Certificate{cert} } else { diags.Append(fwdiags.NewErrorDiagnostic("Unable to read key file", "Path to key file has not been configured or is empty")) return nil, diags } } if certData := esConfig.CertData.ValueString(); certData != "" { if keyData := esConfig.KeyData.ValueString(); keyData != "" { cert, err := tls.X509KeyPair([]byte(certData), []byte(keyData)) if err != nil { diags.Append(fwdiags.NewErrorDiagnostic("Unable to parse certificate or key", err.Error())) return nil, diags } tlsClientConfig := config.ensureTLSClientConfig() tlsClientConfig.Certificates = []tls.Certificate{cert} } else { diags.Append(fwdiags.NewErrorDiagnostic("Unable to parse key", "Key data has not been configured or is empty")) return nil, diags } } if logging.IsDebugOrHigher() { config.config.EnableDebugLogger = true config.config.Logger = &debugLogger{Name: "elasticsearch"} } config = config.withEnvironmentOverrides() return &config, nil } func (c *elasticsearchConfig) ensureTLSClientConfig() *tls.Config { if c.config.Transport == nil { c.config.Transport = http.DefaultTransport.(*http.Transport) } if c.config.Transport.(*http.Transport).TLSClientConfig == nil { c.config.Transport.(*http.Transport).TLSClientConfig = &tls.Config{} } return c.config.Transport.(*http.Transport).TLSClientConfig } func (c elasticsearchConfig) withEnvironmentOverrides() elasticsearchConfig { if endpointsCSV, ok := os.LookupEnv("ELASTICSEARCH_ENDPOINTS"); ok { endpoints := make([]string, 0) for _, e := range strings.Split(endpointsCSV, ",") { endpoints = append(endpoints, strings.TrimSpace(e)) } c.config.Addresses = endpoints } if insecure, ok := os.LookupEnv("ELASTICSEARCH_INSECURE"); ok { if insecureValue, err := strconv.ParseBool(insecure); err == nil { tlsClientConfig := c.ensureTLSClientConfig() tlsClientConfig.InsecureSkipVerify = insecureValue } } if bearerToken := os.Getenv("ELASTICSEARCH_BEARER_TOKEN"); bearerToken != "" { c.bearerToken = bearerToken } if esClientAuthentication := os.Getenv("ELASTICSEARCH_ES_CLIENT_AUTHENTICATION"); esClientAuthentication != "" { c.esClientAuthentication = esClientAuthentication } return c } func (c elasticsearchConfig) toElasticsearchConfiguration() elasticsearch.Config { if c.bearerToken != "" { c.config.Header.Set("Authorization", fmt.Sprintf("Bearer %s", c.bearerToken)) } if c.esClientAuthentication != "" { c.config.Header.Set("ES-Client-Authentication", fmt.Sprintf("SharedSecret %s", c.esClientAuthentication)) } return c.config }