add phase execution info to ILM Explain API (#33488)
adds a section for phase execution to the Explain API. This contains - phase definition - policy name - policy version - modified date
This commit is contained in:
parent
7ff11b4ae1
commit
94a66c556d
|
@ -19,18 +19,14 @@
|
|||
|
||||
package org.elasticsearch.client.indexlifecycle;
|
||||
|
||||
import org.elasticsearch.action.ActionResponse;
|
||||
import org.elasticsearch.common.ParseField;
|
||||
import org.elasticsearch.common.Strings;
|
||||
import org.elasticsearch.common.io.stream.StreamInput;
|
||||
import org.elasticsearch.common.io.stream.StreamOutput;
|
||||
import org.elasticsearch.common.xcontent.ConstructingObjectParser;
|
||||
import org.elasticsearch.common.xcontent.ToXContentObject;
|
||||
import org.elasticsearch.common.xcontent.XContentBuilder;
|
||||
import org.elasticsearch.common.xcontent.XContentParser;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
@ -43,9 +39,9 @@ import java.util.stream.Collectors;
|
|||
* Since the API can be run over multiple indices the response provides a map of
|
||||
* index to the explanation of the lifecycle status for that index.
|
||||
*/
|
||||
public class ExplainLifecycleResponse extends ActionResponse implements ToXContentObject {
|
||||
public class ExplainLifecycleResponse implements ToXContentObject {
|
||||
|
||||
public static final ParseField INDICES_FIELD = new ParseField("indices");
|
||||
private static final ParseField INDICES_FIELD = new ParseField("indices");
|
||||
|
||||
private Map<String, IndexLifecycleExplainResponse> indexResponses;
|
||||
|
||||
|
@ -62,9 +58,6 @@ public class ExplainLifecycleResponse extends ActionResponse implements ToXConte
|
|||
return PARSER.apply(parser, null);
|
||||
}
|
||||
|
||||
public ExplainLifecycleResponse() {
|
||||
}
|
||||
|
||||
public ExplainLifecycleResponse(Map<String, IndexLifecycleExplainResponse> indexResponses) {
|
||||
this.indexResponses = indexResponses;
|
||||
}
|
||||
|
@ -91,25 +84,6 @@ public class ExplainLifecycleResponse extends ActionResponse implements ToXConte
|
|||
return builder;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void readFrom(StreamInput in) throws IOException {
|
||||
int size = in.readVInt();
|
||||
Map<String, IndexLifecycleExplainResponse> indexResponses = new HashMap<>(size);
|
||||
for (int i = 0; i < size; i++) {
|
||||
IndexLifecycleExplainResponse indexResponse = new IndexLifecycleExplainResponse(in);
|
||||
indexResponses.put(indexResponse.getIndex(), indexResponse);
|
||||
}
|
||||
this.indexResponses = indexResponses;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeTo(StreamOutput out) throws IOException {
|
||||
out.writeVInt(indexResponses.size());
|
||||
for (IndexLifecycleExplainResponse e : indexResponses.values()) {
|
||||
e.writeTo(out);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(indexResponses);
|
||||
|
|
|
@ -23,9 +23,6 @@ import org.elasticsearch.common.ParseField;
|
|||
import org.elasticsearch.common.Strings;
|
||||
import org.elasticsearch.common.bytes.BytesArray;
|
||||
import org.elasticsearch.common.bytes.BytesReference;
|
||||
import org.elasticsearch.common.io.stream.StreamInput;
|
||||
import org.elasticsearch.common.io.stream.StreamOutput;
|
||||
import org.elasticsearch.common.io.stream.Writeable;
|
||||
import org.elasticsearch.common.xcontent.ConstructingObjectParser;
|
||||
import org.elasticsearch.common.xcontent.ToXContentObject;
|
||||
import org.elasticsearch.common.xcontent.XContentBuilder;
|
||||
|
@ -37,7 +34,7 @@ import org.joda.time.chrono.ISOChronology;
|
|||
import java.io.IOException;
|
||||
import java.util.Objects;
|
||||
|
||||
public class IndexLifecycleExplainResponse implements ToXContentObject, Writeable {
|
||||
public class IndexLifecycleExplainResponse implements ToXContentObject {
|
||||
|
||||
private static final ParseField INDEX_FIELD = new ParseField("index");
|
||||
private static final ParseField MANAGED_BY_ILM_FIELD = new ParseField("managed");
|
||||
|
@ -52,6 +49,7 @@ public class IndexLifecycleExplainResponse implements ToXContentObject, Writeabl
|
|||
private static final ParseField ACTION_TIME_FIELD = new ParseField("action_time");
|
||||
private static final ParseField STEP_TIME_FIELD = new ParseField("step_time");
|
||||
private static final ParseField STEP_INFO_FIELD = new ParseField("step_info");
|
||||
private static final ParseField PHASE_EXECUTION_INFO = new ParseField("phase_execution");
|
||||
|
||||
public static final ConstructingObjectParser<IndexLifecycleExplainResponse, Void> PARSER = new ConstructingObjectParser<>(
|
||||
"index_lifecycle_explain_response",
|
||||
|
@ -68,7 +66,8 @@ public class IndexLifecycleExplainResponse implements ToXContentObject, Writeabl
|
|||
(long) (a[9] == null ? -1L: a[9]),
|
||||
(long) (a[10] == null ? -1L: a[10]),
|
||||
(long) (a[11] == null ? -1L: a[11]),
|
||||
(BytesReference) a[12]));
|
||||
(BytesReference) a[12],
|
||||
(PhaseExecutionInfo) a[13]));
|
||||
static {
|
||||
PARSER.declareString(ConstructingObjectParser.constructorArg(), INDEX_FIELD);
|
||||
PARSER.declareBoolean(ConstructingObjectParser.constructorArg(), MANAGED_BY_ILM_FIELD);
|
||||
|
@ -87,6 +86,8 @@ public class IndexLifecycleExplainResponse implements ToXContentObject, Writeabl
|
|||
builder.copyCurrentStructure(p);
|
||||
return BytesArray.bytes(builder);
|
||||
}, STEP_INFO_FIELD);
|
||||
PARSER.declareObject(ConstructingObjectParser.optionalConstructorArg(), (p, c) -> PhaseExecutionInfo.parse(p, ""),
|
||||
PHASE_EXECUTION_INFO);
|
||||
}
|
||||
|
||||
private final String index;
|
||||
|
@ -102,28 +103,30 @@ public class IndexLifecycleExplainResponse implements ToXContentObject, Writeabl
|
|||
private final boolean skip;
|
||||
private final boolean managedByILM;
|
||||
private final BytesReference stepInfo;
|
||||
private final PhaseExecutionInfo phaseExecutionInfo;
|
||||
|
||||
public static IndexLifecycleExplainResponse newManagedIndexResponse(String index, String policyName, boolean skip, long lifecycleDate,
|
||||
String phase, String action, String step, String failedStep, long phaseTime, long actionTime, long stepTime,
|
||||
BytesReference stepInfo) {
|
||||
String phase, String action, String step, String failedStep,
|
||||
long phaseTime, long actionTime, long stepTime,
|
||||
BytesReference stepInfo, PhaseExecutionInfo phaseExecutionInfo) {
|
||||
return new IndexLifecycleExplainResponse(index, true, policyName, skip, lifecycleDate, phase, action, step, failedStep, phaseTime,
|
||||
actionTime, stepTime, stepInfo);
|
||||
actionTime, stepTime, stepInfo, phaseExecutionInfo);
|
||||
}
|
||||
|
||||
public static IndexLifecycleExplainResponse newUnmanagedIndexResponse(String index) {
|
||||
return new IndexLifecycleExplainResponse(index, false, null, false, -1L, null, null, null, null, -1L, -1L, -1L, null);
|
||||
return new IndexLifecycleExplainResponse(index, false, null, false, -1L, null, null, null, null, -1L, -1L, -1L, null, null);
|
||||
}
|
||||
|
||||
private IndexLifecycleExplainResponse(String index, boolean managedByILM, String policyName, boolean skip, long lifecycleDate,
|
||||
String phase, String action, String step, String failedStep, long phaseTime, long actionTime, long stepTime,
|
||||
BytesReference stepInfo) {
|
||||
String phase, String action, String step, String failedStep, long phaseTime, long actionTime,
|
||||
long stepTime, BytesReference stepInfo, PhaseExecutionInfo phaseExecutionInfo) {
|
||||
if (managedByILM) {
|
||||
if (policyName == null) {
|
||||
throw new IllegalArgumentException("[" + POLICY_NAME_FIELD.getPreferredName() + "] cannot be null for managed index");
|
||||
}
|
||||
} else {
|
||||
if (policyName != null || lifecycleDate >= 0 || phase != null || action != null || step != null || failedStep != null
|
||||
|| phaseTime >= 0 || actionTime >= 0 || stepTime >= 0 || stepInfo != null) {
|
||||
|| phaseTime >= 0 || actionTime >= 0 || stepTime >= 0 || stepInfo != null || phaseExecutionInfo != null) {
|
||||
throw new IllegalArgumentException(
|
||||
"Unmanaged index response must only contain fields: [" + MANAGED_BY_ILM_FIELD + ", " + INDEX_FIELD + "]");
|
||||
}
|
||||
|
@ -141,56 +144,7 @@ public class IndexLifecycleExplainResponse implements ToXContentObject, Writeabl
|
|||
this.stepTime = stepTime;
|
||||
this.failedStep = failedStep;
|
||||
this.stepInfo = stepInfo;
|
||||
}
|
||||
|
||||
public IndexLifecycleExplainResponse(StreamInput in) throws IOException {
|
||||
index = in.readString();
|
||||
managedByILM = in.readBoolean();
|
||||
if (managedByILM) {
|
||||
policyName = in.readString();
|
||||
skip = in.readBoolean();
|
||||
lifecycleDate = in.readZLong();
|
||||
phase = in.readString();
|
||||
action = in.readString();
|
||||
step = in.readString();
|
||||
failedStep = in.readOptionalString();
|
||||
phaseTime = in.readZLong();
|
||||
actionTime = in.readZLong();
|
||||
stepTime = in.readZLong();
|
||||
stepInfo = in.readOptionalBytesReference();
|
||||
|
||||
} else {
|
||||
policyName = null;
|
||||
skip = false;
|
||||
lifecycleDate = -1L;
|
||||
phase = null;
|
||||
action = null;
|
||||
step = null;
|
||||
failedStep = null;
|
||||
phaseTime = -1L;
|
||||
actionTime = -1L;
|
||||
stepTime = -1L;
|
||||
stepInfo = null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeTo(StreamOutput out) throws IOException {
|
||||
out.writeString(index);
|
||||
out.writeBoolean(managedByILM);
|
||||
if (managedByILM) {
|
||||
out.writeString(policyName);
|
||||
out.writeBoolean(skip);
|
||||
out.writeZLong(lifecycleDate);
|
||||
out.writeString(phase);
|
||||
out.writeString(action);
|
||||
out.writeString(step);
|
||||
out.writeOptionalString(failedStep);
|
||||
out.writeZLong(phaseTime);
|
||||
out.writeZLong(actionTime);
|
||||
out.writeZLong(stepTime);
|
||||
out.writeOptionalBytesReference(stepInfo);
|
||||
}
|
||||
this.phaseExecutionInfo = phaseExecutionInfo;
|
||||
}
|
||||
|
||||
public String getIndex() {
|
||||
|
@ -245,6 +199,10 @@ public class IndexLifecycleExplainResponse implements ToXContentObject, Writeabl
|
|||
return stepInfo;
|
||||
}
|
||||
|
||||
public PhaseExecutionInfo getPhaseExecutionInfo() {
|
||||
return phaseExecutionInfo;
|
||||
}
|
||||
|
||||
@Override
|
||||
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
|
||||
builder.startObject();
|
||||
|
@ -282,6 +240,9 @@ public class IndexLifecycleExplainResponse implements ToXContentObject, Writeabl
|
|||
if (stepInfo != null && stepInfo.length() > 0) {
|
||||
builder.rawField(STEP_INFO_FIELD.getPreferredName(), stepInfo.streamInput(), XContentType.JSON);
|
||||
}
|
||||
if (phaseExecutionInfo != null) {
|
||||
builder.field(PHASE_EXECUTION_INFO.getPreferredName(), phaseExecutionInfo);
|
||||
}
|
||||
}
|
||||
builder.endObject();
|
||||
return builder;
|
||||
|
@ -290,7 +251,7 @@ public class IndexLifecycleExplainResponse implements ToXContentObject, Writeabl
|
|||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(index, managedByILM, policyName, skip, lifecycleDate, phase, action, step, failedStep, phaseTime, actionTime,
|
||||
stepTime, stepInfo);
|
||||
stepTime, stepInfo, phaseExecutionInfo);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -314,7 +275,8 @@ public class IndexLifecycleExplainResponse implements ToXContentObject, Writeabl
|
|||
Objects.equals(phaseTime, other.phaseTime) &&
|
||||
Objects.equals(actionTime, other.actionTime) &&
|
||||
Objects.equals(stepTime, other.stepTime) &&
|
||||
Objects.equals(stepInfo, other.stepInfo);
|
||||
Objects.equals(stepInfo, other.stepInfo) &&
|
||||
Objects.equals(phaseExecutionInfo, other.phaseExecutionInfo);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -323,3 +285,4 @@ public class IndexLifecycleExplainResponse implements ToXContentObject, Writeabl
|
|||
}
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,130 @@
|
|||
/*
|
||||
* Licensed to Elasticsearch under one or more contributor
|
||||
* license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright
|
||||
* ownership. Elasticsearch licenses this file to you under
|
||||
* the Apache License, Version 2.0 (the "License"); you may
|
||||
* not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package org.elasticsearch.client.indexlifecycle;
|
||||
|
||||
import org.elasticsearch.common.ParseField;
|
||||
import org.elasticsearch.common.Strings;
|
||||
import org.elasticsearch.common.xcontent.ConstructingObjectParser;
|
||||
import org.elasticsearch.common.xcontent.ToXContentObject;
|
||||
import org.elasticsearch.common.xcontent.XContentBuilder;
|
||||
import org.elasticsearch.common.xcontent.XContentParser;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* This class contains information about the current phase being executed by Index
|
||||
* Lifecycle Management on the specific index.
|
||||
*/
|
||||
public class PhaseExecutionInfo implements ToXContentObject {
|
||||
private static final ParseField POLICY_NAME_FIELD = new ParseField("policy");
|
||||
private static final ParseField PHASE_DEFINITION_FIELD = new ParseField("phase_definition");
|
||||
private static final ParseField VERSION_FIELD = new ParseField("version");
|
||||
private static final ParseField MODIFIED_DATE_IN_MILLIS_FIELD = new ParseField("modified_date_in_millis");
|
||||
|
||||
private static final ConstructingObjectParser<PhaseExecutionInfo, String> PARSER = new ConstructingObjectParser<>(
|
||||
"phase_execution_info", false,
|
||||
(a, name) -> new PhaseExecutionInfo((String) a[0], (Phase) a[1], (long) a[2], (long) a[3]));
|
||||
static {
|
||||
PARSER.declareString(ConstructingObjectParser.constructorArg(), POLICY_NAME_FIELD);
|
||||
PARSER.declareObject(ConstructingObjectParser.optionalConstructorArg(), Phase::parse, PHASE_DEFINITION_FIELD);
|
||||
PARSER.declareLong(ConstructingObjectParser.constructorArg(), VERSION_FIELD);
|
||||
PARSER.declareLong(ConstructingObjectParser.constructorArg(), MODIFIED_DATE_IN_MILLIS_FIELD);
|
||||
}
|
||||
|
||||
public static PhaseExecutionInfo parse(XContentParser parser, String name) {
|
||||
return PARSER.apply(parser, name);
|
||||
}
|
||||
|
||||
private final String policyName;
|
||||
private final Phase phase;
|
||||
private final long version;
|
||||
private final long modifiedDate;
|
||||
|
||||
/**
|
||||
* This class holds information about the current phase that is being executed
|
||||
*
|
||||
* @param policyName the name of the policy being executed, this may not be the current policy assigned to an index
|
||||
* @param phase the current phase definition executed
|
||||
* @param version the version of the <code>policyName</code> being executed
|
||||
* @param modifiedDate the time the executing version of the phase was modified
|
||||
*/
|
||||
public PhaseExecutionInfo(String policyName, Phase phase, long version, long modifiedDate) {
|
||||
this.policyName = policyName;
|
||||
this.phase = phase;
|
||||
this.version = version;
|
||||
this.modifiedDate = modifiedDate;
|
||||
}
|
||||
|
||||
public String getPolicyName() {
|
||||
return policyName;
|
||||
}
|
||||
|
||||
public Phase getPhase() {
|
||||
return phase;
|
||||
}
|
||||
|
||||
public long getVersion() {
|
||||
return version;
|
||||
}
|
||||
|
||||
public long getModifiedDate() {
|
||||
return modifiedDate;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(policyName, phase, version, modifiedDate);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (obj == null) {
|
||||
return false;
|
||||
}
|
||||
if (getClass() != obj.getClass()) {
|
||||
return false;
|
||||
}
|
||||
PhaseExecutionInfo other = (PhaseExecutionInfo) obj;
|
||||
return Objects.equals(policyName, other.policyName) &&
|
||||
Objects.equals(phase, other.phase) &&
|
||||
Objects.equals(version, other.version) &&
|
||||
Objects.equals(modifiedDate, other.modifiedDate);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return Strings.toString(this, false, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
|
||||
builder.startObject();
|
||||
builder.field(POLICY_NAME_FIELD.getPreferredName(), policyName);
|
||||
if (phase != null) {
|
||||
builder.field(PHASE_DEFINITION_FIELD.getPreferredName(), phase);
|
||||
}
|
||||
builder.field(VERSION_FIELD.getPreferredName(), version);
|
||||
builder.timeField(MODIFIED_DATE_IN_MILLIS_FIELD.getPreferredName(), "modified_date", modifiedDate);
|
||||
builder.endObject();
|
||||
return builder;
|
||||
}
|
||||
}
|
||||
|
|
@ -36,6 +36,7 @@ import org.elasticsearch.client.indexlifecycle.LifecyclePolicy;
|
|||
import org.elasticsearch.client.indexlifecycle.LifecyclePolicyMetadata;
|
||||
import org.elasticsearch.client.indexlifecycle.OperationMode;
|
||||
import org.elasticsearch.client.indexlifecycle.Phase;
|
||||
import org.elasticsearch.client.indexlifecycle.PhaseExecutionInfo;
|
||||
import org.elasticsearch.client.indexlifecycle.PutLifecyclePolicyRequest;
|
||||
import org.elasticsearch.client.indexlifecycle.RolloverAction;
|
||||
import org.elasticsearch.client.indexlifecycle.ShrinkAction;
|
||||
|
@ -132,7 +133,8 @@ public class IndexLifecycleIT extends ESRestHighLevelClientTestCase {
|
|||
Map<String, LifecycleAction> hotActions = Collections.singletonMap(
|
||||
RolloverAction.NAME,
|
||||
new RolloverAction(null, TimeValue.timeValueHours(50 * 24), null));
|
||||
lifecyclePhases.put("hot", new Phase("hot", randomFrom(TimeValue.ZERO, null), hotActions));
|
||||
Phase hotPhase = new Phase("hot", randomFrom(TimeValue.ZERO, null), hotActions);
|
||||
lifecyclePhases.put("hot", hotPhase);
|
||||
|
||||
Map<String, LifecycleAction> warmActions = new HashMap<>();
|
||||
warmActions.put(AllocateAction.NAME, new AllocateAction(null, null, null, Collections.singletonMap("_name", "node-1")));
|
||||
|
@ -152,6 +154,11 @@ public class IndexLifecycleIT extends ESRestHighLevelClientTestCase {
|
|||
AcknowledgedResponse putResponse = execute(putRequest, highLevelClient().indexLifecycle()::putLifecyclePolicy,
|
||||
highLevelClient().indexLifecycle()::putLifecyclePolicyAsync);
|
||||
assertTrue(putResponse.isAcknowledged());
|
||||
GetLifecyclePolicyRequest getRequest = new GetLifecyclePolicyRequest(policy.getName());
|
||||
GetLifecyclePolicyResponse getResponse = execute(getRequest, highLevelClient().indexLifecycle()::getLifecyclePolicy,
|
||||
highLevelClient().indexLifecycle()::getLifecyclePolicyAsync);
|
||||
long expectedPolicyModifiedDate = getResponse.getPolicies().get(policy.getName()).getModifiedDate();
|
||||
|
||||
|
||||
createIndex("foo-01", Settings.builder().put("index.lifecycle.name", policy.getName())
|
||||
.put("index.lifecycle.rollover_alias", "foo-alias").build(), "", "\"foo-alias\" : {}");
|
||||
|
@ -182,6 +189,8 @@ public class IndexLifecycleIT extends ESRestHighLevelClientTestCase {
|
|||
assertEquals("hot", fooResponse.getPhase());
|
||||
assertEquals("rollover", fooResponse.getAction());
|
||||
assertEquals("attempt_rollover", fooResponse.getStep());
|
||||
assertEquals(new PhaseExecutionInfo(policy.getName(), new Phase("", hotPhase.getMinimumAge(), hotPhase.getActions()),
|
||||
1L, expectedPolicyModifiedDate), fooResponse.getPhaseExecutionInfo());
|
||||
IndexLifecycleExplainResponse bazResponse = indexResponses.get("baz-01");
|
||||
assertNotNull(bazResponse);
|
||||
assertTrue(bazResponse.managedByILM());
|
||||
|
|
|
@ -18,14 +18,19 @@
|
|||
*/
|
||||
package org.elasticsearch.client.indexlifecycle;
|
||||
|
||||
import org.elasticsearch.cluster.ClusterModule;
|
||||
import org.elasticsearch.common.ParseField;
|
||||
import org.elasticsearch.common.xcontent.NamedXContentRegistry;
|
||||
import org.elasticsearch.common.xcontent.XContentParser;
|
||||
import org.elasticsearch.test.AbstractStreamableXContentTestCase;
|
||||
import org.elasticsearch.test.AbstractXContentTestCase;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class ExplainLifecycleResponseTests extends AbstractStreamableXContentTestCase<ExplainLifecycleResponse> {
|
||||
public class ExplainLifecycleResponseTests extends AbstractXContentTestCase<ExplainLifecycleResponse> {
|
||||
|
||||
@Override
|
||||
protected ExplainLifecycleResponse createTestInstance() {
|
||||
|
@ -37,19 +42,6 @@ public class ExplainLifecycleResponseTests extends AbstractStreamableXContentTes
|
|||
return new ExplainLifecycleResponse(indexResponses);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ExplainLifecycleResponse createBlankInstance() {
|
||||
return new ExplainLifecycleResponse();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ExplainLifecycleResponse mutateInstance(ExplainLifecycleResponse response) {
|
||||
Map<String, IndexLifecycleExplainResponse> indexResponses = new HashMap<>(response.getIndexResponses());
|
||||
IndexLifecycleExplainResponse indexResponse = IndexExplainResponseTests.randomIndexExplainResponse();
|
||||
indexResponses.put(indexResponse.getIndex(), indexResponse);
|
||||
return new ExplainLifecycleResponse(indexResponses);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ExplainLifecycleResponse doParseInstance(XContentParser parser) throws IOException {
|
||||
return ExplainLifecycleResponse.fromXContent(parser);
|
||||
|
@ -59,4 +51,11 @@ public class ExplainLifecycleResponseTests extends AbstractStreamableXContentTes
|
|||
protected boolean supportsUnknownFields() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected NamedXContentRegistry xContentRegistry() {
|
||||
List<NamedXContentRegistry.Entry> entries = new ArrayList<>(ClusterModule.getNamedXWriteables());
|
||||
entries.add(new NamedXContentRegistry.Entry(LifecycleAction.class, new ParseField(DeleteAction.NAME), DeleteAction::parse));
|
||||
return new NamedXContentRegistry(entries);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,20 +19,23 @@
|
|||
|
||||
package org.elasticsearch.client.indexlifecycle;
|
||||
|
||||
import org.elasticsearch.cluster.ClusterModule;
|
||||
import org.elasticsearch.common.ParseField;
|
||||
import org.elasticsearch.common.Strings;
|
||||
import org.elasticsearch.common.bytes.BytesArray;
|
||||
import org.elasticsearch.common.bytes.BytesReference;
|
||||
import org.elasticsearch.common.io.stream.Writeable.Reader;
|
||||
import org.elasticsearch.common.xcontent.NamedXContentRegistry;
|
||||
import org.elasticsearch.common.xcontent.ToXContentObject;
|
||||
import org.elasticsearch.common.xcontent.XContentBuilder;
|
||||
import org.elasticsearch.common.xcontent.XContentParser;
|
||||
import org.elasticsearch.test.AbstractSerializingTestCase;
|
||||
import org.elasticsearch.test.AbstractXContentTestCase;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
public class IndexExplainResponseTests extends AbstractSerializingTestCase<IndexLifecycleExplainResponse> {
|
||||
public class IndexExplainResponseTests extends AbstractXContentTestCase<IndexLifecycleExplainResponse> {
|
||||
|
||||
static IndexLifecycleExplainResponse randomIndexExplainResponse() {
|
||||
if (frequently()) {
|
||||
|
@ -50,7 +53,8 @@ public class IndexExplainResponseTests extends AbstractSerializingTestCase<Index
|
|||
return IndexLifecycleExplainResponse.newManagedIndexResponse(randomAlphaOfLength(10), randomAlphaOfLength(10), randomBoolean(),
|
||||
randomNonNegativeLong(), randomAlphaOfLength(10), randomAlphaOfLength(10), randomAlphaOfLength(10),
|
||||
randomBoolean() ? null : randomAlphaOfLength(10), randomNonNegativeLong(), randomNonNegativeLong(), randomNonNegativeLong(),
|
||||
randomBoolean() ? null : new BytesArray(new RandomStepInfo(() -> randomAlphaOfLength(10)).toString()));
|
||||
randomBoolean() ? null : new BytesArray(new RandomStepInfo(() -> randomAlphaOfLength(10)).toString()),
|
||||
randomBoolean() ? null : PhaseExecutionInfoTests.randomPhaseExecutionInfo(""));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -58,99 +62,14 @@ public class IndexExplainResponseTests extends AbstractSerializingTestCase<Index
|
|||
return randomIndexExplainResponse();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Reader<IndexLifecycleExplainResponse> instanceReader() {
|
||||
return IndexLifecycleExplainResponse::new;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected IndexLifecycleExplainResponse doParseInstance(XContentParser parser) throws IOException {
|
||||
return IndexLifecycleExplainResponse.PARSER.apply(parser, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected IndexLifecycleExplainResponse mutateInstance(IndexLifecycleExplainResponse instance) throws IOException {
|
||||
String index = instance.getIndex();
|
||||
String policy = instance.getPolicyName();
|
||||
String phase = instance.getPhase();
|
||||
String action = instance.getAction();
|
||||
String step = instance.getStep();
|
||||
String failedStep = instance.getFailedStep();
|
||||
long policyTime = instance.getLifecycleDate();
|
||||
long phaseTime = instance.getPhaseTime();
|
||||
long actionTime = instance.getActionTime();
|
||||
long stepTime = instance.getStepTime();
|
||||
boolean managed = instance.managedByILM();
|
||||
boolean skip = instance.skip();
|
||||
BytesReference stepInfo = instance.getStepInfo();
|
||||
if (managed) {
|
||||
switch (between(0, 12)) {
|
||||
case 0:
|
||||
index = index + randomAlphaOfLengthBetween(1, 5);
|
||||
break;
|
||||
case 1:
|
||||
policy = policy + randomAlphaOfLengthBetween(1, 5);
|
||||
break;
|
||||
case 2:
|
||||
phase = phase + randomAlphaOfLengthBetween(1, 5);
|
||||
break;
|
||||
case 3:
|
||||
action = action + randomAlphaOfLengthBetween(1, 5);
|
||||
break;
|
||||
case 4:
|
||||
step = step + randomAlphaOfLengthBetween(1, 5);
|
||||
break;
|
||||
case 5:
|
||||
if (Strings.hasLength(failedStep) == false) {
|
||||
failedStep = randomAlphaOfLength(10);
|
||||
} else if (randomBoolean()) {
|
||||
failedStep = failedStep + randomAlphaOfLengthBetween(1, 5);
|
||||
} else {
|
||||
failedStep = null;
|
||||
}
|
||||
break;
|
||||
case 6:
|
||||
policyTime += randomLongBetween(0, 100000);
|
||||
break;
|
||||
case 7:
|
||||
phaseTime += randomLongBetween(0, 100000);
|
||||
break;
|
||||
case 8:
|
||||
actionTime += randomLongBetween(0, 100000);
|
||||
break;
|
||||
case 9:
|
||||
stepTime += randomLongBetween(0, 100000);
|
||||
break;
|
||||
case 10:
|
||||
if (Strings.hasLength(stepInfo) == false) {
|
||||
stepInfo = new BytesArray(randomByteArrayOfLength(100));
|
||||
} else if (randomBoolean()) {
|
||||
stepInfo = randomValueOtherThan(stepInfo,
|
||||
() -> new BytesArray(new RandomStepInfo(() -> randomAlphaOfLength(10)).toString()));
|
||||
} else {
|
||||
stepInfo = null;
|
||||
}
|
||||
break;
|
||||
case 11:
|
||||
skip = skip == false;
|
||||
break;
|
||||
case 12:
|
||||
return IndexLifecycleExplainResponse.newUnmanagedIndexResponse(index);
|
||||
default:
|
||||
throw new AssertionError("Illegal randomisation branch");
|
||||
}
|
||||
return IndexLifecycleExplainResponse.newManagedIndexResponse(index, policy, skip, policyTime, phase, action, step, failedStep,
|
||||
phaseTime, actionTime, stepTime, stepInfo);
|
||||
} else {
|
||||
switch (between(0, 1)) {
|
||||
case 0:
|
||||
return IndexLifecycleExplainResponse.newUnmanagedIndexResponse(index + randomAlphaOfLengthBetween(1, 5));
|
||||
case 1:
|
||||
return randomManagedIndexExplainResponse();
|
||||
default:
|
||||
throw new AssertionError("Illegal randomisation branch");
|
||||
}
|
||||
}
|
||||
protected boolean supportsUnknownFields() {
|
||||
return false;
|
||||
}
|
||||
|
||||
private static class RandomStepInfo implements ToXContentObject {
|
||||
|
@ -194,4 +113,10 @@ public class IndexExplainResponseTests extends AbstractSerializingTestCase<Index
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected NamedXContentRegistry xContentRegistry() {
|
||||
List<NamedXContentRegistry.Entry> entries = new ArrayList<>(ClusterModule.getNamedXWriteables());
|
||||
entries.add(new NamedXContentRegistry.Entry(LifecycleAction.class, new ParseField(DeleteAction.NAME), DeleteAction::parse));
|
||||
return new NamedXContentRegistry(entries);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,67 @@
|
|||
/*
|
||||
* Licensed to Elasticsearch under one or more contributor
|
||||
* license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright
|
||||
* ownership. Elasticsearch licenses this file to you under
|
||||
* the Apache License, Version 2.0 (the "License"); you may
|
||||
* not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.elasticsearch.client.indexlifecycle;
|
||||
|
||||
import org.elasticsearch.cluster.ClusterModule;
|
||||
import org.elasticsearch.common.ParseField;
|
||||
import org.elasticsearch.common.xcontent.NamedXContentRegistry;
|
||||
import org.elasticsearch.common.xcontent.XContentParser;
|
||||
import org.elasticsearch.test.AbstractXContentTestCase;
|
||||
import org.junit.Before;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class PhaseExecutionInfoTests extends AbstractXContentTestCase<PhaseExecutionInfo> {
|
||||
|
||||
static PhaseExecutionInfo randomPhaseExecutionInfo(String phaseName) {
|
||||
return new PhaseExecutionInfo(randomAlphaOfLength(5), PhaseTests.randomPhase(phaseName),
|
||||
randomNonNegativeLong(), randomNonNegativeLong());
|
||||
}
|
||||
|
||||
String phaseName;
|
||||
|
||||
@Before
|
||||
public void setupPhaseName() {
|
||||
phaseName = randomAlphaOfLength(7);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected PhaseExecutionInfo createTestInstance() {
|
||||
return randomPhaseExecutionInfo(phaseName);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected PhaseExecutionInfo doParseInstance(XContentParser parser) throws IOException {
|
||||
return PhaseExecutionInfo.parse(parser, phaseName);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean supportsUnknownFields() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected NamedXContentRegistry xContentRegistry() {
|
||||
List<NamedXContentRegistry.Entry> entries = new ArrayList<>(ClusterModule.getNamedXWriteables());
|
||||
entries.add(new NamedXContentRegistry.Entry(LifecycleAction.class, new ParseField(DeleteAction.NAME), DeleteAction::parse));
|
||||
return new NamedXContentRegistry(entries);
|
||||
}
|
||||
}
|
|
@ -41,6 +41,10 @@ public class PhaseTests extends AbstractXContentTestCase<Phase> {
|
|||
|
||||
@Override
|
||||
protected Phase createTestInstance() {
|
||||
return randomPhase(phaseName);
|
||||
}
|
||||
|
||||
static Phase randomPhase(String phaseName) {
|
||||
TimeValue after = null;
|
||||
if (randomBoolean()) {
|
||||
after = TimeValue.parseTimeValue(randomTimeValue(0, 1000000000, "s", "m", "h", "d"), "test_after");
|
||||
|
|
|
@ -39,6 +39,7 @@ public class IndexLifecycleExplainResponse implements ToXContentObject, Writeabl
|
|||
private static final ParseField ACTION_TIME_FIELD = new ParseField("action_time");
|
||||
private static final ParseField STEP_TIME_FIELD = new ParseField("step_time");
|
||||
private static final ParseField STEP_INFO_FIELD = new ParseField("step_info");
|
||||
private static final ParseField PHASE_EXECUTION_INFO = new ParseField("phase_execution");
|
||||
|
||||
public static final ConstructingObjectParser<IndexLifecycleExplainResponse, Void> PARSER = new ConstructingObjectParser<>(
|
||||
"index_lifecycle_explain_response",
|
||||
|
@ -55,7 +56,8 @@ public class IndexLifecycleExplainResponse implements ToXContentObject, Writeabl
|
|||
(long) (a[9] == null ? -1L: a[9]),
|
||||
(long) (a[10] == null ? -1L: a[10]),
|
||||
(long) (a[11] == null ? -1L: a[11]),
|
||||
(BytesReference) a[12]));
|
||||
(BytesReference) a[12],
|
||||
(PhaseExecutionInfo) a[13]));
|
||||
static {
|
||||
PARSER.declareString(ConstructingObjectParser.constructorArg(), INDEX_FIELD);
|
||||
PARSER.declareBoolean(ConstructingObjectParser.constructorArg(), MANAGED_BY_ILM_FIELD);
|
||||
|
@ -74,6 +76,8 @@ public class IndexLifecycleExplainResponse implements ToXContentObject, Writeabl
|
|||
builder.copyCurrentStructure(p);
|
||||
return BytesArray.bytes(builder);
|
||||
}, STEP_INFO_FIELD);
|
||||
PARSER.declareObject(ConstructingObjectParser.optionalConstructorArg(), (p, c) -> PhaseExecutionInfo.parse(p, ""),
|
||||
PHASE_EXECUTION_INFO);
|
||||
}
|
||||
|
||||
private final String index;
|
||||
|
@ -89,28 +93,29 @@ public class IndexLifecycleExplainResponse implements ToXContentObject, Writeabl
|
|||
private final boolean skip;
|
||||
private final boolean managedByILM;
|
||||
private final BytesReference stepInfo;
|
||||
private final PhaseExecutionInfo phaseExecutionInfo;
|
||||
|
||||
public static IndexLifecycleExplainResponse newManagedIndexResponse(String index, String policyName, boolean skip, long lifecycleDate,
|
||||
String phase, String action, String step, String failedStep, long phaseTime, long actionTime, long stepTime,
|
||||
BytesReference stepInfo) {
|
||||
BytesReference stepInfo, PhaseExecutionInfo phaseExecutionInfo) {
|
||||
return new IndexLifecycleExplainResponse(index, true, policyName, skip, lifecycleDate, phase, action, step, failedStep, phaseTime,
|
||||
actionTime, stepTime, stepInfo);
|
||||
actionTime, stepTime, stepInfo, phaseExecutionInfo);
|
||||
}
|
||||
|
||||
public static IndexLifecycleExplainResponse newUnmanagedIndexResponse(String index) {
|
||||
return new IndexLifecycleExplainResponse(index, false, null, false, -1L, null, null, null, null, -1L, -1L, -1L, null);
|
||||
return new IndexLifecycleExplainResponse(index, false, null, false, -1L, null, null, null, null, -1L, -1L, -1L, null, null);
|
||||
}
|
||||
|
||||
private IndexLifecycleExplainResponse(String index, boolean managedByILM, String policyName, boolean skip, long lifecycleDate,
|
||||
String phase, String action, String step, String failedStep, long phaseTime, long actionTime,
|
||||
long stepTime, BytesReference stepInfo) {
|
||||
long stepTime, BytesReference stepInfo, PhaseExecutionInfo phaseExecutionInfo) {
|
||||
if (managedByILM) {
|
||||
if (policyName == null) {
|
||||
throw new IllegalArgumentException("[" + POLICY_NAME_FIELD.getPreferredName() + "] cannot be null for managed index");
|
||||
}
|
||||
} else {
|
||||
if (policyName != null || lifecycleDate >= 0 || phase != null || action != null || step != null || failedStep != null
|
||||
|| phaseTime >= 0 || actionTime >= 0 || stepTime >= 0 || stepInfo != null) {
|
||||
|| phaseTime >= 0 || actionTime >= 0 || stepTime >= 0 || stepInfo != null || phaseExecutionInfo != null) {
|
||||
throw new IllegalArgumentException(
|
||||
"Unmanaged index response must only contain fields: [" + MANAGED_BY_ILM_FIELD + ", " + INDEX_FIELD + "]");
|
||||
}
|
||||
|
@ -128,6 +133,7 @@ public class IndexLifecycleExplainResponse implements ToXContentObject, Writeabl
|
|||
this.stepTime = stepTime;
|
||||
this.failedStep = failedStep;
|
||||
this.stepInfo = stepInfo;
|
||||
this.phaseExecutionInfo = phaseExecutionInfo;
|
||||
}
|
||||
|
||||
public IndexLifecycleExplainResponse(StreamInput in) throws IOException {
|
||||
|
@ -145,7 +151,7 @@ public class IndexLifecycleExplainResponse implements ToXContentObject, Writeabl
|
|||
actionTime = in.readZLong();
|
||||
stepTime = in.readZLong();
|
||||
stepInfo = in.readOptionalBytesReference();
|
||||
|
||||
phaseExecutionInfo = in.readOptionalWriteable(PhaseExecutionInfo::new);
|
||||
} else {
|
||||
policyName = null;
|
||||
skip = false;
|
||||
|
@ -158,6 +164,7 @@ public class IndexLifecycleExplainResponse implements ToXContentObject, Writeabl
|
|||
actionTime = -1L;
|
||||
stepTime = -1L;
|
||||
stepInfo = null;
|
||||
phaseExecutionInfo = null;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -177,6 +184,7 @@ public class IndexLifecycleExplainResponse implements ToXContentObject, Writeabl
|
|||
out.writeZLong(actionTime);
|
||||
out.writeZLong(stepTime);
|
||||
out.writeOptionalBytesReference(stepInfo);
|
||||
out.writeOptionalWriteable(phaseExecutionInfo);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -232,6 +240,10 @@ public class IndexLifecycleExplainResponse implements ToXContentObject, Writeabl
|
|||
return stepInfo;
|
||||
}
|
||||
|
||||
public PhaseExecutionInfo getPhaseExecutionInfo() {
|
||||
return phaseExecutionInfo;
|
||||
}
|
||||
|
||||
@Override
|
||||
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
|
||||
builder.startObject();
|
||||
|
@ -269,6 +281,9 @@ public class IndexLifecycleExplainResponse implements ToXContentObject, Writeabl
|
|||
if (stepInfo != null && stepInfo.length() > 0) {
|
||||
builder.rawField(STEP_INFO_FIELD.getPreferredName(), stepInfo.streamInput(), XContentType.JSON);
|
||||
}
|
||||
if (phaseExecutionInfo != null) {
|
||||
builder.field(PHASE_EXECUTION_INFO.getPreferredName(), phaseExecutionInfo);
|
||||
}
|
||||
}
|
||||
builder.endObject();
|
||||
return builder;
|
||||
|
@ -277,7 +292,7 @@ public class IndexLifecycleExplainResponse implements ToXContentObject, Writeabl
|
|||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(index, managedByILM, policyName, skip, lifecycleDate, phase, action, step, failedStep, phaseTime, actionTime,
|
||||
stepTime, stepInfo);
|
||||
stepTime, stepInfo, phaseExecutionInfo);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -301,7 +316,8 @@ public class IndexLifecycleExplainResponse implements ToXContentObject, Writeabl
|
|||
Objects.equals(phaseTime, other.phaseTime) &&
|
||||
Objects.equals(actionTime, other.actionTime) &&
|
||||
Objects.equals(stepTime, other.stepTime) &&
|
||||
Objects.equals(stepInfo, other.stepInfo);
|
||||
Objects.equals(stepInfo, other.stepInfo) &&
|
||||
Objects.equals(phaseExecutionInfo, other.phaseExecutionInfo);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -0,0 +1,135 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
package org.elasticsearch.xpack.core.indexlifecycle;
|
||||
|
||||
import org.elasticsearch.common.Nullable;
|
||||
import org.elasticsearch.common.ParseField;
|
||||
import org.elasticsearch.common.Strings;
|
||||
import org.elasticsearch.common.io.stream.StreamInput;
|
||||
import org.elasticsearch.common.io.stream.StreamOutput;
|
||||
import org.elasticsearch.common.io.stream.Writeable;
|
||||
import org.elasticsearch.common.xcontent.ConstructingObjectParser;
|
||||
import org.elasticsearch.common.xcontent.ToXContentObject;
|
||||
import org.elasticsearch.common.xcontent.XContentBuilder;
|
||||
import org.elasticsearch.common.xcontent.XContentParser;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* This class contains information about the current phase being executed by Index
|
||||
* Lifecycle Management on the specific index.
|
||||
*/
|
||||
public class PhaseExecutionInfo implements ToXContentObject, Writeable {
|
||||
private static final ParseField POLICY_NAME_FIELD = new ParseField("policy");
|
||||
private static final ParseField PHASE_DEFINITION_FIELD = new ParseField("phase_definition");
|
||||
private static final ParseField VERSION_FIELD = new ParseField("version");
|
||||
private static final ParseField MODIFIED_DATE_IN_MILLIS_FIELD = new ParseField("modified_date_in_millis");
|
||||
|
||||
private static final ConstructingObjectParser<PhaseExecutionInfo, String> PARSER = new ConstructingObjectParser<>(
|
||||
"phase_execution_info", false,
|
||||
(a, name) -> new PhaseExecutionInfo((String) a[0], (Phase) a[1], (long) a[2], (long) a[3]));
|
||||
static {
|
||||
PARSER.declareString(ConstructingObjectParser.constructorArg(), POLICY_NAME_FIELD);
|
||||
PARSER.declareObject(ConstructingObjectParser.optionalConstructorArg(), Phase::parse, PHASE_DEFINITION_FIELD);
|
||||
PARSER.declareLong(ConstructingObjectParser.constructorArg(), VERSION_FIELD);
|
||||
PARSER.declareLong(ConstructingObjectParser.constructorArg(), MODIFIED_DATE_IN_MILLIS_FIELD);
|
||||
}
|
||||
|
||||
public static PhaseExecutionInfo parse(XContentParser parser, String name) {
|
||||
return PARSER.apply(parser, name);
|
||||
}
|
||||
|
||||
private final String policyName;
|
||||
private final Phase phase;
|
||||
private final long version;
|
||||
private final long modifiedDate;
|
||||
|
||||
/**
|
||||
* This class holds information about the current phase that is being executed
|
||||
*
|
||||
* @param policyName the name of the policy being executed, this may not be the current policy assigned to an index
|
||||
* @param phase the current phase definition executed
|
||||
* @param version the version of the <code>policyName</code> being executed
|
||||
* @param modifiedDate the time the executing version of the phase was modified
|
||||
*/
|
||||
public PhaseExecutionInfo(String policyName, @Nullable Phase phase, long version, long modifiedDate) {
|
||||
this.policyName = policyName;
|
||||
this.phase = phase;
|
||||
this.version = version;
|
||||
this.modifiedDate = modifiedDate;
|
||||
}
|
||||
|
||||
PhaseExecutionInfo(StreamInput in) throws IOException {
|
||||
this.policyName = in.readString();
|
||||
this.phase = in.readOptionalWriteable(Phase::new);
|
||||
this.version = in.readVLong();
|
||||
this.modifiedDate = in.readVLong();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeTo(StreamOutput out) throws IOException {
|
||||
out.writeString(policyName);
|
||||
out.writeOptionalWriteable(phase);
|
||||
out.writeVLong(version);
|
||||
out.writeVLong(modifiedDate);
|
||||
}
|
||||
|
||||
public String getPolicyName() {
|
||||
return policyName;
|
||||
}
|
||||
|
||||
public Phase getPhase() {
|
||||
return phase;
|
||||
}
|
||||
|
||||
public long getVersion() {
|
||||
return version;
|
||||
}
|
||||
|
||||
public long getModifiedDate() {
|
||||
return modifiedDate;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(policyName, phase, version, modifiedDate);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (obj == null) {
|
||||
return false;
|
||||
}
|
||||
if (getClass() != obj.getClass()) {
|
||||
return false;
|
||||
}
|
||||
PhaseExecutionInfo other = (PhaseExecutionInfo) obj;
|
||||
return Objects.equals(policyName, other.policyName) &&
|
||||
Objects.equals(phase, other.phase) &&
|
||||
Objects.equals(version, other.version) &&
|
||||
Objects.equals(modifiedDate, other.modifiedDate);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return Strings.toString(this, false, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
|
||||
builder.startObject();
|
||||
builder.field(POLICY_NAME_FIELD.getPreferredName(), policyName);
|
||||
if (phase != null) {
|
||||
builder.field(PHASE_DEFINITION_FIELD.getPreferredName(), phase);
|
||||
}
|
||||
builder.field(VERSION_FIELD.getPreferredName(), version);
|
||||
builder.timeField(MODIFIED_DATE_IN_MILLIS_FIELD.getPreferredName(), "modified_date", modifiedDate);
|
||||
builder.endObject();
|
||||
return builder;
|
||||
}
|
||||
|
||||
}
|
|
@ -6,11 +6,18 @@
|
|||
|
||||
package org.elasticsearch.xpack.core.indexlifecycle;
|
||||
|
||||
import org.elasticsearch.cluster.ClusterModule;
|
||||
import org.elasticsearch.common.ParseField;
|
||||
import org.elasticsearch.common.io.stream.NamedWriteableRegistry;
|
||||
import org.elasticsearch.common.xcontent.NamedXContentRegistry;
|
||||
import org.elasticsearch.common.xcontent.XContentParser;
|
||||
import org.elasticsearch.test.AbstractStreamableXContentTestCase;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class ExplainLifecycleResponseTests extends AbstractStreamableXContentTestCase<ExplainLifecycleResponse> {
|
||||
|
@ -47,4 +54,16 @@ public class ExplainLifecycleResponseTests extends AbstractStreamableXContentTes
|
|||
protected boolean supportsUnknownFields() {
|
||||
return false;
|
||||
}
|
||||
|
||||
protected NamedWriteableRegistry getNamedWriteableRegistry() {
|
||||
return new NamedWriteableRegistry(Arrays
|
||||
.asList(new NamedWriteableRegistry.Entry(LifecycleAction.class, MockAction.NAME, MockAction::new)));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected NamedXContentRegistry xContentRegistry() {
|
||||
List<NamedXContentRegistry.Entry> entries = new ArrayList<>(ClusterModule.getNamedXWriteables());
|
||||
entries.add(new NamedXContentRegistry.Entry(LifecycleAction.class, new ParseField(MockAction.NAME), MockAction::parse));
|
||||
return new NamedXContentRegistry(entries);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,16 +6,23 @@
|
|||
|
||||
package org.elasticsearch.xpack.core.indexlifecycle;
|
||||
|
||||
import org.elasticsearch.cluster.ClusterModule;
|
||||
import org.elasticsearch.common.ParseField;
|
||||
import org.elasticsearch.common.Strings;
|
||||
import org.elasticsearch.common.bytes.BytesArray;
|
||||
import org.elasticsearch.common.bytes.BytesReference;
|
||||
import org.elasticsearch.common.io.stream.NamedWriteableRegistry;
|
||||
import org.elasticsearch.common.io.stream.Writeable.Reader;
|
||||
import org.elasticsearch.common.xcontent.NamedXContentRegistry;
|
||||
import org.elasticsearch.common.xcontent.ToXContentObject;
|
||||
import org.elasticsearch.common.xcontent.XContentBuilder;
|
||||
import org.elasticsearch.common.xcontent.XContentParser;
|
||||
import org.elasticsearch.test.AbstractSerializingTestCase;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
|
@ -37,7 +44,8 @@ public class IndexExplainResponseTests extends AbstractSerializingTestCase<Index
|
|||
return IndexLifecycleExplainResponse.newManagedIndexResponse(randomAlphaOfLength(10), randomAlphaOfLength(10), randomBoolean(),
|
||||
randomNonNegativeLong(), randomAlphaOfLength(10), randomAlphaOfLength(10), randomAlphaOfLength(10),
|
||||
randomBoolean() ? null : randomAlphaOfLength(10), randomNonNegativeLong(), randomNonNegativeLong(), randomNonNegativeLong(),
|
||||
randomBoolean() ? null : new BytesArray(new RandomStepInfo(() -> randomAlphaOfLength(10)).toString()));
|
||||
randomBoolean() ? null : new BytesArray(new RandomStepInfo(() -> randomAlphaOfLength(10)).toString()),
|
||||
randomBoolean() ? null : PhaseExecutionInfoTests.randomPhaseExecutionInfo(""));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -70,8 +78,9 @@ public class IndexExplainResponseTests extends AbstractSerializingTestCase<Index
|
|||
boolean managed = instance.managedByILM();
|
||||
boolean skip = instance.skip();
|
||||
BytesReference stepInfo = instance.getStepInfo();
|
||||
PhaseExecutionInfo phaseExecutionInfo = instance.getPhaseExecutionInfo();
|
||||
if (managed) {
|
||||
switch (between(0, 12)) {
|
||||
switch (between(0, 13)) {
|
||||
case 0:
|
||||
index = index + randomAlphaOfLengthBetween(1, 5);
|
||||
break;
|
||||
|
@ -122,12 +131,15 @@ public class IndexExplainResponseTests extends AbstractSerializingTestCase<Index
|
|||
skip = skip == false;
|
||||
break;
|
||||
case 12:
|
||||
phaseExecutionInfo = randomValueOtherThan(phaseExecutionInfo, () -> PhaseExecutionInfoTests.randomPhaseExecutionInfo(""));
|
||||
break;
|
||||
case 13:
|
||||
return IndexLifecycleExplainResponse.newUnmanagedIndexResponse(index);
|
||||
default:
|
||||
throw new AssertionError("Illegal randomisation branch");
|
||||
}
|
||||
return IndexLifecycleExplainResponse.newManagedIndexResponse(index, policy, skip, policyTime, phase, action, step, failedStep,
|
||||
phaseTime, actionTime, stepTime, stepInfo);
|
||||
phaseTime, actionTime, stepTime, stepInfo, phaseExecutionInfo);
|
||||
} else {
|
||||
switch (between(0, 1)) {
|
||||
case 0:
|
||||
|
@ -140,6 +152,18 @@ public class IndexExplainResponseTests extends AbstractSerializingTestCase<Index
|
|||
}
|
||||
}
|
||||
|
||||
protected NamedWriteableRegistry getNamedWriteableRegistry() {
|
||||
return new NamedWriteableRegistry(Arrays
|
||||
.asList(new NamedWriteableRegistry.Entry(LifecycleAction.class, MockAction.NAME, MockAction::new)));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected NamedXContentRegistry xContentRegistry() {
|
||||
List<NamedXContentRegistry.Entry> entries = new ArrayList<>(ClusterModule.getNamedXWriteables());
|
||||
entries.add(new NamedXContentRegistry.Entry(LifecycleAction.class, new ParseField(MockAction.NAME), MockAction::parse));
|
||||
return new NamedXContentRegistry(entries);
|
||||
}
|
||||
|
||||
private static class RandomStepInfo implements ToXContentObject {
|
||||
|
||||
private final String key;
|
||||
|
|
|
@ -0,0 +1,88 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
package org.elasticsearch.xpack.core.indexlifecycle;
|
||||
|
||||
import org.elasticsearch.cluster.ClusterModule;
|
||||
import org.elasticsearch.common.ParseField;
|
||||
import org.elasticsearch.common.io.stream.NamedWriteableRegistry;
|
||||
import org.elasticsearch.common.io.stream.Writeable.Reader;
|
||||
import org.elasticsearch.common.xcontent.NamedXContentRegistry;
|
||||
import org.elasticsearch.common.xcontent.XContentParser;
|
||||
import org.elasticsearch.test.AbstractSerializingTestCase;
|
||||
import org.junit.Before;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
public class PhaseExecutionInfoTests extends AbstractSerializingTestCase<PhaseExecutionInfo> {
|
||||
|
||||
static PhaseExecutionInfo randomPhaseExecutionInfo(String phaseName) {
|
||||
return new PhaseExecutionInfo(randomAlphaOfLength(5), PhaseTests.randomTestPhase(phaseName),
|
||||
randomNonNegativeLong(), randomNonNegativeLong());
|
||||
}
|
||||
|
||||
String phaseName;
|
||||
|
||||
@Before
|
||||
public void setupPhaseName() {
|
||||
phaseName = randomAlphaOfLength(7);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected PhaseExecutionInfo createTestInstance() {
|
||||
return randomPhaseExecutionInfo(phaseName);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Reader<PhaseExecutionInfo> instanceReader() {
|
||||
return PhaseExecutionInfo::new;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected PhaseExecutionInfo doParseInstance(XContentParser parser) throws IOException {
|
||||
return PhaseExecutionInfo.parse(parser, phaseName);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected PhaseExecutionInfo mutateInstance(PhaseExecutionInfo instance) throws IOException {
|
||||
String policyName = instance.getPolicyName();
|
||||
Phase phase = instance.getPhase();
|
||||
long version = instance.getVersion();
|
||||
long modifiedDate = instance.getModifiedDate();
|
||||
switch (between(0, 3)) {
|
||||
case 0:
|
||||
policyName = policyName + randomAlphaOfLengthBetween(1, 5);
|
||||
break;
|
||||
case 1:
|
||||
phase = randomValueOtherThan(phase, () -> PhaseTests.randomTestPhase(randomAlphaOfLength(6)));
|
||||
break;
|
||||
case 2:
|
||||
version++;
|
||||
break;
|
||||
case 3:
|
||||
modifiedDate++;
|
||||
break;
|
||||
default:
|
||||
throw new AssertionError("Illegal randomisation branch");
|
||||
}
|
||||
return new PhaseExecutionInfo(policyName, phase, version, modifiedDate);
|
||||
}
|
||||
|
||||
protected NamedWriteableRegistry getNamedWriteableRegistry() {
|
||||
return new NamedWriteableRegistry(Arrays
|
||||
.asList(new NamedWriteableRegistry.Entry(LifecycleAction.class, MockAction.NAME, MockAction::new)));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected NamedXContentRegistry xContentRegistry() {
|
||||
List<NamedXContentRegistry.Entry> entries = new ArrayList<>(ClusterModule.getNamedXWriteables());
|
||||
entries.add(new NamedXContentRegistry.Entry(LifecycleAction.class, new ParseField(MockAction.NAME), MockAction::parse));
|
||||
return new NamedXContentRegistry(entries);
|
||||
}
|
||||
}
|
|
@ -33,6 +33,10 @@ public class PhaseTests extends AbstractSerializingTestCase<Phase> {
|
|||
|
||||
@Override
|
||||
protected Phase createTestInstance() {
|
||||
return randomTestPhase(phaseName);
|
||||
}
|
||||
|
||||
static Phase randomTestPhase(String phaseName) {
|
||||
TimeValue after = null;
|
||||
if (randomBoolean()) {
|
||||
after = TimeValue.parseTimeValue(randomTimeValue(0, 1000000000, "s", "m", "h", "d"), "test_after");
|
||||
|
|
|
@ -28,10 +28,11 @@ import org.elasticsearch.xpack.core.indexlifecycle.ClusterStateActionStep;
|
|||
import org.elasticsearch.xpack.core.indexlifecycle.ClusterStateWaitStep;
|
||||
import org.elasticsearch.xpack.core.indexlifecycle.ErrorStep;
|
||||
import org.elasticsearch.xpack.core.indexlifecycle.IndexLifecycleMetadata;
|
||||
import org.elasticsearch.xpack.core.indexlifecycle.LifecyclePolicy;
|
||||
import org.elasticsearch.xpack.core.indexlifecycle.LifecyclePolicyMetadata;
|
||||
import org.elasticsearch.xpack.core.indexlifecycle.LifecycleSettings;
|
||||
import org.elasticsearch.xpack.core.indexlifecycle.Phase;
|
||||
import org.elasticsearch.xpack.core.indexlifecycle.PhaseCompleteStep;
|
||||
import org.elasticsearch.xpack.core.indexlifecycle.PhaseExecutionInfo;
|
||||
import org.elasticsearch.xpack.core.indexlifecycle.RolloverAction;
|
||||
import org.elasticsearch.xpack.core.indexlifecycle.Step;
|
||||
import org.elasticsearch.xpack.core.indexlifecycle.Step.StepKey;
|
||||
|
@ -250,8 +251,10 @@ public class IndexLifecycleRunner {
|
|||
LongSupplier nowSupplier) {
|
||||
IndexMetaData idxMeta = clusterState.getMetaData().index(index);
|
||||
IndexLifecycleMetadata ilmMeta = clusterState.metaData().custom(IndexLifecycleMetadata.TYPE);
|
||||
LifecyclePolicy policy = ilmMeta.getPolicies().get(LifecycleSettings.LIFECYCLE_NAME_SETTING.get(idxMeta.getSettings()));
|
||||
Settings.Builder indexSettings = moveIndexSettingsToNextStep(policy, idxMeta.getSettings(), currentStep, nextStep, nowSupplier);
|
||||
LifecyclePolicyMetadata policyMetadata = ilmMeta.getPolicyMetadatas()
|
||||
.get(LifecycleSettings.LIFECYCLE_NAME_SETTING.get(idxMeta.getSettings()));
|
||||
Settings.Builder indexSettings = moveIndexSettingsToNextStep(policyMetadata, idxMeta.getSettings(), currentStep,
|
||||
nextStep, nowSupplier);
|
||||
ClusterState.Builder newClusterStateBuilder = newClusterStateWithIndexSettings(index, clusterState, indexSettings);
|
||||
return newClusterStateBuilder.build();
|
||||
}
|
||||
|
@ -260,12 +263,13 @@ public class IndexLifecycleRunner {
|
|||
LongSupplier nowSupplier) throws IOException {
|
||||
IndexMetaData idxMeta = clusterState.getMetaData().index(index);
|
||||
IndexLifecycleMetadata ilmMeta = clusterState.metaData().custom(IndexLifecycleMetadata.TYPE);
|
||||
LifecyclePolicy policy = ilmMeta.getPolicies().get(LifecycleSettings.LIFECYCLE_NAME_SETTING.get(idxMeta.getSettings()));
|
||||
LifecyclePolicyMetadata policyMetadata = ilmMeta.getPolicyMetadatas()
|
||||
.get(LifecycleSettings.LIFECYCLE_NAME_SETTING.get(idxMeta.getSettings()));
|
||||
XContentBuilder causeXContentBuilder = JsonXContent.contentBuilder();
|
||||
causeXContentBuilder.startObject();
|
||||
ElasticsearchException.generateThrowableXContent(causeXContentBuilder, ToXContent.EMPTY_PARAMS, cause);
|
||||
causeXContentBuilder.endObject();
|
||||
Settings.Builder indexSettings = moveIndexSettingsToNextStep(policy, idxMeta.getSettings(), currentStep,
|
||||
Settings.Builder indexSettings = moveIndexSettingsToNextStep(policyMetadata, idxMeta.getSettings(), currentStep,
|
||||
new StepKey(currentStep.getPhase(), currentStep.getAction(), ErrorStep.NAME), nowSupplier)
|
||||
.put(LifecycleSettings.LIFECYCLE_FAILED_STEP, currentStep.getName())
|
||||
.put(LifecycleSettings.LIFECYCLE_STEP_INFO, BytesReference.bytes(causeXContentBuilder).utf8ToString());
|
||||
|
@ -294,7 +298,7 @@ public class IndexLifecycleRunner {
|
|||
return newState;
|
||||
}
|
||||
|
||||
private static Settings.Builder moveIndexSettingsToNextStep(LifecyclePolicy policy, Settings existingSettings,
|
||||
private static Settings.Builder moveIndexSettingsToNextStep(LifecyclePolicyMetadata policyMetadata, Settings existingSettings,
|
||||
StepKey currentStep, StepKey nextStep, LongSupplier nowSupplier) {
|
||||
long nowAsMillis = nowSupplier.getAsLong();
|
||||
Settings.Builder newSettings = Settings.builder().put(existingSettings).put(LifecycleSettings.LIFECYCLE_PHASE, nextStep.getPhase())
|
||||
|
@ -305,16 +309,15 @@ public class IndexLifecycleRunner {
|
|||
.put(LifecycleSettings.LIFECYCLE_STEP_INFO, (String) null);
|
||||
if (currentStep.getPhase().equals(nextStep.getPhase()) == false) {
|
||||
final String newPhaseDefinition;
|
||||
final Phase nextPhase;
|
||||
if ("new".equals(nextStep.getPhase()) || TerminalPolicyStep.KEY.equals(nextStep)) {
|
||||
newPhaseDefinition = nextStep.getPhase();
|
||||
nextPhase = null;
|
||||
} else {
|
||||
Phase nextPhase = policy.getPhases().get(nextStep.getPhase());
|
||||
if (nextPhase == null) {
|
||||
newPhaseDefinition = null;
|
||||
} else {
|
||||
newPhaseDefinition = Strings.toString(nextPhase, false, false);
|
||||
}
|
||||
nextPhase = policyMetadata.getPolicy().getPhases().get(nextStep.getPhase());
|
||||
}
|
||||
PhaseExecutionInfo phaseExecutionInfo = new PhaseExecutionInfo(policyMetadata.getName(), nextPhase,
|
||||
policyMetadata.getVersion(), policyMetadata.getModifiedDate());
|
||||
newPhaseDefinition = Strings.toString(phaseExecutionInfo, false, false);
|
||||
newSettings.put(LifecycleSettings.LIFECYCLE_PHASE_DEFINITION, newPhaseDefinition);
|
||||
newSettings.put(LifecycleSettings.LIFECYCLE_PHASE_TIME, nowAsMillis);
|
||||
}
|
||||
|
@ -378,7 +381,7 @@ public class IndexLifecycleRunner {
|
|||
}
|
||||
|
||||
public static ClusterState setPolicyForIndexes(final String newPolicyName, final Index[] indices, ClusterState currentState,
|
||||
LifecyclePolicy newPolicy, List<String> failedIndexes, LongSupplier nowSupplier) {
|
||||
LifecyclePolicyMetadata newPolicyMetadata, List<String> failedIndexes, LongSupplier nowSupplier) {
|
||||
MetaData.Builder newMetadata = MetaData.builder(currentState.getMetaData());
|
||||
boolean clusterStateChanged = false;
|
||||
for (Index index : indices) {
|
||||
|
@ -388,7 +391,7 @@ public class IndexLifecycleRunner {
|
|||
failedIndexes.add(index.getName());
|
||||
} else {
|
||||
IndexMetaData.Builder newIdxMetadata = IndexLifecycleRunner.setPolicyForIndex(newPolicyName,
|
||||
newPolicy, indexMetadata, nowSupplier);
|
||||
newPolicyMetadata, indexMetadata, nowSupplier);
|
||||
if (newIdxMetadata != null) {
|
||||
newMetadata.put(newIdxMetadata);
|
||||
clusterStateChanged = true;
|
||||
|
@ -404,7 +407,7 @@ public class IndexLifecycleRunner {
|
|||
}
|
||||
}
|
||||
|
||||
private static IndexMetaData.Builder setPolicyForIndex(final String newPolicyName, LifecyclePolicy newPolicy,
|
||||
private static IndexMetaData.Builder setPolicyForIndex(final String newPolicyName, LifecyclePolicyMetadata newPolicyMetadata,
|
||||
IndexMetaData indexMetadata, LongSupplier nowSupplier) {
|
||||
Settings idxSettings = indexMetadata.getSettings();
|
||||
StepKey currentStepKey = IndexLifecycleRunner.getCurrentStepKey(idxSettings);
|
||||
|
@ -413,9 +416,9 @@ public class IndexLifecycleRunner {
|
|||
if (currentStepKey != null) {
|
||||
// Check if current step exists in new policy and if not move to
|
||||
// next available step
|
||||
StepKey nextValidStepKey = newPolicy.getNextValidStep(currentStepKey);
|
||||
StepKey nextValidStepKey = newPolicyMetadata.getPolicy().getNextValidStep(currentStepKey);
|
||||
if (nextValidStepKey.equals(currentStepKey) == false) {
|
||||
newSettings = moveIndexSettingsToNextStep(newPolicy, idxSettings, currentStepKey, nextValidStepKey, nowSupplier);
|
||||
newSettings = moveIndexSettingsToNextStep(newPolicyMetadata, idxSettings, currentStepKey, nextValidStepKey, nowSupplier);
|
||||
}
|
||||
}
|
||||
newSettings.put(LifecycleSettings.LIFECYCLE_NAME_SETTING.getKey(), newPolicyName);
|
||||
|
|
|
@ -30,6 +30,7 @@ import org.elasticsearch.xpack.core.indexlifecycle.LifecyclePolicy;
|
|||
import org.elasticsearch.xpack.core.indexlifecycle.LifecyclePolicyMetadata;
|
||||
import org.elasticsearch.xpack.core.indexlifecycle.LifecycleSettings;
|
||||
import org.elasticsearch.xpack.core.indexlifecycle.Phase;
|
||||
import org.elasticsearch.xpack.core.indexlifecycle.PhaseExecutionInfo;
|
||||
import org.elasticsearch.xpack.core.indexlifecycle.Step;
|
||||
import org.elasticsearch.xpack.core.indexlifecycle.TerminalPolicyStep;
|
||||
|
||||
|
@ -170,7 +171,7 @@ public class PolicyStepsRegistry {
|
|||
// parse existing phase steps from the phase definition in the index settings
|
||||
String phaseDef = imd.value.getSettings().get(LifecycleSettings.LIFECYCLE_PHASE_DEFINITION,
|
||||
InitializePolicyContextStep.INITIALIZATION_PHASE);
|
||||
final Phase phase;
|
||||
final PhaseExecutionInfo phaseExecutionInfo;
|
||||
LifecyclePolicy currentPolicy = lifecyclePolicyMap.get(policy).getPolicy();
|
||||
final LifecyclePolicy policyToExecute;
|
||||
if (InitializePolicyContextStep.INITIALIZATION_PHASE.equals(phaseDef)
|
||||
|
@ -181,15 +182,15 @@ public class PolicyStepsRegistry {
|
|||
// if the current phase definition describes an internal step/phase, do not parse
|
||||
try (XContentParser parser = JsonXContent.jsonXContent.createParser(xContentRegistry,
|
||||
DeprecationHandler.THROW_UNSUPPORTED_OPERATION, phaseDef)) {
|
||||
phase = Phase.parse(parser, currentPhase);
|
||||
phaseExecutionInfo = PhaseExecutionInfo.parse(parser, currentPhase);
|
||||
} catch (IOException e) {
|
||||
logger.error("failed to configure phase [" + currentPhase + "] for index [" + index.getName() + "]", e);
|
||||
indexPhaseSteps.remove(index);
|
||||
continue;
|
||||
}
|
||||
Map<String, Phase> phaseMap = new HashMap<>(currentPolicy.getPhases());
|
||||
if (phase != null) {
|
||||
phaseMap.put(currentPhase, phase);
|
||||
if (phaseExecutionInfo.getPhase() != null) {
|
||||
phaseMap.put(currentPhase, phaseExecutionInfo.getPhase());
|
||||
}
|
||||
policyToExecute = new LifecyclePolicy(currentPolicy.getType(), currentPolicy.getName(), phaseMap);
|
||||
}
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
|
||||
package org.elasticsearch.xpack.indexlifecycle.action;
|
||||
|
||||
import org.elasticsearch.ElasticsearchParseException;
|
||||
import org.elasticsearch.action.ActionListener;
|
||||
import org.elasticsearch.action.support.ActionFilters;
|
||||
import org.elasticsearch.action.support.master.info.TransportClusterInfoAction;
|
||||
|
@ -19,25 +20,36 @@ import org.elasticsearch.common.Strings;
|
|||
import org.elasticsearch.common.bytes.BytesArray;
|
||||
import org.elasticsearch.common.inject.Inject;
|
||||
import org.elasticsearch.common.settings.Settings;
|
||||
import org.elasticsearch.common.xcontent.DeprecationHandler;
|
||||
import org.elasticsearch.common.xcontent.NamedXContentRegistry;
|
||||
import org.elasticsearch.common.xcontent.XContentParser;
|
||||
import org.elasticsearch.common.xcontent.json.JsonXContent;
|
||||
import org.elasticsearch.xpack.core.indexlifecycle.ExplainLifecycleRequest;
|
||||
import org.elasticsearch.xpack.core.indexlifecycle.ExplainLifecycleResponse;
|
||||
import org.elasticsearch.xpack.core.indexlifecycle.IndexLifecycleExplainResponse;
|
||||
import org.elasticsearch.threadpool.ThreadPool;
|
||||
import org.elasticsearch.transport.TransportService;
|
||||
import org.elasticsearch.xpack.core.indexlifecycle.LifecycleSettings;
|
||||
import org.elasticsearch.xpack.core.indexlifecycle.PhaseExecutionInfo;
|
||||
import org.elasticsearch.xpack.core.indexlifecycle.action.ExplainLifecycleAction;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public class TransportExplainLifecycleAction
|
||||
extends TransportClusterInfoAction<ExplainLifecycleRequest, ExplainLifecycleResponse> {
|
||||
|
||||
private final NamedXContentRegistry xContentRegistry;
|
||||
|
||||
@Inject
|
||||
public TransportExplainLifecycleAction(Settings settings, TransportService transportService, ClusterService clusterService,
|
||||
ThreadPool threadPool, ActionFilters actionFilters, IndexNameExpressionResolver indexNameExpressionResolver) {
|
||||
ThreadPool threadPool, ActionFilters actionFilters,
|
||||
IndexNameExpressionResolver indexNameExpressionResolver,
|
||||
NamedXContentRegistry xContentRegistry) {
|
||||
super(settings, ExplainLifecycleAction.NAME, transportService, clusterService, threadPool, actionFilters,
|
||||
ExplainLifecycleRequest::new, indexNameExpressionResolver);
|
||||
this.xContentRegistry = xContentRegistry;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -65,6 +77,20 @@ public class TransportExplainLifecycleAction
|
|||
IndexMetaData idxMetadata = state.metaData().index(index);
|
||||
Settings idxSettings = idxMetadata.getSettings();
|
||||
String policyName = LifecycleSettings.LIFECYCLE_NAME_SETTING.get(idxSettings);
|
||||
String currentPhase = LifecycleSettings.LIFECYCLE_PHASE_SETTING.get(idxSettings);
|
||||
// parse existing phase steps from the phase definition in the index settings
|
||||
String phaseDef = idxSettings.get(LifecycleSettings.LIFECYCLE_PHASE_DEFINITION);
|
||||
PhaseExecutionInfo phaseExecutionInfo = null;
|
||||
if (Strings.isNullOrEmpty(phaseDef) == false) {
|
||||
try (XContentParser parser = JsonXContent.jsonXContent.createParser(xContentRegistry,
|
||||
DeprecationHandler.THROW_UNSUPPORTED_OPERATION, phaseDef)) {
|
||||
phaseExecutionInfo = PhaseExecutionInfo.parse(parser, currentPhase);
|
||||
} catch (IOException e) {
|
||||
listener.onFailure(new ElasticsearchParseException(
|
||||
"failed to parse [" + LifecycleSettings.LIFECYCLE_PHASE_DEFINITION + "] for index [" + index + "]", e));
|
||||
return;
|
||||
}
|
||||
}
|
||||
final IndexLifecycleExplainResponse indexResponse;
|
||||
if (Strings.hasLength(policyName)) {
|
||||
indexResponse = IndexLifecycleExplainResponse.newManagedIndexResponse(index, policyName,
|
||||
|
@ -77,7 +103,8 @@ public class TransportExplainLifecycleAction
|
|||
LifecycleSettings.LIFECYCLE_PHASE_TIME_SETTING.get(idxSettings),
|
||||
LifecycleSettings.LIFECYCLE_ACTION_TIME_SETTING.get(idxSettings),
|
||||
LifecycleSettings.LIFECYCLE_STEP_TIME_SETTING.get(idxSettings),
|
||||
new BytesArray(LifecycleSettings.LIFECYCLE_STEP_INFO_SETTING.get(idxSettings)));
|
||||
new BytesArray(LifecycleSettings.LIFECYCLE_STEP_INFO_SETTING.get(idxSettings)),
|
||||
phaseExecutionInfo);
|
||||
} else {
|
||||
indexResponse = IndexLifecycleExplainResponse.newUnmanagedIndexResponse(index);
|
||||
}
|
||||
|
|
|
@ -19,12 +19,12 @@ import org.elasticsearch.cluster.service.ClusterService;
|
|||
import org.elasticsearch.common.inject.Inject;
|
||||
import org.elasticsearch.common.settings.Settings;
|
||||
import org.elasticsearch.index.Index;
|
||||
import org.elasticsearch.xpack.core.indexlifecycle.LifecyclePolicyMetadata;
|
||||
import org.elasticsearch.xpack.core.indexlifecycle.SetIndexLifecyclePolicyRequest;
|
||||
import org.elasticsearch.xpack.core.indexlifecycle.SetIndexLifecyclePolicyResponse;
|
||||
import org.elasticsearch.threadpool.ThreadPool;
|
||||
import org.elasticsearch.transport.TransportService;
|
||||
import org.elasticsearch.xpack.core.indexlifecycle.IndexLifecycleMetadata;
|
||||
import org.elasticsearch.xpack.core.indexlifecycle.LifecyclePolicy;
|
||||
import org.elasticsearch.xpack.core.indexlifecycle.action.SetIndexLifecyclePolicyAction;
|
||||
import org.elasticsearch.xpack.indexlifecycle.IndexLifecycleRunner;
|
||||
|
||||
|
@ -76,14 +76,14 @@ public class TransportSetIndexLifecyclePolicyAction
|
|||
throw new ResourceNotFoundException("Policy does not exist [{}]", newPolicyName);
|
||||
}
|
||||
|
||||
LifecyclePolicy newPolicy = ilmMetadata.getPolicies().get(newPolicyName);
|
||||
LifecyclePolicyMetadata newPolicyMetadata = ilmMetadata.getPolicyMetadatas().get(newPolicyName);
|
||||
|
||||
if (newPolicy == null) {
|
||||
if (newPolicyMetadata == null) {
|
||||
throw new ResourceNotFoundException("Policy does not exist [{}]", newPolicyName);
|
||||
}
|
||||
|
||||
return IndexLifecycleRunner.setPolicyForIndexes(newPolicyName, indices, currentState, newPolicy, failedIndexes,
|
||||
() -> System.currentTimeMillis());
|
||||
return IndexLifecycleRunner.setPolicyForIndexes(newPolicyName, indices, currentState, newPolicyMetadata,
|
||||
failedIndexes, System::currentTimeMillis);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -28,14 +28,19 @@ import org.elasticsearch.transport.TransportService;
|
|||
import org.elasticsearch.xpack.core.LocalStateCompositeXPackPlugin;
|
||||
import org.elasticsearch.xpack.core.XPackSettings;
|
||||
import org.elasticsearch.xpack.core.indexlifecycle.ClusterStateWaitStep;
|
||||
import org.elasticsearch.xpack.core.indexlifecycle.ExplainLifecycleRequest;
|
||||
import org.elasticsearch.xpack.core.indexlifecycle.ExplainLifecycleResponse;
|
||||
import org.elasticsearch.xpack.core.indexlifecycle.IndexLifecycleExplainResponse;
|
||||
import org.elasticsearch.xpack.core.indexlifecycle.LifecycleAction;
|
||||
import org.elasticsearch.xpack.core.indexlifecycle.LifecyclePolicy;
|
||||
import org.elasticsearch.xpack.core.indexlifecycle.LifecycleSettings;
|
||||
import org.elasticsearch.xpack.core.indexlifecycle.LifecycleType;
|
||||
import org.elasticsearch.xpack.core.indexlifecycle.MockAction;
|
||||
import org.elasticsearch.xpack.core.indexlifecycle.Phase;
|
||||
import org.elasticsearch.xpack.core.indexlifecycle.PhaseExecutionInfo;
|
||||
import org.elasticsearch.xpack.core.indexlifecycle.Step;
|
||||
import org.elasticsearch.xpack.core.indexlifecycle.TerminalPolicyStep;
|
||||
import org.elasticsearch.xpack.core.indexlifecycle.action.ExplainLifecycleAction;
|
||||
import org.elasticsearch.xpack.core.indexlifecycle.action.GetLifecycleAction;
|
||||
import org.elasticsearch.xpack.core.indexlifecycle.action.PutLifecycleAction;
|
||||
import org.junit.Before;
|
||||
|
@ -69,6 +74,7 @@ import static org.hamcrest.core.IsNull.nullValue;
|
|||
public class IndexLifecycleInitialisationIT extends ESIntegTestCase {
|
||||
private Settings settings;
|
||||
private LifecyclePolicy lifecyclePolicy;
|
||||
private Phase mockPhase;
|
||||
private static final ObservableAction OBSERVABLE_ACTION;
|
||||
static {
|
||||
List<Step> steps = new ArrayList<>();
|
||||
|
@ -127,7 +133,8 @@ public class IndexLifecycleInitialisationIT extends ESIntegTestCase {
|
|||
Step.StepKey key = new Step.StepKey("mock", ObservableAction.NAME, ObservableClusterStateWaitStep.NAME);
|
||||
steps.add(new ObservableClusterStateWaitStep(key, TerminalPolicyStep.KEY));
|
||||
Map<String, LifecycleAction> actions = Collections.singletonMap(ObservableAction.NAME, OBSERVABLE_ACTION);
|
||||
Map<String, Phase> phases = Collections.singletonMap("mock", new Phase("mock", TimeValue.timeValueSeconds(0), actions));
|
||||
mockPhase = new Phase("mock", TimeValue.timeValueSeconds(0), actions);
|
||||
Map<String, Phase> phases = Collections.singletonMap("mock", mockPhase);
|
||||
lifecyclePolicy = newLockableLifecyclePolicy("test", phases);
|
||||
}
|
||||
|
||||
|
@ -175,6 +182,58 @@ public class IndexLifecycleInitialisationIT extends ESIntegTestCase {
|
|||
});
|
||||
}
|
||||
|
||||
public void testExplainExecution() throws Exception {
|
||||
// start node
|
||||
logger.info("Starting server1");
|
||||
final String server_1 = internalCluster().startNode();
|
||||
logger.info("Creating lifecycle [test_lifecycle]");
|
||||
PutLifecycleAction.Request putLifecycleRequest = new PutLifecycleAction.Request(lifecyclePolicy);
|
||||
PutLifecycleAction.Response putLifecycleResponse = client().execute(PutLifecycleAction.INSTANCE, putLifecycleRequest).get();
|
||||
assertAcked(putLifecycleResponse);
|
||||
|
||||
GetLifecycleAction.Response getLifecycleResponse = client().execute(GetLifecycleAction.INSTANCE,
|
||||
new GetLifecycleAction.Request()).get();
|
||||
assertThat(getLifecycleResponse.getPolicies().size(), equalTo(1));
|
||||
GetLifecycleAction.LifecyclePolicyResponseItem responseItem = getLifecycleResponse.getPolicies().get(0);
|
||||
assertThat(responseItem.getLifecyclePolicy(), equalTo(lifecyclePolicy));
|
||||
assertThat(responseItem.getVersion(), equalTo(1L));
|
||||
long actualModifiedDate = Instant.parse(responseItem.getModifiedDate()).toEpochMilli();
|
||||
|
||||
logger.info("Creating index [test]");
|
||||
CreateIndexResponse createIndexResponse = client().admin().indices().create(createIndexRequest("test").settings(settings))
|
||||
.actionGet();
|
||||
assertAcked(createIndexResponse);
|
||||
|
||||
{
|
||||
PhaseExecutionInfo expectedExecutionInfo = new PhaseExecutionInfo(lifecyclePolicy.getName(), mockPhase, 1L, actualModifiedDate);
|
||||
assertBusy(() -> {
|
||||
ExplainLifecycleRequest explainRequest = new ExplainLifecycleRequest();
|
||||
ExplainLifecycleResponse explainResponse = client().execute(ExplainLifecycleAction.INSTANCE, explainRequest).get();
|
||||
assertThat(explainResponse.getIndexResponses().size(), equalTo(1));
|
||||
IndexLifecycleExplainResponse indexResponse = explainResponse.getIndexResponses().get("test");
|
||||
assertThat(indexResponse.getStep(), equalTo("observable_cluster_state_action"));
|
||||
assertThat(indexResponse.getPhaseExecutionInfo(), equalTo(expectedExecutionInfo));
|
||||
});
|
||||
}
|
||||
|
||||
// complete the step
|
||||
client().admin().indices().prepareUpdateSettings("test")
|
||||
.setSettings(Collections.singletonMap("index.lifecycle.test.complete", true)).get();
|
||||
|
||||
{
|
||||
PhaseExecutionInfo expectedExecutionInfo = new PhaseExecutionInfo(lifecyclePolicy.getName(), null, 1L, actualModifiedDate);
|
||||
assertBusy(() -> {
|
||||
ExplainLifecycleRequest explainRequest = new ExplainLifecycleRequest();
|
||||
ExplainLifecycleResponse explainResponse = client().execute(ExplainLifecycleAction.INSTANCE, explainRequest).get();
|
||||
assertThat(explainResponse.getIndexResponses().size(), equalTo(1));
|
||||
IndexLifecycleExplainResponse indexResponse = explainResponse.getIndexResponses().get("test");
|
||||
assertThat(indexResponse.getPhase(), equalTo(TerminalPolicyStep.COMPLETED_PHASE));
|
||||
assertThat(indexResponse.getStep(), equalTo(TerminalPolicyStep.KEY.getName()));
|
||||
assertThat(indexResponse.getPhaseExecutionInfo(), equalTo(expectedExecutionInfo));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public void testMasterDedicatedDataDedicated() throws Exception {
|
||||
settings = Settings.builder().put(settings).put("index.lifecycle.test.complete", true).build();
|
||||
// start master node
|
||||
|
|
|
@ -896,14 +896,15 @@ public class IndexLifecycleRunnerTests extends ESTestCase {
|
|||
List<LifecyclePolicyMetadata> policyMetadatas = new ArrayList<>();
|
||||
policyMetadatas.add(new LifecyclePolicyMetadata(oldPolicy, Collections.emptyMap(),
|
||||
randomNonNegativeLong(), randomNonNegativeLong()));
|
||||
policyMetadatas.add(new LifecyclePolicyMetadata(newPolicy, Collections.emptyMap(),
|
||||
randomNonNegativeLong(), randomNonNegativeLong()));
|
||||
LifecyclePolicyMetadata newPolicyMetadata = new LifecyclePolicyMetadata(newPolicy, Collections.emptyMap(),
|
||||
randomNonNegativeLong(), randomNonNegativeLong());
|
||||
policyMetadatas.add(newPolicyMetadata);
|
||||
ClusterState clusterState = buildClusterState(indexName, indexSettingsBuilder, policyMetadatas);
|
||||
Index index = clusterState.metaData().index(indexName).getIndex();
|
||||
Index[] indices = new Index[] { index };
|
||||
List<String> failedIndexes = new ArrayList<>();
|
||||
|
||||
ClusterState newClusterState = IndexLifecycleRunner.setPolicyForIndexes(newPolicyName, indices, clusterState, newPolicy,
|
||||
ClusterState newClusterState = IndexLifecycleRunner.setPolicyForIndexes(newPolicyName, indices, clusterState, newPolicyMetadata,
|
||||
failedIndexes, () -> now);
|
||||
|
||||
assertTrue(failedIndexes.isEmpty());
|
||||
|
@ -915,14 +916,16 @@ public class IndexLifecycleRunnerTests extends ESTestCase {
|
|||
String indexName = randomAlphaOfLength(10);
|
||||
String newPolicyName = "new_policy";
|
||||
LifecyclePolicy newPolicy = newTestLifecyclePolicy(newPolicyName, Collections.emptyMap());
|
||||
LifecyclePolicyMetadata newPolicyMetadata = new LifecyclePolicyMetadata(newPolicy, Collections.emptyMap(),
|
||||
randomNonNegativeLong(), randomNonNegativeLong());
|
||||
StepKey currentStep = new StepKey("", "", "");
|
||||
Settings.Builder indexSettingsBuilder = Settings.builder();
|
||||
ClusterState clusterState = buildClusterState(indexName, indexSettingsBuilder, Collections.emptyList());
|
||||
ClusterState clusterState = buildClusterState(indexName, indexSettingsBuilder, Collections.singletonList(newPolicyMetadata));
|
||||
Index index = clusterState.metaData().index(indexName).getIndex();
|
||||
Index[] indices = new Index[] { index };
|
||||
List<String> failedIndexes = new ArrayList<>();
|
||||
|
||||
ClusterState newClusterState = IndexLifecycleRunner.setPolicyForIndexes(newPolicyName, indices, clusterState, newPolicy,
|
||||
ClusterState newClusterState = IndexLifecycleRunner.setPolicyForIndexes(newPolicyName, indices, clusterState, newPolicyMetadata,
|
||||
failedIndexes, () -> now);
|
||||
|
||||
assertTrue(failedIndexes.isEmpty());
|
||||
|
@ -936,6 +939,8 @@ public class IndexLifecycleRunnerTests extends ESTestCase {
|
|||
String newPolicyName = "new_policy";
|
||||
LifecyclePolicy oldPolicy = newTestLifecyclePolicy(oldPolicyName, Collections.emptyMap());
|
||||
LifecyclePolicy newPolicy = newTestLifecyclePolicy(newPolicyName, Collections.emptyMap());
|
||||
LifecyclePolicyMetadata newPolicyMetadata = new LifecyclePolicyMetadata(newPolicy, Collections.emptyMap(),
|
||||
randomNonNegativeLong(), randomNonNegativeLong());
|
||||
StepKey currentStep = AbstractStepTestCase.randomStepKey();
|
||||
Settings.Builder indexSettingsBuilder = Settings.builder().put(LifecycleSettings.LIFECYCLE_NAME, oldPolicyName)
|
||||
.put(LifecycleSettings.LIFECYCLE_PHASE, currentStep.getPhase())
|
||||
|
@ -949,7 +954,7 @@ public class IndexLifecycleRunnerTests extends ESTestCase {
|
|||
Index[] indices = new Index[] { index };
|
||||
List<String> failedIndexes = new ArrayList<>();
|
||||
|
||||
ClusterState newClusterState = IndexLifecycleRunner.setPolicyForIndexes(newPolicyName, indices, clusterState, newPolicy,
|
||||
ClusterState newClusterState = IndexLifecycleRunner.setPolicyForIndexes(newPolicyName, indices, clusterState, newPolicyMetadata,
|
||||
failedIndexes, () -> now);
|
||||
|
||||
assertEquals(1, failedIndexes.size());
|
||||
|
|
|
@ -114,6 +114,7 @@ teardown:
|
|||
- match: { indices.my_index.step: "complete" }
|
||||
- is_false: indices.my_index.failed_step
|
||||
- is_false: indices.my_index.step_info
|
||||
- is_false: indices.my_index.phase_execution
|
||||
|
||||
- is_false: indices.my_index2
|
||||
- is_false: indices.another_index
|
||||
|
@ -135,6 +136,7 @@ teardown:
|
|||
- match: { indices.my_index.step: "complete" }
|
||||
- is_false: indices.my_index.failed_step
|
||||
- is_false: indices.my_index.step_info
|
||||
- is_false: indices.my_index.phase_execution
|
||||
|
||||
- is_true: indices.my_index2.managed
|
||||
- match: { indices.my_index2.index: "my_index2" }
|
||||
|
@ -144,6 +146,7 @@ teardown:
|
|||
- match: { indices.my_index2.step: "complete" }
|
||||
- is_false: indices.my_index2.failed_step
|
||||
- is_false: indices.my_index2.step_info
|
||||
- is_false: indices.my_index2.phase_execution
|
||||
|
||||
- is_false: indices.another_index
|
||||
- is_false: indices.unmanaged_index
|
||||
|
@ -165,6 +168,7 @@ teardown:
|
|||
- match: { indices.my_index.step: "complete" }
|
||||
- is_false: indices.my_index.failed_step
|
||||
- is_false: indices.my_index.step_info
|
||||
- is_false: indices.my_index.phase_execution
|
||||
|
||||
- is_true: indices.my_index2.managed
|
||||
- match: { indices.my_index2.index: "my_index2" }
|
||||
|
@ -174,6 +178,7 @@ teardown:
|
|||
- match: { indices.my_index2.step: "complete" }
|
||||
- is_false: indices.my_index2.failed_step
|
||||
- is_false: indices.my_index2.step_info
|
||||
- is_false: indices.my_index2.phase_execution
|
||||
|
||||
- is_true: indices.another_index.managed
|
||||
- match: { indices.another_index.index: "another_index" }
|
||||
|
@ -183,6 +188,7 @@ teardown:
|
|||
- match: { indices.another_index.step: "complete" }
|
||||
- is_false: indices.another_index.failed_step
|
||||
- is_false: indices.another_index.step_info
|
||||
- is_false: indices.another_index.phase_execution
|
||||
|
||||
- match: { indices.unmanaged_index.index: "unmanaged_index" }
|
||||
- is_false: indices.unmanaged_index.managed
|
||||
|
@ -207,6 +213,7 @@ teardown:
|
|||
- is_false: indices.unmanaged_index.phase
|
||||
- is_false: indices.unmanaged_index.action
|
||||
- is_false: indices.unmanaged_index.step
|
||||
- is_false: indices.unmanaged_index.phase_execution
|
||||
- is_false: indices.another_index.failed_step
|
||||
- is_false: indices.another_index.step_info
|
||||
- is_false: indices.my_index
|
||||
|
|
Loading…
Reference in New Issue