Skip to content

Commit 4637501

Browse files
committed
Implemented code review comments: md5 fallback and re.search
1 parent e02e026 commit 4637501

2 files changed

Lines changed: 49 additions & 4 deletions

File tree

bignbit/handle_big_result.py

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -93,8 +93,7 @@ def process(self) -> dict[str, Any]:
9393
nrt_filename_regex = dataset_config.get('nrtFilenameRegex')
9494
is_nrt = False
9595
if nrt_filename_regex:
96-
nrt_match = re.match(nrt_filename_regex, granule_id)
97-
if nrt_match:
96+
if re.search(nrt_filename_regex, granule_id):
9897
is_nrt = True
9998

10099
try:
@@ -242,15 +241,27 @@ def process_harmony_results(harmony_job: dict[str, str], cmr_env: str) -> list[d
242241

243242
response = s3_client.head_object(Bucket=bucket, Key=key,
244243
ExpectedBucketOwner=utils.get_aws_account_id())
245-
# For single-part uploads, the S3 ETag is the MD5 hex digest of the object
246244
etag = response['ETag'].strip('"')
247245

246+
# For single-part uploads, the S3 ETag is the MD5 hex digest of the object.
247+
# For multipart uploads, the ETag has a '-<partcount>' suffix and is not an MD5.
248+
if re.fullmatch(r'[0-9a-fA-F]{32}', etag):
249+
checksum = etag
250+
else:
251+
CUMULUS_LOGGER.warning(
252+
'ETag {} for s3://{}/{} is not a valid MD5 digest; '
253+
'falling back to streaming MD5 computation', etag, bucket, key
254+
)
255+
get_response = s3_client.get_object(Bucket=bucket, Key=key,
256+
ExpectedBucketOwner=utils.get_aws_account_id())
257+
checksum = hashlib.md5(get_response['Body'].read()).hexdigest()
258+
248259
filename = key.split('/')[-1]
249260
file_dict = {
250261
'fileName': filename,
251262
'bucket': bucket,
252263
'key': key,
253-
'checksum': etag,
264+
'checksum': checksum,
254265
'checksumType': 'md5'
255266
}
256267
# Weird quirk where if we are working with a collection that doesn't define variables, the Harmony request

tests/test_handle_big_result.py

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
"""Unit tests for handle_big_result module"""
22
import hashlib
3+
import io
34
import json
45
import xml.etree.ElementTree as ET
56
from unittest.mock import MagicMock, patch
67
import boto3
78
import pytest
9+
from botocore.response import StreamingBody
810
from moto import mock_s3, mock_sts
911

1012
import bignbit.utils
@@ -673,3 +675,35 @@ def test_process_harmony_results_all_variable_omitted(mock_get_harmony_client):
673675
assert 'variable' not in result[0]
674676
assert result[0]['output_crs'] == 'EPSG:4326'
675677
assert result[0]['fileName'] == 'result_image.png'
678+
679+
680+
@patch('bignbit.utils.get_harmony_client')
681+
@patch('bignbit.handle_big_result.boto3')
682+
def test_process_harmony_results_multipart_etag_fallback(mock_boto3, mock_get_harmony_client):
683+
"""When ETag has a multipart suffix, falls back to streaming the object to compute MD5."""
684+
bignbit.utils.AWS_ACCOUNT_ID = '123456789012'
685+
686+
image_data = b'fake image data for multipart test'
687+
expected_checksum = hashlib.md5(image_data).hexdigest()
688+
689+
bucket_name = 'test-harmony-bucket'
690+
image_key = 'path/to/result_image.png'
691+
692+
mock_s3_instance = MagicMock()
693+
mock_s3_instance.head_object.return_value = {'ETag': '"abcdef1234567890abcdef1234567890-5"'}
694+
mock_s3_instance.get_object.return_value = {
695+
'Body': StreamingBody(io.BytesIO(image_data), len(image_data))
696+
}
697+
mock_boto3.client.return_value = mock_s3_instance
698+
699+
mock_harmony_client = MagicMock()
700+
mock_harmony_client.result_urls.return_value = iter([f's3://{bucket_name}/{image_key}'])
701+
mock_get_harmony_client.return_value = mock_harmony_client
702+
703+
harmony_job = {'job': 'valid-job-id', 'variable': 'temperature', 'output_crs': 'EPSG:4326'}
704+
result = process_harmony_results(harmony_job, 'UAT')
705+
706+
assert len(result) == 1
707+
assert result[0]['checksumType'] == 'md5'
708+
assert result[0]['checksum'] == expected_checksum
709+
mock_s3_instance.get_object.assert_called_once()

0 commit comments

Comments
 (0)