cloud_sync 0.1
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.
- data/README.markdown +23 -0
- data/Rakefile +7 -0
- data/cloud_sync.gemspec +30 -0
- data/lib/cloud_sync.rb +36 -0
- data/lib/cloud_sync/archiver.rb +39 -0
- data/lib/cloud_sync/configuration.rb +41 -0
- data/lib/cloud_sync/log.rb +58 -0
- data/lib/cloud_sync/media/base.rb +19 -0
- data/lib/cloud_sync/media/filesystem.rb +27 -0
- data/lib/cloud_sync/media/rackspace.rb +65 -0
- data/lib/cloud_sync/media/s3.rb +60 -0
- data/lib/cloud_sync/resource.rb +50 -0
- data/lib/cloud_sync/synchronizer.rb +16 -0
- metadata +109 -0
data/README.markdown
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
|
2
|
+
|
3
|
+
##The Idea...
|
4
|
+
|
5
|
+
CloudSync.schedule do
|
6
|
+
every 12.hours do
|
7
|
+
archive "/home/user/documents", :to => "filesystem:/mnt/external/snapshots"
|
8
|
+
end
|
9
|
+
|
10
|
+
every 30.minutes do
|
11
|
+
system "mysqldump mydb -u special > /tmp/dump.sql"
|
12
|
+
archive "/tmp/dump.sql", :to => "filesystem:/backups/sql/mydb"
|
13
|
+
archive "/tmp/dump.sql", :to => "s3:/backups/mydb"
|
14
|
+
end
|
15
|
+
|
16
|
+
every 5.minutes do
|
17
|
+
synchronize "/some/directory", :with => "remote:special", :target => "/other/path"
|
18
|
+
end
|
19
|
+
|
20
|
+
on_change "/some/dir" do
|
21
|
+
# ...
|
22
|
+
end
|
23
|
+
end
|
data/Rakefile
ADDED
data/cloud_sync.gemspec
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
spec = Gem::Specification.new do |s|
|
2
|
+
s.name = 'cloud_sync'
|
3
|
+
s.version = '0.1'
|
4
|
+
s.date = '2010-06-16'
|
5
|
+
s.summary = 'A tool for syncing files between clouds'
|
6
|
+
s.email = 'dan.simpson@gmail.com'
|
7
|
+
s.homepage = 'http://github.com/dansimpson/cloud_sync'
|
8
|
+
s.description = 'A tool for syncing files between clouds'
|
9
|
+
s.has_rdoc = true
|
10
|
+
|
11
|
+
s.authors = ['Dan Simpson']
|
12
|
+
s.add_dependency('cloudfiles', '>= 1.4.7')
|
13
|
+
s.add_dependency('aws-s3', '>= 0.6.2')
|
14
|
+
|
15
|
+
s.files = [
|
16
|
+
'README.markdown',
|
17
|
+
'cloud_sync.gemspec',
|
18
|
+
'Rakefile',
|
19
|
+
'lib/cloud_sync.rb',
|
20
|
+
'lib/cloud_sync/archiver.rb',
|
21
|
+
'lib/cloud_sync/configuration.rb',
|
22
|
+
'lib/cloud_sync/log.rb',
|
23
|
+
'lib/cloud_sync/resource.rb',
|
24
|
+
'lib/cloud_sync/synchronizer.rb',
|
25
|
+
'lib/cloud_sync/media/base.rb',
|
26
|
+
'lib/cloud_sync/media/filesystem.rb',
|
27
|
+
'lib/cloud_sync/media/rackspace.rb',
|
28
|
+
'lib/cloud_sync/media/s3.rb'
|
29
|
+
]
|
30
|
+
end
|
data/lib/cloud_sync.rb
ADDED
@@ -0,0 +1,36 @@
|
|
1
|
+
$:.unshift File.dirname(__FILE__)
|
2
|
+
|
3
|
+
require "rubygems"
|
4
|
+
require "cloudfiles"
|
5
|
+
require "aws/s3"
|
6
|
+
require "pp"
|
7
|
+
|
8
|
+
module CloudSync
|
9
|
+
|
10
|
+
VERSION = 0.1
|
11
|
+
|
12
|
+
def self.copy source, destination
|
13
|
+
Resource.new(source).copy(Resource.new(destination))
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.archive source, destination
|
17
|
+
Archiver.new(source, destination).archive
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.synchronize source, destination
|
21
|
+
Synchronizer.new(source).synchronize(destination)
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
25
|
+
|
26
|
+
#archivers
|
27
|
+
require "cloud_sync/configuration"
|
28
|
+
require "cloud_sync/media/base"
|
29
|
+
require "cloud_sync/media/filesystem"
|
30
|
+
require "cloud_sync/media/rackspace"
|
31
|
+
require "cloud_sync/media/s3"
|
32
|
+
require "cloud_sync/resource"
|
33
|
+
require "cloud_sync/log"
|
34
|
+
require "cloud_sync/archiver"
|
35
|
+
require "cloud_sync/synchronizer"
|
36
|
+
|
@@ -0,0 +1,39 @@
|
|
1
|
+
module CloudSync
|
2
|
+
class Archiver
|
3
|
+
|
4
|
+
attr_accessor :source, :destination
|
5
|
+
|
6
|
+
def initialize source, destination
|
7
|
+
@source = Resource.new source
|
8
|
+
@destination = Resource.new destination
|
9
|
+
end
|
10
|
+
|
11
|
+
def archive
|
12
|
+
|
13
|
+
unless source
|
14
|
+
return Log.error "Invalid syntax for resource #{source}"
|
15
|
+
end
|
16
|
+
|
17
|
+
unless destination
|
18
|
+
return Log.error "Invalid syntax for resource #{destination}"
|
19
|
+
end
|
20
|
+
|
21
|
+
tar = Resource.new("filesystem:#{archive_path}")
|
22
|
+
system "tar czvf #{archive_path} #{source.path}"
|
23
|
+
destination.istream("/home/dan/moo.tgz") << tar.ostream
|
24
|
+
system "rm #{archive_path}"
|
25
|
+
|
26
|
+
end
|
27
|
+
|
28
|
+
protected
|
29
|
+
|
30
|
+
def archive_path
|
31
|
+
"/tmp/#{archive_name}"
|
32
|
+
end
|
33
|
+
|
34
|
+
def archive_name
|
35
|
+
@archive_name ||= "#{File.basename(source.path)}.#{Time.now.strftime("%Y%j%H%M%S")}.tgz"
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
module CloudSync
|
2
|
+
|
3
|
+
class Configuration
|
4
|
+
|
5
|
+
@map = {
|
6
|
+
}
|
7
|
+
|
8
|
+
def self.get key
|
9
|
+
@map[key.to_sym]
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.put key, val
|
13
|
+
@map[key.to_sym] = val
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.method_missing method, *args
|
17
|
+
if method.to_s =~ /=$/
|
18
|
+
return put method.to_s.chop, args.first
|
19
|
+
else
|
20
|
+
return get method
|
21
|
+
end
|
22
|
+
super
|
23
|
+
end
|
24
|
+
|
25
|
+
def self.load file
|
26
|
+
begin
|
27
|
+
configure YAML.load_file(file)
|
28
|
+
rescue
|
29
|
+
raise IOError, "#{file} could not be loaded."
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def self.configure map
|
34
|
+
map.each do |k,v|
|
35
|
+
put k, v
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
end
|
40
|
+
|
41
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
module CloudSync
|
2
|
+
|
3
|
+
class Log
|
4
|
+
|
5
|
+
@threshhold = 1
|
6
|
+
|
7
|
+
@descriptor = {
|
8
|
+
1 => "DBUG",
|
9
|
+
2 => "WARN",
|
10
|
+
3 => "INFO",
|
11
|
+
4 => "ERROR"
|
12
|
+
}
|
13
|
+
|
14
|
+
def self.debug msg
|
15
|
+
log(msg,1) if @threshhold <= 1
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.warning msg
|
19
|
+
log(msg,2) if @threshhold <= 2
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.info msg
|
23
|
+
log(msg,3) if @threshhold <= 3
|
24
|
+
end
|
25
|
+
|
26
|
+
def self.error msg
|
27
|
+
log(msg,4) if @threshhold <= 4
|
28
|
+
end
|
29
|
+
|
30
|
+
def self.debug!
|
31
|
+
@threshhold = 1
|
32
|
+
end
|
33
|
+
|
34
|
+
def self.warn!
|
35
|
+
@threshhold = 2
|
36
|
+
end
|
37
|
+
|
38
|
+
def self.info!
|
39
|
+
@threshhold = 3
|
40
|
+
end
|
41
|
+
|
42
|
+
def self.shutthefuckup!
|
43
|
+
@threshhold = 4
|
44
|
+
end
|
45
|
+
|
46
|
+
def self.is_debug?
|
47
|
+
@threshhold == 1
|
48
|
+
end
|
49
|
+
|
50
|
+
private
|
51
|
+
|
52
|
+
def self.log msg, level
|
53
|
+
puts "[#{@descriptor[level]}] #{Time.now} - #{msg}"
|
54
|
+
end
|
55
|
+
|
56
|
+
end
|
57
|
+
|
58
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module CloudSync
|
2
|
+
module Media
|
3
|
+
class Base
|
4
|
+
|
5
|
+
def exists? path
|
6
|
+
raise "#{self.class}.exists? not implemented"
|
7
|
+
end
|
8
|
+
|
9
|
+
def writer path
|
10
|
+
raise "#{self.class}.writer not implemented"
|
11
|
+
end
|
12
|
+
|
13
|
+
def reader path
|
14
|
+
raise "#{self.class}.reader not implemented"
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module CloudSync
|
2
|
+
module Media
|
3
|
+
class Filesystem < Base
|
4
|
+
|
5
|
+
def exists? path
|
6
|
+
File.exists? path
|
7
|
+
end
|
8
|
+
|
9
|
+
def is_container? path
|
10
|
+
File.directory? path
|
11
|
+
end
|
12
|
+
|
13
|
+
def is_object? path
|
14
|
+
File.exists? path
|
15
|
+
end
|
16
|
+
|
17
|
+
def reader path
|
18
|
+
File.open(path, "r")
|
19
|
+
end
|
20
|
+
|
21
|
+
def writer path
|
22
|
+
File.open(path, "w")
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
module CloudFiles
|
2
|
+
class StorageObject
|
3
|
+
def read
|
4
|
+
data
|
5
|
+
end
|
6
|
+
end
|
7
|
+
end
|
8
|
+
|
9
|
+
module CloudSync
|
10
|
+
module Media
|
11
|
+
class Rackspace < Base
|
12
|
+
|
13
|
+
def initialize
|
14
|
+
raise "rackspace_user not set" unless Configuration.rackspace_user
|
15
|
+
raise "rackspace_key not set" unless Configuration.rackspace_key
|
16
|
+
@conn = CloudFiles::Connection.new(Configuration.rackspace_user, Configuration.rackspace_key)
|
17
|
+
end
|
18
|
+
|
19
|
+
def list container
|
20
|
+
@conn.container(container).objects
|
21
|
+
end
|
22
|
+
|
23
|
+
def exists? path
|
24
|
+
container, object = parse path
|
25
|
+
object_exists?(container, object)
|
26
|
+
end
|
27
|
+
|
28
|
+
def reader path
|
29
|
+
container, object = parse path
|
30
|
+
get_object(container, object)
|
31
|
+
end
|
32
|
+
|
33
|
+
def writer path
|
34
|
+
container, object = parse path
|
35
|
+
get_object(container, object)
|
36
|
+
end
|
37
|
+
|
38
|
+
protected
|
39
|
+
|
40
|
+
def parse path
|
41
|
+
parts = path.split "/"
|
42
|
+
container = parts.shift
|
43
|
+
[container, parts.join("/")]
|
44
|
+
end
|
45
|
+
|
46
|
+
def container_exists? container
|
47
|
+
@conn.container_exists?(container)
|
48
|
+
end
|
49
|
+
|
50
|
+
def object_exists? container, object
|
51
|
+
container_exists?(container) && @conn.container(container).object_exists?(object)
|
52
|
+
end
|
53
|
+
|
54
|
+
def get_container container
|
55
|
+
container_exists?(container) ? @conn.container(container) : @conn.create_container(container)
|
56
|
+
end
|
57
|
+
|
58
|
+
def get_object container, object
|
59
|
+
tmp = get_container(container)
|
60
|
+
tmp.object_exists?(object) ? tmp.object(object) : tmp.create_object(object, true)
|
61
|
+
end
|
62
|
+
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
module CloudSync
|
2
|
+
module Media
|
3
|
+
class S3 < Base
|
4
|
+
|
5
|
+
def initialize
|
6
|
+
raise "s3_access_key not set" unless Configuration.s3_access_key
|
7
|
+
raise "s3_secret_key not set" unless Configuration.s3_secret_key
|
8
|
+
|
9
|
+
AWS::S3::Base.establish_connection!({
|
10
|
+
:access_key_id => Configuration.s3_access_key,
|
11
|
+
:secret_access_key => Configuration.s3_secret_key
|
12
|
+
})
|
13
|
+
end
|
14
|
+
|
15
|
+
def exists? path
|
16
|
+
bucket, object = parse path
|
17
|
+
object_exists?(bucket, object)
|
18
|
+
end
|
19
|
+
|
20
|
+
def reader path
|
21
|
+
bucket, object = parse path
|
22
|
+
get_object(bucket, object)
|
23
|
+
end
|
24
|
+
|
25
|
+
def writer path
|
26
|
+
bucket, object = parse path
|
27
|
+
get_object(bucket, object)
|
28
|
+
end
|
29
|
+
|
30
|
+
protected
|
31
|
+
|
32
|
+
def parse path
|
33
|
+
parts = path.split "/"
|
34
|
+
bucket = parts.shift
|
35
|
+
[bucket, parts.join("/")]
|
36
|
+
end
|
37
|
+
|
38
|
+
def bucket_exists? bucket
|
39
|
+
AWS::S3::Bucket.find(bucket) rescue false
|
40
|
+
end
|
41
|
+
|
42
|
+
def object_exists? bucket, object
|
43
|
+
AWS::S3::S3Object.exists?(object, bucket)
|
44
|
+
end
|
45
|
+
|
46
|
+
def get_bucket bucket
|
47
|
+
unless bucket_exists?(bucket)
|
48
|
+
AWS::S3::Bucket.create(bucket)
|
49
|
+
end
|
50
|
+
AWS::S3::Bucket.find(bucket)
|
51
|
+
end
|
52
|
+
|
53
|
+
def get_object bucket, object
|
54
|
+
object_exists?(bucket, object) ? AWS::S3::S3Object.find(object, bucket) : nil
|
55
|
+
end
|
56
|
+
|
57
|
+
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
module CloudSync
|
2
|
+
|
3
|
+
MediaTypes = {
|
4
|
+
:filesystem => CloudSync::Media::Filesystem,
|
5
|
+
:rackspace => CloudSync::Media::Rackspace,
|
6
|
+
:s3 => CloudSync::Media::S3
|
7
|
+
}
|
8
|
+
|
9
|
+
class Resource
|
10
|
+
|
11
|
+
attr_accessor :medium, :path, :type, :uri
|
12
|
+
|
13
|
+
def initialize uri
|
14
|
+
raise "Invalid resource string #{uri}" unless uri =~ /\w+:/
|
15
|
+
|
16
|
+
@uri = uri
|
17
|
+
@type, @path = uri.split(":")
|
18
|
+
@type = @type.to_sym
|
19
|
+
|
20
|
+
raise "Invalid medium type: #{@type}" unless MediaTypes.has_key?(@type)
|
21
|
+
|
22
|
+
@medium = MediaTypes[@type].new
|
23
|
+
end
|
24
|
+
|
25
|
+
def exists?
|
26
|
+
@medium.exists?(@path)
|
27
|
+
end
|
28
|
+
|
29
|
+
def copy to
|
30
|
+
|
31
|
+
raise "Resource #{@uri} does not exist" unless exists?
|
32
|
+
|
33
|
+
input = reader
|
34
|
+
output = to.writer
|
35
|
+
output.write(input.read)
|
36
|
+
|
37
|
+
input.close if input.respond_to? :close
|
38
|
+
output.close if output.respond_to? :close
|
39
|
+
end
|
40
|
+
|
41
|
+
def writer
|
42
|
+
@medium.writer @path
|
43
|
+
end
|
44
|
+
|
45
|
+
def reader
|
46
|
+
@medium.reader @path
|
47
|
+
end
|
48
|
+
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module CloudSync
|
2
|
+
class Synchronizer
|
3
|
+
|
4
|
+
attr_accessor :source, :destination
|
5
|
+
|
6
|
+
def initialize source, destination
|
7
|
+
@source = Resource.new source
|
8
|
+
@destination = Resource.new destination
|
9
|
+
end
|
10
|
+
|
11
|
+
def synchronize
|
12
|
+
raise "Not implemented"
|
13
|
+
end
|
14
|
+
|
15
|
+
end
|
16
|
+
end
|
metadata
ADDED
@@ -0,0 +1,109 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: cloud_sync
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
hash: 9
|
5
|
+
prerelease: false
|
6
|
+
segments:
|
7
|
+
- 0
|
8
|
+
- 1
|
9
|
+
version: "0.1"
|
10
|
+
platform: ruby
|
11
|
+
authors:
|
12
|
+
- Dan Simpson
|
13
|
+
autorequire:
|
14
|
+
bindir: bin
|
15
|
+
cert_chain: []
|
16
|
+
|
17
|
+
date: 2010-06-16 00:00:00 -07:00
|
18
|
+
default_executable:
|
19
|
+
dependencies:
|
20
|
+
- !ruby/object:Gem::Dependency
|
21
|
+
name: cloudfiles
|
22
|
+
prerelease: false
|
23
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
24
|
+
none: false
|
25
|
+
requirements:
|
26
|
+
- - ">="
|
27
|
+
- !ruby/object:Gem::Version
|
28
|
+
hash: 9
|
29
|
+
segments:
|
30
|
+
- 1
|
31
|
+
- 4
|
32
|
+
- 7
|
33
|
+
version: 1.4.7
|
34
|
+
type: :runtime
|
35
|
+
version_requirements: *id001
|
36
|
+
- !ruby/object:Gem::Dependency
|
37
|
+
name: aws-s3
|
38
|
+
prerelease: false
|
39
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
40
|
+
none: false
|
41
|
+
requirements:
|
42
|
+
- - ">="
|
43
|
+
- !ruby/object:Gem::Version
|
44
|
+
hash: 3
|
45
|
+
segments:
|
46
|
+
- 0
|
47
|
+
- 6
|
48
|
+
- 2
|
49
|
+
version: 0.6.2
|
50
|
+
type: :runtime
|
51
|
+
version_requirements: *id002
|
52
|
+
description: A tool for syncing files between clouds
|
53
|
+
email: dan.simpson@gmail.com
|
54
|
+
executables: []
|
55
|
+
|
56
|
+
extensions: []
|
57
|
+
|
58
|
+
extra_rdoc_files: []
|
59
|
+
|
60
|
+
files:
|
61
|
+
- README.markdown
|
62
|
+
- cloud_sync.gemspec
|
63
|
+
- Rakefile
|
64
|
+
- lib/cloud_sync.rb
|
65
|
+
- lib/cloud_sync/archiver.rb
|
66
|
+
- lib/cloud_sync/configuration.rb
|
67
|
+
- lib/cloud_sync/log.rb
|
68
|
+
- lib/cloud_sync/resource.rb
|
69
|
+
- lib/cloud_sync/synchronizer.rb
|
70
|
+
- lib/cloud_sync/media/base.rb
|
71
|
+
- lib/cloud_sync/media/filesystem.rb
|
72
|
+
- lib/cloud_sync/media/rackspace.rb
|
73
|
+
- lib/cloud_sync/media/s3.rb
|
74
|
+
has_rdoc: true
|
75
|
+
homepage: http://github.com/dansimpson/cloud_sync
|
76
|
+
licenses: []
|
77
|
+
|
78
|
+
post_install_message:
|
79
|
+
rdoc_options: []
|
80
|
+
|
81
|
+
require_paths:
|
82
|
+
- lib
|
83
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
84
|
+
none: false
|
85
|
+
requirements:
|
86
|
+
- - ">="
|
87
|
+
- !ruby/object:Gem::Version
|
88
|
+
hash: 3
|
89
|
+
segments:
|
90
|
+
- 0
|
91
|
+
version: "0"
|
92
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
93
|
+
none: false
|
94
|
+
requirements:
|
95
|
+
- - ">="
|
96
|
+
- !ruby/object:Gem::Version
|
97
|
+
hash: 3
|
98
|
+
segments:
|
99
|
+
- 0
|
100
|
+
version: "0"
|
101
|
+
requirements: []
|
102
|
+
|
103
|
+
rubyforge_project:
|
104
|
+
rubygems_version: 1.3.7
|
105
|
+
signing_key:
|
106
|
+
specification_version: 3
|
107
|
+
summary: A tool for syncing files between clouds
|
108
|
+
test_files: []
|
109
|
+
|