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,205 @@
1
+ module VCAP
2
+ module Services
3
+ module Base
4
+ module Warden
5
+ end
6
+ end
7
+ end
8
+ end
9
+
10
+ module VCAP::Services::Base::Warden::NodeUtils
11
+
12
+ attr_accessor :m_interval, :m_actions
13
+
14
+ def warden_node_init(options={})
15
+ @m_interval = options[:m_interval] || 10
16
+ @m_actions = options[:m_actions] || []
17
+ setup_monitor_timer
18
+ end
19
+
20
+ def setup_monitor_timer
21
+ EM.add_timer(m_interval){ EM.defer{ monitor_all_instances } } if m_interval > 0
22
+ end
23
+
24
+ def service_instances
25
+ []
26
+ end
27
+
28
+ def init_ports(port_range)
29
+ @free_ports = Set.new
30
+ port_range.each {|port| @free_ports << port}
31
+ @free_ports_lock = Mutex.new
32
+ end
33
+
34
+ def new_port(port=nil)
35
+ @free_ports_lock.synchronize do
36
+ raise "No ports available." if @free_ports.empty?
37
+ if port.nil? || !@free_ports.include?(port)
38
+ port = @free_ports.first
39
+ @free_ports.delete(port)
40
+ else
41
+ @free_ports.delete(port)
42
+ end
43
+ end
44
+ port
45
+ end
46
+
47
+ def free_port(port)
48
+ @free_ports_lock.synchronize do
49
+ raise "port #{port} already freed!" if @free_ports.include?(port)
50
+ @free_ports.add(port)
51
+ end
52
+ end
53
+
54
+ def del_port(port)
55
+ @free_ports_lock.synchronize do
56
+ @free_ports.delete(port)
57
+ end
58
+ end
59
+
60
+ def port_occupied?(port)
61
+ Timeout::timeout(1) do
62
+ begin
63
+ TCPSocket.open('localhost', port).close
64
+ return true
65
+ rescue => e
66
+ return false
67
+ end
68
+ end
69
+ end
70
+
71
+ def pool_run(params, worker_count=10)
72
+ lock = Mutex.new
73
+ ths = []
74
+ ind = 0
75
+ worker_count.times do |i|
76
+ ths << Thread.new(i) do |tid|
77
+ loop do
78
+ param = nil
79
+ lock.synchronize do
80
+ Thread.exit if ind >= params.size
81
+ param = params[ind]
82
+ ind += 1
83
+ end
84
+ begin
85
+ yield(param, tid)
86
+ rescue => e
87
+ @logger.warn("pool_run error: #{e} from #{caller(1).first(3).join(";")}")
88
+ end
89
+ end
90
+ end
91
+ end
92
+ ths.each(&:join)
93
+ end
94
+
95
+ def monitor_all_instances
96
+ params = service_instances.map {|instance| instance }
97
+ lock = Mutex.new
98
+ failed_instances = []
99
+ pool_run(params) do |ins, _|
100
+ if !closing && ins.in_monitored?
101
+ if ins.running?
102
+ ins.failed_times = 0
103
+ else
104
+ ins.failed_times ||= 0
105
+ ins.failed_times += 1
106
+ if ins.in_monitored?
107
+ lock.synchronize { failed_instances << ins }
108
+ else
109
+ @logger.error("Instance #{ins.name} is failed too many times. Unmonitored.")
110
+ ins.stop
111
+ end
112
+ end
113
+ end
114
+ end
115
+ @logger.debug("Found failed_instances: #{failed_instances.map{|i| i.name}}") if failed_instances.size > 0
116
+ m_actions.each do |act|
117
+ unless closing
118
+ method = "#{act}_failed_instances"
119
+ if respond_to?(method.to_sym)
120
+ begin
121
+ send(method.to_sym, failed_instances)
122
+ rescue => e
123
+ @logger.warn("#{method}: #{e}")
124
+ end
125
+ else
126
+ @logger.warn("Failover action #{act} is not defined")
127
+ end
128
+ end
129
+ end
130
+ rescue => e
131
+ @logger.warn("monitor_all_instances: #{e}")
132
+ ensure
133
+ setup_monitor_timer unless closing
134
+ end
135
+
136
+ def restart_failed_instances(failed_instances)
137
+ stop_instances(failed_instances)
138
+ start_instances(failed_instances)
139
+ end
140
+
141
+ def start_all_instances
142
+ start_instances(service_instances)
143
+ end
144
+
145
+ def start_instances(all_instances)
146
+ pool_run(all_instances, @instance_parallel_start_count || 10) do |instance|
147
+ next if closing
148
+ del_port(instance.port)
149
+
150
+ if instance.running?
151
+ @logger.warn("Service #{instance.name} already listening on port #{instance.port}")
152
+ next
153
+ end
154
+
155
+ unless instance.base_dir?
156
+ @logger.warn("Service #{instance.name} in local DB, but not in file system")
157
+ next
158
+ end
159
+
160
+ begin
161
+ instance.migration_check()
162
+ rescue => e
163
+ @logger.error("Error on migration_check: #{e}")
164
+ next
165
+ end
166
+
167
+ begin
168
+ instance.run(instance.start_options)
169
+ @logger.info("Successfully start provisioned instance #{instance.name}")
170
+ rescue => e
171
+ @logger.error("Error starting instance #{instance.name}: #{e}")
172
+ # Try to stop the instance since the container could be created
173
+ begin
174
+ instance.stop
175
+ rescue => e
176
+ # Ignore the rollback error and just record a warning log
177
+ @logger.warn("Error stopping instance #{instance.name} when rollback from a starting failure")
178
+ end
179
+ end
180
+ end
181
+ end
182
+
183
+ def stop_all_instances
184
+ stop_instances(service_instances)
185
+ end
186
+
187
+ def stop_instances(all_instances)
188
+ pool_run(all_instances, @instance_parallel_stop_count || 10) do |instance|
189
+ begin
190
+ instance.stop
191
+ @logger.info("Successfully stop instance #{instance.name}")
192
+ rescue => e
193
+ @logger.error("Error stopping instance #{instance.name}: #{e}")
194
+ end
195
+ end
196
+ end
197
+
198
+ def varz_details
199
+ varz = super
200
+ unmonitored = []
201
+ service_instances.each{|ins| unmonitored << ins.name unless ins.in_monitored? }
202
+ varz[:unmonitored] = unmonitored
203
+ varz
204
+ end
205
+ end
@@ -0,0 +1,426 @@
1
+ $LOAD_PATH.unshift File.dirname(__FILE__)
2
+ require 'instance_utils'
3
+ require 'timeout'
4
+
5
+ class VCAP::Services::Base::Warden::Service
6
+
7
+ include VCAP::Services::Base::Utils
8
+ include VCAP::Services::Base::Warden::InstanceUtils
9
+
10
+ class << self
11
+
12
+ def init(options)
13
+ @@options = options
14
+ @base_dir = options[:base_dir]
15
+ @log_dir = options[:service_log_dir]
16
+ @common_dir = options[:service_common_dir]
17
+ @bin_dir = options[:service_bin_dir]
18
+ @image_dir = options[:image_dir]
19
+ @logger = options[:logger]
20
+ @max_disk = options[:max_disk]
21
+ @max_memory = options[:max_memory]
22
+ @memory_overhead = options[:memory_overhead]
23
+ @quota = options[:filesystem_quota] || false
24
+ @service_start_timeout = options[:service_start_timeout] || 3
25
+ @service_status_timeout = options[:service_status_timeout] || 3
26
+ @bandwidth_per_second = options[:bandwidth_per_second]
27
+ @service_port = options[:service_port]
28
+ @rm_instance_dir_timeout = options[:rm_instance_dir_timeout] || 10
29
+ @m_failed_times = options[:m_failed_times] || 3
30
+ @warden_socket_path = options[:warden_socket_path] || "/tmp/warden.sock"
31
+ FileUtils.mkdir_p(File.dirname(options[:local_db].split(':')[1]))
32
+ DataMapper.setup(:default, options[:local_db])
33
+ DataMapper::auto_upgrade!
34
+ FileUtils.mkdir_p(base_dir)
35
+ FileUtils.mkdir_p(log_dir)
36
+ FileUtils.mkdir_p(image_dir) if @image_dir
37
+ @in_memory_status = {}
38
+ end
39
+
40
+ def define_im_properties(*args)
41
+ args.each do |prop|
42
+ define_method("#{prop}=".to_sym) do |value|
43
+ self.class.in_memory_status[self[:name]] ||= {}
44
+ self.class.in_memory_status[self[:name]][prop] = value
45
+ end
46
+
47
+ define_method(prop) do
48
+ self.class.in_memory_status[self[:name]] && self.class.in_memory_status[self[:name]][prop]
49
+ end
50
+ end
51
+ end
52
+
53
+ attr_reader :base_dir, :log_dir, :bin_dir, :common_dir, :image_dir, :max_disk, :logger, :quota,
54
+ :max_memory, :memory_overhead, :service_start_timeout, :service_status_timeout,
55
+ :bandwidth_per_second, :service_port, :rm_instance_dir_timeout, :m_failed_times,
56
+ :in_memory_status, :warden_socket_path
57
+
58
+ end
59
+
60
+ define_im_properties :failed_times
61
+
62
+ def in_monitored?
63
+ !failed_times || failed_times <= self.class.m_failed_times
64
+ end
65
+
66
+ def logger
67
+ self.class.logger
68
+ end
69
+
70
+ def prepare_filesystem(max_size, opts={})
71
+ if base_dir?
72
+ self.class.sh "umount #{base_dir}", :raise => false if self.class.quota
73
+ logger.warn("Service #{self[:name]} base_dir:#{base_dir} already exists, deleting it")
74
+ FileUtils.rm_rf(base_dir)
75
+ end
76
+ if log_dir?
77
+ logger.warn("Service #{self[:name]} log_dir:#{log_dir} already exists, deleting it")
78
+ FileUtils.rm_rf(log_dir)
79
+ end
80
+ if image_file?
81
+ logger.warn("Service #{self[:name]} image_file:#{image_file} already exists, deleting it")
82
+ FileUtils.rm_f(image_file)
83
+ end
84
+ FileUtils.mkdir_p(base_dir)
85
+ FileUtils.mkdir_p(data_dir)
86
+ FileUtils.mkdir_p(log_dir)
87
+
88
+ ext_opts = nil
89
+ ext_opts = "-E \"lazy_itable_init=1\"" if opts[:lazy_itable_init]
90
+ if self.class.quota
91
+ self.class.sh "dd if=/dev/null of=#{image_file} bs=1M seek=#{max_size.to_i}"
92
+ self.class.sh "mkfs.ext4 -q -F -O \"^has_journal,uninit_bg\" #{"#{ext_opts}" if ext_opts} #{image_file}"
93
+ loop_setup
94
+ end
95
+ end
96
+
97
+ def loop_setdown
98
+ self.class.sh "umount #{base_dir}"
99
+ end
100
+
101
+ def loop_setup
102
+ self.class.sh "mount -n -o loop #{image_file} #{base_dir}"
103
+ # Set the dir owner back to the process user
104
+ self.class.sh "chown -R #{Process.uid}:#{Process.gid} #{base_dir}"
105
+ end
106
+
107
+ def loop_setup?
108
+ mounted = false
109
+ File.open("/proc/mounts", mode="r") do |f|
110
+ f.each do |w|
111
+ if Regexp.new(base_dir) =~ w
112
+ mounted = true
113
+ break
114
+ end
115
+ end
116
+ end
117
+ mounted
118
+ end
119
+
120
+ def need_loop_resize?
121
+ image_file? && File.size(image_file) != self.class.max_disk * 1024 * 1024
122
+ end
123
+
124
+ def loop_resize
125
+ loop_up = loop_setup?
126
+ loop_setdown if loop_up
127
+ self.class.sh "cp #{image_file} #{image_file}.bak"
128
+ begin
129
+ old_size = File.size(image_file)
130
+ self.class.sh "resize2fs -f #{image_file} #{self.class.max_disk.to_i}M"
131
+ logger.info("Service #{self[:name]} change loop file size from #{old_size / 1024 / 1024}M to #{self.class.max_disk}M")
132
+ rescue => e
133
+ # Revert image file to the backup if resize raise error
134
+ self.class.sh "cp #{image_file}.bak #{image_file}"
135
+ logger.error("Service #{self[:name]} revert image file for error #{e}")
136
+ ensure
137
+ self.class.sh "rm -f #{image_file}.bak"
138
+ end
139
+ loop_setup if loop_up
140
+ end
141
+
142
+ def to_loopfile
143
+ self.class.sh "mv #{base_dir} #{base_dir+"_bak"}"
144
+ self.class.sh "mkdir -p #{base_dir}"
145
+ self.class.sh "A=`du -sm #{base_dir+"_bak"} | awk '{ print $1 }'`;A=$((A+32));if [ $A -lt #{self.class.max_disk.to_i} ]; then A=#{self.class.max_disk.to_i}; fi;dd if=/dev/null of=#{image_file} bs=1M seek=$A"
146
+ self.class.sh "mkfs.ext4 -q -F -O \"^has_journal,uninit_bg\" #{image_file}"
147
+ self.class.sh "mount -n -o loop #{image_file} #{base_dir}"
148
+ self.class.sh "cp -af #{base_dir+"_bak"}/* #{base_dir}", :timeout => 60.0
149
+ end
150
+
151
+ def migration_check
152
+ # incase for bosh --recreate, which will delete log dir
153
+ FileUtils.mkdir_p(base_dir) unless base_dir?
154
+ FileUtils.mkdir_p(log_dir) unless log_dir?
155
+
156
+ if image_file?
157
+ loop_resize if need_loop_resize?
158
+ unless loop_setup?
159
+ # for case where VM rebooted
160
+ logger.info("Service #{self[:name]} mounting data file")
161
+ loop_setup
162
+ end
163
+ else
164
+ if self.class.quota
165
+ logger.warn("Service #{self[:name]} need migration to quota")
166
+ to_loopfile
167
+ end
168
+ end
169
+ end
170
+
171
+ def task(desc)
172
+ begin
173
+ yield
174
+ rescue => e
175
+ logger.error("Fail to #{desc}. Error: #{e}")
176
+ end
177
+ end
178
+
179
+ # instance operation helper
180
+ def delete
181
+ container_name = self[:container]
182
+ name = self[:name]
183
+ task "destroy record in local db" do
184
+ destroy! if saved?
185
+ end
186
+
187
+ task "delete in-memory status" do
188
+ self.class.in_memory_status.delete(name)
189
+ end
190
+
191
+ task "stop container when deleting service #{name}" do
192
+ stop(container_name)
193
+ end
194
+
195
+ task "delete instance directories" do
196
+ if self.class.quota
197
+ self.class.sh("rm -f #{image_file}", {:block => false})
198
+ end
199
+ # delete service data directory could be slow, so increase the timeout
200
+ self.class.sh("rm -rf #{base_dir} #{log_dir} #{util_dirs.join(' ')}", {:block => false, :timeout => self.class.rm_instance_dir_timeout})
201
+ end
202
+ end
203
+
204
+ def run_command(handle, cmd_hash)
205
+ if cmd_hash[:use_spawn]
206
+ container_spawn_command(handle, cmd_hash[:script], cmd_hash[:use_root])
207
+ else
208
+ container_run_command(handle, cmd_hash[:script], cmd_hash[:use_root])
209
+ end
210
+ end
211
+
212
+ # The logic in instance run function is:
213
+ # 0. To avoid to create orphan, clean up container if handle exists
214
+ # 1. Generate bind mount request and create warden container with bind mount options
215
+ # 2. Limit memory and bandwidth of the container (optional)
216
+ # 3. Run pre service start script (optional)
217
+ # 4. Run service start script
218
+ # 5. Create iptables rules for service process (optional)
219
+ # 6. Get container IP address and wait for the service finishing starting
220
+ # 7. Run post service start script (optional)
221
+ # 8. Run post service start block (optional)
222
+ # 9. Save the instance info to local db
223
+ def run(options=nil, &post_start_block)
224
+ stop if self[:container] && self[:container].length > 0
225
+ # If no options specified, then check whether the instance is stored in local db
226
+ # to decide to use which start options
227
+ options = (new? ? first_start_options : start_options) unless options
228
+ loop_setup if self.class.quota && (not loop_setup?)
229
+ bind_mounts = []
230
+ bind_mounts = options[:bind_dirs].map { |bind_dir| bind_mount_request(bind_dir) }
231
+ handle = container_start(bind_mounts)
232
+ self[:container] = handle
233
+ rw_dirs = options[:bind_dirs].map { |bind_dir| bind_dir[:dst] || bind_dir[:src] unless bind_dir[:read_only] }.compact
234
+ run_command(handle, {:script => "chown -R vcap:vcap #{rw_dirs.join(' ')}", :use_root => true}) unless rw_dirs.empty?
235
+ limit_memory(handle, memory_limit) if memory_limit
236
+ limit_bandwidth(handle, bandwidth_limit) if bandwidth_limit
237
+ run_command(handle, options[:pre_start_script]) if options[:pre_start_script]
238
+ run_command(handle, options[:start_script]) if options[:start_script]
239
+ map_port(handle, self[:port], options[:service_port]) if options[:need_map_port]
240
+ rsp = container_info(handle)
241
+ self[:ip] = rsp.container_ip
242
+ # Check whether the service finish starting,
243
+ # the check method can be different depends on whether the service is first start
244
+ raise VCAP::Services::Base::Error::ServiceError::new(VCAP::Services::Base::Error::ServiceError::SERVICE_START_TIMEOUT) unless wait_service_start(options[:service_start_timeout], options[:is_first_start])
245
+ run_command(handle, options[:post_start_script]) if options[:post_start_script]
246
+ # The post start block is some work that need do after first start in provision,
247
+ # where restart this instance, there should be no such work
248
+ post_start_block.call(self) if post_start_block
249
+ save!
250
+ true
251
+ end
252
+
253
+ def running?
254
+ finish_start?
255
+ end
256
+
257
+ # Usually, stop can retrieve container_name from local_db
258
+ # An exception is unprovision, which destroys record in local_db first.
259
+ def stop(container_name=nil)
260
+ name = container_name || self[:container]
261
+ if container_running?(name)
262
+ begin
263
+ run_command(name, stop_options[:stop_script]) if stop_options[:stop_script]
264
+ rescue => e
265
+ logger.error("Failed to call instance stop script #{stop_options[:stop_script]} with error #{e}")
266
+ end
267
+ container_stop(name)
268
+ container_destroy(name)
269
+ unless container_name
270
+ self[:container] = ''
271
+ save
272
+ end
273
+ loop_setdown if self.class.quota
274
+ end
275
+ end
276
+
277
+ # directory helper
278
+ def image_file
279
+ return File.join(self.class.image_dir, "#{self[:name]}.img") if self.class.image_dir
280
+ ''
281
+ end
282
+
283
+ def base_dir
284
+ return File.join(self.class.base_dir, self[:name]) if self.class.base_dir
285
+ ''
286
+ end
287
+
288
+ def log_dir
289
+ return File.join(self.class.log_dir, self[:name]) if self.class.log_dir
290
+ ''
291
+ end
292
+
293
+ def image_file?
294
+ File.exists?(image_file)
295
+ end
296
+
297
+ def base_dir?
298
+ Dir.exists?(base_dir)
299
+ end
300
+
301
+ def log_dir?
302
+ Dir.exists?(log_dir)
303
+ end
304
+
305
+ def util_dirs
306
+ []
307
+ end
308
+
309
+ def data_dir
310
+ File.join(base_dir, "data")
311
+ end
312
+
313
+ def script_dir
314
+ File.join(self.class.common_dir, "bin")
315
+ end
316
+
317
+ def bin_dir
318
+ self.class.bin_dir[version]
319
+ end
320
+
321
+ def common_dir
322
+ self.class.common_dir
323
+ end
324
+
325
+ def update_bind_dirs(bind_dirs, old_bind, new_bind)
326
+ find_bind = bind_dirs.select { |bind| bind[:src] == old_bind[:src] && bind[:dst] == old_bind[:dst] && bind[:read_only] == old_bind[:read_only] }
327
+ unless find_bind.empty?
328
+ find_bind[0][:src] = new_bind[:src]
329
+ find_bind[0][:dst] = new_bind[:dst]
330
+ find_bind[0][:read_only] = new_bind[:read_only]
331
+ end
332
+ end
333
+
334
+ # service start/stop helper
335
+ def wait_service_start(service_start_timeout, is_first_start=false)
336
+ (service_start_timeout * 10).times do
337
+ sleep 0.1
338
+ if is_first_start
339
+ return true if finish_first_start?
340
+ else
341
+ return true if finish_start?
342
+ end
343
+ end
344
+ false
345
+ end
346
+
347
+ ### Service Node subclasses can override these following method ###
348
+
349
+ # Instance start options, basically the node need define ":start_script",
350
+ # and use other default options.
351
+ def start_options
352
+ bind_dirs = []
353
+ bind_dirs << {:src => bin_dir, :read_only => true}
354
+ bind_dirs << {:src => common_dir, :read_only => true}
355
+ # Since the script "warden_service_ctl" writes log in a hard-code directory "/var/vcap/sys/log/monit,
356
+ # then we need has this directory with write permission in warden container,
357
+ # now the work around is bind-mount instance log dir to "/var/vcap/sys/log/monit"
358
+ bind_dirs << {:src => log_dir, :dst => "/var/vcap/sys/log/monit"}
359
+ bind_dirs << {:src => base_dir}
360
+ bind_dirs << {:src => log_dir}
361
+ bind_dirs.concat util_dirs.map { |dir| {:src => dir} }
362
+ {
363
+ :service_port => self.class.service_port,
364
+ :need_map_port => true,
365
+ :is_first_start => false,
366
+ :bind_dirs => bind_dirs,
367
+ :service_start_timeout => self.class.service_start_timeout,
368
+ }
369
+ end
370
+
371
+ # It's the same with start_options except "is_first_start" key by default,
372
+ # but can be different for some services between instance provision (first start)
373
+ # and restart (normal start), then the node subclass need override this function
374
+ def first_start_options
375
+ options = start_options
376
+ options[:is_first_start] = true
377
+ options
378
+ end
379
+
380
+ # Check where the service process finish starting,
381
+ # the node subclass should override this function.
382
+ def finish_start?
383
+ true
384
+ end
385
+
386
+ # For some services the check methods in instance provision and restart are different,
387
+ # then they should override this method, otherwise the behavior is the same with first_start
388
+ def finish_first_start?
389
+ finish_start?
390
+ end
391
+
392
+ # Base user should provide a script to stop instance.
393
+ # if stop_options is empty, the process will get a SIGTERM first then SIGKILL later.
394
+ def stop_options
395
+ {
396
+ :stop_script => {:script => "#{service_script} stop #{base_dir} #{log_dir} #{common_dir}"},
397
+ }
398
+ end
399
+
400
+ # Provide a command to monitor the health of instance.
401
+ # if status_options is empty, running? method will only show the health of container
402
+ def status_options
403
+ {
404
+ :status_script => {:script => "#{service_script} status #{base_dir} #{log_dir} #{common_dir}"}
405
+ }
406
+ end
407
+
408
+ # Generally the node can use this default service script path
409
+ def service_script
410
+ File.join(script_dir, "warden_service_ctl")
411
+ end
412
+
413
+ # Generally the node can use this default calculation method for memory limitation
414
+ def memory_limit
415
+ if self.class.max_memory
416
+ (self.class.max_memory + (self.class.memory_overhead || 0)).to_i
417
+ else
418
+ nil
419
+ end
420
+ end
421
+
422
+ # Generally the node can use this default calculation method for bandwidth limitation
423
+ def bandwidth_limit
424
+ self.class.bandwidth_per_second
425
+ end
426
+ end