blower 2.0.0 → 2.1.2
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.
- checksums.yaml +4 -4
 - data/bin/blow +12 -5
 - data/lib/blower/context.rb +108 -41
 - data/lib/blower/host.rb +35 -16
 - data/lib/blower/logger.rb +48 -14
 - data/lib/blower/version.rb +1 -1
 - data/lib/blower.rb +0 -1
 - metadata +17 -3
 - data/lib/blower/host_group.rb +0 -36
 
    
        checksums.yaml
    CHANGED
    
    | 
         @@ -1,7 +1,7 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            ---
         
     | 
| 
       2 
2 
     | 
    
         
             
            SHA1:
         
     | 
| 
       3 
     | 
    
         
            -
              metadata.gz:  
     | 
| 
       4 
     | 
    
         
            -
              data.tar.gz:  
     | 
| 
      
 3 
     | 
    
         
            +
              metadata.gz: 1346720ababac71f881dae28ba20012eaa110361
         
     | 
| 
      
 4 
     | 
    
         
            +
              data.tar.gz: 567937a547591d6bcaaafc55f10d3c515fd78969
         
     | 
| 
       5 
5 
     | 
    
         
             
            SHA512:
         
     | 
| 
       6 
     | 
    
         
            -
              metadata.gz:  
     | 
| 
       7 
     | 
    
         
            -
              data.tar.gz:  
     | 
| 
      
 6 
     | 
    
         
            +
              metadata.gz: e94f4c1b1074a20ff18fd7bf416af1fe52436e473eba52c7a53ea642c46fdbae5dc9a48f2b3e30c4458f49ccab4c69010426494978590be4751824708571dd06
         
     | 
| 
      
 7 
     | 
    
         
            +
              data.tar.gz: 7013297bf513abbb9b364dc429f78fa71d21916bb6c773816eeb1ba71aa23fa183aee0bf885ca6ace9968475dee576b2b9bc5cf44f4db5aeb264f7523d8c2b73
         
     | 
    
        data/bin/blow
    CHANGED
    
    | 
         @@ -3,18 +3,25 @@ require 'blower' 
     | 
|
| 
       3 
3 
     | 
    
         
             
            require 'pathname'
         
     | 
| 
       4 
4 
     | 
    
         
             
            require 'optparse'
         
     | 
| 
       5 
5 
     | 
    
         | 
| 
      
 6 
     | 
    
         
            +
            $LOGLEVEL = :info
         
     | 
| 
      
 7 
     | 
    
         
            +
             
     | 
| 
       6 
8 
     | 
    
         
             
            OptionParser.new do |opts|
         
     | 
| 
       7 
9 
     | 
    
         
             
              opts.banner = "Usage: blow [options] task..."
         
     | 
| 
       8 
10 
     | 
    
         
             
              opts.on "-d DIR", "Change directory" do |v|
         
     | 
| 
       9 
11 
     | 
    
         
             
                Dir.chdir v
         
     | 
| 
       10 
12 
     | 
    
         
             
              end
         
     | 
| 
      
 13 
     | 
    
         
            +
              opts.on "-l LEVEL", "Minimal log level" do |l|
         
     | 
| 
      
 14 
     | 
    
         
            +
                $LOGLEVEL = l.downcase.to_sym
         
     | 
| 
      
 15 
     | 
    
         
            +
              end
         
     | 
| 
       11 
16 
     | 
    
         
             
            end.order!
         
     | 
| 
       12 
17 
     | 
    
         | 
| 
      
 18 
     | 
    
         
            +
            context = Blower::Context.new([".", Dir.pwd])
         
     | 
| 
      
 19 
     | 
    
         
            +
            context.run "Blowfile", optional: true
         
     | 
| 
       13 
20 
     | 
    
         
             
            begin
         
     | 
| 
       14 
     | 
    
         
            -
               
     | 
| 
       15 
     | 
    
         
            -
             
     | 
| 
       16 
     | 
    
         
            -
              context.run "Blowfile"
         
     | 
| 
       17 
     | 
    
         
            -
              ARGV.each do |arg|
         
     | 
| 
       18 
     | 
    
         
            -
                context.run arg
         
     | 
| 
      
 21 
     | 
    
         
            +
              until ARGV.empty?
         
     | 
| 
      
 22 
     | 
    
         
            +
                context.run ARGV.shift
         
     | 
| 
       19 
23 
     | 
    
         
             
              end
         
     | 
| 
      
 24 
     | 
    
         
            +
            rescue RuntimeError => e
         
     | 
| 
      
 25 
     | 
    
         
            +
              puts e.message.colorize(:red)
         
     | 
| 
      
 26 
     | 
    
         
            +
              exit 1
         
     | 
| 
       20 
27 
     | 
    
         
             
            end
         
     | 
    
        data/lib/blower/context.rb
    CHANGED
    
    | 
         @@ -2,77 +2,138 @@ require 'forwardable' 
     | 
|
| 
       2 
2 
     | 
    
         | 
| 
       3 
3 
     | 
    
         
             
            module Blower
         
     | 
| 
       4 
4 
     | 
    
         | 
| 
      
 5 
     | 
    
         
            +
              # Blower tasks are executed within a context.
         
     | 
| 
      
 6 
     | 
    
         
            +
              #
         
     | 
| 
      
 7 
     | 
    
         
            +
              # The context can be used to share information between tasks, by storing it in instance variables.
         
     | 
| 
      
 8 
     | 
    
         
            +
              #
         
     | 
| 
       5 
9 
     | 
    
         
             
              class Context
         
     | 
| 
       6 
10 
     | 
    
         
             
                extend Forwardable
         
     | 
| 
       7 
11 
     | 
    
         | 
| 
      
 12 
     | 
    
         
            +
                # Search path for tasks.
         
     | 
| 
       8 
13 
     | 
    
         
             
                attr_accessor :path
         
     | 
| 
       9 
     | 
    
         
            -
             
     | 
| 
       10 
     | 
    
         
            -
                 
     | 
| 
      
 14 
     | 
    
         
            +
             
     | 
