Skip to content

Commit

Permalink
Merge branch 'feature/recipes'
Browse files Browse the repository at this point in the history
  • Loading branch information
gurgeous committed Nov 3, 2011
2 parents ed90f6c + b6d0aba commit ea14631
Show file tree
Hide file tree
Showing 6 changed files with 122 additions and 39 deletions.
55 changes: 38 additions & 17 deletions lib/teleport/config.rb
Expand Up @@ -3,13 +3,14 @@ module Teleport
class Config
RUBIES = ["1.9.3", "1.9.2", "REE", "1.8.7"]

attr_accessor :user, :ruby, :ssh_options, :roles, :servers, :apt, :packages, :callbacks, :dsl
attr_accessor :user, :ruby, :ssh_options, :roles, :servers, :apt, :packages, :recipes, :callbacks, :dsl

def initialize(file = "Telfile")
@roles = []
@servers = []
@apt = []
@packages = []
@recipes = []
@callbacks = { }

@dsl = DSL.new(self)
Expand All @@ -26,7 +27,7 @@ def role(n)
end

def server(n)
@servers.find { |i| i.name == n.to_s }
@servers.find { |i| i.name == n.to_s }
end

def sanity_check_gemfiles
Expand All @@ -42,34 +43,50 @@ def sanity_check_gemfiles

# The model for role in the Telfile.
class Role
attr_reader :name, :options, :packages
attr_reader :name, :options, :packages, :recipes

def initialize(name, options)
raise "role name must be a sym" if !name.is_a?(Symbol)
raise "role options must be a hash" if !options.is_a?(Hash)

@name, @options, @packages = name, options, []

@name, @options, @packages, @recipes = name, options, [], []

# Packages
if p = @options.delete(:packages)
raise "role :packages must be an array" if !p.is_a?(Array)
@packages = p
end

# Recipes
if r = @options.delete(:recipes)
raise "server :recipes must be an array" if !r.is_a?(Array)
@recipes = r
end
end
end

# The model for server in the Telfile.
class Server
attr_reader :name, :options, :packages
attr_reader :name, :options, :packages, :recipes

def initialize(name, options)
raise "server name must be a string" if !name.is_a?(String)
raise "server options must be a hash" if !options.is_a?(Hash)
raise "server :role must be a sym" if !options[:role].is_a?(Symbol)

@name, @options, @packages = name, options, []
raise "server :role must be a sym" if options[:role] && !options[:role].is_a?(Symbol)

@name, @options, @packages, @recipes = name, options, [], []

# Packages
if p = @options.delete(:packages)
raise "server :packages must be an array" if !p.is_a?(Array)
@packages = p
end

# Recipes
if r = @options.delete(:recipes)
raise "server :recipes must be an array" if !r.is_a?(Array)
@recipes = r
end
end
end

Expand All @@ -96,20 +113,20 @@ def initialize(config)

def ruby(v)
raise "ruby called twice" if @config.ruby
raise "ruby must be a string" if !v.is_a?(String)
raise "ruby must be a string" if !v.is_a?(String)
raise "don't recognize ruby #{v.inspect}." if !Config::RUBIES.include?(v)
@config.ruby = v
end

def user(v)
raise "user called twice" if @config.user
raise "user must be a string" if !v.is_a?(String)
raise "user must be a string" if !v.is_a?(String)
@config.user = v
end

def ssh_options(v)
raise "ssh_options called twice" if @config.ssh_options
raise "ssh_options must be an Array" if !v.is_a?(Array)
raise "ssh_options must be an Array" if !v.is_a?(Array)
@config.ssh_options = v
end

Expand All @@ -131,7 +148,11 @@ def packages(*list)
@config.packages += list.flatten
end

%w(install user packages gemfiles files).each do |op|
def recipes(*list)
@config.recipes += list.flatten
end

%w(install user packages gemfiles files recipes).each do |op|
%w(before after).each do |before_after|
callback = "#{before_after}_#{op}".to_sym
define_method(callback) do |&block|
Expand Down
47 changes: 36 additions & 11 deletions lib/teleport/install.rb
@@ -1,10 +1,10 @@
module Teleport
# Class that performs the install on the target machine.
class Install
include Constants
include Constants
include Util
include Mirror
include Mirror

