Skip to content

Commit f2a232d

Browse files
authored
[7.x] Fix ExplainLifecycleIT.testExplainFilters (#74941) (#74970)
* Fix ExplainLifecycleIT.testExplainFilters (#74941) Explain `onlyErrors` is tricky to integration test as all the ILM steps are now retryable (so the `explain` API might not catch a particular failing step when it's in the `ERROR` state as when we retry we move it back into the step that failed). This adds an unit test for the "explain onlyErrors" case. (cherry picked from commit 973610a) Signed-off-by: Andrei Dan <[email protected]> * Remove unused import
1 parent 31f3ea3 commit f2a232d

File tree

3 files changed

+218
-55
lines changed

3 files changed

+218
-55
lines changed

x-pack/plugin/ilm/qa/multi-node/src/javaRestTest/java/org/elasticsearch/xpack/ilm/ExplainLifecycleIT.java

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,8 @@
1515
import org.elasticsearch.cluster.metadata.IndexMetadata;
1616
import org.elasticsearch.common.Strings;
1717
import org.elasticsearch.common.settings.Settings;
18-
import org.elasticsearch.core.TimeValue;
1918
import org.elasticsearch.common.xcontent.XContentBuilder;
19+
import org.elasticsearch.core.TimeValue;
2020
import org.elasticsearch.test.rest.ESRestTestCase;
2121
import org.elasticsearch.xpack.core.ilm.DeleteAction;
2222
import org.elasticsearch.xpack.core.ilm.LifecycleAction;
@@ -61,7 +61,6 @@ public void refreshIndex() {
6161
alias = "alias-" + randomAlphaOfLength(5);
6262
}
6363

64-
@AwaitsFix(bugUrl = "https://github.com/elastic/elasticsearch/issues/71010")
6564
public void testExplainFilters() throws Exception {
6665
String goodIndex = index + "-good-000001";
6766
String errorIndex = index + "-error";
@@ -115,7 +114,7 @@ public void testExplainFilters() throws Exception {
115114

116115
Map<String, Map<String, Object>> onlyErrorsResponse = explain(client(), index + "*", true, true);
117116
assertNotNull(onlyErrorsResponse);
118-
assertThat(onlyErrorsResponse, allOf(hasKey(errorIndex), hasKey(nonexistantPolicyIndex)));
117+
assertThat(onlyErrorsResponse, hasKey(nonexistantPolicyIndex));
119118
assertThat(onlyErrorsResponse, allOf(not(hasKey(goodIndex)), not(hasKey(unmanagedIndex))));
120119
});
121120
}

x-pack/plugin/ilm/src/main/java/org/elasticsearch/xpack/ilm/action/TransportExplainLifecycleAction.java

Lines changed: 66 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
import org.elasticsearch.common.xcontent.NamedXContentRegistry;
2424
import org.elasticsearch.common.xcontent.XContentParser;
2525
import org.elasticsearch.common.xcontent.json.JsonXContent;
26+
import org.elasticsearch.core.Nullable;
2627
import org.elasticsearch.tasks.Task;
2728
import org.elasticsearch.threadpool.ThreadPool;
2829
import org.elasticsearch.transport.TransportService;
@@ -52,8 +53,8 @@ public class TransportExplainLifecycleAction
5253
public TransportExplainLifecycleAction(TransportService transportService, ClusterService clusterService, ThreadPool threadPool,
5354
ActionFilters actionFilters, IndexNameExpressionResolver indexNameExpressionResolver,
5455
NamedXContentRegistry xContentRegistry, IndexLifecycleService indexLifecycleService) {
55-
super(ExplainLifecycleAction.NAME, transportService, clusterService, threadPool, actionFilters,
56-
ExplainLifecycleRequest::new, indexNameExpressionResolver, ExplainLifecycleResponse::new);
56+
super(ExplainLifecycleAction.NAME, transportService, clusterService, threadPool, actionFilters, ExplainLifecycleRequest::new,
57+
indexNameExpressionResolver, ExplainLifecycleResponse::new);
5758
this.xContentRegistry = xContentRegistry;
5859
this.indexLifecycleService = indexLifecycleService;
5960
}
@@ -64,57 +65,13 @@ protected void doMasterOperation(Task task, ExplainLifecycleRequest request, Str
6465
Map<String, IndexLifecycleExplainResponse> indexResponses = new HashMap<>();
6566
for (String index : concreteIndices) {
6667
IndexMetadata idxMetadata = state.metadata().index(index);
67-
Settings idxSettings = idxMetadata.getSettings();
68-
LifecycleExecutionState lifecycleState = LifecycleExecutionState.fromIndexMetadata(idxMetadata);
69-
String policyName = LifecycleSettings.LIFECYCLE_NAME_SETTING.get(idxSettings);
70-
String currentPhase = lifecycleState.getPhase();
71-
String stepInfo = lifecycleState.getStepInfo();
72-
BytesArray stepInfoBytes = null;
73-
if (stepInfo != null) {
74-
stepInfoBytes = new BytesArray(stepInfo);
75-
}
76-
// parse existing phase steps from the phase definition in the index settings
77-
String phaseDef = lifecycleState.getPhaseDefinition();
78-
PhaseExecutionInfo phaseExecutionInfo = null;
79-
if (Strings.isNullOrEmpty(phaseDef) == false) {
80-
try (XContentParser parser = JsonXContent.jsonXContent.createParser(xContentRegistry,
81-
DeprecationHandler.THROW_UNSUPPORTED_OPERATION, phaseDef)) {
82-
phaseExecutionInfo = PhaseExecutionInfo.parse(parser, currentPhase);
83-
} catch (IOException e) {
84-
listener.onFailure(new ElasticsearchParseException(
85-
"failed to parse phase definition for index [" + index + "]", e));
86-
return;
87-
}
88-
}
8968
final IndexLifecycleExplainResponse indexResponse;
90-
if (Strings.hasLength(policyName)) {
91-
// If this is requesting only errors, only include indices in the error step or which are using a nonexistent policy
92-
if (request.onlyErrors() == false
93-
|| (ErrorStep.NAME.equals(lifecycleState.getStep()) || indexLifecycleService.policyExists(policyName) == false)) {
94-
Long originationDate = idxSettings.getAsLong(LIFECYCLE_ORIGINATION_DATE, -1L);
95-
indexResponse = IndexLifecycleExplainResponse.newManagedIndexResponse(index, policyName,
96-
originationDate != -1L ? originationDate : lifecycleState.getLifecycleDate(),
97-
lifecycleState.getPhase(),
98-
lifecycleState.getAction(),
99-
lifecycleState.getStep(),
100-
lifecycleState.getFailedStep(),
101-
lifecycleState.isAutoRetryableError(),
102-
lifecycleState.getFailedStepRetryCount(),
103-
lifecycleState.getPhaseTime(),
104-
lifecycleState.getActionTime(),
105-
lifecycleState.getStepTime(),
106-
lifecycleState.getSnapshotRepository(),
107-
lifecycleState.getSnapshotName(),
108-
lifecycleState.getShrinkIndexName(),
109-
stepInfoBytes,
110-
phaseExecutionInfo);
111-
} else {
112-
indexResponse = null;
113-
}
114-
} else if (request.onlyManaged() == false && request.onlyErrors() == false) {
115-
indexResponse = IndexLifecycleExplainResponse.newUnmanagedIndexResponse(index);
116-
} else {
117-
indexResponse = null;
69+
try {
70+
indexResponse = getIndexLifecycleExplainResponse(idxMetadata, request.onlyErrors(), request.onlyManaged(),
71+
indexLifecycleService, xContentRegistry);
72+
} catch (IOException e) {
73+
listener.onFailure(new ElasticsearchParseException("failed to parse phase definition for index [" + index + "]", e));
74+
return;
11875
}
11976

12077
if (indexResponse != null) {
@@ -124,4 +81,61 @@ protected void doMasterOperation(Task task, ExplainLifecycleRequest request, Str
12481
listener.onResponse(new ExplainLifecycleResponse(indexResponses));
12582
}
12683

84+
@Nullable
85+
static IndexLifecycleExplainResponse getIndexLifecycleExplainResponse(IndexMetadata indexMetadata, boolean onlyErrors,
86+
boolean onlyManaged, IndexLifecycleService indexLifecycleService,
87+
NamedXContentRegistry xContentRegistry) throws IOException {
88+
Settings idxSettings = indexMetadata.getSettings();
89+
LifecycleExecutionState lifecycleState = LifecycleExecutionState.fromIndexMetadata(indexMetadata);
90+
String policyName = LifecycleSettings.LIFECYCLE_NAME_SETTING.get(idxSettings);
91+
String currentPhase = lifecycleState.getPhase();
92+
String stepInfo = lifecycleState.getStepInfo();
93+
BytesArray stepInfoBytes = null;
94+
if (stepInfo != null) {
95+
stepInfoBytes = new BytesArray(stepInfo);
96+
}
97+
String indexName = indexMetadata.getIndex().getName();
98+
99+
// parse existing phase steps from the phase definition in the index settings
100+
String phaseDef = lifecycleState.getPhaseDefinition();
101+
PhaseExecutionInfo phaseExecutionInfo = null;
102+
if (Strings.isNullOrEmpty(phaseDef) == false) {
103+
try (XContentParser parser = JsonXContent.jsonXContent.createParser(xContentRegistry,
104+
DeprecationHandler.THROW_UNSUPPORTED_OPERATION, phaseDef)) {
105+
phaseExecutionInfo = PhaseExecutionInfo.parse(parser, currentPhase);
106+
}
107+
}
108+
109+
final IndexLifecycleExplainResponse indexResponse;
110+
if (Strings.hasLength(policyName)) {
111+
// If this is requesting only errors, only include indices in the error step or which are using a nonexistent policy
112+
if (onlyErrors == false
113+
|| (ErrorStep.NAME.equals(lifecycleState.getStep()) || indexLifecycleService.policyExists(policyName) == false)) {
114+
Long originationDate = idxSettings.getAsLong(LIFECYCLE_ORIGINATION_DATE, -1L);
115+
indexResponse = IndexLifecycleExplainResponse.newManagedIndexResponse(indexName, policyName,
116+
originationDate != -1L ? originationDate : lifecycleState.getLifecycleDate(),
117+
lifecycleState.getPhase(),
118+
lifecycleState.getAction(),
119+
lifecycleState.getStep(),
120+
lifecycleState.getFailedStep(),
121+
lifecycleState.isAutoRetryableError(),
122+
lifecycleState.getFailedStepRetryCount(),
123+
lifecycleState.getPhaseTime(),
124+
lifecycleState.getActionTime(),
125+
lifecycleState.getStepTime(),
126+
lifecycleState.getSnapshotRepository(),
127+
lifecycleState.getSnapshotName(),
128+
lifecycleState.getShrinkIndexName(),
129+
stepInfoBytes,
130+
phaseExecutionInfo);
131+
} else {
132+
indexResponse = null;
133+
}
134+
} else if (onlyManaged == false && onlyErrors == false) {
135+
indexResponse = IndexLifecycleExplainResponse.newUnmanagedIndexResponse(indexName);
136+
} else {
137+
indexResponse = null;
138+
}
139+
return indexResponse;
140+
}
127141
}
Lines changed: 150 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,150 @@
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.ilm.action;
9+
10+
import org.elasticsearch.Version;
11+
import org.elasticsearch.cluster.metadata.IndexMetadata;
12+
import org.elasticsearch.common.xcontent.NamedXContentRegistry;
13+
import org.elasticsearch.common.xcontent.ParseField;
14+
import org.elasticsearch.test.ESTestCase;
15+
import org.elasticsearch.xpack.core.ilm.ErrorStep;
16+
import org.elasticsearch.xpack.core.ilm.IndexLifecycleExplainResponse;
17+
import org.elasticsearch.xpack.core.ilm.LifecycleAction;
18+
import org.elasticsearch.xpack.core.ilm.LifecycleExecutionState;
19+
import org.elasticsearch.xpack.core.ilm.LifecycleSettings;
20+
import org.elasticsearch.xpack.core.ilm.RolloverAction;
21+
import org.elasticsearch.xpack.core.ilm.WaitForRolloverReadyStep;
22+
import org.elasticsearch.xpack.ilm.IndexLifecycleService;
23+
24+
import java.io.IOException;
25+
26+
import static org.elasticsearch.xpack.core.ilm.LifecycleExecutionState.ILM_CUSTOM_METADATA_KEY;
27+
import static org.elasticsearch.xpack.ilm.action.TransportExplainLifecycleAction.getIndexLifecycleExplainResponse;
28+
import static org.hamcrest.Matchers.is;
29+
import static org.hamcrest.Matchers.notNullValue;
30+
import static org.hamcrest.Matchers.nullValue;
31+
import static org.mockito.Matchers.anyString;
32+
import static org.mockito.Mockito.mock;
33+
import static org.mockito.Mockito.when;
34+
35+
public class TransportExplainLifecycleActionTests extends ESTestCase {
36+
37+
public static final String PHASE_DEFINITION = "{\n" +
38+
" \"policy\" : \"my-policy\",\n" +
39+
" \"phase_definition\" : {\n" +
40+
" \"min_age\" : \"20m\",\n" +
41+
" \"actions\" : {\n" +
42+
" \"rollover\" : {\n" +
43+
" \"max_age\" : \"5s\"\n" +
44+
" }\n" +
45+
" }\n" +
46+
" },\n" +
47+
" \"version\" : 1,\n" +
48+
" \"modified_date_in_millis\" : 1578521007076\n" +
49+
" }";
50+
51+
private static final NamedXContentRegistry REGISTRY;
52+
53+
static {
54+
REGISTRY = new NamedXContentRegistry(
55+
org.elasticsearch.core.List.of(new NamedXContentRegistry.Entry(LifecycleAction.class, new ParseField(RolloverAction.NAME),
56+
RolloverAction::parse))
57+
);
58+
}
59+
60+
public void testGetIndexLifecycleExplainResponse() throws IOException {
61+
{
62+
// only errors index
63+
IndexLifecycleService indexLifecycleService = mock(IndexLifecycleService.class);
64+
when(indexLifecycleService.policyExists("my-policy")).thenReturn(true);
65+
66+
LifecycleExecutionState.Builder errorStepState = LifecycleExecutionState.builder()
67+
.setPhase("hot")
68+
.setAction("rollover")
69+
.setStep(ErrorStep.NAME)
70+
.setPhaseDefinition(PHASE_DEFINITION);
71+
String indexInErrorStep = "index_in_error";
72+
IndexMetadata meta = IndexMetadata.builder(indexInErrorStep)
73+
.settings(settings(Version.CURRENT).put(LifecycleSettings.LIFECYCLE_NAME, "my-policy"))
74+
.numberOfShards(randomIntBetween(1, 5))
75+
.numberOfReplicas(randomIntBetween(0, 5))
76+
.putCustom(ILM_CUSTOM_METADATA_KEY, errorStepState.build().asMap())
77+
.build();
78+
79+
IndexLifecycleExplainResponse onlyErrorsResponse = getIndexLifecycleExplainResponse(meta, true, true, indexLifecycleService,
80+
REGISTRY);
81+
assertThat(onlyErrorsResponse, notNullValue());
82+
assertThat(onlyErrorsResponse.getIndex(), is(indexInErrorStep));
83+
assertThat(onlyErrorsResponse.getStep(), is(ErrorStep.NAME));
84+
}
85+
86+
{
87+
// only managed index
88+
IndexLifecycleService indexLifecycleService = mock(IndexLifecycleService.class);
89+
when(indexLifecycleService.policyExists("my-policy")).thenReturn(true);
90+
91+
LifecycleExecutionState.Builder checkRolloverReadyStepState = LifecycleExecutionState.builder()
92+
.setPhase("hot")
93+
.setAction("rollover")
94+
.setStep(WaitForRolloverReadyStep.NAME)
95+
.setPhaseDefinition(PHASE_DEFINITION);
96+
97+
String indexInCheckRolloverStep = "index_in_check_rollover";
98+
IndexMetadata meta = IndexMetadata.builder(indexInCheckRolloverStep)
99+
.settings(settings(Version.CURRENT).put(LifecycleSettings.LIFECYCLE_NAME, "my-policy"))
100+
.numberOfShards(randomIntBetween(1, 5))
101+
.numberOfReplicas(randomIntBetween(0, 5))
102+
.putCustom(ILM_CUSTOM_METADATA_KEY, checkRolloverReadyStepState.build().asMap())
103+
.build();
104+
105+
IndexLifecycleExplainResponse onlyErrorsResponse = getIndexLifecycleExplainResponse(meta, true, true, indexLifecycleService,
106+
REGISTRY);
107+
assertThat(onlyErrorsResponse, nullValue());
108+
109+
IndexLifecycleExplainResponse allManagedResponse = getIndexLifecycleExplainResponse(meta, false, true, indexLifecycleService,
110+
REGISTRY);
111+
assertThat(allManagedResponse, notNullValue());
112+
assertThat(allManagedResponse.getIndex(), is(indexInCheckRolloverStep));
113+
assertThat(allManagedResponse.getStep(), is(WaitForRolloverReadyStep.NAME));
114+
}
115+
116+
{
117+
// index with missing policy
118+
IndexLifecycleService indexLifecycleService = mock(IndexLifecycleService.class);
119+
when(indexLifecycleService.policyExists("random-policy")).thenReturn(false);
120+
121+
String indexWithMissingPolicy = "index_with_missing_policy";
122+
IndexMetadata meta = IndexMetadata.builder(indexWithMissingPolicy)
123+
.settings(settings(Version.CURRENT).put(LifecycleSettings.LIFECYCLE_NAME, "random-policy"))
124+
.numberOfShards(randomIntBetween(1, 5))
125+
.numberOfReplicas(randomIntBetween(0, 5))
126+
.build();
127+
128+
IndexLifecycleExplainResponse onlyErrorsResponse = getIndexLifecycleExplainResponse(meta, true, true, indexLifecycleService,
129+
REGISTRY);
130+
assertThat(onlyErrorsResponse, notNullValue());
131+
assertThat(onlyErrorsResponse.getPolicyName(), is("random-policy"));
132+
}
133+
134+
{
135+
// not managed index
136+
IndexLifecycleService indexLifecycleService = mock(IndexLifecycleService.class);
137+
when(indexLifecycleService.policyExists(anyString())).thenReturn(true);
138+
139+
IndexMetadata meta = IndexMetadata.builder("index")
140+
.settings(settings(Version.CURRENT))
141+
.numberOfShards(randomIntBetween(1, 5))
142+
.numberOfReplicas(randomIntBetween(0, 5))
143+
.build();
144+
145+
IndexLifecycleExplainResponse onlyManaged = getIndexLifecycleExplainResponse(meta, false, true, indexLifecycleService,
146+
REGISTRY);
147+
assertThat(onlyManaged, nullValue());
148+
}
149+
}
150+
}

0 commit comments

Comments
 (0)