| 
      
 15 
     | 
    
         
            +
                # The target hosts.
         
     | 
| 
      
 16 
     | 
    
         
            +
                attr_accessor :hosts
         
     | 
| 
       11 
17 
     | 
    
         | 
| 
       12 
18 
     | 
    
         
             
                def initialize (path)
         
     | 
| 
       13 
19 
     | 
    
         
             
                  @path = path
         
     | 
| 
      
 20 
     | 
    
         
            +
                  @hosts = []
         
     | 
| 
       14 
21 
     | 
    
         
             
                  @have_seen = {}
         
     | 
| 
       15 
22 
     | 
    
         
             
                end
         
     | 
| 
       16 
23 
     | 
    
         | 
| 
       17 
     | 
    
         
            -
                def log 
     | 
| 
       18 
     | 
    
         
            -
                   
     | 
| 
       19 
     | 
    
         
            -
             
     | 
| 
      
 24 
     | 
    
         
            +
                def log
         
     | 
| 
      
 25 
     | 
    
         
            +
                  Logger.instance
         
     | 
| 
      
 26 
     | 
    
         
            +
                end
         
     | 
| 
      
 27 
     | 
    
         
            +
             
     | 
| 
      
 28 
     | 
    
         
            +
                def add_host (spec)
         
     | 
| 
      
 29 
     | 
    
         
            +
                  host = Host.new(*spec) unless spec.is_a?(Host)
         
     | 
| 
      
 30 
     | 
    
         
            +
                  @hosts << host
         
     | 
| 
      
 31 
     | 
    
         
            +
                end
         
     | 
| 
      
 32 
     | 
    
         
            +
             
     | 
| 
      
 33 
     | 
    
         
            +
                # Execute the block on one host.
         
     | 
| 
      
 34 
     | 
    
         
            +
                # @param host Host to use. If nil, a random host is picked.
         
     | 
| 
      
 35 
     | 
    
         
            +
                def one_host (host = nil, &block)
         
     | 
| 
      
 36 
     | 
    
         
            +
                  map [host || target.hosts.sample], &block
         
     | 
| 
       20 
37 
     | 
    
         
             
                end
         
     | 
| 
       21 
38 
     | 
    
         | 
| 
       22 
     | 
    
         
            -
                 
     | 
| 
       23 
     | 
    
         
            -
             
     | 
| 
      
 39 
     | 
    
         
            +
                # Execute the block once for each host.
         
     | 
| 
      
 40 
     | 
    
         
            +
                # Each block executes in a copy of the context.
         
     | 
| 
      
 41 
     | 
    
         
            +
                def each (hosts = @hosts, &block)
         
     | 
| 
      
 42 
     | 
    
         
            +
                  map(hosts, &block)
         
     | 
| 
      
 43 
     | 
    
         
            +
                  nil
         
     | 
| 
       24 
44 
     | 
    
         
             
                end
         
     | 
| 
       25 
45 
     | 
    
         | 
| 
       26 
     | 
    
         
            -
                 
     | 
| 
       27 
     | 
    
         
            -
             
     | 
| 
      
 46 
     | 
    
         
            +
                # Execute the block once for each host.
         
     | 
| 
      
 47 
     | 
    
         
            +
                # Each block executes in a copy of the context.
         
     | 
| 
      
 48 
     | 
    
         
            +
                def map (hosts = @hosts, &block)
         
     | 
| 
      
 49 
     | 
    
         
            +
                  Kernel.fail "No hosts left" if hosts.empty?
         
     | 
| 
      
 50 
     | 
    
         
            +
                  hosts.map do |host|
         
     | 
| 
      
 51 
     | 
    
         
            +
                    Thread.new do
         
     | 
| 
      
 52 
     | 
    
         
            +
                      block.(host)
         
     | 
| 
      
 53 
     | 
    
         
            +
                    end
         
     | 
| 
      
 54 
     | 
    
         
            +
                  end.map(&:join)
         
     | 
| 
       28 
55 
     | 
    
         
             
                end
         
     | 
| 
       29 
56 
     | 
    
         | 
| 
       30 
     | 
    
         
            -
                 
     | 
| 
       31 
     | 
    
         
            -
             
     | 
| 
       32 
     | 
    
         
            -
             
     | 
| 
       33 
     | 
    
         
            -
             
     | 
| 
       34 
     | 
    
         
            -
                     
     | 
| 
      
 57 
     | 
    
         
            +
                # Reboot each host and waits for them to come back up.
         
     | 
| 
      
 58 
     | 
    
         
            +
                # @param command The reboot command. A string.
         
     | 
| 
      
 59 
     | 
    
         
            +
                def reboot (command = "reboot")
         
     | 
| 
      
 60 
     | 
    
         
            +
                  each do
         
     | 
| 
      
 61 
     | 
    
         
            +
                    begin
         
     | 
| 
      
 62 
     | 
    
         
            +
                      sh command
         
     | 
| 
      
 63 
     | 
    
         
            +
                    rescue IOError
         
     | 
| 
      
 64 
     | 
    
         
            +
                    end
         
     | 
| 
      
 65 
     | 
    
         
            +
                    log.debug "Waiting for server to go away..."
         
     | 
| 
      
 66 
     | 
    
         
            +
                    sleep 0.1 while ping(true)
         
     | 
| 
      
 67 
     | 
    
         
            +
                    log.debug "Waiting for server to come back..."
         
     | 
| 
      
 68 
     | 
    
         
            +
                    sleep 1.0 until ping(true)
         
     | 
| 
       35 
69 
     | 
    
         
             
                  end
         
     | 
| 
       36 
70 
     | 
    
         
             
                end
         
     | 
| 
       37 
71 
     | 
    
         | 
| 
       38 
     | 
    
         
            -
                 
     | 
| 
       39 
     | 
    
         
            -
             
     | 
| 
       40 
     | 
    
         
            -
             
     | 
| 
       41 
     | 
    
         
            -
                   
     | 
| 
       42 
     | 
    
         
            -
                     
     | 
| 
       43 
     | 
    
         
            -
                     
     | 
