From 1fe7a560e4f6f4d332761e6638890507ab25380e Mon Sep 17 00:00:00 2001
From: Esduard <eduardomotta528@gmail.com>
Date: Wed, 7 Jul 2021 23:36:27 -0300
Subject: [PATCH 1/5] update message for out of order page error

---
 .../core/ilm/TimeseriesLifecycleType.java     | 34 ++++++++++++++++---
 .../ilm/TimeseriesLifecycleTypeTests.java     | 15 ++++----
 2 files changed, 38 insertions(+), 11 deletions(-)

diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ilm/TimeseriesLifecycleType.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ilm/TimeseriesLifecycleType.java
index 90d89195604aa..8cf995aea327b 100644
--- a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ilm/TimeseriesLifecycleType.java
+++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ilm/TimeseriesLifecycleType.java
@@ -8,8 +8,8 @@
 
 import org.elasticsearch.common.Strings;
 import org.elasticsearch.common.io.stream.StreamOutput;
-import org.elasticsearch.core.TimeValue;
 import org.elasticsearch.common.util.set.Sets;
+import org.elasticsearch.core.TimeValue;
 import org.elasticsearch.rollup.RollupV2;
 
 import java.io.IOException;
@@ -19,6 +19,7 @@
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.HashSet;
+import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
 import java.util.Objects;
@@ -399,10 +400,33 @@ public static String validateMonotonicallyIncreasingPhaseTimings(Collection<Phas
                     .collect(Collectors.toSet());
                 if (phasesWithBadAges.size() > 0) {
                     phasesWithBadAges.forEach(p -> invalidPhases.add(p.getName()));
-                    errors.add("phases [" + phasesWithBadAges.stream().map(Phase::getName).collect(Collectors.joining(",")) +
-                        "] configure a [min_age] value less than the [min_age] of [" + phase.getMinimumAge() +
-                        "] for the [" + phaseName + "] phase, configuration: " +
-                        phasesWithBadAges.stream().collect(Collectors.toMap(Phase::getName, Phase::getMinimumAge)));
+
+                    //build an error message string
+                    Iterator<Phase> it = phasesWithBadAges.iterator();
+                    Phase badPhase = it.next();
+
+                    String error = "Your policy is configured to run the "
+                        + badPhase.getName() + " phase (min_age: " + badPhase.getMinimumAge() + ")";
+
+                    if (phasesWithBadAges.size() > 1) {
+                        while (it.hasNext()) {
+                            badPhase = it.next();
+                            error = error + ", the " + badPhase.getName() + " phase (min_age: "
+                                + badPhase.getMinimumAge()+ ")";
+                        }
+                            //if multiple phases are cited
+                            //replace last occurrence of "," with " and"
+                            StringBuilder builder = new StringBuilder();
+                            int last_comma_index = error.lastIndexOf(",");
+                            builder.append(error, 0, last_comma_index);
+                            builder.append(" and");
+                            builder.append(error.substring(last_comma_index + 1));
+                            error = builder.toString();
+                    }
+                    error = error + " before the " + phaseName + " phase (min_age: " + phase.getMinimumAge() +
+                        "). You should change the phase timing so that the phases will execute in the order of hot, warm then cold.";
+
+                    errors.add(error);
                 }
             }
         }
