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 +4 -4
- data/README.md +22 -5
- data/bin/ec2_bootstrap +1 -1
- data/lib/ec2_bootstrap.rb +42 -21
- data/lib/ec2_bootstrap/ami.rb +45 -0
- data/lib/ec2_bootstrap/instance.rb +32 -11
- data/lib/ec2_bootstrap/version.rb +1 -1
- data/spec/ec2_bootstrap/instance_spec.rb +59 -14
- data/spec/ec2_bootstrap_spec.rb +34 -41
- data/spec/spec_helper.rb +22 -0
- metadata +17 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 2edaecb9c4791dfaf2c8135fd9cfa8409f97d7c4
|
4
|
+
data.tar.gz: e40fbe8bcb044cc1671a3b684c95222c95ca5f26
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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
|
-
|
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
|
-
|
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
|
30
|
-
2.
|
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
|
-
|
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
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
|
15
|
-
@logger =
|
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
|
-
|
25
|
-
|
26
|
-
|
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
|
-
|
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
|
39
|
-
|
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.
|
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
|
-
|
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
|
-
|
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
|
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.
|
71
|
+
@logger.info(msg)
|
51
72
|
else
|
52
73
|
self.write_cloud_config_to_file(cloud_config_path, formatted_cloud_config)
|
53
|
-
@logger.
|
74
|
+
@logger.info("Wrote cloud config to #{cloud_config_path}.")
|
54
75
|
end
|
55
76
|
|
56
|
-
|
77
|
+
return cloud_config_path
|
57
78
|
end
|
58
79
|
|
59
80
|
def write_cloud_config_to_file(path, contents)
|
@@ -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(:
|
20
|
-
|
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
|
-
|
29
|
-
|
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
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
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
|
-
|
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
|
data/spec/ec2_bootstrap_spec.rb
CHANGED
@@ -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
|
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
|
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
|
62
|
+
bootstrap = EC2BootstrapMock.from_config(yaml_content)
|
46
63
|
|
47
64
|
expect(bootstrap.cloud_config).to eq(yaml_content['cloud_config'])
|
48
|
-
expect(bootstrap.
|
49
|
-
expect(bootstrap.
|
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 '
|
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
|
-
|
72
|
-
|
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
|
-
|
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
|
-
|
90
|
-
|
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.
|
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-
|
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
|