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

aws configure does not always prepend 'profile' #523

Open
wants to merge 15 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
19 changes: 16 additions & 3 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,19 @@
language: ruby

sudo: false

cache: bundler

rvm:
- ruby-head
- 2.3
- 2.2
- 2.2.7
- 2.3.4

deploy:
provider: rubygems
api_key:
secure: A1AYezvoon6JdYHphtrUEeG98r7AtFqsPcETp4BX3sPlSvOghHqco57/JasxFBFbis1irfVxhjqIFaS0lfOO0yxfIJQFnbZ6tTzKJB7IkSWUMuVfGo6qRxnO4vnuodx9Vhh27A7lovuptMCQBDdHXoSPUjgqHacrEzYKLDOarHercnRNRT9mWYR//qFO4RHtQz7hG+6p9tUvGmSeUkXB6AZwDPycPAJgrCd6bRYwlTmID6Jlq07bjcqfTTN+jZzdC22nhgjw2Cao0skcszO3H71DZYeFhoRIA8sMKXnNt7LGtvA2FJFM65bJj3TLVVpDe5Itn/KpSLSwuE9phPpVhv828S98h8pbLVYrhat+2jvNOqwtVw9C0LC9GpPlBcd/8AYuDwEO5a+hdnZ08JnihzqSvQd2XX9XvBiKy/A5mBc5D9bYJ3YXsy6zXToMM0nP8Xr0z3NCEFWtJ3ueGzHVmsZqwdoiH/2g6syx4k1YBHb9zdeGciMdk1vmckPYZJLpGh4cj4bDeIHaGPaZ2tYiBSaQNN+6YEMShQgl1d5k5Du6lHdUKnYR4Z7lvQkjfU73lkDoA9PvZuR7b7yi44stEvsxTpNJCZUZN0JmlXAsGlS3hDpD8/LcJWI6M0y7tBPQ3gEB/3YSAYsAcqJZNZ3wyTuqLPZBPJFr6Prwq3VoMhU=
gem: vagrant-aws-iam-decoder
on:
repo: iam-decoder/vagrant-aws
branch: iam-decoder
rvm: 2.3.4
6 changes: 2 additions & 4 deletions Gemfile
Original file line number Diff line number Diff line change
@@ -1,14 +1,12 @@
source "https://rubygems.org"

gemspec

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
# gem dependency because we expect it to be installed within the
# Vagrant environment itself using `vagrant plugin`.
gem "vagrant", :git => "https://github.com/mitchellh/vagrant.git"
end

