Skip to content
Merged
32 changes: 28 additions & 4 deletions .github/workflows/e2e-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,8 @@ jobs:
runs-on: ubuntu-latest
needs: [integration-tests]
if: always() && github.repository == 'linode/linode_api4-python' # Run even if integration tests fail and only on main repository
outputs:
summary: ${{ steps.set-test-summary.outputs.summary }}

steps:
- name: Checkout code
Expand All @@ -197,7 +199,6 @@ jobs:
- name: Set release version env
run: echo "RELEASE_VERSION=${GITHUB_REF#refs/*/}" >> $GITHUB_ENV


- name: Add variables and upload test results
if: always()
run: |
Expand All @@ -213,12 +214,24 @@ jobs:
LINODE_CLI_OBJ_ACCESS_KEY: ${{ secrets.LINODE_CLI_OBJ_ACCESS_KEY }}
LINODE_CLI_OBJ_SECRET_KEY: ${{ secrets.LINODE_CLI_OBJ_SECRET_KEY }}

- name: Generate test summary and save to output
id: set-test-summary
run: |
filename=$(ls | grep -E '^[0-9]{12}_sdk_test_report\.xml$')
test_output=$(python3 e2e_scripts/tod_scripts/generate_test_summary.py "${filename}")
{
echo 'summary<<EOF'
echo "$test_output"
echo EOF
} >> "$GITHUB_OUTPUT"

notify-slack:
runs-on: ubuntu-latest
needs: [integration-tests]
needs: [integration-tests, process-upload-report]
if: ${{ (success() || failure()) }} # Run even if integration tests fail and only on main repository
steps:
- name: Notify Slack
id: main_message
uses: slackapi/slack-github-action@v2.0.0
with:
method: chat.postMessage
Expand All @@ -229,7 +242,7 @@ jobs:
- type: section
text:
type: mrkdwn
text: ":rocket: *${{ github.workflow }} Completed in: ${{ github.repository }}* :white_check_mark:"
text: ":rocket: *${{ github.workflow }} Completed in: ${{ github.repository }}* ${{ needs.integration-tests.result == 'success' && ':white_check_mark:' || ':failed:' }}"
- type: divider
- type: section
fields:
Expand All @@ -247,4 +260,15 @@ jobs:
- type: context
elements:
- type: mrkdwn
text: "Triggered by: :bust_in_silhouette: `${{ github.actor }}`"
text: "Triggered by: :bust_in_silhouette: `${{ github.actor }}`"

