org.apache.accumulo.test.functional.KerberosRenewalIT.java Source code

Java tutorial

Introduction

Here is the source code for org.apache.accumulo.test.functional.KerberosRenewalIT.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.test.functional;

import static org.junit.Assert.assertEquals;

import java.util.Map;
import java.util.Map.Entry;

import org.apache.accumulo.cluster.ClusterUser;
import org.apache.accumulo.core.client.AccumuloException;
import org.apache.accumulo.core.client.AccumuloSecurityException;
import org.apache.accumulo.core.client.BatchWriter;
import org.apache.accumulo.core.client.BatchWriterConfig;
import org.apache.accumulo.core.client.Connector;
import org.apache.accumulo.core.client.Scanner;
import org.apache.accumulo.core.client.TableExistsException;
import org.apache.accumulo.core.client.TableNotFoundException;
import org.apache.accumulo.core.client.admin.CompactionConfig;
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.core.data.Key;
import org.apache.accumulo.core.data.Mutation;
import org.apache.accumulo.core.data.PartialKey;
import org.apache.accumulo.core.data.Value;
import org.apache.accumulo.core.security.Authorizations;
import org.apache.accumulo.harness.AccumuloITBase;
import org.apache.accumulo.harness.MiniClusterConfigurationCallback;
import org.apache.accumulo.harness.MiniClusterHarness;
import org.apache.accumulo.harness.TestingKdc;
import org.apache.accumulo.minicluster.impl.MiniAccumuloClusterImpl;
import org.apache.accumulo.minicluster.impl.MiniAccumuloConfigImpl;
import org.apache.accumulo.test.categories.MiniClusterOnlyTests;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.CommonConfigurationKeysPublic;
import org.apache.hadoop.minikdc.MiniKdc;
import org.apache.hadoop.security.UserGroupInformation;
import org.junit.After;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.experimental.categories.Category;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.google.common.collect.Iterables;

/**
 * MAC test which uses {@link MiniKdc} to simulate ta secure environment. Can be used as a sanity check for Kerberos/SASL testing.
 */
@Category(MiniClusterOnlyTests.class)
public class KerberosRenewalIT extends AccumuloITBase {
    private static final Logger log = LoggerFactory.getLogger(KerberosRenewalIT.class);

    private static TestingKdc kdc;
    private static String krbEnabledForITs = null;
    private static ClusterUser rootUser;

    private static final long TICKET_LIFETIME = 6 * 60 * 1000; // Anything less seems to fail when generating the ticket
    private static final long TICKET_TEST_LIFETIME = 8 * 60 * 1000; // Run a test for 8 mins
    private static final long TEST_DURATION = 9 * 60 * 1000; // The test should finish within 9 mins

    @BeforeClass
    public static void startKdc() throws Exception {
        // 30s renewal time window
        kdc = new TestingKdc(TestingKdc.computeKdcDir(), TestingKdc.computeKeytabDir(), TICKET_LIFETIME);
        kdc.start();
        krbEnabledForITs = System.getProperty(MiniClusterHarness.USE_KERBEROS_FOR_IT_OPTION);
        if (null == krbEnabledForITs || !Boolean.parseBoolean(krbEnabledForITs)) {
            System.setProperty(MiniClusterHarness.USE_KERBEROS_FOR_IT_OPTION, "true");
        }
        rootUser = kdc.getRootUser();
    }

    @AfterClass
    public static void stopKdc() throws Exception {
        if (null != kdc) {
            kdc.stop();
        }
        if (null != krbEnabledForITs) {
            System.setProperty(MiniClusterHarness.USE_KERBEROS_FOR_IT_OPTION, krbEnabledForITs);
        }
    }

    @Override
    public int defaultTimeoutSeconds() {
        return (int) TEST_DURATION / 1000;
    }

    private MiniAccumuloClusterImpl mac;

    @Before
    public void startMac() throws Exception {
        MiniClusterHarness harness = new MiniClusterHarness();
        mac = harness.create(this, new PasswordToken("unused"), kdc, new MiniClusterConfigurationCallback() {

            @Override
            public void configureMiniCluster(MiniAccumuloConfigImpl cfg, Configuration coreSite) {
                Map<String, String> site = cfg.getSiteConfig();
                site.put(Property.INSTANCE_ZK_TIMEOUT.getKey(), "15s");
                // Reduce the period just to make sure we trigger renewal fast
                site.put(Property.GENERAL_KERBEROS_RENEWAL_PERIOD.getKey(), "5s");
                cfg.setSiteConfig(site);
            }

        });

        mac.getConfig().setNumTservers(1);
        mac.start();
        // Enabled kerberos auth
        Configuration conf = new Configuration(false);
        conf.set(CommonConfigurationKeysPublic.HADOOP_SECURITY_AUTHENTICATION, "kerberos");
        UserGroupInformation.setConfiguration(conf);
    }

    @After
    public void stopMac() throws Exception {
        if (null != mac) {
            mac.stop();
        }
    }

    // Intentially setting the Test annotation timeout. We do not want to scale the timeout.
    @Test(timeout = TEST_DURATION)
    public void testReadAndWriteThroughTicketLifetime() throws Exception {
        // Attempt to use Accumulo for a duration of time that exceeds the Kerberos ticket lifetime.
        // This is a functional test to verify that Accumulo services renew their ticket.
        // If the test doesn't finish on its own, this signifies that Accumulo services failed
        // and the test should fail. If Accumulo services renew their ticket, the test case
        // should exit gracefully on its own.

        // Login as the "root" user
        UserGroupInformation.loginUserFromKeytab(rootUser.getPrincipal(), rootUser.getKeytab().getAbsolutePath());
        log.info("Logged in as {}", rootUser.getPrincipal());

        Connector conn = mac.getConnector(rootUser.getPrincipal(), new KerberosToken());
        log.info("Created connector as {}", rootUser.getPrincipal());
        assertEquals(rootUser.getPrincipal(), conn.whoami());

        long duration = 0;
        long last = System.currentTimeMillis();
        // Make sure we have a couple renewals happen
        while (duration < TICKET_TEST_LIFETIME) {
            // Create a table, write a record, compact, read the record, drop the table.
            createReadWriteDrop(conn);
            // Wait a bit after
            Thread.sleep(5000);

            // Update the duration
            long now = System.currentTimeMillis();
            duration += now - last;
            last = now;
        }
    }

    /**
     * Creates a table, adds a record to it, and then compacts the table. A simple way to make sure that the system user exists (since the master does an RPC to
     * the tserver which will create the system user if it doesn't already exist).
     */
    private void createReadWriteDrop(Connector conn)
            throws TableNotFoundException, AccumuloSecurityException, AccumuloException, TableExistsException {
        final String table = testName.getMethodName() + "_table";
        conn.tableOperations().create(table);
        BatchWriter bw = conn.createBatchWriter(table, new BatchWriterConfig());
        Mutation m = new Mutation("a");
        m.put("b", "c", "d");
        bw.addMutation(m);
        bw.close();
        conn.tableOperations().compact(table, new CompactionConfig().setFlush(true).setWait(true));
        Scanner s = conn.createScanner(table, Authorizations.EMPTY);
        Entry<Key, Value> entry = Iterables.getOnlyElement(s);
        assertEquals("Did not find the expected key", 0,
                new Key("a", "b", "c").compareTo(entry.getKey(), PartialKey.ROW_COLFAM_COLQUAL));
        assertEquals("d", entry.getValue().toString());
        conn.tableOperations().delete(table);
    }
}