blob: e60605744d86d51c7292081a83220be6ed4fc24e [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.accumulo.core.conf;
import java.io.IOException;
import java.util.Map.Entry;
import java.util.Objects;
import org.apache.accumulo.core.spi.crypto.CryptoService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.common.base.Preconditions;
/**
* A utility class for validating {@link AccumuloConfiguration} instances.
*/
public class ConfigSanityCheck {
private static final Logger log = LoggerFactory.getLogger(ConfigSanityCheck.class);
private static final String PREFIX = "BAD CONFIG ";
/**
* Validates the given configuration entries. A valid configuration contains only valid properties
* (i.e., defined or otherwise valid) that are not prefixes and whose values are formatted
* correctly for their property types. A valid configuration also contains a value for property
* {@link Property#INSTANCE_ZK_TIMEOUT} within a valid range.
*
* @param entries
* iterable through configuration keys and values
* @throws SanityCheckException
* if a fatal configuration error is found
*/
public static void validate(Iterable<Entry<String,String>> entries) {
String instanceZkTimeoutValue = null;
for (Entry<String,String> entry : entries) {
String key = entry.getKey();
String value = entry.getValue();
Property prop = Property.getPropertyByKey(entry.getKey());
if (prop == null && Property.isValidPropertyKey(key))
continue; // unknown valid property (i.e. has proper prefix)
else if (prop == null)
log.warn(PREFIX + "unrecognized property key (" + key + ")");
else if (prop.getType() == PropertyType.PREFIX)
fatal(PREFIX + "incomplete property key (" + key + ")");
else if (!prop.getType().isValidFormat(value))
fatal(PREFIX + "improperly formatted value for key (" + key + ", type=" + prop.getType()
+ ") : " + value);
if (key.equals(Property.INSTANCE_ZK_TIMEOUT.getKey())) {
instanceZkTimeoutValue = value;
}
// If the block size or block size index is configured to be too large, we throw an exception
// to avoid potentially corrupting RFiles later
if (key.equals(Property.TABLE_FILE_COMPRESSED_BLOCK_SIZE_INDEX.getKey())
|| key.equals(Property.TABLE_FILE_COMPRESSED_BLOCK_SIZE.getKey())) {
long bsize = ConfigurationTypeHelper.getFixedMemoryAsBytes(value);
Preconditions.checkArgument(bsize > 0 && bsize < Integer.MAX_VALUE, key
+ " must be greater than 0 and less than " + Integer.MAX_VALUE + " but was: " + bsize);
}
if (key.equals(Property.INSTANCE_CRYPTO_SERVICE.getKey())) {
String cryptoStrategy = Objects.requireNonNull(value);
verifyValidClassName(key, cryptoStrategy, CryptoService.class);
}
}
if (instanceZkTimeoutValue != null) {
checkTimeDuration(Property.INSTANCE_ZK_TIMEOUT, instanceZkTimeoutValue,
new CheckTimeDurationBetween(1000, 300000));
}
}
private interface CheckTimeDuration {
boolean check(long propVal);
String getDescription(Property prop);
}
private static class CheckTimeDurationBetween implements CheckTimeDuration {
long min, max;
CheckTimeDurationBetween(long x, long y) {
min = Math.min(x, y);
max = Math.max(x, y);
}
@Override
public boolean check(long propVal) {
return propVal >= min && propVal <= max;
}
@Override
public String getDescription(Property prop) {
return "ensure " + min + " <= " + prop + " <= " + max;
}
}
private static void checkTimeDuration(Property prop, String value, CheckTimeDuration chk) {
verifyPropertyTypes(PropertyType.TIMEDURATION, prop);
if (!chk.check(ConfigurationTypeHelper.getTimeInMillis(value)))
fatal(PREFIX + chk.getDescription(prop));
}
private static void verifyPropertyTypes(PropertyType type, Property... properties) {
for (Property prop : properties)
if (prop.getType() != type)
fatal("Unexpected property type (" + prop.getType() + " != " + type + ")");
}
/**
* The exception thrown when {@link ConfigSanityCheck#validate(Iterable)} fails.
*/
public static class SanityCheckException extends RuntimeException {
private static final long serialVersionUID = 1L;
/**
* Creates a new exception with the given message.
*/
public SanityCheckException(String msg) {
super(msg);
}
}
private static void fatal(String msg) {
// ACCUMULO-3651 Level changed from fatal to error and FATAL added to message for slf4j
// compatibility
log.error("FATAL: {}", msg);
throw new SanityCheckException(msg);
}
/**
* Verifies a configured option is a legal class and has a required base class.
*
* @param confOption
* The Property key name
* @param className
* The Property value, the string representation of a class to be loaded
* @param requiredBaseClass
* The base class required for the className
*/
private static void verifyValidClassName(String confOption, String className,
Class<?> requiredBaseClass) {
try {
ConfigurationTypeHelper.getClassInstance(null, className, requiredBaseClass);
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException
| IOException e) {
fatal(confOption + " has an invalid class name: " + className);
} catch (ClassCastException e) {
fatal(confOption + " must implement " + requiredBaseClass
+ ", but the configured class does not: " + className);
}
}
}