Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[7.x] Add _meta field to ilm policy (#73515) #73624

Merged
merged 2 commits into from
Jun 1, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 11 additions & 1 deletion docs/reference/ilm/apis/put-lifecycle.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -47,13 +47,23 @@ include::{es-repo-dir}/rest-api/common-parms.asciidoc[tag=timeoutparms]
[[ilm-put-lifecycle-example]]
==== {api-examples-title}

The following example creates a new policy named `my_policy`:
The following example creates a new policy named `my_policy`. In addition, you can use the
`_meta` parameter to add arbitrary metadata to the policy, the `_meta` parameter is optional
and not automatically generated or used by Elasticsearch. To unset `_meta`, replace the policy
without specifying one. To check the `_meta`, you can use the <<ilm-get-lifecycle,Get lifecycle policy>> API.

[source,console]
--------------------------------------------------
PUT _ilm/policy/my_policy
{
"policy": {
"_meta": {
"description": "used for nginx log",
"project": {
"name": "myProject",
"department": "myDepartment"
}
},
"phases": {
"warm": {
"min_age": "10d",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,11 @@
*/
package org.elasticsearch.xpack.core.ilm;

import org.elasticsearch.Version;
import org.elasticsearch.client.Client;
import org.elasticsearch.cluster.AbstractDiffable;
import org.elasticsearch.cluster.Diffable;
import org.elasticsearch.common.Nullable;
import org.elasticsearch.common.ParseField;
import org.elasticsearch.common.Strings;
import org.elasticsearch.common.io.stream.StreamInput;
Expand Down Expand Up @@ -43,33 +45,51 @@ public class LifecyclePolicy extends AbstractDiffable<LifecyclePolicy>
private static final int MAX_INDEX_NAME_BYTES = 255;

public static final ParseField PHASES_FIELD = new ParseField("phases");
private static final ParseField METADATA = new ParseField("_meta");

@SuppressWarnings("unchecked")
public static final ConstructingObjectParser<LifecyclePolicy, String> PARSER = new ConstructingObjectParser<>("lifecycle_policy", false,
(a, name) -> {
List<Phase> phases = (List<Phase>) a[0];
Map<String, Phase> phaseMap = phases.stream().collect(Collectors.toMap(Phase::getName, Function.identity()));
return new LifecyclePolicy(TimeseriesLifecycleType.INSTANCE, name, phaseMap);
return new LifecyclePolicy(TimeseriesLifecycleType.INSTANCE, name, phaseMap, (Map<String, Object>) a[1]);
});
static {
PARSER.declareNamedObjects(ConstructingObjectParser.constructorArg(), (p, c, n) -> Phase.parse(p, n), v -> {
throw new IllegalArgumentException("ordered " + PHASES_FIELD.getPreferredName() + " are not supported");
}, PHASES_FIELD);
PARSER.declareObject(ConstructingObjectParser.optionalConstructorArg(), (p, c) -> p.map(), METADATA);
}

private final String name;
private final LifecycleType type;
private final Map<String, Phase> phases;
@Nullable
private final Map<String, Object> metadata;

/**
* @param name
* the name of this {@link LifecyclePolicy}
* @param phases
* a {@link Map} of {@link Phase}s which make up this
* {@link LifecyclePolicy}.
*
*/
public LifecyclePolicy(String name, Map<String, Phase> phases) {
this(TimeseriesLifecycleType.INSTANCE, name, phases);
this(TimeseriesLifecycleType.INSTANCE, name, phases, null);
}

/**
* @param name
* the name of this {@link LifecyclePolicy}
* @param phases
* a {@link Map} of {@link Phase}s which make up this
* {@link LifecyclePolicy}.
* @param metadata
* the custom metadata of this {@link LifecyclePolicy}
*/
public LifecyclePolicy(String name, Map<String, Phase> phases, @Nullable Map<String, Object> metadata) {
this(TimeseriesLifecycleType.INSTANCE, name, phases, metadata);
}

/**
Expand All @@ -79,6 +99,11 @@ public LifecyclePolicy(StreamInput in) throws IOException {
type = in.readNamedWriteable(LifecycleType.class);
name = in.readString();
phases = Collections.unmodifiableMap(in.readMap(StreamInput::readString, Phase::new));
if (in.getVersion().onOrAfter(Version.V_7_14_0)) {
this.metadata = in.readMap();
} else {
this.metadata = null;
}
}

/**
Expand All @@ -89,11 +114,14 @@ public LifecyclePolicy(StreamInput in) throws IOException {
* @param phases
* a {@link Map} of {@link Phase}s which make up this
* {@link LifecyclePolicy}.
* @param metadata
* the custom metadata of this {@link LifecyclePolicy}
*/
public LifecyclePolicy(LifecycleType type, String name, Map<String, Phase> phases) {
public LifecyclePolicy(LifecycleType type, String name, Map<String, Phase> phases, @Nullable Map<String, Object> metadata) {
this.name = name;
this.phases = phases;
this.type = type;
this.metadata = metadata;
this.type.validate(phases.values());
}

Expand All @@ -106,6 +134,9 @@ public void writeTo(StreamOutput out) throws IOException {
out.writeNamedWriteable(type);
out.writeString(name);
out.writeMap(phases, StreamOutput::writeString, (o, val) -> val.writeTo(o));
if (out.getVersion().onOrAfter(Version.V_7_14_0)) {
out.writeMap(this.metadata);
}
}

/**
Expand All @@ -130,6 +161,13 @@ public Map<String, Phase> getPhases() {
return phases;
}

/**
* @return the custom metadata of this {@link LifecyclePolicy}
*/
public Map<String, Object> getMetadata() {
return metadata;
}

@Override
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
builder.startObject();
Expand All @@ -138,6 +176,9 @@ public XContentBuilder toXContent(XContentBuilder builder, Params params) throws
builder.field(phase.getName(), phase);
}
builder.endObject();
if (this.metadata != null) {
builder.field(METADATA.getPreferredName(), this.metadata);
}
builder.endObject();
return builder;
}
Expand Down Expand Up @@ -264,7 +305,7 @@ public static void validatePolicyName(String policy) {

@Override
public int hashCode() {
return Objects.hash(name, phases);
return Objects.hash(name, phases, metadata);
}

@Override
Expand All @@ -277,7 +318,8 @@ public boolean equals(Object obj) {
}
LifecyclePolicy other = (LifecyclePolicy) obj;
return Objects.equals(name, other.name) &&
Objects.equals(phases, other.phases);
Objects.equals(phases, other.phases) &&
Objects.equals(metadata, other.metadata);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@
import java.util.List;
import java.util.Map;

import static org.elasticsearch.xpack.core.ilm.LifecyclePolicyTests.randomMeta;

public class LifecyclePolicyMetadataTests extends AbstractSerializingTestCase<LifecyclePolicyMetadata> {

private String lifecycleName;
Expand Down Expand Up @@ -110,7 +112,7 @@ protected LifecyclePolicyMetadata mutateInstance(LifecyclePolicyMetadata instanc
switch (between(0, 3)) {
case 0:
policy = new LifecyclePolicy(TimeseriesLifecycleType.INSTANCE, policy.getName() + randomAlphaOfLengthBetween(1, 5),
policy.getPhases());
policy.getPhases(), randomMeta());
break;
case 1:
headers = new HashMap<>(headers);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ public static LifecyclePolicy randomTimeseriesLifecyclePolicyWithAllPhases(@Null
}
phases.put(phase, new Phase(phase, after, actions));
}
return new LifecyclePolicy(TimeseriesLifecycleType.INSTANCE, lifecycleName, phases);
return new LifecyclePolicy(TimeseriesLifecycleType.INSTANCE, lifecycleName, phases, randomMeta());
}

public static LifecyclePolicy randomTimeseriesLifecyclePolicy(@Nullable String lifecycleName) {
Expand Down Expand Up @@ -203,7 +203,7 @@ public static LifecyclePolicy randomTimeseriesLifecyclePolicy(@Nullable String l
} else {
phases.remove(TimeseriesLifecycleType.FROZEN_PHASE);
}
return new LifecyclePolicy(TimeseriesLifecycleType.INSTANCE, lifecycleName, phases);
return new LifecyclePolicy(TimeseriesLifecycleType.INSTANCE, lifecycleName, phases, randomMeta());
}

private static Function<String, Set<String>> getPhaseToValidActions() {
Expand Down Expand Up @@ -271,7 +271,7 @@ public static LifecyclePolicy randomTestLifecyclePolicy(@Nullable String lifecyc
String phaseName = randomAlphaOfLength(10);
phases.put(phaseName, new Phase(phaseName, after, actions));
}
return new LifecyclePolicy(TestLifecycleType.INSTANCE, lifecycleName, phases);
return new LifecyclePolicy(TestLifecycleType.INSTANCE, lifecycleName, phases, randomMeta());
}

@Override
Expand Down Expand Up @@ -299,7 +299,7 @@ protected LifecyclePolicy mutateInstance(LifecyclePolicy instance) throws IOExce
default:
throw new AssertionError("Illegal randomisation branch");
}
return new LifecyclePolicy(TimeseriesLifecycleType.INSTANCE, name, phases);
return new LifecyclePolicy(TimeseriesLifecycleType.INSTANCE, name, phases, randomMeta());
}

@Override
Expand All @@ -311,7 +311,7 @@ public void testFirstAndLastSteps() {
Client client = mock(Client.class);
lifecycleName = randomAlphaOfLengthBetween(1, 20);
Map<String, Phase> phases = new LinkedHashMap<>();
LifecyclePolicy policy = new LifecyclePolicy(TestLifecycleType.INSTANCE, lifecycleName, phases);
LifecyclePolicy policy = new LifecyclePolicy(TestLifecycleType.INSTANCE, lifecycleName, phases, randomMeta());
List<Step> steps = policy.toSteps(client);
assertThat(steps.size(), equalTo(2));
assertThat(steps.get(0), instanceOf(InitializePolicyContextStep.class));
Expand All @@ -331,7 +331,7 @@ public void testToStepsWithOneStep() {
Map<String, LifecycleAction> actions = Collections.singletonMap(MockAction.NAME, firstAction);
Phase firstPhase = new Phase("test", TimeValue.ZERO, actions);
phases.put(firstPhase.getName(), firstPhase);
LifecyclePolicy policy = new LifecyclePolicy(TestLifecycleType.INSTANCE, lifecycleName, phases);
LifecyclePolicy policy = new LifecyclePolicy(TestLifecycleType.INSTANCE, lifecycleName, phases, randomMeta());
StepKey firstStepKey = InitializePolicyContextStep.KEY;
StepKey secondStepKey = PhaseCompleteStep.finalStep("new").getKey();
List<Step> steps = policy.toSteps(client);
Expand Down Expand Up @@ -366,7 +366,7 @@ public void testToStepsWithTwoPhases() {
Phase secondPhase = new Phase("second_phase", TimeValue.ZERO, secondActions);
phases.put(firstPhase.getName(), firstPhase);
phases.put(secondPhase.getName(), secondPhase);
LifecyclePolicy policy = new LifecyclePolicy(TestLifecycleType.INSTANCE, lifecycleName, phases);
LifecyclePolicy policy = new LifecyclePolicy(TestLifecycleType.INSTANCE, lifecycleName, phases, randomMeta());

List<Step> steps = policy.toSteps(client);
assertThat(steps.size(), equalTo(7));
Expand Down Expand Up @@ -395,7 +395,7 @@ public void testIsActionSafe() {
Phase secondPhase = new Phase("second_phase", TimeValue.ZERO, secondActions);
phases.put(firstPhase.getName(), firstPhase);
phases.put(secondPhase.getName(), secondPhase);
LifecyclePolicy policy = new LifecyclePolicy(TestLifecycleType.INSTANCE, lifecycleName, phases);
LifecyclePolicy policy = new LifecyclePolicy(TestLifecycleType.INSTANCE, lifecycleName, phases, randomMeta());

assertTrue(policy.isActionSafe(new StepKey("first_phase", MockAction.NAME, randomAlphaOfLength(10))));

Expand Down Expand Up @@ -428,4 +428,17 @@ public void testValidatePolicyName() {

LifecyclePolicy.validatePolicyName(randomAlphaOfLengthBetween(1, 255));
}

public static Map<String, Object> randomMeta() {
if (randomBoolean()) {
if (randomBoolean()) {
return Collections.singletonMap(randomAlphaOfLength(4), randomAlphaOfLength(4));
} else {
return Collections.singletonMap(randomAlphaOfLength(5),
Collections.singletonMap(randomAlphaOfLength(4), randomAlphaOfLength(4)));
}
} else {
return null;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -162,7 +162,7 @@ private List<Step> parseStepsFromPhase(String policy, String currentPhase, Strin
if (phaseExecutionInfo.getPhase() != null) {
phaseMap.put(currentPhase, phaseExecutionInfo.getPhase());
}
policyToExecute = new LifecyclePolicy(currentPolicy.getType(), currentPolicy.getName(), phaseMap);
policyToExecute = new LifecyclePolicy(currentPolicy.getType(), currentPolicy.getName(), phaseMap, currentPolicy.getMetadata());
}
LifecyclePolicySecurityClient policyClient = new LifecyclePolicySecurityClient(client,
ClientHelper.INDEX_LIFECYCLE_ORIGIN, lifecyclePolicyMap.get(policy).getHeaders());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@

import java.util.Map;

import static org.elasticsearch.xpack.core.ilm.LifecyclePolicyTests.randomMeta;

/**
* This class is here for constructing instances of {@link LifecyclePolicy} that differs from
* the main {@link TimeseriesLifecycleType} one. Since the more generic constructor is package-private so
Expand All @@ -24,11 +26,11 @@
public class LifecyclePolicyTestsUtils {

public static LifecyclePolicy newTestLifecyclePolicy(String policyName, Map<String, Phase> phases) {
return new LifecyclePolicy(TestLifecycleType.INSTANCE, policyName, phases);
return new LifecyclePolicy(TestLifecycleType.INSTANCE, policyName, phases, randomMeta());
}

public static LifecyclePolicy newLockableLifecyclePolicy(String policyName, Map<String, Phase> phases) {
return new LifecyclePolicy(LockableLifecycleType.INSTANCE, policyName, phases);
return new LifecyclePolicy(LockableLifecycleType.INSTANCE, policyName, phases, randomMeta());
}

public static LifecyclePolicy randomTimeseriesLifecyclePolicy(String policyName) {
Expand Down