Netflix/aminator — pull request #113 by kvick (2013-07-09 00:10:26+00:00)
Title: Add the ability to execute scripts delivered in the package while still in the chroot

The problem this solves is that we need the ability to both deliver applications and execute Chef cookbooks at amination time. Currently, aminator can do one of, install packages or run Chef. We can't safely run chef-solo in the postinst of the package install as cookbooks that install packages will fail (since aminator has the package database locked installing the package containing the app + cookbooks).

This PR adds the ability to deliver python and shell scripts in a well known (configurable) location and have them executed after the package has been installed and the yum/apt install is finished. While running chef-solo is the primary use, we can use this to also execute any python or shell scripts that are delivered in the package. This could be helpful for users that need to do multiple steps and a simple chef-solo run or shell script will do the trick. Create a package (fpm is great for this) containing your scripts and aminate against that package.

Currently, this is used to build our Base AMI from a native package (and pristine Foundation AMI) that contains cookbooks (/var/chef/cookbooks and /var/chef/00-node-base.json). It will also be used by other applications that layer on top of the Base AMI by delivering their application files, and if needed, cookbooks in /var/chef/site-cookbooks.

Here's an example script for Ubuntu we can use for the Base AMI & app AMI builds (it will also install chef if not present)

(/var/local//var/local/install_and_run_chef_solo.py)
```python
#!/usr/bin/env python
#
# Copyright 2013 Netflix, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# Installs chef from a URL if not already installed (e.g. foundation image)
# Run chef-solo against each node-*.json file it finds in chef_dir
#
# This is intended to be used at amination time, when you have an DEB deliver the cookbooks
# + node-.json and you want chef to be run after the DEB is installed. This can't reliably
# be run in the postinst as the dpkg database is still being used to install the cookbooks
# package :)
#
# This script will be called by aminator after it's done installing the DEB package
#
# The first use of this, will be in creating the Netflix Base AMI from a pristine
# Canonical Ubuntu foundation image
#

from urllib import urlretrieve
from subprocess import call
from glob import glob

from apt.debfile import DebPackage
from apt.cache import Cache

# ugly chef config stuff
repo_url="your URL to your chef package"
chef_dir="/var/chef"
solo_config_file="/var/chef/solo.rb"
chef_solo_command='/opt/chef/bin/chef-solo'

apt_cache = Cache()

try:
chef_package = apt_cache["chef"]
print "chef is installed"
except KeyError:
print "chef is NOT installed"
urlretrieve('{0}/{1}'.format(repo_url, chef_package), chef_package)
package = DebPackage(chef_package)
package.install()

node_dir = chef_dir + '/*.json'
node_files = sorted(glob(node_dir))

print node_files

for node_file in node_files:
call([chef_solo_command, '-j', node_file, '-c', solo_config_file])
```