blob: b7e73b988b5459736baea4991c7b21d4c9505767 [file] [log] [blame]
/*
* 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.bookie;
import static java.nio.charset.StandardCharsets.UTF_8;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.ArgumentMatchers.same;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
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.spy;
import static org.powermock.api.mockito.PowerMockito.verifyNew;
import static org.powermock.api.mockito.PowerMockito.whenNew;
import com.google.common.collect.Maps;
import java.util.Set;
import java.util.SortedMap;
import java.util.function.Function;
import org.apache.bookkeeper.bookie.BookieShell.MyCommand;
import org.apache.bookkeeper.bookie.BookieShell.RecoverCmd;
import org.apache.bookkeeper.client.BookKeeperAdmin;
import org.apache.bookkeeper.client.api.LedgerMetadata;
import org.apache.bookkeeper.conf.ClientConfiguration;
import org.apache.bookkeeper.conf.ServerConfiguration;
import org.apache.bookkeeper.discover.RegistrationManager;
import org.apache.bookkeeper.meta.MetadataBookieDriver;
import org.apache.bookkeeper.meta.MetadataDrivers;
import org.apache.bookkeeper.tools.cli.commands.bookie.LastMarkCommand;
import org.apache.bookkeeper.tools.cli.commands.bookies.ListBookiesCommand;
import org.apache.bookkeeper.tools.cli.commands.bookies.RecoverCommand;
import org.apache.bookkeeper.tools.cli.commands.client.SimpleTestCommand;
import org.apache.bookkeeper.tools.framework.CliFlags;
import org.apache.bookkeeper.util.EntryFormatter;
import org.apache.bookkeeper.util.LedgerIdFormatter;
import org.apache.bookkeeper.versioning.LongVersion;
import org.apache.bookkeeper.versioning.Version;
import org.apache.bookkeeper.versioning.Versioned;
import org.apache.commons.cli.BasicParser;
import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.MissingArgumentException;
import org.apache.commons.cli.ParseException;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.powermock.api.mockito.PowerMockito;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;
/**
* Unit test for {@link BookieShell}.
*/
@RunWith(PowerMockRunner.class)
@PrepareForTest({ BookieShell.class, MetadataDrivers.class, RecoverCommand.class })
public class BookieShellTest {
private ClientConfiguration clientConf;
private BookieShell shell;
private BookKeeperAdmin admin;
private RegistrationManager rm;
private MetadataBookieDriver driver;
private Cookie cookie;
private Version version;
// commands
private LastMarkCommand mockLastMarkCommand;
private SimpleTestCommand.Flags mockSimpleTestFlags;
private SimpleTestCommand mockSimpleTestCommand;
private ListBookiesCommand.Flags mockListBookiesFlags;
private ListBookiesCommand mockListBookiesCommand;
@Before
public void setup() throws Exception {
// setup the required mocks before constructing bookie shell.
this.mockLastMarkCommand = mock(LastMarkCommand.class);
whenNew(LastMarkCommand.class)
.withNoArguments()
.thenReturn(mockLastMarkCommand);
// setup the mocks for simple test command
this.mockSimpleTestFlags = spy(new SimpleTestCommand.Flags());
whenNew(SimpleTestCommand.Flags.class)
.withNoArguments()
.thenReturn(mockSimpleTestFlags);
this.mockSimpleTestCommand = spy(new SimpleTestCommand());
doReturn(true).when(mockSimpleTestCommand)
.apply(any(ServerConfiguration.class), any(SimpleTestCommand.Flags.class));
whenNew(SimpleTestCommand.class)
.withParameterTypes(SimpleTestCommand.Flags.class)
.withArguments(mockSimpleTestFlags)
.thenReturn(mockSimpleTestCommand);
// setup the mocks for list bookies command
this.mockListBookiesFlags = spy(new ListBookiesCommand.Flags());
whenNew(ListBookiesCommand.Flags.class)
.withNoArguments()
.thenReturn(mockListBookiesFlags);
this.mockListBookiesCommand = spy(new ListBookiesCommand());
doReturn(true).when(mockListBookiesCommand)
.apply(any(ServerConfiguration.class), any(ListBookiesCommand.Flags.class));
whenNew(ListBookiesCommand.class)
.withParameterTypes(ListBookiesCommand.Flags.class)
.withArguments(mockListBookiesFlags)
.thenReturn(mockListBookiesCommand);
// construct the bookie shell.
this.shell = new BookieShell(LedgerIdFormatter.LONG_LEDGERID_FORMATTER, EntryFormatter.STRING_FORMATTER);
this.admin = PowerMockito.mock(BookKeeperAdmin.class);
whenNew(BookKeeperAdmin.class)
.withParameterTypes(ClientConfiguration.class)
.withArguments(any(ClientConfiguration.class))
.thenReturn(admin);
this.clientConf = new ClientConfiguration();
this.clientConf.setMetadataServiceUri("zk://127.0.0.1/path/to/ledgers");
when(admin.getConf()).thenReturn(this.clientConf);
this.rm = PowerMockito.mock(RegistrationManager.class);
this.cookie = Cookie.newBuilder()
.setBookieHost("127.0.0.1:3181")
.setInstanceId("xyz")
.setJournalDirs("/path/to/journal/dir")
.setLedgerDirs("/path/to/journal/dir")
.setLayoutVersion(Cookie.CURRENT_COOKIE_LAYOUT_VERSION)
.build();
this.version = new LongVersion(1L);
when(rm.readCookie(anyString()))
.thenReturn(new Versioned<>(cookie.toString().getBytes(UTF_8), version));
this.driver = mock(MetadataBookieDriver.class);
when(driver.getRegistrationManager())
.thenReturn(rm);
PowerMockito.mockStatic(MetadataDrivers.class);
PowerMockito.doAnswer(invocationOnMock -> {
Function<RegistrationManager, Object> function = invocationOnMock.getArgument(1);
function.apply(rm);
return null;
}).when(
MetadataDrivers.class,
"runFunctionWithRegistrationManager",
any(ServerConfiguration.class),
any(Function.class)
);
}
private static CommandLine parseCommandLine(MyCommand cmd, String... args) throws ParseException {
BasicParser parser = new BasicParser();
return parser.parse(cmd.getOptions(), args);
}
@Test
public void testRecoverCmdMissingArgument() throws Exception {
RecoverCmd cmd = (RecoverCmd) shell.commands.get("recover");
CommandLine cmdLine = parseCommandLine(cmd);
try {
cmd.runCmd(cmdLine);
fail("should fail running command when the arguments are missing");
} catch (MissingArgumentException e) {
// expected
}
PowerMockito.verifyNew(BookKeeperAdmin.class, never()).withArguments(any(ClientConfiguration.class));
}
@Test
public void testRecoverCmdInvalidBookieAddress() throws Exception {
RecoverCmd cmd = (RecoverCmd) shell.commands.get("recover");
CommandLine cmdLine = parseCommandLine(cmd, "127.0.0.1");
assertEquals(-1, cmd.runCmd(cmdLine));
PowerMockito.verifyNew(BookKeeperAdmin.class, never()).withArguments(any(ClientConfiguration.class));
}
@SuppressWarnings("unchecked")
@Test
public void testRecoverCmdQuery() throws Exception {
SortedMap<Long, LedgerMetadata> ledgersContainBookies = Maps.newTreeMap();
when(admin.getLedgersContainBookies(any(Set.class)))
.thenReturn(ledgersContainBookies);
RecoverCmd cmd = (RecoverCmd) shell.commands.get("recover");
CommandLine cmdLine = parseCommandLine(cmd, "-force", "-q", "127.0.0.1:3181");
assertEquals(0, cmd.runCmd(cmdLine));
PowerMockito
.verifyNew(BookKeeperAdmin.class, times(1))
.withArguments(any(ClientConfiguration.class));
verify(admin, times(1)).getLedgersContainBookies(any(Set.class));
verify(admin, times(1)).close();
}
@Test
public void testRecoverCmdRecoverLedgerDefault() throws Exception {
// default behavior
testRecoverCmdRecoverLedger(
12345, false, false, false,
"-force", "-l", "12345", "127.0.0.1:3181");
}
@Test
public void testRecoverCmdRecoverLedgerDeleteCookie() throws Exception {
// dryrun
testRecoverCmdRecoverLedger(
12345, false, false, true,
"-force", "-l", "12345", "-deleteCookie", "127.0.0.1:3181");
}
@Test
public void testRecoverCmdRecoverLedgerSkipOpenLedgersDeleteCookie() throws Exception {
// dryrun
testRecoverCmdRecoverLedger(
12345, false, true, true,
"-force", "-l", "12345", "-deleteCookie", "-skipOpenLedgers", "127.0.0.1:3181");
}
@Test
public void testRecoverCmdRecoverLedgerDryrun() throws Exception {
// dryrun
testRecoverCmdRecoverLedger(
12345, true, false, false,
"-force", "-l", "12345", "-dryrun", "127.0.0.1:3181");
}
@Test
public void testRecoverCmdRecoverLedgerDryrunDeleteCookie() throws Exception {
// dryrun & removeCookie : removeCookie should be false
testRecoverCmdRecoverLedger(
12345, true, false, false,
"-force", "-l", "12345", "-dryrun", "-deleteCookie", "127.0.0.1:3181");
}
@SuppressWarnings("unchecked")
void testRecoverCmdRecoverLedger(long ledgerId,
boolean dryrun,
boolean skipOpenLedgers,
boolean removeCookies,
String... args) throws Exception {
RecoverCmd cmd = (RecoverCmd) shell.commands.get("recover");
CommandLine cmdLine = parseCommandLine(cmd, args);
assertEquals(0, cmd.runCmd(cmdLine));
PowerMockito
.verifyNew(BookKeeperAdmin.class, times(1))
.withArguments(any(ClientConfiguration.class));
verify(admin, times(1))
.recoverBookieData(eq(ledgerId), any(Set.class), eq(dryrun), eq(skipOpenLedgers));
verify(admin, times(1)).close();
if (removeCookies) {
PowerMockito.verifyStatic(MetadataDrivers.class);
MetadataDrivers.runFunctionWithRegistrationManager(any(ServerConfiguration.class), any(Function.class));
verify(rm, times(1)).readCookie(anyString());
verify(rm, times(1)).removeCookie(anyString(), eq(version));
} else {
verify(rm, times(0)).readCookie(anyString());
verify(rm, times(0)).removeCookie(anyString(), eq(version));
}
}
@Test
public void testRecoverCmdRecoverDefault() throws Exception {
// default behavior
testRecoverCmdRecover(
false, false, false,
"-force", "127.0.0.1:3181");
}
@Test
public void testRecoverCmdRecoverDeleteCookie() throws Exception {
// dryrun
testRecoverCmdRecover(
false, false, true,
"-force", "-deleteCookie", "127.0.0.1:3181");
}
@Test
public void testRecoverCmdRecoverSkipOpenLedgersDeleteCookie() throws Exception {
// dryrun
testRecoverCmdRecover(
false, true, true,
"-force", "-deleteCookie", "-skipOpenLedgers", "127.0.0.1:3181");
}
@Test
public void testRecoverCmdRecoverDryrun() throws Exception {
// dryrun
testRecoverCmdRecover(
true, false, false,
"-force", "-dryrun", "127.0.0.1:3181");
}
@Test
public void testRecoverCmdRecoverDryrunDeleteCookie() throws Exception {
// dryrun & removeCookie : removeCookie should be false
testRecoverCmdRecover(
true, false, false,
"-force", "-dryrun", "-deleteCookie", "127.0.0.1:3181");
}
@SuppressWarnings("unchecked")
void testRecoverCmdRecover(boolean dryrun,
boolean skipOpenLedgers,
boolean removeCookies,
String... args) throws Exception {
RecoverCmd cmd = (RecoverCmd) shell.commands.get("recover");
CommandLine cmdLine = parseCommandLine(cmd, args);
assertEquals(0, cmd.runCmd(cmdLine));
PowerMockito
.verifyNew(BookKeeperAdmin.class, times(1))
.withArguments(any(ClientConfiguration.class));
verify(admin, times(1))
.recoverBookieData(any(Set.class), eq(dryrun), eq(skipOpenLedgers));
verify(admin, times(1)).close();
if (removeCookies) {
PowerMockito.verifyStatic(MetadataDrivers.class);
MetadataDrivers.runFunctionWithRegistrationManager(any(ServerConfiguration.class), any(Function.class));
verify(rm, times(1)).readCookie(anyString());
verify(rm, times(1)).removeCookie(anyString(), eq(version));
} else {
verify(rm, times(0)).readCookie(anyString());
verify(rm, times(0)).removeCookie(anyString(), eq(version));
}
}
@Test
public void testLastMarkCmd() throws Exception {
shell.run(new String[] { "lastmark"});
verifyNew(LastMarkCommand.class, times(1)).withNoArguments();
verify(mockLastMarkCommand, times(1))
.apply(same(shell.bkConf), any(CliFlags.class));
}
@Test
public void testSimpleTestCmd() throws Exception {
shell.run(new String[] {
"simpletest",
"-e", "10",
"-w", "5",
"-a", "3",
"-n", "200"
});
verifyNew(SimpleTestCommand.class, times(1))
.withArguments(same(mockSimpleTestFlags));
verify(mockSimpleTestCommand, times(1))
.apply(same(shell.bkConf), same(mockSimpleTestFlags));
verify(mockSimpleTestFlags, times(1)).ensembleSize(eq(10));
verify(mockSimpleTestFlags, times(1)).writeQuorumSize(eq(5));
verify(mockSimpleTestFlags, times(1)).ackQuorumSize(eq(3));
verify(mockSimpleTestFlags, times(1)).numEntries(eq(200));
}
@Test
public void testListBookiesCmdNoArgs() throws Exception {
assertEquals(1, shell.run(new String[] {
"listbookies"
}));
verifyNew(ListBookiesCommand.class, times(0)).withNoArguments();
}
@Test
public void testListBookiesCmdConflictArgs() throws Exception {
assertEquals(1, shell.run(new String[] {
"listbookies", "-rw", "-ro"
}));
verifyNew(ListBookiesCommand.class, times(0)).withNoArguments();
}
@Test
public void testListBookiesCmdReadOnly() throws Exception {
assertEquals(0, shell.run(new String[] {
"listbookies", "-ro"
}));
verifyNew(ListBookiesCommand.class, times(1))
.withArguments(same(mockListBookiesFlags));
verify(mockListBookiesCommand, times(1))
.apply(same(shell.bkConf), same(mockListBookiesFlags));
verify(mockListBookiesFlags, times(1)).readonly(true);
verify(mockListBookiesFlags, times(1)).readwrite(false);
verify(mockListBookiesFlags, times(1)).all(false);
}
@Test
public void testListBookiesCmdReadWrite() throws Exception {
assertEquals(0, shell.run(new String[] {
"listbookies", "-rw"
}));
verifyNew(ListBookiesCommand.class, times(1))
.withArguments(same(mockListBookiesFlags));
verify(mockListBookiesCommand, times(1))
.apply(same(shell.bkConf), same(mockListBookiesFlags));
verify(mockListBookiesFlags, times(1)).readonly(false);
verify(mockListBookiesFlags, times(1)).readwrite(true);
verify(mockListBookiesFlags, times(1)).all(false);
}
@Test
public void testListBookiesCmdAll() throws Exception {
assertEquals(0, shell.run(new String[] {
"listbookies", "-a"
}));
verifyNew(ListBookiesCommand.class, times(1))
.withArguments(same(mockListBookiesFlags));
verify(mockListBookiesCommand, times(1))
.apply(same(shell.bkConf), same(mockListBookiesFlags));
verify(mockListBookiesFlags, times(1)).readonly(false);
verify(mockListBookiesFlags, times(1)).readwrite(false);
verify(mockListBookiesFlags, times(1)).all(true);
}
}