| 
      
 72 
     | 
    
         
            +
                # Execute a shell command on each host.
         
     | 
| 
      
 73 
     | 
    
         
            +
                def sh (command, quiet = false)
         
     | 
| 
      
 74 
     | 
    
         
            +
                  log.info "sh: #{command}" unless quiet
         
     | 
| 
      
 75 
     | 
    
         
            +
                  map do |host|
         
     | 
| 
      
 76 
     | 
    
         
            +
                    status = host.sh(command)
         
     | 
| 
      
 77 
     | 
    
         
            +
                    fail host, "#{command}: exit status #{status}" if status != 0
         
     | 
| 
       44 
78 
     | 
    
         
             
                  end
         
     | 
| 
       45 
79 
     | 
    
         
             
                end
         
     | 
| 
       46 
80 
     | 
    
         | 
| 
       47 
     | 
    
         
            -
                 
     | 
| 
       48 
     | 
    
         
            -
             
     | 
| 
       49 
     | 
    
         
            -
             
     | 
| 
      
 81 
     | 
    
         
            +
                # Execute a command on the remote host.
         
     | 
| 
      
 82 
     | 
    
         
            +
                # @return false if the command exits with a non-zero status
         
     | 
| 
      
 83 
     | 
    
         
            +
                def sh? (command, quiet = false)
         
     | 
| 
      
 84 
     | 
    
         
            +
                  log.info "sh?: #{command}" unless quiet
         
     | 
| 
      
 85 
     | 
    
         
            +
                  win = true
         
     | 
| 
      
 86 
     | 
    
         
            +
                  map do |host|
         
     | 
| 
      
 87 
     | 
    
         
            +
                    status = host.sh(command)
         
     | 
| 
      
 88 
     | 
    
         
            +
                    win = false if status != 0
         
     | 
| 
      
 89 
     | 
    
         
            +
                  end
         
     | 
| 
      
 90 
     | 
    
         
            +
                  win
         
     | 
| 
       50 
91 
     | 
    
         
             
                end
         
     | 
| 
       51 
92 
     | 
    
         | 
| 
      
 93 
     | 
    
         
            +
                # Execute a command on the remote host.
         
     | 
| 
      
 94 
     | 
    
         
            +
                # @return false if the command exits with a non-zero status
         
     | 
| 
      
 95 
     | 
    
         
            +
                def ping (quiet = false)
         
     | 
| 
      
 96 
     | 
    
         
            +
                  log.info "ping" unless quiet
         
     | 
| 
      
 97 
     | 
    
         
            +
                  win = true
         
     | 
| 
      
 98 
     | 
    
         
            +
                  map do |host|
         
     | 
| 
      
 99 
     | 
    
         
            +
                    win &&= host.ping
         
     | 
| 
      
 100 
     | 
    
         
            +
                  end
         
     | 
| 
      
 101 
     | 
    
         
            +
                  win
         
     | 
| 
      
 102 
     | 
    
         
            +
                end
         
     | 
| 
      
 103 
     | 
    
         
            +
             
     | 
| 
      
 104 
     | 
    
         
            +
                def fail (host, message)
         
     | 
| 
      
 105 
     | 
    
         
            +
                  @hosts -= [host]
         
     | 
| 
      
 106 
     | 
    
         
            +
                end
         
     | 
| 
      
 107 
     | 
    
         
            +
             
     | 
| 
      
 108 
     | 
    
         
            +
                # Copy a file or readable to the host filesystem.
         
     | 
| 
      
 109 
     | 
    
         
            +
                # @param from An object that responds to read, or a string which names a file, or an array of either.
         
     | 
| 
      
 110 
     | 
    
         
            +
                # @param to A string.
         
     | 
| 
       52 
111 
     | 
    
         
             
                def cp (from, to)
         
     | 
| 
       53 
     | 
    
         
            -
                  log " 
     | 
| 
       54 
     | 
    
         
            -
                   
     | 
| 
      
 112 
     | 
    
         
            +
                  log.info "cp: #{from} -> #{to}"
         
     | 
| 
      
 113 
     | 
    
         
            +
                  map do |host|
         
     | 
| 
      
 114 
     | 
    
         
            +
                    host.cp(from, to)
         
     | 
| 
      
 115 
     | 
    
         
            +
                  end
         
     | 
| 
       55 
116 
     | 
    
         
             
                end
         
     | 
| 
       56 
117 
     | 
    
         | 
| 
      
 118 
     | 
    
         
            +
                # Writes a string to a file on the host filesystem.
         
     | 
| 
      
 119 
     | 
    
         
            +
                # @param string The string to write.
         
     | 
| 
      
 120 
     | 
    
         
            +
                # @param to A string.
         
     | 
| 
       57 
121 
     | 
    
         
             
                def write (string, to)
         
     | 
| 
       58 
122 
     | 
    
         
             
                  log "upload data to #{to}", :debug
         
     | 
| 
       59 
123 
     | 
    
         
             
                  target.cp(StringIO.new(string), to)
         
     | 
| 
       60 
124 
     | 
    
         
             
                end
         
     | 
| 
       61 
125 
     | 
    
         | 
| 
       62 
     | 
    
         
            -
                 
     | 
| 
       63 
     | 
    
         
            -
             
     | 
| 
       64 
     | 
    
         
            -
                  target.sh(command)
         
     | 
| 
       65 
     | 
    
         
            -
                rescue Blower::Host::ExecuteError
         
     | 
| 
       66 
     | 
    
         
            -
                  false
         
     | 
| 
       67 
     | 
    
         
            -
                end
         
     | 
| 
       68 
     | 
    
         
            -
             
     | 
| 
      
 126 
     | 
    
         
            +
                # Capture the output a command on the remote host.
         
     | 
| 
      
 127 
     | 
    
         
            +
                # @return (String) The combined stdout and stderr of the command.
         
     | 
| 
       69 
128 
     | 
    
         
             
                def capture (command)
         
     | 
| 
       70 
129 
     | 
    
         
             
                  stdout = ""
         
     | 
