1818import com .google .api .core .ApiFunction ;
1919import com .google .api .core .ApiFuture ;
2020import com .google .api .core .ApiFutures ;
21+ import com .google .bigtable .admin .v2 .ClusterName ;
22+ import com .google .bigtable .admin .v2 .DeleteClusterRequest ;
2123import com .google .bigtable .admin .v2 .DeleteInstanceRequest ;
24+ import com .google .bigtable .admin .v2 .GetClusterRequest ;
2225import com .google .bigtable .admin .v2 .GetInstanceRequest ;
2326import com .google .bigtable .admin .v2 .InstanceName ;
27+ import com .google .bigtable .admin .v2 .ListClustersRequest ;
28+ import com .google .bigtable .admin .v2 .ListClustersResponse ;
2429import com .google .bigtable .admin .v2 .ListInstancesRequest ;
2530import com .google .bigtable .admin .v2 .ListInstancesResponse ;
2631import com .google .bigtable .admin .v2 .LocationName ;
2732import com .google .bigtable .admin .v2 .ProjectName ;
33+ import com .google .cloud .bigtable .admin .v2 .models .Cluster ;
34+ import com .google .cloud .bigtable .admin .v2 .models .CreateClusterRequest ;
2835import com .google .cloud .bigtable .admin .v2 .models .CreateInstanceRequest ;
2936import com .google .cloud .bigtable .admin .v2 .models .Instance ;
37+ import com .google .cloud .bigtable .admin .v2 .models .PartialListClustersException ;
3038import com .google .cloud .bigtable .admin .v2 .models .PartialListInstancesException ;
3139import com .google .cloud .bigtable .admin .v2 .models .UpdateInstanceRequest ;
3240import com .google .cloud .bigtable .admin .v2 .stub .BigtableInstanceAdminStub ;
@@ -397,6 +405,282 @@ public Void apply(Empty input) {
397405 );
398406 }
399407
408+ /**
409+ * Creates a new cluster in the specified instance.
410+ *
411+ * <p>Sample code:
412+ *
413+ * <pre>{@code
414+ * Cluster cluster = client.createCluster(
415+ * CreateClusterRequest.of("my-instance", "my-new-cluster")
416+ * .setZone("us-east1-c")
417+ * .setServeNodes(3)
418+ * .setStorageType(StorageType.SSD)
419+ * );
420+ * }</pre>
421+ */
422+ @ SuppressWarnings ("WeakerAccess" )
423+ public Cluster createCluster (CreateClusterRequest request ) {
424+ return awaitFuture (createClusterAsync (request ));
425+ }
426+
427+ /**
428+ * Asynchronously creates a new cluster in the specified instance.
429+ *
430+ * <p>Sample code:
431+ *
432+ * <pre>{@code
433+ * ApiFuture<Cluster> clusterFuture = client.createClusterAsync(
434+ * CreateClusterRequest.of("my-instance", "my-new-cluster")
435+ * .setZone("us-east1-c")
436+ * .setServeNodes(3)
437+ * .setStorageType(StorageType.SSD)
438+ * );
439+ *
440+ * Cluster cluster = clusterFuture.get();
441+ * }</pre>
442+ */
443+ @ SuppressWarnings ("WeakerAccess" )
444+ public ApiFuture <Cluster > createClusterAsync (CreateClusterRequest request ) {
445+ return ApiFutures .transform (
446+ stub .createClusterOperationCallable ().futureCall (request .toProto (projectName )),
447+ new ApiFunction <com .google .bigtable .admin .v2 .Cluster , Cluster >() {
448+ @ Override
449+ public Cluster apply (com .google .bigtable .admin .v2 .Cluster proto ) {
450+ return Cluster .fromProto (proto );
451+ }
452+ },
453+ MoreExecutors .directExecutor ()
454+ );
455+ }
456+
457+ /**
458+ * Get the cluster representation by ID.
459+ *
460+ * <p>Sample code:
461+ *
462+ * <pre>{@code
463+ * Cluster cluster = client.getCluster("my-instance", "my-cluster");
464+ * }</pre>
465+ */
466+ @ SuppressWarnings ("WeakerAccess" )
467+ public Cluster getCluster (String instanceId , String clusterId ) {
468+ return awaitFuture (getClusterAsync (instanceId , clusterId ));
469+ }
470+
471+ /**
472+ * Asynchronously gets the cluster representation by ID.
473+ *
474+ * <p>Sample code:
475+ *
476+ * <pre>{@code
477+ * ApiFuture<Cluster> clusterFuture = client.getClusterAsync("my-instance", "my-cluster");
478+ * Cluster cluster = clusterFuture.get();
479+ * }</pre>
480+ */
481+ @ SuppressWarnings ("WeakerAccess" )
482+ public ApiFuture <Cluster > getClusterAsync (String instanceId , String clusterId ) {
483+ ClusterName name = ClusterName .of (projectName .getProject (), instanceId , clusterId );
484+
485+ GetClusterRequest request = GetClusterRequest .newBuilder ()
486+ .setName (name .toString ())
487+ .build ();
488+
489+ return ApiFutures .transform (
490+ stub .getClusterCallable ().futureCall (request ),
491+ new ApiFunction <com .google .bigtable .admin .v2 .Cluster , Cluster >() {
492+ @ Override
493+ public Cluster apply (com .google .bigtable .admin .v2 .Cluster proto ) {
494+ return Cluster .fromProto (proto );
495+ }
496+ },
497+ MoreExecutors .directExecutor ()
498+ );
499+ }
500+
501+ /**
502+ * Lists all clusters in the specified instance.
503+ *
504+ * <p>This method will throw a {@link PartialListClustersException} when any zone is
505+ * unavailable. If partial listing are ok, the exception can be caught and inspected.
506+ *
507+ * <p>Sample code:
508+ *
509+ * <pre>{@code
510+ * try {
511+ * List<Cluster> clusters = cluster.listClusters("my-instance");
512+ * } catch (PartialListClustersException e) {
513+ * System.out.println("The following zones are unavailable: " + e.getUnavailableZones());
514+ * System.out.println("But the following clusters are reachable: " + e.getClusters())
515+ * }
516+ * }</pre>
517+ */
518+ @ SuppressWarnings ("WeakerAccess" )
519+ public List <Cluster > listClusters (String instanceId ) {
520+ return awaitFuture (listClustersAsync (instanceId ));
521+ }
522+
523+ /**
524+ * Asynchronously lists all clusters in the specified instance.
525+ *
526+ * <p>This method will throw a {@link PartialListClustersException} when any zone is
527+ * unavailable. If partial listing are ok, the exception can be caught and inspected.
528+ *
529+ * <p>Sample code:
530+ *
531+ * <pre>{@code
532+ * ApiFuture<Cluster> clustersFuture = client.listClustersAsync("my-instance");
533+ *
534+ * ApiFutures.addCallback(clustersFuture, new ApiFutureCallback<List<Cluster>>() {
535+ * public void onFailure(Throwable t) {
536+ * if (t instanceof PartialListClustersException) {
537+ * PartialListClustersException partialError = (PartialListClustersException)t;
538+ * System.out.println("The following zones are unavailable: " + partialError.getUnavailableZones());
539+ * System.out.println("But the following clusters are reachable: " + partialError.getClusters());
540+ * } else {
541+ * t.printStackTrace();
542+ * }
543+ * }
544+ *
545+ * public void onSuccess(List<Cluster> result) {
546+ * System.out.println("Found a complete set of instances: " + result);
547+ * }
548+ * }, MoreExecutors.directExecutor());
549+ * }</pre>
550+ */
551+ @ SuppressWarnings ("WeakerAccess" )
552+ public ApiFuture <List <Cluster >> listClustersAsync (String instanceId ) {
553+ InstanceName name = InstanceName .of (projectName .getProject (), instanceId );
554+ ListClustersRequest request = ListClustersRequest .newBuilder ()
555+ .setParent (name .toString ())
556+ .build ();
557+
558+ return ApiFutures .transform (
559+ stub .listClustersCallable ().futureCall (request ),
560+ new ApiFunction <ListClustersResponse , List <Cluster >>() {
561+ @ Override
562+ public List <Cluster > apply (ListClustersResponse proto ) {
563+ // NOTE: serverside pagination is not and will not be implemented, so remaining pages
564+ // are not fetched. However, if that assumption turns out to be wrong, fail fast to
565+ // avoid returning partial data.
566+ Verify .verify (proto .getNextPageToken ().isEmpty (),
567+ "Server returned an unexpected paginated response" );
568+
569+ ImmutableList .Builder <Cluster > clusters = ImmutableList .builder ();
570+ for (com .google .bigtable .admin .v2 .Cluster cluster : proto .getClustersList ()) {
571+ clusters .add (Cluster .fromProto (cluster ));
572+ }
573+
574+ ImmutableList .Builder <String > failedZones = ImmutableList .builder ();
575+ for (String locationStr : proto .getFailedLocationsList ()) {
576+ LocationName fullLocation = Objects .requireNonNull (LocationName .parse (locationStr ));
577+ failedZones .add (fullLocation .getLocation ());
578+ }
579+
580+ if (!failedZones .build ().isEmpty ()) {
581+ throw new PartialListClustersException (failedZones .build (), clusters .build ());
582+ }
583+
584+ return clusters .build ();
585+ }
586+ },
587+ MoreExecutors .directExecutor ()
588+ );
589+ }
590+
591+ /**
592+ * Resizes the cluster's node count. Please note that only clusters that belong to a PRODUCTION
593+ * instance can be resized.
594+ *
595+ * <p>Sample code:
596+ *
597+ * <pre>{@code
598+ * Cluster cluster = cluster.resizeCluster("my-instance", "my-cluster", 30);
599+ * }</pre>
600+ */
601+ @ SuppressWarnings ("WeakerAccess" )
602+ public Cluster resizeCluster (String instanceId , String clusterId , int numServeNodes ) {
603+ return awaitFuture (resizeClusterAsync (instanceId , clusterId , numServeNodes ));
604+ }
605+
606+ /**
607+ * Asynchronously resizes the cluster's node count. Please note that only clusters that belong to
608+ * a PRODUCTION instance can be resized.
609+ *
610+ * <pre>{@code
611+ * ApiFuture<Cluster> clusterFuture = cluster.resizeCluster("my-instance", "my-cluster", 30);
612+ * Cluster cluster = clusterFuture.get();
613+ * }</pre>
614+ */
615+ @ SuppressWarnings ("WeakerAccess" )
616+ public ApiFuture <Cluster > resizeClusterAsync (String instanceId , String clusterId ,
617+ int numServeNodes ) {
618+
619+ ClusterName name = ClusterName .of (projectName .getProject (), instanceId , clusterId );
620+
621+ com .google .bigtable .admin .v2 .Cluster request = com .google .bigtable .admin .v2 .Cluster .newBuilder ()
622+ .setName (name .toString ())
623+ .setServeNodes (numServeNodes )
624+ .build ();
625+
626+ return ApiFutures .transform (
627+ stub .updateClusterOperationCallable ().futureCall (request ),
628+ new ApiFunction <com .google .bigtable .admin .v2 .Cluster , Cluster >() {
629+ @ Override
630+ public Cluster apply (com .google .bigtable .admin .v2 .Cluster proto ) {
631+ return Cluster .fromProto (proto );
632+ }
633+ },
634+ MoreExecutors .directExecutor ()
635+ );
636+ }
637+
638+ /**
639+ * Deletes the specified cluster. Please note that an instance must have at least 1 cluster. To
640+ * remove the last cluster, please use {@link BigtableInstanceAdminClient#deleteInstance(String)}.
641+ *
642+ * <p>Sample code:
643+ *
644+ * <pre>{@code
645+ * client.deleteCluster("my-instance", "my-cluster");
646+ * }</pre>
647+ */
648+ @ SuppressWarnings ("WeakerAccess" )
649+ public void deleteCluster (String instanceId , String clusterId ) {
650+ awaitFuture (deleteClusterAsync (instanceId , clusterId ));
651+ }
652+
653+ /**
654+ * Asynchronously deletes the specified cluster. Please note that an instance must have at least 1
655+ * cluster. To remove the last cluster, please use {@link BigtableInstanceAdminClient#deleteInstanceAsync(String)}.
656+ *
657+ * <p>Sample code:
658+ *
659+ * <pre>{@code
660+ * ApiFuture<Void> future = client.deleteClusterAsync("my-instance", "my-cluster");
661+ * future.get();
662+ * }</pre>
663+ */
664+ @ SuppressWarnings ("WeakerAccess" )
665+ public ApiFuture <Void > deleteClusterAsync (String instanceId , String clusterId ) {
666+ ClusterName name = ClusterName .of (projectName .getProject (), instanceId , clusterId );
667+
668+ DeleteClusterRequest request = DeleteClusterRequest .newBuilder ()
669+ .setName (name .toString ())
670+ .build ();
671+
672+ return ApiFutures .transform (
673+ stub .deleteClusterCallable ().futureCall (request ),
674+ new ApiFunction <Empty , Void >() {
675+ @ Override
676+ public Void apply (Empty input ) {
677+ return null ;
678+ }
679+ },
680+ MoreExecutors .directExecutor ()
681+ );
682+ }
683+
400684 /**
401685 * Awaits the result of a future, taking care to propagate errors while maintaining the call site
402686 * in a suppressed exception. This allows semantic errors to be caught across threads, while
@@ -406,7 +690,6 @@ public Void apply(Empty input) {
406690 // TODO(igorbernstein2): try to move this into gax
407691 private <T > T awaitFuture (ApiFuture <T > future ) {
408692 RuntimeException error ;
409-
410693 try {
411694 return Futures .getUnchecked (future );
412695 } catch (UncheckedExecutionException e ) {
@@ -418,10 +701,8 @@ private <T> T awaitFuture(ApiFuture<T> future) {
418701 } catch (RuntimeException e ) {
419702 error = e ;
420703 }
421-
422704 // Add the caller's stack as a suppressed exception
423705 error .addSuppressed (new RuntimeException ("Encountered error while awaiting future" ));
424-
425706 throw error ;
426707 }
427708}
0 commit comments