diff --git a/.github/workflows/clean-release-notes.yml b/.github/workflows/clean-release-notes.yml index 9006a5daf..9b7305275 100644 --- a/.github/workflows/clean-release-notes.yml +++ b/.github/workflows/clean-release-notes.yml @@ -12,7 +12,7 @@ jobs: steps: - name: Remove ticket prefixes from release notes - uses: actions/github-script@v8 + uses: actions/github-script@v9 with: script: | const release = context.payload.release; diff --git a/.github/workflows/e2e-test-pr.yml b/.github/workflows/e2e-test-pr.yml index f765b0a0d..de95ac92b 100644 --- a/.github/workflows/e2e-test-pr.yml +++ b/.github/workflows/e2e-test-pr.yml @@ -123,7 +123,7 @@ jobs: LINODE_CLI_OBJ_ACCESS_KEY: ${{ secrets.LINODE_CLI_OBJ_ACCESS_KEY }} LINODE_CLI_OBJ_SECRET_KEY: ${{ secrets.LINODE_CLI_OBJ_SECRET_KEY }} - - uses: actions/github-script@v8 + - uses: actions/github-script@v9 id: update-check-run if: ${{ inputs.pull_request_number != '' && fromJson(steps.commit-hash.outputs.data).repository.pullRequest.headRef.target.oid == inputs.sha }} env: diff --git a/linode_api4/groups/lock.py b/linode_api4/groups/lock.py index 42cc58d80..2f19e2c1d 100644 --- a/linode_api4/groups/lock.py +++ b/linode_api4/groups/lock.py @@ -24,7 +24,7 @@ def __call__(self, *filters): locks = client.locks() - API Documentation: TBD + API Documentation: https://techdocs.akamai.com/linode-api/reference/get-resource-locks :param filters: Any number of filters to apply to this query. See :doc:`Filtering Collections` @@ -44,7 +44,7 @@ def create( """ Creates a new Resource Lock for the specified entity. - API Documentation: TBD + API Documentation: https://techdocs.akamai.com/linode-api/reference/post-resource-lock :param entity_type: The type of entity to lock (e.g., "linode"). :type entity_type: str diff --git a/linode_api4/groups/object_storage.py b/linode_api4/groups/object_storage.py index 5ffab3ffc..d36690111 100644 --- a/linode_api4/groups/object_storage.py +++ b/linode_api4/groups/object_storage.py @@ -19,6 +19,7 @@ ObjectStorageACL, ObjectStorageBucket, ObjectStorageCluster, + ObjectStorageGlobalQuota, ObjectStorageKeyPermission, ObjectStorageKeys, ObjectStorageQuota, @@ -533,3 +534,18 @@ def quotas(self, *filters): :rtype: PaginatedList of ObjectStorageQuota """ return self.client._get_and_filter(ObjectStorageQuota, *filters) + + def global_quotas(self, *filters): + """ + Lists the active account-level Object Storage quotas applied to your account. + + API Documentation: TBD + + :param filters: Any number of filters to apply to this query. + See :doc:`Filtering Collections` + for more details on filtering. + + :returns: A list of account-level Object Storage Quotas that matched the query. + :rtype: PaginatedList of ObjectStorageGlobalQuota + """ + return self.client._get_and_filter(ObjectStorageGlobalQuota, *filters) diff --git a/linode_api4/objects/linode.py b/linode_api4/objects/linode.py index b8b0fc9dc..bf2f12717 100644 --- a/linode_api4/objects/linode.py +++ b/linode_api4/objects/linode.py @@ -2055,8 +2055,6 @@ def interface_create( Creates a new interface under this Linode. Linode interfaces are not interchangeable with Config interfaces. - NOTE: Linode interfaces may not currently be available to all users. - API Documentation: https://techdocs.akamai.com/linode-api/reference/post-linode-interface Example: Creating a simple public interface for this Linode:: @@ -2132,8 +2130,6 @@ def interfaces_settings(self) -> LinodeInterfacesSettings: """ The settings for all interfaces under this Linode. - NOTE: Linode interfaces may not currently be available to all users. - :returns: The settings for instance-level interface settings for this Linode. :rtype: LinodeInterfacesSettings """ @@ -2202,8 +2198,6 @@ def upgrade_interfaces( NOTE: If dry_run is True, interfaces in the result will be of type MappedObject rather than LinodeInterface. - NOTE: Linode interfaces may not currently be available to all users. - API Documentation: https://techdocs.akamai.com/linode-api/reference/post-upgrade-linode-interfaces :param config: The configuration profile the legacy interfaces to diff --git a/linode_api4/objects/linode_interfaces.py b/linode_api4/objects/linode_interfaces.py index 0598d1f3c..69cebca23 100644 --- a/linode_api4/objects/linode_interfaces.py +++ b/linode_api4/objects/linode_interfaces.py @@ -11,8 +11,6 @@ class LinodeInterfacesSettingsDefaultRouteOptions(JSONObject): """ The options used to configure the default route settings for a Linode's network interfaces. - - NOTE: Linode interfaces may not currently be available to all users. """ ipv4_interface_id: Optional[int] = None @@ -23,8 +21,6 @@ class LinodeInterfacesSettingsDefaultRouteOptions(JSONObject): class LinodeInterfacesSettingsDefaultRoute(JSONObject): """ The default route settings for a Linode's network interfaces. - - NOTE: Linode interfaces may not currently be available to all users. """ put_class = LinodeInterfacesSettingsDefaultRouteOptions @@ -40,8 +36,6 @@ class LinodeInterfacesSettings(Base): The settings related to a Linode's network interfaces. API Documentation: https://techdocs.akamai.com/linode-api/reference/get-linode-interface-settings - - NOTE: Linode interfaces may not currently be available to all users. """ api_endpoint = "/linode/instances/{id}/interfaces/settings" @@ -60,8 +54,6 @@ class LinodeInterfacesSettings(Base): class LinodeInterfaceDefaultRouteOptions(JSONObject): """ Options accepted when creating or updating a Linode Interface's default route settings. - - NOTE: Linode interfaces may not currently be available to all users. """ ipv4: Optional[bool] = None @@ -72,8 +64,6 @@ class LinodeInterfaceDefaultRouteOptions(JSONObject): class LinodeInterfaceVPCIPv4AddressOptions(JSONObject): """ Options accepted for a single address when creating or updating the IPv4 configuration of a VPC Linode Interface. - - NOTE: Linode interfaces may not currently be available to all users. """ address: Optional[str] = None @@ -85,8 +75,6 @@ class LinodeInterfaceVPCIPv4AddressOptions(JSONObject): class LinodeInterfaceVPCIPv4RangeOptions(JSONObject): """ Options accepted for a single range when creating or updating the IPv4 configuration of a VPC Linode Interface. - - NOTE: Linode interfaces may not currently be available to all users. """ range: str = "" @@ -96,8 +84,6 @@ class LinodeInterfaceVPCIPv4RangeOptions(JSONObject): class LinodeInterfaceVPCIPv4Options(JSONObject): """ Options accepted when creating or updating the IPv4 configuration of a VPC Linode Interface. - - NOTE: Linode interfaces may not currently be available to all users. """ addresses: Optional[List[LinodeInterfaceVPCIPv4AddressOptions]] = None @@ -108,8 +94,6 @@ class LinodeInterfaceVPCIPv4Options(JSONObject): class LinodeInterfaceVPCIPv6SLAACOptions(JSONObject): """ Options accepted for a single SLAAC when creating or updating the IPv6 configuration of a VPC Linode Interface. - - NOTE: Linode interfaces may not currently be available to all users. """ range: Optional[str] = None @@ -119,8 +103,6 @@ class LinodeInterfaceVPCIPv6SLAACOptions(JSONObject): class LinodeInterfaceVPCIPv6RangeOptions(JSONObject): """ Options accepted for a single range when creating or updating the IPv6 configuration of a VPC Linode Interface. - - NOTE: Linode interfaces may not currently be available to all users. """ range: Optional[str] = None @@ -130,8 +112,6 @@ class LinodeInterfaceVPCIPv6RangeOptions(JSONObject): class LinodeInterfaceVPCIPv6Options(JSONObject): """ Options accepted when creating or updating the IPv6 configuration of a VPC Linode Interface. - - NOTE: Linode interfaces may not currently be available to all users. """ is_public: Optional[bool] = None @@ -143,8 +123,6 @@ class LinodeInterfaceVPCIPv6Options(JSONObject): class LinodeInterfaceVPCOptions(JSONObject): """ VPC-exclusive options accepted when creating or updating a Linode Interface. - - NOTE: Linode interfaces may not currently be available to all users. """ subnet_id: int = 0 @@ -156,8 +134,6 @@ class LinodeInterfaceVPCOptions(JSONObject): class LinodeInterfacePublicIPv4AddressOptions(JSONObject): """ Options accepted for a single address when creating or updating the IPv4 configuration of a public Linode Interface. - - NOTE: Linode interfaces may not currently be available to all users. """ address: str = "" @@ -168,8 +144,6 @@ class LinodeInterfacePublicIPv4AddressOptions(JSONObject): class LinodeInterfacePublicIPv4Options(JSONObject): """ Options accepted when creating or updating the IPv4 configuration of a public Linode Interface. - - NOTE: Linode interfaces may not currently be available to all users. """ addresses: Optional[List[LinodeInterfacePublicIPv4AddressOptions]] = None @@ -179,8 +153,6 @@ class LinodeInterfacePublicIPv4Options(JSONObject): class LinodeInterfacePublicIPv6RangeOptions(JSONObject): """ Options accepted for a single range when creating or updating the IPv6 configuration of a public Linode Interface. - - NOTE: Linode interfaces may not currently be available to all users. """ range: str = "" @@ -190,8 +162,6 @@ class LinodeInterfacePublicIPv6RangeOptions(JSONObject): class LinodeInterfacePublicIPv6Options(JSONObject): """ Options accepted when creating or updating the IPv6 configuration of a public Linode Interface. - - NOTE: Linode interfaces may not currently be available to all users. """ ranges: Optional[List[LinodeInterfacePublicIPv6RangeOptions]] = None @@ -201,8 +171,6 @@ class LinodeInterfacePublicIPv6Options(JSONObject): class LinodeInterfacePublicOptions(JSONObject): """ Public-exclusive options accepted when creating or updating a Linode Interface. - - NOTE: Linode interfaces may not currently be available to all users. """ ipv4: Optional[LinodeInterfacePublicIPv4Options] = None @@ -213,8 +181,6 @@ class LinodeInterfacePublicOptions(JSONObject): class LinodeInterfaceVLANOptions(JSONObject): """ VLAN-exclusive options accepted when creating or updating a Linode Interface. - - NOTE: Linode interfaces may not currently be available to all users. """ vlan_label: str = "" @@ -225,8 +191,6 @@ class LinodeInterfaceVLANOptions(JSONObject): class LinodeInterfaceOptions(JSONObject): """ Options accepted when creating or updating a Linode Interface. - - NOTE: Linode interfaces may not currently be available to all users. """ always_include = { @@ -249,8 +213,6 @@ class LinodeInterfaceOptions(JSONObject): class LinodeInterfaceDefaultRoute(JSONObject): """ The default route configuration of a Linode Interface. - - NOTE: Linode interfaces may not currently be available to all users. """ put_class = LinodeInterfaceDefaultRouteOptions @@ -263,8 +225,6 @@ class LinodeInterfaceDefaultRoute(JSONObject): class LinodeInterfaceVPCIPv4Address(JSONObject): """ A single address under the IPv4 configuration of a VPC Linode Interface. - - NOTE: Linode interfaces may not currently be available to all users. """ put_class = LinodeInterfaceVPCIPv4AddressOptions @@ -278,8 +238,6 @@ class LinodeInterfaceVPCIPv4Address(JSONObject): class LinodeInterfaceVPCIPv4Range(JSONObject): """ A single range under the IPv4 configuration of a VPC Linode Interface. - - NOTE: Linode interfaces may not currently be available to all users. """ put_class = LinodeInterfaceVPCIPv4RangeOptions @@ -291,8 +249,6 @@ class LinodeInterfaceVPCIPv4Range(JSONObject): class LinodeInterfaceVPCIPv4(JSONObject): """ A single address under the IPv4 configuration of a VPC Linode Interface. - - NOTE: Linode interfaces may not currently be available to all users. """ put_class = LinodeInterfaceVPCIPv4Options @@ -305,8 +261,6 @@ class LinodeInterfaceVPCIPv4(JSONObject): class LinodeInterfaceVPCIPv6SLAAC(JSONObject): """ A single SLAAC entry under the IPv6 configuration of a VPC Linode Interface. - - NOTE: Linode interfaces may not currently be available to all users. """ range: str = "" @@ -317,8 +271,6 @@ class LinodeInterfaceVPCIPv6SLAAC(JSONObject): class LinodeInterfaceVPCIPv6Range(JSONObject): """ A single range under the IPv6 configuration of a VPC Linode Interface. - - NOTE: Linode interfaces may not currently be available to all users. """ range: str = "" @@ -328,8 +280,6 @@ class LinodeInterfaceVPCIPv6Range(JSONObject): class LinodeInterfaceVPCIPv6(JSONObject): """ A single address under the IPv6 configuration of a VPC Linode Interface. - - NOTE: Linode interfaces may not currently be available to all users. """ put_class = LinodeInterfaceVPCIPv6Options @@ -343,8 +293,6 @@ class LinodeInterfaceVPCIPv6(JSONObject): class LinodeInterfaceVPC(JSONObject): """ VPC-specific configuration field for a Linode Interface. - - NOTE: Linode interfaces may not currently be available to all users. """ put_class = LinodeInterfaceVPCOptions @@ -360,8 +308,6 @@ class LinodeInterfaceVPC(JSONObject): class LinodeInterfacePublicIPv4Address(JSONObject): """ A single address under the IPv4 configuration of a public Linode Interface. - - NOTE: Linode interfaces may not currently be available to all users. """ put_class = LinodeInterfacePublicIPv4AddressOptions @@ -374,8 +320,6 @@ class LinodeInterfacePublicIPv4Address(JSONObject): class LinodeInterfacePublicIPv4Shared(JSONObject): """ A single shared address under the IPv4 configuration of a public Linode Interface. - - NOTE: Linode interfaces may not currently be available to all users. """ address: str = "" @@ -386,8 +330,6 @@ class LinodeInterfacePublicIPv4Shared(JSONObject): class LinodeInterfacePublicIPv4(JSONObject): """ The IPv4 configuration of a public Linode Interface. - - NOTE: Linode interfaces may not currently be available to all users. """ put_class = LinodeInterfacePublicIPv4Options @@ -402,8 +344,6 @@ class LinodeInterfacePublicIPv4(JSONObject): class LinodeInterfacePublicIPv6SLAAC(JSONObject): """ A single SLAAC entry under the IPv6 configuration of a public Linode Interface. - - NOTE: Linode interfaces may not currently be available to all users. """ address: str = "" @@ -414,8 +354,6 @@ class LinodeInterfacePublicIPv6SLAAC(JSONObject): class LinodeInterfacePublicIPv6Shared(JSONObject): """ A single shared range under the IPv6 configuration of a public Linode Interface. - - NOTE: Linode interfaces may not currently be available to all users. """ range: str = "" @@ -426,8 +364,6 @@ class LinodeInterfacePublicIPv6Shared(JSONObject): class LinodeInterfacePublicIPv6Range(JSONObject): """ A single range under the IPv6 configuration of a public Linode Interface. - - NOTE: Linode interfaces may not currently be available to all users. """ put_class = LinodeInterfacePublicIPv6RangeOptions @@ -440,8 +376,6 @@ class LinodeInterfacePublicIPv6Range(JSONObject): class LinodeInterfacePublicIPv6(JSONObject): """ The IPv6 configuration of a Linode Interface. - - NOTE: Linode interfaces may not currently be available to all users. """ put_class = LinodeInterfacePublicIPv6Options @@ -455,8 +389,6 @@ class LinodeInterfacePublicIPv6(JSONObject): class LinodeInterfacePublic(JSONObject): """ Public-specific configuration fields for a Linode Interface. - - NOTE: Linode interfaces may not currently be available to all users. """ put_class = LinodeInterfacePublicOptions @@ -469,8 +401,6 @@ class LinodeInterfacePublic(JSONObject): class LinodeInterfaceVLAN(JSONObject): """ VLAN-specific configuration fields for a Linode Interface. - - NOTE: Linode interfaces may not currently be available to all users. """ put_class = LinodeInterfaceVLANOptions @@ -483,8 +413,6 @@ class LinodeInterface(DerivedBase): """ A Linode's network interface. - NOTE: Linode interfaces may not currently be available to all users. - NOTE: When using the ``save()`` method, certain local fields with computed values will not be refreshed on the local object until after ``invalidate()`` has been called:: @@ -528,8 +456,6 @@ def firewalls(self, *filters) -> List[Firewall]: Retrieves a list of Firewalls for this Linode Interface. Linode interfaces are not interchangeable with Config interfaces. - NOTE: Linode interfaces may not currently be available to all users. - :param filters: Any number of filters to apply to this query. See :doc:`Filtering Collections` for more details on filtering. diff --git a/linode_api4/objects/lock.py b/linode_api4/objects/lock.py index 9cee64517..3a1cd32d2 100644 --- a/linode_api4/objects/lock.py +++ b/linode_api4/objects/lock.py @@ -10,7 +10,7 @@ class LockType(StrEnum): """ LockType defines valid values for resource lock types. - API Documentation: TBD + API Documentation: https://techdocs.akamai.com/linode-api/reference/get-resource-lock """ cannot_delete = "cannot_delete" @@ -22,7 +22,7 @@ class LockEntity(JSONObject): """ Represents the entity that is locked. - API Documentation: TBD + API Documentation: https://techdocs.akamai.com/linode-api/reference/get-resource-lock """ id: int = 0 @@ -35,7 +35,7 @@ class Lock(Base): """ A resource lock that prevents deletion or modification of a resource. - API Documentation: TBD + API Documentation: https://techdocs.akamai.com/linode-api/reference/get-resource-lock """ api_endpoint = "/locks/{id}" diff --git a/linode_api4/objects/monitor.py b/linode_api4/objects/monitor.py index 1a83b59d6..7e0f4ae4d 100644 --- a/linode_api4/objects/monitor.py +++ b/linode_api4/objects/monitor.py @@ -433,7 +433,6 @@ class AlertDefinition(DerivedBase): "severity": Property(mutable=True), "type": Property(mutable=True), "status": Property(mutable=True), - "has_more_resources": Property(), # Deprecated; use entities.has_more_resources. "rule_criteria": Property(mutable=True, json_object=RuleCriteria), "trigger_conditions": Property( mutable=True, json_object=TriggerConditions @@ -449,6 +448,7 @@ class AlertDefinition(DerivedBase): "scope": Property(AlertScope), "regions": Property(mutable=True), "entities": Property(json_object=AlertEntities), + "channel_ids": Property(mutable=True), } diff --git a/linode_api4/objects/networking.py b/linode_api4/objects/networking.py index ed975ab71..44e4599b2 100644 --- a/linode_api4/objects/networking.py +++ b/linode_api4/objects/networking.py @@ -108,8 +108,6 @@ def interface(self) -> Optional["LinodeInterface"]: NOTE: This function will only return Linode interfaces, not Config interfaces. - NOTE: Linode interfaces may not currently be available to all users. - :returns: The Linode Interface associated with this IP address. :rtype: LinodeInterface """ diff --git a/linode_api4/objects/object_storage.py b/linode_api4/objects/object_storage.py index a2e61405f..fdb91e180 100644 --- a/linode_api4/objects/object_storage.py +++ b/linode_api4/objects/object_storage.py @@ -596,6 +596,8 @@ class ObjectStorageQuota(Base): "description": Property(), "quota_limit": Property(), "resource_metric": Property(), + "quota_type": Property(), + "has_usage": Property(), } def usage(self): @@ -614,3 +616,41 @@ def usage(self): ) return ObjectStorageQuotaUsage.from_json(result) + + +class ObjectStorageGlobalQuota(Base): + """ + An account-level Object Storage quota. + + API documentation: TBD + """ + + api_endpoint = "/object-storage/global-quotas/{quota_id}" + id_attribute = "quota_id" + + properties = { + "quota_id": Property(identifier=True), + "quota_type": Property(), + "quota_name": Property(), + "description": Property(), + "resource_metric": Property(), + "quota_limit": Property(), + "has_usage": Property(), + } + + def usage(self): + """ + Gets usage data for a specific account-level Object Storage quota. + + API documentation: https://techdocs.akamai.com/linode-api/reference/get-object-storage-global-quota-usage + + :returns: The Object Storage Global Quota usage. + :rtype: ObjectStorageQuotaUsage + """ + + result = self._client.get( + f"{type(self).api_endpoint}/usage", + model=self, + ) + + return ObjectStorageQuotaUsage.from_json(result) diff --git a/test/fixtures/monitor_alert-definitions.json b/test/fixtures/monitor_alert-definitions.json index e500354d1..2e040605f 100644 --- a/test/fixtures/monitor_alert-definitions.json +++ b/test/fixtures/monitor_alert-definitions.json @@ -9,7 +9,6 @@ "description": "A test alert for dbaas service", "scope": "entity", "regions": [], - "entity_ids": ["13217"], "entities": { "url": "/monitor/services/dbaas/alert-definitions/12345/entities", "count": 1, @@ -23,7 +22,6 @@ "url": "/monitor/alert-channels/10000" } ], - "has_more_resources": false, "rule_criteria": null, "trigger_conditions": null, "class": "alert", diff --git a/test/fixtures/monitor_services_dbaas_alert-definitions.json b/test/fixtures/monitor_services_dbaas_alert-definitions.json index 494b407d4..67ea9d2ab 100644 --- a/test/fixtures/monitor_services_dbaas_alert-definitions.json +++ b/test/fixtures/monitor_services_dbaas_alert-definitions.json @@ -9,9 +9,6 @@ "description": "A test alert for dbaas service", "scope": "entity", "regions": [], - "entity_ids": [ - "13217" - ], "entities": { "url": "/monitor/services/dbaas/alert-definitions/12345/entities", "count": 1, @@ -25,7 +22,6 @@ "url": "/monitor/alert-channels/10000" } ], - "has_more_resources": false, "rule_criteria": { "rules": [ { diff --git a/test/fixtures/monitor_services_dbaas_alert-definitions_12345.json b/test/fixtures/monitor_services_dbaas_alert-definitions_12345.json index 4b6a76272..4d70f66b1 100644 --- a/test/fixtures/monitor_services_dbaas_alert-definitions_12345.json +++ b/test/fixtures/monitor_services_dbaas_alert-definitions_12345.json @@ -7,9 +7,6 @@ "description": "A test alert for dbaas service", "scope": "entity", "regions": [], - "entity_ids": [ - "13217" - ], "entities": { "url": "/monitor/services/dbaas/alert-definitions/12345/entities", "count": 1, @@ -23,7 +20,6 @@ "url": "/monitor/alert-channels/10000" } ], - "has_more_resources": false, "rule_criteria": { "rules": [ { diff --git a/test/fixtures/object-storage_global-quotas.json b/test/fixtures/object-storage_global-quotas.json new file mode 100644 index 000000000..c9cc73b8c --- /dev/null +++ b/test/fixtures/object-storage_global-quotas.json @@ -0,0 +1,25 @@ +{ + "data": [ + { + "quota_id": "obj-access-keys-per-account", + "quota_type": "obj-access-keys", + "quota_name": "Object Storage Access Keys per Account", + "description": "Maximum number of access keys this customer is allowed to have on their account.", + "resource_metric": "access_key", + "quota_limit": 100, + "has_usage": true + }, + { + "quota_id": "obj-total-capacity-per-account", + "quota_type": "obj-total-capacity", + "quota_name": "Object Storage Total Capacity per Account", + "description": "Maximum total storage capacity in bytes this customer is allowed on their account.", + "resource_metric": "byte", + "quota_limit": 1099511627776, + "has_usage": true + } + ], + "page": 1, + "pages": 1, + "results": 2 +} diff --git a/test/fixtures/object-storage_global-quotas_obj-access-keys-per-account.json b/test/fixtures/object-storage_global-quotas_obj-access-keys-per-account.json new file mode 100644 index 000000000..b3f167550 --- /dev/null +++ b/test/fixtures/object-storage_global-quotas_obj-access-keys-per-account.json @@ -0,0 +1,9 @@ +{ + "quota_id": "obj-access-keys-per-account", + "quota_type": "obj-access-keys", + "quota_name": "Object Storage Access Keys per Account", + "description": "Maximum number of access keys this customer is allowed to have on their account.", + "resource_metric": "access_key", + "quota_limit": 100, + "has_usage": true +} diff --git a/test/fixtures/object-storage_global-quotas_obj-access-keys-per-account_usage.json b/test/fixtures/object-storage_global-quotas_obj-access-keys-per-account_usage.json new file mode 100644 index 000000000..ae3be8a3c --- /dev/null +++ b/test/fixtures/object-storage_global-quotas_obj-access-keys-per-account_usage.json @@ -0,0 +1,4 @@ +{ + "quota_limit": 100, + "usage": 25 +} diff --git a/test/fixtures/object-storage_quotas.json b/test/fixtures/object-storage_quotas.json index e831d7303..e6b11554a 100644 --- a/test/fixtures/object-storage_quotas.json +++ b/test/fixtures/object-storage_quotas.json @@ -7,7 +7,9 @@ "endpoint_type": "E1", "s3_endpoint": "us-iad-1.linodeobjects.com", "quota_limit": 50, - "resource_metric": "object" + "resource_metric": "object", + "quota_type": "obj-objects", + "has_usage": true }, { "quota_id": "obj-bucket-us-ord-1", @@ -16,7 +18,9 @@ "endpoint_type": "E1", "s3_endpoint": "us-iad-1.linodeobjects.com", "quota_limit": 50, - "resource_metric": "bucket" + "resource_metric": "bucket", + "quota_type": "obj-bucket", + "has_usage": true } ], "page": 1, diff --git a/test/fixtures/object-storage_quotas_obj-objects-us-ord-1.json b/test/fixtures/object-storage_quotas_obj-objects-us-ord-1.json index e01d743c3..fe216e776 100644 --- a/test/fixtures/object-storage_quotas_obj-objects-us-ord-1.json +++ b/test/fixtures/object-storage_quotas_obj-objects-us-ord-1.json @@ -5,5 +5,7 @@ "endpoint_type": "E1", "s3_endpoint": "us-iad-1.linodeobjects.com", "quota_limit": 50, - "resource_metric": "object" + "resource_metric": "object", + "quota_type": "obj-objects", + "has_usage": true } \ No newline at end of file diff --git a/test/integration/models/object_storage/test_obj_quotas.py b/test/integration/models/object_storage/test_obj_quotas.py index 10a546bc7..d9be84c3d 100644 --- a/test/integration/models/object_storage/test_obj_quotas.py +++ b/test/integration/models/object_storage/test_obj_quotas.py @@ -1,6 +1,8 @@ import pytest +from linode_api4.errors import ApiError from linode_api4.objects.object_storage import ( + ObjectStorageGlobalQuota, ObjectStorageQuota, ObjectStorageQuotaUsage, ) @@ -25,6 +27,8 @@ def test_list_and_get_obj_storage_quotas(test_linode_client): 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 + assert found_quota.quota_type == get_quota.quota_type + assert found_quota.has_usage == get_quota.has_usage def test_get_obj_storage_quota_usage(test_linode_client): @@ -33,7 +37,21 @@ def test_get_obj_storage_quota_usage(test_linode_client): if len(quotas) < 1: pytest.skip("No available quota for testing. Skipping now...") - quota_id = quotas[0].quota_id + quota_with_usage = next( + (quota for quota in quotas if quota.has_usage), None + ) + + if quota_with_usage is None: + quota_id = quotas[0].quota_id + quota = test_linode_client.load(ObjectStorageQuota, quota_id) + + # quota without usage should return an API error on usage retrieval + with pytest.raises(ApiError): + quota.usage() + + return + + quota_id = quota_with_usage.quota_id quota = test_linode_client.load(ObjectStorageQuota, quota_id) quota_usage = quota.usage() @@ -43,3 +61,56 @@ def test_get_obj_storage_quota_usage(test_linode_client): if quota_usage.usage is not None: assert quota_usage.usage >= 0 + + +def test_list_and_get_obj_storage_global_quotas(test_linode_client): + quotas = test_linode_client.object_storage.global_quotas() + + if len(quotas) < 1: + pytest.skip("No available global quota for testing. Skipping now...") + + found_quota = quotas[0] + + get_quota = test_linode_client.load( + ObjectStorageGlobalQuota, found_quota.quota_id + ) + + assert found_quota.quota_id == get_quota.quota_id + assert found_quota.quota_type == get_quota.quota_type + assert found_quota.quota_name == get_quota.quota_name + assert found_quota.description == get_quota.description + assert found_quota.resource_metric == get_quota.resource_metric + assert found_quota.quota_limit == get_quota.quota_limit + assert found_quota.has_usage == get_quota.has_usage + + +def test_get_obj_storage_global_quota_usage(test_linode_client): + quotas = test_linode_client.object_storage.global_quotas() + + if len(quotas) < 1: + pytest.skip("No available global quota for testing. Skipping now...") + + quota_with_usage = next( + (quota for quota in quotas if quota.has_usage), None + ) + + if quota_with_usage is None: + quota_id = quotas[0].quota_id + quota = test_linode_client.load(ObjectStorageGlobalQuota, quota_id) + + # quota without usage should return an API error on usage retrieval + with pytest.raises(ApiError): + quota.usage() + + return + + quota_id = quota_with_usage.quota_id + quota = test_linode_client.load(ObjectStorageGlobalQuota, 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 diff --git a/test/unit/objects/object_storage_test.py b/test/unit/objects/object_storage_test.py index b7ff7e49c..e0deb4211 100644 --- a/test/unit/objects/object_storage_test.py +++ b/test/unit/objects/object_storage_test.py @@ -6,6 +6,7 @@ ObjectStorageACL, ObjectStorageBucket, ObjectStorageCluster, + ObjectStorageGlobalQuota, ObjectStorageQuota, ) @@ -306,6 +307,8 @@ def test_quota_get_and_list(self): self.assertEqual(quota.s3_endpoint, "us-iad-1.linodeobjects.com") self.assertEqual(quota.quota_limit, 50) self.assertEqual(quota.resource_metric, "object") + self.assertEqual(quota.quota_type, "obj-objects") + self.assertTrue(quota.has_usage) quota_usage_url = "/object-storage/quotas/obj-objects-us-ord-1/usage" with self.mock_get(quota_usage_url) as m: @@ -335,3 +338,59 @@ def test_quota_get_and_list(self): ) self.assertEqual(quotas[0].quota_limit, 50) self.assertEqual(quotas[0].resource_metric, "object") + self.assertEqual(quotas[0].quota_type, "obj-objects") + self.assertTrue(quotas[0].has_usage) + + def test_global_quota_get_and_list(self): + """ + Test that you can get and list account-level Object Storage global quotas and usage. + """ + quota = ObjectStorageGlobalQuota( + self.client, + "obj-access-keys-per-account", + ) + + self.assertIsNotNone(quota) + self.assertEqual(quota.quota_id, "obj-access-keys-per-account") + self.assertEqual(quota.quota_type, "obj-access-keys") + self.assertEqual( + quota.quota_name, + "Object Storage Access Keys per Account", + ) + self.assertEqual( + quota.description, + "Maximum number of access keys this customer is allowed to have on their account.", + ) + self.assertEqual(quota.resource_metric, "access_key") + self.assertEqual(quota.quota_limit, 100) + self.assertTrue(quota.has_usage) + + usage_url = ( + "/object-storage/global-quotas/obj-access-keys-per-account/usage" + ) + with self.mock_get(usage_url) as m: + usage = quota.usage() + self.assertIsNotNone(usage) + self.assertEqual(m.call_url, usage_url) + self.assertEqual(usage.quota_limit, 100) + self.assertEqual(usage.usage, 25) + + list_url = "/object-storage/global-quotas" + with self.mock_get(list_url) as m: + quotas = self.client.object_storage.global_quotas() + self.assertIsNotNone(quotas) + self.assertEqual(m.call_url, list_url) + self.assertEqual(len(quotas), 2) + self.assertEqual(quotas[0].quota_id, "obj-access-keys-per-account") + self.assertEqual(quotas[0].quota_type, "obj-access-keys") + self.assertEqual( + quotas[0].quota_name, + "Object Storage Access Keys per Account", + ) + self.assertEqual( + quotas[0].description, + "Maximum number of access keys this customer is allowed to have on their account.", + ) + self.assertEqual(quotas[0].resource_metric, "access_key") + self.assertEqual(quotas[0].quota_limit, 100) + self.assertTrue(quotas[0].has_usage)