Skip to content

Commit 22e22dd

Browse files
authored
Merge branch 'main' into mhh-domain-node
2 parents b1ad1a5 + 4080042 commit 22e22dd

14 files changed

+129
-55
lines changed

examples/httpgateway.py

+1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
""" Server metrics upload.
22
"""
3+
34
# -*- coding: utf-8 -*-
45

56
import click

examples/metrics.py

+1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
""" Server metrics upload.
22
"""
3+
34
import asyncio
45
import os
56
import platform

examples/mqtt.py

+1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
""" Server metrics upload.
22
"""
3+
34
# -*- coding: utf-8 -*-
45

56
import asyncio

setup.cfg

+1-1
Original file line numberDiff line numberDiff line change
@@ -167,7 +167,7 @@ exclude =
167167
dist
168168
.eggs
169169
docs/conf.py
170-
ignore = E501 W291 W503 E203
170+
ignore = E501 W291 W503 E203 E704
171171

172172
[isort]
173173
profile = black

src/aleph/sdk/chains/nuls1.py

+1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
""" Barebone NULS address and message signing support.
22
"""
3+
34
import hashlib
45
import logging
56
import struct

src/aleph/sdk/chains/remote.py

+1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
"""
22
Remote account, accessible via an API.
33
"""
4+
45
import asyncio
56
from typing import Dict, Optional
67

src/aleph/sdk/client/authenticated_http.py

+15-13
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@
3737
from ..conf import settings
3838
from ..exceptions import BroadcastError, InsufficientFundsError, InvalidMessageError
3939
from ..types import Account, StorageEnum
40-
from ..utils import extended_json_encoder
40+
from ..utils import extended_json_encoder, parse_volume
4141
from .abstract import AuthenticatedAlephClient
4242
from .http import AlephHttpClient
4343

