-
Notifications
You must be signed in to change notification settings - Fork 65
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
r/azurestack_subnet_route_table_association: new resource
- Loading branch information
1 parent
394db14
commit c17c119
Showing
5 changed files
with
593 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
257 changes: 257 additions & 0 deletions
257
internal/services/network/subnet_route_table_association_resource.go
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,257 @@ | ||
package network | ||
|
||
import ( | ||
"fmt" | ||
"log" | ||
"time" | ||
|
||
"github.com/Azure/azure-sdk-for-go/profiles/2020-09-01/network/mgmt/network" | ||
"github.com/hashicorp/terraform-provider-azurestack/internal/clients" | ||
"github.com/hashicorp/terraform-provider-azurestack/internal/locks" | ||
"github.com/hashicorp/terraform-provider-azurestack/internal/services/network/parse" | ||
"github.com/hashicorp/terraform-provider-azurestack/internal/services/network/validate" | ||
"github.com/hashicorp/terraform-provider-azurestack/internal/tf" | ||
"github.com/hashicorp/terraform-provider-azurestack/internal/tf/pluginsdk" | ||
"github.com/hashicorp/terraform-provider-azurestack/internal/tf/timeouts" | ||
"github.com/hashicorp/terraform-provider-azurestack/internal/utils" | ||
) | ||
|
||
func subnetRouteTableAssociation() *pluginsdk.Resource { | ||
return &pluginsdk.Resource{ | ||
Create: subnetRouteTableAssociationCreate, | ||
Read: subnetRouteTableAssociationRead, | ||
Delete: subnetRouteTableAssociationDelete, | ||
|
||
Timeouts: &pluginsdk.ResourceTimeout{ | ||
Create: pluginsdk.DefaultTimeout(30 * time.Minute), | ||
Read: pluginsdk.DefaultTimeout(5 * time.Minute), | ||
Update: pluginsdk.DefaultTimeout(30 * time.Minute), | ||
Delete: pluginsdk.DefaultTimeout(30 * time.Minute), | ||
}, | ||
|
||
Importer: pluginsdk.ImporterValidatingResourceId(func(id string) error { | ||
_, err := parse.SubnetID(id) | ||
return err | ||
}), | ||
|
||
Schema: map[string]*pluginsdk.Schema{ | ||
"subnet_id": { | ||
Type: pluginsdk.TypeString, | ||
Required: true, | ||
ForceNew: true, | ||
ValidateFunc: validate.SubnetID, | ||
}, | ||
|
||
"route_table_id": { | ||
Type: pluginsdk.TypeString, | ||
Required: true, | ||
ForceNew: true, | ||
ValidateFunc: validate.RouteTableID, | ||
}, | ||
}, | ||
} | ||
} | ||
|
||
func subnetRouteTableAssociationCreate(d *pluginsdk.ResourceData, meta interface{}) error { | ||
client := meta.(*clients.Client).Network.SubnetsClient | ||
vnetClient := meta.(*clients.Client).Network.VnetClient | ||
ctx, cancel := timeouts.ForCreate(meta.(*clients.Client).StopContext, d) | ||
defer cancel() | ||
|
||
log.Printf("[INFO] preparing arguments for Subnet <-> Route Table Association creation.") | ||
|
||
subnetId := d.Get("subnet_id").(string) | ||
routeTableId := d.Get("route_table_id").(string) | ||
|
||
parsedSubnetId, err := parse.SubnetID(subnetId) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
parsedRouteTableId, err := parse.RouteTableID(routeTableId) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
locks.ByName(parsedRouteTableId.Name, routeTableResourceName) | ||
defer locks.UnlockByName(parsedRouteTableId.Name, routeTableResourceName) | ||
|
||
subnetName := parsedSubnetId.Name | ||
virtualNetworkName := parsedSubnetId.VirtualNetworkName | ||
resourceGroup := parsedSubnetId.ResourceGroup | ||
|
||
locks.ByName(virtualNetworkName, VirtualNetworkResourceName) | ||
defer locks.UnlockByName(virtualNetworkName, VirtualNetworkResourceName) | ||
|
||
subnet, err := client.Get(ctx, resourceGroup, virtualNetworkName, subnetName, "") | ||
if err != nil { | ||
if utils.ResponseWasNotFound(subnet.Response) { | ||
return fmt.Errorf("Subnet %q (Virtual Network %q / Resource Group %q) was not found!", subnetName, virtualNetworkName, resourceGroup) | ||
} | ||
|
||
return fmt.Errorf("retrieving Subnet %q (Virtual Network %q / Resource Group %q): %+v", subnetName, virtualNetworkName, resourceGroup, err) | ||
} | ||
|
||
if props := subnet.SubnetPropertiesFormat; props != nil { | ||
if rt := props.RouteTable; rt != nil { | ||
// we're intentionally not checking the ID - if there's a RouteTable, it needs to be imported | ||
if rt.ID != nil && subnet.ID != nil { | ||
return tf.ImportAsExistsError("azurestack_subnet_route_table_association", *subnet.ID) | ||
} | ||
} | ||
|
||
props.RouteTable = &network.RouteTable{ | ||
ID: utils.String(routeTableId), | ||
} | ||
} | ||
|
||
future, err := client.CreateOrUpdate(ctx, resourceGroup, virtualNetworkName, subnetName, subnet) | ||
if err != nil { | ||
return fmt.Errorf("updating Route Table Association for Subnet %q (Virtual Network %q / Resource Group %q): %+v", subnetName, virtualNetworkName, resourceGroup, err) | ||
} | ||
|
||
if err = future.WaitForCompletionRef(ctx, client.Client); err != nil { | ||
return fmt.Errorf("waiting for completion of Route Table Association for Subnet %q (VN %q / Resource Group %q): %+v", subnetName, virtualNetworkName, resourceGroup, err) | ||
} | ||
|
||
timeout, _ := ctx.Deadline() | ||
|
||
stateConf := &pluginsdk.StateChangeConf{ | ||
Pending: []string{string(network.Updating)}, | ||
Target: []string{string(network.Succeeded)}, | ||
Refresh: SubnetProvisioningStateRefreshFunc(ctx, client, *parsedSubnetId), | ||
MinTimeout: 1 * time.Minute, | ||
Timeout: time.Until(timeout), | ||
} | ||
if _, err = stateConf.WaitForStateContext(ctx); err != nil { | ||
return fmt.Errorf("waiting for provisioning state of subnet for Route Table Association for Subnet %q (Virtual Network %q / Resource Group %q): %+v", subnetName, virtualNetworkName, resourceGroup, err) | ||
} | ||
|
||
vnetId := parse.NewVirtualNetworkID(parsedSubnetId.SubscriptionId, parsedSubnetId.ResourceGroup, parsedSubnetId.VirtualNetworkName) | ||
vnetStateConf := &pluginsdk.StateChangeConf{ | ||
Pending: []string{string(network.Updating)}, | ||
Target: []string{string(network.Succeeded)}, | ||
Refresh: VirtualNetworkProvisioningStateRefreshFunc(ctx, vnetClient, vnetId), | ||
MinTimeout: 1 * time.Minute, | ||
Timeout: time.Until(timeout), | ||
} | ||
if _, err = vnetStateConf.WaitForStateContext(ctx); err != nil { | ||
return fmt.Errorf("waiting for provisioning state of virtual network for Route Table Association for Subnet %q (Virtual Network %q / Resource Group %q): %+v", subnetName, virtualNetworkName, resourceGroup, err) | ||
} | ||
|
||
d.SetId(parsedSubnetId.ID()) | ||
|
||
return subnetRouteTableAssociationRead(d, meta) | ||
} | ||
|
||
func subnetRouteTableAssociationRead(d *pluginsdk.ResourceData, meta interface{}) error { | ||
client := meta.(*clients.Client).Network.SubnetsClient | ||
ctx, cancel := timeouts.ForRead(meta.(*clients.Client).StopContext, d) | ||
defer cancel() | ||
|
||
id, err := parse.SubnetID(d.Id()) | ||
if err != nil { | ||
return err | ||
} | ||
resourceGroup := id.ResourceGroup | ||
virtualNetworkName := id.VirtualNetworkName | ||
subnetName := id.Name | ||
|
||
resp, err := client.Get(ctx, resourceGroup, virtualNetworkName, subnetName, "") | ||
if err != nil { | ||
if utils.ResponseWasNotFound(resp.Response) { | ||
log.Printf("[DEBUG] Subnet %q (Virtual Network %q / Resource Group %q) could not be found - removing from state!", subnetName, virtualNetworkName, resourceGroup) | ||
d.SetId("") | ||
return nil | ||
} | ||
return fmt.Errorf("retrieving Subnet %q (Virtual Network %q / Resource Group %q): %+v", subnetName, virtualNetworkName, resourceGroup, err) | ||
} | ||
|
||
props := resp.SubnetPropertiesFormat | ||
if props == nil { | ||
return fmt.Errorf("Error: `properties` was nil for Subnet %q (Virtual Network %q / Resource Group %q)", subnetName, virtualNetworkName, resourceGroup) | ||
} | ||
|
||
routeTable := props.RouteTable | ||
if routeTable == nil { | ||
log.Printf("[DEBUG] Subnet %q (Virtual Network %q / Resource Group %q) doesn't have a Route Table - removing from state!", subnetName, virtualNetworkName, resourceGroup) | ||
d.SetId("") | ||
return nil | ||
} | ||
|
||
d.Set("subnet_id", resp.ID) | ||
d.Set("route_table_id", routeTable.ID) | ||
|
||
return nil | ||
} | ||
|
||
func subnetRouteTableAssociationDelete(d *pluginsdk.ResourceData, meta interface{}) error { | ||
client := meta.(*clients.Client).Network.SubnetsClient | ||
ctx, cancel := timeouts.ForDelete(meta.(*clients.Client).StopContext, d) | ||
defer cancel() | ||
|
||
id, err := parse.SubnetID(d.Id()) | ||
if err != nil { | ||
return err | ||
} | ||
resourceGroup := id.ResourceGroup | ||
virtualNetworkName := id.VirtualNetworkName | ||
subnetName := id.Name | ||
|
||
// retrieve the subnet | ||
read, err := client.Get(ctx, resourceGroup, virtualNetworkName, subnetName, "") | ||
if err != nil { | ||
if utils.ResponseWasNotFound(read.Response) { | ||
log.Printf("[DEBUG] Subnet %q (Virtual Network %q / Resource Group %q) could not be found - removing from state!", subnetName, virtualNetworkName, resourceGroup) | ||
return nil | ||
} | ||
|
||
return fmt.Errorf("retrieving Subnet %q (Virtual Network %q / Resource Group %q): %+v", subnetName, virtualNetworkName, resourceGroup, err) | ||
} | ||
|
||
props := read.SubnetPropertiesFormat | ||
if props == nil { | ||
return fmt.Errorf("`Properties` was nil for Subnet %q (Virtual Network %q / Resource Group %q)", subnetName, virtualNetworkName, resourceGroup) | ||
} | ||
|
||
if props.RouteTable == nil || props.RouteTable.ID == nil { | ||
log.Printf("[DEBUG] Subnet %q (Virtual Network %q / Resource Group %q) has no Route Table - removing from state!", subnetName, virtualNetworkName, resourceGroup) | ||
return nil | ||
} | ||
|
||
// once we have the route table id to lock on, lock on that | ||
parsedRouteTableId, err := parse.RouteTableID(*props.RouteTable.ID) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
locks.ByName(parsedRouteTableId.Name, routeTableResourceName) | ||
defer locks.UnlockByName(parsedRouteTableId.Name, routeTableResourceName) | ||
|
||
locks.ByName(virtualNetworkName, VirtualNetworkResourceName) | ||
defer locks.UnlockByName(virtualNetworkName, VirtualNetworkResourceName) | ||
|
||
// then re-retrieve it to ensure we've got the latest state | ||
read, err = client.Get(ctx, resourceGroup, virtualNetworkName, subnetName, "") | ||
if err != nil { | ||
if utils.ResponseWasNotFound(read.Response) { | ||
log.Printf("[DEBUG] Subnet %q (Virtual Network %q / Resource Group %q) could not be found - removing from state!", subnetName, virtualNetworkName, resourceGroup) | ||
return nil | ||
} | ||
|
||
return fmt.Errorf("retrieving Subnet %q (Virtual Network %q / Resource Group %q): %+v", subnetName, virtualNetworkName, resourceGroup, err) | ||
} | ||
|
||
read.SubnetPropertiesFormat.RouteTable = nil | ||
|
||
future, err := client.CreateOrUpdate(ctx, resourceGroup, virtualNetworkName, subnetName, read) | ||
if err != nil { | ||
return fmt.Errorf("removing Route Table Association from Subnet %q (Virtual Network %q / Resource Group %q): %+v", subnetName, virtualNetworkName, resourceGroup, err) | ||
} | ||
|
||
if err = future.WaitForCompletionRef(ctx, client.Client); err != nil { | ||
return fmt.Errorf("waiting for removal of Route Table Association from Subnet %q (Virtual Network %q / Resource Group %q): %+v", subnetName, virtualNetworkName, resourceGroup, err) | ||
} | ||
|
||
return nil | ||
} |
Oops, something went wrong.