001/*
002 * Licensed to the Apache Software Foundation (ASF) under one
003 * or more contributor license agreements.  See the NOTICE file
004 * distributed with this work for additional information
005 * regarding copyright ownership.  The ASF licenses this file
006 * to you under the Apache License, Version 2.0 (the
007 * "License"); you may not use this file except in compliance
008 * with the License.  You may obtain a copy of the License at
009 *
010 *     http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing, software
013 * distributed under the License is distributed on an "AS IS" BASIS,
014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015 * See the License for the specific language governing permissions and
016 * limitations under the License.
017 */
018package org.apache.hadoop.hbase;
019
020import java.io.File;
021import java.io.IOException;
022import org.apache.hadoop.conf.Configuration;
023import org.apache.hadoop.fs.Path;
024import org.apache.hadoop.hbase.zookeeper.MiniZooKeeperCluster;
025import org.apache.hadoop.hbase.zookeeper.ZKWatcher;
026import org.apache.yetus.audience.InterfaceAudience;
027import org.apache.yetus.audience.InterfaceStability;
028
029/**
030 * Helpers for testing HBase that do not depend on specific server/etc. things. The main difference
031 * from {@link HBaseCommonTestingUtil} is that we can start a zookeeper cluster.
032 */
033@InterfaceAudience.LimitedPrivate(HBaseInterfaceAudience.PHOENIX)
034@InterfaceStability.Evolving
035public class HBaseZKTestingUtil extends HBaseCommonTestingUtil {
036  private MiniZooKeeperCluster zkCluster;
037
038  /**
039   * Set if we were passed a zkCluster. If so, we won't shutdown zk as part of general shutdown.
040   */
041  private boolean passedZkCluster;
042
043  protected ZKWatcher zooKeeperWatcher;
044
045  /** Directory (a subdirectory of dataTestDir) used by the dfs cluster if any */
046  protected File clusterTestDir;
047
048  public HBaseZKTestingUtil() {
049    this(HBaseConfiguration.create());
050  }
051
052  public HBaseZKTestingUtil(Configuration conf) {
053    super(conf);
054  }
055
056  /**
057   * Returns Where the cluster will write data on the local subsystem. Creates it if it does not
058   * exist already. A subdir of {@code HBaseCommonTestingUtility#getBaseTestDir()}
059   */
060  Path getClusterTestDir() {
061    if (clusterTestDir == null) {
062      setupClusterTestDir();
063    }
064    return new Path(clusterTestDir.getAbsolutePath());
065  }
066
067  /**
068   * Creates a directory for the cluster, under the test data
069   */
070  protected void setupClusterTestDir() {
071    if (clusterTestDir != null) {
072      return;
073    }
074
075    // Using randomUUID ensures that multiple clusters can be launched by
076    // a same test, if it stops & starts them
077    Path testDir = getDataTestDir("cluster_" + getRandomUUID().toString());
078    clusterTestDir = new File(testDir.toString()).getAbsoluteFile();
079    // Have it cleaned up on exit
080    boolean b = deleteOnExit();
081    if (b) {
082      clusterTestDir.deleteOnExit();
083    }
084    LOG.info("Created new mini-cluster data directory: " + clusterTestDir + ", deleteOnExit=" + b);
085  }
086
087  /**
088   * Call this if you only want a zk cluster.
089   * @see #shutdownMiniZKCluster()
090   * @return zk cluster started.
091   */
092  public MiniZooKeeperCluster startMiniZKCluster() throws Exception {
093    return startMiniZKCluster(1);
094  }
095
096  /**
097   * Call this if you only want a zk cluster.
098   * @see #shutdownMiniZKCluster()
099   * @return zk cluster started.
100   */
101  public MiniZooKeeperCluster startMiniZKCluster(int zooKeeperServerNum, int... clientPortList)
102    throws Exception {
103    setupClusterTestDir();
104    return startMiniZKCluster(clusterTestDir, zooKeeperServerNum, clientPortList);
105  }
106
107  /**
108   * Start a mini ZK cluster. If the property "test.hbase.zookeeper.property.clientPort" is set the
109   * port mentioned is used as the default port for ZooKeeper.
110   */
111  private MiniZooKeeperCluster startMiniZKCluster(File dir, int zooKeeperServerNum,
112    int[] clientPortList) throws Exception {
113    if (this.zkCluster != null) {
114      throw new IOException("Cluster already running at " + dir);
115    }
116    this.passedZkCluster = false;
117    this.zkCluster = new MiniZooKeeperCluster(this.getConfiguration());
118    int defPort = this.conf.getInt("test.hbase.zookeeper.property.clientPort", 0);
119    if (defPort > 0) {
120      // If there is a port in the config file, we use it.
121      this.zkCluster.setDefaultClientPort(defPort);
122    }
123
124    if (clientPortList != null) {
125      // Ignore extra client ports
126      int clientPortListSize = Math.min(clientPortList.length, zooKeeperServerNum);
127      for (int i = 0; i < clientPortListSize; i++) {
128        this.zkCluster.addClientPort(clientPortList[i]);
129      }
130    }
131    int clientPort = this.zkCluster.startup(dir, zooKeeperServerNum);
132    this.conf.set(HConstants.ZOOKEEPER_CLIENT_PORT, Integer.toString(clientPort));
133    return this.zkCluster;
134  }
135
136  public MiniZooKeeperCluster getZkCluster() {
137    return zkCluster;
138  }
139
140  public void setZkCluster(MiniZooKeeperCluster zkCluster) {
141    this.passedZkCluster = true;
142    this.zkCluster = zkCluster;
143    conf.setInt(HConstants.ZOOKEEPER_CLIENT_PORT, zkCluster.getClientPort());
144  }
145
146  /**
147   * Shuts down zk cluster created by call to {@link #startMiniZKCluster()} or does nothing.
148   * @see #startMiniZKCluster()
149   */
150  public void shutdownMiniZKCluster() throws IOException {
151    if (!passedZkCluster && this.zkCluster != null) {
152      this.zkCluster.shutdown();
153      this.zkCluster = null;
154    }
155  }
156
157  /**
158   * Returns a ZKWatcher instance. This instance is shared between HBaseTestingUtility instance
159   * users. Don't close it, it will be closed automatically when the cluster shutdowns
160   * @return The ZKWatcher instance.
161   */
162  public ZKWatcher getZooKeeperWatcher() throws IOException {
163    if (zooKeeperWatcher == null) {
164      zooKeeperWatcher = new ZKWatcher(conf, "testing utility", new Abortable() {
165        @Override
166        public void abort(String why, Throwable e) {
167          throw new RuntimeException("Unexpected abort in HBaseZKTestingUtility:" + why, e);
168        }
169
170        @Override
171        public boolean isAborted() {
172          return false;
173        }
174      });
175    }
176    return zooKeeperWatcher;
177  }
178
179  /**
180   * Returns true if we removed the test dirs
181   */
182  @Override
183  public boolean cleanupTestDir() {
184    boolean ret = super.cleanupTestDir();
185    if (deleteDir(this.clusterTestDir)) {
186      this.clusterTestDir = null;
187      return ret;
188    }
189    return false;
190  }
191}