content: /*
   *
   * 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 org.apache.bookkeeper.util.BookKeeperConstants.COOKIE_NODE;
  
  import java.io.File;
  import java.io.IOException;
  import java.net.URI;
  import java.net.UnknownHostException;
  import java.util.List;
  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.meta.zk.ZKMetadataDriverBase;
  import org.apache.bookkeeper.server.Main;
  import org.apache.bookkeeper.stats.NullStatsLogger;
  import org.apache.bookkeeper.test.BookKeeperClusterTestCase;
  import org.apache.bookkeeper.versioning.Version;
  import org.apache.zookeeper.KeeperException;
  import org.junit.Assert;
  import org.junit.Test;
  import org.slf4j.Logger;
  import org.slf4j.LoggerFactory;
  
  /**
   * This tests 'updatecookie' shell command.
   */
  public class UpdateCookieCmdTest extends BookKeeperClusterTestCase {
  
      private static final Logger LOG = LoggerFactory.getLogger(UpdateCookieCmdTest.class);
  
      MetadataBookieDriver driver;
      RegistrationManager rm;
      ServerConfiguration conf;
  
      public UpdateCookieCmdTest() {
          super(0);
          useUUIDasBookieId = false;
      }
  
      @Override
      public void setUp() throws Exception {
          super.setUp();
          LOG.info("setUp ZKRegistrationManager");
          baseConf.setMetadataServiceUri(zkUtil.getMetadataServiceUri());
          driver = MetadataDrivers.getBookieDriver(
              URI.create(baseConf.getMetadataServiceUri()));
          driver.initialize(baseConf, NullStatsLogger.INSTANCE);
          rm = driver.createRegistrationManager();
  
          conf = newServerConfiguration();
          LegacyCookieValidation validation = new LegacyCookieValidation(conf, rm);
          validation.checkCookies(Main.storageDirectoriesFromConf(conf));
      }
  
      @Override
      public void tearDown() throws Exception {
          super.tearDown();
          if (rm != null) {
              rm.close();
          }
          if (driver != null) {
              driver.close();
          }
      }
  
      /**
       * updatecookie to hostname.
       */
      @Test
      public void testUpdateCookieIpAddressToHostname() throws Exception {
          updateCookie("-bookieId", "hostname", true);
      }
  
      /**
       * updatecookie to short hostname.
       */
      @Test
      public void testUpdateCookieIpAddressToShortHostname() throws Exception {
          updateCookie("-bookieId", "hostname", true, true);
      }
  
      /**
       * updatecookie to ipaddress.
       */
      @Test
      public void testUpdateCookieHostnameToIpAddress() throws Exception {
          updateCookie("-bookieId", "hostname", true);
  
          updateCookie("-b", "ip", false);
  
          // start bookie to ensure everything works fine
          conf.setUseHostNameAsBookieID(false);
          LegacyCookieValidation validation = new LegacyCookieValidation(conf, rm);
          validation.checkCookies(Main.storageDirectoriesFromConf(conf));
      }
  
      /**
       * updatecookie to invalid bookie id.
       */
      @Test
      public void testUpdateCookieWithInvalidOption() throws Exception {
          String[] argv = new String[] { "updatecookie", "-b", "invalidBookieID" };
          final ServerConfiguration conf = this.conf;
          updateCookie(argv, -1, conf);
  
          argv = new String[] { "updatecookie", "-b" };
          updateCookie(argv, -1, conf);
  
          argv = new String[] { "updatecookie" };
          updateCookie(argv, -1, conf);
  
          // conf not updated
          argv = new String[] { "updatecookie", "-b", "hostname" };
          conf.setUseHostNameAsBookieID(false);
          updateCookie(argv, -1, conf);
  
          argv = new String[] { "updatecookie", "-b", "ip" };
          conf.setUseHostNameAsBookieID(true);
          updateCookie(argv, -1, conf);
      }
  
      /**
       * During first updatecookie it successfully created the hostname cookie but
       * it fails to delete the old ipaddress cookie. Here user will issue
       * updatecookie again, now it should be able to delete the old cookie
       * gracefully.
       */
      @Test
      public void testWhenBothIPaddressAndHostNameCookiesExists() throws Exception {
          updateCookie("-b", "hostname", true);
  
          // creates cookie with ipaddress
          final ServerConfiguration conf = this.conf;
          conf.setUseHostNameAsBookieID(true); // sets to hostname
          Cookie cookie = Cookie.readFromRegistrationManager(rm, conf).getValue();
          Cookie.Builder cookieBuilder = Cookie.newBuilder(cookie);
          conf.setUseHostNameAsBookieID(false); // sets to hostname
  
          final String newBookieHost = BookieImpl.getBookieAddress(conf).toString();
          cookieBuilder.setBookieId(newBookieHost);
  
          cookieBuilder.build().writeToRegistrationManager(rm, conf, Version.NEW);
          verifyCookieInZooKeeper(conf, 2);
  
          // again issue hostname cmd
          BookieShell bkShell = new BookieShell();
          conf.setUseHostNameAsBookieID(true); // sets to hostname
          bkShell.setConf(conf);
          String[] argv = new String[] { "updatecookie", "-b", "hostname" };
          Assert.assertEquals("Failed to return the error code!", 0, bkShell.run(argv));
  
          conf.setUseHostNameAsBookieID(true);
          cookie = Cookie.readFromRegistrationManager(rm, conf).getValue();
          Assert.assertFalse("Cookie has created with IP!", cookie.isBookieHostCreatedFromIp());
          // ensure the old cookie is deleted
          verifyCookieInZooKeeper(conf, 1);
      }
  
      /**
       * updatecookie to hostname.
       */
      @Test
      public void testDuplicateUpdateCookieIpAddress() throws Exception {
          String[] argv = new String[] { "updatecookie", "-b", "ip" };
          final ServerConfiguration conf = this.conf;
          conf.setUseHostNameAsBookieID(true);
          updateCookie(argv, -1, conf);
      }
  
      @Test
      public void testWhenNoCookieExists() throws Exception {
          String zkCookiePath = ZKMetadataDriverBase.resolveZkLedgersRootPath(conf)
              + "/" + COOKIE_NODE + "/" + BookieImpl.getBookieAddress(conf);
          Assert.assertNotNull("Cookie path doesn't still exists!", zkc.exists(zkCookiePath, false));
          zkc.delete(zkCookiePath, -1);
          Assert.assertNull("Cookie path still exists!", zkc.exists(zkCookiePath, false));
  
          BookieShell bkShell = new BookieShell();
          conf.setUseHostNameAsBookieID(true);
          bkShell.setConf(conf);
          String[] argv = new String[] { "updatecookie", "-b", "hostname" };
          Assert.assertEquals("Failed to return the error code!", -1, bkShell.run(argv));
      }
  
      private void verifyCookieInZooKeeper(ServerConfiguration conf, int expectedCount) throws KeeperException,
              InterruptedException {
          List<String> cookies;
          String bookieCookiePath1 = ZKMetadataDriverBase.resolveZkLedgersRootPath(conf) + "/" + COOKIE_NODE;
          cookies = zkc.getChildren(bookieCookiePath1, false);
          Assert.assertEquals("Wrongly updated the cookie!", expectedCount, cookies.size());
      }
  
      private void updateCookie(String option, String optionVal, boolean useHostNameAsBookieID) throws Exception {
          updateCookie(option, optionVal, useHostNameAsBookieID, false);
      }
  
      private void updateCookie(String option, String optionVal, boolean useHostNameAsBookieID, boolean useShortHostName)
              throws Exception {
          conf.setUseHostNameAsBookieID(!useHostNameAsBookieID);
          Cookie cookie = Cookie.readFromRegistrationManager(rm, conf).getValue();
          final boolean previousBookieID = cookie.isBookieHostCreatedFromIp();
          Assert.assertEquals("Wrong cookie!", useHostNameAsBookieID, previousBookieID);
  
          LOG.info("Perform updatecookie command");
          ServerConfiguration newconf = new ServerConfiguration(conf);
          newconf.setUseHostNameAsBookieID(useHostNameAsBookieID);
          newconf.setUseShortHostName(useShortHostName);
          BookieShell bkShell = new BookieShell();
          bkShell.setConf(newconf);
          String[] argv = new String[] { "updatecookie", option, optionVal };
          Assert.assertEquals("Failed to return exit code!", 0, bkShell.run(argv));
  
          newconf.setUseHostNameAsBookieID(useHostNameAsBookieID);
          newconf.setUseShortHostName(useShortHostName);
          cookie = Cookie.readFromRegistrationManager(rm, newconf).getValue();
          Assert.assertEquals("Wrongly updated cookie!", previousBookieID, !cookie.isBookieHostCreatedFromIp());
          Assert.assertEquals("Wrongly updated cookie!", useHostNameAsBookieID, !cookie.isBookieHostCreatedFromIp());
          verifyCookieInZooKeeper(newconf, 1);
  
          for (File journalDir : conf.getJournalDirs()) {
              journalDir = BookieImpl.getCurrentDirectory(journalDir);
              Cookie jCookie = Cookie.readFromDirectory(journalDir);
              jCookie.verify(cookie);
          }
          File[] ledgerDir = BookieImpl.getCurrentDirectories(conf.getLedgerDirs());
          for (File dir : ledgerDir) {
              Cookie lCookie = Cookie.readFromDirectory(dir);
              lCookie.verify(cookie);
          }
      }
  
      private void updateCookie(String[] argv, int exitCode, ServerConfiguration conf) throws KeeperException,
              InterruptedException, IOException, UnknownHostException, Exception {
          LOG.info("Perform updatecookie command");
          BookieShell bkShell = new BookieShell();
          bkShell.setConf(conf);
  
          Assert.assertEquals("Failed to return exit code!", exitCode, bkShell.run(argv));
      }
  }
  