ec2_bootstrap 1.0.0 → 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 50318b3c980d39aa1255261e3721ebcd5b167731
4
- data.tar.gz: 4a43edf57231b9a9b5368f933f6925e3e08ce995
3
+ metadata.gz: 2edaecb9c4791dfaf2c8135fd9cfa8409f97d7c4
4
+ data.tar.gz: e40fbe8bcb044cc1671a3b684c95222c95ca5f26
5
5
  SHA512:
6
- metadata.gz: 1395ad633953e8d5c09a39e55c0a81e0089df51dae759fb1672373556d80223150d86a21eae4dcb16fb3e06affeddf97eb149cb07d600c60a101bcd9222a9443
7
- data.tar.gz: ad1a5bacefbeb3a9de43c9885fc6c0e50ab994bde1e37abf6b71a1c85c2aad1cdd7546ed68c9b0f62d16e93a48abe3999f23821c6919c295bc21f974b80b8626
6
+ metadata.gz: 939212801a7d6420941bba382494be32d2d1dca9be73b7b0cfc92840449b450b01f016ce98188e0245761354a4b2dc53afb9514eaee11e39bbd3d3ee72cc151d
7
+ data.tar.gz: a5c366803c0bd12bf50e96e8845d2fb1b923698eeb9e78aa0e9acb62c9af2560ac34ba60aa3c7f73a05896a0ec5069ea1c260a88440442225d37a98612842b12
data/README.md CHANGED
@@ -20,16 +20,33 @@ Or install it yourself as:
20
20
 
21
21
  ## Configuration
22
22
 
