rivet 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +2 -0
- data/.travis.yml +9 -0
- data/CHANGELOG.md +7 -0
- data/Gemfile +10 -0
- data/Gemfile.lock +31 -0
- data/LICENSE +202 -0
- data/README.md +128 -0
- data/Rakefile +9 -0
- data/bin/rivet +43 -0
- data/lib/rivet/autoscale.rb +125 -0
- data/lib/rivet/aws_utils.rb +74 -0
- data/lib/rivet/bootstrap.rb +62 -0
- data/lib/rivet/client.rb +29 -0
- data/lib/rivet/deep_merge.rb +27 -0
- data/lib/rivet/launch_config.rb +90 -0
- data/lib/rivet/logger.rb +52 -0
- data/lib/rivet/utils.rb +68 -0
- data/lib/rivet/version.rb +4 -0
- data/lib/rivet.rb +18 -0
- data/rivet.gemspec +31 -0
- data/spec/rivet_bootstrap_spec.rb +69 -0
- data/spec/rivet_launch_config_spec.rb +37 -0
- data/spec/rivet_spec_setup.rb +62 -0
- data/spec/rivet_util_spec.rb +123 -0
- metadata +124 -0
@@ -0,0 +1,62 @@
|
|
1
|
+
module Rivet
|
2
|
+
class Bootstrap
|
3
|
+
TEMPLATE_SUB_DIR = "bootstrap"
|
4
|
+
|
5
|
+
attr_reader :gems, :run_list, :template, :environment
|
6
|
+
attr_reader :template_path, :chef_command, :chef_organization
|
7
|
+
|
8
|
+
def initialize(bootstrap_definition = Hash.new)
|
9
|
+
ivars = [
|
10
|
+
'gems','run_list','template','environment',
|
11
|
+
'config_dir','chef_organization']
|
12
|
+
|
13
|
+
ivars.each do |i|
|
14
|
+
if bootstrap_definition.has_key?(i)
|
15
|
+
instance_variable_set("@#{i}",bootstrap_definition[i])
|
16
|
+
end
|
17
|
+
end unless bootstrap_definition.nil?
|
18
|
+
|
19
|
+
@config_dir ||= "."
|
20
|
+
@template ||= "default.erb"
|
21
|
+
|
22
|
+
set_calculated_attrs
|
23
|
+
end
|
24
|
+
|
25
|
+
def user_data
|
26
|
+
@user_data ||= generate_user_data
|
27
|
+
end
|
28
|
+
|
29
|
+
protected
|
30
|
+
|
31
|
+
def set_calculated_attrs
|
32
|
+
@template_path = File.join(@config_dir,TEMPLATE_SUB_DIR)
|
33
|
+
@chef_command = "/usr/bin/chef-client -j /etc/chef/first-boot.json -L /root/first_run.log -E #{@environment}"
|
34
|
+
@secret_file = File.join(@config_dir,"encrypted_data_bag_secret_#{@environment}")
|
35
|
+
@validation_key = File.new(File.join(@config_dir,"#{@chef_organization}-validator.pem")).read
|
36
|
+
end
|
37
|
+
|
38
|
+
def generate_user_data
|
39
|
+
config_content = "log_level :info\n"
|
40
|
+
config_content << "log_location STDOUT\n"
|
41
|
+
config_content << "environment #{environment}\n"
|
42
|
+
config_content << "chef_server_url 'https://api.opscode.com/organizations/#{chef_organization}'\n"
|
43
|
+
config_content << "validation_client_name '#{chef_organization}-validator'\n"
|
44
|
+
|
45
|
+
install_gems = String.new
|
46
|
+
|
47
|
+
gems.each do |gem|
|
48
|
+
if gem.size > 1
|
49
|
+
install_gems << "gem install #{gem[0]} -v #{gem[1]} --no-rdoc --no-ri\n"
|
50
|
+
else
|
51
|
+
install_gems << "gem install #{gem[0]} --no-rdoc --no-ri\n"
|
52
|
+
end
|
53
|
+
end unless gems.nil?
|
54
|
+
|
55
|
+
first_boot = { :run_list => @run_list.join(",") }.to_json unless @run_list.nil?
|
56
|
+
|
57
|
+
template = ERB.new File.new(File.join(@template_path,@template)).read
|
58
|
+
template.result(binding)
|
59
|
+
end
|
60
|
+
|
61
|
+
end
|
62
|
+
end
|
data/lib/rivet/client.rb
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
module Rivet
|
2
|
+
class Client
|
3
|
+
def initialize
|
4
|
+
end
|
5
|
+
|
6
|
+
def run(options)
|
7
|
+
AwsUtils.set_aws_credentials(options[:profile])
|
8
|
+
Rivet::Log.level(options[:log_level])
|
9
|
+
Rivet::Utils.ensure_minimum_setup
|
10
|
+
|
11
|
+
group_def = Rivet::Utils.get_definition(options[:group])
|
12
|
+
|
13
|
+
Rivet::Utils.die "The #{options[:group]} definition doesn't exist" unless group_def
|
14
|
+
|
15
|
+
Rivet::Log.info("Checking #{options[:group]} autoscaling definition")
|
16
|
+
autoscale_def = Rivet::Autoscale.new(options[:group],group_def)
|
17
|
+
autoscale_def.show_differences
|
18
|
+
|
19
|
+
if options[:sync]
|
20
|
+
autoscale_def.sync
|
21
|
+
else
|
22
|
+
Rivet::Log.info("use the -s [--sync] flag to sync changes")
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
@@ -0,0 +1,27 @@
|
|
1
|
+
class Hash
|
2
|
+
# Returns a new hash with +self+ and +other_hash+ merged recursively.
|
3
|
+
#
|
4
|
+
# h1 = { x: { y: [4,5,6] }, z: [7,8,9] }
|
5
|
+
# h2 = { x: { y: [7,8,9] }, z: 'xyz' }
|
6
|
+
#
|
7
|
+
# h1.deep_merge(h2) #=> {x: {y: [7, 8, 9]}, z: "xyz"}
|
8
|
+
# h2.deep_merge(h1) #=> {x: {y: [4, 5, 6]}, z: [7, 8, 9]}
|
9
|
+
# h1.deep_merge(h2) { |key, old, new| Array.wrap(old) + Array.wrap(new) }
|
10
|
+
# #=> {:x=>{:y=>[4, 5, 6, 7, 8, 9]}, :z=>[7, 8, 9, "xyz"]}
|
11
|
+
def deep_merge(other_hash, &block)
|
12
|
+
dup.deep_merge!(other_hash, &block)
|
13
|
+
end
|
14
|
+
|
15
|
+
# Same as +deep_merge+, but modifies +self+.
|
16
|
+
def deep_merge!(other_hash, &block)
|
17
|
+
other_hash.each_pair do |k,v|
|
18
|
+
tv = self[k]
|
19
|
+
if tv.is_a?(Hash) && v.is_a?(Hash)
|
20
|
+
self[k] = tv.deep_merge(v, &block)
|
21
|
+
else
|
22
|
+
self[k] = block && tv ? block.call(k, tv, v) : v
|
23
|
+
end
|
24
|
+
end
|
25
|
+
self
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,90 @@
|
|
1
|
+
module Rivet
|
2
|
+
class LaunchConfig
|
3
|
+
|
4
|
+
LC_ATTRIBUTES = ['key_name','image_id','instance_type','security_groups','bootstrap']
|
5
|
+
|
6
|
+
LC_ATTRIBUTES.each do |a|
|
7
|
+
attr_reader a.to_sym
|
8
|
+
end
|
9
|
+
|
10
|
+
attr_reader :id_prefix
|
11
|
+
|
12
|
+
def initialize(spec,id_prefix="rivet_")
|
13
|
+
@id_prefix = id_prefix
|
14
|
+
|
15
|
+
LC_ATTRIBUTES.each do |a|
|
16
|
+
|
17
|
+
if respond_to? "normalize_#{a}".to_sym
|
18
|
+
spec[a] = self.send("normalize_#{a.to_sym}",spec[a])
|
19
|
+
end
|
20
|
+
|
21
|
+
instance_variable_set("@#{a}",spec[a])
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def user_data
|
26
|
+
@user_data ||= Bootstrap.new(bootstrap).user_data
|
27
|
+
end
|
28
|
+
|
29
|
+
def identity
|
30
|
+
@identity ||= generate_identity
|
31
|
+
end
|
32
|
+
|
33
|
+
def save
|
34
|
+
AwsUtils.verify_security_groups(security_groups)
|
35
|
+
|
36
|
+
lc_collection = AWS::AutoScaling.new().launch_configurations
|
37
|
+
|
38
|
+
if lc_collection[identity].exists?
|
39
|
+
Rivet::Log.info("Launch configuration #{identity} already exists in AWS")
|
40
|
+
else
|
41
|
+
options = { :key_pair => key_name, :security_groups => security_groups, :user_data => user_data}
|
42
|
+
Rivet::Log.info("Saving launch configuration #{identity} to AWS")
|
43
|
+
Rivet::Log.debug("Launch Config options:\n #{options.inspect}")
|
44
|
+
lc_collection.create(identity,image_id,instance_type, options)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
protected
|
49
|
+
|
50
|
+
def build_identity_string
|
51
|
+
identity = LC_ATTRIBUTES.inject(String.new) do |accum,attribute|
|
52
|
+
if attribute != 'bootstrap'
|
53
|
+
attr_value = self.send(attribute.to_sym) ? self.send(attribute.to_sym) : "\0"
|
54
|
+
attr_value = attr_value.join("\t") if attr_value.respond_to?(:join)
|
55
|
+
accum << attribute.to_s
|
56
|
+
accum << Base64.encode64(attr_value)
|
57
|
+
else
|
58
|
+
accum << attribute.to_s
|
59
|
+
accum << Base64.encode64(user_data ? user_data : "\0")
|
60
|
+
end
|
61
|
+
accum
|
62
|
+
end
|
63
|
+
identity
|
64
|
+
end
|
65
|
+
|
66
|
+
def generate_identity
|
67
|
+
@id_prefix + Digest::SHA1.hexdigest(build_identity_string)
|
68
|
+
end
|
69
|
+
|
70
|
+
def old_generate_identity
|
71
|
+
identity = LC_ATTRIBUTES.inject({}) do |ident_hash,attribute|
|
72
|
+
if attribute != 'bootstrap'
|
73
|
+
Rivet::Log.debug("Adding #{attribute} : #{self.send(attribute.to_sym)} to identity hash for LaunchConfig")
|
74
|
+
ident_hash[attribute] = self.send(attribute.to_sym)
|
75
|
+
else
|
76
|
+
Rivet::Log.debug("Adding user_data to identity hash for LaunchConfig:\n#{user_data} ")
|
77
|
+
ident_hash[attribute] = user_data
|
78
|
+
end
|
79
|
+
ident_hash
|
80
|
+
end
|
81
|
+
@id_prefix + Digest::SHA1.hexdigest(Marshal::dump(identity))
|
82
|
+
end
|
83
|
+
|
84
|
+
def normalize_security_groups(groups)
|
85
|
+
groups.sort
|
86
|
+
end
|
87
|
+
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
data/lib/rivet/logger.rb
ADDED
@@ -0,0 +1,52 @@
|
|
1
|
+
module Rivet
|
2
|
+
|
3
|
+
module Log
|
4
|
+
|
5
|
+
def self.write(level,message)
|
6
|
+
@@log ||= SimpleLogger.instance
|
7
|
+
@@log.send(level.to_sym) { message }
|
8
|
+
end
|
9
|
+
|
10
|
+
def self.info(message)
|
11
|
+
write('info',message)
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.debug(message)
|
15
|
+
write('debug',message)
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.fatal(message)
|
19
|
+
write('fatal',message)
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.warn(message)
|
23
|
+
write('warn',message)
|
24
|
+
end
|
25
|
+
|
26
|
+
def self.level(level)
|
27
|
+
@@log ||= SimpleLogger.instance
|
28
|
+
@@log.level = level
|
29
|
+
end
|
30
|
+
|
31
|
+
class SimpleLogger< Logger
|
32
|
+
include Singleton
|
33
|
+
|
34
|
+
def initialize
|
35
|
+
@dev = Logger::LogDevice.new(STDOUT)
|
36
|
+
super @dev
|
37
|
+
@progname = "Rivet"
|
38
|
+
@formatter = proc do |sev,datetime,name,msg|
|
39
|
+
"[#{name}] [#{datetime}] [#{sev}]: #{msg}\n"
|
40
|
+
end
|
41
|
+
@datetime_format
|
42
|
+
end
|
43
|
+
|
44
|
+
def close
|
45
|
+
@dev.close
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
|
data/lib/rivet/utils.rb
ADDED
@@ -0,0 +1,68 @@
|
|
1
|
+
module Rivet
|
2
|
+
module Utils
|
3
|
+
AUTOSCALE_DIR = "autoscale"
|
4
|
+
|
5
|
+
def self.die(level = 'fatal',message)
|
6
|
+
Rivet::Log.write(level,message)
|
7
|
+
exit
|
8
|
+
end
|
9
|
+
|
10
|
+
def self.ensure_minimum_setup
|
11
|
+
if Dir.exists?(AUTOSCALE_DIR)
|
12
|
+
true
|
13
|
+
else
|
14
|
+
Rivet::Log.info("Creating #{AUTOSCALE_DIR}")
|
15
|
+
Dir.mkdir(AUTOSCALE_DIR)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
# This returns the merged definition given a group
|
20
|
+
|
21
|
+
def self.get_definition(group)
|
22
|
+
defaults = consume_defaults
|
23
|
+
group_def = load_definition(group)
|
24
|
+
if defaults && group_def
|
25
|
+
group_def = defaults.deep_merge(group_def)
|
26
|
+
end
|
27
|
+
group_def ? group_def : false
|
28
|
+
end
|
29
|
+
|
30
|
+
# Gobbles up the defaults file from YML, returns the hash or false if empty
|
31
|
+
|
32
|
+
def self.consume_defaults(autoscale_dir = AUTOSCALE_DIR)
|
33
|
+
defaults_file = File.join(autoscale_dir,"defaults.yml")
|
34
|
+
if File.exists?(defaults_file)
|
35
|
+
parsed = begin
|
36
|
+
Rivet::Log.debug("Consuming defaults from #{defaults_file}")
|
37
|
+
YAML.load(File.open(defaults_file))
|
38
|
+
rescue ArgumentError => e
|
39
|
+
Rivet::Log.fatal("Could not parse YAML from #{defaults_file}: #{e.message}")
|
40
|
+
end
|
41
|
+
parsed
|
42
|
+
else
|
43
|
+
false
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
# This loads the given definition from it's YML file, returns the hash or
|
48
|
+
# false if empty
|
49
|
+
|
50
|
+
def self.load_definition(name)
|
51
|
+
definition_dir = File.join(AUTOSCALE_DIR,name)
|
52
|
+
conf_file = File.join(definition_dir,"conf.yml")
|
53
|
+
if Dir.exists?(definition_dir) && File.exists?(conf_file)
|
54
|
+
Rivet::Log.debug("Loading definition for #{name} from #{conf_file}")
|
55
|
+
parsed = begin
|
56
|
+
YAML.load(File.open(conf_file))
|
57
|
+
rescue
|
58
|
+
Rivet::Log.fatal("Could not parse YAML from #{conf_file}: #{e.message}")
|
59
|
+
end
|
60
|
+
parsed ? parsed : { }
|
61
|
+
else
|
62
|
+
false
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
data/lib/rivet.rb
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
require 'aws-sdk'
|
2
|
+
require 'base64'
|
3
|
+
require 'digest/sha1'
|
4
|
+
require 'erb'
|
5
|
+
require 'json'
|
6
|
+
require 'logger'
|
7
|
+
require 'optparse'
|
8
|
+
require 'singleton'
|
9
|
+
require 'yaml'
|
10
|
+
|
11
|
+
require_relative 'rivet/deep_merge'
|
12
|
+
require_relative 'rivet/logger'
|
13
|
+
require_relative 'rivet/utils'
|
14
|
+
require_relative 'rivet/aws_utils'
|
15
|
+
require_relative 'rivet/launch_config'
|
16
|
+
require_relative 'rivet/autoscale'
|
17
|
+
require_relative 'rivet/bootstrap'
|
18
|
+
require_relative 'rivet/client'
|
data/rivet.gemspec
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
|
3
|
+
lib = File.expand_path('../lib/',__FILE__)
|
4
|
+
$:.unshift lib unless $:.include?(lib)
|
5
|
+
require 'rivet/version'
|
6
|
+
|
7
|
+
Gem::Specification.new do |spec|
|
8
|
+
spec.name = 'rivet'
|
9
|
+
spec.version = Rivet::VERSION
|
10
|
+
spec.licenses = ['Apache2']
|
11
|
+
spec.authors = ['Brian Bianco']
|
12
|
+
spec.email = ['brian.bianco@gmail.com']
|
13
|
+
spec.homepage = 'http://www.github.com/brianbianco/rivet'
|
14
|
+
spec.summary = %q{A tool for managing autoscaling groups}
|
15
|
+
spec.description = %q{Rivet allows you to define autoscaling groups and launch configurations as YAML and can SYNC that to AWS}
|
16
|
+
|
17
|
+
spec.required_ruby_version = '>= 1.9.1'
|
18
|
+
spec.required_rubygems_version = '>= 1.3.6'
|
19
|
+
|
20
|
+
spec.files = `git ls-files`.split($/)
|
21
|
+
spec.test_files = spec.files.grep(%r{^spec/})
|
22
|
+
|
23
|
+
spec.executables = %w(rivet)
|
24
|
+
spec.require_paths = ["lib"]
|
25
|
+
|
26
|
+
spec.add_dependency "aws-sdk", "~> 1.24.0"
|
27
|
+
spec.add_development_dependency "rake", ">= 10.1.0"
|
28
|
+
spec.add_development_dependency "rspec", "~> 2.14.1"
|
29
|
+
end
|
30
|
+
|
31
|
+
|
@@ -0,0 +1,69 @@
|
|
1
|
+
require_relative './rivet_spec_setup'
|
2
|
+
|
3
|
+
include SpecHelpers
|
4
|
+
|
5
|
+
describe 'rivet bootstrap' do
|
6
|
+
let (:bootstrap) { Rivet::Bootstrap.new(SpecHelpers::AUTOSCALE_DEF['bootstrap']) }
|
7
|
+
let (:bootstrap_def) { SpecHelpers::AUTOSCALE_DEF['bootstrap'] }
|
8
|
+
|
9
|
+
tempdir_context 'with all necessary files in place' do
|
10
|
+
before do
|
11
|
+
|
12
|
+
|
13
|
+
validator_file = File.join(
|
14
|
+
bootstrap_def['config_dir'],
|
15
|
+
"#{bootstrap_def['environment']}-validator.pem")
|
16
|
+
|
17
|
+
template_dir = File.join(
|
18
|
+
bootstrap_def['config_dir'],
|
19
|
+
Rivet::Bootstrap::TEMPLATE_SUB_DIR)
|
20
|
+
|
21
|
+
template_file = File.join(template_dir,bootstrap_def['template'])
|
22
|
+
|
23
|
+
FileUtils.mkdir_p(bootstrap_def['config_dir'])
|
24
|
+
FileUtils.mkdir_p(template_dir)
|
25
|
+
File.open(template_file,'w') { |f| f.write(SpecHelpers::BOOTSTRAP_TEMPLATE) }
|
26
|
+
FileUtils.touch(validator_file)
|
27
|
+
end
|
28
|
+
|
29
|
+
describe "#user_data" do
|
30
|
+
it 'returns a string that contains the chef organization' do
|
31
|
+
org = bootstrap_def['organization']
|
32
|
+
bootstrap.user_data.should =~ /chef_server_url\s*.*#{org}.*/
|
33
|
+
end
|
34
|
+
|
35
|
+
it 'returns a string that contains the environment' do
|
36
|
+
env = bootstrap_def['env']
|
37
|
+
bootstrap.user_data.should =~ /environment\s*.*#{env}.*/
|
38
|
+
end
|
39
|
+
|
40
|
+
it 'returns a string that contains the run_list as json' do
|
41
|
+
run_list = { :run_list => bootstrap_def['run_list'].join(",") }.to_json
|
42
|
+
bootstrap.user_data.should =~ /#{run_list}/
|
43
|
+
end
|
44
|
+
|
45
|
+
it 'returns a string that contains each gem to install' do
|
46
|
+
bootstrap_def['gems'].each do |g|
|
47
|
+
if g.size > 1
|
48
|
+
gem_regexp = /gem\s*install\s*#{g[0]}.*#{g[1]}/
|
49
|
+
else
|
50
|
+
gem_regexp = /gem\s*install\s*#{g[0]}/
|
51
|
+
end
|
52
|
+
bootstrap.user_data.should =~ gem_regexp
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
end
|
57
|
+
|
58
|
+
end
|
59
|
+
|
60
|
+
end
|
61
|
+
|
62
|
+
|
63
|
+
|
64
|
+
|
65
|
+
|
66
|
+
|
67
|
+
|
68
|
+
|
69
|
+
|
@@ -0,0 +1,37 @@
|
|
1
|
+
require_relative './rivet_spec_setup'
|
2
|
+
|
3
|
+
include SpecHelpers
|
4
|
+
|
5
|
+
describe "rivet launch config" do
|
6
|
+
let (:launch_config) { Rivet::LaunchConfig.new(SpecHelpers::AUTOSCALE_DEF) }
|
7
|
+
|
8
|
+
context "with a valid autoscale definition" do
|
9
|
+
before do
|
10
|
+
user_data_mock = double('user_data_mock')
|
11
|
+
user_data_mock.stub(:user_data).and_return("unit_test_user_data")
|
12
|
+
Rivet::Bootstrap.stub(:new).and_return(user_data_mock)
|
13
|
+
end
|
14
|
+
|
15
|
+
describe "#build_identity_string" do
|
16
|
+
it "should return a valid identity_string" do
|
17
|
+
launch_config.send(:build_identity_string).should == SpecHelpers::AUTOSCALE_IDENTITY_STRING
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
describe "#identity" do
|
22
|
+
it "should return a deterministic identity" do
|
23
|
+
launch_config.identity.should == "rivet_#{Digest::SHA1.hexdigest(SpecHelpers::AUTOSCALE_IDENTITY_STRING)}"
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
describe "#normalize_security_groups" do
|
28
|
+
it "returns a sorted array of groups" do
|
29
|
+
unsorted_groups = ['group3','group1','group2']
|
30
|
+
sorted_groups = unsorted_groups.sort
|
31
|
+
returned_groups = launch_config.send(:normalize_security_groups,unsorted_groups)
|
32
|
+
returned_groups.should == sorted_groups
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
require 'rspec'
|
2
|
+
require 'fileutils'
|
3
|
+
require 'tempfile'
|
4
|
+
require 'pathname'
|
5
|
+
require 'base64'
|
6
|
+
require_relative '../lib/rivet'
|
7
|
+
|
8
|
+
Rivet::Log.level(Logger::FATAL)
|
9
|
+
|
10
|
+
|
11
|
+
module SpecHelpers
|
12
|
+
|
13
|
+
|
14
|
+
BOOTSTRAP_TEMPLATE = '<%= install_gems %>'\
|
15
|
+
'<%= config_content %>'\
|
16
|
+
'<%= first_boot %>'\
|
17
|
+
"\n"\
|
18
|
+
'<%= chef_command %>'
|
19
|
+
|
20
|
+
|
21
|
+
AUTOSCALE_DEF = {
|
22
|
+
'min_size' => 1,
|
23
|
+
'max_size' => 3,
|
24
|
+
'region' => 'us-west-2',
|
25
|
+
'availability_zones' => ['a','b','c'],
|
26
|
+
'key_name' => 'UnitTests',
|
27
|
+
'instance_type' => 'm1.large',
|
28
|
+
'security_groups' => ['unit_tests1','unit_tests2'],
|
29
|
+
'image_id' => 'ami-12345678',
|
30
|
+
'bootstrap' => {
|
31
|
+
'chef_organization' => 'unit_tests',
|
32
|
+
'template' => 'default.erb',
|
33
|
+
'config_dir' => 'unit_tests',
|
34
|
+
'environment' => 'unit_tests',
|
35
|
+
'gems' => [ ['gem1','0.0.1'],['gem2','0.0.2'] ],
|
36
|
+
'run_list' => ['unit_tests']
|
37
|
+
}
|
38
|
+
}
|
39
|
+
|
40
|
+
AUTOSCALE_IDENTITY_STRING = "key_name#{Base64.encode64(AUTOSCALE_DEF['key_name'])}"\
|
41
|
+
"image_id#{Base64.encode64(AUTOSCALE_DEF['image_id'])}"\
|
42
|
+
"instance_type#{Base64.encode64(AUTOSCALE_DEF['instance_type'])}"\
|
43
|
+
"security_groups#{Base64.encode64(AUTOSCALE_DEF['security_groups'].join("\t"))}"\
|
44
|
+
"bootstrap#{Base64.encode64('unit_test_user_data')}"\
|
45
|
+
|
46
|
+
def tempdir_context(name, &block)
|
47
|
+
context name do
|
48
|
+
before do
|
49
|
+
@origin_dir = Dir.pwd
|
50
|
+
@temp_dir = ::Pathname.new(::File.expand_path(::Dir.mktmpdir))
|
51
|
+
Dir.chdir @temp_dir
|
52
|
+
end
|
53
|
+
|
54
|
+
after do
|
55
|
+
Dir.chdir @origin_dir
|
56
|
+
FileUtils.remove_entry(@temp_dir)
|
57
|
+
end
|
58
|
+
|
59
|
+
instance_eval &block
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
@@ -0,0 +1,123 @@
|
|
1
|
+
require_relative './rivet_spec_setup'
|
2
|
+
|
3
|
+
include SpecHelpers
|
4
|
+
|
5
|
+
definition_name = "unit_test"
|
6
|
+
definition_dir = File.join(Rivet::Utils::AUTOSCALE_DIR,definition_name)
|
7
|
+
launch_config_params = ['ssh_key','instance_size','security_groups','ami','bootstrap']
|
8
|
+
|
9
|
+
defaults_hash = {
|
10
|
+
'min_size' => 0,
|
11
|
+
'max_size' => 0,
|
12
|
+
'region' => 'us-west-2',
|
13
|
+
'zones' => ['a','b','c'],
|
14
|
+
'key_name' => 'unit_tests',
|
15
|
+
'instance_type' => 'm1.large',
|
16
|
+
'security_groups' => ['unit_tests'],
|
17
|
+
'image_id' => 'ami-unit_tests',
|
18
|
+
'bootstrap' => {
|
19
|
+
'run_list' => ['role[unit_tests]']
|
20
|
+
}
|
21
|
+
}
|
22
|
+
|
23
|
+
unit_test_definition_hash = {
|
24
|
+
'min_size' => 1,
|
25
|
+
'max_size' => 5,
|
26
|
+
'bootstrap' => {
|
27
|
+
'run_list' => ['role[merging_test']
|
28
|
+
}
|
29
|
+
}
|
30
|
+
|
31
|
+
describe "rivet utils" do
|
32
|
+
tempdir_context "without an autoscaling directory" do
|
33
|
+
describe "ensure_minimum_setup" do
|
34
|
+
it "creates the autoscale directory if it doesn't exist" do
|
35
|
+
Rivet::Utils.ensure_minimum_setup
|
36
|
+
Dir.exists?(Rivet::Utils::AUTOSCALE_DIR).should be_true
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
tempdir_context "with an autoscaling directory" do
|
42
|
+
before do
|
43
|
+
FileUtils.mkdir_p(Rivet::Utils::AUTOSCALE_DIR)
|
44
|
+
end
|
45
|
+
|
46
|
+
describe "ensure_minimum_setup" do
|
47
|
+
it "should return true" do
|
48
|
+
Rivet::Utils.ensure_minimum_setup.should be_true
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
describe "consume_defaults" do
|
53
|
+
it "should return false" do
|
54
|
+
Rivet::Utils.consume_defaults.should be_false
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
describe "load_definition" do
|
59
|
+
it "should return false" do
|
60
|
+
Rivet::Utils.load_definition("unit_test").should be_false
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
describe "get_definition" do
|
65
|
+
it "should return false" do
|
66
|
+
Rivet::Utils.get_definition("unit_test")
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
context "and with a group directory" do
|
71
|
+
before do
|
72
|
+
FileUtils.mkdir_p definition_dir
|
73
|
+
end
|
74
|
+
|
75
|
+
describe "load_definition" do
|
76
|
+
it "should return false" do
|
77
|
+
Rivet::Utils.load_definition("unit_test").should be_false
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
context "and with a conf.yml" do
|
82
|
+
before do
|
83
|
+
FileUtils.mkdir_p definition_dir
|
84
|
+
File.open(File.join(definition_dir,"conf.yml"),'w') do |f|
|
85
|
+
f.write(unit_test_definition_hash.to_yaml)
|
86
|
+
end
|
87
|
+
end
|
88
|
+
describe "load_definition" do
|
89
|
+
it "returns a hash" do
|
90
|
+
loaded_def = Rivet::Utils.load_definition("unit_test")
|
91
|
+
unit_test_definition_hash.each_pair { |k,v| loaded_def.should include(k => v) }
|
92
|
+
end
|
93
|
+
end
|
94
|
+
context "and with a defaults.yml" do
|
95
|
+
before do
|
96
|
+
File.open(File.join(Rivet::Utils::AUTOSCALE_DIR,"defaults.yml"),'w') do |f|
|
97
|
+
f.write(defaults_hash.to_yaml)
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
describe "consume_defaults" do
|
102
|
+
it "consume defaults returns a hash" do
|
103
|
+
results = Rivet::Utils.consume_defaults
|
104
|
+
defaults_hash.each_pair { |k,v| results.should include(k => v) }
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
describe "get_definition" do
|
109
|
+
it "returns a merged hash" do
|
110
|
+
result = Rivet::Utils.get_definition(definition_name)
|
111
|
+
merged_hash = defaults_hash.merge(unit_test_definition_hash)
|
112
|
+
result.should == defaults_hash.merge(unit_test_definition_hash)
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
end
|
117
|
+
|
118
|
+
end
|
119
|
+
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|