vagrant-mos 0.8.39

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 +3 -0
  3. data/CHANGELOG.md +92 -0
  4. data/Gemfile +12 -0
  5. data/LICENSE +8 -0
  6. data/README.md +292 -0
  7. data/Rakefile +21 -0
  8. data/Vagrantfile +1 -0
  9. data/dummy.box +0 -0
  10. data/example_box/README.md +13 -0
  11. data/example_box/Vagrantfile +8 -0
  12. data/example_box/metadata.json +3 -0
  13. data/lib/vagrant-mos.rb +18 -0
  14. data/lib/vagrant-mos/action.rb +212 -0
  15. data/lib/vagrant-mos/action/connect_mos.rb +51 -0
  16. data/lib/vagrant-mos/action/elb_deregister_instance.rb +24 -0
  17. data/lib/vagrant-mos/action/elb_register_instance.rb +24 -0
  18. data/lib/vagrant-mos/action/is_created.rb +18 -0
  19. data/lib/vagrant-mos/action/is_stopped.rb +19 -0
  20. data/lib/vagrant-mos/action/message_already_created.rb +16 -0
  21. data/lib/vagrant-mos/action/message_not_created.rb +16 -0
  22. data/lib/vagrant-mos/action/message_will_not_destroy.rb +16 -0
  23. data/lib/vagrant-mos/action/package_instance.rb +192 -0
  24. data/lib/vagrant-mos/action/read_ssh_info.rb +64 -0
  25. data/lib/vagrant-mos/action/read_state.rb +46 -0
  26. data/lib/vagrant-mos/action/run_instance.rb +287 -0
  27. data/lib/vagrant-mos/action/start_instance.rb +82 -0
  28. data/lib/vagrant-mos/action/stop_instance.rb +29 -0
  29. data/lib/vagrant-mos/action/terminate_instance.rb +52 -0
  30. data/lib/vagrant-mos/action/timed_provision.rb +21 -0
  31. data/lib/vagrant-mos/action/wait_for_state.rb +41 -0
  32. data/lib/vagrant-mos/action/warn_networks.rb +19 -0
  33. data/lib/vagrant-mos/config.rb +405 -0
  34. data/lib/vagrant-mos/errors.rb +43 -0
  35. data/lib/vagrant-mos/plugin.rb +73 -0
  36. data/lib/vagrant-mos/provider.rb +53 -0
  37. data/lib/vagrant-mos/util/elb.rb +56 -0
  38. data/lib/vagrant-mos/util/timer.rb +17 -0
  39. data/lib/vagrant-mos/version.rb +5 -0
  40. data/locales/en.yml +153 -0
  41. data/mos.box +0 -0
  42. data/spec/spec_helper.rb +1 -0
  43. data/spec/vagrant-mos/config_spec.rb +225 -0
  44. data/templates/metadata.json.erb +3 -0
  45. data/templates/vagrant-aws_package_Vagrantfile.erb +5 -0
  46. data/vagrant-mos.gemspec +60 -0
  47. metadata +172 -0
