cloud66 0.0.26
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of cloud66 might be problematic. Click here for more details.
- data/bin/c66-agent +632 -0
- data/lib/client_auth.rb +32 -0
- data/lib/cloud-quartz.rb +64 -0
- data/lib/plugins/backup.rb +74 -0
- data/lib/plugins/broken.rb +19 -0
- data/lib/plugins/file_rotate.rb +66 -0
- data/lib/plugins/log_rotate.rb +65 -0
- data/lib/plugins/mysql_backup.rb +92 -0
- data/lib/plugins/quartz_plugin.rb +44 -0
- data/lib/plugins/rackspace_backup.rb +207 -0
- data/lib/plugins/rake.rb +29 -0
- data/lib/plugins/redis_backup.rb +95 -0
- data/lib/plugins/s3_backup.rb +200 -0
- data/lib/plugins/shell.rb +26 -0
- data/lib/plugins/tester.rb +22 -0
- data/lib/plugins/webget.rb +39 -0
- data/lib/version.rb +52 -0
- data/lib/vital_signs_utils.rb +161 -0
- metadata +207 -0
data/lib/cloud-quartz.rb
ADDED
@@ -0,0 +1,64 @@
|
|
1
|
+
require 'httparty'
|
2
|
+
require 'json'
|
3
|
+
|
4
|
+
class CloudQuartz
|
5
|
+
include HTTParty
|
6
|
+
@api_key = ""
|
7
|
+
@agent_id = ""
|
8
|
+
|
9
|
+
def initialize(options = {})
|
10
|
+
@api_key = options[:api_key]
|
11
|
+
@agent_id = options[:agent_id]
|
12
|
+
@secret_key = options[:secret_key]
|
13
|
+
self.class.base_uri options[:url] || 'https://api.cloud66.com'
|
14
|
+
end
|
15
|
+
|
16
|
+
def get_job
|
17
|
+
process(self.class.get("/queue/#{@agent_id}.json", { :headers => ClientAuth.build_headers(@api_key, @secret_key) } ))
|
18
|
+
end
|
19
|
+
|
20
|
+
def register(agent)
|
21
|
+
process(self.class.post('/agent.json', { :headers => ClientAuth.build_headers(@api_key, @secret_key).merge({'Content-Type' => 'application/json'}), :body => agent.to_json }))
|
22
|
+
end
|
23
|
+
|
24
|
+
def unregister(agent)
|
25
|
+
process(self.class.delete("/agent/#{@agent_id}.json", :headers => ClientAuth.build_headers(@api_key, @secret_key)))
|
26
|
+
end
|
27
|
+
|
28
|
+
def post_results(job_id, data)
|
29
|
+
process(self.class.post("/job/#{job_id}/complete.json", { :headers => ClientAuth.build_headers(@api_key, @secret_key).merge({'Content-Type' => 'application/json'}), :body => data.to_json } ))
|
30
|
+
end
|
31
|
+
|
32
|
+
def pulse_without_ip_address
|
33
|
+
process(self.class.get("/agent/#{@agent_id}/pulse.json", { :headers => ClientAuth.build_headers(@api_key, @secret_key) } ))
|
34
|
+
end
|
35
|
+
|
36
|
+
def pulse_with_ip_address(data)
|
37
|
+
process(self.class.post("/agent/#{@agent_id}/pulse.json", { :headers => ClientAuth.build_headers(@api_key, @secret_key).merge({'Content-Type' => 'application/json'}), :body => data.to_json } ))
|
38
|
+
end
|
39
|
+
|
40
|
+
def status(stat)
|
41
|
+
data = { :status => stat }
|
42
|
+
process(self.class.post("/agent/#{@agent_id}/status.json", { :headers => ClientAuth.build_headers(@api_key, @secret_key).merge({'Content-Type' => 'application/json'}), :body => data.to_json }))
|
43
|
+
end
|
44
|
+
|
45
|
+
def init(data)
|
46
|
+
process(self.class.post("/agent/#{@agent_id}/initialize.json", { :headers => ClientAuth.build_headers(@api_key, @secret_key).merge({'Content-Type' => 'application/json'}), :body => data.to_json }))
|
47
|
+
end
|
48
|
+
|
49
|
+
def send_vital_signs(data)
|
50
|
+
process(self.class.post("/agent/#{@agent_id}/vitalsigns.json", { :headers => ClientAuth.build_headers(@api_key, @secret_key).merge({'Content-Type' => 'application/json'}), :body => data.to_json } ))
|
51
|
+
end
|
52
|
+
|
53
|
+
private
|
54
|
+
|
55
|
+
def process(response)
|
56
|
+
if response.code != 200
|
57
|
+
raise response.body
|
58
|
+
else
|
59
|
+
response.parsed_response
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
|
@@ -0,0 +1,74 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), 'quartz_plugin')
|
2
|
+
require 'tempfile'
|
3
|
+
require 'fileutils'
|
4
|
+
|
5
|
+
class Backup < QuartzPlugin
|
6
|
+
|
7
|
+
@@version_major = 0
|
8
|
+
@@version_minor = 0
|
9
|
+
@@version_revision = 1
|
10
|
+
|
11
|
+
def info
|
12
|
+
{ :uid => "64cec894b98b43ce9e6047a9284a4b7d", :name => "Backup", :version => get_version }
|
13
|
+
end
|
14
|
+
|
15
|
+
def run(message)
|
16
|
+
|
17
|
+
pl = payload(message)
|
18
|
+
|
19
|
+
job_name = pl['job_name']
|
20
|
+
backup_script = "#Script generated by Cloud66 job '#{job_name}'\n\n" + pl['script']
|
21
|
+
script_name = pl['script_name']
|
22
|
+
|
23
|
+
random_file = Tempfile.new('cloud66').path + '.rb'
|
24
|
+
File.open(random_file, 'w') do |f|
|
25
|
+
f.puts backup_script
|
26
|
+
end
|
27
|
+
|
28
|
+
backup_root_directory = File.expand_path('~/.cloud66/backup')
|
29
|
+
FileUtils.mkdir_p(backup_root_directory)
|
30
|
+
|
31
|
+
command = "backup perform --trigger #{script_name} --root-path #{backup_root_directory} --config_file #{random_file} 2>&1"
|
32
|
+
@log.info "Shell command '#{command}'"
|
33
|
+
|
34
|
+
begin
|
35
|
+
result = run_shell("#{command}")
|
36
|
+
data = result[:message]
|
37
|
+
|
38
|
+
#regex to identify all the "error" lines
|
39
|
+
errorRegex = Regexp.new('(?<line>\[\d{4}\/\d{2}\/\d{2}\s\d{2}\:\d{2}\:\d{2}\]\[\\e\[31merror\\e\[0m\].*?)(\\n|$)')
|
40
|
+
errors = data.scan(errorRegex)
|
41
|
+
|
42
|
+
if errors.size > 0
|
43
|
+
|
44
|
+
#log all the errors
|
45
|
+
@log.info errors
|
46
|
+
|
47
|
+
#get rid of the backtrace
|
48
|
+
removeRegex = Regexp.new('(Backtrace\:|\d{2}\:\d{2}\]\[\\e\[31merror\\e\[0m\]\s*\/)')
|
49
|
+
|
50
|
+
#subregex to get rid of nasty timestamp and [\e[31merror\e[0m]
|
51
|
+
replaceRegex = Regexp.new('^.*\[\\e\[31merror\\e\[0m\]')
|
52
|
+
|
53
|
+
errorResult = []
|
54
|
+
errors.each do |errorArr|
|
55
|
+
error = errorArr[0]
|
56
|
+
errorResult << error.gsub(replaceRegex,'').chomp unless error =~ removeRegex
|
57
|
+
end
|
58
|
+
|
59
|
+
run_result(false, errorResult.join("\n"))
|
60
|
+
else
|
61
|
+
if (result[:ok])
|
62
|
+
run_result(true, "Backup completed successfully!")
|
63
|
+
else
|
64
|
+
run_result(true, "An error occurred!")
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
rescue => ex
|
69
|
+
run_result(false, "Failed to execute backup due to #{ex}")
|
70
|
+
ensure
|
71
|
+
File.delete random_file if File.exists? random_file
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), 'quartz_plugin')
|
2
|
+
|
3
|
+
class Broken < QuartzPlugin
|
4
|
+
|
5
|
+
@@version_major = 0
|
6
|
+
@@version_minor = 0
|
7
|
+
@@version_revision = 1
|
8
|
+
|
9
|
+
def info
|
10
|
+
{ :uid => "04165a45fde840a9a17b41f019b3dca3", :name => "Broken", :version => get_version }
|
11
|
+
end
|
12
|
+
|
13
|
+
def run(message)
|
14
|
+
@log.info "Running with #{message}"
|
15
|
+
@log.info "This is a failure"
|
16
|
+
|
17
|
+
run_result(false, "Boo! It's broken")
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), 'quartz_plugin')
|
2
|
+
require 'fileutils'
|
3
|
+
|
4
|
+
class FileRotate < QuartzPlugin
|
5
|
+
|
6
|
+
@@version_major = 0
|
7
|
+
@@version_minor = 0
|
8
|
+
@@version_revision = 1
|
9
|
+
|
10
|
+
def info
|
11
|
+
{ :uid => "02f7d8237bcc438e8f0659babfef2911", :name => "File Rotater", :version => get_version }
|
12
|
+
end
|
13
|
+
|
14
|
+
def run(message)
|
15
|
+
pl = payload(message)
|
16
|
+
|
17
|
+
source_pattern = pl['source_pattern']
|
18
|
+
dest_folder = pl['location']
|
19
|
+
keep = pl['keep'].nil? ? 5 : pl['keep'].to_i
|
20
|
+
post_rotate = pl['post_rotate']
|
21
|
+
|
22
|
+
archive = File.join(dest_folder, '/archive')
|
23
|
+
|
24
|
+
ext = Time.now.utc.strftime('%Y%m%d%H%M%S')
|
25
|
+
|
26
|
+
FileUtils.mkdir_p(archive)
|
27
|
+
rotated = false
|
28
|
+
Dir.glob(source_pattern).each do |f|
|
29
|
+
rotated = true
|
30
|
+
fname = File.basename(f)
|
31
|
+
dest_filename = "#{fname}.#{ext}"
|
32
|
+
dest_path = File.join(archive, dest_filename)
|
33
|
+
@log.debug "Moving #{f} to #{dest_path}"
|
34
|
+
|
35
|
+
move_shell = run_shell("mv #{f} #{dest_path}")
|
36
|
+
return run_result(false, move_shell[:message]) unless move_shell[:ok]
|
37
|
+
|
38
|
+
return run_result(true, "Files moved successfully with no rotation") if keep == 0
|
39
|
+
|
40
|
+
# find all the files from this one's rotations
|
41
|
+
rotated_pattern = "#{File.join(archive, fname)}.*"
|
42
|
+
@log.debug "Looking for rotated files #{rotated_pattern}"
|
43
|
+
all_rotated = Dir.glob(rotated_pattern)
|
44
|
+
if all_rotated.count > keep
|
45
|
+
remove_count = all_rotated.count - keep
|
46
|
+
to_remote = all_rotated.sort! { |a,b| File.mtime(a) <=> File.mtime(b) }[0...remove_count]
|
47
|
+
@log.debug "Removing extra files"
|
48
|
+
to_remote.each do |tr|
|
49
|
+
@log.debug "Removing #{tr}"
|
50
|
+
remove_shell = run_shell("rm #{tr}")
|
51
|
+
return run_result(false, remove_shell[:message]) unless remove_shell[:ok]
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
if !post_rotate.empty? && rotated
|
57
|
+
@log.debug "Running post rotate step #{post_rotate}"
|
58
|
+
post_shell = run_shell(post_rotate)
|
59
|
+
return run_result(false, post_shell[:message]) unless post_shell[:ok]
|
60
|
+
end
|
61
|
+
|
62
|
+
result = rotated ? "Files rotated successfully" : "No files to rotate"
|
63
|
+
|
64
|
+
run_result(true, result)
|
65
|
+
end
|
66
|
+
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), 'quartz_plugin')
|
2
|
+
require 'fileutils'
|
3
|
+
|
4
|
+
class LogRotate < QuartzPlugin
|
5
|
+
|
6
|
+
@@version_major = 0
|
7
|
+
@@version_minor = 0
|
8
|
+
@@version_revision = 1
|
9
|
+
|
10
|
+
def info
|
11
|
+
{ :uid => "8f4286bfd946c8b08b234833673b8860", :name => "Log Rotate", :version => get_version }
|
12
|
+
end
|
13
|
+
|
14
|
+
def run(message)
|
15
|
+
pl = payload(message)
|
16
|
+
|
17
|
+
@source_pattern = pl['source_pattern']
|
18
|
+
@dest_folder = pl['destination']
|
19
|
+
@keep = pl['keep'].empty? ? 0 : pl['keep'].to_i
|
20
|
+
@post_run_step = pl['post_rotate']
|
21
|
+
|
22
|
+
ext = Time.now.utc.strftime('%Y%m%d%H%M%S')
|
23
|
+
|
24
|
+
FileUtils.mkdir_p(@dest_folder)
|
25
|
+
|
26
|
+
Dir.glob(@source_pattern).each do |f|
|
27
|
+
dest_file = File.join(@dest_folder, File.basename(f))
|
28
|
+
next if File.directory?(f)
|
29
|
+
fname = "#{dest_file}.gz"
|
30
|
+
dump_cmd = "cat #{f} | gzip > '#{fname}.#{ext}'"
|
31
|
+
@log.debug "Running #{dump_cmd}"
|
32
|
+
copy_result = run_shell(dump_cmd)
|
33
|
+
|
34
|
+
return run_result(false, copy_result[:message]) unless copy_result[:ok]
|
35
|
+
|
36
|
+
@log.debug "Removing source log files"
|
37
|
+
remove_result = run_shell("rm #{f}")
|
38
|
+
|
39
|
+
return run_result(false, remove_result[:message]) unless remove_result[:ok]
|
40
|
+
|
41
|
+
# find all the files from this one's rotations
|
42
|
+
rotated_pattern = "#{fname}.*"
|
43
|
+
@log.debug "Looking for rotated files #{rotated_pattern}"
|
44
|
+
all_rotated = Dir.glob(rotated_pattern)
|
45
|
+
if all_rotated.count > @keep
|
46
|
+
remove_count = all_rotated.count - @keep
|
47
|
+
to_remote = all_rotated.sort! { |a,b| File.mtime(a) <=> File.mtime(b) }[0...remove_count]
|
48
|
+
@log.debug "Removing extra #{remove_count} files"
|
49
|
+
to_remote.each do |tr|
|
50
|
+
@log.debug "Removing #{tr}"
|
51
|
+
remove_shell = run_shell("rm #{tr}")
|
52
|
+
return run_result(false, remove_shell[:message]) unless remove_shell[:ok]
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
if !@post_run_step.nil? && !@post_run_step.empty?
|
58
|
+
@log.debug "Running post rotate step #{@post_run_step}"
|
59
|
+
post_shell = run_shell(@post_run_step)
|
60
|
+
return run_result(false, post_shell[:message]) unless post_shell[:ok]
|
61
|
+
end
|
62
|
+
|
63
|
+
return run_result(true, "Log files rotated succesfully")
|
64
|
+
end
|
65
|
+
end
|
@@ -0,0 +1,92 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), 'quartz_plugin')
|
2
|
+
require 'fileutils'
|
3
|
+
|
4
|
+
class MysqlBackup < QuartzPlugin
|
5
|
+
|
6
|
+
@@version_major = 0
|
7
|
+
@@version_minor = 0
|
8
|
+
@@version_revision = 1
|
9
|
+
|
10
|
+
def info
|
11
|
+
{ :uid => "67deb35a555344c8a7651c656e6c8e2e", :name => "MySQL Backup", :version => get_version }
|
12
|
+
end
|
13
|
+
|
14
|
+
def run(message)
|
15
|
+
pl = payload(message)
|
16
|
+
pl = pl.select { |k,v| !v.nil? && !v.empty? }
|
17
|
+
|
18
|
+
@log.debug "Pruned payload #{pl}"
|
19
|
+
|
20
|
+
@job_name = pl['job_name'].gsub(/[^\w\s_-]+/, '').gsub(/(^|\b\s)\s+($|\s?\b)/, '\\1\\2').gsub(/\s/, '_')
|
21
|
+
@mysqldump_utility = pl['dump_utility'] || '/usr/bin/mysqldump'
|
22
|
+
@name = pl['db_name'] || :all
|
23
|
+
@username = pl['username']
|
24
|
+
@password = pl['password']
|
25
|
+
@socket = pl['socket']
|
26
|
+
@host = pl['host']
|
27
|
+
@port = pl['port']
|
28
|
+
@skip_tables = pl['skip_tables']
|
29
|
+
@only_tables = pl['only_tables']
|
30
|
+
@additional_options = pl['additional_options'] || ['--single-transaction', '--quick']
|
31
|
+
@path = pl['backup_folder']
|
32
|
+
|
33
|
+
@only_tables = @only_tables.split(',') unless @only_tables.nil?
|
34
|
+
@skip_tables = @skip_tables.split(',') unless @skip_tables.nil?
|
35
|
+
|
36
|
+
dump_cmd = "#{mysqldump} | gzip > '#{ File.join(@path, @job_name.downcase) }.sql.gz'"
|
37
|
+
@log.debug "Running #{dump_cmd}"
|
38
|
+
|
39
|
+
FileUtils.mkdir_p(@path)
|
40
|
+
|
41
|
+
result = run_shell dump_cmd
|
42
|
+
if result[:ok]
|
43
|
+
run_result(true, "MySQL Backup finished successfully")
|
44
|
+
else
|
45
|
+
run_result(false, result[:message])
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
private
|
50
|
+
|
51
|
+
# copied from backup gem with little mods
|
52
|
+
def mysqldump
|
53
|
+
"#{ @mysqldump_utility } #{ credential_options } #{ connectivity_options } " +
|
54
|
+
"#{ user_options } #{ @name } #{ tables_to_dump } #{ tables_to_skip }"
|
55
|
+
end
|
56
|
+
|
57
|
+
def credential_options
|
58
|
+
%w[username password].map do |option|
|
59
|
+
value = instance_variable_get("@#{option}")
|
60
|
+
next if value.to_s.empty?
|
61
|
+
"--#{option}='#{value}'".gsub('--username', '--user')
|
62
|
+
end.compact.join(' ')
|
63
|
+
end
|
64
|
+
|
65
|
+
def connectivity_options
|
66
|
+
%w[host port socket].map do |option|
|
67
|
+
value = instance_variable_get("@#{option}")
|
68
|
+
next if value.to_s.empty?
|
69
|
+
"--#{option}='#{value}'"
|
70
|
+
end.compact.join(' ')
|
71
|
+
end
|
72
|
+
|
73
|
+
def user_options
|
74
|
+
@additional_options.join(' ') unless @additional_options.nil?
|
75
|
+
end
|
76
|
+
|
77
|
+
def tables_to_dump
|
78
|
+
@only_tables.join(' ') unless @only_tables.nil? || dump_all?
|
79
|
+
end
|
80
|
+
|
81
|
+
def tables_to_skip
|
82
|
+
return '' if @skip_tables.nil?
|
83
|
+
@skip_tables.map do |table|
|
84
|
+
"--ignore-table='#{@name}.#{table}'"
|
85
|
+
end.join(' ') unless dump_all?
|
86
|
+
end
|
87
|
+
|
88
|
+
def dump_all?
|
89
|
+
@name == :all
|
90
|
+
end
|
91
|
+
|
92
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
require 'open4'
|
2
|
+
require 'json'
|
3
|
+
|
4
|
+
class QuartzPlugin
|
5
|
+
|
6
|
+
def initialize(log, options)
|
7
|
+
@log = log
|
8
|
+
@options = options
|
9
|
+
end
|
10
|
+
|
11
|
+
def run_result(success, message)
|
12
|
+
result = { :ok => success, :message => message }
|
13
|
+
@log.debug "Job finished with result #{result}"
|
14
|
+
|
15
|
+
result
|
16
|
+
end
|
17
|
+
|
18
|
+
def payload(message)
|
19
|
+
v = message['payload']
|
20
|
+
@log.debug "Payload received: #{v}"
|
21
|
+
v = v.merge({'job_name' => message['job_name']})
|
22
|
+
@log.debug "Payload used: #{v}"
|
23
|
+
v
|
24
|
+
end
|
25
|
+
|
26
|
+
def run_shell(command)
|
27
|
+
pid, stdin, stdout, stderr = Open4::popen4("#{command}")
|
28
|
+
ignored, status = Process::waitpid2 pid
|
29
|
+
|
30
|
+
if status.exitstatus == 0
|
31
|
+
{ :ok => true, :message => stdout.read.strip }
|
32
|
+
else
|
33
|
+
{ :ok => false, :message => stderr.read.strip}
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
@@version_major = 0
|
38
|
+
@@version_minor = 0
|
39
|
+
@@version_revision = 1
|
40
|
+
|
41
|
+
def get_version
|
42
|
+
"#{@@version_major}.#{@@version_minor}.#{@@version_revision}"
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,207 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), 'quartz_plugin')
|
2
|
+
require 'fileutils'
|
3
|
+
require 'fog'
|
4
|
+
require 'set'
|
5
|
+
|
6
|
+
class RackspaceBackup < QuartzPlugin
|
7
|
+
|
8
|
+
@@version_major = 0
|
9
|
+
@@version_minor = 0
|
10
|
+
@@version_revision = 1
|
11
|
+
|
12
|
+
def info
|
13
|
+
{ :uid => "86a34908c51311e1a0a923db6188709b", :name => "Rackspace Backup", :version => get_version }
|
14
|
+
end
|
15
|
+
|
16
|
+
def run(message)
|
17
|
+
|
18
|
+
pl = payload(message)
|
19
|
+
@log.debug "Pruned payload #{pl}"
|
20
|
+
|
21
|
+
@username = pl['username']
|
22
|
+
@api_key = pl['api_key']
|
23
|
+
@container = pl['container']
|
24
|
+
@remote_path = pl['remote_path']
|
25
|
+
@region = pl['region']
|
26
|
+
@keep = pl['keep'].empty? ? 0 : pl['keep'].to_i
|
27
|
+
@local_pattern = pl['local_pattern']
|
28
|
+
@testing = pl['testing']
|
29
|
+
|
30
|
+
return transfer
|
31
|
+
end
|
32
|
+
|
33
|
+
private
|
34
|
+
|
35
|
+
def get_connection
|
36
|
+
#Fog.mock! unless @testing.nil? || @testing == false
|
37
|
+
if @region == 'europe'
|
38
|
+
connection = Fog::Storage.new(
|
39
|
+
:provider => 'Rackspace',
|
40
|
+
:rackspace_username => @username,
|
41
|
+
:rackspace_api_key => @api_key,
|
42
|
+
:rackspace_auth_url => "lon.auth.api.rackspacecloud.com"
|
43
|
+
)
|
44
|
+
else
|
45
|
+
connection = Fog::Storage.new(
|
46
|
+
:provider => 'Rackspace',
|
47
|
+
:rackspace_username => @username,
|
48
|
+
:rackspace_api_key => @api_key,
|
49
|
+
)
|
50
|
+
end
|
51
|
+
connection
|
52
|
+
|
53
|
+
end
|
54
|
+
|
55
|
+
def transfer
|
56
|
+
begin
|
57
|
+
#set up the rackspace connection
|
58
|
+
@connection = get_connection
|
59
|
+
if @keep <= 0
|
60
|
+
sync_files_without_version_history
|
61
|
+
else
|
62
|
+
sync_files_with_version_history
|
63
|
+
end
|
64
|
+
|
65
|
+
rescue Excon::Errors::SocketError => exc
|
66
|
+
@log.error exc.message
|
67
|
+
return run_result(false, exc.message)
|
68
|
+
rescue Excon::Errors::Error => exc
|
69
|
+
@log.error exc.message
|
70
|
+
result = exc.response.body
|
71
|
+
message = result.match(/\<Message\>(.*)\<\/Message\>/)
|
72
|
+
if !message.nil?
|
73
|
+
message = message[1]
|
74
|
+
return run_result(false, message)
|
75
|
+
elsif exc.response.status == 404
|
76
|
+
return run_result(false, "Remote rackspace serivce or container not found (404)")
|
77
|
+
elsif exc.response.status != 0
|
78
|
+
return run_result(false, "Remote rackspace serivce returned error #{exc.response.status} without any more details")
|
79
|
+
else
|
80
|
+
return run_result(false, exc.message)
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
def remove_initial_slash(filename)
|
86
|
+
filename.sub(/^\//, '')
|
87
|
+
end
|
88
|
+
|
89
|
+
def ensure_trailing_slash(filename)
|
90
|
+
return "" if filename.empty?
|
91
|
+
filename.sub(/\/$/, '') + '/'
|
92
|
+
end
|
93
|
+
|
94
|
+
def sync_files_without_version_history
|
95
|
+
|
96
|
+
#prepare the remote directory variable
|
97
|
+
@remote_path = remove_initial_slash(ensure_trailing_slash(@remote_path))
|
98
|
+
count = 0
|
99
|
+
|
100
|
+
#for each local file match
|
101
|
+
Dir.glob(File.expand_path(@local_pattern)).each do |f|
|
102
|
+
|
103
|
+
#skip to next match if current is a directory
|
104
|
+
next if File.directory?(f)
|
105
|
+
|
106
|
+
#assign the remote filename
|
107
|
+
new_remote_filename = remove_initial_slash(File.join(@remote_path, f))
|
108
|
+
@log.debug "Copying #{f} to #{new_remote_filename}"
|
109
|
+
count += 1
|
110
|
+
|
111
|
+
#push file to rackspace
|
112
|
+
File.open(f, 'r') do |file|
|
113
|
+
@connection.put_object(@container, new_remote_filename, file)
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
return run_result(true, "Successully pushed #{count} file(s) to Rackspace Cloud Files container (without version history)")
|
118
|
+
|
119
|
+
end
|
120
|
+
|
121
|
+
def sync_files_with_version_history
|
122
|
+
|
123
|
+
#prepare the remote directory variable
|
124
|
+
@remote_path = remove_initial_slash(ensure_trailing_slash(@remote_path))
|
125
|
+
count = 0
|
126
|
+
|
127
|
+
#get remote directory
|
128
|
+
directory = @connection.directories.get(@container)
|
129
|
+
|
130
|
+
#cache the rackspace remote directory identifing all appropriate files
|
131
|
+
remote_path_match = Regexp.new("^#{@remote_path}", "i")
|
132
|
+
rackspace_directory = directory.files.map {|f| f.key }
|
133
|
+
|
134
|
+
all_remote_files = rackspace_directory.select {|m| m =~ remote_path_match}.map {|m| remove_initial_slash(m.gsub(remote_path_match, ''))}
|
135
|
+
archive_regex = /(?<folder>^Archive_Cloud66 \(\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}\)\/)/
|
136
|
+
non_archive_files = all_remote_files.reject { |m| m =~ archive_regex }
|
137
|
+
archived_files = all_remote_files.select { |m| m =~ archive_regex }
|
138
|
+
|
139
|
+
new_archive_folder = "Archive_Cloud66 (#{Time.now.strftime("%Y-%m-%d %H:%M:%S")})/"
|
140
|
+
|
141
|
+
#copy all non-archive files to new backup folder
|
142
|
+
non_archive_files.each do |relative_file|
|
143
|
+
|
144
|
+
puts "name is #{relative_file}"
|
145
|
+
|
146
|
+
#move file to archive
|
147
|
+
existing_remote_filename = remove_initial_slash(File.join(@remote_path, relative_file))
|
148
|
+
new_remote_relative_filename = File.join(new_archive_folder, "#{relative_file}")
|
149
|
+
new_remote_filename = remove_initial_slash(File.join(@remote_path, new_remote_relative_filename))
|
150
|
+
|
151
|
+
@log.debug "Copying #{existing_remote_filename} to #{new_remote_filename}"
|
152
|
+
@connection.copy_object @container, existing_remote_filename, @container, new_remote_filename
|
153
|
+
|
154
|
+
@log.debug "Removing #{existing_remote_filename}"
|
155
|
+
@connection.delete_object @container, existing_remote_filename
|
156
|
+
|
157
|
+
#add newly archived file to list of archived files
|
158
|
+
archived_files << new_remote_relative_filename
|
159
|
+
end
|
160
|
+
|
161
|
+
#copy up all new files from source
|
162
|
+
all_local_files = Dir.glob(File.expand_path(@local_pattern))
|
163
|
+
return run_result(true, "No file(s) identified to push to Rackspace Cloud Files container (with version history)") if all_local_files.size == 0
|
164
|
+
|
165
|
+
#determine a local root to create relative files (TODO?)
|
166
|
+
#local_root = ""
|
167
|
+
#local_root_regex = Regexp.new local_root
|
168
|
+
|
169
|
+
#copy all local matches up to rackspace
|
170
|
+
all_local_files.each do |f|
|
171
|
+
|
172
|
+
#skip to next match if current is a directory
|
173
|
+
next if File.directory?(f)
|
174
|
+
|
175
|
+
#assign the remote filename
|
176
|
+
new_remote_filename = remove_initial_slash(File.join(@remote_path, f))
|
177
|
+
@log.debug "Copying #{f} to #{new_remote_filename}"
|
178
|
+
count += 1
|
179
|
+
|
180
|
+
#push file to rackspace
|
181
|
+
File.open(f, 'r') do |file|
|
182
|
+
@connection.put_object @container, new_remote_filename, file
|
183
|
+
end
|
184
|
+
|
185
|
+
end
|
186
|
+
|
187
|
+
#get list of archive folders
|
188
|
+
archive_folders = archived_files.map {|m| archive_regex.match(m)['folder']}.uniq.sort.reverse
|
189
|
+
|
190
|
+
#if we have too many archive folders
|
191
|
+
while archive_folders.size > @keep do
|
192
|
+
archive_folder = archive_folders.delete_at(archive_folders.size-1)
|
193
|
+
archive_regex = Regexp.new "^#{Regexp.escape(archive_folder)}", "i"
|
194
|
+
|
195
|
+
#remove old archived files
|
196
|
+
archived_files.select { |m| m =~ archive_regex }.each do |file|
|
197
|
+
remote_file_to_remove = remove_initial_slash(File.join(@remote_path, file))
|
198
|
+
@log.debug "Removing old archive file #{remote_file_to_remove}"
|
199
|
+
@connection.delete_object @container, remote_file_to_remove
|
200
|
+
end
|
201
|
+
end
|
202
|
+
|
203
|
+
return run_result(true, "Successully pushed #{count} file(s) to Rackspace Cloud Files container (with version history)")
|
204
|
+
|
205
|
+
end
|
206
|
+
|
207
|
+
end
|
data/lib/plugins/rake.rb
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), 'quartz_plugin')
|
2
|
+
|
3
|
+
class Rake < QuartzPlugin
|
4
|
+
|
5
|
+
@@version_major = 0
|
6
|
+
@@version_minor = 0
|
7
|
+
@@version_revision = 1
|
8
|
+
|
9
|
+
def info
|
10
|
+
{ :uid => "62e3583abfc24f209916c4ff97661fa0", :name => "Rake", :version => get_version }
|
11
|
+
end
|
12
|
+
|
13
|
+
def run(message)
|
14
|
+
@log.debug "Running with #{message}"
|
15
|
+
payload = payload(message)
|
16
|
+
task = payload['task']
|
17
|
+
location = payload['location']
|
18
|
+
params = payload['parameters']
|
19
|
+
@log.info "Rake #{task} in #{location} with params:#{params}"
|
20
|
+
|
21
|
+
begin
|
22
|
+
command = "cd #{location} && bundle exec rake #{task} #{params}"
|
23
|
+
result = run_shell(command)
|
24
|
+
run_result(result[:ok], result[:message])
|
25
|
+
rescue => ex
|
26
|
+
run_result(false, "Failed to run rake due to #{ex}")
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|