Skip to content
This repository has been archived by the owner on Feb 11, 2022. It is now read-only.

Added support for config parameters elastic_ip => '<ip>', allocate_elast... #129

Open
wants to merge 22 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
9a86a91
Added support for config parameters elastic_ip => '<ip>', allocate_el…
Aug 27, 2013
aa8d3fe
Fixed terminate_instance issue and added config warning to locales
Aug 28, 2013
afa6b44
Create host directory when using SyncFolders if it doesn't exist
Mar 24, 2013
985a0f2
Fix typo in rsync and mkdir error messages
Mar 25, 2013
9c8c650
Change mkdir SyncFolders fix so that it is cross-platform
Mar 25, 2013
e8447ba
Catch `StandardError` instead of `Exception`
Mar 28, 2013
930b466
elastic IP address for VPC instances
May 2, 2013
833baa2
Added iam_instance_profile_arn and iam_instance_profile_name to confi…
jonathanq May 2, 2013
a3bf212
handle EIPs for both VPC and standard EC2 instances
May 6, 2013
77390c5
check for elastic_ip before warning about not having one
May 23, 2013
972696b
release elastic ip address upon termination
May 28, 2013
604a7a0
added elastic_ip entry in config spec
bwhaley Aug 12, 2013
85f1ab8
update changelog to reflect last merges
tralamazza Aug 29, 2013
cc23b13
Update CHANGELOG.md
tralamazza Aug 29, 2013
2ae6759
OCD
tralamazza Aug 29, 2013
be71c1e
create folder only if option :create is set
tralamazza Aug 29, 2013
b991da5
Update CHANGELOG.md
tralamazza Aug 29, 2013
b28684d
Update CHANGELOG.md
tralamazza Aug 29, 2013
f4f6162
catch leaked Excon errors from Fog
tralamazza Aug 29, 2013
ad593c8
closes #125 implements shutdown_behavior
tralamazza Aug 30, 2013
90196f5
Update CHANGELOG.md
tralamazza Aug 30, 2013
d0a6018
Fixed issue related to instanceInitiatedShutdownBehavior when ami roo…
Aug 31, 2013
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,13 @@
* Request confirmation on `vagrant destroy`, like normal VirtualBox + Vagrant.
* If user data is configured, output is shown on "vagrant up" that
it is being set.
* Add EIP support (GH #65)
* Add block device mapping support (GH #93)
* README improvements (GH #120)
* Fix missing locale message (GH #73)
* SyncFolders creates hostpath if it doesn't exist and `:create` option is set (GH #17)
* Add IAM Instance Profile support (GH #68)
* Add shutdown behavior support (GH #125)

# 0.2.2 (April 18, 2013)

Expand Down
2 changes: 1 addition & 1 deletion Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,5 @@ group :development do
# We depend on Vagrant for development, but we don't add it as a
# gem dependency because we expect to be installed within the
# Vagrant environment itself using `vagrant plugin`.
gem "vagrant", :git => "git://github.com/mitchellh/vagrant.git"
gem "vagrant", :git => "git://github.com/mitchellh/vagrant.git", :tag => "v1.2.7"
end
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,10 @@ This provider exposes quite a few provider-specific configuration options:
* `security_groups` - An array of security groups for the instance. If this
instance will be launched in VPC, this must be a list of security group
IDs.
* `iam_instance_profile_arn` - The Amazon resource name (ARN) of the IAM Instance
Profile to associate with the instance
* `iam_instance_profile_name` - The name of the IAM Instance Profile to associate
with the instance
* `subnet_id` - The subnet to boot the instance into, for VPC.
* `tags` - A hash of tags to set on the machine.
* `use_iam_profile` - If true, will use [IAM profiles](http://docs.aws.amazon.com/IAM/latest/UserGuide/instance-profiles.html)
Expand Down
2 changes: 1 addition & 1 deletion lib/vagrant-aws/action/read_ssh_info.rb
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ def read_ssh_info(aws, machine)

# Read the DNS info
return {
:host => server.dns_name || server.private_ip_address,
:host => server.public_ip_address || server.dns_name || server.private_ip_address,
:port => 22
}
end
Expand Down
60 changes: 58 additions & 2 deletions lib/vagrant-aws/action/run_instance.rb
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
require "log4r"
require 'json'

require 'vagrant/util/retryable'

Expand Down Expand Up @@ -35,14 +36,18 @@ def call(env)
tags = region_config.tags
user_data = region_config.user_data
block_device_mapping = region_config.block_device_mapping
elastic_ip = region_config.elastic_ip
shutdown_behavior = region_config.shutdown_behavior
iam_instance_profile_arn = region_config.iam_instance_profile_arn
iam_instance_profile_name = region_config.iam_instance_profile_name

# If there is no keypair then warn the user
if !keypair
env[:ui].warn(I18n.t("vagrant_aws.launch_no_keypair"))
end

# If there is a subnet ID then warn the user
if subnet_id
if subnet_id and !elastic_ip
env[:ui].warn(I18n.t("vagrant_aws.launch_vpc_warning"))
end

Expand All @@ -54,11 +59,15 @@ def call(env)
env[:ui].info(" -- Availability Zone: #{availability_zone}") if availability_zone
env[:ui].info(" -- Keypair: #{keypair}") if keypair
env[:ui].info(" -- Subnet ID: #{subnet_id}") if subnet_id
env[:ui].info(" -- IAM Instance Profile ARN: #{iam_instance_profile_arn}") if iam_instance_profile_arn
env[:ui].info(" -- IAM Instance Profile Name: #{iam_instance_profile_name}") if iam_instance_profile_name
env[:ui].info(" -- Private IP: #{private_ip_address}") if private_ip_address
env[:ui].info(" -- Elastic IP: #{elastic_ip}") if elastic_ip
env[:ui].info(" -- User Data: yes") if user_data
env[:ui].info(" -- Security Groups: #{security_groups.inspect}") if !security_groups.empty?
env[:ui].info(" -- User Data: #{user_data}") if user_data
env[:ui].info(" -- Block Device Mapping: #{block_device_mapping}") if block_device_mapping
env[:ui].info(" -- Shutdown behavior: #{shutdown_behavior}") if shutdown_behavior

begin
options = {
Expand All @@ -68,10 +77,15 @@ def call(env)
:key_name => keypair,
:private_ip_address => private_ip_address,
:subnet_id => subnet_id,
:iam_instance_profile_arn => iam_instance_profile_arn,
:iam_instance_profile_name => iam_instance_profile_name,
:tags => tags,
:user_data => user_data,
:block_device_mapping => block_device_mapping
:block_device_mapping => block_device_mapping,
}

options[:instance_initiated_shutdown_behavior] = shutdown_behavior if env[:aws_compute].images.get(ami).root_device_type == "ebs" or shutdown_be
havior == "terminate"

if !security_groups.empty?
security_group_key = options[:subnet_id].nil? ? :groups : :security_group_ids
Expand All @@ -90,6 +104,10 @@ def call(env)
raise
rescue Fog::Compute::AWS::Error => e
raise Errors::FogError, :message => e.message
rescue Excon::Errors::HTTPStatusError => e
raise Errors::InternalFogError,
:error => e.message,
:response => e.response.body
end

# Immediately save the ID since it is created at this point.
Expand Down Expand Up @@ -120,6 +138,12 @@ def call(env)

@logger.info("Time to instance ready: #{env[:metrics]["instance_ready_time"]}")

# Allocate and associate an elastic IP if requested
if elastic_ip
domain = subnet_id ? 'vpc' : 'standard'
do_elastic_ip(env, domain, server)
end

if !env[:interrupted]
env[:metrics]["instance_ssh_time"] = Util::Timer.time do
# Wait for SSH to be ready.
Expand Down Expand Up @@ -153,13 +177,45 @@ def recover(env)
end
end

def do_elastic_ip(env, domain, server)
allocation = env[:aws_compute].allocate_address(domain)
if allocation.body['publicIp'].nil?
@logger.debug("Could not allocate Elastic IP.")
return nil
end
@logger.debug("Public IP #{allocation.body['publicIp']}")

# Associate the address and save the metadata to a hash
if domain == 'vpc'
# VPC requires an allocation ID to assign an IP
association = env[:aws_compute].associate_address(server.id, nil, nil, allocation.body['allocationId'])
h = { :allocation_id => allocation.body['allocationId'], :association_id => association.body['associationId'], :public_ip => allocation.body['publicIp'] }
else
# Standard EC2 instances only need the allocated IP address
association = env[:aws_compute].associate_address(server.id, allocation.body['publicIp'])
h = { :public_ip => allocation.body['publicIp'] }
end

unless association.body['return']
@logger.debug("Could not associate Elastic IP.")
return nil
end

# Save this IP to the data dir so it can be released when the instance is destroyed
ip_file = env[:machine].data_dir.join('elastic_ip')
ip_file.open('w+') do |f|
f.write(h.to_json)
end
end

def terminate(env)
destroy_env = env.dup
destroy_env.delete(:interrupted)
destroy_env[:config_validate] = false
destroy_env[:force_confirm_destroy] = true
env[:action_runner].run(Action.action_destroy, destroy_env)
end

end
end
end
Expand Down
11 changes: 11 additions & 0 deletions lib/vagrant-aws/action/sync_folders.rb
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,17 @@ def call(env)
:hostpath => hostpath,
:guestpath => guestpath))

# Create the host path if it doesn't exist and option flag is set
if data[:create]
begin
FileUtils::mkdir_p(hostpath)
rescue => err
raise Errors::MkdirError,
:hostpath => hostpath,
:err => err
end
end

# Create the guest path
env[:machine].communicate.sudo("mkdir -p '#{guestpath}'")
env[:machine].communicate.sudo(
Expand Down
27 changes: 27 additions & 0 deletions lib/vagrant-aws/action/terminate_instance.rb
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
require "log4r"
require "json"

module VagrantPlugins
module AWS
Expand All @@ -16,10 +17,36 @@ def call(env)
# Destroy the server and remove the tracking ID
env[:ui].info(I18n.t("vagrant_aws.terminating"))
server.destroy
# Deallocate Elastic IP if allocated
elastic_ip = env[:aws_compute].describe_addresses('public-ip' => server.public_ip_address)
if !elastic_ip[:body]["addressesSet"].empty?
env[:aws_compute].disassociate_address(nil,elastic_ip[:body]["addressesSet"][0]["associationId"])
env[:ui].info(I18n.t("vagrant_aws.elastic_ip_deallocated"))
end
env[:machine].id = nil

# Release the elastic IP
ip_file = env[:machine].data_dir.join('elastic_ip')
if ip_file.file?
release_address(env,ip_file.read)
ip_file.delete
end

@app.call(env)
end

# Release an elastic IP address
def release_address(env,eip)
h = JSON.parse(eip)
# Use association_id and allocation_id for VPC, use public IP for EC2
if h['association_id']
env[:aws_compute].disassociate_address(nil,h['association_id'])
env[:aws_compute].release_address(h['allocation_id'])
else
env[:aws_compute].disassociate_address(h['public_ip'])
env[:aws_compute].release_address(h['public_ip'])
end
end
end
end
end
Expand Down
37 changes: 36 additions & 1 deletion lib/vagrant-aws/config.rb
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,11 @@ class Config < Vagrant.plugin("2", :config)
# @return [String]
attr_accessor :private_ip_address

# Acquire and attach an elastic IP address (VPC).
#
# @return [Boolean]
attr_accessor :elastic_ip

# The name of the AWS region in which to create the instance.
#
# @return [String]
Expand All @@ -65,6 +70,18 @@ class Config < Vagrant.plugin("2", :config)
# @return [Array<String>]
attr_accessor :security_groups

# The Amazon resource name (ARN) of the IAM Instance Profile
# to associate with the instance.
#
# @return [String]
attr_accessor :iam_instance_profile_arn

# The name of the IAM Instance Profile to associate with
# the instance.
#
# @return [String]
attr_accessor :iam_instance_profile_name

# The subnet ID to launch the machine into (VPC).
#
# @return [String]
Expand All @@ -88,6 +105,11 @@ class Config < Vagrant.plugin("2", :config)

attr_accessor :block_device_mapping

# Indicates whether an instance stops or terminates when you initiate shutdown from the instance
#
# @return [String]
attr_accessor :shutdown_behavior

def initialize(region_specific=false)
@access_key_id = UNSET_VALUE
@ami = UNSET_VALUE
Expand All @@ -106,6 +128,10 @@ def initialize(region_specific=false)
@user_data = UNSET_VALUE
@use_iam_profile = UNSET_VALUE
@block_device_mapping = {}
@elastic_ip = UNSET_VALUE
@iam_instance_profile_arn = UNSET_VALUE
@iam_instance_profile_name = UNSET_VALUE
@shutdown_behavior = UNSET_VALUE

# Internal state (prefix with __ so they aren't automatically
# merged)
Expand Down Expand Up @@ -196,6 +222,9 @@ def finalize!
# Default the private IP to nil since VPC is not default
@private_ip_address = nil if @private_ip_address == UNSET_VALUE

# Acquire an elastic IP if requested
@elastic_ip = nil if @elastic_ip == UNSET_VALUE

# Default region is us-east-1. This is sensible because AWS
# generally defaults to this as well.
@region = "us-east-1" if @region == UNSET_VALUE
Expand All @@ -209,12 +238,19 @@ def finalize!
# Subnet is nil by default otherwise we'd launch into VPC.
@subnet_id = nil if @subnet_id == UNSET_VALUE

# IAM Instance profile arn/name is nil by default.
@iam_instance_profile_arn = nil if @iam_instance_profile_arn == UNSET_VALUE
@iam_instance_profile_name = nil if @iam_instance_profile_name == UNSET_VALUE

# By default we don't use an IAM profile
@use_iam_profile = false if @use_iam_profile == UNSET_VALUE

# User Data is nil by default
@user_data = nil if @user_data == UNSET_VALUE

# default stop
@shutdown_behavior = "stop" if @shutdown_behavior == UNSET_VALUE

# Compile our region specific configurations only within
# NON-REGION-SPECIFIC configurations.
if !@__region_specific
Expand Down Expand Up @@ -256,7 +292,6 @@ def validate(machine)
errors << I18n.t("vagrant_aws.config.secret_access_key_required") if \
config.secret_access_key.nil?
end

errors << I18n.t("vagrant_aws.config.ami_required") if config.ami.nil?
end

Expand Down
8 changes: 8 additions & 0 deletions lib/vagrant-aws/errors.rb
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,21 @@ class FogError < VagrantAWSError
error_key(:fog_error)
end

class InternalFogError < VagrantAWSError
error_key(:internal_fog_error)
end

class InstanceReadyTimeout < VagrantAWSError
error_key(:instance_ready_timeout)
end

class RsyncError < VagrantAWSError
error_key(:rsync_error)
end

class MkdirError < VagrantAWSError
error_key(:mkdir_error)
end
end
end
end
20 changes: 19 additions & 1 deletion locales/en.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,10 @@ en:
Rsyncing folder: %{hostpath} => %{guestpath}
terminating: |-
Terminating the instance...
elastic_ip_allocated: |-
Allocated Elastic IP...
elastic_ip_deallocated: |-
Deallocated Elastic IP...
waiting_for_ready: |-
Waiting for instance to become "ready"...
waiting_for_ssh: |-
Expand All @@ -43,25 +47,39 @@ en:
A region must be specified via "region"
secret_access_key_required: |-
A secret access key is required via "secret_access_key"
allocate_elastic_ip: |-
Allocate_elastic_ip must be set to either "standard" or "vpc"

errors:
fog_error: |-
There was an error talking to AWS. The error message is shown
below:

%{message}
internal_fog_error: |-
There was an error talking to AWS. The error message is shown
below:

Error: %{error}
Response: %{response}
instance_ready_timeout: |-
The instance never became "ready" in AWS. The timeout currently
set waiting for the instance to become ready is %{timeout} seconds.
Please verify that the machine properly boots. If you need more time
set the `instance_ready_timeout` configuration on the AWS provider.
rsync_error: |-
There was an error when attemping to rsync a share folder.
There was an error when attempting to rsync a share folder.
Please inspect the error message below for more info.

Host path: %{hostpath}
Guest path: %{guestpath}
Error: %{stderr}
mkdir_error: |-
There was an error when attempting to create a shared host folder.
Please inspect the error message below for more info.

Host path: %{hostpath}
Error: %{err}

states:
short_not_created: |-
Expand Down
Loading