Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

207 adding creation of an instance with payg #208

Closed
wants to merge 29 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
6476168
Feat: Impoossible to create an instance with a PAYG-compatible CRN fr…
Antonyjin May 15, 2024
c879535
Fix: Fetching nodes not enough visual
Antonyjin May 21, 2024
15c4513
Feat: CRN hash was not checked
Antonyjin May 22, 2024
eb18516
Fix: Tab don't show the right info for the crn
Antonyjin May 24, 2024
d9cb7ba
Fix: Error about payment in the message if it's not PAYG
Antonyjin May 24, 2024
bc0ec46
Fix: The fetch CRN process was too long
Antonyjin May 29, 2024
43abce3
Fix: Using the last server header for getting the CRN version
Antonyjin May 30, 2024
b0064df
Fix: Http check was not perfect
Antonyjin May 30, 2024
6215eaf
Fix: Missing typing notation
Antonyjin May 31, 2024
60a9a9c
Fix: Script crash because of unhandled errors
Antonyjin Jun 3, 2024
faf6351
Fix: The way to stop the queue process was not clear enough
Antonyjin Jun 3, 2024
7307d23
Fix: Adding a list of str in queue was unclear
Antonyjin Jun 4, 2024
7db3cf8
Fix: mypy raised a lot of errors
Antonyjin Jun 5, 2024
816fb6e
Fix: Mypy raised some error in instance.py
Antonyjin Jun 5, 2024
f5a86d5
Fix: Ruff raised some errors about placeholders with f-string
Antonyjin Jun 5, 2024
33fe36a
Fix: Missing docstrings in the new functions
Antonyjin Jun 5, 2024
83c576e
Fix: Missing type annotation inside utils.py
Antonyjin Jun 5, 2024
d61ffbd
Fix: Hardcoded string for the version of CRN needed to be fix
Antonyjin Jun 5, 2024
33fdc09
Fix: Refactor import statements in aleph_client/utils.py
Antonyjin Jun 6, 2024
d229436
Fix: Refactor import statements in aleph_client/utils.py
Antonyjin Jun 6, 2024
4370c29
Fix: hypervisor selection returned a string and not HypervisorType
Antonyjin Jun 6, 2024
b33c516
Fix: `unpersist` fetch several messages and then gets the right message
Antonyjin Jun 6, 2024
267f16c
Fix: Library `psutil` was not useful in aleph-client
hoh Jun 7, 2024
9f25a55
Cleanup: `black` and `isort`
hoh Jun 7, 2024
94f5af4
Refactor: Move models in distinct file
hoh Jun 7, 2024
6179601
Cleanup: Move imports and logger
hoh Jun 7, 2024
91545cd
Refactor: Large refactoring in pair programming
hoh Jun 7, 2024
1f8f0cd
Add tests \o/
hoh Jun 7, 2024
8eda387
Fix type checking
hoh Jun 7, 2024
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
4 changes: 3 additions & 1 deletion src/aleph_client/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,6 @@

