Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Loadbalancer probe #23

Merged
merged 9 commits into from
Aug 9, 2018
Merged
1 change: 1 addition & 0 deletions azurestack/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ func Provider() terraform.ResourceProvider {
"azurestack_local_network_gateway": resourceArmLocalNetworkGateway(),
"azurestack_lb": resourceArmLoadBalancer(),
"azurestack_lb_backend_address_pool": resourceArmLoadBalancerBackendAddressPool(),
"azurestack_lb_probe": resourceArmLoadBalancerProbe(),
"azurestack_public_ip": resourceArmPublicIp(),
"azurestack_resource_group": resourceArmResourceGroup(),
"azurestack_storage_account": resourceArmStorageAccount(),
Expand Down
292 changes: 292 additions & 0 deletions azurestack/resource_arm_loadbalancer_probe.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,292 @@
package azurestack

import (
"fmt"
"log"
"time"

"github.com/Azure/azure-sdk-for-go/services/network/mgmt/2015-06-15/network"
"github.com/hashicorp/errwrap"
"github.com/hashicorp/terraform/helper/resource"
"github.com/hashicorp/terraform/helper/schema"
"github.com/hashicorp/terraform/helper/validation"
"github.com/terraform-providers/terraform-provider-azurestack/azurestack/helpers/azure"
"github.com/terraform-providers/terraform-provider-azurestack/azurestack/utils"
)

func resourceArmLoadBalancerProbe() *schema.Resource {
return &schema.Resource{
Create: resourceArmLoadBalancerProbeCreateUpdate,
Read: resourceArmLoadBalancerProbeRead,
Update: resourceArmLoadBalancerProbeCreateUpdate,
Delete: resourceArmLoadBalancerProbeDelete,
Importer: &schema.ResourceImporter{
State: loadBalancerSubResourceStateImporter,
},

Schema: map[string]*schema.Schema{
"name": {
Type: schema.TypeString,
Required: true,
ForceNew: true,
ValidateFunc: validation.NoZeroValues,
},
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we get some validation here? at the very least a ValidateFunc: validation.NoZeroValues,


"resource_group_name": resourceGroupNameSchema(),

"loadbalancer_id": {
Type: schema.TypeString,
Required: true,
ForceNew: true,
ValidateFunc: azure.ValidateResourceId,
},
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As well here: ValidateFunc: azure.ValidateResourceID,


"protocol": {
Type: schema.TypeString,
Computed: true,
Optional: true,
StateFunc: ignoreCaseStateFunc,
DiffSuppressFunc: ignoreCaseDiffSuppressFunc,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we also validate and limit this property to the valid values?

ValidateFunc: validation.StringInSlice([]string{
"Tcp",
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we please use the SDK values here? ie string(network.ProbeProtocolHTTP),

"Http",
}, true),
},

"port": {
Type: schema.TypeInt,
Required: true,
ValidateFunc: validation.IntBetween(0, 65535),
},
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We could also validate the port: int between 0 and 65535)


"request_path": {
Type: schema.TypeString,
Optional: true,
},

"interval_in_seconds": {
Type: schema.TypeInt,
Optional: true,
Default: 15,
ValidateFunc: validation.IntAtLeast(5),
},
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we get some validation here too?

ValidateFunc: validation.IntAtLeast(5),


"number_of_probes": {
Type: schema.TypeInt,
Optional: true,
Default: 2,
},

"load_balancer_rules": {
Type: schema.TypeSet,
Computed: true,
Elem: &schema.Schema{Type: schema.TypeString},
Set: schema.HashString,
},
},
}
}

func resourceArmLoadBalancerProbeCreateUpdate(d *schema.ResourceData, meta interface{}) error {
client := meta.(*ArmClient).loadBalancerClient
ctx := meta.(*ArmClient).StopContext

loadBalancerID := d.Get("loadbalancer_id").(string)
armMutexKV.Lock(loadBalancerID)
defer armMutexKV.Unlock(loadBalancerID)

loadBalancer, exists, err := retrieveLoadBalancerById(loadBalancerID, meta)
if err != nil {
return fmt.Errorf("Error Getting LoadBalancer By ID: %+v", err)
}
if !exists {
d.SetId("")
log.Printf("[INFO] LoadBalancer %q not found. Removing from state", d.Get("name").(string))
return nil
}

newProbe := expandAzureRmLoadBalancerProbe(d)
if err != nil {
return fmt.Errorf("Error Expanding Probe: %+v", err)
}

probes := append(*loadBalancer.LoadBalancerPropertiesFormat.Probes, *newProbe)

existingProbe, existingProbeIndex, exists := findLoadBalancerProbeByName(loadBalancer, d.Get("name").(string))
if exists {
if d.Get("name").(string) == *existingProbe.Name {
// this probe is being updated/reapplied remove old copy from the slice
probes = append(probes[:existingProbeIndex], probes[existingProbeIndex+1:]...)
}
}

loadBalancer.LoadBalancerPropertiesFormat.Probes = &probes
resGroup, loadBalancerName, err := resourceGroupAndLBNameFromId(d.Get("loadbalancer_id").(string))
if err != nil {
return fmt.Errorf("Error Getting LoadBalancer Name and Group: %+v", err)
}

future, err := client.CreateOrUpdate(ctx, resGroup, loadBalancerName, *loadBalancer)
if err != nil {
return fmt.Errorf("Error Creating/Updating Load Balancer %q (Resource Group %q): %+v", loadBalancerName, resGroup, err)
}

err = future.WaitForCompletion(ctx, client.Client)
if err != nil {
return fmt.Errorf("Error waiting for completion of Load Balancer %q (Resource Group %q): %+v", loadBalancerName, resGroup, err)
}

read, err := client.Get(ctx, resGroup, loadBalancerName, "")
if err != nil {
return fmt.Errorf("Error retrieving Load Balancer %q (Resource Group %q): %+v", loadBalancerName, resGroup, err)
}
if read.ID == nil {
return fmt.Errorf("Cannot read LoadBalancer %q (resource group %q) ID", loadBalancerName, resGroup)
}

var createdProbeId string
for _, Probe := range *(*read.LoadBalancerPropertiesFormat).Probes {
if *Probe.Name == d.Get("name").(string) {
createdProbeId = *Probe.ID
}
}

if createdProbeId == "" {
return fmt.Errorf("Cannot find created LoadBalancer Probe ID %q", createdProbeId)
}

d.SetId(createdProbeId)

log.Printf("[DEBUG] Waiting for LoadBalancer (%s) to become available", loadBalancerName)
stateConf := &resource.StateChangeConf{
Pending: []string{"Accepted", "Updating"},
Target: []string{"Succeeded"},
Refresh: loadbalancerStateRefreshFunc(ctx, client, resGroup, loadBalancerName),
Timeout: 10 * time.Minute,
}
if _, err := stateConf.WaitForState(); err != nil {
return fmt.Errorf("Error waiting for LoadBalancer (%q - Resource Group %q) to become available: %+v", loadBalancerName, resGroup, err)
}

return resourceArmLoadBalancerProbeRead(d, meta)
}

