bosh_aws_registry 0.2.1

Sign up to get free protection for your applications and to get access to all the features.
data/README ADDED
@@ -0,0 +1,3 @@
1
+ # Copyright (c) 2009-2012 VMware, Inc.
2
+
3
+ BOSH AWS Cloud Provider Interface
data/Rakefile ADDED
@@ -0,0 +1,50 @@
1
+ # Copyright (c) 2009-2012 VMware, Inc.
2
+
3
+ $:.unshift(File.expand_path("../../rake", __FILE__))
4
+
5
+ ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../Gemfile", __FILE__)
6
+
7
+ require "rubygems"
8
+ require "bundler"
9
+ Bundler.setup(:default, :test)
10
+
11
+ require "rake"
12
+ begin
13
+ require "rspec/core/rake_task"
14
+ rescue LoadError
15
+ end
16
+
17
+ require "bundler_task"
18
+ require "ci_task"
19
+
20
+ gem_helper = Bundler::GemHelper.new(Dir.pwd)
21
+
22
+ desc "Build AWS Registry gem into the pkg directory"
23
+ task "build" do
24
+ gem_helper.build_gem
25
+ end
26
+
27
+ desc "Build and install AWS Registry into system gems"
28
+ task "install" do
29
+ Rake::Task["bundler:install"].invoke
30
+ gem_helper.install_gem
31
+ end
32
+
33
+ BundlerTask.new
34
+
35
+ if defined?(RSpec)
36
+ namespace :spec do
37
+ desc "Run Unit Tests"
38
+ rspec_task = RSpec::Core::RakeTask.new(:unit) do |t|
39
+ t.pattern = "spec/unit/**/*_spec.rb"
40
+ t.rspec_opts = %w(--format progress --colour)
41
+ end
42
+
43
+ CiTask.new do |task|
44
+ task.rspec_task = rspec_task
45
+ end
46
+ end
47
+
48
+ desc "Run tests"
49
+ task :spec => %w(spec:unit)
50
+ end
data/bin/aws_registry ADDED
@@ -0,0 +1,39 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ gemfile = File.expand_path("../../Gemfile", __FILE__)
4
+
5
+ if File.exists?(gemfile)
6
+ ENV["BUNDLE_GEMFILE"] = gemfile
7
+ require "rubygems"
8
+ require "bundler/setup"
9
+ end
10
+
11
+ $:.unshift(File.expand_path("../../lib", __FILE__))
12
+
13
+ require "aws_registry"
14
+ require "optparse"
15
+
16
+ config_file = nil
17
+
18
+ opts = OptionParser.new do |opts|
19
+ opts.on("-c", "--config FILE", "configuration file") do |opt|
20
+ config_file = opt
21
+ end
22
+ end
23
+
24
+ opts.parse!(ARGV.dup)
25
+
26
+ if config_file.nil?
27
+ puts opts
28
+ exit 1
29
+ end
30
+
31
+ runner = Bosh::AwsRegistry::Runner.new(config_file)
32
+
33
+ Signal.trap("INT") do
34
+ runner.stop
35
+ exit(1)
36
+ end
37
+
38
+ runner.run
39
+
data/bin/migrate ADDED
@@ -0,0 +1,42 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ ENV["BUNDLE_GEMFILE"] = File.expand_path("../../Gemfile", __FILE__)
4
+
5
+ require "rubygems"
6
+ require "bundler/setup"
7
+ require "logger"
8
+ require "sequel"
9
+
10
+ $:.unshift(File.dirname(__FILE__) + "/../lib")
11
+
12
+ require "aws_registry"
13
+
14
+ config_file = nil
15
+
16
+ opts = OptionParser.new do |opts|
17
+ opts.on("-c", "--config FILE", "configuration file") do |opt|
18
+ config_file = opt
19
+ end
20
+ end
21
+
22
+ opts.parse!(ARGV.dup)
23
+
24
+ if config_file.nil?
25
+ puts opts
26
+ exit 1
27
+ end
28
+
29
+ include Bosh::AwsRegistry::YamlHelper
30
+
31
+ config = load_yaml_file(config_file)
32
+
33
+ db = Bosh::AwsRegistry.connect_db(config["db"])
34
+ migrations_dir = File.expand_path("../../db/migrations", __FILE__)
35
+
36
+ options = {
37
+ :table => "aws_registry_schema"
38
+ }
39
+
40
+ Sequel.extension :migration
41
+ Sequel::TimestampMigrator.run(db, migrations_dir, options)
42
+
@@ -0,0 +1,66 @@
1
+ # Copyright (c) 2009-2012 VMware, Inc.
2
+
3
+ module Bosh::AwsRegistry
4
+
5
+ class ApiController < Sinatra::Base
6
+
7
+ not_found do
8
+ json(:status => "not_found")
9
+ end
10
+
11
+ error do
12
+ exception = request.env["sinatra.error"]
13
+ @logger.error(exception)
14
+ status(500)
15
+ json(:status => "error")
16
+ end
17
+
18
+ get "/instances/:instance_id/settings" do
19
+ ip_check = authorized? ? nil : request.ip
20
+ settings = @instance_manager.read_settings(params[:instance_id], ip_check)
21
+ json(:status => "ok", :settings => settings)
22
+ end
23
+
24
+ put "/instances/:instance_id/settings" do
25
+ protected!
26
+ @instance_manager.update_settings(params[:instance_id], request.body.read)
27
+ json(:status => "ok")
28
+ end
29
+
30
+ delete "/instances/:instance_id/settings" do
31
+ protected!
32
+ @instance_manager.delete_settings(params[:instance_id])
33
+ json(:status => "ok")
34
+ end
35
+
36
+ def initialize
37
+ super
38
+ @logger = Bosh::AwsRegistry.logger
39
+
40
+ @users = Set.new
41
+ @users << [Bosh::AwsRegistry.http_user, Bosh::AwsRegistry.http_password]
42
+ @instance_manager = InstanceManager.new
43
+ end
44
+
45
+ def protected!
46
+ unless authorized?
47
+ headers("WWW-Authenticate" => 'Basic realm="EC2 Registry"')
48
+ halt(401, json("status" => "access_denied"))
49
+ end
50
+ end
51
+
52
+ def authorized?
53
+ @auth ||= Rack::Auth::Basic::Request.new(request.env)
54
+ @auth.provided? &&
55
+ @auth.basic? &&
56
+ @auth.credentials &&
57
+ @users.include?(@auth.credentials)
58
+ end
59
+
60
+ def json(payload)
61
+ Yajl::Encoder.encode(payload)
62
+ end
63
+
64
+ end
65
+
66
+ end
@@ -0,0 +1,84 @@
1
+ # Copyright (c) 2009-2012 VMware, Inc.
2
+
3
+ module Bosh::AwsRegistry
4
+
5
+ class << self
6
+
7
+ AWS_MAX_RETRIES = 2
8
+ AWS_EC2_ENDPOINT = "ec2.amazonaws.com"
9
+
10
+ attr_accessor :logger
11
+ attr_accessor :http_port
12
+ attr_accessor :http_user
13
+ attr_accessor :http_password
14
+ attr_accessor :db
15
+
16
+ attr_writer :ec2
17
+
18
+ def configure(config)
19
+ validate_config(config)
20
+
21
+ @logger ||= Logger.new(config["logfile"] || STDOUT)
22
+
23
+ if config["loglevel"].kind_of?(String)
24
+ @logger.level = Logger.const_get(config["loglevel"].upcase)
25
+ end
26
+
27
+ @http_port = config["http"]["port"]
28
+ @http_user = config["http"]["user"]
29
+ @http_password = config["http"]["password"]
30
+
31
+ @aws = config["aws"]
32
+
33
+ @aws_options = {
34
+ :access_key_id => @aws["access_key_id"],
35
+ :secret_access_key => @aws["secret_access_key"],
36
+ :max_retries => @aws["max_retries"] || AWS_MAX_RETRIES,
37
+ :ec2_endpoint => @aws["ec2_endpoint"] || AWS_EC2_ENDPOINT,
38
+ :logger => @logger
39
+ }
40
+
41
+ @db = connect_db(config["db"])
42
+ end
43
+
44
+ def ec2
45
+ @ec2 ||= AWS::EC2.new(@aws_options)
46
+ end
47
+
48
+ def connect_db(db_config)
49
+ connection_options = {
50
+ :max_connections => db_config["max_connections"],
51
+ :pool_timeout => db_config["pool_timeout"]
52
+ }
53
+
54
+ db = Sequel.connect(db_config["database"], connection_options)
55
+ db.logger = @logger
56
+ db.sql_log_level = :debug
57
+ db
58
+ end
59
+
60
+ def validate_config(config)
61
+ unless config.is_a?(Hash)
62
+ raise ConfigError, "Invalid config format, Hash expected, " \
63
+ "#{config.class} given"
64
+ end
65
+
66
+ unless config.has_key?("http") && config["http"].is_a?(Hash)
67
+ raise ConfigError, "HTTP configuration is missing from " \
68
+ "config file"
69
+ end
70
+
71
+ unless config.has_key?("db") && config["db"].is_a?(Hash)
72
+ raise ConfigError, "Database configuration is missing from " \
73
+ "config file"
74
+ end
75
+
76
+ unless config.has_key?("aws") && config["aws"].is_a?(Hash)
77
+ raise ConfigError, "AWS configuration is missing from " \
78
+ "config file"
79
+ end
80
+ end
81
+
82
+ end
83
+
84
+ end
@@ -0,0 +1,20 @@
1
+ # Copyright (c) 2009-2012 VMware, Inc.
2
+
3
+ module Bosh::AwsRegistry
4
+
5
+ class Error < StandardError
6
+ def self.code(code = 500)
7
+ define_method(:code) { code }
8
+ end
9
+ end
10
+
11
+ class FatalError < Error; end
12
+
13
+ class ConfigError < Error; end
14
+ class ConnectionError < Error; end
15
+
16
+ class AwsError < Error; end
17
+
18
+ class InstanceError < Error; end
19
+ class InstanceNotFound < Error; code(404); end
20
+ end
@@ -0,0 +1,73 @@
1
+ # Copyright (c) 2009-2012 VMware, Inc.
2
+
3
+ module Bosh::AwsRegistry
4
+
5
+ class InstanceManager
6
+
7
+ def initialize
8
+ @logger = Bosh::AwsRegistry.logger
9
+ @ec2 = Bosh::AwsRegistry.ec2
10
+ end
11
+
12
+ ##
13
+ # Updates instance settings
14
+ # @param [String] instance_id EC2 instance id (instance record
15
+ # will be created in DB if it doesn't already exist)
16
+ # @param [String] settings New settings for the instance
17
+ def update_settings(instance_id, settings)
18
+ params = {
19
+ :instance_id => instance_id
20
+ }
21
+
22
+ instance = Models::AwsInstance[params] || Models::AwsInstance.new(params)
23
+ instance.settings = settings
24
+ instance.save
25
+ end
26
+
27
+ ##
28
+ # Reads instance settings
29
+ # @param [String] instance_id EC2 instance id
30
+ # @param [optional, String] remote_ip If this IP is provided,
31
+ # check will be performed to see if it instance id
32
+ # actually has this IP address according to EC2.
33
+ def read_settings(instance_id, remote_ip = nil)
34
+ check_instance_ip(remote_ip, instance_id) if remote_ip
35
+
36
+ get_instance(instance_id).settings
37
+ end
38
+
39
+ def delete_settings(instance_id)
40
+ get_instance(instance_id).destroy
41
+ end
42
+
43
+ private
44
+
45
+ def check_instance_ip(ip, instance_id)
46
+ return if ip == "127.0.0.1"
47
+ actual_ip = instance_private_ip(instance_id)
48
+ unless ip == actual_ip
49
+ raise InstanceError, "Instance IP mismatch, expected IP is " \
50
+ "`%s', actual IP is `%s'" % [ ip, actual_ip ]
51
+ end
52
+ end
53
+
54
+ def get_instance(instance_id)
55
+ instance = Models::AwsInstance[:instance_id => instance_id]
56
+
57
+ if instance.nil?
58
+ raise InstanceNotFound, "Can't find instance `#{instance_id}'"
59
+ end
60
+
61
+ instance
62
+ end
63
+
64
+ def instance_private_ip(instance_id)
65
+ @ec2.instances[instance_id].private_ip_address
66
+ rescue AWS::Errors::Base => e
67
+ raise Bosh::AwsRegistry::AwsError, "AWS error: #{e}"
68
+ end
69
+
70
+ end
71
+
72
+ end
73
+
@@ -0,0 +1,13 @@
1
+ # Copyright (c) 2009-2012 VMware, Inc.
2
+
3
+ module Bosh::AwsRegistry::Models
4
+ class AwsInstance < Sequel::Model
5
+
6
+ def validate
7
+ validates_presence [:instance_id, :settings]
8
+ validates_unique :instance_id
9
+ end
10
+
11
+ end
12
+ end
13
+
@@ -0,0 +1,11 @@
1
+ # Copyright (c) 2009-2012 VMware, Inc.
2
+
3
+ module Bosh::AwsRegistry
4
+ module Models
5
+ end
6
+ end
7
+
8
+ require "aws_registry/models/aws_instance"
9
+
10
+
11
+
@@ -0,0 +1,56 @@
1
+ # Copyright (c) 2009-2012 VMware, Inc.
2
+
3
+ module Bosh::AwsRegistry
4
+ class Runner
5
+ include YamlHelper
6
+
7
+ def initialize(config_file)
8
+ Bosh::AwsRegistry.configure(load_yaml_file(config_file))
9
+
10
+ @logger = Bosh::AwsRegistry.logger
11
+ @http_port = Bosh::AwsRegistry.http_port
12
+ @http_user = Bosh::AwsRegistry.http_user
13
+ @http_password = Bosh::AwsRegistry.http_password
14
+ end
15
+
16
+ def run
17
+ @logger.info("BOSH AWS Registry starting...")
18
+ EM.kqueue if EM.kqueue?
19
+ EM.epoll if EM.epoll?
20
+
21
+ EM.error_handler { |e| handle_em_error(e) }
22
+
23
+ EM.run do
24
+ start_http_server
25
+ end
26
+ end
27
+
28
+ def stop
29
+ @logger.info("BOSH AWS Registry shutting down...")
30
+ @http_server.stop! if @http_server
31
+ EM.stop
32
+ end
33
+
34
+ def start_http_server
35
+ @logger.info "HTTP server is starting on port #{@http_port}..."
36
+ @http_server = Thin::Server.new("0.0.0.0", @http_port, :signals => false) do
37
+ Thin::Logging.silent = true
38
+ map "/" do
39
+ run Bosh::AwsRegistry::ApiController.new
40
+ end
41
+ end
42
+ @http_server.start!
43
+ end
44
+
45
+ private
46
+
47
+ def handle_em_error(e)
48
+ @logger.send(level, e.to_s)
49
+ if e.respond_to?(:backtrace) && e.backtrace.respond_to?(:join)
50
+ @logger.send(level, e.backtrace.join("\n"))
51
+ end
52
+ stop
53
+ end
54
+
55
+ end
56
+ end
@@ -0,0 +1,7 @@
1
+ # Copyright (c) 2009-2012 VMware, Inc.
2
+
3
+ module Bosh
4
+ module AwsRegistry
5
+ VERSION = "0.2.1"
6
+ end
7
+ end
@@ -0,0 +1,24 @@
1
+ # Copyright (c) 2009-2012 VMware, Inc.
2
+
3
+ module Bosh::AwsRegistry
4
+ module YamlHelper
5
+
6
+ def load_yaml_file(path, expected_type = Hash)
7
+ unless File.exists?(path)
8
+ raise(ConfigError, "Cannot find file `#{path}'")
9
+ end
10
+
11
+ yaml = YAML.load_file(path)
12
+
13
+ if expected_type && !yaml.is_a?(expected_type)
14
+ raise ConfigError, "Incorrect file format in `#{path}', " \
15
+ "#{expected_type} expected"
16
+ end
17
+
18
+ yaml
19
+ rescue SystemCallError => e
20
+ raise ConfigError, "Cannot load YAML file at `#{path}': #{e}"
21
+ end
22
+
23
+ end
24
+ end
@@ -0,0 +1,26 @@
1
+ # Copyright (c) 2009-2012 VMware, Inc.
2
+
3
+ module Bosh
4
+ module AwsRegistry
5
+ autoload :Models, "aws_registry/models"
6
+ end
7
+ end
8
+
9
+ require "aws-sdk"
10
+ require "logger"
11
+ require "sequel"
12
+ require "sinatra/base"
13
+ require "thin"
14
+ require "yajl"
15
+
16
+ require "aws_registry/yaml_helper"
17
+
18
+ require "aws_registry/api_controller"
19
+ require "aws_registry/config"
20
+ require "aws_registry/errors"
21
+ require "aws_registry/instance_manager"
22
+ require "aws_registry/runner"
23
+ require "aws_registry/version"
24
+
25
+ Sequel::Model.plugin :validation_helpers
26
+
@@ -0,0 +1,17 @@
1
+ ---
2
+ loglevel: debug
3
+
4
+ http:
5
+ port: 25695
6
+ user: admin
7
+ password: admin
8
+
9
+ db:
10
+ database: "sqlite:///:memory:"
11
+ max_connections: 32
12
+ pool_timeout: 10
13
+
14
+ aws:
15
+ access_key_id: foo
16
+ secret_access_key: bar
17
+ max_retries: 2
@@ -0,0 +1,113 @@
1
+ # Copyright (c) 2009-2012 VMware, Inc.
2
+
3
+ $:.unshift(File.expand_path("../../lib", __FILE__))
4
+
5
+ ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile", __FILE__)
6
+
7
+ require "rubygems"
8
+ require "bundler"
9
+ Bundler.setup(:default, :test)
10
+
11
+ require "fileutils"
12
+ require "logger"
13
+ require "tmpdir"
14
+
15
+ require "rspec"
16
+ require "rack/test"
17
+
18
+ module SpecHelper
19
+ class << self
20
+ attr_accessor :logger
21
+ attr_accessor :temp_dir
22
+
23
+ def init
24
+ ENV["RACK_ENV"] = "test"
25
+ configure_logging
26
+ configure_temp_dir
27
+
28
+ require "aws_registry"
29
+ init_database
30
+ end
31
+
32
+ def configure_logging
33
+ if ENV["DEBUG"]
34
+ @logger = Logger.new(STDOUT)
35
+ else
36
+ path = File.expand_path("../spec.log", __FILE__)
37
+ log_file = File.open(path, "w")
38
+ log_file.sync = true
39
+ @logger = Logger.new(log_file)
40
+ end
41
+ end
42
+
43
+ def configure_temp_dir
44
+ @temp_dir = Dir.mktmpdir
45
+ ENV["TMPDIR"] = @temp_dir
46
+ FileUtils.mkdir_p(@temp_dir)
47
+ at_exit { FileUtils.rm_rf(@temp_dir) }
48
+ end
49
+
50
+ def init_database
51
+ @migrations_dir = File.expand_path("../../db/migrations", __FILE__)
52
+
53
+ Sequel.extension :migration
54
+
55
+ @db = Sequel.sqlite(:database => nil, :max_connections => 32, :pool_timeout => 10)
56
+ @db.loggers << @logger
57
+ Bosh::AwsRegistry.db = @db
58
+
59
+ run_migrations
60
+ end
61
+
62
+ def run_migrations
63
+ Sequel::Migrator.apply(@db, @migrations_dir, nil)
64
+ end
65
+
66
+ def reset_database
67
+ @db.execute("PRAGMA foreign_keys = OFF")
68
+ @db.tables.each do |table|
69
+ @db.drop_table(table)
70
+ end
71
+ @db.execute("PRAGMA foreign_keys = ON")
72
+ end
73
+
74
+ def reset
75
+ reset_database
76
+ run_migrations
77
+
78
+ Bosh::AwsRegistry.db = @db
79
+ Bosh::AwsRegistry.logger = @logger
80
+ end
81
+ end
82
+ end
83
+
84
+ SpecHelper.init
85
+
86
+ def valid_config
87
+ {
88
+ "logfile" => nil,
89
+ "loglevel" => "debug",
90
+ "http" => {
91
+ "user" => "admin",
92
+ "password" => "admin",
93
+ "port" => 25777
94
+ },
95
+ "db" => {
96
+ "max_connections" => 433,
97
+ "pool_timeout" => 227,
98
+ "database" => "sqlite:///:memory:"
99
+ },
100
+ "aws" => {
101
+ "access_key_id" => "foo",
102
+ "secret_access_key" => "bar",
103
+ "max_retries" => 5
104
+ }
105
+ }
106
+ end
107
+
108
+ RSpec.configure do |rspec|
109
+ rspec.before(:each) do
110
+ SpecHelper.reset
111
+ Bosh::AwsRegistry.logger = Logger.new(StringIO.new)
112
+ end
113
+ end
@@ -0,0 +1,74 @@
1
+ # Copyright (c) 2009-2012 VMware, Inc.
2
+
3
+ require File.expand_path("../../spec_helper", __FILE__)
4
+
5
+ describe Bosh::AwsRegistry::ApiController do
6
+
7
+ before(:each) do
8
+ Bosh::AwsRegistry.http_user = "admin"
9
+ Bosh::AwsRegistry.http_password = "admin"
10
+
11
+ @instance_manager = mock("instance manager")
12
+ Bosh::AwsRegistry::InstanceManager.stub!(:new).and_return(@instance_manager)
13
+
14
+ rack_mock = Rack::MockSession.new(Bosh::AwsRegistry::ApiController.new)
15
+ @session = Rack::Test::Session.new(rack_mock)
16
+ end
17
+
18
+ def expect_json_response(response, status, body)
19
+ response.status.should == status
20
+ Yajl::Parser.parse(response.body).should == body
21
+ end
22
+
23
+ it "returns settings for given EC2 instance (IP check)" do
24
+ @instance_manager.should_receive(:read_settings).
25
+ with("foo", "127.0.0.1").and_return("bar")
26
+
27
+ @session.get("/instances/foo/settings")
28
+
29
+ expect_json_response(@session.last_response, 200,
30
+ { "status" => "ok", "settings" => "bar" })
31
+ end
32
+
33
+ it "returns settings (authorized user, no IP check)" do
34
+ @instance_manager.should_receive(:read_settings).
35
+ with("foo", nil).and_return("bar")
36
+
37
+ @session.basic_authorize("admin", "admin")
38
+ @session.get("/instances/foo/settings")
39
+
40
+ expect_json_response(@session.last_response, 200,
41
+ { "status" => "ok", "settings" => "bar" })
42
+ end
43
+
44
+ it "updates settings" do
45
+ @session.put("/instances/foo/settings", {}, { :input => "bar" })
46
+ expect_json_response(@session.last_response, 401,
47
+ { "status" => "access_denied" })
48
+
49
+ @instance_manager.should_receive(:update_settings).
50
+ with("foo", "bar").and_return(true)
51
+
52
+ @session.basic_authorize("admin", "admin")
53
+ @session.put("/instances/foo/settings", {}, { :input => "bar" })
54
+
55
+ expect_json_response(@session.last_response, 200,
56
+ { "status" => "ok" })
57
+ end
58
+
59
+ it "deletes settings" do
60
+ @session.delete("/instances/foo/settings")
61
+ expect_json_response(@session.last_response, 401,
62
+ { "status" => "access_denied" })
63
+
64
+ @instance_manager.should_receive(:delete_settings).
65
+ with("foo").and_return(true)
66
+
67
+ @session.basic_authorize("admin", "admin")
68
+ @session.delete("/instances/foo/settings")
69
+
70
+ expect_json_response(@session.last_response, 200,
71
+ { "status" => "ok" })
72
+ end
73
+
74
+ end
@@ -0,0 +1,56 @@
1
+ # Copyright (c) 2009-2012 VMware, Inc.
2
+
3
+ require File.expand_path("../../spec_helper", __FILE__)
4
+
5
+ describe Bosh::AwsRegistry do
6
+
7
+ describe "configuring AWS registry" do
8
+ it "reads provided configuration file and sets singletons" do
9
+ Bosh::AwsRegistry.configure(valid_config)
10
+
11
+ logger = Bosh::AwsRegistry.logger
12
+
13
+ logger.should be_kind_of(Logger)
14
+ logger.level.should == Logger::DEBUG
15
+
16
+ Bosh::AwsRegistry.http_port.should == 25777
17
+ Bosh::AwsRegistry.http_user.should == "admin"
18
+ Bosh::AwsRegistry.http_password.should == "admin"
19
+
20
+ db = Bosh::AwsRegistry.db
21
+ db.should be_kind_of(Sequel::SQLite::Database)
22
+ db.opts[:database].should == "/:memory:"
23
+ db.opts[:max_connections].should == 433
24
+ db.opts[:pool_timeout].should == 227
25
+ end
26
+
27
+ it "validates configuration file" do
28
+ expect {
29
+ Bosh::AwsRegistry.configure("foobar")
30
+ }.to raise_error(Bosh::AwsRegistry::ConfigError,
31
+ /Invalid config format/)
32
+
33
+ config = valid_config.merge("http" => nil)
34
+
35
+ expect {
36
+ Bosh::AwsRegistry.configure(config)
37
+ }.to raise_error(Bosh::AwsRegistry::ConfigError,
38
+ /HTTP configuration is missing/)
39
+
40
+ config = valid_config.merge("db" => nil)
41
+
42
+ expect {
43
+ Bosh::AwsRegistry.configure(config)
44
+ }.to raise_error(Bosh::AwsRegistry::ConfigError,
45
+ /Database configuration is missing/)
46
+
47
+ config = valid_config.merge("aws" => nil)
48
+
49
+ expect {
50
+ Bosh::AwsRegistry.configure(config)
51
+ }.to raise_error(Bosh::AwsRegistry::ConfigError,
52
+ /AWS configuration is missing/)
53
+ end
54
+
55
+ end
56
+ end
@@ -0,0 +1,92 @@
1
+ # Copyright (c) 2009-2012 VMware, Inc.
2
+
3
+ require File.expand_path("../../spec_helper", __FILE__)
4
+
5
+ describe Bosh::AwsRegistry::InstanceManager do
6
+
7
+ before(:each) do
8
+ @ec2 = mock("ec2")
9
+ Bosh::AwsRegistry.ec2 = @ec2
10
+ end
11
+
12
+ let(:manager) do
13
+ Bosh::AwsRegistry::InstanceManager.new
14
+ end
15
+
16
+ def create_instance(params)
17
+ Bosh::AwsRegistry::Models::AwsInstance.create(params)
18
+ end
19
+
20
+ def actual_ip_is(ip)
21
+ instances = mock("instances")
22
+ instance = mock("instance")
23
+ @ec2.should_receive(:instances).and_return(instances)
24
+ instances.should_receive(:[]).with("foo").and_return(instance)
25
+ instance.should_receive(:private_ip_address).and_return(ip)
26
+ end
27
+
28
+ describe "reading settings" do
29
+ it "returns settings after verifying IP address" do
30
+ create_instance(:instance_id => "foo", :settings => "bar")
31
+ actual_ip_is("10.0.0.1")
32
+ manager.read_settings("foo", "10.0.0.1").should == "bar"
33
+ end
34
+
35
+ it "raises an error if IP cannot be verified" do
36
+ create_instance(:instance_id => "foo", :settings => "bar")
37
+ actual_ip_is("10.0.0.2")
38
+
39
+ expect {
40
+ manager.read_settings("foo", "10.0.0.1")
41
+ }.to raise_error(Bosh::AwsRegistry::InstanceError,
42
+ "Instance IP mismatch, expected IP is `10.0.0.1', " \
43
+ "actual IP is `10.0.0.2'")
44
+ end
45
+
46
+ it "doesn't check remote IP if it's not provided" do
47
+ create_instance(:instance_id => "foo", :settings => "bar")
48
+ manager.read_settings("foo").should == "bar"
49
+ end
50
+
51
+ it "raises an error if instance not found" do
52
+ expect {
53
+ manager.read_settings("foo")
54
+ }.to raise_error(Bosh::AwsRegistry::InstanceNotFound,
55
+ "Can't find instance `foo'")
56
+ end
57
+ end
58
+
59
+ describe "updating settings" do
60
+ it "updates settings (new instance)" do
61
+ manager.update_settings("foo", "baz")
62
+ manager.read_settings("foo").should == "baz"
63
+ end
64
+
65
+ it "updates settings (existing instance)" do
66
+ create_instance(:instance_id => "foo", :settings => "bar")
67
+ manager.read_settings("foo").should == "bar"
68
+ manager.update_settings("foo", "baz")
69
+ manager.read_settings("foo").should == "baz"
70
+ end
71
+ end
72
+
73
+ describe "deleting settings" do
74
+ it "deletes settings" do
75
+ manager.update_settings("foo", "baz")
76
+ manager.delete_settings("foo")
77
+
78
+ expect {
79
+ manager.read_settings("foo")
80
+ }.to raise_error(Bosh::AwsRegistry::InstanceNotFound,
81
+ "Can't find instance `foo'")
82
+ end
83
+
84
+ it "raises an error if instance not found" do
85
+ expect {
86
+ manager.delete_settings("foo")
87
+ }.to raise_error(Bosh::AwsRegistry::InstanceNotFound,
88
+ "Can't find instance `foo'")
89
+ end
90
+ end
91
+
92
+ end
@@ -0,0 +1,84 @@
1
+ # Copyright (c) 2009-2012 VMware, Inc.
2
+
3
+ require File.expand_path("../../spec_helper", __FILE__)
4
+
5
+ describe Bosh::AwsRegistry::Runner do
6
+
7
+ def make_runner(config_file)
8
+ Bosh::AwsRegistry::Runner.new(config_file)
9
+ end
10
+
11
+ def write_config(file, config)
12
+ File.open(file, "w") do |f|
13
+ YAML.dump(config, f)
14
+ end
15
+ end
16
+
17
+ describe "initializing" do
18
+ it "configures AWS registry using provided file" do
19
+ config_file = Tempfile.new("config")
20
+ config = { "key" => "value" }
21
+ write_config(config_file.path, config)
22
+
23
+ Bosh::AwsRegistry.should_receive(:configure).with(config)
24
+ make_runner(config_file.path)
25
+ end
26
+
27
+ it "fails if config file not found" do
28
+ expect {
29
+ make_runner("foo")
30
+ }.to raise_error(Bosh::AwsRegistry::ConfigError, "Cannot find file `foo'")
31
+ end
32
+
33
+ it "fails when config file has incorrect format" do
34
+ config_file = Tempfile.new("config")
35
+ config = "foo"
36
+ write_config(config_file.path, config)
37
+
38
+ expect {
39
+ make_runner(config_file.path)
40
+ }.to raise_error(Bosh::AwsRegistry::ConfigError, /Incorrect file format/)
41
+ end
42
+
43
+ it "fails when some syscall fails" do
44
+ config_file = Tempfile.new("config")
45
+ write_config(config_file.path, { "foo" => "bar" })
46
+
47
+ YAML.stub!(:load_file).and_raise(SystemCallError.new("baz"))
48
+
49
+ expect {
50
+ make_runner(config_file.path)
51
+ }.to raise_error(Bosh::AwsRegistry::ConfigError, /baz/)
52
+ end
53
+ end
54
+
55
+ describe "running/stopping" do
56
+ before(:each) do
57
+ @config_file = Tempfile.new("config")
58
+ write_config(@config_file.path, valid_config)
59
+ end
60
+
61
+ it "spins up/shuts down reactor and HTTP server" do
62
+ runner = make_runner(@config_file)
63
+ mock_thin = mock("thin")
64
+
65
+ EM.should_receive(:run).and_yield
66
+
67
+ Thin::Server.should_receive(:new).
68
+ with("0.0.0.0", 25777, :signals => false).
69
+ and_return(mock_thin)
70
+
71
+ mock_thin.should_receive(:start!)
72
+
73
+ runner.run
74
+
75
+ mock_thin.should_receive(:stop!)
76
+ EM.should_receive(:stop)
77
+
78
+ runner.stop
79
+ end
80
+ end
81
+
82
+ end
83
+
84
+
metadata ADDED
@@ -0,0 +1,157 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: bosh_aws_registry
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.2.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - VMware
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-07-12 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: sequel
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ! '>='
28
+ - !ruby/object:Gem::Version
29
+ version: '0'
30
+ - !ruby/object:Gem::Dependency
31
+ name: sinatra
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ! '>='
36
+ - !ruby/object:Gem::Version
37
+ version: '0'
38
+ type: :runtime
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ! '>='
44
+ - !ruby/object:Gem::Version
45
+ version: '0'
46
+ - !ruby/object:Gem::Dependency
47
+ name: thin
48
+ requirement: !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - ! '>='
52
+ - !ruby/object:Gem::Version
53
+ version: '0'
54
+ type: :runtime
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ! '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ - !ruby/object:Gem::Dependency
63
+ name: yajl-ruby
64
+ requirement: !ruby/object:Gem::Requirement
65
+ none: false
66
+ requirements:
67
+ - - ! '>='
68
+ - !ruby/object:Gem::Version
69
+ version: '0'
70
+ type: :runtime
71
+ prerelease: false
72
+ version_requirements: !ruby/object:Gem::Requirement
73
+ none: false
74
+ requirements:
75
+ - - ! '>='
76
+ - !ruby/object:Gem::Version
77
+ version: '0'
78
+ - !ruby/object:Gem::Dependency
79
+ name: aws-sdk
80
+ requirement: !ruby/object:Gem::Requirement
81
+ none: false
82
+ requirements:
83
+ - - ~>
84
+ - !ruby/object:Gem::Version
85
+ version: 1.3.6
86
+ type: :runtime
87
+ prerelease: false
88
+ version_requirements: !ruby/object:Gem::Requirement
89
+ none: false
90
+ requirements:
91
+ - - ~>
92
+ - !ruby/object:Gem::Version
93
+ version: 1.3.6
94
+ description: BOSH AWS registry
95
+ email: support@vmware.com
96
+ executables:
97
+ - aws_registry
98
+ extensions: []
99
+ extra_rdoc_files: []
100
+ files:
101
+ - bin/aws_registry
102
+ - bin/migrate
103
+ - lib/aws_registry.rb
104
+ - lib/aws_registry/api_controller.rb
105
+ - lib/aws_registry/config.rb
106
+ - lib/aws_registry/errors.rb
107
+ - lib/aws_registry/instance_manager.rb
108
+ - lib/aws_registry/models.rb
109
+ - lib/aws_registry/models/aws_instance.rb
110
+ - lib/aws_registry/runner.rb
111
+ - lib/aws_registry/version.rb
112
+ - lib/aws_registry/yaml_helper.rb
113
+ - README
114
+ - Rakefile
115
+ - spec/assets/sample_config.yml
116
+ - spec/spec_helper.rb
117
+ - spec/unit/api_controller_spec.rb
118
+ - spec/unit/config_spec.rb
119
+ - spec/unit/instance_manager_spec.rb
120
+ - spec/unit/runner_spec.rb
121
+ homepage: http://www.vmware.com
122
+ licenses: []
123
+ post_install_message:
124
+ rdoc_options: []
125
+ require_paths:
126
+ - lib
127
+ required_ruby_version: !ruby/object:Gem::Requirement
128
+ none: false
129
+ requirements:
130
+ - - ! '>='
131
+ - !ruby/object:Gem::Version
132
+ version: '0'
133
+ segments:
134
+ - 0
135
+ hash: -3082160961986360018
136
+ required_rubygems_version: !ruby/object:Gem::Requirement
137
+ none: false
138
+ requirements:
139
+ - - ! '>='
140
+ - !ruby/object:Gem::Version
141
+ version: '0'
142
+ segments:
143
+ - 0
144
+ hash: -3082160961986360018
145
+ requirements: []
146
+ rubyforge_project:
147
+ rubygems_version: 1.8.24
148
+ signing_key:
149
+ specification_version: 3
150
+ summary: BOSH AWS registry
151
+ test_files:
152
+ - spec/assets/sample_config.yml
153
+ - spec/spec_helper.rb
154
+ - spec/unit/api_controller_spec.rb
155
+ - spec/unit/config_spec.rb
156
+ - spec/unit/instance_manager_spec.rb
157
+ - spec/unit/runner_spec.rb