vagrant-aws-mscottford 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 (47) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +21 -0
  3. data/.rspec +1 -0
  4. data/CHANGELOG.md +96 -0
  5. data/Gemfile +14 -0
  6. data/LICENSE +8 -0
  7. data/README.md +328 -0
  8. data/Rakefile +22 -0
  9. data/cortex.yaml +15 -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 +51 -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 +311 -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 +209 -0
  32. data/lib/vagrant-aws/cap/winrm_info.rb +46 -0
  33. data/lib/vagrant-aws/config.rb +577 -0
  34. data/lib/vagrant-aws/errors.rb +43 -0
  35. data/lib/vagrant-aws/plugin.rb +78 -0
  36. data/lib/vagrant-aws/provider.rb +50 -0
  37. data/lib/vagrant-aws/util/elb.rb +58 -0
  38. data/lib/vagrant-aws/util/timer.rb +17 -0
  39. data/lib/vagrant-aws/version.rb +5 -0
  40. data/lib/vagrant-aws.rb +18 -0
  41. data/locales/en.yml +159 -0
  42. data/spec/spec_helper.rb +1 -0
  43. data/spec/vagrant-aws/config_spec.rb +374 -0
  44. data/templates/metadata.json.erb +3 -0
  45. data/templates/vagrant-aws_package_Vagrantfile.erb +5 -0
  46. data/vagrant-aws-mscottford.gemspec +64 -0
  47. metadata +177 -0
