Skip to content

Commit

Permalink
add nginx configuration resource and doc for nginx configuration
Browse files Browse the repository at this point in the history
  • Loading branch information
wuxu92 committed Oct 14, 2022
1 parent eebab07 commit 9ad8e48
Show file tree
Hide file tree
Showing 4 changed files with 574 additions and 0 deletions.
318 changes: 318 additions & 0 deletions internal/services/nginx/nginx_configuration_resource.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,318 @@
package nginx

import (
"context"
"fmt"
"net/http"
"time"

"github.com/hashicorp/go-azure-helpers/lang/pointer"
"github.com/hashicorp/go-azure-helpers/lang/response"
"github.com/hashicorp/go-azure-sdk/resource-manager/nginx/2022-08-01/nginxconfiguration"
"github.com/hashicorp/go-azure-sdk/resource-manager/nginx/2022-08-01/nginxdeployment"
"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"
)

const defaultConfigurationName = "default"

type ConfigFile struct {
Content string `tfschema:"content"`
VirtualPath string `tfschema:"virtual_path"`
}

type ProtectedFile struct {
Content string `tfschema:"content"`
VirtualPath string `tfschema:"virtual_path"`
}

type ConfigurationModel struct {
Name string `tfschema:"name"` // always default
NginxDeploymentId string `tfschema:"nginx_deployment_id"`
ConfigFile []ConfigFile `tfschema:"config_file"`
ProtectedFile []ProtectedFile `tfschema:"protected_file"`
PackageData string `tfschema:"package_data"`
RootFile string `tfschema:"root_file"`
}

// ToSDKModel used in both Create and Update
func (c ConfigurationModel) ToSDKModel() nginxconfiguration.NginxConfiguration {
req := nginxconfiguration.NginxConfiguration{
Name: pointer.FromString(c.Name),
Properties: &nginxconfiguration.NginxConfigurationProperties{
RootFile: pointer.FromString(c.RootFile),
},
}

var files []nginxconfiguration.NginxConfigurationFile
for _, file := range c.ConfigFile {
files = append(files, nginxconfiguration.NginxConfigurationFile{
Content: pointer.FromString(file.Content),
VirtualPath: pointer.FromString(file.VirtualPath),
})
}
req.Properties.Files = &files

if len(c.ProtectedFile) > 0 {
var protectedFiles []nginxconfiguration.NginxConfigurationFile
for _, file := range c.ProtectedFile {
protectedFiles = append(protectedFiles, nginxconfiguration.NginxConfigurationFile{
Content: pointer.FromString(file.Content),
VirtualPath: pointer.FromString(file.VirtualPath),
})
}
req.Properties.ProtectedFiles = &protectedFiles
}

if c.PackageData != "" {
req.Properties.Package = &nginxconfiguration.NginxConfigurationPackage{
Data: pointer.FromString(c.PackageData),
}
}

return req
}

type ConfigurationResource struct{}

var _ sdk.Resource = (*ConfigurationResource)(nil)

func (m ConfigurationResource) Arguments() map[string]*pluginsdk.Schema {
return map[string]*pluginsdk.Schema{
"nginx_deployment_id": {
Type: pluginsdk.TypeString,
Required: true,
ForceNew: true,
ValidateFunc: validation.StringIsNotEmpty,
},

"config_file": {
Type: pluginsdk.TypeList,
Required: true,
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.TypeList,
Optional: true,
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,
},
},
},
},

"package_data": {
Type: pluginsdk.TypeString,
Optional: true,
ValidateFunc: validation.StringIsNotEmpty,
},

"root_file": {
Type: pluginsdk.TypeString,
Required: true,
ValidateFunc: validation.StringIsNotEmpty,
},
}
}

func (m ConfigurationResource) Attributes() map[string]*pluginsdk.Schema {
return map[string]*pluginsdk.Schema{
// name of nginx configuration set to a fix value `default` by service team.
"name": {
Type: pluginsdk.TypeString,
Computed: true,
},
}
}

func (m ConfigurationResource) ModelObject() interface{} {
return &ConfigurationModel{}
}

func (m ConfigurationResource) ResourceType() string {
return "azurerm_nginx_configuration"
}

