capistrano-scope

Lightweight command line server selection for capistrano.

Downloads
31.3K
Stars
2
Committers
1

Lightweight command line server selection for capistrano. Rather than having to blast a task across all servers with a given role or as with multistage having to group all of your servers into separate files ahead of time. With this package you can pick groups or sub groups of servers or even individual servers to execute tasks on via the command line. For example:

$ cap @:projectx:production:app:1 app:install_bundler
$ cap @:projectx:all server:deploy_authorized_keys USERS='john_doe jane_doe'
$ cap @:all nagios:nrpe:deploy

From a config file perspective the only new functionality is the scope.server and scope.role methods. From a command line persective all that has been added is the @: namespace which contains all the server scope tasks.

== Some config file (deploy.rb)

require 'capistrano/scope'

# Optional variable.  It'll use this one if none is specified on the command line.
set :default_scope, '@:projectx:staging:all'

== clientx.rb capistrano config file

The order you specify servers or roles doesn't matter, you can define scope.role for a server before you define scope.server or visa versa. You can give servers any name you like but they are broken into logical groups by using the ":" separator.

scope.server 'projectx:staging:lb:1', '123.456.567.1'
scope.server 'projectx:staging:app:1', '123.456.567.3'
scope.server 'projectx:staging:app:2', '123.456.567.4'
scope.server 'projectx:staging:db:1', '123.456.567.5'

scope.server 'projectx:production:lb:1', '123.456.567.7'
scope.server 'projectx:production:app:1', '123.456.567.9'
scope.server 'projectx:production:app:2', '123.456.567.10'
scope.server 'projectx:production:app:3', '123.456.567.10'
scope.server 'projectx:production:db:1', '123.456.567.12'


scope.role :load_balancer, 'projectx:staging:lb:1'
scope.role :load_balancer, 'projectx:production:lb:1'

scope.role :app, 'projectx:staging:app:1'
scope.role :app, 'projectx:staging:app:2'
scope.role :app, 'projectx:production:app:1'
scope.role :app, 'projectx:production:app:2'

scope.role :db, 'projectx:staging:db:1', :primary => true
scope.role :db, 'projectx:production:db:1', :primary => true

== recipes.rb For the sake of demonstration lets assume we also have the following tasks defined and included from somewhere else.

namespace :server do
  desc "Concatenate Keys and upload new authorized_keys file to the server."
  task :authorized_keys do
    ...
  end

  desc "Install Subversion"
  task :install_subversion do
    ...
  end
end

namespace :mysql do
  desc "Backup Database"
  task :backup, :roles => :db do
    ...
  end
end

namespace :app do
  desc "Install bundler"
  task :install_bundler, :roles => :app do
    ...
  end

  desc "Install nginx"
  task :install_nginx, :roles => :app do
    ...
  end

  desc "Install imagemagick"
  task :install_imagemagick, :roles => :app do
    ...
  end
end

namespace :nagios do
  desc "Update nagios config on the admin server and restart nagios service"
  task :deploy, :roles => :admin do
    ...
  end

  desc "Restart the nagios SMF Service"
  task :restart, :roles => :admin do
    ...
  end

  namespace :nrpe do
    desc "Install NRPE"
    task :install, :roles => :server do
      ...
    end

    desc "Update NRPE Configuration"
    task :deploy, :roles => :server do
      ...
    end
  end

  namespace :plugins do
    desc "Copy plugins to servers"
    task :deploy, :roles => :server do
      ...
    end
  end
end

end of recipes.rb

= Usage

Now on the command line you can see the tasks that you normally would by listing out tasks with descriptions.

$ cap -T

cap @:all                                       # Scope all defined servers
cap @:show                                      # Show the roles and servers ...
cap app:install_bundler
cap app:install_nginx
cap app:install_image_magick
cap mysql:backup
cap nagios:deploy
cap nagios:restart
cap nagios:nrpe:install
cap nagios:nrpe:deploy
cap nagios:plugins:deploy
cap server:authorized_keys
cap server:install_subversion

