class Hubcap::Group
Constants
- IP_PATTERN
Attributes
Public Class Methods
Supply the parent group, the name of this new group and a block of code to evaluate in the context of this new group.
Every group must have a parent group, unless it is the top-most group: the hub. The hub must be a Hubcap::Hub
.
# File lib/hubcap/group.rb, line 14 def initialize(parent, name, &blk) @name = name.to_s @parent = parent unless @parent || kind_of?(Hubcap::Hub) raise(Hubcap::GroupWithoutParent, self.inspect) end @cap_attributes = {} @cap_roles = [] @puppet_roles = {} @params = {} @extra_attributes = {} @hosts = {} @children = [] instance_eval(&blk) if blk && processable? end
Public Instance Methods
Load a Ruby file and evaluate it in the context of this group. Like Ruby's require(), the '.rb' is optional in the path.
# File lib/hubcap/group.rb, line 34 def absorb(path) p = path p += '.rb' unless File.exists?(p) && !File.directory?(p) raise("File not found: #{path}") unless File.exists?(p) raise("#{path} is a directory") if File.directory?(p) code = IO.read(p) eval(code, binding, p) end
Instantiate an application as a child of this group.
# File lib/hubcap/group.rb, line 302 def application(name, options = {}, &blk) add_child(:applications, Hubcap::Application.new(self, name, options, &blk)) end
Sets an attribute in the Capistrano
server() definition for all Hubcap
servers to which it applies.
For eg, :primary => true or :no_release => true.
Either:
cap_attribute(:foo, 'bar')
or:
cap_attribute(:foo => 'bar')
and this works too:
cap_attribute(:foo => 'bar', :garply => 'grault')
# File lib/hubcap/group.rb, line 123 def cap_attribute(*args) if args.length == 2 cap_attribute(args.first => args.last) elsif args.length == 1 && args.first.kind_of?(Hash) @cap_attributes.update(args.first) else raise ArgumentError('Must be (key, value) or (hash).') end end
# File lib/hubcap/group.rb, line 235 def cap_attributes @parent ? @parent.cap_attributes.merge(@cap_attributes) : @cap_attributes end
# File lib/hubcap/group.rb, line 154 def cap_role(*args) inv = args.select { |a| !a.kind_of?(String) && !a.kind_of?(Symbol) } raise "Capistrano role must be string or symbol: #{inv}" if inv.any? @cap_roles += args end
# File lib/hubcap/group.rb, line 240 def cap_roles @parent ? @parent.cap_roles + @cap_roles : @cap_roles end
Sets a variable in the Capistrano
instance.
Note: when Hubcap
is in application mode (not executing a default task), an exception will be raised if a variable is set twice to two different values.
Either:
cap_set(:foo, 'bar')
or:
cap_set(:foo => 'bar')
and this works too:
cap_set(:foo => 'bar', :garply => 'grault')
in fact, even this works:
cap_set(:foo) { bar }
# File lib/hubcap/group.rb, line 96 def cap_set(*args, &blk) if args.length == 2 hub.cap_set(args.first => args.last) elsif args.length == 1 if block_given? hub.cap_set(args.first => blk) elsif args.first.kind_of?(Hash) hub.cap_set(args.first) end else raise ArgumentError('Must be (key, value) or (hash) or (key) { block }.') end end
Indicates whether we should store this group in the hub's array of groups/applications/servers. We only store groups at the end of the filter and below.
That is, this group's history should be the same length or longer than the filter, but identical at each point in the filter.
# File lib/hubcap/group.rb, line 76 def collectable? hub.filters.any? { |fr| history.size >= fr.size && matching_filter?(fr) } end
Adds extra (inheritable) attributes to the referencing scope These attributes are not used by hubcap or puppet, but can be used by by other tools using hubcap to parse the configuration tree.
extra({'some_key' => 'some_value'})
# File lib/hubcap/group.rb, line 204 def extra(hash) hash.each_key { |k| unless k.kind_of?(String) || k.kind_of?(Symbol) raise(Hubcap::InvalidParamKeyType, k.inspect) end } update_with_stringified_keys(@extra_attributes, hash) end
# File lib/hubcap/group.rb, line 260 def extras @parent ? @parent.extras.merge(@extra_attributes) : @extra_attributes end
Instantiate a group as a child of this group.
# File lib/hubcap/group.rb, line 316 def group(name, &blk) add_child(:groups, Hubcap::Group.new(self, name, &blk)) end
An array of names, from the oldest ancestor to the parent to self.
# File lib/hubcap/group.rb, line 53 def history @parent ? @parent.history + [@name] : [] end
Defines hostnames that point to IP addresses. Eg,
host('staging-1' => '192.168.1.20')
This is solely used as a look-up table for resolv(). If the hostname can't be found in this list, resolv() will fall back to a normal DNS look-up. It's a neat way to define your infrastructure with names, rather than hard-coding IPs, without needing a shared DNS server for everyone (and without having to wait for DNS to propagate). Think of it as a built-in /etc/hosts file.
You can define multiple hostname/ip pairs in a single call.
A group inherits a clone of its parent's hosts. So if you want to change the IP of a hostname just for a child-group, you can (but: caveat emptor).
# File lib/hubcap/group.rb, line 230 def host(hash) @hosts.update(hash) end
# File lib/hubcap/group.rb, line 255 def hosts @parent ? @parent.hosts.merge(@hosts) : @hosts end
Finds the top-level Hubcap::Hub
to which this group belongs.
# File lib/hubcap/group.rb, line 46 def hub @parent ? @parent.hub : self end
Dereferences the host name using the @hosts table. Follows references. Returns the first value that looks like an IP address OR that it can't find in the table. (So, if there's no match, you just get the name back.
# File lib/hubcap/group.rb, line 286 def lookup(hname, history = []) return hname if hname.match(IP_PATTERN) result = hosts[hname] if !result return hname elsif history.include?(result) raise(Hubcap::HostCircularReference, history.to_s) else history << hname lookup(result, history) end end
Adds values to a hash that is supplied to Puppet when it is provisioning the server.
If you do this…
params(:foo => 'bar')
…then Puppet will have a top-level variable called $foo, containing 'bar'.
Note that hash keys that are not strings or symbols will raise an error, and symbol keys will be converted to strings. ie, :foo becomes 'foo' in the above example.
# File lib/hubcap/group.rb, line 188 def param(hash) hash.each_key { |k| unless k.kind_of?(String) || k.kind_of?(Symbol) raise(Hubcap::InvalidParamKeyType, k.inspect) end } update_with_stringified_keys(@params, hash) end
# File lib/hubcap/group.rb, line 250 def params @parent ? @parent.params.merge(@params) : @params end
Indicates whether we should process this group. We process all groups that match any of the filters or are below the furthest point in the filter.
“Match the filter” means that this group's history and the filter are identical to the end of the shortest of the two arrays.
# File lib/hubcap/group.rb, line 64 def processable? hub.filters.any? { |fr| matching_filter?(fr) } end
# File lib/hubcap/group.rb, line 161 def puppet_role(*args) args.each { |a| if a.kind_of?(String) || a.kind_of?(Symbol) update_with_stringified_keys(@puppet_roles, { a => nil }) elsif a.kind_of?(Hash) inv = [] a.each_pair { |k, v| inv << v unless v.kind_of?(Hash) } raise "Puppet role parameters must be a hash: #{inv}" if inv.any? update_with_stringified_keys(@puppet_roles, a) else raise "Puppet role must be a string, symbol or hash: #{a}" end } end
# File lib/hubcap/group.rb, line 245 def puppet_roles @parent ? @parent.puppet_roles.merge(@puppet_roles) : @puppet_roles end
Takes a name and returns an IP address. It looks at the @hosts table first, then falls back to a normal DNS look-up.
# File lib/hubcap/group.rb, line 268 def resolv(*hnames) if hnames.size == 1 result = lookup(hnames.first) begin result.match(IP_PATTERN) ? result : Resolv.getaddress(result) rescue Resolv::ResolvError => e raise(Hubcap::ServerAddressUnknown, hnames.first) end else hnames.collect { |hname| resolv(hname) } end end
Sets the Capistrano
role and/or Puppet class for all Hubcap
servers to which it applies.
When declared multiple times (even in parents), it's additive.
Either:
role(:app)
or:
role(:app, :db)
or:
role(:cap => :app, :puppet => 'relishapp')
or:
role(:cap => [:app, :db], :puppet => 'relishapp')
# File lib/hubcap/group.rb, line 148 def role(*args) cap_role(*args) puppet_role(*args) end
Instantiate a server as a child of this group.
# File lib/hubcap/group.rb, line 309 def server(name, options = {}, &blk) add_child(:servers, Hubcap::Server.new(self, name, options, &blk)) end
Private Instance Methods
# File lib/hubcap/group.rb, line 323 def add_child(category, child) @children << child if child.processable? hub.send(category) << child if child.collectable? child end
# File lib/hubcap/group.rb, line 330 def matching_filter?(fr) s = [history.size, fr.size].min history.slice(0, s) == fr.slice(0, s) end
# File lib/hubcap/group.rb, line 336 def update_with_stringified_keys(dest, src) src.each_pair { |k, v| v = update_with_stringified_keys({}, v) if v.is_a?(Hash) v = v.to_s if v.kind_of?(Symbol) # ensure strings not symbols dest.update(k.to_s => v) } dest end