Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Stack instance tagging #39

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
70 changes: 7 additions & 63 deletions gonzo/backends/__init__.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import json
from gonzo.aws.route53 import Route53
from gonzo.config import config_proxy as config
from gonzo.helpers.document_loader import get_parsed_document
from gonzo.backends.instance_utils import add_default_security_groups
from gonzo.backends.template_utils import (generate_stack_template,
get_default_instance_tags_dict)


def get_current_cloud():
Expand Down Expand Up @@ -63,13 +65,8 @@ def launch_instance(env_type, size=None,
key_name = config.CLOUD['PUBLIC_KEY_NAME']

tags = extra_tags or {}
tags.update({
'environment': environment,
'server_type': server_type,
})

if owner:
tags['owner'] = owner
tags.update(
get_default_instance_tags_dict(environment, server_type, owner))

security_groups = add_default_security_groups(server_type, security_groups)
for security_group in security_groups:
Expand All @@ -87,74 +84,21 @@ def launch_instance(env_type, size=None,
user_data=user_data, tags=tags)


def add_default_security_groups(server_type, additional_security_groups=None):
# Set defaults
security_groups = [server_type, 'gonzo']

# Add argument passed groups
if additional_security_groups is not None:
security_groups += additional_security_groups

# Remove Duplicates
security_groups = list(set(security_groups))

return security_groups


def create_if_not_exist_security_group(group_name):
cloud = get_current_cloud()
if not cloud.security_group_exists(group_name):
cloud.create_security_group(group_name)


def configure_instance(instance):
instance.create_dns_entry()


def generate_stack_template(stack_type, stack_name,
template_uri, template_params,
owner=None):
template_uri = config.get_namespaced_cloud_config_value(
'ORCHESTRATION_TEMPLATE_URIS', stack_type, override=template_uri)
if template_uri is None:
raise ValueError('A template must be specified by argument or '
'in config')

template = get_parsed_document(stack_name, template_uri,
'ORCHESTRATION_TEMPLATE_PARAMS',
template_params)

# Parse as json for validation and for injecting gonzo defaults
template_dict = json.loads(template)

if owner:
template_dict = insert_stack_owner_output(template_dict, owner)

return json.dumps(template_dict)


def insert_stack_owner_output(template_dict, owner):
""" Adds a stack output to a template with key "owner" """
template_outputs = template_dict.get('Outputs', {})
template_outputs['owner'] = {
'Value': owner,
'Description': "This stack's launcher (Managed by Gonzo)"
}
template_dict.update({'Outputs': template_outputs})

return template_dict


def launch_stack(stack_name, template_uri, template_params, owner=None):
""" Launch stacks """

unique_stack_name = get_next_hostname(stack_name)

cloud = get_current_cloud()
template = generate_stack_template(stack_name, unique_stack_name,
template_uri, template_params,
cloud.stack_class.instance_type,
owner)

cloud = get_current_cloud()
return cloud.launch_stack(unique_stack_name, template)


Expand Down
29 changes: 29 additions & 0 deletions gonzo/backends/instance_utils.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
def get_default_instance_tags_dict(environment, server_type, owner=None):
""" Generate a tag dictionary suitable for identifying instance ownership
and for identifying instances for gonzo releases.
"""
default_tags = {
'environment': environment,
'server_type': server_type,
}
if owner:
default_tags['owner'] = owner
return default_tags


def add_default_security_groups(server_type, additional_security_groups=None):
# Set defaults
security_groups = [server_type, 'gonzo']

# Add argument passed groups
if additional_security_groups is not None:
security_groups += additional_security_groups

# Remove Duplicates
security_groups = list(set(security_groups))

return security_groups


def configure_instance(instance):
instance.create_dns_entry()
74 changes: 74 additions & 0 deletions gonzo/backends/template_utils.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
import json

from gonzo.config import config_proxy as config
from gonzo.helpers.document_loader import get_parsed_document
from gonzo.backends.instance_utils import get_default_instance_tags_dict


def generate_stack_template(stack_type, stack_name,
template_uri, template_params,
instance_resource_type, owner=None):
template_uri = config.get_namespaced_cloud_config_value(
'ORCHESTRATION_TEMPLATE_URIS', stack_type, override=template_uri)
if template_uri is None:
raise ValueError('A template must be specified by argument or '
'in config')

template = get_parsed_document(stack_name, template_uri,
'ORCHESTRATION_TEMPLATE_PARAMS',
template_params)

# Parse as json for validation and for injecting gonzo defaults
template_dict = json.loads(template)

if owner:
template_dict = insert_stack_owner_output(template_dict, owner)

template_dict = insert_stack_instance_tags(
template_dict, instance_resource_type, stack_name, owner)

return json.dumps(template_dict)


def insert_stack_owner_output(template_dict, owner):
""" Adds a stack output to a template with key "owner" """
template_outputs = template_dict.get('Outputs', {})
template_outputs['owner'] = {
'Value': owner,
'Description': "This stack's launcher (Managed by Gonzo)"
}
template_dict.update({'Outputs': template_outputs})

return template_dict


def insert_stack_instance_tags(template_dict, instance_resource_type,
environment, owner):
""" Updates tags of each instance resource to include gonzo defaults
"""
resources = template_dict.get('Resources', {})
for resource_name, resource_details in resources.items():
if resource_details['Type'] != instance_resource_type:
# We only care about tagging instances
continue

# Ensure proper tag structure exists
if 'Properties' not in resource_details:
resource_details['Properties'] = {}
if 'Tags' not in resource_details['Properties']:
resource_details['Properties']['Tags'] = []

existing_tags = resource_details['Properties']['Tags']
default_tags = get_default_instance_tags_dict(
environment, resource_name, owner)

# Remove potential duplicates
for existing_tag in existing_tags:
if existing_tag['Key'] in default_tags.keys():
existing_tags.remove(existing_tag)

# Add defaults
for key, value in default_tags.iteritems():
existing_tags.append({'Key': key, 'Value': value})

return template_dict
3 changes: 2 additions & 1 deletion gonzo/scripts/instance/launch.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,9 @@
import sys
from time import sleep

from gonzo.backends import launch_instance, configure_instance
from gonzo.backends import launch_instance
from gonzo.exceptions import CommandError, DataError
from gonzo.backends.instance_utils import configure_instance
from gonzo.scripts.utils import colorize
from gonzo.utils import abort, csv_dict, csv_list

Expand Down
2 changes: 1 addition & 1 deletion tests/scripts/stack/test_launch.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

@patch('gonzo.scripts.stack.launch.print_stack')
@patch('gonzo.scripts.stack.launch.wait_for_stack_complete')
@patch('gonzo.backends.get_parsed_document')
@patch('gonzo.backends.template_utils.get_parsed_document')
def test_launch(get_parsed_doc, wait_for_stack_complete,
print_stack, cloud_fixture, minimum_config_fixture):
(get_cloud, get_hostname, create_security_group) = cloud_fixture
Expand Down
11 changes: 8 additions & 3 deletions tests/scripts/stack/test_ownership.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,19 @@
import json

from mock import patch
from gonzo.backends import generate_stack_template

from gonzo.backends.template_utils import generate_stack_template


@patch("gonzo.backends.get_parsed_document")
@patch("gonzo.backends.template_utils.get_parsed_document")
def test_ownership(get_parsed_doc):

template = """
{
"Resources" : {
"existing_resource": {}
"existing_resource": {
"Type": "moot"
}
},

"Outputs" : {
Expand All @@ -22,6 +26,7 @@ def test_ownership(get_parsed_doc):
get_parsed_doc.return_value = template

template = generate_stack_template(None, None, "template_uri", None,
instance_resource_type="",
owner="test-user")
template_dict = json.loads(template)

Expand Down
68 changes: 68 additions & 0 deletions tests/scripts/stack/test_tagging.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import json

from mock import patch

from gonzo.backends.template_utils import generate_stack_template


@patch("gonzo.backends.template_utils.get_default_instance_tags_dict")
@patch("gonzo.backends.template_utils.get_parsed_document")
def test_instance_tagging(get_parsed_doc, get_tags_dict):
tags_dict = {
"environment": "overwritten",
"extra": "value",
}
get_tags_dict.return_value = tags_dict

template = """
{
"Resources" : {
"decoy-resource": {
"Type": "not-interesting-type",
"Properties": {}
},
"no-other-tags": {
"Type": "instance-type",
"Properties": {}
},
"existing-tags": {
"Type": "instance-type",
"Properties": {
"Tags": [
{
"Key": "decoy",
"Value": "leave me be"
},
{
"Key": "environment",
"Value": "overwrite me"
}
]
}
}
}
}"""
get_parsed_doc.return_value = template

template = generate_stack_template(None, None, "template_uri", None,
instance_resource_type="instance-type",
owner="test-user")
template_dict = json.loads(template)

resources = template_dict.get("Resources", None)

assert resources is not None

decoy_resource = resources.get("decoy-resource", None)
assert decoy_resource is not None
assert decoy_resource['Properties'] == {}

no_other_tags_resource = resources.get("no-other-tags", None)
no_other_tags_tags = no_other_tags_resource['Properties']['Tags']
for key, value in tags_dict.items():
assert {'Key': key, 'Value': value} in no_other_tags_tags

existing_tags_resource = resources.get("existing-tags", None)
existing_tags_tags = existing_tags_resource['Properties']['Tags']
for key, value in tags_dict.items():
assert {'Key': key, 'Value': value} in existing_tags_tags