And if you want to see a list of all the server scopes that you have defined use the verbose flag. Since we used the '@' as the top level namespace for our scoping tasks they should all sort to the top of the list and then be followed by your normal capistrano tasks.

$ cap -vT

cap @:all                                       # Scope all defined servers
cap @:clienta:all                               # 
cap @:clienta:projectx:all                      # 
cap @:clienta:projectx:production:all           # 
cap @:clienta:projectx:production:app:1         # 
cap @:clienta:projectx:production:app:2         # 
cap @:clienta:projectx:production:app:all       # 
cap @:clienta:projectx:production:db:1          # 
cap @:clienta:projectx:production:db:2          # 
cap @:clienta:projectx:production:db:all        # 
cap @:clienta:projectx:production:lb:1          # 
cap @:clienta:projectx:production:lb:2          # 
cap @:clienta:projectx:production:lb:all        # 
cap @:clienta:projectx:staging:all              # 
cap @:clienta:projectx:staging:app:1            # 
cap @:clienta:projectx:staging:app:2            # 
cap @:clienta:projectx:staging:app:all          # 
cap @:clienta:projectx:staging:db:1             # 
cap @:clienta:projectx:staging:db:2             # 
cap @:clienta:projectx:staging:db:all           # 
cap @:clienta:projectx:staging:lb:1             # 
cap @:clienta:projectx:staging:lb:2             # 
cap @:clienta:projectx:staging:lb:all           # 
cap @:create_role_tasks                         # 
cap @:show                                      # Show the roles and servers ...
cap app:install_bundler
cap app:install_nginx
cap app:install_image_magick
cap mysql:backup
cap nagios:deploy
cap nagios:restart
cap nagios:nrpe:install
cap nagios:nrpe:deploy
cap nagios:plugins:deploy
cap server:authorized_keys
cap server:install_subversion

Now to execute a task on a single server you can precede the task with the scope task for that server.

$ cap @:clienta:projectx:production:app:1 app:install_bundler

Or if you want to execute that task on a group of servers you can use the all task that is defined for you at each level.

$ cap @:clienta:projectx:production:app:all

Since we have added roles to our tasks and servers we can still execute tasks on larger groups of servers that include servers that we don't want the command to run on. Because the task is role based it will only execute the task on the correct servers. For example if we wanted to install nginx on all app servers of clientx but not clienty we could do this:

$ cap @:clientx:all app:install_nginx

And if we really wanted to go global we can execute something across all servers with the @:all scope. Lets say we want to deploy they updated nrpe config file and a new plugin we wrote to all our servers.

$ cap @:all nagios:nrpe:deploy nagios:plugins:deploy

Included with this package is a @:show task that will list out all the servers and roles that are defined for a given scope.

$ cap @:clienta:projectx:all @:show

    triggering load callbacks
  * executing `@:create_role_tasks'
  * executing `@:clienta:projectx:all'
  * executing `@:show'
Roles:
         app
         db
         load_balancer
Servers:
         clienta:projectx:production:app:1
         clienta:projectx:production:app:2
         clienta:projectx:production:db:1
         clienta:projectx:production:db:2
         clienta:projectx:staging:app:1
         clienta:projectx:staging:app:2
         clienta:projectx:staging:db:2


$ cap @:clienta:projectx:staging:db:all @:show

    triggering load callbacks
  * executing `@:create_role_tasks'
  * executing `@:clienta:projectx:staging:db:all'
  * executing `@:show'
Roles:
         db
Servers:
         clienta:projectx:staging:db:2

= Integrating with multi-stage

Note: if you're using this with multi-stage, include capistrano-scope after the multi-stage include. A nifty way to give you the individual server functionality of capistrano-scope and the easy stage deployment of multi-stage would be to put all your server definitions into a globally included config file and then put your variable definitions and set a :default_scope in each multi-stage config file.

= How it Works

The implementation behind this is simple. When the configuration is loaded it generates tasks for each server and server group in memory. Each of these tasks is not much more than a list of capistrano server and role method calls. When you call one of the scope tasks from the command line the task executes and the roles and servers are defined just in time for the next task on the command line to execute on them.

Package Rankings
Top 23.51% on Rubygems.org