rack2aws 0.1.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.
@@ -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: []