|
#!/usr/bin/env ruby |
|
|
|
require 'rubygems' |
|
require 'thor' |
|
|
|
class Vagrantify < Thor |
|
include Thor::Actions |
|
|
|
desc "init VM_NAME [options]", "Initializes a Vagrant VM Project" |
|
method_option :bundler, :type => :boolean, :default => true, |
|
:desc => "Creates Gemfile and runs bundle install" |
|
method_option :git, :type => :boolean, :default => true, |
|
:desc => "Creates a .gitignore file and runs git init" |
|
method_option :chef, :type => :boolean, :default => true, |
|
:desc => "Creates a chef-repo suitable for Chef cookbook development" |
|
method_option :box, :type => :string, |
|
:desc => 'Default box name' |
|
def init(name) |
|
@vm_name = name |
|
@chef_repo = options[:chef] ? "#{vm_name}/chef-repo" : nil |
|
|
|
empty_directory vm_name |
|
install_vagrant_butter |
|
bundlerize if options[:bundler] |
|
git_init if options[:git] |
|
create_chef_repo if options[:chef] |
|
create_vagrantfile(options[:box]) |
|
end |
|
|
|
private |
|
|
|
attr_reader :vm_name |
|
attr_reader :chef_repo |
|
|
|
def bundlerize |
|
create_gemfile |
|
install_bundler |
|
inside vm_name do |
|
run "bundle install" |
|
end |
|
end |
|
|
|
def create_gemfile |
|
create_file "#{vm_name}/Gemfile", <<-GEMFILE.gsub(/^ {6}/, '') |
|
source "http://rubygems.org" |
|
|
|
gem 'librarian' |
|
GEMFILE |
|
end |
|
|
|
def install_bundler |
|
run "gem install bundler --pre" unless %x[gem list --no-versions] =~ /^bundler$/ |
|
end |
|
|
|
def install_vagrant_butter |
|
unless %x[vagrant gem list --no-versions] =~ /^vagrant-butter$/ |
|
run "vagrant gem install vagrant-butter" |
|
end |
|
end |
|
|
|
def create_chef_repo |
|
empty_directory chef_repo |
|
%w{.chef cookbooks data_bags roles tmp}.each do |dir| |
|
empty_directory "#{chef_repo}/#{dir}" |
|
end |
|
|
|
create_rvmrc |
|
create_knife_rb |
|
create_cheffile |
|
create_user_json |
|
inside chef_repo do |
|
run "librarian-chef install --clean" |
|
end |
|
end |
|
|
|
def create_rvmrc |
|
create_file "#{chef_repo}/.rvmrc", <<-RVMRC.gsub(/^ {6}/, '') |
|
#!/usr/bin/env bash |
|
|
|
# This is an RVM Project .rvmrc file, used to automatically load the ruby |
|
# development environment upon cd'ing into the directory |
|
|
|
# First we specify our desired <ruby>[@<gemset>], the @gemset name is optional. |
|
environment_id="default" |
|
|
|
# |
|
# Uncomment following line if you want options to be set only for given project. |
|
# |
|
# PROJECT_JRUBY_OPTS=( --1.9 ) |
|
|
|
# |
|
# First we attempt to load the desired environment directly from the environment |
|
# file. This is very fast and efficient compared to running through the entire |
|
# CLI and selector. If you want feedback on which environment was used then |
|
# insert the word 'use' after --create as this triggers verbose mode. |
|
# |
|
if [[ -d "${rvm_path:-$HOME/.rvm}/environments" \ |
|
&& -s "${rvm_path:-$HOME/.rvm}/environments/$environment_id" ]] |
|
then |
|
\. "${rvm_path:-$HOME/.rvm}/environments/$environment_id" |
|
|
|
if [[ -s "${rvm_path:-$HOME/.rvm}/hooks/after_use" ]] |
|
then |
|
. "${rvm_path:-$HOME/.rvm}/hooks/after_use" |
|
fi |
|
else |
|
# If the environment file has not yet been created, use the RVM CLI to select. |
|
if ! rvm --create "$environment_id" |
|
then |
|
echo "Failed to create RVM environment '${environment_id}'." |
|
exit 1 |
|
fi |
|
fi |
|
|
|
|
|
for f in ${HOME}/.chef.d/env-opscode.sh ${HOME}/.aws/env.sh ; do |
|
if [[ -f "${f}" ]] ; then |
|
source "$f" |
|
fi |
|
done ; unset f |
|
RVMRC |
|
end |
|
|
|
def create_cheffile |
|
create_file "#{chef_repo}/Cheffile", <<-CHEFFILE.gsub(/^ {6}/, '') |
|
#!/usr/bin/env ruby |
|
|
|
site 'http://community.opscode.com/api/v1' |
|
|
|
cookbook 'solo_data_bags' |
|
cookbook 'user' |
|
CHEFFILE |
|
end |
|
|
|
def create_knife_rb |
|
create_file "#{chef_repo}/.chef/knife.rb", <<-'KNIFE_RB'.gsub(/^ {6}/, '') |
|
### Chef Solo |
|
# |
|
# |
|
### Opscode Hosted Chef Server |
|
# |
|
# export KNIFE_USER="jdoe" |
|
# export KNIFE_ORGNAME="acmeco" |
|
# |
|
# * Your Opscode client key should be at `~/.chef.d/opscode-jdoe.pem`. |
|
# * Your Opscode validation key should be at `~/.chef.d/opscode-acmeco-validator.pem`. |
|
# |
|
### Chef Server |
|
# |
|
# export KNIFE_USER="hsolo" |
|
# export KNIFE_SERVER_NAME="widgetinc" |
|
# export KNIFE_SERVER_URL="https://chef.widgetinc.com" |
|
# |
|
# * Your Chef Server client key should be at `~/.chef.d/widgetinc-hsolo.pem`. |
|
# * Your Chef Server validation key should be at `~/.chef.d/widgetinc-validator.pem`. |
|
# |
|
|
|
current_dir = File.dirname(__FILE__) |
|
home_dir = ENV['HOME'] |
|
chef_dir = "#{home_dir}/.chef.d" |
|
|
|
user = ENV['KNIFE_USER'] || ENV['USER'] |
|
orgname = ENV['KNIFE_ORGNAME'] |
|
server_name = ENV['KNIFE_SERVER_NAME'] |
|
server_url = ENV['KNIFE_SERVER_URL'] |
|
|
|
# path to cookbooks |
|
cookbook_path ["#{current_dir}/../cookbooks", |
|
"#{current_dir}/../site-cookbooks"] |
|
|
|
# logging details |
|
log_level :info |
|
log_location STDOUT |
|
|
|
# user/client and private key to authenticate to a Chef Server, if needed |
|
node_name user |
|
|
|
if orgname |
|
# if KNIFE_ORGNAME is given, then we're talking to the Opscode Hosted Chef |
|
# Server |
|
validation_client_name "#{orgname}-validator" |
|
client_key "#{chef_dir}/opscode-#{user}.pem" |
|
validation_key "#{chef_dir}/opscode-#{orgname}-validator.pem" |
|
chef_server_url "https://api.opscode.com/organizations/#{orgname}" |
|
elsif server_name |
|
# if KNIFE_SERVER_NAME is defined, then we're talking to a Chef Server |
|
validation_client_name "#{server_name}-validator" |
|
client_key "#{chef_dir}/#{server_name}-#{user}.pem" |
|
validation_key "#{chef_dir}/#{server_name}-validator.pem" |
|
chef_server_url server_url |
|
end |
|
|
|
# caching options |
|
cache_type 'BasicFile' |
|
cache_options( :path => "#{home_dir}/.chef/checksums" ) |
|
|
|
# new cookbook defaults |
|
cookbook_copyright ENV['KNIFE_COOKBOOK_COPYRIGHT'] || |
|
%x{git config --get user.name}.chomp |
|
cookbook_email ENV['KNIFE_COOKBOOK_EMAIL'] || |
|
%x{git config --get user.email}.chomp |
|
cookbook_license "apachev2" |
|
KNIFE_RB |
|
end |
|
|
|
def create_user_json |
|
json_file = "#{chef_repo}/data_bags/users/#{ENV['USER']}.json" |
|
|
|
empty_directory File.dirname(json_file) |
|
create_file json_file, %[{ "id": "#{ENV['USER']}" }] |
|
end |
|
|
|
def create_vagrantfile(default_box = nil) |
|
create_file "#{vm_name}/Vagrantfile", <<-VAGRANTFILE.gsub(/^ {6}/, '') |
|
#!/usr/bin/env ruby |
|
# coding: utf-8 |
|
|
|
include Vagrant::Butter::Helpers |
|
|
|
Vagrant::Config.run do |config| |
|
|
|
#{vm_boxes(default_box)} |
|
|
|
# config.vm.customize ["modifyvm", :id, "--memory", "256"] |
|
#{chef_solo_provisioner || ''} |
|
end |
|
VAGRANTFILE |
|
end |
|
|
|
def vm_boxes(default_box = nil) |
|
other_boxes = %x[vagrant box list].split("\n").sort |
|
default_box ||= other_boxes.shift |
|
|
|
boxes = Array(%{ config.vm.box = "#{default_box}"}) |
|
other_boxes.each { |box| boxes << %{ # config.vm.box = "#{box}"} } |
|
boxes.join("\n") |
|
end |
|
|
|
def chef_solo_provisioner |
|
if options[:chef] |
|
<<-PROVISION.gsub(/^ {6}/, '') |
|
|
|
config.vm.provision :chef_solo do |chef| |
|
chef.cookbooks_path = "chef-repo/cookbooks" |
|
chef.roles_path = "chef-repo/roles" |
|
chef.data_bags_path = "chef-repo/data_bags" |
|
|
|
chef.log_level = :debug if ENV['vdb'] |
|
|
|
# patches |
|
chef.add_recipe "solo_data_bags" |
|
|
|
# LWRPs |
|
chef.add_recipe "user" |
|
|
|
# recipes |
|
# chef.add_recipe "apt::cacher-client" |
|
# chef.add_recipe "apt" |
|
# chef.add_recipe "user::data_bag" |
|
|
|
chef.json = { |
|
# 'apt' => { |
|
# 'cacher_ipaddress' => local_ip |
|
# }, |
|
# 'users' => [ |
|
# '#{ENV['USER']}' |
|
# ] |
|
} |
|
end |
|
|
|
PROVISION |
|
end |
|
end |
|
|
|
def git_init |
|
create_file "#{vm_name}/.gitignore", <<-GITIGNORE.gsub(/^ {6}/, '') |
|
/.vagrant |
|
/Gemfile.lock |
|
/chef-repo/cookbooks/ |
|
/chef-repo/tmp/ |
|
GITIGNORE |
|
inside vm_name do |
|
run "git init" unless File.directory? ".git" |
|
end |
|
end |
|
end |
|
|
|
Vagrantify.start |
@calavera Sweet, thanks! So glad these are git repos underneath--"pull request" merged :)