group :plugins do
gem "vagrant-aws" , path: "."
gemspec
end
22 changes: 11 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,13 +1,7 @@
# Vagrant AWS Provider
[![Gitter](https://badges.gitter.im/Join Chat.svg)](https://gitter.im/mitchellh/vagrant-aws?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)

<span class="badges">
[![Gem Version](https://badge.fury.io/rb/vagrant-aws.png)][gem]
[![Dependency Status](https://gemnasium.com/mitchellh/vagrant-aws.png)][gemnasium]
</span>

[gem]: https://rubygems.org/gems/vagrant-aws
[gemnasium]: https://gemnasium.com/mitchellh/vagrant-aws
[![Build Status](https://travis-ci.org/iam-decoder/vagrant-aws.svg?branch=iam-decoder)](https://travis-ci.org/iam-decoder/vagrant-aws)
[![Gem Version](https://badge.fury.io/rb/vagrant-aws-iam-decoder.svg)](https://badge.fury.io/rb/vagrant-aws-iam-decoder)

This is a [Vagrant](http://www.vagrantup.com) 1.2+ plugin that adds an [AWS](http://aws.amazon.com)
provider to Vagrant, allowing Vagrant to control and provision machines in
Expand All @@ -24,6 +18,7 @@ EC2 and VPC.
* Define region-specific configurations so Vagrant can manage machines
in multiple regions.
* Package running instances into new vagrant-aws friendly boxes
* Spot Instance Support

## Usage

Expand All @@ -32,7 +27,7 @@ installing, `vagrant up` and specify the `aws` provider. An example is
shown below.

```
$ vagrant plugin install vagrant-aws
$ vagrant plugin install vagrant-aws-iam-decoder
...
$ vagrant up --provider=aws
...
Expand Down Expand Up @@ -137,7 +132,7 @@ This provider exposes quite a few provider-specific configuration options:
* `session_token` - The session token provided by STS
* `private_ip_address` - The private IP address to assign to an instance
within a [VPC](http://aws.amazon.com/vpc/)
* `elastic_ip` - Can be set to 'true', or to an existing Elastic IP address.
* `elastic_ip` - Can be set to 'true', or to an existing Elastic IP address.
If true, allocate a new Elastic IP address to the instance. If set
to an existing Elastic IP address, assign the address to the instance.
* `region` - The region to start the instance in, such as "us-east-1"
Expand Down Expand Up @@ -170,6 +165,11 @@ This provider exposes quite a few provider-specific configuration options:
when you initiate shutdown from the instance.
* `endpoint` - The endpoint URL for connecting to AWS (or an AWS-like service). Only required for non AWS clouds, such as [eucalyptus](https://github.com/eucalyptus/eucalyptus/wiki).

* `spot_instance` - Boolean value; indicates whether the config is for a spot instance, or on-demand. For more information about spot instances, see the [AWS Documentation](http://docs.aws.amazon.com/AWSEC2/latest/UserGuide/how-spot-instances-work.html)
* `spot_max_price` - Decimal value; state the maximum bid for your spot instance. If nil, it will compute average price in `region` for selected `instance_type`.
* `spot_price_product_description` - The product description for the spot price history used to compute average price. Defaults to 'Linux/UNIX'.
* `spot_valid_until` - Timestamp; when this spot instance request should expire, destroying any related instances. Ignored if `spot_instance` is not true.

These can be set like typical provider-specific configuration:

```ruby
Expand Down Expand Up @@ -316,7 +316,7 @@ $ bundle exec rake
If those pass, you're ready to start developing the plugin. You can test
the plugin without installing it into your Vagrant environment by just
creating a `Vagrantfile` in the top level of this directory (it is gitignored)
and add the following line to your `Vagrantfile`
and add the following line to your `Vagrantfile`
```ruby
Vagrant.require_plugin "vagrant-aws"
```
Expand Down
File renamed without changes.
87 changes: 86 additions & 1 deletion lib/vagrant-aws/action/run_instance.rb
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,11 @@ def call(env)
end

begin
server = env[:aws_compute].servers.create(options)
server = if region_config.spot_instance
server_from_spot_request(env, region_config, options)
else
env[:aws_compute].servers.create(options)
end
rescue Fog::Compute::AWS::NotFound => e
# Invalid subnet doesn't have its own error so we catch and
# check the error message here.
Expand Down Expand Up @@ -212,6 +216,87 @@ def call(env)
@app.call(env)
end

# returns a fog server or nil
def server_from_spot_request(env, config, options)
if config.spot_max_price.nil?
spot_price_current = env[:aws_compute].describe_spot_price_history({
'StartTime' => Time.now.iso8601,
'EndTime' => Time.now.iso8601,
'InstanceType' => [config.instance_type],
'ProductDescription' => [config.spot_price_product_description.nil? ? 'Linux/UNIX' : config.spot_price_product_description]
})

spot_price_current.body['spotPriceHistorySet'].each do |set|
(@price_set ||= []) << set['spotPrice'].to_f
end

if @price_set.nil?
raise Errors::FogError,
:message => "Could not find any history spot prices."
end

avg_price = @price_set.inject(0.0) { |sum, el| sum + el } / @price_set.size

# make the bid 10% higher than the average
price = (avg_price * 1.1).round(4)
else
price = config.spot_max_price
end

options.merge!({
:price => price,
:valid_until => config.spot_valid_until
})

env[:ui].info(I18n.t("vagrant_aws.launching_spot_instance"))
env[:ui].info(" -- Price: #{price}")
env[:ui].info(" -- Valid until: #{config.spot_valid_until}") if config.spot_valid_until

# create the spot instance
spot_req = env[:aws_compute].spot_requests.create(options)

@logger.info("Spot request ID: #{spot_req.id}")

# initialize state
status_code = ""
while true
sleep 5

spot_req.reload()

# display something whenever the status code changes
if status_code != spot_req.state
env[:ui].info(spot_req.fault)
status_code = spot_req.state
end
spot_state = spot_req.state.to_sym
case spot_state
when :not_created, :open
@logger.debug("Spot request #{spot_state} #{status_code}, waiting")
when :active
break; # :)
when :closed, :cancelled, :failed
msg = "Spot request #{spot_state} #{status_code}, aborting"
@logger.error(msg)
raise Errors::FogError, :message => msg
else
@logger.debug("Unknown spot state #{spot_state} #{status_code}, waiting")
end
end
# cancel the spot request but let the server go thru
spot_req.destroy()

server = env[:aws_compute].servers.get(spot_req.instance_id)

# Spot Instances don't support tagging arguments on creation
# Retrospectively tag the server to handle this
if !config.tags.empty?
env[:aws_compute].create_tags(server.identity, config.tags)
end

server
end

def recover(env)
return if env["vagrant.error"].is_a?(Vagrant::Errors::VagrantError)

Expand Down
53 changes: 45 additions & 8 deletions lib/vagrant-aws/config.rb
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,26 @@ class Config < Vagrant.plugin("2", :config)
# @return [String]
attr_accessor :aws_profile

# Launch as spot instance
#
# @return [Boolean]
attr_accessor :spot_instance

# Spot request max price
#
# @return [String]
attr_accessor :spot_max_price

# Spot request validity
#
# @return [Time]
attr_accessor :spot_valid_until

# The product description for the spot price history
#
# @return [String]
attr_accessor :spot_price_product_description

def initialize(region_specific=false)
@access_key_id = UNSET_VALUE
@ami = UNSET_VALUE
Expand Down Expand Up @@ -233,6 +253,9 @@ def initialize(region_specific=false)
@tenancy = UNSET_VALUE
@aws_dir = UNSET_VALUE
@aws_profile = UNSET_VALUE
@spot_instance = UNSET_VALUE
@spot_max_price = UNSET_VALUE
@spot_valid_until = UNSET_VALUE

# Internal state (prefix with __ so they aren't automatically
# merged)
Expand Down Expand Up @@ -323,8 +346,8 @@ def finalize!
if @access_key_id == UNSET_VALUE or @secret_access_key == UNSET_VALUE
@aws_profile = 'default' if @aws_profile == UNSET_VALUE
@aws_dir = ENV['HOME'].to_s + '/.aws/' if @aws_dir == UNSET_VALUE
@region, @access_key_id, @secret_access_key, @session_token = Credentials.new.get_aws_info(@aws_profile, @aws_dir)
@region = UNSET_VALUE if @region.nil?
@aws_region, @access_key_id, @secret_access_key, @session_token = Credentials.new.get_aws_info(@aws_profile, @aws_dir)
@region = @aws_region if @region == UNSET_VALUE and !@aws_region.nil?
else
@aws_profile = nil
@aws_dir = nil
Expand Down Expand Up @@ -409,6 +432,18 @@ def finalize!
# default to nil
@kernel_id = nil if @kernel_id == UNSET_VALUE

# By default don't use spot requests
@spot_instance = false if @spot_instance == UNSET_VALUE

# default to nil
@spot_max_price = nil if @spot_max_price == UNSET_VALUE

# Default: Request is effective indefinitely.
@spot_valid_until = nil if @spot_valid_until == UNSET_VALUE

# default to nil
@spot_price_product_description = nil if @spot_price_product_description == UNSET_VALUE

# Compile our region specific configurations only within
# NON-REGION-SPECIFIC configurations.
if !@__region_specific
Expand Down Expand Up @@ -479,14 +514,14 @@ def get_region_config(name)


class Credentials < Vagrant.plugin("2", :config)
# This module reads AWS config and credentials.
# This module reads AWS config and credentials.
# Behaviour aims to mimic what is described in AWS documentation:
# http://docs.aws.amazon.com/cli/latest/userguide/cli-chap-getting-started.html
# http://docs.aws.amazon.com/cli/latest/topic/config-vars.html
# Which is the following (stopping at the first successful case):
# 1) read config and credentials from environment variables
# 2) read config and credentials from files at location defined by environment variables
# 3) read config and credentials from files at default location
# 3) read config and credentials from files at default location
#
# The mandatory fields for a successful "get credentials" are the id and the secret keys.
# Region is not required since Config#finalize falls back to sensible defaults.
Expand Down Expand Up @@ -525,15 +560,17 @@ def get_aws_info(profile, location)
private

def read_aws_files(profile, aws_config, aws_creds)
# get info from config ini file for selected profile
data = File.read(aws_config)
doc_cfg = IniParse.parse(data)

# determine section in config ini file
if profile == 'default'
if profile == 'default' || !doc_cfg[profile].nil?
ini_profile = profile
else
ini_profile = 'profile ' + profile
end
# get info from config ini file for selected profile
data = File.read(aws_config)
doc_cfg = IniParse.parse(data)

aws_region = doc_cfg[ini_profile]['region']

# determine section in credentials ini file
Expand Down
4 changes: 3 additions & 1 deletion locales/en.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ en:

launching_instance: |-
Launching an instance with the following settings...
launching_spot_instance: |-
Launching a spot request instance with the following settings...
launch_no_keypair: |-
Warning! You didn't specify a keypair to launch your instance with.
This can sometimes result in not being able to access your instance.
Expand Down Expand Up @@ -104,7 +106,7 @@ en:
Error: %{err}
instance_package_timeout: |-
The AMI failed to become "ready" in AWS. The timeout currently
set waiting for the instance to become ready is %{timeout} seconds. For
set waiting for the instance to become ready is %{timeout} seconds. For
larger instances AMI burning may take long periods of time. Please
ensure the timeout is set high enough, it can be changed by adjusting
the `instance_package_timeout` configuration on the AWS provider.
Expand Down
21 changes: 21 additions & 0 deletions spec/vagrant-aws/config_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,10 @@
its("associate_public_ip") { should == false }
its("unregister_elb_from_az") { should == true }
its("tenancy") { should == "default" }
its("spot_instance") { should == false }
its("spot_max_price") { should be_nil }
its("spot_price_product_description") { should be_nil }
its("spot_valid_until") { should be_nil }
end

describe "overriding defaults" do
Expand Down Expand Up @@ -219,6 +223,23 @@
its("access_key_id") { should == "AKIdefault" }
its("secret_access_key") { should == "PASSdefault" }
its("session_token") { should be_nil }
its("region") { should == "eu-west-1" }
end

context "with default profile and overriding region" do
subject do
allow(File).to receive(:exist?).and_return(true)
allow(File).to receive(:read).with(filename_cfg).and_return(data_cfg)
allow(File).to receive(:read).with(filename_keys).and_return(data_keys)
instance.region = "eu-central-1"
instance.tap do |o|
o.finalize!
end
end
its("access_key_id") { should == "AKIdefault" }
its("secret_access_key") { should == "PASSdefault" }
its("session_token") { should be_nil }
its("region") { should == "eu-central-1" }
end

context "without any credential environment variables and chosing a profile" do
Expand Down
6 changes: 4 additions & 2 deletions vagrant-aws.gemspec → vagrant-aws-iam-decoder.gemspec
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,15 @@ $:.unshift File.expand_path("../lib", __FILE__)
require "vagrant-aws/version"

Gem::Specification.new do |s|
s.name = "vagrant-aws"
s.name = "vagrant-aws-iam-decoder"
s.version = VagrantPlugins::AWS::VERSION
# http://guides.rubygems.org/patterns/#prerelease-gems
s.version = "#{s.version}.pre.#{ENV['TRAVIS_BUILD_NUMBER']}" if ENV['TRAVIS']
s.platform = Gem::Platform::RUBY
s.license = "MIT"
s.authors = "Mitchell Hashimoto"
s.email = "[email protected]"
s.homepage = "http://www.vagrantup.com"
s.homepage = "https://github.com/iam-decoder/vagrant-aws"
s.summary = "Enables Vagrant to manage machines in EC2 and VPC."
s.description = "Enables Vagrant to manage machines in EC2 and VPC."

Expand Down