View Javadoc

1   /**
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *     http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing, software
13   * distributed under the License is distributed on an "AS IS" BASIS,
14   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15   * See the License for the specific language governing permissions and
16   * limitations under the License.
17   */
18  
19  package org.apache.hadoop.hbase;
20  
21  import java.io.IOException;
22  import java.util.Properties;
23  import java.util.Set;
24  
25  import org.apache.commons.cli.CommandLine;
26  import org.apache.commons.lang.StringUtils;
27  import org.apache.commons.logging.Log;
28  import org.apache.commons.logging.LogFactory;
29  import org.apache.hadoop.conf.Configuration;
30  import org.apache.hadoop.hbase.chaos.factories.MonkeyFactory;
31  import org.apache.hadoop.hbase.chaos.monkies.ChaosMonkey;
32  import org.apache.hadoop.hbase.util.AbstractHBaseTool;
33  import org.junit.After;
34  import org.junit.Before;
35  
36  /**
37   * Base class for HBase integration tests that want to use the Chaos Monkey.
38   * Usage: bin/hbase <sub_class_of_IntegrationTestBase> <options>
39   * Options: -h,--help Show usage
40   *          -m,--monkey <arg> Which chaos monkey to run
41   *          -monkeyProps <arg> The properties file for specifying chaos monkey properties.
42   *          -ncc Option to not clean up the cluster at the end.
43   */
44  public abstract class IntegrationTestBase extends AbstractHBaseTool {
45  
46    public static final String NO_CLUSTER_CLEANUP_LONG_OPT = "noClusterCleanUp";
47    public static final String MONKEY_LONG_OPT = "monkey";
48    public static final String CHAOS_MONKEY_PROPS = "monkeyProps";
49    private static final Log LOG = LogFactory.getLog(IntegrationTestBase.class);
50  
51    protected IntegrationTestingUtility util;
52    protected ChaosMonkey monkey;
53    protected String monkeyToUse;
54    protected Properties monkeyProps;
55    protected boolean noClusterCleanUp = false;
56  
57    public IntegrationTestBase() {
58      this(null);
59    }
60  
61    public IntegrationTestBase(String monkeyToUse) {
62      this.monkeyToUse = monkeyToUse;
63    }
64  
65    @Override
66    protected void addOptions() {
67      addOptWithArg("m", MONKEY_LONG_OPT, "Which chaos monkey to run");
68      addOptNoArg("ncc", NO_CLUSTER_CLEANUP_LONG_OPT,
69        "Don't clean up the cluster at the end");
70      addOptWithArg(CHAOS_MONKEY_PROPS, "The properties file for specifying chaos "
71          + "monkey properties.");
72    }
73  
74    /**
75     * This allows tests that subclass children of this base class such as
76     * {@link org.apache.hadoop.hbase.test.IntegrationTestReplication} to
77     * include the base options without having to also include the options from the test.
78     *
79     * @param cmd the command line
80     */
81    protected void processBaseOptions(CommandLine cmd) {
82      if (cmd.hasOption(MONKEY_LONG_OPT)) {
83        monkeyToUse = cmd.getOptionValue(MONKEY_LONG_OPT);
84      }
85      if (cmd.hasOption(NO_CLUSTER_CLEANUP_LONG_OPT)) {
86        noClusterCleanUp = true;
87      }
88      monkeyProps = new Properties();
89      if (cmd.hasOption(CHAOS_MONKEY_PROPS)) {
90        String chaosMonkeyPropsFile = cmd.getOptionValue(CHAOS_MONKEY_PROPS);
91        if (StringUtils.isNotEmpty(chaosMonkeyPropsFile)) {
92          try {
93            monkeyProps.load(this.getClass().getClassLoader()
94                .getResourceAsStream(chaosMonkeyPropsFile));
95          } catch (IOException e) {
96            LOG.warn(e);
97            System.exit(EXIT_FAILURE);
98          }
99        }
100     }
101   }
102 
103   @Override
104   protected void processOptions(CommandLine cmd) {
105     processBaseOptions(cmd);
106   }
107 
108   @Override
109   public Configuration getConf() {
110     Configuration c = super.getConf();
111     if (c == null && util != null) {
112       conf = util.getConfiguration();
113       c = conf;
114     }
115     return c;
116   }
117 
118   @Override
119   protected int doWork() throws Exception {
120     setUp();
121     int result = -1;
122     try {
123       result = runTestFromCommandLine();
124     } finally {
125       cleanUp();
126     }
127 
128     return result;
129   }
130 
131   @Before
132   public void setUp() throws Exception {
133     setUpCluster();
134     setUpMonkey();
135   }
136 
137   @After
138   public void cleanUp() throws Exception {
139     cleanUpMonkey();
140     cleanUpCluster();
141   }
142 
143   public void setUpMonkey() throws Exception {
144     util = getTestingUtil(getConf());
145     MonkeyFactory fact = MonkeyFactory.getFactory(monkeyToUse);
146     if (fact == null) {
147       fact = getDefaultMonkeyFactory();
148     }
149     monkey = fact.setUtil(util)
150                  .setTableName(getTablename())
151                  .setProperties(monkeyProps)
152                  .setColumnFamilies(getColumnFamilies()).build();
153     startMonkey();
154   }
155 
156   protected MonkeyFactory getDefaultMonkeyFactory() {
157     // Run with no monkey in distributed context, with real monkey in local test context.
158     return MonkeyFactory.getFactory(
159       util.isDistributedCluster() ? MonkeyFactory.CALM : MonkeyFactory.SLOW_DETERMINISTIC);
160   }
161 
162   protected void startMonkey() throws Exception {
163     monkey.start();
164   }
165 
166   public void cleanUpMonkey() throws Exception {
167     cleanUpMonkey("Ending test");
168   }
169 
170   protected void cleanUpMonkey(String why) throws Exception {
171     if (monkey != null && !monkey.isStopped()) {
172       monkey.stop(why);
173       monkey.waitForStop();
174     }
175   }
176 
177   protected IntegrationTestingUtility getTestingUtil(Configuration conf) {
178     if (this.util == null) {
179       if (conf == null) {
180         this.util = new IntegrationTestingUtility();
181         this.setConf(util.getConfiguration());
182       } else {
183         this.util = new IntegrationTestingUtility(conf);
184       }
185     }
186     return util;
187   }
188 
189   public abstract void setUpCluster() throws Exception;
190 
191   public void cleanUpCluster() throws Exception {
192     if (util.isDistributedCluster() &&  (monkey == null || !monkey.isDestructive())) {
193       noClusterCleanUp = true;
194     }
195     if (noClusterCleanUp) {
196       LOG.debug("noClusterCleanUp is set, skip restoring the cluster");
197       return;
198     }
199     LOG.debug("Restoring the cluster");
200     util.restoreCluster();
201     LOG.debug("Done restoring the cluster");
202   }
203 
204   public abstract int runTestFromCommandLine() throws Exception;
205 
206   /**
207    * Provides the name of the table that is protected from random Chaos monkey activity
208    * @return table to not delete.
209    */
210   public abstract TableName getTablename();
211 
212   /**
213    * Provides the name of the CFs that are protected from random Chaos monkey activity (alter)
214    * @return set of cf names to protect.
215    */
216   protected abstract Set<String> getColumnFamilies();
217 }