bosh_aws_registry 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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