From 36f78cc1dcc8426a77059be5d14ce1a040dd0e08 Mon Sep 17 00:00:00 2001 From: Garrett Heel Date: Fri, 2 Oct 2015 16:11:49 -0700 Subject: [PATCH] provider/aws: Allow tags for kinesis streams --- .../aws/resource_aws_kinesis_stream.go | 33 +++++- .../aws/resource_aws_kinesis_stream_test.go | 3 + builtin/providers/aws/tags_kinesis.go | 105 ++++++++++++++++++ builtin/providers/aws/tags_kinesis_test.go | 84 ++++++++++++++ .../aws/r/kinesis_stream.html.markdown | 4 + 5 files changed, 227 insertions(+), 2 deletions(-) create mode 100644 builtin/providers/aws/tags_kinesis.go create mode 100644 builtin/providers/aws/tags_kinesis_test.go diff --git a/builtin/providers/aws/resource_aws_kinesis_stream.go b/builtin/providers/aws/resource_aws_kinesis_stream.go index 45d685c1d128..1abb9dbc3252 100644 --- a/builtin/providers/aws/resource_aws_kinesis_stream.go +++ b/builtin/providers/aws/resource_aws_kinesis_stream.go @@ -2,6 +2,7 @@ package aws import ( "fmt" + "log" "time" "github.com/aws/aws-sdk-go/aws" @@ -15,6 +16,7 @@ func resourceAwsKinesisStream() *schema.Resource { return &schema.Resource{ Create: resourceAwsKinesisStreamCreate, Read: resourceAwsKinesisStreamRead, + Update: resourceAwsKinesisStreamUpdate, Delete: resourceAwsKinesisStreamDelete, Schema: map[string]*schema.Schema{ @@ -35,6 +37,7 @@ func resourceAwsKinesisStream() *schema.Resource { Optional: true, Computed: true, }, + "tags": tagsSchema(), }, } } @@ -75,13 +78,28 @@ func resourceAwsKinesisStreamCreate(d *schema.ResourceData, meta interface{}) er d.SetId(*s.StreamARN) d.Set("arn", s.StreamARN) - return nil + return resourceAwsKinesisStreamUpdate(d, meta) +} + +func resourceAwsKinesisStreamUpdate(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).kinesisconn + + d.Partial(true) + if err := setTagsKinesis(conn, d); err != nil { + return err + } + + d.SetPartial("tags") + d.Partial(false) + + return resourceAwsKinesisStreamRead(d, meta) } func resourceAwsKinesisStreamRead(d *schema.ResourceData, meta interface{}) error { conn := meta.(*AWSClient).kinesisconn + sn := d.Get("name").(string) describeOpts := &kinesis.DescribeStreamInput{ - StreamName: aws.String(d.Get("name").(string)), + StreamName: aws.String(sn), } resp, err := conn.DescribeStream(describeOpts) if err != nil { @@ -99,6 +117,17 @@ func resourceAwsKinesisStreamRead(d *schema.ResourceData, meta interface{}) erro d.Set("arn", *s.StreamARN) d.Set("shard_count", len(s.Shards)) + // set tags + describeTagsOpts := &kinesis.ListTagsForStreamInput{ + StreamName: aws.String(sn), + } + tagsResp, err := conn.ListTagsForStream(describeTagsOpts) + if err != nil { + log.Printf("[DEBUG] Error retrieving tags for Stream: %s. %s", sn, err) + } else { + d.Set("tags", tagsToMapKinesis(tagsResp.Tags)) + } + return nil } diff --git a/builtin/providers/aws/resource_aws_kinesis_stream_test.go b/builtin/providers/aws/resource_aws_kinesis_stream_test.go index c9580ad2280a..82c0b64facc3 100644 --- a/builtin/providers/aws/resource_aws_kinesis_stream_test.go +++ b/builtin/providers/aws/resource_aws_kinesis_stream_test.go @@ -107,5 +107,8 @@ var testAccKinesisStreamConfig = fmt.Sprintf(` resource "aws_kinesis_stream" "test_stream" { name = "terraform-kinesis-test-%d" shard_count = 2 + tags { + Name = "tf-test" + } } `, rand.New(rand.NewSource(time.Now().UnixNano())).Int()) diff --git a/builtin/providers/aws/tags_kinesis.go b/builtin/providers/aws/tags_kinesis.go new file mode 100644 index 000000000000..c9562644d360 --- /dev/null +++ b/builtin/providers/aws/tags_kinesis.go @@ -0,0 +1,105 @@ +package aws + +import ( + "log" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/kinesis" + "github.com/hashicorp/terraform/helper/schema" +) + +// setTags is a helper to set the tags for a resource. It expects the +// tags field to be named "tags" +func setTagsKinesis(conn *kinesis.Kinesis, d *schema.ResourceData) error { + + sn := d.Get("name").(string) + + if d.HasChange("tags") { + oraw, nraw := d.GetChange("tags") + o := oraw.(map[string]interface{}) + n := nraw.(map[string]interface{}) + create, remove := diffTagsKinesis(tagsFromMapKinesis(o), tagsFromMapKinesis(n)) + + // Set tags + if len(remove) > 0 { + log.Printf("[DEBUG] Removing tags: %#v", remove) + k := make([]*string, len(remove), len(remove)) + for i, t := range remove { + k[i] = t.Key + } + + _, err := conn.RemoveTagsFromStream(&kinesis.RemoveTagsFromStreamInput{ + StreamName: aws.String(sn), + TagKeys: k, + }) + if err != nil { + return err + } + } + + if len(create) > 0 { + + log.Printf("[DEBUG] Creating tags: %#v", create) + t := make(map[string]*string) + for _, tag := range create { + t[*tag.Key] = tag.Value + } + + _, err := conn.AddTagsToStream(&kinesis.AddTagsToStreamInput{ + StreamName: aws.String(sn), + Tags: t, + }) + if err != nil { + return err + } + } + } + + return nil +} + +// diffTags takes our tags locally and the ones remotely and returns +// the set of tags that must be created, and the set of tags that must +// be destroyed. +func diffTagsKinesis(oldTags, newTags []*kinesis.Tag) ([]*kinesis.Tag, []*kinesis.Tag) { + // First, we're creating everything we have + create := make(map[string]interface{}) + for _, t := range newTags { + create[*t.Key] = *t.Value + } + + // Build the list of what to remove + var remove []*kinesis.Tag + for _, t := range oldTags { + old, ok := create[*t.Key] + if !ok || old != *t.Value { + // Delete it! + remove = append(remove, t) + } + } + + return tagsFromMapKinesis(create), remove +} + +// tagsFromMap returns the tags for the given map of data. +func tagsFromMapKinesis(m map[string]interface{}) []*kinesis.Tag { + var result []*kinesis.Tag + for k, v := range m { + result = append(result, &kinesis.Tag{ + Key: aws.String(k), + Value: aws.String(v.(string)), + }) + } + + return result +} + +// tagsToMap turns the list of tags into a map. +func tagsToMapKinesis(ts []*kinesis.Tag) map[string]string { + result := make(map[string]string) + for _, t := range ts { + result[*t.Key] = *t.Value + } + + return result +} diff --git a/builtin/providers/aws/tags_kinesis_test.go b/builtin/providers/aws/tags_kinesis_test.go new file mode 100644 index 000000000000..d97365ad85db --- /dev/null +++ b/builtin/providers/aws/tags_kinesis_test.go @@ -0,0 +1,84 @@ +package aws + +import ( + "fmt" + "reflect" + "testing" + + "github.com/aws/aws-sdk-go/service/kinesis" + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" +) + +func TestDiffTagsKinesis(t *testing.T) { + cases := []struct { + Old, New map[string]interface{} + Create, Remove map[string]string + }{ + // Basic add/remove + { + Old: map[string]interface{}{ + "foo": "bar", + }, + New: map[string]interface{}{ + "bar": "baz", + }, + Create: map[string]string{ + "bar": "baz", + }, + Remove: map[string]string{ + "foo": "bar", + }, + }, + + // Modify + { + Old: map[string]interface{}{ + "foo": "bar", + }, + New: map[string]interface{}{ + "foo": "baz", + }, + Create: map[string]string{ + "foo": "baz", + }, + Remove: map[string]string{ + "foo": "bar", + }, + }, + } + + for i, tc := range cases { + c, r := diffTagsKinesis(tagsFromMapKinesis(tc.Old), tagsFromMapKinesis(tc.New)) + cm := tagsToMapKinesis(c) + rm := tagsToMapKinesis(r) + if !reflect.DeepEqual(cm, tc.Create) { + t.Fatalf("%d: bad create: %#v", i, cm) + } + if !reflect.DeepEqual(rm, tc.Remove) { + t.Fatalf("%d: bad remove: %#v", i, rm) + } + } +} + +// testAccCheckTags can be used to check the tags on a resource. +func testAccCheckKinesisTags(ts []*kinesis.Tag, key string, value string) resource.TestCheckFunc { + return func(s *terraform.State) error { + m := tagsToMapKinesis(ts) + v, ok := m[key] + if value != "" && !ok { + return fmt.Errorf("Missing tag: %s", key) + } else if value == "" && ok { + return fmt.Errorf("Extra tag: %s", key) + } + if value == "" { + return nil + } + + if v != value { + return fmt.Errorf("%s: bad value: %s", key, v) + } + + return nil + } +} diff --git a/website/source/docs/providers/aws/r/kinesis_stream.html.markdown b/website/source/docs/providers/aws/r/kinesis_stream.html.markdown index a667bb4278e8..b1ef962997eb 100644 --- a/website/source/docs/providers/aws/r/kinesis_stream.html.markdown +++ b/website/source/docs/providers/aws/r/kinesis_stream.html.markdown @@ -19,6 +19,9 @@ For more details, see the [Amazon Kinesis Documentation][1]. resource "aws_kinesis_stream" "test_stream" { name = "terraform-kinesis-test" shard_count = 1 + tags { + Environment = "test" + } } ``` @@ -31,6 +34,7 @@ AWS account and region the Stream is created in. * `shard_count` – (Required) The number of shards that the stream will use. Amazon has guidlines for specifying the Stream size that should be referenced when creating a Kinesis stream. See [Amazon Kinesis Streams][2] for more. +* `tags` - (Optional) A mapping of tags to assign to the resource. ## Attributes Reference