Collection of Packer templates used for various infrastructure layers.
To build a given template, one may use the make implicit builder, like the
following for ci-stevonnie:
make ci-stevonnieor, with a specific builder:
make ci-stevonnie BUILDER=dockeror forget about the Makefile and run with packer directly:
packer build -only=docker <(bin/yml2json < ci-stevonnie.yml)Most of the templates in here require some env vars. Take a look at
.example.env for an example. Use of
autoenv is encouraged but not
required.
There are two primary types of templates present at the top level: those
intended for use as execution environment for jobs flowing through Travis CI,
and those used for various backend fun in the Travis CI infrastructure. The
former type all have the prefix ci-, described in more detail below:
There are two primary types of stacks: those targeting Ubuntu 14.04 (trusty), and those targeting Ubuntu 16.04 (xenial) that run on GCE and Docker.
Take a peek at what's what:
make stacks-trustymake stacks-xenialThere may be some subtle variations, but for the most part each stack is built via the following steps.
The generated files in ./tmp/git-meta/ are copied onto the provisioned machine
at /var/tmp/git-meta/ for later use by the ./packer-scripts/packer-env-dump
script.
A git-tracked file in ./packer-assets is copied onto the provisioned machine
at /var/tmp/purge.txt for later use by the ./packer-scripts/purge script.
A git-tracked file in ./packer-assets is copied onto the provisioned machine
at /var/tmp/packages.txt for later use by both the
travis_packer_templates::default recipe and the serverspec suites via
./cookbooks/lib/support.rb.
The script at ./packer-scripts/packer-env-dump creates a directory on the
provisioned machine at /.packer-env which is intended to be in the envdir
format. Any environment variables
that match ^(PACKER|TRAVIS), and (if present) the files previously written to
/var/tmp/git-meta/ are copied or written into /.packer-env/.
The script at ./packer-scripts/remove-default-users will perform a best-effort
removal of users defined in ${DEFAULT_USERS} (default vagrant ubuntu). The
primary reasons for this are general tidyness and to try to free up uid 2000.
The script at ./packer-scripts/pre-chef-bootstrap is responsible for ensuring
the provisioned machine has all necessary packages and users for the Chef
provisioning process. The steps executed include:
- remove the "partner" APT source list file
- remove all cached APT list files
- install APT packages needed by Chef
- ensure
/var/run/sshddir exists - ensure
sshd: ALL: ALLOWexists in/etc/hosts.allow - ensure there is a
travisuser - change the
travisuser password totravis - ensure
#includedir /etc/sudoers.dexists in/etc/sudoers - ensure the
/etc/sudoers.ddir exists - ensure the
/etc/sudoers.d/travisfile exists with specific permissions - ensure the
/home/travis/.sshdir exists - ensure the
/home/travis/.ssh/authorized_keysfile exists - add
/var/tmp/*_rsa.pubto/home/travis/.ssh/authorized_keys - ensure
/home/travis/.ssh/authorized_keysperms are0600 - ensure the
/home/travis/bindir exists
The script at ./packer-scripts/clone-travis-cookbooks is responsible for git clone'ing travis-cookbooks
into /tmp/chef-stuff on the provisioned machine. Optional env vars supported
by this script are:
TRAVIS_COOKBOOKS_BRANCH- the branch specified duringgit cloneTRAVIS_COOKBOOKS_EDGE_BRANCH- the default branch used ifTRAVIS_COOKBOOKS_BRANCHis not definedTRAVIS_COOKBOOKS_URL- the git clone remote (defaulthttps://github.com/travis-ci/travis-cookbooks.git)TRAVIS_COOKBOOKS_SHA- a git tree-ish to which the clone will be checked out if defined (default not set)
Once the clone is complete, the clone directory is written to
/.packer-env/TRAVIS_COOKBOOKS_DIR and the head sha is written to
/.packer-env/TRAVIS_COOKBOOKS_SHA.
The chef-solo provisioner will typically have no json data, but instead will
leave all attribute and effective run list definition to a single wrapper
cookbook located in ./cookbooks/.
Each wrapper cookbook must contain at least a metadata.rb and a
recipes/default.rb. Typically, the attributes/default.rb is defined and
contains all override attribute settings. The earliest version of Chef used by
either trusty or xenial stacks is 12.9, which means that all cookbook
dependencies must be declared in metadata.rb, a requirement that is also
enforced by the foodcritic checks.
For example, the minimal trusty image "ci-stevonnie" has a wrapper cookbook at
./cookbooks/travis-ci_stevonnie that looks like this:
cookbooks/travis_ci_stevonnie
├── README.md
├── attributes
│ └── default.rb
├── metadata.rb
├── recipes
│ └── default.rb
└── spec
├── ...
The script at ./packer-scripts/ensure-travis-user is responsible for ensuring
the existence of the travis user and its home directory permissions,
optionally setting the password to a random string. The list of operations is:
- ensure the
travisuser exists - set the
travisuser password - ensure
/home/travisexists - ensure
/home/travis/.ssh/authorized_keysand/home/travis/.ssh/known_hostsboth exist and have permissions of0600 - blank out
/home/travis/.ssh/authorized_keys - ensure
/home/travisis fully owned bytravis:travis
Optional env vars supported by this script are:
TRAVIS_USER_PASSWORD- a string (default "travis")TRAVIS_OBFUSCATE_PASSWORD- if non-empty, causesTRAVIS_USER_PASSWORDto be set to a random string
The script at ./packer-scripts/purge is responsible for purging packages that
are not desirable for the CI environment, such as the Chef that was installed
prior for the Chef provisioner. Additionally, any package names present in
/var/tmp/purge.txt will be purged. Optional env vars supported by this script
are:
APT_GET_UPGRADE_DURING_CLEANUP- if non-empty, triggers anapt-get -y upgradeprior to package purging.CLEAN_DEV_PACKAGES- if non-empty, purges any packages matching-dev$
The script at ./packer-scripts/disable-apparmor is responsible for disabling
apparmor if detected. This is done primarily so that services such as
PostgreSQL and Docker may be used in the CI environment without first updating
apparmor configuration and restaring said services.
The script at ./packer-scripts/run-serverspecs is responsible for running the
serverspec suites via the rspec executable that is part of the chefdk package.
The list of operations is:
- install the
chefdkpackage - create a
sudo-bashwrapper for use in some specs - ensure all spec files are owned by
travis:travis - run each suite defined in
${SPEC_SUITES} - optionally remove the
chefdkpackage
Optional env vars supported by this script are:
PACKER_CHEF_PREFIX- directory in which to find packer chef stuff (default/tmp)SPEC_RUNNER- string used to wrap execution of rspec (defaultsudo -u travis HOME=/home/travis -- bash -lc)SPEC_SUITES- comma-delimited string of spec suites to run (default not set)SKIP_CHEFDK_REMOVAL- if non-empty do not remove thechefdkpackage and APT source
The script at ./packer-scripts/cleanup is responsible for removing files and
directories that are unnecessary for the CI environment or otherwise add
unnecessary mass to the mastered image. The list of operations is:
- recursively remove a bunch of files and directories
- conditionally remove
/var/lib/apt/lists/* - conditionally remove
/var/lib/man-db - conditionally remove
/home/travis/linux.isoand/home/travis/shutdown.sh - empty all files in
/var/log
Optional env vars supported by this script are:
CLEANUP_APT_LISTS- if non-empty, trigger removal of/var/lib/apt/lists/*CLEANUP_MAN_DB- if non-empty, trigger removal of/var/lib/man-db
The script at ./packer-scripts/minimize is responsible for reducing the size
of the provisioned image by squeezing out all of the empty space into a
contiguous area using the same method as
bento.
The list of operations is:
- exit
0if$PACKER_BUILDER_TYPEis eithergooglecomputeoramazon-ebs, as minimizing like this is superfluous on those builders - if
$PACKER_BUILDER_TYPEis notdocker, turn off swap and zero out the swap partition if available. - write zeros to
/EMPTYuntil the disk is out of space - remove
/EMPTYand runsync - if the
vmware-toolbox-cmdis available, run disk shrink operations for both//bootpaths.
The script at ./bin/job-board-register is responsible for "registering" the
mastered image in a post-processing step by making an HTTP request to the
job-board images API. The list of
operations is:
- source any available env vars exported from the provisioned VM
- dump any env vars with prefixes
^(PACKER|TRAVIS|TAGS|IMAGE_NAME) - define a
TAGSenv var that will be used as thetagsHTTP request param. - define a URI-escaped query string from several env vars
- perform the HTTP request to job-board with
curland pipe the response throughjq
Required env vars for this script are:
JOB_BOARD_IMAGES_URL- the URL includingPATH_INFOprefix to job-boardIMAGE_NAME- the name of the image, typically the same as that used by the target infrastructure
Optional env vars supported by this script are:
PACKER_ENV_DIR- path to the envdir containing packer-specific env vars, default/.packer-envTAGS- initial value for tags set during job-board registrationGROUP- value used ingrouptag, defaultedgeif edge conditions match, elsedevDIST- value used indisttag, default either Linux release codename or OS X product versionOS- value used inostag, default lowercase value ofuname, mapped toosxon Darwin
For more info on the relationship between a given packer build artifact and job-board, see job-board details below.
The job-board application is
responsible for tracking stack image metadata and presenting a queryable API
that is used by the travis-worker API image
selector.
As described above, each stack image is registered with job-board along with a
group, os, dist, and map of tags. When travis-worker requests a stack
image identifier, it performs a series of queries with progressively lower
specificity.
Of the values assigned to each stack image, the tags map is perhaps most
mysterious, in part because it is so loosely defined. This is intentional, as
the number of values that could be considered "tags" varies enough that
maintaining them all as individual columns would result in (opinions!) too much
overhead in the form of schema management and query complexity.
The implementation of the job-board-register
script includes a process that converts the
languages and features arrays present in /.job-board-register.yml, written
from the values present in chef attributes at
travis_packer_templates.job_board.{features,languages}, into "sets"
represented as {key} => true. For example, if a given wrapper cookbook
contains attributes like this:
override['travis_packer_templates']['job_board']['languages'] = %w(
fribble
snurp
zzz
)then the tags generated for registration with job-board would be equivalent to:
{
"language_fribble": true,
"language_snurp": true,
"language_zzz": true
}A "tagset" is the "set" (as in the type) of the "tags" applied during job-board
registration of a particular stack image, including languages and features.
At the time of this writing, both tagsets are used during serverspec runs, and
only the languages tagset is considered during selection via the job-board
API.
Because the travis-worker API image
selector
is querying job-board for stack images that match a particular language, it is
important for us to ensure reasonably consistent image selection by way of
asserting the languages values do not overlap between certain stacks (an
"exclusive" relationship). Additionally, it is important that we ensure certain
stack features are subsets of others (an "inclusive" relationship).
Part of the CI process for this repository makes assertions about such exclusive and inclusive relationships by way of the check-job-board-tags script. The exact relationships being enforced may be viewed like so:
./bin/check-job-board-tags --list-onlyAn exclusive tagset relationship is equivalent to asserting that the set intersection is the empty set, e.g.:
tagset_a = %w(a b c)
tagset_b = %w(d e f)
assert (tagset_a & tagset_b).empty?An inclusive tagset relationship is equivalent to asserting that all members of one tagset are present in another, or that a tagset's intersection with its superset is equivalent to itself, e.g.:
tagset_a = %w(a b c d e f)
tagset_b = %w(f d b)
assert (tagset_a & tagset_b).sort == tagset_b.sortWhen submitting changes to this repository, please be aware that the top level-specs are shallow and don't include logic changes in the cookbooks.
Any cookbook specs are ran as part of the actual image building
process, which is triggered when any of the ci-<image-name>.yml
templates are modified.
The image build is ran as part of the packer-build repo on the branch corresponding to each template and is triggered by travis-packer-build.
This can be installed and invoked locally by running bundle install
and then bundle exec travis-packer-build [options].
Example:
bundle exec travis-packer-build \
-I ci-sardonyx.yml \
--target-repo-slug="travis-infrastructure/packer-build" \
--github-api-token="<your-token-here>" \
--body-tmpl=".packer-build-pull-request-false-tmpl.yml"
You can specify the branch using -B (if you don't want to build from master).
The file .packer-build-pull-request-false-tmpl.yml here is just an
example, but you can also create a different template that specifies
other travis-cookbooks or packer-template branches.
Additionaly, if you just want to test a change in
travis-cookbooks, you
can use the shortcut script in ./bin/packer-build-cookbooks-branch:
./bin/packer-build-cookbooks-branch <travis-cookbooks-branch-name> <template-name>
Note: The above script expects the GITHUB_API_TOKEN
environment variable to be set.
Once created, the images will be registered in job-board under the
group: dev tag.