Browse Source

4.0.0: Use new launch and topup endpoints

api_client return types are different now, hence the major version.
master 4.0.0
/dev/null 4 weeks ago
parent
commit
4c042c24a6
  1. 2
      Makefile
  2. 2
      setup.py
  3. 6
      src/sporestack/api.py
  4. 62
      src/sporestack/api_client.py
  5. 148
      src/sporestack/cli.py
  6. 81
      tests/test_api_client.py

2
Makefile

@ -7,7 +7,7 @@ test:
isort --check .
flake8 .
mypy --strict .
pytest --cov=sporestack --cov-fail-under=39 --cov-report=term --durations=3 --cache-clear
pytest --cov=sporestack --cov-fail-under=50 --cov-report=term --durations=3 --cache-clear
install:
python3 -m pip install -r requirements.txt

2
setup.py

@ -2,7 +2,7 @@
from setuptools import find_packages, setup
VERSION = "3.1.0"
VERSION = "3.2.0"
DOWNLOAD_HOST = "https://git.sporestack.com"
REPO_URL = f"{DOWNLOAD_HOST}/SporeStack/sporestack-python"
DOWNLOAD_URL = f"{REPO_URL}/archive/{VERSION}.tar.gz"

6
src/sporestack/api.py

@ -90,7 +90,8 @@ class token_balance(object):
class server_launch(object):
url = "/v2/launch"
url = "/server/{machine_id}/launch"
deprecated_url = "/v2/launch"
method = "POST"
class Request(BaseModel):
@ -123,7 +124,8 @@ class server_launch(object):
class server_topup(object):
url = "/v2/topup"
url = "/server/{machine_id}/topup"
deprecated_url = "/v2/topup"
method = "POST"
class Request(BaseModel):

62
src/sporestack/api_client.py

@ -123,22 +123,24 @@ def launch(
retry: bool = False,
affiliate_amount: Optional[int] = None,
affiliate_token: Optional[str] = None,
) -> Any:
json_params = {
"machine_id": machine_id,
"days": days,
"flavor": flavor,
"currency": currency,
"region": region,
"settlement_token": settlement_token,
"operating_system": operating_system,
"ssh_key": ssh_key,
"affiliate_amount": affiliate_amount,
"affiliate_token": affiliate_token,
}
url = f"{api_endpoint}/v2/launch"
return api_request(url=url, json_params=json_params, retry=retry)
) -> api.server_launch.Response:
request = api.server_launch.Request(
machine_id=machine_id,
days=days,
currency=currency,
settlement_token=settlement_token,
affiliate_amount=affiliate_amount,
affiliate_token=affiliate_token,
flavor=flavor,
region=region,
operating_system=operating_system,
ssh_key=ssh_key,
)
url = api_endpoint + api.server_launch.url.format(machine_id=machine_id)
response = api_request(url=url, json_params=request.dict(), retry=retry)
response_object = api.server_launch.Response.parse_obj(response)
assert response_object.machine_id == machine_id
return response_object
def topup(
@ -150,17 +152,23 @@ def topup(
retry: bool = False,
affiliate_amount: Optional[int] = None,
affiliate_token: Optional[str] = None,
) -> Any:
json_params = {
"machine_id": machine_id,
"days": days,
"settlement_token": settlement_token,
"currency": currency,
"affiliate_amount": affiliate_amount,
"affiliate_token": affiliate_token,
}
url = f"{api_endpoint}/v2/topup"
return api_request(url=url, json_params=json_params, retry=retry)
) -> api.server_topup.Response:
"""
Topup a server.
"""
request = api.server_topup.Request(
machine_id=machine_id,
days=days,
currency=currency,
settlement_token=settlement_token,
affiliate_amount=affiliate_amount,
affiliate_token=affiliate_token,
)
url = api_endpoint + api.server_topup.url.format(machine_id=machine_id)
response = api_request(url=url, json_params=request.dict(), retry=retry)
response_object = api.server_topup.Response.parse_obj(response)
assert response_object.machine_id == machine_id
return response_object
def start(machine_id: str, api_endpoint: str = API_ENDPOINT) -> None:

148
src/sporestack/cli.py

