mirror of
https://github.com/capistrano/capistrano
synced 2023-03-27 23:21:18 -04:00
b998303f70
When a literal hostname was supplied instead of a server object created by the DSL 'server' keyword, on-filtering would not work. Now host filtering works as expected, and role filtering causes no servers to be returned (as literally defined servers have no roles)
615 lines
19 KiB
Ruby
615 lines
19 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
|
|
|
|
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 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
|
|
|
|
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
|