Skip to content

Commit f0d9177

Browse files
committed
Update tests for ElasticsearchNodeCommand
The PR updates ElasticsearchNodeCommandTests so that they test the right behaviours after multi-project related changes: 1. Persisted cluster state is always written with multi-project enabled 2. The CLI tools are expected to run on the same node version, i.e. no BWC 3. Unknown (unregistered) customs can be read and write by the CLI tools without modification Resolves: ES-11328
1 parent cc02ac8 commit f0d9177

File tree

2 files changed

+159
-46
lines changed

2 files changed

+159
-46
lines changed

server/src/main/java/org/elasticsearch/gateway/PersistedClusterStateService.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -783,7 +783,7 @@ private static BytesReference uncompress(BytesReference bytesReference) throws I
783783
private static final ToXContent.Params FORMAT_PARAMS;
784784

785785
static {
786-
Map<String, String> params = Maps.newMapWithExpectedSize(2);
786+
Map<String, String> params = Maps.newMapWithExpectedSize(4);
787787
params.put("binary", "true");
788788
params.put(Metadata.CONTEXT_MODE_PARAM, Metadata.CONTEXT_MODE_GATEWAY);
789789
params.put(Metadata.DEDUPLICATED_MAPPINGS_PARAM, Boolean.TRUE.toString());

server/src/test/java/org/elasticsearch/cluster/coordination/ElasticsearchNodeCommandTests.java

Lines changed: 158 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -9,23 +9,30 @@
99

1010
package org.elasticsearch.cluster.coordination;
1111

12+
import org.elasticsearch.TransportVersion;
1213
import org.elasticsearch.cluster.ClusterModule;
14+
import org.elasticsearch.cluster.ClusterState;
1315
import org.elasticsearch.cluster.metadata.IndexGraveyard;
1416
import org.elasticsearch.cluster.metadata.IndexMetadata;
1517
import org.elasticsearch.cluster.metadata.Metadata;
1618
import org.elasticsearch.common.UUIDs;
17-
import org.elasticsearch.common.bytes.BytesReference;
19+
import org.elasticsearch.common.settings.ClusterSettings;
20+
import org.elasticsearch.common.settings.Settings;
21+
import org.elasticsearch.core.Tuple;
22+
import org.elasticsearch.env.Environment;
23+
import org.elasticsearch.env.TestEnvironment;
24+
import org.elasticsearch.gateway.PersistedClusterStateService;
1825
import org.elasticsearch.index.Index;
1926
import org.elasticsearch.indices.IndicesModule;
2027
import org.elasticsearch.test.ESTestCase;
28+
import org.elasticsearch.test.TestClusterCustomMetadata;
29+
import org.elasticsearch.test.TestProjectCustomMetadata;
2130
import org.elasticsearch.xcontent.NamedXContentRegistry;
22-
import org.elasticsearch.xcontent.XContentBuilder;
23-
import org.elasticsearch.xcontent.XContentParser;
24-
import org.elasticsearch.xcontent.XContentParserConfiguration;
25-
import org.elasticsearch.xcontent.json.JsonXContent;
31+
import org.elasticsearch.xcontent.ParseField;
2632

2733
import java.io.IOException;
2834
import java.nio.file.Path;
35+
import java.util.EnumSet;
2936
import java.util.List;
3037
import java.util.function.Function;
3138
import java.util.stream.Stream;
@@ -34,65 +41,100 @@
3441
import static org.elasticsearch.cluster.metadata.DataStreamTestHelper.newInstance;
3542
import static org.hamcrest.Matchers.equalTo;
3643
import static org.hamcrest.Matchers.instanceOf;
37-
import static org.hamcrest.Matchers.not;
3844

3945
public class ElasticsearchNodeCommandTests extends ESTestCase {
4046

41-
public void testLoadStateWithoutMissingCustoms() throws IOException {
42-
runLoadStateTest(false, false);
43-
}
44-
4547
public void testLoadStateWithoutMissingCustomsButPreserved() throws IOException {
46-
runLoadStateTest(false, true);
48+
runLoadStateTest(false);
4749
}
4850

4951
public void testLoadStateWithMissingCustomsButPreserved() throws IOException {
50-
runLoadStateTest(true, true);
52+
runLoadStateTest(true);
5153
}
5254

53-
public void testLoadStateWithMissingCustomsAndNotPreserved() throws IOException {
54-
runLoadStateTest(true, false);
55+
@Override
56+
public Settings buildEnvSettings(Settings settings) {
57+
// we control the data path in the tests, so we don't need to set it here
58+
return settings;
5559
}
5660

57-
private void runLoadStateTest(boolean hasMissingCustoms, boolean preserveUnknownCustoms) throws IOException {
58-
final Metadata latestMetadata = randomMeta();
59-
final XContentBuilder builder = JsonXContent.contentBuilder();
60-
builder.startObject();
61-
Metadata.FORMAT.toXContent(builder, latestMetadata);
62-
builder.endObject();
63-
64-
XContentParserConfiguration parserConfig = hasMissingCustoms
65-
? parserConfig().withRegistry(ElasticsearchNodeCommand.namedXContentRegistry)
66-
: parserConfig();
67-
Metadata loadedMetadata;
68-
try (XContentParser parser = createParser(parserConfig, JsonXContent.jsonXContent, BytesReference.bytes(builder))) {
69-
loadedMetadata = Metadata.fromXContent(parser);
70-
}
71-
assertThat(loadedMetadata.clusterUUID(), not(equalTo("_na_")));
72-
assertThat(loadedMetadata.clusterUUID(), equalTo(latestMetadata.clusterUUID()));
73-
assertThat(loadedMetadata.getProject().dataStreams(), equalTo(latestMetadata.getProject().dataStreams()));
61+
private void runLoadStateTest(boolean hasMissingCustoms) throws IOException {
62+
final var dataPath = createTempDir();
63+
final Settings settings = Settings.builder()
64+
.putList(Environment.PATH_DATA_SETTING.getKey(), List.of(dataPath.toString()))
65+
.put(Environment.PATH_HOME_SETTING.getKey(), createTempDir().toAbsolutePath())
66+
.build();
7467

75-
// make sure the index tombstones are the same too
76-
if (hasMissingCustoms) {
68+
try (var nodeEnvironment = newNodeEnvironment(settings)) {
69+
final var persistedClusterStateServiceWithFullRegistry = new PersistedClusterStateService(
70+
nodeEnvironment,
71+
xContentRegistry(),
72+
new ClusterSettings(Settings.EMPTY, ClusterSettings.BUILT_IN_CLUSTER_SETTINGS),
73+
() -> 0L,
74+
() -> false
75+
);
76+
77+
// 1. Simulating persisting cluster state by a running node
78+
final long initialTerm = randomNonNegativeLong();
79+
final Metadata initialMetadata = randomMeta(hasMissingCustoms);
80+
final ClusterState initialState = ClusterState.builder(ClusterState.EMPTY_STATE).metadata(initialMetadata).build();
81+
try (var writer = persistedClusterStateServiceWithFullRegistry.createWriter()) {
82+
writer.writeFullStateAndCommit(initialTerm, initialState);
83+
}
84+
85+
// 2. Simulating loading the persisted cluster state by the CLI
86+
final var persistedClusterStateServiceForNodeCommand = ElasticsearchNodeCommand.createPersistedClusterStateService(
87+
Settings.EMPTY,
88+
new Path[] { dataPath }
89+
);
90+
final Tuple<Long, ClusterState> loadedTermAndClusterState = ElasticsearchNodeCommand.loadTermAndClusterState(
91+
persistedClusterStateServiceForNodeCommand,
92+
TestEnvironment.newEnvironment(settings)
93+
);
94+
95+
assertThat(loadedTermAndClusterState.v1(), equalTo(initialTerm));
96+
final var loadedMetadata = loadedTermAndClusterState.v2().metadata();
7797
assertNotNull(loadedMetadata.getProject().custom(IndexGraveyard.TYPE));
7898
assertThat(
7999
loadedMetadata.getProject().custom(IndexGraveyard.TYPE),
80100
instanceOf(ElasticsearchNodeCommand.UnknownProjectCustom.class)
81101
);
102+
if (hasMissingCustoms) {
103+
assertThat(
104+
loadedMetadata.getProject().custom(MissingProjectCustomMetadata.TYPE),
105+
instanceOf(ElasticsearchNodeCommand.UnknownProjectCustom.class)
106+
);
107+
assertThat(
108+
loadedMetadata.custom(MissingClusterCustomMetadata.TYPE),
109+
instanceOf(ElasticsearchNodeCommand.UnknownClusterCustom.class)
110+
);
111+
} else {
112+
assertNull(loadedMetadata.getProject().custom(MissingProjectCustomMetadata.TYPE));
113+
assertNull(loadedMetadata.custom(MissingClusterCustomMetadata.TYPE));
114+
}
82115

83-
if (preserveUnknownCustoms) {
84-
// check that we reserialize unknown metadata correctly again
85-
final Path tempdir = createTempDir();
86-
Metadata.FORMAT.write(loadedMetadata, tempdir);
87-
final Metadata reloadedMetadata = Metadata.FORMAT.loadLatestState(logger, xContentRegistry(), tempdir);
88-
assertThat(reloadedMetadata.getProject().indexGraveyard(), equalTo(latestMetadata.getProject().indexGraveyard()));
116+
final long newTerm = initialTerm + 1;
117+
try (var writer = persistedClusterStateServiceForNodeCommand.createWriter()) {
118+
writer.writeFullStateAndCommit(newTerm, ClusterState.builder(ClusterState.EMPTY_STATE).metadata(loadedMetadata).build());
119+
}
120+
121+
// 3. Simulate node restart after updating on-disk state with the CLI tool
122+
final var bestOnDiskState = persistedClusterStateServiceWithFullRegistry.loadBestOnDiskState();
123+
assertThat(bestOnDiskState.currentTerm, equalTo(newTerm));
124+
final Metadata reloadedMetadata = bestOnDiskState.metadata;
125+
assertThat(reloadedMetadata.getProject().indexGraveyard(), equalTo(initialMetadata.getProject().indexGraveyard()));
126+
if (hasMissingCustoms) {
127+
assertThat(
128+
reloadedMetadata.getProject().custom(MissingProjectCustomMetadata.TYPE),
129+
equalTo(initialMetadata.getProject().custom(MissingProjectCustomMetadata.TYPE))
130+
);
131+
} else {
132+
assertNull(reloadedMetadata.getProject().custom(MissingProjectCustomMetadata.TYPE));
89133
}
90-
} else {
91-
assertThat(loadedMetadata.getProject().indexGraveyard(), equalTo(latestMetadata.getProject().indexGraveyard()));
92134
}
93135
}
94136

95-
private Metadata randomMeta() {
137+
private Metadata randomMeta(boolean hasMissingCustoms) {
96138
Metadata.Builder mdBuilder = Metadata.builder();
97139
mdBuilder.generateClusterUuidIfNeeded();
98140
int numDelIndices = randomIntBetween(0, 5);
@@ -109,15 +151,86 @@ private Metadata randomMeta() {
109151
}
110152
}
111153
mdBuilder.indexGraveyard(graveyard.build());
154+
if (hasMissingCustoms) {
155+
mdBuilder.putCustom(
156+
MissingProjectCustomMetadata.TYPE,
157+
new MissingProjectCustomMetadata("test missing project custom metadata")
158+
);
159+
mdBuilder.putCustom(
160+
MissingClusterCustomMetadata.TYPE,
161+
new MissingClusterCustomMetadata("test missing cluster custom metadata")
162+
);
163+
}
112164
return mdBuilder.build();
113165
}
114166

115167
@Override
116168
protected NamedXContentRegistry xContentRegistry() {
117169
return new NamedXContentRegistry(
118-
Stream.of(ClusterModule.getNamedXWriteables().stream(), IndicesModule.getNamedXContents().stream())
119-
.flatMap(Function.identity())
120-
.toList()
170+
Stream.of(
171+
ClusterModule.getNamedXWriteables().stream(),
172+
IndicesModule.getNamedXContents().stream(),
173+
Stream.of(
174+
new NamedXContentRegistry.Entry(
175+
Metadata.ProjectCustom.class,
176+
new ParseField(MissingProjectCustomMetadata.TYPE),
177+
parser -> MissingProjectCustomMetadata.fromXContent(MissingProjectCustomMetadata::new, parser)
178+
),
179+
new NamedXContentRegistry.Entry(
180+
Metadata.ClusterCustom.class,
181+
new ParseField(MissingClusterCustomMetadata.TYPE),
182+
parser -> MissingClusterCustomMetadata.fromXContent(MissingClusterCustomMetadata::new, parser)
183+
)
184+
)
185+
).flatMap(Function.identity()).toList()
121186
);
122187
}
188+
189+
private static class MissingProjectCustomMetadata extends TestProjectCustomMetadata {
190+
191+
static final String TYPE = "missing_project_custom_metadata";
192+
193+
MissingProjectCustomMetadata(String data) {
194+
super(data);
195+
}
196+
197+
@Override
198+
public String getWriteableName() {
199+
return TYPE;
200+
}
201+
202+
@Override
203+
public TransportVersion getMinimalSupportedVersion() {
204+
return TransportVersion.current();
205+
}
206+
207+
@Override
208+
public EnumSet<Metadata.XContentContext> context() {
209+
return EnumSet.of(Metadata.XContentContext.GATEWAY);
210+
}
211+
}
212+
213+
private static class MissingClusterCustomMetadata extends TestClusterCustomMetadata {
214+
215+
static final String TYPE = "missing_cluster_custom_metadata";
216+
217+
MissingClusterCustomMetadata(String data) {
218+
super(data);
219+
}
220+
221+
@Override
222+
public String getWriteableName() {
223+
return TYPE;
224+
}
225+
226+
@Override
227+
public TransportVersion getMinimalSupportedVersion() {
228+
return TransportVersion.current();
229+
}
230+
231+
@Override
232+
public EnumSet<Metadata.XContentContext> context() {
233+
return EnumSet.of(Metadata.XContentContext.GATEWAY);
234+
}
235+
}
123236
}

0 commit comments

Comments
 (0)