コード例 #1
0
def invalidate_cache_entries(request_ids: list = None,
                             request_hashes: list = None):
    """
    Invalidates a list of request IDs and/or request hashes.
    Invalidation refers to the invalidation of the request in DynamoDB
    and the deletion of the associated matrix in S3.

    Invalidated requests will return an `ERROR` state and explanation
    to the user via the GET endpoint.

    Request hashes are resolved to a list of associated request IDs.
    :param request_ids: list of request IDs to invalidate
    :param request_hashes: list of request hashes to invalidate
    """
    print(f"Invalidating request IDs: {request_ids}")
    print(f"Invalidating request hashes: {request_hashes}")
    deployment_stage = os.environ['DEPLOYMENT_STAGE']
    dynamo_handler = DynamoHandler()
    data_version = dynamo_handler.get_table_item(
        table=DynamoTable.DEPLOYMENT_TABLE,
        key=deployment_stage)[DeploymentTableField.CURRENT_DATA_VERSION.value]
    for request_hash in request_hashes:
        items = dynamo_handler.filter_table_items(
            table=DynamoTable.REQUEST_TABLE,
            attrs={
                RequestTableField.REQUEST_HASH.value: request_hash,
                RequestTableField.DATA_VERSION.value: data_version
            })
        for item in items:
            request_ids.append(item[RequestTableField.REQUEST_ID.value])

    s3_keys_to_delete = []
    for request_id in request_ids:
        print(f"Writing deletion error to {request_id} in DynamoDB.")
        request_tracker = RequestTracker(request_id=request_id)
        request_tracker.log_error(
            "This request has been deleted and is no longer available for download. "
            "Please generate a new matrix at POST /v1/matrix.")
        s3_keys_to_delete.append(request_tracker.s3_results_key)

    print(f"Deleting matrices at the following S3 keys: {s3_keys_to_delete}")
    s3_results_bucket_handler = S3Handler(os.environ['MATRIX_RESULTS_BUCKET'])
    deleted_objects = s3_results_bucket_handler.delete_objects(
        s3_keys_to_delete)

    deleted_keys = [
        deleted_object['Key'] for deleted_object in deleted_objects
    ]

    print(
        f"Successfully deleted the following matrices {deleted_keys}. ({len(deleted_keys)}/{len(s3_keys_to_delete)})"
    )
