-
Notifications
You must be signed in to change notification settings - Fork 5k
Add azurerm_cosmosdb_fleet resource
#31546
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
3191b8e
3bf3db1
f3e96a7
4299628
034e586
99bb218
8dc1520
9f37cb0
46fef7c
4de3868
0fce9e4
7d57ab9
f72f330
c38d77b
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,177 @@ | ||
| // Copyright IBM Corp. 2014, 2025 | ||
| // SPDX-License-Identifier: MPL-2.0 | ||
|
|
||
| package cosmos | ||
|
|
||
| import ( | ||
| "context" | ||
| "fmt" | ||
| "time" | ||
|
|
||
| "github.com/hashicorp/go-azure-helpers/lang/pointer" | ||
| "github.com/hashicorp/go-azure-helpers/lang/response" | ||
| "github.com/hashicorp/go-azure-helpers/resourcemanager/commonschema" | ||
| "github.com/hashicorp/go-azure-helpers/resourcemanager/location" | ||
| "github.com/hashicorp/go-azure-helpers/resourcemanager/resourceids" | ||
| "github.com/hashicorp/go-azure-helpers/resourcemanager/tags" | ||
| "github.com/hashicorp/go-azure-sdk/resource-manager/cosmosdb/2025-10-15/fleets" | ||
| "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" | ||
| "github.com/hashicorp/terraform-provider-azurerm/internal/sdk" | ||
| "github.com/hashicorp/terraform-provider-azurerm/internal/services/cosmos/validate" | ||
| "github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk" | ||
| ) | ||
|
|
||
| //go:generate go run ../../tools/generator-tests resourceidentity -resource-name cosmosdb_fleet -service-package-name cosmos -properties "name,resource_group_name" -known-values "subscription_id:data.Subscriptions.Primary" | ||
|
|
||
| type CosmosDbFleetResource struct{} | ||
|
|
||
| var _ sdk.ResourceWithIdentity = CosmosDbFleetResource{} | ||
|
|
||
| type CosmosDbFleetModel struct { | ||
| Name string `tfschema:"name"` | ||
| ResourceGroupName string `tfschema:"resource_group_name"` | ||
| Location string `tfschema:"location"` | ||
| Tags map[string]string `tfschema:"tags"` | ||
| } | ||
|
|
||
| func (CosmosDbFleetResource) Arguments() map[string]*pluginsdk.Schema { | ||
| return map[string]*pluginsdk.Schema{ | ||
| "name": { | ||
| Type: pluginsdk.TypeString, | ||
| Required: true, | ||
| ForceNew: true, | ||
| ValidateFunc: validate.FleetName, | ||
| }, | ||
|
|
||
| "resource_group_name": commonschema.ResourceGroupName(), | ||
|
|
||
| "location": commonschema.Location(), | ||
|
|
||
| "tags": { | ||
| Type: schema.TypeMap, | ||
| Optional: true, | ||
| // `ForceNew` behavior is added as `tags` property is absent in update model | ||
| ForceNew: true, | ||
| ValidateFunc: tags.Validate, | ||
| Elem: &schema.Schema{ | ||
| Type: schema.TypeString, | ||
| }, | ||
| }, | ||
| } | ||
| } | ||
|
|
||
| func (CosmosDbFleetResource) Attributes() map[string]*pluginsdk.Schema { | ||
| return map[string]*pluginsdk.Schema{} | ||
| } | ||
|
|
||
| func (CosmosDbFleetResource) ModelObject() interface{} { | ||
| return &CosmosDbFleetModel{} | ||
| } | ||
|
|
||
| func (CosmosDbFleetResource) ResourceType() string { | ||
| return "azurerm_cosmosdb_fleet" | ||
| } | ||
|
|
||
| func (r CosmosDbFleetResource) Create() sdk.ResourceFunc { | ||
| return sdk.ResourceFunc{ | ||
| Timeout: 30 * time.Minute, | ||
| Func: func(ctx context.Context, metadata sdk.ResourceMetaData) error { | ||
| client := metadata.Client.Cosmos.FleetsClient | ||
| subscriptionId := metadata.Client.Account.SubscriptionId | ||
|
|
||
| var config CosmosDbFleetModel | ||
| if err := metadata.Decode(&config); err != nil { | ||
| return fmt.Errorf("decoding: %+v", err) | ||
| } | ||
| id := fleets.NewFleetID(subscriptionId, config.ResourceGroupName, config.Name) | ||
|
|
||
| existing, err := client.FleetGet(ctx, id) | ||
| if err != nil && !response.WasNotFound(existing.HttpResponse) { | ||
| return fmt.Errorf("checking for presence of existing %s: %+v", id, err) | ||
| } | ||
| if !response.WasNotFound(existing.HttpResponse) { | ||
| return metadata.ResourceRequiresImport(r.ResourceType(), id) | ||
| } | ||
|
|
||
| param := fleets.FleetResource{ | ||
| Location: location.Normalize(config.Location), | ||
| Tags: pointer.To(config.Tags), | ||
| } | ||
| if _, err := client.FleetCreate(ctx, id, param); err != nil { | ||
| return fmt.Errorf("creating %s: %+v", id, err) | ||
| } | ||
|
|
||
| metadata.SetID(id) | ||
| if err := pluginsdk.SetResourceIdentityData(metadata.ResourceData, &id); err != nil { | ||
| return err | ||
| } | ||
|
|
||
| return nil | ||
| }, | ||
| } | ||
| } | ||
|
|
||
| func (r CosmosDbFleetResource) Read() sdk.ResourceFunc { | ||
| return sdk.ResourceFunc{ | ||
| Timeout: 5 * time.Minute, | ||
| Func: func(ctx context.Context, metadata sdk.ResourceMetaData) error { | ||
| client := metadata.Client.Cosmos.FleetsClient | ||
| id, err := fleets.ParseFleetID(metadata.ResourceData.Id()) | ||
| if err != nil { | ||
| return err | ||
| } | ||
|
|
||
| resp, err := client.FleetGet(ctx, *id) | ||
| if err != nil { | ||
| if response.WasNotFound(resp.HttpResponse) { | ||
| return metadata.MarkAsGone(id) | ||
| } | ||
|
|
||
| return fmt.Errorf("retrieving %s: %+v", id, err) | ||
| } | ||
|
|
||
| state := CosmosDbFleetModel{ | ||
| Name: id.FleetName, | ||
| ResourceGroupName: id.ResourceGroupName, | ||
| } | ||
|
|
||
| if model := resp.Model; model != nil { | ||
| state.Location = location.NormalizeNilable(&model.Location) | ||
| state.Tags = pointer.From(model.Tags) | ||
| } | ||
|
|
||
| if err := pluginsdk.SetResourceIdentityData(metadata.ResourceData, id); err != nil { | ||
| return err | ||
| } | ||
|
|
||
| return metadata.Encode(&state) | ||
| }, | ||
| } | ||
| } | ||
|
|
||
| func (CosmosDbFleetResource) Delete() sdk.ResourceFunc { | ||
| return sdk.ResourceFunc{ | ||
| Timeout: 30 * time.Minute, | ||
| Func: func(ctx context.Context, metadata sdk.ResourceMetaData) error { | ||
| client := metadata.Client.Cosmos.FleetsClient | ||
|
|
||
| id, err := fleets.ParseFleetID(metadata.ResourceData.Id()) | ||
| if err != nil { | ||
| return err | ||
| } | ||
|
|
||
| if err := client.FleetDeleteThenPoll(ctx, *id); err != nil { | ||
| return fmt.Errorf("deleting %s: %+v", *id, err) | ||
| } | ||
| return nil | ||
| }, | ||
| } | ||
| } | ||
|
|
||
| func (CosmosDbFleetResource) IDValidationFunc() pluginsdk.SchemaValidateFunc { | ||
| return fleets.ValidateFleetID | ||
| } | ||
|
|
||
| func (CosmosDbFleetResource) Identity() resourceids.ResourceId { | ||
| return &fleets.FleetId{} | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,131 @@ | ||
| // Copyright IBM Corp. 2014, 2025 | ||
| // SPDX-License-Identifier: MPL-2.0 | ||
|
|
||
| package cosmos_test | ||
|
|
||
| import ( | ||
| "context" | ||
| "fmt" | ||
| "testing" | ||
|
|
||
| "github.com/hashicorp/go-azure-helpers/lang/pointer" | ||
| "github.com/hashicorp/go-azure-sdk/resource-manager/cosmosdb/2025-10-15/fleets" | ||
| "github.com/hashicorp/terraform-provider-azurerm/internal/acceptance" | ||
| "github.com/hashicorp/terraform-provider-azurerm/internal/acceptance/check" | ||
| "github.com/hashicorp/terraform-provider-azurerm/internal/clients" | ||
| "github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk" | ||
| ) | ||
|
|
||
| type CosmosDbFleetResource struct{} | ||
|
|
||
| func TestAccCosmosDbFleet_basic(t *testing.T) { | ||
| data := acceptance.BuildTestData(t, "azurerm_cosmosdb_fleet", "test") | ||
| r := CosmosDbFleetResource{} | ||
|
|
||
| data.ResourceTest(t, r, []acceptance.TestStep{ | ||
| { | ||
| Config: r.basic(data), | ||
| Check: acceptance.ComposeTestCheckFunc( | ||
| check.That(data.ResourceName).ExistsInAzure(r), | ||
| ), | ||
| }, | ||
| data.ImportStep(), | ||
| }) | ||
| } | ||
|
|
||
| func TestAccCosmosDbFleet_requiresImport(t *testing.T) { | ||
| data := acceptance.BuildTestData(t, "azurerm_cosmosdb_fleet", "test") | ||
| r := CosmosDbFleetResource{} | ||
|
|
||
| data.ResourceTest(t, r, []acceptance.TestStep{ | ||
| { | ||
| Config: r.basic(data), | ||
| Check: acceptance.ComposeTestCheckFunc( | ||
| check.That(data.ResourceName).ExistsInAzure(r), | ||
| ), | ||
| }, | ||
| data.RequiresImportErrorStep(r.requiresImport), | ||
| }) | ||
| } | ||
|
|
||
| func TestAccCosmosDbFleet_complete(t *testing.T) { | ||
| data := acceptance.BuildTestData(t, "azurerm_cosmosdb_fleet", "test") | ||
| r := CosmosDbFleetResource{} | ||
|
|
||
| data.ResourceTest(t, r, []acceptance.TestStep{ | ||
| { | ||
| Config: r.complete(data), | ||
| Check: acceptance.ComposeTestCheckFunc( | ||
| check.That(data.ResourceName).ExistsInAzure(r), | ||
| ), | ||
| }, | ||
| data.ImportStep(), | ||
| }) | ||
| } | ||
|
|
||
| func (CosmosDbFleetResource) Exists(ctx context.Context, client *clients.Client, state *pluginsdk.InstanceState) (*bool, error) { | ||
| id, err := fleets.ParseFleetID(state.ID) | ||
| if err != nil { | ||
| return nil, err | ||
| } | ||
|
|
||
| resp, err := client.Cosmos.FleetsClient.FleetGet(ctx, *id) | ||
| if err != nil { | ||
| return nil, fmt.Errorf("retrieving %s: %+v", *id, err) | ||
| } | ||
|
|
||
| return pointer.To(resp.Model != nil), nil | ||
| } | ||
|
|
||
| func (CosmosDbFleetResource) template(data acceptance.TestData) string { | ||
| return fmt.Sprintf(` | ||
| provider "azurerm" { | ||
| features {} | ||
| } | ||
|
|
||
| resource "azurerm_resource_group" "test" { | ||
| name = "acctest-cosmos-%d" | ||
| location = "%s" | ||
| } | ||
| `, data.RandomInteger, data.Locations.Primary) | ||
| } | ||
|
|
||
| func (r CosmosDbFleetResource) basic(data acceptance.TestData) string { | ||
| return fmt.Sprintf(` | ||
| %s | ||
|
|
||
| resource "azurerm_cosmosdb_fleet" "test" { | ||
| name = "acctest-cosfleet-%d" | ||
| resource_group_name = azurerm_resource_group.test.name | ||
| location = azurerm_resource_group.test.location | ||
| } | ||
| `, r.template(data), data.RandomInteger) | ||
| } | ||
|
|
||
| func (r CosmosDbFleetResource) requiresImport(data acceptance.TestData) string { | ||
| return fmt.Sprintf(` | ||
| %s | ||
|
|
||
| resource "azurerm_cosmosdb_fleet" "import" { | ||
| name = azurerm_cosmosdb_fleet.test.name | ||
| resource_group_name = azurerm_cosmosdb_fleet.test.resource_group_name | ||
| location = azurerm_cosmosdb_fleet.test.location | ||
| } | ||
| `, r.basic(data)) | ||
| } | ||
|
|
||
| func (r CosmosDbFleetResource) complete(data acceptance.TestData) string { | ||
| return fmt.Sprintf(` | ||
| %s | ||
|
|
||
| resource "azurerm_cosmosdb_fleet" "test" { | ||
| name = "acctest-cosfleet-%d" | ||
| resource_group_name = azurerm_resource_group.test.name | ||
| location = azurerm_resource_group.test.location | ||
|
|
||
| tags = { | ||
| env = "test" | ||
| } | ||
| } | ||
| `, r.template(data), data.RandomInteger) | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -28,14 +28,15 @@ func (r Registration) DataSources() []sdk.DataSource { | |
|
|
||
| func (r Registration) Resources() []sdk.Resource { | ||
| return []sdk.Resource{ | ||
| CosmosDbFleetResource{}, | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This has to be sorted alphabetically. Please run azurerm-linter and check violations.
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Fixed. |
||
| CosmosDbMongoRoleDefinitionResource{}, | ||
| CosmosDbMongoUserDefinitionResource{}, | ||
| CosmosDbPostgreSQLClusterResource{}, | ||
| CosmosDbPostgreSQLCoordinatorConfigurationResource{}, | ||
| CosmosDbPostgreSQLFirewallRuleResource{}, | ||
| CosmosDbPostgreSQLNodeConfigurationResource{}, | ||
| CosmosDbPostgreSQLRoleResource{}, | ||
| CosmosDbSqlDedicatedGatewayResource{}, | ||
| CosmosDbMongoRoleDefinitionResource{}, | ||
| } | ||
| } | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,23 @@ | ||
| // Copyright IBM Corp. 2014, 2025 | ||
| // SPDX-License-Identifier: MPL-2.0 | ||
|
|
||
| package validate | ||
|
|
||
| import ( | ||
| "fmt" | ||
| "regexp" | ||
| ) | ||
|
|
||
| // Validate according to https://learn.microsoft.com/en-us/rest/api/cosmos-db-resource-provider/fleet/get?view=rest-cosmos-db-resource-provider-2025-10-15&tabs=HTTP | ||
| func FleetName(v interface{}, k string) (warnings []string, errors []error) { | ||
| name := v.(string) | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Put the link as a comment, so other people will know where those restrictions are from.
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Fixed |
||
| if len(name) < 3 || len(name) > 50 { | ||
| errors = append(errors, fmt.Errorf("length of %q must be between 3 to 50 (inclusive)", k)) | ||
| } | ||
|
|
||
| if !regexp.MustCompile(`^[a-z0-9]+(?:-[a-z0-9]+)*$`).MatchString(name) { | ||
| errors = append(errors, fmt.Errorf("%q must consist of lower case letters, digits, and hyphens. The first and last character must be a letter or digit", k)) | ||
| } | ||
|
|
||
| return warnings, errors | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The "Create" SDK method uses PUT HTTP method, can you please check if we can use this for update?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
By applying a typical
Updatemethod and test which involves only change inTagsproperty, an error occurs mentioning that it is unable to create a resource with same ID. Following are the REST API request and response.Request body before update
{ "location": "southeastasia", "tags": null }Request body during update
{ "id": "/subscriptions/REDACTED/resourceGroups/REDACTED/providers/Microsoft.DocumentDB/fleets/REDACTED", "location": "Southeast Asia", "name": "REDACTED", "properties": { "provisioningState": "Succeeded" }, "tags": { "env": "test" }, "type": "Microsoft.DocumentDB/fleets" }Error from response body during update
{ "code": "BadRequest", "message": "Exception occurred trying to create Fleet with Name: REDACTED and SubscriptionID: REDACTED, Message: Entity with the specified id already exists in the system.\r\nActivityId: REDACTED, Microsoft.Azure.Documents.Common/2.14.0, Microsoft.Azure.Documents.Common/2.14.0" }There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Update FleetPool use PATCH - see details here: https://learn.microsoft.com/en-us/rest/api/cosmos-db-resource-provider/fleet/update?view=rest-cosmos-db-resource-provider-2025-10-15&tabs=HTTP
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
But that is not useful @sesmyrnov , the only updatable property (tags) is not supported by that PATCH API.