Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
33 changes: 18 additions & 15 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,38 +13,41 @@ jobs:
contents: read
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- name: Setup Python
uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0
- name: Setup uv
uses: astral-sh/setup-uv@c18668ad3cf93ea998bef934396af7bb5c839dc7 # v10.2.0
with:
python-version: 3.13
- uses: pre-commit/action@2c7b3805fd2a0fd8c1884dcaebf91fc102a13ecd # v3.0.1
- run: uv sync --locked
- run: uv run --locked pre-commit run --all-files
test:
runs-on: ubuntu-latest
strategy:
matrix:
tox_env:
- py310
- py311
- py312
- py313
- pypy311
python-version:
- "3.10"
- "3.11"
- "3.12"
- "3.13"
- "pypy3.11"
permissions:
contents: read
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- name: Fedora Tox with ${{ matrix.tox_env }}
uses: fedora-python/tox-github-action@807f27871410c7391018dc9a245c8cffdced15e9 # v41.0
- name: Setup uv with Python ${{ matrix.python-version }}
uses: astral-sh/setup-uv@c18668ad3cf93ea998bef934396af7bb5c839dc7 # v10.2.0
with:
tox_env: ${{ matrix.tox_env }}
python-version: ${{ matrix.python-version }}
- run: uv sync --locked
- run: uv run --locked pytest
build:
runs-on: ubuntu-latest
permissions:
contents: read
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0
- run: python -m pip install --upgrade build
- run: python -m build
- name: Setup uv
uses: astral-sh/setup-uv@c18668ad3cf93ea998bef934396af7bb5c839dc7 # v10.2.0
- run: uv build
- uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
with:
name: dist
Expand Down
5 changes: 2 additions & 3 deletions .github/workflows/publish-testpypi-manual.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,9 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683
- uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065
- uses: astral-sh/setup-uv@c18668ad3cf93ea998bef934396af7bb5c839dc7 # v10.2.0
# build artifacts (wheel + sdist)
- run: python -m pip install --upgrade build
- run: python -m build
- run: uv build
# publish to **TestPyPI** via Trusted Publishers (OIDC)
- uses: pypa/gh-action-pypi-publish@release/v1
with:
Expand Down
3 changes: 0 additions & 3 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,6 @@ docs/html/
.coverage
htmlcov

# pyenv
.python-version

# local
.test-snippets
sandbox
Expand Down
11 changes: 4 additions & 7 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -1,14 +1,11 @@
minimum_pre_commit_version: 2.15.0
minimum_pre_commit_version: 4.0.0
ci:
autofix_prs: false
repos:
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.0.278
rev: v0.16.8
hooks:
- id: ruff
- id: ruff-check
args:
- --fix
- repo: https://github.com/psf/black
rev: 23.7.0
hooks:
- id: black
- id: ruff-format
1 change: 1 addition & 0 deletions .python-version
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
3.13
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Changed

- Python versions supported: 3.10, 3.11, 3.12, 3.13, PyPy3. Dropped support for 3.9.
- Migrated packaging, dependency management, tests, formatting, and builds to uv.

## [2.9.0] - 2025-09-25

Expand Down
7 changes: 0 additions & 7 deletions MANIFEST.in

This file was deleted.

120 changes: 62 additions & 58 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,10 @@ testing / developing the client is recommended.
pip install upcloud-api
```

Alternatively, if you want the newest (possibly not yet released) stuff, clone the project and run:
Alternatively, install the newest, possibly unreleased, revision directly from GitHub:

``` bash
python setup.py install
```bash
pip install "upcloud-api @ git+https://github.com/UpCloudLtd/upcloud-python-api.git"
```

### Supported Python versions
Expand Down Expand Up @@ -57,7 +57,6 @@ c.get_account()
### Defining and creating servers

```python

import upcloud_api
from upcloud_api import CloudManager, Server, Storage, login_user_block

Expand All @@ -68,23 +67,23 @@ manager.authenticate()
login_user = login_user_block(
username='theuser',
ssh_keys=['ssh-rsa AAAAB3NzaC1yc2EAA[...]ptshi44x user@some.host'],
create_password=False
create_password=False,
)

cluster = {
'web1': Server(
plan='2xCPU-4GB',
hostname='web1.example.com',
zone='uk-lon1', # All available zones with ids can be retrieved by using manager.get_zones()
zone='uk-lon1', # All available zones with ids can be retrieved by using manager.get_zones()
storage_devices=[
# OS: template storage UUID, all available os templates can be retrieved by calling manager.get_templates()
# Note: the storage os template uuid:s will change when OS is updated. So check that the UUID is correct
# default tier: maxIOPS, the 100k IOPS storage backend
Storage(os='01000000-0000-4000-8000-000030240200', size=10),
# secondary storage, hdd for reduced speed & cost
Storage(size=100, tier='hdd')
Storage(size=100, tier='hdd'),
],
login_user=login_user # user and ssh-keys
login_user=login_user, # user and ssh-keys
),
'web2': Server(
plan='2xCPU-4GB',
Expand All @@ -94,34 +93,31 @@ cluster = {
Storage(os='01000000-0000-4000-8000-000030240200', size=10),
Storage(size=100, tier='hdd'),
],
login_user=login_user
login_user=login_user,
),
'db': Server(
# use custom resources, instead of a plan
core_number=12, # CPU cores
memory_amount=49152, # RAM in MB
core_number=12, # CPU cores
memory_amount=49152, # RAM in MB
hostname='db.example.com',
zone='uk-lon1',
storage_devices=[
Storage(os='01000000-0000-4000-8000-000030240200', size=10),
Storage(size=100),
],
login_user=login_user
login_user=login_user,
),
'lb': Server(
plan='2xCPU-4GB',
hostname='balancer.example.com',
zone='uk-lon1',
storage_devices=[
Storage(os='01000000-0000-4000-8000-000030240200', size=10)
],
login_user=login_user
)
storage_devices=[Storage(os='01000000-0000-4000-8000-000030240200', size=10)],
login_user=login_user,
),
}

for server in cluster:
manager.create_server(cluster[server]) # creates all server objects defined in cluster

manager.create_server(cluster[server]) # creates all server objects defined in cluster
```

