Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
93 commits
Select commit Hold shift + click to select a range
c8ab889
disable hack
sanderegg Sep 25, 2025
d0fc1c6
refactor
sanderegg Sep 25, 2025
29465a5
added generic resource to ec2 resource model
sanderegg Sep 25, 2025
3915a82
do not define gt it is weird for resources
sanderegg Sep 25, 2025
2209e2c
improve coverage
sanderegg Sep 25, 2025
4db4871
ruff
sanderegg Sep 25, 2025
e5012b6
add missing test
sanderegg Sep 25, 2025
cf39158
ruff
sanderegg Sep 25, 2025
cfc3ec7
use ge operator
sanderegg Sep 25, 2025
9e3e916
added model dump flat
sanderegg Sep 25, 2025
4dd4191
both direction
sanderegg Sep 25, 2025
073050f
added ENV variables for nthreads and threads multiplier
sanderegg Sep 25, 2025
74bcf7d
use ge operator instead of incorrect gt operator
sanderegg Sep 25, 2025
9a7eb37
define variables for tests
sanderegg Sep 25, 2025
2a67d9e
pass nthreads and multiplier also to the autoscaling service
sanderegg Sep 25, 2025
2a3019f
ongoing
sanderegg Sep 25, 2025
30b4367
typo
sanderegg Sep 25, 2025
3c87321
fix counter
sanderegg Sep 25, 2025
7971dea
fixed test assert
sanderegg Sep 25, 2025
8981962
fix assert
sanderegg Sep 25, 2025
8fd2fce
typo
sanderegg Sep 25, 2025
f62edf4
wrong types
sanderegg Sep 25, 2025
6911754
ongoing
sanderegg Sep 25, 2025
7c9a8b7
mypy
sanderegg Sep 26, 2025
6dfde1d
added test for getting threads resources
sanderegg Sep 26, 2025
03be3af
added test
sanderegg Sep 26, 2025
c3117a7
improve testing
sanderegg Sep 26, 2025
37d1b44
improve testing
sanderegg Sep 26, 2025
0902d08
ruff
sanderegg Sep 26, 2025
64d84dc
adding test
sanderegg Sep 26, 2025
067f8ff
implemented compute cluster total resources
sanderegg Sep 26, 2025
7048af2
adjusted compute used resources
sanderegg Sep 26, 2025
52ee133
testing
sanderegg Sep 26, 2025
171e702
simplify
sanderegg Sep 26, 2025
7328d75
simplify
sanderegg Sep 26, 2025
addb6fd
create a typed dict
sanderegg Sep 26, 2025
82889c4
simplify
sanderegg Sep 26, 2025
711a7e9
moved naming
sanderegg Sep 26, 2025
b236f57
more
sanderegg Sep 26, 2025
d9682ff
mypy
sanderegg Sep 26, 2025
fb5f001
mypy
sanderegg Sep 26, 2025
adfdb9c
fix test
sanderegg Sep 26, 2025
a402559
mypy
sanderegg Oct 16, 2025
97a195b
improve docs
sanderegg Oct 16, 2025
89c547c
remove todo
sanderegg Oct 16, 2025
6179fe7
re-added gt operator
sanderegg Oct 16, 2025
03a2614
use Required
sanderegg Oct 16, 2025
8e0f905
improve docs
sanderegg Oct 16, 2025
74d8ca8
moved docs
sanderegg Oct 16, 2025
1158e99
added mapping
sanderegg Oct 16, 2025
49b4bb9
added mapping
sanderegg Oct 16, 2025
83db960
improve error
sanderegg Oct 16, 2025
4d2281f
make private
sanderegg Oct 16, 2025
7418819
simplify
sanderegg Oct 17, 2025
3f930bd
simplify
sanderegg Oct 17, 2025
ec8cbaf
fix computation
sanderegg Oct 17, 2025
f40f5db
type
sanderegg Oct 17, 2025
fd3a58d
type
sanderegg Oct 17, 2025
b114585
no need to call items
sanderegg Oct 17, 2025
f852935
revert
sanderegg Oct 17, 2025
b02ec97
refactor
sanderegg Oct 17, 2025
44eb03d
better assert
sanderegg Oct 17, 2025
8916cc6
improving test
sanderegg Oct 17, 2025
255a7d2
add generic instances based on the provider
sanderegg Oct 17, 2025
2254919
sonar
sanderegg Oct 17, 2025
b9d7428
fix?
sanderegg Oct 17, 2025
824dfa7
improve error
sanderegg Oct 17, 2025
6020b89
add resource info on instance types as well
sanderegg Oct 17, 2025
1c0869f
mypy
sanderegg Oct 19, 2025
e55ec44
need to be fixed
sanderegg Oct 19, 2025
d2f241a
done
sanderegg Oct 20, 2025
31aa4c2
fixed tests
sanderegg Oct 20, 2025
ac71422
added missing calls
sanderegg Oct 20, 2025
28bdfee
@copilot review
sanderegg Oct 20, 2025
04e68cb
Update services/autoscaling/src/simcore_service_autoscaling/core/erro…
sanderegg Oct 20, 2025
d396dab
Update services/autoscaling/tests/unit/test_modules_dask.py
sanderegg Oct 20, 2025
2b52109
ensure thread is at least 1
sanderegg Oct 20, 2025
56c4326
improve coverage
sanderegg Oct 20, 2025
e3b998e
added test and a fix
sanderegg Oct 20, 2025
3bb8fad
fix code
sanderegg Oct 20, 2025
a6c322b
adjust ram cpu
sanderegg Oct 20, 2025
2e71395
Update packages/dask-task-models-library/src/dask_task_models_library…
sanderegg Oct 20, 2025
4cc9bb3
Update services/autoscaling/src/simcore_service_autoscaling/modules/d…
sanderegg Oct 20, 2025
ed8fcd0
fix return value
sanderegg Oct 20, 2025
e23a789
created a base function to compute resources
sanderegg Oct 21, 2025
525954c
fixed tests
sanderegg Oct 21, 2025
fc09515
pylint
sanderegg Oct 21, 2025
a7d975b
linter
sanderegg Oct 21, 2025
c998a25
fixed tests
sanderegg Oct 21, 2025
6371b25
fixed types
sanderegg Oct 21, 2025
c773ac1
Update services/autoscaling/src/simcore_service_autoscaling/modules/c…
sanderegg Oct 21, 2025
29d4211
Update packages/aws-library/src/aws_library/ec2/_models.py
sanderegg Oct 21, 2025
f2bcb52
@pcrespov review: add some more string comparisons
sanderegg Oct 22, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions packages/aws-library/src/aws_library/ec2/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
EC2InstanceData,
EC2InstanceType,
EC2Tags,
GenericResourceValueType,
Resources,
)

