module MotherBrain module Bootstrap # Manifest for bootstrapping a collection of nodes as a specified node group # # @example valid manifest structure # { # "nodes": [ # { # "groups": ["activemq::master"], # "hosts": [ # "euca-10-20-37-171.eucalyptus.cloud.riotgames.com", # "euca-10-20-37-172.eucalyptus.cloud.riotgames.com" # ] # }, # { # "groups": ["activemq::slave"], # "hosts": [ # "euca-10-20-37-168.eucalyptus.cloud.riotgames.com" # ] # } # ] # } # class Manifest < MotherBrain::Manifest class << self # Create a new instance of {Bootstrap::Manifest} from the returning # value from calling {MotherBrain::Provisioner#up} on a provisioner and # the provision manifest sent to {MotherBrain::Provisioner#up}. # # @param [Hash] nodes # the result from calling {MotherBrain::Provisioner#up} # @param [Provisioner::Manifest] provisioner_manifest # the manifest sent to the provisioner performing # {MotherBrain::Provisioner#up} # # @return [Bootstrap::Manifest] def from_provisioner(nodes, provisioner_manifest, path = nil) nodes = nodes.dup attributes = provisioner_manifest.dup new.tap do |boot_manifest| boot_manifest.path = path boot_manifest[:nodes] = Array.new boot_manifest[:options] = provisioner_manifest[:options] attributes.node_groups.each do |node_group| instance_type = node_group[:type] count = node_group[:count] || 1 groups = node_group[:groups] hosts = Set.new used_instances = nodes.select { |node| node[:instance_type] == instance_type }.take(count) used_instances.each do |used_instance| hosts.add(used_instance[:public_hostname]) nodes.delete(used_instance) end boot_manifest[:nodes] << { groups: groups, hosts: hosts.to_a } end end end # Validates that the instance of manifest describes a layout for the given routine # # @param [Bootstrap::Manifest] manifest # @param [Plugin] plugin # # @raise [InvalidBootstrapManifest] # @raise [NoBootstrapRoutine] def validate!(manifest, plugin) unless plugin.bootstrap_routine raise NoBootstrapRoutine, "Plugin '#{plugin.name}' (#{plugin.version}) does not contain a bootstrap routine" end unless manifest[:nodes].is_a?(Array) msg = "Manifest should contain an array of nodes" raise InvalidBootstrapManifest, msg end manifest.node_groups.each do |node_group| groups = node_group[:groups] groups.each do |group| match = group.match(Plugin::NODE_GROUP_ID_REGX) unless match msg = "Manifest contained the entry: '#{group}' which is not" msg << " in the proper node group format: 'component::group'" raise InvalidBootstrapManifest, msg end unless plugin.bootstrap_routine.has_task?(group) msg = "Manifest describes the node group '#{group}' which is not found" msg << " in the given routine for '#{plugin}'" raise InvalidBootstrapManifest, msg end end end end end # Finds all hosts to be bootstrapped with a set of groups # # @return [Array] of hosts def hosts_for_group(group) hosts = node_groups.select do |node_group| node_group[:groups].include?(group) end.collect { |node_group| node_group[:hosts] }.flatten MB::NodeFilter.expand_ipranges(hosts) end # Validates that the instance of manifest describes a layout for the given routine # # @param [Plugin] plugin # # @raise [InvalidBootstrapManifest] # @raise [NoBootstrapRoutine] def validate!(plugin) self.class.validate!(self, plugin) end end end end