1
0
Fork 0
mirror of https://github.com/capistrano/capistrano synced 2023-03-27 23:21:18 -04:00
capistrano/spec/integration/dsl_spec.rb
Nick Townsend 368ee4fca8 fix 'filter' variable so it works as documented
The variable ':filter' is documented as requiring the keys 'hosts' and
'roles' to set Host and Role filters. In actual fact it uses 'host' and
'role'. This commit adds support and tests for both.

Fixes #1457
2015-07-16 16:03:20 -07:00

680 lines
21 KiB
Ruby

require 'spec_helper'
describe Capistrano::DSL do
let(:dsl) { Class.new.extend Capistrano::DSL }
before do
Capistrano::Configuration.reset!
end
describe 'setting and fetching hosts' do
describe 'when defining a host using the `server` syntax' do
before do
dsl.server 'example1.com', roles: %w{web}, active: true
dsl.server 'example2.com', roles: %w{web}
dsl.server 'example3.com', roles: %w{app web}, active: true
dsl.server 'example4.com', roles: %w{app}, primary: true
dsl.server 'example5.com', roles: %w{db}, no_release: true, active:true
end
describe 'fetching all servers' do
subject { dsl.roles(:all) }
it 'returns all servers' do
expect(subject.map(&:hostname)).to eq %w{example1.com example2.com example3.com example4.com example5.com}
end
end
describe 'fetching all release servers' do
context 'with no additional options' do
subject { dsl.release_roles(:all) }
it 'returns all release servers' do
expect(subject.map(&:hostname)).to eq %w{example1.com example2.com example3.com example4.com}
end
end
context 'with property filter options' do
subject { dsl.release_roles(:all, filter: :active) }
it 'returns all release servers that match the property filter' do
expect(subject.map(&:hostname)).to eq %w{example1.com example3.com}
end
end
end
describe 'fetching servers by multiple roles' do
it "does not confuse the last role with options" do
expect(dsl.roles(:app, :web).count).to eq 4
expect(dsl.roles(:app, :web, filter: :active).count).to eq 2
end
end
describe 'fetching servers by role' do
subject { dsl.roles(:app) }
it 'returns the servers' do
expect(subject.map(&:hostname)).to eq %w{example3.com example4.com}
end
end
describe 'fetching servers by an array of roles' do
subject { dsl.roles([:app]) }
it 'returns the servers' do
expect(subject.map(&:hostname)).to eq %w{example3.com example4.com}
end
end
describe 'fetching filtered servers by role' do
subject { dsl.roles(:app, filter: :active) }
it 'returns the servers' do
expect(subject.map(&:hostname)).to eq %w{example3.com}
end
end
describe 'fetching selected servers by role' do
subject { dsl.roles(:app, select: :active) }
it 'returns the servers' do
expect(subject.map(&:hostname)).to eq %w{example3.com}
end
end
describe 'fetching the primary server by role' do
context 'when inferring primary status based on order' do
subject { dsl.primary(:web) }
it 'returns the servers' do
expect(subject.hostname).to eq 'example1.com'
end
end
context 'when the attribute `primary` is explicitly set' do
subject { dsl.primary(:app) }
it 'returns the servers' do
expect(subject.hostname).to eq 'example4.com'
end
end
end
describe 'setting an internal host filter' do
subject { dsl.roles(:app) }
it 'is ignored' do
dsl.set :filter, { host: 'example3.com' }
expect(subject.map(&:hostname)).to eq(['example3.com', 'example4.com'])
end
end
describe 'setting an internal role filter' do
subject { dsl.roles(:app) }
it 'ignores it' do
dsl.set :filter, { role: :web }
expect(subject.map(&:hostname)).to eq(['example3.com','example4.com'])
end
end
describe 'setting an internal host and role filter' do
subject { dsl.roles(:app) }
it 'ignores it' do
dsl.set :filter, { role: :web, host: 'example1.com' }
expect(subject.map(&:hostname)).to eq(['example3.com','example4.com'])
end
end
describe 'setting an internal regexp host filter' do
subject { dsl.roles(:all) }
it 'is ignored' do
dsl.set :filter, { host: /1/ }
expect(subject.map(&:hostname)).to eq(%w{example1.com example2.com example3.com example4.com example5.com})
end
end
describe 'setting an internal hosts filter' do
subject { dsl.roles(:app) }
it 'is ignored' do
dsl.set :filter, { hosts: 'example3.com' }
expect(subject.map(&:hostname)).to eq(['example3.com', 'example4.com'])
end
end
describe 'setting an internal roles filter' do
subject { dsl.roles(:app) }
it 'ignores it' do
dsl.set :filter, { roles: :web }
expect(subject.map(&:hostname)).to eq(['example3.com','example4.com'])
end
end
describe 'setting an internal hosts and roles filter' do
subject { dsl.roles(:app) }
it 'ignores it' do
dsl.set :filter, { roles: :web, hosts: 'example1.com' }
expect(subject.map(&:hostname)).to eq(['example3.com','example4.com'])
end
end
describe 'setting an internal regexp hosts filter' do
subject { dsl.roles(:all) }
it 'is ignored' do
dsl.set :filter, { hosts: /1/ }
expect(subject.map(&:hostname)).to eq(%w{example1.com example2.com example3.com example4.com example5.com})
end
end
end
describe 'when defining role with reserved name' do
it 'fails with ArgumentError' do
expect {
dsl.role :all, %w{example1.com}
}.to raise_error(ArgumentError, "all reserved name for role. Please choose another name")
end
end
describe 'when defining hosts using the `role` syntax' do
before do
dsl.role :web, %w{example1.com example2.com example3.com}
dsl.role :web, %w{example1.com}, active: true
dsl.role :app, %w{example3.com example4.com}
dsl.role :app, %w{example3.com}, active: true
dsl.role :app, %w{example4.com}, primary: true
dsl.role :db, %w{example5.com}, no_release: true
end
describe 'fetching all servers' do
subject { dsl.roles(:all) }
it 'returns all servers' do
expect(subject.map(&:hostname)).to eq %w{example1.com example2.com example3.com example4.com example5.com}
end
end
describe 'fetching all release servers' do
context 'with no additional options' do
subject { dsl.release_roles(:all) }
it 'returns all release servers' do
expect(subject.map(&:hostname)).to eq %w{example1.com example2.com example3.com example4.com}
end
end
context 'with filter options' do
subject { dsl.release_roles(:all, filter: :active) }
it 'returns all release servers that match the filter' do
expect(subject.map(&:hostname)).to eq %w{example1.com example3.com}
end
end
end
describe 'fetching servers by role' do
subject { dsl.roles(:app) }
it 'returns the servers' do
expect(subject.map(&:hostname)).to eq %w{example3.com example4.com}
end
end
describe 'fetching servers by an array of roles' do
subject { dsl.roles([:app]) }
it 'returns the servers' do
expect(subject.map(&:hostname)).to eq %w{example3.com example4.com}
end
end
describe 'fetching filtered servers by role' do
subject { dsl.roles(:app, filter: :active) }
it 'returns the servers' do
expect(subject.map(&:hostname)).to eq %w{example3.com}
end
end
describe 'fetching selected servers by role' do
subject { dsl.roles(:app, select: :active) }
it 'returns the servers' do
expect(subject.map(&:hostname)).to eq %w{example3.com}
end
end
describe 'fetching the primary server by role' do
context 'when inferring primary status based on order' do
subject { dsl.primary(:web) }
it 'returns the servers' do
expect(subject.hostname).to eq 'example1.com'
end
end
context 'when the attribute `primary` is explicity set' do
subject { dsl.primary(:app) }
it 'returns the servers' do
expect(subject.hostname).to eq 'example4.com'
end
end
end
end
describe 'when defining a host using a combination of the `server` and `role` syntax' do
before do
dsl.server 'db@example1.com:1234', roles: %w{db}, active: true
dsl.server 'root@example1.com:1234', roles: %w{web}, active: true
dsl.server 'example1.com:5678', roles: %w{web}, active: true
dsl.role :app, %w{deployer@example1.com:1234}
dsl.role :app, %w{example1.com:5678}
end
describe 'fetching all servers' do
it 'creates one server per hostname, ignoring user and port combinations' do
expect(dsl.roles(:all).size).to eq(1)
end
end
describe 'fetching servers for a role' do
it 'roles defined using the `server` syntax are included' do
as = dsl.roles(:web).map { |server| "#{server.user}@#{server.hostname}:#{server.port}" }
expect(as.size).to eq(1)
expect(as[0]).to eq("deployer@example1.com:5678")
end
it 'roles defined using the `role` syntax are included' do
as = dsl.roles(:app).map { |server| "#{server.user}@#{server.hostname}:#{server.port}" }
expect(as.size).to eq(1)
expect(as[0]).to eq("deployer@example1.com:5678")
end
end
end
describe 'when setting user and port' do
subject { dsl.roles(:all).map { |server| "#{server.user}@#{server.hostname}:#{server.port}" }.first }
describe "using the :user property" do
it "takes precedence over in the host string" do
dsl.server 'db@example1.com:1234', roles: %w{db}, active: true, user: 'brian'
expect(subject).to eq("brian@example1.com:1234")
end
end
describe "using the :port property" do
it "takes precedence over in the host string" do
dsl.server 'db@example1.com:9090', roles: %w{db}, active: true, port: 1234
expect(subject).to eq("db@example1.com:1234")
end
end
end
end
describe 'setting and fetching variables' do
before do
dsl.set :scm, :git
end
context 'without a default' do
context 'when the variables is defined' do
it 'returns the variable' do
expect(dsl.fetch(:scm)).to eq :git
end
end
context 'when the variables is undefined' do
it 'returns nil' do
expect(dsl.fetch(:source_control)).to be_nil
end
end
end
context 'with a default' do
context 'when the variables is defined' do
it 'returns the variable' do
expect(dsl.fetch(:scm, :svn)).to eq :git
end
end
context 'when the variables is undefined' do
it 'returns the default' do
expect(dsl.fetch(:source_control, :svn)).to eq :svn
end
end
end
context 'with a block' do
context 'when the variables is defined' do
it 'returns the variable' do
expect(dsl.fetch(:scm) { :svn }).to eq :git
end
end
context 'when the variables is undefined' do
it 'calls the block' do
expect(dsl.fetch(:source_control) { :svn }).to eq :svn
end
end
end
end
describe 'asking for a variable' do
before do
dsl.ask(:scm, :svn)
$stdout.stubs(:print)
end
context 'variable is provided' do
before do
$stdin.expects(:gets).returns('git')
end
it 'sets the input as the variable' do
expect(dsl.fetch(:scm)).to eq 'git'
end
end
context 'variable is not provided' do
before do
$stdin.expects(:gets).returns('')
end
it 'sets the variable as the default' do
expect(dsl.fetch(:scm)).to eq :svn
end
end
end
describe 'checking for presence' do
subject { dsl.any? :linked_files }
before do
dsl.set(:linked_files, linked_files)
end
context 'variable is an non-empty array' do
let(:linked_files) { %w{1} }
it { expect(subject).to be_truthy }
end
context 'variable is an empty array' do
let(:linked_files) { [] }
it { expect(subject).to be_falsey }
end
context 'variable exists, is not an array' do
let(:linked_files) { stub }
it { expect(subject).to be_truthy }
end
context 'variable is nil' do
let(:linked_files) { nil }
it { expect(subject).to be_falsey }
end
end
describe 'configuration SSHKit' do
let(:config) { SSHKit.config }
let(:backend) { SSHKit.config.backend.config }
let(:default_env) { { rails_env: :production } }
before do
dsl.set(:format, :dot)
dsl.set(:log_level, :debug)
dsl.set(:default_env, default_env)
dsl.set(:pty, true)
dsl.set(:connection_timeout, 10)
dsl.set(:ssh_options, {
keys: %w(/home/user/.ssh/id_rsa),
forward_agent: false,
auth_methods: %w(publickey password)
})
dsl.configure_backend
end
it 'sets the output' do
expect(config.output).to be_a SSHKit::Formatter::Dot
end
it 'sets the output verbosity' do
expect(config.output_verbosity).to eq 0
end
it 'sets the default env' do
expect(config.default_env).to eq default_env
end
it 'sets the backend pty' do
expect(backend.pty).to be_truthy
end
it 'sets the backend connection timeout' do
expect(backend.connection_timeout).to eq 10
end
it 'sets the backend ssh_options' do
expect(backend.ssh_options[:keys]).to eq %w(/home/user/.ssh/id_rsa)
expect(backend.ssh_options[:forward_agent]).to eq false
expect(backend.ssh_options[:auth_methods]).to eq %w(publickey password)
end
end
describe 'local_user' do
before do
dsl.set :local_user, -> { Etc.getlogin }
end
describe 'fetching local_user' do
subject { dsl.local_user }
context 'where a local_user is not set' do
before do
Etc.expects(:getlogin).returns('login')
end
it 'returns the login name' do
expect(subject.to_s).to eq 'login'
end
end
context 'where a local_user is set' do
before do
dsl.set(:local_user, -> { 'custom login' })
end
it 'returns the custom name' do
expect(subject.to_s).to eq 'custom login'
end
end
end
end
describe 'on()' do
describe "when passed server objects" do
before do
dsl.server 'example1.com', roles: %w{web}, active: true
dsl.server 'example2.com', roles: %w{web}
dsl.server 'example3.com', roles: %w{app web}, active: true
dsl.server 'example4.com', roles: %w{app}, primary: true
dsl.server 'example5.com', roles: %w{db}, no_release: true
@coordinator = mock('coordinator')
@coordinator.expects(:each).returns(nil)
ENV.delete 'ROLES'
ENV.delete 'HOSTS'
end
it 'filters by role from the :filter variable' do
hosts = dsl.roles(:web)
all = dsl.roles(:all)
SSHKit::Coordinator.expects(:new).with(hosts).returns(@coordinator)
dsl.set :filter, { role: 'web' }
dsl.on(all)
end
it 'filters by host and role from the :filter variable' do
all = dsl.roles(:all)
SSHKit::Coordinator.expects(:new).with([]).returns(@coordinator)
dsl.set :filter, { role: 'db', host: 'example3.com' }
dsl.on(all)
end
it 'filters by roles from the :filter variable' do
hosts = dsl.roles(:web)
all = dsl.roles(:all)
SSHKit::Coordinator.expects(:new).with(hosts).returns(@coordinator)
dsl.set :filter, { roles: 'web' }
dsl.on(all)
end
it 'filters by hosts and roles from the :filter variable' do
all = dsl.roles(:all)
SSHKit::Coordinator.expects(:new).with([]).returns(@coordinator)
dsl.set :filter, { roles: 'db', hosts: 'example3.com' }
dsl.on(all)
end
it 'filters from ENV[ROLES]' do
hosts = dsl.roles(:db)
all = dsl.roles(:all)
SSHKit::Coordinator.expects(:new).with(hosts).returns(@coordinator)
ENV['ROLES'] = 'db'
dsl.on(all)
end
it 'filters from ENV[HOSTS]' do
hosts = dsl.roles(:db)
all = dsl.roles(:all)
SSHKit::Coordinator.expects(:new).with(hosts).returns(@coordinator)
ENV['HOSTS'] = 'example5.com'
dsl.on(all)
end
it 'filters by ENV[HOSTS] && ENV[ROLES]' do
all = dsl.roles(:all)
SSHKit::Coordinator.expects(:new).with([]).returns(@coordinator)
ENV['HOSTS'] = 'example5.com'
ENV['ROLES'] = 'web'
dsl.on(all)
end
end
describe "when passed server literal names" do
before do
ENV.delete 'ROLES'
ENV.delete 'HOSTS'
@coordinator = mock('coordinator')
@coordinator.expects(:each).returns(nil)
end
it "selects nothing when a role filter is present" do
dsl.set :filter, { role: 'web' }
SSHKit::Coordinator.expects(:new).with([]).returns(@coordinator)
dsl.on('my.server')
end
it "selects using the string when a host filter is present" do
dsl.set :filter, { host: 'server.local' }
SSHKit::Coordinator.expects(:new).with(['server.local']).returns(@coordinator)
dsl.on('server.local')
end
it "doesn't select when a host filter is present that doesn't match" do
dsl.set :filter, { host: 'ruby.local' }
SSHKit::Coordinator.expects(:new).with([]).returns(@coordinator)
dsl.on('server.local')
end
it "selects nothing when a roles filter is present" do
dsl.set :filter, { roles: 'web' }
SSHKit::Coordinator.expects(:new).with([]).returns(@coordinator)
dsl.on('my.server')
end
it "selects using the string when a hosts filter is present" do
dsl.set :filter, { hosts: 'server.local' }
SSHKit::Coordinator.expects(:new).with(['server.local']).returns(@coordinator)
dsl.on('server.local')
end
it "doesn't select when a hosts filter is present that doesn't match" do
dsl.set :filter, { hosts: 'ruby.local' }
SSHKit::Coordinator.expects(:new).with([]).returns(@coordinator)
dsl.on('server.local')
end
end
end
describe 'role_properties()' do
before do
dsl.role :redis, %w[example1.com example2.com], redis: { port: 6379, type: :slave }
dsl.server 'example1.com', roles: %w{web}, active: true, web: { port: 80 }
dsl.server 'example2.com', roles: %w{web redis}, web: { port: 81 }, redis: { type: :master }
dsl.server 'example3.com', roles: %w{app}, primary: true
end
it 'retrieves properties for a single role as a set' do
rps = dsl.role_properties(:app)
expect(rps).to eq(Set[{ hostname: 'example3.com', role: :app}])
end
it 'retrieves properties for multiple roles as a set' do
rps = dsl.role_properties(:app, :web)
expect(rps).to eq(Set[{ hostname: 'example3.com', role: :app},{ hostname: 'example1.com', role: :web, port: 80},{ hostname: 'example2.com', role: :web, port: 81}])
end
it 'yields the properties for a single role' do
recipient = mock('recipient')
recipient.expects(:doit).with('example1.com', :redis, { port: 6379, type: :slave})
recipient.expects(:doit).with('example2.com', :redis, { port: 6379, type: :master})
dsl.role_properties(:redis) do |host, role, props|
recipient.doit(host, role, props)
end
end
it 'yields the properties for multiple roles' do
recipient = mock('recipient')
recipient.expects(:doit).with('example1.com', :redis, { port: 6379, type: :slave})
recipient.expects(:doit).with('example2.com', :redis, { port: 6379, type: :master})
recipient.expects(:doit).with('example3.com', :app, nil)
dsl.role_properties(:redis, :app) do |host, role, props|
recipient.doit(host, role, props)
end
end
it 'yields the merged properties for multiple roles' do
recipient = mock('recipient')
recipient.expects(:doit).with('example1.com', :redis, { port: 6379, type: :slave})
recipient.expects(:doit).with('example2.com', :redis, { port: 6379, type: :master})
recipient.expects(:doit).with('example1.com', :web, { port: 80 })
recipient.expects(:doit).with('example2.com', :web, { port: 81 })
dsl.role_properties(:redis, :web) do |host, role, props|
recipient.doit(host, role, props)
end
end
it 'honours a property filter before yielding' do
recipient = mock('recipient')
recipient.expects(:doit).with('example1.com', :redis, { port: 6379, type: :slave})
recipient.expects(:doit).with('example1.com', :web, { port: 80 })
dsl.role_properties(:redis, :web, select: :active) do |host, role, props|
recipient.doit(host, role, props)
end
end
end
end