Makes some fields in Index Lifecycle API optional (#3687)
Specifically this change makes it optional to: * Specify `includes`, `excludes` and `requires`maps in the allocate action as long as at least one fo the options is specified and is not an empty map * Specify an `after` parameter on a phase. If no `after` value is specified `TimeValue.ZERO` is used and the phase will be moved to as soon as the previous phase reports `ACTIONS COMPLETED`. `after` is always non-null when we are serialising the Phase. * Specify a `type` for a LifecyclePolicy. If no `type` is specified `TimeSeriesLifecycleType.INSTANCE` is used since this is currently the only production `type`. `type` is always non-null when we are serialising the LifecyclePolicy.
This commit is contained in:
parent
f2fa988f2f
commit
fd502aa3e6
|
@ -52,9 +52,9 @@ public class AllocateAction implements LifecycleAction {
|
||||||
a -> new AllocateAction((Map<String, String>) a[0], (Map<String, String>) a[1], (Map<String, String>) a[2]));
|
a -> new AllocateAction((Map<String, String>) a[0], (Map<String, String>) a[1], (Map<String, String>) a[2]));
|
||||||
|
|
||||||
static {
|
static {
|
||||||
PARSER.declareObject(ConstructingObjectParser.constructorArg(), (p, c) -> p.mapStrings(), INCLUDE_FIELD);
|
PARSER.declareObject(ConstructingObjectParser.optionalConstructorArg(), (p, c) -> p.mapStrings(), INCLUDE_FIELD);
|
||||||
PARSER.declareObject(ConstructingObjectParser.constructorArg(), (p, c) -> p.mapStrings(), EXCLUDE_FIELD);
|
PARSER.declareObject(ConstructingObjectParser.optionalConstructorArg(), (p, c) -> p.mapStrings(), EXCLUDE_FIELD);
|
||||||
PARSER.declareObject(ConstructingObjectParser.constructorArg(), (p, c) -> p.mapStrings(), REQUIRE_FIELD);
|
PARSER.declareObject(ConstructingObjectParser.optionalConstructorArg(), (p, c) -> p.mapStrings(), REQUIRE_FIELD);
|
||||||
}
|
}
|
||||||
|
|
||||||
private final Map<String, String> include;
|
private final Map<String, String> include;
|
||||||
|
@ -67,9 +67,26 @@ public class AllocateAction implements LifecycleAction {
|
||||||
}
|
}
|
||||||
|
|
||||||
public AllocateAction(Map<String, String> include, Map<String, String> exclude, Map<String, String> require) {
|
public AllocateAction(Map<String, String> include, Map<String, String> exclude, Map<String, String> require) {
|
||||||
this.include = include;
|
if (include == null) {
|
||||||
this.exclude = exclude;
|
this.include = Collections.emptyMap();
|
||||||
this.require = require;
|
} else {
|
||||||
|
this.include = include;
|
||||||
|
}
|
||||||
|
if (exclude == null) {
|
||||||
|
this.exclude = Collections.emptyMap();
|
||||||
|
} else {
|
||||||
|
this.exclude = exclude;
|
||||||
|
}
|
||||||
|
if (require == null) {
|
||||||
|
this.require = Collections.emptyMap();
|
||||||
|
} else {
|
||||||
|
this.require = require;
|
||||||
|
}
|
||||||
|
if (this.include.isEmpty() && this.exclude.isEmpty() && this.require.isEmpty()) {
|
||||||
|
throw new IllegalArgumentException(
|
||||||
|
"At least one of " + INCLUDE_FIELD.getPreferredName() + ", " + EXCLUDE_FIELD.getPreferredName() + " or "
|
||||||
|
+ REQUIRE_FIELD.getPreferredName() + "must contain attributes for action " + NAME);
|
||||||
|
}
|
||||||
FilterAllocationDecider decider = new FilterAllocationDecider(Settings.EMPTY,
|
FilterAllocationDecider decider = new FilterAllocationDecider(Settings.EMPTY,
|
||||||
new ClusterSettings(Settings.EMPTY, ClusterSettings.BUILT_IN_CLUSTER_SETTINGS));
|
new ClusterSettings(Settings.EMPTY, ClusterSettings.BUILT_IN_CLUSTER_SETTINGS));
|
||||||
this.allocationDeciders = new AllocationDeciders(Settings.EMPTY, Collections.singletonList(decider));
|
this.allocationDeciders = new AllocationDeciders(Settings.EMPTY, Collections.singletonList(decider));
|
||||||
|
|
|
@ -28,8 +28,6 @@ import java.util.Objects;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
import static org.elasticsearch.common.xcontent.ConstructingObjectParser.constructorArg;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents the lifecycle of an index from creation to deletion. A
|
* Represents the lifecycle of an index from creation to deletion. A
|
||||||
* {@link LifecyclePolicy} is made up of a set of {@link Phase}s which it will
|
* {@link LifecyclePolicy} is made up of a set of {@link Phase}s which it will
|
||||||
|
@ -54,9 +52,9 @@ public class LifecyclePolicy extends AbstractDiffable<LifecyclePolicy>
|
||||||
return new LifecyclePolicy(type, name, phaseMap);
|
return new LifecyclePolicy(type, name, phaseMap);
|
||||||
});
|
});
|
||||||
static {
|
static {
|
||||||
PARSER.declareField(constructorArg(), (p, c) -> p.namedObject(LifecycleType.class, p.text(), null), TYPE_FIELD,
|
PARSER.declareField(ConstructingObjectParser.optionalConstructorArg(), (p, c) -> p.namedObject(LifecycleType.class, p.text(), null),
|
||||||
ValueType.STRING);
|
TYPE_FIELD, ValueType.STRING);
|
||||||
PARSER.declareNamedObjects(constructorArg(), (p, c, n) -> Phase.parse(p, n), v -> {
|
PARSER.declareNamedObjects(ConstructingObjectParser.constructorArg(), (p, c, n) -> Phase.parse(p, n), v -> {
|
||||||
throw new IllegalArgumentException("ordered " + PHASES_FIELD.getPreferredName() + " are not supported");
|
throw new IllegalArgumentException("ordered " + PHASES_FIELD.getPreferredName() + " are not supported");
|
||||||
}, PHASES_FIELD);
|
}, PHASES_FIELD);
|
||||||
}
|
}
|
||||||
|
@ -73,10 +71,14 @@ public class LifecyclePolicy extends AbstractDiffable<LifecyclePolicy>
|
||||||
* {@link LifecyclePolicy}.
|
* {@link LifecyclePolicy}.
|
||||||
*/
|
*/
|
||||||
public LifecyclePolicy(LifecycleType type, String name, Map<String, Phase> phases) {
|
public LifecyclePolicy(LifecycleType type, String name, Map<String, Phase> phases) {
|
||||||
this.type = type;
|
if (type == null) {
|
||||||
|
this.type = TimeseriesLifecycleType.INSTANCE;
|
||||||
|
} else {
|
||||||
|
this.type = type;
|
||||||
|
}
|
||||||
this.name = name;
|
this.name = name;
|
||||||
this.phases = phases;
|
this.phases = phases;
|
||||||
type.validate(phases.values());
|
this.type.validate(phases.values());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -106,6 +108,13 @@ public class LifecyclePolicy extends AbstractDiffable<LifecyclePolicy>
|
||||||
return name;
|
return name;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the type of this {@link LifecyclePolicy}
|
||||||
|
*/
|
||||||
|
public LifecycleType getType() {
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return the {@link Phase}s for this {@link LifecyclePolicy} in the order
|
* @return the {@link Phase}s for this {@link LifecyclePolicy} in the order
|
||||||
* in which they will be executed.
|
* in which they will be executed.
|
||||||
|
|
|
@ -45,7 +45,7 @@ public class Phase implements ToXContentObject, Writeable {
|
||||||
(a, name) -> new Phase(name, (TimeValue) a[0],
|
(a, name) -> new Phase(name, (TimeValue) a[0],
|
||||||
convertListToMapValues(LifecycleAction::getWriteableName, (List<LifecycleAction>) a[1])));
|
convertListToMapValues(LifecycleAction::getWriteableName, (List<LifecycleAction>) a[1])));
|
||||||
static {
|
static {
|
||||||
PARSER.declareField(ConstructingObjectParser.constructorArg(),
|
PARSER.declareField(ConstructingObjectParser.optionalConstructorArg(),
|
||||||
(p, c) -> TimeValue.parseTimeValue(p.text(), AFTER_FIELD.getPreferredName()), AFTER_FIELD, ValueType.VALUE);
|
(p, c) -> TimeValue.parseTimeValue(p.text(), AFTER_FIELD.getPreferredName()), AFTER_FIELD, ValueType.VALUE);
|
||||||
PARSER.declareNamedObjects(ConstructingObjectParser.constructorArg(),
|
PARSER.declareNamedObjects(ConstructingObjectParser.constructorArg(),
|
||||||
(p, c, n) -> p.namedObject(LifecycleAction.class, n, null), v -> {
|
(p, c, n) -> p.namedObject(LifecycleAction.class, n, null), v -> {
|
||||||
|
@ -75,7 +75,11 @@ public class Phase implements ToXContentObject, Writeable {
|
||||||
*/
|
*/
|
||||||
public Phase(String name, TimeValue after, Map<String, LifecycleAction> actions) {
|
public Phase(String name, TimeValue after, Map<String, LifecycleAction> actions) {
|
||||||
this.name = name;
|
this.name = name;
|
||||||
this.after = after;
|
if (after == null) {
|
||||||
|
this.after = TimeValue.ZERO;
|
||||||
|
} else {
|
||||||
|
this.after = after;
|
||||||
|
}
|
||||||
this.actions = actions;
|
this.actions = actions;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -34,6 +34,7 @@ import org.elasticsearch.test.AbstractSerializingTestCase;
|
||||||
import org.elasticsearch.xpack.indexlifecycle.LifecycleAction.Listener;
|
import org.elasticsearch.xpack.indexlifecycle.LifecycleAction.Listener;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.util.Collections;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.function.BiConsumer;
|
import java.util.function.BiConsumer;
|
||||||
|
@ -49,9 +50,27 @@ public class AllocateActionTests extends AbstractSerializingTestCase<AllocateAct
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected AllocateAction createTestInstance() {
|
protected AllocateAction createTestInstance() {
|
||||||
Map<String, String> includes = randomMap(0, 100);
|
boolean hasAtLeastOneMap = false;
|
||||||
Map<String, String> excludes = randomMap(0, 100);
|
Map<String, String> includes;
|
||||||
Map<String, String> requires = randomMap(0, 100);
|
if (randomBoolean()) {
|
||||||
|
includes = randomMap(0, 100);
|
||||||
|
hasAtLeastOneMap = true;
|
||||||
|
} else {
|
||||||
|
includes = randomBoolean() ? null : Collections.emptyMap();
|
||||||
|
}
|
||||||
|
Map<String, String> excludes;
|
||||||
|
if (randomBoolean()) {
|
||||||
|
hasAtLeastOneMap = true;
|
||||||
|
excludes = randomMap(0, 100);
|
||||||
|
} else {
|
||||||
|
excludes = randomBoolean() ? null : Collections.emptyMap();
|
||||||
|
}
|
||||||
|
Map<String, String> requires;
|
||||||
|
if (hasAtLeastOneMap == false || randomBoolean()) {
|
||||||
|
requires = randomMap(0, 100);
|
||||||
|
} else {
|
||||||
|
requires = randomBoolean() ? null : Collections.emptyMap();
|
||||||
|
}
|
||||||
return new AllocateAction(includes, excludes, requires);
|
return new AllocateAction(includes, excludes, requires);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -84,6 +103,17 @@ public class AllocateActionTests extends AbstractSerializingTestCase<AllocateAct
|
||||||
return new AllocateAction(include, exclude, require);
|
return new AllocateAction(include, exclude, require);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void testAllMapsNullOrEmpty() {
|
||||||
|
Map<String, String> include = randomBoolean() ? null : Collections.emptyMap();
|
||||||
|
Map<String, String> exclude = randomBoolean() ? null : Collections.emptyMap();
|
||||||
|
Map<String, String> require = randomBoolean() ? null : Collections.emptyMap();
|
||||||
|
IllegalArgumentException exception = expectThrows(IllegalArgumentException.class,
|
||||||
|
() -> new AllocateAction(include, exclude, require));
|
||||||
|
assertEquals("At least one of " + AllocateAction.INCLUDE_FIELD.getPreferredName() + ", "
|
||||||
|
+ AllocateAction.EXCLUDE_FIELD.getPreferredName() + " or " + AllocateAction.REQUIRE_FIELD.getPreferredName()
|
||||||
|
+ "must contain attributes for action " + AllocateAction.NAME, exception.getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
public void testExecuteNoExistingSettings() throws Exception {
|
public void testExecuteNoExistingSettings() throws Exception {
|
||||||
Map<String, String> includes = randomMap(1, 5);
|
Map<String, String> includes = randomMap(1, 5);
|
||||||
Map<String, String> excludes = randomMap(1, 5);
|
Map<String, String> excludes = randomMap(1, 5);
|
||||||
|
|
|
@ -98,6 +98,11 @@ public class LifecyclePolicyTests extends AbstractSerializingTestCase<LifecycleP
|
||||||
return LifecyclePolicy::new;
|
return LifecyclePolicy::new;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void testDefaultLifecycleType() {
|
||||||
|
LifecyclePolicy policy = new LifecyclePolicy(null, randomAlphaOfLength(10), Collections.emptyMap());
|
||||||
|
assertSame(TimeseriesLifecycleType.INSTANCE, policy.getType());
|
||||||
|
}
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
public void setupPolicy() {
|
public void setupPolicy() {
|
||||||
indexName = randomAlphaOfLengthBetween(1, 20);
|
indexName = randomAlphaOfLengthBetween(1, 20);
|
||||||
|
|
|
@ -34,7 +34,10 @@ public class PhaseTests extends AbstractSerializingTestCase<Phase> {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Phase createTestInstance() {
|
protected Phase createTestInstance() {
|
||||||
TimeValue after = TimeValue.parseTimeValue(randomTimeValue(0, 1000000000, "s", "m", "h", "d"), "test_after");
|
TimeValue after = null;
|
||||||
|
if (randomBoolean()) {
|
||||||
|
after = TimeValue.parseTimeValue(randomTimeValue(0, 1000000000, "s", "m", "h", "d"), "test_after");
|
||||||
|
}
|
||||||
Map<String, LifecycleAction> actions = Collections.emptyMap();
|
Map<String, LifecycleAction> actions = Collections.emptyMap();
|
||||||
if (randomBoolean()) {
|
if (randomBoolean()) {
|
||||||
actions = Collections.singletonMap(DeleteAction.NAME, new DeleteAction());
|
actions = Collections.singletonMap(DeleteAction.NAME, new DeleteAction());
|
||||||
|
@ -86,6 +89,11 @@ public class PhaseTests extends AbstractSerializingTestCase<Phase> {
|
||||||
return new Phase(name, after, actions);
|
return new Phase(name, after, actions);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void testDefaultAfter() {
|
||||||
|
Phase phase = new Phase(randomAlphaOfLength(20), null, Collections.emptyMap());
|
||||||
|
assertEquals(TimeValue.ZERO, phase.getAfter());
|
||||||
|
}
|
||||||
|
|
||||||
public void testExecuteNewIndexCompleteActions() throws Exception {
|
public void testExecuteNewIndexCompleteActions() throws Exception {
|
||||||
String indexName = randomAlphaOfLengthBetween(1, 20);
|
String indexName = randomAlphaOfLengthBetween(1, 20);
|
||||||
String phaseName = randomAlphaOfLengthBetween(1, 20);
|
String phaseName = randomAlphaOfLengthBetween(1, 20);
|
||||||
|
|
|
@ -5,7 +5,6 @@
|
||||||
*/
|
*/
|
||||||
package org.elasticsearch.xpack.indexlifecycle;
|
package org.elasticsearch.xpack.indexlifecycle;
|
||||||
|
|
||||||
import org.elasticsearch.common.settings.Settings;
|
|
||||||
import org.elasticsearch.common.unit.ByteSizeValue;
|
import org.elasticsearch.common.unit.ByteSizeValue;
|
||||||
import org.elasticsearch.common.unit.TimeValue;
|
import org.elasticsearch.common.unit.TimeValue;
|
||||||
import org.elasticsearch.test.ESTestCase;
|
import org.elasticsearch.test.ESTestCase;
|
||||||
|
|
Loading…
Reference in New Issue