|
| 1 | +/* |
| 2 | +* Copyright 2020 Google LLC |
| 3 | +* |
| 4 | +* Licensed under the Apache License, Version 2.0 (the "License"); |
| 5 | +* you may not use this file except in compliance with the License. |
| 6 | +* You may obtain a copy of the License at |
| 7 | +* |
| 8 | +* http://www.apache.org/licenses/LICENSE-2.0 |
| 9 | +* |
| 10 | +* Unless required by applicable law or agreed to in writing, software |
| 11 | +* distributed under the License is distributed on an "AS IS" BASIS, |
| 12 | +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 13 | +* See the License for the specific language governing permissions and |
| 14 | +* limitations under the License. |
| 15 | +*/ |
| 16 | + |
| 17 | +package com.google.cloud.spanner; |
| 18 | + |
| 19 | +import static com.google.common.base.Preconditions.checkArgument; |
| 20 | + |
| 21 | +import com.google.api.client.util.Preconditions; |
| 22 | +import com.google.api.gax.longrunning.OperationFuture; |
| 23 | +import com.google.api.gax.paging.Page; |
| 24 | +import com.google.cloud.Policy; |
| 25 | +import com.google.cloud.Timestamp; |
| 26 | +import com.google.longrunning.Operation; |
| 27 | +import com.google.spanner.admin.database.v1.CreateBackupMetadata; |
| 28 | +import com.google.spanner.admin.database.v1.RestoreDatabaseMetadata; |
| 29 | + |
| 30 | +/** |
| 31 | +* Represents a Cloud Spanner database backup. {@code Backup} adds a layer of service related |
| 32 | +* functionality over {@code BackupInfo}. |
| 33 | +*/ |
| 34 | +public class Backup extends BackupInfo { |
| 35 | +public static class Builder extends BackupInfo.BuilderImpl { |
| 36 | +private final DatabaseAdminClient dbClient; |
| 37 | + |
| 38 | +Builder(DatabaseAdminClient dbClient, BackupId backupId) { |
| 39 | +super(backupId); |
| 40 | +this.dbClient = Preconditions.checkNotNull(dbClient); |
| 41 | +} |
| 42 | + |
| 43 | +private Builder(Backup backup) { |
| 44 | +super(backup); |
| 45 | +this.dbClient = backup.dbClient; |
| 46 | +} |
| 47 | + |
| 48 | +@Override |
| 49 | +public Backup build() { |
| 50 | +return new Backup(this); |
| 51 | +} |
| 52 | +} |
| 53 | + |
| 54 | +private static final String FILTER_BACKUP_OPERATIONS_TEMPLATE = "name:backups/%s"; |
| 55 | +private final DatabaseAdminClient dbClient; |
| 56 | + |
| 57 | +Backup(Builder builder) { |
| 58 | +super(builder); |
| 59 | +this.dbClient = Preconditions.checkNotNull(builder.dbClient); |
| 60 | +} |
| 61 | + |
| 62 | +/** Creates a backup on the server based on the source of this {@link Backup} instance. */ |
| 63 | +public OperationFuture<Backup, CreateBackupMetadata> create() { |
| 64 | +Preconditions.checkState( |
| 65 | +getExpireTime() != null, "Cannot create a backup without an expire time"); |
| 66 | +Preconditions.checkState( |
| 67 | +getDatabase() != null, "Cannot create a backup without a source database"); |
| 68 | +return dbClient.createBackup(instance(), backup(), sourceDatabase(), getExpireTime()); |
| 69 | +} |
| 70 | + |
| 71 | +/** |
| 72 | +* Returns <code>true</code> if a backup with the id of this {@link Backup} exists on Cloud |
| 73 | +* Spanner. |
| 74 | +*/ |
| 75 | +public boolean exists() { |
| 76 | +try { |
| 77 | +dbClient.getBackup(instance(), backup()); |
| 78 | +} catch (SpannerException e) { |
| 79 | +if (e.getErrorCode() == ErrorCode.NOT_FOUND) { |
| 80 | +return false; |
| 81 | +} |
| 82 | +throw e; |
| 83 | +} |
| 84 | +return true; |
| 85 | +} |
| 86 | + |
| 87 | +/** |
| 88 | +* Returns <code>true</code> if this backup is ready to use. The value returned by this method |
| 89 | +* could be out-of-sync with the value returned by {@link #getState()}, as this method will make a |
| 90 | +* round-trip to the server and return a value based on the response from the server. |
| 91 | +*/ |
| 92 | +public boolean isReady() { |
| 93 | +return reload().getState() == State.READY; |
| 94 | +} |
| 95 | + |
| 96 | +/** |
| 97 | +* Fetches the backup's current information and returns a new {@link Backup} instance. It does not |
| 98 | +* update this instance. |
| 99 | +*/ |
| 100 | +public Backup reload() throws SpannerException { |
| 101 | +return dbClient.getBackup(instance(), backup()); |
| 102 | +} |
| 103 | + |
| 104 | +/** Deletes this backup on Cloud Spanner. */ |
| 105 | +public void delete() throws SpannerException { |
| 106 | +dbClient.deleteBackup(instance(), backup()); |
| 107 | +} |
| 108 | + |
| 109 | +/** |
| 110 | +* Updates the expire time of this backup on Cloud Spanner. If this {@link Backup} does not have |
| 111 | +* an expire time, the method will throw an {@link IllegalStateException}. |
| 112 | +*/ |
| 113 | +public void updateExpireTime() { |
| 114 | +Preconditions.checkState(getExpireTime() != null, "This backup has no expire time"); |
| 115 | +dbClient.updateBackup(instance(), backup(), getExpireTime()); |
| 116 | +} |
| 117 | + |
| 118 | +/** |
| 119 | +* Restores this backup to the specified database. The database must not already exist and will be |
| 120 | +* created by this call. The database may be created in a different instance than where the backup |
| 121 | +* is stored. |
| 122 | +*/ |
| 123 | +public OperationFuture<Database, RestoreDatabaseMetadata> restore(DatabaseId database) { |
| 124 | +Preconditions.checkNotNull(database); |
| 125 | +return dbClient.restoreDatabase( |
| 126 | +instance(), backup(), database.getInstanceId().getInstance(), database.getDatabase()); |
| 127 | +} |
| 128 | + |
| 129 | +/** Returns all long-running backup operations for this {@link Backup}. */ |
| 130 | +public Page<Operation> listBackupOperations() { |
| 131 | +return dbClient.listBackupOperations( |
| 132 | +instance(), Options.filter(String.format(FILTER_BACKUP_OPERATIONS_TEMPLATE, backup()))); |
| 133 | +} |
| 134 | + |
| 135 | +/** Returns the IAM {@link Policy} for this backup. */ |
| 136 | +public Policy getIAMPolicy() { |
| 137 | +return dbClient.getBackupIAMPolicy(instance(), backup()); |
| 138 | +} |
| 139 | + |
| 140 | +/** |
| 141 | +* Updates the IAM policy for this backup and returns the resulting policy. It is highly |
| 142 | +* recommended to first get the current policy and base the updated policy on the returned policy. |
| 143 | +* See {@link Policy.Builder#setEtag(String)} for information on the recommended read-modify-write |
| 144 | +* cycle. |
| 145 | +*/ |
| 146 | +public Policy setIAMPolicy(Policy policy) { |
| 147 | +return dbClient.setBackupIAMPolicy(instance(), backup(), policy); |
| 148 | +} |
| 149 | + |
| 150 | +/** |
| 151 | +* Tests for the given permissions on this backup for the caller. |
| 152 | +* |
| 153 | +* @param permissions the permissions to test for. Permissions with wildcards (such as '*', |
| 154 | +* 'spanner.*', 'spanner.instances.*') are not allowed. |
| 155 | +* @return the subset of the tested permissions that the caller is allowed. |
| 156 | +*/ |
| 157 | +public Iterable<String> testIAMPermissions(Iterable<String> permissions) { |
| 158 | +return dbClient.testBackupIAMPermissions(instance(), backup(), permissions); |
| 159 | +} |
| 160 | + |
| 161 | +public Builder toBuilder() { |
| 162 | +return new Builder(this); |
| 163 | +} |
| 164 | + |
| 165 | +private String instance() { |
| 166 | +return getInstanceId().getInstance(); |
| 167 | +} |
| 168 | + |
| 169 | +private String backup() { |
| 170 | +return getId().getBackup(); |
| 171 | +} |
| 172 | + |
| 173 | +private String sourceDatabase() { |
| 174 | +return getDatabase().getDatabase(); |
| 175 | +} |
| 176 | + |
| 177 | +static Backup fromProto( |
| 178 | +com.google.spanner.admin.database.v1.Backup proto, DatabaseAdminClient client) { |
| 179 | +checkArgument(!proto.getName().isEmpty(), "Missing expected 'name' field"); |
| 180 | +checkArgument(!proto.getDatabase().isEmpty(), "Missing expected 'database' field"); |
| 181 | +return new Backup.Builder(client, BackupId.of(proto.getName())) |
| 182 | +.setState(fromProtoState(proto.getState())) |
| 183 | +.setSize(proto.getSizeBytes()) |
| 184 | +.setExpireTime(Timestamp.fromProto(proto.getExpireTime())) |
| 185 | +.setDatabase(DatabaseId.of(proto.getDatabase())) |
| 186 | +.setProto(proto) |
| 187 | +.build(); |
| 188 | +} |
| 189 | + |
| 190 | +static BackupInfo.State fromProtoState( |
| 191 | +com.google.spanner.admin.database.v1.Backup.State protoState) { |
| 192 | +switch (protoState) { |
| 193 | +case STATE_UNSPECIFIED: |
| 194 | +return BackupInfo.State.UNSPECIFIED; |
| 195 | +case CREATING: |
| 196 | +return BackupInfo.State.CREATING; |
| 197 | +case READY: |
| 198 | +return BackupInfo.State.READY; |
| 199 | +default: |
| 200 | +throw new IllegalArgumentException("Unrecognized state " + protoState); |
| 201 | +} |
| 202 | +} |
| 203 | +} |
0 commit comments