def initialize(config)
@config = config
run_verbose!
Expand All @@ -16,10 +16,10 @@ def initialize(config)
Config::DSL.const_set("ROLE", @role && @role.name)

# add mixins
@config.dsl.extend(Mirror)
@config.dsl.extend(Mirror)
@config.dsl.extend(Util)
@config.dsl.run_verbose!

_with_callback(:install) do
_gems
_hostname
Expand All @@ -36,6 +36,9 @@ def initialize(config)
_with_callback(:files) do
_files
end
_with_callback(:recipes) do
_recipes
end
end
end

Expand Down Expand Up @@ -80,17 +83,39 @@ def _gems
gem_if_necessary("bundler")
end

def _recipes
list = @config.recipes
list += @role.recipes if @role
list += @server.recipes if @server

banner "Recipes..."
list.each do |recipe|
path = "#{DATA}/recipes/#{recipe}"
if File.exists?(path)
banner "#{recipe}..."
# eval ruby files instead of running them
if path =~ /\.rb$/
eval(File.read(path), nil, path)
else
run path
end
else
fatal "Recipe '#{recipe}' does not exist inside recipes/"
end
end
end

def _hostname
banner "Hostname..."

# ipv4?
return if @host =~ /^\d+(\.\d+){3}$/
# ipv6?
return if @host =~ /:/

old_hostname = `hostname`.strip
return if old_hostname == @host

puts "setting hostname to #{@host} (it was #{old_hostname})..."
File.open("/etc/hostname", "w") do |f|
f.write @host
Expand Down Expand Up @@ -123,7 +148,7 @@ def _hostname

def _create_user
user = @config.user

banner "Creating #{user} account..."
# create the account
if !File.directory?("/home/#{user}")
Expand Down Expand Up @@ -157,7 +182,7 @@ def _apt
banner "Apt..."

dirty = false

# keys
keys = @config.apt.map { |i| i.options[:key] }.compact
keys.each do |i|
Expand Down Expand Up @@ -214,7 +239,7 @@ def _files
end

protected

def _with_callback(op, &block)
if before = @config.callbacks["before_#{op}".to_sym]
before.call
Expand All @@ -241,7 +266,7 @@ def _rewrite(path, &block)
end

