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.constraint;
019
020import static org.junit.Assert.assertFalse;
021import static org.junit.Assert.assertTrue;
022import static org.junit.Assert.fail;
023
024import org.apache.hadoop.hbase.HBaseClassTestRule;
025import org.apache.hadoop.hbase.HBaseTestingUtility;
026import org.apache.hadoop.hbase.HColumnDescriptor;
027import org.apache.hadoop.hbase.HTableDescriptor;
028import org.apache.hadoop.hbase.TableName;
029import org.apache.hadoop.hbase.client.Put;
030import org.apache.hadoop.hbase.client.Table;
031import org.apache.hadoop.hbase.coprocessor.CoprocessorHost;
032import org.apache.hadoop.hbase.testclassification.MediumTests;
033import org.apache.hadoop.hbase.testclassification.MiscTests;
034import org.apache.hadoop.hbase.util.Bytes;
035import org.junit.After;
036import org.junit.AfterClass;
037import org.junit.BeforeClass;
038import org.junit.ClassRule;
039import org.junit.Test;
040import org.junit.experimental.categories.Category;
041import org.slf4j.Logger;
042import org.slf4j.LoggerFactory;
043
044/**
045 * Do the complex testing of constraints against a minicluster
046 */
047@Category({MiscTests.class, MediumTests.class})
048public class TestConstraint {
049
050  @ClassRule
051  public static final HBaseClassTestRule CLASS_RULE =
052      HBaseClassTestRule.forClass(TestConstraint.class);
053
054  private static final Logger LOG = LoggerFactory
055      .getLogger(TestConstraint.class);
056
057  private static HBaseTestingUtility util;
058  private static final TableName tableName = TableName.valueOf("test");
059  private static final byte[] dummy = Bytes.toBytes("dummy");
060  private static final byte[] row1 = Bytes.toBytes("r1");
061  private static final byte[] test = Bytes.toBytes("test");
062
063  @BeforeClass
064  public static void setUpBeforeClass() throws Exception {
065    util = new HBaseTestingUtility();
066    util.getConfiguration().setBoolean(CoprocessorHost.ABORT_ON_ERROR_KEY, false);
067    util.startMiniCluster();
068  }
069
070  /**
071   * Test that we run a passing constraint
072   * @throws Exception
073   */
074  @SuppressWarnings("unchecked")
075  @Test
076  public void testConstraintPasses() throws Exception {
077    // create the table
078    // it would be nice if this was also a method on the util
079    HTableDescriptor desc = new HTableDescriptor(tableName);
080    for (byte[] family : new byte[][] { dummy, test }) {
081      desc.addFamily(new HColumnDescriptor(family));
082    }
083    // add a constraint
084    Constraints.add(desc, CheckWasRunConstraint.class);
085
086    util.getAdmin().createTable(desc);
087    Table table = util.getConnection().getTable(tableName);
088    try {
089      // test that we don't fail on a valid put
090      Put put = new Put(row1);
091      byte[] value = Bytes.toBytes(Integer.toString(10));
092      byte[] qualifier = new byte[0];
093      put.addColumn(dummy, qualifier, value);
094      table.put(put);
095    } finally {
096      table.close();
097    }
098    assertTrue(CheckWasRunConstraint.wasRun);
099  }
100
101  /**
102   * Test that constraints will fail properly
103   * @throws Exception
104   */
105  @SuppressWarnings("unchecked")
106  @Test
107  public void testConstraintFails() throws Exception {
108
109    // create the table
110    // it would be nice if this was also a method on the util
111    HTableDescriptor desc = new HTableDescriptor(tableName);
112    for (byte[] family : new byte[][] { dummy, test }) {
113      desc.addFamily(new HColumnDescriptor(family));
114    }
115
116    // add a constraint that is sure to fail
117    Constraints.add(desc, AllFailConstraint.class);
118
119    util.getAdmin().createTable(desc);
120    Table table = util.getConnection().getTable(tableName);
121
122    // test that we do fail on violation
123    Put put = new Put(row1);
124    byte[] qualifier = new byte[0];
125    put.addColumn(dummy, qualifier, Bytes.toBytes("fail"));
126    LOG.warn("Doing put in table");
127    try {
128      table.put(put);
129      fail("This put should not have suceeded - AllFailConstraint was not run!");
130    } catch (ConstraintException e) {
131      // expected
132    }
133    table.close();
134  }
135
136  /**
137   * Check that if we just disable one constraint, then
138   * @throws Throwable
139   */
140  @SuppressWarnings("unchecked")
141  @Test
142  public void testDisableConstraint() throws Throwable {
143    // create the table
144    HTableDescriptor desc = new HTableDescriptor(tableName);
145    // add a family to the table
146    for (byte[] family : new byte[][] { dummy, test }) {
147      desc.addFamily(new HColumnDescriptor(family));
148    }
149    // add a constraint to make sure it others get run
150    Constraints.add(desc, CheckWasRunConstraint.class);
151
152    // Add Constraint to check
153    Constraints.add(desc, AllFailConstraint.class);
154
155    // and then disable the failing constraint
156    Constraints.disableConstraint(desc, AllFailConstraint.class);
157
158    util.getAdmin().createTable(desc);
159    Table table = util.getConnection().getTable(tableName);
160    try {
161      // test that we don't fail because its disabled
162      Put put = new Put(row1);
163      byte[] qualifier = new byte[0];
164      put.addColumn(dummy, qualifier, Bytes.toBytes("pass"));
165      table.put(put);
166    } finally {
167      table.close();
168    }
169    assertTrue(CheckWasRunConstraint.wasRun);
170  }
171
172  /**
173   * Test that if we disable all constraints, then nothing gets run
174   * @throws Throwable
175   */
176  @SuppressWarnings("unchecked")
177  @Test
178  public void testDisableConstraints() throws Throwable {
179    // create the table
180    HTableDescriptor desc = new HTableDescriptor(tableName);
181    // add a family to the table
182    for (byte[] family : new byte[][] { dummy, test }) {
183      desc.addFamily(new HColumnDescriptor(family));
184    }
185    // add a constraint to check to see if is run
186    Constraints.add(desc, CheckWasRunConstraint.class);
187
188    // then disable all the constraints
189    Constraints.disable(desc);
190
191    util.getAdmin().createTable(desc);
192    Table table = util.getConnection().getTable(tableName);
193    try {
194      // test that we do fail on violation
195      Put put = new Put(row1);
196      byte[] qualifier = new byte[0];
197      put.addColumn(dummy, qualifier, Bytes.toBytes("pass"));
198      LOG.warn("Doing put in table");
199      table.put(put);
200    } finally {
201      table.close();
202    }
203    assertFalse(CheckWasRunConstraint.wasRun);
204  }
205
206  /**
207   * Check to make sure a constraint is unloaded when it fails
208   * @throws Exception
209   */
210  @Test
211  public void testIsUnloaded() throws Exception {
212    // create the table
213    HTableDescriptor desc = new HTableDescriptor(tableName);
214    // add a family to the table
215    for (byte[] family : new byte[][] { dummy, test }) {
216      desc.addFamily(new HColumnDescriptor(family));
217    }
218    // make sure that constraints are unloaded
219    Constraints.add(desc, RuntimeFailConstraint.class);
220    // add a constraint to check to see if is run
221    Constraints.add(desc, CheckWasRunConstraint.class);
222    CheckWasRunConstraint.wasRun = false;
223
224    util.getAdmin().createTable(desc);
225    Table table = util.getConnection().getTable(tableName);
226
227    // test that we do fail on violation
228    Put put = new Put(row1);
229    byte[] qualifier = new byte[0];
230    put.addColumn(dummy, qualifier, Bytes.toBytes("pass"));
231
232    try{
233    table.put(put);
234    fail("RuntimeFailConstraint wasn't triggered - this put shouldn't work!");
235    } catch (Exception e) {// NOOP
236    }
237
238    // try the put again, this time constraints are not used, so it works
239    table.put(put);
240    // and we make sure that constraints were not run...
241    assertFalse(CheckWasRunConstraint.wasRun);
242    table.close();
243  }
244
245  @After
246  public void cleanup() throws Exception {
247    // cleanup
248    CheckWasRunConstraint.wasRun = false;
249    util.getAdmin().disableTable(tableName);
250    util.getAdmin().deleteTable(tableName);
251  }
252
253  @AfterClass
254  public static void tearDownAfterClass() throws Exception {
255    util.shutdownMiniCluster();
256  }
257
258  /**
259   * Constraint to check that it was actually run (or not)
260   */
261  public static class CheckWasRunConstraint extends BaseConstraint {
262    public static boolean wasRun = false;
263
264    @Override
265    public void check(Put p) {
266      wasRun = true;
267    }
268  }
269
270}