Skip to content

Commit 773addb

Browse files
authored
Add create_instance method (#78)
* Add allow_amend, internet and aleph_api parameters to create_program * Add method to create VM instances * Fix formatting and interface issues * Fix create_program
1 parent 25a48da commit 773addb

File tree

3 files changed

+139
-2
lines changed

3 files changed

+139
-2
lines changed

src/aleph/sdk/client/abstract.py

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -348,6 +348,54 @@ async def create_program(
348348
"""
349349
pass
350350

351+
@abstractmethod
352+
async def create_instance(
353+
self,
354+
rootfs: str,
355+
rootfs_size: int,
356+
rootfs_name: str,
357+
environment_variables: Optional[Mapping[str, str]] = None,
358+
storage_engine: StorageEnum = StorageEnum.storage,
359+
channel: Optional[str] = None,
360+
address: Optional[str] = None,
361+
sync: bool = False,
362+
memory: Optional[int] = None,
363+
vcpus: Optional[int] = None,
364+
timeout_seconds: Optional[float] = None,
365+
allow_amend: bool = False,
366+
internet: bool = True,
367+
aleph_api: bool = True,
368+
encoding: Encoding = Encoding.zip,
369+
volumes: Optional[List[Mapping]] = None,
370+
volume_persistence: str = "host",
371+
ssh_keys: Optional[List[str]] = None,
372+
metadata: Optional[Mapping[str, Any]] = None,
373+
) -> Tuple[AlephMessage, MessageStatus]:
374+
"""
375+
Post a (create) PROGRAM message.
376+
377+
:param rootfs: Root filesystem to use
378+
:param rootfs_size: Size of root filesystem
379+
:param rootfs_name: Name of root filesystem
380+
:param environment_variables: Environment variables to pass to the program
381+
:param storage_engine: Storage engine to use (Default: "storage")
382+
:param channel: Channel to use (Default: "TEST")
383+
:param address: Address to use (Default: account.get_address())
384+
:param sync: If true, waits for the message to be processed by the API server
385+
:param memory: Memory in MB for the VM to be allocated (Default: 128)
386+
:param vcpus: Number of vCPUs to allocate (Default: 1)
387+
:param timeout_seconds: Timeout in seconds (Default: 30.0)
388+
:param allow_amend: Whether the deployed VM image may be changed (Default: False)
389+
:param internet: Whether the VM should have internet connectivity. (Default: True)
390+
:param aleph_api: Whether the VM needs access to Aleph messages API (Default: True)
391+
:param encoding: Encoding to use (Default: Encoding.zip)
392+
:param volumes: Volumes to mount
393+
:param volume_persistence: Where volumes are persisted, can be "host" or "store", meaning distributed across Aleph.im (Default: "host")
394+
:param ssh_keys: SSH keys to authorize access to the VM
395+
:param metadata: Metadata to attach to the message
396+
"""
397+
pass
398+
351399
@abstractmethod
352400
async def forget(
353401
self,

src/aleph/sdk/client/authenticated_http.py

Lines changed: 75 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@
1313
AlephMessage,
1414
ForgetContent,
1515
ForgetMessage,
16+
InstanceContent,
17+
InstanceMessage,
1618
ItemType,
1719
MessageType,
1820
PostContent,
@@ -27,8 +29,9 @@
2729
FunctionEnvironment,
2830
MachineResources,
2931
)
32+
from aleph_message.models.execution.instance import RootfsVolume
3033
from aleph_message.models.execution.program import CodeContent, FunctionRuntime
31-
from aleph_message.models.execution.volume import MachineVolume
34+
from aleph_message.models.execution.volume import MachineVolume, ParentVolume
3235
from aleph_message.status import MessageStatus
3336
from pydantic.json import pydantic_encoder
3437

@@ -463,7 +466,7 @@ async def create_program(
463466
if runtime == settings.DEFAULT_RUNTIME_ID
464467
else "",
465468
),
466-
volumes=volumes,
469+
volumes=[MachineVolume.parse_obj(volume) for volume in volumes],
467470
time=time.time(),
468471
metadata=metadata,
469472
)
@@ -479,6 +482,76 @@ async def create_program(
479482
sync=sync,
480483
)
481484

485+
async def create_instance(
486+
self,
487+
rootfs: str,
488+
rootfs_size: int,
489+
rootfs_name: str,
490+
environment_variables: Optional[Mapping[str, str]] = None,
491+
storage_engine: StorageEnum = StorageEnum.storage,
492+
channel: Optional[str] = None,
493+
address: Optional[str] = None,
494+
sync: bool = False,
495+
memory: Optional[int] = None,
496+
vcpus: Optional[int] = None,
497+
timeout_seconds: Optional[float] = None,
498+
allow_amend: bool = False,
499+
internet: bool = True,
500+
aleph_api: bool = True,
501+
encoding: Encoding = Encoding.zip,
502+
volumes: Optional[List[Mapping]] = None,
503+
volume_persistence: str = "host",
504+
ssh_keys: Optional[List[str]] = None,
505+
metadata: Optional[Mapping[str, Any]] = None,
506+
) -> Tuple[InstanceMessage, MessageStatus]:
507+
address = address or settings.ADDRESS_TO_USE or self.account.get_address()
508+
509+
volumes = volumes if volumes is not None else []
510+
memory = memory or settings.DEFAULT_VM_MEMORY
511+
vcpus = vcpus or settings.DEFAULT_VM_VCPUS
512+
timeout_seconds = timeout_seconds or settings.DEFAULT_VM_TIMEOUT
513+
514+
content = InstanceContent(
515+
address=address,
516+
allow_amend=allow_amend,
517+
environment=FunctionEnvironment(
518+
reproducible=False,
519+
internet=internet,
520+
aleph_api=aleph_api,
521+
),
522+
variables=environment_variables,
523+
resources=MachineResources(
524+
vcpus=vcpus,
525+
memory=memory,
526+
seconds=timeout_seconds,
527+
),
528+
rootfs=RootfsVolume(
529+
parent=ParentVolume(
530+
ref=rootfs,
531+
use_latest=True,
532+
),
533+
name=rootfs_name,
534+
size_mib=rootfs_size,
535+
persistence="host",
536+
use_latest=True,
537+
comment="Official Aleph Debian root filesystem"
538+
if rootfs == settings.DEFAULT_RUNTIME_ID
539+
else "",
540+
),
541+
volumes=[MachineVolume.parse_obj(volume) for volume in volumes],
542+
time=time.time(),
543+
authorized_keys=ssh_keys,
544+
metadata=metadata,
545+
)
546+
547+
return await self.submit(
548+
content=content.dict(exclude_none=True),
549+
message_type=MessageType.instance,
550+
channel=channel,
551+
storage_engine=storage_engine,
552+
sync=sync,
553+
)
554+
482555
async def forget(
483556
self,
484557
hashes: List[str],

tests/unit/test_asynchronous.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
from aleph_message.models import (
66
AggregateMessage,
77
ForgetMessage,
8+
InstanceMessage,
89
PostMessage,
910
ProgramMessage,
1011
StoreMessage,
@@ -141,6 +142,21 @@ async def test_create_program(mock_session_with_post_success):
141142
assert isinstance(program_message, ProgramMessage)
142143

143144

145+
@pytest.mark.asyncio
146+
async def test_create_instance(mock_session_with_post_success):
147+
async with mock_session_with_post_success as session:
148+
instance_message, message_status = await session.create_instance(
149+
rootfs="cafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafe",
150+
rootfs_size=1,
151+
rootfs_name="rootfs",
152+
channel="TEST",
153+
metadata={"tags": ["test"]},
154+
)
155+
156+
assert mock_session_with_post_success.http_session.post.called_once
157+
assert isinstance(instance_message, InstanceMessage)
158+
159+
144160
@pytest.mark.asyncio
145161
async def test_forget(mock_session_with_post_success):
146162
async with mock_session_with_post_success as session:

0 commit comments

Comments
 (0)