Skip to main content

Async-first Python SDK and CLI for Rackspace Spot

Project description

rsspot

rsspot is a Python SDK and CLI for Rackspace Spot with unified sync/async ergonomics, sqlite-backed runtime state, and profile-based configuration.

Highlights

  • Unified client API:
    • SpotClient for sync + async access on one object
    • AsyncSpotClient for explicit async-only usage
  • Persistent sqlite state (state.db) for:
    • defaults/preferences
    • HTTP cache
    • redacted CLI command history
    • VM registration ledger entries
  • Config precedence + migration:
    • new default path: ~/.config/rsspot/config.yml
    • legacy ~/.spot_config auto-imported/migrated
  • Strong typing via pydantic models
  • Multi-profile workflows + global singleton helpers
  • OpenAPI sync/index scripts for upstream tracking

Install

uv sync

Quickstart (CLI)

uv run rsspot configure \
  --profile default \
  --org <org-name> \
  --region us-central-dfw-1 \
  --refresh-token "$SPOT_REFRESH_TOKEN"

uv run rsspot organizations list --output table

Pricing Explorer + Builder

# default output is a rich table when --output is not explicitly set
uv run rsspot pricing list --region us-central-dfw-1 --class gp,ch --min-cpu 4 --gen 2 --nodes 5

# explicit machine-readable output stays json/yaml
uv run rsspot -o json pricing list --class gp --max-cpu 8 --nodes 10

# generate 3 recommendation strategies (max_performance, max_value, balanced)
uv run rsspot pricing build --nodes 5 --risk med --classes gp,ch,mh

# spread balanced strategy across multiple pools and constrain cluster hourly spend
uv run rsspot pricing build --nodes 5 --balanced --risk low --max-hour 0.25

# constrain by total cluster monthly spend
uv run rsspot pricing build --nodes 5 --min-month 50 --max-month 180

Quickstart (SDK)

Unified client

from rsspot import SpotClient

client = SpotClient(profile="default")
orgs = client.organizations.list()
print([org.name for org in orgs.organizations])
client.close()
import asyncio
from rsspot import SpotClient

async def main() -> None:
    client = SpotClient(profile="default")
    orgs = await client.aorganizations.list()
    print([org.name for org in orgs.organizations])
    await client.aclose()

asyncio.run(main())

Async-only client

import asyncio
from rsspot import AsyncSpotClient

async def main() -> None:
    async with AsyncSpotClient(profile="default") as client:
        regions = await client.regions.list()
        print([r.name for r in regions])

asyncio.run(main())

Configuration

Resolution precedence:

  1. Runtime config dict/model passed to client constructor
  2. Explicit config_path=...
  3. Env path (RSSPOT_CONFIG, RSSPOT_CONFIG_FILE, SPOT_CONFIG_FILE)
  4. Default search in ~/.config/rsspot/config.{yml,yaml,toml,json}
  5. Legacy fallback ~/.spot_config (auto-migrated)

Expected schema is identical across config.yaml, config.toml, and config.json.

config.yaml

version: "2"
default_profile: prod
active_profile: prod
state_path: /Users/you/.config/rsspot/state.db

profiles:
  prod:
    org: sparkai
    region: us-central-dfw-1
    refresh_token: replace-me
    base_url: https://spot.rackspace.com
    oauth_url: https://spot.rackspace.com
    request_timeout_seconds: 30
    verify_ssl: true

preferences:
  default_profile: prod
  default_org: sparkai
  default_region: us-central-dfw-1

retry:
  max_attempts: 4
  base_delay: 0.2
  max_delay: 2.5
  jitter: 0.2

cache:
  enabled: true
  default_ttl: 5
  max_entries: 1000
  backend: sqlite

config.toml

version = "2"
default_profile = "prod"
active_profile = "prod"
state_path = "/Users/you/.config/rsspot/state.db"

[profiles.prod]
org = "sparkai"
region = "us-central-dfw-1"
refresh_token = "replace-me"
base_url = "https://spot.rackspace.com"
oauth_url = "https://spot.rackspace.com"
request_timeout_seconds = 30
verify_ssl = true

[preferences]
default_profile = "prod"
default_org = "sparkai"
default_region = "us-central-dfw-1"

[retry]
max_attempts = 4
base_delay = 0.2
max_delay = 2.5
jitter = 0.2

[cache]
enabled = true
default_ttl = 5
max_entries = 1000
backend = "sqlite"

config.json

{
  "version": "2",
  "default_profile": "prod",
  "active_profile": "prod",
  "state_path": "/Users/you/.config/rsspot/state.db",
  "profiles": {
    "prod": {
      "org": "sparkai",
      "region": "us-central-dfw-1",
      "refresh_token": "replace-me",
      "base_url": "https://spot.rackspace.com",
      "oauth_url": "https://spot.rackspace.com",
      "request_timeout_seconds": 30,
      "verify_ssl": true
    }
  },
  "preferences": {
    "default_profile": "prod",
    "default_org": "sparkai",
    "default_region": "us-central-dfw-1"
  },
  "retry": {
    "max_attempts": 4,
    "base_delay": 0.2,
    "max_delay": 2.5,
    "jitter": 0.2
  },
  "cache": {
    "enabled": true,
    "default_ttl": 5,
    "max_entries": 1000,
    "backend": "sqlite"
  }
}

Notes:

  • profiles.<name> is where profile-specific credentials/selectors live.
  • Top-level retry/cache are defaults; profile-level retry/cache can override them.
  • Legacy aliases like refreshToken, clientId, and baseUrl are still accepted.

VM Registration Workflow Primitives

rsspot includes registration state helpers for composing with external orchestrators (including separate omni-sdk scripts) without taking a direct dependency.

Core entrypoint:

  • rsspot.workflows.RegistrationWorkflow

OpenAPI Tracking

uv run python scripts/sync_openapi.py
uv run python scripts/generate_openapi_index.py

Development

uv run ruff check src tests
uv run mypy src
uv run pytest -q

Changelog

See CHANGELOG.md.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

rsspot-0.3.0.tar.gz (92.7 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

rsspot-0.3.0-py3-none-any.whl (51.7 kB view details)

Uploaded Python 3

File details

Details for the file rsspot-0.3.0.tar.gz.

File metadata

  • Download URL: rsspot-0.3.0.tar.gz
  • Upload date:
  • Size: 92.7 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: uv/0.10.6 {"installer":{"name":"uv","version":"0.10.6","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for rsspot-0.3.0.tar.gz
Algorithm Hash digest
SHA256 e7048a2f867461eb3ee4ff3f898e684837bded8333e0457822acb7edeb758fe3
MD5 473b3bdda87e44f9207ea58375972869
BLAKE2b-256 24a9c85d029e13c393f7b63e9eeb4d110b09457f3e22945a340923ceb8e2417f

See more details on using hashes here.

File details

Details for the file rsspot-0.3.0-py3-none-any.whl.

File metadata

  • Download URL: rsspot-0.3.0-py3-none-any.whl
  • Upload date:
  • Size: 51.7 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: uv/0.10.6 {"installer":{"name":"uv","version":"0.10.6","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for rsspot-0.3.0-py3-none-any.whl
Algorithm Hash digest
SHA256 895479fc68c9b6222a297b9c791a609f6749c4cf2b8aa9b98201d1c4173189a9
MD5 b7ba01b7d99c6b7bab9ba04ad1b5197d
BLAKE2b-256 6c322577a1bc25b27fadb611549eea3e26b7618eb77931666144544dfe92c3bf

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page