func (m ConfigurationResource) Create() sdk.ResourceFunc {
return sdk.ResourceFunc{
Timeout: 30 * time.Minute,
Func: func(ctx context.Context, meta sdk.ResourceMetaData) error {
client := meta.Client.Nginx.NginxConfiguration

var model ConfigurationModel
if err := meta.Decode(&model); err != nil {
return err
}

model.Name = defaultConfigurationName
deployID, err := nginxdeployment.ParseNginxDeploymentID(model.NginxDeploymentId)
if err != nil {
return err
}

subscriptionID := meta.Client.Account.SubscriptionId
id := nginxconfiguration.NewConfigurationID(subscriptionID, deployID.ResourceGroupName, deployID.DeploymentName, model.Name)
// get/list, get will cause internal server error if default configuration not exists
// todo remove set retry to 1
client.Client.RetryAttempts = 1
existing, err := client.ConfigurationsGet(ctx, id)
if !response.WasNotFound(existing.HttpResponse) && !response.WasStatusCode(existing.HttpResponse, http.StatusInternalServerError) {
if err != nil {
return fmt.Errorf("retreiving %s: %v", id, err)
}
return meta.ResourceRequiresImport(m.ResourceType(), id)
}

req := model.ToSDKModel()

future, err := client.ConfigurationsCreateOrUpdate(ctx, id, req)
if err != nil {
return fmt.Errorf("creating %s: %v", id, err)
}

if err := future.Poller.PollUntilDone(); err != nil {
return fmt.Errorf("waiting for creation of %s: %v", id, err)
}

meta.SetID(id)
return nil
},
}
}

func (m ConfigurationResource) Read() sdk.ResourceFunc {
return sdk.ResourceFunc{
Timeout: 5 * time.Minute,
Func: func(ctx context.Context, meta sdk.ResourceMetaData) error {
id, err := nginxconfiguration.ParseConfigurationID(meta.ResourceData.Id())
if err != nil {
return err
}

client := meta.Client.Nginx.NginxConfiguration
result, err := client.ConfigurationsGet(ctx, *id)
if err != nil {
return err
}

if result.Model == nil {
return fmt.Errorf("retrieving %s got nil model", id)
}

var output ConfigurationModel
output.Name = pointer.ToString(result.Model.Name)
deployID := nginxdeployment.NewNginxDeploymentID(id.SubscriptionId, id.ResourceGroupName, id.DeploymentName)
output.NginxDeploymentId = deployID.ID()

if prop := result.Model.Properties; prop != nil {
output.RootFile = pointer.ToString(prop.RootFile)

if prop.Package != nil && prop.Package.Data != nil {
output.PackageData = pointer.ToString(prop.Package.Data)
}

if files := prop.Files; files != nil {
for _, file := range *files {
output.ConfigFile = append(output.ConfigFile, ConfigFile{
Content: pointer.ToString(file.Content),
VirtualPath: pointer.ToString(file.VirtualPath),
})
}
}

if files := prop.ProtectedFiles; files != nil {
for _, file := range *files {
output.ProtectedFile = append(output.ProtectedFile, ProtectedFile{
Content: pointer.ToString(file.Content),
VirtualPath: pointer.ToString(file.VirtualPath),
})
}
}
}

return meta.Encode(&output)
},
}
}

func (m ConfigurationResource) Update() sdk.ResourceFunc {
return sdk.ResourceFunc{
Timeout: 10 * time.Minute,
Func: func(ctx context.Context, meta sdk.ResourceMetaData) (err error) {
client := meta.Client.Nginx.NginxConfiguration
id, err := nginxconfiguration.ParseConfigurationID(meta.ResourceData.Id())
if err != nil {
return err
}

var model ConfigurationModel
if err = meta.Decode(&model); err != nil {
return fmt.Errorf("decoding err: %+v", err)
}

upd := model.ToSDKModel()
result, err := client.ConfigurationsCreateOrUpdate(ctx, *id, upd)
if err != nil {
return fmt.Errorf("updating %s: %v", id, err)
}
if err := result.Poller.PollUntilDone(); err != nil {
return fmt.Errorf("waiting update %s: %v", *id, err)
}

return nil
},
}
}

func (m ConfigurationResource) Delete() sdk.ResourceFunc {
return sdk.ResourceFunc{
Timeout: 10 * time.Minute,
Func: func(ctx context.Context, meta sdk.ResourceMetaData) error {
id, err := nginxconfiguration.ParseConfigurationID(meta.ResourceData.Id())
if err != nil {
return err
}

meta.Logger.Infof("deleting %s", id)
client := meta.Client.Nginx.NginxConfiguration
result, err := client.ConfigurationsDelete(ctx, *id)
if err != nil {
return fmt.Errorf("deleting %s: %v", id, err)
}
if err := result.Poller.PollUntilDone(); err != nil {
return fmt.Errorf("waiting deleting %s: %v", *id, err)
}
return nil
},
}
}

func (m ConfigurationResource) IDValidationFunc() pluginsdk.SchemaValidateFunc {
return nginxconfiguration.ValidateConfigurationID
}
Loading

0 comments on commit 9ad8e48

Please sign in to comment.