Class | AutomateIt::Project |
In: |
lib/automateit/project.rb
|
Parent: | Common |
An AutomateIt Project is a collection of related recipes, tags, fields and custom plugins.
You can create a project by running the following from the Unix shell:
automateit --create myproject
This will create a directory called myproject with a number of directories and files. Each directory has a README.txt that explains what it‘s used for.
Although you can run recipes without a project, putting your recipes into a project provides you with the following benefits:
cp(dist+"/source.txt", "/tmp/target.txt")
For example, create a new project:
automateit --create hello_project
Inside this project, edit its fields, which are stored in the config/fields.yml file, and make it look like this:
greeting: Hello world!
Then create a recipe in the recipes/greet.rb file:
puts lookup(:greeting)
You can run the recipe:
automateit recipes/greet.rb
And you should get the following output:
Hello world!
You should split up your recipe code into different recipe files. This will improve the clarity of your code because each file can perform one task, and you‘ll also be able to easily execute a specific recipe.
For example, you can use a task-specific recipes/postgresql.rb to set up the PostgreSQL database server, and a recipes/apache.rb to setup the Apache web server.
You can run one recipe from another. It‘s a good idea to create a top-level recipe that invokes the other recipes. This lets you run a single recipe that will in turn run all your other recipes in the correct order, such as setting up the database server before the web server so that websites.
For example, consider a recipes/all.rb file with these lines:
invoke 'postgresql' if tagged? :postgresql_server invoke 'nginx' if tagged? :nginx_server invoke 'apache' if tagged? :apache_server
The first line above checks to see if the current host has the postgresql_server tag, and if it does, invokes the recipes/postgresql.rb recipe.
You must run recipes from other recipes using AutomateIt‘s invoke method and not Ruby‘s require, because the invoke passes along the AutomateIt interpreter to the other recipes so they can continue execution.
Any files ending with .rb that you put into the project‘s lib directory will be loaded before your recipe starts executing. This is a good way to add common features, custom plugins and such.
For example, put the following into a new lib/meow.rb file:
def meow "MEOW!" end
Now create a new recipe that uses this method in recipes/speak.rb
puts meow
Now you can run it:
automateit recipes/speak.rb
And you‘ll get this:
MEOW!
AutomateIt will load the project automatically if you‘re executing a recipe that‘s inside a project‘s recipes directory.
For example, assume that you‘ve create your project as /tmp/hello_project and have a recipe at /tmp/hello_project/recipes/greet.rb.
You can execute the recipe with a full path:
automateit /tmp/hello_project/recipes/greet.rb
Or execute it with a relative path:
cd /tmp/hello_project/recipes automateit greet.rb
Or you can prepend a header to the greet.rb recipe so it looks like this
#!/usr/bin/env automateit puts lookup(:greeting)
And then make the file executable:
chmod a+x /tmp/hello_project/recipes/greet.rb
And execute the recipe directly:
/tmp/hello_project/recipes/greet.rb
If you‘re embedding the Interpreter into another Ruby program, you can run recipes and they‘ll automatically load the project if applicable. For example:
require 'rubygems' require 'automateit' AutomateIt.invoke("/tmp/hello_project/recipes/greet.rb")
Or if you may specify the project path explicitly:
require 'rubygems' require 'automateit' interpreter = AutomateIt.new(:project => "/tmp/hello_project") puts interpreter.lookup("greeting")
You can access a project‘s tags and fields from the Unix shell. This helps other programs access configuration data and make use of your roles.
For example, with the hello_project we‘ve created, we can lookup fields from the Unix shell like this:
aifield -p /tmp/hello_project greeting
The -p specifies the project path (its an alias for —project).
More commands are available. For documentation and examples run the following commands from the Unix shell:
aifield --help aitag --help
Sometimes it‘s convenient to set a default project path so you don‘t need to type as much by specifing the AUTOMATEIT_PROJECT environmental variable (or AIP if you want a shortcut) and use it like this:
export AUTOMATEIT_PROJECT=/tmp/hello_project aifield greeting
If you want to share a project between different hosts, you‘re responsible for distributing the files between them. This isn‘t a big deal though because these are just text files and your OS has dozens of excellent ways to distribute these.
Common approaches to distribution:
An example of a complete solution for distributing system configuration management files:
#!/bin/sh cd /var/local/myautomateit svn update --quiet automateit recipe/default.rb
for host in `aitag -p /var/local/myautomateit -w apache_server`; do echo "# $host" ssh $host myautomateit done
In case you‘re interested, the project creator is actually an AutomateIt recipe. You can read the recipe source code by looking at the AutomateIt::Project::create method.
RECIPE_HELLO_CONTENT | = | <<-'EOB' #:nodoc puts "Hello, I'm an #{self.class} -- pleased to meet you!" puts "I'm in preview mode" if preview? EOB |
RAKEFILE_CONTENT | = | <<-EOB #:nodoc require "automateit" # Create an Interpreter for project in current directory. @interpreter = AutomateIt.new(:project => ".") # Include Interpreter's methods into Rake session. @interpreter.include_in(self) task :default => :shell desc "Interactive AutomateIt shell" task :shell do AutomateIt::CLI.run end desc "Run a recipe" task :hello do invoke "hello" end desc "Preview action, e.g, 'rake preview hello'" task :preview do preview true end EOB |
Create a new project.
Options:
# File lib/automateit/project.rb, line 217 def self.create(opts) display = lambda {|message| puts message if ! opts[:verbosity] || (opts[:verbosity] && opts[:verbosity] <= Logger::INFO) } path = opts.delete(:create) \ or raise ArgumentError.new(":create option not specified") interpreter = AutomateIt.new(opts) interpreter.instance_eval do # +render+ only files that don't exist. template_manager.default_check = :exists mkdir_p(path) do |created| display.call PNOTE+"%s AutomateIt project at: %s" % [created ? "Creating" : "Updating", path] render(:text => WELCOME_CONTENT, :to => "README_AutomateIt.txt") render(:text => RAKEFILE_CONTENT, :to => "Rakefile") mkdir("config") render(:text => TAGS_CONTENT, :to => "config/tags.yml") render(:text => FIELDS_CONTENT, :to => "config/fields.yml") render(:text => ENV_CONTENT, :to => "config/automateit_env.rb") mkdir("dist") render(:text => DIST_README_CONTENT, :to => "dist/README_AutomateIt_dist.txt") mkdir("lib") render(:text => BASE_README_CONTENT, :to => "lib/README_AutomateIt_lib.txt") mkdir("recipes") render(:text => RECIPE_README_CONTENT, :to => "recipes/README_AutomateIt_recipes.txt") render(:text => RECIPE_HELLO_CONTENT, :to => "recipes/hello.rb") end if log.info? and not opts[:quiet] puts '-----------------------------------------------------------------------' puts WELCOME_MESSAGE puts '-----------------------------------------------------------------------' end end # of interpreter.instance_eval end