Skip to content

Release v5.30.0 #545

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
May 13, 2025
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
6 changes: 4 additions & 2 deletions .github/workflows/publish-pypi.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,11 @@ on:
types: [ published ]
jobs:
pypi-release:
permissions:
# IMPORTANT: this permission is mandatory for trusted publishing
id-token: write
runs-on: ubuntu-latest
environment: pypi-release
steps:
- name: Checkout
uses: actions/checkout@v4
Expand All @@ -25,5 +29,3 @@ jobs:

- name: Publish the release artifacts to PyPI
uses: pypa/gh-action-pypi-publish@76f52bc884231f62b9a034ebfe128415bbaabdfc # pin@release/v1.12.4
with:
password: ${{ secrets.PYPI_API_TOKEN }}
16 changes: 16 additions & 0 deletions linode_api4/groups/object_storage.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
ObjectStorageCluster,
ObjectStorageKeyPermission,
ObjectStorageKeys,
ObjectStorageQuota,
)
from linode_api4.util import drop_null_keys

Expand Down Expand Up @@ -517,3 +518,18 @@ def object_url_create(
)

return MappedObject(**result)

def quotas(self, *filters):
"""
Lists the active ObjectStorage-related quotas applied to your account.

API Documentation: https://techdocs.akamai.com/linode-api/reference/get-object-storage-quotas

:param filters: Any number of filters to apply to this query.
See :doc:`Filtering Collections</linode_api4/objects/filtering>`
for more details on filtering.

:returns: A list of Object Storage Quotas that matched the query.
:rtype: PaginatedList of ObjectStorageQuota
"""
return self.client._get_and_filter(ObjectStorageQuota, *filters)
49 changes: 49 additions & 0 deletions linode_api4/objects/object_storage.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,16 @@ class ObjectStorageEndpoint(JSONObject):
s3_endpoint: Optional[str] = None


@dataclass
class ObjectStorageQuotaUsage(JSONObject):
"""
ObjectStorageQuotaUsage contains the fields of an object storage quota usage information.
"""

quota_limit: int = 0
usage: int = 0


class ObjectStorageType(Base):
"""
An ObjectStorageType represents the structure of a valid Object Storage type.
Expand Down Expand Up @@ -566,3 +576,42 @@ class ObjectStorageKeys(Base):
"limited": Property(),
"regions": Property(unordered=True),
}


class ObjectStorageQuota(Base):
"""
An Object Storage related quota information on your account.
Object Storage Quota related features are under v4beta and may not currently be available to all users.

API documentation: https://techdocs.akamai.com/linode-api/reference/get-object-storage-quota
"""

api_endpoint = "/object-storage/quotas/{quota_id}"
id_attribute = "quota_id"

properties = {
"quota_id": Property(identifier=True),
"quota_name": Property(),
"endpoint_type": Property(),
"s3_endpoint": Property(),
"description": Property(),
"quota_limit": Property(),
"resource_metric": Property(),
}

def usage(self):
"""
Gets usage data for a specific ObjectStorage Quota resource you can have on your account and the current usage for that resource.

API documentation: https://techdocs.akamai.com/linode-api/reference/get-object-storage-quota-usage

