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.IOException;
021import org.apache.hadoop.conf.Configuration;
022import org.apache.hadoop.util.ReflectionUtils;
023
024/**
025 * Facility for <strong>integration/system</strong> tests. This extends {@link HBaseTestingUtil} and
026 * adds-in the functionality needed by integration and system tests. This class understands
027 * distributed and pseudo-distributed/local cluster deployments, and abstracts those from the tests
028 * in this module.
029 * <p>
030 * IntegrationTestingUtility is constructed and used by the integration tests, but the tests
031 * themselves should not assume a particular deployment. They can rely on the methods in this class
032 * and HBaseCluster. Before the testing begins, the test should initialize the cluster by calling
033 * {@link #initializeCluster(int)}.
034 * <p>
035 * The cluster that is used defaults to a mini cluster, but it can be forced to use a distributed
036 * cluster by calling {@link #setUseDistributedCluster(Configuration)}. This method is invoked by
037 * test drivers (maven, IntegrationTestsDriver, etc) before initializing the cluster via
038 * {@link #initializeCluster(int)}. Individual tests should not directly call
039 * {@link #setUseDistributedCluster(Configuration)}.
040 */
041public class IntegrationTestingUtility extends HBaseTestingUtil {
042
043  public IntegrationTestingUtility() {
044    this(HBaseConfiguration.create());
045  }
046
047  public IntegrationTestingUtility(Configuration conf) {
048    super(conf);
049  }
050
051  /**
052   * Configuration that controls whether this utility assumes a running/deployed cluster. This is
053   * different than "hbase.cluster.distributed" since that parameter indicates whether the cluster
054   * is in an actual distributed environment, while this shows that there is a deployed (distributed
055   * or pseudo-distributed) cluster running, and we do not need to start a mini-cluster for tests.
056   */
057  public static final String IS_DISTRIBUTED_CLUSTER = "hbase.test.cluster.distributed";
058
059  /**
060   * Config for pluggable hbase cluster manager. Pass fully-qualified class name as property value.
061   * Drop the '.class' suffix.
062   */
063  public static final String HBASE_CLUSTER_MANAGER_CLASS = "hbase.it.clustermanager.class";
064  private static final Class<? extends ClusterManager> DEFAULT_HBASE_CLUSTER_MANAGER_CLASS =
065    HBaseClusterManager.class;
066
067  /**
068   * Initializes the state of the cluster. It starts a new in-process mini cluster, OR if we are
069   * given an already deployed distributed cluster it initializes the state.
070   * @param numSlaves Number of slaves to start up if we are booting a mini cluster. Otherwise we
071   *                  check whether this many nodes are available and throw an exception if not.
072   */
073  public void initializeCluster(int numSlaves) throws Exception {
074    if (isDistributedCluster()) {
075      createDistributedHBaseCluster();
076      checkNodeCount(numSlaves);
077    } else {
078      startMiniCluster(numSlaves);
079    }
080  }
081
082  /**
083   * Checks whether we have more than numSlaves nodes. Throws an exception otherwise.
084   */
085  public void checkNodeCount(int numSlaves) throws Exception {
086    HBaseClusterInterface cluster = getHBaseClusterInterface();
087    if (cluster.getClusterMetrics().getLiveServerMetrics().size() < numSlaves) {
088      throw new Exception("Cluster does not have enough nodes:" + numSlaves);
089    }
090  }
091
092  /**
093   * Restores the cluster to the initial state if it is a distributed cluster, otherwise, shutdowns
094   * the mini cluster.
095   */
096  public void restoreCluster() throws IOException {
097    if (isDistributedCluster()) {
098      getHBaseClusterInterface().restoreInitialStatus();
099    } else {
100      try {
101        shutdownMiniCluster();
102      } catch (Exception e) {
103        // re-wrap into IOException
104        throw new IOException(e);
105      }
106    }
107  }
108
109  /**
110   * Sets the configuration property to use a distributed cluster for the integration tests. Test
111   * drivers should use this to enforce cluster deployment.
112   */
113  public static void setUseDistributedCluster(Configuration conf) {
114    conf.setBoolean(IS_DISTRIBUTED_CLUSTER, true);
115    System.setProperty(IS_DISTRIBUTED_CLUSTER, "true");
116  }
117
118  /**
119   * Returns whether we are interacting with a distributed cluster as opposed to and in-process mini
120   * cluster or a local cluster.
121   * @see IntegrationTestingUtility#setUseDistributedCluster(Configuration)
122   */
123  public boolean isDistributedCluster() {
124    Configuration conf = getConfiguration();
125    boolean isDistributedCluster =
126      Boolean.parseBoolean(System.getProperty(IS_DISTRIBUTED_CLUSTER, "false"));
127    if (!isDistributedCluster) {
128      isDistributedCluster = conf.getBoolean(IS_DISTRIBUTED_CLUSTER, false);
129    }
130    return isDistributedCluster;
131  }
132
133  public void createDistributedHBaseCluster() throws IOException {
134    // if it is a distributed HBase cluster, use the conf provided by classpath
135    // to set hbase dir and fs.defaultFS.
136    // Since when the super class HBaseTestingUtility initializing, it will
137    // change hbase.rootdir to a local test dir.
138    // we use "original.defaultFS" and "original.hbase.dir" to restore them.
139    Configuration conf = getConfiguration();
140    if (conf.get("original.defaultFS") != null) {
141      conf.set("fs.defaultFS", conf.get("original.defaultFS"));
142    }
143    if (conf.get("original.hbase.dir") != null) {
144      conf.set(HConstants.HBASE_DIR, conf.get("original.hbase.dir"));
145    }
146    LOG.debug("Setting {} to {} since it is a distributed cluster", HConstants.HBASE_DIR,
147      conf.get(HConstants.HBASE_DIR));
148    Class<? extends ClusterManager> clusterManagerClass = conf.getClass(HBASE_CLUSTER_MANAGER_CLASS,
149      DEFAULT_HBASE_CLUSTER_MANAGER_CLASS, ClusterManager.class);
150    LOG.info("Instantiating {}", clusterManagerClass.getName());
151    ClusterManager clusterManager = ReflectionUtils.newInstance(clusterManagerClass, conf);
152    setHBaseCluster(new DistributedHBaseCluster(conf, clusterManager));
153    getAdmin();
154  }
155}