- name: Test summary thread
if: success()
uses: slackapi/slack-github-action@v2.0.0
with:
method: chat.postMessage
token: ${{ secrets.SLACK_BOT_TOKEN }}
payload: |
channel: ${{ secrets.SLACK_CHANNEL_ID }}
thread_ts: "${{ steps.main_message.outputs.ts }}"
text: "${{ needs.process-upload-report.outputs.summary }}"
2 changes: 1 addition & 1 deletion linode_api4/groups/lke.py
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,7 @@ def cluster_create(

result = self.client.post(
"/lke/clusters",
data=_flatten_request_body_recursive(drop_null_keys(params)),
data=drop_null_keys(_flatten_request_body_recursive(params)),
)

if "id" not in result:
Expand Down
40 changes: 40 additions & 0 deletions linode_api4/groups/object_storage.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,11 @@

from deprecated import deprecated

from linode_api4 import (
ObjectStorageEndpoint,
ObjectStorageEndpointType,
PaginatedList,
)
from linode_api4.errors import UnexpectedResponseError
from linode_api4.groups import Group
from linode_api4.objects import (
Expand Down Expand Up @@ -272,6 +277,30 @@ def transfer(self):

return MappedObject(**result)

def endpoints(self, *filters) -> PaginatedList:
"""
Returns a paginated list of all Object Storage endpoints available in your account.

This is intended to be called from the :any:`LinodeClient`
class, like this::

endpoints = client.object_storage.endpoints()

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

: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 Endpoints that matched the query.
:rtype: PaginatedList of ObjectStorageEndpoint
"""
return self.client._get_and_filter(
ObjectStorageEndpoint,
*filters,
endpoint="/object-storage/endpoints",
)

def buckets(self, *filters):
"""
Returns a paginated list of all Object Storage Buckets that you own.
Expand Down Expand Up @@ -299,6 +328,8 @@ def bucket_create(
label: str,
acl: ObjectStorageACL = ObjectStorageACL.PRIVATE,
cors_enabled=False,
s3_endpoint: Optional[str] = None,
endpoint_type: Optional[ObjectStorageEndpointType] = None,
):
"""
Creates an Object Storage Bucket in the specified cluster. Accounts with
Expand All @@ -320,6 +351,13 @@ def bucket_create(
should be created.
:type cluster: str

:param endpoint_type: The type of s3_endpoint available to the active user in this region.
:type endpoint_type: str
Enum: E0,E1,E2,E3

:param s3_endpoint: The active user's s3 endpoint URL, based on the endpoint_type and region.
:type s3_endpoint: str

:param cors_enabled: If true, the bucket will be created with CORS enabled for
all origins. For more fine-grained controls of CORS, use
the S3 API directly.
Expand All @@ -346,6 +384,8 @@ def bucket_create(
"label": label,
"acl": acl,
"cors_enabled": cors_enabled,
"s3_endpoint": s3_endpoint,
"endpoint_type": endpoint_type,
}

if self.is_cluster(cluster_or_region_id):
Expand Down
17 changes: 17 additions & 0 deletions linode_api4/objects/database.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
from deprecated import deprecated

from linode_api4.objects import Base, DerivedBase, MappedObject, Property


Expand Down Expand Up @@ -63,6 +65,9 @@ def invalidate(self):
Base.invalidate(self)


@deprecated(
reason="Backups are not supported for non-legacy database clusters."
)
class DatabaseBackup(DerivedBase):
"""
A generic Managed Database backup.
Expand Down Expand Up @@ -97,6 +102,9 @@ def restore(self):
)


@deprecated(
reason="Backups are not supported for non-legacy database clusters."
)
class MySQLDatabaseBackup(DatabaseBackup):
"""
A backup for an accessible Managed MySQL Database.
Expand All @@ -107,6 +115,9 @@ class MySQLDatabaseBackup(DatabaseBackup):
api_endpoint = "/databases/mysql/instances/{database_id}/backups/{id}"


@deprecated(
reason="Backups are not supported for non-legacy database clusters."
)
class PostgreSQLDatabaseBackup(DatabaseBackup):
"""
A backup for an accessible Managed PostgreSQL Database.
Expand Down Expand Up @@ -221,6 +232,9 @@ def patch(self):
"{}/patch".format(MySQLDatabase.api_endpoint), model=self
)

@deprecated(
reason="Backups are not supported for non-legacy database clusters."
)
def backup_create(self, label, **kwargs):
"""
Creates a snapshot backup of a Managed MySQL Database.
Expand Down Expand Up @@ -358,6 +372,9 @@ def patch(self):
"{}/patch".format(PostgreSQLDatabase.api_endpoint), model=self
)

@deprecated(
reason="Backups are not supported for non-legacy database clusters."
)
def backup_create(self, label, **kwargs):
"""
Creates a snapshot backup of a Managed PostgreSQL Database.
Expand Down
13 changes: 2 additions & 11 deletions linode_api4/objects/lke.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
Region,
Type,
)
from linode_api4.util import drop_null_keys


class LKEType(Base):
Expand Down Expand Up @@ -79,8 +80,6 @@ class LKEClusterControlPlaneACLOptions(JSONObject):
"""
LKEClusterControlPlaneACLOptions is used to set
the ACL configuration of an LKE cluster's control plane.

NOTE: Control Plane ACLs may not currently be available to all users.
"""

enabled: Optional[bool] = None
Expand Down Expand Up @@ -116,8 +115,6 @@ class LKEClusterControlPlaneACL(JSONObject):
"""
LKEClusterControlPlaneACL describes the ACL configuration of an LKE cluster's
control plane.

NOTE: Control Plane ACLs may not currently be available to all users.
"""

include_none_values = True
Expand Down Expand Up @@ -337,8 +334,6 @@ def control_plane_acl(self) -> LKEClusterControlPlaneACL:
"""
Gets the ACL configuration of this cluster's control plane.

NOTE: Control Plane ACLs may not currently be available to all users.

API Documentation: https://techdocs.akamai.com/linode-api/reference/get-lke-cluster-acl

:returns: The cluster's control plane ACL configuration.
Expand Down Expand Up @@ -558,8 +553,6 @@ def control_plane_acl_update(
"""
Updates the ACL configuration for this cluster's control plane.

NOTE: Control Plane ACLs may not currently be available to all users.

API Documentation: https://techdocs.akamai.com/linode-api/reference/put-lke-cluster-acl

:param acl: The ACL configuration to apply to this cluster.
Expand All @@ -574,7 +567,7 @@ def control_plane_acl_update(
result = self._client.put(
f"{LKECluster.api_endpoint}/control_plane_acl",
model=self,
data={"acl": acl},
data={"acl": drop_null_keys(acl)},
)

acl = result.get("acl")
Expand All @@ -589,8 +582,6 @@ def control_plane_acl_delete(self):
This has the same effect as calling control_plane_acl_update with the `enabled` field
set to False. Access controls are disabled and all rules are deleted.

NOTE: Control Plane ACLs may not currently be available to all users.

API Documentation: https://techdocs.akamai.com/linode-api/reference/delete-lke-cluster-acl
"""
self._client.delete(
Expand Down
60 changes: 52 additions & 8 deletions linode_api4/objects/object_storage.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
from dataclasses import dataclass
from typing import Optional
from urllib import parse

Expand All @@ -11,7 +12,7 @@
Property,
Region,
)
from linode_api4.objects.serializable import StrEnum
from linode_api4.objects.serializable import JSONObject, StrEnum
from linode_api4.util import drop_null_keys


Expand All @@ -28,6 +29,27 @@ class ObjectStorageKeyPermission(StrEnum):
READ_WRITE = "read_write"


class ObjectStorageEndpointType(StrEnum):
E0 = "E0"
E1 = "E1"
E2 = "E2"
E3 = "E3"


@dataclass
class ObjectStorageEndpoint(JSONObject):
"""
ObjectStorageEndpoint contains the core fields of an object storage endpoint object.

NOTE: This is not implemented as a typical API object (Base) because Object Storage Endpoints
cannot be refreshed, as there is no singular GET endpoint.
"""

region: str = ""
endpoint_type: ObjectStorageEndpointType = ""
s3_endpoint: Optional[str] = None


class ObjectStorageBucket(DerivedBase):
"""
A bucket where objects are stored in.
Expand All @@ -47,6 +69,8 @@ class ObjectStorageBucket(DerivedBase):
"label": Property(identifier=True),
"objects": Property(),
"size": Property(),
"endpoint_type": Property(),
"s3_endpoint": Property(),
}

@classmethod
Expand All @@ -63,13 +87,8 @@ def make_instance(cls, id, client, parent_id=None, json=None):
Override this method to pass in the parent_id from the _raw_json object
when it's available.
"""
if json is None:
return None

cluster_or_region = json.get("region") or json.get("cluster")

if parent_id is None and cluster_or_region:
parent_id = cluster_or_region
if json is not None:
parent_id = parent_id or json.get("region") or json.get("cluster")

if parent_id:
return super().make(id, client, cls, parent_id=parent_id, json=json)
Expand All @@ -78,6 +97,31 @@ def make_instance(cls, id, client, parent_id=None, json=None):
"Unexpected json response when making a new Object Storage Bucket instance."
)

def access_get(self):
"""
Returns a result object which wraps the current access config for this ObjectStorageBucket.

API Documentation: TODO

:returns: A result object which wraps the access that this ObjectStorageBucket is currently configured with.
:rtype: MappedObject
"""
result = self._client.get(
"{}/access".format(self.api_endpoint),
model=self,
)

if not any(
key in result
for key in ["acl", "acl_xml", "cors_enabled", "cors_xml"]
):
raise UnexpectedResponseError(
"Unexpected response when getting the bucket access config of a bucket!",
json=result,
)

return MappedObject(**result)

def access_modify(
self,
acl: Optional[ObjectStorageACL] = None,
Expand Down
4 changes: 3 additions & 1 deletion test/fixtures/object-storage_buckets_us-east-1.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@
"hostname": "example-bucket.us-east-1.linodeobjects.com",
"label": "example-bucket",
"objects": 4,
"size": 188318981
"size": 188318981,
"endpoint_type": "E1",
"s3_endpoint": "us-east-12.linodeobjects.com"
}
],
"page": 1,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,7 @@
"hostname": "example-bucket.us-east-1.linodeobjects.com",
"label": "example-bucket",
"objects": 4,
"size": 188318981
"size": 188318981,
"endpoint_type": "E1",
"s3_endpoint": "us-east-12.linodeobjects.com"
}
Loading
Loading