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.HBaseTestingUtil;
026import org.apache.hadoop.hbase.TableName;
027import org.apache.hadoop.hbase.client.ColumnFamilyDescriptorBuilder;
028import org.apache.hadoop.hbase.client.Put;
029import org.apache.hadoop.hbase.client.Table;
030import org.apache.hadoop.hbase.client.TableDescriptorBuilder;
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 HBaseTestingUtil 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 HBaseTestingUtil();
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  @Test
073  public void testConstraintPasses() throws Exception {
074    // create the table
075    // it would be nice if this was also a method on the util
076    TableDescriptorBuilder builder = TableDescriptorBuilder.newBuilder(tableName);
077
078    for (byte[] family : new byte[][] { dummy, test }) {
079      builder.setColumnFamily(ColumnFamilyDescriptorBuilder.of(family));
080    }
081    // add a constraint
082    Constraints.add(builder, CheckWasRunConstraint.class);
083
084    util.getAdmin().createTable(builder.build());
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  @Test
103  public void testConstraintFails() throws Exception {
104    // create the table
105    // it would be nice if this was also a method on the util
106    TableDescriptorBuilder builder = TableDescriptorBuilder.newBuilder(tableName);
107    for (byte[] family : new byte[][] { dummy, test }) {
108      builder.setColumnFamily(ColumnFamilyDescriptorBuilder.of(family));
109    }
110
111    // add a constraint that is sure to fail
112    Constraints.add(builder, AllFailConstraint.class);
113
114    util.getAdmin().createTable(builder.build());
115    Table table = util.getConnection().getTable(tableName);
116
117    // test that we do fail on violation
118    Put put = new Put(row1);
119    byte[] qualifier = new byte[0];
120    put.addColumn(dummy, qualifier, Bytes.toBytes("fail"));
121    LOG.warn("Doing put in table");
122    try {
123      table.put(put);
124      fail("This put should not have suceeded - AllFailConstraint was not run!");
125    } catch (ConstraintException e) {
126      // expected
127    }
128    table.close();
129  }
130
131  /**
132   * Check that if we just disable one constraint, then
133   */
134  @Test
135  public void testDisableConstraint() throws Exception {
136    // create the table
137    TableDescriptorBuilder builder = TableDescriptorBuilder.newBuilder(tableName);
138    // add a family to the table
139    for (byte[] family : new byte[][] { dummy, test }) {
140      builder.setColumnFamily(ColumnFamilyDescriptorBuilder.of(family));
141    }
142    // add a constraint to make sure it others get run
143    Constraints.add(builder, CheckWasRunConstraint.class);
144
145    // Add Constraint to check
146    Constraints.add(builder, AllFailConstraint.class);
147
148    // and then disable the failing constraint
149    Constraints.disableConstraint(builder, AllFailConstraint.class);
150
151    util.getAdmin().createTable(builder.build());
152    Table table = util.getConnection().getTable(tableName);
153    try {
154      // test that we don't fail because its disabled
155      Put put = new Put(row1);
156      byte[] qualifier = new byte[0];
157      put.addColumn(dummy, qualifier, Bytes.toBytes("pass"));
158      table.put(put);
159    } finally {
160      table.close();
161    }
162    assertTrue(CheckWasRunConstraint.wasRun);
163  }
164
165  /**
166   * Test that if we disable all constraints, then nothing gets run
167   */
168  @Test
169  public void testDisableConstraints() throws Exception {
170    // create the table
171    TableDescriptorBuilder builder = TableDescriptorBuilder.newBuilder(tableName);
172
173    // add a family to the table
174    for (byte[] family : new byte[][] { dummy, test }) {
175      builder.setColumnFamily(ColumnFamilyDescriptorBuilder.of(family));
176    }
177    // add a constraint to check to see if is run
178    Constraints.add(builder, CheckWasRunConstraint.class);
179
180    // then disable all the constraints
181    Constraints.disable(builder);
182
183    util.getAdmin().createTable(builder.build());
184    Table table = util.getConnection().getTable(tableName);
185    try {
186      // test that we do fail on violation
187      Put put = new Put(row1);
188      byte[] qualifier = new byte[0];
189      put.addColumn(dummy, qualifier, Bytes.toBytes("pass"));
190      LOG.warn("Doing put in table");
191      table.put(put);
192    } finally {
193      table.close();
194    }
195    assertFalse(CheckWasRunConstraint.wasRun);
196  }
197
198  /**
199   * Check to make sure a constraint is unloaded when it fails
200   */
201  @Test
202  public void testIsUnloaded() throws Exception {
203    // create the table
204    TableDescriptorBuilder builder = TableDescriptorBuilder.newBuilder(tableName);
205
206    // add a family to the table
207    for (byte[] family : new byte[][] { dummy, test }) {
208      builder.setColumnFamily(ColumnFamilyDescriptorBuilder.of(family));
209    }
210    // make sure that constraints are unloaded
211    Constraints.add(builder, RuntimeFailConstraint.class);
212    // add a constraint to check to see if is run
213    Constraints.add(builder, CheckWasRunConstraint.class);
214    CheckWasRunConstraint.wasRun = false;
215
216    util.getAdmin().createTable(builder.build());
217    Table table = util.getConnection().getTable(tableName);
218
219    // test that we do fail on violation
220    Put put = new Put(row1);
221    byte[] qualifier = new byte[0];
222    put.addColumn(dummy, qualifier, Bytes.toBytes("pass"));
223
224    try {
225      table.put(put);
226      fail("RuntimeFailConstraint wasn't triggered - this put shouldn't work!");
227    } catch (Exception e) {// NOOP
228    }
229
230    // try the put again, this time constraints are not used, so it works
231    table.put(put);
232    // and we make sure that constraints were not run...
233    assertFalse(CheckWasRunConstraint.wasRun);
234    table.close();
235  }
236
237  @After
238  public void cleanup() throws Exception {
239    // cleanup
240    CheckWasRunConstraint.wasRun = false;
241    util.getAdmin().disableTable(tableName);
242    util.getAdmin().deleteTable(tableName);
243  }
244
245  @AfterClass
246  public static void tearDownAfterClass() throws Exception {
247    util.shutdownMiniCluster();
248  }
249
250  /**
251   * Constraint to check that it was actually run (or not)
252   */
253  public static class CheckWasRunConstraint extends BaseConstraint {
254    public static boolean wasRun = false;
255
256    @Override
257    public void check(Put p) {
258      wasRun = true;
259    }
260  }
261
262}