diff --git a/aleph_message/models/execution/instance.py b/aleph_message/models/execution/instance.py index 8a4a44f..29a34cf 100644 --- a/aleph_message/models/execution/instance.py +++ b/aleph_message/models/execution/instance.py @@ -3,6 +3,7 @@ from typing import List, Optional from pydantic import Field, model_validator +from typing_extensions import Self from aleph_message.models.abstract import HashableModel @@ -43,38 +44,37 @@ class InstanceContent(BaseExecutableContent): ) @model_validator(mode="after") - def check_requirements(cls, values): - if values.requirements: + def check_requirements(self) -> Self: + if self.requirements: + if ( + self.payment and (self.payment.is_stream or self.payment.is_credit) + ) and (not self.requirements.node or not self.requirements.node.node_hash): + raise ValueError( + "Node hash assignment is needed for PAYG or Credit payments" + ) # GPU filter only supported for QEmu instances with node_hash assigned - if values.requirements.gpu: - if ( - not values.requirements.node - or not values.requirements.node.node_hash - ): + if self.requirements.gpu: + if not self.requirements.node or not self.requirements.node.node_hash: raise ValueError("Node hash assignment is needed for GPU support") if ( - values.environment - and values.environment.hypervisor != HypervisorType.qemu + self.environment + and self.environment.hypervisor != HypervisorType.qemu ): raise ValueError("GPU option is only supported for QEmu hypervisor") # Terms and conditions filter only supported for PAYG/coco instances with node_hash assigned - if ( - values.requirements.node - and values.requirements.node.terms_and_conditions - ): - if not values.requirements.node.node_hash: + if self.requirements.node and self.requirements.node.terms_and_conditions: + if not self.requirements.node.node_hash: raise ValueError( "Terms_and_conditions field needs a requirements.node.node_hash value" ) if ( - not values.payment.is_stream - and not values.environment.trusted_execution - ): + not self.payment or not self.payment.is_stream + ) and not self.environment.trusted_execution: raise ValueError( "Only PAYG/coco instances can have a terms_and_conditions" ) - return values + return self diff --git a/aleph_message/tests/test_models.py b/aleph_message/tests/test_models.py index 7b5cdc7..4620292 100644 --- a/aleph_message/tests/test_models.py +++ b/aleph_message/tests/test_models.py @@ -245,6 +245,71 @@ def test_validation_on_gpu_hypervisor_options(): ) # Ignore "Value error, ..." +def test_validation_on_payg_payment_options(): + """Ensure that a node_hash option is required for stream payments.""" + path = Path(__file__).parent / "messages/instance_gpu_machine.json" + message_dict = json.loads(path.read_text()) + message_dict["content"]["requirements"]["gpu"] = None + message = create_new_message(message_dict, factory=InstanceMessage) + + assert isinstance(message, InstanceMessage) + assert hash(message.content) + assert message.content.payment + assert message.content.payment.type == PaymentType.superfluid + assert message.content.environment.hypervisor == HypervisorType.qemu + + message_dict["content"]["requirements"]["node"] = None + try: + _ = create_new_message(message_dict, factory=InstanceMessage) + raise AssertionError("An exception should have been raised before this point.") + except ValidationError as e: + assert e.errors()[0]["loc"] in [("content",), ("content", "__root__")] + + error_msg = e.errors()[0]["msg"] + assert ( + "Node hash assignment is needed for PAYG or Credit payments" in error_msg + ) # Ignore "Value error, ..." + + +def test_validation_on_credits_payment_options(): + """Ensure that a node_hash option is required for credit payments.""" + path = Path(__file__).parent / "messages/instance_gpu_machine.json" + message_dict = json.loads(path.read_text()) + # Patch the gpu field with some info + message_dict["content"]["payment"]["type"] = "credit" + message_dict["content"]["payment"]["receiver"] = None + message = create_new_message(message_dict, factory=InstanceMessage) + + assert isinstance(message, InstanceMessage) + assert hash(message.content) + assert message.content.payment + assert message.content.payment.type == PaymentType.credit + assert message.content.environment.hypervisor == HypervisorType.qemu + + # Remove gpu field with some info + message_dict["content"]["requirements"]["gpu"] = None + message = create_new_message(message_dict, factory=InstanceMessage) + + assert isinstance(message, InstanceMessage) + assert hash(message.content) + assert message.content.payment + assert message.content.payment.type == PaymentType.credit + assert message.content.environment.hypervisor == HypervisorType.qemu + assert message.content.requirements.gpu is None + + message_dict["content"]["requirements"]["node"] = None + try: + _ = create_new_message(message_dict, factory=InstanceMessage) + raise AssertionError("An exception should have been raised before this point.") + except ValidationError as e: + assert e.errors()[0]["loc"] in [("content",), ("content", "__root__")] + + error_msg = e.errors()[0]["msg"] + assert ( + "Node hash assignment is needed for PAYG or Credit payments" in error_msg + ) # Ignore "Value error, ..." + + def test_message_machine_port_mapping(): message_dict = { "chain": "ETH",