| 
       71 
130 
     | 
    
         
             
                  target.sh(command, stdout)
         
     | 
| 
       72 
131 
     | 
    
         
             
                  stdout
         
     | 
| 
       73 
132 
     | 
    
         
             
                end
         
     | 
| 
       74 
133 
     | 
    
         | 
| 
       75 
     | 
    
         
            -
                 
     | 
| 
      
 134 
     | 
    
         
            +
                # Run a task.
         
     | 
| 
      
 135 
     | 
    
         
            +
                # @param task (String) The name of the task
         
     | 
| 
      
 136 
     | 
    
         
            +
                def run (task, optional: false)
         
     | 
| 
       76 
137 
     | 
    
         
             
                  files = []
         
     | 
| 
       77 
138 
     | 
    
         
             
                  @path.each do |dir|
         
     | 
| 
       78 
139 
     | 
    
         
             
                    name = File.join(dir, task)
         
     | 
| 
         @@ -88,16 +149,22 @@ module Blower 
     | 
|
| 
       88 
149 
     | 
    
         
             
                    break unless files.empty?
         
     | 
| 
       89 
150 
     | 
    
         
             
                  end
         
     | 
| 
       90 
151 
     | 
    
         
             
                  if files.empty?
         
     | 
| 
       91 
     | 
    
         
            -
                     
     | 
| 
      
 152 
     | 
    
         
            +
                    if optional
         
     | 
| 
      
 153 
     | 
    
         
            +
                      return
         
     | 
| 
      
 154 
     | 
    
         
            +
                    else
         
     | 
| 
      
 155 
     | 
    
         
            +
                      fail "can't find #{task}"
         
     | 
| 
      
 156 
     | 
    
         
            +
                    end
         
     | 
| 
       92 
157 
     | 
    
         
             
                  else
         
     | 
| 
       93 
     | 
    
         
            -
                     
     | 
| 
       94 
     | 
    
         
            -
                       
     | 
| 
       95 
     | 
    
         
            -
             
     | 
| 
       96 
     | 
    
         
            -
                         
     | 
| 
       97 
     | 
    
         
            -
             
     | 
| 
      
 158 
     | 
    
         
            +
                    log.info "Running #{task}" do
         
     | 
| 
      
 159 
     | 
    
         
            +
                      begin
         
     | 
| 
      
 160 
     | 
    
         
            +
                        old_task, @task = @task, task
         
     | 
| 
      
 161 
     | 
    
         
            +
                        files.each do |file|
         
     | 
| 
      
 162 
     | 
    
         
            +
                          @have_seen[file] = true
         
     | 
| 
      
 163 
     | 
    
         
            +
                          instance_eval(File.read(file), file)
         
     | 
| 
      
 164 
     | 
    
         
            +
                        end
         
     | 
| 
      
 165 
     | 
    
         
            +
                      ensure
         
     | 
| 
      
 166 
     | 
    
         
            +
                        @task = old_task
         
     | 
| 
       98 
167 
     | 
    
         
             
                      end
         
     | 
| 
       99 
     | 
    
         
            -
                    ensure
         
     | 
| 
       100 
     | 
    
         
            -
                      @task = old_task
         
     | 
| 
       101 
168 
     | 
    
         
             
                    end
         
     | 
| 
       102 
169 
     | 
    
         
             
                  end
         
     | 
| 
       103 
170 
     | 
    
         
             
                end
         
     | 
    
        data/lib/blower/host.rb
    CHANGED
    
    | 
         @@ -2,6 +2,7 @@ require 'net/ssh' 
     | 
|
| 
       2 
2 
     | 
    
         
             
            require 'net/scp'
         
     | 
| 
       3 
3 
     | 
    
         
             
            require 'monitor'
         
     | 
| 
       4 
4 
     | 
    
         
             
            require 'colorize'
         
     | 
| 
      
 5 
     | 
    
         
            +
            require 'timeout'
         
     | 
| 
       5 
6 
     | 
    
         | 
| 
       6 
7 
     | 
    
         
             
            module Blower
         
     | 
| 
       7 
8 
     | 
    
         | 
| 
         @@ -11,7 +12,6 @@ module Blower 
     | 
|
| 
       11 
12 
     | 
    
         | 
| 
       12 
13 
     | 
    
         
             
                attr_accessor :name
         
     | 
| 
       13 
14 
     | 
    
         
             
                attr_accessor :user
         
     | 
| 
       14 
     | 
    
         
            -
                attr_accessor :data
         
     | 
| 
       15 
15 
     | 
    
         | 
| 
       16 
16 
     | 
    
         
             
                def_delegators :data, :[], :[]=
         
     | 
| 
       17 
17 
     | 
    
         | 
| 
         @@ -29,10 +29,6 @@ module Blower 
     | 
|
| 
       29 
29 
     | 
    
         
             
                  super()
         
     | 
| 
       30 
30 
     | 
    
         
             
                end
         
     | 
| 
       31 
31 
     | 
    
         | 
| 
       32 
     | 
    
         
            -
                def log
         
     | 
| 
       33 
     | 
    
         
            -
                  Logger.instance
         
     | 
| 
       34 
     | 
    
         
            -
                end
         
     | 
| 
       35 
     | 
    
         
            -
             
     | 
| 
       36 
32 
     | 
    
         
             
                def ping
         
     | 
| 
       37 
33 
     | 
    
         
             
                  Timeout.timeout(1) do
         
     | 
| 
       38 
34 
     | 
    
         
             
                    TCPSocket.new(name, 22).close
         
     | 
| 
         @@ -48,8 +44,10 @@ module Blower 
     | 
|
| 
       48 
44 
     | 
    
         
             
                  synchronize do
         
     | 
| 
       49 
45 
     | 
    
         
             
                    if from.is_a?(String) || from.is_a?(Array)
         
     | 
| 
       50 
46 
     | 
    
         
             
                      to += "/" if to[-1] != "/" && from.is_a?(Array)
         
     | 
| 
       51 
     | 
    
         
            -
                       
     | 
| 
       52 
     | 
    
         
            -
             
     | 