diff --git a/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ilm/TimeseriesLifecycleTypeTests.java b/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ilm/TimeseriesLifecycleTypeTests.java
index a82938541dda7..49acc9e834967 100644
--- a/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ilm/TimeseriesLifecycleTypeTests.java
+++ b/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ilm/TimeseriesLifecycleTypeTests.java
@@ -754,8 +754,9 @@ public void testValidatingIncreasingAges() {
                 validateMonotonicallyIncreasingPhaseTimings(Arrays.asList(hotPhase, warmPhase, coldPhase, frozenPhase, deletePhase));
 
             assertThat(err,
-                containsString("phases [cold] configure a [min_age] value less than the" +
-                    " [min_age] of [1d] for the [hot] phase, configuration: {cold=12h}"));
+                containsString("Your policy is configured to run the cold phase "+
+                    "(min_age: 12h) before the hot phase (min_age: 1d). You should change "+
+                    "the phase timing so that the phases will execute in the order of hot, warm then cold."));
         }
 
         {
@@ -769,8 +770,9 @@ public void testValidatingIncreasingAges() {
                 validateMonotonicallyIncreasingPhaseTimings(Arrays.asList(hotPhase, warmPhase, coldPhase, frozenPhase, deletePhase));
 
             assertThat(err,
-                containsString("phases [frozen,delete] configure a [min_age] value less " +
-                    "than the [min_age] of [3d] for the [warm] phase, configuration: {frozen=1d, delete=2d}"));
+                containsString("Your policy is configured to run the frozen phase "+
+                    "(min_age: 1d) and the delete phase (min_age: 2d) before the warm phase (min_age: 3d)."+
+                    " You should change the phase timing so that the phases will execute in the order of hot, warm then cold."));
         }
 
         {
@@ -784,8 +786,9 @@ public void testValidatingIncreasingAges() {
                 validateMonotonicallyIncreasingPhaseTimings(Arrays.asList(hotPhase, warmPhase, coldPhase, frozenPhase, deletePhase));
 
             assertThat(err,
-                containsString("phases [frozen,delete] configure a [min_age] value less than " +
-                    "the [min_age] of [3d] for the [warm] phase, configuration: {frozen=2d, delete=1d}"));
+                containsString("Your policy is configured to run the frozen phase "+
+                    "(min_age: 2d) and the delete phase (min_age: 1d) before the warm phase (min_age: 3d)."+
+                    " You should change the phase timing so that the phases will execute in the order of hot, warm then cold."));
         }
     }
 

From fb38dfbe12b85e4bf3370a991dbb859643205f0a Mon Sep 17 00:00:00 2001
From: Esduard <eduardomotta528@gmail.com>
Date: Thu, 8 Jul 2021 19:42:09 -0300
Subject: [PATCH 2/5] change yml test

---
 .../yamlRestTest/resources/rest-api-spec/test/ilm/10_basic.yml  | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/x-pack/plugin/ilm/qa/rest/src/yamlRestTest/resources/rest-api-spec/test/ilm/10_basic.yml b/x-pack/plugin/ilm/qa/rest/src/yamlRestTest/resources/rest-api-spec/test/ilm/10_basic.yml
index 2383b858f2c0c..99f5edd002503 100644
--- a/x-pack/plugin/ilm/qa/rest/src/yamlRestTest/resources/rest-api-spec/test/ilm/10_basic.yml
+++ b/x-pack/plugin/ilm/qa/rest/src/yamlRestTest/resources/rest-api-spec/test/ilm/10_basic.yml
@@ -281,7 +281,7 @@ setup:
 "Test increasing phase timings validated":
 
   - do:
-      catch: /phases \[delete\] configure a \[min_age\] value less than the \[min_age\] of \[10s\] for the \[warm\] phase/
+      catch: /Your policy is configured to run the delete phase \(min_age\:\ 5s\) before the warm phase \(min_age\:\ 10s\). You should change the phase timing so that the phases will execute in the order of hot, warm then cold./
       ilm.put_lifecycle:
         policy: "bad_policy"
         body: |

From 3b1f1deb48e12b771ddf9ae6e0401ca4725bd6e7 Mon Sep 17 00:00:00 2001
From: Esduard <eduardomotta528@gmail.com>
Date: Thu, 8 Jul 2021 19:57:23 -0300
Subject: [PATCH 3/5] update error message

---
 .../xpack/core/ilm/TimeseriesLifecycleType.java             | 2 +-
 .../xpack/core/ilm/TimeseriesLifecycleTypeTests.java        | 6 +++---
 .../resources/rest-api-spec/test/ilm/10_basic.yml           | 2 +-
 3 files changed, 5 insertions(+), 5 deletions(-)

diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ilm/TimeseriesLifecycleType.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ilm/TimeseriesLifecycleType.java
index 8cf995aea327b..97c03d52bfad3 100644
--- a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ilm/TimeseriesLifecycleType.java
+++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ilm/TimeseriesLifecycleType.java
@@ -424,7 +424,7 @@ public static String validateMonotonicallyIncreasingPhaseTimings(Collection<Phas
                             error = builder.toString();
                     }
                     error = error + " before the " + phaseName + " phase (min_age: " + phase.getMinimumAge() +
