From ca1eb1917b84127d31e55334c8c9dea1c3f822c7 Mon Sep 17 00:00:00 2001 From: Sander van Harmelen Date: Mon, 18 May 2015 15:40:45 +0200 Subject: [PATCH] Adding docs and tweaking the provider --- .../azure/resource_azure_instance.go | 152 +++++++++++++----- .../azure/resource_azure_security_group.go | 25 ++- website/source/assets/stylesheets/_docs.scss | 16 +- .../docs/providers/azure/index.html.markdown | 43 +++++ .../docs/providers/azure/r/disk.html.markdown | 58 +++++++ .../providers/azure/r/instance.html.markdown | 59 +++++++ .../providers/azure/r/security_group.markdown | 69 ++++++++ .../azure/r/virtual_network.html.markdown | 54 +++++++ website/source/layouts/azure.erb | 38 +++++ website/source/layouts/docs.erb | 10 +- 10 files changed, 467 insertions(+), 57 deletions(-) create mode 100644 website/source/docs/providers/azure/index.html.markdown create mode 100644 website/source/docs/providers/azure/r/disk.html.markdown create mode 100644 website/source/docs/providers/azure/r/instance.html.markdown create mode 100644 website/source/docs/providers/azure/r/security_group.markdown create mode 100644 website/source/docs/providers/azure/r/virtual_network.html.markdown create mode 100644 website/source/layouts/azure.erb diff --git a/builtin/providers/azure/resource_azure_instance.go b/builtin/providers/azure/resource_azure_instance.go index d0a4e1186..dd1752477 100644 --- a/builtin/providers/azure/resource_azure_instance.go +++ b/builtin/providers/azure/resource_azure_instance.go @@ -4,7 +4,6 @@ import ( "bytes" "fmt" "log" - "strings" "github.com/hashicorp/terraform/helper/hashcode" "github.com/hashicorp/terraform/helper/schema" @@ -12,6 +11,7 @@ import ( "github.com/svanharmelen/azure-sdk-for-go/management/hostedservice" "github.com/svanharmelen/azure-sdk-for-go/management/osimage" "github.com/svanharmelen/azure-sdk-for-go/management/virtualmachine" + "github.com/svanharmelen/azure-sdk-for-go/management/virtualmachineimage" "github.com/svanharmelen/azure-sdk-for-go/management/vmutils" ) @@ -55,6 +55,7 @@ func resourceAzureInstance() *schema.Resource { "subnet": &schema.Schema{ Type: schema.TypeString, Optional: true, + Computed: true, ForceNew: true, }, @@ -142,14 +143,10 @@ func resourceAzureInstance() *schema.Resource { Set: resourceAzureEndpointHash, }, - "security_groups": &schema.Schema{ - Type: schema.TypeSet, + "security_group": &schema.Schema{ + Type: schema.TypeString, Optional: true, Computed: true, - Elem: &schema.Schema{Type: schema.TypeString}, - Set: func(v interface{}) int { - return hashcode.String(v.(string)) - }, }, "ip_address": &schema.Schema{ @@ -177,19 +174,11 @@ func resourceAzureInstanceCreate(d *schema.ResourceData, meta interface{}) (err } // Retrieve the needed details of the image - imageName, imageURL, osType, err := retrieveImageDetails(mc, d.Get("image").(string)) + configForImage, osType, err := retrieveImageDetails(mc, d.Get("image").(string)) if err != nil { return err } - if imageURL == "" { - storage, ok := d.GetOk("storage") - if !ok { - return fmt.Errorf("When using a platform image, the 'storage' parameter is required") - } - imageURL = fmt.Sprintf("http://%s.blob.core.windows.net/vhds/%s.vhd", storage, name) - } - // Verify if we have all parameters required for the image OS type if err := verifyParameters(d, osType); err != nil { return err @@ -299,14 +288,23 @@ func resourceAzureInstanceCreate(d *schema.ResourceData, meta interface{}) (err } } - err = vmutils.ConfigureForSubnet(&role, d.Get("subnet").(string)) - if err != nil { - return fmt.Errorf( - "Error adding role to subnet %s for instance %s: %s", d.Get("subnet").(string), name, err) + if subnet, ok := d.GetOk("subnet"); ok { + err = vmutils.ConfigureWithSubnet(&role, subnet.(string)) + if err != nil { + return fmt.Errorf( + "Error associating subnet %s with instance %s: %s", d.Get("subnet").(string), name, err) + } + } + + if sg, ok := d.GetOk("security_group"); ok { + err = vmutils.ConfigureWithSecurityGroup(&role, sg.(string)) + if err != nil { + return fmt.Errorf( + "Error associating security group %s with instance %s: %s", sg.(string), name, err) + } } options := virtualmachine.CreateDeploymentOptions{ - Subnet: d.Get("subnet").(string), VirtualNetworkName: d.Get("virtual_network").(string), } @@ -349,11 +347,14 @@ func resourceAzureInstanceRead(d *schema.ResourceData, meta interface{}) error { return fmt.Errorf( "Instance %s has an unexpected number of roles: %d", d.Id(), len(dpmt.RoleList)) } + + d.Set("image", dpmt.RoleList[0].VMImageName) d.Set("size", dpmt.RoleList[0].RoleSize) if len(dpmt.RoleInstanceList) != 1 { return fmt.Errorf( - "Instance %s has an unexpected number of role instances %d", d.Id(), len(dpmt.RoleInstanceList)) + "Instance %s has an unexpected number of role instances: %d", + d.Id(), len(dpmt.RoleInstanceList)) } d.Set("ip_address", dpmt.RoleInstanceList[0].IpAddress) @@ -361,15 +362,16 @@ func resourceAzureInstanceRead(d *schema.ResourceData, meta interface{}) error { d.Set("vip_address", dpmt.RoleInstanceList[0].InstanceEndpoints[0].Vip) } - // Create a new set to hold all configured endpoints - endpoints := &schema.Set{ - F: resourceAzureEndpointHash, - } - - // Loop through all endpoints and add them to the set - for _, c := range *dpmt.RoleList[0].ConfigurationSets { + // Find the network configuration set + for _, c := range dpmt.RoleList[0].ConfigurationSets { if c.ConfigurationSetType == virtualmachine.ConfigurationSetTypeNetwork { - for _, ep := range *c.InputEndpoints { + // Create a new set to hold all configured endpoints + endpoints := &schema.Set{ + F: resourceAzureEndpointHash, + } + + // Loop through all endpoints + for _, ep := range c.InputEndpoints { endpoint := map[string]interface{}{} // Update the values @@ -379,8 +381,22 @@ func resourceAzureInstanceRead(d *schema.ResourceData, meta interface{}) error { endpoint["private_port"] = ep.LocalPort endpoints.Add(endpoint) } - d.Set("endpoint", endpoints) + + // Update the subnet + switch len(c.SubnetNames) { + case 1: + d.Set("subnet", c.SubnetNames[0]) + case 0: + d.Set("subnet", "") + default: + return fmt.Errorf( + "Instance %s has an unexpected number of associated subnets %d", + d.Id(), len(dpmt.RoleInstanceList)) + } + + // Update the security group + d.Set("security_group", c.NetworkSecurityGroup) } } @@ -403,7 +419,7 @@ func resourceAzureInstanceUpdate(d *schema.ResourceData, meta interface{}) error mc := meta.(*management.Client) // First check if anything we can update changed, and if not just return - if !d.HasChange("size") && !d.HasChange("endpoint") { + if !d.HasChange("size") && !d.HasChange("endpoint") && !d.HasChange("security_group") { return nil } @@ -426,10 +442,10 @@ func resourceAzureInstanceUpdate(d *schema.ResourceData, meta interface{}) error _, n := d.GetChange("endpoint") // Delete the existing endpoints - for i, c := range *role.ConfigurationSets { + for i, c := range role.ConfigurationSets { if c.ConfigurationSetType == virtualmachine.ConfigurationSetTypeNetwork { c.InputEndpoints = nil - (*role.ConfigurationSets)[i] = c + role.ConfigurationSets[i] = c } } @@ -452,6 +468,15 @@ func resourceAzureInstanceUpdate(d *schema.ResourceData, meta interface{}) error } } + if d.HasChange("security_group") { + sg := d.Get("security_group").(string) + err := vmutils.ConfigureWithSecurityGroup(role, sg) + if err != nil { + return fmt.Errorf( + "Error associating security group %s with instance %s: %s", sg, d.Id(), err) + } + } + // Update the adjusted role req, err := virtualmachine.NewClient(*mc).UpdateRole(d.Id(), d.Id(), d.Id(), *role) if err != nil { @@ -497,26 +522,65 @@ func resourceAzureEndpointHash(v interface{}) int { return hashcode.String(buf.String()) } -func retrieveImageDetails(mc *management.Client, label string) (string, string, string, error) { +func retrieveImageDetails(mc *management.Client, id, storage string) (func() error, string, error) { + imageName, imageURL, osType, err := retrieveOSImageDetails(mc, id) + if err == nil { + + return imageName, imageURL, osType, nil + } + + imageName, imageURL, osType, err = retrieveVMImageDetails(mc, id) + if err == nil { + return imageName, imageURL, osType, nil + } + + return "", "", "", fmt.Errorf("Could not find image with label or name '%s'", id) +} + +func retrieveOSImageDetails( + mc *management.Client, + label, + storage string) (func() 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") + } + imageURL = fmt.Sprintf("http://%s.blob.core.windows.net/vhds/%s.vhd", storage, id) + } + return img.Name, img.MediaLink, img.OS, nil + } + } + + return "", "", "", fmt.Errorf("Could not find image with label '%s'", label) +} + +func retrieveVMImageDetails(mc *management.Client, name string) (string, string, string, error) { + imgs, err := virtualmachineimage.NewClient(*mc).GetImageList() if err != nil { return "", "", "", fmt.Errorf("Error retrieving image details: %s", err) } - var labels []string for _, img := range imgs { - if img.Label == label { - if img.OS != linux && img.OS != windows { - return "", "", "", fmt.Errorf("Unsupported image OS: %s", img.OS) + if img.Name == name { + if img.OSDiskConfiguration.OS != linux && img.OSDiskConfiguration.OS != windows { + return "", "", "", fmt.Errorf("Unsupported image OS: %s", img.OSDiskConfiguration.OS) } - return img.Name, img.MediaLink, img.OS, nil + return img.Name, img.OSDiskConfiguration.MediaLink, img.OSDiskConfiguration.OS, nil } - labels = append(labels, img.Label) } - return "", "", "", - fmt.Errorf("Could not find image with label '%s', available labels are: %s", - label, strings.Join(labels, ",")) + return "", "", "", fmt.Errorf("Could not find image with name '%s'", name) } func endpointProtocol(p string) virtualmachine.InputEndpointProtocol { diff --git a/builtin/providers/azure/resource_azure_security_group.go b/builtin/providers/azure/resource_azure_security_group.go index 97351a91a..cbac36a3e 100644 --- a/builtin/providers/azure/resource_azure_security_group.go +++ b/builtin/providers/azure/resource_azure_security_group.go @@ -1,10 +1,12 @@ package azure import ( + "bytes" "fmt" "log" "strconv" + "github.com/hashicorp/terraform/helper/hashcode" "github.com/hashicorp/terraform/helper/schema" "github.com/svanharmelen/azure-sdk-for-go/management" "github.com/svanharmelen/azure-sdk-for-go/management/networksecuritygroup" @@ -107,9 +109,15 @@ func resourceAzureSecurityGroupCreate(d *schema.ResourceData, meta interface{}) name := d.Get("name").(string) + // Compute/set the label + label := d.Get("label").(string) + if label == "" { + label = name + } + req, err := networksecuritygroup.NewClient(*mc).CreateNetworkSecurityGroup( name, - d.Get("label").(string), + label, d.Get("location").(string), ) if err != nil { @@ -301,7 +309,20 @@ func resourceAzureSecurityGroupRuleDelete( } func resourceAzureSecurityGroupRuleHash(v interface{}) int { - return 0 + var buf bytes.Buffer + m := v.(map[string]interface{}) + buf.WriteString(fmt.Sprintf( + "%s-%d-%s-%s-%s-%s-%s-%s", + m["type"].(string), + m["priority"].(int), + m["action"].(string), + m["source_cidr"].(string), + m["source_port"].(string), + m["destination_cidr"].(string), + m["destination_port"].(string), + m["protocol"].(string))) + + return hashcode.String(buf.String()) } func verifySecurityGroupRuleParams(rule map[string]interface{}) error { diff --git a/website/source/assets/stylesheets/_docs.scss b/website/source/assets/stylesheets/_docs.scss index 48a92b0a7..0cf800715 100755 --- a/website/source/assets/stylesheets/_docs.scss +++ b/website/source/assets/stylesheets/_docs.scss @@ -7,22 +7,23 @@ body.page-sub{ } body.layout-atlas, -body.layout-consul, -body.layout-dnsimple, -body.layout-dme, -body.layout-docker, +body.layout-aws, +body.layout-azure, body.layout-cloudflare, body.layout-cloudstack, +body.layout-consul, +body.layout-digitalocean, +body.layout-dme, +body.layout-dnsimple, +body.layout-docker, body.layout-google, body.layout-heroku, body.layout-mailgun, body.layout-openstack, body.layout-template, -body.layout-digitalocean, -body.layout-aws, body.layout-docs, -body.layout-inner, body.layout-downloads, +body.layout-inner, body.layout-intro{ background: $light-black image-url('sidebar-wire.png') left 62px no-repeat; @@ -287,4 +288,3 @@ body.layout-intro{ } } } - diff --git a/website/source/docs/providers/azure/index.html.markdown b/website/source/docs/providers/azure/index.html.markdown new file mode 100644 index 000000000..fd4081a67 --- /dev/null +++ b/website/source/docs/providers/azure/index.html.markdown @@ -0,0 +1,43 @@ +--- +layout: "azure" +page_title: "Provider: Azure" +sidebar_current: "docs-azure-index" +description: |- + The Azure provider is used to interact with the many resources supported by Azure. The provider needs to be configured with a publish settings file and optionally a subscription ID before it can be used. +--- + +# Azure Provider + +The Azure provider is used to interact with the many resources supported +by Azure. The provider needs to be configured with a [publish settings +file](https://manage.windowsazure.com/publishsettings) and optionally a +subscription ID before it can be used. + +Use the navigation to the left to read about the available resources. + +## Example Usage + +``` +# Configure the Azure Provider +provider "azure" { + settings_file = "${var.azure_settings_file}" +} + +# Create a web server +resource "azure_instance" "web" { + ... +} +``` + +## Argument Reference + +The following arguments are supported: + +* `settings_file` - (Required) The path to a publish settings file used to + authenticate with the Azure API. You can download the settings file here: + https://manage.windowsazure.com/publishsettings. It must be provided, but + it can also be sourced from the `AZURE_SETTINGS_FILE` environment variable. + +* `subscription_id` - (Optional) The subscription ID to use. If not provided + the first subscription ID in publish settings file will be used. It can + also be sourced from the `AZURE_SUBSCRIPTION_ID` environment variable. diff --git a/website/source/docs/providers/azure/r/disk.html.markdown b/website/source/docs/providers/azure/r/disk.html.markdown new file mode 100644 index 000000000..53ae73656 --- /dev/null +++ b/website/source/docs/providers/azure/r/disk.html.markdown @@ -0,0 +1,58 @@ +--- +layout: "cloudstack" +page_title: "CloudStack: cloudstack_disk" +sidebar_current: "docs-cloudstack-resource-disk" +description: |- + Creates a disk volume from a disk offering. This disk volume will be attached to a virtual machine if the optional parameters are configured. +--- + +# cloudstack\_disk + +Creates a disk volume from a disk offering. This disk volume will be attached to +a virtual machine if the optional parameters are configured. + +## Example Usage + +``` +resource "cloudstack_disk" "default" { + name = "test-disk" + attach = "true" + disk_offering = "custom" + size = 50 + virtual-machine = "server-1" + zone = "zone-1" +} +``` + +## Argument Reference + +The following arguments are supported: + +* `name` - (Required) The name of the disk volume. Changing this forces a new + resource to be created. + +* `attach` - (Optional) Determines whether or not to attach the disk volume to a + virtual machine (defaults false). + +* `device` - (Optional) The device to map the disk volume to within the guest OS. + +* `disk_offering` - (Required) The name or ID of the disk offering to use for + this disk volume. + +* `size` - (Optional) The size of the disk volume in gigabytes. + +* `shrink_ok` - (Optional) Verifies if the disk volume is allowed to shrink when + resizing (defaults false). + +* `virtual_machine` - (Optional) The name of the virtual machine to which you + want to attach the disk volume. + +* `zone` - (Required) The name or ID of the zone where this disk volume will be available. + Changing this forces a new resource to be created. + +## Attributes Reference + +The following attributes are exported: + +* `id` - The ID of the disk volume. +* `device` - The device the disk volume is mapped to within the guest OS. diff --git a/website/source/docs/providers/azure/r/instance.html.markdown b/website/source/docs/providers/azure/r/instance.html.markdown new file mode 100644 index 000000000..8779494e4 --- /dev/null +++ b/website/source/docs/providers/azure/r/instance.html.markdown @@ -0,0 +1,59 @@ +--- +layout: "azure" +page_title: "Azure: azure_instance" +sidebar_current: "docs-azure-resource-instance" +description: |- + Creates and automatically starts a virtual machine based on a service offering, disk offering, and template. +--- + +# azure\_instance + +Creates and automatically starts a virtual machine based on a service offering, +disk offering, and template. + +## Example Usage + +``` +resource "azure_instance" "web" { + name = "server-1" + service_offering= "small" + network = "network-1" + template = "CentOS 6.5" + zone = "zone-1" +} +``` + +## Argument Reference + +The following arguments are supported: + +* `name` - (Required) The name of the instance. Changing this forces a new + resource to be created. + +* `display_name` - (Optional) The display name of the instance. + +* `service_offering` - (Required) The name or ID of the service offering used for this instance. + +* `network` - (Optional) The name or ID of the network to connect this instance to. + Changing this forces a new resource to be created. + +* `ipaddress` - (Optional) The IP address to assign to this instance. Changing + this forces a new resource to be created. + +* `template` - (Required) The name or ID of the template used for this instance. + Changing this forces a new resource to be created. + +* `zone` - (Required) The name of the zone where this instance will be created. + Changing this forces a new resource to be created. + +* `user_data` - (Optional) The user data to provide when launching the instance. + +* `expunge` - (Optional) This determines if the instance is expunged when it is + destroyed (defaults false) + +## Attributes Reference + +The following attributes are exported: + +* `id` - The instance ID. +* `display_name` - The display name of the instance. diff --git a/website/source/docs/providers/azure/r/security_group.markdown b/website/source/docs/providers/azure/r/security_group.markdown new file mode 100644 index 000000000..f82b8f446 --- /dev/null +++ b/website/source/docs/providers/azure/r/security_group.markdown @@ -0,0 +1,69 @@ +--- +layout: "cloudstack" +page_title: "CloudStack: cloudstack_network_acl_rule" +sidebar_current: "docs-cloudstack-resource-network-acl-rule" +description: |- + Creates network ACL rules for a given network ACL. +--- + +# cloudstack\_network\_acl\_rule + +Creates network ACL rules for a given network ACL. + +## Example Usage + +``` +resource "cloudstack_network_acl_rule" "default" { + aclid = "f3843ce0-334c-4586-bbd3-0c2e2bc946c6" + + rule { + action = "allow" + source_cidr = "10.0.0.0/8" + protocol = "tcp" + ports = ["80", "1000-2000"] + traffic_type = "ingress" + } +} +``` + +## Argument Reference + +The following arguments are supported: + +* `aclid` - (Required) The network ACL ID for which to create the rules. + Changing this forces a new resource to be created. + +* `managed` - (Optional) USE WITH CAUTION! If enabled all the firewall rules for + this network ACL will be managed by this resource. This means it will delete + all firewall rules that are not in your config! (defaults false) + +* `rule` - (Optional) Can be specified multiple times. Each rule block supports + fields documented below. If `managed = false` at least one rule is required! + +The `rule` block supports: + +* `action` - (Optional) The action for the rule. Valid options are: `allow` and + `deny` (defaults allow). + +* `source_cidr` - (Required) The source CIDR to allow access to the given ports. + +* `protocol` - (Required) The name of the protocol to allow. Valid options are: + `tcp`, `udp`, `icmp`, `all` or a valid protocol number. + +* `icmp_type` - (Optional) The ICMP type to allow. This can only be specified if + the protocol is ICMP. + +* `icmp_code` - (Optional) The ICMP code to allow. This can only be specified if + the protocol is ICMP. + +* `ports` - (Optional) List of ports and/or port ranges to allow. This can only + be specified if the protocol is TCP, UDP, ALL or a valid protocol number. + +* `traffic_type` - (Optional) The traffic type for the rule. Valid options are: + `ingress` or `egress` (defaults ingress). + +## Attributes Reference + +The following attributes are exported: + +* `id` - The ACL ID for which the rules are created. diff --git a/website/source/docs/providers/azure/r/virtual_network.html.markdown b/website/source/docs/providers/azure/r/virtual_network.html.markdown new file mode 100644 index 000000000..9819a8ce3 --- /dev/null +++ b/website/source/docs/providers/azure/r/virtual_network.html.markdown @@ -0,0 +1,54 @@ +--- +layout: "cloudstack" +page_title: "CloudStack: cloudstack_network" +sidebar_current: "docs-cloudstack-resource-network" +description: |- + Creates a network. +--- + +# cloudstack\_network + +Creates a network. + +## Example Usage + +Basic usage: + +``` +resource "cloudstack_network" "default" { + name = "test-network" + cidr = "10.0.0.0/16" + network_offering = "Default Network" + zone = "zone-1" +} +``` + +## Argument Reference + +The following arguments are supported: + +* `name` - (Required) The name of the network. + +* `display_text` - (Optional) The display text of the network. + +* `cidr` - (Required) The CIDR block for the network. Changing this forces a new + resource to be created. + +* `network_offering` - (Required) The name or ID of the network offering to use + for this network. + +* `vpc` - (Optional) The name of the VPC to create this network for. Changing + this forces a new resource to be created. + +* `aclid` - (Optional) The ID of a network ACL that should be attached to the + network. Changing this forces a new resource to be created. + +* `zone` - (Required) The name or ID of the zone where this disk volume will be + available. Changing this forces a new resource to be created. + +## Attributes Reference + +The following attributes are exported: + +* `id` - The ID of the network. +* `display_text` - The display text of the network. diff --git a/website/source/layouts/azure.erb b/website/source/layouts/azure.erb new file mode 100644 index 000000000..9d60e3f90 --- /dev/null +++ b/website/source/layouts/azure.erb @@ -0,0 +1,38 @@ +<% wrap_layout :inner do %> + <% content_for :sidebar do %> + + <% end %> + + <%= yield %> +<% end %> diff --git a/website/source/layouts/docs.erb b/website/source/layouts/docs.erb index dd60cad04..dbbf87b8a 100644 --- a/website/source/layouts/docs.erb +++ b/website/source/layouts/docs.erb @@ -119,19 +119,23 @@