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     ChoreService choreService = null;
121 
122     // Launches chore for refreshing kerberos credentials if security is enabled.
123     // Please see http://hbase.apache.org/book.html#_running_canary_in_a_kerberos_enabled_cluster
124     // for more details.
125     final ScheduledChore authChore = AuthUtil.getAuthChore(conf);
126     if (authChore != null) {
127       choreService = new ChoreService("INTEGRATION_TEST");
128       choreService.scheduleChore(authChore);
129     }
130 
131     setUp();
132     int result = -1;
133     try {
134       result = runTestFromCommandLine();
135     } finally {
136       cleanUp();
137     }
138 
139     if (choreService != null) {
140       choreService.shutdown();
141     }
142 
143     return result;
144   }
145 
146   @Before
147   public void setUp() throws Exception {
148     setUpCluster();
149     setUpMonkey();
150   }
151 
152   @After
153   public void cleanUp() throws Exception {
154     cleanUpMonkey();
155     cleanUpCluster();
156   }
157 
158   public void setUpMonkey() throws Exception {
159     util = getTestingUtil(getConf());
160     MonkeyFactory fact = MonkeyFactory.getFactory(monkeyToUse);
161     if (fact == null) {
162       fact = getDefaultMonkeyFactory();
163     }
164     monkey = fact.setUtil(util)
165                  .setTableName(getTablename())
166                  .setProperties(monkeyProps)
167                  .setColumnFamilies(getColumnFamilies()).build();
168     startMonkey();
169   }
170 
171   protected MonkeyFactory getDefaultMonkeyFactory() {
172     // Run with no monkey in distributed context, with real monkey in local test context.
173     return MonkeyFactory.getFactory(
174       util.isDistributedCluster() ? MonkeyFactory.CALM : MonkeyFactory.SLOW_DETERMINISTIC);
175   }
176 
177   protected void startMonkey() throws Exception {
178     monkey.start();
179   }
180 
181   public void cleanUpMonkey() throws Exception {
182     cleanUpMonkey("Ending test");
183   }
184 
185   protected void cleanUpMonkey(String why) throws Exception {
186     if (monkey != null && !monkey.isStopped()) {
187       monkey.stop(why);
188       monkey.waitForStop();
189     }
190   }
191 
192   protected IntegrationTestingUtility getTestingUtil(Configuration conf) {
193     if (this.util == null) {
194       if (conf == null) {
195         this.util = new IntegrationTestingUtility();
196         this.setConf(util.getConfiguration());
197       } else {
198         this.util = new IntegrationTestingUtility(conf);
199       }
200     }
201     return util;
202   }
203 
204   public abstract void setUpCluster() throws Exception;
205 
206   public void cleanUpCluster() throws Exception {
207     if (util.isDistributedCluster() &&  (monkey == null || !monkey.isDestructive())) {
208       noClusterCleanUp = true;
209     }
210     if (noClusterCleanUp) {
211       LOG.debug("noClusterCleanUp is set, skip restoring the cluster");
212       return;
213     }
214     LOG.debug("Restoring the cluster");
215     util.restoreCluster();
216     LOG.debug("Done restoring the cluster");
217   }
218 
219   public abstract int runTestFromCommandLine() throws Exception;
220 
221   /**
222    * Provides the name of the table that is protected from random Chaos monkey activity
223    * @return table to not delete.
224    */
225   public abstract TableName getTablename();
226 
227   /**
228    * Provides the name of the CFs that are protected from random Chaos monkey activity (alter)
229    * @return set of cf names to protect.
230    */
231   protected abstract Set<String> getColumnFamilies();
232 }