All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.elasticsearch.test.ESBackcompatTestCase Maven / Gradle / Ivy

/*
 * Licensed to Elasticsearch under one or more contributor
 * license agreements. See the NOTICE file distributed with
 * this work for additional information regarding copyright
 * ownership. Elasticsearch 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.elasticsearch.test;

import com.carrotsearch.randomizedtesting.annotations.TestGroup;
import org.elasticsearch.Version;
import org.elasticsearch.cluster.ClusterState;
import org.elasticsearch.cluster.routing.IndexRoutingTable;
import org.elasticsearch.cluster.routing.IndexShardRoutingTable;
import org.elasticsearch.cluster.routing.ShardRouting;
import org.elasticsearch.common.Strings;
import org.elasticsearch.common.io.PathUtils;
import org.elasticsearch.common.network.NetworkModule;
import org.elasticsearch.common.regex.Regex;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.plugins.Plugin;
import org.elasticsearch.test.discovery.TestZenDiscovery;
import org.elasticsearch.test.junit.annotations.TestLogging;
import org.elasticsearch.test.junit.listeners.LoggingListener;

import java.io.IOException;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Collection;
import java.util.Collections;
import java.util.Map;

import static org.hamcrest.Matchers.is;

/**
 * Abstract base class for backwards compatibility tests. Subclasses of this class
 * can run tests against a mixed version cluster. A subset of the nodes in the cluster
 * are started in dedicated process running off a full fledged elasticsearch release.
 * Nodes can be "upgraded" from the "backwards" node to an "new" node where "new" nodes
 * version corresponds to current version.
 * The purpose of this test class is to run tests in scenarios where clusters are in an
 * intermediate state during a rolling upgrade as well as upgrade situations. The clients
 * accessed via #client() are random clients to the nodes in the cluster which might
 * execute requests on the "new" as well as the "old" nodes.
 * 

* Note: this base class is still experimental and might have bugs or leave external processes running behind. *

* Backwards compatibility tests are disabled by default via {@link Backwards} annotation. * The following system variables control the test execution: *
    *
  • * {@value #TESTS_BACKWARDS_COMPATIBILITY} enables / disables * tests annotated with {@link Backwards} (defaults to * false) *
  • *
  • * {@value #TESTS_BACKWARDS_COMPATIBILITY_VERSION} * sets the version to run the external nodes from formatted as X.Y.Z. * The tests class will try to locate a release folder elasticsearch-X.Y.Z * within path passed via {@value #TESTS_BACKWARDS_COMPATIBILITY_PATH} * depending on this system variable. *
  • *
  • * {@value #TESTS_BACKWARDS_COMPATIBILITY_PATH} the path to the * elasticsearch releases to run backwards compatibility tests against. *
  • *
* */ // the transportClientRatio is tricky here since we don't fully control the cluster nodes @ESBackcompatTestCase.Backwards @ESIntegTestCase.ClusterScope(minNumDataNodes = 0, maxNumDataNodes = 2, scope = ESIntegTestCase.Scope.SUITE, numClientNodes = 0, transportClientRatio = 0.0) public abstract class ESBackcompatTestCase extends ESIntegTestCase { /** * Key used to set the path for the elasticsearch executable used to run backwards compatibility tests from * via the commandline -D{@value #TESTS_BACKWARDS_COMPATIBILITY} */ public static final String TESTS_BACKWARDS_COMPATIBILITY = "tests.bwc"; public static final String TESTS_BACKWARDS_COMPATIBILITY_VERSION = "tests.bwc.version"; /** * Key used to set the path for the elasticsearch executable used to run backwards compatibility tests from * via the commandline -D{@value #TESTS_BACKWARDS_COMPATIBILITY_PATH} */ public static final String TESTS_BACKWARDS_COMPATIBILITY_PATH = "tests.bwc.path"; /** * Property that allows to adapt the tests behaviour to older features/bugs based on the input version */ private static final String TESTS_COMPATIBILITY = "tests.compatibility"; private static final Version GLOBAL_COMPATIBILITY_VERSION = Version.fromString(compatibilityVersionProperty()); private static Path backwardsCompatibilityPath() { String path = System.getProperty(TESTS_BACKWARDS_COMPATIBILITY_PATH); if (path == null || path.isEmpty()) { throw new IllegalArgumentException("Must specify backwards test path with property " + TESTS_BACKWARDS_COMPATIBILITY_PATH); } String version = System.getProperty(TESTS_BACKWARDS_COMPATIBILITY_VERSION); if (version == null || version.isEmpty()) { throw new IllegalArgumentException("Must specify backwards test version with property " + TESTS_BACKWARDS_COMPATIBILITY_VERSION); } if (Version.fromString(version).before(Version.CURRENT.minimumCompatibilityVersion())) { throw new IllegalArgumentException("Backcompat elasticsearch version must be same major version as current. " + "backcompat: " + version + ", current: " + Version.CURRENT.toString()); } Path file = PathUtils.get(path, "elasticsearch-" + version); if (!Files.exists(file)) { throw new IllegalArgumentException("Backwards tests location is missing: " + file.toAbsolutePath()); } if (!Files.isDirectory(file)) { throw new IllegalArgumentException("Backwards tests location is not a directory: " + file.toAbsolutePath()); } return file; } /** * Retruns the tests compatibility version. */ public Version compatibilityVersion() { return compatibilityVersion(getClass()); } private Version compatibilityVersion(Class clazz) { if (clazz == Object.class || clazz == ESIntegTestCase.class) { return globalCompatibilityVersion(); } CompatibilityVersion annotation = clazz.getAnnotation(CompatibilityVersion.class); if (annotation != null) { return Version.min(Version.fromId(annotation.version()), compatibilityVersion(clazz.getSuperclass())); } return compatibilityVersion(clazz.getSuperclass()); } /** * Returns a global compatibility version that is set via the * {@value #TESTS_COMPATIBILITY} or {@value #TESTS_BACKWARDS_COMPATIBILITY_VERSION} system property. * If both are unset the current version is used as the global compatibility version. This * compatibility version is used for static randomization. For per-suite compatibility version see * {@link #compatibilityVersion()} */ public static Version globalCompatibilityVersion() { return GLOBAL_COMPATIBILITY_VERSION; } private static String compatibilityVersionProperty() { final String version = System.getProperty(TESTS_COMPATIBILITY); if (Strings.hasLength(version)) { return version; } return System.getProperty(TESTS_BACKWARDS_COMPATIBILITY_VERSION); } public CompositeTestCluster backwardsCluster() { return (CompositeTestCluster) cluster(); } @Override protected TestCluster buildTestCluster(Scope scope, long seed) throws IOException { TestCluster cluster = super.buildTestCluster(scope, seed); ExternalNode externalNode = new ExternalNode(backwardsCompatibilityPath(), randomLong(), new NodeConfigurationSource() { @Override public Settings nodeSettings(int nodeOrdinal) { return externalNodeSettings(nodeOrdinal); } @Override public Collection> nodePlugins() { return Collections.emptyList(); } @Override public Settings transportClientSettings() { return transportClientSettings(); } }); return new CompositeTestCluster((InternalTestCluster) cluster, between(minExternalNodes(), maxExternalNodes()), externalNode); } private Settings addLoggerSettings(Settings externalNodesSettings) { TestLogging logging = getClass().getAnnotation(TestLogging.class); Map loggingLevels = LoggingListener.getLoggersAndLevelsFromAnnotation(logging); Settings.Builder finalSettings = Settings.builder(); if (loggingLevels != null) { for (Map.Entry level : loggingLevels.entrySet()) { finalSettings.put("logger." + level.getKey(), level.getValue()); } } finalSettings.put(externalNodesSettings); return finalSettings.build(); } protected int minExternalNodes() { return 1; } protected int maxExternalNodes() { return 2; } @Override protected int maximumNumberOfReplicas() { return 1; } protected Settings requiredSettings() { return ExternalNode.REQUIRED_SETTINGS; } @Override protected Settings nodeSettings(int nodeOrdinal) { return commonNodeSettings(nodeOrdinal); } public void assertAllShardsOnNodes(String index, String pattern) { ClusterState clusterState = client().admin().cluster().prepareState().execute().actionGet().getState(); for (IndexRoutingTable indexRoutingTable : clusterState.routingTable()) { for (IndexShardRoutingTable indexShardRoutingTable : indexRoutingTable) { for (ShardRouting shardRouting : indexShardRoutingTable) { if (shardRouting.currentNodeId() != null && index.equals(shardRouting.getIndexName())) { String name = clusterState.nodes().get(shardRouting.currentNodeId()).getName(); assertThat("Allocated on new node: " + name, Regex.simpleMatch(pattern, name), is(true)); } } } } } protected Settings commonNodeSettings(int nodeOrdinal) { Settings.Builder builder = Settings.builder().put(requiredSettings()); builder.put(NetworkModule.TRANSPORT_TYPE_KEY, randomBoolean() ? "netty3" : "netty4"); // run same transport / disco as external builder.put(TestZenDiscovery.USE_MOCK_PINGS.getKey(), false); return builder.build(); } protected Settings externalNodeSettings(int nodeOrdinal) { return addLoggerSettings(commonNodeSettings(nodeOrdinal)); } /** * Annotation for backwards compat tests */ @Inherited @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) @TestGroup(enabled = false, sysProperty = ESBackcompatTestCase.TESTS_BACKWARDS_COMPATIBILITY) public @interface Backwards { } /** * If a test is annotated with {@link CompatibilityVersion} * all randomized settings will only contain settings or mappings which are compatible with the specified version ID. */ @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.TYPE}) public @interface CompatibilityVersion { int version(); } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy