master-cap 0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/Rakefile +2 -0
- data/Readme.markdown +101 -0
- data/lib/master-cap/chef.rb +105 -0
- data/lib/master-cap/default_translation_strategy.rb +14 -0
- data/lib/master-cap/git_repos_manager.rb +48 -0
- data/lib/master-cap/misc.rb +90 -0
- data/lib/master-cap/topology-directory.rb +64 -0
- data/lib/master-cap/topology.rb +71 -0
- data/master-cap.gemspec +25 -0
- metadata +87 -0
    
        data/Rakefile
    ADDED
    
    
    
        data/Readme.markdown
    ADDED
    
    | @@ -0,0 +1,101 @@ | |
| 1 | 
            +
            # What is it ?
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            Here are [capistrano](https://github.com/capistrano/capistrano/wiki) extensions to be used with [master-chef](https://github.com/octo-technology/master-chef)
         | 
| 4 | 
            +
             | 
| 5 | 
            +
            # How to use it
         | 
| 6 | 
            +
             | 
| 7 | 
            +
            * Add ``master-cap`` to your Gemfile
         | 
| 8 | 
            +
            * Load ``master-cap`` in your Capfile: ``require 'master-cap/topology-directory.rb'``
         | 
| 9 | 
            +
            * Create a subdirectory named ``topology``, and add your toplogy YAML files into
         | 
| 10 | 
            +
            * Enjoy :)
         | 
| 11 | 
            +
             | 
| 12 | 
            +
            # Topology file
         | 
| 13 | 
            +
             | 
| 14 | 
            +
            Example of a topology file: ``integ.yml``
         | 
| 15 | 
            +
             | 
| 16 | 
            +
            ```yml
         | 
| 17 | 
            +
            :topology:
         | 
| 18 | 
            +
              :app:
         | 
| 19 | 
            +
                :hostname: my_app_server.mydomain.net
         | 
| 20 | 
            +
                :type: linux_chef
         | 
| 21 | 
            +
                :roles:
         | 
| 22 | 
            +
                  - app_server
         | 
| 23 | 
            +
              :db:
         | 
| 24 | 
            +
                :hostname: db_server.mydomain.net
         | 
| 25 | 
            +
                :type: linux_chef
         | 
| 26 | 
            +
                :roles:
         | 
| 27 | 
            +
                  - db_server
         | 
| 28 | 
            +
              :redis:
         | 
| 29 | 
            +
                :hostname: redise.mydomain.net
         | 
| 30 | 
            +
                :type: linux_chef
         | 
| 31 | 
            +
                :roles:
         | 
| 32 | 
            +
                  - redis_server
         | 
| 33 | 
            +
            :cap_override:
         | 
| 34 | 
            +
              :my_specific_cap_param: 'toto'
         | 
| 35 | 
            +
            :default_role_list:
         | 
| 36 | 
            +
              - base
         | 
| 37 | 
            +
            ```
         | 
| 38 | 
            +
             | 
| 39 | 
            +
            # Capistrano commands
         | 
| 40 | 
            +
             | 
| 41 | 
            +
            ## Node selection
         | 
| 42 | 
            +
             | 
| 43 | 
            +
            * ``cap integ show``
         | 
| 44 | 
            +
            * ``cap app-integ show``
         | 
| 45 | 
            +
            * ``cap integ_db_server show``
         | 
| 46 | 
            +
             | 
| 47 | 
            +
            ## SSH command
         | 
| 48 | 
            +
             | 
| 49 | 
            +
            * ``cap integ check``: try to connect on each nodes with ssh
         | 
| 50 | 
            +
            * ``cap integ ssh_cmd -s cmd=uname``: exec ``uname``command on each nodes
         | 
| 51 | 
            +
             | 
| 52 | 
            +
            # Master-chef binding
         | 
| 53 | 
            +
             | 
| 54 | 
            +
            ## Configuration
         | 
| 55 | 
            +
             | 
| 56 | 
            +
            If you use only master-chef (no custom repo with Chef recipes), you have nothing to do.
         | 
| 57 | 
            +
             | 
| 58 | 
            +
            If you use a custom repo, please add following lines before requiring master-cap
         | 
| 59 | 
            +
             | 
| 60 | 
            +
            ```ruby
         | 
| 61 | 
            +
            set :git_repos_manager_class, 'SimpleGitReposManager'
         | 
| 62 | 
            +
             | 
| 63 | 
            +
            set :git_repos, [
         | 
| 64 | 
            +
              {
         | 
| 65 | 
            +
                :url => "http://github.com/octo-technology/master-chef.git",
         | 
| 66 | 
            +
                :ref => "aa597911b6d394dff27338c825aa966105cb6607",
         | 
| 67 | 
            +
              },
         | 
| 68 | 
            +
              {
         | 
| 69 | 
            +
                :url => "git@github.com:xxxx/yyyy.git",
         | 
| 70 | 
            +
                :local_path => "../yyyy",
         | 
| 71 | 
            +
              }
         | 
| 72 | 
            +
            ]
         | 
| 73 | 
            +
            ```
         | 
