Class: Furnish::ProvisionerGroup
- Inherits:
-
Array
- Object
- Array
- Furnish::ProvisionerGroup
- Includes:
- Logger::Mixins
- Defined in:
- lib/furnish/provisioner_group.rb
Overview
A provisioner group is an array of provisioners. See Furnish::Provisioner for what the Provisioner API looks like.
A group has a set of provisioner objects, a name for the group, and a list of names that count as dependencies. It has methods to operate on the group as a unit, starting them up as a unit and shutting them down. It is primarily operated on by Furnish::Scheduler.
In general, you interact with this class via Furnish::Scheduler#schedule_provision, but you can also construct groups yourself and deal with them via Furnish::Scheduler#schedule_provisioner_group.
It delegates to Array and can be treated like one via the semantics of Ruby’s DelegateClass.
Instance Attribute Summary collapse
-
#dependencies ⇒ Object
readonly
The list of names the group depends on.
-
#group_state ⇒ Object
readonly
group state object.
-
#name ⇒ Object
readonly
The name of the group.
Instance Method Summary collapse
-
#initialize(provisioners, furnish_group_name, dependencies = []) ⇒ ProvisionerGroup
constructor
Create a new Provisioner group.
-
#recover(force_deprovision = false) ⇒ Object
Initiate recovery for this group.
-
#shutdown(args = { }, force = false) ⇒ Object
Deprovision this group.
-
#startup(args = { }) ⇒ Object
Provision this group.
Methods included from Logger::Mixins
Constructor Details
#initialize(provisioners, furnish_group_name, dependencies = []) ⇒ ProvisionerGroup
Create a new Provisioner group.
-
provisioners can be an array of provisioner objects or a single item (which will be boxed). This is what the array consists of that this object is.
-
furnish_group_name is a string. always.
-
dependencies can either be passed as an Array or Set, and will be converted to a Set if they are not a Set.
See #assert_provisioner_protocol, Furnish::Protocol, and Furnish::Provisioner::API for information on how a set of provisioner objects will be validated during the construction of the group.
48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 |
# File 'lib/furnish/provisioner_group.rb', line 48 def initialize(provisioners, furnish_group_name, dependencies=[]) @group_state = Palsy::Map.new('vm_group_state', furnish_group_name) # # FIXME maybe move the naming construct to here instead of populating it # out to the provisioners # provisioners = [provisioners].compact unless provisioners.kind_of?(Array) if provisioners.empty? raise ArgumentError, "A non-empty list of provisioners must be provided" end provisioners.each do |prov| prov.furnish_group_name = furnish_group_name end @name = furnish_group_name @dependencies = dependencies.kind_of?(Set) ? dependencies : Set[*dependencies] assert_provisioner_protocol(provisioners) super(provisioners) end |
Instance Attribute Details
#dependencies ⇒ Object (readonly)
The list of names the group depends on.
30 31 32 |
# File 'lib/furnish/provisioner_group.rb', line 30 def dependencies @dependencies end |
#group_state ⇒ Object (readonly)
group state object. should not be used outside of internals.
32 33 34 |
# File 'lib/furnish/provisioner_group.rb', line 32 def group_state @group_state end |
#name ⇒ Object (readonly)
The name of the group.
28 29 30 |
# File 'lib/furnish/provisioner_group.rb', line 28 def name @name end |
Instance Method Details
#recover(force_deprovision = false) ⇒ Object
Initiate recovery for this group. Reading Furnish::Provisioner::API#recover is essential for this documentation.
This method should not be used directly – see Furnish::Scheduler#recover.
#startup and #shutdown track various bits of information about state as they run provisioners. #recover uses this information to find out where things stopped, and executes a Furnish::Provisioner::API#recover method with the action and last parameters supplied. If the result of the recovery is true, it then attempts to finish the provisioning process by starting with the action that failed the last time (the same provisioner the recover method was called on).
#recover will return nil if it can’t actually recover anything because it doesn’t have enough information. It will also make no attempt to recover (and fail by returning false) if the provisioner does not allow recovery (see Furnish::Provisioner::API.allows_recovery?).
If you pass a truthy argument, it will pass this on to #shutdown if the action is required – this is required for forced deprovisioning and is dealt with by Furnish::Scheduler.
198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 |
# File 'lib/furnish/provisioner_group.rb', line 198 def recover(force_deprovision=false) index = @group_state['index'] action = @group_state['action'] provisioner = @group_state['provisioner'] provisioner_args = @group_state['provisioner_args'] return nil unless action and provisioner and index result = false # # The next few lines here work around mutable state needing to happen in # the original provisioner, but since the one we looked up will actually # not be the same object, we need to deal with that by dispatching # recovery to the actual provisioner object in the group. # # The one stored is still useful for informational and validation # purposes, but the index is the ultimate authority. # offset = case action when :startup index when :shutdown size - 1 - index else raise "Wtf?" end orig_prov = self[offset] unless orig_prov.class == provisioner.class raise "index and provisioner data don't seem to agree" end if orig_prov.class.respond_to?(:allows_recovery?) and orig_prov.class.allows_recovery? if orig_prov.recover(action, provisioner_args) @start_index = index @start_provisioner = orig_prov result = case action when :startup startup(provisioner_args) when :shutdown shutdown(provisioner_args, force_deprovision) else raise "Wtf?" end end end @start_index, @start_provisioner = nil, nil return result # scheduler will take it from here end |
#shutdown(args = { }, force = false) ⇒ Object
Deprovision this group.
Provisioners are run in reverse order against the shutdown method. Argument handling semantics are exactly the same as #startup.
If a true argument is passed to this method as the second argument, the raise semantics will be ignored (but still logged), allowing all the provisioners to run their shutdown routines. See Furnish::Scheduler#force_deprovision for information on how to use this externally.
132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 |
# File 'lib/furnish/provisioner_group.rb', line 132 def shutdown(args={ }, force=false) @group_state['action'] = :shutdown reverse.each_with_index do |this_prov, i| next unless check_recovery(this_prov, i) set_recovery(this_prov, i, args) shutdown_args = args begin args = perform_deprovision(this_prov, shutdown_args) rescue Exception => e if_debug do puts "Deprovision of #{this_prov} had errors:" puts "#{e.}" end unless force set_recovery(this_prov, i, shutdown_args) raise e end end unless args or force set_recovery(this_prov, i, shutdown_args) raise "Could not deprovision #{this_prov}" end unless args.kind_of?(Hash) or force set_recovery(this_prov, i, startup_args) raise ArgumentError, "#{this_prov.class} does not return data that can be consumed by the next provisioner" end args = shutdown_args.merge(args || { }) end clean_state return true end |
#startup(args = { }) ⇒ Object
Provision this group.
Initial arguments go to the first provisioner’s startup method, and then the return values, if a Hash, get merged with what was passed, and then the result is passed to the next provisioner’s startup method. Any falsey value causes a RuntimeError to be raised and provisioning halts, effectively creating a chain of responsibility pattern.
If a block is provided, will yield self to it for each step through the group.
86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 |
# File 'lib/furnish/provisioner_group.rb', line 86 def startup(args={ }) @group_state['action'] = :startup each_with_index do |this_prov, i| next unless check_recovery(this_prov, i) set_recovery(this_prov, i, args) startup_args = args unless args = this_prov.startup(startup_args) if_debug do puts "Could not provision #{this_prov}" end set_recovery(this_prov, i, startup_args) raise "Could not provision #{this_prov}" end unless args.kind_of?(Hash) set_recovery(this_prov, i, startup_args) raise ArgumentError, "#{this_prov.class} does not return data that can be consumed by the next provisioner" end args = startup_args.merge(args) yield self if block_given? end clean_state return true end |