| 
      
 47 
     | 
    
         
            +
                      command = ["rsync", "-e", "ssh -oStrictHostKeyChecking=no", "-zz", "-r", "--progress", *from,
         
     | 
| 
      
 48 
     | 
    
         
            +
                                 "#{@user}@#{@name}:#{to}"]
         
     | 
| 
      
 49 
     | 
    
         
            +
                      log.trace command.shelljoin
         
     | 
| 
      
 50 
     | 
    
         
            +
                      IO.popen(command, in: :close, err: %i(child out)) do |io|
         
     | 
| 
       53 
51 
     | 
    
         
             
                        until io.eof?
         
     | 
| 
       54 
52 
     | 
    
         
             
                          begin
         
     | 
| 
       55 
53 
     | 
    
         
             
                            output << io.read_nonblock(100)
         
     | 
| 
         @@ -58,53 +56,74 @@ module Blower 
     | 
|
| 
       58 
56 
     | 
    
         
             
                            retry
         
     | 
| 
       59 
57 
     | 
    
         
             
                          end
         
     | 
| 
       60 
58 
     | 
    
         
             
                        end
         
     | 
| 
      
 59 
     | 
    
         
            +
                        io.close
         
     | 
| 
      
 60 
     | 
    
         
            +
                        if !$?.success?
         
     | 
| 
      
 61 
     | 
    
         
            +
                          log.fatal "exit status #{$?.exitstatus}: #{command}"
         
     | 
| 
      
 62 
     | 
    
         
            +
                          log.raw output
         
     | 
| 
      
 63 
     | 
    
         
            +
                        end
         
     | 
| 
       61 
64 
     | 
    
         
             
                      end
         
     | 
| 
       62 
     | 
    
         
            -
                    elsif from. 
     | 
| 
       63 
     | 
    
         
            -
                      log.info "string -> #{to}" unless quiet
         
     | 
| 
      
 65 
     | 
    
         
            +
                    elsif from.respond_to?(:read)
         
     | 
| 
       64 
66 
     | 
    
         
             
                      ssh.scp.upload!(from, to)
         
     | 
| 
       65 
67 
     | 
    
         
             
                    else
         
     | 
| 
       66 
68 
     | 
    
         
             
                      fail "Don't know how to copy a #{from.class}: #{from}"
         
     | 
| 
       67 
69 
     | 
    
         
             
                    end
         
     | 
| 
       68 
70 
     | 
    
         
             
                  end
         
     | 
| 
       69 
71 
     | 
    
         
             
                  true
         
     | 
| 
       70 
     | 
    
         
            -
                rescue => e
         
     | 
| 
       71 
     | 
    
         
            -
                  false
         
     | 
| 
       72 
72 
     | 
    
         
             
                end
         
     | 
| 
       73 
73 
     | 
    
         | 
| 
       74 
74 
     | 
    
         
             
                def sh (command, output = "")
         
     | 
| 
       75 
75 
     | 
    
         
             
                  synchronize do
         
     | 
| 
      
 76 
     | 
    
         
            +
                    log.debug command
         
     | 
| 
       76 
77 
     | 
    
         
             
                    result = nil
         
     | 
| 
       77 
78 
     | 
    
         
             
                    ch = ssh.open_channel do |ch|
         
     | 
| 
       78 
79 
     | 
    
         
             
                      ch.exec(command) do |_, success|
         
     | 
| 
       79 
80 
     | 
    
         
             
                        fail "failed to execute command" unless success
         
     | 
| 
       80 
81 
     | 
    
         
             
                        ch.on_data do |_, data|
         
     | 
| 
      
 82 
     | 
    
         
            +
                          log.trace "received #{data.bytesize} bytes stdout"
         
     | 
| 
       81 
83 
     | 
    
         
             
                          output << data
         
     | 
| 
       82 
84 
     | 
    
         
             
                        end
         
     | 
| 
       83 
85 
     | 
    
         
             
                        ch.on_extended_data do |_, _, data|
         
     | 
| 
      
 86 
     | 
    
         
            +
                          log.trace "received #{data.bytesize} bytes stderr"
         
     | 
| 
       84 
87 
     | 
    
         
             
                          output << data.colorize(:red)
         
     | 
| 
       85 
88 
     | 
    
         
             
                        end
         
     | 
| 
       86 
     | 
    
         
            -
                        ch.on_request("exit-status")  
     | 
| 
      
 89 
     | 
    
         
            +
                        ch.on_request("exit-status") do |_, data|
         
     | 
| 
      
 90 
     | 
    
         
            +
                          result = data.read_long
         
     | 
| 
      
 91 
     | 
    
         
            +
                          log.trace "received exit-status #{result}"
         
     | 
| 
      
 92 
     | 
    
         
            +
                        end
         
     | 
| 
       87 
93 
     | 
    
         
             
                      end
         
     | 
| 
       88 
94 
     | 
    
         
             
                    end
         
     | 
| 
       89 
95 
     | 
    
         
             
                    ch.wait
         
     | 
| 
       90 
96 
     | 
    
         
             
                    if result != 0
         
     | 
| 
       91 
     | 
    
         
            -
                      log.fatal " 
     | 
| 
      
 97 
     | 
    
         
            +
                      log.fatal "exit status #{result}: #{command}"
         
     | 
| 
       92 
98 
     | 
    
         
             
                      log.raw output
         
     | 
| 
       93 
     | 
    
         
            -
                      exit 1
         
     | 
| 
       94 
99 
     | 
    
         
             
                    end
         
     | 
| 
       95 
100 
     | 
    
         
             
                    result
         
     | 
| 
       96 
101 
     | 
    
         
             
                  end
         
     | 
| 
       97 
102 
     | 
    
         
             
                end
         
     | 
| 
       98 
103 
     | 
    
         | 
| 
      
 104 
     | 
    
         
            +
                # Execute the block with self as a parameter.
         
     | 
| 
      
 105 
     | 
    
         
            +
                # Exists to confirm with the HostGroup interface.
         
     | 
| 
       99 
