Browse files

AWS Registry new auth + specs

AWS Registry now uses instance id to validate instance private IP
address before giving out instance settings.

Change-Id: Ice3d0d8ab7f8c28bf108dc22ab0238807174311b
  • Loading branch information...
1 parent 5b1ac7f commit d5ffa3c2b9b33c52c0fa8bc960ed0ba4fa7745aa @olegshaldybin olegshaldybin committed Mar 13, 2012
View
11 aws_registry/Gemfile
@@ -5,18 +5,23 @@ gem "sequel"
gem "sinatra"
gem "thin"
gem "yajl-ruby"
+gem "aws-sdk", ">=1.3.6"
group :production do
gem "pg"
end
+group :development do
+ gem "ruby-debug", :platforms => :ruby_18
+ gem "ruby-debug19", :platforms => :ruby_19
+end
+
group :test, :development do
+ gem "rack-test"
+
gem "ci_reporter"
gem "rspec"
- gem "ruby-debug", :platforms => :ruby_18
- gem "ruby-debug19", :platforms => :ruby_19
-
gem "rcov", :platforms => :ruby_18
gem "simplecov", :platforms => :ruby_19
View
16 aws_registry/Gemfile.lock
@@ -1,22 +1,35 @@
GEM
specs:
archive-tar-minitar (0.5.2)
+ aws-sdk (1.3.6)
+ httparty (~> 0.7)
+ json (~> 1.4)
+ nokogiri (>= 1.4.4)
+ uuidtools (~> 2.1)
builder (3.0.0)
ci_reporter (1.7.0)
builder (>= 2.1.2)
columnize (0.3.6)
daemons (1.1.8)
diff-lcs (1.1.3)
eventmachine (0.12.10)
+ httparty (0.8.1)
+ multi_json
+ multi_xml
+ json (1.6.5)
linecache (0.46)
rbx-require-relative (> 0.0.4)
linecache19 (0.5.12)
ruby_core_source (>= 0.1.4)
multi_json (1.1.0)
+ multi_xml (0.4.1)
+ nokogiri (1.5.0)
pg (0.11.0)
rack (1.4.1)
rack-protection (1.2.0)
rack
+ rack-test (0.6.1)
+ rack (>= 1.0)
rake (0.9.2.2)
rbx-require-relative (0.0.9)
rcov (1.0.0)
@@ -60,14 +73,17 @@ GEM
eventmachine (>= 0.12.6)
rack (>= 1.0.0)
tilt (1.3.3)
+ uuidtools (2.1.2)
yajl-ruby (1.1.0)
PLATFORMS
ruby
DEPENDENCIES
+ aws-sdk (>= 1.3.6)
ci_reporter
pg
+ rack-test
rake
rcov
rspec
View
4 ...s/20120306020921_create_agent_settings.rb → ...ns/20120306020921_create_aws_instances.rb
@@ -2,10 +2,10 @@
Sequel.migration do
change do
- create_table :agent_settings do
+ create_table :aws_instances do
primary_key :id
- String :ip_address, :null => false, :unique => true
+ String :instance_id, :null => false, :unique => true
String :settings, :null => false, :text => true
end
end
View
2 aws_registry/lib/aws_registry.rb
@@ -6,6 +6,7 @@ module AwsRegistry
end
end
+require "aws-sdk"
require "logger"
require "sequel"
require "sinatra/base"
@@ -17,6 +18,7 @@ module AwsRegistry
require "aws_registry/api_controller"
require "aws_registry/config"
require "aws_registry/errors"
+require "aws_registry/instance_manager"
require "aws_registry/runner"
require "aws_registry/version"
View
105 aws_registry/lib/aws_registry/api_controller.rb
@@ -2,55 +2,10 @@
module Bosh::AwsRegistry
- # EC2 agents use private EC2 IP address for
- # authentication, as it guaranteed to be unique
- # during VM lifecycle. This simplifies credentials
- # management, as we only need one set of credentials
- # for CPI itself to modify agent settings but no
- # per-agent credentials.
class ApiController < Sinatra::Base
- def initialize
- super
- @logger = Bosh::AwsRegistry.logger
- @users = Set.new
- @users << [Bosh::AwsRegistry.http_user, Bosh::AwsRegistry.http_password]
- end
-
- def protected!
- unless authorized?
- response["WWW-Authenticate"] = %(Basic realm="EC2 Registry")
- throw :halt, [401, "Not authorized\n"]
- end
- end
-
- def authorized?
- @auth ||= Rack::Auth::Basic::Request.new(request.env)
- @auth.provided? &&
- @auth.basic? &&
- @auth.credentials &&
- @users.include?(@auth.credentials)
- end
-
- def json(payload)
- Yajl::Encoder.encode(payload)
- end
-
- def get_settings(ip)
- agent_settings = Models::AgentSettings[:ip_address => ip]
-
- if agent_settings.nil?
- status(404)
- json(:status => "not_found")
- else
- json(:status => "ok", :settings => agent_settings.settings)
- end
- end
-
- configure do
- set(:show_exceptions, false)
- set(:raise_errors, false)
- set(:dump_errors, false)
+ not_found do
+ json(:status => "not_found")
end
error do
@@ -60,44 +15,50 @@ def get_settings(ip)
json(:status => "error")
end
- get "/settings", :provides => "json" do
- get_settings(request.ip)
+ get "/instances/:instance_id/settings" do
+ ip_check = authorized? ? nil : request.ip
+ settings = @instance_manager.read_settings(params[:instance_id], ip_check)
+ json(:status => "ok", :settings => settings)
end
- before "/agents/*" do
+ put "/instances/:instance_id/settings" do
protected!
+ @instance_manager.update_settings(params[:instance_id], request.body.read)
+ json(:status => "ok")
end
- get "/agents/:ip_address/settings", :provides => "json" do
- get_settings(params[:ip_address])
+ delete "/instances/:instance_id/settings" do
+ protected!
+ @instance_manager.delete_settings(params[:instance_id])
+ json(:status => "ok")
end
- put "/agents/:ip_address/settings", :provides => "json" do
- ip_address = params[:ip_address]
+ def initialize
+ super
+ @logger = Bosh::AwsRegistry.logger
- agent_settings = Models::AgentSettings[:ip_address => ip_address]
+ @users = Set.new
+ @users << [Bosh::AwsRegistry.http_user, Bosh::AwsRegistry.http_password]
+ @instance_manager = InstanceManager.new
+ end
- if agent_settings.nil?
- agent_settings = Models::AgentSettings.new(:ip_address => ip_address)
+ def protected!
+ unless authorized?
+ headers("WWW-Authenticate" => 'Basic realm="EC2 Registry"')
+ halt(401, json("status" => "access_denied"))
end
-
- agent_settings.settings = request.body.read
- agent_settings.save
- json(:status => "ok")
end
- delete "/agents/:ip_address/settings", :provides => "json" do
- ip_address = params[:ip_address]
-
- agent_settings = Models::AgentSettings[:ip_address => ip_address]
+ def authorized?
+ @auth ||= Rack::Auth::Basic::Request.new(request.env)
+ @auth.provided? &&
+ @auth.basic? &&
+ @auth.credentials &&
+ @users.include?(@auth.credentials)
+ end
- if agent_settings.nil?
- status(404)
- json(:status => "not_found")
- else
- agent_settings.destroy
- json(:status => "ok")
- end
+ def json(payload)
+ Yajl::Encoder.encode(payload)
end
end
View
24 aws_registry/lib/aws_registry/config.rb
@@ -4,16 +4,20 @@ module Bosh::AwsRegistry
class << self
+ AWS_MAX_RETRIES = 2
+
attr_accessor :logger
attr_accessor :http_port
attr_accessor :http_user
attr_accessor :http_password
attr_accessor :db
+ attr_writer :ec2
+
def configure(config)
validate_config(config)
- @logger = Logger.new(config["logfile"] || STDOUT)
+ @logger ||= Logger.new(config["logfile"] || STDOUT)
if config["loglevel"].kind_of?(String)
@logger.level = Logger.const_get(config["loglevel"].upcase)
@@ -23,9 +27,22 @@ def configure(config)
@http_user = config["http"]["user"]
@http_password = config["http"]["password"]
+ @aws = config["aws"]
+
+ @aws_options = {
+ :access_key_id => @aws["access_key_id"],
+ :secret_access_key => @aws["secret_access_key"],
+ :max_retries => @aws["max_retries"] || AWS_MAX_RETRIES,
+ :logger => @logger
+ }
+
@db = connect_db(config["db"])
end
+ def ec2
+ @ec2 ||= AWS::EC2.new(@aws_options)
+ end
+
def connect_db(db_config)
connection_options = {
:max_connections => db_config["max_connections"],
@@ -53,6 +70,11 @@ def validate_config(config)
raise ConfigError, "Database configuration is missing from " \
"config file"
end
+
+ unless config.has_key?("aws") && config["aws"].is_a?(Hash)
+ raise ConfigError, "AWS configuration is missing from " \
+ "config file"
+ end
end
end
View
13 aws_registry/lib/aws_registry/errors.rb
@@ -3,13 +3,18 @@
module Bosh::AwsRegistry
class Error < StandardError
- def self.code(code = nil)
+ def self.code(code = 500)
define_method(:code) { code }
end
end
- class FatalError < Error; code(42); end
+ class FatalError < Error; end
- class ConfigError < Error; code(101); end
- class ConnectionError < Error; code(202); end
+ class ConfigError < Error; end
+ class ConnectionError < Error; end
+
+ class AwsError < Error; end
+
+ class InstanceError < Error; end
+ class InstanceNotFound < Error; code(404); end
end
View
72 aws_registry/lib/aws_registry/instance_manager.rb
@@ -0,0 +1,72 @@
+# Copyright (c) 2009-2012 VMware, Inc.
+
+module Bosh::AwsRegistry
+
+ class InstanceManager
+
+ def initialize
+ @logger = Bosh::AwsRegistry.logger
+ @ec2 = Bosh::AwsRegistry.ec2
+ end
+
+ ##
+ # Updates instance settings
+ # @param [String] instance_id EC2 instance id (instance record
+ # will be created in DB if it doesn't already exist)
+ # @param [String] settings New settings for the instance
+ def update_settings(instance_id, settings)
+ params = {
+ :instance_id => instance_id
+ }
+
+ instance = Models::AwsInstance[params] || Models::AwsInstance.new(params)
+ instance.settings = settings
+ instance.save
+ end
+
+ ##
+ # Reads instance settings
+ # @param [String] instance_id EC2 instance id
+ # @param [optional, String] remote_ip If this IP is provided,
+ # check will be performed to see if it instance id
+ # actually has this IP address according to EC2.
+ def read_settings(instance_id, remote_ip = nil)
+ check_instance_ip(remote_ip, instance_id) if remote_ip
+
+ get_instance(instance_id).settings
+ end
+
+ def delete_settings(instance_id)
+ get_instance(instance_id).destroy
+ end
+
+ private
+
+ def check_instance_ip(ip, instance_id)
+ actual_ip = instance_private_ip(instance_id)
+ unless ip == actual_ip
+ raise InstanceError, "Instance IP mismatch, expected IP is " \
+ "`%s', actual IP is `%s'" % [ ip, actual_ip ]
+ end
+ end
+
+ def get_instance(instance_id)
+ instance = Models::AwsInstance[:instance_id => instance_id]
+
+ if instance.nil?
+ raise InstanceNotFound, "Can't find instance `#{instance_id}'"
+ end
+
+ instance
+ end
+
+ def instance_private_ip(instance_id)
+ @ec2.instances[instance_id].private_ip_address
+ rescue AWS::Errors::Base => e
+ raise Bosh::AwsRegistry::AwsError, "AWS error: #{e}"
+ end
+
+ end
+
+end
+
View
2 aws_registry/lib/aws_registry/models.rb
@@ -5,7 +5,7 @@ module Models
end
end
-require "aws_registry/models/agent_settings"
+require "aws_registry/models/aws_instance"
View
13 aws_registry/lib/aws_registry/models/agent_settings.rb
@@ -1,13 +0,0 @@
-# Copyright (c) 2009-2012 VMware, Inc.
-
-module Bosh::AwsRegistry::Models
- class AgentSettings < Sequel::Model
-
- def validate
- validates_presence [:ip_address, :settings]
- validates_unique :ip_address
- end
-
- end
-end
-
View
13 aws_registry/lib/aws_registry/models/aws_instance.rb
@@ -0,0 +1,13 @@
+# Copyright (c) 2009-2012 VMware, Inc.
+
+module Bosh::AwsRegistry::Models
+ class AwsInstance < Sequel::Model
+
+ def validate
+ validates_presence [:instance_id, :settings]
+ validates_unique :instance_id
+ end
+
+ end
+end
+
View
8 aws_registry/lib/aws_registry/runner.rb
@@ -45,17 +45,11 @@ def start_http_server
private
def handle_em_error(e)
- @shutting_down = true
- log_exception(e, :fatal)
- stop
- end
-
- def log_exception(e, level = :error)
- level = :error unless level == :fatal
@logger.send(level, e.to_s)
if e.respond_to?(:backtrace) && e.backtrace.respond_to?(:join)
@logger.send(level, e.backtrace.join("\n"))
end
+ stop
end
end
View
2 aws_registry/lib/aws_registry/version.rb
@@ -2,6 +2,6 @@
module Bosh
module AwsRegistry
- VERSION = "0.1"
+ VERSION = "0.2"
end
end
View
12 aws_registry/spec/assets/sample_config.yml
@@ -1,11 +1,17 @@
---
-logging:
- level: DEBUG
+loglevel: debug
+
http:
port: 25695
user: admin
password: admin
+
db:
- database: "sqlite:///tmp/aws_registry.db"
+ database: "sqlite:///:memory:"
max_connections: 32
pool_timeout: 10
+
+aws:
+ access_key_id: foo
+ secret_access_key: bar
+ max_retries: 2
View
26 aws_registry/spec/spec_helper.rb
@@ -1,5 +1,7 @@
# Copyright (c) 2009-2012 VMware, Inc.
+$:.unshift(File.expand_path("../../lib", __FILE__))
+
ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile", __FILE__)
require "rubygems"
@@ -11,6 +13,7 @@
require "tmpdir"
require "rspec"
+require "rack/test"
module SpecHelper
class << self
@@ -80,8 +83,31 @@ def reset
SpecHelper.init
+def valid_config
+ {
+ "logfile" => nil,
+ "loglevel" => "debug",
+ "http" => {
+ "user" => "admin",
+ "password" => "admin",
+ "port" => 25777
+ },
+ "db" => {
+ "max_connections" => 433,
+ "pool_timeout" => 227,
+ "database" => "sqlite:///:memory:"
+ },
+ "aws" => {
+ "access_key_id" => "foo",
+ "secret_access_key" => "bar",
+ "max_retries" => 5
+ }
+ }
+end
+
RSpec.configure do |rspec|
rspec.before(:each) do
SpecHelper.reset
+ Bosh::AwsRegistry.logger = Logger.new(StringIO.new)
end
end
View
74 aws_registry/spec/unit/api_controller_spec.rb
@@ -0,0 +1,74 @@
+# Copyright (c) 2009-2012 VMware, Inc.
+
+require File.expand_path("../../spec_helper", __FILE__)
+
+describe Bosh::AwsRegistry::ApiController do
+
+ before(:each) do
+ Bosh::AwsRegistry.http_user = "admin"
+ Bosh::AwsRegistry.http_password = "admin"
+
+ @instance_manager = mock("instance manager")
+ Bosh::AwsRegistry::InstanceManager.stub!(:new).and_return(@instance_manager)
+
+ rack_mock = Rack::MockSession.new(Bosh::AwsRegistry::ApiController.new)
+ @session = Rack::Test::Session.new(rack_mock)
+ end
+
+ def expect_json_response(response, status, body)
+ response.status.should == status
+ Yajl::Parser.parse(response.body).should == body
+ end
+
+ it "returns settings for given EC2 instance (IP check)" do
+ @instance_manager.should_receive(:read_settings).
+ with("foo", "127.0.0.1").and_return("bar")
+
+ @session.get("/instances/foo/settings")
+
+ expect_json_response(@session.last_response, 200,
+ { "status" => "ok", "settings" => "bar" })
+ end
+
+ it "returns settings (authorized user, no IP check)" do
+ @instance_manager.should_receive(:read_settings).
+ with("foo", nil).and_return("bar")
+
+ @session.basic_authorize("admin", "admin")
+ @session.get("/instances/foo/settings")
+
+ expect_json_response(@session.last_response, 200,
+ { "status" => "ok", "settings" => "bar" })
+ end
+
+ it "updates settings" do
+ @session.put("/instances/foo/settings", {}, { :input => "bar" })
+ expect_json_response(@session.last_response, 401,
+ { "status" => "access_denied" })
+
+ @instance_manager.should_receive(:update_settings).
+ with("foo", "bar").and_return(true)
+
+ @session.basic_authorize("admin", "admin")
+ @session.put("/instances/foo/settings", {}, { :input => "bar" })
+
+ expect_json_response(@session.last_response, 200,
+ { "status" => "ok" })
+ end
+
+ it "deletes settings" do
+ @session.delete("/instances/foo/settings")
+ expect_json_response(@session.last_response, 401,
+ { "status" => "access_denied" })
+
+ @instance_manager.should_receive(:delete_settings).
+ with("foo").and_return(true)
+
+ @session.basic_authorize("admin", "admin")
+ @session.delete("/instances/foo/settings")
+
+ expect_json_response(@session.last_response, 200,
+ { "status" => "ok" })
+ end
+
+end
View
56 aws_registry/spec/unit/config_spec.rb
@@ -0,0 +1,56 @@
+# Copyright (c) 2009-2012 VMware, Inc.
+
+require File.expand_path("../../spec_helper", __FILE__)
+
+describe Bosh::AwsRegistry do
+
+ describe "configuring AWS registry" do
+ it "reads provided configuration file and sets singletons" do
+ Bosh::AwsRegistry.configure(valid_config)
+
+ logger = Bosh::AwsRegistry.logger
+
+ logger.should be_kind_of(Logger)
+ logger.level.should == Logger::DEBUG
+
+ Bosh::AwsRegistry.http_port.should == 25777
+ Bosh::AwsRegistry.http_user.should == "admin"
+ Bosh::AwsRegistry.http_password.should == "admin"
+
+ db = Bosh::AwsRegistry.db
+ db.should be_kind_of(Sequel::SQLite::Database)
+ db.opts[:database].should == "/:memory:"
+ db.opts[:max_connections].should == 433
+ db.opts[:pool_timeout].should == 227
+ end
+
+ it "validates configuration file" do
+ expect {
+ Bosh::AwsRegistry.configure("foobar")
+ }.to raise_error(Bosh::AwsRegistry::ConfigError,
+ /Invalid config format/)
+
+ config = valid_config.merge("http" => nil)
+
+ expect {
+ Bosh::AwsRegistry.configure(config)
+ }.to raise_error(Bosh::AwsRegistry::ConfigError,
+ /HTTP configuration is missing/)
+
+ config = valid_config.merge("db" => nil)
+
+ expect {
+ Bosh::AwsRegistry.configure(config)
+ }.to raise_error(Bosh::AwsRegistry::ConfigError,
+ /Database configuration is missing/)
+
+ config = valid_config.merge("aws" => nil)
+
+ expect {
+ Bosh::AwsRegistry.configure(config)
+ }.to raise_error(Bosh::AwsRegistry::ConfigError,
+ /AWS configuration is missing/)
+ end
+
+ end
+end
View
92 aws_registry/spec/unit/instance_manager_spec.rb
@@ -0,0 +1,92 @@
+# Copyright (c) 2009-2012 VMware, Inc.
+
+require File.expand_path("../../spec_helper", __FILE__)
+
+describe Bosh::AwsRegistry::InstanceManager do
+
+ before(:each) do
+ @ec2 = mock("ec2")
+ Bosh::AwsRegistry.ec2 = @ec2
+ end
+
+ let(:manager) do
+ Bosh::AwsRegistry::InstanceManager.new
+ end
+
+ def create_instance(params)
+ Bosh::AwsRegistry::Models::AwsInstance.create(params)
+ end
+
+ def actual_ip_is(ip)
+ instances = mock("instances")
+ instance = mock("instance")
+ @ec2.should_receive(:instances).and_return(instances)
+ instances.should_receive(:[]).with("foo").and_return(instance)
+ instance.should_receive(:private_ip_address).and_return(ip)
+ end
+
+ describe "reading settings" do
+ it "returns settings after verifying IP address" do
+ create_instance(:instance_id => "foo", :settings => "bar")
+ actual_ip_is("10.0.0.1")
+ manager.read_settings("foo", "10.0.0.1").should == "bar"
+ end
+
+ it "raises an error if IP cannot be verified" do
+ create_instance(:instance_id => "foo", :settings => "bar")
+ actual_ip_is("10.0.0.2")
+
+ expect {
+ manager.read_settings("foo", "10.0.0.1")
+ }.to raise_error(Bosh::AwsRegistry::InstanceError,
+ "Instance IP mismatch, expected IP is `10.0.0.1', " \
+ "actual IP is `10.0.0.2'")
+ end
+
+ it "doesn't check remote IP if it's not provided" do
+ create_instance(:instance_id => "foo", :settings => "bar")
+ manager.read_settings("foo").should == "bar"
+ end
+
+ it "raises an error if instance not found" do
+ expect {
+ manager.read_settings("foo")
+ }.to raise_error(Bosh::AwsRegistry::InstanceNotFound,
+ "Can't find instance `foo'")
+ end
+ end
+
+ describe "updating settings" do
+ it "updates settings (new instance)" do
+ manager.update_settings("foo", "baz")
+ manager.read_settings("foo").should == "baz"
+ end
+
+ it "updates settings (existing instance)" do
+ create_instance(:instance_id => "foo", :settings => "bar")
+ manager.read_settings("foo").should == "bar"
+ manager.update_settings("foo", "baz")
+ manager.read_settings("foo").should == "baz"
+ end
+ end
+
+ describe "deleting settings" do
+ it "deletes settings" do
+ manager.update_settings("foo", "baz")
+ manager.delete_settings("foo")
+
+ expect {
+ manager.read_settings("foo")
+ }.to raise_error(Bosh::AwsRegistry::InstanceNotFound,
+ "Can't find instance `foo'")
+ end
+
+ it "raises an error if instance not found" do
+ expect {
+ manager.delete_settings("foo")
+ }.to raise_error(Bosh::AwsRegistry::InstanceNotFound,
+ "Can't find instance `foo'")
+ end
+ end
+
+end
View
77 aws_registry/spec/unit/runner_spec.rb
@@ -4,12 +4,81 @@
describe Bosh::AwsRegistry::Runner do
- it "reads provided configuration file and sets singletons" do
- pending
+ def make_runner(config_file)
+ Bosh::AwsRegistry::Runner.new(config_file)
end
- it "validates configuration file" do
- pending
+ def write_config(file, config)
+ File.open(file, "w") do |f|
+ YAML.dump(config, f)
+ end
+ end
+
+ describe "initializing" do
+ it "configures AWS registry using provided file" do
+ config_file = Tempfile.new("config")
+ config = { "key" => "value" }
+ write_config(config_file.path, config)
+
+ Bosh::AwsRegistry.should_receive(:configure).with(config)
+ make_runner(config_file.path)
+ end
+
+ it "fails if config file not found" do
+ expect {
+ make_runner("foo")
+ }.to raise_error(Bosh::AwsRegistry::ConfigError, "Cannot find file `foo'")
+ end
+
+ it "fails when config file has incorrect format" do
+ config_file = Tempfile.new("config")
+ config = "foo"
+ write_config(config_file.path, config)
+
+ expect {
+ make_runner(config_file.path)
+ }.to raise_error(Bosh::AwsRegistry::ConfigError, /Incorrect file format/)
+ end
+
+ it "fails when some syscall fails" do
+ config_file = Tempfile.new("config")
+ write_config(config_file.path, { "foo" => "bar" })
+
+ YAML.stub!(:load_file).and_raise(SystemCallError.new("baz"))
+
+ expect {
+ make_runner(config_file.path)
+ }.to raise_error(Bosh::AwsRegistry::ConfigError, /baz/)
+ end
+ end
+
+ describe "running/stopping" do
+ before(:each) do
+ @config_file = Tempfile.new("config")
+ write_config(@config_file.path, valid_config)
+ end
+
+ it "spins up/shuts down reactor and HTTP server" do
+ runner = make_runner(@config_file)
+ mock_thin = mock("thin")
+
+ EM.should_receive(:run).and_yield
+
+ Thin::Server.should_receive(:new).
+ with("0.0.0.0", 25777, :signals => false).
+ and_return(mock_thin)
+
+ mock_thin.should_receive(:start!)
+
+ runner.run
+
+ mock_thin.should_receive(:stop!)
+ EM.should_receive(:stop)
+
+ runner.stop
+ end
end
end
+
+
View
BIN aws_registry/vendor/cache/aws-sdk-1.3.6.gem
Binary file not shown.
View
BIN aws_registry/vendor/cache/httparty-0.8.1.gem
Binary file not shown.
View
BIN aws_registry/vendor/cache/json-1.6.5.gem
Binary file not shown.
View
BIN aws_registry/vendor/cache/multi_xml-0.4.1.gem
Binary file not shown.
View
BIN aws_registry/vendor/cache/nokogiri-1.5.0.gem
Binary file not shown.
View
BIN aws_registry/vendor/cache/rack-test-0.6.1.gem
Binary file not shown.
View
BIN aws_registry/vendor/cache/uuidtools-2.1.2.gem
Binary file not shown.

0 comments on commit d5ffa3c

Please sign in to comment.