Seems to be almost ready...

This commit is contained in:
Sander van Harmelen 2015-05-22 15:31:14 +02:00
parent 1dbd32c6a7
commit 83e3ab1fc7
5 changed files with 372 additions and 99 deletions

View File

@ -23,7 +23,7 @@ func Provider() terraform.ResourceProvider {
},
ResourcesMap: map[string]*schema.Resource{
"azure_disk": resourceAzureDisk(),
"azure_data_disk": resourceAzureDataDisk(),
"azure_instance": resourceAzureInstance(),
"azure_security_group": resourceAzureSecurityGroup(),
"azure_virtual_network": resourceAzureVirtualNetwork(),

View File

@ -1,44 +1,301 @@
package azure
import "github.com/hashicorp/terraform/helper/schema"
import (
"fmt"
"log"
func resourceAzureDisk() *schema.Resource {
"github.com/hashicorp/terraform/helper/schema"
"github.com/svanharmelen/azure-sdk-for-go/management"
"github.com/svanharmelen/azure-sdk-for-go/management/virtualmachinedisk"
)
const dataDiskBlobStorageURL = "http://%s.blob.core.windows.net/disks/%s.vhd"
func resourceAzureDataDisk() *schema.Resource {
return &schema.Resource{
Create: resourceAzureDiskCreate,
Read: resourceAzureDiskRead,
Update: resourceAzureDiskUpdate,
Delete: resourceAzureDiskDelete,
Create: resourceAzureDataDiskCreate,
Read: resourceAzureDataDiskRead,
Update: resourceAzureDataDiskUpdate,
Delete: resourceAzureDataDiskDelete,
Schema: map[string]*schema.Schema{
"name": &schema.Schema{
Type: schema.TypeString,
Required: true,
Optional: true,
Computed: true,
ForceNew: true,
},
"lun": &schema.Schema{
Type: schema.TypeInt,
Required: true,
},
"size": &schema.Schema{
Type: schema.TypeInt,
Required: true,
},
"caching": &schema.Schema{
Type: schema.TypeString,
Optional: true,
Default: "None",
},
"storage": &schema.Schema{
Type: schema.TypeString,
Optional: true,
ForceNew: true,
},
"media_link": &schema.Schema{
Type: schema.TypeString,
Optional: true,
Computed: true,
ForceNew: true,
},
"source_media_link": &schema.Schema{
Type: schema.TypeString,
Optional: true,
ForceNew: true,
},
"virtual_machine": &schema.Schema{
Type: schema.TypeString,
Required: true,
},
},
}
}
func resourceAzureDiskCreate(d *schema.ResourceData, meta interface{}) error {
//mc := meta.(*management.Client)
func resourceAzureDataDiskCreate(d *schema.ResourceData, meta interface{}) error {
mc := meta.(*management.Client)
return resourceAzureDiskRead(d, meta)
if err := verifyDataDiskParameters(d); err != nil {
return err
}
lun := d.Get("lun").(int)
vm := d.Get("virtual_machine").(string)
p := virtualmachinedisk.CreateDataDiskParameters{
Lun: lun,
LogicalDiskSizeInGB: d.Get("size").(int),
HostCaching: hostCaching(d),
MediaLink: mediaLink(d),
SourceMediaLink: d.Get("source_media_link").(string),
}
if name, ok := d.GetOk("name"); ok {
p.DiskName = name.(string)
}
log.Printf("[DEBUG] Adding data disk %d to instance: %s", lun, vm)
req, err := virtualmachinedisk.NewClient(*mc).AddDataDisk(vm, vm, vm, p)
if err != nil {
return fmt.Errorf("Error adding data disk %d to instance %s: %s", lun, vm, err)
}
// Wait until the data disk is added
if err := mc.WaitForOperation(req, nil); err != nil {
return fmt.Errorf(
"Error waiting for data disk %d to be added to instance %s: %s", lun, vm, err)
}
log.Printf("[DEBUG] Retrieving data disk %d from instance %s", lun, vm)
disk, err := virtualmachinedisk.NewClient(*mc).GetDataDisk(vm, vm, vm, lun)
if err != nil {
return fmt.Errorf("Error retrieving data disk %d from instance %s: %s", lun, vm, err)
}
d.SetId(disk.DiskName)
return resourceAzureDataDiskRead(d, meta)
}
func resourceAzureDiskRead(d *schema.ResourceData, meta interface{}) error {
//mc := meta.(*management.Client)
func resourceAzureDataDiskRead(d *schema.ResourceData, meta interface{}) error {
mc := meta.(*management.Client)
lun := d.Get("lun").(int)
vm := d.Get("virtual_machine").(string)
log.Printf("[DEBUG] Retrieving data disk: %s", d.Id())
datadisk, err := virtualmachinedisk.NewClient(*mc).GetDataDisk(vm, vm, vm, lun)
if err != nil {
return fmt.Errorf("Error retrieving data disk %s: %s", d.Id(), err)
}
d.Set("name", datadisk.DiskName)
d.Set("lun", datadisk.Lun)
d.Set("size", datadisk.LogicalDiskSizeInGB)
d.Set("caching", datadisk.HostCaching)
d.Set("media_link", datadisk.MediaLink)
log.Printf("[DEBUG] Retrieving disk: %s", d.Id())
disk, err := virtualmachinedisk.NewClient(*mc).GetDisk(d.Id())
if err != nil {
return fmt.Errorf("Error retrieving disk %s: %s", d.Id(), err)
}
d.Set("virtual_machine", disk.AttachedTo.RoleName)
return nil
}
func resourceAzureDiskUpdate(d *schema.ResourceData, meta interface{}) error {
//mc := meta.(*management.Client)
func resourceAzureDataDiskUpdate(d *schema.ResourceData, meta interface{}) error {
mc := meta.(*management.Client)
lun := d.Get("lun").(int)
vm := d.Get("virtual_machine").(string)
return resourceAzureDiskRead(d, meta)
if d.HasChange("size") {
p := virtualmachinedisk.UpdateDiskParameters{
Name: d.Id(),
}
if d.HasChange("size") {
p.ResizedSizeInGB = d.Get("size").(int)
}
log.Printf("[DEBUG] Updating disk: %s", d.Id())
req, err := virtualmachinedisk.NewClient(*mc).UpdateDisk(d.Id(), p)
if err != nil {
return fmt.Errorf("Error updating disk %s: %s", d.Id(), err)
}
// Wait until the disk is updated
if err := mc.WaitForOperation(req, nil); err != nil {
return fmt.Errorf(
"Error waiting for disk %s to be updated: %s", d.Id(), err)
}
}
if d.HasChange("virtual_machine") {
log.Printf("[DEBUG] Detaching data disk: %s", d.Id())
req, err := virtualmachinedisk.NewClient(*mc).DeleteDataDisk(vm, vm, vm, lun, false)
if err != nil {
return fmt.Errorf("Error detaching data disk %s: %s", d.Id(), err)
}
// Wait until the data disk is detached
if err := mc.WaitForOperation(req, nil); err != nil {
return fmt.Errorf(
"Error waiting for data disk %s to be detached: %s", d.Id(), err)
}
p := virtualmachinedisk.CreateDataDiskParameters{
DiskName: d.Id(),
Lun: lun,
HostCaching: hostCaching(d),
MediaLink: mediaLink(d),
}
log.Printf("[DEBUG] Attaching data disk: %s", d.Id())
req, err = virtualmachinedisk.NewClient(*mc).AddDataDisk(vm, vm, vm, p)
if err != nil {
return fmt.Errorf("Error attaching data disk %s to instance %s: %s", d.Id(), vm, err)
}
// Wait until the data disk is attached
if err := mc.WaitForOperation(req, nil); err != nil {
return fmt.Errorf(
"Error waiting for data disk %s to be attached to instance %s: %s", d.Id(), vm, err)
}
// Make sure we return here since all possible changes are
// already updated if we reach this point
return nil
}
if d.HasChange("caching") || d.HasChange("lun") {
p := virtualmachinedisk.UpdateDataDiskParameters{
DiskName: d.Id(),
Lun: lun,
HostCaching: hostCaching(d),
MediaLink: mediaLink(d),
}
log.Printf("[DEBUG] Updating data disk: %s", d.Id())
req, err := virtualmachinedisk.NewClient(*mc).UpdateDataDisk(vm, vm, vm, lun, p)
if err != nil {
return fmt.Errorf("Error updating data disk %s: %s", d.Id(), err)
}
// Wait until the data disk is updated
if err := mc.WaitForOperation(req, nil); err != nil {
return fmt.Errorf(
"Error waiting for data disk %s to be updated: %s", d.Id(), err)
}
}
return resourceAzureDataDiskRead(d, meta)
}
func resourceAzureDiskDelete(d *schema.ResourceData, meta interface{}) error {
//mc := meta.(*management.Client)
func resourceAzureDataDiskDelete(d *schema.ResourceData, meta interface{}) error {
mc := meta.(*management.Client)
lun := d.Get("lun").(int)
vm := d.Get("virtual_machine").(string)
// If a name was not supplied, it means we created a new emtpy disk and we now want to
// delete that disk again. Otherwise we only want to detach the disk and keep the blob.
_, removeBlob := d.GetOk("name")
log.Printf("[DEBUG] Detaching data disk %s with removeBlob = %t", d.Id(), removeBlob)
req, err := virtualmachinedisk.NewClient(*mc).DeleteDataDisk(vm, vm, vm, lun, removeBlob)
if err != nil {
return fmt.Errorf(
"Error detaching data disk %s with removeBlob = %t: %s", d.Id(), removeBlob, err)
}
// Wait until the data disk is detached and optionally deleted
if err := mc.WaitForOperation(req, nil); err != nil {
return fmt.Errorf(
"Error waiting for data disk %s to be detached with removeBlob = %t: %s",
d.Id(), removeBlob, err)
}
d.SetId("")
return nil
}
func hostCaching(d *schema.ResourceData) virtualmachinedisk.HostCachingType {
switch d.Get("caching").(string) {
case "ReadOnly":
return virtualmachinedisk.HostCachingTypeReadOnly
case "ReadWrite":
return virtualmachinedisk.HostCachingTypeReadWrite
default:
return virtualmachinedisk.HostCachingTypeNone
}
}
func mediaLink(d *schema.ResourceData) string {
mediaLink, ok := d.GetOk("media_link")
if ok {
return mediaLink.(string)
}
name, ok := d.GetOk("name")
if !ok {
name = fmt.Sprintf("%s-%d", d.Get("virtual_machine").(string), d.Get("lun").(int))
}
return fmt.Sprintf(dataDiskBlobStorageURL, d.Get("storage").(string), name.(string))
}
func verifyDataDiskParameters(d *schema.ResourceData) error {
caching := d.Get("caching").(string)
if caching != "None" && caching != "ReadOnly" && caching != "ReadWrite" {
return fmt.Errorf(
"Invalid caching type %s! Valid options are 'None', 'ReadOnly' and 'ReadWrite'.", caching)
}
if _, ok := d.GetOk("media_link"); !ok {
if _, ok := d.GetOk("storage"); !ok {
return fmt.Errorf("If not supplying 'media_link', you must supply 'storage'.")
}
}
return nil
}

View File

@ -2,6 +2,7 @@ package azure
import (
"bytes"
"encoding/base64"
"fmt"
"log"
@ -16,8 +17,9 @@ import (
)
const (
linux = "Linux"
windows = "Windows"
linux = "Linux"
windows = "Windows"
osDiskBlobStorageURL = "http://%s.blob.core.windows.net/vhds/%s.vhd"
)
func resourceAzureInstance() *schema.Resource {
@ -183,28 +185,23 @@ func resourceAzureInstanceCreate(d *schema.ResourceData, meta interface{}) (err
return err
}
// Verify if we have all parameters required for the image OS type
if err := verifyParameters(d, osType); err != nil {
// Verify if we have all required parameters
if err := verifyInstanceParameters(d, osType); err != nil {
return err
}
log.Printf("[DEBUG] Creating Cloud Service for instance: %s", name)
req, err := hostedservice.NewClient(*mc).
CreateHostedService(
name,
d.Get("location").(string),
d.Get("reverse_dns").(string),
name,
fmt.Sprintf("Cloud Service created automatically for instance %s", name),
)
if err != nil {
return fmt.Errorf("Error creating Cloud Service for instance %s: %s", name, err)
p := hostedservice.CreateHostedServiceParameters{
ServiceName: name,
Label: base64.StdEncoding.EncodeToString([]byte(name)),
Description: fmt.Sprintf("Cloud Service created automatically for instance %s", name),
Location: d.Get("location").(string),
ReverseDNSFqdn: d.Get("reverse_dns").(string),
}
// Wait until the Cloud Service is created
if err := mc.WaitAsyncOperation(req); err != nil {
return fmt.Errorf(
"Error waiting for Cloud Service of instance %s to be created: %s", name, err)
log.Printf("[DEBUG] Creating Cloud Service for instance: %s", name)
err = hostedservice.NewClient(*mc).CreateHostedService(p)
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
@ -217,9 +214,9 @@ func resourceAzureInstanceCreate(d *schema.ResourceData, meta interface{}) (err
}
// Wait until the Cloud Service is deleted
if err := mc.WaitAsyncOperation(req); err != nil {
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)
"[DEBUG] Error waiting for Cloud Service of instance %s to be deleted: %s", name, err)
}
}
}(mc)
@ -308,13 +305,13 @@ func resourceAzureInstanceCreate(d *schema.ResourceData, meta interface{}) (err
}
log.Printf("[DEBUG] Creating the new instance...")
req, err = virtualmachine.NewClient(*mc).CreateDeployment(role, name, options)
req, err := virtualmachine.NewClient(*mc).CreateDeployment(role, name, options)
if err != nil {
return fmt.Errorf("Error creating instance %s: %s", name, err)
}
log.Printf("[DEBUG] Waiting for the new instance to be created...")
if err := mc.WaitAsyncOperation(req); err != nil {
if err := mc.WaitForOperation(req, nil); err != nil {
return fmt.Errorf(
"Error waiting for instance %s to be created: %s", name, err)
}
@ -400,14 +397,15 @@ func resourceAzureInstanceRead(d *schema.ResourceData, meta interface{}) error {
connType := "ssh"
if dpmt.RoleList[0].OSVirtualHardDisk.OS == windows {
connType = windows
connType = "winrm"
}
// Set the connection info for any configured provisioners
d.SetConnInfo(map[string]string{
"type": connType,
"host": dpmt.VirtualIPs[0].Address,
"user": d.Get("username").(string),
"type": connType,
"host": dpmt.VirtualIPs[0].Address,
"user": d.Get("username").(string),
"password": d.Get("password").(string),
})
return nil
@ -427,8 +425,8 @@ func resourceAzureInstanceUpdate(d *schema.ResourceData, meta interface{}) error
return fmt.Errorf("Error retrieving role of instance %s: %s", d.Id(), err)
}
// Verify if we have all parameters required for the image OS type
if err := verifyParameters(d, role.OSVirtualHardDisk.OS); err != nil {
// Verify if we have all required parameters
if err := verifyInstanceParameters(d, role.OSVirtualHardDisk.OS); err != nil {
return err
}
@ -481,7 +479,7 @@ func resourceAzureInstanceUpdate(d *schema.ResourceData, meta interface{}) error
return fmt.Errorf("Error updating role of instance %s: %s", d.Id(), err)
}
if err := mc.WaitAsyncOperation(req); err != nil {
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)
}
@ -499,7 +497,7 @@ func resourceAzureInstanceDelete(d *schema.ResourceData, meta interface{}) error
}
// Wait until the instance is deleted
if err := mc.WaitAsyncOperation(req); err != nil {
if err := mc.WaitForOperation(req, nil); err != nil {
return fmt.Errorf(
"Error waiting for instance %s to be deleted: %s", d.Id(), err)
}
@ -524,12 +522,12 @@ func retrieveImageDetails(
mc *management.Client,
label string,
storage string) (func(*virtualmachine.Role) error, string, error) {
configureForImage, osType, err := retrieveOSImageDetails(mc, label, storage)
configureForImage, osType, err := retrieveVMImageDetails(mc, label)
if err == nil {
return configureForImage, osType, nil
}
configureForImage, osType, err = retrieveVMImageDetails(mc, label)
configureForImage, osType, err = retrieveOSImageDetails(mc, label, storage)
if err == nil {
return configureForImage, osType, nil
}
@ -537,53 +535,15 @@ func retrieveImageDetails(
return nil, "", fmt.Errorf("Could not find image with label '%s'", label)
}
func retrieveOSImageDetails(
mc *management.Client,
label,
storage string) (func(*virtualmachine.Role) error, string, error) {
imgs, err := osimage.NewClient(*mc).GetImageList()
if err != nil {
return nil, "", fmt.Errorf("Error retrieving image details: %s", err)
}
for _, img := range imgs {
if img.Label == label {
if img.OS != linux && img.OS != windows {
return nil, "", fmt.Errorf("Unsupported image OS: %s", img.OS)
}
if img.MediaLink == "" {
if storage == "" {
return nil, "",
fmt.Errorf("When using a platform image, the 'storage' parameter is required")
}
img.MediaLink = fmt.Sprintf("http://%s.blob.core.windows.net/vhds/%s.vhd", storage, label)
}
configureForImage := func(role *virtualmachine.Role) error {
return vmutils.ConfigureDeploymentFromPlatformImage(
role,
img.Name,
img.MediaLink,
label,
)
}
return configureForImage, img.OS, nil
}
}
return nil, "", fmt.Errorf("Could not find image with label '%s'", label)
}
func retrieveVMImageDetails(
mc *management.Client,
label string) (func(*virtualmachine.Role) error, string, error) {
imgs, err := virtualmachineimage.NewClient(*mc).GetImageList()
imgs, err := virtualmachineimage.NewClient(*mc).ListVirtualMachineImages()
if err != nil {
return nil, "", fmt.Errorf("Error retrieving image details: %s", err)
}
for _, img := range imgs {
for _, img := range imgs.VMImages {
if img.Label == label {
if img.OSDiskConfiguration.OS != linux && img.OSDiskConfiguration.OS != windows {
return nil, "", fmt.Errorf("Unsupported image OS: %s", img.OSDiskConfiguration.OS)
@ -605,6 +565,44 @@ func retrieveVMImageDetails(
return nil, "", fmt.Errorf("Could not find image with label '%s'", label)
}
func retrieveOSImageDetails(
mc *management.Client,
label,
storage string) (func(*virtualmachine.Role) error, string, error) {
imgs, err := osimage.NewClient(*mc).ListOSImages()
if err != nil {
return nil, "", fmt.Errorf("Error retrieving image details: %s", err)
}
for _, img := range imgs.OSImages {
if img.Label == label {
if img.OS != linux && img.OS != windows {
return nil, "", fmt.Errorf("Unsupported image OS: %s", img.OS)
}
if img.MediaLink == "" {
if storage == "" {
return nil, "",
fmt.Errorf("When using a platform image, the 'storage' parameter is required")
}
img.MediaLink = fmt.Sprintf(osDiskBlobStorageURL, storage, label)
}
configureForImage := func(role *virtualmachine.Role) error {
return vmutils.ConfigureDeploymentFromPlatformImage(
role,
img.Name,
img.MediaLink,
label,
)
}
return configureForImage, img.OS, nil
}
}
return nil, "", fmt.Errorf("Could not find image with label '%s'", label)
}
func endpointProtocol(p string) virtualmachine.InputEndpointProtocol {
if p == "tcp" {
return virtualmachine.InputEndpointProtocolTCP
@ -613,7 +611,7 @@ func endpointProtocol(p string) virtualmachine.InputEndpointProtocol {
return virtualmachine.InputEndpointProtocolUDP
}
func verifyParameters(d *schema.ResourceData, osType string) error {
func verifyInstanceParameters(d *schema.ResourceData, osType string) error {
if osType == linux {
_, pass := d.GetOk("password")
_, key := d.GetOk("ssh_key_thumbprint")

View File

@ -124,7 +124,7 @@ func resourceAzureSecurityGroupCreate(d *schema.ResourceData, meta interface{})
return fmt.Errorf("Error creating Network Security Group %s: %s", name, err)
}
if err := mc.WaitAsyncOperation(req); err != nil {
if err := mc.WaitForOperation(req, nil); err != nil {
return fmt.Errorf(
"Error waiting for Network Security Group %s to be created: %s", name, err)
}
@ -185,7 +185,7 @@ func resourceAzureSecurityGroupRuleCreate(
return fmt.Errorf("Error creating Network Security Group rule %s: %s", name, err)
}
if err := mc.WaitAsyncOperation(req); err != nil {
if err := mc.WaitForOperation(req, nil); err != nil {
return fmt.Errorf(
"Error waiting for Network Security Group rule %s to be created: %s", name, err)
}
@ -278,7 +278,7 @@ func resourceAzureSecurityGroupDelete(d *schema.ResourceData, meta interface{})
}
// Wait until the network security group is deleted
if err := mc.WaitAsyncOperation(req); err != nil {
if err := mc.WaitForOperation(req, nil); err != nil {
return fmt.Errorf(
"Error waiting for Network Security Group %s to be deleted: %s", d.Id(), err)
}
@ -300,7 +300,7 @@ func resourceAzureSecurityGroupRuleDelete(
return fmt.Errorf("Error deleting Network Security Group rule %s: %s", name, err)
}
if err := mc.WaitAsyncOperation(req); err != nil {
if err := mc.WaitForOperation(req, nil); err != nil {
return fmt.Errorf(
"Error waiting for Network Security Group rule %s to be deleted: %s", name, err)
}

View File

@ -76,11 +76,17 @@ func resourceAzureVirtualNetworkCreate(d *schema.ResourceData, meta interface{})
nc.Configuration.VirtualNetworkSites = append(nc.Configuration.VirtualNetworkSites, network)
err = virtualnetwork.NewClient(*mc).SetVirtualNetworkConfiguration(nc)
req, err := virtualnetwork.NewClient(*mc).SetVirtualNetworkConfiguration(nc)
if err != nil {
return fmt.Errorf("Error creating Virtual Network %s: %s", name, err)
}
// Wait until the virtual network is created
if err := mc.WaitForOperation(req, nil); err != nil {
log.Printf(
"[DEBUG] Error waiting for Virtual Network %s to be created: %s", name, err)
}
d.SetId(name)
return resourceAzureVirtualNetworkRead(d, meta)
@ -142,11 +148,17 @@ func resourceAzureVirtualNetworkUpdate(d *schema.ResourceData, meta interface{})
return fmt.Errorf("Virtual Network %s does not exists!", d.Id())
}
err = virtualnetwork.NewClient(*mc).SetVirtualNetworkConfiguration(nc)
req, err := virtualnetwork.NewClient(*mc).SetVirtualNetworkConfiguration(nc)
if err != nil {
return fmt.Errorf("Error updating Virtual Network %s: %s", d.Id(), err)
}
// Wait until the virtual network is updated
if err := mc.WaitForOperation(req, nil); err != nil {
log.Printf(
"[DEBUG] Error waiting for Virtual Network %s to be updated: %s", d.Id(), err)
}
return resourceAzureVirtualNetworkRead(d, meta)
}
@ -167,11 +179,17 @@ func resourceAzureVirtualNetworkDelete(d *schema.ResourceData, meta interface{})
nc.Configuration.VirtualNetworkSites = filtered
err = virtualnetwork.NewClient(*mc).SetVirtualNetworkConfiguration(nc)
req, err := virtualnetwork.NewClient(*mc).SetVirtualNetworkConfiguration(nc)
if err != nil {
return fmt.Errorf("Error deleting Virtual Network %s: %s", d.Id(), err)
}
// Wait until the virtual network is deleted
if err := mc.WaitForOperation(req, nil); err != nil {
log.Printf(
"[DEBUG] Error waiting for Virtual Network %s to be deleted: %s", d.Id(), err)
}
d.SetId("")
return nil