Impeça a eliminação acidental da base de dados

Esta página descreve como proteger as bases de dados do Spanner contra a eliminação acidental.

A proteção contra eliminação da base de dados do Spanner impede a eliminação acidental de bases de dados existentes por parte de utilizadores ou contas de serviço que tenham as autorizações do IAM necessárias para eliminar a base de dados. Ao ativar a proteção contra eliminação de bases de dados, pode proteger as bases de dados importantes para a sua aplicação e serviços. Use a proteção contra eliminação de bases de dados juntamente com a recuperação num determinado momento e as funcionalidades de cópia de segurança para oferecer um conjunto abrangente de capacidades de proteção de dados para as suas bases de dados do Spanner.

Por predefinição, a definição de proteção contra eliminação está desativada quando cria a nova base de dados. Pode ativar a definição de proteção contra eliminação depois de a criação da base de dados ser bem-sucedida. Além disso, pode ativar esta definição numa base de dados existente. Se quiser proteger várias bases de dados, ative a definição em cada base de dados individualmente. A ativação ou a desativação da proteção contra eliminação não tem qualquer impacto no desempenho da base de dados. Se precisar de eliminar uma base de dados com a proteção de base de dados ativada, tem de desativar a proteção antes de poder eliminar a base de dados.

Limitações

Não pode ativar a proteção contra eliminação de bases de dados nos seguintes cenários:

  • Se a base de dados estiver a ser eliminada.
  • Se a base de dados estiver a ser restaurada a partir de uma cópia de segurança. (Depois de a operação de restauro estar concluída, pode ativar a proteção da base de dados).

Além disso, as cópias de segurança de uma base de dados e as bases de dados restauradas a partir de uma cópia de segurança não herdam a definição de proteção contra eliminação de bases de dados da respetiva base de dados de origem. Depois de restaurar uma base de dados a partir de uma cópia de segurança, tem de ativar a respetiva proteção contra eliminação de bases de dados separadamente.

Se eliminar o seu projeto, a proteção contra eliminação da base de dados do Spanner não impede a eliminação da base de dados nem da instância. Para mais informações sobre o que acontece quando elimina o seu projeto, consulte o artigo Encerrar (eliminar) projetos.

Controlo de acesso com a IAM

Para ativar a definição de proteção contra eliminação da sua base de dados, tem de ter determinadas autorizações de IAM.

Tem de ter a autorização spanner.databases.update para ativar ou desativar a proteção contra eliminação da base de dados. Se só precisar de ver o estado da configuração da base de dados, tem de ter a autorização spanner.databases.list ou spanner.databases.get. Para obter informações sobre como conceder autorizações de IAM do Spanner, consulte o artigo Aplique autorizações de IAM.

Se tiver a função predefinida Spanner Database Admin roles/spanner.databaseAdmin para a sua base de dados, pode atualizar e ativar a proteção contra eliminação de bases de dados.

Pode ativar a definição de proteção contra a eliminação da base de dados numa base de dados existente para evitar a eliminação acidental da base de dados.

Ative a proteção de eliminação da base de dados

Pode ativar a proteção contra eliminação de bases de dados através da CLI gcloud, das bibliotecas cliente e das APIs REST ou RPC. Não pode ativar a proteção contra eliminação da base de dados através da Google Cloud consola.

gcloud

Para ativar a definição de proteção contra eliminação de uma base 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 da base de dados.
INSTANCE_ID
ID da instância da base de dados.

As seguintes opções são opcionais:

--async
Retorne imediatamente, sem aguardar a conclusão da operação em curso.

Bibliotecas 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

Verifique se uma base de dados tem a proteção contra eliminação ativada

Pode determinar se a sua base de dados tem a proteção contra eliminação ativada consultando a configuração da base de dados.

gcloud

Para verificar se uma base de dados tem a proteção contra eliminação ativada, pode executar o comando gcloud spanner databases describe para obter informações detalhadas sobre uma base de dados ou pode executar o comando gcloud spanner databases list para obter informações detalhadas sobre bases de dados numa 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 da base de dados.
INSTANCE_ID
ID da instância da base de dados.
DATABASE_ID
ID da base de dados.

Se a proteção contra eliminação estiver ativada, é apresentado um parâmetro enableDropProtection: true no resultado.

Desative a proteção de eliminação da base de dados

Pode desativar a proteção contra eliminação de bases de dados se uma base de dados já não precisar desta proteção ou se precisar de eliminar uma base de dados que tenha esta definição ativada.

Se quiser eliminar uma instância que tenha uma ou mais bases de dados com a proteção contra eliminação ativada, primeiro tem de desativar a proteção contra eliminação em todas as bases de dados nessa instância antes de poder eliminar a instância.

gcloud

Para desativar a definição de proteção contra eliminação de uma base 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 da base de dados.
INSTANCE_ID
ID da instância da base de dados.

As seguintes opções são opcionais:

--async
Retorne imediatamente, sem aguardar a conclusão da operação em curso.

O que se segue?