Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: support customer managed instance configurations #1742

Merged
merged 56 commits into from Sep 29, 2022
Merged
Show file tree
Hide file tree
Changes from 45 commits
Commits
Show all changes
56 commits
Select commit Hold shift + click to select a range
d89cd50
feat: implementation changes to add support for
rajatbhatta Mar 24, 2022
4177d63
test: add more unit tests to Instance admin client
rajatbhatta Mar 24, 2022
a669c67
feat: add samples for create, update and deleting
rajatbhatta Mar 24, 2022
ec44bd5
chore: add test instance config to samples pom
rajatbhatta Mar 24, 2022
7fa5ef4
test: add CUD instance config integration tests
rajatbhatta Mar 24, 2022
83e1f25
feat: add proto file as reference.
rajatbhatta Mar 24, 2022
a4c1178
feat: lint the code changes, add documentation
rajatbhatta Mar 24, 2022
3ec6854
feat: minor alignment changes
rajatbhatta Mar 24, 2022
62f6988
feat: fix checkstyle violations.
rajatbhatta Mar 24, 2022
05e491b
feat: change read-only fields setters to protected.
rajatbhatta Mar 25, 2022
b09cec7
feat: change setConfigType to protected.
rajatbhatta Mar 25, 2022
2e957b5
feat: change some more method access modifiers
rajatbhatta Mar 25, 2022
813e5d5
feat: add optional fields, some design changes.
rajatbhatta Mar 30, 2022
9ed7fa2
feat: some documentation changes
rajatbhatta Mar 30, 2022
a3395ec
Merge branch 'main' into cmmr#2
rajatbhatta Mar 30, 2022
5c34085
feat: change BASE_CONFIG project name in tests.
rajatbhatta Mar 31, 2022
dd86710
feat: pom.xml changes
rajatbhatta Mar 31, 2022
9e940e7
Merge branch 'cmmr#2' of https://github.com/rajatbhatta/java-spanner …
rajatbhatta Mar 31, 2022
0a248f9
feat: add support for adding readonly replicas
rajatbhatta Mar 31, 2022
61a2048
feat: clirr changes
rajatbhatta Mar 31, 2022
1922766
feat: some refactoring
rajatbhatta Mar 31, 2022
1a1a557
feat: some method doc changes.
rajatbhatta Mar 31, 2022
46861cf
feat: incorporate review comments.
rajatbhatta Apr 1, 2022
a85e455
feat: remove samples
rajatbhatta Apr 1, 2022
ab903e7
Update pom.xml
rajatbhatta Apr 1, 2022
b76eb57
Update SampleIdGenerator.java
rajatbhatta Apr 1, 2022
614822e
Update SampleRunner.java
rajatbhatta Apr 1, 2022
c9f11ef
Update SampleTestBase.java
rajatbhatta Apr 1, 2022
8c216fd
feat: changes to InstanceConfigTest
rajatbhatta Apr 1, 2022
aa924a5
feat: changes to pom.xml
rajatbhatta Apr 1, 2022
cf72700
Update google-cloud-spanner/src/main/java/com/google/cloud/spanner/In…
rajatbhatta Apr 1, 2022
9b2eabb
Update google-cloud-spanner/src/main/java/com/google/cloud/spanner/In…
rajatbhatta Apr 1, 2022
1696035
Update google-cloud-spanner/src/main/java/com/google/cloud/spanner/In…
rajatbhatta Apr 1, 2022
e3ecd86
Update google-cloud-spanner/src/main/java/com/google/cloud/spanner/In…
rajatbhatta Apr 1, 2022
06e754c
Update google-cloud-spanner/src/main/java/com/google/cloud/spanner/In…
rajatbhatta Apr 1, 2022
4c244dc
Update google-cloud-spanner/src/main/java/com/google/cloud/spanner/In…
rajatbhatta Apr 1, 2022
ee58890
feat: make setBaseConfig protected.
rajatbhatta Apr 1, 2022
ef66169
Merge branch 'cmmr#2' of https://github.com/rajatbhatta/java-spanner …
rajatbhatta Apr 1, 2022
31c615c
feat: add method doc for addReadOnlyReplicas
rajatbhatta Apr 1, 2022
8753739
feat: incorporate review comments.
rajatbhatta Apr 4, 2022
c17ce53
feat: deprecate few constructors
rajatbhatta Apr 4, 2022
721d24c
feat: make some methods private
rajatbhatta Apr 4, 2022
1d23171
feat: move initialization to property declaration.
rajatbhatta Apr 4, 2022
7c0fbb7
feat: make InstanceConfig.Builder setters return
rajatbhatta Apr 4, 2022
3281950
feat: some doc changes
rajatbhatta Apr 4, 2022
bbb4ba9
feat: add support for ListInstanceConfigOperations
rajatbhatta May 2, 2022
96ecca3
fix: linting
rajatbhatta May 2, 2022
98d3a8c
feat: few changes
rajatbhatta May 30, 2022
2e6607b
Merge branch 'main' into cmmr#2
rajatbhatta Sep 19, 2022
1d27cd7
Update spanner_instance_admin.proto
rajatbhatta Sep 19, 2022
6029581
Update SpannerRpc.java
rajatbhatta Sep 19, 2022
503a9ac
feat: remove unnecessary parameter from
rajatbhatta Sep 21, 2022
6f73619
feat: clirr fix
rajatbhatta Sep 21, 2022
71d3e4c
🦉 Updates from OwlBot post-processor
gcf-owl-bot[bot] Sep 21, 2022
f7d0710
feat: revert unintended sample changes
rajatbhatta Sep 21, 2022
8bfb5ad
Merge branch 'main' into cmmr#2
rajatbhatta Sep 28, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
30 changes: 30 additions & 0 deletions google-cloud-spanner/clirr-ignored-differences.xml
Expand Up @@ -35,6 +35,36 @@
<className>com/google/cloud/spanner/connection/ConnectionOptions</className>
<method>com.google.cloud.spanner.Dialect getDialect()</method>
</difference>
<difference>
<differenceType>7012</differenceType>
<className>com/google/cloud/spanner/InstanceAdminClient</className>
<method>com.google.api.gax.longrunning.OperationFuture createInstanceConfig(com.google.cloud.spanner.InstanceConfigInfo, com.google.cloud.spanner.Options$CreateAdminApiOption[])</method>
</difference>
<difference>
<differenceType>7012</differenceType>
<className>com/google/cloud/spanner/InstanceAdminClient</className>
<method>com.google.api.gax.longrunning.OperationFuture updateInstanceConfig(com.google.cloud.spanner.InstanceConfigInfo, java.lang.Iterable, com.google.cloud.spanner.Options$UpdateAdminApiOption[])</method>
</difference>
<difference>
<differenceType>7012</differenceType>
<className>com/google/cloud/spanner/InstanceAdminClient</className>
<method>void deleteInstanceConfig(java.lang.String, com.google.cloud.spanner.Options$DeleteAdminApiOption[])</method>
</difference>
<difference>
<differenceType>7012</differenceType>
<className>com/google/cloud/spanner/spi/v1/SpannerRpc</className>
<method>com.google.api.gax.longrunning.OperationFuture createInstanceConfig(java.lang.String, java.lang.String, com.google.spanner.admin.instance.v1.InstanceConfig, java.lang.Boolean)</method>
</difference>
<difference>
<differenceType>7012</differenceType>
<className>com/google/cloud/spanner/spi/v1/SpannerRpc</className>
<method>com.google.api.gax.longrunning.OperationFuture updateInstanceConfig(com.google.spanner.admin.instance.v1.InstanceConfig, java.lang.Boolean, com.google.protobuf.FieldMask)</method>
</difference>
<difference>
<differenceType>7012</differenceType>
<className>com/google/cloud/spanner/spi/v1/SpannerRpc</className>
<method>void deleteInstanceConfig(java.lang.String, java.lang.String, java.lang.Boolean)</method>
</difference>
<difference>
<differenceType>7013</differenceType>
<className>com/google/cloud/spanner/BackupInfo$Builder</className>
Expand Down
Expand Up @@ -19,14 +19,140 @@
import com.google.api.gax.longrunning.OperationFuture;
import com.google.api.gax.paging.Page;
import com.google.cloud.Policy;
import com.google.cloud.spanner.Options.CreateAdminApiOption;
import com.google.cloud.spanner.Options.DeleteAdminApiOption;
import com.google.cloud.spanner.Options.ListOption;
import com.google.cloud.spanner.Options.UpdateAdminApiOption;
import com.google.longrunning.Operation;
import com.google.spanner.admin.instance.v1.CreateInstanceConfigMetadata;
import com.google.spanner.admin.instance.v1.CreateInstanceMetadata;
import com.google.spanner.admin.instance.v1.UpdateInstanceConfigMetadata;
import com.google.spanner.admin.instance.v1.UpdateInstanceMetadata;