@@ -0,0 +1,192 @@
1
+ require "log4r"
2
+ require 'vagrant/util/template_renderer'
3
+ require 'vagrant-mos/util/timer'
4
+ require 'vagrant/action/general/package'
5
+
6
+ module VagrantPlugins
7
+ module MOS
8
+ module Action
9
+ # This action packages a running mos-based server into an
10
+ # mos-based vagrant box. It does so by burning the associated
11
+ # vagrant-mos 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
+ @app = app
29
+ @logger = Log4r::Logger.new("vagrant_mos::action::package_instance")
30
+ env["package.include"] ||= []
31
+ env["package.output"] ||= "package.box"
32
+ end
33
+
34
+ alias_method :general_call, :call
35
+ def call(env)
36
+ # Initialize metrics if they haven't been
37
+ env[:metrics] ||= {}
38
+
39
+ # This block attempts to burn the server instance into an AMI
40
+ begin
41
+ # Get the Fog server object for given machine
42
+ server = env[:mos_compute].servers.get(env[:machine].id)
43
+
44
+ env[:ui].info(I18n.t("vagrant_mos.packaging_instance", :instance_id => server.id))
45
+
46
+ # Make the request to MOS to create an AMI from machine's instance
47
+ ami_response = server.service.create_image server.id, "#{server.tags["Name"]} Package - #{Time.now.strftime("%Y%m%d-%H%M%S")}", ""
48
+
49
+ # Find ami id
50
+ @ami_id = ami_response.data[:body]["imageId"]
51
+
52
+ # Attempt to burn the mos instance into an AMI within timeout
53
+ env[:metrics]["instance_ready_time"] = Util::Timer.time do
54
+
55
+ # Get the config, to set the ami burn timeout
56
+ region = env[:machine].provider_config.region
57
+ region_config = env[:machine].provider_config.get_region_config(region)
58
+ tries = region_config.instance_package_timeout / 2
59
+
60
+ env[:ui].info(I18n.t("vagrant_mos.burning_ami", :ami_id => @ami_id))
61
+
62
+ # Check the status of the AMI every 2 seconds until the ami burn timeout has been reached
63
+ begin
64
+ retryable(:on => Fog::Errors::TimeoutError, :tries => tries) do
65
+ # If we're interrupted don't worry about waiting
66
+ next if env[:interrupted]
67
+
68
+ # Need to update the ami_obj on each cycle
69
+ ami_obj = server.service.images.get(@ami_id)
70
+
71
+ # Wait for the server to be ready, raise error if timeout reached
72
+ server.wait_for(2) {
73
+ if ami_obj.state == "failed"
74
+ raise Errors::InstancePackageError,
75
+ ami_id: ami_obj.id,
76
+ err: ami_obj.state
77
+ return
78
+ else
79
+ # Successful AMI burn will result in true here
80
+ ami_obj.ready?
81
+ end
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_mos.packaging_instance_complete", :time_seconds => env[:metrics]["instance_ready_time"].to_i))
91
+ rescue Fog::Compute::MOS::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-mos_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 mos-vagrant templates
185
+ def template_root
186
+ MOS.source_root.join("templates")
187
+ end
188
+
189
+ end
190
+ end
191
+ end
192
+ end
@@ -0,0 +1,64 @@
1
+ require "log4r"
2
+
3
+ module VagrantPlugins
4
+ module MOS
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_mos::action::read_ssh_info")
12
+ end
13
+
14
+ def call(env)
15
+ env[:machine_ssh_info] = read_ssh_info(env[:mos_compute], env[:machine])
16
+
17
+ @app.call(env)
18
+ end
19
+
20
+ def read_ssh_info(mos, machine)
21
+ return nil if machine.id.nil?
22
+
23
+ # Find the machine
24
+ server = (mos.describe_instances([machine.id]))["Instance"]
25
+ #puts server
26
+ #server = mos.servers.get(machine.id)
27
+ if server.nil?
28
+ # The machine can't be found
29
+ @logger.info("Machine couldn't be found, assuming it got destroyed.")
30
+ machine.id = nil
31
+ return nil
32
+ end
33
+
34
+ # read attribute override
35
+ ssh_host_attribute = machine.provider_config.
36
+ get_region_config(machine.provider_config.region).ssh_host_attribute
37
+ # default host attributes to try. NOTE: Order matters!
38
+ ssh_attrs = [:public_ip_address, :dns_name, :private_ip_address]
39
+ ssh_attrs = (Array(ssh_host_attribute) + ssh_attrs).uniq if ssh_host_attribute
40
+ # try each attribute, get out on first value
41
+ host_value = nil
42
+
43
+ while !host_value and attr_name = ssh_attrs.shift
44
+ begin
45
+ host_value = server.send(attr_name)
46
+ rescue NoMethodError
47
+ @logger.info("SSH host attribute not found #{attr_name}")
48
+ end
49
+ end
50
+ #puts 2
51
+
52
+ if !host_value
53
+ host_value = server["ipAddresses"]
54
+ end
55
+ #puts server
56
+ #puts server["ipAddresses"]
57
+ #puts host_value
58
+ #puts 3
59
+ return {:host => host_value, :port => 22}
60
+ end
61
+ end
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,46 @@
1
+ require "log4r"
2
+
3
+ module VagrantPlugins
4
+ module MOS
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_mos::action::read_state")
12
+ end
13
+
14
+ def call(env)
15
+ env[:machine_state_id] = read_state(env[:mos_compute], env[:machine])
16
+
17
+ @app.call(env)
18
+ end
19
+
20
+ def read_state(mos, machine)
21
+ return :not_created if machine.id.nil?
22
+
23
+ # Find the machine
24
+ #puts mos
25
+ #puts machine.id
26
+ server = (mos.describe_instances([machine.id]))["Instance"]
27
+ #puts "read_state"
28
+ #puts server
29
+ #puts "finish read_state"
30
+ #server = mos.servers.get(machine.id)
31
+ if server.nil? || [:"shutting-down", :terminated].include?(server["status"])
32
+ # The machine can't be found
33
+ @logger.info("Machine not found or terminated, assuming it got destroyed.")
34
+ machine.id = nil
35
+ return :not_created
36
+ end
37
+
38
+ # Return the state
39
+ puts server["status"]
40
+ return server["status"]
41
+
42
+ end
43
+ end
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,287 @@
1
+ require "log4r"
2
+ require 'json'
3
+
4
+ require 'vagrant/util/retryable'
5
+
6
+ require 'vagrant-mos/util/timer'
7
+
8
+ module VagrantPlugins
9
+ module MOS
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_mos::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
+ associate_public_ip = region_config.associate_public_ip
46
+
47
+ # If there is no keypair then warn the user
48
+ if !keypair
49
+ env[:ui].warn(I18n.t("vagrant_mos.launch_no_keypair"))
50
+ end
51
+
52
+ # If there is a subnet ID then warn the user
53
+ if subnet_id && !elastic_ip
54
+ env[:ui].warn(I18n.t("vagrant_mos.launch_vpc_warning"))
55
+ end
56
+
57
+ # Launch!
58
+ env[:ui].info(I18n.t("vagrant_mos.launching_instance"))
59
+ env[:ui].info(" -- Type: #{instance_type}")
60
+ env[:ui].info(" -- AMI: #{ami}")
61
+ env[:ui].info(" -- Region: #{region}")
62
+ env[:ui].info(" -- Availability Zone: #{availability_zone}") if availability_zone
63
+ env[:ui].info(" -- Keypair: #{keypair}") if keypair
64
+ env[:ui].info(" -- Subnet ID: #{subnet_id}") if subnet_id
65
+ env[:ui].info(" -- IAM Instance Profile ARN: #{iam_instance_profile_arn}") if iam_instance_profile_arn
66
+ env[:ui].info(" -- IAM Instance Profile Name: #{iam_instance_profile_name}") if iam_instance_profile_name
67
+ env[:ui].info(" -- Private IP: #{private_ip_address}") if private_ip_address
68
+ env[:ui].info(" -- Elastic IP: #{elastic_ip}") if elastic_ip
69
+ env[:ui].info(" -- User Data: yes") if user_data
70
+ env[:ui].info(" -- Security Groups: #{security_groups.inspect}") if !security_groups.empty?
71
+ env[:ui].info(" -- User Data: #{user_data}") if user_data
72
+ env[:ui].info(" -- Block Device Mapping: #{block_device_mapping}") if block_device_mapping
73
+ env[:ui].info(" -- Terminate On Shutdown: #{terminate_on_shutdown}")
74
+ env[:ui].info(" -- Monitoring: #{monitoring}")
75
+ env[:ui].info(" -- EBS optimized: #{ebs_optimized}")
76
+ env[:ui].info(" -- Assigning a public IP address in a VPC: #{associate_public_ip}")
77
+
78
+ options = {
79
+ :availability_zone => availability_zone,
80
+ :flavor_id => instance_type,
81
+ :image_id => ami,
82
+ :key_name => keypair,
83
+ :private_ip_address => private_ip_address,
84
+ :subnet_id => subnet_id,
85
+ :iam_instance_profile_arn => iam_instance_profile_arn,
86
+ :iam_instance_profile_name => iam_instance_profile_name,
87
+ :tags => tags,
88
+ :user_data => user_data,
89
+ :block_device_mapping => block_device_mapping,
90
+ :instance_initiated_shutdown_behavior => terminate_on_shutdown == true ? "terminate" : nil,
91
+ :monitoring => monitoring,
92
+ :ebs_optimized => ebs_optimized,
93
+ :associate_public_ip => associate_public_ip
94
+ }
95
+ if !security_groups.empty?
96
+ security_group_key = options[:subnet_id].nil? ? :groups : :security_group_ids
97
+ options[security_group_key] = security_groups
98
+ env[:ui].warn(I18n.t("vagrant_mos.warn_ssh_access")) unless allows_ssh_port?(env, security_groups, subnet_id)
99
+ end
100
+
101
+ begin
102
+ # todo
103
+ #puts options
104
+ #puts options['image_id']
105
+ #puts options[:flavor_id]
106
+ #puts options["key_name"]
107
+ server = env[:mos_compute].create_instance(options[:image_id], options[:flavor_id], nil, nil, options[:key_name], datadisk=9, bandwidth=2)
108
+ #server = env[:mos_compute].create_instance("320bbeb9-788f-4e7b-86af-7ea377b6a99e", "C2_M2", nil, nil, nil, datadisk=9, bandwidth=2)
109
+ #server = env[:mos_compute].servers.create(options)
110
+ #puts server
111
+ rescue Exception => e
112
+ raise Errors::FogError, :message
113
+ =begin
114
+ rescue Fog::Compute::MOS::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::MOS::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
+
130
+ =end
131
+ end
132
+
133
+ # Immediately save the ID since it is created at this point.
134
+ env[:machine].id = server['instanceId']
135
+
136
+ # Wait for the instance to be ready first
137
+ env[:metrics]["instance_ready_time"] = Util::Timer.time do
138
+ tries = region_config.instance_ready_timeout / 2
139
+
140
+ env[:ui].info(I18n.t("vagrant_mos.waiting_for_ready"))
141
+ begin
142
+ retryable(:on => Fog::Errors::TimeoutError, :tries => tries) do
143
+ # If we're interrupted don't worry about waiting
144
+ next if env[:interrupted]
145
+
146
+ # Wait for the server to be ready
147
+ #server.wait_for(2, 5) { ready? }
148
+ end
149
+ rescue Fog::Errors::TimeoutError
150
+ # Delete the instance
151
+ terminate(env)
152
+
153
+ # Notify the user
154
+ raise Errors::InstanceReadyTimeout,
155
+ timeout: region_config.instance_ready_timeout
156
+ end
157
+ end
158
+
159
+ @logger.info("Time to instance ready: #{env[:metrics]["instance_ready_time"]}")
160
+
161
+ # Allocate and associate an elastic IP if requested
162
+ if elastic_ip
163
+ domain = subnet_id ? 'vpc' : 'standard'
164
+ do_elastic_ip(env, domain, server, elastic_ip)
165
+ end
166
+
167
+ if !env[:interrupted]
168
+ env[:metrics]["instance_ssh_time"] = Util::Timer.time do
169
+ # Wait for SSH to be ready.
170
+ env[:ui].info(I18n.t("vagrant_mos.waiting_for_ssh"))
171
+ while true
172
+ # If we're interrupted then just back out
173
+ break if env[:interrupted]
174
+ break if env[:machine].communicate.ready?
175
+ sleep 2
176
+ end
177
+ end
178
+
179
+ @logger.info("Time for SSH ready: #{env[:metrics]["instance_ssh_time"]}")
180
+
181
+ # Ready and booted!
182
+ env[:ui].info(I18n.t("vagrant_mos.ready"))
183
+ end
184
+
185
+ # Terminate the instance if we were interrupted
186
+ terminate(env) if env[:interrupted]
187
+
188
+ @app.call(env)
189
+ end
190
+
191
+ def recover(env)
192
+ return if env["vagrant.error"].is_a?(Vagrant::Errors::VagrantError)
193
+
194
+ if env[:machine].provider.state.id != :not_created
195
+ # Undo the import
196
+ terminate(env)
197
+ end
198
+ end
199
+
200
+ def allows_ssh_port?(env, test_sec_groups, is_vpc)
201
+ port = 22 # TODO get ssh_info port
202
+ test_sec_groups = ["default"] if test_sec_groups.empty? # MOS default security group
203
+ # filter groups by name or group_id (vpc)
204
+ groups = test_sec_groups.map do |tsg|
205
+ env[:mos_compute].security_groups.all.select { |sg| tsg == (is_vpc ? sg.group_id : sg.name) }
206
+ end.flatten
207
+ # filter TCP rules
208
+ rules = groups.map { |sg| sg.ip_permissions.select { |r| r["ipProtocol"] == "tcp" } }.flatten
209
+ # test if any range includes port
210
+ !rules.select { |r| (r["fromPort"]..r["toPort"]).include?(port) }.empty?
211
+ end
212
+
213
+ def do_elastic_ip(env, domain, server, elastic_ip)
214
+ if elastic_ip =~ /\d+\.\d+\.\d+\.\d+/
215
+ begin
216
+ address = env[:mos_compute].addresses.get(elastic_ip)
217
+ rescue
218
+ handle_elastic_ip_error(env, "Could not retrieve Elastic IP: #{elastic_ip}")
219
+ end
220
+ if address.nil?
221
+ handle_elastic_ip_error(env, "Elastic IP not available: #{elastic_ip}")
222
+ end
223
+ @logger.debug("Public IP #{address.public_ip}")
224
+ else
225
+ begin
226
+ allocation = env[:mos_compute].allocate_address(domain)
227
+ rescue
228
+ handle_elastic_ip_error(env, "Could not allocate Elastic IP.")
229
+ end
230
+ @logger.debug("Public IP #{allocation.body['publicIp']}")
231
+ end
232
+
233
+ # Associate the address and save the metadata to a hash
234
+ h = nil
235
+ if domain == 'vpc'
236
+ # VPC requires an allocation ID to assign an IP
237
+ if address
238
+ association = env[:mos_compute].associate_address(server.id, nil, nil, address.allocation_id)
239
+ else
240
+ association = env[:mos_compute].associate_address(server.id, nil, nil, allocation.body['allocationId'])
241
+ # Only store release data for an allocated address
242
+ h = {:allocation_id => allocation.body['allocationId'], :association_id => association.body['associationId'], :public_ip => allocation.body['publicIp']}
243
+ end
244
+ else
245
+ # Standard EC2 instances only need the allocated IP address
246
+ if address
247
+ association = env[:mos_compute].associate_address(server.id, address.public_ip)
248
+ else
249
+ association = env[:mos_compute].associate_address(server.id, allocation.body['publicIp'])
250
+ h = {:public_ip => allocation.body['publicIp']}
251
+ end
252
+ end
253
+
254
+ unless association.body['return']
255
+ @logger.debug("Could not associate Elastic IP.")
256
+ terminate(env)
257
+ raise Errors::FogError,
258
+ :message => "Could not allocate Elastic IP."
259
+ end
260
+
261
+ # Save this IP to the data dir so it can be released when the instance is destroyed
262
+ if h
263
+ ip_file = env[:machine].data_dir.join('elastic_ip')
264
+ ip_file.open('w+') do |f|
265
+ f.write(h.to_json)
266
+ end
267
+ end
268
+ end
269
+
270
+ def handle_elastic_ip_error(env, message)
271
+ @logger.debug(message)
272
+ terminate(env)
273
+ raise Errors::FogError,
274
+ :message => message
275
+ end
276
+
277
+ def terminate(env)
278
+ destroy_env = env.dup
279
+ destroy_env.delete(:interrupted)
280
+ destroy_env[:config_validate] = false
281
+ destroy_env[:force_confirm_destroy] = true
282
+ env[:action_runner].run(Action.action_destroy, destroy_env)
283
+ end
284
+ end
285
+ end
286
+ end
287
+ end