Adds an explain API endpoint (#31005)

* Adds an explain API endpoint

This endpoint can be used to explain the current lifecycle state of an
index

x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/indexlifec
ycle/action/ExplainLifecycleAction.java
x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/indexlifec
ycle/action/IndexExplainResponse.java
x-pack/plugin/index-lifecycle/src/main/java/org/elasticsearch/xpack/inde
xlifecycle/action/TransportExplainLifecycleAction.java
x-pack/plugin/src/test/resources/rest-api-spec/test/index_lifecycle/20_m
ove_to_step.yml

x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/XPackClien
tPlugin.java
x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/indexlifec
ycle/action/ExplainLifecycleAction.java
x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/indexlifec
ycle/action/IndexExplainResponse.java
x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/indexlifec
ycle/RandomStepInfo.java
x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/indexlifec
ycle/action/ExplainLifecycleRequestTests.java
x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/indexlifec
ycle/action/ExplainLifecycleResponseTests.java
x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/indexlifec
ycle/action/IndexExplainResponseTests.java
x-pack/plugin/index-lifecycle/src/main/java/org/elasticsearch/xpack/inde
xlifecycle/IndexLifecycle.java
x-pack/plugin/index-lifecycle/src/main/java/org/elasticsearch/xpack/inde
xlifecycle/action/RestExplainLifecycleAction.java
x-pack/plugin/index-lifecycle/src/main/java/org/elasticsearch/xpack/inde
xlifecycle/action/TransportExplainLifecycleAction.java
x-pack/plugin/index-lifecycle/src/test/java/org/elasticsearch/xpack/inde
xlifecycle/ExecuteStepsUpdateTaskTests.java
x-pack/plugin/index-lifecycle/src/test/java/org/elasticsearch/xpack/inde
xlifecycle/IndexLifecycleRunnerTests.java
x-pack/plugin/src/test/resources/rest-api-spec/api/xpack.index_lifecycle
.explain_lifecycle.json
x-pack/plugin/src/test/resources/rest-api-spec/test/index_lifecycle/20_m
ove_to_step.yml
x-pack/plugin/src/test/resources/rest-api-spec/test/index_lifecycle/30_e
xplain_lifecycle.yml

* Adds tests for explain API

* Addresses Review comments and fixes REST tests

* Removes RequestBuilder from ExplainLifecycleAction
This commit is contained in:
Colin Goodheart-Smithe 2018-06-01 19:39:47 +01:00 committed by Tal Levy
parent 0f12ecc95f
commit 39844be808
14 changed files with 1152 additions and 49 deletions

View File

@ -28,6 +28,13 @@ import org.elasticsearch.license.LicensesMetaData;
import org.elasticsearch.license.PostStartBasicAction; import org.elasticsearch.license.PostStartBasicAction;
import org.elasticsearch.license.PostStartTrialAction; import org.elasticsearch.license.PostStartTrialAction;
import org.elasticsearch.license.PutLicenseAction; import org.elasticsearch.license.PutLicenseAction;
import org.elasticsearch.persistent.CompletionPersistentTaskAction;
import org.elasticsearch.persistent.PersistentTaskParams;
import org.elasticsearch.persistent.PersistentTasksCustomMetaData;
import org.elasticsearch.persistent.PersistentTasksNodeService;
import org.elasticsearch.persistent.RemovePersistentTaskAction;
import org.elasticsearch.persistent.StartPersistentTaskAction;
import org.elasticsearch.persistent.UpdatePersistentTaskStatusAction;
import org.elasticsearch.plugins.ActionPlugin; import org.elasticsearch.plugins.ActionPlugin;
import org.elasticsearch.plugins.NetworkPlugin; import org.elasticsearch.plugins.NetworkPlugin;
import org.elasticsearch.plugins.Plugin; import org.elasticsearch.plugins.Plugin;
@ -52,6 +59,7 @@ import org.elasticsearch.xpack.core.indexlifecycle.RolloverAction;
import org.elasticsearch.xpack.core.indexlifecycle.ShrinkAction; import org.elasticsearch.xpack.core.indexlifecycle.ShrinkAction;
import org.elasticsearch.xpack.core.indexlifecycle.TimeseriesLifecycleType; import org.elasticsearch.xpack.core.indexlifecycle.TimeseriesLifecycleType;
import org.elasticsearch.xpack.core.indexlifecycle.action.DeleteLifecycleAction; import org.elasticsearch.xpack.core.indexlifecycle.action.DeleteLifecycleAction;
import org.elasticsearch.xpack.core.indexlifecycle.action.ExplainLifecycleAction;
import org.elasticsearch.xpack.core.indexlifecycle.action.GetLifecycleAction; import org.elasticsearch.xpack.core.indexlifecycle.action.GetLifecycleAction;
import org.elasticsearch.xpack.core.indexlifecycle.action.PutLifecycleAction; import org.elasticsearch.xpack.core.indexlifecycle.action.PutLifecycleAction;
import org.elasticsearch.xpack.core.logstash.LogstashFeatureSetUsage; import org.elasticsearch.xpack.core.logstash.LogstashFeatureSetUsage;
@ -76,7 +84,6 @@ import org.elasticsearch.xpack.core.ml.action.GetDatafeedsAction;
import org.elasticsearch.xpack.core.ml.action.GetDatafeedsStatsAction; import org.elasticsearch.xpack.core.ml.action.GetDatafeedsStatsAction;
import org.elasticsearch.xpack.core.ml.action.GetFiltersAction; import org.elasticsearch.xpack.core.ml.action.GetFiltersAction;
import org.elasticsearch.xpack.core.ml.action.GetInfluencersAction; import org.elasticsearch.xpack.core.ml.action.GetInfluencersAction;
import org.elasticsearch.xpack.core.ml.action.MlInfoAction;
import org.elasticsearch.xpack.core.ml.action.GetJobsAction; import org.elasticsearch.xpack.core.ml.action.GetJobsAction;
import org.elasticsearch.xpack.core.ml.action.GetJobsStatsAction; import org.elasticsearch.xpack.core.ml.action.GetJobsStatsAction;
import org.elasticsearch.xpack.core.ml.action.GetModelSnapshotsAction; import org.elasticsearch.xpack.core.ml.action.GetModelSnapshotsAction;
@ -84,6 +91,7 @@ import org.elasticsearch.xpack.core.ml.action.GetOverallBucketsAction;
import org.elasticsearch.xpack.core.ml.action.GetRecordsAction; import org.elasticsearch.xpack.core.ml.action.GetRecordsAction;
import org.elasticsearch.xpack.core.ml.action.IsolateDatafeedAction; import org.elasticsearch.xpack.core.ml.action.IsolateDatafeedAction;
import org.elasticsearch.xpack.core.ml.action.KillProcessAction; import org.elasticsearch.xpack.core.ml.action.KillProcessAction;
import org.elasticsearch.xpack.core.ml.action.MlInfoAction;
import org.elasticsearch.xpack.core.ml.action.OpenJobAction; import org.elasticsearch.xpack.core.ml.action.OpenJobAction;
import org.elasticsearch.xpack.core.ml.action.PersistJobAction; import org.elasticsearch.xpack.core.ml.action.PersistJobAction;
import org.elasticsearch.xpack.core.ml.action.PostCalendarEventsAction; import org.elasticsearch.xpack.core.ml.action.PostCalendarEventsAction;
@ -148,6 +156,8 @@ import org.elasticsearch.xpack.core.security.authc.support.mapper.expressiondsl.
import org.elasticsearch.xpack.core.security.transport.netty4.SecurityNetty4Transport; import org.elasticsearch.xpack.core.security.transport.netty4.SecurityNetty4Transport;
import org.elasticsearch.xpack.core.ssl.SSLService; import org.elasticsearch.xpack.core.ssl.SSLService;
import org.elasticsearch.xpack.core.ssl.action.GetCertificateInfoAction; import org.elasticsearch.xpack.core.ssl.action.GetCertificateInfoAction;
import org.elasticsearch.xpack.core.upgrade.actions.IndexUpgradeAction;
import org.elasticsearch.xpack.core.upgrade.actions.IndexUpgradeInfoAction;
import org.elasticsearch.xpack.core.watcher.WatcherFeatureSetUsage; import org.elasticsearch.xpack.core.watcher.WatcherFeatureSetUsage;
import org.elasticsearch.xpack.core.watcher.WatcherMetaData; import org.elasticsearch.xpack.core.watcher.WatcherMetaData;
import org.elasticsearch.xpack.core.watcher.transport.actions.ack.AckWatchAction; import org.elasticsearch.xpack.core.watcher.transport.actions.ack.AckWatchAction;
@ -158,8 +168,6 @@ import org.elasticsearch.xpack.core.watcher.transport.actions.get.GetWatchAction
import org.elasticsearch.xpack.core.watcher.transport.actions.put.PutWatchAction; import org.elasticsearch.xpack.core.watcher.transport.actions.put.PutWatchAction;
import org.elasticsearch.xpack.core.watcher.transport.actions.service.WatcherServiceAction; import org.elasticsearch.xpack.core.watcher.transport.actions.service.WatcherServiceAction;
import org.elasticsearch.xpack.core.watcher.transport.actions.stats.WatcherStatsAction; import org.elasticsearch.xpack.core.watcher.transport.actions.stats.WatcherStatsAction;
import org.elasticsearch.xpack.core.upgrade.actions.IndexUpgradeAction;
import org.elasticsearch.xpack.core.upgrade.actions.IndexUpgradeInfoAction;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
@ -318,7 +326,8 @@ public class XPackClientPlugin extends Plugin implements ActionPlugin, NetworkPl
// ILM // ILM
DeleteLifecycleAction.INSTANCE, DeleteLifecycleAction.INSTANCE,
GetLifecycleAction.INSTANCE, GetLifecycleAction.INSTANCE,
PutLifecycleAction.INSTANCE PutLifecycleAction.INSTANCE,
ExplainLifecycleAction.INSTANCE
); );
} }

