diff --git a/bookkeeper-server/src/main/java/org/apache/bookkeeper/bookie/BookieShell.java b/bookkeeper-server/src/main/java/org/apache/bookkeeper/bookie/BookieShell.java index c24be832822..927c2a33cd6 100644 --- a/bookkeeper-server/src/main/java/org/apache/bookkeeper/bookie/BookieShell.java +++ b/bookkeeper-server/src/main/java/org/apache/bookkeeper/bookie/BookieShell.java @@ -48,6 +48,7 @@ import org.apache.bookkeeper.replication.ReplicationException; import org.apache.bookkeeper.tools.cli.commands.autorecovery.ListUnderReplicatedCommand; import org.apache.bookkeeper.tools.cli.commands.autorecovery.LostBookieRecoveryDelayCommand; +import org.apache.bookkeeper.tools.cli.commands.autorecovery.MarkLedgerReplicatedCommand; import org.apache.bookkeeper.tools.cli.commands.autorecovery.QueryAutoRecoveryStatusCommand; import org.apache.bookkeeper.tools.cli.commands.autorecovery.ToggleCommand; import org.apache.bookkeeper.tools.cli.commands.autorecovery.TriggerAuditCommand; @@ -133,6 +134,7 @@ public class BookieShell implements Tool { static final String CMD_LISTLEDGERS = "listledgers"; static final String CMD_LEDGERMETADATA = "ledgermetadata"; static final String CMD_LISTUNDERREPLICATED = "listunderreplicated"; + static final String CMD_MARKLEDGERREPLICATED = "markledgerreplicated"; static final String CMD_WHOISAUDITOR = "whoisauditor"; static final String CMD_WHATISINSTANCEID = "whatisinstanceid"; static final String CMD_SIMPLETEST = "simpletest"; @@ -702,6 +704,48 @@ int runCmd(CommandLine cmdLine) throws Exception { } } + /** + * Command to delete a given ledger. + */ + class MarkLedgerReplicatedCmd extends MyCommand { + + MarkLedgerReplicatedCmd() { + super(CMD_MARKLEDGERREPLICATED); + opts.addOption("l", "ledgerid", true, "Ledger ID"); + opts.addOption("f", "force", false, "Whether to force mark the Ledger replicated without prompt..?"); + } + + @Override + public int runCmd(CommandLine cmdLine) throws Exception { + final long lid = getOptionLedgerIdValue(cmdLine, "ledgerid", -1); + + boolean force = cmdLine.hasOption("f"); + MarkLedgerReplicatedCommand cmd = new MarkLedgerReplicatedCommand(ledgerIdFormatter); + + MarkLedgerReplicatedCommand.MarkLedgerReplicatedFlags flags = + new MarkLedgerReplicatedCommand.MarkLedgerReplicatedFlags() + .ledgerId(lid).force(force); + cmd.apply(bkConf, flags); + + return 0; + } + + @Override + String getDescription() { + return "Mark a ledger replicated."; + } + + @Override + String getUsage() { + return "markledgerreplicated -ledgerid [-force]"; + } + + @Override + Options getOptions() { + return opts; + } + } + static final int LIST_BATCH_SIZE = 1000; /** @@ -2287,6 +2331,7 @@ int runCmd(CommandLine cmdLine) throws Exception { commands.put(CMD_LISTLEDGERS, new ListLedgersCmd()); commands.put(CMD_ACTIVE_LEDGERS_ON_ENTRY_LOG_FILE, new ListActiveLedgersCmd()); commands.put(CMD_LISTUNDERREPLICATED, new ListUnderreplicatedCmd()); + commands.put(CMD_MARKLEDGERREPLICATED, new MarkLedgerReplicatedCmd()); commands.put(CMD_WHOISAUDITOR, new WhoIsAuditorCmd()); commands.put(CMD_WHATISINSTANCEID, new WhatIsInstanceId()); commands.put(CMD_LEDGERMETADATA, new LedgerMetadataCmd()); diff --git a/bookkeeper-server/src/main/java/org/apache/bookkeeper/tools/cli/commands/autorecovery/ListUnderReplicatedCommand.java b/bookkeeper-server/src/main/java/org/apache/bookkeeper/tools/cli/commands/autorecovery/ListUnderReplicatedCommand.java index 09077cef700..7662b4e5ce2 100644 --- a/bookkeeper-server/src/main/java/org/apache/bookkeeper/tools/cli/commands/autorecovery/ListUnderReplicatedCommand.java +++ b/bookkeeper-server/src/main/java/org/apache/bookkeeper/tools/cli/commands/autorecovery/ListUnderReplicatedCommand.java @@ -78,7 +78,7 @@ private ListUnderReplicatedCommand(LURFlags flags) { */ @Accessors(fluent = true) @Setter - public static class LURFlags extends CliFlags{ + public static class LURFlags extends CliFlags { @Parameter(names = { "-pmr", "--printmissingreplica" }, description = "Whether to print missingreplicas list?") private boolean printMissingReplica; diff --git a/bookkeeper-server/src/main/java/org/apache/bookkeeper/tools/cli/commands/autorecovery/MarkLedgerReplicatedCommand.java b/bookkeeper-server/src/main/java/org/apache/bookkeeper/tools/cli/commands/autorecovery/MarkLedgerReplicatedCommand.java new file mode 100644 index 00000000000..a6f53f449b4 --- /dev/null +++ b/bookkeeper-server/src/main/java/org/apache/bookkeeper/tools/cli/commands/autorecovery/MarkLedgerReplicatedCommand.java @@ -0,0 +1,135 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.bookkeeper.tools.cli.commands.autorecovery; + +import static org.apache.bookkeeper.meta.MetadataDrivers.runFunctionWithLedgerManagerFactory; + +import com.beust.jcommander.Parameter; +import com.google.common.util.concurrent.UncheckedExecutionException; +import java.io.IOException; +import java.io.UncheckedIOException; +import java.util.concurrent.ExecutionException; +import lombok.Setter; +import lombok.experimental.Accessors; +import org.apache.bookkeeper.conf.ServerConfiguration; +import org.apache.bookkeeper.meta.LedgerUnderreplicationManager; +import org.apache.bookkeeper.meta.exceptions.MetadataException; +import org.apache.bookkeeper.replication.ReplicationException; +import org.apache.bookkeeper.tools.cli.helpers.BookieCommand; +import org.apache.bookkeeper.tools.framework.CliFlags; +import org.apache.bookkeeper.tools.framework.CliSpec; +import org.apache.bookkeeper.util.IOUtils; +import org.apache.bookkeeper.util.LedgerIdFormatter; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class MarkLedgerReplicatedCommand extends BookieCommand { + + static final Logger LOG = LoggerFactory.getLogger(MarkLedgerReplicatedCommand.class); + private static final String NAME = "markledgerreplicated"; + private static final String DESC = "Mark a ledger replicated."; + private static final String DEFAULT = ""; + private LedgerIdFormatter ledgerIdFormatter; + + public MarkLedgerReplicatedCommand() { + this(new MarkLedgerReplicatedCommand.MarkLedgerReplicatedFlags()); + } + + public MarkLedgerReplicatedCommand(LedgerIdFormatter ledgerIdFormatter) { + this(new MarkLedgerReplicatedCommand.MarkLedgerReplicatedFlags()); + this.ledgerIdFormatter = ledgerIdFormatter; + } + + private MarkLedgerReplicatedCommand(MarkLedgerReplicatedCommand.MarkLedgerReplicatedFlags flags) { + super(CliSpec.newBuilder().withName(NAME) + .withDescription(DESC).withFlags(flags).build()); + } + + /** + * Flags for delete ledger command. + */ + @Accessors(fluent = true) + @Setter + public static class MarkLedgerReplicatedFlags extends CliFlags { + + @Parameter(names = {"-l", "--ledgerid"}, description = "Ledger ID", required = true) + private long ledgerId; + + @Parameter(names = {"-f", + "--force"}, description = "Whether to force mark the Ledger replicated without prompt..?") + private boolean force; + + @Parameter(names = {"-lf", "--ledgeridformatter"}, description = "Set ledger id formatter") + private String ledgerIdFormatter = DEFAULT; + } + + @Override + public boolean apply(ServerConfiguration conf, MarkLedgerReplicatedFlags cmdFlags) { + initLedgerIdFormatter(conf, cmdFlags); + try { + return markLedgerReplicated(conf, cmdFlags); + } catch (Exception e) { + throw new UncheckedExecutionException(e.getMessage(), e); + } + } + + private void initLedgerIdFormatter(ServerConfiguration conf, + MarkLedgerReplicatedCommand.MarkLedgerReplicatedFlags flags) { + if (null == ledgerIdFormatter && !flags.ledgerIdFormatter.equals(DEFAULT)) { + this.ledgerIdFormatter = LedgerIdFormatter.newLedgerIdFormatter(flags.ledgerIdFormatter, conf); + } else if (null == ledgerIdFormatter && flags.ledgerIdFormatter.equals(DEFAULT)) { + this.ledgerIdFormatter = LedgerIdFormatter.newLedgerIdFormatter(conf); + } + } + + private boolean markLedgerReplicated(ServerConfiguration bkConf, + MarkLedgerReplicatedCommand.MarkLedgerReplicatedFlags flags) + throws ExecutionException, MetadataException { + return runFunctionWithLedgerManagerFactory(bkConf, mFactory -> { + LedgerUnderreplicationManager underreplicationManager; + try { + underreplicationManager = mFactory.newLedgerUnderreplicationManager(); + if (flags.ledgerId < 0) { + LOG.error("Ledger id error."); + return false; + } + boolean confirm = false; + if (!flags.force) { + confirm = IOUtils.confirmPrompt( + "Are your sure to mark Ledger:" + ledgerIdFormatter.formatLedgerId(flags.ledgerId) + + " replicated?"); + } + if (flags.force || confirm) { + underreplicationManager.markLedgerReplicated(flags.ledgerId); + return true; + } else { + return false; + } + } catch (ReplicationException e) { + throw new UncheckedExecutionException("Failed to new ledger underreplicated manager", e); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + throw new UncheckedExecutionException("Interrupted on newing ledger underreplicated manager", e); + } catch (IOException e) { + throw new UncheckedIOException(e); + } + }); + } + +} diff --git a/bookkeeper-server/src/main/java/org/apache/bookkeeper/tools/cli/commands/client/DeleteLedgerCommand.java b/bookkeeper-server/src/main/java/org/apache/bookkeeper/tools/cli/commands/client/DeleteLedgerCommand.java index f53b6650d9f..33ff4210904 100644 --- a/bookkeeper-server/src/main/java/org/apache/bookkeeper/tools/cli/commands/client/DeleteLedgerCommand.java +++ b/bookkeeper-server/src/main/java/org/apache/bookkeeper/tools/cli/commands/client/DeleteLedgerCommand.java @@ -121,13 +121,14 @@ private boolean deleteLedger(ServerConfiguration conf, DeleteLedgerFlags flags) configuration.addConfiguration(conf); bookKeeper = new BookKeeper(configuration); bookKeeper.deleteLedger(flags.ledgerId); + return true; + } else { + return false; } } finally { if (bookKeeper != null) { bookKeeper.close(); } } - - return true; } } diff --git a/tools/ledger/src/main/java/org/apache/bookkeeper/tools/cli/commands/AutoRecoveryCommandGroup.java b/tools/ledger/src/main/java/org/apache/bookkeeper/tools/cli/commands/AutoRecoveryCommandGroup.java index 7cc88ea7dfd..ab69324261d 100644 --- a/tools/ledger/src/main/java/org/apache/bookkeeper/tools/cli/commands/AutoRecoveryCommandGroup.java +++ b/tools/ledger/src/main/java/org/apache/bookkeeper/tools/cli/commands/AutoRecoveryCommandGroup.java @@ -22,6 +22,7 @@ import org.apache.bookkeeper.tools.cli.commands.autorecovery.ListUnderReplicatedCommand; import org.apache.bookkeeper.tools.cli.commands.autorecovery.LostBookieRecoveryDelayCommand; +import org.apache.bookkeeper.tools.cli.commands.autorecovery.MarkLedgerReplicatedCommand; import org.apache.bookkeeper.tools.cli.commands.autorecovery.ToggleCommand; import org.apache.bookkeeper.tools.cli.commands.autorecovery.TriggerAuditCommand; import org.apache.bookkeeper.tools.cli.commands.autorecovery.WhoIsAuditorCommand; @@ -47,6 +48,7 @@ public class AutoRecoveryCommandGroup extends CliCommandGroup { .addCommand(new TriggerAuditCommand()) .addCommand(new ListUnderReplicatedCommand()) .addCommand(new LostBookieRecoveryDelayCommand()) + .addCommand(new MarkLedgerReplicatedCommand()) .build(); public AutoRecoveryCommandGroup() { diff --git a/tools/ledger/src/test/java/org/apache/bookkeeper/tools/cli/commands/autorecovery/MarkLedgerReplicatedCommandTest.java b/tools/ledger/src/test/java/org/apache/bookkeeper/tools/cli/commands/autorecovery/MarkLedgerReplicatedCommandTest.java new file mode 100644 index 00000000000..42c376ef1e6 --- /dev/null +++ b/tools/ledger/src/test/java/org/apache/bookkeeper/tools/cli/commands/autorecovery/MarkLedgerReplicatedCommandTest.java @@ -0,0 +1,93 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.bookkeeper.tools.cli.commands.autorecovery; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.anyLong; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; +import static org.powermock.api.mockito.PowerMockito.doNothing; + +import org.apache.bookkeeper.meta.LedgerManagerFactory; +import org.apache.bookkeeper.meta.LedgerUnderreplicationManager; +import org.apache.bookkeeper.replication.ReplicationException; +import org.apache.bookkeeper.tools.cli.helpers.BookieCommandTestBase; +import org.apache.bookkeeper.util.IOUtils; +import org.junit.Test; + +/** + * Unit test for {@link MarkLedgerReplicatedCommand}. + */ +public class MarkLedgerReplicatedCommandTest extends BookieCommandTestBase { + + private LedgerManagerFactory factory; + private LedgerUnderreplicationManager underreplicationManager; + + public MarkLedgerReplicatedCommandTest() { + super(3, 0); + } + + @Override + public void setup() throws Exception { + super.setup(); + mockStatic(IOUtils.class); + + factory = mock(LedgerManagerFactory.class); + mockMetadataDriversWithLedgerManagerFactory(factory); + underreplicationManager = mock(LedgerUnderreplicationManager.class); + when(factory.newLedgerUnderreplicationManager()).thenReturn(underreplicationManager); + doNothing().when(underreplicationManager).markLedgerReplicated(anyLong()); + } + + @Test + public void testCommandWithoutForceAndConfirmNo() throws InterruptedException, ReplicationException { + getMockedStatic(IOUtils.class).when(() -> IOUtils.confirmPrompt(anyString())).thenReturn(false); + + MarkLedgerReplicatedCommand cmd = new MarkLedgerReplicatedCommand(); + assertFalse(cmd.apply(bkFlags, new String[] { "-l", "1" })); + + verify(factory, times(1)).newLedgerUnderreplicationManager(); + verify(underreplicationManager, times(0)).markLedgerReplicated(1L); + } + + @Test + public void testCommandWithoutForceAndConfirmYes() throws InterruptedException, ReplicationException { + getMockedStatic(IOUtils.class).when(() -> IOUtils.confirmPrompt(anyString())).thenReturn(true); + + MarkLedgerReplicatedCommand cmd = new MarkLedgerReplicatedCommand(); + assertTrue(cmd.apply(bkFlags, new String[] { "-l", "1" })); + + verify(factory, times(1)).newLedgerUnderreplicationManager(); + verify(underreplicationManager, times(1)).markLedgerReplicated(1L); + } + + @Test + public void testCommandWithForce() throws InterruptedException, ReplicationException { + MarkLedgerReplicatedCommand cmd = new MarkLedgerReplicatedCommand(); + assertTrue(cmd.apply(bkFlags, new String[] { "-l", "1", "-f" })); + + verify(factory, times(1)).newLedgerUnderreplicationManager(); + verify(underreplicationManager, times(1)).markLedgerReplicated(1L); + } + +} diff --git a/tools/ledger/src/test/java/org/apache/bookkeeper/tools/cli/commands/client/DeleteLedgerCommandTest.java b/tools/ledger/src/test/java/org/apache/bookkeeper/tools/cli/commands/client/DeleteLedgerCommandTest.java index cb31b110767..cbeebaf5320 100644 --- a/tools/ledger/src/test/java/org/apache/bookkeeper/tools/cli/commands/client/DeleteLedgerCommandTest.java +++ b/tools/ledger/src/test/java/org/apache/bookkeeper/tools/cli/commands/client/DeleteLedgerCommandTest.java @@ -18,6 +18,7 @@ */ package org.apache.bookkeeper.tools.cli.commands.client; +import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyLong; @@ -35,7 +36,6 @@ import org.apache.commons.configuration.Configuration; import org.junit.Test; - /** * Unit test for {@link DeleteLedgerCommand}. */ @@ -64,7 +64,7 @@ public void testCommandWithoutForce() throws Exception { }); DeleteLedgerCommand cmd = new DeleteLedgerCommand(); - assertTrue(cmd.apply(bkFlags, new String[] { "-l", "1" })); + assertFalse(cmd.apply(bkFlags, new String[] { "-l", "1" })); assertTrue(getMockedConstruction(BookKeeper.class).constructed().isEmpty()); } diff --git a/tools/ledger/src/test/java/org/apache/bookkeeper/tools/cli/helpers/CommandTestBase.java b/tools/ledger/src/test/java/org/apache/bookkeeper/tools/cli/helpers/CommandTestBase.java index 2e774d45187..10452ce34cc 100644 --- a/tools/ledger/src/test/java/org/apache/bookkeeper/tools/cli/helpers/CommandTestBase.java +++ b/tools/ledger/src/test/java/org/apache/bookkeeper/tools/cli/helpers/CommandTestBase.java @@ -85,8 +85,7 @@ protected void mockMetadataDriversWithLedgerManagerFactory(LedgerManagerFactory .runFunctionWithLedgerManagerFactory(any(ServerConfiguration.class), any(Function.class)) ).then(invocation -> { Function func = invocation.getArgument(1); - func.apply(ledgerManagerFactory); - return true; + return func.apply(ledgerManagerFactory); }); }