Merge pull request #2515 from aznashwan/f-azure-instance-services
provider/azure: Made instances deployable on already existing services.
This commit is contained in:
commit
db11d80947
|
@ -11,7 +11,10 @@ import (
|
|||
var testAccProviders map[string]terraform.ResourceProvider
|
||||
var testAccProvider *schema.Provider
|
||||
|
||||
const testAccSecurityGroupName = "terraform-security-group"
|
||||
const (
|
||||
testAccSecurityGroupName = "terraform-security-group"
|
||||
testAccHostedServiceName = "terraform-testing-service"
|
||||
)
|
||||
|
||||
// testAccStorageServiceName is used as the name for the Storage Service
|
||||
// created in all storage-related tests.
|
||||
|
|
|
@ -37,6 +37,13 @@ func resourceAzureInstance() *schema.Resource {
|
|||
ForceNew: true,
|
||||
},
|
||||
|
||||
"hosted_service_name": &schema.Schema{
|
||||
Type: schema.TypeString,
|
||||
Optional: true,
|
||||
Computed: true,
|
||||
ForceNew: true,
|
||||
},
|
||||
|
||||
"description": &schema.Schema{
|
||||
Type: schema.TypeString,
|
||||
Optional: true,
|
||||
|
@ -197,8 +204,15 @@ func resourceAzureInstanceCreate(d *schema.ResourceData, meta interface{}) (err
|
|||
return err
|
||||
}
|
||||
|
||||
var hostedServiceName string
|
||||
// check if hosted service name parameter was given:
|
||||
if serviceName, ok := d.GetOk("hosted_service_name"); !ok {
|
||||
// if not provided; just use the name of the instance to create a new one:
|
||||
hostedServiceName = name
|
||||
d.Set("hosted_service_name", hostedServiceName)
|
||||
|
||||
p := hostedservice.CreateHostedServiceParameters{
|
||||
ServiceName: name,
|
||||
ServiceName: hostedServiceName,
|
||||
Label: base64.StdEncoding.EncodeToString([]byte(name)),
|
||||
Description: fmt.Sprintf("Cloud Service created automatically for instance %s", name),
|
||||
Location: d.Get("location").(string),
|
||||
|
@ -210,24 +224,11 @@ func resourceAzureInstanceCreate(d *schema.ResourceData, meta interface{}) (err
|
|||
if err != nil {
|
||||
return fmt.Errorf("Error creating Cloud Service for instance %s: %s", name, err)
|
||||
}
|
||||
|
||||
// Put in this defer here, so we are sure to cleanup already created parts
|
||||
// when we exit with an error
|
||||
defer func(mc management.Client) {
|
||||
if err != nil {
|
||||
req, err := hostedServiceClient.DeleteHostedService(name, true)
|
||||
if err != nil {
|
||||
log.Printf("[DEBUG] Error cleaning up Cloud Service of instance %s: %s", name, err)
|
||||
} else {
|
||||
// else; use the provided hosted service name:
|
||||
hostedServiceName = serviceName.(string)
|
||||
}
|
||||
|
||||
// Wait until the Cloud Service is deleted
|
||||
if err := mc.WaitForOperation(req, nil); err != nil {
|
||||
log.Printf(
|
||||
"[DEBUG] Error waiting for Cloud Service of instance %s to be deleted: %s", name, err)
|
||||
}
|
||||
}
|
||||
}(mc)
|
||||
|
||||
// Create a new role for the instance
|
||||
role := vmutils.NewVMConfiguration(name, d.Get("size").(string))
|
||||
|
||||
|
@ -312,7 +313,7 @@ func resourceAzureInstanceCreate(d *schema.ResourceData, meta interface{}) (err
|
|||
}
|
||||
|
||||
log.Printf("[DEBUG] Creating the new instance...")
|
||||
req, err := vmClient.CreateDeployment(role, name, options)
|
||||
req, err := vmClient.CreateDeployment(role, hostedServiceName, options)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error creating instance %s: %s", name, err)
|
||||
}
|
||||
|
@ -333,28 +334,41 @@ func resourceAzureInstanceRead(d *schema.ResourceData, meta interface{}) error {
|
|||
hostedServiceClient := azureClient.hostedServiceClient
|
||||
vmClient := azureClient.vmClient
|
||||
|
||||
log.Printf("[DEBUG] Retrieving Cloud Service for instance: %s", d.Id())
|
||||
cs, err := hostedServiceClient.GetHostedService(d.Id())
|
||||
name := d.Get("name").(string)
|
||||
|
||||
// check if the instance belongs to an independent hosted service
|
||||
// or it had one created for it.
|
||||
var hostedServiceName string
|
||||
if serviceName, ok := d.GetOk("hosted_service_name"); ok {
|
||||
// if independent; use that hosted service name:
|
||||
hostedServiceName = serviceName.(string)
|
||||
} else {
|
||||
// else; suppose it's the instance's name:
|
||||
hostedServiceName = name
|
||||
}
|
||||
|
||||
log.Printf("[DEBUG] Retrieving Cloud Service for instance: %s", name)
|
||||
cs, err := hostedServiceClient.GetHostedService(hostedServiceName)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error retrieving Cloud Service of instance %s: %s", d.Id(), err)
|
||||
return fmt.Errorf("Error retrieving Cloud Service of instance %s (%q): %s", name, hostedServiceName, err)
|
||||
}
|
||||
|
||||
d.Set("reverse_dns", cs.ReverseDNSFqdn)
|
||||
d.Set("location", cs.Location)
|
||||
|
||||
log.Printf("[DEBUG] Retrieving instance: %s", d.Id())
|
||||
dpmt, err := vmClient.GetDeployment(d.Id(), d.Id())
|
||||
log.Printf("[DEBUG] Retrieving instance: %s", name)
|
||||
dpmt, err := vmClient.GetDeployment(hostedServiceName, name)
|
||||
if err != nil {
|
||||
if management.IsResourceNotFoundError(err) {
|
||||
d.SetId("")
|
||||
return nil
|
||||
}
|
||||
return fmt.Errorf("Error retrieving instance %s: %s", d.Id(), err)
|
||||
return fmt.Errorf("Error retrieving instance %s: %s", name, err)
|
||||
}
|
||||
|
||||
if len(dpmt.RoleList) != 1 {
|
||||
return fmt.Errorf(
|
||||
"Instance %s has an unexpected number of roles: %d", d.Id(), len(dpmt.RoleList))
|
||||
"Instance %s has an unexpected number of roles: %d", name, len(dpmt.RoleList))
|
||||
}
|
||||
|
||||
d.Set("size", dpmt.RoleList[0].RoleSize)
|
||||
|
@ -362,7 +376,7 @@ func resourceAzureInstanceRead(d *schema.ResourceData, meta interface{}) error {
|
|||
if len(dpmt.RoleInstanceList) != 1 {
|
||||
return fmt.Errorf(
|
||||
"Instance %s has an unexpected number of role instances: %d",
|
||||
d.Id(), len(dpmt.RoleInstanceList))
|
||||
name, len(dpmt.RoleInstanceList))
|
||||
}
|
||||
d.Set("ip_address", dpmt.RoleInstanceList[0].IPAddress)
|
||||
|
||||
|
@ -400,7 +414,7 @@ func resourceAzureInstanceRead(d *schema.ResourceData, meta interface{}) error {
|
|||
default:
|
||||
return fmt.Errorf(
|
||||
"Instance %s has an unexpected number of associated subnets %d",
|
||||
d.Id(), len(dpmt.RoleInstanceList))
|
||||
name, len(dpmt.RoleInstanceList))
|
||||
}
|
||||
|
||||
// Update the security group
|
||||
|
@ -434,10 +448,13 @@ func resourceAzureInstanceUpdate(d *schema.ResourceData, meta interface{}) error
|
|||
return nil
|
||||
}
|
||||
|
||||
name := d.Get("name").(string)
|
||||
hostedServiceName := d.Get("hosted_service_name").(string)
|
||||
|
||||
// Get the current role
|
||||
role, err := vmClient.GetRole(d.Id(), d.Id(), d.Id())
|
||||
role, err := vmClient.GetRole(hostedServiceName, name, name)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error retrieving role of instance %s: %s", d.Id(), err)
|
||||
return fmt.Errorf("Error retrieving role of instance %s: %s", name, err)
|
||||
}
|
||||
|
||||
// Verify if we have all required parameters
|
||||
|
@ -473,7 +490,7 @@ func resourceAzureInstanceUpdate(d *schema.ResourceData, meta interface{}) error
|
|||
)
|
||||
if err != nil {
|
||||
return fmt.Errorf(
|
||||
"Error adding endpoint %s for instance %s: %s", m["name"].(string), d.Id(), err)
|
||||
"Error adding endpoint %s for instance %s: %s", m["name"].(string), name, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -484,19 +501,19 @@ func resourceAzureInstanceUpdate(d *schema.ResourceData, meta interface{}) error
|
|||
err := vmutils.ConfigureWithSecurityGroup(role, sg)
|
||||
if err != nil {
|
||||
return fmt.Errorf(
|
||||
"Error associating security group %s with instance %s: %s", sg, d.Id(), err)
|
||||
"Error associating security group %s with instance %s: %s", sg, name, err)
|
||||
}
|
||||
}
|
||||
|
||||
// Update the adjusted role
|
||||
req, err := vmClient.UpdateRole(d.Id(), d.Id(), d.Id(), *role)
|
||||
req, err := vmClient.UpdateRole(hostedServiceName, name, name, *role)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error updating role of instance %s: %s", d.Id(), err)
|
||||
return fmt.Errorf("Error updating role of instance %s: %s", name, err)
|
||||
}
|
||||
|
||||
if err := mc.WaitForOperation(req, nil); err != nil {
|
||||
return fmt.Errorf(
|
||||
"Error waiting for role of instance %s to be updated: %s", d.Id(), err)
|
||||
"Error waiting for role of instance %s to be updated: %s", name, err)
|
||||
}
|
||||
|
||||
return resourceAzureInstanceRead(d, meta)
|
||||
|
@ -505,21 +522,40 @@ func resourceAzureInstanceUpdate(d *schema.ResourceData, meta interface{}) error
|
|||
func resourceAzureInstanceDelete(d *schema.ResourceData, meta interface{}) error {
|
||||
azureClient := meta.(*Client)
|
||||
mc := azureClient.mgmtClient
|
||||
vmClient := azureClient.vmClient
|
||||
hostedServiceClient := azureClient.hostedServiceClient
|
||||
|
||||
log.Printf("[DEBUG] Deleting instance: %s", d.Id())
|
||||
req, err := hostedServiceClient.DeleteHostedService(d.Id(), true)
|
||||
name := d.Get("name").(string)
|
||||
hostedServiceName := d.Get("hosted_service_name").(string)
|
||||
|
||||
log.Printf("[DEBUG] Deleting instance: %s", name)
|
||||
|
||||
// check if the instance had a hosted service created especially for it:
|
||||
if name == hostedServiceName {
|
||||
// if so; we must delete the associated hosted service as well:
|
||||
req, err := hostedServiceClient.DeleteHostedService(name, true)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error deleting instance %s: %s", d.Id(), err)
|
||||
return fmt.Errorf("Error deleting instance and hosted service %s: %s", name, err)
|
||||
}
|
||||
|
||||
// Wait until the instance is deleted
|
||||
// Wait until the hosted service and the instance it contains is deleted:
|
||||
if err := mc.WaitForOperation(req, nil); err != nil {
|
||||
return fmt.Errorf(
|
||||
"Error waiting for instance %s to be deleted: %s", d.Id(), err)
|
||||
"Error waiting for instance %s to be deleted: %s", name, err)
|
||||
}
|
||||
} else {
|
||||
// else; just delete the instance:
|
||||
reqID, err := vmClient.DeleteDeployment(hostedServiceName, name)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error deleting instance %s off hosted service %s: %s", name, hostedServiceName, err)
|
||||
}
|
||||
|
||||
d.SetId("")
|
||||
// and wait for the deletion:
|
||||
if err := mc.WaitForOperation(reqID, nil); err != nil {
|
||||
return fmt.Errorf("Error waiting for intance %s to be deleted off the hosted service %s: %s",
|
||||
name, hostedServiceName, err)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -16,16 +16,46 @@ func TestAccAzureInstance_basic(t *testing.T) {
|
|||
resource.Test(t, resource.TestCase{
|
||||
PreCheck: func() { testAccPreCheck(t) },
|
||||
Providers: testAccProviders,
|
||||
CheckDestroy: testAccCheckAzureInstanceDestroy,
|
||||
CheckDestroy: testAccCheckAzureInstanceDestroyed(""),
|
||||
Steps: []resource.TestStep{
|
||||
resource.TestStep{
|
||||
Config: testAccAzureInstance_basic,
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
testAccCheckAzureInstanceExists(
|
||||
"azure_instance.foo", &dpmt),
|
||||
"azure_instance.foo", "", &dpmt),
|
||||
testAccCheckAzureInstanceBasicAttributes(&dpmt),
|
||||
resource.TestCheckResourceAttr(
|
||||
"azure_instance.foo", "name", "terraform-test"),
|
||||
resource.TestCheckResourceAttr(
|
||||
"azure_instance.foo", "hosted_service_name", "terraform-test"),
|
||||
resource.TestCheckResourceAttr(
|
||||
"azure_instance.foo", "location", "West US"),
|
||||
resource.TestCheckResourceAttr(
|
||||
"azure_instance.foo", "endpoint.2462817782.public_port", "22"),
|
||||
),
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func TestAccAzureInstance_separateHostedService(t *testing.T) {
|
||||
var dpmt virtualmachine.DeploymentResponse
|
||||
|
||||
resource.Test(t, resource.TestCase{
|
||||
PreCheck: func() { testAccPreCheck(t) },
|
||||
Providers: testAccProviders,
|
||||
CheckDestroy: testAccCheckAzureInstanceDestroyed(testAccHostedServiceName),
|
||||
Steps: []resource.TestStep{
|
||||
resource.TestStep{
|
||||
Config: testAccAzureInstance_seperateHostedService,
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
testAccCheckAzureInstanceExists(
|
||||
"azure_instance.foo", testAccHostedServiceName, &dpmt),
|
||||
testAccCheckAzureInstanceBasicAttributes(&dpmt),
|
||||
resource.TestCheckResourceAttr(
|
||||
"azure_instance.foo", "name", "terraform-test"),
|
||||
resource.TestCheckResourceAttr(
|
||||
"azure_instance.foo", "hosted_service_name", "terraform-testing-service"),
|
||||
resource.TestCheckResourceAttr(
|
||||
"azure_instance.foo", "location", "West US"),
|
||||
resource.TestCheckResourceAttr(
|
||||
|
@ -42,16 +72,18 @@ func TestAccAzureInstance_advanced(t *testing.T) {
|
|||
resource.Test(t, resource.TestCase{
|
||||
PreCheck: func() { testAccPreCheck(t) },
|
||||
Providers: testAccProviders,
|
||||
CheckDestroy: testAccCheckAzureInstanceDestroy,
|
||||
CheckDestroy: testAccCheckAzureInstanceDestroyed(""),
|
||||
Steps: []resource.TestStep{
|
||||
resource.TestStep{
|
||||
Config: testAccAzureInstance_advanced,
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
testAccCheckAzureInstanceExists(
|
||||
"azure_instance.foo", &dpmt),
|
||||
"azure_instance.foo", "", &dpmt),
|
||||
testAccCheckAzureInstanceAdvancedAttributes(&dpmt),
|
||||
resource.TestCheckResourceAttr(
|
||||
"azure_instance.foo", "name", "terraform-test1"),
|
||||
resource.TestCheckResourceAttr(
|
||||
"azure_instance.foo", "hosted_service_name", "terraform-test1"),
|
||||
resource.TestCheckResourceAttr(
|
||||
"azure_instance.foo", "size", "Basic_A1"),
|
||||
resource.TestCheckResourceAttr(
|
||||
|
@ -74,16 +106,18 @@ func TestAccAzureInstance_update(t *testing.T) {
|
|||
resource.Test(t, resource.TestCase{
|
||||
PreCheck: func() { testAccPreCheck(t) },
|
||||
Providers: testAccProviders,
|
||||
CheckDestroy: testAccCheckAzureInstanceDestroy,
|
||||
CheckDestroy: testAccCheckAzureInstanceDestroyed(""),
|
||||
Steps: []resource.TestStep{
|
||||
resource.TestStep{
|
||||
Config: testAccAzureInstance_advanced,
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
testAccCheckAzureInstanceExists(
|
||||
"azure_instance.foo", &dpmt),
|
||||
"azure_instance.foo", "", &dpmt),
|
||||
testAccCheckAzureInstanceAdvancedAttributes(&dpmt),
|
||||
resource.TestCheckResourceAttr(
|
||||
"azure_instance.foo", "name", "terraform-test1"),
|
||||
resource.TestCheckResourceAttr(
|
||||
"azure_instance.foo", "hosted_service_name", "terraform-test1"),
|
||||
resource.TestCheckResourceAttr(
|
||||
"azure_instance.foo", "size", "Basic_A1"),
|
||||
resource.TestCheckResourceAttr(
|
||||
|
@ -101,7 +135,7 @@ func TestAccAzureInstance_update(t *testing.T) {
|
|||
Config: testAccAzureInstance_update,
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
testAccCheckAzureInstanceExists(
|
||||
"azure_instance.foo", &dpmt),
|
||||
"azure_instance.foo", "", &dpmt),
|
||||
testAccCheckAzureInstanceUpdatedAttributes(&dpmt),
|
||||
resource.TestCheckResourceAttr(
|
||||
"azure_instance.foo", "size", "Basic_A2"),
|
||||
|
@ -119,6 +153,7 @@ func TestAccAzureInstance_update(t *testing.T) {
|
|||
|
||||
func testAccCheckAzureInstanceExists(
|
||||
n string,
|
||||
hostedServiceName string,
|
||||
dpmt *virtualmachine.DeploymentResponse) resource.TestCheckFunc {
|
||||
return func(s *terraform.State) error {
|
||||
rs, ok := s.RootModule().Resources[n]
|
||||
|
@ -130,8 +165,17 @@ func testAccCheckAzureInstanceExists(
|
|||
return fmt.Errorf("No instance ID is set")
|
||||
}
|
||||
|
||||
// if not hosted service was provided; it means that we expect it
|
||||
// to be identical with the name of the instance; which is in the ID.
|
||||
var serviceName string
|
||||
if hostedServiceName == "" {
|
||||
serviceName = rs.Primary.ID
|
||||
} else {
|
||||
serviceName = hostedServiceName
|
||||
}
|
||||
|
||||
vmClient := testAccProvider.Meta().(*Client).vmClient
|
||||
vm, err := vmClient.GetDeployment(rs.Primary.ID, rs.Primary.ID)
|
||||
vm, err := vmClient.GetDeployment(serviceName, rs.Primary.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -281,7 +325,8 @@ func testAccCheckAzureInstanceUpdatedAttributes(
|
|||
}
|
||||
}
|
||||
|
||||
func testAccCheckAzureInstanceDestroy(s *terraform.State) error {
|
||||
func testAccCheckAzureInstanceDestroyed(hostedServiceName string) resource.TestCheckFunc {
|
||||
return func(s *terraform.State) error {
|
||||
hostedServiceClient := testAccProvider.Meta().(*Client).hostedServiceClient
|
||||
|
||||
for _, rs := range s.RootModule().Resources {
|
||||
|
@ -293,7 +338,16 @@ func testAccCheckAzureInstanceDestroy(s *terraform.State) error {
|
|||
return fmt.Errorf("No instance ID is set")
|
||||
}
|
||||
|
||||
_, err := hostedServiceClient.GetHostedService(rs.Primary.ID)
|
||||
// if not hosted service was provided; it means that we expect it
|
||||
// to be identical with the name of the instance; which is in the ID.
|
||||
var serviceName string
|
||||
if hostedServiceName == "" {
|
||||
serviceName = rs.Primary.ID
|
||||
} else {
|
||||
serviceName = hostedServiceName
|
||||
}
|
||||
|
||||
_, err := hostedServiceClient.GetHostedService(serviceName)
|
||||
if err == nil {
|
||||
return fmt.Errorf("Instance %s still exists", rs.Primary.ID)
|
||||
}
|
||||
|
@ -305,6 +359,7 @@ func testAccCheckAzureInstanceDestroy(s *terraform.State) error {
|
|||
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
var testAccAzureInstance_basic = fmt.Sprintf(`
|
||||
resource "azure_instance" "foo" {
|
||||
|
@ -324,6 +379,31 @@ resource "azure_instance" "foo" {
|
|||
}
|
||||
}`, testAccStorageServiceName)
|
||||
|
||||
var testAccAzureInstance_seperateHostedService = fmt.Sprintf(`
|
||||
resource "azure_hosted_service" "foo" {
|
||||
name = "%s"
|
||||
location = "West US"
|
||||
ephemeral_contents = true
|
||||
}
|
||||
|
||||
resource "azure_instance" "foo" {
|
||||
name = "terraform-test"
|
||||
hosted_service_name = "${azure_hosted_service.foo.name}"
|
||||
image = "Ubuntu Server 14.04 LTS"
|
||||
size = "Basic_A1"
|
||||
storage_service_name = "%s"
|
||||
location = "West US"
|
||||
username = "terraform"
|
||||
password = "Pass!admin123"
|
||||
|
||||
endpoint {
|
||||
name = "SSH"
|
||||
protocol = "tcp"
|
||||
public_port = 22
|
||||
private_port = 22
|
||||
}
|
||||
}`, testAccHostedServiceName, testAccStorageServiceName)
|
||||
|
||||
var testAccAzureInstance_advanced = fmt.Sprintf(`
|
||||
resource "azure_virtual_network" "foo" {
|
||||
name = "terraform-vnet"
|
||||
|
|
|
@ -14,8 +14,17 @@ machine in the deployment based on the specified configuration.
|
|||
## Example Usage
|
||||
|
||||
```
|
||||
resource "azure_hosted_service" "terraform-service" {
|
||||
name = "terraform-service"
|
||||
location = "North Europe"
|
||||
ephemeral_contents = false
|
||||
description = "Hosted service created by Terraform."
|
||||
label = "tf-hs-01"
|
||||
}
|
||||
|
||||
resource "azure_instance" "web" {
|
||||
name = "terraform-test"
|
||||
hosted_service_name = "${azure_hosted_service.terraform-service.name}"
|
||||
image = "Ubuntu Server 14.04 LTS"
|
||||
size = "Basic_A1"
|
||||
storage_service_name = "yourstorage"
|
||||
|
@ -39,6 +48,11 @@ The following arguments are supported:
|
|||
* `name` - (Required) The name of the instance. Changing this forces a new
|
||||
resource to be created.
|
||||
|
||||
* `hosted_service_name` - (Optional) The name of the hosted service the
|
||||
instance should be deployed under. If not provided; it will default to the
|
||||
value of `name`. Changes to this parameter forces the creation of a new
|
||||
resource.
|
||||
|
||||
* `description` - (Optional) The description for the associated hosted service.
|
||||
Changing this forces a new resource to be created (defaults to the instance
|
||||
name).
|
||||
|
|
|
@ -21,7 +21,6 @@ resource "azure_sql_database_service" "sql-server" {
|
|||
max_size_bytes = "5368709120"
|
||||
service_level_id = "f1173c43-91bd-4aaa-973c-54e79e15235b"
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
## Argument Reference
|
||||
|
|
Loading…
Reference in New Issue