diff --git a/src/main/kotlin/org/opensearch/commons/alerting/model/Monitor.kt b/src/main/kotlin/org/opensearch/commons/alerting/model/Monitor.kt index b2099d93..d726d561 100644 --- a/src/main/kotlin/org/opensearch/commons/alerting/model/Monitor.kt +++ b/src/main/kotlin/org/opensearch/commons/alerting/model/Monitor.kt @@ -22,7 +22,7 @@ import org.opensearch.core.xcontent.XContentParser import org.opensearch.core.xcontent.XContentParserUtils import java.io.IOException import java.time.Instant -import java.util.Locale +import java.util.regex.Pattern data class Monitor( override val id: String = NO_ID, @@ -34,7 +34,7 @@ data class Monitor( override val enabledTime: Instant?, // TODO: Check how this behaves during rolling upgrade/multi-version cluster // Can read/write and parsing break if it's done from an old -> new version of the plugin? - val monitorType: MonitorType, + val monitorType: String, val user: User?, val schemaVersion: Int = NO_SCHEMA_VERSION, val inputs: List, @@ -56,13 +56,13 @@ data class Monitor( require(triggerIds.add(trigger.id)) { "Duplicate trigger id: ${trigger.id}. Trigger ids must be unique." } // Verify Trigger type based on Monitor type when (monitorType) { - MonitorType.QUERY_LEVEL_MONITOR -> + MonitorType.QUERY_LEVEL_MONITOR.value -> require(trigger is QueryLevelTrigger) { "Incompatible trigger [${trigger.id}] for monitor type [$monitorType]" } - MonitorType.BUCKET_LEVEL_MONITOR -> + MonitorType.BUCKET_LEVEL_MONITOR.value -> require(trigger is BucketLevelTrigger) { "Incompatible trigger [${trigger.id}] for monitor type [$monitorType]" } - MonitorType.CLUSTER_METRICS_MONITOR -> + MonitorType.CLUSTER_METRICS_MONITOR.value -> require(trigger is QueryLevelTrigger) { "Incompatible trigger [${trigger.id}] for monitor type [$monitorType]" } - MonitorType.DOC_LEVEL_MONITOR -> + MonitorType.DOC_LEVEL_MONITOR.value -> require(trigger is DocumentLevelTrigger) { "Incompatible trigger [${trigger.id}] for monitor type [$monitorType]" } } } @@ -94,7 +94,7 @@ data class Monitor( schedule = Schedule.readFrom(sin), lastUpdateTime = sin.readInstant(), enabledTime = sin.readOptionalInstant(), - monitorType = sin.readEnum(MonitorType::class.java), + monitorType = sin.readString(), user = if (sin.readBoolean()) { User(sin) } else { @@ -179,7 +179,7 @@ data class Monitor( schedule.writeTo(out) out.writeInstant(lastUpdateTime) out.writeOptionalInstant(enabledTime) - out.writeEnum(monitorType) + out.writeString(monitorType) out.writeBoolean(user != null) user?.writeTo(out) out.writeInt(schemaVersion) @@ -227,6 +227,7 @@ data class Monitor( const val DATA_SOURCES_FIELD = "data_sources" const val ENABLED_TIME_FIELD = "enabled_time" const val OWNER_FIELD = "owner" + val MONITOR_TYPE_PATTERN = Pattern.compile("[a-zA-Z0-9_]{5,25}") // This is defined here instead of in ScheduledJob to avoid having the ScheduledJob class know about all // the different subclasses and creating circular dependencies @@ -265,9 +266,10 @@ data class Monitor( NAME_FIELD -> name = xcp.text() MONITOR_TYPE_FIELD -> { monitorType = xcp.text() - val allowedTypes = MonitorType.values().map { it.value } - if (!allowedTypes.contains(monitorType)) { - throw IllegalStateException("Monitor type should be one of $allowedTypes") + val matcher = MONITOR_TYPE_PATTERN.matcher(monitorType) + val find = matcher.matches() + if (!find) { + throw IllegalStateException("Monitor type should follow pattern ${MONITOR_TYPE_PATTERN.pattern()}") } } USER_FIELD -> user = if (xcp.currentToken() == XContentParser.Token.VALUE_NULL) null else User.parse(xcp) @@ -325,7 +327,7 @@ data class Monitor( requireNotNull(schedule) { "Monitor schedule is null" }, lastUpdateTime ?: Instant.now(), enabledTime, - MonitorType.valueOf(monitorType.uppercase(Locale.ROOT)), + monitorType, user, schemaVersion, inputs.toList(), diff --git a/src/main/kotlin/org/opensearch/commons/alerting/util/IndexUtils.kt b/src/main/kotlin/org/opensearch/commons/alerting/util/IndexUtils.kt index 18454465..9b822b56 100644 --- a/src/main/kotlin/org/opensearch/commons/alerting/util/IndexUtils.kt +++ b/src/main/kotlin/org/opensearch/commons/alerting/util/IndexUtils.kt @@ -8,6 +8,7 @@ import org.opensearch.core.xcontent.XContentBuilder import org.opensearch.core.xcontent.XContentParser import org.opensearch.core.xcontent.XContentParserUtils import java.time.Instant +import java.util.Locale class IndexUtils { companion object { @@ -46,7 +47,9 @@ class IndexUtils { } } -fun Monitor.isBucketLevelMonitor(): Boolean = this.monitorType == Monitor.MonitorType.BUCKET_LEVEL_MONITOR +fun Monitor.isBucketLevelMonitor(): Boolean = + isMonitorOfStandardType() && + Monitor.MonitorType.valueOf(this.monitorType.uppercase(Locale.ROOT)) == Monitor.MonitorType.BUCKET_LEVEL_MONITOR fun XContentBuilder.optionalUserField(name: String, user: User?): XContentBuilder { if (user == null) { @@ -78,3 +81,8 @@ fun XContentParser.instant(): Instant? { * Extension function for ES 6.3 and above that duplicates the ES 6.2 XContentBuilder.string() method. */ fun XContentBuilder.string(): String = BytesReference.bytes(this).utf8ToString() + +fun Monitor.isMonitorOfStandardType(): Boolean { + val standardMonitorTypes = Monitor.MonitorType.values().map { it.value.uppercase(Locale.ROOT) }.toSet() + return standardMonitorTypes.contains(this.monitorType.uppercase(Locale.ROOT)) +} diff --git a/src/test/kotlin/org/opensearch/commons/alerting/TestHelpers.kt b/src/test/kotlin/org/opensearch/commons/alerting/TestHelpers.kt index ca193224..d0715431 100644 --- a/src/test/kotlin/org/opensearch/commons/alerting/TestHelpers.kt +++ b/src/test/kotlin/org/opensearch/commons/alerting/TestHelpers.kt @@ -80,7 +80,7 @@ fun randomQueryLevelMonitor( withMetadata: Boolean = false ): Monitor { return Monitor( - name = name, monitorType = Monitor.MonitorType.QUERY_LEVEL_MONITOR, enabled = enabled, inputs = inputs, + name = name, monitorType = Monitor.MonitorType.QUERY_LEVEL_MONITOR.value, enabled = enabled, inputs = inputs, schedule = schedule, triggers = triggers, enabledTime = enabledTime, lastUpdateTime = lastUpdateTime, user = user, uiMetadata = if (withMetadata) mapOf("foo" to "bar") else mapOf() ) @@ -98,7 +98,7 @@ fun randomQueryLevelMonitorWithoutUser( withMetadata: Boolean = false ): Monitor { return Monitor( - name = name, monitorType = Monitor.MonitorType.QUERY_LEVEL_MONITOR, enabled = enabled, inputs = inputs, + name = name, monitorType = Monitor.MonitorType.QUERY_LEVEL_MONITOR.value, enabled = enabled, inputs = inputs, schedule = schedule, triggers = triggers, enabledTime = enabledTime, lastUpdateTime = lastUpdateTime, user = null, uiMetadata = if (withMetadata) mapOf("foo" to "bar") else mapOf() ) @@ -122,7 +122,7 @@ fun randomBucketLevelMonitor( withMetadata: Boolean = false ): Monitor { return Monitor( - name = name, monitorType = Monitor.MonitorType.BUCKET_LEVEL_MONITOR, enabled = enabled, inputs = inputs, + name = name, monitorType = Monitor.MonitorType.BUCKET_LEVEL_MONITOR.value, enabled = enabled, inputs = inputs, schedule = schedule, triggers = triggers, enabledTime = enabledTime, lastUpdateTime = lastUpdateTime, user = user, uiMetadata = if (withMetadata) mapOf("foo" to "bar") else mapOf() ) @@ -140,7 +140,7 @@ fun randomClusterMetricsMonitor( withMetadata: Boolean = false ): Monitor { return Monitor( - name = name, monitorType = Monitor.MonitorType.CLUSTER_METRICS_MONITOR, enabled = enabled, inputs = inputs, + name = name, monitorType = Monitor.MonitorType.CLUSTER_METRICS_MONITOR.value, enabled = enabled, inputs = inputs, schedule = schedule, triggers = triggers, enabledTime = enabledTime, lastUpdateTime = lastUpdateTime, user = user, uiMetadata = if (withMetadata) mapOf("foo" to "bar") else mapOf() ) @@ -158,7 +158,7 @@ fun randomDocumentLevelMonitor( withMetadata: Boolean = false ): Monitor { return Monitor( - name = name, monitorType = Monitor.MonitorType.DOC_LEVEL_MONITOR, enabled = enabled, inputs = inputs, + name = name, monitorType = Monitor.MonitorType.DOC_LEVEL_MONITOR.value, enabled = enabled, inputs = inputs, schedule = schedule, triggers = triggers, enabledTime = enabledTime, lastUpdateTime = lastUpdateTime, user = user, uiMetadata = if (withMetadata) mapOf("foo" to "bar") else mapOf() ) diff --git a/src/test/kotlin/org/opensearch/commons/alerting/action/GetMonitorResponseTests.kt b/src/test/kotlin/org/opensearch/commons/alerting/action/GetMonitorResponseTests.kt index d91c7471..eb3f08e4 100644 --- a/src/test/kotlin/org/opensearch/commons/alerting/action/GetMonitorResponseTests.kt +++ b/src/test/kotlin/org/opensearch/commons/alerting/action/GetMonitorResponseTests.kt @@ -42,7 +42,7 @@ class GetMonitorResponseTests : OpenSearchTestCase() { schedule = cronSchedule, lastUpdateTime = Instant.now(), enabledTime = Instant.now(), - monitorType = Monitor.MonitorType.QUERY_LEVEL_MONITOR, + monitorType = Monitor.MonitorType.QUERY_LEVEL_MONITOR.value, user = randomUser(), schemaVersion = 0, inputs = mutableListOf(), diff --git a/src/test/kotlin/org/opensearch/commons/alerting/action/IndexMonitorResponseTests.kt b/src/test/kotlin/org/opensearch/commons/alerting/action/IndexMonitorResponseTests.kt index 2b5ee04d..ca3afa3e 100644 --- a/src/test/kotlin/org/opensearch/commons/alerting/action/IndexMonitorResponseTests.kt +++ b/src/test/kotlin/org/opensearch/commons/alerting/action/IndexMonitorResponseTests.kt @@ -26,7 +26,7 @@ class IndexMonitorResponseTests { schedule = cronSchedule, lastUpdateTime = Instant.now(), enabledTime = Instant.now(), - monitorType = Monitor.MonitorType.QUERY_LEVEL_MONITOR, + monitorType = Monitor.MonitorType.QUERY_LEVEL_MONITOR.value, user = randomUser(), schemaVersion = 0, inputs = mutableListOf(), diff --git a/src/test/kotlin/org/opensearch/commons/alerting/model/XContentTests.kt b/src/test/kotlin/org/opensearch/commons/alerting/model/XContentTests.kt index 065191fb..ea64df79 100644 --- a/src/test/kotlin/org/opensearch/commons/alerting/model/XContentTests.kt +++ b/src/test/kotlin/org/opensearch/commons/alerting/model/XContentTests.kt @@ -367,7 +367,7 @@ class XContentTests { """.trimIndent() val parsedMonitor = Monitor.parse(parser(monitorString)) Assertions.assertEquals( - Monitor.MonitorType.QUERY_LEVEL_MONITOR, + Monitor.MonitorType.QUERY_LEVEL_MONITOR.value, parsedMonitor.monitorType, "Incorrect monitor type" )