func resourceArmLoadBalancerProbeRead(d *schema.ResourceData, meta interface{}) error {
id, err := parseAzureResourceID(d.Id())
if err != nil {
return err
}
name := id.Path["probes"]

loadBalancer, exists, err := retrieveLoadBalancerById(d.Get("loadbalancer_id").(string), meta)
if err != nil {
return errwrap.Wrapf("Error Getting LoadBalancer By ID {{err}}", err)
}
if !exists {
d.SetId("")
log.Printf("[INFO] LoadBalancer %q not found. Removing from state", name)
return nil
}

config, _, exists := findLoadBalancerProbeByName(loadBalancer, name)
if !exists {
d.SetId("")
log.Printf("[INFO] LoadBalancer Probe %q not found. Removing from state", name)
return nil
}

d.Set("name", config.Name)
d.Set("resource_group_name", id.ResourceGroup)

if properties := config.ProbePropertiesFormat; properties != nil {
d.Set("protocol", properties.Protocol)
d.Set("interval_in_seconds", properties.IntervalInSeconds)
d.Set("number_of_probes", properties.NumberOfProbes)
d.Set("port", properties.Port)
d.Set("request_path", properties.RequestPath)

var loadBalancerRules []string
if rules := properties.LoadBalancingRules; rules != nil {
for _, ruleConfig := range *rules {
loadBalancerRules = append(loadBalancerRules, *ruleConfig.ID)
}
}
d.Set("load_balancer_rules", loadBalancerRules)
}

return nil
}

func resourceArmLoadBalancerProbeDelete(d *schema.ResourceData, meta interface{}) error {
client := meta.(*ArmClient).loadBalancerClient
ctx := meta.(*ArmClient).StopContext

loadBalancerID := d.Get("loadbalancer_id").(string)
armMutexKV.Lock(loadBalancerID)
defer armMutexKV.Unlock(loadBalancerID)

loadBalancer, exists, err := retrieveLoadBalancerById(loadBalancerID, meta)
if err != nil {
return fmt.Errorf("Error Getting LoadBalancer By ID: %+v", err)
}
if !exists {
d.SetId("")
return nil
}

_, index, exists := findLoadBalancerProbeByName(loadBalancer, d.Get("name").(string))
if !exists {
return nil
}

oldProbes := *loadBalancer.LoadBalancerPropertiesFormat.Probes
newProbes := append(oldProbes[:index], oldProbes[index+1:]...)
loadBalancer.LoadBalancerPropertiesFormat.Probes = &newProbes

resGroup, loadBalancerName, err := resourceGroupAndLBNameFromId(d.Get("loadbalancer_id").(string))
if err != nil {
return errwrap.Wrapf("Error Getting LoadBalancer Name and Group: {{err}}", err)
}

future, err := client.CreateOrUpdate(ctx, resGroup, loadBalancerName, *loadBalancer)
if err != nil {
return fmt.Errorf("Error Creating/Updating LoadBalancer %q (Resource Group %q): %+v", loadBalancerName, resGroup, err)
}

err = future.WaitForCompletion(ctx, client.Client)
if err != nil {
return fmt.Errorf("Error waiting for completion of LoadBalancer %q (Resource Group %q): %+v", loadBalancerName, resGroup, err)
}

read, err := client.Get(ctx, resGroup, loadBalancerName, "")
if err != nil {
return errwrap.Wrapf("Error Getting LoadBalancer {{err}}", err)
}
if read.ID == nil {
return fmt.Errorf("Cannot read LoadBalancer %s (resource group %s) ID", loadBalancerName, resGroup)
}

return nil
}

func expandAzureRmLoadBalancerProbe(d *schema.ResourceData) *network.Probe {

properties := network.ProbePropertiesFormat{
NumberOfProbes: utils.Int32(int32(d.Get("number_of_probes").(int))),
IntervalInSeconds: utils.Int32(int32(d.Get("interval_in_seconds").(int))),
Port: utils.Int32(int32(d.Get("port").(int))),
}

if v, ok := d.GetOk("protocol"); ok {
properties.Protocol = network.ProbeProtocol(v.(string))
}

if v, ok := d.GetOk("request_path"); ok {
properties.RequestPath = utils.String(v.(string))
}

return &network.Probe{
Name: utils.String(d.Get("name").(string)),
ProbePropertiesFormat: &properties,
}
}
Loading