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.coprocessor;
019
020import static org.junit.Assert.assertEquals;
021import static org.junit.Assert.assertFalse;
022import static org.junit.Assert.assertTrue;
023import static org.mockito.Mockito.mock;
024import static org.mockito.Mockito.when;
025
026import java.io.IOException;
027import java.util.concurrent.atomic.AtomicBoolean;
028import org.apache.hadoop.conf.Configuration;
029import org.apache.hadoop.hbase.CoprocessorEnvironment;
030import org.apache.hadoop.hbase.HBaseClassTestRule;
031import org.apache.hadoop.hbase.HBaseConfiguration;
032import org.apache.hadoop.hbase.HRegionInfo;
033import org.apache.hadoop.hbase.HTableDescriptor;
034import org.apache.hadoop.hbase.TableName;
035import org.apache.hadoop.hbase.master.MasterCoprocessorHost;
036import org.apache.hadoop.hbase.master.MasterServices;
037import org.apache.hadoop.hbase.regionserver.HRegion;
038import org.apache.hadoop.hbase.regionserver.RegionCoprocessorHost;
039import org.apache.hadoop.hbase.regionserver.RegionServerCoprocessorHost;
040import org.apache.hadoop.hbase.regionserver.RegionServerServices;
041import org.apache.hadoop.hbase.testclassification.CoprocessorTests;
042import org.apache.hadoop.hbase.testclassification.SmallTests;
043import org.junit.ClassRule;
044import org.junit.Rule;
045import org.junit.Test;
046import org.junit.experimental.categories.Category;
047import org.junit.rules.ExpectedException;
048
049/**
050 * Tests for global coprocessor loading configuration
051 */
052@Category({CoprocessorTests.class, SmallTests.class})
053public class TestCoprocessorConfiguration {
054
055  @ClassRule
056  public static final HBaseClassTestRule CLASS_RULE =
057      HBaseClassTestRule.forClass(TestCoprocessorConfiguration.class);
058
059  @Rule public ExpectedException thrown = ExpectedException.none();
060
061  private static final Configuration CONF = HBaseConfiguration.create();
062  static {
063    CONF.setStrings(CoprocessorHost.MASTER_COPROCESSOR_CONF_KEY,
064      SystemCoprocessor.class.getName());
065    CONF.setStrings(CoprocessorHost.REGIONSERVER_COPROCESSOR_CONF_KEY,
066      SystemCoprocessor.class.getName());
067    CONF.setStrings(CoprocessorHost.REGION_COPROCESSOR_CONF_KEY,
068      SystemCoprocessor.class.getName());
069  }
070  private static final TableName TABLENAME = TableName.valueOf("TestCoprocessorConfiguration");
071  private static final HRegionInfo REGIONINFO = new HRegionInfo(TABLENAME);
072  private static final HTableDescriptor TABLEDESC = new HTableDescriptor(TABLENAME);
073  static {
074    try {
075      TABLEDESC.addCoprocessor(TableCoprocessor.class.getName());
076    } catch (IOException e) {
077      throw new RuntimeException(e);
078    }
079  }
080
081  // use atomic types in case coprocessor loading is ever multithreaded, also
082  // so we can mutate them even though they are declared final here
083  private static final AtomicBoolean systemCoprocessorLoaded = new AtomicBoolean();
084  private static final AtomicBoolean tableCoprocessorLoaded = new AtomicBoolean();
085
086  public static class SystemCoprocessor implements MasterCoprocessor, RegionCoprocessor,
087      RegionServerCoprocessor {
088    @Override
089    public void start(CoprocessorEnvironment env) throws IOException {
090      systemCoprocessorLoaded.set(true);
091    }
092
093    @Override
094    public void stop(CoprocessorEnvironment env) throws IOException { }
095  }
096
097  public static class TableCoprocessor implements RegionCoprocessor {
098    @Override
099    public void start(CoprocessorEnvironment env) throws IOException {
100      tableCoprocessorLoaded.set(true);
101    }
102
103    @Override
104    public void stop(CoprocessorEnvironment env) throws IOException { }
105  }
106
107  @Test
108  public void testRegionCoprocessorHostDefaults() throws Exception {
109    Configuration conf = new Configuration(CONF);
110    HRegion region = mock(HRegion.class);
111    when(region.getRegionInfo()).thenReturn(REGIONINFO);
112    when(region.getTableDescriptor()).thenReturn(TABLEDESC);
113    RegionServerServices rsServices = mock(RegionServerServices.class);
114    systemCoprocessorLoaded.set(false);
115    tableCoprocessorLoaded.set(false);
116    new RegionCoprocessorHost(region, rsServices, conf);
117    assertEquals("System coprocessors loading default was not honored",
118        CoprocessorHost.DEFAULT_COPROCESSORS_ENABLED, systemCoprocessorLoaded.get());
119    assertEquals("Table coprocessors loading default was not honored",
120        CoprocessorHost.DEFAULT_COPROCESSORS_ENABLED &&
121        CoprocessorHost.DEFAULT_USER_COPROCESSORS_ENABLED, tableCoprocessorLoaded.get());
122  }
123
124  @Test
125  public void testRegionServerCoprocessorHostDefaults() throws Exception {
126    Configuration conf = new Configuration(CONF);
127    RegionServerServices rsServices = mock(RegionServerServices.class);
128    systemCoprocessorLoaded.set(false);
129    new RegionServerCoprocessorHost(rsServices, conf);
130    assertEquals("System coprocessors loading default was not honored",
131        CoprocessorHost.DEFAULT_COPROCESSORS_ENABLED, systemCoprocessorLoaded.get());
132  }
133
134  @Test
135  public void testMasterCoprocessorHostDefaults() throws Exception {
136    Configuration conf = new Configuration(CONF);
137    MasterServices masterServices = mock(MasterServices.class);
138    systemCoprocessorLoaded.set(false);
139    new MasterCoprocessorHost(masterServices, conf);
140    assertEquals("System coprocessors loading default was not honored",
141        CoprocessorHost.DEFAULT_COPROCESSORS_ENABLED, systemCoprocessorLoaded.get());
142  }
143
144  @Test
145  public void testRegionCoprocessorHostAllDisabled() throws Exception {
146    Configuration conf = new Configuration(CONF);
147    conf.setBoolean(CoprocessorHost.COPROCESSORS_ENABLED_CONF_KEY, false);
148    HRegion region = mock(HRegion.class);
149    when(region.getRegionInfo()).thenReturn(REGIONINFO);
150    when(region.getTableDescriptor()).thenReturn(TABLEDESC);
151    RegionServerServices rsServices = mock(RegionServerServices.class);
152    systemCoprocessorLoaded.set(false);
153    tableCoprocessorLoaded.set(false);
154    new RegionCoprocessorHost(region, rsServices, conf);
155    assertFalse("System coprocessors should not have been loaded",
156      systemCoprocessorLoaded.get());
157    assertFalse("Table coprocessors should not have been loaded",
158      tableCoprocessorLoaded.get());
159  }
160
161  @Test
162  public void testRegionCoprocessorHostTableLoadingDisabled() throws Exception {
163    Configuration conf = new Configuration(CONF);
164    conf.setBoolean(CoprocessorHost.COPROCESSORS_ENABLED_CONF_KEY, true); // if defaults change
165    conf.setBoolean(CoprocessorHost.USER_COPROCESSORS_ENABLED_CONF_KEY, false);
166    HRegion region = mock(HRegion.class);
167    when(region.getRegionInfo()).thenReturn(REGIONINFO);
168    when(region.getTableDescriptor()).thenReturn(TABLEDESC);
169    RegionServerServices rsServices = mock(RegionServerServices.class);
170    systemCoprocessorLoaded.set(false);
171    tableCoprocessorLoaded.set(false);
172    new RegionCoprocessorHost(region, rsServices, conf);
173    assertTrue("System coprocessors should have been loaded",
174      systemCoprocessorLoaded.get());
175    assertFalse("Table coprocessors should not have been loaded",
176      tableCoprocessorLoaded.get());
177  }
178
179  /**
180   * Rough test that Coprocessor Environment is Read-Only.
181   * Just check a random CP and see that it returns a read-only config.
182   */
183  @Test
184  public void testReadOnlyConfiguration() throws Exception {
185    Configuration conf = new Configuration(CONF);
186    HRegion region = mock(HRegion.class);
187    when(region.getRegionInfo()).thenReturn(REGIONINFO);
188    when(region.getTableDescriptor()).thenReturn(TABLEDESC);
189    RegionServerServices rsServices = mock(RegionServerServices.class);
190    RegionCoprocessorHost rcp = new RegionCoprocessorHost(region, rsServices, conf);
191    boolean found = false;
192    for (String cpStr: rcp.getCoprocessors()) {
193      CoprocessorEnvironment cpenv = rcp.findCoprocessorEnvironment(cpStr);
194      if (cpenv != null) {
195        found = true;
196      }
197      Configuration c = cpenv.getConfiguration();
198      thrown.expect(UnsupportedOperationException.class);
199      c.set("one.two.three", "four.five.six");
200    }
201    assertTrue("Should be at least one CP found", found);
202  }
203}