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.testing;
019
020import java.util.Arrays;
021import java.util.List;
022import org.apache.commons.lang3.StringUtils;
023import org.apache.hadoop.conf.Configuration;
024import org.apache.hadoop.hbase.StartTestingClusterOption;
025import org.apache.yetus.audience.InterfaceAudience;
026
027/**
028 * Options for starting up a mini testing cluster {@link TestingHBaseCluster} (including an hbase,
029 * dfs and zookeeper clusters) in test. The options include HDFS options to build mini dfs cluster,
030 * Zookeeper options to build mini zk cluster, and mostly HBase options to build mini hbase cluster.
031 * To create an object, use a {@link Builder}. Example usage:
032 *
033 * <pre>
034 *    TestingHBaseClusterOption option = TestingHBaseClusterOption.builder().
035 *        .numMasters(3).createWALDir(true).build();
036 * </pre>
037 *
038 * Default values can be found in {@link Builder}.
039 */
040@InterfaceAudience.Public
041public final class TestingHBaseClusterOption {
042
043  /**
044   * Configuration for this testing cluster. Can be {@code null}.
045   */
046  private final Configuration conf;
047
048  /**
049   * Number of masters to start up. We'll start this many hbase masters.
050   */
051  private final int numMasters;
052
053  /**
054   * Number of masters that always remain standby. These set of masters never transition to active
055   * even if an active master does not exist.
056   */
057  private final int numAlwaysStandByMasters;
058
059  /**
060   * Number of region servers to start up. If this value is > 1, then make sure config
061   * "hbase.regionserver.info.port" is -1 (i.e. no ui per regionserver) otherwise bind errors.
062   */
063  private final int numRegionServers;
064
065  /**
066   * Ports that RegionServer should use. Pass ports if you want to test cluster restart where for
067   * sure the regionservers come up on same address+port (but just with different startcode); by
068   * default mini hbase clusters choose new arbitrary ports on each cluster start.
069   */
070  private final List<Integer> rsPorts;
071
072  /**
073   * Number of datanodes. Used to create mini DSF cluster. Surpassed by {@link #dataNodeHosts} size.
074   */
075  private final int numDataNodes;
076
077  /**
078   * The hostnames of DataNodes to run on. This is useful if you want to run datanode on distinct
079   * hosts for things like HDFS block location verification. If you start MiniDFSCluster without
080   * host names, all instances of the datanodes will have the same host name.
081   */
082  private final String[] dataNodeHosts;
083
084  /**
085   * Number of Zookeeper servers.
086   */
087  private final int numZkServers;
088
089  /**
090   * Whether to create a new root or data directory path. If true, the newly created data directory
091   * will be configured as HBase rootdir. This will overwrite existing root directory config.
092   */
093  private final boolean createRootDir;
094
095  /**
096   * Whether to create a new WAL directory. If true, the newly created directory will be configured
097   * as HBase wal.dir which is separate from HBase rootdir.
098   */
099  private final boolean createWALDir;
100
101  private final String externalDfsUri;
102
103  private final String externalZkConnectString;
104
105  /**
106   * Private constructor. Use {@link Builder#build()}.
107   */
108  private TestingHBaseClusterOption(Configuration conf, int numMasters, int numAlwaysStandByMasters,
109    int numRegionServers, List<Integer> rsPorts, int numDataNodes, String[] dataNodeHosts,
110    int numZkServers, boolean createRootDir, boolean createWALDir, String externalDfsUri,
111    String externalZkConnectString) {
112    this.conf = conf;
113    this.numMasters = numMasters;
114    this.numAlwaysStandByMasters = numAlwaysStandByMasters;
115    this.numRegionServers = numRegionServers;
116    this.rsPorts = rsPorts;
117    this.numDataNodes = numDataNodes;
118    this.dataNodeHosts = dataNodeHosts;
119    this.numZkServers = numZkServers;
120    this.createRootDir = createRootDir;
121    this.createWALDir = createWALDir;
122    this.externalDfsUri = externalDfsUri;
123    this.externalZkConnectString = externalZkConnectString;
124  }
125
126  public Configuration conf() {
127    return conf;
128  }
129
130  public int getNumMasters() {
131    return numMasters;
132  }
133
134  public int getNumAlwaysStandByMasters() {
135    return numAlwaysStandByMasters;
136  }
137
138  public int getNumRegionServers() {
139    return numRegionServers;
140  }
141
142  public List<Integer> getRsPorts() {
143    return rsPorts;
144  }
145
146  public int getNumDataNodes() {
147    return numDataNodes;
148  }
149
150  public String[] getDataNodeHosts() {
151    return dataNodeHosts;
152  }
153
154  public int getNumZkServers() {
155    return numZkServers;
156  }
157
158  public boolean isCreateRootDir() {
159    return createRootDir;
160  }
161
162  public boolean isCreateWALDir() {
163    return createWALDir;
164  }
165
166  public String getExternalDfsUri() {
167    return externalDfsUri;
168  }
169
170  public String getExternalZkConnectString() {
171    return externalZkConnectString;
172  }
173
174  @Override
175  public String toString() {
176    return "StartMiniClusterOption{" + "numMasters=" + numMasters + ", numRegionServers="
177      + numRegionServers + ", rsPorts=" + StringUtils.join(rsPorts) + ", numDataNodes="
178      + numDataNodes + ", dataNodeHosts=" + Arrays.toString(dataNodeHosts) + ", numZkServers="
179      + numZkServers + ", createRootDir=" + createRootDir + ", createWALDir=" + createWALDir + '}';
180  }
181
182  /**
183   * Convert to the internal option. Not for public use so package private.
184   */
185  StartTestingClusterOption convert() {
186    return StartTestingClusterOption.builder().numMasters(numMasters)
187      .numAlwaysStandByMasters(numAlwaysStandByMasters).numRegionServers(numRegionServers)
188      .rsPorts(rsPorts).numDataNodes(numDataNodes).dataNodeHosts(dataNodeHosts)
189      .numZkServers(numZkServers).createRootDir(createRootDir).createWALDir(createWALDir).build();
190  }
191
192  /**
193   * Returns a new builder.
194   */
195  public static Builder builder() {
196    return new Builder();
197  }
198
199  /**
200   * Builder pattern for creating an {@link TestingHBaseClusterOption}. The default values of its
201   * fields should be considered public and constant. Changing the default values may cause other
202   * tests fail.
203   */
204  public static final class Builder {
205    private Configuration conf;
206    private int numMasters = 1;
207    private int numAlwaysStandByMasters = 0;
208    private int numRegionServers = 1;
209    private List<Integer> rsPorts = null;
210    private int numDataNodes = 1;
211    private String[] dataNodeHosts = null;
212    private int numZkServers = 1;
213    private boolean createRootDir = false;
214    private boolean createWALDir = false;
215    private String externalDfsUri = null;
216    private String externalZkConnectString = null;
217
218    private Builder() {
219    }
220
221    public TestingHBaseClusterOption build() {
222      if (dataNodeHosts != null && dataNodeHosts.length != 0) {
223        numDataNodes = dataNodeHosts.length;
224      }
225      return new TestingHBaseClusterOption(conf, numMasters, numAlwaysStandByMasters,
226        numRegionServers, rsPorts, numDataNodes, dataNodeHosts, numZkServers, createRootDir,
227        createWALDir, externalDfsUri, externalZkConnectString);
228    }
229
230    public Builder conf(Configuration conf) {
231      this.conf = conf;
232      return this;
233    }
234
235    public Builder numMasters(int numMasters) {
236      this.numMasters = numMasters;
237      return this;
238    }
239
240    public Builder numAlwaysStandByMasters(int numAlwaysStandByMasters) {
241      this.numAlwaysStandByMasters = numAlwaysStandByMasters;
242      return this;
243    }
244
245    public Builder numRegionServers(int numRegionServers) {
246      this.numRegionServers = numRegionServers;
247      return this;
248    }
249
250    public Builder rsPorts(List<Integer> rsPorts) {
251      this.rsPorts = rsPorts;
252      return this;
253    }
254
255    public Builder numDataNodes(int numDataNodes) {
256      this.numDataNodes = numDataNodes;
257      return this;
258    }
259
260    public Builder dataNodeHosts(String[] dataNodeHosts) {
261      this.dataNodeHosts = dataNodeHosts;
262      return this;
263    }
264
265    public Builder numZkServers(int numZkServers) {
266      this.numZkServers = numZkServers;
267      return this;
268    }
269
270    public Builder createRootDir(boolean createRootDir) {
271      this.createRootDir = createRootDir;
272      return this;
273    }
274
275    public Builder createWALDir(boolean createWALDir) {
276      this.createWALDir = createWALDir;
277      return this;
278    }
279
280    public Builder useExternalDfs(String uri) {
281      this.externalDfsUri = uri;
282      return this;
283    }
284
285    public Builder useExternalZooKeeper(String connectString) {
286      this.externalZkConnectString = connectString;
287      return this;
288    }
289  }
290}