Evitar a exclusão acidental do banco de dados

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

A proteção contra exclusão de bancos de dados do Spanner impede a exclusão acidental de bancos de dados atuais por usuários ou contas de serviço que tenham as permissões do IAM necessárias para excluir o banco de dados. Ao ativar a proteção contra exclusão de banco de dados, é possível proteger os bancos de dados que são importantes para seu aplicativo e serviços. Use a proteção contra exclusão de bancos de dados, além de recuperação pontual e recursos de backup para oferecer um conjunto abrangente de recursos de proteção de dados para os bancos de dados do Spanner.

Por padrão, a configuração de proteção contra exclusão é desativada quando você cria o novo banco de dados. Será 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 atual. 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 de banco de dados 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 está sendo excluído.
  • Se o banco de dados está sendo restaurado a partir de um backup. Após a conclusão da operação de restauração, será 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 de um backup não herdam a configuração de proteção contra exclusão de banco de dados do banco de dados de origem. Depois de restaurar um banco de dados usando 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 de bancos de dados do Spanner não vai impedir a exclusão do banco de dados ou instância. Para saber mais sobre o que acontece quando você exclui o projeto, consulte Como encerrar (excluir) projetos.

Controle de acesso com o IAM

Para ativar a configuração de proteção contra exclusão do banco de dados, é preciso ter determinadas permissões do IAM.

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

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

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

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

É possível ativar a proteção contra exclusão do 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
Retornar 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: %v", 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: %v", 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: %v", 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 está com a proteção contra exclusão ativada

É possível determinar se o banco de dados está com a proteção contra exclusão ativada visualizando a configuração dele.

gcloud

Para verificar se um banco de dados está com a proteção contra exclusão ativada, execute o comando gcloud spanner databases describe para receber informações detalhadas sobre um banco de dados ou execute 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ê verá um parâmetro enableDropProtection: true na saída.

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

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

Para excluir uma instância que tenha um ou mais bancos de dados com a proteção contra exclusão ativada, desative a proteção em todos os bancos de dados da instância antes de excluí-la.

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
Retornar imediatamente, sem aguardar a conclusão da operação em andamento.

A seguir