Éviter la suppression accidentelle de bases de données

Cette page explique comment protéger les bases de données Spanner contre une suppression accidentelle.

La protection contre la suppression de bases de données Spanner empêche la suppression accidentelle de bases de données existantes par les utilisateurs ou les comptes de service disposant des autorisations IAM nécessaires pour supprimer la base de données. En activant la protection contre la suppression de bases de données, vous pouvez sauvegarder les bases de données importantes pour votre application et vos services. Utilisez la protection contre la suppression de bases de données ainsi que les fonctionnalités de récupération à un moment précis et de sauvegarde afin de fournir un ensemble complet de fonctionnalités de protection des données pour vos bases de données Spanner.

Par défaut, le paramètre de protection contre la suppression est désactivé lorsque vous créez votre base de données. Vous pouvez activer le paramètre de protection contre la suppression une fois la base de données créée. Vous pouvez également activer ce paramètre sur une base de données existante. Si vous souhaitez protéger plusieurs bases de données, activez le paramètre sur chaque base de données individuellement. L'activation ou la désactivation de la protection contre la suppression n'a aucun impact sur les performances de la base de données. Si vous devez supprimer une base de données sur laquelle la protection de base de données est activée, vous devez désactiver la protection avant de pouvoir supprimer la base de données.

Limites

Vous ne pouvez pas activer la protection contre la suppression de bases de données dans les cas suivants:

  • Si la base de données est en cours de suppression
  • Si la base de données est en cours de restauration à partir d'une sauvegarde. (Une fois l'opération de restauration terminée, vous pouvez activer la protection de la base de données.)

De plus, les sauvegardes d'une base de données et celles restaurées à partir d'une sauvegarde n'héritent pas du paramètre de protection contre la suppression de base de données de leur base de données source. Après avoir restauré une base de données à partir d'une sauvegarde, vous devez activer sa protection contre la suppression de bases de données séparément.

Si vous supprimez votre projet, la protection contre la suppression de bases de données Spanner n'empêche pas la suppression de votre base de données ou de votre instance. Pour en savoir plus sur ce qui se passe lorsque vous supprimez votre projet, consultez la section Arrêter (supprimer) des projets.

Contrôle des accès avec IAM

Pour activer le paramètre de protection contre la suppression de votre base de données, vous devez disposer de certaines autorisations IAM.

Vous devez disposer de l'autorisation spanner.databases.update pour activer ou désactiver la protection contre la suppression de bases de données. Si vous avez uniquement besoin d'afficher l'état de la configuration de votre base de données, vous devez disposer de l'autorisation spanner.databases.list ou spanner.databases.get. Pour en savoir plus sur l'attribution d'autorisations IAM pour Spanner, consultez la page Appliquer des autorisations IAM.

Si vous disposez du rôle prédéfini Administrateur de bases de données Spanner roles/spanner.databaseAdmin pour votre base de données, vous pouvez mettre à jour et activer la protection contre la suppression de bases de données.

Vous pouvez activer le paramètre de protection contre la suppression de bases de données sur une base de données existante afin d'empêcher la suppression accidentelle de la base de données.

Activer la protection contre la suppression de bases de données

Vous pouvez activer la protection contre la suppression de bases de données à l'aide de la gcloud CLI, des bibliothèques clientes et des API REST ou RPC. Vous ne pouvez pas activer la protection contre la suppression de bases de données à l'aide de la console Google Cloud.

gcloud

Pour activer le paramètre de protection contre la suppression d'une base de données, exécutez la commande suivante:

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

Les options suivantes sont requises :

DATABASE_ID
ID de la base de données.
INSTANCE_ID
ID de l'instance associée à la base de données.

Les options suivantes sont facultatives :

--async
Renvoie immédiatement, sans attendre la fin de l'opération en cours.

Bibliothèques clientes

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

Vérifier si la protection contre la suppression est activée pour une base de données

Pour déterminer si la protection contre la suppression est activée pour votre base de données, consultez la configuration de la base de données.

gcloud

Pour vérifier si la protection contre la suppression est activée pour une base de données, vous pouvez exécuter la commande gcloud spanner databases describe afin d'obtenir des informations détaillées sur celle-ci, ou exécuter la commande gcloud spanner databases list pour obtenir des informations détaillées sur les bases de données d'une instance.

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

Les options suivantes sont requises :

PROJECT_ID
ID du projet pour la base de données.
INSTANCE_ID
ID de l'instance associée à la base de données.
DATABASE_ID
ID de la base de données.

Si la protection contre la suppression est activée, un paramètre enableDropProtection: true s'affiche dans le résultat.

Désactiver la protection contre la suppression de bases de données

Vous pouvez désactiver la protection contre la suppression de bases de données si une base de données n'a plus besoin de cette protection ou si vous devez supprimer une base de données sur laquelle ce paramètre est activé.

Si vous souhaitez supprimer une instance contenant une ou plusieurs bases de données avec une protection contre la suppression activée, vous devez d'abord désactiver la protection contre la suppression sur toutes les bases de données de cette instance avant de pouvoir supprimer l'instance.

gcloud

Pour désactiver le paramètre de protection contre la suppression d'une base de données, exécutez la commande suivante:

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

Les options suivantes sont requises :

DATABASE_ID
ID de la base de données.
INSTANCE_ID
ID de l'instance associée à la base de données.

Les options suivantes sont facultatives :

--async
Renvoie immédiatement, sans attendre la fin de l'opération en cours.

Étapes suivantes