Blueprint

Blueprint is a simple configuration management tool that reverse-engineers servers. It figures out what you’ve done manually, stores it locally in a Git repository, generates code that’s able to recreate your efforts, and helps you deploy those changes to production.

Table of contents

  1. Philosophy
  2. Installation
  3. Reverse-engineering systems with blueprint-create
  4. Inspecting blueprints
  5. Ignoring particular resources
  6. Rules files and blueprint-rules
  7. Diffing, splitting and pruning existing blueprints
  8. Rendering templates of configuration files
  9. Controlling service restart conditions
  10. Generating POSIX shell scripts
  11. Sharing and distributing blueprints
  12. Generating Puppet modules and Chef cookbooks
  13. Integrating with AWS CloudFormation
  14. Deploying your application with Blueprint
  15. Local Git repository
  16. Running your own Blueprint Server
  17. Blueprint Server Protocols
  18. Blueprint Server Endpoints
  19. Contributing to Blueprint
  20. Alternatives to Blueprint

Manuals

Plumbing

APIs


Philosophy

Blueprint was born out of frustration with development environments, deployment processes, and the complexity of configuration management systems.

Blueprint insists development environments realistically model production and that starts with using Linux. Blueprint only works on Debian- or Red Hat-based Linux systems. We recommend VirtualBox, Vagrant, Rackspace Cloud, or AWS EC2 for development systems that use the same operating system (and version) as is used in production.

On top of the operating system, we recommend using the same web servers, databases, message queue brokers, and other software in development and production. This brings development visibility to entire classes of bugs that only occur due to interactions between production components.

When development and production share the same operating system and software stack, they also share the same interactive management tools, meaning developers and operators alike don’t need to maintain two vocabularies. Well-understood tools like apt-get/dpkg, yum/rpm, and the whole collection of Linux system tools are available everywhere. Blueprint is unique relative to other configuration management in encouraging use of these tools.

What’s common to all configuration management tools is the desire to manage the whole stack: from the operating system packages and services through language-specific packages, all the way to your applications. We need to span all of these across all our systems. To pick on RubyGems arbitrarily: RubyGems is purposely ignorant of its relationship to the underlying system, favoring compatibility with Windows and a wide variety of UNIX-like operating systems. Blueprint understands the macro-dependencies between RubyGems itself and the underlying system and is able to predictably reinstall a selection of gems on top of a properly configured operating system.

When constructing this predictable order-of-operations used to reinstall files, packages, services, and source installations, Blueprint, along with other configuration management tools, takes great care in performing idempotent actions. Thus Blueprint prefers to manage the entire contents of a file rather than a diff or a line to append. Idempotency means you can apply a blueprint over and over again with confidence that nothing will change if nothing needs to change.

Because Blueprint can reverse-engineer systems, it is of particular use migrating legacy systems into configuration management. It doesn’t matter when you install Blueprint: changes made to the system even before Blueprint is installed will be taken into account.


Installation

Prerequisites:

From DevStructure’s Debian archive

echo "deb http://packages.devstructure.com $(lsb_release -sc) main" | sudo tee /etc/apt/sources.list.d/devstructure.list
sudo wget -O /etc/apt/trusted.gpg.d/devstructure.gpg http://packages.devstructure.com/keyring.gpg
sudo apt-get update
sudo apt-get -y install blueprint

From PyPI

pip install blueprint

Make sure pip is using Python >= 2.6, otherwise the installation will succeed but Blueprint will not run.

From source on Debian, Ubuntu, Fedora, CentOS 6, and RHEL 6

git clone git://github.com/devstructure/blueprint.git
cd blueprint
git submodule update --init
make && sudo make install

From source on CentOS 5 and RHEL 5

rpm -Uvh http://download.fedora.redhat.com/pub/epel/5/i386/epel-release-5-4.noarch.rpm
yum install python26
git clone git://github.com/devstructure/blueprint.git
cd blueprint
git submodule update --init
make && sudo make install PYTHON=/usr/bin/python26

This installs Python 2.6 from EPEL side-by-side with Python 2.4 and so won’t break Yum.


Reverse-engineering systems with blueprint-create

By now you’ve hopefully created a development environment running the same operating system (and version) that you use in production and installed your production stack. If this is your first time configuring production databases and web servers, don’t be shy about copying-and-pasting from the guy at the next desk or a staging or production system (perhaps even multiple systems).

Once Blueprint itself is installed you can reverse-engineer your development system with a single command.

blueprint create name

Blueprint will list packages managed by APT, Yum, RubyGems, Python’s easy_install and pip, PHP’s PEAR and PECL, and Node.js’ NPM. It will also determine which configuration files in /etc have been added or modified from their packaged versions and collect files in /usr/local that are part of any software packages installed from source (typically via GNU make(1)). Finally, it will build a list of conditions under which System V init or Upstart services should be restarted, including package upgrades and configuration changes.

All of this information is encoded in a blueprint(5) JSON document and zero or more tar(5) archives and stored in a branch called name in the local Git repository ~/.blueprints.git.

Any blueprint in the local Git repository may be applied to the system with blueprint-apply(1):

blueprint apply name

Example

Suppose you want to install a basic Ruby stack for running a Sinatra application called example proxied by Nginx on Debian-based Linux. Install the prerequisite packages:

sudo apt-get install build-essential nginx-light ruby ruby-dev rubygems
sudo gem install sinatra unicorn

Configure Nginx in /etc/nginx/sites-available/example:

