Enabling direct programming of your provisioning and deployment
automation, rather than obscuring it, is a principal goal of
SyncWrap.

Getting Started

Lets consider, as an example, provisioning users (aka roles) for use
with a PostgreSQL database. The database engine may be installed via
the existing SyncWrap::PostgreSQL component. If doing this manually
we might start by adding the first database user we need, simply by
running (PostgreSQL CLI) createuser bob on each host. Lets start by
simply doing the same with SyncWrap. We create a new MyDatabase
component (here directly in the sync.rb, but it could also be
require’d from lib/my_database.rb, see LAYOUT) and add it to the
appropriate hosts (or roles):

The <<-SHhere-document is arguably overkill for this simple Bash
command, but the syntax will prove helpful as the Bash fragments
become more complex. The above assumes we are testing this on a
localhost (where PostgreSQL already installed), but you might also
want to try plugging this into something like examples/ec2.rb from
the SyncWrap git repo and test with a temporary EC2 host.

This continues to show the error on verbose output but returns success
and allows the automation to continue. In general this is preferable
to no automation. If this is checked into version control, basic
repeatable automation and self documentation has been achieved. A
remaining problem is that createuser could fail for more reasons
than just that we already created the user, but our script fragment
ignores all errors. A more robust approach would be to first check if
the user already exists:1

The SyncWrap command queue will effectively combine any number of
pg_create_user calls into a script to be executed in a single shell
session, so there is no significant overhead vs. implementing the loop
in Bash. Indeed we can see this by running it with verbose output
enabled:

Note that the script echoing is via Bash’s own set -v. Since the
psql test command is run in a subshell it is echoed more than
once. If we drop users “bob” and “joanne” and run it again with the -x
flag, which uses Bash’s set -x:

As a final example of composition, the fragment below taken from
SyncWrap::PostgreSQL demonstrates the block form of sh (here
the sudo variant) where Ruby function dist_service enqueues
commands from within an outer Bash conditional.

Conclusions

The Bash used above can be thought of as low-level anonymous
functions2, where all higher level logic is implemented in
Ruby. The remote pre-provisioning requirements are limited to sudo
access and the bash shell, which comes by default on target Linux
distributions. Since provisioning tasks are typically composed from
system commands, Bash is a reasonable choice at this level. To access
remote state, we need to be able to write bash conditionals and some
simple pipelines. The (Wooledge) BashGuide and Bash Hackers Wiki
may be useful resources. As shown, syncwrap provides convenient
diagnostic options in order to debug your bash fragments.

Should every PostgreSQL user need to implement pg_create_user like
methods?. The SyncWrap::PostgreSQL component can and should (#1) be
extended to provide these kinds of general utility methods, freeing
MyDatabase of needing to implement the same. Given dynamic component
binding, the pg_create_user method can simply be moved to the
SyncWrap::PostgreSQL component class, with the install method
remaining unchanged.

The example demonstrates the low barrier from manual provisioning in
an interactive shell, to basic automation in SyncWrap using similar
bash fragments, unobscured. For flexibility and reuse, functional
decomposition and re-composition can be done in Ruby with real
Component objects–no need to deal with Bash functions. Add the
ability to push templated configuration files, coordinated in Ruby,
and we have a complete and direct system for provisioning and
deployment.

More intelligent provisioning might also check if the user privileges have changed since initially created. PostgreSQL’s ALTER ROLE could be used to later promote bob to a super-user, for example.↩

But be careful with leaking Bash variables, as there is no real closure.↩