vagrant-gecko-aws 0.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (46) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +21 -0
  3. data/.rspec +1 -0
  4. data/.travis.yml +6 -0
  5. data/CHANGELOG.md +96 -0
  6. data/Gemfile +10 -0
  7. data/LICENSE +8 -0
  8. data/README.md +329 -0
  9. data/Rakefile +22 -0
  10. data/dummy.box +0 -0
  11. data/example_box/README.md +13 -0
  12. data/example_box/metadata.json +3 -0
  13. data/lib/vagrant-aws/action/connect_aws.rb +48 -0
  14. data/lib/vagrant-aws/action/elb_deregister_instance.rb +24 -0
  15. data/lib/vagrant-aws/action/elb_register_instance.rb +24 -0
  16. data/lib/vagrant-aws/action/is_created.rb +18 -0
  17. data/lib/vagrant-aws/action/is_stopped.rb +18 -0
  18. data/lib/vagrant-aws/action/message_already_created.rb +16 -0
  19. data/lib/vagrant-aws/action/message_not_created.rb +16 -0
  20. data/lib/vagrant-aws/action/message_will_not_destroy.rb +16 -0
  21. data/lib/vagrant-aws/action/package_instance.rb +192 -0
  22. data/lib/vagrant-aws/action/read_ssh_info.rb +53 -0
  23. data/lib/vagrant-aws/action/read_state.rb +38 -0
  24. data/lib/vagrant-aws/action/run_instance.rb +314 -0
  25. data/lib/vagrant-aws/action/start_instance.rb +81 -0
  26. data/lib/vagrant-aws/action/stop_instance.rb +28 -0
  27. data/lib/vagrant-aws/action/terminate_instance.rb +51 -0
  28. data/lib/vagrant-aws/action/timed_provision.rb +21 -0
  29. data/lib/vagrant-aws/action/wait_for_state.rb +41 -0
  30. data/lib/vagrant-aws/action/warn_networks.rb +19 -0
  31. data/lib/vagrant-aws/action.rb +210 -0
  32. data/lib/vagrant-aws/config.rb +572 -0
  33. data/lib/vagrant-aws/errors.rb +43 -0
  34. data/lib/vagrant-aws/plugin.rb +73 -0
  35. data/lib/vagrant-aws/provider.rb +50 -0
  36. data/lib/vagrant-aws/util/elb.rb +58 -0
  37. data/lib/vagrant-aws/util/timer.rb +17 -0
  38. data/lib/vagrant-aws/version.rb +5 -0
  39. data/lib/vagrant-aws.rb +18 -0
  40. data/locales/en.yml +159 -0
  41. data/spec/spec_helper.rb +1 -0
  42. data/spec/vagrant-aws/config_spec.rb +374 -0
  43. data/templates/metadata.json.erb +3 -0
  44. data/templates/vagrant-aws_package_Vagrantfile.erb +5 -0
  45. data/vagrant-aws.gemspec +58 -0
  46. metadata +156 -0
