From 8dd4b155e08614e59f26e89ae41938c97d0e15c6 Mon Sep 17 00:00:00 2001 From: Eloy Coto Date: Tue, 27 Oct 2015 09:43:01 +0000 Subject: [PATCH 01/17] Vsphere_virtual_machine: Delete all network interfaces from template before added new one. Fixes #3559 and #3560 --- .../vsphere/resource_vsphere_virtual_machine.go | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/builtin/providers/vsphere/resource_vsphere_virtual_machine.go b/builtin/providers/vsphere/resource_vsphere_virtual_machine.go index c6b1292acf..33c4ff2276 100644 --- a/builtin/providers/vsphere/resource_vsphere_virtual_machine.go +++ b/builtin/providers/vsphere/resource_vsphere_virtual_machine.go @@ -890,6 +890,22 @@ func (vm *virtualMachine) deployVirtualMachine(c *govmomi.Client) error { } log.Printf("[DEBUG] template: %#v", template) + devices, err := template.Device(context.TODO()) + if err != nil { + log.Printf("[DEBUG] Template devices can't be found") + return err + } + + for _, dvc := range devices { + // Issue 3559/3560: Delete all ethernet devices to add the correct ones later + if devices.Type(dvc) == "ethernet" { + err := template.RemoveDevice(context.TODO(), dvc) + if err != nil { + return err + } + } + } + var resourcePool *object.ResourcePool if vm.resourcePool == "" { if vm.cluster == "" { From 8c494031327e21aa7f549e0155eda64cc5a1f86a Mon Sep 17 00:00:00 2001 From: Eloy Coto Date: Wed, 28 Oct 2015 07:43:43 +0000 Subject: [PATCH 02/17] Vsphere_virtual_machine: Keep template as it is and apply the customizations and add devices in the new server. When finished PowerOn --- .../resource_vsphere_virtual_machine.go | 63 ++++++++++++------- 1 file changed, 41 insertions(+), 22 deletions(-) diff --git a/builtin/providers/vsphere/resource_vsphere_virtual_machine.go b/builtin/providers/vsphere/resource_vsphere_virtual_machine.go index 33c4ff2276..c1e4225f35 100644 --- a/builtin/providers/vsphere/resource_vsphere_virtual_machine.go +++ b/builtin/providers/vsphere/resource_vsphere_virtual_machine.go @@ -890,22 +890,6 @@ func (vm *virtualMachine) deployVirtualMachine(c *govmomi.Client) error { } log.Printf("[DEBUG] template: %#v", template) - devices, err := template.Device(context.TODO()) - if err != nil { - log.Printf("[DEBUG] Template devices can't be found") - return err - } - - for _, dvc := range devices { - // Issue 3559/3560: Delete all ethernet devices to add the correct ones later - if devices.Type(dvc) == "ethernet" { - err := template.RemoveDevice(context.TODO(), dvc) - if err != nil { - return err - } - } - } - var resourcePool *object.ResourcePool if vm.resourcePool == "" { if vm.cluster == "" { @@ -1013,7 +997,6 @@ func (vm *virtualMachine) deployVirtualMachine(c *govmomi.Client) error { NumCPUs: vm.vcpu, NumCoresPerSocket: 1, MemoryMB: vm.memoryMb, - DeviceChange: networkDevices, } log.Printf("[DEBUG] virtual machine config spec: %v", configSpec) @@ -1037,11 +1020,10 @@ func (vm *virtualMachine) deployVirtualMachine(c *govmomi.Client) error { // make vm clone spec cloneSpec := types.VirtualMachineCloneSpec{ - Location: relocateSpec, - Template: false, - Config: &configSpec, - Customization: &customSpec, - PowerOn: true, + Location: relocateSpec, + Template: false, + Config: &configSpec, + PowerOn: false, } log.Printf("[DEBUG] clone spec: %v", cloneSpec) @@ -1061,6 +1043,43 @@ func (vm *virtualMachine) deployVirtualMachine(c *govmomi.Client) error { } log.Printf("[DEBUG] new vm: %v", newVM) + devices, err := newVM.Device(context.TODO()) + if err != nil { + log.Printf("[DEBUG] Template devices can't be found") + return err + } + + for _, dvc := range devices { + // Issue 3559/3560: Delete all ethernet devices to add the correct ones later + if devices.Type(dvc) == "ethernet" { + err := newVM.RemoveDevice(context.TODO(), dvc) + if err != nil { + return err + } + } + } + // Add Network devices + for _, dvc := range networkDevices { + err := newVM.AddDevice( + context.TODO(), dvc.GetVirtualDeviceConfigSpec().Device) + if err != nil { + return err + } + } + + taskb, err := newVM.Customize(context.TODO(), customSpec) + if err != nil { + return err + } + + _, err = taskb.WaitForResult(context.TODO(), nil) + if err != nil { + return err + } + log.Printf("[DEBUG]VM customization finished") + + newVM.PowerOn(context.TODO()) + ip, err := newVM.WaitForIP(context.TODO()) if err != nil { return err From 12fc9a1d7887c530cc982d8964d321159e66dd4c Mon Sep 17 00:00:00 2001 From: Takaaki Furukawa Date: Fri, 30 Oct 2015 18:18:25 +0900 Subject: [PATCH 03/17] Fix acceptance tests for using optional parameters at different environment --- .../resource_vsphere_virtual_machine_test.go | 86 ++++++++----------- .../providers/vsphere/index.html.markdown | 12 ++- 2 files changed, 46 insertions(+), 52 deletions(-) diff --git a/builtin/providers/vsphere/resource_vsphere_virtual_machine_test.go b/builtin/providers/vsphere/resource_vsphere_virtual_machine_test.go index 75bc339e85..66d6ea44f8 100644 --- a/builtin/providers/vsphere/resource_vsphere_virtual_machine_test.go +++ b/builtin/providers/vsphere/resource_vsphere_virtual_machine_test.go @@ -15,9 +15,21 @@ import ( func TestAccVSphereVirtualMachine_basic(t *testing.T) { var vm virtualMachine - datacenter := os.Getenv("VSPHERE_DATACENTER") - cluster := os.Getenv("VSPHERE_CLUSTER") - datastore := os.Getenv("VSPHERE_DATASTORE") + var locationOpt string + var datastoreOpt string + + if v := os.Getenv("VSPHERE_DATACENTER"); v != "" { + locationOpt += fmt.Sprintf(" datacenter = \"%s\"\n", v) + } + if v := os.Getenv("VSPHERE_CLUSTER"); v != "" { + locationOpt += fmt.Sprintf(" cluster = \"%s\"\n", v) + } + if v := os.Getenv("VSPHERE_RESOURCE_POOL"); v != "" { + locationOpt += fmt.Sprintf(" resource_pool = \"%s\"\n", v) + } + if v := os.Getenv("VSPHERE_DATASTORE"); v != "" { + datastoreOpt = fmt.Sprintf(" datastore = \"%s\"\n", v) + } template := os.Getenv("VSPHERE_TEMPLATE") gateway := os.Getenv("VSPHERE_NETWORK_GATEWAY") label := os.Getenv("VSPHERE_NETWORK_LABEL") @@ -31,28 +43,23 @@ func TestAccVSphereVirtualMachine_basic(t *testing.T) { resource.TestStep{ Config: fmt.Sprintf( testAccCheckVSphereVirtualMachineConfig_basic, - datacenter, - cluster, + locationOpt, gateway, label, ip_address, - datastore, + datastoreOpt, template, ), Check: resource.ComposeTestCheckFunc( testAccCheckVSphereVirtualMachineExists("vsphere_virtual_machine.foo", &vm), resource.TestCheckResourceAttr( "vsphere_virtual_machine.foo", "name", "terraform-test"), - resource.TestCheckResourceAttr( - "vsphere_virtual_machine.foo", "datacenter", datacenter), resource.TestCheckResourceAttr( "vsphere_virtual_machine.foo", "vcpu", "2"), resource.TestCheckResourceAttr( "vsphere_virtual_machine.foo", "memory", "4096"), resource.TestCheckResourceAttr( "vsphere_virtual_machine.foo", "disk.#", "2"), - resource.TestCheckResourceAttr( - "vsphere_virtual_machine.foo", "disk.0.datastore", datastore), resource.TestCheckResourceAttr( "vsphere_virtual_machine.foo", "disk.0.template", template), resource.TestCheckResourceAttr( @@ -67,12 +74,23 @@ func TestAccVSphereVirtualMachine_basic(t *testing.T) { func TestAccVSphereVirtualMachine_dhcp(t *testing.T) { var vm virtualMachine - datacenter := os.Getenv("VSPHERE_DATACENTER") - cluster := os.Getenv("VSPHERE_CLUSTER") - datastore := os.Getenv("VSPHERE_DATASTORE") + var locationOpt string + var datastoreOpt string + + if v := os.Getenv("VSPHERE_DATACENTER"); v != "" { + locationOpt += fmt.Sprintf(" datacenter = \"%s\"\n", v) + } + if v := os.Getenv("VSPHERE_CLUSTER"); v != "" { + locationOpt += fmt.Sprintf(" cluster = \"%s\"\n", v) + } + if v := os.Getenv("VSPHERE_RESOURCE_POOL"); v != "" { + locationOpt += fmt.Sprintf(" resource_pool = \"%s\"\n", v) + } + if v := os.Getenv("VSPHERE_DATASTORE"); v != "" { + datastoreOpt = fmt.Sprintf(" datastore = \"%s\"\n", v) + } template := os.Getenv("VSPHERE_TEMPLATE") label := os.Getenv("VSPHERE_NETWORK_LABEL_DHCP") - password := os.Getenv("VSPHERE_VM_PASSWORD") resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, @@ -82,27 +100,21 @@ func TestAccVSphereVirtualMachine_dhcp(t *testing.T) { resource.TestStep{ Config: fmt.Sprintf( testAccCheckVSphereVirtualMachineConfig_dhcp, - datacenter, - cluster, + locationOpt, label, - datastore, + datastoreOpt, template, - password, ), Check: resource.ComposeTestCheckFunc( testAccCheckVSphereVirtualMachineExists("vsphere_virtual_machine.bar", &vm), resource.TestCheckResourceAttr( "vsphere_virtual_machine.bar", "name", "terraform-test"), - resource.TestCheckResourceAttr( - "vsphere_virtual_machine.bar", "datacenter", datacenter), resource.TestCheckResourceAttr( "vsphere_virtual_machine.bar", "vcpu", "2"), resource.TestCheckResourceAttr( "vsphere_virtual_machine.bar", "memory", "4096"), resource.TestCheckResourceAttr( "vsphere_virtual_machine.bar", "disk.#", "1"), - resource.TestCheckResourceAttr( - "vsphere_virtual_machine.bar", "disk.0.datastore", datastore), resource.TestCheckResourceAttr( "vsphere_virtual_machine.bar", "disk.0.template", template), resource.TestCheckResourceAttr( @@ -168,20 +180,6 @@ func testAccCheckVSphereVirtualMachineExists(n string, vm *virtualMachine) resou } _, err = object.NewSearchIndex(client.Client).FindChild(context.TODO(), dcFolders.VmFolder, rs.Primary.Attributes["name"]) - /* - vmRef, err := client.SearchIndex().FindChild(dcFolders.VmFolder, rs.Primary.Attributes["name"]) - if err != nil { - return fmt.Errorf("error %s", err) - } - - found := govmomi.NewVirtualMachine(client, vmRef.Reference()) - fmt.Printf("%v", found) - - if found.Name != rs.Primary.ID { - return fmt.Errorf("Instance not found") - } - *instance = *found - */ *vm = virtualMachine{ name: rs.Primary.ID, @@ -194,8 +192,7 @@ func testAccCheckVSphereVirtualMachineExists(n string, vm *virtualMachine) resou const testAccCheckVSphereVirtualMachineConfig_basic = ` resource "vsphere_virtual_machine" "foo" { name = "terraform-test" - datacenter = "%s" - cluster = "%s" +%s vcpu = 2 memory = 4096 gateway = "%s" @@ -205,7 +202,7 @@ resource "vsphere_virtual_machine" "foo" { subnet_mask = "255.255.255.0" } disk { - datastore = "%s" +%s template = "%s" iops = 500 } @@ -219,22 +216,15 @@ resource "vsphere_virtual_machine" "foo" { const testAccCheckVSphereVirtualMachineConfig_dhcp = ` resource "vsphere_virtual_machine" "bar" { name = "terraform-test" - datacenter = "%s" - cluster = "%s" +%s vcpu = 2 memory = 4096 network_interface { label = "%s" } disk { - datastore = "%s" +%s template = "%s" } - - connection { - host = "${self.network_interface.0.ip_address}" - user = "root" - password = "%s" - } } ` diff --git a/website/source/docs/providers/vsphere/index.html.markdown b/website/source/docs/providers/vsphere/index.html.markdown index d8168e072f..3030337870 100644 --- a/website/source/docs/providers/vsphere/index.html.markdown +++ b/website/source/docs/providers/vsphere/index.html.markdown @@ -64,15 +64,19 @@ configuration fields to be set using the documented environment variables. In addition, the following environment variables are used in tests, and must be set to valid values for your vSphere environment: - * VSPHERE\_CLUSTER - * VSPHERE\_DATACENTER - * VSPHERE\_DATASTORE * VSPHERE\_NETWORK\_GATEWAY * VSPHERE\_NETWORK\_IP\_ADDRESS * VSPHERE\_NETWORK\_LABEL * VSPHERE\_NETWORK\_LABEL\_DHCP * VSPHERE\_TEMPLATE - * VSPHERE\_VM\_PASSWORD + +The following environment variables depend on your vSphere environment: + + * VSPHERE\_DATACENTER + * VSPHERE\_CLUSTER + * VSPHERE\_RESOURCE\_POOL + * VSPHERE\_DATASTORE + These are used to set and verify attributes on the `vsphere_virtual_machine` resource in tests. From 964c9cd7e8c6a3b7e0cebb56195079366cf8b62c Mon Sep 17 00:00:00 2001 From: Cameron Stokes Date: Mon, 2 Nov 2015 18:21:08 -0800 Subject: [PATCH 04/17] Change all occurrences of 'vSphere' to 'VMware vSphere' --- .../providers/vsphere/index.html.markdown | 25 ++++++++++--------- website/source/layouts/docs.erb | 2 +- 2 files changed, 14 insertions(+), 13 deletions(-) diff --git a/website/source/docs/providers/vsphere/index.html.markdown b/website/source/docs/providers/vsphere/index.html.markdown index d8168e072f..f4999be73e 100644 --- a/website/source/docs/providers/vsphere/index.html.markdown +++ b/website/source/docs/providers/vsphere/index.html.markdown @@ -1,27 +1,28 @@ --- layout: "vsphere" -page_title: "Provider: vSphere" +page_title: "Provider: VMware vSphere" sidebar_current: "docs-vsphere-index" description: |- - The vSphere provider is used to interact with the resources supported by - vSphere. The provider needs to be configured with the proper credentials before - it can be used. + The VMware vSphere provider is used to interact with the resources supported by + VMware vSphere. The provider needs to be configured with the proper credentials + before it can be used. --- -# vSphere Provider +# VMware vSphere Provider -The vSphere provider is used to interact with the resources supported by vSphere. +The VMware vSphere provider is used to interact with the resources supported by +VMware vSphere. The provider needs to be configured with the proper credentials before it can be used. Use the navigation to the left to read about the available resources. -~> **NOTE:** The vSphere Provider currently represents _initial support_ and -therefore may undergo significant changes as the community improves it. +~> **NOTE:** The VMware vSphere Provider currently represents _initial support_ +and therefore may undergo significant changes as the community improves it. ## Example Usage ``` -# Configure the vSphere Provider +# Configure the VMware vSphere Provider provider "vsphere" { user = "${var.vsphere_user}" password = "${var.vsphere_password}" @@ -47,7 +48,7 @@ resource "vsphere_virtual_machine" "web" { ## Argument Reference -The following arguments are used to configure the vSphere Provider: +The following arguments are used to configure the VMware vSphere Provider: * `user` - (Required) This is the username for vSphere API operations. Can also be specified with the `VSPHERE_USER` environment variable. @@ -59,10 +60,10 @@ The following arguments are used to configure the vSphere Provider: ## Acceptance Tests -The vSphere provider's acceptance tests require the above provider +The VMware vSphere provider's acceptance tests require the above provider configuration fields to be set using the documented environment variables. -In addition, the following environment variables are used in tests, and must be set to valid values for your vSphere environment: +In addition, the following environment variables are used in tests, and must be set to valid values for your VMware vSphere environment: * VSPHERE\_CLUSTER * VSPHERE\_DATACENTER diff --git a/website/source/layouts/docs.erb b/website/source/layouts/docs.erb index a5e8eb1c9b..59eb10df9e 100644 --- a/website/source/layouts/docs.erb +++ b/website/source/layouts/docs.erb @@ -194,7 +194,7 @@ > - vSphere + VMware vSphere From d99b9aaa6f0e977aa2c19818a9788ddbf3aaf95b Mon Sep 17 00:00:00 2001 From: Cameron Stokes Date: Mon, 2 Nov 2015 18:25:05 -0800 Subject: [PATCH 05/17] vSphere occurrence in layout --- website/source/layouts/vsphere.erb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/source/layouts/vsphere.erb b/website/source/layouts/vsphere.erb index 49e58c0578..511511511a 100644 --- a/website/source/layouts/vsphere.erb +++ b/website/source/layouts/vsphere.erb @@ -7,7 +7,7 @@ > - vSphere Provider + VMware vSphere Provider > From 05a0a41236d04d0a29a45ff97207abd0ae3aa58c Mon Sep 17 00:00:00 2001 From: Cameron Stokes Date: Mon, 2 Nov 2015 18:27:22 -0800 Subject: [PATCH 06/17] vSphere occurrences in vsphere_virtual_machine resource page --- .../docs/providers/vsphere/r/virtual_machine.html.markdown | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/website/source/docs/providers/vsphere/r/virtual_machine.html.markdown b/website/source/docs/providers/vsphere/r/virtual_machine.html.markdown index 6ce012d65c..d008357ecc 100644 --- a/website/source/docs/providers/vsphere/r/virtual_machine.html.markdown +++ b/website/source/docs/providers/vsphere/r/virtual_machine.html.markdown @@ -1,14 +1,14 @@ --- layout: "vsphere" -page_title: "vSphere: vsphere_virtual_machine" +page_title: "VMware vSphere: vsphere_virtual_machine" sidebar_current: "docs-vsphere-resource-virtual-machine" description: |- - Provides a vSphere virtual machine resource. This can be used to create, modify, and delete virtual machines. + Provides a VMware vSphere virtual machine resource. This can be used to create, modify, and delete virtual machines. --- # vsphere\_virtual\_machine -Provides a vSphere virtual machine resource. This can be used to create, +Provides a VMware vSphere virtual machine resource. This can be used to create, modify, and delete virtual machines. ## Example Usage From 1d0dbc5d19bfb3451d07070a25d4a9095dd0b124 Mon Sep 17 00:00:00 2001 From: stack72 Date: Wed, 4 Nov 2015 19:40:22 +0000 Subject: [PATCH 07/17] Adding backup_retention_period, preferred_backup_window and preferred_maintenance_window to RDS Cluster --- .../providers/aws/resource_aws_rds_cluster.go | 55 +++++++++++++++++++ .../aws/resource_aws_rds_cluster_test.go | 9 +++ .../providers/aws/r/rds_cluster.html.markdown | 9 ++- 3 files changed, 72 insertions(+), 1 deletion(-) diff --git a/builtin/providers/aws/resource_aws_rds_cluster.go b/builtin/providers/aws/resource_aws_rds_cluster.go index 57f3a27b33..f4c54c3d67 100644 --- a/builtin/providers/aws/resource_aws_rds_cluster.go +++ b/builtin/providers/aws/resource_aws_rds_cluster.go @@ -4,6 +4,7 @@ import ( "fmt" "log" "regexp" + "strings" "time" "github.com/aws/aws-sdk-go/aws" @@ -122,6 +123,33 @@ func resourceAwsRDSCluster() *schema.Resource { Elem: &schema.Schema{Type: schema.TypeString}, Set: schema.HashString, }, + + "preferred_backup_window": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + }, + + "preferred_maintenance_window": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + StateFunc: func(val interface{}) string { + return strings.ToLower(val.(string)) + }, + }, + + "backup_retention_period": &schema.Schema{ + Type: schema.TypeInt, + Optional: true, + Default: 1, + ValidateFunc: func(v interface{}, k string) (ws []string, es []error) { + value := v.(int) + if value > 35 { + es = append(es, fmt.Errorf( + "backup retention period cannot be more than 35 days")) + } + return + }, + }, }, } } @@ -156,6 +184,18 @@ func resourceAwsRDSClusterCreate(d *schema.ResourceData, meta interface{}) error createOpts.AvailabilityZones = expandStringList(attr.List()) } + if v, ok := d.GetOk("backup_retention_period"); ok { + createOpts.BackupRetentionPeriod = aws.Int64(int64(v.(int))) + } + + if v, ok := d.GetOk("preferred_backup_window"); ok { + createOpts.PreferredBackupWindow = aws.String(v.(string)) + } + + if v, ok := d.GetOk("preferred_maintenance_window"); ok { + createOpts.PreferredMaintenanceWindow = aws.String(v.(string)) + } + log.Printf("[DEBUG] RDS Cluster create options: %s", createOpts) resp, err := conn.CreateDBCluster(createOpts) if err != nil { @@ -223,6 +263,9 @@ func resourceAwsRDSClusterRead(d *schema.ResourceData, meta interface{}) error { d.Set("engine", dbc.Engine) d.Set("master_username", dbc.MasterUsername) d.Set("port", dbc.Port) + d.Set("backup_retention_period", dbc.BackupRetentionPeriod) + d.Set("preferred_backup_window", dbc.PreferredBackupWindow) + d.Set("preferred_maintenance_window", dbc.PreferredMaintenanceWindow) var vpcg []string for _, g := range dbc.VpcSecurityGroups { @@ -263,6 +306,18 @@ func resourceAwsRDSClusterUpdate(d *schema.ResourceData, meta interface{}) error } } + if d.HasChange("preferred_backup_window") { + req.PreferredBackupWindow = aws.String(d.Get("preferred_backup_window").(string)) + } + + if d.HasChange("preferred_maintaince_window") { + req.PreferredMaintenanceWindow = aws.String(d.Get("preferred_maintaince_window").(string)) + } + + if d.HasChange("backup_retention_limit") { + req.BackupRetentionPeriod = aws.Int64(int64(d.Get("backup_retention_limit").(int))) + } + _, err := conn.ModifyDBCluster(req) if err != nil { return fmt.Errorf("[WARN] Error modifying RDS Cluster (%s): %s", d.Id(), err) diff --git a/builtin/providers/aws/resource_aws_rds_cluster_test.go b/builtin/providers/aws/resource_aws_rds_cluster_test.go index ffa2fa8e90..79da2acf4b 100644 --- a/builtin/providers/aws/resource_aws_rds_cluster_test.go +++ b/builtin/providers/aws/resource_aws_rds_cluster_test.go @@ -26,6 +26,12 @@ func TestAccAWSRDSCluster_basic(t *testing.T) { Config: testAccAWSClusterConfig, Check: resource.ComposeTestCheckFunc( testAccCheckAWSClusterExists("aws_rds_cluster.default", &v), + resource.TestCheckResourceAttr( + "aws_rds_cluster.default", "preferred_backup_window", "07:00-09:00"), + resource.TestCheckResourceAttr( + "aws_rds_cluster.default", "backup_retention_period", "5"), + resource.TestCheckResourceAttr( + "aws_rds_cluster.default", "preferred_maintenance_window", "tue:04:00-tue:04:30"), ), }, }, @@ -105,4 +111,7 @@ resource "aws_rds_cluster" "default" { database_name = "mydb" master_username = "foo" master_password = "mustbeeightcharaters" + backup_retention_period = 5 + preferred_backup_window = "07:00-09:00" + preferred_maintenance_window = "tue:04:00-tue:04:30" }`, rand.New(rand.NewSource(time.Now().UnixNano())).Int()) diff --git a/website/source/docs/providers/aws/r/rds_cluster.html.markdown b/website/source/docs/providers/aws/r/rds_cluster.html.markdown index c45814f466..fb1f0dac85 100644 --- a/website/source/docs/providers/aws/r/rds_cluster.html.markdown +++ b/website/source/docs/providers/aws/r/rds_cluster.html.markdown @@ -24,6 +24,8 @@ resource "aws_rds_cluster" "default" { database_name = "mydb" master_username = "foo" master_password = "bar" + backup_retention_period = 5 + preferred_backup_window = "07:00-09:00" } ``` @@ -52,6 +54,9 @@ string. instances in the DB cluster can be created in * `backup_retention_period` - (Optional) The days to retain backups for. Default 1 +* `preferred_backup_window` - (Optional) The daily time range during which automated backups are created if automated backups are enabled using the BackupRetentionPeriod parameter. +Default: A 30-minute window selected at random from an 8-hour block of time per region. e.g. 04:00-09:00 +* `preferred_maintenance_window` - (Optional) The weekly time range during which system maintenance can occur, in (UTC) e.g. wed:04:00-wed:04:30 * `port` - (Optional) The port on which the DB accepts connections * `vpc_security_group_ids` - (Optional) List of VPC security groups to associate with the Cluster @@ -70,7 +75,8 @@ The following attributes are exported: * `allocated_storage` - The amount of allocated storage * `availability_zones` - The availability zone of the instance * `backup_retention_period` - The backup retention period -* `backup_window` - The backup window +* `preferred_backup_window` - The backup window +* `preferred_maintenance_window` - The maintenance window * `endpoint` - The primary, writeable connection endpoint * `engine` - The database engine * `engine_version` - The database engine version @@ -80,6 +86,7 @@ The following attributes are exported: * `status` - The RDS instance status * `username` - The master username for the database * `storage_encrypted` - Specifies whether the DB instance is encrypted +* `preferred_backup_window` - The daily time range during which the backups happen [1]: http://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/Overview.Replication.html From 4e485d4254ddb8ed807a701a6bed443bd4108dc3 Mon Sep 17 00:00:00 2001 From: stack72 Date: Wed, 4 Nov 2015 21:06:41 +0000 Subject: [PATCH 08/17] Fixing the spelling mistakes and adding a test to prove that the Updates to the new properties of RDS Cluster work as expected --- .../providers/aws/resource_aws_rds_cluster.go | 8 +-- .../aws/resource_aws_rds_cluster_test.go | 64 ++++++++++++++++++- 2 files changed, 65 insertions(+), 7 deletions(-) diff --git a/builtin/providers/aws/resource_aws_rds_cluster.go b/builtin/providers/aws/resource_aws_rds_cluster.go index f4c54c3d67..ece9b686f1 100644 --- a/builtin/providers/aws/resource_aws_rds_cluster.go +++ b/builtin/providers/aws/resource_aws_rds_cluster.go @@ -310,12 +310,12 @@ func resourceAwsRDSClusterUpdate(d *schema.ResourceData, meta interface{}) error req.PreferredBackupWindow = aws.String(d.Get("preferred_backup_window").(string)) } - if d.HasChange("preferred_maintaince_window") { - req.PreferredMaintenanceWindow = aws.String(d.Get("preferred_maintaince_window").(string)) + if d.HasChange("preferred_maintenance_window") { + req.PreferredMaintenanceWindow = aws.String(d.Get("preferred_maintenance_window").(string)) } - if d.HasChange("backup_retention_limit") { - req.BackupRetentionPeriod = aws.Int64(int64(d.Get("backup_retention_limit").(int))) + if d.HasChange("backup_retention_period") { + req.BackupRetentionPeriod = aws.Int64(int64(d.Get("backup_retention_period").(int))) } _, err := conn.ModifyDBCluster(req) diff --git a/builtin/providers/aws/resource_aws_rds_cluster_test.go b/builtin/providers/aws/resource_aws_rds_cluster_test.go index 79da2acf4b..a9eed687e3 100644 --- a/builtin/providers/aws/resource_aws_rds_cluster_test.go +++ b/builtin/providers/aws/resource_aws_rds_cluster_test.go @@ -17,13 +17,16 @@ import ( func TestAccAWSRDSCluster_basic(t *testing.T) { var v rds.DBCluster + ri := rand.New(rand.NewSource(time.Now().UnixNano())).Int() + config := fmt.Sprintf(testAccAWSClusterConfig, ri) + resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, Providers: testAccProviders, CheckDestroy: testAccCheckAWSClusterDestroy, Steps: []resource.TestStep{ resource.TestStep{ - Config: testAccAWSClusterConfig, + Config: config, Check: resource.ComposeTestCheckFunc( testAccCheckAWSClusterExists("aws_rds_cluster.default", &v), resource.TestCheckResourceAttr( @@ -38,6 +41,47 @@ func TestAccAWSRDSCluster_basic(t *testing.T) { }) } +func TestAccAWSRDSCluster_update(t *testing.T) { + var v rds.DBCluster + + ri := rand.New(rand.NewSource(time.Now().UnixNano())).Int() + preConfig := fmt.Sprintf(testAccAWSClusterConfig, ri) + postConfig := fmt.Sprintf(testAccAWSClusterConfig_update, ri) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSClusterDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: preConfig, + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSClusterExists("aws_rds_cluster.default", &v), + resource.TestCheckResourceAttr( + "aws_rds_cluster.default", "preferred_backup_window", "07:00-09:00"), + resource.TestCheckResourceAttr( + "aws_rds_cluster.default", "backup_retention_period", "5"), + resource.TestCheckResourceAttr( + "aws_rds_cluster.default", "preferred_maintenance_window", "tue:04:00-tue:04:30"), + ), + }, + + resource.TestStep{ + Config: postConfig, + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSClusterExists("aws_rds_cluster.default", &v), + resource.TestCheckResourceAttr( + "aws_rds_cluster.default", "preferred_backup_window", "03:00-09:00"), + resource.TestCheckResourceAttr( + "aws_rds_cluster.default", "backup_retention_period", "10"), + resource.TestCheckResourceAttr( + "aws_rds_cluster.default", "preferred_maintenance_window", "wed:01:00-wed:01:30"), + ), + }, + }, + }) +} + func testAccCheckAWSClusterDestroy(s *terraform.State) error { for _, rs := range s.RootModule().Resources { if rs.Type != "aws_rds_cluster" { @@ -104,7 +148,7 @@ func testAccCheckAWSClusterExists(n string, v *rds.DBCluster) resource.TestCheck } // Add some random to the name, to avoid collision -var testAccAWSClusterConfig = fmt.Sprintf(` +var testAccAWSClusterConfig = ` resource "aws_rds_cluster" "default" { cluster_identifier = "tf-aurora-cluster-%d" availability_zones = ["us-west-2a","us-west-2b","us-west-2c"] @@ -114,4 +158,18 @@ resource "aws_rds_cluster" "default" { backup_retention_period = 5 preferred_backup_window = "07:00-09:00" preferred_maintenance_window = "tue:04:00-tue:04:30" -}`, rand.New(rand.NewSource(time.Now().UnixNano())).Int()) +}` + +// Add some random to the name, to avoid collision +var testAccAWSClusterConfig_update = ` +resource "aws_rds_cluster" "default" { + cluster_identifier = "tf-aurora-cluster-%d" + availability_zones = ["us-west-2a","us-west-2b","us-west-2c"] + database_name = "mydb" + master_username = "foo" + master_password = "mustbeeightcharaters" + backup_retention_period = 10 + preferred_backup_window = "03:00-09:00" + preferred_maintenance_window = "wed:01:00-wed:01:30" + apply_immediately = true +}` From 409df4866d8a05293ba3d1c87aee18684a8d12a8 Mon Sep 17 00:00:00 2001 From: stack72 Date: Thu, 5 Nov 2015 10:25:01 +0000 Subject: [PATCH 09/17] Changes after the feedback from @catsby - these all made perfect sense --- .../providers/aws/resource_aws_rds_cluster.go | 5 +++ .../aws/resource_aws_rds_cluster_test.go | 40 ++++++++++++++++--- 2 files changed, 39 insertions(+), 6 deletions(-) diff --git a/builtin/providers/aws/resource_aws_rds_cluster.go b/builtin/providers/aws/resource_aws_rds_cluster.go index ece9b686f1..6dac39f0ea 100644 --- a/builtin/providers/aws/resource_aws_rds_cluster.go +++ b/builtin/providers/aws/resource_aws_rds_cluster.go @@ -127,12 +127,17 @@ func resourceAwsRDSCluster() *schema.Resource { "preferred_backup_window": &schema.Schema{ Type: schema.TypeString, Optional: true, + Computed: true, }, "preferred_maintenance_window": &schema.Schema{ Type: schema.TypeString, Optional: true, + Computed: true, StateFunc: func(val interface{}) string { + if val == nil { + return "" + } return strings.ToLower(val.(string)) }, }, diff --git a/builtin/providers/aws/resource_aws_rds_cluster_test.go b/builtin/providers/aws/resource_aws_rds_cluster_test.go index a9eed687e3..d0d2853627 100644 --- a/builtin/providers/aws/resource_aws_rds_cluster_test.go +++ b/builtin/providers/aws/resource_aws_rds_cluster_test.go @@ -20,6 +20,27 @@ func TestAccAWSRDSCluster_basic(t *testing.T) { ri := rand.New(rand.NewSource(time.Now().UnixNano())).Int() config := fmt.Sprintf(testAccAWSClusterConfig, ri) + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSClusterDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: config, + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSClusterExists("aws_rds_cluster.default", &v), + ), + }, + }, + }) +} + +func TestAccAWSRDSCluster_backups(t *testing.T) { + var v rds.DBCluster + + ri := rand.New(rand.NewSource(time.Now().UnixNano())).Int() + config := fmt.Sprintf(testAccAWSClusterConfig_backups, ri) + resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, Providers: testAccProviders, @@ -41,12 +62,12 @@ func TestAccAWSRDSCluster_basic(t *testing.T) { }) } -func TestAccAWSRDSCluster_update(t *testing.T) { +func TestAccAWSRDSCluster_backupsUpdate(t *testing.T) { var v rds.DBCluster ri := rand.New(rand.NewSource(time.Now().UnixNano())).Int() - preConfig := fmt.Sprintf(testAccAWSClusterConfig, ri) - postConfig := fmt.Sprintf(testAccAWSClusterConfig_update, ri) + preConfig := fmt.Sprintf(testAccAWSClusterConfig_backups, ri) + postConfig := fmt.Sprintf(testAccAWSClusterConfig_backupsUpdate, ri) resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, @@ -147,8 +168,16 @@ func testAccCheckAWSClusterExists(n string, v *rds.DBCluster) resource.TestCheck } } -// Add some random to the name, to avoid collision var testAccAWSClusterConfig = ` +resource "aws_rds_cluster" "default" { + cluster_identifier = "tf-aurora-cluster-%d" + availability_zones = ["us-west-2a","us-west-2b","us-west-2c"] + database_name = "mydb" + master_username = "foo" + master_password = "mustbeeightcharaters" +}` + +var testAccAWSClusterConfig_backups = ` resource "aws_rds_cluster" "default" { cluster_identifier = "tf-aurora-cluster-%d" availability_zones = ["us-west-2a","us-west-2b","us-west-2c"] @@ -160,8 +189,7 @@ resource "aws_rds_cluster" "default" { preferred_maintenance_window = "tue:04:00-tue:04:30" }` -// Add some random to the name, to avoid collision -var testAccAWSClusterConfig_update = ` +var testAccAWSClusterConfig_backupsUpdate = ` resource "aws_rds_cluster" "default" { cluster_identifier = "tf-aurora-cluster-%d" availability_zones = ["us-west-2a","us-west-2b","us-west-2c"] From 032a42797ea8b41bc29cade1223716284259f30b Mon Sep 17 00:00:00 2001 From: stack72 Date: Thu, 5 Nov 2015 15:01:07 +0000 Subject: [PATCH 10/17] Fixing the DigitalOcean Droplet 404 potential on refresh of state --- builtin/providers/digitalocean/resource_digitalocean_droplet.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/builtin/providers/digitalocean/resource_digitalocean_droplet.go b/builtin/providers/digitalocean/resource_digitalocean_droplet.go index ef25b00741..decf944767 100644 --- a/builtin/providers/digitalocean/resource_digitalocean_droplet.go +++ b/builtin/providers/digitalocean/resource_digitalocean_droplet.go @@ -189,7 +189,7 @@ func resourceDigitalOceanDropletRead(d *schema.ResourceData, meta interface{}) e droplet, _, err := client.Droplets.Get(id) if err != nil { // check if the droplet no longer exists. - if err.Error() == "Error retrieving droplet: API Error: 404 Not Found" { + if strings.Contains(err.Error(), "404 The resource you were accessing could not be found") { d.SetId("") return nil } From 2504cb46246d7cac8ce9d1f08fb002337b1fedbc Mon Sep 17 00:00:00 2001 From: stack72 Date: Thu, 5 Nov 2015 16:33:29 +0000 Subject: [PATCH 11/17] Changing the DigitalOcean Droplet 404 from a message string to an actual 404 status code --- .../providers/digitalocean/resource_digitalocean_droplet.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/builtin/providers/digitalocean/resource_digitalocean_droplet.go b/builtin/providers/digitalocean/resource_digitalocean_droplet.go index decf944767..a4f80015b9 100644 --- a/builtin/providers/digitalocean/resource_digitalocean_droplet.go +++ b/builtin/providers/digitalocean/resource_digitalocean_droplet.go @@ -186,10 +186,10 @@ func resourceDigitalOceanDropletRead(d *schema.ResourceData, meta interface{}) e } // Retrieve the droplet properties for updating the state - droplet, _, err := client.Droplets.Get(id) + droplet, resp, err := client.Droplets.Get(id) if err != nil { // check if the droplet no longer exists. - if strings.Contains(err.Error(), "404 The resource you were accessing could not be found") { + if resp.StatusCode == 404 { d.SetId("") return nil } From 9cee18b3de8ea63c5403cc6176f501ea4aaf33bb Mon Sep 17 00:00:00 2001 From: stack72 Date: Thu, 5 Nov 2015 14:31:55 +0000 Subject: [PATCH 12/17] ElastiCache cluster read tolerates removed cluster. Previously it would fail if a Terraform-managed ElastiCache cluster were deleted outside of Terraform. Now it marks it as deleted in the state so that Terraform can know it doesn't need to be destroyed, and can potentially recreate it if asked. --- builtin/providers/aws/resource_aws_elasticache_cluster.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/builtin/providers/aws/resource_aws_elasticache_cluster.go b/builtin/providers/aws/resource_aws_elasticache_cluster.go index 3460fb292f..f2410dcd50 100644 --- a/builtin/providers/aws/resource_aws_elasticache_cluster.go +++ b/builtin/providers/aws/resource_aws_elasticache_cluster.go @@ -241,6 +241,12 @@ func resourceAwsElasticacheClusterRead(d *schema.ResourceData, meta interface{}) res, err := conn.DescribeCacheClusters(req) if err != nil { + if eccErr, ok := err.(awserr.Error); ok && eccErr.Code() == "CacheClusterNotFound" { + log.Printf("[WARN] ElastiCache Cluster (%s) not found", d.Id()) + d.SetId("") + return nil + } + return err } From 6a811e2e4f075b645249ecfb2512e0266b602e8e Mon Sep 17 00:00:00 2001 From: stack72 Date: Thu, 5 Nov 2015 17:32:57 +0000 Subject: [PATCH 13/17] Logging that the DO droplet wasn't found before removing it --- builtin/providers/digitalocean/resource_digitalocean_droplet.go | 1 + 1 file changed, 1 insertion(+) diff --git a/builtin/providers/digitalocean/resource_digitalocean_droplet.go b/builtin/providers/digitalocean/resource_digitalocean_droplet.go index a4f80015b9..050577854e 100644 --- a/builtin/providers/digitalocean/resource_digitalocean_droplet.go +++ b/builtin/providers/digitalocean/resource_digitalocean_droplet.go @@ -190,6 +190,7 @@ func resourceDigitalOceanDropletRead(d *schema.ResourceData, meta interface{}) e if err != nil { // check if the droplet no longer exists. if resp.StatusCode == 404 { + log.Printf("[WARN] DigitalOcean Droplet (%s) not found", d.Id()) d.SetId("") return nil } From 99f3c0580b8aa0005d90f8ff5923e70a4b999c3a Mon Sep 17 00:00:00 2001 From: Clint Date: Thu, 5 Nov 2015 11:38:18 -0600 Subject: [PATCH 14/17] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 51103570d4..6fc1082b0f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -47,6 +47,7 @@ BUG FIXES: * provider/aws: Tolerate ElastiCache clusters being deleted outside Terraform [GH-3767] * provider/azure: various bugfixes [GH-3695] * provider/digitalocean: fix issue preventing SSH fingerprints from working [GH-3633] + * provider/digitalocean: Fixing the DigitalOcean Droplet 404 potential on refresh of state [GH-3768] * provider/openstack: Fix several issues causing unresolvable diffs [GH-3440] * provider/openstack: Safely delete security groups [GH-3696] * provider/openstack: Ignore order of security_groups in instance [GH-3651] From 6a5e591143a3789e050780ffd96ba64ff8891230 Mon Sep 17 00:00:00 2001 From: stack72 Date: Thu, 5 Nov 2015 19:01:41 +0000 Subject: [PATCH 15/17] Removing an unnecessary duplicate test for the RDS Cluster Backups --- .../aws/resource_aws_rds_cluster_test.go | 27 ------------------- 1 file changed, 27 deletions(-) diff --git a/builtin/providers/aws/resource_aws_rds_cluster_test.go b/builtin/providers/aws/resource_aws_rds_cluster_test.go index d0d2853627..dfb4fcc51a 100644 --- a/builtin/providers/aws/resource_aws_rds_cluster_test.go +++ b/builtin/providers/aws/resource_aws_rds_cluster_test.go @@ -35,33 +35,6 @@ func TestAccAWSRDSCluster_basic(t *testing.T) { }) } -func TestAccAWSRDSCluster_backups(t *testing.T) { - var v rds.DBCluster - - ri := rand.New(rand.NewSource(time.Now().UnixNano())).Int() - config := fmt.Sprintf(testAccAWSClusterConfig_backups, ri) - - resource.Test(t, resource.TestCase{ - PreCheck: func() { testAccPreCheck(t) }, - Providers: testAccProviders, - CheckDestroy: testAccCheckAWSClusterDestroy, - Steps: []resource.TestStep{ - resource.TestStep{ - Config: config, - Check: resource.ComposeTestCheckFunc( - testAccCheckAWSClusterExists("aws_rds_cluster.default", &v), - resource.TestCheckResourceAttr( - "aws_rds_cluster.default", "preferred_backup_window", "07:00-09:00"), - resource.TestCheckResourceAttr( - "aws_rds_cluster.default", "backup_retention_period", "5"), - resource.TestCheckResourceAttr( - "aws_rds_cluster.default", "preferred_maintenance_window", "tue:04:00-tue:04:30"), - ), - }, - }, - }) -} - func TestAccAWSRDSCluster_backupsUpdate(t *testing.T) { var v rds.DBCluster From e6c4a4fd1155a29bbfbe0398deb14858af0941cd Mon Sep 17 00:00:00 2001 From: Paul Hinze Date: Thu, 5 Nov 2015 13:07:08 -0600 Subject: [PATCH 16/17] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6fc1082b0f..4e92937eed 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -33,6 +33,7 @@ IMPROVEMENTS: * provider/aws: Making the AutoScalingGroup name optional [GH-3710] * provider/openstack: Add "delete on termination" boot-from-volume option [GH-3232] * provider/digitalocean: Make user_data force a new droplet [GH-3740] + * provider/vsphere: Do not add network interfaces by default [GH-3652] BUG FIXES: From 364b297700a4837802023361c3861e7ddc5d8b23 Mon Sep 17 00:00:00 2001 From: Clint Date: Thu, 5 Nov 2015 14:29:23 -0600 Subject: [PATCH 17/17] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4e92937eed..8c8fb36f77 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -28,6 +28,7 @@ IMPROVEMENTS: * provider/aws: Add `kinesis_endpoint` for configuring Kinesis [GH-3255] * provider/aws: Add a computed ARN for S3 Buckets [GH-3685] * provider/aws: Add configuration to enable copying RDS tags to final snapshot [GH-3529] + * provider/aws: RDS Cluster additions (`backup_retention_period`, `preferred_backup_window`, `preferred_maintenance_window`) [GH-3757] * provider/openstack: Use IPv4 as the defeault IP version for subnets [GH-3091] * provider/aws: Apply security group after restoring db_instance from snapshot [GH-3513] * provider/aws: Making the AutoScalingGroup name optional [GH-3710]