├── .gitignore ├── .gitmodules ├── LICENSE ├── MANIFEST.in ├── Makefile ├── README.md ├── bin ├── blueprint ├── blueprint-apply ├── blueprint-create ├── blueprint-destroy ├── blueprint-diff ├── blueprint-git ├── blueprint-list ├── blueprint-prune ├── blueprint-pull ├── blueprint-push ├── blueprint-rules ├── blueprint-show ├── blueprint-show-files ├── blueprint-show-ignore ├── blueprint-show-packages ├── blueprint-show-services ├── blueprint-show-sources ├── blueprint-split └── blueprint-template.mustache ├── blueprint ├── __init__.py ├── backend │ ├── __init__.py │ ├── apt.py │ ├── files.py │ ├── gem.py │ ├── npm.py │ ├── php.py │ ├── pypi.py │ ├── sources.py │ └── yum.py ├── cli.py ├── context_managers.py ├── deps.py ├── frontend │ ├── __init__.py │ ├── bcfg2.py │ ├── blueprint-template.d │ │ ├── lsb.sh │ │ ├── net.sh │ │ ├── proc.sh │ │ └── uname.sh │ ├── cfengine3.py │ ├── cfn.json │ ├── cfn.py │ ├── chef.py │ ├── puppet.py │ ├── rules.py │ └── sh.py ├── git.py ├── interactive.py ├── io │ ├── __init__.py │ ├── http.py │ └── server │ │ ├── __init__.py │ │ ├── backend.py │ │ ├── librato.py │ │ └── statsd.py ├── managers.py ├── rules.py ├── services.py ├── util.py └── walk.py ├── bootstrap.sh ├── etc ├── bash_completion.d │ └── blueprint └── init │ └── blueprint-io-server.conf ├── man ├── man1 │ ├── blueprint-apply.1 │ ├── blueprint-apply.1.ronn │ ├── blueprint-create.1 │ ├── blueprint-create.1.ronn │ ├── blueprint-destroy.1 │ ├── blueprint-destroy.1.ronn │ ├── blueprint-diff.1 │ ├── blueprint-diff.1.ronn │ ├── blueprint-git.1 │ ├── blueprint-git.1.ronn │ ├── blueprint-list.1 │ ├── blueprint-list.1.ronn │ ├── blueprint-prune.1 │ ├── blueprint-prune.1.ronn │ ├── blueprint-pull.1 │ ├── blueprint-pull.1.ronn │ ├── blueprint-push.1 │ ├── blueprint-push.1.ronn │ ├── blueprint-rules.1 │ ├── blueprint-rules.1.ronn │ ├── blueprint-show-files.1 │ ├── blueprint-show-files.1.ronn │ ├── blueprint-show-ignore.1 │ ├── blueprint-show-ignore.1.ronn │ ├── blueprint-show-packages.1 │ ├── blueprint-show-packages.1.ronn │ ├── blueprint-show-services.1 │ ├── blueprint-show-services.1.ronn │ ├── blueprint-show-sources.1 │ ├── blueprint-show-sources.1.ronn │ ├── blueprint-show.1 │ ├── blueprint-show.1.ronn │ ├── blueprint-split.1 │ ├── blueprint-split.1.ronn │ ├── blueprint-template.1 │ ├── blueprint-template.1.ronn │ ├── blueprint.1 │ └── blueprint.1.ronn ├── man5 │ ├── blueprint-rules.5 │ ├── blueprint-rules.5.ronn │ ├── blueprint-template.5 │ ├── blueprint-template.5.ronn │ ├── blueprint.5 │ ├── blueprint.5.ronn │ ├── blueprint.cfg.5 │ ├── blueprint.cfg.5.ronn │ ├── blueprintignore.5 │ └── blueprintignore.5.ronn └── man7 │ ├── blueprint-template.7 │ ├── blueprint-template.7.ronn │ ├── blueprint.7 │ └── blueprint.7.ronn ├── pydir.py ├── setup.py.mustache ├── tests.py ├── tests.sh └── tests ├── devstructure-debian-archive.json ├── empty.json ├── example.blueprint-rules ├── file-content-json.json ├── file-source-base64.json ├── file-source.json ├── invalid-schema.json ├── invalid-syntax.json ├── rpm.json ├── source-url-noname.json ├── source-url.json ├── sources.json └── ssh.blueprint-rules /.gitignore: -------------------------------------------------------------------------------- 1 | *.deb 2 | *.egg 3 | *.egg-info 4 | *.html 5 | *.pyc 6 | *.pyo 7 | *.swp 8 | *.tar 9 | *.tar.gz 10 | .coverage 11 | bin/blueprint-template 12 | blueprint/frontend/mustache.sh 13 | build 14 | control 15 | dist 16 | setup.py 17 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "mustache.sh"] 2 | path = mustache.sh 3 | url = git://github.com/rcrowley/mustache.sh.git 4 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2011 DevStructure. All rights reserved. 2 | 3 | Redistribution and use in source and binary forms, with or without 4 | modification, are permitted provided that the following conditions are 5 | met: 6 | 7 | 1. Redistributions of source code must retain the above copyright 8 | notice, this list of conditions and the following disclaimer. 9 | 10 | 2. Redistributions in binary form must reproduce the above 11 | copyright notice, this list of conditions and the following 12 | disclaimer in the documentation and/or other materials provided 13 | with the distribution. 14 | 15 | THIS SOFTWARE IS PROVIDED BY DEVSTRUCTURE ``AS IS'' AND ANY EXPRESS 16 | OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 17 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 18 | DISCLAIMED. IN NO EVENT SHALL DEVSTRUCTURE OR CONTRIBUTORS BE LIABLE 19 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 20 | CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 21 | SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 22 | INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 23 | CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 24 | ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 25 | THE POSSIBILITY OF SUCH DAMAGE. 26 | 27 | The views and conclusions contained in the software and documentation 28 | are those of the authors and should not be interpreted as representing 29 | official policies, either expressed or implied, of DevStructure. 30 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include blueprint/frontend/cfn.json 2 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | VERSION=3.4.2 2 | BUILD=1 3 | 4 | PYTHON=$(shell which python2.7 || which python27 || which python2.6 || which python26 || which python) 5 | PYTHON_VERSION=$(shell ${PYTHON} -c "from distutils.sysconfig import get_python_version; print(get_python_version())") 6 | 7 | prefix=/usr/local 8 | bindir=${prefix}/bin 9 | libdir=${prefix}/lib 10 | pydir=$(shell ${PYTHON} pydir.py ${libdir}) 11 | mandir=${prefix}/share/man 12 | sysconfdir=${prefix}/etc 13 | 14 | all: bin/blueprint-template blueprint/frontend/mustache.sh 15 | 16 | bin/blueprint-template: bin/blueprint-template.mustache 17 | pydir=$(pydir) mustache.sh/bin/mustache.sh <$< >$@ 18 | chmod 755 $@ 19 | 20 | blueprint/frontend/mustache.sh: mustache.sh/lib/mustache.sh 21 | install -m644 $< $@ 22 | 23 | clean: 24 | rm -f bin/blueprint-template blueprint/frontend/mustache.sh 25 | rm -rf \ 26 | *.deb \ 27 | setup.py blueprint-$(VERSION) build dist *.egg *.egg-info \ 28 | man/man*/*.html 29 | find . -name \*.pyc -delete 30 | 31 | test: 32 | nosetests --nocapture --with-coverage --cover-package=blueprint 33 | 34 | install: install-bin install-lib install-man install-sysconf 35 | 36 | install-bin: 37 | install -d $(DESTDIR)$(bindir) 38 | find bin -type f -printf %P\\n | while read PROGNAME \ 39 | ; do \ 40 | { \ 41 | head -n1 bin/$$PROGNAME | sed "s@#!/usr/bin/python@#!$(PYTHON)@"; \ 42 | tail -n+2 bin/$$PROGNAME; \ 43 | } >$(DESTDIR)$(bindir)/$$PROGNAME; \ 44 | chmod 755 $(DESTDIR)$(bindir)/$$PROGNAME; \ 45 | done 46 | 47 | install-lib: 48 | find blueprint -type d -printf %P\\0 | xargs -0r -I__ install -d $(DESTDIR)$(pydir)/blueprint/__ 49 | find blueprint -type f -name \*.py -printf %P\\0 | xargs -0r -I__ install -m644 blueprint/__ $(DESTDIR)$(pydir)/blueprint/__ 50 | install -m644 blueprint/frontend/cfn.json $(DESTDIR)$(pydir)/blueprint/frontend/ 51 | install -m644 blueprint/frontend/mustache.sh $(DESTDIR)$(pydir)/blueprint/frontend/ 52 | find blueprint/frontend/blueprint-template.d -type f -name \*.sh -printf %P\\0 | xargs -0r -I__ install -m644 blueprint/frontend/blueprint-template.d/__ $(DESTDIR)$(pydir)/blueprint/frontend/blueprint-template.d/__ 53 | PYTHONPATH=$(DESTDIR)$(pydir) $(PYTHON) -mcompileall $(DESTDIR)$(pydir)/blueprint 54 | 55 | install-man: 56 | find man -type d -printf %P\\0 | xargs -0r -I__ install -d $(DESTDIR)$(mandir)/__ 57 | find man -type f -name \*.[12345678] -printf %P\\0 | xargs -0r -I__ install -m644 man/__ $(DESTDIR)$(mandir)/__ 58 | find man -type f -name \*.[12345678] -printf %P\\0 | xargs -0r -I__ gzip -f $(DESTDIR)$(mandir)/__ 59 | 60 | install-sysconf: 61 | find etc -type d -printf %P\\0 | xargs -0r -I__ install -d $(DESTDIR)$(sysconfdir)/__ 62 | find etc -type f -printf %P\\0 | xargs -0r -I__ install -m644 etc/__ $(DESTDIR)$(sysconfdir)/__ 63 | 64 | uninstall: uninstall-bin uninstall-lib uninstall-man uninstall-sysconf 65 | 66 | uninstall-bin: 67 | find bin -type f -printf %P\\0 | xargs -0r -I__ rm -f $(DESTDIR)$(bindir)/__ 68 | rmdir -p --ignore-fail-on-non-empty $(DESTDIR)$(bindir) || true 69 | 70 | uninstall-lib: 71 | find blueprint -type f -name \*.py -printf %P\\0 | xargs -0r -I__ rm -f $(DESTDIR)$(pydir)/blueprint/__ $(DESTDIR)$(pydir)/blueprint/__c 72 | rm -f $(DESTDIR)$(pydir)/blueprint/frontend/cfn.json 73 | rm -f $(DESTDIR)$(pydir)/blueprint/frontend/mustache.sh 74 | find blueprint/frontend/blueprint-template.d -type f -name \*.sh -printf %P\\0 | xargs -0r -I__ rm -f $(DESTDIR)$(pydir)/blueprint/frontend/blueprint-template.d/__ 75 | find blueprint -depth -mindepth 1 -type d -printf %P\\0 | xargs -0r -I__ rmdir $(DESTDIR)$(pydir)/blueprint/__ || true 76 | rmdir -p --ignore-fail-on-non-empty $(DESTDIR)$(pydir)/blueprint || true 77 | 78 | uninstall-man: 79 | find man -type f -name \*.[12345678] -printf %P\\0 | xargs -0r -I__ rm -f $(DESTDIR)$(mandir)/__.gz 80 | find man -depth -mindepth 1 -type d -printf %P\\0 | xargs -0r -I__ rmdir $(DESTDIR)$(mandir)/__ || true 81 | rmdir -p --ignore-fail-on-non-empty $(DESTDIR)$(mandir) || true 82 | 83 | uninstall-sysconf: 84 | find etc -type f -printf %P\\0 | xargs -0r -I__ rm -f $(DESTDIR)$(sysconfdir)/__ 85 | find etc -depth -mindepth 1 -type d -printf %P\\0 | xargs -0r -I__ rmdir $(DESTDIR)$(sysconfdir)/__ || true 86 | rmdir -p --ignore-fail-on-non-empty $(DESTDIR)$(sysconfdir) || true 87 | 88 | build: 89 | sudo make build-deb 90 | make build-pypi 91 | 92 | build-deb: 93 | make clean all install prefix=/usr sysconfdir=/etc DESTDIR=debian 94 | FPM_EDITOR="echo 'Replaces: blueprint-io' >>" fpm -s dir -t deb -C debian \ 95 | -n blueprint -v $(VERSION)-$(BUILD)py$(PYTHON_VERSION) -a all \ 96 | -d git-core \ 97 | -d python$(PYTHON_VERSION) \ 98 | -m "Richard Crowley " \ 99 | --url "https://github.com/devstructure/blueprint" \ 100 | --description "Reverse-engineer server configuration." \ 101 | --edit . 102 | make uninstall prefix=/usr sysconfdir=/etc DESTDIR=debian 103 | 104 | build-pypi: 105 | VERSION=$(VERSION) mustache.sh/bin/mustache.sh setup.py 106 | $(PYTHON) setup.py bdist_egg 107 | 108 | deploy: deploy-deb deploy-pypi 109 | 110 | deploy-deb: 111 | scp blueprint_$(VERSION)-$(BUILD)py$(PYTHON_VERSION)_all.deb freight@packages.devstructure.com: 112 | make deploy-deb-$(PYTHON_VERSION) 113 | ssh -t freight@packages.devstructure.com rm blueprint_$(VERSION)-$(BUILD)py$(PYTHON_VERSION)_all.deb 114 | 115 | deploy-deb-2.6: 116 | ssh -t freight@packages.devstructure.com freight add blueprint_$(VERSION)-$(BUILD)py$(PYTHON_VERSION)_all.deb apt/lenny apt/squeeze apt/lucid apt/maverick 117 | ssh -t freight@packages.devstructure.com freight cache apt/lenny apt/squeeze apt/lucid apt/maverick 118 | 119 | deploy-deb-2.7: 120 | ssh -t freight@packages.devstructure.com freight add blueprint_$(VERSION)-$(BUILD)py$(PYTHON_VERSION)_all.deb apt/natty apt/oneiric apt/precise 121 | ssh -t freight@packages.devstructure.com freight cache apt/natty apt/oneiric apt/precise 122 | 123 | deploy-pypi: 124 | $(PYTHON) setup.py sdist upload 125 | 126 | man: 127 | find man -name \*.ronn | PATH="$(HOME)/work/ronn/bin:$(PATH)" RUBYLIB="$(HOME)/work/ronn/lib" xargs -n1 ronn --manual=Blueprint --organization=DevStructure --style=toc 128 | 129 | gh-pages: man 130 | mkdir -p gh-pages 131 | find man -name \*.html | xargs -I__ mv __ gh-pages/ 132 | git checkout -q gh-pages 133 | cp -R gh-pages/* ./ 134 | rm -rf gh-pages 135 | git add . 136 | git commit -m "Rebuilt manual." 137 | git push origin gh-pages 138 | git checkout -q master 139 | 140 | .PHONY: all clean test install install-bin install-lib install-man install-sysconf uninstall uninstall-bin uninstall-lib uninstall-man uninstall-sysconf build build-deb build-pypi deploy deploy-deb deploy-deb-2.6 deploy-deb-2.7 deploy-pypi man gh-pages 141 | -------------------------------------------------------------------------------- /bin/blueprint: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | set -e 4 | 5 | COMMAND="$0-$1" 6 | [ -x "$COMMAND" ] && { 7 | shift 8 | exec "$COMMAND" "$@" 9 | } 10 | 11 | echo "Usage: $(basename $0) [...]" >&2 12 | echo "Common commands: list, create, show, diff, apply, destroy, git" >&2 13 | exit 1 14 | -------------------------------------------------------------------------------- /bin/blueprint-apply: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | import logging 4 | import optparse 5 | import os 6 | import subprocess 7 | import sys 8 | 9 | import blueprint.cli 10 | from blueprint import context_managers 11 | 12 | parser = optparse.OptionParser('Usage: %prog [-r] [-q] []') 13 | parser.add_option('-r', '--relaxed', 14 | dest='relaxed', 15 | default=False, 16 | action='store_true', 17 | help='relax version constraints in generated code') 18 | parser.add_option('-q', '--quiet', 19 | dest='quiet', 20 | default=False, 21 | action='store_true', 22 | help='operate quietly') 23 | options, args = parser.parse_args() 24 | 25 | if options.quiet: 26 | logging.root.setLevel(logging.CRITICAL) 27 | 28 | if 1 < len(args): 29 | parser.print_usage() 30 | sys.exit(1) 31 | 32 | b = blueprint.cli.read(options, args) 33 | 34 | if 0 != os.geteuid(): 35 | logging.warning('applying a blueprint may not work as a normal user') 36 | 37 | with context_managers.mkdtemp(): 38 | filename = b.sh(options.relaxed).dumpf() 39 | p = subprocess.Popen(['sh', filename], close_fds=True) 40 | p.communicate() 41 | sys.exit(p.returncode) 42 | -------------------------------------------------------------------------------- /bin/blueprint-create: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | import errno 4 | import logging 5 | import optparse 6 | import sys 7 | 8 | import blueprint.cli 9 | from blueprint import context_managers 10 | import blueprint.git 11 | 12 | parser = optparse.OptionParser('Usage: %prog [-d ] [-P|-C|-S|-R|...] ' 13 | '[-m ] [-r] [-q] ') 14 | parser.add_option('-d', '--diff', 15 | dest='subtrahend', 16 | default=None, 17 | help='blueprint to subtract from the generated blueprint') 18 | parser.add_option('-P', '--puppet', 19 | dest='generate', 20 | action='store_const', 21 | const='puppet', 22 | help='generate a Puppet module') 23 | parser.add_option('-C', '--chef', 24 | dest='generate', 25 | action='store_const', 26 | const='chef', 27 | help='generate a Chef cookbook') 28 | parser.add_option('-B', '--bcfg2', 29 | dest='generate', 30 | action='store_const', 31 | const='bcfg2', 32 | help='generate a Bcfg2 bundle') 33 | parser.add_option('-S', '--sh', 34 | dest='generate', 35 | action='store_const', 36 | const='sh', 37 | help='generate POSIX shell code') 38 | parser.add_option('-R', '--rules', 39 | dest='generate', 40 | action='store_const', 41 | const='blueprint_rules', 42 | help='generate Blueprint rules') 43 | parser.add_option('--cfn', 44 | dest='generate', 45 | action='store_const', 46 | const='cfn', 47 | help='generate an AWS CloudFormation template') 48 | parser.add_option('--cfengine3', 49 | dest='generate', 50 | action='store_const', 51 | const='cfengine3', 52 | help='generate a CFEngine 3 template') 53 | parser.add_option('-m', '--message', 54 | dest='message', 55 | default=None, 56 | help='commit message') 57 | parser.add_option('-r', '--relaxed', 58 | dest='relaxed', 59 | default=False, 60 | action='store_true', 61 | help='relax version constraints in generated code') 62 | parser.add_option('-q', '--quiet', 63 | dest='quiet', 64 | default=False, 65 | action='store_true', 66 | help='operate quietly') 67 | options, args = parser.parse_args() 68 | 69 | if options.quiet: 70 | logging.root.setLevel(logging.CRITICAL) 71 | 72 | if 1 != len(args): 73 | parser.print_usage() 74 | sys.exit(1) 75 | 76 | if not blueprint.git.configured(): 77 | logging.error('please give Git your name and email address so commits have an author') 78 | logging.error('') 79 | logging.error(' git config --global user.email "you@example.com"') 80 | logging.error(' git config --global user.name "Your Name"') 81 | logging.error('') 82 | sys.exit(1) 83 | 84 | b = blueprint.cli.create(options, args) 85 | 86 | try: 87 | if options.generate is not None: 88 | try: 89 | filename = getattr(b, options.generate)(options.relaxed).dumpf() 90 | except OSError as e: 91 | if errno.EEXIST == e.errno: 92 | logging.error('{0} already exists'.format(args[0])) 93 | sys.exit(1) 94 | if not options.quiet: 95 | print(filename) 96 | except IOError: 97 | pass 98 | -------------------------------------------------------------------------------- /bin/blueprint-destroy: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | import logging 4 | import optparse 5 | import sys 6 | 7 | import blueprint 8 | 9 | parser = optparse.OptionParser('Usage: %prog [-q] ') 10 | parser.add_option('-q', '--quiet', 11 | dest='quiet', 12 | default=False, 13 | action='store_true', 14 | help='operate quietly') 15 | options, args = parser.parse_args() 16 | 17 | if options.quiet: 18 | logging.root.setLevel(logging.CRITICAL) 19 | 20 | if 1 != len(args): 21 | parser.print_usage() 22 | sys.exit(1) 23 | 24 | try: 25 | blueprint.Blueprint.destroy(args[0]) 26 | except blueprint.NotFoundError: 27 | logging.error('blueprint {0} does not exist'.format(args[0])) 28 | sys.exit(1) 29 | except blueprint.NameError: 30 | logging.error('invalid blueprint name') 31 | sys.exit(1) 32 | -------------------------------------------------------------------------------- /bin/blueprint-diff: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | import logging 4 | import optparse 5 | import sys 6 | 7 | import blueprint 8 | from blueprint import context_managers 9 | from blueprint import git 10 | 11 | parser = optparse.OptionParser( 12 | 'Usage: %prog [-m ] [-q] ') 13 | parser.add_option('-m', '--message', 14 | dest='message', 15 | default=None, 16 | help='commit message') 17 | parser.add_option('-q', '--quiet', 18 | dest='quiet', 19 | default=False, 20 | action='store_true', 21 | help='operate quietly') 22 | options, args = parser.parse_args() 23 | 24 | if options.quiet: 25 | logging.root.setLevel(logging.CRITICAL) 26 | 27 | if 3 != len(args): 28 | parser.print_usage() 29 | sys.exit(1) 30 | minuend, subtrahend, difference = args 31 | 32 | try: 33 | b_m = blueprint.Blueprint.checkout(minuend) 34 | except blueprint.NotFoundError: 35 | logging.error('blueprint {0} does not exist'.format(minuend)) 36 | sys.exit(1) 37 | except blueprint.NameError: 38 | logging.error('invalid blueprint name {0}'.format(minuend)) 39 | sys.exit(1) 40 | try: 41 | b_s = blueprint.Blueprint.checkout(subtrahend) 42 | except blueprint.NotFoundError: 43 | logging.error('blueprint {0} does not exist'.format(subtrahend)) 44 | sys.exit(1) 45 | except blueprint.NameError: 46 | logging.error('invalid blueprint name {0}'.format(subtrahend)) 47 | sys.exit(1) 48 | 49 | with context_managers.mkdtemp(): 50 | b_d = b_m - b_s 51 | try: 52 | b_d.name = difference 53 | except blueprint.NameError: 54 | logging.error('invalid blueprint name {0}'.format(difference)) 55 | sys.exit(1) 56 | 57 | # Grab all of the source tarballs from the minuend. 58 | # TODO Factor this pattern into a method on `blueprint.Blueprint`s. 59 | tree = git.tree(getattr(b_m, '_commit')) 60 | for dirname, filename in sorted(b_m.sources.iteritems()): 61 | blob = git.blob(tree, filename) 62 | content = git.content(blob) 63 | f = open(filename, 'w') 64 | f.write(content) 65 | f.close() 66 | 67 | b_d.commit(options.message or '') 68 | -------------------------------------------------------------------------------- /bin/blueprint-git: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | set -e 4 | 5 | [ -z "$*" ] && exit 0 6 | 7 | GIT_DIR="$HOME/.blueprints.git" 8 | WORK_TREE="$(mktemp -d)" 9 | trap "rm -rf \"$WORK_TREE\"" 0 10 | 11 | CMD="$1" 12 | shift 13 | 14 | case "$CMD" in 15 | clone) 16 | exec git clone "$GIT_DIR" "$@" blueprints;; 17 | *) 18 | exec git --git-dir "$GIT_DIR" --work-tree "$WORK_TREE" "$CMD" "$@";; 19 | esac 20 | -------------------------------------------------------------------------------- /bin/blueprint-list: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | import logging 4 | import optparse 5 | 6 | import blueprint 7 | 8 | parser = optparse.OptionParser('Usage: %prog [-q]') 9 | parser.add_option('-q', '--quiet', 10 | dest='quiet', 11 | default=False, 12 | action='store_true', 13 | help='operate quietly') 14 | options, args = parser.parse_args() 15 | 16 | if options.quiet: 17 | logging.root.setLevel(logging.CRITICAL) 18 | 19 | for name in blueprint.Blueprint.iter(): 20 | print(' {0}'.format(name)) 21 | -------------------------------------------------------------------------------- /bin/blueprint-prune: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | import logging 4 | import optparse 5 | import readline 6 | import sys 7 | 8 | import blueprint 9 | from blueprint import cli 10 | from blueprint import context_managers 11 | from blueprint import interactive 12 | 13 | parser = optparse.OptionParser('Usage: %prog [-m ] [-q] ') 14 | parser.add_option('-m', '--message', 15 | dest='message', 16 | default=None, 17 | help='commit message') 18 | parser.add_option('-q', '--quiet', 19 | dest='quiet', 20 | default=False, 21 | action='store_true', 22 | help='operate quietly') 23 | options, args = parser.parse_args() 24 | 25 | if options.quiet: 26 | logging.root.setLevel(logging.CRITICAL) 27 | 28 | if len(args) not in (1, 2): 29 | parser.print_usage() 30 | sys.exit(1) 31 | 32 | b_s = blueprint.cli.read(options, args) 33 | try: 34 | b_d = blueprint.Blueprint(args[-1]) 35 | except blueprint.NameError: 36 | logging.error('invalid blueprint name {0}'.format(args[-1])) 37 | sys.exit(1) 38 | 39 | def choose(): 40 | """ 41 | Return the destination blueprint object or `None`. 42 | """ 43 | prompt = 'Include in blueprint {0}? [y/n] '.format(args[-1]) 44 | yn = raw_input(prompt).lower() 45 | while yn not in ('y', 'n', 'yes', 'no'): 46 | yn = raw_input(prompt) 47 | return b_d if 'y' == yn[0] else None 48 | 49 | try: 50 | with context_managers.mkdtemp(): 51 | interactive.walk(b_s, choose) 52 | b_d.commit(options.message or '') 53 | except IOError: 54 | pass 55 | except KeyboardInterrupt: 56 | print('') # Expect this kind of thing in an interactive process. 57 | -------------------------------------------------------------------------------- /bin/blueprint-pull: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | import logging 4 | import optparse 5 | import re 6 | import sys 7 | import urlparse 8 | 9 | from blueprint import cfg 10 | from blueprint import context_managers 11 | import blueprint.io 12 | 13 | parser = optparse.OptionParser( 14 | 'Usage: %prog [-m ] [-q] |') 15 | parser.add_option('-m', '--message', 16 | dest='message', 17 | default=None, 18 | help='commit message') 19 | parser.add_option('-q', '--quiet', 20 | dest='quiet', 21 | default=False, 22 | action='store_true', 23 | help='operate quietly') 24 | options, args = parser.parse_args() 25 | 26 | if 1 != len(args): 27 | parser.print_usage() 28 | sys.exit(1) 29 | url = urlparse.urlparse(args[0]) 30 | if url.scheme and url.netloc: 31 | match = re.match(r'^/([0-9A-Za-z_-]{64})/([^/ \t\r\n]+)$', url.path) 32 | if match is None: 33 | logging.error('invalid blueprint URL') 34 | sys.exit(1) 35 | server = urlparse.urlunparse((url.scheme, url.netloc, '', '', '', '')) 36 | secret, name = match.group(1, 2) 37 | else: 38 | try: 39 | server, secret = cfg.get('io', 'server'), cfg.get('io', 'secret') 40 | except (NoOptionError, NoSectionError): 41 | logging.error( 42 | 'secret not found - use a blueprint URL or configure your secret') 43 | sys.exit(1) 44 | name = args[0] 45 | 46 | with context_managers.mkdtemp(): 47 | try: 48 | b = blueprint.io.pull(server, secret, name) 49 | except ValueError: 50 | logging.error('invalid blueprint name') 51 | sys.exit(1) 52 | if b is not None: 53 | b.commit(options.message or '') 54 | logging.info('completed - blueprint stored locally and ready for use') 55 | -------------------------------------------------------------------------------- /bin/blueprint-push: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | from ConfigParser import NoOptionError, NoSectionError 4 | import logging 5 | import optparse 6 | import sys 7 | 8 | from blueprint import cfg 9 | import blueprint.cli 10 | import blueprint.io 11 | 12 | parser = optparse.OptionParser('Usage: %prog [-q] ') 13 | parser.add_option('-q', '--quiet', 14 | dest='quiet', 15 | default=False, 16 | action='store_true', 17 | help='operate quietly') 18 | options, args = parser.parse_args() 19 | 20 | if options.quiet: 21 | logging.root.setLevel(logging.CRITICAL) 22 | 23 | if 1 != len(args): 24 | parser.print_usage() 25 | sys.exit(1) 26 | 27 | b = blueprint.cli.read(options, args) 28 | 29 | server = cfg.get('io', 'server') 30 | try: 31 | secret = cfg.get('io', 'secret') 32 | except (NoOptionError, NoSectionError): 33 | secret = blueprint.io.secret(server) 34 | if secret is None: 35 | sys.exit(1) 36 | url = blueprint.io.push(server, secret, b) 37 | if url is not None: 38 | logging.info('completed - blueprint URL:') 39 | print(url) 40 | -------------------------------------------------------------------------------- /bin/blueprint-rules: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | import errno 4 | import logging 5 | import optparse 6 | import sys 7 | 8 | import blueprint.cli 9 | from blueprint import context_managers 10 | 11 | parser = optparse.OptionParser('Usage: %prog [-P|-C|-S|-R|...] [-r] [-q] ' 12 | '[]') 13 | parser.add_option('-P', '--puppet', 14 | dest='generate', 15 | action='store_const', 16 | const='puppet', 17 | help='generate a Puppet module') 18 | parser.add_option('-C', '--chef', 19 | dest='generate', 20 | action='store_const', 21 | const='chef', 22 | help='generate a Chef cookbook') 23 | parser.add_option('-B', '--bcfg2', 24 | dest='generate', 25 | action='store_const', 26 | const='bcfg2', 27 | help='generate a Bcfg2 bundle') 28 | parser.add_option('-S', '--sh', 29 | dest='generate', 30 | action='store_const', 31 | const='sh', 32 | help='generate POSIX shell code') 33 | parser.add_option('-R', '--rules', 34 | dest='generate', 35 | action='store_const', 36 | const='blueprint_rules', 37 | help='generate Blueprint rules') 38 | parser.add_option('--cfn', 39 | dest='generate', 40 | action='store_const', 41 | const='cfn', 42 | help='generate an AWS CloudFormation template') 43 | parser.add_option('--cfengine3', 44 | dest='generate', 45 | action='store_const', 46 | const='cfengine3', 47 | help='generate a CFEngine 3 template') 48 | parser.add_option('-m', '--message', 49 | dest='message', 50 | default=None, 51 | help='commit message') 52 | parser.add_option('-r', '--relaxed', 53 | dest='relaxed', 54 | default=False, 55 | action='store_true', 56 | help='relax version constraints in generated code') 57 | parser.add_option('-q', '--quiet', 58 | dest='quiet', 59 | default=False, 60 | action='store_true', 61 | help='operate quietly') 62 | options, args = parser.parse_args() 63 | 64 | if options.quiet: 65 | logging.root.setLevel(logging.CRITICAL) 66 | 67 | if 1 < len(args): 68 | parser.print_usage() 69 | sys.exit(1) 70 | 71 | if not blueprint.git.configured(): 72 | logging.error('please give Git your name and email address so commits have an author') 73 | logging.error('') 74 | logging.error(' git config --global user.email "you@example.com"') 75 | logging.error(' git config --global user.name "Your Name"') 76 | logging.error('') 77 | sys.exit(1) 78 | 79 | b = blueprint.cli.read_rules(options, args) 80 | 81 | try: 82 | if options.generate is not None: 83 | try: 84 | filename = getattr(b, options.generate)(options.relaxed).dumpf() 85 | except OSError as e: 86 | if errno.EEXIST == e.errno: 87 | logging.error('{0} already exists'.format(b.name)) 88 | sys.exit(1) 89 | if not options.quiet: 90 | print(filename) 91 | except IOError: 92 | pass 93 | -------------------------------------------------------------------------------- /bin/blueprint-show: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | import errno 4 | import logging 5 | import optparse 6 | import sys 7 | 8 | import blueprint.cli 9 | 10 | parser = optparse.OptionParser('Usage: %prog [-P|-C|-S|-R|...] ' 11 | '[-r] [-q] []') 12 | parser.add_option('-P', '--puppet', 13 | dest='generate', 14 | action='store_const', 15 | const='puppet', 16 | help='generate a Puppet module') 17 | parser.add_option('-C', '--chef', 18 | dest='generate', 19 | action='store_const', 20 | const='chef', 21 | help='generate a Chef cookbook') 22 | parser.add_option('-B', '--bcfg2', 23 | dest='generate', 24 | action='store_const', 25 | const='bcfg2', 26 | help='generate a Bcfg2 bundle') 27 | parser.add_option('-S', '--sh', 28 | dest='generate', 29 | action='store_const', 30 | const='sh', 31 | help='generate POSIX shell code') 32 | parser.add_option('-R', '--rules', 33 | dest='generate', 34 | action='store_const', 35 | const='blueprint_rules', 36 | help='generate Blueprint rules') 37 | parser.add_option('--cfn', 38 | dest='generate', 39 | action='store_const', 40 | const='cfn', 41 | help='generate an AWS CloudFormation template') 42 | parser.add_option('--cfengine3', 43 | dest='generate', 44 | action='store_const', 45 | const='cfengine3', 46 | help='generate a CFEngine 3 template') 47 | parser.add_option('-r', '--relaxed', 48 | dest='relaxed', 49 | default=False, 50 | action='store_true', 51 | help='relax version constraints in generated code') 52 | parser.add_option('-q', '--quiet', 53 | dest='quiet', 54 | default=False, 55 | action='store_true', 56 | help='operate quietly') 57 | options, args = parser.parse_args() 58 | 59 | if options.quiet: 60 | logging.root.setLevel(logging.CRITICAL) 61 | 62 | if 1 < len(args): 63 | parser.print_usage() 64 | sys.exit(1) 65 | 66 | b = blueprint.cli.read(options, args) 67 | 68 | try: 69 | if options.generate is None: 70 | print(b.dumps()) 71 | else: 72 | try: 73 | filename = getattr(b, options.generate)(options.relaxed).dumpf() 74 | except OSError as e: 75 | if errno.EEXIST == e.errno: 76 | logging.error('{0} already exists'.format(b.name)) 77 | sys.exit(1) 78 | if not options.quiet: 79 | print(filename) 80 | except IOError: 81 | pass 82 | -------------------------------------------------------------------------------- /bin/blueprint-show-files: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | import logging 4 | import optparse 5 | import sys 6 | 7 | import blueprint.cli 8 | 9 | parser = optparse.OptionParser('Usage: %prog [-q] []') 10 | parser.add_option('-q', '--quiet', 11 | dest='quiet', 12 | default=False, 13 | action='store_true', 14 | help='operate quietly') 15 | options, args = parser.parse_args() 16 | 17 | if options.quiet: 18 | logging.root.setLevel(logging.CRITICAL) 19 | 20 | if 1 < len(args): 21 | parser.print_usage() 22 | sys.exit(1) 23 | 24 | b = blueprint.cli.read(options, args) 25 | 26 | try: 27 | def file(pathname, f): 28 | print(pathname) 29 | b.walk(file=file) 30 | except IOError: 31 | pass 32 | -------------------------------------------------------------------------------- /bin/blueprint-show-ignore: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | import logging 4 | import optparse 5 | import sys 6 | 7 | import blueprint.cli 8 | 9 | parser = optparse.OptionParser('Usage: %prog [-q] []') 10 | parser.add_option('-q', '--quiet', 11 | dest='quiet', 12 | default=False, 13 | action='store_true', 14 | help='operate quietly') 15 | options, args = parser.parse_args() 16 | 17 | if options.quiet: 18 | logging.root.setLevel(logging.CRITICAL) 19 | 20 | if 1 < len(args): 21 | parser.print_usage() 22 | sys.exit(1) 23 | 24 | b = blueprint.cli.read(options, args) 25 | 26 | try: 27 | for line in b.blueprintignore(): 28 | sys.stdout.write(line) 29 | except IOError: 30 | pass 31 | -------------------------------------------------------------------------------- /bin/blueprint-show-packages: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | import logging 4 | import optparse 5 | import sys 6 | 7 | import blueprint.cli 8 | 9 | parser = optparse.OptionParser('Usage: %prog [-q] []') 10 | parser.add_option('-q', '--quiet', 11 | dest='quiet', 12 | default=False, 13 | action='store_true', 14 | help='operate quietly') 15 | options, args = parser.parse_args() 16 | 17 | if options.quiet: 18 | logging.root.setLevel(logging.CRITICAL) 19 | 20 | if 1 < len(args): 21 | parser.print_usage() 22 | sys.exit(1) 23 | 24 | b = blueprint.cli.read(options, args) 25 | 26 | try: 27 | def package(manager, package, version): 28 | print('{0} {1} {2}'.format(manager, package, version)) 29 | b.walk(package=package) 30 | except IOError: 31 | pass 32 | -------------------------------------------------------------------------------- /bin/blueprint-show-services: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | import logging 4 | import optparse 5 | import sys 6 | 7 | import blueprint.cli 8 | 9 | parser = optparse.OptionParser('Usage: %prog [-q] []') 10 | parser.add_option('-q', '--quiet', 11 | dest='quiet', 12 | default=False, 13 | action='store_true', 14 | help='operate quietly') 15 | options, args = parser.parse_args() 16 | 17 | if options.quiet: 18 | logging.root.setLevel(logging.CRITICAL) 19 | 20 | if 1 < len(args): 21 | parser.print_usage() 22 | sys.exit(1) 23 | 24 | b = blueprint.cli.read(options, args) 25 | 26 | try: 27 | def service(manager, service): 28 | print('{0} {1}'.format(manager, service)) 29 | b.walk(service=service) 30 | except IOError: 31 | pass 32 | -------------------------------------------------------------------------------- /bin/blueprint-show-sources: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | import logging 4 | import optparse 5 | import subprocess 6 | import sys 7 | 8 | import blueprint.cli 9 | from blueprint import git 10 | 11 | parser = optparse.OptionParser('Usage: %prog [-q] [][...]') 12 | parser.add_option('-q', '--quiet', 13 | dest='quiet', 14 | default=False, 15 | action='store_true', 16 | help='operate quietly') 17 | options, args = parser.parse_args() 18 | 19 | if options.quiet: 20 | logging.root.setLevel(logging.CRITICAL) 21 | 22 | if 1 > len(args): 23 | parser.print_usage() 24 | sys.exit(1) 25 | 26 | b = blueprint.cli.read(options, args) 27 | 28 | try: 29 | commit = git.rev_parse(args[0]) 30 | tree = git.tree(commit) 31 | def source(dirname, filename, gen_content, url): 32 | if args[1:] and dirname not in args[1:]: 33 | return 34 | if url is not None: 35 | sys.stderr.write('{0} {1}\n'.format(dirname, url)) 36 | elif gen_content is not None: 37 | sys.stderr.write('{0} {1}\n'.format(dirname, filename)) 38 | blob = git.blob(tree, filename) 39 | p = subprocess.Popen(['tar', 'tv'], 40 | close_fds=True, 41 | stdin=git.cat_file(blob)) 42 | p.communicate() 43 | b.walk(source=source) 44 | except IOError: 45 | pass 46 | -------------------------------------------------------------------------------- /bin/blueprint-split: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | import logging 4 | import optparse 5 | import readline 6 | import sys 7 | 8 | import blueprint 9 | from blueprint import cli 10 | from blueprint import context_managers 11 | from blueprint import interactive 12 | 13 | parser = optparse.OptionParser( 14 | 'Usage: %prog [-m ] [-q] ') 15 | parser.add_option('-m', '--message', 16 | dest='message', 17 | default=None, 18 | help='commit message') 19 | parser.add_option('-q', '--quiet', 20 | dest='quiet', 21 | default=False, 22 | action='store_true', 23 | help='operate quietly') 24 | options, args = parser.parse_args() 25 | 26 | if options.quiet: 27 | logging.root.setLevel(logging.CRITICAL) 28 | 29 | if len(args) not in (2, 3): 30 | parser.print_usage() 31 | sys.exit(1) 32 | 33 | b_s = blueprint.cli.read(options, args) 34 | try: 35 | b_da = blueprint.Blueprint(args[-2]) 36 | except blueprint.NameError: 37 | logging.error('invalid blueprint name {0}'.format(args[-2])) 38 | sys.exit(1) 39 | try: 40 | b_db = blueprint.Blueprint(args[-1]) 41 | except blueprint.NameError: 42 | logging.error('invalid blueprint name {0}'.format(args[-1])) 43 | sys.exit(1) 44 | 45 | def choose(): 46 | """ 47 | Return the blueprint object indicated by the user's input. 48 | """ 49 | prompt = '"{0}" or "{1}"? '.format(args[-2], args[-1]) 50 | prefix = raw_input(prompt) 51 | while args[-2].startswith(prefix) == args[-1].startswith(prefix): 52 | print('Ambiguous; please give a unique prefix of a blueprint name.') 53 | prefix = raw_input(prompt) 54 | return {(True, False): b_da, 55 | (False, True): b_db}[(args[-2].startswith(prefix), 56 | args[-1].startswith(prefix))] 57 | 58 | try: 59 | with context_managers.mkdtemp(): 60 | interactive.walk(b_s, choose) 61 | b_da.commit(options.message or '') 62 | b_db.commit(options.message or '') 63 | except IOError: 64 | pass 65 | except KeyboardInterrupt: 66 | print('') # Expect this kind of thing in an interactive process. 67 | -------------------------------------------------------------------------------- /bin/blueprint-template.mustache: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | set -e 4 | 5 | exec 6>&2 6 | 7 | #/ Usage: blueprint template [-q] 8 | usage() { 9 | grep "^#/" "$0" | cut -c4- 1>&2 10 | exit 1 11 | } 12 | while [ "$#" -gt 0 ] 13 | do 14 | case "$1" in 15 | -h|--help) usage;; 16 | -q|--quiet) exec 6>/dev/null;; 17 | *) break;; 18 | esac 19 | done 20 | 21 | # BEGIN mustache.sh 22 | {{`cat mustache.sh/lib/mustache.sh`}} 23 | # END mustache.sh 24 | 25 | for PATHNAME in $(find "/etc/blueprint-template.d" "{{pydir}}/blueprint/frontend/blueprint-template.d" "-name" "*.sh" 2>/dev/null) 26 | do 27 | echo "# [blueprint-template] sourcing $PATHNAME" >&6 28 | . "$PATHNAME" 29 | done 30 | 31 | if [ -f "$1.blueprint-template.sh" ] 32 | then 33 | echo "# [blueprint-template] sourcing $1.blueprint-template.sh" >&6 34 | . "$1.blueprint-template.sh" 35 | fi 36 | 37 | mustache <"$1.blueprint-template.mustache" >"$1" 38 | -------------------------------------------------------------------------------- /blueprint/backend/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | Every backend provider defines a function named as the module that contains 3 | it, which will be called when a blueprint is created. This module gathers 4 | those functions together. 5 | """ 6 | 7 | import glob 8 | import os.path 9 | import sys 10 | 11 | 12 | __all__ = [os.path.basename(filename)[:-3] 13 | for filename in glob.glob(os.path.join(os.path.dirname(__file__), 14 | '[!_]*.py'))] 15 | for name in __all__: 16 | module = __import__(name, globals(), locals(), [], 1) 17 | setattr(sys.modules[__name__], name, getattr(module, name)) 18 | -------------------------------------------------------------------------------- /blueprint/backend/apt.py: -------------------------------------------------------------------------------- 1 | """ 2 | Search for `apt` packages to include in the blueprint. 3 | """ 4 | 5 | import os 6 | import logging 7 | import subprocess 8 | 9 | from blueprint import util 10 | 11 | 12 | def apt(b, r): 13 | logging.info('searching for APT packages') 14 | 15 | # Define a default output format string for dpkg-query. 16 | output_format = '${Status}\x1E${binary:Package}\x1E${Version}\n' 17 | 18 | # Try running dpkg --print-foreign-architectures to see if dpkg is 19 | # multi-arch aware. If not, revert to old style output_format. 20 | try: 21 | with open(os.devnull, 'w') as fnull: 22 | rv = subprocess.call(['dpkg', '--print-foreign-architectures'], 23 | stdout = fnull, stderr = fnull) 24 | if rv != 0: 25 | output_format = '${Status}\x1E${Package}\x1E${Version}\n' 26 | except OSError: 27 | return 28 | 29 | # Try for the full list of packages. If this fails, don't even 30 | # bother with the rest because this is probably a Yum/RPM-based 31 | # system. 32 | try: 33 | p = subprocess.Popen(['dpkg-query','-Wf', output_format], 34 | close_fds=True, stdout=subprocess.PIPE) 35 | except OSError: 36 | return 37 | 38 | for line in p.stdout: 39 | status, package, version = line.strip().split('\x1E') 40 | if 'install ok installed' != status: 41 | continue 42 | if r.ignore_package('apt', package): 43 | continue 44 | 45 | b.add_package('apt', package, version) 46 | 47 | # Create service resources for each service init script or config 48 | # found in this package. 49 | p = subprocess.Popen(['dpkg-query', '-L', package], 50 | close_fds=True, stdout=subprocess.PIPE) 51 | for line in p.stdout: 52 | try: 53 | manager, service = util.parse_service(line.rstrip()) 54 | if not r.ignore_service(manager, service): 55 | b.add_service(manager, service) 56 | b.add_service_package(manager, service, 'apt', package) 57 | except ValueError: 58 | pass 59 | -------------------------------------------------------------------------------- /blueprint/backend/gem.py: -------------------------------------------------------------------------------- 1 | """ 2 | Search for Ruby gems to include in the blueprint. 3 | """ 4 | 5 | import glob 6 | import logging 7 | import os 8 | import re 9 | 10 | from blueprint import util 11 | 12 | 13 | def gem(b, r): 14 | logging.info('searching for Ruby gems') 15 | 16 | # Precompile a pattern for extracting the version of Ruby that was used 17 | # to install the gem. 18 | pattern = re.compile(r'gems/([^/]+)/gems') 19 | 20 | # Look for gems in all the typical places. This is easier than looking 21 | # for `gem` commands, which may or may not be on `PATH`. 22 | for globname in ('/usr/lib/ruby/gems/*/gems', 23 | '/usr/local/lib/ruby/gems/*/gems', 24 | '/var/lib/gems/*/gems'): 25 | for dirname in glob.glob(globname): 26 | 27 | # The `ruby1.9.1` (really 1.9.2) package on Maverick begins 28 | # including RubyGems in the `ruby1.9.1` package and marks the 29 | # `rubygems1.9.1` package as virtual. So for Maverick and 30 | # newer, the manager is actually `ruby1.9.1`. 31 | match = pattern.search(dirname) 32 | if '1.9.1' == match.group(1) and util.rubygems_virtual(): 33 | manager = 'ruby{0}'.format(match.group(1)) 34 | 35 | # Oneiric and RPM-based distros just have one RubyGems package. 36 | elif util.rubygems_unversioned(): 37 | manager = 'rubygems' 38 | 39 | # Debian-based distros qualify the package name with the version 40 | # of Ruby it will use. 41 | else: 42 | manager = 'rubygems{0}'.format(match.group(1)) 43 | 44 | for entry in os.listdir(dirname): 45 | try: 46 | package, version = entry.rsplit('-', 1) 47 | except ValueError: 48 | logging.warning('skipping questionably named gem {0}'. 49 | format(entry)) 50 | continue 51 | if not r.ignore_package(manager, package): 52 | b.add_package(manager, package, version) 53 | -------------------------------------------------------------------------------- /blueprint/backend/npm.py: -------------------------------------------------------------------------------- 1 | """ 2 | Search for npm packages to include in the blueprint. This assumes that 3 | Node itself is installed via Chris Lea's PPAs, either 4 | or 5 | . 6 | """ 7 | 8 | import logging 9 | import re 10 | import subprocess 11 | 12 | 13 | def npm(b, r): 14 | logging.info('searching for npm packages') 15 | 16 | # Precompile a pattern for parsing the output of `{pear,pecl} list`. 17 | pattern = re.compile(r'^\S+ (\S+)@(\S+)$') 18 | 19 | try: 20 | p = subprocess.Popen(['npm', 'ls', '-g'], 21 | close_fds=True, 22 | stdout=subprocess.PIPE) 23 | for line in p.stdout: 24 | match = pattern.match(line.rstrip()) 25 | if match is None: 26 | continue 27 | package, version = match.group(1), match.group(2) 28 | if not r.ignore_package('nodejs', package): 29 | b.add_package('nodejs', package, version) 30 | except OSError: 31 | pass 32 | -------------------------------------------------------------------------------- /blueprint/backend/php.py: -------------------------------------------------------------------------------- 1 | """ 2 | Search for PEAR/PECL packages to include in the blueprint. 3 | """ 4 | 5 | import logging 6 | import re 7 | import subprocess 8 | 9 | from blueprint import util 10 | 11 | 12 | def php(b, r): 13 | logging.info('searching for PEAR/PECL packages') 14 | 15 | # Precompile a pattern for parsing the output of `{pear,pecl} list`. 16 | pattern = re.compile(r'^([0-9a-zA-Z_]+)\s+([0-9][0-9a-zA-Z\.-]*)\s') 17 | 18 | # PEAR packages are managed by `php-pear` (obviously). PECL packages 19 | # are managed by `php5-dev` because they require development headers 20 | # (less obvious but still makes sense). 21 | if util.lsb_release_codename() is None: 22 | pecl_manager = 'php-devel' 23 | else: 24 | pecl_manager = 'php5-dev' 25 | for manager, progname in (('php-pear', 'pear'), 26 | (pecl_manager, 'pecl')): 27 | 28 | try: 29 | p = subprocess.Popen([progname, 'list'], 30 | close_fds=True, stdout=subprocess.PIPE) 31 | except OSError: 32 | continue 33 | for line in p.stdout: 34 | match = pattern.match(line) 35 | if match is None: 36 | continue 37 | package, version = match.group(1), match.group(2) 38 | if not r.ignore_package(manager, package): 39 | b.add_package(manager, package, version) 40 | -------------------------------------------------------------------------------- /blueprint/backend/yum.py: -------------------------------------------------------------------------------- 1 | """ 2 | Search for `yum` packages to include in the blueprint. 3 | """ 4 | 5 | import logging 6 | import subprocess 7 | 8 | from blueprint import util 9 | 10 | 11 | def yum(b, r): 12 | logging.info('searching for Yum packages') 13 | 14 | # Try for the full list of packages. If this fails, don't even 15 | # bother with the rest because this is probably a Debian-based 16 | # system. 17 | try: 18 | p = subprocess.Popen(['rpm', 19 | '--qf=%{NAME}\x1E%{GROUP}\x1E%{EPOCH}' # No , 20 | '\x1E%{VERSION}-%{RELEASE}\x1E%{ARCH}\n', 21 | '-qa'], 22 | close_fds=True, stdout=subprocess.PIPE) 23 | except OSError: 24 | return 25 | 26 | for line in p.stdout: 27 | package, group, epoch, version, arch = line.strip().split('\x1E') 28 | if r.ignore_package('yum', package): 29 | continue 30 | 31 | if '(none)' != epoch: 32 | version = '{0}:{1}'.format(epoch, version) 33 | if '(none)' != arch: 34 | version = '{0}.{1}'.format(version, arch) 35 | b.add_package('yum', package, version) 36 | 37 | # Create service resources for each service init script or config 38 | # in this package. 39 | p = subprocess.Popen(['rpm', '-ql', package], 40 | close_fds=True, stdout=subprocess.PIPE) 41 | for line in p.stdout: 42 | try: 43 | manager, service = util.parse_service(line.rstrip()) 44 | if not r.ignore_service(manager, service): 45 | b.add_service(manager, service) 46 | b.add_service_package(manager, service, 'yum', package) 47 | except ValueError: 48 | pass 49 | -------------------------------------------------------------------------------- /blueprint/cli.py: -------------------------------------------------------------------------------- 1 | """ 2 | Instantiate Blueprint objects for the command-line tools. Use of these 3 | functions outside of command-line tools is not advised, as in many cases 4 | they exit the Python interpreter. 5 | """ 6 | 7 | import logging 8 | import os 9 | import sys 10 | 11 | import blueprint 12 | import context_managers 13 | import rules 14 | 15 | 16 | def create(options, args): 17 | """ 18 | Instantiate and return a Blueprint object from either standard input or by 19 | reverse-engineering the system. 20 | """ 21 | try: 22 | with context_managers.mkdtemp(): 23 | 24 | if not os.isatty(sys.stdin.fileno()): 25 | try: 26 | b = blueprint.Blueprint.load(sys.stdin, args[0]) 27 | except ValueError: 28 | logging.error( 29 | 'standard input contains invalid blueprint JSON') 30 | sys.exit(1) 31 | else: 32 | b = blueprint.Blueprint.create(args[0]) 33 | 34 | if options.subtrahend: 35 | logging.info('subtracting {0}'.format(options.subtrahend)) 36 | b_s = blueprint.Blueprint.checkout(options.subtrahend) 37 | b = b - b_s 38 | 39 | b.commit(options.message or '') 40 | return b 41 | 42 | except blueprint.NameError: 43 | logging.error('invalid blueprint name') 44 | sys.exit(1) 45 | 46 | 47 | def read(options, args): 48 | """ 49 | Instantiate and return a Blueprint object from either standard input or by 50 | reading from the local Git repository. 51 | """ 52 | try: 53 | name = args[0] 54 | except IndexError: 55 | name = None 56 | name, stdin = '-' == name and (None, True) or (name, False) 57 | try: 58 | if not os.isatty(sys.stdin.fileno()) or stdin: 59 | try: 60 | 61 | # TODO This implementation won't be able to find source 62 | # tarballs that should be associated with the blueprint 63 | # on standard input. 64 | return blueprint.Blueprint.load(sys.stdin, name) 65 | 66 | except ValueError: 67 | logging.error('standard input contains invalid blueprint JSON') 68 | sys.exit(1) 69 | if name is not None: 70 | return blueprint.Blueprint.checkout(name) 71 | except blueprint.NotFoundError: 72 | logging.error('blueprint {0} does not exist'.format(name)) 73 | sys.exit(1) 74 | except blueprint.NameError: 75 | logging.error('invalid blueprint name {0}'.format(name)) 76 | sys.exit(1) 77 | logging.error('no blueprint found on standard input') 78 | sys.exit(1) 79 | 80 | 81 | def read_rules(options, args): 82 | """ 83 | Instantiate and return a Blueprint object created by rules read from 84 | either standard input or the given pathname. 85 | """ 86 | try: 87 | pathname = args[0] 88 | except IndexError: 89 | pathname = None 90 | pathname, stdin = '-' == pathname and (None, True) or (pathname, False) 91 | r = rules.none() 92 | if not os.isatty(sys.stdin.fileno()) or stdin: 93 | r.parse(sys.stdin) 94 | with context_managers.mkdtemp(): 95 | b = blueprint.Blueprint.rules(r, 'blueprint-rendered-rules') 96 | b.commit(options.message or '') 97 | return b 98 | if pathname is not None: 99 | name, _ = os.path.splitext(os.path.basename(pathname)) 100 | try: 101 | r.parse(open(pathname)) 102 | with context_managers.mkdtemp(): 103 | b = blueprint.Blueprint.rules(r, name) 104 | b.commit(options.message or '') 105 | return b 106 | except blueprint.NameError: 107 | logging.error('invalid blueprint name {0}'.format(name)) 108 | sys.exit(1) 109 | except IOError: 110 | logging.error('{0} does not exist'.format(pathname)) 111 | sys.exit(1) 112 | logging.error('no rules found on standard input') 113 | sys.exit(1) 114 | -------------------------------------------------------------------------------- /blueprint/context_managers.py: -------------------------------------------------------------------------------- 1 | import os 2 | import shutil 3 | import tempfile 4 | 5 | from blueprint import util 6 | 7 | 8 | class cd(object): 9 | """ 10 | Run in an alternative working directory in this context. 11 | """ 12 | 13 | def __init__(self, new_cwd): 14 | self.new_cwd = new_cwd 15 | 16 | def __enter__(self): 17 | self.old_cwd = os.getcwd() 18 | os.chdir(self.new_cwd) 19 | return self 20 | 21 | def __exit__(self, exc_type, exc_value, traceback): 22 | os.chdir(self.old_cwd) 23 | 24 | 25 | class mkdtemp(object): 26 | """ 27 | Run in a temporary working directory in this context. Remove the 28 | temporary directory automatically afterward. 29 | """ 30 | 31 | def __init__(self, dir=None): 32 | self.cwd = os.getcwd() 33 | if dir is None: 34 | dir = tempfile.gettempdir() 35 | self.tempdir = tempfile.mkdtemp(dir=dir) 36 | if util.via_sudo(): 37 | uid = int(os.environ['SUDO_UID']) 38 | gid = int(os.environ['SUDO_GID']) 39 | os.chown(self.tempdir, uid, gid) 40 | 41 | def __enter__(self): 42 | os.chdir(self.tempdir) 43 | return self 44 | 45 | def __exit__(self, exc_type, exc_value, traceback): 46 | os.chdir(self.cwd) 47 | shutil.rmtree(self.tempdir) 48 | -------------------------------------------------------------------------------- /blueprint/deps.py: -------------------------------------------------------------------------------- 1 | import logging 2 | import re 3 | import subprocess 4 | 5 | 6 | def apt(s): 7 | """ 8 | Walk the dependency tree of all the packages in set s all the way to 9 | the leaves. Return the set of s plus all their dependencies. 10 | """ 11 | logging.debug('searching for APT dependencies') 12 | if not isinstance(s, set): 13 | s = set([s]) 14 | tmp_s = s 15 | pattern_sub = re.compile(r'\([^)]+\)') 16 | pattern_split = re.compile(r'[,\|]') 17 | while 1: 18 | new_s = set() 19 | for package in tmp_s: 20 | p = subprocess.Popen( 21 | ['dpkg-query', 22 | '-f', '${Pre-Depends}\n${Depends}\n${Recommends}\n', 23 | '-W', package], 24 | close_fds=True, stdout=subprocess.PIPE) 25 | for line in p.stdout: 26 | line = line.strip() 27 | if '' == line: 28 | continue 29 | for part in pattern_split.split(pattern_sub.sub('', line)): 30 | new_s.add(part.strip()) 31 | 32 | # If there is to be a next iteration, `new_s` must contain some 33 | # packages not yet in `s`. 34 | tmp_s = new_s - s 35 | if 0 == len(tmp_s): 36 | break 37 | s |= new_s 38 | 39 | return s 40 | 41 | 42 | def yum(s): 43 | """ 44 | Walk the dependency tree of all the packages in set s all the way to 45 | the leaves. Return the set of s plus all their dependencies. 46 | """ 47 | logging.debug('searching for Yum dependencies') 48 | 49 | if not hasattr(yum, '_cache'): 50 | yum._cache = {} 51 | try: 52 | p = subprocess.Popen(['rpm', 53 | '-qa', 54 | '--qf=%{NAME}\x1E[%{PROVIDES}\x1F]\n'], 55 | close_fds=True, 56 | stdout=subprocess.PIPE) 57 | for line in p.stdout: 58 | name, caps = line.rstrip().split('\x1E') 59 | yum._cache.update([(cap, name) for cap in caps.split('\x1F')]) 60 | except OSError: 61 | pass 62 | 63 | if not isinstance(s, set): 64 | s = set([s]) 65 | 66 | tmp_s = s 67 | while 1: 68 | new_s = set() 69 | for package in tmp_s: 70 | try: 71 | p = subprocess.Popen(['rpm', '-qR', package], 72 | close_fds=True, 73 | stdout=subprocess.PIPE) 74 | except OSError: 75 | continue 76 | for line in p.stdout: 77 | cap = line.rstrip()[0:line.find(' ')] 78 | if 'rpmlib' == cap[0:6]: 79 | continue 80 | try: 81 | new_s.add(yum._cache[cap]) 82 | except KeyError: 83 | try: 84 | p2 = subprocess.Popen(['rpm', 85 | '-q', 86 | '--qf=%{NAME}', 87 | '--whatprovides', 88 | cap], 89 | close_fds=True, 90 | stdout=subprocess.PIPE) 91 | stdout, stderr = p2.communicate() 92 | yum._cache[cap] = stdout 93 | new_s.add(stdout) 94 | except OSError: 95 | pass 96 | 97 | # If there is to be a next iteration, `new_s` must contain some 98 | # packages not yet in `s`. 99 | tmp_s = new_s - s 100 | if 0 == len(tmp_s): 101 | break 102 | s |= new_s 103 | 104 | return s 105 | -------------------------------------------------------------------------------- /blueprint/frontend/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | Every frontend provider defines a function named as the module that contains 3 | it, which is to be used as its main entrypoint. Only one frontend is 4 | typically in use so, unlike with backends, they're not preloaded. 5 | """ 6 | -------------------------------------------------------------------------------- /blueprint/frontend/blueprint-template.d/lsb.sh: -------------------------------------------------------------------------------- 1 | # LSB properties for use in Blueprint file templates. 2 | 3 | # The distro name. 4 | export DISTRO="$(lsb_release -si 2>/dev/null || { 5 | if [ -f "/etc/debian_version" ] 6 | then echo "Debian" 7 | elif [ -f "/etc/fedora-release" ] 8 | then echo "Fedora" 9 | elif [ -f "/etc/redhat-release" ] 10 | then 11 | if grep -i "CentOS" "/etc/redhat-release" 12 | then echo "CentOS" 13 | elif grep -i "Scientific" "/etc/redhat-release" 14 | then echo "Scientific" 15 | else echo "RedHat" 16 | fi 17 | fi 18 | })" 19 | 20 | # The operating system release codename. 21 | export RELEASE="$(lsb_release -sc 2>/dev/null || { 22 | if [ -f "/etc/debian_version" ] 23 | then cat "/etc/debian_version" 24 | elif [ -f "/etc/redhat-release" ] 25 | then egrep -o "release [0-9.]+" /etc/redhat-release | cut -d" " -f2 26 | fi 27 | })" 28 | -------------------------------------------------------------------------------- /blueprint/frontend/blueprint-template.d/net.sh: -------------------------------------------------------------------------------- 1 | # Network properties for use in Blueprint file templates. 2 | 3 | # Each IPv4 address assigned to an interface, one per line. 4 | export EACH_IP="$(ifconfig | egrep -o "inet addr:[0-9.]+" | cut -d":" -f2)" 5 | 6 | # Each IPv6 address assigned to an interface, one per line. 7 | export EACH_IPv6="$(ifconfig | egrep -o "inet6 addr: [0-9a-f:/]+" | cut -d" " -f3)" 8 | 9 | # The first IPv6 address assigned to any interface. 10 | export IPv6="$(echo "$EACH_IPv6" | head -n1)" 11 | 12 | # The first private IPv4 address assigned to any interface. 13 | export PRIVATE_IP="$(echo "$EACH_IP" | while read IP 14 | do 15 | case "$IP" in 16 | "10."*) echo "$IP";; 17 | "127."*) ;; 18 | "172.16."*|"172.32."*|"172.48."*|"172.64."*|"172.80."*|"172.96."*|"172.112."*|"172.128."*|"172.144."*|"172.160."*|"172.176."*|"172.192."*|"172.208."*|"172.224."*|"172.240."*) echo "$IP";; 19 | "192.168."*) echo "$IP";; 20 | *) ;; 21 | esac 22 | done | head -n1)" 23 | 24 | # The first public IPv4 address assigned to any interface. 25 | export PUBLIC_IP="$(echo "$EACH_IP" | while read IP 26 | do 27 | case "$IP" in 28 | "10."*) ;; 29 | "127."*) ;; 30 | "172.16."*|"172.32."*|"172.48."*|"172.64."*|"172.80."*|"172.96."*|"172.112."*|"172.128."*|"172.144."*|"172.160."*|"172.176."*|"172.192."*|"172.208."*|"172.224."*|"172.240."*) ;; 31 | "192.168."*) ;; 32 | *) echo "$IP";; 33 | esac 34 | done | head -n1)" 35 | 36 | export HOSTNAME="$(hostname)" 37 | 38 | export DNSDOMAINNAME="$(dnsdomainname)" 39 | 40 | export FQDN="$(hostname --fqdn)" 41 | -------------------------------------------------------------------------------- /blueprint/frontend/blueprint-template.d/proc.sh: -------------------------------------------------------------------------------- 1 | # System properties from `/proc` for use in Blueprint file templates. 2 | 3 | # The number of cores in the system. The semantics of this value when read 4 | # in a virtualized environment are undefined. 5 | export CORES="$(grep "^processor" "/proc/cpuinfo" | wc -l)" 6 | 7 | # The total amount of memory in the system in bytes. 8 | export MEM="$(grep "^MemTotal" "/proc/meminfo" | awk '{print $2 * 1024}')" 9 | -------------------------------------------------------------------------------- /blueprint/frontend/blueprint-template.d/uname.sh: -------------------------------------------------------------------------------- 1 | # uname(1) properties for use in Blueprint file templates. 2 | 3 | # The kernel name. This is always "Linux" on systems Blueprint supports. 4 | export KERNEL="$(uname -s)" 5 | 6 | # The kernel release. 7 | export KERNEL_RELEASE="$(uname -r)" 8 | 9 | # The system's hardware architecture. Probably "i386" or "x86_64". 10 | export ARCH="$(uname -i)" 11 | -------------------------------------------------------------------------------- /blueprint/frontend/cfn.json: -------------------------------------------------------------------------------- 1 | { 2 | "AWSTemplateFormatVersion": "2010-09-09", 3 | "Description": "Create an Amazon EC2 instance and bootstrap it as instructed by a blueprint. This is intended to be a starting point for building larger architectures with AWS CloudFormation. **WARNING** This template creates an Amazon EC2 instance. You will be billed for the AWS resources used if you create a stack from this template.", 4 | "Mappings": { 5 | "AWSInstanceType2Arch": { 6 | "c1.medium": { 7 | "Arch": "32" 8 | }, 9 | "c1.xlarge": { 10 | "Arch": "64" 11 | }, 12 | "cc1.4xlarge": { 13 | "Arch": "64" 14 | }, 15 | "m1.large": { 16 | "Arch": "64" 17 | }, 18 | "m1.small": { 19 | "Arch": "32" 20 | }, 21 | "m1.xlarge": { 22 | "Arch": "64" 23 | }, 24 | "m2.2xlarge": { 25 | "Arch": "64" 26 | }, 27 | "m2.4xlarge": { 28 | "Arch": "64" 29 | }, 30 | "m2.xlarge": { 31 | "Arch": "64" 32 | }, 33 | "t1.micro": { 34 | "Arch": "32" 35 | } 36 | }, 37 | "AWSRegionArch2AMI": { 38 | "ap-northeast-1": { 39 | "32": "ami-dcfa4edd", 40 | "64": "ami-e8fa4ee9" 41 | }, 42 | "ap-southeast-1": { 43 | "32": "ami-74dda626", 44 | "64": "ami-7edda62c" 45 | }, 46 | "eu-west-1": { 47 | "32": "ami-24506250", 48 | "64": "ami-20506254" 49 | }, 50 | "us-east-1": { 51 | "32": "ami-7f418316", 52 | "64": "ami-7341831a" 53 | }, 54 | "us-west-1": { 55 | "32": "ami-951945d0", 56 | "64": "ami-971945d2" 57 | } 58 | } 59 | }, 60 | "Outputs": { 61 | "PublicDnsName": { 62 | "Description": "Public DNS name of the EC2 instance.", 63 | "Value": { 64 | "Fn::GetAtt": [ 65 | "EC2Instance", 66 | "PublicDnsName" 67 | ] 68 | } 69 | } 70 | }, 71 | "Parameters": { 72 | "InstanceType": { 73 | "AllowedValues": [ 74 | "t1.micro", 75 | "m1.small", 76 | "m1.large", 77 | "m1.xlarge", 78 | "m2.xlarge", 79 | "m2.2xlarge", 80 | "m2.4xlarge", 81 | "c1.medium", 82 | "c1.xlarge", 83 | "cc1.4xlarge" 84 | ], 85 | "ConstraintDescription": "must be a valid EC2 instance type.", 86 | "Default": "m1.small", 87 | "Description": "EC2 instance type.", 88 | "Type": "String" 89 | }, 90 | "KeyName": { 91 | "Description": "Name of an existing EC2 KeyPair to enable SSH access to the instances", 92 | "Type": "String" 93 | } 94 | }, 95 | "Resources": { 96 | "CfnUser": { 97 | "Properties": { 98 | "Path": "/", 99 | "Policies": [ 100 | { 101 | "PolicyDocument": { 102 | "Statement": [ 103 | { 104 | "Action": "cloudformation:DescribeStackResource", 105 | "Effect": "Allow", 106 | "Resource": "*" 107 | } 108 | ] 109 | }, 110 | "PolicyName": "root" 111 | } 112 | ] 113 | }, 114 | "Type": "AWS::IAM::User" 115 | }, 116 | "EC2Instance": { 117 | "Metadata": { 118 | "AWS::CloudFormation::Init": { 119 | "config": {} 120 | } 121 | }, 122 | "Properties": { 123 | "ImageId": { 124 | "Fn::FindInMap": [ 125 | "AWSRegionArch2AMI", 126 | { 127 | "Ref": "AWS::Region" 128 | }, 129 | { 130 | "Fn::FindInMap": [ 131 | "AWSInstanceType2Arch", 132 | { 133 | "Ref": "InstanceType" 134 | }, 135 | "Arch" 136 | ] 137 | } 138 | ] 139 | }, 140 | "InstanceType": { 141 | "Ref": "InstanceType" 142 | }, 143 | "KeyName": { 144 | "Ref": "KeyName" 145 | }, 146 | "SecurityGroups": [ 147 | { 148 | "Ref": "SecurityGroup" 149 | } 150 | ], 151 | "UserData": { 152 | "Fn::Base64": { 153 | "Fn::Join": [ 154 | "", 155 | [ 156 | "#!/bin/bash\n", 157 | "/opt/aws/bin/cfn-init -s ", 158 | { 159 | "Ref": "AWS::StackName" 160 | }, 161 | " -r EC2Instance ", 162 | " --access-key ", 163 | { 164 | "Ref": "HostKeys" 165 | }, 166 | " --secret-key ", 167 | { 168 | "Fn::GetAtt": [ 169 | "HostKeys", 170 | "SecretAccessKey" 171 | ] 172 | }, 173 | " --region ", 174 | { 175 | "Ref": "AWS::Region" 176 | }, 177 | "\n", 178 | "/opt/aws/bin/cfn-signal -e $? '", 179 | { 180 | "Ref": "WaitHandle" 181 | }, 182 | "'\n" 183 | ] 184 | ] 185 | } 186 | } 187 | }, 188 | "Type": "AWS::EC2::Instance" 189 | }, 190 | "HostKeys": { 191 | "Properties": { 192 | "UserName": { 193 | "Ref": "CfnUser" 194 | } 195 | }, 196 | "Type": "AWS::IAM::AccessKey" 197 | }, 198 | "SecurityGroup": { 199 | "Properties": { 200 | "GroupDescription": "SSH access only.", 201 | "SecurityGroupIngress": [ 202 | { 203 | "CidrIp": "0.0.0.0/0", 204 | "FromPort": "22", 205 | "IpProtocol": "tcp", 206 | "ToPort": "22" 207 | } 208 | ] 209 | }, 210 | "Type": "AWS::EC2::SecurityGroup" 211 | }, 212 | "WaitCondition": { 213 | "DependsOn": "EC2Instance", 214 | "Properties": { 215 | "Handle": { 216 | "Ref": "WaitHandle" 217 | }, 218 | "Timeout": "600" 219 | }, 220 | "Type": "AWS::CloudFormation::WaitCondition" 221 | }, 222 | "WaitHandle": { 223 | "Type": "AWS::CloudFormation::WaitConditionHandle" 224 | } 225 | } 226 | } 227 | -------------------------------------------------------------------------------- /blueprint/frontend/cfn.py: -------------------------------------------------------------------------------- 1 | """ 2 | AWS CloudFormation template generator. 3 | """ 4 | 5 | import codecs 6 | import copy 7 | import gzip as gziplib 8 | import json 9 | import logging 10 | import os.path 11 | import tarfile 12 | 13 | from blueprint import util 14 | 15 | 16 | def cfn(b, relaxed=False): 17 | b2 = copy.deepcopy(b) 18 | def file(pathname, f): 19 | if 'template' in f: 20 | logging.warning('file template {0} won\'t appear in generated ' 21 | 'CloudFormation templates'.format(pathname)) 22 | del b2.files[pathname] 23 | if relaxed: 24 | def package(manager, package, version): 25 | b2.packages[manager][package] = [] 26 | b.walk(file=file, package=package) 27 | else: 28 | b.walk(file=file) 29 | return Template(b2) 30 | 31 | 32 | class Template(dict): 33 | """ 34 | An AWS CloudFormation template that contains a blueprint. 35 | """ 36 | 37 | def __init__(self, b): 38 | self.b = b 39 | if b.name is None: 40 | self.name = 'blueprint-generated-cfn-template' 41 | else: 42 | self.name = b.name 43 | super(Template, self).__init__(json.load(open( 44 | os.path.join(os.path.dirname(__file__), 'cfn.json')))) 45 | b.normalize() 46 | self['Resources']['EC2Instance']['Metadata']\ 47 | ['AWS::CloudFormation::Init']['config'] = b 48 | 49 | def dumps(self): 50 | """ 51 | Serialize this AWS CloudFormation template to JSON in a string. 52 | """ 53 | return util.json_dumps(self) 54 | 55 | def dumpf(self, gzip=False): 56 | """ 57 | Serialize this AWS CloudFormation template to JSON in a file. 58 | """ 59 | if 0 != len(self.b.sources): 60 | logging.warning('this blueprint contains source tarballs - ' 61 | 'to use them with AWS CloudFormation, you must ' 62 | 'store them online and edit the template to ' 63 | 'reference their URLs') 64 | if gzip: 65 | filename = '{0}.json.gz'.format(self.name) 66 | f = gziplib.open(filename, 'w') 67 | else: 68 | filename = '{0}.json'.format(self.name) 69 | f = codecs.open(filename, 'w', encoding='utf-8') 70 | f.write(self.dumps()) 71 | f.close() 72 | return filename 73 | -------------------------------------------------------------------------------- /blueprint/frontend/rules.py: -------------------------------------------------------------------------------- 1 | """ 2 | Blueprint rules generator 3 | """ 4 | 5 | import codecs 6 | import gzip as gziplib 7 | 8 | 9 | def rules(b, relaxed=False): 10 | """ 11 | Generated Blueprint rules. 12 | """ 13 | r = Rules(b.name, comment=b.DISCLAIMER) 14 | 15 | def source(dirname, filename, gen_content, url): 16 | r.append(':source:{0}'.format(dirname)) 17 | 18 | def file(pathname, f): 19 | r.append(':file:{0}'.format(pathname)) 20 | 21 | def package(manager, package, version): 22 | r.append(':package:{0}/{1}'.format(manager, package)) 23 | 24 | def service(manager, service): 25 | r.append(':service:{0}/{1}'.format(manager, service)) 26 | 27 | b.walk(source=source, file=file, package=package, service=service) 28 | 29 | return r 30 | 31 | 32 | class Rules(list): 33 | 34 | def __init__(self, name, comment=None): 35 | if name is None: 36 | self.name = 'blueprint-generated-rules' 37 | else: 38 | self.name = name 39 | super(Rules, self).__init__() 40 | if comment is not None: 41 | self.append(comment) 42 | 43 | def dumpf(self, gzip=False): 44 | """ 45 | Serialize the blueprint to a rules file. 46 | """ 47 | if gzip: 48 | filename = '{0}.blueprint-rules.gz'.format(self.name) 49 | f = gziplib.open(filename, 'w') 50 | else: 51 | filename = '{0}.blueprint-rules'.format(self.name) 52 | f = codecs.open(filename, 'w', encoding='utf-8') 53 | f.write(self.dumps()) 54 | f.close() 55 | return filename 56 | 57 | def dumps(self): 58 | """ 59 | Serialize the blueprint to rules. 60 | """ 61 | return ''.join(['{0}\n'.format(item) for item in self]) 62 | -------------------------------------------------------------------------------- /blueprint/git.py: -------------------------------------------------------------------------------- 1 | import logging 2 | import os 3 | import os.path 4 | import subprocess 5 | import sys 6 | 7 | from blueprint import util 8 | 9 | 10 | class GitError(EnvironmentError): 11 | pass 12 | 13 | 14 | def unroot(): 15 | """ 16 | Drop privileges gained through sudo(1). 17 | """ 18 | if util.via_sudo(): 19 | uid = int(os.environ['SUDO_UID']) 20 | gid = int(os.environ['SUDO_GID']) 21 | os.setgid(gid) 22 | os.setegid(gid) 23 | os.setuid(uid) 24 | os.seteuid(uid) 25 | 26 | 27 | def init(): 28 | """ 29 | Initialize the Git repository. 30 | """ 31 | dirname = repo() 32 | try: 33 | os.makedirs(dirname) 34 | if util.via_sudo(): 35 | uid = int(os.environ['SUDO_UID']) 36 | gid = int(os.environ['SUDO_GID']) 37 | os.chown(dirname, uid, gid) 38 | except OSError: 39 | pass 40 | try: 41 | p = subprocess.Popen(['git', 42 | '--git-dir', dirname, 43 | 'init', 44 | '--bare', 45 | '-q'], 46 | close_fds=True, 47 | preexec_fn=unroot, 48 | stdout=sys.stderr, 49 | stderr=sys.stderr) 50 | except OSError: 51 | logging.error('git not found on PATH - exiting') 52 | sys.exit(1) 53 | p.communicate() 54 | if 0 != p.returncode: 55 | #sys.exit(p.returncode) 56 | raise GitError(p.returncode) 57 | 58 | 59 | def git_args(): 60 | """ 61 | Return the basic arguments for running Git commands against 62 | the blueprints repository. 63 | """ 64 | return ['git', '--git-dir', repo(), '--work-tree', os.getcwd()] 65 | 66 | 67 | def git(*args, **kwargs): 68 | """ 69 | Execute a Git command. Raises GitError on non-zero exits unless the 70 | raise_exc keyword argument is falsey. 71 | """ 72 | try: 73 | p = subprocess.Popen(git_args() + list(args), 74 | close_fds=True, 75 | preexec_fn=unroot, 76 | stdin=subprocess.PIPE, 77 | stdout=subprocess.PIPE) 78 | except OSError: 79 | logging.error('git not found on PATH - exiting') 80 | sys.exit(1) 81 | stdout, stderr = p.communicate(kwargs.get('stdin')) 82 | if 0 != p.returncode and kwargs.get('raise_exc', True): 83 | raise GitError(p.returncode) 84 | return p.returncode, stdout 85 | 86 | 87 | def repo(): 88 | """ 89 | Return the full path to the Git repository. 90 | """ 91 | return os.path.expandvars('$HOME/.blueprints.git') 92 | 93 | 94 | def rev_parse(refname): 95 | """ 96 | Return the referenced commit or None. 97 | """ 98 | status, stdout = git('rev-parse', '-q', '--verify', refname, 99 | raise_exc=False) 100 | if 0 != status: 101 | return None 102 | return stdout.rstrip() 103 | 104 | 105 | def tree(commit): 106 | """ 107 | Return the tree in the given commit or None. 108 | """ 109 | status, stdout = git('show', '--pretty=format:%T', commit) 110 | if 0 != status: 111 | return None 112 | return stdout[0:40] 113 | 114 | 115 | def ls_tree(tree, dirname=[]): 116 | """ 117 | Generate all the pathnames in the given tree. 118 | """ 119 | status, stdout = git('ls-tree', tree) 120 | for line in stdout.splitlines(): 121 | mode, type, sha, filename = line.split() 122 | if 'tree' == type: 123 | for entry in ls_tree(sha, dirname + [filename]): 124 | yield entry 125 | else: 126 | yield mode, type, sha, os.path.join(*dirname + [filename]) 127 | 128 | 129 | def blob(tree, pathname): 130 | """ 131 | Return the SHA of the blob by the given name in the given tree. 132 | """ 133 | for mode, type, sha, pathname2 in ls_tree(tree): 134 | if pathname == pathname2: 135 | return sha 136 | return None 137 | 138 | 139 | def content(blob): 140 | """ 141 | Return the content of the given blob. 142 | """ 143 | status, stdout = git('show', blob) 144 | if 0 != status: 145 | return None 146 | return stdout 147 | 148 | 149 | def cat_file(blob, pathname=None): 150 | """ 151 | If `pathname` is `None`, return an open file handle to the blob in 152 | Git's object store, otherwise stream the blob to `pathname`, all via 153 | the git-cat-file(1) command. 154 | """ 155 | args = git_args() + ['cat-file', 'blob', blob] 156 | if pathname is None: 157 | return subprocess.Popen(args, 158 | close_fds=True, 159 | preexec_fn=unroot, 160 | stdout=subprocess.PIPE).stdout 161 | else: 162 | subprocess.Popen(args, 163 | close_fds=True, 164 | preexec_fn=unroot, 165 | stdout=open(pathname, 'w')).communicate() 166 | 167 | 168 | def write_tree(): 169 | status, stdout = git('write-tree') 170 | if 0 != status: 171 | return None 172 | return stdout.rstrip() 173 | 174 | 175 | def commit_tree(tree, message='', parent=None): 176 | if parent is None: 177 | status, stdout = git('commit-tree', tree, stdin=message) 178 | else: 179 | status, stdout = git('commit-tree', tree, '-p', parent, stdin=message) 180 | if 0 != status: 181 | return None 182 | return stdout.rstrip() 183 | 184 | 185 | def configured(): 186 | """ 187 | Return `True` if the author is configured in Git. This allows Blueprint 188 | to bail out early for users that don't have things configured just right. 189 | """ 190 | return not git('config', 'user.name', raise_exc=False)[0] \ 191 | and not git('config', 'user.email', raise_exc=False)[0] 192 | -------------------------------------------------------------------------------- /blueprint/interactive.py: -------------------------------------------------------------------------------- 1 | """ 2 | Interactively walk blueprints. 3 | """ 4 | 5 | import git 6 | import walk as walklib 7 | 8 | 9 | def walk(b, choose): 10 | """ 11 | Given a function for choosing a `Blueprint` object (based typically on 12 | the result of a `raw_input` call within the `choose` function), populate 13 | one or more `Blueprint`s closed into `choose`. 14 | """ 15 | 16 | def file(pathname, f): 17 | print(pathname) 18 | b_chosen = choose() 19 | if b_chosen is None: 20 | return 21 | b_chosen.add_file(pathname, **f) 22 | 23 | def package(manager, package, version): 24 | print('{0} {1} {2}'.format(manager, package, version)) 25 | b_chosen = choose() 26 | if b_chosen is None: 27 | return 28 | b_chosen.add_package(manager, package, version) 29 | 30 | def service(manager, service): 31 | print('{0} {1}'.format(manager, service)) 32 | b_chosen = choose() 33 | if b_chosen is None: 34 | return 35 | b_chosen.add_service(manager, service) 36 | 37 | def service_file(manager, service, pathname): 38 | b_chosen.add_service_file(manager, service, pathname) 39 | walklib.walk_service_files(b_chosen, 40 | manager, 41 | service, 42 | service_file=service_file) 43 | 44 | def service_package(manager, service, package_manager, package): 45 | b_chosen.add_service_package(manager, 46 | service, 47 | package_manager, 48 | package) 49 | walklib.walk_service_packages(b_chosen, 50 | manager, 51 | service, 52 | service_package=service_package) 53 | def service_source(manager, service, dirname): 54 | b_chosen.add_service_source(manager, service, dirname) 55 | walklib.walk_service_sources(b_chosen, 56 | manager, 57 | service, 58 | service_source=service_source) 59 | 60 | commit = git.rev_parse(b.name) 61 | tree = git.tree(commit) 62 | def source(dirname, filename, gen_content, url): 63 | if url is not None: 64 | print('{0} {1}'.format(dirname, url)) 65 | elif gen_content is not None: 66 | blob = git.blob(tree, filename) 67 | git.cat_file(blob, filename) 68 | print('{0} {1}'.format(dirname, filename)) 69 | b_chosen = choose() 70 | if b_chosen is None: 71 | return 72 | b_chosen.add_source(dirname, filename) 73 | 74 | b.walk(file=file, package=package, service=service, source=source) 75 | -------------------------------------------------------------------------------- /blueprint/io/__init__.py: -------------------------------------------------------------------------------- 1 | import logging 2 | import sys 3 | 4 | from blueprint import Blueprint 5 | from blueprint import cfg 6 | from blueprint import git 7 | import http 8 | 9 | 10 | def pull(server, secret, name): 11 | """ 12 | Pull a blueprint from the secret and name on the configured server. 13 | """ 14 | r = http.get('/{0}/{1}'.format(secret, name), server=server) 15 | if 200 == r.status: 16 | b = Blueprint.load(r, name) 17 | 18 | for filename in b.sources.itervalues(): 19 | logging.info('fetching source tarballs - this may take a while') 20 | r = http.get('/{0}/{1}/{2}'.format(secret, name, filename), 21 | server=server) 22 | if 200 == r.status: 23 | try: 24 | f = open(filename, 'w') 25 | f.write(r.read()) 26 | except OSError: 27 | logging.error('could not open {0}'.format(filename)) 28 | return None 29 | finally: 30 | f.close() 31 | elif 404 == r.status: 32 | logging.error('{0} not found'.format(filename)) 33 | return None 34 | elif 502 == r.status: 35 | logging.error('upstream storage service failed') 36 | return None 37 | else: 38 | logging.error('unexpected {0} fetching tarball'. 39 | format(r.status)) 40 | return None 41 | 42 | return b 43 | elif 404 == r.status: 44 | logging.error('blueprint not found') 45 | elif 502 == r.status: 46 | logging.error('upstream storage service failed') 47 | else: 48 | logging.error('unexpected {0} fetching blueprint'.format(r.status)) 49 | return None 50 | 51 | 52 | def push(server, secret, b): 53 | """ 54 | Push a blueprint to the secret and its name on the configured server. 55 | """ 56 | 57 | r = http.put('/{0}/{1}'.format(secret, b.name), 58 | b.dumps(), 59 | {'Content-Type': 'application/json'}, 60 | server=server) 61 | if 202 == r.status: 62 | pass 63 | elif 400 == r.status: 64 | logging.error('malformed blueprint') 65 | return None 66 | elif 502 == r.status: 67 | logging.error('upstream storage service failed') 68 | return None 69 | else: 70 | logging.error('unexpected {0} storing blueprint'.format(r.status)) 71 | return None 72 | 73 | if b._commit is None and 0 < len(b.sources): 74 | logging.warning('blueprint came from standard input - ' 75 | 'source tarballs will not be pushed') 76 | elif b._commit is not None: 77 | tree = git.tree(b._commit) 78 | for dirname, filename in sorted(b.sources.iteritems()): 79 | blob = git.blob(tree, filename) 80 | content = git.content(blob) 81 | logging.info('storing source tarballs - this may take a while') 82 | r = http.put('/{0}/{1}/{2}'.format(secret, b.name, filename), 83 | content, 84 | {'Content-Type': 'application/x-tar'}, 85 | server=server) 86 | if 202 == r.status: 87 | pass 88 | elif 400 == r.status: 89 | logging.error('tarball content or name not expected') 90 | return None 91 | elif 404 == r.status: 92 | logging.error('blueprint not found') 93 | return None 94 | elif 413 == r.status: 95 | logging.error('tarballs can\'t exceed 64MB') 96 | return None 97 | elif 502 == r.status: 98 | logging.error('upstream storage service failed') 99 | return None 100 | else: 101 | logging.error('unexpected {0} storing tarball'. 102 | format(r.status)) 103 | return None 104 | 105 | return '{0}/{1}/{2}'.format(server, secret, b.name) 106 | 107 | 108 | def secret(server): 109 | """ 110 | Fetch a new secret from the configured server. 111 | """ 112 | r = http.get('/secret', server=server) 113 | if 201 == r.status: 114 | secret = r.read().rstrip() 115 | logging.warning('created secret {0}'.format(secret)) 116 | logging.warning('to set as the default secret, store it in ~/.blueprint.cfg:') 117 | sys.stderr.write('\n[io]\nsecret = {0}\nserver = {1}\n\n'. 118 | format(secret, cfg.get('io', 'server'))) 119 | return secret 120 | elif 502 == r.status: 121 | logging.error('upstream storage service failed') 122 | return None 123 | else: 124 | logging.error('unexpected {0} creating secret'.format(r.status)) 125 | return None 126 | -------------------------------------------------------------------------------- /blueprint/io/http.py: -------------------------------------------------------------------------------- 1 | import errno 2 | import httplib 3 | import socket 4 | import urlparse 5 | 6 | from blueprint import cfg 7 | 8 | 9 | def _connect(server=None): 10 | if server is None: 11 | server = cfg.get('io', 'server') 12 | url = urlparse.urlparse(server) 13 | if -1 == url.netloc.find(':'): 14 | port = url.port or 443 if 'https' == url.scheme else 80 15 | else: 16 | port = None 17 | if 'https' == url.scheme: 18 | return httplib.HTTPSConnection(url.netloc, port) 19 | else: 20 | return httplib.HTTPConnection(url.netloc, port) 21 | 22 | 23 | def _request(verb, path, body=None, headers={}, server=None): 24 | c = _connect(server) 25 | try: 26 | c.request(verb, path, body, headers) 27 | except socket.error as e: 28 | if errno.EPIPE != e.errno: 29 | raise e 30 | return c.getresponse() 31 | 32 | 33 | def delete(path, server=None): 34 | return _request('DELETE', path, server=server) 35 | 36 | 37 | def get(path, headers={}, server=None): 38 | c = _connect(server) 39 | c.request('GET', path, None, headers) 40 | r = c.getresponse() 41 | while r.status in (301, 302, 307): 42 | url = urlparse.urlparse(r.getheader('Location')) 43 | r = get(url.path, 44 | {'Content-Type': r.getheader('Content-Type')}, 45 | urlparse.urlunparse((url.scheme, url.netloc, '', '', '', ''))) 46 | return r 47 | 48 | 49 | def post(path, body, headers={}, server=None): 50 | return _request('POST', path, body, headers, server) 51 | 52 | 53 | def put(path, body, headers={}, server=None): 54 | return _request('PUT', path, body, headers, server) 55 | -------------------------------------------------------------------------------- /blueprint/io/server/backend.py: -------------------------------------------------------------------------------- 1 | import boto 2 | import boto.exception 3 | import httplib 4 | import socket 5 | 6 | from blueprint import cfg 7 | import librato 8 | import statsd 9 | 10 | 11 | access_key = cfg.get('s3', 'access_key') 12 | bucket = cfg.get('s3', 'bucket') 13 | protocol = 'https' if cfg.getboolean('s3', 'use_https') else 'http' 14 | region = cfg.get('s3', 'region') 15 | s3_region = 's3' if 'US' == region else 's3-{0}'.format(region) 16 | secret_key = cfg.get('s3', 'secret_key') 17 | 18 | 19 | def delete(key): 20 | """ 21 | Remove an object from S3. DELETE requests are free but this function 22 | still makes one billable request to account for freed storage. 23 | """ 24 | content_length = head(key) 25 | if content_length is None: 26 | return None 27 | librato.count('blueprint-io-server.requests.delete') 28 | statsd.increment('blueprint-io-server.requests.delete') 29 | c = boto.connect_s3(access_key, secret_key) 30 | b = c.get_bucket(bucket, validate=False) 31 | try: 32 | b.delete_key(key) 33 | # TODO librato.something('blueprint-io-server.storage', -content_length) 34 | statsd.update('blueprint-io-server.storage', -content_length) 35 | except (boto.exception.BotoClientError, 36 | boto.exception.BotoServerError, 37 | boto.exception.S3ResponseError, 38 | httplib.HTTPException, 39 | socket.error, 40 | socket.gaierror): 41 | return False 42 | 43 | 44 | def delete_blueprint(secret, name): 45 | return delete(key_for_blueprint(secret, name)) 46 | 47 | 48 | def delete_tarball(secret, name, sha): 49 | return delete(key_for_tarball(secret, name, sha)) 50 | 51 | 52 | def get(key): 53 | """ 54 | Fetch an object from S3. This function makes one billable request. 55 | """ 56 | librato.count('blueprint-io-server.requests.get') 57 | statsd.increment('blueprint-io-server.requests.get') 58 | c = boto.connect_s3(access_key, secret_key) 59 | b = c.get_bucket(bucket, validate=False) 60 | k = b.new_key(key) 61 | try: 62 | return k.get_contents_as_string() 63 | except boto.exception.S3ResponseError: 64 | return None 65 | except (boto.exception.BotoClientError, 66 | boto.exception.BotoServerError, 67 | httplib.HTTPException, 68 | socket.error, 69 | socket.gaierror): 70 | return False 71 | 72 | 73 | def get_blueprint(secret, name): 74 | return get(key_for_blueprint(secret, name)) 75 | 76 | 77 | def get_tarball(secret, name, sha): 78 | return get(key_for_tarball(secret, name, sha)) 79 | 80 | 81 | def head(key): 82 | """ 83 | Make a HEAD request for an object in S3. This is needed to find the 84 | object's length so it can be accounted. This function makes one 85 | billable request and anticipates another. 86 | """ 87 | librato.count('blueprint-io-server.requests.head') 88 | statsd.increment('blueprint-io-server.requests.head') 89 | c = boto.connect_s3(access_key, secret_key) 90 | b = c.get_bucket(bucket, validate=False) 91 | try: 92 | k = b.get_key(key) 93 | if k is None: 94 | return None 95 | return k.size 96 | except (boto.exception.BotoClientError, 97 | boto.exception.BotoServerError, 98 | httplib.HTTPException, 99 | socket.error, 100 | socket.gaierror): 101 | return False 102 | 103 | 104 | def head_blueprint(secret, name): 105 | return head(key_for_blueprint(secret, name)) 106 | 107 | 108 | def head_tarball(secret, name, sha): 109 | return head(key_for_tarball(secret, name, sha)) 110 | 111 | 112 | def key_for_blueprint(secret, name): 113 | return '{0}/{1}/{2}'.format(secret, 114 | name, 115 | 'blueprint.json') 116 | 117 | 118 | def key_for_tarball(secret, name, sha): 119 | return '{0}/{1}/{2}.tar'.format(secret, 120 | name, 121 | sha) 122 | 123 | 124 | def list(key): 125 | """ 126 | List objects in S3 whose keys begin with the given prefix. This 127 | function makes at least one billable request. 128 | """ 129 | librato.count('blueprint-io-server.requests.list') 130 | statsd.increment('blueprint-io-server.requests.list') 131 | c = boto.connect_s3(access_key, secret_key) 132 | b = c.get_bucket(bucket, validate=False) 133 | return b.list(key) 134 | try: 135 | return True 136 | except (boto.exception.BotoClientError, 137 | boto.exception.BotoServerError, 138 | httplib.HTTPException, 139 | socket.error, 140 | socket.gaierror): 141 | return False 142 | 143 | 144 | def put(key, data): 145 | """ 146 | Store an object in S3. This function makes one billable request. 147 | """ 148 | librato.count('blueprint-io-server.requests.put') 149 | statsd.increment('blueprint-io-server.requests.put') 150 | # TODO librato.something('blueprint-io-server.storage', len(data)) 151 | statsd.update('blueprint-io-server.storage', len(data)) 152 | c = boto.connect_s3(access_key, secret_key) 153 | b = c.get_bucket(bucket, validate=False) 154 | k = b.new_key(key) 155 | try: 156 | k.set_contents_from_string(data, 157 | policy='public-read', 158 | reduced_redundancy=True) 159 | return True 160 | except (boto.exception.BotoClientError, 161 | boto.exception.BotoServerError, 162 | httplib.HTTPException, 163 | socket.error, 164 | socket.gaierror): 165 | return False 166 | 167 | 168 | def put_blueprint(secret, name, data): 169 | return put(key_for_blueprint(secret, name), data) 170 | 171 | 172 | def put_tarball(secret, name, sha, data): 173 | return put(key_for_tarball(secret, name, sha), data) 174 | 175 | 176 | def url_for(key): 177 | return '{0}://{1}.{2}.amazonaws.com/{3}'.format(protocol, 178 | bucket, 179 | s3_region, 180 | key) 181 | 182 | 183 | def url_for_blueprint(secret, name): 184 | return url_for(key_for_blueprint(secret, name)) 185 | 186 | 187 | def url_for_tarball(secret, name, sha): 188 | return url_for(key_for_tarball(secret, name, sha)) 189 | -------------------------------------------------------------------------------- /blueprint/io/server/librato.py: -------------------------------------------------------------------------------- 1 | """ 2 | Testing out Librato's metrics platform. 3 | """ 4 | 5 | from ConfigParser import NoOptionError, NoSectionError 6 | import base64 7 | import httplib 8 | import urllib 9 | 10 | from blueprint import cfg 11 | 12 | 13 | try: 14 | token = cfg.get('librato', 'token') 15 | username = cfg.get('librato', 'username') 16 | auth = 'Basic {0}'.format(base64.b64encode('{0}:{1}'.format(username, 17 | token))) 18 | except (NoOptionError, NoSectionError): 19 | auth = None 20 | 21 | 22 | def count(name, value=1): 23 | """ 24 | Update a counter in Librato's metrics platform. 25 | """ 26 | if auth is None: 27 | return 28 | conn = httplib.HTTPSConnection('metrics-api.librato.com') 29 | conn.request('POST', 30 | '/v1/counters/{0}.json'.format(urllib.quote(name)), 31 | urllib.urlencode({'value': value}), 32 | {'Authorization': auth, 33 | 'Content-Type': 'application/x-www-form-urlencoded'}) 34 | r = conn.getresponse() 35 | conn.close() 36 | -------------------------------------------------------------------------------- /blueprint/io/server/statsd.py: -------------------------------------------------------------------------------- 1 | """ 2 | Python interface to StatsD, cribbed from Steve Ivy 's 3 | python_example.py in the standard distribution. 4 | """ 5 | 6 | from ConfigParser import NoOptionError, NoSectionError 7 | import logging 8 | import random 9 | import socket 10 | import sys 11 | 12 | from blueprint import cfg 13 | 14 | 15 | try: 16 | host, port = cfg.get('statsd', 'host'), cfg.getint('statsd', 'port') 17 | except (NoOptionError, NoSectionError, ValueError): 18 | host = port = None 19 | 20 | 21 | def timing(stat, time, sample_rate=1): 22 | """ 23 | Log timing information. 24 | >>> statsd.timing('some.time', 500) 25 | """ 26 | # TODO First positional argument may be string or list like the others. 27 | _send({stat: '{0}|ms'.format(time)}, sample_rate) 28 | 29 | 30 | def increment(stats, sample_rate=1): 31 | """ 32 | Increments one or more stats counters. 33 | >>> statsd.increment('some.int') 34 | >>> statsd.increment('some.int', 0.5) 35 | """ 36 | update(stats, 1, sample_rate) 37 | 38 | 39 | def decrement(stats, sample_rate=1): 40 | """ 41 | Decrements one or more stats counters. 42 | >>> statsd.decrement('some.int') 43 | """ 44 | update(stats, -1, sample_rate) 45 | 46 | 47 | def update(stats, delta=1, sample_rate=1): 48 | """ 49 | Updates one or more stats counters by arbitrary amounts. 50 | >>> statsd.update('some.int', 10) 51 | """ 52 | if type(stats) is not list: 53 | stats = [stats] 54 | _send(dict([(stat, '{0}|c'.format(delta)) for stat in stats]), sample_rate) 55 | 56 | 57 | def _send(data, sample_rate=1): 58 | """ 59 | Squirt the metrics over UDP. 60 | """ 61 | if host is None or port is None: 62 | return 63 | sampled_data = {} 64 | if 1 > sample_rate: 65 | if random.random() <= sample_rate: 66 | for k, v in data.iteritems(): 67 | sampled_data[k] = '{0}|@{1}'.format(v, sample_rate) 68 | else: 69 | sampled_data = data 70 | s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) 71 | try: 72 | for k, v in sampled_data.iteritems(): 73 | #print('{0}:{1}'.format(k, v)) 74 | s.sendto('{0}:{1}'.format(k, v), (host, port)) 75 | except: 76 | logging.error(repr(sys.exc_info())) 77 | -------------------------------------------------------------------------------- /blueprint/services.py: -------------------------------------------------------------------------------- 1 | """ 2 | Search harder for service dependencies. The APT, Yum, and files backends 3 | have already found the services of note but the file and package resources 4 | which need to trigger restarts have not been fully enumerated. 5 | """ 6 | 7 | from collections import defaultdict 8 | import logging 9 | import os.path 10 | import re 11 | import subprocess 12 | 13 | import util 14 | import walk 15 | 16 | 17 | # Pattern for matching pathnames in init scripts and such. 18 | pattern = re.compile(r'(/[/0-9A-Za-z_.-]+)') 19 | 20 | 21 | def services(b): 22 | logging.info('searching for service dependencies') 23 | 24 | # Command fragments for listing the files in a package. 25 | commands = {'apt': ['dpkg-query', '-L'], 26 | 'yum': ['rpm', '-ql']} 27 | 28 | # Build a map of the directory that contains each file in the 29 | # blueprint to the pathname of that file. 30 | dirs = defaultdict(list) 31 | for pathname in b.files: 32 | dirname = os.path.dirname(pathname) 33 | if dirname not in ('/etc', '/etc/init', '/etc/init.d'): 34 | dirs[dirname].append(pathname) 35 | 36 | def service_file(manager, service, pathname): 37 | """ 38 | Add dependencies for every pathname extracted from init scripts and 39 | other dependent files. 40 | """ 41 | content = open(pathname).read() 42 | for match in pattern.finditer(content): 43 | if match.group(1) in b.files: 44 | b.add_service_file(manager, service, match.group(1)) 45 | for dirname in b.sources.iterkeys(): 46 | content = util.unicodeme(content) 47 | if dirname in content: 48 | b.add_service_source(manager, service, dirname) 49 | 50 | def service_package(manager, service, package_manager, package): 51 | """ 52 | Add dependencies for every file in the blueprint that's also in 53 | this service's package or in a directory in this service's package. 54 | """ 55 | try: 56 | p = subprocess.Popen(commands[package_manager] + [package], 57 | close_fds=True, 58 | stdout=subprocess.PIPE) 59 | except KeyError: 60 | return 61 | for line in p.stdout: 62 | pathname = line.rstrip() 63 | if pathname in b.files: 64 | b.add_service_file(manager, service, pathname) 65 | elif pathname in dirs: 66 | b.add_service_file(manager, service, *dirs[pathname]) 67 | 68 | def service(manager, service): 69 | """ 70 | Add extra file dependencies found in packages. Then add extra file 71 | dependencies found by searching file content for pathnames. 72 | """ 73 | walk.walk_service_packages(b, 74 | manager, 75 | service, 76 | service_package=service_package) 77 | if 'sysvinit' == manager: 78 | service_file(manager, service, '/etc/init.d/{0}'.format(service)) 79 | elif 'upstart' == manager: 80 | service_file(manager, 81 | service, 82 | '/etc/init/{0}.conf'.format(service)) 83 | walk.walk_service_files(b, manager, service, service_file=service_file) 84 | 85 | b.walk(service=service) 86 | -------------------------------------------------------------------------------- /blueprint/util.py: -------------------------------------------------------------------------------- 1 | """ 2 | Utility functions. 3 | """ 4 | 5 | import json 6 | import os 7 | import os.path 8 | import re 9 | import subprocess 10 | 11 | 12 | def arch(): 13 | """ 14 | Return the system's architecture according to dpkg or rpm. 15 | """ 16 | try: 17 | p = subprocess.Popen(['dpkg', '--print-architecture'], 18 | close_fds=True, stdout=subprocess.PIPE) 19 | except OSError as e: 20 | p = subprocess.Popen(['rpm', '--eval', '%_arch'], 21 | close_fds=True, stdout=subprocess.PIPE) 22 | stdout, stderr = p.communicate() 23 | if 0 != p.returncode: 24 | return None 25 | return stdout.rstrip() 26 | 27 | 28 | def lsb_release_codename(): 29 | """ 30 | Return the OS release's codename. 31 | """ 32 | if hasattr(lsb_release_codename, '_cache'): 33 | return lsb_release_codename._cache 34 | try: 35 | p = subprocess.Popen(['lsb_release', '-c'], stdout=subprocess.PIPE) 36 | except OSError: 37 | lsb_release_codename._cache = None 38 | return lsb_release_codename._cache 39 | stdout, stderr = p.communicate() 40 | if 0 != p.returncode: 41 | lsb_release_codename._cache = None 42 | return lsb_release_codename._cache 43 | match = re.search(r'\t(\w+)$', stdout) 44 | if match is None: 45 | lsb_release_codename._cache = None 46 | return lsb_release_codename._cache 47 | lsb_release_codename._cache = match.group(1) 48 | return lsb_release_codename._cache 49 | 50 | 51 | # Patterns for determining which Upstart services should be included, based 52 | # on the events used to start them. 53 | pattern_upstart_1 = re.compile(r'start\s+on\s+runlevel\s+\[[2345]', re.S) 54 | pattern_upstart_2 = re.compile(r'start\s+on\s+\([^)]*(?:filesystem|filesystems|local-filesystems|mounted|net-device-up|remote-filesystems|startup|virtual-filesystems)[^)]*\)', re.S) 55 | 56 | 57 | def parse_service(pathname): 58 | """ 59 | Parse a potential service init script or config file into the 60 | manager and service name or raise `ValueError`. Use the Upstart 61 | "start on" stanzas and SysV init's LSB headers to restrict services to 62 | only those that start at boot and run all the time. 63 | """ 64 | dirname, basename = os.path.split(pathname) 65 | if '/etc/init' == dirname: 66 | service, ext = os.path.splitext(basename) 67 | 68 | # Ignore extraneous files in /etc/init. 69 | if '.conf' != ext: 70 | raise ValueError('not an Upstart config') 71 | 72 | # Ignore services that don't operate on the (faked) main runlevels. 73 | try: 74 | content = open(pathname).read() 75 | except IOError: 76 | raise ValueError('not a readable Upstart config') 77 | if not (pattern_upstart_1.search(content) \ 78 | or pattern_upstart_2.search(content)): 79 | raise ValueError('not a running service') 80 | 81 | return ('upstart', service) 82 | elif '/etc/init.d' == dirname or '/etc/rc.d/init.d' == dirname: 83 | 84 | # Let Upstart handle its services. 85 | if os.path.islink(pathname) \ 86 | and '/lib/init/upstart-job' == os.readlink(pathname): 87 | raise ValueError('proxy for an Upstart config') 88 | 89 | # Ignore services that don't operate on the main runlevels. 90 | try: 91 | content = open(pathname).read() 92 | except IOError: 93 | raise ValueError('not a readable SysV init script') 94 | if not re.search(r'(?:Default-Start|chkconfig):\s*[2345]', content): 95 | raise ValueError('not a running service') 96 | 97 | return ('sysvinit', basename) 98 | else: 99 | raise ValueError('not a service') 100 | 101 | 102 | def rubygems_unversioned(): 103 | """ 104 | Determine whether RubyGems is suffixed by the Ruby language version. 105 | It ceased to be on Oneiric. It always has been on RPM-based distros. 106 | """ 107 | codename = lsb_release_codename() 108 | return codename is None or codename[0] >= 'o' 109 | 110 | 111 | def rubygems_update(): 112 | """ 113 | Determine whether the `rubygems-update` gem is needed. It is needed 114 | on Lucid and older systems. 115 | """ 116 | codename = lsb_release_codename() 117 | return codename is not None and codename[0] < 'm' 118 | 119 | 120 | def rubygems_virtual(): 121 | """ 122 | Determine whether RubyGems is baked into the Ruby 1.9 distribution. 123 | It is on Maverick and newer systems. 124 | """ 125 | codename = lsb_release_codename() 126 | return codename is not None and codename[0] >= 'm' 127 | 128 | 129 | def rubygems_path(): 130 | """ 131 | Determine based on the OS release where RubyGems will install gems. 132 | """ 133 | if lsb_release_codename() is None or rubygems_update(): 134 | return '/usr/lib/ruby/gems' 135 | return '/var/lib/gems' 136 | 137 | 138 | def via_sudo(): 139 | """ 140 | Return `True` if Blueprint was invoked via `sudo`(8), which indicates 141 | that privileges must be dropped when writing to the filesystem. 142 | """ 143 | return 'SUDO_UID' in os.environ \ 144 | and 'SUDO_GID' in os.environ \ 145 | and 'blueprint' in os.environ.get('SUDO_COMMAND', '') 146 | 147 | 148 | class BareString(unicode): 149 | """ 150 | Strings of this type will not be quoted when written into a Puppet 151 | manifest or Chef cookbook. 152 | """ 153 | pass 154 | 155 | 156 | class JSONEncoder(json.JSONEncoder): 157 | 158 | def default(self, o): 159 | if isinstance(o, set): 160 | return list(o) 161 | return super(JSONEncoder, self).default(o) 162 | 163 | def json_dumps(o): 164 | return JSONEncoder(indent=2, sort_keys=True).encode(o) 165 | 166 | 167 | def unicodeme(s): 168 | if isinstance(s, unicode): 169 | return s 170 | for encoding in ('utf_8', 'latin_1'): 171 | try: 172 | return unicode(s, encoding) 173 | except UnicodeDecodeError: 174 | pass 175 | # TODO Issue a warning? 176 | return s 177 | -------------------------------------------------------------------------------- /bootstrap.sh: -------------------------------------------------------------------------------- 1 | if [ ! -f "$HOME/production.pem" ] 2 | then 3 | echo "Paste the contents of ~/production.pem here and press ^D:" >&2 4 | cat >"$HOME/production.pem" 5 | chmod 600 "$HOME/production.pem" 6 | fi 7 | 8 | sudo apt-get -q update 9 | 10 | sudo apt-get -q -y install \ 11 | "build-essential" \ 12 | "git-core" \ 13 | "python" "python-dev" "python-pip" "python-setuptools" \ 14 | "ruby" "ruby-dev" "rubygems" \ 15 | 16 | sudo gem install --no-rdoc --no-ri "fpm" 17 | 18 | sudo pip install boto flask gunicorn nose nose_cov 19 | 20 | mkdir -p "$HOME/work" 21 | cd "$HOME/work" 22 | [ -d "blueprint" ] || git clone "git://github.com/devstructure/blueprint.git" 23 | (cd "blueprint" && git submodule update --init) 24 | [ -d "ronn" ] || git clone "git://github.com/rcrowley/ronn.git" -b "dots" 25 | -------------------------------------------------------------------------------- /etc/bash_completion.d/blueprint: -------------------------------------------------------------------------------- 1 | # vim:ft=sh 2 | _blueprint() { 3 | case "$COMP_CWORD" in 4 | 0) return 0;; 5 | 1) local command="${COMP_WORDS[0]}";; 6 | *) local command="${COMP_WORDS[1]}";; 7 | esac 8 | local prev="${COMP_WORDS[COMP_CWORD-1]}" 9 | case "$command" in 10 | blueprint) 11 | words="list create show apply destroy";; 12 | list|blueprint-list) 13 | case "$prev" in 14 | -q|--quiet|-h|--help) return 0;; 15 | *) words="--quiet --help";; 16 | esac;; 17 | create|blueprint-create) 18 | case "$prev" in 19 | -m|--message|-h|--help) return 0;; 20 | *) words="--sh --puppet --chef --cfn --bcfg2 --cfengine3 --message --quiet --help";; 21 | esac;; 22 | show|blueprint-show) 23 | case "$prev" in 24 | -h|--help) return 0;; 25 | *) words="$(blueprint-list) --sh --puppet --chef --cfn --bcfg2 --cfengine3 --quiet --help";; 26 | esac;; 27 | apply|blueprint-apply) 28 | case "$prev" in 29 | -h|--help) return 0;; 30 | *) words="$(blueprint-list) --quiet --help";; 31 | esac;; 32 | destroy|blueprint-destroy) 33 | case "$prev" in 34 | -h|--help) return 0;; 35 | *) words="$(blueprint-list) --quiet --help";; 36 | esac;; 37 | esac 38 | COMPREPLY=( $(compgen -W "$words" -- "${COMP_WORDS[COMP_CWORD]}") ) 39 | return 0 40 | } 41 | complete -F _blueprint blueprint blueprint-{list,create,show,apply,destroy} 42 | -------------------------------------------------------------------------------- /etc/init/blueprint-io-server.conf: -------------------------------------------------------------------------------- 1 | description "Blueprint I/O Server" 2 | 3 | start on runlevel [2345] 4 | stop on runlevel [!2345] 5 | 6 | respawn 7 | 8 | env VIA=upstart 9 | 10 | pre-start exec mkdir -p /var/log/blueprint-io-server 11 | 12 | exec gunicorn -p/var/run/blueprint-io-server.pid -b127.0.0.1:5000 -w4 blueprint.io.server:app >>/var/log/blueprint-io-server/error.log 2>&1 13 | 14 | # Mention /etc/blueprint.cfg to incite restarts when it changes. 15 | -------------------------------------------------------------------------------- /man/man1/blueprint-apply.1: -------------------------------------------------------------------------------- 1 | .\" generated with Ronn/v0.7.3 2 | .\" http://github.com/rtomayko/ronn/tree/0.7.3 3 | . 4 | .TH "BLUEPRINT\-APPLY" "1" "December 2011" "DevStructure" "Blueprint" 5 | . 6 | .SH "NAME" 7 | \fBblueprint\-apply\fR \- run a blueprint\'s generated shell code 8 | . 9 | .SH "SYNOPSIS" 10 | \fBblueprint apply\fR [\fB\-r\fR] [\fB\-q\fR] [\fIname\fR] 11 | . 12 | .SH "DESCRIPTION" 13 | \fBblueprint\-apply\fR generated POSIX shell code for the blueprint \fIname\fR and executes it\. It is equivalent to 14 | . 15 | .IP "" 4 16 | . 17 | .nf 18 | 19 | blueprint show \-\-sh | sh 20 | . 21 | .fi 22 | . 23 | .IP "" 0 24 | . 25 | .P 26 | and provided for convenience and completeness\. 27 | . 28 | .P 29 | If \fIname\fR is omitted or \fB\-\fR, a blueprint is read from standard input and applied in the same manner\. See \fBblueprint\fR(5) for the details of the format\. 30 | . 31 | .SH "OPTIONS" 32 | . 33 | .TP 34 | \fB\-r\fR, \fB\-\-relaxed\fR 35 | Relax version constraints in generated code\. 36 | . 37 | .TP 38 | \fB\-q\fR, \fB\-\-quiet\fR 39 | Operate quietly\. 40 | . 41 | .TP 42 | \fB\-h\fR, \fB\-\-help\fR 43 | Show a help message\. 44 | . 45 | .SH "FILES" 46 | . 47 | .TP 48 | \fB~/\.blueprints\.git\fR 49 | The local repsitory where blueprints are stored, each on its own branch\. 50 | . 51 | .SH "THEME SONG" 52 | The Flaming Lips \- "The W\.A\.N\.D\. (The Will Always Negates Defeat)" 53 | . 54 | .SH "AUTHOR" 55 | Richard Crowley \fIrichard@devstructure\.com\fR 56 | . 57 | .SH "SEE ALSO" 58 | Part of \fBblueprint\fR(1)\. 59 | -------------------------------------------------------------------------------- /man/man1/blueprint-apply.1.ronn: -------------------------------------------------------------------------------- 1 | blueprint-apply(1) -- run a blueprint's generated shell code 2 | ============================================================ 3 | 4 | ## SYNOPSIS 5 | 6 | `blueprint apply` [`-r`] [`-q`] [_name_] 7 | 8 | ## DESCRIPTION 9 | 10 | `blueprint-apply` generated POSIX shell code for the blueprint _name_ and executes it. It is equivalent to 11 | 12 | blueprint show --sh | sh 13 | 14 | and provided for convenience and completeness. 15 | 16 | If _name_ is omitted or `-`, a blueprint is read from standard input and applied in the same manner. See `blueprint`(5) for the details of the format. 17 | 18 | ## OPTIONS 19 | 20 | * `-r`, `--relaxed`: 21 | Relax version constraints in generated code. 22 | * `-q`, `--quiet`: 23 | Operate quietly. 24 | * `-h`, `--help`: 25 | Show a help message. 26 | 27 | ## FILES 28 | 29 | * `~/.blueprints.git`: 30 | The local repsitory where blueprints are stored, each on its own branch. 31 | 32 | ## THEME SONG 33 | 34 | The Flaming Lips - "The W.A.N.D. (The Will Always Negates Defeat)" 35 | 36 | ## AUTHOR 37 | 38 | Richard Crowley 39 | 40 | ## SEE ALSO 41 | 42 | Part of `blueprint`(1). 43 | -------------------------------------------------------------------------------- /man/man1/blueprint-create.1: -------------------------------------------------------------------------------- 1 | .\" generated with Ronn/v0.7.3 2 | .\" http://github.com/rtomayko/ronn/tree/0.7.3 3 | . 4 | .TH "BLUEPRINT\-CREATE" "1" "December 2011" "DevStructure" "Blueprint" 5 | . 6 | .SH "NAME" 7 | \fBblueprint\-create\fR \- create a blueprint 8 | . 9 | .SH "SYNOPSIS" 10 | \fBblueprint create\fR [\fB\-d\fR \fIsubtrahend\fR] [\fB\-P\fR|\fB\-C\fR|\fB\-S\fR|\|\.\|\.\|\.] [\fB\-m\fR \fImessage\fR] [\fB\-r\fR] [\fB\-q\fR] \fIname\fR 11 | . 12 | .SH "DESCRIPTION" 13 | \fBblueprint\-create\fR creates a list of all installed packages and modified configuration files and stores it in the branch \fIname\fR in the local blueprint repository with the commit \fImessage\fR (if given)\. 14 | . 15 | .P 16 | If standard input is not a TTY, a blueprint is read from standard input rather than created from the system\. See \fBblueprint\fR(5) for the details of the format\. 17 | . 18 | .P 19 | If a \fIsubtrahend\fR is given, it is subtracted from the generated or provided blueprint and the difference is committed as \fIname\fR\. 20 | . 21 | .P 22 | If one of \fB\-\-puppet\fR, \fB\-\-chef\fR, \fB\-\-bcfg2\fR, \fB\-\-sh\fR, or \fB\-\-cfn\fR is given, a Puppet module, a Chef cookbook, a bcfg2 repository, POSIX shell code, or an AWS CloudFormation template will be generated, written to a file or directory in the current working directory, and its filename will be printed to standard output\. 23 | . 24 | .P 25 | Debian packages, Ruby gems, NPM packages, Python packages, PHP PEAR/PECL packages are enumerated in the blueprint\. 26 | . 27 | .P 28 | The contents of system configuration files in \fB/etc\fR that have been created or modified from their packaged versions will be included in the blueprint\. If file is found to have a corresponding template (a file with "\fB\.blueprint\-template\.mustache\fR" appended to its pathname) and optionally a corresponding data script (a file with "\fB\.blueprint\-template\.sh\fR" appended to its pathname), this \fBtemplate\fR and \fBdata\fR are included in the blueprint rather than the file\'s literal content\. 29 | . 30 | .P 31 | Anything installed in \fB/usr/local\fR will be archived and included in the blueprint, tagged with the architecture of the local system\. 32 | . 33 | .P 34 | \fBblueprintignore\fR(5) provides means for ignoring specific files and packages plus treating arbitrary directories as \fB/usr/local\fR is treated\. 35 | . 36 | .SH "OPTIONS" 37 | . 38 | .TP 39 | \fB\-d\fR \fIsubtrahend\fR, \fB\-\-diff=\fR\fIsubtrahend\fR 40 | Blueprint to subtract from the generated blueprint\. 41 | . 42 | .TP 43 | \fB\-P\fR, \fB\-\-puppet\fR 44 | Generate a Puppet module\. 45 | . 46 | .TP 47 | \fB\-C\fR, \fB\-\-chef\fR 48 | Generate a Chef cookbook\. 49 | . 50 | .TP 51 | \fB\-B\fR, \fB\-\-bcfg2\fR 52 | Generate bcfg2 configuration\. 53 | . 54 | .TP 55 | \fB\-S\fR, \fB\-\-sh\fR 56 | Generate POSIX shell code\. 57 | . 58 | .TP 59 | \fB\-\-cfn\fR 60 | Generate an AWS CloudFormation template\. 61 | . 62 | .TP 63 | \fB\-m\fR \fImessage\fR, \fB\-\-message=\fR\fImessage\fR 64 | Commit message\. 65 | . 66 | .TP 67 | \fB\-r\fR, \fB\-\-relaxed\fR 68 | Relax version constraints in generated code\. 69 | . 70 | .TP 71 | \fB\-q\fR, \fB\-\-quiet\fR 72 | Operate quietly\. 73 | . 74 | .TP 75 | \fB\-h\fR, \fB\-\-help\fR 76 | Show a help message\. 77 | . 78 | .SH "FILES" 79 | . 80 | .TP 81 | \fB~/\.blueprints\.git\fR 82 | The local repsitory where blueprints are stored, each on its own branch\. 83 | . 84 | .TP 85 | \fB/etc/blueprintignore\fR, \fB~/\.blueprintignore\fR 86 | Lists of filename patterns to be ignored when creating blueprints\. See \fBblueprintignore\fR(5)\. 87 | . 88 | .SH "THEME SONG" 89 | The Flaming Lips \- "The W\.A\.N\.D\. (The Will Always Negates Defeat)" 90 | . 91 | .SH "AUTHOR" 92 | Richard Crowley \fIrichard@devstructure\.com\fR 93 | . 94 | .SH "SEE ALSO" 95 | Part of \fBblueprint\fR(1)\. 96 | -------------------------------------------------------------------------------- /man/man1/blueprint-create.1.ronn: -------------------------------------------------------------------------------- 1 | blueprint-create(1) -- create a blueprint 2 | ========================================= 3 | 4 | ## SYNOPSIS 5 | 6 | `blueprint create` [`-d` _subtrahend_] [`-P`|`-C`|`-S`|...] [`-m` _message_] [`-r`] [`-q`] _name_ 7 | 8 | ## DESCRIPTION 9 | 10 | `blueprint-create` creates a list of all installed packages and modified configuration files and stores it in the branch _name_ in the local blueprint repository with the commit _message_ (if given). 11 | 12 | If standard input is not a TTY, a blueprint is read from standard input rather than created from the system. See `blueprint`(5) for the details of the format. 13 | 14 | If a _subtrahend_ is given, it is subtracted from the generated or provided blueprint and the difference is committed as _name_. 15 | 16 | If one of `--puppet`, `--chef`, `--bcfg2`, `--sh`, or `--cfn` is given, a Puppet module, a Chef cookbook, POSIX shell code, or an AWS CloudFormation template will be generated, written to a file or directory in the current working directory, and its filename will be printed to standard output. 17 | 18 | Debian packages, Ruby gems, NPM packages, Python packages, PHP PEAR/PECL packages are enumerated in the blueprint. 19 | 20 | The contents of system configuration files in `/etc` that have been created or modified from their packaged versions will be included in the blueprint. If file is found to have a corresponding template (a file with "`.blueprint-template.mustache`" appended to its pathname) and optionally a corresponding data script (a file with "`.blueprint-template.sh`" appended to its pathname), this `template` and `data` are included in the blueprint rather than the file's literal content. 21 | 22 | Anything installed in `/usr/local` will be archived and included in the blueprint, tagged with the architecture of the local system. 23 | 24 | `blueprintignore`(5) provides means for ignoring specific files and packages plus treating arbitrary directories as `/usr/local` is treated. 25 | 26 | ## OPTIONS 27 | 28 | * `-d` _subtrahend_, `--diff=`_subtrahend_: 29 | Blueprint to subtract from the generated blueprint. 30 | * `-P`, `--puppet`: 31 | Generate a Puppet module. 32 | * `-C`, `--chef`: 33 | Generate a Chef cookbook. 34 | * `-B`, `--bcfg2`: 35 | Generate bcfg2 configuration. 36 | * `-S`, `--sh`: 37 | Generate POSIX shell code. 38 | * `--cfn`: 39 | Generate an AWS CloudFormation template. 40 | * `-m` _message_, `--message=`_message_: 41 | Commit message. 42 | * `-r`, `--relaxed`: 43 | Relax version constraints in generated code. 44 | * `-q`, `--quiet`: 45 | Operate quietly. 46 | * `-h`, `--help`: 47 | Show a help message. 48 | 49 | ## FILES 50 | 51 | * `~/.blueprints.git`: 52 | The local repsitory where blueprints are stored, each on its own branch. 53 | * `/etc/blueprintignore`, `~/.blueprintignore`: 54 | Lists of filename patterns to be ignored when creating blueprints. See `blueprintignore`(5). 55 | 56 | ## THEME SONG 57 | 58 | The Flaming Lips - "The W.A.N.D. (The Will Always Negates Defeat)" 59 | 60 | ## AUTHOR 61 | 62 | Richard Crowley 63 | 64 | ## SEE ALSO 65 | 66 | Part of `blueprint`(1). 67 | -------------------------------------------------------------------------------- /man/man1/blueprint-destroy.1: -------------------------------------------------------------------------------- 1 | .\" generated with Ronn/v0.7.3 2 | .\" http://github.com/rtomayko/ronn/tree/0.7.3 3 | . 4 | .TH "BLUEPRINT\-DESTROY" "1" "December 2011" "DevStructure" "Blueprint" 5 | . 6 | .SH "NAME" 7 | \fBblueprint\-destroy\fR \- destroy a blueprint 8 | . 9 | .SH "SYNOPSIS" 10 | \fBblueprint destroy\fR [\fB\-q\fR] \fIname\fR 11 | . 12 | .SH "DESCRIPTION" 13 | \fBblueprint\-destroy\fR removes the blueprint \fIname\fR\. That is, it removes the branch \fIname\fR from the local blueprint repository\. 14 | . 15 | .SH "OPTIONS" 16 | . 17 | .TP 18 | \fB\-q\fR, \fB\-\-quiet\fR 19 | Operate quietly\. 20 | . 21 | .TP 22 | \fB\-h\fR, \fB\-\-help\fR 23 | Show a help message\. 24 | . 25 | .SH "FILES" 26 | . 27 | .TP 28 | \fB~/\.blueprints\.git\fR 29 | The local repsitory where blueprints are stored, each on its own branch\. 30 | . 31 | .SH "THEME SONG" 32 | The Flaming Lips \- "The W\.A\.N\.D\. (The Will Always Negates Defeat)" 33 | . 34 | .SH "AUTHOR" 35 | Richard Crowley \fIrichard@devstructure\.com\fR 36 | . 37 | .SH "SEE ALSO" 38 | Part of \fBblueprint\fR(1)\. 39 | -------------------------------------------------------------------------------- /man/man1/blueprint-destroy.1.ronn: -------------------------------------------------------------------------------- 1 | blueprint-destroy(1) -- destroy a blueprint 2 | =========================================== 3 | 4 | ## SYNOPSIS 5 | 6 | `blueprint destroy` [`-q`] _name_ 7 | 8 | ## DESCRIPTION 9 | 10 | `blueprint-destroy` removes the blueprint _name_. That is, it removes the branch _name_ from the local blueprint repository. 11 | 12 | ## OPTIONS 13 | 14 | * `-q`, `--quiet`: 15 | Operate quietly. 16 | * `-h`, `--help`: 17 | Show a help message. 18 | 19 | ## FILES 20 | 21 | * `~/.blueprints.git`: 22 | The local repsitory where blueprints are stored, each on its own branch. 23 | 24 | ## THEME SONG 25 | 26 | The Flaming Lips - "The W.A.N.D. (The Will Always Negates Defeat)" 27 | 28 | ## AUTHOR 29 | 30 | Richard Crowley 31 | 32 | ## SEE ALSO 33 | 34 | Part of `blueprint`(1). 35 | -------------------------------------------------------------------------------- /man/man1/blueprint-diff.1: -------------------------------------------------------------------------------- 1 | .\" generated with Ronn/v0.7.3 2 | .\" http://github.com/rtomayko/ronn/tree/0.7.3 3 | . 4 | .TH "BLUEPRINT\-DIFF" "1" "December 2011" "DevStructure" "Blueprint" 5 | . 6 | .SH "NAME" 7 | \fBblueprint\-diff\fR \- save the difference between two blueprints 8 | . 9 | .SH "SYNOPSIS" 10 | \fBblueprint diff\fR [\fB\-m\fR \fImessage\fR] [\fB\-q\fR] \fIminuend\fR \fIsubtrahend\fR \fIdifference\fR 11 | . 12 | .SH "DESCRIPTION" 13 | \fBblueprint\-diff\fR saves the difference between the blueprints \fIminuend\fR and \fIsubtrahend\fR as \fIdifference\fR\. Files, package versions, and source tarballs in \fIminuend\fR that either differ from their counterparts in \fIsubtrahend\fR or do not appear at all will be included in \fIdifference\fR\. 14 | . 15 | .SH "OPTIONS" 16 | . 17 | .TP 18 | \fB\-m\fR \fImessage\fR, \fB\-\-message=\fR\fImessage\fR 19 | Commit message\. 20 | . 21 | .TP 22 | \fB\-q\fR, \fB\-\-quiet\fR 23 | Operate quietly\. 24 | . 25 | .TP 26 | \fB\-h\fR, \fB\-\-help\fR 27 | Show a help message\. 28 | . 29 | .SH "FILES" 30 | . 31 | .TP 32 | \fB~/\.blueprints\.git\fR 33 | The local repsitory where blueprints are stored, each on its own branch\. 34 | . 35 | .SH "THEME SONG" 36 | The Flaming Lips \- "The W\.A\.N\.D\. (The Will Always Negates Defeat)" 37 | . 38 | .SH "AUTHOR" 39 | Richard Crowley \fIrichard@devstructure\.com\fR 40 | . 41 | .SH "SEE ALSO" 42 | Part of \fBblueprint\fR(1)\. 43 | -------------------------------------------------------------------------------- /man/man1/blueprint-diff.1.ronn: -------------------------------------------------------------------------------- 1 | blueprint-diff(1) -- save the difference between two blueprints 2 | =============================================================== 3 | 4 | ## SYNOPSIS 5 | 6 | `blueprint diff` [`-m` _message_] [`-q`] _minuend_ _subtrahend_ _difference_ 7 | 8 | ## DESCRIPTION 9 | 10 | `blueprint-diff` saves the difference between the blueprints _minuend_ and _subtrahend_ as _difference_. Files, package versions, and source tarballs in _minuend_ that either differ from their counterparts in _subtrahend_ or do not appear at all will be included in _difference_. 11 | 12 | ## OPTIONS 13 | 14 | * `-m` _message_, `--message=`_message_: 15 | Commit message. 16 | * `-q`, `--quiet`: 17 | Operate quietly. 18 | * `-h`, `--help`: 19 | Show a help message. 20 | 21 | ## FILES 22 | 23 | * `~/.blueprints.git`: 24 | The local repsitory where blueprints are stored, each on its own branch. 25 | 26 | ## THEME SONG 27 | 28 | The Flaming Lips - "The W.A.N.D. (The Will Always Negates Defeat)" 29 | 30 | ## AUTHOR 31 | 32 | Richard Crowley 33 | 34 | ## SEE ALSO 35 | 36 | Part of `blueprint`(1). 37 | -------------------------------------------------------------------------------- /man/man1/blueprint-git.1: -------------------------------------------------------------------------------- 1 | .\" generated with Ronn/v0.7.3 2 | .\" http://github.com/rtomayko/ronn/tree/0.7.3 3 | . 4 | .TH "BLUEPRINT\-GIT" "1" "December 2011" "DevStructure" "Blueprint" 5 | . 6 | .SH "NAME" 7 | \fBblueprint\-git\fR \- low\-level access to blueprints 8 | . 9 | .SH "SYNOPSIS" 10 | \fBblueprint git clone\fR [\fIoptions\fR][\fI\|\.\|\.\|\.\fR] 11 | . 12 | .br 13 | \fBblueprint git\fR [\fIcommand\fR] [\fIoptions\fR][\fI\|\.\|\.\|\.\fR] 14 | . 15 | .SH "DESCRIPTION" 16 | \fBblueprint\-git\fR is a convenience wrapper around \fBgit\fR(1) that provides low\-level access to blueprints stored in the \fB~/\.blueprints\.git\fR repository\. 17 | . 18 | .P 19 | \fBblueprint git clone\fR will clone the entire repository into the \fBblueprints\fR directory within the current working directory\. Since it is unlikely this repository has a \fBmaster\fR branch, it is recommended you use \fBblueprint git clone \-b\fR\fIname\fR to clone the blueprint \fIname\fR\. 20 | . 21 | .P 22 | All other \fBgit\fR(1) subcommands are executed as given with the appropriate \fB\-\-git\-dir\fR set and a temporary working directory which is cleaned up after the command exits\. 23 | . 24 | .SH "OPTIONS" 25 | . 26 | .TP 27 | \fB\-q\fR, \fB\-\-quiet\fR 28 | Operate quietly\. 29 | . 30 | .TP 31 | \fB\-h\fR, \fB\-\-help\fR 32 | Show a help message\. 33 | . 34 | .SH "FILES" 35 | . 36 | .TP 37 | \fB~/\.blueprints\.git\fR 38 | The local repsitory where blueprints are stored, each on its own branch\. 39 | . 40 | .SH "THEME SONG" 41 | The Flaming Lips \- "The W\.A\.N\.D\. (The Will Always Negates Defeat)" 42 | . 43 | .SH "AUTHOR" 44 | Richard Crowley \fIrichard@devstructure\.com\fR 45 | . 46 | .SH "SEE ALSO" 47 | Part of \fBblueprint\fR(1)\. 48 | -------------------------------------------------------------------------------- /man/man1/blueprint-git.1.ronn: -------------------------------------------------------------------------------- 1 | blueprint-git(1) -- low-level access to blueprints 2 | ================================================== 3 | 4 | ## SYNOPSIS 5 | 6 | `blueprint git clone` [_options_][_..._] 7 | `blueprint git` [_command_] [_options_][_..._] 8 | 9 | ## DESCRIPTION 10 | 11 | `blueprint-git` is a convenience wrapper around `git`(1) that provides low-level access to blueprints stored in the `~/.blueprints.git` repository. 12 | 13 | `blueprint git clone` will clone the entire repository into the `blueprints` directory within the current working directory. Since it is unlikely this repository has a `master` branch, it is recommended you use `blueprint git clone -b`_name_ to clone the blueprint _name_. 14 | 15 | All other `git`(1) subcommands are executed as given with the appropriate `--git-dir` set and a temporary working directory which is cleaned up after the command exits. 16 | 17 | ## OPTIONS 18 | 19 | * `-q`, `--quiet`: 20 | Operate quietly. 21 | * `-h`, `--help`: 22 | Show a help message. 23 | 24 | ## FILES 25 | 26 | * `~/.blueprints.git`: 27 | The local repsitory where blueprints are stored, each on its own branch. 28 | 29 | ## THEME SONG 30 | 31 | The Flaming Lips - "The W.A.N.D. (The Will Always Negates Defeat)" 32 | 33 | ## AUTHOR 34 | 35 | Richard Crowley 36 | 37 | ## SEE ALSO 38 | 39 | Part of `blueprint`(1). 40 | -------------------------------------------------------------------------------- /man/man1/blueprint-list.1: -------------------------------------------------------------------------------- 1 | .\" generated with Ronn/v0.7.3 2 | .\" http://github.com/rtomayko/ronn/tree/0.7.3 3 | . 4 | .TH "BLUEPRINT\-LIST" "1" "December 2011" "DevStructure" "Blueprint" 5 | . 6 | .SH "NAME" 7 | \fBblueprint\-list\fR \- list all blueprints 8 | . 9 | .SH "SYNOPSIS" 10 | \fBblueprint list\fR [\fB\-q\fR] 11 | . 12 | .SH "DESCRIPTION" 13 | \fBblueprint\-list\fR lists all blueprints\. That is, all branches in the local blueprint repository\. 14 | . 15 | .SH "OPTIONS" 16 | . 17 | .TP 18 | \fB\-q\fR, \fB\-\-quiet\fR 19 | Operate quietly\. 20 | . 21 | .TP 22 | \fB\-h\fR, \fB\-\-help\fR 23 | Show a help message\. 24 | . 25 | .SH "FILES" 26 | . 27 | .TP 28 | \fB~/\.blueprints\.git\fR 29 | The local repsitory where blueprints are stored, each on its own branch\. 30 | . 31 | .SH "THEME SONG" 32 | The Flaming Lips \- "The W\.A\.N\.D\. (The Will Always Negates Defeat)" 33 | . 34 | .SH "AUTHOR" 35 | Richard Crowley \fIrichard@devstructure\.com\fR 36 | . 37 | .SH "SEE ALSO" 38 | Part of \fBblueprint\fR(1)\. 39 | -------------------------------------------------------------------------------- /man/man1/blueprint-list.1.ronn: -------------------------------------------------------------------------------- 1 | blueprint-list(1) -- list all blueprints 2 | ======================================== 3 | 4 | ## SYNOPSIS 5 | 6 | `blueprint list` [`-q`] 7 | 8 | ## DESCRIPTION 9 | 10 | `blueprint-list` lists all blueprints. That is, all branches in the local blueprint repository. 11 | 12 | ## OPTIONS 13 | 14 | * `-q`, `--quiet`: 15 | Operate quietly. 16 | * `-h`, `--help`: 17 | Show a help message. 18 | 19 | ## FILES 20 | 21 | * `~/.blueprints.git`: 22 | The local repsitory where blueprints are stored, each on its own branch. 23 | 24 | ## THEME SONG 25 | 26 | The Flaming Lips - "The W.A.N.D. (The Will Always Negates Defeat)" 27 | 28 | ## AUTHOR 29 | 30 | Richard Crowley 31 | 32 | ## SEE ALSO 33 | 34 | Part of `blueprint`(1). 35 | -------------------------------------------------------------------------------- /man/man1/blueprint-prune.1: -------------------------------------------------------------------------------- 1 | .\" generated with Ronn/v0.7.3 2 | .\" http://github.com/rtomayko/ronn/tree/0.7.3 3 | . 4 | .TH "BLUEPRINT\-PRUNE" "1" "December 2011" "DevStructure" "Blueprint" 5 | . 6 | .SH "NAME" 7 | \fBblueprint\-prune\fR \- select a subset of resources interactively 8 | . 9 | .SH "SYNOPSIS" 10 | \fBblueprint prune\fR [\fB\-m\fR \fImessage\fR] [\fB\-q\fR] \fIsrc\fR \fIdest\fR 11 | . 12 | .SH "DESCRIPTION" 13 | \fBblueprint\-prune\fR interactively selects a subset of the resources in \fIsrc\fR into \fIdest\fR\. Each resource is presented to the user and the user must indicate whether the resource should be included in \fIdest\fR\. 14 | . 15 | .P 16 | The input provides full \fBreadline\fR(3) support but may not be piped from some other source\. 17 | . 18 | .SH "OPTIONS" 19 | . 20 | .TP 21 | \fB\-m\fR \fImessage\fR, \fB\-\-message=\fR\fImessage\fR 22 | Commit message\. 23 | . 24 | .TP 25 | \fB\-q\fR, \fB\-\-quiet\fR 26 | Operate quietly\. 27 | . 28 | .TP 29 | \fB\-h\fR, \fB\-\-help\fR 30 | Show a help message\. 31 | . 32 | .SH "FILES" 33 | . 34 | .TP 35 | \fB~/\.blueprints\.git\fR 36 | The local repsitory where blueprints are stored, each on its own branch\. 37 | . 38 | .SH "THEME SONG" 39 | The Flaming Lips \- "The W\.A\.N\.D\. (The Will Always Negates Defeat)" 40 | . 41 | .SH "AUTHOR" 42 | Richard Crowley \fIrichard@devstructure\.com\fR 43 | . 44 | .SH "SEE ALSO" 45 | Part of \fBblueprint\fR(1)\. 46 | -------------------------------------------------------------------------------- /man/man1/blueprint-prune.1.ronn: -------------------------------------------------------------------------------- 1 | blueprint-prune(1) -- select a subset of resources interactively 2 | ================================================================ 3 | 4 | ## SYNOPSIS 5 | 6 | `blueprint prune` [`-m` _message_] [`-q`] _src_ _dest_ 7 | 8 | ## DESCRIPTION 9 | 10 | `blueprint-prune` interactively selects a subset of the resources in _src_ into _dest_. Each resource is presented to the user and the user must indicate whether the resource should be included in _dest_. 11 | 12 | The input provides full `readline`(3) support but may not be piped from some other source. 13 | 14 | ## OPTIONS 15 | 16 | * `-m` _message_, `--message=`_message_: 17 | Commit message. 18 | * `-q`, `--quiet`: 19 | Operate quietly. 20 | * `-h`, `--help`: 21 | Show a help message. 22 | 23 | ## FILES 24 | 25 | * `~/.blueprints.git`: 26 | The local repsitory where blueprints are stored, each on its own branch. 27 | 28 | ## THEME SONG 29 | 30 | The Flaming Lips - "The W.A.N.D. (The Will Always Negates Defeat)" 31 | 32 | ## AUTHOR 33 | 34 | Richard Crowley 35 | 36 | ## SEE ALSO 37 | 38 | Part of `blueprint`(1). 39 | -------------------------------------------------------------------------------- /man/man1/blueprint-pull.1: -------------------------------------------------------------------------------- 1 | .\" generated with Ronn/v0.7.3 2 | .\" http://github.com/rtomayko/ronn/tree/0.7.3 3 | . 4 | .TH "BLUEPRINT\-PULL" "1" "December 2011" "DevStructure" "Blueprint" 5 | . 6 | .SH "NAME" 7 | \fBblueprint\-pull\fR \- pull a blueprint from the Internet 8 | . 9 | .SH "SYNOPSIS" 10 | \fBblueprint pull\fR [\fB\-m\fR \fImessage\fR] [\fB\-q\fR] \fIurl\fR 11 | . 12 | .P 13 | With server and secret configured: 14 | . 15 | .P 16 | \fBblueprint pull\fR [\fB\-m\fR \fImessage\fR] [\fB\-q\fR] \fIname\fR 17 | . 18 | .SH "DESCRIPTION" 19 | \fBblueprint\-pull\fR fetches a blueprint and all associated tarballs from the configured server (which is \fBhttps://devstructure\.com\fR by default) and stores them in the local repository as \fIname\fR or the final component of \fIurl\fR\. 20 | . 21 | .P 22 | If \fIname\fR is given, the \fIsecret\fR must be configured in \fB/etc/blueprint\.cfg\fR or \fB~/\.blueprint\.cfg\fR\. See \fBEXAMPLES\fR below\. 23 | . 24 | .P 25 | If \fIurl\fR is given, it must be a fully\-qualified HTTP or HTTPS URL of the form \fIserver\fR/\fIsecret\fR/\fIname\fR\. 26 | . 27 | .P 28 | DevStructure provides a free Blueprint I/O Server at \fIhttps://devstructure\.com\fR, which stores blueprints in Amazon S3\. Alternatively, you can supply your own backend server\. 29 | . 30 | .SH "OPTIONS" 31 | . 32 | .TP 33 | \fB\-m\fR \fImessage\fR, \fB\-\-message=\fR\fImessage\fR 34 | Commit message\. 35 | . 36 | .TP 37 | \fB\-q\fR, \fB\-\-quiet\fR 38 | Operate quietly\. 39 | . 40 | .TP 41 | \fB\-h\fR, \fB\-\-help\fR 42 | Show a help message\. 43 | . 44 | .SH "FILES" 45 | . 46 | .TP 47 | \fB~/\.blueprints\.git\fR 48 | The local repsitory where blueprints are stored, each on its own branch\. 49 | . 50 | .TP 51 | \fB/etc/blueprint\.cfg\fR, \fB~/\.blueprint\.cfg\fR 52 | Optional INI\-style configuration files\. See \fBblueprint\.cfg\fR(5)\. 53 | . 54 | .SH "EXAMPLES" 55 | Example \fB~/\.blueprint\.cfg\fR: 56 | . 57 | .IP "" 4 58 | . 59 | .nf 60 | 61 | [io] 62 | server = https://devstructure\.com 63 | secret = 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz_\- 64 | . 65 | .fi 66 | . 67 | .IP "" 0 68 | . 69 | .P 70 | The \fIserver\fR in this case specifies the default and may be omitted\. 71 | . 72 | .SH "THEME SONG" 73 | Girl Talk \- "Let It Out" 74 | . 75 | .SH "AUTHOR" 76 | . 77 | .IP "\(bu" 4 78 | Matt Tanase \fImatt@devstructure\.com\fR 79 | . 80 | .IP "\(bu" 4 81 | Richard Crowley \fIrichard@devstructure\.com\fR 82 | . 83 | .IP "" 0 84 | . 85 | .SH "SEE ALSO" 86 | Part of \fBblueprint\fR(1)\. 87 | -------------------------------------------------------------------------------- /man/man1/blueprint-pull.1.ronn: -------------------------------------------------------------------------------- 1 | blueprint-pull(1) -- pull a blueprint from the Internet 2 | ======================================================= 3 | 4 | ## SYNOPSIS 5 | 6 | `blueprint pull` [`-m` _message_] [`-q`] _url_ 7 | 8 | With server and secret configured: 9 | 10 | `blueprint pull` [`-m` _message_] [`-q`] _name_ 11 | 12 | ## DESCRIPTION 13 | 14 | `blueprint-pull` fetches a blueprint and all associated tarballs from the configured server (which is `https://devstructure.com` by default) and stores them in the local repository as _name_ or the final component of _url_. 15 | 16 | If _name_ is given, the _secret_ must be configured in `/etc/blueprint.cfg` or `~/.blueprint.cfg`. See `EXAMPLES` below. 17 | 18 | If _url_ is given, it must be a fully-qualified HTTP or HTTPS URL of the form _server_/_secret_/_name_. 19 | 20 | DevStructure provides a free Blueprint I/O Server at , which stores blueprints in Amazon S3. Alternatively, you can supply your own backend server. 21 | 22 | ## OPTIONS 23 | 24 | * `-m` _message_, `--message=`_message_: 25 | Commit message. 26 | * `-q`, `--quiet`: 27 | Operate quietly. 28 | * `-h`, `--help`: 29 | Show a help message. 30 | 31 | ## FILES 32 | 33 | * `~/.blueprints.git`: 34 | The local repsitory where blueprints are stored, each on its own branch. 35 | * `/etc/blueprint.cfg`, `~/.blueprint.cfg`: 36 | Optional INI-style configuration files. See `blueprint.cfg`(5). 37 | 38 | ## EXAMPLES 39 | 40 | Example `~/.blueprint.cfg`: 41 | 42 | [io] 43 | server = https://devstructure.com 44 | secret = 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz_- 45 | 46 | The _server_ in this case specifies the default and may be omitted. 47 | 48 | ## THEME SONG 49 | 50 | Girl Talk - "Let It Out" 51 | 52 | ## AUTHOR 53 | 54 | * Matt Tanase 55 | * Richard Crowley 56 | 57 | ## SEE ALSO 58 | 59 | Part of `blueprint`(1). 60 | -------------------------------------------------------------------------------- /man/man1/blueprint-push.1: -------------------------------------------------------------------------------- 1 | .\" generated with Ronn/v0.7.3 2 | .\" http://github.com/rtomayko/ronn/tree/0.7.3 3 | . 4 | .TH "BLUEPRINT\-PUSH" "1" "December 2011" "DevStructure" "Blueprint" 5 | . 6 | .SH "NAME" 7 | \fBblueprint\-push\fR \- push a blueprint to the Internet 8 | . 9 | .SH "SYNOPSIS" 10 | \fBblueprint push\fR [\fB\-q\fR] \fIname\fR 11 | . 12 | .SH "DESCRIPTION" 13 | \fBblueprint\-push\fR stores the blueprint \fIname\fR and all associated tarballs as \fIname\fR on the configured server (which is \fBhttps://devstructure\.com\fR by default) and prints the URL from which it may be fetched\. 14 | . 15 | .P 16 | If \fIname\fR does not exist and standard input is not a TTY, a blueprint is read from standard input\. See \fBblueprint\fR(5) for the details of the format\. 17 | . 18 | .P 19 | If no \fIsecret\fR is configured in \fB/etc/blueprint\.cfg\fR or \fB~/\.blueprint\.cfg\fR, one is created and a sample configuration is printed\. 20 | . 21 | .P 22 | DevStructure provides a free Blueprint I/O Server at \fIhttps://devstructure\.com\fR, which stores blueprints in Amazon S3\. Alternatively, you can supply your own backend server\. 23 | . 24 | .SH "OPTIONS" 25 | . 26 | .TP 27 | \fB\-q\fR, \fB\-\-quiet\fR 28 | Operate quietly\. 29 | . 30 | .TP 31 | \fB\-h\fR, \fB\-\-help\fR 32 | Show a help message\. 33 | . 34 | .SH "FILES" 35 | . 36 | .TP 37 | \fB~/\.blueprints\.git\fR 38 | The local repsitory where blueprints are stored, each on its own branch\. 39 | . 40 | .TP 41 | \fB/etc/blueprint\.cfg\fR, \fB~/\.blueprint\.cfg\fR 42 | Optional INI\-style configuration files\. See \fBblueprint\.cfg\fR(5)\. 43 | . 44 | .SH "EXAMPLES" 45 | Example \fB~/\.blueprint\.cfg\fR: 46 | . 47 | .IP "" 4 48 | . 49 | .nf 50 | 51 | [io] 52 | server = https://devstructure\.com 53 | secret = 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz_\- 54 | . 55 | .fi 56 | . 57 | .IP "" 0 58 | . 59 | .P 60 | The \fIserver\fR in this case specifies the default and may be omitted\. 61 | . 62 | .SH "THEME SONG" 63 | Girl Talk \- "Let It Out" 64 | . 65 | .SH "AUTHOR" 66 | . 67 | .IP "\(bu" 4 68 | Matt Tanase \fImatt@devstructure\.com\fR 69 | . 70 | .IP "\(bu" 4 71 | Richard Crowley \fIrichard@devstructure\.com\fR 72 | . 73 | .IP "" 0 74 | . 75 | .SH "SEE ALSO" 76 | Part of \fBblueprint\fR(1)\. 77 | -------------------------------------------------------------------------------- /man/man1/blueprint-push.1.ronn: -------------------------------------------------------------------------------- 1 | blueprint-push(1) -- push a blueprint to the Internet 2 | ===================================================== 3 | 4 | ## SYNOPSIS 5 | 6 | `blueprint push` [`-q`] _name_ 7 | 8 | ## DESCRIPTION 9 | 10 | `blueprint-push` stores the blueprint _name_ and all associated tarballs as _name_ on the configured server (which is `https://devstructure.com` by default) and prints the URL from which it may be fetched. 11 | 12 | If _name_ does not exist and standard input is not a TTY, a blueprint is read from standard input. See `blueprint`(5) for the details of the format. 13 | 14 | If no _secret_ is configured in `/etc/blueprint.cfg` or `~/.blueprint.cfg`, one is created and a sample configuration is printed. 15 | 16 | DevStructure provides a free Blueprint I/O Server at , which stores blueprints in Amazon S3. Alternatively, you can supply your own backend server. 17 | 18 | ## OPTIONS 19 | 20 | * `-q`, `--quiet`: 21 | Operate quietly. 22 | * `-h`, `--help`: 23 | Show a help message. 24 | 25 | ## FILES 26 | 27 | * `~/.blueprints.git`: 28 | The local repsitory where blueprints are stored, each on its own branch. 29 | * `/etc/blueprint.cfg`, `~/.blueprint.cfg`: 30 | Optional INI-style configuration files. See `blueprint.cfg`(5). 31 | 32 | ## EXAMPLES 33 | 34 | Example `~/.blueprint.cfg`: 35 | 36 | [io] 37 | server = https://devstructure.com 38 | secret = 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz_- 39 | 40 | The _server_ in this case specifies the default and may be omitted. 41 | 42 | ## THEME SONG 43 | 44 | Girl Talk - "Let It Out" 45 | 46 | ## AUTHOR 47 | 48 | * Matt Tanase 49 | * Richard Crowley 50 | 51 | ## SEE ALSO 52 | 53 | Part of `blueprint`(1). 54 | -------------------------------------------------------------------------------- /man/man1/blueprint-rules.1: -------------------------------------------------------------------------------- 1 | .\" generated with Ronn/v0.7.3 2 | .\" http://github.com/rtomayko/ronn/tree/0.7.3 3 | . 4 | .TH "BLUEPRINT\-RULES" "1" "December 2011" "DevStructure" "Blueprint" 5 | . 6 | .SH "NAME" 7 | \fBblueprint\-rules\fR \- create a blueprint from a blueprint\-rules file 8 | . 9 | .SH "SYNOPSIS" 10 | \fBblueprint rules\fR [\fB\-P\fR|\fB\-C\fR|\fB\-S\fR|\|\.\|\.\|\.] [\fB\-m\fR \fImessage\fR] [\fB\-r\fR] [\fB\-q\fR] [\fIpathname\fR] 11 | . 12 | .SH "DESCRIPTION" 13 | \fBblueprint\-rules\fR renders the \fBblueprint\-rules\fR(5) file \fIpathname\fR and stores the resulting blueprint in a branch of the local blueprint repository with the commit \fImessage\fR (if given)\. The branch name is the basename of \fIpathname\fR with its extension (conventionally \fB\.blueprint\-rules\fR) removed\. 14 | . 15 | .P 16 | If standard input is not a TTY or \fIpathname\fR is \fB\-\fR, rules are read from standard input\. 17 | . 18 | .P 19 | The rendered blueprint will contain only the resources enumerated in \fIpathname\fR (or on standard input), opening the possibility of creating multiple orthogonal blueprints from the same system\. 20 | . 21 | .P 22 | If one of \fB\-\-puppet\fR, \fB\-\-chef\fR, \fB\-\-bcfg2\fR, \fB\-\-sh\fR, or \fB\-\-cfn\fR is given, a Puppet module, a Chef cookbook, a bcfg2 repository, POSIX shell code, or an AWS CloudFormation template will be generated, written to a file or directory in the current working directory, and its filename will be printed to standard output\. 23 | . 24 | .SH "OPTIONS" 25 | . 26 | .TP 27 | \fB\-P\fR, \fB\-\-puppet\fR 28 | Generate a Puppet module\. 29 | . 30 | .TP 31 | \fB\-C\fR, \fB\-\-chef\fR 32 | Generate a Chef cookbook\. 33 | . 34 | .TP 35 | \fB\-B\fR, \fB\-\-bcfg2\fR 36 | Generate bcfg2 configuration\. 37 | . 38 | .TP 39 | \fB\-S\fR, \fB\-\-sh\fR 40 | Generate POSIX shell code\. 41 | . 42 | .TP 43 | \fB\-\-cfn\fR 44 | Generate an AWS CloudFormation template\. 45 | . 46 | .TP 47 | \fB\-m\fR \fImessage\fR, \fB\-\-message=\fR\fImessage\fR 48 | Commit message\. 49 | . 50 | .TP 51 | \fB\-r\fR, \fB\-\-relaxed\fR 52 | Relax version constraints in generated code\. 53 | . 54 | .TP 55 | \fB\-q\fR, \fB\-\-quiet\fR 56 | Operate quietly\. 57 | . 58 | .TP 59 | \fB\-h\fR, \fB\-\-help\fR 60 | Show a help message\. 61 | . 62 | .SH "FILES" 63 | . 64 | .TP 65 | \fB~/\.blueprints\.git\fR 66 | The local repsitory where blueprints are stored, each on its own branch\. 67 | . 68 | .SH "THEME SONG" 69 | The Flaming Lips \- "The W\.A\.N\.D\. (The Will Always Negates Defeat)" 70 | . 71 | .SH "AUTHOR" 72 | Richard Crowley \fIrichard@devstructure\.com\fR 73 | . 74 | .SH "SEE ALSO" 75 | Part of \fBblueprint\fR(1)\. 76 | -------------------------------------------------------------------------------- /man/man1/blueprint-rules.1.ronn: -------------------------------------------------------------------------------- 1 | blueprint-rules(1) -- create a blueprint from a blueprint-rules file 2 | ==================================================================== 3 | 4 | ## SYNOPSIS 5 | 6 | `blueprint rules` [`-P`|`-C`|`-S`|...] [`-m` _message_] [`-r`] [`-q`] [_pathname_] 7 | 8 | ## DESCRIPTION 9 | 10 | `blueprint-rules` renders the `blueprint-rules`(5) file _pathname_ and stores the resulting blueprint in a branch of the local blueprint repository with the commit _message_ (if given). The branch name is the basename of _pathname_ with its extension (conventionally `.blueprint-rules`) removed. 11 | 12 | If standard input is not a TTY or _pathname_ is `-`, rules are read from standard input. 13 | 14 | The rendered blueprint will contain only the resources enumerated in _pathname_ (or on standard input), opening the possibility of creating multiple orthogonal blueprints from the same system. 15 | 16 | If one of `--puppet`, `--chef`, `--sh`, or `--cfn` is given, a Puppet module, a Chef cookbook, POSIX shell code, or an AWS CloudFormation template will be generated, written to a file or directory in the current working directory, and its filename will be printed to standard output. 17 | 18 | ## OPTIONS 19 | 20 | * `-P`, `--puppet`: 21 | Generate a Puppet module. 22 | * `-C`, `--chef`: 23 | Generate a Chef cookbook. 24 | * `-B`, `--bcfg2`: 25 | Generate bcfg2 configuration. 26 | * `-S`, `--sh`: 27 | Generate POSIX shell code. 28 | * `--cfn`: 29 | Generate an AWS CloudFormation template. 30 | * `-m` _message_, `--message=`_message_: 31 | Commit message. 32 | * `-r`, `--relaxed`: 33 | Relax version constraints in generated code. 34 | * `-q`, `--quiet`: 35 | Operate quietly. 36 | * `-h`, `--help`: 37 | Show a help message. 38 | 39 | ## FILES 40 | 41 | * `~/.blueprints.git`: 42 | The local repsitory where blueprints are stored, each on its own branch. 43 | 44 | ## THEME SONG 45 | 46 | The Flaming Lips - "The W.A.N.D. (The Will Always Negates Defeat)" 47 | 48 | ## AUTHOR 49 | 50 | Richard Crowley 51 | 52 | ## SEE ALSO 53 | 54 | Part of `blueprint`(1). 55 | -------------------------------------------------------------------------------- /man/man1/blueprint-show-files.1: -------------------------------------------------------------------------------- 1 | .\" generated with Ronn/v0.7.3 2 | .\" http://github.com/rtomayko/ronn/tree/0.7.3 3 | . 4 | .TH "BLUEPRINT\-SHOW\-FILES" "1" "December 2011" "DevStructure" "Blueprint" 5 | . 6 | .SH "NAME" 7 | \fBblueprint\-show\-files\fR \- show files in a blueprint 8 | . 9 | .SH "SYNOPSIS" 10 | \fBblueprint show\-files\fR [\fB\-q\fR] [\fIname\fR] 11 | . 12 | .SH "DESCRIPTION" 13 | \fBblueprint\-show\-files\fR prints the fully\-qualified pathname of each configuration file (always in \fB/etc\fR) in the blueprint \fIname\fR\. They are printed one\-per\-line to make this command useful in pipelines\. 14 | . 15 | .P 16 | If \fIname\fR is omitted or \fB\-\fR, a blueprint is read from standard input and treated in the same manner\. See \fBblueprint\fR(5) for the details of the format\. 17 | . 18 | .SH "OPTIONS" 19 | . 20 | .TP 21 | \fB\-q\fR, \fB\-\-quiet\fR 22 | Operate quietly\. 23 | . 24 | .TP 25 | \fB\-h\fR, \fB\-\-help\fR 26 | Show a help message\. 27 | . 28 | .SH "FILES" 29 | . 30 | .TP 31 | \fB~/\.blueprints\.git\fR 32 | The local repsitory where blueprints are stored, each on its own branch\. 33 | . 34 | .TP 35 | \fB/etc/blueprintignore\fR, \fB~/\.blueprintignore\fR 36 | Lists of filename patterns to be ignored when creating blueprints\. See \fBblueprintignore\fR(5)\. 37 | . 38 | .SH "THEME SONG" 39 | The Flaming Lips \- "The W\.A\.N\.D\. (The Will Always Negates Defeat)" 40 | . 41 | .SH "AUTHOR" 42 | Richard Crowley \fIrichard@devstructure\.com\fR 43 | . 44 | .SH "SEE ALSO" 45 | Part of \fBblueprint\fR(1)\. 46 | -------------------------------------------------------------------------------- /man/man1/blueprint-show-files.1.ronn: -------------------------------------------------------------------------------- 1 | blueprint-show-files(1) -- show files in a blueprint 2 | ==================================================== 3 | 4 | ## SYNOPSIS 5 | 6 | `blueprint show-files` [`-q`] [_name_] 7 | 8 | ## DESCRIPTION 9 | 10 | `blueprint-show-files` prints the fully-qualified pathname of each configuration file (always in `/etc`) in the blueprint _name_. They are printed one-per-line to make this command useful in pipelines. 11 | 12 | If _name_ is omitted or `-`, a blueprint is read from standard input and treated in the same manner. See `blueprint`(5) for the details of the format. 13 | 14 | ## OPTIONS 15 | 16 | * `-q`, `--quiet`: 17 | Operate quietly. 18 | * `-h`, `--help`: 19 | Show a help message. 20 | 21 | ## FILES 22 | 23 | * `~/.blueprints.git`: 24 | The local repsitory where blueprints are stored, each on its own branch. 25 | * `/etc/blueprintignore`, `~/.blueprintignore`: 26 | Lists of filename patterns to be ignored when creating blueprints. See `blueprintignore`(5). 27 | 28 | ## THEME SONG 29 | 30 | The Flaming Lips - "The W.A.N.D. (The Will Always Negates Defeat)" 31 | 32 | ## AUTHOR 33 | 34 | Richard Crowley 35 | 36 | ## SEE ALSO 37 | 38 | Part of `blueprint`(1). 39 | -------------------------------------------------------------------------------- /man/man1/blueprint-show-ignore.1: -------------------------------------------------------------------------------- 1 | .\" generated with Ronn/v0.7.3 2 | .\" http://github.com/rtomayko/ronn/tree/0.7.3 3 | . 4 | .TH "BLUEPRINT\-SHOW\-IGNORE" "1" "December 2011" "DevStructure" "Blueprint" 5 | . 6 | .SH "NAME" 7 | \fBblueprint\-show\-ignore\fR \- show \.blueprintignore rules from a blueprint 8 | . 9 | .SH "SYNOPSIS" 10 | \fBblueprint show\-ignore\fR [\fB\-q\fR] [\fIname\fR] 11 | . 12 | .SH "DESCRIPTION" 13 | \fBblueprint\-show\-ignore\fR prints the \fBblueprintignore\fR(5) rules from the time the blueprint \fIname\fR was created\. 14 | . 15 | .P 16 | If \fIname\fR is omitted or \fB\-\fR, a blueprint is read from standard input and treated in the same manner\. See \fBblueprint\fR(5) for the details of the format\. 17 | . 18 | .P 19 | This command is preferred over \fBblueprint\-show\fR(1)\'s \fB\-I\fR option\. 20 | . 21 | .SH "OPTIONS" 22 | . 23 | .TP 24 | \fB\-q\fR, \fB\-\-quiet\fR 25 | Operate quietly\. 26 | . 27 | .TP 28 | \fB\-h\fR, \fB\-\-help\fR 29 | Show a help message\. 30 | . 31 | .SH "FILES" 32 | . 33 | .TP 34 | \fB~/\.blueprints\.git\fR 35 | The local repsitory where blueprints are stored, each on its own branch\. 36 | . 37 | .TP 38 | \fB/etc/blueprintignore\fR, \fB~/\.blueprintignore\fR 39 | Lists of filename patterns to be ignored when creating blueprints\. See \fBblueprintignore\fR(5)\. 40 | . 41 | .SH "THEME SONG" 42 | The Flaming Lips \- "The W\.A\.N\.D\. (The Will Always Negates Defeat)" 43 | . 44 | .SH "AUTHOR" 45 | Richard Crowley \fIrichard@devstructure\.com\fR 46 | . 47 | .SH "SEE ALSO" 48 | Part of \fBblueprint\fR(1)\. 49 | -------------------------------------------------------------------------------- /man/man1/blueprint-show-ignore.1.ronn: -------------------------------------------------------------------------------- 1 | blueprint-show-ignore(1) -- show .blueprintignore rules from a blueprint 2 | ======================================================================== 3 | 4 | ## SYNOPSIS 5 | 6 | `blueprint show-ignore` [`-q`] [_name_] 7 | 8 | ## DESCRIPTION 9 | 10 | `blueprint-show-ignore` prints the `blueprintignore`(5) rules from the time the blueprint _name_ was created. 11 | 12 | If _name_ is omitted or `-`, a blueprint is read from standard input and treated in the same manner. See `blueprint`(5) for the details of the format. 13 | 14 | This command is preferred over `blueprint-show`(1)'s `-I` option. 15 | 16 | ## OPTIONS 17 | 18 | * `-q`, `--quiet`: 19 | Operate quietly. 20 | * `-h`, `--help`: 21 | Show a help message. 22 | 23 | ## FILES 24 | 25 | * `~/.blueprints.git`: 26 | The local repsitory where blueprints are stored, each on its own branch. 27 | * `/etc/blueprintignore`, `~/.blueprintignore`: 28 | Lists of filename patterns to be ignored when creating blueprints. See `blueprintignore`(5). 29 | 30 | ## THEME SONG 31 | 32 | The Flaming Lips - "The W.A.N.D. (The Will Always Negates Defeat)" 33 | 34 | ## AUTHOR 35 | 36 | Richard Crowley 37 | 38 | ## SEE ALSO 39 | 40 | Part of `blueprint`(1). 41 | -------------------------------------------------------------------------------- /man/man1/blueprint-show-packages.1: -------------------------------------------------------------------------------- 1 | .\" generated with Ronn/v0.7.3 2 | .\" http://github.com/rtomayko/ronn/tree/0.7.3 3 | . 4 | .TH "BLUEPRINT\-SHOW\-PACKAGES" "1" "December 2011" "DevStructure" "Blueprint" 5 | . 6 | .SH "NAME" 7 | \fBblueprint\-show\-packages\fR \- show packages in a blueprint 8 | . 9 | .SH "SYNOPSIS" 10 | \fBblueprint show\-packages\fR [\fB\-q\fR] [\fIname\fR] 11 | . 12 | .SH "DESCRIPTION" 13 | \fBblueprint\-show\-packages\fR prints the manager, name, and version (separated by spaces) of each package in the blueprint \fIname\fR\. They are printed one\-per\-line to make this command useful in pipelines\. 14 | . 15 | .P 16 | If \fIname\fR is omitted or \fB\-\fR, a blueprint is read from standard input and treated in the same manner\. See \fBblueprint\fR(5) for the details of the format\. 17 | . 18 | .SH "OPTIONS" 19 | . 20 | .TP 21 | \fB\-q\fR, \fB\-\-quiet\fR 22 | Operate quietly\. 23 | . 24 | .TP 25 | \fB\-h\fR, \fB\-\-help\fR 26 | Show a help message\. 27 | . 28 | .SH "FILES" 29 | . 30 | .TP 31 | \fB~/\.blueprints\.git\fR 32 | The local repsitory where blueprints are stored, each on its own branch\. 33 | . 34 | .TP 35 | \fB/etc/blueprintignore\fR, \fB~/\.blueprintignore\fR 36 | Lists of filename patterns to be ignored when creating blueprints\. See \fBblueprintignore\fR(5)\. 37 | . 38 | .SH "THEME SONG" 39 | The Flaming Lips \- "The W\.A\.N\.D\. (The Will Always Negates Defeat)" 40 | . 41 | .SH "AUTHOR" 42 | Richard Crowley \fIrichard@devstructure\.com\fR 43 | . 44 | .SH "SEE ALSO" 45 | Part of \fBblueprint\fR(1)\. 46 | -------------------------------------------------------------------------------- /man/man1/blueprint-show-packages.1.ronn: -------------------------------------------------------------------------------- 1 | blueprint-show-packages(1) -- show packages in a blueprint 2 | ========================================================== 3 | 4 | ## SYNOPSIS 5 | 6 | `blueprint show-packages` [`-q`] [_name_] 7 | 8 | ## DESCRIPTION 9 | 10 | `blueprint-show-packages` prints the manager, name, and version (separated by spaces) of each package in the blueprint _name_. They are printed one-per-line to make this command useful in pipelines. 11 | 12 | If _name_ is omitted or `-`, a blueprint is read from standard input and treated in the same manner. See `blueprint`(5) for the details of the format. 13 | 14 | ## OPTIONS 15 | 16 | * `-q`, `--quiet`: 17 | Operate quietly. 18 | * `-h`, `--help`: 19 | Show a help message. 20 | 21 | ## FILES 22 | 23 | * `~/.blueprints.git`: 24 | The local repsitory where blueprints are stored, each on its own branch. 25 | * `/etc/blueprintignore`, `~/.blueprintignore`: 26 | Lists of filename patterns to be ignored when creating blueprints. See `blueprintignore`(5). 27 | 28 | ## THEME SONG 29 | 30 | The Flaming Lips - "The W.A.N.D. (The Will Always Negates Defeat)" 31 | 32 | ## AUTHOR 33 | 34 | Richard Crowley 35 | 36 | ## SEE ALSO 37 | 38 | Part of `blueprint`(1). 39 | -------------------------------------------------------------------------------- /man/man1/blueprint-show-services.1: -------------------------------------------------------------------------------- 1 | .\" generated with Ronn/v0.7.3 2 | .\" http://github.com/rtomayko/ronn/tree/0.7.3 3 | . 4 | .TH "BLUEPRINT\-SHOW\-SERVICES" "1" "December 2011" "DevStructure" "Blueprint" 5 | . 6 | .SH "NAME" 7 | \fBblueprint\-show\-services\fR \- show services in a blueprint 8 | . 9 | .SH "SYNOPSIS" 10 | \fBblueprint show\-services\fR [\fB\-q\fR] [\fIname\fR] 11 | . 12 | .SH "DESCRIPTION" 13 | \fBblueprint\-show\-servies\fR prints the manager and name (separated by spaces) of each service in the blueprint \fIname\fR\. They are printed one\-per\-line to make this command useful in pipelines\. 14 | . 15 | .P 16 | If \fIname\fR is omitted or \fB\-\fR, a blueprint is read from standard input and treated in the same manner\. See \fBblueprint\fR(5) for the details of the format\. 17 | . 18 | .SH "OPTIONS" 19 | . 20 | .TP 21 | \fB\-q\fR, \fB\-\-quiet\fR 22 | Operate quietly\. 23 | . 24 | .TP 25 | \fB\-h\fR, \fB\-\-help\fR 26 | Show a help message\. 27 | . 28 | .SH "FILES" 29 | . 30 | .TP 31 | \fB~/\.blueprints\.git\fR 32 | The local repsitory where blueprints are stored, each on its own branch\. 33 | . 34 | .TP 35 | \fB/etc/blueprintignore\fR, \fB~/\.blueprintignore\fR 36 | Lists of filename patterns to be ignored when creating blueprints\. See \fBblueprintignore\fR(5)\. 37 | . 38 | .SH "THEME SONG" 39 | The Flaming Lips \- "The W\.A\.N\.D\. (The Will Always Negates Defeat)" 40 | . 41 | .SH "AUTHOR" 42 | Richard Crowley \fIrichard@devstructure\.com\fR 43 | . 44 | .SH "SEE ALSO" 45 | Part of \fBblueprint\fR(1)\. 46 | -------------------------------------------------------------------------------- /man/man1/blueprint-show-services.1.ronn: -------------------------------------------------------------------------------- 1 | blueprint-show-services(1) -- show services in a blueprint 2 | ========================================================== 3 | 4 | ## SYNOPSIS 5 | 6 | `blueprint show-services` [`-q`] [_name_] 7 | 8 | ## DESCRIPTION 9 | 10 | `blueprint-show-servies` prints the manager and name (separated by spaces) of each service in the blueprint _name_. They are printed one-per-line to make this command useful in pipelines. 11 | 12 | If _name_ is omitted or `-`, a blueprint is read from standard input and treated in the same manner. See `blueprint`(5) for the details of the format. 13 | 14 | ## OPTIONS 15 | 16 | * `-q`, `--quiet`: 17 | Operate quietly. 18 | * `-h`, `--help`: 19 | Show a help message. 20 | 21 | ## FILES 22 | 23 | * `~/.blueprints.git`: 24 | The local repsitory where blueprints are stored, each on its own branch. 25 | * `/etc/blueprintignore`, `~/.blueprintignore`: 26 | Lists of filename patterns to be ignored when creating blueprints. See `blueprintignore`(5). 27 | 28 | ## THEME SONG 29 | 30 | The Flaming Lips - "The W.A.N.D. (The Will Always Negates Defeat)" 31 | 32 | ## AUTHOR 33 | 34 | Richard Crowley 35 | 36 | ## SEE ALSO 37 | 38 | Part of `blueprint`(1). 39 | -------------------------------------------------------------------------------- /man/man1/blueprint-show-sources.1: -------------------------------------------------------------------------------- 1 | .\" generated with Ronn/v0.7.3 2 | .\" http://github.com/rtomayko/ronn/tree/0.7.3 3 | . 4 | .TH "BLUEPRINT\-SHOW\-SOURCES" "1" "December 2011" "DevStructure" "Blueprint" 5 | . 6 | .SH "NAME" 7 | \fBblueprint\-show\-sources\fR \- show source tarballs in a blueprint 8 | . 9 | .SH "SYNOPSIS" 10 | \fBblueprint show\-services\fR [\fB\-q\fR] [\fIname\fR] 11 | . 12 | .SH "DESCRIPTION" 13 | \fBblueprint\-show\-sources\fR prints details about each source tarball in the blueprint \fIname\fR\. The directory name and tarball filename (separated by spaces) are printed to standard error\. The verbose listing of the tarball\'s contents are listed by \fBtar\fR(1)\'s \fBtv\fR options\. 14 | . 15 | .P 16 | If \fIname\fR is omitted or \fB\-\fR, a blueprint is read from standard input and treated in the same manner\. See \fBblueprint\fR(5) for the details of the format\. 17 | . 18 | .SH "OPTIONS" 19 | . 20 | .TP 21 | \fB\-q\fR, \fB\-\-quiet\fR 22 | Operate quietly\. 23 | . 24 | .TP 25 | \fB\-h\fR, \fB\-\-help\fR 26 | Show a help message\. 27 | . 28 | .SH "FILES" 29 | . 30 | .TP 31 | \fB~/\.blueprints\.git\fR 32 | The local repsitory where blueprints are stored, each on its own branch\. 33 | . 34 | .TP 35 | \fB/etc/blueprintignore\fR, \fB~/\.blueprintignore\fR 36 | Lists of filename patterns to be ignored when creating blueprints\. See \fBblueprintignore\fR(5)\. 37 | . 38 | .SH "THEME SONG" 39 | The Flaming Lips \- "The W\.A\.N\.D\. (The Will Always Negates Defeat)" 40 | . 41 | .SH "AUTHOR" 42 | Richard Crowley \fIrichard@devstructure\.com\fR 43 | . 44 | .SH "SEE ALSO" 45 | Part of \fBblueprint\fR(1)\. 46 | -------------------------------------------------------------------------------- /man/man1/blueprint-show-sources.1.ronn: -------------------------------------------------------------------------------- 1 | blueprint-show-sources(1) -- show source tarballs in a blueprint 2 | ================================================================ 3 | 4 | ## SYNOPSIS 5 | 6 | `blueprint show-services` [`-q`] [_name_] 7 | 8 | ## DESCRIPTION 9 | 10 | `blueprint-show-sources` prints details about each source tarball in the blueprint _name_. The directory name and tarball filename (separated by spaces) are printed to standard error. The verbose listing of the tarball's contents are listed by `tar`(1)'s `tv` options. 11 | 12 | If _name_ is omitted or `-`, a blueprint is read from standard input and treated in the same manner. See `blueprint`(5) for the details of the format. 13 | 14 | ## OPTIONS 15 | 16 | * `-q`, `--quiet`: 17 | Operate quietly. 18 | * `-h`, `--help`: 19 | Show a help message. 20 | 21 | ## FILES 22 | 23 | * `~/.blueprints.git`: 24 | The local repsitory where blueprints are stored, each on its own branch. 25 | * `/etc/blueprintignore`, `~/.blueprintignore`: 26 | Lists of filename patterns to be ignored when creating blueprints. See `blueprintignore`(5). 27 | 28 | ## THEME SONG 29 | 30 | The Flaming Lips - "The W.A.N.D. (The Will Always Negates Defeat)" 31 | 32 | ## AUTHOR 33 | 34 | Richard Crowley 35 | 36 | ## SEE ALSO 37 | 38 | Part of `blueprint`(1). 39 | -------------------------------------------------------------------------------- /man/man1/blueprint-show.1: -------------------------------------------------------------------------------- 1 | .\" generated with Ronn/v0.7.3 2 | .\" http://github.com/rtomayko/ronn/tree/0.7.3 3 | . 4 | .TH "BLUEPRINT\-SHOW" "1" "December 2011" "DevStructure" "Blueprint" 5 | . 6 | .SH "NAME" 7 | \fBblueprint\-show\fR \- generate code from a blueprint 8 | . 9 | .SH "SYNOPSIS" 10 | \fBblueprint show\fR [\fB\-P\fR|\fB\-C\fR|\fB\-S\fR|\|\.\|\.\|\.] [\fB\-r\fR] [\fB\-q\fR] [\fIname\fR] 11 | . 12 | .SH "DESCRIPTION" 13 | \fBblueprint\-show\fR generates code from the blueprint \fIname\fR as a Puppet module, a Chef cookbook, a bcfg2 repository, POSIX shell code, or an AWS CloudFormation template as indicated by the \fB\-\-puppet\fR, \fB\-\-chef\fR, \fB\-\-bcfg2\fR, \fB\-\-sh\fR, or \fB\-\-cfn\fR option\. The generated code will be written to a file or directory in the current working directory and its name will be printed to standard output\. 14 | . 15 | .P 16 | If none of \fB\-\-puppet\fR, \fB\-\-chef\fR, \fB\-\-bcfg2\fR, \fB\-\-sh\fR, or \fB\-\-cfn\fR are given, the raw JSON data structure that describes the blueprint is printed as described in \fBblueprint\fR(5)\. 17 | . 18 | .P 19 | The POSIX shell code generator is the only one prepared to handle files to be rendered from a template\. All other code generators will print a warning and ignore such files\. 20 | . 21 | .P 22 | If \fIname\fR is omitted or \fB\-\fR, a blueprint is read from standard input and treated in the same manner\. See \fBblueprint\fR(5) for the details of the format\. 23 | . 24 | .SH "OPTIONS" 25 | . 26 | .TP 27 | \fB\-P\fR, \fB\-\-puppet\fR 28 | Generate a Puppet module\. 29 | . 30 | .TP 31 | \fB\-C\fR, \fB\-\-chef\fR 32 | Generate a Chef cookbook\. 33 | . 34 | .TP 35 | \fB\-B\fR, \fB\-\-bcfg2\fR 36 | Generate bcfg2 configuration\. 37 | . 38 | .TP 39 | \fB\-S\fR, \fB\-\-sh\fR 40 | Generate POSIX shell code\. 41 | . 42 | .TP 43 | \fB\-\-cfn\fR 44 | Generate an AWS CloudFormation template\. 45 | . 46 | .TP 47 | \fB\-r\fR, \fB\-\-relaxed\fR 48 | Relax version constraints in generated code\. 49 | . 50 | .TP 51 | \fB\-q\fR, \fB\-\-quiet\fR 52 | Operate quietly\. 53 | . 54 | .TP 55 | \fB\-h\fR, \fB\-\-help\fR 56 | Show a help message\. 57 | . 58 | .SH "FILES" 59 | . 60 | .TP 61 | \fB~/\.blueprints\.git\fR 62 | The local repsitory where blueprints are stored, each on its own branch\. 63 | . 64 | .TP 65 | \fB/etc/blueprintignore\fR, \fB~/\.blueprintignore\fR 66 | Lists of filename patterns to be ignored when creating blueprints\. See \fBblueprintignore\fR(5)\. 67 | . 68 | .SH "THEME SONG" 69 | The Flaming Lips \- "The W\.A\.N\.D\. (The Will Always Negates Defeat)" 70 | . 71 | .SH "AUTHOR" 72 | Richard Crowley \fIrichard@devstructure\.com\fR 73 | . 74 | .SH "SEE ALSO" 75 | Part of \fBblueprint\fR(1)\. 76 | -------------------------------------------------------------------------------- /man/man1/blueprint-show.1.ronn: -------------------------------------------------------------------------------- 1 | blueprint-show(1) -- generate code from a blueprint 2 | =================================================== 3 | 4 | ## SYNOPSIS 5 | 6 | `blueprint show` [`-P`|`-C`|`-S`|...] [`-r`] [`-q`] [_name_] 7 | 8 | ## DESCRIPTION 9 | 10 | `blueprint-show` generates code from the blueprint _name_ as a Puppet module, a Chef cookbook, POSIX shell code, or an AWS CloudFormation template as indicated by the `--puppet`, `--chef`, `--bcfg2`, `--sh`, or `--cfn` option. The generated code will be written to a file or directory in the current working directory and its name will be printed to standard output. 11 | 12 | If none of `--puppet`, `--chef`, `--bcfg2`, `--sh`, or `--cfn` are given, the raw JSON data structure that describes the blueprint is printed as described in `blueprint`(5). 13 | 14 | The POSIX shell code generator is the only one prepared to handle files to be rendered from a template. All other code generators will print a warning and ignore such files. 15 | 16 | If _name_ is omitted or `-`, a blueprint is read from standard input and treated in the same manner. See `blueprint`(5) for the details of the format. 17 | 18 | ## OPTIONS 19 | 20 | * `-P`, `--puppet`: 21 | Generate a Puppet module. 22 | * `-C`, `--chef`: 23 | Generate a Chef cookbook. 24 | * `-B`, `--bcfg2`: 25 | Generate bcfg2 configuration. 26 | * `-S`, `--sh`: 27 | Generate POSIX shell code. 28 | * `--cfn`: 29 | Generate an AWS CloudFormation template. 30 | * `-r`, `--relaxed`: 31 | Relax version constraints in generated code. 32 | * `-q`, `--quiet`: 33 | Operate quietly. 34 | * `-h`, `--help`: 35 | Show a help message. 36 | 37 | ## FILES 38 | 39 | * `~/.blueprints.git`: 40 | The local repsitory where blueprints are stored, each on its own branch. 41 | * `/etc/blueprintignore`, `~/.blueprintignore`: 42 | Lists of filename patterns to be ignored when creating blueprints. See `blueprintignore`(5). 43 | 44 | ## THEME SONG 45 | 46 | The Flaming Lips - "The W.A.N.D. (The Will Always Negates Defeat)" 47 | 48 | ## AUTHOR 49 | 50 | Richard Crowley 51 | 52 | ## SEE ALSO 53 | 54 | Part of `blueprint`(1). 55 | -------------------------------------------------------------------------------- /man/man1/blueprint-split.1: -------------------------------------------------------------------------------- 1 | .\" generated with Ronn/v0.7.3 2 | .\" http://github.com/rtomayko/ronn/tree/0.7.3 3 | . 4 | .TH "BLUEPRINT\-SPLIT" "1" "December 2011" "DevStructure" "Blueprint" 5 | . 6 | .SH "NAME" 7 | \fBblueprint\-split\fR \- split one blueprint into two others interactively 8 | . 9 | .SH "SYNOPSIS" 10 | \fBblueprint split\fR [\fB\-m\fR \fImessage\fR] [\fB\-q\fR] \fIsrc\fR \fIdest\-a\fR \fIdest\-b\fR 11 | . 12 | .SH "DESCRIPTION" 13 | \fBblueprint\-split\fR interactively splits \fIsrc\fR into \fIdest\-a\fR and \fIdest\-b\fR\. Each resource is presented to the user and the user must indicate which destination blueprint will receive the resource by typing a unique prefix of the destination blueprint\'s name\. 14 | . 15 | .P 16 | The input provides full \fBreadline\fR(3) support but may not be piped from some other source\. 17 | . 18 | .SH "OPTIONS" 19 | . 20 | .TP 21 | \fB\-m\fR \fImessage\fR, \fB\-\-message=\fR\fImessage\fR 22 | Commit message\. 23 | . 24 | .TP 25 | \fB\-q\fR, \fB\-\-quiet\fR 26 | Operate quietly\. 27 | . 28 | .TP 29 | \fB\-h\fR, \fB\-\-help\fR 30 | Show a help message\. 31 | . 32 | .SH "FILES" 33 | . 34 | .TP 35 | \fB~/\.blueprints\.git\fR 36 | The local repsitory where blueprints are stored, each on its own branch\. 37 | . 38 | .SH "THEME SONG" 39 | The Flaming Lips \- "The W\.A\.N\.D\. (The Will Always Negates Defeat)" 40 | . 41 | .SH "AUTHOR" 42 | Richard Crowley \fIrichard@devstructure\.com\fR 43 | . 44 | .SH "SEE ALSO" 45 | Part of \fBblueprint\fR(1)\. 46 | -------------------------------------------------------------------------------- /man/man1/blueprint-split.1.ronn: -------------------------------------------------------------------------------- 1 | blueprint-split(1) -- split one blueprint into two others interactively 2 | ======================================================================= 3 | 4 | ## SYNOPSIS 5 | 6 | `blueprint split` [`-m` _message_] [`-q`] _src_ _dest-a_ _dest-b_ 7 | 8 | ## DESCRIPTION 9 | 10 | `blueprint-split` interactively splits _src_ into _dest-a_ and _dest-b_. Each resource is presented to the user and the user must indicate which destination blueprint will receive the resource by typing a unique prefix of the destination blueprint's name. 11 | 12 | The input provides full `readline`(3) support but may not be piped from some other source. 13 | 14 | ## OPTIONS 15 | 16 | * `-m` _message_, `--message=`_message_: 17 | Commit message. 18 | * `-q`, `--quiet`: 19 | Operate quietly. 20 | * `-h`, `--help`: 21 | Show a help message. 22 | 23 | ## FILES 24 | 25 | * `~/.blueprints.git`: 26 | The local repsitory where blueprints are stored, each on its own branch. 27 | 28 | ## THEME SONG 29 | 30 | The Flaming Lips - "The W.A.N.D. (The Will Always Negates Defeat)" 31 | 32 | ## AUTHOR 33 | 34 | Richard Crowley 35 | 36 | ## SEE ALSO 37 | 38 | Part of `blueprint`(1). 39 | -------------------------------------------------------------------------------- /man/man1/blueprint-template.1: -------------------------------------------------------------------------------- 1 | .\" generated with Ronn/v0.7.3 2 | .\" http://github.com/rtomayko/ronn/tree/0.7.3 3 | . 4 | .TH "BLUEPRINT\-TEMPLATE" "1" "December 2011" "DevStructure" "Blueprint" 5 | . 6 | .SH "NAME" 7 | \fBblueprint\-template\fR \- render mustache\.sh templates locally 8 | . 9 | .SH "SYNOPSIS" 10 | \fBblueprint template\fR [\fB\-q\fR] \fIpathname\fR 11 | . 12 | .SH "DESCRIPTION" 13 | \fBblueprint\-template\fR renders the template for the given \fIpathname\fR using the \fBmustache\.sh\fR template rendering engine\. The template for \fIpathname\fR must be stored in \fIpathname\fR\fB\.blueprint\-template\.mustache\fR\. Data meant only for \fIpathname\fR may be stored in \fIpathname\fR\fB\.blueprint\-template\.sh\fR, a \fBsource\fR\-able POSIX shell script\. 14 | . 15 | .P 16 | Data meant for all templates may be stored in files ending in \fB\.sh\fR in \fB/etc/blueprint\-template\.d\fR, again as \fBsource\fR\-able POSIX shell scripts\. Blueprint comes with a selection of suitable data, see \fBblueprint\-template\fR(7)\. 17 | . 18 | .P 19 | This program is meant for use during development and testing of templates; \fBblueprint\-apply\fR(1) and \fBblueprint\-show\fR(1) take care of rendering templates automatically\. 20 | . 21 | .SH "OPTIONS" 22 | . 23 | .TP 24 | \fB\-q\fR, \fB\-\-quiet\fR 25 | Operate quietly\. 26 | . 27 | .TP 28 | \fB\-h\fR, \fB\-\-help\fR 29 | Show a help message\. 30 | . 31 | .SH "FILES" 32 | . 33 | .TP 34 | \fB/etc/blueprint\-template\.d\fR 35 | \fBsource\fR\-able POSIX shell scripts ending in \fB\.sh\fR that set environment variables for use in templates\. 36 | . 37 | .SH "THEME SONG" 38 | The Flaming Lips \- "The W\.A\.N\.D\. (The Will Always Negates Defeat)" 39 | . 40 | .SH "AUTHOR" 41 | Richard Crowley \fIrichard@devstructure\.com\fR 42 | . 43 | .SH "SEE ALSO" 44 | Part of \fBblueprint\fR(1)\. 45 | . 46 | .P 47 | \fBblueprint\-template\fR(5) for \fBmustache\.sh\fR template language syntax\. 48 | . 49 | .P 50 | \fBblueprint\-template\fR(7) for built\-in template data\. 51 | -------------------------------------------------------------------------------- /man/man1/blueprint-template.1.ronn: -------------------------------------------------------------------------------- 1 | blueprint-template(1) -- render mustache.sh templates locally 2 | ============================================================= 3 | 4 | ## SYNOPSIS 5 | 6 | `blueprint template` [`-q`] _pathname_ 7 | 8 | ## DESCRIPTION 9 | 10 | `blueprint-template` renders the template for the given _pathname_ using the `mustache.sh` template rendering engine. The template for _pathname_ must be stored in _pathname_`.blueprint-template.mustache`. Data meant only for _pathname_ may be stored in _pathname_`.blueprint-template.sh`, a `source`-able POSIX shell script. 11 | 12 | Data meant for all templates may be stored in files ending in `.sh` in `/etc/blueprint-template.d`, again as `source`-able POSIX shell scripts. Blueprint comes with a selection of suitable data, see `blueprint-template`(7). 13 | 14 | This program is meant for use during development and testing of templates; `blueprint-apply`(1) and `blueprint-show`(1) take care of rendering templates automatically. 15 | 16 | ## OPTIONS 17 | 18 | * `-q`, `--quiet`: 19 | Operate quietly. 20 | * `-h`, `--help`: 21 | Show a help message. 22 | 23 | ## FILES 24 | 25 | * `/etc/blueprint-template.d`: 26 | `source`-able POSIX shell scripts ending in `.sh` that set environment variables for use in templates. 27 | 28 | ## THEME SONG 29 | 30 | The Flaming Lips - "The W.A.N.D. (The Will Always Negates Defeat)" 31 | 32 | ## AUTHOR 33 | 34 | Richard Crowley 35 | 36 | ## SEE ALSO 37 | 38 | Part of `blueprint`(1). 39 | 40 | `blueprint-template`(5) for `mustache.sh` template language syntax. 41 | 42 | `blueprint-template`(7) for built-in template data. 43 | -------------------------------------------------------------------------------- /man/man1/blueprint.1: -------------------------------------------------------------------------------- 1 | .\" generated with Ronn/v0.7.3 2 | .\" http://github.com/rtomayko/ronn/tree/0.7.3 3 | . 4 | .TH "BLUEPRINT" "1" "December 2011" "DevStructure" "Blueprint" 5 | . 6 | .SH "NAME" 7 | \fBblueprint\fR \- reverse engineer server configuration 8 | . 9 | .SH "SYNOPSIS" 10 | \fBblueprint\fR [\fIcommand\fR] [\fIoptions\fR][\fI\|\.\|\.\|\.\fR] 11 | . 12 | .SH "DESCRIPTION" 13 | \fBblueprint\fR programs track packages and system files in Git\. Use stored blueprints to build new environments anytime\. Blueprints can be used to generate Puppet modules, Chef cookbooks, or POSIX shell code\. 14 | . 15 | .TP 16 | \fBblueprint\-list\fR(1) 17 | List all blueprints\. 18 | . 19 | .TP 20 | \fBblueprint\-create\fR(1) 21 | Create a blueprint\. 22 | . 23 | .TP 24 | \fBblueprint\-rules\fR(1) 25 | Create a blueprint from a blueprint\-rules file\. 26 | . 27 | .TP 28 | \fBblueprint\-show\fR(1) 29 | Generate code from a blueprint\. 30 | . 31 | .TP 32 | \fBblueprint\-diff\fR(1) 33 | Save the difference between two blueprints\. 34 | . 35 | .TP 36 | \fBblueprint\-split\fR(1) 37 | Split one blueprint into two others interactively\. 38 | . 39 | .TP 40 | \fBblueprint\-prune\fR(1) 41 | Select a subset of resources interactively\. 42 | . 43 | .TP 44 | \fBblueprint\-template\fR(1) 45 | Render mustache\.sh templates locally\. 46 | . 47 | .TP 48 | \fBblueprint\-apply\fR(1) 49 | Run a blueprint\'s generated shell code\. 50 | . 51 | .TP 52 | \fBblueprint\-push\fR(1) 53 | Push a blueprint to the Internet\. 54 | . 55 | .TP 56 | \fBblueprint\-pull\fR(1) 57 | Pull a blueprint from the Internet\. 58 | . 59 | .TP 60 | \fBblueprint\-destroy\fR(1) 61 | Destroy a blueprint\. 62 | . 63 | .TP 64 | \fBblueprint\fR(5) 65 | Blueprint JSON format\. 66 | . 67 | .TP 68 | \fBblueprintignore\fR(5) 69 | Ignore specific files when creating blueprints\. 70 | . 71 | .TP 72 | \fBblueprint\-rules\fR(5) 73 | Enumerate resources in blueprints\. 74 | . 75 | .TP 76 | \fBblueprint\.cfg\fR(5) 77 | Centralized blueprint service configuration\. 78 | . 79 | .TP 80 | \fBblueprint\-template\fR(5) 81 | \fBmustache\.sh\fR template language syntax\. 82 | . 83 | .TP 84 | \fBblueprint\-template\fR(7) 85 | Built\-in template data\. 86 | . 87 | .TP 88 | \fBblueprint\fR(7) 89 | Blueprint Python library\. 90 | . 91 | .SS "Plumbing" 92 | Much like \fBgit\fR(1) itself, \fBblueprint\fR includes many plumbing tools that make it convenient to inspect blueprints\. 93 | . 94 | .TP 95 | \fBblueprint\-git\fR(1) 96 | Low\-level access to blueprints\. 97 | . 98 | .TP 99 | \fBblueprint\-show\-files\fR(1) 100 | Show files in a blueprint\. 101 | . 102 | .TP 103 | \fBblueprint\-show\-ignore\fR(1) 104 | Show \fBblueprintignore\fR(5) rules from a blueprint\. 105 | . 106 | .TP 107 | \fBblueprint\-show\-packages\fR(1) 108 | Show packages in a blueprint\. 109 | . 110 | .TP 111 | \fBblueprint\-show\-services\fR(1) 112 | Show services in a blueprint\. 113 | . 114 | .TP 115 | \fBblueprint\-show\-sources\fR(1) 116 | Show source tarballs in a blueprint\. 117 | . 118 | .SH "FILES" 119 | . 120 | .TP 121 | \fB~/\.blueprints\.git\fR 122 | The local repsitory where blueprints are stored, each on its own branch\. 123 | . 124 | .TP 125 | \fB/etc/blueprint\.cfg\fR, \fB~/\.blueprint\.cfg\fR 126 | Optional INI\-style configuration files\. See \fBblueprint\.cfg\fR(5)\. 127 | . 128 | .TP 129 | \fB/etc/blueprintignore\fR, \fB~/\.blueprintignore\fR 130 | Lists of filename patterns to be ignored when creating blueprints\. See \fBblueprintignore\fR(5)\. 131 | . 132 | .TP 133 | \fB/etc/blueprint\-template\.d\fR 134 | \fBsource\fR\-able POSIX shell scripts ending in \fB\.sh\fR that set environment variables for use in templates\. 135 | . 136 | .SH "THEME SONG" 137 | The Flaming Lips \- "The W\.A\.N\.D\. (The Will Always Negates Defeat)" 138 | . 139 | .SH "AUTHOR" 140 | Richard Crowley \fIrichard@devstructure\.com\fR 141 | -------------------------------------------------------------------------------- /man/man1/blueprint.1.ronn: -------------------------------------------------------------------------------- 1 | blueprint(1) -- reverse engineer server configuration 2 | ===================================================== 3 | 4 | ## SYNOPSIS 5 | 6 | `blueprint` [_command_] [_options_][_..._] 7 | 8 | ## DESCRIPTION 9 | 10 | `blueprint` programs track packages and system files in Git. Use stored blueprints to build new environments anytime. Blueprints can be used to generate Puppet modules, Chef cookbooks, or POSIX shell code. 11 | 12 | * `blueprint-list`(1): 13 | List all blueprints. 14 | * `blueprint-create`(1): 15 | Create a blueprint. 16 | * `blueprint-rules`(1): 17 | Create a blueprint from a blueprint-rules file. 18 | * `blueprint-show`(1): 19 | Generate code from a blueprint. 20 | * `blueprint-diff`(1): 21 | Save the difference between two blueprints. 22 | * `blueprint-split`(1): 23 | Split one blueprint into two others interactively. 24 | * `blueprint-prune`(1): 25 | Select a subset of resources interactively. 26 | * `blueprint-template`(1): 27 | Render mustache.sh templates locally. 28 | * `blueprint-apply`(1): 29 | Run a blueprint's generated shell code. 30 | * `blueprint-push`(1): 31 | Push a blueprint to the Internet. 32 | * `blueprint-pull`(1): 33 | Pull a blueprint from the Internet. 34 | * `blueprint-destroy`(1): 35 | Destroy a blueprint. 36 | * `blueprint`(5): 37 | Blueprint JSON format. 38 | * `blueprintignore`(5): 39 | Ignore specific files when creating blueprints. 40 | * `blueprint-rules`(5): 41 | Enumerate resources in blueprints. 42 | * `blueprint.cfg`(5): 43 | Centralized blueprint service configuration. 44 | * `blueprint-template`(5): 45 | `mustache.sh` template language syntax. 46 | * `blueprint-template`(7): 47 | Built-in template data. 48 | * `blueprint`(7): 49 | Blueprint Python library. 50 | 51 | ### Plumbing 52 | 53 | Much like `git`(1) itself, `blueprint` includes many plumbing tools that make it convenient to inspect blueprints. 54 | 55 | * `blueprint-git`(1): 56 | Low-level access to blueprints. 57 | * `blueprint-show-files`(1): 58 | Show files in a blueprint. 59 | * `blueprint-show-ignore`(1): 60 | Show `blueprintignore`(5) rules from a blueprint. 61 | * `blueprint-show-packages`(1): 62 | Show packages in a blueprint. 63 | * `blueprint-show-services`(1): 64 | Show services in a blueprint. 65 | * `blueprint-show-sources`(1): 66 | Show source tarballs in a blueprint. 67 | 68 | ## FILES 69 | 70 | * `~/.blueprints.git`: 71 | The local repsitory where blueprints are stored, each on its own branch. 72 | * `/etc/blueprint.cfg`, `~/.blueprint.cfg`: 73 | Optional INI-style configuration files. See `blueprint.cfg`(5). 74 | * `/etc/blueprintignore`, `~/.blueprintignore`: 75 | Lists of filename patterns to be ignored when creating blueprints. See `blueprintignore`(5). 76 | * `/etc/blueprint-template.d`: 77 | `source`-able POSIX shell scripts ending in `.sh` that set environment variables for use in templates. 78 | 79 | ## THEME SONG 80 | 81 | The Flaming Lips - "The W.A.N.D. (The Will Always Negates Defeat)" 82 | 83 | ## AUTHOR 84 | 85 | Richard Crowley 86 | -------------------------------------------------------------------------------- /man/man5/blueprint-rules.5: -------------------------------------------------------------------------------- 1 | .\" generated with Ronn/v0.7.3 2 | .\" http://github.com/rtomayko/ronn/tree/0.7.3 3 | . 4 | .TH "BLUEPRINT\-RULES" "5" "December 2011" "DevStructure" "Blueprint" 5 | . 6 | .SH "NAME" 7 | \fBblueprint\-rules\fR \- enumerate resources in blueprints 8 | . 9 | .SH "SYNOPSIS" 10 | . 11 | .nf 12 | 13 | /etc/ssh 14 | :package:apt/openssh\-server 15 | :service:sysvinit/ssh 16 | . 17 | .fi 18 | . 19 | .SH "DESCRIPTION" 20 | \fBblueprint\-rules\fR files, typically seen with the \fB\.blueprint\-rules\fR extension or with negated meaning as \fB/etc/blueprintignore\fR and \fB~/\.blueprintignore\fR, contain an ordered list of rules that determine whether resources are included in blueprints\. 21 | . 22 | .P 23 | The general form has its origins in \fBgitignore\fR(5): any valid \fBgitignore\fR file is also a valid \fBblueprint\-rules\fR file\. 24 | . 25 | .P 26 | Blank lines and lines that begin with a \fB#\fR will be ignored\. 27 | . 28 | .P 29 | The general form of each line is \fB:\fR\fItype\fR\fB:\fR\fIresource\fR, where \fItype\fR is one of \fBfile\fR, \fBpackage\fR, \fBservice\fR, or \fBsource\fR\. The \fBfile\fR type is assumed when a rule omits the \fB:\fR\fItype\fR\fB:\fR prefix\. 30 | . 31 | .P 32 | The meaning of a rule that begins with a \fB!\fR is negated\. 33 | . 34 | .SS "File resources" 35 | File resources that do not contain a \fB/\fR are matched using \fBfnmatch\fR(3) against the last component of each file considered\. Patterns that contain a \fB/\fR but do not begin with a \fB/\fR are expanded relative to \fB/etc\fR before being matched using \fBglob\fR(3)\. 36 | . 37 | .SS "Package resources" 38 | Package resources are of the form \fB:package:\fR\fImanager\fR\fB/\fR\fIpackage\fR\. Note that the \fImanager\fR is not a colloquial name (for example, "\fBgem\fR") but rather the specific name of the package manager\'s package (in the example, on Debian, "\fBrubygems1\.8\fR")\. 39 | . 40 | .P 41 | A rule that ignores a package implicitly ignores its dependencies as well\. This empirically results in the most compact, yet complete, blueprints\. A rule that includes a package takes no such implicit action\. 42 | . 43 | .SS "Service resources" 44 | Service resources are of the form \fB:service:\fR\fImanager\fR\fB/\fR\fIservice\fR\. Ignoring a service will prevent Blueprint from taking action on its behalf\. 45 | . 46 | .SS "Source resources" 47 | Source resources are of the form \fB:source:\fR\fIpathname\fR format and must be fully\-qualified (that is, begin with a \fB/\fR)\. 48 | . 49 | .SH "EXAMPLES" 50 | . 51 | .SS "ssh\.blueprint\-rules" 52 | \fBssh\.blueprint\-rules\fR enumerates the resources that install and configure an SSH server, while ignoring \fB/etc/ssh/moduli\fR\. 53 | . 54 | .IP "" 4 55 | . 56 | .nf 57 | 58 | /etc/ssh 59 | !/etc/ssh/moduli 60 | :package:apt/openssh\-server 61 | :service:sysvinit/ssh 62 | . 63 | .fi 64 | . 65 | .IP "" 0 66 | . 67 | .SS "/etc/blueprintignore" 68 | \fB/etc/blueprintignore\fR is parsed in a negative context \- it enumerates resources that should be ignored by \fBblueprint\-create\fR(1)\. 69 | . 70 | .IP "" 4 71 | . 72 | .nf 73 | 74 | /etc/apt/sources\.list 75 | /etc/ssl/certs 76 | :package:apt/build\-essential 77 | !:package:apt/build\-essential 78 | . 79 | .fi 80 | . 81 | .IP "" 0 82 | . 83 | .P 84 | This example takes advantage of an emergent behavior: by ignoring and immediately unignoring (with the \fB!\fR rule) the package \fBbuild\-essential\fR, \fBblueprint\-create\fR(1) will include \fBbuild\-essential\fR but not its dependencies, thus creating a more compact blueprint\. 85 | . 86 | .SH "FILES" 87 | . 88 | .TP 89 | \fB/etc/blueprintignore\fR, \fB~/\.blueprintignore\fR 90 | Lists of filename patterns to be ignored when creating blueprints\. 91 | . 92 | .SH "THEME SONG" 93 | The Flaming Lips \- "The W\.A\.N\.D\. (The Will Always Negates Defeat)" 94 | . 95 | .SH "AUTHOR" 96 | Richard Crowley \fIrichard@devstructure\.com\fR 97 | . 98 | .SH "SEE ALSO" 99 | Part of \fBblueprint\fR(1)\. 100 | -------------------------------------------------------------------------------- /man/man5/blueprint-rules.5.ronn: -------------------------------------------------------------------------------- 1 | blueprint-rules(5) -- enumerate resources in blueprints 2 | ======================================================= 3 | 4 | ## SYNOPSIS 5 | 6 | /etc/ssh 7 | :package:apt/openssh-server 8 | :service:sysvinit/ssh 9 | 10 | ## DESCRIPTION 11 | 12 | `blueprint-rules` files, typically seen with the `.blueprint-rules` extension or with negated meaning as `/etc/blueprintignore` and `~/.blueprintignore`, contain an ordered list of rules that determine whether resources are included in blueprints. 13 | 14 | The general form has its origins in `gitignore`(5): any valid `gitignore` file is also a valid `blueprint-rules` file. 15 | 16 | Blank lines and lines that begin with a `#` will be ignored. 17 | 18 | The general form of each line is `:`_type_`:`_resource_, where _type_ is one of `file`, `package`, `service`, or `source`. The `file` type is assumed when a rule omits the `:`_type_`:` prefix. 19 | 20 | The meaning of a rule that begins with a `!` is negated. 21 | 22 | ### File resources 23 | 24 | File resources that do not contain a `/` are matched using `fnmatch`(3) against the last component of each file considered. Patterns that contain a `/` but do not begin with a `/` are expanded relative to `/etc` before being matched using `glob`(3). 25 | 26 | ### Package resources 27 | 28 | Package resources are of the form `:package:`_manager_`/`_package_. Note that the _manager_ is not a colloquial name (for example, "`gem`") but rather the specific name of the package manager's package (in the example, on Debian, "`rubygems1.8`"). 29 | 30 | A rule that ignores a package implicitly ignores its dependencies as well. This empirically results in the most compact, yet complete, blueprints. A rule that includes a package takes no such implicit action. 31 | 32 | ### Service resources 33 | 34 | Service resources are of the form `:service:`_manager_`/`_service_. Ignoring a service will prevent Blueprint from taking action on its behalf. 35 | 36 | ### Source resources 37 | 38 | Source resources are of the form `:source:`_pathname_ format and must be fully-qualified (that is, begin with a `/`). 39 | 40 | ## EXAMPLES 41 | 42 | ### ssh.blueprint-rules 43 | 44 | `ssh.blueprint-rules` enumerates the resources that install and configure an SSH server, while ignoring `/etc/ssh/moduli`. 45 | 46 | /etc/ssh 47 | !/etc/ssh/moduli 48 | :package:apt/openssh-server 49 | :service:sysvinit/ssh 50 | 51 | ### /etc/blueprintignore 52 | 53 | `/etc/blueprintignore` is parsed in a negative context - it enumerates resources that should be ignored by `blueprint-create`(1). 54 | 55 | /etc/apt/sources.list 56 | /etc/ssl/certs 57 | :package:apt/build-essential 58 | !:package:apt/build-essential 59 | 60 | This example takes advantage of an emergent behavior: by ignoring and immediately unignoring (with the `!` rule) the package `build-essential`, `blueprint-create`(1) will include `build-essential` but not its dependencies, thus creating a more compact blueprint. 61 | 62 | ## FILES 63 | 64 | * `/etc/blueprintignore`, `~/.blueprintignore`: 65 | Lists of filename patterns to be ignored when creating blueprints. 66 | 67 | ## THEME SONG 68 | 69 | The Flaming Lips - "The W.A.N.D. (The Will Always Negates Defeat)" 70 | 71 | ## AUTHOR 72 | 73 | Richard Crowley 74 | 75 | ## SEE ALSO 76 | 77 | Part of `blueprint`(1). 78 | -------------------------------------------------------------------------------- /man/man5/blueprint-template.5: -------------------------------------------------------------------------------- 1 | .\" generated with Ronn/v0.7.3 2 | .\" http://github.com/rtomayko/ronn/tree/0.7.3 3 | . 4 | .TH "BLUEPRINT\-TEMPLATE" "5" "December 2011" "DevStructure" "Blueprint" 5 | . 6 | .SH "NAME" 7 | \fBblueprint\-template\fR \- mustache\.sh template language syntax 8 | . 9 | .SH "SYNOPSIS" 10 | . 11 | .nf 12 | 13 | {{VARIABLE}} 14 | {{#VARIABLE}}\|\.\|\.\|\.{{/VARIABLE}} 15 | {{^VARIABLE}}\|\.\|\.\|\.{{/VARIABLE}} 16 | {{!COMMENT}} 17 | {{`COMMAND`}} 18 | {{#`COMMAND`}}\|\.\|\.\|\.{{/`COMMAND`}} 19 | . 20 | .fi 21 | . 22 | .SH "DESCRIPTION" 23 | Templates are rendered by \fBblueprint\-template\fR(1) and \fBblueprint\-apply\fR(1), and by POSIX shell code generated by \fBblueprint\-show\fR(1)\. They use the \fBmustache\.sh\fR template rendering engine\. 24 | . 25 | .P 26 | Templates may make use of the following syntax: 27 | . 28 | .IP "" 4 29 | . 30 | .nf 31 | 32 | {{VARIABLE}} 33 | . 34 | .fi 35 | . 36 | .IP "" 0 37 | . 38 | .P 39 | \fBVARIABLE\fR is read from the environment and substituted directly\. 40 | . 41 | .IP "" 4 42 | . 43 | .nf 44 | 45 | {{#VARIABLE}}\|\.\|\.\|\.{{/VARIABLE}} 46 | . 47 | .fi 48 | . 49 | .IP "" 0 50 | . 51 | .P 52 | The intervening \fB\|\.\|\.\|\.\fR is substituted for the entire tag if \fBVARIABLE\fR is in the environmente and is non\-empty\. Other tags may be nested within\. 53 | . 54 | .IP "" 4 55 | . 56 | .nf 57 | 58 | {{^VARIABLE}}\|\.\|\.\|\.{{/VARIABLE}} 59 | . 60 | .fi 61 | . 62 | .IP "" 0 63 | . 64 | .P 65 | The opposite of \fB#\fR sections: the intervening \fB\|\.\|\.\|\.\fR is substituted for the entire tag if \fBVARIABLE\fR is not in the environmente or is empty\. Other tags may be nested within\. 66 | . 67 | .IP "" 4 68 | . 69 | .nf 70 | 71 | {{!COMMENT}} 72 | . 73 | .fi 74 | . 75 | .IP "" 0 76 | . 77 | .P 78 | The tag is removed unconditionally\. Comments do not appear in the rendered output\. 79 | . 80 | .IP "" 4 81 | . 82 | .nf 83 | 84 | {{`COMMAND`}} 85 | . 86 | .fi 87 | . 88 | .IP "" 0 89 | . 90 | .P 91 | \fBCOMMAND\fR is executed and anything written to standard output is substituted directly\. 92 | . 93 | .IP "" 4 94 | . 95 | .nf 96 | 97 | {{#`COMMAND`}}\|\.\|\.\|\.{{/`COMMAND`}} 98 | . 99 | .fi 100 | . 101 | .IP "" 0 102 | . 103 | .P 104 | \fBCOMMAND\fR is executed and \fB\|\.\|\.\|\.\fR is rendered once for each line written to standard output\. The environment is unchanged from line\-to\-line except for the value of \fB_M_LINE\fR, which contains each successive line of standard output\. 105 | . 106 | .SH "THEME SONG" 107 | The Flaming Lips \- "The W\.A\.N\.D\. (The Will Always Negates Defeat)" 108 | . 109 | .SH "AUTHOR" 110 | Richard Crowley \fIrichard@devstructure\.com\fR 111 | . 112 | .SH "SEE ALSO" 113 | Part of \fBblueprint\fR(1)\. 114 | . 115 | .P 116 | \fBmustache\fR(5) at \fIhttp://mustache\.github\.com/mustache\.5\.html\fR and \fBmustache\.sh\fR at \fIhttps://github\.com/rcrowley/mustache\.sh\fR\. 117 | -------------------------------------------------------------------------------- /man/man5/blueprint-template.5.ronn: -------------------------------------------------------------------------------- 1 | blueprint-template(5) -- mustache.sh template language syntax 2 | ============================================================= 3 | 4 | ## SYNOPSIS 5 | 6 | {{VARIABLE}} 7 | {{#VARIABLE}}...{{/VARIABLE}} 8 | {{^VARIABLE}}...{{/VARIABLE}} 9 | {{!COMMENT}} 10 | {{`COMMAND`}} 11 | {{#`COMMAND`}}...{{/`COMMAND`}} 12 | 13 | ## DESCRIPTION 14 | 15 | Templates are rendered by `blueprint-template`(1) and `blueprint-apply`(1), and by POSIX shell code generated by `blueprint-show`(1). They use the `mustache.sh` template rendering engine. 16 | 17 | Templates may make use of the following syntax: 18 | 19 | {{VARIABLE}} 20 | 21 | `VARIABLE` is read from the environment and substituted directly. 22 | 23 | {{#VARIABLE}}...{{/VARIABLE}} 24 | 25 | The intervening `...` is substituted for the entire tag if `VARIABLE` is in the environmente and is non-empty. Other tags may be nested within. 26 | 27 | {{^VARIABLE}}...{{/VARIABLE}} 28 | 29 | The opposite of `#` sections: the intervening `...` is substituted for the entire tag if `VARIABLE` is not in the environmente or is empty. Other tags may be nested within. 30 | 31 | {{!COMMENT}} 32 | 33 | The tag is removed unconditionally. Comments do not appear in the rendered output. 34 | 35 | {{`COMMAND`}} 36 | 37 | `COMMAND` is executed and anything written to standard output is substituted directly. 38 | 39 | {{#`COMMAND`}}...{{/`COMMAND`}} 40 | 41 | `COMMAND` is executed and `...` is rendered once for each line written to standard output. The environment is unchanged from line-to-line except for the value of `_M_LINE`, which contains each successive line of standard output. 42 | 43 | ## THEME SONG 44 | 45 | The Flaming Lips - "The W.A.N.D. (The Will Always Negates Defeat)" 46 | 47 | ## AUTHOR 48 | 49 | Richard Crowley 50 | 51 | ## SEE ALSO 52 | 53 | Part of `blueprint`(1). 54 | 55 | `mustache`(5) at and `mustache.sh` at . 56 | -------------------------------------------------------------------------------- /man/man5/blueprint.5.ronn: -------------------------------------------------------------------------------- 1 | blueprint(5) -- blueprint JSON format 2 | ===================================== 3 | 4 | ## SYNOPSIS 5 | 6 | { 7 | "arch": "ARCHITECTURE", 8 | "files": { 9 | "PATHNAME": { 10 | "content": "CONTENT", 11 | "encoding": "ENCODING", 12 | "group": "GROUP", 13 | "mode": "MODE", 14 | "owner": "OWNER" 15 | }, 16 | "PATHNAME": { 17 | "encoding": "ENCODING", 18 | "group": "GROUP", 19 | "mode": "MODE", 20 | "owner": "OWNER", 21 | "template": "TEMPLATE" 22 | }, 23 | "PATHNAME": { 24 | "dat": "DATA" 25 | "encoding": "ENCODING", 26 | "group": "GROUP", 27 | "mode": "MODE", 28 | "owner": "OWNER", 29 | "template": "TEMPLATE" 30 | }, 31 | "PATHNAME": { 32 | "encoding": "ENCODING", 33 | "group": "GROUP", 34 | "mode": "MODE", 35 | "owner": "OWNER", 36 | "source": "SOURCE" 37 | } 38 | }, 39 | "packages": { 40 | "MANAGER": { 41 | "PACKAGE": ["VERSION"], 42 | "PACKAGE": ["VERSION", "VERSION"] 43 | }, 44 | "MANAGER": { 45 | "PACKAGE": ["VERSION"], 46 | "PACKAGE": ["VERSION"] 47 | }, 48 | "rpm": { 49 | "PACKAGE": ["URL"] 50 | } 51 | }, 52 | "services": { 53 | "MANAGER": { 54 | "SERVICENAME": { 55 | "enable": true, 56 | "ensure": "running", 57 | "files": ["PATHNAME"], 58 | "packages": { 59 | "MANAGER": ["PACKAGE"] 60 | }, 61 | "sources": ["DIRNAME"] 62 | } 63 | } 64 | }, 65 | "sources": { 66 | "DIRNAME": "FILENAME", 67 | "DIRNAME": "URL" 68 | } 69 | } 70 | 71 | (Keys and values written in CAPITALS represent variable data.) 72 | 73 | ## DESCRIPTION 74 | 75 | `blueprint-create`(1) commits `blueprint.json` to the appropriate branch in the local blueprint repository. The format described here is used to generate Puppet modules, Chef cookbooks, and POSIX shell scripts in `blueprint-show`(1) and `blueprint-apply`(1). These sections must be followed in order. 76 | 77 | ### Sources 78 | 79 | Each key in the optional `sources` object is the fully-qualified path to a directory. These directory names should be traversed in alphabetical order. The associated value is the name of a tarball of the contents of that directory at the time the blueprint was created. It must be extracted there when the blueprint is applied. The tarball is stored in Git alongside `blueprint.json`. 80 | 81 | If `sources` is present and non-empty, `arch` will also be present indicating the architecture of the server that created the blueprint. If present, this value will be _amd64_ or _i386_ on Debian-based systems or _x86_64_ or _x86_ on RPM-based systems. It is legal to refuse to apply a blueprint with a mismatched architecture. The architecture can be found by running `dpkg --print-architecture` or `rpm --eval %_arch` as appropriate. 82 | 83 | For compatibility with AWS `cfn-init`, a URL may appear in the value in place of a filename. If such a value is encountered, the URL should be fetched and extracted. Blueprint will never generate such objects. 84 | 85 | ### Files 86 | 87 | Each key in the optional `files` object is the fully-qualified pathname to a file. These pathnames should be traversed in alphabetical order. The associated value contains the `owner`, owning `group`, `mode` (a string containing the full 6-digit octal representation), `content`, and the `encoding` of content (one of _plain_ or _base64_). Each file must be placed at its pathname and its metadata must be updated when the blueprint is applied. 88 | 89 | The file's content may alternately be specified as `template` and (optionally) `data` which contain a `mustache.sh` template in the `mustache`(5) format and POSIX shell code, respectively. When a blueprint is applied, the template should be given as standard input to `mustache.sh` with this data and other default data available in the environment. The resulting standard output should be taken as the file's content. A copy of `mustache.sh` is distributed with Blueprint. 90 | 91 | For compatibility with AWS `cfn-init`, `source` takes precedence over `content`. If a file with a `source` is encountered, the `source` URL should be fetched as the file's content. Blueprint will never generate such objects. 92 | 93 | ### Packages 94 | 95 | Each key within `packages` names a package manager. Each manager contains keys that name packages to be installed by that manager. Each package name is associated with an array of versions that must be installed. In most cases, for most managers, this array will have only one element. 96 | 97 | Each manager is itself the name of a package that appears elsewhere. `packages` must be processed by starting with the `apt`, `rpm`, and `yum` managers, installing their dependencies. Any manager that was installed during this pass must next have its dependencies installed, continuing recursively until all managers and all dependencies have been installed. 98 | 99 | For compatibility with AWS `cfn-init`, the `rpm` manager is included. Packages managed by `rpm` specify URLs in place of version numbers. If such a package is encountered, the URL should be installed via `rpm`(1). Blueprint will never generate such objects. 100 | 101 | ### Services 102 | 103 | Each key within `services` names a service manager. Each manager contains keys that name services on the system. Each service name is associated with a set of dependencies which themselves take a form much like a blueprint: `files` may be present and point to a list of pathnames; `packages` may be present and point to a set of package managers which each point to a list of packages; `sources` may be present and point to a list of directory names. 104 | 105 | If any of the resources on which the service depends are changed, upgraded, initailized, or otherwise acted upon, the service should be restarted. 106 | 107 | For compatibility with AWS `cfn-init`, `"enable": true` and `"ensure": "running"` are included with each service. Blueprint ignores these when generating code. 108 | 109 | ## THEME SONG 110 | 111 | The Flaming Lips - "The W.A.N.D. (The Will Always Negates Defeat)" 112 | 113 | ## AUTHOR 114 | 115 | Richard Crowley 116 | 117 | ## SEE ALSO 118 | 119 | Part of `blueprint`(1). 120 | 121 | `mustache`(5) at and `mustache.sh` at . 122 | -------------------------------------------------------------------------------- /man/man5/blueprint.cfg.5: -------------------------------------------------------------------------------- 1 | .\" generated with Ronn/v0.7.3 2 | .\" http://github.com/rtomayko/ronn/tree/0.7.3 3 | . 4 | .TH "BLUEPRINT\.CFG" "5" "December 2011" "DevStructure" "Blueprint" 5 | . 6 | .SH "NAME" 7 | \fBblueprint\.cfg\fR \- centralized blueprint service configuration 8 | . 9 | .SH "SYNOPSIS" 10 | . 11 | .nf 12 | 13 | [io] 14 | server = https://devstructure\.com 15 | secret = 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz_\- 16 | . 17 | .fi 18 | . 19 | .SH "DESCRIPTION" 20 | \fB/etc/blueprint\.cfg\fR or \fB~/\.blueprint\.cfg\fR allow customizing the configuration of Blueprint and Blueprint I/O\. Blueprint I/O will prompt you with snippets that can be used in these files when you perform pushes and pulls for the first time\. 21 | . 22 | .P 23 | The file is INI\-style and divided into sections, of which there is only one, currently\. 24 | . 25 | .SS "[io]" 26 | . 27 | .TP 28 | \fBmax_content_length\fR 29 | The maximal \fBContent\-Length\fR to allow on the server\. Defaults to 64 MB\. 30 | . 31 | .TP 32 | \fBsecret\fR 33 | The 64\-byte secret key used for all \fBblueprint\-push\fR(1) calls and \fBblueprint\-pull\fR(1) calls that don\'t specify a full URL\. 34 | . 35 | .TP 36 | \fBserver\fR 37 | The Blueprint I/O Server that receives push and pull calls\. \fBhttps://devstructure\.com\fR by default\. 38 | . 39 | .SS "[s3]" 40 | . 41 | .TP 42 | \fBaccess_key\fR 43 | An AWS access key\. 44 | . 45 | .TP 46 | \fBbucket\fR 47 | The name of an existing bucket to be used for blueprints\. 48 | . 49 | .TP 50 | \fBregion\fR 51 | The region that contains the \fBbucket\fR\. \fBUS\fR by default\. 52 | . 53 | .TP 54 | \fBsecret_key\fR 55 | An AWS secret key that matches the \fBaccess_key\fR\. 56 | . 57 | .SS "[librato]" 58 | . 59 | .TP 60 | \fBtoken\fR 61 | A Librato API token\. 62 | . 63 | .TP 64 | \fBusername\fR 65 | A Librato API username (which is typically an email address)\. 66 | . 67 | .SS "[statsd]" 68 | . 69 | .TP 70 | \fBhost\fR 71 | The hostname or IP address of a StatsD instance\. 72 | . 73 | .TP 74 | \fBport\fR 75 | The port of a StatsD instance\. Defaults to \fB8125\fR\. 76 | . 77 | .SH "FILES" 78 | . 79 | .TP 80 | \fB/etc/blueprint\.cfg\fR, \fB~/\.blueprint\.cfg\fR 81 | Optional INI\-style configuration files\. 82 | . 83 | .SH "THEME SONG" 84 | Girl Talk \- "Let It Out" 85 | . 86 | .SH "AUTHOR" 87 | . 88 | .IP "\(bu" 4 89 | Matt Tanase \fImatt@devstructure\.com\fR 90 | . 91 | .IP "\(bu" 4 92 | Richard Crowley \fIrichard@devstructure\.com\fR 93 | . 94 | .IP "" 0 95 | . 96 | .SH "SEE ALSO" 97 | Part of \fBblueprint\fR(1)\. 98 | -------------------------------------------------------------------------------- /man/man5/blueprint.cfg.5.ronn: -------------------------------------------------------------------------------- 1 | blueprint.cfg(5) -- centralized blueprint service configuration 2 | =============================================================== 3 | 4 | ## SYNOPSIS 5 | 6 | [io] 7 | server = https://devstructure.com 8 | secret = 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz_- 9 | 10 | ## DESCRIPTION 11 | 12 | `/etc/blueprint.cfg` or `~/.blueprint.cfg` allow customizing the configuration of Blueprint and Blueprint I/O. Blueprint I/O will prompt you with snippets that can be used in these files when you perform pushes and pulls for the first time. 13 | 14 | The file is INI-style and divided into sections, of which there is only one, currently. 15 | 16 | ### [io] 17 | 18 | * `max_content_length`: 19 | The maximal `Content-Length` to allow on the server. Defaults to 64 MB. 20 | * `secret`: 21 | The 64-byte secret key used for all `blueprint-push`(1) calls and `blueprint-pull`(1) calls that don't specify a full URL. 22 | * `server`: 23 | The Blueprint I/O Server that receives push and pull calls. `https://devstructure.com` by default. 24 | 25 | ### [s3] 26 | 27 | * `access_key`: 28 | An AWS access key. 29 | * `bucket`: 30 | The name of an existing bucket to be used for blueprints. 31 | * `region`: 32 | The region that contains the `bucket`. `US` by default. 33 | * `secret_key`: 34 | An AWS secret key that matches the `access_key`. 35 | 36 | ### [librato] 37 | 38 | * `token`: 39 | A Librato API token. 40 | * `username`: 41 | A Librato API username (which is typically an email address). 42 | 43 | ### [statsd] 44 | 45 | * `host`: 46 | The hostname or IP address of a StatsD instance. 47 | * `port`: 48 | The port of a StatsD instance. Defaults to `8125`. 49 | 50 | ## FILES 51 | 52 | * `/etc/blueprint.cfg`, `~/.blueprint.cfg`: 53 | Optional INI-style configuration files. 54 | 55 | ## THEME SONG 56 | 57 | Girl Talk - "Let It Out" 58 | 59 | ## AUTHOR 60 | 61 | * Matt Tanase 62 | * Richard Crowley 63 | 64 | ## SEE ALSO 65 | 66 | Part of `blueprint`(1). 67 | -------------------------------------------------------------------------------- /man/man7/blueprint-template.7: -------------------------------------------------------------------------------- 1 | .\" generated with Ronn/v0.7.3 2 | .\" http://github.com/rtomayko/ronn/tree/0.7.3 3 | . 4 | .TH "BLUEPRINT\-TEMPLATE" "7" "December 2011" "DevStructure" "Blueprint" 5 | . 6 | .SH "NAME" 7 | \fBblueprint\-template\fR \- built\-in template data 8 | . 9 | .SH "SYNOPSIS" 10 | . 11 | .nf 12 | 13 | {{FQDN}} 14 | . 15 | .fi 16 | . 17 | .SH "DESCRIPTION" 18 | Blueprint provides basic data about the local system for use in rendering templates of files\. 19 | . 20 | .SS "uname(1) properties" 21 | . 22 | .TP 23 | \fBARCH\fR 24 | The system\'s hardware architecture\. Probably "i386" or "x86_64"\. 25 | . 26 | .TP 27 | \fBKERNEL\fR 28 | The kernel name\. This is always "Linux" on systems Blueprint supports\. 29 | . 30 | .TP 31 | \fBKERNEL_RELEASE\fR 32 | The kernel release\. 33 | . 34 | .SS "System properties from /proc" 35 | . 36 | .TP 37 | \fBCORES\fR 38 | The number of cores in the system\. The semantics of this value when read in a virtualized environment are undefined\. 39 | . 40 | .TP 41 | \fBMEM\fR 42 | The total amount of memory in the system in bytes\. 43 | . 44 | .SS "Network properties" 45 | . 46 | .TP 47 | \fBDNSDOMAINNAME\fR 48 | The DNS domain name according to \fBdnsdomainname\fR(1)\. 49 | . 50 | .TP 51 | \fBEACH_IP\fR 52 | Each IPv4 address assigned to an interface, one per line\. 53 | . 54 | .TP 55 | \fBEACH_IPv6\fR 56 | Each IPv6 address assigned to an interface, one per line\. 57 | . 58 | .TP 59 | \fBFQDN\fR 60 | The fully\-qualified domain name according to \fBhostname\fR(1)\'s \fB\-\-fqdn\fR option\. 61 | . 62 | .TP 63 | \fBHOSTNAME\fR 64 | The hostname according to \fBhostname\fR(1)\. 65 | . 66 | .TP 67 | \fBIPv6\fR 68 | The first IPv6 address assigned to any interface\. 69 | . 70 | .TP 71 | \fBPRIVATE_IP\fR 72 | The first private IPv4 address assigned to any interface\. 73 | . 74 | .TP 75 | \fBPUBLIC_IP\fR 76 | The first public IPv4 address assigned to any interface\. 77 | . 78 | .SS "LSB properties" 79 | . 80 | .TP 81 | \fBDISTRO\fR 82 | The distro name\. 83 | . 84 | .TP 85 | \fBRELEASE\fR 86 | The operating system release codename\. 87 | . 88 | .SH "THEME SONG" 89 | The Flaming Lips \- "The W\.A\.N\.D\. (The Will Always Negates Defeat)" 90 | . 91 | .SH "AUTHOR" 92 | Richard Crowley \fIrichard@devstructure\.com\fR 93 | . 94 | .SH "SEE ALSO" 95 | Part of \fBblueprint\fR(1)\. 96 | . 97 | .P 98 | \fBmustache\fR(5) at \fIhttp://mustache\.github\.com/mustache\.5\.html\fR and \fBmustache\.sh\fR at \fIhttps://github\.com/rcrowley/mustache\.sh\fR\. 99 | -------------------------------------------------------------------------------- /man/man7/blueprint-template.7.ronn: -------------------------------------------------------------------------------- 1 | blueprint-template(7) -- built-in template data 2 | =============================================== 3 | 4 | ## SYNOPSIS 5 | 6 | {{FQDN}} 7 | 8 | ## DESCRIPTION 9 | 10 | Blueprint provides basic data about the local system for use in rendering templates of files. 11 | 12 | ### uname(1) properties 13 | 14 | * `ARCH`: 15 | The system's hardware architecture. Probably "i386" or "x86_64". 16 | * `KERNEL`: 17 | The kernel name. This is always "Linux" on systems Blueprint supports. 18 | * `KERNEL_RELEASE`: 19 | The kernel release. 20 | 21 | ### System properties from `/proc` 22 | 23 | * `CORES`: 24 | The number of cores in the system. The semantics of this value when read in a virtualized environment are undefined. 25 | * `MEM`: 26 | The total amount of memory in the system in bytes. 27 | 28 | ### Network properties 29 | 30 | * `DNSDOMAINNAME`: 31 | The DNS domain name according to `dnsdomainname`(1). 32 | * `EACH_IP`: 33 | Each IPv4 address assigned to an interface, one per line. 34 | * `EACH_IPv6`: 35 | Each IPv6 address assigned to an interface, one per line. 36 | * `FQDN`: 37 | The fully-qualified domain name according to `hostname`(1)'s `--fqdn` option. 38 | * `HOSTNAME`: 39 | The hostname according to `hostname`(1). 40 | * `IPv6`: 41 | The first IPv6 address assigned to any interface. 42 | * `PRIVATE_IP`: 43 | The first private IPv4 address assigned to any interface. 44 | * `PUBLIC_IP`: 45 | The first public IPv4 address assigned to any interface. 46 | 47 | ### LSB properties 48 | 49 | * `DISTRO`: 50 | The distro name. 51 | * `RELEASE`: 52 | The operating system release codename. 53 | 54 | ## THEME SONG 55 | 56 | The Flaming Lips - "The W.A.N.D. (The Will Always Negates Defeat)" 57 | 58 | ## AUTHOR 59 | 60 | Richard Crowley 61 | 62 | ## SEE ALSO 63 | 64 | Part of `blueprint`(1). 65 | 66 | `mustache`(5) at and `mustache.sh` at . 67 | -------------------------------------------------------------------------------- /pydir.py: -------------------------------------------------------------------------------- 1 | from distutils.sysconfig import get_python_version, get_python_lib 2 | import os.path 3 | import sys 4 | for s in ('dist', 'site'): 5 | pydir = os.path.join(sys.argv[1], 6 | 'python%s' % get_python_version(), 7 | '%s-packages' % s) 8 | if pydir in sys.path: 9 | print(pydir) 10 | sys.exit(0) 11 | print(get_python_lib()) 12 | -------------------------------------------------------------------------------- /setup.py.mustache: -------------------------------------------------------------------------------- 1 | from setuptools import setup, find_packages 2 | 3 | setup(name='blueprint', 4 | version='{{VERSION}}', 5 | description='reverse engineer server configuration', 6 | author='Richard Crowley', 7 | author_email='richard@devstructure.com', 8 | url='http://devstructure.com/', 9 | packages=find_packages(), 10 | scripts=['bin/blueprint', 11 | 'bin/blueprint-apply', 12 | 'bin/blueprint-create', 13 | 'bin/blueprint-destroy', 14 | 'bin/blueprint-diff', 15 | 'bin/blueprint-git', 16 | 'bin/blueprint-list', 17 | 'bin/blueprint-prune', 18 | 'bin/blueprint-pull', 19 | 'bin/blueprint-push', 20 | 'bin/blueprint-rules', 21 | 'bin/blueprint-show', 22 | 'bin/blueprint-show-files', 23 | 'bin/blueprint-show-ignore', 24 | 'bin/blueprint-show-packages', 25 | 'bin/blueprint-show-services', 26 | 'bin/blueprint-show-sources', 27 | 'bin/blueprint-split', 28 | 'bin/blueprint-template'], 29 | license='BSD', 30 | zip_safe=False) 31 | -------------------------------------------------------------------------------- /tests.py: -------------------------------------------------------------------------------- 1 | from flask.testing import FlaskClient 2 | import json 3 | import os.path 4 | import sys 5 | 6 | from blueprint.io.server import app 7 | 8 | SECRET = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz_-' 9 | NAME = 'test' 10 | SHA = 'adff242fbc01ba3753abf8c3f9b45eeedec23ec6' 11 | 12 | filename = '{0}.tar'.format(SHA) 13 | pathname = os.path.join(os.path.dirname(__file__), 'tests', filename) 14 | 15 | c = app.test_client() 16 | 17 | def test_GET_secret(): 18 | response = c.get('/secret') 19 | assert 201 == response.status_code 20 | assert 64 + 1 == len(response.data) 21 | 22 | def test_PUT_blueprint_invalid_secret(): 23 | response = c.put('/{0}/{1}'.format('invalid', NAME), 24 | content_type='application/json', 25 | data=json.dumps({})) 26 | assert 400 == response.status_code 27 | 28 | def test_PUT_blueprint_invalid_name(): 29 | response = c.put('/{0}/{1}'.format(SECRET, '%20'), 30 | content_type='application/json', 31 | data=json.dumps({})) 32 | assert 400 == response.status_code 33 | 34 | def test_PUT_blueprint_invalid_syntax_data(): 35 | response = c.put('/{0}/{1}'.format(SECRET, NAME), 36 | content_type='application/json', 37 | data='}{') 38 | assert 400 == response.status_code 39 | 40 | def test_PUT_blueprint_invalid_schema_data(): 41 | response = c.put('/{0}/{1}'.format(SECRET, NAME), 42 | content_type='application/json', 43 | data=json.dumps({'invalid': 'invalid'})) 44 | assert 400 == response.status_code 45 | 46 | def test_PUT_blueprint_invalid_length_data(): 47 | response = c.put('/{0}/{1}'.format(SECRET, NAME), 48 | content_type='application/json', 49 | data=json.dumps({ 50 | 'files': { 51 | '/etc/long': '.' * 65 * 1024 * 1024, 52 | }, 53 | })) 54 | assert 413 == response.status_code 55 | 56 | def test_PUT_blueprint_empty(): 57 | response = c.put('/{0}/{1}'.format(SECRET, NAME), 58 | content_type='application/json', 59 | data=json.dumps({})) 60 | assert 202 == response.status_code 61 | 62 | def test_PUT_tarball_empty(): 63 | test_PUT_blueprint_empty() 64 | response = c.put('/{0}/{1}/{2}.tar'.format(SECRET, NAME, SHA), 65 | content_type='application/x-tar', 66 | data=open(pathname).read()) 67 | assert 400 == response.status_code 68 | 69 | def test_PUT_blueprint_sources(): 70 | response = c.put('/{0}/{1}'.format(SECRET, NAME), 71 | content_type='application/json', 72 | data=json.dumps({ 73 | 'sources': { 74 | '/usr/local': filename, 75 | }, 76 | })) 77 | assert 202 == response.status_code 78 | 79 | def test_PUT_tarball_invalid_sha(): 80 | test_PUT_blueprint_sources() 81 | response = c.put('/{0}/{1}/{2}.tar'.format(SECRET, NAME, 'invalid'), 82 | content_type='application/x-tar', 83 | data=open(pathname).read()) 84 | assert 400 == response.status_code 85 | 86 | def test_PUT_tarball_invalid_data(): 87 | test_PUT_blueprint_sources() 88 | response = c.put('/{0}/{1}/{2}.tar'.format(SECRET, NAME, '0' * 40), 89 | content_type='application/x-tar', 90 | data=open(pathname).read()) 91 | assert 400 == response.status_code 92 | 93 | def test_PUT_tarball_invalid_length_data(): 94 | test_PUT_blueprint_sources() 95 | response = c.put('/{0}/{1}/{2}.tar'.format(SECRET, NAME, '0' * 40), 96 | content_type='application/x-tar', 97 | data='.' * 65 * 1024 * 1024) 98 | assert 413 == response.status_code 99 | 100 | def test_PUT_tarball(): 101 | test_PUT_blueprint_sources() 102 | response = c.put('/{0}/{1}/{2}.tar'.format(SECRET, NAME, SHA), 103 | content_type='application/x-tar', 104 | data=open(pathname).read()) 105 | assert 202 == response.status_code 106 | 107 | def test_GET_blueprint_invalid(): 108 | test_PUT_blueprint_empty() 109 | response = c.get('/{0}/{1}'.format(SECRET, 'four-oh-four')) 110 | assert 404 == response.status_code 111 | 112 | def test_GET_blueprint(): 113 | test_PUT_blueprint_empty() 114 | response = c.get('/{0}/{1}'.format(SECRET, NAME)) 115 | assert 301 == response.status_code 116 | 117 | def test_GET_blueprint_sh_invalid(): 118 | test_PUT_blueprint_empty() 119 | response = c.get('/{0}/{1}/{1}.sh'.format(SECRET, 'four-oh-four')) 120 | assert 404 == response.status_code 121 | 122 | def test_GET_blueprint_sh_mismatch(): 123 | test_PUT_blueprint_empty() 124 | response = c.get('/{0}/{1}/{2}.sh'.format(SECRET, 'four-oh-four', 'wrong')) 125 | assert 400 == response.status_code 126 | 127 | def test_GET_blueprint_sh(): 128 | test_PUT_blueprint_empty() 129 | response = c.get('/{0}/{1}/{1}.sh'.format(SECRET, NAME)) 130 | assert 200 == response.status_code 131 | assert '#!' == response.data[0:2] 132 | 133 | def test_GET_blueprint_userdata_invalid(): 134 | response = c.get('/{0}/{1}/user-data.sh'.format(SECRET, 'four-oh-four')) 135 | assert 404 == response.status_code 136 | 137 | def test_GET_blueprint_userdata(): 138 | test_PUT_blueprint_empty() 139 | response = c.get('/{0}/{1}/user-data.sh'.format(SECRET, NAME)) 140 | assert 200 == response.status_code 141 | assert '#!' == response.data[0:2] 142 | 143 | def test_GET_tarball_invalid(): 144 | test_PUT_blueprint_empty() 145 | response = c.get('/{0}/{1}/{2}.tar'.format(SECRET, NAME, '0' * 40)) 146 | assert 404 == response.status_code 147 | 148 | def test_GET_tarball(): 149 | test_PUT_tarball() 150 | response = c.get('/{0}/{1}/{2}.tar'.format(SECRET, NAME, SHA)) 151 | assert 301 == response.status_code 152 | -------------------------------------------------------------------------------- /tests.sh: -------------------------------------------------------------------------------- 1 | BASE="http://127.0.0.1:5000" 2 | CURLARGS="-sv" 3 | 4 | SECRET="0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz_-" 5 | NAME="test" 6 | SHA="adff242fbc01ba3753abf8c3f9b45eeedec23ec6" 7 | 8 | set -ex 9 | 10 | cd "$(dirname "$0")" 11 | 12 | curl $CURLARGS -X GET "$BASE/secret" 13 | 14 | curl $CURLARGS -X PUT \ 15 | -H "Content-Type: application/json" \ 16 | -T "tests/empty.json" "$BASE/invalid/$NAME" 17 | 18 | curl $CURLARGS -X PUT \ 19 | -H "Content-Type: application/json" \ 20 | -T "tests/empty.json" "$BASE/$SECRET/%20" 21 | 22 | curl $CURLARGS -X PUT \ 23 | -H "Content-Type: application/json" \ 24 | -T "tests/invalid-syntax.json" "$BASE/$SECRET/$NAME" 25 | 26 | curl $CURLARGS -X PUT \ 27 | -H "Content-Type: application/json" \ 28 | -T "tests/invalid-schema.json" "$BASE/$SECRET/$NAME" 29 | 30 | curl $CURLARGS -X PUT \ 31 | -H "Content-Type: application/json" \ 32 | -T "tests/empty.json" "$BASE/$SECRET/$NAME" 33 | curl $CURLARGS -X PUT \ 34 | -H "Content-Type: application/x-tar" \ 35 | -T "tests/$SHA.tar" "$BASE/$SECRET/$NAME/$SHA.tar" 36 | 37 | curl $CURLARGS -X PUT \ 38 | -H "Content-Type: application/json" \ 39 | -T "tests/sources.json" "$BASE/$SECRET/$NAME" 40 | curl $CURLARGS -X PUT \ 41 | -H "Content-Type: application/x-tar" \ 42 | -T "tests/$SHA.tar" "$BASE/$SECRET/$NAME/$SHA.tar" 43 | 44 | curl $CURLARGS -X PUT \ 45 | -H "Content-Type: application/x-tar" \ 46 | -T "tests/$SHA.tar" "$BASE/$SECRET/$NAME/invalid.tar" 47 | 48 | curl $CURLARGS -X PUT \ 49 | -H "Content-Type: application/x-tar" \ 50 | -T "tests/$SHA.tar" \ 51 | "$BASE/$SECRET/$NAME/0000000000000000000000000000000000000000.tar" 52 | 53 | curl $CURLARGS -X GET "$BASE/$SECRET/four-oh-four" 54 | 55 | curl $CURLARGS -X GET "$BASE/$SECRET/$NAME" 56 | 57 | curl $CURLARGS -X GET "$BASE/$SECRET/four-oh-four/four-oh-four.sh" 58 | 59 | curl $CURLARGS -X GET "$BASE/$SECRET/four-oh-four/wrong.sh" 60 | 61 | curl $CURLARGS -X GET "$BASE/$SECRET/$NAME/$NAME.sh" 62 | 63 | curl $CURLARGS -X GET "$BASE/$SECRET/four-oh-four/user-data.sh" 64 | 65 | curl $CURLARGS -X GET "$BASE/$SECRET/$NAME/user-data.sh" 66 | 67 | curl $CURLARGS -X GET \ 68 | "$BASE/$SECRET/$NAME/0000000000000000000000000000000000000000.tar" 69 | 70 | curl $CURLARGS -X GET "$BASE/$SECRET/$NAME/$SHA.tar" 71 | -------------------------------------------------------------------------------- /tests/devstructure-debian-archive.json: -------------------------------------------------------------------------------- 1 | { 2 | "files": { 3 | "/etc/apt/sources.list.d/devstructure.list": { 4 | "content": "deb http://packages.devstructure.com natty main\n", 5 | "encoding": "plain", 6 | "group": "root", 7 | "mode": "100644", 8 | "owner": "root" 9 | }, 10 | "/etc/apt/trusted.gpg.d/devstructure.gpg": { 11 | "content": "mQENBExH+WABCADPVuh2idQvP4owKpXDCzqpEl2ePvo01tXhWTEX7Opsu3w8adwpG1BB0frYY5GQ61N9D6y9zwhpWivxL975SauPVHuZ6iVoZbOaNjL8JmlscGdIhqysV3cOeEkdDT3j7Zem1pmsvZrmqnBjj9ha1XkY1kEidp5yBNCZSFx/u7ZgvOZ3I/trMijnEU2GthjbiwTkLLFmRDV/6qw/DqDXh9F/PffbbKTCJ3Kc6jrdhKe7iIhR3euQ86xI+cBLtFxxBTuH6Ywr0hmdh6StKLWBn56185ErCXjVBt35XTVR/cKFPTYVZGvneXI5cA0cAlK83KiYuIay7WkTZ+P74zPkdxBRABEBAAG0KERldlN0cnVjdHVyZSA8cGFja2FnZXNAZGV2c3RydWN0dXJlLmNvbT6JATgEEwECACIFAkxH+WACGwMGCwkIBwMCBhUIAgkKCwQWAgMBAh4BAheAAAoJELPO0+qsl06CYG0IAIVaoGOkcJibTxWMbSQVunoJvK87eZBN79Zq/tJkL5/jU4HWdu+AALJmmcKMMVI7vyvSjVQDb3qU4jLbnUHLRw4ZkFqhrkaAOfOcHzvc/rvjTj9juRQaztiOtdcWCesyUGyENTf6rZAtO3jmwMdPn0t/suThAYl8B17/6OIyu0spEwx5lG/lWYdQyGhDTvj1F6lZcphFlOwSDFY0EUheXIeWli886XxnrnxyqlGC0nmm8h5mxHZDUqp3MgDlKRcdz6yo1WLkECGaEW1EWDt01Et0Qi6EBIlFLYozejU4C0MFVfR5wmB/95kTvXjfVqQIZLVb24sGLfaVwrsx/ZQtpZywAgADuQENBExH+WABCADK3tJMpC5Zm45HH1iEgI9mlk9CPwzLqdUR5CyYSQhJJhP3kkAkl31XrnugdxnOTXPuh7vO/EN3VUFthVYr9eXt+WEmX5I2zVPxA6bMP11tre5eJUAKrj2eOEskHs0aXusWhQlyM/9Bbt6X83YqbHZooNJ5m7zyARHp1nNo4go9Vesee9mIDTFBpqrHV3UvaxEl+DgRaKWIucMzevE9IF8tJKlKaolnJhKOxDd2vDepmMhLio1Ner3St8QJ+DghAhjZqlrQK/wjQ2eHEo0wFQX28UljaXWNFBV54+6kpCxQFg793k9Nb05er8Cj5oV/dQqLEbDOIXcGa3QDQ+Wl0XUlABEBAAGJAR8EGAECAAkFAkxH+WACGwwACgkQs87T6qyXToLchgf/XSSfyTxrZRnbxdb1g+KrU6D3xaMTQlD+Axj245vdzUaMkjsYnkHV/btUl8b/GE0MkErnAGuZAbMeXg1ci02PGTyX3W2HCVa06nU7AdOiCIjNktEg3BtjMT0t3DKnS/Q94Gy0EYF5yHURBzSG95+Vop2R+4eBzct/UsUg3CK885so59wERYtCQ8KarOqO/QQVRlLZPJ2K6SBinthTLTbG8Sou64OsJbeiSajLBW++/bETCXat9zRFbvvzh2RNBFZWWtGkWS9yf3wA0rC8kyztFAV344ZGLJAzPtRoEh66o524aMNJxJiCBg7ZCMzbviZHremDiajvPYZX9V06tzOjKbACAAM=", 12 | "encoding": "base64", 13 | "group": "root", 14 | "mode": "100644", 15 | "owner": "root" 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /tests/empty.json: -------------------------------------------------------------------------------- 1 | {} 2 | -------------------------------------------------------------------------------- /tests/example.blueprint-rules: -------------------------------------------------------------------------------- 1 | :source:/usr/local 2 | 3 | /etc/init/example.conf 4 | /etc/nginx/sites-*/example 5 | /etc/unicorn.conf.rb 6 | 7 | :package:apt/libmysqlclient-dev 8 | :package:apt/mysql-client-5.1 9 | :package:apt/nginx-common 10 | :package:apt/nginx-light 11 | :package:apt/ruby-dev 12 | :package:apt/rubygems 13 | :package:rubygems/* 14 | 15 | :service:sysvinit/nginx 16 | :service:upstart/example 17 | -------------------------------------------------------------------------------- /tests/file-content-json.json: -------------------------------------------------------------------------------- 1 | { 2 | "files": { 3 | "/etc/foobar": { 4 | "content": {"foo": "bar"}, 5 | "encoding": "plain", 6 | "group": "root", 7 | "mode": "100644", 8 | "owner": "root" 9 | } 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /tests/file-source-base64.json: -------------------------------------------------------------------------------- 1 | { 2 | "files": { 3 | "/etc/foobar": { 4 | "encoding": "base64", 5 | "group": "root", 6 | "mode": "100644", 7 | "owner": "root", 8 | "source": "http://rcrowley.org/foobar" 9 | } 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /tests/file-source.json: -------------------------------------------------------------------------------- 1 | { 2 | "files": { 3 | "/etc/foobar": { 4 | "encoding": "plain", 5 | "group": "root", 6 | "mode": "100644", 7 | "owner": "root", 8 | "source": "http://rcrowley.org/foobar" 9 | } 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /tests/invalid-schema.json: -------------------------------------------------------------------------------- 1 | { 2 | "invalid": "invalid" 3 | } 4 | -------------------------------------------------------------------------------- /tests/invalid-syntax.json: -------------------------------------------------------------------------------- 1 | }{ 2 | -------------------------------------------------------------------------------- /tests/rpm.json: -------------------------------------------------------------------------------- 1 | { 2 | "packages": { 3 | "rpm": { 4 | "epel-release": ["http://download.fedora.redhat.com/pub/epel/5/i386/epel-release-5-4.noarch.rpm"] 5 | } 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /tests/source-url-noname.json: -------------------------------------------------------------------------------- 1 | { 2 | "sources": { 3 | "/foobar": "http://rcrowley.org/" 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /tests/source-url.json: -------------------------------------------------------------------------------- 1 | { 2 | "sources": { 3 | "/foobar": "http://rcrowley.org/work/foobar.tar.gz" 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /tests/sources.json: -------------------------------------------------------------------------------- 1 | { 2 | "sources": { 3 | "/usr/local": "adff242fbc01ba3753abf8c3f9b45eeedec23ec6.tar" 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /tests/ssh.blueprint-rules: -------------------------------------------------------------------------------- 1 | # Blueprint rules 2 | 3 | # These are the exact opposite of blueprintignore(5) files, so they only 4 | # list resource names - the data for those resources should be extracted 5 | # from the system on which the blueprint is rendered. 6 | 7 | /etc/blueprintignore 8 | 9 | #/etc/ssh/*_config 10 | /etc/ssh 11 | !/etc/ssh/moduli 12 | 13 | :package:apt/openssh-server 14 | 15 | :service:sysvinit/ssh 16 | --------------------------------------------------------------------------------