Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I access server specific options in Capistrano?

I'm trying to configure Capistrano to do the same task on two different servers, each of them having different credentials. I'd like to do something simmilar to:

namespace :deploy do
  role :db,  "192.168.1.1", :credentials => "db1.yml"
  role :db,  "192.168.1.1", :credentials => "db2.yml"

  task :mytask, :roles => :db do
    credentials = YAML.load_file(something)
    ...

Is that possible? What should I substitute something with, in order to access the current server configuration?

like image 726
Barnaba Avatar asked Sep 26 '11 13:09

Barnaba


4 Answers

OK, I finally had some time to solve this. Hopefully someone else will find this answer useful. Here's how i eventually solved the problem:

role :db, "db1" ,{ :credentials => 'db1-credentials'}                     
role :db, "db2" ,{ :credentials => 'db2-credentials'}                     
role :db, "db3"

namespace :stackoverflow do

  # Don't run this task on host that don't have credentials defined
  task :default, {:role => :db, :except => {:credentials => nil } } do
    servers = find_servers_for_task(current_task)
    servers.each do |server|
      credentials = server.options[:credentials]

      puts credentials # actual task

    end
  end
end

I see now that I may had stated the question in a confusing way - that's because I hadn't understood, that task are run concurrently.

This will in fact execute the task (here puts credentials) once for each server, which was what I was trying to do.

Output:

$ cap stackoverflow
  * executing `stackoverflow'
db1-credentials
db2-credentials

It's a good idea to add a filter to a task so it won't run if the server has no credentials.

That being said, making everybody in the team maintain current (and for security reasons non-versioned) credentials to all servers turned out to be too much hassle (thus defeating the idea of using Capistrano). Now instead of keeping the external configuration on the disks of users I'm going to keep the data on the servers affected (mostly in form of runnable scripts with all credentials hidden inside). Like this:

task :dump {:role => :db} do
  run "/root/dump_db.sh | gzip > /tmp/dump.sql.gz"
  download "/tmp/dump.sql.gz", "somewhere"
end
like image 197
Barnaba Avatar answered Oct 31 '22 23:10

Barnaba


I'm working on this problem at the moment. The 'parallel' function doesn't give you the opportunity to modify the command line being executed, but it does give you the ability to have alternative command lines depending on the server options. I am thinking about doing a monkey-patch on the replace-placeholders function in command.rb. Only having $CAPISTRANO:HOST$ as an option seems very limiting. I wonder how much chaos would be caused by just doing the following:

module Capistrano
  module Command
      class Tree
          def replace_placeholders(command, channel)
              server = channel[:server]
              command.eval(command)
          end
       end
  end
end

In theory now you could do this:

role :whatever, "myserver.com", :special_feature => "foo"

run "do-something #{server.options[:special_feature]}"

The above code might needs some work.

like image 3
ben Avatar answered Oct 31 '22 22:10

ben


You can use capistrano in multi environment setup.

You can require capistrano multistage gem that in deploy.rb file as -

require 'capistrano/ext/multistage'

For this you work you need to capistrano-ext gem as well. After this, you can setup two environments as -

set :stages, %w(staging production)
set :default_stage, "staging"

After that, inside your deploy/production.rb and deploy/staging.rb file, you can use configurations which are different for both server. All common configurations will go inside the deploy.rb file.

like image 1
rtdp Avatar answered Oct 31 '22 22:10

rtdp


If you actually want use the server's options on remote commands you can still use find_servers_for_task without patching it:

server 'server-one', :db, :credentials => 'cred1.yml'
server 'server-two', :db, :credentials => 'cred2.yml'

namespace :stackoverflow do
  task :default, :roles => :db do
    find_servers_for_task(current_task).each do |server|
      run "start_your_db -c #{server.options[:credentials]}", :hosts => server.name
    end
  end
end

But the :hosts on the run command is essential, as Capistrano commands will execute in parallel and without it would execute two commands on each server in this example.

like image 1
Danilo Moret Avatar answered Nov 01 '22 00:11

Danilo Moret