Skip to content

Commit

Permalink
Switch from websocket to http in Atc::Aws::FixityCheck class; Also re…
Browse files Browse the repository at this point in the history
…factor code to simplify swapping between WEBSOCKET and HTTP fixity check methods
  • Loading branch information
elohanlon committed Jul 26, 2024
1 parent 3950ef8 commit 334ff3c
Show file tree
Hide file tree
Showing 6 changed files with 83 additions and 62 deletions.
10 changes: 10 additions & 0 deletions app/jobs/verify_fixity_job.rb
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,15 @@ def perform(stored_object_id)
fixity_verification_record = create_pending_fixity_verification stored_object
provider_fixity_check = instantiate_provider_fixity_check fixity_verification_record
verify_fixity(fixity_verification_record, provider_fixity_check)
rescue StandardError => e
handle_unexpected_error(fixity_verification_record, e) unless fixity_verification_record.nil?
end

def handle_unexpected_error(fixity_verification_record, err)
fixity_verification_record.update!(
status: :failure,
error_message: "An unexpected error occurred: #{err.message}"
)
end

def process_existing_fixity_verification_record(existing_fixity_verification_record)
Expand Down Expand Up @@ -49,6 +58,7 @@ def instantiate_provider_fixity_check(fixity_verification_record)

def verify_fixity(fixity_verification_record, provider_fixity_check)
object_checksum, object_size, fixity_check_error = provider_fixity_check.fixity_checksum_object_size

if fixity_check_error.present?
fixity_verification_record.error_message = fixity_check_error
fixity_verification_record.failure!
Expand Down
44 changes: 14 additions & 30 deletions lib/atc/aws/fixity_check.rb
Original file line number Diff line number Diff line change
@@ -1,41 +1,25 @@
# frozen_string_literal: true

class Atc::Aws::FixityCheck
def initialize(stored_object, stream_id)
def initialize(stored_object, fixity_check_identifier)
@bucket_name = stored_object.storage_provider.container_name
@object_path = stored_object.path
@fixity_checksum_algorithm = stored_object.source_object.fixity_checksum_algorithm
@stream_id = stream_id
@fixity_check_identifier = fixity_check_identifier
end

# Returns an array with the checksum, object size, and (if something went wrong) an error error_message.
# If there is an error, checksum and object size will be nil. If there is not an error,
# checksum and object size will be non-nil and error will be nil.
# @return [Array(String, Integer, String)] A 3-element array containing: [checksum, object_size, error_message]
def fixity_checksum_object_size
aws_fixity_check_response =
aws_fixity_websocket_channel_stream(@bucket_name,
@object_path,
@fixity_checksum_algorithm.name.downcase,
@stream_id)
case aws_fixity_check_response['type']
when 'fixity_check_complete'
[aws_fixity_check_response['data']['checksum_hexdigest'], aws_fixity_check_response['data']['object_size'], nil]
when 'fixity_check_error'
# if only want to return the error from the AWS fixity response, without data,
# use the following commented-out line instead of the last line
# [nil, nil, aws_fixity_check_response['data']['error_message']]
[nil, nil, response_data_as_string(aws_fixity_check_response)]
end
end

def response_data_as_string(aws_fixity_check_response)
# AWS response data contains, among other things, the error message
"AWS error response with the following data: #{aws_fixity_check_response['data']} "
end

def aws_fixity_websocket_channel_stream(bucket_name,
object_path,
checksum_algorithm_name,
job_identifier)
# Response received (Hash) is returned as-is.
remote_fixity_check = Atc::Aws::RemoteFixityCheck.new(CHECK_PLEASE['ws_url'], CHECK_PLEASE['auth_token'])
remote_fixity_check.perform(job_identifier, bucket_name, object_path, checksum_algorithm_name)
response = Atc::Aws::RemoteFixityCheck.new(
CHECK_PLEASE['http_base_url'], CHECK_PLEASE['ws_url'], CHECK_PLEASE['auth_token']
).perform(
@fixity_check_identifier, @bucket_name,
@object_path, @fixity_checksum_algorithm.name.downcase,
Atc::Aws::RemoteFixityCheck::HTTP
)
[response['checksum_hexdigest'], response['object_size'], response['error_message']]
end
end
6 changes: 5 additions & 1 deletion lib/atc/aws/remote_fixity_check.rb
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ def create_websocket_connection
def perform(job_identifier, bucket_name, object_path, checksum_algorithm_name, method = WEBSOCKET)
case method
when WEBSOCKET
perform_websocket(job_identifier, bucket_name, object_path, checksum_algorithm_name)
perform_websocket(job_identifier, bucket_name, object_path, checksum_algorithm_name)['data']
when HTTP
perform_http(bucket_name, object_path, checksum_algorithm_name)
else
Expand All @@ -60,6 +60,10 @@ def perform_http(bucket_name, object_path, checksum_algorithm_name)
end