Servers can be defined as dicts without using Server or Storage classes.
Expand All @@ -132,14 +128,13 @@ This feature is mainly for easier usage of the module from Ansible, but may prov

```python
for server in cluster:
server.shutdown()
# OR:
server.start()
# OR:
server.destroy()
for storage in server.storage_devices:
storage.destroy()

server.shutdown()
# OR:
server.start()
# OR:
server.destroy()
for storage in server.storage_devices:
storage.destroy()
```

As the success of server.start() or server.destroy() and storage.destroy()
Expand All @@ -162,24 +157,20 @@ Following example would delete all storages attached to a server, but would keep
of each storage if backups exist.

```python

from upcloud_api.storage import BackupDeletionPolicy

manager.delete_server(uuid, delete_storages=True, backups=BackupDeletionPolicy.KEEP_LATEST)

```

### Upgrade a Server

```python

server = cluster['web1']
server.shutdown()
server.core_number = 4
server.memory_amount = 4096
server.save()
server.start()

```

### Clone a new server from existing storage
Expand All @@ -195,9 +186,9 @@ clone = Server(
storage_devices=[
Storage(
uuid='012bea57-0f70-4154-84d0-b3d25f4a018b',
size=50 # size must be defined and it has to be at least same size than storage being cloned
size=50, # size must be defined and it has to be at least same size than storage being cloned
),
]
],
)

manager.create_server(clone)
Expand All @@ -206,62 +197,75 @@ manager.create_server(clone)
### Easy access to servers and their information

```python

# returns a public IPv4 (preferred) IPv6 (no public IPv4 was attached) address
server.get_public_ip()

# returns a JSON serializable dict with the server's information (storages and ip-addresses included)
server.to_dict()

```

### Get resources

```python
servers = manager.get_servers()
server1 = manager.get_server(uuid) # e.g servers[0].uuid
storages = manager.get_storages()
storage1 = manager.get_storage(uuid) # e.g server1.storage_devices[0].uuid
ip_addrs = manager.get_ips()
ip_addr = manager.get_ip(address) # e.g server1.ip_addresses[0].address
```

## Development

servers = manager.get_servers()
server1 = manager.get_server(uuid) # e.g servers[0].uuid
storages = manager.get_storages()
storage1 = manager.get_storage(uuid) # e.g server1.storage_devices[0].uuid
ip_addrs = manager.get_ips()
ip_addr = manager.get_ip(address) # e.g server1.ip_addresses[0].address
Install [uv](https://docs.astral.sh/uv/getting-started/installation/), then create
the locked development environment:

```bash
uv sync --locked
```

## Testing
Run the unit tests:

Set up environment and install dependencies:
```bash
uv run pytest
```

``` bash
# run at project root, python3 and virtualenv must be installed
virtualenv venv
source venv/bin/activate
CI runs the suite with CPython 3.10 through 3.13 and PyPy 3.11. To reproduce
the full interpreter matrix locally:

```bash
for python in 3.10 3.11 3.12 3.13 pypy3.11; do
uv run --locked --python "$python" pytest
done
```

Install the package in editable mode.
The integration tests under `test/test_integration` can permanently remove all
resources associated with an account. Run them only with a throwaway
development account:

```bash
# run at project root
pip install -e .
uv run pytest --integration-tests -x
```

Tests are located under `test/`. Run with:
Run all lint and formatting hooks:

```bash
py.test test/
uv run pre-commit run --all-files
```

To test against all supported python versions, run:
Build the wheel and source distribution:

```bash
tox
uv build
```

For a release, set the new version in both places:

- `pyproject.toml`: the `version = "2.9.0"` field under `[project]`.
- `upcloud_api/__init__.py`: the `__version__ = '2.9.0'` assignment.

The project also supplies a small test suite to test against the live API in `test/test_integration`.
This suite is NOT run with `py.test` dy default as it will permanently remove all resources related to an account.
It should only be run with a throwaway dev-only account when preparing for a new release. It is not shipped with
PyPI releases. To run the integration tests, append `--integration-tests` flag to the `py.test` command.
Use the same new version in both files, then run `uv lock` to update `uv.lock`.
Do not edit the lockfile by hand.

## Bugs, Issues, Problems, Ideas

Expand Down
Loading
Loading