From fdc431e2e77a5c66dbb4700146fd1356208d860b Mon Sep 17 00:00:00 2001 From: Jeff Albrecht Date: Tue, 9 Jul 2019 21:07:12 -0700 Subject: [PATCH 01/14] more generalized lambda sqs trigger --- stac_updater/resources.py | 42 +++++++++++++++++++++++++++++---------- 1 file changed, 32 insertions(+), 10 deletions(-) diff --git a/stac_updater/resources.py b/stac_updater/resources.py index 756eb7e..8e840ce 100644 --- a/stac_updater/resources.py +++ b/stac_updater/resources.py @@ -119,24 +119,39 @@ def sns_topic(topic_name): } return resource -def lambda_sqs_trigger(func_name, queue_name, catalog_root, concurrency): +# def lambda_sqs_trigger(func_name, queue_name, catalog_root, concurrency): +# func = { +# "handler": f"stac_updater.handler.{func_name}", +# "environment": { +# 'COLLECTION_ROOT': catalog_root +# }, +# "events": [ +# { +# "sqs": { +# "arn": "arn:aws:sqs:#{}:#{}:{}".format("{AWS::Region}", +# "{AWS::AccountId}", +# queue_name), +# } +# } +# ], +# "reservedConcurrency": concurrency +# } +# +# return func + +def lambda_sqs_trigger(func_name, queue_name): func = { "handler": f"stac_updater.handler.{func_name}", - "environment": { - 'COLLECTION_ROOT': catalog_root - }, "events": [ { "sqs": { "arn": "arn:aws:sqs:#{}:#{}:{}".format("{AWS::Region}", "{AWS::AccountId}", - queue_name), + queue_name) } } - ], - "reservedConcurrency": concurrency + ] } - return func def lambda_s3_trigger(func_name, bucket_name): @@ -205,7 +220,14 @@ def update_collection(name, root, filter_rule, long_poll, concurrency, path, fil dlq = sqs_queue(dlq_name) queue = sqs_queue(queue_name, dlq_name=dlq_name, maxRetry=3, long_poll=long_poll) sns_subscription, sqs_policy = subscribe_sqs_to_sns(queue_name, 'newStacItemTopic', filter_rule) - lambda_updater = lambda_sqs_trigger(lambda_name, queue_name, root, concurrency) + lambda_updater = lambda_sqs_trigger(lambda_name, queue_name) + + lambda_updater.update({ + 'environment': { + 'COLLECTION_ROOT': root + }, + "reservedConcurrency": concurrency + }) if path: lambda_updater['environment'].update({ @@ -227,4 +249,4 @@ def update_collection(name, root, filter_rule, long_poll, concurrency, path, fil 'functions': { f"{name}_{lambda_name}": lambda_updater } - } + } \ No newline at end of file From ecb6309decda25e679592b7ff5434fd71ea38bba Mon Sep 17 00:00:00 2001 From: Jeff Albrecht Date: Tue, 9 Jul 2019 21:28:21 -0700 Subject: [PATCH 02/14] removing comments --- stac_updater/resources.py | 20 -------------------- 1 file changed, 20 deletions(-) diff --git a/stac_updater/resources.py b/stac_updater/resources.py index 8e840ce..8a10598 100644 --- a/stac_updater/resources.py +++ b/stac_updater/resources.py @@ -119,26 +119,6 @@ def sns_topic(topic_name): } return resource -# def lambda_sqs_trigger(func_name, queue_name, catalog_root, concurrency): -# func = { -# "handler": f"stac_updater.handler.{func_name}", -# "environment": { -# 'COLLECTION_ROOT': catalog_root -# }, -# "events": [ -# { -# "sqs": { -# "arn": "arn:aws:sqs:#{}:#{}:{}".format("{AWS::Region}", -# "{AWS::AccountId}", -# queue_name), -# } -# } -# ], -# "reservedConcurrency": concurrency -# } -# -# return func - def lambda_sqs_trigger(func_name, queue_name): func = { "handler": f"stac_updater.handler.{func_name}", From 3758d77f87b8ced3619c0d08e3d6cc53db8836bb Mon Sep 17 00:00:00 2001 From: Jeff Albrecht Date: Tue, 9 Jul 2019 21:29:32 -0700 Subject: [PATCH 03/14] adding cli command to backfill thumbnails as items are inserted into catalog --- stac_updater/cli.py | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/stac_updater/cli.py b/stac_updater/cli.py index f151fbc..b98fa64 100644 --- a/stac_updater/cli.py +++ b/stac_updater/cli.py @@ -150,6 +150,44 @@ def add_logging(es_host): with open(sls_config_path, 'w') as outf: yaml.dump(sls_config, outf, indent=1) +@stac_updater.command(name='build-thumbnails', short_help="Generate thumbnails when ingesting items.") +@click.option('--collection', '-c', type=str, multiple=True, help="Limit thumbnails to specific collections.") +def build_thumbnails(collection): + # Deploy the stac-thumbnail service + # Subscribe notification SNS topic to stac-thumbnail SQS queue + queue_name = 'newThumbnailQueue' + + with open(sls_config_path, 'r') as f: + sls_config = yaml.unsafe_load(f) + + # Build notification topic if it doesn't already exist + if notification_topic_name not in sls_config['resources']['Resources']: + sls_config['resources']['Resources'].update({ + notification_topic_name: resources.sns_topic(notification_topic_name) + }) + sls_config['provider']['environment'].update({ + 'NOTIFICATION_TOPIC': notification_topic_name + }) + + # Create filter policies based on input collections + filter_policy = {"collection": collection} if len(collection) > 0 else None + subscription, policy = resources.subscribe_sqs_to_sns(queue_name, notification_topic_name, filter_policy) + + # Use remote references instead of local (queue is defined in separate service). + policy['Properties']['PolicyDocument']['Statement'][0].update({ + 'Resource': "arn:aws:sqs:#{AWS::Region}:#{AWS::AccountId}:" + queue_name + }) + policy['Properties']['Queues'][0] = 'https://sqs.#{AWS::Region}.amazonaws.com/#{AWS::AccountId}/' + queue_name + subscription['Properties'].update({'Endpoint': "arn:aws:sqs:#{AWS::Region}:#{AWS::AccountId}:" + queue_name}) + + sls_config['resources']['Resources'].update({ + 'thumbnailSnsSub': subscription, + 'thumbnailSqsPolicy': policy, + }) + + with open(sls_config_path, 'w') as outf: + yaml.dump(sls_config, outf, indent=1) + @stac_updater.command(name='deploy', short_help="deploy service to aws.") def deploy(): subprocess.call("docker build . -t stac-updater:latest", shell=True) From 01af34cf72fc02df2389a024cc572d4ed2405613 Mon Sep 17 00:00:00 2001 From: Jeff Albrecht Date: Tue, 9 Jul 2019 21:41:10 -0700 Subject: [PATCH 04/14] updating readme --- README.md | 15 ++++++++++++++- docs/images/build-thumbnails.png | Bin 0 -> 65710 bytes 2 files changed, 14 insertions(+), 1 deletion(-) create mode 100644 docs/images/build-thumbnails.png diff --git a/README.md b/README.md index d611373..2e12a48 100644 --- a/README.md +++ b/README.md @@ -88,7 +88,7 @@ It took 86 total invocations to process the 200 STAC Items. ![es-logging-2](docs/images/es-logging-summary.png) ## Update Dynamic Catalog -STAC Items which are successfully ingested into a static collection may also by ingested into a deployed instance of [sat-api](https://github.com/sat-utils/sat-api). +STAC Items which are successfully ingested into a static collection may also by ingested into a deployed instance of [sat-api](https://github.com/sat-utils/sat-api)28. ``` stac-updater update-dynamic-collection --arn arn:aws:lambda:::function:sat-api-ingest @@ -96,7 +96,20 @@ stac-updater update-dynamic-collection --arn arn:aws:lambda:: ![update-sat-api](docs/images/update-sat-api.png) +## Thumbnails +You may backfill thumbnails as items are inserted into the STAC catalog using a deployed instance of [stac-thumbnails](https://github.com/geospatial-jeff/stac-thumbnails). +``` +stac-updater build-thumbnails +``` + +![update-sat-api](docs/images/build-thumbnails.png) + +This plugin is currently designed for a specific use-case and expects the following conditions: +- The underlying asset is referenced at `stac_item['assets']['data']['href']`. +- The underlying asset is a three band image (RGB). + +More generic support for thumbnails will be added at a later date if there is interest. # TODOS - Add support for [staccato](https://github.com/boundlessgeo/staccato). diff --git a/docs/images/build-thumbnails.png b/docs/images/build-thumbnails.png new file mode 100644 index 0000000000000000000000000000000000000000..4cc6a6b698411aecc4b9b8db2d627c4354718d5c GIT binary patch literal 65710 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

