diff --git a/server/src/main/java/org/elasticsearch/cluster/coordination/JoinTaskExecutor.java b/server/src/main/java/org/elasticsearch/cluster/coordination/JoinTaskExecutor.java index 0a70ec0cc7dd7..03d58a9760ca7 100644 --- a/server/src/main/java/org/elasticsearch/cluster/coordination/JoinTaskExecutor.java +++ b/server/src/main/java/org/elasticsearch/cluster/coordination/JoinTaskExecutor.java @@ -272,22 +272,22 @@ public static void ensureIndexCompatibility(final Version nodeVersion, Metadata // we ensure that all indices in the cluster we join are compatible with us no matter if they are // closed or not we can't read mappings of these indices so we need to reject the join... for (IndexMetadata idxMetadata : metadata) { - if (idxMetadata.getCreationVersion().after(nodeVersion)) { + if (idxMetadata.getCompatibilityVersion().after(nodeVersion)) { throw new IllegalStateException( "index " + idxMetadata.getIndex() + " version not supported: " - + idxMetadata.getCreationVersion() + + idxMetadata.getCompatibilityVersion() + " the node version is: " + nodeVersion ); } - if (idxMetadata.getCreationVersion().before(supportedIndexVersion)) { + if (idxMetadata.getCompatibilityVersion().before(supportedIndexVersion)) { throw new IllegalStateException( "index " + idxMetadata.getIndex() + " version not supported: " - + idxMetadata.getCreationVersion() + + idxMetadata.getCompatibilityVersion() + " minimum compatible index version is: " + supportedIndexVersion ); diff --git a/server/src/main/java/org/elasticsearch/cluster/metadata/IndexMetadata.java b/server/src/main/java/org/elasticsearch/cluster/metadata/IndexMetadata.java index efef1d121bc25..31eaff551f18e 100644 --- a/server/src/main/java/org/elasticsearch/cluster/metadata/IndexMetadata.java +++ b/server/src/main/java/org/elasticsearch/cluster/metadata/IndexMetadata.java @@ -336,6 +336,49 @@ public static APIBlock readFrom(StreamInput input) throws IOException { @Deprecated public static final String SETTING_VERSION_UPGRADED_STRING = "index.version.upgraded_string"; + public static final String SETTING_VERSION_COMPATIBILITY = "index.version.compatibility"; + + /** + * See {@link #getCompatibilityVersion()} + */ + public static final Setting SETTING_INDEX_VERSION_COMPATIBILITY = Setting.versionSetting( + SETTING_VERSION_COMPATIBILITY, + SETTING_INDEX_VERSION_CREATED, // fall back to index.version.created + new Setting.Validator<>() { + + @Override + public void validate(final Version compatibilityVersion) { + + } + + @Override + public void validate(final Version compatibilityVersion, final Map, Object> settings) { + Version createdVersion = (Version) settings.get(SETTING_INDEX_VERSION_CREATED); + if (compatibilityVersion.before(createdVersion)) { + throw new IllegalArgumentException( + SETTING_VERSION_COMPATIBILITY + + " [" + + compatibilityVersion + + "] must be >= " + + SETTING_VERSION_CREATED + + " [" + + createdVersion + + "]" + ); + } + } + + @Override + public Iterator> settings() { + final List> settings = List.of(SETTING_INDEX_VERSION_CREATED); + return settings.iterator(); + } + + }, + Property.IndexScope, + Property.PrivateIndex + ); + /** * The user provided name for an index. This is the plain string provided by the user when the index was created. * It might still contain date math expressions etc. (added in 5.0) @@ -484,6 +527,7 @@ public static APIBlock readFrom(StreamInput input) throws IOException { private final DiscoveryNodeFilters initialRecoveryFilters; private final Version indexCreatedVersion; + private final Version indexCompatibilityVersion; private final ActiveShardCount waitForActiveShards; private final ImmutableOpenMap rolloverInfos; @@ -592,6 +636,7 @@ private IndexMetadata( this.autoExpandReplicas = autoExpandReplicas; this.isSearchableSnapshot = isSearchableSnapshot; this.isPartialSearchableSnapshot = isPartialSearchableSnapshot; + this.indexCompatibilityVersion = SETTING_INDEX_VERSION_COMPATIBILITY.get(settings); assert numberOfShards * routingFactor == routingNumShards : routingNumShards + " must be a multiple of " + numberOfShards; } @@ -689,11 +734,23 @@ public long primaryTerm(int shardId) { /** * Return the {@link Version} on which this index has been created. This * information is typically useful for backward compatibility. + * To check index compatibility (e.g. N-1 checks), use {@link #getCompatibilityVersion()} instead. */ public Version getCreationVersion() { return indexCreatedVersion; } + /** + * Return the {@link Version} that this index provides compatibility for. + * This is typically compared to the {@link Version#minimumIndexCompatibilityVersion()} to figure out whether the index can be handled + * by the cluster. + * By default, this is equal to the {@link #getCreationVersion()}, but can also be a newer version if the index has been imported as + * a legacy index from an older snapshot, and its metadata has been converted to be handled by newer version nodes. + */ + public Version getCompatibilityVersion() { + return indexCompatibilityVersion; + } + public long getCreationDate() { return creationDate; } @@ -1923,11 +1980,12 @@ public static IndexMetadata legacyFromXContent(XContentParser parser) throws IOE } else if (token == XContentParser.Token.START_OBJECT) { if ("settings".equals(currentFieldName)) { Settings settings = Settings.fromXContent(parser); - if (SETTING_INDEX_VERSION_CREATED.get(settings).onOrAfter(Version.CURRENT.minimumIndexCompatibilityVersion())) { + if (SETTING_INDEX_VERSION_COMPATIBILITY.get(settings) + .onOrAfter(Version.CURRENT.minimumIndexCompatibilityVersion())) { throw new IllegalStateException( - "this method should only be used to parse older index metadata versions " + "this method should only be used to parse older incompatible index metadata versions " + "but got " - + SETTING_INDEX_VERSION_CREATED.get(settings) + + SETTING_INDEX_VERSION_COMPATIBILITY.get(settings) ); } builder.settings(settings); @@ -2008,7 +2066,7 @@ public static IndexMetadata legacyFromXContent(XContentParser parser) throws IOE } IndexMetadata indexMetadata = builder.build(); - assert indexMetadata.getCreationVersion().before(Version.CURRENT.minimumIndexCompatibilityVersion()); + assert indexMetadata.getCompatibilityVersion().before(Version.CURRENT.minimumIndexCompatibilityVersion()); return indexMetadata; } diff --git a/server/src/main/java/org/elasticsearch/cluster/metadata/IndexMetadataVerifier.java b/server/src/main/java/org/elasticsearch/cluster/metadata/IndexMetadataVerifier.java index f20bb98c1dcc7..100e8782f9b48 100644 --- a/server/src/main/java/org/elasticsearch/cluster/metadata/IndexMetadataVerifier.java +++ b/server/src/main/java/org/elasticsearch/cluster/metadata/IndexMetadataVerifier.java @@ -99,13 +99,13 @@ public IndexMetadata verifyIndexMetadata(IndexMetadata indexMetadata, Version mi * previous major version. */ private void checkSupportedVersion(IndexMetadata indexMetadata, Version minimumIndexCompatibilityVersion) { - boolean isSupportedVersion = indexMetadata.getCreationVersion().onOrAfter(minimumIndexCompatibilityVersion); + boolean isSupportedVersion = indexMetadata.getCompatibilityVersion().onOrAfter(minimumIndexCompatibilityVersion); if (isSupportedVersion == false) { throw new IllegalStateException( "The index " + indexMetadata.getIndex() - + " was created with version [" - + indexMetadata.getCreationVersion() + + " has current compatibility version [" + + indexMetadata.getCompatibilityVersion() + "] but the minimum compatible version is [" + minimumIndexCompatibilityVersion + "]. It should be re-indexed in Elasticsearch " diff --git a/server/src/main/java/org/elasticsearch/cluster/metadata/Metadata.java b/server/src/main/java/org/elasticsearch/cluster/metadata/Metadata.java index 55bc8eb863e86..41f13470fbec2 100644 --- a/server/src/main/java/org/elasticsearch/cluster/metadata/Metadata.java +++ b/server/src/main/java/org/elasticsearch/cluster/metadata/Metadata.java @@ -1663,7 +1663,7 @@ public Metadata build(boolean builtIndicesLookupEagerly) { } } indexMetadata.getAliases().keysIt().forEachRemaining(indicesAliases::add); - oldestIndexVersionId = Math.min(oldestIndexVersionId, indexMetadata.getCreationVersion().id); + oldestIndexVersionId = Math.min(oldestIndexVersionId, indexMetadata.getCompatibilityVersion().id); } final DataStreamMetadata dataStreamMetadata = (DataStreamMetadata) this.customs.get(DataStreamMetadata.TYPE); diff --git a/server/src/main/java/org/elasticsearch/cluster/metadata/MetadataCreateIndexService.java b/server/src/main/java/org/elasticsearch/cluster/metadata/MetadataCreateIndexService.java index 0e054447957ee..3c639a5d15012 100644 --- a/server/src/main/java/org/elasticsearch/cluster/metadata/MetadataCreateIndexService.java +++ b/server/src/main/java/org/elasticsearch/cluster/metadata/MetadataCreateIndexService.java @@ -1463,6 +1463,7 @@ static void prepareResizeIndexSettings( } indexSettingsBuilder.put(IndexMetadata.SETTING_VERSION_CREATED, sourceMetadata.getCreationVersion()) + .put(IndexMetadata.SETTING_VERSION_COMPATIBILITY, sourceMetadata.getCompatibilityVersion()) .put(builder.build()) .put(IndexMetadata.SETTING_ROUTING_PARTITION_SIZE, sourceMetadata.getRoutingPartitionSize()) .put(IndexMetadata.INDEX_RESIZE_SOURCE_NAME.getKey(), resizeSourceIndex.getName()) diff --git a/server/src/main/java/org/elasticsearch/common/settings/IndexScopedSettings.java b/server/src/main/java/org/elasticsearch/common/settings/IndexScopedSettings.java index e0dca571247b5..5d8fb642da10a 100644 --- a/server/src/main/java/org/elasticsearch/common/settings/IndexScopedSettings.java +++ b/server/src/main/java/org/elasticsearch/common/settings/IndexScopedSettings.java @@ -53,6 +53,7 @@ public final class IndexScopedSettings extends AbstractScopedSettings { MergeSchedulerConfig.MAX_MERGE_COUNT_SETTING, MergeSchedulerConfig.MAX_THREAD_COUNT_SETTING, IndexMetadata.SETTING_INDEX_VERSION_CREATED, + IndexMetadata.SETTING_INDEX_VERSION_COMPATIBILITY, IndexMetadata.INDEX_ROUTING_EXCLUDE_GROUP_SETTING, IndexMetadata.INDEX_ROUTING_INCLUDE_GROUP_SETTING, IndexMetadata.INDEX_ROUTING_REQUIRE_GROUP_SETTING, diff --git a/server/src/main/java/org/elasticsearch/common/settings/Setting.java b/server/src/main/java/org/elasticsearch/common/settings/Setting.java index 65b5259192dfe..058af04695a01 100644 --- a/server/src/main/java/org/elasticsearch/common/settings/Setting.java +++ b/server/src/main/java/org/elasticsearch/common/settings/Setting.java @@ -1233,6 +1233,15 @@ public static Setting versionSetting(final String key, final Version de return new Setting<>(key, s -> Integer.toString(defaultValue.id), s -> Version.fromId(Integer.parseInt(s)), properties); } + public static Setting versionSetting( + final String key, + Setting fallbackSetting, + Validator validator, + Property... properties + ) { + return new Setting<>(key, fallbackSetting, s -> Version.fromId(Integer.parseInt(s)), properties); + } + public static Setting floatSetting(String key, float defaultValue, Property... properties) { return new Setting<>(key, (s) -> Float.toString(defaultValue), Float::parseFloat, properties); } diff --git a/server/src/main/java/org/elasticsearch/gateway/LocalAllocateDangledIndices.java b/server/src/main/java/org/elasticsearch/gateway/LocalAllocateDangledIndices.java index ba085a58b195d..e25bca3736f94 100644 --- a/server/src/main/java/org/elasticsearch/gateway/LocalAllocateDangledIndices.java +++ b/server/src/main/java/org/elasticsearch/gateway/LocalAllocateDangledIndices.java @@ -120,24 +120,24 @@ public ClusterState execute(ClusterState currentState) { boolean importNeeded = false; StringBuilder sb = new StringBuilder(); for (IndexMetadata indexMetadata : request.indices) { - if (indexMetadata.getCreationVersion().before(minIndexCompatibilityVersion)) { + if (indexMetadata.getCompatibilityVersion().before(minIndexCompatibilityVersion)) { logger.warn( - "ignoring dangled index [{}] on node [{}] since it's created version [{}] is not supported by at " - + "least one node in the cluster minVersion [{}]", + "ignoring dangled index [{}] on node [{}] since it's current compatibility version [{}] " + + "is not supported by at least one node in the cluster minVersion [{}]", indexMetadata.getIndex(), request.fromNode, - indexMetadata.getCreationVersion(), + indexMetadata.getCompatibilityVersion(), minIndexCompatibilityVersion ); continue; } - if (currentState.nodes().getMinNodeVersion().before(indexMetadata.getCreationVersion())) { + if (currentState.nodes().getMinNodeVersion().before(indexMetadata.getCompatibilityVersion())) { logger.warn( - "ignoring dangled index [{}] on node [{}]" - + " since its created version [{}] is later than the oldest versioned node in the cluster [{}]", + "ignoring dangled index [{}] on node [{}] since its current compatibility version [{}] " + + "is later than the oldest versioned node in the cluster [{}]", indexMetadata.getIndex(), request.fromNode, - indexMetadata.getCreationVersion(), + indexMetadata.getCompatibilityVersion(), currentState.getNodes().getMasterNode().getVersion() ); continue; diff --git a/server/src/main/java/org/elasticsearch/index/IndexSettings.java b/server/src/main/java/org/elasticsearch/index/IndexSettings.java index 022d2d090117e..b4c2a07164ba7 100644 --- a/server/src/main/java/org/elasticsearch/index/IndexSettings.java +++ b/server/src/main/java/org/elasticsearch/index/IndexSettings.java @@ -938,6 +938,16 @@ public synchronized boolean updateIndexMetadata(IndexMetadata indexMetadata) { if (version.equals(newIndexVersion) == false) { throw new IllegalArgumentException("version mismatch on settings update expected: " + version + " but was: " + newIndexVersion); } + Version newCompatibilityVersion = IndexMetadata.SETTING_INDEX_VERSION_COMPATIBILITY.get(newSettings); + Version compatibilityVersion = IndexMetadata.SETTING_INDEX_VERSION_COMPATIBILITY.get(settings); + if (compatibilityVersion.equals(newCompatibilityVersion) == false) { + throw new IllegalArgumentException( + "compatibility version mismatch on settings update expected: " + + compatibilityVersion + + " but was: " + + newCompatibilityVersion + ); + } final String newUUID = newSettings.get(IndexMetadata.SETTING_INDEX_UUID, IndexMetadata.INDEX_UUID_NA_VALUE); if (newUUID.equals(getUUID()) == false) { throw new IllegalArgumentException("uuid mismatch on settings update expected: " + getUUID() + " but was: " + newUUID); diff --git a/server/src/main/java/org/elasticsearch/snapshots/RestoreService.java b/server/src/main/java/org/elasticsearch/snapshots/RestoreService.java index 8c0fc498a1e67..b9e600e228de5 100644 --- a/server/src/main/java/org/elasticsearch/snapshots/RestoreService.java +++ b/server/src/main/java/org/elasticsearch/snapshots/RestoreService.java @@ -1285,9 +1285,7 @@ public ClusterState execute(ClusterState currentState) { final ImmutableOpenMap.Builder shardsBuilder = ImmutableOpenMap.builder(); - final Version minIndexCompatibilityVersion = skipVersionChecks(repositoryMetadata) - ? Version.fromString("1.0.0") - : currentState.getNodes().getMaxNodeVersion().minimumIndexCompatibilityVersion(); + final Version minIndexCompatibilityVersion = currentState.getNodes().getMaxNodeVersion().minimumIndexCompatibilityVersion(); final String localNodeId = clusterService.state().nodes().getLocalNodeId(); for (Map.Entry indexEntry : indicesToRestore.entrySet()) { final IndexId index = indexEntry.getValue(); @@ -1297,10 +1295,9 @@ public ClusterState execute(ClusterState currentState) { request.indexSettings(), request.ignoreIndexSettings() ); - if (snapshotIndexMetadata.getCreationVersion() - .before(currentState.getNodes().getMaxNodeVersion().minimumIndexCompatibilityVersion())) { + if (snapshotIndexMetadata.getCompatibilityVersion().before(minIndexCompatibilityVersion)) { // adapt index metadata so that it can be understood by current version - snapshotIndexMetadata = convertLegacyIndex(snapshotIndexMetadata); + snapshotIndexMetadata = convertLegacyIndex(snapshotIndexMetadata, currentState); } try { snapshotIndexMetadata = indexMetadataVerifier.verifyIndexMetadata(snapshotIndexMetadata, minIndexCompatibilityVersion); @@ -1590,7 +1587,10 @@ public void clusterStateProcessed(ClusterState oldState, ClusterState newState) } } - private IndexMetadata convertLegacyIndex(IndexMetadata snapshotIndexMetadata) { + private IndexMetadata convertLegacyIndex(IndexMetadata snapshotIndexMetadata, ClusterState clusterState) { + if (snapshotIndexMetadata.getCreationVersion().before(Version.fromString("5.0.0"))) { + throw new IllegalArgumentException("can't restore an index created before version 5.0.0"); + } MappingMetadata mappingMetadata = snapshotIndexMetadata.mapping(); Map loadedMappingSource = mappingMetadata.rawSourceAsMap(); @@ -1613,7 +1613,17 @@ private IndexMetadata convertLegacyIndex(IndexMetadata snapshotIndexMetadata) { Map newMapping = new LinkedHashMap<>(); newMapping.put(mappingMetadata.type(), newMappingSource); // TODO: _routing? Perhaps we don't need to obey any routing here as stuff is read-only anyway and get API will be disabled - return IndexMetadata.builder(snapshotIndexMetadata).putMapping(new MappingMetadata(mappingMetadata.type(), newMapping)).build(); + return IndexMetadata.builder(snapshotIndexMetadata) + .putMapping(new MappingMetadata(mappingMetadata.type(), newMapping)) + .settings( + Settings.builder() + .put(snapshotIndexMetadata.getSettings()) + .put( + IndexMetadata.SETTING_INDEX_VERSION_COMPATIBILITY.getKey(), + clusterState.getNodes().getSmallestNonClientNodeVersion() + ) + ) + .build(); } private static IndexMetadata.Builder restoreToCreateNewIndex(IndexMetadata snapshotIndexMetadata, String renamedIndexName) { diff --git a/server/src/test/java/org/elasticsearch/cluster/coordination/JoinTaskExecutorTests.java b/server/src/test/java/org/elasticsearch/cluster/coordination/JoinTaskExecutorTests.java index 6479a51395726..0fe9752c9979a 100644 --- a/server/src/test/java/org/elasticsearch/cluster/coordination/JoinTaskExecutorTests.java +++ b/server/src/test/java/org/elasticsearch/cluster/coordination/JoinTaskExecutorTests.java @@ -110,17 +110,13 @@ public void testSuccess() { Settings.builder().build(); Metadata.Builder metaBuilder = Metadata.builder(); IndexMetadata indexMetadata = IndexMetadata.builder("test") - .settings( - settings(VersionUtils.randomVersionBetween(random(), Version.CURRENT.minimumIndexCompatibilityVersion(), Version.CURRENT)) - ) + .settings(randomCompatibleVersionSettings()) .numberOfShards(1) .numberOfReplicas(1) .build(); metaBuilder.put(indexMetadata, false); indexMetadata = IndexMetadata.builder("test1") - .settings( - settings(VersionUtils.randomVersionBetween(random(), Version.CURRENT.minimumIndexCompatibilityVersion(), Version.CURRENT)) - ) + .settings(randomCompatibleVersionSettings()) .numberOfShards(1) .numberOfReplicas(1) .build(); @@ -129,6 +125,24 @@ public void testSuccess() { JoinTaskExecutor.ensureIndexCompatibility(Version.CURRENT, metadata); } + public static Settings.Builder randomCompatibleVersionSettings() { + Settings.Builder builder = Settings.builder(); + if (randomBoolean()) { + builder.put(IndexMetadata.SETTING_VERSION_CREATED, getRandomCompatibleVersion()); + if (randomBoolean()) { + builder.put(IndexMetadata.SETTING_VERSION_COMPATIBILITY, getRandomCompatibleVersion()); + } + } else { + builder.put(IndexMetadata.SETTING_VERSION_CREATED, randomFrom(Version.fromString("5.0.0"), Version.fromString("6.0.0"))); + builder.put(IndexMetadata.SETTING_VERSION_COMPATIBILITY, getRandomCompatibleVersion()); + } + return builder; + } + + private static Version getRandomCompatibleVersion() { + return VersionUtils.randomVersionBetween(random(), Version.CURRENT.minimumIndexCompatibilityVersion(), Version.CURRENT); + } + public void testUpdatesNodeWithNewRoles() throws Exception { // Node roles vary by version, and new roles are suppressed for BWC. This means we can receive a join from a node that's already // in the cluster but with a different set of roles: the node didn't change roles, but the cluster state came via an older master. diff --git a/server/src/test/java/org/elasticsearch/cluster/metadata/IndexMetadataTests.java b/server/src/test/java/org/elasticsearch/cluster/metadata/IndexMetadataTests.java index 4fb6402dd3ffa..487854516fe18 100644 --- a/server/src/test/java/org/elasticsearch/cluster/metadata/IndexMetadataTests.java +++ b/server/src/test/java/org/elasticsearch/cluster/metadata/IndexMetadataTests.java @@ -116,6 +116,7 @@ public void testIndexMetadataSerialization() throws IOException { assertEquals(metadata.getNumberOfReplicas(), fromXContentMeta.getNumberOfReplicas()); assertEquals(metadata.getNumberOfShards(), fromXContentMeta.getNumberOfShards()); assertEquals(metadata.getCreationVersion(), fromXContentMeta.getCreationVersion()); + assertEquals(metadata.getCompatibilityVersion(), fromXContentMeta.getCompatibilityVersion()); assertEquals(metadata.getRoutingNumShards(), fromXContentMeta.getRoutingNumShards()); assertEquals(metadata.getCreationDate(), fromXContentMeta.getCreationDate()); assertEquals(metadata.getRoutingFactor(), fromXContentMeta.getRoutingFactor()); @@ -137,6 +138,7 @@ public void testIndexMetadataSerialization() throws IOException { assertEquals(metadata.getNumberOfReplicas(), deserialized.getNumberOfReplicas()); assertEquals(metadata.getNumberOfShards(), deserialized.getNumberOfShards()); assertEquals(metadata.getCreationVersion(), deserialized.getCreationVersion()); + assertEquals(metadata.getCompatibilityVersion(), deserialized.getCompatibilityVersion()); assertEquals(metadata.getRoutingNumShards(), deserialized.getRoutingNumShards()); assertEquals(metadata.getCreationDate(), deserialized.getCreationDate()); assertEquals(metadata.getRoutingFactor(), deserialized.getRoutingFactor()); diff --git a/server/src/test/java/org/elasticsearch/cluster/metadata/IndexMetadataVerifierTests.java b/server/src/test/java/org/elasticsearch/cluster/metadata/IndexMetadataVerifierTests.java index 5f06a3c0565f3..ad55c53bed5f3 100644 --- a/server/src/test/java/org/elasticsearch/cluster/metadata/IndexMetadataVerifierTests.java +++ b/server/src/test/java/org/elasticsearch/cluster/metadata/IndexMetadataVerifierTests.java @@ -78,7 +78,7 @@ public void testIncompatibleVersion() { equalTo( "The index [foo/" + metadata.getIndexUUID() - + "] was created with version [" + + "] has current compatibility version [" + indexCreated + "] " + "but the minimum compatible version is [" diff --git a/server/src/test/java/org/elasticsearch/index/IndexSettingsTests.java b/server/src/test/java/org/elasticsearch/index/IndexSettingsTests.java index af8a0e7775049..6240615948416 100644 --- a/server/src/test/java/org/elasticsearch/index/IndexSettingsTests.java +++ b/server/src/test/java/org/elasticsearch/index/IndexSettingsTests.java @@ -154,6 +154,22 @@ public void testSettingsConsistency() { assertTrue(ex.getMessage(), ex.getMessage().startsWith("version mismatch on settings update expected: ")); } + try { + settings.updateIndexMetadata( + newIndexMeta( + "index", + Settings.builder() + .put(IndexMetadata.SETTING_VERSION_CREATED, version) + .put(IndexMetadata.SETTING_VERSION_COMPATIBILITY, Version.CURRENT) + .put("index.test.setting.int", 42) + .build() + ) + ); + fail("version has changed"); + } catch (IllegalArgumentException ex) { + assertTrue(ex.getMessage(), ex.getMessage().startsWith("compatibility version mismatch on settings update expected: ")); + } + // use version number that is unknown metadata = newIndexMeta("index", Settings.builder().put(IndexMetadata.SETTING_VERSION_CREATED, Version.fromId(999999)).build()); settings = new IndexSettings(metadata, Settings.EMPTY); diff --git a/x-pack/plugin/deprecation/src/main/java/org/elasticsearch/xpack/deprecation/IndexDeprecationChecks.java b/x-pack/plugin/deprecation/src/main/java/org/elasticsearch/xpack/deprecation/IndexDeprecationChecks.java index 1e24bddd4dd54..c1edab8f2c8bb 100644 --- a/x-pack/plugin/deprecation/src/main/java/org/elasticsearch/xpack/deprecation/IndexDeprecationChecks.java +++ b/x-pack/plugin/deprecation/src/main/java/org/elasticsearch/xpack/deprecation/IndexDeprecationChecks.java @@ -21,13 +21,14 @@ public class IndexDeprecationChecks { static DeprecationIssue oldIndicesCheck(IndexMetadata indexMetadata) { - Version createdWith = indexMetadata.getCreationVersion(); - if (createdWith.before(Version.V_7_0_0)) { + // TODO: this check needs to be revised. It's trivially true right now. + Version currentCompatibilityVersion = indexMetadata.getCompatibilityVersion(); + if (currentCompatibilityVersion.before(Version.V_7_0_0)) { return new DeprecationIssue( DeprecationIssue.Level.CRITICAL, - "Index created before 7.0", + "Old index with a compatibility version < 7.0", "https://www.elastic.co/guide/en/elasticsearch/reference/master/" + "breaking-changes-8.0.html", - "This index was created using version: " + createdWith, + "This index has version: " + currentCompatibilityVersion, false, null ); diff --git a/x-pack/plugin/deprecation/src/test/java/org/elasticsearch/xpack/deprecation/IndexDeprecationChecksTests.java b/x-pack/plugin/deprecation/src/test/java/org/elasticsearch/xpack/deprecation/IndexDeprecationChecksTests.java index ecb8ec1559465..a98f6425c8ea5 100644 --- a/x-pack/plugin/deprecation/src/test/java/org/elasticsearch/xpack/deprecation/IndexDeprecationChecksTests.java +++ b/x-pack/plugin/deprecation/src/test/java/org/elasticsearch/xpack/deprecation/IndexDeprecationChecksTests.java @@ -33,9 +33,9 @@ public void testOldIndicesCheck() { .build(); DeprecationIssue expected = new DeprecationIssue( DeprecationIssue.Level.CRITICAL, - "Index created before 7.0", + "Old index with a compatibility version < 7.0", "https://www.elastic.co/guide/en/elasticsearch/reference/master/" + "breaking-changes-8.0.html", - "This index was created using version: " + createdWith, + "This index has version: " + createdWith, false, null ); diff --git a/x-pack/plugin/searchable-snapshots/src/main/java/org/elasticsearch/xpack/searchablesnapshots/upgrade/SearchableSnapshotIndexMetadataUpgrader.java b/x-pack/plugin/searchable-snapshots/src/main/java/org/elasticsearch/xpack/searchablesnapshots/upgrade/SearchableSnapshotIndexMetadataUpgrader.java index a512c732678bd..29a05f4607f98 100644 --- a/x-pack/plugin/searchable-snapshots/src/main/java/org/elasticsearch/xpack/searchablesnapshots/upgrade/SearchableSnapshotIndexMetadataUpgrader.java +++ b/x-pack/plugin/searchable-snapshots/src/main/java/org/elasticsearch/xpack/searchablesnapshots/upgrade/SearchableSnapshotIndexMetadataUpgrader.java @@ -92,7 +92,9 @@ public void onFailure(Exception e) { static boolean needsUpgrade(ClusterState state) { return StreamSupport.stream(state.metadata().spliterator(), false) - .filter(imd -> imd.getCreationVersion().onOrAfter(Version.V_7_12_0) && imd.getCreationVersion().before(Version.V_8_0_0)) + .filter( + imd -> imd.getCompatibilityVersion().onOrAfter(Version.V_7_12_0) && imd.getCompatibilityVersion().before(Version.V_8_0_0) + ) .filter(IndexMetadata::isPartialSearchableSnapshot) .map(IndexMetadata::getSettings) .anyMatch(SearchableSnapshotIndexMetadataUpgrader::notFrozenShardLimitGroup); @@ -104,7 +106,9 @@ static ClusterState upgradeIndices(ClusterState currentState) { } Metadata.Builder builder = Metadata.builder(currentState.metadata()); StreamSupport.stream(currentState.metadata().spliterator(), false) - .filter(imd -> imd.getCreationVersion().onOrAfter(Version.V_7_12_0) && imd.getCreationVersion().before(Version.V_8_0_0)) + .filter( + imd -> imd.getCompatibilityVersion().onOrAfter(Version.V_7_12_0) && imd.getCompatibilityVersion().before(Version.V_8_0_0) + ) .filter(imd -> imd.isPartialSearchableSnapshot() && notFrozenShardLimitGroup(imd.getSettings())) .map(SearchableSnapshotIndexMetadataUpgrader::setShardLimitGroupFrozen) .forEach(imd -> builder.put(imd, true)); diff --git a/x-pack/qa/repository-old-versions/build.gradle b/x-pack/qa/repository-old-versions/build.gradle index b5f1c54a37189..54e6958c58ac3 100644 --- a/x-pack/qa/repository-old-versions/build.gradle +++ b/x-pack/qa/repository-old-versions/build.gradle @@ -69,6 +69,7 @@ if (Os.isFamily(Os.FAMILY_WINDOWS)) { def testClusterProvider = testClusters.register(clusterName) { testDistribution = 'DEFAULT' numberOfNodes = 2 + versions = [project.version, project.version] // to test full cluster restart setting 'path.repo', repoLocation setting 'xpack.license.self_generated.type', 'trial' @@ -107,25 +108,41 @@ if (Os.isFamily(Os.FAMILY_WINDOWS)) { } } - tasks.register("javaRestTest#${versionNoDots}", StandaloneRestIntegTestTask) { + tasks.register("javaRestTestBeforeRestart#${versionNoDots}", StandaloneRestIntegTestTask) { useCluster testClusterProvider dependsOn fixture doFirst { delete(repoLocation) mkdir(repoLocation) } - systemProperty "tests.repo.location", repoLocation - systemProperty "tests.es.version", version.toString() + systemProperty 'tests.after_restart', 'false' + } + + tasks.register("javaRestTestAfterRestart#${versionNoDots}", StandaloneRestIntegTestTask) { + useCluster testClusterProvider + dependsOn fixture + dependsOn "javaRestTestBeforeRestart#${versionNoDots}" + systemProperty 'tests.after_restart', 'true' + + doFirst { + testClusterProvider.get().goToNextVersion() + } + } + + tasks.matching { it.name.startsWith("javaRestTest") && it.name.endsWith(versionNoDots) }.configureEach { + it.systemProperty "tests.repo.location", repoLocation + it.systemProperty "tests.es.version", version.toString() + /* Use a closure on the string to delay evaluation until right before we * run the integration tests so that we can be sure that the file is * ready. */ - nonInputProperties.systemProperty "tests.es.port", "${-> fixture.get().addressAndPort}" - nonInputProperties.systemProperty('tests.rest.cluster', "${-> testClusterProvider.get().allHttpSocketURI.join(",")}") - nonInputProperties.systemProperty('tests.clustername', "${-> testClusterProvider.get().getName()}") + it.nonInputProperties.systemProperty "tests.es.port", "${-> fixture.get().addressAndPort}" + it.nonInputProperties.systemProperty('tests.rest.cluster', "${-> testClusterProvider.get().allHttpSocketURI.join(",")}") + it.nonInputProperties.systemProperty('tests.clustername', "${-> testClusterProvider.get().getName()}") } tasks.named("check").configure { - dependsOn "javaRestTest#${versionNoDots}" + dependsOn "javaRestTestAfterRestart#${versionNoDots}" } } } diff --git a/x-pack/qa/repository-old-versions/src/test/java/org/elasticsearch/oldrepos/OldRepositoryAccessIT.java b/x-pack/qa/repository-old-versions/src/test/java/org/elasticsearch/oldrepos/OldRepositoryAccessIT.java index 5a43bc5824204..a2e12e6046f06 100644 --- a/x-pack/qa/repository-old-versions/src/test/java/org/elasticsearch/oldrepos/OldRepositoryAccessIT.java +++ b/x-pack/qa/repository-old-versions/src/test/java/org/elasticsearch/oldrepos/OldRepositoryAccessIT.java @@ -39,7 +39,8 @@ import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.util.concurrent.ThreadContext; import org.elasticsearch.common.util.set.Sets; -import org.elasticsearch.core.internal.io.IOUtils; +import org.elasticsearch.core.Booleans; +import org.elasticsearch.core.PathUtils; import org.elasticsearch.index.query.QueryBuilders; import org.elasticsearch.search.SearchHit; import org.elasticsearch.search.builder.SearchSourceBuilder; @@ -72,9 +73,10 @@ import static org.hamcrest.Matchers.startsWith; public class OldRepositoryAccessIT extends ESRestTestCase { + @Override - protected Map>> wipeSnapshots() { - return Collections.emptyMap(); + protected boolean preserveClusterUponCompletion() { + return true; } @Override @@ -99,7 +101,9 @@ public void testOldSourceOnlyRepoAccess() throws IOException { @SuppressWarnings("removal") public void runTest(boolean sourceOnlyRepository) throws IOException { + boolean afterRestart = Booleans.parseBoolean(System.getProperty("tests.after_restart")); String repoLocation = System.getProperty("tests.repo.location"); + repoLocation = PathUtils.get(repoLocation).resolve("source_only_" + sourceOnlyRepository).toString(); Version oldVersion = Version.fromString(System.getProperty("tests.es.version")); assumeTrue( "source only repositories only supported since ES 6.5.0", @@ -107,6 +111,12 @@ public void runTest(boolean sourceOnlyRepository) throws IOException { ); int oldEsPort = Integer.parseInt(System.getProperty("tests.es.port")); + String indexName; + if (sourceOnlyRepository) { + indexName = "source_only_test_index"; + } else { + indexName = "test_index"; + } int numDocs = 10; int extraDocs = 1; final Set expectedIds = new HashSet<>(); @@ -114,157 +124,188 @@ public void runTest(boolean sourceOnlyRepository) throws IOException { RestHighLevelClient client = highLevelClient(adminClient()); RestClient oldEs = RestClient.builder(new HttpHost("127.0.0.1", oldEsPort)).build() ) { - try { - Request createIndex = new Request("PUT", "/test"); - int numberOfShards = randomIntBetween(1, 3); - - XContentBuilder settingsBuilder = XContentFactory.jsonBuilder().startObject().startObject("settings"); - settingsBuilder.field("index.number_of_shards", numberOfShards); - - // 6.5.0 started using soft-deletes, but it was only enabled by default on 7.0 - if (oldVersion.onOrAfter(Version.fromString("6.5.0")) - && oldVersion.before(Version.fromString("7.0.0")) - && randomBoolean()) { - settingsBuilder.field("index.soft_deletes.enabled", true); - } + if (afterRestart == false) { + beforeRestart(sourceOnlyRepository, repoLocation, oldVersion, numDocs, extraDocs, expectedIds, client, oldEs, indexName); + } else { + afterRestart(indexName); + } + } + } - settingsBuilder.endObject().endObject(); + private void afterRestart(String indexName) throws IOException { + if (Build.CURRENT.isSnapshot()) { + ensureGreen("restored_" + indexName); + ensureGreen("mounted_full_copy_" + indexName); + ensureGreen("mounted_shared_cache_" + indexName); + } + } - createIndex.setJsonEntity(Strings.toString(settingsBuilder)); - assertOK(oldEs.performRequest(createIndex)); + @SuppressWarnings("removal") + private void beforeRestart( + boolean sourceOnlyRepository, + String repoLocation, + Version oldVersion, + int numDocs, + int extraDocs, + Set expectedIds, + RestHighLevelClient client, + RestClient oldEs, + String indexName + ) throws IOException { + String repoName = "repo_" + indexName; + String snapshotName = "snap_" + indexName; + Request createIndex = new Request("PUT", "/" + indexName); + int numberOfShards = randomIntBetween(1, 3); - for (int i = 0; i < numDocs + extraDocs; i++) { - String id = "testdoc" + i; - expectedIds.add(id); - // use multiple types for ES versions < 6.0.0 - String type = getType(oldVersion, id); - Request doc = new Request("PUT", "/test/" + type + "/" + id); - doc.addParameter("refresh", "true"); - doc.setJsonEntity(sourceForDoc(i)); - assertOK(oldEs.performRequest(doc)); - } + XContentBuilder settingsBuilder = XContentFactory.jsonBuilder().startObject().startObject("settings"); + settingsBuilder.field("index.number_of_shards", numberOfShards); - for (int i = 0; i < extraDocs; i++) { - String id = randomFrom(expectedIds); - expectedIds.remove(id); - String type = getType(oldVersion, id); - Request doc = new Request("DELETE", "/test/" + type + "/" + id); - doc.addParameter("refresh", "true"); - oldEs.performRequest(doc); - } + // 6.5.0 started using soft-deletes, but it was only enabled by default on 7.0 + if (oldVersion.onOrAfter(Version.fromString("6.5.0")) && oldVersion.before(Version.fromString("7.0.0")) && randomBoolean()) { + settingsBuilder.field("index.soft_deletes.enabled", true); + } - // register repo on old ES and take snapshot - Request createRepoRequest = new Request("PUT", "/_snapshot/testrepo"); - createRepoRequest.setJsonEntity(sourceOnlyRepository ? """ - {"type":"source","settings":{"location":"%s","delegate_type":"fs"}} - """.formatted(repoLocation) : """ - {"type":"fs","settings":{"location":"%s"}} - """.formatted(repoLocation)); - assertOK(oldEs.performRequest(createRepoRequest)); - - Request createSnapshotRequest = new Request("PUT", "/_snapshot/testrepo/snap1"); - createSnapshotRequest.addParameter("wait_for_completion", "true"); - createSnapshotRequest.setJsonEntity("{\"indices\":\"test\"}"); - assertOK(oldEs.performRequest(createSnapshotRequest)); - - // register repo on new ES - Settings.Builder repoSettingsBuilder = Settings.builder().put("location", repoLocation); - if (sourceOnlyRepository) { - repoSettingsBuilder.put("delegate_type", "fs"); - } - if (Build.CURRENT.isSnapshot()) { - repoSettingsBuilder.put("allow_bwc_indices", true); - } - ElasticsearchAssertions.assertAcked( - client.snapshot() - .createRepository( - new PutRepositoryRequest("testrepo").type(sourceOnlyRepository ? "source" : "fs").settings(repoSettingsBuilder), - RequestOptions.DEFAULT - ) - ); + settingsBuilder.endObject().endObject(); - // list snapshots on new ES - List snapshotInfos = client.snapshot() - .get(new GetSnapshotsRequest("testrepo").snapshots(new String[] { "_all" }), RequestOptions.DEFAULT) - .getSnapshots(); - assertThat(snapshotInfos, hasSize(1)); - SnapshotInfo snapshotInfo = snapshotInfos.get(0); - assertEquals("snap1", snapshotInfo.snapshotId().getName()); - assertEquals("testrepo", snapshotInfo.repository()); - assertEquals(Arrays.asList("test"), snapshotInfo.indices()); - assertEquals(SnapshotState.SUCCESS, snapshotInfo.state()); - assertEquals(numberOfShards, snapshotInfo.successfulShards()); - assertEquals(numberOfShards, snapshotInfo.totalShards()); - assertEquals(0, snapshotInfo.failedShards()); - assertEquals(oldVersion, snapshotInfo.version()); - - // list specific snapshot on new ES - snapshotInfos = client.snapshot() - .get(new GetSnapshotsRequest("testrepo").snapshots(new String[] { "snap1" }), RequestOptions.DEFAULT) - .getSnapshots(); - assertThat(snapshotInfos, hasSize(1)); - snapshotInfo = snapshotInfos.get(0); - assertEquals("snap1", snapshotInfo.snapshotId().getName()); - assertEquals("testrepo", snapshotInfo.repository()); - assertEquals(Arrays.asList("test"), snapshotInfo.indices()); - assertEquals(SnapshotState.SUCCESS, snapshotInfo.state()); - assertEquals(numberOfShards, snapshotInfo.successfulShards()); - assertEquals(numberOfShards, snapshotInfo.totalShards()); - assertEquals(0, snapshotInfo.failedShards()); - assertEquals(oldVersion, snapshotInfo.version()); - - // list advanced snapshot info on new ES - SnapshotsStatusResponse snapshotsStatusResponse = client.snapshot() - .status(new SnapshotsStatusRequest("testrepo").snapshots(new String[] { "snap1" }), RequestOptions.DEFAULT); - assertThat(snapshotsStatusResponse.getSnapshots(), hasSize(1)); - SnapshotStatus snapshotStatus = snapshotsStatusResponse.getSnapshots().get(0); - assertEquals("snap1", snapshotStatus.getSnapshot().getSnapshotId().getName()); - assertEquals("testrepo", snapshotStatus.getSnapshot().getRepository()); - assertEquals(Sets.newHashSet("test"), snapshotStatus.getIndices().keySet()); - assertEquals(SnapshotsInProgress.State.SUCCESS, snapshotStatus.getState()); - assertEquals(numberOfShards, snapshotStatus.getShardsStats().getDoneShards()); - assertEquals(numberOfShards, snapshotStatus.getShardsStats().getTotalShards()); - assertEquals(0, snapshotStatus.getShardsStats().getFailedShards()); - assertThat(snapshotStatus.getStats().getTotalSize(), greaterThan(0L)); - assertThat(snapshotStatus.getStats().getTotalFileCount(), greaterThan(0)); - - if (Build.CURRENT.isSnapshot()) { - // restore / mount and check whether searches work - restoreMountAndVerify(numDocs, expectedIds, client, numberOfShards, sourceOnlyRepository, oldVersion); - - // close indices - assertTrue( - client.indices().close(new CloseIndexRequest("restored_test"), RequestOptions.DEFAULT).isShardsAcknowledged() - ); - assertTrue( - client.indices() - .close(new CloseIndexRequest("mounted_full_copy_test"), RequestOptions.DEFAULT) - .isShardsAcknowledged() - ); - assertTrue( - client.indices() - .close(new CloseIndexRequest("mounted_shared_cache_test"), RequestOptions.DEFAULT) - .isShardsAcknowledged() - ); - - // restore / mount again - restoreMountAndVerify(numDocs, expectedIds, client, numberOfShards, sourceOnlyRepository, oldVersion); - } - } finally { - IOUtils.closeWhileHandlingException( - () -> oldEs.performRequest(new Request("DELETE", "/test")), - () -> oldEs.performRequest(new Request("DELETE", "/_snapshot/testrepo/snap1")), - () -> oldEs.performRequest(new Request("DELETE", "/_snapshot/testrepo")) - ); - if (Build.CURRENT.isSnapshot()) { - IOUtils.closeWhileHandlingException( - () -> client().performRequest(new Request("DELETE", "/restored_test")), - () -> client().performRequest(new Request("DELETE", "/mounted_full_copy_test")), - () -> client().performRequest(new Request("DELETE", "/mounted_shared_cache_test")) - ); - } - IOUtils.closeWhileHandlingException(() -> client().performRequest(new Request("DELETE", "/_snapshot/testrepo"))); - } + createIndex.setJsonEntity(Strings.toString(settingsBuilder)); + assertOK(oldEs.performRequest(createIndex)); + + for (int i = 0; i < numDocs + extraDocs; i++) { + String id = "testdoc" + i; + expectedIds.add(id); + // use multiple types for ES versions < 6.0.0 + String type = getType(oldVersion, id); + Request doc = new Request("PUT", "/" + indexName + "/" + type + "/" + id); + doc.addParameter("refresh", "true"); + doc.setJsonEntity(sourceForDoc(i)); + assertOK(oldEs.performRequest(doc)); + } + + for (int i = 0; i < extraDocs; i++) { + String id = randomFrom(expectedIds); + expectedIds.remove(id); + String type = getType(oldVersion, id); + Request doc = new Request("DELETE", "/" + indexName + "/" + type + "/" + id); + doc.addParameter("refresh", "true"); + oldEs.performRequest(doc); + } + + // register repo on old ES and take snapshot + Request createRepoRequest = new Request("PUT", "/_snapshot/" + repoName); + createRepoRequest.setJsonEntity(sourceOnlyRepository ? """ + {"type":"source","settings":{"location":"%s","delegate_type":"fs"}} + """.formatted(repoLocation) : """ + {"type":"fs","settings":{"location":"%s"}} + """.formatted(repoLocation)); + assertOK(oldEs.performRequest(createRepoRequest)); + + Request createSnapshotRequest = new Request("PUT", "/_snapshot/" + repoName + "/" + snapshotName); + createSnapshotRequest.addParameter("wait_for_completion", "true"); + createSnapshotRequest.setJsonEntity("{\"indices\":\"" + indexName + "\"}"); + assertOK(oldEs.performRequest(createSnapshotRequest)); + + // register repo on new ES + Settings.Builder repoSettingsBuilder = Settings.builder().put("location", repoLocation); + if (sourceOnlyRepository) { + repoSettingsBuilder.put("delegate_type", "fs"); + } + if (Build.CURRENT.isSnapshot()) { + repoSettingsBuilder.put("allow_bwc_indices", true); + } + ElasticsearchAssertions.assertAcked( + client.snapshot() + .createRepository( + new PutRepositoryRequest(repoName).type(sourceOnlyRepository ? "source" : "fs").settings(repoSettingsBuilder), + RequestOptions.DEFAULT + ) + ); + + // list snapshots on new ES + List snapshotInfos = client.snapshot() + .get(new GetSnapshotsRequest(repoName).snapshots(new String[] { "_all" }), RequestOptions.DEFAULT) + .getSnapshots(); + assertThat(snapshotInfos, hasSize(1)); + SnapshotInfo snapshotInfo = snapshotInfos.get(0); + assertEquals(snapshotName, snapshotInfo.snapshotId().getName()); + assertEquals(repoName, snapshotInfo.repository()); + assertEquals(Arrays.asList(indexName), snapshotInfo.indices()); + assertEquals(SnapshotState.SUCCESS, snapshotInfo.state()); + assertEquals(numberOfShards, snapshotInfo.successfulShards()); + assertEquals(numberOfShards, snapshotInfo.totalShards()); + assertEquals(0, snapshotInfo.failedShards()); + assertEquals(oldVersion, snapshotInfo.version()); + + // list specific snapshot on new ES + snapshotInfos = client.snapshot() + .get(new GetSnapshotsRequest(repoName).snapshots(new String[] { snapshotName }), RequestOptions.DEFAULT) + .getSnapshots(); + assertThat(snapshotInfos, hasSize(1)); + snapshotInfo = snapshotInfos.get(0); + assertEquals(snapshotName, snapshotInfo.snapshotId().getName()); + assertEquals(repoName, snapshotInfo.repository()); + assertEquals(Arrays.asList(indexName), snapshotInfo.indices()); + assertEquals(SnapshotState.SUCCESS, snapshotInfo.state()); + assertEquals(numberOfShards, snapshotInfo.successfulShards()); + assertEquals(numberOfShards, snapshotInfo.totalShards()); + assertEquals(0, snapshotInfo.failedShards()); + assertEquals(oldVersion, snapshotInfo.version()); + + // list advanced snapshot info on new ES + SnapshotsStatusResponse snapshotsStatusResponse = client.snapshot() + .status(new SnapshotsStatusRequest(repoName).snapshots(new String[] { snapshotName }), RequestOptions.DEFAULT); + assertThat(snapshotsStatusResponse.getSnapshots(), hasSize(1)); + SnapshotStatus snapshotStatus = snapshotsStatusResponse.getSnapshots().get(0); + assertEquals(snapshotName, snapshotStatus.getSnapshot().getSnapshotId().getName()); + assertEquals(repoName, snapshotStatus.getSnapshot().getRepository()); + assertEquals(Sets.newHashSet(indexName), snapshotStatus.getIndices().keySet()); + assertEquals(SnapshotsInProgress.State.SUCCESS, snapshotStatus.getState()); + assertEquals(numberOfShards, snapshotStatus.getShardsStats().getDoneShards()); + assertEquals(numberOfShards, snapshotStatus.getShardsStats().getTotalShards()); + assertEquals(0, snapshotStatus.getShardsStats().getFailedShards()); + assertThat(snapshotStatus.getStats().getTotalSize(), greaterThan(0L)); + assertThat(snapshotStatus.getStats().getTotalFileCount(), greaterThan(0)); + + if (Build.CURRENT.isSnapshot()) { + // restore / mount and check whether searches work + restoreMountAndVerify( + numDocs, + expectedIds, + client, + numberOfShards, + sourceOnlyRepository, + oldVersion, + indexName, + repoName, + snapshotName + ); + + // close indices + assertTrue( + client.indices().close(new CloseIndexRequest("restored_" + indexName), RequestOptions.DEFAULT).isShardsAcknowledged() + ); + assertTrue( + client.indices() + .close(new CloseIndexRequest("mounted_full_copy_" + indexName), RequestOptions.DEFAULT) + .isShardsAcknowledged() + ); + assertTrue( + client.indices() + .close(new CloseIndexRequest("mounted_shared_cache_" + indexName), RequestOptions.DEFAULT) + .isShardsAcknowledged() + ); + + // restore / mount again + restoreMountAndVerify( + numDocs, + expectedIds, + client, + numberOfShards, + sourceOnlyRepository, + oldVersion, + indexName, + repoName, + snapshotName + ); } } @@ -283,12 +324,15 @@ private void restoreMountAndVerify( RestHighLevelClient client, int numberOfShards, boolean sourceOnlyRepository, - Version oldVersion + Version oldVersion, + String indexName, + String repoName, + String snapshotName ) throws IOException { // restore index RestoreSnapshotResponse restoreSnapshotResponse = client.snapshot() .restore( - new RestoreSnapshotRequest("testrepo", "snap1").indices("test") + new RestoreSnapshotRequest(repoName, snapshotName).indices(indexName) .renamePattern("(.+)") .renameReplacement("restored_$1") .waitForCompletion(true), @@ -302,16 +346,16 @@ private void restoreMountAndVerify( ClusterHealthStatus.GREEN, client.cluster() .health( - new ClusterHealthRequest("restored_test").waitForGreenStatus().waitForNoRelocatingShards(true), + new ClusterHealthRequest("restored_" + indexName).waitForGreenStatus().waitForNoRelocatingShards(true), RequestOptions.DEFAULT ) .getStatus() ); MappingMetadata mapping = client.indices() - .getMapping(new GetMappingsRequest().indices("restored_test"), RequestOptions.DEFAULT) + .getMapping(new GetMappingsRequest().indices("restored_" + indexName), RequestOptions.DEFAULT) .mappings() - .get("restored_test"); + .get("restored_" + indexName); logger.info("mapping for {}: {}", mapping.type(), mapping.source().string()); Map root = mapping.sourceAsMap(); assertThat(root, hasKey("_meta")); @@ -342,13 +386,13 @@ private void restoreMountAndVerify( } // run a search against the index - assertDocs("restored_test", numDocs, expectedIds, client, sourceOnlyRepository, oldVersion); + assertDocs("restored_" + indexName, numDocs, expectedIds, client, sourceOnlyRepository, oldVersion); // mount as full copy searchable snapshot RestoreSnapshotResponse mountSnapshotResponse = client.searchableSnapshots() .mountSnapshot( - new MountSnapshotRequest("testrepo", "snap1", "test").storage(MountSnapshotRequest.Storage.FULL_COPY) - .renamedIndex("mounted_full_copy_test") + new MountSnapshotRequest(repoName, snapshotName, indexName).storage(MountSnapshotRequest.Storage.FULL_COPY) + .renamedIndex("mounted_full_copy_" + indexName) .indexSettings(Settings.builder().put(IndexMetadata.SETTING_NUMBER_OF_REPLICAS, 1).build()) .waitForCompletion(true), RequestOptions.DEFAULT @@ -361,20 +405,20 @@ private void restoreMountAndVerify( ClusterHealthStatus.GREEN, client.cluster() .health( - new ClusterHealthRequest("mounted_full_copy_test").waitForGreenStatus().waitForNoRelocatingShards(true), + new ClusterHealthRequest("mounted_full_copy_" + indexName).waitForGreenStatus().waitForNoRelocatingShards(true), RequestOptions.DEFAULT ) .getStatus() ); // run a search against the index - assertDocs("mounted_full_copy_test", numDocs, expectedIds, client, sourceOnlyRepository, oldVersion); + assertDocs("mounted_full_copy_" + indexName, numDocs, expectedIds, client, sourceOnlyRepository, oldVersion); // mount as shared cache searchable snapshot mountSnapshotResponse = client.searchableSnapshots() .mountSnapshot( - new MountSnapshotRequest("testrepo", "snap1", "test").storage(MountSnapshotRequest.Storage.SHARED_CACHE) - .renamedIndex("mounted_shared_cache_test") + new MountSnapshotRequest(repoName, snapshotName, indexName).storage(MountSnapshotRequest.Storage.SHARED_CACHE) + .renamedIndex("mounted_shared_cache_" + indexName) .waitForCompletion(true), RequestOptions.DEFAULT ); @@ -383,7 +427,7 @@ private void restoreMountAndVerify( assertEquals(numberOfShards, mountSnapshotResponse.getRestoreInfo().successfulShards()); // run a search against the index - assertDocs("mounted_shared_cache_test", numDocs, expectedIds, client, sourceOnlyRepository, oldVersion); + assertDocs("mounted_shared_cache_" + indexName, numDocs, expectedIds, client, sourceOnlyRepository, oldVersion); } @SuppressWarnings("removal")