Skip to content

Commit 807f3ff

Browse files
committed
Refactoring some code into discrete classes so the Ec2 class isn't so giant
1 parent 16f6955 commit 807f3ff

File tree

3 files changed

+159
-105
lines changed

3 files changed

+159
-105
lines changed

lib/kitchen/driver/ec2.rb

+11-103
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,8 @@
2121
require "aws"
2222
require "kitchen"
2323
require "kitchen/driver/ec2_version"
24-
require_relative "client/ec2_client"
24+
require_relative "ec2/client"
25+
require_relative "ec2/instance_generator"
2526

2627
module Kitchen
2728

@@ -52,9 +53,7 @@ class Ec2 < Kitchen::Driver::Base # rubocop:disable Metrics/ClassLength
5253
default_config :retryable_sleep, 5
5354
default_config :aws_access_key_id, nil
5455
default_config :aws_secret_access_key, nil
55-
default_config :aws_ssh_key_id do |_driver|
56-
ENV["AWS_SSH_KEY_ID"]
57-
end
56+
default_config :aws_ssh_key_id, ENV["AWS_SSH_KEY_ID"]
5857
default_config :image_id do |driver|
5958
driver.default_ami
6059
end
@@ -230,14 +229,18 @@ def default_ami
230229
private
231230

232231
def ec2
233-
@ec2 ||= Client::Ec2Client.new(
232+
@ec2 ||= Client.new(
234233
config[:region],
235234
config[:shared_credentials_profile],
236235
config[:aws_access_key_id],
237236
config[:aws_secret_access_key]
238237
)
239238
end
240239

240+
def instance_generator
241+
@instance_generator ||= InstanceGenerator.new(config, ec2)
242+
end
243+
241244
# This copies transport config from the current config object into the
242245
# state. This relies on logic in the transport that merges the transport
243246
# config with the current state object, so its a bad coupling. But we
@@ -267,7 +270,7 @@ def copy_deprecated_configs(state)
267270
# Fog AWS helper for creating the instance
268271
def submit_server
269272
debug("Creating EC2 Instance..")
270-
instance_data = ec2_instance_data
273+
instance_data = instance_generator.ec2_instance_data
271274
instance_data[:min_count] = 1
272275
instance_data[:max_count] = 1
273276
ec2.create_instance(instance_data)
@@ -277,7 +280,7 @@ def submit_spot(state) # rubocop:disable Metrics/AbcSize
277280
debug("Creating EC2 Spot Instance..")
278281
request_data = {}
279282
request_data[:spot_price] = config[:price].to_s
280-
request_data[:launch_specification] = ec2_instance_data
283+
request_data[:launch_specification] = instance_generator.ec2_instance_data
281284

282285
response = ec2.client.request_spot_instances(request_data)
283286
spot_request_id = response[:spot_instance_requests][0][:spot_instance_request_id]
@@ -302,35 +305,7 @@ def tag_server(server)
302305
server.create_tags({:tags => tags})
303306
end
304307

305-
# Transform the provided config into the hash to send to AWS. Some fields
306-
# can be passed in null, others need to be ommitted if they are null
307-
def ec2_instance_data
308-
i = {
309-
:placement => {
310-
:availability_zone => config[:availability_zone],
311-
},
312-
:instance_type => config[:instance_type],
313-
:ebs_optimized => config[:ebs_optimized],
314-
:image_id => config[:image_id],
315-
:key_name => config[:aws_ssh_key_id],
316-
:subnet_id => config[:subnet_id],
317-
:private_ip_address => config[:private_ip_address],
318-
}
319-
i[:block_device_mappings] = block_device_mappings unless block_device_mappings.empty?
320-
i[:security_group_ids] = config[:security_group_ids] if config[:security_group_ids]
321-
i[:user_data] = prepared_user_data if prepared_user_data
322-
if config[:iam_instance_profile]
323-
i[:iam_instance_profile] = {:name => config[:iam_profile_name]}
324-
end
325-
if config.fetch(:associate_public_ip_address, nil) != nil
326-
i[:network_interfaces] =
327-
[{
328-
:device_index => 0,
329-
:associate_public_ip_address => config[:associate_public_ip_address]
330-
}]
331-
end
332-
i
333-
end
308+
334309

335310
def amis
336311
@amis ||= begin
@@ -371,73 +346,6 @@ def hostname(server, interface_type = nil)
371346
end
372347
end
373348

374-
# Transforms the provided config into the appropriate hash for creating a BDM
375-
# in AWS
376-
def block_device_mappings # rubocop:disable all
377-
return @bdms if @bdms
378-
bdms = config[:block_device_mappings] || []
379-
if bdms.nil? || bdms.empty?
380-
if config[:ebs_volume_size] || config.fetch(:ebs_delete_on_termination, nil) ||
381-
config[:ebs_device_name] || config[:ebs_volume_type]
382-
# If the user didn't supply block_device_mappings but did supply
383-
# the old configs, copy them into the block_device_mappings array correctly
384-
bdms << {
385-
:ebs_volume_size => config[:ebs_volume_size] || 8,
386-
:ebs_delete_on_termination => config.fetch(:ebs_delete_on_termination, true),
387-
:ebs_device_name => config[:ebs_device_name] || "/dev/sda1",
388-
:ebs_volume_type => config[:ebs_volume_type] || "standard"
389-
}
390-
end
391-
end
392-
393-
# Convert the provided keys to what AWS expects
394-
bdms = bdms.map do |bdm|
395-
b = {
396-
:ebs => {
397-
:volume_size => bdm[:ebs_volume_size],
398-
:volume_type => bdm[:ebs_volume_type],
399-
:delete_on_termination => bdm[:ebs_delete_on_termination]
400-
},
401-
:device_name => bdm[:ebs_device_name]
402-
}
403-
b[:ebs][:snapshot_id] = bdm[:ebs_snapshot_id] if bdm[:ebs_snapshot_id]
404-
b[:virtual_name] = bdm[:ebs_virtual_name] if bdm[:ebs_virtual_name]
405-
b
406-
end
407-
408-
debug_if_root_device(bdms)
409-
410-
@bdms = bdms
411-
end
412-
413-
# If the provided bdms match the root device in the AMI, emit log that
414-
# states this
415-
def debug_if_root_device(bdms)
416-
image_id = config[:image_id]
417-
image = ec2.resource.image(image_id)
418-
begin
419-
root_device_name = image.root_device_name
420-
rescue ::Aws::EC2::Errors::InvalidAMIIDNotFound
421-
# Not raising here because AWS will give a more meaningful message
422-
# when we try to create the instance
423-
return
424-
end
425-
bdms.find { |bdm|
426-
if bdm[:device_name] == root_device_name
427-
info("Overriding root device [#{root_device_name}] from image [#{image_id}]")
428-
end
429-
}
430-
end
431-
432-
def prepared_user_data
433-
# If user_data is a file reference, lets read it as such
434-
@user_data ||= unless config[:user_data].nil?
435-
if File.file?(config[:user_data])
436-
config[:user_data] = File.read(config[:user_data])
437-
end
438-
end
439-
end
440-
441349
def create_ec2_json(state)
442350
instance.transport.connection(state).execute(
443351
"sudo mkdir -p /etc/chef/ohai/hints;sudo touch /etc/chef/ohai/hints/ec2.json"

lib/kitchen/driver/client/ec2_client.rb lib/kitchen/driver/ec2/client.rb

+6-2
Original file line numberDiff line numberDiff line change
@@ -22,12 +22,12 @@ module Kitchen
2222

2323
module Driver
2424

25-
module Client
25+
class Ec2 < Kitchen::Driver::Base
2626

2727
# A class for creating and managing the EC2 client connection
2828
#
2929
# @author Tyler Ball <[email protected]>
30-
class Ec2Client
30+
class Client
3131

3232
def initialize(region, profile_name = nil, access_key_id = nil, secret_access_key = nil)
3333
Aws.config = {
@@ -37,10 +37,14 @@ def initialize(region, profile_name = nil, access_key_id = nil, secret_access_ke
3737
end
3838

3939
# Try and get the credentials from an ordered list of locations
40+
# http://docs.aws.amazon.com/sdkforruby/api/index.html#Configuration
4041
def get_credentials(profile_name, access_key_id, secret_access_key)
4142
shared_creds = Aws::SharedCredentials.new(:profile_name => profile_name)
4243
if access_key_id && secret_access_key
4344
Aws::Credentials.new(access_key_id, secret_access_key)
45+
# TODO these are deprecated, remove them in the next major version
46+
elsif ENV['AWS_ACCESS_KEY'] && ENV['AWS_SECRET_KEY']
47+
Aws::Credentials.new(ENV['AWS_ACCESS_KEY'], ENV['AWS_SECRET_KEY'], ENV['AWS_TOKEN'])
4448
elsif ENV['AWS_ACCESS_KEY_ID'] && ENV['AWS_SECRET_ACCESS_KEY']
4549
Aws::Credentials.new(ENV['AWS_ACCESS_KEY_ID'], ENV['AWS_SECRET_ACCESS_KEY'], ENV['AWS_SESSION_TOKEN'])
4650
elsif shared_creds.loadable?
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
1+
# -*- encoding: utf-8 -*-
2+
#
3+
# Author:: Tyler Ball (<[email protected]>)
4+
#
5+
# Copyright (C) 2015, Fletcher Nichol
6+
#
7+
# Licensed under the Apache License, Version 2.0 (the "License");
8+
# you may not use this file except in compliance with the License.
9+
# You may obtain a copy of the License at
10+
#
11+
# http://www.apache.org/licenses/LICENSE-2.0
12+
#
13+
# Unless required by applicable law or agreed to in writing, software
14+
# distributed under the License is distributed on an "AS IS" BASIS,
15+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16+
# See the License for the specific language governing permissions and
17+
# limitations under the License.
18+
19+
require 'kitchen/logging'
20+
21+
module Kitchen
22+
23+
module Driver
24+
25+
class Ec2 < Kitchen::Driver::Base
26+
27+
class InstanceGenerator
28+
29+
include Logging
30+
31+
attr_reader :config, :ec2
32+
33+
def initialize(config, ec2)
34+
@config = config
35+
@ec2 = ec2
36+
end
37+
38+
39+
# Transform the provided config into the hash to send to AWS. Some fields
40+
# can be passed in null, others need to be ommitted if they are null
41+
def ec2_instance_data
42+
i = {
43+
:placement => {
44+
:availability_zone => config[:availability_zone],
45+
},
46+
:instance_type => config[:instance_type],
47+
:ebs_optimized => config[:ebs_optimized],
48+
:image_id => config[:image_id],
49+
:key_name => config[:aws_ssh_key_id],
50+
:subnet_id => config[:subnet_id],
51+
:private_ip_address => config[:private_ip_address],
52+
}
53+
i[:block_device_mappings] = block_device_mappings unless block_device_mappings.empty?
54+
i[:security_group_ids] = config[:security_group_ids] if config[:security_group_ids]
55+
i[:user_data] = prepared_user_data if prepared_user_data
56+
if config[:iam_instance_profile]
57+
i[:iam_instance_profile] = {:name => config[:iam_profile_name]}
58+
end
59+
if config.fetch(:associate_public_ip_address, nil) != nil
60+
i[:network_interfaces] =
61+
[{
62+
:device_index => 0,
63+
:associate_public_ip_address => config[:associate_public_ip_address]
64+
}]
65+
end
66+
i
67+
end
68+
69+
# Transforms the provided config into the appropriate hash for creating a BDM
70+
# in AWS
71+
def block_device_mappings # rubocop:disable all
72+
return @bdms if @bdms
73+
bdms = config[:block_device_mappings] || []
74+
if bdms.nil? || bdms.empty?
75+
if config[:ebs_volume_size] || config.fetch(:ebs_delete_on_termination, nil) ||
76+
config[:ebs_device_name] || config[:ebs_volume_type]
77+
# If the user didn't supply block_device_mappings but did supply
78+
# the old configs, copy them into the block_device_mappings array correctly
79+
bdms << {
80+
:ebs_volume_size => config[:ebs_volume_size] || 8,
81+
:ebs_delete_on_termination => config.fetch(:ebs_delete_on_termination, true),
82+
:ebs_device_name => config[:ebs_device_name] || "/dev/sda1",
83+
:ebs_volume_type => config[:ebs_volume_type] || "standard"
84+
}
85+
end
86+
end
87+
88+
# Convert the provided keys to what AWS expects
89+
bdms = bdms.map do |bdm|
90+
b = {
91+
:ebs => {
92+
:volume_size => bdm[:ebs_volume_size],
93+
:volume_type => bdm[:ebs_volume_type],
94+
:delete_on_termination => bdm[:ebs_delete_on_termination]
95+
},
96+
:device_name => bdm[:ebs_device_name]
97+
}
98+
b[:ebs][:snapshot_id] = bdm[:ebs_snapshot_id] if bdm[:ebs_snapshot_id]
99+
b[:virtual_name] = bdm[:ebs_virtual_name] if bdm[:ebs_virtual_name]
100+
b
101+
end
102+
103+
debug_if_root_device(bdms)
104+
105+
@bdms = bdms
106+
end
107+
108+
# If the provided bdms match the root device in the AMI, emit log that
109+
# states this
110+
def debug_if_root_device(bdms)
111+
image_id = config[:image_id]
112+
image = ec2.resource.image(image_id)
113+
begin
114+
root_device_name = image.root_device_name
115+
rescue ::Aws::EC2::Errors::InvalidAMIIDNotFound
116+
# Not raising here because AWS will give a more meaningful message
117+
# when we try to create the instance
118+
return
119+
end
120+
bdms.find { |bdm|
121+
if bdm[:device_name] == root_device_name
122+
info("Overriding root device [#{root_device_name}] from image [#{image_id}]")
123+
end
124+
}
125+
end
126+
127+
def prepared_user_data
128+
# If user_data is a file reference, lets read it as such
129+
@user_data ||= unless config[:user_data].nil?
130+
if File.file?(config[:user_data])
131+
config[:user_data] = File.read(config[:user_data])
132+
end
133+
end
134+
end
135+
136+
end
137+
138+
end
139+
140+
end
141+
142+
end

0 commit comments

Comments
 (0)