diff --git a/CHANGELOG.md b/CHANGELOG.md index 8a09de02a..25197e65b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -43,6 +43,7 @@ alias = [ - Support `.bedrock` and `.gen-ai` connectors ([#1467](https://github.com/elastic/terraform-provider-elasticstack/pull/1467)) - Support the `solution` attribute in `elasticstack_kibana_space` from 8.16 ([#1486](https://github.com/elastic/terraform-provider-elasticstack/pull/1486)) - Add `elasticstack_elasticsearch_alias` resource ([#1343](https://github.com/elastic/terraform-provider-elasticstack/pull/1343)) +- Add `elasticstack_kibana_default_data_view` resource ([#1379](https://github.com/elastic/terraform-provider-elasticstack/pull/1379)) ## [0.12.2] - 2025-11-19 - Fix `elasticstack_elasticsearch_snapshot_lifecycle` metadata type conversion causing terraform apply to fail ([#1409](https://github.com/elastic/terraform-provider-elasticstack/issues/1409)) diff --git a/docs/resources/kibana_default_data_view.md b/docs/resources/kibana_default_data_view.md new file mode 100644 index 000000000..92bc132a4 --- /dev/null +++ b/docs/resources/kibana_default_data_view.md @@ -0,0 +1,49 @@ +--- +# generated by https://github.com/hashicorp/terraform-plugin-docs +page_title: "elasticstack_kibana_default_data_view Resource - terraform-provider-elasticstack" +subcategory: "Kibana" +description: |- + Manages the default Kibana data view. See the Kibana Data Views API documentation https://www.elastic.co/docs/api/doc/kibana/v8/operation/operation-setdefaultdatailviewdefault for more information. +--- + +# elasticstack_kibana_default_data_view (Resource) + +Manages the default Kibana data view. See the [Kibana Data Views API documentation](https://www.elastic.co/docs/api/doc/kibana/v8/operation/operation-setdefaultdatailviewdefault) for more information. + +## Example Usage + +```terraform +resource "elasticstack_elasticsearch_index" "my_index" { + name = "my-index-000001" + deletion_protection = false +} + +resource "elasticstack_kibana_data_view" "my_data_view" { + data_view = { + title = "my-index-*" + name = "My Index Data View" + } + + depends_on = [elasticstack_elasticsearch_index.my_index] +} + +resource "elasticstack_kibana_default_data_view" "default" { + data_view_id = elasticstack_kibana_data_view.my_data_view.data_view.id + force = true + skip_delete = false +} +``` + + +## Schema + +### Optional + +- `data_view_id` (String) The data view identifier to set as default. NOTE: The API does not validate whether it is a valid identifier. Leave this unset (or explicitly `null`) to unset the default data view. +- `force` (Boolean) Update an existing default data view identifier. If set to false and a default data view already exists, the operation will fail. +- `skip_delete` (Boolean) If set to true, the default data view will not be unset when the resource is destroyed. The existing default data view will remain unchanged. +- `space_id` (String) The Kibana space ID to set the default data view in. Defaults to `default`. + +### Read-Only + +- `id` (String) Internal identifier of the resource. diff --git a/examples/resources/elasticstack_kibana_default_data_view/resource.tf b/examples/resources/elasticstack_kibana_default_data_view/resource.tf new file mode 100644 index 000000000..8b8f3fd8c --- /dev/null +++ b/examples/resources/elasticstack_kibana_default_data_view/resource.tf @@ -0,0 +1,19 @@ +resource "elasticstack_elasticsearch_index" "my_index" { + name = "my-index-000001" + deletion_protection = false +} + +resource "elasticstack_kibana_data_view" "my_data_view" { + data_view = { + title = "my-index-*" + name = "My Index Data View" + } + + depends_on = [elasticstack_elasticsearch_index.my_index] +} + +resource "elasticstack_kibana_default_data_view" "default" { + data_view_id = elasticstack_kibana_data_view.my_data_view.data_view.id + force = true + skip_delete = false +} diff --git a/generated/kbapi/kibana.gen.go b/generated/kbapi/kibana.gen.go index 49eef4b17..28bad859d 100644 --- a/generated/kbapi/kibana.gen.go +++ b/generated/kbapi/kibana.gen.go @@ -27286,15 +27286,6 @@ type UpdateRuntimeFieldDefaultJSONBody struct { RuntimeField map[string]interface{} `json:"runtimeField"` } -// SetDefaultDatailViewDefaultJSONBody defines parameters for SetDefaultDatailViewDefault. -type SetDefaultDatailViewDefaultJSONBody struct { - // DataViewId The data view identifier. NOTE: The API does not validate whether it is a valid identifier. Use `null` to unset the default data view. - DataViewId *string `json:"data_view_id,omitempty"` - - // Force Update an existing default data view identifier. - Force *bool `json:"force,omitempty"` -} - // PerformRulesBulkActionJSONBody defines parameters for PerformRulesBulkAction. type PerformRulesBulkActionJSONBody struct { union json.RawMessage @@ -47081,6 +47072,15 @@ type PutActionsConnectorIdJSONBody struct { Secrets *UpdateConnectorSecrets `json:"secrets,omitempty"` } +// SetDefaultDatailViewDefaultJSONBody defines parameters for SetDefaultDatailViewDefault. +type SetDefaultDatailViewDefaultJSONBody struct { + // DataViewId The data view identifier. NOTE: The API does not validate whether it is a valid identifier. Use `null` to unset the default data view. + DataViewId *string `json:"data_view_id,omitempty"` + + // Force Update an existing default data view identifier. + Force *bool `json:"force,omitempty"` +} + // DeleteRuleParams defines parameters for DeleteRule. type DeleteRuleParams struct { // Id The rule's `id` value. @@ -47736,9 +47736,6 @@ type CreateUpdateRuntimeFieldDefaultJSONRequestBody CreateUpdateRuntimeFieldDefa // UpdateRuntimeFieldDefaultJSONRequestBody defines body for UpdateRuntimeFieldDefault for application/json ContentType. type UpdateRuntimeFieldDefaultJSONRequestBody UpdateRuntimeFieldDefaultJSONBody -// SetDefaultDatailViewDefaultJSONRequestBody defines body for SetDefaultDatailViewDefault for application/json ContentType. -type SetDefaultDatailViewDefaultJSONRequestBody SetDefaultDatailViewDefaultJSONBody - // SwapDataViewsDefaultJSONRequestBody defines body for SwapDataViewsDefault for application/json ContentType. type SwapDataViewsDefaultJSONRequestBody = DataViewsSwapDataViewRequestObject @@ -48318,6 +48315,9 @@ type CreateDataViewDefaultwJSONRequestBody = DataViewsCreateDataViewRequestObjec // UpdateDataViewDefaultJSONRequestBody defines body for UpdateDataViewDefault for application/json ContentType. type UpdateDataViewDefaultJSONRequestBody = DataViewsUpdateDataViewRequestObject +// SetDefaultDatailViewDefaultJSONRequestBody defines body for SetDefaultDatailViewDefault for application/json ContentType. +type SetDefaultDatailViewDefaultJSONRequestBody SetDefaultDatailViewDefaultJSONBody + // PatchRuleJSONRequestBody defines body for PatchRule for application/json ContentType. type PatchRuleJSONRequestBody = SecurityDetectionsAPIRulePatchProps @@ -75216,14 +75216,6 @@ type ClientInterface interface { UpdateRuntimeFieldDefault(ctx context.Context, viewId DataViewsViewId, fieldName DataViewsFieldName, body UpdateRuntimeFieldDefaultJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) - // GetDefaultDataViewDefault request - GetDefaultDataViewDefault(ctx context.Context, reqEditors ...RequestEditorFn) (*http.Response, error) - - // SetDefaultDatailViewDefaultWithBody request with any body - SetDefaultDatailViewDefaultWithBody(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) - - SetDefaultDatailViewDefault(ctx context.Context, body SetDefaultDatailViewDefaultJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) - // SwapDataViewsDefaultWithBody request with any body SwapDataViewsDefaultWithBody(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) @@ -76752,6 +76744,14 @@ type ClientInterface interface { UpdateDataViewDefault(ctx context.Context, spaceId SpaceId, viewId DataViewsViewId, body UpdateDataViewDefaultJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) + // GetDefaultDataViewDefault request + GetDefaultDataViewDefault(ctx context.Context, spaceId SpaceId, reqEditors ...RequestEditorFn) (*http.Response, error) + + // SetDefaultDatailViewDefaultWithBody request with any body + SetDefaultDatailViewDefaultWithBody(ctx context.Context, spaceId SpaceId, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) + + SetDefaultDatailViewDefault(ctx context.Context, spaceId SpaceId, body SetDefaultDatailViewDefaultJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) + // DeleteRule request DeleteRule(ctx context.Context, spaceId SpaceId, params *DeleteRuleParams, reqEditors ...RequestEditorFn) (*http.Response, error) @@ -78530,42 +78530,6 @@ func (c *Client) UpdateRuntimeFieldDefault(ctx context.Context, viewId DataViews return c.Client.Do(req) } -func (c *Client) GetDefaultDataViewDefault(ctx context.Context, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewGetDefaultDataViewDefaultRequest(c.Server) - if err != nil { - return nil, err - } - req = req.WithContext(ctx) - if err := c.applyEditors(ctx, req, reqEditors); err != nil { - return nil, err - } - return c.Client.Do(req) -} - -func (c *Client) SetDefaultDatailViewDefaultWithBody(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewSetDefaultDatailViewDefaultRequestWithBody(c.Server, contentType, body) - if err != nil { - return nil, err - } - req = req.WithContext(ctx) - if err := c.applyEditors(ctx, req, reqEditors); err != nil { - return nil, err - } - return c.Client.Do(req) -} - -func (c *Client) SetDefaultDatailViewDefault(ctx context.Context, body SetDefaultDatailViewDefaultJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewSetDefaultDatailViewDefaultRequest(c.Server, body) - if err != nil { - return nil, err - } - req = req.WithContext(ctx) - if err := c.applyEditors(ctx, req, reqEditors); err != nil { - return nil, err - } - return c.Client.Do(req) -} - func (c *Client) SwapDataViewsDefaultWithBody(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { req, err := NewSwapDataViewsDefaultRequestWithBody(c.Server, contentType, body) if err != nil { @@ -85418,6 +85382,42 @@ func (c *Client) UpdateDataViewDefault(ctx context.Context, spaceId SpaceId, vie return c.Client.Do(req) } +func (c *Client) GetDefaultDataViewDefault(ctx context.Context, spaceId SpaceId, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewGetDefaultDataViewDefaultRequest(c.Server, spaceId) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +func (c *Client) SetDefaultDatailViewDefaultWithBody(ctx context.Context, spaceId SpaceId, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewSetDefaultDatailViewDefaultRequestWithBody(c.Server, spaceId, contentType, body) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +func (c *Client) SetDefaultDatailViewDefault(ctx context.Context, spaceId SpaceId, body SetDefaultDatailViewDefaultJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewSetDefaultDatailViewDefaultRequest(c.Server, spaceId, body) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + func (c *Client) DeleteRule(ctx context.Context, spaceId SpaceId, params *DeleteRuleParams, reqEditors ...RequestEditorFn) (*http.Response, error) { req, err := NewDeleteRuleRequest(c.Server, spaceId, params) if err != nil { @@ -91374,73 +91374,6 @@ func NewUpdateRuntimeFieldDefaultRequestWithBody(server string, viewId DataViews return req, nil } -// NewGetDefaultDataViewDefaultRequest generates requests for GetDefaultDataViewDefault -func NewGetDefaultDataViewDefaultRequest(server string) (*http.Request, error) { - var err error - - serverURL, err := url.Parse(server) - if err != nil { - return nil, err - } - - operationPath := fmt.Sprintf("/api/data_views/default") - if operationPath[0] == '/' { - operationPath = "." + operationPath - } - - queryURL, err := serverURL.Parse(operationPath) - if err != nil { - return nil, err - } - - req, err := http.NewRequest("GET", queryURL.String(), nil) - if err != nil { - return nil, err - } - - return req, nil -} - -// NewSetDefaultDatailViewDefaultRequest calls the generic SetDefaultDatailViewDefault builder with application/json body -func NewSetDefaultDatailViewDefaultRequest(server string, body SetDefaultDatailViewDefaultJSONRequestBody) (*http.Request, error) { - var bodyReader io.Reader - buf, err := json.Marshal(body) - if err != nil { - return nil, err - } - bodyReader = bytes.NewReader(buf) - return NewSetDefaultDatailViewDefaultRequestWithBody(server, "application/json", bodyReader) -} - -// NewSetDefaultDatailViewDefaultRequestWithBody generates requests for SetDefaultDatailViewDefault with any type of body -func NewSetDefaultDatailViewDefaultRequestWithBody(server string, contentType string, body io.Reader) (*http.Request, error) { - var err error - - serverURL, err := url.Parse(server) - if err != nil { - return nil, err - } - - operationPath := fmt.Sprintf("/api/data_views/default") - if operationPath[0] == '/' { - operationPath = "." + operationPath - } - - queryURL, err := serverURL.Parse(operationPath) - if err != nil { - return nil, err - } - - req, err := http.NewRequest("POST", queryURL.String(), body) - if err != nil { - return nil, err - } - - req.Header.Add("Content-Type", contentType) - - return req, nil -} - // NewSwapDataViewsDefaultRequest calls the generic SwapDataViewsDefault builder with application/json body func NewSwapDataViewsDefaultRequest(server string, body SwapDataViewsDefaultJSONRequestBody) (*http.Request, error) { var bodyReader io.Reader @@ -111901,6 +111834,87 @@ func NewUpdateDataViewDefaultRequestWithBody(server string, spaceId SpaceId, vie return req, nil } +// NewGetDefaultDataViewDefaultRequest generates requests for GetDefaultDataViewDefault +func NewGetDefaultDataViewDefaultRequest(server string, spaceId SpaceId) (*http.Request, error) { + var err error + + var pathParam0 string + + pathParam0, err = runtime.StyleParamWithLocation("simple", false, "spaceId", runtime.ParamLocationPath, spaceId) + if err != nil { + return nil, err + } + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/s/%s/api/data_views/default", pathParam0) + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + req, err := http.NewRequest("GET", queryURL.String(), nil) + if err != nil { + return nil, err + } + + return req, nil +} + +// NewSetDefaultDatailViewDefaultRequest calls the generic SetDefaultDatailViewDefault builder with application/json body +func NewSetDefaultDatailViewDefaultRequest(server string, spaceId SpaceId, body SetDefaultDatailViewDefaultJSONRequestBody) (*http.Request, error) { + var bodyReader io.Reader + buf, err := json.Marshal(body) + if err != nil { + return nil, err + } + bodyReader = bytes.NewReader(buf) + return NewSetDefaultDatailViewDefaultRequestWithBody(server, spaceId, "application/json", bodyReader) +} + +// NewSetDefaultDatailViewDefaultRequestWithBody generates requests for SetDefaultDatailViewDefault with any type of body +func NewSetDefaultDatailViewDefaultRequestWithBody(server string, spaceId SpaceId, contentType string, body io.Reader) (*http.Request, error) { + var err error + + var pathParam0 string + + pathParam0, err = runtime.StyleParamWithLocation("simple", false, "spaceId", runtime.ParamLocationPath, spaceId) + if err != nil { + return nil, err + } + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/s/%s/api/data_views/default", pathParam0) + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + req, err := http.NewRequest("POST", queryURL.String(), body) + if err != nil { + return nil, err + } + + req.Header.Add("Content-Type", contentType) + + return req, nil +} + // NewDeleteRuleRequest generates requests for DeleteRule func NewDeleteRuleRequest(server string, spaceId SpaceId, params *DeleteRuleParams) (*http.Request, error) { var err error @@ -114843,14 +114857,6 @@ type ClientWithResponsesInterface interface { UpdateRuntimeFieldDefaultWithResponse(ctx context.Context, viewId DataViewsViewId, fieldName DataViewsFieldName, body UpdateRuntimeFieldDefaultJSONRequestBody, reqEditors ...RequestEditorFn) (*UpdateRuntimeFieldDefaultResponse, error) - // GetDefaultDataViewDefaultWithResponse request - GetDefaultDataViewDefaultWithResponse(ctx context.Context, reqEditors ...RequestEditorFn) (*GetDefaultDataViewDefaultResponse, error) - - // SetDefaultDatailViewDefaultWithBodyWithResponse request with any body - SetDefaultDatailViewDefaultWithBodyWithResponse(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*SetDefaultDatailViewDefaultResponse, error) - - SetDefaultDatailViewDefaultWithResponse(ctx context.Context, body SetDefaultDatailViewDefaultJSONRequestBody, reqEditors ...RequestEditorFn) (*SetDefaultDatailViewDefaultResponse, error) - // SwapDataViewsDefaultWithBodyWithResponse request with any body SwapDataViewsDefaultWithBodyWithResponse(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*SwapDataViewsDefaultResponse, error) @@ -116379,6 +116385,14 @@ type ClientWithResponsesInterface interface { UpdateDataViewDefaultWithResponse(ctx context.Context, spaceId SpaceId, viewId DataViewsViewId, body UpdateDataViewDefaultJSONRequestBody, reqEditors ...RequestEditorFn) (*UpdateDataViewDefaultResponse, error) + // GetDefaultDataViewDefaultWithResponse request + GetDefaultDataViewDefaultWithResponse(ctx context.Context, spaceId SpaceId, reqEditors ...RequestEditorFn) (*GetDefaultDataViewDefaultResponse, error) + + // SetDefaultDatailViewDefaultWithBodyWithResponse request with any body + SetDefaultDatailViewDefaultWithBodyWithResponse(ctx context.Context, spaceId SpaceId, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*SetDefaultDatailViewDefaultResponse, error) + + SetDefaultDatailViewDefaultWithResponse(ctx context.Context, spaceId SpaceId, body SetDefaultDatailViewDefaultJSONRequestBody, reqEditors ...RequestEditorFn) (*SetDefaultDatailViewDefaultResponse, error) + // DeleteRuleWithResponse request DeleteRuleWithResponse(ctx context.Context, spaceId SpaceId, params *DeleteRuleParams, reqEditors ...RequestEditorFn) (*DeleteRuleResponse, error) @@ -120699,56 +120713,6 @@ func (r UpdateRuntimeFieldDefaultResponse) StatusCode() int { return 0 } -type GetDefaultDataViewDefaultResponse struct { - Body []byte - HTTPResponse *http.Response - JSON200 *struct { - DataViewId *string `json:"data_view_id,omitempty"` - } - JSON400 *DataViews400Response -} - -// Status returns HTTPResponse.Status -func (r GetDefaultDataViewDefaultResponse) Status() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Status - } - return http.StatusText(0) -} - -// StatusCode returns HTTPResponse.StatusCode -func (r GetDefaultDataViewDefaultResponse) StatusCode() int { - if r.HTTPResponse != nil { - return r.HTTPResponse.StatusCode - } - return 0 -} - -type SetDefaultDatailViewDefaultResponse struct { - Body []byte - HTTPResponse *http.Response - JSON200 *struct { - Acknowledged *bool `json:"acknowledged,omitempty"` - } - JSON400 *DataViews400Response -} - -// Status returns HTTPResponse.Status -func (r SetDefaultDatailViewDefaultResponse) Status() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Status - } - return http.StatusText(0) -} - -// StatusCode returns HTTPResponse.StatusCode -func (r SetDefaultDatailViewDefaultResponse) StatusCode() int { - if r.HTTPResponse != nil { - return r.HTTPResponse.StatusCode - } - return 0 -} - type SwapDataViewsDefaultResponse struct { Body []byte HTTPResponse *http.Response @@ -134433,6 +134397,56 @@ func (r UpdateDataViewDefaultResponse) StatusCode() int { return 0 } +type GetDefaultDataViewDefaultResponse struct { + Body []byte + HTTPResponse *http.Response + JSON200 *struct { + DataViewId *string `json:"data_view_id,omitempty"` + } + JSON400 *DataViews400Response +} + +// Status returns HTTPResponse.Status +func (r GetDefaultDataViewDefaultResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r GetDefaultDataViewDefaultResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +type SetDefaultDatailViewDefaultResponse struct { + Body []byte + HTTPResponse *http.Response + JSON200 *struct { + Acknowledged *bool `json:"acknowledged,omitempty"` + } + JSON400 *DataViews400Response +} + +// Status returns HTTPResponse.Status +func (r SetDefaultDatailViewDefaultResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r SetDefaultDatailViewDefaultResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + type DeleteRuleResponse struct { Body []byte HTTPResponse *http.Response @@ -136943,32 +136957,6 @@ func (c *ClientWithResponses) UpdateRuntimeFieldDefaultWithResponse(ctx context. return ParseUpdateRuntimeFieldDefaultResponse(rsp) } -// GetDefaultDataViewDefaultWithResponse request returning *GetDefaultDataViewDefaultResponse -func (c *ClientWithResponses) GetDefaultDataViewDefaultWithResponse(ctx context.Context, reqEditors ...RequestEditorFn) (*GetDefaultDataViewDefaultResponse, error) { - rsp, err := c.GetDefaultDataViewDefault(ctx, reqEditors...) - if err != nil { - return nil, err - } - return ParseGetDefaultDataViewDefaultResponse(rsp) -} - -// SetDefaultDatailViewDefaultWithBodyWithResponse request with arbitrary body returning *SetDefaultDatailViewDefaultResponse -func (c *ClientWithResponses) SetDefaultDatailViewDefaultWithBodyWithResponse(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*SetDefaultDatailViewDefaultResponse, error) { - rsp, err := c.SetDefaultDatailViewDefaultWithBody(ctx, contentType, body, reqEditors...) - if err != nil { - return nil, err - } - return ParseSetDefaultDatailViewDefaultResponse(rsp) -} - -func (c *ClientWithResponses) SetDefaultDatailViewDefaultWithResponse(ctx context.Context, body SetDefaultDatailViewDefaultJSONRequestBody, reqEditors ...RequestEditorFn) (*SetDefaultDatailViewDefaultResponse, error) { - rsp, err := c.SetDefaultDatailViewDefault(ctx, body, reqEditors...) - if err != nil { - return nil, err - } - return ParseSetDefaultDatailViewDefaultResponse(rsp) -} - // SwapDataViewsDefaultWithBodyWithResponse request with arbitrary body returning *SwapDataViewsDefaultResponse func (c *ClientWithResponses) SwapDataViewsDefaultWithBodyWithResponse(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*SwapDataViewsDefaultResponse, error) { rsp, err := c.SwapDataViewsDefaultWithBody(ctx, contentType, body, reqEditors...) @@ -141923,6 +141911,32 @@ func (c *ClientWithResponses) UpdateDataViewDefaultWithResponse(ctx context.Cont return ParseUpdateDataViewDefaultResponse(rsp) } +// GetDefaultDataViewDefaultWithResponse request returning *GetDefaultDataViewDefaultResponse +func (c *ClientWithResponses) GetDefaultDataViewDefaultWithResponse(ctx context.Context, spaceId SpaceId, reqEditors ...RequestEditorFn) (*GetDefaultDataViewDefaultResponse, error) { + rsp, err := c.GetDefaultDataViewDefault(ctx, spaceId, reqEditors...) + if err != nil { + return nil, err + } + return ParseGetDefaultDataViewDefaultResponse(rsp) +} + +// SetDefaultDatailViewDefaultWithBodyWithResponse request with arbitrary body returning *SetDefaultDatailViewDefaultResponse +func (c *ClientWithResponses) SetDefaultDatailViewDefaultWithBodyWithResponse(ctx context.Context, spaceId SpaceId, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*SetDefaultDatailViewDefaultResponse, error) { + rsp, err := c.SetDefaultDatailViewDefaultWithBody(ctx, spaceId, contentType, body, reqEditors...) + if err != nil { + return nil, err + } + return ParseSetDefaultDatailViewDefaultResponse(rsp) +} + +func (c *ClientWithResponses) SetDefaultDatailViewDefaultWithResponse(ctx context.Context, spaceId SpaceId, body SetDefaultDatailViewDefaultJSONRequestBody, reqEditors ...RequestEditorFn) (*SetDefaultDatailViewDefaultResponse, error) { + rsp, err := c.SetDefaultDatailViewDefault(ctx, spaceId, body, reqEditors...) + if err != nil { + return nil, err + } + return ParseSetDefaultDatailViewDefaultResponse(rsp) +} + // DeleteRuleWithResponse request returning *DeleteRuleResponse func (c *ClientWithResponses) DeleteRuleWithResponse(ctx context.Context, spaceId SpaceId, params *DeleteRuleParams, reqEditors ...RequestEditorFn) (*DeleteRuleResponse, error) { rsp, err := c.DeleteRule(ctx, spaceId, params, reqEditors...) @@ -147183,76 +147197,6 @@ func ParseUpdateRuntimeFieldDefaultResponse(rsp *http.Response) (*UpdateRuntimeF return response, nil } -// ParseGetDefaultDataViewDefaultResponse parses an HTTP response from a GetDefaultDataViewDefaultWithResponse call -func ParseGetDefaultDataViewDefaultResponse(rsp *http.Response) (*GetDefaultDataViewDefaultResponse, error) { - bodyBytes, err := io.ReadAll(rsp.Body) - defer func() { _ = rsp.Body.Close() }() - if err != nil { - return nil, err - } - - response := &GetDefaultDataViewDefaultResponse{ - Body: bodyBytes, - HTTPResponse: rsp, - } - - switch { - case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: - var dest struct { - DataViewId *string `json:"data_view_id,omitempty"` - } - if err := json.Unmarshal(bodyBytes, &dest); err != nil { - return nil, err - } - response.JSON200 = &dest - - case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 400: - var dest DataViews400Response - if err := json.Unmarshal(bodyBytes, &dest); err != nil { - return nil, err - } - response.JSON400 = &dest - - } - - return response, nil -} - -// ParseSetDefaultDatailViewDefaultResponse parses an HTTP response from a SetDefaultDatailViewDefaultWithResponse call -func ParseSetDefaultDatailViewDefaultResponse(rsp *http.Response) (*SetDefaultDatailViewDefaultResponse, error) { - bodyBytes, err := io.ReadAll(rsp.Body) - defer func() { _ = rsp.Body.Close() }() - if err != nil { - return nil, err - } - - response := &SetDefaultDatailViewDefaultResponse{ - Body: bodyBytes, - HTTPResponse: rsp, - } - - switch { - case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: - var dest struct { - Acknowledged *bool `json:"acknowledged,omitempty"` - } - if err := json.Unmarshal(bodyBytes, &dest); err != nil { - return nil, err - } - response.JSON200 = &dest - - case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 400: - var dest DataViews400Response - if err := json.Unmarshal(bodyBytes, &dest); err != nil { - return nil, err - } - response.JSON400 = &dest - - } - - return response, nil -} - // ParseSwapDataViewsDefaultResponse parses an HTTP response from a SwapDataViewsDefaultWithResponse call func ParseSwapDataViewsDefaultResponse(rsp *http.Response) (*SwapDataViewsDefaultResponse, error) { bodyBytes, err := io.ReadAll(rsp.Body) @@ -162029,6 +161973,76 @@ func ParseUpdateDataViewDefaultResponse(rsp *http.Response) (*UpdateDataViewDefa return response, nil } +// ParseGetDefaultDataViewDefaultResponse parses an HTTP response from a GetDefaultDataViewDefaultWithResponse call +func ParseGetDefaultDataViewDefaultResponse(rsp *http.Response) (*GetDefaultDataViewDefaultResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &GetDefaultDataViewDefaultResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + switch { + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: + var dest struct { + DataViewId *string `json:"data_view_id,omitempty"` + } + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON200 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 400: + var dest DataViews400Response + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON400 = &dest + + } + + return response, nil +} + +// ParseSetDefaultDatailViewDefaultResponse parses an HTTP response from a SetDefaultDatailViewDefaultWithResponse call +func ParseSetDefaultDatailViewDefaultResponse(rsp *http.Response) (*SetDefaultDatailViewDefaultResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &SetDefaultDatailViewDefaultResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + switch { + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: + var dest struct { + Acknowledged *bool `json:"acknowledged,omitempty"` + } + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON200 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 400: + var dest DataViews400Response + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON400 = &dest + + } + + return response, nil +} + // ParseDeleteRuleResponse parses an HTTP response from a DeleteRuleWithResponse call func ParseDeleteRuleResponse(rsp *http.Response) (*DeleteRuleResponse, error) { bodyBytes, err := io.ReadAll(rsp.Body) diff --git a/generated/kbapi/transform_schema.go b/generated/kbapi/transform_schema.go index 94d7ed207..05fbf0bca 100644 --- a/generated/kbapi/transform_schema.go +++ b/generated/kbapi/transform_schema.go @@ -691,6 +691,7 @@ func transformKibanaPaths(schema *Schema) { "/api/maintenance_window/{id}", "/api/actions/connector/{id}", "/api/actions/connectors", + "/api/data_views/default", "/api/detection_engine/rules", "/api/exception_lists", "/api/exception_lists/items", diff --git a/internal/clients/kibana_oapi/data_views.go b/internal/clients/kibana_oapi/data_views.go index 1126a73cc..baebc7808 100644 --- a/internal/clients/kibana_oapi/data_views.go +++ b/internal/clients/kibana_oapi/data_views.go @@ -87,3 +87,41 @@ func DeleteDataView(ctx context.Context, client *Client, spaceID string, viewID return reportUnknownError(resp.StatusCode(), resp.Body) } } + +// GetDefaultDataView reads the default data view from the API. +func GetDefaultDataView(ctx context.Context, client *Client, spaceID string) (*string, diag.Diagnostics) { + resp, err := client.API.GetDefaultDataViewDefaultWithResponse(ctx, spaceID) + if err != nil { + return nil, diagutil.FrameworkDiagFromError(err) + } + + // We don't check for a 404 here. The API doesn't document a 404 response for this endpoint. + // In testing, there's no case where a 404 is returned: + // - If no default data view is set, the API returns 200 with an empty string as the data view ID. + // - If the space doesn't exist, the API still returns 200 with an empty string as the data view ID. + // Therefore, we only handle the 200 response and treat any other status code as an error. + switch resp.StatusCode() { + case http.StatusOK: + if resp.JSON200 != nil && resp.JSON200.DataViewId != nil && *resp.JSON200.DataViewId != "" { + return resp.JSON200.DataViewId, nil + } + return nil, nil + default: + return nil, reportUnknownError(resp.StatusCode(), resp.Body) + } +} + +// SetDefaultDataView sets the default data view. +func SetDefaultDataView(ctx context.Context, client *Client, spaceID string, req kbapi.SetDefaultDatailViewDefaultJSONRequestBody) diag.Diagnostics { + resp, err := client.API.SetDefaultDatailViewDefaultWithResponse(ctx, spaceID, req) + if err != nil { + return diagutil.FrameworkDiagFromError(err) + } + + switch resp.StatusCode() { + case http.StatusOK: + return nil + default: + return reportUnknownError(resp.StatusCode(), resp.Body) + } +} diff --git a/internal/kibana/default_data_view/acc_test.go b/internal/kibana/default_data_view/acc_test.go new file mode 100644 index 000000000..1ccb316b6 --- /dev/null +++ b/internal/kibana/default_data_view/acc_test.go @@ -0,0 +1,129 @@ +package default_data_view_test + +import ( + "embed" + "testing" + + "github.com/elastic/terraform-provider-elasticstack/internal/acctest" + "github.com/elastic/terraform-provider-elasticstack/internal/versionutils" + "github.com/hashicorp/go-version" + "github.com/hashicorp/terraform-plugin-testing/config" + sdkacctest "github.com/hashicorp/terraform-plugin-testing/helper/acctest" + "github.com/hashicorp/terraform-plugin-testing/helper/resource" +) + +//go:embed test_data/*.tf +var testDataFS embed.FS + +var minDataViewAPISupport = version.Must(version.NewVersion("8.1.0")) + +// loadTestData reads and returns the content of a test data file +func loadTestData(filename string) string { + data, err := testDataFS.ReadFile("test_data/" + filename) + if err != nil { + panic("Failed to load test data file: " + filename + " - " + err.Error()) + } + return string(data) +} + +func TestAccResourceDefaultDataView(t *testing.T) { + indexName1 := "my-index-" + sdkacctest.RandStringFromCharSet(4, sdkacctest.CharSetAlphaNum) + indexName2 := "my-other-index-" + sdkacctest.RandStringFromCharSet(4, sdkacctest.CharSetAlphaNum) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(t) }, + ProtoV6ProviderFactories: acctest.Providers, + Steps: []resource.TestStep{ + { + SkipFunc: versionutils.CheckIfVersionIsUnsupported(minDataViewAPISupport), + Config: loadTestData("basic.tf"), + ConfigVariables: config.Variables{ + "index_name": config.StringVariable(indexName1), + }, + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr("elasticstack_kibana_default_data_view.test", "id", "default"), + resource.TestCheckResourceAttrSet("elasticstack_kibana_default_data_view.test", "data_view_id"), + resource.TestCheckResourceAttr("elasticstack_kibana_default_data_view.test", "force", "true"), + resource.TestCheckResourceAttr("elasticstack_kibana_default_data_view.test", "skip_delete", "false"), + resource.TestCheckResourceAttr("elasticstack_kibana_default_data_view.test", "space_id", "default"), + ), + }, + { + SkipFunc: versionutils.CheckIfVersionIsUnsupported(minDataViewAPISupport), + Config: loadTestData("update.tf"), + ConfigVariables: config.Variables{ + "index_name1": config.StringVariable(indexName1), + "index_name2": config.StringVariable(indexName2), + }, + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr("elasticstack_kibana_default_data_view.test", "id", "default"), + resource.TestCheckResourceAttrSet("elasticstack_kibana_default_data_view.test", "data_view_id"), + resource.TestCheckResourceAttr("elasticstack_kibana_default_data_view.test", "space_id", "default"), + ), + }, + { + SkipFunc: versionutils.CheckIfVersionIsUnsupported(minDataViewAPISupport), + Config: loadTestData("unset.tf"), + ConfigVariables: config.Variables{ + "index_name1": config.StringVariable(indexName1), + "index_name2": config.StringVariable(indexName2), + }, + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr("elasticstack_kibana_default_data_view.test", "id", "default"), + resource.TestCheckNoResourceAttr("elasticstack_kibana_default_data_view.test", "data_view_id"), + resource.TestCheckResourceAttr("elasticstack_kibana_default_data_view.test", "space_id", "default"), + ), + }, + }, + }) +} + +func TestAccResourceDefaultDataViewWithSkipDelete(t *testing.T) { + indexName := "my-index-" + sdkacctest.RandStringFromCharSet(4, sdkacctest.CharSetAlphaNum) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(t) }, + ProtoV6ProviderFactories: acctest.Providers, + Steps: []resource.TestStep{ + { + SkipFunc: versionutils.CheckIfVersionIsUnsupported(minDataViewAPISupport), + Config: loadTestData("skip_delete.tf"), + ConfigVariables: config.Variables{ + "index_name": config.StringVariable(indexName), + }, + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr("elasticstack_kibana_default_data_view.test", "id", "default"), + resource.TestCheckResourceAttr("elasticstack_kibana_default_data_view.test", "skip_delete", "true"), + resource.TestCheckResourceAttr("elasticstack_kibana_default_data_view.test", "space_id", "default"), + ), + }, + }, + }) +} + +func TestAccResourceDefaultDataViewWithCustomSpace(t *testing.T) { + indexName := "my-index-" + sdkacctest.RandStringFromCharSet(4, sdkacctest.CharSetAlphaNum) + spaceID := "test-space-" + sdkacctest.RandStringFromCharSet(6, sdkacctest.CharSetAlphaNum) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(t) }, + ProtoV6ProviderFactories: acctest.Providers, + Steps: []resource.TestStep{ + { + SkipFunc: versionutils.CheckIfVersionIsUnsupported(minDataViewAPISupport), + Config: loadTestData("custom_space.tf"), + ConfigVariables: config.Variables{ + "index_name": config.StringVariable(indexName), + "space_id": config.StringVariable(spaceID), + }, + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr("elasticstack_kibana_default_data_view.test", "id", spaceID), + resource.TestCheckResourceAttrSet("elasticstack_kibana_default_data_view.test", "data_view_id"), + resource.TestCheckResourceAttr("elasticstack_kibana_default_data_view.test", "force", "true"), + resource.TestCheckResourceAttr("elasticstack_kibana_default_data_view.test", "skip_delete", "false"), + resource.TestCheckResourceAttr("elasticstack_kibana_default_data_view.test", "space_id", spaceID), + ), + }, + }, + }) +} diff --git a/internal/kibana/default_data_view/create.go b/internal/kibana/default_data_view/create.go new file mode 100644 index 000000000..993d68d37 --- /dev/null +++ b/internal/kibana/default_data_view/create.go @@ -0,0 +1,53 @@ +package default_data_view + +import ( + "context" + + "github.com/elastic/terraform-provider-elasticstack/generated/kbapi" + "github.com/elastic/terraform-provider-elasticstack/internal/clients/kibana_oapi" + "github.com/hashicorp/terraform-plugin-framework/diag" + "github.com/hashicorp/terraform-plugin-framework/resource" + "github.com/hashicorp/terraform-plugin-framework/tfsdk" +) + +func (r *DefaultDataViewResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { + resp.Diagnostics.Append(r.setDefaultDataView(ctx, req.Plan, &resp.State)...) +} + +// setDefaultDataView is a helper method that contains the core logic for setting the default data view. +func (r *DefaultDataViewResource) setDefaultDataView(ctx context.Context, plan tfsdk.Plan, state *tfsdk.State) diag.Diagnostics { + var model defaultDataViewModel + diags := plan.Get(ctx, &model) + if diags.HasError() { + return diags + } + + client, err := r.client.GetKibanaOapiClient() + if err != nil { + diags.AddError("unable to get kibana client", err.Error()) + return diags + } + + dataViewID := model.DataViewID.ValueStringPointer() + force := model.Force.ValueBool() + spaceID := model.SpaceID.ValueString() + setReq := kbapi.SetDefaultDatailViewDefaultJSONRequestBody{ + DataViewId: dataViewID, + Force: &force, + } + + apiDiags := kibana_oapi.SetDefaultDataView(ctx, client, spaceID, setReq) + diags.Append(apiDiags...) + if diags.HasError() { + return diags + } + + model, readDiags := r.read(ctx, client, model) + diags.Append(readDiags...) + if diags.HasError() { + return diags + } + + diags = state.Set(ctx, model) + return diags +} diff --git a/internal/kibana/default_data_view/delete.go b/internal/kibana/default_data_view/delete.go new file mode 100644 index 000000000..bc32c0ebd --- /dev/null +++ b/internal/kibana/default_data_view/delete.go @@ -0,0 +1,40 @@ +package default_data_view + +import ( + "context" + + "github.com/elastic/terraform-provider-elasticstack/generated/kbapi" + "github.com/elastic/terraform-provider-elasticstack/internal/clients/kibana_oapi" + "github.com/elastic/terraform-provider-elasticstack/internal/utils" + "github.com/hashicorp/terraform-plugin-framework/resource" +) + +func (r *DefaultDataViewResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { + var state defaultDataViewModel + diags := req.State.Get(ctx, &state) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + // If skip_delete is true, leave the default data view unchanged + if state.SkipDelete.ValueBool() { + return + } + + client, err := r.client.GetKibanaOapiClient() + if err != nil { + resp.Diagnostics.AddError("unable to get kibana client", err.Error()) + return + } + + spaceID := state.SpaceID.ValueString() + + // Unset the default data view by setting it to null + setReq := kbapi.SetDefaultDatailViewDefaultJSONRequestBody{ + Force: utils.Pointer(true), + } + + diags = kibana_oapi.SetDefaultDataView(ctx, client, spaceID, setReq) + resp.Diagnostics.Append(diags...) +} diff --git a/internal/kibana/default_data_view/models.go b/internal/kibana/default_data_view/models.go new file mode 100644 index 000000000..f1dc34c65 --- /dev/null +++ b/internal/kibana/default_data_view/models.go @@ -0,0 +1,13 @@ +package default_data_view + +import ( + "github.com/hashicorp/terraform-plugin-framework/types" +) + +type defaultDataViewModel struct { + ID types.String `tfsdk:"id"` + DataViewID types.String `tfsdk:"data_view_id"` + Force types.Bool `tfsdk:"force"` + SkipDelete types.Bool `tfsdk:"skip_delete"` + SpaceID types.String `tfsdk:"space_id"` +} diff --git a/internal/kibana/default_data_view/read.go b/internal/kibana/default_data_view/read.go new file mode 100644 index 000000000..f9a43733a --- /dev/null +++ b/internal/kibana/default_data_view/read.go @@ -0,0 +1,50 @@ +package default_data_view + +import ( + "context" + + "github.com/elastic/terraform-provider-elasticstack/internal/clients/kibana_oapi" + "github.com/hashicorp/terraform-plugin-framework/diag" + "github.com/hashicorp/terraform-plugin-framework/resource" + "github.com/hashicorp/terraform-plugin-framework/types" +) + +func (r *DefaultDataViewResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { + var state defaultDataViewModel + diags := req.State.Get(ctx, &state) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + client, err := r.client.GetKibanaOapiClient() + if err != nil { + resp.Diagnostics.AddError("unable to get kibana client", err.Error()) + return + } + + state, diags = r.read(ctx, client, state) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + diags = resp.State.Set(ctx, state) + resp.Diagnostics.Append(diags...) +} + +func (r *DefaultDataViewResource) read(ctx context.Context, client *kibana_oapi.Client, state defaultDataViewModel) (defaultDataViewModel, diag.Diagnostics) { + spaceID := state.SpaceID.ValueString() + defaultDataViewID, diags := kibana_oapi.GetDefaultDataView(ctx, client, spaceID) + if diags.HasError() { + return state, diags + } + + // Update state with current default data view + state.DataViewID = types.StringPointerValue(defaultDataViewID) + + // Use the space_id as the resource ID + state.ID = types.StringValue(spaceID) + + return state, nil +} diff --git a/internal/kibana/default_data_view/resource.go b/internal/kibana/default_data_view/resource.go new file mode 100644 index 000000000..dd5d262a0 --- /dev/null +++ b/internal/kibana/default_data_view/resource.go @@ -0,0 +1,33 @@ +package default_data_view + +import ( + "context" + "fmt" + + "github.com/elastic/terraform-provider-elasticstack/internal/clients" + "github.com/hashicorp/terraform-plugin-framework/resource" +) + +var ( + _ resource.Resource = &DefaultDataViewResource{} + _ resource.ResourceWithConfigure = &DefaultDataViewResource{} +) + +// NewResource is a helper function to simplify the provider implementation. +func NewResource() resource.Resource { + return &DefaultDataViewResource{} +} + +type DefaultDataViewResource struct { + client *clients.ApiClient +} + +func (r *DefaultDataViewResource) Configure(ctx context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { + client, diags := clients.ConvertProviderData(req.ProviderData) + resp.Diagnostics.Append(diags...) + r.client = client +} + +func (r *DefaultDataViewResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { + resp.TypeName = fmt.Sprintf("%s_%s", req.ProviderTypeName, "kibana_default_data_view") +} diff --git a/internal/kibana/default_data_view/schema.go b/internal/kibana/default_data_view/schema.go new file mode 100644 index 000000000..c7603f80c --- /dev/null +++ b/internal/kibana/default_data_view/schema.go @@ -0,0 +1,59 @@ +package default_data_view + +import ( + "context" + + "github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator" + "github.com/hashicorp/terraform-plugin-framework/resource" + "github.com/hashicorp/terraform-plugin-framework/resource/schema" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" + "github.com/hashicorp/terraform-plugin-framework/schema/validator" +) + +func (r *DefaultDataViewResource) Schema(_ context.Context, _ resource.SchemaRequest, resp *resource.SchemaResponse) { + resp.Schema = getSchema() +} + +func getSchema() schema.Schema { + return schema.Schema{ + MarkdownDescription: "Manages the default Kibana data view. See the [Kibana Data Views API documentation](https://www.elastic.co/docs/api/doc/kibana/v8/operation/operation-setdefaultdatailviewdefault) for more information.", + Attributes: map[string]schema.Attribute{ + "id": schema.StringAttribute{ + Computed: true, + MarkdownDescription: "Internal identifier of the resource.", + PlanModifiers: []planmodifier.String{ + stringplanmodifier.UseStateForUnknown(), + }, + }, + "data_view_id": schema.StringAttribute{ + MarkdownDescription: "The data view identifier to set as default. NOTE: The API does not validate whether it is a valid identifier. Leave this unset (or explicitly `null`) to unset the default data view.", + Optional: true, + Validators: []validator.String{ + stringvalidator.LengthAtLeast(1), + }, + }, + "force": schema.BoolAttribute{ + MarkdownDescription: "Update an existing default data view identifier. If set to false and a default data view already exists, the operation will fail.", + Optional: true, + }, + "skip_delete": schema.BoolAttribute{ + MarkdownDescription: "If set to true, the default data view will not be unset when the resource is destroyed. The existing default data view will remain unchanged.", + Optional: true, + Computed: true, + Default: booldefault.StaticBool(false), + }, + "space_id": schema.StringAttribute{ + MarkdownDescription: "The Kibana space ID to set the default data view in. Defaults to `default`.", + Optional: true, + Computed: true, + Default: stringdefault.StaticString("default"), + PlanModifiers: []planmodifier.String{ + stringplanmodifier.RequiresReplace(), + }, + }, + }, + } +} diff --git a/internal/kibana/default_data_view/test_data/basic.tf b/internal/kibana/default_data_view/test_data/basic.tf new file mode 100644 index 000000000..1f3ab00dc --- /dev/null +++ b/internal/kibana/default_data_view/test_data/basic.tf @@ -0,0 +1,26 @@ +variable "index_name" { + description = "The name of the Elasticsearch index" + type = string +} + +provider "elasticstack" { + elasticsearch {} + kibana {} +} + +resource "elasticstack_elasticsearch_index" "my_index" { + name = var.index_name + deletion_protection = false +} + +resource "elasticstack_kibana_data_view" "dv" { + data_view = { + title = "${var.index_name}*" + } + depends_on = [elasticstack_elasticsearch_index.my_index] +} + +resource "elasticstack_kibana_default_data_view" "test" { + data_view_id = elasticstack_kibana_data_view.dv.data_view.id + force = true +} \ No newline at end of file diff --git a/internal/kibana/default_data_view/test_data/custom_space.tf b/internal/kibana/default_data_view/test_data/custom_space.tf new file mode 100644 index 000000000..10878d9e3 --- /dev/null +++ b/internal/kibana/default_data_view/test_data/custom_space.tf @@ -0,0 +1,39 @@ +variable "index_name" { + description = "The name of the Elasticsearch index" + type = string +} + +variable "space_id" { + description = "The ID of the Kibana space" + type = string +} + +provider "elasticstack" { + elasticsearch {} + kibana {} +} + +resource "elasticstack_kibana_space" "test_space" { + space_id = var.space_id + name = "Test Space ${var.space_id}" + description = "Test space for default data view" +} + +resource "elasticstack_elasticsearch_index" "my_index" { + name = var.index_name + deletion_protection = false +} + +resource "elasticstack_kibana_data_view" "dv" { + space_id = elasticstack_kibana_space.test_space.space_id + data_view = { + title = "${var.index_name}*" + } + depends_on = [elasticstack_elasticsearch_index.my_index] +} + +resource "elasticstack_kibana_default_data_view" "test" { + space_id = elasticstack_kibana_space.test_space.space_id + data_view_id = elasticstack_kibana_data_view.dv.data_view.id + force = true +} \ No newline at end of file diff --git a/internal/kibana/default_data_view/test_data/skip_delete.tf b/internal/kibana/default_data_view/test_data/skip_delete.tf new file mode 100644 index 000000000..9f7fba451 --- /dev/null +++ b/internal/kibana/default_data_view/test_data/skip_delete.tf @@ -0,0 +1,27 @@ +variable "index_name" { + description = "The name of the Elasticsearch index" + type = string +} + +provider "elasticstack" { + elasticsearch {} + kibana {} +} + +resource "elasticstack_elasticsearch_index" "my_index" { + name = var.index_name + deletion_protection = false +} + +resource "elasticstack_kibana_data_view" "dv" { + data_view = { + title = "${var.index_name}*" + } + depends_on = [elasticstack_elasticsearch_index.my_index] +} + +resource "elasticstack_kibana_default_data_view" "test" { + data_view_id = elasticstack_kibana_data_view.dv.data_view.id + force = true + skip_delete = true +} \ No newline at end of file diff --git a/internal/kibana/default_data_view/test_data/unset.tf b/internal/kibana/default_data_view/test_data/unset.tf new file mode 100644 index 000000000..3f469ded0 --- /dev/null +++ b/internal/kibana/default_data_view/test_data/unset.tf @@ -0,0 +1,42 @@ +variable "index_name1" { + description = "The name of the first Elasticsearch index" + type = string +} + +variable "index_name2" { + description = "The name of the second Elasticsearch index" + type = string +} + +provider "elasticstack" { + elasticsearch {} + kibana {} +} + +resource "elasticstack_elasticsearch_index" "my_index" { + name = var.index_name1 + deletion_protection = false +} + +resource "elasticstack_elasticsearch_index" "my_other_index" { + name = var.index_name2 + deletion_protection = false +} + +resource "elasticstack_kibana_data_view" "dv" { + data_view = { + title = "${var.index_name1}*" + } + depends_on = [elasticstack_elasticsearch_index.my_index] +} + +resource "elasticstack_kibana_data_view" "dv2" { + data_view = { + title = "${var.index_name2}*" + } + depends_on = [elasticstack_elasticsearch_index.my_other_index] +} + +resource "elasticstack_kibana_default_data_view" "test" { + force = true +} \ No newline at end of file diff --git a/internal/kibana/default_data_view/test_data/update.tf b/internal/kibana/default_data_view/test_data/update.tf new file mode 100644 index 000000000..53cd380a4 --- /dev/null +++ b/internal/kibana/default_data_view/test_data/update.tf @@ -0,0 +1,43 @@ +variable "index_name1" { + description = "The name of the first Elasticsearch index" + type = string +} + +variable "index_name2" { + description = "The name of the second Elasticsearch index" + type = string +} + +provider "elasticstack" { + elasticsearch {} + kibana {} +} + +resource "elasticstack_elasticsearch_index" "my_index" { + name = var.index_name1 + deletion_protection = false +} + +resource "elasticstack_elasticsearch_index" "my_other_index" { + name = var.index_name2 + deletion_protection = false +} + +resource "elasticstack_kibana_data_view" "dv" { + data_view = { + title = "${var.index_name1}*" + } + depends_on = [elasticstack_elasticsearch_index.my_index] +} + +resource "elasticstack_kibana_data_view" "dv2" { + data_view = { + title = "${var.index_name2}*" + } + depends_on = [elasticstack_elasticsearch_index.my_other_index] +} + +resource "elasticstack_kibana_default_data_view" "test" { + data_view_id = elasticstack_kibana_data_view.dv2.data_view.id + force = true +} \ No newline at end of file diff --git a/internal/kibana/default_data_view/update.go b/internal/kibana/default_data_view/update.go new file mode 100644 index 000000000..49d327384 --- /dev/null +++ b/internal/kibana/default_data_view/update.go @@ -0,0 +1,11 @@ +package default_data_view + +import ( + "context" + + "github.com/hashicorp/terraform-plugin-framework/resource" +) + +func (r *DefaultDataViewResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { + resp.Diagnostics.Append(r.setDefaultDataView(ctx, req.Plan, &resp.State)...) +} diff --git a/provider/plugin_framework.go b/provider/plugin_framework.go index e6aa43a00..4560dea04 100644 --- a/provider/plugin_framework.go +++ b/provider/plugin_framework.go @@ -29,6 +29,7 @@ import ( "github.com/elastic/terraform-provider-elasticstack/internal/fleet/server_host" "github.com/elastic/terraform-provider-elasticstack/internal/kibana/connectors" "github.com/elastic/terraform-provider-elasticstack/internal/kibana/data_view" + "github.com/elastic/terraform-provider-elasticstack/internal/kibana/default_data_view" "github.com/elastic/terraform-provider-elasticstack/internal/kibana/export_saved_objects" "github.com/elastic/terraform-provider-elasticstack/internal/kibana/import_saved_objects" "github.com/elastic/terraform-provider-elasticstack/internal/kibana/maintenance_window" @@ -121,6 +122,7 @@ func (p *Provider) resources(ctx context.Context) []func() resource.Resource { agent_configuration.NewAgentConfigurationResource, func() resource.Resource { return &import_saved_objects.Resource{} }, data_view.NewResource, + default_data_view.NewResource, func() resource.Resource { return ¶meter.Resource{} }, func() resource.Resource { return &private_location.Resource{} }, func() resource.Resource { return &index.Resource{} },