def __getattr__(name):
if name in moved_types:
raise ImportError(f"The 'aleph_client.{name}' type is deprecated and has been removed from aleph_client. Please use `aleph.sdk.{name}` instead.")
raise ImportError(
f"The 'aleph_client.{name}' type is deprecated and has been removed from aleph_client. Please use `aleph.sdk.{name}` instead."
)
3 changes: 1 addition & 2 deletions src/aleph_client/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@
Aleph Client command-line interface.
"""

from aleph_client.utils import AsyncTyper

from aleph_client.commands import (
about,
account,
Expand All @@ -15,6 +13,7 @@
node,
program,
)
from aleph_client.utils import AsyncTyper

app = AsyncTyper(no_args_is_help=True)

Expand Down
4 changes: 3 additions & 1 deletion src/aleph_client/commands/account.py
Original file line number Diff line number Diff line change
Expand Up @@ -170,7 +170,9 @@ async def balance(
response = await session.get(uri)
if response.status == 200:
balance_data = await response.json()
formatted_balance_data = json.dumps(balance_data, indent=4, default=extended_json_encoder)
formatted_balance_data = json.dumps(
balance_data, indent=4, default=extended_json_encoder
)
typer.echo(formatted_balance_data)
else:
typer.echo(
Expand Down
6 changes: 4 additions & 2 deletions src/aleph_client/commands/aggregate.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
from aleph.sdk.query.filters import MessageFilter
from aleph.sdk.types import AccountFromPrivateKey
from aleph.sdk.utils import extended_json_encoder
from aleph_message.models import MessageType
from aleph_message.models.base import MessageType

from aleph_client.commands import help_strings
from aleph_client.commands.utils import setup_logging
Expand Down Expand Up @@ -95,7 +95,9 @@ async def post(
inline=inline,
address=address,
)
log_message = json.dumps(message.dict(), indent=4, default=extended_json_encoder)
log_message = json.dumps(
message.dict(), indent=4, default=extended_json_encoder
)
typer.echo(log_message)


Expand Down
30 changes: 19 additions & 11 deletions src/aleph_client/commands/domain.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,16 @@
from aleph.sdk.exceptions import DomainConfigurationError
from aleph.sdk.query.filters import MessageFilter
from aleph.sdk.types import AccountFromPrivateKey
from aleph_client.commands import help_strings
from aleph_client.commands.utils import is_environment_interactive
from aleph_client.utils import AsyncTyper
from aleph_message.models import AggregateMessage, MessageType
from aleph_message.models import AggregateMessage
from aleph_message.models.base import MessageType
from rich.console import Console
from rich.prompt import Confirm, Prompt
from rich.table import Table

from aleph_client.commands import help_strings
from aleph_client.commands.utils import is_environment_interactive
from aleph_client.utils import AsyncTyper

app = AsyncTyper(no_args_is_help=True)


Expand Down Expand Up @@ -83,7 +85,9 @@ async def attach_resource(
resource_type = await get_target_type(fqdn)

if resource_type == TargetType.IPFS and not catch_all_path:
catch_all_path = Prompt.ask("Catch all path? ex: /404.html or press [Enter] to ignore", default=None)
catch_all_path = Prompt.ask(
"Catch all path? ex: /404.html or press [Enter] to ignore", default=None
)

if domain_info is not None and domain_info.get("info"):
current_resource = domain_info["info"]["message_id"]
Expand All @@ -109,14 +113,12 @@ async def attach_resource(
"message_id": item_hash,
"type": resource_type,
# console page compatibility
"programType": resource_type
"programType": resource_type,
}
}

if catch_all_path and catch_all_path.startswith("/"):
aggregate_content[fqdn]["options"] = {
"catch_all_path": catch_all_path
}
aggregate_content[fqdn]["options"] = {"catch_all_path": catch_all_path}

aggregate_message, message_status = await client.create_aggregate(
key="domains", content=aggregate_content, channel="ALEPH-CLOUDSOLUTIONS"
Expand Down Expand Up @@ -276,14 +278,20 @@ async def attach(
item_hash: Optional[str] = typer.Option(
None, help=help_strings.CUSTOM_DOMAIN_ITEM_HASH
),
catch_all_path: str = typer.Option(default=None, help=help_strings.IPFS_CATCH_ALL_PATH),
catch_all_path: str = typer.Option(
default=None, help=help_strings.IPFS_CATCH_ALL_PATH
),
ask: bool = typer.Option(default=True, help=help_strings.ASK_FOR_CONFIRMATION),
):
"""Attach resource to a Custom Domain."""
account: AccountFromPrivateKey = _load_account(private_key, private_key_file)

await attach_resource(
account, Hostname(fqdn), item_hash, interactive=False if (not ask) else None, catch_all_path=catch_all_path
account,
Hostname(fqdn),
item_hash,
interactive=False if (not ask) else None,
catch_all_path=catch_all_path,
)
raise typer.Exit()

Expand Down
4 changes: 3 additions & 1 deletion src/aleph_client/commands/help_strings.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,6 @@
Requires at least a "ref" (message hash) and "mount" path. "use_latest" is True by default, to use the latest version of the volume, if it has been amended. See the docs for more info: https://docs.aleph.im/computing/volumes/immutable/\n
Example: --immutable-volume ref=25a393222692c2f73489dc6710ae87605a96742ceef7b91de4d7ec34bb688d94,mount=/lib/python3.8/site-packages"""
ASK_FOR_CONFIRMATION = "Prompt user for confirmation"
IPFS_CATCH_ALL_PATH = "Choose a relative path to catch all unmatched route or a 404 error"
IPFS_CATCH_ALL_PATH = (
"Choose a relative path to catch all unmatched route or a 404 error"
)
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,18 @@
)
from aleph.sdk.query.filters import MessageFilter
from aleph.sdk.types import AccountFromPrivateKey, StorageEnum
from aleph_message.models import InstanceMessage, ItemHash, MessageType, StoreMessage
from aleph_message.models import InstanceMessage, StoreMessage
from aleph_message.models.base import Chain, MessageType
from aleph_message.models.execution.base import Payment, PaymentType
from aleph_message.models.execution.environment import HypervisorType
from aleph_message.models.item_hash import ItemHash
from rich import box
from rich.console import Console
from rich.prompt import Prompt
from rich.table import Table

