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.Map.Entry;
23  import java.util.Properties;
24  import java.util.Set;
25  
26  import org.apache.commons.cli.CommandLine;
27  import org.apache.commons.lang.StringUtils;
28  import org.apache.commons.logging.Log;
29  import org.apache.commons.logging.LogFactory;
30  import org.apache.hadoop.conf.Configuration;
31  import org.apache.hadoop.hbase.chaos.factories.MonkeyConstants;
32  import org.apache.hadoop.hbase.chaos.factories.MonkeyFactory;
33  import org.apache.hadoop.hbase.chaos.monkies.ChaosMonkey;
34  import org.apache.hadoop.hbase.util.AbstractHBaseTool;
35  import org.junit.After;
36  import org.junit.Before;
37  
38  /**
39   * Base class for HBase integration tests that want to use the Chaos Monkey.
40   * Usage: bin/hbase <sub_class_of_IntegrationTestBase> <options>
41   * Options: -h,--help Show usage
42   *          -m,--monkey <arg> Which chaos monkey to run
43   *          -monkeyProps <arg> The properties file for specifying chaos monkey properties.
44   *          -ncc Option to not clean up the cluster at the end.
45   */
46  public abstract class IntegrationTestBase extends AbstractHBaseTool {
47  
48    public static final String NO_CLUSTER_CLEANUP_LONG_OPT = "noClusterCleanUp";
49    public static final String MONKEY_LONG_OPT = "monkey";
50    public static final String CHAOS_MONKEY_PROPS = "monkeyProps";
51    private static final Log LOG = LogFactory.getLog(IntegrationTestBase.class);
52  
53    protected IntegrationTestingUtility util;
54    protected ChaosMonkey monkey;
55    protected String monkeyToUse;
56    protected Properties monkeyProps;
57    protected boolean noClusterCleanUp = false;
58  
59    public IntegrationTestBase() {
60      this(null);
61    }
62  
63    public IntegrationTestBase(String monkeyToUse) {
64      this.monkeyToUse = monkeyToUse;
65    }
66  
67    @Override
68    protected void addOptions() {
69      addOptWithArg("m", MONKEY_LONG_OPT, "Which chaos monkey to run");
70      addOptNoArg("ncc", NO_CLUSTER_CLEANUP_LONG_OPT,
71        "Don't clean up the cluster at the end");
72      addOptWithArg(CHAOS_MONKEY_PROPS, "The properties file for specifying chaos "
73          + "monkey properties.");
74    }
75  
76    /**
77     * This allows tests that subclass children of this base class such as
78     * {@link org.apache.hadoop.hbase.test.IntegrationTestReplication} to
79     * include the base options without having to also include the options from the test.
80     *
81     * @param cmd the command line
82     */
83    protected void processBaseOptions(CommandLine cmd) {
84      if (cmd.hasOption(MONKEY_LONG_OPT)) {
85        monkeyToUse = cmd.getOptionValue(MONKEY_LONG_OPT);
86      }
87      if (cmd.hasOption(NO_CLUSTER_CLEANUP_LONG_OPT)) {
88        noClusterCleanUp = true;
89      }
90      monkeyProps = new Properties();
91      // Add entries for the CM from hbase-site.xml as a convenience.
92      // Do this prior to loading from the properties file to make sure those in the properties
93      // file are given precedence to those in hbase-site.xml (backwards compatibility).
94      loadMonkeyProperties(monkeyProps, HBaseConfiguration.create());
95      if (cmd.hasOption(CHAOS_MONKEY_PROPS)) {
96        String chaosMonkeyPropsFile = cmd.getOptionValue(CHAOS_MONKEY_PROPS);
97        if (StringUtils.isNotEmpty(chaosMonkeyPropsFile)) {
98          try {
99            monkeyProps.load(this.getClass().getClassLoader()
100               .getResourceAsStream(chaosMonkeyPropsFile));
101         } catch (IOException e) {
102           LOG.warn(e);
103           System.exit(EXIT_FAILURE);
104         }
105       }
106     }
107   }
108 
109   /**
110    * Loads entries from the provided {@code conf} into {@code props} when the configuration key
111    * is one that may be configuring ChaosMonkey actions.
112    */
113   void loadMonkeyProperties(Properties props, Configuration conf) {
114     for (Entry<String,String> entry : conf) {
115       for (String prefix : MonkeyConstants.MONKEY_CONFIGURATION_KEY_PREFIXES) {
116         if (entry.getKey().startsWith(prefix)) {
117           props.put(entry.getKey(), entry.getValue());
118           break;
119         }
120       }
121     }
122   }
123 
124   @Override
125   protected void processOptions(CommandLine cmd) {
126     processBaseOptions(cmd);
127   }
128 
129   @Override
130   public Configuration getConf() {
131     Configuration c = super.getConf();
132     if (c == null && util != null) {
133       conf = util.getConfiguration();
134       c = conf;
135     }
136     return c;
137   }
138 
139   @Override
140   protected int doWork() throws Exception {
141     ChoreService choreService = null;
142 
143     // Launches chore for refreshing kerberos credentials if security is enabled.
144     // Please see http://hbase.apache.org/book.html#_running_canary_in_a_kerberos_enabled_cluster
145     // for more details.
146     final ScheduledChore authChore = AuthUtil.getAuthChore(conf);
147     if (authChore != null) {
148       choreService = new ChoreService("INTEGRATION_TEST");
149       choreService.scheduleChore(authChore);
150     }
151 
152     setUp();
153     int result = -1;
154     try {
155       result = runTestFromCommandLine();
156     } finally {
157       cleanUp();
158     }
159 
160     if (choreService != null) {
161       choreService.shutdown();
162     }
163 
164     return result;
165   }
166 
167   @Before
168   public void setUp() throws Exception {
169     setUpCluster();
170     setUpMonkey();
171   }
172 
173   @After
174   public void cleanUp() throws Exception {
175     cleanUpMonkey();
176     cleanUpCluster();
177   }
178 
179   public void setUpMonkey() throws Exception {
180     util = getTestingUtil(getConf());
181     MonkeyFactory fact = MonkeyFactory.getFactory(monkeyToUse);
182     if (fact == null) {
183       fact = getDefaultMonkeyFactory();
184     }
185     monkey = fact.setUtil(util)
186                  .setTableName(getTablename())
187                  .setProperties(monkeyProps)
188                  .setColumnFamilies(getColumnFamilies()).build();
189     startMonkey();
190   }
191 
192   protected MonkeyFactory getDefaultMonkeyFactory() {
193     // Run with no monkey in distributed context, with real monkey in local test context.
194     return MonkeyFactory.getFactory(
195       util.isDistributedCluster() ? MonkeyFactory.CALM : MonkeyFactory.SLOW_DETERMINISTIC);
196   }
197 
198   protected void startMonkey() throws Exception {
199     monkey.start();
200   }
201 
202   public void cleanUpMonkey() throws Exception {
203     cleanUpMonkey("Ending test");
204   }
205 
206   protected void cleanUpMonkey(String why) throws Exception {
207     if (monkey != null && !monkey.isStopped()) {
208       monkey.stop(why);
209       monkey.waitForStop();
210     }
211   }
212 
213   protected IntegrationTestingUtility getTestingUtil(Configuration conf) {
214     if (this.util == null) {
215       if (conf == null) {
216         this.util = new IntegrationTestingUtility();
217         this.setConf(util.getConfiguration());
218       } else {
219         this.util = new IntegrationTestingUtility(conf);
220       }
221     }
222     return util;
223   }
224 
225   public abstract void setUpCluster() throws Exception;
226 
227   public void cleanUpCluster() throws Exception {
228     if (util.isDistributedCluster() &&  (monkey == null || !monkey.isDestructive())) {
229       noClusterCleanUp = true;
230     }
231     if (noClusterCleanUp) {
232       LOG.debug("noClusterCleanUp is set, skip restoring the cluster");
233       return;
234     }
235     LOG.debug("Restoring the cluster");
236     util.restoreCluster();
237     LOG.debug("Done restoring the cluster");
238   }
239 
240   public abstract int runTestFromCommandLine() throws Exception;
241 
242   /**
243    * Provides the name of the table that is protected from random Chaos monkey activity
244    * @return table to not delete.
245    */
246   public abstract TableName getTablename();
247 
248   /**
249    * Provides the name of the CFs that are protected from random Chaos monkey activity (alter)
250    * @return set of cf names to protect.
251    */
252   protected abstract Set<String> getColumnFamilies();
253 }