@ -91,38 +91,29 @@ def launch(
machine_id = random_machine_id()
def create_vm() -> Any:
assert currency is not None
return api_client.launch(
machine_id=machine_id,
days=days,
flavor=flavor,
operating_system=operating_system,
ssh_key=ssh_key,
currency=currency,
region=region,
settlement_token=settlement_token,
api_endpoint=api_endpoint,
affiliate_amount=affiliate_amount,
affiliate_token=affiliate_token,
retry=True,
)
assert currency is not None
response = api_client.launch(
machine_id=machine_id,
days=days,
flavor=flavor,
operating_system=operating_system,
ssh_key=ssh_key,
currency=currency,
region=region,
settlement_token=settlement_token,
api_endpoint=api_endpoint,
affiliate_amount=affiliate_amount,
affiliate_token=affiliate_token,
retry=True,
)
created_dict = create_vm()
# This will be false at least the first time if paying with BTC or BCH.
if created_dict["paid"] is False:
uri = created_dict["payment"]["uri"]
if "usd" in created_dict["payment"]:
usd = created_dict["payment"]["usd"]
else:
# Whoops?
usd = ""
if response.payment.paid is False:
assert response.payment.uri is not None
make_payment(
currency=currency,
uri=uri,
usd=usd,
uri=response.payment.uri,
usd=response.payment.usd,
)
tries = 360
@ -132,27 +123,53 @@ def launch(
# FIXME: Wait one hour in a smarter way.
# Waiting for payment to set in.
time.sleep(10)
created_dict = create_vm()
if created_dict["paid"] is True:
response = api_client.launch(
machine_id=machine_id,
days=days,
flavor=flavor,
operating_system=operating_system,
ssh_key=ssh_key,
currency=currency,
region=region,
settlement_token=settlement_token,
api_endpoint=api_endpoint,
affiliate_amount=affiliate_amount,
affiliate_token=affiliate_token,
retry=True,
)
if response.payment.paid is True:
break
if created_dict["created"] is False:
if response.created is False:
tries = 360
while tries > 0:
print("Waiting for server to build...", file=sys.stderr)
tries = tries + 1
# Waiting for server to spin up.
time.sleep(10)
created_dict = create_vm()
if created_dict["created"] is True:
response = api_client.launch(
machine_id=machine_id,
days=days,
flavor=flavor,
operating_system=operating_system,
ssh_key=ssh_key,
currency=currency,
region=region,
settlement_token=settlement_token,
api_endpoint=api_endpoint,
affiliate_amount=affiliate_amount,
affiliate_token=affiliate_token,
retry=True,
)
if response.created is True:
break
if created_dict["created"] is False:
if response.created is False:
# FIXME: Bad exception type.
raise ValueError("Server creation failed, tries exceeded.")
created_dict = response.dict()
created_dict["vm_hostname"] = hostname
created_dict["machine_id"] = machine_id
created_dict["api_endpoint"] = api_endpoint
save_machine_info(created_dict)
print(pretty_machine_info(created_dict), file=sys.stderr)
@ -189,37 +206,28 @@ def topup(
machine_id = machine_info["machine_id"]
if api_endpoint is None:
api_endpoint = machine_info["api_endpoint"]
assert api_endpoint is not None
def topup_vm() -> Any:
assert currency is not None
assert api_endpoint is not None
return api_client.topup(
machine_id=machine_id,
days=days,
currency=currency,
api_endpoint=api_endpoint,
settlement_token=settlement_token,
affiliate_amount=affiliate_amount,
affiliate_token=affiliate_token,
retry=True,
)
topped_dict = topup_vm()
assert currency is not None
assert api_endpoint is not None
response = api_client.topup(
machine_id=machine_id,
days=days,
currency=currency,
api_endpoint=api_endpoint,
settlement_token=settlement_token,
affiliate_amount=affiliate_amount,
affiliate_token=affiliate_token,
retry=True,
)
# This will be false at least the first time if paying with anything
# but settlement.
if topped_dict["paid"] is False:
uri = topped_dict["payment"]["uri"]
if "usd" in topped_dict["payment"]:
usd = topped_dict["payment"]["usd"]
else:
usd = None
if response.payment.paid is False:
assert response.payment.uri is not None
make_payment(
currency=currency,
uri=uri,
usd=usd,
uri=response.payment.uri,
usd=response.payment.usd,
)
tries = 360
@ -229,11 +237,20 @@ def topup(
# FIXME: Wait one hour in a smarter way.
# Waiting for payment to set in.
time.sleep(10)
topped_dict = topup_vm()
if topped_dict["paid"] is True:
response = api_client.topup(
machine_id=machine_id,
days=days,
currency=currency,
api_endpoint=api_endpoint,
settlement_token=settlement_token,
affiliate_amount=affiliate_amount,
affiliate_token=affiliate_token,
retry=True,
)
if response.payment.paid is True:
break
machine_info["expiration"] = topped_dict["expiration"]
machine_info["expiration"] = response.expiration
save_machine_info(machine_info, overwrite=True)
print(machine_info["expiration"])
@ -348,8 +365,7 @@ def machine_exists(hostname: str) -> bool:
"""
Check if the VM's JSON exists locally.
"""
directory = machine_info_path()
return directory.joinpath(f"{hostname}.json").exists()
return machine_info_path().joinpath(f"{hostname}.json").exists()
@cli.command()

81
tests/test_api_client.py

@ -20,10 +20,60 @@ def test_is_onion_url() -> None:
assert api_client.is_onion_url("http://me.me/file.onion") is False
@patch("sporestack.api_client.api_request")
def test_launch(mock_api_request: MagicMock) -> None:
with pytest.raises(ValidationError):
api_client.launch(
"dummymachineid",
currency="xmr",
days=1,
operating_system="freebsd-12",
ssh_key="id-rsa...",
flavor="aflavor",
)
json_params = {
"machine_id": "dummymachineid",
"days": 1,
"currency": "xmr",
"flavor": "aflavor",
"ssh_key": "id-rsa...",
"operating_system": "freebsd-12",
"region": None,
"organization": None,
"settlement_token": None,
"affiliate_amount": None,
"affiliate_token": None,
}
mock_api_request.assert_called_once_with(
url="https://api.sporestack.com/server/dummymachineid/launch",
json_params=json_params,
retry=False,
)
@patch("sporestack.api_client.api_request")
def test_topup(mock_api_request: MagicMock) -> None:
with pytest.raises(ValidationError):
api_client.topup("dummymachineid", currency="xmr", days=1)
json_params = {
"machine_id": "dummymachineid",
"days": 1,
"currency": "xmr",
"settlement_token": None,
"affiliate_amount": None,
"affiliate_token": None,
}
mock_api_request.assert_called_once_with(
url="https://api.sporestack.com/server/dummymachineid/topup",
json_params=json_params,
retry=False,
)
@patch("sporestack.api_client.api_request")
def test_start(mock_api_request: MagicMock) -> None:
api_client.start("dummymachineid")
assert mock_api_request.called_once_with(
mock_api_request.assert_called_once_with(
"https://api.sporestack.com/server/dummymachineid/start", empty_post=True
)
@ -31,7 +81,7 @@ def test_start(mock_api_request: MagicMock) -> None:
@patch("sporestack.api_client.api_request")
def test_stop(mock_api_request: MagicMock) -> None:
api_client.stop("dummymachineid")
assert mock_api_request.called_once_with(
mock_api_request.assert_called_once_with(
"https://api.sporestack.com/server/dummymachineid/stop", empty_post=True
)
@ -40,14 +90,35 @@ def test_stop(mock_api_request: MagicMock) -> None:
def test_info(mock_api_request: MagicMock) -> None:
with pytest.raises(ValidationError):
api_client.info("dummymachineid")
assert mock_api_request.called_once_with(
mock_api_request.assert_called_once_with(
"https://api.sporestack.com/server/dummymachineid/info"
)
@patch("sporestack.api_client.api_request")
def test_delete(mock_api_request: MagicMock) -> None:
api_client.stop("dummymachineid")
assert mock_api_request.called_once_with(
api_client.delete("dummymachineid")
mock_api_request.assert_called_once_with(
"https://api.sporestack.com/server/dummymachineid/delete", empty_post=True
)
@patch("sporestack.api_client.api_request")
def test_token_balance(mock_api_request: MagicMock) -> None:
with pytest.raises(ValidationError):
api_client.token_balance("dummytoken")
mock_api_request.assert_called_once_with(
url="https://api.sporestack.com/token/dummytoken/balance"
)
@patch("sporestack.api_client.api_request")
def test_token_enable(mock_api_request: MagicMock) -> None:
with pytest.raises(ValidationError):
api_client.token_enable("dummytoken", currency="xmr", dollars=20)
json_params = {"currency": "xmr", "dollars": 20}
mock_api_request.assert_called_once_with(
url="https://api.sporestack.com/token/dummytoken/enable",
json_params=json_params,
retry=False,
)

Loading…
Cancel
Save