blob: 6a6c8792d05bcf81fead10e2bbebb7571ac90315 [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.tests.integration;
import static java.nio.charset.StandardCharsets.UTF_8;
import static org.junit.Assert.assertTrue;
import com.github.dockerjava.api.DockerClient;
import java.util.concurrent.ExecutionException;
import lombok.extern.slf4j.Slf4j;
import org.apache.bookkeeper.client.BKException;
import org.apache.bookkeeper.client.api.BookKeeper;
import org.apache.bookkeeper.client.api.LedgerEntries;
import org.apache.bookkeeper.client.api.ReadHandle;
import org.apache.bookkeeper.client.api.WriteHandle;
import org.apache.bookkeeper.conf.ClientConfiguration;
import org.apache.bookkeeper.tests.integration.utils.BookKeeperClusterUtils;
import org.apache.bookkeeper.tests.integration.utils.DockerUtils;
import org.jboss.arquillian.junit.Arquillian;
import org.jboss.arquillian.test.api.ArquillianResource;
import org.junit.Assert;
import org.junit.FixMethodOrder;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.MethodSorters;
/**
* Test cluster related commands in bookie shell.
*/
@Slf4j
@RunWith(Arquillian.class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
public class TestBookieShellCluster extends BookieShellTestBase {
@ArquillianResource
private DockerClient docker;
private String currentVersion = System.getProperty("currentVersion");
private static final byte[] PASSWORD = "foobar".getBytes(UTF_8);
@Test
@Override
public void test000_Setup() throws Exception {
// First test to run, formats metadata and bookies
if (BookKeeperClusterUtils.metadataFormatIfNeeded(docker, currentVersion)) {
BookKeeperClusterUtils.formatAllBookies(docker, currentVersion);
}
assertTrue(BookKeeperClusterUtils.startAllBookiesWithVersion(docker, currentVersion));
}
@Test
@Override
public void test999_Teardown() {
assertTrue(BookKeeperClusterUtils.stopAllBookies(docker));
}
@Override
protected String runCommandInAnyContainer(String... cmds) throws Exception {
String bookie = BookKeeperClusterUtils.getAnyBookie();
return DockerUtils.runCommand(docker, bookie, cmds);
}
@Test
@Override
public void test001_SimpleTest() throws Exception {
super.test001_SimpleTest();
}
@Test
@Override
public void test002_ListROBookies() throws Exception {
super.test002_ListROBookies();
}
@Test
@Override
public void test003_ListRWBookies() throws Exception {
super.test003_ListRWBookies();
}
private static long writeNEntries(BookKeeper bk, int n, int numBookies) throws Exception {
try (WriteHandle writer = bk.newCreateLedgerOp().withEnsembleSize(numBookies)
.withWriteQuorumSize(numBookies).withAckQuorumSize(numBookies)
.withPassword(PASSWORD).execute().get()) {
int i = 0;
for (; i < n - 1; i++) {
writer.appendAsync(("entry" + i).getBytes(UTF_8));
}
writer.append(("entry" + i).getBytes(UTF_8));
return writer.getId();
}
}
private static void validateNEntries(BookKeeper bk, long ledgerId, int n) throws Exception {
try (ReadHandle reader = bk.newOpenLedgerOp()
.withLedgerId(ledgerId)
.withPassword(PASSWORD)
.execute().get();
LedgerEntries entries = reader.read(0, n - 1)) {
Assert.assertEquals(reader.getLastAddConfirmed(), n - 1);
for (int i = 0; i < n; i++) {
Assert.assertEquals("entry" + i, new String(entries.getEntry(i).getEntryBytes(), UTF_8));
}
}
}
/**
* These tests on being able to access cluster internals, so can't be put in test base.
*/
@Test
public void test101_RegenerateIndex() throws Exception {
String zookeeper = String.format("zk+hierarchical://%s/ledgers",
BookKeeperClusterUtils.zookeeperConnectString(docker));
int numEntries = 100;
try (BookKeeper bk = BookKeeper.newBuilder(
new ClientConfiguration().setMetadataServiceUri(zookeeper)).build()) {
log.info("Writing entries");
long ledgerId1 = writeNEntries(bk, numEntries, BookKeeperClusterUtils.allBookies().size());
long ledgerId2 = writeNEntries(bk, numEntries, BookKeeperClusterUtils.allBookies().size());
log.info("Validate that we can read back");
validateNEntries(bk, ledgerId1, numEntries);
validateNEntries(bk, ledgerId2, numEntries);
String indexFileName1 = String.format("/opt/bookkeeper/data/ledgers/current/0/%d/%d.idx",
ledgerId1, ledgerId1);
String indexFileName2 = String.format("/opt/bookkeeper/data/ledgers/current/0/%d/%d.idx",
ledgerId2, ledgerId2);
log.info("Stop bookies to flush, delete the index and start again");
assertTrue(BookKeeperClusterUtils.stopAllBookies(docker));
BookKeeperClusterUtils.runOnAllBookies(docker, "rm", indexFileName1, indexFileName2);
assertTrue(BookKeeperClusterUtils.startAllBookiesWithVersion(docker, currentVersion));
log.info("Validate that we cannot read back");
try {
validateNEntries(bk, ledgerId1, numEntries);
Assert.fail("Shouldn't have been able to find anything");
} catch (BKException.BKNoSuchLedgerExistsException e) {
// expected
}
try {
validateNEntries(bk, ledgerId2, numEntries);
Assert.fail("Shouldn't have been able to find anything");
} catch (BKException.BKNoSuchLedgerExistsException e) {
// expected
}
assertTrue(BookKeeperClusterUtils.stopAllBookies(docker));
log.info("Regenerate the index file");
BookKeeperClusterUtils.runOnAllBookies(docker,
bkScript, "shell", "regenerate-interleaved-storage-index-file",
"--ledgerIds", String.format("%d,%d", ledgerId1, ledgerId2),
"--password", new String(PASSWORD, UTF_8));
assertTrue(BookKeeperClusterUtils.startAllBookiesWithVersion(docker, currentVersion));
log.info("Validate that we can read back, after regeneration");
validateNEntries(bk, ledgerId1, numEntries);
validateNEntries(bk, ledgerId2, numEntries);
}
}
@Test
public void test102_DumpRestoreMetadata() throws Exception {
String zookeeper = String.format("zk+hierarchical://%s/ledgers",
BookKeeperClusterUtils.zookeeperConnectString(docker));
int numEntries = 100;
try (BookKeeper bk = BookKeeper.newBuilder(
new ClientConfiguration().setMetadataServiceUri(zookeeper)).build()) {
log.info("Writing entries");
long ledgerId = writeNEntries(bk, numEntries, 1);
log.info("Dumping ledger metadata to file");
String bookie = BookKeeperClusterUtils.getAnyBookie();
String dumpFile = String.format("/tmp/ledger-%d-%d", ledgerId, System.nanoTime());
DockerUtils.runCommand(docker, bookie,
bkScript, "shell", "ledgermetadata",
"--ledgerid", String.valueOf(ledgerId),
"--dumptofile", dumpFile);
log.info("Delete the ledger metadata");
bk.newDeleteLedgerOp().withLedgerId(ledgerId).execute().get();
// hopefully ledger gc doesn't kick in
log.info("Verify that we cannot open ledger");
try {
validateNEntries(bk, ledgerId, numEntries);
Assert.fail("Shouldn't have been able to find anything");
} catch (ExecutionException ee) {
Assert.assertEquals(ee.getCause().getClass(),
BKException.BKNoSuchLedgerExistsOnMetadataServerException.class);
}
log.info("Restore the ledger metadata");
DockerUtils.runCommand(docker, bookie,
bkScript, "shell", "ledgermetadata",
"--ledgerid", String.valueOf(ledgerId),
"--restorefromfile", dumpFile);
log.info("Validate that we can read back, after regeneration");
validateNEntries(bk, ledgerId, numEntries);
}
}
}