-                        "). You should change the phase timing so that the phases will execute in the order of hot, warm then cold.";
+                        "). You should change the phase timing so that the phases will execute in the order of hot, warm, then cold.";
 
                     errors.add(error);
                 }
diff --git a/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ilm/TimeseriesLifecycleTypeTests.java b/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ilm/TimeseriesLifecycleTypeTests.java
index 49acc9e834967..4839cccf09835 100644
--- a/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ilm/TimeseriesLifecycleTypeTests.java
+++ b/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ilm/TimeseriesLifecycleTypeTests.java
@@ -756,7 +756,7 @@ public void testValidatingIncreasingAges() {
             assertThat(err,
                 containsString("Your policy is configured to run the cold phase "+
                     "(min_age: 12h) before the hot phase (min_age: 1d). You should change "+
-                    "the phase timing so that the phases will execute in the order of hot, warm then cold."));
+                    "the phase timing so that the phases will execute in the order of hot, warm, then cold."));
         }
 
         {
@@ -772,7 +772,7 @@ public void testValidatingIncreasingAges() {
             assertThat(err,
                 containsString("Your policy is configured to run the frozen phase "+
                     "(min_age: 1d) and the delete phase (min_age: 2d) before the warm phase (min_age: 3d)."+
-                    " You should change the phase timing so that the phases will execute in the order of hot, warm then cold."));
+                    " You should change the phase timing so that the phases will execute in the order of hot, warm, then cold."));
         }
 
         {
@@ -788,7 +788,7 @@ public void testValidatingIncreasingAges() {
             assertThat(err,
                 containsString("Your policy is configured to run the frozen phase "+
                     "(min_age: 2d) and the delete phase (min_age: 1d) before the warm phase (min_age: 3d)."+
-                    " You should change the phase timing so that the phases will execute in the order of hot, warm then cold."));
+                    " You should change the phase timing so that the phases will execute in the order of hot, warm, then cold."));
         }
     }
 
diff --git a/x-pack/plugin/ilm/qa/rest/src/yamlRestTest/resources/rest-api-spec/test/ilm/10_basic.yml b/x-pack/plugin/ilm/qa/rest/src/yamlRestTest/resources/rest-api-spec/test/ilm/10_basic.yml
index 99f5edd002503..89f51590861a6 100644
--- a/x-pack/plugin/ilm/qa/rest/src/yamlRestTest/resources/rest-api-spec/test/ilm/10_basic.yml
+++ b/x-pack/plugin/ilm/qa/rest/src/yamlRestTest/resources/rest-api-spec/test/ilm/10_basic.yml
@@ -281,7 +281,7 @@ setup:
 "Test increasing phase timings validated":
 
   - do:
-      catch: /Your policy is configured to run the delete phase \(min_age\:\ 5s\) before the warm phase \(min_age\:\ 10s\). You should change the phase timing so that the phases will execute in the order of hot, warm then cold./
+      catch: /Your policy is configured to run the delete phase \(min_age\:\ 5s\) before the warm phase \(min_age\:\ 10s\). You should change the phase timing so that the phases will execute in the order of hot, warm, then cold./
       ilm.put_lifecycle:
         policy: "bad_policy"
         body: |

From 6b58e7c70f90179789cfb7968ed52a8f4b4fcd12 Mon Sep 17 00:00:00 2001
From: Esduard <eduardomotta528@gmail.com>
Date: Mon, 12 Jul 2021 17:23:30 -0300
Subject: [PATCH 4/5] temporarily blacklist test

---
 x-pack/plugin/ilm/qa/rest/build.gradle | 7 +++++++
 1 file changed, 7 insertions(+)