@@ -0,0 +1,16 @@
1
+ module VagrantPlugins
2
+ module AWS
3
+ module Action
4
+ class MessageAlreadyCreated
5
+ def initialize(app, env)
6
+ @app = app
7
+ end
8
+
9
+ def call(env)
10
+ env[:ui].info(I18n.t("vagrant_aws.already_status", :status => "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 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,311 @@
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
+ instance_type = region_config.instance_type
32
+ keypair = region_config.keypair_name
33
+ private_ip_address = region_config.private_ip_address
34
+ security_groups = region_config.security_groups
35
+ subnet_id = region_config.subnet_id
36
+ tags = region_config.tags
37
+ user_data = region_config.user_data
38
+ block_device_mapping = region_config.block_device_mapping
39
+ elastic_ip = region_config.elastic_ip
40
+ terminate_on_shutdown = region_config.terminate_on_shutdown
41
+ iam_instance_profile_arn = region_config.iam_instance_profile_arn
42
+ iam_instance_profile_name = region_config.iam_instance_profile_name
43
+ monitoring = region_config.monitoring
44
+ ebs_optimized = region_config.ebs_optimized
45
+ source_dest_check = region_config.source_dest_check
46
+ associate_public_ip = region_config.associate_public_ip
47
+ kernel_id = region_config.kernel_id
48
+ tenancy = region_config.tenancy
49
+
50
+ # If there is no keypair then warn the user
51
+ if !keypair
52
+ env[:ui].warn(I18n.t("vagrant_aws.launch_no_keypair"))
53
+ end
54
+
55
+ # If there is a subnet ID then warn the user
56
+ if subnet_id && !elastic_ip
57
+ env[:ui].warn(I18n.t("vagrant_aws.launch_vpc_warning"))
58
+ end
59
+
60
+ # Launch!
61
+ env[:ui].info(I18n.t("vagrant_aws.launching_instance"))
62
+ env[:ui].info(" -- Type: #{instance_type}")
63
+ env[:ui].info(" -- AMI: #{ami}")
64
+ env[:ui].info(" -- Region: #{region}")
65
+ env[:ui].info(" -- Availability Zone: #{availability_zone}") if availability_zone
66
+ env[:ui].info(" -- Keypair: #{keypair}") if keypair
67
+ env[:ui].info(" -- Subnet ID: #{subnet_id}") if subnet_id
68
+ env[:ui].info(" -- IAM Instance Profile ARN: #{iam_instance_profile_arn}") if iam_instance_profile_arn
69
+ env[:ui].info(" -- IAM Instance Profile Name: #{iam_instance_profile_name}") if iam_instance_profile_name
70
+ env[:ui].info(" -- Private IP: #{private_ip_address}") if private_ip_address
71
+ env[:ui].info(" -- Elastic IP: #{elastic_ip}") if elastic_ip
72
+ env[:ui].info(" -- User Data: yes") if user_data
73
+ env[:ui].info(" -- Security Groups: #{security_groups.inspect}") if !security_groups.empty?
74
+ env[:ui].info(" -- User Data: #{user_data}") if user_data
75
+ env[:ui].info(" -- Block Device Mapping: #{block_device_mapping}") if block_device_mapping
76
+ env[:ui].info(" -- Terminate On Shutdown: #{terminate_on_shutdown}")
77
+ env[:ui].info(" -- Monitoring: #{monitoring}")
78
+ env[:ui].info(" -- EBS optimized: #{ebs_optimized}")
79
+ env[:ui].info(" -- Source Destination check: #{source_dest_check}")
80
+ env[:ui].info(" -- Assigning a public IP address in a VPC: #{associate_public_ip}")
81
+ env[:ui].info(" -- VPC tenancy specification: #{tenancy}")
82
+
83
+ options = {
84
+ :availability_zone => availability_zone,
85
+ :flavor_id => instance_type,
86
+ :image_id => ami,
87
+ :key_name => keypair,
88
+ :private_ip_address => private_ip_address,
89
+ :subnet_id => subnet_id,
90
+ :iam_instance_profile_arn => iam_instance_profile_arn,
91
+ :iam_instance_profile_name => iam_instance_profile_name,
92
+ :tags => tags,
93
+ :user_data => user_data,
94
+ :block_device_mapping => block_device_mapping,
95
+ :instance_initiated_shutdown_behavior => terminate_on_shutdown == true ? "terminate" : nil,
96
+ :monitoring => monitoring,
97
+ :ebs_optimized => ebs_optimized,
98
+ :associate_public_ip => associate_public_ip,
99
+ :kernel_id => kernel_id,
100
+ :tenancy => tenancy
101
+ }
102
+
103
+ if !security_groups.empty?
104
+ security_group_key = options[:subnet_id].nil? ? :groups : :security_group_ids
105
+ options[security_group_key] = security_groups
106
+ env[:ui].warn(I18n.t("vagrant_aws.warn_ssh_access")) unless allows_ssh_port?(env, security_groups, subnet_id)
107
+ end
108
+
109
+ begin
110
+ server = env[:aws_compute].servers.create(options)
111
+ rescue Fog::Compute::AWS::NotFound => e
112
+ # Invalid subnet doesn't have its own error so we catch and
113
+ # check the error message here.
114
+ if e.message =~ /subnet ID/
115
+ raise Errors::FogError,
116
+ :message => "Subnet ID not found: #{subnet_id}"
117
+ end
118
+
119
+ raise
120
+ rescue Fog::Compute::AWS::Error => e
121
+ raise Errors::FogError, :message => e.message
122
+ rescue Excon::Errors::HTTPStatusError => e
123
+ raise Errors::InternalFogError,
124
+ :error => e.message,
125
+ :response => e.response.body
126
+ end
127
+
128
+ # Immediately save the ID since it is created at this point.
129
+ env[:machine].id = server.id
130
+
131
+ # Wait for the instance to be ready first
132
+ env[:metrics]["instance_ready_time"] = Util::Timer.time do
133
+ tries = region_config.instance_ready_timeout / 2
134
+
135
+ env[:ui].info(I18n.t("vagrant_aws.waiting_for_ready"))
136
+ begin
137
+ retryable(:on => Fog::Errors::TimeoutError, :tries => tries) do
138
+ # If we're interrupted don't worry about waiting
139
+ next if env[:interrupted]
140
+
141
+ # Wait for the server to be ready
142
+ server.wait_for(2, region_config.instance_check_interval) { ready? }
143
+ end
144
+ rescue Fog::Errors::TimeoutError
145
+ # Delete the instance
146
+ terminate(env)
147
+
148
+ # Notify the user
149
+ raise Errors::InstanceReadyTimeout,
150
+ timeout: region_config.instance_ready_timeout
151
+ end
152
+ end
153
+
154
+ @logger.info("Time to instance ready: #{env[:metrics]["instance_ready_time"]}")
155
+
156
+ # Allocate and associate an elastic IP if requested
157
+ if elastic_ip
158
+ domain = subnet_id ? 'vpc' : 'standard'
159
+ do_elastic_ip(env, domain, server, elastic_ip)
160
+ end
161
+
162
+ # Set the source destination checks
163
+ if !source_dest_check.nil?
164
+ if server.vpc_id.nil?
165
+ env[:ui].warn(I18n.t("vagrant_aws.source_dest_checks_no_vpc"))
166
+ else
167
+ begin
168
+ attrs = {
169
+ "SourceDestCheck.Value" => source_dest_check
170
+ }
171
+ env[:aws_compute].modify_instance_attribute(server.id, attrs)
172
+ rescue Fog::Compute::AWS::Error => e
173
+ raise Errors::FogError, :message => e.message
174
+ end
175
+ end
176
+ end
177
+
178
+ if !env[:interrupted]
179
+ env[:metrics]["instance_ssh_time"] = Util::Timer.time do
180
+ # Wait for SSH to be ready.
181
+ env[:ui].info(I18n.t("vagrant_aws.waiting_for_ssh"))
182
+ network_ready_retries = 0
183
+ network_ready_retries_max = 10
184
+ while true
185
+ # If we're interrupted then just back out
186
+ break if env[:interrupted]
187
+ # When an ec2 instance comes up, it's networking may not be ready
188
+ # by the time we connect.
189
+ begin
190
+ break if env[:machine].communicate.ready?
191
+ rescue Exception => e
192
+ if network_ready_retries < network_ready_retries_max then
193
+ network_ready_retries += 1
194
+ @logger.warn(I18n.t("vagrant_aws.waiting_for_ssh, retrying"))
195
+ else
196
+ raise e
197
+ end
198
+ end
199
+ sleep 2
200
+ end
201
+ end
202
+
203
+ @logger.info("Time for SSH ready: #{env[:metrics]["instance_ssh_time"]}")
204
+
205
+ # Ready and booted!
206
+ env[:ui].info(I18n.t("vagrant_aws.ready"))
207
+ end
208
+
209
+ # Terminate the instance if we were interrupted
210
+ terminate(env) if env[:interrupted]
211
+
212
+ @app.call(env)
213
+ end
214
+
215
+ def recover(env)
216
+ return if env["vagrant.error"].is_a?(Vagrant::Errors::VagrantError)
217
+
218
+ if env[:machine].provider.state.id != :not_created
219
+ # Undo the import
220
+ terminate(env)
221
+ end
222
+ end
223
+
224
+ def allows_ssh_port?(env, test_sec_groups, is_vpc)
225
+ port = 22 # TODO get ssh_info port
226
+ test_sec_groups = [ "default" ] if test_sec_groups.empty? # AWS default security group
227
+ # filter groups by name or group_id (vpc)
228
+ groups = test_sec_groups.map do |tsg|
229
+ env[:aws_compute].security_groups.all.select { |sg| tsg == (is_vpc ? sg.group_id : sg.name) }
230
+ end.flatten
231
+ # filter TCP rules
232
+ rules = groups.map { |sg| sg.ip_permissions.select { |r| r["ipProtocol"] == "tcp" } }.flatten
233
+ # test if any range includes port
234
+ !rules.select { |r| (r["fromPort"]..r["toPort"]).include?(port) }.empty?
235
+ end
236
+
237
+ def do_elastic_ip(env, domain, server, elastic_ip)
238
+ if elastic_ip =~ /\d+\.\d+\.\d+\.\d+/
239
+ begin
240
+ address = env[:aws_compute].addresses.get(elastic_ip)
241
+ rescue
242
+ handle_elastic_ip_error(env, "Could not retrieve Elastic IP: #{elastic_ip}")
243
+ end
244
+ if address.nil?
245
+ handle_elastic_ip_error(env, "Elastic IP not available: #{elastic_ip}")
246
+ end
247
+ @logger.debug("Public IP #{address.public_ip}")
248
+ else
249
+ begin
250
+ allocation = env[:aws_compute].allocate_address(domain)
251
+ rescue
252
+ handle_elastic_ip_error(env, "Could not allocate Elastic IP.")
253
+ end
254
+ @logger.debug("Public IP #{allocation.body['publicIp']}")
255
+ end
256
+
257
+ # Associate the address and save the metadata to a hash
258
+ h = nil
259
+ if domain == 'vpc'
260
+ # VPC requires an allocation ID to assign an IP
261
+ if address
262
+ association = env[:aws_compute].associate_address(server.id, nil, nil, address.allocation_id)
263
+ else
264
+ association = env[:aws_compute].associate_address(server.id, nil, nil, allocation.body['allocationId'])
265
+ # Only store release data for an allocated address
266
+ h = { :allocation_id => allocation.body['allocationId'], :association_id => association.body['associationId'], :public_ip => allocation.body['publicIp'] }
267
+ end
268
+ else
269
+ # Standard EC2 instances only need the allocated IP address
270
+ if address
271
+ association = env[:aws_compute].associate_address(server.id, address.public_ip)
272
+ else
273
+ association = env[:aws_compute].associate_address(server.id, allocation.body['publicIp'])
274
+ h = { :public_ip => allocation.body['publicIp'] }
275
+ end
276
+ end
277
+
278
+ unless association.body['return']
279
+ @logger.debug("Could not associate Elastic IP.")
280
+ terminate(env)
281
+ raise Errors::FogError,
282
+ :message => "Could not allocate Elastic IP."
283
+ end
284
+
285
+ # Save this IP to the data dir so it can be released when the instance is destroyed
286
+ if h
287
+ ip_file = env[:machine].data_dir.join('elastic_ip')
288
+ ip_file.open('w+') do |f|
289
+ f.write(h.to_json)
290
+ end
291
+ end
292
+ end
293
+
294
+ def handle_elastic_ip_error(env, message)
295
+ @logger.debug(message)
296
+ terminate(env)
297
+ raise Errors::FogError,
298
+ :message => message
299
+ end
300
+
301
+ def terminate(env)
302
+ destroy_env = env.dup
303
+ destroy_env.delete(:interrupted)
304
+ destroy_env[:config_validate] = false
305
+ destroy_env[:force_confirm_destroy] = true
306
+ env[:action_runner].run(Action.action_destroy, destroy_env)
307
+ end
308
+ end
309
+ end
310
+ end
311
+ end