@@ -68,7 +68,7 @@ class AuthenticatedAlephHttpClient(AlephHttpClient, AuthenticatedAlephClient):
6868
def __init__(
6969
self,
7070
account: Account,
71-
api_server: Optional[str],
71+
api_server: Optional[str] = None,
7272
api_unix_socket: Optional[str] = None,
7373
allow_unix_sockets: bool = True,
7474
timeout: Optional[aiohttp.ClientTimeout] = None,
@@ -449,9 +449,7 @@ async def create_program(
449449
# Trigger on HTTP calls.
450450
triggers = {"http": True, "persistent": persistent}
451451

452-
volumes: List[MachineVolume] = [
453-
MachineVolume.parse_obj(volume) for volume in volumes
454-
]
452+
volumes: List[MachineVolume] = [parse_volume(volume) for volume in volumes]
455453

456454
content = ProgramContent(
457455
type="vm-function",
@@ -478,11 +476,13 @@ async def create_program(
478476
runtime=FunctionRuntime(
479477
ref=runtime,
480478
use_latest=True,
481-
comment="Official aleph.im runtime"
482-
if runtime == settings.DEFAULT_RUNTIME_ID
483-
else "",
479+
comment=(
480+
"Official aleph.im runtime"
481+
if runtime == settings.DEFAULT_RUNTIME_ID
482+
else ""
483+
),
484484
),
485-
volumes=[MachineVolume.parse_obj(volume) for volume in volumes],
485+
volumes=[parse_volume(volume) for volume in volumes],
486486
time=time.time(),
487487
metadata=metadata,
488488
)
@@ -551,11 +551,13 @@ async def create_instance(
551551
size_mib=rootfs_size,
552552
persistence="host",
553553
use_latest=True,
554-
comment="Official Aleph Debian root filesystem"
555-
if rootfs == settings.DEFAULT_RUNTIME_ID
556-
else "",
554+
comment=(
555+
"Official Aleph Debian root filesystem"
556+
if rootfs == settings.DEFAULT_RUNTIME_ID
557+
else ""
558+
),
557559
),
558-
volumes=[MachineVolume.parse_obj(volume) for volume in volumes],
560+
volumes=[parse_volume(volume) for volume in volumes],
559561
time=time.time(),
560562
authorized_keys=ssh_keys,
561563
metadata=metadata,

src/aleph/sdk/conf.py

+3-1
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,9 @@ class Settings(BaseSettings):
2929
REMOTE_CRYPTO_UNIX_SOCKET: Optional[str] = None
3030
ADDRESS_TO_USE: Optional[str] = None
3131

32-
DEFAULT_RUNTIME_ID: str = "f873715dc2feec3833074bd4b8745363a0e0093746b987b4c8191268883b2463" # Debian 12 official runtime
32+
DEFAULT_RUNTIME_ID: str = (
33+
"f873715dc2feec3833074bd4b8745363a0e0093746b987b4c8191268883b2463" # Debian 12 official runtime
34+
)
3335
DEFAULT_VM_MEMORY: int = 256
3436
DEFAULT_VM_VCPUS: int = 1
3537
DEFAULT_VM_TIMEOUT: float = 30.0

src/aleph/sdk/domain.py

+16-17
Original file line numberDiff line numberDiff line change
@@ -187,10 +187,10 @@ async def check_domain(
187187
Returns:
188188
A dictionary containing the status of the domain configuration.
189189
"""
190-
status = {"cname": False, "owner_proof": False}
190+
status = {}
191191

192192
dns_rules = self.get_required_dns_rules(hostname, target, owner)
193-
193+
resolver = await self.get_resolver_for(hostname)
194194
for dns_rule in dns_rules:
195195
status[dns_rule.name] = False
196196

@@ -199,26 +199,25 @@ async def check_domain(
199199
record_value = dns_rule.dns["value"]
200200

201201
try:
202-
resolver = await self.get_resolver_for(hostname)
203202
entries = await resolver.query(record_name, record_type.upper())
204203
except aiodns.error.DNSError:
205204
# Continue checks
206205
entries = None
207206

208-
if entries and record_type == "txt":
209-
for entry in entries:
210-
if hasattr(entry, "text") and entry.text == record_value:
211-
break
212-
else:
213-
return dns_rule.raise_error(status)
214-
elif (
215-
entries is None
216-
or not hasattr(entries, record_type)
217-
or getattr(entries, record_type) != record_value
218-
):
219-
return dns_rule.raise_error(status)
220-
221-
status[dns_rule.name] = True
207+
if entries:
208+
if record_type == "txt":
209+
for entry in entries:
210+
if hasattr(entry, "text") and entry.text == record_value:
211+
status[dns_rule.name] = True
212+
break
213+
elif (
214+
hasattr(entries, record_type)
215+
and getattr(entries, record_type) == record_value
216+
):
217+
status[dns_rule.name] = True
218+
219+
if all(status.values()) is False:
220+
dns_rule.raise_error(status)
222221

223222
return status
224223

src/aleph/sdk/types.py

+4-8
Original file line numberDiff line numberDiff line change
@@ -18,23 +18,19 @@ class Account(Protocol):
1818
CURVE: str
1919

2020
@abstractmethod
21-
async def sign_message(self, message: Dict) -> Dict:
22-
...
21+
async def sign_message(self, message: Dict) -> Dict: ...
2322

2423
@abstractmethod
25-
def get_address(self) -> str:
26-
...
24+
def get_address(self) -> str: ...
2725

2826
@abstractmethod
29-
def get_public_key(self) -> str:
30-
...
27+
def get_public_key(self) -> str: ...
3128

3229

3330
class AccountFromPrivateKey(Account, Protocol):
3431
"""Only accounts that are initialized from a private key string are supported."""
3532

36-
def __init__(self, private_key: bytes):
37-
...
33+
def __init__(self, private_key: bytes): ...
3834

3935

4036
GenericMessage = TypeVar("GenericMessage", bound=AlephMessage)

src/aleph/sdk/utils.py

+31-5
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,23 @@
55
from enum import Enum
66
from pathlib import Path
77
from shutil import make_archive
8-
from typing import Any, Iterable, Optional, Protocol, Tuple, Type, TypeVar, Union
8+
from typing import (
9+
Any,
10+
Iterable,
11+
Mapping,
12+
Optional,
13+
Protocol,
14+
Tuple,
15+
Type,
16+
TypeVar,
17+
Union,
18+
get_args,
19+
)
920
from zipfile import BadZipFile, ZipFile
1021

1122
from aleph_message.models import MessageType
1223
from aleph_message.models.execution.program import Encoding
24+
from aleph_message.models.execution.volume import MachineVolume
1325
from pydantic.json import pydantic_encoder
1426

1527
from aleph.sdk.conf import settings
@@ -86,13 +98,11 @@ def check_unix_socket_valid(unix_socket_path: str) -> bool:
8698

8799

88100
class AsyncReadable(Protocol[T]):
89-
async def read(self, n: int = -1) -> T:
90-
...
101+
async def read(self, n: int = -1) -> T: ...
91102

92103

93104
class Writable(Protocol[U]):
94-
def write(self, buffer: U) -> int:
95-
...
105+
def write(self, buffer: U) -> int: ...
96106

97107

98108
async def copy_async_readable_to_buffer(
@@ -150,3 +160,19 @@ def extended_json_encoder(obj: Any) -> Any:
150160
return obj.hour * 3600 + obj.minute * 60 + obj.second + obj.microsecond / 1e6
151161
else:
152162
return pydantic_encoder(obj)
163+
164+
165+
def parse_volume(volume_dict: Union[Mapping, MachineVolume]) -> MachineVolume:
166+
# Python 3.9 does not support `isinstance(volume_dict, MachineVolume)`,
167+
# so we need to iterate over all types.
168+
if any(
169+
isinstance(volume_dict, volume_type) for volume_type in get_args(MachineVolume)
170+
):
171+
return volume_dict
172+
for volume_type in get_args(MachineVolume):
173+
try:
174+
return volume_type.parse_obj(volume_dict)
175+
except ValueError:
176+
continue
177+
else:
178+
raise ValueError(f"Could not parse volume: {volume_dict}")

tests/unit/conftest.py

+6-9
Original file line numberDiff line numberDiff line change
@@ -130,9 +130,9 @@ def json_post() -> dict:
130130
@pytest.fixture
131131
def raw_messages_response(aleph_messages) -> Callable[[int], Dict[str, Any]]:
132132
return lambda page: {
133-
"messages": [message.dict() for message in aleph_messages]
134-
if int(page) == 1
135-
else [],
133+
"messages": (
134+
[message.dict() for message in aleph_messages] if int(page) == 1 else []
135+
),
136136
"pagination_item": "messages",
137137
"pagination_page": int(page),
138138
"pagination_per_page": max(len(aleph_messages), 20),
@@ -158,14 +158,11 @@ def __init__(self, sync: bool):
158158
async def __aenter__(self):
159159
return self
160160

161-
async def __aexit__(self, exc_type, exc_val, exc_tb):
162-
...
161+
async def __aexit__(self, exc_type, exc_val, exc_tb): ...
163162

164-
def raise_for_status(self):
165-
...
163+
def raise_for_status(self): ...
166164

167-
async def close(self):
168-
...
165+
async def close(self): ...
169166

170167
@property
171168
def status(self):

tests/unit/test_chain_nuls1_compat.py

+1
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
33
This file tests that both implementations returns identical results.
44
"""
5+
56
from pathlib import Path
67
from tempfile import NamedTemporaryFile
78

tests/unit/test_utils.py

+47-1
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,13 @@
1212
StoreMessage,
1313
)
1414
from aleph_message.models.execution.environment import MachineResources
15+
from aleph_message.models.execution.volume import (
16+
EphemeralVolume,
17+
ImmutableVolume,
18+
PersistentVolume,
19+
)
1520

16-
from aleph.sdk.utils import enum_as_str, get_message_type_value
21+
from aleph.sdk.utils import enum_as_str, get_message_type_value, parse_volume
1722

1823

1924
def test_get_message_type_value():
@@ -128,3 +133,44 @@ async def test_prepare_aleph_message(
128133
)
129134

130135
assert message.content.dict() == content
136+
137+
138+
def test_parse_immutable_volume():
139+
volume_dict = {
140+
"ref": "QmX8K1c22WmQBAww5ShWQqwMiFif7XFrJD6iFBj7skQZXW",
141+
"use_latest": True,
142+
"comment": "Dummy hash",
143+
}
144+
volume = parse_volume(volume_dict)
145+
volume = parse_volume(volume)
146+
assert volume
147+
assert isinstance(volume, ImmutableVolume)
148+
149+
150+
def test_parse_ephemeral_volume():
151+
volume_dict = {
152+
"comment": "Dummy hash",
153+
"ephemeral": True,
154+
"size_mib": 1,
155+
}
156+
volume = parse_volume(volume_dict)
157+
volume = parse_volume(volume)
158+
assert volume
159+
assert isinstance(volume, EphemeralVolume)
160+
161+
162+
def test_parse_persistent_volume():
163+
volume_dict = {
164+
"parent": {
165+
"ref": "QmX8K1c22WmQBAww5ShWQqwMiFif7XFrJD6iFBj7skQZXW",
166+
"use_latest": True,
167+
"comment": "Dummy hash",
168+
},
169+
"persistence": "host",
170+
"name": "test",
171+
"size_mib": 1,
172+
}
173+
volume = parse_volume(volume_dict)
174+
volume = parse_volume(volume)
175+
assert volume
176+
assert isinstance(volume, PersistentVolume)

0 commit comments

Comments
 (0)