:returns: The Object Storage Quota usage.
:rtype: ObjectStorageQuotaUsage
"""

result = self._client.get(
f"{type(self).api_endpoint}/usage",
model=self,
)

return ObjectStorageQuotaUsage.from_json(result)
25 changes: 25 additions & 0 deletions test/fixtures/object-storage_quotas.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
{
"data": [
{
"quota_id": "obj-objects-us-ord-1",
"quota_name": "Object Storage Maximum Objects",
"description": "Maximum number of Objects this customer is allowed to have on this endpoint.",
"endpoint_type": "E1",
"s3_endpoint": "us-iad-1.linodeobjects.com",
"quota_limit": 50,
"resource_metric": "object"
},
{
"quota_id": "obj-bucket-us-ord-1",
"quota_name": "Object Storage Maximum Buckets",
"description": "Maximum number of buckets this customer is allowed to have on this endpoint.",
"endpoint_type": "E1",
"s3_endpoint": "us-iad-1.linodeobjects.com",
"quota_limit": 50,
"resource_metric": "bucket"
}
],
"page": 1,
"pages": 1,
"results": 2
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"quota_id": "obj-objects-us-ord-1",
"quota_name": "Object Storage Maximum Objects",
"description": "Maximum number of Objects this customer is allowed to have on this endpoint.",
"endpoint_type": "E1",
"s3_endpoint": "us-iad-1.linodeobjects.com",
"quota_limit": 50,
"resource_metric": "object"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"quota_limit": 100,
"usage": 10
}
5 changes: 4 additions & 1 deletion test/integration/models/lke/test_lke.py
Original file line number Diff line number Diff line change
Expand Up @@ -208,7 +208,10 @@ def _to_comparable(p: LKENodePool) -> Dict[str, Any]:

assert _to_comparable(cluster.pools[0]) == _to_comparable(pool)

assert pool.disk_encryption == InstanceDiskEncryptionType.disabled
assert pool.disk_encryption in (
InstanceDiskEncryptionType.enabled,
InstanceDiskEncryptionType.disabled,
)


def test_cluster_dashboard_url_view(lke_cluster):
Expand Down
45 changes: 45 additions & 0 deletions test/integration/models/object_storage/test_obj_quotas.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import pytest

from linode_api4.objects.object_storage import (
ObjectStorageQuota,
ObjectStorageQuotaUsage,
)


def test_list_and_get_obj_storage_quotas(test_linode_client):
quotas = test_linode_client.object_storage.quotas()

if len(quotas) < 1:
pytest.skip("No available quota for testing. Skipping now...")

found_quota = quotas[0]

get_quota = test_linode_client.load(
ObjectStorageQuota, found_quota.quota_id
)

assert found_quota.quota_id == get_quota.quota_id
assert found_quota.quota_name == get_quota.quota_name
assert found_quota.endpoint_type == get_quota.endpoint_type
assert found_quota.s3_endpoint == get_quota.s3_endpoint
assert found_quota.description == get_quota.description
assert found_quota.quota_limit == get_quota.quota_limit
assert found_quota.resource_metric == get_quota.resource_metric


def test_get_obj_storage_quota_usage(test_linode_client):
quotas = test_linode_client.object_storage.quotas()

if len(quotas) < 1:
pytest.skip("No available quota for testing. Skipping now...")

quota_id = quotas[0].quota_id
quota = test_linode_client.load(ObjectStorageQuota, quota_id)

quota_usage = quota.usage()

assert isinstance(quota_usage, ObjectStorageQuotaUsage)
assert quota_usage.quota_limit >= 0

if quota_usage.usage is not None:
assert quota_usage.usage >= 0
51 changes: 51 additions & 0 deletions test/unit/objects/object_storage_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
ObjectStorageACL,
ObjectStorageBucket,
ObjectStorageCluster,
ObjectStorageQuota,
)


Expand Down Expand Up @@ -284,3 +285,53 @@ def test_object_acl_config_update(self):
"name": "example",
},
)

def test_quota_get_and_list(self):
"""
Test that you can get and list an Object storage quota and usage information.
"""
quota = ObjectStorageQuota(
self.client,
"obj-objects-us-ord-1",
)

self.assertIsNotNone(quota)
self.assertEqual(quota.quota_id, "obj-objects-us-ord-1")
self.assertEqual(quota.quota_name, "Object Storage Maximum Objects")
self.assertEqual(
quota.description,
"Maximum number of Objects this customer is allowed to have on this endpoint.",
)
self.assertEqual(quota.endpoint_type, "E1")
self.assertEqual(quota.s3_endpoint, "us-iad-1.linodeobjects.com")
self.assertEqual(quota.quota_limit, 50)
self.assertEqual(quota.resource_metric, "object")

quota_usage_url = "/object-storage/quotas/obj-objects-us-ord-1/usage"
with self.mock_get(quota_usage_url) as m:
usage = quota.usage()
self.assertIsNotNone(usage)
self.assertEqual(m.call_url, quota_usage_url)
self.assertEqual(usage.quota_limit, 100)
self.assertEqual(usage.usage, 10)

quota_list_url = "/object-storage/quotas"
with self.mock_get(quota_list_url) as m:
quotas = self.client.object_storage.quotas()
self.assertIsNotNone(quotas)
self.assertEqual(m.call_url, quota_list_url)
self.assertEqual(len(quotas), 2)
self.assertEqual(quotas[0].quota_id, "obj-objects-us-ord-1")
self.assertEqual(
quotas[0].quota_name, "Object Storage Maximum Objects"
)
self.assertEqual(
quotas[0].description,
"Maximum number of Objects this customer is allowed to have on this endpoint.",
)
self.assertEqual(quotas[0].endpoint_type, "E1")
self.assertEqual(
quotas[0].s3_endpoint, "us-iad-1.linodeobjects.com"
)
self.assertEqual(quotas[0].quota_limit, 50)
self.assertEqual(quotas[0].resource_metric, "object")
Loading