From 7598e07d61ceb88797c91b5425799e3242bc1f6f Mon Sep 17 00:00:00 2001 From: Andreas Motl Date: Sat, 18 Nov 2023 01:40:03 +0100 Subject: [PATCH] Cloud API: Clean up API and utility functions --- cratedb_toolkit/api/main.py | 21 +++++++------ cratedb_toolkit/cli.py | 2 +- cratedb_toolkit/cluster/cli.py | 4 +-- cratedb_toolkit/cluster/model.py | 52 ++++++++++++++++++++++++++++++++ cratedb_toolkit/cluster/util.py | 40 ------------------------ cratedb_toolkit/model.py | 13 -------- cratedb_toolkit/shell/cli.py | 4 +-- tests/conftest.py | 1 - 8 files changed, 69 insertions(+), 68 deletions(-) create mode 100644 cratedb_toolkit/cluster/model.py delete mode 100644 cratedb_toolkit/cluster/util.py diff --git a/cratedb_toolkit/api/main.py b/cratedb_toolkit/api/main.py index 298b2d91..65f5de97 100644 --- a/cratedb_toolkit/api/main.py +++ b/cratedb_toolkit/api/main.py @@ -11,14 +11,12 @@ from cratedb_toolkit.api.guide import GuidingTexts from cratedb_toolkit.api.model import ClientBundle, ClusterBase -from cratedb_toolkit.cluster.util import ( - deploy_cluster, - get_cluster_by_id_or_name, -) +from cratedb_toolkit.cluster.croud import CloudManager +from cratedb_toolkit.cluster.model import ClusterInformation from cratedb_toolkit.config import CONFIG from cratedb_toolkit.exception import CroudException, OperationFailed from cratedb_toolkit.io.croud import CloudJob -from cratedb_toolkit.model import ClusterInformation, DatabaseAddress, InputOutputResource, TableAddress +from cratedb_toolkit.model import DatabaseAddress, InputOutputResource, TableAddress from cratedb_toolkit.util import DatabaseAdapter from cratedb_toolkit.util.data import asbool from cratedb_toolkit.util.runtime import flexfun @@ -174,7 +172,7 @@ def probe(self) -> "ManagedCluster": TODO: Investigate callers, and reduce number of invocations. """ try: - self.info = get_cluster_by_id_or_name(cluster_id=self.id, cluster_name=self.name) + self.info = ClusterInformation.from_id_or_name(cluster_id=self.id, cluster_name=self.name) self.id = self.info.cloud["id"] self.name = self.info.cloud["name"] except (CroudException, ValueError) as ex: @@ -226,10 +224,15 @@ def deploy(self) -> "ManagedCluster": # FIXME: Accept id or name. if self.name is None: raise ValueError("Need cluster name to deploy") - deploy_cluster( - self.name, subscription_id=self.settings.subscription_id, organization_id=self.settings.organization_id + cm = CloudManager() + # TODO: Only create new project when needed. Otherwise, use existing project. + project = cm.create_project(name=self.name, organization_id=self.settings.organization_id) + project_id = project["id"] + logger.info(f"Created project: {project_id}") + cluster_info = cm.deploy_cluster( + name=self.name, project_id=project_id, subscription_id=self.settings.subscription_id ) - return self + return cluster_info @flexfun(domain="runtime") def load_table(self, source: InputOutputResource, target: t.Optional[TableAddress] = None) -> CloudJob: diff --git a/cratedb_toolkit/cli.py b/cratedb_toolkit/cli.py index f571aad6..077c07fc 100644 --- a/cratedb_toolkit/cli.py +++ b/cratedb_toolkit/cli.py @@ -9,8 +9,8 @@ from .io.cli import cli as io_cli from .job.cli import cli_list_jobs from .shell.cli import cli as shell_cli -from .wtf.cli import cli as wtf_cli from .util.setting import init_dotenv +from .wtf.cli import cli as wtf_cli @click.group(cls=ClickAliasedGroup) # type: ignore[arg-type] diff --git a/cratedb_toolkit/cluster/cli.py b/cratedb_toolkit/cluster/cli.py index b148a3f0..ea0b85a0 100644 --- a/cratedb_toolkit/cluster/cli.py +++ b/cratedb_toolkit/cluster/cli.py @@ -5,7 +5,7 @@ from click_aliases import ClickAliasedGroup from cratedb_toolkit import ManagedCluster -from cratedb_toolkit.cluster.util import get_cluster_by_id_or_name +from cratedb_toolkit.cluster.model import ClusterInformation from cratedb_toolkit.common import option_cluster_id, option_cluster_name from cratedb_toolkit.exception import CroudException from cratedb_toolkit.util import jd @@ -38,7 +38,7 @@ def info(ctx: click.Context, cluster_id: str, cluster_name: str): ctk cluster info --cluster-id=e1e38d92-a650-48f1-8a70-8133f2d5c400 croud clusters get e1e38d92-a650-48f1-8a70-8133f2d5c400 --format=json """ - cluster_info = get_cluster_by_id_or_name(cluster_id=cluster_id, cluster_name=cluster_name) + cluster_info = ClusterInformation.from_id_or_name(cluster_id=cluster_id, cluster_name=cluster_name) try: jd(cluster_info.asdict()) diff --git a/cratedb_toolkit/cluster/model.py b/cratedb_toolkit/cluster/model.py new file mode 100644 index 00000000..57dba934 --- /dev/null +++ b/cratedb_toolkit/cluster/model.py @@ -0,0 +1,52 @@ +import dataclasses +import typing as t + +from cratedb_toolkit.cluster.croud import CloudCluster, CloudManager +from cratedb_toolkit.exception import CroudException + + +@dataclasses.dataclass +class ClusterInformation: + """ + Manage a database cluster's information. + """ + + cratedb: t.Any = dataclasses.field(default_factory=dict) + cloud: t.Dict[str, t.Any] = dataclasses.field(default_factory=dict) + + @classmethod + def from_id_or_name(cls, cluster_id: str = None, cluster_name: str = None) -> "ClusterInformation": + """ + Look up cluster by identifier (UUID) or name. + """ + if cluster_id is not None: + return cls.from_id(cluster_id=cluster_id) + elif cluster_name is not None: + return cls.from_name(cluster_name=cluster_name) + else: + raise ValueError("Failed to address cluster: Either cluster identifier or name needs to be specified") + + @classmethod + def from_id(cls, cluster_id: str) -> "ClusterInformation": + """ + Look up cluster by identifier (UUID). + """ + + cc = CloudCluster(cluster_id=cluster_id) + return ClusterInformation(cloud=cc.get_info()) + + @classmethod + def from_name(cls, cluster_name: str) -> "ClusterInformation": + """ + Look up cluster by name. + """ + + cm = CloudManager() + cluster_list = cm.list_clusters() + for cluster in cluster_list: + if cluster["name"] == cluster_name: + return ClusterInformation(cloud=cluster) + raise CroudException(f"Cluster not found: {cluster_name}") + + def asdict(self): + return dataclasses.asdict(self) diff --git a/cratedb_toolkit/cluster/util.py b/cratedb_toolkit/cluster/util.py deleted file mode 100644 index ebc5416d..00000000 --- a/cratedb_toolkit/cluster/util.py +++ /dev/null @@ -1,40 +0,0 @@ -import logging - -from cratedb_toolkit.cluster.croud import CloudCluster, CloudManager -from cratedb_toolkit.exception import CroudException -from cratedb_toolkit.model import ClusterInformation - -logger = logging.getLogger(__name__) - - -def get_cluster_info(cluster_id: str) -> ClusterInformation: - cc = CloudCluster(cluster_id=cluster_id) - return ClusterInformation(cloud=cc.get_info()) - - -def get_cluster_by_name(cluster_name: str) -> ClusterInformation: - cm = CloudManager() - cluster_list = cm.list_clusters() - for cluster in cluster_list: - if cluster["name"] == cluster_name: - return ClusterInformation(cloud=cluster) - raise CroudException(f"Cluster not found: {cluster_name}") - - -def get_cluster_by_id_or_name(cluster_id: str = None, cluster_name: str = None): - if cluster_id is not None: - return get_cluster_info(cluster_id=cluster_id) - elif cluster_name is not None: - return get_cluster_by_name(cluster_name=cluster_name) - else: - raise ValueError("Failed to address cluster: Either cluster identifier or name needs to be specified") - - -def deploy_cluster(cluster_name: str, subscription_id: str = None, organization_id: str = None) -> ClusterInformation: - cm = CloudManager() - # TODO: Only create new project when needed. Otherwise, use existing project. - project = cm.create_project(name=cluster_name, organization_id=organization_id) - project_id = project["id"] - logger.info(f"Created project: {project_id}") - cluster_info = cm.deploy_cluster(name=cluster_name, project_id=project_id, subscription_id=subscription_id) - return cluster_info diff --git a/cratedb_toolkit/model.py b/cratedb_toolkit/model.py index 964358af..0f00b953 100644 --- a/cratedb_toolkit/model.py +++ b/cratedb_toolkit/model.py @@ -100,19 +100,6 @@ def fullname(self): return f'"{self.table}"' -@dataclasses.dataclass -class ClusterInformation: - """ - Manage a database cluster's information. - """ - - cratedb: t.Any = dataclasses.field(default_factory=dict) - cloud: t.Dict[str, t.Any] = dataclasses.field(default_factory=dict) - - def asdict(self): - return dataclasses.asdict(self) - - @dataclasses.dataclass class InputOutputResource: """ diff --git a/cratedb_toolkit/shell/cli.py b/cratedb_toolkit/shell/cli.py index 779cb308..9d979774 100644 --- a/cratedb_toolkit/shell/cli.py +++ b/cratedb_toolkit/shell/cli.py @@ -1,6 +1,6 @@ import click -from cratedb_toolkit.cluster.util import get_cluster_by_id_or_name +from cratedb_toolkit.cluster.model import ClusterInformation from cratedb_toolkit.common import option_cluster_id, option_cluster_name from cratedb_toolkit.util.cli import boot_click from cratedb_toolkit.util.crash import get_crash_output_formats, run_crash @@ -52,7 +52,7 @@ def cli( """ boot_click(ctx, verbose, debug) - cluster_info = get_cluster_by_id_or_name(cluster_id=cluster_id, cluster_name=cluster_name) + cluster_info = ClusterInformation.from_id_or_name(cluster_id=cluster_id, cluster_name=cluster_name) cratedb_http_url = cluster_info.cloud["url"] run_crash( diff --git a/tests/conftest.py b/tests/conftest.py index 81a8cd8f..a49bd057 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1,6 +1,5 @@ # Copyright (c) 2021-2023, Crate.io Inc. # Distributed under the terms of the AGPLv3 license, see LICENSE. -import os import json import os