23
- Requires AWS credentials in a format compatible with the [EC2 Knife plugin](https://github.com/chef/knife-ec2/blob/master/README.md).
23
+ This is a simple gem that wraps the functionality of the [EC2 Knife plugin](https://github.com/chef/knife-ec2/blob/master/README.md) and the [aws-sdk gem](https://github.com/aws/aws-sdk-ruby). As a result, there is very little code and a very fussy config file.
24
24
 
25
- Also requires a YAML config file that looks like the example `config.example.yml`. The config file must include a top-level `nodes` key whose value is an array of hashes. Each node must include the keys `node_name` and `knife_ec2_flags`.
25
+ ### Required Config
26
+
27
+ Requires AWS credentials in a format compatible with the EC2 Knife plugin.
28
+
29
+ Also requires a YAML config file that looks like the example `config.example.yml`. The config file must include a top-level `instances` key whose value is an array of hashes. Each individual instance must include the keys `instance_name` and `knife_ec2_flags`. The latter is a hash of flags that are passed directly to Knife EC2. The `private-ip-address` flag is required. Each instance also requires the `image` flag, but if you include a `default_ami` section at the top level of your config, `ec2_bootstrap` will set a default AMI so you don't need to define an image for each instance (see "Optional Config" below for more info). To check what flags are currently accepted by Knife EC2, run:
30
+
31
+ $ knife ec2 server create --help
32
+
33
+ For any `knife_ec2_flags` values that are lists, they need to be formatted as one long string with values separated by commas. Ex: `security-group-ids: sg-12345678,sg-abcdef12`.
34
+
35
+ ### Optional Config
26
36
 
27
37
  You may also want to include some form of cloud-init config. To do this, you can do one of two things:
28
38
 
29
- 1. Include a top-level `cloud_config` key with the contents you'd like in the cloud config files used for each node, and ec2_bootstrap will generate separate config files for each node that all include the node's hostname and fqdn.
30
- 2. If you'd prefer to write your own cloud config files, you can include the cloud config's path in a `user-data` key in the node's `knife_ec2_flags` hash.
39
+ 1. Include the cloud config's path in a `user-data` key in the `knife_ec2_flags` hash for every instance.
40
+ 2. Include a top-level `cloud_config` key with the contents you'd like in the cloud config files used for each node. `ec2_bootstrap` will use this to generate separate config files for each node that all include the node's hostname and fqdn. This will be used as the default cloud-init config for any instances that don't have their own defined.
41
+
42
+ The Knife EC2 plugin does not currently support passing in a JSON attributes file. As a result, if you'd like to pass in a JSON attributes file without having to parse it yourself, you can include the `json_attributes_file` key in an instance's hash. Alternatively, you can include the JSON directly by adding it under the `json-attributes` key in the instance's `knife_ec2_flags`. The `json-attributes` key in `knife_ec2_flags` takes precedence over the `json_attributes_file` key.
43
+
44
+ For the `image` flag within an instance's `knife_ec2_flags`, you have two choices:
45
+
46
+ 1. Include the flag for every instance.
47
+ 2. Include a top-level `default_ami` hash. `ec2_bootstrap` will use the parameters within `default_ami` to search for the most recent available AMI that matches your criteria, then will use that image when as the default for any instances that don't have their own `image` field defined.
31
48
 
32
- For any `knife_EC2_flags` values that are lists, they need to be formatted as one long string with values separated by commas. Ex: `security-group-ids: sg-12345678,sg-abcdef12`.
49
+ You can add any of the options listed in [Amazon's aws-sdk gem docs](http://docs.aws.amazon.com/sdkforruby/api/Aws/EC2/Resource.html#images-instance_method) to search for AMIs through `ec2_bootstrap`. If you don't include a `region` key, `ec2_bootstrap` will default to us-east-1. If you choose to include `filters`, it should be a hash where all values are arrays. All other keys besides `region` and `filters` should also have arrays as values.
33
50
 
34
51
  ## Usage
35
52
 
data/bin/ec2_bootstrap CHANGED
@@ -36,4 +36,4 @@ unless config_file
36
36
  exit 1
37
37
  end
38
38
 
39
- EC2Bootstrap.from_config_file(config_file, options[:dryrun], options[:verbose]).create_instances
39
+ EC2Bootstrap.from_config_file(config_file, dryrun: options[:dryrun], verbose: options[:verbose]).create_instances
data/lib/ec2_bootstrap.rb CHANGED
@@ -4,39 +4,61 @@ require 'logger'
4
4
 
5
5
  require 'ec2_bootstrap/version'
6
6
  require 'ec2_bootstrap/instance'
7
+ require 'ec2_bootstrap/ami'
7
8
 
8
9
  class EC2Bootstrap
9
10
 
10
- attr_accessor :cloud_config
11
- attr_accessor :instances
12
11
  attr_accessor :dryrun
12
+ attr_accessor :cloud_config
13
+ attr_accessor :instances_config
14
+ attr_accessor :default_ami_config
13
15
 
14
- def initialize(config, dryrun=true, verbose=false)
15
- @logger = Logger.new(STDOUT)
16
- verbose ? @logger.level = Logger::DEBUG : @logger.level = Logger::INFO
17
-
18
- @cloud_config = config['cloud_config']
19
- @instances = self.make_instances(config['instances'])
16
+ def initialize(config, dryrun: true, verbose: false, logger: nil)
17
+ @logger = logger || self.new_logger(verbose)
20
18
  @dryrun = dryrun
19
+ @cloud_config = config['cloud_config']
20
+ @instances_config = config['instances']
21
+ @default_ami_config = config['default_ami']
21
22
  end
22
23
 
23
24
  def self.from_config(config, *args)
24
- instances = config['instances']
25
- raise KeyError, "Config file is missing 'instances' key." unless instances
26
- raise TypeError, "'instances' config must be an array of hashes." unless instances.is_a?(Array) && instances.first.is_a?(Hash)
27
- config['instances'] = instances.map {|i| i.map {|key, value| [key.to_sym, value]}.to_h}
25
+ self.validate_config(config)
26
+
27
+ config['instances'].map! {|i| i.map {|key, value| [key.to_sym, value]}.to_h}
28
28
 
29
29
  return self.new(config, *args)
30
30
  end
31
31
 
32
32
  def self.from_config_file(config_path, *args)
33
33
  config = YAML.load(File.read(config_path))
34
+ return self.from_config(config, *args)
35
+ end
34
36
 
35
- self.from_config(config, *args)
37
+ def self.validate_config(config)
38
+ instances = config['instances']
39
+ raise KeyError, "Config file is missing 'instances' key." unless instances
40
+ raise TypeError, "'instances' config must be an array of hashes." unless instances.is_a?(Array) && instances.first.is_a?(Hash)
41
+
42
+ if config['default_ami']
43
+ raise TypeError, "'default_ami' config must be a hash." unless config['default_ami'].is_a?(Hash)
44
+ end
45
+
46
+ return true
36
47
  end
37
48
 
38
- def make_instances(instances_config)
39
- return instances_config.map {|i| self.instance_class.new(i.merge(logger: @logger))}
49
+ def new_logger(verbose)
50
+ logger = Logger.new(STDOUT)
51
+ verbose ? logger.level = Logger::DEBUG : logger.level = Logger::INFO
52
+ return logger
53
+ end
54
+
55
+ def ami_class
56
+ return AMI
57
+ end
58
+
59
+ def make_instances(default_image_id)
60
+ generic_args = {logger: @logger, image: default_image_id, dryrun: @dryrun, cloud_config: @cloud_config}
61
+ return @instances_config.map {|i| self.instance_class.new(i.merge(generic_args))}
40
62
  end
41
63
 
42
64
  def instance_class
@@ -44,15 +66,14 @@ class EC2Bootstrap
44
66
  end
45
67
 
46
68
  def create_instances
47
- @logger.debug("This was a dry run. No EC2 instances were created.") if @dryrun
48
-
49
- @instances.each do |instance|
50
- @logger.debug("Instance name: #{instance.name}")
69
+ @logger.info("This was a dry run. No EC2 instances were created.") if @dryrun
51
70
 
52
- instance.generate_cloud_config(@cloud_config, @dryrun) if @cloud_config
71
+ default_image_id = @default_ami_config ? ami_class.from_config(@default_ami_config, @logger).find_newest_image_id : nil
72
+ instances = self.make_instances(default_image_id)
53
73
 
74
+ instances.each do |instance|
54
75
  knife_shell_command = instance.format_knife_shell_command
55
- @logger.debug("Knife shell command:\n#{knife_shell_command}")
76
+ @logger.debug("Knife shell command for #{instance.name}:\n#{knife_shell_command}")
56
77
 
57
78
  unless @dryrun
58
79
  status = self.shell_out_command(knife_shell_command)
@@ -0,0 +1,45 @@
1
+ require 'aws-sdk'
2
+
3
+ class EC2Bootstrap
4
+ class AMI
5
+
6
+ DEFAULT_AWS_REGION = 'us-east-1'
7
+
8
+ DEFAULT_IMAGE_FILTERS = [
9
+ {name: 'image-type', values: ['machine']},
10
+ {name: 'state', values: ['available']}
11
+ ]
12
+
13
+ def initialize(config, logger)
14
+ @region = config.delete('region') || DEFAULT_AWS_REGION
15
+ @search_options = config
16
+ @logger = logger
17
+ end
18
+
19
+ def self.from_config(config, logger)
20
+ if config['filters']
21
+ config['filters'] = config['filters'].to_a.map {|filter| {name: filter.first, values: filter.last}}
22
+ config['filters'] += DEFAULT_IMAGE_FILTERS
23
+ end
24
+ return self.new(config, logger)
25
+ end
26
+
27
+ def find_newest_image_id
28
+ images = self.fetch_eligible_images
29
+
30
+ newest_image = images.max_by{|i| Time.parse(i.creation_date)}
31
+ newest_image_id = newest_image ? newest_image.id : nil
32
+
33
+ @logger.error("Couldn't find any AMIs matching your specifications. Can't set a default AMI.") unless newest_image_id
34
+ @logger.info("Using #{newest_image_id} as the default AMI.") if newest_image_id
35
+
36
+ return newest_image_id
37
+ end
38
+
39
+ def fetch_eligible_images
40
+ ENV['AWS_REGION'] = @region
41
+ return Aws::EC2::Resource.new.images(@search_options)
42
+ end
43
+
44
+ end
45
+ end
@@ -3,26 +3,40 @@ require 'json'
3
3
  class EC2Bootstrap
4
4
  class Instance
5
5
 
6
+ REQUIRED_KNIFE_EC2_FLAGS = ['image', 'private-ip-address']
7
+
6
8
  attr_accessor :name
7
9
  attr_accessor :knife_ec2_flags
8
10
 
9
- def initialize(instance_name:, knife_ec2_flags:, logger:, domain: nil, json_attributes_file:nil)
11
+ def initialize(instance_name:, knife_ec2_flags:, logger:, dryrun:, domain: nil, json_attributes_file:nil, image: nil, cloud_config: nil)
10
12
  @name = instance_name
11
- @json_attributes_file = json_attributes_file
12
- @knife_ec2_flags = build_knife_ec2_flags_hash(knife_ec2_flags)
13
13
  @logger = logger
14
+
15
+ @logger.debug("Instance name: #{@name}")
16
+
17
+ @dryrun = dryrun
18
+ @json_attributes_file = json_attributes_file
19
+ @image = image
14
20
  @domain = domain
21
+
22
+ @knife_ec2_flags = build_knife_ec2_flags_hash(knife_ec2_flags, cloud_config)
15
23
  end
16
24
 
17
- def build_knife_ec2_flags_hash(knife_ec2_flags)
18
- knife_ec2_flags['json-attributes'] = "'#{self.load_json_attributes(@json_attributes_file)}'" if @json_attributes_file
25
+ def build_knife_ec2_flags_hash(knife_ec2_flags, cloud_config)
26
+ knife_ec2_flags['json-attributes'] = "'#{self.load_json_attributes(@json_attributes_file)}'" if @json_attributes_file and not knife_ec2_flags['json-attributes']
27
+ knife_ec2_flags['user-data'] = self.generate_cloud_init(cloud_config) if cloud_config and not knife_ec2_flags['user-data']
28
+ knife_ec2_flags['image'] = @image unless knife_ec2_flags['image']
19
29
 
20
30
  additional_knife_flags = {
21
31
  'node-name' => @name,
22
32
  'tags' => "Name=#{@name}"
23
33
  }
24
34
 
25
- return knife_ec2_flags.merge(additional_knife_flags)
35
+ knife_flags_hash = knife_ec2_flags.merge(additional_knife_flags)
36
+
37
+ self.validate_knife_flags(knife_flags_hash)
38
+
39
+ return knife_flags_hash
26
40
  end
27
41
 
28
42
  # Load the JSON and then dump it back out to ensure it's valid JSON.
@@ -32,28 +46,35 @@ class EC2Bootstrap
32
46
  return JSON.dump(JSON.load(File.read(file_path)))
33
47
  end
34
48
 
49
+ # Ensure that all REQUIRED_EC2_FLAGS have values other than nil.
50
+ def validate_knife_flags(given_knife_flags)
51
+ missing_flags = REQUIRED_KNIFE_EC2_FLAGS.select {|flag| given_knife_flags[flag].nil? }
52
+ raise KeyError, "Instance #{@name} is missing one or more required flags. Missing flags are: #{missing_flags}." unless missing_flags.empty?
53
+ return true
54
+ end
55
+
35
56
  def format_knife_shell_command
36
57
  prefix = 'knife ec2 server create '
37
58
  knife_flag_array = @knife_ec2_flags.map {|key, value| ['--' + key, value]}.flatten.compact
38
59
  return prefix + knife_flag_array.join(' ')
39
60
  end
40
61
 
41
- def generate_cloud_config(cloud_config, dryrun)
62
+ def generate_cloud_init(cloud_config)
42
63
  cloud_config['hostname'] = @name
43
64
  cloud_config['fqdn'] = "#{@name}.#{@domain}" if @domain
44
65
 
45
66
  formatted_cloud_config = cloud_config.to_yaml.gsub('---', '#cloud-config')
46
67
  cloud_config_path = "cloud_config_#{@name}.txt"
47
68
 
48
- if dryrun
69
+ if @dryrun
49
70
  msg = "If this weren't a dry run, I would write the following contents to #{cloud_config_path}:\n#{formatted_cloud_config}"
50
- @logger.debug(msg)
71
+ @logger.info(msg)
51
72
  else
52
73
  self.write_cloud_config_to_file(cloud_config_path, formatted_cloud_config)
53
- @logger.debug("Wrote cloud config to #{cloud_config_path}.")
74
+ @logger.info("Wrote cloud config to #{cloud_config_path}.")
54
75
  end
55
76
 
56
- @knife_ec2_flags['user-data'] = cloud_config_path
77
+ return cloud_config_path
57
78
  end
58
79
 
59
80
  def write_cloud_config_to_file(path, contents)
@@ -1,3 +1,3 @@
1
1
  module Ec2Bootstrap
2
- VERSION = "1.0.0"
2
+ VERSION = "1.1.0"
3
3
  end
@@ -6,7 +6,8 @@ describe 'EC2Bootstrap::Instance' do
6
6
  {
7
7
  'availability-zone' => 'us-east-1a',
8
8
  'environment' => 'production',
9
- 'flavor' => 'm4.large'
9
+ 'flavor' => 'm4.large',
10
+ 'private-ip-address' => '255.255.255.255'
10
11
  }
11
12
  end
12
13
 
@@ -16,28 +17,72 @@ describe 'EC2Bootstrap::Instance' do
16
17
  logger
17
18
  end
18
19
 
19
- let(:instance) do
20
- EC2Bootstrap::InstanceMock.new(
20
+ let(:default_image) do
21
+ 'ami-11111111'
22
+ end
23
+
24
+ let(:instance_args) do
25
+ {
21
26
  instance_name: 'pumpkin',
22
27
  domain: 'chocolate.muffins.com',
23
28
  knife_ec2_flags: knife_flags_hash,
24
- logger: logger
25
- )
29
+ logger: logger,
30
+ image: default_image,
31
+ dryrun: true
32
+ }
26
33
  end
27
34
 
28
- it 'properly formats the knife shell command' do
29
- knife_command = 'knife ec2 server create --availability-zone us-east-1a --environment production --flavor m4.large'
35
+ context 'picking an image' do
36
+
37
+ it "uses the default AMI if it doesn't have one set" do
38
+ instance = EC2Bootstrap::InstanceMock.new(instance_args)
39
+ expect(instance.knife_ec2_flags['image']).to eq(default_image)
40
+ end
41
+
42
+ it "doesn't override an existing image flag with the default" do
43
+ image = 'ami-12345678'
44
+ knife_flags_hash_with_image_flag = knife_flags_hash.merge({'image' => image})
45
+ instance_args_with_image_flag = instance_args.merge({knife_ec2_flags: knife_flags_hash_with_image_flag})
46
+ instance = EC2Bootstrap::InstanceMock.new(instance_args_with_image_flag)
47
+ expect(instance.knife_ec2_flags['image']).to eq(image)
48
+ end
49
+
50
+ it "raises a KeyError if there is no image" do
51
+ instance_args_without_default_image = instance_args.reject {|k,v| k == :image}
52
+ expect {EC2Bootstrap::InstanceMock.new(instance_args_without_default_image)}.to raise_error(KeyError)
53
+ end
30
54
 
31
- expect(instance.format_knife_shell_command).to include(knife_command)
32
55
  end
33
56
 
34
- it 'can generate its own cloud config' do
35
- cloud_config = {
36
- 'manage_etc_hosts': 'true',
37
- 'bootcmd': ['do stuff', 'do some more stuff']
38
- }
57
+ context 'generating cloud config' do
58
+
59
+ let(:default_cloud_config) do
60
+ {
61
+ 'manage_etc_hosts': 'true',
62
+ 'bootcmd': ['do stuff', 'do some more stuff']
63
+ }
64
+ end
65
+
66
+ it "generates a cloud config file if one isn't set and default cloud config exists" do
67
+ instance = EC2Bootstrap::InstanceMock.new(instance_args.merge({cloud_config: default_cloud_config}))
68
+ expect(instance.knife_ec2_flags['user-data']).to be_a(String)
69
+ end
70
+
71
+ it "doesn't override existing cloud config with the default" do
72
+ cloud_init_file = 'my_custom_cloud_config.txt'
73
+ knife_flags_hash_with_user_data_flag = knife_flags_hash.merge({'user-data' => cloud_init_file})
74
+ instance_args_with_user_data_flag = instance_args.merge({knife_ec2_flags: knife_flags_hash_with_user_data_flag, cloud_config: default_cloud_config})
75
+ instance = EC2Bootstrap::InstanceMock.new(instance_args_with_user_data_flag)
76
+ expect(instance.knife_ec2_flags['user-data']).to eq(cloud_init_file)
77
+ end
78
+
79
+ end
39
80
 
40
- expect(instance.generate_cloud_config(cloud_config, false)).to eq("cloud_config_#{instance.name}.txt")
81
+ it 'properly formats the knife shell command' do
82
+ knife_command = 'knife ec2 server create --availability-zone us-east-1a --environment production --flavor m4.large'
83
+
84
+ instance = EC2Bootstrap::InstanceMock.new(instance_args)
85
+ expect(instance.format_knife_shell_command).to include(knife_command)
41
86
  end
42
87
 
43
88
  end
@@ -2,8 +2,19 @@ require_relative 'spec_helper'
2
2
 
3
3
  describe 'EC2Bootstrap' do
4
4
 
5
+ let(:knife_flags) do
6
+ {
7
+ 'image' => 'ami-12345678',
8
+ 'private-ip-address' => '255.255.255.255'
9
+ }
10
+ end
11
+
5
12
  let(:yaml_content) do
6
13
  {
14
+ 'default_ami' => {
15
+ 'region' => 'us-west-1',
16
+ 'owners' => ['111111111111']
17
+ },
7
18
  'cloud_config' => {
8
19
  'manage_etc_hosts' => 'true',
9
20
  'bootcmd' => ['do stuff', 'do some more stuff']
@@ -11,17 +22,17 @@ describe 'EC2Bootstrap' do
11
22
  'instances' => [
12
23
  {
13
24
  'instance_name' => 'cat',
14
- 'knife_ec2_flags' => {},
25
+ 'knife_ec2_flags' => knife_flags,
15
26
  'domain' => 'cats.com'
16
27
  },
17
28
  {
18
29
  'instance_name' => 'mouse',
19
30
  'json_attributes_file' => {},
20
- 'knife_ec2_flags' => {}
31
+ 'knife_ec2_flags' => knife_flags
21
32
  },
22
33
  {
23
34
  'instance_name' => 'whale',
24
- 'knife_ec2_flags' => {}
35
+ 'knife_ec2_flags' => knife_flags
25
36
  }
26
37
  ]
27
38
  }
@@ -32,63 +43,45 @@ describe 'EC2Bootstrap' do
32
43
  it "raises a KeyError if the yaml content lacks an 'instances' key" do
33
44
  yaml = yaml_content.reject {|k,v| k == 'instances'}
34
45
 
35
- expect {EC2BootstrapMock.from_config(yaml, false)}.to raise_error(KeyError)
46
+ expect {EC2BootstrapMock.from_config(yaml)}.to raise_error(KeyError)
36
47
  end
37
48
 
38
49
  it "raises a TypeError if the 'instances' value is not an array of hashes" do
39
50
  yaml = yaml_content.merge({'instances' => ['a thing', 'another thing']})
40
51
 
41
- expect {EC2BootstrapMock.from_config(yaml, false)}.to raise_error(TypeError)
52
+ expect {EC2BootstrapMock.from_config(yaml)}.to raise_error(TypeError)
53
+ end
54
+
55
+ it "raises a TypeError if 'default_ami' exists but is not a hash" do
56
+ yaml = yaml_content.merge({'default_ami' => ['an', 'array']})
57
+
58
+ expect {EC2BootstrapMock.from_config(yaml)}.to raise_error(TypeError)
42
59
  end
43
60
 
44
61
  it 'loads successfully if the yaml content is properly formatted' do
45
- bootstrap = EC2BootstrapMock.from_config(yaml_content, false)
62
+ bootstrap = EC2BootstrapMock.from_config(yaml_content)
46
63
 
47
64
  expect(bootstrap.cloud_config).to eq(yaml_content['cloud_config'])
48
- expect(bootstrap.instances).to be_an(Array)
49
- expect(bootstrap.instances.first).to be_a(EC2Bootstrap::InstanceMock)
50
- expect(bootstrap.dryrun).to be_falsey
65
+ expect(bootstrap.instances_config).to eq(yaml_content['instances'])
66
+ expect(bootstrap.default_ami_config).to eq(yaml_content['default_ami'])
51
67
  end
52
68
 
53
69
  end
54
70
 
55
- context 'creating instances' do
56
- context 'generating cloud config' do
57
-
58
- it "doesn't generate cloud config if it wasn't included at the top level of the yaml config" do
59
- yaml = yaml_content.reject {|k,v| k == 'cloud_config'}
60
- bootstrap = EC2BootstrapMock.from_config(yaml, false)
61
- instance = bootstrap.instances.first
62
-
63
- expect(instance).to_not receive(:generate_cloud_config)
64
- bootstrap.create_instances
65
- end
66
-
67
- it 'generates cloud config if it was included at the top level in the yaml config' do
68
- bootstrap = EC2BootstrapMock.from_config(yaml_content, false)
69
- instance = bootstrap.instances.first
71
+ context 'shelling out knife EC2 command' do
70
72
 
71
- expect(instance).to receive(:generate_cloud_config)
72
- bootstrap.create_instances
73
- end
73
+ it "doesn't shell out if it's a dryrun" do
74
+ bootstrap = EC2BootstrapMock.from_config(yaml_content, dryrun: true)
74
75
 
76
+ expect(bootstrap).to_not receive(:shell_out_command)
77
+ bootstrap.create_instances
75
78
  end
76
79
 
77
- context 'shelling out knife EC2 command' do
78
-
79
- it "doesn't shell out if it's a dryrun" do
80
- bootstrap = EC2BootstrapMock.from_config(yaml_content, true)
81
-
82
- expect(bootstrap).to_not receive(:shell_out_command)
83
- bootstrap.create_instances
84
- end
85
-
86
- it "shells out if it's not a dryrun" do
87
- bootstrap = EC2BootstrapMock.from_config(yaml_content, false)
80
+ it "shells out if it's not a dryrun" do
81
+ bootstrap = EC2BootstrapMock.from_config(yaml_content, dryrun: false)
88
82
 
89
- expect(bootstrap).to receive(:shell_out_command)
90
- bootstrap.create_instances
91
- end
83
+ expect(bootstrap).to receive(:shell_out_command)
84
+ bootstrap.create_instances
92
85
  end
93
86
  end
94
87
 
data/spec/spec_helper.rb CHANGED
@@ -3,6 +3,16 @@ require 'rspec'
3
3
 
4
4
  class EC2BootstrapMock < EC2Bootstrap
5
5
 
6
+ def new_logger(verbose)
7
+ logger = Logger.new(STDOUT)
8
+ logger.level = Logger::ERROR
9
+ return logger
10
+ end
11
+
12
+ def ami_class
13
+ return AMIMock
14
+ end
15
+
6
16
  def instance_class
7
17
  return InstanceMock
8
18
  end
@@ -25,9 +35,21 @@ class EC2Bootstrap
25
35
  end
26
36
 
27
37
  end
38
+
39
+ class AMIMock < AMI
40
+
41
+ def fetch_eligible_images
42
+ ids = ['ami-01234567', 'ami-abcdef12', 'ami-87654321']
43
+ return ids.map {|id| Struct::AmazonAMI.new(id, Time.now.to_s)}
44
+ end
45
+
46
+ end
28
47
  end
29
48
 
30
49
  RSpec.configure do |config|
31
50
  config.run_all_when_everything_filtered = true
32
51
  config.filter_run :focus
52
+ config.before(:suite) do
53
+ Struct.new('AmazonAMI', :id, :creation_date)
54
+ end
33
55
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ec2_bootstrap
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.0
4
+ version: 1.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Cozy Services Ltd.
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2016-03-18 00:00:00.000000000 Z
12
+ date: 2016-04-04 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: bundler
@@ -81,6 +81,20 @@ dependencies:
81
81
  - - "~>"
82
82
  - !ruby/object:Gem::Version
83
83
  version: '0.12'
84
+ - !ruby/object:Gem::Dependency
85
+ name: aws-sdk
86
+ requirement: !ruby/object:Gem::Requirement
87
+ requirements:
88
+ - - "~>"
89
+ - !ruby/object:Gem::Version
90
+ version: '2'
91
+ type: :runtime
92
+ prerelease: false
93
+ version_requirements: !ruby/object:Gem::Requirement
94
+ requirements:
95
+ - - "~>"
96
+ - !ruby/object:Gem::Version
97
+ version: '2'
84
98
  description: Bootstrap EC2 instances with custom config.
85
99
  email:
86
100
  - opensource@cozy.co
@@ -94,6 +108,7 @@ files:
94
108
  - Rakefile
95
109
  - bin/ec2_bootstrap
96
110
  - lib/ec2_bootstrap.rb
111
+ - lib/ec2_bootstrap/ami.rb
97
112
  - lib/ec2_bootstrap/instance.rb
98
113
  - lib/ec2_bootstrap/version.rb
99
114
  - spec/ec2_bootstrap/instance_spec.rb