Class: Blower::Context

Inherits:
Object show all
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

Instance Method Summary collapse

Constructor Details

#initialize(path) ⇒ Context

Create a new Context.

Parameters:

  • path (Array)

    The search path for tasks.



43
44
45
46
47
# File 'lib/blower/context.rb', line 43

def initialize (path)
  @path = path
  @data = {}
  @hosts = []
end

Instance Attribute Details

#fileObject

The file name of the currently running task. Context#cp interprets relative file names relative to this file name’s directory component.



39
40
41
# File 'lib/blower/context.rb', line 39

def file
  @file
end

#hostsObject

The target hosts.



32
33
34
# File 'lib/blower/context.rb', line 32

def hosts
  @hosts
end

#pathObject

Search path for tasks.



29
30
31
# File 'lib/blower/context.rb', line 29

def path
  @path
end

#userObject

Username override. If not-nil, this user is used for all remote accesses.



35
36
37
# File 'lib/blower/context.rb', line 35

def user
  @user
end

Instance Method Details

#as(user, quiet: false) ⇒ Object

Yield with a temporary username override

Parameters:

  • quiet (Boolean) (defaults to: false)

    Whether to suppress log messages.



99
100
101
102
103
104
105
# File 'lib/blower/context.rb', line 99

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.

Overloads:

  • #cp(readable, to, as: user, on: hosts, quiet: false) ⇒ Object

    Parameters:

    • from (#read)

      An object from which to read the contents of the new file.

    • to (String)

      The file name to write the string to.

    • on (Array) (defaults to: hosts)

      The hosts to operate on. Defaults to the context’s current host list.

    • as (#to_s) (defaults to: user)

      The remote user to operate as. Defaults to the context’s current user if that is not nil, and the host’s configured user otherwise.

    • quiet (Boolean) (defaults to: false)

      Whether to suppress log messages.

    • once (String)

      If non-nil, only perform the operation if it hasn’t been done before as per #once.

  • #cp(filename, to, as: user, on: hosts, quiet: false) ⇒ Object

    Parameters:

    • from (String)

      The name of the local file to copy.

    • to (String)

      The file name to write the string to.

    • on (Array) (defaults to: hosts)

      The hosts to operate on. Defaults to the context’s current host list.

    • as (#to_s) (defaults to: user)

      The remote user to operate as. Defaults to the context’s current user if that is not nil, and the host’s configured user otherwise.

    • quiet (Boolean) (defaults to: false)

      Whether to suppress log messages.

    • once (String)

      If non-nil, only perform the operation if it hasn’t been done before as per #once.



161
162
163
164
165
166
167
168
169
170
171
# File 'lib/blower/context.rb', line 161

def cp (from, to, as: user, on: hosts, quiet: false, once: nil)
  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
        end
      end
    end
  end
end

#get(name, default = nil) ⇒ Object

Return a context variable.

Parameters:

  • name

    The name of the variable.

  • default (defaults to: nil)

    The value to return if the variable is not set.



52
53
54
# File 'lib/blower/context.rb', line 52

def get (name, default = nil)
  @data.fetch(name, default)
end

#on(*hosts, quiet: false) ⇒ Object

Yield with a temporary host list

Parameters:

  • quiet (Boolean) (defaults to: false)

    Whether to suppress log messages.



89
90
91
92
93
94
95
# File 'lib/blower/context.rb', line 89

def on (*hosts, quiet: false)
  let :@hosts => hosts.flatten do
    log.info "on #{@hosts.map(&:name).join(", ")}", quiet: quiet do
      yield
    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.

Parameters:

  • key (String)

    Uniquely identifies the block.

  • store (String) (defaults to: "/var/cache/blower.json")

    File to store once‘s state in.

  • quiet (Boolean) (defaults to: false)

    Whether to suppress log messages.



246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
# File 'lib/blower/context.rb', line 246

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

Parameters:

  • on (Array) (defaults to: hosts)

    The hosts to operate on. Defaults to the context’s current host list.

  • quiet (Boolean) (defaults to: false)

    Whether to suppress log messages.



231
232
233
234
235
236
237
# File 'lib/blower/context.rb', line 231

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.

Parameters:

  • filename (String)

    The file to read.

  • on (Array) (defaults to: hosts)

    The hosts to operate on. Defaults to the context’s current host list.

  • as (#to_s) (defaults to: user)

    The remote user to operate as. Defaults to the context’s current user if that is not nil, and the host’s configured user otherwise.

  • quiet (Boolean) (defaults to: false)

    Whether to suppress log messages.

Returns:

  • (Hash)

    A hash of Host objects to Strings of the file contents.



179
180
181
182
183
184
185
# File 'lib/blower/context.rb', line 179

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.

Parameters:

  • from (String)

    The directory to search for .erb files.

  • to (String)

    The remote directory to put files in.

  • on (Array) (defaults to: hosts)

    The hosts to operate on. Defaults to the context’s current host list.

  • as (#to_s) (defaults to: user)

    The remote user to operate as. Defaults to the context’s current user if that is not nil, and the host’s configured user otherwise.

  • quiet (Boolean) (defaults to: false)

    Whether to suppress log messages.

  • once (String) (defaults to: nil)

    If non-nil, only perform the operation if it hasn’t been done before as per #once.



212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
# File 'lib/blower/context.rb', line 212

def render (from, to, as: user, on: hosts, quiet: false, once: nil)
  self.once once, quiet: quiet do
    Dir.chdir File.dirname(file) do
      (Dir["#{from}**/*.erb"] + Dir["#{from}**/.*.erb"]).each do |path|
        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
      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.

Parameters:

  • task (String)

    The name of the task.

  • quiet (Boolean) (defaults to: false)

    Whether to suppress log messages.

  • once (String) (defaults to: nil)

    If non-nil, only perform the operation if it hasn’t been done before as per #once.

Returns:

  • The result of evaluating the task file.

Raises:

  • (TaskNotFound)

    If no task is found.

  • Whatever the task itself raises.



116
117
118
119
120
121
122
123
124
125
126
127
128
129
# File 'lib/blower/context.rb', line 116

def run (task, optional: false, quiet: false, once: nil)
  once once, quiet: quiet do
    log.info "run #{task}", quiet: quiet do
      file = find_task(task)
      code = File.read(file)
      let :@file => file do
        instance_eval(code, file)
      end
    end
  end
rescue TaskNotFound => e
  return if optional
  raise e
end

#set(hash) ⇒ Object

Merge the hash into the context variables.

Parameters:

  • hash (Hash)

    The values to merge into the context variables.



66
67
68
# File 'lib/blower/context.rb', line 66

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

Parameters:

  • on (Array) (defaults to: hosts)

    The hosts to operate on. Defaults to the context’s current host list.

  • as (#to_s) (defaults to: user)

    The remote user to operate as. Defaults to the context’s current user if that is not nil, and the host’s configured user otherwise.

  • quiet (Boolean) (defaults to: false)

    Whether to suppress log messages.

  • once (String) (defaults to: nil)

    If non-nil, only perform the operation if it hasn’t been done before as per #once.



136
137
138
139
140
141
142
143
144
# File 'lib/blower/context.rb', line 136

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

#unset(*names) ⇒ Object

Remove context variables

Parameters:

  • names

    The names to remove from the variables.



58
59
60
61
62
# File 'lib/blower/context.rb', line 58

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.

Parameters:

  • hash (Hash)

    The values to merge into the context variables.

  • quiet (Boolean) (defaults to: false)

    Whether to suppress log messages.

Returns:

  • Whatever the yielded-to block returns.



75
76
77
78
79
80
81
82
83
84
85
# File 'lib/blower/context.rb', line 75

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.

Parameters:

  • string (String)

    The string to write.

  • to (String)

    The file name to write the string to.

  • on (Array) (defaults to: hosts)

    The hosts to operate on. Defaults to the context’s current host list.

  • as (#to_s) (defaults to: user)

    The remote user to operate as. Defaults to the context’s current user if that is not nil, and the host’s configured user otherwise.

  • quiet (Boolean) (defaults to: false)

    Whether to suppress log messages.

  • once (String) (defaults to: nil)

    If non-nil, only perform the operation if it hasn’t been done before as per #once.



194
195
196
197
198
199
200
201
202
# File 'lib/blower/context.rb', line 194

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