vagrant-aws-iam-decoder 0.7.2

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 (46) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +21 -0
  3. data/.rspec +1 -0
  4. data/.travis.yml +19 -0
  5. data/CHANGELOG.md +96 -0
  6. data/Gemfile +12 -0
  7. data/LICENSE +8 -0
  8. data/README.md +326 -0
  9. data/Rakefile +22 -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-iam-decoder.rb +18 -0
  14. data/lib/vagrant-aws/action.rb +210 -0
  15. data/lib/vagrant-aws/action/connect_aws.rb +48 -0
  16. data/lib/vagrant-aws/action/elb_deregister_instance.rb +24 -0
  17. data/lib/vagrant-aws/action/elb_register_instance.rb +24 -0
  18. data/lib/vagrant-aws/action/is_created.rb +18 -0
  19. data/lib/vagrant-aws/action/is_stopped.rb +18 -0
  20. data/lib/vagrant-aws/action/message_already_created.rb +16 -0
  21. data/lib/vagrant-aws/action/message_not_created.rb +16 -0
  22. data/lib/vagrant-aws/action/message_will_not_destroy.rb +16 -0
  23. data/lib/vagrant-aws/action/package_instance.rb +192 -0
  24. data/lib/vagrant-aws/action/read_ssh_info.rb +53 -0
  25. data/lib/vagrant-aws/action/read_state.rb +38 -0
  26. data/lib/vagrant-aws/action/run_instance.rb +396 -0
  27. data/lib/vagrant-aws/action/start_instance.rb +81 -0
  28. data/lib/vagrant-aws/action/stop_instance.rb +28 -0
  29. data/lib/vagrant-aws/action/terminate_instance.rb +51 -0
  30. data/lib/vagrant-aws/action/timed_provision.rb +21 -0
  31. data/lib/vagrant-aws/action/wait_for_state.rb +41 -0
  32. data/lib/vagrant-aws/action/warn_networks.rb +19 -0
  33. data/lib/vagrant-aws/config.rb +601 -0
  34. data/lib/vagrant-aws/errors.rb +43 -0
  35. data/lib/vagrant-aws/plugin.rb +73 -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/locales/en.yml +161 -0
  41. data/spec/spec_helper.rb +1 -0
  42. data/spec/vagrant-aws/config_spec.rb +395 -0
  43. data/templates/metadata.json.erb +3 -0
  44. data/templates/vagrant-aws_package_Vagrantfile.erb +5 -0
  45. data/vagrant-aws-iam-decoder.gemspec +62 -0
  46. metadata +163 -0
