From 9b2ec3ac533201cdb262c6a4d649c2c906880784 Mon Sep 17 00:00:00 2001 From: David Radcliffe Date: Sat, 18 Jul 2015 22:31:33 -0400 Subject: [PATCH 001/173] add Dyn provider --- builtin/bins/provider-dyn/main.go | 12 ++ builtin/bins/provider-dyn/main_test.go | 1 + builtin/providers/dyn/config.go | 28 +++ builtin/providers/dyn/provider.go | 50 +++++ builtin/providers/dyn/provider_test.go | 47 +++++ builtin/providers/dyn/resource_dyn_record.go | 177 +++++++++++++++++ .../providers/dyn/resource_dyn_record_test.go | 178 ++++++++++++++++++ 7 files changed, 493 insertions(+) create mode 100644 builtin/bins/provider-dyn/main.go create mode 100644 builtin/bins/provider-dyn/main_test.go create mode 100644 builtin/providers/dyn/config.go create mode 100644 builtin/providers/dyn/provider.go create mode 100644 builtin/providers/dyn/provider_test.go create mode 100644 builtin/providers/dyn/resource_dyn_record.go create mode 100644 builtin/providers/dyn/resource_dyn_record_test.go diff --git a/builtin/bins/provider-dyn/main.go b/builtin/bins/provider-dyn/main.go new file mode 100644 index 0000000000..22809f46a2 --- /dev/null +++ b/builtin/bins/provider-dyn/main.go @@ -0,0 +1,12 @@ +package main + +import ( + "github.com/hashicorp/terraform/builtin/providers/dyn" + "github.com/hashicorp/terraform/plugin" +) + +func main() { + plugin.Serve(&plugin.ServeOpts{ + ProviderFunc: dyn.Provider, + }) +} diff --git a/builtin/bins/provider-dyn/main_test.go b/builtin/bins/provider-dyn/main_test.go new file mode 100644 index 0000000000..06ab7d0f9a --- /dev/null +++ b/builtin/bins/provider-dyn/main_test.go @@ -0,0 +1 @@ +package main diff --git a/builtin/providers/dyn/config.go b/builtin/providers/dyn/config.go new file mode 100644 index 0000000000..091c929d93 --- /dev/null +++ b/builtin/providers/dyn/config.go @@ -0,0 +1,28 @@ +package dyn + +import ( + "fmt" + "log" + + "github.com/nesv/go-dynect/dynect" +) + +type Config struct { + CustomerName string + Username string + Password string +} + +// Client() returns a new client for accessing dyn. +func (c *Config) Client() (*dynect.ConvenientClient, error) { + client := dynect.NewConvenientClient(c.CustomerName) + err := client.Login(c.Username, c.Password) + + if err != nil { + return nil, fmt.Errorf("Error setting up Dyn client: %s", err) + } + + log.Printf("[INFO] Dyn client configured for customer: %s, user: %s", c.CustomerName, c.Username) + + return client, nil +} diff --git a/builtin/providers/dyn/provider.go b/builtin/providers/dyn/provider.go new file mode 100644 index 0000000000..c591745aec --- /dev/null +++ b/builtin/providers/dyn/provider.go @@ -0,0 +1,50 @@ +package dyn + +import ( + "github.com/hashicorp/terraform/helper/schema" + "github.com/hashicorp/terraform/terraform" +) + +// Provider returns a terraform.ResourceProvider. +func Provider() terraform.ResourceProvider { + return &schema.Provider{ + Schema: map[string]*schema.Schema{ + "customer_name": &schema.Schema{ + Type: schema.TypeString, + Required: true, + DefaultFunc: schema.EnvDefaultFunc("DYN_CUSTOMER_NAME", nil), + Description: "A Dyn customer name.", + }, + + "username": &schema.Schema{ + Type: schema.TypeString, + Required: true, + DefaultFunc: schema.EnvDefaultFunc("DYN_USERNAME", nil), + Description: "A Dyn username.", + }, + + "password": &schema.Schema{ + Type: schema.TypeString, + Required: true, + DefaultFunc: schema.EnvDefaultFunc("DYN_PASSWORD", nil), + Description: "The Dyn password.", + }, + }, + + ResourcesMap: map[string]*schema.Resource{ + "dyn_record": resourceDynRecord(), + }, + + ConfigureFunc: providerConfigure, + } +} + +func providerConfigure(d *schema.ResourceData) (interface{}, error) { + config := Config{ + CustomerName: d.Get("customer_name").(string), + Username: d.Get("username").(string), + Password: d.Get("password").(string), + } + + return config.Client() +} diff --git a/builtin/providers/dyn/provider_test.go b/builtin/providers/dyn/provider_test.go new file mode 100644 index 0000000000..da148ff2fe --- /dev/null +++ b/builtin/providers/dyn/provider_test.go @@ -0,0 +1,47 @@ +package dyn + +import ( + "os" + "testing" + + "github.com/hashicorp/terraform/helper/schema" + "github.com/hashicorp/terraform/terraform" +) + +var testAccProviders map[string]terraform.ResourceProvider +var testAccProvider *schema.Provider + +func init() { + testAccProvider = Provider().(*schema.Provider) + testAccProviders = map[string]terraform.ResourceProvider{ + "dyn": testAccProvider, + } +} + +func TestProvider(t *testing.T) { + if err := Provider().(*schema.Provider).InternalValidate(); err != nil { + t.Fatalf("err: %s", err) + } +} + +func TestProvider_impl(t *testing.T) { + var _ terraform.ResourceProvider = Provider() +} + +func testAccPreCheck(t *testing.T) { + if v := os.Getenv("DYN_CUSTOMER_NAME"); v == "" { + t.Fatal("DYN_CUSTOMER_NAME must be set for acceptance tests") + } + + if v := os.Getenv("DYN_USERNAME"); v == "" { + t.Fatal("DYN_USERNAME must be set for acceptance tests") + } + + if v := os.Getenv("DYN_PASSWORD"); v == "" { + t.Fatal("DYN_PASSWORD must be set for acceptance tests.") + } + + if v := os.Getenv("DYN_ZONE"); v == "" { + t.Fatal("DYN_ZONE must be set for acceptance tests. The domain is used to ` and destroy record against.") + } +} diff --git a/builtin/providers/dyn/resource_dyn_record.go b/builtin/providers/dyn/resource_dyn_record.go new file mode 100644 index 0000000000..24c2977d5e --- /dev/null +++ b/builtin/providers/dyn/resource_dyn_record.go @@ -0,0 +1,177 @@ +package dyn + +import ( + "fmt" + "log" + + "github.com/hashicorp/terraform/helper/schema" + "github.com/nesv/go-dynect/dynect" +) + +func resourceDynRecord() *schema.Resource { + return &schema.Resource{ + Create: resourceDynRecordCreate, + Read: resourceDynRecordRead, + Update: resourceDynRecordUpdate, + Delete: resourceDynRecordDelete, + + Schema: map[string]*schema.Schema{ + "zone": &schema.Schema{ + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + + "name": &schema.Schema{ + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + + "fqdn": &schema.Schema{ + Type: schema.TypeString, + Computed: true, + }, + + "type": &schema.Schema{ + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + + "value": &schema.Schema{ + Type: schema.TypeString, + Required: true, + }, + + "ttl": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + Default: "0", // 0 means use zone default + }, + }, + } +} + +func resourceDynRecordCreate(d *schema.ResourceData, meta interface{}) error { + client := meta.(*dynect.ConvenientClient) + + record := &dynect.Record{ + Name: d.Get("name").(string), + Zone: d.Get("zone").(string), + Type: d.Get("type").(string), + TTL: d.Get("ttl").(string), + Value: d.Get("value").(string), + } + log.Printf("[DEBUG] Dyn record create configuration: %#v", record) + + // create the record + err := client.CreateRecord(record) + if err != nil { + return fmt.Errorf("Failed to create Dyn record: %s", err) + } + + // publish the zone + err = client.PublishZone(record.Zone) + if err != nil { + return fmt.Errorf("Failed to publish Dyn zone: %s", err) + } + + // get the record ID + err = client.GetRecordID(record) + if err != nil { + return fmt.Errorf("%s", err) + } + d.SetId(record.ID) + + return resourceDynRecordRead(d, meta) +} + +func resourceDynRecordRead(d *schema.ResourceData, meta interface{}) error { + client := meta.(*dynect.ConvenientClient) + + record := &dynect.Record{ + ID: d.Id(), + Name: d.Get("name").(string), + Zone: d.Get("zone").(string), + TTL: d.Get("ttl").(string), + FQDN: d.Get("fqdn").(string), + Type: d.Get("type").(string), + } + + err := client.GetRecord(record) + if err != nil { + return fmt.Errorf("Couldn't find Dyn record: %s", err) + } + + d.Set("zone", record.Zone) + d.Set("fqdn", record.FQDN) + d.Set("name", record.Name) + d.Set("type", record.Type) + d.Set("ttl", record.TTL) + d.Set("value", record.Value) + + return nil +} + +func resourceDynRecordUpdate(d *schema.ResourceData, meta interface{}) error { + client := meta.(*dynect.ConvenientClient) + + record := &dynect.Record{ + Name: d.Get("name").(string), + Zone: d.Get("zone").(string), + TTL: d.Get("ttl").(string), + Type: d.Get("type").(string), + Value: d.Get("value").(string), + } + log.Printf("[DEBUG] Dyn record update configuration: %#v", record) + + // update the record + err := client.UpdateRecord(record) + if err != nil { + return fmt.Errorf("Failed to update Dyn record: %s", err) + } + + // publish the zone + err = client.PublishZone(record.Zone) + if err != nil { + return fmt.Errorf("Failed to publish Dyn zone: %s", err) + } + + // get the record ID + err = client.GetRecordID(record) + if err != nil { + return fmt.Errorf("%s", err) + } + d.SetId(record.ID) + + return resourceDynRecordRead(d, meta) +} + +func resourceDynRecordDelete(d *schema.ResourceData, meta interface{}) error { + client := meta.(*dynect.ConvenientClient) + + record := &dynect.Record{ + ID: d.Id(), + Name: d.Get("name").(string), + Zone: d.Get("zone").(string), + FQDN: d.Get("fqdn").(string), + Type: d.Get("type").(string), + } + + log.Printf("[INFO] Deleting Dyn record: %s, %s", record.FQDN, record.ID) + + // delete the record + err := client.DeleteRecord(record) + if err != nil { + return fmt.Errorf("Failed to delete Dyn record: %s", err) + } + + // publish the zone + err = client.PublishZone(record.Zone) + if err != nil { + return fmt.Errorf("Failed to publish Dyn zone: %s", err) + } + + return nil +} diff --git a/builtin/providers/dyn/resource_dyn_record_test.go b/builtin/providers/dyn/resource_dyn_record_test.go new file mode 100644 index 0000000000..ccb0ccd982 --- /dev/null +++ b/builtin/providers/dyn/resource_dyn_record_test.go @@ -0,0 +1,178 @@ +package dyn + +import ( + "fmt" + "os" + "testing" + + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" + "github.com/nesv/go-dynect/dynect" +) + +func TestAccDynRecord_Basic(t *testing.T) { + var record dynect.Record + zone := os.Getenv("DYN_ZONE") + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckDynRecordDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: fmt.Sprintf(testAccCheckDynRecordConfig_basic, zone), + Check: resource.ComposeTestCheckFunc( + testAccCheckDynRecordExists("dyn_record.foobar", &record), + testAccCheckDynRecordAttributes(&record), + resource.TestCheckResourceAttr( + "dyn_record.foobar", "name", "terraform"), + resource.TestCheckResourceAttr( + "dyn_record.foobar", "zone", zone), + resource.TestCheckResourceAttr( + "dyn_record.foobar", "value", "192.168.0.10"), + ), + }, + }, + }) +} + +func TestAccDynRecord_Updated(t *testing.T) { + var record dynect.Record + zone := os.Getenv("DYN_ZONE") + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckDynRecordDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: fmt.Sprintf(testAccCheckDynRecordConfig_basic, zone), + Check: resource.ComposeTestCheckFunc( + testAccCheckDynRecordExists("dyn_record.foobar", &record), + testAccCheckDynRecordAttributes(&record), + resource.TestCheckResourceAttr( + "dyn_record.foobar", "name", "terraform"), + resource.TestCheckResourceAttr( + "dyn_record.foobar", "zone", zone), + resource.TestCheckResourceAttr( + "dyn_record.foobar", "value", "192.168.0.10"), + ), + }, + resource.TestStep{ + Config: fmt.Sprintf(testAccCheckDynRecordConfig_new_value, zone), + Check: resource.ComposeTestCheckFunc( + testAccCheckDynRecordExists("dyn_record.foobar", &record), + testAccCheckDynRecordAttributesUpdated(&record), + resource.TestCheckResourceAttr( + "dyn_record.foobar", "name", "terraform"), + resource.TestCheckResourceAttr( + "dyn_record.foobar", "zone", zone), + resource.TestCheckResourceAttr( + "dyn_record.foobar", "value", "192.168.0.11"), + ), + }, + }, + }) +} + +func testAccCheckDynRecordDestroy(s *terraform.State) error { + client := testAccProvider.Meta().(*dynect.ConvenientClient) + + for _, rs := range s.RootModule().Resources { + if rs.Type != "dyn_record" { + continue + } + + foundRecord := &dynect.Record{ + Zone: rs.Primary.Attributes["zone"], + ID: rs.Primary.ID, + FQDN: rs.Primary.Attributes["fqdn"], + Type: rs.Primary.Attributes["type"], + } + + err := client.GetRecord(foundRecord) + + if err != nil { + return fmt.Errorf("Record still exists") + } + } + + return nil +} + +func testAccCheckDynRecordAttributes(record *dynect.Record) resource.TestCheckFunc { + return func(s *terraform.State) error { + + if record.Value != "192.168.0.10" { + return fmt.Errorf("Bad value: %s", record.Value) + } + + return nil + } +} + +func testAccCheckDynRecordAttributesUpdated(record *dynect.Record) resource.TestCheckFunc { + return func(s *terraform.State) error { + + if record.Value != "192.168.0.11" { + return fmt.Errorf("Bad value: %s", record.Value) + } + + return nil + } +} + +func testAccCheckDynRecordExists(n string, record *dynect.Record) resource.TestCheckFunc { + return func(s *terraform.State) error { + rs, ok := s.RootModule().Resources[n] + + if !ok { + return fmt.Errorf("Not found: %s", n) + } + + if rs.Primary.ID == "" { + return fmt.Errorf("No Record ID is set") + } + + client := testAccProvider.Meta().(*dynect.ConvenientClient) + + foundRecord := &dynect.Record{ + Zone: rs.Primary.Attributes["zone"], + ID: rs.Primary.ID, + FQDN: rs.Primary.Attributes["fqdn"], + Type: rs.Primary.Attributes["type"], + } + + err := client.GetRecord(foundRecord) + + if err != nil { + return err + } + + if foundRecord.ID != rs.Primary.ID { + return fmt.Errorf("Record not found") + } + + *record = *foundRecord + + return nil + } +} + +const testAccCheckDynRecordConfig_basic = ` +resource "dyn_record" "foobar" { + zone = "%s" + name = "terraform" + value = "192.168.0.10" + type = "A" + ttl = 3600 +}` + +const testAccCheckDynRecordConfig_new_value = ` +resource "dyn_record" "foobar" { + zone = "%s" + name = "terraform" + value = "192.168.0.11" + type = "A" + ttl = 3600 +}` From 76dcc6659710d2b4de58645a324c0a7f115a09fb Mon Sep 17 00:00:00 2001 From: David Radcliffe Date: Tue, 21 Jul 2015 20:43:20 -0400 Subject: [PATCH 002/173] Dyn provider docs --- .../docs/providers/dyn/index.html.markdown | 39 ++++++++++++++++++ .../docs/providers/dyn/r/record.html.markdown | 41 +++++++++++++++++++ website/source/layouts/dyn.erb | 24 +++++++++++ 3 files changed, 104 insertions(+) create mode 100644 website/source/docs/providers/dyn/index.html.markdown create mode 100644 website/source/docs/providers/dyn/r/record.html.markdown create mode 100644 website/source/layouts/dyn.erb diff --git a/website/source/docs/providers/dyn/index.html.markdown b/website/source/docs/providers/dyn/index.html.markdown new file mode 100644 index 0000000000..700bb00870 --- /dev/null +++ b/website/source/docs/providers/dyn/index.html.markdown @@ -0,0 +1,39 @@ +--- +layout: "dyn" +page_title: "Provider: Dyn" +sidebar_current: "docs-dyn-index" +description: |- + The Dyn provider is used to interact with the resources supported by Dyn. The provider needs to be configured with the proper credentials before it can be used. +--- + +# Dyn Provider + +The Dyn provider is used to interact with the +resources supported by Dyn. 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. + +## Example Usage + +``` +# Configure the Dyn provider +provider "dyn" { + customer_name = "${var.dyn_customer_name}" + username = "${var.dyn_username}" + password = "${var.dyn_password}" +} + +# Create a record +resource "dyn_record" "www" { + ... +} +``` + +## Argument Reference + +The following arguments are supported: + +* `customer_name` - (Required) The Dyn customer name. It must be provided, but it can also be sourced from the `DYN_CUSTOMER_NAME` environment variable. +* `username` - (Required) The Dyn username. It must be provided, but it can also be sourced from the `DYN_USERNAME` environment variable. +* `password` - (Required) The Dyn password. It must be provided, but it can also be sourced from the `DYN_PASSWORD` environment variable. diff --git a/website/source/docs/providers/dyn/r/record.html.markdown b/website/source/docs/providers/dyn/r/record.html.markdown new file mode 100644 index 0000000000..6094c27dee --- /dev/null +++ b/website/source/docs/providers/dyn/r/record.html.markdown @@ -0,0 +1,41 @@ +--- +layout: "dyn" +page_title: "Dyn: dyn_record" +sidebar_current: "docs-dyn-resource-record" +description: |- + Provides a Dyn DNS record resource. +--- + +# dyn\_record + +Provides a Dyn DNS record resource. + +## Example Usage + +``` +# Add a record to the domain +resource "dyn_record" "foobar" { + zone = "${var.dyn_zone}" + name = "terraform" + value = "192.168.0.11" + type = "A" + ttl = 3600 +} +``` + +## Argument Reference + +The following arguments are supported: + +* `name` - (Required) The name of the record. +* `type` - (Required) The type of the record. +* `value` - (Required) The value of the record. +* `zone` - (Required) The DNS zone to add the record to. +* `ttl` - (Optional) The TTL of the record. Default uses the zone default. + +## Attributes Reference + +The following attributes are exported: + +* `id` - The record ID. +* `fqdn` - The FQDN of the record, built from the `name` and the `zone`. diff --git a/website/source/layouts/dyn.erb b/website/source/layouts/dyn.erb new file mode 100644 index 0000000000..ee66e72705 --- /dev/null +++ b/website/source/layouts/dyn.erb @@ -0,0 +1,24 @@ +<% wrap_layout :inner do %> + <% content_for :sidebar do %> + + <% end %> + + <%= yield %> +<% end %> From 5576ec6b107b4786b6879f6d2827d332b2ee7dbe Mon Sep 17 00:00:00 2001 From: Patrick Gray Date: Sun, 13 Sep 2015 03:27:07 -0400 Subject: [PATCH 003/173] implemented update function for aws_iam_user and it works but still causes transient error for aws_iam_group_membership --- .../providers/aws/resource_aws_iam_user.go | 35 +++++++++++++++---- 1 file changed, 28 insertions(+), 7 deletions(-) diff --git a/builtin/providers/aws/resource_aws_iam_user.go b/builtin/providers/aws/resource_aws_iam_user.go index c0ef4b8a46..e122e521ec 100644 --- a/builtin/providers/aws/resource_aws_iam_user.go +++ b/builtin/providers/aws/resource_aws_iam_user.go @@ -14,9 +14,7 @@ func resourceAwsIamUser() *schema.Resource { return &schema.Resource{ Create: resourceAwsIamUserCreate, Read: resourceAwsIamUserRead, - // There is an UpdateUser API call, but goamz doesn't support it yet. - // XXX but we aren't using goamz anymore. - //Update: resourceAwsIamUserUpdate, + Update: resourceAwsIamUserUpdate, Delete: resourceAwsIamUserDelete, Schema: map[string]*schema.Schema{ @@ -39,7 +37,6 @@ func resourceAwsIamUser() *schema.Resource { "name": &schema.Schema{ Type: schema.TypeString, Required: true, - ForceNew: true, }, "path": &schema.Schema{ Type: schema.TypeString, @@ -54,9 +51,10 @@ func resourceAwsIamUser() *schema.Resource { func resourceAwsIamUserCreate(d *schema.ResourceData, meta interface{}) error { iamconn := meta.(*AWSClient).iamconn name := d.Get("name").(string) + path := d.Get("path").(string) request := &iam.CreateUserInput{ - Path: aws.String(d.Get("path").(string)), + Path: aws.String(path), UserName: aws.String(name), } @@ -69,9 +67,9 @@ func resourceAwsIamUserCreate(d *schema.ResourceData, meta interface{}) error { func resourceAwsIamUserRead(d *schema.ResourceData, meta interface{}) error { iamconn := meta.(*AWSClient).iamconn - + name := d.Get("name").(string) request := &iam.GetUserInput{ - UserName: aws.String(d.Id()), + UserName: aws.String(name), } getResp, err := iamconn.GetUser(request) @@ -102,6 +100,29 @@ func resourceAwsIamUserReadResult(d *schema.ResourceData, user *iam.User) error return nil } +func resourceAwsIamUserUpdate(d *schema.ResourceData, meta interface{}) error { + if d.HasChange("name") || d.HasChange("path") { + iamconn := meta.(*AWSClient).iamconn + on, nn := d.GetChange("name") + op, np := d.GetChange("path") + fmt.Println(on, nn, op, np) + request := &iam.UpdateUserInput{ + UserName: aws.String(on.(string)), + NewUserName: aws.String(nn.(string)), + NewPath: aws.String(np.(string)), + } + _, err := iamconn.UpdateUser(request) + if err != nil { + if iamerr, ok := err.(awserr.Error); ok && iamerr.Code() == "NoSuchEntity" { + d.SetId("") + return nil + } + return fmt.Errorf("Error updating IAM User %s: %s", d.Id(), err) + } + return resourceAwsIamUserRead(d, meta) + } + return nil +} func resourceAwsIamUserDelete(d *schema.ResourceData, meta interface{}) error { iamconn := meta.(*AWSClient).iamconn From 0ab12a54f138a9d70e9bc6ba97b955c58f57f874 Mon Sep 17 00:00:00 2001 From: Patrick Gray Date: Sun, 13 Sep 2015 03:37:45 -0400 Subject: [PATCH 004/173] handle error of trying to remove a user from a group when their name has already changed --- builtin/providers/aws/resource_aws_iam_group_membership.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/builtin/providers/aws/resource_aws_iam_group_membership.go b/builtin/providers/aws/resource_aws_iam_group_membership.go index c90511cd62..14bdd37139 100644 --- a/builtin/providers/aws/resource_aws_iam_group_membership.go +++ b/builtin/providers/aws/resource_aws_iam_group_membership.go @@ -135,6 +135,9 @@ func removeUsersFromGroup(conn *iam.IAM, users []*string, group string) error { }) if err != nil { + if iamerr, ok := err.(awserr.Error); ok && iamerr.Code() == "NoSuchEntity" { + return nil + } return err } } From 37e280f6be4de2b6983d869096198261159b64b8 Mon Sep 17 00:00:00 2001 From: Patrick Gray Date: Sun, 13 Sep 2015 03:52:40 -0400 Subject: [PATCH 005/173] add tests for updating a user --- builtin/providers/aws/resource_aws_iam_user_test.go | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/builtin/providers/aws/resource_aws_iam_user_test.go b/builtin/providers/aws/resource_aws_iam_user_test.go index 0dd2530a1b..4a7939658a 100644 --- a/builtin/providers/aws/resource_aws_iam_user_test.go +++ b/builtin/providers/aws/resource_aws_iam_user_test.go @@ -26,6 +26,13 @@ func TestAccAWSUser_basic(t *testing.T) { testAccCheckAWSUserAttributes(&conf), ), }, + resource.TestStep{ + Config: testAccAWSUserConfig2, + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSUserExists("aws_iam_user.user", &conf), + testAccCheckAWSUserAttributes(&conf), + ), + }, }, }) } @@ -105,3 +112,9 @@ resource "aws_iam_user" "user" { path = "/" } ` +const testAccAWSUserConfig2 = ` +resource "aws_iam_user" "user" { + name = "test-user2" + path = "/balls/" +} +` From 83e6d8b60ce58f71dce0c0dc60f468fc63551973 Mon Sep 17 00:00:00 2001 From: David Radcliffe Date: Wed, 2 Sep 2015 13:51:36 -0400 Subject: [PATCH 006/173] add Mutex so that we only do one Dyn operation at a time (extra thanks to @daveadams) --- builtin/providers/dyn/resource_dyn_record.go | 21 +++++++ .../providers/dyn/resource_dyn_record_test.go | 61 +++++++++++++++++++ 2 files changed, 82 insertions(+) diff --git a/builtin/providers/dyn/resource_dyn_record.go b/builtin/providers/dyn/resource_dyn_record.go index 24c2977d5e..7f7b66fd50 100644 --- a/builtin/providers/dyn/resource_dyn_record.go +++ b/builtin/providers/dyn/resource_dyn_record.go @@ -3,11 +3,14 @@ package dyn import ( "fmt" "log" + "sync" "github.com/hashicorp/terraform/helper/schema" "github.com/nesv/go-dynect/dynect" ) +var mutex = &sync.Mutex{} + func resourceDynRecord() *schema.Resource { return &schema.Resource{ Create: resourceDynRecordCreate, @@ -54,6 +57,8 @@ func resourceDynRecord() *schema.Resource { } func resourceDynRecordCreate(d *schema.ResourceData, meta interface{}) error { + mutex.Lock() + client := meta.(*dynect.ConvenientClient) record := &dynect.Record{ @@ -68,26 +73,33 @@ func resourceDynRecordCreate(d *schema.ResourceData, meta interface{}) error { // create the record err := client.CreateRecord(record) if err != nil { + mutex.Unlock() return fmt.Errorf("Failed to create Dyn record: %s", err) } // publish the zone err = client.PublishZone(record.Zone) if err != nil { + mutex.Unlock() return fmt.Errorf("Failed to publish Dyn zone: %s", err) } // get the record ID err = client.GetRecordID(record) if err != nil { + mutex.Unlock() return fmt.Errorf("%s", err) } d.SetId(record.ID) + mutex.Unlock() return resourceDynRecordRead(d, meta) } func resourceDynRecordRead(d *schema.ResourceData, meta interface{}) error { + mutex.Lock() + defer mutex.Unlock() + client := meta.(*dynect.ConvenientClient) record := &dynect.Record{ @@ -115,6 +127,8 @@ func resourceDynRecordRead(d *schema.ResourceData, meta interface{}) error { } func resourceDynRecordUpdate(d *schema.ResourceData, meta interface{}) error { + mutex.Lock() + client := meta.(*dynect.ConvenientClient) record := &dynect.Record{ @@ -129,26 +143,33 @@ func resourceDynRecordUpdate(d *schema.ResourceData, meta interface{}) error { // update the record err := client.UpdateRecord(record) if err != nil { + mutex.Unlock() return fmt.Errorf("Failed to update Dyn record: %s", err) } // publish the zone err = client.PublishZone(record.Zone) if err != nil { + mutex.Unlock() return fmt.Errorf("Failed to publish Dyn zone: %s", err) } // get the record ID err = client.GetRecordID(record) if err != nil { + mutex.Unlock() return fmt.Errorf("%s", err) } d.SetId(record.ID) + mutex.Unlock() return resourceDynRecordRead(d, meta) } func resourceDynRecordDelete(d *schema.ResourceData, meta interface{}) error { + mutex.Lock() + defer mutex.Unlock() + client := meta.(*dynect.ConvenientClient) record := &dynect.Record{ diff --git a/builtin/providers/dyn/resource_dyn_record_test.go b/builtin/providers/dyn/resource_dyn_record_test.go index ccb0ccd982..e233672834 100644 --- a/builtin/providers/dyn/resource_dyn_record_test.go +++ b/builtin/providers/dyn/resource_dyn_record_test.go @@ -75,6 +75,44 @@ func TestAccDynRecord_Updated(t *testing.T) { }) } +func TestAccDynRecord_Multiple(t *testing.T) { + var record dynect.Record + zone := os.Getenv("DYN_ZONE") + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckDynRecordDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: fmt.Sprintf(testAccCheckDynRecordConfig_multiple, zone, zone, zone), + Check: resource.ComposeTestCheckFunc( + testAccCheckDynRecordExists("dyn_record.foobar1", &record), + testAccCheckDynRecordAttributes(&record), + resource.TestCheckResourceAttr( + "dyn_record.foobar1", "name", "terraform1"), + resource.TestCheckResourceAttr( + "dyn_record.foobar1", "zone", zone), + resource.TestCheckResourceAttr( + "dyn_record.foobar1", "value", "192.168.0.10"), + resource.TestCheckResourceAttr( + "dyn_record.foobar2", "name", "terraform2"), + resource.TestCheckResourceAttr( + "dyn_record.foobar2", "zone", zone), + resource.TestCheckResourceAttr( + "dyn_record.foobar2", "value", "192.168.1.10"), + resource.TestCheckResourceAttr( + "dyn_record.foobar3", "name", "terraform3"), + resource.TestCheckResourceAttr( + "dyn_record.foobar3", "zone", zone), + resource.TestCheckResourceAttr( + "dyn_record.foobar3", "value", "192.168.2.10"), + ), + }, + }, + }) +} + func testAccCheckDynRecordDestroy(s *terraform.State) error { client := testAccProvider.Meta().(*dynect.ConvenientClient) @@ -176,3 +214,26 @@ resource "dyn_record" "foobar" { type = "A" ttl = 3600 }` + +const testAccCheckDynRecordConfig_multiple = ` +resource "dyn_record" "foobar1" { + zone = "%s" + name = "terraform1" + value = "192.168.0.10" + type = "A" + ttl = 3600 +} +resource "dyn_record" "foobar2" { + zone = "%s" + name = "terraform2" + value = "192.168.1.10" + type = "A" + ttl = 3600 +} +resource "dyn_record" "foobar3" { + zone = "%s" + name = "terraform3" + value = "192.168.2.10" + type = "A" + ttl = 3600 +}` From d798042475f8f9c4545052bb67b8941dc565e939 Mon Sep 17 00:00:00 2001 From: clint shryock Date: Tue, 20 Oct 2015 13:32:35 -0500 Subject: [PATCH 007/173] provider/aws: Downcase Route 53 record names in statefile --- builtin/providers/aws/resource_aws_route53_record.go | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/builtin/providers/aws/resource_aws_route53_record.go b/builtin/providers/aws/resource_aws_route53_record.go index 1966c33de4..9944d4312e 100644 --- a/builtin/providers/aws/resource_aws_route53_record.go +++ b/builtin/providers/aws/resource_aws_route53_record.go @@ -28,6 +28,10 @@ func resourceAwsRoute53Record() *schema.Resource { Type: schema.TypeString, Required: true, ForceNew: true, + StateFunc: func(v interface{}) string { + value := v.(string) + return strings.ToLower(value) + }, }, "fqdn": &schema.Schema{ @@ -242,6 +246,8 @@ func resourceAwsRoute53RecordRead(d *schema.ResourceData, meta interface{}) erro StartRecordType: aws.String(d.Get("type").(string)), } + log.Printf("[DEBUG] List resource records sets for zone: %s, opts: %s", + zone, lopts) resp, err := conn.ListResourceRecordSets(lopts) if err != nil { return err @@ -251,7 +257,7 @@ func resourceAwsRoute53RecordRead(d *schema.ResourceData, meta interface{}) erro found := false for _, record := range resp.ResourceRecordSets { name := cleanRecordName(*record.Name) - if FQDN(name) != FQDN(*lopts.StartRecordName) { + if FQDN(strings.ToLower(name)) != FQDN(strings.ToLower(*lopts.StartRecordName)) { continue } if strings.ToUpper(*record.Type) != strings.ToUpper(*lopts.StartRecordType) { @@ -279,6 +285,7 @@ func resourceAwsRoute53RecordRead(d *schema.ResourceData, meta interface{}) erro } if !found { + log.Printf("[DEBUG] No matching record found for: %s, removing from state file", en) d.SetId("") } From 953f38c534f1ca5b271127d85732691080d8ecba Mon Sep 17 00:00:00 2001 From: clint shryock Date: Tue, 20 Oct 2015 16:36:25 -0500 Subject: [PATCH 008/173] lowercase everything in r53 names --- builtin/providers/aws/resource_aws_route53_record.go | 5 +++-- builtin/providers/aws/resource_aws_route53_record_test.go | 4 ++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/builtin/providers/aws/resource_aws_route53_record.go b/builtin/providers/aws/resource_aws_route53_record.go index 9944d4312e..25d73683c8 100644 --- a/builtin/providers/aws/resource_aws_route53_record.go +++ b/builtin/providers/aws/resource_aws_route53_record.go @@ -196,12 +196,13 @@ func resourceAwsRoute53RecordCreate(d *schema.ResourceData, meta interface{}) er // Generate an ID vars := []string{ zone, - d.Get("name").(string), + strings.ToLower(d.Get("name").(string)), d.Get("type").(string), } if v, ok := d.GetOk("set_identifier"); ok { vars = append(vars, v.(string)) } + d.SetId(strings.Join(vars, "_")) // Wait until we are done @@ -447,7 +448,7 @@ func cleanRecordName(name string) string { // If it does not, add the zone name to form a fully qualified name // and keep AWS happy. func expandRecordName(name, zone string) string { - rn := strings.TrimSuffix(name, ".") + rn := strings.ToLower(strings.TrimSuffix(name, ".")) zone = strings.TrimSuffix(zone, ".") if !strings.HasSuffix(rn, zone) { rn = strings.Join([]string{name, zone}, ".") diff --git a/builtin/providers/aws/resource_aws_route53_record_test.go b/builtin/providers/aws/resource_aws_route53_record_test.go index bbeb859cd8..d10653a0ce 100644 --- a/builtin/providers/aws/resource_aws_route53_record_test.go +++ b/builtin/providers/aws/resource_aws_route53_record_test.go @@ -291,7 +291,7 @@ func testAccCheckRoute53RecordExists(n string) resource.TestCheckFunc { // rec := resp.ResourceRecordSets[0] for _, rec := range resp.ResourceRecordSets { recName := cleanRecordName(*rec.Name) - if FQDN(recName) == FQDN(en) && *rec.Type == rType { + if FQDN(strings.ToLower(recName)) == FQDN(strings.ToLower(en)) && *rec.Type == rType { return nil } } @@ -306,7 +306,7 @@ resource "aws_route53_zone" "main" { resource "aws_route53_record" "default" { zone_id = "${aws_route53_zone.main.zone_id}" - name = "www.notexample.com" + name = "www.NOTexamplE.com" type = "A" ttl = "30" records = ["127.0.0.1", "127.0.0.27"] From 65cd53f70afac43665150b30c7e3287236648ce9 Mon Sep 17 00:00:00 2001 From: Wojtek Mach Date: Thu, 22 Oct 2015 21:14:54 +0200 Subject: [PATCH 009/173] digitalocean: update index docs for do_token var Updates the docs and clarifies the usage of `do_token` variable. I was experiencing an issue mentioned here https://github.com/hashicorp/terraform/issues/124 and so adding more docs should be helpful. --- website/source/docs/providers/do/index.html.markdown | 3 +++ 1 file changed, 3 insertions(+) diff --git a/website/source/docs/providers/do/index.html.markdown b/website/source/docs/providers/do/index.html.markdown index 9e18277a3c..468539c57d 100644 --- a/website/source/docs/providers/do/index.html.markdown +++ b/website/source/docs/providers/do/index.html.markdown @@ -17,6 +17,9 @@ Use the navigation to the left to read about the available resources. ## Example Usage ``` +# Set the variable value in *.tfvars file or using -var="do_token=..." CLI option +variable "do_token" {} + # Configure the DigitalOcean Provider provider "digitalocean" { token = "${var.do_token}" From 7dd15469a5aa97a973f0a5590a7c2ac84cb9373c Mon Sep 17 00:00:00 2001 From: stack72 Date: Fri, 30 Oct 2015 23:55:00 +0000 Subject: [PATCH 010/173] Adding the ability to specify a snapshot window and retention limit for Redis ElastiCache clusters --- .../aws/resource_aws_elasticache_cluster.go | 37 ++++++++++++ .../resource_aws_elasticache_cluster_test.go | 58 +++++++++++++++++++ .../aws/r/elasticache_cluster.html.markdown | 9 +++ 3 files changed, 104 insertions(+) diff --git a/builtin/providers/aws/resource_aws_elasticache_cluster.go b/builtin/providers/aws/resource_aws_elasticache_cluster.go index 3460fb292f..dde2cd5e38 100644 --- a/builtin/providers/aws/resource_aws_elasticache_cluster.go +++ b/builtin/providers/aws/resource_aws_elasticache_cluster.go @@ -138,6 +138,24 @@ func resourceAwsElasticacheCluster() *schema.Resource { }, }, + "snapshot_window": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + }, + + "snapshot_retention_limit": &schema.Schema{ + Type: schema.TypeInt, + Optional: true, + ValidateFunc: func(v interface{}, k string) (ws []string, es []error) { + value := v.(int) + if value > 35 { + es = append(es, fmt.Errorf( + "snapshot retention limit cannot be more than 35 days")) + } + return + }, + }, + "tags": tagsSchema(), // apply_immediately is used to determine when the update modifications @@ -187,6 +205,14 @@ func resourceAwsElasticacheClusterCreate(d *schema.ResourceData, meta interface{ req.CacheParameterGroupName = aws.String(v.(string)) } + if v, ok := d.GetOk("snapshot_retention_limit"); ok { + req.SnapshotRetentionLimit = aws.Int64(int64(v.(int))) + } + + if v, ok := d.GetOk("snapshot_window"); ok { + req.SnapshotWindow = aws.String(v.(string)) + } + if v, ok := d.GetOk("maintenance_window"); ok { req.PreferredMaintenanceWindow = aws.String(v.(string)) } @@ -261,6 +287,8 @@ func resourceAwsElasticacheClusterRead(d *schema.ResourceData, meta interface{}) d.Set("security_group_ids", c.SecurityGroups) d.Set("parameter_group_name", c.CacheParameterGroup) d.Set("maintenance_window", c.PreferredMaintenanceWindow) + d.Set("snapshot_window", c.SnapshotWindow) + d.Set("snapshot_retention_limit", c.SnapshotRetentionLimit) if c.NotificationConfiguration != nil { if *c.NotificationConfiguration.TopicStatus == "active" { d.Set("notification_topic_arn", c.NotificationConfiguration.TopicArn) @@ -344,6 +372,15 @@ func resourceAwsElasticacheClusterUpdate(d *schema.ResourceData, meta interface{ requestUpdate = true } + if d.HasChange("snapshot_window") { + req.EngineVersion = aws.String(d.Get("snapshot_window").(string)) + requestUpdate = true + } + + if d.HasChange("snapshot_retention_limit") { + req.NumCacheNodes = aws.Int64(int64(d.Get("snapshot_retention_limit").(int))) + } + if d.HasChange("num_cache_nodes") { req.NumCacheNodes = aws.Int64(int64(d.Get("num_cache_nodes").(int))) requestUpdate = true diff --git a/builtin/providers/aws/resource_aws_elasticache_cluster_test.go b/builtin/providers/aws/resource_aws_elasticache_cluster_test.go index b930600288..d084224fb1 100644 --- a/builtin/providers/aws/resource_aws_elasticache_cluster_test.go +++ b/builtin/providers/aws/resource_aws_elasticache_cluster_test.go @@ -33,6 +33,28 @@ func TestAccAWSElasticacheCluster_basic(t *testing.T) { }) } +func TestAccAWSElasticacheCluster_snapshots(t *testing.T) { + var ec elasticache.CacheCluster + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSElasticacheClusterDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccAWSElasticacheClusterConfig_snapshots, + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSElasticacheSecurityGroupExists("aws_elasticache_security_group.bar"), + testAccCheckAWSElasticacheClusterExists("aws_elasticache_cluster.bar", &ec), + resource.TestCheckResourceAttr( + "aws_elasticache_cluster.bar", "snapshot_window", "05:00-09:00"), + resource.TestCheckResourceAttr( + "aws_elasticache_cluster.bar", "snapshot_retention_limit", "3"), + ), + }, + }, + }) +} + func TestAccAWSElasticacheCluster_vpc(t *testing.T) { var csg elasticache.CacheSubnetGroup var ec elasticache.CacheCluster @@ -149,6 +171,42 @@ resource "aws_elasticache_cluster" "bar" { port = 11211 parameter_group_name = "default.memcached1.4" security_group_names = ["${aws_elasticache_security_group.bar.name}"] + snapshot_window = "05:00-09:00" + snapshot_retention_limit = 3 +} +`, genRandInt(), genRandInt(), genRandInt()) + +var testAccAWSElasticacheClusterConfig_snapshots = fmt.Sprintf(` +provider "aws" { + region = "us-east-1" +} +resource "aws_security_group" "bar" { + name = "tf-test-security-group-%03d" + description = "tf-test-security-group-descr" + ingress { + from_port = -1 + to_port = -1 + protocol = "icmp" + cidr_blocks = ["0.0.0.0/0"] + } +} + +resource "aws_elasticache_security_group" "bar" { + name = "tf-test-security-group-%03d" + description = "tf-test-security-group-descr" + security_group_names = ["${aws_security_group.bar.name}"] +} + +resource "aws_elasticache_cluster" "bar" { + cluster_id = "tf-test-%03d" + engine = "redis" + node_type = "cache.m1.small" + num_cache_nodes = 1 + port = 6379 + parameter_group_name = "default.redis2.8" + security_group_names = ["${aws_elasticache_security_group.bar.name}"] + snapshot_window = "05:00-09:00" + snapshot_retention_limit = 3 } `, genRandInt(), genRandInt(), genRandInt()) diff --git a/website/source/docs/providers/aws/r/elasticache_cluster.html.markdown b/website/source/docs/providers/aws/r/elasticache_cluster.html.markdown index ef1d69ed4a..4a4cb4d76e 100644 --- a/website/source/docs/providers/aws/r/elasticache_cluster.html.markdown +++ b/website/source/docs/providers/aws/r/elasticache_cluster.html.markdown @@ -73,6 +73,15 @@ names to associate with this cache cluster Amazon Resource Name (ARN) of a Redis RDB snapshot file stored in Amazon S3. Example: `arn:aws:s3:::my_bucket/snapshot1.rdb` +* `snapshot_window` - (Optional) The daily time range (in UTC) during which ElastiCache will +begin taking a daily snapshot of your cache cluster. Can only be used for the Redis engine. Example: 05:00-09:00 + +* `snapshow_retention_limit` - (Optional) The number of days for which ElastiCache will +retain automatic cache cluster snapshots before deleting them. For example, if you set +SnapshotRetentionLimit to 5, then a snapshot that was taken today will be retained for 5 days +before being deleted. If the value of SnapshotRetentionLimit is set to zero (0), backups are turned off. +Can only be used for the Redis engine. + * `notification_topic_arn` – (Optional) An Amazon Resource Name (ARN) of an SNS topic to send ElastiCache notifications to. Example: `arn:aws:sns:us-east-1:012345678999:my_sns_topic` From 4f05df6cad9773c2bda92bae8def4017ec3d1041 Mon Sep 17 00:00:00 2001 From: stack72 Date: Mon, 2 Nov 2015 20:57:04 +0000 Subject: [PATCH 011/173] When I was setting the update parameters for the Snapshotting, I didn't update the copy/pasted params --- builtin/providers/aws/resource_aws_elasticache_cluster.go | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/builtin/providers/aws/resource_aws_elasticache_cluster.go b/builtin/providers/aws/resource_aws_elasticache_cluster.go index dde2cd5e38..a33321c3b4 100644 --- a/builtin/providers/aws/resource_aws_elasticache_cluster.go +++ b/builtin/providers/aws/resource_aws_elasticache_cluster.go @@ -373,12 +373,11 @@ func resourceAwsElasticacheClusterUpdate(d *schema.ResourceData, meta interface{ } if d.HasChange("snapshot_window") { - req.EngineVersion = aws.String(d.Get("snapshot_window").(string)) - requestUpdate = true + req.SnapshotWindow = aws.String(d.Get("snapshot_window").(string)) } if d.HasChange("snapshot_retention_limit") { - req.NumCacheNodes = aws.Int64(int64(d.Get("snapshot_retention_limit").(int))) + req.SnapshotRetentionLimit = aws.Int64(int64(d.Get("snapshot_retention_limit").(int))) } if d.HasChange("num_cache_nodes") { From 707bfd739aa2bdd9748896e3cbc5b6e02d1e1077 Mon Sep 17 00:00:00 2001 From: stack72 Date: Tue, 3 Nov 2015 12:35:06 +0000 Subject: [PATCH 012/173] Added an extra test for the Elasticache Cluster to show that updates work. Also added some debugging to show that the API returns the Elasticache retention period info --- .../aws/resource_aws_elasticache_cluster.go | 2 + .../resource_aws_elasticache_cluster_test.go | 67 +++++++++++++++++++ 2 files changed, 69 insertions(+) diff --git a/builtin/providers/aws/resource_aws_elasticache_cluster.go b/builtin/providers/aws/resource_aws_elasticache_cluster.go index a33321c3b4..a2c312d353 100644 --- a/builtin/providers/aws/resource_aws_elasticache_cluster.go +++ b/builtin/providers/aws/resource_aws_elasticache_cluster.go @@ -287,7 +287,9 @@ func resourceAwsElasticacheClusterRead(d *schema.ResourceData, meta interface{}) d.Set("security_group_ids", c.SecurityGroups) d.Set("parameter_group_name", c.CacheParameterGroup) d.Set("maintenance_window", c.PreferredMaintenanceWindow) + log.Printf("[INFO] Found %s as the Snapshow Window", *c.SnapshotWindow) d.Set("snapshot_window", c.SnapshotWindow) + log.Printf("[INFO] Found %d as the Snapshow Retention Limit", *c.SnapshotRetentionLimit) d.Set("snapshot_retention_limit", c.SnapshotRetentionLimit) if c.NotificationConfiguration != nil { if *c.NotificationConfiguration.TopicStatus == "active" { diff --git a/builtin/providers/aws/resource_aws_elasticache_cluster_test.go b/builtin/providers/aws/resource_aws_elasticache_cluster_test.go index d084224fb1..666be2c8ab 100644 --- a/builtin/providers/aws/resource_aws_elasticache_cluster_test.go +++ b/builtin/providers/aws/resource_aws_elasticache_cluster_test.go @@ -54,6 +54,39 @@ func TestAccAWSElasticacheCluster_snapshots(t *testing.T) { }, }) } +func TestAccAWSElasticacheCluster_snapshotsWithUpdates(t *testing.T) { + var ec elasticache.CacheCluster + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSElasticacheClusterDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccAWSElasticacheClusterConfig_snapshots, + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSElasticacheSecurityGroupExists("aws_elasticache_security_group.bar"), + testAccCheckAWSElasticacheClusterExists("aws_elasticache_cluster.bar", &ec), + resource.TestCheckResourceAttr( + "aws_elasticache_cluster.bar", "snapshot_window", "05:00-09:00"), + resource.TestCheckResourceAttr( + "aws_elasticache_cluster.bar", "snapshot_retention_limit", "3"), + ), + }, + + resource.TestStep{ + Config: testAccAWSElasticacheClusterConfig_snapshotsUpdated, + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSElasticacheSecurityGroupExists("aws_elasticache_security_group.bar"), + testAccCheckAWSElasticacheClusterExists("aws_elasticache_cluster.bar", &ec), + resource.TestCheckResourceAttr( + "aws_elasticache_cluster.bar", "snapshot_window", "07:00-09:00"), + resource.TestCheckResourceAttr( + "aws_elasticache_cluster.bar", "snapshot_retention_limit", "7"), + ), + }, + }, + }) +} func TestAccAWSElasticacheCluster_vpc(t *testing.T) { var csg elasticache.CacheSubnetGroup @@ -210,6 +243,40 @@ resource "aws_elasticache_cluster" "bar" { } `, genRandInt(), genRandInt(), genRandInt()) +var testAccAWSElasticacheClusterConfig_snapshotsUpdated = fmt.Sprintf(` +provider "aws" { + region = "us-east-1" +} +resource "aws_security_group" "bar" { + name = "tf-test-security-group-%03d" + description = "tf-test-security-group-descr" + ingress { + from_port = -1 + to_port = -1 + protocol = "icmp" + cidr_blocks = ["0.0.0.0/0"] + } +} + +resource "aws_elasticache_security_group" "bar" { + name = "tf-test-security-group-%03d" + description = "tf-test-security-group-descr" + security_group_names = ["${aws_security_group.bar.name}"] +} + +resource "aws_elasticache_cluster" "bar" { + cluster_id = "tf-test-%03d" + engine = "redis" + node_type = "cache.m1.small" + num_cache_nodes = 1 + port = 6379 + parameter_group_name = "default.redis2.8" + security_group_names = ["${aws_elasticache_security_group.bar.name}"] + snapshot_window = "07:00-09:00" + snapshot_retention_limit = 7 +} +`, genRandInt(), genRandInt(), genRandInt()) + var testAccAWSElasticacheClusterInVPCConfig = fmt.Sprintf(` resource "aws_vpc" "foo" { cidr_block = "192.168.0.0/16" From e390d7ddf232d83fd6e0f8776edc333445fdc8d8 Mon Sep 17 00:00:00 2001 From: Florin Patan Date: Sun, 23 Aug 2015 20:58:25 +0200 Subject: [PATCH 013/173] Add elb access logs setting --- builtin/providers/aws/resource_aws_elb.go | 44 ++++++++++++++++++++++- 1 file changed, 43 insertions(+), 1 deletion(-) diff --git a/builtin/providers/aws/resource_aws_elb.go b/builtin/providers/aws/resource_aws_elb.go index 9955c7cf0a..d80d7e1fac 100644 --- a/builtin/providers/aws/resource_aws_elb.go +++ b/builtin/providers/aws/resource_aws_elb.go @@ -101,6 +101,35 @@ func resourceAwsElb() *schema.Resource { Default: 300, }, + "access_logs": &schema.Schema{ + Type: schema.TypeSet, + Optional: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "enabled": &schema.Schema{ + Type: schema.TypeBool, + Required: true, + Default: false, + }, + "interval": &schema.Schema{ + Type: schema.TypeInt, + Optional: true, + Default: 60, + }, + "bucket": &schema.Schema{ + Type: schema.TypeString, + Required: true, + Default: "", + }, + "bucket_prefix": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + Default: "", + }, + }, + }, + }, + "listener": &schema.Schema{ Type: schema.TypeSet, Required: true, @@ -305,6 +334,7 @@ func resourceAwsElbRead(d *schema.ResourceData, meta interface{}) error { d.Set("idle_timeout", lbAttrs.ConnectionSettings.IdleTimeout) d.Set("connection_draining", lbAttrs.ConnectionDraining.Enabled) d.Set("connection_draining_timeout", lbAttrs.ConnectionDraining.Timeout) + d.Set("access_logs", lbAttrs.AccessLog) resp, err := elbconn.DescribeTags(&elb.DescribeTagsInput{ LoadBalancerNames: []*string{lb.LoadBalancerName}, @@ -405,7 +435,7 @@ func resourceAwsElbUpdate(d *schema.ResourceData, meta interface{}) error { d.SetPartial("instances") } - if d.HasChange("cross_zone_load_balancing") || d.HasChange("idle_timeout") { + if d.HasChange("cross_zone_load_balancing") || d.HasChange("idle_timeout") || d.HasChange("access_logs") { attrs := elb.ModifyLoadBalancerAttributesInput{ LoadBalancerName: aws.String(d.Get("name").(string)), LoadBalancerAttributes: &elb.LoadBalancerAttributes{ @@ -418,6 +448,18 @@ func resourceAwsElbUpdate(d *schema.ResourceData, meta interface{}) error { }, } + logs := d.Get("access_logs").(*schema.Set).List() + if len(logs) > 0 { + log := logs[0].(map[string]interface{}) + accessLogs := &elb.AccessLog{ + Enabled: aws.Bool(log["enabled"].(bool)), + EmitInterval: aws.Int64(log["interval"].(int64)), + S3BucketName: aws.String(log["bucket"].(string)), + S3BucketPrefix: aws.String(log["bucket"].(string)), + } + attrs.LoadBalancerAttributes.AccessLog = accessLogs + } + _, err := elbconn.ModifyLoadBalancerAttributes(&attrs) if err != nil { return fmt.Errorf("Failure configuring ELB attributes: %s", err) From e173b60f104c68553b44ed8a5e5eb5b3151d1ee2 Mon Sep 17 00:00:00 2001 From: Florin Patan Date: Sun, 13 Sep 2015 23:06:57 +0200 Subject: [PATCH 014/173] Changes per feedback --- builtin/providers/aws/resource_aws_elb.go | 29 ++++++++++++++++++----- 1 file changed, 23 insertions(+), 6 deletions(-) diff --git a/builtin/providers/aws/resource_aws_elb.go b/builtin/providers/aws/resource_aws_elb.go index d80d7e1fac..e0c273f121 100644 --- a/builtin/providers/aws/resource_aws_elb.go +++ b/builtin/providers/aws/resource_aws_elb.go @@ -108,8 +108,8 @@ func resourceAwsElb() *schema.Resource { Schema: map[string]*schema.Schema{ "enabled": &schema.Schema{ Type: schema.TypeBool, - Required: true, - Default: false, + Optional: true, + Default: true, }, "interval": &schema.Schema{ Type: schema.TypeInt, @@ -119,18 +119,17 @@ func resourceAwsElb() *schema.Resource { "bucket": &schema.Schema{ Type: schema.TypeString, Required: true, - Default: "", }, "bucket_prefix": &schema.Schema{ Type: schema.TypeString, Optional: true, - Default: "", }, }, }, + Set: resourceAwsElbAccessLogsHash, }, - "listener": &schema.Schema{ + "listener": &schema.Schema{ Type: schema.TypeSet, Required: true, Elem: &schema.Resource{ @@ -455,8 +454,12 @@ func resourceAwsElbUpdate(d *schema.ResourceData, meta interface{}) error { Enabled: aws.Bool(log["enabled"].(bool)), EmitInterval: aws.Int64(log["interval"].(int64)), S3BucketName: aws.String(log["bucket"].(string)), - S3BucketPrefix: aws.String(log["bucket"].(string)), } + + if log["bucket_prefix"] != "" { + accessLogs.S3BucketPrefix = aws.String(log["bucket_prefix"].(string)) + } + attrs.LoadBalancerAttributes.AccessLog = accessLogs } @@ -592,6 +595,20 @@ func resourceAwsElbHealthCheckHash(v interface{}) int { return hashcode.String(buf.String()) } +func resourceAwsElbAccessLogsHash(v interface{}) int { + var buf bytes.Buffer + m := v.(map[string]interface{}) + buf.WriteString(fmt.Sprintf("%t-", m["enabled"].(bool))) + buf.WriteString(fmt.Sprintf("%d-", m["interval"].(int))) + buf.WriteString(fmt.Sprintf("%s-", + strings.ToLower(m["bucket"].(string)))) + if v, ok := m["bucket_prefix"]; ok { + buf.WriteString(fmt.Sprintf("%s-", strings.ToLower(v.(string)))) + } + + return hashcode.String(buf.String()) +} + func resourceAwsElbListenerHash(v interface{}) int { var buf bytes.Buffer m := v.(map[string]interface{}) From e8f1f57ead9a4a97f2b3739e42534c16324763e4 Mon Sep 17 00:00:00 2001 From: Trevor Pounds Date: Fri, 30 Oct 2015 15:02:58 -0700 Subject: [PATCH 015/173] Fix int64 cast. --- builtin/providers/aws/resource_aws_elb.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/builtin/providers/aws/resource_aws_elb.go b/builtin/providers/aws/resource_aws_elb.go index e0c273f121..6bfb3f0f75 100644 --- a/builtin/providers/aws/resource_aws_elb.go +++ b/builtin/providers/aws/resource_aws_elb.go @@ -452,7 +452,7 @@ func resourceAwsElbUpdate(d *schema.ResourceData, meta interface{}) error { log := logs[0].(map[string]interface{}) accessLogs := &elb.AccessLog{ Enabled: aws.Bool(log["enabled"].(bool)), - EmitInterval: aws.Int64(log["interval"].(int64)), + EmitInterval: aws.Int64(int64(log["interval"].(int))), S3BucketName: aws.String(log["bucket"].(string)), } From 91b1d0c23d4bcbf356173b26c39f236e844cacb5 Mon Sep 17 00:00:00 2001 From: Trevor Pounds Date: Fri, 30 Oct 2015 15:03:38 -0700 Subject: [PATCH 016/173] Can only set access logs once per ELB. --- builtin/providers/aws/resource_aws_elb.go | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/builtin/providers/aws/resource_aws_elb.go b/builtin/providers/aws/resource_aws_elb.go index 6bfb3f0f75..620ae5ee01 100644 --- a/builtin/providers/aws/resource_aws_elb.go +++ b/builtin/providers/aws/resource_aws_elb.go @@ -445,10 +445,12 @@ func resourceAwsElbUpdate(d *schema.ResourceData, meta interface{}) error { IdleTimeout: aws.Int64(int64(d.Get("idle_timeout").(int))), }, }, - } + } logs := d.Get("access_logs").(*schema.Set).List() - if len(logs) > 0 { + if len(logs) > 1 { + return fmt.Errorf("Only one access logs config per ELB is supported") + } else if len(logs) == 1 { log := logs[0].(map[string]interface{}) accessLogs := &elb.AccessLog{ Enabled: aws.Bool(log["enabled"].(bool)), From 4e3d0b5f6c366b92a3ce671c54cfa2f4d52c4564 Mon Sep 17 00:00:00 2001 From: Trevor Pounds Date: Fri, 30 Oct 2015 15:45:43 -0700 Subject: [PATCH 017/173] Fix schema conversion. --- builtin/providers/aws/resource_aws_elb.go | 12 ++++++------ builtin/providers/aws/structure.go | 16 ++++++++++++++++ 2 files changed, 22 insertions(+), 6 deletions(-) diff --git a/builtin/providers/aws/resource_aws_elb.go b/builtin/providers/aws/resource_aws_elb.go index 620ae5ee01..8d64e0588c 100644 --- a/builtin/providers/aws/resource_aws_elb.go +++ b/builtin/providers/aws/resource_aws_elb.go @@ -330,13 +330,13 @@ func resourceAwsElbRead(d *schema.ResourceData, meta interface{}) error { d.Set("source_security_group", lb.SourceSecurityGroup.GroupName) } d.Set("subnets", lb.Subnets) - d.Set("idle_timeout", lbAttrs.ConnectionSettings.IdleTimeout) - d.Set("connection_draining", lbAttrs.ConnectionDraining.Enabled) - d.Set("connection_draining_timeout", lbAttrs.ConnectionDraining.Timeout) - d.Set("access_logs", lbAttrs.AccessLog) + d.Set("idle_timeout", lbAttrs.ConnectionSettings.IdleTimeout) + d.Set("connection_draining", lbAttrs.ConnectionDraining.Enabled) + d.Set("connection_draining_timeout", lbAttrs.ConnectionDraining.Timeout) + d.Set("access_logs", flattenAccessLog(lbAttrs.AccessLog)) - resp, err := elbconn.DescribeTags(&elb.DescribeTagsInput{ - LoadBalancerNames: []*string{lb.LoadBalancerName}, + resp, err := elbconn.DescribeTags(&elb.DescribeTagsInput{ + LoadBalancerNames: []*string{lb.LoadBalancerName}, }) var et []*elb.Tag diff --git a/builtin/providers/aws/structure.go b/builtin/providers/aws/structure.go index fd581c84a7..e7590001f9 100644 --- a/builtin/providers/aws/structure.go +++ b/builtin/providers/aws/structure.go @@ -234,6 +234,22 @@ func expandElastiCacheParameters(configured []interface{}) ([]*elasticache.Param return parameters, nil } +// Flattens an access log into something that flatmap.Flatten() can handle +func flattenAccessLog(log *elb.AccessLog) []map[string]interface{} { + result := make([]map[string]interface{}, 0, 1) + + if log != nil { + result = append(result, map[string]interface{}{ + "enabled": *log.Enabled, + "interval": *log.EmitInterval, + "bucket": *log.S3BucketName, + "bucket_prefix": *log.S3BucketPrefix, + }) + } + + return result +} + // Flattens a health check into something that flatmap.Flatten() // can handle func flattenHealthCheck(check *elb.HealthCheck) []map[string]interface{} { From 2e03a7ebff539b1989bf632c2be00354ce5567d9 Mon Sep 17 00:00:00 2001 From: clint shryock Date: Tue, 3 Nov 2015 16:30:18 -0600 Subject: [PATCH 018/173] go fmt after rebase --- builtin/providers/aws/resource_aws_elb.go | 122 +++++++++++----------- builtin/providers/aws/structure.go | 20 ++-- 2 files changed, 71 insertions(+), 71 deletions(-) diff --git a/builtin/providers/aws/resource_aws_elb.go b/builtin/providers/aws/resource_aws_elb.go index 8d64e0588c..3fa0b1ddbd 100644 --- a/builtin/providers/aws/resource_aws_elb.go +++ b/builtin/providers/aws/resource_aws_elb.go @@ -101,35 +101,35 @@ func resourceAwsElb() *schema.Resource { Default: 300, }, - "access_logs": &schema.Schema{ - Type: schema.TypeSet, - Optional: true, - Elem: &schema.Resource{ - Schema: map[string]*schema.Schema{ - "enabled": &schema.Schema{ - Type: schema.TypeBool, - Optional: true, - Default: true, - }, - "interval": &schema.Schema{ - Type: schema.TypeInt, - Optional: true, - Default: 60, - }, - "bucket": &schema.Schema{ - Type: schema.TypeString, - Required: true, - }, - "bucket_prefix": &schema.Schema{ - Type: schema.TypeString, - Optional: true, - }, - }, - }, - Set: resourceAwsElbAccessLogsHash, - }, + "access_logs": &schema.Schema{ + Type: schema.TypeSet, + Optional: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "enabled": &schema.Schema{ + Type: schema.TypeBool, + Optional: true, + Default: true, + }, + "interval": &schema.Schema{ + Type: schema.TypeInt, + Optional: true, + Default: 60, + }, + "bucket": &schema.Schema{ + Type: schema.TypeString, + Required: true, + }, + "bucket_prefix": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + }, + }, + }, + Set: resourceAwsElbAccessLogsHash, + }, - "listener": &schema.Schema{ + "listener": &schema.Schema{ Type: schema.TypeSet, Required: true, Elem: &schema.Resource{ @@ -330,13 +330,13 @@ func resourceAwsElbRead(d *schema.ResourceData, meta interface{}) error { d.Set("source_security_group", lb.SourceSecurityGroup.GroupName) } d.Set("subnets", lb.Subnets) - d.Set("idle_timeout", lbAttrs.ConnectionSettings.IdleTimeout) - d.Set("connection_draining", lbAttrs.ConnectionDraining.Enabled) - d.Set("connection_draining_timeout", lbAttrs.ConnectionDraining.Timeout) - d.Set("access_logs", flattenAccessLog(lbAttrs.AccessLog)) + d.Set("idle_timeout", lbAttrs.ConnectionSettings.IdleTimeout) + d.Set("connection_draining", lbAttrs.ConnectionDraining.Enabled) + d.Set("connection_draining_timeout", lbAttrs.ConnectionDraining.Timeout) + d.Set("access_logs", flattenAccessLog(lbAttrs.AccessLog)) - resp, err := elbconn.DescribeTags(&elb.DescribeTagsInput{ - LoadBalancerNames: []*string{lb.LoadBalancerName}, + resp, err := elbconn.DescribeTags(&elb.DescribeTagsInput{ + LoadBalancerNames: []*string{lb.LoadBalancerName}, }) var et []*elb.Tag @@ -434,7 +434,7 @@ func resourceAwsElbUpdate(d *schema.ResourceData, meta interface{}) error { d.SetPartial("instances") } - if d.HasChange("cross_zone_load_balancing") || d.HasChange("idle_timeout") || d.HasChange("access_logs") { + if d.HasChange("cross_zone_load_balancing") || d.HasChange("idle_timeout") || d.HasChange("access_logs") { attrs := elb.ModifyLoadBalancerAttributesInput{ LoadBalancerName: aws.String(d.Get("name").(string)), LoadBalancerAttributes: &elb.LoadBalancerAttributes{ @@ -445,25 +445,25 @@ func resourceAwsElbUpdate(d *schema.ResourceData, meta interface{}) error { IdleTimeout: aws.Int64(int64(d.Get("idle_timeout").(int))), }, }, - } + } - logs := d.Get("access_logs").(*schema.Set).List() - if len(logs) > 1 { - return fmt.Errorf("Only one access logs config per ELB is supported") - } else if len(logs) == 1 { - log := logs[0].(map[string]interface{}) - accessLogs := &elb.AccessLog{ - Enabled: aws.Bool(log["enabled"].(bool)), - EmitInterval: aws.Int64(int64(log["interval"].(int))), - S3BucketName: aws.String(log["bucket"].(string)), - } + logs := d.Get("access_logs").(*schema.Set).List() + if len(logs) > 1 { + return fmt.Errorf("Only one access logs config per ELB is supported") + } else if len(logs) == 1 { + log := logs[0].(map[string]interface{}) + accessLogs := &elb.AccessLog{ + Enabled: aws.Bool(log["enabled"].(bool)), + EmitInterval: aws.Int64(int64(log["interval"].(int))), + S3BucketName: aws.String(log["bucket"].(string)), + } - if log["bucket_prefix"] != "" { - accessLogs.S3BucketPrefix = aws.String(log["bucket_prefix"].(string)) - } + if log["bucket_prefix"] != "" { + accessLogs.S3BucketPrefix = aws.String(log["bucket_prefix"].(string)) + } - attrs.LoadBalancerAttributes.AccessLog = accessLogs - } + attrs.LoadBalancerAttributes.AccessLog = accessLogs + } _, err := elbconn.ModifyLoadBalancerAttributes(&attrs) if err != nil { @@ -598,17 +598,17 @@ func resourceAwsElbHealthCheckHash(v interface{}) int { } func resourceAwsElbAccessLogsHash(v interface{}) int { - var buf bytes.Buffer - m := v.(map[string]interface{}) - buf.WriteString(fmt.Sprintf("%t-", m["enabled"].(bool))) - buf.WriteString(fmt.Sprintf("%d-", m["interval"].(int))) - buf.WriteString(fmt.Sprintf("%s-", - strings.ToLower(m["bucket"].(string)))) - if v, ok := m["bucket_prefix"]; ok { - buf.WriteString(fmt.Sprintf("%s-", strings.ToLower(v.(string)))) - } + var buf bytes.Buffer + m := v.(map[string]interface{}) + buf.WriteString(fmt.Sprintf("%t-", m["enabled"].(bool))) + buf.WriteString(fmt.Sprintf("%d-", m["interval"].(int))) + buf.WriteString(fmt.Sprintf("%s-", + strings.ToLower(m["bucket"].(string)))) + if v, ok := m["bucket_prefix"]; ok { + buf.WriteString(fmt.Sprintf("%s-", strings.ToLower(v.(string)))) + } - return hashcode.String(buf.String()) + return hashcode.String(buf.String()) } func resourceAwsElbListenerHash(v interface{}) int { diff --git a/builtin/providers/aws/structure.go b/builtin/providers/aws/structure.go index e7590001f9..9a9260df74 100644 --- a/builtin/providers/aws/structure.go +++ b/builtin/providers/aws/structure.go @@ -236,18 +236,18 @@ func expandElastiCacheParameters(configured []interface{}) ([]*elasticache.Param // Flattens an access log into something that flatmap.Flatten() can handle func flattenAccessLog(log *elb.AccessLog) []map[string]interface{} { - result := make([]map[string]interface{}, 0, 1) + result := make([]map[string]interface{}, 0, 1) - if log != nil { - result = append(result, map[string]interface{}{ - "enabled": *log.Enabled, - "interval": *log.EmitInterval, - "bucket": *log.S3BucketName, - "bucket_prefix": *log.S3BucketPrefix, - }) - } + if log != nil { + result = append(result, map[string]interface{}{ + "enabled": *log.Enabled, + "interval": *log.EmitInterval, + "bucket": *log.S3BucketName, + "bucket_prefix": *log.S3BucketPrefix, + }) + } - return result + return result } // Flattens a health check into something that flatmap.Flatten() From 87dd5c5bd077f67631a0103ea8db3d16fb2c9055 Mon Sep 17 00:00:00 2001 From: Tomas Doran Date: Wed, 4 Nov 2015 04:23:13 -0800 Subject: [PATCH 019/173] Fix panic I see when upgrading to 0.6.6 Check if the policy is nil or not before type casting it --- builtin/providers/aws/resource_aws_sns_topic.go | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/builtin/providers/aws/resource_aws_sns_topic.go b/builtin/providers/aws/resource_aws_sns_topic.go index 6a1c46590f..26e98a9b10 100644 --- a/builtin/providers/aws/resource_aws_sns_topic.go +++ b/builtin/providers/aws/resource_aws_sns_topic.go @@ -47,7 +47,10 @@ func resourceAwsSnsTopic() *schema.Resource { ForceNew: false, Computed: true, StateFunc: func(v interface{}) string { - jsonb := []byte(v.(string)) + jsonb := []byte{} + if v != nil { + jsonb = []byte(v.(string)) + } buffer := new(bytes.Buffer) if err := json.Compact(buffer, jsonb); err != nil { log.Printf("[WARN] Error compacting JSON for Policy in SNS Topic") From 5f7254eb1a689c0c716700cfa08f45993d268af7 Mon Sep 17 00:00:00 2001 From: clint shryock Date: Wed, 4 Nov 2015 11:50:34 -0600 Subject: [PATCH 020/173] providers/aws: Add ELB Access Logs (continues #3708) - continues #3708 - adds some tests - other fixes I found along the way --- builtin/providers/aws/resource_aws_elb.go | 20 ++-- .../providers/aws/resource_aws_elb_test.go | 104 ++++++++++++++++++ builtin/providers/aws/structure.go | 23 +++- 3 files changed, 133 insertions(+), 14 deletions(-) diff --git a/builtin/providers/aws/resource_aws_elb.go b/builtin/providers/aws/resource_aws_elb.go index 3fa0b1ddbd..cc398c69b2 100644 --- a/builtin/providers/aws/resource_aws_elb.go +++ b/builtin/providers/aws/resource_aws_elb.go @@ -106,11 +106,6 @@ func resourceAwsElb() *schema.Resource { Optional: true, Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ - "enabled": &schema.Schema{ - Type: schema.TypeBool, - Optional: true, - Default: true, - }, "interval": &schema.Schema{ Type: schema.TypeInt, Optional: true, @@ -333,7 +328,11 @@ func resourceAwsElbRead(d *schema.ResourceData, meta interface{}) error { d.Set("idle_timeout", lbAttrs.ConnectionSettings.IdleTimeout) d.Set("connection_draining", lbAttrs.ConnectionDraining.Enabled) d.Set("connection_draining_timeout", lbAttrs.ConnectionDraining.Timeout) - d.Set("access_logs", flattenAccessLog(lbAttrs.AccessLog)) + if lbAttrs.AccessLog != nil { + if err := d.Set("access_logs", flattenAccessLog(lbAttrs.AccessLog)); err != nil { + log.Printf("[WARN] Error setting ELB Access Logs for (%s): %s", d.Id(), err) + } + } resp, err := elbconn.DescribeTags(&elb.DescribeTagsInput{ LoadBalancerNames: []*string{lb.LoadBalancerName}, @@ -453,7 +452,7 @@ func resourceAwsElbUpdate(d *schema.ResourceData, meta interface{}) error { } else if len(logs) == 1 { log := logs[0].(map[string]interface{}) accessLogs := &elb.AccessLog{ - Enabled: aws.Bool(log["enabled"].(bool)), + Enabled: aws.Bool(true), EmitInterval: aws.Int64(int64(log["interval"].(int))), S3BucketName: aws.String(log["bucket"].(string)), } @@ -463,8 +462,14 @@ func resourceAwsElbUpdate(d *schema.ResourceData, meta interface{}) error { } attrs.LoadBalancerAttributes.AccessLog = accessLogs + } else if len(logs) == 0 { + // disable access logs + attrs.LoadBalancerAttributes.AccessLog = &elb.AccessLog{ + Enabled: aws.Bool(false), + } } + log.Printf("[DEBUG] ELB Modify Load Balancer Attributes Request: %#v", attrs) _, err := elbconn.ModifyLoadBalancerAttributes(&attrs) if err != nil { return fmt.Errorf("Failure configuring ELB attributes: %s", err) @@ -600,7 +605,6 @@ func resourceAwsElbHealthCheckHash(v interface{}) int { func resourceAwsElbAccessLogsHash(v interface{}) int { var buf bytes.Buffer m := v.(map[string]interface{}) - buf.WriteString(fmt.Sprintf("%t-", m["enabled"].(bool))) buf.WriteString(fmt.Sprintf("%d-", m["interval"].(int))) buf.WriteString(fmt.Sprintf("%s-", strings.ToLower(m["bucket"].(string)))) diff --git a/builtin/providers/aws/resource_aws_elb_test.go b/builtin/providers/aws/resource_aws_elb_test.go index dadf4aba3c..242365de27 100644 --- a/builtin/providers/aws/resource_aws_elb_test.go +++ b/builtin/providers/aws/resource_aws_elb_test.go @@ -75,6 +75,52 @@ func TestAccAWSELB_fullCharacterRange(t *testing.T) { }) } +func TestAccAWSELB_AccessLogs(t *testing.T) { + var conf elb.LoadBalancerDescription + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSELBDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccAWSELBAccessLogs, + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSELBExists("aws_elb.foo", &conf), + resource.TestCheckResourceAttr( + "aws_elb.foo", "name", "FoobarTerraform-test123"), + ), + }, + + resource.TestStep{ + Config: testAccAWSELBAccessLogsOn, + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSELBExists("aws_elb.foo", &conf), + resource.TestCheckResourceAttr( + "aws_elb.foo", "name", "FoobarTerraform-test123"), + resource.TestCheckResourceAttr( + "aws_elb.foo", "access_logs.#", "1"), + resource.TestCheckResourceAttr( + "aws_elb.foo", "access_logs.1713209538.bucket", "terraform-access-logs-bucket"), + resource.TestCheckResourceAttr( + "aws_elb.foo", "access_logs.1713209538.interval", "5"), + ), + }, + + resource.TestStep{ + Config: testAccAWSELBAccessLogs, + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSELBExists("aws_elb.foo", &conf), + resource.TestCheckResourceAttr( + "aws_elb.foo", "name", "FoobarTerraform-test123"), + resource.TestCheckResourceAttr( + "aws_elb.foo", "access_logs.#", "0"), + ), + }, + }, + }) +} + func TestAccAWSELB_generatedName(t *testing.T) { var conf elb.LoadBalancerDescription generatedNameRegexp := regexp.MustCompile("^tf-lb-") @@ -650,6 +696,64 @@ resource "aws_elb" "foo" { } ` +const testAccAWSELBAccessLogs = ` +resource "aws_elb" "foo" { + name = "FoobarTerraform-test123" + availability_zones = ["us-west-2a", "us-west-2b", "us-west-2c"] + + listener { + instance_port = 8000 + instance_protocol = "http" + lb_port = 80 + lb_protocol = "http" + } +} +` +const testAccAWSELBAccessLogsOn = ` +# an S3 bucket configured for Access logs +# The 797873946194 is the AWS ID for us-west-2, so this test +# must be ran in us-west-2 +resource "aws_s3_bucket" "acceslogs_bucket" { + bucket = "terraform-access-logs-bucket" + acl = "private" + force_destroy = true + policy = < Date: Tue, 3 Nov 2015 23:15:02 -0800 Subject: [PATCH 021/173] Add website docs. --- .../source/docs/providers/aws/r/elb.html.markdown | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/website/source/docs/providers/aws/r/elb.html.markdown b/website/source/docs/providers/aws/r/elb.html.markdown index 824a5507f8..13cf517b91 100644 --- a/website/source/docs/providers/aws/r/elb.html.markdown +++ b/website/source/docs/providers/aws/r/elb.html.markdown @@ -18,6 +18,13 @@ resource "aws_elb" "bar" { name = "foobar-terraform-elb" availability_zones = ["us-west-2a", "us-west-2b", "us-west-2c"] + access_logs { + enabled = true + bucket = "foo" + bucket_prefix = "bar" + interval = 60 + } + listener { instance_port = 8000 instance_protocol = "http" @@ -58,6 +65,7 @@ resource "aws_elb" "bar" { The following arguments are supported: * `name` - (Optional) The name of the ELB. By default generated by terraform. +* `access_logs` - (Optional) An Access Logs block. Access Logs documented below. * `availability_zones` - (Required for an EC2-classic ELB) The AZ's to serve traffic in. * `security_groups` - (Optional) A list of security group IDs to assign to the ELB. * `subnets` - (Required for a VPC ELB) A list of subnet IDs to attach to the ELB. @@ -74,6 +82,13 @@ The following arguments are supported: Exactly one of `availability_zones` or `subnets` must be specified: this determines if the ELB exists in a VPC or in EC2-classic. +Access Logs support the following: + +* `bucket` - (Required) The S3 bucket name to store the logs in. +* `bucket_prefix` - (Optional) The S3 bucket prefix. Logs are stored in the root if not configured. +* `enabled` - (Optional) Whether capturing access logs is enabled. Default: true. +* `interval` - (Optional) The publishing interval in minutes. Default: 60 minutes. + Listeners support the following: * `instance_port` - (Required) The port on the instance to route to From e3be1d5f10b60620e83535dd31dd88f4d5d9ff0e Mon Sep 17 00:00:00 2001 From: clint shryock Date: Wed, 4 Nov 2015 14:15:40 -0600 Subject: [PATCH 022/173] update elb access logs docs --- website/source/docs/providers/aws/r/elb.html.markdown | 2 -- 1 file changed, 2 deletions(-) diff --git a/website/source/docs/providers/aws/r/elb.html.markdown b/website/source/docs/providers/aws/r/elb.html.markdown index 13cf517b91..024d71544e 100644 --- a/website/source/docs/providers/aws/r/elb.html.markdown +++ b/website/source/docs/providers/aws/r/elb.html.markdown @@ -19,7 +19,6 @@ resource "aws_elb" "bar" { availability_zones = ["us-west-2a", "us-west-2b", "us-west-2c"] access_logs { - enabled = true bucket = "foo" bucket_prefix = "bar" interval = 60 @@ -86,7 +85,6 @@ Access Logs support the following: * `bucket` - (Required) The S3 bucket name to store the logs in. * `bucket_prefix` - (Optional) The S3 bucket prefix. Logs are stored in the root if not configured. -* `enabled` - (Optional) Whether capturing access logs is enabled. Default: true. * `interval` - (Optional) The publishing interval in minutes. Default: 60 minutes. Listeners support the following: From ca2ea80af36ddd1907c0daa8bcd74e4985301145 Mon Sep 17 00:00:00 2001 From: stack72 Date: Thu, 5 Nov 2015 12:23:07 +0000 Subject: [PATCH 023/173] Making the changes to the snapshotting for Elasticache Redis as per @catsby's findings --- .../aws/resource_aws_elasticache_cluster.go | 37 +++++++++++-------- .../resource_aws_elasticache_cluster_test.go | 34 ++++++++++------- 2 files changed, 43 insertions(+), 28 deletions(-) diff --git a/builtin/providers/aws/resource_aws_elasticache_cluster.go b/builtin/providers/aws/resource_aws_elasticache_cluster.go index a2c312d353..69777a8663 100644 --- a/builtin/providers/aws/resource_aws_elasticache_cluster.go +++ b/builtin/providers/aws/resource_aws_elasticache_cluster.go @@ -205,12 +205,14 @@ func resourceAwsElasticacheClusterCreate(d *schema.ResourceData, meta interface{ req.CacheParameterGroupName = aws.String(v.(string)) } - if v, ok := d.GetOk("snapshot_retention_limit"); ok { - req.SnapshotRetentionLimit = aws.Int64(int64(v.(int))) - } + if !strings.Contains(d.Get("node_type").(string), "cache.t2") { + if v, ok := d.GetOk("snapshot_retention_limit"); ok { + req.SnapshotRetentionLimit = aws.Int64(int64(v.(int))) + } - if v, ok := d.GetOk("snapshot_window"); ok { - req.SnapshotWindow = aws.String(v.(string)) + if v, ok := d.GetOk("snapshot_window"); ok { + req.SnapshotWindow = aws.String(v.(string)) + } } if v, ok := d.GetOk("maintenance_window"); ok { @@ -287,10 +289,12 @@ func resourceAwsElasticacheClusterRead(d *schema.ResourceData, meta interface{}) d.Set("security_group_ids", c.SecurityGroups) d.Set("parameter_group_name", c.CacheParameterGroup) d.Set("maintenance_window", c.PreferredMaintenanceWindow) - log.Printf("[INFO] Found %s as the Snapshow Window", *c.SnapshotWindow) - d.Set("snapshot_window", c.SnapshotWindow) - log.Printf("[INFO] Found %d as the Snapshow Retention Limit", *c.SnapshotRetentionLimit) - d.Set("snapshot_retention_limit", c.SnapshotRetentionLimit) + if c.SnapshotWindow != nil { + d.Set("snapshot_window", c.SnapshotWindow) + } + if c.SnapshotRetentionLimit != nil { + d.Set("snapshot_retention_limit", c.SnapshotRetentionLimit) + } if c.NotificationConfiguration != nil { if *c.NotificationConfiguration.TopicStatus == "active" { d.Set("notification_topic_arn", c.NotificationConfiguration.TopicArn) @@ -373,13 +377,16 @@ func resourceAwsElasticacheClusterUpdate(d *schema.ResourceData, meta interface{ req.EngineVersion = aws.String(d.Get("engine_version").(string)) requestUpdate = true } + if !strings.Contains(d.Get("node_type").(string), "cache.t2") { + if d.HasChange("snapshot_window") { + req.SnapshotWindow = aws.String(d.Get("snapshot_window").(string)) + requestUpdate = true + } - if d.HasChange("snapshot_window") { - req.SnapshotWindow = aws.String(d.Get("snapshot_window").(string)) - } - - if d.HasChange("snapshot_retention_limit") { - req.SnapshotRetentionLimit = aws.Int64(int64(d.Get("snapshot_retention_limit").(int))) + if d.HasChange("snapshot_retention_limit") { + req.SnapshotRetentionLimit = aws.Int64(int64(d.Get("snapshot_retention_limit").(int))) + requestUpdate = true + } } if d.HasChange("num_cache_nodes") { diff --git a/builtin/providers/aws/resource_aws_elasticache_cluster_test.go b/builtin/providers/aws/resource_aws_elasticache_cluster_test.go index 666be2c8ab..78e28763dc 100644 --- a/builtin/providers/aws/resource_aws_elasticache_cluster_test.go +++ b/builtin/providers/aws/resource_aws_elasticache_cluster_test.go @@ -35,13 +35,17 @@ func TestAccAWSElasticacheCluster_basic(t *testing.T) { func TestAccAWSElasticacheCluster_snapshots(t *testing.T) { var ec elasticache.CacheCluster + + ri := genRandInt() + config := fmt.Sprintf(testAccAWSElasticacheClusterConfig_snapshots, ri) + resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, Providers: testAccProviders, CheckDestroy: testAccCheckAWSElasticacheClusterDestroy, Steps: []resource.TestStep{ resource.TestStep{ - Config: testAccAWSElasticacheClusterConfig_snapshots, + Config: config, Check: resource.ComposeTestCheckFunc( testAccCheckAWSElasticacheSecurityGroupExists("aws_elasticache_security_group.bar"), testAccCheckAWSElasticacheClusterExists("aws_elasticache_cluster.bar", &ec), @@ -56,13 +60,18 @@ func TestAccAWSElasticacheCluster_snapshots(t *testing.T) { } func TestAccAWSElasticacheCluster_snapshotsWithUpdates(t *testing.T) { var ec elasticache.CacheCluster + + ri := genRandInt() + preConfig := fmt.Sprintf(testAccAWSElasticacheClusterConfig_snapshots, ri) + postConfig := fmt.Sprintf(testAccAWSElasticacheClusterConfig_snapshotsUpdated, ri) + resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, Providers: testAccProviders, CheckDestroy: testAccCheckAWSElasticacheClusterDestroy, Steps: []resource.TestStep{ resource.TestStep{ - Config: testAccAWSElasticacheClusterConfig_snapshots, + Config: preConfig, Check: resource.ComposeTestCheckFunc( testAccCheckAWSElasticacheSecurityGroupExists("aws_elasticache_security_group.bar"), testAccCheckAWSElasticacheClusterExists("aws_elasticache_cluster.bar", &ec), @@ -74,7 +83,7 @@ func TestAccAWSElasticacheCluster_snapshotsWithUpdates(t *testing.T) { }, resource.TestStep{ - Config: testAccAWSElasticacheClusterConfig_snapshotsUpdated, + Config: postConfig, Check: resource.ComposeTestCheckFunc( testAccCheckAWSElasticacheSecurityGroupExists("aws_elasticache_security_group.bar"), testAccCheckAWSElasticacheClusterExists("aws_elasticache_cluster.bar", &ec), @@ -204,17 +213,15 @@ resource "aws_elasticache_cluster" "bar" { port = 11211 parameter_group_name = "default.memcached1.4" security_group_names = ["${aws_elasticache_security_group.bar.name}"] - snapshot_window = "05:00-09:00" - snapshot_retention_limit = 3 } `, genRandInt(), genRandInt(), genRandInt()) -var testAccAWSElasticacheClusterConfig_snapshots = fmt.Sprintf(` +var testAccAWSElasticacheClusterConfig_snapshots = ` provider "aws" { region = "us-east-1" } resource "aws_security_group" "bar" { - name = "tf-test-security-group-%03d" + name = "tf-test-security-group" description = "tf-test-security-group-descr" ingress { from_port = -1 @@ -225,7 +232,7 @@ resource "aws_security_group" "bar" { } resource "aws_elasticache_security_group" "bar" { - name = "tf-test-security-group-%03d" + name = "tf-test-security-group" description = "tf-test-security-group-descr" security_group_names = ["${aws_security_group.bar.name}"] } @@ -241,14 +248,14 @@ resource "aws_elasticache_cluster" "bar" { snapshot_window = "05:00-09:00" snapshot_retention_limit = 3 } -`, genRandInt(), genRandInt(), genRandInt()) +` -var testAccAWSElasticacheClusterConfig_snapshotsUpdated = fmt.Sprintf(` +var testAccAWSElasticacheClusterConfig_snapshotsUpdated = ` provider "aws" { region = "us-east-1" } resource "aws_security_group" "bar" { - name = "tf-test-security-group-%03d" + name = "tf-test-security-group" description = "tf-test-security-group-descr" ingress { from_port = -1 @@ -259,7 +266,7 @@ resource "aws_security_group" "bar" { } resource "aws_elasticache_security_group" "bar" { - name = "tf-test-security-group-%03d" + name = "tf-test-security-group" description = "tf-test-security-group-descr" security_group_names = ["${aws_security_group.bar.name}"] } @@ -274,8 +281,9 @@ resource "aws_elasticache_cluster" "bar" { security_group_names = ["${aws_elasticache_security_group.bar.name}"] snapshot_window = "07:00-09:00" snapshot_retention_limit = 7 + apply_immediately = true } -`, genRandInt(), genRandInt(), genRandInt()) +` var testAccAWSElasticacheClusterInVPCConfig = fmt.Sprintf(` resource "aws_vpc" "foo" { From 3a63b48f97d1ff917fe125eef56e1c76b3c9383f Mon Sep 17 00:00:00 2001 From: Kirill Shirinkin Date: Thu, 5 Nov 2015 19:23:05 +0100 Subject: [PATCH 024/173] provider/openstack: fixed_ips implementation for ports --- ...ce_openstack_networking_network_v2_test.go | 5 ++- .../resource_openstack_networking_port_v2.go | 37 +++++++++++++++++++ ...ource_openstack_networking_port_v2_test.go | 4 ++ .../r/networking_network_v2.html.markdown | 5 ++- .../r/networking_port_v2.html.markdown | 11 ++++++ 5 files changed, 60 insertions(+), 2 deletions(-) diff --git a/builtin/providers/openstack/resource_openstack_networking_network_v2_test.go b/builtin/providers/openstack/resource_openstack_networking_network_v2_test.go index faa007e32f..6efdf3daf8 100644 --- a/builtin/providers/openstack/resource_openstack_networking_network_v2_test.go +++ b/builtin/providers/openstack/resource_openstack_networking_network_v2_test.go @@ -150,7 +150,10 @@ func TestAccNetworkingV2Network_fullstack(t *testing.T) { admin_state_up = "true" security_groups = ["${openstack_compute_secgroup_v2.foo.id}"] - depends_on = ["openstack_networking_subnet_v2.foo"] + fixed_ips { + "subnet_id" = "008ba151-0b8c-4a67-98b5-0d2b87666062" + "ip_address" = "172.24.4.2" + } } resource "openstack_compute_instance_v2" "foo" { diff --git a/builtin/providers/openstack/resource_openstack_networking_port_v2.go b/builtin/providers/openstack/resource_openstack_networking_port_v2.go index 701e42c05c..318f0097bf 100644 --- a/builtin/providers/openstack/resource_openstack_networking_port_v2.go +++ b/builtin/providers/openstack/resource_openstack_networking_port_v2.go @@ -78,6 +78,23 @@ func resourceNetworkingPortV2() *schema.Resource { ForceNew: true, Computed: true, }, + "fixed_ips": &schema.Schema{ + Type: schema.TypeList, + Optional: true, + ForceNew: false, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "subnet_id": &schema.Schema{ + Type: schema.TypeString, + Required: true, + }, + "ip_address": &schema.Schema{ + Type: schema.TypeString, + Required: true, + }, + }, + }, + }, }, } } @@ -98,6 +115,7 @@ func resourceNetworkingPortV2Create(d *schema.ResourceData, meta interface{}) er DeviceOwner: d.Get("device_owner").(string), SecurityGroups: resourcePortSecurityGroupsV2(d), DeviceID: d.Get("device_id").(string), + FixedIPs: resourcePortFixedIpsV2(d), } log.Printf("[DEBUG] Create Options: %#v", createOpts) @@ -146,6 +164,7 @@ func resourceNetworkingPortV2Read(d *schema.ResourceData, meta interface{}) erro d.Set("device_owner", p.DeviceOwner) d.Set("security_groups", p.SecurityGroups) d.Set("device_id", p.DeviceID) + d.Set("fixed_ips", p.FixedIPs) return nil } @@ -179,6 +198,10 @@ func resourceNetworkingPortV2Update(d *schema.ResourceData, meta interface{}) er updateOpts.DeviceID = d.Get("device_id").(string) } + if d.HasChange("fixed_ips") { + updateOpts.FixedIPs = resourcePortFixedIpsV2(d) + } + log.Printf("[DEBUG] Updating Port %s with options: %+v", d.Id(), updateOpts) _, err = ports.Update(networkingClient, d.Id(), updateOpts).Extract() @@ -223,6 +246,20 @@ func resourcePortSecurityGroupsV2(d *schema.ResourceData) []string { return groups } +func resourcePortFixedIpsV2(d *schema.ResourceData) []ports.IP { + rawIP := d.Get("fixed_ips").([]interface{}) + ip := make([]ports.IP, len(rawIP)) + for i, raw := range rawIP { + rawMap := raw.(map[string]interface{}) + ip[i] = ports.IP{ + SubnetID: rawMap["subnet_id"].(string), + IPAddress: rawMap["ip_address"].(string), + } + } + + return ip +} + func resourcePortAdminStateUpV2(d *schema.ResourceData) *bool { value := false diff --git a/builtin/providers/openstack/resource_openstack_networking_port_v2_test.go b/builtin/providers/openstack/resource_openstack_networking_port_v2_test.go index edeb619011..1e2a98a77e 100644 --- a/builtin/providers/openstack/resource_openstack_networking_port_v2_test.go +++ b/builtin/providers/openstack/resource_openstack_networking_port_v2_test.go @@ -30,6 +30,10 @@ func TestAccNetworkingV2Port_basic(t *testing.T) { name = "port_1" network_id = "${openstack_networking_network_v2.foo.id}" admin_state_up = "true" + fixed_ips { + ip_address = "192.168.0.0" + subnet_id = "008ba151-0b8c-4a67-98b5-0d2b87666062" + } }`, region, region) resource.Test(t, resource.TestCase{ diff --git a/website/source/docs/providers/openstack/r/networking_network_v2.html.markdown b/website/source/docs/providers/openstack/r/networking_network_v2.html.markdown index 9a9eab935b..ce4f46db8f 100644 --- a/website/source/docs/providers/openstack/r/networking_network_v2.html.markdown +++ b/website/source/docs/providers/openstack/r/networking_network_v2.html.markdown @@ -42,7 +42,10 @@ resource "openstack_networking_port_v2" "port_1" { admin_state_up = "true" security_groups = ["${openstack_compute_secgroup_v2.secgroup_1.id}"] - depends_on = ["openstack_networking_subnet_v2.subnet_1"] + fixed_ips { + "subnet_id" = "008ba151-0b8c-4a67-98b5-0d2b87666062" + "ip_address" = "172.24.4.2" + } } resource "openstack_compute_instance_v2" "instance_1" { diff --git a/website/source/docs/providers/openstack/r/networking_port_v2.html.markdown b/website/source/docs/providers/openstack/r/networking_port_v2.html.markdown index d10130a56f..df1e1c914c 100644 --- a/website/source/docs/providers/openstack/r/networking_port_v2.html.markdown +++ b/website/source/docs/providers/openstack/r/networking_port_v2.html.markdown @@ -60,6 +60,17 @@ The following arguments are supported: * `device_id` - (Optional) The ID of the device attached to the port. Changing this creates a new port. +* `fixed_ips` - (Optional) An array of desired IPs for this port. + + +The `fixed_ips` block supports: + +* `subnet_id` - (Required) Subnet in which to allocate IP address for +this port. + +* `ip_address` - (Required) IP address desired in the subnet for this +port. + ## Attributes Reference The following attributes are exported: From 274781224ee8cb1b4b8c9f6dffa299201680fcc8 Mon Sep 17 00:00:00 2001 From: clint shryock Date: Thu, 5 Nov 2015 15:25:04 -0600 Subject: [PATCH 025/173] provider/aws: fix panic with SNS topic policy if omitted --- builtin/providers/aws/resource_aws_sns_topic.go | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/builtin/providers/aws/resource_aws_sns_topic.go b/builtin/providers/aws/resource_aws_sns_topic.go index 26e98a9b10..b0d6fecf56 100644 --- a/builtin/providers/aws/resource_aws_sns_topic.go +++ b/builtin/providers/aws/resource_aws_sns_topic.go @@ -44,13 +44,12 @@ func resourceAwsSnsTopic() *schema.Resource { "policy": &schema.Schema{ Type: schema.TypeString, Optional: true, - ForceNew: false, Computed: true, StateFunc: func(v interface{}) string { - jsonb := []byte{} - if v != nil { - jsonb = []byte(v.(string)) + if v == nil { + return "" } + jsonb := []byte{} buffer := new(bytes.Buffer) if err := json.Compact(buffer, jsonb); err != nil { log.Printf("[WARN] Error compacting JSON for Policy in SNS Topic") From 9390674a05edf53cf2e08084f0f602b323788191 Mon Sep 17 00:00:00 2001 From: clint shryock Date: Thu, 5 Nov 2015 16:43:49 -0600 Subject: [PATCH 026/173] providers/aws: Provide source security group id for ELBs --- builtin/providers/aws/resource_aws_elb.go | 67 +++++++++++++++++++++++ 1 file changed, 67 insertions(+) diff --git a/builtin/providers/aws/resource_aws_elb.go b/builtin/providers/aws/resource_aws_elb.go index 9955c7cf0a..6be03b112b 100644 --- a/builtin/providers/aws/resource_aws_elb.go +++ b/builtin/providers/aws/resource_aws_elb.go @@ -9,6 +9,7 @@ import ( "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/aws/awserr" + "github.com/aws/aws-sdk-go/service/ec2" "github.com/aws/aws-sdk-go/service/elb" "github.com/hashicorp/terraform/helper/hashcode" "github.com/hashicorp/terraform/helper/resource" @@ -74,6 +75,11 @@ func resourceAwsElb() *schema.Resource { Computed: true, }, + "source_security_group_id": &schema.Schema{ + Type: schema.TypeString, + Computed: true, + }, + "subnets": &schema.Schema{ Type: schema.TypeSet, Elem: &schema.Schema{Type: schema.TypeString}, @@ -300,6 +306,18 @@ func resourceAwsElbRead(d *schema.ResourceData, meta interface{}) error { d.Set("security_groups", lb.SecurityGroups) if lb.SourceSecurityGroup != nil { d.Set("source_security_group", lb.SourceSecurityGroup.GroupName) + + // Manually look up the ELB Security Group ID, since it's not provided + var elbVpc string + if lb.VPCId != nil { + elbVpc = *lb.VPCId + } + sgId, err := sourceSGIdByName(meta, *lb.SourceSecurityGroup.GroupName, elbVpc) + if err != nil { + log.Printf("[WARN] Error looking up ELB Security Group ID: %s", err) + } else { + d.Set("source_security_group_id", sgId) + } } d.Set("subnets", lb.Subnets) d.Set("idle_timeout", lbAttrs.ConnectionSettings.IdleTimeout) @@ -594,3 +612,52 @@ func validateElbName(v interface{}, k string) (ws []string, errors []error) { return } + +func sourceSGIdByName(meta interface{}, sg, vpcId string) (string, error) { + conn := meta.(*AWSClient).ec2conn + var filters []*ec2.Filter + var sgFilterName, sgFilterVPCID *ec2.Filter + sgFilterName = &ec2.Filter{ + Name: aws.String("group-name"), + Values: []*string{aws.String(sg)}, + } + + if vpcId != "" { + sgFilterVPCID = &ec2.Filter{ + Name: aws.String("vpc-id"), + Values: []*string{aws.String(vpcId)}, + } + } + + filters = append(filters, sgFilterName) + + if sgFilterVPCID != nil { + filters = append(filters, sgFilterVPCID) + } + + req := &ec2.DescribeSecurityGroupsInput{ + Filters: filters, + } + resp, err := conn.DescribeSecurityGroups(req) + if err != nil { + if ec2err, ok := err.(awserr.Error); ok { + if ec2err.Code() == "InvalidSecurityGroupID.NotFound" || + ec2err.Code() == "InvalidGroup.NotFound" { + resp = nil + err = nil + } + } + + if err != nil { + log.Printf("Error on ELB SG look up: %s", err) + return "", err + } + } + + if resp == nil || len(resp.SecurityGroups) == 0 { + return "", fmt.Errorf("No security groups found for name %s and vpc id %s", sg, vpcId) + } + + group := resp.SecurityGroups[0] + return *group.GroupId, nil +} From d5e6929118c3291081a4ba282e8d3f9adab933d7 Mon Sep 17 00:00:00 2001 From: Kirill Shirinkin Date: Fri, 6 Nov 2015 11:10:44 +0100 Subject: [PATCH 027/173] Fix tests --- ...urce_openstack_networking_network_v2_test.go | 4 ++-- ...esource_openstack_networking_port_v2_test.go | 17 ++++++++++++++--- 2 files changed, 16 insertions(+), 5 deletions(-) diff --git a/builtin/providers/openstack/resource_openstack_networking_network_v2_test.go b/builtin/providers/openstack/resource_openstack_networking_network_v2_test.go index 6efdf3daf8..32a0c96bea 100644 --- a/builtin/providers/openstack/resource_openstack_networking_network_v2_test.go +++ b/builtin/providers/openstack/resource_openstack_networking_network_v2_test.go @@ -151,8 +151,8 @@ func TestAccNetworkingV2Network_fullstack(t *testing.T) { security_groups = ["${openstack_compute_secgroup_v2.foo.id}"] fixed_ips { - "subnet_id" = "008ba151-0b8c-4a67-98b5-0d2b87666062" - "ip_address" = "172.24.4.2" + "subnet_id" = "${openstack_networking_subnet_v2.foo.id}" + "ip_address" = "192.168.199.23" } } diff --git a/builtin/providers/openstack/resource_openstack_networking_port_v2_test.go b/builtin/providers/openstack/resource_openstack_networking_port_v2_test.go index 1e2a98a77e..c1e100d4f1 100644 --- a/builtin/providers/openstack/resource_openstack_networking_port_v2_test.go +++ b/builtin/providers/openstack/resource_openstack_networking_port_v2_test.go @@ -10,6 +10,7 @@ import ( "github.com/rackspace/gophercloud/openstack/networking/v2/networks" "github.com/rackspace/gophercloud/openstack/networking/v2/ports" + "github.com/rackspace/gophercloud/openstack/networking/v2/subnets" ) func TestAccNetworkingV2Port_basic(t *testing.T) { @@ -17,6 +18,7 @@ func TestAccNetworkingV2Port_basic(t *testing.T) { var network networks.Network var port ports.Port + var subnet subnets.Subnet var testAccNetworkingV2Port_basic = fmt.Sprintf(` resource "openstack_networking_network_v2" "foo" { @@ -25,16 +27,24 @@ func TestAccNetworkingV2Port_basic(t *testing.T) { admin_state_up = "true" } + resource "openstack_networking_subnet_v2" "foo" { + region = "%s" + name = "subnet_1" + network_id = "${openstack_networking_network_v2.foo.id}" + cidr = "192.168.199.0/24" + ip_version = 4 + } + resource "openstack_networking_port_v2" "foo" { region = "%s" name = "port_1" network_id = "${openstack_networking_network_v2.foo.id}" admin_state_up = "true" fixed_ips { - ip_address = "192.168.0.0" - subnet_id = "008ba151-0b8c-4a67-98b5-0d2b87666062" + subnet_id = "${openstack_networking_subnet_v2.foo.id}" + ip_address = "192.168.199.23" } - }`, region, region) + }`, region, region, region) resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, @@ -44,6 +54,7 @@ func TestAccNetworkingV2Port_basic(t *testing.T) { resource.TestStep{ Config: testAccNetworkingV2Port_basic, Check: resource.ComposeTestCheckFunc( + testAccCheckNetworkingV2SubnetExists(t, "openstack_networking_subnet_v2.foo", &subnet), testAccCheckNetworkingV2NetworkExists(t, "openstack_networking_network_v2.foo", &network), testAccCheckNetworkingV2PortExists(t, "openstack_networking_port_v2.foo", &port), ), From 350f91ec063b0057ea7fd37ef87227ca6225ad3a Mon Sep 17 00:00:00 2001 From: stack72 Date: Fri, 6 Nov 2015 11:16:51 +0000 Subject: [PATCH 028/173] Removing the instance_type check in the ElastiCache cluster creation. We now allow the error to bubble up to the userr when the wrong instance type is used. The limitation for t2 instance types now allowing snapshotting is also now documented --- .../aws/resource_aws_elasticache_cluster.go | 37 ++++++++----------- .../resource_aws_elasticache_cluster_test.go | 35 +++--------------- .../aws/r/elasticache_cluster.html.markdown | 1 + 3 files changed, 21 insertions(+), 52 deletions(-) diff --git a/builtin/providers/aws/resource_aws_elasticache_cluster.go b/builtin/providers/aws/resource_aws_elasticache_cluster.go index 69777a8663..6f178b71e3 100644 --- a/builtin/providers/aws/resource_aws_elasticache_cluster.go +++ b/builtin/providers/aws/resource_aws_elasticache_cluster.go @@ -205,14 +205,12 @@ func resourceAwsElasticacheClusterCreate(d *schema.ResourceData, meta interface{ req.CacheParameterGroupName = aws.String(v.(string)) } - if !strings.Contains(d.Get("node_type").(string), "cache.t2") { - if v, ok := d.GetOk("snapshot_retention_limit"); ok { - req.SnapshotRetentionLimit = aws.Int64(int64(v.(int))) - } + if v, ok := d.GetOk("snapshot_retention_limit"); ok { + req.SnapshotRetentionLimit = aws.Int64(int64(v.(int))) + } - if v, ok := d.GetOk("snapshot_window"); ok { - req.SnapshotWindow = aws.String(v.(string)) - } + if v, ok := d.GetOk("snapshot_window"); ok { + req.SnapshotWindow = aws.String(v.(string)) } if v, ok := d.GetOk("maintenance_window"); ok { @@ -289,12 +287,8 @@ func resourceAwsElasticacheClusterRead(d *schema.ResourceData, meta interface{}) d.Set("security_group_ids", c.SecurityGroups) d.Set("parameter_group_name", c.CacheParameterGroup) d.Set("maintenance_window", c.PreferredMaintenanceWindow) - if c.SnapshotWindow != nil { - d.Set("snapshot_window", c.SnapshotWindow) - } - if c.SnapshotRetentionLimit != nil { - d.Set("snapshot_retention_limit", c.SnapshotRetentionLimit) - } + d.Set("snapshot_window", c.SnapshotWindow) + d.Set("snapshot_retention_limit", c.SnapshotRetentionLimit) if c.NotificationConfiguration != nil { if *c.NotificationConfiguration.TopicStatus == "active" { d.Set("notification_topic_arn", c.NotificationConfiguration.TopicArn) @@ -377,16 +371,15 @@ func resourceAwsElasticacheClusterUpdate(d *schema.ResourceData, meta interface{ req.EngineVersion = aws.String(d.Get("engine_version").(string)) requestUpdate = true } - if !strings.Contains(d.Get("node_type").(string), "cache.t2") { - if d.HasChange("snapshot_window") { - req.SnapshotWindow = aws.String(d.Get("snapshot_window").(string)) - requestUpdate = true - } - if d.HasChange("snapshot_retention_limit") { - req.SnapshotRetentionLimit = aws.Int64(int64(d.Get("snapshot_retention_limit").(int))) - requestUpdate = true - } + if d.HasChange("snapshot_window") { + req.SnapshotWindow = aws.String(d.Get("snapshot_window").(string)) + requestUpdate = true + } + + if d.HasChange("snapshot_retention_limit") { + req.SnapshotRetentionLimit = aws.Int64(int64(d.Get("snapshot_retention_limit").(int))) + requestUpdate = true } if d.HasChange("num_cache_nodes") { diff --git a/builtin/providers/aws/resource_aws_elasticache_cluster_test.go b/builtin/providers/aws/resource_aws_elasticache_cluster_test.go index 78e28763dc..0620ef47ba 100644 --- a/builtin/providers/aws/resource_aws_elasticache_cluster_test.go +++ b/builtin/providers/aws/resource_aws_elasticache_cluster_test.go @@ -33,37 +33,12 @@ func TestAccAWSElasticacheCluster_basic(t *testing.T) { }) } -func TestAccAWSElasticacheCluster_snapshots(t *testing.T) { - var ec elasticache.CacheCluster - - ri := genRandInt() - config := fmt.Sprintf(testAccAWSElasticacheClusterConfig_snapshots, ri) - - resource.Test(t, resource.TestCase{ - PreCheck: func() { testAccPreCheck(t) }, - Providers: testAccProviders, - CheckDestroy: testAccCheckAWSElasticacheClusterDestroy, - Steps: []resource.TestStep{ - resource.TestStep{ - Config: config, - Check: resource.ComposeTestCheckFunc( - testAccCheckAWSElasticacheSecurityGroupExists("aws_elasticache_security_group.bar"), - testAccCheckAWSElasticacheClusterExists("aws_elasticache_cluster.bar", &ec), - resource.TestCheckResourceAttr( - "aws_elasticache_cluster.bar", "snapshot_window", "05:00-09:00"), - resource.TestCheckResourceAttr( - "aws_elasticache_cluster.bar", "snapshot_retention_limit", "3"), - ), - }, - }, - }) -} func TestAccAWSElasticacheCluster_snapshotsWithUpdates(t *testing.T) { var ec elasticache.CacheCluster ri := genRandInt() - preConfig := fmt.Sprintf(testAccAWSElasticacheClusterConfig_snapshots, ri) - postConfig := fmt.Sprintf(testAccAWSElasticacheClusterConfig_snapshotsUpdated, ri) + preConfig := fmt.Sprintf(testAccAWSElasticacheClusterConfig_snapshots, ri, ri, ri) + postConfig := fmt.Sprintf(testAccAWSElasticacheClusterConfig_snapshotsUpdated, ri, ri, ri) resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, @@ -221,7 +196,7 @@ provider "aws" { region = "us-east-1" } resource "aws_security_group" "bar" { - name = "tf-test-security-group" + name = "tf-test-security-group-%03d" description = "tf-test-security-group-descr" ingress { from_port = -1 @@ -232,7 +207,7 @@ resource "aws_security_group" "bar" { } resource "aws_elasticache_security_group" "bar" { - name = "tf-test-security-group" + name = "tf-test-security-group-%03d" description = "tf-test-security-group-descr" security_group_names = ["${aws_security_group.bar.name}"] } @@ -240,7 +215,7 @@ resource "aws_elasticache_security_group" "bar" { resource "aws_elasticache_cluster" "bar" { cluster_id = "tf-test-%03d" engine = "redis" - node_type = "cache.m1.small" + node_type = "cache.t2.small" num_cache_nodes = 1 port = 6379 parameter_group_name = "default.redis2.8" diff --git a/website/source/docs/providers/aws/r/elasticache_cluster.html.markdown b/website/source/docs/providers/aws/r/elasticache_cluster.html.markdown index 4a4cb4d76e..e39d6172a7 100644 --- a/website/source/docs/providers/aws/r/elasticache_cluster.html.markdown +++ b/website/source/docs/providers/aws/r/elasticache_cluster.html.markdown @@ -88,6 +88,7 @@ SNS topic to send ElastiCache notifications to. Example: * `tags` - (Optional) A mapping of tags to assign to the resource. +~> **NOTE:** Snapshotting functionality is not compatible with t2 instance types. ## Attributes Reference From e8a411ce5f1b21211080eb5a2abe22111b5a4801 Mon Sep 17 00:00:00 2001 From: stack72 Date: Fri, 6 Nov 2015 16:10:21 +0000 Subject: [PATCH 029/173] Fixing the RDS Cluster Instance docs to show that either an instance must be set as public OR have a db_subnet_group_name set --- .../docs/providers/aws/r/rds_cluster_instance.html.markdown | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/website/source/docs/providers/aws/r/rds_cluster_instance.html.markdown b/website/source/docs/providers/aws/r/rds_cluster_instance.html.markdown index 782339a343..0d64a3f876 100644 --- a/website/source/docs/providers/aws/r/rds_cluster_instance.html.markdown +++ b/website/source/docs/providers/aws/r/rds_cluster_instance.html.markdown @@ -64,6 +64,10 @@ and memory, see [Scaling Aurora DB Instances][4]. Aurora currently Default `false`. See the documentation on [Creating DB Instances][6] for more details on controlling this property. +* `db_subnet_group_name` - (Optional) A DB subnet group to associate with this DB instance. + +~> **NOTE:** `db_subnet_group_name` is a required field when you are trying to create a private instance (`publicly_accessible` = false) + ## Attributes Reference The following attributes are exported: From c905bfef226ba9b1bdd5f6fb67fbbc8fdfd19497 Mon Sep 17 00:00:00 2001 From: clint shryock Date: Fri, 6 Nov 2015 11:18:57 -0600 Subject: [PATCH 030/173] Test source_security_group_id for ELBs --- builtin/providers/aws/resource_aws_elb_test.go | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/builtin/providers/aws/resource_aws_elb_test.go b/builtin/providers/aws/resource_aws_elb_test.go index dadf4aba3c..9d010d8462 100644 --- a/builtin/providers/aws/resource_aws_elb_test.go +++ b/builtin/providers/aws/resource_aws_elb_test.go @@ -611,6 +611,15 @@ func testAccCheckAWSELBExists(n string, res *elb.LoadBalancerDescription) resour *res = *describe.LoadBalancerDescriptions[0] + // Confirm source_security_group_id for ELBs in a VPC + // See https://github.com/hashicorp/terraform/pull/3780 + if res.VPCId != nil { + sgid := rs.Primary.Attributes["source_security_group_id"] + if sgid == "" { + return fmt.Errorf("Expected to find source_security_group_id for ELB, but was empty") + } + } + return nil } } From 83ae6fe410361e16c69f9384ddd94e49271610a5 Mon Sep 17 00:00:00 2001 From: clint shryock Date: Fri, 6 Nov 2015 11:20:30 -0600 Subject: [PATCH 031/173] document source_security_group_id for ELB --- website/source/docs/providers/aws/r/elb.html.markdown | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/website/source/docs/providers/aws/r/elb.html.markdown b/website/source/docs/providers/aws/r/elb.html.markdown index 824a5507f8..8d51449329 100644 --- a/website/source/docs/providers/aws/r/elb.html.markdown +++ b/website/source/docs/providers/aws/r/elb.html.markdown @@ -100,5 +100,8 @@ The following attributes are exported: * `instances` - The list of instances in the ELB * `source_security_group` - The name of the security group that you can use as part of your inbound rules for your load balancer's back-end application - instances. + instances. Use this for Classic or Default VPC only. +* `source_security_group_id` - The ID of the security group that you can use as + part of your inbound rules for your load balancer's back-end application + instances. Only available on ELBs launch in a VPC. * `zone_id` - The canonical hosted zone ID of the ELB (to be used in a Route 53 Alias record) From 7675246cccfeb9d14825cd4ae71a5caf67dae691 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Fri, 6 Nov 2015 10:22:20 -0700 Subject: [PATCH 032/173] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8c8fb36f77..5215c84e3c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -35,6 +35,7 @@ IMPROVEMENTS: * 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] + * provider/openstack: Configure Fixed IPs through ports [GH-3772] BUG FIXES: From 4c1083c9c5ff7e81093300436a2942db6d827a6f Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Fri, 6 Nov 2015 17:27:18 +0000 Subject: [PATCH 033/173] provider/openstack: fix test formatting --- ...ce_openstack_networking_network_v2_test.go | 77 +++++++++---------- ...ource_openstack_networking_port_v2_test.go | 18 ++--- 2 files changed, 47 insertions(+), 48 deletions(-) diff --git a/builtin/providers/openstack/resource_openstack_networking_network_v2_test.go b/builtin/providers/openstack/resource_openstack_networking_network_v2_test.go index 32a0c96bea..0ae8422933 100644 --- a/builtin/providers/openstack/resource_openstack_networking_network_v2_test.go +++ b/builtin/providers/openstack/resource_openstack_networking_network_v2_test.go @@ -117,54 +117,53 @@ func TestAccNetworkingV2Network_fullstack(t *testing.T) { var subnet subnets.Subnet var testAccNetworkingV2Network_fullstack = fmt.Sprintf(` - resource "openstack_networking_network_v2" "foo" { - region = "%s" - name = "network_1" - admin_state_up = "true" - } + resource "openstack_networking_network_v2" "foo" { + region = "%s" + name = "network_1" + admin_state_up = "true" + } - resource "openstack_networking_subnet_v2" "foo" { - region = "%s" - name = "subnet_1" - network_id = "${openstack_networking_network_v2.foo.id}" - cidr = "192.168.199.0/24" - ip_version = 4 - } + resource "openstack_networking_subnet_v2" "foo" { + region = "%s" + name = "subnet_1" + network_id = "${openstack_networking_network_v2.foo.id}" + cidr = "192.168.199.0/24" + ip_version = 4 + } - resource "openstack_compute_secgroup_v2" "foo" { - region = "%s" - name = "secgroup_1" - description = "a security group" - rule { - from_port = 22 - to_port = 22 - ip_protocol = "tcp" - cidr = "0.0.0.0/0" - } - } - - resource "openstack_networking_port_v2" "foo" { - region = "%s" - name = "port_1" - network_id = "${openstack_networking_network_v2.foo.id}" - admin_state_up = "true" - security_groups = ["${openstack_compute_secgroup_v2.foo.id}"] + resource "openstack_compute_secgroup_v2" "foo" { + region = "%s" + name = "secgroup_1" + description = "a security group" + rule { + from_port = 22 + to_port = 22 + ip_protocol = "tcp" + cidr = "0.0.0.0/0" + } + } + resource "openstack_networking_port_v2" "foo" { + region = "%s" + name = "port_1" + network_id = "${openstack_networking_network_v2.foo.id}" + admin_state_up = "true" + security_groups = ["${openstack_compute_secgroup_v2.foo.id}"] fixed_ips { "subnet_id" = "${openstack_networking_subnet_v2.foo.id}" "ip_address" = "192.168.199.23" } - } + } - resource "openstack_compute_instance_v2" "foo" { - region = "%s" - name = "terraform-test" - security_groups = ["${openstack_compute_secgroup_v2.foo.name}"] + resource "openstack_compute_instance_v2" "foo" { + region = "%s" + name = "terraform-test" + security_groups = ["${openstack_compute_secgroup_v2.foo.name}"] - network { - port = "${openstack_networking_port_v2.foo.id}" - } - }`, region, region, region, region, region) + network { + port = "${openstack_networking_port_v2.foo.id}" + } + }`, region, region, region, region, region) resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, diff --git a/builtin/providers/openstack/resource_openstack_networking_port_v2_test.go b/builtin/providers/openstack/resource_openstack_networking_port_v2_test.go index c1e100d4f1..9ad2f1deb0 100644 --- a/builtin/providers/openstack/resource_openstack_networking_port_v2_test.go +++ b/builtin/providers/openstack/resource_openstack_networking_port_v2_test.go @@ -27,13 +27,13 @@ func TestAccNetworkingV2Port_basic(t *testing.T) { admin_state_up = "true" } - resource "openstack_networking_subnet_v2" "foo" { - region = "%s" - name = "subnet_1" - network_id = "${openstack_networking_network_v2.foo.id}" - cidr = "192.168.199.0/24" - ip_version = 4 - } + resource "openstack_networking_subnet_v2" "foo" { + region = "%s" + name = "subnet_1" + network_id = "${openstack_networking_network_v2.foo.id}" + cidr = "192.168.199.0/24" + ip_version = 4 + } resource "openstack_networking_port_v2" "foo" { region = "%s" @@ -41,8 +41,8 @@ func TestAccNetworkingV2Port_basic(t *testing.T) { network_id = "${openstack_networking_network_v2.foo.id}" admin_state_up = "true" fixed_ips { - subnet_id = "${openstack_networking_subnet_v2.foo.id}" - ip_address = "192.168.199.23" + subnet_id = "${openstack_networking_subnet_v2.foo.id}" + ip_address = "192.168.199.23" } }`, region, region, region) From 15533dca0908815f4172bf6c6571288c406831b3 Mon Sep 17 00:00:00 2001 From: clint shryock Date: Fri, 6 Nov 2015 14:06:50 -0600 Subject: [PATCH 034/173] actually use the value --- builtin/providers/aws/resource_aws_sns_topic.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/builtin/providers/aws/resource_aws_sns_topic.go b/builtin/providers/aws/resource_aws_sns_topic.go index b0d6fecf56..6bf0127d0c 100644 --- a/builtin/providers/aws/resource_aws_sns_topic.go +++ b/builtin/providers/aws/resource_aws_sns_topic.go @@ -46,10 +46,11 @@ func resourceAwsSnsTopic() *schema.Resource { Optional: true, Computed: true, StateFunc: func(v interface{}) string { - if v == nil { + s, ok := v.(string) + if !ok || s == "" { return "" } - jsonb := []byte{} + jsonb := []byte(s) buffer := new(bytes.Buffer) if err := json.Compact(buffer, jsonb); err != nil { log.Printf("[WARN] Error compacting JSON for Policy in SNS Topic") From 39cbc5616173916739b2bb5ef2c56bda802b71e0 Mon Sep 17 00:00:00 2001 From: captainill Date: Fri, 6 Nov 2015 12:35:24 -0800 Subject: [PATCH 035/173] in progress header redesign --- website/Gemfile.lock | 3 - website/source/assets/javascripts/app/Init.js | 6 + .../source/assets/javascripts/app/Sidebar.js | 50 +++ .../source/assets/javascripts/application.js | 1 + website/source/assets/stylesheets/_fonts.scss | 1 + .../source/assets/stylesheets/_header.scss | 402 ++---------------- .../source/assets/stylesheets/_sidebar.scss | 23 + .../source/assets/stylesheets/_utilities.scss | 16 - .../assets/stylesheets/application.scss | 12 +- .../hashicorp-shared/_hashicorp-header.scss | 166 ++++++++ .../hashicorp-shared/_hashicorp-sidebar.scss | 293 +++++++++++++ .../hashicorp-shared/_hashicorp-utility.scss | 86 ++++ .../hashicorp-shared/_project-utility.scss | 17 + website/source/layouts/_header.erb | 63 +-- website/source/layouts/_sidebar.erb | 26 ++ website/source/layouts/layout.erb | 1 + .../source/layouts/svg/_svg-by-hashicorp.erb | 18 + website/source/layouts/svg/_svg-download.erb | 4 + website/source/layouts/svg/_svg-github.erb | 9 + .../layouts/svg/_svg-hashicorp-logo.erb | 7 + 20 files changed, 791 insertions(+), 413 deletions(-) create mode 100644 website/source/assets/javascripts/app/Sidebar.js create mode 100644 website/source/assets/stylesheets/_sidebar.scss create mode 100755 website/source/assets/stylesheets/hashicorp-shared/_hashicorp-header.scss create mode 100644 website/source/assets/stylesheets/hashicorp-shared/_hashicorp-sidebar.scss create mode 100755 website/source/assets/stylesheets/hashicorp-shared/_hashicorp-utility.scss create mode 100755 website/source/assets/stylesheets/hashicorp-shared/_project-utility.scss create mode 100644 website/source/layouts/_sidebar.erb create mode 100644 website/source/layouts/svg/_svg-by-hashicorp.erb create mode 100644 website/source/layouts/svg/_svg-download.erb create mode 100644 website/source/layouts/svg/_svg-github.erb create mode 100644 website/source/layouts/svg/_svg-hashicorp-logo.erb diff --git a/website/Gemfile.lock b/website/Gemfile.lock index 7034f311e8..725b16df37 100644 --- a/website/Gemfile.lock +++ b/website/Gemfile.lock @@ -186,6 +186,3 @@ PLATFORMS DEPENDENCIES middleman-hashicorp! - -BUNDLED WITH - 1.10.6 diff --git a/website/source/assets/javascripts/app/Init.js b/website/source/assets/javascripts/app/Init.js index 074c95d8d9..06e772bebe 100644 --- a/website/source/assets/javascripts/app/Init.js +++ b/website/source/assets/javascripts/app/Init.js @@ -21,6 +21,12 @@ var Init = { if (this.Pages[id]) { this.Pages[id](); } + //always init sidebar + Init.initializeSidebar(); + }, + + initializeSidebar: function(){ + new Sidebar(); }, generateAnimatedLogo: function(){ diff --git a/website/source/assets/javascripts/app/Sidebar.js b/website/source/assets/javascripts/app/Sidebar.js new file mode 100644 index 0000000000..bb5d4cc9e7 --- /dev/null +++ b/website/source/assets/javascripts/app/Sidebar.js @@ -0,0 +1,50 @@ +(function(){ + +Sidebar = Base.extend({ + + $body: null, + $overlay: null, + $sidebar: null, + $sidebarHeader: null, + $sidebarImg: null, + $toggleButton: null, + + constructor: function(){ + this.$body = $('body'); + this.$overlay = $('.sidebar-overlay'); + this.$sidebar = $('#sidebar'); + this.$sidebarHeader = $('#sidebar .sidebar-header'); + this.$toggleButton = $('.navbar-toggle'); + this.sidebarImg = this.$sidebarHeader.css('background-image'); + + this.addEventListeners(); + }, + + addEventListeners: function(){ + var _this = this; + + _this.$toggleButton.on('click', function() { + _this.$sidebar.toggleClass('open'); + if ((_this.$sidebar.hasClass('sidebar-fixed-left') || _this.$sidebar.hasClass('sidebar-fixed-right')) && _this.$sidebar.hasClass('open')) { + _this.$overlay.addClass('active'); + _this.$body.css('overflow', 'hidden'); + } else { + _this.$overlay.removeClass('active'); + _this.$body.css('overflow', 'auto'); + } + + return false; + }); + + _this.$overlay.on('click', function() { + $(this).removeClass('active'); + _this.$body.css('overflow', 'auto'); + _this.$sidebar.removeClass('open'); + }); + } + +}); + +window.Sidebar = Sidebar; + +})(); diff --git a/website/source/assets/javascripts/application.js b/website/source/assets/javascripts/application.js index 9180016ef3..15542d8dc1 100644 --- a/website/source/assets/javascripts/application.js +++ b/website/source/assets/javascripts/application.js @@ -21,4 +21,5 @@ //= require app/Engine.Shape //= require app/Engine.Shape.Puller //= require app/Engine.Typewriter +//= require app/Sidebar //= require app/Init diff --git a/website/source/assets/stylesheets/_fonts.scss b/website/source/assets/stylesheets/_fonts.scss index 3f1d4aaed7..c14cb70711 100755 --- a/website/source/assets/stylesheets/_fonts.scss +++ b/website/source/assets/stylesheets/_fonts.scss @@ -2,6 +2,7 @@ // Typography // -------------------------------------------------- + //light .rls-l{ font-family: $font-family-lato; diff --git a/website/source/assets/stylesheets/_header.scss b/website/source/assets/stylesheets/_header.scss index 408d11d781..1923344425 100755 --- a/website/source/assets/stylesheets/_header.scss +++ b/website/source/assets/stylesheets/_header.scss @@ -1,382 +1,58 @@ // // Header +// - Project Specific +// - edits should be made here // -------------------------------------------------- -body.page-sub{ - - .terra-btn{ - background-color: rgba(130, 47, 247, 1); - } - - #header{ - height: 90px; - background-color: $purple; - - .navbar-collapse{ - background-color: rgba(255, 255, 255, 0.98); - } - - .nav-logo{ - height: 90px; - } - - .nav-white{ - height: 90px; - background-color: white; - } - - .main-links.navbar-nav{ - float: left !important; - li > a { - color: black; - @include transition( color 0.3s ease ); - } - } - - .buttons.nav > li > a, .buttons.nav > li > a { - //background-color: lighten($purple, 1%); - @include transition( background-color 0.3s ease ); - } - - .buttons.nav > li > a:hover, .buttons.nav > li > a:focus { - background-color: black; - @include transition( background-color 0.3s ease ); - } - - .main-links.nav > li > a:hover, .main-links.nav > li > a:focus { - color: $purple; - @include transition( color 0.3s ease ); - } - } -} - #header { - position: relative; - color: $white; - text-rendering: optimizeLegibility; - margin-bottom: 0; - - &.navbar-static-top{ - height:70px; - - -webkit-transform:translate3d(0,0,0); - -moz-transform:translate3d(0,0,0); - -ms-transform:translate3d(0,0,0); - -o-transform:translate3d(0,0,0); - transform:translate3d(0,0,0); - z-index: 1000; - } - - a{ - color: $white; - } - - .navbar-toggle{ - margin-top: 26px; - margin-bottom: 14px; - margin-right: 0; - border: 2px solid $white; - border-radius: 0; - .icon-bar{ - border: 1px solid $white; - border-radius: 0; - } - } - .navbar-brand { - &.logo{ - margin-top: 15px; - padding: 5px 0 0 68px; - height: 56px; - line-height: 56px; - font-size: 24px; - @include lato-light(); - text-transform: uppercase; - background: image-url('consul-header-logo.png') 0 0 no-repeat; - @include img-retina("header-logo.png", "header-logo@2x.png", 50px, 56px); - -webkit-font-smoothing: default; + .logo{ + color: $black; + font-size: 28px; + background: image-url('../images/logo-header.png') 0 0 no-repeat; + @include img-retina("../images/logo-header.png", "../images/logo-header@2x.png", $project-logo-size, $project-logo-size); + background-position: 0 center; + + &:hover{ + color: $orange; + } } - } - .navbar-nav{ - -webkit-font-smoothing: antialiased; - li{ - position: relative; - - > a { - font-size: 12px; - text-transform: uppercase; - letter-spacing: 3px; - padding-left: 22px; - @include transition( color 0.3s ease ); - } - - &.first{ - >a{ - padding-left: 15px; + .by{ + color: $black; + &:hover{ + svg{ + line{ + stroke: $purple; + } } } } } - .nav > li > a:hover, .nav > li > a:focus { - background-color: transparent; - color: lighten($purple, 15%); - @include transition( color 0.3s ease ); + .buttons{ + margin-top: 2px; //baseline everything + + ul.navbar-nav{ + li { + &:hover{ + svg path{ + fill: $purple; + } + } + + svg path{ + fill: $white; + } + } + } } - .main-links.navbar-nav{ - margin-top: 28px; - - li + li{ - padding-left: 6px; - } - - li + li::before { - content: ""; - position: absolute; - left: 0; - top: 7px; - width: 1px; - height: 12px; - background-color: $purple; - @include skewY(24deg); - padding-right: 0; - } - + .main-links, + .external-links { li > a { - //border-bottom: 2px solid rgba(255, 255, 255, .2); - line-height: 26px; - margin: 0 8px; - padding: 0 0 0 4px; - } - - } - - .buttons.navbar-nav{ - margin-top: 25px; - margin-left: 30px; - - li{ - &.first{ - margin-right: 13px; - } - - &.download{ - a{ - padding-left: 30px; - background: image-url("header-download-icon.png") 12px 8px no-repeat; - @include img-retina("header-download-icon.png", "header-download-icon@2x.png", 12px, 13px); - } - } - - &.github{ - a{ - background: image-url("header-github-icon.png") 12px 7px no-repeat; - @include img-retina("header-github-icon.png", "header-github-icon@2x.png", 12px, 13px); - } - } - } - - li > a { - color: white; - padding-top: 4px; - padding-bottom: 4px; - padding-left: 32px; - padding-right: 12px; - letter-spacing: 0.05em; + @include project-a-style(); + font-weight: 400; } } } - -@media (min-width: 1200px) { - - #header{ - .main-links.navbar-nav{ - margin-top: 28px; - - li + li{ - padding-left: 6px; - } - - li + li::before { - content: ""; - position: absolute; - left: 0; - top: 9px; - width: 6px; - height: 8px; - background-color: $purple; - @include skewY(24deg); - padding-right: 8px; - } - - li > a { - //border-bottom: 2px solid rgba(255, 255, 255, .2); - line-height: 26px; - margin: 0 12px; - padding: 0 0 0 4px; - } - - } - } -} - -@media (min-width: 992px) { - - .collapse{ - margin-top: 8px; - } - - //homepage has more space at this width to accommodate chevrons - .page-home{ - #header{ - .main-links.navbar-nav{ - li + li{ - padding-left: 6px; - } - - li + li::before { - content: ""; - position: absolute; - left: 0; - top: 9px; - width: 6px; - height: 8px; - background-color: $purple; - @include skewY(24deg); - padding-right: 8px; - } - } - } - } -} - - - -@media (min-width: 768px) and (max-width: 992px) { - - body.page-home{ - .nav-logo{ - width: 30%; - } - .nav-white{ - margin-top: 8px; - width: 70%; - } - .buttons.navbar-nav{ - li{ - > a{ - padding-right: 4px !important; - text-indent: -9999px; - white-space: nowrap; - } - } - } - } -} - - -@media (max-width: 992px) { - - #header { - .navbar-brand { - &.logo{ - span{ - width: 120px; - height: 39px; - margin-top: 12px; - background-size: 120px 39px; - } - } - } - } -} - -@media (max-width: 768px) { - - body.page-sub{ - #header{ - .nav-white{ - background-color: transparent; - } - } - } - - #header{ - .buttons.navbar-nav{ - float: none !important; - margin: 0; - padding-bottom: 0 !important; - - li{ - &.first{ - margin-right: 0; - } - } - } - } - - //#footer, - #header{ - .buttons.navbar-nav, - .main-links.navbar-nav{ - display: block; - padding-bottom: 15px; - li{ - display: block; - float: none; - margin-top: 15px; - } - - .li-under a::after, - li + li::before { - display: none; - } - } - } - - //#footer, - #header{ - .main-links.navbar-nav{ - float: left !important; - li > a { - padding: 0; - padding-left: 0; - line-height: 22px; - } - } - } -} - -@media (max-width: 763px) { - .navbar-static-top { - .nav-white { - background-color:rgba(0,0,0,0.5); - } - } -} - -@media (max-width: 320px) { - - #header{ - .navbar-brand { - &.logo{ - padding:0 0 0 54px !important; - font-size: 20px !important; - line-height:42px !important; - margin-top: 23px !important ; - @include img-retina("../images/header-logo.png", "../images/header-logo@2x.png", 39px, 44px); - } - } - - } - - #feature-auto{ - .terminal-text{ - line-height: 48px !important; - font-size: 20px !important; - } - } - -} diff --git a/website/source/assets/stylesheets/_sidebar.scss b/website/source/assets/stylesheets/_sidebar.scss new file mode 100644 index 0000000000..4c632d4808 --- /dev/null +++ b/website/source/assets/stylesheets/_sidebar.scss @@ -0,0 +1,23 @@ +// +// Sidebar +// - Project Specific +// - Make sidebar edits here +// -------------------------------------------------- + +.sidebar { + .sidebar-nav { + // Links + //---------------- + li { + a { + color: $black; + + svg{ + path{ + fill: $black; + } + } + } + } + } +} diff --git a/website/source/assets/stylesheets/_utilities.scss b/website/source/assets/stylesheets/_utilities.scss index b01423f500..4f30e502b3 100755 --- a/website/source/assets/stylesheets/_utilities.scss +++ b/website/source/assets/stylesheets/_utilities.scss @@ -2,27 +2,11 @@ // Utility classes // -------------------------------------------------- - -// -// ------------------------- - @mixin anti-alias() { text-rendering: optimizeLegibility; -webkit-font-smoothing: antialiased; } -@mixin consul-gradient-bg() { - background: #694a9c; /* Old browsers */ - background: -moz-linear-gradient(left, #694a9c 0%, #cd2028 100%); /* FF3.6+ */ - background: -webkit-gradient(linear, left top, right top, color-stop(0%,#694a9c), color-stop(100%,#cd2028)); /* Chrome,Safari4+ */ - background: -webkit-linear-gradient(left, #694a9c 0%,#cd2028 100%); /* Chrome10+,Safari5.1+ */ - background: -o-linear-gradient(left, #694a9c 0%,#cd2028 100%); /* Opera 11.10+ */ - background: -ms-linear-gradient(left, #694a9c 0%,#cd2028 100%); /* IE10+ */ - background: linear-gradient(to right, #694a9c 0%,#cd2028 100%); /* W3C */ - filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#694a9c', endColorstr='#cd2028',GradientType=1 ); /* IE6-9 */ - -} - @mixin lato-light() { font-family: $font-family-lato; font-weight: 300; diff --git a/website/source/assets/stylesheets/application.scss b/website/source/assets/stylesheets/application.scss index 95ae64b73a..3776f90566 100755 --- a/website/source/assets/stylesheets/application.scss +++ b/website/source/assets/stylesheets/application.scss @@ -1,13 +1,12 @@ @import 'bootstrap-sprockets'; @import 'bootstrap'; -@import url("//fonts.googleapis.com/css?family=Lato:300,400,700"); +@import url("//fonts.googleapis.com/css?family=Lato:300,400,700|Open+Sans:300,400,600"); // Core variables and mixins @import '_variables'; -@import '_mixins'; -// Utility classes +// Utility @import '_utilities'; // Core CSS @@ -16,11 +15,18 @@ //Global Site @import '_global'; +// Hashicorp Shared Project Styles +@import 'hashicorp-shared/_project-utility'; +@import 'hashicorp-shared/_hashicorp-utility'; +@import 'hashicorp-shared/_hashicorp-header'; +@import 'hashicorp-shared/_hashicorp-sidebar'; + // Components @import '_header'; @import '_footer'; @import '_jumbotron'; @import '_buttons'; +@import '_sidebar'; // Pages @import '_home'; diff --git a/website/source/assets/stylesheets/hashicorp-shared/_hashicorp-header.scss b/website/source/assets/stylesheets/hashicorp-shared/_hashicorp-header.scss new file mode 100755 index 0000000000..915820c337 --- /dev/null +++ b/website/source/assets/stylesheets/hashicorp-shared/_hashicorp-header.scss @@ -0,0 +1,166 @@ +// +// Hashicorp header +// - Shared throughout projects +// - Edits should not be made here +// -------------------------------------------------- + +#header { + position: relative; + color: $white; + text-rendering: optimizeLegibility; + margin-bottom: 0; + transition: all 1s ease; + + &.white{ + .navbar-brand { + .by{ + svg{ + path, + polygon{ + fill: white; + } + line{ + stroke: white; + } + } + } + } + } + + .navbar-header{ + .navbar-toggle{ + height: $header-height; + margin: 0; + padding-right: 15px; + border-radius: 0; + .icon-bar{ + border: 1px solid $black; + border-radius: 0; + } + } + } + + .navbar-brand { + display: block; + height: $header-height; + padding: 0; + margin: 0 10px 0 0 ; + + .logo{ + display: inline-block; + height: $header-height; + vertical-align:top; + padding: 0; + line-height: $header-height; + padding-left: $project-logo-size + $project-logo-pad-left; + background-position: 0 center; + + &:hover{ + @include transition(all 300ms ease-in); + text-decoration: none; + } + } + + .by{ + display: inline-block; + vertical-align:top; + height: $header-height; + margin-left: 3px; + padding-top: 2px; + line-height: $header-height; + font-family: $header-font-family; + font-weight: 300; + font-size: 0; + text-decoration: none; + + .svg-wrap{ + font-size: 13px; + } + + svg{ + &.svg-by{ + width: $by-hashicorp-width; + height: $by-hashicorp-height; + margin-bottom: -4px; + margin-left: 4px; + } + + &.svg-logo{ + width: 16px; + height: 16px; + margin-bottom: -3px; + margin-left: 4px; + } + + path, + polygon{ + fill: black; + @include transition(all 300ms ease-in); + + &:hover{ + @include transition(all 300ms ease-in); + } + } + line{ + stroke: black; + + &:hover{ + @include transition(all 300ms ease-in); + } + } + } + } + } + + .external-links { + li { + position: relative; + + svg path{ + @include transition( all 0.3s ease ); + } + + &:hover{ + svg path{ + @include transition( all 0.3s ease ); + } + } + + &.download{ + margin-right: 10px; + } + + >a { + padding-left: 12px !important; + svg{ + position: absolute; + left: -12px; + top: 50%; + margin-top: -7px; + width: 14px; + height: 14px; + } + } + } + } + + .main-links{ + margin-right: $nav-margin-right * 2; + } + + .main-links, + .external-links { + li > a { + @include hashi-a-style(); + margin: 0 10px; + padding-top: 1px; + line-height: $header-height; + } + } + + .nav > li > a:hover, .nav > li > a:focus { + background-color: transparent; + color: $black; + @include transition( color 0.3s ease ); + } +} diff --git a/website/source/assets/stylesheets/hashicorp-shared/_hashicorp-sidebar.scss b/website/source/assets/stylesheets/hashicorp-shared/_hashicorp-sidebar.scss new file mode 100644 index 0000000000..4a5a6ae07f --- /dev/null +++ b/website/source/assets/stylesheets/hashicorp-shared/_hashicorp-sidebar.scss @@ -0,0 +1,293 @@ +// +// Hashicorp Sidebar +// - Shared throughout projects +// - Edits should not be made here +// -------------------------------------------------- + +// Base variables +// -------------------------------------------------- +$screen-tablet: 768px; + +$gray-darker: #212121; // #212121 - text +$gray-secondary: #757575; // #757575 - secondary text, icons +$gray: #bdbdbd; // #bdbdbd - hint text +$gray-light: #e0e0e0; // #e0e0e0 - divider +$gray-lighter: #f5f5f5; // #f5f5f5 - background +$link-color: $gray-darker; +$link-bg: transparent; +$link-hover-color: $gray-lighter; +$link-hover-bg: $gray-lighter; +$link-active-color: $gray-darker; +$link-active-bg: $gray-light; +$link-disabled-color: $gray-light; +$link-disabled-bg: transparent; + +/* -- Sidebar style ------------------------------- */ + +// Sidebar variables +// -------------------------------------------------- +$zindex-sidebar-fixed: 1035; + +$sidebar-desktop-width: 280px; +$sidebar-width: 240px; + +$sidebar-padding: 16px; +$sidebar-divider: $sidebar-padding/2; + +$sidebar-icon-width: 40px; +$sidebar-icon-height: 20px; + +@mixin sidebar-nav-base { + text-align: center; + + &:last-child{ + border-bottom: none; + } + + li > a { + background-color: $link-bg; + } + li:hover > a { + background-color: $link-hover-bg; + } + li:focus > a, li > a:focus { + background-color: $link-bg; + } + + > .open > a { + &, + &:hover, + &:focus { + background-color: $link-hover-bg; + } + } + + > .active > a { + &, + &:hover, + &:focus { + background-color: $link-active-bg; + } + } + > .disabled > a { + &, + &:hover, + &:focus { + background-color: $link-disabled-bg; + } + } + + // Dropdown menu items + > .dropdown { + // Remove background color from open dropdown + > .dropdown-menu { + background-color: $link-hover-bg; + + > li > a { + &:focus { + background-color: $link-hover-bg; + } + &:hover { + background-color: $link-hover-bg; + } + } + + > .active > a { + &, + &:hover, + &:focus { + color: $link-active-color; + background-color: $link-active-bg; + } + } + } + } +} + +// +// Sidebar +// -------------------------------------------------- + +// Sidebar Elements +// +// Basic style of sidebar elements +.sidebar { + position: relative; + display: block; + min-height: 100%; + overflow-y: auto; + overflow-x: hidden; + border: none; + @include transition(all 0.5s cubic-bezier(0.55, 0, 0.1, 1)); + @include clearfix(); + background-color: $white; + + ul{ + padding-left: 0; + list-style-type: none; + } + + .sidebar-divider, .divider { + width: 80%; + height: 1px; + margin: 8px auto; + background-color: lighten($gray, 20%); + } + + // Sidebar heading + //---------------- + .sidebar-header { + position: relative; + margin-bottom: $sidebar-padding; + @include transition(all .2s ease-in-out); + } + + .sidebar-image { + padding-top: 24px; + img { + display: block; + margin: 0 auto; + } + } + + + // Sidebar icons + //---------------- + .sidebar-icon { + display: inline-block; + height: $sidebar-icon-height; + margin-right: $sidebar-divider; + text-align: left; + font-size: $sidebar-icon-height; + vertical-align: middle; + + &:before, &:after { + vertical-align: middle; + } + } + + .sidebar-nav { + margin: 0; + padding: 0; + + @include sidebar-nav-base(); + + // Links + //---------------- + li { + position: relative; + list-style-type: none; + text-align: center; + + a { + position: relative; + cursor: pointer; + user-select: none; + @include hashi-a-style-core(); + + svg{ + top: 2px; + width: 14px; + height: 14px; + margin-bottom: -2px; + margin-right: 4px; + } + } + } + } +} + +// Sidebar toggling +// +// Hide sidebar +.sidebar { + width: 0; + @include translate3d(-$sidebar-desktop-width, 0, 0); + + &.open { + min-width: $sidebar-desktop-width; + width: $sidebar-desktop-width; + @include translate3d(0, 0, 0); + } +} + +// Sidebar positions: fix the left/right sidebars +.sidebar-fixed-left, +.sidebar-fixed-right, +.sidebar-stacked { + position: fixed; + top: 0; + bottom: 0; + z-index: $zindex-sidebar-fixed; +} +.sidebar-stacked { + left: 0; +} +.sidebar-fixed-left { + left: 0; + box-shadow: 2px 0px 25px rgba(0,0,0,0.15); + -webkit-box-shadow: 2px 0px 25px rgba(0,0,0,0.15); +} +.sidebar-fixed-right { + right: 0; + box-shadow: 0px 2px 25px rgba(0,0,0,0.15); + -webkit-box-shadow: 0px 2px 25px rgba(0,0,0,0.15); + + @include translate3d($sidebar-desktop-width, 0, 0); + &.open { + @include translate3d(0, 0, 0); + } + .icon-material-sidebar-arrow:before { + content: "\e614"; // icon-material-arrow-forward + } +} + +// Sidebar size +// +// Change size of sidebar and sidebar elements on small screens +@media (max-width: $screen-tablet) { + .sidebar.open { + min-width: $sidebar-width; + width: $sidebar-width; + } + + .sidebar .sidebar-header { + //height: $sidebar-width * 9/16; // 16:9 header dimension + } + + .sidebar .sidebar-image { + /* img { + width: $sidebar-width/4 - $sidebar-padding; + height: $sidebar-width/4 - $sidebar-padding; + } */ + } +} + +.sidebar-overlay { + visibility: hidden; + position: fixed; + top: 0; + left: 0; + right: 0; + bottom: 0; + opacity: 0; + background: $white; + z-index: $zindex-sidebar-fixed - 1; + + -webkit-transition: visibility 0 linear .4s,opacity .4s cubic-bezier(.4,0,.2,1); + -moz-transition: visibility 0 linear .4s,opacity .4s cubic-bezier(.4,0,.2,1); + transition: visibility 0 linear .4s,opacity .4s cubic-bezier(.4,0,.2,1); + -webkit-transform: translateZ(0); + -moz-transform: translateZ(0); + -ms-transform: translateZ(0); + -o-transform: translateZ(0); + transform: translateZ(0); +} + +.sidebar-overlay.active { + opacity: 0.3; + visibility: visible; + -webkit-transition-delay: 0; + -moz-transition-delay: 0; + transition-delay: 0; +} diff --git a/website/source/assets/stylesheets/hashicorp-shared/_hashicorp-utility.scss b/website/source/assets/stylesheets/hashicorp-shared/_hashicorp-utility.scss new file mode 100755 index 0000000000..be6c9f8fc6 --- /dev/null +++ b/website/source/assets/stylesheets/hashicorp-shared/_hashicorp-utility.scss @@ -0,0 +1,86 @@ +// +// Hashicorp Nav (header/footer) Utiliy Vars and Mixins +// +// Notes: +// - Include this in Application.scss before header and feature-footer +// - Open Sans Google (Semibold - 600) font needs to be included if not already +// -------------------------------------------------- + +// Variables +$font-family-open-sans: 'Open Sans', 'Helvetica Neue', Helvetica, Arial, sans-serif; +$header-font-family: $font-family-open-sans; +$header-font-weight: 600; // semi-bold + +$header-height: 74px; +$by-hashicorp-width: 74px; +$by-hashicorp-height: 16px; +$nav-margin-right: 12px; + +// Mixins +@mixin hashi-a-style-core{ + font-family: $header-font-family; + font-weight: $header-font-weight; + font-size: 14px; + //letter-spacing: 0.0625em; +} + +@mixin hashi-a-style{ + margin: 0 15px; + padding: 0; + line-height: 22px; + @include hashi-a-style-core(); + @include transition( color 0.3s ease ); + + &:hover{ + @include transition( color 0.3s ease ); + background-color: transparent; + } +} + +//general shared project mixins +@mixin img-retina($image1x, $image, $width, $height) { + background-image: url($image1x); + background-size: $width $height; + background-repeat: no-repeat; + + @media (min--moz-device-pixel-ratio: 1.3), + (-o-min-device-pixel-ratio: 2.6/2), + (-webkit-min-device-pixel-ratio: 1.3), + (min-device-pixel-ratio: 1.3), + (min-resolution: 1.3dppx) { + /* on retina, use image that's scaled by 2 */ + background-image: url($image); + background-size: $width $height; + } +} + +// +// ------------------------- +@mixin anti-alias() { + text-rendering: optimizeLegibility; + -webkit-font-smoothing: antialiased; +} + +@mixin open-light() { + font-family: $font-family-open-sans; + font-weight: 300; +} + +@mixin open() { + font-family: $font-family-open-sans; + font-weight: 400; +} + +@mixin open-sb() { + font-family: $font-family-open-sans; + font-weight: 600; +} + +@mixin open-bold() { + font-family: $font-family-open-sans; + font-weight: 700; +} + +@mixin bez-1-transition{ + @include transition( all 300ms ease-in-out ); +} diff --git a/website/source/assets/stylesheets/hashicorp-shared/_project-utility.scss b/website/source/assets/stylesheets/hashicorp-shared/_project-utility.scss new file mode 100755 index 0000000000..42910526ae --- /dev/null +++ b/website/source/assets/stylesheets/hashicorp-shared/_project-utility.scss @@ -0,0 +1,17 @@ +// +// Mixins Specific to project +// - make edits to mixins here +// -------------------------------------------------- + +// Variables +$project-logo-size: 40px; +$project-logo-pad-left: 8px; + +// Mixins +@mixin project-a-style{ + color: $white; + + &:hover{ + color: $purple; + } +} diff --git a/website/source/layouts/_header.erb b/website/source/layouts/_header.erb index fa6119a295..484f551b84 100644 --- a/website/source/layouts/_header.erb +++ b/website/source/layouts/_header.erb @@ -1,30 +1,37 @@ - diff --git a/website/source/layouts/_sidebar.erb b/website/source/layouts/_sidebar.erb new file mode 100644 index 0000000000..5a906142f1 --- /dev/null +++ b/website/source/layouts/_sidebar.erb @@ -0,0 +1,26 @@ + + + + + diff --git a/website/source/layouts/layout.erb b/website/source/layouts/layout.erb index 665a0a1289..2b7a67363a 100644 --- a/website/source/layouts/layout.erb +++ b/website/source/layouts/layout.erb @@ -1,4 +1,5 @@ <%= partial "layouts/meta" %> <%= partial "layouts/header" %> +<%= partial "layouts/sidebar" %> <%= yield %> <%= partial "layouts/footer" %> diff --git a/website/source/layouts/svg/_svg-by-hashicorp.erb b/website/source/layouts/svg/_svg-by-hashicorp.erb new file mode 100644 index 0000000000..d89929590e --- /dev/null +++ b/website/source/layouts/svg/_svg-by-hashicorp.erb @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + diff --git a/website/source/layouts/svg/_svg-download.erb b/website/source/layouts/svg/_svg-download.erb new file mode 100644 index 0000000000..6d8441fea6 --- /dev/null +++ b/website/source/layouts/svg/_svg-download.erb @@ -0,0 +1,4 @@ + + + diff --git a/website/source/layouts/svg/_svg-github.erb b/website/source/layouts/svg/_svg-github.erb new file mode 100644 index 0000000000..f0264d5aa4 --- /dev/null +++ b/website/source/layouts/svg/_svg-github.erb @@ -0,0 +1,9 @@ + + + + diff --git a/website/source/layouts/svg/_svg-hashicorp-logo.erb b/website/source/layouts/svg/_svg-hashicorp-logo.erb new file mode 100644 index 0000000000..60663e1406 --- /dev/null +++ b/website/source/layouts/svg/_svg-hashicorp-logo.erb @@ -0,0 +1,7 @@ + From 1269d2a1568cfbe1bb1e2bfc274efd65c05802ae Mon Sep 17 00:00:00 2001 From: captainill Date: Fri, 6 Nov 2015 12:45:10 -0800 Subject: [PATCH 036/173] shared styles update --- .../stylesheets/hashicorp-shared/_hashicorp-header.scss | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/website/source/assets/stylesheets/hashicorp-shared/_hashicorp-header.scss b/website/source/assets/stylesheets/hashicorp-shared/_hashicorp-header.scss index 915820c337..1578ad93a6 100755 --- a/website/source/assets/stylesheets/hashicorp-shared/_hashicorp-header.scss +++ b/website/source/assets/stylesheets/hashicorp-shared/_hashicorp-header.scss @@ -14,6 +14,8 @@ &.white{ .navbar-brand { .by{ + color: white; + font-weight: 300; svg{ path, polygon{ @@ -67,9 +69,10 @@ height: $header-height; margin-left: 3px; padding-top: 2px; + color: black; line-height: $header-height; font-family: $header-font-family; - font-weight: 300; + font-weight: 600; font-size: 0; text-decoration: none; From 0e9397fc741fa69651a9dfdf2f2ef1881fd7d769 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Fri, 6 Nov 2015 21:52:30 +0000 Subject: [PATCH 037/173] provider/openstack: Security Group Rules Fix This commit fixes how security group rules are read by Terraform and enables them to be correctly removed when the rule resource is modified. --- .../resource_openstack_compute_secgroup_v2.go | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/builtin/providers/openstack/resource_openstack_compute_secgroup_v2.go b/builtin/providers/openstack/resource_openstack_compute_secgroup_v2.go index 3cc0cbf0cc..202c4d9794 100644 --- a/builtin/providers/openstack/resource_openstack_compute_secgroup_v2.go +++ b/builtin/providers/openstack/resource_openstack_compute_secgroup_v2.go @@ -131,10 +131,10 @@ func resourceComputeSecGroupV2Read(d *schema.ResourceData, meta interface{}) err d.Set("description", sg.Description) rtm := rulesToMap(sg.Rules) for _, v := range rtm { - if v["group"] == d.Get("name") { - v["self"] = "1" + if v["from_group_id"] == d.Get("name") { + v["self"] = true } else { - v["self"] = "0" + v["self"] = false } } log.Printf("[DEBUG] rulesToMap(sg.Rules): %+v", rtm) @@ -283,12 +283,12 @@ func rulesToMap(sgrs []secgroups.Rule) []map[string]interface{} { sgrMap := make([]map[string]interface{}, len(sgrs)) for i, sgr := range sgrs { sgrMap[i] = map[string]interface{}{ - "id": sgr.ID, - "from_port": sgr.FromPort, - "to_port": sgr.ToPort, - "ip_protocol": sgr.IPProtocol, - "cidr": sgr.IPRange.CIDR, - "group": sgr.Group.Name, + "id": sgr.ID, + "from_port": sgr.FromPort, + "to_port": sgr.ToPort, + "ip_protocol": sgr.IPProtocol, + "cidr": sgr.IPRange.CIDR, + "from_group_id": sgr.Group.Name, } } return sgrMap From 1ab69ab6383b65b4775b955fa4e2db6f9b92f2ed Mon Sep 17 00:00:00 2001 From: Will Stern Date: Fri, 6 Nov 2015 15:55:58 -0600 Subject: [PATCH 038/173] typo in cluster_identifier --- .../docs/providers/aws/r/rds_cluster_instance.html.markdown | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/source/docs/providers/aws/r/rds_cluster_instance.html.markdown b/website/source/docs/providers/aws/r/rds_cluster_instance.html.markdown index 0d64a3f876..c124713b38 100644 --- a/website/source/docs/providers/aws/r/rds_cluster_instance.html.markdown +++ b/website/source/docs/providers/aws/r/rds_cluster_instance.html.markdown @@ -27,7 +27,7 @@ For more information on Amazon Aurora, see [Aurora on Amazon RDS][2] in the Amaz resource "aws_rds_cluster_instance" "cluster_instances" { count = 2 identifier = "aurora-cluster-demo" - cluster_identifer = "${aws_rds_cluster.default.id}" + cluster_identifier = "${aws_rds_cluster.default.id}" instance_class = "db.r3.large" } From 327bd4f9c05afa1634358b2348c3ef9d615629c2 Mon Sep 17 00:00:00 2001 From: Rob Zienert Date: Fri, 6 Nov 2015 15:27:47 -0600 Subject: [PATCH 039/173] Adding S3 support for Lambda provider --- .../aws/resource_aws_lambda_function.go | 64 ++++++++++++++----- .../aws/r/lambda_function.html.markdown | 5 +- 2 files changed, 52 insertions(+), 17 deletions(-) diff --git a/builtin/providers/aws/resource_aws_lambda_function.go b/builtin/providers/aws/resource_aws_lambda_function.go index 4ce8981744..324016455e 100644 --- a/builtin/providers/aws/resource_aws_lambda_function.go +++ b/builtin/providers/aws/resource_aws_lambda_function.go @@ -13,6 +13,8 @@ import ( "github.com/aws/aws-sdk-go/service/lambda" "github.com/mitchellh/go-homedir" + "errors" + "github.com/hashicorp/terraform/helper/schema" ) @@ -25,13 +27,28 @@ func resourceAwsLambdaFunction() *schema.Resource { Schema: map[string]*schema.Schema{ "filename": &schema.Schema{ - Type: schema.TypeString, - Required: true, + Type: schema.TypeString, + Optional: true, + ConflictsWith: []string{"s3_bucket", "s3_key", "s3_object_version"}, + }, + "s3_bucket": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + ConflictsWith: []string{"filename"}, + }, + "s3_key": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + ConflictsWith: []string{"filename"}, + }, + "s3_object_version": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + ConflictsWith: []string{"filename"}, }, "description": &schema.Schema{ Type: schema.TypeString, Optional: true, - ForceNew: true, // TODO make this editable }, "function_name": &schema.Schema{ Type: schema.TypeString, @@ -93,22 +110,36 @@ func resourceAwsLambdaFunctionCreate(d *schema.ResourceData, meta interface{}) e log.Printf("[DEBUG] Creating Lambda Function %s with role %s", functionName, iamRole) - filename, err := homedir.Expand(d.Get("filename").(string)) - if err != nil { - return err + var functionCode *lambda.FunctionCode + if v, ok := d.GetOk("filename"); ok { + filename, err := homedir.Expand(v.(string)) + if err != nil { + return err + } + zipfile, err := ioutil.ReadFile(filename) + if err != nil { + return err + } + d.Set("source_code_hash", sha256.Sum256(zipfile)) + functionCode = &lambda.FunctionCode{ + ZipFile: zipfile, + } + } else { + s3Bucket, bucketOk := d.GetOk("s3_bucket") + s3Key, keyOk := d.GetOk("s3_key") + s3ObjectVersion, versionOk := d.GetOk("s3_object_version") + if !bucketOk || !keyOk || !versionOk { + return errors.New("s3_bucket, s3_key and s3_object_version must all be set while using S3 code source") + } + functionCode = &lambda.FunctionCode{ + S3Bucket: aws.String(s3Bucket.(string)), + S3Key: aws.String(s3Key.(string)), + S3ObjectVersion: aws.String(s3ObjectVersion.(string)), + } } - zipfile, err := ioutil.ReadFile(filename) - if err != nil { - return err - } - d.Set("source_code_hash", sha256.Sum256(zipfile)) - - log.Printf("[DEBUG] ") params := &lambda.CreateFunctionInput{ - Code: &lambda.FunctionCode{ - ZipFile: zipfile, - }, + Code: functionCode, Description: aws.String(d.Get("description").(string)), FunctionName: aws.String(functionName), Handler: aws.String(d.Get("handler").(string)), @@ -118,6 +149,7 @@ func resourceAwsLambdaFunctionCreate(d *schema.ResourceData, meta interface{}) e Timeout: aws.Int64(int64(d.Get("timeout").(int))), } + var err error for i := 0; i < 5; i++ { _, err = conn.CreateFunction(params) if awsErr, ok := err.(awserr.Error); ok { diff --git a/website/source/docs/providers/aws/r/lambda_function.html.markdown b/website/source/docs/providers/aws/r/lambda_function.html.markdown index 4c931fbada..f9c1ea4a3f 100644 --- a/website/source/docs/providers/aws/r/lambda_function.html.markdown +++ b/website/source/docs/providers/aws/r/lambda_function.html.markdown @@ -44,7 +44,10 @@ resource "aws_lambda_function" "test_lambda" { ## Argument Reference -* `filename` - (Required) A [zip file][2] containing your lambda function source code. +* `filename` - (Optional) A [zip file][2] containing your lambda function source code. If defined, The `s3_*` options cannot be used. +* `s3_bucket` - (Optional) The S3 bucket location containing your lambda function source code. Conflicts with `filename`. +* `s3_key` - (Optional) The S3 key containing your lambda function source code. Conflicts with `filename`. +* `s3_object_version` - (Optional) The object version of your lambda function source code. Conflicts with `filename`. * `function_name` - (Required) A unique name for your Lambda Function. * `handler` - (Required) The function [entrypoint][3] in your code. * `role` - (Required) IAM role attached to the Lambda Function. This governs both who / what can invoke your Lambda Function, as well as what resources our Lambda Function has access to. See [Lambda Permission Model][4] for more details. From 996f88acd8cab692cd26ea32c2c63dcb26dcb21a Mon Sep 17 00:00:00 2001 From: Clint Date: Fri, 6 Nov 2015 16:17:22 -0600 Subject: [PATCH 040/173] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5215c84e3c..cdb5a393bb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -27,6 +27,7 @@ IMPROVEMENTS: * provider/aws: Add notification topic ARN for ElastiCache clusters [GH-3674] * provider/aws: Add `kinesis_endpoint` for configuring Kinesis [GH-3255] * provider/aws: Add a computed ARN for S3 Buckets [GH-3685] + * provider/aws: Add S3 support for Lambda Function resource [GH-3794] * 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] From dbd2a43f464b80ab3c3893b7280e87d7105ce92f Mon Sep 17 00:00:00 2001 From: clint shryock Date: Fri, 6 Nov 2015 16:55:04 -0600 Subject: [PATCH 041/173] config updates for ElastiCache test --- .../providers/aws/resource_aws_elasticache_cluster_test.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/builtin/providers/aws/resource_aws_elasticache_cluster_test.go b/builtin/providers/aws/resource_aws_elasticache_cluster_test.go index 0620ef47ba..a17c5d9b1e 100644 --- a/builtin/providers/aws/resource_aws_elasticache_cluster_test.go +++ b/builtin/providers/aws/resource_aws_elasticache_cluster_test.go @@ -215,7 +215,7 @@ resource "aws_elasticache_security_group" "bar" { resource "aws_elasticache_cluster" "bar" { cluster_id = "tf-test-%03d" engine = "redis" - node_type = "cache.t2.small" + node_type = "cache.m1.small" num_cache_nodes = 1 port = 6379 parameter_group_name = "default.redis2.8" @@ -230,7 +230,7 @@ provider "aws" { region = "us-east-1" } resource "aws_security_group" "bar" { - name = "tf-test-security-group" + name = "tf-test-security-group-%03d" description = "tf-test-security-group-descr" ingress { from_port = -1 @@ -241,7 +241,7 @@ resource "aws_security_group" "bar" { } resource "aws_elasticache_security_group" "bar" { - name = "tf-test-security-group" + name = "tf-test-security-group-%03d" description = "tf-test-security-group-descr" security_group_names = ["${aws_security_group.bar.name}"] } From c40f689201336b363d722e56d61003c1aacc4587 Mon Sep 17 00:00:00 2001 From: Clint Date: Fri, 6 Nov 2015 16:56:54 -0600 Subject: [PATCH 042/173] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index cdb5a393bb..1399eca4fb 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 S3 support for Lambda Function resource [GH-3794] + * provider/aws: Add snapshot window and retention limits for ElastiCache (Redis) [GH-3707] * 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] From a5f8ecfdd869b997f917993b5187f231ebf0e3d1 Mon Sep 17 00:00:00 2001 From: captainill Date: Fri, 6 Nov 2015 15:41:23 -0800 Subject: [PATCH 043/173] header styles complete + subpage --- .../assets/images/logo-header-black@2x.png | Bin 0 -> 1771 bytes .../{header-logo.png => logo-header.png} | Bin ...{header-logo@2x.png => logo-header@2x.png} | Bin .../source/assets/stylesheets/_header.scss | 45 +++++++++++++----- .../hashicorp-shared/_hashicorp-header.scss | 38 +++++++++++++-- .../hashicorp-shared/_hashicorp-utility.scss | 4 +- .../hashicorp-shared/_project-utility.scss | 7 ++- website/source/layouts/_sidebar.erb | 2 +- 8 files changed, 75 insertions(+), 21 deletions(-) create mode 100644 website/source/assets/images/logo-header-black@2x.png rename website/source/assets/images/{header-logo.png => logo-header.png} (100%) rename website/source/assets/images/{header-logo@2x.png => logo-header@2x.png} (100%) diff --git a/website/source/assets/images/logo-header-black@2x.png b/website/source/assets/images/logo-header-black@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..74521a66462127900dbb32d29c7024f8ebc041e9 GIT binary patch literal 1771 zcmb7_i8tHX7r?)5DYXRoiR9N-TY~9=h@}mo##S`e(g-C;Vl66`h+5j(8WLMjts*+6 zqzopt&7js$TFZ=#RtdEfRk38$(iZ)tf5PvcbMNQAd+$5%oO|whH{Hp>T2|_~6aWCS zwlrDY7q#eo@bY)fIDM3@i<4Z0GG5{2JQaEsX;y| z!|4J|kgY37{giF?*HvlD^Bl>W-Jg9Pp^$p|2AM_k%h?9mcqaSMCd(jT6Z0ImE$ z`5hzkmSeH2pEa@?2*o)vNOH4t{Nbh&a<5I(iOMBPOO4(6K6%IaE5-SfUz(7auz^!A z7shkYv?d)oY+$W7i+``-UMvmCt>4vf7*=-5dsGM;0L_%6F2?xSlcou3-{9JV%AP!2 zK5QWIu1loEJ4Gd}KKc7X!;n|XA8qxG-Lfz9CI2WV34rF}Vb_ zNxIy*6|&KVN}5!#7d}G?{_Yk>FGeZg1JxO6qOUr9g_bKok3T?HyM%Mt`L$5*axQG{ z{jOTp>ylX{%FFf2%OT7>*QQD0)n*(U*}c2taAYY-c||v<`v%6+{SX zgIO4-B4)S@bAdc$#QonbNfI0Hg_lOCgdQ*&)NP*RdH$ zt^%I{Mf^;Ywo;Frjt#1T4Q$WD7hUp9AX}r)d&~T)EDOrF&Lf% z_vQp;&pd`IK3w-@=QU~kYIBQ2&he4ybnC*>qO5xC-;0#C<8S8%_zsj*XGp_8RLarB z{K>zybsP*g6@<5U{P)f^_k{X0C;lx#ojBdo#m{QawUyoEBQ6z=pZ zM?KSwtJ|JJL2r#HQ`0{LHkztpQ{%ACqwV)uD{F9pWc!23^sx}dP=jw;b$s`+T_kR9 z{rl71Fs$Yct0NP+N^~Kc&Aynn`bVW7;bdPlN+l+u(llS*6GI-TpD#!Bm+)<$Va1TYA9NPdx!C96UMkkr z+?$Dh#=ABq77ZOfNgg5->KD5H3w9gh$LRFzul1}%{1l0;GDyNi?+n3fr_ydz+TpK9 zc62H3#2#MfF>NcxjJno(B{J~_G zTh(G@GQ^bShwvdj8`SP|(k$frh#o7X$F`Pp$Fr-Wq9UUGE{kTob*oan&36Bg7OU~$ z{(R;WU2i0Fs$6AB{(&pSOR+V)<17}E0P=-$ZOHg@6;BdyPnP5iR#7p?6JwKe} zK;sobXW=N<=GS`&w{*kRk}_8$ZCUbCI;W2rFAR)ouEa%^W_KceK=)WE#sGX9} z3@DsE{bz~0n7!ye>7W!g`PGW-8Q`cgNc1o-K+`OF2fElzH%`Y~%`PS^!2jtoL5)H` zU$@6;41!bbU{70 z|BY&I;3lyuD+)A=R-}S_b5F a { @include project-a-style(); - font-weight: 400; } } } diff --git a/website/source/assets/stylesheets/hashicorp-shared/_hashicorp-header.scss b/website/source/assets/stylesheets/hashicorp-shared/_hashicorp-header.scss index 1578ad93a6..f8b5974fba 100755 --- a/website/source/assets/stylesheets/hashicorp-shared/_hashicorp-header.scss +++ b/website/source/assets/stylesheets/hashicorp-shared/_hashicorp-header.scss @@ -6,13 +6,18 @@ #header { position: relative; - color: $white; + color: black; text-rendering: optimizeLegibility; margin-bottom: 0; transition: all 1s ease; &.white{ + color: white; .navbar-brand { + .logo { + color: white; + } + .by{ color: white; font-weight: 300; @@ -27,6 +32,27 @@ } } } + + .main-links, + .external-links { + li > a { + &:hover{ + opacity: 1; + } + } + } + + .nav > li > a:hover, .nav > li > a:focus { + color: white; + } + + .navbar-header{ + .navbar-toggle{ + .icon-bar{ + border: 1px solid white; + } + } + } } .navbar-header{ @@ -54,8 +80,9 @@ vertical-align:top; padding: 0; line-height: $header-height; - padding-left: $project-logo-size + $project-logo-pad-left; + padding-left: $project-logo-width + $project-logo-pad-left; background-position: 0 center; + @include transition(all 300ms ease-in); &:hover{ @include transition(all 300ms ease-in); @@ -106,6 +133,7 @@ } line{ stroke: black; + @include transition(all 300ms ease-in); &:hover{ @include transition(all 300ms ease-in); @@ -120,12 +148,12 @@ position: relative; svg path{ - @include transition( all 0.3s ease ); + @include transition( all 300ms ease-in ); } &:hover{ svg path{ - @include transition( all 0.3s ease ); + @include transition( all 300ms ease-in ); } } @@ -164,6 +192,6 @@ .nav > li > a:hover, .nav > li > a:focus { background-color: transparent; color: $black; - @include transition( color 0.3s ease ); + @include transition( all 300ms ease-in ); } } diff --git a/website/source/assets/stylesheets/hashicorp-shared/_hashicorp-utility.scss b/website/source/assets/stylesheets/hashicorp-shared/_hashicorp-utility.scss index be6c9f8fc6..38943f97c1 100755 --- a/website/source/assets/stylesheets/hashicorp-shared/_hashicorp-utility.scss +++ b/website/source/assets/stylesheets/hashicorp-shared/_hashicorp-utility.scss @@ -29,10 +29,10 @@ $nav-margin-right: 12px; padding: 0; line-height: 22px; @include hashi-a-style-core(); - @include transition( color 0.3s ease ); + @include transition( all 300ms ease-in ); &:hover{ - @include transition( color 0.3s ease ); + @include transition( all 300ms ease-in ); background-color: transparent; } } diff --git a/website/source/assets/stylesheets/hashicorp-shared/_project-utility.scss b/website/source/assets/stylesheets/hashicorp-shared/_project-utility.scss index 42910526ae..80368c2698 100755 --- a/website/source/assets/stylesheets/hashicorp-shared/_project-utility.scss +++ b/website/source/assets/stylesheets/hashicorp-shared/_project-utility.scss @@ -4,14 +4,17 @@ // -------------------------------------------------- // Variables -$project-logo-size: 40px; +$project-logo-width: 38px; +$project-logo-height: 40px; $project-logo-pad-left: 8px; // Mixins @mixin project-a-style{ color: $white; + font-weight: 300; + opacity: .75; &:hover{ - color: $purple; + color: $white; } } diff --git a/website/source/layouts/_sidebar.erb b/website/source/layouts/_sidebar.erb index 5a906142f1..9a13aab719 100644 --- a/website/source/layouts/_sidebar.erb +++ b/website/source/layouts/_sidebar.erb @@ -7,7 +7,7 @@ From 2d30d8f483fb22610045a2308ef5653b403677e0 Mon Sep 17 00:00:00 2001 From: captainill Date: Fri, 6 Nov 2015 15:45:02 -0800 Subject: [PATCH 044/173] fix incorrect link --- website/source/layouts/_sidebar.erb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/source/layouts/_sidebar.erb b/website/source/layouts/_sidebar.erb index 9a13aab719..757a0888e7 100644 --- a/website/source/layouts/_sidebar.erb +++ b/website/source/layouts/_sidebar.erb @@ -21,6 +21,6 @@ From 8a1bf06815f3a961c761f1df93bff178d9a1222f Mon Sep 17 00:00:00 2001 From: captainill Date: Fri, 6 Nov 2015 16:01:39 -0800 Subject: [PATCH 045/173] responsive cleanup --- .../source/assets/stylesheets/_header.scss | 24 +++++++++++++++ .../hashicorp-shared/_hashicorp-header.scss | 30 +++++++++++++++++++ .../hashicorp-shared/_hashicorp-utility.scss | 1 + 3 files changed, 55 insertions(+) diff --git a/website/source/assets/stylesheets/_header.scss b/website/source/assets/stylesheets/_header.scss index fa3f0754a6..551c20b852 100755 --- a/website/source/assets/stylesheets/_header.scss +++ b/website/source/assets/stylesheets/_header.scss @@ -79,3 +79,27 @@ body.page-sub{ } } } + +@media (max-width: 414px) { + #header { + .navbar-brand { + .logo{ + padding-left: 37px; + font-size: 18px; + @include img-retina("../images/logo-header.png", "../images/logo-header@2x.png", $project-logo-width * .75, $project-logo-height * .75); + //background-position: 0 45%; + } + } + } +} + + +@media (max-width: 320px) { + #header { + .navbar-brand { + .logo{ + font-size: 0 !important; //hide terraform text + } + } + } +} diff --git a/website/source/assets/stylesheets/hashicorp-shared/_hashicorp-header.scss b/website/source/assets/stylesheets/hashicorp-shared/_hashicorp-header.scss index f8b5974fba..da89db584f 100755 --- a/website/source/assets/stylesheets/hashicorp-shared/_hashicorp-header.scss +++ b/website/source/assets/stylesheets/hashicorp-shared/_hashicorp-header.scss @@ -195,3 +195,33 @@ @include transition( all 300ms ease-in ); } } + +@media (max-width: 414px) { + #header { + .navbar-header{ + .navbar-toggle{ + padding-top: 13px; + height: $header-mobile-height; + } + } + + .navbar-brand { + height: $header-mobile-height; + + .logo{ + height: $header-mobile-height; + line-height: $header-mobile-height; + } + .by{ + height: $header-mobile-height; + line-height: $header-mobile-height; + } + } + .main-links, + .external-links { + li > a { + line-height: $header-mobile-height; + } + } + } +} diff --git a/website/source/assets/stylesheets/hashicorp-shared/_hashicorp-utility.scss b/website/source/assets/stylesheets/hashicorp-shared/_hashicorp-utility.scss index 38943f97c1..c4105b112f 100755 --- a/website/source/assets/stylesheets/hashicorp-shared/_hashicorp-utility.scss +++ b/website/source/assets/stylesheets/hashicorp-shared/_hashicorp-utility.scss @@ -12,6 +12,7 @@ $header-font-family: $font-family-open-sans; $header-font-weight: 600; // semi-bold $header-height: 74px; +$header-mobile-height: 60px; $by-hashicorp-width: 74px; $by-hashicorp-height: 16px; $nav-margin-right: 12px; From 7f53f82f0d1534e96d7a12e4a0cd1b2be20f313e Mon Sep 17 00:00:00 2001 From: captainill Date: Fri, 6 Nov 2015 16:06:48 -0800 Subject: [PATCH 046/173] responsive tweak --- .../assets/stylesheets/hashicorp-shared/_hashicorp-header.scss | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/website/source/assets/stylesheets/hashicorp-shared/_hashicorp-header.scss b/website/source/assets/stylesheets/hashicorp-shared/_hashicorp-header.scss index da89db584f..1cfb9f8acc 100755 --- a/website/source/assets/stylesheets/hashicorp-shared/_hashicorp-header.scss +++ b/website/source/assets/stylesheets/hashicorp-shared/_hashicorp-header.scss @@ -200,7 +200,7 @@ #header { .navbar-header{ .navbar-toggle{ - padding-top: 13px; + padding-top: 10px; height: $header-mobile-height; } } @@ -215,6 +215,7 @@ .by{ height: $header-mobile-height; line-height: $header-mobile-height; + padding-top: 0; } } .main-links, From a727b0c415981afb9ff29d351b603aef120d7340 Mon Sep 17 00:00:00 2001 From: Joe Gross Date: Fri, 6 Nov 2015 19:02:05 -0800 Subject: [PATCH 047/173] add closing quote --- website/source/intro/getting-started/variables.html.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/source/intro/getting-started/variables.html.md b/website/source/intro/getting-started/variables.html.md index 41e828a724..24154ca25d 100644 --- a/website/source/intro/getting-started/variables.html.md +++ b/website/source/intro/getting-started/variables.html.md @@ -186,7 +186,7 @@ And access them via `lookup()`: ``` output "ami" { - value = "${lookup(var.amis, var.region)} + value = "${lookup(var.amis, var.region)}" } ``` From 98a441314b7aa631ef6753d14f32b07486bf6dc5 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Sat, 7 Nov 2015 04:01:50 +0000 Subject: [PATCH 048/173] provider/openstack: Make Security Groups Computed This commit makes security groups in the openstack_compute_instance_v2 resource computed. This fixes issues where a security group is omitted which causes the instance to have the "default" group applied. When re-applying without this patch, an error will occur. --- .../openstack/resource_openstack_compute_instance_v2.go | 1 + 1 file changed, 1 insertion(+) diff --git a/builtin/providers/openstack/resource_openstack_compute_instance_v2.go b/builtin/providers/openstack/resource_openstack_compute_instance_v2.go index 4cf68de038..d21e1afedb 100644 --- a/builtin/providers/openstack/resource_openstack_compute_instance_v2.go +++ b/builtin/providers/openstack/resource_openstack_compute_instance_v2.go @@ -95,6 +95,7 @@ func resourceComputeInstanceV2() *schema.Resource { Type: schema.TypeSet, Optional: true, ForceNew: false, + Computed: true, Elem: &schema.Schema{Type: schema.TypeString}, Set: schema.HashString, }, From b33c3fcd0dfcfc3c4a803ede96766cef4d65e1e4 Mon Sep 17 00:00:00 2001 From: captainill Date: Fri, 6 Nov 2015 21:01:14 -0800 Subject: [PATCH 049/173] whitespace --- .../source/assets/javascripts/app/Sidebar.js | 74 ++-- .../source/assets/stylesheets/_header.scss | 118 +++--- .../source/assets/stylesheets/_sidebar.scss | 24 +- .../hashicorp-shared/_hashicorp-header.scss | 358 ++++++++--------- .../hashicorp-shared/_hashicorp-sidebar.scss | 376 +++++++++--------- .../hashicorp-shared/_hashicorp-utility.scss | 56 +-- .../hashicorp-shared/_project-utility.scss | 12 +- 7 files changed, 509 insertions(+), 509 deletions(-) diff --git a/website/source/assets/javascripts/app/Sidebar.js b/website/source/assets/javascripts/app/Sidebar.js index bb5d4cc9e7..b36e508c4a 100644 --- a/website/source/assets/javascripts/app/Sidebar.js +++ b/website/source/assets/javascripts/app/Sidebar.js @@ -1,50 +1,50 @@ (function(){ -Sidebar = Base.extend({ + Sidebar = Base.extend({ - $body: null, - $overlay: null, - $sidebar: null, - $sidebarHeader: null, - $sidebarImg: null, - $toggleButton: null, + $body: null, + $overlay: null, + $sidebar: null, + $sidebarHeader: null, + $sidebarImg: null, + $toggleButton: null, - constructor: function(){ - this.$body = $('body'); - this.$overlay = $('.sidebar-overlay'); - this.$sidebar = $('#sidebar'); - this.$sidebarHeader = $('#sidebar .sidebar-header'); - this.$toggleButton = $('.navbar-toggle'); - this.sidebarImg = this.$sidebarHeader.css('background-image'); + constructor: function(){ + this.$body = $('body'); + this.$overlay = $('.sidebar-overlay'); + this.$sidebar = $('#sidebar'); + this.$sidebarHeader = $('#sidebar .sidebar-header'); + this.$toggleButton = $('.navbar-toggle'); + this.sidebarImg = this.$sidebarHeader.css('background-image'); - this.addEventListeners(); - }, + this.addEventListeners(); + }, - addEventListeners: function(){ - var _this = this; + addEventListeners: function(){ + var _this = this; - _this.$toggleButton.on('click', function() { - _this.$sidebar.toggleClass('open'); - if ((_this.$sidebar.hasClass('sidebar-fixed-left') || _this.$sidebar.hasClass('sidebar-fixed-right')) && _this.$sidebar.hasClass('open')) { - _this.$overlay.addClass('active'); - _this.$body.css('overflow', 'hidden'); - } else { - _this.$overlay.removeClass('active'); - _this.$body.css('overflow', 'auto'); - } + _this.$toggleButton.on('click', function() { + _this.$sidebar.toggleClass('open'); + if ((_this.$sidebar.hasClass('sidebar-fixed-left') || _this.$sidebar.hasClass('sidebar-fixed-right')) && _this.$sidebar.hasClass('open')) { + _this.$overlay.addClass('active'); + _this.$body.css('overflow', 'hidden'); + } else { + _this.$overlay.removeClass('active'); + _this.$body.css('overflow', 'auto'); + } - return false; - }); + return false; + }); - _this.$overlay.on('click', function() { - $(this).removeClass('active'); - _this.$body.css('overflow', 'auto'); - _this.$sidebar.removeClass('open'); - }); - } + _this.$overlay.on('click', function() { + $(this).removeClass('active'); + _this.$body.css('overflow', 'auto'); + _this.$sidebar.removeClass('open'); + }); + } -}); + }); -window.Sidebar = Sidebar; + window.Sidebar = Sidebar; })(); diff --git a/website/source/assets/stylesheets/_header.scss b/website/source/assets/stylesheets/_header.scss index 551c20b852..82a5cf69b8 100755 --- a/website/source/assets/stylesheets/_header.scss +++ b/website/source/assets/stylesheets/_header.scss @@ -5,79 +5,79 @@ // -------------------------------------------------- body.page-sub{ - #header{ - background-color: $purple; + #header{ + background-color: $purple; - .navbar-brand { - .logo{ - &:hover{ - color: $black; - } - } - .by{ - &:hover{ - svg{ - line{ - stroke: $black; - } - } - } - } + .navbar-brand { + .logo{ + &:hover{ + color: $black; } } + .by{ + &:hover{ + svg{ + line{ + stroke: $black; + } + } + } + } + } + } } #header { - .navbar-brand { - .logo{ - font-size: 20px; - text-transform: uppercase; - @include lato-light(); - background: image-url('../images/logo-header.png') 0 0 no-repeat; - @include img-retina("../images/logo-header.png", "../images/logo-header@2x.png", $project-logo-width, $project-logo-height); - background-position: 0 45%; + .navbar-brand { + .logo{ + font-size: 20px; + text-transform: uppercase; + @include lato-light(); + background: image-url('../images/logo-header.png') 0 0 no-repeat; + @include img-retina("../images/logo-header.png", "../images/logo-header@2x.png", $project-logo-width, $project-logo-height); + background-position: 0 45%; - &:hover{ - color: $purple; - } - } - - .by{ - color: $black; - &:hover{ - svg{ - line{ - stroke: $purple; - } - } - } - } + &:hover{ + color: $purple; + } } - .buttons{ - margin-top: 2px; //baseline everything - - ul.navbar-nav{ - li { - // &:hover{ - // svg path{ - // fill: $purple; - // } - // } - - svg path{ - fill: $white; - } - } + .by{ + color: $black; + &:hover{ + svg{ + line{ + stroke: $purple; + } } + } } + } - .main-links, - .external-links { - li > a { - @include project-a-style(); + .buttons{ + margin-top: 2px; //baseline everything + + ul.navbar-nav{ + li { + // &:hover{ + // svg path{ + // fill: $purple; + // } + // } + + svg path{ + fill: $white; } + } } + } + + .main-links, + .external-links { + li > a { + @include project-a-style(); + } + } } @media (max-width: 414px) { diff --git a/website/source/assets/stylesheets/_sidebar.scss b/website/source/assets/stylesheets/_sidebar.scss index 4c632d4808..45a4ee64fa 100644 --- a/website/source/assets/stylesheets/_sidebar.scss +++ b/website/source/assets/stylesheets/_sidebar.scss @@ -5,19 +5,19 @@ // -------------------------------------------------- .sidebar { - .sidebar-nav { - // Links - //---------------- - li { - a { - color: $black; + .sidebar-nav { + // Links + //---------------- + li { + a { + color: $black; - svg{ - path{ - fill: $black; - } - } - } + svg{ + path{ + fill: $black; + } } + } } + } } diff --git a/website/source/assets/stylesheets/hashicorp-shared/_hashicorp-header.scss b/website/source/assets/stylesheets/hashicorp-shared/_hashicorp-header.scss index 1cfb9f8acc..c9bc593b65 100755 --- a/website/source/assets/stylesheets/hashicorp-shared/_hashicorp-header.scss +++ b/website/source/assets/stylesheets/hashicorp-shared/_hashicorp-header.scss @@ -5,195 +5,195 @@ // -------------------------------------------------- #header { - position: relative; - color: black; - text-rendering: optimizeLegibility; - margin-bottom: 0; - transition: all 1s ease; - - &.white{ - color: white; - .navbar-brand { - .logo { - color: white; - } - - .by{ - color: white; - font-weight: 300; - svg{ - path, - polygon{ - fill: white; - } - line{ - stroke: white; - } - } - } - } - - .main-links, - .external-links { - li > a { - &:hover{ - opacity: 1; - } - } - } - - .nav > li > a:hover, .nav > li > a:focus { - color: white; - } - - .navbar-header{ - .navbar-toggle{ - .icon-bar{ - border: 1px solid white; - } - } - } - } - - .navbar-header{ - .navbar-toggle{ - height: $header-height; - margin: 0; - padding-right: 15px; - border-radius: 0; - .icon-bar{ - border: 1px solid $black; - border-radius: 0; - } - } - } + position: relative; + color: black; + text-rendering: optimizeLegibility; + margin-bottom: 0; + transition: all 1s ease; + &.white{ + color: white; .navbar-brand { - display: block; - height: $header-height; - padding: 0; - margin: 0 10px 0 0 ; + .logo { + color: white; + } - .logo{ - display: inline-block; - height: $header-height; - vertical-align:top; - padding: 0; - line-height: $header-height; - padding-left: $project-logo-width + $project-logo-pad-left; - background-position: 0 center; - @include transition(all 300ms ease-in); - - &:hover{ - @include transition(all 300ms ease-in); - text-decoration: none; - } + .by{ + color: white; + font-weight: 300; + svg{ + path, + polygon{ + fill: white; + } + line{ + stroke: white; + } } - - .by{ - display: inline-block; - vertical-align:top; - height: $header-height; - margin-left: 3px; - padding-top: 2px; - color: black; - line-height: $header-height; - font-family: $header-font-family; - font-weight: 600; - font-size: 0; - text-decoration: none; - - .svg-wrap{ - font-size: 13px; - } - - svg{ - &.svg-by{ - width: $by-hashicorp-width; - height: $by-hashicorp-height; - margin-bottom: -4px; - margin-left: 4px; - } - - &.svg-logo{ - width: 16px; - height: 16px; - margin-bottom: -3px; - margin-left: 4px; - } - - path, - polygon{ - fill: black; - @include transition(all 300ms ease-in); - - &:hover{ - @include transition(all 300ms ease-in); - } - } - line{ - stroke: black; - @include transition(all 300ms ease-in); - - &:hover{ - @include transition(all 300ms ease-in); - } - } - } - } - } - - .external-links { - li { - position: relative; - - svg path{ - @include transition( all 300ms ease-in ); - } - - &:hover{ - svg path{ - @include transition( all 300ms ease-in ); - } - } - - &.download{ - margin-right: 10px; - } - - >a { - padding-left: 12px !important; - svg{ - position: absolute; - left: -12px; - top: 50%; - margin-top: -7px; - width: 14px; - height: 14px; - } - } - } - } - - .main-links{ - margin-right: $nav-margin-right * 2; + } } .main-links, .external-links { - li > a { - @include hashi-a-style(); - margin: 0 10px; - padding-top: 1px; - line-height: $header-height; + li > a { + &:hover{ + opacity: 1; } + } } .nav > li > a:hover, .nav > li > a:focus { - background-color: transparent; - color: $black; - @include transition( all 300ms ease-in ); + color: white; } + + .navbar-header{ + .navbar-toggle{ + .icon-bar{ + border: 1px solid white; + } + } + } + } + + .navbar-header{ + .navbar-toggle{ + height: $header-height; + margin: 0; + padding-right: 15px; + border-radius: 0; + .icon-bar{ + border: 1px solid $black; + border-radius: 0; + } + } + } + + .navbar-brand { + display: block; + height: $header-height; + padding: 0; + margin: 0 10px 0 0 ; + + .logo{ + display: inline-block; + height: $header-height; + vertical-align:top; + padding: 0; + line-height: $header-height; + padding-left: $project-logo-width + $project-logo-pad-left; + background-position: 0 center; + @include transition(all 300ms ease-in); + + &:hover{ + @include transition(all 300ms ease-in); + text-decoration: none; + } + } + + .by{ + display: inline-block; + vertical-align:top; + height: $header-height; + margin-left: 3px; + padding-top: 2px; + color: black; + line-height: $header-height; + font-family: $header-font-family; + font-weight: 600; + font-size: 0; + text-decoration: none; + + .svg-wrap{ + font-size: 13px; + } + + svg{ + &.svg-by{ + width: $by-hashicorp-width; + height: $by-hashicorp-height; + margin-bottom: -4px; + margin-left: 4px; + } + + &.svg-logo{ + width: 16px; + height: 16px; + margin-bottom: -3px; + margin-left: 4px; + } + + path, + polygon{ + fill: black; + @include transition(all 300ms ease-in); + + &:hover{ + @include transition(all 300ms ease-in); + } + } + line{ + stroke: black; + @include transition(all 300ms ease-in); + + &:hover{ + @include transition(all 300ms ease-in); + } + } + } + } + } + + .external-links { + li { + position: relative; + + svg path{ + @include transition( all 300ms ease-in ); + } + + &:hover{ + svg path{ + @include transition( all 300ms ease-in ); + } + } + + &.download{ + margin-right: 10px; + } + + > a { + padding-left: 12px !important; + svg{ + position: absolute; + left: -12px; + top: 50%; + margin-top: -7px; + width: 14px; + height: 14px; + } + } + } + } + + .main-links{ + margin-right: $nav-margin-right * 2; + } + + .main-links, + .external-links { + li > a { + @include hashi-a-style(); + margin: 0 10px; + padding-top: 1px; + line-height: $header-height; + } + } + + .nav > li > a:hover, .nav > li > a:focus { + background-color: transparent; + color: $black; + @include transition( all 300ms ease-in ); + } } @media (max-width: 414px) { @@ -220,9 +220,9 @@ } .main-links, .external-links { - li > a { - line-height: $header-mobile-height; - } + li > a { + line-height: $header-mobile-height; + } } } } diff --git a/website/source/assets/stylesheets/hashicorp-shared/_hashicorp-sidebar.scss b/website/source/assets/stylesheets/hashicorp-shared/_hashicorp-sidebar.scss index 4a5a6ae07f..99f77f6c52 100644 --- a/website/source/assets/stylesheets/hashicorp-shared/_hashicorp-sidebar.scss +++ b/website/source/assets/stylesheets/hashicorp-shared/_hashicorp-sidebar.scss @@ -38,70 +38,70 @@ $sidebar-icon-width: 40px; $sidebar-icon-height: 20px; @mixin sidebar-nav-base { - text-align: center; + text-align: center; - &:last-child{ - border-bottom: none; - } + &:last-child{ + border-bottom: none; + } - li > a { - background-color: $link-bg; - } - li:hover > a { - background-color: $link-hover-bg; - } - li:focus > a, li > a:focus { - background-color: $link-bg; - } + li > a { + background-color: $link-bg; + } + li:hover > a { + background-color: $link-hover-bg; + } + li:focus > a, li > a:focus { + background-color: $link-bg; + } - > .open > a { + > .open > a { + &, + &:hover, + &:focus { + background-color: $link-hover-bg; + } + } + + > .active > a { + &, + &:hover, + &:focus { + background-color: $link-active-bg; + } + } + > .disabled > a { + &, + &:hover, + &:focus { + background-color: $link-disabled-bg; + } + } + + // Dropdown menu items + > .dropdown { + // Remove background color from open dropdown + > .dropdown-menu { + background-color: $link-hover-bg; + + > li > a { + &:focus { + background-color: $link-hover-bg; + } + &:hover { + background-color: $link-hover-bg; + } + } + + > .active > a { &, &:hover, &:focus { - background-color: $link-hover-bg; - } - } - - > .active > a { - &, - &:hover, - &:focus { - background-color: $link-active-bg; - } - } - > .disabled > a { - &, - &:hover, - &:focus { - background-color: $link-disabled-bg; - } - } - - // Dropdown menu items - > .dropdown { - // Remove background color from open dropdown - > .dropdown-menu { - background-color: $link-hover-bg; - - > li > a { - &:focus { - background-color: $link-hover-bg; - } - &:hover { - background-color: $link-hover-bg; - } - } - - > .active > a { - &, - &:hover, - &:focus { - color: $link-active-color; - background-color: $link-active-bg; - } - } + color: $link-active-color; + background-color: $link-active-bg; } + } } + } } // @@ -112,182 +112,182 @@ $sidebar-icon-height: 20px; // // Basic style of sidebar elements .sidebar { + position: relative; + display: block; + min-height: 100%; + overflow-y: auto; + overflow-x: hidden; + border: none; + @include transition(all 0.5s cubic-bezier(0.55, 0, 0.1, 1)); + @include clearfix(); + background-color: $white; + + ul{ + padding-left: 0; + list-style-type: none; + } + + .sidebar-divider, .divider { + width: 80%; + height: 1px; + margin: 8px auto; + background-color: lighten($gray, 20%); + } + + // Sidebar heading + //---------------- + .sidebar-header { position: relative; - display: block; - min-height: 100%; - overflow-y: auto; - overflow-x: hidden; - border: none; - @include transition(all 0.5s cubic-bezier(0.55, 0, 0.1, 1)); - @include clearfix(); - background-color: $white; + margin-bottom: $sidebar-padding; + @include transition(all .2s ease-in-out); + } - ul{ - padding-left: 0; - list-style-type: none; + .sidebar-image { + padding-top: 24px; + img { + display: block; + margin: 0 auto; } + } - .sidebar-divider, .divider { - width: 80%; - height: 1px; - margin: 8px auto; - background-color: lighten($gray, 20%); + + // Sidebar icons + //---------------- + .sidebar-icon { + display: inline-block; + height: $sidebar-icon-height; + margin-right: $sidebar-divider; + text-align: left; + font-size: $sidebar-icon-height; + vertical-align: middle; + + &:before, &:after { + vertical-align: middle; } + } - // Sidebar heading + .sidebar-nav { + margin: 0; + padding: 0; + + @include sidebar-nav-base(); + + // Links //---------------- - .sidebar-header { + li { + position: relative; + list-style-type: none; + text-align: center; + + a { position: relative; - margin-bottom: $sidebar-padding; - @include transition(all .2s ease-in-out); - } + cursor: pointer; + user-select: none; + @include hashi-a-style-core(); - .sidebar-image { - padding-top: 24px; - img { - display: block; - margin: 0 auto; - } - } - - - // Sidebar icons - //---------------- - .sidebar-icon { - display: inline-block; - height: $sidebar-icon-height; - margin-right: $sidebar-divider; - text-align: left; - font-size: $sidebar-icon-height; - vertical-align: middle; - - &:before, &:after { - vertical-align: middle; - } - } - - .sidebar-nav { - margin: 0; - padding: 0; - - @include sidebar-nav-base(); - - // Links - //---------------- - li { - position: relative; - list-style-type: none; - text-align: center; - - a { - position: relative; - cursor: pointer; - user-select: none; - @include hashi-a-style-core(); - - svg{ - top: 2px; - width: 14px; - height: 14px; - margin-bottom: -2px; - margin-right: 4px; - } - } + svg{ + top: 2px; + width: 14px; + height: 14px; + margin-bottom: -2px; + margin-right: 4px; } + } } + } } // Sidebar toggling // // Hide sidebar .sidebar { - width: 0; - @include translate3d(-$sidebar-desktop-width, 0, 0); + width: 0; + @include translate3d(-$sidebar-desktop-width, 0, 0); - &.open { - min-width: $sidebar-desktop-width; - width: $sidebar-desktop-width; - @include translate3d(0, 0, 0); - } + &.open { + min-width: $sidebar-desktop-width; + width: $sidebar-desktop-width; + @include translate3d(0, 0, 0); + } } // Sidebar positions: fix the left/right sidebars .sidebar-fixed-left, .sidebar-fixed-right, .sidebar-stacked { - position: fixed; - top: 0; - bottom: 0; - z-index: $zindex-sidebar-fixed; + position: fixed; + top: 0; + bottom: 0; + z-index: $zindex-sidebar-fixed; } .sidebar-stacked { - left: 0; + left: 0; } .sidebar-fixed-left { - left: 0; - box-shadow: 2px 0px 25px rgba(0,0,0,0.15); - -webkit-box-shadow: 2px 0px 25px rgba(0,0,0,0.15); + left: 0; + box-shadow: 2px 0px 25px rgba(0,0,0,0.15); + -webkit-box-shadow: 2px 0px 25px rgba(0,0,0,0.15); } .sidebar-fixed-right { - right: 0; - box-shadow: 0px 2px 25px rgba(0,0,0,0.15); - -webkit-box-shadow: 0px 2px 25px rgba(0,0,0,0.15); + right: 0; + box-shadow: 0px 2px 25px rgba(0,0,0,0.15); + -webkit-box-shadow: 0px 2px 25px rgba(0,0,0,0.15); - @include translate3d($sidebar-desktop-width, 0, 0); - &.open { - @include translate3d(0, 0, 0); - } - .icon-material-sidebar-arrow:before { - content: "\e614"; // icon-material-arrow-forward - } + @include translate3d($sidebar-desktop-width, 0, 0); + &.open { + @include translate3d(0, 0, 0); + } + .icon-material-sidebar-arrow:before { + content: "\e614"; // icon-material-arrow-forward + } } // Sidebar size // // Change size of sidebar and sidebar elements on small screens @media (max-width: $screen-tablet) { - .sidebar.open { - min-width: $sidebar-width; - width: $sidebar-width; - } + .sidebar.open { + min-width: $sidebar-width; + width: $sidebar-width; + } - .sidebar .sidebar-header { - //height: $sidebar-width * 9/16; // 16:9 header dimension - } + .sidebar .sidebar-header { + //height: $sidebar-width * 9/16; // 16:9 header dimension + } - .sidebar .sidebar-image { - /* img { - width: $sidebar-width/4 - $sidebar-padding; - height: $sidebar-width/4 - $sidebar-padding; - } */ - } + .sidebar .sidebar-image { + /* img { + width: $sidebar-width/4 - $sidebar-padding; + height: $sidebar-width/4 - $sidebar-padding; + } */ + } } .sidebar-overlay { - visibility: hidden; - position: fixed; - top: 0; - left: 0; - right: 0; - bottom: 0; - opacity: 0; - background: $white; - z-index: $zindex-sidebar-fixed - 1; + visibility: hidden; + position: fixed; + top: 0; + left: 0; + right: 0; + bottom: 0; + opacity: 0; + background: $white; + z-index: $zindex-sidebar-fixed - 1; - -webkit-transition: visibility 0 linear .4s,opacity .4s cubic-bezier(.4,0,.2,1); - -moz-transition: visibility 0 linear .4s,opacity .4s cubic-bezier(.4,0,.2,1); - transition: visibility 0 linear .4s,opacity .4s cubic-bezier(.4,0,.2,1); - -webkit-transform: translateZ(0); - -moz-transform: translateZ(0); - -ms-transform: translateZ(0); - -o-transform: translateZ(0); - transform: translateZ(0); + -webkit-transition: visibility 0 linear .4s,opacity .4s cubic-bezier(.4,0,.2,1); + -moz-transition: visibility 0 linear .4s,opacity .4s cubic-bezier(.4,0,.2,1); + transition: visibility 0 linear .4s,opacity .4s cubic-bezier(.4,0,.2,1); + -webkit-transform: translateZ(0); + -moz-transform: translateZ(0); + -ms-transform: translateZ(0); + -o-transform: translateZ(0); + transform: translateZ(0); } .sidebar-overlay.active { - opacity: 0.3; - visibility: visible; - -webkit-transition-delay: 0; - -moz-transition-delay: 0; - transition-delay: 0; + opacity: 0.3; + visibility: visible; + -webkit-transition-delay: 0; + -moz-transition-delay: 0; + transition-delay: 0; } diff --git a/website/source/assets/stylesheets/hashicorp-shared/_hashicorp-utility.scss b/website/source/assets/stylesheets/hashicorp-shared/_hashicorp-utility.scss index c4105b112f..de17e9815d 100755 --- a/website/source/assets/stylesheets/hashicorp-shared/_hashicorp-utility.scss +++ b/website/source/assets/stylesheets/hashicorp-shared/_hashicorp-utility.scss @@ -19,23 +19,23 @@ $nav-margin-right: 12px; // Mixins @mixin hashi-a-style-core{ - font-family: $header-font-family; - font-weight: $header-font-weight; - font-size: 14px; - //letter-spacing: 0.0625em; + font-family: $header-font-family; + font-weight: $header-font-weight; + font-size: 14px; + //letter-spacing: 0.0625em; } @mixin hashi-a-style{ - margin: 0 15px; - padding: 0; - line-height: 22px; - @include hashi-a-style-core(); - @include transition( all 300ms ease-in ); + margin: 0 15px; + padding: 0; + line-height: 22px; + @include hashi-a-style-core(); + @include transition( all 300ms ease-in ); - &:hover{ - @include transition( all 300ms ease-in ); - background-color: transparent; - } + &:hover{ + @include transition( all 300ms ease-in ); + background-color: transparent; + } } //general shared project mixins @@ -45,10 +45,10 @@ $nav-margin-right: 12px; background-repeat: no-repeat; @media (min--moz-device-pixel-ratio: 1.3), - (-o-min-device-pixel-ratio: 2.6/2), - (-webkit-min-device-pixel-ratio: 1.3), - (min-device-pixel-ratio: 1.3), - (min-resolution: 1.3dppx) { + (-o-min-device-pixel-ratio: 2.6/2), + (-webkit-min-device-pixel-ratio: 1.3), + (min-device-pixel-ratio: 1.3), + (min-resolution: 1.3dppx) { /* on retina, use image that's scaled by 2 */ background-image: url($image); background-size: $width $height; @@ -58,30 +58,30 @@ $nav-margin-right: 12px; // // ------------------------- @mixin anti-alias() { - text-rendering: optimizeLegibility; - -webkit-font-smoothing: antialiased; + text-rendering: optimizeLegibility; + -webkit-font-smoothing: antialiased; } @mixin open-light() { - font-family: $font-family-open-sans; - font-weight: 300; + font-family: $font-family-open-sans; + font-weight: 300; } @mixin open() { - font-family: $font-family-open-sans; - font-weight: 400; + font-family: $font-family-open-sans; + font-weight: 400; } @mixin open-sb() { - font-family: $font-family-open-sans; - font-weight: 600; + font-family: $font-family-open-sans; + font-weight: 600; } @mixin open-bold() { - font-family: $font-family-open-sans; - font-weight: 700; + font-family: $font-family-open-sans; + font-weight: 700; } @mixin bez-1-transition{ - @include transition( all 300ms ease-in-out ); + @include transition( all 300ms ease-in-out ); } diff --git a/website/source/assets/stylesheets/hashicorp-shared/_project-utility.scss b/website/source/assets/stylesheets/hashicorp-shared/_project-utility.scss index 80368c2698..1a511152df 100755 --- a/website/source/assets/stylesheets/hashicorp-shared/_project-utility.scss +++ b/website/source/assets/stylesheets/hashicorp-shared/_project-utility.scss @@ -10,11 +10,11 @@ $project-logo-pad-left: 8px; // Mixins @mixin project-a-style{ - color: $white; - font-weight: 300; - opacity: .75; + color: $white; + font-weight: 300; + opacity: .75; - &:hover{ - color: $white; - } + &:hover{ + color: $white; + } } From 4d640c6528fc7557f78f02dbe6ebbe6071000dc0 Mon Sep 17 00:00:00 2001 From: Paul Forman Date: Sat, 7 Nov 2015 00:25:07 -0700 Subject: [PATCH 050/173] providers/aws: add name_prefix option to launch config See #2911. This adds a `name_prefix` option to `aws_launch_configuration` resources. When specified, it is used instead of `terraform-` as the prefix for the launch configuration. It conflicts with `name`, so existing functionality is unchanged. `name` still sets the name explicitly. Added an acceptance test, and updated the site documentation. --- .../aws/resource_aws_launch_configuration.go | 27 ++++++++++++++++--- .../resource_aws_launch_configuration_test.go | 18 +++++++++++++ .../aws/r/launch_configuration.html.markdown | 10 ++++--- 3 files changed, 48 insertions(+), 7 deletions(-) diff --git a/builtin/providers/aws/resource_aws_launch_configuration.go b/builtin/providers/aws/resource_aws_launch_configuration.go index 9b464e5d2a..1cc010634e 100644 --- a/builtin/providers/aws/resource_aws_launch_configuration.go +++ b/builtin/providers/aws/resource_aws_launch_configuration.go @@ -26,10 +26,11 @@ func resourceAwsLaunchConfiguration() *schema.Resource { Schema: map[string]*schema.Schema{ "name": &schema.Schema{ - Type: schema.TypeString, - Optional: true, - Computed: true, - ForceNew: true, + Type: schema.TypeString, + Optional: true, + Computed: true, + ForceNew: true, + ConflictsWith: []string{"name_prefix"}, ValidateFunc: func(v interface{}, k string) (ws []string, errors []error) { // https://github.com/boto/botocore/blob/9f322b1/botocore/data/autoscaling/2011-01-01/service-2.json#L1932-L1939 value := v.(string) @@ -41,6 +42,22 @@ func resourceAwsLaunchConfiguration() *schema.Resource { }, }, + "name_prefix": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + ForceNew: true, + ValidateFunc: func(v interface{}, k string) (ws []string, errors []error) { + // https://github.com/boto/botocore/blob/9f322b1/botocore/data/autoscaling/2011-01-01/service-2.json#L1932-L1939 + // uuid is 26 characters, limit the prefix to 229. + value := v.(string) + if len(value) > 229 { + errors = append(errors, fmt.Errorf( + "%q cannot be longer than 229 characters, name is limited to 255", k)) + } + return + }, + }, + "image_id": &schema.Schema{ Type: schema.TypeString, Required: true, @@ -386,6 +403,8 @@ func resourceAwsLaunchConfigurationCreate(d *schema.ResourceData, meta interface var lcName string if v, ok := d.GetOk("name"); ok { lcName = v.(string) + } else if v, ok := d.GetOk("name_prefix"); ok { + lcName = resource.PrefixedUniqueId(v.(string)) } else { lcName = resource.UniqueId() } diff --git a/builtin/providers/aws/resource_aws_launch_configuration_test.go b/builtin/providers/aws/resource_aws_launch_configuration_test.go index f8d4d89783..c6a0086a14 100644 --- a/builtin/providers/aws/resource_aws_launch_configuration_test.go +++ b/builtin/providers/aws/resource_aws_launch_configuration_test.go @@ -30,6 +30,14 @@ func TestAccAWSLaunchConfiguration_basic(t *testing.T) { "aws_launch_configuration.bar", "terraform-"), ), }, + resource.TestStep{ + Config: testAccAWSLaunchConfigurationPrefixNameConfig, + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSLaunchConfigurationExists("aws_launch_configuration.baz", &conf), + testAccCheckAWSLaunchConfigurationGeneratedNamePrefix( + "aws_launch_configuration.baz", "baz-"), + ), + }, }, }) } @@ -255,3 +263,13 @@ resource "aws_launch_configuration" "bar" { associate_public_ip_address = false } ` + +const testAccAWSLaunchConfigurationPrefixNameConfig = ` +resource "aws_launch_configuration" "baz" { + name_prefix = "baz-" + image_id = "ami-21f78e11" + instance_type = "t1.micro" + user_data = "foobar-user-data-change" + associate_public_ip_address = false +} +` diff --git a/website/source/docs/providers/aws/r/launch_configuration.html.markdown b/website/source/docs/providers/aws/r/launch_configuration.html.markdown index 8492640184..413f1b4a1e 100644 --- a/website/source/docs/providers/aws/r/launch_configuration.html.markdown +++ b/website/source/docs/providers/aws/r/launch_configuration.html.markdown @@ -26,11 +26,13 @@ Launch Configurations cannot be updated after creation with the Amazon Web Service API. In order to update a Launch Configuration, Terraform will destroy the existing resource and create a replacement. In order to effectively use a Launch Configuration resource with an [AutoScaling Group resource][1], -it's recommend to omit the Launch Configuration `name` attribute, and -specify `create_before_destroy` in a [lifecycle][2] block, as shown: +it's recommended to specify `create_before_destroy` in a [lifecycle][2] block. +Either omit the Launch Configuration `name` attribute, or specify a partial name +with `name_prefix`. Example: ``` resource "aws_launch_configuration" "as_conf" { + name_prefix = "terraform-lc-example-" image_id = "ami-1234" instance_type = "m1.small" @@ -87,7 +89,9 @@ resource "aws_autoscaling_group" "bar" { The following arguments are supported: * `name` - (Optional) The name of the launch configuration. If you leave - this blank, Terraform will auto-generate it. + this blank, Terraform will auto-generate a unique name. +* `name_prefix` - (Optional) Creates a unique name beginning with the specified + prefix. Conflicts with `name`. * `image_id` - (Required) The EC2 image ID to launch. * `instance_type` - (Required) The size of instance to launch. * `iam_instance_profile` - (Optional) The IAM instance profile to associate From ad1c28990c37324575a1249a5f021ae45255a327 Mon Sep 17 00:00:00 2001 From: Eddie Forson Date: Sat, 7 Nov 2015 13:35:21 +0000 Subject: [PATCH 051/173] providers/google: add pubsub auth endpoint #3803 --- builtin/providers/google/service_scope.go | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/builtin/providers/google/service_scope.go b/builtin/providers/google/service_scope.go index d4c5181251..5770dbeaa1 100644 --- a/builtin/providers/google/service_scope.go +++ b/builtin/providers/google/service_scope.go @@ -11,6 +11,7 @@ func canonicalizeServiceScope(scope string) string { "datastore": "https://www.googleapis.com/auth/datastore", "logging-write": "https://www.googleapis.com/auth/logging.write", "monitoring": "https://www.googleapis.com/auth/monitoring", + "pubsub": "https://www.googleapis.com/auth/pubsub", "sql": "https://www.googleapis.com/auth/sqlservice", "sql-admin": "https://www.googleapis.com/auth/sqlservice.admin", "storage-full": "https://www.googleapis.com/auth/devstorage.full_control", @@ -22,9 +23,9 @@ func canonicalizeServiceScope(scope string) string { "userinfo-email": "https://www.googleapis.com/auth/userinfo.email", } - if matchedUrl, ok := scopeMap[scope]; ok { - return matchedUrl - } else { - return scope + if matchedURL, ok := scopeMap[scope]; ok { + return matchedURL } + + return scope } From dc96d7e61bd1c12897060cca1a94eec077887321 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Sat, 7 Nov 2015 12:32:22 -0700 Subject: [PATCH 052/173] Update CHANGELOG.md --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1399eca4fb..a472b61bd8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -60,6 +60,8 @@ BUG FIXES: * provider/openstack: Fix boot from volume [GH-3206] * provider/openstack: Fix crashing when image is no longer accessible [GH-2189] * provider/openstack: Better handling of network resource state changes [GH-3712] + * provider/openstack: Fix issue preventing security group rules from being removed [GH-3796] + * provider/openstack: Fix crashing when no security group is specified [GH-3801] ## 0.6.6 (October 23, 2015) From 02f512d4bd665c2983a22af8d51d368c509049c1 Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Sat, 7 Nov 2015 16:53:42 -0800 Subject: [PATCH 053/173] config: new HCL API --- config/loader.go | 2 +- config/loader_hcl.go | 519 +++++++++++++++++++++---------------------- 2 files changed, 249 insertions(+), 272 deletions(-) diff --git a/config/loader.go b/config/loader.go index 5711ce8ef8..c9a1295fe1 100644 --- a/config/loader.go +++ b/config/loader.go @@ -25,7 +25,7 @@ func LoadJSON(raw json.RawMessage) (*Config, error) { // Start building the result hclConfig := &hclConfigurable{ - Object: obj, + Root: obj, } return hclConfig.Config() diff --git a/config/loader_hcl.go b/config/loader_hcl.go index f451a31d15..07b0eef84a 100644 --- a/config/loader_hcl.go +++ b/config/loader_hcl.go @@ -5,15 +5,15 @@ import ( "io/ioutil" "github.com/hashicorp/hcl" - hclobj "github.com/hashicorp/hcl/hcl" + "github.com/hashicorp/hcl/hcl/ast" "github.com/mitchellh/mapstructure" ) // hclConfigurable is an implementation of configurable that knows // how to turn HCL configuration into a *Config object. type hclConfigurable struct { - File string - Object *hclobj.Object + File string + Root *ast.File } func (t *hclConfigurable) Config() (*Config, error) { @@ -36,7 +36,13 @@ func (t *hclConfigurable) Config() (*Config, error) { Variable map[string]*hclVariable } - if err := hcl.DecodeObject(&rawConfig, t.Object); err != nil { + // Top-level item should be the object list + list, ok := t.Root.Node.(*ast.ObjectList) + if !ok { + return nil, fmt.Errorf("error parsing: file doesn't contain a root object") + } + + if err := hcl.DecodeObject(&rawConfig, list); err != nil { return nil, err } @@ -73,7 +79,7 @@ func (t *hclConfigurable) Config() (*Config, error) { } // Get Atlas configuration - if atlas := t.Object.Get("atlas", false); atlas != nil { + if atlas := list.Filter("atlas"); len(atlas.Items) > 0 { var err error config.Atlas, err = loadAtlasHcl(atlas) if err != nil { @@ -82,7 +88,7 @@ func (t *hclConfigurable) Config() (*Config, error) { } // Build the modules - if modules := t.Object.Get("module", false); modules != nil { + if modules := list.Filter("module"); len(modules.Items) > 0 { var err error config.Modules, err = loadModulesHcl(modules) if err != nil { @@ -91,7 +97,7 @@ func (t *hclConfigurable) Config() (*Config, error) { } // Build the provider configs - if providers := t.Object.Get("provider", false); providers != nil { + if providers := list.Filter("provider"); len(providers.Items) > 0 { var err error config.ProviderConfigs, err = loadProvidersHcl(providers) if err != nil { @@ -100,7 +106,7 @@ func (t *hclConfigurable) Config() (*Config, error) { } // Build the resources - if resources := t.Object.Get("resource", false); resources != nil { + if resources := list.Filter("resource"); len(resources.Items) > 0 { var err error config.Resources, err = loadResourcesHcl(resources) if err != nil { @@ -109,7 +115,7 @@ func (t *hclConfigurable) Config() (*Config, error) { } // Build the outputs - if outputs := t.Object.Get("output", false); outputs != nil { + if outputs := list.Filter("output"); len(outputs.Items) > 0 { var err error config.Outputs, err = loadOutputsHcl(outputs) if err != nil { @@ -118,8 +124,13 @@ func (t *hclConfigurable) Config() (*Config, error) { } // Check for invalid keys - for _, elem := range t.Object.Elem(true) { - k := elem.Key + for _, item := range list.Items { + if len(item.Keys) == 0 { + // Not sure how this would happen, but let's avoid a panic + continue + } + + k := item.Keys[0].Token.Value().(string) if _, ok := validKeys[k]; ok { continue } @@ -133,8 +144,6 @@ func (t *hclConfigurable) Config() (*Config, error) { // loadFileHcl is a fileLoaderFunc that knows how to read HCL // files and turn them into hclConfigurables. func loadFileHcl(root string) (configurable, []string, error) { - var obj *hclobj.Object = nil - // Read the HCL file and prepare for parsing d, err := ioutil.ReadFile(root) if err != nil { @@ -143,7 +152,7 @@ func loadFileHcl(root string) (configurable, []string, error) { } // Parse it - obj, err = hcl.Parse(string(d)) + hclRoot, err := hcl.Parse(string(d)) if err != nil { return nil, nil, fmt.Errorf( "Error parsing %s: %s", root, err) @@ -151,8 +160,8 @@ func loadFileHcl(root string) (configurable, []string, error) { // Start building the result result := &hclConfigurable{ - File: root, - Object: obj, + File: root, + Root: hclRoot, } // Dive in, find the imports. This is disabled for now since @@ -200,9 +209,16 @@ func loadFileHcl(root string) (configurable, []string, error) { // Given a handle to a HCL object, this transforms it into the Atlas // configuration. -func loadAtlasHcl(obj *hclobj.Object) (*AtlasConfig, error) { +func loadAtlasHcl(list *ast.ObjectList) (*AtlasConfig, error) { + if len(list.Items) > 1 { + return nil, fmt.Errorf("only one 'atlas' block allowed") + } + + // Get our one item + item := list.Items[0] + var config AtlasConfig - if err := hcl.DecodeObject(&config, obj); err != nil { + if err := hcl.DecodeObject(&config, item.Val); err != nil { return nil, fmt.Errorf( "Error reading atlas config: %s", err) @@ -217,18 +233,10 @@ func loadAtlasHcl(obj *hclobj.Object) (*AtlasConfig, error) { // The resulting modules may not be unique, but each module // represents exactly one module definition in the HCL configuration. // We leave it up to another pass to merge them together. -func loadModulesHcl(os *hclobj.Object) ([]*Module, error) { - var allNames []*hclobj.Object - - // See loadResourcesHcl for why this exists. Don't touch this. - for _, o1 := range os.Elem(false) { - // Iterate the inner to get the list of types - for _, o2 := range o1.Elem(true) { - // Iterate all of this type to get _all_ the types - for _, o3 := range o2.Elem(false) { - allNames = append(allNames, o3) - } - } +func loadModulesHcl(list *ast.ObjectList) ([]*Module, error) { + list = list.Children() + if len(list.Items) == 0 { + return nil, nil } // Where all the results will go @@ -236,11 +244,18 @@ func loadModulesHcl(os *hclobj.Object) ([]*Module, error) { // Now go over all the types and their children in order to get // all of the actual resources. - for _, obj := range allNames { - k := obj.Key + for _, item := range list.Items { + k := item.Keys[0].Token.Value().(string) + + var listVal *ast.ObjectList + if ot, ok := item.Val.(*ast.ObjectType); ok { + listVal = ot.List + } else { + return nil, fmt.Errorf("module '%s': should be an object", k) + } var config map[string]interface{} - if err := hcl.DecodeObject(&config, obj); err != nil { + if err := hcl.DecodeObject(&config, item.Val); err != nil { return nil, fmt.Errorf( "Error reading config for %s: %s", k, @@ -260,8 +275,8 @@ func loadModulesHcl(os *hclobj.Object) ([]*Module, error) { // If we have a count, then figure it out var source string - if o := obj.Get("source", false); o != nil { - err = hcl.DecodeObject(&source, o) + if o := listVal.Filter("source"); len(o.Items) > 0 { + err = hcl.DecodeObject(&source, o.Items[0].Val) if err != nil { return nil, fmt.Errorf( "Error parsing source for %s: %s", @@ -282,27 +297,19 @@ func loadModulesHcl(os *hclobj.Object) ([]*Module, error) { // LoadOutputsHcl recurses into the given HCL object and turns // it into a mapping of outputs. -func loadOutputsHcl(os *hclobj.Object) ([]*Output, error) { - objects := make(map[string]*hclobj.Object) - - // Iterate over all the "output" blocks and get the keys along with - // their raw configuration objects. We'll parse those later. - for _, o1 := range os.Elem(false) { - for _, o2 := range o1.Elem(true) { - objects[o2.Key] = o2 - } - } - - if len(objects) == 0 { +func loadOutputsHcl(list *ast.ObjectList) ([]*Output, error) { + list = list.Children() + if len(list.Items) == 0 { return nil, nil } // Go through each object and turn it into an actual result. - result := make([]*Output, 0, len(objects)) - for n, o := range objects { - var config map[string]interface{} + result := make([]*Output, 0, len(list.Items)) + for _, item := range list.Items { + n := item.Keys[0].Token.Value().(string) - if err := hcl.DecodeObject(&config, o); err != nil { + var config map[string]interface{} + if err := hcl.DecodeObject(&config, item.Val); err != nil { return nil, err } @@ -325,27 +332,26 @@ func loadOutputsHcl(os *hclobj.Object) ([]*Output, error) { // LoadProvidersHcl recurses into the given HCL object and turns // it into a mapping of provider configs. -func loadProvidersHcl(os *hclobj.Object) ([]*ProviderConfig, error) { - var objects []*hclobj.Object - - // Iterate over all the "provider" blocks and get the keys along with - // their raw configuration objects. We'll parse those later. - for _, o1 := range os.Elem(false) { - for _, o2 := range o1.Elem(true) { - objects = append(objects, o2) - } - } - - if len(objects) == 0 { +func loadProvidersHcl(list *ast.ObjectList) ([]*ProviderConfig, error) { + list = list.Children() + if len(list.Items) == 0 { return nil, nil } // Go through each object and turn it into an actual result. - result := make([]*ProviderConfig, 0, len(objects)) - for _, o := range objects { - var config map[string]interface{} + result := make([]*ProviderConfig, 0, len(list.Items)) + for _, item := range list.Items { + n := item.Keys[0].Token.Value().(string) - if err := hcl.DecodeObject(&config, o); err != nil { + var listVal *ast.ObjectList + if ot, ok := item.Val.(*ast.ObjectType); ok { + listVal = ot.List + } else { + return nil, fmt.Errorf("module '%s': should be an object", n) + } + + var config map[string]interface{} + if err := hcl.DecodeObject(&config, item.Val); err != nil { return nil, err } @@ -355,24 +361,24 @@ func loadProvidersHcl(os *hclobj.Object) ([]*ProviderConfig, error) { if err != nil { return nil, fmt.Errorf( "Error reading config for provider config %s: %s", - o.Key, + n, err) } // If we have an alias field, then add those in var alias string - if a := o.Get("alias", false); a != nil { - err := hcl.DecodeObject(&alias, a) + if a := listVal.Filter("alias"); len(a.Items) > 0 { + err := hcl.DecodeObject(&alias, a.Items[0].Val) if err != nil { return nil, fmt.Errorf( "Error reading alias for provider[%s]: %s", - o.Key, + n, err) } } result = append(result, &ProviderConfig{ - Name: o.Key, + Name: n, Alias: alias, RawConfig: rawConfig, }) @@ -387,27 +393,10 @@ func loadProvidersHcl(os *hclobj.Object) ([]*ProviderConfig, error) { // The resulting resources may not be unique, but each resource // represents exactly one resource definition in the HCL configuration. // We leave it up to another pass to merge them together. -func loadResourcesHcl(os *hclobj.Object) ([]*Resource, error) { - var allTypes []*hclobj.Object - - // HCL object iteration is really nasty. Below is likely to make - // no sense to anyone approaching this code. Luckily, it is very heavily - // tested. If working on a bug fix or feature, we recommend writing a - // test first then doing whatever you want to the code below. If you - // break it, the tests will catch it. Likewise, if you change this, - // MAKE SURE you write a test for your change, because its fairly impossible - // to reason about this mess. - // - // Functionally, what the code does below is get the libucl.Objects - // for all the TYPES, such as "aws_security_group". - for _, o1 := range os.Elem(false) { - // Iterate the inner to get the list of types - for _, o2 := range o1.Elem(true) { - // Iterate all of this type to get _all_ the types - for _, o3 := range o2.Elem(false) { - allTypes = append(allTypes, o3) - } - } +func loadResourcesHcl(list *ast.ObjectList) ([]*Resource, error) { + list = list.Children() + if len(list.Items) == 0 { + return nil, nil } // Where all the results will go @@ -415,191 +404,179 @@ func loadResourcesHcl(os *hclobj.Object) ([]*Resource, error) { // Now go over all the types and their children in order to get // all of the actual resources. - for _, t := range allTypes { - for _, obj := range t.Elem(true) { - k := obj.Key - - var config map[string]interface{} - if err := hcl.DecodeObject(&config, obj); err != nil { - return nil, fmt.Errorf( - "Error reading config for %s[%s]: %s", - t.Key, - k, - err) - } - - // Remove the fields we handle specially - delete(config, "connection") - delete(config, "count") - delete(config, "depends_on") - delete(config, "provisioner") - delete(config, "provider") - delete(config, "lifecycle") - - rawConfig, err := NewRawConfig(config) - if err != nil { - return nil, fmt.Errorf( - "Error reading config for %s[%s]: %s", - t.Key, - k, - err) - } - - // If we have a count, then figure it out - var count string = "1" - if o := obj.Get("count", false); o != nil { - err = hcl.DecodeObject(&count, o) - if err != nil { - return nil, fmt.Errorf( - "Error parsing count for %s[%s]: %s", - t.Key, - k, - err) - } - } - countConfig, err := NewRawConfig(map[string]interface{}{ - "count": count, - }) - if err != nil { - return nil, err - } - countConfig.Key = "count" - - // If we have depends fields, then add those in - var dependsOn []string - if o := obj.Get("depends_on", false); o != nil { - err := hcl.DecodeObject(&dependsOn, o) - if err != nil { - return nil, fmt.Errorf( - "Error reading depends_on for %s[%s]: %s", - t.Key, - k, - err) - } - } - - // If we have connection info, then parse those out - var connInfo map[string]interface{} - if o := obj.Get("connection", false); o != nil { - err := hcl.DecodeObject(&connInfo, o) - if err != nil { - return nil, fmt.Errorf( - "Error reading connection info for %s[%s]: %s", - t.Key, - k, - err) - } - } - - // If we have provisioners, then parse those out - var provisioners []*Provisioner - if os := obj.Get("provisioner", false); os != nil { - var err error - provisioners, err = loadProvisionersHcl(os, connInfo) - if err != nil { - return nil, fmt.Errorf( - "Error reading provisioners for %s[%s]: %s", - t.Key, - k, - err) - } - } - - // If we have a provider, then parse it out - var provider string - if o := obj.Get("provider", false); o != nil { - err := hcl.DecodeObject(&provider, o) - if err != nil { - return nil, fmt.Errorf( - "Error reading provider for %s[%s]: %s", - t.Key, - k, - err) - } - } - - // Check if the resource should be re-created before - // destroying the existing instance - var lifecycle ResourceLifecycle - if o := obj.Get("lifecycle", false); o != nil { - var raw map[string]interface{} - if err = hcl.DecodeObject(&raw, o); err != nil { - return nil, fmt.Errorf( - "Error parsing lifecycle for %s[%s]: %s", - t.Key, - k, - err) - } - - if err := mapstructure.WeakDecode(raw, &lifecycle); err != nil { - return nil, fmt.Errorf( - "Error parsing lifecycle for %s[%s]: %s", - t.Key, - k, - err) - } - } - - result = append(result, &Resource{ - Name: k, - Type: t.Key, - RawCount: countConfig, - RawConfig: rawConfig, - Provisioners: provisioners, - Provider: provider, - DependsOn: dependsOn, - Lifecycle: lifecycle, - }) + for _, item := range list.Items { + if len(item.Keys) != 2 { + // TODO: bad error message + return nil, fmt.Errorf("resource needs exactly 2 names") } + + t := item.Keys[0].Token.Value().(string) + k := item.Keys[1].Token.Value().(string) + + var listVal *ast.ObjectList + if ot, ok := item.Val.(*ast.ObjectType); ok { + listVal = ot.List + } else { + return nil, fmt.Errorf("resources %s[%s]: should be an object", t, k) + } + + var config map[string]interface{} + if err := hcl.DecodeObject(&config, item.Val); err != nil { + return nil, fmt.Errorf( + "Error reading config for %s[%s]: %s", + t, + k, + err) + } + + // Remove the fields we handle specially + delete(config, "connection") + delete(config, "count") + delete(config, "depends_on") + delete(config, "provisioner") + delete(config, "provider") + delete(config, "lifecycle") + + rawConfig, err := NewRawConfig(config) + if err != nil { + return nil, fmt.Errorf( + "Error reading config for %s[%s]: %s", + t, + k, + err) + } + + // If we have a count, then figure it out + var count string = "1" + if o := listVal.Filter("count"); len(o.Items) > 0 { + err = hcl.DecodeObject(&count, o.Items[0].Val) + if err != nil { + return nil, fmt.Errorf( + "Error parsing count for %s[%s]: %s", + t, + k, + err) + } + } + countConfig, err := NewRawConfig(map[string]interface{}{ + "count": count, + }) + if err != nil { + return nil, err + } + countConfig.Key = "count" + + // If we have depends fields, then add those in + var dependsOn []string + if o := listVal.Filter("depends_on"); len(o.Items) > 0 { + err := hcl.DecodeObject(&dependsOn, o.Items[0].Val) + if err != nil { + return nil, fmt.Errorf( + "Error reading depends_on for %s[%s]: %s", + t, + k, + err) + } + } + + // If we have connection info, then parse those out + var connInfo map[string]interface{} + if o := listVal.Filter("connection"); len(o.Items) > 0 { + err := hcl.DecodeObject(&connInfo, o.Items[0].Val) + if err != nil { + return nil, fmt.Errorf( + "Error reading connection info for %s[%s]: %s", + t, + k, + err) + } + } + + // If we have provisioners, then parse those out + var provisioners []*Provisioner + if os := listVal.Filter("provisioner"); len(os.Items) > 0 { + var err error + provisioners, err = loadProvisionersHcl(os, connInfo) + if err != nil { + return nil, fmt.Errorf( + "Error reading provisioners for %s[%s]: %s", + t, + k, + err) + } + } + + // If we have a provider, then parse it out + var provider string + if o := listVal.Filter("provider"); len(o.Items) > 0 { + err := hcl.DecodeObject(&provider, o.Items[0].Val) + if err != nil { + return nil, fmt.Errorf( + "Error reading provider for %s[%s]: %s", + t, + k, + err) + } + } + + // Check if the resource should be re-created before + // destroying the existing instance + var lifecycle ResourceLifecycle + if o := listVal.Filter("lifecycle"); len(o.Items) > 0 { + var raw map[string]interface{} + if err = hcl.DecodeObject(&raw, o.Items[0].Val); err != nil { + return nil, fmt.Errorf( + "Error parsing lifecycle for %s[%s]: %s", + t, + k, + err) + } + + if err := mapstructure.WeakDecode(raw, &lifecycle); err != nil { + return nil, fmt.Errorf( + "Error parsing lifecycle for %s[%s]: %s", + t, + k, + err) + } + } + + result = append(result, &Resource{ + Name: k, + Type: t, + RawCount: countConfig, + RawConfig: rawConfig, + Provisioners: provisioners, + Provider: provider, + DependsOn: dependsOn, + Lifecycle: lifecycle, + }) } return result, nil } -func loadProvisionersHcl(os *hclobj.Object, connInfo map[string]interface{}) ([]*Provisioner, error) { - pos := make([]*hclobj.Object, 0, int(os.Len())) - - // Accumulate all the actual provisioner configuration objects. We - // have to iterate twice here: - // - // 1. The first iteration is of the list of `provisioner` blocks. - // 2. The second iteration is of the dictionary within the - // provisioner which will have only one element which is the - // type of provisioner to use along with tis config. - // - // In JSON it looks kind of like this: - // - // [ - // { - // "shell": { - // ... - // } - // } - // ] - // - for _, o1 := range os.Elem(false) { - for _, o2 := range o1.Elem(true) { - - switch o1.Type { - case hclobj.ValueTypeList: - for _, o3 := range o2.Elem(true) { - pos = append(pos, o3) - } - case hclobj.ValueTypeObject: - pos = append(pos, o2) - } - } - } - - // Short-circuit if there are no items - if len(pos) == 0 { +func loadProvisionersHcl(list *ast.ObjectList, connInfo map[string]interface{}) ([]*Provisioner, error) { + println(fmt.Sprintf("%#v", list.Items[0])) + list = list.Children() + if len(list.Items) == 0 { return nil, nil } - result := make([]*Provisioner, 0, len(pos)) - for _, po := range pos { + // Go through each object and turn it into an actual result. + result := make([]*Provisioner, 0, len(list.Items)) + for _, item := range list.Items { + n := item.Keys[0].Token.Value().(string) + + var listVal *ast.ObjectList + if ot, ok := item.Val.(*ast.ObjectType); ok { + listVal = ot.List + } else { + return nil, fmt.Errorf("provisioner '%s': should be an object", n) + } + var config map[string]interface{} - if err := hcl.DecodeObject(&config, po); err != nil { + if err := hcl.DecodeObject(&config, item.Val); err != nil { return nil, err } @@ -614,8 +591,8 @@ func loadProvisionersHcl(os *hclobj.Object, connInfo map[string]interface{}) ([] // Check if we have a provisioner-level connection // block that overrides the resource-level var subConnInfo map[string]interface{} - if o := po.Get("connection", false); o != nil { - err := hcl.DecodeObject(&subConnInfo, o) + if o := listVal.Filter("connection"); len(o.Items) > 0 { + err := hcl.DecodeObject(&subConnInfo, o.Items[0].Val) if err != nil { return nil, err } @@ -640,7 +617,7 @@ func loadProvisionersHcl(os *hclobj.Object, connInfo map[string]interface{}) ([] } result = append(result, &Provisioner{ - Type: po.Key, + Type: n, RawConfig: rawConfig, ConnInfo: connRaw, }) From 13c5fdb1542424210dbb955e341b9f92b50cbed3 Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Sat, 7 Nov 2015 16:55:07 -0800 Subject: [PATCH 054/173] config: remove debug line --- config/loader_hcl.go | 1 - 1 file changed, 1 deletion(-) diff --git a/config/loader_hcl.go b/config/loader_hcl.go index 07b0eef84a..c62ca37314 100644 --- a/config/loader_hcl.go +++ b/config/loader_hcl.go @@ -557,7 +557,6 @@ func loadResourcesHcl(list *ast.ObjectList) ([]*Resource, error) { } func loadProvisionersHcl(list *ast.ObjectList, connInfo map[string]interface{}) ([]*Provisioner, error) { - println(fmt.Sprintf("%#v", list.Items[0])) list = list.Children() if len(list.Items) == 0 { return nil, nil From 6ecec7fe833e11e09c16b994eb24746fa2e20b77 Mon Sep 17 00:00:00 2001 From: Matt Morrison Date: Sun, 8 Nov 2015 19:34:56 +1300 Subject: [PATCH 055/173] Add coalesce func --- config/interpolate_funcs.go | 25 +++++++++++++++++ config/interpolate_funcs_test.go | 27 +++++++++++++++++++ .../docs/configuration/interpolation.html.md | 3 +++ 3 files changed, 55 insertions(+) diff --git a/config/interpolate_funcs.go b/config/interpolate_funcs.go index e98ade2f0c..5538763c0c 100644 --- a/config/interpolate_funcs.go +++ b/config/interpolate_funcs.go @@ -25,6 +25,7 @@ func init() { "cidrhost": interpolationFuncCidrHost(), "cidrnetmask": interpolationFuncCidrNetmask(), "cidrsubnet": interpolationFuncCidrSubnet(), + "coalesce": interpolationFuncCoalesce(), "compact": interpolationFuncCompact(), "concat": interpolationFuncConcat(), "element": interpolationFuncElement(), @@ -145,6 +146,30 @@ func interpolationFuncCidrSubnet() ast.Function { } } +// interpolationFuncCoalesce implements the "coalesce" function that +// returns the first non null / empty string from the provided input +func interpolationFuncCoalesce() ast.Function { + return ast.Function{ + ArgTypes: []ast.Type{ast.TypeString}, + ReturnType: ast.TypeString, + Variadic: true, + VariadicType: ast.TypeString, + Callback: func(args []interface{}) (interface{}, error) { + if len(args) < 2 { + return nil, fmt.Errorf("must provide at least two arguments") + } + for _, arg := range args { + argument := arg.(string) + + if argument != "" { + return argument, nil + } + } + return "", nil + }, + } +} + // interpolationFuncConcat implements the "concat" function that // concatenates multiple strings. This isn't actually necessary anymore // since our language supports string concat natively, but for backwards diff --git a/config/interpolate_funcs_test.go b/config/interpolate_funcs_test.go index bbfdd484ad..3aeb50db17 100644 --- a/config/interpolate_funcs_test.go +++ b/config/interpolate_funcs_test.go @@ -147,6 +147,33 @@ func TestInterpolateFuncCidrSubnet(t *testing.T) { }) } +func TestInterpolateFuncCoalesce(t *testing.T) { + testFunction(t, testFunctionConfig{ + Cases: []testFunctionCase{ + { + `${coalesce("first", "second", "third")}`, + "first", + false, + }, + { + `${coalesce("", "second", "third")}`, + "second", + false, + }, + { + `${coalesce("", "", "")}`, + "", + false, + }, + { + `${coalesce("foo")}`, + nil, + true, + }, + }, + }) +} + func TestInterpolateFuncDeprecatedConcat(t *testing.T) { testFunction(t, testFunctionConfig{ Cases: []testFunctionCase{ diff --git a/website/source/docs/configuration/interpolation.html.md b/website/source/docs/configuration/interpolation.html.md index 049c718251..21efbd83e6 100644 --- a/website/source/docs/configuration/interpolation.html.md +++ b/website/source/docs/configuration/interpolation.html.md @@ -95,6 +95,9 @@ The supported built-in functions are: CIDR notation (like ``10.0.0.0/8``) and extends its prefix to include an additional subnet number. For example, ``cidrsubnet("10.0.0.0/8", 8, 2)`` returns ``10.2.0.0/16``. + + * `coalesce(string1, string2, ...)` - Returns the first non-empty value from + the given arguments. At least two arguments must be provided. * `compact(list)` - Removes empty string elements from a list. This can be useful in some cases, for example when passing joined lists as module From 63049c0176ec4892a422c11b3ebda517143be4f6 Mon Sep 17 00:00:00 2001 From: Radek Simko Date: Sun, 8 Nov 2015 20:31:18 +0100 Subject: [PATCH 056/173] provider/aws: Support scratch volumes in ecs_task_definition --- .../aws/resource_aws_ecs_task_definition.go | 2 +- .../resource_aws_ecs_task_definition_test.go | 39 +++++++++++++++++++ builtin/providers/aws/structure.go | 18 ++++++--- 3 files changed, 53 insertions(+), 6 deletions(-) diff --git a/builtin/providers/aws/resource_aws_ecs_task_definition.go b/builtin/providers/aws/resource_aws_ecs_task_definition.go index 4ab0d0b8aa..337c2aadfd 100644 --- a/builtin/providers/aws/resource_aws_ecs_task_definition.go +++ b/builtin/providers/aws/resource_aws_ecs_task_definition.go @@ -58,7 +58,7 @@ func resourceAwsEcsTaskDefinition() *schema.Resource { "host_path": &schema.Schema{ Type: schema.TypeString, - Required: true, + Optional: true, }, }, }, diff --git a/builtin/providers/aws/resource_aws_ecs_task_definition_test.go b/builtin/providers/aws/resource_aws_ecs_task_definition_test.go index 2cff051c59..de53f597e2 100644 --- a/builtin/providers/aws/resource_aws_ecs_task_definition_test.go +++ b/builtin/providers/aws/resource_aws_ecs_task_definition_test.go @@ -32,6 +32,23 @@ func TestAccAWSEcsTaskDefinition_basic(t *testing.T) { }) } +// Regression for https://github.com/hashicorp/terraform/issues/2370 +func TestAccAWSEcsTaskDefinition_withScratchVolume(t *testing.T) { + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSEcsTaskDefinitionDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccAWSEcsTaskDefinitionWithScratchVolume, + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSEcsTaskDefinitionExists("aws_ecs_task_definition.sleep"), + ), + }, + }, + }) +} + func testAccCheckAWSEcsTaskDefinitionDestroy(s *terraform.State) error { conn := testAccProvider.Meta().(*AWSClient).ecsconn @@ -116,6 +133,28 @@ TASK_DEFINITION } ` +var testAccAWSEcsTaskDefinitionWithScratchVolume = ` +resource "aws_ecs_task_definition" "sleep" { + family = "terraform-acc-sc-volume-test" + container_definitions = < Date: Sun, 8 Nov 2015 20:43:34 +0100 Subject: [PATCH 057/173] provider/openstack: extend documentation of Neutron::FloatingIP --- .../openstack/r/networking_floatingip_v2.html.markdown | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/website/source/docs/providers/openstack/r/networking_floatingip_v2.html.markdown b/website/source/docs/providers/openstack/r/networking_floatingip_v2.html.markdown index 9389eafeb2..fb1c57cbc4 100644 --- a/website/source/docs/providers/openstack/r/networking_floatingip_v2.html.markdown +++ b/website/source/docs/providers/openstack/r/networking_floatingip_v2.html.markdown @@ -35,6 +35,9 @@ The following arguments are supported: * `pool` - (Required) The name of the pool from which to obtain the floating IP. Changing this creates a new floating IP. +* `port_id` - ID of an existing port with at least one IP address to associate with +this floating IP. + ## Attributes Reference The following attributes are exported: @@ -42,3 +45,4 @@ The following attributes are exported: * `region` - See Argument Reference above. * `pool` - See Argument Reference above. * `address` - The actual floating IP address itself. +* `port_id` - ID of associated port. From 87e384dc97829f3278311833ac1e7933fcd18cec Mon Sep 17 00:00:00 2001 From: Jef Statham Date: Mon, 9 Nov 2015 09:56:38 -0500 Subject: [PATCH 058/173] Fix spelling mistake in aws saml example Example resource name is `aws_iam_saml_provider` instead of `aws_saml_provider`. --- .../source/docs/providers/aws/r/iam_saml_provider.html.markdown | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/source/docs/providers/aws/r/iam_saml_provider.html.markdown b/website/source/docs/providers/aws/r/iam_saml_provider.html.markdown index adba6d350d..a27d45b64f 100644 --- a/website/source/docs/providers/aws/r/iam_saml_provider.html.markdown +++ b/website/source/docs/providers/aws/r/iam_saml_provider.html.markdown @@ -13,7 +13,7 @@ Provides an IAM SAML provider. ## Example Usage ``` -resource "aws_saml_provider" "default" { +resource "aws_iam_saml_provider" "default" { name = "myprovider" saml_metadata_document = "${file("saml-metadata.xml")}" } From 13e862673f31ce2c27b7a704946b23bb9c317a5c Mon Sep 17 00:00:00 2001 From: Jef Statham Date: Mon, 9 Nov 2015 10:52:09 -0500 Subject: [PATCH 059/173] Fix spelling mistake in aws iam saml title --- .../source/docs/providers/aws/r/iam_saml_provider.html.markdown | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/source/docs/providers/aws/r/iam_saml_provider.html.markdown b/website/source/docs/providers/aws/r/iam_saml_provider.html.markdown index a27d45b64f..49fe6ec73e 100644 --- a/website/source/docs/providers/aws/r/iam_saml_provider.html.markdown +++ b/website/source/docs/providers/aws/r/iam_saml_provider.html.markdown @@ -1,6 +1,6 @@ --- layout: "aws" -page_title: "AWS: aws_saml_provider" +page_title: "AWS: aws_iam_saml_provider" sidebar_current: "docs-aws-resource-iam-saml-provider" description: |- Provides an IAM SAML provider. From a80f6fd9795e731c72f4be3c596ccd28ebdcc088 Mon Sep 17 00:00:00 2001 From: clint shryock Date: Mon, 9 Nov 2015 10:18:53 -0600 Subject: [PATCH 060/173] Always deploy from stable website branch --- scripts/website_push.sh | 3 +++ 1 file changed, 3 insertions(+) diff --git a/scripts/website_push.sh b/scripts/website_push.sh index fa58fd694a..53ed59777c 100755 --- a/scripts/website_push.sh +++ b/scripts/website_push.sh @@ -1,5 +1,8 @@ #!/bin/bash +# Switch to the stable-website branch +git checkout stable-website + # Set the tmpdir if [ -z "$TMPDIR" ]; then TMPDIR="/tmp" From f4c03ec2a6b8636a3ca74d4dc75470ffaa019693 Mon Sep 17 00:00:00 2001 From: James Nugent Date: Mon, 9 Nov 2015 11:38:51 -0500 Subject: [PATCH 061/173] Reflect new comment format in stringer.go As of November 8th 2015, (4b07c5ce8a), the word "Code" is prepended to the comments in Go source files generated by the stringer utility. --- command/counthookaction_string.go | 2 +- config/lang/ast/type_string.go | 2 +- helper/schema/getsource_string.go | 2 +- helper/schema/valuetype_string.go | 2 +- terraform/graphnodeconfigtype_string.go | 2 +- terraform/instancetype_string.go | 2 +- terraform/walkoperation_string.go | 2 +- 7 files changed, 7 insertions(+), 7 deletions(-) diff --git a/command/counthookaction_string.go b/command/counthookaction_string.go index 8b90dc50bc..c0c40d0de6 100644 --- a/command/counthookaction_string.go +++ b/command/counthookaction_string.go @@ -1,4 +1,4 @@ -// generated by stringer -type=countHookAction hook_count_action.go; DO NOT EDIT +// Code generated by "stringer -type=countHookAction hook_count_action.go"; DO NOT EDIT package command diff --git a/config/lang/ast/type_string.go b/config/lang/ast/type_string.go index d9b5a2df4c..5410e011e1 100644 --- a/config/lang/ast/type_string.go +++ b/config/lang/ast/type_string.go @@ -1,4 +1,4 @@ -// generated by stringer -type=Type; DO NOT EDIT +// Code generated by "stringer -type=Type"; DO NOT EDIT package ast diff --git a/helper/schema/getsource_string.go b/helper/schema/getsource_string.go index 039bb561a0..790dbff919 100644 --- a/helper/schema/getsource_string.go +++ b/helper/schema/getsource_string.go @@ -1,4 +1,4 @@ -// generated by stringer -type=getSource resource_data_get_source.go; DO NOT EDIT +// Code generated by "stringer -type=getSource resource_data_get_source.go"; DO NOT EDIT package schema diff --git a/helper/schema/valuetype_string.go b/helper/schema/valuetype_string.go index 42442a46b5..08f008450f 100644 --- a/helper/schema/valuetype_string.go +++ b/helper/schema/valuetype_string.go @@ -1,4 +1,4 @@ -// generated by stringer -type=ValueType valuetype.go; DO NOT EDIT +// Code generated by "stringer -type=ValueType valuetype.go"; DO NOT EDIT package schema diff --git a/terraform/graphnodeconfigtype_string.go b/terraform/graphnodeconfigtype_string.go index d8c1724f47..9ea0acbebe 100644 --- a/terraform/graphnodeconfigtype_string.go +++ b/terraform/graphnodeconfigtype_string.go @@ -1,4 +1,4 @@ -// generated by stringer -type=GraphNodeConfigType graph_config_node_type.go; DO NOT EDIT +// Code generated by "stringer -type=GraphNodeConfigType graph_config_node_type.go"; DO NOT EDIT package terraform diff --git a/terraform/instancetype_string.go b/terraform/instancetype_string.go index 3114bc1571..f65414b347 100644 --- a/terraform/instancetype_string.go +++ b/terraform/instancetype_string.go @@ -1,4 +1,4 @@ -// generated by stringer -type=InstanceType instancetype.go; DO NOT EDIT +// Code generated by "stringer -type=InstanceType instancetype.go"; DO NOT EDIT package terraform diff --git a/terraform/walkoperation_string.go b/terraform/walkoperation_string.go index 1ce3661c49..0811fc8744 100644 --- a/terraform/walkoperation_string.go +++ b/terraform/walkoperation_string.go @@ -1,4 +1,4 @@ -// generated by stringer -type=walkOperation graph_walk_operation.go; DO NOT EDIT +// Code generated by "stringer -type=walkOperation graph_walk_operation.go"; DO NOT EDIT package terraform From 796fdeef53d4653d4af983bc2ce879edf1dac4c0 Mon Sep 17 00:00:00 2001 From: "Michael H. Oshita" Date: Tue, 10 Nov 2015 01:42:59 +0900 Subject: [PATCH 062/173] Update rds_cluster.html.markdown I needed `db_subnet_group_name` in the rds_cluster resource as well when creating on a non-default VPC. https://github.com/hashicorp/terraform/pull/2935#issuecomment-133481106 --- website/source/docs/providers/aws/r/rds_cluster.html.markdown | 1 + 1 file changed, 1 insertion(+) 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 fb1f0dac85..c60e6ef294 100644 --- a/website/source/docs/providers/aws/r/rds_cluster.html.markdown +++ b/website/source/docs/providers/aws/r/rds_cluster.html.markdown @@ -63,6 +63,7 @@ Default: A 30-minute window selected at random from an 8-hour block of time per * `apply_immediately` - (Optional) Specifies whether any cluster modifications are applied immediately, or during the next maintenance window. Default is `false`. See [Amazon RDS Documentation for more information.](http://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/Overview.DBInstance.Modifying.html) +* `db_subnet_group_name` - (Optional) A DB subnet group to associate with this DB instance. ## Attributes Reference From 29636dc51d26110608ba3895803a5b60de938f52 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Mon, 9 Nov 2015 17:31:40 +0000 Subject: [PATCH 063/173] provider/openstack: Revert Security Group Rule Fix This commit reverts the patch from #3796. It has been discovered that multiple rules are being reported out of order when the configuration is applied multiple times. I feel this is a larger issue than the bug this patch originally fixed, so until I can resolve it, I am reverting the patch. --- .../resource_openstack_compute_secgroup_v2.go | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/builtin/providers/openstack/resource_openstack_compute_secgroup_v2.go b/builtin/providers/openstack/resource_openstack_compute_secgroup_v2.go index 202c4d9794..3cc0cbf0cc 100644 --- a/builtin/providers/openstack/resource_openstack_compute_secgroup_v2.go +++ b/builtin/providers/openstack/resource_openstack_compute_secgroup_v2.go @@ -131,10 +131,10 @@ func resourceComputeSecGroupV2Read(d *schema.ResourceData, meta interface{}) err d.Set("description", sg.Description) rtm := rulesToMap(sg.Rules) for _, v := range rtm { - if v["from_group_id"] == d.Get("name") { - v["self"] = true + if v["group"] == d.Get("name") { + v["self"] = "1" } else { - v["self"] = false + v["self"] = "0" } } log.Printf("[DEBUG] rulesToMap(sg.Rules): %+v", rtm) @@ -283,12 +283,12 @@ func rulesToMap(sgrs []secgroups.Rule) []map[string]interface{} { sgrMap := make([]map[string]interface{}, len(sgrs)) for i, sgr := range sgrs { sgrMap[i] = map[string]interface{}{ - "id": sgr.ID, - "from_port": sgr.FromPort, - "to_port": sgr.ToPort, - "ip_protocol": sgr.IPProtocol, - "cidr": sgr.IPRange.CIDR, - "from_group_id": sgr.Group.Name, + "id": sgr.ID, + "from_port": sgr.FromPort, + "to_port": sgr.ToPort, + "ip_protocol": sgr.IPProtocol, + "cidr": sgr.IPRange.CIDR, + "group": sgr.Group.Name, } } return sgrMap From 3f06a7c56780fcf539d9ebd8d2313b4c37c22cbf Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Mon, 9 Nov 2015 10:58:06 -0700 Subject: [PATCH 064/173] Update CHANGELOG.md --- CHANGELOG.md | 1 - 1 file changed, 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f010a10422..554c1666ab 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -61,7 +61,6 @@ BUG FIXES: * provider/openstack: Fix boot from volume [GH-3206] * provider/openstack: Fix crashing when image is no longer accessible [GH-2189] * provider/openstack: Better handling of network resource state changes [GH-3712] - * provider/openstack: Fix issue preventing security group rules from being removed [GH-3796] * provider/openstack: Fix crashing when no security group is specified [GH-3801] ## 0.6.6 (October 23, 2015) From cf5cfdbff0da060c9f0e16164fba2f3d87f78023 Mon Sep 17 00:00:00 2001 From: Radek Simko Date: Mon, 9 Nov 2015 17:07:31 +0100 Subject: [PATCH 065/173] provider/aws: Set previously missing ARN in iam_saml_provider - fixes https://github.com/hashicorp/terraform/issues/3820 --- builtin/providers/aws/resource_aws_iam_saml_provider.go | 1 + 1 file changed, 1 insertion(+) diff --git a/builtin/providers/aws/resource_aws_iam_saml_provider.go b/builtin/providers/aws/resource_aws_iam_saml_provider.go index 6a166d711e..3ca575efe4 100644 --- a/builtin/providers/aws/resource_aws_iam_saml_provider.go +++ b/builtin/providers/aws/resource_aws_iam_saml_provider.go @@ -68,6 +68,7 @@ func resourceAwsIamSamlProviderRead(d *schema.ResourceData, meta interface{}) er } validUntil := out.ValidUntil.Format(time.RFC1123) + d.Set("arn", d.Id()) d.Set("valid_until", validUntil) d.Set("saml_metadata_document", *out.SAMLMetadataDocument) From 96838964808959ceb9857f1716374ecd6ee72ee8 Mon Sep 17 00:00:00 2001 From: Radek Simko Date: Mon, 9 Nov 2015 15:10:40 +0100 Subject: [PATCH 066/173] provider/aws: Treat ecs_service w/ Status==INACTIVE as deleted - fixes https://github.com/hashicorp/terraform/issues/3582 --- builtin/providers/aws/resource_aws_ecs_service.go | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/builtin/providers/aws/resource_aws_ecs_service.go b/builtin/providers/aws/resource_aws_ecs_service.go index ab8562acb9..febb40c7c4 100644 --- a/builtin/providers/aws/resource_aws_ecs_service.go +++ b/builtin/providers/aws/resource_aws_ecs_service.go @@ -160,6 +160,13 @@ func resourceAwsEcsServiceRead(d *schema.ResourceData, meta interface{}) error { } service := out.Services[0] + + // Status==INACTIVE means deleted service + if *service.Status == "INACTIVE" { + d.SetId("") + return nil + } + log.Printf("[DEBUG] Received ECS service %s", service) d.SetId(*service.ServiceArn) From 2a0334125c0db514bc1fb5aa1cb5df9845342f5e Mon Sep 17 00:00:00 2001 From: James Nugent Date: Mon, 9 Nov 2015 15:15:25 -0500 Subject: [PATCH 067/173] Add test attempting to reproduce #2598 This attempts to reproduce the issue described in #2598 whereby outputs added after an apply are not reflected in the output. As per the issue the outputs are described using the JSON syntax. --- terraform/context_apply_test.go | 49 +++++++++++++++++++ terraform/terraform_test.go | 16 ++++++ .../apply-output-add-after/main.tf | 6 +++ .../apply-output-add-after/outputs.tf.json | 10 ++++ .../apply-output-add-before/main.tf | 6 +++ .../apply-output-add-before/outputs.tf.json | 7 +++ 6 files changed, 94 insertions(+) create mode 100644 terraform/test-fixtures/apply-output-add-after/main.tf create mode 100644 terraform/test-fixtures/apply-output-add-after/outputs.tf.json create mode 100644 terraform/test-fixtures/apply-output-add-before/main.tf create mode 100644 terraform/test-fixtures/apply-output-add-before/outputs.tf.json diff --git a/terraform/context_apply_test.go b/terraform/context_apply_test.go index 1fd069db08..4060dd3e37 100644 --- a/terraform/context_apply_test.go +++ b/terraform/context_apply_test.go @@ -2851,6 +2851,55 @@ func TestContext2Apply_outputInvalid(t *testing.T) { } } +func TestContext2Apply_outputAdd(t *testing.T) { + m1 := testModule(t, "apply-output-add-before") + p1 := testProvider("aws") + p1.ApplyFn = testApplyFn + p1.DiffFn = testDiffFn + ctx1 := testContext2(t, &ContextOpts{ + Module: m1, + Providers: map[string]ResourceProviderFactory{ + "aws": testProviderFuncFixed(p1), + }, + }) + + if _, err := ctx1.Plan(); err != nil { + t.Fatalf("err: %s", err) + } + + state1, err := ctx1.Apply() + if err != nil { + t.Fatalf("err: %s", err) + } + + m2 := testModule(t, "apply-output-add-after") + p2 := testProvider("aws") + p2.ApplyFn = testApplyFn + p2.DiffFn = testDiffFn + ctx2 := testContext2(t, &ContextOpts{ + Module: m2, + Providers: map[string]ResourceProviderFactory{ + "aws": testProviderFuncFixed(p2), + }, + State: state1, + }) + + if _, err := ctx2.Plan(); err != nil { + t.Fatalf("err: %s", err) + } + + state2, err := ctx2.Apply() + if err != nil { + t.Fatalf("err: %s", err) + } + + actual := strings.TrimSpace(state2.String()) + expected := strings.TrimSpace(testTerraformApplyOutputAddStr) + if actual != expected { + t.Fatalf("bad: \n%s", actual) + } +} + func TestContext2Apply_outputList(t *testing.T) { m := testModule(t, "apply-output-list") p := testProvider("aws") diff --git a/terraform/terraform_test.go b/terraform/terraform_test.go index d17726acb4..3b1653f431 100644 --- a/terraform/terraform_test.go +++ b/terraform/terraform_test.go @@ -575,6 +575,22 @@ Outputs: foo_num = 2 ` +const testTerraformApplyOutputAddStr = ` +aws_instance.test.0: + ID = foo + foo = foo0 + type = aws_instance +aws_instance.test.1: + ID = foo + foo = foo1 + type = aws_instance + +Outputs: + +firstOutput = foo0 +secondOutput = foo1 +` + const testTerraformApplyOutputListStr = ` aws_instance.bar.0: ID = foo diff --git a/terraform/test-fixtures/apply-output-add-after/main.tf b/terraform/test-fixtures/apply-output-add-after/main.tf new file mode 100644 index 0000000000..1c10eaafc5 --- /dev/null +++ b/terraform/test-fixtures/apply-output-add-after/main.tf @@ -0,0 +1,6 @@ +provider "aws" {} + +resource "aws_instance" "test" { + foo = "${format("foo%d", count.index)}" + count = 2 +} diff --git a/terraform/test-fixtures/apply-output-add-after/outputs.tf.json b/terraform/test-fixtures/apply-output-add-after/outputs.tf.json new file mode 100644 index 0000000000..32e96b0ee0 --- /dev/null +++ b/terraform/test-fixtures/apply-output-add-after/outputs.tf.json @@ -0,0 +1,10 @@ +{ + "output": { + "firstOutput": { + "value": "${aws_instance.test.0.foo}" + }, + "secondOutput": { + "value": "${aws_instance.test.1.foo}" + } + } +} diff --git a/terraform/test-fixtures/apply-output-add-before/main.tf b/terraform/test-fixtures/apply-output-add-before/main.tf new file mode 100644 index 0000000000..1c10eaafc5 --- /dev/null +++ b/terraform/test-fixtures/apply-output-add-before/main.tf @@ -0,0 +1,6 @@ +provider "aws" {} + +resource "aws_instance" "test" { + foo = "${format("foo%d", count.index)}" + count = 2 +} diff --git a/terraform/test-fixtures/apply-output-add-before/outputs.tf.json b/terraform/test-fixtures/apply-output-add-before/outputs.tf.json new file mode 100644 index 0000000000..238668ef3d --- /dev/null +++ b/terraform/test-fixtures/apply-output-add-before/outputs.tf.json @@ -0,0 +1,7 @@ +{ + "output": { + "firstOutput": { + "value": "${aws_instance.test.0.foo}" + } + } +} From 2694022b4acc3bad411d03ca8bff0a05d9a595f4 Mon Sep 17 00:00:00 2001 From: clint shryock Date: Mon, 9 Nov 2015 14:33:20 -0600 Subject: [PATCH 068/173] Fix a panic that could occur if no ECS Cluster was found for a given cluster name --- builtin/providers/aws/resource_aws_ecs_cluster.go | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/builtin/providers/aws/resource_aws_ecs_cluster.go b/builtin/providers/aws/resource_aws_ecs_cluster.go index 7f5d0ea1e4..f9e3a4abb9 100644 --- a/builtin/providers/aws/resource_aws_ecs_cluster.go +++ b/builtin/providers/aws/resource_aws_ecs_cluster.go @@ -59,9 +59,16 @@ func resourceAwsEcsClusterRead(d *schema.ResourceData, meta interface{}) error { } log.Printf("[DEBUG] Received ECS clusters: %s", out.Clusters) - d.SetId(*out.Clusters[0].ClusterArn) - d.Set("name", *out.Clusters[0].ClusterName) + for _, c := range out.Clusters { + if *c.ClusterName == clusterName { + d.SetId(*c.ClusterArn) + d.Set("name", c.ClusterName) + return nil + } + } + log.Printf("[ERR] No matching ECS Cluster found for (%s)", d.Id()) + d.SetId("") return nil } From fc983c5505c8f332f7f199afa6fb48222c88f18a Mon Sep 17 00:00:00 2001 From: stack72 Date: Mon, 9 Nov 2015 22:26:55 +0000 Subject: [PATCH 069/173] Initial Create, Read and Delete work for the S3 part of the Kinesis Firehose resource --- builtin/providers/aws/config.go | 5 + builtin/providers/aws/provider.go | 203 ++++++++--------- .../resource_aws_kinesis_firehose_stream.go | 210 ++++++++++++++++++ 3 files changed, 317 insertions(+), 101 deletions(-) create mode 100644 builtin/providers/aws/resource_aws_kinesis_firehose_stream.go diff --git a/builtin/providers/aws/config.go b/builtin/providers/aws/config.go index 3e835c1063..bd7b089248 100644 --- a/builtin/providers/aws/config.go +++ b/builtin/providers/aws/config.go @@ -27,6 +27,7 @@ import ( "github.com/aws/aws-sdk-go/service/elasticache" elasticsearch "github.com/aws/aws-sdk-go/service/elasticsearchservice" "github.com/aws/aws-sdk-go/service/elb" + "github.com/aws/aws-sdk-go/service/firehose" "github.com/aws/aws-sdk-go/service/glacier" "github.com/aws/aws-sdk-go/service/iam" "github.com/aws/aws-sdk-go/service/kinesis" @@ -74,6 +75,7 @@ type AWSClient struct { rdsconn *rds.RDS iamconn *iam.IAM kinesisconn *kinesis.Kinesis + firehoseconn *firehose.Firehose elasticacheconn *elasticache.ElastiCache lambdaconn *lambda.Lambda opsworksconn *opsworks.OpsWorks @@ -168,6 +170,9 @@ func (c *Config) Client() (interface{}, error) { errs = append(errs, authErr) } + log.Println("[INFO] Initializing Kinesis Firehose Connection") + client.firehoseconn = autoscaling.New(sess) + log.Println("[INFO] Initializing AutoScaling connection") client.autoscalingconn = autoscaling.New(sess) diff --git a/builtin/providers/aws/provider.go b/builtin/providers/aws/provider.go index b5392429aa..dfa1565fcc 100644 --- a/builtin/providers/aws/provider.go +++ b/builtin/providers/aws/provider.go @@ -163,107 +163,108 @@ func Provider() terraform.ResourceProvider { }, ResourcesMap: map[string]*schema.Resource{ - "aws_ami": resourceAwsAmi(), - "aws_ami_copy": resourceAwsAmiCopy(), - "aws_ami_from_instance": resourceAwsAmiFromInstance(), - "aws_app_cookie_stickiness_policy": resourceAwsAppCookieStickinessPolicy(), - "aws_autoscaling_group": resourceAwsAutoscalingGroup(), - "aws_autoscaling_notification": resourceAwsAutoscalingNotification(), - "aws_autoscaling_policy": resourceAwsAutoscalingPolicy(), - "aws_cloudformation_stack": resourceAwsCloudFormationStack(), - "aws_cloudtrail": resourceAwsCloudTrail(), - "aws_cloudwatch_log_group": resourceAwsCloudWatchLogGroup(), - "aws_autoscaling_lifecycle_hook": resourceAwsAutoscalingLifecycleHook(), - "aws_cloudwatch_metric_alarm": resourceAwsCloudWatchMetricAlarm(), - "aws_codedeploy_app": resourceAwsCodeDeployApp(), - "aws_codedeploy_deployment_group": resourceAwsCodeDeployDeploymentGroup(), - "aws_codecommit_repository": resourceAwsCodeCommitRepository(), - "aws_customer_gateway": resourceAwsCustomerGateway(), - "aws_db_instance": resourceAwsDbInstance(), - "aws_db_parameter_group": resourceAwsDbParameterGroup(), - "aws_db_security_group": resourceAwsDbSecurityGroup(), - "aws_db_subnet_group": resourceAwsDbSubnetGroup(), - "aws_directory_service_directory": resourceAwsDirectoryServiceDirectory(), - "aws_dynamodb_table": resourceAwsDynamoDbTable(), - "aws_ebs_volume": resourceAwsEbsVolume(), - "aws_ecs_cluster": resourceAwsEcsCluster(), - "aws_ecs_service": resourceAwsEcsService(), - "aws_ecs_task_definition": resourceAwsEcsTaskDefinition(), - "aws_efs_file_system": resourceAwsEfsFileSystem(), - "aws_efs_mount_target": resourceAwsEfsMountTarget(), - "aws_eip": resourceAwsEip(), - "aws_elasticache_cluster": resourceAwsElasticacheCluster(), - "aws_elasticache_parameter_group": resourceAwsElasticacheParameterGroup(), - "aws_elasticache_security_group": resourceAwsElasticacheSecurityGroup(), - "aws_elasticache_subnet_group": resourceAwsElasticacheSubnetGroup(), - "aws_elasticsearch_domain": resourceAwsElasticSearchDomain(), - "aws_elb": resourceAwsElb(), - "aws_flow_log": resourceAwsFlowLog(), - "aws_glacier_vault": resourceAwsGlacierVault(), - "aws_iam_access_key": resourceAwsIamAccessKey(), - "aws_iam_group_policy": resourceAwsIamGroupPolicy(), - "aws_iam_group": resourceAwsIamGroup(), - "aws_iam_group_membership": resourceAwsIamGroupMembership(), - "aws_iam_instance_profile": resourceAwsIamInstanceProfile(), - "aws_iam_policy": resourceAwsIamPolicy(), - "aws_iam_policy_attachment": resourceAwsIamPolicyAttachment(), - "aws_iam_role_policy": resourceAwsIamRolePolicy(), - "aws_iam_role": resourceAwsIamRole(), - "aws_iam_saml_provider": resourceAwsIamSamlProvider(), - "aws_iam_server_certificate": resourceAwsIAMServerCertificate(), - "aws_iam_user_policy": resourceAwsIamUserPolicy(), - "aws_iam_user": resourceAwsIamUser(), - "aws_instance": resourceAwsInstance(), - "aws_internet_gateway": resourceAwsInternetGateway(), - "aws_key_pair": resourceAwsKeyPair(), - "aws_kinesis_stream": resourceAwsKinesisStream(), - "aws_lambda_function": resourceAwsLambdaFunction(), - "aws_launch_configuration": resourceAwsLaunchConfiguration(), - "aws_lb_cookie_stickiness_policy": resourceAwsLBCookieStickinessPolicy(), - "aws_main_route_table_association": resourceAwsMainRouteTableAssociation(), - "aws_network_acl": resourceAwsNetworkAcl(), - "aws_network_interface": resourceAwsNetworkInterface(), - "aws_opsworks_stack": resourceAwsOpsworksStack(), - "aws_opsworks_java_app_layer": resourceAwsOpsworksJavaAppLayer(), - "aws_opsworks_haproxy_layer": resourceAwsOpsworksHaproxyLayer(), - "aws_opsworks_static_web_layer": resourceAwsOpsworksStaticWebLayer(), - "aws_opsworks_php_app_layer": resourceAwsOpsworksPhpAppLayer(), - "aws_opsworks_rails_app_layer": resourceAwsOpsworksRailsAppLayer(), - "aws_opsworks_nodejs_app_layer": resourceAwsOpsworksNodejsAppLayer(), - "aws_opsworks_memcached_layer": resourceAwsOpsworksMemcachedLayer(), - "aws_opsworks_mysql_layer": resourceAwsOpsworksMysqlLayer(), - "aws_opsworks_ganglia_layer": resourceAwsOpsworksGangliaLayer(), - "aws_opsworks_custom_layer": resourceAwsOpsworksCustomLayer(), - "aws_placement_group": resourceAwsPlacementGroup(), - "aws_proxy_protocol_policy": resourceAwsProxyProtocolPolicy(), - "aws_rds_cluster": resourceAwsRDSCluster(), - "aws_rds_cluster_instance": resourceAwsRDSClusterInstance(), - "aws_route53_delegation_set": resourceAwsRoute53DelegationSet(), - "aws_route53_record": resourceAwsRoute53Record(), - "aws_route53_zone_association": resourceAwsRoute53ZoneAssociation(), - "aws_route53_zone": resourceAwsRoute53Zone(), - "aws_route53_health_check": resourceAwsRoute53HealthCheck(), - "aws_route": resourceAwsRoute(), - "aws_route_table": resourceAwsRouteTable(), - "aws_route_table_association": resourceAwsRouteTableAssociation(), - "aws_s3_bucket": resourceAwsS3Bucket(), - "aws_s3_bucket_object": resourceAwsS3BucketObject(), - "aws_security_group": resourceAwsSecurityGroup(), - "aws_security_group_rule": resourceAwsSecurityGroupRule(), - "aws_spot_instance_request": resourceAwsSpotInstanceRequest(), - "aws_sqs_queue": resourceAwsSqsQueue(), - "aws_sns_topic": resourceAwsSnsTopic(), - "aws_sns_topic_subscription": resourceAwsSnsTopicSubscription(), - "aws_subnet": resourceAwsSubnet(), - "aws_volume_attachment": resourceAwsVolumeAttachment(), - "aws_vpc_dhcp_options_association": resourceAwsVpcDhcpOptionsAssociation(), - "aws_vpc_dhcp_options": resourceAwsVpcDhcpOptions(), - "aws_vpc_peering_connection": resourceAwsVpcPeeringConnection(), - "aws_vpc": resourceAwsVpc(), - "aws_vpc_endpoint": resourceAwsVpcEndpoint(), - "aws_vpn_connection": resourceAwsVpnConnection(), - "aws_vpn_connection_route": resourceAwsVpnConnectionRoute(), - "aws_vpn_gateway": resourceAwsVpnGateway(), + "aws_ami": resourceAwsAmi(), + "aws_ami_copy": resourceAwsAmiCopy(), + "aws_ami_from_instance": resourceAwsAmiFromInstance(), + "aws_app_cookie_stickiness_policy": resourceAwsAppCookieStickinessPolicy(), + "aws_autoscaling_group": resourceAwsAutoscalingGroup(), + "aws_autoscaling_notification": resourceAwsAutoscalingNotification(), + "aws_autoscaling_policy": resourceAwsAutoscalingPolicy(), + "aws_cloudformation_stack": resourceAwsCloudFormationStack(), + "aws_cloudtrail": resourceAwsCloudTrail(), + "aws_cloudwatch_log_group": resourceAwsCloudWatchLogGroup(), + "aws_autoscaling_lifecycle_hook": resourceAwsAutoscalingLifecycleHook(), + "aws_cloudwatch_metric_alarm": resourceAwsCloudWatchMetricAlarm(), + "aws_codedeploy_app": resourceAwsCodeDeployApp(), + "aws_codedeploy_deployment_group": resourceAwsCodeDeployDeploymentGroup(), + "aws_codecommit_repository": resourceAwsCodeCommitRepository(), + "aws_customer_gateway": resourceAwsCustomerGateway(), + "aws_db_instance": resourceAwsDbInstance(), + "aws_db_parameter_group": resourceAwsDbParameterGroup(), + "aws_db_security_group": resourceAwsDbSecurityGroup(), + "aws_db_subnet_group": resourceAwsDbSubnetGroup(), + "aws_directory_service_directory": resourceAwsDirectoryServiceDirectory(), + "aws_dynamodb_table": resourceAwsDynamoDbTable(), + "aws_ebs_volume": resourceAwsEbsVolume(), + "aws_ecs_cluster": resourceAwsEcsCluster(), + "aws_ecs_service": resourceAwsEcsService(), + "aws_ecs_task_definition": resourceAwsEcsTaskDefinition(), + "aws_efs_file_system": resourceAwsEfsFileSystem(), + "aws_efs_mount_target": resourceAwsEfsMountTarget(), + "aws_eip": resourceAwsEip(), + "aws_elasticache_cluster": resourceAwsElasticacheCluster(), + "aws_elasticache_parameter_group": resourceAwsElasticacheParameterGroup(), + "aws_elasticache_security_group": resourceAwsElasticacheSecurityGroup(), + "aws_elasticache_subnet_group": resourceAwsElasticacheSubnetGroup(), + "aws_elasticsearch_domain": resourceAwsElasticSearchDomain(), + "aws_elb": resourceAwsElb(), + "aws_flow_log": resourceAwsFlowLog(), + "aws_glacier_vault": resourceAwsGlacierVault(), + "aws_iam_access_key": resourceAwsIamAccessKey(), + "aws_iam_group_policy": resourceAwsIamGroupPolicy(), + "aws_iam_group": resourceAwsIamGroup(), + "aws_iam_group_membership": resourceAwsIamGroupMembership(), + "aws_iam_instance_profile": resourceAwsIamInstanceProfile(), + "aws_iam_policy": resourceAwsIamPolicy(), + "aws_iam_policy_attachment": resourceAwsIamPolicyAttachment(), + "aws_iam_role_policy": resourceAwsIamRolePolicy(), + "aws_iam_role": resourceAwsIamRole(), + "aws_iam_saml_provider": resourceAwsIamSamlProvider(), + "aws_iam_server_certificate": resourceAwsIAMServerCertificate(), + "aws_iam_user_policy": resourceAwsIamUserPolicy(), + "aws_iam_user": resourceAwsIamUser(), + "aws_instance": resourceAwsInstance(), + "aws_internet_gateway": resourceAwsInternetGateway(), + "aws_key_pair": resourceAwsKeyPair(), + "aws_kinesis_firehose_delivery_stream": resourceAwsKinesisFirehoseDeliveryStream(), + "aws_kinesis_stream": resourceAwsKinesisStream(), + "aws_lambda_function": resourceAwsLambdaFunction(), + "aws_launch_configuration": resourceAwsLaunchConfiguration(), + "aws_lb_cookie_stickiness_policy": resourceAwsLBCookieStickinessPolicy(), + "aws_main_route_table_association": resourceAwsMainRouteTableAssociation(), + "aws_network_acl": resourceAwsNetworkAcl(), + "aws_network_interface": resourceAwsNetworkInterface(), + "aws_opsworks_stack": resourceAwsOpsworksStack(), + "aws_opsworks_java_app_layer": resourceAwsOpsworksJavaAppLayer(), + "aws_opsworks_haproxy_layer": resourceAwsOpsworksHaproxyLayer(), + "aws_opsworks_static_web_layer": resourceAwsOpsworksStaticWebLayer(), + "aws_opsworks_php_app_layer": resourceAwsOpsworksPhpAppLayer(), + "aws_opsworks_rails_app_layer": resourceAwsOpsworksRailsAppLayer(), + "aws_opsworks_nodejs_app_layer": resourceAwsOpsworksNodejsAppLayer(), + "aws_opsworks_memcached_layer": resourceAwsOpsworksMemcachedLayer(), + "aws_opsworks_mysql_layer": resourceAwsOpsworksMysqlLayer(), + "aws_opsworks_ganglia_layer": resourceAwsOpsworksGangliaLayer(), + "aws_opsworks_custom_layer": resourceAwsOpsworksCustomLayer(), + "aws_placement_group": resourceAwsPlacementGroup(), + "aws_proxy_protocol_policy": resourceAwsProxyProtocolPolicy(), + "aws_rds_cluster": resourceAwsRDSCluster(), + "aws_rds_cluster_instance": resourceAwsRDSClusterInstance(), + "aws_route53_delegation_set": resourceAwsRoute53DelegationSet(), + "aws_route53_record": resourceAwsRoute53Record(), + "aws_route53_zone_association": resourceAwsRoute53ZoneAssociation(), + "aws_route53_zone": resourceAwsRoute53Zone(), + "aws_route53_health_check": resourceAwsRoute53HealthCheck(), + "aws_route": resourceAwsRoute(), + "aws_route_table": resourceAwsRouteTable(), + "aws_route_table_association": resourceAwsRouteTableAssociation(), + "aws_s3_bucket": resourceAwsS3Bucket(), + "aws_s3_bucket_object": resourceAwsS3BucketObject(), + "aws_security_group": resourceAwsSecurityGroup(), + "aws_security_group_rule": resourceAwsSecurityGroupRule(), + "aws_spot_instance_request": resourceAwsSpotInstanceRequest(), + "aws_sqs_queue": resourceAwsSqsQueue(), + "aws_sns_topic": resourceAwsSnsTopic(), + "aws_sns_topic_subscription": resourceAwsSnsTopicSubscription(), + "aws_subnet": resourceAwsSubnet(), + "aws_volume_attachment": resourceAwsVolumeAttachment(), + "aws_vpc_dhcp_options_association": resourceAwsVpcDhcpOptionsAssociation(), + "aws_vpc_dhcp_options": resourceAwsVpcDhcpOptions(), + "aws_vpc_peering_connection": resourceAwsVpcPeeringConnection(), + "aws_vpc": resourceAwsVpc(), + "aws_vpc_endpoint": resourceAwsVpcEndpoint(), + "aws_vpn_connection": resourceAwsVpnConnection(), + "aws_vpn_connection_route": resourceAwsVpnConnectionRoute(), + "aws_vpn_gateway": resourceAwsVpnGateway(), }, ConfigureFunc: providerConfigure, diff --git a/builtin/providers/aws/resource_aws_kinesis_firehose_stream.go b/builtin/providers/aws/resource_aws_kinesis_firehose_stream.go new file mode 100644 index 0000000000..320e078b2b --- /dev/null +++ b/builtin/providers/aws/resource_aws_kinesis_firehose_stream.go @@ -0,0 +1,210 @@ +package aws + +import ( + "fmt" + "time" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/aws/awserr" + "github.com/aws/aws-sdk-go/service/firehose" + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/helper/schema" +) + +func resourceAwsKinesisFirehoseDeliveryStream() *schema.Resource { + return &schema.Resource{ + Create: resourceAwsKinesisFirehoseDeliveryStreamCreate, + Read: resourceAwsKinesisFirehoseDeliveryStreamRead, + Update: resourceAwsKinesisFirehoseDeliveryStreamUpdate, + Delete: resourceAwsKinesisFirehoseDeliveryStreamDelete, + + Schema: map[string]*schema.Schema{ + "name": &schema.Schema{ + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + + "destination": &schema.Schema{ + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + + "role_arn": &schema.Schema{ + Type: schema.TypeString, + Required: true, + }, + + "s3_bucket_arn": &schema.Schema{ + Type: schema.TypeString, + Required: true, + }, + + "s3_prefix": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + }, + + "s3_buffer_size": &schema.Schema{ + Type: schema.TypeInt, + Optional: true, + Default: 5, + }, + + "s3_buffer_interval": &schema.Schema{ + Type: schema.TypeInt, + Optional: true, + Default: 300, + }, + + "s3_data_compression": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + Default: "UNCOMPRESSED", + }, + + "arn": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + Computed: true, + }, + }, + } +} + +func resourceAwsKinesisFirehoseDeliveryStreamCreate(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).firehoseconn + + sn := d.Get("name").(string) + input := &firehose.CreateDeliveryStreamInput{ + DeliveryStreamName: aws.String(sn), + } + + s3_config := &firehose.S3DestinationConfiguration{ + BucketARN: aws.String(d.Get("s3_bucket_arn").(string)), + RoleARN: aws.String(d.Get("role_arn").(string)), + BufferingHints: &firehose.BufferingHints{ + IntervalInSeconds: aws.Int64(int64(d.Get("s3_buffer_interval").(int))), + SizeInMBs: aws.Int64(int64(d.Get("s3_buffer_size").(int))), + }, + CompressionFormat: aws.String(d.Get("s3_data_compression").(string)), + } + if v, ok := d.GetOk("s3_prefix"); ok { + s3_config.Prefix = aws.String(v.(string)) + } + + input.S3DestinationConfiguration = s3_config + + _, err := conn.CreateDeliveryStream(input) + if err != nil { + if awsErr, ok := err.(awserr.Error); ok { + return fmt.Errorf("[WARN] Error creating Kinesis Firehose Delivery Stream: \"%s\", code: \"%s\"", awsErr.Message(), awsErr.Code()) + } + return err + } + + stateConf := &resource.StateChangeConf{ + Pending: []string{"CREATING"}, + Target: "ACTIVE", + Refresh: firehoseStreamStateRefreshFunc(conn, sn), + Timeout: 5 * time.Minute, + Delay: 10 * time.Second, + MinTimeout: 3 * time.Second, + } + + firehoseStream, err := stateConf.WaitForState() + if err != nil { + return fmt.Errorf( + "Error waiting for Kinesis Stream (%s) to become active: %s", + sn, err) + } + + s := firehoseStream.(*firehose.DeliveryStreamDescription) + d.SetId(*s.DeliveryStreamARN) + d.Set("arn", s.DeliveryStreamARN) + + return resourceAwsKinesisStreamUpdate(d, meta) +} + +func resourceAwsKinesisFirehoseDeliveryStreamUpdate(d *schema.ResourceData, meta interface{}) error { + // conn := meta.(*AWSClient).firehoseconn + + return resourceAwsKinesisFirehoseDeliveryStreamRead(d, meta) +} + +func resourceAwsKinesisFirehoseDeliveryStreamRead(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).firehoseconn + sn := d.Get("name").(string) + describeOpts := &firehose.DeliveryStreamDescription{ + DeliveryStreamName: aws.String(sn), + } + resp, err := conn.DescribeDeliveryStream(describeOpts) + if err != nil { + if awsErr, ok := err.(awserr.Error); ok { + if awsErr.Code() == "ResourceNotFoundException" { + d.SetId("") + return nil + } + return fmt.Errorf("[WARN] Error reading Kinesis Firehose Delivery Stream: \"%s\", code: \"%s\"", awsErr.Message(), awsErr.Code()) + } + return err + } + + s := resp.DeliveryStreamDescription + d.Set("arn", *s.DeliveryStreamARN) + + return nil +} + +func resourceAwsKinesisFirehoseDeliveryStreamDelete(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).firehoseconn + + sn := d.Get("name").(string) + _, err := conn.DeleteDeliveryStream(&firehose.DeleteDeliveryStreamInput{ + DeliveryStreamName: aws.String(sn), + }) + + if err != nil { + return err + } + + stateConf := &resource.StateChangeConf{ + Pending: []string{"DELETING"}, + Target: "DESTROYED", + Refresh: firehoseStreamStateRefreshFunc(conn, sn), + Timeout: 5 * time.Minute, + Delay: 10 * time.Second, + MinTimeout: 3 * time.Second, + } + + _, err = stateConf.WaitForState() + if err != nil { + return fmt.Errorf( + "Error waiting for Delivery Stream (%s) to be destroyed: %s", + sn, err) + } + + d.SetId("") + return nil +} + +func firehoseStreamStateRefreshFunc(conn *firehose.Firehose, sn string) resource.StateRefreshFunc { + return func() (interface{}, string, error) { + describeOpts := &firehose.DescribeDeliveryStreamInput{ + DeliveryStreamName: aws.String(sn), + } + resp, err := conn.DescribeDeliveryStream(describeOpts) + if err != nil { + if awsErr, ok := err.(awserr.Error); ok { + if awsErr.Code() == "ResourceNotFoundException" { + return 42, "DESTROYED", nil + } + return nil, awsErr.Code(), err + } + return nil, "failed", err + } + + return resp.DeliveryStreamDescription, *resp.DeliveryStreamDescription.DeliveryStreamStatus, nil + } +} From 64d982ac9ed2fe17a50967952bfce5e47211139d Mon Sep 17 00:00:00 2001 From: mcinteer Date: Tue, 10 Nov 2015 11:27:45 +1300 Subject: [PATCH 070/173] Change the docs as the token can be explicitly set This tripped me up today when I was trying to connect using MFA. I had a look at the source and found the token property, tested it out and low and behold it worked! Hopefully this saves someone else going through the same pain --- website/source/docs/providers/aws/index.html.markdown | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/website/source/docs/providers/aws/index.html.markdown b/website/source/docs/providers/aws/index.html.markdown index 05efd5700f..7199111c2b 100644 --- a/website/source/docs/providers/aws/index.html.markdown +++ b/website/source/docs/providers/aws/index.html.markdown @@ -59,5 +59,4 @@ The following arguments are supported in the `provider` block: * `kinesis_endpoint` - (Optional) Use this to override the default endpoint URL constructed from the `region`. It's typically used to connect to kinesalite. -In addition to the above parameters, the `AWS_SESSION_TOKEN` environmental -variable can be set to set an MFA token. +* `token` - (Optional) Use this to set an MFA token. It can also be sourced from the `AWS_SECURITY_TOKEN` environment variable. From 5dfa9ac8234457b3b80eabe6ad7aecb8decc883b Mon Sep 17 00:00:00 2001 From: stack72 Date: Mon, 9 Nov 2015 22:44:26 +0000 Subject: [PATCH 071/173] Adding the shell for the acceptance tests for the AWS Kinesis Firehose work --- ...e_aws_kinesis_firehose_delivery_stream.go} | 0 ...s_kinesis_firehose_delivery_stream_test.go | 106 ++++++++++++++++++ 2 files changed, 106 insertions(+) rename builtin/providers/aws/{resource_aws_kinesis_firehose_stream.go => resource_aws_kinesis_firehose_delivery_stream.go} (100%) create mode 100644 builtin/providers/aws/resource_aws_kinesis_firehose_delivery_stream_test.go diff --git a/builtin/providers/aws/resource_aws_kinesis_firehose_stream.go b/builtin/providers/aws/resource_aws_kinesis_firehose_delivery_stream.go similarity index 100% rename from builtin/providers/aws/resource_aws_kinesis_firehose_stream.go rename to builtin/providers/aws/resource_aws_kinesis_firehose_delivery_stream.go diff --git a/builtin/providers/aws/resource_aws_kinesis_firehose_delivery_stream_test.go b/builtin/providers/aws/resource_aws_kinesis_firehose_delivery_stream_test.go new file mode 100644 index 0000000000..1414f7ae60 --- /dev/null +++ b/builtin/providers/aws/resource_aws_kinesis_firehose_delivery_stream_test.go @@ -0,0 +1,106 @@ +package aws + +import ( + "fmt" + "math/rand" + "strings" + "testing" + "time" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/firehose" + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" +) + +func TestAccAWSKinesisFirehoseDeliveryStream_basic(t *testing.T) { + var stream firehose.DeliveryStreamDescription + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckKinesisFirehoseDeliveryStreamDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccKinesisFirehoseDeliveryStreamConfig, + Check: resource.ComposeTestCheckFunc( + testAccCheckKinesisFirehoseDeliveryStreamExists("aws_kinesis_firehose_delivery_stream.test_stream", &stream), + testAccCheckAWSKinesisFirehoseDeliveryStreamAttributes(&stream), + ), + }, + }, + }) +} + +func testAccCheckKinesisFirehoseDeliveryStreamExists(n string, stream *firehose.DeliveryStreamDescription) resource.TestCheckFunc { + return func(s *terraform.State) error { + rs, ok := s.RootModule().Resources[n] + if !ok { + return fmt.Errorf("Not found: %s", n) + } + + if rs.Primary.ID == "" { + return fmt.Errorf("No Kinesis Firehose ID is set") + } + + conn := testAccProvider.Meta().(*AWSClient).firehoseconn + describeOpts := &firehose.DescribeDeliveryStreamInput{ + DeliveryStreamName: aws.String(rs.Primary.Attributes["name"]), + } + resp, err := conn.DescribeDeliveryStream(describeOpts) + if err != nil { + return err + } + + *stream = *resp.DeliveryStreamDescription + + return nil + } +} + +func testAccCheckAWSKinesisFirehoseDeliveryStreamAttributes(stream *firehose.DeliveryStreamDescription) resource.TestCheckFunc { + return func(s *terraform.State) error { + if !strings.HasPrefix(*stream.DeliveryStreamName, "terraform-kinesis-firehose-test") { + return fmt.Errorf("Bad Stream name: %s", *stream.DeliveryStreamName) + } + for _, rs := range s.RootModule().Resources { + if rs.Type != "aws_kinesis_firehose_delivery_stream" { + continue + } + if *stream.DeliveryStreamARN != rs.Primary.Attributes["arn"] { + return fmt.Errorf("Bad Delivery Stream ARN\n\t expected: %s\n\tgot: %s\n", rs.Primary.Attributes["arn"], *stream.DeliveryStreamARN) + } + } + return nil + } +} + +func testAccCheckKinesisFirehoseDeliveryStreamDestroy(s *terraform.State) error { + for _, rs := range s.RootModule().Resources { + if rs.Type != "aws_kinesis_firehose_delivery_stream" { + continue + } + conn := testAccProvider.Meta().(*AWSClient).firehoseconn + describeOpts := &firehose.DescribeDeliveryStreamInput{ + DeliveryStreamName: aws.String(rs.Primary.Attributes["name"]), + } + resp, err := conn.DescribeDeliveryStream(describeOpts) + if err == nil { + if resp.DeliveryStreamDescription != nil && *resp.DeliveryStreamDescription.DeliveryStreamStatus != "DELETING" { + return fmt.Errorf("Error: Delivery Stream still exists") + } + } + + return nil + + } + + return nil +} + +var testAccKinesisFirehoseDeliveryStreamConfig = fmt.Sprintf(` +resource "aws_kinesis_firehose_delivery_stream" "test_stream" { + name = "terraform-kinesis-firehose-test-%d" + +} +`, rand.New(rand.NewSource(time.Now().UnixNano())).Int()) From 22876be830dbd0a4fee1ed268c92fe2db30790ff Mon Sep 17 00:00:00 2001 From: "Michael H. Oshita" Date: Mon, 9 Nov 2015 23:59:09 +0900 Subject: [PATCH 072/173] Document `license_model` as supported argument Required for Oracle SE1 --- website/source/docs/providers/aws/r/db_instance.html.markdown | 1 + 1 file changed, 1 insertion(+) diff --git a/website/source/docs/providers/aws/r/db_instance.html.markdown b/website/source/docs/providers/aws/r/db_instance.html.markdown index 499e13ba40..15122d9dd9 100644 --- a/website/source/docs/providers/aws/r/db_instance.html.markdown +++ b/website/source/docs/providers/aws/r/db_instance.html.markdown @@ -81,6 +81,7 @@ database, and to use this value as the source database. This correlates to the [Working with PostgreSQL and MySQL Read Replicas](http://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/USER_ReadRepl.html) for more information on using Replication. * `snapshot_identifier` - (Optional) Specifies whether or not to create this database from a snapshot. This correlates to the snapshot ID you'd find in the RDS console, e.g: rds:production-2015-06-26-06-05. +* `license_model` - (Optional, but required for some DB engines, i.e. Oracle SE1) License model information for this DB instance. ~> **NOTE:** Removing the `replicate_source_db` attribute from an existing RDS Replicate database managed by Terraform will promote the database to a fully From ebec7e2bc237a054e751e3d9208da0044bc5e0f4 Mon Sep 17 00:00:00 2001 From: captainill Date: Mon, 9 Nov 2015 22:11:00 -0800 Subject: [PATCH 073/173] use more atomic styles for header/footer classes. use page url to determin class names --- .../source/assets/stylesheets/_footer.scss | 248 ++++--------- .../hashicorp-shared/_hashicorp-header.scss | 345 ++++++++++++------ .../hashicorp-shared/_project-utility.scss | 51 +++ website/source/layouts/_footer.erb | 65 ++-- website/source/layouts/_header.erb | 6 +- 5 files changed, 387 insertions(+), 328 deletions(-) diff --git a/website/source/assets/stylesheets/_footer.scss b/website/source/assets/stylesheets/_footer.scss index c16acff753..c50ce8190a 100644 --- a/website/source/assets/stylesheets/_footer.scss +++ b/website/source/assets/stylesheets/_footer.scss @@ -1,210 +1,88 @@ - -#footer-wrap{ - background-color: white; - padding: 0 0 50px 0; -} - -body.page-home{ - #footer{ - margin-top: -40px; - } +body.page-sub{ + #footer{ + padding: 40px 0; + margin-top: 0; + } } #footer{ - padding: 140px 0 40px; - color: black; - - a{ - color: black; - } - + background-color: white; + padding: 100px 0 60px; + margin-top: -40px; + &.white{ + background-color: $black; .footer-links{ - margin-bottom: 20px; - - .li-under a:hover::after, - .li-under a:focus::after { - opacity: 1; - -webkit-transform: skewY(15deg) translateY(8px); - -moz-transform: skewY(15deg) translateY(8px); - transform: skewY(15deg) translateY(8px); - } - - .li-under a::after { - background-color: $purple; - } - - li{ - a{ - text-transform: uppercase; - font-size: 12px; - letter-spacing: 3px; - @include transition( color 0.3s ease ); - font-weight: 400; - - &:hover{ - color: $purple; - @include transition( color 0.3s ease ); - background-color: transparent; - } - } - } + li > a { + @include project-footer-a-subpage-style(); + } } + } - .buttons.navbar-nav{ - float: none; - display: inline-block; - margin-bottom: 30px; - margin-top: 0px; - - li{ - &.first{ - margin-right: 12px; - } - - &.download{ - a{ - background: image-url('icon-download-purple.png') 8px 6px no-repeat; - @include img-retina("icon-download-purple.png", "icon-download-purple@2x.png", 20px, 20px); - } - } - - &.github{ - a{ - background: image-url('icon-github-purple.png') 8px 6px no-repeat; - @include img-retina("icon-github-purple.png", "icon-github-purple@2x.png", 20px, 20px); - } - } - } - - li > a { - padding-top: 6px; - padding-bottom: 6px; - padding-left: 40px; - } + .footer-links{ + li > a { + @include project-footer-a-style(); } + } - .footer-hashi{ - float: right; - padding-top: 5px; - letter-spacing: 2px; + .hashicorp-project{ + margin-top: 24px; + } - a{ - color: black; - font-weight: $font-weight-lato-xb; - } - - span{ - margin-right: 10px; - } - - .hashi-logo{ - display: inline-block; - vertical-align: middle; - i{ - display: inline-block; - width: 37px; - height: 40px; - background: image-url('footer-hashicorp-logo.png') 0 0 no-repeat; - @include img-retina("footer-hashicorp-logo.png", "footer-hashicorp-logo@2x.png", 37px, 40px); - } - } - } + .pull-right{ + padding-right: 15px; + } } -.page-sub{ - #footer-wrap{ - padding: 0; - } +.edit-page-link{ + position: absolute; + top: -70px; + right: 30px;; - #footer{ - padding: 140px 0 100px; - background-color: $black; - transform: none; - - >.container{ - transform: none; - } - - a{ - color: white; - } - - .footer-hashi{ - color: white; - - .hashi-logo{ - i{ - background: image-url('footer-hashicorp-white-logo.png') 0 0 no-repeat; - @include img-retina("footer-hashicorp-white-logo.png", "footer-hashicorp-white-logo@2x.png", 37px, 40px); - } - } - } - } -} - - -@media (min-width: 1500px) { - body.page-home{ - #footer{ - margin-top: -60px; - padding: 190px 0 40px; - } - } + a{ + text-transform: uppercase; + color: $black; + font-size: 13px; + } } @media (max-width: 992px) { - .page-sub #footer, #footer{ - .footer-hashi { - padding-top: 14px; - span{ - margin-right: 6px; - } - .hashi-logo{ - i{ - margin-top: -6px; - width: 20px; - height: 22px; - background-size: 20px 22px; - } - } - } + .footer-links { + display: block; + text-align: center; + + ul{ + display: inline-block;; + float: none !important; } - -} - -@media (max-width: 768px) { - #footer{ - padding: 100px 0 40px; - text-align: center; - - .footer-links{ - float: none; - display: inline-block; - } - - .footer-hashi { - float: none; - display: inline-block; - - .pull-right{ - float: none !important; - } - } + .footer-hashi{ + display: block; + float: none !important; } + } } -@media (max-width: 320px) { - #footer{ - text-align: center; +@media (max-width: 414px) { + #footer{ + ul{ + display: block; + li{ + display: block; + float: none; + } - .footer-links{ - .li-under{ - float: none !important; - } + &.external-links{ + li{ + svg{ + position: relative; + left: 0; + top: 2px; + margin-top: 0; + margin-right: 4px; + } } + } } + } } - - diff --git a/website/source/assets/stylesheets/hashicorp-shared/_hashicorp-header.scss b/website/source/assets/stylesheets/hashicorp-shared/_hashicorp-header.scss index c9bc593b65..e9bbe501e7 100755 --- a/website/source/assets/stylesheets/hashicorp-shared/_hashicorp-header.scss +++ b/website/source/assets/stylesheets/hashicorp-shared/_hashicorp-header.scss @@ -4,33 +4,21 @@ // - Edits should not be made here // -------------------------------------------------- -#header { +#header{ position: relative; + margin-bottom: 0; +} + +.navigation { color: black; text-rendering: optimizeLegibility; - margin-bottom: 0; transition: all 1s ease; &.white{ - color: white; .navbar-brand { .logo { color: white; } - - .by{ - color: white; - font-weight: 300; - svg{ - path, - polygon{ - fill: white; - } - line{ - stroke: white; - } - } - } } .main-links, @@ -41,109 +29,40 @@ } } } + } - .nav > li > a:hover, .nav > li > a:focus { - color: white; + &.black{ + .navbar-brand { + .logo { + color: black; + } } - .navbar-header{ - .navbar-toggle{ - .icon-bar{ - border: 1px solid white; - } + .main-links, + .external-links { + li > a { + color: black; } } } - .navbar-header{ - .navbar-toggle{ - height: $header-height; - margin: 0; - padding-right: 15px; - border-radius: 0; - .icon-bar{ - border: 1px solid $black; - border-radius: 0; - } - } - } - - .navbar-brand { - display: block; + .navbar-toggle{ height: $header-height; - padding: 0; - margin: 0 10px 0 0 ; - - .logo{ - display: inline-block; - height: $header-height; - vertical-align:top; - padding: 0; - line-height: $header-height; - padding-left: $project-logo-width + $project-logo-pad-left; - background-position: 0 center; - @include transition(all 300ms ease-in); - - &:hover{ - @include transition(all 300ms ease-in); - text-decoration: none; - } - } - - .by{ - display: inline-block; - vertical-align:top; - height: $header-height; - margin-left: 3px; - padding-top: 2px; - color: black; - line-height: $header-height; - font-family: $header-font-family; - font-weight: 600; - font-size: 0; - text-decoration: none; - - .svg-wrap{ - font-size: 13px; - } - - svg{ - &.svg-by{ - width: $by-hashicorp-width; - height: $by-hashicorp-height; - margin-bottom: -4px; - margin-left: 4px; - } - - &.svg-logo{ - width: 16px; - height: 16px; - margin-bottom: -3px; - margin-left: 4px; - } - - path, - polygon{ - fill: black; - @include transition(all 300ms ease-in); - - &:hover{ - @include transition(all 300ms ease-in); - } - } - line{ - stroke: black; - @include transition(all 300ms ease-in); - - &:hover{ - @include transition(all 300ms ease-in); - } - } - } + margin: 0; + border-radius: 0; + .icon-bar{ + border: 1px solid $black; + border-radius: 0; } } .external-links { + &.white{ + svg path{ + fill: $white; + } + } + li { position: relative; @@ -157,6 +76,8 @@ } } + @include project-svg-external-links-style(); + &.download{ margin-right: 10px; } @@ -181,28 +102,222 @@ .main-links, .external-links { + &.white{ + li > a { + color: white; + } + } li > a { @include hashi-a-style(); margin: 0 10px; padding-top: 1px; line-height: $header-height; + @include project-a-style(); } } .nav > li > a:hover, .nav > li > a:focus { background-color: transparent; - color: $black; @include transition( all 300ms ease-in ); } } +.navbar-brand { + display: block; + height: $header-height; + padding: 0; + margin: 0 10px 0 0; + + .logo{ + display: inline-block; + height: $header-height; + vertical-align:top; + padding: 0; + line-height: $header-height; + padding-left: $project-logo-width + $project-logo-pad-left; + background-position: 0 center; + @include transition(all 300ms ease-in); + + &:hover{ + @include transition(all 300ms ease-in); + text-decoration: none; + } + } +} + +.navbar-toggle{ + &.white{ + .icon-bar{ + border: 1px solid white; + } + } +} + +.by-hashicorp{ + display: inline-block; + vertical-align:top; + height: $header-height; + margin-left: 3px; + padding-top: 2px; + color: black; + line-height: $header-height; + font-family: $header-font-family; + font-weight: 600; + font-size: 0; + text-decoration: none; + + &.white{ + color: white; + font-weight: 300; + svg{ + path, + polygon{ + fill: white; + } + line{ + stroke: white; + } + } + + &:focus, + &:hover{ + text-decoration: none; + color: white; + } + } + + &:focus, + &:hover{ + text-decoration: none; + } + + .svg-wrap{ + font-size: 13px; + } + + svg{ + &.svg-by{ + width: $by-hashicorp-width; + height: $by-hashicorp-height; + margin-bottom: -4px; + margin-left: 4px; + } + + &.svg-logo{ + width: 16px; + height: 16px; + margin-bottom: -3px; + margin-left: 4px; + } + + path, + polygon{ + fill: black; + @include transition(all 300ms ease-in); + + &:hover{ + @include transition(all 300ms ease-in); + } + } + line{ + stroke: black; + @include transition(all 300ms ease-in); + + &:hover{ + @include transition(all 300ms ease-in); + } + } + } +} + +.hashicorp-project{ + display: inline-block; + height: 30px; + line-height: 30px; + text-decoration: none; + font-size: 14px; + color: $black; + font-weight: 600; + + &.white{ + color: white; + svg{ + path, + polygon{ + fill: white; + } + line{ + stroke: white; + } + } + } + + &:focus{ + text-decoration: none; + } + + &:hover{ + text-decoration: none; + svg{ + &.svg-by{ + line{ + stroke: $purple; + } + } + } + } + + span{ + margin-right: 4px; + font-family: $header-font-family; + font-weight: 500; + } + + span, + svg{ + display: inline-block; + } + + svg{ + &.svg-by{ + width: $by-hashicorp-width; + height: $by-hashicorp-height; + margin-bottom: -4px; + margin-left: -3px; + } + + &.svg-logo{ + width: 30px; + height: 30px; + margin-bottom: -10px; + margin-left: -1px; + } + + path, + line{ + fill: $black; + @include transition(all 300ms ease-in); + + &:hover{ + @include transition(all 300ms ease-in); + } + } + } +} + +@media (max-width: 480px) { + .navigation { + .main-links{ + margin-right: 0; + } + } +} + @media (max-width: 414px) { #header { - .navbar-header{ - .navbar-toggle{ - padding-top: 10px; - height: $header-mobile-height; - } + .navbar-toggle{ + padding-top: 10px; + height: $header-mobile-height; } .navbar-brand { @@ -212,7 +327,7 @@ height: $header-mobile-height; line-height: $header-mobile-height; } - .by{ + .by-hashicorp{ height: $header-mobile-height; line-height: $header-mobile-height; padding-top: 0; diff --git a/website/source/assets/stylesheets/hashicorp-shared/_project-utility.scss b/website/source/assets/stylesheets/hashicorp-shared/_project-utility.scss index 1a511152df..5e51f91d5f 100755 --- a/website/source/assets/stylesheets/hashicorp-shared/_project-utility.scss +++ b/website/source/assets/stylesheets/hashicorp-shared/_project-utility.scss @@ -16,5 +16,56 @@ $project-logo-pad-left: 8px; &:hover{ color: $white; + opacity: 1; + } +} + +@mixin project-footer-a-style{ + color: $black; + font-weight: 400; + + &:hover{ + color: $purple; + + svg path{ + fill: $purple; + } + } +} + +@mixin project-footer-a-subpage-style{ + color: $white; + font-weight: 400; + + svg path{ + fill: $white; + } + + &:hover{ + color: $purple; + + svg path{ + fill: $purple; + } + } +} + +@mixin project-svg-external-links-style{ + svg path{ + fill: $black; + } + + &:hover{ + svg path{ + fill: $blue; + } + } +} + +@mixin project-by-hashicorp-style{ + &:hover{ + line{ + stroke: $blue; + } } } diff --git a/website/source/layouts/_footer.erb b/website/source/layouts/_footer.erb index d42c55cac6..1be5b8d471 100644 --- a/website/source/layouts/_footer.erb +++ b/website/source/layouts/_footer.erb @@ -1,28 +1,43 @@ - +