From 40f87c02c82a672a8f49543af737e75c8622752f Mon Sep 17 00:00:00 2001 From: Aolin Date: Tue, 3 Oct 2023 15:46:46 +0800 Subject: [PATCH] release 1.0.9 (#25) Support three new endpoints, which are released in [v1beta Release 20230328](https://docs.pingcap.com/tidbcloud/api/v1beta#section/API-Changelog/20230328): - [Create a project](https://docs.pingcap.com/tidbcloud/api/v1beta#tag/Project/operation/CreateProject) - [List AWS Customer-Managed Encryption Keys for a project](https://docs.pingcap.com/tidbcloud/api/v1beta#tag/Cluster/operation/ListAwsCmek) - [Configure AWS Customer-Managed Encryption Keys for a project](https://docs.pingcap.com/tidbcloud/api/v1beta#tag/Cluster/operation/CreateAwsCmek) --- README.md | 57 +++++++++++++++- examples/0_list_resources.py | 12 +++- examples/2_1_create_serverless_cluster.py | 2 +- examples/2_2_create_dedicated_cluster.py | 2 +- examples/8_manage_project.py | 19 ++++++ pyproject.toml | 2 +- tidbcloudy/project.py | 82 +++++++++++++++++++++-- tidbcloudy/specification.py | 10 +++ tidbcloudy/tidbcloud.py | 29 ++++++++ 9 files changed, 203 insertions(+), 12 deletions(-) create mode 100644 examples/8_manage_project.py diff --git a/README.md b/README.md index ffcedf2..233902e 100644 --- a/README.md +++ b/README.md @@ -14,6 +14,7 @@ - [Prerequisites](https://github.com/Oreoxmt/tidbcloudy#prerequisites) - [List all resources in your organization](https://github.com/Oreoxmt/tidbcloudy#list-all-resources-in-your-organization) + - [Create a project and manage your AWS CMEK](https://github.com/Oreoxmt/tidbcloudy#create-a-project-and-manage-your-aws-cmek) **New in 1.0.9** - [Create a cluster](https://github.com/Oreoxmt/tidbcloudy#create-a-cluster) - [Connect to TiDB](https://github.com/Oreoxmt/tidbcloudy#connect-to-tidb) - [Modify a cluster](https://github.com/Oreoxmt/tidbcloudy#modify-a-cluster) @@ -35,7 +36,7 @@ If you do not have a TiDB Cloud account yet, you can sign up [here](https://tidb You can use this SDK to access [TiDB Cloud](https://tidbcloud.com) and manage your billings, projects, clusters, backups and restores: - manage your **billings** of your organization (_get_) -- manage your TiDB Cloud **projects** (only _list_ is supported now) +- manage your TiDB Cloud **projects** (_list_, _create_) and manage the **AWS CMEK** of your projects (_get_, _create_) - list all available cloud providers (AWS and GCP), regions, and specifications before creating or modifying a cluster - manage your TiDB Serverless or TiDB Dedicated **clusters** (_create_, _modify_, _pause_, _resume_, _get_, _list_, _delete_) - manage your **backups** of a cluster (_create_, _get_, _list_, _delete_) @@ -43,7 +44,7 @@ You can use this SDK to access [TiDB Cloud](https://tidbcloud.com) and manage yo ### Compatibility with TiDB Cloud API -`tidbcloudy` is compatible with TiDB Cloud API. **Endpoints added in [v1beta Release 20230228](https://docs.pingcap.com/tidbcloud/api/v1beta#section/API-Changelog/20230228), [v1beta Release 20230328](https://docs.pingcap.com/tidbcloud/api/v1beta#section/API-Changelog/20230328), and [v1beta Release 20230905](https://docs.pingcap.com/tidbcloud/api/v1beta#section/API-Changelog/20230905) are not supported for now**. The following table lists the compatibility between `tidbcloudy` and TiDB Cloud API. +`tidbcloudy` is compatible with TiDB Cloud API. **Endpoints added in [v1beta Release 20230228](https://docs.pingcap.com/tidbcloud/api/v1beta#section/API-Changelog/20230228) and [v1beta Release 20230905](https://docs.pingcap.com/tidbcloud/api/v1beta#section/API-Changelog/20230905) are not supported for now**. The following table lists the compatibility between `tidbcloudy` and TiDB Cloud API. @@ -70,6 +71,23 @@ You can use this SDK to access [TiDB Cloud](https://tidbcloud.com) and manage yo + + + + + + + + + + + + + + + + + @@ -316,6 +334,8 @@ To get full code examples, see the [`examples`](https://github.com/Oreoxmt/tidbc ### List all resources in your organization +**This feature is available in `tidbcloudy` 1.0.9 or later.** + To get the full code example of listing all projects, clusters, backup tasks, and restore tasks in your organization, see [`0_list_resources.py`](https://github.com/Oreoxmt/tidbcloudy/tree/main/examples/0_list_resources.py). ```python @@ -331,6 +351,9 @@ api = tidbcloudy.TiDBCloud(public_key=public_key, private_key=private_key) for project in api.iter_projects(): print(project) + if project.aws_cmek_enabled: + for aws_cmek in project.iter_aws_cmek(): + print(aws_cmek) for cluster in project.iter_clusters(): print(cluster) if cluster.cluster_type == ClusterType.DEDICATED: @@ -340,6 +363,32 @@ for project in api.iter_projects(): print(restore) ``` +### Create a project and manage your AWS CMEK + +To create a project, run the [`8_manage_project.py`](https://github.com/Oreoxmt/tidbcloudy/tree/main/examples/8_manage_project.py). + +```python +import os + +import tidbcloudy + +public_key = os.environ.get("PUBLIC_KEY") +private_key = os.environ.get("PRIVATE_KEY") +debug_mode = os.environ.get("TIDBCLOUDY_LOG") + +api = tidbcloudy.TiDBCloud(public_key=public_key, private_key=private_key) +# Create a project with AWS CMEK enabled +project = api.create_project(name="0", aws_cmek_enabled=True, update_from_server=True) +print(project) + +# Configure AWS CMEK for the project +project.create_aws_cmek([("your_aws_region_1", "your_aws_kms_arn_1"), ("your_aws_region_2", "your_aws_kms_arn_2")]) + +# List all AWS CMEKs of the project +for cmek in project.iter_aws_cmek(): + print(cmek) +``` + ### Create a cluster Before creating a cluster, you should list all available provider regions and cluster configuration specifications. For more details, run the [`1_list_provider_regions.py`](https://github.com/Oreoxmt/tidbcloudy/tree/main/examples/1_list_provider_regions.py). @@ -524,6 +573,8 @@ for restore in project.iter_restores(): ### Pause or resume your cluster +**This feature is available in `tidbcloudy` 1.0.0 or later.** + To pause or resume your cluster, run the [`6_pause_cluster.py`](https://github.com/Oreoxmt/tidbcloudy/tree/main/examples/6_pause_cluster.py). ```python @@ -554,6 +605,8 @@ if cluster.status.cluster_status == ClusterStatus.RESUMING: ### Get monthly bills of your organization +**This feature is available in `tidbcloudy` 1.0.8 or later.** + To get the billing information of your organization, run the [`v1beta1_get_monthly_bill.py`](https://github.com/Oreoxmt/tidbcloudy/tree/main/examples/v1beta1_get_monthly_bill.py). ```python diff --git a/examples/0_list_resources.py b/examples/0_list_resources.py index 642c8b6..e824aac 100644 --- a/examples/0_list_resources.py +++ b/examples/0_list_resources.py @@ -1,8 +1,11 @@ import os import tidbcloudy +from tidbcloudy.backup import Backup +from tidbcloudy.cluster import Cluster from tidbcloudy.project import Project -from tidbcloudy.specification import ClusterType +from tidbcloudy.restore import Restore +from tidbcloudy.specification import ClusterType, ProjectAWSCMEK public_key = os.environ.get("PUBLIC_KEY") private_key = os.environ.get("PRIVATE_KEY") @@ -14,12 +17,19 @@ for project in api.iter_projects(): assert isinstance(project, Project) print(project) + if project.aws_cmek_enabled: + for aws_cmek in project.iter_aws_cmek(): + assert isinstance(aws_cmek, ProjectAWSCMEK) + print(aws_cmek) for cluster in project.iter_clusters(): + assert isinstance(cluster, Cluster) print(cluster) if cluster.cluster_type == ClusterType.DEDICATED: for backup in cluster.iter_backups(): + assert isinstance(backup, Backup) print(backup) for restore in project.iter_restores(): + assert isinstance(restore, Restore) print(restore) # get_project() does not fetch full information of a project from the server by default diff --git a/examples/2_1_create_serverless_cluster.py b/examples/2_1_create_serverless_cluster.py index 274c9c8..d066646 100644 --- a/examples/2_1_create_serverless_cluster.py +++ b/examples/2_1_create_serverless_cluster.py @@ -12,7 +12,7 @@ project = api.get_project(project_id, update_from_server=True) config = CreateClusterConfig() -config\ +config \ .set_name("serverless-0") \ .set_cluster_type("DEVELOPER") \ .set_cloud_provider("AWS") \ diff --git a/examples/2_2_create_dedicated_cluster.py b/examples/2_2_create_dedicated_cluster.py index a335193..5889646 100644 --- a/examples/2_2_create_dedicated_cluster.py +++ b/examples/2_2_create_dedicated_cluster.py @@ -12,7 +12,7 @@ project = api.get_project(project_id, update_from_server=True) config = CreateClusterConfig() -config\ +config \ .set_name("dedicated-1") \ .set_cluster_type("DEDICATED") \ .set_cloud_provider("AWS") \ diff --git a/examples/8_manage_project.py b/examples/8_manage_project.py new file mode 100644 index 0000000..fc5a004 --- /dev/null +++ b/examples/8_manage_project.py @@ -0,0 +1,19 @@ +import os + +import tidbcloudy + +public_key = os.environ.get("PUBLIC_KEY") +private_key = os.environ.get("PRIVATE_KEY") +debug_mode = os.environ.get("TIDBCLOUDY_LOG") + +api = tidbcloudy.TiDBCloud(public_key=public_key, private_key=private_key) +# Create a project with AWS CMEK enabled +project = api.create_project(name="0", aws_cmek_enabled=True, update_from_server=True) +print(project) + +# Configure AWS CMEK for the project +project.create_aws_cmek([("your_aws_region_1", "your_aws_kms_arn_1"), ("your_aws_region_2", "your_aws_kms_arn_2")]) + +# List all AWS CMEKs of the project +for cmek in project.iter_aws_cmek(): + print(cmek) diff --git a/pyproject.toml b/pyproject.toml index e245c18..6679c27 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "tidbcloudy" -version = "1.0.8" +version = "1.0.9" description = "(Unofficial) Python SDK for TiDB Cloud" readme = "README.md" authors = ["Aolin "] diff --git a/tidbcloudy/project.py b/tidbcloudy/project.py index d7dff20..af6aba9 100644 --- a/tidbcloudy/project.py +++ b/tidbcloudy/project.py @@ -1,22 +1,23 @@ -from typing import Union, Iterator +from typing import Iterator, List, Tuple, Union from ._base import TiDBCloudyBase, TiDBCloudyContextualBase, TiDBCloudyField from .cluster import Cluster from .restore import Restore -from .specification import CreateClusterConfig, UpdateClusterConfig -from .util.timestamp import timestamp_to_string +from .specification import CreateClusterConfig, ProjectAWSCMEK, UpdateClusterConfig from .util.page import Page +from .util.timestamp import timestamp_to_string # noinspection PyShadowingBuiltins class Project(TiDBCloudyBase, TiDBCloudyContextualBase): - __slots__ = ["_id", "_org_id", "_name", "_cluster_count", "_user_count", "_create_timestamp"] + __slots__ = ["_id", "_org_id", "_name", "_cluster_count", "_user_count", "_create_timestamp", "_aws_cmek_enabled"] id: str = TiDBCloudyField(str) org_id: str = TiDBCloudyField(str) name: str = TiDBCloudyField(str) cluster_count: int = TiDBCloudyField(int) user_count: int = TiDBCloudyField(int) create_timestamp: int = TiDBCloudyField(int, convert_from=int, convert_to=str) + aws_cmek_enabled: bool = TiDBCloudyField(bool) def create_cluster(self, config: Union[CreateClusterConfig, dict]) -> Cluster: """ @@ -248,6 +249,75 @@ def iter_restores(self, page_size: int = 10) -> Iterator[Restore]: yield restore page += 1 + def create_aws_cmek(self, config: List[Tuple[str, str]]) -> None: + """ + Configure the AWS Customer-Managed Encryption Keys (CMEK) for the project. + Args: + config: the configuration of the CMEK. The format is [(region, kms_arn), ...] + + Examples: + .. code-block:: python + import tidbcloudy + api = tidbcloudy.TiDBCloud(public_key="your_public_key", private_key="your_private_key") + project = api.create_project(name="your_project_name", aws_cmek_enabled=True, update_from_server=True) + project.create_aws_cmek([(region, kms_arn), ...] + for cmek in project.iter_aws_cmek(): + print(cmek) + """ + payload = { + "specs": [] + } + for region, kms_arn in config: + payload["specs"].append({ + "region": region, + "kms_arn": kms_arn + }) + path = f"projects/{self.id}/aws-cmek" + self.context.call_post(path=path, json=payload) + + def list_aws_cmek(self) -> Page[ProjectAWSCMEK]: + """ + List all AWS Customer-Managed Encryption Keys (CMEK) in the project. + + Returns: + The page of the CMEK in the project. + + Examples: + .. code-block:: python + import tidbcloudy + api = tidbcloudy.TiDBCloud(public_key="your_public_key", private_key="your_private_key") + project = api.get_project(project_id) + cmeks = project.list_aws_cmek() + for cmek in cmeks.items: + print(cmek) + """ + path = f"projects/{self.id}/aws-cmek" + resp = self.context.call_get(path=path) + total = len(resp["items"]) + return Page( + [ProjectAWSCMEK.from_object(self.context, item) for item in resp["items"]], + 1, total, total) + + def iter_aws_cmek(self) -> Iterator[ProjectAWSCMEK]: + """ + This is not a TiDB Cloud API official endpoint. + Iterate all AWS Customer-Managed Encryption Keys (CMEK) in the project. + + Returns: + The iterator of the CMEK in the project. + + Examples: + .. code-block:: python + import tidbcloudy + api = tidbcloudy.TiDBCloud(public_key="your_public_key", private_key="your_private_key") + project = api.get_project(project_id) + for cmek in project.iter_aws_cmek(): + print(cmek) + """ + cmeks = self.list_aws_cmek() + for cmek in cmeks.items: + yield cmek + def __repr__(self): - return "".format( - self.id, self.name, timestamp_to_string(self.create_timestamp)) + return "".format( + self.id, self.name, self.aws_cmek_enabled, timestamp_to_string(self.create_timestamp)) diff --git a/tidbcloudy/specification.py b/tidbcloudy/specification.py index b5ae562..c2f49f6 100644 --- a/tidbcloudy/specification.py +++ b/tidbcloudy/specification.py @@ -364,6 +364,16 @@ def __repr__(self): self.status.value if self.status is not None else None) +class ProjectAWSCMEK(TiDBCloudyBase): + __slots__ = ["_region", "_kms_arn"] + region: str = None + kms_arn: str = None + + def __repr__(self): + return "".format( + self.region, self.kms_arn) + + class BillingBase(TiDBCloudyBase): __slots__ = ["_credits", "_discounts", "_runningTotal", "_totalCost"] credits: str = TiDBCloudyField(str) diff --git a/tidbcloudy/tidbcloud.py b/tidbcloudy/tidbcloud.py index 5443f77..cf9efdb 100644 --- a/tidbcloudy/tidbcloud.py +++ b/tidbcloudy/tidbcloud.py @@ -12,6 +12,35 @@ class TiDBCloud: def __init__(self, public_key: str, private_key: str): self._context = Context(public_key, private_key) + def create_project(self, name: str, aws_cmek_enabled: bool = False, update_from_server: bool = False) -> Project: + """ + Create a project. + Args: + name: the project name. + aws_cmek_enabled: whether to enable AWS Customer-Managed Encryption Keys. + update_from_server: whether to update the project info after creating. + + Returns: + If the update_from_server is False, return a Project object with only the context and project_id. + If the update_from_server is True, return a Project object with all the info. + + Examples: + .. code-block:: python + import tidbcloudy + api = tidbcloudy.TiDBCloud(public_key="your_public_key", private_key="your_private_key") + project = api.create_project(name="your_project_name", aws_cmek_enabled=False, update_from_server=True) + print(project) + """ + config = { + "name": name, + "aws_cmek_enabled": aws_cmek_enabled + } + resp = self._context.call_post(path="projects", json=config) + project_id = resp["id"] + if update_from_server: + return self.get_project(project_id=project_id, update_from_server=True) + return Project(context=self._context, id=project_id) + def get_project(self, project_id: str, update_from_server: bool = False) -> Project: """ Get the project object by project_id.
1.0.9
1.0.8