diff --git a/x-pack/plugin/src/main/java/org/elasticsearch/xpack/indexlifecycle/DeleteAction.java b/x-pack/plugin/src/main/java/org/elasticsearch/xpack/indexlifecycle/DeleteAction.java index 384712d0385..98dfd1b4564 100644 --- a/x-pack/plugin/src/main/java/org/elasticsearch/xpack/indexlifecycle/DeleteAction.java +++ b/x-pack/plugin/src/main/java/org/elasticsearch/xpack/indexlifecycle/DeleteAction.java @@ -9,6 +9,7 @@ import org.apache.logging.log4j.Logger; import org.elasticsearch.action.ActionListener; import org.elasticsearch.action.admin.indices.delete.DeleteIndexResponse; import org.elasticsearch.client.Client; +import org.elasticsearch.common.Strings; import org.elasticsearch.common.io.stream.StreamInput; import org.elasticsearch.common.io.stream.StreamOutput; import org.elasticsearch.common.logging.ESLoggerFactory; @@ -66,4 +67,25 @@ public class DeleteAction extends LifecycleAction { }); } + @Override + public int hashCode() { + return 1; + } + + @Override + public boolean equals(Object obj) { + if (obj == null) { + return false; + } + if (obj.getClass() != getClass()) { + return false; + } + return true; + } + + @Override + public String toString() { + return Strings.toString(this); + } + } diff --git a/x-pack/plugin/src/main/java/org/elasticsearch/xpack/indexlifecycle/IndexLifecycleMetadata.java b/x-pack/plugin/src/main/java/org/elasticsearch/xpack/indexlifecycle/IndexLifecycleMetadata.java index 0412bd054bc..2205997bd9f 100644 --- a/x-pack/plugin/src/main/java/org/elasticsearch/xpack/indexlifecycle/IndexLifecycleMetadata.java +++ b/x-pack/plugin/src/main/java/org/elasticsearch/xpack/indexlifecycle/IndexLifecycleMetadata.java @@ -13,6 +13,7 @@ import org.elasticsearch.cluster.NamedDiff; import org.elasticsearch.cluster.metadata.MetaData; import org.elasticsearch.cluster.metadata.MetaData.Custom; import org.elasticsearch.common.ParseField; +import org.elasticsearch.common.Strings; import org.elasticsearch.common.collect.Tuple; import org.elasticsearch.common.io.stream.StreamInput; import org.elasticsearch.common.io.stream.StreamOutput; @@ -23,7 +24,9 @@ import org.elasticsearch.common.xcontent.XContentBuilder; import java.io.IOException; import java.util.Collections; import java.util.EnumSet; +import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.SortedMap; import java.util.TreeMap; @@ -35,7 +38,7 @@ public class IndexLifecycleMetadata implements MetaData.Custom { public static final IndexLifecycleMetadata EMPTY_METADATA = new IndexLifecycleMetadata(Collections.emptySortedMap(), 3); @SuppressWarnings("unchecked") public static final ConstructingObjectParser PARSER = new ConstructingObjectParser<>( - TYPE, a -> new IndexLifecycleMetadata((SortedMap) a[0], (long) a[1])); + TYPE, a -> new IndexLifecycleMetadata(convertListToMapValues((List) a[0]), (long) a[1])); static { PARSER.declareNamedObjects(ConstructingObjectParser.constructorArg(), (p, c, n) -> LifecyclePolicy.parse(p, new Tuple<>(n, c)), v -> { @@ -92,6 +95,14 @@ public class IndexLifecycleMetadata implements MetaData.Custom { return builder; } + private static SortedMap convertListToMapValues(List list) { + SortedMap map = new TreeMap<>(); + for (LifecyclePolicy policy : list) { + map.put(policy.getName(), policy); + } + return map; + } + @Override public Version getMinimalSupportedVersion() { return Version.V_7_0_0_alpha1; @@ -107,6 +118,29 @@ public class IndexLifecycleMetadata implements MetaData.Custom { return MetaData.ALL_CONTEXTS; } + @Override + public int hashCode() { + return Objects.hash(policies, pollInterval); + } + + @Override + public boolean equals(Object obj) { + if (obj == null) { + return false; + } + if (obj.getClass() != getClass()) { + return false; + } + IndexLifecycleMetadata other = (IndexLifecycleMetadata) obj; + return Objects.equals(policies, other.policies) && + Objects.equals(pollInterval, other.pollInterval); + } + + @Override + public String toString() { + return Strings.toString(this, true, true); + } + public static class IndexLifecycleMetadataDiff implements NamedDiff { final Diff> policies; diff --git a/x-pack/plugin/src/main/java/org/elasticsearch/xpack/indexlifecycle/LifecyclePolicy.java b/x-pack/plugin/src/main/java/org/elasticsearch/xpack/indexlifecycle/LifecyclePolicy.java index eb3eb1bca2d..045dde170bb 100644 --- a/x-pack/plugin/src/main/java/org/elasticsearch/xpack/indexlifecycle/LifecyclePolicy.java +++ b/x-pack/plugin/src/main/java/org/elasticsearch/xpack/indexlifecycle/LifecyclePolicy.java @@ -27,6 +27,7 @@ import org.elasticsearch.common.xcontent.XContentParser; import java.io.IOException; import java.util.List; +import java.util.Objects; public class LifecyclePolicy extends AbstractDiffable implements ToXContentObject, Writeable { private static final Logger logger = ESLoggerFactory.getLogger(LifecyclePolicy.class); @@ -78,7 +79,6 @@ public class LifecyclePolicy extends AbstractDiffable implement @Override public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException { builder.startObject(); - builder.array(PHASES_FIELD.getPreferredName(), phases); builder.startObject(PHASES_FIELD.getPreferredName()); for (Phase phase : phases) { builder.field(phase.getName(), phase); @@ -155,4 +155,26 @@ public class LifecyclePolicy extends AbstractDiffable implement currentPhase.execute(idxMeta, client); } } + + @Override + public int hashCode() { + return Objects.hash(name, phases); + } + + @Override + public boolean equals(Object obj) { + if (obj == null) { + return false; + } + if (obj.getClass() != getClass()) { + return false; + } + LifecyclePolicy other = (LifecyclePolicy) obj; + return Objects.equals(name, other.name) && Objects.equals(phases, other.phases); + } + + @Override + public String toString() { + return Strings.toString(this, true, true); + } } diff --git a/x-pack/plugin/src/main/java/org/elasticsearch/xpack/indexlifecycle/Phase.java b/x-pack/plugin/src/main/java/org/elasticsearch/xpack/indexlifecycle/Phase.java index 3da729fa29a..99ff3b9d3d4 100644 --- a/x-pack/plugin/src/main/java/org/elasticsearch/xpack/indexlifecycle/Phase.java +++ b/x-pack/plugin/src/main/java/org/elasticsearch/xpack/indexlifecycle/Phase.java @@ -28,6 +28,7 @@ import org.elasticsearch.common.xcontent.XContentParser; import java.io.IOException; import java.util.List; +import java.util.Objects; public class Phase implements ToXContentObject, Writeable { public static final String PHASE_COMPLETED = "ACTIONS COMPLETED"; @@ -137,7 +138,7 @@ public class Phase implements ToXContentObject, Writeable { @Override public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException { builder.startObject(); - builder.field(AFTER_FIELD.getPreferredName(), after); + builder.field(AFTER_FIELD.getPreferredName(), after.seconds() + "s"); // Need a better way to get a parsable format out here builder.startObject(ACTIONS_FIELD.getPreferredName()); for (LifecycleAction action : actions) { builder.field(action.getWriteableName(), action); @@ -146,5 +147,29 @@ public class Phase implements ToXContentObject, Writeable { builder.endObject(); return builder; } + + @Override + public int hashCode() { + return Objects.hash(name, after, actions); + } + + @Override + public boolean equals(Object obj) { + if (obj == null) { + return false; + } + if (obj.getClass() != getClass()) { + return false; + } + Phase other = (Phase) obj; + return Objects.equals(name, other.name) && + Objects.equals(after, other.after) && + Objects.equals(actions, other.actions); + } + + @Override + public String toString() { + return Strings.toString(this, true, true); + } } diff --git a/x-pack/plugin/src/test/java/org/elasticsearch/xpack/indexlifecycle/DeleteActionTests.java b/x-pack/plugin/src/test/java/org/elasticsearch/xpack/indexlifecycle/DeleteActionTests.java new file mode 100644 index 00000000000..2dc0e1b1c19 --- /dev/null +++ b/x-pack/plugin/src/test/java/org/elasticsearch/xpack/indexlifecycle/DeleteActionTests.java @@ -0,0 +1,31 @@ +/* + * 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; + +import org.elasticsearch.common.io.stream.Writeable.Reader; +import org.elasticsearch.common.xcontent.XContentParser; +import org.elasticsearch.test.AbstractSerializingTestCase; + +import java.io.IOException; + +public class DeleteActionTests extends AbstractSerializingTestCase { + + @Override + protected DeleteAction doParseInstance(XContentParser parser) throws IOException { + return DeleteAction.parse(parser); + } + + @Override + protected DeleteAction createTestInstance() { + return new DeleteAction(); + } + + @Override + protected Reader instanceReader() { + return DeleteAction::new; + } + +} diff --git a/x-pack/plugin/src/test/java/org/elasticsearch/xpack/indexlifecycle/IndexLifecycleMetadataTests.java b/x-pack/plugin/src/test/java/org/elasticsearch/xpack/indexlifecycle/IndexLifecycleMetadataTests.java new file mode 100644 index 00000000000..950563e8a76 --- /dev/null +++ b/x-pack/plugin/src/test/java/org/elasticsearch/xpack/indexlifecycle/IndexLifecycleMetadataTests.java @@ -0,0 +1,77 @@ +/* + * 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; + +import org.elasticsearch.common.ParseField; +import org.elasticsearch.common.io.stream.NamedWriteableRegistry; +import org.elasticsearch.common.io.stream.Writeable.Reader; +import org.elasticsearch.common.unit.TimeValue; +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; +import java.util.SortedMap; +import java.util.TreeMap; + +public class IndexLifecycleMetadataTests extends AbstractSerializingTestCase { + + private NamedXContentRegistry registry; + + @Before + public void setup() { + List entries = Arrays + .asList(new NamedXContentRegistry.Entry(LifecycleAction.class, new ParseField(DeleteAction.NAME), DeleteAction::parse)); + registry = new NamedXContentRegistry(entries); + } + + @Override + protected IndexLifecycleMetadata createTestInstance() { + int numPolicies = 1; // randomInt(5); + SortedMap policies = new TreeMap<>(); + for (int i = 0; i < numPolicies; i++) { + int numberPhases = randomInt(5); + List phases = new ArrayList<>(numberPhases); + for (int j = 0; j < numberPhases; j++) { + TimeValue after = TimeValue.parseTimeValue(randomTimeValue(0, 1000000000, "s", "m", "h", "d"), "test_after"); + List actions = new ArrayList<>(); + if (randomBoolean()) { + actions.add(new DeleteAction()); + } + phases.add(new Phase(randomAlphaOfLength(10), after, actions)); + } + String policyName = randomAlphaOfLength(10); + policies.put(policyName, new LifecyclePolicy(policyName, phases)); + } + long pollInterval = randomNonNegativeLong(); + return new IndexLifecycleMetadata(policies, pollInterval); + } + + @Override + protected String[] getShuffleFieldsExceptions() { + return new String[] { "phases" }; // NOCOMMIT this needs to be temporary since we should not rely on the order of the JSON map + } + + @Override + protected IndexLifecycleMetadata doParseInstance(XContentParser parser) throws IOException { + return IndexLifecycleMetadata.PARSER.apply(parser, registry); + } + + @Override + protected Reader instanceReader() { + return IndexLifecycleMetadata::new; + } + + protected NamedWriteableRegistry getNamedWriteableRegistry() { + return new NamedWriteableRegistry( + Arrays.asList(new NamedWriteableRegistry.Entry(LifecycleAction.class, DeleteAction.NAME, DeleteAction::new))); + } + +} diff --git a/x-pack/plugin/src/test/java/org/elasticsearch/xpack/indexlifecycle/LifecyclePolicyTests.java b/x-pack/plugin/src/test/java/org/elasticsearch/xpack/indexlifecycle/LifecyclePolicyTests.java new file mode 100644 index 00000000000..d4708965aab --- /dev/null +++ b/x-pack/plugin/src/test/java/org/elasticsearch/xpack/indexlifecycle/LifecyclePolicyTests.java @@ -0,0 +1,72 @@ +/* + * 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; + +import org.elasticsearch.common.ParseField; +import org.elasticsearch.common.collect.Tuple; +import org.elasticsearch.common.io.stream.NamedWriteableRegistry; +import org.elasticsearch.common.io.stream.Writeable.Reader; +import org.elasticsearch.common.unit.TimeValue; +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 LifecyclePolicyTests extends AbstractSerializingTestCase { + + private NamedXContentRegistry registry; + private String lifecycleName; + + @Before + public void setup() { + List entries = Arrays + .asList(new NamedXContentRegistry.Entry(LifecycleAction.class, new ParseField(DeleteAction.NAME), DeleteAction::parse)); + registry = new NamedXContentRegistry(entries); + lifecycleName = randomAlphaOfLength(20); // NOCOMMIT we need to randomise the lifecycle name rather + // than use the same name for all instances + } + + @Override + protected LifecyclePolicy createTestInstance() { + int numberPhases = randomInt(5); + List phases = new ArrayList<>(numberPhases); + for (int i = 0; i < numberPhases; i++) { + TimeValue after = TimeValue.parseTimeValue(randomTimeValue(0, 1000000000, "s", "m", "h", "d"), "test_after"); + List actions = new ArrayList<>(); + if (randomBoolean()) { + actions.add(new DeleteAction()); + } + phases.add(new Phase(randomAlphaOfLength(10), after, actions)); + } + return new LifecyclePolicy(lifecycleName, phases); + } + + @Override + protected String[] getShuffleFieldsExceptions() { + return new String[] { "phases" }; // NOCOMMIT this needs to be temporary since we should not rely on the order of the JSON map + } + + @Override + protected LifecyclePolicy doParseInstance(XContentParser parser) throws IOException { + return LifecyclePolicy.parse(parser, new Tuple<>(lifecycleName, registry)); + } + + @Override + protected Reader instanceReader() { + return LifecyclePolicy::new; + } + + protected NamedWriteableRegistry getNamedWriteableRegistry() { + return new NamedWriteableRegistry( + Arrays.asList(new NamedWriteableRegistry.Entry(LifecycleAction.class, DeleteAction.NAME, DeleteAction::new))); + } + +} diff --git a/x-pack/plugin/src/test/java/org/elasticsearch/xpack/indexlifecycle/PhaseTests.java b/x-pack/plugin/src/test/java/org/elasticsearch/xpack/indexlifecycle/PhaseTests.java new file mode 100644 index 00000000000..c1797dc7bc9 --- /dev/null +++ b/x-pack/plugin/src/test/java/org/elasticsearch/xpack/indexlifecycle/PhaseTests.java @@ -0,0 +1,63 @@ +/* + * 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; + +import org.elasticsearch.common.ParseField; +import org.elasticsearch.common.collect.Tuple; +import org.elasticsearch.common.io.stream.NamedWriteableRegistry; +import org.elasticsearch.common.io.stream.Writeable.Reader; +import org.elasticsearch.common.unit.TimeValue; +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 PhaseTests extends AbstractSerializingTestCase { + + private NamedXContentRegistry registry; + private String phaseName; + + @Before + public void setup() { + List entries = Arrays + .asList(new NamedXContentRegistry.Entry(LifecycleAction.class, new ParseField(DeleteAction.NAME), DeleteAction::parse)); + registry = new NamedXContentRegistry(entries); + phaseName = randomAlphaOfLength(20); // NOCOMMIT we need to randomise the phase name rather + // than use the same name for all instances + } + + @Override + protected Phase createTestInstance() { + TimeValue after = TimeValue.parseTimeValue(randomTimeValue(0, 1000000000, "s", "m", "h", "d"), "test_after"); + List actions = new ArrayList<>(); + if (randomBoolean()) { + actions.add(new DeleteAction()); + } + return new Phase(phaseName, after, actions); + } + + @Override + protected Phase doParseInstance(XContentParser parser) throws IOException { + + return Phase.parse(parser, new Tuple<>(phaseName, registry)); + } + + @Override + protected Reader instanceReader() { + return Phase::new; + } + + protected NamedWriteableRegistry getNamedWriteableRegistry() { + return new NamedWriteableRegistry(Arrays + .asList(new NamedWriteableRegistry.Entry(LifecycleAction.class, DeleteAction.NAME, DeleteAction::new))); + } + +}