cloudsync 2.1.1 → 2.2.0
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/VERSION +1 -1
- data/cloudsync.gemspec +2 -2
- data/lib/cloudsync/backend/base.rb +6 -11
- data/lib/cloudsync/backend/cloudfiles.rb +14 -5
- data/lib/cloudsync/backend/s3.rb +14 -8
- data/lib/cloudsync/backend/sftp.rb +25 -23
- data/lib/cloudsync/sync_manager.rb +14 -16
- metadata +5 -5
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
2.
|
1
|
+
2.2.0
|
data/cloudsync.gemspec
CHANGED
@@ -5,11 +5,11 @@
|
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
7
|
s.name = %q{cloudsync}
|
8
|
-
s.version = "2.
|
8
|
+
s.version = "2.2.0"
|
9
9
|
|
10
10
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
11
|
s.authors = ["Cory Forsyth"]
|
12
|
-
s.date = %q{2010-10-
|
12
|
+
s.date = %q{2010-10-15}
|
13
13
|
s.default_executable = %q{cloudsync}
|
14
14
|
s.description = %q{Sync files between various clouds or sftp servers. Available backends are S3, CloudFiles, and SFTP servers. Can sync, mirror, and prune.}
|
15
15
|
s.email = %q{cory.forsyth@gmail.com}
|
@@ -69,26 +69,21 @@ module Cloudsync
|
|
69
69
|
raise NotImplementedError
|
70
70
|
end
|
71
71
|
|
72
|
-
|
73
|
-
def all_files
|
72
|
+
def files_to_sync(upload_prefix={})
|
74
73
|
raise NotImplementedError
|
75
74
|
end
|
76
75
|
|
77
|
-
|
78
|
-
|
76
|
+
# get_file_from_store
|
77
|
+
def get_file_from_store(file)
|
78
|
+
raise NotImplementedError
|
79
79
|
end
|
80
|
-
|
80
|
+
|
81
81
|
private
|
82
82
|
|
83
83
|
def dry_run?
|
84
84
|
return false unless @sync_manager
|
85
85
|
@sync_manager.dry_run?
|
86
86
|
end
|
87
|
-
|
88
|
-
# get_file_from_store
|
89
|
-
def get_file_from_store(file)
|
90
|
-
raise NotImplementedError
|
91
|
-
end
|
92
87
|
end
|
93
88
|
end
|
94
|
-
end
|
89
|
+
end
|
@@ -49,7 +49,13 @@ module Cloudsync
|
|
49
49
|
container = get_or_create_container(container)
|
50
50
|
objects_from_container(container, upload_prefix).each do |path, hash|
|
51
51
|
next if hash[:content_type] == "application/directory"
|
52
|
-
|
52
|
+
|
53
|
+
file = Cloudsync::File.from_cf_info(container, path, hash, self.to_s)
|
54
|
+
if block_given?
|
55
|
+
yield file
|
56
|
+
else
|
57
|
+
files << file
|
58
|
+
end
|
53
59
|
end
|
54
60
|
files
|
55
61
|
end
|
@@ -75,6 +81,10 @@ module Cloudsync
|
|
75
81
|
$LOGGER.error("Failed to delete file #{file}")
|
76
82
|
end
|
77
83
|
|
84
|
+
def get_file_from_store(file)
|
85
|
+
Cloudsync::File.from_cf_obj( get_obj_from_store(file), self.to_s )
|
86
|
+
end
|
87
|
+
|
78
88
|
private
|
79
89
|
|
80
90
|
def get_or_create_container(container_name)
|
@@ -90,6 +100,8 @@ module Cloudsync
|
|
90
100
|
end
|
91
101
|
|
92
102
|
def objects_from_container(container, upload_prefix)
|
103
|
+
$LOGGER.debug("Getting files from #{container.name}")
|
104
|
+
|
93
105
|
objects = []
|
94
106
|
if upload_prefix[:prefix]
|
95
107
|
container.objects_detail(:path => upload_prefix[:prefix]).collect do |path, hash|
|
@@ -104,6 +116,7 @@ module Cloudsync
|
|
104
116
|
end
|
105
117
|
objects
|
106
118
|
end
|
119
|
+
|
107
120
|
|
108
121
|
def get_obj_from_store(file)
|
109
122
|
@store.container(file.bucket).object(file.upload_path)
|
@@ -111,10 +124,6 @@ module Cloudsync
|
|
111
124
|
nil
|
112
125
|
end
|
113
126
|
|
114
|
-
def get_file_from_store(file)
|
115
|
-
Cloudsync::File.from_cf_obj( get_obj_from_store(file), self.to_s )
|
116
|
-
end
|
117
|
-
|
118
127
|
def get_or_create_obj_from_store(file)
|
119
128
|
container = get_or_create_container(file.container)
|
120
129
|
|
data/lib/cloudsync/backend/s3.rb
CHANGED
@@ -67,18 +67,28 @@ module Cloudsync
|
|
67
67
|
rescue RightAws::AwsError => e
|
68
68
|
$LOGGER.error("Caught error: #{e} trying to delete #{file}")
|
69
69
|
end
|
70
|
-
|
70
|
+
|
71
71
|
def files_to_sync(upload_prefix="")
|
72
72
|
$LOGGER.info("Getting files to sync [#{self}]")
|
73
73
|
|
74
74
|
buckets_to_sync(upload_prefix).inject([]) do |files, bucket|
|
75
75
|
objects_from_bucket(bucket, upload_prefix).collect do |key|
|
76
|
-
|
76
|
+
file = Cloudsync::File.from_s3_obj(key, self.to_s)
|
77
|
+
if block_given?
|
78
|
+
yield file
|
79
|
+
else
|
80
|
+
files << file
|
81
|
+
end
|
77
82
|
end
|
78
83
|
files
|
79
84
|
end
|
80
85
|
end
|
81
86
|
|
87
|
+
# Convenience to grab a single file
|
88
|
+
def get_file_from_store(file)
|
89
|
+
Cloudsync::File.from_s3_obj( get_obj_from_store(file), self.to_s )
|
90
|
+
end
|
91
|
+
|
82
92
|
private
|
83
93
|
|
84
94
|
def buckets_to_sync(upload_prefix="")
|
@@ -91,6 +101,8 @@ module Cloudsync
|
|
91
101
|
end
|
92
102
|
|
93
103
|
def objects_from_bucket(bucket, upload_prefix="")
|
104
|
+
$LOGGER.debug("Getting files from #{bucket}")
|
105
|
+
|
94
106
|
prefix_parts = upload_prefix.split("/")
|
95
107
|
prefix_parts.shift
|
96
108
|
prefix = prefix_parts.join("/")
|
@@ -102,17 +114,11 @@ module Cloudsync
|
|
102
114
|
end
|
103
115
|
end
|
104
116
|
|
105
|
-
# Convenience to grab a single file
|
106
|
-
def get_file_from_store(file)
|
107
|
-
Cloudsync::File.from_s3_obj( get_obj_from_store(file), self.to_s )
|
108
|
-
end
|
109
|
-
|
110
117
|
def get_or_create_obj_from_store(file)
|
111
118
|
@store.bucket(file.bucket, true).key(file.upload_path)
|
112
119
|
end
|
113
120
|
|
114
121
|
def get_obj_from_store(file)
|
115
|
-
$LOGGER.debug("gofs, buck: #{file.bucket}. upload path: #{file.upload_path}")
|
116
122
|
if bucket = @store.bucket(file.bucket)
|
117
123
|
key = bucket.key(file.upload_path)
|
118
124
|
return key if key.exists?
|
@@ -63,19 +63,19 @@ module Cloudsync::Backend
|
|
63
63
|
end
|
64
64
|
end
|
65
65
|
|
66
|
-
def files_to_sync(upload_prefix=
|
66
|
+
def files_to_sync(upload_prefix="")
|
67
67
|
$LOGGER.info("Getting files to sync [#{self}]")
|
68
68
|
files = []
|
69
69
|
Net::SSH.start(@host, @username, :password => @password) do |ssh|
|
70
70
|
ssh.sftp.connect do |sftp|
|
71
71
|
filepaths = sftp.dir.glob(@download_prefix, "**/**").collect {|entry| entry.name}
|
72
72
|
|
73
|
-
|
73
|
+
filepaths.each do |filepath|
|
74
74
|
attrs = sftp.stat!(local_filepath_from_filepath(filepath))
|
75
75
|
next unless attrs.file?
|
76
76
|
|
77
77
|
e_tag = ssh.exec!(md5sum_cmd(filepath)).split(" ").first
|
78
|
-
Cloudsync::File.new \
|
78
|
+
file = Cloudsync::File.new \
|
79
79
|
:path => filepath,
|
80
80
|
:upload_prefix => @upload_prefix,
|
81
81
|
:download_prefix => @download_prefix,
|
@@ -84,31 +84,18 @@ module Cloudsync::Backend
|
|
84
84
|
:e_tag => e_tag,
|
85
85
|
:backend => self.to_s,
|
86
86
|
:backend_type => Cloudsync::Backend::Sftp
|
87
|
-
|
87
|
+
|
88
|
+
if block_given?
|
89
|
+
yield file
|
90
|
+
else
|
91
|
+
files << file
|
92
|
+
end
|
93
|
+
end
|
88
94
|
end
|
89
95
|
end
|
90
96
|
files
|
91
97
|
end
|
92
98
|
|
93
|
-
# def absolute_path(path)
|
94
|
-
# @download_prefix + "/" + path
|
95
|
-
# end
|
96
|
-
|
97
|
-
private
|
98
|
-
|
99
|
-
def md5sum_cmd(filepath)
|
100
|
-
Escape.shell_command(["md5sum","#{local_filepath_from_filepath(filepath)}"])
|
101
|
-
end
|
102
|
-
|
103
|
-
def local_filepath_from_filepath(filepath)
|
104
|
-
stripped_path = filepath.sub(/^#{@upload_prefix}\/?/,"")
|
105
|
-
if @download_prefix
|
106
|
-
"#{@download_prefix}/#{stripped_path}"
|
107
|
-
else
|
108
|
-
stripped_path
|
109
|
-
end
|
110
|
-
end
|
111
|
-
|
112
99
|
# get_file_from_store
|
113
100
|
def get_file_from_store(file)
|
114
101
|
$LOGGER.debug("Looking for local filepath: #{local_filepath_from_filepath(file.full_download_path)}")
|
@@ -137,5 +124,20 @@ module Cloudsync::Backend
|
|
137
124
|
end
|
138
125
|
sftp_file
|
139
126
|
end
|
127
|
+
|
128
|
+
private
|
129
|
+
|
130
|
+
def md5sum_cmd(filepath)
|
131
|
+
Escape.shell_command(["md5sum","#{local_filepath_from_filepath(filepath)}"])
|
132
|
+
end
|
133
|
+
|
134
|
+
def local_filepath_from_filepath(filepath)
|
135
|
+
stripped_path = filepath.sub(/^#{@upload_prefix}\/?/,"")
|
136
|
+
if @download_prefix
|
137
|
+
"#{@download_prefix}/#{stripped_path}"
|
138
|
+
else
|
139
|
+
stripped_path
|
140
|
+
end
|
141
|
+
end
|
140
142
|
end
|
141
143
|
end
|
@@ -74,11 +74,9 @@ module Cloudsync
|
|
74
74
|
|
75
75
|
$LOGGER.info("[SM]: Prune from #{from_backend} to #{to_backend} started at #{prune_start = Time.now}. Dry-run? #{!!dry_run?}")
|
76
76
|
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
to_backend_files.each_with_index do |file, index|
|
77
|
+
index = 1
|
78
|
+
|
79
|
+
to_backend.files_to_sync(from_backend.upload_prefix) do |file|
|
82
80
|
$LOGGER.debug("Checking if file #{file} exists on [#{from_backend}]")
|
83
81
|
if found_file = from_backend.get_file_from_store(file)
|
84
82
|
$LOGGER.debug("Keeping file #{file} because it was found on #{from_backend}.")
|
@@ -90,10 +88,11 @@ module Cloudsync
|
|
90
88
|
to_backend.delete(file)
|
91
89
|
end
|
92
90
|
|
93
|
-
if
|
94
|
-
|
95
|
-
$LOGGER.info("[SM]: Prune: Completed #{index} files (skipped: #{file_stats[:skipped].size}, removed: #{file_stats[:removed].size}). #{last_decile_complete * 10}% complete")
|
91
|
+
if index % 1000 == 0
|
92
|
+
$LOGGER.info("[SM]: Prune: Completed #{index} files (skipped: #{file_stats[:skipped].size}, removed: #{file_stats[:removed].size}).")
|
96
93
|
end
|
94
|
+
|
95
|
+
index += 1
|
97
96
|
end
|
98
97
|
|
99
98
|
$LOGGER.info(["[SM]: Prune from #{from_backend} to #{to_backend} finished at #{Time.now}, took #{Time.now - prune_start}s.",
|
@@ -106,11 +105,9 @@ module Cloudsync
|
|
106
105
|
file_stats = {:copied => [], :skipped => []}
|
107
106
|
$LOGGER.info("[SM]: Sync from #{from_backend} to #{to_backend} started at #{sync_start = Time.now}. Mode: #{mode}. Dry-run? #{!!dry_run?}")
|
108
107
|
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
from_backend_files.each_with_index do |file, index|
|
108
|
+
index = 1
|
109
|
+
|
110
|
+
from_backend.files_to_sync(to_backend.upload_prefix) do |file|
|
114
111
|
if (mode == :sync_all || to_backend.needs_update?(file))
|
115
112
|
file_stats[:copied] << file
|
116
113
|
from_backend.copy(file, to_backend)
|
@@ -119,10 +116,11 @@ module Cloudsync
|
|
119
116
|
$LOGGER.debug("Skipping up-to-date file #{file}")
|
120
117
|
end
|
121
118
|
|
122
|
-
if
|
123
|
-
|
124
|
-
$LOGGER.info("[SM]: Sync from #{from_backend} to #{to_backend}: Completed #{index} files (skipped: #{file_stats[:skipped].size}, copied: #{file_stats[:copied].size}). #{last_decile_complete * 10}% complete")
|
119
|
+
if index % 1000 == 0
|
120
|
+
$LOGGER.info("[SM]: Sync from #{from_backend} to #{to_backend}: Completed #{index} files (skipped: #{file_stats[:skipped].size}, copied: #{file_stats[:copied].size}).")
|
125
121
|
end
|
122
|
+
|
123
|
+
index += 1
|
126
124
|
end
|
127
125
|
|
128
126
|
$LOGGER.info(["[SM]: Sync from #{from_backend} to #{to_backend} finished at #{Time.now}, took #{Time.now - sync_start}s.",
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: cloudsync
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 7
|
5
5
|
prerelease: false
|
6
6
|
segments:
|
7
7
|
- 2
|
8
|
-
-
|
9
|
-
-
|
10
|
-
version: 2.
|
8
|
+
- 2
|
9
|
+
- 0
|
10
|
+
version: 2.2.0
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- Cory Forsyth
|
@@ -15,7 +15,7 @@ autorequire:
|
|
15
15
|
bindir: bin
|
16
16
|
cert_chain: []
|
17
17
|
|
18
|
-
date: 2010-10-
|
18
|
+
date: 2010-10-15 00:00:00 -04:00
|
19
19
|
default_executable: cloudsync
|
20
20
|
dependencies:
|
21
21
|
- !ruby/object:Gem::Dependency
|