From cf271b406b37f3902f754bf0acc09eef2bdb6bb7 Mon Sep 17 00:00:00 2001 From: Jake Herrmann Date: Fri, 9 Feb 2024 16:24:48 -0900 Subject: [PATCH 01/42] start implementing variable costs in _get_credit_cost --- lib/dynamo/dynamo/jobs.py | 16 ++++++++++-- tests/test_dynamo/test_jobs.py | 47 ++++++++++++++++++++++++++++++++++ 2 files changed, 61 insertions(+), 2 deletions(-) diff --git a/lib/dynamo/dynamo/jobs.py b/lib/dynamo/dynamo/jobs.py index 3214136c2..5c48b3ae8 100644 --- a/lib/dynamo/dynamo/jobs.py +++ b/lib/dynamo/dynamo/jobs.py @@ -1,5 +1,7 @@ +import json from datetime import datetime, timezone from os import environ +from pathlib import Path from typing import List, Optional from uuid import uuid4 @@ -8,13 +10,23 @@ import dynamo.user from dynamo.util import DYNAMODB_RESOURCE, convert_floats_to_decimals, format_time, get_request_time_expression +costs_file = Path(__file__).parent / 'costs.json' +if costs_file.exists(): + COSTS = json.loads(costs_file.read_text()) + class InsufficientCreditsError(Exception): """Raised when trying to submit jobs whose total cost exceeds the user's remaining credits.""" -def _get_credit_cost(job: dict) -> float: - return 1.0 +def _get_credit_cost(job: dict, costs: dict) -> float: + # FIXME: don't worry about default parameter value + cost_definition = costs[job['job_type']] + if 'cost_parameter' in cost_definition: + parameter_value = job['job_parameters'].get(cost_definition['cost_parameter']) + if parameter_value is not None: + return cost_definition['cost_table'][parameter_value] + return cost_definition['default_cost'] def put_jobs(user_id: str, jobs: List[dict], dry_run=False) -> List[dict]: diff --git a/tests/test_dynamo/test_jobs.py b/tests/test_dynamo/test_jobs.py index ee9833856..4862c9027 100644 --- a/tests/test_dynamo/test_jobs.py +++ b/tests/test_dynamo/test_jobs.py @@ -182,6 +182,53 @@ def test_query_jobs_by_type(tables): assert list_have_same_elements(response, table_items[:2]) +def test_get_credit_cost(): + # FIXME: don't specify default value of resolution + costs = { + 'RTC_GAMMA': { + 'cost_parameter': 'resolution', + 'cost_table': { + 10: 60.0, + 20: 15.0, + 30: 5.0, + }, + 'default_cost': 3.0, + }, + 'INSAR_ISCE_BURST': { + 'default_cost': 1.0, + } + } + assert dynamo.jobs._get_credit_cost( + {'job_type': 'RTC_GAMMA', 'job_parameters': {'resolution': 10.0}}, + costs + ) == 60.0 + assert dynamo.jobs._get_credit_cost( + {'job_type': 'RTC_GAMMA', 'job_parameters': {'resolution': 20.0}}, + costs + ) == 15.0 + assert dynamo.jobs._get_credit_cost( + {'job_type': 'RTC_GAMMA', 'job_parameters': {'resolution': 30.0}}, + costs + ) == 5.0 + assert dynamo.jobs._get_credit_cost( + {'job_type': 'RTC_GAMMA', 'job_parameters': {}}, + costs + ) == 3.0 + with pytest.raises(KeyError): + dynamo.jobs._get_credit_cost( + {'job_type': 'RTC_GAMMA', 'job_parameters': {'resolution': 13.0}}, + costs + ) + assert dynamo.jobs._get_credit_cost( + {'job_type': 'INSAR_ISCE_BURST', 'job_parameters': {'foo': 'bar'}}, + costs + ) == 1.0 + assert dynamo.jobs._get_credit_cost( + {'job_type': 'INSAR_ISCE_BURST', 'job_parameters': {}}, + costs + ) == 1.0 + + def test_put_jobs(tables, monkeypatch): monkeypatch.setenv('DEFAULT_CREDITS_PER_USER', '10') payload = [{'name': 'name1'}, {'name': 'name1'}, {'name': 'name2'}] From d5277266ea2035df7c4ccb239a55a7c6cd5e9b7c Mon Sep 17 00:00:00 2001 From: Jake Herrmann Date: Tue, 13 Feb 2024 10:16:16 -0900 Subject: [PATCH 02/42] do not specify default cost if cost param given --- lib/dynamo/dynamo/jobs.py | 7 +++---- tests/test_dynamo/test_jobs.py | 6 ------ 2 files changed, 3 insertions(+), 10 deletions(-) diff --git a/lib/dynamo/dynamo/jobs.py b/lib/dynamo/dynamo/jobs.py index 5c48b3ae8..385768c4b 100644 --- a/lib/dynamo/dynamo/jobs.py +++ b/lib/dynamo/dynamo/jobs.py @@ -20,12 +20,11 @@ class InsufficientCreditsError(Exception): def _get_credit_cost(job: dict, costs: dict) -> float: - # FIXME: don't worry about default parameter value + # TODO: verify has cost param and table OR default cost cost_definition = costs[job['job_type']] if 'cost_parameter' in cost_definition: - parameter_value = job['job_parameters'].get(cost_definition['cost_parameter']) - if parameter_value is not None: - return cost_definition['cost_table'][parameter_value] + parameter_value = job['job_parameters'][cost_definition['cost_parameter']] + return cost_definition['cost_table'][parameter_value] return cost_definition['default_cost'] diff --git a/tests/test_dynamo/test_jobs.py b/tests/test_dynamo/test_jobs.py index 4862c9027..3db6ca91c 100644 --- a/tests/test_dynamo/test_jobs.py +++ b/tests/test_dynamo/test_jobs.py @@ -183,7 +183,6 @@ def test_query_jobs_by_type(tables): def test_get_credit_cost(): - # FIXME: don't specify default value of resolution costs = { 'RTC_GAMMA': { 'cost_parameter': 'resolution', @@ -192,7 +191,6 @@ def test_get_credit_cost(): 20: 15.0, 30: 5.0, }, - 'default_cost': 3.0, }, 'INSAR_ISCE_BURST': { 'default_cost': 1.0, @@ -210,10 +208,6 @@ def test_get_credit_cost(): {'job_type': 'RTC_GAMMA', 'job_parameters': {'resolution': 30.0}}, costs ) == 5.0 - assert dynamo.jobs._get_credit_cost( - {'job_type': 'RTC_GAMMA', 'job_parameters': {}}, - costs - ) == 3.0 with pytest.raises(KeyError): dynamo.jobs._get_credit_cost( {'job_type': 'RTC_GAMMA', 'job_parameters': {'resolution': 13.0}}, From 6fc03f5152b6094d7376cb54d38363052ce9470f Mon Sep 17 00:00:00 2001 From: Jake Herrmann Date: Wed, 14 Feb 2024 11:34:41 -0900 Subject: [PATCH 03/42] validate cost definition keys --- lib/dynamo/dynamo/jobs.py | 9 +++++++-- tests/test_dynamo/test_jobs.py | 23 +++++++++++++++++++++++ 2 files changed, 30 insertions(+), 2 deletions(-) diff --git a/lib/dynamo/dynamo/jobs.py b/lib/dynamo/dynamo/jobs.py index f25433967..bd2de9220 100644 --- a/lib/dynamo/dynamo/jobs.py +++ b/lib/dynamo/dynamo/jobs.py @@ -27,11 +27,16 @@ class InsufficientCreditsError(Exception): def _get_credit_cost(job: dict, costs: dict) -> float: - # TODO: verify has cost param and table OR default cost - cost_definition = costs[job['job_type']] + job_type = job['job_type'] + cost_definition = costs[job_type] + + if cost_definition.keys() not in ({'cost_parameter', 'cost_table'}, {'default_cost'}): + raise ValueError(f'Cost definition for job type {job_type} has invalid keys: {cost_definition.keys()}') + if 'cost_parameter' in cost_definition: parameter_value = job['job_parameters'][cost_definition['cost_parameter']] return cost_definition['cost_table'][parameter_value] + return cost_definition['default_cost'] diff --git a/tests/test_dynamo/test_jobs.py b/tests/test_dynamo/test_jobs.py index 6f03130ca..f59854da7 100644 --- a/tests/test_dynamo/test_jobs.py +++ b/tests/test_dynamo/test_jobs.py @@ -223,6 +223,29 @@ def test_get_credit_cost(): ) == 1.0 +def test_get_credit_cost_validate_keys(): + costs = { + 'JOB_TYPE_A': {'cost_parameter': 'foo', 'cost_table': {'bar': 3.0}}, + 'JOB_TYPE_B': {'default_cost': 5.0}, + 'JOB_TYPE_C': {'cost_parameter': ''}, + 'JOB_TYPE_D': {'cost_table': {}}, + 'JOB_TYPE_E': {'cost_parameter': '', 'cost_table': {}, 'default_cost': 1.0}, + 'JOB_TYPE_F': {'cost_parameter': '', 'cost_table': {}, 'foo': None}, + } + + assert dynamo.jobs._get_credit_cost({'job_type': 'JOB_TYPE_A', 'job_parameters': {'foo': 'bar'}}, costs) == 3.0 + assert dynamo.jobs._get_credit_cost({'job_type': 'JOB_TYPE_B'}, costs) == 5.0 + + with pytest.raises(ValueError, match=r'^Cost definition for job type JOB_TYPE_C has invalid keys.*'): + dynamo.jobs._get_credit_cost({'job_type': 'JOB_TYPE_C'}, costs) + with pytest.raises(ValueError, match=r'^Cost definition for job type JOB_TYPE_D has invalid keys.*'): + dynamo.jobs._get_credit_cost({'job_type': 'JOB_TYPE_D'}, costs) + with pytest.raises(ValueError, match=r'^Cost definition for job type JOB_TYPE_E has invalid keys.*'): + dynamo.jobs._get_credit_cost({'job_type': 'JOB_TYPE_E'}, costs) + with pytest.raises(ValueError, match=r'^Cost definition for job type JOB_TYPE_F has invalid keys.*'): + dynamo.jobs._get_credit_cost({'job_type': 'JOB_TYPE_F'}, costs) + + def test_put_jobs(tables, monkeypatch): monkeypatch.setenv('DEFAULT_CREDITS_PER_USER', '10') payload = [{'name': 'name1'}, {'name': 'name1'}, {'name': 'name2'}] From 68d402e7c2ab3c4ff4e99211004c854958e51489 Mon Sep 17 00:00:00 2001 From: Jake Herrmann Date: Wed, 14 Feb 2024 13:45:58 -0900 Subject: [PATCH 04/42] move function def and add missing param --- lib/dynamo/dynamo/jobs.py | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/lib/dynamo/dynamo/jobs.py b/lib/dynamo/dynamo/jobs.py index bd2de9220..12b074dd2 100644 --- a/lib/dynamo/dynamo/jobs.py +++ b/lib/dynamo/dynamo/jobs.py @@ -26,20 +26,6 @@ class InsufficientCreditsError(Exception): """Raised when trying to submit jobs whose total cost exceeds the user's remaining credits.""" -def _get_credit_cost(job: dict, costs: dict) -> float: - job_type = job['job_type'] - cost_definition = costs[job_type] - - if cost_definition.keys() not in ({'cost_parameter', 'cost_table'}, {'default_cost'}): - raise ValueError(f'Cost definition for job type {job_type} has invalid keys: {cost_definition.keys()}') - - if 'cost_parameter' in cost_definition: - parameter_value = job['job_parameters'][cost_definition['cost_parameter']] - return cost_definition['cost_table'][parameter_value] - - return cost_definition['default_cost'] - - def put_jobs(user_id: str, jobs: List[dict], dry_run=False) -> List[dict]: table = DYNAMODB_RESOURCE.Table(environ['JOBS_TABLE_NAME']) request_time = format_time(datetime.now(timezone.utc)) @@ -110,10 +96,24 @@ def _prepare_job_for_database( **DEFAULT_PARAMS_BY_JOB_TYPE[prepared_job['job_type']], **prepared_job.get('job_parameters', {}) } - prepared_job['credit_cost'] = _get_credit_cost(prepared_job) + prepared_job['credit_cost'] = _get_credit_cost(prepared_job, COSTS) return prepared_job +def _get_credit_cost(job: dict, costs: dict) -> float: + job_type = job['job_type'] + cost_definition = costs[job_type] + + if cost_definition.keys() not in ({'cost_parameter', 'cost_table'}, {'default_cost'}): + raise ValueError(f'Cost definition for job type {job_type} has invalid keys: {cost_definition.keys()}') + + if 'cost_parameter' in cost_definition: + parameter_value = job['job_parameters'][cost_definition['cost_parameter']] + return cost_definition['cost_table'][parameter_value] + + return cost_definition['default_cost'] + + def query_jobs(user, start=None, end=None, status_code=None, name=None, job_type=None, start_key=None): table = DYNAMODB_RESOURCE.Table(environ['JOBS_TABLE_NAME']) From 7c26b275dd96792f5783c726335d0d87609d2a06 Mon Sep 17 00:00:00 2001 From: Jake Herrmann Date: Wed, 14 Feb 2024 13:54:29 -0900 Subject: [PATCH 05/42] add TODOs for updating tests --- tests/test_api/test_submit_job.py | 2 ++ tests/test_dynamo/test_jobs.py | 4 ++++ 2 files changed, 6 insertions(+) diff --git a/tests/test_api/test_submit_job.py b/tests/test_api/test_submit_job.py index 1a0a80f05..59d06107f 100644 --- a/tests/test_api/test_submit_job.py +++ b/tests/test_api/test_submit_job.py @@ -112,6 +112,7 @@ def test_submit_many_jobs(client, tables): assert response.status_code == HTTPStatus.BAD_REQUEST +# TODO update for float credits def test_submit_exceeds_remaining_credits(client, tables, monkeypatch): login(client) monkeypatch.setenv('DEFAULT_CREDITS_PER_USER', '25') @@ -375,6 +376,7 @@ def test_submit_mixed_job_parameters(client, tables): assert response.status_code == HTTPStatus.BAD_REQUEST +# TODO update for float credits def test_float_input(client, tables): login(client) batch = [make_job(parameters={'resolution': 30.0})] diff --git a/tests/test_dynamo/test_jobs.py b/tests/test_dynamo/test_jobs.py index f59854da7..f98ef163b 100644 --- a/tests/test_dynamo/test_jobs.py +++ b/tests/test_dynamo/test_jobs.py @@ -246,6 +246,7 @@ def test_get_credit_cost_validate_keys(): dynamo.jobs._get_credit_cost({'job_type': 'JOB_TYPE_F'}, costs) +# TODO update for float credits def test_put_jobs(tables, monkeypatch): monkeypatch.setenv('DEFAULT_CREDITS_PER_USER', '10') payload = [{'name': 'name1'}, {'name': 'name1'}, {'name': 'name2'}] @@ -324,6 +325,7 @@ def test_put_jobs_user_exists(tables): assert tables.users_table.scan()['Items'] == [{'user_id': 'user1', 'remaining_credits': 3}] +# TODO update for float credits def test_put_jobs_insufficient_credits(tables, monkeypatch): monkeypatch.setenv('DEFAULT_CREDITS_PER_USER', '1') payload = [{'name': 'name1'}, {'name': 'name2'}] @@ -371,6 +373,7 @@ def test_put_jobs_priority_override(tables): assert job['priority'] == 550 +# TODO update for float credits def test_put_jobs_priority(tables): tables.users_table.put_item(Item={'user_id': 'user1', 'remaining_credits': 7}) @@ -386,6 +389,7 @@ def test_put_jobs_priority(tables): assert jobs[6]['priority'] == 1 +# TODO update for float credits def test_put_jobs_priority_extra_credits(tables): tables.users_table.put_item(Item={'user_id': 'user1', 'remaining_credits': 10_003}) From e502e543954635dd1dc769c978e9271f282feb96 Mon Sep 17 00:00:00 2001 From: Jake Herrmann Date: Wed, 14 Feb 2024 14:03:32 -0900 Subject: [PATCH 06/42] default to cost of 1 --- lib/dynamo/dynamo/jobs.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/lib/dynamo/dynamo/jobs.py b/lib/dynamo/dynamo/jobs.py index 12b074dd2..e54861708 100644 --- a/lib/dynamo/dynamo/jobs.py +++ b/lib/dynamo/dynamo/jobs.py @@ -13,6 +13,8 @@ costs_file = Path(__file__).parent / 'costs.json' if costs_file.exists(): COSTS = json.loads(costs_file.read_text()) +else: + COSTS = {} default_params_file = Path(__file__).parent / 'default_params_by_job_type.json' if default_params_file.exists(): @@ -101,6 +103,9 @@ def _prepare_job_for_database( def _get_credit_cost(job: dict, costs: dict) -> float: + if not costs: + return 1.0 + job_type = job['job_type'] cost_definition = costs[job_type] From 61f4ea64daeff98c062661c2f4fe8bde64035c47 Mon Sep 17 00:00:00 2001 From: Jake Herrmann Date: Wed, 14 Feb 2024 17:49:37 -0900 Subject: [PATCH 07/42] Add test_put_jobs_costs, handle credits as Decimal, round priority --- lib/dynamo/dynamo/jobs.py | 18 ++++----- lib/dynamo/dynamo/user.py | 6 +-- tests/test_dynamo/test_jobs.py | 68 ++++++++++++++++++++++++++++++++++ 3 files changed, 79 insertions(+), 13 deletions(-) diff --git a/lib/dynamo/dynamo/jobs.py b/lib/dynamo/dynamo/jobs.py index e54861708..a1e571cf0 100644 --- a/lib/dynamo/dynamo/jobs.py +++ b/lib/dynamo/dynamo/jobs.py @@ -1,5 +1,6 @@ import json from datetime import datetime, timezone +from decimal import Decimal from os import environ from pathlib import Path from typing import List, Optional @@ -12,6 +13,7 @@ costs_file = Path(__file__).parent / 'costs.json' if costs_file.exists(): + # TODO convert floats to decimals COSTS = json.loads(costs_file.read_text()) else: COSTS = {} @@ -33,14 +35,10 @@ def put_jobs(user_id: str, jobs: List[dict], dry_run=False) -> List[dict]: request_time = format_time(datetime.now(timezone.utc)) user_record = dynamo.user.get_or_create_user(user_id) - remaining_credits = user_record['remaining_credits'] - if remaining_credits is not None: - remaining_credits = float(remaining_credits) - priority_override = user_record.get('priority_override') - total_cost = 0.0 + total_cost = Decimal(0) prepared_jobs = [] for job in jobs: prepared_job = _prepare_job_for_database( @@ -74,16 +72,16 @@ def _prepare_job_for_database( job: dict, user_id: str, request_time: str, - remaining_credits: Optional[float], + remaining_credits: Optional[Decimal], priority_override: Optional[int], - running_cost: float, + running_cost: Decimal, ) -> dict: if priority_override: priority = priority_override elif remaining_credits is None: priority = 0 else: - priority = min(int(remaining_credits - running_cost), 9999) + priority = min(round(remaining_credits - running_cost), 9999) prepared_job = { 'job_id': str(uuid4()), 'user_id': user_id, @@ -102,9 +100,9 @@ def _prepare_job_for_database( return prepared_job -def _get_credit_cost(job: dict, costs: dict) -> float: +def _get_credit_cost(job: dict, costs: dict) -> Decimal: if not costs: - return 1.0 + return Decimal(1) job_type = job['job_type'] cost_definition = costs[job_type] diff --git a/lib/dynamo/dynamo/user.py b/lib/dynamo/dynamo/user.py index ac5b3c444..ee16b9eea 100644 --- a/lib/dynamo/dynamo/user.py +++ b/lib/dynamo/dynamo/user.py @@ -76,8 +76,8 @@ def _reset_credits_if_needed(user: dict, default_credits: Decimal, current_month return user -def decrement_credits(user_id: str, cost: float) -> None: - if cost <= 0: +def decrement_credits(user_id: str, cost: Decimal) -> None: + if cost <= Decimal(0): raise ValueError(f'Cost {cost} <= 0') users_table = DYNAMODB_RESOURCE.Table(environ['USERS_TABLE_NAME']) try: @@ -85,7 +85,7 @@ def decrement_credits(user_id: str, cost: float) -> None: Key={'user_id': user_id}, UpdateExpression='ADD remaining_credits :delta', ConditionExpression='remaining_credits >= :cost', - ExpressionAttributeValues={':cost': Decimal(cost), ':delta': Decimal(-cost)}, + ExpressionAttributeValues={':cost': cost, ':delta': -cost}, ) except botocore.exceptions.ClientError as e: if e.response['Error']['Code'] == 'ConditionalCheckFailedException': diff --git a/tests/test_dynamo/test_jobs.py b/tests/test_dynamo/test_jobs.py index f98ef163b..351dc3217 100644 --- a/tests/test_dynamo/test_jobs.py +++ b/tests/test_dynamo/test_jobs.py @@ -315,6 +315,74 @@ def test_put_jobs_default_params(tables): assert tables.jobs_table.scan()['Items'] == jobs +def test_put_jobs_costs(tables): + tables.users_table.put_item(Item={'user_id': 'user1', 'remaining_credits': Decimal(100)}) + + costs = { + 'RTC_GAMMA': { + 'cost_parameter': 'resolution', + 'cost_table': { + 30: Decimal('5.0'), + 20: Decimal('15.0'), + 10: Decimal('60.0'), + }, + }, + 'INSAR_ISCE_BURST': { + 'cost_parameter': 'looks', + 'cost_table': { + '20x4': Decimal('0.4'), + '10x2': Decimal('0.7'), + '5x1': Decimal('1.8'), + }, + }, + } + default_params = { + 'RTC_GAMMA': {'resolution': 30}, + 'INSAR_ISCE_BURST': {'looks': '20x4'}, + } + payload = [ + {'job_type': 'RTC_GAMMA', 'job_parameters': {'resolution': 30}}, + {'job_type': 'RTC_GAMMA', 'job_parameters': {'resolution': 20}}, + {'job_type': 'RTC_GAMMA', 'job_parameters': {'resolution': 10}}, + + {'job_type': 'INSAR_ISCE_BURST', 'job_parameters': {'looks': '20x4'}}, + {'job_type': 'INSAR_ISCE_BURST', 'job_parameters': {'looks': '10x2'}}, + {'job_type': 'INSAR_ISCE_BURST', 'job_parameters': {'looks': '5x1'}}, + + {'job_type': 'RTC_GAMMA', 'job_parameters': {}}, + {'job_type': 'INSAR_ISCE_BURST', 'job_parameters': {}}, + ] + with unittest.mock.patch('dynamo.jobs.COSTS', costs), \ + unittest.mock.patch('dynamo.jobs.DEFAULT_PARAMS_BY_JOB_TYPE', default_params): + jobs = dynamo.jobs.put_jobs('user1', payload) + + assert len(jobs) == 8 + + assert jobs[0]['priority'] == 100 + assert jobs[1]['priority'] == 95 + assert jobs[2]['priority'] == 80 + assert jobs[3]['priority'] == 20 + assert jobs[4]['priority'] == 20 + assert jobs[5]['priority'] == 19 + assert jobs[6]['priority'] == 17 + assert jobs[7]['priority'] == 12 + + assert jobs[0]['credit_cost'] == Decimal('5.0') + assert jobs[1]['credit_cost'] == Decimal('15.0') + assert jobs[2]['credit_cost'] == Decimal('60.0') + assert jobs[3]['credit_cost'] == Decimal('0.4') + assert jobs[4]['credit_cost'] == Decimal('0.7') + assert jobs[5]['credit_cost'] == Decimal('1.8') + assert jobs[6]['credit_cost'] == Decimal('5.0') + assert jobs[7]['credit_cost'] == Decimal('0.4') + + assert tables.jobs_table.scan()['Items'] == jobs + + # FIXME: fails because apparently the user record is equal to: + # {'user_id': 'user1', 'remaining_credits': Decimal('11.700000000000003')} + assert tables.users_table.scan()['Items'] == [{'user_id': 'user1', 'remaining_credits': Decimal('11.7')}] + + def test_put_jobs_user_exists(tables): tables.users_table.put_item(Item={'user_id': 'user1', 'remaining_credits': 5}) From 5c847de8478d758ac10ff3f122a6068f911a2a96 Mon Sep 17 00:00:00 2001 From: Jake Herrmann Date: Wed, 14 Feb 2024 18:08:12 -0900 Subject: [PATCH 08/42] fix failing test, add todo --- lib/dynamo/dynamo/jobs.py | 4 ++-- tests/test_dynamo/test_jobs.py | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/dynamo/dynamo/jobs.py b/lib/dynamo/dynamo/jobs.py index a1e571cf0..8c1ae0841 100644 --- a/lib/dynamo/dynamo/jobs.py +++ b/lib/dynamo/dynamo/jobs.py @@ -38,7 +38,7 @@ def put_jobs(user_id: str, jobs: List[dict], dry_run=False) -> List[dict]: remaining_credits = user_record['remaining_credits'] priority_override = user_record.get('priority_override') - total_cost = Decimal(0) + total_cost = Decimal('0.0') prepared_jobs = [] for job in jobs: prepared_job = _prepare_job_for_database( @@ -102,7 +102,7 @@ def _prepare_job_for_database( def _get_credit_cost(job: dict, costs: dict) -> Decimal: if not costs: - return Decimal(1) + return Decimal('1.0') job_type = job['job_type'] cost_definition = costs[job_type] diff --git a/tests/test_dynamo/test_jobs.py b/tests/test_dynamo/test_jobs.py index 351dc3217..2ac34344c 100644 --- a/tests/test_dynamo/test_jobs.py +++ b/tests/test_dynamo/test_jobs.py @@ -315,6 +315,7 @@ def test_put_jobs_default_params(tables): assert tables.jobs_table.scan()['Items'] == jobs +# TODO any other assertions / test cases? def test_put_jobs_costs(tables): tables.users_table.put_item(Item={'user_id': 'user1', 'remaining_credits': Decimal(100)}) From a49b9b8d077eaab6abdff50c8729737e802d1961 Mon Sep 17 00:00:00 2001 From: Jake Herrmann Date: Wed, 14 Feb 2024 18:10:35 -0900 Subject: [PATCH 09/42] update some TODOs --- tests/test_api/test_submit_job.py | 4 ++-- tests/test_dynamo/test_jobs.py | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/tests/test_api/test_submit_job.py b/tests/test_api/test_submit_job.py index 59d06107f..90919199f 100644 --- a/tests/test_api/test_submit_job.py +++ b/tests/test_api/test_submit_job.py @@ -112,7 +112,7 @@ def test_submit_many_jobs(client, tables): assert response.status_code == HTTPStatus.BAD_REQUEST -# TODO update for float credits +# TODO update for fractional credit values def test_submit_exceeds_remaining_credits(client, tables, monkeypatch): login(client) monkeypatch.setenv('DEFAULT_CREDITS_PER_USER', '25') @@ -376,7 +376,7 @@ def test_submit_mixed_job_parameters(client, tables): assert response.status_code == HTTPStatus.BAD_REQUEST -# TODO update for float credits +# TODO update for fractional credit values def test_float_input(client, tables): login(client) batch = [make_job(parameters={'resolution': 30.0})] diff --git a/tests/test_dynamo/test_jobs.py b/tests/test_dynamo/test_jobs.py index 2ac34344c..23145ba5e 100644 --- a/tests/test_dynamo/test_jobs.py +++ b/tests/test_dynamo/test_jobs.py @@ -246,7 +246,7 @@ def test_get_credit_cost_validate_keys(): dynamo.jobs._get_credit_cost({'job_type': 'JOB_TYPE_F'}, costs) -# TODO update for float credits +# TODO update for fractional credit values def test_put_jobs(tables, monkeypatch): monkeypatch.setenv('DEFAULT_CREDITS_PER_USER', '10') payload = [{'name': 'name1'}, {'name': 'name1'}, {'name': 'name2'}] @@ -394,7 +394,7 @@ def test_put_jobs_user_exists(tables): assert tables.users_table.scan()['Items'] == [{'user_id': 'user1', 'remaining_credits': 3}] -# TODO update for float credits +# TODO update for fractional credit values def test_put_jobs_insufficient_credits(tables, monkeypatch): monkeypatch.setenv('DEFAULT_CREDITS_PER_USER', '1') payload = [{'name': 'name1'}, {'name': 'name2'}] @@ -442,7 +442,7 @@ def test_put_jobs_priority_override(tables): assert job['priority'] == 550 -# TODO update for float credits +# TODO update for fractional credit values def test_put_jobs_priority(tables): tables.users_table.put_item(Item={'user_id': 'user1', 'remaining_credits': 7}) @@ -458,7 +458,7 @@ def test_put_jobs_priority(tables): assert jobs[6]['priority'] == 1 -# TODO update for float credits +# TODO update for fractional credit values def test_put_jobs_priority_extra_credits(tables): tables.users_table.put_item(Item={'user_id': 'user1', 'remaining_credits': 10_003}) From fc8701b4d735a5c64bc9e9cd1f84d8e151c6e48f Mon Sep 17 00:00:00 2001 From: Jake Herrmann Date: Fri, 16 Feb 2024 13:49:55 -0900 Subject: [PATCH 10/42] remove some TODOs --- tests/test_dynamo/test_jobs.py | 5 ----- 1 file changed, 5 deletions(-) diff --git a/tests/test_dynamo/test_jobs.py b/tests/test_dynamo/test_jobs.py index 23145ba5e..2c9ac12e4 100644 --- a/tests/test_dynamo/test_jobs.py +++ b/tests/test_dynamo/test_jobs.py @@ -246,7 +246,6 @@ def test_get_credit_cost_validate_keys(): dynamo.jobs._get_credit_cost({'job_type': 'JOB_TYPE_F'}, costs) -# TODO update for fractional credit values def test_put_jobs(tables, monkeypatch): monkeypatch.setenv('DEFAULT_CREDITS_PER_USER', '10') payload = [{'name': 'name1'}, {'name': 'name1'}, {'name': 'name2'}] @@ -315,7 +314,6 @@ def test_put_jobs_default_params(tables): assert tables.jobs_table.scan()['Items'] == jobs -# TODO any other assertions / test cases? def test_put_jobs_costs(tables): tables.users_table.put_item(Item={'user_id': 'user1', 'remaining_credits': Decimal(100)}) @@ -394,7 +392,6 @@ def test_put_jobs_user_exists(tables): assert tables.users_table.scan()['Items'] == [{'user_id': 'user1', 'remaining_credits': 3}] -# TODO update for fractional credit values def test_put_jobs_insufficient_credits(tables, monkeypatch): monkeypatch.setenv('DEFAULT_CREDITS_PER_USER', '1') payload = [{'name': 'name1'}, {'name': 'name2'}] @@ -442,7 +439,6 @@ def test_put_jobs_priority_override(tables): assert job['priority'] == 550 -# TODO update for fractional credit values def test_put_jobs_priority(tables): tables.users_table.put_item(Item={'user_id': 'user1', 'remaining_credits': 7}) @@ -458,7 +454,6 @@ def test_put_jobs_priority(tables): assert jobs[6]['priority'] == 1 -# TODO update for fractional credit values def test_put_jobs_priority_extra_credits(tables): tables.users_table.put_item(Item={'user_id': 'user1', 'remaining_credits': 10_003}) From 74c6620e9c20b8d4873133346fb8659d8426844b Mon Sep 17 00:00:00 2001 From: Jake Herrmann Date: Fri, 16 Feb 2024 13:54:25 -0900 Subject: [PATCH 11/42] remove some more TODOs --- tests/test_api/test_submit_job.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/tests/test_api/test_submit_job.py b/tests/test_api/test_submit_job.py index 90919199f..1a0a80f05 100644 --- a/tests/test_api/test_submit_job.py +++ b/tests/test_api/test_submit_job.py @@ -112,7 +112,6 @@ def test_submit_many_jobs(client, tables): assert response.status_code == HTTPStatus.BAD_REQUEST -# TODO update for fractional credit values def test_submit_exceeds_remaining_credits(client, tables, monkeypatch): login(client) monkeypatch.setenv('DEFAULT_CREDITS_PER_USER', '25') @@ -376,7 +375,6 @@ def test_submit_mixed_job_parameters(client, tables): assert response.status_code == HTTPStatus.BAD_REQUEST -# TODO update for fractional credit values def test_float_input(client, tables): login(client) batch = [make_job(parameters={'resolution': 30.0})] From 3e625abe79b27f32fb94357891809ef6ef17d748 Mon Sep 17 00:00:00 2001 From: Jake Herrmann Date: Fri, 16 Feb 2024 14:07:15 -0900 Subject: [PATCH 12/42] convert float to Decimal in costs dict --- lib/dynamo/dynamo/jobs.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/lib/dynamo/dynamo/jobs.py b/lib/dynamo/dynamo/jobs.py index 8c1ae0841..2a7e15f71 100644 --- a/lib/dynamo/dynamo/jobs.py +++ b/lib/dynamo/dynamo/jobs.py @@ -13,8 +13,7 @@ costs_file = Path(__file__).parent / 'costs.json' if costs_file.exists(): - # TODO convert floats to decimals - COSTS = json.loads(costs_file.read_text()) + COSTS = convert_floats_to_decimals(json.loads(costs_file.read_text())) else: COSTS = {} From 80603b1b08be837893f2e1258b26fbc4b62ab3a4 Mon Sep 17 00:00:00 2001 From: Jake Herrmann Date: Fri, 16 Feb 2024 14:51:32 -0900 Subject: [PATCH 13/42] render cost definitions --- Makefile | 3 ++- apps/render_cf.py | 29 +++++++++++++++++++++++++++++ job_spec/INSAR_ISCE_BURST.yml | 1 + job_spec/RTC_GAMMA.yml | 10 ++++++++++ 4 files changed, 42 insertions(+), 1 deletion(-) diff --git a/Makefile b/Makefile index b0d1ba599..4e00703dc 100644 --- a/Makefile +++ b/Makefile @@ -37,8 +37,9 @@ install: files ?= job_spec/*.yml security_environment ?= ASF api_name ?= local +cost_profile ?= EDC_UAT render: - @echo rendering $(files) for API $(api_name) and security environment $(security_environment); python apps/render_cf.py -j $(files) -s $(security_environment) -n $(api_name) + @echo rendering $(files) for API $(api_name) and security environment $(security_environment); python apps/render_cf.py -j $(files) -s $(security_environment) -n $(api_name) -c $(cost_profile) static: flake8 openapi-validate cfn-lint diff --git a/apps/render_cf.py b/apps/render_cf.py index f180cec2e..caa731fab 100644 --- a/apps/render_cf.py +++ b/apps/render_cf.py @@ -47,11 +47,39 @@ def render_default_params_by_job_type(job_types: dict) -> None: json.dump(default_params_by_job_type, f, indent=2) +def render_costs(job_types: dict, cost_profile: str) -> None: + costs = { + job_type: get_cost_dict(job_spec, cost_profile) + for job_type, job_spec in job_types.items() + } + # FIXME: json requires keys to be strings, so e.g. RTC resolution gets converted to str + with open(Path('lib') / 'dynamo' / 'dynamo' / 'costs.json', 'w') as f: + json.dump(costs, f, indent=2) + + +def get_cost_dict(job_spec: dict, cost_profile: str) -> dict: + keys = job_spec.keys() + + # TODO delete this case after all job specs have cost definitions + if 'cost_parameter' not in keys and 'cost_tables' not in keys and 'default_cost' not in keys: + return {'default_cost': 99999999.0} + + if 'cost_parameter' in keys: + assert 'cost_tables' in keys and 'default_cost' not in keys + return { + 'cost_parameter': job_spec['cost_parameter'], + 'cost_table': job_spec['cost_tables'][cost_profile], + } + assert 'default_cost' in keys and 'cost_tables' not in keys + return {'default_cost': job_spec['default_cost']} + + def main(): parser = argparse.ArgumentParser() parser.add_argument('-j', '--job-spec-files', required=True, nargs='+', type=Path) parser.add_argument('-s', '--security-environment', default='ASF', choices=['ASF', 'EDC', 'JPL', 'JPL-public']) parser.add_argument('-n', '--api-name', required=True) + parser.add_argument('-c', '--cost-profile', required=True) args = parser.parse_args() job_types = {} @@ -63,6 +91,7 @@ def main(): task['name'] = job_type + '_' + task['name'] if task['name'] else job_type render_default_params_by_job_type(job_types) + render_costs(job_types, args.cost_profile) render_templates(job_types, args.security_environment, args.api_name) diff --git a/job_spec/INSAR_ISCE_BURST.yml b/job_spec/INSAR_ISCE_BURST.yml index fb1aa29a6..90fd24f00 100644 --- a/job_spec/INSAR_ISCE_BURST.yml +++ b/job_spec/INSAR_ISCE_BURST.yml @@ -34,6 +34,7 @@ INSAR_ISCE_BURST: - 20x4 - 10x2 - 5x1 + default_cost: 1.0 validators: - check_dem_coverage - check_valid_polarizations diff --git a/job_spec/RTC_GAMMA.yml b/job_spec/RTC_GAMMA.yml index 5949d1e7f..64b80b58f 100644 --- a/job_spec/RTC_GAMMA.yml +++ b/job_spec/RTC_GAMMA.yml @@ -89,6 +89,16 @@ RTC_GAMMA: description: Include a false-color RGB decomposition in the product package for dual-pol granules (ignored for single-pol granules) default: false type: boolean + cost_parameter: resolution + cost_tables: + EDC_UAT: + 30.0: 5.0 + 20.0: 15.0 + 10.0: 60.0 + EDC_PROD: + 30.0: 1.0 + 20.0: 1.0 + 10.0: 1.0 validators: - check_dem_coverage tasks: From 230db0647bbea74bd9e46f99ce31cabf2af0c32b Mon Sep 17 00:00:00 2001 From: Jake Herrmann Date: Sat, 17 Feb 2024 10:00:03 -0900 Subject: [PATCH 14/42] finish basic implementation of cost rendering --- .gitignore | 1 + Makefile | 2 +- apps/render_cf.py | 29 +++++++---------------------- job_spec/INSAR_ISCE_BURST.yml | 4 +++- job_spec/RTC_GAMMA.yml | 17 ++++++++--------- lib/dynamo/dynamo/jobs.py | 9 +++++---- lib/dynamo/setup.py | 2 +- tests/test_dynamo/test_jobs.py | 6 +++--- 8 files changed, 29 insertions(+), 41 deletions(-) diff --git a/.gitignore b/.gitignore index 1967b0af3..f1e057d6a 100644 --- a/.gitignore +++ b/.gitignore @@ -6,6 +6,7 @@ apps/api/src/hyp3_api/job_validation_map.yml apps/step-function.json apps/**/*-cf.yml lib/dynamo/dynamo/*.json +lib/dynamo/dynamo/*.yml # Byte-compiled / optimized / DLL files diff --git a/Makefile b/Makefile index 4e00703dc..0555d5f5c 100644 --- a/Makefile +++ b/Makefile @@ -37,7 +37,7 @@ install: files ?= job_spec/*.yml security_environment ?= ASF api_name ?= local -cost_profile ?= EDC_UAT +cost_profile ?= None render: @echo rendering $(files) for API $(api_name) and security environment $(security_environment); python apps/render_cf.py -j $(files) -s $(security_environment) -n $(api_name) -c $(cost_profile) diff --git a/apps/render_cf.py b/apps/render_cf.py index caa731fab..5bc53cec2 100644 --- a/apps/render_cf.py +++ b/apps/render_cf.py @@ -43,35 +43,19 @@ def render_default_params_by_job_type(job_types: dict) -> None: } for job_type, job_spec in job_types.items() } + # TODO format as yaml for consistency with costs.yml? with open(Path('lib') / 'dynamo' / 'dynamo' / 'default_params_by_job_type.json', 'w') as f: json.dump(default_params_by_job_type, f, indent=2) def render_costs(job_types: dict, cost_profile: str) -> None: costs = { - job_type: get_cost_dict(job_spec, cost_profile) + job_type: job_spec['cost_profiles'][cost_profile] for job_type, job_spec in job_types.items() + if 'cost_profiles' in job_spec # TODO: remove this line after all job specs have cost_profiles } - # FIXME: json requires keys to be strings, so e.g. RTC resolution gets converted to str - with open(Path('lib') / 'dynamo' / 'dynamo' / 'costs.json', 'w') as f: - json.dump(costs, f, indent=2) - - -def get_cost_dict(job_spec: dict, cost_profile: str) -> dict: - keys = job_spec.keys() - - # TODO delete this case after all job specs have cost definitions - if 'cost_parameter' not in keys and 'cost_tables' not in keys and 'default_cost' not in keys: - return {'default_cost': 99999999.0} - - if 'cost_parameter' in keys: - assert 'cost_tables' in keys and 'default_cost' not in keys - return { - 'cost_parameter': job_spec['cost_parameter'], - 'cost_table': job_spec['cost_tables'][cost_profile], - } - assert 'default_cost' in keys and 'cost_tables' not in keys - return {'default_cost': job_spec['default_cost']} + with open(Path('lib') / 'dynamo' / 'dynamo' / 'costs.yml', 'w') as f: + yaml.safe_dump(costs, f) def main(): @@ -91,7 +75,8 @@ def main(): task['name'] = job_type + '_' + task['name'] if task['name'] else job_type render_default_params_by_job_type(job_types) - render_costs(job_types, args.cost_profile) + if args.cost_profile != 'None': + render_costs(job_types, args.cost_profile) render_templates(job_types, args.security_environment, args.api_name) diff --git a/job_spec/INSAR_ISCE_BURST.yml b/job_spec/INSAR_ISCE_BURST.yml index 90fd24f00..848683e0a 100644 --- a/job_spec/INSAR_ISCE_BURST.yml +++ b/job_spec/INSAR_ISCE_BURST.yml @@ -34,7 +34,9 @@ INSAR_ISCE_BURST: - 20x4 - 10x2 - 5x1 - default_cost: 1.0 + cost_profiles: + DEFAULT: + cost: 1.0 validators: - check_dem_coverage - check_valid_polarizations diff --git a/job_spec/RTC_GAMMA.yml b/job_spec/RTC_GAMMA.yml index 64b80b58f..890a98cb2 100644 --- a/job_spec/RTC_GAMMA.yml +++ b/job_spec/RTC_GAMMA.yml @@ -89,16 +89,15 @@ RTC_GAMMA: description: Include a false-color RGB decomposition in the product package for dual-pol granules (ignored for single-pol granules) default: false type: boolean - cost_parameter: resolution - cost_tables: + cost_profiles: EDC_UAT: - 30.0: 5.0 - 20.0: 15.0 - 10.0: 60.0 - EDC_PROD: - 30.0: 1.0 - 20.0: 1.0 - 10.0: 1.0 + cost_parameter: resolution + cost_table: + 30.0: 5.0 + 20.0: 15.0 + 10.0: 60.0 + DEFAULT: + cost: 1.0 validators: - check_dem_coverage tasks: diff --git a/lib/dynamo/dynamo/jobs.py b/lib/dynamo/dynamo/jobs.py index 2a7e15f71..72c416b88 100644 --- a/lib/dynamo/dynamo/jobs.py +++ b/lib/dynamo/dynamo/jobs.py @@ -6,14 +6,15 @@ from typing import List, Optional from uuid import uuid4 +import yaml from boto3.dynamodb.conditions import Attr, Key import dynamo.user from dynamo.util import DYNAMODB_RESOURCE, convert_floats_to_decimals, format_time, get_request_time_expression -costs_file = Path(__file__).parent / 'costs.json' +costs_file = Path(__file__).parent / 'costs.yml' if costs_file.exists(): - COSTS = convert_floats_to_decimals(json.loads(costs_file.read_text())) + COSTS = convert_floats_to_decimals(yaml.safe_load(costs_file.read_text())) else: COSTS = {} @@ -106,14 +107,14 @@ def _get_credit_cost(job: dict, costs: dict) -> Decimal: job_type = job['job_type'] cost_definition = costs[job_type] - if cost_definition.keys() not in ({'cost_parameter', 'cost_table'}, {'default_cost'}): + if cost_definition.keys() not in ({'cost_parameter', 'cost_table'}, {'cost'}): raise ValueError(f'Cost definition for job type {job_type} has invalid keys: {cost_definition.keys()}') if 'cost_parameter' in cost_definition: parameter_value = job['job_parameters'][cost_definition['cost_parameter']] return cost_definition['cost_table'][parameter_value] - return cost_definition['default_cost'] + return cost_definition['cost'] def query_jobs(user, start=None, end=None, status_code=None, name=None, job_type=None, start_key=None): diff --git a/lib/dynamo/setup.py b/lib/dynamo/setup.py index 62d7e31c0..a7db37bba 100644 --- a/lib/dynamo/setup.py +++ b/lib/dynamo/setup.py @@ -13,5 +13,5 @@ packages=find_packages(), - package_data={'dynamo': ['*.json']}, + package_data={'dynamo': ['*.json', '*.yml']}, ) diff --git a/tests/test_dynamo/test_jobs.py b/tests/test_dynamo/test_jobs.py index 2c9ac12e4..15828b877 100644 --- a/tests/test_dynamo/test_jobs.py +++ b/tests/test_dynamo/test_jobs.py @@ -193,7 +193,7 @@ def test_get_credit_cost(): }, }, 'INSAR_ISCE_BURST': { - 'default_cost': 1.0, + 'cost': 1.0, } } assert dynamo.jobs._get_credit_cost( @@ -226,10 +226,10 @@ def test_get_credit_cost(): def test_get_credit_cost_validate_keys(): costs = { 'JOB_TYPE_A': {'cost_parameter': 'foo', 'cost_table': {'bar': 3.0}}, - 'JOB_TYPE_B': {'default_cost': 5.0}, + 'JOB_TYPE_B': {'cost': 5.0}, 'JOB_TYPE_C': {'cost_parameter': ''}, 'JOB_TYPE_D': {'cost_table': {}}, - 'JOB_TYPE_E': {'cost_parameter': '', 'cost_table': {}, 'default_cost': 1.0}, + 'JOB_TYPE_E': {'cost_parameter': '', 'cost_table': {}, 'cost': 1.0}, 'JOB_TYPE_F': {'cost_parameter': '', 'cost_table': {}, 'foo': None}, } From c10ec969f3199cd9ed3c5a9ee9d2ed634f8b650e Mon Sep 17 00:00:00 2001 From: Jake Herrmann Date: Sat, 17 Feb 2024 10:20:52 -0900 Subject: [PATCH 15/42] add cost_profile to deploy-daac --- .github/actions/deploy-hyp3/action.yml | 5 ++++- .github/workflows/deploy-daac.yml | 3 +++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/.github/actions/deploy-hyp3/action.yml b/.github/actions/deploy-hyp3/action.yml index f1c5649b4..6d5fa269d 100644 --- a/.github/actions/deploy-hyp3/action.yml +++ b/.github/actions/deploy-hyp3/action.yml @@ -41,6 +41,9 @@ inputs: RESET_CREDITS_MONTHLY: description: "Whether to reset each user's remaining credits each month" required: true + COST_PROFILE: + description: "Job spec cost profile" + required: true JOB_FILES: description: "Space separated list of job spec YAMLs to include" required: true @@ -83,7 +86,7 @@ runs: run: | pip install --upgrade pip make install - make files='${{ inputs.JOB_FILES }}' security_environment='${{ inputs.SECURITY_ENVIRONMENT }}' api_name='${{ inputs.API_NAME }}' build + make files='${{ inputs.JOB_FILES }}' security_environment='${{ inputs.SECURITY_ENVIRONMENT }}' api_name='${{ inputs.API_NAME }}' cost_profile='${{ inputs.COST_PROFILE }}' build - name: Package and deploy shell: bash run: | diff --git a/.github/workflows/deploy-daac.yml b/.github/workflows/deploy-daac.yml index 749dfde28..b4c676aac 100644 --- a/.github/workflows/deploy-daac.yml +++ b/.github/workflows/deploy-daac.yml @@ -23,6 +23,7 @@ jobs: product_lifetime_in_days: 14 default_credits_per_user: 1000 reset_credits_monthly: true + cost_profile: DEFAULT deploy_ref: refs/heads/main job_files: job_spec/AUTORIFT.yml job_spec/INSAR_GAMMA.yml job_spec/RTC_GAMMA.yml job_spec/INSAR_ISCE_BURST.yml instance_types: r6id.xlarge,r6id.2xlarge,r6id.4xlarge,r6id.8xlarge,r6idn.xlarge,r6idn.2xlarge,r6idn.4xlarge,r6idn.8xlarge @@ -41,6 +42,7 @@ jobs: product_lifetime_in_days: 14 default_credits_per_user: 1000 reset_credits_monthly: true + cost_profile: EDC_UAT deploy_ref: refs/heads/develop job_files: >- job_spec/AUTORIFT.yml @@ -90,6 +92,7 @@ jobs: CLOUDFORMATION_ROLE_ARN: ${{ secrets.CLOUDFORMATION_ROLE_ARN }} DEFAULT_CREDITS_PER_USER: ${{ matrix.default_credits_per_user }} RESET_CREDITS_MONTHLY: ${{ matrix.reset_credits_monthly }} + COST_PROFILE: ${{ matrix.cost_profile }} JOB_FILES: ${{ matrix.job_files }} DEFAULT_MAX_VCPUS: ${{ matrix.default_max_vcpus }} EXPANDED_MAX_VCPUS: ${{ matrix.expanded_max_vcpus }} From 78d2f4bac5a18f7d70ceb0c74c82fc7df621bfff Mon Sep 17 00:00:00 2001 From: Jake Herrmann Date: Sat, 17 Feb 2024 10:26:25 -0900 Subject: [PATCH 16/42] add INSAR_GAMMA cost_profiles --- job_spec/INSAR_GAMMA.yml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/job_spec/INSAR_GAMMA.yml b/job_spec/INSAR_GAMMA.yml index f94228bbe..0f252cdfc 100644 --- a/job_spec/INSAR_GAMMA.yml +++ b/job_spec/INSAR_GAMMA.yml @@ -70,6 +70,14 @@ INSAR_GAMMA: minimum: 0.0 maximum: 1.0 default: 0.6 + cost_profiles: + EDC_UAT: + cost_parameter: looks + cost_table: + 20x4: 10.0 + 10x2: 15.0 + DEFAULT: + cost: 1.0 validators: - check_dem_coverage tasks: From 642b943736884a5096ec331c9e5ef3dac869989a Mon Sep 17 00:00:00 2001 From: Jake Herrmann Date: Sat, 17 Feb 2024 11:37:53 -0900 Subject: [PATCH 17/42] install moto from fork --- requirements-all.txt | 3 ++- tests/test_dynamo/test_jobs.py | 3 --- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/requirements-all.txt b/requirements-all.txt index a61017199..351b08b31 100644 --- a/requirements-all.txt +++ b/requirements-all.txt @@ -7,7 +7,8 @@ -r requirements-apps-update-db.txt boto3==1.34.42 jinja2==3.1.3 -moto[dynamodb]==5.0.1 +# TODO pin moto[dynamodb] to the latest version from PyPI after https://github.com/getmoto/moto/issues/7349 is resolved +moto[dynamodb] @ git+https://github.com/jtherrmann/moto pytest==8.0.0 PyYAML==6.0.1 responses==0.25.0 diff --git a/tests/test_dynamo/test_jobs.py b/tests/test_dynamo/test_jobs.py index 15828b877..2a209a11e 100644 --- a/tests/test_dynamo/test_jobs.py +++ b/tests/test_dynamo/test_jobs.py @@ -376,9 +376,6 @@ def test_put_jobs_costs(tables): assert jobs[7]['credit_cost'] == Decimal('0.4') assert tables.jobs_table.scan()['Items'] == jobs - - # FIXME: fails because apparently the user record is equal to: - # {'user_id': 'user1', 'remaining_credits': Decimal('11.700000000000003')} assert tables.users_table.scan()['Items'] == [{'user_id': 'user1', 'remaining_credits': Decimal('11.7')}] From f10ec5c97750ae9f007fcb845ba1c0f300194ae7 Mon Sep 17 00:00:00 2001 From: Jake Herrmann Date: Mon, 19 Feb 2024 09:38:32 -0900 Subject: [PATCH 18/42] add cost_profile to credits-sandbox --- .github/workflows/deploy-credits-sandbox.yml | 3 ++- job_spec/INSAR_ISCE_BURST.yml | 2 ++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/.github/workflows/deploy-credits-sandbox.yml b/.github/workflows/deploy-credits-sandbox.yml index 4bed2ae9a..ded62d6ef 100644 --- a/.github/workflows/deploy-credits-sandbox.yml +++ b/.github/workflows/deploy-credits-sandbox.yml @@ -21,8 +21,9 @@ jobs: product_lifetime_in_days: 14 default_credits_per_user: 0 reset_credits_monthly: true + cost_profile: EDC_UAT deploy_ref: refs/heads/credits-sandbox - job_files: job_spec/AUTORIFT.yml job_spec/INSAR_GAMMA.yml job_spec/RTC_GAMMA.yml job_spec/INSAR_ISCE_BURST.yml + job_files: job_spec/INSAR_GAMMA.yml job_spec/RTC_GAMMA.yml job_spec/INSAR_ISCE_BURST.yml instance_types: r6id.xlarge,r6id.2xlarge,r6id.4xlarge,r6id.8xlarge,r6idn.xlarge,r6idn.2xlarge,r6idn.4xlarge,r6idn.8xlarge default_max_vcpus: 640 expanded_max_vcpus: 640 diff --git a/job_spec/INSAR_ISCE_BURST.yml b/job_spec/INSAR_ISCE_BURST.yml index 848683e0a..1e149d74b 100644 --- a/job_spec/INSAR_ISCE_BURST.yml +++ b/job_spec/INSAR_ISCE_BURST.yml @@ -35,6 +35,8 @@ INSAR_ISCE_BURST: - 10x2 - 5x1 cost_profiles: + EDC_UAT: + cost: 1.0 DEFAULT: cost: 1.0 validators: From a4999aa846e3851f90fb73ee590fcf08ac472ca2 Mon Sep 17 00:00:00 2001 From: Jake Herrmann Date: Mon, 19 Feb 2024 09:42:00 -0900 Subject: [PATCH 19/42] pass cost profile to deploy action --- .github/workflows/deploy-credits-sandbox.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/deploy-credits-sandbox.yml b/.github/workflows/deploy-credits-sandbox.yml index ded62d6ef..c4e5cf8f5 100644 --- a/.github/workflows/deploy-credits-sandbox.yml +++ b/.github/workflows/deploy-credits-sandbox.yml @@ -65,6 +65,7 @@ jobs: CLOUDFORMATION_ROLE_ARN: ${{ secrets.CLOUDFORMATION_ROLE_ARN }} DEFAULT_CREDITS_PER_USER: ${{ matrix.default_credits_per_user }} RESET_CREDITS_MONTHLY: ${{ matrix.reset_credits_monthly }} + COST_PROFILE: ${{ matrix.cost_profile }} JOB_FILES: ${{ matrix.job_files }} DEFAULT_MAX_VCPUS: ${{ matrix.default_max_vcpus }} EXPANDED_MAX_VCPUS: ${{ matrix.expanded_max_vcpus }} From 4083fea888546648667a5e3ba768b11309814a45 Mon Sep 17 00:00:00 2001 From: Jake Herrmann Date: Mon, 19 Feb 2024 10:22:05 -0900 Subject: [PATCH 20/42] Add /costs API endpoint --- .../src/hyp3_api/api-spec/openapi-spec.yml.j2 | 16 ++++++++++++++++ apps/api/src/hyp3_api/handlers.py | 5 +++++ apps/api/src/hyp3_api/routes.py | 7 +++++++ 3 files changed, 28 insertions(+) diff --git a/apps/api/src/hyp3_api/api-spec/openapi-spec.yml.j2 b/apps/api/src/hyp3_api/api-spec/openapi-spec.yml.j2 index 8ba8d8332..69d3c83e1 100644 --- a/apps/api/src/hyp3_api/api-spec/openapi-spec.yml.j2 +++ b/apps/api/src/hyp3_api/api-spec/openapi-spec.yml.j2 @@ -16,6 +16,17 @@ servers: paths: + /costs: + get: + description: Get table of job costs. + responses: + "200": + description: 200 response + content: + application/json: + schema: + $ref: "#/components/schemas/costs" + /jobs: post: @@ -106,6 +117,11 @@ paths: components: schemas: + # TODO specify more information? + costs: + description: Table of job costs. + type: object + post_jobs_body: description: List for new jobs to submit for processing. type: object diff --git a/apps/api/src/hyp3_api/handlers.py b/apps/api/src/hyp3_api/handlers.py index fd873a67e..c90c76d5e 100644 --- a/apps/api/src/hyp3_api/handlers.py +++ b/apps/api/src/hyp3_api/handlers.py @@ -31,6 +31,11 @@ def is_uuid(val): return True +# TODO tests +def get_costs(): + return dynamo.jobs.COSTS + + def post_jobs(body, user): print(body) diff --git a/apps/api/src/hyp3_api/routes.py b/apps/api/src/hyp3_api/routes.py index 3fdb01204..eadcda3f3 100644 --- a/apps/api/src/hyp3_api/routes.py +++ b/apps/api/src/hyp3_api/routes.py @@ -111,6 +111,13 @@ def __call__(self, errors): ) +# TODO tests +@app.route('/costs', methods=['GET']) +@openapi +def costs_get(): + return jsonify(handlers.get_costs()) + + @app.route('/jobs', methods=['POST']) @openapi def jobs_post(): From e94ba25b009ac378212acc5867154d8b23df1b08 Mon Sep 17 00:00:00 2001 From: Jake Herrmann Date: Mon, 19 Feb 2024 10:39:33 -0900 Subject: [PATCH 21/42] Add AUTORIFT costs --- .github/workflows/deploy-credits-sandbox.yml | 2 +- job_spec/AUTORIFT.yml | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/.github/workflows/deploy-credits-sandbox.yml b/.github/workflows/deploy-credits-sandbox.yml index c4e5cf8f5..3f62f759f 100644 --- a/.github/workflows/deploy-credits-sandbox.yml +++ b/.github/workflows/deploy-credits-sandbox.yml @@ -23,7 +23,7 @@ jobs: reset_credits_monthly: true cost_profile: EDC_UAT deploy_ref: refs/heads/credits-sandbox - job_files: job_spec/INSAR_GAMMA.yml job_spec/RTC_GAMMA.yml job_spec/INSAR_ISCE_BURST.yml + job_files: job_spec/AUTORIFT.yml job_spec/INSAR_GAMMA.yml job_spec/RTC_GAMMA.yml job_spec/INSAR_ISCE_BURST.yml instance_types: r6id.xlarge,r6id.2xlarge,r6id.4xlarge,r6id.8xlarge,r6idn.xlarge,r6idn.2xlarge,r6idn.4xlarge,r6idn.8xlarge default_max_vcpus: 640 expanded_max_vcpus: 640 diff --git a/job_spec/AUTORIFT.yml b/job_spec/AUTORIFT.yml index 96028de0d..c523aac57 100644 --- a/job_spec/AUTORIFT.yml +++ b/job_spec/AUTORIFT.yml @@ -33,6 +33,11 @@ AUTORIFT: example: LC08_L1GT_118112_20210107_20210107_02_T2 bucket_prefix: default: '""' + cost_profiles: + EDC_UAT: + cost: 25.0 + DEFAULT: + cost: 1.0 validators: [] tasks: - name: '' From 591f09c41ac0825f810130bcddcfa570b8728813 Mon Sep 17 00:00:00 2001 From: Jake Herrmann Date: Mon, 19 Feb 2024 14:36:39 -0900 Subject: [PATCH 22/42] upgrade moto --- requirements-all.txt | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/requirements-all.txt b/requirements-all.txt index 351b08b31..e0d898647 100644 --- a/requirements-all.txt +++ b/requirements-all.txt @@ -7,8 +7,7 @@ -r requirements-apps-update-db.txt boto3==1.34.42 jinja2==3.1.3 -# TODO pin moto[dynamodb] to the latest version from PyPI after https://github.com/getmoto/moto/issues/7349 is resolved -moto[dynamodb] @ git+https://github.com/jtherrmann/moto +moto[dynamodb]==5.0.3.dev5 pytest==8.0.0 PyYAML==6.0.1 responses==0.25.0 From 1073364c17c68d50dc1835ed29897d1de8afa9c1 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 23 Feb 2024 19:45:53 +0000 Subject: [PATCH 23/42] Bump boto3 from 1.34.47 to 1.34.48 Bumps [boto3](https://github.com/boto/boto3) from 1.34.47 to 1.34.48. - [Release notes](https://github.com/boto/boto3/releases) - [Changelog](https://github.com/boto/boto3/blob/develop/CHANGELOG.rst) - [Commits](https://github.com/boto/boto3/compare/1.34.47...1.34.48) --- updated-dependencies: - dependency-name: boto3 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- requirements-all.txt | 2 +- requirements-apps-disable-private-dns.txt | 2 +- requirements-apps-start-execution-manager.txt | 2 +- requirements-apps-start-execution-worker.txt | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/requirements-all.txt b/requirements-all.txt index 47e5c56aa..f4a24ef3b 100644 --- a/requirements-all.txt +++ b/requirements-all.txt @@ -5,7 +5,7 @@ -r requirements-apps-start-execution-worker.txt -r requirements-apps-disable-private-dns.txt -r requirements-apps-update-db.txt -boto3==1.34.47 +boto3==1.34.48 jinja2==3.1.3 moto[dynamodb]==5.0.2 pytest==8.0.1 diff --git a/requirements-apps-disable-private-dns.txt b/requirements-apps-disable-private-dns.txt index 20cc58b75..7fa5f1b16 100644 --- a/requirements-apps-disable-private-dns.txt +++ b/requirements-apps-disable-private-dns.txt @@ -1 +1 @@ -boto3==1.34.47 +boto3==1.34.48 diff --git a/requirements-apps-start-execution-manager.txt b/requirements-apps-start-execution-manager.txt index d03787992..7589f65aa 100644 --- a/requirements-apps-start-execution-manager.txt +++ b/requirements-apps-start-execution-manager.txt @@ -1,3 +1,3 @@ -boto3==1.34.47 +boto3==1.34.48 ./lib/dynamo/ ./lib/lambda_logging/ diff --git a/requirements-apps-start-execution-worker.txt b/requirements-apps-start-execution-worker.txt index 7217575b7..8a62963c6 100644 --- a/requirements-apps-start-execution-worker.txt +++ b/requirements-apps-start-execution-worker.txt @@ -1,2 +1,2 @@ -boto3==1.34.47 +boto3==1.34.48 ./lib/lambda_logging/ From 140a8c3242f0f25744158355a258f8fe3a983308 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 23 Feb 2024 19:45:59 +0000 Subject: [PATCH 24/42] Bump setuptools from 69.1.0 to 69.1.1 Bumps [setuptools](https://github.com/pypa/setuptools) from 69.1.0 to 69.1.1. - [Release notes](https://github.com/pypa/setuptools/releases) - [Changelog](https://github.com/pypa/setuptools/blob/main/NEWS.rst) - [Commits](https://github.com/pypa/setuptools/compare/v69.1.0...v69.1.1) --- updated-dependencies: - dependency-name: setuptools dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- requirements-all.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-all.txt b/requirements-all.txt index 47e5c56aa..b6cdd8484 100644 --- a/requirements-all.txt +++ b/requirements-all.txt @@ -15,6 +15,6 @@ flake8==7.0.0 flake8-import-order==0.18.2 flake8-blind-except==0.2.1 flake8-builtins==2.2.0 -setuptools==69.1.0 +setuptools==69.1.1 openapi-spec-validator==0.7.1 cfn-lint==0.85.2 From bf2932b0f554b947572b265efc444830ed19c31b Mon Sep 17 00:00:00 2001 From: Andrew Johnston Date: Fri, 23 Feb 2024 16:02:57 -0900 Subject: [PATCH 25/42] add DEFAULT cost profile to all job specs --- job_spec/AUTORIFT_ITS_LIVE.yml | 3 +++ job_spec/AUTORIFT_ITS_LIVE_TEST.yml | 3 +++ job_spec/INSAR_ISCE.yml | 3 +++ job_spec/INSAR_ISCE_TEST.yml | 3 +++ job_spec/S1_CORRECTION_ITS_LIVE.yml | 3 +++ job_spec/WATER_MAP.yml | 3 +++ job_spec/WATER_MAP_EQ.yml | 3 +++ 7 files changed, 21 insertions(+) diff --git a/job_spec/AUTORIFT_ITS_LIVE.yml b/job_spec/AUTORIFT_ITS_LIVE.yml index d2d802328..5358a3190 100644 --- a/job_spec/AUTORIFT_ITS_LIVE.yml +++ b/job_spec/AUTORIFT_ITS_LIVE.yml @@ -38,6 +38,9 @@ AUTORIFT: description: Shapefile for determining the correct search parameters by geographic location. Path to shapefile must be understood by GDAL. type: string default: '/vsicurl/http://its-live-data.s3.amazonaws.com/autorift_parameters/v001/autorift_landice_0120m.shp' + cost_profiles: + DEFAULT: + cost: 1.0 validators: [] tasks: - name: '' diff --git a/job_spec/AUTORIFT_ITS_LIVE_TEST.yml b/job_spec/AUTORIFT_ITS_LIVE_TEST.yml index b4d11c34a..e8b4c18ee 100644 --- a/job_spec/AUTORIFT_ITS_LIVE_TEST.yml +++ b/job_spec/AUTORIFT_ITS_LIVE_TEST.yml @@ -38,6 +38,9 @@ AUTORIFT_TEST: description: Shapefile for determining the correct search parameters by geographic location. Path to shapefile must be understood by GDAL. type: string default: '/vsicurl/http://its-live-data.s3.amazonaws.com/autorift_parameters/v001/autorift_landice_0120m.shp' + cost_profiles: + DEFAULT: + cost: 1.0 validators: [] tasks: - name: '' diff --git a/job_spec/INSAR_ISCE.yml b/job_spec/INSAR_ISCE.yml index adcc5691a..404cdd64e 100644 --- a/job_spec/INSAR_ISCE.yml +++ b/job_spec/INSAR_ISCE.yml @@ -64,6 +64,9 @@ INSAR_ISCE: - HRRR bucket_prefix: default: '""' + cost_profiles: + DEFAULT: + cost: 1.0 validators: [] tasks: - name: '' diff --git a/job_spec/INSAR_ISCE_TEST.yml b/job_spec/INSAR_ISCE_TEST.yml index 18057a7ed..ad0567024 100644 --- a/job_spec/INSAR_ISCE_TEST.yml +++ b/job_spec/INSAR_ISCE_TEST.yml @@ -88,6 +88,9 @@ INSAR_ISCE_TEST: - HRRR bucket_prefix: default: '""' + cost_profiles: + DEFAULT: + cost: 1.0 validators: [] tasks: - name: '' diff --git a/job_spec/S1_CORRECTION_ITS_LIVE.yml b/job_spec/S1_CORRECTION_ITS_LIVE.yml index 25ee95156..bb92f25b8 100644 --- a/job_spec/S1_CORRECTION_ITS_LIVE.yml +++ b/job_spec/S1_CORRECTION_ITS_LIVE.yml @@ -25,6 +25,9 @@ S1_CORRECTION_TEST: minimum: 0 bucket_prefix: default: '""' + cost_profiles: + DEFAULT: + cost: 1.0 validators: [] tasks: - name: '' diff --git a/job_spec/WATER_MAP.yml b/job_spec/WATER_MAP.yml index 512330dd9..ae9cd6e54 100644 --- a/job_spec/WATER_MAP.yml +++ b/job_spec/WATER_MAP.yml @@ -111,6 +111,9 @@ WATER_MAP: enum: - fmi - ts + cost_profiles: + DEFAULT: + cost: 1.0 validators: - check_dem_coverage tasks: diff --git a/job_spec/WATER_MAP_EQ.yml b/job_spec/WATER_MAP_EQ.yml index 9be1ec357..c251747a2 100644 --- a/job_spec/WATER_MAP_EQ.yml +++ b/job_spec/WATER_MAP_EQ.yml @@ -53,6 +53,9 @@ WATER_MAP_EQ: description: The minimum fraction of valid HAND pixels required in a tile for thresholding default: 0.8 type: number + cost_profiles: + DEFAULT: + cost: 1.0 validators: - check_dem_coverage tasks: From 98c2107a648974ef63b1b32cb80ad7af80c39ed7 Mon Sep 17 00:00:00 2001 From: Jake Herrmann Date: Fri, 23 Feb 2024 16:30:31 -0900 Subject: [PATCH 26/42] require cost_profile to be set to something --- .github/workflows/deploy-credits-sandbox.yml | 2 +- .github/workflows/deploy-daac.yml | 2 +- Makefile | 2 +- apps/render_cf.py | 6 ++---- job_spec/AUTORIFT.yml | 2 +- job_spec/INSAR_GAMMA.yml | 2 +- job_spec/INSAR_ISCE_BURST.yml | 2 +- job_spec/RTC_GAMMA.yml | 2 +- lib/dynamo/dynamo/jobs.py | 12 ++++-------- tests/test_dynamo/test_jobs.py | 8 +++++++- 10 files changed, 20 insertions(+), 20 deletions(-) diff --git a/.github/workflows/deploy-credits-sandbox.yml b/.github/workflows/deploy-credits-sandbox.yml index 3f62f759f..994b2299d 100644 --- a/.github/workflows/deploy-credits-sandbox.yml +++ b/.github/workflows/deploy-credits-sandbox.yml @@ -21,7 +21,7 @@ jobs: product_lifetime_in_days: 14 default_credits_per_user: 0 reset_credits_monthly: true - cost_profile: EDC_UAT + cost_profile: EDC deploy_ref: refs/heads/credits-sandbox job_files: job_spec/AUTORIFT.yml job_spec/INSAR_GAMMA.yml job_spec/RTC_GAMMA.yml job_spec/INSAR_ISCE_BURST.yml instance_types: r6id.xlarge,r6id.2xlarge,r6id.4xlarge,r6id.8xlarge,r6idn.xlarge,r6idn.2xlarge,r6idn.4xlarge,r6idn.8xlarge diff --git a/.github/workflows/deploy-daac.yml b/.github/workflows/deploy-daac.yml index b4c676aac..dfd29d924 100644 --- a/.github/workflows/deploy-daac.yml +++ b/.github/workflows/deploy-daac.yml @@ -42,7 +42,7 @@ jobs: product_lifetime_in_days: 14 default_credits_per_user: 1000 reset_credits_monthly: true - cost_profile: EDC_UAT + cost_profile: EDC deploy_ref: refs/heads/develop job_files: >- job_spec/AUTORIFT.yml diff --git a/Makefile b/Makefile index 0555d5f5c..c97f2c118 100644 --- a/Makefile +++ b/Makefile @@ -37,7 +37,7 @@ install: files ?= job_spec/*.yml security_environment ?= ASF api_name ?= local -cost_profile ?= None +cost_profile ?= DEFAULT render: @echo rendering $(files) for API $(api_name) and security environment $(security_environment); python apps/render_cf.py -j $(files) -s $(security_environment) -n $(api_name) -c $(cost_profile) diff --git a/apps/render_cf.py b/apps/render_cf.py index 5bc53cec2..45d5cc1cd 100644 --- a/apps/render_cf.py +++ b/apps/render_cf.py @@ -52,7 +52,6 @@ def render_costs(job_types: dict, cost_profile: str) -> None: costs = { job_type: job_spec['cost_profiles'][cost_profile] for job_type, job_spec in job_types.items() - if 'cost_profiles' in job_spec # TODO: remove this line after all job specs have cost_profiles } with open(Path('lib') / 'dynamo' / 'dynamo' / 'costs.yml', 'w') as f: yaml.safe_dump(costs, f) @@ -63,7 +62,7 @@ def main(): parser.add_argument('-j', '--job-spec-files', required=True, nargs='+', type=Path) parser.add_argument('-s', '--security-environment', default='ASF', choices=['ASF', 'EDC', 'JPL', 'JPL-public']) parser.add_argument('-n', '--api-name', required=True) - parser.add_argument('-c', '--cost-profile', required=True) + parser.add_argument('-c', '--cost-profile', default='DEFAULT', choices=['DEFAULT', 'EDC']) args = parser.parse_args() job_types = {} @@ -75,8 +74,7 @@ def main(): task['name'] = job_type + '_' + task['name'] if task['name'] else job_type render_default_params_by_job_type(job_types) - if args.cost_profile != 'None': - render_costs(job_types, args.cost_profile) + render_costs(job_types, args.cost_profile) render_templates(job_types, args.security_environment, args.api_name) diff --git a/job_spec/AUTORIFT.yml b/job_spec/AUTORIFT.yml index c523aac57..b14493542 100644 --- a/job_spec/AUTORIFT.yml +++ b/job_spec/AUTORIFT.yml @@ -34,7 +34,7 @@ AUTORIFT: bucket_prefix: default: '""' cost_profiles: - EDC_UAT: + EDC: cost: 25.0 DEFAULT: cost: 1.0 diff --git a/job_spec/INSAR_GAMMA.yml b/job_spec/INSAR_GAMMA.yml index 0f252cdfc..6b662b4d6 100644 --- a/job_spec/INSAR_GAMMA.yml +++ b/job_spec/INSAR_GAMMA.yml @@ -71,7 +71,7 @@ INSAR_GAMMA: maximum: 1.0 default: 0.6 cost_profiles: - EDC_UAT: + EDC: cost_parameter: looks cost_table: 20x4: 10.0 diff --git a/job_spec/INSAR_ISCE_BURST.yml b/job_spec/INSAR_ISCE_BURST.yml index 1e149d74b..15f2b59a5 100644 --- a/job_spec/INSAR_ISCE_BURST.yml +++ b/job_spec/INSAR_ISCE_BURST.yml @@ -35,7 +35,7 @@ INSAR_ISCE_BURST: - 10x2 - 5x1 cost_profiles: - EDC_UAT: + EDC: cost: 1.0 DEFAULT: cost: 1.0 diff --git a/job_spec/RTC_GAMMA.yml b/job_spec/RTC_GAMMA.yml index 890a98cb2..27a77e176 100644 --- a/job_spec/RTC_GAMMA.yml +++ b/job_spec/RTC_GAMMA.yml @@ -90,7 +90,7 @@ RTC_GAMMA: default: false type: boolean cost_profiles: - EDC_UAT: + EDC: cost_parameter: resolution cost_table: 30.0: 5.0 diff --git a/lib/dynamo/dynamo/jobs.py b/lib/dynamo/dynamo/jobs.py index 72c416b88..9187369f1 100644 --- a/lib/dynamo/dynamo/jobs.py +++ b/lib/dynamo/dynamo/jobs.py @@ -13,10 +13,7 @@ from dynamo.util import DYNAMODB_RESOURCE, convert_floats_to_decimals, format_time, get_request_time_expression costs_file = Path(__file__).parent / 'costs.yml' -if costs_file.exists(): - COSTS = convert_floats_to_decimals(yaml.safe_load(costs_file.read_text())) -else: - COSTS = {} +COSTS = convert_floats_to_decimals(yaml.safe_load(costs_file.read_text())) default_params_file = Path(__file__).parent / 'default_params_by_job_type.json' if default_params_file.exists(): @@ -96,14 +93,13 @@ def _prepare_job_for_database( **DEFAULT_PARAMS_BY_JOB_TYPE[prepared_job['job_type']], **prepared_job.get('job_parameters', {}) } - prepared_job['credit_cost'] = _get_credit_cost(prepared_job, COSTS) + prepared_job['credit_cost'] = _get_credit_cost(prepared_job, COSTS) + else: + prepared_job['credit_cost'] = Decimal('1.0') return prepared_job def _get_credit_cost(job: dict, costs: dict) -> Decimal: - if not costs: - return Decimal('1.0') - job_type = job['job_type'] cost_definition = costs[job_type] diff --git a/tests/test_dynamo/test_jobs.py b/tests/test_dynamo/test_jobs.py index 2a209a11e..06f69c539 100644 --- a/tests/test_dynamo/test_jobs.py +++ b/tests/test_dynamo/test_jobs.py @@ -281,6 +281,11 @@ def test_put_jobs_default_params(tables): 'JOB_TYPE_B': {'b1': 'b1_default'}, 'JOB_TYPE_C': {}, } + costs = { + 'JOB_TYPE_A': {'cost': Decimal('1.0')}, + 'JOB_TYPE_B': {'cost': Decimal('1.0')}, + 'JOB_TYPE_C': {'cost': Decimal('1.0')}, + } payload = [ {}, {'job_type': 'JOB_TYPE_A'}, @@ -295,7 +300,8 @@ def test_put_jobs_default_params(tables): {'job_type': 'JOB_TYPE_C', 'job_parameters': {'c1': 'foo'}}, {'job_parameters': {'n1': 'foo'}}, ] - with unittest.mock.patch('dynamo.jobs.DEFAULT_PARAMS_BY_JOB_TYPE', default_params): + with unittest.mock.patch('dynamo.jobs.DEFAULT_PARAMS_BY_JOB_TYPE', default_params),\ + unittest.mock.patch('dynamo.jobs.COSTS', costs): jobs = dynamo.jobs.put_jobs('user1', payload) assert 'job_parameters' not in jobs[0] From 9c617b67c6e5f4f388f060a46cee4b75b0b69c24 Mon Sep 17 00:00:00 2001 From: Jake Herrmann Date: Fri, 23 Feb 2024 16:39:28 -0900 Subject: [PATCH 27/42] add cost_profile to all deployments --- .github/workflows/deploy-enterprise-test.yml | 1 + .github/workflows/deploy-enterprise.yml | 12 ++++++++++++ 2 files changed, 13 insertions(+) diff --git a/.github/workflows/deploy-enterprise-test.yml b/.github/workflows/deploy-enterprise-test.yml index 0bd80a138..fd7b38c31 100644 --- a/.github/workflows/deploy-enterprise-test.yml +++ b/.github/workflows/deploy-enterprise-test.yml @@ -21,6 +21,7 @@ jobs: product_lifetime_in_days: 14 default_credits_per_user: 0 reset_credits_monthly: false + cost_profile: DEFAULT deploy_ref: refs/heads/develop job_files: >- job_spec/AUTORIFT_ITS_LIVE.yml diff --git a/.github/workflows/deploy-enterprise.yml b/.github/workflows/deploy-enterprise.yml index 4104e3948..b4ef7a03f 100644 --- a/.github/workflows/deploy-enterprise.yml +++ b/.github/workflows/deploy-enterprise.yml @@ -21,6 +21,7 @@ jobs: product_lifetime_in_days: 45 default_credits_per_user: 0 reset_credits_monthly: true + cost_profile: DEFAULT job_files: >- job_spec/AUTORIFT_ITS_LIVE.yml job_spec/AUTORIFT_ITS_LIVE_TEST.yml @@ -40,6 +41,7 @@ jobs: product_lifetime_in_days: 180 default_credits_per_user: 0 reset_credits_monthly: true + cost_profile: DEFAULT job_files: job_spec/INSAR_ISCE.yml job_spec/INSAR_ISCE_TEST.yml instance_types: c6id.xlarge,c6id.2xlarge,c6id.4xlarge,c6id.8xlarge default_max_vcpus: 10000 @@ -56,6 +58,7 @@ jobs: product_lifetime_in_days: 30 default_credits_per_user: 0 reset_credits_monthly: true + cost_profile: DEFAULT job_files: job_spec/INSAR_ISCE.yml job_spec/INSAR_ISCE_TEST.yml instance_types: c6id.xlarge,c6id.2xlarge,c6id.4xlarge,c6id.8xlarge default_max_vcpus: 10000 @@ -72,6 +75,7 @@ jobs: product_lifetime_in_days: 14 default_credits_per_user: 0 reset_credits_monthly: true + cost_profile: DEFAULT job_files: job_spec/INSAR_ISCE.yml job_spec/INSAR_ISCE_TEST.yml instance_types: c6id.xlarge,c6id.2xlarge,c6id.4xlarge,c6id.8xlarge default_max_vcpus: 1600 @@ -88,6 +92,7 @@ jobs: product_lifetime_in_days: 365000 default_credits_per_user: 0 reset_credits_monthly: true + cost_profile: DEFAULT job_files: job_spec/INSAR_GAMMA.yml instance_types: r6id.xlarge,r6id.2xlarge,r6id.4xlarge,r6id.8xlarge,r6idn.xlarge,r6idn.2xlarge,r6idn.4xlarge,r6idn.8xlarge default_max_vcpus: 640 @@ -104,6 +109,7 @@ jobs: product_lifetime_in_days: 14 default_credits_per_user: 0 reset_credits_monthly: true + cost_profile: DEFAULT job_files: job_spec/RTC_GAMMA.yml job_spec/WATER_MAP.yml job_spec/WATER_MAP_EQ.yml instance_types: r6id.xlarge,r6id.2xlarge,r6id.4xlarge,r6id.8xlarge,r6idn.xlarge,r6idn.2xlarge,r6idn.4xlarge,r6idn.8xlarge default_max_vcpus: 640 @@ -120,6 +126,7 @@ jobs: product_lifetime_in_days: 90 default_credits_per_user: 0 reset_credits_monthly: true + cost_profile: DEFAULT job_files: job_spec/RTC_GAMMA.yml job_spec/WATER_MAP.yml job_spec/WATER_MAP_EQ.yml instance_types: r6id.xlarge,r6id.2xlarge,r6id.4xlarge,r6id.8xlarge,r6idn.xlarge,r6idn.2xlarge,r6idn.4xlarge,r6idn.8xlarge default_max_vcpus: 1600 @@ -136,6 +143,7 @@ jobs: product_lifetime_in_days: 30 default_credits_per_user: 0 reset_credits_monthly: true + cost_profile: DEFAULT job_files: job_spec/INSAR_GAMMA.yml job_spec/INSAR_ISCE_BURST.yml instance_types: r6id.xlarge,r6id.2xlarge,r6id.4xlarge,r6id.8xlarge,r6idn.xlarge,r6idn.2xlarge,r6idn.4xlarge,r6idn.8xlarge default_max_vcpus: 640 @@ -152,6 +160,7 @@ jobs: product_lifetime_in_days: 14 default_credits_per_user: 0 reset_credits_monthly: true + cost_profile: DEFAULT job_files: job_spec/INSAR_GAMMA.yml job_spec/RTC_GAMMA.yml instance_types: r6id.xlarge,r6id.2xlarge,r6id.4xlarge,r6id.8xlarge,r6idn.xlarge,r6idn.2xlarge,r6idn.4xlarge,r6idn.8xlarge default_max_vcpus: 640 @@ -168,6 +177,7 @@ jobs: product_lifetime_in_days: 14 default_credits_per_user: 0 reset_credits_monthly: true + cost_profile: DEFAULT job_files: job_spec/INSAR_GAMMA.yml job_spec/RTC_GAMMA.yml instance_types: r6id.xlarge,r6id.2xlarge,r6id.4xlarge,r6id.8xlarge,r6idn.xlarge,r6idn.2xlarge,r6idn.4xlarge,r6idn.8xlarge default_max_vcpus: 640 @@ -184,6 +194,7 @@ jobs: product_lifetime_in_days: 30 default_credits_per_user: 0 reset_credits_monthly: true + cost_profile: DEFAULT job_files: job_spec/INSAR_GAMMA.yml job_spec/RTC_GAMMA.yml instance_types: r6id.xlarge,r6id.2xlarge,r6id.4xlarge,r6id.8xlarge,r6idn.xlarge,r6idn.2xlarge,r6idn.4xlarge,r6idn.8xlarge default_max_vcpus: 640 @@ -202,6 +213,7 @@ jobs: product_lifetime_in_days: 14 default_credits_per_user: 0 reset_credits_monthly: true + cost_profile: DEFAULT job_files: job_spec/WATER_MAP.yml instance_types: r6id.xlarge,r6id.2xlarge,r6id.4xlarge,r6id.8xlarge,r6idn.xlarge,r6idn.2xlarge,r6idn.4xlarge,r6idn.8xlarge default_max_vcpus: 640 From 3736a2420a00cc0f4fb46bb3c329a0e00aa4dd89 Mon Sep 17 00:00:00 2001 From: Andrew Johnston Date: Fri, 23 Feb 2024 16:44:15 -0900 Subject: [PATCH 28/42] fix whitespace for flake8 --- tests/test_dynamo/test_jobs.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_dynamo/test_jobs.py b/tests/test_dynamo/test_jobs.py index 06f69c539..46cf68334 100644 --- a/tests/test_dynamo/test_jobs.py +++ b/tests/test_dynamo/test_jobs.py @@ -300,7 +300,7 @@ def test_put_jobs_default_params(tables): {'job_type': 'JOB_TYPE_C', 'job_parameters': {'c1': 'foo'}}, {'job_parameters': {'n1': 'foo'}}, ] - with unittest.mock.patch('dynamo.jobs.DEFAULT_PARAMS_BY_JOB_TYPE', default_params),\ + with unittest.mock.patch('dynamo.jobs.DEFAULT_PARAMS_BY_JOB_TYPE', default_params), \ unittest.mock.patch('dynamo.jobs.COSTS', costs): jobs = dynamo.jobs.put_jobs('user1', payload) From 6556b3b599d166fc4da4b0e4035ebaac97d3d737 Mon Sep 17 00:00:00 2001 From: Jake Herrmann Date: Fri, 23 Feb 2024 17:05:55 -0900 Subject: [PATCH 29/42] add test for costs endpoint --- apps/api/src/hyp3_api/handlers.py | 5 ----- apps/api/src/hyp3_api/routes.py | 5 ++++- tests/test_api/conftest.py | 1 + tests/test_api/test_get_costs.py | 14 ++++++++++++++ 4 files changed, 19 insertions(+), 6 deletions(-) create mode 100644 tests/test_api/test_get_costs.py diff --git a/apps/api/src/hyp3_api/handlers.py b/apps/api/src/hyp3_api/handlers.py index c90c76d5e..fd873a67e 100644 --- a/apps/api/src/hyp3_api/handlers.py +++ b/apps/api/src/hyp3_api/handlers.py @@ -31,11 +31,6 @@ def is_uuid(val): return True -# TODO tests -def get_costs(): - return dynamo.jobs.COSTS - - def post_jobs(body, user): print(body) diff --git a/apps/api/src/hyp3_api/routes.py b/apps/api/src/hyp3_api/routes.py index eadcda3f3..eb2e27158 100644 --- a/apps/api/src/hyp3_api/routes.py +++ b/apps/api/src/hyp3_api/routes.py @@ -11,6 +11,7 @@ from openapi_core.contrib.flask.decorators import FlaskOpenAPIViewDecorator from openapi_core.contrib.flask.handlers import FlaskOpenAPIErrorsHandler +import dynamo from hyp3_api import app, auth, handlers from hyp3_api.openapi import get_spec_yaml @@ -112,10 +113,12 @@ def __call__(self, errors): # TODO tests +# TODO what are the implications of not explicitly handling Decimal type? +# jsonify automatically converts them to float? @app.route('/costs', methods=['GET']) @openapi def costs_get(): - return jsonify(handlers.get_costs()) + return jsonify(dynamo.jobs.COSTS) @app.route('/jobs', methods=['POST']) diff --git a/tests/test_api/conftest.py b/tests/test_api/conftest.py index 464c68cb5..202a57a26 100644 --- a/tests/test_api/conftest.py +++ b/tests/test_api/conftest.py @@ -8,6 +8,7 @@ from hyp3_api.util import get_granules AUTH_COOKIE = 'asf-urs' +COSTS_URI = '/costs' JOBS_URI = '/jobs' USER_URI = '/user' diff --git a/tests/test_api/test_get_costs.py b/tests/test_api/test_get_costs.py new file mode 100644 index 000000000..a1756c660 --- /dev/null +++ b/tests/test_api/test_get_costs.py @@ -0,0 +1,14 @@ +import unittest.mock +from decimal import Decimal +from http import HTTPStatus + +from test_api.conftest import COSTS_URI + + +def test_get_costs(client): + costs = {'foo': Decimal('1.3')} + with unittest.mock.patch('dynamo.jobs.COSTS', costs): + response = client.get(COSTS_URI) + + assert response.status_code == HTTPStatus.OK + assert response.json == {'foo': 1.3} From 21329d6d3dcba4dee7f88142281193f7cd086c8d Mon Sep 17 00:00:00 2001 From: Jake Herrmann Date: Fri, 23 Feb 2024 17:12:08 -0900 Subject: [PATCH 30/42] update todos --- apps/api/src/hyp3_api/routes.py | 2 +- tests/test_api/test_get_costs.py | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/apps/api/src/hyp3_api/routes.py b/apps/api/src/hyp3_api/routes.py index eb2e27158..cf55f607e 100644 --- a/apps/api/src/hyp3_api/routes.py +++ b/apps/api/src/hyp3_api/routes.py @@ -112,9 +112,9 @@ def __call__(self, errors): ) -# TODO tests # TODO what are the implications of not explicitly handling Decimal type? # jsonify automatically converts them to float? +# TODO don't require authentication @app.route('/costs', methods=['GET']) @openapi def costs_get(): diff --git a/tests/test_api/test_get_costs.py b/tests/test_api/test_get_costs.py index a1756c660..f5ab8f3a2 100644 --- a/tests/test_api/test_get_costs.py +++ b/tests/test_api/test_get_costs.py @@ -5,6 +5,7 @@ from test_api.conftest import COSTS_URI +# FIXME fails with 403 def test_get_costs(client): costs = {'foo': Decimal('1.3')} with unittest.mock.patch('dynamo.jobs.COSTS', costs): From 1555d0147f2c4d8f736bfa6fb1f771867ed36edd Mon Sep 17 00:00:00 2001 From: Jake Herrmann Date: Fri, 23 Feb 2024 17:17:29 -0900 Subject: [PATCH 31/42] no auth costs endpoint --- apps/api/src/hyp3_api/routes.py | 2 -- tests/test_api/test_get_costs.py | 1 - 2 files changed, 3 deletions(-) diff --git a/apps/api/src/hyp3_api/routes.py b/apps/api/src/hyp3_api/routes.py index cf55f607e..a026cd16b 100644 --- a/apps/api/src/hyp3_api/routes.py +++ b/apps/api/src/hyp3_api/routes.py @@ -114,9 +114,7 @@ def __call__(self, errors): # TODO what are the implications of not explicitly handling Decimal type? # jsonify automatically converts them to float? -# TODO don't require authentication @app.route('/costs', methods=['GET']) -@openapi def costs_get(): return jsonify(dynamo.jobs.COSTS) diff --git a/tests/test_api/test_get_costs.py b/tests/test_api/test_get_costs.py index f5ab8f3a2..a1756c660 100644 --- a/tests/test_api/test_get_costs.py +++ b/tests/test_api/test_get_costs.py @@ -5,7 +5,6 @@ from test_api.conftest import COSTS_URI -# FIXME fails with 403 def test_get_costs(client): costs = {'foo': Decimal('1.3')} with unittest.mock.patch('dynamo.jobs.COSTS', costs): From 85dfeeabb8448a330ac2a7980fff9bd9e58c1acb Mon Sep 17 00:00:00 2001 From: Jake Herrmann Date: Fri, 23 Feb 2024 17:18:54 -0900 Subject: [PATCH 32/42] todo --- apps/api/src/hyp3_api/routes.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/apps/api/src/hyp3_api/routes.py b/apps/api/src/hyp3_api/routes.py index a026cd16b..87fa8f0f1 100644 --- a/apps/api/src/hyp3_api/routes.py +++ b/apps/api/src/hyp3_api/routes.py @@ -114,6 +114,9 @@ def __call__(self, errors): # TODO what are the implications of not explicitly handling Decimal type? # jsonify automatically converts them to float? +# +# TODO move this up with the other non-auth functions +# if we're not planning to add back the @openapi decorator @app.route('/costs', methods=['GET']) def costs_get(): return jsonify(dynamo.jobs.COSTS) From 9ca5bf3a6659b7c35a366aa8225cf5319450417e Mon Sep 17 00:00:00 2001 From: Jake Herrmann Date: Fri, 23 Feb 2024 17:20:36 -0900 Subject: [PATCH 33/42] todo formatting --- apps/api/src/hyp3_api/routes.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/api/src/hyp3_api/routes.py b/apps/api/src/hyp3_api/routes.py index 87fa8f0f1..16c9ceeb6 100644 --- a/apps/api/src/hyp3_api/routes.py +++ b/apps/api/src/hyp3_api/routes.py @@ -115,8 +115,8 @@ def __call__(self, errors): # TODO what are the implications of not explicitly handling Decimal type? # jsonify automatically converts them to float? # -# TODO move this up with the other non-auth functions -# if we're not planning to add back the @openapi decorator +# TODO move this up with the other non-auth functions? +# (if we're not planning to add back the @openapi decorator) @app.route('/costs', methods=['GET']) def costs_get(): return jsonify(dynamo.jobs.COSTS) From 201dbd0f39851340c25ca3d90d2817d51c1cc029 Mon Sep 17 00:00:00 2001 From: Jake Herrmann Date: Fri, 23 Feb 2024 17:23:34 -0900 Subject: [PATCH 34/42] remove todo --- apps/api/src/hyp3_api/routes.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/apps/api/src/hyp3_api/routes.py b/apps/api/src/hyp3_api/routes.py index 16c9ceeb6..a026cd16b 100644 --- a/apps/api/src/hyp3_api/routes.py +++ b/apps/api/src/hyp3_api/routes.py @@ -114,9 +114,6 @@ def __call__(self, errors): # TODO what are the implications of not explicitly handling Decimal type? # jsonify automatically converts them to float? -# -# TODO move this up with the other non-auth functions? -# (if we're not planning to add back the @openapi decorator) @app.route('/costs', methods=['GET']) def costs_get(): return jsonify(dynamo.jobs.COSTS) From 706cf78ac4c32f942e9d3be5c93065e7f8aea2a8 Mon Sep 17 00:00:00 2001 From: Jake Herrmann Date: Fri, 23 Feb 2024 17:39:49 -0900 Subject: [PATCH 35/42] remove todos --- apps/api/src/hyp3_api/api-spec/openapi-spec.yml.j2 | 1 - apps/api/src/hyp3_api/routes.py | 2 -- apps/render_cf.py | 1 - 3 files changed, 4 deletions(-) diff --git a/apps/api/src/hyp3_api/api-spec/openapi-spec.yml.j2 b/apps/api/src/hyp3_api/api-spec/openapi-spec.yml.j2 index 69d3c83e1..e4f257f69 100644 --- a/apps/api/src/hyp3_api/api-spec/openapi-spec.yml.j2 +++ b/apps/api/src/hyp3_api/api-spec/openapi-spec.yml.j2 @@ -117,7 +117,6 @@ paths: components: schemas: - # TODO specify more information? costs: description: Table of job costs. type: object diff --git a/apps/api/src/hyp3_api/routes.py b/apps/api/src/hyp3_api/routes.py index a026cd16b..23ace2bad 100644 --- a/apps/api/src/hyp3_api/routes.py +++ b/apps/api/src/hyp3_api/routes.py @@ -112,8 +112,6 @@ def __call__(self, errors): ) -# TODO what are the implications of not explicitly handling Decimal type? -# jsonify automatically converts them to float? @app.route('/costs', methods=['GET']) def costs_get(): return jsonify(dynamo.jobs.COSTS) diff --git a/apps/render_cf.py b/apps/render_cf.py index 45d5cc1cd..f767f0d71 100644 --- a/apps/render_cf.py +++ b/apps/render_cf.py @@ -43,7 +43,6 @@ def render_default_params_by_job_type(job_types: dict) -> None: } for job_type, job_spec in job_types.items() } - # TODO format as yaml for consistency with costs.yml? with open(Path('lib') / 'dynamo' / 'dynamo' / 'default_params_by_job_type.json', 'w') as f: json.dump(default_params_by_job_type, f, indent=2) From b70de1e171017345bd43d28bf65022d403b83144 Mon Sep 17 00:00:00 2001 From: Jake Herrmann Date: Fri, 23 Feb 2024 21:21:54 -0900 Subject: [PATCH 36/42] add `EDC` cost profile for `INSAR_ISCE_TEST` --- job_spec/INSAR_ISCE_TEST.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/job_spec/INSAR_ISCE_TEST.yml b/job_spec/INSAR_ISCE_TEST.yml index ad0567024..36ac52156 100644 --- a/job_spec/INSAR_ISCE_TEST.yml +++ b/job_spec/INSAR_ISCE_TEST.yml @@ -89,6 +89,8 @@ INSAR_ISCE_TEST: bucket_prefix: default: '""' cost_profiles: + EDC: + cost: 1.0 DEFAULT: cost: 1.0 validators: [] From 761cb60a2af5af8dc3667a1ba3677788b4032709 Mon Sep 17 00:00:00 2001 From: Jake Herrmann Date: Fri, 23 Feb 2024 21:40:40 -0900 Subject: [PATCH 37/42] changelog --- CHANGELOG.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index fba49271c..f9f10a35c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,18 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [6.2.0] + +HyP3 is in the process of transitioning from a monthly job quota to a credits system. [HyP3 v6.0.0](https://github.com/ASFHyP3/hyp3/releases/tag/v6.0.0) implemented the new credits system without changing the number of jobs that users can run per month. This release implements the capability to assign a different credit cost to each type of job, again without actually changing the number of jobs that users can run per month. + +Beginning on April 1st, the production API at will assign a different cost to each type of job and users will be given an allotment of 10,000 credits per month. Visit the [credits announcement page](https://hyp3-docs.asf.alaska.edu/using/credits/) for full details. + +### Added +- `/costs` API endpoint that returns a table that can be used to look up the credit costs for different types of jobs. + +### Changed +- The API now implements the credit costs displayed on the [credits announcement page](https://hyp3-docs.asf.alaska.edu/using/credits/). + ## [6.1.1] ### Changed From a24daaac518979552ac335b64cbf2fc21a9c0c5f Mon Sep 17 00:00:00 2001 From: Jake Herrmann Date: Mon, 26 Feb 2024 09:44:07 -0900 Subject: [PATCH 38/42] increase default credits to 10,000 in UAT --- .github/workflows/deploy-daac.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/deploy-daac.yml b/.github/workflows/deploy-daac.yml index dfd29d924..eb66a03ac 100644 --- a/.github/workflows/deploy-daac.yml +++ b/.github/workflows/deploy-daac.yml @@ -40,7 +40,7 @@ jobs: template_bucket: cf-templates-118ylv0o6jp2n-us-west-2 image_tag: test product_lifetime_in_days: 14 - default_credits_per_user: 1000 + default_credits_per_user: 10000 reset_credits_monthly: true cost_profile: EDC deploy_ref: refs/heads/develop From d3fbc714535d466c8f90d1580ae3f4491ccc18ae Mon Sep 17 00:00:00 2001 From: Jake Herrmann Date: Mon, 26 Feb 2024 09:57:14 -0900 Subject: [PATCH 39/42] add `pyyaml` --- lib/dynamo/setup.py | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/dynamo/setup.py b/lib/dynamo/setup.py index a7db37bba..9cf00f174 100644 --- a/lib/dynamo/setup.py +++ b/lib/dynamo/setup.py @@ -8,6 +8,7 @@ install_requires=[ 'boto3', 'python-dateutil', + 'pyyaml', ], python_requires='~=3.9', From 81e7a400852fbf1a6a175df1c042f7273d4df831 Mon Sep 17 00:00:00 2001 From: Jake Herrmann Date: Mon, 26 Feb 2024 10:06:40 -0900 Subject: [PATCH 40/42] pass `cost_profile` to deploy action --- .github/workflows/deploy-enterprise-test.yml | 1 + .github/workflows/deploy-enterprise.yml | 1 + 2 files changed, 2 insertions(+) diff --git a/.github/workflows/deploy-enterprise-test.yml b/.github/workflows/deploy-enterprise-test.yml index fd7b38c31..d2a0d1ac6 100644 --- a/.github/workflows/deploy-enterprise-test.yml +++ b/.github/workflows/deploy-enterprise-test.yml @@ -73,6 +73,7 @@ jobs: CLOUDFORMATION_ROLE_ARN: ${{ secrets.CLOUDFORMATION_ROLE_ARN }} DEFAULT_CREDITS_PER_USER: ${{ matrix.default_credits_per_user }} RESET_CREDITS_MONTHLY: ${{ matrix.reset_credits_monthly }} + COST_PROFILE: ${{ matrix.cost_profile }} JOB_FILES: ${{ matrix.job_files }} DEFAULT_MAX_VCPUS: ${{ matrix.default_max_vcpus }} EXPANDED_MAX_VCPUS: ${{ matrix.expanded_max_vcpus }} diff --git a/.github/workflows/deploy-enterprise.yml b/.github/workflows/deploy-enterprise.yml index b4ef7a03f..30d1d7b48 100644 --- a/.github/workflows/deploy-enterprise.yml +++ b/.github/workflows/deploy-enterprise.yml @@ -256,6 +256,7 @@ jobs: CLOUDFORMATION_ROLE_ARN: ${{ secrets.CLOUDFORMATION_ROLE_ARN }} DEFAULT_CREDITS_PER_USER: ${{ matrix.default_credits_per_user }} RESET_CREDITS_MONTHLY: ${{ matrix.reset_credits_monthly }} + COST_PROFILE: ${{ matrix.cost_profile }} JOB_FILES: ${{ matrix.job_files }} DEFAULT_MAX_VCPUS: ${{ matrix.default_max_vcpus }} EXPANDED_MAX_VCPUS: ${{ matrix.expanded_max_vcpus }} From 108cdaa842b9aa1c2d84d30b5c599516e4f35296 Mon Sep 17 00:00:00 2001 From: Joseph H Kennedy Date: Tue, 27 Feb 2024 11:06:03 -0900 Subject: [PATCH 41/42] constrain ARIA fleets due to spot interuptions --- .github/workflows/deploy-enterprise.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/deploy-enterprise.yml b/.github/workflows/deploy-enterprise.yml index 30d1d7b48..7b0974674 100644 --- a/.github/workflows/deploy-enterprise.yml +++ b/.github/workflows/deploy-enterprise.yml @@ -44,8 +44,8 @@ jobs: cost_profile: DEFAULT job_files: job_spec/INSAR_ISCE.yml job_spec/INSAR_ISCE_TEST.yml instance_types: c6id.xlarge,c6id.2xlarge,c6id.4xlarge,c6id.8xlarge - default_max_vcpus: 10000 - expanded_max_vcpus: 10000 + default_max_vcpus: 1000 + expanded_max_vcpus: 1000 required_surplus: 0 security_environment: JPL-public ami_id: /aws/service/ecs/optimized-ami/amazon-linux-2023/recommended/image_id @@ -61,8 +61,8 @@ jobs: cost_profile: DEFAULT job_files: job_spec/INSAR_ISCE.yml job_spec/INSAR_ISCE_TEST.yml instance_types: c6id.xlarge,c6id.2xlarge,c6id.4xlarge,c6id.8xlarge - default_max_vcpus: 10000 - expanded_max_vcpus: 10000 + default_max_vcpus: 1000 + expanded_max_vcpus: 1000 required_surplus: 0 security_environment: JPL-public ami_id: /aws/service/ecs/optimized-ami/amazon-linux-2023/recommended/image_id From 2fbaf50e8b1d3b9dd9ddb0426257afae56674720 Mon Sep 17 00:00:00 2001 From: Joseph H Kennedy Date: Tue, 27 Feb 2024 11:08:24 -0900 Subject: [PATCH 42/42] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index f9f10a35c..2d15f0cdd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,7 @@ Beginning on April 1st, the production API at ### Changed - The API now implements the credit costs displayed on the [credits announcement page](https://hyp3-docs.asf.alaska.edu/using/credits/). +- `hyp3-a19-jpl` and `hyp3-tibet-jpl` deployments max vCPUs have been reduced to 1,000 from 10,000 because of persistent spot interruptions. ## [6.1.1]