Evitar a exclusão acidental do banco de dados

Nesta página, descrevemos como proteger os bancos de dados do Spanner contra erros exclusão.

A proteção contra exclusão do banco de dados do Spanner impede a exclusão acidental bancos de dados por usuários ou contas de serviço que tenham Permissões do IAM para excluir o banco de dados. Ao ativar a proteção contra exclusão de bancos de dados, é possível proteger os bancos de dados que são importantes para seus aplicativos e serviços. Use a proteção contra exclusão de banco de dados com a recuperação pontual e os recursos de backup para fornecer um conjunto abrangente de recursos de proteção de dados para seus bancos de dados do Spanner.

Por padrão, a configuração de proteção contra exclusão é desativada quando você cria um novo no seu banco de dados. É possível ativar a configuração de proteção contra exclusão após a criação do banco de dados for bem-sucedida. Além disso, é possível ativar essa configuração em um banco de dados existente. Se você quiser proteger vários bancos de dados, ative a configuração em cada um deles individualmente. Ativar ou desativar a proteção contra exclusão não afetam o desempenho do banco de dados. Se você precisar excluir um banco de dados com a proteção do banco de dados ativada, é necessário desativar a proteção antes é possível excluir o banco de dados.

Limitações

Não é possível ativar a proteção contra exclusão de banco de dados nos seguintes cenários:

  • Se o banco de dados estiver sendo excluído.
  • Se o banco de dados estiver sendo restaurado de um backup. Após a operação de restauração é possível ativar a proteção do banco de dados).

Além disso, os backups de um banco de dados e os bancos de dados restaurados a partir de um backup não herdam a configuração de proteção contra exclusão do banco de dados de origem. Depois de restaurar um banco de dados de um backup, é necessário ativar a proteção contra exclusão do banco de dados separadamente.

Se você excluir seu projeto, a proteção contra exclusão do banco de dados do Spanner não impedirá a exclusão do banco de dados ou da instância. Para mais informações sobre o que acontece quando você exclui seu projeto, consulte Como encerrar (excluir) projetos.

Controle de acesso com o IAM

Para ativar a configuração de proteção contra exclusão do seu banco de dados, é necessário ter certas permissões do IAM.

Você precisa ter a permissão spanner.databases.update para ativar ou desativar a proteção contra exclusão do banco de dados. Se você só precisa conferir o status da configuração do banco de dados, é necessário ter a permissão spanner.databases.list ou spanner.databases.get. Para saber como conceder permissões do IAM do Spanner, consulte Aplicar permissões do IAM.

Se você tiver o papel predefinido de administrador do banco de dados do Spanner roles/spanner.databaseAdmin para seu banco de dados, poderá atualizar e ativar a proteção contra exclusão do banco de dados.

É possível ativar a configuração de proteção contra exclusão em um banco de dados para evitar a exclusão acidental dele.

Ativar a proteção contra exclusão do banco de dados

É possível ativar a proteção contra exclusão de banco de dados usando a CLI gcloud, as bibliotecas de cliente e as APIs REST ou RPC. Não é possível ativar a proteção contra exclusão de banco de dados usando o console do Google Cloud.

gcloud

Para ativar a configuração de proteção contra exclusão de um banco de dados, execute o seguinte comando:

  gcloud spanner databases update
  DATABASE_ID --instance=INSTANCE_ID
  --enable-drop-protection [--async]

As seguintes opções são obrigatórias:

DATABASE_ID
ID do banco de dados.
INSTANCE_ID
ID da instância para o banco de dados.

As seguintes opções são opcionais:

--async
Retornar imediatamente, sem aguardar a operação em andamento a serem concluídas.

Bibliotecas de cliente

C++

void UpdateDatabase(google::cloud::spanner_admin::DatabaseAdminClient client,
                    std::string const& project_id,
                    std::string const& instance_id,
                    std::string const& database_id, bool drop_protection) {
  google::cloud::spanner::Database db(project_id, instance_id, database_id);
  google::spanner::admin::database::v1::Database database;
  database.set_name(db.FullName());
  database.set_enable_drop_protection(drop_protection);
  google::protobuf::FieldMask update_mask;
  update_mask.add_paths("enable_drop_protection");
  auto updated = client.UpdateDatabase(database, update_mask).get();
  if (!updated) throw std::move(updated).status();
  std::cout << "Database " << updated->name() << " successfully updated.\n";
}

