Skip to content

Commit

Permalink
added TLK for bucket_tags w/ failback method to tags function if not set
Browse files Browse the repository at this point in the history
  • Loading branch information
Garison Draper authored and Garison Draper committed Oct 23, 2017
1 parent 90d84a1 commit 1660c94
Show file tree
Hide file tree
Showing 5 changed files with 105 additions and 12 deletions.
43 changes: 36 additions & 7 deletions docs/config.rst
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,33 @@ See the `CloudFormation Limits Reference`_.

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


S3 Bucket tags
----

Various resources in AWS support arbitrary key-value pair tags. You can set
the `bucket_tags` Top Level Keyword to populate tags on all S3 buckets Staker
attempts to create for CloudFormation template uploads, inclduing the S3 bucket
created by the aws_lambda pre-hook.

If bucket_tags is not set in your Configuration, stacker will fallback to the
method used to determine tags in your config by the `tags` top level keyword.
The `bucket_tags` keyword takes precedence over `tags` when applying. Example::

bucket_tags:
"hello": world
"my_tag:with_colons_in_key": ${dynamic_tag_value_from_my_env}
simple_tag: simple value

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

bucket_tags: {}

Tags updates get applied on every stacker run

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

Module Paths
------------
When setting the ``classpath`` for blueprints/hooks, it is sometimes desirable to
Expand Down Expand Up @@ -220,29 +247,31 @@ Tags
----

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

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

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

tags:
"hello": world
"my_tag:with_colons_in_key": ${dynamic_tag_value_from_my_env}
simple_tag: simple value


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

tags: {}

Tags are updated on every stacker run
Tags updates get applied on every stacker run

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

Expand Down
2 changes: 2 additions & 0 deletions stacker/config/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -339,6 +339,8 @@ class Config(Model):

tags = DictType(StringType, serialize_when_none=False)

bucket_tags = DictType(StringType, serialize_when_none=False)

mappings = DictType(
DictType(DictType(StringType)), serialize_when_none=False)

Expand Down
16 changes: 16 additions & 0 deletions stacker/context.py
Original file line number Diff line number Diff line change
Expand Up @@ -97,11 +97,27 @@ def upload_templates_to_s3(self):
def tags(self):
tags = self.config.tags
if tags is not None:
if "stacker_namespace" in tags:
return tags
tags["stacker_namespace"] = self.namespace
return tags
if self.namespace:
return {"stacker_namespace": self.namespace}
return {}

@property
def s3_bucket_tags(self):
s3_bucket_tags = self.config.bucket_tags
if s3_bucket_tags is not None:
return s3_bucket_tags
else:
s3_bucket_tags = self.config.tags
if s3_bucket_tags is not None:
return s3_bucket_tags
if self.namespace:
return {"stacker_namespace": self.namespace}
return {}

@property
def _base_fqn(self):
return self.namespace.replace(".", "-").lower()
Expand Down
33 changes: 33 additions & 0 deletions stacker/tests/actions/test_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,17 @@ def test_ensure_cfn_bucket_exists(self):
"Bucket": ANY,
}
)
stubber.add_response(
"put_bucket_tagging",
service_response={},
expected_params={
"Bucket": ANY,
"Tagging": {
"TagSet": [
{"Key": "stacker_namespace",
"Value": u"mynamespace"}]}
}
)
with stubber:
action.ensure_cfn_bucket()

Expand All @@ -65,6 +76,17 @@ def test_ensure_cfn_bucket_doesnt_exist_us_east(self):
"Bucket": ANY,
}
)
stubber.add_response(
"put_bucket_tagging",
service_response={},
expected_params={
"Bucket": ANY,
"Tagging": {
"TagSet": [
{"Key": "stacker_namespace",
"Value": u"mynamespace"}]}
}
)
with stubber:
action.ensure_cfn_bucket()

Expand All @@ -91,6 +113,17 @@ def test_ensure_cfn_bucket_doesnt_exist_us_west(self):
}
}
)
stubber.add_response(
"put_bucket_tagging",
service_response={},
expected_params={
"Bucket": ANY,
"Tagging": {
"TagSet": [
{"Key": "stacker_namespace",
"Value": u"mynamespace"}]}
}
)
with stubber:
action.ensure_cfn_bucket()

Expand Down
23 changes: 18 additions & 5 deletions stacker/util.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@
import yaml
from collections import OrderedDict
from git import Repo
import stacker.actions.build
import botocore.exceptions

logger = logging.getLogger(__name__)
Expand Down Expand Up @@ -481,8 +480,8 @@ def ensure_s3_bucket(s3_client, bucket_name, bucket_region, context):
try:
# Checking is bucket exists
s3_client.head_bucket(Bucket=bucket_name)
# pulling tags from context
tagset = stacker.actions.build.build_stack_tags(context)
# pulling tags from s3_bucket_tags function
tagset = _s3_bucket_tags(context)
# setting tags on every run - must have permission to perform
# the s3:PutBucketTagging action
s3_client.put_bucket_tagging(Bucket=bucket_name,
Expand All @@ -498,8 +497,8 @@ def ensure_s3_bucket(s3_client, bucket_name, bucket_region, context):
create_args["CreateBucketConfiguration"] = {
"LocationConstraint": location_constraint
}
# pulling tags from context
tagset = stacker.actions.build.build_stack_tags(context)
# pulling tags from s3_bucket_tags function
tagset = _s3_bucket_tags(context)
s3_client.create_bucket(**create_args)
# setting tags on every run - must have permission to perform
# the s3:PutBucketTagging action
Expand All @@ -516,6 +515,20 @@ def ensure_s3_bucket(s3_client, bucket_name, bucket_region, context):
raise


def _s3_bucket_tags(context):
"""Returns the tags to be applied for a S3 bucket.
Args:
context (:class:`stacker.context.Context`): The stacker context, used
set the S3 bucket tags from the stacker config
Returns:
List of dictionaries containing tags to apply to that bucket.
"""
return [
{'Key': t[0], 'Value': t[1]} for t in context.s3_bucket_tags.items()]


class SourceProcessor():
"""Makes remote python package sources available in the running python
environment."""
Expand Down

0 comments on commit 1660c94

Please sign in to comment.