Skip to content

Commit

Permalink
Ensure on_end makes input tz aware and fix on_end method (#28)
Browse files Browse the repository at this point in the history
* Ensure on_end makes input tz aware

* allow "affinity: None" in on-end requests

* round credit spending

* add current request deletion test
---------

Co-authored-by: scrungus <[email protected]>
  • Loading branch information
JohnGarbutt and scrungus authored Sep 30, 2024
1 parent 212a8b0 commit bb9b148
Show file tree
Hide file tree
Showing 6 changed files with 105 additions and 17 deletions.
4 changes: 2 additions & 2 deletions coral_credits/api/business_objects.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ class PhysicalReservation(BaseReservation):
class FlavorReservation(BaseReservation):
amount: int
flavor_id: str
affinity: str = "None"
affinity: Optional[str] = None


@dataclass(kw_only=True)
Expand All @@ -51,7 +51,7 @@ class VirtualReservation(BaseReservation):
vcpus: int
memory_mb: int
disk_gb: int
affinity: str = "None"
affinity: Optional[str] = None
resource_properties: Optional[str] = None


Expand Down
5 changes: 3 additions & 2 deletions coral_credits/api/db_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -313,9 +313,10 @@ def spend_credits(
)
# Subtract expenditure from CreditAllocationResource
# Or add, if the update delta is < 0
credit_allocations[resource_class].resource_hours = (
credit_allocations[resource_class].resource_hours = round(
credit_allocations[resource_class].resource_hours
- resource_requests[resource_class]
- resource_requests[resource_class],
0,
)
credit_allocations[resource_class].save()

Expand Down
6 changes: 3 additions & 3 deletions coral_credits/api/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -130,16 +130,16 @@ class PhysicalReservationSerializer(BaseReservationSerializer):
class FlavorReservationSerializer(BaseReservationSerializer):
amount = serializers.IntegerField()
flavor_id = serializers.CharField()
affinity = serializers.CharField(required=False, default="None")
affinity = serializers.CharField(required=False, default=None, allow_null=True)


class VirtualReservationSerializer(BaseReservationSerializer):
amount = serializers.IntegerField()
vcpus = serializers.IntegerField()
memory_mb = serializers.IntegerField()
disk_gb = serializers.IntegerField()
affinity = serializers.CharField(required=False, default="None")
resource_properties = serializers.CharField(required=False, allow_blank=True)
affinity = serializers.CharField(required=False, default=None, allow_blank=True)
resource_properties = serializers.CharField(required=False, allow_null=True)


class ReservationFactory:
Expand Down
28 changes: 26 additions & 2 deletions coral_credits/api/tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ def pytest_configure(config):
config.END_DATE = config.START_DATE + timedelta(days=1)
config.END_EARLY_DATE = config.START_DATE + timedelta(days=0.75)
config.END_LATE_DATE = config.START_DATE + timedelta(days=1.5)
config.START_EARLY_DATE = config.START_DATE + timedelta(days=-0.75)


# Get auth token
Expand Down Expand Up @@ -69,6 +70,16 @@ def credit_allocation(account, request):
)


@pytest.fixture
def early_credit_allocation(account, request):
return models.CreditAllocation.objects.create(
account=account,
name="test",
start=request.config.START_EARLY_DATE,
end=request.config.END_DATE,
)


@pytest.fixture
def api_client(token):
client = APIClient()
Expand Down Expand Up @@ -162,7 +173,7 @@ def flavor_request_data(base_request_data):
"amount": 2,
"flavor_id": "e26a4241-b83d-4516-8e0e-8ce2665d1966",
"resource_type": "flavor:instance",
"affinity": "None",
"affinity": None,
"allocations": [],
}
],
Expand Down Expand Up @@ -196,6 +207,19 @@ def flavor_shorten_current_request_data(flavor_request_data, request):
return deep_merge(flavor_request_data, shorten_request_data)