Expand All @@ -36,6 +37,7 @@
"EC2NotConnectedError",
"EC2RuntimeError",
"EC2Tags",
"GenericResourceValueType",
"Resources",
"SimcoreEC2API",
)
Expand Down
160 changes: 144 additions & 16 deletions packages/aws-library/src/aws_library/ec2/_models.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import contextlib
import datetime
import re
import tempfile
Expand All @@ -14,45 +15,171 @@
Field,
NonNegativeFloat,
NonNegativeInt,
StrictFloat,
StrictInt,
StringConstraints,
TypeAdapter,
ValidationError,
field_validator,
)
from pydantic.config import JsonDict
from types_aiobotocore_ec2.literals import InstanceStateNameType, InstanceTypeType

GenericResourceValueType: TypeAlias = StrictInt | StrictFloat | str


class Resources(BaseModel, frozen=True):
cpus: NonNegativeFloat
ram: ByteSize
generic_resources: Annotated[
dict[str, GenericResourceValueType],
Field(
default_factory=dict,
description=(
"Arbitrary additional resources (e.g. {'threads': 8}). "
"Numeric values are treated as quantities and participate in add/sub/compare."
),
),
] = DEFAULT_FACTORY

@classmethod
def create_as_empty(cls) -> "Resources":
return cls(cpus=0, ram=ByteSize(0))

def __ge__(self, other: "Resources") -> bool:
return self.cpus >= other.cpus and self.ram >= other.ram
"""operator for >= comparison
if self has greater or equal resources than other, returns True
This will return True only if all of the resources in self are greater or equal to other

Note that generic_resources are compared only if they are numeric
Non-numeric generic resources must be equal in both or only defined in self
to be considered greater or equal
"""
if self == other:
return True
return self > other

def __gt__(self, other: "Resources") -> bool:
return self.cpus > other.cpus or self.ram > other.ram
"""operator for > comparison
if self has resources greater than other, returns True
This will return True only if all of the resources in self are greater than other

Note that generic_resources are compared only if they are numeric
Non-numeric generic resources must only be defined in self
to be considered greater
"""
if (self.cpus < other.cpus) or (self.ram < other.ram):
return False