@@ -0,0 +1,16 @@
1
+ module VagrantPlugins
2
+ module AWS
3
+ module Action
4
+ class MessageNotCreated
5
+ def initialize(app, env)
6
+ @app = app
7
+ end
8
+
9
+ def call(env)
10
+ env[:ui].info(I18n.t("vagrant_aws.not_created"))
11
+ @app.call(env)
12
+ end
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,16 @@
1
+ module VagrantPlugins
2
+ module AWS
3
+ module Action
4
+ class MessageWillNotDestroy
5
+ def initialize(app, env)
6
+ @app = app
7
+ end
8
+
9
+ def call(env)
10
+ env[:ui].info(I18n.t("vagrant_aws.will_not_destroy", name: env[:machine].name))
11
+ @app.call(env)
12
+ end
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,192 @@
1
+ require "log4r"
2
+ require 'vagrant/util/template_renderer'
3
+ require 'vagrant-aws/util/timer'
4
+ require 'vagrant/action/general/package'
5
+
6
+ module VagrantPlugins
7
+ module AWS
8
+ module Action
9
+ # This action packages a running aws-based server into an
10
+ # aws-based vagrant box. It does so by burning the associated
11
+ # vagrant-aws server instance, into an AMI via fog. Upon
12
+ # successful AMI burning, the action will create a .box tarball
13
+ # writing a Vagrantfile with the fresh AMI id into it.
14
+
15
+ # Vagrant itself comes with a general package action, which
16
+ # this plugin action does call. The general action provides
17
+ # the actual packaging as well as other options such as
18
+ # --include for including additional files and --vagrantfile
19
+ # which is pretty much not useful here anyway.
20
+
21
+ # The virtualbox package plugin action was loosely used
22
+ # as a model for this class.
23
+
24
+ class PackageInstance < Vagrant::Action::General::Package
25
+ include Vagrant::Util::Retryable
26
+
27
+ def initialize(app, env)
28
+ super
29
+ @logger = Log4r::Logger.new("vagrant_aws::action::package_instance")
30
+ env["package.include"] ||= []
31
+ end
32
+
33
+ alias_method :general_call, :call
34
+ def call(env)
35
+ # Initialize metrics if they haven't been
36
+ env[:metrics] ||= {}
37
+
38
+ # This block attempts to burn the server instance into an AMI
39
+ begin
40
+ # Get the Fog server object for given machine
41
+ server = env[:aws_compute].servers.get(env[:machine].id)
42
+
43
+ env[:ui].info(I18n.t("vagrant_aws.packaging_instance", :instance_id => server.id))
44
+
45
+ # Make the request to AWS to create an AMI from machine's instance
46
+ ami_response = server.service.create_image server.id, "#{server.tags["Name"]} Package - #{Time.now.strftime("%Y%m%d-%H%M%S")}", ""
47
+
48
+ # Find ami id
49
+ @ami_id = ami_response.data[:body]["imageId"]
50
+
51
+ # Attempt to burn the aws instance into an AMI within timeout
52
+ env[:metrics]["instance_ready_time"] = Util::Timer.time do
53
+
54
+ # Get the config, to set the ami burn timeout
55
+ region = env[:machine].provider_config.region
56
+ region_config = env[:machine].provider_config.get_region_config(region)
57
+ tries = region_config.instance_package_timeout / 2
58
+
59
+ env[:ui].info(I18n.t("vagrant_aws.burning_ami", :ami_id => @ami_id))
60
+ if !region_config.package_tags.empty?
61
+ server.service.create_tags(@ami_id, region_config.package_tags)
62
+ end
63
+
64
+ # Check the status of the AMI every 2 seconds until the ami burn timeout has been reached
65
+ begin
66
+ retryable(:on => Fog::Errors::TimeoutError, :tries => tries) do
67
+ # If we're interrupted don't worry about waiting
68
+ next if env[:interrupted]
69
+
70
+ # Need to update the ami_obj on each cycle
71
+ ami_obj = server.service.images.get(@ami_id)
72
+
73
+ # Wait for the server to be ready, raise error if timeout reached
74
+ server.wait_for(2) {
75
+ if ami_obj.state == "failed"
76
+ raise Errors::InstancePackageError,
77
+ ami_id: ami_obj.id,
78
+ err: ami_obj.state
79
+ end
80
+
81
+ ami_obj.ready?
82
+ }
83
+ end
84
+ rescue Fog::Errors::TimeoutError
85
+ # Notify the user upon timeout
86
+ raise Errors::InstancePackageTimeout,
87
+ timeout: region_config.instance_package_timeout
88
+ end
89
+ end
90
+ env[:ui].info(I18n.t("vagrant_aws.packaging_instance_complete", :time_seconds => env[:metrics]["instance_ready_time"].to_i))
91
+ rescue Fog::Compute::AWS::Error => e
92
+ raise Errors::FogError, :message => e.message
93
+ end
94
+
95
+ # Handles inclusions from --include and --vagrantfile options
96
+ setup_package_files(env)
97
+
98
+ # Setup the temporary directory for the tarball files
99
+ @temp_dir = env[:tmp_path].join(Time.now.to_i.to_s)
100
+ env["export.temp_dir"] = @temp_dir
101
+ FileUtils.mkpath(env["export.temp_dir"])
102
+
103
+ # Create the Vagrantfile and metadata.json files from templates to go in the box
104
+ create_vagrantfile(env)
105
+ create_metadata_file(env)
106
+
107
+ # Just match up a couple environmental variables so that
108
+ # the superclass will do the right thing. Then, call the
109
+ # superclass to actually create the tarball (.box file)
110
+ env["package.directory"] = env["export.temp_dir"]
111
+ general_call(env)
112
+
113
+ # Always call recover to clean up the temp dir
114
+ clean_temp_dir
115
+ end
116
+
117
+ protected
118
+
119
+ # Cleanup temp dir and files
120
+ def clean_temp_dir
121
+ if @temp_dir && File.exist?(@temp_dir)
122
+ FileUtils.rm_rf(@temp_dir)
123
+ end
124
+ end
125
+
126
+ # This method generates the Vagrantfile at the root of the box. Taken from
127
+ # VagrantPlugins::ProviderVirtualBox::Action::PackageVagrantfile
128
+ def create_vagrantfile env
129
+ File.open(File.join(env["export.temp_dir"], "Vagrantfile"), "w") do |f|
130
+ f.write(TemplateRenderer.render("vagrant-aws_package_Vagrantfile", {
131
+ region: env[:machine].provider_config.region,
132
+ ami: @ami_id,
133
+ template_root: template_root
134
+ }))
135
+ end
136
+ end
137
+
138
+ # This method generates the metadata.json file at the root of the box.
139
+ def create_metadata_file env
140
+ File.open(File.join(env["export.temp_dir"], "metadata.json"), "w") do |f|
141
+ f.write(TemplateRenderer.render("metadata.json", {
142
+ template_root: template_root
143
+ }))
144
+ end
145
+ end
146
+
147
+ # Sets up --include and --vagrantfile files which may be added as optional
148
+ # parameters. Taken from VagrantPlugins::ProviderVirtualBox::Action::SetupPackageFiles
149
+ def setup_package_files(env)
150
+ files = {}
151
+ env["package.include"].each do |file|
152
+ source = Pathname.new(file)
153
+ dest = nil
154
+
155
+ # If the source is relative then we add the file as-is to the include
156
+ # directory. Otherwise, we copy only the file into the root of the
157
+ # include directory. Kind of strange, but seems to match what people
158
+ # expect based on history.
159
+ if source.relative?
160
+ dest = source
161
+ else
162
+ dest = source.basename
163
+ end
164
+
165
+ # Assign the mapping
166
+ files[file] = dest
167
+ end
168
+
169
+ if env["package.vagrantfile"]
170
+ # Vagrantfiles are treated special and mapped to a specific file
171
+ files[env["package.vagrantfile"]] = "_Vagrantfile"
172
+ end
173
+
174
+ # Verify the mapping
175
+ files.each do |from, _|
176
+ raise Vagrant::Errors::PackageIncludeMissing,
177
+ file: from if !File.exist?(from)
178
+ end
179
+
180
+ # Save the mapping
181
+ env["package.files"] = files
182
+ end
183
+
184
+ # Used to find the base location of aws-vagrant templates
185
+ def template_root
186
+ AWS.source_root.join("templates")
187
+ end
188
+
189
+ end
190
+ end
191
+ end
192
+ end
@@ -0,0 +1,53 @@
1
+ require "log4r"
2
+
3
+ module VagrantPlugins
4
+ module AWS
5
+ module Action
6
+ # This action reads the SSH info for the machine and puts it into the
7
+ # `:machine_ssh_info` key in the environment.
8
+ class ReadSSHInfo
9
+ def initialize(app, env)
10
+ @app = app
11
+ @logger = Log4r::Logger.new("vagrant_aws::action::read_ssh_info")
12
+ end
13
+
14
+ def call(env)
15
+ env[:machine_ssh_info] = read_ssh_info(env[:aws_compute], env[:machine])
16
+
17
+ @app.call(env)
18
+ end
19
+
20
+ def read_ssh_info(aws, machine)
21
+ return nil if machine.id.nil?
22
+
23
+ # Find the machine
24
+ server = aws.servers.get(machine.id)
25
+ if server.nil?
26
+ # The machine can't be found
27
+ @logger.info("Machine couldn't be found, assuming it got destroyed.")
28
+ machine.id = nil
29
+ return nil
30
+ end
31
+
32
+ # read attribute override
33
+ ssh_host_attribute = machine.provider_config.
34
+ get_region_config(machine.provider_config.region).ssh_host_attribute
35
+ # default host attributes to try. NOTE: Order matters!
36
+ ssh_attrs = [:dns_name, :public_ip_address, :private_ip_address]
37
+ ssh_attrs = (Array(ssh_host_attribute) + ssh_attrs).uniq if ssh_host_attribute
38
+ # try each attribute, get out on first value
39
+ host_value = nil
40
+ while !host_value and attr_name = ssh_attrs.shift
41
+ begin
42
+ host_value = server.send(attr_name)
43
+ rescue NoMethodError
44
+ @logger.info("SSH host attribute not found #{attr_name}")
45
+ end
46
+ end
47
+
48
+ return { :host => host_value, :port => 22 }
49
+ end
50
+ end
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,38 @@
1
+ require "log4r"
2
+
3
+ module VagrantPlugins
4
+ module AWS
5
+ module Action
6
+ # This action reads the state of the machine and puts it in the
7
+ # `:machine_state_id` key in the environment.
8
+ class ReadState
9
+ def initialize(app, env)
10
+ @app = app
11
+ @logger = Log4r::Logger.new("vagrant_aws::action::read_state")
12
+ end
13
+
14
+ def call(env)
15
+ env[:machine_state_id] = read_state(env[:aws_compute], env[:machine])
16
+
17
+ @app.call(env)
18
+ end
19
+
20
+ def read_state(aws, machine)
21
+ return :not_created if machine.id.nil?
22
+
23
+ # Find the machine
24
+ server = aws.servers.get(machine.id)
25
+ if server.nil? || [:"shutting-down", :terminated].include?(server.state.to_sym)
26
+ # The machine can't be found
27
+ @logger.info("Machine not found or terminated, assuming it got destroyed.")
28
+ machine.id = nil
29
+ return :not_created
30
+ end
31
+
32
+ # Return the state
33
+ return server.state.to_sym
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,314 @@
1
+ require "log4r"
2
+ require 'json'
3
+
4
+ require 'vagrant/util/retryable'
5
+
6
+ require 'vagrant-aws/util/timer'
7
+
8
+ module VagrantPlugins
9
+ module AWS
10
+ module Action
11
+ # This runs the configured instance.
12
+ class RunInstance
13
+ include Vagrant::Util::Retryable
14
+
15
+ def initialize(app, env)
16
+ @app = app
17
+ @logger = Log4r::Logger.new("vagrant_aws::action::run_instance")
18
+ end
19
+
20
+ def call(env)
21
+ # Initialize metrics if they haven't been
22
+ env[:metrics] ||= {}
23
+
24
+ # Get the region we're going to booting up in
25
+ region = env[:machine].provider_config.region
26
+
27
+ # Get the configs
28
+ region_config = env[:machine].provider_config.get_region_config(region)
29
+ ami = region_config.ami
30
+ availability_zone = region_config.availability_zone
31
+ placement_group = region_config.placement_group
32
+ instance_type = region_config.instance_type
33
+ keypair = region_config.keypair_name
34
+ private_ip_address = region_config.private_ip_address
35
+ security_groups = region_config.security_groups
36
+ subnet_id = region_config.subnet_id
37
+ tags = region_config.tags
38
+ user_data = region_config.user_data
39
+ block_device_mapping = region_config.block_device_mapping
40
+ elastic_ip = region_config.elastic_ip
41
+ terminate_on_shutdown = region_config.terminate_on_shutdown
42
+ iam_instance_profile_arn = region_config.iam_instance_profile_arn
43
+ iam_instance_profile_name = region_config.iam_instance_profile_name
44
+ monitoring = region_config.monitoring
45
+ ebs_optimized = region_config.ebs_optimized
46
+ source_dest_check = region_config.source_dest_check
47
+ associate_public_ip = region_config.associate_public_ip
48
+ kernel_id = region_config.kernel_id
49
+ tenancy = region_config.tenancy
50
+
51
+ # If there is no keypair then warn the user
52
+ if !keypair
53
+ env[:ui].warn(I18n.t("vagrant_aws.launch_no_keypair"))
54
+ end
55
+
56
+ # If there is a subnet ID then warn the user
57
+ if subnet_id && !elastic_ip
58
+ env[:ui].warn(I18n.t("vagrant_aws.launch_vpc_warning"))
59
+ end
60
+
61
+ # Launch!
62
+ env[:ui].info(I18n.t("vagrant_aws.launching_instance"))
63
+ env[:ui].info(" -- Type: #{instance_type}")
64
+ env[:ui].info(" -- AMI: #{ami}")
65
+ env[:ui].info(" -- Region: #{region}")
66
+ env[:ui].info(" -- Availability Zone: #{availability_zone}") if availability_zone
67
+ env[:ui].info(" -- Placement Group: #{placement_group}") if placement_group
68
+ env[:ui].info(" -- Keypair: #{keypair}") if keypair
69
+ env[:ui].info(" -- Subnet ID: #{subnet_id}") if subnet_id
70
+ env[:ui].info(" -- IAM Instance Profile ARN: #{iam_instance_profile_arn}") if iam_instance_profile_arn
71
+ env[:ui].info(" -- IAM Instance Profile Name: #{iam_instance_profile_name}") if iam_instance_profile_name
72
+ env[:ui].info(" -- Private IP: #{private_ip_address}") if private_ip_address
73
+ env[:ui].info(" -- Elastic IP: #{elastic_ip}") if elastic_ip
74
+ env[:ui].info(" -- User Data: yes") if user_data
75
+ env[:ui].info(" -- Security Groups: #{security_groups.inspect}") if !security_groups.empty?
76
+ env[:ui].info(" -- User Data: #{user_data}") if user_data
77
+ env[:ui].info(" -- Block Device Mapping: #{block_device_mapping}") if block_device_mapping
78
+ env[:ui].info(" -- Terminate On Shutdown: #{terminate_on_shutdown}")
79
+ env[:ui].info(" -- Monitoring: #{monitoring}")
80
+ env[:ui].info(" -- EBS optimized: #{ebs_optimized}")
81
+ env[:ui].info(" -- Source Destination check: #{source_dest_check}")
82
+ env[:ui].info(" -- Assigning a public IP address in a VPC: #{associate_public_ip}")
83
+ env[:ui].info(" -- VPC tenancy specification: #{tenancy}")
84
+
85
+ options = {
86
+ :availability_zone => availability_zone,
87
+ :placement_group => placement_group,
88
+ :flavor_id => instance_type,
89
+ :image_id => ami,
90
+ :key_name => keypair,
91
+ :private_ip_address => private_ip_address,
92
+ :subnet_id => subnet_id,
93
+ :iam_instance_profile_arn => iam_instance_profile_arn,
94
+ :iam_instance_profile_name => iam_instance_profile_name,
95
+ :tags => tags,
96
+ :user_data => user_data,
97
+ :block_device_mapping => block_device_mapping,
98
+ :instance_initiated_shutdown_behavior => terminate_on_shutdown == true ? "terminate" : nil,
99
+ :monitoring => monitoring,
100
+ :ebs_optimized => ebs_optimized,
101
+ :associate_public_ip => associate_public_ip,
102
+ :kernel_id => kernel_id,
103
+ :tenancy => tenancy
104
+ }
105
+
106
+ if !security_groups.empty?
107
+ security_group_key = options[:subnet_id].nil? ? :groups : :security_group_ids
108
+ options[security_group_key] = security_groups
109
+ env[:ui].warn(I18n.t("vagrant_aws.warn_ssh_access")) unless allows_ssh_port?(env, security_groups, subnet_id)
110
+ end
111
+
112
+ begin
113
+ server = env[:aws_compute].servers.create(options)
114
+ rescue Fog::Compute::AWS::NotFound => e
115
+ # Invalid subnet doesn't have its own error so we catch and
116
+ # check the error message here.
117
+ if e.message =~ /subnet ID/
118
+ raise Errors::FogError,
119
+ :message => "Subnet ID not found: #{subnet_id}"
120
+ end
121
+
122
+ raise
123
+ rescue Fog::Compute::AWS::Error => e
124
+ raise Errors::FogError, :message => e.message
125
+ rescue Excon::Errors::HTTPStatusError => e
126
+ raise Errors::InternalFogError,
127
+ :error => e.message,
128
+ :response => e.response.body
129
+ end
130
+
131
+ # Immediately save the ID since it is created at this point.
132
+ env[:machine].id = server.id
133
+
134
+ # Wait for the instance to be ready first
135
+ env[:metrics]["instance_ready_time"] = Util::Timer.time do
136
+ tries = region_config.instance_ready_timeout / 2
137
+
138
+ env[:ui].info(I18n.t("vagrant_aws.waiting_for_ready"))
139
+ begin
140
+ retryable(:on => Fog::Errors::TimeoutError, :tries => tries) do
141
+ # If we're interrupted don't worry about waiting
142
+ next if env[:interrupted]
143
+
144
+ # Wait for the server to be ready
145
+ server.wait_for(2, region_config.instance_check_interval) { ready? }
146
+ end
147
+ rescue Fog::Errors::TimeoutError
148
+ # Delete the instance
149
+ terminate(env)
150
+
151
+ # Notify the user
152
+ raise Errors::InstanceReadyTimeout,
153
+ timeout: region_config.instance_ready_timeout
154
+ end
155
+ end
156
+
157
+ @logger.info("Time to instance ready: #{env[:metrics]["instance_ready_time"]}")
158
+
159
+ # Allocate and associate an elastic IP if requested
160
+ if elastic_ip
161
+ domain = subnet_id ? 'vpc' : 'standard'
162
+ do_elastic_ip(env, domain, server, elastic_ip)
163
+ end
164
+
165
+ # Set the source destination checks
166
+ if !source_dest_check.nil?
167
+ if server.vpc_id.nil?
168
+ env[:ui].warn(I18n.t("vagrant_aws.source_dest_checks_no_vpc"))
169
+ else
170
+ begin
171
+ attrs = {
172
+ "SourceDestCheck.Value" => source_dest_check
173
+ }
174
+ env[:aws_compute].modify_instance_attribute(server.id, attrs)
175
+ rescue Fog::Compute::AWS::Error => e
176
+ raise Errors::FogError, :message => e.message
177
+ end
178
+ end
179
+ end
180
+
181
+ if !env[:interrupted]
182
+ env[:metrics]["instance_ssh_time"] = Util::Timer.time do
183
+ # Wait for SSH to be ready.
184
+ env[:ui].info(I18n.t("vagrant_aws.waiting_for_ssh"))
185
+ network_ready_retries = 0
186
+ network_ready_retries_max = 10
187
+ while true
188
+ # If we're interrupted then just back out
189
+ break if env[:interrupted]
190
+ # When an ec2 instance comes up, it's networking may not be ready
191
+ # by the time we connect.
192
+ begin
193
+ break if env[:machine].communicate.ready?
194
+ rescue Exception => e
195
+ if network_ready_retries < network_ready_retries_max then
196
+ network_ready_retries += 1
197
+ @logger.warn(I18n.t("vagrant_aws.waiting_for_ssh, retrying"))
198
+ else
199
+ raise e
200
+ end
201
+ end
202
+ sleep 2
203
+ end
204
+ end
205
+
206
+ @logger.info("Time for SSH ready: #{env[:metrics]["instance_ssh_time"]}")
207
+
208
+ # Ready and booted!
209
+ env[:ui].info(I18n.t("vagrant_aws.ready"))
210
+ end
211
+
212
+ # Terminate the instance if we were interrupted
213
+ terminate(env) if env[:interrupted]
214
+
215
+ @app.call(env)
216
+ end
217
+
218
+ def recover(env)
219
+ return if env["vagrant.error"].is_a?(Vagrant::Errors::VagrantError)
220
+
221
+ if env[:machine].provider.state.id != :not_created
222
+ # Undo the import
223
+ terminate(env)
224
+ end
225
+ end
226
+
227
+ def allows_ssh_port?(env, test_sec_groups, is_vpc)
228
+ port = 22 # TODO get ssh_info port
229
+ test_sec_groups = [ "default" ] if test_sec_groups.empty? # AWS default security group
230
+ # filter groups by name or group_id (vpc)
231
+ groups = test_sec_groups.map do |tsg|
232
+ env[:aws_compute].security_groups.all.select { |sg| tsg == (is_vpc ? sg.group_id : sg.name) }
233
+ end.flatten
234
+ # filter TCP rules
235
+ rules = groups.map { |sg| sg.ip_permissions.select { |r| r["ipProtocol"] == "tcp" } }.flatten
236
+ # test if any range includes port
237
+ !rules.select { |r| (r["fromPort"]..r["toPort"]).include?(port) }.empty?
238
+ end
239
+
240
+ def do_elastic_ip(env, domain, server, elastic_ip)
241
+ if elastic_ip =~ /\d+\.\d+\.\d+\.\d+/
242
+ begin
243
+ address = env[:aws_compute].addresses.get(elastic_ip)
244
+ rescue
245
+ handle_elastic_ip_error(env, "Could not retrieve Elastic IP: #{elastic_ip}")
246
+ end
247
+ if address.nil?
248
+ handle_elastic_ip_error(env, "Elastic IP not available: #{elastic_ip}")
249
+ end
250
+ @logger.debug("Public IP #{address.public_ip}")
251
+ else
252
+ begin
253
+ allocation = env[:aws_compute].allocate_address(domain)
254
+ rescue
255
+ handle_elastic_ip_error(env, "Could not allocate Elastic IP.")
256
+ end
257
+ @logger.debug("Public IP #{allocation.body['publicIp']}")
258
+ end
259
+
260
+ # Associate the address and save the metadata to a hash
261
+ h = nil
262
+ if domain == 'vpc'
263
+ # VPC requires an allocation ID to assign an IP
264
+ if address
265
+ association = env[:aws_compute].associate_address(server.id, nil, nil, address.allocation_id)
266
+ else
267
+ association = env[:aws_compute].associate_address(server.id, nil, nil, allocation.body['allocationId'])
268
+ # Only store release data for an allocated address
269
+ h = { :allocation_id => allocation.body['allocationId'], :association_id => association.body['associationId'], :public_ip => allocation.body['publicIp'] }
270
+ end
271
+ else
272
+ # Standard EC2 instances only need the allocated IP address
273
+ if address
274
+ association = env[:aws_compute].associate_address(server.id, address.public_ip)
275
+ else
276
+ association = env[:aws_compute].associate_address(server.id, allocation.body['publicIp'])
277
+ h = { :public_ip => allocation.body['publicIp'] }
278
+ end
279
+ end
280
+
281
+ unless association.body['return']
282
+ @logger.debug("Could not associate Elastic IP.")
283
+ terminate(env)
284
+ raise Errors::FogError,
285
+ :message => "Could not allocate Elastic IP."
286
+ end
287
+
288
+ # Save this IP to the data dir so it can be released when the instance is destroyed
289
+ if h
290
+ ip_file = env[:machine].data_dir.join('elastic_ip')
291
+ ip_file.open('w+') do |f|
292
+ f.write(h.to_json)
293
+ end
294
+ end
295
+ end
296
+
297
+ def handle_elastic_ip_error(env, message)
298
+ @logger.debug(message)
299
+ terminate(env)
300
+ raise Errors::FogError,
301
+ :message => message
302
+ end
303
+
304
+ def terminate(env)
305
+ destroy_env = env.dup
306
+ destroy_env.delete(:interrupted)
307
+ destroy_env[:config_validate] = false
308
+ destroy_env[:force_confirm_destroy] = true
309
+ env[:action_runner].run(Action.action_destroy, destroy_env)
310
+ end
311
+ end
312
+ end
313
+ end
314
+ end