AutomateIt - Open source server automation
  • Home
  • About
  • Compare
  •  
  • Screenshots
  • Tutorial
  • Documentation
  • Compatibility
  • Download
  •  
  • Community
  • Contact
  • Credits
  • Changes

AutomateIt screenshots

AutomateIt is an open source tool for automating the setup and maintenance of servers, applications and their dependencies.

The screenshots below will show how to use AutomateIt to setup a Ruby on Rails application server on Linux. AutomateIt can also be used to fully automate entire systems and works well with a variety of OSes, frameworks and tools.

The code in these screens is self-documenting and the accompanying text is deliberately brief. Please read the Tutorial for further information.

You will need to install AutomateIt to run these examples.

Let’s see some code!

Project

Create a project, this will make a demo directory for your configuration files and recipes:

root@kagami> automateit --create demo

Change into this new directory with cd demo.

Tags

Describe hosts and their roles in the project’s config/tags.yml file. The example in these screenshots is run on host a called kagami. This host is tagged with the rails_servers and myapp_servers roles. To make this recipe run on your system, replace kagami with your hostname. Tags will be checked in the recipe using tagged?
methods:

rails_servers:
    - kagami
    - tsukasa
myapp_servers:
    - kagami

Fields

Define configuration constants in the project’s config/fields.yml file, these will be retrieved in the recipe using lookup methods:

user: myapp_user
port: 9826
path: /tmp/myapp_server

Installer recipe

Write a recipe to set up a Rails application server, saving it as recipes/install.rb:

# Install dependencies on hosts with 'rails_servers' or 'myapp_servers' roles
if tagged?("rails_servers | myapp_servers")
  # Install platform-specific packages
  if tagged?("ubuntu | debian")
    # Install the 'build-essential' package and others on Ubuntu or Debian
    package_manager.install("build-essential", "ruby1.8-dev", "libsqlite3-dev")
  elsif tagged?("fedoracore | fedora | centos")
    # Install equivalent packages on Fedora and similar OSes
    package_manager.install("gcc", "ruby-devel", "sqlite-devel")
  else
    # Fail if running on another platform
    raise NotImplementedError.new("no packages specified for this platform")
  end

  # Install Rails and supporting libraries with RubyGems
  package_manager.install("rails", "sqlite3-ruby", "mongrel",
    :with => :gem, :docs => false)
end # ENDS: if tagged?("rails_servers | myapp_servers")

# Setup the myapp server, a simple Rails server instance
if tagged?(:myapp_servers)
  # Create user for the application
  account_manager.add_user(lookup(:user))

  # Create a directory for the application and 'cd' into it
  mkdir_p(lookup(:path)) do
    # Run shell commands to create the app and database
    unless File.exists?("config/routes.rb")
      sh("rails --database=sqlite3 . > /dev/null")
    end

    # Create the database if it doesn't exist.
    if Dir["db/*.sqlite3"].empty?
      sh("rake db:migrate")
    end

    # Edit the homepage
    edit(:file => "public/index.html") do
      append("<!-- Edited by AutomateIt -->")
      replace("Welcome aboard", "This is MyAppServer")
    end

    # Set the ownership of the created files
    chperm(".", :user => lookup(:user), :recurse => true)

    # Generate a service startup file using a template
    render(
      :file => dist+"myapp_server.erb",
      :to => "/etc/init.d/myapp_server",
      :mode => 0555,
      :locals => {
        :path => lookup(:path),
        :user => lookup(:user),
        :port => lookup(:port),
      }
    )

    # Start the server
    service_manager.start("myapp_server")
  end # ENDS: mkdir_p(lookup(:path)) do
end # ENDS: if tagged?(:myapp_servers)

Template

Create a template file to render, dist/myapp_server.erb. This is not AutomateIt code — the only thing worth paying attention to is that the ERB template fields will be replaced by the render call in the recipe. For example, the port = "<%=port%>" line will be rendered as port = "9826".

#!/usr/bin/env ruby

user = "<%=user%>"
port = "<%=port%>"
path = "<%=path%>"
pid = "mongrel.pid"

ENV["PATH"] = "%s/bin:%s" % [`gem env gemdir`.strip, ENV["PATH"]]

case ARGV.first
when "start"
  Dir.chdir(path)
  puts "Starting MyAppServer at http://localhost:#{port}/"
  exit system("PATH=#{ENV["PATH"]} mongrel_rails start " \
    "--user #{user} --group #{user} --pid #{pid} --daemonize " \
    "--port #{port} 2>&1 | grep -v cgi_multipart_eof_fix") ? 0 : 1
when "stop"
  Dir.chdir(path)
  exit system("PATH=#{ENV["PATH"]} mongrel_rails stop " \
    "--pid #{pid} 2>&1 | grep -v cgi_multipart_eof_fix") ? 0 : 1
when "status"
  begin
    Process.kill(0, File.read(File.join(path, pid)).to_i)
    exit 0
  rescue Errno::ENOENT, Errno::ESRCH
    exit -1 # File or pid not found
  end
