Skip to content

Commit b021acb

Browse files
committed
add notion of version and creation_date to LifecyclePolicyMetadata
It is useful to keep track of which version of a policy is currently being executed by a specific index. For management purposes, it would also be useful to know at which time the latest version was inserted so that an audit trail is left for reconciling changes happening in ILM.
1 parent 96d515e commit b021acb

File tree

15 files changed

+190
-74
lines changed

15 files changed

+190
-74
lines changed

x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/indexlifecycle/LifecyclePolicyMetadata.java

Lines changed: 42 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -18,23 +18,33 @@
1818
import org.elasticsearch.common.xcontent.XContentParser;
1919

2020
import java.io.IOException;
21+
import java.time.Instant;
22+
import java.time.ZoneOffset;
23+
import java.time.ZonedDateTime;
2124
import java.util.Map;
2225
import java.util.Objects;
2326

2427
public class LifecyclePolicyMetadata extends AbstractDiffable<LifecyclePolicyMetadata>
2528
implements ToXContentObject, Diffable<LifecyclePolicyMetadata> {
2629

27-
public static final ParseField POLICY = new ParseField("policy");
28-
public static final ParseField HEADERS = new ParseField("headers");
30+
static final ParseField POLICY = new ParseField("policy");
31+
static final ParseField HEADERS = new ParseField("headers");
32+
static final ParseField VERSION = new ParseField("version");
33+
static final ParseField CREATION_DATE = new ParseField("creation_date");
34+
static final ParseField CREATION_DATE_STRING = new ParseField("creation_date_string");
35+
2936
@SuppressWarnings("unchecked")
3037
public static final ConstructingObjectParser<LifecyclePolicyMetadata, String> PARSER = new ConstructingObjectParser<>("policy_metadata",
3138
a -> {
3239
LifecyclePolicy policy = (LifecyclePolicy) a[0];
33-
return new LifecyclePolicyMetadata(policy, (Map<String, String>) a[1]);
40+
return new LifecyclePolicyMetadata(policy, (Map<String, String>) a[1], (long) a[2], (long) a[3]);
3441
});
3542
static {
3643
PARSER.declareObject(ConstructingObjectParser.constructorArg(), LifecyclePolicy::parse, POLICY);
3744
PARSER.declareField(ConstructingObjectParser.constructorArg(), XContentParser::mapStrings, HEADERS, ValueType.OBJECT);
45+
PARSER.declareLong(ConstructingObjectParser.constructorArg(), VERSION);
46+
PARSER.declareLong(ConstructingObjectParser.constructorArg(), CREATION_DATE);
47+
PARSER.declareString(ConstructingObjectParser.constructorArg(), CREATION_DATE_STRING);
3848
}
3949

4050
public static LifecyclePolicyMetadata parse(XContentParser parser, String name) {
@@ -43,16 +53,22 @@ public static LifecyclePolicyMetadata parse(XContentParser parser, String name)
4353

4454
private final LifecyclePolicy policy;
4555
private final Map<String, String> headers;
56+
private final long version;
57+
private final long creationDate;
4658

47-
public LifecyclePolicyMetadata(LifecyclePolicy policy, Map<String, String> headers) {
59+
public LifecyclePolicyMetadata(LifecyclePolicy policy, Map<String, String> headers, long version, long creationDate) {
4860
this.policy = policy;
4961
this.headers = headers;
62+
this.version = version;
63+
this.creationDate = creationDate;
5064
}
5165

5266
@SuppressWarnings("unchecked")
5367
public LifecyclePolicyMetadata(StreamInput in) throws IOException {
5468
this.policy = new LifecyclePolicy(in);
5569
this.headers = (Map<String, String>) in.readGenericValue();
70+
this.version = in.readVLong();
71+
this.creationDate = in.readVLong();
5672
}
5773

5874
public Map<String, String> getHeaders() {
@@ -67,11 +83,27 @@ public String getName() {
6783
return policy.getName();
6884
}
6985

86+
public long getVersion() {
87+
return version;
88+
}
89+
90+
public long getCreationDate() {
91+
return creationDate;
92+
}
93+
94+
public String getCreationDateString() {
95+
ZonedDateTime creationDateTime = ZonedDateTime.ofInstant(Instant.ofEpochMilli(creationDate), ZoneOffset.UTC);
96+
return creationDateTime.toString();
97+
}
98+
7099
@Override
71100
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
72101
builder.startObject();
73102
builder.field(POLICY.getPreferredName(), policy);
74103
builder.field(HEADERS.getPreferredName(), headers);
104+
builder.field(VERSION.getPreferredName(), version);
105+
builder.field(CREATION_DATE.getPreferredName(), creationDate);
106+
builder.field(CREATION_DATE_STRING.getPreferredName(), getCreationDateString());
75107
builder.endObject();
76108
return builder;
77109
}
@@ -80,11 +112,13 @@ public XContentBuilder toXContent(XContentBuilder builder, Params params) throws
80112
public void writeTo(StreamOutput out) throws IOException {
81113
policy.writeTo(out);
82114
out.writeGenericValue(headers);
115+
out.writeVLong(version);
116+
out.writeVLong(creationDate);
83117
}
84118

85119
@Override
86120
public int hashCode() {
87-
return Objects.hash(policy, headers);
121+
return Objects.hash(policy, headers, version, creationDate);
88122
}
89123

90124
@Override
@@ -97,7 +131,9 @@ public boolean equals(Object obj) {
97131
}
98132
LifecyclePolicyMetadata other = (LifecyclePolicyMetadata) obj;
99133
return Objects.equals(policy, other.policy) &&
100-
Objects.equals(headers, other.headers);
134+
Objects.equals(headers, other.headers) &&
135+
Objects.equals(version, other.version) &&
136+
Objects.equals(creationDate, other.creationDate);
101137
}
102138

103139
}

x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/indexlifecycle/action/GetLifecycleAction.java

Lines changed: 20 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
import org.elasticsearch.action.ActionResponse;
1212
import org.elasticsearch.action.support.master.AcknowledgedRequest;
1313
import org.elasticsearch.common.Strings;
14+
import org.elasticsearch.common.collect.Tuple;
1415
import org.elasticsearch.common.io.stream.StreamInput;
1516
import org.elasticsearch.common.io.stream.StreamOutput;
1617
import org.elasticsearch.common.xcontent.ToXContentObject;
@@ -19,7 +20,7 @@
1920

2021
import java.io.IOException;
2122
import java.util.Arrays;
22-
import java.util.List;
23+
import java.util.Map;
2324
import java.util.Objects;
2425

2526
public class GetLifecycleAction extends Action<GetLifecycleAction.Response> {
@@ -37,42 +38,49 @@ public Response newResponse() {
3738

3839
public static class Response extends ActionResponse implements ToXContentObject {
3940

40-
private List<LifecyclePolicy> policies;
41+
private Map<LifecyclePolicy, Tuple<Long, String>> policiesToMetadata;
4142

4243
public Response() {
4344
}
4445

45-
public Response(List<LifecyclePolicy> policies) {
46-
this.policies = policies;
46+
public Response(Map<LifecyclePolicy, Tuple<Long, String>> policiesToMetadata) {
47+
this.policiesToMetadata = policiesToMetadata;
4748
}
4849

49-
public List<LifecyclePolicy> getPolicies() {
50-
return policies;
50+
public Map<LifecyclePolicy, Tuple<Long, String>> getPoliciesToMetadata() {
51+
return policiesToMetadata;
5152
}
5253

5354
@Override
5455
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
5556
builder.startObject();
56-
for (LifecyclePolicy policy : policies) {
57-
builder.field(policy.getName(), policy);
57+
for (Map.Entry<LifecyclePolicy, Tuple<Long, String>> entry : policiesToMetadata.entrySet()) {
58+
builder.startObject(entry.getKey().getName());
59+
builder.field("version", entry.getValue().v1());
60+
builder.field("creation_date", entry.getValue().v2());
61+
builder.field("policy", entry.getKey());
62+
builder.endObject();
5863
}
5964
builder.endObject();
6065
return builder;
6166
}
6267

6368
@Override
6469
public void readFrom(StreamInput in) throws IOException {
65-
policies = in.readList(LifecyclePolicy::new);
70+
policiesToMetadata = in.readMap(LifecyclePolicy::new, (inn) -> new Tuple<>(inn.readVLong(), inn.readString()));
6671
}
6772

6873
@Override
6974
public void writeTo(StreamOutput out) throws IOException {
70-
out.writeList(policies);
75+
out.writeMap(policiesToMetadata, (o, policy) -> policy.writeTo(o), (o, tuple) -> {
76+
o.writeVLong(tuple.v1());
77+
o.writeString(tuple.v2());
78+
});
7179
}
7280

7381
@Override
7482
public int hashCode() {
75-
return Objects.hash(policies);
83+
return Objects.hash(policiesToMetadata);
7684
}
7785

7886
@Override
@@ -84,7 +92,7 @@ public boolean equals(Object obj) {
8492
return false;
8593
}
8694
Response other = (Response) obj;
87-
return Objects.equals(policies, other.policies);
95+
return Objects.equals(policiesToMetadata, other.policiesToMetadata);
8896
}
8997

9098
@Override

x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/indexlifecycle/LifecyclePolicyMetadataTests.java

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,8 @@ protected LifecyclePolicyMetadata createTestInstance() {
7575
for (int i = 0; i < numberHeaders; i++) {
7676
headers.put(randomAlphaOfLength(10), randomAlphaOfLength(10));
7777
}
78-
return new LifecyclePolicyMetadata(LifecyclePolicyTests.randomTimeseriesLifecyclePolicy(lifecycleName), headers);
78+
return new LifecyclePolicyMetadata(LifecyclePolicyTests.randomTimeseriesLifecyclePolicy(lifecycleName), headers,
79+
randomNonNegativeLong(), randomNonNegativeLong());
7980
}
8081

8182
@Override
@@ -87,7 +88,9 @@ protected Reader<LifecyclePolicyMetadata> instanceReader() {
8788
protected LifecyclePolicyMetadata mutateInstance(LifecyclePolicyMetadata instance) throws IOException {
8889
LifecyclePolicy policy = instance.getPolicy();
8990
Map<String, String> headers = instance.getHeaders();
90-
switch (between(0, 1)) {
91+
long version = instance.getVersion();
92+
long creationDate = instance.getCreationDate();
93+
switch (between(0, 3)) {
9194
case 0:
9295
policy = new LifecyclePolicy(TimeseriesLifecycleType.INSTANCE, policy.getName() + randomAlphaOfLengthBetween(1, 5),
9396
policy.getPhases());
@@ -96,10 +99,16 @@ protected LifecyclePolicyMetadata mutateInstance(LifecyclePolicyMetadata instanc
9699
headers = new HashMap<>(headers);
97100
headers.put(randomAlphaOfLength(11), randomAlphaOfLength(11));
98101
break;
102+
case 2:
103+
version++;
104+
break;
105+
case 3:
106+
creationDate++;
107+
break;
99108
default:
100109
throw new AssertionError("Illegal randomisation branch");
101110
}
102-
return new LifecyclePolicyMetadata(policy, headers);
111+
return new LifecyclePolicyMetadata(policy, headers, version, creationDate);
103112
}
104113

105114
}

x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/indexlifecycle/action/GetLifecycleResponseTests.java

Lines changed: 14 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
*/
66
package org.elasticsearch.xpack.core.indexlifecycle.action;
77

8+
import org.elasticsearch.common.collect.Tuple;
89
import org.elasticsearch.common.io.stream.NamedWriteableRegistry;
910
import org.elasticsearch.test.AbstractStreamableTestCase;
1011
import org.elasticsearch.xpack.core.indexlifecycle.LifecycleAction;
@@ -14,9 +15,9 @@
1415
import org.elasticsearch.xpack.core.indexlifecycle.TestLifecycleType;
1516
import org.elasticsearch.xpack.core.indexlifecycle.action.GetLifecycleAction.Response;
1617

17-
import java.util.ArrayList;
1818
import java.util.Arrays;
19-
import java.util.List;
19+
import java.util.HashMap;
20+
import java.util.Map;
2021

2122
import static org.elasticsearch.xpack.core.indexlifecycle.LifecyclePolicyTests.randomTestLifecyclePolicy;
2223

@@ -25,11 +26,12 @@ public class GetLifecycleResponseTests extends AbstractStreamableTestCase<GetLif
2526
@Override
2627
protected Response createTestInstance() {
2728
String randomPrefix = randomAlphaOfLength(5);
28-
List<LifecyclePolicy> policies = new ArrayList<>();
29+
Map<LifecyclePolicy, Tuple<Long, String>> policyMap = new HashMap<>();
2930
for (int i = 0; i < randomIntBetween(0, 2); i++) {
30-
policies.add(randomTestLifecyclePolicy(randomPrefix + i));
31+
policyMap.put(randomTestLifecyclePolicy(randomPrefix + i),
32+
new Tuple<>(randomNonNegativeLong(), randomAlphaOfLength(8)));
3133
}
32-
return new Response(policies);
34+
return new Response(policyMap);
3335
}
3436

3537
@Override
@@ -45,16 +47,17 @@ protected NamedWriteableRegistry getNamedWriteableRegistry() {
4547

4648
@Override
4749
protected Response mutateInstance(Response response) {
48-
List<LifecyclePolicy> policies = new ArrayList<>(response.getPolicies());
49-
if (policies.size() > 0) {
50+
Map<LifecyclePolicy, Tuple<Long, String>> policyMap = new HashMap<>(response.getPoliciesToMetadata());
51+
if (policyMap.size() > 0) {
5052
if (randomBoolean()) {
51-
policies.add(randomTestLifecyclePolicy(randomAlphaOfLength(5)));
53+
policyMap.put(randomTestLifecyclePolicy(randomAlphaOfLength(5)),
54+
new Tuple<>(randomNonNegativeLong(), randomAlphaOfLength(4)));
5255
} else {
53-
policies.remove(policies.size() - 1);
56+
policyMap.remove(randomFrom(response.getPoliciesToMetadata().keySet()));
5457
}
5558
} else {
56-
policies.add(randomTestLifecyclePolicy(randomAlphaOfLength(2)));
59+
policyMap.put(randomTestLifecyclePolicy(randomAlphaOfLength(2)), new Tuple<>(randomNonNegativeLong(), randomAlphaOfLength(4)));
5760
}
58-
return new Response(policies);
61+
return new Response(policyMap);
5962
}
6063
}

x-pack/plugin/ilm/src/main/java/org/elasticsearch/xpack/indexlifecycle/action/TransportGetLifecycleAction.java

Lines changed: 17 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -15,19 +15,23 @@
1515
import org.elasticsearch.cluster.block.ClusterBlockLevel;
1616
import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver;
1717
import org.elasticsearch.cluster.service.ClusterService;
18+
import org.elasticsearch.common.collect.Tuple;
1819
import org.elasticsearch.common.inject.Inject;
1920
import org.elasticsearch.common.settings.Settings;
2021
import org.elasticsearch.threadpool.ThreadPool;
2122
import org.elasticsearch.transport.TransportService;
2223
import org.elasticsearch.xpack.core.indexlifecycle.IndexLifecycleMetadata;
2324
import org.elasticsearch.xpack.core.indexlifecycle.LifecyclePolicy;
25+
import org.elasticsearch.xpack.core.indexlifecycle.LifecyclePolicyMetadata;
2426
import org.elasticsearch.xpack.core.indexlifecycle.action.GetLifecycleAction;
2527
import org.elasticsearch.xpack.core.indexlifecycle.action.GetLifecycleAction.Request;
2628
import org.elasticsearch.xpack.core.indexlifecycle.action.GetLifecycleAction.Response;
2729

2830
import java.util.ArrayList;
2931
import java.util.Arrays;
32+
import java.util.HashMap;
3033
import java.util.List;
34+
import java.util.Map;
3135

3236
public class TransportGetLifecycleAction extends TransportMasterNodeAction<Request, Response> {
3337

@@ -49,32 +53,37 @@ protected Response newResponse() {
4953
}
5054

5155
@Override
52-
protected void masterOperation(Request request, ClusterState state, ActionListener<Response> listener) throws Exception {
56+
protected void masterOperation(Request request, ClusterState state, ActionListener<Response> listener) {
5357
IndexLifecycleMetadata metadata = clusterService.state().metaData().custom(IndexLifecycleMetadata.TYPE);
5458
if (metadata == null) {
5559
listener.onFailure(new ResourceNotFoundException("Lifecycle policy not found: {}", Arrays.toString(request.getPolicyNames())));
5660
} else {
57-
List<LifecyclePolicy> requestedPolicies;
61+
List<LifecyclePolicyMetadata> requestedPolicies;
5862
// if no policies explicitly provided, behave as if `*` was specified
5963
if (request.getPolicyNames().length == 0) {
60-
requestedPolicies = new ArrayList<>(metadata.getPolicies().values());
64+
requestedPolicies = new ArrayList<>(metadata.getPolicyMetadatas().values());
6165
} else {
6266
requestedPolicies = new ArrayList<>(request.getPolicyNames().length);
6367
for (String name : request.getPolicyNames()) {
64-
LifecyclePolicy policy = metadata.getPolicies().get(name);
65-
if (policy == null) {
68+
LifecyclePolicyMetadata policyMetadata = metadata.getPolicyMetadatas().get(name);
69+
if (policyMetadata == null) {
6670
listener.onFailure(new ResourceNotFoundException("Lifecycle policy not found: {}", name));
6771
return;
6872
}
69-
requestedPolicies.add(policy);
73+
requestedPolicies.add(policyMetadata);
7074
}
7175
}
72-
listener.onResponse(new Response(requestedPolicies));
76+
Map<LifecyclePolicy, Tuple<Long, String>> policyMap = new HashMap<>(requestedPolicies.size());
77+
for (LifecyclePolicyMetadata policyMetadata : requestedPolicies) {
78+
policyMap.put(policyMetadata.getPolicy(),
79+
new Tuple<>(policyMetadata.getVersion(), policyMetadata.getCreationDateString()));
80+
}
81+
listener.onResponse(new Response(policyMap));
7382
}
7483
}
7584

7685
@Override
7786
protected ClusterBlockException checkBlock(Request request, ClusterState state) {
7887
return state.blocks().globalBlockedException(ClusterBlockLevel.METADATA_WRITE);
7988
}
80-
}
89+
}

x-pack/plugin/ilm/src/main/java/org/elasticsearch/xpack/indexlifecycle/action/TransportPutLifecycleAction.java

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
import org.elasticsearch.xpack.core.indexlifecycle.action.PutLifecycleAction.Request;
2929
import org.elasticsearch.xpack.core.indexlifecycle.action.PutLifecycleAction.Response;
3030

31+
import java.time.Instant;
3132
import java.util.Map;
3233
import java.util.SortedMap;
3334
import java.util.TreeMap;
@@ -79,9 +80,12 @@ public ClusterState execute(ClusterState currentState) throws Exception {
7980
if (currentMetadata == null) { // first time using index-lifecycle feature, bootstrap metadata
8081
currentMetadata = IndexLifecycleMetadata.EMPTY;
8182
}
82-
// NORELEASE Check if current step exists in new policy and if not move to next available step
83+
LifecyclePolicyMetadata existingPolicyMetadata = currentMetadata.getPolicyMetadatas()
84+
.get(request.getPolicy().getName());
85+
long nextVersion = (existingPolicyMetadata == null) ? 1L : existingPolicyMetadata.getVersion() + 1L;
8386
SortedMap<String, LifecyclePolicyMetadata> newPolicies = new TreeMap<>(currentMetadata.getPolicyMetadatas());
84-
LifecyclePolicyMetadata lifecyclePolicyMetadata = new LifecyclePolicyMetadata(request.getPolicy(), filteredHeaders);
87+
LifecyclePolicyMetadata lifecyclePolicyMetadata = new LifecyclePolicyMetadata(request.getPolicy(), filteredHeaders,
88+
nextVersion, Instant.now().toEpochMilli());
8589
newPolicies.put(lifecyclePolicyMetadata.getName(), lifecyclePolicyMetadata);
8690
IndexLifecycleMetadata newMetadata = new IndexLifecycleMetadata(newPolicies, OperationMode.RUNNING);
8791
newState.metaData(MetaData.builder(currentState.getMetaData())

0 commit comments

Comments
 (0)