com.google.cloud.spanner.DatabaseAdminClientImpl Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of google-cloud-spanner Show documentation
Show all versions of google-cloud-spanner Show documentation
Java idiomatic client for Google Cloud Spanner.
/*
* Copyright 2019 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.
*/
package com.google.cloud.spanner;
import com.google.api.gax.grpc.ProtoOperationTransformers;
import com.google.api.gax.longrunning.OperationFuture;
import com.google.api.gax.longrunning.OperationFutureImpl;
import com.google.api.gax.paging.Page;
import com.google.cloud.Policy;
import com.google.cloud.Policy.DefaultMarshaller;
import com.google.cloud.Timestamp;
import com.google.cloud.spanner.DatabaseInfo.DatabaseField;
import com.google.cloud.spanner.Options.ListOption;
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.MoreObjects;
import com.google.common.base.Preconditions;
import com.google.iam.v1.GetPolicyOptions;
import com.google.longrunning.Operation;
import com.google.protobuf.Empty;
import com.google.protobuf.FieldMask;
import com.google.spanner.admin.database.v1.*;
import java.util.List;
import java.util.UUID;
import javax.annotation.Nullable;
/** Default implementation of {@link DatabaseAdminClient}. */
class DatabaseAdminClientImpl implements DatabaseAdminClient {
private static final class PolicyMarshaller extends DefaultMarshaller {
@Override
protected Policy fromPb(com.google.iam.v1.Policy policyPb) {
return super.fromPb(policyPb);
}
@Override
protected com.google.iam.v1.Policy toPb(Policy policy) {
return super.toPb(policy);
}
}
private final String projectId;
private final SpannerRpc rpc;
private final PolicyMarshaller policyMarshaller = new PolicyMarshaller();
private static final String EXPIRE_TIME_MASK = "expire_time";
DatabaseAdminClientImpl(String projectId, SpannerRpc rpc) {
this.projectId = projectId;
this.rpc = rpc;
}
/** Generates a random operation id for long-running database operations. */
private static String randomOperationId() {
UUID uuid = UUID.randomUUID();
return ("r" + uuid.toString()).replace("-", "_");
}
@Override
public Database.Builder newDatabaseBuilder(DatabaseId databaseId) {
return new Database.Builder(this, databaseId);
}
@Override
public Backup.Builder newBackupBuilder(BackupId backupId) {
return new Backup.Builder(this, backupId);
}
@Override
public Restore.Builder newRestoreBuilder(BackupId source, DatabaseId destination) {
return new Restore.Builder(source, destination);
}
@Override
public OperationFuture restoreDatabase(
String backupInstanceId, String backupId, String restoreInstanceId, String restoreDatabaseId)
throws SpannerException {
return restoreDatabase(
newRestoreBuilder(
BackupId.of(projectId, backupInstanceId, backupId),
DatabaseId.of(projectId, restoreInstanceId, restoreDatabaseId))
.build());
}
@Override
public OperationFuture restoreDatabase(Restore restore)
throws SpannerException {
final OperationFuture
rawOperationFuture = rpc.restoreDatabase(restore);
return new OperationFutureImpl<>(
rawOperationFuture.getPollingFuture(),
rawOperationFuture.getInitialFuture(),
snapshot ->
Database.fromProto(
ProtoOperationTransformers.ResponseTransformer.create(
com.google.spanner.admin.database.v1.Database.class)
.apply(snapshot),
DatabaseAdminClientImpl.this),
ProtoOperationTransformers.MetadataTransformer.create(RestoreDatabaseMetadata.class),
e -> {
throw SpannerExceptionFactory.newSpannerException(e);
});
}
@Override
public OperationFuture createBackup(
String instanceId, String backupId, String databaseId, Timestamp expireTime)
throws SpannerException {
final Backup backupInfo =
newBackupBuilder(BackupId.of(projectId, instanceId, backupId))
.setDatabase(DatabaseId.of(projectId, instanceId, databaseId))
.setExpireTime(expireTime)
.build();
return createBackup(backupInfo);
}
@Override
public OperationFuture createBackup(Backup backupInfo)
throws SpannerException {
Preconditions.checkArgument(
backupInfo.getExpireTime() != null, "Cannot create a backup without an expire time");
Preconditions.checkArgument(
backupInfo.getDatabase() != null, "Cannot create a backup without a source database");
final OperationFuture
rawOperationFuture = rpc.createBackup(backupInfo);
return new OperationFutureImpl<>(
rawOperationFuture.getPollingFuture(),
rawOperationFuture.getInitialFuture(),
snapshot -> {
com.google.spanner.admin.database.v1.Backup proto =
ProtoOperationTransformers.ResponseTransformer.create(
com.google.spanner.admin.database.v1.Backup.class)
.apply(snapshot);
return Backup.fromProto(
com.google.spanner.admin.database.v1.Backup.newBuilder(proto)
.setName(proto.getName())
.setExpireTime(proto.getExpireTime())
.setVersionTime(proto.getVersionTime())
.setState(proto.getState())
.setEncryptionInfo(proto.getEncryptionInfo())
.build(),
DatabaseAdminClientImpl.this);
},
ProtoOperationTransformers.MetadataTransformer.create(CreateBackupMetadata.class),
e -> {
throw SpannerExceptionFactory.newSpannerException(e);
});
}
@Override
public OperationFuture copyBackup(
String instanceId, String sourceBackupId, String destinationBackupId, Timestamp expireTime)
throws SpannerException {
final Backup destinationBackup =
newBackupBuilder(BackupId.of(projectId, instanceId, destinationBackupId))
.setExpireTime(expireTime)
.build();
return copyBackup(BackupId.of(projectId, instanceId, sourceBackupId), destinationBackup);
}
@Override
public OperationFuture copyBackup(
BackupId sourceBackupId, Backup destinationBackup) throws SpannerException {
Preconditions.checkNotNull(sourceBackupId);
Preconditions.checkNotNull(destinationBackup);
final OperationFuture
rawOperationFuture = rpc.copyBackup(sourceBackupId, destinationBackup);
return new OperationFutureImpl<>(
rawOperationFuture.getPollingFuture(),
rawOperationFuture.getInitialFuture(),
snapshot -> {
com.google.spanner.admin.database.v1.Backup proto =
ProtoOperationTransformers.ResponseTransformer.create(
com.google.spanner.admin.database.v1.Backup.class)
.apply(snapshot);
return Backup.fromProto(
com.google.spanner.admin.database.v1.Backup.newBuilder(proto)
.setName(proto.getName())
.setExpireTime(proto.getExpireTime())
.setEncryptionInfo(proto.getEncryptionInfo())
.build(),
DatabaseAdminClientImpl.this);
},
ProtoOperationTransformers.MetadataTransformer.create(CopyBackupMetadata.class),
e -> {
throw SpannerExceptionFactory.newSpannerException(e);
});
}
@Override
public Backup updateBackup(String instanceId, String backupId, Timestamp expireTime) {
String backupName = getBackupName(instanceId, backupId);
final com.google.spanner.admin.database.v1.Backup backup =
com.google.spanner.admin.database.v1.Backup.newBuilder()
.setName(backupName)
.setExpireTime(expireTime.toProto())
.build();
// Only update the expire time of the backup.
final FieldMask updateMask = FieldMask.newBuilder().addPaths(EXPIRE_TIME_MASK).build();
try {
return Backup.fromProto(rpc.updateBackup(backup, updateMask), DatabaseAdminClientImpl.this);
} catch (Exception e) {
throw SpannerExceptionFactory.newSpannerException(e);
}
}
@Override
public void deleteBackup(String instanceId, String backupId) {
final String backupName = getBackupName(instanceId, backupId);
try {
rpc.deleteBackup(backupName);
} catch (Exception e) {
throw SpannerExceptionFactory.newSpannerException(e);
}
}
@Override
public Backup getBackup(String instanceId, String backupId) throws SpannerException {
final String backupName = getBackupName(instanceId, backupId);
return Backup.fromProto(rpc.getBackup(backupName), DatabaseAdminClientImpl.this);
}
@Override
public final Page listBackupOperations(String instanceId, ListOption... options) {
final String instanceName = getInstanceName(instanceId);
final Options listOptions = Options.fromListOptions(options);
final int pageSize = listOptions.hasPageSize() ? listOptions.pageSize() : 0;
final String filter = listOptions.hasFilter() ? listOptions.filter() : null;
final String pageToken = listOptions.hasPageToken() ? listOptions.pageToken() : null;
PageFetcher pageFetcher =
new PageFetcher() {
@Override
public Paginated getNextPage(String nextPageToken) {
return rpc.listBackupOperations(instanceName, pageSize, filter, pageToken);
}
@Override
public Operation fromProto(Operation proto) {
return proto;
}
};
if (listOptions.hasPageToken()) {
pageFetcher.setNextPageToken(listOptions.pageToken());
}
return pageFetcher.getNextPage();
}
@Override
public final Page listDatabaseOperations(String instanceId, ListOption... options) {
final String instanceName = getInstanceName(instanceId);
final Options listOptions = Options.fromListOptions(options);
final int pageSize = listOptions.hasPageSize() ? listOptions.pageSize() : 0;
final String filter = listOptions.hasFilter() ? listOptions.filter() : null;
final String pageToken = listOptions.hasPageToken() ? listOptions.pageToken() : null;
PageFetcher pageFetcher =
new PageFetcher() {
@Override
public Paginated getNextPage(String nextPageToken) {
return rpc.listDatabaseOperations(instanceName, pageSize, filter, pageToken);
}
@Override
public Operation fromProto(Operation proto) {
return proto;
}
};
if (listOptions.hasPageToken()) {
pageFetcher.setNextPageToken(listOptions.pageToken());
}
return pageFetcher.getNextPage();
}
@Override
public final Page listDatabaseRoles(
String instanceId, String databaseId, ListOption... options) {
final String databaseName = getDatabaseName(instanceId, databaseId);
final Options listOptions = Options.fromListOptions(options);
final int pageSize = listOptions.hasPageSize() ? listOptions.pageSize() : 0;
PageFetcher pageFetcher =
new PageFetcher() {
@Override
public Paginated getNextPage(
String nextPageToken) {
try {
return rpc.listDatabaseRoles(databaseName, pageSize, nextPageToken);
} catch (SpannerException e) {
throw SpannerExceptionFactory.newSpannerException(
e.getErrorCode(),
String.format(
"Failed to list the databases roles of %s with pageToken %s: %s",
databaseName,
MoreObjects.firstNonNull(nextPageToken, ""),
e.getMessage()),
e);
}
}
@Override
public DatabaseRole fromProto(com.google.spanner.admin.database.v1.DatabaseRole proto) {
return DatabaseRole.fromProto(proto);
}
};
if (listOptions.hasPageToken()) {
pageFetcher.setNextPageToken(listOptions.pageToken());
}
return pageFetcher.getNextPage();
}
@Override
public Page listBackups(String instanceId, ListOption... options) {
final String instanceName = getInstanceName(instanceId);
final Options listOptions = Options.fromListOptions(options);
final String filter = listOptions.hasFilter() ? listOptions.filter() : null;
final int pageSize = listOptions.hasPageSize() ? listOptions.pageSize() : 0;
PageFetcher pageFetcher =
new PageFetcher() {
@Override
public Paginated getNextPage(
String nextPageToken) {
return rpc.listBackups(instanceName, pageSize, filter, nextPageToken);
}
@Override
public Backup fromProto(com.google.spanner.admin.database.v1.Backup proto) {
return Backup.fromProto(proto, DatabaseAdminClientImpl.this);
}
};
if (listOptions.hasPageToken()) {
pageFetcher.setNextPageToken(listOptions.pageToken());
}
return pageFetcher.getNextPage();
}
@Override
public OperationFuture createDatabase(
String instanceId, String databaseId, Iterable statements) throws SpannerException {
return createDatabase(
newDatabaseBuilder(DatabaseId.of(projectId, instanceId, databaseId))
.setDialect(Dialect.GOOGLE_STANDARD_SQL)
.build(),
statements);
}
@Override
public OperationFuture createDatabase(
Database database, Iterable statements) throws SpannerException {
final Dialect dialect = Preconditions.checkNotNull(database.getDialect());
final String createStatement =
dialect.createDatabaseStatementFor(database.getId().getDatabase());
return createDatabase(createStatement, database, statements);
}
@Override
public OperationFuture createDatabase(
String instanceId,
String createDatabaseStatement,
Dialect dialect,
Iterable statements)
throws SpannerException {
Database database =
newDatabaseBuilder(DatabaseId.of(projectId, instanceId, "")).setDialect(dialect).build();
return createDatabase(createDatabaseStatement, database, statements);
}
private OperationFuture createDatabase(
String createStatement, Database database, Iterable statements)
throws SpannerException {
OperationFuture
rawOperationFuture =
rpc.createDatabase(
database.getId().getInstanceId().getName(), createStatement, statements, database);
return new OperationFutureImpl<>(
rawOperationFuture.getPollingFuture(),
rawOperationFuture.getInitialFuture(),
snapshot ->
Database.fromProto(
ProtoOperationTransformers.ResponseTransformer.create(
com.google.spanner.admin.database.v1.Database.class)
.apply(snapshot),
DatabaseAdminClientImpl.this),
ProtoOperationTransformers.MetadataTransformer.create(CreateDatabaseMetadata.class),
e -> {
throw SpannerExceptionFactory.newSpannerException(e);
});
}
@Override
public Database getDatabase(String instanceId, String databaseId) throws SpannerException {
String dbName = getDatabaseName(instanceId, databaseId);
return Database.fromProto(rpc.getDatabase(dbName), DatabaseAdminClientImpl.this);
}
@Override
public OperationFuture updateDatabase(
Database database, DatabaseField... fieldsToUpdate) throws SpannerException {
FieldMask fieldMask = DatabaseInfo.DatabaseField.toFieldMask(fieldsToUpdate);
OperationFuture
rawOperationFuture = rpc.updateDatabase(database.toProto(), fieldMask);
return new OperationFutureImpl<>(
rawOperationFuture.getPollingFuture(),
rawOperationFuture.getInitialFuture(),
snapshot ->
Database.fromProto(
ProtoOperationTransformers.ResponseTransformer.create(
com.google.spanner.admin.database.v1.Database.class)
.apply(snapshot),
DatabaseAdminClientImpl.this),
ProtoOperationTransformers.MetadataTransformer.create(UpdateDatabaseMetadata.class),
e -> {
throw SpannerExceptionFactory.newSpannerException(e);
});
}
@Override
public OperationFuture updateDatabaseDdl(
final String instanceId,
final String databaseId,
final Iterable statements,
@Nullable String operationId)
throws SpannerException {
return updateDatabaseDdl(
newDatabaseBuilder(DatabaseId.of(projectId, instanceId, databaseId)).build(),
statements,
operationId);
}
@Override
public OperationFuture updateDatabaseDdl(
Database database, final Iterable statements, @Nullable String operationId)
throws SpannerException {
final String opId = operationId != null ? operationId : randomOperationId();
OperationFuture rawOperationFuture =
rpc.updateDatabaseDdl(database, statements, opId);
return new OperationFutureImpl<>(
rawOperationFuture.getPollingFuture(),
rawOperationFuture.getInitialFuture(),
snapshot -> {
ProtoOperationTransformers.ResponseTransformer.create(Empty.class).apply(snapshot);
return null;
},
ProtoOperationTransformers.MetadataTransformer.create(UpdateDatabaseDdlMetadata.class),
e -> {
throw SpannerExceptionFactory.newSpannerException(e);
});
}
@Override
public void dropDatabase(String instanceId, String databaseId) throws SpannerException {
String dbName = getDatabaseName(instanceId, databaseId);
rpc.dropDatabase(dbName);
}
@Override
public List getDatabaseDdl(String instanceId, String databaseId) {
return getDatabaseDdlResponse(instanceId, databaseId).getStatementsList();
}
@Override
public GetDatabaseDdlResponse getDatabaseDdlResponse(String instanceId, String databaseId) {
String dbName = getDatabaseName(instanceId, databaseId);
return rpc.getDatabaseDdl(dbName);
}
@Override
public Page listDatabases(String instanceId, ListOption... options) {
final String instanceName = getInstanceName(instanceId);
final Options listOptions = Options.fromListOptions(options);
Preconditions.checkArgument(
!listOptions.hasFilter(), "Filter option is not supported by listDatabases");
final int pageSize = listOptions.hasPageSize() ? listOptions.pageSize() : 0;
PageFetcher pageFetcher =
new PageFetcher() {
@Override
public Paginated getNextPage(
String nextPageToken) {
try {
return rpc.listDatabases(instanceName, pageSize, nextPageToken);
} catch (SpannerException e) {
throw SpannerExceptionFactory.newSpannerException(
e.getErrorCode(),
String.format(
"Failed to list the databases of %s with pageToken %s: %s",
instanceName,
MoreObjects.firstNonNull(nextPageToken, ""),
e.getMessage()),
e);
}
}
@Override
public Database fromProto(com.google.spanner.admin.database.v1.Database proto) {
return Database.fromProto(proto, DatabaseAdminClientImpl.this);
}
};
if (listOptions.hasPageToken()) {
pageFetcher.setNextPageToken(listOptions.pageToken());
}
return pageFetcher.getNextPage();
}
@Override
public void cancelOperation(String name) {
Preconditions.checkNotNull(name);
rpc.cancelOperation(name);
}
@Override
public Operation getOperation(String name) {
Preconditions.checkNotNull(name);
return rpc.getOperation(name);
}
@Override
public Policy getDatabaseIAMPolicy(String instanceId, String databaseId, int version) {
final String databaseName = DatabaseId.of(projectId, instanceId, databaseId).getName();
GetPolicyOptions options = null;
if (version > 0) {
options = GetPolicyOptions.newBuilder().setRequestedPolicyVersion(version).build();
}
return policyMarshaller.fromPb(rpc.getDatabaseAdminIAMPolicy(databaseName, options));
}
@Override
public Policy setDatabaseIAMPolicy(String instanceId, String databaseId, Policy policy) {
Preconditions.checkNotNull(policy);
String databaseName = DatabaseId.of(projectId, instanceId, databaseId).getName();
return policyMarshaller.fromPb(
rpc.setDatabaseAdminIAMPolicy(databaseName, policyMarshaller.toPb(policy)));
}
@Override
public Iterable testDatabaseIAMPermissions(
String instanceId, String databaseId, Iterable permissions) {
Preconditions.checkNotNull(permissions);
String databaseName = DatabaseId.of(projectId, instanceId, databaseId).getName();
return rpc.testDatabaseAdminIAMPermissions(databaseName, permissions).getPermissionsList();
}
@Override
public Policy getBackupIAMPolicy(String instanceId, String backupId) {
final String databaseName = BackupId.of(projectId, instanceId, backupId).getName();
return policyMarshaller.fromPb(rpc.getDatabaseAdminIAMPolicy(databaseName, null));
}
@Override
public Policy setBackupIAMPolicy(String instanceId, String backupId, final Policy policy) {
Preconditions.checkNotNull(policy);
final String databaseName = BackupId.of(projectId, instanceId, backupId).getName();
return policyMarshaller.fromPb(
rpc.setDatabaseAdminIAMPolicy(databaseName, policyMarshaller.toPb(policy)));
}
@Override
public Iterable testBackupIAMPermissions(
String instanceId, String backupId, final Iterable permissions) {
Preconditions.checkNotNull(permissions);
final String databaseName = BackupId.of(projectId, instanceId, backupId).getName();
return rpc.testDatabaseAdminIAMPermissions(databaseName, permissions).getPermissionsList();
}
private String getInstanceName(String instanceId) {
return new InstanceId(projectId, instanceId).getName();
}
private String getDatabaseName(String instanceId, String databaseId) {
return new DatabaseId(new InstanceId(projectId, instanceId), databaseId).getName();
}
private String getBackupName(String instanceId, String backupId) {
InstanceId instance = new InstanceId(projectId, instanceId);
return new BackupId(instance, backupId).getName();
}
}