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..90aa11f --- /dev/null +++ b/bin/blueprint-cfn @@ -0,0 +1,41 @@ +#!/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) + response = cf.describe_stack_resource(stack_name, config_name) + + 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(): + filename = b.sh().dumpf() + p = subprocess.Popen(['sh', 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',