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
060  public ExpectedException thrown = ExpectedException.none();
061
062  private static final Configuration CONF = HBaseConfiguration.create();
063  static {
064    CONF.setStrings(CoprocessorHost.MASTER_COPROCESSOR_CONF_KEY, SystemCoprocessor.class.getName());
065    CONF.setStrings(CoprocessorHost.REGIONSERVER_COPROCESSOR_CONF_KEY,
066      SystemCoprocessor.class.getName());
067    CONF.setStrings(CoprocessorHost.REGION_COPROCESSOR_CONF_KEY, SystemCoprocessor.class.getName());
068  }
069  private static final TableName TABLENAME = TableName.valueOf("TestCoprocessorConfiguration");
070  private static final HRegionInfo REGIONINFO = new HRegionInfo(TABLENAME);
071  private static final HTableDescriptor TABLEDESC = new HTableDescriptor(TABLENAME);
072  static {
073    try {
074      TABLEDESC.addCoprocessor(TableCoprocessor.class.getName());
075    } catch (IOException e) {
076      throw new RuntimeException(e);
077    }
078  }
079
080  // use atomic types in case coprocessor loading is ever multithreaded, also
081  // so we can mutate them even though they are declared final here
082  private static final AtomicBoolean systemCoprocessorLoaded = new AtomicBoolean();
083  private static final AtomicBoolean tableCoprocessorLoaded = new AtomicBoolean();
084
085  public static class SystemCoprocessor
086    implements MasterCoprocessor, RegionCoprocessor, RegionServerCoprocessor {
087    @Override
088    public void start(CoprocessorEnvironment env) throws IOException {
089      systemCoprocessorLoaded.set(true);
090    }
091
092    @Override
093    public void stop(CoprocessorEnvironment env) throws IOException {
094    }
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
108  @Test
109  public void testRegionCoprocessorHostDefaults() throws Exception {
110    Configuration conf = new Configuration(CONF);
111    HRegion region = mock(HRegion.class);
112    when(region.getRegionInfo()).thenReturn(REGIONINFO);
113    when(region.getTableDescriptor()).thenReturn(TABLEDESC);
114    RegionServerServices rsServices = mock(RegionServerServices.class);
115    systemCoprocessorLoaded.set(false);
116    tableCoprocessorLoaded.set(false);
117    new RegionCoprocessorHost(region, rsServices, conf);
118    assertEquals("System coprocessors loading default was not honored",
119      CoprocessorHost.DEFAULT_COPROCESSORS_ENABLED, systemCoprocessorLoaded.get());
120    assertEquals("Table coprocessors loading default was not honored",
121      CoprocessorHost.DEFAULT_COPROCESSORS_ENABLED
122        && CoprocessorHost.DEFAULT_USER_COPROCESSORS_ENABLED,
123      tableCoprocessorLoaded.get());
124  }
125
126  @Test
127  public void testRegionServerCoprocessorHostDefaults() throws Exception {
128    Configuration conf = new Configuration(CONF);
129    RegionServerServices rsServices = mock(RegionServerServices.class);
130    systemCoprocessorLoaded.set(false);
131    new RegionServerCoprocessorHost(rsServices, conf);
132    assertEquals("System coprocessors loading default was not honored",
133      CoprocessorHost.DEFAULT_COPROCESSORS_ENABLED, systemCoprocessorLoaded.get());
134  }
135
136  @Test
137  public void testMasterCoprocessorHostDefaults() throws Exception {
138    Configuration conf = new Configuration(CONF);
139    MasterServices masterServices = mock(MasterServices.class);
140    systemCoprocessorLoaded.set(false);
141    new MasterCoprocessorHost(masterServices, conf);
142    assertEquals("System coprocessors loading default was not honored",
143      CoprocessorHost.DEFAULT_COPROCESSORS_ENABLED, systemCoprocessorLoaded.get());
144  }
145
146  @Test
147  public void testRegionCoprocessorHostAllDisabled() throws Exception {
148    Configuration conf = new Configuration(CONF);
149    conf.setBoolean(CoprocessorHost.COPROCESSORS_ENABLED_CONF_KEY, false);
150    HRegion region = mock(HRegion.class);
151    when(region.getRegionInfo()).thenReturn(REGIONINFO);
152    when(region.getTableDescriptor()).thenReturn(TABLEDESC);
153    RegionServerServices rsServices = mock(RegionServerServices.class);
154    systemCoprocessorLoaded.set(false);
155    tableCoprocessorLoaded.set(false);
156    new RegionCoprocessorHost(region, rsServices, conf);
157    assertFalse("System coprocessors should not have been loaded", systemCoprocessorLoaded.get());
158    assertFalse("Table coprocessors should not have been loaded", 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", systemCoprocessorLoaded.get());
174    assertFalse("Table coprocessors should not have been loaded", tableCoprocessorLoaded.get());
175  }
176
177  /**
178   * Rough test that Coprocessor Environment is Read-Only. Just check a random CP and see that it
179   * returns a read-only config.
180   */
181  @Test
182  public void testReadOnlyConfiguration() throws Exception {
183    Configuration conf = new Configuration(CONF);
184    HRegion region = mock(HRegion.class);
185    when(region.getRegionInfo()).thenReturn(REGIONINFO);
186    when(region.getTableDescriptor()).thenReturn(TABLEDESC);
187    RegionServerServices rsServices = mock(RegionServerServices.class);
188    RegionCoprocessorHost rcp = new RegionCoprocessorHost(region, rsServices, conf);
189    boolean found = false;
190    for (String cpStr : rcp.getCoprocessors()) {
191      CoprocessorEnvironment cpenv = rcp.findCoprocessorEnvironment(cpStr);
192      if (cpenv != null) {
193        found = true;
194      }
195      Configuration c = cpenv.getConfiguration();
196      thrown.expect(UnsupportedOperationException.class);
197      c.set("one.two.three", "four.five.six");
198    }
199    assertTrue("Should be at least one CP found", found);
200  }
201}