Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Empty file.
Original file line number Diff line number Diff line change
@@ -0,0 +1,217 @@
#!/usr/bin/env python

# Copyright 2026 Google LLC
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

"""Demonstrates how to run instance admin operations using BigtableInstanceAdminAsyncClient.

Prerequisites:
- Create a Cloud Bigtable project.
- Set your Google Application Default Credentials.
"""

import argparse
import asyncio


async def run_instance_operations(project_id, instance_id, cluster_id):
# [START bigtable_async_check_instance_exists]
from google.api_core.exceptions import NotFound

from google.cloud import bigtable_admin_v2

async with bigtable_admin_v2.BigtableInstanceAdminAsyncClient() as client:
instance_path = client.instance_path(project_id, instance_id)

try:
instance = await client.get_instance(name=instance_path)
print(f"Instance {instance_id} already exists.")
except NotFound:
print(f"Instance {instance_id} does not exist.")
# [END bigtable_async_check_instance_exists]

# [START bigtable_async_create_prod_instance]
from google.api_core.exceptions import NotFound

from google.cloud import bigtable_admin_v2

async with bigtable_admin_v2.BigtableInstanceAdminAsyncClient() as client:
project_path = client.common_project_path(project_id)
instance_path = client.instance_path(project_id, instance_id)

cluster = bigtable_admin_v2.Cluster(
location=client.common_location_path(project_id, "us-central1-f"),
serve_nodes=1,
default_storage_type=bigtable_admin_v2.StorageType.SSD,
)
instance_obj = bigtable_admin_v2.Instance(
display_name=instance_id,
labels={"prod-label": "prod-label"},
type_=bigtable_admin_v2.Instance.Type.PRODUCTION,
)

try:
await client.get_instance(name=instance_path)
except NotFound:
print("\nCreating an instance")
# Create instance with given options
operation = await client.create_instance(
parent=project_path,
instance_id=instance_id,
instance=instance_obj,
clusters={cluster_id: cluster},
)
# Ensure the operation completes.
await operation.result(timeout=480)
print(f"\nCreated instance: {instance_id}")
# [END bigtable_async_create_prod_instance]

# [START bigtable_async_list_instances]
from google.cloud import bigtable_admin_v2

async with bigtable_admin_v2.BigtableInstanceAdminAsyncClient() as client:
project_path = client.common_project_path(project_id)

print("\nListing instances:")
instances_response = await client.list_instances(parent=project_path)
for instance in instances_response.instances:
print(instance.name.split("/")[-1])
# [END bigtable_async_list_instances]

# [START bigtable_async_get_instance]
from google.cloud import bigtable_admin_v2

async with bigtable_admin_v2.BigtableInstanceAdminAsyncClient() as client:
instance_path = client.instance_path(project_id, instance_id)

inst = await client.get_instance(name=instance_path)
print(f"\nName of instance: {inst.display_name}\nLabels: {dict(inst.labels)}")
# [END bigtable_async_get_instance]

# [START bigtable_async_get_clusters]
from google.cloud import bigtable_admin_v2

async with bigtable_admin_v2.BigtableInstanceAdminAsyncClient() as client:
instance_path = client.instance_path(project_id, instance_id)

print("\nListing clusters...")
clusters_response = await client.list_clusters(parent=instance_path)
for cluster in clusters_response.clusters:
print(cluster.name.split("/")[-1])
Comment on lines +108 to +110

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

The list_clusters method returns an ListClustersAsyncPager (an async pager), which does not have a .clusters attribute. Attempting to access .clusters directly on the pager will raise an AttributeError. Instead, you should iterate over the async pager directly using async for.

Suggested change
clusters_response = await client.list_clusters(parent=instance_path)
for cluster in clusters_response.clusters:
print(cluster.name.split("/")[-1])
async for cluster in await client.list_clusters(parent=instance_path):
print(cluster.name.split("/")[-1])

# [END bigtable_async_get_clusters]


async def delete_instance(project_id, instance_id):
# [START bigtable_async_delete_instance]
from google.api_core.exceptions import NotFound

from google.cloud import bigtable_admin_v2

async with bigtable_admin_v2.BigtableInstanceAdminAsyncClient() as client:
instance_path = client.instance_path(project_id, instance_id)

print("\nDeleting instance")
try:
await client.delete_instance(name=instance_path)
print(f"Deleted instance: {instance_id}")
except NotFound:
print(f"Instance {instance_id} does not exist.")
# [END bigtable_async_delete_instance]


async def add_cluster(project_id, instance_id, cluster_id):
# [START bigtable_async_create_cluster]
from google.api_core.exceptions import NotFound

from google.cloud import bigtable_admin_v2

async with bigtable_admin_v2.BigtableInstanceAdminAsyncClient() as client:
instance_path = client.instance_path(project_id, instance_id)
cluster_path = client.cluster_path(project_id, instance_id, cluster_id)