C#


using Google.Cloud.Spanner.Admin.Database.V1;
using Google.Cloud.Spanner.Common.V1;
using Google.Protobuf.WellKnownTypes;
using System;
using System.Threading.Tasks;

public class UpdateDatabaseAsyncSample
{
    public async Task<Database> UpdateDatabaseAsync(string projectId, string instanceId, string databaseId)
    {
        var databaseAdminClient = await DatabaseAdminClient.CreateAsync();
        var databaseName = DatabaseName.Format(projectId, instanceId, databaseId);
        Database databaseToUpdate = await databaseAdminClient.GetDatabaseAsync(databaseName);

        databaseToUpdate.EnableDropProtection = true;
        var updateDatabaseRequest = new UpdateDatabaseRequest()
        {
            Database = databaseToUpdate,
            UpdateMask = new FieldMask { Paths = { "enable_drop_protection" } }
        };

        var operation = await databaseAdminClient.UpdateDatabaseAsync(updateDatabaseRequest);

        // Wait until the operation has finished.
        Console.WriteLine("Waiting for the operation to finish.");
        var completedResponse = await operation.PollUntilCompletedAsync();

        if (completedResponse.IsFaulted)
        {
            Console.WriteLine($"Error while updating database {databaseId}: {completedResponse.Exception}");
            throw completedResponse.Exception;
        }

        Console.WriteLine($"Updated database {databaseId}.");

        // Return the updated database.
        return completedResponse.Result;
    }
}

Go

import (
	"context"
	"fmt"
	"io"

	database "cloud.google.com/go/spanner/admin/database/apiv1"
	adminpb "cloud.google.com/go/spanner/admin/database/apiv1/databasepb"
	"google.golang.org/genproto/protobuf/field_mask"
)

func updateDatabase(ctx context.Context, w io.Writer, db string) error {
	// db = `projects/<project>/instances/<instance-id>/database/<database-id>`
	// Instantiate database admin client.
	adminClient, err := database.NewDatabaseAdminClient(ctx)
	if err != nil {
		return fmt.Errorf("updateDatabase.NewDatabaseAdminClient: %w", err)
	}
	defer adminClient.Close()

	// Instantiate the request for performing update database operation.
	op, err := adminClient.UpdateDatabase(ctx, &adminpb.UpdateDatabaseRequest{
		Database: &adminpb.Database{
			Name:                 db,
			EnableDropProtection: true,
		},
		UpdateMask: &field_mask.FieldMask{
			Paths: []string{"enable_drop_protection"},
		},
	})
	if err != nil {
		return fmt.Errorf("updateDatabase.UpdateDatabase: %w", err)
	}

	// Wait for update database operation to complete.
	fmt.Fprintf(w, "Waiting for update database operation to complete [%s]\n", db)
	if _, err := op.Wait(ctx); err != nil {
		return fmt.Errorf("updateDatabase.Wait: %w", err)
	}
	fmt.Fprintf(w, "Updated database [%s]\n", db)
	return nil
}

Java


import com.google.api.gax.longrunning.OperationFuture;
import com.google.cloud.spanner.Spanner;
import com.google.cloud.spanner.SpannerExceptionFactory;
import com.google.cloud.spanner.SpannerOptions;
import com.google.cloud.spanner.admin.database.v1.DatabaseAdminClient;
import com.google.common.collect.Lists;
import com.google.protobuf.FieldMask;
import com.google.spanner.admin.database.v1.Database;
import com.google.spanner.admin.database.v1.DatabaseName;
import com.google.spanner.admin.database.v1.UpdateDatabaseMetadata;
import com.google.spanner.admin.database.v1.UpdateDatabaseRequest;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;

public class UpdateDatabaseSample {

  static void updateDatabase() {
    // TODO(developer): Replace these variables before running the sample.
    final String projectId = "my-project";
    final String instanceId = "my-instance";
    final String databaseId = "my-database";

    updateDatabase(projectId, instanceId, databaseId);
  }

