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]));
|
||||
|
||||
static {
|
||||
PARSER.declareObject(ConstructingObjectParser.constructorArg(), (p, c) -> p.mapStrings(), INCLUDE_FIELD);
|
||||
PARSER.declareObject(ConstructingObjectParser.constructorArg(), (p, c) -> p.mapStrings(), EXCLUDE_FIELD);
|
||||
PARSER.declareObject(ConstructingObjectParser.constructorArg(), (p, c) -> p.mapStrings(), REQUIRE_FIELD);
|
||||
PARSER.declareObject(ConstructingObjectParser.optionalConstructorArg(), (p, c) -> p.mapStrings(), INCLUDE_FIELD);
|
||||
PARSER.declareObject(ConstructingObjectParser.optionalConstructorArg(), (p, c) -> p.mapStrings(), EXCLUDE_FIELD);
|
||||
PARSER.declareObject(ConstructingObjectParser.optionalConstructorArg(), (p, c) -> p.mapStrings(), REQUIRE_FIELD);
|
||||
}
|
||||
|
||||
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) {
|
||||
if (include == null) {
|
||||
this.include = Collections.emptyMap();
|
||||
} 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,
|
||||
new ClusterSettings(Settings.EMPTY, ClusterSettings.BUILT_IN_CLUSTER_SETTINGS));
|
||||
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.stream.Collectors;
|
||||
|
||||
import static org.elasticsearch.common.xcontent.ConstructingObjectParser.constructorArg;
|
||||
|
||||
/**
|
||||
* 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
|
||||
|
@ -54,9 +52,9 @@ public class LifecyclePolicy extends AbstractDiffable<LifecyclePolicy>
|
|||
return new LifecyclePolicy(type, name, phaseMap);
|
||||
});
|
||||
static {
|
||||
PARSER.declareField(constructorArg(), (p, c) -> p.namedObject(LifecycleType.class, p.text(), null), TYPE_FIELD,
|
||||
ValueType.STRING);
|
||||
PARSER.declareNamedObjects(constructorArg(), (p, c, n) -> Phase.parse(p, n), v -> {
|
||||
PARSER.declareField(ConstructingObjectParser.optionalConstructorArg(), (p, c) -> p.namedObject(LifecycleType.class, p.text(), null),
|
||||
TYPE_FIELD, ValueType.STRING);
|
||||
PARSER.declareNamedObjects(ConstructingObjectParser.constructorArg(), (p, c, n) -> Phase.parse(p, n), v -> {
|
||||
throw new IllegalArgumentException("ordered " + PHASES_FIELD.getPreferredName() + " are not supported");
|
||||
}, PHASES_FIELD);
|
||||
}
|
||||
|
@ -73,10 +71,14 @@ public class LifecyclePolicy extends AbstractDiffable<LifecyclePolicy>
|
|||
* {@link LifecyclePolicy}.
|
||||
*/
|
||||
public LifecyclePolicy(LifecycleType type, String name, Map<String, Phase> phases) {
|
||||
if (type == null) {
|
||||
this.type = TimeseriesLifecycleType.INSTANCE;
|
||||
} else {
|
||||
this.type = type;
|
||||
}
|
||||
this.name = name;
|
||||
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 the type of this {@link LifecyclePolicy}
|
||||
*/
|
||||
public LifecycleType getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the {@link Phase}s for this {@link LifecyclePolicy} in the order
|
||||
* in which they will be executed.
|
||||
|
|
|
@ -45,7 +45,7 @@ public class Phase implements ToXContentObject, Writeable {
|
|||
(a, name) -> new Phase(name, (TimeValue) a[0],
|
||||
convertListToMapValues(LifecycleAction::getWriteableName, (List<LifecycleAction>) a[1])));
|
||||
static {
|
||||
PARSER.declareField(ConstructingObjectParser.constructorArg(),
|
||||
PARSER.declareField(ConstructingObjectParser.optionalConstructorArg(),
|
||||
(p, c) -> TimeValue.parseTimeValue(p.text(), AFTER_FIELD.getPreferredName()), AFTER_FIELD, ValueType.VALUE);
|
||||
PARSER.declareNamedObjects(ConstructingObjectParser.constructorArg(),
|
||||
(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) {
|
||||
this.name = name;
|
||||
if (after == null) {
|
||||
this.after = TimeValue.ZERO;
|
||||
} else {
|
||||
this.after = after;
|
||||
}
|
||||
this.actions = actions;
|
||||
}
|
||||
|
||||
|
|
|
@ -34,6 +34,7 @@ import org.elasticsearch.test.AbstractSerializingTestCase;
|
|||
import org.elasticsearch.xpack.indexlifecycle.LifecycleAction.Listener;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.function.BiConsumer;
|
||||
|
@ -49,9 +50,27 @@ public class AllocateActionTests extends AbstractSerializingTestCase<AllocateAct
|
|||
|
||||
@Override
|
||||
protected AllocateAction createTestInstance() {
|
||||
Map<String, String> includes = randomMap(0, 100);
|
||||
Map<String, String> excludes = randomMap(0, 100);
|
||||
Map<String, String> requires = randomMap(0, 100);
|
||||
boolean hasAtLeastOneMap = false;
|
||||
Map<String, String> includes;
|
||||
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);
|
||||
}
|
||||
|
||||
|
@ -84,6 +103,17 @@ public class AllocateActionTests extends AbstractSerializingTestCase<AllocateAct
|
|||
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 {
|
||||
Map<String, String> includes = randomMap(1, 5);
|
||||
Map<String, String> excludes = randomMap(1, 5);
|
||||
|
|
|
@ -98,6 +98,11 @@ public class LifecyclePolicyTests extends AbstractSerializingTestCase<LifecycleP
|
|||
return LifecyclePolicy::new;
|
||||
}
|
||||
|
||||
public void testDefaultLifecycleType() {
|
||||
LifecyclePolicy policy = new LifecyclePolicy(null, randomAlphaOfLength(10), Collections.emptyMap());
|
||||
assertSame(TimeseriesLifecycleType.INSTANCE, policy.getType());
|
||||
}
|
||||
|
||||
@Before
|
||||
public void setupPolicy() {
|
||||
indexName = randomAlphaOfLengthBetween(1, 20);
|
||||
|
|
|
@ -34,7 +34,10 @@ public class PhaseTests extends AbstractSerializingTestCase<Phase> {
|
|||
|
||||
@Override
|
||||
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();
|
||||
if (randomBoolean()) {
|
||||
actions = Collections.singletonMap(DeleteAction.NAME, new DeleteAction());
|
||||
|
@ -86,6 +89,11 @@ public class PhaseTests extends AbstractSerializingTestCase<Phase> {
|
|||
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 {
|
||||
String indexName = randomAlphaOfLengthBetween(1, 20);
|
||||
String phaseName = randomAlphaOfLengthBetween(1, 20);
|
||||
|
|
|
@ -5,7 +5,6 @@
|
|||
*/
|
||||
package org.elasticsearch.xpack.indexlifecycle;
|
||||
|
||||
import org.elasticsearch.common.settings.Settings;
|
||||
import org.elasticsearch.common.unit.ByteSizeValue;
|
||||
import org.elasticsearch.common.unit.TimeValue;
|
||||
import org.elasticsearch.test.ESTestCase;
|
||||
|
|
Loading…
Reference in New Issue