JSON.parse(response.body)
rescue StandardError => e
{
'checksum_hexdigest' => nil, 'object_size' => nil, 'error_message' => "An unexpected error occurred: #{e.message}"
}
end

def perform_websocket(job_identifier, bucket_name, object_path, checksum_algorithm_name)
Expand Down
18 changes: 18 additions & 0 deletions lib/tasks/comparison.rake
Original file line number Diff line number Diff line change
Expand Up @@ -41,5 +41,23 @@ namespace :atc do

puts "Done!"
end

desc 'List multipart info for an S3 object (including part size)'
task list_multipart_info: :environment do
bucket_name = ENV['bucket_name']
path = ENV['path']

attributes = S3_CLIENT.get_object_attributes(
bucket: 'cul-dlstor-digital-working',
key: 'test/ave_biggert_00001r.tif',
#object_attributes: ['ETag']
#object_attributes: ['ETag', 'Checksum' 'ObjectParts' ]
)

puts attributes.inspect

end
end


end
65 changes: 35 additions & 30 deletions spec/atc/aws/fixity_check_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -18,50 +18,55 @@
source_object: source_object,
storage_provider: aws_storage_provider)
end
let(:aws_hash_response) do
{ 'type' => 'fixity_check_complete',
'data' => { 'checksum_hexdigest' => 'ABCDEF12345', 'object_size' => 1234 } }
end
let(:aws_error_hash_response) do
{ 'type' => 'fixity_check_error',
'data' => { 'error_message' => 'Ooops!',
'job_identifier' => 1234,
'bucket_name' => 'cul_bucket',
'object_path' => 'I/Am/An/Object',
'checksum_algorithm_name' => 'SHA31415' } }
end

describe '#fixity_checksum_object_size' do
context 'with an AWS response without errors ' do
it 'returns the object checksum and object size, and nil for the aws error message' do
allow(aws_fixity_check).to receive(:aws_fixity_websocket_channel_stream) { aws_hash_response }
let(:remote_fixity_check) do
dbl = instance_double(Atc::Aws::RemoteFixityCheck)
allow(dbl).to receive(:perform).and_return(remote_fixity_check_perform_response)
dbl
end

before do
allow(Atc::Aws::RemoteFixityCheck).to receive(:new).and_return(remote_fixity_check)
end

context 'with a response without errors' do
let(:remote_fixity_check_perform_response) do
{
'checksum_hexdigest' => 'ABCDEF12345',
'object_size' => 1234
}
end

it 'returns the object checksum and object size, and nil for the error message' do
result = aws_fixity_check.fixity_checksum_object_size
expect(result).to eq(['ABCDEF12345', 1234, nil])
expect(result).to eq(
[
remote_fixity_check_perform_response['checksum_hexdigest'],
remote_fixity_check_perform_response['object_size'],
nil
]
)
end
end

context 'with an AWS response with errors ' do
context 'with an AWS response with errors' do
let(:remote_fixity_check_perform_response) do
{
'error_message' => 'Ooops!'
}
end

it 'returns nil for the object checksum and object size' do
allow(aws_fixity_check).to receive(:aws_fixity_websocket_channel_stream) { aws_error_hash_response }
result = aws_fixity_check.fixity_checksum_object_size
expect(result[0]).to eq nil
expect(result[1]).to eq nil
end

it 'returns the error message (including data)' do
allow(aws_fixity_check).to receive(:aws_fixity_websocket_channel_stream) { aws_error_hash_response }
it 'returns the error message' do
result = aws_fixity_check.fixity_checksum_object_size
expect(result[2]).to include('Ooops!')
expect(result[2]).to include('cul_bucket')
expect(result[2]).to eq(remote_fixity_check_perform_response['error_message'])
end
end
end

describe '#response_data_as_string' do
it 'returns the aws response data info as a string' do
result = aws_fixity_check.response_data_as_string aws_error_hash_response
expect(result).to include('Ooops!')
expect(result).to include('cul_bucket')
end
end
end
2 changes: 1 addition & 1 deletion spec/atc/aws/remote_fixity_check_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@
# Wait for the thread to finish
t.join

expect(job_response).to eq(JSON.parse(fixity_check_complete_message['message']))
expect(job_response).to eq(JSON.parse(fixity_check_complete_message['message'])['data'])
end
end

Expand Down

0 comments on commit 334ff3c

Please sign in to comment.