View File

@ -0,0 +1,137 @@
/*
* 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.action;
import org.elasticsearch.action.Action;
import org.elasticsearch.action.ActionRequestValidationException;
import org.elasticsearch.action.ActionResponse;
import org.elasticsearch.action.support.master.info.ClusterInfoRequest;
import org.elasticsearch.common.Strings;
import org.elasticsearch.common.io.stream.StreamInput;
import org.elasticsearch.common.io.stream.StreamOutput;
import org.elasticsearch.common.xcontent.ToXContentObject;
import org.elasticsearch.common.xcontent.XContentBuilder;
import java.io.IOException;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
public class ExplainLifecycleAction
extends Action<ExplainLifecycleAction.Request, ExplainLifecycleAction.Response> {
public static final ExplainLifecycleAction INSTANCE = new ExplainLifecycleAction();
public static final String NAME = "indices:admin/xpack/index_lifecycle/explain";
protected ExplainLifecycleAction() {
super(NAME);
}
@Override
public Response newResponse() {
return new Response();
}
public static class Response extends ActionResponse implements ToXContentObject {
private List<IndexLifecycleExplainResponse> indexResponses;
public Response() {
}
public Response(List<IndexLifecycleExplainResponse> indexResponses) {
this.indexResponses = indexResponses;
}
public List<IndexLifecycleExplainResponse> getIndexResponses() {
return indexResponses;
}
@Override
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
builder.startObject();
for (IndexLifecycleExplainResponse indexResponse : indexResponses) {
builder.field(indexResponse.getIndex(), indexResponse);
}
builder.endObject();
return builder;
}
@Override
public void readFrom(StreamInput in) throws IOException {
indexResponses = in.readList(IndexLifecycleExplainResponse::new);
}
@Override
public void writeTo(StreamOutput out) throws IOException {
out.writeList(indexResponses);
}
@Override
public int hashCode() {
return Objects.hash(indexResponses);
}
@Override
public boolean equals(Object obj) {
if (obj == null) {
return false;
}
if (obj.getClass() != getClass()) {
return false;
}
Response other = (Response) obj;
return Objects.equals(indexResponses, other.indexResponses);
}
@Override
public String toString() {
return Strings.toString(this, true, true);
}
}
public static class Request extends ClusterInfoRequest<Request> {
public Request() {
super();
}
public Request(StreamInput in) throws IOException {
super(in);
}
@Override
public ActionRequestValidationException validate() {
return null;
}
@Override
public int hashCode() {
return Objects.hash(Arrays.hashCode(indices()), indicesOptions());
}
@Override
public boolean equals(Object obj) {
if (obj == null) {
return false;
}
if (obj.getClass() != getClass()) {
return false;
}
Request other = (Request) obj;
return Objects.deepEquals(indices(), other.indices()) &&
Objects.equals(indicesOptions(), other.indicesOptions());
}
@Override
public String toString() {
return "Request [indices()=" + Arrays.toString(indices()) + ", indicesOptions()=" + indicesOptions() + "]";
}
}
}

View File

@ -0,0 +1,312 @@
/*
* 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.action;
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;
import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.common.xcontent.json.JsonXContent;
import org.joda.time.DateTime;
import org.joda.time.chrono.ISOChronology;
import java.io.IOException;
import java.util.Objects;
public class IndexLifecycleExplainResponse implements ToXContentObject, Writeable {
private static final ParseField INDEX_FIELD = new ParseField("index");
private static final ParseField MANAGED_BY_ILM_FIELD = new ParseField("managed");
private static final ParseField POLICY_NAME_FIELD = new ParseField("policy");
private static final ParseField SKIP_FIELD = new ParseField("skip");
private static final ParseField LIFECYCLE_DATE_FIELD = new ParseField("lifecycle_date");
private static final ParseField PHASE_FIELD = new ParseField("phase");
private static final ParseField ACTION_FIELD = new ParseField("action");
private static final ParseField STEP_FIELD = new ParseField("step");
private static final ParseField FAILED_STEP_FIELD = new ParseField("failed_step");
private static final ParseField PHASE_TIME_FIELD = new ParseField("phase_time");
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");
public static final ConstructingObjectParser<IndexLifecycleExplainResponse, Void> PARSER = new ConstructingObjectParser<>(
"index_lifecycle_explain_response",
a -> new IndexLifecycleExplainResponse(
(String) a[0],
(boolean) a[1],
(String) a[2],
(boolean) (a[3] == null ? false: a[3]),
(long) (a[4] == null ? -1L: a[4]),
(String) a[5],
(String) a[6],
(String) a[7],
(String) a[8],
(long) (a[9] == null ? -1L: a[9]),
(long) (a[10] == null ? -1L: a[10]),
(long) (a[11] == null ? -1L: a[11]),
(BytesReference) a[12]));
static {
PARSER.declareString(ConstructingObjectParser.constructorArg(), INDEX_FIELD);
PARSER.declareBoolean(ConstructingObjectParser.constructorArg(), MANAGED_BY_ILM_FIELD);
PARSER.declareString(ConstructingObjectParser.optionalConstructorArg(), POLICY_NAME_FIELD);
PARSER.declareBoolean(ConstructingObjectParser.optionalConstructorArg(), SKIP_FIELD);
PARSER.declareLong(ConstructingObjectParser.optionalConstructorArg(), LIFECYCLE_DATE_FIELD);
PARSER.declareString(ConstructingObjectParser.optionalConstructorArg(), PHASE_FIELD);
PARSER.declareString(ConstructingObjectParser.optionalConstructorArg(), ACTION_FIELD);
PARSER.declareString(ConstructingObjectParser.optionalConstructorArg(), STEP_FIELD);
PARSER.declareString(ConstructingObjectParser.optionalConstructorArg(), FAILED_STEP_FIELD);
PARSER.declareLong(ConstructingObjectParser.optionalConstructorArg(), PHASE_TIME_FIELD);
PARSER.declareLong(ConstructingObjectParser.optionalConstructorArg(), ACTION_TIME_FIELD);
PARSER.declareLong(ConstructingObjectParser.optionalConstructorArg(), STEP_TIME_FIELD);
PARSER.declareObject(ConstructingObjectParser.optionalConstructorArg(), (p, c) -> {
XContentBuilder builder = JsonXContent.contentBuilder();
builder.copyCurrentStructure(p);
return BytesArray.bytes(builder);
}, STEP_INFO_FIELD);
}
private final String index;
private final String policyName;
private final String phase;
private final String action;
private final String step;
private final String failedStep;
private final long lifecycleDate;
private final long phaseTime;
private final long actionTime;
private final long stepTime;
private final boolean skip;
private final boolean managedByILM;
private final BytesReference stepInfo;
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) {
return new IndexLifecycleExplainResponse(index, true, policyName, skip, lifecycleDate, phase, action, step, failedStep, phaseTime,
actionTime, stepTime, stepInfo);
}
public static IndexLifecycleExplainResponse newUnmanagedIndexResponse(String index) {
return new IndexLifecycleExplainResponse(index, false, null, false, -1L, null, null, null, null, -1L, -1L, -1L, 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) {
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) {
throw new IllegalArgumentException(
"Unmanaged index response must only contain fields: [" + MANAGED_BY_ILM_FIELD + ", " + INDEX_FIELD + "]");
}
}
this.index = index;
this.policyName = policyName;
this.managedByILM = managedByILM;
this.skip = skip;
this.lifecycleDate = lifecycleDate;
this.phase = phase;
this.action = action;
this.step = step;
this.phaseTime = phaseTime;
this.actionTime = actionTime;
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.readVLong();
phase = in.readString();
action = in.readString();
step = in.readString();
failedStep = in.readOptionalString();
phaseTime = in.readVLong();
actionTime = in.readVLong();
stepTime = in.readVLong();
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.writeVLong(lifecycleDate);
out.writeString(phase);
out.writeString(action);
out.writeString(step);
out.writeOptionalString(failedStep);
out.writeVLong(phaseTime);
out.writeVLong(actionTime);
out.writeVLong(stepTime);
out.writeOptionalBytesReference(stepInfo);
}
}
public String getIndex() {
return index;
}
public boolean managedByILM() {
return managedByILM;
}
public String getPolicyName() {
return policyName;
}
public boolean skip() {
return skip;
}
public long getLifecycleDate() {
return lifecycleDate;
}
public String getPhase() {
return phase;
}
public long getPhaseTime() {
return phaseTime;
}
public String getAction() {
return action;
}
public long getActionTime() {
return actionTime;
}
public String getStep() {
return step;
}
public long getStepTime() {
return stepTime;
}
public String getFailedStep() {
return failedStep;
}
public BytesReference getStepInfo() {
return stepInfo;
}
@Override
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
builder.startObject();
builder.field(INDEX_FIELD.getPreferredName(), index);
builder.field(MANAGED_BY_ILM_FIELD.getPreferredName(), managedByILM);
if (managedByILM) {
builder.field(POLICY_NAME_FIELD.getPreferredName(), policyName);
builder.field(SKIP_FIELD.getPreferredName(), skip);
if (builder.humanReadable()) {
builder.field(LIFECYCLE_DATE_FIELD.getPreferredName(), new DateTime(lifecycleDate, ISOChronology.getInstanceUTC()));
} else {
builder.field(LIFECYCLE_DATE_FIELD.getPreferredName(), lifecycleDate);
}
builder.field(PHASE_FIELD.getPreferredName(), phase);
if (builder.humanReadable()) {
builder.field(PHASE_TIME_FIELD.getPreferredName(), new DateTime(phaseTime, ISOChronology.getInstanceUTC()));
} else {
builder.field(PHASE_TIME_FIELD.getPreferredName(), phaseTime);
}
builder.field(ACTION_FIELD.getPreferredName(), action);
if (builder.humanReadable()) {
builder.field(ACTION_TIME_FIELD.getPreferredName(), new DateTime(actionTime, ISOChronology.getInstanceUTC()));
} else {
builder.field(ACTION_TIME_FIELD.getPreferredName(), actionTime);
}
builder.field(STEP_FIELD.getPreferredName(), step);
if (builder.humanReadable()) {
builder.field(STEP_TIME_FIELD.getPreferredName(), new DateTime(stepTime, ISOChronology.getInstanceUTC()));
} else {
builder.field(STEP_TIME_FIELD.getPreferredName(), stepTime);
}
if (Strings.hasLength(failedStep)) {
builder.field(FAILED_STEP_FIELD.getPreferredName(), failedStep);
}
if (stepInfo != null && stepInfo.length() > 0) {
builder.rawField(STEP_INFO_FIELD.getPreferredName(), stepInfo.streamInput(), XContentType.JSON);
}
}
builder.endObject();
return builder;
}
@Override
public int hashCode() {
return Objects.hash(index, managedByILM, policyName, skip, lifecycleDate, phase, action, step, failedStep, phaseTime, actionTime,
stepTime, stepInfo);
}
@Override
public boolean equals(Object obj) {
if (obj == null) {
return false;
}
if (obj.getClass() != getClass()) {
return false;
}
IndexLifecycleExplainResponse other = (IndexLifecycleExplainResponse) obj;
return Objects.equals(index, other.index) &&
Objects.equals(managedByILM, other.managedByILM) &&
Objects.equals(policyName, other.policyName) &&
Objects.equals(skip, other.skip) &&
Objects.equals(lifecycleDate, other.lifecycleDate) &&
Objects.equals(phase, other.phase) &&
Objects.equals(action, other.action) &&
Objects.equals(step, other.step) &&
Objects.equals(failedStep, other.failedStep) &&
Objects.equals(phaseTime, other.phaseTime) &&
Objects.equals(actionTime, other.actionTime) &&
Objects.equals(stepTime, other.stepTime) &&
Objects.equals(stepInfo, other.stepInfo);
}
@Override
public String toString() {
return Strings.toString(this, true, true);
}
}

View File

@ -0,0 +1,56 @@
/*
* 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.Strings;
import org.elasticsearch.common.xcontent.ToXContentObject;
import org.elasticsearch.common.xcontent.XContentBuilder;
import java.io.IOException;
import java.util.Objects;
import java.util.function.Supplier;
public class RandomStepInfo implements ToXContentObject {
private final String key;
private final String value;
public RandomStepInfo(Supplier<String> randomStringSupplier) {
this.key = randomStringSupplier.get();
this.value = randomStringSupplier.get();
}
@Override
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
builder.startObject();
builder.field(key, value);
builder.endObject();
return builder;
}
@Override
public int hashCode() {
return Objects.hash(key, value);
}
@Override
public boolean equals(Object obj) {
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
RandomStepInfo other = (RandomStepInfo) obj;
return Objects.equals(key, other.key) && Objects.equals(value, other.value);
}
@Override
public String toString() {
return Strings.toString(this);
}
}

View File

@ -0,0 +1,58 @@
/*
* 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.action;
import org.elasticsearch.action.support.IndicesOptions;
import org.elasticsearch.common.io.stream.Writeable.Reader;
import org.elasticsearch.test.AbstractWireSerializingTestCase;
import org.elasticsearch.xpack.core.indexlifecycle.action.ExplainLifecycleAction.Request;
import java.io.IOException;
public class ExplainLifecycleRequestTests extends AbstractWireSerializingTestCase<Request> {
@Override
protected Request createTestInstance() {
Request request = new Request();
if (randomBoolean()) {
request.indices(generateRandomStringArray(20, 20, false));
}
if (randomBoolean()) {
IndicesOptions indicesOptions = IndicesOptions.fromOptions(randomBoolean(), randomBoolean(), randomBoolean(), randomBoolean(),
randomBoolean(), randomBoolean(), randomBoolean());
request.indicesOptions(indicesOptions);
}
return request;
}
@Override
protected Request mutateInstance(Request instance) throws IOException {
String[] indices = instance.indices();
IndicesOptions indicesOptions = instance.indicesOptions();
switch (between(0, 1)) {
case 0:
indices = generateRandomStringArray(20, 10, false);
break;
case 1:
indicesOptions = randomValueOtherThan(indicesOptions, () -> IndicesOptions.fromOptions(randomBoolean(), randomBoolean(),
randomBoolean(), randomBoolean(), randomBoolean(), randomBoolean(), randomBoolean()));
break;
default:
throw new AssertionError("Illegal randomisation branch");
}
Request newRequest = new Request();
newRequest.indices(indices);
newRequest.indicesOptions(indicesOptions);
return newRequest;
}
@Override
protected Reader<Request> instanceReader() {
return Request::new;
}
}

View File

@ -0,0 +1,56 @@
/*
* 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.action;
import org.elasticsearch.common.io.stream.NamedWriteableRegistry;
import org.elasticsearch.test.AbstractStreamableTestCase;
import org.elasticsearch.xpack.core.indexlifecycle.DeleteAction;
import org.elasticsearch.xpack.core.indexlifecycle.LifecycleAction;
import org.elasticsearch.xpack.core.indexlifecycle.LifecycleType;
import org.elasticsearch.xpack.core.indexlifecycle.TestLifecycleType;
import org.elasticsearch.xpack.core.indexlifecycle.action.ExplainLifecycleAction.Response;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class ExplainLifecycleResponseTests extends AbstractStreamableTestCase<ExplainLifecycleAction.Response> {
@Override
protected Response createTestInstance() {
List<IndexLifecycleExplainResponse> indexResponses = new ArrayList<>();
for (int i = 0; i < randomIntBetween(0, 2); i++) {
indexResponses.add(IndexExplainResponseTests.randomIndexExplainResponse());
}
return new Response(indexResponses);
}
@Override
protected Response createBlankInstance() {
return new Response();
}
protected NamedWriteableRegistry getNamedWriteableRegistry() {
return new NamedWriteableRegistry(
Arrays.asList(new NamedWriteableRegistry.Entry(LifecycleAction.class, DeleteAction.NAME, DeleteAction::new),
new NamedWriteableRegistry.Entry(LifecycleType.class, TestLifecycleType.TYPE, in -> TestLifecycleType.INSTANCE)));
}
@Override
protected Response mutateInstance(Response response) {
List<IndexLifecycleExplainResponse> indexResponses = new ArrayList<>(response.getIndexResponses());
if (indexResponses.size() > 0) {
if (randomBoolean()) {
indexResponses.add(IndexExplainResponseTests.randomIndexExplainResponse());
} else {
indexResponses.remove(indexResponses.size() - 1);
}
} else {
indexResponses.add(IndexExplainResponseTests.randomIndexExplainResponse());
}
return new Response(indexResponses);
}
}

View File

@ -0,0 +1,140 @@
/*
* 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.action;
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.XContentParser;
import org.elasticsearch.test.AbstractSerializingTestCase;
import org.elasticsearch.xpack.core.indexlifecycle.RandomStepInfo;
import java.io.IOException;
public class IndexExplainResponseTests extends AbstractSerializingTestCase<IndexLifecycleExplainResponse> {
static IndexLifecycleExplainResponse randomIndexExplainResponse() {
if (frequently()) {
return randomManagedIndexExplainResponse();
} else {
return randomUnmanagedIndexExplainResponse();
}
}
private static IndexLifecycleExplainResponse randomUnmanagedIndexExplainResponse() {
return IndexLifecycleExplainResponse.newUnmanagedIndexResponse(randomAlphaOfLength(10));
}
private static IndexLifecycleExplainResponse randomManagedIndexExplainResponse() {
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()));
}
@Override
protected IndexLifecycleExplainResponse createTestInstance() {
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");
}
}
}
}

View File

@ -35,16 +35,19 @@ import org.elasticsearch.xpack.core.XPackSettings;
import org.elasticsearch.xpack.core.indexlifecycle.LifecycleSettings; import org.elasticsearch.xpack.core.indexlifecycle.LifecycleSettings;
import org.elasticsearch.xpack.core.indexlifecycle.RolloverAction; import org.elasticsearch.xpack.core.indexlifecycle.RolloverAction;
import org.elasticsearch.xpack.core.indexlifecycle.action.DeleteLifecycleAction; import org.elasticsearch.xpack.core.indexlifecycle.action.DeleteLifecycleAction;
import org.elasticsearch.xpack.core.indexlifecycle.action.ExplainLifecycleAction;
import org.elasticsearch.xpack.core.indexlifecycle.action.GetLifecycleAction; import org.elasticsearch.xpack.core.indexlifecycle.action.GetLifecycleAction;
import org.elasticsearch.xpack.core.indexlifecycle.action.MoveToStepAction; import org.elasticsearch.xpack.core.indexlifecycle.action.MoveToStepAction;
import org.elasticsearch.xpack.core.indexlifecycle.action.PutLifecycleAction; import org.elasticsearch.xpack.core.indexlifecycle.action.PutLifecycleAction;
import org.elasticsearch.xpack.core.indexlifecycle.action.RetryAction; import org.elasticsearch.xpack.core.indexlifecycle.action.RetryAction;
import org.elasticsearch.xpack.indexlifecycle.action.RestDeleteLifecycleAction; import org.elasticsearch.xpack.indexlifecycle.action.RestDeleteLifecycleAction;
import org.elasticsearch.xpack.indexlifecycle.action.RestExplainLifecycleAction;
import org.elasticsearch.xpack.indexlifecycle.action.RestGetLifecycleAction; import org.elasticsearch.xpack.indexlifecycle.action.RestGetLifecycleAction;
import org.elasticsearch.xpack.indexlifecycle.action.RestMoveToStepAction; import org.elasticsearch.xpack.indexlifecycle.action.RestMoveToStepAction;
import org.elasticsearch.xpack.indexlifecycle.action.RestPutLifecycleAction; import org.elasticsearch.xpack.indexlifecycle.action.RestPutLifecycleAction;
import org.elasticsearch.xpack.indexlifecycle.action.RestRetryAction; import org.elasticsearch.xpack.indexlifecycle.action.RestRetryAction;
import org.elasticsearch.xpack.indexlifecycle.action.TransportDeleteLifcycleAction; import org.elasticsearch.xpack.indexlifecycle.action.TransportDeleteLifcycleAction;
import org.elasticsearch.xpack.indexlifecycle.action.TransportExplainLifecycleAction;
import org.elasticsearch.xpack.indexlifecycle.action.TransportGetLifecycleAction; import org.elasticsearch.xpack.indexlifecycle.action.TransportGetLifecycleAction;
import org.elasticsearch.xpack.indexlifecycle.action.TransportMoveToStepAction; import org.elasticsearch.xpack.indexlifecycle.action.TransportMoveToStepAction;
import org.elasticsearch.xpack.indexlifecycle.action.TransportPutLifecycleAction; import org.elasticsearch.xpack.indexlifecycle.action.TransportPutLifecycleAction;
@ -143,6 +146,7 @@ public class IndexLifecycle extends Plugin implements ActionPlugin {
new RestPutLifecycleAction(settings, restController), new RestPutLifecycleAction(settings, restController),
new RestGetLifecycleAction(settings, restController), new RestGetLifecycleAction(settings, restController),
new RestDeleteLifecycleAction(settings, restController), new RestDeleteLifecycleAction(settings, restController),
new RestExplainLifecycleAction(settings, restController),
new RestMoveToStepAction(settings, restController), new RestMoveToStepAction(settings, restController),
new RestRetryAction(settings, restController) new RestRetryAction(settings, restController)
); );
@ -157,6 +161,7 @@ public class IndexLifecycle extends Plugin implements ActionPlugin {
new ActionHandler<>(PutLifecycleAction.INSTANCE, TransportPutLifecycleAction.class), new ActionHandler<>(PutLifecycleAction.INSTANCE, TransportPutLifecycleAction.class),
new ActionHandler<>(GetLifecycleAction.INSTANCE, TransportGetLifecycleAction.class), new ActionHandler<>(GetLifecycleAction.INSTANCE, TransportGetLifecycleAction.class),
new ActionHandler<>(DeleteLifecycleAction.INSTANCE, TransportDeleteLifcycleAction.class), new ActionHandler<>(DeleteLifecycleAction.INSTANCE, TransportDeleteLifcycleAction.class),
new ActionHandler<>(ExplainLifecycleAction.INSTANCE, TransportExplainLifecycleAction.class),
new ActionHandler<>(MoveToStepAction.INSTANCE, TransportMoveToStepAction.class), new ActionHandler<>(MoveToStepAction.INSTANCE, TransportMoveToStepAction.class),
new ActionHandler<>(RetryAction.INSTANCE, TransportRetryAction.class)); new ActionHandler<>(RetryAction.INSTANCE, TransportRetryAction.class));
} }

View File

@ -0,0 +1,43 @@
/*
* 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.indexlifecycle.action;
import org.elasticsearch.action.support.IndicesOptions;
import org.elasticsearch.client.node.NodeClient;
import org.elasticsearch.common.Strings;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.rest.BaseRestHandler;
import org.elasticsearch.rest.RestController;
import org.elasticsearch.rest.RestRequest;
import org.elasticsearch.rest.action.RestToXContentListener;
import org.elasticsearch.xpack.core.indexlifecycle.action.ExplainLifecycleAction;
import org.elasticsearch.xpack.indexlifecycle.IndexLifecycle;
import java.io.IOException;
public class RestExplainLifecycleAction extends BaseRestHandler {
public RestExplainLifecycleAction(Settings settings, RestController controller) {
super(settings);
controller.registerHandler(RestRequest.Method.GET, "_" + IndexLifecycle.NAME + "/explain", this);
controller.registerHandler(RestRequest.Method.GET, "{index}/_" + IndexLifecycle.NAME + "/explain", this);
}
@Override
public String getName() {
return "xpack_lifecycle_explain_action";
}
@Override
protected RestChannelConsumer prepareRequest(RestRequest restRequest, NodeClient client) throws IOException {
String[] indexes = Strings.splitStringByCommaToArray(restRequest.param("index"));
ExplainLifecycleAction.Request explainLifecycleRequest = new ExplainLifecycleAction.Request();
explainLifecycleRequest.indices(indexes);
explainLifecycleRequest.indicesOptions(IndicesOptions.fromRequest(restRequest, IndicesOptions.strictExpandOpen()));
return channel -> client.execute(ExplainLifecycleAction.INSTANCE, explainLifecycleRequest, new RestToXContentListener<>(channel));
}
}

View File

@ -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.indexlifecycle.action;
import org.elasticsearch.action.ActionListener;
import org.elasticsearch.action.support.ActionFilters;
import org.elasticsearch.action.support.master.info.TransportClusterInfoAction;
import org.elasticsearch.cluster.ClusterState;
import org.elasticsearch.cluster.block.ClusterBlockException;
import org.elasticsearch.cluster.block.ClusterBlockLevel;
import org.elasticsearch.cluster.metadata.IndexMetaData;
import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver;
import org.elasticsearch.cluster.service.ClusterService;
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.threadpool.ThreadPool;
import org.elasticsearch.transport.TransportService;
import org.elasticsearch.xpack.core.indexlifecycle.LifecycleSettings;
import org.elasticsearch.xpack.core.indexlifecycle.action.ExplainLifecycleAction;
import org.elasticsearch.xpack.core.indexlifecycle.action.ExplainLifecycleAction.Request;
import org.elasticsearch.xpack.core.indexlifecycle.action.ExplainLifecycleAction.Response;
import org.elasticsearch.xpack.core.indexlifecycle.action.IndexLifecycleExplainResponse;
import java.util.ArrayList;
import java.util.List;
public class TransportExplainLifecycleAction
extends TransportClusterInfoAction<ExplainLifecycleAction.Request, ExplainLifecycleAction.Response> {
@Inject
public TransportExplainLifecycleAction(Settings settings, TransportService transportService, ClusterService clusterService,
ThreadPool threadPool, ActionFilters actionFilters, IndexNameExpressionResolver indexNameExpressionResolver) {
super(settings, ExplainLifecycleAction.NAME, transportService, clusterService, threadPool, actionFilters,
ExplainLifecycleAction.Request::new, indexNameExpressionResolver);
}
@Override
protected Response newResponse() {
return new ExplainLifecycleAction.Response();
}
@Override
protected String executor() {
// very lightweight operation, no need to fork
return ThreadPool.Names.SAME;
}
@Override
protected ClusterBlockException checkBlock(ExplainLifecycleAction.Request request, ClusterState state) {
return state.blocks().indicesBlockedException(ClusterBlockLevel.METADATA_READ,
indexNameExpressionResolver.concreteIndexNames(state, request));
}
@Override
protected void doMasterOperation(Request request, String[] concreteIndices, ClusterState state, ActionListener<Response> listener) {
List<IndexLifecycleExplainResponse> indexReponses = new ArrayList<>();
for (String index : concreteIndices) {
IndexMetaData idxMetadata = state.metaData().index(index);
Settings idxSettings = idxMetadata.getSettings();
String policyName = LifecycleSettings.LIFECYCLE_NAME_SETTING.get(idxSettings);
final IndexLifecycleExplainResponse indexResponse;
if (Strings.hasLength(policyName)) {
indexResponse = IndexLifecycleExplainResponse.newManagedIndexResponse(index, policyName,
LifecycleSettings.LIFECYCLE_SKIP_SETTING.get(idxSettings),
LifecycleSettings.LIFECYCLE_INDEX_CREATION_DATE_SETTING.get(idxSettings),
LifecycleSettings.LIFECYCLE_PHASE_SETTING.get(idxSettings),
LifecycleSettings.LIFECYCLE_ACTION_SETTING.get(idxSettings),
LifecycleSettings.LIFECYCLE_STEP_SETTING.get(idxSettings),
LifecycleSettings.LIFECYCLE_FAILED_STEP_SETTING.get(idxSettings),
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)));
} else {
indexResponse = IndexLifecycleExplainResponse.newUnmanagedIndexResponse(index);
}
indexReponses.add(indexResponse);
}
listener.onResponse(new ExplainLifecycleAction.Response(indexReponses));
}
}

View File

@ -27,6 +27,7 @@ import org.elasticsearch.xpack.core.indexlifecycle.LifecycleSettings;
import org.elasticsearch.xpack.core.indexlifecycle.MockAction; import org.elasticsearch.xpack.core.indexlifecycle.MockAction;
import org.elasticsearch.xpack.core.indexlifecycle.MockStep; import org.elasticsearch.xpack.core.indexlifecycle.MockStep;
import org.elasticsearch.xpack.core.indexlifecycle.Phase; import org.elasticsearch.xpack.core.indexlifecycle.Phase;
import org.elasticsearch.xpack.core.indexlifecycle.RandomStepInfo;
import org.elasticsearch.xpack.core.indexlifecycle.Step; import org.elasticsearch.xpack.core.indexlifecycle.Step;
import org.elasticsearch.xpack.core.indexlifecycle.Step.StepKey; import org.elasticsearch.xpack.core.indexlifecycle.Step.StepKey;
import org.elasticsearch.xpack.core.indexlifecycle.TerminalPolicyStep; import org.elasticsearch.xpack.core.indexlifecycle.TerminalPolicyStep;
@ -181,7 +182,7 @@ public class ExecuteStepsUpdateTaskTests extends ESTestCase {
public void testExecuteIncompleteWaitStepWithInfo() throws IOException { public void testExecuteIncompleteWaitStepWithInfo() throws IOException {
secondStep.setWillComplete(false); secondStep.setWillComplete(false);
IndexLifecycleRunnerTests.RandomStepInfo stepInfo = new IndexLifecycleRunnerTests.RandomStepInfo(); RandomStepInfo stepInfo = new RandomStepInfo(() -> randomAlphaOfLength(10));
secondStep.expectedInfo(stepInfo); secondStep.expectedInfo(stepInfo);
setStateToKey(secondStepKey); setStateToKey(secondStepKey);
Step startStep = policyStepsRegistry.getStep(mixedPolicyName, secondStepKey); Step startStep = policyStepsRegistry.getStep(mixedPolicyName, secondStepKey);

View File

@ -12,7 +12,6 @@ import org.elasticsearch.cluster.ClusterState;
import org.elasticsearch.cluster.metadata.IndexMetaData; import org.elasticsearch.cluster.metadata.IndexMetaData;
import org.elasticsearch.cluster.metadata.MetaData; import org.elasticsearch.cluster.metadata.MetaData;
import org.elasticsearch.cluster.service.ClusterService; import org.elasticsearch.cluster.service.ClusterService;
import org.elasticsearch.common.Strings;
import org.elasticsearch.common.bytes.BytesReference; import org.elasticsearch.common.bytes.BytesReference;
import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.settings.Settings.Builder; import org.elasticsearch.common.settings.Settings.Builder;
@ -30,6 +29,7 @@ import org.elasticsearch.xpack.core.indexlifecycle.InitializePolicyContextStep;
import org.elasticsearch.xpack.core.indexlifecycle.LifecyclePolicyMetadata; import org.elasticsearch.xpack.core.indexlifecycle.LifecyclePolicyMetadata;
import org.elasticsearch.xpack.core.indexlifecycle.LifecycleSettings; import org.elasticsearch.xpack.core.indexlifecycle.LifecycleSettings;
import org.elasticsearch.xpack.core.indexlifecycle.MockStep; import org.elasticsearch.xpack.core.indexlifecycle.MockStep;
import org.elasticsearch.xpack.core.indexlifecycle.RandomStepInfo;
import org.elasticsearch.xpack.core.indexlifecycle.Step; import org.elasticsearch.xpack.core.indexlifecycle.Step;
import org.elasticsearch.xpack.core.indexlifecycle.Step.StepKey; import org.elasticsearch.xpack.core.indexlifecycle.Step.StepKey;
import org.elasticsearch.xpack.core.indexlifecycle.TerminalPolicyStep; import org.elasticsearch.xpack.core.indexlifecycle.TerminalPolicyStep;
@ -240,7 +240,7 @@ public class IndexLifecycleRunnerTests extends ESTestCase {
String policyName = "async_wait_policy"; String policyName = "async_wait_policy";
StepKey stepKey = new StepKey("phase", "action", "async_wait_step"); StepKey stepKey = new StepKey("phase", "action", "async_wait_step");
MockAsyncWaitStep step = new MockAsyncWaitStep(stepKey, null); MockAsyncWaitStep step = new MockAsyncWaitStep(stepKey, null);
RandomStepInfo stepInfo = new RandomStepInfo(); RandomStepInfo stepInfo = new RandomStepInfo(() -> randomAlphaOfLength(10));
step.expectedInfo(stepInfo); step.expectedInfo(stepInfo);
step.setWillComplete(false); step.setWillComplete(false);
PolicyStepsRegistry stepRegistry = createOneStepPolicyStepRegistry(policyName, step); PolicyStepsRegistry stepRegistry = createOneStepPolicyStepRegistry(policyName, step);
@ -753,7 +753,8 @@ public class IndexLifecycleRunnerTests extends ESTestCase {
public void testAddStepInfoToClusterState() throws IOException { public void testAddStepInfoToClusterState() throws IOException {
String indexName = "my_index"; String indexName = "my_index";
StepKey currentStep = new StepKey("current_phase", "current_action", "current_step"); StepKey currentStep = new StepKey("current_phase", "current_action", "current_step");
RandomStepInfo stepInfo = new RandomStepInfo(); RandomStepInfo stepInfo = new RandomStepInfo(() -> randomAlphaOfLength(10));
ClusterState clusterState = buildClusterState(indexName, ClusterState clusterState = buildClusterState(indexName,
Settings.builder().put(LifecycleSettings.LIFECYCLE_PHASE, currentStep.getPhase()) Settings.builder().put(LifecycleSettings.LIFECYCLE_PHASE, currentStep.getPhase())
.put(LifecycleSettings.LIFECYCLE_ACTION, currentStep.getAction()) .put(LifecycleSettings.LIFECYCLE_ACTION, currentStep.getAction())
@ -870,47 +871,6 @@ public class IndexLifecycleRunnerTests extends ESTestCase {
LifecycleSettings.LIFECYCLE_STEP_TIME_SETTING.get(newIndexSettings)); LifecycleSettings.LIFECYCLE_STEP_TIME_SETTING.get(newIndexSettings));
} }
static class RandomStepInfo implements ToXContentObject {
private final String key;
private final String value;
RandomStepInfo() {
this.key = randomAlphaOfLength(20);
this.value = randomAlphaOfLength(20);
}
@Override
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
builder.startObject();
builder.field(key, value);
builder.endObject();
return builder;
}
@Override
public int hashCode() {
return Objects.hash(key, value);
}
@Override
public boolean equals(Object obj) {
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
RandomStepInfo other = (RandomStepInfo) obj;
return Objects.equals(key, other.key) && Objects.equals(value, other.value);
}
@Override
public String toString() {
return Strings.toString(this);
}
}
private static class MockAsyncActionStep extends AsyncActionStep { private static class MockAsyncActionStep extends AsyncActionStep {
private Exception exception; private Exception exception;

View File

@ -0,0 +1,24 @@
{
"xpack.index_lifecycle.explain_lifecycle": {
"documentation": "http://www.elastic.co/guide/en/index_lifecycle/current/index_lifecycle.html",
"methods": [ "GET" ],
"url": {
"path": "/{index}/_index_lifecycle/explain",
"paths": ["/{index}/_index_lifecycle/explain", "/_index_lifecycle/explain"],
"parts": {
"index": {
"type" : "string",
"description" : "The name of the index to explain"
}
},
"params": {
"human": {
"type" : "boolean",
"default" : "false",
"description" : "Return data such as dates in a human readable format"
}
}
},
"body": null
}
}

View File

@ -0,0 +1,214 @@
---
setup:
- do:
cluster.health:
wait_for_status: yellow
- do:
acknowlege: true
xpack.index_lifecycle.put_lifecycle:
lifecycle: "my_moveable_timeseries_lifecycle"
body: |
{
"policy": {
"type": "timeseries",
"phases": {
"warm": {
"after": "1000s",
"actions": {
"forcemerge": {
"max_num_segments": 10000
}
}
},
"hot": {
"after": "1000s",
"actions": { }
}
}
}
}
- do:
acknowledge: true
xpack.index_lifecycle.get_lifecycle:
lifecycle: "my_moveable_timeseries_lifecycle"
- do:
indices.create:
index: my_index
body:
settings:
index.lifecycle.name: "my_moveable_timeseries_lifecycle"
- do:
indices.create:
index: my_index2
body:
settings:
index.lifecycle.name: "my_moveable_timeseries_lifecycle"
- do:
indices.create:
index: another_index
body:
settings:
index.lifecycle.name: "my_moveable_timeseries_lifecycle"
- do:
indices.create:
index: unmanaged_index
body:
settings: {}
- do:
indices.create:
index: my_index_no_policy
---
teardown:
- do:
acknowledge: true
indices.delete:
index: my_index
- do:
acknowledge: true
indices.delete:
index: my_index2
- do:
acknowledge: true
indices.delete:
index: another_index
- do:
acknowledge: true
indices.delete:
index: unmanaged_index
- do:
acknowledge: true
indices.delete:
index: my_index_no_policy
- do:
acknowledge: true
xpack.index_lifecycle.delete_lifecycle:
lifecycle: "my_moveable_timeseries_lifecycle"
- do:
catch: missing
xpack.index_lifecycle.get_lifecycle:
lifecycle: "my_moveable_timeseries_lifecycle"
---
"Test Basic Lifecycle Explain":
- do:
acknowledge: true
xpack.index_lifecycle.explain_lifecycle:
index: "my_index"
- is_true: my_index.managed
- match: { my_index.index: "my_index" }
- match: { my_index.policy: "my_moveable_timeseries_lifecycle" }
- match: { my_index.phase: "new" }
- match: { my_index.action: "after" }
- match: { my_index.step: "after" }
- is_false: my_index.failed_step
- is_false: my_index.step_info
- is_false: my_index2
- is_false: another_index
- is_false: unmanaged_index
---
"Test Wildcard Index Lifecycle Explain":
- do:
acknowledge: true
xpack.index_lifecycle.explain_lifecycle:
index: "my_*"
- is_true: my_index.managed
- match: { my_index.index: "my_index" }
- match: { my_index.policy: "my_moveable_timeseries_lifecycle" }
- match: { my_index.phase: "new" }
- match: { my_index.action: "after" }
- match: { my_index.step: "after" }
- is_false: my_index.failed_step
- is_false: my_index.step_info
- is_true: my_index2.managed
- match: { my_index2.index: "my_index2" }
- match: { my_index2.policy: "my_moveable_timeseries_lifecycle" }
- match: { my_index2.phase: "new" }
- match: { my_index2.action: "after" }
- match: { my_index2.step: "after" }
- is_false: my_index2.failed_step
- is_false: my_index2.step_info
- is_false: another_index
- is_false: unmanaged_index
---
"Test All Indexes Lifecycle Explain":
- do:
acknowledge: true
xpack.index_lifecycle.explain_lifecycle: {}
- is_true: my_index.managed
- match: { my_index.index: "my_index" }
- match: { my_index.policy: "my_moveable_timeseries_lifecycle" }
- match: { my_index.phase: "new" }
- match: { my_index.action: "after" }
- match: { my_index.step: "after" }
- is_false: my_index.failed_step
- is_false: my_index.step_info
- is_true: my_index2.managed
- match: { my_index2.index: "my_index2" }
- match: { my_index2.policy: "my_moveable_timeseries_lifecycle" }
- match: { my_index2.phase: "new" }
- match: { my_index2.action: "after" }
- match: { my_index2.step: "after" }
- is_false: my_index2.failed_step
- is_false: my_index2.step_info
- is_true: another_index.managed
- match: { another_index.index: "another_index" }
- match: { another_index.policy: "my_moveable_timeseries_lifecycle" }
- match: { another_index.phase: "new" }
- match: { another_index.action: "after" }
- match: { another_index.step: "after" }
- is_false: another_index.failed_step
- is_false: another_index.step_info
- match: { unmanaged_index.index: "unmanaged_index" }
- is_false: unmanaged_index.managed
- is_false: unmanaged_index.policy
- is_false: unmanaged_index.phase
- is_false: unmanaged_index.action
- is_false: unmanaged_index.step
- is_false: another_index.failed_step
- is_false: another_index.step_info
---
"Test Unmanaged Index Lifecycle Explain":
- do:
acknowledge: true
xpack.index_lifecycle.explain_lifecycle:
index: "unmanaged_index"
- match: { unmanaged_index.index: "unmanaged_index" }
- is_false: unmanaged_index.managed
- is_false: unmanaged_index.policy
- is_false: unmanaged_index.phase
- is_false: unmanaged_index.action
- is_false: unmanaged_index.step
- is_false: another_index.failed_step
- is_false: another_index.step_info
- is_false: my_index
- is_false: my_index2
- is_false: another_index