From ff571e16a45fbce692d9bb172749ff15fafe7a9c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Knut=20Olav=20L=C3=B8ite?= Date: Wed, 22 Apr 2020 07:33:27 +0200 Subject: [PATCH] fix: clean up test instance if creation failed (#162) --- .../clirr-ignored-differences.xml | 12 +++++++ .../cloud/spanner/InstanceAdminClient.java | 7 ++++ .../spanner/InstanceAdminClientImpl.java | 11 ++++++ .../cloud/spanner/IntegrationTestEnv.java | 35 ++++++++++++++++++- 4 files changed, 64 insertions(+), 1 deletion(-) diff --git a/google-cloud-spanner/clirr-ignored-differences.xml b/google-cloud-spanner/clirr-ignored-differences.xml index a8afa4b642..5faa1d6515 100644 --- a/google-cloud-spanner/clirr-ignored-differences.xml +++ b/google-cloud-spanner/clirr-ignored-differences.xml @@ -147,4 +147,16 @@ com.google.api.gax.paging.Page listDatabases() + + + 7012 + com/google/cloud/spanner/InstanceAdminClient + void cancelOperation(java.lang.String) + + + 7012 + com/google/cloud/spanner/InstanceAdminClient + com.google.longrunning.Operation getOperation(java.lang.String) + + diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/InstanceAdminClient.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/InstanceAdminClient.java index 093f4df171..a84bf04aac 100644 --- a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/InstanceAdminClient.java +++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/InstanceAdminClient.java @@ -20,6 +20,7 @@ import com.google.api.gax.paging.Page; import com.google.cloud.Policy; import com.google.cloud.spanner.Options.ListOption; +import com.google.longrunning.Operation; import com.google.spanner.admin.instance.v1.CreateInstanceMetadata; import com.google.spanner.admin.instance.v1.UpdateInstanceMetadata; @@ -217,4 +218,10 @@ OperationFuture updateInstance( /** Returns a builder for {@code Instance} object with the given id. */ Instance.Builder newInstanceBuilder(InstanceId id); + + /** Cancels the specified long-running operation. */ + void cancelOperation(String name); + + /** Gets the specified long-running operation. */ + Operation getOperation(String name); } diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/InstanceAdminClientImpl.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/InstanceAdminClientImpl.java index 52e0639b2d..0bbaa15072 100644 --- a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/InstanceAdminClientImpl.java +++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/InstanceAdminClientImpl.java @@ -30,6 +30,7 @@ import com.google.cloud.spanner.spi.v1.SpannerRpc; import com.google.cloud.spanner.spi.v1.SpannerRpc.Paginated; import com.google.common.base.Preconditions; +import com.google.longrunning.Operation; import com.google.protobuf.FieldMask; import com.google.spanner.admin.instance.v1.CreateInstanceMetadata; import com.google.spanner.admin.instance.v1.UpdateInstanceMetadata; @@ -220,4 +221,14 @@ public Iterable testInstanceIAMPermissions( public Instance.Builder newInstanceBuilder(InstanceId id) { return new Instance.Builder(this, dbClient, id); } + + @Override + public void cancelOperation(String name) { + rpc.cancelOperation(Preconditions.checkNotNull(name)); + } + + @Override + public Operation getOperation(String name) { + return rpc.getOperation(Preconditions.checkNotNull(name)); + } } diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/IntegrationTestEnv.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/IntegrationTestEnv.java index 0aeb74b92f..f643c6021e 100644 --- a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/IntegrationTestEnv.java +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/IntegrationTestEnv.java @@ -22,6 +22,8 @@ import com.google.cloud.spanner.testing.RemoteSpannerHelper; import com.google.common.collect.Iterators; import com.google.spanner.admin.instance.v1.CreateInstanceMetadata; +import io.grpc.Status; +import java.util.Random; import java.util.concurrent.TimeUnit; import java.util.logging.Level; import java.util.logging.Logger; @@ -82,7 +84,10 @@ protected void before() throws Throwable { isOwnedInstance = false; logger.log(Level.INFO, "Using existing test instance: {0}", instanceId); } else { - instanceId = InstanceId.of(config.spannerOptions().getProjectId(), "test-instance"); + instanceId = + InstanceId.of( + config.spannerOptions().getProjectId(), + String.format("test-instance-%08d", new Random().nextInt(100000000))); isOwnedInstance = true; } testHelper = createTestHelper(options, instanceId); @@ -123,6 +128,34 @@ private void initializeInstance(InstanceId instanceId) { try { createdInstance = op.get(30000L, TimeUnit.MILLISECONDS); } catch (Exception e) { + boolean cancelled = false; + try { + // Try to cancel the createInstance operation. + instanceAdminClient.cancelOperation(op.getName()); + com.google.longrunning.Operation createOperation = + instanceAdminClient.getOperation(op.getName()); + cancelled = + createOperation.hasError() + && createOperation.getError().getCode() == Status.CANCELLED.getCode().value(); + if (cancelled) { + logger.info("Cancelled the createInstance operation because the operation failed"); + } else { + logger.info( + "Tried to cancel the createInstance operation because the operation failed, but the operation could not be cancelled. Current status: " + + createOperation.getError().getCode()); + } + } catch (Throwable t) { + logger.log(Level.WARNING, "Failed to cancel the createInstance operation", t); + } + if (!cancelled) { + try { + instanceAdminClient.deleteInstance(instanceId.getInstance()); + logger.info( + "Deleted the test instance because the createInstance operation failed and cancelling the operation did not succeed"); + } catch (Throwable t) { + logger.log(Level.WARNING, "Failed to delete the test instance", t); + } + } throw SpannerExceptionFactory.newSpannerException(e); } logger.log(Level.INFO, "Created test instance: {0}", createdInstance.getId());