(){
@Override
@@ -395,6 +394,24 @@ task bwcTestSuite(type: StandaloneRestIntegTestTask) {
dependsOn tasks.named("${baseName}#fullRestartClusterTask")
}
+task integTestRemote(type: RestIntegTestTask) {
+ testClassesDirs = sourceSets.test.output.classesDirs
+ classpath = sourceSets.test.runtimeClasspath
+ systemProperty 'tests.security.manager', 'false'
+ systemProperty 'java.io.tmpdir', opensearch_tmp_dir.absolutePath
+
+ systemProperty "https", System.getProperty("https")
+ systemProperty "user", System.getProperty("user")
+ systemProperty "password", System.getProperty("password")
+
+ // Only rest case can run with remote cluster
+ if (System.getProperty("tests.rest.cluster") != null) {
+ filter {
+ includeTestsMatching "org.opensearch.observability.rest.*IT"
+ }
+ }
+}
+
run {
doFirst {
// There seems to be an issue when running multi node run or integ tasks with unicast_hosts
diff --git a/opensearch-observability/src/main/kotlin/org/opensearch/observability/model/Application.kt b/opensearch-observability/src/main/kotlin/org/opensearch/observability/model/Application.kt
new file mode 100644
index 000000000..d9f5f688d
--- /dev/null
+++ b/opensearch-observability/src/main/kotlin/org/opensearch/observability/model/Application.kt
@@ -0,0 +1,270 @@
+/*
+ * Copyright OpenSearch Contributors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package org.opensearch.observability.model
+
+import org.opensearch.common.io.stream.StreamInput
+import org.opensearch.common.io.stream.StreamOutput
+import org.opensearch.common.io.stream.Writeable
+import org.opensearch.common.xcontent.ToXContent
+import org.opensearch.common.xcontent.XContentBuilder
+import org.opensearch.common.xcontent.XContentFactory
+import org.opensearch.common.xcontent.XContentParser
+import org.opensearch.common.xcontent.XContentParserUtils
+import org.opensearch.commons.utils.stringList
+import org.opensearch.observability.ObservabilityPlugin.Companion.LOG_PREFIX
+import org.opensearch.observability.util.fieldIfNotNull
+import org.opensearch.observability.util.logger
+
+/**
+ * Application main data class.
+ * * JSON format
+ * {@code
+ * {
+ * "name": "Cool Application",
+ * "description": "Application that includes multiple cool services",
+ * "baseQuery": "source = opensearch_sample_database_flights",
+ * "servicesEntities": [
+ * "Payment",
+ * "Users",
+ * "Purchase"
+ * ],
+ * "traceGroups": [
+ * "Payment.auto",
+ * "Users.admin",
+ * "Purchase.source"
+ * ],
+ * "availabilityLevels": [
+ * {
+ * "label": "Unavailable",
+ * "color": "#D36086",
+ * "condition": "when errorRate() is above or equal to 2%",
+ * "order": "0",
+ * }
+ * ],
+ * }
+ * }
+ */
+
+internal data class Application(
+ val name: String?,
+ val description: String?,
+ val baseQuery: String?,
+ val servicesEntities: List,
+ val traceGroups: List,
+ val availabilityLevels: List
+) : BaseObjectData {
+
+ internal companion object {
+ private val log by logger(Application::class.java)
+ private const val NAME_TAG = "name"
+ private const val DESCRIPTION_TAG = "description"
+ private const val BASE_QUERY_TAG = "baseQuery"
+ private const val SERVICES_ENTITIES_TAG = "servicesEntities"
+ private const val TRACE_GROUPS_TAG = "traceGroups"
+ private const val AVAILABILITY_LEVELS_TAG = "availabilityLevels"
+
+ /**
+ * reader to create instance of class from writable.
+ */
+ val reader = Writeable.Reader { Application(it) }
+
+ /**
+ * Parser to parse xContent
+ */
+ val xParser = XParser { parse(it) }
+
+ /**
+ * Parse the item list from parser
+ * @param parser data referenced at parser
+ * @return created list of items
+ */
+ private fun parseItemList(parser: XContentParser): List {
+ val retList: MutableList = mutableListOf()
+ XContentParserUtils.ensureExpectedToken(XContentParser.Token.START_ARRAY, parser.currentToken(), parser)
+ while (parser.nextToken() != XContentParser.Token.END_ARRAY) {
+ retList.add(AvailabilityLevel.parse(parser))
+ }
+ return retList
+ }
+
+ /**
+ * Parse the data from parser and create ObservabilityObject object
+ * @param parser data referenced at parser
+ * @return created ObservabilityObject object
+ */
+ fun parse(parser: XContentParser): Application {
+ var name: String? = null
+ var description: String? = null
+ var baseQuery: String? = null
+ var servicesEntities: List = listOf()
+ var traceGroups: List = listOf()
+ var availabilityLevels: List = listOf()
+ XContentParserUtils.ensureExpectedToken(
+ XContentParser.Token.START_OBJECT,
+ parser.currentToken(),
+ parser
+ )
+ while (XContentParser.Token.END_OBJECT != parser.nextToken()) {
+ val fieldName = parser.currentName()
+ parser.nextToken()
+ when (fieldName) {
+ NAME_TAG -> name = parser.text()
+ DESCRIPTION_TAG -> description = parser.text()
+ BASE_QUERY_TAG -> baseQuery = parser.text()
+ SERVICES_ENTITIES_TAG -> servicesEntities = parser.stringList()
+ TRACE_GROUPS_TAG -> traceGroups = parser.stringList()
+ AVAILABILITY_LEVELS_TAG -> availabilityLevels = parseItemList(parser)
+ else -> {
+ parser.skipChildren()
+ log.info("$LOG_PREFIX:Application Skipping Unknown field $fieldName")
+ }
+ }
+ }
+ return Application(name, description, baseQuery, servicesEntities, traceGroups, availabilityLevels)
+ }
+ }
+
+ /**
+ * create XContentBuilder from this object using [XContentFactory.jsonBuilder()]
+ * @param params XContent parameters
+ * @return created XContentBuilder object
+ */
+ fun toXContent(params: ToXContent.Params = ToXContent.EMPTY_PARAMS): XContentBuilder? {
+ return toXContent(XContentFactory.jsonBuilder(), params)
+ }
+
+ /**
+ * Constructor used in transport action communication.
+ * @param input StreamInput stream to deserialize data from.
+ */
+ constructor(input: StreamInput) : this(
+ name = input.readString(),
+ description = input.readString(),
+ baseQuery = input.readString(),
+ servicesEntities = input.readStringList(),
+ traceGroups = input.readStringList(),
+ availabilityLevels = input.readList(AvailabilityLevel.reader)
+ )
+
+ /**
+ * {@inheritDoc}
+ */
+ override fun writeTo(output: StreamOutput) {
+ output.writeString(name)
+ output.writeString(description)
+ output.writeString(baseQuery)
+ output.writeStringCollection(servicesEntities)
+ output.writeStringCollection(traceGroups)
+ output.writeCollection(availabilityLevels)
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ override fun toXContent(builder: XContentBuilder?, params: ToXContent.Params?): XContentBuilder {
+ builder!!
+ builder.startObject()
+ .fieldIfNotNull(NAME_TAG, name)
+ .fieldIfNotNull(DESCRIPTION_TAG, description)
+ .fieldIfNotNull(BASE_QUERY_TAG, baseQuery)
+ .fieldIfNotNull(SERVICES_ENTITIES_TAG, servicesEntities)
+ .fieldIfNotNull(TRACE_GROUPS_TAG, traceGroups)
+ .fieldIfNotNull(AVAILABILITY_LEVELS_TAG, availabilityLevels)
+ return builder.endObject()
+ }
+
+ internal data class AvailabilityLevel(
+ val label: String?,
+ val color: String?,
+ val condition: String?,
+ val order: String?
+ ) : BaseModel {
+ internal companion object {
+ private const val LABEL_TAG = "label"
+ private const val COLOR_TAG = "color"
+ private const val CONDITION_TAG = "condition"
+ private const val ORDER_TAG = "order"
+
+ /**
+ * reader to create instance of class from writable.
+ */
+ val reader = Writeable.Reader { AvailabilityLevel(it) }
+
+ /**
+ * Parser to parse xContent
+ */
+ val xParser = XParser { parse(it) }
+
+ /**
+ * Parse the data from parser and create Trigger object
+ * @param parser data referenced at parser
+ * @return created Trigger object
+ */
+ fun parse(parser: XContentParser): AvailabilityLevel {
+ var label: String? = null
+ var color: String? = null
+ var condition: String? = null
+ var order: String? = null
+ XContentParserUtils.ensureExpectedToken(
+ XContentParser.Token.START_OBJECT,
+ parser.currentToken(),
+ parser
+ )
+ while (XContentParser.Token.END_OBJECT != parser.nextToken()) {
+ val fieldName = parser.currentName()
+ parser.nextToken()
+ when (fieldName) {
+ LABEL_TAG -> label = parser.text()
+ COLOR_TAG -> color = parser.text()
+ CONDITION_TAG -> condition = parser.text()
+ ORDER_TAG -> order = parser.text()
+ else -> log.info("$LOG_PREFIX: AvailabilityLevel Skipping Unknown field $fieldName")
+ }
+ }
+ label ?: throw IllegalArgumentException("$LABEL_TAG field absent")
+ color ?: throw IllegalArgumentException("$COLOR_TAG field absent")
+ condition ?: throw IllegalArgumentException("$CONDITION_TAG field absent")
+ order ?: throw IllegalArgumentException("$ORDER_TAG field absent")
+ return AvailabilityLevel(label, color, condition, order)
+ }
+ }
+
+ /**
+ * Constructor used in transport action communication.
+ * @param input StreamInput stream to deserialize data from.
+ */
+ constructor(input: StreamInput) : this(
+ label = input.readString(),
+ color = input.readString(),
+ condition = input.readString(),
+ order = input.readString(),
+ )
+
+ /**
+ * {@inheritDoc}
+ */
+ override fun writeTo(output: StreamOutput) {
+ output.writeString(label)
+ output.writeString(color)
+ output.writeString(condition)
+ output.writeString(order)
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ override fun toXContent(builder: XContentBuilder?, params: ToXContent.Params?): XContentBuilder {
+ builder!!
+ builder.startObject()
+ .field(LABEL_TAG, label)
+ .field(COLOR_TAG, color)
+ .field(CONDITION_TAG, condition)
+ .field(ORDER_TAG, order)
+ builder.endObject()
+ return builder
+ }
+ }
+}
diff --git a/opensearch-observability/src/main/kotlin/org/opensearch/observability/model/ObservabilityObjectDataProperties.kt b/opensearch-observability/src/main/kotlin/org/opensearch/observability/model/ObservabilityObjectDataProperties.kt
index c9b4b6ce3..4da7f24bb 100644
--- a/opensearch-observability/src/main/kotlin/org/opensearch/observability/model/ObservabilityObjectDataProperties.kt
+++ b/opensearch-observability/src/main/kotlin/org/opensearch/observability/model/ObservabilityObjectDataProperties.kt
@@ -29,6 +29,10 @@ internal object ObservabilityObjectDataProperties {
ObservabilityObjectType.OPERATIONAL_PANEL,
ObjectProperty(OperationalPanel.reader, OperationalPanel.xParser)
),
+ Pair(
+ ObservabilityObjectType.APPLICATION,
+ ObjectProperty(Application.reader, Application.xParser)
+ ),
Pair(
ObservabilityObjectType.TIMESTAMP,
ObjectProperty(Timestamp.reader, Timestamp.xParser)
@@ -54,6 +58,7 @@ internal object ObservabilityObjectDataProperties {
ObservabilityObjectType.SAVED_QUERY -> objectData is SavedQuery
ObservabilityObjectType.SAVED_VISUALIZATION -> objectData is SavedVisualization
ObservabilityObjectType.OPERATIONAL_PANEL -> objectData is OperationalPanel
+ ObservabilityObjectType.APPLICATION -> objectData is Application
ObservabilityObjectType.TIMESTAMP -> objectData is Timestamp
ObservabilityObjectType.NONE -> true
}
diff --git a/opensearch-observability/src/main/kotlin/org/opensearch/observability/model/ObservabilityObjectDoc.kt b/opensearch-observability/src/main/kotlin/org/opensearch/observability/model/ObservabilityObjectDoc.kt
index c5e4cd8e8..29aa51852 100644
--- a/opensearch-observability/src/main/kotlin/org/opensearch/observability/model/ObservabilityObjectDoc.kt
+++ b/opensearch-observability/src/main/kotlin/org/opensearch/observability/model/ObservabilityObjectDoc.kt
@@ -1,3 +1,8 @@
+/*
+ * Copyright OpenSearch Contributors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
package org.opensearch.observability.model
import org.opensearch.common.io.stream.StreamInput
diff --git a/opensearch-observability/src/main/kotlin/org/opensearch/observability/model/ObservabilityObjectType.kt b/opensearch-observability/src/main/kotlin/org/opensearch/observability/model/ObservabilityObjectType.kt
index b1940af47..91e4edcd0 100644
--- a/opensearch-observability/src/main/kotlin/org/opensearch/observability/model/ObservabilityObjectType.kt
+++ b/opensearch-observability/src/main/kotlin/org/opensearch/observability/model/ObservabilityObjectType.kt
@@ -6,6 +6,7 @@
package org.opensearch.observability.model
import org.opensearch.commons.utils.EnumParser
+import org.opensearch.observability.model.RestTag.APPLICATION_FIELD
import org.opensearch.observability.model.RestTag.NOTEBOOK_FIELD
import org.opensearch.observability.model.RestTag.OPERATIONAL_PANEL_FIELD
import org.opensearch.observability.model.RestTag.SAVED_QUERY_FIELD
@@ -42,6 +43,11 @@ enum class ObservabilityObjectType(val tag: String) {
return tag
}
},
+ APPLICATION(APPLICATION_FIELD) {
+ override fun toString(): String {
+ return tag
+ }
+ },
TIMESTAMP(TIMESTAMP_FIELD) {
override fun toString(): String {
return tag
diff --git a/opensearch-observability/src/main/kotlin/org/opensearch/observability/model/RestTag.kt b/opensearch-observability/src/main/kotlin/org/opensearch/observability/model/RestTag.kt
index 422582080..1bc390049 100644
--- a/opensearch-observability/src/main/kotlin/org/opensearch/observability/model/RestTag.kt
+++ b/opensearch-observability/src/main/kotlin/org/opensearch/observability/model/RestTag.kt
@@ -31,6 +31,7 @@ internal object RestTag {
const val SAVED_QUERY_FIELD = "savedQuery"
const val SAVED_VISUALIZATION_FIELD = "savedVisualization"
const val OPERATIONAL_PANEL_FIELD = "operationalPanel"
+ const val APPLICATION_FIELD = "application"
const val TIMESTAMP_FIELD = "timestamp"
private val INCLUDE_ID = Pair(OBJECT_ID_FIELD, "true")
private val EXCLUDE_ACCESS = Pair(ACCESS_LIST_FIELD, "false")
diff --git a/opensearch-observability/src/main/resources/observability-mapping.yml b/opensearch-observability/src/main/resources/observability-mapping.yml
index 4748204b8..beb9615bb 100644
--- a/opensearch-observability/src/main/resources/observability-mapping.yml
+++ b/opensearch-observability/src/main/resources/observability-mapping.yml
@@ -54,6 +54,14 @@ properties:
fields:
keyword:
type: keyword
+ application:
+ type: object
+ properties:
+ name:
+ type: text
+ fields:
+ keyword:
+ type: keyword
timestamp:
type: object
properties:
diff --git a/release-notes/opensearch-trace-analytics.release-notes-1.2.1.0.md b/release-notes/opensearch-trace-analytics.release-notes-1.2.1.0.md
new file mode 100644
index 000000000..1cb328ba1
--- /dev/null
+++ b/release-notes/opensearch-trace-analytics.release-notes-1.2.1.0.md
@@ -0,0 +1,6 @@
+## Version 1.2.1.0 Release Notes
+
+Compatible with OpenSearch Version 1.2.1 and OpenSearch Dashboards Version 1.2.0
+
+### Maintenance
+* Bump observability version for OpenSearch 1.2.1 release ([#313](https://github.com/opensearch-project/trace-analytics/pull/313))
\ No newline at end of file