@pytest.fixture
def start_early_request_data(flavor_request_data, request):
zero_hours_request_data = {
"lease": {
"start_date": request.config.START_EARLY_DATE.isoformat(),
"end_date": (
request.config.START_EARLY_DATE + timedelta(days=1)
).isoformat(),
}
}
return deep_merge(flavor_request_data, zero_hours_request_data)


@pytest.fixture
def zero_hours_request_data(flavor_request_data, request):
zero_hours_request_data = {
Expand Down Expand Up @@ -244,7 +268,7 @@ def virtual_request_data(base_request_data):
"vcpus": 1,
"memory_mb": 1,
"disk_gb": 0,
"affinity": "None",
"affinity": None,
"resource_properties": "",
"allocations": [],
}
Expand Down
59 changes: 59 additions & 0 deletions coral_credits/api/tests/consumer_tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,65 @@ def test_flavor_delete_upcoming_request(
)


@pytest.mark.parametrize(
"allocation_hours,request_data",
[
(
{"VCPU": 96.0, "MEMORY_MB": 24000.0, "DISK_GB": 840.0},
lazy_fixture("start_early_request_data"),
),
],
)
@pytest.mark.django_db
def test_flavor_delete_current_request(
resource_classes,
early_credit_allocation,
create_credit_allocation_resources,
resource_provider_account,
api_client,
request_data,
allocation_hours,
request, # contains pytest global vars
):
# Allocate resource credit
create_credit_allocation_resources(
early_credit_allocation, resource_classes, allocation_hours
)

# Create
consumer_create_request(api_client, request_data, status.HTTP_204_NO_CONTENT)

# Delete
consumer_delete_request(api_client, request_data, status.HTTP_204_NO_CONTENT)

# Find consumer and check end date is changed.
new_consumer = models.Consumer.objects.filter(
consumer_ref=request.config.LEASE_NAME
).first()
assert new_consumer is not None

# END_EARLY_DATE is 3/4 the duration of the original allocation.
# on_end will set the end_time to datetime.now()
# so we should have consumed 75% of the reservation
# and refunded 25%.

# Check our allocations are correct.
for resource_class in resource_classes:
c = models.CreditAllocationResource.objects.filter(
resource_class=resource_class.id
).first()
assert c.resource_hours == allocation_hours[resource_class.name] * 0.25

# Check consumption records are correct
for resource_class in resource_classes:
rcr = models.ResourceConsumptionRecord.objects.get(
consumer=new_consumer, resource_class=resource_class.id
)
assert rcr.resource_hours == pytest.approx(
allocation_hours[resource_class.name] * 0.75, 0.5
)


@pytest.mark.parametrize(
"allocation_hours,request_data,shorten_request_data",
[
Expand Down
20 changes: 12 additions & 8 deletions coral_credits/api/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,7 @@ class ConsumerViewSet(viewsets.ModelViewSet):

@action(detail=False, methods=["post"], url_path="create")
def create_consumer(self, request):
LOG.info(f"About to process create commit:\n{request.data}")
return self._create_or_update(request)

@action(detail=False, methods=["post"], url_path="update")
Expand Down Expand Up @@ -176,15 +177,18 @@ def on_end(self, request):
# We can't just set everything to current time as we don't know
# the latency between blazars request and our reception.
# Unless we decide on how to round credit allocations.
if (
datetime.fromisoformat(request.data["lease"]["start_date"])
< time_now
):
request.data["lease"]["end_date"] = request.data["lease"][
"start_date"
]
else:
req_start_date = datetime.fromisoformat(
request.data["lease"]["start_date"]
)
if req_start_date.tzinfo is None:
req_start_date = make_aware(req_start_date)
if req_start_date < time_now:
# TODO(johngarbutt) we need to check what we have in the db!
request.data["lease"]["end_date"] = time_now.isoformat()
else:
request.data["lease"]["end_date"] = req_start_date.isoformat()
request.data["current_lease"] = request.data["lease"]
LOG.info(f"About to process on-end request:\n{request.data}")
return self._create_or_update(
request, current_lease_required=True, dry_run=False
)
Expand Down

0 comments on commit bb9b148

Please sign in to comment.