  static void updateDatabase(
      String projectId, String instanceId, String databaseId) {
    try (Spanner spanner =
        SpannerOptions.newBuilder().setProjectId(projectId).build().getService();
        DatabaseAdminClient databaseAdminClient = spanner.createDatabaseAdminClient()) {
      final Database database =
          Database.newBuilder()
              .setName(DatabaseName.of(projectId, instanceId, databaseId).toString())
              .setEnableDropProtection(true).build();
      final UpdateDatabaseRequest updateDatabaseRequest =
          UpdateDatabaseRequest.newBuilder()
              .setDatabase(database)
              .setUpdateMask(
                  FieldMask.newBuilder().addAllPaths(
                      Lists.newArrayList("enable_drop_protection")).build())
              .build();
      OperationFuture<Database, UpdateDatabaseMetadata> operation =
          databaseAdminClient.updateDatabaseAsync(updateDatabaseRequest);
      System.out.printf("Waiting for update operation for %s to complete...\n", databaseId);
      Database updatedDb = operation.get(5, TimeUnit.MINUTES);
      System.out.printf("Updated database %s.\n", updatedDb.getName());
    } catch (ExecutionException | TimeoutException e) {
      // If the operation failed during execution, expose the cause.
      throw SpannerExceptionFactory.asSpannerException(e.getCause());
    } catch (InterruptedException e) {
      // Throw when a thread is waiting, sleeping, or otherwise occupied,
      // and the thread is interrupted, either before or during the activity.
      throw SpannerExceptionFactory.propagateInterrupt(e);
    }
  }
}

Node.js

/**
 * TODO(developer): Uncomment these variables before running the sample.
 */
// const instanceId = 'my-instance';
// const databaseId = 'my-database';
// const projectId = 'my-project-id';

// Imports the Google Cloud client library
const {Spanner, protos} = require('@google-cloud/spanner');

// creates a client
const spanner = new Spanner({
  projectId: projectId,
});

const databaseAdminClient = spanner.getDatabaseAdminClient();

async function updateDatabase() {
  // Update the database metadata fields
  try {
    console.log(
      `Updating database ${databaseAdminClient.databasePath(
        projectId,
        instanceId,
        databaseId
      )}.`
    );
    const [operation] = await databaseAdminClient.updateDatabase({
      database: {
        name: databaseAdminClient.databasePath(
          projectId,
          instanceId,
          databaseId
        ),
        enableDropProtection: true,
      },
      // updateMask contains the fields to be updated in database
      updateMask: (protos.google.protobuf.FieldMask = {
        paths: ['enable_drop_protection'],
      }),
    });
    console.log(
      `Waiting for update operation for ${databaseId} to complete...`
    );
    await operation.promise();
    console.log(`Updated database ${databaseId}.`);
  } catch (err) {
    console.log('ERROR:', err);
  }
}
updateDatabase();

PHP

use Google\Cloud\Spanner\Admin\Database\V1\Client\DatabaseAdminClient;
use Google\Cloud\Spanner\Admin\Database\V1\Database;
use Google\Cloud\Spanner\Admin\Database\V1\GetDatabaseRequest;
use Google\Cloud\Spanner\Admin\Database\V1\UpdateDatabaseRequest;
use Google\Protobuf\FieldMask;

/**
 * Updates the drop protection setting for a database.
 * Example:
 * ```
 * update_database($projectId, $instanceId, $databaseId);
 * ```
 *
 * @param string $projectId The Google Cloud project ID.
 * @param string $instanceId The Spanner instance ID.
 * @param string $databaseId The Spanner database ID.
 */
function update_database(string $projectId, string $instanceId, string $databaseId): void
{
    $newUpdateMaskField = new FieldMask([
        'paths' => ['enable_drop_protection']
    ]);
    $databaseAdminClient = new DatabaseAdminClient();
    $databaseFullName = DatabaseAdminClient::databaseName($projectId, $instanceId, $databaseId);
    $database = (new Database())
        ->setEnableDropProtection(true)
        ->setName($databaseFullName);

    printf('Updating database %s', $databaseId);
    $operation = $databaseAdminClient->updateDatabase((new UpdateDatabaseRequest())
        ->setDatabase($database)
        ->setUpdateMask($newUpdateMaskField));

    $operation->pollUntilComplete();

    $database = $databaseAdminClient->getDatabase(
        new GetDatabaseRequest(['name' => $databaseFullName])
    );
    printf(
        'Updated the drop protection for %s to %s' . PHP_EOL,
        $database->getName(),
        $database->getEnableDropProtection()
    );
}

