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