org.apache.accumulo.harness.MiniClusterHarness.java Source code

Java tutorial

Introduction

Here is the source code for org.apache.accumulo.harness.MiniClusterHarness.java

Source

/*
 * 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.harness;

import static com.google.common.base.Preconditions.checkArgument;
import static java.nio.charset.StandardCharsets.UTF_8;
import static java.util.Objects.requireNonNull;
import static org.junit.Assert.assertTrue;

import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.OutputStream;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.atomic.AtomicLong;

import org.apache.accumulo.cluster.ClusterUser;
import org.apache.accumulo.core.client.security.tokens.AuthenticationToken;
import org.apache.accumulo.core.client.security.tokens.KerberosToken;
import org.apache.accumulo.core.client.security.tokens.PasswordToken;
import org.apache.accumulo.core.conf.Property;
import org.apache.accumulo.minicluster.impl.MiniAccumuloClusterImpl;
import org.apache.accumulo.minicluster.impl.MiniAccumuloConfigImpl;
import org.apache.accumulo.server.security.handler.KerberosAuthenticator;
import org.apache.accumulo.server.security.handler.KerberosAuthorizor;
import org.apache.accumulo.server.security.handler.KerberosPermissionHandler;
import org.apache.accumulo.test.functional.NativeMapIT;
import org.apache.accumulo.test.util.CertUtils;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.CommonConfigurationKeysPublic;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * Harness that sets up a MiniAccumuloCluster in a manner expected for Accumulo integration tests.
 */
public class MiniClusterHarness {
    private static final Logger log = LoggerFactory.getLogger(MiniClusterHarness.class);

    private static final AtomicLong COUNTER = new AtomicLong(0);

    public static final String USE_SSL_FOR_IT_OPTION = "org.apache.accumulo.test.functional.useSslForIT",
            USE_CRED_PROVIDER_FOR_IT_OPTION = "org.apache.accumulo.test.functional.useCredProviderForIT",
            USE_KERBEROS_FOR_IT_OPTION = "org.apache.accumulo.test.functional.useKrbForIT",
            TRUE = Boolean.toString(true);

    // TODO These are defined in MiniKdc >= 2.6.0. Can be removed when minimum Hadoop dependency is increased to that.
    public static final String JAVA_SECURITY_KRB5_CONF = "java.security.krb5.conf",
            SUN_SECURITY_KRB5_DEBUG = "sun.security.krb5.debug";

    /**
     * Create a MiniAccumuloCluster using the given Token as the credentials for the root user.
     */
    public MiniAccumuloClusterImpl create(AuthenticationToken token) throws Exception {
        return create(MiniClusterHarness.class.getName(), Long.toString(COUNTER.incrementAndGet()), token);
    }

    public MiniAccumuloClusterImpl create(AuthenticationToken token, TestingKdc kdc) throws Exception {
        return create(MiniClusterHarness.class.getName(), Long.toString(COUNTER.incrementAndGet()), token, kdc);
    }

    public MiniAccumuloClusterImpl create(AccumuloITBase testBase, AuthenticationToken token) throws Exception {
        return create(testBase.getClass().getName(), testBase.testName.getMethodName(), token);
    }

    public MiniAccumuloClusterImpl create(AccumuloITBase testBase, AuthenticationToken token, TestingKdc kdc)
            throws Exception {
        return create(testBase, token, kdc, MiniClusterConfigurationCallback.NO_CALLBACK);
    }

    public MiniAccumuloClusterImpl create(AccumuloITBase testBase, AuthenticationToken token, TestingKdc kdc,
            MiniClusterConfigurationCallback configCallback) throws Exception {
        return create(testBase.getClass().getName(), testBase.testName.getMethodName(), token, configCallback, kdc);
    }

    public MiniAccumuloClusterImpl create(AccumuloClusterHarness testBase, AuthenticationToken token,
            TestingKdc kdc) throws Exception {
        return create(testBase.getClass().getName(), testBase.testName.getMethodName(), token, testBase, kdc);
    }

    public MiniAccumuloClusterImpl create(AccumuloClusterHarness testBase, AuthenticationToken token,
            MiniClusterConfigurationCallback callback) throws Exception {
        return create(testBase.getClass().getName(), testBase.testName.getMethodName(), token, callback);
    }

