Skip to content

Commit 9fdd0dc

Browse files
authored
Use write block on archive indices (#85102)
Ensure archive indices have a write block and the write block can't be removed. Relates #81210
1 parent b826516 commit 9fdd0dc

File tree

5 files changed

+171
-95
lines changed

5 files changed

+171
-95
lines changed

server/src/main/java/org/elasticsearch/snapshots/RestoreService.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1609,6 +1609,7 @@ private IndexMetadata convertLegacyIndex(IndexMetadata snapshotIndexMetadata, Cl
16091609
Settings.builder()
16101610
.put(snapshotIndexMetadata.getSettings())
16111611
.put(IndexMetadata.SETTING_INDEX_VERSION_COMPATIBILITY.getKey(), clusterState.getNodes().getSmallestNonClientNodeVersion())
1612+
.put(IndexMetadata.SETTING_BLOCKS_WRITE, true)
16121613
);
16131614
// TODO: _routing? Perhaps we don't need to obey any routing here as stuff is read-only anyway and get API will be disabled
16141615
return convertedIndexMetadata.build();
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
/*
2+
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
3+
* or more contributor license agreements. Licensed under the Elastic License
4+
* 2.0; you may not use this file except in compliance with the Elastic License
5+
* 2.0.
6+
*/
7+
8+
package org.elasticsearch.xpack.lucene.bwc;
9+
10+
import org.elasticsearch.Version;
11+
import org.elasticsearch.cluster.metadata.IndexMetadata;
12+
import org.elasticsearch.cluster.metadata.RepositoryMetadata;
13+
import org.elasticsearch.cluster.service.ClusterService;
14+
import org.elasticsearch.common.settings.Settings;
15+
import org.elasticsearch.common.util.BigArrays;
16+
import org.elasticsearch.env.Environment;
17+
import org.elasticsearch.indices.recovery.RecoverySettings;
18+
import org.elasticsearch.license.License;
19+
import org.elasticsearch.license.PostStartTrialAction;
20+
import org.elasticsearch.license.PostStartTrialRequest;
21+
import org.elasticsearch.plugins.Plugin;
22+
import org.elasticsearch.plugins.RepositoryPlugin;
23+
import org.elasticsearch.repositories.IndexId;
24+
import org.elasticsearch.repositories.Repository;
25+
import org.elasticsearch.repositories.RepositoryData;
26+
import org.elasticsearch.repositories.fs.FsRepository;
27+
import org.elasticsearch.snapshots.AbstractSnapshotIntegTestCase;
28+
import org.elasticsearch.snapshots.SnapshotId;
29+
import org.elasticsearch.snapshots.mockstore.MockRepository;
30+
import org.elasticsearch.test.ESIntegTestCase;
31+
import org.elasticsearch.xcontent.NamedXContentRegistry;
32+
import org.junit.Before;
33+
34+
import java.io.IOException;
35+
import java.util.Arrays;
36+
import java.util.Collection;
37+
import java.util.Map;
38+
39+
import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertAcked;
40+
41+
@ESIntegTestCase.ClusterScope(supportsDedicatedMasters = false, numClientNodes = 0, scope = ESIntegTestCase.Scope.TEST)
42+
public abstract class AbstractArchiveTestCase extends AbstractSnapshotIntegTestCase {
43+
44+
@Override
45+
protected Collection<Class<? extends Plugin>> nodePlugins() {
46+
return Arrays.asList(LocalStateOldLuceneVersions.class, TestRepositoryPlugin.class, MockRepository.Plugin.class);
47+
}
48+
49+
public static class TestRepositoryPlugin extends Plugin implements RepositoryPlugin {
50+
public static final String FAKE_VERSIONS_TYPE = "fakeversionsrepo";
51+
52+
@Override
53+
public Map<String, Repository.Factory> getRepositories(
54+
Environment env,
55+
NamedXContentRegistry namedXContentRegistry,
56+
ClusterService clusterService,
57+
BigArrays bigArrays,
58+
RecoverySettings recoverySettings
59+
) {
60+
return Map.of(
61+
FAKE_VERSIONS_TYPE,
62+
metadata -> new FakeVersionsRepo(metadata, env, namedXContentRegistry, clusterService, bigArrays, recoverySettings)
63+
);
64+
}
65+
66+
// fakes an old index version format to activate license checks
67+
private static class FakeVersionsRepo extends FsRepository {
68+
FakeVersionsRepo(
69+
RepositoryMetadata metadata,
70+
Environment env,
71+
NamedXContentRegistry namedXContentRegistry,
72+
ClusterService clusterService,
73+
BigArrays bigArrays,
74+
RecoverySettings recoverySettings
75+
) {
76+
super(metadata, env, namedXContentRegistry, clusterService, bigArrays, recoverySettings);
77+
}
78+
79+
@Override
80+
public IndexMetadata getSnapshotIndexMetaData(RepositoryData repositoryData, SnapshotId snapshotId, IndexId index)
81+
throws IOException {
82+
final IndexMetadata original = super.getSnapshotIndexMetaData(repositoryData, snapshotId, index);
83+
return IndexMetadata.builder(original)
84+
.settings(
85+
Settings.builder()
86+
.put(original.getSettings())
87+
.put(
88+
IndexMetadata.SETTING_INDEX_VERSION_CREATED.getKey(),
89+
metadata.settings()
90+
.getAsVersion("version", randomBoolean() ? Version.fromString("5.0.0") : Version.fromString("6.0.0"))
91+
)
92+
)
93+
.build();
94+
}
95+
}
96+
}
97+
98+
protected static final String repoName = "test-repo";
99+
protected static final String indexName = "test-index";
100+
protected static final String snapshotName = "test-snapshot";
101+
102+
@Before
103+
public void createAndRestoreArchive() throws Exception {
104+
createRepository(repoName, TestRepositoryPlugin.FAKE_VERSIONS_TYPE);
105+
createIndex(indexName);
106+
createFullSnapshot(repoName, snapshotName);
107+
108+
assertAcked(client().admin().indices().prepareDelete(indexName));
109+
110+
PostStartTrialRequest request = new PostStartTrialRequest().setType(License.LicenseType.TRIAL.getTypeName()).acknowledge(true);
111+
client().execute(PostStartTrialAction.INSTANCE, request).get();
112+
}
113+
}

x-pack/plugin/old-lucene-versions/src/internalClusterTest/java/org/elasticsearch/xpack/lucene/bwc/ArchiveLicenseIntegTests.java

Lines changed: 1 addition & 95 deletions
Original file line numberDiff line numberDiff line change
@@ -13,14 +13,8 @@
1313
import org.elasticsearch.action.admin.cluster.snapshots.restore.RestoreSnapshotResponse;
1414
import org.elasticsearch.cluster.ClusterState;
1515
import org.elasticsearch.cluster.health.ClusterHealthStatus;
16-
import org.elasticsearch.cluster.metadata.IndexMetadata;
1716
import org.elasticsearch.cluster.metadata.Metadata;
18-
import org.elasticsearch.cluster.metadata.RepositoryMetadata;
19-
import org.elasticsearch.cluster.service.ClusterService;
2017
import org.elasticsearch.common.settings.Settings;
21-
import org.elasticsearch.common.util.BigArrays;
22-
import org.elasticsearch.env.Environment;
23-
import org.elasticsearch.indices.recovery.RecoverySettings;
2418
import org.elasticsearch.license.DeleteLicenseAction;
2519
import org.elasticsearch.license.License;
2620
import org.elasticsearch.license.LicensesMetadata;
@@ -29,108 +23,20 @@
2923
import org.elasticsearch.license.PostStartTrialAction;
3024
import org.elasticsearch.license.PostStartTrialRequest;
3125
import org.elasticsearch.license.PostStartTrialResponse;
32-
import org.elasticsearch.plugins.Plugin;
33-
import org.elasticsearch.plugins.RepositoryPlugin;
3426
import org.elasticsearch.protocol.xpack.XPackUsageRequest;
3527
import org.elasticsearch.protocol.xpack.license.DeleteLicenseRequest;
36-
import org.elasticsearch.repositories.IndexId;
37-
import org.elasticsearch.repositories.Repository;
38-
import org.elasticsearch.repositories.RepositoryData;
39-
import org.elasticsearch.repositories.fs.FsRepository;
40-
import org.elasticsearch.snapshots.AbstractSnapshotIntegTestCase;
41-
import org.elasticsearch.snapshots.SnapshotId;
4228
import org.elasticsearch.snapshots.SnapshotRestoreException;
43-
import org.elasticsearch.snapshots.mockstore.MockRepository;
44-
import org.elasticsearch.test.ESIntegTestCase;
45-
import org.elasticsearch.xcontent.NamedXContentRegistry;
4629
import org.elasticsearch.xpack.core.action.XPackUsageFeatureAction;
4730
import org.elasticsearch.xpack.core.action.XPackUsageFeatureResponse;
4831
import org.elasticsearch.xpack.core.archive.ArchiveFeatureSetUsage;
49-
import org.junit.Before;
50-
51-
import java.io.IOException;
52-
import java.util.Arrays;
53-
import java.util.Collection;
54-
import java.util.Map;
5532

5633
import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertAcked;
5734
import static org.hamcrest.Matchers.containsString;
5835
import static org.hamcrest.Matchers.equalTo;
5936
import static org.hamcrest.Matchers.instanceOf;
6037
import static org.hamcrest.Matchers.oneOf;
6138

62-
@ESIntegTestCase.ClusterScope(supportsDedicatedMasters = false, numClientNodes = 0, scope = ESIntegTestCase.Scope.TEST)
63-
public class ArchiveLicenseIntegTests extends AbstractSnapshotIntegTestCase {
64-
65-
@Override
66-
protected Collection<Class<? extends Plugin>> nodePlugins() {
67-
return Arrays.asList(LocalStateOldLuceneVersions.class, TestRepositoryPlugin.class, MockRepository.Plugin.class);
68-
}
69-
70-
public static class TestRepositoryPlugin extends Plugin implements RepositoryPlugin {
71-
public static final String FAKE_VERSIONS_TYPE = "fakeversionsrepo";
72-
73-
@Override
74-
public Map<String, Repository.Factory> getRepositories(
75-
Environment env,
76-
NamedXContentRegistry namedXContentRegistry,
77-
ClusterService clusterService,
78-
BigArrays bigArrays,
79-
RecoverySettings recoverySettings
80-
) {
81-
return Map.of(
82-
FAKE_VERSIONS_TYPE,
83-
metadata -> new FakeVersionsRepo(metadata, env, namedXContentRegistry, clusterService, bigArrays, recoverySettings)
84-
);
85-
}
86-
87-
// fakes an old index version format to activate license checks
88-
private static class FakeVersionsRepo extends FsRepository {
89-
FakeVersionsRepo(
90-
RepositoryMetadata metadata,
91-
Environment env,
92-
NamedXContentRegistry namedXContentRegistry,
93-
ClusterService clusterService,
94-
BigArrays bigArrays,
95-
RecoverySettings recoverySettings
96-
) {
97-
super(metadata, env, namedXContentRegistry, clusterService, bigArrays, recoverySettings);
98-
}
99-
100-
@Override
101-
public IndexMetadata getSnapshotIndexMetaData(RepositoryData repositoryData, SnapshotId snapshotId, IndexId index)
102-
throws IOException {
103-
final IndexMetadata original = super.getSnapshotIndexMetaData(repositoryData, snapshotId, index);
104-
return IndexMetadata.builder(original)
105-
.settings(
106-
Settings.builder()
107-
.put(original.getSettings())
108-
.put(
109-
IndexMetadata.SETTING_INDEX_VERSION_CREATED.getKey(),
110-
metadata.settings()
111-
.getAsVersion("version", randomBoolean() ? Version.fromString("5.0.0") : Version.fromString("6.0.0"))
112-
)
113-
)
114-
.build();
115-
}
116-
}
117-
}
118-
119-
private static final String repoName = "test-repo";
120-
private static final String indexName = "test-index";
121-
private static final String snapshotName = "test-snapshot";
122-
123-
@Before
124-
public void createAndRestoreArchive() throws Exception {
125-
createRepository(repoName, TestRepositoryPlugin.FAKE_VERSIONS_TYPE);
126-
createIndex(indexName);
127-
createFullSnapshot(repoName, snapshotName);
128-
129-
assertAcked(client().admin().indices().prepareDelete(indexName));
130-
131-
PostStartTrialRequest request = new PostStartTrialRequest().setType(License.LicenseType.TRIAL.getTypeName()).acknowledge(true);
132-
client().execute(PostStartTrialAction.INSTANCE, request).get();
133-
}
39+
public class ArchiveLicenseIntegTests extends AbstractArchiveTestCase {
13440

13541
public void testFeatureUsage() throws Exception {
13642
XPackUsageFeatureResponse usage = client().execute(XPackUsageFeatureAction.ARCHIVE, new XPackUsageRequest()).get();
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
/*
2+
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
3+
* or more contributor license agreements. Licensed under the Elastic License
4+
* 2.0; you may not use this file except in compliance with the Elastic License
5+
* 2.0.
6+
*/
7+
8+
package org.elasticsearch.xpack.lucene.bwc;
9+
10+
import org.elasticsearch.action.admin.cluster.snapshots.restore.RestoreSnapshotRequest;
11+
import org.elasticsearch.action.admin.cluster.snapshots.restore.RestoreSnapshotResponse;
12+
import org.elasticsearch.cluster.metadata.IndexMetadata;
13+
import org.elasticsearch.common.settings.Settings;
14+
15+
import java.util.concurrent.ExecutionException;
16+
17+
import static org.hamcrest.Matchers.containsString;
18+
import static org.hamcrest.Matchers.equalTo;
19+
20+
public class ArchiveSettingValidationIntegTests extends AbstractArchiveTestCase {
21+
public void testCannotRemoveWriteBlock() throws ExecutionException, InterruptedException {
22+
final RestoreSnapshotRequest req = new RestoreSnapshotRequest(repoName, snapshotName).indices(indexName).waitForCompletion(true);
23+
24+
final RestoreSnapshotResponse restoreSnapshotResponse = client().admin().cluster().restoreSnapshot(req).get();
25+
assertThat(restoreSnapshotResponse.getRestoreInfo().failedShards(), equalTo(0));
26+
ensureGreen(indexName);
27+
28+
final IllegalArgumentException iae = expectThrows(
29+
IllegalArgumentException.class,
30+
() -> client().admin()
31+
.indices()
32+
.prepareUpdateSettings(indexName)
33+
.setSettings(Settings.builder().put(IndexMetadata.INDEX_BLOCKS_WRITE_SETTING.getKey(), false))
34+
.get()
35+
);
36+
assertThat(
37+
iae.getMessage(),
38+
containsString("illegal value can't update [" + IndexMetadata.INDEX_BLOCKS_WRITE_SETTING.getKey() + "] from [true] to [false]")
39+
);
40+
assertNotNull(iae.getCause());
41+
assertThat(iae.getCause().getMessage(), containsString("Cannot remove write block from archive index"));
42+
43+
client().admin()
44+
.indices()
45+
.prepareUpdateSettings(indexName)
46+
.setSettings(Settings.builder().put(IndexMetadata.INDEX_BLOCKS_WRITE_SETTING.getKey(), true))
47+
.get();
48+
}
49+
}

x-pack/plugin/old-lucene-versions/src/main/java/org/elasticsearch/xpack/lucene/bwc/OldLuceneVersions.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
import org.elasticsearch.action.ActionRequest;
1616
import org.elasticsearch.action.ActionResponse;
1717
import org.elasticsearch.client.internal.Client;
18+
import org.elasticsearch.cluster.metadata.IndexMetadata;
1819
import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver;
1920
import org.elasticsearch.cluster.node.DiscoveryNode;
2021
import org.elasticsearch.cluster.routing.allocation.decider.AllocationDecider;
@@ -128,6 +129,12 @@ public void afterFilesRestoredFromRepository(IndexShard indexShard) {
128129
});
129130

130131
indexModule.addIndexEventListener(failShardsListener.get());
132+
133+
indexModule.addSettingsUpdateConsumer(IndexMetadata.INDEX_BLOCKS_WRITE_SETTING, s -> {}, write -> {
134+
if (write == false) {
135+
throw new IllegalArgumentException("Cannot remove write block from archive index");
136+
}
137+
});
131138
}
132139
}
133140

0 commit comments

Comments
 (0)