Class: Blower::Context
- Extended by:
- Forwardable
- Defined in:
- lib/blower/context.rb
Overview
Blower tasks are executed within a context.
The context can be used to share information between tasks by storing it in instance variables.
Defined Under Namespace
Classes: HostHash
Instance Attribute Summary collapse
-
#failures ⇒ Object
The failed hosts.
-
#file ⇒ Object
The file name of the currently running task.
-
#hosts ⇒ Object
The target hosts.
-
#path ⇒ Object
Search path for tasks.
-
#user ⇒ Object
Username override.
Instance Method Summary collapse
-
#as(user, quiet: false) ⇒ Object
Yield with a temporary username override.
-
#cp(from, to, as: user, on: hosts, quiet: false, once: nil, delete: false) ⇒ Object
Copy a file or readable to the host filesystems.
-
#get(name, default = nil) ⇒ Object
Return a context variable.
-
#initialize(path) ⇒ Context
constructor
Create a new Context.
-
#on(*hosts, quiet: false) ⇒ Object
Yield with a temporary host list.
-
#once(key, store: "/var/cache/blower.json", quiet: false) ⇒ Object
Execute a block only once per host.
-
#ping(on: hosts, quiet: false) ⇒ Object
Ping each host by trying to connect to port 22.
-
#read(filename, as: user, on: hosts, quiet: false) ⇒ Hash
Reads a remote file from each host.
-
#render(from, to, as: user, on: hosts, quiet: false, once: nil) ⇒ Object
Renders and installs files from ERB templates.
-
#run(task, optional: false, quiet: false, once: nil) ⇒ Object
Find and execute a task.
-
#set(hash) ⇒ Object
Merge the hash into the context variables.
-
#sh(command, as: user, on: hosts, quiet: false, once: nil) ⇒ Object
Execute a shell command on each host.
- #sh?(command, as: user, on: hosts, quiet: false, once: nil) ⇒ Boolean
-
#unset(*names) ⇒ Object
Remove context variables.
-
#with(hash, quiet: false) ⇒ Object
Yield with the hash temporary merged into the context variables.
-
#write(string, to, as: user, on: hosts, quiet: false, once: nil) ⇒ Object
Writes a string to a file on the host filesystems.
Constructor Details
#initialize(path) ⇒ Context
Create a new Context.
48 49 50 51 52 53 |
# File 'lib/blower/context.rb', line 48 def initialize (path) @path = path @data = {} @hosts = [] @failures = [] end |
Instance Attribute Details
#failures ⇒ Object
The failed hosts.
37 38 39 |
# File 'lib/blower/context.rb', line 37 def failures @failures end |
#file ⇒ Object
The file name of the currently running task. Context#cp interprets relative file names relative to this file name’s directory component.
44 45 46 |
# File 'lib/blower/context.rb', line 44 def file @file end |
#hosts ⇒ Object
The target hosts.
34 35 36 |
# File 'lib/blower/context.rb', line 34 def hosts @hosts end |
#path ⇒ Object
Search path for tasks.
31 32 33 |
# File 'lib/blower/context.rb', line 31 def path @path end |
#user ⇒ Object
Username override. If not-nil, this user is used for all remote accesses.
40 41 42 |
# File 'lib/blower/context.rb', line 40 def user @user end |
Instance Method Details
#as(user, quiet: false) ⇒ Object
Yield with a temporary username override
105 106 107 108 109 110 111 |
# File 'lib/blower/context.rb', line 105 def as (user, quiet: false) let :@user => user do log.info "as #{user}", quiet: quiet do yield end end end |
#cp(readable, to, as: user, on: hosts, quiet: false) ⇒ Object #cp(filename, to, as: user, on: hosts, quiet: false) ⇒ Object
Copy a file or readable to the host filesystems.
181 182 183 184 185 186 187 188 189 190 191 |
# File 'lib/blower/context.rb', line 181 def cp (from, to, as: user, on: hosts, quiet: false, once: nil, delete: false) self.once once, quiet: quiet do log.info "cp: #{from} -> #{to}", quiet: quiet do Dir.chdir File.dirname(file) do hash_map(hosts) do |host| host.cp from, to, as: as, quiet: quiet, delete: delete end end end end end |
#get(name, default = nil) ⇒ Object
Return a context variable.
58 59 60 |
# File 'lib/blower/context.rb', line 58 def get (name, default = nil) @data.fetch(name, default) end |
#on(*hosts, quiet: false) ⇒ Object
Yield with a temporary host list
95 96 97 98 99 100 101 |
# File 'lib/blower/context.rb', line 95 def on (*hosts, quiet: false) let :@hosts => hosts.flatten do log.info "on #{@hosts.map(&:name).join(", ")}", quiet: quiet do yield *hosts end end end |
#once(key, store: "/var/cache/blower.json", quiet: false) ⇒ Object
Execute a block only once per host. It is usually preferable to make tasks idempotent, but when that isn’t possible, once
will only execute the block on hosts where a block with the same key hasn’t previously been successfully executed.
278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 |
# File 'lib/blower/context.rb', line 278 def once (key, store: "/var/cache/blower.json", quiet: false) return yield unless key log.info "once: #{key}", quiet: quiet do hash_map(hosts) do |host| done = begin JSON.parse(host.read(store, quiet: true)) rescue => e {} end unless done[key] on [host] do yield end done[key] = true host.write(done.to_json, store, quiet: true) end end end end |
#ping(on: hosts, quiet: false) ⇒ Object
Ping each host by trying to connect to port 22
263 264 265 266 267 268 269 |
# File 'lib/blower/context.rb', line 263 def ping (on: hosts, quiet: false) log.info "ping", quiet: quiet do hash_map(hosts) do |host| host.ping end end end |
#read(filename, as: user, on: hosts, quiet: false) ⇒ Hash
Reads a remote file from each host.
199 200 201 202 203 204 205 |
# File 'lib/blower/context.rb', line 199 def read (filename, as: user, on: hosts, quiet: false) log.info "read: #{filename}", quiet: quiet do hash_map(hosts) do |host| host.read filename, as: as end end end |
#render(from, to, as: user, on: hosts, quiet: false, once: nil) ⇒ Object
Renders and installs files from ERB templates. Files are under from
in ERB format. from/foo/bar.conf.erb
is rendered and written to to/foo/bar.conf
. Non-ERB files are ignored.
232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 |
# File 'lib/blower/context.rb', line 232 def render (from, to, as: user, on: hosts, quiet: false, once: nil) self.once once, quiet: quiet do Dir.chdir File.dirname(file) do Find.find(from).each do |path| if File.directory?(path) to_path = to + path[from.length..-1] sh "mkdir -p #{to_path.shellescape}" elsif path =~ /\.erb$/ template = ERB.new(File.read(path)) to_path = to + path[from.length..-5] log.info "render: #{path} -> #{to_path}", quiet: quiet do hash_map(hosts) do |host| host.cp StringIO.new(template.result(binding)), to_path, as: as, quiet: quiet end end else to_path = to + path[from.length..-1] log.info "copy: #{path} -> #{to_path}", quiet: quiet do hash_map(hosts) do |host| host.cp File.open(path), to_path, as: as, quiet: quiet end end end end end end end |
#run(task, optional: false, quiet: false, once: nil) ⇒ Object
Find and execute a task. For each entry in the search path, it checks for path/task.rb
, path/task/blow.rb
, and finally for bare path/task
. The search stops at the first match. If found, the task is executed within the context, with @file bound to the task’s file name.
122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 |
# File 'lib/blower/context.rb', line 122 def run (task, optional: false, quiet: false, once: nil) @run_cache ||= {} once once, quiet: quiet do log.info "run #{task}", quiet: quiet do file = find_task(task) if @run_cache.has_key? file log.info "*cached*" @run_cache[file] else @run_cache[file] = begin code = File.read(file) let :@file => file do instance_eval(code, file) end end end end end rescue TaskNotFound => e return if optional raise e end |
#set(hash) ⇒ Object
Merge the hash into the context variables.
72 73 74 |
# File 'lib/blower/context.rb', line 72 def set (hash) @data.merge! hash end |
#sh(command, as: user, on: hosts, quiet: false, once: nil) ⇒ Object
Execute a shell command on each host
156 157 158 159 160 161 162 163 164 |
# File 'lib/blower/context.rb', line 156 def sh (command, as: user, on: hosts, quiet: false, once: nil) self.once once, quiet: quiet do log.info "sh: #{command}", quiet: quiet do hash_map(hosts) do |host| host.sh command, as: as, quiet: quiet end end end end |
#sh?(command, as: user, on: hosts, quiet: false, once: nil) ⇒ Boolean
145 146 147 148 149 |
# File 'lib/blower/context.rb', line 145 def sh? (command, as: user, on: hosts, quiet: false, once: nil) sh command, as: as, on: on, quiet: quiet, once: once rescue nil end |
#unset(*names) ⇒ Object
Remove context variables
64 65 66 67 68 |
# File 'lib/blower/context.rb', line 64 def unset (*names) names.each do |name| @data.delete name end end |
#with(hash, quiet: false) ⇒ Object
Yield with the hash temporary merged into the context variables. Only variables specifically named in hash
will be reset when the yield returns.
81 82 83 84 85 86 87 88 89 90 91 |
# File 'lib/blower/context.rb', line 81 def with (hash, quiet: false) old_values = data.values_at(hash.keys) log.debug "with #{hash}", quiet: quiet do set hash yield end ensure hash.keys.each.with_index do |key, i| @data[key] = old_values[i] end end |
#write(string, to, as: user, on: hosts, quiet: false, once: nil) ⇒ Object
Writes a string to a file on the host filesystems.
214 215 216 217 218 219 220 221 222 |
# File 'lib/blower/context.rb', line 214 def write (string, to, as: user, on: hosts, quiet: false, once: nil) self.once once, quiet: quiet do log.info "write: #{string.bytesize} bytes -> #{to}", quiet: quiet do hash_map(hosts) do |host| host.write string, to, as: as, quiet: quiet end end end end |