开发者

How can I access server specific options in Capistrano?

开发者 https://www.devze.com 2023-04-07 09:16 出处:网络
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:

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)
    ...
开发者_Python百科

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


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


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.


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.


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.


Check out the gem capistrano-ext, and use the multi-stage feature. It is described fully here.

0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号