3 """Retrieve eStatement from AWS S3 5 Only the correct content is streamed to stdout. Any error must be 6 streamed down to stderr descriptor. 8 On failure, this script retries aws s3 api call for the number provided with 9 --s3-retries option argument. Default is MAX_RETRIES_DEFAULT. 14 1, if any error, exceptions 16 Content (see usage for more detail): 17 PDF binary content, OR 18 S3 Presigned URL - has a short expiration duration 19 (ENV: AWS_S3_PRESIGNURL_EXPIRATION_SECONDS; 23 [1] https://docs.aws.amazon.com/cli/latest/reference/s3/presign.html 24 [2] https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/s3.html#S3.Client.generate_presigned_url 34 from argparse
import ArgumentParser
36 format=(
"[%(levelname)-5s] %(asctime)-15s ({}|%(funcName)s|%(lineno)d) " 37 ":: %(message)s").format(__file__)
38 LOGGER = logging.getLogger(__name__)
40 LOGGER.setLevel(logging.ERROR)
41 logging_handler = logging.StreamHandler()
42 logging_handler.setFormatter(logging.Formatter(format))
43 LOGGER.addHandler(logging_handler)
47 AWS_REGION =
'us-east-1' 48 S3_STMNT_BUCKET =
"homecu.stmnts" 57 MAX_RETRIES_DEFAULT = 2
59 PRESIGN_URL_EXPIRATION_SECONDS = int(os.environ.get(
60 'AWS_S3_PRESIGNURL_EXPIRATION_SECONDS', 120))
64 """Prepare argument parser. 67 parser -- ArgumentParser object 69 parser = ArgumentParser(
70 description=(
"Get PDF EStatement Content from S3 (buffered" 71 "binary content OR s3 presigned url).")
74 parser.add_argument(
"-f",
"--file-estmnt",
75 help=
"Full path to estatement file for this period",
79 parser.add_argument(
"-p",
"--presignedurl", action =
"store_true",
80 help=(
"stream s3 presign url to stdout; " 81 "otherwise streams actual pdf content")
83 parser.add_argument(
"-r",
"--s3-retries",
84 help=
"Maximum number of retries on failure",
86 default=MAX_RETRIES_DEFAULT)
91 """Cleanup temporarily downloaded estatement file""" 92 if os.path.exists(tmp_file):
97 """Return an object's pre-signed""" 100 url = s3_client.generate_presigned_url(
101 ClientMethod=
'get_object',
103 "Bucket" : S3_STMNT_BUCKET,
106 ExpiresIn=PRESIGN_URL_EXPIRATION_SECONDS
108 except Exception
as e:
109 LOGGER.error(
"Estatement `%s` download failed." % object_key)
117 """Download pdf from S3 and return stream content to stdout""" 118 tmp_file = os.path.join(
"/tmp", s3_file_name.replace(
"/",
"_"))
121 s3_client.download_file(S3_STMNT_BUCKET, s3_file_name, tmp_file)
122 except Exception
as e:
124 LOGGER.error(
"Estatement `%s` download failed." % s3_file_name)
128 with open(tmp_file,
'rb')
as f_read:
129 sys.stdout.buffer.write(f_read.read())
134 """Retrieve eStatement from AWS S3 estatement storing bucket""" 137 args = parser.parse_args(argv)
138 success_status =
False 140 file_parts = args.file_estmnt.split(
"/")[1:]
142 if len(file_parts) != 7:
143 LOGGER.error((
"Requested path to estatement '%s' is invalid; " 145 "'/home/CUCODE/stmnt/pdf/yyyymmM/1/1XXXX.pdf" %
147 return success_status
149 cucode = file_parts[1].strip()
150 s3_file_name = os.path.join(file_parts[1],
156 max_retries = args.s3_retries
159 while tries < max_retries
and not success_status:
162 client = boto3.client(
's3', region_name=AWS_REGION)
164 if args.presignedurl:
169 except Exception
as e:
170 LOGGER.error((
"[%s] Estatement Retrieval from AWS S3 failed " 171 "(try# %s/%s): %s") % (cucode.upper(), tries,
174 success_status =
True 176 return success_status
180 """Script entrypoint""" 181 sys.exit(
main(sys.argv[1:]))
184 if __name__ ==
"__main__":
def return_pdf_content(s3_client, s3_file_name)
def cleanup_temp_file(tmp_file)
def return_presigned_url(s3_client, object_key)