コード例 #2
0
class TestDynamoHandler(MatrixTestCaseUsingMockAWS):
    """
    Environment variables are set in tests/unit/__init__.py
    """
    def setUp(self):
        super(TestDynamoHandler, self).setUp()

        self.dynamo = boto3.resource("dynamodb", region_name=os.environ['AWS_DEFAULT_REGION'])
        self.data_version_table_name = os.environ['DYNAMO_DATA_VERSION_TABLE_NAME']
        self.request_table_name = os.environ['DYNAMO_REQUEST_TABLE_NAME']
        self.request_id = str(uuid.uuid4())
        self.data_version = 1
        self.format = "zarr"

        self.create_test_data_version_table()
        self.create_test_deployment_table()
        self.create_test_request_table()

        self.init_test_data_version_table()
        self.init_test_deployment_table()

        self.handler = DynamoHandler()

    def _get_data_version_table_response_and_entry(self):
        data_version_primary_key = self.handler._get_dynamo_table_primary_key_from_enum(DynamoTable.DATA_VERSION_TABLE)
        response = self.dynamo.batch_get_item(
            RequestItems={
                self.data_version_table_name: {
                    'Keys': [{data_version_primary_key: self.data_version}]
                }
            }
        )
        entry = response['Responses'][self.data_version_table_name][0]
        return response, entry

    def _get_request_table_response_and_entry(self):
        response = self.dynamo.batch_get_item(
            RequestItems={
                self.request_table_name: {
                    'Keys': [{'RequestId': self.request_id}]
                }
            }
        )
        entry = response['Responses'][self.request_table_name][0]
        return response, entry

    @mock.patch("matrix.common.v1_api_handler.V1ApiHandler.describe_filter")
    @mock.patch("matrix.common.date.get_datetime_now")
    def test_create_data_version_table_entry(self, mock_get_datetime_now, mock_describe_filter):
        stub_date = '2019-03-18T180907.136216Z'
        mock_get_datetime_now.return_value = stub_date

        stub_cell_counts = {
            'test_project_uuid_1': 10,
            'test_project_uuid_2': 100
        }
        mock_describe_filter.return_value = {
            'cell_counts': stub_cell_counts
        }

        self.handler.create_data_version_table_entry(self.data_version)
        response, entry = self._get_data_version_table_response_and_entry()

        metadata_schema_versions = {}
        for schema_name in SUPPORTED_METADATA_SCHEMA_VERSIONS:
            metadata_schema_versions[schema_name.value] = SUPPORTED_METADATA_SCHEMA_VERSIONS[schema_name]

        self.assertEqual(len(response['Responses'][self.data_version_table_name]), 1)

        self.assertTrue(all(field.value in entry for field in DataVersionTableField))
        self.assertEqual(entry[DataVersionTableField.DATA_VERSION.value], self.data_version)
        self.assertEqual(entry[DataVersionTableField.CREATION_DATE.value], stub_date)
        self.assertEqual(entry[DataVersionTableField.PROJECT_CELL_COUNTS.value], stub_cell_counts)
        self.assertEqual(entry[DataVersionTableField.METADATA_SCHEMA_VERSIONS.value], metadata_schema_versions)

    @mock.patch("matrix.common.date.get_datetime_now")
    def test_create_request_table_entry(self, mock_get_datetime_now):
        stub_date = '2019-03-18T180907.136216Z'
        mock_get_datetime_now.return_value = stub_date
        self.handler.create_request_table_entry(self.request_id, self.format)
        response, entry = self._get_request_table_response_and_entry()

        self.assertEqual(len(response['Responses'][self.request_table_name]), 1)

        self.assertTrue(all(field.value in entry for field in RequestTableField))
        self.assertEqual(entry[RequestTableField.FORMAT.value], self.format)
        self.assertEqual(entry[RequestTableField.METADATA_FIELDS.value], DEFAULT_FIELDS)
        self.assertEqual(entry[RequestTableField.FEATURE.value], "gene")
        self.assertEqual(entry[RequestTableField.DATA_VERSION.value], 0)
        self.assertEqual(entry[RequestTableField.REQUEST_HASH.value], "N/A")
        self.assertEqual(entry[RequestTableField.EXPECTED_DRIVER_EXECUTIONS.value], 1)
        self.assertEqual(entry[RequestTableField.EXPECTED_CONVERTER_EXECUTIONS.value], 1)
        self.assertEqual(entry[RequestTableField.CREATION_DATE.value], stub_date)

    def test_increment_table_field_request_table_path(self):
        self.handler.create_request_table_entry(self.request_id, self.format)

        response, entry = self._get_request_table_response_and_entry()
        self.assertEqual(entry[RequestTableField.COMPLETED_DRIVER_EXECUTIONS.value], 0)
        self.assertEqual(entry[RequestTableField.COMPLETED_CONVERTER_EXECUTIONS.value], 0)

        field_enum = RequestTableField.COMPLETED_DRIVER_EXECUTIONS
        self.handler.increment_table_field(DynamoTable.REQUEST_TABLE, self.request_id, field_enum, 5)
        response, entry = self._get_request_table_response_and_entry()
        self.assertEqual(entry[RequestTableField.COMPLETED_DRIVER_EXECUTIONS.value], 5)
        self.assertEqual(entry[RequestTableField.COMPLETED_CONVERTER_EXECUTIONS.value], 0)

    def test_set_table_field_with_value(self):
        self.handler.create_request_table_entry(self.request_id, self.format)
        response, entry = self._get_request_table_response_and_entry()
        self.assertEqual(entry[RequestTableField.BATCH_JOB_ID.value], "N/A")

        field_enum = RequestTableField.BATCH_JOB_ID
        self.handler.set_table_field_with_value(DynamoTable.REQUEST_TABLE, self.request_id, field_enum, "123-123")
        response, entry = self._get_request_table_response_and_entry()
        self.assertEqual(entry[RequestTableField.BATCH_JOB_ID.value], "123-123")

    def test_increment_field(self):
        self.handler.create_request_table_entry(self.request_id, self.format)
        response, entry = self._get_request_table_response_and_entry()
        self.assertEqual(entry[RequestTableField.COMPLETED_DRIVER_EXECUTIONS.value], 0)
        self.assertEqual(entry[RequestTableField.COMPLETED_CONVERTER_EXECUTIONS.value], 0)

        key_dict = {"RequestId": self.request_id}
        field_enum = RequestTableField.COMPLETED_DRIVER_EXECUTIONS
        self.handler._increment_field(self.handler._get_dynamo_table_resource_from_enum(DynamoTable.REQUEST_TABLE),
                                      key_dict,
                                      field_enum,
                                      15)
        response, entry = self._get_request_table_response_and_entry()
        self.assertEqual(entry[RequestTableField.COMPLETED_DRIVER_EXECUTIONS.value], 15)
        self.assertEqual(entry[RequestTableField.COMPLETED_CONVERTER_EXECUTIONS.value], 0)

    def test_set_field(self):
        self.handler.create_request_table_entry(self.request_id, self.format)
        response, entry = self._get_request_table_response_and_entry()
        self.assertEqual(entry[RequestTableField.BATCH_JOB_ID.value], "N/A")

        key_dict = {"RequestId": self.request_id}
        field_enum = RequestTableField.BATCH_JOB_ID
        self.handler._set_field(self.handler._get_dynamo_table_resource_from_enum(DynamoTable.REQUEST_TABLE),
                                key_dict,
                                field_enum,
                                "123-123")
        response, entry = self._get_request_table_response_and_entry()
        self.assertEqual(entry[RequestTableField.BATCH_JOB_ID.value], "123-123")

    def test_get_request_table_entry(self):
        self.handler.create_request_table_entry(self.request_id, self.format)
        entry = self.handler.get_table_item(DynamoTable.REQUEST_TABLE, key=self.request_id)
        self.assertEqual(entry[RequestTableField.EXPECTED_DRIVER_EXECUTIONS.value], 1)

        key_dict = {"RequestId": self.request_id}
        field_enum = RequestTableField.COMPLETED_DRIVER_EXECUTIONS
        self.handler._increment_field(self.handler._get_dynamo_table_resource_from_enum(DynamoTable.REQUEST_TABLE),
                                      key_dict,
                                      field_enum,
                                      15)
        entry = self.handler.get_table_item(DynamoTable.REQUEST_TABLE, key=self.request_id)
        self.assertEqual(entry[RequestTableField.COMPLETED_DRIVER_EXECUTIONS.value], 15)

    def test_get_table_item(self):
        self.assertRaises(MatrixException, self.handler.get_table_item,
                          DynamoTable.REQUEST_TABLE,
                          key=self.request_id)

        self.handler.create_request_table_entry(self.request_id, self.format)
        entry = self.handler.get_table_item(DynamoTable.REQUEST_TABLE, key=self.request_id)
        self.assertEqual(entry[RequestTableField.ROW_COUNT.value], 0)

    def test_filter_table_items(self):
        items = self.handler.filter_table_items(
            table=DynamoTable.REQUEST_TABLE,
            attrs={RequestTableField.REQUEST_HASH.value: "N/A"}
        )
        self.assertEqual(len(items), 0)

        self.handler.create_request_table_entry(self.request_id, self.format)
        self.handler.create_request_table_entry(str(uuid.uuid4()), "test_format")

        items = self.handler.filter_table_items(
            table=DynamoTable.REQUEST_TABLE,
            attrs={RequestTableField.REQUEST_HASH.value: "N/A"}
        )
        self.assertEqual(len(items), 2)

        items = self.handler.filter_table_items(
            table=DynamoTable.REQUEST_TABLE,
            attrs={RequestTableField.REQUEST_HASH.value: "N/A",
                   RequestTableField.FORMAT.value: self.format}
        )
        self.assertEqual(len(items), 1)
        self.assertEqual(items[0][RequestTableField.REQUEST_ID.value], self.request_id)