else
  puts "ERROR: expected argument -- start, stop or status"
end

Preview and execution

Preview the commands this recipe will execute, without actually executing them, by specifying the --preview flag:

root@kagami> automateit --preview recipes/install.rb
** apt-get install -y -q ruby1.8-dev libsqlite3-dev < /dev/null 2>&1
** gem install -y --no-ri --no-rdoc rails sqlite3-ruby mongrel 2>&1
** useradd --create-home --shell /bin/bash myapp_user > /dev/null
** nscd --invalidate passwd
** mkdir -p /tmp/myapp_server
** pushd /tmp/myapp_server
** rails --database=sqlite3 . > /dev/null
** rake db:migrate
=> Edited 'public/index.html'
** chown -R myapp_user .
=> Rendering '/etc/init.d/myapp_server' because of it doesn't exist
** chmod 0555 /etc/init.d/myapp_server
** /etc/init.d/myapp_server start
** popd
root@kagami>

Run the recipe without the --preview to apply it. Commands like apt-get will now display output because they’re being executed. The gem command typically requires manual intervention, but AutomateIt can guess which versions of Gems to install. When finished, the newly-created Rails server will be started:

root@kagami> automateit recipes/install.rb
** apt-get install -y -q ruby1.8-dev libsqlite3-dev < /dev/null 2>&1
Reading package lists...
Building dependency tree...
Reading state information...
The following extra packages will be installed:
[...]
** gem install -y --no-ri --no-rdoc rails sqlite3-ruby mongrel 2>&1
Select which gem to install for your platform (i486-linux)
1. mongrel 1.0.1 (mswin32)
2. mongrel 1.0.1 (ruby)
3. mongrel 1.0 (mswin32)
4. mongrel 1.0 (ruby)
5. Skip this gem
6. Cancel installation
>=> Guessing: 2
[...]
** useradd --create-home --shell /bin/bash myapp_user > /dev/null
** nscd --invalidate passwd
** mkdir -p /tmp/myapp_server
** pushd /tmp/myapp_server
** rails --database=sqlite3 . > /dev/null
** rake db:migrate
[...]
=> Edited 'public/index.html'
** chown -R myapp_user .
=> Rendering '/etc/init.d/myapp_server' because of it doesn't exist
** chmod 0555 /etc/init.d/myapp_server
** /etc/init.d/myapp_server start
Starting MyAppServer at http://localhost:9826/
** popd
root@kagami>

Run the recipe again — it will detect that everything is as it should be and won’t do anything:

root@kagami> automateit recipes/install.rb
root@kagami>

Now stop the Rails server manually and re-run the recipe — it will figure out that the server isn’t running and start it:

root@kagami> /etc/init.d/myapp_server stop
Sending TERM to Mongrel at PID 32419...Done.
root@kagami> automateit recipes/install.rb
** pushd /tmp/myapp_server
** /etc/init.d/myapp_server start
Starting MyAppServer at http://localhost:9826/
** popd
root@kagami>

Uninstaller recipe

You can uninstall the Rails server with this recipe, save it as recipes/uninstall.rb:

if tagged? :myapp_servers
  service_manager.stop "myapp_server"
  rm "/etc/init.d/myapp_server"
  rm_rf lookup(:path)
  account_manager.remove_user lookup(:user)
end

Run the uninstaller:

root@kagami> automateit recipes/uninstall.rb
** "/etc/init.d/myapp_server" "stop"
Sending TERM to Mongrel at PID 29149...Done.
** rm /etc/init.d/myapp_server
** rm_rf /tmp/myapp_server
** userdel -r myapp_user
** nscd --invalidate passwd
root@kagami>

Re-run the uninstaller — it’ll detect that the files are gone and the server is stopped, and so it won’t do anything:

root@kagami> automateit recipes/uninstall.rb
root@kagami>

Embedding

AutomateIt can be easily embedded into other programs. For example, put the following Rakefile into the demo project directory. You will now be able to execute Rake tasks like rake preview install, which will run the equivalent of automateit --preview recipes/install.rb.

require "automateit"

# Create an Interpreter for project in current directory.
@interpreter = AutomateIt.new(:project => ".")

# Include Interpreter's 'invoke' and 'preview' methods into Rake.
@interpreter.include_in(self, %w(invoke preview))

desc "Install myapp server"
task :install do
  # The 'invoke' method was created by the 'include_in' call earlier,
  # as a convenient shortcut for: @interpreter.invoke("install")
  invoke "install"
end

desc "Uninstall myapp server"
task :uninstall do
  invoke "uninstall"
end

desc "Preview action, e.g, 'rake preview install'"
task :preview do
  preview true
end

Learn more

AutomateIt can do much more than this simple example shows. For more information, see the hands-on Tutorial and reference Documentation.

Copyright © 2007-2009 Igal Koshevoy. Legal.