Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions docs/resources/elasticsearch_project.md
Original file line number Diff line number Diff line change
Expand Up @@ -90,3 +90,5 @@ Projects can be imported using the `id`, for example:
```shell
terraform import ec_elasticsearch_project.id 320b7b540dfc967a7a649c18e2fce4ed
```

~> **Note on Credentials** The `credentials` attribute (containing `username` and `password`) is only available when the project is first created. When importing an existing project, these credentials will not be available in the Terraform state as the API does not return them on read operations.
2 changes: 2 additions & 0 deletions docs/resources/observability_project.md
Original file line number Diff line number Diff line change
Expand Up @@ -80,3 +80,5 @@ Projects can be imported using the `id`, for example:
```shell
terraform import ec_observability_project.id 320b7b540dfc967a7a649c18e2fce4ed
```

~> **Note on Credentials** The `credentials` attribute (containing `username` and `password`) is only available when the project is first created. When importing an existing project, these credentials will not be available in the Terraform state as the API does not return them on read operations.
2 changes: 2 additions & 0 deletions docs/resources/security_project.md
Original file line number Diff line number Diff line change
Expand Up @@ -90,3 +90,5 @@ Projects can be imported using the `id`, for example:
```shell
terraform import ec_security_project.id 320b7b540dfc967a7a649c18e2fce4ed
```

~> **Note on Credentials** The `credentials` attribute (containing `username` and `password`) is only available when the project is first created. When importing an existing project, these credentials will not be available in the Terraform state as the API does not return them on read operations.
53 changes: 53 additions & 0 deletions ec/acc/elasticsearch_project_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,13 @@ func TestAcc_ElasticsearchProject(t *testing.T) {
resource.TestCheckResourceAttrSet(resourceName, "cloud_id"),
),
},
{
// Test import.
ResourceName: resourceName,
ImportState: true,
ImportStateVerify: true,
ImportStateVerifyIgnore: []string{"credentials"},
},
},
})
}
Expand All @@ -106,6 +113,52 @@ resource ec_elasticsearch_project "%s" {
`, id, name, region, alias)
}

func TestAcc_ElasticsearchProjectImport(t *testing.T) {
resId := "import_project"
resourceName := fmt.Sprintf("ec_elasticsearch_project.%s", resId)
randomName := prefix + acctest.RandStringFromCharSet(10, acctest.CharSetAlphaNum)
region := getRegion()
if !strings.HasPrefix("aws-", region) {
region = fmt.Sprintf("aws-%s", region)
}

resource.ParallelTest(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
ProtoV6ProviderFactories: testAccProviderFactory,
CheckDestroy: testAccElasticsearchProjectDestroy,
Steps: []resource.TestStep{
{
// Create a project to import.
Config: testAccBasicElasticsearchProject(resId, randomName, region),
Check: resource.ComposeAggregateTestCheckFunc(
resource.TestCheckResourceAttr(resourceName, "name", randomName),
resource.TestCheckResourceAttrSet(resourceName, "id"),
),
},
{
// Import the project and verify all attributes.
ResourceName: resourceName,
ImportState: true,
ImportStateVerify: true,
ImportStateVerifyIgnore: []string{"credentials"},
Check: resource.ComposeAggregateTestCheckFunc(
resource.TestCheckResourceAttr(resourceName, "name", randomName),
resource.TestCheckResourceAttr(resourceName, "region_id", region),
resource.TestCheckResourceAttrSet(resourceName, "id"),
resource.TestCheckResourceAttrSet(resourceName, "alias"),
resource.TestCheckResourceAttrSet(resourceName, "cloud_id"),
resource.TestCheckResourceAttrSet(resourceName, "endpoints.elasticsearch"),
resource.TestCheckResourceAttrSet(resourceName, "endpoints.kibana"),
resource.TestCheckResourceAttrSet(resourceName, "metadata.created_at"),
resource.TestCheckResourceAttrSet(resourceName, "metadata.created_by"),
resource.TestCheckResourceAttrSet(resourceName, "metadata.organization_id"),
resource.TestCheckResourceAttr(resourceName, "type", "elasticsearch"),
),
},
},
})
}

func testAccElasticsearchProjectDestroy(s *terraform.State) error {
// retrieve the connection established in Provider configuration
client, err := newServerlessAPI()
Expand Down
54 changes: 54 additions & 0 deletions ec/acc/observability_project_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,13 @@ func TestAcc_ObservabilityProject(t *testing.T) {
resource.TestCheckResourceAttrSet(resourceName, "cloud_id"),
),
},
{
// Test import.
ResourceName: resourceName,
ImportState: true,
ImportStateVerify: true,
ImportStateVerifyIgnore: []string{"credentials"},
},
},
})
}
Expand Down Expand Up @@ -179,6 +186,53 @@ resource ec_observability_project "%s" {
`, id, name, region, productTier)
}

