From 20e33b2458743775000717e883d6e9cf283c627d Mon Sep 17 00:00:00 2001 From: Valyria McFarland Date: Wed, 24 Apr 2024 16:09:28 -0700 Subject: [PATCH] deprecate configuration field in deployment resource --- .../nginx/nginx_deployment_resource.go | 197 +++++++++++++++++- .../nginx/nginx_deployment_resource_test.go | 113 +++++++++- 2 files changed, 306 insertions(+), 4 deletions(-) diff --git a/internal/services/nginx/nginx_deployment_resource.go b/internal/services/nginx/nginx_deployment_resource.go index 658ea3032f8e7..180d6c0eb117a 100644 --- a/internal/services/nginx/nginx_deployment_resource.go +++ b/internal/services/nginx/nginx_deployment_resource.go @@ -12,7 +12,9 @@ import ( "github.com/hashicorp/go-azure-helpers/lang/response" "github.com/hashicorp/go-azure-helpers/resourcemanager/commonschema" "github.com/hashicorp/go-azure-helpers/resourcemanager/identity" + "github.com/hashicorp/go-azure-sdk/resource-manager/nginx/2024-01-01-preview/nginxconfiguration" "github.com/hashicorp/go-azure-sdk/resource-manager/nginx/2024-01-01-preview/nginxdeployment" + "github.com/hashicorp/terraform-provider-azurerm/internal/features" "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" @@ -37,6 +39,20 @@ type NetworkInterface struct { SubnetId string `tfschema:"subnet_id"` } +// Deprecated: remove in next major version +type ConfigureFile struct { + Content string `tfschema:"content"` + VirtualPath string `tfschema:"virtual_path"` +} + +// Deprecated: remove in next major version +type Configuration struct { + ConfigureFile []ConfigureFile `tfschema:"config_file"` + ProtectedFile []ConfigureFile `tfschema:"protected_file"` + PackageData string `tfschema:"package_data"` + RootFile string `tfschema:"root_file"` +} + type AutoScaleProfile struct { Name string `tfschema:"name"` Min int64 `tfschema:"min_capacity"` @@ -61,7 +77,9 @@ type DeploymentModel struct { FrontendPrivate []FrontendPrivate `tfschema:"frontend_private"` NetworkInterface []NetworkInterface `tfschema:"network_interface"` UpgradeChannel string `tfschema:"automatic_upgrade_channel"` - Tags map[string]string `tfschema:"tags"` + // Deprecated: remove in next major version + Configuration []Configuration `tfschema:"configuration,removedInNextMajorVersion"` + Tags map[string]string `tfschema:"tags"` } type DeploymentResource struct{} @@ -69,7 +87,7 @@ type DeploymentResource struct{} var _ sdk.ResourceWithUpdate = (*DeploymentResource)(nil) func (m DeploymentResource) Arguments() map[string]*pluginsdk.Schema { - return map[string]*pluginsdk.Schema{ + resource := map[string]*pluginsdk.Schema{ "resource_group_name": commonschema.ResourceGroupName(), "name": { @@ -239,6 +257,78 @@ func (m DeploymentResource) Arguments() map[string]*pluginsdk.Schema { "tags": commonschema.Tags(), } + + if !features.FourPointOhBeta() { + resource["configuration"] = &pluginsdk.Schema{ + Deprecated: "Use azurerm_nginx_configuration resource instead. This attribute will be removed in the next major version of the provider.", + Type: pluginsdk.TypeList, + Optional: true, + Computed: !features.FourPointOh(), + MaxItems: 1, + Elem: &pluginsdk.Resource{ + Schema: map[string]*pluginsdk.Schema{ + "config_file": { + Type: pluginsdk.TypeSet, + Optional: true, + AtLeastOneOf: []string{"configuration.0.config_file", "configuration.0.package_data"}, + Elem: &pluginsdk.Resource{ + Schema: map[string]*pluginsdk.Schema{ + "content": { + Type: pluginsdk.TypeString, + Required: true, + ValidateFunc: validation.StringIsBase64, + }, + + "virtual_path": { + Type: pluginsdk.TypeString, + Required: true, + ValidateFunc: validation.StringIsNotEmpty, + }, + }, + }, + }, + + "protected_file": { + Type: pluginsdk.TypeSet, + Optional: true, + RequiredWith: []string{"configuration.0.config_file"}, + Elem: &pluginsdk.Resource{ + Schema: map[string]*pluginsdk.Schema{ + "content": { + Type: pluginsdk.TypeString, + Required: true, + Sensitive: true, + ValidateFunc: validation.StringIsBase64, + }, + + "virtual_path": { + Type: pluginsdk.TypeString, + Required: true, + ValidateFunc: validation.StringIsNotEmpty, + }, + }, + }, + }, + + "package_data": { + Type: pluginsdk.TypeString, + Optional: true, + ValidateFunc: validation.StringIsNotEmpty, + AtLeastOneOf: []string{"configuration.0.config_file", "configuration.0.package_data"}, + ConflictsWith: []string{"configuration.0.protected_file", "configuration.0.config_file"}, + }, + + "root_file": { + Type: pluginsdk.TypeString, + Required: true, + ValidateFunc: validation.StringIsNotEmpty, + }, + }, + }, + } + } + + return resource } func (m DeploymentResource) Attributes() map[string]*pluginsdk.Schema { @@ -388,6 +478,18 @@ func (m DeploymentResource) Create() sdk.ResourceFunc { return fmt.Errorf("creating %s: %v", id, err) } + if !features.FourPointOhBeta() { + if len(model.Configuration) > 0 { + // update configuration + configID := nginxconfiguration.NewConfigurationID(id.SubscriptionId, id.ResourceGroupName, id.NginxDeploymentName, defaultConfigurationName) + + configProp := expandConfiguration(model.Configuration[0]) + if err := meta.Client.Nginx.NginxConfiguration.ConfigurationsCreateOrUpdateThenPoll(ctx, configID, configProp); err != nil { + return fmt.Errorf("update default configuration of %q: %v", configID, err) + } + } + } + meta.SetID(id) return nil }, @@ -503,6 +605,46 @@ func (m DeploymentResource) Read() sdk.ResourceFunc { } } + if !features.FourPointOhBeta() { + // read configuration + configResp, err := meta.Client.Nginx.NginxConfiguration.ConfigurationsGet(ctx, nginxconfiguration.NewConfigurationID(id.SubscriptionId, id.ResourceGroupName, id.NginxDeploymentName, defaultConfigurationName)) + if err != nil && !response.WasNotFound(configResp.HttpResponse) { + return fmt.Errorf("retrieving default configuration of %q: %v", id, err) + } + if model := configResp.Model; model != nil { + if prop := model.Properties; prop != nil { + var files []ConfigureFile + if prop.Files != nil { + for _, file := range *prop.Files { + files = append(files, ConfigureFile{ + Content: pointer.From(file.Content), + VirtualPath: pointer.From(file.VirtualPath), + }) + } + } + + var protectedFiles []ConfigureFile + if prop.ProtectedFiles != nil { + for _, file := range *prop.ProtectedFiles { + protectedFiles = append(protectedFiles, ConfigureFile{ + Content: pointer.From(file.Content), + VirtualPath: pointer.From(file.VirtualPath), + }) + } + } + + output.Configuration = []Configuration{ + { + ConfigureFile: files, + ProtectedFile: protectedFiles, + PackageData: pointer.From(pointer.From(prop.Package).Data), + RootFile: pointer.From(prop.RootFile), + }, + } + } + } + } + return meta.Encode(&output) }, } @@ -592,6 +734,17 @@ func (m DeploymentResource) Update() sdk.ResourceFunc { return fmt.Errorf("updating %s: %v", id, err) } + if !features.FourPointOhBeta() { + if meta.ResourceData.HasChange("configuration") { + configID := nginxconfiguration.NewConfigurationID(id.SubscriptionId, id.ResourceGroupName, id.NginxDeploymentName, defaultConfigurationName) + + configProp := expandConfiguration(model.Configuration[0]) + if err := meta.Client.Nginx.NginxConfiguration.ConfigurationsCreateOrUpdateThenPoll(ctx, configID, configProp); err != nil { + return fmt.Errorf("update default configuration of %q: %v", configID, err) + } + } + } + return nil }, } @@ -620,3 +773,43 @@ func (m DeploymentResource) Delete() sdk.ResourceFunc { func (m DeploymentResource) IDValidationFunc() pluginsdk.SchemaValidateFunc { return nginxdeployment.ValidateNginxDeploymentID } + +func expandConfiguration(model Configuration) nginxconfiguration.NginxConfiguration { + result := nginxconfiguration.NginxConfiguration{ + Properties: &nginxconfiguration.NginxConfigurationProperties{}, + } + + if len(model.ConfigureFile) > 0 { + var files []nginxconfiguration.NginxConfigurationFile + for _, file := range model.ConfigureFile { + files = append(files, nginxconfiguration.NginxConfigurationFile{ + Content: pointer.To(file.Content), + VirtualPath: pointer.To(file.VirtualPath), + }) + } + result.Properties.Files = &files + } + + if len(model.ProtectedFile) > 0 { + var files []nginxconfiguration.NginxConfigurationFile + for _, file := range model.ProtectedFile { + files = append(files, nginxconfiguration.NginxConfigurationFile{ + Content: pointer.To(file.Content), + VirtualPath: pointer.To(file.VirtualPath), + }) + } + result.Properties.ProtectedFiles = &files + } + + if model.PackageData != "" { + result.Properties.Package = &nginxconfiguration.NginxConfigurationPackage{ + Data: pointer.To(model.PackageData), + } + } + + if model.RootFile != "" { + result.Properties.RootFile = pointer.To(model.RootFile) + } + + return result +} diff --git a/internal/services/nginx/nginx_deployment_resource_test.go b/internal/services/nginx/nginx_deployment_resource_test.go index 39ea538df98a5..de7ad0878bb23 100644 --- a/internal/services/nginx/nginx_deployment_resource_test.go +++ b/internal/services/nginx/nginx_deployment_resource_test.go @@ -82,6 +82,41 @@ func TestAccNginxDeployment_systemAssignedIdentity(t *testing.T) { }) } +func TestAccNginxDeployment_withConfiguration(t *testing.T) { + data := acceptance.BuildTestData(t, nginx.DeploymentResource{}.ResourceType(), "test") + r := DeploymentResource{} + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.withConfiguration(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep(), + }) +} + +func TestAccNginxDeployment_updateWithConfiguration(t *testing.T) { + data := acceptance.BuildTestData(t, nginx.DeploymentResource{}.ResourceType(), "test") + r := DeploymentResource{} + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.basic(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep(), + { + Config: r.withConfiguration(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep(), + }) +} + func TestAccNginxDeployment_userAssignedIdentity(t *testing.T) { data := acceptance.BuildTestData(t, nginx.DeploymentResource{}.ResourceType(), "test") r := DeploymentResource{} @@ -100,8 +135,6 @@ func (a DeploymentResource) basic(data acceptance.TestData) string { return fmt.Sprintf(` - - %s resource "azurerm_nginx_deployment" "test" { @@ -217,6 +250,82 @@ resource "azurerm_nginx_deployment" "test" { `, a.template(data), data.RandomInteger, data.Locations.Primary) } +func (a DeploymentResource) withConfiguration(data acceptance.TestData) string { + return fmt.Sprintf(` +%s +locals { + config_content = base64encode(<<-EOT +http { + server { + listen 80; + location / { + auth_basic "Protected Area"; + auth_basic_user_file /opt/.htpasswd; + default_type text/html; + return 200 ' +
this one will be updated
+
at 10:38 am
+ '; + } + include site/*.conf; + } +} +EOT + ) + protected_content = base64encode(<<-EOT +user:$apr1$VeUA5kt.$IjjRk//8miRxDsZvD4daF1 +EOT + ) + sub_config_content = base64encode(<<-EOT +location /bbb { + default_type text/html; + return 200 ' +
this one will be updated
+
at 10:38 am
+ '; +} +EOT + ) +} +resource "azurerm_nginx_deployment" "test" { + name = "acctest-%[2]d" + resource_group_name = azurerm_resource_group.test.name + sku = "standard_Monthly" + location = azurerm_resource_group.test.location + diagnose_support_enabled = true + frontend_public { + ip_address = [azurerm_public_ip.test.id] + } + network_interface { + subnet_id = azurerm_subnet.test.id + } + capacity = 10 + email = "test@test.com" + configuration { + root_file = "/etc/nginx/nginx.conf" + config_file { + content = local.config_content + virtual_path = "/etc/nginx/nginx.conf" + } + config_file { + content = local.sub_config_content + virtual_path = "/etc/nginx/site/b.conf" + } + protected_file { + content = local.protected_content + virtual_path = "/opt/.htpasswd" + } + } + tags = { + foo = "bar" + } + lifecycle { + ignore_changes = [configuration.0.protected_file] + } +} +`, a.template(data), data.RandomInteger, data.Locations.Primary) +} + func (a DeploymentResource) update(data acceptance.TestData) string { return fmt.Sprintf(`