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;
027
028/**
029 * Helpers for testing HBase that do not depend on specific server/etc. things. The main difference
030 * from {@link HBaseCommonTestingUtility} is that we can start a zookeeper cluster.
031 */
032@InterfaceAudience.Public
033public class HBaseZKTestingUtility extends HBaseCommonTestingUtility {
034  private MiniZooKeeperCluster zkCluster;
035
036  /**
037   * Set if we were passed a zkCluster. If so, we won't shutdown zk as part of general shutdown.
038   */
039  private boolean passedZkCluster;
040
041  protected ZKWatcher zooKeeperWatcher;
042
043  /** Directory (a subdirectory of dataTestDir) used by the dfs cluster if any */
044  protected File clusterTestDir;
045
046  public HBaseZKTestingUtility() {
047    this(HBaseConfiguration.create());
048  }
049
050  public HBaseZKTestingUtility(Configuration conf) {
051    super(conf);
052  }
053
054  /**
055   * @return Where the cluster will write data on the local subsystem. Creates it if it does not
056   *         exist already. A subdir of {@code HBaseCommonTestingUtility#getBaseTestDir()}
057   */
058  Path getClusterTestDir() {
059    if (clusterTestDir == null) {
060      setupClusterTestDir();
061    }
062    return new Path(clusterTestDir.getAbsolutePath());
063  }
064
065  /**
066   * Creates a directory for the cluster, under the test data
067   */
068  protected void setupClusterTestDir() {
069    if (clusterTestDir != null) {
070      return;
071    }
072
073    // Using randomUUID ensures that multiple clusters can be launched by
074    // a same test, if it stops & starts them
075    Path testDir = getDataTestDir("cluster_" + getRandomUUID().toString());
076    clusterTestDir = new File(testDir.toString()).getAbsoluteFile();
077    // Have it cleaned up on exit
078    boolean b = deleteOnExit();
079    if (b) {
080      clusterTestDir.deleteOnExit();
081    }
082    LOG.info("Created new mini-cluster data directory: " + clusterTestDir + ", deleteOnExit=" + b);
083  }
084
085  /**
086   * Call this if you only want a zk cluster.
087   * @see #shutdownMiniZKCluster()
088   * @return zk cluster started.
089   */
090  public MiniZooKeeperCluster startMiniZKCluster() throws Exception {
091    return startMiniZKCluster(1);
092  }
093
094  /**
095   * Call this if you only want a zk cluster.
096   * @see #shutdownMiniZKCluster()
097   * @return zk cluster started.
098   */
099  public MiniZooKeeperCluster startMiniZKCluster(int zooKeeperServerNum, int... clientPortList)
100    throws Exception {
101    setupClusterTestDir();
102    return startMiniZKCluster(clusterTestDir, zooKeeperServerNum, clientPortList);
103  }
104
105  /**
106   * Start a mini ZK cluster. If the property "test.hbase.zookeeper.property.clientPort" is set the
107   * port mentioned is used as the default port for ZooKeeper.
108   */
109  private MiniZooKeeperCluster startMiniZKCluster(File dir, int zooKeeperServerNum,
110    int[] clientPortList) throws Exception {
111    if (this.zkCluster != null) {
112      throw new IOException("Cluster already running at " + dir);
113    }
114    this.passedZkCluster = false;
115    this.zkCluster = new MiniZooKeeperCluster(this.getConfiguration());
116    int defPort = this.conf.getInt("test.hbase.zookeeper.property.clientPort", 0);
117    if (defPort > 0) {
118      // If there is a port in the config file, we use it.
119      this.zkCluster.setDefaultClientPort(defPort);
120    }
121
122    if (clientPortList != null) {
123      // Ignore extra client ports
124      int clientPortListSize = Math.min(clientPortList.length, zooKeeperServerNum);
125      for (int i = 0; i < clientPortListSize; i++) {
126        this.zkCluster.addClientPort(clientPortList[i]);
127      }
128    }
129    int clientPort = this.zkCluster.startup(dir, zooKeeperServerNum);
130    this.conf.set(HConstants.ZOOKEEPER_CLIENT_PORT, Integer.toString(clientPort));
131    return this.zkCluster;
132  }
133
134  public MiniZooKeeperCluster getZkCluster() {
135    return zkCluster;
136  }
137
138  public void setZkCluster(MiniZooKeeperCluster zkCluster) {
139    this.passedZkCluster = true;
140    this.zkCluster = zkCluster;
141    conf.setInt(HConstants.ZOOKEEPER_CLIENT_PORT, zkCluster.getClientPort());
142  }
143
144  /**
145   * Shuts down zk cluster created by call to {@link #startMiniZKCluster()} or does nothing.
146   * @see #startMiniZKCluster()
147   */
148  public void shutdownMiniZKCluster() throws IOException {
149    if (!passedZkCluster && this.zkCluster != null) {
150      this.zkCluster.shutdown();
151      this.zkCluster = null;
152    }
153  }
154
155  /**
156   * Returns a ZKWatcher instance. This instance is shared between HBaseTestingUtility instance
157   * users. Don't close it, it will be closed automatically when the cluster shutdowns
158   * @return The ZKWatcher instance.
159   */
160  public synchronized ZKWatcher getZooKeeperWatcher() throws IOException {
161    if (zooKeeperWatcher == null) {
162      zooKeeperWatcher = new ZKWatcher(conf, "testing utility", new Abortable() {
163        @Override
164        public void abort(String why, Throwable e) {
165          throw new RuntimeException("Unexpected abort in HBaseZKTestingUtility:" + why, e);
166        }
167
168        @Override
169        public boolean isAborted() {
170          return false;
171        }
172      });
173    }
174    return zooKeeperWatcher;
175  }
176
177  /**
178   * Gets a ZKWatcher.
179   */
180  public static ZKWatcher getZooKeeperWatcher(HBaseZKTestingUtility testUtil) throws IOException {
181    return new ZKWatcher(testUtil.getConfiguration(), "unittest", new Abortable() {
182      boolean aborted = false;
183
184      @Override
185      public void abort(String why, Throwable e) {
186        aborted = true;
187        throw new RuntimeException("Fatal ZK error, why=" + why, e);
188      }
189
190      @Override
191      public boolean isAborted() {
192        return aborted;
193      }
194    });
195  }
196
197  /** Returns True if we removed the test dirs */
198  @Override
199  public boolean cleanupTestDir() {
200    boolean ret = super.cleanupTestDir();
201    if (deleteDir(this.clusterTestDir)) {
202      this.clusterTestDir = null;
203      return ret;
204    }
205    return false;
206  }
207}