Skip to content

Commit

Permalink
deprecate configuration field in deployment resource
Browse files Browse the repository at this point in the history
  • Loading branch information
valyria257 committed Apr 26, 2024
1 parent d15c7ea commit 4a6a89b
Show file tree
Hide file tree
Showing 2 changed files with 308 additions and 4 deletions.
199 changes: 197 additions & 2 deletions internal/services/nginx/nginx_deployment_resource.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -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"`
Expand All @@ -61,15 +77,17 @@ 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{}

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": {
Expand Down Expand Up @@ -237,8 +255,82 @@ func (m DeploymentResource) Arguments() map[string]*pluginsdk.Schema {
}, false),
},

"configuration": {},

"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 {
Expand Down Expand Up @@ -388,6 +480,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
},
Expand Down Expand Up @@ -503,6 +607,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)
},
}
Expand Down Expand Up @@ -592,6 +736,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
},
}
Expand Down Expand Up @@ -620,3 +775,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
}
113 changes: 111 additions & 2 deletions internal/services/nginx/nginx_deployment_resource_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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{}
Expand All @@ -100,8 +135,6 @@ func (a DeploymentResource) basic(data acceptance.TestData) string {
return fmt.Sprintf(`
%s
resource "azurerm_nginx_deployment" "test" {
Expand Down Expand Up @@ -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 '<!doctype html><html lang="en"><head></head><body>
<div>this one will be updated</div>
<div>at 10:38 am</div>
</body></html>';
}
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 '<!doctype html><html lang="en"><head></head><body>
<div>this one will be updated</div>
<div>at 10:38 am</div>
</body></html>';
}
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 = "[email protected]"
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(`
Expand Down

0 comments on commit 4a6a89b

Please sign in to comment.