/* * The Alluxio Open Foundation licenses this work under the Apache License, version 2.0 * (the "License"). You may not use this work except in compliance with the License, which is * available at www.apache.org/licenses/LICENSE-2.0 * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, * either express or implied, as more fully set forth in the License. * * See the NOTICE file distributed with this work for information regarding copyright ownership. */ package alluxio; import alluxio.master.LocalAlluxioCluster; import alluxio.metrics.MetricsSystem; import alluxio.security.LoginUserTestUtils; import alluxio.security.authentication.AuthenticatedClientUser; import org.junit.rules.TestRule; import org.junit.runner.Description; import org.junit.runners.model.Statement; import java.lang.annotation.Annotation; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.HashMap; import java.util.Map; import java.util.Map.Entry; import javax.annotation.concurrent.NotThreadSafe; /** * A JUnit Rule resource for automatically managing a local alluxio cluster for testing. To use it, * create an instance of the class under a {@literal @}Rule annotation, with the required * configuration parameters, and any necessary explicit {@link Configuration} settings. The Alluxio * cluster will be set up from scratch at the end of every method (or at the start of every suite if * {@literal @}ClassRule is used), and destroyed at the end. Below is an example of declaring and * using it. * * <pre> * public class SomethingTest { * {@literal @}Rule * public LocalAlluxioClusterResource localAlluxioClusterResource = * new LocalAlluxioClusterResource(WORKER_CAPACITY, BLOCK_SIZE); * * {@literal @}Test * public void testSomething() { * localAlluxioClusterResource.get().getClient().create("/abced"); * ... * } * * {@literal @}Test * {@literal @}LocalAlluxioClusterResource.Config( * confParams = {CONF_KEY_1, CONF_VALUE_1, CONF_KEY_2, CONF_VALUE_2, ...}) * public void testSomethingWithDifferentConf() { * localAlluxioClusterResource.get().getClient().create("/efghi"); * ... * } * * {@literal @}Test * {@literal @}LocalAlluxioClusterResource.Config(startCluster = false) * public void testSomethingWithClusterStartedManually() { * localAlluxioClusterResource.start(); * localAlluxioClusterResource.get().getClient().create("/efghi"); * ... * } * } * </pre> */ @NotThreadSafe public final class LocalAlluxioClusterResource implements TestRule { /** Number of Alluxio workers in the cluster. */ private final int mNumWorkers; /** * If true (default), we start the cluster before running a test method. Otherwise, the method * must start the cluster explicitly. */ private final boolean mStartCluster; /** Configuration values for the cluster. */ private final Map<PropertyKey, String> mConfiguration = new HashMap<>(); /** The Alluxio cluster being managed. */ private LocalAlluxioCluster mLocalAlluxioCluster = null; /** * Creates a new instance. * * @param startCluster whether or not to start the cluster before the test method starts * @param numWorkers the number of Alluxio workers to launch * @param configuration configuration for configuring the cluster */ private LocalAlluxioClusterResource(boolean startCluster, int numWorkers, Map<PropertyKey, String> configuration) { mStartCluster = startCluster; mNumWorkers = numWorkers; mConfiguration.putAll(configuration); MetricsSystem.resetAllCounters(); } /** * @return the {@link LocalAlluxioCluster} being managed */ public LocalAlluxioCluster get() { return mLocalAlluxioCluster; } /** * Adds a property to the cluster resource. * * @param key property key * @param value property value * @return the cluster resource */ public LocalAlluxioClusterResource setProperty(PropertyKey key, Object value) { mConfiguration.put(key, value.toString()); return this; } /** * Explicitly starts the {@link LocalAlluxioCluster}. */ public void start() throws Exception { AuthenticatedClientUser.remove(); LoginUserTestUtils.resetLoginUser(); // Create a new cluster. mLocalAlluxioCluster = new LocalAlluxioCluster(mNumWorkers); // Init configuration for integration test mLocalAlluxioCluster.initConfiguration(); // Overwrite the test configuration with test specific parameters for (Entry<PropertyKey, String> entry : mConfiguration.entrySet()) { Configuration.set(entry.getKey(), entry.getValue()); } Configuration.validate(); // Start the cluster mLocalAlluxioCluster.start(); } @Override public Statement apply(final Statement statement, Description description) { try { boolean startCluster = mStartCluster; Annotation configAnnotation = description.getAnnotation(Config.class); if (configAnnotation != null) { Config config = (Config) configAnnotation; // Override the configuration parameters with any configuration params for (int i = 0; i < config.confParams().length; i += 2) { mConfiguration.put(PropertyKey.fromString(config.confParams()[i]), config.confParams()[i + 1]); } // Override startCluster startCluster = config.startCluster(); } if (startCluster) { start(); } } catch (Exception e) { throw new RuntimeException(e); } return new Statement() { @Override public void evaluate() throws Throwable { try { statement.evaluate(); } finally { mLocalAlluxioCluster.stop(); } } }; } /** * Builder for a {@link LocalAlluxioClusterResource}. */ public static class Builder { private boolean mStartCluster; private int mNumWorkers; private Map<PropertyKey, String> mConfiguration; /** * Constructs the builder with default values. */ public Builder() { mStartCluster = true; mNumWorkers = 1; mConfiguration = new HashMap<>(); } /** * @param startCluster whether to start the cluster at the start of the test */ public Builder setStartCluster(boolean startCluster) { mStartCluster = startCluster; return this; } /** * @param numWorkers the number of workers to run in the cluster */ public Builder setNumWorkers(int numWorkers) { mNumWorkers = numWorkers; return this; } /** * @param key the property key to set for the cluster * @param value the value to set it to */ public Builder setProperty(PropertyKey key, Object value) { mConfiguration.put(key, value.toString()); return this; } /** * @return a {@link LocalAlluxioClusterResource} for the current builder values */ public LocalAlluxioClusterResource build() { return new LocalAlluxioClusterResource(mStartCluster, mNumWorkers, mConfiguration); } } /** * An annotation for test methods that can be used to override any of the defaults set at * construction time. */ @Retention(RetentionPolicy.RUNTIME) public @interface Config { String[] confParams() default {}; boolean startCluster() default true; } }