/** Client to do admin operations on Cloud Spanner Instance and Instance Configs. */
public interface InstanceAdminClient {

/**
* Creates an instance config and begins preparing it to be used. The returned {@code Operation}
* can be used to track the progress of preparing the new instance config. The instance config
* name is assigned by the caller and must start with the string 'custom'. If the named instance config already exists, a SpannerException
* is thrown.
*
* <p>Immediately after the request returns:
*
* <ul>
* <li>The instance config is readable via the API, with all requested attributes.
* <li>The instance config's {@code reconciling} field is set to true. Its state is {@code
* CREATING}.
* </ul>
*
* While the operation is pending:
*
* <ul>
* <li>Cancelling the operation renders the instance config immediately unreadable via the API.
* <li>Except for deleting the creating resource, all other attempts to modify the instance
* config are rejected.
* </ul>
*
* Upon completion of the returned operation:
*
* <ul>
* <li>Instances can be created using the instance configuration.
* <li>The instance config's {@code reconciling} field becomes false.
* <li>Its state becomes {@code READY}.
* </ul>
*
* <!--SNIPPET instance_admin_client_create_instance_config-->
*
* <pre>{@code
* String projectId = "my-project";
* String baseInstanceConfig = "my-base-config";
* String instanceConfigId = "custom-user-config";
*
* final InstanceConfig baseConfig = instanceAdminClient.getInstanceConfig(baseInstanceConfig);
*
* List<ReplicaInfo> readOnlyReplicas = ImmutableList.of(baseConfig.getOptionalReplicas().get(0));
*
* InstanceConfigInfo instanceConfigInfo =
* InstanceConfigInfo.newBuilder(InstanceConfigId.of(projectId, instanceConfigId), baseConfig)
* .setDisplayName(instanceConfigId)
* .addReadOnlyReplicas(readOnlyReplicas)
* .build();
*
* final OperationFuture<InstanceConfig, CreateInstanceConfigMetadata> operation =
* instanceAdminClient.createInstanceConfig(instanceConfigInfo);
*
* InstanceConfig instanceConfig = op.get(5, TimeUnit.MINUTES)
* }</pre>
*
* <!--SNIPPET instance_admin_client_create_instance_config-->
*/
default OperationFuture<InstanceConfig, CreateInstanceConfigMetadata> createInstanceConfig(
InstanceConfigInfo instanceConfig, CreateAdminApiOption... options) throws SpannerException {
throw new UnsupportedOperationException("Not implemented");
}

/**
* Updates a custom instance config. This can not be used to update a Google managed instance config. The returned {@code Operation} can be used to track the progress of
* updating the instance. If the named instance config does not exist, a SpannerException is
* thrown. The request must include at least one field to update.
*
* <p>Only user managed configurations can be updated.
*
* <p>Immediately after the request returns:
*
* <ul>
* <li>The instance config's {@code reconciling} field is set to true.
* </ul>
*
* While the operation is pending:
*
* <ul>
* <li>Cancelling the operation sets its metadata's cancel_time.
* <li>The operation is guaranteed to succeed at undoing all changes, after which point it
* terminates with a `CANCELLED` status.
* <li>All other attempts to modify the instance config are rejected.
* <li>Reading the instance config via the API continues to give the pre-request values.
* </ul>
*
* Upon completion of the returned operation:
*
* <ul>
* <li>Creating instances using the instance configuration uses the new values.
* <li>The instance config's new values are readable via the API.
* <li>The instance config's {@code reconciling} field becomes false.
* </ul>
*
* <!--SNIPPET instance_admin_client_update_instance_config-->
*
* <pre>{@code
* String projectId = "my-project";
* String instanceConfigId = "custom-user-config";
* String displayName = "my-display-name";
*
* InstanceConfigInfo instanceConfigInfo =
* InstanceConfigInfo.newBuilder(InstanceConfigId.of(projectId, instanceConfigId))
* .setDisplayName(displayName)
* .build();
*
* // Only update display name.
* final OperationFuture<InstanceConfig, UpdateInstanceConfigMetadata> operation =
* instanceAdminClient.updateInstanceConfig(
* instanceConfigInfo, ImmutableList.of(InstanceConfigField.DISPLAY_NAME));
*
* InstanceConfig instanceConfig = operation.get(5, TimeUnit.MINUTES);
rajatbhatta marked this conversation as resolved.
Show resolved Hide resolved
* }</pre>
*
* <!--SNIPPET instance_admin_client_update_instance_config-->
*/
default OperationFuture<InstanceConfig, UpdateInstanceConfigMetadata> updateInstanceConfig(
InstanceConfigInfo instanceConfig,
Iterable<InstanceConfigInfo.InstanceConfigField> fieldsToUpdate,
UpdateAdminApiOption... options)
throws SpannerException {
throw new UnsupportedOperationException("Not implemented");
}

/** Gets an instance config. */
/* <!--SNIPPET instance_admin_client_get_instance_config-->
* <pre>{@code
Expand All @@ -37,6 +163,27 @@ public interface InstanceAdminClient {
*/
InstanceConfig getInstanceConfig(String configId) throws SpannerException;

/**
* Deletes a custom instance config. Deletion is only allowed for custom instance configs and when no instances are using the
* configuration. If any instances are using the config, a SpannerException is thrown.
*
* <p>Only user managed configurations can be deleted.
* <!--SNIPPET instance_admin_client_delete_instance_config-->
*
* <pre>{@code
* String projectId = "my-project";
* String instanceConfigId = "custom-user-config";
*
* instanceAdminClient.deleteInstanceConfig(instanceConfigId);
* }</pre>
*
* <!--SNIPPET instance_admin_client_delete_instance_config-->
*/
default void deleteInstanceConfig(String instanceConfigId, DeleteAdminApiOption... options)
throws SpannerException {
throw new UnsupportedOperationException("Not implemented");
}

/** Lists the supported instance configs for current project. */
/* <!--SNIPPET instance_admin_client_list_configs-->
* <pre>{@code
Expand Down
Expand Up @@ -23,14 +23,19 @@
import com.google.api.pathtemplate.PathTemplate;
import com.google.cloud.Policy;
import com.google.cloud.Policy.DefaultMarshaller;
import com.google.cloud.spanner.Options.CreateAdminApiOption;
import com.google.cloud.spanner.Options.DeleteAdminApiOption;
import com.google.cloud.spanner.Options.ListOption;
import com.google.cloud.spanner.Options.UpdateAdminApiOption;
import com.google.cloud.spanner.SpannerImpl.PageFetcher;
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.CreateInstanceConfigMetadata;
import com.google.spanner.admin.instance.v1.CreateInstanceMetadata;
import com.google.spanner.admin.instance.v1.UpdateInstanceConfigMetadata;
import com.google.spanner.admin.instance.v1.UpdateInstanceMetadata;

/** Default implementation of {@link InstanceAdminClient} */
Expand Down Expand Up @@ -60,13 +65,81 @@ protected com.google.iam.v1.Policy toPb(Policy policy) {
this.dbClient = dbClient;
}

@Override
public OperationFuture<InstanceConfig, CreateInstanceConfigMetadata> createInstanceConfig(
InstanceConfigInfo instanceConfig, CreateAdminApiOption... options) throws SpannerException {
final Options createAdminApiOptions = Options.fromAdminApiOptions(options);
String projectName = PROJECT_NAME_TEMPLATE.instantiate("project", projectId);
OperationFuture<
com.google.spanner.admin.instance.v1.InstanceConfig, CreateInstanceConfigMetadata>
rawOperationFuture =
rpc.createInstanceConfig(
projectName,
instanceConfig.getId().getInstanceConfig(),
instanceConfig.toProto(),
createAdminApiOptions.validateOnly());

return new OperationFutureImpl<>(
rawOperationFuture.getPollingFuture(),
rawOperationFuture.getInitialFuture(),
snapshot ->
InstanceConfig.fromProto(
ProtoOperationTransformers.ResponseTransformer.create(
com.google.spanner.admin.instance.v1.InstanceConfig.class)
.apply(snapshot),
InstanceAdminClientImpl.this),
ProtoOperationTransformers.MetadataTransformer.create(CreateInstanceConfigMetadata.class),
e -> {
throw SpannerExceptionFactory.newSpannerException(e);
});
}

@Override
public OperationFuture<InstanceConfig, UpdateInstanceConfigMetadata> updateInstanceConfig(
InstanceConfigInfo instanceConfig,
Iterable<InstanceConfigInfo.InstanceConfigField> fieldsToUpdate,
UpdateAdminApiOption... options)
throws SpannerException {
final Options deleteAdminApiOptions = Options.fromAdminApiOptions(options);
FieldMask fieldMask = InstanceConfigInfo.InstanceConfigField.toFieldMask(fieldsToUpdate);

OperationFuture<
com.google.spanner.admin.instance.v1.InstanceConfig, UpdateInstanceConfigMetadata>
rawOperationFuture =
rpc.updateInstanceConfig(
instanceConfig.toProto(), deleteAdminApiOptions.validateOnly(), fieldMask);
return new OperationFutureImpl<>(
rawOperationFuture.getPollingFuture(),
rawOperationFuture.getInitialFuture(),
snapshot ->
InstanceConfig.fromProto(
ProtoOperationTransformers.ResponseTransformer.create(
com.google.spanner.admin.instance.v1.InstanceConfig.class)
.apply(snapshot),
InstanceAdminClientImpl.this),
ProtoOperationTransformers.MetadataTransformer.create(UpdateInstanceConfigMetadata.class),
e -> {
throw SpannerExceptionFactory.newSpannerException(e);
});
}

@Override
public InstanceConfig getInstanceConfig(String configId) throws SpannerException {
String instanceConfigName = new InstanceConfigId(projectId, configId).getName();
return InstanceConfig.fromProto(
rpc.getInstanceConfig(instanceConfigName), InstanceAdminClientImpl.this);
}

@Override
public void deleteInstanceConfig(final String instanceConfigId, DeleteAdminApiOption... options)
throws SpannerException {
final Options deleteAdminApiOptions = Options.fromAdminApiOptions(options);
rpc.deleteInstanceConfig(
new InstanceConfigId(projectId, instanceConfigId).getName(),
deleteAdminApiOptions.etag(),
deleteAdminApiOptions.validateOnly());
}

@Override
public Page<InstanceConfig> listInstanceConfigs(ListOption... options) {
final Options listOptions = Options.fromListOptions(options);
Expand Down