    public MiniAccumuloClusterImpl create(String testClassName, String testMethodName, AuthenticationToken token)
            throws Exception {
        return create(testClassName, testMethodName, token, MiniClusterConfigurationCallback.NO_CALLBACK);
    }

    public MiniAccumuloClusterImpl create(String testClassName, String testMethodName, AuthenticationToken token,
            TestingKdc kdc) throws Exception {
        return create(testClassName, testMethodName, token, MiniClusterConfigurationCallback.NO_CALLBACK, kdc);
    }

    public MiniAccumuloClusterImpl create(String testClassName, String testMethodName, AuthenticationToken token,
            MiniClusterConfigurationCallback configCallback) throws Exception {
        return create(testClassName, testMethodName, token, configCallback, null);
    }

    public MiniAccumuloClusterImpl create(String testClassName, String testMethodName, AuthenticationToken token,
            MiniClusterConfigurationCallback configCallback, TestingKdc kdc) throws Exception {
        requireNonNull(token);
        checkArgument(token instanceof PasswordToken || token instanceof KerberosToken,
                "A PasswordToken or KerberosToken is required");

        String rootPasswd;
        if (token instanceof PasswordToken) {
            rootPasswd = new String(((PasswordToken) token).getPassword(), UTF_8);
        } else {
            rootPasswd = UUID.randomUUID().toString();
        }

        File baseDir = AccumuloClusterHarness.createTestDir(testClassName + "_" + testMethodName);
        MiniAccumuloConfigImpl cfg = new MiniAccumuloConfigImpl(baseDir, rootPasswd);

        // Enable native maps by default
        cfg.setNativeLibPaths(NativeMapIT.nativeMapLocation().getAbsolutePath());
        cfg.setProperty(Property.TSERV_NATIVEMAP_ENABLED, Boolean.TRUE.toString());

        Configuration coreSite = new Configuration(false);

        // Setup SSL and credential providers if the properties request such
        configureForEnvironment(cfg, getClass(), AccumuloClusterHarness.getSslDir(baseDir), coreSite, kdc);

        // Invoke the callback for tests to configure MAC before it starts
        configCallback.configureMiniCluster(cfg, coreSite);

        MiniAccumuloClusterImpl miniCluster = new MiniAccumuloClusterImpl(cfg);

        // Write out any configuration items to a file so HDFS will pick them up automatically (from the classpath)
        if (coreSite.size() > 0) {
            File csFile = new File(miniCluster.getConfig().getConfDir(), "core-site.xml");
            if (csFile.exists())
                throw new RuntimeException(csFile + " already exist");

            OutputStream out = new BufferedOutputStream(
                    new FileOutputStream(new File(miniCluster.getConfig().getConfDir(), "core-site.xml")));
            coreSite.writeXml(out);
            out.close();
        }

        return miniCluster;
    }

    protected void configureForEnvironment(MiniAccumuloConfigImpl cfg, Class<?> testClass, File folder,
            Configuration coreSite, TestingKdc kdc) {
        if (TRUE.equals(System.getProperty(USE_SSL_FOR_IT_OPTION))) {
            configureForSsl(cfg, folder);
        }
        if (TRUE.equals(System.getProperty(USE_CRED_PROVIDER_FOR_IT_OPTION))) {
            cfg.setUseCredentialProvider(true);
        }

        if (TRUE.equals(System.getProperty(USE_KERBEROS_FOR_IT_OPTION))) {
            if (TRUE.equals(System.getProperty(USE_SSL_FOR_IT_OPTION))) {
                throw new RuntimeException("Cannot use both SSL and Kerberos");
            }

            try {
                configureForKerberos(cfg, folder, coreSite, kdc);
            } catch (Exception e) {
                throw new RuntimeException("Failed to initialize KDC", e);
            }
        }
    }

