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,122 @@
|
|
1
|
+
# Copyright (c) 2009-2011 VMware, Inc.
|
2
|
+
require "rubygems"
|
3
|
+
require "yajl"
|
4
|
+
|
5
|
+
module VCAP
|
6
|
+
module Services
|
7
|
+
module Base
|
8
|
+
module Error
|
9
|
+
class ServiceError < StandardError
|
10
|
+
attr_reader :http_status, :error_code, :error_msg
|
11
|
+
|
12
|
+
# HTTP status code
|
13
|
+
HTTP_BAD_REQUEST = 400
|
14
|
+
HTTP_NOT_AUTHORIZED = 401
|
15
|
+
HTTP_FORBIDDEN = 403
|
16
|
+
HTTP_NOT_FOUND = 404
|
17
|
+
HTTP_INTERNAL = 500
|
18
|
+
HTTP_NOT_IMPLEMENTED = 501
|
19
|
+
HTTP_SERVICE_UNAVAIL = 503
|
20
|
+
HTTP_GATEWAY_TIMEOUT = 504
|
21
|
+
|
22
|
+
# Error Code is defined here
|
23
|
+
#
|
24
|
+
# e.g.
|
25
|
+
# ERR_NAME = [err_code, http_status, err_message_template]
|
26
|
+
# NOT_FOUND = [30300, HTTP_NOT_FOUND, '%s not found!' ]
|
27
|
+
|
28
|
+
# 30000 - 30099 400 Bad Request
|
29
|
+
INVALID_CONTENT = [30000, HTTP_BAD_REQUEST, 'Invalid Content-Type']
|
30
|
+
MALFORMATTED_REQ = [30001, HTTP_BAD_REQUEST, 'Malformatted request']
|
31
|
+
UNKNOWN_PLAN_UNIQUE_ID = [30002, HTTP_BAD_REQUEST, 'Unknown unique_id']
|
32
|
+
UNKNOWN_PLAN = [30003, HTTP_BAD_REQUEST, 'Unknown plan %s']
|
33
|
+
UNSUPPORTED_VERSION = [30004, HTTP_BAD_REQUEST, 'Unsupported version %s']
|
34
|
+
|
35
|
+
# 30100 - 30199 401 Unauthorized
|
36
|
+
NOT_AUTHORIZED = [30100, HTTP_NOT_AUTHORIZED, 'Not authorized']
|
37
|
+
|
38
|
+
# 30200 - 30299 403 Forbidden
|
39
|
+
|
40
|
+
# 30300 - 30399 404 Not Found
|
41
|
+
NOT_FOUND = [30300, HTTP_NOT_FOUND, '%s not found']
|
42
|
+
|
43
|
+
# 30500 - 30599 500 Internal Error
|
44
|
+
INTERNAL_ERROR = [30500, HTTP_INTERNAL, 'Internal Error']
|
45
|
+
EXTENSION_NOT_IMPL = [30501, HTTP_NOT_IMPLEMENTED, "Service extension %s is not implemented."]
|
46
|
+
NODE_OPERATION_TIMEOUT = [30502, HTTP_INTERNAL, "Node operation timeout"]
|
47
|
+
SERVICE_START_TIMEOUT = [30503, HTTP_INTERNAL, "Service start timeout"]
|
48
|
+
WARDEN_RUN_COMMAND_FAILURE = [30504, HTTP_INTERNAL, "Failed to run command %s in warden container %s, exit status: %d, stdout: %s, stderr: %s"]
|
49
|
+
|
50
|
+
# 30600 - 30699 503 Service Unavailable
|
51
|
+
SERVICE_UNAVAILABLE = [30600, HTTP_SERVICE_UNAVAIL, 'Service unavailable']
|
52
|
+
|
53
|
+
# 30700 - 30799 500 Gateway Timeout
|
54
|
+
GATEWAY_TIMEOUT = [30700, HTTP_GATEWAY_TIMEOUT, 'Gateway timeout']
|
55
|
+
|
56
|
+
# 30800 - 30899 500 Lifecycle error
|
57
|
+
OVER_QUOTA = [30800, HTTP_INTERNAL, "Instance %s has %s snapshots. Quota is %s "]
|
58
|
+
JOB_QUEUE_TIMEOUT = [30801, HTTP_INTERNAL, "Job timeout after waiting for %s seconds."]
|
59
|
+
JOB_TIMEOUT = [30802, HTTP_INTERNAL, "Job is killed since it runs longer than ttl: %s seconds."]
|
60
|
+
BAD_SERIALIZED_DATAFILE = [30803, HTTP_INTERNAL, "Invalid serialized data file from: %s"]
|
61
|
+
FILESIZE_TOO_LARGE = [30804, HTTP_BAD_REQUEST, "Size of file from url %s is %s, max allowed %s"]
|
62
|
+
TOO_MANY_REDIRECTS = [30805, HTTP_BAD_REQUEST, "Too many redirects from url:%s, max redirects allowed is %s"]
|
63
|
+
FILE_CORRUPTED = [30806, HTTP_BAD_REQUEST, "Serialized file is corrupted."]
|
64
|
+
REDIS_CONCURRENT_UPDATE = [30807, HTTP_INTERNAL, "Server busy, please try again later."]
|
65
|
+
INVALID_SNAPSHOT_NAME = [30808, HTTP_BAD_REQUEST, "Invalid snapshot name. %s"]
|
66
|
+
|
67
|
+
|
68
|
+
# 31000 - 32000 Service-specific Error
|
69
|
+
# Defined in services directory, for example mongodb/lib/mongodb_service/
|
70
|
+
|
71
|
+
def initialize(code, *args)
|
72
|
+
@http_status = code[1]
|
73
|
+
@error_code = code[0]
|
74
|
+
@error_msg = sprintf(code[2], *args)
|
75
|
+
end
|
76
|
+
|
77
|
+
def to_s
|
78
|
+
"Error Code: #{@error_code}, Error Message: #{@error_msg}"
|
79
|
+
end
|
80
|
+
|
81
|
+
def to_hash
|
82
|
+
{
|
83
|
+
'status' => @http_status,
|
84
|
+
'msg' => {
|
85
|
+
'code' => @error_code,
|
86
|
+
'description' => @error_msg,
|
87
|
+
'error' => {
|
88
|
+
'backtrace' => backtrace,
|
89
|
+
'types' => self.class.ancestors.map(&:name) - Object.ancestors.map(&:name)
|
90
|
+
}
|
91
|
+
}
|
92
|
+
}
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
|
97
|
+
def success(response = true)
|
98
|
+
{'success' => true, 'response' => response}
|
99
|
+
end
|
100
|
+
|
101
|
+
def failure(exception)
|
102
|
+
{'success' => false, 'response' => exception.to_hash}
|
103
|
+
end
|
104
|
+
|
105
|
+
def internal_fail()
|
106
|
+
e = ServiceError.new(ServiceError::INTERNAL_ERROR)
|
107
|
+
failure(e)
|
108
|
+
end
|
109
|
+
|
110
|
+
def timeout_fail()
|
111
|
+
e = ServiceError.new(ServiceError::GATEWAY_TIMEOUT)
|
112
|
+
failure(e)
|
113
|
+
end
|
114
|
+
|
115
|
+
def parse_msg(msg)
|
116
|
+
Yajl::Parser.parse(msg)
|
117
|
+
end
|
118
|
+
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end
|
@@ -0,0 +1,94 @@
|
|
1
|
+
# Copyright (c) 2009-2011 VMware, Inc.
|
2
|
+
#
|
3
|
+
require 'api/message'
|
4
|
+
|
5
|
+
module VCAP::Services::Internal
|
6
|
+
|
7
|
+
# Provisioner --> Node
|
8
|
+
class ProvisionRequest < ServiceMessage
|
9
|
+
required :plan, String
|
10
|
+
optional :credentials
|
11
|
+
optional :version
|
12
|
+
end
|
13
|
+
|
14
|
+
# Node --> Provisioner
|
15
|
+
class ProvisionResponse < ServiceMessage
|
16
|
+
required :success
|
17
|
+
optional :credentials
|
18
|
+
optional :error
|
19
|
+
end
|
20
|
+
|
21
|
+
class UnprovisionRequest < ServiceMessage
|
22
|
+
required :name, String
|
23
|
+
required :bindings, [Hash]
|
24
|
+
end
|
25
|
+
|
26
|
+
class BindRequest < ServiceMessage
|
27
|
+
required :name, String
|
28
|
+
optional :bind_opts, Hash
|
29
|
+
optional :credentials
|
30
|
+
end
|
31
|
+
|
32
|
+
class BindResponse < ServiceMessage
|
33
|
+
required :success
|
34
|
+
optional :credentials
|
35
|
+
optional :error
|
36
|
+
end
|
37
|
+
|
38
|
+
class UnbindRequest < ServiceMessage
|
39
|
+
required :credentials
|
40
|
+
end
|
41
|
+
|
42
|
+
class SimpleResponse < ServiceMessage
|
43
|
+
required :success
|
44
|
+
optional :error
|
45
|
+
end
|
46
|
+
|
47
|
+
class RestoreRequest < ServiceMessage
|
48
|
+
required :instance_id
|
49
|
+
required :backup_path
|
50
|
+
end
|
51
|
+
|
52
|
+
class NodeHandlesReport < ServiceMessage
|
53
|
+
required :instances_list
|
54
|
+
required :bindings_list
|
55
|
+
required :node_id
|
56
|
+
end
|
57
|
+
|
58
|
+
class PurgeOrphanRequest < ServiceMessage
|
59
|
+
# A list of orphan instances names
|
60
|
+
required :orphan_ins_list
|
61
|
+
# A list of orphan bindings credentials
|
62
|
+
required :orphan_binding_list
|
63
|
+
end
|
64
|
+
|
65
|
+
class ServiceHandle < ServiceMessage
|
66
|
+
required :service_id, String
|
67
|
+
required :configuration, Hash
|
68
|
+
required :credentials, Hash
|
69
|
+
optional :dashboard_url, String
|
70
|
+
end
|
71
|
+
|
72
|
+
class ServiceInstanceHandleV2 < ServiceMessage
|
73
|
+
required :gateway_data, Hash
|
74
|
+
required :credentials, Hash
|
75
|
+
optional :name, String
|
76
|
+
optional :service_plan_guid, String
|
77
|
+
optional :space_guid, String
|
78
|
+
optional :service_bindings_url, String
|
79
|
+
optional :space_url, String
|
80
|
+
optional :service_plan_url, String
|
81
|
+
optional :dashboard_url, String
|
82
|
+
end
|
83
|
+
|
84
|
+
class ServiceBindingHandleV2 < ServiceMessage
|
85
|
+
required :credentials, Hash
|
86
|
+
required :gateway_data, Hash
|
87
|
+
required :gateway_name, String
|
88
|
+
optional :app_guid, String
|
89
|
+
optional :service_instance_guid, String
|
90
|
+
optional :binding_options, Hash
|
91
|
+
optional :app_url, String
|
92
|
+
optional :service_instance_url, String
|
93
|
+
end
|
94
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
module VCAP::Services
|
2
|
+
class ServicePlanChangeSet
|
3
|
+
attr_reader :service, :plans_to_add, :plans_to_update, :service_guid
|
4
|
+
def initialize(service, service_guid, options = {})
|
5
|
+
@service = service
|
6
|
+
@service_guid = service_guid
|
7
|
+
@plans_to_add = options[:plans_to_add] || []
|
8
|
+
@plans_to_update = options[:plans_to_update] || []
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
# Copyright (c) 2009-2012 VMware, Inc.
|
2
|
+
# Simple before filter to instance methods. We can chain filters to multiple methods.
|
3
|
+
# Filter method can return nil or raise error to terminater the evoke chain.
|
4
|
+
#
|
5
|
+
# Example:
|
6
|
+
# Class MyClass
|
7
|
+
# include Before
|
8
|
+
#
|
9
|
+
# def f1(msg) puts msg end
|
10
|
+
#
|
11
|
+
# def f2
|
12
|
+
# puts "f2"
|
13
|
+
# end
|
14
|
+
#
|
15
|
+
# def before_filter
|
16
|
+
# puts "before"
|
17
|
+
#
|
18
|
+
# true # return true to proceed
|
19
|
+
# end
|
20
|
+
#
|
21
|
+
# # use before method after methods are defined
|
22
|
+
# before [:f1, :f2], :before_filter
|
23
|
+
# end
|
24
|
+
#
|
25
|
+
# c = MyClass.new
|
26
|
+
# c.f1("hello")
|
27
|
+
# c.f2
|
28
|
+
# =>
|
29
|
+
# before
|
30
|
+
# hello
|
31
|
+
# before
|
32
|
+
# f2
|
33
|
+
module Before
|
34
|
+
|
35
|
+
def self.included(base)
|
36
|
+
base.extend ClassMethods
|
37
|
+
end
|
38
|
+
|
39
|
+
module ClassMethods
|
40
|
+
PREFIX = "___".freeze
|
41
|
+
|
42
|
+
def before(methods, callbacks)
|
43
|
+
Array(methods).each do | method|
|
44
|
+
method = method.to_sym
|
45
|
+
callbacks = Array(callbacks).map{|callback| callback.to_sym}
|
46
|
+
|
47
|
+
enhance_method(method, callbacks)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
# enhance single method with callbacks
|
52
|
+
def enhance_method(method, callbacks)
|
53
|
+
_method = (PREFIX + method.to_s).to_sym
|
54
|
+
alias_method _method, method
|
55
|
+
|
56
|
+
self.send(:define_method, method) do |*args, &blk|
|
57
|
+
[callbacks, _method].flatten.each do |callback|
|
58
|
+
break unless self.send(callback, *args, &blk)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
@@ -0,0 +1,227 @@
|
|
1
|
+
# Copyright (c) 2009-2011 VMware, Inc.
|
2
|
+
require "resque-status"
|
3
|
+
require "fileutils"
|
4
|
+
require "uuid"
|
5
|
+
|
6
|
+
require_relative "../service_error"
|
7
|
+
require_relative "../../base/job/package.rb"
|
8
|
+
require_relative "snapshot_client"
|
9
|
+
|
10
|
+
module VCAP::Services::Base::SnapshotV2
|
11
|
+
# common utils for snapshot job
|
12
|
+
class SnapshotJob
|
13
|
+
attr_reader :name, :snapshot_id
|
14
|
+
|
15
|
+
# FIXME: untangle mixins
|
16
|
+
# include Snapshot
|
17
|
+
include Resque::Plugins::Status
|
18
|
+
|
19
|
+
class << self
|
20
|
+
def queue_lookup_key
|
21
|
+
:node_id
|
22
|
+
end
|
23
|
+
|
24
|
+
def select_queue(*args)
|
25
|
+
result = nil
|
26
|
+
args.each do |arg|
|
27
|
+
result = arg[queue_lookup_key]if (arg.is_a? Hash)&& (arg.has_key?(queue_lookup_key))
|
28
|
+
end
|
29
|
+
@logger = Config.logger
|
30
|
+
@logger.info("Select queue #{result} for job #{self.class} with args:#{args.inspect}") if @logger
|
31
|
+
result
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def initialize(*args)
|
36
|
+
super(*args)
|
37
|
+
parse_config
|
38
|
+
init_worker_logger
|
39
|
+
Snapshot.redis_connect
|
40
|
+
end
|
41
|
+
|
42
|
+
def fmt_error(e)
|
43
|
+
"#{e}: [#{e.backtrace.join(" | ")}]"
|
44
|
+
end
|
45
|
+
|
46
|
+
def init_worker_logger
|
47
|
+
@logger = Config.logger
|
48
|
+
end
|
49
|
+
|
50
|
+
def required_options(*args)
|
51
|
+
missing_opts = args.select{|arg| !options.has_key? arg.to_s}
|
52
|
+
raise ArgumentError, "Missing #{missing_opts.join(', ')} in options: #{options.inspect}" unless missing_opts.empty?
|
53
|
+
end
|
54
|
+
|
55
|
+
def create_lock
|
56
|
+
lock_name = "lock:lifecycle:#{name}"
|
57
|
+
ttl = @config['job_ttl'] || 600
|
58
|
+
lock = Lock.new(lock_name, :logger => @logger, :ttl => ttl)
|
59
|
+
lock
|
60
|
+
end
|
61
|
+
|
62
|
+
def get_dump_path(name, snapshot_id)
|
63
|
+
snapshot_filepath(@config["snapshots_base_dir"], @config["service_name"], name, snapshot_id)
|
64
|
+
end
|
65
|
+
|
66
|
+
def parse_config
|
67
|
+
@config = Yajl::Parser.parse(ENV['WORKER_CONFIG'])
|
68
|
+
raise "Need environment variable: WORKER_CONFIG" unless @config
|
69
|
+
end
|
70
|
+
|
71
|
+
def cleanup(name, snapshot_id)
|
72
|
+
return unless name && snapshot_id
|
73
|
+
@logger.info("Clean up snapshot and files for #{name}, snapshot id: #{snapshot_id}")
|
74
|
+
delete_snapshot(name, snapshot_id)
|
75
|
+
FileUtils.rm_rf(get_dump_path(name, snapshot_id))
|
76
|
+
end
|
77
|
+
|
78
|
+
def handle_error(e)
|
79
|
+
@logger.error("Error in #{self.class} uuid:#{@uuid}: #{fmt_error(e)}")
|
80
|
+
err = (e.instance_of?(ServiceError)? e : ServiceError.new(ServiceError::INTERNAL_ERROR)).to_hash
|
81
|
+
err_msg = Yajl::Encoder.encode(err["msg"])
|
82
|
+
failed(err_msg)
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
class BaseCreateSnapshotJob < SnapshotJob
|
87
|
+
# workflow template
|
88
|
+
# Sub class should implement execute method which returns hash represents of snapshot like:
|
89
|
+
# {:snapshot_id => 1,
|
90
|
+
# :size => 100,
|
91
|
+
# :files => ["my_snapshot.tgz", "readme.txt"]
|
92
|
+
# :manifest => {:version => '1', :service => 'mysql'}
|
93
|
+
# }
|
94
|
+
def perform
|
95
|
+
begin
|
96
|
+
required_options :service_id
|
97
|
+
@name = options["service_id"]
|
98
|
+
@metadata = VCAP.symbolize_keys(options["metadata"])
|
99
|
+
@logger.info("Launch job: #{self.class} for #{name} with metadata: #{@metadata}")
|
100
|
+
|
101
|
+
@snapshot_id = new_snapshot_id
|
102
|
+
lock = create_lock
|
103
|
+
|
104
|
+
@snapshot_files = []
|
105
|
+
lock.lock do
|
106
|
+
quota = @config["snapshot_quota"]
|
107
|
+
if quota
|
108
|
+
current = service_snapshots_count(name)
|
109
|
+
@logger.debug("Current snapshots count for #{name}: #{current}, max: #{quota}")
|
110
|
+
raise ServiceError.new(ServiceError::OVER_QUOTA, name, current, quota) if current >= quota
|
111
|
+
end
|
112
|
+
|
113
|
+
snapshot = execute
|
114
|
+
snapshot = VCAP.symbolize_keys snapshot
|
115
|
+
snapshot[:manifest] ||= {}
|
116
|
+
snapshot[:manifest].merge! @metadata
|
117
|
+
@logger.info("Results of create snapshot: #{snapshot.inspect}")
|
118
|
+
|
119
|
+
# pack snapshot_file into package
|
120
|
+
dump_path = get_dump_path(name, snapshot_id)
|
121
|
+
FileUtils.mkdir_p(dump_path)
|
122
|
+
package_file = "#{snapshot_id}.zip"
|
123
|
+
|
124
|
+
package = Package.new(File.join(dump_path, package_file))
|
125
|
+
package.manifest = snapshot[:manifest]
|
126
|
+
files = Array(snapshot[:files])
|
127
|
+
raise "No snapshot file to package." if files.empty?
|
128
|
+
files.each do |f|
|
129
|
+
full_path = File.join(dump_path, f)
|
130
|
+
@snapshot_files << full_path
|
131
|
+
package.add_files full_path
|
132
|
+
end
|
133
|
+
package.pack(dump_path)
|
134
|
+
@logger.info("Package snapshot file: #{File.join(dump_path, package_file)}")
|
135
|
+
|
136
|
+
# update snapshot metadata for package file
|
137
|
+
snapshot.delete(:files)
|
138
|
+
snapshot[:file] = package_file
|
139
|
+
snapshot[:date] = fmt_time
|
140
|
+
# add default service name
|
141
|
+
snapshot[:name] = "Snapshot #{snapshot[:date]}"
|
142
|
+
|
143
|
+
save_snapshot(name, snapshot)
|
144
|
+
|
145
|
+
completed(Yajl::Encoder.encode(filter_keys(snapshot)))
|
146
|
+
@logger.info("Complete job: #{self.class} for #{name}")
|
147
|
+
end
|
148
|
+
rescue => e
|
149
|
+
cleanup(name, snapshot_id)
|
150
|
+
handle_error(e)
|
151
|
+
ensure
|
152
|
+
set_status({:complete_time => Time.now.to_s})
|
153
|
+
@snapshot_files.each{|f| File.delete(f) if File.exists? f} if @snapshot_files
|
154
|
+
end
|
155
|
+
end
|
156
|
+
end
|
157
|
+
|
158
|
+
class BaseDeleteSnapshotJob < SnapshotJob
|
159
|
+
def perform
|
160
|
+
begin
|
161
|
+
required_options :service_id, :snapshot_id
|
162
|
+
@name = options["service_id"]
|
163
|
+
@snapshot_id = options["snapshot_id"]
|
164
|
+
@logger.info("Launch job: #{self.class} for #{name}")
|
165
|
+
|
166
|
+
lock = create_lock
|
167
|
+
|
168
|
+
lock.lock do
|
169
|
+
result = execute
|
170
|
+
@logger.info("Results of delete snapshot: #{result}")
|
171
|
+
|
172
|
+
delete_snapshot(name, snapshot_id)
|
173
|
+
|
174
|
+
completed(Yajl::Encoder.encode({:result => :ok}))
|
175
|
+
@logger.info("Complete job: #{self.class} for #{name}")
|
176
|
+
end
|
177
|
+
rescue => e
|
178
|
+
handle_error(e)
|
179
|
+
ensure
|
180
|
+
set_status({:complete_time => Time.now.to_s})
|
181
|
+
end
|
182
|
+
end
|
183
|
+
|
184
|
+
def execute
|
185
|
+
cleanup(name, snapshot_id)
|
186
|
+
end
|
187
|
+
end
|
188
|
+
|
189
|
+
class BaseRollbackSnapshotJob < SnapshotJob
|
190
|
+
attr_reader :manifest, :snapshot_files
|
191
|
+
# workflow template
|
192
|
+
# Subclass implement execute method which returns true for a successful rollback
|
193
|
+
def perform
|
194
|
+
begin
|
195
|
+
required_options :service_id, :snapshot_id
|
196
|
+
@name = options["service_id"]
|
197
|
+
@snapshot_id = options["snapshot_id"]
|
198
|
+
@logger.info("Launch job: #{self.class} for #{name}")
|
199
|
+
|
200
|
+
lock = create_lock
|
201
|
+
|
202
|
+
@snapshot_files = []
|
203
|
+
lock.lock do
|
204
|
+
# extract origin files from package
|
205
|
+
dump_path = get_dump_path(name, snapshot_id)
|
206
|
+
package_file = "#{snapshot_id}.zip"
|
207
|
+
package = Package.load(File.join(dump_path, package_file))
|
208
|
+
@manifest = package.manifest
|
209
|
+
@snapshot_files = package.unpack(dump_path)
|
210
|
+
@logger.debug("Unpack files from #{package_file}: #{@snapshot_files}")
|
211
|
+
raise "Package file doesn't contain snapshot file." if @snapshot_files.empty?
|
212
|
+
|
213
|
+
result = execute
|
214
|
+
@logger.info("Results of rollback snapshot: #{result}")
|
215
|
+
|
216
|
+
completed(Yajl::Encoder.encode({:result => :ok}))
|
217
|
+
@logger.info("Complete job: #{self.class} for #{name}")
|
218
|
+
end
|
219
|
+
rescue => e
|
220
|
+
handle_error(e)
|
221
|
+
ensure
|
222
|
+
set_status({:complete_time => Time.now.to_s})
|
223
|
+
@snapshot_files.each{|f| File.delete(f) if File.exists? f} if @snapshot_files
|
224
|
+
end
|
225
|
+
end
|
226
|
+
end
|
227
|
+
end
|