Skip to content

Commit 1660c94

Browse files
Garison DraperGarison Draper
authored andcommitted
added TLK for bucket_tags w/ failback method to tags function if not set
1 parent 90d84a1 commit 1660c94

File tree

5 files changed

+105
-12
lines changed

5 files changed

+105
-12
lines changed

docs/config.rst

Lines changed: 36 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,33 @@ See the `CloudFormation Limits Reference`_.
9090

9191
.. _`CloudFormation Limits Reference`: http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/cloudformation-limits.html
9292

93+
94+
S3 Bucket tags
95+
----
96+
97+
Various resources in AWS support arbitrary key-value pair tags. You can set
98+
the `bucket_tags` Top Level Keyword to populate tags on all S3 buckets Staker
99+
attempts to create for CloudFormation template uploads, inclduing the S3 bucket
100+
created by the aws_lambda pre-hook.
101+
102+
If bucket_tags is not set in your Configuration, stacker will fallback to the
103+
method used to determine tags in your config by the `tags` top level keyword.
104+
The `bucket_tags` keyword takes precedence over `tags` when applying. Example::
105+
106+
bucket_tags:
107+
"hello": world
108+
"my_tag:with_colons_in_key": ${dynamic_tag_value_from_my_env}
109+
simple_tag: simple value
110+
111+
If you prefer to have no tags applied to your stacks (versus the default tags
112+
that stacker applies), specify an empty map for the top-level keyword::
113+
114+
bucket_tags: {}
115+
116+
Tags updates get applied on every stacker run
117+
118+
.. _`AWS CloudFormation Resource Tags Type`: http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-resource-tags.html
119+
93120
Module Paths
94121
------------
95122
When setting the ``classpath`` for blueprints/hooks, it is sometimes desirable to
@@ -220,29 +247,31 @@ Tags
220247
----
221248

222249
Various resources in AWS support arbitrary key-value pair tags. You can set
223-
the `tags` Top Level Keyword to populate tags on all Resources that support
224-
that feature. The S3 bucket created by stacker for CloudFormation uploads and
225-
all CloudFormation stack-level resources, including automatically created tags,
226-
are propagated to resources that AWS CloudFormation supports. See
227-
`AWS CloudFormation Resource Tags Type`_ for more details.
250+
the `tags` Top Level Keyword to populate tags on all Resources that Staker
251+
attempts to create via CloudFormation. All CloudFormation stack-level resources,
252+
including automatically created tags, are propagated to resources that AWS
253+
CloudFormation supports. See `AWS CloudFormation Resource Tags Type`_ for
254+
more details.
228255

229256
If no tags are specified, the `stacker_namespace` tag is applied to your stack
230257
with the value of `namespace` as the tag value.
231258

232259
If you prefer to apply a custom set of tags, specify the top-level keyword
233-
`tags` as a map. Example::
260+
`tags` as a map. The `stacker_namespace` tag will be automaticly added as well
261+
to help identify resources created by Stacker. Example::
234262

235263
tags:
236264
"hello": world
237265
"my_tag:with_colons_in_key": ${dynamic_tag_value_from_my_env}
238266
simple_tag: simple value
239267

268+
240269
If you prefer to have no tags applied to your stacks (versus the default tags that stacker applies), specify an empty
241270
map for the top-level keyword::
242271

243272
tags: {}
244273

245-
Tags are updated on every stacker run
274+
Tags updates get applied on every stacker run
246275

247276
.. _`AWS CloudFormation Resource Tags Type`: http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-resource-tags.html
248277

stacker/config/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -339,6 +339,8 @@ class Config(Model):
339339

340340
tags = DictType(StringType, serialize_when_none=False)
341341

342+
bucket_tags = DictType(StringType, serialize_when_none=False)
343+
342344
mappings = DictType(
343345
DictType(DictType(StringType)), serialize_when_none=False)
344346

stacker/context.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,11 +97,27 @@ def upload_templates_to_s3(self):
9797
def tags(self):
9898
tags = self.config.tags
9999
if tags is not None:
100+
if "stacker_namespace" in tags:
101+
return tags
102+
tags["stacker_namespace"] = self.namespace
100103
return tags
101104
if self.namespace:
102105
return {"stacker_namespace": self.namespace}
103106
return {}
104107