    protected void configureForSsl(MiniAccumuloConfigImpl cfg, File folder) {
        Map<String, String> siteConfig = cfg.getSiteConfig();
        if (TRUE.equals(siteConfig.get(Property.INSTANCE_RPC_SSL_ENABLED.getKey()))) {
            // already enabled; don't mess with it
            return;
        }

        File sslDir = new File(folder, "ssl");
        assertTrue(sslDir.mkdirs() || sslDir.isDirectory());
        File rootKeystoreFile = new File(sslDir, "root-" + cfg.getInstanceName() + ".jks");
        File localKeystoreFile = new File(sslDir, "local-" + cfg.getInstanceName() + ".jks");
        File publicTruststoreFile = new File(sslDir, "public-" + cfg.getInstanceName() + ".jks");
        final String rootKeystorePassword = "root_keystore_password", truststorePassword = "truststore_password";
        try {
            new CertUtils(Property.RPC_SSL_KEYSTORE_TYPE.getDefaultValue(),
                    "o=Apache Accumulo,cn=MiniAccumuloCluster", "RSA", 2048, "sha1WithRSAEncryption").createAll(
                            rootKeystoreFile, localKeystoreFile, publicTruststoreFile, cfg.getInstanceName(),
                            rootKeystorePassword, cfg.getRootPassword(), truststorePassword);
        } catch (Exception e) {
            throw new RuntimeException("error creating MAC keystore", e);
        }

        siteConfig.put(Property.INSTANCE_RPC_SSL_ENABLED.getKey(), "true");
        siteConfig.put(Property.RPC_SSL_KEYSTORE_PATH.getKey(), localKeystoreFile.getAbsolutePath());
        siteConfig.put(Property.RPC_SSL_KEYSTORE_PASSWORD.getKey(), cfg.getRootPassword());
        siteConfig.put(Property.RPC_SSL_TRUSTSTORE_PATH.getKey(), publicTruststoreFile.getAbsolutePath());
        siteConfig.put(Property.RPC_SSL_TRUSTSTORE_PASSWORD.getKey(), truststorePassword);
        cfg.setSiteConfig(siteConfig);
    }

    protected void configureForKerberos(MiniAccumuloConfigImpl cfg, File folder, Configuration coreSite,
            TestingKdc kdc) throws Exception {
        Map<String, String> siteConfig = cfg.getSiteConfig();
        if (TRUE.equals(siteConfig.get(Property.INSTANCE_RPC_SSL_ENABLED.getKey()))) {
            throw new RuntimeException("Cannot use both SSL and SASL/Kerberos");
        }

        if (TRUE.equals(siteConfig.get(Property.INSTANCE_RPC_SASL_ENABLED.getKey()))) {
            // already enabled
            return;
        }

        if (null == kdc) {
            throw new IllegalStateException("MiniClusterKdc was null");
        }

        log.info("Enabling Kerberos/SASL for minicluster");

        // Turn on SASL and set the keytab/principal information
        cfg.setProperty(Property.INSTANCE_RPC_SASL_ENABLED, "true");
        ClusterUser serverUser = kdc.getAccumuloServerUser();
        cfg.setProperty(Property.GENERAL_KERBEROS_KEYTAB, serverUser.getKeytab().getAbsolutePath());
        cfg.setProperty(Property.GENERAL_KERBEROS_PRINCIPAL, serverUser.getPrincipal());
        cfg.setProperty(Property.INSTANCE_SECURITY_AUTHENTICATOR, KerberosAuthenticator.class.getName());
        cfg.setProperty(Property.INSTANCE_SECURITY_AUTHORIZOR, KerberosAuthorizor.class.getName());
        cfg.setProperty(Property.INSTANCE_SECURITY_PERMISSION_HANDLER, KerberosPermissionHandler.class.getName());
        // Piggy-back on the "system user" credential, but use it as a normal KerberosToken, not the SystemToken.
        cfg.setProperty(Property.TRACE_USER, serverUser.getPrincipal());
        cfg.setProperty(Property.TRACE_TOKEN_TYPE, KerberosToken.CLASS_NAME);

        // Pass down some KRB5 debug properties
        Map<String, String> systemProperties = cfg.getSystemProperties();
        systemProperties.put(JAVA_SECURITY_KRB5_CONF, System.getProperty(JAVA_SECURITY_KRB5_CONF, ""));
        systemProperties.put(SUN_SECURITY_KRB5_DEBUG, System.getProperty(SUN_SECURITY_KRB5_DEBUG, "false"));
        cfg.setSystemProperties(systemProperties);

        // Make sure UserGroupInformation will do the correct login
        coreSite.set(CommonConfigurationKeysPublic.HADOOP_SECURITY_AUTHENTICATION, "kerberos");

        cfg.setRootUserName(kdc.getRootUser().getPrincipal());
    }
}