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.
- checksums.yaml +7 -0
- data/.gitignore +21 -0
- data/.rspec +1 -0
- data/CHANGELOG.md +96 -0
- data/Gemfile +14 -0
- data/LICENSE +8 -0
- data/README.md +328 -0
- data/Rakefile +22 -0
- data/cortex.yaml +15 -0
- data/dummy.box +0 -0
- data/example_box/README.md +13 -0
- data/example_box/metadata.json +3 -0
- data/lib/vagrant-aws/action/connect_aws.rb +51 -0
- data/lib/vagrant-aws/action/elb_deregister_instance.rb +24 -0
- data/lib/vagrant-aws/action/elb_register_instance.rb +24 -0
- data/lib/vagrant-aws/action/is_created.rb +18 -0
- data/lib/vagrant-aws/action/is_stopped.rb +18 -0
- data/lib/vagrant-aws/action/message_already_created.rb +16 -0
- data/lib/vagrant-aws/action/message_not_created.rb +16 -0
- data/lib/vagrant-aws/action/message_will_not_destroy.rb +16 -0
- data/lib/vagrant-aws/action/package_instance.rb +192 -0
- data/lib/vagrant-aws/action/read_ssh_info.rb +53 -0
- data/lib/vagrant-aws/action/read_state.rb +38 -0
- data/lib/vagrant-aws/action/run_instance.rb +311 -0
- data/lib/vagrant-aws/action/start_instance.rb +81 -0
- data/lib/vagrant-aws/action/stop_instance.rb +28 -0
- data/lib/vagrant-aws/action/terminate_instance.rb +51 -0
- data/lib/vagrant-aws/action/timed_provision.rb +21 -0
- data/lib/vagrant-aws/action/wait_for_state.rb +41 -0
- data/lib/vagrant-aws/action/warn_networks.rb +19 -0
- data/lib/vagrant-aws/action.rb +209 -0
- data/lib/vagrant-aws/cap/winrm_info.rb +46 -0
- data/lib/vagrant-aws/config.rb +577 -0
- data/lib/vagrant-aws/errors.rb +43 -0
- data/lib/vagrant-aws/plugin.rb +78 -0
- data/lib/vagrant-aws/provider.rb +50 -0
- data/lib/vagrant-aws/util/elb.rb +58 -0
- data/lib/vagrant-aws/util/timer.rb +17 -0
- data/lib/vagrant-aws/version.rb +5 -0
- data/lib/vagrant-aws.rb +18 -0
- data/locales/en.yml +159 -0
- data/spec/spec_helper.rb +1 -0
- data/spec/vagrant-aws/config_spec.rb +374 -0
- data/templates/metadata.json.erb +3 -0
- data/templates/vagrant-aws_package_Vagrantfile.erb +5 -0
- data/vagrant-aws-mscottford.gemspec +64 -0
- 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 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
|