vcap_services_base 0.2.10

Sign up to get free protection for your applications and to get access to all the features.
Files changed (46) hide show
  1. checksums.yaml +7 -0
  2. data/lib/base/abstract.rb +11 -0
  3. data/lib/base/api/message.rb +31 -0
  4. data/lib/base/asynchronous_service_gateway.rb +529 -0
  5. data/lib/base/backup.rb +206 -0
  6. data/lib/base/barrier.rb +54 -0
  7. data/lib/base/base.rb +159 -0
  8. data/lib/base/base_async_gateway.rb +164 -0
  9. data/lib/base/base_job.rb +5 -0
  10. data/lib/base/catalog_manager_base.rb +67 -0
  11. data/lib/base/catalog_manager_v1.rb +225 -0
  12. data/lib/base/catalog_manager_v2.rb +291 -0
  13. data/lib/base/cloud_controller_services.rb +75 -0
  14. data/lib/base/datamapper_l.rb +148 -0
  15. data/lib/base/gateway.rb +167 -0
  16. data/lib/base/gateway_service_catalog.rb +68 -0
  17. data/lib/base/http_handler.rb +101 -0
  18. data/lib/base/job/async_job.rb +71 -0
  19. data/lib/base/job/config.rb +27 -0
  20. data/lib/base/job/lock.rb +153 -0
  21. data/lib/base/job/package.rb +112 -0
  22. data/lib/base/job/serialization.rb +365 -0
  23. data/lib/base/job/snapshot.rb +354 -0
  24. data/lib/base/node.rb +471 -0
  25. data/lib/base/node_bin.rb +154 -0
  26. data/lib/base/plan.rb +63 -0
  27. data/lib/base/provisioner.rb +1120 -0
  28. data/lib/base/provisioner_v1.rb +125 -0
  29. data/lib/base/provisioner_v2.rb +193 -0
  30. data/lib/base/service.rb +93 -0
  31. data/lib/base/service_advertiser.rb +184 -0
  32. data/lib/base/service_error.rb +122 -0
  33. data/lib/base/service_message.rb +94 -0
  34. data/lib/base/service_plan_change_set.rb +11 -0
  35. data/lib/base/simple_aop.rb +63 -0
  36. data/lib/base/snapshot_v2/snapshot.rb +227 -0
  37. data/lib/base/snapshot_v2/snapshot_client.rb +158 -0
  38. data/lib/base/snapshot_v2/snapshot_job.rb +95 -0
  39. data/lib/base/utils.rb +63 -0
  40. data/lib/base/version.rb +7 -0
  41. data/lib/base/warden/instance_utils.rb +161 -0
  42. data/lib/base/warden/node_utils.rb +205 -0
  43. data/lib/base/warden/service.rb +426 -0
  44. data/lib/base/worker_bin.rb +76 -0
  45. data/lib/vcap_services_base.rb +16 -0
  46. 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
@@ -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