108+
@property
109+
def s3_bucket_tags(self):
110+
s3_bucket_tags = self.config.bucket_tags
111+
if s3_bucket_tags is not None:
112+
return s3_bucket_tags
113+
else:
114+
s3_bucket_tags = self.config.tags
115+
if s3_bucket_tags is not None:
116+
return s3_bucket_tags
117+
if self.namespace:
118+
return {"stacker_namespace": self.namespace}
119+
return {}
120+
105121
@property
106122
def _base_fqn(self):
107123
return self.namespace.replace(".", "-").lower()

stacker/tests/actions/test_base.py

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,17 @@ def test_ensure_cfn_bucket_exists(self):
4242
"Bucket": ANY,
4343
}
4444
)
45+
stubber.add_response(
46+
"put_bucket_tagging",
47+
service_response={},
48+
expected_params={
49+
"Bucket": ANY,
50+
"Tagging": {
51+
"TagSet": [
52+
{"Key": "stacker_namespace",
53+
"Value": u"mynamespace"}]}
54+
}
55+
)
4556
with stubber:
4657
action.ensure_cfn_bucket()
4758

@@ -65,6 +76,17 @@ def test_ensure_cfn_bucket_doesnt_exist_us_east(self):
6576
"Bucket": ANY,
6677
}
6778
)
79+
stubber.add_response(
80+
"put_bucket_tagging",
81+
service_response={},
82+
expected_params={
83+
"Bucket": ANY,
84+
"Tagging": {
85+
"TagSet": [
86+
{"Key": "stacker_namespace",
87+
"Value": u"mynamespace"}]}
88+
}
89+
)
6890
with stubber:
6991
action.ensure_cfn_bucket()
7092

@@ -91,6 +113,17 @@ def test_ensure_cfn_bucket_doesnt_exist_us_west(self):
91113
}
92114
}
93115
)
116+
stubber.add_response(
117+
"put_bucket_tagging",
118+
service_response={},
119+
expected_params={
120+
"Bucket": ANY,
121+
"Tagging": {
122+
"TagSet": [
123+
{"Key": "stacker_namespace",
124+
"Value": u"mynamespace"}]}
125+
}
126+
)
94127
with stubber:
95128
action.ensure_cfn_bucket()
96129

stacker/util.py

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@
1313
import yaml
1414
from collections import OrderedDict
1515
from git import Repo
16-
import stacker.actions.build
1716
import botocore.exceptions
1817

1918
logger = logging.getLogger(__name__)
@@ -481,8 +480,8 @@ def ensure_s3_bucket(s3_client, bucket_name, bucket_region, context):
481480
try:
482481
# Checking is bucket exists
483482
s3_client.head_bucket(Bucket=bucket_name)
484-
# pulling tags from context
485-
tagset = stacker.actions.build.build_stack_tags(context)
483+
# pulling tags from s3_bucket_tags function
484+
tagset = _s3_bucket_tags(context)
486485
# setting tags on every run - must have permission to perform
487486
# the s3:PutBucketTagging action
488487
s3_client.put_bucket_tagging(Bucket=bucket_name,
@@ -498,8 +497,8 @@ def ensure_s3_bucket(s3_client, bucket_name, bucket_region, context):
498497
create_args["CreateBucketConfiguration"] = {
499498
"LocationConstraint": location_constraint
500499
}
501-
# pulling tags from context
502-
tagset = stacker.actions.build.build_stack_tags(context)
500+
# pulling tags from s3_bucket_tags function
501+
tagset = _s3_bucket_tags(context)
503502
s3_client.create_bucket(**create_args)
504503
# setting tags on every run - must have permission to perform
505504
# the s3:PutBucketTagging action
@@ -516,6 +515,20 @@ def ensure_s3_bucket(s3_client, bucket_name, bucket_region, context):
516515
raise
517516

518517

518+
def _s3_bucket_tags(context):
519+
"""Returns the tags to be applied for a S3 bucket.
520+
521+
Args:
522+
context (:class:`stacker.context.Context`): The stacker context, used
523+
set the S3 bucket tags from the stacker config
524+
525+
Returns:
526+
List of dictionaries containing tags to apply to that bucket.
527+
"""
528+
return [
529+
{'Key': t[0], 'Value': t[1]} for t in context.s3_bucket_tags.items()]
530+
531+
519532
class SourceProcessor():
520533
"""Makes remote python package sources available in the running python
521534
environment."""

0 commit comments

Comments
 (0)