rack2aws 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 6b4761ec5789bc29a4d5fc2d56799a32b149be32
4
+ data.tar.gz: 63c2dadc08a92138f0e249732aa870d250c41636
5
+ SHA512:
6
+ metadata.gz: acb2647fd5e88e139bce7e1eff15aac44333ac6753b555ffa607702b90975a151f00e502301557bfc1a2596578416cdd15d6825286674cb0bbca545f550e7b5f
7
+ data.tar.gz: c3d38eea9c6ab2591022869c828e791c63666c5e8d5d35a06ed46f04c7834c8f12026c2915113db4766425ab7bcd819365bab7f18e34bfa392700efdc5fd151b
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'rack2aws'
4
+
5
+ Rack2Aws::CLI.new.run
@@ -0,0 +1,130 @@
1
+ require 'fog'
2
+ require 'commander'
3
+ require 'rack2aws/config'
4
+ require 'rack2aws/version'
5
+
6
+
7
+ module Rack2Aws
8
+ class FileCopy
9
+ include Rack2Aws::Configuration
10
+
11
+ attr_reader :per_page, :rackspace, :aws, :rackspace_directory, :aws_directory, :verbose_mode, :files, :total
12
+
13
+ def initialize(options={})
14
+ options = default_options.merge(options)
15
+ @per_page = options[:per_page]
16
+ @rackspace = Fog::Storage.new(RackspaceConfig.load())
17
+ @aws = Fog::Storage.new(AWSConfig.load())
18
+
19
+ @rackspace_directory = rackspace.directories.get(options[:rackspace_container])
20
+ @aws_directory = aws.directories.get(options[:aws_bucket])
21
+ @verbose_mode = options[:verbose]
22
+ @files = []
23
+ @total = 0
24
+ end
25
+
26
+ def default_options
27
+ { :per_page => 10000 }
28
+ end
29
+
30
+ def copy
31
+ time = Time.now
32
+ pages = rackspace_directory.count / per_page + 1
33
+ marker = ''
34
+
35
+ # get Rackspace files
36
+ pages.times do |i|
37
+ puts "! Getting page #{i+1}..."
38
+ files = rackspace_directory.files.all(:limit => per_page, :marker => marker).to_a
39
+ puts "! #{files.size} files in page #{i+1}, forking..." if verbose_mode
40
+ pid = fork do
41
+ copy_files(i, files)
42
+ end
43
+ puts "! Process #{pid} forked to copy files" if verbose_mode
44
+ marker = files.last.key
45
+ @total += files.size
46
+ end
47
+
48
+ pages.times do
49
+ Process.wait
50
+ end
51
+
52
+ puts "--------------------------------------------------"
53
+ puts "! #{total} files copied in #{Time.now - time}secs."
54
+ puts "--------------------------------------------------\n\n"
55
+ end
56
+
57
+ def copy_files(page, files)
58
+ puts " [#{Process.pid}] Page #{page+1}: Copying #{files.size} files..." if verbose_mode
59
+ total = files.size
60
+ max_processes = 4
61
+ process_pids = {}
62
+ time = Time.now
63
+
64
+ while !files.empty? or !process_pids.empty?
65
+ while process_pids.size < max_processes and files.any? do
66
+ file = files.pop
67
+ pid = Process.fork do
68
+ copy_file(file)
69
+ end
70
+ process_pids[pid] = { :file => file }
71
+ end
72
+
73
+ if pid_done = Process.wait
74
+ if job_finished = process_pids.delete(pid_done)
75
+ puts " [#{Process.pid}] Page #{page+1}: Copied #{job_finished[:file].key}." if verbose_mode
76
+ end
77
+ end
78
+ end
79
+
80
+ puts " [#{Process.pid}] ** Page #{page+1}: Copied #{total} files in #{Time.now - time}secs" if verbose_mode
81
+ end
82
+
83
+ def copy_file(file)
84
+ aws_directory.files.create(:key => file.key,
85
+ :body => file.body,
86
+ :content_type => file,
87
+ :public => false)
88
+ end
89
+
90
+ private :copy_files, :copy_file
91
+ end
92
+
93
+ class CLI
94
+ include Commander::Methods
95
+
96
+ def run
97
+ program :name, 'rack2aws'
98
+ program :version, Rack2Aws::VERSION
99
+ program :description, 'Bridge from Rackspace Cloud Files to AWS S3'
100
+ program :help, 'Author', 'Faissal Elamraoui <amr.faissal@gmail.com>'
101
+
102
+ global_option('--verbose', 'Explain what is being done') { $verbose = true }
103
+
104
+ command :port do |cmd|
105
+ cmd.syntax = 'rack2aws port [options]'
106
+ cmd.description = 'Port files from Rackspace Cloud Files(tm) to AWS S3'
107
+
108
+ cmd.option '--container CONTAINER_NAME', String, 'Rackspace Cloud Files container name'
109
+ cmd.option '--bucket BUCKET_NAME', String, 'AWS S3 bucket name'
110
+ cmd.action do |args, options|
111
+ if options.container.nil?
112
+ options.container = ask('Rackspace Cloud Files container: ')
113
+ end
114
+
115
+ if options.bucket.nil?
116
+ options.bucket = ask('AWS S3 bucket: ')
117
+ end
118
+
119
+ FileCopy.new({
120
+ :rackspace_container => options.container,
121
+ :aws_bucket => options.bucket,
122
+ :verbose => $verbose
123
+ }).copy
124
+ end
125
+ end
126
+ run!
127
+ end
128
+
129
+ end
130
+ end
@@ -0,0 +1,187 @@
1
+ require 'rack2aws/props_reader'
2
+ require 'rack2aws/errors'
3
+
4
+
5
+ # Class to parse configuration files in the format of "param = value".
6
+ class KVConfigParser
7
+ attr_accessor :config_file, :params, :groups
8
+
9
+ # Initialize the class with the path to the 'config_fil'
10
+ # The class objects are dynamically generated by the
11
+ # name of the 'param' in the config file. Therefore, if
12
+ # the config file is 'param = value' then the itializer
13
+ # will eval "@param = value"
14
+ #
15
+ def initialize(config_file=nil, separator = '=')
16
+ @config_file = config_file
17
+ @params = {}
18
+ @groups = []
19
+ @splitRegex = '\s*' + separator + '\s*'
20
+
21
+ if(self.config_file)
22
+ self.validate_config()
23
+ self.import_config()
24
+ end
25
+ end
26
+
27
+ # Validate the config file, and contents
28
+ def validate_config()
29
+ unless File.readable?(self.config_file)
30
+ raise Errno::EACCES, "#{self.config_file} is not readable"
31
+ end
32
+ # FIX ME: need to validate contents/structure?
33
+ end
34
+
35
+ # Import data from the config to our config object.
36
+ def import_config()
37
+ # The config is top down.. anything after a [group] gets added as part
38
+ # of that group until a new [group] is found.
39
+ group = nil
40
+ open(self.config_file) {
41
+ |f|
42
+ f.each_with_index do |line, i|
43
+ line.strip!
44
+ # force_encoding not available in all versions of ruby
45
+ begin
46
+ if i.eql? 0 and line.include?("\xef\xbb\xbf".force_encoding("UTF-8"))
47
+ line.delete!("\xef\xbb\xbf".force_encoding("UTF-8"))
48
+ end
49
+ rescue NoMethodError
50
+ end
51
+
52
+ unless (/^\#/.match(line))
53
+ if(/#{@splitRegex}/.match(line))
54
+ param, value = line.split(/#{@splitRegex}/, 2)
55
+ var_name = "#{param}".chomp.strip
56
+ value = value.chomp.strip
57
+ new_value = ''
58
+ if (value)
59
+ if value =~ /^['"](.*)['"]$/
60
+ new_value = $1
61
+ else
62
+ new_value = value
63
+ end
64
+ else
65
+ new_value = ''
66
+ end
67
+
68
+ if group
69
+ self.add_to_group(group, var_name, new_value)
70
+ else
71
+ self.add(var_name, new_value)
72
+ end
73
+
74
+ elsif(/^\[(.+)\]$/.match(line).to_a != [])
75
+ group = /^\[(.+)\]$/.match(line).to_a[1]
76
+ self.add(group, {})
77
+ end
78
+ end
79
+ end
80
+ }
81
+ end
82
+
83
+ # This method will provide the value held by the object "@param"
84
+ # where "@param" is actually the name of the param in the config
85
+ # file.
86
+ #
87
+ # DEPRECATED - will be removed in future versions
88
+ #
89
+ def get_value(param)
90
+ puts "ParseConfig Deprecation Warning: get_value() is deprecated. Use " + \
91
+ "config['param'] or config['group']['param'] instead."
92
+ return self.params[param]
93
+ end
94
+
95
+ # This method is a shortcut to accessing the @params variable
96
+ def [](param)
97
+ return self.params[param]
98
+ end
99
+
100
+ # This method returns all parameters/groups defined in a config file.
101
+ def get_params()
102
+ return self.params.keys
103
+ end
104
+
105
+ # List available sub-groups of the config.
106
+ def get_groups()
107
+ return self.groups
108
+ end
109
+
110
+ # Adds an element to the config object
111
+ def add(param_name, value, override = false)
112
+ if value.class == Hash
113
+ if self.params.has_key?(param_name)
114
+ if self.params[param_name].class == Hash
115
+ if override
116
+ self.params[param_name] = value
117
+ else
118
+ self.params[param_name].merge!(value)
119
+ end
120
+ elsif self.params.has_key?(param_name)
121
+ if self.params[param_name].class != value.class
122
+ raise ArgumentError, "#{param_name} already exists, and is of different type!"
123
+ end
124
+ end
125
+ else
126
+ self.params[param_name] = value
127
+ end
128
+ if ! self.groups.include?(param_name)
129
+ self.groups.push(param_name)
130
+ end
131
+ else
132
+ self.params[param_name] = value
133
+ end
134
+ end
135
+
136
+ # Add parameters to a group. Parameters with the same name
137
+ # could be placed in different groups
138
+ def add_to_group(group, param_name, value)
139
+ if ! self.groups.include?(group)
140
+ self.add(group, {})
141
+ end
142
+ self.params[group][param_name] = value
143
+ end
144
+ end
145
+
146
+
147
+ module Rack2Aws
148
+ module Configuration
149
+
150
+ class RackspaceConfig
151
+ def self.load()
152
+ config_path = "#{ENV['HOME']}/.rack/config"
153
+
154
+ if !File.exist?(config_path)
155
+ raise FileNotFoundError, "Rackspace configuration file not found"
156
+ end
157
+
158
+ props_reader = PropertiesReader.new(config_path)
159
+ return {
160
+ :provider => 'Rackspace',
161
+ :rackspace_api_key => props_reader.get("api-key"),
162
+ :rackspace_username => props_reader.get("username"),
163
+ :rackspace_region => props_reader.get("region")
164
+ }
165
+ end
166
+ end
167
+
168
+ class AWSConfig
169
+ def self.load()
170
+ config_path = "#{ENV['HOME']}/.aws/credentials"
171
+
172
+ if !File.exist?(config_path)
173
+ raise FileNotFoundError, "Rackspace configuration file not found".bold.red
174
+ end
175
+
176
+ credentials = KVConfigParser.new(config_path)
177
+ return {
178
+ :provider => 'AWS',
179
+ :region => credentials['default']['region'],
180
+ :aws_access_key_id => credentials['default']['aws_access_key_id'],
181
+ :aws_secret_access_key => credentials['default']['aws_secret_access_key']
182
+ }
183
+ end
184
+ end
185
+
186
+ end
187
+ end
@@ -0,0 +1,2 @@
1
+ class FileNotFoundError < StandardError
2
+ end
@@ -0,0 +1,19 @@
1
+ class PropertiesReader
2
+ def initialize(file)
3
+ @file = file
4
+ @properties = {}
5
+ IO.foreach(file) do |line|
6
+ @properties[$1.strip] = $2 if line =~ /([^=]*)=(.*)\/\/(.*)/ || line =~ /([^=]*)=(.*)/
7
+ end
8
+ end
9
+
10
+ def to_s
11
+ output = "File Name #{@file} \n"
12
+ @properties.each {|key,value| output += "#{key}= #{value} \n"}
13
+ output
14
+ end
15
+
16
+ def get(key)
17
+ @properties[key].strip
18
+ end
19
+ end
@@ -0,0 +1,3 @@
1
+ module Rack2Aws
2
+ VERSION = "0.1.0"
3
+ end
metadata ADDED
@@ -0,0 +1,107 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: rack2aws
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Faissal Elamraoui
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2016-03-11 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: commander
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: 4.4.0
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: 4.4.0
27
+ - !ruby/object:Gem::Dependency
28
+ name: fog
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: 1.37.0
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: 1.37.0
41
+ - !ruby/object:Gem::Dependency
42
+ name: bundler
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '1.11'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '1.11'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rake
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '10.0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '10.0'
69
+ description:
70
+ email:
71
+ - amr.faissal@gmail.com
72
+ executables:
73
+ - rack2aws
74
+ extensions: []
75
+ extra_rdoc_files: []
76
+ files:
77
+ - bin/rack2aws
78
+ - lib/rack2aws.rb
79
+ - lib/rack2aws/config.rb
80
+ - lib/rack2aws/errors.rb
81
+ - lib/rack2aws/props_reader.rb
82
+ - lib/rack2aws/version.rb
83
+ homepage: https://amrfaissal.github.io/rack2aws
84
+ licenses:
85
+ - MIT
86
+ metadata: {}
87
+ post_install_message:
88
+ rdoc_options: []
89
+ require_paths:
90
+ - lib
91
+ required_ruby_version: !ruby/object:Gem::Requirement
92
+ requirements:
93
+ - - ">="
94
+ - !ruby/object:Gem::Version
95
+ version: '0'
96
+ required_rubygems_version: !ruby/object:Gem::Requirement
97
+ requirements:
98
+ - - ">="
99
+ - !ruby/object:Gem::Version
100
+ version: '0'
101
+ requirements: []
102
+ rubyforge_project:
103
+ rubygems_version: 2.5.1
104
+ signing_key:
105
+ specification_version: 4
106
+ summary: Bridge from Rackspace Cloud Files to AWS S3
107
+ test_files: []