From 8b0e511ab1319455c2f8316097427ee4c1c673a9 Mon Sep 17 00:00:00 2001 From: El De-dog-lo <3859395+fubuloubu@users.noreply.github.com> Date: Fri, 14 Feb 2025 14:55:25 -0500 Subject: [PATCH] feat(cluster): add cluster migration command [Platform only] (#199) --- silverback/_cli.py | 41 ++++++++++++++++++++++++++++++++++++ silverback/cluster/client.py | 20 +++++++++++++++++- 2 files changed, 60 insertions(+), 1 deletion(-) diff --git a/silverback/_cli.py b/silverback/_cli.py index 9d361bdc..c142d6b1 100644 --- a/silverback/_cli.py +++ b/silverback/_cli.py @@ -448,6 +448,47 @@ def update_cluster( click.echo(f"{click.style('SUCCESS', fg='green')}: Updated '{updated_cluster.name}'") +@cluster.command(name="migrate", section="Platform Commands (https://silverback.apeworx.io)") +@click.option("--version", default=None) +@click.argument("cluster_path") +@platform_client +def migrate_cluster( + platform: "PlatformClient", + cluster_path: str, + version: str | None, +): + """Migrate CLUSTER running software version to VERSION""" + if "/" not in cluster_path or len(cluster_path.split("/")) > 2: + raise click.BadArgumentUsage(f"Invalid cluster path: '{cluster_path}'") + + workspace_name, cluster_name = cluster_path.split("/") + if not (workspace_client := platform.workspaces.get(workspace_name)): + raise click.BadArgumentUsage(f"Unknown workspace: '{workspace_name}'") + + elif not (cluster := workspace_client.clusters.get(cluster_name)): + raise click.BadArgumentUsage( + f"Unknown cluster in workspace '{workspace_name}': '{cluster_name}'" + ) + + elif version and version not in (available_versions := workspace_client.available_versions): + available_versions_str = "', '".join(available_versions) + raise click.BadOptionUsage( + "version", + f"Cannot migrate to version '{version}', must be one of: '{available_versions_str}'", + ) + + click.echo( + f"{click.style('INFO', fg='blue')}: " + f"Migrating '{cluster_path}' from '{cluster.version}' to '{version or 'stable'}'" + ) + workspace_client.migrate_cluster(str(cluster.id), version=version) + click.echo(f"{click.style('SUCCESS', fg='green')}: Migration of '{cluster.name}' started") + click.echo( + f"{click.style('INFO', fg='blue')}: " + "This may take a couple of minutes, check `silverback cluster info` for version change" + ) + + @cluster.group(cls=SectionedHelpGroup, section="Platform Commands (https://silverback.apeworx.io)") def pay(): """Pay for CLUSTER with Crypto using ApePay streaming payments""" diff --git a/silverback/cluster/client.py b/silverback/cluster/client.py index ac2a8d5d..a250dfa0 100644 --- a/silverback/cluster/client.py +++ b/silverback/cluster/client.py @@ -392,12 +392,30 @@ def update_cluster( data["slug"] = slug response = self.client.patch( f"/clusters/{cluster_id}", - params=dict(workspace=str(self.id), cluster_id=cluster_id), + params=dict(workspace=str(self.id)), data=data, ) handle_error_with_response(response) return ClusterInfo.model_validate(response.json()) + @property + def available_versions(self) -> list[str]: + response = self.client.get("/versions") + handle_error_with_response(response) + return response.json() + + def migrate_cluster(self, cluster_id: str, version: str | None = None): + data = dict() + if version: + data["version"] = version + + response = self.client.put( + f"/clusters/{cluster_id}", + params=dict(workspace=str(self.id)), + data=data, + ) + handle_error_with_response(response) + def get_payment_stream(self, cluster: ClusterInfo, chain_id: int) -> Stream | None: response = self.client.get( f"/clusters/{cluster.id}/stream",