From fe10a84418ee62511f83f7366612d93bf82e2fe2 Mon Sep 17 00:00:00 2001 From: seenmyfate Date: Sun, 28 Apr 2013 19:00:52 +0100 Subject: [PATCH] Allow each server to have a set of roles This closes issue #429 --- README.md | 36 +------ lib/capistrano/configuration.rb | 91 ++---------------- lib/capistrano/configuration/question.rb | 42 ++++++++ lib/capistrano/configuration/server.rb | 11 +++ lib/capistrano/configuration/servers.rb | 50 ++++++++++ .../capistrano/configuration/question_spec.rb | 49 ++++++++++ .../capistrano/configuration/servers_spec.rb | 67 +++++++++++++ spec/lib/capistrano/configuration_spec.rb | 95 ------------------- 8 files changed, 229 insertions(+), 212 deletions(-) create mode 100644 lib/capistrano/configuration/question.rb create mode 100644 lib/capistrano/configuration/server.rb create mode 100644 lib/capistrano/configuration/servers.rb create mode 100644 spec/lib/capistrano/configuration/question_spec.rb create mode 100644 spec/lib/capistrano/configuration/servers_spec.rb diff --git a/README.md b/README.md index 7c83d005..0159465c 100644 --- a/README.md +++ b/README.md @@ -1,40 +1,6 @@ # Capistrano [![Build Status](https://travis-ci.org/capistrano/capistrano.png?branch=v3)](https://travis-ci.org/capistrano/capistrano) -wip - aim here is to get 'something' up and running - -TODO: - - - [x] harness rake for dsl - - [x] create a working capify equivalent - - [x] create Capfile - - [x] create lib/tasks/deploy - - [x] create config/deploy/ - - [x] write config/deploy.rb with example configuration - - - [x] basic configuration object - - [x] basic 'capistrano/deploy' noop example - - - [x] before/after task hooks - - [x] handle multi stage - - [x] pass any necessary configuration from deploy.rb to SSHKit - - - [x] support set/fetch/role configuration - - [x] basic deploy - - [x] ask - - [x] add `deploy:check` - - [x] prefer `roles(:all)` over `all_roles` - - [x] simplify default deploy - - [x] support setting default environment variables - - [x] support existing significant configuration variables - - [x] set configuration defaults, add commented out examples to templates - - [x] basic rollback - - [x] support primary servers `on primary :db` - - [x] rails specific tasks (see [here](https://github.com/seenmyfate/capistrano-rails)) - - [x] auto load tasks for scm based on variable - - [ ] run locally - - [ ] better descriptions for tasks - - [ ] add examples to README - - [ ] add task packaging run through +TODO ## Installation diff --git a/lib/capistrano/configuration.rb b/lib/capistrano/configuration.rb index 15d9a6b3..6e2c7650 100644 --- a/lib/capistrano/configuration.rb +++ b/lib/capistrano/configuration.rb @@ -1,3 +1,7 @@ +require_relative 'configuration/question' +require_relative 'configuration/servers' +require_relative 'configuration/server' + module Capistrano class Configuration @@ -26,15 +30,15 @@ module Capistrano end def role(name, servers) - roles.add_role(name, servers) + servers.add_role(name, servers) end def roles_for(names) - roles.fetch_roles(names) + servers.fetch_roles(names) end def primary(role) - roles.fetch_primary(role) + servers.fetch_primary(role) end def configure_backend @@ -55,8 +59,8 @@ module Capistrano private - def roles - @roles ||= Roles.new + def servers + @servers ||= Servers.new end def config @@ -71,83 +75,6 @@ module Capistrano end end - class Question - def initialize(env, key, default) - @env, @key, @default = env, key, default - end - - def call - ask_question - save_response - end - - private - attr_reader :env, :key, :default - - def ask_question - $stdout.puts question - end - - def save_response - env.set(key, value) - end - - def value - if response.empty? - default - else - response - end - end - - def response - @response ||= $stdin.gets.chomp - end - - def question - I18n.t(:question, key: key, default_value: default, scope: :capistrano) - end - end - - class Roles - include Enumerable - - def add_role(name, servers) - roles[name] = servers.map { |server| Server.new(server) } - end - - def fetch_roles(names) - roles_for(names).flatten.uniq - end - - def fetch_primary(role) - fetch(role).first - end - - def each - roles.each { |role| yield role } - end - - private - - def fetch(name) - roles.fetch(name) { raise "role #{name} is not defined" } - end - - def roles_for(names) - if names.include?(:all) - roles.values - else - names.map { |name| fetch name } - end - end - - def roles - @roles ||= Hash.new - end - end - - class Server < SSHKit::Host;end; end end diff --git a/lib/capistrano/configuration/question.rb b/lib/capistrano/configuration/question.rb new file mode 100644 index 00000000..27da09f3 --- /dev/null +++ b/lib/capistrano/configuration/question.rb @@ -0,0 +1,42 @@ +module Capistrano + class Configuration + class Question + + def initialize(env, key, default) + @env, @key, @default = env, key, default + end + + def call + ask_question + save_response + end + + private + attr_reader :env, :key, :default + + def ask_question + $stdout.puts question + end + + def save_response + env.set(key, value) + end + + def value + if response.empty? + default + else + response + end + end + + def response + @response ||= $stdin.gets.chomp + end + + def question + I18n.t(:question, key: key, default_value: default, scope: :capistrano) + end + end + end +end diff --git a/lib/capistrano/configuration/server.rb b/lib/capistrano/configuration/server.rb new file mode 100644 index 00000000..9b502393 --- /dev/null +++ b/lib/capistrano/configuration/server.rb @@ -0,0 +1,11 @@ +require 'set' +module Capistrano + class Configuration + class Server < SSHKit::Host + + def roles + @roles ||= Set.new + end + end + end +end diff --git a/lib/capistrano/configuration/servers.rb b/lib/capistrano/configuration/servers.rb new file mode 100644 index 00000000..ce516a5f --- /dev/null +++ b/lib/capistrano/configuration/servers.rb @@ -0,0 +1,50 @@ +require 'set' +module Capistrano + class Configuration + class Servers + include Enumerable + + def add_role(role, hosts) + hosts.each do |host| + server = server_from_host(host) + server.roles << role + servers << server + end + end + + def fetch_roles(names) + roles_for(names) + end + + def fetch_primary(role) + fetch(role).first + end + + def each + servers.each { |server| yield server } + end + + private + + def server_from_host(host) + servers.find { |server| server.hostname == host } || Server.new(host) + end + + def fetch(name) + servers.find_all { |server| server.roles.include? name } + end + + def roles_for(names) + if names.include?(:all) + servers + else + names.flat_map { |name| fetch name }.uniq + end + end + + def servers + @servers ||= Set.new + end + end + end +end diff --git a/spec/lib/capistrano/configuration/question_spec.rb b/spec/lib/capistrano/configuration/question_spec.rb new file mode 100644 index 00000000..bdad0bc0 --- /dev/null +++ b/spec/lib/capistrano/configuration/question_spec.rb @@ -0,0 +1,49 @@ +require 'spec_helper' + +module Capistrano + describe Configuration::Question do + let(:question) { Configuration::Question.new(env, key, default) } + let(:default) { :default } + let(:key) { :branch } + let(:env) { stub } + + describe '.new' do + it 'takes a key, default' do + question + end + end + + describe '#call' do + subject { question.call } + + context 'value is entered' do + let(:branch) { 'branch' } + + before do + $stdout.expects(:puts).with('Please enter branch: |default|') + $stdin.expects(:gets).returns(branch) + end + + it 'sets the value' do + env.expects(:set).with(key, branch) + question.call + end + end + + context 'value is not entered' do + let(:branch) { default } + + before do + $stdout.expects(:puts).with('Please enter branch: |default|') + $stdin.expects(:gets).returns('') + end + + it 'sets the default as the value' do + env.expects(:set).with(key, branch) + question.call + end + end + end + end + +end diff --git a/spec/lib/capistrano/configuration/servers_spec.rb b/spec/lib/capistrano/configuration/servers_spec.rb new file mode 100644 index 00000000..f4534b07 --- /dev/null +++ b/spec/lib/capistrano/configuration/servers_spec.rb @@ -0,0 +1,67 @@ +require 'spec_helper' + +module Capistrano + class Configuration + describe Servers do + let(:servers) { Servers.new } + + describe 'adding a role' do + subject { servers.add_role(:app, %w{1 2}) } + + it 'adds two new server instances' do + expect{subject}.to change{servers.count}.from(0).to(2) + end + end + + describe 'adding a role to an existing server' do + before do + servers.add_role(:web, %w{1 2}) + servers.add_role(:app, %w{1 2}) + end + + it 'adds new roles to existing servers' do + expect(servers.count).to eq 2 + end + end + + describe 'collecting server roles' do + let(:app) { Set.new([:app]) } + let(:web_app) { Set.new([:web, :app]) } + let(:web) { Set.new([:web]) } + + before do + servers.add_role(:app, %w{1 2 3}) + servers.add_role(:web, %w{2 3 4}) + end + + it 'returns an array of the roles' do + expect(servers.fetch_roles([:app]).collect(&:roles)).to eq [app, web_app, web_app] + expect(servers.fetch_roles([:web]).collect(&:roles)).to eq [web_app, web_app, web] + end + end + + describe 'fetching servers' do + before do + servers.add_role(:app, %w{1 2}) + servers.add_role(:web, %w{2 3}) + end + + it 'returns the correct app servers' do + expect(servers.fetch_roles([:app]).map(&:hostname)).to eq %w{1 2} + end + + it 'returns the correct web servers' do + expect(servers.fetch_roles([:web]).map(&:hostname)).to eq %w{2 3} + end + + it 'returns the correct app and web servers' do + expect(servers.fetch_roles([:app, :web]).map(&:hostname)).to eq %w{1 2 3} + end + + it 'returns all servers' do + expect(servers.fetch_roles([:all]).map(&:hostname)).to eq %w{1 2 3} + end + end + end + end +end diff --git a/spec/lib/capistrano/configuration_spec.rb b/spec/lib/capistrano/configuration_spec.rb index 4b67a98b..ed4e6340 100644 --- a/spec/lib/capistrano/configuration_spec.rb +++ b/spec/lib/capistrano/configuration_spec.rb @@ -1,57 +1,6 @@ require 'spec_helper' module Capistrano - - class Configuration - describe Roles do - let(:roles) { Roles.new } - - describe 'adding a role' do - subject { roles.add_role(:app, %w{server1 server2}) } - - before do - Server.expects(:new).with('server1') - Server.expects(:new).with('server2') - end - - it 'adds the role, and creates new server instances' do - expect{subject}.to change{roles.count}.from(0).to(1) - end - end - - describe 'fetching servers' do - let(:server1) { stub } - let(:server2) { stub } - let(:server3) { stub } - - before do - Server.stubs(:new).with('server1').returns(server1) - Server.stubs(:new).with('server2').returns(server2) - Server.stubs(:new).with('server3').returns(server3) - - roles.add_role(:app, %w{server1 server2}) - roles.add_role(:web, %w{server2 server3}) - end - - it 'returns the correct app servers' do - expect(roles.fetch_roles([:app])).to eq [server1, server2] - end - - it 'returns the correct web servers' do - expect(roles.fetch_roles([:web])).to eq [server2, server3] - end - - it 'returns the correct app and web servers' do - expect(roles.fetch_roles([:app, :web])).to eq [server1, server2, server3] - end - - it 'returns all servers' do - expect(roles.fetch_roles([:all])).to eq [server1, server2, server3] - end - end - end - end - describe Configuration do let(:config) { Configuration.new } let(:roles) { stub } @@ -128,48 +77,4 @@ module Capistrano end end - describe Configuration::Question do - let(:question) { Configuration::Question.new(env, key, default) } - let(:default) { :default } - let(:key) { :branch } - let(:env) { stub } - - describe '.new' do - it 'takes a key, default' do - question - end - end - - describe '#call' do - subject { question.call } - - context 'value is entered' do - let(:branch) { 'branch' } - - before do - $stdout.expects(:puts).with('Please enter branch: |default|') - $stdin.expects(:gets).returns(branch) - end - - it 'sets the value' do - env.expects(:set).with(key, branch) - question.call - end - end - - context 'value is not entered' do - let(:branch) { default } - - before do - $stdout.expects(:puts).with('Please enter branch: |default|') - $stdin.expects(:gets).returns('') - end - - it 'sets the default as the value' do - env.expects(:set).with(key, branch) - question.call - end - end - end - end end