server {
	listen 80 default;
	location /static { root /usr/local/share/rack/example/static; }
	location / { proxy_pass http://127.0.0.1:8080; }
}

Enable the Nginx configuration:

ln -s /etc/nginx/sites-{available,enabled}/example

Create the application itself. /usr/local/share/rack/example/app.rb:

require 'sinatra'
get '/hi' do
  "Hello, world!\n"
end

/usr/local/share/rack/example/config.ru:

require 'app'
run Sinatra::Application

Configure Unicorn in /etc/unicorn.conf.rb:

worker_processes 4

Configure Upstart to run the application:

description "Example"
start on runlevel [2345]
stop on runlevel [!2345]
respawn
chdir /usr/local/share/rack/example
exec unicorn -c /etc/unicorn.conf.rb

Now create a blueprint and call it example.

sudo blueprint create example

We’ll pick this example up in the next chapter.


Inspecting blueprints

Once a blueprint has been created and stored in the local Git repository, it’s easy to inspect what’s been included both in its raw JSON form and in more friendly formats.

First, print the entire blueprint(5) JSON document:

blueprint show name | less

The output of blueprint-show(1) can be a bit overwhelming, which is one of the reasons the following four commands were created.

blueprint-show-files(1) lists the pathname of each file included in the blueprint on its own line.

pathname

blueprint-show-packages(1) lists the manager (“apt”, “yum”, “rubygems1.8”, etc.), name, and version number for each package included in the blueprint on its own line. If two versions of the same package are included (as is possibly with RubyGems and some other package managers), each will be printed on its own line.

manager package version

blueprint-show-services(1) lists the manager (“sysvinit” or “upstart”) and name of each service included in the blueprint on its own line.

manager service

blueprint-show-sources(1) lists the source installations included in the blueprint. The root directory (for example, “/usr/local”) and tarball filename are printed to standard error and the contents of the tarball are printed to standard output via tar(1)’s tv options.

dirname filename
`tar tv` output

Example

blueprint show-files example

/etc/apt/sources.list
/etc/hosts
/etc/init/example.conf
/etc/mysql/debian.cnf
/etc/nginx/sites-available/example
/etc/nginx/sites-enabled/default
/etc/nginx/sites-enabled/example
/etc/unicorn.conf.rb

blueprint show-packages example

apt binutils 2.21.53.20110810-0ubuntu3
apt blueprint 3.4.0-1py2.7
apt build-essential 11.5ubuntu1
apt ca-certificates 20110502+nmu1ubuntu5
apt cpp 4:4.6.1-2ubuntu5
apt cpp-4.6 4.6.1-9ubuntu3
apt curl 7.21.6-3ubuntu3
apt dpkg-dev 1.16.0.3ubuntu5
apt fakeroot 1.17-1
apt g++ 4:4.6.1-2ubuntu5
apt g++-4.6 4.6.1-9ubuntu3
apt gcc 4:4.6.1-2ubuntu5
apt gcc-4.6 4.6.1-9ubuntu3
apt git 1:1.7.5.4-1
apt git-core 1:1.7.5.4-1
apt git-man 1:1.7.5.4-1
apt libalgorithm-diff-perl 1.19.02-2
apt libalgorithm-diff-xs-perl 0.04-1build1
apt libalgorithm-merge-perl 0.08-2
apt libc-dev-bin 2.13-20ubuntu5
apt libc6-dev 2.13-20ubuntu5
apt libcurl3 7.21.6-3ubuntu3
apt libcurl3-gnutls 7.21.6-3ubuntu3
apt libdbd-mysql-perl 4.019-1
apt libdbi-perl 1.616-1build1
apt libdpkg-perl 1.16.0.3ubuntu5
apt liberror-perl 0.17-1
apt libgmp10 2:5.0.1+dfsg-7ubuntu2
apt libgomp1 4.6.1-9ubuntu3
apt libhtml-template-perl 2.10-1
apt libmpc2 0.9-3
apt libmpfr4 3.0.1-5
apt libmysqlclient16 5.1.58-1ubuntu1
apt libnet-daemon-perl 0.48-1
apt libplrpc-perl 0.2020-2
apt libquadmath0 4.6.1-9ubuntu3
apt libreadline5 5.2-9ubuntu1
apt librtmp0 2.3-2ubuntu1
apt libruby1.8 1.8.7.352-2
apt libstdc++6-4.6-dev 4.6.1-9ubuntu3
apt libtimedate-perl 1.2000-1
apt libwrap0 7.6.q-21
apt linux-libc-dev 3.0.0-12.20
apt manpages-dev 3.27-1ubuntu2
apt mysql-client-5.1 5.1.58-1ubuntu1
apt mysql-client-core-5.1 5.1.58-1ubuntu1
apt mysql-common 5.1.58-1ubuntu1
apt mysql-server 5.1.58-1ubuntu1
apt mysql-server-5.1 5.1.58-1ubuntu1
apt mysql-server-core-5.1 5.1.58-1ubuntu1
apt nginx-common 1.0.5-1
apt nginx-light 1.0.5-1
apt openssh-server 1:5.8p1-7ubuntu1
apt openssl 1.0.0e-2ubuntu4
apt python-pip 1.0-1
apt python-pkg-resources 0.6.16-1
apt python-setuptools 0.6.16-1
apt rsync 3.0.8-1ubuntu1
apt ruby 4.8
apt ruby-dev 4.8
apt ruby1.8 1.8.7.352-2
apt ruby1.8-dev 1.8.7.352-2
apt rubygems 1.7.2-1
apt tmux 1.5-1
python-pip Django 1.3.1
rubygems fpm 0.3.11
rubygems hpricot 0.8.5
rubygems json 1.6.3
rubygems kgio 2.6.0
rubygems mustache 0.99.4
rubygems rack 1.3.5
rubygems rack-protection 1.1.4
rubygems raindrops 0.8.0
rubygems rdiscount 1.6.8
rubygems sinatra 1.3.1
rubygems tilt 1.3.3
rubygems unicorn 4.1.1

blueprint show-services example

sysvinit nginx
sysvinit ssh
upstart example
upstart mysql

blueprint show-sources example

/usr/local 82789250f3ad94d4cfeacc0a8cabae2d26b0270e.tar
drwxr-xr-x root/root         0 2011-11-29 21:42 ./
drwxr-xr-x root/root         0 2011-11-29 20:50 ./share/
drwxr-xr-x root/root         0 2011-11-29 21:55 ./share/rack/
drwxr-xr-x rcrowley/rcrowley 0 2011-12-09 15:12 ./share/rack/example/
-rw-rw-r-- rcrowley/rcrowley 55 2011-11-29 21:02 ./share/rack/example/app.rb
-rw-rw-r-- rcrowley/rcrowley 39 2011-11-29 20:54 ./share/rack/example/config.ru

We’re off to a great start but there are some extraneous files and packages here that need cleaning up.


Ignoring particular resources

Rather than requiring you enumerate every detail of your infrastructure in code, Blueprint reverse-engineers most of these details from running systems. There are times, though, when it’s a bit too verbose.

Inspired by similar features in version control software, Blueprint allows you to enumerate files, packages, services, and sources that should be ignored in a file format inspired by gitignore(5). See blueprintignore(5) for details.

Blueprint looks for these rules in /etc/blueprintignore and ~/.blueprintignore. Rules in /etc/blueprintignore will be included in the blueprint itself, thereby propagating to other users of the blueprint. Rules in ~/.blueprintignore will remain local though each revision of a blueprint will store the full set of rules used to create it.

Files may be specified by fully-qualified or relative pathnames, possibly including glob syntax:

/etc/foo
foo/*
*.foo
[abc]/[xyz]

Packages must be specified by their manager and name, prefixed with :package::

:package:apt/build-essential

When a package is ignored, packages on which it depends are also ignored.

Services must be specified by their manager and name, prefixed with :service::

:service:sysvinit/ssh

Sources must be specified by fully-qualified pathnames:

:sources:/usr/local

You can ignore and unignore particular files within a source directory to fine-tune what’s included in the tarball.

Any rule may be negated by prefixing it with a !, which overrides defaults and well as previous matching rules - the last matching rule wins.

Example

/etc/apt/sources.list and /etc/hosts really don’t belong in the blueprint since they’re part of the operating system and we haven’t customized them. Add their pathnames to /etc/blueprintignore:

/etc/apt/sources.list
/etc/hosts

/etc/mysql/debian.cnf is generated by the MySQL server package in its postinst maintainer script. Add its pathname to /etc/blueprintignore:

/etc/mysql/debian.cnf

/etc/nginx/sites-enabled/default is part of the basic Nginx installation and again we don’t particularly care about it. Add its pathname to /etc/blueprintignore:

/etc/nginx/sites-enabled/default

build-essential brought a lot of friends along that are clouding what’s really important in this blueprint. Recall that ignoring a package also ignores its dependencies. The opposite is not true: unignoring a package leaves its dependencies alone. Ignoring and immediately unignoring build-essential, ruby, and ruby-dev will slim down the blueprint without any loss in completeness.

:package:apt/build-essential
!:package:apt/build-essential
:package:apt/ruby
!:package:apt/ruby
:package:apt/ruby-dev
!:package:apt/ruby-dev

Running sudo blueprint create example again will commit a new blueprint that takes these rules into account.


Rules files and blueprint-rules

As complexity grows, you’re likely to pass an inflection point when it becomes easier to enumerate the resources you care about, not the resources that should be ignored. When the time comes, Blueprint is ready. The rules syntax used to ignore particular resources can be turned around and used to enumerate the resources to include in the blueprint. See blueprint-rules(5) for details.

The blueprint-rules(1) command reverse-engineers the system just like blueprint-create but limits the resources included in the blueprint to those in the rules file.

blueprint rules pathname

The rules files are a bit of a hybrid between the traditional Blueprint approach of reverse-engineering and the typical configuration-as-code approach of other configuration management tools.

Example

Add these rules to example.blueprint-rules:

:source:/usr/local
/etc/init/example.conf
/etc/nginx/sites-*/example
:package:apt/libmysqlclient-dev
:package:apt/mysql-client-5.1
:package:apt/nginx-common
:package:apt/nginx-light
:package:apt/ruby-dev
:package:apt/rubygems
:package:rubygems/*
:service:sysvinit/nginx
:service:upstart/example

Now create the blueprint example:

blueprint rules example.blueprint-rules

As usual, the blueprint is stored locally in Git, ready for action.

A good exercise for the reader is creation of a separate blueprint for the MySQL server and its configuration. It’s installed alongside the web stack in development but that’s unlikely to be the case in production, so having two blueprints could be advantageous. The next chapter introduces another way to create two blueprints from one system.


Diffing, splitting and pruning existing blueprints

Refactoring is a major part of software development practice and configuration management shouldn’t be left out in the cold. Blueprint provides several tools that can be used to refactor your blueprints into more modular, maintainable, focused artifacts.

blueprint-diff(1) takes direct advantage of the subtraction operator available on Blueprint objects in the underlying library.

blueprint diff minuend subtrahend difference

Resources that appear in minuend but not subtrahend will be included in difference and committed to the local Git repository under that name.

blueprint-split(1) and blueprint-prune(1) are interactive refactoring tools.

blueprint-split prints each resource in src and prompts you for a choice of dest-a or dest-b. The resulting dest-a and dest-b blueprints are committed to the local Git repository.

blueprint split src dest-a dest-b

blueprint-prune instead prompts you to include or ignore each resource in src in dest.

blueprint prune src dest

There are some limitations in both of these tools, however. You can’t currently split the files within a source tarball. The best workaround is to use rules files to create blueprints that contain only the files you want.

Example

blueprint prune example example-nginx

/usr/local 6326b42413443bc2d94e13747d05f650b873a53e.tar
Include in blueprint example-nginx? [y/n] n
/etc/init/example.conf
Include in blueprint example-nginx? [y/n] n
/etc/nginx/sites-available/example
Include in blueprint example-nginx? [y/n] y
/etc/nginx/sites-enabled/example
Include in blueprint example-nginx? [y/n] y
apt nginx-common 1.0.5-1
Include in blueprint example-nginx? [y/n] y
apt nginx-light 1.0.5-1
Include in blueprint example-nginx? [y/n] y
apt ruby-dev 4.8
Include in blueprint example-nginx? [y/n] n
apt rubygems 1.7.2-1
Include in blueprint example-nginx? [y/n] n
rubygems fpm 0.3.11
Include in blueprint example-nginx? [y/n] n
rubygems hpricot 0.8.5
Include in blueprint example-nginx? [y/n] n
rubygems json 1.6.3
Include in blueprint example-nginx? [y/n] n
rubygems kgio 2.6.0
Include in blueprint example-nginx? [y/n] n
rubygems mustache 0.99.4
Include in blueprint example-nginx? [y/n] n
rubygems rack 1.3.5
Include in blueprint example-nginx? [y/n] n
rubygems rack-protection 1.1.4
Include in blueprint example-nginx? [y/n] n
rubygems raindrops 0.8.0
Include in blueprint example-nginx? [y/n] n
rubygems rdiscount 1.6.8
Include in blueprint example-nginx? [y/n] n
rubygems sinatra 1.3.1
Include in blueprint example-nginx? [y/n] n
rubygems tilt 1.3.3
Include in blueprint example-nginx? [y/n] n
rubygems unicorn 4.1.1
Include in blueprint example-nginx? [y/n] n
sysvinit nginx
Include in blueprint example-nginx? [y/n] y
upstart example
Include in blueprint example-nginx? [y/n] n

The same could have been done with blueprint-split to create example-nginx and example-mysql or base and custom — any distinction you desire.


Rendering templates of configuration files

Not all systems are created equal. Certainly your m2.4xlarge AWS EC2 instance packs a bit more CPU than a virtual machine running in a corner of your laptop and your configurations should be able to cope with these operational extremes.

Blueprint allows configuration files (found in /etc) to be specified as templates and (optionally) data rather than static content. These templates are rendered by a special portable dialect of the mustache(5) template language called mustache.sh. See blueprint-template(5) for details.

In their simplest form, these templates allow substitution of system parameters.

{{FOO}}

You can also substitute the output of a shell command.

{{`echo foo`}}

More complex uses can iterate over the lines of output from a shell command.

{{#`echo foo; echo bar; echo baz`}}
{{_M_LINE}}
{{/`echo foo; echo bar; echo baz`}}

Blueprint ships with a small helping of common system parameters:

There are several more, documented in blueprint-template(7). You can provide your own global data by adding source-able shell scripts with the .sh suffix to /etc/blueprint-template.d.

Any pathname may have an associated template, pathname.blueprint-template.mustache. An additional source-able shell script private to just this template may be placed in pathname.blueprint-template.sh.

To render a template locally (likely during development), use blueprint-template(1).

blueprint template pathname

Example

Our Unicorn configuration statically assumes four workers is the best configuration. In reality, Unicorn workers are a function of the number of CPU cores available.

Configure Unicorn to use one worker per CPU in /etc/unicorn.conf.rb.blueprint-template.mustache:

worker_processes {{CORES}}

Configure Unicorn to use four workers per CPU in /etc/unicorn.conf.rb.blueprint-template.mustache:

worker_processes {{`expr 4 \* $CORES`}}

Or, you can extract computation out of the template and into /etc/unicorn.conf.rb.blueprint-template.sh:

export WORKER_PROCESSES="$(expr 4 \* $CORES)"

/etc/unicorn.conf.rb.blueprint-template.mustache becomes:

worker_processes {{WORKER_PROCESSES}}

Now Blueprint can scale this Unicorn configuration up and down as the system allows or requires.


Controlling service restart conditions

The last step in applying a blueprint is to restart all the services whose configuration changed. Most configuration management tools require you to connect the dots explicitly but Blueprint finds many of those relationships automatically.

System V init and Upstart services are included in blueprints when their init script or configuration file, or the package that contains the init script or configuration file, are included in the blueprint. From there, Blueprint searches for resources that, when changed, should cause the service to restart.

Other files may be added to the list to watch by naming them in a comment in the service init script or configuration file.

Example

Suppose our example application reads /etc/database.yml for database connection credentials. The Unicorn application server must be restarted to read changes to this file. Mention /etc/database.yml in the Upstart configuration:

description "Example"
start on runlevel [2345]
stop on runlevel [!2345]
respawn
chdir /usr/local/share/rack/example
exec unicorn -c /etc/unicorn.conf.rb
# Dear Blueprint, restart me when /etc/database.yml changes.

Generating POSIX shell scripts

Beneath the blueprint-apply(1) command briefly introduced before there is the -S option to blueprint-show(1) which generates a POSIX shell script that can apply a blueprint to any system.

Dependencies are especially painful when bootstrapping new systems so Blueprint takes great pains to generate dependency-free shell scripts. They extract source tarballs, place file contents and adjust owners/groups/modes, install packages, and restart services as necessary according to the algorithm described in blueprint(5). Even template rendering is handled without any dependencies.

The generated shell script for the blueprint name is written to a file in your working directory as name.sh or name/bootstrap.sh if the blueprint contains templates or source tarballs.

blueprint show -S name

This shell script and its associated files can drive deployment, provisioning, or other development environments without even having to install Blueprint first.

Example

Running blueprint show -S example creates example/bootstrap.sh as follows and bundles mustache.sh and the source tarball containing our example application. example/bootstrap.sh:

#
# Automatically generated by blueprint(7).  Edit at your own risk.
#

set -x

cd "$(dirname "$0")"

mkdir -p "/etc/init"
cat >"/etc/init/example.conf" <<EOF
description "Example"
start on runlevel [2345]
stop on runlevel [!2345]
respawn
chdir /usr/local/share/rack/example
exec unicorn -c /etc/unicorn.conf.rb
# Dear Blueprint, restart me when /etc/database.yml changes.
EOF
MD5SUM="$(md5sum "/etc/nginx/sites-available/example" 2>/dev/null)"
mkdir -p "/etc/nginx/sites-available"
cat >"/etc/nginx/sites-available/example" <<EOF
server {
listen 80 default;
location /static { root /usr/local/share/rack/example/static; }
location / { proxy_pass http://127.0.0.1:8080; }
}
EOF
[ "$MD5SUM" != "$(md5sum "/etc/nginx/sites-available/example")" ] && SERVICE_sysvinit_nginx=1
MD5SUM="$(md5sum "/etc/nginx/sites-enabled/example" 2>/dev/null)"
mkdir -p "/etc/nginx/sites-enabled"
ln -s "/etc/nginx/sites-available/example" "/etc/nginx/sites-enabled/example"
[ "$MD5SUM" != "$(md5sum "/etc/nginx/sites-enabled/example")" ] && SERVICE_sysvinit_nginx=1
MD5SUM="$(md5sum "/etc/unicorn.conf.rb" 2>/dev/null)"
mkdir -p "/etc"
(
set +x
. "lib/mustache.sh"
for F in */blueprint-template.d/*.sh
do
    . "$F"
done
export WORKER_PROCESSES="$(expr 4 \* $CORES)"
mustache >"/etc/unicorn.conf.rb" <<EOF
worker_processes {{WORKER_PROCESSES}}
EOF
)
[ "$MD5SUM" != "$(md5sum "/etc/unicorn.conf.rb")" ] && SERVICE_upstart_example=1
export APT_LISTBUGS_FRONTEND="none"
export APT_LISTCHANGES_FRONTEND="none"
export DEBIAN_FRONTEND="noninteractive"
apt-get -q update
[ "$(dpkg-query -f'${Version}\n' -W mysql-client-5.1)" = "5.1.58-1ubuntu1" ] || apt-get -y -q -o DPkg::Options::=--force-confold install mysql-client-5.1=5.1.58-1ubuntu1
[ "$(dpkg-query -f'${Version}\n' -W nginx-common)" = "1.0.5-1" ] || { apt-get -y -q -o DPkg::Options::=--force-confold install nginx-common=1.0.5-1; SERVICE_sysvinit_nginx=1; }
[ "$(dpkg-query -f'${Version}\n' -W nginx-light)" = "1.0.5-1" ] || apt-get -y -q -o DPkg::Options::=--force-confold install nginx-light=1.0.5-1
[ "$(dpkg-query -f'${Version}\n' -W ruby-dev)" = "4.8" ] || apt-get -y -q -o DPkg::Options::=--force-confold install ruby-dev=4.8
[ "$(dpkg-query -f'${Version}\n' -W rubygems)" = "1.7.2-1" ] || apt-get -y -q -o DPkg::Options::=--force-confold install rubygems=1.7.2-1
gem -i -v0.3.11 fpm >/dev/null || gem install --no-rdoc --no-ri -v0.3.11 fpm
gem -i -v0.8.5 hpricot >/dev/null || gem install --no-rdoc --no-ri -v0.8.5 hpricot
gem -i -v1.6.3 json >/dev/null || gem install --no-rdoc --no-ri -v1.6.3 json
gem -i -v2.6.0 kgio >/dev/null || gem install --no-rdoc --no-ri -v2.6.0 kgio
gem -i -v0.99.4 mustache >/dev/null || gem install --no-rdoc --no-ri -v0.99.4 mustache
gem -i -v1.3.5 rack >/dev/null || gem install --no-rdoc --no-ri -v1.3.5 rack
gem -i -v1.1.4 rack-protection >/dev/null || gem install --no-rdoc --no-ri -v1.1.4 rack-protection
gem -i -v0.8.0 raindrops >/dev/null || gem install --no-rdoc --no-ri -v0.8.0 raindrops
gem -i -v1.6.8 rdiscount >/dev/null || gem install --no-rdoc --no-ri -v1.6.8 rdiscount
gem -i -v1.3.1 sinatra >/dev/null || gem install --no-rdoc --no-ri -v1.3.1 sinatra
gem -i -v1.3.3 tilt >/dev/null || gem install --no-rdoc --no-ri -v1.3.3 tilt
gem -i -v4.1.1 unicorn >/dev/null || gem install --no-rdoc --no-ri -v4.1.1 unicorn
[ -n "$SERVICE_sysvinit_nginx" ] && /etc/init.d/nginx restart
[ -n "$SERVICE_upstart_example" ] && { restart example || start example; }

Sharing and distributing blueprints

DevStructure runs a free Blueprint Server at devstructure.com but this service will not be available after June 30th, 2012. Learn more about running your own Blueprint Server

We at DevStructure saw the workflow unfolding around these generated shell scripts and in them an opportunity for a macro don’t-repeat-yourself optimization. Thus were born blueprint-push(1) and blueprint-pull(1).

If you decide to run your own Blueprint Server, you’ll need to configure a server ahead of time in /etc/blueprint.cfg:

[io]
server = server

blueprint-push uploads the JSON document and all source tarballs referenced by a blueprint to a Blueprint Server, which stores them behind a long secret key in AWS S3. The URL that may be used to pull the blueprint later is printed to standard output.

blueprint push name

The first time you run this command it will prompt you with the contents of /etc/blueprint.cfg which you can optionally put in place. If you do, Blueprint will reuse the same secret the next time blueprint-push is called. Configuring a secret this way allows you to push revisions to your blueprints which can then be pulled from a known location.

blueprint-pull downloads the JSON document and all source tarballs referenced by a blueprint that has been pushed and stores them in the local Git repository. Typically, it accepts the URL printed by blueprint-push but if you configure a default secret in /etc/blueprint.cfg, you can pull blueprints by only their name.

blueprint pull url
blueprint pull name

Just as blueprint-show’s -S option helps bootstrap systems with zero dependencies, devstructure.com can generate shell scripts remotely so Blueprint doesn’t have to be installed ahead of time. You can bootstrap a new system from a pushed blueprint in one command:

curl https://devstructure.com/secret/name/name.sh | sh

Example

Push the example blueprint to devstructure.com:

blueprint push example

Now configure a production system to apply the latest revision to example every half hour (this behavior should be familiar to Puppet and Chef users). In root’s crontab:

*/30 * * * * curl https://devstructure.com/0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz_-/example/example.sh | sh

Generating Puppet modules and Chef cookbooks

Blueprint can also streamline the development workflow in Puppet- or Chef-managed environments by generating complete Puppet modules or Chef cookbooks.

Generate a Puppet module in name/manifests/init.pp:

blueprint show -P name

Generate a Chef cookbook in name/recipes/default.rb:

blueprint show -C name

These modules and cookbooks may be included directly in any Puppet or Chef environment or be used as the starting point for further development — the code is formatted according to the style guidelines of the respective communities.

Note, however, that the file templates used by Blueprint are incompatible with Puppet and Chef and so can’t be included in the generated modules and cookbooks.

Example

Running blueprint show -P example creates example/manifests/init.pp as follows and bundles the source tarball containing our example application. example/manifests/init.pp:

#
# Automatically generated by blueprint(7).  Edit at your own risk.
#
class example {
	Exec {
		path => '/home/rcrowley/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games',
	}
	Class['sources'] -> Class['files'] -> Class['packages']
	class files {
		file {
			'/etc':
				ensure => directory;
			'/etc/init':
				ensure => directory;
			'/etc/init/example.conf':
				content => template('example/etc/init/example.conf'),
				ensure  => file,
				group   => root,
				mode    => 0644,
				owner   => root;
			'/etc/nginx':
				ensure => directory;
			'/etc/nginx/sites-available':
				ensure => directory;
			'/etc/nginx/sites-available/example':
				content => template('example/etc/nginx/sites-available/example'),
				ensure  => file,
				group   => root,
				mode    => 0644,
				owner   => root;
			'/etc/nginx/sites-enabled':
				ensure => directory;
			'/etc/nginx/sites-enabled/example':
				ensure => '/etc/nginx/sites-available/example',
				group  => root,
				owner  => root;
			'/etc/unicorn.conf.rb':
				content => template('example/etc/unicorn.conf.rb'),
				ensure  => file,
				group   => root,
				mode    => 0644,
				owner   => root;
		}
	}
	include files
	class packages {
		Class['apt'] -> Class['rubygems']
		exec { 'apt-get -q update':
			before => Class['apt'],
		}
		class apt {
			package {
				'mysql-client-5.1':
					ensure => '5.1.58-1ubuntu1';
				'nginx-common':
					ensure => '1.0.5-1';
				'nginx-light':
					ensure => '1.0.5-1';
				'ruby-dev':
					ensure => '4.8';
				'rubygems':
					ensure => '1.7.2-1';
			}
		}
		include apt
		class rubygems {
			package {
				'fpm':
					ensure   => '0.3.11',
					provider => gem;
				'hpricot':
					ensure   => '0.8.5',
					provider => gem;
				'json':
					ensure   => '1.6.3',
					provider => gem;
				'kgio':
					ensure   => '2.6.0',
					provider => gem;
				'mustache':
					ensure   => '0.99.4',
					provider => gem;
				'rack':
					ensure   => '1.3.5',
					provider => gem;
				'rack-protection':
					ensure   => '1.1.4',
					provider => gem;
				'raindrops':
					ensure   => '0.8.0',
					provider => gem;
				'rdiscount':
					ensure   => '1.6.8',
					provider => gem;
				'sinatra':
					ensure   => '1.3.1',
					provider => gem;
				'tilt':
					ensure   => '1.3.3',
					provider => gem;
				'unicorn':
					ensure   => '4.1.1',
					provider => gem;
			}
		}
		include rubygems
	}
	include packages
	class services {
		class sysvinit {
			service { 'nginx':
				enable    => true,
				ensure    => running,
				subscribe => [File['/etc/nginx/sites-available/example'], File['/etc/nginx/sites-enabled/example'], Package['nginx-common'], Exec['82789250f3ad94d4cfeacc0a8cabae2d26b0270e.tar']],
			}
		}
		include sysvinit
		class upstart {
			service { 'example':
				enable    => true,
				ensure    => running,
				provider  => upstart,
				subscribe => [File['/etc/unicorn.conf.rb'], Exec['82789250f3ad94d4cfeacc0a8cabae2d26b0270e.tar']],
			}
		}
		include upstart
	}
	include services
	class sources {
		exec { 'tar xf /tmp/82789250f3ad94d4cfeacc0a8cabae2d26b0270e.tar':
			alias => '/usr/local',
			cwd   => '/usr/local',
		}
		file { '/tmp/82789250f3ad94d4cfeacc0a8cabae2d26b0270e.tar':
			before => Exec['/usr/local'],
			group  => root,
			mode   => 0644,
			owner  => root,
			source => 'puppet:///modules/example/tmp/82789250f3ad94d4cfeacc0a8cabae2d26b0270e.tar',
		}
	}
	include sources
}

Running blueprint show -C example creates example/recipes/default.rb as follows and bundles the source tarball containing our example application. example/recipes/default.rb:

#
# Automatically generated by blueprint(7).  Edit at your own risk.
#
cookbook_file('/tmp/82789250f3ad94d4cfeacc0a8cabae2d26b0270e.tar') do
  backup false
  group 'root'
  mode '0644'
  owner 'root'
  source 'tmp/82789250f3ad94d4cfeacc0a8cabae2d26b0270e.tar'
end
execute('tar xf "/tmp/82789250f3ad94d4cfeacc0a8cabae2d26b0270e.tar"') { cwd '/usr/local' }
directory('/etc/init') do
  group 'root'
  mode '0755'
  owner 'root'
  recursive true
end
cookbook_file('/etc/init/example.conf') do
  backup false
  group 'root'
  mode '0644'
  owner 'root'
  source 'etc/init/example.conf'
end
directory('/etc/nginx/sites-available') do
  group 'root'
  mode '0755'
  owner 'root'
  recursive true
end
cookbook_file('/etc/nginx/sites-available/example') do
  backup false
  group 'root'
  mode '0644'
  owner 'root'
  source 'etc/nginx/sites-available/example'
end
directory('/etc/nginx/sites-enabled') do
  group 'root'
  mode '0755'
  owner 'root'
  recursive true
end
link('/etc/nginx/sites-enabled/example') do
  group 'root'
  owner 'root'
  to '/etc/nginx/sites-available/example'
end
directory('/etc') do
  group 'root'
  mode '0755'
  owner 'root'
  recursive true
end
cookbook_file('/etc/unicorn.conf.rb') do
  backup false
  group 'root'
  mode '0644'
  owner 'root'
  source 'etc/unicorn.conf.rb'
end
execute('apt-get -q update')
package('mysql-client-5.1') { version '5.1.58-1ubuntu1' }
package('nginx-common') { version '1.0.5-1' }
package('nginx-light') { version '1.0.5-1' }
package('ruby-dev') { version '4.8' }
package('rubygems') { version '1.7.2-1' }
gem_package('fpm') { version '0.3.11' }
gem_package('hpricot') { version '0.8.5' }
gem_package('json') { version '1.6.3' }
gem_package('kgio') { version '2.6.0' }
gem_package('mustache') { version '0.99.4' }
gem_package('rack') { version '1.3.5' }
gem_package('rack-protection') { version '1.1.4' }
gem_package('raindrops') { version '0.8.0' }
gem_package('rdiscount') { version '1.6.8' }
gem_package('sinatra') { version '1.3.1' }
gem_package('tilt') { version '1.3.3' }
gem_package('unicorn') { version '4.1.1' }
service('nginx') do
  action [:enable, :start]
  subscribes :restart, resources('cookbook_file[/etc/nginx/sites-available/example]', 'cookbook_file[/etc/nginx/sites-enabled/example]', 'package[nginx-common]', 'execute[82789250f3ad94d4cfeacc0a8cabae2d26b0270e.tar]')
end
service('example') do
  action [:enable, :start]
  provider Chef::Provider::Service::Upstart
  subscribes :restart, resources('cookbook_file[/etc/unicorn.conf.rb]', 'execute[82789250f3ad94d4cfeacc0a8cabae2d26b0270e.tar]')
end

Integrating with AWS CloudFormation

Production environments are much more than a rack of servers these days and nowhere is that more apparent than AWS. Blueprint integrates with AWS CloudFormation to provision and bootstrap entire infrastructures declaratively.

The --cfn option to blueprint-show(1) generates the skeleton of a CloudFormation template that provisions a single EC2 instance running Amazon Linux (an RPM-based distribution supported by Amazon) which will apply the blueprint during its first boot.

File templates and source tarballs aren’t supported seamlessly, however. File templates can be recreated using CloudFormation’s primitive string functions or an executable user-data. Source tarballs may reference fully-qualified URLs.

Example

Running blueprint show --cfn example creates example.json:

{
  "AWSTemplateFormatVersion": "2010-09-09", 
  "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.", 
  "Mappings": {
    "AWSInstanceType2Arch": {
      "c1.medium": {
        "Arch": "32"
      }, 
      "c1.xlarge": {
        "Arch": "64"
      }, 
      "cc1.4xlarge": {
        "Arch": "64"
      }, 
      "m1.large": {
        "Arch": "64"
      }, 
      "m1.small": {
        "Arch": "32"
      }, 
      "m1.xlarge": {
        "Arch": "64"
      }, 
      "m2.2xlarge": {
        "Arch": "64"
      }, 
      "m2.4xlarge": {
        "Arch": "64"
      }, 
      "m2.xlarge": {
        "Arch": "64"
      }, 
      "t1.micro": {
        "Arch": "32"
      }
    }, 
    "AWSRegionArch2AMI": {
      "ap-northeast-1": {
        "32": "ami-dcfa4edd", 
        "64": "ami-e8fa4ee9"
      }, 
      "ap-southeast-1": {
        "32": "ami-74dda626", 
        "64": "ami-7edda62c"
      }, 
      "eu-west-1": {
        "32": "ami-24506250", 
        "64": "ami-20506254"
      }, 
      "us-east-1": {
        "32": "ami-7f418316", 
        "64": "ami-7341831a"
      }, 
      "us-west-1": {
        "32": "ami-951945d0", 
        "64": "ami-971945d2"
      }
    }
  }, 
  "Outputs": {
    "PublicDnsName": {
      "Description": "Public DNS name of the EC2 instance.", 
      "Value": {
        "Fn::GetAtt": [
          "EC2Instance", 
          "PublicDnsName"
        ]
      }
    }
  }, 
  "Parameters": {
    "InstanceType": {
      "AllowedValues": [
        "t1.micro", 
        "m1.small", 
        "m1.large", 
        "m1.xlarge", 
        "m2.xlarge", 
        "m2.2xlarge", 
        "m2.4xlarge", 
        "c1.medium", 
        "c1.xlarge", 
        "cc1.4xlarge"
      ], 
      "ConstraintDescription": "must be a valid EC2 instance type.", 
      "Default": "m1.small", 
      "Description": "EC2 instance type.", 
      "Type": "String"
    }, 
    "KeyName": {
      "Description": "Name of an existing EC2 KeyPair to enable SSH access to the instances", 
      "Type": "String"
    }
  }, 
  "Resources": {
    "CfnUser": {
      "Properties": {
        "Path": "/", 
        "Policies": [
          {
            "PolicyDocument": {
              "Statement": [
                {
                  "Action": "cloudformation:DescribeStackResource", 
                  "Effect": "Allow", 
                  "Resource": "*"
                }
              ]
            }, 
            "PolicyName": "root"
          }
        ]
      }, 
      "Type": "AWS::IAM::User"
    }, 
    "EC2Instance": {
      "Metadata": {
        "AWS::CloudFormation::Init": {
          "config": {
            "files": {
              "/etc/init/example.conf": {
                "content": "description \"Example\"\nstart on runlevel [2345]\nstop on runlevel [!2345]\nrespawn\nchdir /usr/local/share/rack/example\nexec unicorn -c /etc/unicorn.conf.rb\n# Dear Blueprint, restart me when /etc/database.yml changes.\n", 
                "encoding": "plain", 
                "group": "root", 
                "mode": "100644", 
                "owner": "root"
              }, 
              "/etc/nginx/sites-available/example": {
                "content": "server {\n\tlisten 80 default;\n\tlocation /static { root /usr/local/share/rack/example/static; }\n\tlocation / { proxy_pass http://127.0.0.1:8080; }\n}\n", 
                "encoding": "plain", 
                "group": "root", 
                "mode": "100644", 
                "owner": "root"
              }, 
              "/etc/nginx/sites-enabled/example": {
                "content": "/etc/nginx/sites-available/example", 
                "encoding": "plain", 
                "group": "root", 
                "mode": "120777", 
                "owner": "root"
              }, 
              "/etc/unicorn.conf.rb": {
                "content": "worker_processes 4\n", 
                "encoding": "plain", 
                "group": "root", 
                "mode": "100644", 
                "owner": "root"
              }
            }, 
            "packages": {
              "apt": {
                "mysql-client-5.1": [
                  "5.1.58-1ubuntu1"
                ], 
                "nginx-common": [
                  "1.0.5-1"
                ], 
                "nginx-light": [
                  "1.0.5-1"
                ], 
                "ruby-dev": [
                  "4.8"
                ], 
                "rubygems": [
                  "1.7.2-1"
                ]
              }, 
              "rubygems": {
                "fpm": [
                  "0.3.11"
                ], 
                "hpricot": [
                  "0.8.5"
                ], 
                "json": [
                  "1.6.3"
                ], 
                "kgio": [
                  "2.6.0"
                ], 
                "mustache": [
                  "0.99.4"
                ], 
                "rack": [
                  "1.3.5"
                ], 
                "rack-protection": [
                  "1.1.4"
                ], 
                "raindrops": [
                  "0.8.0"
                ], 
                "rdiscount": [
                  "1.6.8"
                ], 
                "sinatra": [
                  "1.3.1"
                ], 
                "tilt": [
                  "1.3.3"
                ], 
                "unicorn": [
                  "4.1.1"
                ]
              }
            }, 
            "services": {
              "sysvinit": {
                "nginx": {
                  "enable": true, 
                  "ensureRunning": true, 
                  "files": [
                    "/etc/nginx/sites-enabled/example", 
                    "/etc/nginx/sites-available/example"
                  ], 
                  "packages": {
                    "apt": [
                      "nginx-common"
                    ]
                  }, 
                  "sources": [
                    "/usr/local"
                  ]
                }
              }, 
              "upstart": {
                "example": {
                  "enable": true, 
                  "ensureRunning": true, 
                  "files": [
                    "/etc/unicorn.conf.rb"
                  ], 
                  "sources": [
                    "/usr/local"
                  ]
                }
              }
            }, 
            "sources": {
              "/usr/local": "82789250f3ad94d4cfeacc0a8cabae2d26b0270e.tar"
            }
          }
        }
      }, 
      "Properties": {
        "ImageId": {
          "Fn::FindInMap": [
            "AWSRegionArch2AMI", 
            {
              "Ref": "AWS::Region"
            }, 
            {
              "Fn::FindInMap": [
                "AWSInstanceType2Arch", 
                {
                  "Ref": "InstanceType"
                }, 
                "Arch"
              ]
            }
          ]
        }, 
        "InstanceType": {
          "Ref": "InstanceType"
        }, 
        "KeyName": {
          "Ref": "KeyName"
        }, 
        "SecurityGroups": [
          {
            "Ref": "SecurityGroup"
          }
        ], 
        "UserData": {
          "Fn::Base64": {
            "Fn::Join": [
              "", 
              [
                "#!/bin/bash\n", 
                "/opt/aws/bin/cfn-init -s ", 
                {
                  "Ref": "AWS::StackName"
                }, 
                " -r EC2Instance ", 
                " --access-key ", 
                {
                  "Ref": "HostKeys"
                }, 
                " --secret-key ", 
                {
                  "Fn::GetAtt": [
                    "HostKeys", 
                    "SecretAccessKey"
                  ]
                }, 
                " --region ", 
                {
                  "Ref": "AWS::Region"
                }, 
                "\n", 
                "/opt/aws/bin/cfn-signal -e $? '", 
                {
                  "Ref": "WaitHandle"
                }, 
                "'\n"
              ]
            ]
          }
        }
      }, 
      "Type": "AWS::EC2::Instance"
    }, 
    "HostKeys": {
      "Properties": {
        "UserName": {
          "Ref": "CfnUser"
        }
      }, 
      "Type": "AWS::IAM::AccessKey"
    }, 
    "SecurityGroup": {
      "Properties": {
        "GroupDescription": "SSH access only.", 
        "SecurityGroupIngress": [
          {
            "CidrIp": "0.0.0.0/0", 
            "FromPort": "22", 
            "IpProtocol": "tcp", 
            "ToPort": "22"
          }
        ]
      }, 
      "Type": "AWS::EC2::SecurityGroup"
    }, 
    "WaitCondition": {
      "DependsOn": "EC2Instance", 
      "Properties": {
        "Handle": {
          "Ref": "WaitHandle"
        }, 
        "Timeout": "600"
      }, 
      "Type": "AWS::CloudFormation::WaitCondition"
    }, 
    "WaitHandle": {
      "Type": "AWS::CloudFormation::WaitConditionHandle"
    }
  }
}

Deploying your application with Blueprint

The example running throughout this tutorial takes advantage of Blueprint’s default handling of /usr/local to package up an example web application. As an application grows, this can become muddled by other packages you install from source.

Blueprint doesn’t dictate how you deploy your applications but here are a few options play very nicely with Blueprint.

And remember to use templates to render configuration files appropriately in development, staging, and production.


Local Git repository

Blueprints are stored in the local Git repository ~/.blueprints.git. Direct access isn’t typically needed but Blueprint comes with the blueprint-git(1) tool that simplifies the parameters to git(1) needed to use this repository.

Example

Clone the entire local Git repository into blueprints in the working directory:

blueprint git clone

Show the diff, from Git’s point-of-view, between the previous two revisions of the example blueprint:

blueprint git show example

The JSON document is pretty-printed for storage so Git’s diffs will actually be meaningful.


Running your own Blueprint Server

DevStructure runs a free Blueprint Server at devstructure.com but this service will not be available after June 30th, 2012.

Running a Blueprint Server opens up the blueprint-push(1) and blueprint-pull(1) commands so you can store your blueprints remotely and share them with your team.

The Blueprint Server protocols and endpoints are documented for adventurous users that want to implement their own client or server.

Installation

Prerequisites:

Install the prerequisites from PyPI:

sudo pip install boto flask gunicorn

The rest of Blueprint Server comes bundled with Blueprint itself in the blueprint.io.server module.

Configuration

Configure your Blueprint Server in /etc/blueprint.cfg:

[s3]
access_key = access_key
secret_key = secret_key
bucket = bucket

Configure your other servers to communicate with the Blueprint Server in /etc/blueprint.cfg:

[io]
server = server

Supervise Blueprint Server with Upstart on Ubuntu or CentOS 6:

description "Blueprint Server"

start on runlevel [2345]
stop on runlevel [!2345]

respawn

env VIA=upstart

pre-start exec mkdir -p /var/log/blueprint-server

exec gunicorn -p/var/run/blueprint-server.pid -b0.0.0.0:5000 -w4 blueprint.io.server:app >>/var/log/blueprint-server/error.log 2>&1

# Mention /etc/blueprint.cfg so Blueprint will connect this service to that file.

Other platforms will have similar production configurations.

Usage

Start the server in development mode:

python /usr/lib/python2.7/dist-packages/blueprint/io/server/__init__.py

(Note that the pathname may be different on your system, particularly if you installed Blueprint from source or from PyPI.)

Start the server in production mode running in the foreground:

gunicorn blueprint.io.server:app

Some sort of process supervision, like Upstart as shown above, is recommended for production use.

Example

First, start your Blueprint Server:

start blueprint-server

Push one of your blueprints to the Blueprint Server for safe-keeping or sharing with others:

blueprint push example

Pull a blueprint from the Blueprint Server to configure new development environments or extra production capacity:

blueprint pull example

Blueprint Server Protocols

Prerequisite: obtain a secret key via the GET /secret endpoint.

Storing a blueprint

  1. Store the JSON representation of a blueprint via the PUT /secret/name endpoint.
  2. Store each tarball referenced in the "sources" object in the blueprint via the PUT /secret/name/sha.tar endpoint.

Fetching a blueprint

  1. Fetch the JSON representation of a blueprint via the GET /secret/name endpoint.
  2. Fetch each tarball referenced in the "sources" object in the blueprint via the GET /secret/name/sha.tar endpoint.

Bootstrapping on AWS

  1. Fetch the user data script for a blueprint via the GET /secret/name/user-data.sh endpoint.
  2. Provision instances with the script as their user data via the AWS Management Console or an argument to the EC2 command-line tools.

Configuring production instances

Production instances can use blueprints without installing Blueprint (and therefore Git and friends) by asking the Blueprint I/O Server to generate POSIX shell code.

  1. Fetch the POSIX shell representation of the blueprint via the GET /secret/name/name.sh endpoint. The resulting program will fetch tarballs as needed via the GET /secret/name/sha.tar endpoint.
  2. sh name.sh interactively, at boot, or periodically via cron(8).

Blueprint Server Endpoints

GET /secret

Create a new secret key known only to the caller. This is the namespace beneath which the caller’s blueprints are stored.

Responses:

PUT /secret/name

Store the JSON representation of the blueprint name. The Content-Type of the body must be application/json.

Parameters:

Responses:

GET /secret/name

Fetch the JSON representation of the blueprint name.

Parameters:

Responses:

PUT /secret/name/sha.tar

Store a source tarball referenced by blueprint name. The Content-Type of the body must be application/x-tar.

Parameters:

Responses:

GET /secret/name/sha.tar

Fetch a source tarball referenced by blueprint name.

Parameters:

Responses:

GET /secret/name/name.sh

Fetch the POSIX shell representation of the blueprint name. The resulting program will fetch tarballs as needed via the GET /secret/name/sha.tar endpoint.

Parameters:

Responses:

GET /secret/name/user-data.sh

Fetch POSIX shell commands to download and apply the blueprint name. EC2 instances provisioned from an AMI with cloud-init will execute this program if given as user data.

Parameters:

Responses:


Contributing to Blueprint

Blueprint is open-source, BSD-licensed software. Contributions are welcome via pull requests on GitHub.


Alternatives to Blueprint

If Blueprint’s not your cup of tea, check out the following tools. It’s much better to have some form of configuration management than none at all.