vcap_services_base 0.2.10
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.
- 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
|