10.7.0: Add suspended server support
This commit is contained in:
parent
190b94746c
commit
ac7eb3a186
|
@ -15,6 +15,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||||
|
|
||||||
- Nothing yet.
|
- Nothing yet.
|
||||||
|
|
||||||
|
## [10.7.0 - 2023-10-31]
|
||||||
|
|
||||||
|
## Added
|
||||||
|
|
||||||
|
- Added `suspended_at` to server info response object.
|
||||||
|
- Added `autorenew_servers` to token info response object.
|
||||||
|
- Added `suspended_servers` to token info response object.
|
||||||
|
|
||||||
## [10.6.3 - 2023-09-18]
|
## [10.6.3 - 2023-09-18]
|
||||||
|
|
||||||
## Changed
|
## Changed
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
"""SporeStack API library and CLI for launching servers with Monero or Bitcoin"""
|
"""SporeStack API library and CLI for launching servers with Monero or Bitcoin"""
|
||||||
|
|
||||||
__all__ = ["api", "api_client", "exceptions"]
|
__all__ = ["api", "api_client", "client", "exceptions"]
|
||||||
|
|
||||||
__version__ = "10.6.3"
|
__version__ = "10.7.0"
|
||||||
|
|
|
@ -117,10 +117,11 @@ class ServerInfo:
|
||||||
ipv6: str
|
ipv6: str
|
||||||
region: str
|
region: str
|
||||||
flavor: Flavor
|
flavor: Flavor
|
||||||
deleted: bool
|
deleted: Annotated[bool, Field(deprecated=True)]
|
||||||
deleted_at: int
|
deleted_at: int
|
||||||
deleted_by: Union[ServerDeletedBy, None]
|
deleted_by: Union[ServerDeletedBy, None]
|
||||||
forgotten_at: Union[datetime, None]
|
forgotten_at: Union[datetime, None]
|
||||||
|
suspended_at: Union[datetime, None]
|
||||||
operating_system: str
|
operating_system: str
|
||||||
hostname: str
|
hostname: str
|
||||||
autorenew: bool
|
autorenew: bool
|
||||||
|
|
|
@ -303,38 +303,49 @@ def epoch_to_human(epoch: int) -> str:
|
||||||
|
|
||||||
|
|
||||||
def print_machine_info(info: "api.ServerInfo.Response") -> None:
|
def print_machine_info(info: "api.ServerInfo.Response") -> None:
|
||||||
if info.hostname != "":
|
from rich.console import Console
|
||||||
typer.echo(f"Hostname: {info.hostname}")
|
|
||||||
else:
|
|
||||||
typer.echo("Hostname: (none) (No hostname set)")
|
|
||||||
|
|
||||||
typer.echo(f"Machine ID (keep this secret!): {info.machine_id}")
|
console = Console(width=None if sys.stdout.isatty() else 10**9)
|
||||||
|
|
||||||
|
output = ""
|
||||||
|
|
||||||
|
if info.hostname != "":
|
||||||
|
output += f"Hostname: {info.hostname}\n"
|
||||||
|
else:
|
||||||
|
output += "Hostname: (none) (No hostname set)\n"
|
||||||
|
|
||||||
|
output += f"Machine ID (keep this secret!): {info.machine_id}\n"
|
||||||
if info.ipv6 != "":
|
if info.ipv6 != "":
|
||||||
typer.echo(f"IPv6: {info.ipv6}")
|
output += f"IPv6: {info.ipv6}\n"
|
||||||
else:
|
else:
|
||||||
typer.echo("IPv6: (Not yet assigned)")
|
output += "IPv6: (Not yet assigned)\n"
|
||||||
if info.ipv4 != "":
|
if info.ipv4 != "":
|
||||||
typer.echo(f"IPv4: {info.ipv4}")
|
output += f"IPv4: {info.ipv4}\n"
|
||||||
else:
|
else:
|
||||||
typer.echo("IPv4: (Not yet assigned)")
|
output += "IPv4: (Not yet assigned)\n"
|
||||||
typer.echo(f"Region: {info.region}")
|
output += f"Region: {info.region}\n"
|
||||||
typer.echo(f"Flavor: {info.flavor.slug}")
|
output += f"Flavor: {info.flavor.slug}\n"
|
||||||
typer.echo(f"Expiration: {epoch_to_human(info.expiration)}")
|
output += f"Token (keep this secret!): {info.token}\n"
|
||||||
typer.echo(f"Token (keep this secret!): {info.token}")
|
if info.deleted_at != 0:
|
||||||
if info.deleted_at != 0 or info.deleted:
|
output += f"Server deleted at: {epoch_to_human(info.deleted_at)}\n"
|
||||||
typer.echo("Server was deleted!")
|
|
||||||
if info.deleted_at != 0:
|
|
||||||
typer.echo(f"Server deleted at: {epoch_to_human(info.deleted_at)}")
|
|
||||||
if info.deleted_by is not None:
|
if info.deleted_by is not None:
|
||||||
typer.echo(f"Server deleted by: {info.deleted_by.value}")
|
output += f"Server deleted by: {info.deleted_by.value}\n"
|
||||||
if info.forgotten_at is not None:
|
if info.forgotten_at is not None:
|
||||||
typer.echo(f"Server forgotten at: {info.forgotten_at}")
|
output += f"Server forgotten at: {info.forgotten_at}\n"
|
||||||
else:
|
else:
|
||||||
typer.echo(f"Running: {info.running}")
|
msg = f"Running: {info.running}\n"
|
||||||
|
if info.suspended_at is not None:
|
||||||
|
msg = (
|
||||||
|
"Running: Server is powered off because it is [bold]suspended[/bold].\n"
|
||||||
|
)
|
||||||
|
output += msg
|
||||||
time_to_live = info.expiration - int(time.time())
|
time_to_live = info.expiration - int(time.time())
|
||||||
hours = time_to_live // 3600
|
hours = time_to_live // 3600
|
||||||
typer.echo(f"Server will be deleted in {hours} hours.")
|
output += f"Server will be deleted in {hours} hours.\n"
|
||||||
typer.echo(f"Autorenew: {info.autorenew}")
|
output += f"Expiration: {epoch_to_human(info.expiration)}\n"
|
||||||
|
output += f"Autorenew: {info.autorenew}"
|
||||||
|
|
||||||
|
console.print(output)
|
||||||
|
|
||||||
|
|
||||||
@server_cli.command(name="list")
|
@server_cli.command(name="list")
|
||||||
|
@ -414,6 +425,9 @@ def server_list(
|
||||||
str(info.autorenew),
|
str(info.autorenew),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if info.suspended_at != 0:
|
||||||
|
typer.echo(f"Warning: {info.machine_id} is suspended!", err=True)
|
||||||
|
|
||||||
printed_machine_ids.append(info.machine_id)
|
printed_machine_ids.append(info.machine_id)
|
||||||
|
|
||||||
console.print(table)
|
console.print(table)
|
||||||
|
@ -887,7 +901,9 @@ def token_info(token: Annotated[str, typer.Argument()] = DEFAULT_TOKEN) -> None:
|
||||||
info = client.token.info()
|
info = client.token.info()
|
||||||
print(f"[bold]Token Information for {token} ({_token})[/bold]")
|
print(f"[bold]Token Information for {token} ({_token})[/bold]")
|
||||||
print(f"Balance: [green]{info.balance_usd}")
|
print(f"Balance: [green]{info.balance_usd}")
|
||||||
print(f"Total Servers: {info.servers}")
|
print(f"Total Servers (not deleted): {info.servers}")
|
||||||
|
print(f"Servers set to autorenew: {info.autorenew_servers}")
|
||||||
|
print(f"Suspended servers: {info.suspended_servers}")
|
||||||
print(
|
print(
|
||||||
f"Burn Rate: [red]{info.burn_rate_usd}[/red] "
|
f"Burn Rate: [red]{info.burn_rate_usd}[/red] "
|
||||||
"(per day of servers set to autorenew)"
|
"(per day of servers set to autorenew)"
|
||||||
|
|
|
@ -62,12 +62,13 @@ class OperatingSystem(BaseModel):
|
||||||
class TokenInfo(BaseModel):
|
class TokenInfo(BaseModel):
|
||||||
balance_cents: int
|
balance_cents: int
|
||||||
balance_usd: str
|
balance_usd: str
|
||||||
burn_rate: int
|
burn_rate: Annotated[int, Field(deprecated=True)]
|
||||||
"""Deprecated."""
|
|
||||||
burn_rate_cents: int
|
burn_rate_cents: int
|
||||||
burn_rate_usd: str
|
burn_rate_usd: str
|
||||||
days_remaining: int
|
days_remaining: int
|
||||||
servers: int
|
servers: int
|
||||||
|
autorenew_servers: int
|
||||||
|
suspended_servers: int
|
||||||
|
|
||||||
|
|
||||||
class Region(BaseModel):
|
class Region(BaseModel):
|
||||||
|
|
|
@ -81,6 +81,8 @@ def test_token_info(respx_mock: respx.MockRouter) -> None:
|
||||||
"balance_cents": 0,
|
"balance_cents": 0,
|
||||||
"balance_usd": "$0.00",
|
"balance_usd": "$0.00",
|
||||||
"servers": 0,
|
"servers": 0,
|
||||||
|
"autorenew_servers": 0,
|
||||||
|
"suspended_servers": 0,
|
||||||
"burn_rate": 0,
|
"burn_rate": 0,
|
||||||
"burn_rate_usd": "$0.00",
|
"burn_rate_usd": "$0.00",
|
||||||
"burn_rate_cents": 0,
|
"burn_rate_cents": 0,
|
||||||
|
@ -133,6 +135,7 @@ def test_server_info(respx_mock: respx.MockRouter) -> None:
|
||||||
"deleted_at": 0,
|
"deleted_at": 0,
|
||||||
"deleted_by": None,
|
"deleted_by": None,
|
||||||
"forgotten_at": None,
|
"forgotten_at": None,
|
||||||
|
"suspended_at": None,
|
||||||
"operating_system": "debian-11",
|
"operating_system": "debian-11",
|
||||||
}
|
}
|
||||||
route_response = httpx.Response(200, json=response_json)
|
route_response = httpx.Response(200, json=response_json)
|
||||||
|
|
Loading…
Reference in New Issue