106 
     | 
    
         
             
                def each (&block)
         
     | 
| 
       100 
107 
     | 
    
         
             
                  block.(self)
         
     | 
| 
       101 
108 
     | 
    
         
             
                end
         
     | 
| 
       102 
109 
     | 
    
         | 
| 
       103 
110 
     | 
    
         
             
                private
         
     | 
| 
       104 
111 
     | 
    
         | 
| 
      
 112 
     | 
    
         
            +
                attr_accessor :data
         
     | 
| 
      
 113 
     | 
    
         
            +
             
     | 
| 
      
 114 
     | 
    
         
            +
                def log
         
     | 
| 
      
 115 
     | 
    
         
            +
                  Logger.instance.with_prefix("(on #{name})")
         
     | 
| 
      
 116 
     | 
    
         
            +
                end
         
     | 
| 
      
 117 
     | 
    
         
            +
             
     | 
| 
       105 
118 
     | 
    
         
             
                def ssh
         
     | 
| 
       106 
     | 
    
         
            -
                   
     | 
| 
       107 
     | 
    
         
            -
             
     | 
| 
      
 119 
     | 
    
         
            +
                  if @ssh && @ssh.closed?
         
     | 
| 
      
 120 
     | 
    
         
            +
                    log.trace "Discovered the connection to ssh:#{name}@#{user} was lost"
         
     | 
| 
      
 121 
     | 
    
         
            +
                    @ssh = nil
         
     | 
| 
      
 122 
     | 
    
         
            +
                  end
         
     | 
| 
      
 123 
     | 
    
         
            +
                  @ssh ||= begin
         
     | 
| 
      
 124 
     | 
    
         
            +
                    log.trace "Connecting to ssh:#{name}@#{user}"
         
     | 
| 
      
 125 
     | 
    
         
            +
                    Net::SSH.start(name, user)
         
     | 
| 
      
 126 
     | 
    
         
            +
                  end
         
     | 
| 
       108 
127 
     | 
    
         
             
                end
         
     | 
| 
       109 
128 
     | 
    
         | 
| 
       110 
129 
     | 
    
         
             
              end
         
     | 
    
        data/lib/blower/logger.rb
    CHANGED
    
    | 
         @@ -2,37 +2,75 @@ require "singleton" 
     | 
|
| 
       2 
2 
     | 
    
         | 
| 
       3 
3 
     | 
    
         
             
            module Blower
         
     | 
| 
       4 
4 
     | 
    
         | 
| 
      
 5 
     | 
    
         
            +
              # Colorized logger.
         
     | 
| 
      
 6 
     | 
    
         
            +
              #
         
     | 
| 
      
 7 
     | 
    
         
            +
              # Prints messages to STDOUT, colorizing them according to the specified log level.
         
     | 
| 
      
 8 
     | 
    
         
            +
              #
         
     | 
| 
      
 9 
     | 
    
         
            +
              # The logging methods accept an optional block. Inside the block, log messages will
         
     | 
| 
      
 10 
     | 
    
         
            +
              # be indented by two spaces. This works recursively.
         
     | 
| 
       5 
11 
     | 
    
         
             
              class Logger
         
     | 
| 
       6 
12 
     | 
    
         
             
                include MonitorMixin
         
     | 
| 
       7 
13 
     | 
    
         
             
                include Singleton
         
     | 
| 
       8 
14 
     | 
    
         | 
| 
       9 
15 
     | 
    
         
             
                COLORS = {
         
     | 
| 
       10 
     | 
    
         
            -
                  trace: :light_black,
         
     | 
| 
       11 
     | 
    
         
            -
                  debug: : 
     | 
| 
       12 
     | 
    
         
            -
                  info: :blue,
         
     | 
| 
       13 
     | 
    
         
            -
                  warn: :yellow,
         
     | 
| 
       14 
     | 
    
         
            -
                  error: :red,
         
     | 
| 
       15 
     | 
    
         
            -
                  fatal: : 
     | 
| 
      
 16 
     | 
    
         
            +
                  trace: {color: :light_black},
         
     | 
| 
      
 17 
     | 
    
         
            +
                  debug: {color: :default},
         
     | 
| 
      
 18 
     | 
    
         
            +
                  info:  {color: :blue},
         
     | 
| 
      
 19 
     | 
    
         
            +
                  warn:  {color: :yellow},
         
     | 
| 
      
 20 
     | 
    
         
            +
                  error: {color: :red},
         
     | 
| 
      
 21 
     | 
    
         
            +
                  fatal: {color: :light_white, background: :red},
         
     | 
| 
       16 
22 
     | 
    
         
             
                }
         
     | 
| 
       17 
23 
     | 
    
         | 
| 
       18 
     | 
    
         
            -
                 
     | 
| 
      
 24 
     | 
    
         
            +
                RANKS = {
         
     | 
| 
      
 25 
     | 
    
         
            +
                  all: 100,
         
     | 
| 
      
 26 
     | 
    
         
            +
                  trace: 60,
         
     | 
| 
      
 27 
     | 
    
         
            +
                  debug: 50,
         
     | 
| 
      
 28 
     | 
    
         
            +
                  info:  40,
         
     | 
| 
      
 29 
     | 
    
         
            +
                  warn:  30,
         
     | 
| 
      
 30 
     | 
    
         
            +
                  error: 20,
         
     | 
| 
      
 31 
     | 
    
         
            +
                  fatal: 10,
         
     | 
| 
      
 32 
     | 
    
         
            +
                  off: 0,
         
     | 
| 
      
 33 
     | 
    
         
            +
                }
         
     | 
| 
      
 34 
     | 
    
         
            +
             
     | 
| 
      
 35 
     | 
    
         
            +
                def initialize (prefix = nil)
         
     | 
| 
       19 
36 
     | 
    
         
             
                  @indent = 0
         
     | 
| 
      
 37 
     | 
    
         
            +
                  @prefix = prefix
         
     | 
| 
       20 
38 
     | 
    
         
             
                  super()
         
     | 
| 
       21 
39 
     | 
    
         
             
                end
         
     | 