| 74 | 
            +
             | 
| 75 | 
            +
            Note:
         | 
| 76 | 
            +
            * ``:ref`` is used to lock master-chef version
         | 
| 77 | 
            +
            * ``:local_path`` is used to run ``chef:local``
         | 
| 78 | 
            +
             | 
| 79 | 
            +
            # Cap commands
         | 
| 80 | 
            +
             | 
| 81 | 
            +
            * ``cap integ chef``: run chef on all servers
         | 
| 82 | 
            +
            * ``cap app-integ chef:local``: run chef on app server, using local recipes. Usefull for debugging.
         | 
| 83 | 
            +
            * ``cap app-integ chef:generate_local_json``: generate the ``local.json`` file using current topology.
         | 
| 84 | 
            +
            * ``cap app-integ chef:stack``: output last chef stack trace
         | 
| 85 | 
            +
            * ``cap integ chef:stack``: purge all master-chef git repos caches
         | 
| 86 | 
            +
             | 
| 87 | 
            +
            # License
         | 
| 88 | 
            +
             | 
| 89 | 
            +
            Copyright 2012 Bertrand Paquet
         | 
| 90 | 
            +
             | 
| 91 | 
            +
            Licensed under the Apache License, Version 2.0 (the "License");
         | 
| 92 | 
            +
            you may not use this file except in compliance with the License.
         | 
| 93 | 
            +
            You may obtain a copy of the License at
         | 
| 94 | 
            +
             | 
| 95 | 
            +
            http://www.apache.org/licenses/LICENSE-2.0
         | 
| 96 | 
            +
             | 
| 97 | 
            +
            Unless required by applicable law or agreed to in writing, software
         | 
| 98 | 
            +
            distributed under the License is distributed on an "AS IS" BASIS,
         | 
| 99 | 
            +
            WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
         | 
| 100 | 
            +
            See the License for the specific language governing permissions and
         | 
| 101 | 
            +
            limitations under the License.
         | 
| @@ -0,0 +1,105 @@ | |
| 1 | 
            +
            require 'tempfile'
         | 
| 2 | 
            +
            require 'json'
         | 
| 3 | 
            +
             | 
| 4 | 
            +
            require File.join(File.dirname(__FILE__), 'git_repos_manager.rb')
         | 
| 5 | 
            +
             | 
| 6 | 
            +
            Capistrano::Configuration.instance.load do
         | 
| 7 | 
            +
             | 
| 8 | 
            +
              namespace :chef do
         | 
| 9 | 
            +
             | 
| 10 | 
            +
                set :master_chef_path, fetch(:master_chef_path, '../master-chef')
         | 
| 11 | 
            +
                set :git_repos_manager, Object.const_get(fetch(:git_repos_manager_class, 'EmptyGitReposManager')).new(self)
         | 
| 12 | 
            +
             | 
| 13 | 
            +
                task :generate_local_json do
         | 
| 14 | 
            +
                  set :user, chef_user
         | 
| 15 | 
            +
                  env = check_only_one_env
         | 
| 16 | 
            +
                  find_servers(:roles => chef_role).each do |s|
         | 
| 17 | 
            +
                    env, node = find_node s.host
         | 
| 18 | 
            +
                    r = []
         | 
| 19 | 
            +
                    r += (TOPOLOGY[env][:default_role_list] || []) unless node[:no_default_role]
         | 
| 20 | 
            +
                    r += node[:roles]
         | 
| 21 | 
            +
                    json = JSON.pretty_generate({
         | 
| 22 | 
            +
                      :repos => {
         | 
| 23 | 
            +
                        :git => git_repos_manager.list,
         | 
| 24 | 
            +
                      },
         | 
| 25 | 
            +
                      :run_list => r.map{|x| "role[#{x}]"},
         | 
| 26 | 
            +
                      :node_config => {
         | 
| 27 | 
            +
                        :topology_node_name => node[:topology_name]
         | 
| 28 | 
            +
                      }
         | 
| 29 | 
            +
                    })
         | 
| 30 | 
            +
                    puts json
         | 
| 31 | 
            +
                    f = Tempfile.new File.basename("local_json_#{name}")
         | 
| 32 | 
            +
                    f.write json
         | 
| 33 | 
            +
                    f.close
         | 
| 34 | 
            +
                    upload f.path, "/etc/chef/local.json", {:hosts => [s]}
         | 
| 35 | 
            +
                  end
         | 
| 36 | 
            +
                  git_repos_manager.list.each do |git_repo|
         | 
| 37 | 
            +
                    if git_repo =~ /^.+@.+:.+\.git$/
         | 
| 38 | 
            +
                      run "sudo ssh -o StrictHostKeyChecking=no #{git_repo.split(':')[0]} echo toto || true > /dev/null 2>&1", :roles => chef_role
         | 
| 39 | 
            +
                    end
         | 
| 40 | 
            +
                  end
         | 
