diff --git a/pom.xml b/pom.xml
index 8f073c45ad3..ded0599965b 100644
--- a/pom.xml
+++ b/pom.xml
@@ -7,7 +7,7 @@
org.elasticsearch
elasticsearch-alerts
- 1.0.0-beta2i
+ 1.0.0-beta2-SNAPSHOT
org.sonatype.oss
@@ -154,6 +154,12 @@
+
+
+ src/main/resources
+ true
+
+
org.apache.maven.plugins
@@ -389,6 +395,25 @@
true
+
+
+ org.codehaus.mojo
+ buildnumber-maven-plugin
+ 1.3
+
+
+ validate
+
+ create
+
+
+
+
+ false
+ false
+
+
+
diff --git a/src/main/java/org/elasticsearch/alerts/AlertsBuild.java b/src/main/java/org/elasticsearch/alerts/AlertsBuild.java
new file mode 100644
index 00000000000..558bc8aeb25
--- /dev/null
+++ b/src/main/java/org/elasticsearch/alerts/AlertsBuild.java
@@ -0,0 +1,104 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+package org.elasticsearch.alerts;
+
+import org.elasticsearch.common.io.FastStringReader;
+import org.elasticsearch.common.io.Streams;
+import org.elasticsearch.common.io.stream.StreamInput;
+import org.elasticsearch.common.io.stream.StreamOutput;
+import org.elasticsearch.common.joda.time.DateTimeZone;
+import org.elasticsearch.common.joda.time.format.ISODateTimeFormat;
+
+import java.io.IOException;
+import java.util.Properties;
+
+/**
+ */
+public class AlertsBuild {
+
+ public static final AlertsBuild CURRENT;
+
+ static {
+ String hash = "NA";
+ String hashShort = "NA";
+ String timestamp = "NA";
+
+ try {
+ String properties = Streams.copyToStringFromClasspath("/alerts-build.properties");
+ Properties props = new Properties();
+ props.load(new FastStringReader(properties));
+ hash = props.getProperty("hash", hash);
+ if (!hash.equals("NA")) {
+ hashShort = hash.substring(0, 7);
+ }
+ String gitTimestampRaw = props.getProperty("timestamp");
+ if (gitTimestampRaw != null) {
+ timestamp = ISODateTimeFormat.dateTimeNoMillis().withZone(DateTimeZone.UTC).print(Long.parseLong(gitTimestampRaw));
+ }
+ } catch (Exception e) {
+ // just ignore...
+ }
+
+ CURRENT = new AlertsBuild(hash, hashShort, timestamp);
+ }
+
+ private final String hash;
+ private final String hashShort;
+ private final String timestamp;
+
+ AlertsBuild(String hash, String hashShort, String timestamp) {
+ this.hash = hash;
+ this.hashShort = hashShort;
+ this.timestamp = timestamp;
+ }
+
+ public String hash() {
+ return hash;
+ }
+
+ public String hashShort() {
+ return hashShort;
+ }
+
+ public String timestamp() {
+ return timestamp;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ AlertsBuild that = (AlertsBuild) o;
+
+ if (!hash.equals(that.hash)) return false;
+ if (!hashShort.equals(that.hashShort)) return false;
+ if (!timestamp.equals(that.timestamp)) return false;
+
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ int result = hash.hashCode();
+ result = 31 * result + hashShort.hashCode();
+ result = 31 * result + timestamp.hashCode();
+ return result;
+ }
+
+ public static AlertsBuild readBuild(StreamInput in) throws IOException {
+ String hash = in.readString();
+ String hashShort = in.readString();
+ String timestamp = in.readString();
+ return new AlertsBuild(hash, hashShort, timestamp);
+ }
+
+ public static void writeBuild(AlertsBuild build, StreamOutput out) throws IOException {
+ out.writeString(build.hash());
+ out.writeString(build.hashShort());
+ out.writeString(build.timestamp());
+ }
+}
diff --git a/src/main/java/org/elasticsearch/alerts/AlertsVersion.java b/src/main/java/org/elasticsearch/alerts/AlertsVersion.java
new file mode 100644
index 00000000000..b715211fe45
--- /dev/null
+++ b/src/main/java/org/elasticsearch/alerts/AlertsVersion.java
@@ -0,0 +1,199 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+package org.elasticsearch.alerts;
+
+import org.elasticsearch.Version;
+import org.elasticsearch.common.Nullable;
+import org.elasticsearch.common.Strings;
+import org.elasticsearch.common.io.stream.StreamInput;
+import org.elasticsearch.common.io.stream.StreamOutput;
+
+import java.io.IOException;
+import java.io.Serializable;
+
+/**
+ */
+@SuppressWarnings("deprecation")
+public class AlertsVersion implements Serializable {
+
+ // The logic for ID is: XXYYZZAA, where XX is major version, YY is minor version, ZZ is revision, and AA is Beta/RC indicator
+ // AA values below 50 are beta builds, and below 99 are RC builds, with 99 indicating a release
+ // the (internal) format of the id is there so we can easily do after/before checks on the id
+
+ // The first internal beta has already been released, without this class being here, so we start version version 2.
+ public static final int V_1_0_0_Beta2_ID = /*00*/1000002;
+ public static final AlertsVersion V_1_0_0_Beta2 = new AlertsVersion(V_1_0_0_Beta2_ID, true, Version.V_1_4_0);
+
+ public static final AlertsVersion CURRENT = V_1_0_0_Beta2;
+
+ public static AlertsVersion readVersion(StreamInput in) throws IOException {
+ return fromId(in.readVInt());
+ }
+
+ public static AlertsVersion fromId(int id) {
+ switch (id) {
+ case V_1_0_0_Beta2_ID:
+ return V_1_0_0_Beta2;
+
+ default:
+ return new AlertsVersion(id, null, Version.CURRENT);
+ }
+ }
+
+ public static void writeVersion(AlertsVersion version, StreamOutput out) throws IOException {
+ out.writeVInt(version.id);
+ }
+
+ /**
+ * Returns the smallest version between the 2.
+ */
+ public static AlertsVersion smallest(AlertsVersion version1, AlertsVersion version2) {
+ return version1.id < version2.id ? version1 : version2;
+ }
+
+ /**
+ * Returns the version given its string representation, current version if the argument is null or empty
+ */
+ public static AlertsVersion fromString(String version) {
+ if (!Strings.hasLength(version)) {
+ return AlertsVersion.CURRENT;
+ }
+
+ String[] parts = version.split("\\.");
+ if (parts.length < 3 || parts.length > 4) {
+ throw new IllegalArgumentException("the version needs to contain major, minor and revision, and optionally the build");
+ }
+
+ try {
+ //we reverse the version id calculation based on some assumption as we can't reliably reverse the modulo
+ int major = Integer.parseInt(parts[0]) * 1000000;
+ int minor = Integer.parseInt(parts[1]) * 10000;
+ int revision = Integer.parseInt(parts[2]) * 100;
+
+ int build = 99;
+ if (parts.length == 4) {
+ String buildStr = parts[3];
+ if (buildStr.startsWith("Beta")) {
+ build = Integer.parseInt(buildStr.substring(4));
+ }
+ if (buildStr.startsWith("RC")) {
+ build = Integer.parseInt(buildStr.substring(2)) + 50;
+ }
+ }
+
+ return fromId(major + minor + revision + build);
+
+ } catch(NumberFormatException e) {
+ throw new IllegalArgumentException("unable to parse version " + version, e);
+ }
+ }
+
+ public final int id;
+ public final byte major;
+ public final byte minor;
+ public final byte revision;
+ public final byte build;
+ public final Boolean snapshot;
+ public final Version minEsCompatibilityVersion;
+ // TODO: Once licencing integration has been completed license version should be added to
+
+ AlertsVersion(int id, @Nullable Boolean snapshot, Version minEsCompatibilityVersion) {
+ this.id = id;
+ this.major = (byte) ((id / 1000000) % 100);
+ this.minor = (byte) ((id / 10000) % 100);
+ this.revision = (byte) ((id / 100) % 100);
+ this.build = (byte) (id % 100);
+ this.snapshot = snapshot;
+ this.minEsCompatibilityVersion = minEsCompatibilityVersion;
+ }
+
+ public boolean snapshot() {
+ return snapshot != null && snapshot;
+ }
+
+ public boolean after(AlertsVersion version) {
+ return version.id < id;
+ }
+
+ public boolean onOrAfter(AlertsVersion version) {
+ return version.id <= id;
+ }
+
+ public boolean before(AlertsVersion version) {
+ return version.id > id;
+ }
+
+ public boolean onOrBefore(AlertsVersion version) {
+ return version.id >= id;
+ }
+
+ public boolean compatibleWith(AlertsVersion version) {
+ return version.onOrAfter(minimumCompatibilityVersion());
+ }
+
+ public boolean compatibleWith(Version esVersion) {
+ return esVersion.onOrAfter(minEsCompatibilityVersion);
+ }
+
+ /**
+ * Returns the minimum compatible version based on the current
+ * version. Ie a node needs to have at least the return version in order
+ * to communicate with a node running the current version. The returned version
+ * is in most of the cases the smallest major version release unless the current version
+ * is a beta or RC release then the version itself is returned.
+ */
+ public AlertsVersion minimumCompatibilityVersion() {
+ return AlertsVersion.smallest(this, fromId(major * 1000000 + 99));
+ }
+
+ /**
+ * @return The minimum elasticsearch version this shield version is compatible with.
+ */
+ public Version minimumEsCompatiblityVersion() {
+ return minEsCompatibilityVersion;
+ }
+
+ /**
+ * Just the version number (without -SNAPSHOT if snapshot).
+ */
+ public String number() {
+ StringBuilder sb = new StringBuilder();
+ sb.append(major).append('.').append(minor).append('.').append(revision);
+ if (build < 50) {
+ sb.append(".Beta").append(build);
+ } else if (build < 99) {
+ sb.append(".RC").append(build - 50);
+ }
+ return sb.toString();
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder();
+ sb.append(number());
+ if (snapshot()) {
+ sb.append("-SNAPSHOT");
+ }
+ return sb.toString();
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ AlertsVersion that = (AlertsVersion) o;
+
+ if (id != that.id) return false;
+
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ return id;
+ }
+}
diff --git a/src/main/java/org/elasticsearch/alerts/rest/RestAlertsStatsAction.java b/src/main/java/org/elasticsearch/alerts/rest/RestAlertsStatsAction.java
index 49adcf6e8a6..b7a450c9f54 100644
--- a/src/main/java/org/elasticsearch/alerts/rest/RestAlertsStatsAction.java
+++ b/src/main/java/org/elasticsearch/alerts/rest/RestAlertsStatsAction.java
@@ -47,6 +47,14 @@ public class RestAlertsStatsAction extends BaseRestHandler {
.field("alert_action_queue_size", alertsStatsResponse.getAlertActionManagerQueueSize())
.field("number_of_alerts", alertsStatsResponse.getNumberOfRegisteredAlerts())
.field("alert_action_queue_max_size", alertsStatsResponse.getAlertActionManagerLargestQueueSize());
+
+ builder.startObject("version")
+ .field("number", alertsStatsResponse.getVersion().number())
+ .field("build_hash", alertsStatsResponse.getBuild().hash())
+ .field("build_timestamp", alertsStatsResponse.getBuild().timestamp())
+ .field("build_snapshot", alertsStatsResponse.getVersion().snapshot)
+ .endObject();
+
return new BytesRestResponse(OK, builder);
}
diff --git a/src/main/java/org/elasticsearch/alerts/transport/actions/stats/AlertsStatsResponse.java b/src/main/java/org/elasticsearch/alerts/transport/actions/stats/AlertsStatsResponse.java
index 3db78e8daeb..1617feca354 100644
--- a/src/main/java/org/elasticsearch/alerts/transport/actions/stats/AlertsStatsResponse.java
+++ b/src/main/java/org/elasticsearch/alerts/transport/actions/stats/AlertsStatsResponse.java
@@ -6,6 +6,8 @@
package org.elasticsearch.alerts.transport.actions.stats;
import org.elasticsearch.action.ActionResponse;
+import org.elasticsearch.alerts.AlertsBuild;
+import org.elasticsearch.alerts.AlertsVersion;
import org.elasticsearch.alerts.State;
import org.elasticsearch.common.io.stream.StreamInput;
import org.elasticsearch.common.io.stream.StreamOutput;
@@ -17,12 +19,12 @@ import java.io.IOException;
*/
public class AlertsStatsResponse extends ActionResponse {
+ private AlertsVersion version;
+ private AlertsBuild build;
private long numberOfRegisteredAlerts;
private State alertManagerState;
private boolean alertActionManagerStarted;
private long alertActionManagerQueueSize;
-
-
private long alertActionManagerLargestQueueSize;
public AlertsStatsResponse() {
@@ -36,11 +38,7 @@ public class AlertsStatsResponse extends ActionResponse {
return alertActionManagerQueueSize;
}
- /**
- * Sets the current size of the alert action queue
- * @param alertActionManagerQueueSize
- */
- public void setAlertActionManagerQueueSize(long alertActionManagerQueueSize) {
+ void setAlertActionManagerQueueSize(long alertActionManagerQueueSize) {
this.alertActionManagerQueueSize = alertActionManagerQueueSize;
}
@@ -52,11 +50,7 @@ public class AlertsStatsResponse extends ActionResponse {
return numberOfRegisteredAlerts;
}
- /**
- * Set the number of alerts currently registered in the system
- * @param numberOfRegisteredAlerts
- */
- public void setNumberOfRegisteredAlerts(long numberOfRegisteredAlerts) {
+ void setNumberOfRegisteredAlerts(long numberOfRegisteredAlerts) {
this.numberOfRegisteredAlerts = numberOfRegisteredAlerts;
}
@@ -79,11 +73,7 @@ public class AlertsStatsResponse extends ActionResponse {
return alertActionManagerStarted;
}
- /**
- * Sets if the alert action manager is started
- * @param alertActionManagerStarted
- */
- public void setAlertActionManagerStarted(boolean alertActionManagerStarted) {
+ void setAlertActionManagerStarted(boolean alertActionManagerStarted) {
this.alertActionManagerStarted = alertActionManagerStarted;
}
@@ -95,14 +85,31 @@ public class AlertsStatsResponse extends ActionResponse {
return alertActionManagerLargestQueueSize;
}
- /**
- * Sets the largest alert action manager queue size
- * @param alertActionManagerLargestQueueSize
- */
- public void setAlertActionManagerLargestQueueSize(long alertActionManagerLargestQueueSize) {
+ void setAlertActionManagerLargestQueueSize(long alertActionManagerLargestQueueSize) {
this.alertActionManagerLargestQueueSize = alertActionManagerLargestQueueSize;
}
+ /**
+ * @return The alerts plugin version.
+ */
+ public AlertsVersion getVersion() {
+ return version;
+ }
+
+ void setVersion(AlertsVersion version) {
+ this.version = version;
+ }
+
+ /**
+ * @return The alerts plugin build information.
+ */
+ public AlertsBuild getBuild() {
+ return build;
+ }
+
+ void setBuild(AlertsBuild build) {
+ this.build = build;
+ }
@Override
public void readFrom(StreamInput in) throws IOException {
@@ -112,6 +119,8 @@ public class AlertsStatsResponse extends ActionResponse {
alertActionManagerLargestQueueSize = in.readLong();
alertManagerState = State.fromId(in.readByte());
alertActionManagerStarted = in.readBoolean();
+ version = AlertsVersion.readVersion(in);
+ build = AlertsBuild.readBuild(in);
}
@Override
@@ -122,5 +131,7 @@ public class AlertsStatsResponse extends ActionResponse {
out.writeLong(alertActionManagerLargestQueueSize);
out.writeByte(alertManagerState.getId());
out.writeBoolean(alertActionManagerStarted);
+ AlertsVersion.writeVersion(version, out);
+ AlertsBuild.writeBuild(build, out);
}
}
diff --git a/src/main/java/org/elasticsearch/alerts/transport/actions/stats/TransportAlertStatsAction.java b/src/main/java/org/elasticsearch/alerts/transport/actions/stats/TransportAlertStatsAction.java
index 6740ff78bb5..71229822545 100644
--- a/src/main/java/org/elasticsearch/alerts/transport/actions/stats/TransportAlertStatsAction.java
+++ b/src/main/java/org/elasticsearch/alerts/transport/actions/stats/TransportAlertStatsAction.java
@@ -11,6 +11,8 @@ import org.elasticsearch.action.support.ActionFilters;
import org.elasticsearch.action.support.master.TransportMasterNodeOperationAction;
import org.elasticsearch.alerts.AlertService;
import org.elasticsearch.alerts.actions.AlertActionService;
+import org.elasticsearch.alerts.AlertsBuild;
+import org.elasticsearch.alerts.AlertsVersion;
import org.elasticsearch.cluster.ClusterService;
import org.elasticsearch.cluster.ClusterState;
import org.elasticsearch.cluster.block.ClusterBlockException;
@@ -60,6 +62,8 @@ public class TransportAlertStatsAction extends TransportMasterNodeOperationActio
statsResponse.setAlertActionManagerQueueSize(alertActionService.getQueueSize());
statsResponse.setNumberOfRegisteredAlerts(alertService.getNumberOfAlerts());
statsResponse.setAlertActionManagerLargestQueueSize(alertActionService.getLargestQueueSize());
+ statsResponse.setVersion(AlertsVersion.CURRENT);
+ statsResponse.setBuild(AlertsBuild.CURRENT);
listener.onResponse(statsResponse);
}
diff --git a/src/main/resources/alerts-build.properties b/src/main/resources/alerts-build.properties
new file mode 100644
index 00000000000..f3ad519cedb
--- /dev/null
+++ b/src/main/resources/alerts-build.properties
@@ -0,0 +1,3 @@
+version=${project.version}
+hash=${buildNumber}
+timestamp=${timestamp}
\ No newline at end of file
diff --git a/src/test/java/org/elasticsearch/alerts/actions/AlertStatsTests.java b/src/test/java/org/elasticsearch/alerts/actions/AlertStatsTests.java
index f86bd051bf2..a59dfd16df0 100644
--- a/src/test/java/org/elasticsearch/alerts/actions/AlertStatsTests.java
+++ b/src/test/java/org/elasticsearch/alerts/actions/AlertStatsTests.java
@@ -7,6 +7,8 @@ package org.elasticsearch.alerts.actions;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.alerts.AbstractAlertingTests;
+import org.elasticsearch.alerts.AlertsBuild;
+import org.elasticsearch.alerts.AlertsVersion;
import org.elasticsearch.alerts.State;
import org.elasticsearch.alerts.client.AlertsClient;
import org.elasticsearch.alerts.transport.actions.stats.AlertsStatsRequest;
@@ -38,6 +40,8 @@ public class AlertStatsTests extends AbstractAlertingTests {
assertThat(response.getAlertActionManagerQueueSize(), equalTo(0L));
assertThat(response.getNumberOfRegisteredAlerts(), equalTo(0L));
assertThat(response.getAlertActionManagerLargestQueueSize(), equalTo(0L));
+ assertThat(response.getVersion(), equalTo(AlertsVersion.CURRENT));
+ assertThat(response.getBuild(), equalTo(AlertsBuild.CURRENT));
}
@Test