| 
       22 
40 
     | 
    
         | 
| 
      
 41 
     | 
    
         
            +
                def with_prefix (string)
         
     | 
| 
      
 42 
     | 
    
         
            +
                  self.class.send(:new, "#{@prefix}#{string}")
         
     | 
| 
      
 43 
     | 
    
         
            +
                end
         
     | 
| 
      
 44 
     | 
    
         
            +
             
     | 
| 
      
 45 
     | 
    
         
            +
                # Log a trace level event
         
     | 
| 
       23 
46 
     | 
    
         
             
                def trace (a=nil, *b, &c); log(a, :trace, *b, &c); end
         
     | 
| 
      
 47 
     | 
    
         
            +
             
     | 
| 
      
 48 
     | 
    
         
            +
                # Log a debug level event
         
     | 
| 
       24 
49 
     | 
    
         
             
                def debug (a=nil, *b, &c); log(a, :debug, *b, &c); end
         
     | 
| 
      
 50 
     | 
    
         
            +
             
     | 
| 
      
 51 
     | 
    
         
            +
                # Log a info level event
         
     | 
| 
       25 
52 
     | 
    
         
             
                def info (a=nil, *b, &c); log(a, :info, *b, &c); end
         
     | 
| 
      
 53 
     | 
    
         
            +
             
     | 
| 
      
 54 
     | 
    
         
            +
                # Log a warn level event
         
     | 
| 
       26 
55 
     | 
    
         
             
                def warn (a=nil, *b, &c); log(a, :warn, *b, &c); end
         
     | 
| 
      
 56 
     | 
    
         
            +
             
     | 
| 
      
 57 
     | 
    
         
            +
                # Log a error level event
         
     | 
| 
       27 
58 
     | 
    
         
             
                def error (a=nil, *b, &c); log(a, :error, *b, &c); end
         
     | 
| 
      
 59 
     | 
    
         
            +
             
     | 
| 
      
 60 
     | 
    
         
            +
                # Log a fatal level event
         
     | 
| 
       28 
61 
     | 
    
         
             
                def fatal (a=nil, *b, &c); log(a, :fatal, *b, &c); end
         
     | 
| 
      
 62 
     | 
    
         
            +
             
     | 
| 
      
 63 
     | 
    
         
            +
                # Log a level-less event
         
     | 
| 
      
 64 
     | 
    
         
            +
                # @deprecated
         
     | 
| 
       29 
65 
     | 
    
         
             
                def raw (a=nil, *b, &c); log(a, nil, *b, &c); end
         
     | 
| 
       30 
66 
     | 
    
         | 
| 
      
 67 
     | 
    
         
            +
                private
         
     | 
| 
      
 68 
     | 
    
         
            +
             
     | 
| 
       31 
69 
     | 
    
         
             
                def log (message = nil, level = :info, &block)
         
     | 
| 
       32 
     | 
    
         
            -
                  if message
         
     | 
| 
       33 
     | 
    
         
            -
                    synchronize do
         
     | 
| 
      
 70 
     | 
    
         
            +
                  if message && (level.nil? || RANKS[level] <= RANKS[$LOGLEVEL])
         
     | 
| 
      
 71 
     | 
    
         
            +
                    Logger.instance.synchronize do
         
     | 
| 
       34 
72 
     | 
    
         
             
                      message = message.colorize(COLORS[level]) if level
         
     | 
| 
       35 
     | 
    
         
            -
                      puts "  " * @indent + message
         
     | 
| 
      
 73 
     | 
    
         
            +
                      puts "  " * @indent + (@prefix ? @prefix + " " : "") + message
         
     | 
| 
       36 
74 
     | 
    
         
             
                    end
         
     | 
| 
       37 
75 
     | 
    
         
             
                  end
         
     | 
| 
       38 
76 
     | 
    
         
             
                  begin
         
     | 
| 
         @@ -45,8 +83,4 @@ module Blower 
     | 
|
| 
       45 
83 
     | 
    
         | 
| 
       46 
84 
     | 
    
         
             
              end
         
     | 
| 
       47 
85 
     | 
    
         | 
| 
       48 
     | 
    
         
            -
              def self.log (*args, &block)
         
     | 
| 
       49 
     | 
    
         
            -
                Logger.instance.log(*args, &block)
         
     | 
| 
       50 
     | 
    
         
            -
              end
         
     | 
| 
       51 
     | 
    
         
            -
             
     | 
| 
       52 
86 
     | 
    
         
             
            end
         
     | 
    
        data/lib/blower/version.rb
    CHANGED
    
    
    
        data/lib/blower.rb
    CHANGED
    
    
    
        metadata
    CHANGED
    
    | 
         @@ -1,14 +1,14 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            --- !ruby/object:Gem::Specification
         
     | 
| 
       2 
2 
     | 
    
         
             
            name: blower
         
     | 
| 
       3 
3 
     | 
    
         
             
            version: !ruby/object:Gem::Version
         
     | 
| 
       4 
     | 
    
         
            -
              version: 2. 
     | 
| 
      
 4 
     | 
    
         
            +
              version: 2.1.2
         
     | 
| 
       5 
5 
     | 
    
         
             
            platform: ruby
         
     | 
| 
       6 
6 
     | 
    
         
             
            authors:
         
     | 
| 
       7 
7 
     | 
    
         
             
            - Nathan Baum
         
     | 
| 
       8 
8 
     | 
    
         
             
            autorequire: 
         
     | 
| 
       9 
9 
     | 
    
         
             
            bindir: bin
         
     | 
| 
       10 
10 
     | 
    
         
             
            cert_chain: []
         
     | 
| 
       11 
     | 
    
         
            -
            date: 2015- 
     | 
| 
      
 11 
     | 
    
         
            +
            date: 2015-12-17 00:00:00.000000000 Z
         
     | 
| 
       12 
12 
     | 
    
         
             
            dependencies:
         
     | 
| 
       13 
13 
     | 
    
         
             
            - !ruby/object:Gem::Dependency
         
     | 
| 
       14 
