diff --git a/internal/services/containers/kubernetes_fleet_manager_resource.go b/internal/services/containers/kubernetes_fleet_manager_resource.go index 2fa078c01a9b..5970eb01ec82 100644 --- a/internal/services/containers/kubernetes_fleet_manager_resource.go +++ b/internal/services/containers/kubernetes_fleet_manager_resource.go @@ -1,80 +1,100 @@ package containers -// NOTE: this file is generated - manual changes will be overwritten. -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. See NOTICE.txt in the project root for license information. import ( "context" "fmt" + "regexp" "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/tags" "github.com/hashicorp/go-azure-sdk/resource-manager/containerservice/2024-04-01/fleets" "github.com/hashicorp/terraform-provider-azurerm/internal/sdk" "github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk" + "github.com/hashicorp/terraform-provider-azurerm/internal/tf/validation" ) -var ( - _ sdk.Resource = KubernetesFleetManagerResource{} - _ sdk.ResourceWithUpdate = KubernetesFleetManagerResource{} -) +type KubernetesFleetManagerModel struct { + Name string `tfschema:"name"` + ResourceGroupName string `tfschema:"resource_group_name"` + Location string `tfschema:"location"` + HubProfile []FleetHubProfileModel `tfschema:"hub_profile"` + Tags map[string]string `tfschema:"tags"` +} + +type FleetHubProfileModel struct { + DnsPrefix string `tfschema:"dns_prefix"` + Fqdn string `tfschema:"fqdn"` + KubernetesVersion string `tfschema:"kubernetes_version"` + PortalFqdn string `tfschema:"portal_fqdn"` +} type KubernetesFleetManagerResource struct{} -func (r KubernetesFleetManagerResource) ModelObject() interface{} { - return &KubernetesFleetManagerResourceSchema{} +var _ sdk.ResourceWithUpdate = KubernetesFleetManagerResource{} + +func (r KubernetesFleetManagerResource) ResourceType() string { + return "azurerm_kubernetes_fleet_manager" } -type KubernetesFleetManagerResourceSchema struct { - Location string `tfschema:"location"` - Name string `tfschema:"name"` - ResourceGroupName string `tfschema:"resource_group_name"` - Tags map[string]interface{} `tfschema:"tags"` +func (r KubernetesFleetManagerResource) ModelObject() interface{} { + return &KubernetesFleetManagerModel{} } func (r KubernetesFleetManagerResource) IDValidationFunc() pluginsdk.SchemaValidateFunc { return fleets.ValidateFleetID } -func (r KubernetesFleetManagerResource) ResourceType() string { - return "azurerm_kubernetes_fleet_manager" -} - func (r KubernetesFleetManagerResource) Arguments() map[string]*pluginsdk.Schema { return map[string]*pluginsdk.Schema{ - "location": commonschema.Location(), "name": { - ForceNew: true, - Required: true, - Type: pluginsdk.TypeString, + Type: pluginsdk.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validation.StringIsNotEmpty, }, + "resource_group_name": commonschema.ResourceGroupName(), + + "location": commonschema.Location(), + "hub_profile": { - Deprecated: "The service team has indicated this field is now deprecated and not to be used, as such we are marking it as such and no longer sending it to the API, please see url: https://learn.microsoft.com/en-us/azure/kubernetes-fleet/architectural-overview", + Type: pluginsdk.TypeList, + Optional: true, + ForceNew: true, + MaxItems: 1, Elem: &pluginsdk.Resource{ Schema: map[string]*pluginsdk.Schema{ "dns_prefix": { - Required: true, Type: pluginsdk.TypeString, + Optional: true, + ForceNew: true, + ValidateFunc: validation.All( + validation.StringLenBetween(1, 54), + validation.StringMatch(regexp.MustCompile(`^[a-zA-Z0-9]$|^[a-zA-Z0-9][a-zA-Z0-9-]{0,52}[a-zA-Z0-9]$`), "must match the pattern ^[a-zA-Z0-9]$|^[a-zA-Z0-9][a-zA-Z0-9-]{0,52}[a-zA-Z0-9]$"), + ), }, + "fqdn": { - Computed: true, Type: pluginsdk.TypeString, + Computed: true, }, + "kubernetes_version": { + Type: pluginsdk.TypeString, Computed: true, + }, + + "portal_fqdn": { Type: pluginsdk.TypeString, + Computed: true, }, }, }, - ForceNew: true, - MaxItems: 1, - Optional: true, - Type: pluginsdk.TypeList, }, + "tags": commonschema.Tags(), } } @@ -87,31 +107,34 @@ func (r KubernetesFleetManagerResource) Create() sdk.ResourceFunc { return sdk.ResourceFunc{ Timeout: 30 * time.Minute, Func: func(ctx context.Context, metadata sdk.ResourceMetaData) error { - client := metadata.Client.ContainerService.V20231015.Fleets - - var config KubernetesFleetManagerResourceSchema - if err := metadata.Decode(&config); err != nil { + var model KubernetesFleetManagerModel + if err := metadata.Decode(&model); err != nil { return fmt.Errorf("decoding: %+v", err) } + client := metadata.Client.ContainerService.V20231015.Fleets subscriptionId := metadata.Client.Account.SubscriptionId - id := fleets.NewFleetID(subscriptionId, config.ResourceGroupName, config.Name) + id := fleets.NewFleetID(subscriptionId, model.ResourceGroupName, model.Name) existing, err := client.Get(ctx, id) - if err != nil { - if !response.WasNotFound(existing.HttpResponse) { - return fmt.Errorf("checking for the presence of an existing %s: %+v", id, err) - } + if err != nil && !response.WasNotFound(existing.HttpResponse) { + return fmt.Errorf("checking for existing %s: %+v", id, err) } + if !response.WasNotFound(existing.HttpResponse) { return metadata.ResourceRequiresImport(r.ResourceType(), id) } - var payload fleets.Fleet - r.mapKubernetesFleetManagerResourceSchemaToFleet(config, &payload) + fleetResource := &fleets.Fleet{ + Location: location.Normalize(model.Location), + Tags: pointer.To(model.Tags), + Properties: &fleets.FleetProperties{ + HubProfile: expandFleetHubProfileModel(model.HubProfile), + }, + } - if err := client.CreateOrUpdateThenPoll(ctx, id, payload, fleets.DefaultCreateOrUpdateOperationOptions()); err != nil { + if err := client.CreateOrUpdateThenPoll(ctx, id, *fleetResource, fleets.DefaultCreateOrUpdateOperationOptions()); err != nil { return fmt.Errorf("creating %s: %+v", id, err) } @@ -121,40 +144,51 @@ func (r KubernetesFleetManagerResource) Create() sdk.ResourceFunc { } } -func (r KubernetesFleetManagerResource) Read() sdk.ResourceFunc { +func (r KubernetesFleetManagerResource) Update() sdk.ResourceFunc { return sdk.ResourceFunc{ - Timeout: 5 * time.Minute, + Timeout: 30 * time.Minute, Func: func(ctx context.Context, metadata sdk.ResourceMetaData) error { client := metadata.Client.ContainerService.V20231015.Fleets - schema := KubernetesFleetManagerResourceSchema{} id, err := fleets.ParseFleetID(metadata.ResourceData.Id()) if err != nil { return err } + var model KubernetesFleetManagerModel + if err := metadata.Decode(&model); err != nil { + return fmt.Errorf("decoding: %+v", err) + } + resp, err := client.Get(ctx, *id) if err != nil { - if response.WasNotFound(resp.HttpResponse) { - return metadata.MarkAsGone(*id) - } return fmt.Errorf("retrieving %s: %+v", *id, err) } - if model := resp.Model; model != nil { - schema.Name = id.FleetName - schema.ResourceGroupName = id.ResourceGroupName - r.mapFleetToKubernetesFleetManagerResourceSchema(*model, &schema) + properties := resp.Model + if resp.Model == nil { + return fmt.Errorf("retrieving %s: `model` was nil", *id) + } + if resp.Model.Properties == nil { + return fmt.Errorf("retrieving %s: `properties` was nil", *id) + } + + if metadata.ResourceData.HasChange("tags") { + properties.Tags = pointer.To(model.Tags) + } + + if err := client.CreateOrUpdateThenPoll(ctx, *id, *properties, fleets.DefaultCreateOrUpdateOperationOptions()); err != nil { + return fmt.Errorf("updating %s: %+v", *id, err) } - return metadata.Encode(&schema) + return nil }, } } -func (r KubernetesFleetManagerResource) Delete() sdk.ResourceFunc { +func (r KubernetesFleetManagerResource) Read() sdk.ResourceFunc { return sdk.ResourceFunc{ - Timeout: 30 * time.Minute, + Timeout: 5 * time.Minute, Func: func(ctx context.Context, metadata sdk.ResourceMetaData) error { client := metadata.Client.ContainerService.V20231015.Fleets @@ -163,16 +197,36 @@ func (r KubernetesFleetManagerResource) Delete() sdk.ResourceFunc { return err } - if err := client.DeleteThenPoll(ctx, *id, fleets.DefaultDeleteOperationOptions()); err != nil { - return fmt.Errorf("deleting %s: %+v", *id, err) + resp, err := client.Get(ctx, *id) + if err != nil { + if response.WasNotFound(resp.HttpResponse) { + return metadata.MarkAsGone(id) + } + + return fmt.Errorf("retrieving %s: %+v", *id, err) } - return nil + state := KubernetesFleetManagerModel{ + Name: id.FleetName, + ResourceGroupName: id.ResourceGroupName, + } + + if model := resp.Model; model != nil { + state.Location = location.Normalize(model.Location) + + if properties := model.Properties; properties != nil { + state.HubProfile = flattenFleetHubProfileModel(properties.HubProfile) + } + + state.Tags = pointer.From(model.Tags) + } + + return metadata.Encode(&state) }, } } -func (r KubernetesFleetManagerResource) Update() sdk.ResourceFunc { +func (r KubernetesFleetManagerResource) Delete() sdk.ResourceFunc { return sdk.ResourceFunc{ Timeout: 30 * time.Minute, Func: func(ctx context.Context, metadata sdk.ResourceMetaData) error { @@ -183,24 +237,8 @@ func (r KubernetesFleetManagerResource) Update() sdk.ResourceFunc { return err } - var config KubernetesFleetManagerResourceSchema - if err := metadata.Decode(&config); err != nil { - return fmt.Errorf("decoding: %+v", err) - } - - existing, err := client.Get(ctx, *id) - if err != nil { - return fmt.Errorf("retrieving existing %s: %+v", *id, err) - } - if existing.Model == nil { - return fmt.Errorf("retrieving existing %s: properties was nil", *id) - } - payload := *existing.Model - - r.mapKubernetesFleetManagerResourceSchemaToFleet(config, &payload) - - if err := client.CreateOrUpdateThenPoll(ctx, *id, payload, fleets.DefaultCreateOrUpdateOperationOptions()); err != nil { - return fmt.Errorf("updating %s: %+v", *id, err) + if err := client.DeleteThenPoll(ctx, *id, fleets.DefaultDeleteOperationOptions()); err != nil { + return fmt.Errorf("deleting %s: %+v", id, err) } return nil @@ -208,20 +246,30 @@ func (r KubernetesFleetManagerResource) Update() sdk.ResourceFunc { } } -func (r KubernetesFleetManagerResource) mapKubernetesFleetManagerResourceSchemaToFleet(input KubernetesFleetManagerResourceSchema, output *fleets.Fleet) { - output.Location = location.Normalize(input.Location) - output.Tags = tags.Expand(input.Tags) +func expandFleetHubProfileModel(inputList []FleetHubProfileModel) *fleets.FleetHubProfile { + if len(inputList) == 0 { + return nil + } - if output.Properties == nil { - output.Properties = &fleets.FleetProperties{} + input := inputList[0] + output := &fleets.FleetHubProfile{ + DnsPrefix: pointer.To(input.DnsPrefix), } + + return output } -func (r KubernetesFleetManagerResource) mapFleetToKubernetesFleetManagerResourceSchema(input fleets.Fleet, output *KubernetesFleetManagerResourceSchema) { - output.Location = location.Normalize(input.Location) - output.Tags = tags.Flatten(input.Tags) +func flattenFleetHubProfileModel(input *fleets.FleetHubProfile) []FleetHubProfileModel { + if input == nil { + return nil + } - if input.Properties == nil { - input.Properties = &fleets.FleetProperties{} + output := FleetHubProfileModel{ + DnsPrefix: pointer.From(input.DnsPrefix), + Fqdn: pointer.From(input.Fqdn), + KubernetesVersion: pointer.From(input.KubernetesVersion), + PortalFqdn: pointer.From(input.PortalFqdn), } + + return []FleetHubProfileModel{output} } diff --git a/internal/services/containers/kubernetes_fleet_manager_resource_test.go b/internal/services/containers/kubernetes_fleet_manager_resource_test.go index ba6e3dee9796..0d9794d6f8f3 100644 --- a/internal/services/containers/kubernetes_fleet_manager_resource_test.go +++ b/internal/services/containers/kubernetes_fleet_manager_resource_test.go @@ -1,8 +1,5 @@ package containers_test -// NOTE: this file is generated - manual changes will be overwritten. -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. See NOTICE.txt in the project root for license information. import ( "context" "fmt" @@ -21,7 +18,6 @@ type KubernetesFleetManagerTestResource struct{} func TestAccKubernetesFleetManager_basic(t *testing.T) { data := acceptance.BuildTestData(t, "azurerm_kubernetes_fleet_manager", "test") r := KubernetesFleetManagerTestResource{} - data.ResourceTest(t, r, []acceptance.TestStep{ { Config: r.basic(data), @@ -36,7 +32,6 @@ func TestAccKubernetesFleetManager_basic(t *testing.T) { func TestAccKubernetesFleetManager_requiresImport(t *testing.T) { data := acceptance.BuildTestData(t, "azurerm_kubernetes_fleet_manager", "test") r := KubernetesFleetManagerTestResource{} - data.ResourceTest(t, r, []acceptance.TestStep{ { Config: r.basic(data), @@ -51,7 +46,6 @@ func TestAccKubernetesFleetManager_requiresImport(t *testing.T) { func TestAccKubernetesFleetManager_complete(t *testing.T) { data := acceptance.BuildTestData(t, "azurerm_kubernetes_fleet_manager", "test") r := KubernetesFleetManagerTestResource{} - data.ResourceTest(t, r, []acceptance.TestStep{ { Config: r.complete(data), @@ -59,31 +53,23 @@ func TestAccKubernetesFleetManager_complete(t *testing.T) { check.That(data.ResourceName).ExistsInAzure(r), ), }, - data.ImportStep("hub_profile.#", "hub_profile.0.%", "hub_profile.0.dns_prefix", "hub_profile.0.fqdn", "hub_profile.0.kubernetes_version"), + data.ImportStep(), }) } func TestAccKubernetesFleetManager_update(t *testing.T) { data := acceptance.BuildTestData(t, "azurerm_kubernetes_fleet_manager", "test") r := KubernetesFleetManagerTestResource{} - data.ResourceTest(t, r, []acceptance.TestStep{ - { - Config: r.basic(data), - Check: acceptance.ComposeTestCheckFunc( - check.That(data.ResourceName).ExistsInAzure(r), - ), - }, - data.ImportStep(), { Config: r.complete(data), Check: acceptance.ComposeTestCheckFunc( check.That(data.ResourceName).ExistsInAzure(r), ), }, - data.ImportStep("hub_profile.#", "hub_profile.0.%", "hub_profile.0.dns_prefix", "hub_profile.0.fqdn", "hub_profile.0.kubernetes_version"), + data.ImportStep(), { - Config: r.basic(data), + Config: r.update(data), Check: acceptance.ComposeTestCheckFunc( check.That(data.ResourceName).ExistsInAzure(r), ), @@ -100,26 +86,35 @@ func (r KubernetesFleetManagerTestResource) Exists(ctx context.Context, clients resp, err := clients.ContainerService.V20231015.Fleets.Get(ctx, *id) if err != nil { - return nil, fmt.Errorf("reading %s: %+v", *id, err) + return nil, fmt.Errorf("retrieving %s: %+v", *id, err) } - return utils.Bool(resp.Model != nil), nil } -func (r KubernetesFleetManagerTestResource) basic(data acceptance.TestData) string { +func (r KubernetesFleetManagerTestResource) template(data acceptance.TestData) string { return fmt.Sprintf(` -%s - provider "azurerm" { features {} } +resource "azurerm_resource_group" "test" { + name = "acctest-rg-%[1]s" + location = "%[2]s" +} + +`, data.RandomString, data.Locations.Primary) +} + +func (r KubernetesFleetManagerTestResource) basic(data acceptance.TestData) string { + return fmt.Sprintf(` +%s + resource "azurerm_kubernetes_fleet_manager" "test" { - location = azurerm_resource_group.test.location - name = "acctestkfm-${var.random_string}" + name = "acctestkfm-%[2]s" resource_group_name = azurerm_resource_group.test.name + location = azurerm_resource_group.test.location } -`, r.template(data)) +`, r.template(data), data.RandomString) } func (r KubernetesFleetManagerTestResource) requiresImport(data acceptance.TestData) string { @@ -127,9 +122,9 @@ func (r KubernetesFleetManagerTestResource) requiresImport(data acceptance.TestD %s resource "azurerm_kubernetes_fleet_manager" "import" { - location = azurerm_kubernetes_fleet_manager.test.location name = azurerm_kubernetes_fleet_manager.test.name - resource_group_name = azurerm_kubernetes_fleet_manager.test.resource_group_name + resource_group_name = azurerm_resource_group.test.name + location = azurerm_resource_group.test.location } `, r.basic(data)) } @@ -138,40 +133,34 @@ func (r KubernetesFleetManagerTestResource) complete(data acceptance.TestData) s return fmt.Sprintf(` %s -provider "azurerm" { - features {} -} - resource "azurerm_kubernetes_fleet_manager" "test" { - location = azurerm_resource_group.test.location - name = "acctestkfm-${var.random_string}" + name = "acctestkfm-%[2]s" resource_group_name = azurerm_resource_group.test.name + location = azurerm_resource_group.test.location + hub_profile { + dns_prefix = "acctestkfm-%[2]s" + } tags = { environment = "terraform-acctests" - some_key = "some-value" - } - hub_profile { - dns_prefix = "val-${var.random_string}" } } -`, r.template(data)) +`, r.template(data), data.RandomString) } -func (r KubernetesFleetManagerTestResource) template(data acceptance.TestData) string { +func (r KubernetesFleetManagerTestResource) update(data acceptance.TestData) string { return fmt.Sprintf(` -variable "primary_location" { - default = %q -} -variable "random_integer" { - default = %d -} -variable "random_string" { - default = %q -} +%s -resource "azurerm_resource_group" "test" { - name = "acctestrg-${var.random_integer}" - location = var.primary_location +resource "azurerm_kubernetes_fleet_manager" "test" { + name = "acctestkfm-%[2]s" + resource_group_name = azurerm_resource_group.test.name + location = azurerm_resource_group.test.location + hub_profile { + dns_prefix = "acctestkfm-%[2]s" + } + tags = { + new_environment = "terraform-acctests-updated" + } } -`, data.Locations.Primary, data.RandomInteger, data.RandomString) +`, r.template(data), data.RandomString) } diff --git a/website/docs/r/kubernetes_fleet_manager.html.markdown b/website/docs/r/kubernetes_fleet_manager.html.markdown index e271953ddcb4..1a4485eebe02 100644 --- a/website/docs/r/kubernetes_fleet_manager.html.markdown +++ b/website/docs/r/kubernetes_fleet_manager.html.markdown @@ -34,14 +34,34 @@ The following arguments are supported: * `resource_group_name` - (Required) Specifies the name of the Resource Group within which this Kubernetes Fleet Manager should exist. Changing this forces a new Kubernetes Fleet Manager to be created. +* `hub_profile` - (Optional) A hub_profile block as defined below. Changing this forces a new Kubernetes Fleet Manager to be created. + * `tags` - (Optional) A mapping of tags which should be assigned to the Kubernetes Fleet Manager. +--- + +A `hub_profile` block supports the following: + +* `dns_prefix` - (Required) DNS prefix used to create the FQDN for the Fleet hub. Changing this forces a new Kubernetes Fleet Manager to be created. + ## Attributes Reference In addition to the Arguments listed above - the following Attributes are exported: * `id` - The ID of the Kubernetes Fleet Manager. +* `hub_profile` - (Optional) A hub_profile block as defined below. + +--- + +A `hub_profile` block exports the following: + +* `fqdn` - The FQDN of the Fleet hub. + +* `kubernetes_version` - The Kubernetes version of the Fleet hub. + +* `portal_fqdn` - The Azure Portal FQDN of the Fleet hub. + ## Timeouts The `timeouts` block allows you to specify [timeouts](https://www.terraform.io/docs/configuration/resources.html#timeouts) for certain actions: