Browse Source

Scripting: Switch watcher to use joda bwc time objects (#35966)

This commit converts the watcher execution context to use the joda
compat java time objects. It also again removes the joda methods from
the painless whitelist.
Ryan Ernst 6 years ago
parent
commit
a0da390df2
18 changed files with 97 additions and 95 deletions
  1. 3 3
      docs/painless/painless-contexts/painless-watcher-condition-context.asciidoc
  2. 3 3
      docs/painless/painless-contexts/painless-watcher-transform-context.asciidoc
  3. 1 2
      modules/lang-painless/spi/src/main/java/org/elasticsearch/painless/spi/Whitelist.java
  4. 0 60
      modules/lang-painless/src/main/resources/org/elasticsearch/painless/spi/joda.time.txt
  5. 9 2
      server/src/main/java/org/elasticsearch/script/JodaCompatibleZonedDateTime.java
  6. 4 0
      server/src/test/java/org/elasticsearch/script/JodaCompatibleZonedDateTimeTests.java
  7. 4 0
      x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/watcher/support/WatcherDateTimeUtils.java
  8. 5 1
      x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/watcher/trigger/TriggerEvent.java
  9. 5 1
      x-pack/plugin/watcher/src/main/java/org/elasticsearch/xpack/watcher/support/Variables.java
  10. 5 1
      x-pack/plugin/watcher/src/main/java/org/elasticsearch/xpack/watcher/trigger/schedule/ScheduleTriggerEvent.java
  11. 7 3
      x-pack/plugin/watcher/src/test/java/org/elasticsearch/xpack/watcher/actions/email/EmailActionTests.java
  12. 7 3
      x-pack/plugin/watcher/src/test/java/org/elasticsearch/xpack/watcher/actions/hipchat/HipChatActionTests.java
  13. 7 3
      x-pack/plugin/watcher/src/test/java/org/elasticsearch/xpack/watcher/actions/logging/LoggingActionTests.java
  14. 7 3
      x-pack/plugin/watcher/src/test/java/org/elasticsearch/xpack/watcher/actions/pagerduty/PagerDutyActionTests.java
  15. 7 3
      x-pack/plugin/watcher/src/test/java/org/elasticsearch/xpack/watcher/actions/slack/SlackActionTests.java
  16. 9 5
      x-pack/plugin/watcher/src/test/java/org/elasticsearch/xpack/watcher/condition/ScriptConditionTests.java
  17. 10 2
      x-pack/plugin/watcher/src/test/java/org/elasticsearch/xpack/watcher/support/VariablesTests.java
  18. 4 0
      x-pack/plugin/watcher/src/test/java/org/elasticsearch/xpack/watcher/transform/script/ScriptTransformTests.java

+ 3 - 3
docs/painless/painless-contexts/painless-watcher-condition-context.asciidoc

@@ -12,13 +12,13 @@ to test if a response is necessary.
 `ctx['watch_id']` (`String`, read-only)::
         The id of the watch.
 
-`ctx['execution_time']` (`DateTime`, read-only)::
+`ctx['execution_time']` (`ZonedDateTime`, read-only)::
         The start time for the watch.
 
-`ctx['trigger']['scheduled_time']` (`DateTime`, read-only)::
+`ctx['trigger']['scheduled_time']` (`ZonedDateTime`, read-only)::
         The scheduled trigger time for the watch.
 
-`ctx['trigger']['triggered_time']` (`DateTime`, read-only)::
+`ctx['trigger']['triggered_time']` (`ZonedDateTime`, read-only)::
         The actual trigger time for the watch.
 
 `ctx['metadata']` (`Map`, read-only)::

+ 3 - 3
docs/painless/painless-contexts/painless-watcher-transform-context.asciidoc

@@ -12,13 +12,13 @@ data into a new payload for use in a response to a condition.
 `ctx['watch_id']` (`String`, read-only)::
         The id of the watch.
 
-`ctx['execution_time']` (`DateTime`, read-only)::
+`ctx['execution_time']` (`ZonedDateTime`, read-only)::
         The start time for the watch.
 
-`ctx['trigger']['scheduled_time']` (`DateTime`, read-only)::
+`ctx['trigger']['scheduled_time']` (`ZonedDateTime`, read-only)::
         The scheduled trigger time for the watch.
 
-`ctx['trigger']['triggered_time']` (`DateTime`, read-only)::
+`ctx['trigger']['triggered_time']` (`ZonedDateTime`, read-only)::
         The actual trigger time for the watch.
 
 `ctx['metadata']` (`Map`, read-only)::

+ 1 - 2
modules/lang-painless/spi/src/main/java/org/elasticsearch/painless/spi/Whitelist.java

@@ -48,8 +48,7 @@ public final class Whitelist {
         "java.util.txt",
         "java.util.function.txt",
         "java.util.regex.txt",
-        "java.util.stream.txt",
-        "joda.time.txt"
+        "java.util.stream.txt"
     };
 
     public static final List<Whitelist> BASE_WHITELISTS =

+ 0 - 60
modules/lang-painless/src/main/resources/org/elasticsearch/painless/spi/joda.time.txt

@@ -1,60 +0,0 @@
-#
-# Licensed to Elasticsearch under one or more contributor
-# license agreements. See the NOTICE file distributed with
-# this work for additional information regarding copyright
-# ownership. Elasticsearch licenses this file to you under
-# the Apache License, Version 2.0 (the "License"); you may
-# not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#    http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied.  See the License for the
-# specific language governing permissions and limitations
-# under the License.
-#
-
-#
-# Painless definition file. This defines the hierarchy of classes,
-# what methods and fields they have, etc.
-#
-
-# NOTE: this just minimal whitelisting of joda time, just to provide
-# convenient access via the scripting API. classes are fully qualified to avoid
-# any confusion with java.time
-
-class org.joda.time.ReadableInstant {
-  boolean equals(Object)
-  long getMillis()
-  int hashCode()
-  boolean isAfter(org.joda.time.ReadableInstant)
-  boolean isBefore(org.joda.time.ReadableInstant)
-  boolean isEqual(org.joda.time.ReadableInstant)
-  String toString()
-}
-
-class org.joda.time.ReadableDateTime {
-  int getCenturyOfEra()
-  int getDayOfMonth()
-  int getDayOfWeek()
-  int getDayOfYear()
-  int getEra()
-  int getHourOfDay()
-  int getMillisOfDay()
-  int getMillisOfSecond()
-  int getMinuteOfDay()
-  int getMinuteOfHour()
-  int getMonthOfYear()
-  int getSecondOfDay()
-  int getSecondOfMinute()
-  int getWeekOfWeekyear()
-  int getWeekyear()
-  int getYear()
-  int getYearOfCentury()
-  int getYearOfEra()
-  String toString(String)
-  String toString(String,Locale)
-}

+ 9 - 2
server/src/main/java/org/elasticsearch/script/JodaCompatibleZonedDateTime.java

@@ -22,6 +22,8 @@ package org.elasticsearch.script;
 import org.apache.logging.log4j.LogManager;
 import org.elasticsearch.common.SuppressForbidden;
 import org.elasticsearch.common.logging.DeprecationLogger;
+import org.elasticsearch.common.time.DateFormatter;
+import org.elasticsearch.common.time.DateFormatters;
 import org.elasticsearch.common.time.DateUtils;
 import org.joda.time.DateTime;
 
@@ -42,11 +44,13 @@ import java.time.temporal.TemporalField;
 import java.time.temporal.TemporalUnit;
 import java.time.temporal.WeekFields;
 import java.util.Locale;
+import java.util.Objects;
 
 /**
  * A wrapper around ZonedDateTime that exposes joda methods for backcompat.
  */
 public class JodaCompatibleZonedDateTime {
+    private static final DateFormatter DATE_FORMATTER = DateFormatters.forPattern("strict_date_time");
     private static final DeprecationLogger deprecationLogger =
         new DeprecationLogger(LogManager.getLogger(JodaCompatibleZonedDateTime.class));
 
@@ -75,7 +79,10 @@ public class JodaCompatibleZonedDateTime {
 
     @Override
     public boolean equals(Object o) {
-        return dt.equals(o);
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+        JodaCompatibleZonedDateTime that = (JodaCompatibleZonedDateTime) o;
+        return Objects.equals(dt, that.dt);
     }
 
     @Override
@@ -85,7 +92,7 @@ public class JodaCompatibleZonedDateTime {
 
     @Override
     public String toString() {
-        return dt.toString();
+        return DATE_FORMATTER.format(dt);
     }
 
     public boolean isAfter(ZonedDateTime o) {

+ 4 - 0
server/src/test/java/org/elasticsearch/script/JodaCompatibleZonedDateTimeTests.java

@@ -97,6 +97,10 @@ public class JodaCompatibleZonedDateTimeTests extends ESTestCase {
         assertDeprecation(assertions, "Use of the joda time method [" + oldMethod + "] is deprecated. Use [" + newMethod + "] instead.");
     }
 
+    public void testEquals() {
+        assertThat(javaTime, equalTo(javaTime));
+    }
+
     public void testToString() {
         assertThat(javaTime.toString(), equalTo(jodaTime.toString()));
     }

+ 4 - 0
x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/watcher/support/WatcherDateTimeUtils.java

@@ -15,6 +15,7 @@ import org.elasticsearch.common.unit.TimeValue;
 import org.elasticsearch.common.xcontent.XContentBuilder;
 import org.elasticsearch.common.xcontent.XContentParser;
 import org.elasticsearch.index.mapper.DateFieldMapper;
+import org.elasticsearch.script.JodaCompatibleZonedDateTime;
 import org.joda.time.DateTime;
 import org.joda.time.DateTimeZone;
 
@@ -35,6 +36,9 @@ public class WatcherDateTimeUtils {
         if (value instanceof DateTime) {
             return (DateTime) value;
         }
+        if (value instanceof JodaCompatibleZonedDateTime) {
+            return new DateTime(((JodaCompatibleZonedDateTime) value).toInstant().toEpochMilli(), DateTimeZone.UTC);
+        }
         if (value instanceof String) {
             return parseDateMath((String) value, DateTimeZone.UTC, clock);
         }

+ 5 - 1
x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/watcher/trigger/TriggerEvent.java

@@ -8,10 +8,13 @@ package org.elasticsearch.xpack.core.watcher.trigger;
 import org.elasticsearch.common.ParseField;
 import org.elasticsearch.common.xcontent.ToXContentObject;
 import org.elasticsearch.common.xcontent.XContentBuilder;
+import org.elasticsearch.script.JodaCompatibleZonedDateTime;
 import org.elasticsearch.xpack.core.watcher.support.WatcherDateTimeUtils;
 import org.joda.time.DateTime;
 
 import java.io.IOException;
+import java.time.Instant;
+import java.time.ZoneOffset;
 import java.util.HashMap;
 import java.util.Map;
 
@@ -25,7 +28,8 @@ public abstract class TriggerEvent implements ToXContentObject {
         this.jobName = jobName;
         this.triggeredTime = triggeredTime;
         this.data = new HashMap<>();
-        data.put(Field.TRIGGERED_TIME.getPreferredName(), triggeredTime);
+        data.put(Field.TRIGGERED_TIME.getPreferredName(),
+            new JodaCompatibleZonedDateTime(Instant.ofEpochMilli(triggeredTime.getMillis()), ZoneOffset.UTC));
     }
 
     public String jobName() {

+ 5 - 1
x-pack/plugin/watcher/src/main/java/org/elasticsearch/xpack/watcher/support/Variables.java

@@ -5,9 +5,12 @@
  */
 package org.elasticsearch.xpack.watcher.support;
 
+import org.elasticsearch.script.JodaCompatibleZonedDateTime;
 import org.elasticsearch.xpack.core.watcher.execution.WatchExecutionContext;
 import org.elasticsearch.xpack.core.watcher.watch.Payload;
 
+import java.time.Instant;
+import java.time.ZoneOffset;
 import java.util.HashMap;
 import java.util.Map;
 
@@ -34,7 +37,8 @@ public final class Variables {
         Map<String, Object> ctxModel = new HashMap<>();
         ctxModel.put(ID, ctx.id().value());
         ctxModel.put(WATCH_ID, ctx.id().watchId());
-        ctxModel.put(EXECUTION_TIME, ctx.executionTime());
+        ctxModel.put(EXECUTION_TIME,
+            new JodaCompatibleZonedDateTime(Instant.ofEpochMilli(ctx.executionTime().getMillis()), ZoneOffset.UTC));
         ctxModel.put(TRIGGER, ctx.triggerEvent().data());
         if (payload != null) {
             ctxModel.put(PAYLOAD, payload.data());

+ 5 - 1
x-pack/plugin/watcher/src/main/java/org/elasticsearch/xpack/watcher/trigger/schedule/ScheduleTriggerEvent.java

@@ -9,6 +9,7 @@ import org.elasticsearch.ElasticsearchParseException;
 import org.elasticsearch.common.ParseField;
 import org.elasticsearch.common.xcontent.XContentBuilder;
 import org.elasticsearch.common.xcontent.XContentParser;
+import org.elasticsearch.script.JodaCompatibleZonedDateTime;
 import org.elasticsearch.xpack.core.watcher.support.WatcherDateTimeUtils;
 import org.elasticsearch.xpack.core.watcher.trigger.TriggerEvent;
 import org.joda.time.DateTime;
@@ -16,6 +17,8 @@ import org.joda.time.DateTimeZone;
 
 import java.io.IOException;
 import java.time.Clock;
+import java.time.Instant;
+import java.time.ZoneOffset;
 
 public class ScheduleTriggerEvent extends TriggerEvent {
 
@@ -28,7 +31,8 @@ public class ScheduleTriggerEvent extends TriggerEvent {
     public ScheduleTriggerEvent(String jobName, DateTime triggeredTime, DateTime scheduledTime) {
         super(jobName, triggeredTime);
         this.scheduledTime = scheduledTime;
-        data.put(Field.SCHEDULED_TIME.getPreferredName(), scheduledTime);
+        data.put(Field.SCHEDULED_TIME.getPreferredName(),
+            new JodaCompatibleZonedDateTime(Instant.ofEpochMilli(scheduledTime.getMillis()), ZoneOffset.UTC));
     }
 
     @Override

+ 7 - 3
x-pack/plugin/watcher/src/test/java/org/elasticsearch/xpack/watcher/actions/email/EmailActionTests.java

@@ -16,6 +16,7 @@ import org.elasticsearch.common.xcontent.ToXContent;
 import org.elasticsearch.common.xcontent.XContentBuilder;
 import org.elasticsearch.common.xcontent.XContentParser;
 import org.elasticsearch.common.xcontent.json.JsonXContent;
+import org.elasticsearch.script.JodaCompatibleZonedDateTime;
 import org.elasticsearch.test.ESTestCase;
 import org.elasticsearch.xpack.core.watcher.actions.Action;
 import org.elasticsearch.xpack.core.watcher.common.secret.Secret;
@@ -52,6 +53,8 @@ import java.io.IOException;
 import java.io.InputStream;
 import java.io.InputStreamReader;
 import java.nio.charset.StandardCharsets;
+import java.time.Instant;
+import java.time.ZoneOffset;
 import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.List;
@@ -129,6 +132,7 @@ public class EmailActionTests extends ESTestCase {
         Map<String, Object> metadata = MapBuilder.<String, Object>newMapBuilder().put("_key", "_val").map();
 
         DateTime now = DateTime.now(DateTimeZone.UTC);
+        JodaCompatibleZonedDateTime jodaJavaNow = new JodaCompatibleZonedDateTime(Instant.ofEpochMilli(now.getMillis()), ZoneOffset.UTC);
 
         Wid wid = new Wid("watch1", now);
         WatchExecutionContext ctx = mockExecutionContextBuilder("watch1")
@@ -139,14 +143,14 @@ public class EmailActionTests extends ESTestCase {
                 .buildMock();
 
         Map<String, Object> triggerModel = new HashMap<>();
-        triggerModel.put("triggered_time", now);
-        triggerModel.put("scheduled_time", now);
+        triggerModel.put("triggered_time", jodaJavaNow);
+        triggerModel.put("scheduled_time", jodaJavaNow);
         Map<String, Object> ctxModel = new HashMap<>();
         ctxModel.put("id", ctx.id().value());
         ctxModel.put("watch_id", "watch1");
         ctxModel.put("payload", data);
         ctxModel.put("metadata", metadata);
-        ctxModel.put("execution_time", now);
+        ctxModel.put("execution_time", jodaJavaNow);
         ctxModel.put("trigger", triggerModel);
         ctxModel.put("vars", emptyMap());
         Map<String, Object> expectedModel = singletonMap("ctx", ctxModel);

+ 7 - 3
x-pack/plugin/watcher/src/test/java/org/elasticsearch/xpack/watcher/actions/hipchat/HipChatActionTests.java

@@ -16,6 +16,7 @@ import org.elasticsearch.common.xcontent.XContentBuilder;
 import org.elasticsearch.common.xcontent.XContentParser;
 import org.elasticsearch.common.xcontent.XContentType;
 import org.elasticsearch.common.xcontent.json.JsonXContent;
+import org.elasticsearch.script.JodaCompatibleZonedDateTime;
 import org.elasticsearch.test.ESTestCase;
 import org.elasticsearch.xpack.core.watcher.actions.Action;
 import org.elasticsearch.xpack.core.watcher.execution.WatchExecutionContext;
@@ -35,6 +36,8 @@ import org.joda.time.DateTimeZone;
 import org.junit.Before;
 
 import java.io.IOException;
+import java.time.Instant;
+import java.time.ZoneOffset;
 import java.util.Arrays;
 import java.util.Collections;
 import java.util.HashMap;
@@ -78,6 +81,7 @@ public class HipChatActionTests extends ESTestCase {
         Map<String, Object> metadata = MapBuilder.<String, Object>newMapBuilder().put("_key", "_val").map();
 
         DateTime now = DateTime.now(DateTimeZone.UTC);
+        JodaCompatibleZonedDateTime jodaJavaNow = new JodaCompatibleZonedDateTime(Instant.ofEpochMilli(now.getMillis()), ZoneOffset.UTC);
 
         Wid wid = new Wid(randomAlphaOfLength(5), now);
         WatchExecutionContext ctx = mockExecutionContextBuilder(wid.watchId())
@@ -88,14 +92,14 @@ public class HipChatActionTests extends ESTestCase {
                 .buildMock();
 
         Map<String, Object> triggerModel = new HashMap<>();
-        triggerModel.put("triggered_time", now);
-        triggerModel.put("scheduled_time", now);
+        triggerModel.put("triggered_time", jodaJavaNow);
+        triggerModel.put("scheduled_time", jodaJavaNow);
         Map<String, Object> ctxModel = new HashMap<>();
         ctxModel.put("id", ctx.id().value());
         ctxModel.put("watch_id", wid.watchId());
         ctxModel.put("payload", data);
         ctxModel.put("metadata", metadata);
-        ctxModel.put("execution_time", now);
+        ctxModel.put("execution_time", jodaJavaNow);
         ctxModel.put("trigger", triggerModel);
         ctxModel.put("vars", Collections.emptyMap());
         Map<String, Object> expectedModel = singletonMap("ctx", ctxModel);

+ 7 - 3
x-pack/plugin/watcher/src/test/java/org/elasticsearch/xpack/watcher/actions/logging/LoggingActionTests.java

@@ -10,6 +10,7 @@ import org.elasticsearch.ElasticsearchParseException;
 import org.elasticsearch.common.SuppressLoggerChecks;
 import org.elasticsearch.common.xcontent.XContentBuilder;
 import org.elasticsearch.common.xcontent.XContentParser;
+import org.elasticsearch.script.JodaCompatibleZonedDateTime;
 import org.elasticsearch.test.ESTestCase;
 import org.elasticsearch.xpack.core.watcher.actions.Action;
 import org.elasticsearch.xpack.core.watcher.execution.WatchExecutionContext;
@@ -22,6 +23,8 @@ import org.joda.time.DateTime;
 import org.junit.Before;
 
 import java.io.IOException;
+import java.time.Instant;
+import java.time.ZoneOffset;
 import java.util.HashMap;
 import java.util.Map;
 
@@ -55,18 +58,19 @@ public class LoggingActionTests extends ESTestCase {
 
     public void testExecute() throws Exception {
         final DateTime now = DateTime.now(UTC);
+        JodaCompatibleZonedDateTime jodaJavaNow = new JodaCompatibleZonedDateTime(Instant.ofEpochMilli(now.getMillis()), ZoneOffset.UTC);
 
         WatchExecutionContext ctx = WatcherTestUtils.mockExecutionContextBuilder("_watch_id")
                 .time("_watch_id", now)
                 .buildMock();
 
         Map<String, Object> triggerModel = new HashMap<>();
-        triggerModel.put("scheduled_time", now);
-        triggerModel.put("triggered_time", now);
+        triggerModel.put("scheduled_time", jodaJavaNow);
+        triggerModel.put("triggered_time", jodaJavaNow);
         Map<String, Object> ctxModel = new HashMap<>();
         ctxModel.put("id", ctx.id().value());
         ctxModel.put("watch_id", "_watch_id");
-        ctxModel.put("execution_time", now);
+        ctxModel.put("execution_time", jodaJavaNow);
         ctxModel.put("payload", emptyMap());
         ctxModel.put("metadata", emptyMap());
         ctxModel.put("vars", emptyMap());

+ 7 - 3
x-pack/plugin/watcher/src/test/java/org/elasticsearch/xpack/watcher/actions/pagerduty/PagerDutyActionTests.java

@@ -13,6 +13,7 @@ import org.elasticsearch.common.xcontent.ToXContent;
 import org.elasticsearch.common.xcontent.XContentBuilder;
 import org.elasticsearch.common.xcontent.XContentParser;
 import org.elasticsearch.common.xcontent.json.JsonXContent;
+import org.elasticsearch.script.JodaCompatibleZonedDateTime;
 import org.elasticsearch.test.ESTestCase;
 import org.elasticsearch.xpack.core.watcher.actions.Action;
 import org.elasticsearch.xpack.core.watcher.execution.WatchExecutionContext;
@@ -33,6 +34,8 @@ import org.joda.time.DateTime;
 import org.joda.time.DateTimeZone;
 import org.junit.Before;
 
+import java.time.Instant;
+import java.time.ZoneOffset;
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.Map;
@@ -78,6 +81,7 @@ public class PagerDutyActionTests extends ESTestCase {
         Map<String, Object> metadata = MapBuilder.<String, Object>newMapBuilder().put("_key", "_val").map();
 
         DateTime now = DateTime.now(DateTimeZone.UTC);
+        JodaCompatibleZonedDateTime jodaJavaNow = new JodaCompatibleZonedDateTime(Instant.ofEpochMilli(now.getMillis()), ZoneOffset.UTC);
 
         Wid wid = new Wid(randomAlphaOfLength(5), now);
         WatchExecutionContext ctx = mockExecutionContextBuilder(wid.watchId())
@@ -92,10 +96,10 @@ public class PagerDutyActionTests extends ESTestCase {
         ctxModel.put("watch_id", wid.watchId());
         ctxModel.put("payload", data);
         ctxModel.put("metadata", metadata);
-        ctxModel.put("execution_time", now);
+        ctxModel.put("execution_time", jodaJavaNow);
         Map<String, Object> triggerModel = new HashMap<>();
-        triggerModel.put("triggered_time", now);
-        triggerModel.put("scheduled_time", now);
+        triggerModel.put("triggered_time", jodaJavaNow);
+        triggerModel.put("scheduled_time", jodaJavaNow);
         ctxModel.put("trigger", triggerModel);
         ctxModel.put("vars", Collections.emptyMap());
         Map<String, Object> expectedModel = new HashMap<>();

+ 7 - 3
x-pack/plugin/watcher/src/test/java/org/elasticsearch/xpack/watcher/actions/slack/SlackActionTests.java

@@ -12,6 +12,7 @@ import org.elasticsearch.common.xcontent.ToXContent;
 import org.elasticsearch.common.xcontent.XContentBuilder;
 import org.elasticsearch.common.xcontent.XContentParser;
 import org.elasticsearch.common.xcontent.json.JsonXContent;
+import org.elasticsearch.script.JodaCompatibleZonedDateTime;
 import org.elasticsearch.test.ESTestCase;
 import org.elasticsearch.xpack.core.watcher.actions.Action;
 import org.elasticsearch.xpack.core.watcher.execution.WatchExecutionContext;
@@ -31,6 +32,8 @@ import org.joda.time.DateTime;
 import org.joda.time.DateTimeZone;
 import org.junit.Before;
 
+import java.time.Instant;
+import java.time.ZoneOffset;
 import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.List;
@@ -75,6 +78,7 @@ public class SlackActionTests extends ESTestCase {
         Map<String, Object> metadata = MapBuilder.<String, Object>newMapBuilder().put("_key", "_val").map();
 
         DateTime now = DateTime.now(DateTimeZone.UTC);
+        JodaCompatibleZonedDateTime jodaJavaNow = new JodaCompatibleZonedDateTime(Instant.ofEpochMilli(now.getMillis()), ZoneOffset.UTC);
 
         Wid wid = new Wid(randomAlphaOfLength(5), now);
         WatchExecutionContext ctx = mockExecutionContextBuilder(wid.watchId())
@@ -85,14 +89,14 @@ public class SlackActionTests extends ESTestCase {
                 .buildMock();
 
         Map<String, Object> triggerModel = new HashMap<>();
-        triggerModel.put("triggered_time", now);
-        triggerModel.put("scheduled_time", now);
+        triggerModel.put("triggered_time", jodaJavaNow);
+        triggerModel.put("scheduled_time", jodaJavaNow);
         Map<String, Object> ctxModel = new HashMap<>();
         ctxModel.put("id", ctx.id().value());
         ctxModel.put("watch_id", wid.watchId());
         ctxModel.put("payload", data);
         ctxModel.put("metadata", metadata);
-        ctxModel.put("execution_time", now);
+        ctxModel.put("execution_time", jodaJavaNow);
         ctxModel.put("trigger", triggerModel);
         ctxModel.put("vars", emptyMap());
         Map<String, Object> expectedModel = singletonMap("ctx", ctxModel);

+ 9 - 5
x-pack/plugin/watcher/src/test/java/org/elasticsearch/xpack/watcher/condition/ScriptConditionTests.java

@@ -20,6 +20,7 @@ import org.elasticsearch.common.xcontent.XContentFactory;
 import org.elasticsearch.common.xcontent.XContentParser;
 import org.elasticsearch.common.xcontent.support.XContentMapValues;
 import org.elasticsearch.script.GeneralScriptException;
+import org.elasticsearch.script.JodaCompatibleZonedDateTime;
 import org.elasticsearch.script.Script;
 import org.elasticsearch.script.ScriptException;
 import org.elasticsearch.script.ScriptMetaData;
@@ -66,9 +67,10 @@ public class ScriptConditionTests extends ESTestCase {
         scripts.put("return true", s -> true);
         scripts.put("return new Object()", s -> new Object());
 
-        scripts.put("ctx.trigger.scheduled_time.getMillis() < new Date().time", vars -> {
-            DateTime scheduledTime = (DateTime) XContentMapValues.extractValue("ctx.trigger.scheduled_time", vars);
-            return scheduledTime.getMillis() < new Date().getTime();
+        scripts.put("ctx.trigger.scheduled_time.toInstant().toEpochMill() < new Date().time", vars -> {
+            JodaCompatibleZonedDateTime scheduledTime =
+                (JodaCompatibleZonedDateTime) XContentMapValues.extractValue("ctx.trigger.scheduled_time", vars);
+            return scheduledTime.toInstant().toEpochMilli() < new Date().getTime();
         });
 
         scripts.put("null.foo", s -> {
@@ -194,8 +196,8 @@ public class ScriptConditionTests extends ESTestCase {
     }
 
     public void testScriptConditionAccessCtx() throws Exception {
-        ScriptCondition condition = new ScriptCondition(mockScript("ctx.trigger.scheduled_time.getMillis() < new Date().time"),
-                scriptService);
+        ScriptCondition condition = new ScriptCondition(
+            mockScript("ctx.trigger.scheduled_time.toInstant().toEpochMill() < new Date().time"), scriptService);
         SearchResponse response = new SearchResponse(InternalSearchResponse.empty(), "", 3, 3, 0, 500L, ShardSearchFailure.EMPTY_ARRAY,
                 SearchResponse.Clusters.EMPTY);
         WatchExecutionContext ctx = mockExecutionContext("_name", new DateTime(DateTimeZone.UTC),
@@ -209,6 +211,8 @@ public class ScriptConditionTests extends ESTestCase {
         when(watcherContext.id()).thenReturn(mock(Wid.class));
         when(watcherContext.watch()).thenReturn(mock(Watch.class));
         when(watcherContext.triggerEvent()).thenReturn(mock(TriggerEvent.class));
+        DateTime now = DateTime.now(DateTimeZone.UTC);
+        when(watcherContext.executionTime()).thenReturn(now);
         WatcherConditionScript watcherScript = new WatcherConditionScript(Collections.emptyMap(), watcherContext) {
             @Override
             public boolean execute() {

+ 10 - 2
x-pack/plugin/watcher/src/test/java/org/elasticsearch/xpack/watcher/support/VariablesTests.java

@@ -5,16 +5,20 @@
  */
 package org.elasticsearch.xpack.watcher.support;
 
+import org.elasticsearch.common.xcontent.ObjectPath;
+import org.elasticsearch.script.JodaCompatibleZonedDateTime;
 import org.elasticsearch.test.ESTestCase;
 import org.elasticsearch.xpack.core.watcher.execution.WatchExecutionContext;
 import org.elasticsearch.xpack.core.watcher.execution.Wid;
-import org.elasticsearch.common.xcontent.ObjectPath;
 import org.elasticsearch.xpack.core.watcher.trigger.TriggerEvent;
 import org.elasticsearch.xpack.core.watcher.watch.Payload;
 import org.elasticsearch.xpack.watcher.test.WatcherTestUtils;
 import org.elasticsearch.xpack.watcher.trigger.schedule.ScheduleTriggerEvent;
+import org.hamcrest.Matchers;
 import org.joda.time.DateTime;
 
+import java.time.Instant;
+import java.time.ZoneOffset;
 import java.util.Map;
 
 import static java.util.Collections.singletonMap;
@@ -44,9 +48,13 @@ public class VariablesTests extends ESTestCase {
         assertThat(model, notNullValue());
         assertThat(model.size(), is(1));
 
+        JodaCompatibleZonedDateTime jodaJavaExecutionTime =
+            new JodaCompatibleZonedDateTime(Instant.ofEpochMilli(executionTime.getMillis()), ZoneOffset.UTC);
         assertThat(ObjectPath.eval("ctx", model), instanceOf(Map.class));
         assertThat(ObjectPath.eval("ctx.id", model), is(wid.value()));
-        assertThat(ObjectPath.eval("ctx.execution_time", model), is(executionTime));
+        // NOTE: we use toString() here because two ZonedDateTime are *not* equal, we need to check with isEqual
+        // for date/time equality, but no hamcrest matcher exists for that
+        assertThat(ObjectPath.eval("ctx.execution_time", model), Matchers.hasToString(jodaJavaExecutionTime.toString()));
         assertThat(ObjectPath.eval("ctx.trigger", model), is(event.data()));
         assertThat(ObjectPath.eval("ctx.payload", model), is(payload.data()));
         assertThat(ObjectPath.eval("ctx.metadata", model), is(metatdata));

+ 4 - 0
x-pack/plugin/watcher/src/test/java/org/elasticsearch/xpack/watcher/transform/script/ScriptTransformTests.java

@@ -22,6 +22,8 @@ import org.elasticsearch.xpack.core.watcher.trigger.TriggerEvent;
 import org.elasticsearch.xpack.core.watcher.watch.Payload;
 import org.elasticsearch.xpack.core.watcher.watch.Watch;
 import org.elasticsearch.xpack.watcher.Watcher;
+import org.joda.time.DateTime;
+import org.joda.time.DateTimeZone;
 
 import java.util.Collections;
 import java.util.HashMap;
@@ -191,6 +193,8 @@ public class ScriptTransformTests extends ESTestCase {
         when(watcherContext.id()).thenReturn(mock(Wid.class));
         when(watcherContext.watch()).thenReturn(mock(Watch.class));
         when(watcherContext.triggerEvent()).thenReturn(mock(TriggerEvent.class));
+        DateTime now = DateTime.now(DateTimeZone.UTC);
+        when(watcherContext.executionTime()).thenReturn(now);
         Payload payload = mock(Payload.class);
         WatcherTransformScript watcherScript = new WatcherTransformScript(Collections.emptyMap(), watcherContext, payload) {
             @Override