jmstacey-cfbackup 0.5.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/CHANGELOG.markdown +23 -0
- data/LICENSE +14 -0
- data/README.markdown +33 -0
- data/Rakefile +59 -0
- data/VERSION.yml +4 -0
- data/bin/cfbackup +6 -0
- data/lib/OptCFBackup.rb +85 -0
- data/lib/cfbackup.rb +162 -0
- data/test/cfbackup_test.rb +7 -0
- data/test/data.txt +11 -0
- data/test/test_helper.rb +10 -0
- metadata +74 -0
data/CHANGELOG.markdown
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
CFBackup ChangeLog
|
2
|
+
==================
|
3
|
+
|
4
|
+
0.5.0 2009-04-18
|
5
|
+
-----------------
|
6
|
+
|
7
|
+
* Conversion to a Ruby Gem
|
8
|
+
* cfbackup put in bin so that it can be called from anywhere
|
9
|
+
* Looks in a few standard locations for the config file
|
10
|
+
* Unit test framework prepped
|
11
|
+
* Cloud Files API has been moved to a separate repository and made available as a gem
|
12
|
+
* Most doc files converted to markdown
|
13
|
+
|
14
|
+
0.0.4 2009-04-11
|
15
|
+
-----------------
|
16
|
+
|
17
|
+
* Pipe data straight to container
|
18
|
+
* Specify remote path or file name
|
19
|
+
|
20
|
+
0.0.3 2009-04-08
|
21
|
+
-------------------
|
22
|
+
|
23
|
+
* Initial release to public
|
data/LICENSE
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
Copyright (C) 2009 Jon Stacey
|
2
|
+
|
3
|
+
This program is free software: you can redistribute it and/or modify
|
4
|
+
it under the terms of the GNU General Public License as published by
|
5
|
+
the Free Software Foundation, either version 3 of the License, or
|
6
|
+
(at your option) any later version.
|
7
|
+
|
8
|
+
This program is distributed in the hope that it will be useful,
|
9
|
+
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
10
|
+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
11
|
+
GNU General Public License for more details.
|
12
|
+
|
13
|
+
You should have received a copy of the GNU General Public License
|
14
|
+
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
data/README.markdown
ADDED
@@ -0,0 +1,33 @@
|
|
1
|
+
CFBackup
|
2
|
+
=========
|
3
|
+
|
4
|
+
CFBackup is a small ruby program that transfers files or directories from the
|
5
|
+
local machine to a Cloud Files container. It is meant to serve as a useful tool
|
6
|
+
for automated backups.
|
7
|
+
|
8
|
+
Features
|
9
|
+
-----------
|
10
|
+
|
11
|
+
* Backup a single file or directory (recursion uses pseudo directories)
|
12
|
+
* Pipe data straight to container
|
13
|
+
* Free transfers over local Rackspace network for Slicehost/Cloud Server
|
14
|
+
customers in DFW1 datacenter
|
15
|
+
|
16
|
+
Requirements
|
17
|
+
--------------
|
18
|
+
|
19
|
+
TODO: Complete this area
|
20
|
+
* ruby-cloudfiles
|
21
|
+
|
22
|
+
Install
|
23
|
+
-----------
|
24
|
+
|
25
|
+
* gem sources -a http://gems.github.com
|
26
|
+
* sudo gem install jmstacey-cfbackup
|
27
|
+
|
28
|
+
Depending on what Operating System you're using, you may be required to install libs that are outside of the gem world.
|
29
|
+
|
30
|
+
Copyright
|
31
|
+
------------
|
32
|
+
|
33
|
+
Copyright (c) 2009 Jon Stacey. See LICENSE for details.
|
data/Rakefile
ADDED
@@ -0,0 +1,59 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rake'
|
3
|
+
|
4
|
+
begin
|
5
|
+
require 'jeweler'
|
6
|
+
Jeweler::Tasks.new do |gem|
|
7
|
+
gem.name = "cfbackup"
|
8
|
+
gem.summary = %Q{TODO}
|
9
|
+
gem.email = "jon@jonsview.com"
|
10
|
+
gem.homepage = "http://github.com/jmstacey/cfbackup"
|
11
|
+
gem.authors = ["Jon Stacey"]
|
12
|
+
|
13
|
+
# dependencies
|
14
|
+
gem.add_dependency('jmstacey-ruby-cloudfiles', '>=1.3.3')
|
15
|
+
|
16
|
+
# gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
|
17
|
+
end
|
18
|
+
rescue LoadError
|
19
|
+
puts "Jeweler not available. Install it with: sudo gem install technicalpickles-jeweler -s http://gems.github.com"
|
20
|
+
end
|
21
|
+
|
22
|
+
require 'rake/testtask'
|
23
|
+
Rake::TestTask.new(:test) do |test|
|
24
|
+
test.libs << 'lib' << 'test'
|
25
|
+
test.pattern = 'test/**/*_test.rb'
|
26
|
+
test.verbose = true
|
27
|
+
end
|
28
|
+
|
29
|
+
begin
|
30
|
+
require 'rcov/rcovtask'
|
31
|
+
Rcov::RcovTask.new do |test|
|
32
|
+
test.libs << 'test'
|
33
|
+
test.pattern = 'test/**/*_test.rb'
|
34
|
+
test.verbose = true
|
35
|
+
end
|
36
|
+
rescue LoadError
|
37
|
+
task :rcov do
|
38
|
+
abort "RCov is not available. In order to run rcov, you must: sudo gem install spicycode-rcov"
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
|
43
|
+
task :default => :test
|
44
|
+
|
45
|
+
require 'rake/rdoctask'
|
46
|
+
Rake::RDocTask.new do |rdoc|
|
47
|
+
if File.exist?('VERSION.yml')
|
48
|
+
config = YAML.load(File.read('VERSION.yml'))
|
49
|
+
version = "#{config[:major]}.#{config[:minor]}.#{config[:patch]}"
|
50
|
+
else
|
51
|
+
version = ""
|
52
|
+
end
|
53
|
+
|
54
|
+
rdoc.rdoc_dir = 'rdoc'
|
55
|
+
rdoc.title = "cfbackup #{version}"
|
56
|
+
rdoc.rdoc_files.include('README*')
|
57
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
58
|
+
end
|
59
|
+
|
data/VERSION.yml
ADDED
data/bin/cfbackup
ADDED
data/lib/OptCFBackup.rb
ADDED
@@ -0,0 +1,85 @@
|
|
1
|
+
require 'optparse'
|
2
|
+
require 'ostruct'
|
3
|
+
|
4
|
+
class OptCFBackup
|
5
|
+
|
6
|
+
# Options structure
|
7
|
+
attr_reader :options
|
8
|
+
|
9
|
+
# Ussage message
|
10
|
+
attr_reader :banner
|
11
|
+
|
12
|
+
# Initializes object with command line arguments passed
|
13
|
+
def initialize(args)
|
14
|
+
|
15
|
+
@banner = "Usage: cfbackup.rb [options] --pipe_data|--local_path PATH --container CONTAINER"
|
16
|
+
|
17
|
+
@options = OpenStruct.new
|
18
|
+
self.options.config = ['~/.cfconfig.yml', './cfconfig.yml', '/etc/cfconfig.yml']
|
19
|
+
self.options.pipe_data = false
|
20
|
+
self.options.show_ver = false
|
21
|
+
self.options.recursive = false
|
22
|
+
self.options.restore = false
|
23
|
+
self.options.local_net = false
|
24
|
+
self.options.container = ''
|
25
|
+
self.options.local_path = ''
|
26
|
+
self.options.remote_path = ''
|
27
|
+
self.options.verbose = false;
|
28
|
+
|
29
|
+
opts = OptionParser.new do |opts|
|
30
|
+
opts.banner = self.banner
|
31
|
+
|
32
|
+
opts.on("--pipe_data", "Pipe data from another application and stream it to Cloud Files") do |pipe|
|
33
|
+
self.options.pipe_data = pipe
|
34
|
+
end
|
35
|
+
|
36
|
+
opts.on("-r", "--recursive", "Traverse local path recursivley") do |recursive|
|
37
|
+
self.options.recursive = recursive
|
38
|
+
end
|
39
|
+
|
40
|
+
opts.on("-v", "--verbose", "Run verbosely") do |verbose|
|
41
|
+
self.options.verbose = verbose
|
42
|
+
end
|
43
|
+
|
44
|
+
opts.on("--local_path LOCAL_PATH", "Local path or file") do |local_path|
|
45
|
+
self.options.local_path = local_path
|
46
|
+
end
|
47
|
+
|
48
|
+
opts.on("--container CONTAINER", "Cloud Files container name") do |name|
|
49
|
+
self.options.container, self.options.remote_path = name.split(":", 2)
|
50
|
+
clean_remote_path unless (self.options.remote_path.nil?)
|
51
|
+
end
|
52
|
+
|
53
|
+
opts.on("--restore", "Restore files to local path") do |restore|
|
54
|
+
self.options.restore = restore
|
55
|
+
end
|
56
|
+
|
57
|
+
opts.on("--version", "Show current version") do |version|
|
58
|
+
self.options.show_ver = version
|
59
|
+
end
|
60
|
+
|
61
|
+
opts.on("--config_file PATH", "Use specified config file, rather than the default") do |config|
|
62
|
+
self.options.config << config
|
63
|
+
end
|
64
|
+
|
65
|
+
opts.on("--local_net", "Use unmetered connection in DFW1 (only applicable to Slicehost or Mosso Cloud Server customers)") do |local_net|
|
66
|
+
self.options.local_net = local_net
|
67
|
+
end
|
68
|
+
|
69
|
+
end
|
70
|
+
|
71
|
+
opts.parse!(args)
|
72
|
+
|
73
|
+
end # parse()
|
74
|
+
|
75
|
+
private
|
76
|
+
|
77
|
+
def clean_remote_path
|
78
|
+
if self.options.remote_path[0,1] == "/"
|
79
|
+
self.options.remote_path.slice!(0)
|
80
|
+
end
|
81
|
+
# Won't work for piped data. Might result in "text.txt/"
|
82
|
+
# self.options.remote_path = self.options.remote_path + "/" unless (self.options.remote_path[-1,1] == "/")
|
83
|
+
end
|
84
|
+
|
85
|
+
end # class OptCFBackup
|
data/lib/cfbackup.rb
ADDED
@@ -0,0 +1,162 @@
|
|
1
|
+
# CFBackup, a small utility script to backup files to Mosso Cloud Files
|
2
|
+
# Copyright (C) 2009 Jon Stacey
|
3
|
+
#
|
4
|
+
# This program is free software: you can redistribute it and/or modify
|
5
|
+
# it under the terms of the GNU General Public License as published by
|
6
|
+
# the Free Software Foundation, either version 3 of the License, or
|
7
|
+
# (at your option) any later version.
|
8
|
+
#
|
9
|
+
# This program is distributed in the hope that it will be useful,
|
10
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
11
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
12
|
+
# GNU General Public License for more details.
|
13
|
+
#
|
14
|
+
# You should have received a copy of the GNU General Public License
|
15
|
+
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
16
|
+
|
17
|
+
require 'rubygems'
|
18
|
+
require 'cloudfiles'
|
19
|
+
require 'OptCFBackup'
|
20
|
+
require 'yaml'
|
21
|
+
|
22
|
+
class CFBackup
|
23
|
+
|
24
|
+
def initialize(args)
|
25
|
+
@opts = OptCFBackup.new(args)
|
26
|
+
|
27
|
+
# Special case if the version is requested
|
28
|
+
if @opts.options.show_ver
|
29
|
+
version_file = File.join(File.dirname(__FILE__), '..', 'VERSION.yml')
|
30
|
+
config = YAML::load(File.open(version_file))
|
31
|
+
show_error("CFBackup v#{config[:major]}.#{config[:minor]}.#{config[:patch]}")
|
32
|
+
end
|
33
|
+
|
34
|
+
# Locate and load config file
|
35
|
+
@opts.options.config.each do |path|
|
36
|
+
if (File.exist?(path))
|
37
|
+
@conf = YAML::load(File.open(path))
|
38
|
+
break
|
39
|
+
end
|
40
|
+
end
|
41
|
+
show_error('Error: Unable to locate config file.') unless (@conf != nil)
|
42
|
+
|
43
|
+
end # initialize()
|
44
|
+
|
45
|
+
def run
|
46
|
+
|
47
|
+
show_error() unless (@opts.options.container != "")
|
48
|
+
|
49
|
+
if @opts.options.pipe_data
|
50
|
+
prep_container
|
51
|
+
run_piped_data
|
52
|
+
elsif @opts.options.local_path != ""
|
53
|
+
prep_container
|
54
|
+
|
55
|
+
if @opts.options.restore
|
56
|
+
run_restore
|
57
|
+
else
|
58
|
+
run_backup
|
59
|
+
end
|
60
|
+
else
|
61
|
+
show_error()
|
62
|
+
end
|
63
|
+
|
64
|
+
end # run()
|
65
|
+
|
66
|
+
private
|
67
|
+
|
68
|
+
def prep_container
|
69
|
+
|
70
|
+
# Establish connection
|
71
|
+
show_verbose "Establishing connection...", false
|
72
|
+
@cf = CloudFiles::Connection.new(@conf["username"], @conf["api_key"]);
|
73
|
+
show_verbose " done."
|
74
|
+
|
75
|
+
# Special option for Slicehost customers in DFW datacenter
|
76
|
+
if @opts.options.local_net
|
77
|
+
@cf.storagehost = 'snet-storage.clouddrive.com'
|
78
|
+
end
|
79
|
+
|
80
|
+
# Check for the container. If it doesn't exist, create it.
|
81
|
+
unless @cf.container_exists?(@opts.options.container)
|
82
|
+
show_verbose "Conainer '#{@opts.options.container}' does not exist. Creating it...", false
|
83
|
+
@cf.create_container(@opts.options.container)
|
84
|
+
show_verbose " done."
|
85
|
+
end
|
86
|
+
|
87
|
+
@container = @cf.container(@opts.options.container)
|
88
|
+
|
89
|
+
end # prepConnection()
|
90
|
+
|
91
|
+
def run_piped_data
|
92
|
+
puts "Warning: 5GB per stream cap"
|
93
|
+
object = @container.create_object(@opts.options.remote_path, true)
|
94
|
+
object.write("STDIN")
|
95
|
+
end
|
96
|
+
|
97
|
+
def run_backup
|
98
|
+
|
99
|
+
path = @opts.options.local_path
|
100
|
+
|
101
|
+
if FileTest::file?(path)
|
102
|
+
Dir.chdir(File::dirname(path))
|
103
|
+
glob_options = File.join(File::basename(path))
|
104
|
+
elsif @opts.options.recursive
|
105
|
+
Dir.chdir(path)
|
106
|
+
glob_options = File.join("**", "*")
|
107
|
+
else
|
108
|
+
Dir.chdir(path)
|
109
|
+
glob_options = File.join("*")
|
110
|
+
end
|
111
|
+
files = Dir.glob(glob_options)
|
112
|
+
|
113
|
+
# Upload file(s)
|
114
|
+
files.each do |file|
|
115
|
+
file = file.sub(/\.\//, '')
|
116
|
+
if file == "" || file[0,1] == "." || FileTest.directory?(file)
|
117
|
+
next
|
118
|
+
end
|
119
|
+
|
120
|
+
show_verbose "Uploading #{file}...", false
|
121
|
+
object = @container.create_object(file, true)
|
122
|
+
object.load_from_filename(file)
|
123
|
+
show_verbose " done."
|
124
|
+
end # files.each
|
125
|
+
|
126
|
+
end # run_backup()
|
127
|
+
|
128
|
+
def run_restore
|
129
|
+
|
130
|
+
# TODO: Implement run_restore
|
131
|
+
# We have to do a bit of fancy footwork to make directories work
|
132
|
+
puts "Oops! Restore hasn't been implemented yet. Help me out and submit a patch :-)"
|
133
|
+
|
134
|
+
end # run_restore()
|
135
|
+
|
136
|
+
# Shows given message if verbose output is turned on
|
137
|
+
def show_verbose(message, line_break = true)
|
138
|
+
|
139
|
+
unless !@opts.options.verbose
|
140
|
+
if line_break
|
141
|
+
puts message
|
142
|
+
else
|
143
|
+
print message
|
144
|
+
end
|
145
|
+
|
146
|
+
$stdout.flush
|
147
|
+
end
|
148
|
+
|
149
|
+
end # show_verbose()
|
150
|
+
|
151
|
+
# Show error message, banner and exit
|
152
|
+
def show_error(message = '')
|
153
|
+
puts message
|
154
|
+
puts @opts.banner
|
155
|
+
exit
|
156
|
+
end # show_error()
|
157
|
+
|
158
|
+
def parse_container_path(container)
|
159
|
+
# Split based on :
|
160
|
+
end # parse_container_path()
|
161
|
+
|
162
|
+
end # class CFBackup
|
data/test/data.txt
ADDED
data/test/test_helper.rb
ADDED
metadata
ADDED
@@ -0,0 +1,74 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: jmstacey-cfbackup
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.5.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Jon Stacey
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2009-04-19 00:00:00 -07:00
|
13
|
+
default_executable: cfbackup
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: jmstacey-ruby-cloudfiles
|
17
|
+
type: :runtime
|
18
|
+
version_requirement:
|
19
|
+
version_requirements: !ruby/object:Gem::Requirement
|
20
|
+
requirements:
|
21
|
+
- - ">="
|
22
|
+
- !ruby/object:Gem::Version
|
23
|
+
version: 1.3.3
|
24
|
+
version:
|
25
|
+
description:
|
26
|
+
email: jon@jonsview.com
|
27
|
+
executables:
|
28
|
+
- cfbackup
|
29
|
+
extensions: []
|
30
|
+
|
31
|
+
extra_rdoc_files:
|
32
|
+
- LICENSE
|
33
|
+
- README.markdown
|
34
|
+
files:
|
35
|
+
- CHANGELOG.markdown
|
36
|
+
- LICENSE
|
37
|
+
- README.markdown
|
38
|
+
- Rakefile
|
39
|
+
- VERSION.yml
|
40
|
+
- bin/cfbackup
|
41
|
+
- lib/OptCFBackup.rb
|
42
|
+
- lib/cfbackup.rb
|
43
|
+
- test/cfbackup_test.rb
|
44
|
+
- test/data.txt
|
45
|
+
- test/test_helper.rb
|
46
|
+
has_rdoc: true
|
47
|
+
homepage: http://github.com/jmstacey/cfbackup
|
48
|
+
post_install_message:
|
49
|
+
rdoc_options:
|
50
|
+
- --charset=UTF-8
|
51
|
+
require_paths:
|
52
|
+
- lib
|
53
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
54
|
+
requirements:
|
55
|
+
- - ">="
|
56
|
+
- !ruby/object:Gem::Version
|
57
|
+
version: "0"
|
58
|
+
version:
|
59
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
60
|
+
requirements:
|
61
|
+
- - ">="
|
62
|
+
- !ruby/object:Gem::Version
|
63
|
+
version: "0"
|
64
|
+
version:
|
65
|
+
requirements: []
|
66
|
+
|
67
|
+
rubyforge_project:
|
68
|
+
rubygems_version: 1.2.0
|
69
|
+
signing_key:
|
70
|
+
specification_version: 2
|
71
|
+
summary: TODO
|
72
|
+
test_files:
|
73
|
+
- test/cfbackup_test.rb
|
74
|
+
- test/test_helper.rb
|