forked from hashicorp/terraform
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request hashicorp#13 from TimeIncOSS/asg-tags-support
ASG tags support
- Loading branch information
Showing
5 changed files
with
387 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,170 @@ | ||
package aws | ||
|
||
import ( | ||
"bytes" | ||
"fmt" | ||
"log" | ||
|
||
"github.com/hashicorp/aws-sdk-go/aws" | ||
"github.com/hashicorp/aws-sdk-go/gen/autoscaling" | ||
"github.com/hashicorp/terraform/helper/hashcode" | ||
"github.com/hashicorp/terraform/helper/schema" | ||
) | ||
|
||
// tagsSchema returns the schema to use for tags. | ||
func autoscalingTagsSchema() *schema.Schema { | ||
return &schema.Schema{ | ||
Type: schema.TypeSet, | ||
Optional: true, | ||
Elem: &schema.Resource{ | ||
Schema: map[string]*schema.Schema{ | ||
"key": &schema.Schema{ | ||
Type: schema.TypeString, | ||
Required: true, | ||
}, | ||
|
||
"value": &schema.Schema{ | ||
Type: schema.TypeString, | ||
Required: true, | ||
}, | ||
|
||
"propagate_at_launch": &schema.Schema{ | ||
Type: schema.TypeBool, | ||
Required: true, | ||
}, | ||
}, | ||
}, | ||
Set: autoscalingTagsToHash, | ||
} | ||
} | ||
|
||
func autoscalingTagsToHash(v interface{}) int { | ||
var buf bytes.Buffer | ||
m := v.(map[string]interface{}) | ||
buf.WriteString(fmt.Sprintf("%s-", m["key"].(string))) | ||
buf.WriteString(fmt.Sprintf("%s-", m["value"].(string))) | ||
buf.WriteString(fmt.Sprintf("%t-", m["propagate_at_launch"].(bool))) | ||
|
||
return hashcode.String(buf.String()) | ||
} | ||
|
||
// setTags is a helper to set the tags for a resource. It expects the | ||
// tags field to be named "tag" | ||
func setAutoscalingTags(conn *autoscaling.AutoScaling, d *schema.ResourceData) error { | ||
if d.HasChange("tag") { | ||
oraw, nraw := d.GetChange("tag") | ||
o := setToMapByKey(oraw.(*schema.Set), "key") | ||
n := setToMapByKey(nraw.(*schema.Set), "key") | ||
|
||
resourceID := d.Get("name").(string) | ||
c, r := diffAutoscalingTags( | ||
autoscalingTagsFromMap(o, resourceID), | ||
autoscalingTagsFromMap(n, resourceID), | ||
resourceID) | ||
create := autoscaling.CreateOrUpdateTagsType{ | ||
Tags: c, | ||
} | ||
remove := autoscaling.DeleteTagsType{ | ||
Tags: r, | ||
} | ||
|
||
// Set tags | ||
if len(r) > 0 { | ||
log.Printf("[DEBUG] Removing autoscaling tags: %#v", r) | ||
if err := conn.DeleteTags(&remove); err != nil { | ||
return err | ||
} | ||
} | ||
if len(c) > 0 { | ||
log.Printf("[DEBUG] Creating autoscaling tags: %#v", c) | ||
if err := conn.CreateOrUpdateTags(&create); 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 diffAutoscalingTags(oldTags, newTags []autoscaling.Tag, resourceID string) ([]autoscaling.Tag, []autoscaling.Tag) { | ||
// First, we're creating everything we have | ||
create := make(map[string]interface{}) | ||
for _, t := range newTags { | ||
tag := map[string]interface{}{ | ||
"value": *t.Value, | ||
"propagate_at_launch": *t.PropagateAtLaunch, | ||
} | ||
create[*t.Key] = tag | ||
} | ||
|
||
// Build the list of what to remove | ||
var remove []autoscaling.Tag | ||
for _, t := range oldTags { | ||
old, ok := create[*t.Key].(map[string]interface{}) | ||
|
||
if !ok || old["value"] != *t.Value || old["propagate_at_launch"] != *t.PropagateAtLaunch { | ||
// Delete it! | ||
remove = append(remove, t) | ||
} | ||
} | ||
|
||
return autoscalingTagsFromMap(create, resourceID), remove | ||
} | ||
|
||
// tagsFromMap returns the tags for the given map of data. | ||
func autoscalingTagsFromMap(m map[string]interface{}, resourceID string) []autoscaling.Tag { | ||
result := make([]autoscaling.Tag, 0, len(m)) | ||
for k, v := range m { | ||
attr := v.(map[string]interface{}) | ||
result = append(result, autoscaling.Tag{ | ||
Key: aws.String(k), | ||
Value: aws.String(attr["value"].(string)), | ||
PropagateAtLaunch: aws.Boolean(attr["propagate_at_launch"].(bool)), | ||
ResourceID: aws.String(resourceID), | ||
ResourceType: aws.String("auto-scaling-group"), | ||
}) | ||
} | ||
|
||
return result | ||
} | ||
|
||
// autoscalingTagsToMap turns the list of tags into a map. | ||
func autoscalingTagsToMap(ts []autoscaling.Tag) map[string]interface{} { | ||
tags := make(map[string]interface{}) | ||
for _, t := range ts { | ||
tag := map[string]interface{}{ | ||
"value": *t.Value, | ||
"propagate_at_launch": *t.PropagateAtLaunch, | ||
} | ||
tags[*t.Key] = tag | ||
} | ||
|
||
return tags | ||
} | ||
|
||
// autoscalingTagDescriptionsToMap turns the list of tags into a map. | ||
func autoscalingTagDescriptionsToMap(ts []autoscaling.TagDescription) map[string]map[string]interface{} { | ||
tags := make(map[string]map[string]interface{}) | ||
for _, t := range ts { | ||
tag := map[string]interface{}{ | ||
"value": t.Value, | ||
"propagate_at_launch": t.PropagateAtLaunch, | ||
} | ||
tags[*t.Key] = tag | ||
} | ||
|
||
return tags | ||
} | ||
|
||
func setToMapByKey(s *schema.Set, key string) map[string]interface{} { | ||
result := make(map[string]interface{}) | ||
for _, rawData := range s.List() { | ||
data := rawData.(map[string]interface{}) | ||
result[data[key].(string)] = data | ||
} | ||
|
||
return result | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,122 @@ | ||
package aws | ||
|
||
import ( | ||
"fmt" | ||
"reflect" | ||
"testing" | ||
|
||
"github.com/hashicorp/aws-sdk-go/gen/autoscaling" | ||
"github.com/hashicorp/terraform/helper/resource" | ||
"github.com/hashicorp/terraform/terraform" | ||
) | ||
|
||
func TestDiffAutoscalingTags(t *testing.T) { | ||
cases := []struct { | ||
Old, New map[string]interface{} | ||
Create, Remove map[string]interface{} | ||
}{ | ||
// Basic add/remove | ||
{ | ||
Old: map[string]interface{}{ | ||
"Name": map[string]interface{}{ | ||
"value": "bar", | ||
"propagate_at_launch": true, | ||
}, | ||
}, | ||
New: map[string]interface{}{ | ||
"DifferentTag": map[string]interface{}{ | ||
"value": "baz", | ||
"propagate_at_launch": true, | ||
}, | ||
}, | ||
Create: map[string]interface{}{ | ||
"DifferentTag": map[string]interface{}{ | ||
"value": "baz", | ||
"propagate_at_launch": true, | ||
}, | ||
}, | ||
Remove: map[string]interface{}{ | ||
"Name": map[string]interface{}{ | ||
"value": "bar", | ||
"propagate_at_launch": true, | ||
}, | ||
}, | ||
}, | ||
|
||
// Modify | ||
{ | ||
Old: map[string]interface{}{ | ||
"Name": map[string]interface{}{ | ||
"value": "bar", | ||
"propagate_at_launch": true, | ||
}, | ||
}, | ||
New: map[string]interface{}{ | ||
"Name": map[string]interface{}{ | ||
"value": "baz", | ||
"propagate_at_launch": false, | ||
}, | ||
}, | ||
Create: map[string]interface{}{ | ||
"Name": map[string]interface{}{ | ||
"value": "baz", | ||
"propagate_at_launch": false, | ||
}, | ||
}, | ||
Remove: map[string]interface{}{ | ||
"Name": map[string]interface{}{ | ||
"value": "bar", | ||
"propagate_at_launch": true, | ||
}, | ||
}, | ||
}, | ||
} | ||
|
||
var resourceID = "sample" | ||
|
||
for i, tc := range cases { | ||
awsTagsOld := autoscalingTagsFromMap(tc.Old, resourceID) | ||
awsTagsNew := autoscalingTagsFromMap(tc.New, resourceID) | ||
|
||
c, r := diffAutoscalingTags(awsTagsOld, awsTagsNew, resourceID) | ||
|
||
cm := autoscalingTagsToMap(c) | ||
rm := autoscalingTagsToMap(r) | ||
if !reflect.DeepEqual(cm, tc.Create) { | ||
t.Fatalf("%d: bad create: \n%#v\n%#v", i, cm, tc.Create) | ||
} | ||
if !reflect.DeepEqual(rm, tc.Remove) { | ||
t.Fatalf("%d: bad remove: \n%#v\n%#v", i, rm, tc.Remove) | ||
} | ||
} | ||
} | ||
|
||
// testAccCheckTags can be used to check the tags on a resource. | ||
func testAccCheckAutoscalingTags( | ||
ts *[]autoscaling.TagDescription, key string, expected map[string]interface{}) resource.TestCheckFunc { | ||
return func(s *terraform.State) error { | ||
m := autoscalingTagDescriptionsToMap(*ts) | ||
v, ok := m[key] | ||
if !ok { | ||
return fmt.Errorf("Missing tag: %s", key) | ||
} | ||
|
||
if v["value"] != expected["value"].(string) || | ||
v["propagate_at_launch"] != expected["propagate_at_launch"].(bool) { | ||
return fmt.Errorf("%s: bad value: %s", key, v) | ||
} | ||
|
||
return nil | ||
} | ||
} | ||
|
||
func testAccCheckAutoscalingTagNotExists(ts *[]autoscaling.TagDescription, key string) resource.TestCheckFunc { | ||
return func(s *terraform.State) error { | ||
m := autoscalingTagDescriptionsToMap(*ts) | ||
if _, ok := m[key]; ok { | ||
return fmt.Errorf("Tag exists when it should not: %s", key) | ||
} | ||
|
||
return nil | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.