diff --git a/.travis.yml b/.travis.yml index 521e5f30..35a5eb0d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -29,6 +29,10 @@ install: script: - export GO111MODULE=on - wget -q --waitretry=1 --retry-connrefused -T 10 -O - $ELASTICSEARCH_URL + # Opendistro lazily initializes it's indexes, warm it up here :| + # https://github.com/opendistro-for-elasticsearch/alerting/issues/60 + - | + if [ -n "$ES_OPENDISTRO_IMAGE" ]; then curl -X POST -H 'Content-type: application/json' -d '{"name":"_warmup","type":"slack","slack":{"url": "http://www.example.com"}}' 127.0.0.1:9220/_opendistro/_alerting/destinations; fi - TF_ACC=1 go test -v -cover before_deploy: - export GO111MODULE=on diff --git a/data_source_elasticsearch_destination.go b/data_source_elasticsearch_destination.go new file mode 100644 index 00000000..169164f4 --- /dev/null +++ b/data_source_elasticsearch_destination.go @@ -0,0 +1,107 @@ +package main + +import ( + "context" + "encoding/json" + "errors" + "fmt" + + "github.com/hashicorp/terraform/helper/schema" + + elastic7 "github.com/olivere/elastic/v7" + elastic6 "gopkg.in/olivere/elastic.v6" +) + +const DESTINATION_NAME_FIELD = "destination.name.keyword" + +func dataSourceElasticsearchDestination() *schema.Resource { + return &schema.Resource{ + Read: dataSourceElasticsearchDestinationRead, + + Schema: map[string]*schema.Schema{ + "name": &schema.Schema{ + Type: schema.TypeString, + Required: true, + }, + "body": &schema.Schema{ + Type: schema.TypeMap, + Computed: true, + }, + }, + } +} + +func dataSourceElasticsearchDestinationRead(d *schema.ResourceData, m interface{}) error { + destinationName := d.Get("name").(string) + + response := new(destinationResponse) + + // See https://github.com/opendistro-for-elasticsearch/alerting/issues/70, no tags or API endpoint for searching destination + var id string + var body *json.RawMessage + var err error + switch m.(type) { + case *elastic7.Client: + client := m.(*elastic7.Client) + id, body, err = elastic7Search(client, DESTINATION_INDEX, destinationName) + case *elastic6.Client: + client := m.(*elastic6.Client) + id, body, err = elastic6Search(client, DESTINATION_INDEX, destinationName) + default: + err = errors.New("destination resource not implemented prior to Elastic v6") + } + + if err != nil { + return err + } else if id == "" { + // short circuit + return nil + } + + if err := json.Unmarshal(*body, response); err != nil { + return fmt.Errorf("error unmarshalling destination body: %+v: %+v", err, body) + } + + d.SetId(id) + d.Set("body", response.Destination.(map[string]interface{})) + + return err +} + +func elastic7Search(client *elastic7.Client, index string, name string) (string, *json.RawMessage, error) { + termQuery := elastic7.NewTermQuery(DESTINATION_NAME_FIELD, name) + result, err := client.Search(). + Index(index). + Query(termQuery). + Do(context.TODO()) + + if err != nil { + return "", nil, err + } + if result.TotalHits() == 1 { + return result.Hits.Hits[0].Id, &result.Hits.Hits[0].Source, nil + } else if result.TotalHits() < 1 { + return "", nil, err + } else { + return "", nil, fmt.Errorf("1 result expected, found %d.", result.TotalHits()) + } +} + +func elastic6Search(client *elastic6.Client, index string, name string) (string, *json.RawMessage, error) { + termQuery := elastic6.NewTermQuery(DESTINATION_NAME_FIELD, name) + result, err := client.Search(). + Index(index). + Query(termQuery). + Do(context.TODO()) + + if err != nil { + return "", nil, err + } + if result.TotalHits() == 1 { + return result.Hits.Hits[0].Id, result.Hits.Hits[0].Source, nil + } else if result.TotalHits() < 1 { + return "", nil, err + } else { + return "", nil, fmt.Errorf("1 result expected, found %d.", result.TotalHits()) + } +} diff --git a/data_source_elasticsearch_destination_test.go b/data_source_elasticsearch_destination_test.go new file mode 100644 index 00000000..823d0f64 --- /dev/null +++ b/data_source_elasticsearch_destination_test.go @@ -0,0 +1,67 @@ +package main + +import ( + "testing" + + elastic7 "github.com/olivere/elastic/v7" + elastic5 "gopkg.in/olivere/elastic.v5" + + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/helper/schema" + "github.com/hashicorp/terraform/terraform" +) + +func TestAccElasticsearchDataSourceDestination_basic(t *testing.T) { + provider := Provider().(*schema.Provider) + err := provider.Configure(&terraform.ResourceConfig{}) + if err != nil { + t.Skipf("err: %s", err) + } + meta := provider.Meta() + var allowed bool + switch meta.(type) { + case *elastic7.Client: + allowed = false + case *elastic5.Client: + allowed = false + default: + allowed = true + } + + resource.Test(t, resource.TestCase{ + PreCheck: func() { + testAccPreCheck(t) + if !allowed { + t.Skip("Destinations only supported on ES 6, https://github.com/opendistro-for-elasticsearch/alerting/issues/66") + } + }, + Providers: testAccOpendistroProviders, + Steps: []resource.TestStep{ + { + Config: testAccElasticsearchDataSourceDestination, + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttrSet("data.elasticsearch_destination.test", "id"), + ), + }, + }, + }) +} + +var testAccElasticsearchDataSourceDestination = ` +resource "elasticsearch_destination" "test" { + body = <