From 0388845bca8a1d22d53e44a56f01e826ecbb10fa Mon Sep 17 00:00:00 2001 From: Steve Kriss Date: Tue, 13 Mar 2018 11:25:26 -0700 Subject: [PATCH] AWS: tag snapshots during create Signed-off-by: Steve Kriss --- pkg/cloudprovider/aws/block_store.go | 32 +++++----- pkg/cloudprovider/aws/block_store_test.go | 72 ++++++++--------------- 2 files changed, 43 insertions(+), 61 deletions(-) diff --git a/pkg/cloudprovider/aws/block_store.go b/pkg/cloudprovider/aws/block_store.go index db0cf6720..69b08b387 100644 --- a/pkg/cloudprovider/aws/block_store.go +++ b/pkg/cloudprovider/aws/block_store.go @@ -164,30 +164,34 @@ func (b *blockStore) describeVolume(volumeID string) (*ec2.Volume, error) { } func (b *blockStore) CreateSnapshot(volumeID, volumeAZ string, tags map[string]string) (string, error) { - res, err := b.ec2.CreateSnapshot(&ec2.CreateSnapshotInput{VolumeId: &volumeID}) - if err != nil { - return "", errors.WithStack(err) - } - // describe the volume so we can copy its tags to the snapshot volumeInfo, err := b.describeVolume(volumeID) if err != nil { return "", err } - _, err = b.ec2.CreateTags(getCreateTagsInput(*res.SnapshotId, tags, volumeInfo.Tags)) + res, err := b.ec2.CreateSnapshot(&ec2.CreateSnapshotInput{ + VolumeId: &volumeID, + TagSpecifications: []*ec2.TagSpecification{ + { + ResourceType: aws.String(ec2.ResourceTypeSnapshot), + Tags: getTags(tags, volumeInfo.Tags), + }, + }, + }) + if err != nil { + return "", errors.WithStack(err) + } - return *res.SnapshotId, errors.WithStack(err) + return *res.SnapshotId, nil } -func getCreateTagsInput(snapshotID string, arkTags map[string]string, volumeTags []*ec2.Tag) *ec2.CreateTagsInput { - tagsInput := &ec2.CreateTagsInput{ - Resources: []*string{&snapshotID}, - } +func getTags(arkTags map[string]string, volumeTags []*ec2.Tag) []*ec2.Tag { + var result []*ec2.Tag // set Ark-assigned tags for k, v := range arkTags { - tagsInput.Tags = append(tagsInput.Tags, ec2Tag(k, v)) + result = append(result, ec2Tag(k, v)) } // copy tags from volume to snapshot @@ -198,10 +202,10 @@ func getCreateTagsInput(snapshotID string, arkTags map[string]string, volumeTags continue } - tagsInput.Tags = append(tagsInput.Tags, ec2Tag(*tag.Key, *tag.Value)) + result = append(result, ec2Tag(*tag.Key, *tag.Value)) } - return tagsInput + return result } func ec2Tag(key, val string) *ec2.Tag { diff --git a/pkg/cloudprovider/aws/block_store_test.go b/pkg/cloudprovider/aws/block_store_test.go index 20d3ed95d..b3581c1e1 100644 --- a/pkg/cloudprovider/aws/block_store_test.go +++ b/pkg/cloudprovider/aws/block_store_test.go @@ -20,7 +20,6 @@ import ( "sort" "testing" - "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/service/ec2" "github.com/heptio/ark/pkg/util/collections" "github.com/stretchr/testify/assert" @@ -88,72 +87,54 @@ func TestSetVolumeID(t *testing.T) { assert.Equal(t, "vol-updated", actual) } -func TestGetCreateTagsInput(t *testing.T) { +func TestGetTags(t *testing.T) { tests := []struct { name string - snapshotID string arkTags map[string]string volumeTags []*ec2.Tag - expected *ec2.CreateTagsInput + expected []*ec2.Tag }{ { name: "degenerate case (no tags)", - snapshotID: "foo", arkTags: nil, volumeTags: nil, - expected: &ec2.CreateTagsInput{ - Resources: []*string{aws.String("foo")}, - Tags: nil, - }, + expected: nil, }, { - name: "ark tags only get applied", - snapshotID: "foo", + name: "ark tags only get applied", arkTags: map[string]string{ "ark-key1": "ark-val1", "ark-key2": "ark-val2", }, volumeTags: nil, - expected: &ec2.CreateTagsInput{ - Resources: []*string{aws.String("foo")}, - Tags: []*ec2.Tag{ - ec2Tag("ark-key1", "ark-val1"), - ec2Tag("ark-key2", "ark-val2"), - }, + expected: []*ec2.Tag{ + ec2Tag("ark-key1", "ark-val1"), + ec2Tag("ark-key2", "ark-val2"), }, }, { - name: "volume tags only get applied", - snapshotID: "foo", - arkTags: nil, + name: "volume tags only get applied", + arkTags: nil, volumeTags: []*ec2.Tag{ ec2Tag("aws-key1", "aws-val1"), ec2Tag("aws-key2", "aws-val2"), }, - expected: &ec2.CreateTagsInput{ - Resources: []*string{aws.String("foo")}, - Tags: []*ec2.Tag{ - ec2Tag("aws-key1", "aws-val1"), - ec2Tag("aws-key2", "aws-val2"), - }, + expected: []*ec2.Tag{ + ec2Tag("aws-key1", "aws-val1"), + ec2Tag("aws-key2", "aws-val2"), }, }, { name: "non-overlapping ark and volume tags both get applied", - snapshotID: "foo", arkTags: map[string]string{"ark-key": "ark-val"}, volumeTags: []*ec2.Tag{ec2Tag("aws-key", "aws-val")}, - expected: &ec2.CreateTagsInput{ - Resources: []*string{aws.String("foo")}, - Tags: []*ec2.Tag{ - ec2Tag("ark-key", "ark-val"), - ec2Tag("aws-key", "aws-val"), - }, + expected: []*ec2.Tag{ + ec2Tag("ark-key", "ark-val"), + ec2Tag("aws-key", "aws-val"), }, }, { - name: "when tags overlap, ark tags take precedence", - snapshotID: "foo", + name: "when tags overlap, ark tags take precedence", arkTags: map[string]string{ "ark-key": "ark-val", "overlapping-key": "ark-val", @@ -162,27 +143,24 @@ func TestGetCreateTagsInput(t *testing.T) { ec2Tag("aws-key", "aws-val"), ec2Tag("overlapping-key", "aws-val"), }, - expected: &ec2.CreateTagsInput{ - Resources: []*string{aws.String("foo")}, - Tags: []*ec2.Tag{ - ec2Tag("ark-key", "ark-val"), - ec2Tag("overlapping-key", "ark-val"), - ec2Tag("aws-key", "aws-val"), - }, + expected: []*ec2.Tag{ + ec2Tag("ark-key", "ark-val"), + ec2Tag("overlapping-key", "ark-val"), + ec2Tag("aws-key", "aws-val"), }, }, } for _, test := range tests { t.Run(test.name, func(t *testing.T) { - res := getCreateTagsInput(test.snapshotID, test.arkTags, test.volumeTags) + res := getTags(test.arkTags, test.volumeTags) - sort.Slice(res.Tags, func(i, j int) bool { - return *res.Tags[i].Key < *res.Tags[j].Key + sort.Slice(res, func(i, j int) bool { + return *res[i].Key < *res[j].Key }) - sort.Slice(test.expected.Tags, func(i, j int) bool { - return *test.expected.Tags[i].Key < *test.expected.Tags[j].Key + sort.Slice(test.expected, func(i, j int) bool { + return *test.expected[i].Key < *test.expected[j].Key }) assert.Equal(t, test.expected, res)