Restricting roles down task call frame

22 views
Skip to first unread message

Joe

unread,
Aug 22, 2007, 6:28:27 PM8/22/07
to Capistrano
Hi,

I am working on a plugin for Capistrano that helps in managing
clusters of machines on EC2. I need to be able to dynamically create a
namespace and top level task that can then call user-defined tasks to
be run once the machine is up.

I'd like to be able to have the equivalent of something like the
following. This isn't quite what I'm doing since some of the tasks are
meta-defined.

task :anonymous_task, :hosts => "dns-of-newly-booted-machine" do
top_level_task
end

task :top_level_task do
user_defined_task_1
user_defined_task_2
end

task :user_defined_task1 do
end

task :user_defined_task2 do
end

The problem is that I want the user defined tasks to only be run on
the host that "anonymous_task" is being run on. Instead, they are
being run on all of the hosts currently known. I'd like to avoid
having to somehow pass this DNS entry to the user since it is implied
that tasks called from "top_level_task" will only be run on the new
host. It doesn't look to me like Cap supports restricting hosts as you
move down the task chain. Am I correct? Does anyone have a suggestion
on how I might be able to accomplish this elegantly without having to
patch Cap?

Thanks,
Joe

Jamis Buck

unread,
Aug 22, 2007, 6:38:44 PM8/22/07
to capis...@googlegroups.com
If you set the HOSTS environment variable, all tasks will use it,
instead of whatever hosts list they might have otherwise.

Thus:

task :anonymous_task do
original, ENV['HOSTS'] = ENV['HOSTS'], "dns-of-newly-booted-machine"
begin
top_level_task
ensure
ENV['HOSTS'] = original
end
end

I admit it's not the most elegant solution, but it should work just
fine. I've considered adding something like a "with" keyword, to make
this easier, but it's been such an edge case that I've not bothered
yet.

- Jamis

Joe

unread,
Aug 22, 2007, 6:52:14 PM8/22/07
to Capistrano
Jamis,

Thanks, I was just about to post this extension. It uses the option
"recursive_restrict," but that name is terrible! It basically walks up
the call chain, and constructs the union of hosts seen at each frame.
If it finds the option at any parent frame, it immediately returns the
union, rather than the result from the normal call to
find_servers_for_task.

Anyway, I'll probably use your solution since it is simpler, and I
also don't like to keep around extensions if they're not needed.

Thanks,
Joe

module CapistranoExt
class Configuration
module Servers
def self.included(base) #:nodoc:

base.send :alias_method, :find_servers_for_task_without_recursive_restrict, :find_servers_for_task

base.send :alias_method, :find_servers_for_task, :find_servers_for_task_with_recursive_restrict
end

def find_servers_for_task_with_recursive_restrict(task,
options={})
servers =
find_servers_for_task_without_recursive_restrict(task, options={})
restricted = servers.dup
task_call_frames.reverse.each do |frame|
restricted &=
find_servers_for_task_without_recursive_restrict(frame.task, {})
return restricted if frame.task.options[:recursive_restrict]
end
servers
end
end
end
end


On Aug 22, 6:38 pm, "Jamis Buck" <ja...@37signals.com> wrote:
> If you set the HOSTS environment variable, all tasks will use it,
> instead of whatever hosts list they might have otherwise.
>
> Thus:
>
> task :anonymous_task do
> original, ENV['HOSTS'] = ENV['HOSTS'], "dns-of-newly-booted-machine"
> begin
> top_level_task
> ensure
> ENV['HOSTS'] = original
> end
> end
>
> I admit it's not the most elegant solution, but it should work just
> fine. I've considered adding something like a "with" keyword, to make
> this easier, but it's been such an edge case that I've not bothered
> yet.
>
> - Jamis
>

Reply all
Reply to author
Forward
0 new messages