from aleph_client.commands import help_strings
from aleph_client.commands.instance.display import fetch_crn_info
from aleph_client.commands.utils import (
get_or_prompt_volumes,
setup_logging,
Expand All @@ -36,13 +40,12 @@
app = AsyncTyper(no_args_is_help=True)


def load_ssh_pubkey(ssh_pubkey_file: Path) -> str:
with open(ssh_pubkey_file, "r") as f:
return f.read().strip()


@app.command()
async def create(
hold: bool = typer.Option(
default=False,
help="Pay using the holder tier instead of pay-as-you-go",
),
channel: Optional[str] = typer.Option(default=None, help=help_strings.CHANNEL),
memory: int = typer.Option(
settings.DEFAULT_INSTANCE_MEMORY, help="Maximum memory allocation on vm in MiB"
Expand Down Expand Up @@ -112,7 +115,7 @@ def validate_ssh_pubkey_file(file: Union[str, Path]) -> Path:
)
)

ssh_pubkey = load_ssh_pubkey(ssh_pubkey_file)
ssh_pubkey: str = ssh_pubkey_file.read_text().strip()

account: AccountFromPrivateKey = _load_account(private_key, private_key_file)

Expand All @@ -127,15 +130,26 @@ def validate_ssh_pubkey_file(file: Union[str, Path]) -> Path:
HypervisorType.qemu: "qemu",
}

if hold:
# Holder tier
reward_address = None
else:
# Pay-as-you-go
valid_address = await fetch_crn_info()
reward_address = validated_prompt(
"Please select and enter the reward address of the wanted CRN",
lambda x: x in valid_address,
)

rootfs = Prompt.ask(
f"Do you want to use a custom rootfs or one of the following prebuilt ones?",
"Do you want to use a custom rootfs or one of the following prebuilt ones?",
default=rootfs,
choices=[*os_map.values(), "custom"],
)

if rootfs == "custom":
rootfs = validated_prompt(
f"Enter the item hash of the rootfs to use for your instance",
"Enter the item hash of the rootfs to use for your instance",
lambda x: len(x) == 64,
)
else:
Expand All @@ -152,22 +166,24 @@ def validate_ssh_pubkey_file(file: Union[str, Path]) -> Path:
rootfs_size = rootfs_message.content.size

vcpus = validated_int_prompt(
f"Number of virtual cpus to allocate", vcpus, min_value=1, max_value=4
"Number of virtual cpus to allocate", vcpus, min_value=1, max_value=4
)

memory = validated_int_prompt(
f"Maximum memory allocation on vm in MiB", memory, min_value=2000, max_value=8000
"Maximum memory allocation on vm in MiB", memory, min_value=2000, max_value=8000
)

rootfs_size = validated_int_prompt(
f"Disk size in MiB", rootfs_size, min_value=20000, max_value=100000
"Disk size in MiB", rootfs_size, min_value=20000, max_value=100000
)

hypervisor = Prompt.ask(
f"Which hypervisor you want to use?",
default=hypervisor,
choices=[*hv_map.values()],
)
hypervisor = HypervisorType[
Prompt.ask(
"Which hypervisor you want to use?",
default=hypervisor.name,
choices=[*hv_map.values()],
)
]

volumes = get_or_prompt_volumes(
persistent_volume=persistent_volume,
Expand All @@ -178,6 +194,13 @@ def validate_ssh_pubkey_file(file: Union[str, Path]) -> Path:
async with AuthenticatedAlephHttpClient(
account=account, api_server=sdk_settings.API_HOST
) as client:
payment: Optional[Payment] = None
if reward_address:
payment = Payment(
chain=Chain.AVAX,
receiver=reward_address,
type=PaymentType["superfluid"],
)
try:
message, status = await client.create_instance(
sync=True,
Expand All @@ -191,6 +214,7 @@ def validate_ssh_pubkey_file(file: Union[str, Path]) -> Path:
volumes=volumes,
ssh_keys=[ssh_pubkey],
hypervisor=hypervisor,
payment=payment,
)
except InsufficientFundsError as e:
typer.echo(
Expand Down Expand Up @@ -291,7 +315,7 @@ async def _show_instances(messages: List[InstanceMessage]):
)
console = Console()
console.print(table)
console.print(f"To connect to an instance, use:\n\n" f" ssh root@<ipv6 address>\n")
console.print("To connect to an instance, use:\n\n" " ssh root@<ipv6 address>\n")


@app.command()
Expand Down
Loading
Loading