diff --git a/.github/workflows/copilot-setup-steps.yml b/.github/workflows/copilot-setup-steps.yml index 6e2478f2b..cc08bc2b9 100644 --- a/.github/workflows/copilot-setup-steps.yml +++ b/.github/workflows/copilot-setup-steps.yml @@ -21,62 +21,6 @@ jobs: permissions: # If you want to clone the repository as part of your setup steps, for example to install dependencies, you'll need the `contents: read` permission. If you don't clone the repository in your setup steps, Copilot will do this for you automatically after the steps complete. contents: read - env: - ELASTICSEARCH_ENDPOINTS: "http://localhost:9200" - ELASTICSEARCH_USERNAME: "elastic" - ELASTICSEARCH_PASSWORD: password - KIBANA_ENDPOINT: "http://localhost:5601" - KIBANA_USERNAME: "elastic" - KIBANA_PASSWORD: password - KIBANA_SYSTEM_USERNAME: kibana_system - KIBANA_SYSTEM_PASSWORD: password - TF_ACC: "1" - services: - elasticsearch: - image: docker.elastic.co/elasticsearch/elasticsearch:9.1.2@sha256:d1a8016cf55be8ffec635ed69f5a9acb0c459db35b46a4549ec5b2847a2f170a - env: - discovery.type: single-node - xpack.security.enabled: true - xpack.security.authc.api_key.enabled: true - xpack.security.authc.token.enabled: true - xpack.watcher.enabled: true - xpack.license.self_generated.type: trial - repositories.url.allowed_urls: https://example.com/* - path.repo: /tmp - ELASTIC_PASSWORD: ${{ env.ELASTICSEARCH_PASSWORD }} - ports: - - 9200:9200 - options: --health-cmd="curl http://localhost:9200/_cluster/health" --health-interval=10s --health-timeout=5s --health-retries=10 - kibana: - image: docker.elastic.co/kibana/kibana:9.1.3@sha256:26792c8e4a68ba0bff3efcc46755f60bf36bb16b2431014c210f2546ca1819ad - env: - SERVER_NAME: kibana - ELASTICSEARCH_HOSTS: http://elasticsearch:9200 - ELASTICSEARCH_USERNAME: ${{ env.KIBANA_SYSTEM_USERNAME }} - ELASTICSEARCH_PASSWORD: ${{ env.KIBANA_SYSTEM_PASSWORD }} - XPACK_ENCRYPTEDSAVEDOBJECTS_ENCRYPTIONKEY: a7a6311933d3503b89bc2dbc36572c33a6c10925682e591bffcab6911c06786d - # LOGGING_ROOT_LEVEL: debug - ports: - - 5601:5601 - options: --health-cmd="curl http://localhost:5601/api/status" --health-interval=10s --health-timeout=5s --health-retries=10 - fleet: - image: docker.elastic.co/elastic-agent/elastic-agent:9.1.2@sha256:942aa0ffe94c268aab83881fc8be0ca0af079c395820ce8e7552f0ce97e0a760 - env: - SERVER_NAME: fleet - FLEET_ENROLL: "1" - FLEET_URL: https://fleet:8220 - FLEET_INSECURE: "true" - FLEET_SERVER_ENABLE: "1" - FLEET_SERVER_POLICY_ID: fleet-server - FLEET_SERVER_ELASTICSEARCH_HOST: http://elasticsearch:9200 - FLEET_SERVER_ELASTICSEARCH_INSECURE: "true" - FLEET_SERVER_INSECURE_HTTP: "true" - KIBANA_HOST: http://kibana:5601 - KIBANA_FLEET_SETUP: "1" - KIBANA_FLEET_PASSWORD: ${{ env.ELASTICSEARCH_PASSWORD }} - ports: - - 8220:8220 - options: --restart="unless-stopped" steps: - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5 @@ -88,6 +32,9 @@ jobs: with: terraform_wrapper: false + - name: Setup Elastic Stack + run: make docker-fleet + - name: Get dependencies run: make setup diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index a44ac8897..cf08fedc0 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -126,6 +126,7 @@ jobs: - '8.15.5' - '8.16.2' - '8.17.0' + - '8.18.3' - '9.0.3' steps: - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5 diff --git a/CHANGELOG.md b/CHANGELOG.md index 01b9fadea..9a829c7ac 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,6 @@ ## [Unreleased] +- Add support for `solution` field in `elasticstack_kibana_space` resource and data source ([#1102](https://github.com/elastic/terraform-provider-elasticstack/issues/1102)) - Add `slo_id` validation to `elasticstack_kibana_slo` ([#1221](https://github.com/elastic/terraform-provider-elasticstack/pull/1221)) - Add `ignore_missing_component_templates` to `elasticstack_elasticsearch_index_template` ([#1206](https://github.com/elastic/terraform-provider-elasticstack/pull/1206)) - Prevent provider panic when a script exists in state, but not in Elasticsearch ([#1218](https://github.com/elastic/terraform-provider-elasticstack/pull/1218)) diff --git a/docs/data-sources/kibana_spaces.md b/docs/data-sources/kibana_spaces.md index 4bb1d0120..efcfdc282 100644 --- a/docs/data-sources/kibana_spaces.md +++ b/docs/data-sources/kibana_spaces.md @@ -41,11 +41,12 @@ Required: Optional: - `description` (String) The description for the space. -- `disabled_features` (List of String) The list of disabled features for the space. To get a list of available feature IDs, use the Features API (https://www.elastic.co/guide/en/kibana/master/features-api-get.html). - `image_url` (String) The data-URL encoded image to display in the space avatar. Read-Only: - `color` (String) The hexadecimal color code used in the space avatar. By default, the color is automatically generated from the space name. +- `disabled_features` (List of String) The list of disabled features for the space. To get a list of available feature IDs, use the Features API (https://www.elastic.co/guide/en/kibana/master/features-api-get.html). - `id` (String) Internal identifier of the resource. - `initials` (String) The initials shown in the space avatar. By default, the initials are automatically generated from the space name. Initials must be 1 or 2 characters. +- `solution` (String) The solution view for the space. Valid options are `security`, `oblt`, `es`, or `classic`. diff --git a/docs/resources/kibana_space.md b/docs/resources/kibana_space.md index 8dbca8e03..c8b1b30a1 100644 --- a/docs/resources/kibana_space.md +++ b/docs/resources/kibana_space.md @@ -41,6 +41,7 @@ resource "elasticstack_kibana_space" "example" { - `disabled_features` (Set of String) The list of disabled features for the space. To get a list of available feature IDs, use the Features API (https://www.elastic.co/guide/en/kibana/master/features-api-get.html). - `image_url` (String) The data-URL encoded image to display in the space avatar. - `initials` (String) The initials shown in the space avatar. By default, the initials are automatically generated from the space name. Initials must be 1 or 2 characters. +- `solution` (String) The solution view for the space. Valid options are `security`, `oblt`, `es`, or `classic`. ### Read-Only diff --git a/internal/kibana/space.go b/internal/kibana/space.go index 55ea8f7c8..908ea1358 100644 --- a/internal/kibana/space.go +++ b/internal/kibana/space.go @@ -6,11 +6,14 @@ import ( "github.com/disaster37/go-kibana-rest/v8/kbapi" "github.com/elastic/terraform-provider-elasticstack/internal/clients" + "github.com/hashicorp/go-version" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" ) +var SpaceSolutionMinVersion = version.Must(version.NewVersion("8.18.0")) + func ResourceSpace() *schema.Resource { apikeySchema := map[string]*schema.Schema{ "id": { @@ -38,6 +41,7 @@ func ResourceSpace() *schema.Resource { Description: "The list of disabled features for the space. To get a list of available feature IDs, use the Features API (https://www.elastic.co/guide/en/kibana/master/features-api-get.html).", Type: schema.TypeSet, Optional: true, + Computed: true, Elem: &schema.Schema{ Type: schema.TypeString, }, @@ -61,6 +65,13 @@ func ResourceSpace() *schema.Resource { Optional: true, ValidateFunc: validation.StringMatch(regexp.MustCompile("^data:image/"), "must be a valid data-URL encoded image"), }, + "solution": { + Description: "The solution view for the space. Valid options are `security`, `oblt`, `es`, or `classic`.", + Type: schema.TypeString, + Optional: true, + Computed: true, + ValidateFunc: validation.StringInSlice([]string{"security", "oblt", "es", "classic"}, false), + }, } return &schema.Resource{ @@ -90,6 +101,18 @@ func resourceSpaceUpsert(ctx context.Context, d *schema.ResourceData, meta inter return diag.FromErr(err) } + // Check version compatibility for solution field + if solution, ok := d.GetOk("solution"); ok && solution.(string) != "" { + serverVersion, diags := client.ServerVersion(ctx) + if diags.HasError() { + return diags + } + + if !serverVersion.GreaterThanOrEqual(SpaceSolutionMinVersion) { + return diag.Errorf("solution field is not supported in this version of the Elastic Stack. Solution field requires %s or higher", SpaceSolutionMinVersion) + } + } + space := kbapi.KibanaSpace{ ID: d.Get("space_id").(string), Name: d.Get("name").(string), @@ -120,6 +143,10 @@ func resourceSpaceUpsert(ctx context.Context, d *schema.ResourceData, meta inter space.ImageURL = imageUrl.(string) } + if solution, ok := d.GetOk("solution"); ok { + space.Solution = solution.(string) + } + var spaceResponse *kbapi.KibanaSpace if d.IsNewResource() { @@ -182,6 +209,9 @@ func resourceSpaceRead(ctx context.Context, d *schema.ResourceData, meta interfa if err := d.Set("color", space.Color); err != nil { return diag.FromErr(err) } + if err := d.Set("solution", space.Solution); err != nil { + return diag.FromErr(err) + } return diags } diff --git a/internal/kibana/space_test.go b/internal/kibana/space_test.go index 9a53286f0..2f3a2cb51 100644 --- a/internal/kibana/space_test.go +++ b/internal/kibana/space_test.go @@ -6,6 +6,8 @@ import ( "github.com/elastic/terraform-provider-elasticstack/internal/acctest" "github.com/elastic/terraform-provider-elasticstack/internal/clients" + "github.com/elastic/terraform-provider-elasticstack/internal/kibana" + "github.com/elastic/terraform-provider-elasticstack/internal/versionutils" sdkacctest "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" @@ -39,6 +41,16 @@ func TestAccResourceSpace(t *testing.T) { resource.TestCheckResourceAttrSet("elasticstack_kibana_space.test_space", "image_url"), ), }, + { + Config: testAccResourceSpaceWithSolution(spaceId), + SkipFunc: versionutils.CheckIfVersionIsUnsupported(kibana.SpaceSolutionMinVersion), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr("elasticstack_kibana_space.test_space", "space_id", spaceId), + resource.TestCheckResourceAttr("elasticstack_kibana_space.test_space", "name", fmt.Sprintf("Solution %s", spaceId)), + resource.TestCheckResourceAttr("elasticstack_kibana_space.test_space", "description", "Test Space with Solution"), + resource.TestCheckResourceAttr("elasticstack_kibana_space.test_space", "solution", "security"), + ), + }, { Config: testAccResourceSpaceCreate(spaceId), Check: resource.ComposeTestCheckFunc( @@ -83,6 +95,21 @@ resource "elasticstack_kibana_space" "test_space" { `, id, fmt.Sprintf("Updated %s", id)) } +func testAccResourceSpaceWithSolution(id string) string { + return fmt.Sprintf(` +provider "elasticstack" { + kibana {} +} + +resource "elasticstack_kibana_space" "test_space" { + space_id = "%s" + name = "%s" + description = "Test Space with Solution" + solution = "security" +} + `, id, fmt.Sprintf("Solution %s", id)) +} + func checkResourceSpaceDestroy(s *terraform.State) error { client, err := clients.NewAcceptanceTestingClient() if err != nil { diff --git a/internal/kibana/spaces/models.go b/internal/kibana/spaces/models.go index e7187566f..4503ddf95 100644 --- a/internal/kibana/spaces/models.go +++ b/internal/kibana/spaces/models.go @@ -17,4 +17,5 @@ type model struct { Initials types.String `tfsdk:"initials"` Color types.String `tfsdk:"color"` ImageUrl types.String `tfsdk:"image_url"` + Solution types.String `tfsdk:"solution"` } diff --git a/internal/kibana/spaces/read.go b/internal/kibana/spaces/read.go index ee7879a81..beaca5772 100644 --- a/internal/kibana/spaces/read.go +++ b/internal/kibana/spaces/read.go @@ -27,6 +27,7 @@ func (d *dataSource) Read(ctx context.Context, req datasource.ReadRequest, resp Initials: types.StringValue(space.Initials), Color: types.StringValue(space.Color), ImageUrl: types.StringValue(space.ImageURL), + Solution: types.StringValue(space.Solution), } disabledFeatures, diags := types.ListValueFrom(ctx, types.StringType, space.DisabledFeatures) diff --git a/internal/kibana/spaces/schema.go b/internal/kibana/spaces/schema.go index bb08c3958..4b45988a6 100644 --- a/internal/kibana/spaces/schema.go +++ b/internal/kibana/spaces/schema.go @@ -37,7 +37,7 @@ func (d *dataSource) Schema(_ context.Context, _ datasource.SchemaRequest, resp "disabled_features": schema.ListAttribute{ Description: "The list of disabled features for the space. To get a list of available feature IDs, use the Features API (https://www.elastic.co/guide/en/kibana/master/features-api-get.html).", ElementType: types.StringType, - Optional: true, + Computed: true, }, "initials": schema.StringAttribute{ Description: "The initials shown in the space avatar. By default, the initials are automatically generated from the space name. Initials must be 1 or 2 characters.", @@ -51,6 +51,10 @@ func (d *dataSource) Schema(_ context.Context, _ datasource.SchemaRequest, resp Description: "The data-URL encoded image to display in the space avatar.", Optional: true, }, + "solution": schema.StringAttribute{ + Description: "The solution view for the space. Valid options are `security`, `oblt`, `es`, or `classic`.", + Computed: true, + }, }, }, }, diff --git a/libs/go-kibana-rest/kbapi/api.kibana_spaces.go b/libs/go-kibana-rest/kbapi/api.kibana_spaces.go index 02d9d6374..9b1e7a9c7 100644 --- a/libs/go-kibana-rest/kbapi/api.kibana_spaces.go +++ b/libs/go-kibana-rest/kbapi/api.kibana_spaces.go @@ -23,6 +23,7 @@ type KibanaSpace struct { Initials string `json:"initials,omitempty"` Color string `json:"color,omitempty"` ImageURL string `json:"imageUrl,omitempty"` + Solution string `json:"solution,omitempty"` } // KibanaSpaces is the list of KibanaSpace object diff --git a/libs/go-kibana-rest/kbapi/api.kibana_spaces_test.go b/libs/go-kibana-rest/kbapi/api.kibana_spaces_test.go index c7a4f9a26..6c7f80e30 100644 --- a/libs/go-kibana-rest/kbapi/api.kibana_spaces_test.go +++ b/libs/go-kibana-rest/kbapi/api.kibana_spaces_test.go @@ -22,6 +22,7 @@ func (s *KBAPITestSuite) TestKibanaSpaces() { ID: "test", Name: "test", Description: "My test", + Solution: "security", } kibanaSpace, err = s.KibanaSpaces.Create(kibanaSpace) assert.NoError(s.T(), err)