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,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
|