| 41 | 
            +
                end
         | 
| 42 | 
            +
             | 
| 43 | 
            +
                def get_prefix
         | 
| 44 | 
            +
                  prefix = ""
         | 
| 45 | 
            +
                  prefix += "http_proxy=#{http_proxy} https_proxy=#{http_proxy}" if exists? :http_proxy
         | 
| 46 | 
            +
                  prefix
         | 
| 47 | 
            +
                end
         | 
| 48 | 
            +
             | 
| 49 | 
            +
                task :upload_git_tag_override, :roles => :linux_chef do
         | 
| 50 | 
            +
                  set :user, chef_user
         | 
| 51 | 
            +
                  env = check_only_one_env
         | 
| 52 | 
            +
             | 
| 53 | 
            +
                  git_tag_override = git_repos_manager.compute_override(env)
         | 
| 54 | 
            +
             | 
| 55 | 
            +
                  if git_tag_override
         | 
| 56 | 
            +
                    f = Tempfile.new File.basename("git_tag_override")
         | 
| 57 | 
            +
                    f.write JSON.dump(git_tag_override)
         | 
| 58 | 
            +
                    f.close
         | 
| 59 | 
            +
             | 
| 60 | 
            +
                    upload_to_root f.path, "/etc/chef/local.json.git_tag_override"
         | 
| 61 | 
            +
                  end
         | 
| 62 | 
            +
             | 
| 63 | 
            +
                end
         | 
| 64 | 
            +
             | 
| 65 | 
            +
                task :upload_topology, :roles => :linux_chef  do
         | 
| 66 | 
            +
                  set :user, chef_user
         | 
| 67 | 
            +
                  env = check_only_one_env
         | 
| 68 | 
            +
             | 
| 69 | 
            +
                  f = Tempfile.new File.basename("topology_env")
         | 
| 70 | 
            +
                  f.write YAML.dump(TOPOLOGY[env])
         | 
| 71 | 
            +
                  f.close
         | 
| 72 | 
            +
                  upload_to_root f.path, "/etc/chef/topology.yml"
         | 
| 73 | 
            +
                end
         | 
| 74 | 
            +
             | 
| 75 | 
            +
                task :default, :roles => chef_role  do
         | 
| 76 | 
            +
                  set :user, chef_user
         | 
| 77 | 
            +
                  upload_topology
         | 
| 78 | 
            +
                  upload_git_tag_override
         | 
| 79 | 
            +
                  run "#{get_prefix} /etc/chef/update.sh"
         | 
| 80 | 
            +
                end
         | 
| 81 | 
            +
             | 
| 82 | 
            +
                task :stack, :roles => chef_role  do
         | 
| 83 | 
            +
                  set :user, chef_user
         | 
| 84 | 
            +
                  run "sudo cat /var/chef/cache/chef-stacktrace.out"
         | 
| 85 | 
            +
                end
         | 
| 86 | 
            +
             | 
| 87 | 
            +
                task :purge_cache, :roles => chef_role do
         | 
| 88 | 
            +
                  set :user, chef_user
         | 
| 89 | 
            +
                  run "sudo rm -rf /var/chef/cache/git_repos"
         | 
| 90 | 
            +
                end
         | 
| 91 | 
            +
             | 
| 92 | 
            +
                task :local, :roles => chef_role  do
         | 
| 93 | 
            +
                  set :user, chef_user
         | 
| 94 | 
            +
                  upload_topology
         | 
| 95 | 
            +
                  find_servers(:roles => chef_role).each do |x|
         | 
| 96 | 
            +
                    prefix = ""
         | 
| 97 | 
            +
                    prefix = "PROXY=#{http_proxy}" if exists? :http_proxy
         | 
| 98 | 
            +
                    command = "sh -c \"#{prefix} #{master_chef_path}/runtime/chef_local.rb #{x} #{git_repos_manager.compute_local_path}\""
         | 
| 99 | 
            +
                    abort unless system command
         | 
| 100 | 
            +
                  end
         | 
| 101 | 
            +
                end
         | 
| 102 | 
            +
             | 
| 103 | 
            +
              end
         | 
| 104 | 
            +
             | 
| 105 | 
            +
            end
         | 
| @@ -0,0 +1,14 @@ | |
| 1 | 
            +
             | 
| 2 | 
            +
            class DefaultTranslationStrategy
         | 
| 3 | 
            +
             | 
| 4 | 
            +
              def node_name env, name, node, topology
         | 
| 5 | 
            +
                return name.to_s if topology[:no_node_suffix]
         | 
| 6 | 
            +
                "#{name}-#{env}"
         | 
| 7 | 
            +
              end
         | 
| 8 | 
            +
             | 
| 9 | 
            +
              def node_hostname env, name, node, topology
         | 
| 10 | 
            +
                return node[:hostname] if node[:hostname]
         | 
| 11 | 
            +
                raise "No hostname in #{node}"
         | 
| 12 | 
            +
              end
         | 
| 13 | 
            +
             | 
