1
0
Fork 0
mirror of https://github.com/capistrano/capistrano synced 2023-03-27 23:21:18 -04:00

Add ability to filter tasks to specific servers (host filtering).

This commit adds the ability to control what servers are involved in an
task from the command line, or by setting an environment variable.

The filter contains a list of servers; these are the only servers that
will be involved in the task.

For example, if you had three servers defined in your configuration
(server1, server2 and server3), you could deploy to only server1 by
doing either:

    cap --hosts server1 production deploy

Or:

    HOSTS=server1 cap production deploy

Multiple servers can be specified by separating them with commas:

    cap --hosts server1,server2 production deploy
    HOSTS=server1,server2 cap production deploy

Host filtering happens after role filtering, and does not change what
roles a server will respond to.
This commit is contained in:
Andy Sykes 2013-11-26 14:51:13 +00:00
parent 7b438780cc
commit a1d3bfb524
6 changed files with 183 additions and 2 deletions

View file

@ -4,6 +4,7 @@ Reverse Chronological Order:
## master ## master
* Add ability to filter tasks to specific servers (host filtering). (@andytinycat)
* Add a command line option to control role filter (`--roles`) (@andytinycat) * Add a command line option to control role filter (`--roles`) (@andytinycat)
## `3.1.0` (not released) ## `3.1.0` (not released)

View file

@ -16,7 +16,7 @@ module Capistrano
end end
def sort_options(options) def sort_options(options)
options.push(version, dry_run, roles) options.push(version, roles, dry_run, hostfilter)
super super
end end
@ -74,6 +74,16 @@ module Capistrano
} }
] ]
end end
def hostfilter
['--hosts HOSTS', '-z',
"Filter command to only apply to these hosts (separate multiple hosts with a comma)",
lambda { |value|
Configuration.env.set(:filter, :hosts => value.split(","))
}
]
end
end end
end end

View file

@ -1,5 +1,6 @@
require 'set' require 'set'
require_relative 'servers/role_filter' require_relative 'servers/role_filter'
require_relative 'servers/host_filter'
module Capistrano module Capistrano
class Configuration class Configuration
class Servers class Servers
@ -39,7 +40,7 @@ module Capistrano
def fetch_roles(required, options) def fetch_roles(required, options)
filter_roles = RoleFilter.for(required, available_roles) filter_roles = RoleFilter.for(required, available_roles)
select(servers_with_roles(filter_roles), options) HostFilter.for(select(servers_with_roles(filter_roles), options))
end end
def servers_with_roles(roles) def servers_with_roles(roles)

View file

@ -0,0 +1,82 @@
module Capistrano
class Configuration
class Servers
class HostFilter
def initialize(available)
@available = available
end
def self.for(available)
new(available).hosts
end
def hosts
if host_filter.any?
@available.select { |server| host_filter.include? server.hostname }
else
@available
end
end
private
def filter
if host_filter.any?
host_filter
else
@available
end
end
def host_filter
env_filter | configuration_filter
end
def configuration_filter
ConfigurationFilter.new.hosts
end
def env_filter
EnvFilter.new.hosts
end
class ConfigurationFilter
def hosts
if filter
Array(filter.fetch(:hosts, []))
else
[]
end
end
def config
Configuration.env
end
def filter
config.fetch(:filter) || config.fetch(:select)
end
end
class EnvFilter
def hosts
if filter
filter.split(',')
else
[]
end
end
def filter
ENV['HOSTS']
end
end
end
end
end
end

View file

@ -0,0 +1,86 @@
require 'spec_helper'
module Capistrano
class Configuration
class Servers
describe HostFilter do
let(:host_filter) { HostFilter.new(available) }
let(:available) { [ Server.new('server1'), Server.new('server2'), Server.new('server3') ] }
describe '#new' do
it 'takes one array of hostnames' do
expect(host_filter)
end
end
describe '.for' do
subject { HostFilter.for(available) }
context 'without env vars' do
it 'returns all available hosts' do
expect(subject).to eq available
end
end
context 'with ENV vars' do
before do
ENV.stubs(:[]).with('HOSTS').returns('server1,server2')
end
it 'returns all required hosts defined in HOSTS' do
expect(subject).to eq [Server.new('server1'), Server.new('server2')]
end
end
context 'with configuration filters' do
before do
Configuration.env.set(:filter, hosts: %w{server1 server2})
end
it 'returns all required hosts defined in the filter' do
expect(subject).to eq [Server.new('server1'), Server.new('server2')]
end
after do
Configuration.env.delete(:filter)
end
end
context 'with a single configuration filter' do
before do
Configuration.env.set(:filter, hosts: 'server3')
end
it 'returns all required hosts defined in the filter' do
expect(subject).to eq [Server.new('server3')]
end
after do
Configuration.env.delete(:filter)
end
end
context 'with configuration filters and ENV vars' do
before do
Configuration.env.set(:filter, hosts: 'server1')
ENV.stubs(:[]).with('HOSTS').returns('server3')
end
it 'returns all required hosts defined in the filter' do
expect(subject).to eq [Server.new('server1'), Server.new('server3')]
end
after do
Configuration.env.delete(:filter)
end
end
end
end
end
end
end

View file

@ -179,6 +179,7 @@ module Capistrano
before do before do
ENV.stubs(:[]).with('ROLES').returns('web,db') ENV.stubs(:[]).with('ROLES').returns('web,db')
ENV.stubs(:[]).with('HOSTS').returns(nil)
servers.add_host('1', roles: :app, active: true) servers.add_host('1', roles: :app, active: true)
servers.add_host('2', roles: :app) servers.add_host('2', roles: :app)
servers.add_host('3', roles: :web) servers.add_host('3', roles: :web)