Python

def update_database(instance_id, database_id):
    """Updates the drop protection setting for a database."""
    from google.cloud.spanner_admin_database_v1.types import \
        spanner_database_admin

    spanner_client = spanner.Client()
    database_admin_api = spanner_client.database_admin_api

    request = spanner_database_admin.UpdateDatabaseRequest(
        database=spanner_database_admin.Database(
            name=database_admin_api.database_path(
                spanner_client.project, instance_id, database_id
            ),
            enable_drop_protection=True,
        ),
        update_mask={"paths": ["enable_drop_protection"]},
    )
    operation = database_admin_api.update_database(request=request)
    print(
        "Waiting for update operation for {}/databases/{} to complete...".format(
            database_admin_api.instance_path(spanner_client.project, instance_id),
            database_id,
        )
    )
    operation.result(OPERATION_TIMEOUT_SECONDS)

    print(
        "Updated database {}/databases/{}.".format(
            database_admin_api.instance_path(spanner_client.project, instance_id),
            database_id,
        )
    )

Ruby

require "google/cloud/spanner/admin/database"

##
# This is a snippet for showcasing how to update database.
#
# @param project_id  [String] The ID of the Google Cloud project.
# @param instance_id [String] The ID of the spanner instance.
# @param database_id [String] The ID of the database.
#
def spanner_update_database project_id:, instance_id:, database_id:
  client = Google::Cloud::Spanner::Admin::Database.database_admin project_id: project_id
  db_path = client.database_path project: project_id, instance: instance_id, database: database_id
  database = client.get_database name: db_path

  puts "Updating database #{database.name}"
  database.enable_drop_protection = true
  job = client.update_database database: database, update_mask: { paths: ["enable_drop_protection"] }
  puts "Waiting for update operation for #{database.name} to complete..."
  job.wait_until_done!
  puts "Updated database #{database.name}"
end

Verificar se a proteção contra exclusão está ativada em um banco de dados

É possível determinar se a proteção contra exclusão do banco de dados está ativada e visualizar a configuração do banco de dados.

gcloud

Para verificar se a proteção contra exclusão está ativada em um banco de dados, execute o comando o comando gcloud spanner databases describe para receber informações detalhadas. sobre um banco de dados ou execute gcloud spanner databases list para acessar informações detalhadas sobre os bancos de dados em uma instância.

  gcloud spanner databases describe
  projects/PROJECT_ID/instances/INSTANCE_ID/databases/DATABASE_ID

As seguintes opções são obrigatórias:

PROJECT_ID
ID do projeto para o banco de dados.
INSTANCE_ID
ID da instância para o banco de dados.
DATABASE_ID
ID do banco de dados.

Se a proteção contra exclusão estiver ativada, o parâmetro enableDropProtection: true na saída.

Desativar a proteção contra exclusão do banco de dados

É possível desativar a proteção contra exclusão de bancos de dados se um banco de dados não precisar mais dela ou se precisar excluir um banco de dados com essa configuração ativada.

Se você quiser excluir uma instância que tenha um ou mais bancos de dados com exclusão ativada, desative a proteção contra exclusão em todos bancos de dados na instância antes de excluir a instância.

gcloud

Para desativar a configuração de proteção contra exclusão de um banco de dados, execute o seguinte comando:

  gcloud spanner databases update
  DATABASE_ID --instance=INSTANCE_ID
  --no-enable-drop-protection [--async]

As seguintes opções são obrigatórias:

DATABASE_ID
ID do banco de dados.
INSTANCE_ID
ID da instância para o banco de dados.

As seguintes opções são opcionais:

--async
Retorna imediatamente, sem aguardar a conclusão da operação em andamento.

A seguir