| 14 | 
            +
            end
         | 
| @@ -0,0 +1,48 @@ | |
| 1 | 
            +
             | 
| 2 | 
            +
            class EmptyGitReposManager
         | 
| 3 | 
            +
             | 
| 4 | 
            +
              def initialize cap
         | 
| 5 | 
            +
                @cap = cap
         | 
| 6 | 
            +
              end
         | 
| 7 | 
            +
             | 
| 8 | 
            +
              def compute_override env
         | 
| 9 | 
            +
                if @cap.exists? :master_chef_version
         | 
| 10 | 
            +
                  return {"http://github.com/octo-technology/master-chef.git" => @cap.master_chef_version}
         | 
| 11 | 
            +
                end
         | 
| 12 | 
            +
                nil
         | 
| 13 | 
            +
              end
         | 
| 14 | 
            +
             | 
| 15 | 
            +
              def list
         | 
| 16 | 
            +
                ["http://github.com/octo-technology/master-chef.git"]
         | 
| 17 | 
            +
              end
         | 
| 18 | 
            +
             | 
| 19 | 
            +
              def compute_local_path
         | 
| 20 | 
            +
                ""
         | 
| 21 | 
            +
              end
         | 
| 22 | 
            +
             | 
| 23 | 
            +
            end
         | 
| 24 | 
            +
             | 
| 25 | 
            +
            class SimpleGitReposManager
         | 
| 26 | 
            +
             | 
| 27 | 
            +
              def initialize cap
         | 
| 28 | 
            +
                @cap = cap
         | 
| 29 | 
            +
                @repos = @cap.fetch(:git_repos, [])
         | 
| 30 | 
            +
              end
         | 
| 31 | 
            +
             | 
| 32 | 
            +
              def compute_override env
         | 
| 33 | 
            +
                result = {}
         | 
| 34 | 
            +
                @repos.each do |x|
         | 
| 35 | 
            +
                  result[x[:url]] = x[:ref] if x[:ref]
         | 
| 36 | 
            +
                end
         | 
| 37 | 
            +
                result.size == 0 ? nil : result
         | 
| 38 | 
            +
              end
         | 
| 39 | 
            +
             | 
| 40 | 
            +
              def list
         | 
| 41 | 
            +
                @repos.map{|x| x[:url]}
         | 
| 42 | 
            +
              end
         | 
| 43 | 
            +
             | 
| 44 | 
            +
              def compute_local_path
         | 
| 45 | 
            +
                @repos.map{|x| x[:local_path] ? File.expand_path(x[:local_path]) : ""}.join(' ')
         | 
| 46 | 
            +
              end
         | 
| 47 | 
            +
             | 
| 48 | 
            +
            end
         | 
| @@ -0,0 +1,90 @@ | |
| 1 | 
            +
             | 
| 2 | 
            +
            require 'timeout'
         | 
| 3 | 
            +
             | 
| 4 | 
            +
            unless Capistrano::Configuration.respond_to?(:instance)
         | 
| 5 | 
            +
              abort "master-cap requires Capistrano 2"
         | 
| 6 | 
            +
            end
         | 
| 7 | 
            +
             | 
| 8 | 
            +
            Capistrano::Configuration.instance.load do
         | 
| 9 | 
            +
             | 
| 10 | 
            +
              def exec_local_with_timeout cmd, timeout
         | 
| 11 | 
            +
                pid = Process.fork { exec cmd }
         | 
| 12 | 
            +
                begin
         | 
| 13 | 
            +
                  Timeout.timeout(timeout) do
         | 
| 14 | 
            +
                    Process.wait(pid)
         | 
| 15 | 
            +
                    raise "Wrong return code for #{cmd} : #{$?.exitstatus}" unless $?.exitstatus == 0
         | 
| 16 | 
            +
                  end
         | 
| 17 | 
            +
                rescue Timeout::Error
         | 
| 18 | 
            +
                  Process.kill('TERM', pid)
         | 
| 19 | 
            +
                  raise "Timeout when executing #{cmd}"
         | 
| 20 | 
            +
                end
         | 
| 21 | 
            +
              end
         | 
| 22 | 
            +
             | 
| 23 | 
            +
              def exec_local cmd
         | 
| 24 | 
            +
                begin
         | 
| 25 | 
            +
                  abort "#{cmd} failed. Aborting..." unless system cmd
         | 
| 26 | 
            +
                rescue
         | 
| 27 | 
            +
                  abort "#{cmd} failed. Aborting..."
         | 
| 28 | 
            +
                end
         | 
| 29 | 
            +
              end
         | 
| 30 | 
            +
             | 
| 31 | 
            +
              def capture_local cmd
         | 
| 32 | 
            +
                begin
         | 
