From 79916136e602daf18f3dd49fd21317f7fb5e4c5a Mon Sep 17 00:00:00 2001 From: Nick Clark Date: Tue, 16 Apr 2024 13:32:14 +1000 Subject: [PATCH 01/23] Add alerting connector data source --- .../data-source.tf | 16 +++ internal/clients/kibana/connector.go | 61 ++++++++ internal/clients/kibana/connector_test.go | 96 +++++++++++++ internal/kibana/connector.go | 131 ++++++++++-------- internal/kibana/connector_data_source.go | 25 ++++ provider/provider.go | 3 +- 6 files changed, 274 insertions(+), 58 deletions(-) create mode 100644 examples/data-sources/elasticstack_kibana_action_connector/data-source.tf create mode 100644 internal/kibana/connector_data_source.go diff --git a/examples/data-sources/elasticstack_kibana_action_connector/data-source.tf b/examples/data-sources/elasticstack_kibana_action_connector/data-source.tf new file mode 100644 index 000000000..20a1b2bda --- /dev/null +++ b/examples/data-sources/elasticstack_kibana_action_connector/data-source.tf @@ -0,0 +1,16 @@ + +provider "elasticstack" { + elasticsearch {} + kibana {} + +} + +data "elasticstack_kibana_action_connector" "example" { + name = "myslackconnector" + space_id = "default" + connector_type = ".slack" +} + +output "connector_id" { + value = data.elasticstack_kibana_action_connector.example.connector_id +} diff --git a/internal/clients/kibana/connector.go b/internal/clients/kibana/connector.go index 6ee9b104f..0f6732576 100644 --- a/internal/clients/kibana/connector.go +++ b/internal/clients/kibana/connector.go @@ -141,6 +141,67 @@ func GetConnector(ctx context.Context, apiClient *clients.ApiClient, connectorID return connector, nil } +func GetConnectorByName(ctx context.Context, apiClient *clients.ApiClient, connectorName, spaceID string) (*models.KibanaActionConnector, diag.Diagnostics) { + client, err := apiClient.GetKibanaConnectorsClient(ctx) + if err != nil { + return nil, diag.FromErr(err) + } + + httpResp, err := client.GetConnectors(ctx, spaceID) + + if err != nil { + return nil, diag.Errorf("unable to get connectors: [%v]", err) + } + + defer httpResp.Body.Close() + + resp, err := connectors.ParseGetConnectorsResponse(httpResp) + if err != nil { + return nil, diag.Errorf("unable to parse connectors get response: [%v]", err) + } + + if resp.JSON401 != nil { + return nil, diag.Errorf("%s: %s", *resp.JSON401.Error, *resp.JSON401.Message) + } + + if resp.JSON200 == nil { + return nil, diag.Errorf("%s: %s", resp.Status(), string(resp.Body)) + } + + foundConnectors := []*models.KibanaActionConnector{} + for _, connector := range *resp.JSON200 { + //this marshaling and unmarshaling business allows us to create a type with unexported fields. + bytes, err := json.Marshal(connector) + if err != nil { + return nil, diag.Errorf("cannot marshal connector: %v", err) + } + + var respProps connectors.ConnectorResponseProperties + err = json.Unmarshal(bytes, &respProps) + if err != nil { + return nil, diag.Errorf("cannot unmarshal connector: %v", err) + } + + c, err := connectorResponseToModel(spaceID, respProps) + if err != nil { + return nil, diag.Errorf("unable to convert response to model: %v", err) + } + if c.Name == connectorName { + foundConnectors = append(foundConnectors, c) + } + } + + if len(foundConnectors) == 1 { + return foundConnectors[0], nil + } + + if len(foundConnectors) > 1 { + return nil, diag.Errorf("multiple connectors with name [%s/%s] found while creating elasticstack_kibana_action_connector datasource", spaceID, connectorName) + } + + return nil, diag.Errorf("connector [%s/%s] not found in elasticstack_kibana_action_connector datasource", spaceID, connectorName) +} + func DeleteConnector(ctx context.Context, apiClient *clients.ApiClient, connectorID string, spaceID string) diag.Diagnostics { client, err := apiClient.GetKibanaConnectorsClient(ctx) if err != nil { diff --git a/internal/clients/kibana/connector_test.go b/internal/clients/kibana/connector_test.go index acaec9f9a..657c0bb8c 100644 --- a/internal/clients/kibana/connector_test.go +++ b/internal/clients/kibana/connector_test.go @@ -1,11 +1,16 @@ package kibana import ( + "context" "encoding/json" "fmt" + "net/http" + "net/http/httptest" + "os" "testing" "github.com/elastic/terraform-provider-elasticstack/generated/connectors" + "github.com/elastic/terraform-provider-elasticstack/internal/clients" "github.com/elastic/terraform-provider-elasticstack/internal/models" "github.com/stretchr/testify/require" ) @@ -130,3 +135,94 @@ func Test_connectorResponseToModel(t *testing.T) { }) } } + +func TestGetConnectorByName(t *testing.T) { + const getConnectorsResponse = `[ + { + "id": "c55b6eb0-6bad-11eb-9f3b-611eebc6c3ad", + "connector_type_id": ".index", + "name": "my-connector", + "config": { + "index": "test-index", + "refresh": false, + "executionTimeField": null + }, + "is_preconfigured": false, + "is_deprecated": false, + "is_missing_secrets": false, + "referenced_by_count": 3 + }, + { + "id": "d55b6eb0-6bad-11eb-9f3b-611eebc6c3ad", + "connector_type_id": ".index", + "name": "doubledup-connector", + "config": { + "index": "test-index", + "refresh": false, + "executionTimeField": null + }, + "is_preconfigured": false, + "is_deprecated": false, + "is_missing_secrets": false, + "referenced_by_count": 3 + }, + { + "id": "855b6eb0-6bad-11eb-9f3b-611eebc6c3ad", + "connector_type_id": ".index", + "name": "doubledup-connector", + "config": { + "index": "test-index", + "refresh": false, + "executionTimeField": null + }, + "is_preconfigured": false, + "is_deprecated": false, + "is_missing_secrets": false, + "referenced_by_count": 0 + } + ]` + + var requests []*http.Request + var mockResponses []string + server := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) { + requests = append(requests, req) + + if len(mockResponses) > 0 { + r := []byte(mockResponses[0]) + // t.Logf("Responding with %s", r) + rw.Header().Add("X-Elastic-Product", "Elasticsearch") + rw.Header().Add("Content-Type", "application/json") + rw.WriteHeader(http.StatusOK) + rw.Write(r) + mockResponses = mockResponses[1:] + } else { + t.Fatalf("Unexpected request: %s %s", req.Method, req.URL.Path) + } + })) + defer server.Close() + + mockResponses = append(mockResponses, getConnectorsResponse) + + err := os.Setenv("ELASTICSEARCH_URL", server.URL) + require.NoError(t, err) + err = os.Setenv("KIBANA_ENDPOINT", server.URL) + require.NoError(t, err) + + apiClient, err := clients.NewAcceptanceTestingClient() + require.NoError(t, err) + + connector, diags := GetConnectorByName(context.Background(), apiClient, "my-connector", "default") + require.Nil(t, diags) + require.NotNil(t, connector) + + mockResponses = append(mockResponses, getConnectorsResponse) + failConnector, diags := GetConnectorByName(context.Background(), apiClient, "failwhale", "default") + require.NotNil(t, diags) + require.Nil(t, failConnector) + + mockResponses = append(mockResponses, getConnectorsResponse) + dupConnector, diags := GetConnectorByName(context.Background(), apiClient, "doubledup-connector", "default") + require.NotNil(t, diags) + require.Nil(t, dupConnector) + +} diff --git a/internal/kibana/connector.go b/internal/kibana/connector.go index 1a57b7fb7..43c124a6b 100644 --- a/internal/kibana/connector.go +++ b/internal/kibana/connector.go @@ -12,63 +12,64 @@ import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" ) +var connectorSchema = map[string]*schema.Schema{ + "connector_id": { + Description: "A UUID v1 or v4 to use instead of a randomly generated ID.", + Type: schema.TypeString, + Computed: true, + Optional: true, + ForceNew: true, + }, + "space_id": { + Description: "An identifier for the space. If space_id is not provided, the default space is used.", + Type: schema.TypeString, + Optional: true, + Default: "default", + ForceNew: true, + }, + "name": { + Description: "The name of the connector. While this name does not have to be unique, a distinctive name can help you identify a connector.", + Type: schema.TypeString, + Required: true, + }, + "connector_type_id": { + Description: "The ID of the connector type, e.g. `.index`.", + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + "config": { + Description: "The configuration for the connector. Configuration properties vary depending on the connector type.", + Type: schema.TypeString, + Optional: true, + Computed: true, + ValidateFunc: validation.StringIsJSON, + }, + "secrets": { + Description: "The secrets configuration for the connector. Secrets configuration properties vary depending on the connector type.", + Type: schema.TypeString, + Optional: true, + DiffSuppressFunc: utils.DiffJsonSuppress, + ValidateFunc: validation.StringIsJSON, + }, + "is_deprecated": { + Description: "Indicates whether the connector type is deprecated.", + Type: schema.TypeBool, + Computed: true, + }, + "is_missing_secrets": { + Description: "Indicates whether secrets are missing for the connector.", + Type: schema.TypeBool, + Computed: true, + }, + "is_preconfigured": { + Description: "Indicates whether it is a preconfigured connector.", + Type: schema.TypeBool, + Computed: true, + }, +} + func ResourceActionConnector() *schema.Resource { - apikeySchema := map[string]*schema.Schema{ - "connector_id": { - Description: "A UUID v1 or v4 to use instead of a randomly generated ID.", - Type: schema.TypeString, - Computed: true, - Optional: true, - ForceNew: true, - }, - "space_id": { - Description: "An identifier for the space. If space_id is not provided, the default space is used.", - Type: schema.TypeString, - Optional: true, - Default: "default", - ForceNew: true, - }, - "name": { - Description: "The name of the connector. While this name does not have to be unique, a distinctive name can help you identify a connector.", - Type: schema.TypeString, - Required: true, - }, - "connector_type_id": { - Description: "The ID of the connector type, e.g. `.index`.", - Type: schema.TypeString, - Required: true, - ForceNew: true, - }, - "config": { - Description: "The configuration for the connector. Configuration properties vary depending on the connector type.", - Type: schema.TypeString, - Optional: true, - Computed: true, - ValidateFunc: validation.StringIsJSON, - }, - "secrets": { - Description: "The secrets configuration for the connector. Secrets configuration properties vary depending on the connector type.", - Type: schema.TypeString, - Optional: true, - DiffSuppressFunc: utils.DiffJsonSuppress, - ValidateFunc: validation.StringIsJSON, - }, - "is_deprecated": { - Description: "Indicates whether the connector type is deprecated.", - Type: schema.TypeBool, - Computed: true, - }, - "is_missing_secrets": { - Description: "Indicates whether secrets are missing for the connector.", - Type: schema.TypeBool, - Computed: true, - }, - "is_preconfigured": { - Description: "Indicates whether it is a preconfigured connector.", - Type: schema.TypeBool, - Computed: true, - }, - } return &schema.Resource{ Description: "Creates a Kibana action connector. See https://www.elastic.co/guide/en/kibana/current/action-types.html", @@ -83,7 +84,7 @@ func ResourceActionConnector() *schema.Resource { StateContext: schema.ImportStatePassthroughContext, }, - Schema: apikeySchema, + Schema: connectorSchema, } } @@ -198,6 +199,22 @@ func resourceConnectorRead(ctx context.Context, d *schema.ResourceData, meta int return flattenActionConnector(connector, d) } +func resourceConnectorsRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + client, diags := clients.NewApiClientFromSDKResource(d, meta) + if diags.HasError() { + return diags + } + name := d.Get("name").(string) + spaceId := d.Get("space_id").(string) + + connector, diags := kibana.GetConnectorByName(ctx, client, name, spaceId) + if diags.HasError() { + return diags + } + + return flattenActionConnector(connector, d) +} + func resourceConnectorDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { client, diags := clients.NewApiClientFromSDKResource(d, meta) if diags.HasError() { diff --git a/internal/kibana/connector_data_source.go b/internal/kibana/connector_data_source.go new file mode 100644 index 000000000..cc66476b9 --- /dev/null +++ b/internal/kibana/connector_data_source.go @@ -0,0 +1,25 @@ +package kibana + +import ( + "context" + + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" +) + +func DataSourceConnector() *schema.Resource { + return &schema.Resource{ + Description: "Retrieve a specific connector.", + ReadContext: dataSourceConnectorRead, + Schema: connectorSchema, + } +} + +func dataSourceConnectorRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + connectorName := d.Get("name").(string) + d.Set("name", connectorName) + spaceId := d.Get("space_id").(string) + d.SetId(spaceId) + + return resourceConnectorsRead(ctx, d, meta) +} diff --git a/provider/provider.go b/provider/provider.go index ac41b9cf4..18145367c 100644 --- a/provider/provider.go +++ b/provider/provider.go @@ -79,7 +79,8 @@ func New(version string) *schema.Provider { "elasticstack_elasticsearch_info": cluster.DataSourceClusterInfo(), "elasticstack_elasticsearch_enrich_policy": enrich.DataSourceEnrichPolicy(), - "elasticstack_kibana_security_role": kibana.DataSourceRole(), + "elasticstack_kibana_action_connector": kibana.DataSourceConnector(), + "elasticstack_kibana_security_role": kibana.DataSourceRole(), "elasticstack_fleet_enrollment_tokens": fleet.DataSourceEnrollmentTokens(), "elasticstack_fleet_integration": fleet.DataSourceIntegration(), From 6266939a5393f7d5ea02003d13c1468af0853b43 Mon Sep 17 00:00:00 2001 From: Nick Clark Date: Tue, 16 Apr 2024 16:08:56 +1000 Subject: [PATCH 02/23] wee fixes --- internal/clients/kibana/connector_test.go | 3 ++- internal/kibana/connector_data_source.go | 9 +++++++-- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/internal/clients/kibana/connector_test.go b/internal/clients/kibana/connector_test.go index 657c0bb8c..63a9129e8 100644 --- a/internal/clients/kibana/connector_test.go +++ b/internal/clients/kibana/connector_test.go @@ -193,7 +193,8 @@ func TestGetConnectorByName(t *testing.T) { rw.Header().Add("X-Elastic-Product", "Elasticsearch") rw.Header().Add("Content-Type", "application/json") rw.WriteHeader(http.StatusOK) - rw.Write(r) + _, err := rw.Write(r) + require.NoError(t, err) mockResponses = mockResponses[1:] } else { t.Fatalf("Unexpected request: %s %s", req.Method, req.URL.Path) diff --git a/internal/kibana/connector_data_source.go b/internal/kibana/connector_data_source.go index cc66476b9..6e16691b9 100644 --- a/internal/kibana/connector_data_source.go +++ b/internal/kibana/connector_data_source.go @@ -17,9 +17,14 @@ func DataSourceConnector() *schema.Resource { func dataSourceConnectorRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { connectorName := d.Get("name").(string) - d.Set("name", connectorName) + if err := d.Set("name", connectorName); err != nil { + return diag.FromErr(err) + } + spaceId := d.Get("space_id").(string) - d.SetId(spaceId) + if err := d.Set("space_id", spaceId); err != nil { + return diag.FromErr(err) + } return resourceConnectorsRead(ctx, d, meta) } From 9ab7916445d5455dfe85dbce9ca8eeff3807a800 Mon Sep 17 00:00:00 2001 From: Nick Clark Date: Tue, 16 Apr 2024 16:09:40 +1000 Subject: [PATCH 03/23] add docs --- docs/data-sources/kibana_action_connector.md | 53 ++++++++++++++++++++ 1 file changed, 53 insertions(+) create mode 100644 docs/data-sources/kibana_action_connector.md diff --git a/docs/data-sources/kibana_action_connector.md b/docs/data-sources/kibana_action_connector.md new file mode 100644 index 000000000..f4ada69bb --- /dev/null +++ b/docs/data-sources/kibana_action_connector.md @@ -0,0 +1,53 @@ +--- +# generated by https://github.com/hashicorp/terraform-plugin-docs +page_title: "elasticstack_kibana_action_connector Data Source - terraform-provider-elasticstack" +subcategory: "" +description: |- + Retrieve a specific connector. +--- + +# elasticstack_kibana_action_connector (Data Source) + +Retrieve a specific connector. + +## Example Usage + +```terraform +provider "elasticstack" { + elasticsearch {} + kibana {} + +} + +data "elasticstack_kibana_action_connector" "example" { + name = "myslackconnector" + space_id = "default" + connector_type = ".slack" +} + +output "connector_id" { + value = data.elasticstack_kibana_action_connector.example.connector_id +} +``` + + +## Schema + +### Required + +- `connector_type_id` (String) The ID of the connector type, e.g. `.index`. +- `name` (String) The name of the connector. While this name does not have to be unique, a distinctive name can help you identify a connector. + +### Optional + +- `config` (String) The configuration for the connector. Configuration properties vary depending on the connector type. +- `connector_id` (String) A UUID v1 or v4 to use instead of a randomly generated ID. +- `secrets` (String) The secrets configuration for the connector. Secrets configuration properties vary depending on the connector type. +- `space_id` (String) An identifier for the space. If space_id is not provided, the default space is used. + +### Read-Only + +- `id` (String) The ID of this resource. +- `is_deprecated` (Boolean) Indicates whether the connector type is deprecated. +- `is_missing_secrets` (Boolean) Indicates whether secrets are missing for the connector. +- `is_preconfigured` (Boolean) Indicates whether it is a preconfigured connector. From 47e4866c06d74b724236b0569845de7379f9ced0 Mon Sep 17 00:00:00 2001 From: Nick Clark Date: Tue, 16 Apr 2024 16:11:52 +1000 Subject: [PATCH 04/23] docs detail --- docs/data-sources/kibana_action_connector.md | 4 ++-- internal/kibana/connector_data_source.go | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/data-sources/kibana_action_connector.md b/docs/data-sources/kibana_action_connector.md index f4ada69bb..ddc88f010 100644 --- a/docs/data-sources/kibana_action_connector.md +++ b/docs/data-sources/kibana_action_connector.md @@ -3,12 +3,12 @@ page_title: "elasticstack_kibana_action_connector Data Source - terraform-provider-elasticstack" subcategory: "" description: |- - Retrieve a specific connector. + Search for a connector by name, space id, and type. Note, that this data source will fail if more than one connector shares the same name. --- # elasticstack_kibana_action_connector (Data Source) -Retrieve a specific connector. +Search for a connector by name, space id, and type. Note, that this data source will fail if more than one connector shares the same name. ## Example Usage diff --git a/internal/kibana/connector_data_source.go b/internal/kibana/connector_data_source.go index 6e16691b9..7ff2de46b 100644 --- a/internal/kibana/connector_data_source.go +++ b/internal/kibana/connector_data_source.go @@ -9,7 +9,7 @@ import ( func DataSourceConnector() *schema.Resource { return &schema.Resource{ - Description: "Retrieve a specific connector.", + Description: "Search for a connector by name, space id, and type. Note, that this data source will fail if more than one connector shares the same name.", ReadContext: dataSourceConnectorRead, Schema: connectorSchema, } From 5d9eedb42b5d5ce76400281f527ad4ad8ef1c60e Mon Sep 17 00:00:00 2001 From: Nick Clark Date: Tue, 16 Apr 2024 16:23:16 +1000 Subject: [PATCH 05/23] Changelog --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8190079cc..fd7ddd2f4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,10 @@ - Prevent a provider panic when an `elasticstack_elasticsearch_template` or `elasticstack_elasticsearch_component_template` includes an empty `template` (`template {}`) block. ([#598](https://github.com/elastic/terraform-provider-elasticstack/pull/598)) - Prevent `elasticstack_kibana_space` to attempt the space recreation if `initials` and `color` are not provided. ([#606](https://github.com/elastic/terraform-provider-elasticstack/pull/606)) +### Added + +- Added datasource for alerting connectors. ([#607](https://github.com/elastic/terraform-provider-elasticstack/pull/607)) + ## [0.11.2] - 2024-03-13 ### Fixed From e8921fd85b5b2e8398400a1f2461a97f88e699c3 Mon Sep 17 00:00:00 2001 From: Nick Clark Date: Wed, 17 Apr 2024 09:16:45 +1000 Subject: [PATCH 06/23] Update internal/clients/kibana/connector_test.go Co-authored-by: Toby Brain --- internal/clients/kibana/connector_test.go | 1 - 1 file changed, 1 deletion(-) diff --git a/internal/clients/kibana/connector_test.go b/internal/clients/kibana/connector_test.go index 63a9129e8..c38bb563d 100644 --- a/internal/clients/kibana/connector_test.go +++ b/internal/clients/kibana/connector_test.go @@ -189,7 +189,6 @@ func TestGetConnectorByName(t *testing.T) { if len(mockResponses) > 0 { r := []byte(mockResponses[0]) - // t.Logf("Responding with %s", r) rw.Header().Add("X-Elastic-Product", "Elasticsearch") rw.Header().Add("Content-Type", "application/json") rw.WriteHeader(http.StatusOK) From fcc52628fdacd4fa800dc8c87b582b2c45b863eb Mon Sep 17 00:00:00 2001 From: Nick Clark Date: Wed, 17 Apr 2024 09:17:37 +1000 Subject: [PATCH 07/23] fix tabs --- internal/clients/kibana/connector_test.go | 24 +++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/internal/clients/kibana/connector_test.go b/internal/clients/kibana/connector_test.go index c38bb563d..83d61901f 100644 --- a/internal/clients/kibana/connector_test.go +++ b/internal/clients/kibana/connector_test.go @@ -139,27 +139,27 @@ func Test_connectorResponseToModel(t *testing.T) { func TestGetConnectorByName(t *testing.T) { const getConnectorsResponse = `[ { - "id": "c55b6eb0-6bad-11eb-9f3b-611eebc6c3ad", - "connector_type_id": ".index", - "name": "my-connector", - "config": { + "id": "c55b6eb0-6bad-11eb-9f3b-611eebc6c3ad", + "connector_type_id": ".index", + "name": "my-connector", + "config": { "index": "test-index", "refresh": false, "executionTimeField": null - }, - "is_preconfigured": false, - "is_deprecated": false, - "is_missing_secrets": false, - "referenced_by_count": 3 + }, + "is_preconfigured": false, + "is_deprecated": false, + "is_missing_secrets": false, + "referenced_by_count": 3 }, { "id": "d55b6eb0-6bad-11eb-9f3b-611eebc6c3ad", "connector_type_id": ".index", "name": "doubledup-connector", "config": { - "index": "test-index", - "refresh": false, - "executionTimeField": null + "index": "test-index", + "refresh": false, + "executionTimeField": null }, "is_preconfigured": false, "is_deprecated": false, From 11597351cb53e548ce45ac4c1067fdd3143cf40e Mon Sep 17 00:00:00 2001 From: Nick Clark Date: Wed, 17 Apr 2024 09:24:22 +1000 Subject: [PATCH 08/23] add action connector template --- .../data-sources/kibana_action_connector.tmpl | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 templates/data-sources/kibana_action_connector.tmpl diff --git a/templates/data-sources/kibana_action_connector.tmpl b/templates/data-sources/kibana_action_connector.tmpl new file mode 100644 index 000000000..ce1432540 --- /dev/null +++ b/templates/data-sources/kibana_action_connector.tmpl @@ -0,0 +1,17 @@ +--- +subcategory: "Kibana" +layout: "" +page_title: "Elasticstack: elasticstack_kibana_action_connector Data Source" +description: |- + Retrieve a specific action connector role. See https://www.elastic.co/guide/en/kibana/current/get-all-connectors-api.html. +--- + +# Data Source: elasticstack_kibana_action_connector + +Use this data source to get information about an existing action connector. + +## Example Usage + +{{ tffile "examples/data-sources/elasticstack_kibana_action_connector/data-source.tf" }} + +{{ .SchemaMarkdown | trimspace }} From aa9da27fda7f95f7b82052dba915736875e6bda8 Mon Sep 17 00:00:00 2001 From: Nick Clark Date: Wed, 17 Apr 2024 20:28:15 +1000 Subject: [PATCH 09/23] add docs --- docs/data-sources/kibana_action_connector | 53 +++++++++++++++++++++++ 1 file changed, 53 insertions(+) create mode 100644 docs/data-sources/kibana_action_connector diff --git a/docs/data-sources/kibana_action_connector b/docs/data-sources/kibana_action_connector new file mode 100644 index 000000000..ba0dda769 --- /dev/null +++ b/docs/data-sources/kibana_action_connector @@ -0,0 +1,53 @@ +--- +subcategory: "Kibana" +layout: "" +page_title: "Elasticstack: elasticstack_kibana_action_connector Data Source" +description: |- + Retrieve a specific action connector role. See https://www.elastic.co/guide/en/kibana/current/get-all-connectors-api.html. +--- + +# Data Source: elasticstack_kibana_action_connector + +Use this data source to get information about an existing action connector. + +## Example Usage + +```terraform +provider "elasticstack" { + elasticsearch {} + kibana {} + +} + +data "elasticstack_kibana_action_connector" "example" { + name = "myslackconnector" + space_id = "default" + connector_type = ".slack" +} + +output "connector_id" { + value = data.elasticstack_kibana_action_connector.example.connector_id +} +``` + + +## Schema + +### Required + +- `connector_type_id` (String) The ID of the connector type, e.g. `.index`. +- `name` (String) The name of the connector. While this name does not have to be unique, a distinctive name can help you identify a connector. + +### Optional + +- `config` (String) The configuration for the connector. Configuration properties vary depending on the connector type. +- `connector_id` (String) A UUID v1 or v4 to use instead of a randomly generated ID. +- `id` (String) The ID of this resource. +- `secrets` (String) The secrets configuration for the connector. Secrets configuration properties vary depending on the connector type. +- `space_id` (String) An identifier for the space. If space_id is not provided, the default space is used. + +### Read-Only + +- `is_deprecated` (Boolean) Indicates whether the connector type is deprecated. +- `is_missing_secrets` (Boolean) Indicates whether secrets are missing for the connector. +- `is_preconfigured` (Boolean) Indicates whether it is a preconfigured connector. From 6ce6cc809d9aea8bf5586869ace9e5608cf1ebba Mon Sep 17 00:00:00 2001 From: Nick Clark Date: Wed, 17 Apr 2024 20:29:00 +1000 Subject: [PATCH 10/23] error message tweaks --- internal/clients/kibana/connector.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/internal/clients/kibana/connector.go b/internal/clients/kibana/connector.go index 0f6732576..75c8fb406 100644 --- a/internal/clients/kibana/connector.go +++ b/internal/clients/kibana/connector.go @@ -196,10 +196,10 @@ func GetConnectorByName(ctx context.Context, apiClient *clients.ApiClient, conne } if len(foundConnectors) > 1 { - return nil, diag.Errorf("multiple connectors with name [%s/%s] found while creating elasticstack_kibana_action_connector datasource", spaceID, connectorName) + return nil, diag.Errorf("error while creating elasticstack_kibana_action_connector datasource: multiple connectors with name [%s/%s] found", spaceID, connectorName) } - return nil, diag.Errorf("connector [%s/%s] not found in elasticstack_kibana_action_connector datasource", spaceID, connectorName) + return nil, diag.Errorf("error while creating elasticstack_kibana_action_connector datasource: connector [%s/%s] not found", spaceID, connectorName) } func DeleteConnector(ctx context.Context, apiClient *clients.ApiClient, connectorID string, spaceID string) diag.Diagnostics { From 0586f8a06dcc998cc3b7351368f7a228ed80ad4a Mon Sep 17 00:00:00 2001 From: Nick Clark Date: Wed, 17 Apr 2024 20:29:50 +1000 Subject: [PATCH 11/23] wip --- .../data-source.tf | 34 +++++- internal/kibana/connector.go | 114 +++++++++--------- internal/kibana/connector_data_source.go | 76 +++++++++--- internal/kibana/connector_data_source_test.go | 52 ++++++++ 4 files changed, 195 insertions(+), 81 deletions(-) create mode 100644 internal/kibana/connector_data_source_test.go diff --git a/examples/data-sources/elasticstack_kibana_action_connector/data-source.tf b/examples/data-sources/elasticstack_kibana_action_connector/data-source.tf index 20a1b2bda..6a4a0d3ef 100644 --- a/examples/data-sources/elasticstack_kibana_action_connector/data-source.tf +++ b/examples/data-sources/elasticstack_kibana_action_connector/data-source.tf @@ -1,16 +1,40 @@ +terraform { + required_providers { + elasticstack = { + source = "elastic/elasticstack" + } + } +} provider "elasticstack" { - elasticsearch {} - kibana {} + elasticsearch { + username = "elastic" + password = "password" + endpoints = ["http://localhost:9200"] + } + kibana { + username = "elastic" + password = "password" + endpoints = ["http://localhost:5601"] + } +} +resource "elasticstack_kibana_action_connector" "slack-connector" { + name = "slack" + connector_type_id = ".slack" + secrets = jsonencode({ + webhookUrl = "https://lol.com" + }) } data "elasticstack_kibana_action_connector" "example" { - name = "myslackconnector" - space_id = "default" - connector_type = ".slack" + name = "myslackconnector" + space_id = "default" + connector_type_id = ".slack" + depends_on = [elasticstack_kibana_action_connector.slack-connector] } output "connector_id" { + # value = elasticstack_kibana_action_connector.slack-connector.connector_id value = data.elasticstack_kibana_action_connector.example.connector_id } diff --git a/internal/kibana/connector.go b/internal/kibana/connector.go index 43c124a6b..3306a5457 100644 --- a/internal/kibana/connector.go +++ b/internal/kibana/connector.go @@ -12,65 +12,63 @@ import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" ) -var connectorSchema = map[string]*schema.Schema{ - "connector_id": { - Description: "A UUID v1 or v4 to use instead of a randomly generated ID.", - Type: schema.TypeString, - Computed: true, - Optional: true, - ForceNew: true, - }, - "space_id": { - Description: "An identifier for the space. If space_id is not provided, the default space is used.", - Type: schema.TypeString, - Optional: true, - Default: "default", - ForceNew: true, - }, - "name": { - Description: "The name of the connector. While this name does not have to be unique, a distinctive name can help you identify a connector.", - Type: schema.TypeString, - Required: true, - }, - "connector_type_id": { - Description: "The ID of the connector type, e.g. `.index`.", - Type: schema.TypeString, - Required: true, - ForceNew: true, - }, - "config": { - Description: "The configuration for the connector. Configuration properties vary depending on the connector type.", - Type: schema.TypeString, - Optional: true, - Computed: true, - ValidateFunc: validation.StringIsJSON, - }, - "secrets": { - Description: "The secrets configuration for the connector. Secrets configuration properties vary depending on the connector type.", - Type: schema.TypeString, - Optional: true, - DiffSuppressFunc: utils.DiffJsonSuppress, - ValidateFunc: validation.StringIsJSON, - }, - "is_deprecated": { - Description: "Indicates whether the connector type is deprecated.", - Type: schema.TypeBool, - Computed: true, - }, - "is_missing_secrets": { - Description: "Indicates whether secrets are missing for the connector.", - Type: schema.TypeBool, - Computed: true, - }, - "is_preconfigured": { - Description: "Indicates whether it is a preconfigured connector.", - Type: schema.TypeBool, - Computed: true, - }, -} - func ResourceActionConnector() *schema.Resource { - + var connectorSchema = map[string]*schema.Schema{ + "connector_id": { + Description: "A UUID v1 or v4 to use instead of a randomly generated ID.", + Type: schema.TypeString, + Computed: true, + Optional: true, + ForceNew: true, + }, + "space_id": { + Description: "An identifier for the space. If space_id is not provided, the default space is used.", + Type: schema.TypeString, + Optional: true, + Default: "default", + ForceNew: true, + }, + "name": { + Description: "The name of the connector. While this name does not have to be unique, a distinctive name can help you identify a connector.", + Type: schema.TypeString, + Required: true, + }, + "connector_type_id": { + Description: "The ID of the connector type, e.g. `.index`.", + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + "config": { + Description: "The configuration for the connector. Configuration properties vary depending on the connector type.", + Type: schema.TypeString, + Optional: true, + Computed: true, + ValidateFunc: validation.StringIsJSON, + }, + "secrets": { + Description: "The secrets configuration for the connector. Secrets configuration properties vary depending on the connector type.", + Type: schema.TypeString, + Optional: true, + DiffSuppressFunc: utils.DiffJsonSuppress, + ValidateFunc: validation.StringIsJSON, + }, + "is_deprecated": { + Description: "Indicates whether the connector type is deprecated.", + Type: schema.TypeBool, + Computed: true, + }, + "is_missing_secrets": { + Description: "Indicates whether secrets are missing for the connector.", + Type: schema.TypeBool, + Computed: true, + }, + "is_preconfigured": { + Description: "Indicates whether it is a preconfigured connector.", + Type: schema.TypeBool, + Computed: true, + }, + } return &schema.Resource{ Description: "Creates a Kibana action connector. See https://www.elastic.co/guide/en/kibana/current/action-types.html", diff --git a/internal/kibana/connector_data_source.go b/internal/kibana/connector_data_source.go index 7ff2de46b..68e23e27c 100644 --- a/internal/kibana/connector_data_source.go +++ b/internal/kibana/connector_data_source.go @@ -1,30 +1,70 @@ package kibana import ( - "context" - - "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/elastic/terraform-provider-elasticstack/internal/utils" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" ) func DataSourceConnector() *schema.Resource { + var connectorSchema = map[string]*schema.Schema{ + "connector_id": { + Description: "A UUID v1 or v4 to use instead of a randomly generated ID.", + Type: schema.TypeString, + Computed: true, + }, + "space_id": { + Description: "An identifier for the space. If space_id is not provided, the default space is used.", + Type: schema.TypeString, + Optional: true, + Default: "default", + ForceNew: true, + }, + "name": { + Description: "The name of the connector. While this name does not have to be unique, a distinctive name can help you identify a connector.", + Type: schema.TypeString, + Required: true, + }, + "connector_type_id": { + Description: "The ID of the connector type, e.g. `.index`.", + Type: schema.TypeString, + Optional: true, + ForceNew: true, + }, + "config": { + Description: "The configuration for the connector. Configuration properties vary depending on the connector type.", + Type: schema.TypeString, + Optional: true, + Computed: true, + ValidateFunc: validation.StringIsJSON, + }, + "secrets": { + Description: "The secrets configuration for the connector. Secrets configuration properties vary depending on the connector type.", + Type: schema.TypeString, + Optional: true, + DiffSuppressFunc: utils.DiffJsonSuppress, + ValidateFunc: validation.StringIsJSON, + }, + "is_deprecated": { + Description: "Indicates whether the connector type is deprecated.", + Type: schema.TypeBool, + Computed: true, + }, + "is_missing_secrets": { + Description: "Indicates whether secrets are missing for the connector.", + Type: schema.TypeBool, + Computed: true, + }, + "is_preconfigured": { + Description: "Indicates whether it is a preconfigured connector.", + Type: schema.TypeBool, + Computed: true, + }, + } + return &schema.Resource{ Description: "Search for a connector by name, space id, and type. Note, that this data source will fail if more than one connector shares the same name.", - ReadContext: dataSourceConnectorRead, + ReadContext: resourceConnectorsRead, Schema: connectorSchema, } } - -func dataSourceConnectorRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { - connectorName := d.Get("name").(string) - if err := d.Set("name", connectorName); err != nil { - return diag.FromErr(err) - } - - spaceId := d.Get("space_id").(string) - if err := d.Set("space_id", spaceId); err != nil { - return diag.FromErr(err) - } - - return resourceConnectorsRead(ctx, d, meta) -} diff --git a/internal/kibana/connector_data_source_test.go b/internal/kibana/connector_data_source_test.go new file mode 100644 index 000000000..fac35b299 --- /dev/null +++ b/internal/kibana/connector_data_source_test.go @@ -0,0 +1,52 @@ +package kibana_test + +import ( + "testing" + + "github.com/elastic/terraform-provider-elasticstack/internal/acctest" + "github.com/elastic/terraform-provider-elasticstack/internal/utils" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" +) + +func TestAccDataSourceKibanaConnector(t *testing.T) { + resource.Test(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(t) }, + ProtoV6ProviderFactories: acctest.Providers, + Steps: []resource.TestStep{ + { + Config: testAccDataSourceConnector, + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr("data.elasticstack_kibana_action_connector.myconnector", "name", "myconnector"), + resource.TestCheckResourceAttr("data.elasticstack_kibana_action_connector.myconnector", "space_id", "supdawg"), + resource.TestCheckResourceAttr("data.elasticstack_kibana_action_connector.myconnector", "connector_type_id", ".slack"), + utils.TestCheckResourceListAttr("data.elasticstack_kibana_security_role.test", "elasticsearch.0.run_as", []string{"elastic", "kibana"}), + utils.TestCheckResourceListAttr("data.elasticstack_kibana_security_role.test", "kibana.0.base", []string{"all"}), + utils.TestCheckResourceListAttr("data.elasticstack_kibana_security_role.test", "kibana.0.spaces", []string{"default"}), + ), + }, + }, + }) +} + +const testAccDataSourceConnector = ` +provider "elasticstack" { + elasticsearch {} + kibana {} +} + + +resource "elasticstack_kibana_action_connector" "slack" { + name = "myconnector" + space_id = "supdawg" + connector_type_id = ".slack" + secrets = jsonencode({ + webhookUrl = "https://internet.com" + }) + } + +data "elasticstack_kibana_action_connector" "myconnector" { + name = "myconnector" + space_id = "supdawg" +} + +` From 242fb7db39930c874c2fb638d8408f8ea7180545 Mon Sep 17 00:00:00 2001 From: Nick Clark Date: Wed, 17 Apr 2024 20:34:21 +1000 Subject: [PATCH 12/23] wip tests --- internal/kibana/connector_data_source_test.go | 4 ---- 1 file changed, 4 deletions(-) diff --git a/internal/kibana/connector_data_source_test.go b/internal/kibana/connector_data_source_test.go index fac35b299..f538b2ee5 100644 --- a/internal/kibana/connector_data_source_test.go +++ b/internal/kibana/connector_data_source_test.go @@ -4,7 +4,6 @@ import ( "testing" "github.com/elastic/terraform-provider-elasticstack/internal/acctest" - "github.com/elastic/terraform-provider-elasticstack/internal/utils" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" ) @@ -19,9 +18,6 @@ func TestAccDataSourceKibanaConnector(t *testing.T) { resource.TestCheckResourceAttr("data.elasticstack_kibana_action_connector.myconnector", "name", "myconnector"), resource.TestCheckResourceAttr("data.elasticstack_kibana_action_connector.myconnector", "space_id", "supdawg"), resource.TestCheckResourceAttr("data.elasticstack_kibana_action_connector.myconnector", "connector_type_id", ".slack"), - utils.TestCheckResourceListAttr("data.elasticstack_kibana_security_role.test", "elasticsearch.0.run_as", []string{"elastic", "kibana"}), - utils.TestCheckResourceListAttr("data.elasticstack_kibana_security_role.test", "kibana.0.base", []string{"all"}), - utils.TestCheckResourceListAttr("data.elasticstack_kibana_security_role.test", "kibana.0.spaces", []string{"default"}), ), }, }, From a9d9e5d090bef0aa013eec851014b307cc7bcac9 Mon Sep 17 00:00:00 2001 From: Nick Clark Date: Wed, 17 Apr 2024 20:42:09 +1000 Subject: [PATCH 13/23] bail early on connector search --- internal/clients/kibana/connector.go | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/internal/clients/kibana/connector.go b/internal/clients/kibana/connector.go index 75c8fb406..a2ad62422 100644 --- a/internal/clients/kibana/connector.go +++ b/internal/clients/kibana/connector.go @@ -170,6 +170,9 @@ func GetConnectorByName(ctx context.Context, apiClient *clients.ApiClient, conne foundConnectors := []*models.KibanaActionConnector{} for _, connector := range *resp.JSON200 { + if connector.Name != connectorName { + continue + } //this marshaling and unmarshaling business allows us to create a type with unexported fields. bytes, err := json.Marshal(connector) if err != nil { @@ -186,9 +189,8 @@ func GetConnectorByName(ctx context.Context, apiClient *clients.ApiClient, conne if err != nil { return nil, diag.Errorf("unable to convert response to model: %v", err) } - if c.Name == connectorName { - foundConnectors = append(foundConnectors, c) - } + + foundConnectors = append(foundConnectors, c) } if len(foundConnectors) == 1 { From 14b280ee31ce48002bd326ef8bd0f1c613e5a7dc Mon Sep 17 00:00:00 2001 From: Nick Clark Date: Wed, 17 Apr 2024 21:10:17 +1000 Subject: [PATCH 14/23] set ID on found resource --- internal/kibana/connector.go | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/internal/kibana/connector.go b/internal/kibana/connector.go index 3306a5457..1422806b3 100644 --- a/internal/kibana/connector.go +++ b/internal/kibana/connector.go @@ -209,7 +209,11 @@ func resourceConnectorsRead(ctx context.Context, d *schema.ResourceData, meta in if diags.HasError() { return diags } - + id, diags := client.ID(ctx, connector.ConnectorID) + if diags.HasError() { + return diags + } + d.SetId(id.String()) return flattenActionConnector(connector, d) } From f0f1dfa98bc2dce93787588b51edb52f7fc4dfa6 Mon Sep 17 00:00:00 2001 From: Nick Clark Date: Wed, 17 Apr 2024 21:10:29 +1000 Subject: [PATCH 15/23] fix example --- .../data-source.tf | 30 ++----------------- 1 file changed, 2 insertions(+), 28 deletions(-) diff --git a/examples/data-sources/elasticstack_kibana_action_connector/data-source.tf b/examples/data-sources/elasticstack_kibana_action_connector/data-source.tf index 6a4a0d3ef..000ebc5ad 100644 --- a/examples/data-sources/elasticstack_kibana_action_connector/data-source.tf +++ b/examples/data-sources/elasticstack_kibana_action_connector/data-source.tf @@ -1,40 +1,14 @@ -terraform { - required_providers { - elasticstack = { - source = "elastic/elasticstack" - } - } -} - provider "elasticstack" { - elasticsearch { - username = "elastic" - password = "password" - endpoints = ["http://localhost:9200"] - } - kibana { - username = "elastic" - password = "password" - endpoints = ["http://localhost:5601"] - } -} - -resource "elasticstack_kibana_action_connector" "slack-connector" { - name = "slack" - connector_type_id = ".slack" - secrets = jsonencode({ - webhookUrl = "https://lol.com" - }) + elasticsearch {} + kibana {} } data "elasticstack_kibana_action_connector" "example" { name = "myslackconnector" space_id = "default" connector_type_id = ".slack" - depends_on = [elasticstack_kibana_action_connector.slack-connector] } output "connector_id" { - # value = elasticstack_kibana_action_connector.slack-connector.connector_id value = data.elasticstack_kibana_action_connector.example.connector_id } From eab4a16572f129cf824d17a51446587e4763ab13 Mon Sep 17 00:00:00 2001 From: Nick Clark Date: Wed, 17 Apr 2024 21:10:50 +1000 Subject: [PATCH 16/23] fix acc test --- internal/kibana/connector_data_source_test.go | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/internal/kibana/connector_data_source_test.go b/internal/kibana/connector_data_source_test.go index f538b2ee5..4b24e866f 100644 --- a/internal/kibana/connector_data_source_test.go +++ b/internal/kibana/connector_data_source_test.go @@ -16,8 +16,9 @@ func TestAccDataSourceKibanaConnector(t *testing.T) { Config: testAccDataSourceConnector, Check: resource.ComposeTestCheckFunc( resource.TestCheckResourceAttr("data.elasticstack_kibana_action_connector.myconnector", "name", "myconnector"), - resource.TestCheckResourceAttr("data.elasticstack_kibana_action_connector.myconnector", "space_id", "supdawg"), + resource.TestCheckResourceAttr("data.elasticstack_kibana_action_connector.myconnector", "space_id", "default"), resource.TestCheckResourceAttr("data.elasticstack_kibana_action_connector.myconnector", "connector_type_id", ".slack"), + resource.TestCheckResourceAttrSet("data.elasticstack_kibana_action_connector.myconnector", "connector_id"), ), }, }, @@ -30,10 +31,8 @@ provider "elasticstack" { kibana {} } - resource "elasticstack_kibana_action_connector" "slack" { name = "myconnector" - space_id = "supdawg" connector_type_id = ".slack" secrets = jsonencode({ webhookUrl = "https://internet.com" @@ -41,8 +40,6 @@ resource "elasticstack_kibana_action_connector" "slack" { } data "elasticstack_kibana_action_connector" "myconnector" { - name = "myconnector" - space_id = "supdawg" + name = elasticstack_kibana_action_connector.slack.name } - ` From b8c3a26918ef7da639d54e0ee6aba895253f119a Mon Sep 17 00:00:00 2001 From: Nick Clark Date: Wed, 17 Apr 2024 21:10:58 +1000 Subject: [PATCH 17/23] data source schema tweaks --- internal/kibana/connector_data_source.go | 21 ++++----------------- 1 file changed, 4 insertions(+), 17 deletions(-) diff --git a/internal/kibana/connector_data_source.go b/internal/kibana/connector_data_source.go index 68e23e27c..00dedcc93 100644 --- a/internal/kibana/connector_data_source.go +++ b/internal/kibana/connector_data_source.go @@ -1,15 +1,13 @@ package kibana import ( - "github.com/elastic/terraform-provider-elasticstack/internal/utils" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" - "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" ) func DataSourceConnector() *schema.Resource { var connectorSchema = map[string]*schema.Schema{ "connector_id": { - Description: "A UUID v1 or v4 to use instead of a randomly generated ID.", + Description: "A UUID v1 or v4 randomly generated ID.", Type: schema.TypeString, Computed: true, }, @@ -18,7 +16,6 @@ func DataSourceConnector() *schema.Resource { Type: schema.TypeString, Optional: true, Default: "default", - ForceNew: true, }, "name": { Description: "The name of the connector. While this name does not have to be unique, a distinctive name can help you identify a connector.", @@ -29,21 +26,11 @@ func DataSourceConnector() *schema.Resource { Description: "The ID of the connector type, e.g. `.index`.", Type: schema.TypeString, Optional: true, - ForceNew: true, }, "config": { - Description: "The configuration for the connector. Configuration properties vary depending on the connector type.", - Type: schema.TypeString, - Optional: true, - Computed: true, - ValidateFunc: validation.StringIsJSON, - }, - "secrets": { - Description: "The secrets configuration for the connector. Secrets configuration properties vary depending on the connector type.", - Type: schema.TypeString, - Optional: true, - DiffSuppressFunc: utils.DiffJsonSuppress, - ValidateFunc: validation.StringIsJSON, + Description: "The configuration for the connector. Configuration properties vary depending on the connector type.", + Type: schema.TypeString, + Computed: true, }, "is_deprecated": { Description: "Indicates whether the connector type is deprecated.", From 3c98ee1ff0282d72cf90b38a7ffd323f511f5978 Mon Sep 17 00:00:00 2001 From: Nick Clark Date: Wed, 17 Apr 2024 21:13:32 +1000 Subject: [PATCH 18/23] regen docs --- docs/data-sources/kibana_action_connector | 14 ++++++-------- docs/data-sources/kibana_action_connector.md | 14 ++++++-------- 2 files changed, 12 insertions(+), 16 deletions(-) diff --git a/docs/data-sources/kibana_action_connector b/docs/data-sources/kibana_action_connector index ba0dda769..d7870342c 100644 --- a/docs/data-sources/kibana_action_connector +++ b/docs/data-sources/kibana_action_connector @@ -16,13 +16,12 @@ Use this data source to get information about an existing action connector. provider "elasticstack" { elasticsearch {} kibana {} - } data "elasticstack_kibana_action_connector" "example" { - name = "myslackconnector" - space_id = "default" - connector_type = ".slack" + name = "myslackconnector" + space_id = "default" + connector_type_id = ".slack" } output "connector_id" { @@ -35,19 +34,18 @@ output "connector_id" { ### Required -- `connector_type_id` (String) The ID of the connector type, e.g. `.index`. - `name` (String) The name of the connector. While this name does not have to be unique, a distinctive name can help you identify a connector. ### Optional -- `config` (String) The configuration for the connector. Configuration properties vary depending on the connector type. -- `connector_id` (String) A UUID v1 or v4 to use instead of a randomly generated ID. +- `connector_type_id` (String) The ID of the connector type, e.g. `.index`. - `id` (String) The ID of this resource. -- `secrets` (String) The secrets configuration for the connector. Secrets configuration properties vary depending on the connector type. - `space_id` (String) An identifier for the space. If space_id is not provided, the default space is used. ### Read-Only +- `config` (String) The configuration for the connector. Configuration properties vary depending on the connector type. +- `connector_id` (String) A UUID v1 or v4 randomly generated ID. - `is_deprecated` (Boolean) Indicates whether the connector type is deprecated. - `is_missing_secrets` (Boolean) Indicates whether secrets are missing for the connector. - `is_preconfigured` (Boolean) Indicates whether it is a preconfigured connector. diff --git a/docs/data-sources/kibana_action_connector.md b/docs/data-sources/kibana_action_connector.md index ddc88f010..cc2f8c73f 100644 --- a/docs/data-sources/kibana_action_connector.md +++ b/docs/data-sources/kibana_action_connector.md @@ -16,13 +16,12 @@ Search for a connector by name, space id, and type. Note, that this data source provider "elasticstack" { elasticsearch {} kibana {} - } data "elasticstack_kibana_action_connector" "example" { - name = "myslackconnector" - space_id = "default" - connector_type = ".slack" + name = "myslackconnector" + space_id = "default" + connector_type_id = ".slack" } output "connector_id" { @@ -35,18 +34,17 @@ output "connector_id" { ### Required -- `connector_type_id` (String) The ID of the connector type, e.g. `.index`. - `name` (String) The name of the connector. While this name does not have to be unique, a distinctive name can help you identify a connector. ### Optional -- `config` (String) The configuration for the connector. Configuration properties vary depending on the connector type. -- `connector_id` (String) A UUID v1 or v4 to use instead of a randomly generated ID. -- `secrets` (String) The secrets configuration for the connector. Secrets configuration properties vary depending on the connector type. +- `connector_type_id` (String) The ID of the connector type, e.g. `.index`. - `space_id` (String) An identifier for the space. If space_id is not provided, the default space is used. ### Read-Only +- `config` (String) The configuration for the connector. Configuration properties vary depending on the connector type. +- `connector_id` (String) A UUID v1 or v4 randomly generated ID. - `id` (String) The ID of this resource. - `is_deprecated` (Boolean) Indicates whether the connector type is deprecated. - `is_missing_secrets` (Boolean) Indicates whether secrets are missing for the connector. From 64c3c0e3434f2406f6b10a4828f0df6d971e285c Mon Sep 17 00:00:00 2001 From: Nick Clark Date: Thu, 18 Apr 2024 20:10:01 +1000 Subject: [PATCH 19/23] rename doc template name --- ...bana_action_connector.tmpl => kibana_action_connector.md.tmpl} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename templates/data-sources/{kibana_action_connector.tmpl => kibana_action_connector.md.tmpl} (100%) diff --git a/templates/data-sources/kibana_action_connector.tmpl b/templates/data-sources/kibana_action_connector.md.tmpl similarity index 100% rename from templates/data-sources/kibana_action_connector.tmpl rename to templates/data-sources/kibana_action_connector.md.tmpl From 0d20608e085954f4f3053f19a9807ece37b07320 Mon Sep 17 00:00:00 2001 From: Nick Clark Date: Thu, 18 Apr 2024 20:11:53 +1000 Subject: [PATCH 20/23] regen docs again --- docs/data-sources/kibana_action_connector | 51 -------------------- docs/data-sources/kibana_action_connector.md | 12 ++--- 2 files changed, 6 insertions(+), 57 deletions(-) delete mode 100644 docs/data-sources/kibana_action_connector diff --git a/docs/data-sources/kibana_action_connector b/docs/data-sources/kibana_action_connector deleted file mode 100644 index d7870342c..000000000 --- a/docs/data-sources/kibana_action_connector +++ /dev/null @@ -1,51 +0,0 @@ ---- -subcategory: "Kibana" -layout: "" -page_title: "Elasticstack: elasticstack_kibana_action_connector Data Source" -description: |- - Retrieve a specific action connector role. See https://www.elastic.co/guide/en/kibana/current/get-all-connectors-api.html. ---- - -# Data Source: elasticstack_kibana_action_connector - -Use this data source to get information about an existing action connector. - -## Example Usage - -```terraform -provider "elasticstack" { - elasticsearch {} - kibana {} -} - -data "elasticstack_kibana_action_connector" "example" { - name = "myslackconnector" - space_id = "default" - connector_type_id = ".slack" -} - -output "connector_id" { - value = data.elasticstack_kibana_action_connector.example.connector_id -} -``` - - -## Schema - -### Required - -- `name` (String) The name of the connector. While this name does not have to be unique, a distinctive name can help you identify a connector. - -### Optional - -- `connector_type_id` (String) The ID of the connector type, e.g. `.index`. -- `id` (String) The ID of this resource. -- `space_id` (String) An identifier for the space. If space_id is not provided, the default space is used. - -### Read-Only - -- `config` (String) The configuration for the connector. Configuration properties vary depending on the connector type. -- `connector_id` (String) A UUID v1 or v4 randomly generated ID. -- `is_deprecated` (Boolean) Indicates whether the connector type is deprecated. -- `is_missing_secrets` (Boolean) Indicates whether secrets are missing for the connector. -- `is_preconfigured` (Boolean) Indicates whether it is a preconfigured connector. diff --git a/docs/data-sources/kibana_action_connector.md b/docs/data-sources/kibana_action_connector.md index cc2f8c73f..5513b5d70 100644 --- a/docs/data-sources/kibana_action_connector.md +++ b/docs/data-sources/kibana_action_connector.md @@ -1,14 +1,14 @@ --- -# generated by https://github.com/hashicorp/terraform-plugin-docs -page_title: "elasticstack_kibana_action_connector Data Source - terraform-provider-elasticstack" -subcategory: "" +subcategory: "Kibana" +layout: "" +page_title: "Elasticstack: elasticstack_kibana_action_connector Data Source" description: |- - Search for a connector by name, space id, and type. Note, that this data source will fail if more than one connector shares the same name. + Retrieve a specific action connector role. See https://www.elastic.co/guide/en/kibana/current/get-all-connectors-api.html. --- -# elasticstack_kibana_action_connector (Data Source) +# Data Source: elasticstack_kibana_action_connector -Search for a connector by name, space id, and type. Note, that this data source will fail if more than one connector shares the same name. +Use this data source to get information about an existing action connector. ## Example Usage From 19f51fab6d6d106a3b086ade51e7808767df1254 Mon Sep 17 00:00:00 2001 From: Nick Clark Date: Thu, 18 Apr 2024 20:13:53 +1000 Subject: [PATCH 21/23] allow searches on connector type --- internal/clients/kibana/connector.go | 7 ++++++- internal/clients/kibana/connector_test.go | 16 +++++++++++++--- 2 files changed, 19 insertions(+), 4 deletions(-) diff --git a/internal/clients/kibana/connector.go b/internal/clients/kibana/connector.go index a2ad62422..f2d6ae177 100644 --- a/internal/clients/kibana/connector.go +++ b/internal/clients/kibana/connector.go @@ -141,7 +141,7 @@ func GetConnector(ctx context.Context, apiClient *clients.ApiClient, connectorID return connector, nil } -func GetConnectorByName(ctx context.Context, apiClient *clients.ApiClient, connectorName, spaceID string) (*models.KibanaActionConnector, diag.Diagnostics) { +func SearchConnector(ctx context.Context, apiClient *clients.ApiClient, connectorName, spaceID, connectorTypeID string) (*models.KibanaActionConnector, diag.Diagnostics) { client, err := apiClient.GetKibanaConnectorsClient(ctx) if err != nil { return nil, diag.FromErr(err) @@ -173,6 +173,11 @@ func GetConnectorByName(ctx context.Context, apiClient *clients.ApiClient, conne if connector.Name != connectorName { continue } + + if connectorTypeID != "" && string(connector.ConnectorTypeId) != connectorTypeID { + continue + } + //this marshaling and unmarshaling business allows us to create a type with unexported fields. bytes, err := json.Marshal(connector) if err != nil { diff --git a/internal/clients/kibana/connector_test.go b/internal/clients/kibana/connector_test.go index 83d61901f..a727cf62c 100644 --- a/internal/clients/kibana/connector_test.go +++ b/internal/clients/kibana/connector_test.go @@ -211,18 +211,28 @@ func TestGetConnectorByName(t *testing.T) { apiClient, err := clients.NewAcceptanceTestingClient() require.NoError(t, err) - connector, diags := GetConnectorByName(context.Background(), apiClient, "my-connector", "default") + connector, diags := SearchConnector(context.Background(), apiClient, "my-connector", "default", "") require.Nil(t, diags) require.NotNil(t, connector) mockResponses = append(mockResponses, getConnectorsResponse) - failConnector, diags := GetConnectorByName(context.Background(), apiClient, "failwhale", "default") + failConnector, diags := SearchConnector(context.Background(), apiClient, "failwhale", "default", "") require.NotNil(t, diags) require.Nil(t, failConnector) mockResponses = append(mockResponses, getConnectorsResponse) - dupConnector, diags := GetConnectorByName(context.Background(), apiClient, "doubledup-connector", "default") + dupConnector, diags := SearchConnector(context.Background(), apiClient, "doubledup-connector", "default", "") require.NotNil(t, diags) require.Nil(t, dupConnector) + mockResponses = append(mockResponses, getConnectorsResponse) + wrongConnectorType, diags := SearchConnector(context.Background(), apiClient, "my-connector", "default", ".slack") + require.NotNil(t, diags) + require.Nil(t, wrongConnectorType) + + mockResponses = append(mockResponses, getConnectorsResponse) + successConnector, diags := SearchConnector(context.Background(), apiClient, "my-connecctor", "default", ".index") + require.NotNil(t, diags) + require.Nil(t, successConnector) + } From 7c63a698d6711ee012dc62381a37773198d4d2e1 Mon Sep 17 00:00:00 2001 From: Nick Clark Date: Thu, 18 Apr 2024 20:14:20 +1000 Subject: [PATCH 22/23] move data source function --- internal/kibana/connector.go | 20 ------------------ internal/kibana/connector_data_source.go | 27 +++++++++++++++++++++++- 2 files changed, 26 insertions(+), 21 deletions(-) diff --git a/internal/kibana/connector.go b/internal/kibana/connector.go index 1422806b3..e44227fd1 100644 --- a/internal/kibana/connector.go +++ b/internal/kibana/connector.go @@ -197,26 +197,6 @@ func resourceConnectorRead(ctx context.Context, d *schema.ResourceData, meta int return flattenActionConnector(connector, d) } -func resourceConnectorsRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { - client, diags := clients.NewApiClientFromSDKResource(d, meta) - if diags.HasError() { - return diags - } - name := d.Get("name").(string) - spaceId := d.Get("space_id").(string) - - connector, diags := kibana.GetConnectorByName(ctx, client, name, spaceId) - if diags.HasError() { - return diags - } - id, diags := client.ID(ctx, connector.ConnectorID) - if diags.HasError() { - return diags - } - d.SetId(id.String()) - return flattenActionConnector(connector, d) -} - func resourceConnectorDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { client, diags := clients.NewApiClientFromSDKResource(d, meta) if diags.HasError() { diff --git a/internal/kibana/connector_data_source.go b/internal/kibana/connector_data_source.go index 00dedcc93..908b8ebc0 100644 --- a/internal/kibana/connector_data_source.go +++ b/internal/kibana/connector_data_source.go @@ -1,6 +1,11 @@ package kibana import ( + "context" + + "github.com/elastic/terraform-provider-elasticstack/internal/clients" + "github.com/elastic/terraform-provider-elasticstack/internal/clients/kibana" + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" ) @@ -51,7 +56,27 @@ func DataSourceConnector() *schema.Resource { return &schema.Resource{ Description: "Search for a connector by name, space id, and type. Note, that this data source will fail if more than one connector shares the same name.", - ReadContext: resourceConnectorsRead, + ReadContext: datasourceConnectorRead, Schema: connectorSchema, } } + +func datasourceConnectorRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + client, diags := clients.NewApiClientFromSDKResource(d, meta) + if diags.HasError() { + return diags + } + name := d.Get("name").(string) + spaceId := d.Get("space_id").(string) + connectorType := d.Get("connector_type_id").(string) + + connector, diags := kibana.SearchConnector(ctx, client, name, spaceId, connectorType) + if diags.HasError() { + return diags + } + + compositeID := &clients.CompositeId{ClusterId: spaceId, ResourceId: connector.ConnectorID} + d.SetId(compositeID.String()) + + return flattenActionConnector(connector, d) +} From 40f45f881148acd63d6082117580164a4182deda Mon Sep 17 00:00:00 2001 From: Nick Clark Date: Tue, 23 Apr 2024 11:16:31 +1000 Subject: [PATCH 23/23] remove checks in connector client used for data source, add more tests --- internal/clients/kibana/connector.go | 14 +++----- internal/clients/kibana/connector_test.go | 41 ++++++++++++++++------- internal/kibana/connector_data_source.go | 16 ++++++--- 3 files changed, 45 insertions(+), 26 deletions(-) diff --git a/internal/clients/kibana/connector.go b/internal/clients/kibana/connector.go index f2d6ae177..027dc5c19 100644 --- a/internal/clients/kibana/connector.go +++ b/internal/clients/kibana/connector.go @@ -11,6 +11,7 @@ import ( "github.com/elastic/terraform-provider-elasticstack/internal/clients" "github.com/elastic/terraform-provider-elasticstack/internal/models" "github.com/elastic/terraform-provider-elasticstack/internal/utils" + "github.com/hashicorp/terraform-plugin-log/tflog" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" ) @@ -141,7 +142,7 @@ func GetConnector(ctx context.Context, apiClient *clients.ApiClient, connectorID return connector, nil } -func SearchConnector(ctx context.Context, apiClient *clients.ApiClient, connectorName, spaceID, connectorTypeID string) (*models.KibanaActionConnector, diag.Diagnostics) { +func SearchConnectors(ctx context.Context, apiClient *clients.ApiClient, connectorName, spaceID, connectorTypeID string) ([]*models.KibanaActionConnector, diag.Diagnostics) { client, err := apiClient.GetKibanaConnectorsClient(ctx) if err != nil { return nil, diag.FromErr(err) @@ -197,16 +198,11 @@ func SearchConnector(ctx context.Context, apiClient *clients.ApiClient, connecto foundConnectors = append(foundConnectors, c) } - - if len(foundConnectors) == 1 { - return foundConnectors[0], nil - } - - if len(foundConnectors) > 1 { - return nil, diag.Errorf("error while creating elasticstack_kibana_action_connector datasource: multiple connectors with name [%s/%s] found", spaceID, connectorName) + if len(foundConnectors) == 0 { + tflog.Debug(ctx, fmt.Sprintf("no connectors found with name [%s/%s] and type [%s]", spaceID, connectorName, connectorTypeID)) } - return nil, diag.Errorf("error while creating elasticstack_kibana_action_connector datasource: connector [%s/%s] not found", spaceID, connectorName) + return foundConnectors, nil } func DeleteConnector(ctx context.Context, apiClient *clients.ApiClient, connectorID string, spaceID string) diag.Diagnostics { diff --git a/internal/clients/kibana/connector_test.go b/internal/clients/kibana/connector_test.go index a727cf62c..566611cab 100644 --- a/internal/clients/kibana/connector_test.go +++ b/internal/clients/kibana/connector_test.go @@ -182,8 +182,11 @@ func TestGetConnectorByName(t *testing.T) { } ]` + const emptyConnectorsResponse = `[]` + var requests []*http.Request var mockResponses []string + var httpStatus int server := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) { requests = append(requests, req) @@ -191,7 +194,7 @@ func TestGetConnectorByName(t *testing.T) { r := []byte(mockResponses[0]) rw.Header().Add("X-Elastic-Product", "Elasticsearch") rw.Header().Add("Content-Type", "application/json") - rw.WriteHeader(http.StatusOK) + rw.WriteHeader(httpStatus) _, err := rw.Write(r) require.NoError(t, err) mockResponses = mockResponses[1:] @@ -201,6 +204,7 @@ func TestGetConnectorByName(t *testing.T) { })) defer server.Close() + httpStatus = http.StatusOK mockResponses = append(mockResponses, getConnectorsResponse) err := os.Setenv("ELASTICSEARCH_URL", server.URL) @@ -211,28 +215,39 @@ func TestGetConnectorByName(t *testing.T) { apiClient, err := clients.NewAcceptanceTestingClient() require.NoError(t, err) - connector, diags := SearchConnector(context.Background(), apiClient, "my-connector", "default", "") + connector, diags := SearchConnectors(context.Background(), apiClient, "my-connector", "default", "") require.Nil(t, diags) require.NotNil(t, connector) mockResponses = append(mockResponses, getConnectorsResponse) - failConnector, diags := SearchConnector(context.Background(), apiClient, "failwhale", "default", "") - require.NotNil(t, diags) - require.Nil(t, failConnector) + failConnector, diags := SearchConnectors(context.Background(), apiClient, "failwhale", "default", "") + require.Nil(t, diags) + require.Empty(t, failConnector) mockResponses = append(mockResponses, getConnectorsResponse) - dupConnector, diags := SearchConnector(context.Background(), apiClient, "doubledup-connector", "default", "") - require.NotNil(t, diags) - require.Nil(t, dupConnector) + dupConnector, diags := SearchConnectors(context.Background(), apiClient, "doubledup-connector", "default", "") + require.Nil(t, diags) + require.Len(t, dupConnector, 2) mockResponses = append(mockResponses, getConnectorsResponse) - wrongConnectorType, diags := SearchConnector(context.Background(), apiClient, "my-connector", "default", ".slack") - require.NotNil(t, diags) - require.Nil(t, wrongConnectorType) + wrongConnectorType, diags := SearchConnectors(context.Background(), apiClient, "my-connector", "default", ".slack") + require.Nil(t, diags) + require.Empty(t, wrongConnectorType) mockResponses = append(mockResponses, getConnectorsResponse) - successConnector, diags := SearchConnector(context.Background(), apiClient, "my-connecctor", "default", ".index") + successConnector, diags := SearchConnectors(context.Background(), apiClient, "my-connector", "default", ".index") + require.Nil(t, diags) + require.Len(t, successConnector, 1) + + mockResponses = append(mockResponses, emptyConnectorsResponse) + emptyConnector, diags := SearchConnectors(context.Background(), apiClient, "my-connector", "default", "") + require.Nil(t, diags) + require.Empty(t, emptyConnector) + + httpStatus = http.StatusBadGateway + mockResponses = append(mockResponses, emptyConnectorsResponse) + fail, diags := SearchConnectors(context.Background(), apiClient, "my-connector", "default", "") require.NotNil(t, diags) - require.Nil(t, successConnector) + require.Nil(t, fail) } diff --git a/internal/kibana/connector_data_source.go b/internal/kibana/connector_data_source.go index 908b8ebc0..e901c8bf3 100644 --- a/internal/kibana/connector_data_source.go +++ b/internal/kibana/connector_data_source.go @@ -66,17 +66,25 @@ func datasourceConnectorRead(ctx context.Context, d *schema.ResourceData, meta i if diags.HasError() { return diags } - name := d.Get("name").(string) + connectorName := d.Get("name").(string) spaceId := d.Get("space_id").(string) connectorType := d.Get("connector_type_id").(string) - connector, diags := kibana.SearchConnector(ctx, client, name, spaceId, connectorType) + foundConnectors, diags := kibana.SearchConnectors(ctx, client, connectorName, spaceId, connectorType) if diags.HasError() { return diags } - compositeID := &clients.CompositeId{ClusterId: spaceId, ResourceId: connector.ConnectorID} + if len(foundConnectors) == 0 { + diag.Errorf("error while creating elasticstack_kibana_action_connector datasource: connector with name [%s/%s] and type [%s] not found", spaceId, connectorName, connectorType) + } + + if len(foundConnectors) > 1 { + return diag.Errorf("error while creating elasticstack_kibana_action_connector datasource: multiple connectors found with name [%s/%s] and type [%s]", spaceId, connectorName, connectorType) + } + + compositeID := &clients.CompositeId{ClusterId: spaceId, ResourceId: foundConnectors[0].ConnectorID} d.SetId(compositeID.String()) - return flattenActionConnector(connector, d) + return flattenActionConnector(foundConnectors[0], d) }