From 94a66c556d82f4490b5ac7a05ab970342ab98d81 Mon Sep 17 00:00:00 2001 From: Tal Levy Date: Mon, 17 Sep 2018 17:00:00 -0700 Subject: [PATCH] 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 --- .../ExplainLifecycleResponse.java | 30 +--- .../IndexLifecycleExplainResponse.java | 143 +++++++----------- .../indexlifecycle/PhaseExecutionInfo.java | 130 ++++++++++++++++ .../client/IndexLifecycleIT.java | 11 +- .../ExplainLifecycleResponseTests.java | 29 ++-- .../IndexExplainResponseTests.java | 109 +++---------- .../PhaseExecutionInfoTests.java | 67 ++++++++ .../client/indexlifecycle/PhaseTests.java | 4 + .../IndexLifecycleExplainResponse.java | 62 +++++--- .../indexlifecycle/PhaseExecutionInfo.java | 135 +++++++++++++++++ .../ExplainLifecycleResponseTests.java | 19 +++ .../IndexExplainResponseTests.java | 34 ++++- .../PhaseExecutionInfoTests.java | 88 +++++++++++ .../xpack/core/indexlifecycle/PhaseTests.java | 4 + .../indexlifecycle/IndexLifecycleRunner.java | 39 ++--- .../indexlifecycle/PolicyStepsRegistry.java | 9 +- .../TransportExplainLifecycleAction.java | 49 ++++-- ...ransportSetIndexLifecyclePolicyAction.java | 10 +- .../IndexLifecycleInitialisationIT.java | 61 +++++++- .../IndexLifecycleRunnerTests.java | 17 ++- .../test/ilm/40_explain_lifecycle.yml | 11 +- 21 files changed, 760 insertions(+), 301 deletions(-) create mode 100644 client/rest-high-level/src/main/java/org/elasticsearch/client/indexlifecycle/PhaseExecutionInfo.java create mode 100644 client/rest-high-level/src/test/java/org/elasticsearch/client/indexlifecycle/PhaseExecutionInfoTests.java create mode 100644 x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/indexlifecycle/PhaseExecutionInfo.java create mode 100644 x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/indexlifecycle/PhaseExecutionInfoTests.java diff --git a/client/rest-high-level/src/main/java/org/elasticsearch/client/indexlifecycle/ExplainLifecycleResponse.java b/client/rest-high-level/src/main/java/org/elasticsearch/client/indexlifecycle/ExplainLifecycleResponse.java index 72a41573e46..de2803afe54 100644 --- a/client/rest-high-level/src/main/java/org/elasticsearch/client/indexlifecycle/ExplainLifecycleResponse.java +++ b/client/rest-high-level/src/main/java/org/elasticsearch/client/indexlifecycle/ExplainLifecycleResponse.java @@ -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 indexResponses; @@ -62,9 +58,6 @@ public class ExplainLifecycleResponse extends ActionResponse implements ToXConte return PARSER.apply(parser, null); } - public ExplainLifecycleResponse() { - } - public ExplainLifecycleResponse(Map 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 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); diff --git a/client/rest-high-level/src/main/java/org/elasticsearch/client/indexlifecycle/IndexLifecycleExplainResponse.java b/client/rest-high-level/src/main/java/org/elasticsearch/client/indexlifecycle/IndexLifecycleExplainResponse.java index 651e4970c9e..80434a33874 100644 --- a/client/rest-high-level/src/main/java/org/elasticsearch/client/indexlifecycle/IndexLifecycleExplainResponse.java +++ b/client/rest-high-level/src/main/java/org/elasticsearch/client/indexlifecycle/IndexLifecycleExplainResponse.java @@ -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,23 +49,25 @@ 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 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])); + "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], + (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,30 +103,32 @@ 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 + "]"); + "Unmanaged index response must only contain fields: [" + MANAGED_BY_ILM_FIELD + ", " + INDEX_FIELD + "]"); } } this.index = index; @@ -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 @@ -303,18 +264,19 @@ public class IndexLifecycleExplainResponse implements ToXContentObject, Writeabl } 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); + 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) && + Objects.equals(phaseExecutionInfo, other.phaseExecutionInfo); } @Override @@ -323,3 +285,4 @@ public class IndexLifecycleExplainResponse implements ToXContentObject, Writeabl } } + diff --git a/client/rest-high-level/src/main/java/org/elasticsearch/client/indexlifecycle/PhaseExecutionInfo.java b/client/rest-high-level/src/main/java/org/elasticsearch/client/indexlifecycle/PhaseExecutionInfo.java new file mode 100644 index 00000000000..802ca8834cd --- /dev/null +++ b/client/rest-high-level/src/main/java/org/elasticsearch/client/indexlifecycle/PhaseExecutionInfo.java @@ -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 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 policyName 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; + } +} + diff --git a/client/rest-high-level/src/test/java/org/elasticsearch/client/IndexLifecycleIT.java b/client/rest-high-level/src/test/java/org/elasticsearch/client/IndexLifecycleIT.java index 2b363c4a45c..e893f3a8926 100644 --- a/client/rest-high-level/src/test/java/org/elasticsearch/client/IndexLifecycleIT.java +++ b/client/rest-high-level/src/test/java/org/elasticsearch/client/IndexLifecycleIT.java @@ -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 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 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()); diff --git a/client/rest-high-level/src/test/java/org/elasticsearch/client/indexlifecycle/ExplainLifecycleResponseTests.java b/client/rest-high-level/src/test/java/org/elasticsearch/client/indexlifecycle/ExplainLifecycleResponseTests.java index 348332f2f06..26eacb04b02 100644 --- a/client/rest-high-level/src/test/java/org/elasticsearch/client/indexlifecycle/ExplainLifecycleResponseTests.java +++ b/client/rest-high-level/src/test/java/org/elasticsearch/client/indexlifecycle/ExplainLifecycleResponseTests.java @@ -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 { +public class ExplainLifecycleResponseTests extends AbstractXContentTestCase { @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 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 entries = new ArrayList<>(ClusterModule.getNamedXWriteables()); + entries.add(new NamedXContentRegistry.Entry(LifecycleAction.class, new ParseField(DeleteAction.NAME), DeleteAction::parse)); + return new NamedXContentRegistry(entries); + } } diff --git a/client/rest-high-level/src/test/java/org/elasticsearch/client/indexlifecycle/IndexExplainResponseTests.java b/client/rest-high-level/src/test/java/org/elasticsearch/client/indexlifecycle/IndexExplainResponseTests.java index 681f58cffe8..59a7ce18619 100644 --- a/client/rest-high-level/src/test/java/org/elasticsearch/client/indexlifecycle/IndexExplainResponseTests.java +++ b/client/rest-high-level/src/test/java/org/elasticsearch/client/indexlifecycle/IndexExplainResponseTests.java @@ -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 { +public class IndexExplainResponseTests extends AbstractXContentTestCase { static IndexLifecycleExplainResponse randomIndexExplainResponse() { if (frequently()) { @@ -50,7 +53,8 @@ public class IndexExplainResponseTests extends AbstractSerializingTestCase 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 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 entries = new ArrayList<>(ClusterModule.getNamedXWriteables()); + entries.add(new NamedXContentRegistry.Entry(LifecycleAction.class, new ParseField(DeleteAction.NAME), DeleteAction::parse)); + return new NamedXContentRegistry(entries); + } } diff --git a/client/rest-high-level/src/test/java/org/elasticsearch/client/indexlifecycle/PhaseExecutionInfoTests.java b/client/rest-high-level/src/test/java/org/elasticsearch/client/indexlifecycle/PhaseExecutionInfoTests.java new file mode 100644 index 00000000000..0db9b56aea9 --- /dev/null +++ b/client/rest-high-level/src/test/java/org/elasticsearch/client/indexlifecycle/PhaseExecutionInfoTests.java @@ -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 { + + 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 entries = new ArrayList<>(ClusterModule.getNamedXWriteables()); + entries.add(new NamedXContentRegistry.Entry(LifecycleAction.class, new ParseField(DeleteAction.NAME), DeleteAction::parse)); + return new NamedXContentRegistry(entries); + } +} diff --git a/client/rest-high-level/src/test/java/org/elasticsearch/client/indexlifecycle/PhaseTests.java b/client/rest-high-level/src/test/java/org/elasticsearch/client/indexlifecycle/PhaseTests.java index 1865c4143d2..3b4fc2fec60 100644 --- a/client/rest-high-level/src/test/java/org/elasticsearch/client/indexlifecycle/PhaseTests.java +++ b/client/rest-high-level/src/test/java/org/elasticsearch/client/indexlifecycle/PhaseTests.java @@ -41,6 +41,10 @@ public class PhaseTests extends AbstractXContentTestCase { @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"); diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/indexlifecycle/IndexLifecycleExplainResponse.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/indexlifecycle/IndexLifecycleExplainResponse.java index bbe289950a7..b110697ae07 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/indexlifecycle/IndexLifecycleExplainResponse.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/indexlifecycle/IndexLifecycleExplainResponse.java @@ -39,23 +39,25 @@ 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 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[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])); + (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], + (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,13 +184,14 @@ public class IndexLifecycleExplainResponse implements ToXContentObject, Writeabl out.writeZLong(actionTime); out.writeZLong(stepTime); out.writeOptionalBytesReference(stepInfo); + out.writeOptionalWriteable(phaseExecutionInfo); } } public String getIndex() { return index; } - + public boolean managedByILM() { return managedByILM; } @@ -191,7 +199,7 @@ public class IndexLifecycleExplainResponse implements ToXContentObject, Writeabl public String getPolicyName() { return policyName; } - + public boolean skip() { return skip; } @@ -227,11 +235,15 @@ public class IndexLifecycleExplainResponse implements ToXContentObject, Writeabl public String getFailedStep() { return failedStep; } - + public BytesReference getStepInfo() { 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 diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/indexlifecycle/PhaseExecutionInfo.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/indexlifecycle/PhaseExecutionInfo.java new file mode 100644 index 00000000000..1ba7390ed22 --- /dev/null +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/indexlifecycle/PhaseExecutionInfo.java @@ -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 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 policyName 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; + } + +} diff --git a/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/indexlifecycle/ExplainLifecycleResponseTests.java b/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/indexlifecycle/ExplainLifecycleResponseTests.java index ea72b4df86a..8b64e1128c0 100644 --- a/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/indexlifecycle/ExplainLifecycleResponseTests.java +++ b/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/indexlifecycle/ExplainLifecycleResponseTests.java @@ -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 { @@ -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 entries = new ArrayList<>(ClusterModule.getNamedXWriteables()); + entries.add(new NamedXContentRegistry.Entry(LifecycleAction.class, new ParseField(MockAction.NAME), MockAction::parse)); + return new NamedXContentRegistry(entries); + } } diff --git a/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/indexlifecycle/IndexExplainResponseTests.java b/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/indexlifecycle/IndexExplainResponseTests.java index 8b8f33fe20c..f05def36c4f 100644 --- a/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/indexlifecycle/IndexExplainResponseTests.java +++ b/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/indexlifecycle/IndexExplainResponseTests.java @@ -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; @@ -35,9 +42,10 @@ public class IndexExplainResponseTests extends AbstractSerializingTestCase randomAlphaOfLength(10)).toString())); + 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 : PhaseExecutionInfoTests.randomPhaseExecutionInfo("")); } @Override @@ -70,8 +78,9 @@ public class IndexExplainResponseTests extends AbstractSerializingTestCase 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 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; diff --git a/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/indexlifecycle/PhaseExecutionInfoTests.java b/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/indexlifecycle/PhaseExecutionInfoTests.java new file mode 100644 index 00000000000..9198282a071 --- /dev/null +++ b/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/indexlifecycle/PhaseExecutionInfoTests.java @@ -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 { + + 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 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 entries = new ArrayList<>(ClusterModule.getNamedXWriteables()); + entries.add(new NamedXContentRegistry.Entry(LifecycleAction.class, new ParseField(MockAction.NAME), MockAction::parse)); + return new NamedXContentRegistry(entries); + } +} diff --git a/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/indexlifecycle/PhaseTests.java b/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/indexlifecycle/PhaseTests.java index ea91cefe5c3..0c3530216f2 100644 --- a/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/indexlifecycle/PhaseTests.java +++ b/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/indexlifecycle/PhaseTests.java @@ -33,6 +33,10 @@ public class PhaseTests extends AbstractSerializingTestCase { @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"); diff --git a/x-pack/plugin/ilm/src/main/java/org/elasticsearch/xpack/indexlifecycle/IndexLifecycleRunner.java b/x-pack/plugin/ilm/src/main/java/org/elasticsearch/xpack/indexlifecycle/IndexLifecycleRunner.java index e5637b095fe..6ea936a3a7a 100644 --- a/x-pack/plugin/ilm/src/main/java/org/elasticsearch/xpack/indexlifecycle/IndexLifecycleRunner.java +++ b/x-pack/plugin/ilm/src/main/java/org/elasticsearch/xpack/indexlifecycle/IndexLifecycleRunner.java @@ -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 failedIndexes, LongSupplier nowSupplier) { + LifecyclePolicyMetadata newPolicyMetadata, List 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); diff --git a/x-pack/plugin/ilm/src/main/java/org/elasticsearch/xpack/indexlifecycle/PolicyStepsRegistry.java b/x-pack/plugin/ilm/src/main/java/org/elasticsearch/xpack/indexlifecycle/PolicyStepsRegistry.java index ec4ff9d8d99..50060d16967 100644 --- a/x-pack/plugin/ilm/src/main/java/org/elasticsearch/xpack/indexlifecycle/PolicyStepsRegistry.java +++ b/x-pack/plugin/ilm/src/main/java/org/elasticsearch/xpack/indexlifecycle/PolicyStepsRegistry.java @@ -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 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); } diff --git a/x-pack/plugin/ilm/src/main/java/org/elasticsearch/xpack/indexlifecycle/action/TransportExplainLifecycleAction.java b/x-pack/plugin/ilm/src/main/java/org/elasticsearch/xpack/indexlifecycle/action/TransportExplainLifecycleAction.java index 310d18ab1ce..5c4eaeeef5a 100644 --- a/x-pack/plugin/ilm/src/main/java/org/elasticsearch/xpack/indexlifecycle/action/TransportExplainLifecycleAction.java +++ b/x-pack/plugin/ilm/src/main/java/org/elasticsearch/xpack/indexlifecycle/action/TransportExplainLifecycleAction.java @@ -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 { + 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,19 +77,34 @@ 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, - 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))); + 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)), + phaseExecutionInfo); } else { indexResponse = IndexLifecycleExplainResponse.newUnmanagedIndexResponse(index); } diff --git a/x-pack/plugin/ilm/src/main/java/org/elasticsearch/xpack/indexlifecycle/action/TransportSetIndexLifecyclePolicyAction.java b/x-pack/plugin/ilm/src/main/java/org/elasticsearch/xpack/indexlifecycle/action/TransportSetIndexLifecyclePolicyAction.java index be2ef13ba94..990053d92a6 100644 --- a/x-pack/plugin/ilm/src/main/java/org/elasticsearch/xpack/indexlifecycle/action/TransportSetIndexLifecyclePolicyAction.java +++ b/x-pack/plugin/ilm/src/main/java/org/elasticsearch/xpack/indexlifecycle/action/TransportSetIndexLifecyclePolicyAction.java @@ -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 diff --git a/x-pack/plugin/ilm/src/test/java/org/elasticsearch/xpack/indexlifecycle/IndexLifecycleInitialisationIT.java b/x-pack/plugin/ilm/src/test/java/org/elasticsearch/xpack/indexlifecycle/IndexLifecycleInitialisationIT.java index 376e3d21750..fe8a5a59636 100644 --- a/x-pack/plugin/ilm/src/test/java/org/elasticsearch/xpack/indexlifecycle/IndexLifecycleInitialisationIT.java +++ b/x-pack/plugin/ilm/src/test/java/org/elasticsearch/xpack/indexlifecycle/IndexLifecycleInitialisationIT.java @@ -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 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 actions = Collections.singletonMap(ObservableAction.NAME, OBSERVABLE_ACTION); - Map phases = Collections.singletonMap("mock", new Phase("mock", TimeValue.timeValueSeconds(0), actions)); + mockPhase = new Phase("mock", TimeValue.timeValueSeconds(0), actions); + Map 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 diff --git a/x-pack/plugin/ilm/src/test/java/org/elasticsearch/xpack/indexlifecycle/IndexLifecycleRunnerTests.java b/x-pack/plugin/ilm/src/test/java/org/elasticsearch/xpack/indexlifecycle/IndexLifecycleRunnerTests.java index f8bfeb89681..e3ce7b082b3 100644 --- a/x-pack/plugin/ilm/src/test/java/org/elasticsearch/xpack/indexlifecycle/IndexLifecycleRunnerTests.java +++ b/x-pack/plugin/ilm/src/test/java/org/elasticsearch/xpack/indexlifecycle/IndexLifecycleRunnerTests.java @@ -896,14 +896,15 @@ public class IndexLifecycleRunnerTests extends ESTestCase { List 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 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 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 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()); diff --git a/x-pack/plugin/src/test/resources/rest-api-spec/test/ilm/40_explain_lifecycle.yml b/x-pack/plugin/src/test/resources/rest-api-spec/test/ilm/40_explain_lifecycle.yml index a6a07507bf5..25800465164 100644 --- a/x-pack/plugin/src/test/resources/rest-api-spec/test/ilm/40_explain_lifecycle.yml +++ b/x-pack/plugin/src/test/resources/rest-api-spec/test/ilm/40_explain_lifecycle.yml @@ -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,7 +178,8 @@ 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" } - match: { indices.another_index.policy: "my_moveable_timeseries_lifecycle" } @@ -183,7 +188,8 @@ 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 - is_false: indices.unmanaged_index.policy @@ -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