| 33 | 
            +
                  result = %x{#{cmd}}
         | 
| 34 | 
            +
                  abort "#{cmd} failed. Aborting..." unless $? == 0
         | 
| 35 | 
            +
                  result
         | 
| 36 | 
            +
                rescue
         | 
| 37 | 
            +
                  abort "#{cmd} failed. Aborting..."
         | 
| 38 | 
            +
                end
         | 
| 39 | 
            +
              end
         | 
| 40 | 
            +
             | 
| 41 | 
            +
              def upload_to_root local, remote, options = {}
         | 
| 42 | 
            +
                tmp_file = "/tmp/#{File.basename(remote)}"
         | 
| 43 | 
            +
                upload local, tmp_file, options
         | 
| 44 | 
            +
                run_root "mv #{tmp_file} #{remote}"
         | 
| 45 | 
            +
              end
         | 
| 46 | 
            +
             | 
| 47 | 
            +
              def run_root cmd
         | 
| 48 | 
            +
                run "sudo sh -c '#{cmd}'"
         | 
| 49 | 
            +
              end
         | 
| 50 | 
            +
             | 
| 51 | 
            +
              def error msg
         | 
| 52 | 
            +
                abort "Error : #{msg}"
         | 
| 53 | 
            +
              end
         | 
| 54 | 
            +
             | 
| 55 | 
            +
              def fill s, k
         | 
| 56 | 
            +
                s.length >= k ? s : fill(s.to_s + " ", k)
         | 
| 57 | 
            +
              end
         | 
| 58 | 
            +
             | 
| 59 | 
            +
              def multiple_capture command, options = nil
         | 
| 60 | 
            +
                result = {}
         | 
| 61 | 
            +
                launch = true
         | 
| 62 | 
            +
                launch = false if options && options[:hosts] && options[:hosts] == []
         | 
| 63 | 
            +
                if launch
         | 
| 64 | 
            +
                  begin
         | 
| 65 | 
            +
                    parallel(options) do |session|
         | 
| 66 | 
            +
                      session.else command do |channel, stream, data|
         | 
| 67 | 
            +
                        env, name, node = find_node(channel.properties[:server].host)
         | 
| 68 | 
            +
                        if block_given?
         | 
| 69 | 
            +
                          result[name] = yield name, node, data
         | 
| 70 | 
            +
                        else
         | 
| 71 | 
            +
                          result[name] = data
         | 
| 72 | 
            +
                        end
         | 
| 73 | 
            +
                      end
         | 
| 74 | 
            +
                    end
         | 
| 75 | 
            +
                  rescue Capistrano::ConnectionError => e
         | 
| 76 | 
            +
                    puts "\e[31m#{e}\e[0m"
         | 
| 77 | 
            +
                  end
         | 
| 78 | 
            +
                end
         | 
| 79 | 
            +
                if options
         | 
| 80 | 
            +
                  default_value = options[:default_value]
         | 
| 81 | 
            +
                  if default_value
         | 
| 82 | 
            +
                    find_servers.map{|x| find_node x.host}.each do |env, name, node|
         | 
| 83 | 
            +
                      result[name] = default_value unless result[name]
         | 
| 84 | 
            +
                    end
         | 
| 85 | 
            +
                  end
         | 
| 86 | 
            +
                end
         | 
| 87 | 
            +
                result
         | 
| 88 | 
            +
              end
         | 
| 89 | 
            +
             | 
| 90 | 
            +
            end
         | 
| @@ -0,0 +1,64 @@ | |
| 1 | 
            +
             | 
| 2 | 
            +
            require File.join(File.dirname(__FILE__), 'topology.rb')
         | 
| 3 | 
            +
             | 
| 4 | 
            +
            Capistrano::Configuration.instance.load do
         | 
| 5 | 
            +
             | 
| 6 | 
            +
              topology_directory = fetch(:topology_directory, 'topology')
         | 
| 7 | 
            +
             | 
| 8 | 
            +
              task :load_topology do
         | 
| 9 | 
            +
                Dir["#{topology_directory}/*.yml"].each do |f|
         | 
| 10 | 
            +
                  env = File.basename(f).split('.')[0]
         | 
| 11 | 
            +
                  TOPOLOGY[env] = YAML.load(File.read(f))
         | 
| 12 | 
            +
                  nodes = []
         | 
| 13 | 
            +
                  roles_map = {}
         | 
| 14 | 
            +
                  default_role_nodes = []
         | 
| 15 | 
            +
                  TOPOLOGY[env][:topology].each do |k, v|
         | 
| 16 | 
            +
                    v[:topology_name] = translation_strategy.node_name(env, k, v, TOPOLOGY[env])
         | 
| 17 | 
            +
                    v[:topology_hostname] = begin translation_strategy.node_hostname(env, k, v, TOPOLOGY[env]) rescue nil end
         | 
| 18 | 
            +
                    next unless v[:topology_hostname]
         | 
| 19 | 
            +
                    node_roles = v[:roles].map{|x| x.to_sym}
         | 
| 20 | 
            +
                    node_roles << v[:type].to_sym if v[:type]
         | 
| 21 | 
            +
                    n = {:name => k.to_s, :host => v[:topology_hostname], :roles => node_roles}
         | 
| 22 | 
            +
                    nodes << n
         | 
| 23 | 
            +
                    default_role_nodes << n unless v[:no_default_role]
         | 
| 24 | 
            +
                    task v[:topology_name] do
         | 
| 25 | 
            +
                      server n[:host], *node_roles if n[:host]
         | 
| 26 | 
            +
                      load_cap_override env
         | 
| 27 | 
            +
                    end
         | 
| 28 | 
            +
                    node_roles.each do |r|
         | 
| 29 | 
            +
                      roles_map[r] = [] unless roles_map[r]
         | 
| 30 | 
            +
                      roles_map[r] << n
         | 
| 31 | 
            +
                    end
         | 
| 32 | 
            +
                  end
         | 
| 33 | 
            +
                  roles_map.each do |r, v|
         | 
| 34 | 
            +
                    task "#{env}_#{r}" do
         | 
| 35 | 
            +
                      v.each do |k|
         | 
| 36 | 
            +
                        server k[:host], *(k[:roles]) if k[:host]
         | 
| 37 | 
            +
                        load_cap_override env
         | 
| 38 | 
            +
                      end
         | 
| 39 | 
            +
                    end
         | 
| 40 | 
            +
                  end
         | 
| 41 | 
            +
                  task "#{env}_default_role" do
         | 
| 42 | 
            +
                    default_role_nodes.each do |k|
         | 
| 43 | 
            +
                      server k[:host], *(k[:roles])
         | 
| 44 | 
            +
                      load_cap_override env
         | 
| 45 | 
            +
                    end
         | 
| 46 | 
            +
                  end
         | 
| 47 | 
            +
                  task env do
         | 
| 48 | 
            +
                    nodes.each do |k|
         | 
| 49 | 
            +
                      server k[:host], *(k[:roles]) if k[:host]
         | 
| 50 | 
            +
                      load_cap_override env
         | 
| 51 | 
            +
                    end
         | 
| 52 | 
            +
                  end
         | 
| 53 | 
            +
                end
         | 
| 54 | 
            +
              end
         | 
| 55 | 
            +
             | 
| 56 | 
            +
              on :load, "load_topology"
         | 
| 57 | 
            +
             | 
| 58 | 
            +
              def load_cap_override env
         | 
| 59 | 
            +
                TOPOLOGY[env][:cap_override].each do |k, v|
         | 
| 60 | 
            +
                  set k, v
         | 
| 61 | 
            +
                end if TOPOLOGY[env][:cap_override]
         | 
| 62 | 
            +
              end
         | 
| 63 | 
            +
             | 
| 64 | 
            +
            end
         | 
| @@ -0,0 +1,71 @@ | |
| 1 | 
            +
             | 
| 2 | 
            +
            require File.join(File.dirname(__FILE__), 'misc.rb')
         | 
| 3 | 
            +
            require File.join(File.dirname(__FILE__), 'default_translation_strategy.rb')
         | 
| 4 | 
            +
             | 
| 5 | 
            +
            unless Capistrano::Configuration.respond_to?(:instance)
         | 
| 6 | 
            +
              abort "master-cap requires Capistrano 2"
         | 
| 7 | 
            +
            end
         | 
| 8 | 
            +
             | 
| 9 | 
            +
            TOPOLOGY = {}
         | 
| 10 | 
            +
             | 
| 11 | 
            +
            Capistrano::Configuration.instance.load do
         | 
| 12 | 
            +
             | 
| 13 | 
            +
              set :chef_role, fetch(:chef_role, :linux_chef)
         | 
| 14 | 
            +
              set :chef_user, fetch(:chef_user, "chef")
         | 
| 15 | 
            +
              set :translation_strategy, Object.const_get(fetch(:translation_strategy_class, 'DefaultTranslationStrategy')).new
         | 
| 16 | 
            +
             | 
| 17 | 
            +
              task :check do
         | 
| 18 | 
            +
                find_servers(:roles => chef_role).sort_by{|s| s.host}.each do |s|
         | 
| 19 | 
            +
                  env, node = find_node s.host
         | 
| 20 | 
            +
                  begin
         | 
| 21 | 
            +
                    exec_local_with_timeout "ssh -o StrictHostKeyChecking=no #{chef_user}@#{node[:topology_hostname]} uname > /dev/null 2>&1", fetch(:check_timeout, 10)
         | 
| 22 | 
            +
                    puts "OK    : #{node[:topology_hostname]}"
         | 
| 23 | 
            +
                  rescue
         | 
| 24 | 
            +
                    puts "ERROR : Unable to join #{node[:topology_hostname]}"
         | 
| 25 | 
            +
                  end
         | 
| 26 | 
            +
                end
         | 
| 27 | 
            +
              end
         | 
| 28 | 
            +
             | 
| 29 | 
            +
              task :ssh_cmd, :roles => chef_role do
         | 
| 30 | 
            +
                error "Please specify command with -s cmd=" unless exists? :cmd
         | 
| 31 | 
            +
                set :user, chef_user
         | 
| 32 | 
            +
                run cmd
         | 
| 33 | 
            +
              end
         | 
| 34 | 
            +
             | 
| 35 | 
            +
              task :show do
         | 
| 36 | 
            +
                ss = find_servers
         | 
| 37 | 
            +
                puts "Number of selected servers: #{ss.length}"
         | 
| 38 | 
            +
                ss.each do |s|
         | 
| 39 | 
            +
                  env, node = find_node s.host
         | 
| 40 | 
            +
                  puts "#{fill(node[:topology_name], 30)} #{fill(node[:topology_hostname], 50)} [#{node[:roles].sort.join(',')}]"
         | 
| 41 | 
            +
                end
         | 
| 42 | 
            +
              end
         | 
| 43 | 
            +
             | 
| 44 | 
            +
              def find_node node_name
         | 
| 45 | 
            +
                TOPOLOGY.each do |k, v|
         | 
| 46 | 
            +
                  v[:topology].each do |name, node|
         | 
| 47 | 
            +
                    return [k, node] if translation_strategy.node_hostname(k, name, node, v) == node_name
         | 
| 48 | 
            +
                  end
         | 
| 49 | 
            +
                end
         | 
| 50 | 
            +
                error "Node not found #{node_name}"
         | 
| 51 | 
            +
              end
         | 
| 52 | 
            +
             | 
| 53 | 
            +
            def check_only_one_env servers = nil
         | 
| 54 | 
            +
              servers = find_servers unless servers
         | 
| 55 | 
            +
              env_list = {}
         | 
| 56 | 
            +
              servers.each do |s|
         | 
| 57 | 
            +
                env, name, node = find_node(s.is_a?(String) ? s : s.host)
         | 
| 58 | 
            +
                env_list[env] = :toto
         | 
| 59 | 
            +
              end
         | 
| 60 | 
            +
              error "Please, do not launch this command without env" if env_list.keys.size == 0
         | 
| 61 | 
            +
              error "Please, do not launch this command on two env : #{env_list.keys.join(' ')}" if env_list.keys.size != 1
         | 
| 62 | 
            +
              env = env_list.keys.first
         | 
| 63 | 
            +
             | 
| 64 | 
            +
              check_only_one_env_callback(env, servers) if exists? :check_only_one_env_callback
         | 
| 65 | 
            +
             | 
| 66 | 
            +
              env
         | 
| 67 | 
            +
            end
         | 
| 68 | 
            +
             | 
| 69 | 
            +
            end
         | 
| 70 | 
            +
             | 
| 71 | 
            +
            require File.join(File.dirname(__FILE__), 'chef.rb')
         | 
    
        data/master-cap.gemspec
    ADDED
    
    | @@ -0,0 +1,25 @@ | |
| 1 | 
            +
            # -*- encoding: utf-8 -*-
         | 
| 2 | 
            +
            $:.push File.expand_path("../lib", __FILE__)
         | 
| 3 | 
            +
             | 
| 4 | 
            +
            Gem::Specification.new do |s|
         | 
| 5 | 
            +
             | 
| 6 | 
            +
              s.name        = "master-cap"
         | 
| 7 | 
            +
              s.version     = "0.1"
         | 
| 8 | 
            +
              s.platform    = Gem::Platform::RUBY
         | 
| 9 | 
            +
              s.authors     = ["Bertrand Paquet"]
         | 
| 10 | 
            +
              s.email       = ["bertrand.paquet@gmail.com"]
         | 
| 11 | 
            +
              s.homepage    = "http://github.com/bpaquet/master-cap"
         | 
| 12 | 
            +
              s.summary     = "Capistrano tasks designed to work with master-chef"
         | 
| 13 | 
            +
              s.description = "Capistrano tasks designed to work with master-chef"
         | 
| 14 | 
            +
              s.files         = `git ls-files`.split("\n")
         | 
| 15 | 
            +
              s.test_files    = `git ls-files -- {test,spec,features}/*`.split("\n")
         | 
| 16 | 
            +
              s.executables   = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
         | 
| 17 | 
            +
              s.require_paths = ["lib"]
         | 
| 18 | 
            +
              s.extra_rdoc_files = [
         | 
| 19 | 
            +
                "Readme.markdown"
         | 
| 20 | 
            +
              ]
         | 
| 21 | 
            +
             | 
| 22 | 
            +
              s.specification_version = 3
         | 
| 23 | 
            +
              s.add_runtime_dependency(%q<capistrano>, [">= 2"])
         | 
| 24 | 
            +
              s.add_runtime_dependency(%q<json>)
         | 
| 25 | 
            +
            end
         | 
    
        metadata
    ADDED
    
    | @@ -0,0 +1,87 @@ | |
| 1 | 
            +
            --- !ruby/object:Gem::Specification
         | 
| 2 | 
            +
            name: master-cap
         | 
| 3 | 
            +
            version: !ruby/object:Gem::Version
         | 
| 4 | 
            +
              version: '0.1'
         | 
| 5 | 
            +
              prerelease: 
         | 
| 6 | 
            +
            platform: ruby
         | 
| 7 | 
            +
            authors:
         | 
| 8 | 
            +
            - Bertrand Paquet
         | 
| 9 | 
            +
            autorequire: 
         | 
| 10 | 
            +
            bindir: bin
         | 
| 11 | 
            +
            cert_chain: []
         | 
| 12 | 
            +
            date: 2013-02-21 00:00:00.000000000 Z
         | 
| 13 | 
            +
            dependencies:
         | 
| 14 | 
            +
            - !ruby/object:Gem::Dependency
         | 
| 15 | 
            +
              name: capistrano
         | 
| 16 | 
            +
              requirement: !ruby/object:Gem::Requirement
         | 
| 17 | 
            +
                none: false
         | 
| 18 | 
            +
                requirements:
         | 
| 19 | 
            +
                - - ! '>='
         | 
| 20 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 21 | 
            +
                    version: '2'
         | 
| 22 | 
            +
              type: :runtime
         | 
| 23 | 
            +
              prerelease: false
         | 
| 24 | 
            +
              version_requirements: !ruby/object:Gem::Requirement
         | 
| 25 | 
            +
                none: false
         | 
| 26 | 
            +
                requirements:
         | 
| 27 | 
            +
                - - ! '>='
         | 
| 28 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 29 | 
            +
                    version: '2'
         | 
| 30 | 
            +
            - !ruby/object:Gem::Dependency
         | 
| 31 | 
            +
              name: json
         | 
| 32 | 
            +
              requirement: !ruby/object:Gem::Requirement
         | 
| 33 | 
            +
                none: false
         | 
| 34 | 
            +
                requirements:
         | 
| 35 | 
            +
                - - ! '>='
         | 
| 36 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 37 | 
            +
                    version: '0'
         | 
| 38 | 
            +
              type: :runtime
         | 
| 39 | 
            +
              prerelease: false
         | 
| 40 | 
            +
              version_requirements: !ruby/object:Gem::Requirement
         | 
| 41 | 
            +
                none: false
         | 
| 42 | 
            +
                requirements:
         | 
| 43 | 
            +
                - - ! '>='
         | 
| 44 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 45 | 
            +
                    version: '0'
         | 
| 46 | 
            +
            description: Capistrano tasks designed to work with master-chef
         | 
| 47 | 
            +
            email:
         | 
| 48 | 
            +
            - bertrand.paquet@gmail.com
         | 
| 49 | 
            +
            executables: []
         | 
| 50 | 
            +
            extensions: []
         | 
| 51 | 
            +
            extra_rdoc_files:
         | 
| 52 | 
            +
            - Readme.markdown
         | 
| 53 | 
            +
            files:
         | 
| 54 | 
            +
            - Rakefile
         | 
| 55 | 
            +
            - Readme.markdown
         | 
| 56 | 
            +
            - lib/master-cap/chef.rb
         | 
| 57 | 
            +
            - lib/master-cap/default_translation_strategy.rb
         | 
| 58 | 
            +
            - lib/master-cap/git_repos_manager.rb
         | 
| 59 | 
            +
            - lib/master-cap/misc.rb
         | 
| 60 | 
            +
            - lib/master-cap/topology-directory.rb
         | 
| 61 | 
            +
            - lib/master-cap/topology.rb
         | 
| 62 | 
            +
            - master-cap.gemspec
         | 
| 63 | 
            +
            homepage: http://github.com/bpaquet/master-cap
         | 
| 64 | 
            +
            licenses: []
         | 
| 65 | 
            +
            post_install_message: 
         | 
| 66 | 
            +
            rdoc_options: []
         | 
| 67 | 
            +
            require_paths:
         | 
| 68 | 
            +
            - lib
         | 
| 69 | 
            +
            required_ruby_version: !ruby/object:Gem::Requirement
         | 
| 70 | 
            +
              none: false
         | 
| 71 | 
            +
              requirements:
         | 
| 72 | 
            +
              - - ! '>='
         | 
| 73 | 
            +
                - !ruby/object:Gem::Version
         | 
| 74 | 
            +
                  version: '0'
         | 
| 75 | 
            +
            required_rubygems_version: !ruby/object:Gem::Requirement
         | 
| 76 | 
            +
              none: false
         | 
| 77 | 
            +
              requirements:
         | 
| 78 | 
            +
              - - ! '>='
         | 
| 79 | 
            +
                - !ruby/object:Gem::Version
         | 
| 80 | 
            +
                  version: '0'
         | 
| 81 | 
            +
            requirements: []
         | 
| 82 | 
            +
            rubyforge_project: 
         | 
| 83 | 
            +
            rubygems_version: 1.8.24
         | 
| 84 | 
            +
            signing_key: 
         | 
| 85 | 
            +
            specification_version: 3
         | 
| 86 | 
            +
            summary: Capistrano tasks designed to work with master-chef
         | 
| 87 | 
            +
            test_files: []
         |