Last active
October 16, 2018 13:53
-
-
Save brand-it/224c77b97e3fa7336602e56a9f3a837c to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class Config | |
attr_accessor :total_threads, :files_per_second, :logger | |
class << self | |
def info | |
@info ||= Config.new | |
end | |
end | |
def initialize | |
self.total_threads = 30 | |
self.files_per_second = 1600 | |
self.logger = MigrationLogger.new | |
logger.level = Logger.const_get('INFO') | |
end | |
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
$LOAD_PATH.push('/home/deploy/.gem/ruby/2.3.0/gems/ruby-progressbar-1.10.0/lib') | |
require 'ruby-progressbar' | |
class EncryptS3Files | |
attr_reader :s3, :total_threads, :executions_per_second, :config, :logger | |
attr_accessor :total_number_files_completed, :object_summaries, :started_at | |
delegate :total_threads, to: :config | |
delegate :files_per_second, to: :config | |
def initialize | |
@s3 = S3Util.new | |
@config = Config.info | |
# @semaphore = Mutex.new # don't need this but I keep forget what it is called | |
self.object_summaries = [] | |
@logger = MigrationLogger.new | |
@started_at = nil | |
end | |
def perform | |
self.started_at = Time.now.to_i | |
puts "This is the setup process which downloads all the encryption info and" | |
puts "bucket details. We don't know all the info yet but once this is" | |
puts "done it will give us a full brake down of all the assets encription on s3" | |
puts "This could take some time depending on how many total pages there are in object list api" | |
get_all_object_summaries | |
puts 'Getting the encription information from all the object list info second to last step' | |
get_encription_info | |
puts 'Starting to encrypt the files' | |
encrypt_each_files_page | |
end | |
private | |
def progress_bar | |
return @progress_bar unless @progress_bar.nil? || @progress_bar.finished? | |
@progress_bar = ProgressBar.create(format: '%E |%B| %p%% Processed: %c from %u"', total: nil) | |
end | |
def rate_limit | |
seconds_passed = (Time.now.to_i - started_at) | |
return if progress_bar.progress.to_i.zero? || seconds_passed.zero? | |
while (progress_bar.progress / seconds_passed) > files_per_second | |
Config.info.logger.debug("Rate Limited (#{progress_bar.total / seconds_passed}) > #{files_per_second} | #{progress_bar.progress} \/ #{seconds_passed}") | |
sleep 1 | |
seconds_passed = (Time.now.to_i - started_at) | |
end | |
end | |
def encrypt_each_files_page | |
threaded_loop(object_summaries) do |object| | |
begin | |
s3.turn_on_encryption(object.key) | |
Config.info.logger.info("Encrypted #{object.key}") | |
rescue StandardError => exception | |
Config.info.logger.info("encrypt_each_files_page failure: #{exception.message} #{object.key}") | |
end | |
end | |
end | |
def seahorse_response | |
s3.bucket.client.list_objects(bucket: s3.bucket.name) | |
end | |
def get_all_object_summaries | |
return if object_summaries.any? | |
progress_bar.start | |
seahorse_response.each_page do |page| | |
threaded_loop(page.data.contents, progress: nil) do |c| | |
object_summaries << Aws::S3::ObjectSummary.new( | |
bucket_name: s3.bucket.name, | |
key: c.key, | |
data: c, | |
client: s3.bucket.client | |
) | |
end | |
progress_bar.increment | |
end | |
progress_bar.finish | |
end | |
def get_encription_info | |
new_object_summaries = [] | |
threaded_loop(object_summaries) do |summery| | |
begin | |
if summery.object.server_side_encryption != 'AES256' | |
new_object_summaries << summery | |
end | |
rescue StandardError => exception | |
logger.info("get_encription_info failure: #{exception.message} #{summery.key}") | |
end | |
end | |
self.object_summaries = new_object_summaries | |
end | |
def already_encrypted?(object_key) | |
s3.bucket.object(object_key).server_side_encryption == true | |
end | |
def threaded_loop(array, progress: progress_bar) | |
raise "Total threads is not a number/integer #{total_threads.inspect}" unless total_threads.is_a?(Integer) | |
raise "Array provided does not respond to each_slice - #{array.class}" unless array.respond_to?(:each_slice) | |
slice_size = array.size / total_threads | |
threads = [] | |
progress.total = array.size if progress | |
slice_size = 1 if slice_size.zero? | |
array.each_slice(slice_size) do |grouped_array| | |
grouped_array = grouped_array.to_a # this is to insure that it is not using some other object type | |
threads << Thread.new do | |
while grouped_array.size > 0 | |
value = grouped_array.pop | |
rate_limit | |
yield(value) | |
progress.increment if progress | |
end | |
end | |
end | |
threads.each(&:join) | |
progress.finish if progress | |
end | |
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class MigrationLogger < Logger | |
def initialize(logdev = nil, shift_age = 0, shift_size = 1_048_576) | |
logdev ||= Rails.root.join('log', "#{Rails.env}_s3_encryption.log") | |
super | |
end | |
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class OptParser | |
class << self | |
def parse_args | |
option_parser = OptionParser.new do |opts| | |
opts.banner = "Usage: #{opts.program_name} [options]" | |
opts.separator '' | |
opts.separator 'Specific options:' | |
opts.on('-t', '--total-thread [Integer]', Integer) do |value| | |
unless value.to_i > 0 | |
puts "--total-thread is not a valid value. Needs to be greater than zero #{value.inspect}" | |
puts opts | |
exit 1 | |
end | |
puts "Limting Threds to #{value.to_i}" | |
Config.info.total_threads = value.to_i | |
end | |
opts.on('-s', '--files-per-second [Integer]', Integer, 'Total number of files it can loop threw per second') do |value| | |
unless value.to_i > 0 | |
puts "--files-per-second is not a valid value. Needs to be greater than zero #{value.inspect}" | |
puts opts | |
exit 1 | |
end | |
Config.info.files_per_second = value.to_i | |
puts "Only Processing #{Config.info.files_per_second} files per second" | |
end | |
opts.on('-l', '--log-level [INFO]', String, '[DEBUG|INFO]') do |value| | |
puts "Setting log level to #{Logger.const_get(value)}" | |
Config.info.logger.level = Logger.const_get(value) | |
end | |
end | |
option_parser.parse! | |
end | |
end | |
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
require File.expand_path('migration_logger', __dir__).to_s | |
require File.expand_path('opt_parser', __dir__).to_s | |
require File.expand_path('encrypt_s3_files', __dir__).to_s | |
require File.expand_path('config', __dir__).to_s | |
OptParser.parse_args | |
EncryptS3Files.new.perform |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment