vcap_services_base 0.2.10
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/lib/base/abstract.rb +11 -0
- data/lib/base/api/message.rb +31 -0
- data/lib/base/asynchronous_service_gateway.rb +529 -0
- data/lib/base/backup.rb +206 -0
- data/lib/base/barrier.rb +54 -0
- data/lib/base/base.rb +159 -0
- data/lib/base/base_async_gateway.rb +164 -0
- data/lib/base/base_job.rb +5 -0
- data/lib/base/catalog_manager_base.rb +67 -0
- data/lib/base/catalog_manager_v1.rb +225 -0
- data/lib/base/catalog_manager_v2.rb +291 -0
- data/lib/base/cloud_controller_services.rb +75 -0
- data/lib/base/datamapper_l.rb +148 -0
- data/lib/base/gateway.rb +167 -0
- data/lib/base/gateway_service_catalog.rb +68 -0
- data/lib/base/http_handler.rb +101 -0
- data/lib/base/job/async_job.rb +71 -0
- data/lib/base/job/config.rb +27 -0
- data/lib/base/job/lock.rb +153 -0
- data/lib/base/job/package.rb +112 -0
- data/lib/base/job/serialization.rb +365 -0
- data/lib/base/job/snapshot.rb +354 -0
- data/lib/base/node.rb +471 -0
- data/lib/base/node_bin.rb +154 -0
- data/lib/base/plan.rb +63 -0
- data/lib/base/provisioner.rb +1120 -0
- data/lib/base/provisioner_v1.rb +125 -0
- data/lib/base/provisioner_v2.rb +193 -0
- data/lib/base/service.rb +93 -0
- data/lib/base/service_advertiser.rb +184 -0
- data/lib/base/service_error.rb +122 -0
- data/lib/base/service_message.rb +94 -0
- data/lib/base/service_plan_change_set.rb +11 -0
- data/lib/base/simple_aop.rb +63 -0
- data/lib/base/snapshot_v2/snapshot.rb +227 -0
- data/lib/base/snapshot_v2/snapshot_client.rb +158 -0
- data/lib/base/snapshot_v2/snapshot_job.rb +95 -0
- data/lib/base/utils.rb +63 -0
- data/lib/base/version.rb +7 -0
- data/lib/base/warden/instance_utils.rb +161 -0
- data/lib/base/warden/node_utils.rb +205 -0
- data/lib/base/warden/service.rb +426 -0
- data/lib/base/worker_bin.rb +76 -0
- data/lib/vcap_services_base.rb +16 -0
- metadata +364 -0
@@ -0,0 +1,75 @@
|
|
1
|
+
require 'base/service'
|
2
|
+
require 'base/plan'
|
3
|
+
|
4
|
+
module VCAP::Services
|
5
|
+
class CloudControllerServices
|
6
|
+
def initialize(http_client, headers, logger)
|
7
|
+
@http_client = http_client
|
8
|
+
@headers = headers
|
9
|
+
@logger = logger
|
10
|
+
end
|
11
|
+
|
12
|
+
attr_reader :logger
|
13
|
+
|
14
|
+
def load_registered_services(service_list_uri)
|
15
|
+
logger.debug("Getting services listing from cloud_controller")
|
16
|
+
registered_services = []
|
17
|
+
|
18
|
+
self.each(service_list_uri, "Registered Offerings") do |s|
|
19
|
+
entity = s["entity"]
|
20
|
+
plans = []
|
21
|
+
|
22
|
+
logger.debug("Getting service plans for: #{entity["label"]}/#{entity["provider"]}")
|
23
|
+
self.each(entity.fetch("service_plans_url"), "Service Plans") do |p|
|
24
|
+
plan_entity = p.fetch('entity')
|
25
|
+
plan_metadata = p.fetch('metadata')
|
26
|
+
plans << Plan.new(
|
27
|
+
:unique_id => plan_entity.fetch("unique_id"),
|
28
|
+
:guid => plan_metadata.fetch("guid"),
|
29
|
+
:name => plan_entity.fetch("name"),
|
30
|
+
:description => plan_entity.fetch("description"),
|
31
|
+
:free => plan_entity.fetch("free"),
|
32
|
+
)
|
33
|
+
end
|
34
|
+
|
35
|
+
registered_services << Service.new(
|
36
|
+
'guid' => s["metadata"]["guid"],
|
37
|
+
'label' => entity["label"],
|
38
|
+
'unique_id' => entity["unique_id"],
|
39
|
+
'description' => entity["description"],
|
40
|
+
'provider' => entity["provider"],
|
41
|
+
'version' => entity['version'],
|
42
|
+
'url' => entity["url"],
|
43
|
+
'info_url' => entity["info_url"],
|
44
|
+
'extra' => entity['extra'],
|
45
|
+
'plans' => plans,
|
46
|
+
'bindable' => entity['bindable']
|
47
|
+
)
|
48
|
+
end
|
49
|
+
|
50
|
+
registered_services
|
51
|
+
end
|
52
|
+
|
53
|
+
def each(seed_url, description, &block)
|
54
|
+
url = seed_url
|
55
|
+
logger.info("Fetching #{description} from: #{seed_url}")
|
56
|
+
|
57
|
+
while !url.nil? do
|
58
|
+
logger.debug("#{self.class.name}: Fetching #{description} from: #{url}")
|
59
|
+
@http_client.call(:uri => url,
|
60
|
+
:method => "get",
|
61
|
+
:head => @headers,
|
62
|
+
:need_raise => true) do |http|
|
63
|
+
result = nil
|
64
|
+
if (200..299) === http.response_header.status
|
65
|
+
result = JSON.parse(http.response)
|
66
|
+
else
|
67
|
+
raise "CCNG Catalog Manager: - Multiple page fetch via: #{url} failed: (#{http.response_header.status}) - #{http.response}"
|
68
|
+
end
|
69
|
+
result.fetch("resources").each { |r| block.yield r }
|
70
|
+
url = result["next_url"]
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
@@ -0,0 +1,148 @@
|
|
1
|
+
require "fileutils"
|
2
|
+
require "monitor"
|
3
|
+
require "data_mapper"
|
4
|
+
|
5
|
+
# Export Monitor's count
|
6
|
+
class Monitor
|
7
|
+
def count
|
8
|
+
@mon_count
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
module DataMapper
|
13
|
+
|
14
|
+
class GlobalMutex
|
15
|
+
def initialize(lockfile)
|
16
|
+
@lockfile = lockfile
|
17
|
+
@monitor = Monitor.new
|
18
|
+
end
|
19
|
+
|
20
|
+
def synchronize
|
21
|
+
@monitor.synchronize do
|
22
|
+
File.open(@lockfile, 'r') do |file|
|
23
|
+
# Only Lock/Unlock on first entrance of synchronize to avoid
|
24
|
+
# deadlock on flock
|
25
|
+
file.flock(File::LOCK_EX) if @monitor.count == 1
|
26
|
+
begin
|
27
|
+
yield
|
28
|
+
ensure
|
29
|
+
file.flock(File::LOCK_UN) if @monitor.count == 1
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
class << self
|
37
|
+
attr_reader :lock
|
38
|
+
|
39
|
+
# extend DataMapper.setup parameters for a new :lock_file options
|
40
|
+
# new setup can be called as following:
|
41
|
+
# DataMapper.setup(<name>, <String>, :lock_file => file)
|
42
|
+
# DataMapper.setup(<name>, <Addressable::URI>, :lock_file => file)
|
43
|
+
# DataMapper.setup(<name>, <other_connection_options, :lock_file => file>)
|
44
|
+
alias original_setup setup
|
45
|
+
def setup(*args)
|
46
|
+
unless @lock
|
47
|
+
lock_file = args[1][:lock_file] if args.size == 2 && args[1].kind_of?(Hash)
|
48
|
+
lock_file = args[2][:lock_file] if args.size == 3
|
49
|
+
lock_file ||= '/var/vcap/sys/run/LOCK'
|
50
|
+
initialize_lock_file(lock_file)
|
51
|
+
end
|
52
|
+
original_setup(*(args[0..1]))
|
53
|
+
end
|
54
|
+
|
55
|
+
def initialize_lock_file(lock_file)
|
56
|
+
FileUtils.mkdir_p(File.dirname(lock_file))
|
57
|
+
File.open(lock_file, 'w') do |file|
|
58
|
+
file.truncate(0)
|
59
|
+
end
|
60
|
+
@lock = GlobalMutex.new(lock_file)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
# The following code will overwrite DataMapper's functions, and replace
|
65
|
+
# them with a synchronized version of the same function.
|
66
|
+
module Resource
|
67
|
+
alias original_save save
|
68
|
+
alias original_destroy destroy
|
69
|
+
|
70
|
+
def save
|
71
|
+
DataMapper.lock.synchronize do
|
72
|
+
original_save
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
def destroy
|
77
|
+
DataMapper.lock.synchronize do
|
78
|
+
original_destroy
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
module Model
|
84
|
+
alias original_get get
|
85
|
+
alias original_all all
|
86
|
+
|
87
|
+
def get(*args)
|
88
|
+
DataMapper.lock.synchronize do
|
89
|
+
original_get(*args)
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
def all(*args)
|
94
|
+
DataMapper.lock.synchronize do
|
95
|
+
original_all(*args)
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
class Collection
|
101
|
+
alias original_each each
|
102
|
+
alias original_at []
|
103
|
+
alias original_get get
|
104
|
+
alias original_empty? empty?
|
105
|
+
|
106
|
+
def each(&block)
|
107
|
+
instances = []
|
108
|
+
DataMapper.lock.synchronize do
|
109
|
+
original_each do |instance|
|
110
|
+
instances << instance
|
111
|
+
end
|
112
|
+
end
|
113
|
+
instances.each(&block)
|
114
|
+
end
|
115
|
+
|
116
|
+
def [](*args)
|
117
|
+
DataMapper.lock.synchronize do
|
118
|
+
original_at(*args)
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
def get(*args)
|
123
|
+
DataMapper.lock.synchronize do
|
124
|
+
original_get(*args)
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
def empty?()
|
129
|
+
DataMapper.lock.synchronize do
|
130
|
+
original_empty?()
|
131
|
+
end
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
# For auto_upgrade!
|
136
|
+
module Migrations
|
137
|
+
module SingletonMethods
|
138
|
+
alias original_repository_execute repository_execute
|
139
|
+
|
140
|
+
def repository_execute(*args)
|
141
|
+
DataMapper.lock.synchronize do
|
142
|
+
original_repository_execute(*args)
|
143
|
+
end
|
144
|
+
end
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
end
|
data/lib/base/gateway.rb
ADDED
@@ -0,0 +1,167 @@
|
|
1
|
+
# Copyright (c) 2009-2011 VMware, Inc.
|
2
|
+
require 'rubygems'
|
3
|
+
require 'bundler/setup'
|
4
|
+
|
5
|
+
require 'optparse'
|
6
|
+
require 'net/http'
|
7
|
+
require 'thin'
|
8
|
+
require 'yaml'
|
9
|
+
require 'steno'
|
10
|
+
|
11
|
+
$LOAD_PATH.unshift File.join(File.dirname(__FILE__), '..', '..', '..')
|
12
|
+
require 'vcap/common'
|
13
|
+
|
14
|
+
$LOAD_PATH.unshift File.dirname(__FILE__)
|
15
|
+
require 'asynchronous_service_gateway'
|
16
|
+
require 'job/config'
|
17
|
+
require 'abstract'
|
18
|
+
|
19
|
+
module VCAP
|
20
|
+
module Services
|
21
|
+
module Base
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
|
27
|
+
class VCAP::Services::Base::Gateway
|
28
|
+
|
29
|
+
abstract :default_config_file
|
30
|
+
abstract :provisioner_class
|
31
|
+
|
32
|
+
def parse_config
|
33
|
+
config_file = default_config_file
|
34
|
+
|
35
|
+
OptionParser.new do |opts|
|
36
|
+
opts.banner = "Usage: $0 [options]"
|
37
|
+
opts.on("-c", "--config [ARG]", "Configuration File") do |opt|
|
38
|
+
config_file = opt
|
39
|
+
end
|
40
|
+
opts.on("-h", "--help", "Help") do
|
41
|
+
puts opts
|
42
|
+
exit
|
43
|
+
end
|
44
|
+
end.parse!
|
45
|
+
|
46
|
+
begin
|
47
|
+
@config = parse_gateway_config(config_file)
|
48
|
+
rescue => e
|
49
|
+
puts "Couldn't read config file: #{e}"
|
50
|
+
exit
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def setup_vcap_logging
|
55
|
+
steno_config = Steno::Config.to_config_hash(@config[:logging])
|
56
|
+
steno_config[:context] = Steno::Context::FiberLocal.new
|
57
|
+
Steno.init(Steno::Config.new(steno_config))
|
58
|
+
# Use the current running binary name for logger identity name, since service gateway only has one instance now.
|
59
|
+
logger = Steno.logger(File.basename($0))
|
60
|
+
@config[:logger] = logger
|
61
|
+
end
|
62
|
+
|
63
|
+
def setup_async_job_config
|
64
|
+
resque = @config[:resque]
|
65
|
+
if resque
|
66
|
+
resque = VCAP.symbolize_keys(resque)
|
67
|
+
VCAP::Services::Base::AsyncJob::Config.redis_config = resque
|
68
|
+
VCAP::Services::Base::AsyncJob::Config.logger = @config[:logger]
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
def setup_pid
|
73
|
+
if @config[:pid]
|
74
|
+
pf = VCAP::PidFile.new(@config[:pid])
|
75
|
+
pf.unlink_at_exit
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
def start
|
80
|
+
parse_config
|
81
|
+
|
82
|
+
setup_vcap_logging
|
83
|
+
|
84
|
+
setup_pid
|
85
|
+
|
86
|
+
setup_async_job_config
|
87
|
+
|
88
|
+
@config[:host] = VCAP.local_ip(@config[:ip_route])
|
89
|
+
@config[:port] ||= VCAP.grab_ephemeral_port
|
90
|
+
@config[:service][:label] = "#{@config[:service][:name]}-#{@config[:service][:version]}"
|
91
|
+
@config[:service][:url] = "http://#{@config[:host]}:#{@config[:port]}"
|
92
|
+
node_timeout = @config[:node_timeout] || 5
|
93
|
+
|
94
|
+
EM.error_handler do |ex|
|
95
|
+
@config[:logger].fatal("#{ex} #{ex.backtrace.join("|")}")
|
96
|
+
exit
|
97
|
+
end
|
98
|
+
|
99
|
+
# Go!
|
100
|
+
EM.run do
|
101
|
+
sp = provisioner_class.new(
|
102
|
+
:logger => @config[:logger],
|
103
|
+
:index => @config[:index],
|
104
|
+
:ip_route => @config[:ip_route],
|
105
|
+
:mbus => @config[:mbus],
|
106
|
+
:node_timeout => node_timeout,
|
107
|
+
:z_interval => @config[:z_interval],
|
108
|
+
:max_nats_payload => @config[:max_nats_payload],
|
109
|
+
:additional_options => additional_options,
|
110
|
+
:status => @config[:status],
|
111
|
+
:plan_management => @config[:plan_management],
|
112
|
+
:service => @config[:service],
|
113
|
+
:download_url_template => @config[:download_url_template],
|
114
|
+
:cc_api_version => @config[:cc_api_version] || "v1" ,
|
115
|
+
:snapshot_db => @config[:resque],
|
116
|
+
)
|
117
|
+
|
118
|
+
opts = @config.dup
|
119
|
+
opts[:provisioner] = sp
|
120
|
+
opts[:node_timeout] = node_timeout
|
121
|
+
opts[:cloud_controller_uri] = @config[:cloud_controller_uri] || "api.vcap.me"
|
122
|
+
|
123
|
+
sg = async_gateway_class.new(opts)
|
124
|
+
|
125
|
+
server = Thin::Server.new(@config[:host], @config[:port], sg)
|
126
|
+
if @config[:service][:timeout]
|
127
|
+
server.timeout = [@config[:service][:timeout] + 1, Thin::Server::DEFAULT_TIMEOUT].max
|
128
|
+
end
|
129
|
+
server.start!
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
def async_gateway_class
|
134
|
+
VCAP::Services::AsynchronousServiceGateway
|
135
|
+
end
|
136
|
+
|
137
|
+
def parse_gateway_config(config_file)
|
138
|
+
config = YAML.load_file(config_file)
|
139
|
+
config = VCAP.symbolize_keys(config)
|
140
|
+
|
141
|
+
|
142
|
+
cc_api_version = config[:cc_api_version] || "v1"
|
143
|
+
|
144
|
+
if cc_api_version == "v1"
|
145
|
+
token = config[:token]
|
146
|
+
raise "Token missing" unless token
|
147
|
+
raise "Token must be a String or Int, #{token.class} given" unless (token.is_a?(Integer) || token.is_a?(String))
|
148
|
+
config[:token] = token.to_s
|
149
|
+
else
|
150
|
+
service_auth_tokens = config[:service_auth_tokens]
|
151
|
+
raise "Service auth token missing" unless service_auth_tokens
|
152
|
+
raise "Token must be hash of the form: label_provider => token" unless service_auth_tokens.is_a?(Hash)
|
153
|
+
|
154
|
+
# Each gateway only handles one service, so service_auth_tokens is expected to have just 1 entry
|
155
|
+
raise "Unable to manage multiple services" unless service_auth_tokens.size == 1
|
156
|
+
|
157
|
+
# Used by legacy services for validating incoming request (and temporarily for handle fetch/update v1 api)
|
158
|
+
config[:token] = service_auth_tokens.values[0].to_s # For legacy services
|
159
|
+
end
|
160
|
+
|
161
|
+
config
|
162
|
+
end
|
163
|
+
|
164
|
+
def additional_options
|
165
|
+
{}
|
166
|
+
end
|
167
|
+
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
module VCAP::Services
|
2
|
+
class GatewayServiceCatalog
|
3
|
+
attr_reader :service
|
4
|
+
|
5
|
+
def initialize(services)
|
6
|
+
raise ArgumentError.new('a service list is required') unless services and services.is_a?(Array)
|
7
|
+
@service = services.fetch(0)
|
8
|
+
end
|
9
|
+
|
10
|
+
def to_hash
|
11
|
+
id, version = VCAP::Services::Api::Util.parse_label(service[:label])
|
12
|
+
version = service[:version_aliases][:current] if service[:version_aliases][:current]
|
13
|
+
provider = service[:provider] || 'core'
|
14
|
+
|
15
|
+
catalog_key = "#{id}_#{provider}"
|
16
|
+
|
17
|
+
unique_id = service[:unique_id] ? {"unique_id" => service[:unique_id]} : {}
|
18
|
+
catalog = {}
|
19
|
+
|
20
|
+
plans = service.fetch(:plans)
|
21
|
+
unless plans.is_a?(Array)
|
22
|
+
plans.each do |name, plan|
|
23
|
+
plan[:name] = name
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
catalog[catalog_key] = {
|
28
|
+
"id" => id,
|
29
|
+
"version" => version,
|
30
|
+
"label" => service[:label],
|
31
|
+
"url" => service[:url],
|
32
|
+
"plans" => plans,
|
33
|
+
"cf_plan_id" => service[:cf_plan_id],
|
34
|
+
"tags" => service[:tags],
|
35
|
+
"active" => true,
|
36
|
+
"description" => service[:description],
|
37
|
+
"plan_options" => service[:plan_options],
|
38
|
+
"acls" => service[:acls],
|
39
|
+
"timeout" => service[:timeout],
|
40
|
+
"provider" => provider,
|
41
|
+
"default_plan" => service[:default_plan],
|
42
|
+
"supported_versions" => service[:supported_versions],
|
43
|
+
"version_aliases" => service[:version_aliases],
|
44
|
+
}.merge(extra).merge(unique_id)
|
45
|
+
|
46
|
+
return catalog
|
47
|
+
end
|
48
|
+
|
49
|
+
private
|
50
|
+
|
51
|
+
def extra
|
52
|
+
if (service.keys & [:logo_url, :blurb, :provider_name]).empty?
|
53
|
+
{}
|
54
|
+
else
|
55
|
+
{"extra" => Yajl::Encoder.encode(
|
56
|
+
"listing" => {
|
57
|
+
"imageUrl" => service[:logo_url],
|
58
|
+
"blurb" => service[:blurb]
|
59
|
+
},
|
60
|
+
"provider" => {
|
61
|
+
"name" => service[:provider_name]
|
62
|
+
}
|
63
|
+
)
|
64
|
+
}
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|