mirror of https://github.com/OpenIdentityPlatform/OpenDJ.git

Valery Kharseko
yesterday 7313588e280915ed5ce879761e16ad398488f54c
[#1025] Stop a restore which cannot lock its backend instead of completing it (#1027)
2 files modified
120 ■■■■■ changed files
opendj-server-legacy/src/main/java/org/opends/server/tasks/RestoreTask.java 10 ●●●● patch | view | raw | blame | history
opendj-server-legacy/src/test/java/org/opends/server/tasks/TestBackupAndRestore.java 110 ●●●●● patch | view | raw | blame | history
opendj-server-legacy/src/main/java/org/opends/server/tasks/RestoreTask.java
@@ -289,9 +289,14 @@
        }
      }
      // Acquire an exclusive lock for the backend.
      if (verifyOnly || lockBackend(backend))
      // Acquire an exclusive lock for the backend. Without it nothing is restored, and the
      // task must say so rather than complete as if it had.
      if (!verifyOnly && !lockBackend(backend))
      {
        errorsEncountered = true;
        return TaskState.STOPPED_BY_ERROR;
      }
        // From here we must make sure to release the backend exclusive lock.
        try
        {
@@ -320,7 +325,6 @@
          }
        }
      }
    }
    finally
    {
      // Enable the backend, if it was this task which disabled it.
opendj-server-legacy/src/test/java/org/opends/server/tasks/TestBackupAndRestore.java
@@ -13,6 +13,7 @@
 *
 * Copyright 2006-2008 Sun Microsystems, Inc.
 * Portions Copyright 2014-2016 ForgeRock AS.
 * Portions Copyright 2026 3A Systems, LLC.
 */
package org.opends.server.tasks;
@@ -20,10 +21,16 @@
import java.util.ArrayList;
import java.util.Arrays;
import java.util.UUID;
import java.util.concurrent.atomic.AtomicReference;
import org.opends.server.TestCaseUtils;
import org.opends.server.api.LocalBackend;
import org.opends.server.api.RestoreTaskListener;
import org.opends.server.backends.task.TaskState;
import org.opends.server.core.DirectoryServer;
import org.opends.server.core.LockFileManager;
import org.opends.server.types.Entry;
import org.opends.server.types.RestoreConfig;
import org.forgerock.opendj.ldap.schema.ObjectClass;
import org.testng.annotations.AfterClass;
import org.testng.annotations.BeforeClass;
@@ -210,4 +217,107 @@
    }
  }
  /**
   * A restore which cannot lock its backend has restored nothing and must say so: the task
   * ends STOPPED_BY_ERROR and the restore task listeners are told the restore failed, as they
   * are when the restore itself fails.
   */
  @Test
  public void testRestoreEndsInErrorWhenTheBackendCannotBeLocked() throws Exception
  {
    File backupDirectory = TestCaseUtils.createTemporaryDirectory("restore-lock");
    try
    {
      testTask(TestCaseUtils.makeEntry(
          "dn: ds-task-id=" + UUID.randomUUID() + ",cn=Scheduled Tasks,cn=Tasks",
          "objectclass: top",
          "objectclass: ds-task",
          "objectclass: ds-task-backup",
          "ds-task-class-name: org.opends.server.tasks.BackupTask",
          "ds-task-backup-backend-id: userRoot",
          "ds-backup-directory-path: " + backupDirectory.getPath()),
          TaskState.COMPLETED_SUCCESSFULLY, 30);
      final AtomicReference<Boolean> restoreSuccessful = new AtomicReference<>();
      RestoreTaskListener outcome = new RestoreTaskListener()
      {
        @Override
        public void processRestoreBegin(LocalBackend<?> backend, RestoreConfig config)
        {
        }
        @Override
        public void processRestoreEnd(LocalBackend<?> backend, RestoreConfig config, boolean successful)
        {
          restoreSuccessful.set(successful);
        }
      };
      final int restoreBeginCountStart = restoreBeginCount.get();
      final int restoreEndCountStart = restoreEndCount.get();
      /*
       * Hold the backend lock as another shared holder would: the task takes the backend
       * offline, which releases the backend's own reference but not this one, so the
       * exclusive lock it needs to restore is refused.
       */
      LocalBackend<?> userRoot = TestCaseUtils.getServerContext().getBackendConfigManager().getLocalBackendById("userRoot");
      String lockFile = LockFileManager.getBackendLockFileName(userRoot);
      StringBuilder failureReason = new StringBuilder();
      assertTrue(LockFileManager.acquireSharedLock(lockFile, failureReason), failureReason.toString());
      DirectoryServer.registerRestoreTaskListener(outcome);
      try
      {
        testTask(TestCaseUtils.makeEntry(restoreTask(
            "ds-backup-directory-path: " + backupDirectory.getPath())),
            TaskState.STOPPED_BY_ERROR, 30);
      }
      finally
      {
        DirectoryServer.deregisterRestoreTaskListener(outcome);
        LockFileManager.releaseLock(lockFile, new StringBuilder());
      }
      assertEquals(restoreBeginCount.get(), restoreBeginCountStart + 1);
      assertEquals(restoreEndCount.get(), restoreEndCountStart + 1);
      assertEquals(restoreSuccessful.get(), Boolean.FALSE);
      // The backend the task took offline is back.
      assertNotNull(TestCaseUtils.getServerContext().getBackendConfigManager().getLocalBackendById("userRoot"));
    }
    finally
    {
      TestCaseUtils.deleteDirectory(backupDirectory);
    }
  }
  /**
   * A verify-only restore never takes the backend lock: the backend stays enabled and keeps the
   * shared lock it was given when it was enabled, which an exclusive lock request is refused over.
   */
  @Test
  public void testVerifyOnlyRestoreDoesNotTakeTheBackendLock() throws Exception
  {
    File backupDirectory = TestCaseUtils.createTemporaryDirectory("restore-verify-only");
    try
    {
      testTask(TestCaseUtils.makeEntry(
          "dn: ds-task-id=" + UUID.randomUUID() + ",cn=Scheduled Tasks,cn=Tasks",
          "objectclass: top",
          "objectclass: ds-task",
          "objectclass: ds-task-backup",
          "ds-task-class-name: org.opends.server.tasks.BackupTask",
          "ds-task-backup-backend-id: userRoot",
          "ds-backup-directory-path: " + backupDirectory.getPath()),
          TaskState.COMPLETED_SUCCESSFULLY, 30);
      testTask(TestCaseUtils.makeEntry(restoreTask(
          "ds-backup-directory-path: " + backupDirectory.getPath(),
          "ds-task-restore-verify-only: true")),
          TaskState.COMPLETED_SUCCESSFULLY, 30);
    }
    finally
    {
      TestCaseUtils.deleteDirectory(backupDirectory);
    }
  }
}