Skip to content

Commit

Permalink
Moving client creation to resource to allow for provider interpolation (
Browse files Browse the repository at this point in the history
#119)

* Moving client creation to allow for provider interpolation

* mod tidy

* Few test fixes

* Correcting interface usage in tests

* Fixing Tests

* Missing clients in methods

* add changelog

Co-authored-by: Phillip Baker <[email protected]>
  • Loading branch information
john-delivuk-rl and phillbaker authored Dec 15, 2020
1 parent 068a12c commit 5302ba1
Show file tree
Hide file tree
Showing 43 changed files with 897 additions and 313 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
- Gracefully handle the case where `elasticsearch_index_template` objects exist in the terraform state but not in the ES domain (e.g. because they were manually deleted.)
- Create index `aliases` and `mappings` even if no settings are set.
- Bump aws client to v1.35.33.
- Allow provider variable interpolation by deferring client instanation, `providerConfigure` only returns a configuration struct.

### Added
-
Expand Down
9 changes: 6 additions & 3 deletions es/data_source_elasticsearch_host.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,12 @@ func dataSourceElasticsearchHostRead(d *schema.ResourceData, m interface{}) erro
// The upstream elastic client does not export the property for the urls
// it's using. Presumably the URLS would be available where the client is
// intantiated, but in terraform, that's not always practicable.

var err error
switch client := m.(type) {
esClient, err := getClient(m.(*ProviderConf))
if err != nil {
return err
}
switch client := esClient.(type) {
case *elastic7.Client:
urls := reflect.ValueOf(client).Elem().FieldByName("urls")
if urls.Len() > 0 {
Expand All @@ -46,7 +49,7 @@ func dataSourceElasticsearchHostRead(d *schema.ResourceData, m interface{}) erro
d.SetId(urls.Index(0).String())
}
default:
client = m.(*elastic5.Client)
client = esClient.(*elastic5.Client)

urls := reflect.ValueOf(client).Elem().FieldByName("urls")
if urls.Len() > 0 {
Expand Down
6 changes: 5 additions & 1 deletion es/data_source_elasticsearch_opendistro_destination.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,11 @@ func dataSourceElasticsearchOpenDistroDestinationRead(d *schema.ResourceData, m
var id string
var body *json.RawMessage
var err error
switch client := m.(type) {
esClient, err := getClient(m.(*ProviderConf))
if err != nil {
return err
}
switch client := esClient.(type) {
case *elastic7.Client:
id, body, err = elastic7Search(client, DESTINATION_INDEX, destinationName)
case *elastic6.Client:
Expand Down
6 changes: 5 additions & 1 deletion es/data_source_elasticsearch_opendistro_destination_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,12 @@ func TestAccElasticsearchDataSourceDestination_basic(t *testing.T) {
t.Skipf("err: %s", err)
}
meta := provider.Meta()
esClient, err := getClient(meta.(*ProviderConf))
if err != nil {
t.Skipf("err: %s", err)
}
var allowed bool
switch meta.(type) {
switch esClient.(type) {
case *elastic5.Client:
allowed = false
default:
Expand Down
206 changes: 115 additions & 91 deletions es/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,27 @@ import (

var awsUrlRegexp = regexp.MustCompile(`([a-z0-9-]+).es.amazonaws.com$`)

type ProviderConf struct {
rawUrl string
insecure bool
sniffing bool
healthchecking bool
cacertFile string
username string
password string
parsedUrl *url.URL
signAWSRequests bool
esVersion string
awsRegion string
awsAssumeRoleArn string
awsAccessKeyId string
awsSecretAccessKey string
awsSessionToken string
awsProfile string
certPemPath string
keyPemPath string
}

func Provider() terraform.ResourceProvider {
return &schema.Provider{
Schema: map[string]*schema.Schema{
Expand Down Expand Up @@ -180,42 +201,57 @@ func Provider() terraform.ResourceProvider {

func providerConfigure(d *schema.ResourceData) (interface{}, error) {
rawUrl := d.Get("url").(string)
insecure := d.Get("insecure").(bool)
sniffing := d.Get("sniff").(bool)
healthchecking := d.Get("healthcheck").(bool)
cacertFile := d.Get("cacert_file").(string)
username := d.Get("username").(string)
password := d.Get("password").(string)
parsedUrl, err := url.Parse(rawUrl)
signAWSRequests := d.Get("sign_aws_requests").(bool)
esVersion := d.Get("elasticsearch_version").(string)
if err != nil {
return nil, err
}

return &ProviderConf{
rawUrl: rawUrl,
insecure: d.Get("insecure").(bool),
sniffing: d.Get("sniff").(bool),
healthchecking: d.Get("healthcheck").(bool),
cacertFile: d.Get("cacert_file").(string),
username: d.Get("username").(string),
password: d.Get("password").(string),
parsedUrl: parsedUrl,
signAWSRequests: d.Get("sign_aws_requests").(bool),
esVersion: d.Get("elasticsearch_version").(string),
awsRegion: d.Get("aws_region").(string),

awsAssumeRoleArn: d.Get("aws_assume_role_arn").(string),
awsAccessKeyId: d.Get("aws_access_key").(string),
awsSecretAccessKey: d.Get("aws_secret_key").(string),
awsSessionToken: d.Get("aws_token").(string),
awsProfile: d.Get("aws_profile").(string),
certPemPath: d.Get("client_cert_path").(string),
keyPemPath: d.Get("client_key_path").(string),
}, nil
}
func getClient(conf *ProviderConf) (interface{}, error) {
opts := []elastic7.ClientOptionFunc{
elastic7.SetURL(rawUrl),
elastic7.SetScheme(parsedUrl.Scheme),
elastic7.SetSniff(sniffing),
elastic7.SetHealthcheck(healthchecking),
elastic7.SetURL(conf.rawUrl),
elastic7.SetScheme(conf.parsedUrl.Scheme),
elastic7.SetSniff(conf.sniffing),
elastic7.SetHealthcheck(conf.healthchecking),
}

if parsedUrl.User.Username() != "" {
p, _ := parsedUrl.User.Password()
opts = append(opts, elastic7.SetBasicAuth(parsedUrl.User.Username(), p))
if conf.parsedUrl.User.Username() != "" {
p, _ := conf.parsedUrl.User.Password()
opts = append(opts, elastic7.SetBasicAuth(conf.parsedUrl.User.Username(), p))
}
if username != "" && password != "" {
opts = append(opts, elastic7.SetBasicAuth(username, password))
if conf.username != "" && conf.password != "" {
opts = append(opts, elastic7.SetBasicAuth(conf.username, conf.password))
}

if m := awsUrlRegexp.FindStringSubmatch(parsedUrl.Hostname()); m != nil && signAWSRequests {
if m := awsUrlRegexp.FindStringSubmatch(conf.parsedUrl.Hostname()); m != nil && conf.signAWSRequests {
log.Printf("[INFO] Using AWS: %+v", m[1])
opts = append(opts, elastic7.SetHttpClient(awsHttpClient(m[1], d)), elastic7.SetSniff(false))
} else if awsRegion := d.Get("aws_region").(string); awsRegion != "" && signAWSRequests {
opts = append(opts, elastic7.SetHttpClient(awsHttpClient(m[1], conf)), elastic7.SetSniff(false))
} else if awsRegion := conf.awsRegion; conf.awsRegion != "" && conf.signAWSRequests {
log.Printf("[INFO] Using AWS: %+v", awsRegion)
opts = append(opts, elastic7.SetHttpClient(awsHttpClient(awsRegion, d)), elastic7.SetSniff(false))
} else if insecure || cacertFile != "" {
opts = append(opts, elastic7.SetHttpClient(tlsHttpClient(d)), elastic7.SetSniff(false))
opts = append(opts, elastic7.SetHttpClient(awsHttpClient(awsRegion, conf)), elastic7.SetSniff(false))
} else if conf.insecure || conf.cacertFile != "" {
opts = append(opts, elastic7.SetHttpClient(tlsHttpClient(conf)), elastic7.SetSniff(false))
}

var relevantClient interface{}
Expand All @@ -226,75 +262,75 @@ func providerConfigure(d *schema.ResourceData) (interface{}, error) {
relevantClient = client

// Use the v7 client to ping the cluster to determine the version if one was not provided
if esVersion == "" {
log.Printf("[INFO] Pinging url to determine version %+v", rawUrl)
info, _, err := client.Ping(rawUrl).Do(context.TODO())
if conf.esVersion == "" {
log.Printf("[INFO] Pinging url to determine version %+v", conf.rawUrl)
info, _, err := client.Ping(conf.rawUrl).Do(context.TODO())
if err != nil {
return nil, err
}
esVersion = info.Version.Number
conf.esVersion = info.Version.Number
}

if esVersion < "7.0.0" && esVersion >= "6.0.0" {
if conf.esVersion < "7.0.0" && conf.esVersion >= "6.0.0" {
log.Printf("[INFO] Using ES 6")
opts := []elastic6.ClientOptionFunc{
elastic6.SetURL(rawUrl),
elastic6.SetScheme(parsedUrl.Scheme),
elastic6.SetSniff(sniffing),
elastic6.SetHealthcheck(healthchecking),
elastic6.SetURL(conf.rawUrl),
elastic6.SetScheme(conf.parsedUrl.Scheme),
elastic6.SetSniff(conf.sniffing),
elastic6.SetHealthcheck(conf.healthchecking),
}

if parsedUrl.User.Username() != "" {
p, _ := parsedUrl.User.Password()
opts = append(opts, elastic6.SetBasicAuth(parsedUrl.User.Username(), p))
if conf.parsedUrl.User.Username() != "" {
p, _ := conf.parsedUrl.User.Password()
opts = append(opts, elastic6.SetBasicAuth(conf.parsedUrl.User.Username(), p))
}
if username != "" && password != "" {
opts = append(opts, elastic6.SetBasicAuth(username, password))
if conf.username != "" && conf.password != "" {
opts = append(opts, elastic6.SetBasicAuth(conf.username, conf.password))
}

if m := awsUrlRegexp.FindStringSubmatch(parsedUrl.Hostname()); m != nil && signAWSRequests {
if m := awsUrlRegexp.FindStringSubmatch(conf.parsedUrl.Hostname()); m != nil && conf.signAWSRequests {
log.Printf("[INFO] Using AWS: %+v", m[1])
opts = append(opts, elastic6.SetHttpClient(awsHttpClient(m[1], d)), elastic6.SetSniff(false))
} else if awsRegion := d.Get("aws_region").(string); awsRegion != "" && signAWSRequests {
log.Printf("[INFO] Using AWS: %+v", awsRegion)
opts = append(opts, elastic6.SetHttpClient(awsHttpClient(awsRegion, d)), elastic6.SetSniff(false))
} else if insecure || cacertFile != "" {
opts = append(opts, elastic6.SetHttpClient(tlsHttpClient(d)), elastic6.SetSniff(false))
opts = append(opts, elastic6.SetHttpClient(awsHttpClient(m[1], conf)), elastic6.SetSniff(false))
} else if awsRegion := conf.awsRegion; conf.awsRegion != "" && conf.signAWSRequests {
log.Printf("[INFO] Using AWS: %+v", conf.awsRegion)
opts = append(opts, elastic6.SetHttpClient(awsHttpClient(awsRegion, conf)), elastic6.SetSniff(false))
} else if conf.insecure || conf.cacertFile != "" {
opts = append(opts, elastic6.SetHttpClient(tlsHttpClient(conf)), elastic6.SetSniff(false))
}
relevantClient, err = elastic6.NewClient(opts...)
if err != nil {
return nil, err
}
} else if esVersion < "6.0.0" && esVersion >= "5.0.0" {
} else if conf.esVersion < "6.0.0" && conf.esVersion >= "5.0.0" {
log.Printf("[INFO] Using ES 5")
opts := []elastic5.ClientOptionFunc{
elastic5.SetURL(rawUrl),
elastic5.SetScheme(parsedUrl.Scheme),
elastic5.SetSniff(sniffing),
elastic5.SetHealthcheck(healthchecking),
elastic5.SetURL(conf.rawUrl),
elastic5.SetScheme(conf.parsedUrl.Scheme),
elastic5.SetSniff(conf.sniffing),
elastic5.SetHealthcheck(conf.healthchecking),
}

if parsedUrl.User.Username() != "" {
p, _ := parsedUrl.User.Password()
opts = append(opts, elastic5.SetBasicAuth(parsedUrl.User.Username(), p))
if conf.parsedUrl.User.Username() != "" {
p, _ := conf.parsedUrl.User.Password()
opts = append(opts, elastic5.SetBasicAuth(conf.parsedUrl.User.Username(), p))
}
if username != "" && password != "" {
opts = append(opts, elastic5.SetBasicAuth(username, password))
if conf.username != "" && conf.password != "" {
opts = append(opts, elastic5.SetBasicAuth(conf.username, conf.password))
}

if m := awsUrlRegexp.FindStringSubmatch(parsedUrl.Hostname()); m != nil && signAWSRequests {
opts = append(opts, elastic5.SetHttpClient(awsHttpClient(m[1], d)), elastic5.SetSniff(false))
} else if awsRegion := d.Get("aws_region").(string); awsRegion != "" && signAWSRequests {
log.Printf("[INFO] Using AWS: %+v", awsRegion)
opts = append(opts, elastic5.SetHttpClient(awsHttpClient(awsRegion, d)), elastic5.SetSniff(false))
} else if insecure || cacertFile != "" {
opts = append(opts, elastic5.SetHttpClient(tlsHttpClient(d)), elastic5.SetSniff(false))
if m := awsUrlRegexp.FindStringSubmatch(conf.parsedUrl.Hostname()); m != nil && conf.signAWSRequests {
opts = append(opts, elastic5.SetHttpClient(awsHttpClient(m[1], conf)), elastic5.SetSniff(false))
} else if awsRegion := conf.awsRegion; conf.awsRegion != "" && conf.signAWSRequests {
log.Printf("[INFO] Using AWS: %+v", conf.awsRegion)
opts = append(opts, elastic5.SetHttpClient(awsHttpClient(awsRegion, conf)), elastic5.SetSniff(false))
} else if conf.insecure || conf.cacertFile != "" {
opts = append(opts, elastic5.SetHttpClient(tlsHttpClient(conf)), elastic5.SetSniff(false))
}
relevantClient, err = elastic5.NewClient(opts...)
if err != nil {
return nil, err
}
} else if esVersion < "5.0.0" {
} else if conf.esVersion < "5.0.0" {
return nil, errors.New("ElasticSearch is older than 5.0.0!")
}

Expand All @@ -317,15 +353,7 @@ func assumeRoleCredentials(region, roleARN, profile string) *awscredentials.Cred
return awscredentials.NewChainCredentials([]awscredentials.Provider{assumeRoleProvider})
}

func awsSession(region string, d *schema.ResourceData) *awssession.Session {
insecure := d.Get("insecure").(bool)

awsAssumeRoleArn := d.Get("aws_assume_role_arn").(string)
awsAccessKeyId := d.Get("aws_access_key").(string)
awsSecretAccessKey := d.Get("aws_secret_key").(string)
awsSessionToken := d.Get("aws_token").(string)
awsProfile := d.Get("aws_profile").(string)

func awsSession(region string, conf *ProviderConf) *awssession.Session {
sessOpts := awssession.Options{
Config: aws.Config{
Region: aws.String(region),
Expand All @@ -337,16 +365,16 @@ func awsSession(region string, d *schema.ResourceData) *awssession.Session {
// 4. let the default credentials provider figure out the rest (env, ec2, etc..)
//
// note: if #1 is chosen, then no further providers will be tested, since we've overridden the credentials with just a static provider
if awsAccessKeyId != "" {
sessOpts.Config.Credentials = awscredentials.NewStaticCredentials(awsAccessKeyId, awsSecretAccessKey, awsSessionToken)
} else if awsAssumeRoleArn != "" {
sessOpts.Config.Credentials = assumeRoleCredentials(region, awsAssumeRoleArn, awsProfile)
} else if awsProfile != "" {
sessOpts.Profile = awsProfile
if conf.awsAccessKeyId != "" {
sessOpts.Config.Credentials = awscredentials.NewStaticCredentials(conf.awsAccessKeyId, conf.awsSecretAccessKey, conf.awsSessionToken)
} else if conf.awsAssumeRoleArn != "" {
sessOpts.Config.Credentials = assumeRoleCredentials(region, conf.awsAssumeRoleArn, conf.awsProfile)
} else if conf.awsProfile != "" {
sessOpts.Profile = conf.awsProfile
}

// If configured as insecure, turn off SSL verification
if insecure {
if conf.insecure {
client := &http.Client{Transport: &http.Transport{
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
}}
Expand All @@ -356,27 +384,23 @@ func awsSession(region string, d *schema.ResourceData) *awssession.Session {
return awssession.Must(awssession.NewSessionWithOptions(sessOpts))
}

func awsHttpClient(region string, d *schema.ResourceData) *http.Client {
signer := awssigv4.NewSigner(awsSession(region, d).Config.Credentials)
func awsHttpClient(region string, conf *ProviderConf) *http.Client {
signer := awssigv4.NewSigner(awsSession(region, conf).Config.Credentials)
client, _ := aws_signing_client.New(signer, nil, "es", region)

return client
}

func tlsHttpClient(d *schema.ResourceData) *http.Client {
insecure := d.Get("insecure").(bool)
cacertFile := d.Get("cacert_file").(string)
certPemPath := d.Get("client_cert_path").(string)
keyPemPath := d.Get("client_key_path").(string)
func tlsHttpClient(conf *ProviderConf) *http.Client {

// Configure TLS/SSL
tlsConfig := &tls.Config{}
if certPemPath != "" && keyPemPath != "" {
certPem, _, err := pathorcontents.Read(certPemPath)
if conf.certPemPath != "" && conf.keyPemPath != "" {
certPem, _, err := pathorcontents.Read(conf.certPemPath)
if err != nil {
log.Fatal(err)
}
keyPem, _, err := pathorcontents.Read(keyPemPath)
keyPem, _, err := pathorcontents.Read(conf.keyPemPath)
if err != nil {
log.Fatal(err)
}
Expand All @@ -388,16 +412,16 @@ func tlsHttpClient(d *schema.ResourceData) *http.Client {
}

// If a cacertFile has been specified, use that for cert validation
if cacertFile != "" {
caCert, _, _ := pathorcontents.Read(cacertFile)
if conf.cacertFile != "" {
caCert, _, _ := pathorcontents.Read(conf.cacertFile)

caCertPool := x509.NewCertPool()
caCertPool.AppendCertsFromPEM([]byte(caCert))
tlsConfig.RootCAs = caCertPool
}

// If configured as insecure, turn off SSL verification
if insecure {
if conf.insecure {
tlsConfig.InsecureSkipVerify = true
}

Expand Down
Loading

0 comments on commit 5302ba1

Please sign in to comment.