&za5&>5@vW3n+24& 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 FKL8)?nj!!I literal 0 HcmV?d00001 From 67b8fef009e9731c5621af3f545c94fbcb08cb82 Mon Sep 17 00:00:00 2001 From: Jeff Albrecht Date: Tue, 9 Jul 2019 21:42:47 -0700 Subject: [PATCH 05/14] typo --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 2e12a48..4c15174 100644 --- a/README.md +++ b/README.md @@ -19,7 +19,7 @@ stac-updater new-service # Build AWS resources to update collection stac-updater update-collection --root https://stac.com/landsat-8-l1/catalog.json \ --path {landsat:path}/{landsat:row} \ - --row {date}/{id} + --filename {date}/{id} # Modify kickoff event source to s3:ObjectCreated stac-updater modify-kickoff --type s3 --bucket_name stac-updater-kickoff From 29b45a895babf2016043d33971aff7219c0d713c Mon Sep 17 00:00:00 2001 From: Jeff Albrecht Date: Sun, 14 Jul 2019 11:30:10 -0700 Subject: [PATCH 06/14] exposing lambda timeout in cli and upping default to 15 --- stac_updater/cli.py | 5 +++-- stac_updater/resources.py | 5 +++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/stac_updater/cli.py b/stac_updater/cli.py index b98fa64..22f09d7 100644 --- a/stac_updater/cli.py +++ b/stac_updater/cli.py @@ -26,9 +26,10 @@ def new_service(): @click.option('--root', '-r', type=str, required=True, help="URL of collection.") @click.option('--long-poll/--short-poll', default=False, help="Enable long polling.") @click.option('--concurrency', type=int, default=1, help="Sets lambda concurrency limit when polling the queue.") +@click.option('--timeout', type=int, default=15, help="Sets lambda timeout.") @click.option('--path', type=str, help="Pattern used by sat-stac to build sub-catalogs.") @click.option('--filename', type=str, help="Pattern used by sat-stac to build item name.") -def update_collection(root, long_poll, concurrency, path, filename): +def update_collection(root, long_poll, concurrency, timeout, path, filename): # Create a SQS queue for the collection # Subscribe SQS queue to SNS topic with filter policy on collection name # Configure lambda function and attach to SQS queue (use ENV variables to pass state) @@ -43,7 +44,7 @@ def update_collection(root, long_poll, concurrency, path, filename): # Using unsafe load to preserve type. sls_config = yaml.unsafe_load(f) - aws_resources = resources.update_collection(name, root, filter_rule, long_poll, concurrency, path, filename) + aws_resources = resources.update_collection(name, root, filter_rule, long_poll, concurrency, timeout, path, filename) sls_config['resources']['Resources'].update(aws_resources['resources']) sls_config['functions'].update(aws_resources['functions']) diff --git a/stac_updater/resources.py b/stac_updater/resources.py index 8a10598..2a70bf2 100644 --- a/stac_updater/resources.py +++ b/stac_updater/resources.py @@ -190,7 +190,7 @@ def lambda_invoke(func_name): } return func -def update_collection(name, root, filter_rule, long_poll, concurrency, path, filename): +def update_collection(name, root, filter_rule, long_poll, concurrency, timeout, path, filename): dlq_name = f"{name}Dlq" queue_name = f"{name}Queue" sns_sub_name = f"{name}SnsSub" @@ -206,7 +206,8 @@ def update_collection(name, root, filter_rule, long_poll, concurrency, path, fil 'environment': { 'COLLECTION_ROOT': root }, - "reservedConcurrency": concurrency + "reservedConcurrency": concurrency, + "timeout": timeout }) if path: From 65f30d34e9147ae1dda0fffc808faaffeb5545c4 Mon Sep 17 00:00:00 2001 From: Jeff Albrecht Date: Sun, 14 Jul 2019 17:40:34 -0700 Subject: [PATCH 07/14] making sure notification topic is deployed before subscriptions are created --- stac_updater/cli.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/stac_updater/cli.py b/stac_updater/cli.py index 22f09d7..3259379 100644 --- a/stac_updater/cli.py +++ b/stac_updater/cli.py @@ -74,6 +74,7 @@ def update_dynamic_catalog(arn): 'satApiIngestSubscription': sns_subscription, 'satApiIngestPolicy': policy }) + sns_subscription.update({'DependsOn': 'stacUpdaterNotifications'}) with open(sls_config_path, 'w') as outf: yaml.dump(sls_config, outf, indent=1) @@ -180,6 +181,7 @@ def build_thumbnails(collection): }) policy['Properties']['Queues'][0] = 'https://sqs.#{AWS::Region}.amazonaws.com/#{AWS::AccountId}/' + queue_name subscription['Properties'].update({'Endpoint': "arn:aws:sqs:#{AWS::Region}:#{AWS::AccountId}:" + queue_name}) + subscription.update({'DependsOn': 'stacUpdaterNotifications'}) sls_config['resources']['Resources'].update({ 'thumbnailSnsSub': subscription, From 4f9d1facd609c07766ca6113ad9c2b8f75ed1749 Mon Sep 17 00:00:00 2001 From: Jeff Albrecht Date: Sun, 14 Jul 2019 17:45:16 -0700 Subject: [PATCH 08/14] adding visibility timeout to prevent messages from being prematurely moved to dlq --- stac_updater/resources.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/stac_updater/resources.py b/stac_updater/resources.py index 2a70bf2..5e5cf6e 100644 --- a/stac_updater/resources.py +++ b/stac_updater/resources.py @@ -67,7 +67,6 @@ def subscribe_lambda_to_sns(lambda_arn, topic_name): "FunctionName": lambda_arn, "Principal": "sns.amazonaws.com", "SourceArn": {"Ref": topic_name}, - } } @@ -85,7 +84,7 @@ def subscribe_lambda_to_sns(lambda_arn, topic_name): return subscription, lambda_policy -def sqs_queue(queue_name, dlq_name=None, maxRetry=3, long_poll=False): +def sqs_queue(queue_name, dlq_name=None, maxRetry=3, long_poll=False, timeout=None): resource = { "Type": "AWS::SQS::Queue", "Properties": { @@ -108,6 +107,9 @@ def sqs_queue(queue_name, dlq_name=None, maxRetry=3, long_poll=False): if long_poll: resource['Properties'].update({'ReceiveMessageWaitTimeSeconds': 20}) + if timeout: + resource['Properties'].update({'VisibilityTimeout': timeout}) + return resource def sns_topic(topic_name): @@ -198,7 +200,7 @@ def update_collection(name, root, filter_rule, long_poll, concurrency, timeout, lambda_name = "update_collection" dlq = sqs_queue(dlq_name) - queue = sqs_queue(queue_name, dlq_name=dlq_name, maxRetry=3, long_poll=long_poll) + queue = sqs_queue(queue_name, dlq_name=dlq_name, maxRetry=3, long_poll=long_poll, timeout=60) sns_subscription, sqs_policy = subscribe_sqs_to_sns(queue_name, 'newStacItemTopic', filter_rule) lambda_updater = lambda_sqs_trigger(lambda_name, queue_name) From c5b4784cf69599a2d29efa7ecb99e293ad553d85 Mon Sep 17 00:00:00 2001 From: Jeff Albrecht Date: Sun, 14 Jul 2019 17:54:41 -0700 Subject: [PATCH 09/14] removing collection filtering from thumbnail service --- stac_updater/cli.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/stac_updater/cli.py b/stac_updater/cli.py index 3259379..929cde2 100644 --- a/stac_updater/cli.py +++ b/stac_updater/cli.py @@ -153,8 +153,7 @@ def add_logging(es_host): yaml.dump(sls_config, outf, indent=1) @stac_updater.command(name='build-thumbnails', short_help="Generate thumbnails when ingesting items.") -@click.option('--collection', '-c', type=str, multiple=True, help="Limit thumbnails to specific collections.") -def build_thumbnails(collection): +def build_thumbnails(): # Deploy the stac-thumbnail service # Subscribe notification SNS topic to stac-thumbnail SQS queue queue_name = 'newThumbnailQueue' @@ -172,8 +171,7 @@ def build_thumbnails(collection): }) # Create filter policies based on input collections - filter_policy = {"collection": collection} if len(collection) > 0 else None - subscription, policy = resources.subscribe_sqs_to_sns(queue_name, notification_topic_name, filter_policy) + subscription, policy = resources.subscribe_sqs_to_sns(queue_name, notification_topic_name) # Use remote references instead of local (queue is defined in separate service). policy['Properties']['PolicyDocument']['Statement'][0].update({ From 2dc7f611cecd5571b5f83597056a620f93fa10ac Mon Sep 17 00:00:00 2001 From: Jeff Albrecht Date: Sun, 14 Jul 2019 18:45:50 -0700 Subject: [PATCH 10/14] adding option to backfill collection extents -- spatial and temporal -- as new items are ingested into catalog --- stac_updater/cli.py | 7 +-- stac_updater/handler.py | 99 +++++++++++++++++++++++++-------------- stac_updater/resources.py | 7 ++- stac_updater/utils.py | 6 +++ 4 files changed, 80 insertions(+), 39 deletions(-) diff --git a/stac_updater/cli.py b/stac_updater/cli.py index 929cde2..6f811c5 100644 --- a/stac_updater/cli.py +++ b/stac_updater/cli.py @@ -29,7 +29,8 @@ def new_service(): @click.option('--timeout', type=int, default=15, help="Sets lambda timeout.") @click.option('--path', type=str, help="Pattern used by sat-stac to build sub-catalogs.") @click.option('--filename', type=str, help="Pattern used by sat-stac to build item name.") -def update_collection(root, long_poll, concurrency, timeout, path, filename): +@click.option('--backfill_extent/--no_backfill', default=False, help="Enable backfilling of collection spatial/temporal extent.") +def update_collection(root, long_poll, concurrency, timeout, path, filename, backfill_extent): # Create a SQS queue for the collection # Subscribe SQS queue to SNS topic with filter policy on collection name # Configure lambda function and attach to SQS queue (use ENV variables to pass state) @@ -43,8 +44,8 @@ def update_collection(root, long_poll, concurrency, timeout, path, filename): with open(sls_config_path, 'r') as f: # Using unsafe load to preserve type. sls_config = yaml.unsafe_load(f) - - aws_resources = resources.update_collection(name, root, filter_rule, long_poll, concurrency, timeout, path, filename) + aws_resources = resources.update_collection(name, root, filter_rule, long_poll, concurrency, + timeout, path, filename, backfill_extent) sls_config['resources']['Resources'].update(aws_resources['resources']) sls_config['functions'].update(aws_resources['functions']) diff --git a/stac_updater/handler.py b/stac_updater/handler.py index 5b20cf4..a80cf6c 100644 --- a/stac_updater/handler.py +++ b/stac_updater/handler.py @@ -2,6 +2,7 @@ import json import base64 import gzip +from datetime import datetime import boto3 from satstac import Collection, Item @@ -50,41 +51,69 @@ def kickoff(event, context): ) def update_collection(event, context): - collection_root = os.getenv('COLLECTION_ROOT') - path = os.getenv('PATH') - filename = os.getenv('FILENAME') - - item_count = len(event['Records']) - stac_links = [] - - for record in event['Records']: - stac_item = json.loads(record['body']) - - print(stac_item) - - col = Collection.open(collection_root) - collection_name = col.id - kwargs = {'item': Item(stac_item)} - if path: - kwargs.update({'path': '$' + '/$'.join(path.split('/'))}) - if filename: - kwargs.update({'filename': '$' + '/$'.join(filename.split('/'))}) - print(kwargs) - col.add_item(**kwargs) - col.save() - - stac_links.append(kwargs['item'].links('self')[0]) - - # Send message to SNS Topic if enabled - if NOTIFICATION_TOPIC: - kwargs = utils.stac_to_sns(kwargs['item'].data) - kwargs.update({ - 'TopicArn': f"arn:aws:sns:{REGION}:{ACCOUNT_ID}:{NOTIFICATION_TOPIC}" - }) - sns_client.publish(**kwargs) - - - print(f"LOGS CollectionName: {collection_name}\tItemCount: {item_count}\tItemLinks: {stac_links}") + try: + collection_root = os.getenv('COLLECTION_ROOT') + path = os.getenv('PATH') + filename = os.getenv('FILENAME') + backfill_extent = os.getenv('BACKFILL_EXTENT') + + item_count = len(event['Records']) + stac_links = [] + + for record in event['Records']: + stac_item = json.loads(record['body']) + print(stac_item) + + col = Collection.open(collection_root) + collection_name = col.id + kwargs = {'item': Item(stac_item)} + if path: + kwargs.update({'path': '$' + '/$'.join(path.split('/'))}) + if filename: + kwargs.update({'filename': '$' + '/$'.join(filename.split('/'))}) + + # Update spatial and temporal extent of collection + if backfill_extent: + if 'spatial' in col.data['extent']: + if stac_item['bbox'][0] < col.data['extent']['spatial'][0]: + col.data['extent']['spatial'][0] = stac_item['bbox'][0] + if stac_item['bbox'][1] < col.data['extent']['spatial'][1]: + col.data['extent']['spatial'][1] = stac_item['bbox'][1] + if stac_item['bbox'][2] > col.data['extent']['spatial'][2]: + col.data['extent']['spatial'][2] = stac_item['bbox'][2] + if stac_item['bbox'][3] > col.data['extent']['spatial'][3]: + col.data['extent']['spatial'][3] = stac_item['bbox'][3] + else: + col.data['extent'].update({'spatial': stac_item['bbox']}) + + if 'temporal' in col.data['extent']: + item_dt = utils.load_datetime(stac_item['properties']['datetime']) + min_dt = utils.load_datetime(col.data['extent']['temporal'][0]) + max_dt = utils.load_datetime(col.data['extent']['temporal'][1]) + if item_dt < min_dt: + col.data['extent']['temporal'][0] = stac_item['properties']['datetime'] + if item_dt > max_dt: + col.data['extent']['temporal'][1] = stac_item['properties']['datetime'] + else: + col.data['extent'].update({'temporal': [stac_item['properties']['datetime'], stac_item['properties']['datetime']]}) + + col.add_item(**kwargs) + col.save() + + stac_links.append(kwargs['item'].links('self')[0]) + + # Send message to SNS Topic if enabled + if NOTIFICATION_TOPIC: + kwargs = utils.stac_to_sns(kwargs['item'].data) + kwargs.update({ + 'TopicArn': f"arn:aws:sns:{REGION}:{ACCOUNT_ID}:{NOTIFICATION_TOPIC}" + }) + sns_client.publish(**kwargs) + + + print(f"LOGS CollectionName: {collection_name}\tItemCount: {item_count}\tItemLinks: {stac_links}") + except: + raise def es_log_ingest(event, context): diff --git a/stac_updater/resources.py b/stac_updater/resources.py index 5e5cf6e..9b0d6e2 100644 --- a/stac_updater/resources.py +++ b/stac_updater/resources.py @@ -192,7 +192,7 @@ def lambda_invoke(func_name): } return func -def update_collection(name, root, filter_rule, long_poll, concurrency, timeout, path, filename): +def update_collection(name, root, filter_rule, long_poll, concurrency, timeout, path, filename, backfill_extent): dlq_name = f"{name}Dlq" queue_name = f"{name}Queue" sns_sub_name = f"{name}SnsSub" @@ -222,6 +222,11 @@ def update_collection(name, root, filter_rule, long_poll, concurrency, timeout, 'FILENAME': filename }) + if backfill_extent: + lambda_updater['environment'].update({ + 'BACKFILL_EXTENT': backfill_extent + }) + return { 'resources': { dlq_name: dlq, diff --git a/stac_updater/utils.py b/stac_updater/utils.py index 298c270..69817e3 100644 --- a/stac_updater/utils.py +++ b/stac_updater/utils.py @@ -1,3 +1,4 @@ +from datetime import datetime import json def stac_to_sns(stac_item): @@ -36,3 +37,8 @@ def stac_to_sns(stac_item): "MessageAttributes": attributes } +def load_datetime(date_str): + try: + return datetime.strptime(date_str, "%Y-%m-%dT%H:%M:%S.%fZ") + except: + return datetime.strptime(date_str, "%Y-%m-%d") \ No newline at end of file From 882beb92db8798bfd23ddb00ef66d5dfe944811a Mon Sep 17 00:00:00 2001 From: Jeff Albrecht Date: Sun, 14 Jul 2019 19:09:18 -0700 Subject: [PATCH 11/14] fixing bug where long collection names caused deployment errors --- stac_updater/resources.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/stac_updater/resources.py b/stac_updater/resources.py index 9b0d6e2..fd2e407 100644 --- a/stac_updater/resources.py +++ b/stac_updater/resources.py @@ -193,10 +193,10 @@ def lambda_invoke(func_name): return func def update_collection(name, root, filter_rule, long_poll, concurrency, timeout, path, filename, backfill_extent): - dlq_name = f"{name}Dlq" - queue_name = f"{name}Queue" - sns_sub_name = f"{name}SnsSub" - sqs_policy_name = f"{name}SqsPolicy" + dlq_name = f"{name}Dlq"[:45] + queue_name = f"{name}Queue"[:45] + sns_sub_name = f"{name}SnsSub"[:45] + sqs_policy_name = f"{name}SqsPolicy"[:45] lambda_name = "update_collection" dlq = sqs_queue(dlq_name) @@ -235,6 +235,6 @@ def update_collection(name, root, filter_rule, long_poll, concurrency, timeout, sqs_policy_name: sqs_policy }, 'functions': { - f"{name}_{lambda_name}": lambda_updater + f"{name}_{lambda_name}"[:45]: lambda_updater } } \ No newline at end of file From 96e5f35752d80e8acde624c6fa8a5c49aca9811c Mon Sep 17 00:00:00 2001 From: Jeff Albrecht Date: Sun, 14 Jul 2019 21:31:39 -0700 Subject: [PATCH 12/14] bumping version --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 49217e9..e93a2f8 100644 --- a/setup.py +++ b/setup.py @@ -4,7 +4,7 @@ requirements = [line.rstrip() for line in reqs] setup(name="STAC Updater", - version='0.3', + version='0.3.1', author='Jeff Albrecht', author_email='geospatialjeff@gmail.com', packages=find_packages(exclude=['package']), From e3f18ac35bf48f59dc069f1d8f759e62b6be55c3 Mon Sep 17 00:00:00 2001 From: Jeff Albrecht Date: Mon, 15 Jul 2019 14:17:58 -0700 Subject: [PATCH 13/14] exposing visibility timeout to cli --- stac_updater/cli.py | 5 +++-- stac_updater/resources.py | 4 ++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/stac_updater/cli.py b/stac_updater/cli.py index 6f811c5..db2162f 100644 --- a/stac_updater/cli.py +++ b/stac_updater/cli.py @@ -27,10 +27,11 @@ def new_service(): @click.option('--long-poll/--short-poll', default=False, help="Enable long polling.") @click.option('--concurrency', type=int, default=1, help="Sets lambda concurrency limit when polling the queue.") @click.option('--timeout', type=int, default=15, help="Sets lambda timeout.") +@click.option('--visibility_timeout', type=int, default=60, help="Sets visibility timeout of messages consumed by the queue.") @click.option('--path', type=str, help="Pattern used by sat-stac to build sub-catalogs.") @click.option('--filename', type=str, help="Pattern used by sat-stac to build item name.") @click.option('--backfill_extent/--no_backfill', default=False, help="Enable backfilling of collection spatial/temporal extent.") -def update_collection(root, long_poll, concurrency, timeout, path, filename, backfill_extent): +def update_collection(root, long_poll, concurrency, timeout, visibility_timeout, path, filename, backfill_extent): # Create a SQS queue for the collection # Subscribe SQS queue to SNS topic with filter policy on collection name # Configure lambda function and attach to SQS queue (use ENV variables to pass state) @@ -45,7 +46,7 @@ def update_collection(root, long_poll, concurrency, timeout, path, filename, bac # Using unsafe load to preserve type. sls_config = yaml.unsafe_load(f) aws_resources = resources.update_collection(name, root, filter_rule, long_poll, concurrency, - timeout, path, filename, backfill_extent) + timeout, visibility_timeout, path, filename, backfill_extent) sls_config['resources']['Resources'].update(aws_resources['resources']) sls_config['functions'].update(aws_resources['functions']) diff --git a/stac_updater/resources.py b/stac_updater/resources.py index fd2e407..b217c03 100644 --- a/stac_updater/resources.py +++ b/stac_updater/resources.py @@ -192,7 +192,7 @@ def lambda_invoke(func_name): } return func -def update_collection(name, root, filter_rule, long_poll, concurrency, timeout, path, filename, backfill_extent): +def update_collection(name, root, filter_rule, long_poll, concurrency, timeout, vis_timeout, path, filename, backfill_extent): dlq_name = f"{name}Dlq"[:45] queue_name = f"{name}Queue"[:45] sns_sub_name = f"{name}SnsSub"[:45] @@ -200,7 +200,7 @@ def update_collection(name, root, filter_rule, long_poll, concurrency, timeout, lambda_name = "update_collection" dlq = sqs_queue(dlq_name) - queue = sqs_queue(queue_name, dlq_name=dlq_name, maxRetry=3, long_poll=long_poll, timeout=60) + queue = sqs_queue(queue_name, dlq_name=dlq_name, maxRetry=3, long_poll=long_poll, timeout=vis_timeout) sns_subscription, sqs_policy = subscribe_sqs_to_sns(queue_name, 'newStacItemTopic', filter_rule) lambda_updater = lambda_sqs_trigger(lambda_name, queue_name) From 4fce20d61e6834c3e8484704b6979703278b35df Mon Sep 17 00:00:00 2001 From: Jeff Albrecht Date: Fri, 2 Aug 2019 12:49:25 -0700 Subject: [PATCH 14/14] bugfix --- stac_updater/resources.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/stac_updater/resources.py b/stac_updater/resources.py index b217c03..895e92c 100644 --- a/stac_updater/resources.py +++ b/stac_updater/resources.py @@ -193,10 +193,10 @@ def lambda_invoke(func_name): return func def update_collection(name, root, filter_rule, long_poll, concurrency, timeout, vis_timeout, path, filename, backfill_extent): - dlq_name = f"{name}Dlq"[:45] - queue_name = f"{name}Queue"[:45] - sns_sub_name = f"{name}SnsSub"[:45] - sqs_policy_name = f"{name}SqsPolicy"[:45] + dlq_name = f"{name}Dlq"[:43] + queue_name = f"{name}Queue"[:43] + sns_sub_name = f"{name}SnsSub"[:43] + sqs_policy_name = f"{name}SqsPolicy"[:43] lambda_name = "update_collection" dlq = sqs_queue(dlq_name)