diff --git a/x-pack/plugin/ilm/qa/rest/build.gradle b/x-pack/plugin/ilm/qa/rest/build.gradle
index 7c459e4808ed2..adbf72c977b71 100644
--- a/x-pack/plugin/ilm/qa/rest/build.gradle
+++ b/x-pack/plugin/ilm/qa/rest/build.gradle
@@ -26,3 +26,10 @@ testClusters.all {
   setting 'xpack.license.self_generated.type', 'trial'
   user clusterCredentials
 }
+
+tasks.named("yamlRestCompatTest").configure {
+  systemProperty 'tests.rest.blacklist', [
+    //TODO: remove once #75099 is back ported to 7.x
+    'ilm/10_basic/Test increasing phase timings validated'
+  ].join(',')
+}

From 1eab7d0a4d7204092065093391a7478fc852c4c6 Mon Sep 17 00:00:00 2001
From: Esduard <eduardomotta528@gmail.com>
Date: Tue, 13 Jul 2021 16:31:49 -0300
Subject: [PATCH 5/5] Test cases for 3+ bad phases

---
 .../ilm/TimeseriesLifecycleTypeTests.java     | 32 +++++++++++++++++++
 1 file changed, 32 insertions(+)

diff --git a/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ilm/TimeseriesLifecycleTypeTests.java b/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ilm/TimeseriesLifecycleTypeTests.java
index 4839cccf09835..ad5ab8d75da86 100644
--- a/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ilm/TimeseriesLifecycleTypeTests.java
+++ b/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ilm/TimeseriesLifecycleTypeTests.java
@@ -790,6 +790,38 @@ public void testValidatingIncreasingAges() {
                     "(min_age: 2d) and the delete phase (min_age: 1d) before the warm phase (min_age: 3d)."+
                     " You should change the phase timing so that the phases will execute in the order of hot, warm, then cold."));
         }
+
+        {
+            Phase hotPhase = new Phase(HOT_PHASE, TimeValue.timeValueDays(3), Collections.emptyMap());
+            Phase warmPhase = new Phase(WARM_PHASE, TimeValue.timeValueDays(2), Collections.emptyMap());
+            Phase coldPhase = new Phase(COLD_PHASE, null, Collections.emptyMap());
+            Phase frozenPhase = new Phase(FROZEN_PHASE, TimeValue.timeValueDays(2), Collections.emptyMap());
+            Phase deletePhase = new Phase(DELETE_PHASE, TimeValue.timeValueDays(1), Collections.emptyMap());
+
+            String err =
+                validateMonotonicallyIncreasingPhaseTimings(Arrays.asList(hotPhase, warmPhase, coldPhase, frozenPhase, deletePhase));
+
+            assertThat(err,
+                containsString("Your policy is configured to run the frozen phase "+
+                    "(min_age: 2d), the delete phase (min_age: 1d) and the warm phase (min_age: 2d) before the hot phase (min_age: 3d)."+
+                    " You should change the phase timing so that the phases will execute in the order of hot, warm, then cold."));
+        }
+
+        {
+            Phase hotPhase = new Phase(HOT_PHASE, TimeValue.timeValueDays(3), Collections.emptyMap());
+            Phase warmPhase = new Phase(WARM_PHASE, TimeValue.timeValueDays(2), Collections.emptyMap());
+            Phase coldPhase = new Phase(COLD_PHASE, TimeValue.timeValueDays(2), Collections.emptyMap());
+            Phase frozenPhase = new Phase(FROZEN_PHASE, TimeValue.timeValueDays(2), Collections.emptyMap());
+            Phase deletePhase = new Phase(DELETE_PHASE, TimeValue.timeValueDays(1), Collections.emptyMap());
+
+            String err =
+                validateMonotonicallyIncreasingPhaseTimings(Arrays.asList(hotPhase, warmPhase, coldPhase, frozenPhase, deletePhase));
+
+            assertThat(err,
+                containsString("Your policy is configured to run the cold phase (min_age: 2d), the frozen phase "+
+                    "(min_age: 2d), the delete phase (min_age: 1d) and the warm phase (min_age: 2d) before the hot phase (min_age: 3d)."+
+                    " You should change the phase timing so that the phases will execute in the order of hot, warm, then cold."));
+        }
     }
 
     public void testValidateFrozenPhaseHasSearchableSnapshot() {