Skip to content

Commit

Permalink
New Resource: azurerm_application_load_balancer_frontend
Browse files Browse the repository at this point in the history
Signed-off-by: ziyeqf <[email protected]>
  • Loading branch information
ziyeqf committed Sep 28, 2023
1 parent f93d250 commit 82d0ba4
Show file tree
Hide file tree
Showing 7 changed files with 551 additions and 12 deletions.
1 change: 1 addition & 0 deletions internal/services/servicenetworking/registration.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ func (r Registration) DataSources() []sdk.DataSource {
func (r Registration) Resources() []sdk.Resource {
return []sdk.Resource{
ApplicationLoadBalancerResource{},
FrontendsResource{},
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,219 @@
package servicenetworking

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/tags"
"github.com/hashicorp/go-azure-sdk/resource-manager/servicenetworking/2023-05-01-preview/frontendsinterface"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
"github.com/hashicorp/terraform-provider-azurerm/helpers/tf"
"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"
)

type FrontendsResource struct{}

type FrontendsModel struct {
Name string `tfschema:"name"`
ApplicationLoadBalancerId string `tfschema:"application_load_balancer_id"`
Location string `tfschema:"location"`
Fqdn string `tfschema:"fully_qualified_domain_name"`
Tags map[string]interface{} `tfschema:"tags"`
}

var _ sdk.Resource = FrontendsResource{}

func (f FrontendsResource) Arguments() map[string]*schema.Schema {
return map[string]*schema.Schema{
"name": {
Type: pluginsdk.TypeString,
Required: true,
ForceNew: true,
ValidateFunc: validation.StringIsNotEmpty,
},

"application_load_balancer_id": {
Type: pluginsdk.TypeString,
Required: true,
ForceNew: true,
ValidateFunc: frontendsinterface.ValidateTrafficControllerID,
},

"location": commonschema.Location(),

"tags": commonschema.Tags(),
}
}

func (f FrontendsResource) Attributes() map[string]*schema.Schema {
return map[string]*schema.Schema{
"fully_qualified_domain_name": {
Type: pluginsdk.TypeString,
Computed: true,
},
}
}

func (f FrontendsResource) ModelObject() interface{} {
return &FrontendsModel{}
}

func (f FrontendsResource) ResourceType() string {
return "azurerm_application_load_balancer_frontend"
}

func (f FrontendsResource) IDValidationFunc() pluginsdk.SchemaValidateFunc {
return frontendsinterface.ValidateFrontendID
}

func (f FrontendsResource) Create() sdk.ResourceFunc {
return sdk.ResourceFunc{
Timeout: 30 * time.Minute,
Func: func(ctx context.Context, metadata sdk.ResourceMetaData) error {
client := metadata.Client.ServiceNetworking.FrontendsInterface

var config FrontendsModel
if err := metadata.Decode(&config); err != nil {
return fmt.Errorf("decoding %v", err)
}

trafficControllerId, err := frontendsinterface.ParseTrafficControllerID(config.ApplicationLoadBalancerId)
if err != nil {
return err
}

id := frontendsinterface.NewFrontendID(trafficControllerId.SubscriptionId, trafficControllerId.ResourceGroupName, trafficControllerId.TrafficControllerName, config.Name)

resp, err := client.Get(ctx, id)
if err != nil {
if !response.WasNotFound(resp.HttpResponse) {
return fmt.Errorf("checking for presence of existing %s: %+v", id, err)
}
}

if !response.WasNotFound(resp.HttpResponse) {
return tf.ImportAsExistsError(f.ResourceType(), id.ID())
}

frontend := frontendsinterface.Frontend{
Location: location.Normalize(config.Location),
Properties: &frontendsinterface.FrontendProperties{},
Tags: tags.Expand(config.Tags),
}

if err := client.CreateOrUpdateThenPoll(ctx, id, frontend); err != nil {
return fmt.Errorf("creating %s: %+v", id, err)
}

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

func (f FrontendsResource) Read() sdk.ResourceFunc {
return sdk.ResourceFunc{
Timeout: 5 * time.Minute,
Func: func(ctx context.Context, metadata sdk.ResourceMetaData) error {
client := metadata.Client.ServiceNetworking.FrontendsInterface

id, err := frontendsinterface.ParseFrontendID(metadata.ResourceData.Id())
if err != nil {
return fmt.Errorf("parsing %s: %+v", metadata.ResourceData.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", metadata.ResourceData.Id(), err)
}

trafficControllerId := frontendsinterface.NewTrafficControllerID(id.SubscriptionId, id.ResourceGroupName, id.TrafficControllerName)

state := FrontendsModel{
Name: id.FrontendName,
ApplicationLoadBalancerId: trafficControllerId.ID(),
}

if model := resp.Model; model != nil {
state.Location = location.NormalizeNilable(pointer.To(model.Location))
state.Tags = tags.Flatten(model.Tags)

if prop := model.Properties; prop != nil {
state.Fqdn = pointer.From(prop.Fqdn)
}
}

return metadata.Encode(&state)
},
}
}

func (f FrontendsResource) Update() sdk.ResourceFunc {
return sdk.ResourceFunc{
Timeout: 30 * time.Minute,
Func: func(ctx context.Context, metadata sdk.ResourceMetaData) error {
client := metadata.Client.ServiceNetworking.FrontendsInterface

id, err := frontendsinterface.ParseFrontendID(metadata.ResourceData.Id())
if err != nil {
return err
}

var config FrontendsModel
if err := metadata.Decode(&config); err != nil {
return fmt.Errorf("decoding %v", err)
}

resp, err := client.Get(ctx, *id)
if err != nil {
return fmt.Errorf("retiring %s: %+v", *id, err)
}

if resp.Model == nil {
return fmt.Errorf("retiring %s: Model was nil", *id)
}

model := *resp.Model

if metadata.ResourceData.HasChange("tags") {
model.Tags = tags.Expand(config.Tags)
}

if err := client.CreateOrUpdateThenPoll(ctx, *id, model); err != nil {
return fmt.Errorf("updating `azurerm_alb_frontend` %s: %+v", *id, err)
}

return nil
},
}
}

func (f FrontendsResource) Delete() sdk.ResourceFunc {
return sdk.ResourceFunc{
Timeout: 30 * time.Minute,
Func: func(ctx context.Context, metadata sdk.ResourceMetaData) error {
client := metadata.Client.ServiceNetworking.FrontendsInterface

id, err := frontendsinterface.ParseFrontendID(metadata.ResourceData.Id())
if err != nil {
return err
}

if err = client.DeleteThenPoll(ctx, *id); err != nil {
return fmt.Errorf("deleting %q: %+v", id.ID(), err)
}

return nil
},
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,169 @@
package servicenetworking_test

import (
"context"
"fmt"
"testing"

"github.com/hashicorp/go-azure-helpers/lang/pointer"
"github.com/hashicorp/go-azure-helpers/lang/response"
"github.com/hashicorp/go-azure-sdk/resource-manager/servicenetworking/2023-05-01-preview/frontendsinterface"
"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 ApplicationLoadBalancerFrontendResource struct{}

func (r ApplicationLoadBalancerFrontendResource) Exists(ctx context.Context, clients *clients.Client, state *pluginsdk.InstanceState) (*bool, error) {
id, err := frontendsinterface.ParseFrontendID(state.ID)
if err != nil {
return nil, fmt.Errorf("while parsing resource ID: %+v", err)
}

resp, err := clients.ServiceNetworking.FrontendsInterface.Get(ctx, *id)
if err != nil {
if response.WasNotFound(resp.HttpResponse) {
return pointer.To(false), nil
}
return nil, fmt.Errorf("while checking existence for %q: %+v", id.String(), err)
}
return pointer.To(resp.Model != nil), nil
}

func TestAccApplicationLoadBalancerFrontend_basic(t *testing.T) {
data := acceptance.BuildTestData(t, "azurerm_application_load_balancer_frontend", "test")

r := ApplicationLoadBalancerFrontendResource{}
data.ResourceTest(t, r, []acceptance.TestStep{
{
Config: r.basic(data),
Check: acceptance.ComposeTestCheckFunc(
check.That(data.ResourceName).ExistsInAzure(r),
check.That(data.ResourceName).Key("fully_qualified_domain_name").Exists(),
),
},
data.ImportStep(),
})
}

func TestAccApplicationLoadBalancerFrontend_complete(t *testing.T) {
data := acceptance.BuildTestData(t, "azurerm_application_load_balancer_frontend", "test")

r := ApplicationLoadBalancerFrontendResource{}
data.ResourceTest(t, r, []acceptance.TestStep{
{
Config: r.complete(data),
Check: acceptance.ComposeTestCheckFunc(
check.That(data.ResourceName).ExistsInAzure(r),
check.That(data.ResourceName).Key("fully_qualified_domain_name").Exists(),
),
},
data.ImportStep(),
})
}

func TestAccApplicationLoadBalancerFrontend_update(t *testing.T) {
data := acceptance.BuildTestData(t, "azurerm_application_load_balancer_frontend", "test")

r := ApplicationLoadBalancerFrontendResource{}
data.ResourceTest(t, r, []acceptance.TestStep{
{
Config: r.basic(data),
Check: acceptance.ComposeTestCheckFunc(
check.That(data.ResourceName).ExistsInAzure(r),
check.That(data.ResourceName).Key("fully_qualified_domain_name").Exists(),
),
},
data.ImportStep(),
{
Config: r.complete(data),
Check: acceptance.ComposeTestCheckFunc(
check.That(data.ResourceName).ExistsInAzure(r),
check.That(data.ResourceName).Key("fully_qualified_domain_name").Exists(),
),
},
data.ImportStep(),
})
}

func TestAccApplicationLoadBalancerFrontend_requiresImport(t *testing.T) {
data := acceptance.BuildTestData(t, "azurerm_application_load_balancer_frontend", "test")

r := ApplicationLoadBalancerFrontendResource{}
data.ResourceTest(t, r, []acceptance.TestStep{
{
Config: r.basic(data),
Check: acceptance.ComposeTestCheckFunc(
check.That(data.ResourceName).ExistsInAzure(r),
),
},
data.RequiresImportErrorStep(r.requiresImport),
})
}

func (r ApplicationLoadBalancerFrontendResource) template(data acceptance.TestData) string {
return fmt.Sprintf(`
resource "azurerm_resource_group" "test" {
name = "acctestrg-alb-%[1]d"
location = "%[2]s"
}
resource "azurerm_application_load_balancer" "test" {
name = "acctestalb-%[1]d"
location = azurerm_resource_group.test.location
resource_group_name = azurerm_resource_group.test.name
}
`, data.RandomInteger, data.Locations.Primary)
}

func (r ApplicationLoadBalancerFrontendResource) basic(data acceptance.TestData) string {
return fmt.Sprintf(`
provider "azurerm" {
features {
}
}
%s
resource "azurerm_application_load_balancer_frontend" "test" {
name = "acct-frnt-%d"
application_load_balancer_id = azurerm_application_load_balancer.test.id
location = azurerm_application_load_balancer.test.location
}
`, r.template(data), data.RandomInteger)
}

func (r ApplicationLoadBalancerFrontendResource) complete(data acceptance.TestData) string {
return fmt.Sprintf(`
provider "azurerm" {
features {
}
}
%s
resource "azurerm_application_load_balancer_frontend" "test" {
name = "acct-frnt-%d"
application_load_balancer_id = azurerm_application_load_balancer.test.id
location = azurerm_application_load_balancer.test.location
tags = {
"tag1" = "value1"
}
}
`, r.template(data), data.RandomInteger)
}

func (r ApplicationLoadBalancerFrontendResource) requiresImport(data acceptance.TestData) string {
return fmt.Sprintf(`
%s
resource "azurerm_application_load_balancer_frontend" "import" {
name = azurerm_application_load_balancer_frontend.test.name
application_load_balancer_id = azurerm_application_load_balancer_frontend.test.application_load_balancer_id
location = azurerm_application_load_balancer_frontend.test.location
}
`, r.basic(data))
}
Loading

0 comments on commit 82d0ba4

Please sign in to comment.