From 0fc97edd87b25ae411f85c1efe4da3a0174d3926 Mon Sep 17 00:00:00 2001 From: Jason Allum Date: Mon, 11 Jun 2012 17:24:58 -0400 Subject: [PATCH 1/3] Added initial version of the blueprint-cfn command --- bin/blueprint | 2 +- bin/blueprint-cfn | 44 ++++++++++++++++++++++++++++++++++++++++ blueprint/frontend/sh.py | 29 +++++++++++++++----------- setup.py.mustache | 1 + 4 files changed, 63 insertions(+), 13 deletions(-) create mode 100755 bin/blueprint-cfn diff --git a/bin/blueprint b/bin/blueprint index fea7b0c..f343950 100755 --- a/bin/blueprint +++ b/bin/blueprint @@ -9,5 +9,5 @@ COMMAND="$0-$1" } echo "Usage: $(basename $0) [...]" >&2 -echo "Common commands: list, create, show, diff, apply, destroy, git" >&2 +echo "Common commands: list, create, show, diff, apply, destroy, git, cfn" >&2 exit 1 diff --git a/bin/blueprint-cfn b/bin/blueprint-cfn new file mode 100755 index 0000000..8c30de2 --- /dev/null +++ b/bin/blueprint-cfn @@ -0,0 +1,44 @@ +#!/usr/bin/python + +import errno +import logging +import optparse +import sys +import json +import subprocess + +try: + import boto +except: + raise SystemExit("The boto module is required.") + +import blueprint.cli +from blueprint import context_managers + + +if __name__ == '__main__': + parser = optparse.OptionParser('usage: %prog [options] stack config') + parser.add_option("-a", "--aws-access-key-id", dest="aws_access_key_id") + parser.add_option("-k", "--aws-secret-access-key", dest="aws_secret_access_key") + (options, args) = parser.parse_args() + if len(args) != 2 or (not options.aws_access_key_id or not options.aws_secret_access_key): + parser.print_help() + exit(1) + + stack_name = args[0] + config_name = args[1] + + cf = boto.connect_cloudformation(options.aws_access_key_id, options.aws_secret_access_key) + stacks = cf.describe_stacks(stack_name_or_id=stack_name) + if 1 != len(stacks): + exit(2) + stack = stacks[0] + + metadata = json.loads(stack.describe_resource(config_name)['DescribeStackResourceResponse']['DescribeStackResourceResult']['StackResourceDetail']['Metadata']) + as_json = json.dumps(metadata['AWS::CloudFormation::Init']['config'], indent=2) + b = blueprint.Blueprint.loads(as_json) + with context_managers.mkdtemp(): + filename = b.sh().dumpf() + p = subprocess.Popen(['cat', filename], close_fds=True) + p.communicate() + sys.exit(p.returncode) diff --git a/blueprint/frontend/sh.py b/blueprint/frontend/sh.py index 9ea1e9b..e060231 100644 --- a/blueprint/frontend/sh.py +++ b/blueprint/frontend/sh.py @@ -10,6 +10,7 @@ import re from shutil import copyfile import tarfile +from hashlib import md5 from blueprint import git from blueprint import util @@ -36,7 +37,7 @@ def service_source(manager, service, dirname): service_package=service_package, service_source=service_source) - commit = git.rev_parse(b.name) + commit = None if b.name is None else git.rev_parse(b.name) tree = None if commit is None else git.tree(commit) def source(dirname, filename, gen_content, url): """ @@ -46,14 +47,15 @@ def source(dirname, filename, gen_content, url): s.add('MD5SUM="$(find "{0}" -printf %T@\\\\n | md5sum)"', args=(dirname,)) if url is not None: + tmp_filename = md5(url).hexdigest() s.add_list(('curl -o "{0}" "{1}"',), ('wget -O "{0}" "{1}"',), - args=(filename, url), + args=(tmp_filename, url), operator='||') - if '.zip' == pathname[-4:]: - s.add('unzip "{0}" -d "{1}"', args=(filename, dirname)) + if '.zip' == filename[-4:]: + s.add('unzip "{0}" -d "{1}"', args=(tmp_filename, dirname)) else: - s.add('mkdir -p "{1}" && tar xf "{0}" -C "{1}"', args=(filename, dirname)) + s.add('mkdir -p "{1}" && tar xf "{0}" -C "{1}"', args=(tmp_filename, dirname)) elif secret is not None: s.add_list(('curl -O "{0}/{1}/{2}/{3}"',), ('wget "{0}/{1}/{2}/{3}"',), @@ -111,12 +113,15 @@ def file(pathname, f): else: commands = ('cat',) s.add(*commands, stdin=f['content'], stdout=pathname) - if 'root' != f['owner']: - s.add('chown {0} "{1}"', args=(f['owner'], pathname)) - if 'root' != f['group']: - s.add('chgrp {0} "{1}"', args=(f['group'], pathname)) - if '100644' != f['mode']: - s.add('chmod {0} "{1}"', args=(f['mode'][-4:], pathname)) + owner = f.get('owner', f.get('user', 'root')) + if 'root' != owner: + s.add('chown {0} "{1}"', args=(owner, pathname)) + group = f.get('group', 'root') + if 'root' != group: + s.add('chgrp {0} "{1}"', args=(group, pathname)) + mode = f.get('mode', '100644') + if '100644' != mode: + s.add('chmod {0} "{1}"', args=(mode[-4:], pathname)) for manager, service in lut['files'][pathname]: s.add('[ "$MD5SUM" != "$(md5sum "{0}")" ] && {1}=1', args=(pathname, manager.env_var(service))) @@ -282,7 +287,7 @@ def dumps(self): """ Generate a string containing shell code and all file contents. """ - return ''.join(self.out) + return '\n'.join(self.out) def dumpf(self, gzip=False): """ diff --git a/setup.py.mustache b/setup.py.mustache index 1a5a94d..6187c24 100644 --- a/setup.py.mustache +++ b/setup.py.mustache @@ -11,6 +11,7 @@ setup(name='blueprint', scripts=['bin/blueprint', 'bin/blueprint-apply', 'bin/blueprint-create', + 'bin/blueprint-cfn', 'bin/blueprint-destroy', 'bin/blueprint-diff', 'bin/blueprint-git', From a83e449dfe3de970ed1043e34419d886326ec5aa Mon Sep 17 00:00:00 2001 From: Jason Allum Date: Mon, 11 Jun 2012 17:48:21 -0400 Subject: [PATCH 2/3] Updated to run the generated script directly --- bin/blueprint-cfn | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bin/blueprint-cfn b/bin/blueprint-cfn index 8c30de2..d29fb4d 100755 --- a/bin/blueprint-cfn +++ b/bin/blueprint-cfn @@ -39,6 +39,6 @@ if __name__ == '__main__': b = blueprint.Blueprint.loads(as_json) with context_managers.mkdtemp(): filename = b.sh().dumpf() - p = subprocess.Popen(['cat', filename], close_fds=True) + p = subprocess.Popen(['sh', filename], close_fds=True) p.communicate() sys.exit(p.returncode) From 1f358f518efe477d656cdba5ed76880386ce4404 Mon Sep 17 00:00:00 2001 From: Jason Allum Date: Tue, 12 Jun 2012 09:45:47 -0400 Subject: [PATCH 3/3] Cut the number of AWS calls to one. --- bin/blueprint-cfn | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/bin/blueprint-cfn b/bin/blueprint-cfn index d29fb4d..90aa11f 100755 --- a/bin/blueprint-cfn +++ b/bin/blueprint-cfn @@ -29,12 +29,9 @@ if __name__ == '__main__': config_name = args[1] cf = boto.connect_cloudformation(options.aws_access_key_id, options.aws_secret_access_key) - stacks = cf.describe_stacks(stack_name_or_id=stack_name) - if 1 != len(stacks): - exit(2) - stack = stacks[0] + response = cf.describe_stack_resource(stack_name, config_name) - metadata = json.loads(stack.describe_resource(config_name)['DescribeStackResourceResponse']['DescribeStackResourceResult']['StackResourceDetail']['Metadata']) + metadata = json.loads(response['DescribeStackResourceResponse']['DescribeStackResourceResult']['StackResourceDetail']['Metadata']) as_json = json.dumps(metadata['AWS::CloudFormation::Init']['config'], indent=2) b = blueprint.Blueprint.loads(as_json) with context_managers.mkdtemp():