bosh-registry 1.5.0.pre.1113
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.md +105 -0
- data/bin/bosh-registry +28 -0
- data/bin/bosh-registry-migrate +35 -0
- data/db/migrations/20130306112035_create_registry_instances.rb +12 -0
- data/lib/bosh/registry.rb +26 -0
- data/lib/bosh/registry/api_controller.rb +68 -0
- data/lib/bosh/registry/client.rb +124 -0
- data/lib/bosh/registry/config.rb +76 -0
- data/lib/bosh/registry/errors.rb +20 -0
- data/lib/bosh/registry/instance_manager.rb +65 -0
- data/lib/bosh/registry/instance_manager/aws.rb +53 -0
- data/lib/bosh/registry/instance_manager/openstack.rb +69 -0
- data/lib/bosh/registry/models.rb +8 -0
- data/lib/bosh/registry/models/registry_instance.rb +12 -0
- data/lib/bosh/registry/runner.rb +48 -0
- data/lib/bosh/registry/version.rb +7 -0
- data/lib/bosh/registry/yaml_helper.rb +24 -0
- metadata +162 -0
data/README.md
ADDED
@@ -0,0 +1,105 @@
|
|
1
|
+
# BOSH Registry
|
2
|
+
Copyright (c) 2009-2013 VMware, Inc.
|
3
|
+
|
4
|
+
For online documentation see: http://rubydoc.info/gems/bosh-registry/
|
5
|
+
|
6
|
+
## Usage
|
7
|
+
|
8
|
+
bin/bosh-registry-migrate [<options>]
|
9
|
+
-c, --config FILE Bosh Registry configuration file
|
10
|
+
|
11
|
+
bin/bosh-registry [<options>]
|
12
|
+
-c, --config FILE Bosh Registry configuration file
|
13
|
+
|
14
|
+
## Configuration
|
15
|
+
|
16
|
+
These options are passed to the Bosh Registry when it is instantiated.
|
17
|
+
|
18
|
+
### Registry options
|
19
|
+
|
20
|
+
These are the options for the Registry HTTP server (by default server is
|
21
|
+
bound to address 0.0.0.0):
|
22
|
+
|
23
|
+
* `port` (required)
|
24
|
+
Registry port
|
25
|
+
* `user` (required)
|
26
|
+
Registry user (for HTTP Basic authentication)
|
27
|
+
* `password` (required)
|
28
|
+
Registry password (for HTTP Basic authentication)
|
29
|
+
|
30
|
+
### Database options
|
31
|
+
|
32
|
+
These are the options for the database connection where registry will store
|
33
|
+
instance properties:
|
34
|
+
|
35
|
+
* `database` (required)
|
36
|
+
DB connection URI
|
37
|
+
* `max_connections` (required)
|
38
|
+
Maximum size of the connection pool
|
39
|
+
* `pool_timeout` (required)
|
40
|
+
Number of seconds to wait if a connection cannot be acquired before
|
41
|
+
raising an error
|
42
|
+
|
43
|
+
### Cloud options
|
44
|
+
|
45
|
+
These are the options for the cloud connection where registry will fetch for
|
46
|
+
the IP addresses belonging to a instances:
|
47
|
+
|
48
|
+
* `plugin` (required)
|
49
|
+
Cloud Provider (currently supported: aws and openstack)
|
50
|
+
|
51
|
+
#### AWS options
|
52
|
+
|
53
|
+
These are the credentials to connect to AWS services:
|
54
|
+
|
55
|
+
* `access_key_id` (required)
|
56
|
+
IAM Access Key ID
|
57
|
+
* `secret_access_key` (required)
|
58
|
+
AWS IAM Secret Access Key
|
59
|
+
* `region` (required)
|
60
|
+
AWS EC2 Region
|
61
|
+
* `max_retries` (optional, defaults to 2)
|
62
|
+
Max number of retries to connect to AWS
|
63
|
+
|
64
|
+
#### OpenStack options
|
65
|
+
|
66
|
+
These are the credentials to connect to OpenStack services:
|
67
|
+
|
68
|
+
* `auth_url` (required)
|
69
|
+
URL of the OpenStack Identity endpoint to connect to
|
70
|
+
* `username` (required)
|
71
|
+
OpenStack user name
|
72
|
+
* `api_key` (required)
|
73
|
+
OpenStack API key
|
74
|
+
* `tenant` (required)
|
75
|
+
OpenStack tenant name
|
76
|
+
* `region` (optional)
|
77
|
+
OpenStack region
|
78
|
+
* `endpoint_type` (optional)
|
79
|
+
OpenStack endpoint type (publicURL (default), adminURL, internalURL)
|
80
|
+
|
81
|
+
## Example
|
82
|
+
|
83
|
+
This is a sample of an Bosh Registry configuration file:
|
84
|
+
|
85
|
+
---
|
86
|
+
loglevel: debug
|
87
|
+
|
88
|
+
http:
|
89
|
+
port: 25695
|
90
|
+
user: admin
|
91
|
+
password: admin
|
92
|
+
|
93
|
+
db:
|
94
|
+
database: "sqlite:///:memory:"
|
95
|
+
max_connections: 32
|
96
|
+
pool_timeout: 10
|
97
|
+
|
98
|
+
cloud:
|
99
|
+
plugin: openstack
|
100
|
+
openstack:
|
101
|
+
auth_url: "http://127.0.0.1:5000/v2.0"
|
102
|
+
username: foo
|
103
|
+
api_key: bar
|
104
|
+
tenant: foo
|
105
|
+
region:
|
data/bin/bosh-registry
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require "bosh/registry"
|
4
|
+
require "optparse"
|
5
|
+
|
6
|
+
config_file = nil
|
7
|
+
|
8
|
+
opts = OptionParser.new do |opts|
|
9
|
+
opts.on("-c", "--config FILE", "configuration file") do |opt|
|
10
|
+
config_file = opt
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
opts.parse!(ARGV.dup)
|
15
|
+
|
16
|
+
if config_file.nil?
|
17
|
+
puts opts
|
18
|
+
exit 1
|
19
|
+
end
|
20
|
+
|
21
|
+
runner = Bosh::Registry::Runner.new(config_file)
|
22
|
+
|
23
|
+
Signal.trap("INT") do
|
24
|
+
runner.stop
|
25
|
+
exit(1)
|
26
|
+
end
|
27
|
+
|
28
|
+
runner.run
|
@@ -0,0 +1,35 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require "logger"
|
4
|
+
require "sequel"
|
5
|
+
require "bosh/registry"
|
6
|
+
require "optparse"
|
7
|
+
|
8
|
+
config_file = nil
|
9
|
+
|
10
|
+
opts = OptionParser.new do |opts|
|
11
|
+
opts.on("-c", "--config FILE", "configuration file") do |opt|
|
12
|
+
config_file = opt
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
opts.parse!(ARGV.dup)
|
17
|
+
|
18
|
+
if config_file.nil?
|
19
|
+
puts opts
|
20
|
+
exit 1
|
21
|
+
end
|
22
|
+
|
23
|
+
include Bosh::Registry::YamlHelper
|
24
|
+
|
25
|
+
config = load_yaml_file(config_file)
|
26
|
+
|
27
|
+
db = Bosh::Registry.connect_db(config["db"])
|
28
|
+
migrations_dir = File.expand_path("../../db/migrations", __FILE__)
|
29
|
+
|
30
|
+
options = {
|
31
|
+
:table => "registry_schema"
|
32
|
+
}
|
33
|
+
|
34
|
+
Sequel.extension :migration
|
35
|
+
Sequel::TimestampMigrator.run(db, migrations_dir, options)
|
@@ -0,0 +1,12 @@
|
|
1
|
+
# Copyright (c) 2009-2013 VMware, Inc.
|
2
|
+
|
3
|
+
Sequel.migration do
|
4
|
+
change do
|
5
|
+
create_table :registry_instances do
|
6
|
+
primary_key :id
|
7
|
+
|
8
|
+
String :instance_id, :null => false, :unique => true
|
9
|
+
String :settings, :null => false, :text => true
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
# Copyright (c) 2009-2013 VMware, Inc.
|
2
|
+
|
3
|
+
module Bosh
|
4
|
+
module Registry
|
5
|
+
autoload :Models, "bosh/registry/models"
|
6
|
+
end
|
7
|
+
end
|
8
|
+
|
9
|
+
require "aws-sdk"
|
10
|
+
require "fog"
|
11
|
+
require "logger"
|
12
|
+
require "sequel"
|
13
|
+
require "sinatra/base"
|
14
|
+
require "thin"
|
15
|
+
require "yajl"
|
16
|
+
|
17
|
+
require "bosh/registry/yaml_helper"
|
18
|
+
|
19
|
+
require "bosh/registry/api_controller"
|
20
|
+
require "bosh/registry/config"
|
21
|
+
require "bosh/registry/errors"
|
22
|
+
require "bosh/registry/instance_manager"
|
23
|
+
require "bosh/registry/runner"
|
24
|
+
require "bosh/registry/version"
|
25
|
+
|
26
|
+
Sequel::Model.plugin :validation_helpers
|
@@ -0,0 +1,68 @@
|
|
1
|
+
# Copyright (c) 2009-2013 VMware, Inc.
|
2
|
+
|
3
|
+
module Bosh::Registry
|
4
|
+
|
5
|
+
class ApiController < Sinatra::Base
|
6
|
+
|
7
|
+
not_found do
|
8
|
+
exception = request.env["sinatra.error"]
|
9
|
+
@logger.error(exception.message)
|
10
|
+
json(:status => "not_found")
|
11
|
+
end
|
12
|
+
|
13
|
+
error do
|
14
|
+
exception = request.env["sinatra.error"]
|
15
|
+
@logger.error(exception)
|
16
|
+
status(500)
|
17
|
+
json(:status => "error")
|
18
|
+
end
|
19
|
+
|
20
|
+
get "/instances/:instance_id/settings" do
|
21
|
+
ip_check = authorized? ? nil : request.ip
|
22
|
+
settings = @instance_manager.read_settings(params[:instance_id], ip_check)
|
23
|
+
json(:status => "ok", :settings => settings)
|
24
|
+
end
|
25
|
+
|
26
|
+
put "/instances/:instance_id/settings" do
|
27
|
+
protected!
|
28
|
+
@instance_manager.update_settings(params[:instance_id], request.body.read)
|
29
|
+
json(:status => "ok")
|
30
|
+
end
|
31
|
+
|
32
|
+
delete "/instances/:instance_id/settings" do
|
33
|
+
protected!
|
34
|
+
@instance_manager.delete_settings(params[:instance_id]) rescue InstanceNotFound
|
35
|
+
json(:status => "ok")
|
36
|
+
end
|
37
|
+
|
38
|
+
def initialize
|
39
|
+
super
|
40
|
+
@logger = Bosh::Registry.logger
|
41
|
+
|
42
|
+
@users = Set.new
|
43
|
+
@users << [Bosh::Registry.http_user, Bosh::Registry.http_password]
|
44
|
+
@instance_manager = Bosh::Registry.instance_manager
|
45
|
+
end
|
46
|
+
|
47
|
+
def protected!
|
48
|
+
unless authorized?
|
49
|
+
headers("WWW-Authenticate" => 'Basic realm="Bosh Registry"')
|
50
|
+
halt(401, json("status" => "access_denied"))
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def authorized?
|
55
|
+
@auth ||= Rack::Auth::Basic::Request.new(request.env)
|
56
|
+
@auth.provided? &&
|
57
|
+
@auth.basic? &&
|
58
|
+
@auth.credentials &&
|
59
|
+
@users.include?(@auth.credentials)
|
60
|
+
end
|
61
|
+
|
62
|
+
def json(payload)
|
63
|
+
Yajl::Encoder.encode(payload)
|
64
|
+
end
|
65
|
+
|
66
|
+
end
|
67
|
+
|
68
|
+
end
|
@@ -0,0 +1,124 @@
|
|
1
|
+
# Copyright (c) 2009-2013 VMware, Inc.
|
2
|
+
# Copyright (c) 2012 Piston Cloud Computing, Inc.
|
3
|
+
require 'httpclient'
|
4
|
+
require 'cloud/errors'
|
5
|
+
require 'base64'
|
6
|
+
|
7
|
+
module Bosh::Registry
|
8
|
+
##
|
9
|
+
# Represents Bosh Registry Client. It performs CRUD operations against
|
10
|
+
# the OpenStack Registry.
|
11
|
+
#
|
12
|
+
# Settings example:
|
13
|
+
# settings = {
|
14
|
+
# "vm" => {
|
15
|
+
# "name" => server_name
|
16
|
+
# },
|
17
|
+
# "agent_id" => agent_id,
|
18
|
+
# "networks" => network_spec,
|
19
|
+
# "disks" => {
|
20
|
+
# "system" => "/dev/vda",
|
21
|
+
# "ephemeral" => "/dev/vdb",
|
22
|
+
# "persistent" => {"volume_id" => device_name}
|
23
|
+
# }
|
24
|
+
# }
|
25
|
+
class Client
|
26
|
+
attr_reader :endpoint
|
27
|
+
attr_reader :user
|
28
|
+
attr_reader :password
|
29
|
+
|
30
|
+
def initialize(endpoint, user, password)
|
31
|
+
@endpoint = endpoint
|
32
|
+
|
33
|
+
unless @endpoint =~ /^http:\/\//
|
34
|
+
@endpoint = "http://#{@endpoint}"
|
35
|
+
end
|
36
|
+
|
37
|
+
@user = user
|
38
|
+
@password = password
|
39
|
+
|
40
|
+
auth = Base64.encode64("#{@user}:#{@password}").gsub("\n", '')
|
41
|
+
|
42
|
+
@headers = {
|
43
|
+
"Accept" => 'application/json',
|
44
|
+
"Authorization" => "Basic #{auth}"
|
45
|
+
}
|
46
|
+
|
47
|
+
@client = HTTPClient.new
|
48
|
+
end
|
49
|
+
|
50
|
+
##
|
51
|
+
# Update instance settings in the registry
|
52
|
+
# @param [String] instance_id EC2 instance id
|
53
|
+
# @param [Hash] settings New agent settings
|
54
|
+
# @return [Boolean]
|
55
|
+
def update_settings(instance_id, settings)
|
56
|
+
unless settings.is_a?(Hash)
|
57
|
+
raise ArgumentError, "Invalid settings format, Hash expected, #{settings.class} given"
|
58
|
+
end
|
59
|
+
|
60
|
+
payload = Yajl::Encoder.encode(settings)
|
61
|
+
url = "#{@endpoint}/instances/#{instance_id}/settings"
|
62
|
+
|
63
|
+
response = @client.put(url, {:body => payload, :header => @headers})
|
64
|
+
|
65
|
+
if response.status != 200
|
66
|
+
cloud_error("Cannot update settings for '#{instance_id}', got HTTP #{response.status}")
|
67
|
+
end
|
68
|
+
|
69
|
+
true
|
70
|
+
end
|
71
|
+
|
72
|
+
##
|
73
|
+
# Read instance settings from the registry
|
74
|
+
# @param [String] instance_id EC2 instance id
|
75
|
+
# @return [Hash] Agent settings
|
76
|
+
def read_settings(instance_id)
|
77
|
+
url = "#{@endpoint}/instances/#{instance_id}/settings"
|
78
|
+
|
79
|
+
response = @client.get(url, {:header => @headers})
|
80
|
+
|
81
|
+
if response.status != 200
|
82
|
+
cloud_error("Cannot read settings for '#{instance_id}', got HTTP #{response.status}")
|
83
|
+
end
|
84
|
+
|
85
|
+
body = Yajl::Parser.parse(response.body)
|
86
|
+
|
87
|
+
unless body.is_a?(Hash)
|
88
|
+
cloud_error("Invalid registry response, Hash expected, got #{body.class}: #{body}")
|
89
|
+
end
|
90
|
+
|
91
|
+
settings = Yajl::Parser.parse(body["settings"])
|
92
|
+
|
93
|
+
unless settings.is_a?(Hash)
|
94
|
+
cloud_error("Invalid settings format, Hash expected, got #{settings.class}: #{settings}")
|
95
|
+
end
|
96
|
+
|
97
|
+
settings
|
98
|
+
rescue Yajl::ParseError => e
|
99
|
+
cloud_error("Cannot parse settings for '#{instance_id}': #{e.message}")
|
100
|
+
end
|
101
|
+
|
102
|
+
##
|
103
|
+
# Delete instance settings from the registry
|
104
|
+
# @param [String] instance_id EC2 instance id
|
105
|
+
# @return [Boolean]
|
106
|
+
def delete_settings(instance_id)
|
107
|
+
url = "#{@endpoint}/instances/#{instance_id}/settings"
|
108
|
+
|
109
|
+
response = @client.delete(url, {:header => @headers})
|
110
|
+
|
111
|
+
if response.status != 200
|
112
|
+
cloud_error("Cannot delete settings for '#{instance_id}', got HTTP #{response.status}")
|
113
|
+
end
|
114
|
+
|
115
|
+
true
|
116
|
+
end
|
117
|
+
|
118
|
+
private
|
119
|
+
|
120
|
+
def cloud_error(message)
|
121
|
+
raise Bosh::Clouds::CloudError, message
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|
@@ -0,0 +1,76 @@
|
|
1
|
+
# Copyright (c) 2009-2013 VMware, Inc.
|
2
|
+
|
3
|
+
module Bosh::Registry
|
4
|
+
|
5
|
+
class << self
|
6
|
+
|
7
|
+
attr_accessor :logger
|
8
|
+
attr_accessor :http_port
|
9
|
+
attr_accessor :http_user
|
10
|
+
attr_accessor :http_password
|
11
|
+
attr_accessor :db
|
12
|
+
attr_accessor :instance_manager
|
13
|
+
|
14
|
+
def configure(config)
|
15
|
+
validate_config(config)
|
16
|
+
|
17
|
+
@logger ||= Logger.new(config["logfile"] || STDOUT)
|
18
|
+
if config["loglevel"].kind_of?(String)
|
19
|
+
@logger.level = Logger.const_get(config["loglevel"].upcase)
|
20
|
+
end
|
21
|
+
|
22
|
+
@http_port = config["http"]["port"]
|
23
|
+
@http_user = config["http"]["user"]
|
24
|
+
@http_password = config["http"]["password"]
|
25
|
+
|
26
|
+
@db = connect_db(config["db"])
|
27
|
+
|
28
|
+
plugin = config["cloud"]["plugin"]
|
29
|
+
begin
|
30
|
+
require "bosh/registry/instance_manager/#{plugin}"
|
31
|
+
rescue LoadError
|
32
|
+
raise ConfigError, "Could not find Provider Plugin: #{plugin}"
|
33
|
+
end
|
34
|
+
@instance_manager = Bosh::Registry::InstanceManager.const_get(plugin.capitalize).new(config["cloud"])
|
35
|
+
end
|
36
|
+
|
37
|
+
def connect_db(db_config)
|
38
|
+
connection_options = db_config.delete('connection_options') {{}}
|
39
|
+
db_config.delete_if { |_, v| v.to_s.empty? }
|
40
|
+
db_config = db_config.merge(connection_options)
|
41
|
+
|
42
|
+
db = Sequel.connect(db_config)
|
43
|
+
if logger
|
44
|
+
db.logger = @logger
|
45
|
+
db.sql_log_level = :debug
|
46
|
+
end
|
47
|
+
|
48
|
+
db
|
49
|
+
end
|
50
|
+
|
51
|
+
def validate_config(config)
|
52
|
+
unless config.is_a?(Hash)
|
53
|
+
raise ConfigError, "Invalid config format, Hash expected, " \
|
54
|
+
"#{config.class} given"
|
55
|
+
end
|
56
|
+
|
57
|
+
unless config.has_key?("http") && config["http"].is_a?(Hash)
|
58
|
+
raise ConfigError, "HTTP configuration is missing from config file"
|
59
|
+
end
|
60
|
+
|
61
|
+
unless config.has_key?("db") && config["db"].is_a?(Hash)
|
62
|
+
raise ConfigError, "Database configuration is missing from config file"
|
63
|
+
end
|
64
|
+
|
65
|
+
unless config.has_key?("cloud") && config["cloud"].is_a?(Hash)
|
66
|
+
raise ConfigError, "Cloud configuration is missing from config file"
|
67
|
+
end
|
68
|
+
|
69
|
+
if config["cloud"]["plugin"].nil?
|
70
|
+
raise ConfigError, "Cloud plugin is missing from config file"
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
end
|
75
|
+
|
76
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
# Copyright (c) 2009-2013 VMware, Inc.
|
2
|
+
|
3
|
+
module Bosh::Registry
|
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,65 @@
|
|
1
|
+
# Copyright (c) 2009-2013 VMware, Inc.
|
2
|
+
|
3
|
+
module Bosh::Registry
|
4
|
+
|
5
|
+
class InstanceManager
|
6
|
+
|
7
|
+
##
|
8
|
+
# Updates instance settings
|
9
|
+
# @param [String] instance_id instance id (instance record
|
10
|
+
# will be created in DB if it doesn't already exist)
|
11
|
+
# @param [String] settings New settings for the instance
|
12
|
+
def update_settings(instance_id, settings)
|
13
|
+
params = {
|
14
|
+
:instance_id => instance_id
|
15
|
+
}
|
16
|
+
|
17
|
+
instance = Models::RegistryInstance[params] || Models::RegistryInstance.new(params)
|
18
|
+
instance.settings = settings
|
19
|
+
instance.save
|
20
|
+
end
|
21
|
+
|
22
|
+
##
|
23
|
+
# Reads instance settings
|
24
|
+
# @param [String] instance_id instance id
|
25
|
+
# @param [optional, String] remote_ip If this IP is provided,
|
26
|
+
# check will be performed to see if it instance id
|
27
|
+
# actually has this IP address according to the IaaS.
|
28
|
+
def read_settings(instance_id, remote_ip = nil)
|
29
|
+
check_instance_ips(remote_ip, instance_id) if remote_ip
|
30
|
+
|
31
|
+
get_instance(instance_id).settings
|
32
|
+
end
|
33
|
+
|
34
|
+
##
|
35
|
+
# Seletes instance settings
|
36
|
+
# @param [String] instance_id instance id
|
37
|
+
def delete_settings(instance_id)
|
38
|
+
get_instance(instance_id).destroy
|
39
|
+
end
|
40
|
+
|
41
|
+
private
|
42
|
+
|
43
|
+
def check_instance_ips(ip, instance_id)
|
44
|
+
return if ip == "127.0.0.1"
|
45
|
+
actual_ips = instance_ips(instance_id)
|
46
|
+
unless actual_ips.include?(ip)
|
47
|
+
raise InstanceError, "Instance IP mismatch, expected IP is " \
|
48
|
+
"`%s', actual IP(s): `%s'" %
|
49
|
+
[ ip, actual_ips.join(", ") ]
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def get_instance(instance_id)
|
54
|
+
instance = Models::RegistryInstance[:instance_id => instance_id]
|
55
|
+
|
56
|
+
if instance.nil?
|
57
|
+
raise InstanceNotFound, "Can't find instance `#{instance_id}'"
|
58
|
+
end
|
59
|
+
|
60
|
+
instance
|
61
|
+
end
|
62
|
+
|
63
|
+
end
|
64
|
+
|
65
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
# Copyright (c) 2009-2013 VMware, Inc.
|
2
|
+
|
3
|
+
module Bosh::Registry
|
4
|
+
|
5
|
+
class InstanceManager
|
6
|
+
|
7
|
+
class Aws < InstanceManager
|
8
|
+
|
9
|
+
AWS_MAX_RETRIES = 2
|
10
|
+
|
11
|
+
def initialize(cloud_config)
|
12
|
+
validate_options(cloud_config)
|
13
|
+
|
14
|
+
@logger = Bosh::Registry.logger
|
15
|
+
|
16
|
+
@aws_properties = cloud_config["aws"]
|
17
|
+
@aws_options = {
|
18
|
+
:access_key_id => @aws_properties["access_key_id"],
|
19
|
+
:secret_access_key => @aws_properties["secret_access_key"],
|
20
|
+
:max_retries => @aws_properties["max_retries"] || AWS_MAX_RETRIES,
|
21
|
+
:ec2_endpoint => "ec2.#{@aws_properties['region']}.amazonaws.com",
|
22
|
+
:logger => @logger
|
23
|
+
}
|
24
|
+
@ec2 = AWS::EC2.new(@aws_options)
|
25
|
+
end
|
26
|
+
|
27
|
+
def validate_options(cloud_config)
|
28
|
+
unless cloud_config.has_key?("aws") &&
|
29
|
+
cloud_config["aws"].is_a?(Hash) &&
|
30
|
+
cloud_config["aws"]["access_key_id"] &&
|
31
|
+
cloud_config["aws"]["secret_access_key"] &&
|
32
|
+
cloud_config["aws"]["region"]
|
33
|
+
raise ConfigError, "Invalid AWS configuration parameters"
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
# Get the list of IPs belonging to this instance
|
38
|
+
def instance_ips(instance_id)
|
39
|
+
instance = @ec2.instances[instance_id]
|
40
|
+
ips = [instance.private_ip_address, instance.public_ip_address]
|
41
|
+
if instance.has_elastic_ip?
|
42
|
+
ips << instance.elastic_ip.public_ip
|
43
|
+
end
|
44
|
+
ips
|
45
|
+
rescue AWS::Errors::Base => e
|
46
|
+
raise Bosh::Registry::AwsError, "AWS error: #{e}"
|
47
|
+
end
|
48
|
+
|
49
|
+
end
|
50
|
+
|
51
|
+
end
|
52
|
+
|
53
|
+
end
|
@@ -0,0 +1,69 @@
|
|
1
|
+
# Copyright (c) 2009-2013 VMware, Inc.
|
2
|
+
|
3
|
+
module Bosh::Registry
|
4
|
+
|
5
|
+
class InstanceManager
|
6
|
+
|
7
|
+
class Openstack < InstanceManager
|
8
|
+
|
9
|
+
def initialize(cloud_config)
|
10
|
+
validate_options(cloud_config)
|
11
|
+
|
12
|
+
@logger = Bosh::Registry.logger
|
13
|
+
|
14
|
+
@openstack_properties = cloud_config["openstack"]
|
15
|
+
|
16
|
+
unless @openstack_properties["auth_url"].match(/\/tokens$/)
|
17
|
+
@openstack_properties["auth_url"] = @openstack_properties["auth_url"] + "/tokens"
|
18
|
+
end
|
19
|
+
|
20
|
+
@openstack_options = {
|
21
|
+
:provider => "OpenStack",
|
22
|
+
:openstack_auth_url => @openstack_properties["auth_url"],
|
23
|
+
:openstack_username => @openstack_properties["username"],
|
24
|
+
:openstack_api_key => @openstack_properties["api_key"],
|
25
|
+
:openstack_tenant => @openstack_properties["tenant"],
|
26
|
+
:openstack_region => @openstack_properties["region"],
|
27
|
+
:openstack_endpoint_type => @openstack_properties["endpoint_type"]
|
28
|
+
}
|
29
|
+
end
|
30
|
+
|
31
|
+
def openstack
|
32
|
+
@openstack ||= Fog::Compute.new(@openstack_options)
|
33
|
+
end
|
34
|
+
|
35
|
+
def validate_options(cloud_config)
|
36
|
+
unless cloud_config.has_key?("openstack") &&
|
37
|
+
cloud_config["openstack"].is_a?(Hash) &&
|
38
|
+
cloud_config["openstack"]["auth_url"] &&
|
39
|
+
cloud_config["openstack"]["username"] &&
|
40
|
+
cloud_config["openstack"]["api_key"] &&
|
41
|
+
cloud_config["openstack"]["tenant"]
|
42
|
+
raise ConfigError, "Invalid OpenStack configuration parameters"
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
# Get the list of IPs belonging to this instance
|
47
|
+
def instance_ips(instance_id)
|
48
|
+
# If we get an Unauthorized error, it could mean that the OpenStack auth token has expired, so we are
|
49
|
+
# going renew the fog connection one time to make sure that we get a new non-expired token.
|
50
|
+
retried = false
|
51
|
+
begin
|
52
|
+
instance = openstack.servers.find { |s| s.name == instance_id }
|
53
|
+
rescue Excon::Errors::Unauthorized => e
|
54
|
+
unless retried
|
55
|
+
retried = true
|
56
|
+
@openstack = nil
|
57
|
+
retry
|
58
|
+
end
|
59
|
+
raise ConnectionError, "Unable to connect to OpenStack API: #{e.message}"
|
60
|
+
end
|
61
|
+
raise InstanceNotFound, "Instance `#{instance_id}' not found" unless instance
|
62
|
+
return (instance.private_ip_addresses + instance.floating_ip_addresses).compact
|
63
|
+
end
|
64
|
+
|
65
|
+
end
|
66
|
+
|
67
|
+
end
|
68
|
+
|
69
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
# Copyright (c) 2009-2013 VMware, Inc.
|
2
|
+
|
3
|
+
module Bosh::Registry
|
4
|
+
class Runner
|
5
|
+
include YamlHelper
|
6
|
+
|
7
|
+
def initialize(config_file)
|
8
|
+
Bosh::Registry.configure(load_yaml_file(config_file))
|
9
|
+
|
10
|
+
@logger = Bosh::Registry.logger
|
11
|
+
@http_port = Bosh::Registry.http_port
|
12
|
+
@http_user = Bosh::Registry.http_user
|
13
|
+
@http_password = Bosh::Registry.http_password
|
14
|
+
end
|
15
|
+
|
16
|
+
def run
|
17
|
+
@logger.info("BOSH Registry starting...")
|
18
|
+
start_http_server
|
19
|
+
end
|
20
|
+
|
21
|
+
def stop
|
22
|
+
@logger.info("BOSH Registry shutting down...")
|
23
|
+
@http_server.stop! if @http_server
|
24
|
+
end
|
25
|
+
|
26
|
+
def start_http_server
|
27
|
+
@logger.info "HTTP server is starting on port #{@http_port}..."
|
28
|
+
@http_server = Thin::Server.new("0.0.0.0", @http_port, :signals => false) do
|
29
|
+
Thin::Logging.silent = true
|
30
|
+
map "/" do
|
31
|
+
run Bosh::Registry::ApiController.new
|
32
|
+
end
|
33
|
+
end
|
34
|
+
@http_server.start!
|
35
|
+
end
|
36
|
+
|
37
|
+
private
|
38
|
+
|
39
|
+
def handle_em_error(e, level = :fatal)
|
40
|
+
@logger.send(level, e.to_s)
|
41
|
+
if e.respond_to?(:backtrace) && e.backtrace.respond_to?(:join)
|
42
|
+
@logger.send(level, e.backtrace.join("\n"))
|
43
|
+
end
|
44
|
+
stop
|
45
|
+
end
|
46
|
+
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
# Copyright (c) 2009-2013 VMware, Inc.
|
2
|
+
|
3
|
+
module Bosh::Registry
|
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 = Psych.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
|
metadata
ADDED
@@ -0,0 +1,162 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: bosh-registry
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.5.0.pre.1113
|
5
|
+
prerelease: 6
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- VMware
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2013-10-16 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: 3.43.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: 3.43.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: 1.4.2
|
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: 1.4.2
|
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: 1.5.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: 1.5.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: 1.1.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: 1.1.0
|
78
|
+
- !ruby/object:Gem::Dependency
|
79
|
+
name: fog
|
80
|
+
requirement: !ruby/object:Gem::Requirement
|
81
|
+
none: false
|
82
|
+
requirements:
|
83
|
+
- - ~>
|
84
|
+
- !ruby/object:Gem::Version
|
85
|
+
version: 1.14.0
|
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.14.0
|
94
|
+
- !ruby/object:Gem::Dependency
|
95
|
+
name: aws-sdk
|
96
|
+
requirement: !ruby/object:Gem::Requirement
|
97
|
+
none: false
|
98
|
+
requirements:
|
99
|
+
- - '='
|
100
|
+
- !ruby/object:Gem::Version
|
101
|
+
version: 1.8.5
|
102
|
+
type: :runtime
|
103
|
+
prerelease: false
|
104
|
+
version_requirements: !ruby/object:Gem::Requirement
|
105
|
+
none: false
|
106
|
+
requirements:
|
107
|
+
- - '='
|
108
|
+
- !ruby/object:Gem::Version
|
109
|
+
version: 1.8.5
|
110
|
+
description: ! 'BOSH Registry
|
111
|
+
|
112
|
+
cfd471'
|
113
|
+
email: support@cloudfoundry.com
|
114
|
+
executables:
|
115
|
+
- bosh-registry
|
116
|
+
- bosh-registry-migrate
|
117
|
+
extensions: []
|
118
|
+
extra_rdoc_files: []
|
119
|
+
files:
|
120
|
+
- db/migrations/20130306112035_create_registry_instances.rb
|
121
|
+
- lib/bosh/registry.rb
|
122
|
+
- lib/bosh/registry/api_controller.rb
|
123
|
+
- lib/bosh/registry/client.rb
|
124
|
+
- lib/bosh/registry/config.rb
|
125
|
+
- lib/bosh/registry/errors.rb
|
126
|
+
- lib/bosh/registry/instance_manager.rb
|
127
|
+
- lib/bosh/registry/instance_manager/aws.rb
|
128
|
+
- lib/bosh/registry/instance_manager/openstack.rb
|
129
|
+
- lib/bosh/registry/models.rb
|
130
|
+
- lib/bosh/registry/models/registry_instance.rb
|
131
|
+
- lib/bosh/registry/runner.rb
|
132
|
+
- lib/bosh/registry/version.rb
|
133
|
+
- lib/bosh/registry/yaml_helper.rb
|
134
|
+
- README.md
|
135
|
+
- bin/bosh-registry
|
136
|
+
- bin/bosh-registry-migrate
|
137
|
+
homepage: https://github.com/cloudfoundry/bosh
|
138
|
+
licenses:
|
139
|
+
- Apache 2.0
|
140
|
+
post_install_message:
|
141
|
+
rdoc_options: []
|
142
|
+
require_paths:
|
143
|
+
- lib
|
144
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
145
|
+
none: false
|
146
|
+
requirements:
|
147
|
+
- - ! '>='
|
148
|
+
- !ruby/object:Gem::Version
|
149
|
+
version: 1.9.3
|
150
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
151
|
+
none: false
|
152
|
+
requirements:
|
153
|
+
- - ! '>'
|
154
|
+
- !ruby/object:Gem::Version
|
155
|
+
version: 1.3.1
|
156
|
+
requirements: []
|
157
|
+
rubyforge_project:
|
158
|
+
rubygems_version: 1.8.23
|
159
|
+
signing_key:
|
160
|
+
specification_version: 3
|
161
|
+
summary: BOSH Registry
|
162
|
+
test_files: []
|