This is an automated email from the ASF dual-hosted git repository.
sijie pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/bookkeeper.git
The following commit(s) were added to refs/heads/master by this push:
new 0666215 Migrate command `updatecookie`
0666215 is described below
commit 066621507f2d40ea67aa7d7ddadbb9304db17993
Author: Yong Zhang <[email protected]>
AuthorDate: Fri Apr 19 08:30:26 2019 +0800
Migrate command `updatecookie`
Descriptions of the changes in this PR:
#2059
### Description
Provide some admin operation about cookie
```
Command to update cookie
Usage: bkctl cookie admin [flags]
Flags:
-d, --delete
Delete cookie both locally and in zooKeeper
-e, --expandstorage
Add new empty ledger/index directories
-f, --force
Force delete cookie
-host, --hostname
Expects config useHostNameAsBookieID=true as the option value
-l, --list
List paths of all the cookies present locally and on zooKeeper
-h, --help
Display help information
```
### Changes
Update command `updatecookie` to `admin`
Reviewers: Jia Zhai <[email protected]>
This closes #2060 from zymap/command-updatecookie
---
.../org/apache/bookkeeper/bookie/BookieShell.java | 226 +-------------
.../tools/cli/commands/cookie/AdminCommand.java | 326 +++++++++++++++++++++
.../tools/cli/commands/CookieCommandGroup.java | 2 +
.../cli/commands/cookie/AdminCommandTest.java | 218 ++++++++++++++
4 files changed, 559 insertions(+), 213 deletions(-)
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 700ce11..02a7391 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
@@ -19,11 +19,8 @@
package org.apache.bookkeeper.bookie;
import static
org.apache.bookkeeper.meta.MetadataDrivers.runFunctionWithLedgerManagerFactory;
-import static
org.apache.bookkeeper.meta.MetadataDrivers.runFunctionWithMetadataBookieDriver;
-import static
org.apache.bookkeeper.meta.MetadataDrivers.runFunctionWithRegistrationManager;
import com.google.common.annotations.VisibleForTesting;
-import com.google.common.collect.Lists;
import com.google.common.util.concurrent.UncheckedExecutionException;
import java.io.File;
import java.io.IOException;
@@ -40,13 +37,10 @@ import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
-import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Collectors;
-import org.apache.bookkeeper.bookie.BookieException.CookieNotFoundException;
-import org.apache.bookkeeper.bookie.BookieException.InvalidCookieException;
import org.apache.bookkeeper.client.LedgerEntry;
import org.apache.bookkeeper.client.api.LedgerMetadata;
import org.apache.bookkeeper.common.annotation.InterfaceAudience.Private;
@@ -86,18 +80,16 @@ import
org.apache.bookkeeper.tools.cli.commands.bookies.NukeExistingClusterComma
import org.apache.bookkeeper.tools.cli.commands.bookies.RecoverCommand;
import org.apache.bookkeeper.tools.cli.commands.client.DeleteLedgerCommand;
import org.apache.bookkeeper.tools.cli.commands.client.SimpleTestCommand;
+import org.apache.bookkeeper.tools.cli.commands.cookie.AdminCommand;
import org.apache.bookkeeper.tools.cli.commands.cookie.CreateCookieCommand;
import org.apache.bookkeeper.tools.cli.commands.cookie.DeleteCookieCommand;
import org.apache.bookkeeper.tools.cli.commands.cookie.GenerateCookieCommand;
import org.apache.bookkeeper.tools.cli.commands.cookie.GetCookieCommand;
import org.apache.bookkeeper.tools.cli.commands.cookie.UpdateCookieCommand;
import org.apache.bookkeeper.tools.framework.CliFlags;
-import org.apache.bookkeeper.util.BookKeeperConstants;
import org.apache.bookkeeper.util.EntryFormatter;
-import org.apache.bookkeeper.util.IOUtils;
import org.apache.bookkeeper.util.LedgerIdFormatter;
import org.apache.bookkeeper.util.Tool;
-import org.apache.bookkeeper.versioning.Version;
import org.apache.bookkeeper.versioning.Versioned;
import org.apache.commons.cli.BasicParser;
import org.apache.commons.cli.CommandLine;
@@ -111,7 +103,6 @@ import
org.apache.commons.configuration.CompositeConfiguration;
import org.apache.commons.configuration.PropertiesConfiguration;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang.StringUtils;
-import org.apache.commons.lang3.ArrayUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -1453,7 +1444,8 @@ public class BookieShell implements Tool {
@Override
int runCmd(CommandLine cmdLine) throws Exception {
- int retValue = -1;
+ AdminCommand cmd = new AdminCommand();
+ AdminCommand.AdminFlags flags = new AdminCommand.AdminFlags();
Option[] options = cmdLine.getOptions();
if (options.length != 1) {
LOG.error("Invalid command!");
@@ -1474,214 +1466,22 @@ public class BookieShell implements Tool {
return -1;
}
boolean useHostName = getOptionalValue(bookieId, HOSTNAME);
- if (!bkConf.getUseHostNameAsBookieID() && useHostName) {
- LOG.error(
- "Expects config useHostNameAsBookieID=true as the
option value passed is 'hostname'");
- return -1;
- } else if (bkConf.getUseHostNameAsBookieID() && !useHostName) {
- LOG.error("Expects configuration
useHostNameAsBookieID=false as the option value passed is 'ip'");
- return -1;
- }
- retValue = updateBookieIdInCookie(bookieId, useHostName);
- } else if (thisCommandOption.getLongOpt().equals(EXPANDSTORAGE)) {
- bkConf.setAllowStorageExpansion(true);
- return expandStorage();
- } else if (thisCommandOption.getLongOpt().equals(LIST)) {
- return listOrDeleteCookies(false, false);
- } else if (thisCommandOption.getLongOpt().equals(DELETE)) {
+ flags.hostname(useHostName);
+ flags.ip(!useHostName);
+ }
+
flags.expandstorage(thisCommandOption.getLongOpt().equals(EXPANDSTORAGE));
+ flags.list(thisCommandOption.getLongOpt().equals(LIST));
+ flags.delete(thisCommandOption.getLongOpt().equals(DELETE));
+ if (thisCommandOption.getLongOpt().equals(DELETE)) {
boolean force = false;
String optionValue = thisCommandOption.getValue();
if (!StringUtils.isEmpty(optionValue) &&
optionValue.equals(FORCE)) {
force = true;
}
- return listOrDeleteCookies(true, force);
- } else {
- LOG.error("Invalid command!");
- this.printUsage();
- return -1;
+ flags.force(force);
}
- return retValue;
- }
-
- private int updateBookieIdInCookie(final String bookieId, final
boolean useHostname)
- throws Exception {
- return runFunctionWithRegistrationManager(bkConf, rm -> {
- try {
- ServerConfiguration conf = new ServerConfiguration(bkConf);
- String newBookieId =
Bookie.getBookieAddress(conf).toString();
- // read oldcookie
- Versioned<Cookie> oldCookie = null;
- try {
- conf.setUseHostNameAsBookieID(!useHostname);
- oldCookie = Cookie.readFromRegistrationManager(rm,
conf);
- } catch (CookieNotFoundException nne) {
- LOG.error("Either cookie already updated with
UseHostNameAsBookieID={} or no cookie exists!",
- useHostname, nne);
- return -1;
- }
- Cookie newCookie =
Cookie.newBuilder(oldCookie.getValue()).setBookieHost(newBookieId).build();
- boolean hasCookieUpdatedInDirs = verifyCookie(newCookie,
journalDirectories[0]);
- for (File dir : ledgerDirectories) {
- hasCookieUpdatedInDirs &= verifyCookie(newCookie, dir);
- }
- if (indexDirectories != ledgerDirectories) {
- for (File dir : indexDirectories) {
- hasCookieUpdatedInDirs &= verifyCookie(newCookie,
dir);
- }
- }
-
- if (hasCookieUpdatedInDirs) {
- try {
- conf.setUseHostNameAsBookieID(useHostname);
- Cookie.readFromRegistrationManager(rm, conf);
- // since newcookie exists, just do cleanup of
oldcookie and return
- conf.setUseHostNameAsBookieID(!useHostname);
-
oldCookie.getValue().deleteFromRegistrationManager(rm, conf,
oldCookie.getVersion());
- return 0;
- } catch (CookieNotFoundException nne) {
- if (LOG.isDebugEnabled()) {
- LOG.debug("Ignoring, cookie will be written to
zookeeper");
- }
- }
- } else {
- // writes newcookie to local dirs
- for (File journalDirectory : journalDirectories) {
- newCookie.writeToDirectory(journalDirectory);
- LOG.info("Updated cookie file present in
journalDirectory {}", journalDirectory);
- }
- for (File dir : ledgerDirectories) {
- newCookie.writeToDirectory(dir);
- }
- LOG.info("Updated cookie file present in
ledgerDirectories {}", (Object) ledgerDirectories);
- if (ledgerDirectories != indexDirectories) {
- for (File dir : indexDirectories) {
- newCookie.writeToDirectory(dir);
- }
- LOG.info("Updated cookie file present in
indexDirectories {}", (Object) indexDirectories);
- }
- }
- // writes newcookie to zookeeper
- conf.setUseHostNameAsBookieID(useHostname);
- newCookie.writeToRegistrationManager(rm, conf,
Version.NEW);
-
- // delete oldcookie
- conf.setUseHostNameAsBookieID(!useHostname);
- oldCookie.getValue().deleteFromRegistrationManager(rm,
conf, oldCookie.getVersion());
- return 0;
- } catch (IOException | BookieException ioe) {
- LOG.error("IOException during cookie updation!", ioe);
- return -1;
- }
- });
- }
-
- private int expandStorage() throws Exception {
- return runFunctionWithMetadataBookieDriver(bkConf, driver -> {
- List<File> allLedgerDirs = Lists.newArrayList();
- allLedgerDirs.addAll(Arrays.asList(ledgerDirectories));
- if (indexDirectories != ledgerDirectories) {
- allLedgerDirs.addAll(Arrays.asList(indexDirectories));
- }
-
- try {
- Bookie.checkEnvironmentWithStorageExpansion(
- bkConf, driver, Arrays.asList(journalDirectories),
allLedgerDirs);
- return 0;
- } catch (BookieException e) {
- LOG.error("Exception while updating cookie for storage
expansion", e);
- return -1;
- }
- });
- }
-
- private boolean verifyCookie(Cookie oldCookie, File dir) throws
IOException {
- try {
- Cookie cookie = Cookie.readFromDirectory(dir);
- cookie.verify(oldCookie);
- } catch (InvalidCookieException e) {
- return false;
- }
- return true;
- }
-
- private int listOrDeleteCookies(boolean delete, boolean force) throws
Exception {
- BookieSocketAddress bookieAddress =
Bookie.getBookieAddress(bkConf);
- File[] journalDirs = bkConf.getJournalDirs();
- File[] ledgerDirs = bkConf.getLedgerDirs();
- File[] indexDirs = bkConf.getIndexDirs();
- File[] allDirs = ArrayUtils.addAll(journalDirs, ledgerDirs);
- if (indexDirs != null) {
- allDirs = ArrayUtils.addAll(allDirs, indexDirs);
- }
-
- File[] allCurDirs = Bookie.getCurrentDirectories(allDirs);
- List<File> allVersionFiles = new LinkedList<File>();
- File versionFile;
- for (File curDir : allCurDirs) {
- versionFile = new File(curDir,
BookKeeperConstants.VERSION_FILENAME);
- if (versionFile.exists()) {
- allVersionFiles.add(versionFile);
- }
- }
-
- if (!allVersionFiles.isEmpty()) {
- if (delete) {
- boolean confirm = force;
- if (!confirm) {
- confirm = IOUtils.confirmPrompt("Are you sure you want
to delete Cookies locally?");
- }
- if (confirm) {
- for (File verFile : allVersionFiles) {
- if (!verFile.delete()) {
- LOG.error(
- "Failed to delete Local cookie file
{}. So aborting deletecookie of Bookie: {}",
- verFile, bookieAddress);
- return -1;
- }
- }
- LOG.info("Deleted Local Cookies of Bookie: {}",
bookieAddress);
- } else {
- LOG.info("Skipping deleting local Cookies of Bookie:
{}", bookieAddress);
- }
- } else {
- LOG.info("Listing local Cookie Files of Bookie: {}",
bookieAddress);
- for (File verFile : allVersionFiles) {
- LOG.info(verFile.getCanonicalPath());
- }
- }
- } else {
- LOG.info("No local cookies for Bookie: {}", bookieAddress);
- }
-
- return runFunctionWithRegistrationManager(bkConf, rm -> {
- try {
- Versioned<Cookie> cookie = null;
- try {
- cookie = Cookie.readFromRegistrationManager(rm,
bookieAddress);
- } catch (CookieNotFoundException nne) {
- LOG.info("No cookie for {} in metadata store",
bookieAddress);
- return 0;
- }
-
- if (delete) {
- boolean confirm = force;
- if (!confirm) {
- confirm = IOUtils.confirmPrompt(
- "Are you sure you want to delete Cookies from
metadata store?");
- }
-
- if (confirm) {
-
cookie.getValue().deleteFromRegistrationManager(rm, bkConf,
cookie.getVersion());
- LOG.info("Deleted Cookie from metadata store for
Bookie: {}", bookieAddress);
- } else {
- LOG.info("Skipping deleting cookie from metadata
store for Bookie: {}", bookieAddress);
- }
- }
- } catch (BookieException | IOException e) {
- return -1;
- }
- return 0;
- });
+ boolean result = cmd.apply(bkConf, flags);
+ return (result) ? 0 : -1;
}
}
diff --git
a/bookkeeper-server/src/main/java/org/apache/bookkeeper/tools/cli/commands/cookie/AdminCommand.java
b/bookkeeper-server/src/main/java/org/apache/bookkeeper/tools/cli/commands/cookie/AdminCommand.java
new file mode 100644
index 0000000..dce11ce
--- /dev/null
+++
b/bookkeeper-server/src/main/java/org/apache/bookkeeper/tools/cli/commands/cookie/AdminCommand.java
@@ -0,0 +1,326 @@
+/*
+ * 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.cookie;
+
+import static
org.apache.bookkeeper.meta.MetadataDrivers.runFunctionWithMetadataBookieDriver;
+import static
org.apache.bookkeeper.meta.MetadataDrivers.runFunctionWithRegistrationManager;
+
+import com.beust.jcommander.Parameter;
+import com.google.common.collect.Lists;
+import com.google.common.util.concurrent.UncheckedExecutionException;
+import java.io.File;
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.LinkedList;
+import java.util.List;
+import lombok.Setter;
+import lombok.experimental.Accessors;
+import org.apache.bookkeeper.bookie.Bookie;
+import org.apache.bookkeeper.bookie.BookieException;
+import org.apache.bookkeeper.bookie.Cookie;
+import org.apache.bookkeeper.conf.ServerConfiguration;
+import org.apache.bookkeeper.net.BookieSocketAddress;
+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.BookKeeperConstants;
+import org.apache.bookkeeper.util.IOUtils;
+import org.apache.bookkeeper.versioning.Version;
+import org.apache.bookkeeper.versioning.Versioned;
+import org.apache.commons.lang3.ArrayUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Command to update cookie.
+ */
+public class AdminCommand extends BookieCommand<AdminCommand.AdminFlags> {
+
+ static final Logger LOG = LoggerFactory.getLogger(AdminCommand.class);
+
+ private static final String NAME = "admin";
+ private static final String DESC = "Command to update cookie";
+
+ private File[] journalDirectories;
+ private File[] ledgerDirectories;
+ private File[] indexDirectories;
+
+ public AdminCommand() {
+ this(new AdminFlags());
+ }
+
+ private AdminCommand(AdminFlags flags) {
+
super(CliSpec.<AdminFlags>newBuilder().withName(NAME).withDescription(DESC).withFlags(flags).build());
+ }
+
+ /**
+ * Flags for admin command.
+ */
+ @Accessors(fluent = true)
+ @Setter
+ public static class AdminFlags extends CliFlags {
+
+ @Parameter(names = { "-host",
+ "--hostname" }, description = "Expects config
useHostNameAsBookieID=true as the option value")
+ private boolean hostname;
+
+ @Parameter(names = { "-p", "-ip" },
+ description = "Expects config useHostNameAsBookieID=false as the
option value")
+ private boolean ip;
+
+ @Parameter(names = { "-e", "--expandstorage" }, description = "Add new
empty ledger/index directories")
+ private boolean expandstorage;
+
+ @Parameter(names = { "-l", "--list" }, description = "List paths of
all the cookies present locally and on "
+ + "zooKeeper")
+ private boolean list;
+
+ @Parameter(names = { "-d", "--delete" }, description = "Delete cookie
both locally and in zooKeeper")
+ private boolean delete;
+
+ @Parameter(names = {"-f", "--force"}, description = "Force delete
cookie")
+ private boolean force;
+
+ }
+
+ @Override
+ public boolean apply(ServerConfiguration conf, AdminFlags cmdFlags) {
+ initDirectory(conf);
+ try {
+ return update(conf, cmdFlags);
+ } catch (Exception e) {
+ throw new UncheckedExecutionException(e.getMessage(), e);
+ }
+ }
+
+ private void initDirectory(ServerConfiguration bkConf) {
+ this.journalDirectories =
Bookie.getCurrentDirectories(bkConf.getJournalDirs());
+ this.ledgerDirectories =
Bookie.getCurrentDirectories(bkConf.getLedgerDirs());
+ if (null == bkConf.getIndexDirs()) {
+ this.indexDirectories = this.ledgerDirectories;
+ } else {
+ this.indexDirectories =
Bookie.getCurrentDirectories(bkConf.getIndexDirs());
+ }
+ }
+
+ private boolean update(ServerConfiguration conf, AdminFlags flags) throws
Exception {
+ boolean useHostName = flags.hostname;
+ if (flags.hostname || flags.ip) {
+ if (!conf.getUseHostNameAsBookieID() && useHostName) {
+ LOG.error("Expects configuration useHostNameAsBookieID=true as
the option value");
+ return false;
+ } else if (conf.getUseHostNameAsBookieID() && !useHostName) {
+ LOG.error("Expects configuration useHostNameAsBookieID=false
as the option value");
+ return false;
+ }
+ return updateBookieIdInCookie(conf, flags.hostname);
+ } else if (flags.expandstorage) {
+ conf.setAllowStorageExpansion(true);
+ return expandStorage(conf);
+ } else if (flags.list) {
+ return listOrDeleteCookies(conf, false, false);
+ } else if (flags.delete) {
+ return listOrDeleteCookies(conf, true, flags.force);
+ } else {
+ LOG.error("Invalid command !");
+ usage();
+ return false;
+ }
+ }
+
+ private boolean updateBookieIdInCookie(ServerConfiguration bkConf, final
boolean useHostname)
+ throws Exception {
+ return runFunctionWithRegistrationManager(bkConf, rm -> {
+ try {
+ ServerConfiguration conf = new ServerConfiguration(bkConf);
+ String newBookieId = Bookie.getBookieAddress(conf).toString();
+ // read oldcookie
+ Versioned<Cookie> oldCookie = null;
+ try {
+ conf.setUseHostNameAsBookieID(!useHostname);
+ oldCookie = Cookie.readFromRegistrationManager(rm, conf);
+ } catch (BookieException.CookieNotFoundException nne) {
+ LOG.error("Either cookie already updated with
UseHostNameAsBookieID={} or no cookie exists!",
+ useHostname, nne);
+ return false;
+ }
+ Cookie newCookie =
Cookie.newBuilder(oldCookie.getValue()).setBookieHost(newBookieId).build();
+
+ boolean hasCookieUpdatedInDirs = verifyCookie(newCookie,
journalDirectories[0]);
+ for (File dir : ledgerDirectories) {
+ hasCookieUpdatedInDirs &= verifyCookie(newCookie, dir);
+ }
+ if (indexDirectories != ledgerDirectories) {
+ for (File dir : indexDirectories) {
+ hasCookieUpdatedInDirs &= verifyCookie(newCookie, dir);
+ }
+ }
+
+ if (hasCookieUpdatedInDirs) {
+ try {
+ conf.setUseHostNameAsBookieID(useHostname);
+ Cookie.readFromRegistrationManager(rm, conf);
+ // since newcookie exists, just do cleanup of
oldcookie and return
+ conf.setUseHostNameAsBookieID(!useHostname);
+ oldCookie.getValue().deleteFromRegistrationManager(rm,
conf, oldCookie.getVersion());
+ return true;
+ } catch (BookieException.CookieNotFoundException nne) {
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("Ignoring, cookie will be written to
zookeeper");
+ }
+ }
+ } else {
+ // writes newcookie to local dirs
+ for (File journalDirectory : journalDirectories) {
+ newCookie.writeToDirectory(journalDirectory);
+ LOG.info("Updated cookie file present in
journalDirectory {}", journalDirectory);
+ }
+ for (File dir : ledgerDirectories) {
+ newCookie.writeToDirectory(dir);
+ }
+ LOG.info("Updated cookie file present in ledgerDirectories
{}", (Object) ledgerDirectories);
+ if (ledgerDirectories != indexDirectories) {
+ for (File dir : indexDirectories) {
+ newCookie.writeToDirectory(dir);
+ }
+ LOG.info("Updated cookie file present in
indexDirectories {}", (Object) indexDirectories);
+ }
+ }
+ // writes newcookie to zookeeper
+ conf.setUseHostNameAsBookieID(useHostname);
+ newCookie.writeToRegistrationManager(rm, conf, Version.NEW);
+
+ // delete oldcookie
+ conf.setUseHostNameAsBookieID(!useHostname);
+ oldCookie.getValue().deleteFromRegistrationManager(rm, conf,
oldCookie.getVersion());
+ return true;
+ } catch (IOException | BookieException ioe) {
+ LOG.error("IOException during cookie updation!", ioe);
+ return false;
+ }
+ });
+ }
+
+ private boolean verifyCookie(Cookie oldCookie, File dir) throws
IOException {
+ try {
+ Cookie cookie = Cookie.readFromDirectory(dir);
+ cookie.verify(oldCookie);
+ } catch (BookieException.InvalidCookieException e) {
+ return false;
+ }
+ return true;
+ }
+
+ private boolean expandStorage(ServerConfiguration bkConf) throws Exception
{
+ return runFunctionWithMetadataBookieDriver(bkConf, driver -> {
+ List<File> allLedgerDirs = Lists.newArrayList();
+ allLedgerDirs.addAll(Arrays.asList(ledgerDirectories));
+ if (indexDirectories != ledgerDirectories) {
+ allLedgerDirs.addAll(Arrays.asList(indexDirectories));
+ }
+
+ try {
+ Bookie.checkEnvironmentWithStorageExpansion(bkConf, driver,
Arrays.asList(journalDirectories),
+ allLedgerDirs);
+ return true;
+ } catch (BookieException e) {
+ LOG.error("Exception while updating cookie for storage
expansion", e);
+ return false;
+ }
+ });
+ }
+
+ private boolean listOrDeleteCookies(ServerConfiguration bkConf, boolean
delete, boolean force) throws Exception {
+ BookieSocketAddress bookieAddress = Bookie.getBookieAddress(bkConf);
+ File[] journalDirs = bkConf.getJournalDirs();
+ File[] ledgerDirs = bkConf.getLedgerDirs();
+ File[] indexDirs = bkConf.getIndexDirs();
+ File[] allDirs = ArrayUtils.addAll(journalDirs, ledgerDirs);
+ if (indexDirs != null) {
+ allDirs = ArrayUtils.addAll(allDirs, indexDirs);
+ }
+
+ File[] allCurDirs = Bookie.getCurrentDirectories(allDirs);
+ List<File> allVersionFiles = new LinkedList<File>();
+ File versionFile;
+ for (File curDir : allCurDirs) {
+ versionFile = new File(curDir,
BookKeeperConstants.VERSION_FILENAME);
+ if (versionFile.exists()) {
+ allVersionFiles.add(versionFile);
+ }
+ }
+
+ if (!allVersionFiles.isEmpty()) {
+ if (delete) {
+ boolean confirm = force;
+ if (!confirm) {
+ confirm = IOUtils.confirmPrompt("Are you sure you want to
delete Cookies locally?");
+ }
+ if (confirm) {
+ for (File verFile : allVersionFiles) {
+ if (!verFile.delete()) {
+ LOG.error("Failed to delete Local cookie file {}.
So aborting deletecookie of Bookie: {}",
+ verFile, bookieAddress);
+ return false;
+ }
+ }
+ LOG.info("Deleted Local Cookies of Bookie: {}",
bookieAddress);
+ } else {
+ LOG.info("Skipping deleting local Cookies of Bookie: {}",
bookieAddress);
+ }
+ } else {
+ LOG.info("Listing local Cookie Files of Bookie: {}",
bookieAddress);
+ for (File verFile : allVersionFiles) {
+ LOG.info(verFile.getCanonicalPath());
+ }
+ }
+ } else {
+ LOG.info("No local cookies for Bookie: {}", bookieAddress);
+ }
+
+ return runFunctionWithRegistrationManager(bkConf, rm -> {
+ try {
+ Versioned<Cookie> cookie = null;
+ try {
+ cookie = Cookie.readFromRegistrationManager(rm,
bookieAddress);
+ } catch (BookieException.CookieNotFoundException nne) {
+ LOG.info("No cookie for {} in metadata store",
bookieAddress);
+ return true;
+ }
+
+ if (delete) {
+ boolean confirm = force;
+ if (!confirm) {
+ confirm = IOUtils.confirmPrompt("Are you sure you want
to delete Cookies from metadata store?");
+ }
+
+ if (confirm) {
+ cookie.getValue().deleteFromRegistrationManager(rm,
bkConf, cookie.getVersion());
+ LOG.info("Deleted Cookie from metadata store for
Bookie: {}", bookieAddress);
+ } else {
+ LOG.info("Skipping deleting cookie from metadata store
for Bookie: {}", bookieAddress);
+ }
+ }
+ } catch (BookieException | IOException e) {
+ return false;
+ }
+ return true;
+ });
+ }
+}
diff --git
a/tools/ledger/src/main/java/org/apache/bookkeeper/tools/cli/commands/CookieCommandGroup.java
b/tools/ledger/src/main/java/org/apache/bookkeeper/tools/cli/commands/CookieCommandGroup.java
index 3dded4e..83dfc26 100644
---
a/tools/ledger/src/main/java/org/apache/bookkeeper/tools/cli/commands/CookieCommandGroup.java
+++
b/tools/ledger/src/main/java/org/apache/bookkeeper/tools/cli/commands/CookieCommandGroup.java
@@ -20,6 +20,7 @@
package org.apache.bookkeeper.tools.cli.commands;
import org.apache.bookkeeper.tools.cli.BKCtl;
+import org.apache.bookkeeper.tools.cli.commands.cookie.AdminCommand;
import org.apache.bookkeeper.tools.cli.commands.cookie.CreateCookieCommand;
import org.apache.bookkeeper.tools.cli.commands.cookie.DeleteCookieCommand;
import org.apache.bookkeeper.tools.cli.commands.cookie.GenerateCookieCommand;
@@ -46,6 +47,7 @@ public class CookieCommandGroup extends
CliCommandGroup<BKFlags> {
.addCommand(new GetCookieCommand())
.addCommand(new UpdateCookieCommand())
.addCommand(new GenerateCookieCommand())
+ .addCommand(new AdminCommand())
.build();
public CookieCommandGroup() {
diff --git
a/tools/ledger/src/test/java/org/apache/bookkeeper/tools/cli/commands/cookie/AdminCommandTest.java
b/tools/ledger/src/test/java/org/apache/bookkeeper/tools/cli/commands/cookie/AdminCommandTest.java
new file mode 100644
index 0000000..9f39867
--- /dev/null
+++
b/tools/ledger/src/test/java/org/apache/bookkeeper/tools/cli/commands/cookie/AdminCommandTest.java
@@ -0,0 +1,218 @@
+/*
+ * 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.cookie;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.verify;
+import static org.mockito.internal.verification.VerificationModeFactory.times;
+import static org.powermock.api.mockito.PowerMockito.mock;
+import static org.powermock.api.mockito.PowerMockito.verifyNew;
+import static org.powermock.api.mockito.PowerMockito.when;
+
+import java.io.File;
+import java.io.IOException;
+import java.net.UnknownHostException;
+import java.util.function.Function;
+import org.apache.bookkeeper.bookie.Bookie;
+import org.apache.bookkeeper.bookie.BookieException;
+import org.apache.bookkeeper.bookie.Cookie;
+import org.apache.bookkeeper.conf.AbstractConfiguration;
+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.net.BookieSocketAddress;
+import org.apache.bookkeeper.tools.cli.helpers.BookieCommandTestBase;
+import org.apache.bookkeeper.util.BookKeeperConstants;
+import org.apache.bookkeeper.versioning.Version;
+import org.apache.bookkeeper.versioning.Versioned;
+import org.junit.Assert;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.powermock.api.mockito.PowerMockito;
+import org.powermock.core.classloader.annotations.PrepareForTest;
+import org.powermock.modules.junit4.PowerMockRunner;
+
+/**
+ * Unit test for {@link AdminCommand}.
+ */
+@RunWith(PowerMockRunner.class)
+@PrepareForTest({ AdminCommand.class, MetadataDrivers.class, Cookie.class,
Bookie.class, RegistrationManager.class })
+public class AdminCommandTest extends BookieCommandTestBase {
+
+ @Mock
+ private ServerConfiguration serverConfiguration;
+
+ @Mock
+ private Versioned<Cookie> cookieVersioned;
+
+ @Mock
+ private Cookie cookie;
+
+ @Mock
+ private BookieSocketAddress bookieSocketAddress;
+
+ public AdminCommandTest() throws IOException {
+ super(3, 3);
+ }
+
+ public void createIndex() throws IOException {
+ String[] indexDirs = new String[3];
+ for (int i = 0; i < indexDirs.length; i++) {
+ File dir = this.testDir.newFile();
+ dir.mkdirs();
+ indexDirs[i] = dir.getAbsolutePath();
+ }
+ this.conf.setIndexDirName(indexDirs);
+
+ }
+ @Override
+ public void setup() throws Exception {
+ super.setup();
+ createIndex();
+
+
PowerMockito.whenNew(ServerConfiguration.class).withNoArguments().thenReturn(conf);
+ PowerMockito.mockStatic(MetadataDrivers.class);
+
PowerMockito.whenNew(ServerConfiguration.class).withParameterTypes(AbstractConfiguration.class)
+ .withArguments(eq(conf)).thenReturn(serverConfiguration);
+ PowerMockito.mockStatic(Cookie.class);
+ PowerMockito.mockStatic(MetadataDrivers.class);
+ PowerMockito.mockStatic(Bookie.class);
+
+ mockUpdateBookieIdInCookie();
+ mockVerifyCookie();
+ mockInitDirecotory();
+ mockExpandStorage();
+ mockListOrDeleteCookies();
+
+ }
+
+ private void mockInitDirecotory() throws IOException {
+ File[] files = new File[1];
+ files[0] = testDir.getRoot();
+ testDir.newFile(BookKeeperConstants.VERSION_FILENAME);
+
PowerMockito.when(Bookie.getCurrentDirectories(any())).thenReturn(files);
+ }
+
+ private void mockUpdateBookieIdInCookie() throws Exception {
+ RegistrationManager registrationManager =
mock(RegistrationManager.class);
+ PowerMockito.doAnswer(invocationOnMock -> {
+ Function<RegistrationManager, ?> f =
invocationOnMock.getArgument(1);
+ f.apply(registrationManager);
+ return true;
+ }).when(MetadataDrivers.class, "runFunctionWithRegistrationManager",
any(ServerConfiguration.class),
+ any(Function.class));
+
+
PowerMockito.when(Bookie.getBookieAddress(eq(serverConfiguration))).thenReturn(bookieSocketAddress);
+ when(bookieSocketAddress.toString()).thenReturn("1");
+
PowerMockito.when(Cookie.readFromRegistrationManager(eq(registrationManager),
eq(serverConfiguration)))
+ .thenReturn(cookieVersioned);
+
PowerMockito.when(Cookie.readFromRegistrationManager(eq(registrationManager),
eq(bookieSocketAddress)))
+ .thenReturn(cookieVersioned);
+ when(cookieVersioned.getValue()).thenReturn(cookie);
+ Cookie.Builder builder = mock(Cookie.Builder.class);
+ PowerMockito.when(Cookie.newBuilder(eq(cookie))).thenReturn(builder);
+
PowerMockito.when(builder.setBookieHost(anyString())).thenReturn(builder);
+ when(builder.build()).thenReturn(cookie);
+
+
PowerMockito.when(serverConfiguration.setUseHostNameAsBookieID(anyBoolean())).thenReturn(serverConfiguration);
+
PowerMockito.when(Cookie.readFromRegistrationManager(eq(registrationManager),
eq(serverConfiguration)))
+ .thenReturn(cookieVersioned);
+
+ Version version = mock(Version.class);
+ when(cookieVersioned.getVersion()).thenReturn(version);
+ when(cookieVersioned.getValue()).thenReturn(cookie);
+ doNothing().when(cookie)
+ .deleteFromRegistrationManager(eq(registrationManager),
eq(serverConfiguration), eq(version));
+
+ doNothing().when(cookie).writeToDirectory(any(File.class));
+ doNothing().when(cookie)
+ .writeToRegistrationManager(eq(registrationManager),
eq(serverConfiguration), eq(Version.NEW));
+
+ doNothing().when(cookie)
+ .deleteFromRegistrationManager(eq(registrationManager),
any(ServerConfiguration.class), eq(version));
+ }
+
+ private void mockVerifyCookie() throws IOException,
BookieException.InvalidCookieException {
+
PowerMockito.when(Cookie.readFromDirectory(any(File.class))).thenReturn(cookie);
+ doNothing().when(cookie).verify(any(Cookie.class));
+ }
+
+ private void mockExpandStorage() throws Exception {
+ MetadataBookieDriver metadataBookieDriver =
mock(MetadataBookieDriver.class);
+ PowerMockito.doAnswer(invocationOnMock -> {
+ Function<MetadataBookieDriver, ?> f =
invocationOnMock.getArgument(1);
+ f.apply(metadataBookieDriver);
+ return true;
+ }).when(MetadataDrivers.class, "runFunctionWithMetadataBookieDriver",
any(ServerConfiguration.class),
+ any(Function.class));
+ PowerMockito.doNothing()
+ .when(Bookie.class,
"checkEnvironmentWithStorageExpansion", any(ServerConfiguration.class),
+ eq(metadataBookieDriver), any(), any());
+ }
+
+ private void mockListOrDeleteCookies() throws UnknownHostException {
+
+
when(Bookie.getBookieAddress(any(ServerConfiguration.class))).thenReturn(bookieSocketAddress);
+ }
+
+ @Test
+ public void testWithoutAnyFlags() {
+ AdminCommand cmd = new AdminCommand();
+ Assert.assertFalse(cmd.apply(bkFlags, new String[] {""}));
+ }
+
+ @Test
+ public void testWithHostName() throws Exception {
+ conf.setUseHostNameAsBookieID(true);
+ testCommand("-host");
+ verifyNew(ServerConfiguration.class, times(1)).withArguments(eq(conf));
+ verify(serverConfiguration,
times(3)).setUseHostNameAsBookieID(anyBoolean());
+
+ verify(cookie, times(2)).verify(any(Cookie.class));
+ }
+
+ @Test
+ public void testWithExpand() {
+ testCommand("-e");
+ }
+
+ @Test
+ public void testWithList() {
+ testCommand("-l");
+ }
+
+ @Test
+ public void testWithDelete() throws BookieException {
+ testCommand("-d", "-f");
+ verify(cookie, times(1))
+ .deleteFromRegistrationManager(any(RegistrationManager.class),
any(ServerConfiguration.class),
+ any(Version.class));
+ }
+
+ private void testCommand(String... args) {
+ AdminCommand cmd = new AdminCommand();
+ Assert.assertTrue(cmd.apply(bkFlags, args));
+ }
+}