func TestAcc_ObservabilityProjectImport(t *testing.T) {
resId := "import_project"
resourceName := fmt.Sprintf("ec_observability_project.%s", resId)
randomName := prefix + acctest.RandStringFromCharSet(10, acctest.CharSetAlphaNum)
region := getRegion()
if !strings.HasPrefix("aws-", region) {
region = fmt.Sprintf("aws-%s", region)
}

resource.ParallelTest(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
ProtoV6ProviderFactories: testAccProviderFactory,
CheckDestroy: testAccObservabilityProjectDestroy,
Steps: []resource.TestStep{
{
// Create a project to import.
Config: testAccBasicObservabilityProject(resId, randomName, region),
Check: resource.ComposeAggregateTestCheckFunc(
resource.TestCheckResourceAttr(resourceName, "name", randomName),
resource.TestCheckResourceAttrSet(resourceName, "id"),
),
},
{
// Import the project and verify all attributes.
ResourceName: resourceName,
ImportState: true,
ImportStateVerify: true,
ImportStateVerifyIgnore: []string{"credentials"},
Check: resource.ComposeAggregateTestCheckFunc(
resource.TestCheckResourceAttr(resourceName, "name", randomName),
resource.TestCheckResourceAttr(resourceName, "region_id", region),
resource.TestCheckResourceAttrSet(resourceName, "id"),
resource.TestCheckResourceAttrSet(resourceName, "alias"),
resource.TestCheckResourceAttrSet(resourceName, "cloud_id"),
resource.TestCheckResourceAttrSet(resourceName, "endpoints.elasticsearch"),
resource.TestCheckResourceAttrSet(resourceName, "endpoints.kibana"),
resource.TestCheckResourceAttrSet(resourceName, "endpoints.apm"),
resource.TestCheckResourceAttrSet(resourceName, "metadata.created_at"),
resource.TestCheckResourceAttrSet(resourceName, "metadata.created_by"),
resource.TestCheckResourceAttrSet(resourceName, "metadata.organization_id"),
resource.TestCheckResourceAttr(resourceName, "type", "observability"),
),
},
},
})
}

