Class: Furnish::Provisioner::SecurityGroup
- Defined in:
- lib/furnish/provisioners/security_group.rb
Overview
Provision an EC2 Security Group. Currently receives no input and yields the group id created as output.
See the attributes and constructor for more information about creating this provisioner.
Instance Attribute Summary collapse
-
#group_id ⇒ Object
readonly
the group_id as returned from EC2, only set after provisioning has occurred.
Instance Method Summary collapse
-
#allow_ping ⇒ Object
:attr: allow_ping.
-
#apply_group_rules(group) ⇒ Object
Applies network rules (see #ingress, #egress, #allow_ping, #disallow_ping attributes) to a created security group.
-
#description ⇒ Object
:attr: description.
-
#disallow_ping ⇒ Object
:attr: disallow_ping.
-
#egress ⇒ Object
:attr: egress.
-
#find_group_by_name(group_name) ⇒ Object
Searches for a group by its name, returns the AWS::EC2::SecurityGroup instance.
-
#find_secgroup_running_instances(group) ⇒ Object
Queries the API for all instances, returns any that are not in the terminated state.
-
#generate_group_name ⇒ Object
Generate a group name (only used if :auto is assigned to #group_name).
-
#group_name ⇒ Object
:attr: group_name.
-
#group_prefix ⇒ Object
:attr: group_prefix.
-
#ingress ⇒ Object
:attr: ingress.
-
#initialize(args) ⇒ SecurityGroup
constructor
Construct a new security group provisioner.
-
#kill_instances ⇒ Object
:attr: kill_instances.
-
#load_group_for_shutdown ⇒ Object
Only used during shutdown; returns the group if it exists, otherwise logs and returns false.
-
#report ⇒ Object
Report method as required by Furnish: yields the name of the group, it’s group id, and any vpc information if VPC is in use.
-
#shutdown(args = {}) ⇒ Object
Deprovision a security group.
-
#startup(args = {}) ⇒ Object
Provision a security group.
-
#terminate_instances_for_shutdown(group) ⇒ Object
only used during shutdown; if kill_instances is true, terminates all the non-terminated machines in the group so it can be deleted.
-
#vpc ⇒ Object
:attr: vpc.
Methods inherited from AWS
#access_key, #check_aws_settings, #check_region, #ec2, #region, #secret_key
Constructor Details
#initialize(args) ⇒ SecurityGroup
Construct a new security group provisioner.
Example:
obj = Furnish::Provisioners::SecurityGroup.new(
:access_key => "foo",
:secret_key => "bar",
:region => "my-region"
)
See Furnish::Provisioner::AWS for how constructor arguments and attributes interact.
139 140 141 142 143 144 145 146 147 148 149 150 151 |
# File 'lib/furnish/provisioners/security_group.rb', line 139 def initialize(args) super check_region @ingress ||= { :authorize => [], :revoke => [] } @egress ||= { :authorize => [], :revoke => [] } @allow_ping ||= [] @disallow_ping ||= [] @group_name ||= :auto @group_prefix ||= "furnish-auto-" @kill_instances = args.has_key?(:kill_instances) ? args[:kill_instances] : true @group_id = nil end |
Instance Attribute Details
#group_id ⇒ Object (readonly)
the group_id as returned from EC2, only set after provisioning has occurred.
123 124 125 |
# File 'lib/furnish/provisioners/security_group.rb', line 123 def group_id @group_id end |
Instance Method Details
#allow_ping ⇒ Object
:attr: allow_ping
If true, allow ping from all hosts. If an array of strings, allow ping from that set of CIDR networks.
109 110 |
# File 'lib/furnish/provisioners/security_group.rb', line 109 furnish_property :allow_ping, "If true, allow ping from all hosts, if array of strings, treats them as CIDR networks." |
#apply_group_rules(group) ⇒ Object
Applies network rules (see #ingress, #egress, #allow_ping, #disallow_ping attributes) to a created security group.
Raises ArgumentError if security groups do not belong to a VPC and egress filters are set.
191 192 193 194 195 196 197 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 |
# File 'lib/furnish/provisioners/security_group.rb', line 191 def apply_group_rules(group) # XXX this meta programs passing the contents set on the object to the # group object. [:allow_ping, :disallow_ping].each do |sym| if send(sym).kind_of?(Array) ary = send(sym) if_debug(3) do puts "group #{group.id}/#{group.name}: applying #{sym} to #{ary.inspect}" end group.send(sym, *ary) elsif send(sym) if_debug(3) do puts "group #{group.id}/#{group.name}: applying #{sym} to all instances" end group.send(sym) # XXX "everybody" end end # XXX this mess is similar to above, but generates the call_name as well. # e.g.: "authorize_egress" comes from @egress[:authorize], and each # inner array in the data structure is expanded as args in the call. [:authorize, :revoke].each do |part| [:ingress, :egress].each do |type| if send(type) and ary = send(type)[part] and ary.kind_of?(Array) and !ary.empty? if type == :egress and !group.vpc? raise ArgumentError, "egress filtering was configured for #{group.id}/#{group.name} and it is not a VPC security group" end ary.each do |rule| if_debug(3) do puts "group #{group.id}/#{group.name}: applying #{part}_#{type} with values #{rule.inspect}" end group.send("#{part}_#{type}", *rule.flatten) end end end end end |
#description ⇒ Object
:attr: description
String description of the group. Optional.
46 47 48 |
# File 'lib/furnish/provisioners/security_group.rb', line 46 furnish_property :description, "Description of the group. Optional", String |
#disallow_ping ⇒ Object
:attr: disallow_ping
Inverse of #allow_ping.
117 118 |
# File 'lib/furnish/provisioners/security_group.rb', line 117 furnish_property :disallow_ping, "Inverse of :allow_ping." |
#egress ⇒ Object
:attr: egress
Same as #ingress but for egress rules. Note that egress rules only work against groups that belong to a VPCs.
99 100 101 |
# File 'lib/furnish/provisioners/security_group.rb', line 99 furnish_property :egress, "Same as :ingress, but only work on VPCs.", Hash |
#find_group_by_name(group_name) ⇒ Object
Searches for a group by its name, returns the AWS::EC2::SecurityGroup instance.
246 247 248 249 |
# File 'lib/furnish/provisioners/security_group.rb', line 246 def find_group_by_name(group_name) return nil unless group_name ec2.security_groups.filter('group-name', [group_name.to_s]).first end |
#find_secgroup_running_instances(group) ⇒ Object
Queries the API for all instances, returns any that are not in the terminated state.
238 239 240 |
# File 'lib/furnish/provisioners/security_group.rb', line 238 def find_secgroup_running_instances(group) group.instances.select { |i| i.status != :terminated } end |
#generate_group_name ⇒ Object
Generate a group name (only used if :auto is assigned to #group_name). Ensures generated names are not already in use. Returns the value it ends up with.
158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 |
# File 'lib/furnish/provisioners/security_group.rb', line 158 def generate_group_name unless group_prefix raise ArgumentError, "group_prefix must be set when auto-generating security group names" end tmp_name = nil loop do tmp_name = group_prefix + (0..rand(10).to_i).map { rand(0..9).to_s }.join("") if_debug(3) do puts "Seeing if security group name #{tmp_name} is taken" end break unless find_group_by_name(tmp_name) if_debug(3) do puts "group exists, waiting to try again" end sleep 0.3 end return tmp_name end |
#group_name ⇒ Object
:attr: group_name
Name of the group. Supply a string to name it explicitly, or :auto (the default). If :auto, will pick a name for you based on #group_prefix and a random string of numbers that do not already correspond to a group.
28 29 |
# File 'lib/furnish/provisioners/security_group.rb', line 28 furnish_property :group_name, "Name of the group. Supply a string to name it. Default is :auto -- will pick a name for you and overwrite this accessor." |
#group_prefix ⇒ Object
:attr: group_prefix
If #group_name is :auto, will use this value as a prefix. See #group_name for more information.
37 38 39 |
# File 'lib/furnish/provisioners/security_group.rb', line 37 furnish_property :group_prefix, "If :group_name is :auto, will use this value as prefix, and a string of random numbers as the postfix. Default is 'furnish-auto-'", String |
#ingress ⇒ Object
:attr: ingress
Hash containing two keys: :authorize and :revoke, which each contain array of arrays. Passed to authorize_ingress and revoke_ingress respectively.
Example:
Furnish::Provisioners::SecurityGroup.new(
:ingress => {
:authorize => [
[:tcp, 22]
[:tcp, (1024..1234)]
[:tcp, 53, "127.0.0.1/32"]
]
}
)
89 90 91 |
# File 'lib/furnish/provisioners/security_group.rb', line 89 furnish_property :ingress, "Hash containing two keys: :authorize and :revoke, which each contain array of arrays. Passed to authorize_ingress and revoke_ingress respectively.", Hash |
#kill_instances ⇒ Object
:attr: kill_instances
If true (the default), at shutdown time will terminate any instances that belong to the group provisioned by this provisioner so the security group can be destroyed.
67 68 |
# File 'lib/furnish/provisioners/security_group.rb', line 67 furnish_property :kill_instances, "If true (default), on shutdown will force termination of all instances in the security group." |
#load_group_for_shutdown ⇒ Object
Only used during shutdown; returns the group if it exists, otherwise logs and returns false. Intended to isolate idempotent function.
255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 |
# File 'lib/furnish/provisioners/security_group.rb', line 255 def load_group_for_shutdown unless group_id prov_name = furnish_group_name this_group_name = group_name if_debug(2) do puts "group id for #{this_group_name} did not exist during provisioner shutdown for #{prov_name || "unknown"} -- did this get provisioned?" end return false end group = ec2.security_groups[group_id] unless group and group.exists? this_group_name = group_name if_debug(2) do puts "group #{this_group_name} did not exist during shutdown; not attempting to deprovision it and returning success" end return false end return group end |
#report ⇒ Object
Report method as required by Furnish: yields the name of the group, it’s group id, and any vpc information if VPC is in use.
374 375 376 377 |
# File 'lib/furnish/provisioners/security_group.rb', line 374 def report vpc_str = vpc ? " vpc_id: #{vpc}" : "" ["name: #{group_name}, id: #{group_id}#{vpc_str}"] end |
#shutdown(args = {}) ⇒ Object
Deprovision a security group. If the group does not exist, immediately returns a true result.
If #kill_instances is true, it will terminate (and wait for the termination to succeed) any machines that are using the security group that are not yet terminated.
After deleting the group via the API, it will return true if the group no longer exists.
361 362 363 364 365 366 367 368 |
# File 'lib/furnish/provisioners/security_group.rb', line 361 def shutdown(args={}) return { } unless group = load_group_for_shutdown terminate_instances_for_shutdown(group) group.delete return group.exists? ? false : { } end |
#startup(args = {}) ⇒ Object
Provision a security group. Group name generation happens here if #group_name is set to :auto, if the group already exists, it will be used and then rules will be applied to it. Returns (and sets on the object) the group_id as returned by EC2.
321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 |
# File 'lib/furnish/provisioners/security_group.rb', line 321 def startup(args={}) # FIXME VPC as argument? @group_name = generate_group_name if group_name == :auto begin group = ec2.security_groups.create( group_name, :description => description, :vpc => vpc ) rescue ::AWS::EC2::Errors::InvalidGroup::Duplicate => e group = find_group_by_name(group_name) raise e unless group my_name = furnish_group_name # yay instance_eval if_debug(1) do puts "#{group.id}/#{group.name} already existed during #{my_name || "ungrouped"} provision -- applying rules and continuing" end end apply_group_rules(group) @group_id = group.id return({ :security_group_ids => (args[:security_group_ids] || []) + [group_id] }) end |
#terminate_instances_for_shutdown(group) ⇒ Object
only used during shutdown; if kill_instances is true, terminates all the non-terminated machines in the group so it can be deleted. Will keep trying this until all machines are in a terminated state.
287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 |
# File 'lib/furnish/provisioners/security_group.rb', line 287 def terminate_instances_for_shutdown(group) running_instances = find_secgroup_running_instances(group) if running_instances.count > 0 if kill_instances if_debug(3) do puts "group #{group.id}/#{group.name} is flagged to kill instances in its group on deprovision" end until (instances = find_secgroup_running_instances(group)).empty? if_debug(1) do puts "Trying to destroy security group #{group.name}, but instances are still bound to it." puts instances.map(&:id).inspect puts "Terminating instances, sleeping, and trying again." end instances.each do |i| i.terminate rescue nil end sleep 10 end else raise "instances #{running_instances.map(&:id).inspect} are still running for group #{group.id}/#{group.name} and kill_instances is off. Cannot continue." end end end |
#vpc ⇒ Object
:attr: vpc
VPC identifier. Optional, has significant effect on how security groups work. See the EC2 and VPC documentation for details.
56 57 58 |
# File 'lib/furnish/provisioners/security_group.rb', line 56 furnish_property :vpc, "VPC identifier. Optional, see EC2 and VPC documentation for details.", String |