@@ -0,0 +1,81 @@
1
+ require "log4r"
2
+
3
+ require 'vagrant/util/retryable'
4
+
5
+ require 'vagrant-aws/util/timer'
6
+
7
+ module VagrantPlugins
8
+ module AWS
9
+ module Action
10
+ # This starts a stopped instance.
11
+ class StartInstance
12
+ include Vagrant::Util::Retryable
13
+
14
+ def initialize(app, env)
15
+ @app = app
16
+ @logger = Log4r::Logger.new("vagrant_aws::action::start_instance")
17
+ end
18
+
19
+ def call(env)
20
+ # Initialize metrics if they haven't been
21
+ env[:metrics] ||= {}
22
+
23
+ server = env[:aws_compute].servers.get(env[:machine].id)
24
+
25
+ env[:ui].info(I18n.t("vagrant_aws.starting"))
26
+
27
+ begin
28
+ server.start
29
+
30
+ region = env[:machine].provider_config.region
31
+ region_config = env[:machine].provider_config.get_region_config(region)
32
+
33
+ # Wait for the instance to be ready first
34
+ env[:metrics]["instance_ready_time"] = Util::Timer.time do
35
+ tries = region_config.instance_ready_timeout / 2
36
+
37
+ env[:ui].info(I18n.t("vagrant_aws.waiting_for_ready"))
38
+ begin
39
+ retryable(:on => Fog::Errors::TimeoutError, :tries => tries) do
40
+ # If we're interrupted don't worry about waiting
41
+ next if env[:interrupted]
42
+
43
+ # Wait for the server to be ready
44
+ server.wait_for(2) { ready? }
45
+ end
46
+ rescue Fog::Errors::TimeoutError
47
+ # Notify the user
48
+ raise Errors::InstanceReadyTimeout,
49
+ timeout: region_config.instance_ready_timeout
50
+ end
51
+ end
52
+ rescue Fog::Compute::AWS::Error => e
53
+ raise Errors::FogError, :message => e.message
54
+ end
55
+
56
+ @logger.info("Time to instance ready: #{env[:metrics]["instance_ready_time"]}")
57
+
58
+ if !env[:interrupted]
59
+ env[:metrics]["instance_ssh_time"] = Util::Timer.time do
60
+ # Wait for SSH to be ready.
61
+ env[:ui].info(I18n.t("vagrant_aws.waiting_for_ssh"))
62
+ while true
63
+ # If we're interrupted then just back out
64
+ break if env[:interrupted]
65
+ break if env[:machine].communicate.ready?
66
+ sleep 2
67
+ end
68
+ end
69
+
70
+ @logger.info("Time for SSH ready: #{env[:metrics]["instance_ssh_time"]}")
71
+
72
+ # Ready and booted!
73
+ env[:ui].info(I18n.t("vagrant_aws.ready"))
74
+ end
75
+
76
+ @app.call(env)
77
+ end
78
+ end
79
+ end
80
+ end
81
+ end
@@ -0,0 +1,28 @@
1
+ require "log4r"
2
+
3
+ module VagrantPlugins
4
+ module AWS
5
+ module Action
6
+ # This stops the running instance.
7
+ class StopInstance
8
+ def initialize(app, env)
9
+ @app = app
10
+ @logger = Log4r::Logger.new("vagrant_aws::action::stop_instance")
11
+ end
12
+
13
+ def call(env)
14
+ server = env[:aws_compute].servers.get(env[:machine].id)
15
+
16
+ if env[:machine].state.id == :stopped
17
+ env[:ui].info(I18n.t("vagrant_aws.already_status", :status => env[:machine].state.id))
18
+ else
19
+ env[:ui].info(I18n.t("vagrant_aws.stopping"))
20
+ server.stop(!!env[:force_halt])
21
+ end
22
+
23
+ @app.call(env)
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,51 @@
1
+ require "log4r"
2
+ require "json"
3
+
4
+ module VagrantPlugins
5
+ module AWS
6
+ module Action
7
+ # This terminates the running instance.
8
+ class TerminateInstance
9
+ def initialize(app, env)
10
+ @app = app
11
+ @logger = Log4r::Logger.new("vagrant_aws::action::terminate_instance")
12
+ end
13
+
14
+ def call(env)
15
+ server = env[:aws_compute].servers.get(env[:machine].id)
16
+ region = env[:machine].provider_config.region
17
+ region_config = env[:machine].provider_config.get_region_config(region)
18
+
19
+ elastic_ip = region_config.elastic_ip
20
+
21
+ # Release the elastic IP
22
+ ip_file = env[:machine].data_dir.join('elastic_ip')
23
+ if ip_file.file?
24
+ release_address(env,ip_file.read)
25
+ ip_file.delete
26
+ end
27
+
28
+ # Destroy the server and remove the tracking ID
29
+ env[:ui].info(I18n.t("vagrant_aws.terminating"))
30
+ server.destroy
31
+ env[:machine].id = nil
32
+
33
+ @app.call(env)
34
+ end
35
+
36
+ # Release an elastic IP address
37
+ def release_address(env,eip)
38
+ h = JSON.parse(eip)
39
+ # Use association_id and allocation_id for VPC, use public IP for EC2
40
+ if h['association_id']
41
+ env[:aws_compute].disassociate_address(nil,h['association_id'])
42
+ env[:aws_compute].release_address(h['allocation_id'])
43
+ else
44
+ env[:aws_compute].disassociate_address(h['public_ip'])
45
+ env[:aws_compute].release_address(h['public_ip'])
46
+ end
47
+ end
48
+ end
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,21 @@
1
+ require "vagrant-aws/util/timer"
2
+
3
+ module VagrantPlugins
4
+ module AWS
5
+ module Action
6
+ # This is the same as the builtin provision except it times the
7
+ # provisioner runs.
8
+ class TimedProvision < Vagrant::Action::Builtin::Provision
9
+ def run_provisioner(env, name, p)
10
+ timer = Util::Timer.time do
11
+ super
12
+ end
13
+
14
+ env[:metrics] ||= {}
15
+ env[:metrics]["provisioner_times"] ||= []
16
+ env[:metrics]["provisioner_times"] << [name, timer]
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,41 @@
1
+ require "log4r"
2
+ require "timeout"
3
+
4
+ module VagrantPlugins
5
+ module AWS
6
+ module Action
7
+ # This action will wait for a machine to reach a specific state or quit by timeout
8
+ class WaitForState
9
+ # env[:result] will be false in case of timeout.
10
+ # @param [Symbol] state Target machine state.
11
+ # @param [Number] timeout Timeout in seconds.
12
+ def initialize(app, env, state, timeout)
13
+ @app = app
14
+ @logger = Log4r::Logger.new("vagrant_aws::action::wait_for_state")
15
+ @state = state
16
+ @timeout = timeout
17
+ end
18
+
19
+ def call(env)
20
+ env[:result] = true
21
+ if env[:machine].state.id == @state
22
+ @logger.info(I18n.t("vagrant_aws.already_status", :status => @state))
23
+ else
24
+ @logger.info("Waiting for machine to reach state #{@state}")
25
+ begin
26
+ Timeout.timeout(@timeout) do
27
+ until env[:machine].state.id == @state
28
+ sleep 2
29
+ end
30
+ end
31
+ rescue Timeout::Error
32
+ env[:result] = false # couldn't reach state in time
33
+ end
34
+ end
35
+
36
+ @app.call(env)
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,19 @@
1
+ module VagrantPlugins
2
+ module AWS
3
+ module Action
4
+ class WarnNetworks
5
+ def initialize(app, env)
6
+ @app = app
7
+ end
8
+
9
+ def call(env)
10
+ if env[:machine].config.vm.networks.length > 0
11
+ env[:ui].warn(I18n.t("vagrant_aws.warn_networks"))
12
+ end
13
+
14
+ @app.call(env)
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,601 @@
1
+ require "vagrant"
2
+ require "iniparse"
3
+
4
+ module VagrantPlugins
5
+ module AWS
6
+ class Config < Vagrant.plugin("2", :config)
7
+ # The access key ID for accessing AWS.
8
+ #
9
+ # @return [String]
10
+ attr_accessor :access_key_id
11
+
12
+ # The ID of the AMI to use.
13
+ #
14
+ # @return [String]
15
+ attr_accessor :ami
16
+
17
+ # The availability zone to launch the instance into. If nil, it will
18
+ # use the default for your account.
19
+ #
20
+ # @return [String]
21
+ attr_accessor :availability_zone
22
+
23
+ # The timeout to wait for an instance to become ready.
24
+ #
25
+ # @return [Fixnum]
26
+ attr_accessor :instance_ready_timeout
27
+
28
+ # The interval to wait for checking an instance's state.
29
+ #
30
+ # @return [Fixnum]
31
+ attr_accessor :instance_check_interval
32
+
33
+ # The timeout to wait for an instance to successfully burn into an AMI.
34
+ #
35
+ # @return [Fixnum]
36
+ attr_accessor :instance_package_timeout
37
+
38
+ # The type of instance to launch, such as "m3.medium"
39
+ #
40
+ # @return [String]
41
+ attr_accessor :instance_type
42
+
43
+ # The name of the keypair to use.
44
+ #
45
+ # @return [String]
46
+ attr_accessor :keypair_name
47
+
48
+ # The private IP address to give this machine (VPC).
49
+ #
50
+ # @return [String]
51
+ attr_accessor :private_ip_address
52
+
53
+ # If true, acquire and attach an elastic IP address.
54
+ # If set to an IP address, assign to the instance.
55
+ #
56
+ # @return [String]
57
+ attr_accessor :elastic_ip
58
+
59
+ # The name of the AWS region in which to create the instance.
60
+ #
61
+ # @return [String]
62
+ attr_accessor :region
63
+
64
+ # The EC2 endpoint to connect to
65
+ #
66
+ # @return [String]
67
+ attr_accessor :endpoint
68
+
69
+ # The version of the AWS api to use
70
+ #
71
+ # @return [String]
72
+ attr_accessor :version
73
+
74
+ # The secret access key for accessing AWS.
75
+ #
76
+ # @return [String]
77
+ attr_accessor :secret_access_key
78
+
79
+ # The token associated with the key for accessing AWS.
80
+ #
81
+ # @return [String]
82
+ attr_accessor :session_token
83
+
84
+ # The security groups to set on the instance. For VPC this must
85
+ # be a list of IDs. For EC2, it can be either.
86
+ #
87
+ # @return [Array<String>]
88
+ attr_reader :security_groups
89
+
90
+ # The Amazon resource name (ARN) of the IAM Instance Profile
91
+ # to associate with the instance.
92
+ #
93
+ # @return [String]
94
+ attr_accessor :iam_instance_profile_arn
95
+
96
+ # The name of the IAM Instance Profile to associate with
97
+ # the instance.
98
+ #
99
+ # @return [String]
100
+ attr_accessor :iam_instance_profile_name
101
+
102
+ # The subnet ID to launch the machine into (VPC).
103
+ #
104
+ # @return [String]
105
+ attr_accessor :subnet_id
106
+
107
+ # The tags for the machine.
108
+ #
109
+ # @return [Hash<String, String>]
110
+ attr_accessor :tags
111
+
112
+ # The tags for the AMI generated with package.
113
+ #
114
+ # @return [Hash<String, String>]
115
+ attr_accessor :package_tags
116
+
117
+ # Use IAM Instance Role for authentication to AWS instead of an
118
+ # explicit access_id and secret_access_key
119
+ #
120
+ # @return [Boolean]
121
+ attr_accessor :use_iam_profile
122
+
123
+ # The user data string
124
+ #
125
+ # @return [String]
126
+ attr_accessor :user_data
127
+
128
+ # Block device mappings
129
+ #
130
+ # @return [Array<Hash>]
131
+ attr_accessor :block_device_mapping
132
+
133
+ # Indicates whether an instance stops or terminates when you initiate shutdown from the instance
134
+ #
135
+ # @return [bool]
136
+ attr_accessor :terminate_on_shutdown
137
+
138
+ # Specifies which address to connect to with ssh
139
+ # Must be one of:
140
+ # - :public_ip_address
141
+ # - :dns_name
142
+ # - :private_ip_address
143
+ # This attribute also accepts an array of symbols
144
+ #
145
+ # @return [Symbol]
146
+ attr_accessor :ssh_host_attribute
147
+
148
+ # Enables Monitoring
149
+ #
150
+ # @return [Boolean]
151
+ attr_accessor :monitoring
152
+
153
+ # EBS optimized instance
154
+ #
155
+ # @return [Boolean]
156
+ attr_accessor :ebs_optimized
157
+
158
+ # Source Destination check
159
+ #
160
+ # @return [Boolean]
161
+ attr_accessor :source_dest_check
162
+
163
+ # Assigning a public IP address in a VPC
164
+ #
165
+ # @return [Boolean]
166
+ attr_accessor :associate_public_ip
167
+
168
+ # The name of ELB, which an instance should be
169
+ # attached to
170
+ #
171
+ # @return [String]
172
+ attr_accessor :elb
173
+
174
+ # Disable unregisering ELB's from AZ - useful in case of not using default VPC
175
+ # @return [Boolean]
176
+ attr_accessor :unregister_elb_from_az
177
+
178
+ # Kernel Id
179
+ #
180
+ # @return [String]
181
+ attr_accessor :kernel_id
182
+
183
+ # The tenancy of the instance in a VPC.
184
+ # Defaults to 'default'.
185
+ #
186
+ # @return [String]
187
+ attr_accessor :tenancy
188
+
189
+ # The directory where AWS files are stored (usually $HOME/.aws)
190
+ #
191
+ # @return [String]
192
+ attr_accessor :aws_dir
193
+
194
+ # The selected AWS named profile (as defined in $HOME/.aws/* files)
195
+ #
196
+ # @return [String]
197
+ attr_accessor :aws_profile
198
+
199
+ # Launch as spot instance
200
+ #
201
+ # @return [Boolean]
202
+ attr_accessor :spot_instance
203
+
204
+ # Spot request max price
205
+ #
206
+ # @return [String]
207
+ attr_accessor :spot_max_price
208
+
209
+ # Spot request validity
210
+ #
211
+ # @return [Time]
212
+ attr_accessor :spot_valid_until
213
+
214
+ # The product description for the spot price history
215
+ #
216
+ # @return [String]
217
+ attr_accessor :spot_price_product_description
218
+
219
+ def initialize(region_specific=false)
220
+ @access_key_id = UNSET_VALUE
221
+ @ami = UNSET_VALUE
222
+ @availability_zone = UNSET_VALUE
223
+ @instance_check_interval = UNSET_VALUE
224
+ @instance_ready_timeout = UNSET_VALUE
225
+ @instance_package_timeout = UNSET_VALUE
226
+ @instance_type = UNSET_VALUE
227
+ @keypair_name = UNSET_VALUE
228
+ @private_ip_address = UNSET_VALUE
229
+ @region = UNSET_VALUE
230
+ @endpoint = UNSET_VALUE
231
+ @version = UNSET_VALUE
232
+ @secret_access_key = UNSET_VALUE
233
+ @session_token = UNSET_VALUE
234
+ @security_groups = UNSET_VALUE
235
+ @subnet_id = UNSET_VALUE
236
+ @tags = {}
237
+ @package_tags = {}
238
+ @user_data = UNSET_VALUE
239
+ @use_iam_profile = UNSET_VALUE
240
+ @block_device_mapping = []
241
+ @elastic_ip = UNSET_VALUE
242
+ @iam_instance_profile_arn = UNSET_VALUE
243
+ @iam_instance_profile_name = UNSET_VALUE
244
+ @terminate_on_shutdown = UNSET_VALUE
245
+ @ssh_host_attribute = UNSET_VALUE
246
+ @monitoring = UNSET_VALUE
247
+ @ebs_optimized = UNSET_VALUE
248
+ @source_dest_check = UNSET_VALUE
249
+ @associate_public_ip = UNSET_VALUE
250
+ @elb = UNSET_VALUE
251
+ @unregister_elb_from_az = UNSET_VALUE
252
+ @kernel_id = UNSET_VALUE
253
+ @tenancy = UNSET_VALUE
254
+ @aws_dir = UNSET_VALUE
255
+ @aws_profile = UNSET_VALUE
256
+ @spot_instance = UNSET_VALUE
257
+ @spot_max_price = UNSET_VALUE
258
+ @spot_valid_until = UNSET_VALUE
259
+
260
+ # Internal state (prefix with __ so they aren't automatically
261
+ # merged)
262
+ @__compiled_region_configs = {}
263
+ @__finalized = false
264
+ @__region_config = {}
265
+ @__region_specific = region_specific
266
+ end
267
+
268
+ # set security_groups
269
+ def security_groups=(value)
270
+ # convert value to array if necessary
271
+ @security_groups = value.is_a?(Array) ? value : [value]
272
+ end
273
+
274
+ # Allows region-specific overrides of any of the settings on this
275
+ # configuration object. This allows the user to override things like
276
+ # AMI and keypair name for regions. Example:
277
+ #
278
+ # aws.region_config "us-east-1" do |region|
279
+ # region.ami = "ami-12345678"
280
+ # region.keypair_name = "company-east"
281
+ # end
282
+ #
283
+ # @param [String] region The region name to configure.
284
+ # @param [Hash] attributes Direct attributes to set on the configuration
285
+ # as a shortcut instead of specifying a full block.
286
+ # @yield [config] Yields a new AWS configuration.
287
+ def region_config(region, attributes=nil, &block)
288
+ # Append the block to the list of region configs for that region.
289
+ # We'll evaluate these upon finalization.
290
+ @__region_config[region] ||= []
291
+
292
+ # Append a block that sets attributes if we got one
293
+ if attributes
294
+ attr_block = lambda do |config|
295
+ config.set_options(attributes)
296
+ end
297
+
298
+ @__region_config[region] << attr_block
299
+ end
300
+
301
+ # Append a block if we got one
302
+ @__region_config[region] << block if block_given?
303
+ end
304
+
305
+ #-------------------------------------------------------------------
306
+ # Internal methods.
307
+ #-------------------------------------------------------------------
308
+
309
+ def merge(other)
310
+ super.tap do |result|
311
+ # Copy over the region specific flag. "True" is retained if either
312
+ # has it.
313
+ new_region_specific = other.instance_variable_get(:@__region_specific)
314
+ result.instance_variable_set(
315
+ :@__region_specific, new_region_specific || @__region_specific)
316
+
317
+ # Go through all the region configs and prepend ours onto
318
+ # theirs.
319
+ new_region_config = other.instance_variable_get(:@__region_config)
320
+ @__region_config.each do |key, value|
321
+ new_region_config[key] ||= []
322
+ new_region_config[key] = value + new_region_config[key]
323
+ end
324
+
325
+ # Set it
326
+ result.instance_variable_set(:@__region_config, new_region_config)
327
+
328
+ # Merge in the tags
329
+ result.tags.merge!(self.tags)
330
+ result.tags.merge!(other.tags)
331
+
332
+ # Merge in the package tags
333
+ result.package_tags.merge!(self.package_tags)
334
+ result.package_tags.merge!(other.package_tags)
335
+
336
+ # Merge block_device_mapping
337
+ result.block_device_mapping |= self.block_device_mapping
338
+ result.block_device_mapping |= other.block_device_mapping
339
+ end
340
+ end
341
+
342
+ def finalize!
343
+ # If access_key_id or secret_access_key were not specified in Vagrantfile
344
+ # then try to read from environment variables first, and if it fails from
345
+ # the AWS folder.
346
+ if @access_key_id == UNSET_VALUE or @secret_access_key == UNSET_VALUE
347
+ @aws_profile = 'default' if @aws_profile == UNSET_VALUE
348
+ @aws_dir = ENV['HOME'].to_s + '/.aws/' if @aws_dir == UNSET_VALUE
349
+ @aws_region, @access_key_id, @secret_access_key, @session_token = Credentials.new.get_aws_info(@aws_profile, @aws_dir)
350
+ @region = @aws_region if @region == UNSET_VALUE and !@aws_region.nil?
351
+ else
352
+ @aws_profile = nil
353
+ @aws_dir = nil
354
+ end
355
+
356
+ # session token must be set to nil, empty string isn't enough!
357
+ @session_token = nil if @session_token == UNSET_VALUE
358
+
359
+ # AMI must be nil, since we can't default that
360
+ @ami = nil if @ami == UNSET_VALUE
361
+
362
+ # Set the default timeout for waiting for an instance to be ready
363
+ @instance_ready_timeout = 120 if @instance_ready_timeout == UNSET_VALUE
364
+
365
+ # Set the default interval to check instance state
366
+ @instance_check_interval = 2 if @instance_check_interval == UNSET_VALUE
367
+
368
+ # Set the default timeout for waiting for an instance to burn into and ami
369
+ @instance_package_timeout = 600 if @instance_package_timeout == UNSET_VALUE
370
+
371
+ # Default instance type is an m3.medium
372
+ @instance_type = "m3.medium" if @instance_type == UNSET_VALUE
373
+
374
+ # Keypair defaults to nil
375
+ @keypair_name = nil if @keypair_name == UNSET_VALUE
376
+
377
+ # Default the private IP to nil since VPC is not default
378
+ @private_ip_address = nil if @private_ip_address == UNSET_VALUE
379
+
380
+ # Acquire an elastic IP if requested
381
+ @elastic_ip = nil if @elastic_ip == UNSET_VALUE
382
+
383
+ # Default region is us-east-1. This is sensible because AWS
384
+ # generally defaults to this as well.
385
+ @region = "us-east-1" if @region == UNSET_VALUE
386
+ @availability_zone = nil if @availability_zone == UNSET_VALUE
387
+ @endpoint = nil if @endpoint == UNSET_VALUE
388
+ @version = nil if @version == UNSET_VALUE
389
+
390
+ # The security groups are empty by default.
391
+ @security_groups = [] if @security_groups == UNSET_VALUE
392
+
393
+ # Subnet is nil by default otherwise we'd launch into VPC.
394
+ @subnet_id = nil if @subnet_id == UNSET_VALUE
395
+
396
+ # IAM Instance profile arn/name is nil by default.
397
+ @iam_instance_profile_arn = nil if @iam_instance_profile_arn == UNSET_VALUE
398
+ @iam_instance_profile_name = nil if @iam_instance_profile_name == UNSET_VALUE
399
+
400
+ # By default we don't use an IAM profile
401
+ @use_iam_profile = false if @use_iam_profile == UNSET_VALUE
402
+
403
+ # User Data is nil by default
404
+ @user_data = nil if @user_data == UNSET_VALUE
405
+
406
+ # default false
407
+ @terminate_on_shutdown = false if @terminate_on_shutdown == UNSET_VALUE
408
+
409
+ # default to nil
410
+ @ssh_host_attribute = nil if @ssh_host_attribute == UNSET_VALUE
411
+
412
+ # default false
413
+ @monitoring = false if @monitoring == UNSET_VALUE
414
+
415
+ # default false
416
+ @ebs_optimized = false if @ebs_optimized == UNSET_VALUE
417
+
418
+ # default to nil
419
+ @source_dest_check = nil if @source_dest_check == UNSET_VALUE
420
+
421
+ # default false
422
+ @associate_public_ip = false if @associate_public_ip == UNSET_VALUE
423
+
424
+ # default 'default'
425
+ @tenancy = "default" if @tenancy == UNSET_VALUE
426
+
427
+ # Don't attach instance to any ELB by default
428
+ @elb = nil if @elb == UNSET_VALUE
429
+
430
+ @unregister_elb_from_az = true if @unregister_elb_from_az == UNSET_VALUE
431
+
432
+ # default to nil
433
+ @kernel_id = nil if @kernel_id == UNSET_VALUE
434
+
435
+ # By default don't use spot requests
436
+ @spot_instance = false if @spot_instance == UNSET_VALUE
437
+
438
+ # default to nil
439
+ @spot_max_price = nil if @spot_max_price == UNSET_VALUE
440
+
441
+ # Default: Request is effective indefinitely.
442
+ @spot_valid_until = nil if @spot_valid_until == UNSET_VALUE
443
+
444
+ # default to nil
445
+ @spot_price_product_description = nil if @spot_price_product_description == UNSET_VALUE
446
+
447
+ # Compile our region specific configurations only within
448
+ # NON-REGION-SPECIFIC configurations.
449
+ if !@__region_specific
450
+ @__region_config.each do |region, blocks|
451
+ config = self.class.new(true).merge(self)
452
+
453
+ # Execute the configuration for each block
454
+ blocks.each { |b| b.call(config) }
455
+
456
+ # The region name of the configuration always equals the
457
+ # region config name:
458
+ config.region = region
459
+
460
+ # Finalize the configuration
461
+ config.finalize!
462
+
463
+ # Store it for retrieval
464
+ @__compiled_region_configs[region] = config
465
+ end
466
+ end
467
+
468
+ # Mark that we finalized
469
+ @__finalized = true
470
+ end
471
+
472
+ def validate(machine)
473
+ errors = _detected_errors
474
+
475
+ errors << I18n.t("vagrant_aws.config.aws_info_required",
476
+ :profile => @aws_profile, :location => @aws_dir) if \
477
+ @aws_profile and (@access_key_id.nil? or @secret_access_key.nil? or @region.nil?)
478
+
479
+ errors << I18n.t("vagrant_aws.config.region_required") if @region.nil?
480
+
481
+ if @region
482
+ # Get the configuration for the region we're using and validate only
483
+ # that region.
484
+ config = get_region_config(@region)
485
+
486
+ if !config.use_iam_profile
487
+ errors << I18n.t("vagrant_aws.config.access_key_id_required") if \
488
+ config.access_key_id.nil?
489
+ errors << I18n.t("vagrant_aws.config.secret_access_key_required") if \
490
+ config.secret_access_key.nil?
491
+ end
492
+
493
+ if config.associate_public_ip && !config.subnet_id
494
+ errors << I18n.t("vagrant_aws.config.subnet_id_required_with_public_ip")
495
+ end
496
+
497
+ errors << I18n.t("vagrant_aws.config.ami_required", :region => @region) if config.ami.nil?
498
+ end
499
+
500
+ { "AWS Provider" => errors }
501
+ end
502
+
503
+ # This gets the configuration for a specific region. It shouldn't
504
+ # be called by the general public and is only used internally.
505
+ def get_region_config(name)
506
+ if !@__finalized
507
+ raise "Configuration must be finalized before calling this method."
508
+ end
509
+
510
+ # Return the compiled region config
511
+ @__compiled_region_configs[name] || self
512
+ end
513
+ end
514
+
515
+
516
+ class Credentials < Vagrant.plugin("2", :config)
517
+ # This module reads AWS config and credentials.
518
+ # Behaviour aims to mimic what is described in AWS documentation:
519
+ # http://docs.aws.amazon.com/cli/latest/userguide/cli-chap-getting-started.html
520
+ # http://docs.aws.amazon.com/cli/latest/topic/config-vars.html
521
+ # Which is the following (stopping at the first successful case):
522
+ # 1) read config and credentials from environment variables
523
+ # 2) read config and credentials from files at location defined by environment variables
524
+ # 3) read config and credentials from files at default location
525
+ #
526
+ # The mandatory fields for a successful "get credentials" are the id and the secret keys.
527
+ # Region is not required since Config#finalize falls back to sensible defaults.
528
+ # The behaviour is all-or-nothing (ie: no mixing between vars and files).
529
+ #
530
+ # It also allows choosing a profile (by default it's [default]) and an "info"
531
+ # directory (by default $HOME/.aws), which can be specified in the Vagrantfile.
532
+ # Supported information: region, aws_access_key_id, aws_secret_access_key, and aws_session_token.
533
+
534
+ def get_aws_info(profile, location)
535
+ # read credentials from environment variables
536
+ aws_region, aws_id, aws_secret, aws_token = read_aws_environment()
537
+ # if nothing there, then read from files
538
+ # (the _if_ doesn't check aws_region since Config#finalize sets one by default)
539
+ if aws_id.to_s == '' or aws_secret.to_s == ''
540
+ # check if there are env variables for credential location, if so use then
541
+ aws_config = ENV['AWS_CONFIG_FILE'].to_s
542
+ aws_creds = ENV['AWS_SHARED_CREDENTIALS_FILE'].to_s
543
+ if aws_config == '' or aws_creds == ''
544
+ aws_config = location + 'config'
545
+ aws_creds = location + 'credentials'
546
+ end
547
+ if File.exist?(aws_config) and File.exist?(aws_creds)
548
+ aws_region, aws_id, aws_secret, aws_token = read_aws_files(profile, aws_config, aws_creds)
549
+ end
550
+ end
551
+ aws_region = nil if aws_region == ''
552
+ aws_id = nil if aws_id == ''
553
+ aws_secret = nil if aws_secret == ''
554
+ aws_token = nil if aws_token == ''
555
+
556
+ return aws_region, aws_id, aws_secret, aws_token
557
+ end
558
+
559
+
560
+ private
561
+
562
+ def read_aws_files(profile, aws_config, aws_creds)
563
+ # get info from config ini file for selected profile
564
+ data = File.read(aws_config)
565
+ doc_cfg = IniParse.parse(data)
566
+
567
+ # determine section in config ini file
568
+ if profile == 'default' || !doc_cfg[profile].nil?
569
+ ini_profile = profile
570
+ else
571
+ ini_profile = 'profile ' + profile
572
+ end
573
+
574
+ aws_region = doc_cfg[ini_profile]['region']
575
+
576
+ # determine section in credentials ini file
577
+ ini_profile = profile
578
+ # get info from credentials ini file for selected profile
579
+ data = File.read(aws_creds)
580
+ doc_cfg = IniParse.parse(data)
581
+ aws_id = doc_cfg[ini_profile]['aws_access_key_id']
582
+ aws_secret = doc_cfg[ini_profile]['aws_secret_access_key']
583
+ aws_token = doc_cfg[ini_profile]['aws_session_token']
584
+
585
+ return aws_region, aws_id, aws_secret, aws_token
586
+ end
587
+
588
+ def read_aws_environment()
589
+ aws_region = ENV['AWS_DEFAULT_REGION']
590
+ aws_id = ENV['AWS_ACCESS_KEY_ID']
591
+ aws_secret = ENV['AWS_SECRET_ACCESS_KEY']
592
+ aws_token = ENV['AWS_SESSION_TOKEN']
593
+
594
+ return aws_region, aws_id, aws_secret, aws_token
595
+ end
596
+
597
+ end
598
+
599
+
600
+ end
601
+ end