func testAccObservabilityProjectDestroy(s *terraform.State) error {
// retrieve the connection established in Provider configuration
client, err := newServerlessAPI()
Expand Down
64 changes: 64 additions & 0 deletions ec/acc/security_project_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import (

"github.com/hashicorp/terraform-plugin-testing/helper/acctest"
"github.com/hashicorp/terraform-plugin-testing/helper/resource"
"github.com/hashicorp/terraform-plugin-testing/plancheck"
"github.com/hashicorp/terraform-plugin-testing/terraform"
)

Expand Down Expand Up @@ -83,6 +84,23 @@ func TestAcc_SecurityProject(t *testing.T) {
resource.TestCheckResourceAttrSet(resourceName, "cloud_id"),
),
},
{
// Test import.
ResourceName: resourceName,
ImportState: true,
ImportStateVerify: true,
// product_types are verified by ImportPlanChecks, so can be ignored here.
ImportStateVerifyIgnore: []string{"credentials", "product_types"},
// Use ImportPlanChecks to verify semantic equality of product_types.
// ExpectEmptyPlan confirms that the plan after import shows no changes,
// which indicates semantic equality is working correctly even if product_types
// are returned in a different order by the API.
ImportPlanChecks: resource.ImportPlanChecks{
PreApply: []plancheck.PlanCheck{
plancheck.ExpectEmptyPlan(),
},
},
},
},
})
}
Expand Down Expand Up @@ -156,6 +174,52 @@ resource ec_security_project "%s" {
`, id, name, region, adminPackage)
}

func TestAcc_SecurityProjectImport(t *testing.T) {
resId := "import_project"
resourceName := fmt.Sprintf("ec_security_project.%s", resId)
randomName := prefix + acctest.RandStringFromCharSet(10, acctest.CharSetAlphaNum)
region := getRegion()
if !strings.HasPrefix("aws-", region) {
region = fmt.Sprintf("aws-%s", region)
}

resource.ParallelTest(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
ProtoV6ProviderFactories: testAccProviderFactory,
CheckDestroy: testAccSecurityProjectDestroy,
Steps: []resource.TestStep{
{
// Create a project to import.
Config: testAccBasicSecurityProject(resId, randomName, region),
Check: resource.ComposeAggregateTestCheckFunc(
resource.TestCheckResourceAttr(resourceName, "name", randomName),
resource.TestCheckResourceAttrSet(resourceName, "id"),
),
},
{
// Import the project and verify all attributes.
ResourceName: resourceName,
ImportState: true,
ImportStateVerify: true,
ImportStateVerifyIgnore: []string{"credentials"},
Check: resource.ComposeAggregateTestCheckFunc(
resource.TestCheckResourceAttr(resourceName, "name", randomName),
resource.TestCheckResourceAttr(resourceName, "region_id", region),
resource.TestCheckResourceAttrSet(resourceName, "id"),
resource.TestCheckResourceAttrSet(resourceName, "alias"),
resource.TestCheckResourceAttrSet(resourceName, "cloud_id"),
resource.TestCheckResourceAttrSet(resourceName, "endpoints.elasticsearch"),
resource.TestCheckResourceAttrSet(resourceName, "endpoints.kibana"),
resource.TestCheckResourceAttrSet(resourceName, "metadata.created_at"),
resource.TestCheckResourceAttrSet(resourceName, "metadata.created_by"),
resource.TestCheckResourceAttrSet(resourceName, "metadata.organization_id"),
resource.TestCheckResourceAttr(resourceName, "type", "security"),
),
},
},
})
}

func testAccSecurityProjectDestroy(s *terraform.State) error {
// retrieve the connection established in Provider configuration
client, err := newServerlessAPI()
Expand Down
29 changes: 29 additions & 0 deletions ec/ecresource/projectresource/import.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
// Licensed to Elasticsearch B.V. under one or more contributor
// license agreements. See the NOTICE file distributed with
// this work for additional information regarding copyright
// ownership. Elasticsearch B.V. licenses this file to you under
// the Apache License, Version 2.0 (the "License"); you may
// not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.

package projectresource

import (
"context"

"github.com/hashicorp/terraform-plugin-framework/path"
"github.com/hashicorp/terraform-plugin-framework/resource"
)

func (r *Resource[T]) ImportState(ctx context.Context, request resource.ImportStateRequest, response *resource.ImportStateResponse) {
resource.ImportStatePassthroughID(ctx, path.Root("id"), request, response)
}
8 changes: 8 additions & 0 deletions ec/ecresource/projectresource/observability.go
Original file line number Diff line number Diff line change
Expand Up @@ -277,6 +277,14 @@ func (obs observabilityApi) Read(ctx context.Context, id string, model resource_
model.RegionId = basetypes.NewStringValue(resp.JSON200.RegionId)
model.Type = basetypes.NewStringValue(string(resp.JSON200.Type))

// Set product_tier from API response, defaulting to "complete" if not present
if resp.JSON200.ProductTier != nil {
model.ProductTier = basetypes.NewStringValue(string(*resp.JSON200.ProductTier))
} else {
// Default value as per schema
model.ProductTier = basetypes.NewStringValue(string(serverless.ObservabilityProjectProductTierComplete))
}

return true, model, nil
}

Expand Down
14 changes: 8 additions & 6 deletions ec/ecresource/projectresource/observability_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -902,9 +902,10 @@ func TestObservabilityApi_Read(t *testing.T) {
"suspended_reason": basetypes.NewStringNull(),
},
),
Name: types.StringValue(readModel.Name),
RegionId: types.StringValue(readModel.RegionId),
Type: types.StringValue(string(readModel.Type)),
Name: types.StringValue(readModel.Name),
RegionId: types.StringValue(readModel.RegionId),
Type: types.StringValue(string(readModel.Type)),
ProductTier: types.StringValue(string(serverless.ObservabilityProjectProductTierComplete)),
}

mockApiClient := mocks.NewMockClientWithResponsesInterface(ctrl)
Expand Down Expand Up @@ -976,9 +977,10 @@ func TestObservabilityApi_Read(t *testing.T) {
"suspended_reason": basetypes.NewStringValue(*readModel.Metadata.SuspendedReason),
},
),
Name: types.StringValue(readModel.Name),
RegionId: types.StringValue(readModel.RegionId),
Type: types.StringValue(string(readModel.Type)),
Name: types.StringValue(readModel.Name),
RegionId: types.StringValue(readModel.RegionId),
Type: types.StringValue(string(readModel.Type)),
ProductTier: types.StringValue(string(serverless.ObservabilityProjectProductTierComplete)),
}

mockApiClient := mocks.NewMockClientWithResponsesInterface(ctrl)
Expand Down
1 change: 1 addition & 0 deletions ec/ecresource/projectresource/resource.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ import (
var _ resource.Resource = &Resource[resource_elasticsearch_project.ElasticsearchProjectModel]{}
var _ resource.ResourceWithConfigure = &Resource[resource_elasticsearch_project.ElasticsearchProjectModel]{}
var _ resource.ResourceWithModifyPlan = &Resource[resource_elasticsearch_project.ElasticsearchProjectModel]{}
var _ resource.ResourceWithImportState = &Resource[resource_elasticsearch_project.ElasticsearchProjectModel]{}

type Resource[T any] struct {
modelHandler modelHandler[T]
Expand Down
31 changes: 31 additions & 0 deletions ec/ecresource/projectresource/resource_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import (
"github.com/elastic/terraform-provider-ec/ec/internal"
"github.com/elastic/terraform-provider-ec/ec/internal/gen/serverless/mocks"
"github.com/elastic/terraform-provider-ec/ec/internal/gen/serverless/resource_elasticsearch_project"
"github.com/elastic/terraform-provider-ec/ec/internal/util"
"github.com/hashicorp/terraform-plugin-framework/path"
"github.com/hashicorp/terraform-plugin-framework/resource"
"github.com/hashicorp/terraform-plugin-framework/tfsdk"
Expand Down Expand Up @@ -182,3 +183,33 @@ func TestModifyPlan(t *testing.T) {
require.Equal(t, planModel.Id.ValueString(), id)
})
}

func TestImportState(t *testing.T) {
t.Run("should successfully import project", func(t *testing.T) {
ctx := context.Background()
projectID := "project-id"
req := resource.ImportStateRequest{
ID: projectID,
}
schema := resource_elasticsearch_project.ElasticsearchProjectResourceSchema(ctx)
emptyModel := resource_elasticsearch_project.ElasticsearchProjectModel{}
emptyValue := util.TfTypesValueFromGoTypeValue(t, emptyModel, schema.Type())

res := resource.ImportStateResponse{
State: tfsdk.State{
Schema: schema,
Raw: emptyValue,
},
}

r := Resource[resource_elasticsearch_project.ElasticsearchProjectModel]{}
r.ImportState(ctx, req, &res)

require.False(t, res.Diagnostics.HasError())

// Validate that the imported ID was set in the state
var id string
res.State.GetAttribute(ctx, path.Root("id"), &id)
require.Equal(t, projectID, id)
})
}
2 changes: 2 additions & 0 deletions templates/resources/elasticsearch_project.md.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -24,3 +24,5 @@ Elastic will work to fix any issues, but features in technical preview are not s
Projects can be imported using the `id`, for example:

{{ codefile "shell" .ImportFile }}

~> **Note on Credentials** The `credentials` attribute (containing `username` and `password`) is only available when the project is first created. When importing an existing project, these credentials will not be available in the Terraform state as the API does not return them on read operations.
2 changes: 2 additions & 0 deletions templates/resources/observability_project.md.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -24,3 +24,5 @@ Elastic will work to fix any issues, but features in technical preview are not s
Projects can be imported using the `id`, for example:

{{ codefile "shell" .ImportFile }}

~> **Note on Credentials** The `credentials` attribute (containing `username` and `password`) is only available when the project is first created. When importing an existing project, these credentials will not be available in the Terraform state as the API does not return them on read operations.
2 changes: 2 additions & 0 deletions templates/resources/security_project.md.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -24,3 +24,5 @@ Elastic will work to fix any issues, but features in technical preview are not s
Projects can be imported using the `id`, for example:

{{ codefile "shell" .ImportFile }}

~> **Note on Credentials** The `credentials` attribute (containing `username` and `password`) is only available when the project is first created. When importing an existing project, these credentials will not be available in the Terraform state as the API does not return them on read operations.
Loading