Skip to content

Commit 5bd137e

Browse files
authored
Merge pull request #93 from verda-cloud/feature/add-instance-type-images-query
Feature/add instance type images query
2 parents 3dc3f19 + 1691706 commit 5bd137e

File tree

3 files changed

+71
-78
lines changed

3 files changed

+71
-78
lines changed

CHANGELOG.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
77

88
## [Unreleased]
99

10+
### Added
11+
12+
- Support for querying OS images by instance type via `verda.images.get(instance_type=...)`
13+
14+
### Changed
15+
16+
- Refactored `Image` model to use `@dataclass` and `@dataclass_json` for consistency with `Instance` and `Volume`
17+
1018
## [1.24.0] - 2026-03-30
1119

1220
### Added

tests/unit_tests/images/test_images.py

Lines changed: 36 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,24 @@
1+
import json
2+
13
import responses # https://github.com/getsentry/responses
4+
from responses import matchers
25

36
from verda.images import Image, ImagesService
47

8+
IMAGE_RESPONSE = {
9+
'id': '0888da25-bb0d-41cc-a191-dccae45d96fd',
10+
'name': 'Ubuntu 20.04 + CUDA 11.0',
11+
'details': ['Ubuntu 20.04', 'CUDA 11.0'],
12+
'image_type': 'ubuntu-20.04-cuda-11.0',
13+
}
14+
515

616
def test_images(http_client):
7-
# arrange - add response mock
17+
# arrange
818
responses.add(
919
responses.GET,
1020
http_client._base_url + '/images',
11-
json=[
12-
{
13-
'id': '0888da25-bb0d-41cc-a191-dccae45d96fd',
14-
'name': 'Ubuntu 20.04 + CUDA 11.0',
15-
'details': ['Ubuntu 20.04', 'CUDA 11.0'],
16-
'image_type': 'ubuntu-20.04-cuda-11.0',
17-
}
18-
],
21+
json=[IMAGE_RESPONSE],
1922
status=200,
2023
)
2124

@@ -34,4 +37,27 @@ def test_images(http_client):
3437
assert isinstance(images[0].details, list)
3538
assert images[0].details[0] == 'Ubuntu 20.04'
3639
assert images[0].details[1] == 'CUDA 11.0'
37-
assert isinstance(images[0].__str__(), str)
40+
assert json.loads(str(images[0])) == IMAGE_RESPONSE
41+
42+
43+
def test_images_filter_by_instance_type(http_client):
44+
# arrange
45+
responses.add(
46+
responses.GET,
47+
http_client._base_url + '/images',
48+
match=[matchers.query_param_matcher({'instance_type': '1A100.22V'})],
49+
json=[IMAGE_RESPONSE],
50+
status=200,
51+
)
52+
53+
image_service = ImagesService(http_client)
54+
55+
# act
56+
images = image_service.get(instance_type='1A100.22V')
57+
58+
# assert
59+
assert isinstance(images, list)
60+
assert len(images) == 1
61+
assert isinstance(images[0], Image)
62+
assert images[0].id == '0888da25-bb0d-41cc-a191-dccae45d96fd'
63+
assert images[0].image_type == 'ubuntu-20.04-cuda-11.0'

verda/images/_images.py

Lines changed: 27 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -1,71 +1,29 @@
1-
from verda.helpers import stringify_class_object_properties
1+
from dataclasses import dataclass
2+
3+
from dataclasses_json import Undefined, dataclass_json
24

35
IMAGES_ENDPOINT = '/images'
46

57

8+
@dataclass_json(undefined=Undefined.EXCLUDE)
9+
@dataclass
610
class Image:
7-
"""An image model class."""
8-
9-
def __init__(self, id: str, name: str, image_type: str, details: list[str]) -> None:
10-
"""Initialize an image object.
11-
12-
:param id: image id
13-
:type id: str
14-
:param name: image name
15-
:type name: str
16-
:param image_type: image type, e.g. 'ubuntu-20.04-cuda-11.0'
17-
:type image_type: str
18-
:param details: image details
19-
:type details: list[str]
20-
"""
21-
self._id = id
22-
self._name = name
23-
self._image_type = image_type
24-
self._details = details
25-
26-
@property
27-
def id(self) -> str:
28-
"""Get the image id.
11+
"""Represents an OS image available for instances.
2912
30-
:return: image id
31-
:rtype: str
32-
"""
33-
return self._id
34-
35-
@property
36-
def name(self) -> str:
37-
"""Get the image name.
38-
39-
:return: image name
40-
:rtype: str
41-
"""
42-
return self._name
13+
Attributes:
14+
id: Unique identifier for the image.
15+
name: Human-readable name of the image.
16+
image_type: Image type identifier, e.g. 'ubuntu-20.04-cuda-11.0'.
17+
details: List of additional image details.
18+
"""
4319

44-
@property
45-
def image_type(self) -> str:
46-
"""Get the image type.
47-
48-
:return: image type
49-
:rtype: str
50-
"""
51-
return self._image_type
52-
53-
@property
54-
def details(self) -> list[str]:
55-
"""Get the image details.
56-
57-
:return: image details
58-
:rtype: list[str]
59-
"""
60-
return self._details
20+
id: str
21+
name: str
22+
image_type: str
23+
details: list[str]
6124

6225
def __str__(self) -> str:
63-
"""Returns a string of the json representation of the image.
64-
65-
:return: json representation of the image
66-
:rtype: str
67-
"""
68-
return stringify_class_object_properties(self)
26+
return self.to_json(indent=2)
6927

7028

7129
class ImagesService:
@@ -74,15 +32,16 @@ class ImagesService:
7432
def __init__(self, http_client) -> None:
7533
self._http_client = http_client
7634

77-
def get(self) -> list[Image]:
35+
def get(self, instance_type: str | None = None) -> list[Image]:
7836
"""Get the available instance images.
7937
80-
:return: list of images objects
81-
:rtype: list[Image]
38+
Args:
39+
instance_type: Filter OS images by instance type, e.g. '1A100.22V'.
40+
Default is all instance images.
41+
42+
Returns:
43+
List of Image objects.
8244
"""
83-
images = self._http_client.get(IMAGES_ENDPOINT).json()
84-
image_objects = [
85-
Image(image['id'], image['name'], image['image_type'], image['details'])
86-
for image in images
87-
]
88-
return image_objects
45+
params = {'instance_type': instance_type} if instance_type else None
46+
images = self._http_client.get(IMAGES_ENDPOINT, params=params).json()
47+
return [Image.from_dict(image) for image in images]

0 commit comments

Comments
 (0)