Evitare l'eliminazione accidentale del database

Questa pagina descrive come proteggere i database Spanner da incidenti l'eliminazione dei dati.

La protezione da eliminazione del database di Spanner impedisce l'eliminazione accidentale di database esistenti da utenti o account di servizio che hanno Autorizzazioni IAM per eliminare il database. Abilitando la protezione da eliminazione dei database, puoi salvaguardare i database importanti per la tua applicazione e i tuoi servizi. Utilizza la protezione da eliminazione del database insieme al recupero point-in-time e funzionalità di backup per offrire un insieme completo di protezione dei dati per i database Spanner.

Per impostazione predefinita, l'impostazione di protezione da eliminazione è disattivata quando crei il nuovo database. Puoi abilitare l'impostazione della protezione da eliminazione dopo il creazione del database riuscita. Inoltre, puoi attivare questa impostazione su una un database esistente. Se vuoi proteggere più database, abilita l'impostazione su ciascun database. L'attivazione o la disattivazione della protezione da eliminazione non non avranno un impatto sulle prestazioni del database. Se devi eliminare un database per il quale è abilitata la protezione, devi disattivare la protezione prima di poter eliminare il database.

Limitazioni

Non puoi abilitare la protezione da eliminazione dei database nei seguenti scenari:

  • Se il database è in fase di eliminazione.
  • Se il database è in fase di ripristino da un backup. Dopo l'operazione di ripristino, viene completata, puoi abilitare la protezione del database).

Inoltre, i backup di un database e dei database ripristinati da un backup non vengono ereditare l'impostazione di protezione da eliminazione del database del database di origine. Dopo il giorno se ripristini un database da un backup, devi abilitarne l'eliminazione la protezione separatamente.

Se elimini il progetto, la protezione da eliminazione del database Spanner non impedisce l'eliminazione del database o dell'istanza. Per saperne di più su cosa succede quando elimini un progetto, consulta l'articolo relativo all'arresto (eliminazione) dei progetti.

Controllo dell'accesso con IAM

Per abilitare l'impostazione di protezione dall'eliminazione del database, devi avere alcune autorizzazioni IAM.

Devi disporre dell'autorizzazione spanner.databases.update per attivare o Disabilita la protezione da eliminazione del database. Se devi solo visualizzare lo stato della configurazione del database, devi disporre dell'elemento spanner.databases.list Autorizzazione spanner.databases.get. Per informazioni su come concedere Autorizzazioni IAM di Spanner, consulta Applica le autorizzazioni IAM.

Se hai il ruolo Amministratore database Spanner Ruolo roles/spanner.databaseAdmin per del database, puoi aggiornare e abilitare la protezione da eliminazione del database.

Puoi attivare l'impostazione di protezione dall'eliminazione del database su un database esistente per impedirne l'eliminazione accidentale.

Abilita la protezione da eliminazione dei database

Puoi abilitare la protezione da eliminazione dei database utilizzando gcloud CLI, librerie client e il comando REST o RPC su quelle di livello inferiore. Non puoi attivare la protezione dall'eliminazione dei database utilizzando la console Google Cloud.

gcloud

Per abilitare l'impostazione di protezione dall'eliminazione di un database, esegui questo comando:

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

Sono necessarie le seguenti opzioni:

DATABASE_ID
ID del database.
INSTANCE_ID
ID dell'istanza per il database.

Le seguenti opzioni sono facoltative:

--async
Restituisci subito, senza attendere l'operazione in corso per completare l'operazione.

Librerie client

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;
    }
}

Vai

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

Verificare se in un database è abilitata la protezione da eliminazione

Puoi determinare se la protezione da eliminazione è abilitata per il tuo database che visualizza la configurazione del database.

gcloud

Per verificare se in un database è abilitata la protezione da eliminazione, puoi eseguire il Comando gcloud spanner databases describe per ottenere informazioni dettagliate informazioni su un database, oppure puoi eseguire gcloud spanner databases list ottenere informazioni dettagliate sui database all'interno di un'istanza.

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

Sono necessarie le seguenti opzioni:

PROJECT_ID
ID del progetto per il database.
INSTANCE_ID
ID dell'istanza per il database.
DATABASE_ID
ID del database.

Se la protezione dall'eliminazione è attiva, vedrai un enableDropProtection: true nell'output.

Disattivare la protezione dall'eliminazione del database

Puoi disabilitare la protezione dall'eliminazione dei database se non è più necessaria per un database o se devi eliminare un database in cui è abilitata questa impostazione.

Se vuoi eliminare un'istanza con uno o più database con la protezione dall'eliminazione abilitata, devi prima disattivare la protezione dall'eliminazione su tutti i database dell'istanza prima di poter eliminare l'istanza.

gcloud

Per disattivare l'impostazione di protezione dall'eliminazione di un database, esegui il seguente comando:

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

Sono necessarie le seguenti opzioni:

DATABASE_ID
ID del database.
INSTANCE_ID
ID dell'istanza per il database.

Le seguenti opzioni sono facoltative:

--async
Restituisci subito, senza attendere l'operazione in corso per completare l'operazione.

Passaggi successivi