Evitar a exclusão acidental do banco de dados

Esta página descreve como proteger os bancos de dados do Spanner contra exclusão acidental.

A proteção contra exclusão de banco de dados do Spanner evita a exclusão acidental de bancos de dados existentes por usuários ou contas de serviço que têm as permissões necessárias do IAM para excluir o banco de dados. Ao ativar a proteção contra exclusão de banco de dados, é possível proteger bancos de dados 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 fica desativada quando você cria um novo banco de dados. É possível ativar a configuração de proteção contra exclusão após a criação do banco de dados. 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 tem nenhum impacto no desempenho do banco de dados. Se você precisar excluir um banco de dados com a proteção ativada, desative a proteção antes de 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. Depois que a operação de restauração for concluída, você poderá 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 um 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.

É necessário ter a permissão spanner.databases.update para ativar ou desativar a proteção contra exclusão de 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 de 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 do banco de dados.

As seguintes opções são opcionais:

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

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 um banco de dados tem a proteção contra exclusão ativada

É possível determinar se o banco de dados tem a proteção contra exclusão ativada conferindo 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 gcloud spanner databases describe para receber informações detalhadas sobre um banco de dados ou o gcloud spanner databases list para receber informações detalhadas sobre 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 do banco de dados.
DATABASE_ID
ID do banco de dados.

Se a proteção contra exclusão estiver ativada, você vai encontrar um 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 banco de dados se um banco de dados não precisar mais dessa proteção ou se você 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 a proteção contra exclusão ativada, primeiro desative a proteção contra exclusão em todos os bancos de dados dessa 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 do 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