14 
     | 
    
         
             
              name: net-ssh
         
     | 
| 
         @@ -52,6 +52,20 @@ dependencies: 
     | 
|
| 
       52 
52 
     | 
    
         
             
                - - "~>"
         
     | 
| 
       53 
53 
     | 
    
         
             
                  - !ruby/object:Gem::Version
         
     | 
| 
       54 
54 
     | 
    
         
             
                    version: '0.7'
         
     | 
| 
      
 55 
     | 
    
         
            +
            - !ruby/object:Gem::Dependency
         
     | 
| 
      
 56 
     | 
    
         
            +
              name: yard
         
     | 
| 
      
 57 
     | 
    
         
            +
              requirement: !ruby/object:Gem::Requirement
         
     | 
| 
      
 58 
     | 
    
         
            +
                requirements:
         
     | 
| 
      
 59 
     | 
    
         
            +
                - - ">="
         
     | 
| 
      
 60 
     | 
    
         
            +
                  - !ruby/object:Gem::Version
         
     | 
| 
      
 61 
     | 
    
         
            +
                    version: '0'
         
     | 
| 
      
 62 
     | 
    
         
            +
              type: :development
         
     | 
| 
      
 63 
     | 
    
         
            +
              prerelease: false
         
     | 
| 
      
 64 
     | 
    
         
            +
              version_requirements: !ruby/object:Gem::Requirement
         
     | 
| 
      
 65 
     | 
    
         
            +
                requirements:
         
     | 
| 
      
 66 
     | 
    
         
            +
                - - ">="
         
     | 
| 
      
 67 
     | 
    
         
            +
                  - !ruby/object:Gem::Version
         
     | 
| 
      
 68 
     | 
    
         
            +
                    version: '0'
         
     | 
| 
       55 
69 
     | 
    
         
             
            description: Really simple server orchestration
         
     | 
| 
       56 
70 
     | 
    
         
             
            email: n@p12a.org.uk
         
     | 
| 
       57 
71 
     | 
    
         
             
            executables:
         
     | 
| 
         @@ -63,7 +77,6 @@ files: 
     | 
|
| 
       63 
77 
     | 
    
         
             
            - lib/blower.rb
         
     | 
| 
       64 
78 
     | 
    
         
             
            - lib/blower/context.rb
         
     | 
| 
       65 
79 
     | 
    
         
             
            - lib/blower/host.rb
         
     | 
| 
       66 
     | 
    
         
            -
            - lib/blower/host_group.rb
         
     | 
| 
       67 
80 
     | 
    
         
             
            - lib/blower/logger.rb
         
     | 
| 
       68 
81 
     | 
    
         
             
            - lib/blower/mock_host.rb
         
     | 
| 
       69 
82 
     | 
    
         
             
            - lib/blower/version.rb
         
     | 
| 
         @@ -92,3 +105,4 @@ signing_key: 
     | 
|
| 
       92 
105 
     | 
    
         
             
            specification_version: 4
         
     | 
| 
       93 
106 
     | 
    
         
             
            summary: Really simple server orchestration
         
     | 
| 
       94 
107 
     | 
    
         
             
            test_files: []
         
     | 
| 
      
 108 
     | 
    
         
            +
            has_rdoc: 
         
     | 
    
        data/lib/blower/host_group.rb
    DELETED
    
    | 
         @@ -1,36 +0,0 @@ 
     | 
|
| 
       1 
     | 
    
         
            -
            module Blower
         
     | 
| 
       2 
     | 
    
         
            -
             
     | 
| 
       3 
     | 
    
         
            -
              class HostGroup
         
     | 
| 
       4 
     | 
    
         
            -
             
     | 
| 
       5 
     | 
    
         
            -
                attr_accessor :hosts
         
     | 
| 
       6 
     | 
    
         
            -
                attr_accessor :root
         
     | 
| 
       7 
     | 
    
         
            -
                attr_accessor :location
         
     | 
| 
       8 
     | 
    
         
            -
             
     | 
| 
       9 
     | 
    
         
            -
                def initialize (hosts)
         
     | 
| 
       10 
     | 
    
         
            -
                  @hosts = hosts
         
     | 
| 
       11 
     | 
    
         
            -
                end
         
     | 
| 
       12 
     | 
    
         
            -
             
     | 
| 
       13 
     | 
    
         
            -
                def sh (command = nil, *args, &block)
         
     | 
| 
       14 
     | 
    
         
            -
                  each do |host|
         
     | 
| 
       15 
     | 
    
         
            -
                    command = block.() if block
         
     | 
| 
       16 
     | 
    
         
            -
                    host.sh(command)
         
     | 
| 
       17 
     | 
    
         
            -
                  end
         
     | 
| 
       18 
     | 
    
         
            -
                end
         
     | 
| 
       19 
     | 
    
         
            -
             
     | 
| 
       20 
     | 
    
         
            -
                def cp (from, to)
         
     | 
| 
       21 
     | 
    
         
            -
                  each do |host|
         
     | 
| 
       22 
     | 
    
         
            -
                    host.cp(from, to)
         
     | 
| 
       23 
     | 
    
         
            -
                  end
         
     | 
| 
       24 
     | 
    
         
            -
                end
         
     | 
| 
       25 
     | 
    
         
            -
             
     | 
| 
       26 
     | 
    
         
            -
                def each (&block)
         
     | 
| 
       27 
     | 
    
         
            -
                  hosts.map do |host|
         
     | 
| 
       28 
     | 
    
         
            -
                    Thread.new do
         
     | 
| 
       29 
     | 
    
         
            -
                      block.(host)
         
     | 
| 
       30 
     | 
    
         
            -
                    end
         
     | 
| 
       31 
     | 
    
         
            -
                  end.map(&:join)
         
     | 
| 
       32 
     | 
    
         
            -
                end
         
     | 
| 
       33 
     | 
    
         
            -
             
     | 
| 
       34 
     | 
    
         
            -
              end
         
     | 
| 
       35 
     | 
    
         
            -
             
     | 
| 
       36 
     | 
    
         
            -
            end
         
     |