Evita la eliminación accidental de la base de datos

En esta página, se describe cómo proteger las bases de datos de Spanner de la eliminación accidental.

La protección contra la eliminación de bases de datos de Spanner evita que los usuarios o las cuentas de servicio que tengan los permisos de IAM necesarios para borrar la base de datos borren accidentalmente bases de datos existentes. Si habilitas la protección contra la eliminación de bases de datos, puedes proteger las bases de datos que son importantes para tu aplicación y tus servicios. Usa la protección contra eliminaciones de bases de datos junto con la recuperación de un momento determinado y las funciones de copia de seguridad para proporcionar un conjunto integral de funciones de protección de datos para tus bases de datos de Spanner.

La configuración de protección contra la eliminación se inhabilita de forma predeterminada cuando creas una nueva en la base de datos. Puedes habilitar la configuración de protección contra la eliminación después de la creación de la base de datos es exitosa. Además, puedes habilitar este parámetro de configuración en una base de datos existente. Si deseas proteger varias bases de datos, habilita la configuración en cada una de ellas de forma individual. Habilitar o inhabilitar la protección contra eliminaciones no tiene ningún impacto en el rendimiento de la base de datos. Si necesitas borrar una base de datos que tenga habilitada la protección de la base de datos, debes inhabilitarla antes de puedes borrar la base de datos.

Limitaciones

No puedes habilitar la protección contra la eliminación de bases de datos en las siguientes situaciones:

  • Si se borra la base de datos.
  • Si la base de datos se restablece a partir de una copia de seguridad. (Después de que se complete la operación de restablecimiento, puedes habilitar la protección de la base de datos).

Además, las copias de seguridad de una base de datos y las bases de datos restablecidas a partir de una copia de seguridad no heredan la configuración de protección contra eliminación de la base de datos de origen. Después del restableces una base de datos a partir de una copia de seguridad, debes habilitar su eliminación. por separado.

Si borras tu proyecto, la protección contra la eliminación de bases de datos de Spanner no evitará que se borre tu base de datos o instancia. Más información sobre lo que sucede cuando borras tu proyecto, consulta Cómo apagar (borrar) proyectos.

Control de acceso con IAM

Para habilitar el parámetro de configuración de protección contra la eliminación de tu base de datos, debes tener ciertos permisos de IAM.

Debes tener el permiso spanner.databases.update para habilitar o inhabilitar inhabilitar la protección contra la eliminación de bases de datos. Si solo necesitas ver el estado de la configuración de la base de datos, debes tener el permiso spanner.databases.list o spanner.databases.get. Si deseas obtener información para otorgar permisos de IAM de Spanner, consulta Cómo aplicar permisos de IAM.

Si tienes el rol predefinido de Administrador de la base de datos de Spanner roles/spanner.databaseAdmin para tu base de datos, puedes actualizar y habilitar la protección contra la eliminación de la base de datos.

Puedes habilitar el parámetro de configuración de protección contra la eliminación de bases de datos en una base de datos existente para evitar que se borre accidentalmente.

Habilitar la protección contra la eliminación de la base de datos

Puedes habilitar la protección contra la eliminación de bases de datos con gcloud CLI, las bibliotecas cliente y las APIs de REST o RPC. No puedes habilitar la protección contra la eliminación de bases de datos con la consola de Google Cloud.

gcloud

Para habilitar la configuración de protección contra eliminación de una base de datos, ejecuta el siguiente comando:

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

Se requieren las siguientes opciones:

DATABASE_ID
ID de la base de datos.
INSTANCE_ID
Es el ID de la instancia de la base de datos.

Las siguientes configuraciones son opcionales:

--async
Se muestra de inmediato, sin necesidad de esperar a que se complete la operación en 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

Verifica si una base de datos tiene habilitada la protección contra la eliminación

Puedes determinar si tu base de datos tiene habilitada la protección contra la eliminación si consultas su configuración.

gcloud

Para verificar si una base de datos tiene habilitada la protección contra la eliminación, puedes ejecutar el comando gcloud spanner databases describe para obtener información detallada sobre una base de datos, o bien puedes ejecutar gcloud spanner databases list para obtener información detallada sobre las bases de datos de una instancia.

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

Se requieren las siguientes opciones:

PROJECT_ID
Es el ID del proyecto de la base de datos.
INSTANCE_ID
ID de la instancia de la base de datos.
DATABASE_ID
ID de la base de datos.

Si la protección contra la eliminación está habilitada, verás enableDropProtection: true en el resultado.

Inhabilitar la protección contra la eliminación de la base de datos

Puedes inhabilitar la protección contra la eliminación de bases de datos si una base de datos ya no lo necesita o si necesitas borrar una base de datos que tenga habilitado este parámetro.

Si deseas borrar una instancia que tiene una o más bases de datos con la protección contra la eliminación habilitada, primero debes inhabilitar la protección contra la eliminación en todas las bases de datos de esa instancia para poder borrarla.

gcloud

Para inhabilitar la configuración de protección contra la eliminación de una base de datos, ejecuta el siguiente comando:

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

Se requieren las siguientes opciones:

DATABASE_ID
ID de la base de datos.
INSTANCE_ID
Es el ID de la instancia de la base de datos.

Las siguientes configuraciones son opcionales:

--async
Regresa de inmediato, sin esperar a que la operación esté en curso. en completarse.

¿Qué sigue?