keys = set(self.generic_resources) | set(other.generic_resources)
for k in keys:
a = self.generic_resources.get(k)
b = other.generic_resources.get(k)
if a is None:
return False
if b is None:
# a is greater as b is not defined
continue
if isinstance(a, int | float) and isinstance(b, int | float):
if a < b:
return False
else:
# remaining options is a is str and b is str or mixed types
# NOTE: we cannot compare strings unless they are equal or some kind of boolean (e.g. "true", "false", "yes", "no", "1", "0")
assert isinstance(a, str) # nosec
assert isinstance(b, int | float | str) # nosec
# let's try to get a boolean out of the values to compare them
with contextlib.suppress(ValidationError):
a_as_boolean = TypeAdapter(bool).validate_python(a)
b_as_boolean = TypeAdapter(bool).validate_python(b)
if not a_as_boolean and b_as_boolean:
return False

# here we have either everything greater or equal or non-comparable strings

return self != other

def __add__(self, other: "Resources") -> "Resources":
"""operator for adding two Resources
Note that only numeric generic resources are added
Non-numeric generic resources are ignored
"""
merged: dict[str, GenericResourceValueType] = {}
keys = set(self.generic_resources) | set(other.generic_resources)
for k in keys:
a = self.generic_resources.get(k)
b = other.generic_resources.get(k)
# adding non numeric values does not make sense, so we skip those for the resulting resource
if isinstance(a, int | float) and isinstance(b, int | float):
merged[k] = a + b
elif a is None and isinstance(b, int | float):
merged[k] = b
elif b is None and isinstance(a, int | float):
merged[k] = a

return Resources.model_construct(
**{
key: a + b
for (key, a), b in zip(
self.model_dump().items(), other.model_dump().values(), strict=True
)
}
cpus=self.cpus + other.cpus,
ram=self.ram + other.ram,
generic_resources=merged,
)

def __sub__(self, other: "Resources") -> "Resources":
"""operator for subtracting two Resources
Note that only numeric generic resources are subtracted
Non-numeric generic resources are ignored
"""
merged: dict[str, GenericResourceValueType] = {}
keys = set(self.generic_resources) | set(other.generic_resources)
for k in keys:
a = self.generic_resources.get(k)
b = other.generic_resources.get(k)
# subtracting non numeric values does not make sense, so we skip those for the resulting resource
if isinstance(a, int | float) and isinstance(b, int | float):
merged[k] = a - b
elif a is None and isinstance(b, int | float):
merged[k] = -b
elif b is None and isinstance(a, int | float):
merged[k] = a

return Resources.model_construct(
**{
key: a - b
for (key, a), b in zip(
self.model_dump().items(), other.model_dump().values(), strict=True
)
}
cpus=self.cpus - other.cpus,
ram=self.ram - other.ram,
generic_resources=merged,
)

def __hash__(self) -> int:
"""Deterministic hash including cpus, ram (in bytes) and generic_resources."""
# sort generic_resources items to ensure order-independent hashing
generic_items: tuple[tuple[str, GenericResourceValueType], ...] = tuple(
sorted(self.generic_resources.items())
)
return hash((self.cpus, self.ram, generic_items))

def as_flat_dict(self) -> dict[str, int | float | str]:
"""Like model_dump, but flattens generic_resources to top level keys"""
base = self.model_dump()
base.update(base.pop("generic_resources"))
return base

@classmethod
def from_flat_dict(
cls,
data: dict[str, int | float | str],
*,
mapping: dict[str, str] | None = None,
) -> "Resources":
"""Inverse of as_flat_dict with optional key mapping"""
mapped_data = data
if mapping:
mapped_data = {mapping.get(k, k): v for k, v in data.items()}
generic_resources = {
k: v for k, v in mapped_data.items() if k not in {"cpus", "ram"}
}

return cls(
cpus=float(mapped_data.get("cpus", 0)),
ram=ByteSize(mapped_data.get("ram", 0)),
generic_resources=generic_resources,
)

@field_validator("cpus", mode="before")
Expand Down Expand Up @@ -174,8 +301,9 @@ def validate_bash_calls(cls, v):
temp_file.flush()
# NOTE: this will not capture runtime errors, but at least some syntax errors such as invalid quotes
sh.bash(
"-n", temp_file.name
) # pyright: ignore[reportCallIssue] # sh is untyped, but this call is safe for bash syntax checking
"-n",
temp_file.name, # pyright: ignore[reportCallIssue] - sh is untyped but safe for bash syntax checking
)
except sh.ErrorReturnCode as exc:
msg = f"Invalid bash call in custom_boot_scripts: {v}, Error: {exc.stderr}"
raise ValueError(msg) from exc
Expand Down
Loading
Loading