def _etc_hosts_regex(host)
/^([^#]+[ \t])#{Regexp.escape(host)}([ \t]|$)/
/^([^#]+[ \t])#{Regexp.escape(host)}([ \t]|$)/
end
end
end
3 changes: 2 additions & 1 deletion lib/teleport/main.rb
Expand Up @@ -77,6 +77,7 @@ def assemble_tgz
copy = []
copy << "Telfile"
copy += Dir["files*"]
copy << "recipes" if File.exists?("recipes")
copy.sort.each { |i| run("cp", ["-r", i, DATA]) }
# config.sh
File.open("#{DIR}/config", "w") do |f|
Expand Down Expand Up @@ -110,7 +111,7 @@ def ssh_tgz
"cd /tmp",
"(sudo -n echo gub > /dev/null 2> /dev/null || (echo `whoami` could not sudo. && exit 1))",
"sudo rm -rf #{DIR}",
"sudo tar xfpz #{TAR}",
"sudo tar xmfpz #{TAR}",
"sudo #{DIR}/gem/teleport/run.sh"
]
banner "ssh to #{@options[:host]} and run..."
Expand Down
20 changes: 16 additions & 4 deletions spec/end_to_end_spec.rb
Expand Up @@ -11,8 +11,8 @@
ruby "1.8.7"
ssh_options ["-o", "User=ubuntu", "-o", "StrictHostKeyChecking=no", "-o", "IdentityFile=#{ENV["TELEPORT_SSH_KEY"]}"]
role :master, :packages => %w(nginx)
server "#{$ec2_ip_address}", :role => :master, :packages => %w(strace)
role :master, :packages => %w(nginx), :recipes => %w(ruby.rb)
server "#{$ec2_ip_address}", :role => :master, :packages => %w(strace), :recipes => %w(some_command)
packages %w(atop)
before_install do
Expand All @@ -30,16 +30,28 @@
Util.run "gem list"
end
after_recipes do
puts "AFTER_RECIPES"
end
after_install do
puts "AFTER_INSTALL"
run "touch /tmp/gub.txt"
end
EOF
end

# Roles. This is clunky, unfortunately.
role(nil, "test.txt.erb" => "<%= 1+2 %>", "Gemfile" => "source 'http://rubygems.org'\ngem 'trollop'", "Gemfile.lock" => "GEM\n remote: http://rubygems.org/\n specs:\n trollop (1.16.2)\n\nPLATFORMS\n ruby\n\nDEPENDENCIES\n trollop")
role("master", "Gemfile" => "source 'http://rubygems.org'\ngem 'awesome_print'", "Gemfile.lock" => "GEM\n remote: http://rubygems.org/\n specs:\n awesome_print (0.4.0)\n\nPLATFORMS\n ruby\n\nDEPENDENCIES\n awesome_print")

# amd: commenting the Gemfiles because they are too slow. revisit for bundler 1.1?
# role(nil, "test.txt.erb" => "<%= 1+2 %>", "Gemfile" => "source 'http://rubygems.org'\ngem 'trollop'", "Gemfile.lock" => "GEM\n remote: http://rubygems.org/\n specs:\n trollop (1.16.2)\n\nPLATFORMS\n ruby\n\nDEPENDENCIES\n trollop")
# role("master", "Gemfile" => "source 'http://rubygems.org'\ngem 'awesome_print'", "Gemfile.lock" => "GEM\n remote: http://rubygems.org/\n specs:\n awesome_print (0.4.0)\n\nPLATFORMS\n ruby\n\nDEPENDENCIES\n awesome_print")
role(nil, "test.txt.erb" => "<%= 1+2 %>")

# Recipes
recipe("ruby.rb", "Util.run 'echo ruby.rb is running'")
recipe("some_command", "#!/bin/bash\necho some_command is running")

it "installs properly" do
ARGV.clear
Expand Down
13 changes: 13 additions & 0 deletions spec/support/telfile.rb
Expand Up @@ -33,5 +33,18 @@ def role(role, files)
end
end
end

def recipe(name, content)
before(:all) do
path = "recipes"
`mkdir -p #{path}`
Dir.chdir(path) do
File.open(name, "w") { |f| f.write(content) }
if name !~ /\.rb/
File.chmod 0755, name
end
end
end
end
end
end
23 changes: 17 additions & 6 deletions spec/unit/teleport/config_spec.rb
Expand Up @@ -8,7 +8,7 @@
Teleport::Config.new
end
it "defaults to the current username" do
config.user.should == `whoami`.strip
config.user.should == `whoami`.strip
end
it "defaults to the first vm in RUBIES" do
config.ruby.should == Teleport::Config::RUBIES.first
Expand All @@ -21,11 +21,12 @@
user "somebody"
ruby "1.8.7"
role :master, :packages => %w(nginx)
role :master, :packages => %w(nginx), :recipes => %w(master.rb)
role :slave, :packages => %w(memcached)
server "one", :role => :master, :packages => %w(strace)
server "two", :role => :slave, :packages => %w(telnet)
server "two", :role => :slave, :packages => %w(telnet), :recipes => %w(two.rb)
packages %w(atop)
recipes %w(global.rb)
apt "blah blah blah", :key => "123"
before_install do
Expand All @@ -37,28 +38,38 @@
end
EOF
end

let(:config) do
Teleport::Config.new
end
it "has the master role" do
config.role(:master).name.should == :master
config.role(:master).packages.should == %w(nginx)
config.role(:master).recipes.should == %w(master.rb)
end
it "has server one" do
config.server("one").name.should == "one"
config.server("one").packages.should == %w(strace)
config.server("one").recipes.should == []
end
it "has server two" do
config.server("two").name.should == "two"
config.server("two").packages.should == %w(telnet)
config.server("two").recipes.should == %w(two.rb)
end
it "has default packages" do
config.packages.should == %w(atop)
end
it "has default recipes" do
config.recipes.should == %w(global.rb)
end
it "has callbacks" do
config.callbacks[:before_install].should_not == nil
config.callbacks[:after_install].should_not == nil
config.callbacks[:after_install].should_not == nil
end
it "has an apt line" do
config.apt.first.line.should == "blah blah blah"
config.apt.first.options[:key].should == "123"
config.apt.first.options[:key].should == "123"
end
end
end

0 comments on commit ea14631

Please sign in to comment.