print("\nListing clusters...")
clusters_response = await client.list_clusters(parent=instance_path)
for cluster in clusters_response.clusters:
print(cluster.name.split("/")[-1])
Comment on lines +143 to +145

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

The list_clusters method returns an ListClustersAsyncPager (an async pager), which does not have a .clusters attribute. Attempting to access .clusters directly on the pager will raise an AttributeError. Instead, you should iterate over the async pager directly using async for.

Suggested change
clusters_response = await client.list_clusters(parent=instance_path)
for cluster in clusters_response.clusters:
print(cluster.name.split("/")[-1])
async for cluster in await client.list_clusters(parent=instance_path):
print(cluster.name.split("/")[-1])


new_cluster = bigtable_admin_v2.Cluster(
location=client.common_location_path(project_id, "us-central1-a"),
serve_nodes=1,
default_storage_type=bigtable_admin_v2.StorageType.SSD,
)
try:
await client.get_cluster(name=cluster_path)
print(f"\nCluster not created, as {cluster_id} already exists.")
except NotFound:
operation = await client.create_cluster(
parent=instance_path, cluster_id=cluster_id, cluster=new_cluster
)
# Ensure the operation completes.
await operation.result(timeout=480)
print(f"\nCluster created: {cluster_id}")
# [END bigtable_async_create_cluster]


async def delete_cluster(project_id, instance_id, cluster_id):
"""Delete the cluster

:type project_id: str
:param project_id: Project id of the client.

:type instance_id: str
:param instance_id: Instance of the client.

:type cluster_id: str
:param cluster_id: Cluster id.
"""
# [START bigtable_async_delete_cluster]
from google.api_core.exceptions import NotFound

from google.cloud import bigtable_admin_v2

async with bigtable_admin_v2.BigtableInstanceAdminAsyncClient() as client:
cluster_path = client.cluster_path(project_id, instance_id, cluster_id)

print("\nDeleting cluster")
try:
await client.delete_cluster(name=cluster_path)
print(f"Cluster deleted: {cluster_id}")
except NotFound:
print(f"\nCluster {cluster_id} does not exist.")
# [END bigtable_async_delete_cluster]


if __name__ == "__main__":
parser = argparse.ArgumentParser(
description=__doc__, formatter_class=argparse.ArgumentDefaultsHelpFormatter
)
parser.add_argument(
"command",
help="run, del-instance, add-cluster or del-cluster.",
)
parser.add_argument("project_id", help="Your Cloud Platform project ID.")
parser.add_argument("instance_id", help="ID of the Cloud Bigtable instance.")
parser.add_argument("cluster_id", help="ID of the Cloud Bigtable cluster.")

args = parser.parse_args()

if args.command.lower() == "run":
asyncio.run(
run_instance_operations(args.project_id, args.instance_id, args.cluster_id)
)
elif args.command.lower() == "del-instance":
asyncio.run(delete_instance(args.project_id, args.instance_id))
elif args.command.lower() == "add-cluster":
asyncio.run(add_cluster(args.project_id, args.instance_id, args.cluster_id))
elif args.command.lower() == "del-cluster":
asyncio.run(delete_cluster(args.project_id, args.instance_id, args.cluster_id))
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
google-cloud-bigtable>=2.35.0
backoff==2.2.1
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
# Copyright 2026 Google LLC
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

import os
import uuid

import backoff
import pytest
from google.api_core import exceptions

from .instanceadmin_async import (
add_cluster,
delete_cluster,
delete_instance,
run_instance_operations,
)

PROJECT = os.environ["GOOGLE_CLOUD_PROJECT"]
INSTANCE_ID = f"instance-admin-async-{str(uuid.uuid4())[:8]}"
CLUSTER_ID = f"cluster-admin-async-{str(uuid.uuid4())[:8]}"
NEW_CLUSTER_ID = f"cluster-add-async-{str(uuid.uuid4())[:8]}"


@pytest.mark.asyncio
async def test_instance_operations(capsys):
try:
await run_instance_operations(PROJECT, INSTANCE_ID, CLUSTER_ID)
out, _ = capsys.readouterr()
assert f"Created instance: {INSTANCE_ID}" in out
assert "Listing instances:" in out
assert "Listing clusters..." in out

backoff_503 = backoff.on_exception(backoff.expo, exceptions.ServiceUnavailable)
await backoff_503(add_cluster)(PROJECT, INSTANCE_ID, NEW_CLUSTER_ID)
out, _ = capsys.readouterr()
assert f"Cluster created: {NEW_CLUSTER_ID}" in out

await delete_cluster(PROJECT, INSTANCE_ID, NEW_CLUSTER_ID)
out, _ = capsys.readouterr()
assert f"Cluster deleted: {NEW_CLUSTER_ID}" in out

await delete_instance(PROJECT, INSTANCE_ID)
out, _ = capsys.readouterr()
assert f"Deleted instance: {INSTANCE_ID}" in out
finally:
await delete_instance(PROJECT, INSTANCE_ID)
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
google-cloud-bigtable>=2.35.0
Loading
Loading