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