boxes 2.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +13 -0
- data/.ruby-version +1 -0
- data/CHANGELOG.md +9 -0
- data/Gemfile +3 -0
- data/LICENSE +22 -0
- data/README.md +51 -0
- data/Rakefile +34 -0
- data/bin/boxes +6 -0
- data/boxes.gemspec +39 -0
- data/features/boxes.feature +8 -0
- data/features/build.feature +16 -0
- data/features/env.feature +18 -0
- data/features/support/env.rb +1 -0
- data/lib/boxes.rb +28 -0
- data/lib/boxes/builder.rb +77 -0
- data/lib/boxes/command.rb +12 -0
- data/lib/boxes/command/build.rb +45 -0
- data/lib/boxes/command/env.rb +50 -0
- data/lib/boxes/config.rb +73 -0
- data/lib/boxes/environment.rb +74 -0
- data/lib/boxes/errors.rb +19 -0
- data/lib/boxes/subprocess.rb +39 -0
- data/lib/boxes/template.rb +50 -0
- data/lib/boxes/version.rb +4 -0
- data/scripts/ansible.sh +13 -0
- data/scripts/chef.sh +28 -0
- data/scripts/postinstall.sh +42 -0
- data/scripts/puppet.sh +14 -0
- data/scripts/purge.sh +60 -0
- data/scripts/ruby.sh +41 -0
- data/scripts/vmtools.sh +32 -0
- data/spec/boxes/builder_spec.rb +52 -0
- data/spec/boxes/config_spec.rb +142 -0
- data/spec/boxes/environment_spec.rb +73 -0
- data/spec/boxes/subprocess_spec.rb +35 -0
- data/spec/boxes/template_spec.rb +53 -0
- data/spec/spec_helper.rb +5 -0
- data/spec/support/subprocess_command.rb +7 -0
- data/templates/debian/preseed.cfg +69 -0
- data/templates/debian/wheezy64.erb +59 -0
- data/templates/ubuntu/precise64.erb +59 -0
- data/templates/ubuntu/preseed.cfg +61 -0
- data/templates/ubuntu/trusty64.erb +59 -0
- metadata +230 -0
data/lib/boxes/config.rb
ADDED
@@ -0,0 +1,73 @@
|
|
1
|
+
module Boxes
|
2
|
+
# Stores the configuration for Boxes.
|
3
|
+
class Config
|
4
|
+
# The default settings for the configuration.
|
5
|
+
DEFAULTS = {
|
6
|
+
environment_vars: [
|
7
|
+
{ 'PACKER_CACHE_DIR' => (
|
8
|
+
Pathname.new(ENV['BOXES_HOME_DIR'] || '~/.boxes'
|
9
|
+
).expand_path + 'packer_cache') }
|
10
|
+
],
|
11
|
+
template_paths: [
|
12
|
+
# the gem install directory
|
13
|
+
File.expand_path('../../../templates', __FILE__)
|
14
|
+
],
|
15
|
+
script_paths: [
|
16
|
+
# the gem install directory
|
17
|
+
File.expand_path('../../../scripts', __FILE__)
|
18
|
+
]
|
19
|
+
}
|
20
|
+
|
21
|
+
# The directory which boxes works out of.
|
22
|
+
def home_dir
|
23
|
+
@home_dir ||= Pathname.new(
|
24
|
+
ENV['BOXES_HOME_DIR'] || '~/.boxes').expand_path
|
25
|
+
end
|
26
|
+
|
27
|
+
# The directory inside the `home_dir` which boxes runs builds inside of.
|
28
|
+
def working_dir
|
29
|
+
@working_dir ||= Pathname.new(
|
30
|
+
ENV['BOXES_WORKING_DIR'] || home_dir + 'tmp').expand_path
|
31
|
+
end
|
32
|
+
|
33
|
+
# Paths known to boxes for discovering templates.
|
34
|
+
attr_accessor :template_paths
|
35
|
+
|
36
|
+
# Paths known to boxes for discovering scripts.
|
37
|
+
attr_accessor :script_paths
|
38
|
+
|
39
|
+
# A Hash of environment variables Boxes sets in the run environment.
|
40
|
+
attr_accessor :environment_vars
|
41
|
+
|
42
|
+
def initialize
|
43
|
+
configure_with(DEFAULTS)
|
44
|
+
|
45
|
+
return unless user_settings_file.exist?
|
46
|
+
|
47
|
+
user_settings = YAML.load_file(user_settings_file)
|
48
|
+
configure_with(user_settings)
|
49
|
+
end
|
50
|
+
|
51
|
+
private
|
52
|
+
|
53
|
+
def user_settings_file
|
54
|
+
home_dir + 'config.yml'
|
55
|
+
end
|
56
|
+
|
57
|
+
def configure_with(opts = {}) # rubocop:disable Metrics/MethodLength
|
58
|
+
opts.each do |k, v|
|
59
|
+
next unless respond_to?("#{k}=")
|
60
|
+
|
61
|
+
if v.class == Array
|
62
|
+
v.each do |e|
|
63
|
+
set = Set.new(send("#{k}".to_sym))
|
64
|
+
set << e
|
65
|
+
send("#{k}=".to_sym, set.to_a)
|
66
|
+
end
|
67
|
+
else
|
68
|
+
send("#{k}=".to_sym, v)
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
@@ -0,0 +1,74 @@
|
|
1
|
+
module Boxes
|
2
|
+
# For creating and managing the environment which boxes uses.
|
3
|
+
class Environment
|
4
|
+
def initialize
|
5
|
+
FileUtils.mkdir_p(Boxes.config.working_dir)
|
6
|
+
|
7
|
+
copy_templates
|
8
|
+
copy_scripts
|
9
|
+
end
|
10
|
+
|
11
|
+
def available_templates
|
12
|
+
t = Dir.glob("#{Boxes.config.working_dir}/templates/*/**")
|
13
|
+
a = t.collect { |c| c.include?('preseed.cfg') ? next : c }.compact
|
14
|
+
|
15
|
+
a.collect do |c|
|
16
|
+
c = c.gsub(Boxes.config.working_dir.to_s + '/templates/', '')
|
17
|
+
c.gsub('.erb', '')
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def hidden_templates
|
22
|
+
t = Dir.glob("#{Boxes.config.working_dir}/templates/*/**")
|
23
|
+
a = t.collect { |c| c.include?('preseed.cfg') ? c : next }.compact
|
24
|
+
|
25
|
+
a.collect do |c|
|
26
|
+
c.gsub(Boxes.config.working_dir.to_s + '/templates/', '')
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def available_scripts
|
31
|
+
t = Dir.glob("#{Boxes.config.working_dir}/scripts/*")
|
32
|
+
a = t.collect { |c| c.include?('purge.sh') ? next : c }.compact
|
33
|
+
|
34
|
+
a.collect do |c|
|
35
|
+
c.gsub(Boxes.config.working_dir.to_s + '/scripts/', '')
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def hidden_scripts
|
40
|
+
t = Dir.glob("#{Boxes.config.working_dir}/scripts/*")
|
41
|
+
a = t.collect { |c| c.include?('purge.sh') ? c : next }.compact
|
42
|
+
|
43
|
+
a.collect do |c|
|
44
|
+
c.gsub(Boxes.config.working_dir.to_s + '/scripts/', '')
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
private
|
49
|
+
|
50
|
+
def copy_templates
|
51
|
+
templates_dir = Boxes.config.working_dir + 'templates'
|
52
|
+
|
53
|
+
FileUtils.mkdir_p(templates_dir)
|
54
|
+
|
55
|
+
Boxes.config.template_paths.each do |template_path|
|
56
|
+
Dir.glob("#{template_path}/*").each do |src_template|
|
57
|
+
FileUtils.cp_r(src_template, templates_dir)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
def copy_scripts
|
63
|
+
scripts_dir = Boxes.config.working_dir + 'scripts'
|
64
|
+
|
65
|
+
FileUtils.mkdir_p(Boxes.config.working_dir + 'scripts')
|
66
|
+
|
67
|
+
Boxes.config.script_paths.each do |script_path|
|
68
|
+
Dir.glob("#{script_path}/*").each do |src_script|
|
69
|
+
FileUtils.cp_r(src_script, scripts_dir)
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
data/lib/boxes/errors.rb
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
module Boxes
|
2
|
+
# A collection of errors which can be raised by boxes.
|
3
|
+
module Errors
|
4
|
+
# Base error class for all other errors.
|
5
|
+
class BoxesError < StandardError; end
|
6
|
+
|
7
|
+
# Raised when a template is missing.
|
8
|
+
class TemplateNotFoundError < BoxesError; end
|
9
|
+
|
10
|
+
# Raised when a script is missing.
|
11
|
+
class ScriptNotFoundError < BoxesError; end
|
12
|
+
|
13
|
+
# Raised when an expected argument is missing.
|
14
|
+
class MissingArgumentError < BoxesError; end
|
15
|
+
|
16
|
+
# Raised when a build fails.
|
17
|
+
class BuildRunError < BoxesError; end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
module Boxes
|
2
|
+
# Standardise handling the stdout and stderr from Open3.
|
3
|
+
#
|
4
|
+
# @example Print the values returned to stdout and stderr
|
5
|
+
# Boxes::Utils::Subprocess.run 'ls' do |stdout, stderr, thread|
|
6
|
+
# puts stdout unless stdout == nil
|
7
|
+
# puts stderr unless stderr == nil
|
8
|
+
# end
|
9
|
+
#
|
10
|
+
class Subprocess
|
11
|
+
# Create a new subprocess with a command, with a block for the response.
|
12
|
+
#
|
13
|
+
# @param cmd [String] the command to run
|
14
|
+
# @yield [stdout, stderr, thread] Gives the stdout, stderr and process
|
15
|
+
# thread to the block.
|
16
|
+
def self.run(command) # rubocop:disable Metrics/MethodLength
|
17
|
+
# see: http://stackoverflow.com/a/1162850/83386
|
18
|
+
Open3.popen3(command) do |_stdin, stdout, stderr, thread|
|
19
|
+
# read each stream from a new thread
|
20
|
+
{ out: stdout, err: stderr }.each do |key, stream|
|
21
|
+
Thread.new do
|
22
|
+
stream.each_line do |line|
|
23
|
+
# yield the block depending on the stream
|
24
|
+
if key == :out
|
25
|
+
yield line, nil, thread if block_given?
|
26
|
+
else
|
27
|
+
yield nil, line, thread if block_given?
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
thread.join # don't exit until the external process is done
|
34
|
+
|
35
|
+
thread.value
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
module Boxes
|
2
|
+
# Representations of Packer templates.
|
3
|
+
class Template
|
4
|
+
include Boxes::Errors
|
5
|
+
|
6
|
+
attr_reader :name, :template
|
7
|
+
|
8
|
+
# Load a template with a given name.
|
9
|
+
#
|
10
|
+
# @param env [Boxes::Environment] the environment to source templates.
|
11
|
+
# @param name [String] the name of the template.
|
12
|
+
#
|
13
|
+
# @return [Boxes::Template] a template instance.
|
14
|
+
def initialize(env, name)
|
15
|
+
fail(TemplateNotFoundError) unless env.available_templates.include?(name)
|
16
|
+
|
17
|
+
@name = name
|
18
|
+
@template = ''
|
19
|
+
File.open(Boxes.config.working_dir + "templates/#{name}.erb") do |f|
|
20
|
+
@template << f.read
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
# Render the template.
|
25
|
+
#
|
26
|
+
# @param args [Hash] the values to set.
|
27
|
+
#
|
28
|
+
# @return [String] the rendered template.
|
29
|
+
def render(args)
|
30
|
+
ERB.new(template, nil, '-').result(ERBContext.new(args).get_binding)
|
31
|
+
end
|
32
|
+
|
33
|
+
# A context to render inside, to avoid polluting other classes.
|
34
|
+
class ERBContext
|
35
|
+
# Create a new context with a given hash of values.
|
36
|
+
#
|
37
|
+
# @params args [Hash] the values to substitute.
|
38
|
+
def initialize(args = {})
|
39
|
+
args.each_pair do |k, v|
|
40
|
+
instance_variable_set('@' + k.to_s, v)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
# The binding which is passed to ERB.
|
45
|
+
def get_binding # rubocop:disable Style/AccessorMethodName
|
46
|
+
binding
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
data/scripts/ansible.sh
ADDED
data/scripts/chef.sh
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
#!/bin/bash
|
2
|
+
|
3
|
+
# Get the Chef package version through their metadata service.
|
4
|
+
platform=$(lsb_release -si | tr '[:upper:]' '[:lower:]')
|
5
|
+
release=$(lsb_release -sr)
|
6
|
+
version_url="https://www.opscode.com/chef/metadata?v=&prerelease=false&nightlies=false&p=$platform&pv=$release&m=x86_64"
|
7
|
+
current_version=$(curl -s "$version_url")
|
8
|
+
|
9
|
+
version_url=$(echo "$current_version" | awk '/url/{print $2}')
|
10
|
+
version_sha=$(echo "$current_version" | awk '/sha256/{print $2}')
|
11
|
+
|
12
|
+
# fetch chef
|
13
|
+
curl $version_url -o chef.deb
|
14
|
+
|
15
|
+
# check the file
|
16
|
+
echo "$version_sha chef.deb" > '/tmp/chef-checksum'
|
17
|
+
shasum -a 256 -c '/tmp/chef-checksum'
|
18
|
+
if [ $? -ne 0 ]; then
|
19
|
+
echo "Downloaded Chef package failed to checksum."
|
20
|
+
exit 1
|
21
|
+
fi
|
22
|
+
|
23
|
+
# install
|
24
|
+
dpkg -i chef.deb
|
25
|
+
|
26
|
+
# cleanup
|
27
|
+
rm chef.deb
|
28
|
+
rm /tmp/chef-checksum
|
@@ -0,0 +1,42 @@
|
|
1
|
+
# postinstall.sh based upon Mitchell's old basebox example
|
2
|
+
|
3
|
+
# mark the build time
|
4
|
+
date > /etc/vagrant_box_build_time
|
5
|
+
|
6
|
+
# update the apt cache and packages
|
7
|
+
case $(lsb_release -cs) in
|
8
|
+
'precise')
|
9
|
+
apt-get clean
|
10
|
+
rm -rf /var/lib/apt/lists/*
|
11
|
+
apt-get clean
|
12
|
+
;;
|
13
|
+
*)
|
14
|
+
;;
|
15
|
+
esac
|
16
|
+
|
17
|
+
apt-get -qy update
|
18
|
+
apt-get -qy upgrade
|
19
|
+
|
20
|
+
# install some oft used packages
|
21
|
+
apt-get -qy install linux-headers-$(uname -r) build-essential
|
22
|
+
apt-get -qy install zlib1g-dev libssl-dev
|
23
|
+
apt-get -qy install python-software-properties python-setuptools python-dev
|
24
|
+
apt-get -qy install ruby1.9.3
|
25
|
+
|
26
|
+
# configure password-less sudo
|
27
|
+
usermod -a -G sudo vagrant
|
28
|
+
echo "%vagrant ALL=NOPASSWD:ALL" > /tmp/vagrant
|
29
|
+
mv /tmp/vagrant /etc/sudoers.d/vagrant
|
30
|
+
chmod 0440 /etc/sudoers.d/vagrant
|
31
|
+
|
32
|
+
# install the vagrant-provided ssh keys
|
33
|
+
mkdir -pm 700 /home/vagrant/.ssh
|
34
|
+
curl -Lo /home/vagrant/.ssh/authorized_keys \
|
35
|
+
'https://raw.github.com/mitchellh/vagrant/master/keys/vagrant.pub'
|
36
|
+
chmod 0600 /home/vagrant/.ssh/authorized_keys
|
37
|
+
chown -R vagrant:vagrant /home/vagrant/.ssh
|
38
|
+
|
39
|
+
# clean up any artifacts
|
40
|
+
rm -f /home/vagrant/shutdown.sh
|
41
|
+
|
42
|
+
exit
|
data/scripts/puppet.sh
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/bin/bash
|
2
|
+
|
3
|
+
# see: http://docs.puppetlabs.com/guides/puppetlabs_package_repositories.html
|
4
|
+
|
5
|
+
# determine the os release
|
6
|
+
os_release=$(lsb_release -cs)
|
7
|
+
|
8
|
+
# configure the puppet package sources
|
9
|
+
wget http://apt.puppetlabs.com/puppetlabs-release-$os_release.deb
|
10
|
+
dpkg -i puppetlabs-release-$os_release.deb
|
11
|
+
apt-get -q update
|
12
|
+
|
13
|
+
# install puppet
|
14
|
+
apt-get install -qy puppet
|
data/scripts/purge.sh
ADDED
@@ -0,0 +1,60 @@
|
|
1
|
+
#!/bin/bash
|
2
|
+
|
3
|
+
##
|
4
|
+
# Purge unnecessary data from the image to keep it small.
|
5
|
+
#
|
6
|
+
# Based on: https://gist.github.com/adrienbrault/3775253
|
7
|
+
##
|
8
|
+
|
9
|
+
# tidy up DCHP leases
|
10
|
+
echo "Cleaning up dhcp..."
|
11
|
+
rm /var/lib/dhcp/*
|
12
|
+
|
13
|
+
# make sure Udev doesn't block our network
|
14
|
+
# http://6.ptmc.org/?p=164
|
15
|
+
echo "Cleaning up udev..."
|
16
|
+
rm /etc/udev/rules.d/70-persistent-net.rules
|
17
|
+
mkdir /etc/udev/rules.d/70-persistent-net.rules
|
18
|
+
rm -rf /dev/.udev/
|
19
|
+
rm /lib/udev/rules.d/75-persistent-net-generator.rules
|
20
|
+
|
21
|
+
# clean up apt
|
22
|
+
echo "Cleaning up apt..."
|
23
|
+
apt-get -qy autoremove
|
24
|
+
apt-get clean -qy
|
25
|
+
apt-get autoclean -qy
|
26
|
+
|
27
|
+
# nuke the bash history
|
28
|
+
echo "Removing bash history..."
|
29
|
+
unset HISTFILE
|
30
|
+
rm -f /root/.bash_history
|
31
|
+
rm -f /home/vagrant/.bash_history
|
32
|
+
|
33
|
+
# clean up the logs
|
34
|
+
echo "Cleaning up logs..."
|
35
|
+
find /var/log -type f | while read f; do echo -ne '' > $f; done;
|
36
|
+
|
37
|
+
# zero any and all free space
|
38
|
+
echo "Cleaning free space..."
|
39
|
+
dd if=/dev/zero of=/EMPTY bs=1M
|
40
|
+
rm -f /EMPTY
|
41
|
+
|
42
|
+
# whiteout root
|
43
|
+
echo "Cleaning up /..."
|
44
|
+
#count=`df --sync -kP / | tail -n1 | awk -F ' ' '{print $4}'`;
|
45
|
+
dd if=/dev/zero of=/tmp/whitespace bs=1024;
|
46
|
+
rm /tmp/whitespace;
|
47
|
+
|
48
|
+
# whiteout /boot
|
49
|
+
echo "Cleaning up /boot..."
|
50
|
+
#count=`df --sync -kP /boot | tail -n1 | awk -F ' ' '{print $4}'`;
|
51
|
+
dd if=/dev/zero of=/boot/whitespace bs=1024;
|
52
|
+
rm /boot/whitespace;
|
53
|
+
|
54
|
+
# whiteout the swap
|
55
|
+
echo "Cleaning up swap partitions..."
|
56
|
+
swappart=`cat /proc/swaps | tail -n1 | awk -F ' ' '{print $1}'`
|
57
|
+
swapoff $swappart;
|
58
|
+
dd if=/dev/zero of=$swappart;
|
59
|
+
mkswap $swappart;
|
60
|
+
swapon $swappart;
|
data/scripts/ruby.sh
ADDED
@@ -0,0 +1,41 @@
|
|
1
|
+
#!/bin/bash
|
2
|
+
|
3
|
+
# this installs chruby, ruby-install and a selection of rubies and is used in
|
4
|
+
# the 'ruby' special box type.
|
5
|
+
|
6
|
+
chruby_version=0.3.9
|
7
|
+
rubyinstall_version=0.5.0
|
8
|
+
|
9
|
+
# install chruby
|
10
|
+
wget -O chruby-$chruby_version.tar.gz https://github.com/postmodern/chruby/archive/v$chruby_version.tar.gz
|
11
|
+
tar -xzvf chruby-$chruby_version.tar.gz
|
12
|
+
cd chruby-$chruby_version/
|
13
|
+
make install
|
14
|
+
|
15
|
+
# configure system-wide
|
16
|
+
cat << 'EOF' > /etc/profile.d/chruby.sh
|
17
|
+
if [ -n "$BASH_VERSION" ] || [ -n "$ZSH_VERSION" ]; then
|
18
|
+
source /usr/local/share/chruby/chruby.sh
|
19
|
+
source /usr/local/share/chruby/auto.sh
|
20
|
+
fi
|
21
|
+
EOF
|
22
|
+
|
23
|
+
# install ruby-install
|
24
|
+
wget -O ruby-install-$rubyinstall_version.tar.gz https://github.com/postmodern/ruby-install/archive/v$rubyinstall_version.tar.gz
|
25
|
+
tar -xzvf ruby-install-$rubyinstall_version.tar.gz
|
26
|
+
cd ruby-install-$rubyinstall_version/
|
27
|
+
make install
|
28
|
+
|
29
|
+
# install a set of recent MRI Rubies.
|
30
|
+
ruby-install ruby 2.1.5
|
31
|
+
ruby-install ruby 2.2.0
|
32
|
+
ruby-install ruby 2.2.1
|
33
|
+
ruby-install ruby 2.2.2
|
34
|
+
|
35
|
+
# update gems and install bundler
|
36
|
+
source /usr/local/share/chruby/chruby.sh
|
37
|
+
for ruby in `chruby`; do
|
38
|
+
chruby-exec $ruby -- gem install bundler
|
39
|
+
done
|
40
|
+
|
41
|
+
exit
|