Skip to content

Commit b2e9ddc

Browse files
authored
add auditDelegateMonitorAlerts flag (#476)
* add auditDelegateMonitorAlerts flag Signed-off-by: Surya Sashank Nistala <[email protected]> * add audit state check in error alert validation Signed-off-by: Surya Sashank Nistala <[email protected]> * add test to verify workflow with auditDelegateMonitor flag null Signed-off-by: Surya Sashank Nistala <[email protected]> --------- Signed-off-by: Surya Sashank Nistala <[email protected]>
1 parent b555e24 commit b2e9ddc

File tree

8 files changed

+116
-12
lines changed

8 files changed

+116
-12
lines changed

src/main/kotlin/org/opensearch/commons/alerting/action/GetWorkflowAlertsRequest.kt

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ class GetWorkflowAlertsRequest : ActionRequest {
1212
val severityLevel: String
1313
val alertState: String
1414
val alertIndex: String?
15+
val associatedAlertsIndex: String?
1516
val monitorIds: List<String>?
1617
val workflowIds: List<String>?
1718
val alertIds: List<String>?
@@ -22,15 +23,17 @@ class GetWorkflowAlertsRequest : ActionRequest {
2223
severityLevel: String,
2324
alertState: String,
2425
alertIndex: String?,
26+
associatedAlertsIndex: String?,
2527
monitorIds: List<String>? = null,
2628
workflowIds: List<String>? = null,
2729
alertIds: List<String>? = null,
28-
getAssociatedAlerts: Boolean
30+
getAssociatedAlerts: Boolean,
2931
) : super() {
3032
this.table = table
3133
this.severityLevel = severityLevel
3234
this.alertState = alertState
3335
this.alertIndex = alertIndex
36+
this.associatedAlertsIndex = associatedAlertsIndex
3437
this.monitorIds = monitorIds
3538
this.workflowIds = workflowIds
3639
this.alertIds = alertIds
@@ -43,6 +46,7 @@ class GetWorkflowAlertsRequest : ActionRequest {
4346
severityLevel = sin.readString(),
4447
alertState = sin.readString(),
4548
alertIndex = sin.readOptionalString(),
49+
associatedAlertsIndex = sin.readOptionalString(),
4650
monitorIds = sin.readOptionalStringList(),
4751
workflowIds = sin.readOptionalStringList(),
4852
alertIds = sin.readOptionalStringList(),
@@ -59,6 +63,7 @@ class GetWorkflowAlertsRequest : ActionRequest {
5963
out.writeString(severityLevel)
6064
out.writeString(alertState)
6165
out.writeOptionalString(alertIndex)
66+
out.writeOptionalString(associatedAlertsIndex)
6267
out.writeOptionalStringCollection(monitorIds)
6368
out.writeOptionalStringCollection(workflowIds)
6469
out.writeOptionalStringCollection(alertIds)

src/main/kotlin/org/opensearch/commons/alerting/model/Alert.kt

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ data class Alert(
4646
) : Writeable, ToXContent {
4747

4848
init {
49-
if (errorMessage != null) require(state == State.DELETED || state == State.ERROR) {
49+
if (errorMessage != null) require(state == State.DELETED || state == State.ERROR || state == State.AUDIT) {
5050
"Attempt to create an alert with an error in state: $state"
5151
}
5252
}
@@ -422,7 +422,9 @@ data class Alert(
422422
SCHEMA_VERSION_FIELD -> schemaVersion = xcp.intValue()
423423
MONITOR_NAME_FIELD -> monitorName = xcp.text()
424424
MONITOR_VERSION_FIELD -> monitorVersion = xcp.longValue()
425-
MONITOR_USER_FIELD -> monitorUser = if (xcp.currentToken() == XContentParser.Token.VALUE_NULL) null else User.parse(xcp)
425+
MONITOR_USER_FIELD ->
426+
monitorUser = if (xcp.currentToken() == XContentParser.Token.VALUE_NULL) null
427+
else User.parse(xcp)
426428
TRIGGER_ID_FIELD -> triggerId = xcp.text()
427429
FINDING_IDS -> {
428430
ensureExpectedToken(XContentParser.Token.START_ARRAY, xcp.currentToken(), xcp)

src/main/kotlin/org/opensearch/commons/alerting/model/Workflow.kt

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,8 @@ data class Workflow(
3636
val schemaVersion: Int = NO_SCHEMA_VERSION,
3737
val inputs: List<WorkflowInput>,
3838
val owner: String? = DEFAULT_OWNER,
39-
val triggers: List<Trigger>
39+
val triggers: List<Trigger>,
40+
val auditDelegateMonitorAlerts: Boolean? = true,
4041
) : ScheduledJob {
4142
override val type = WORKFLOW_TYPE
4243

@@ -70,7 +71,8 @@ data class Workflow(
7071
schemaVersion = sin.readInt(),
7172
inputs = sin.readList((WorkflowInput)::readFrom),
7273
owner = sin.readOptionalString(),
73-
triggers = sin.readList((Trigger)::readFrom)
74+
triggers = sin.readList((Trigger)::readFrom),
75+
auditDelegateMonitorAlerts = sin.readOptionalBoolean()
7476
)
7577

7678
// This enum classifies different workflows
@@ -99,7 +101,7 @@ data class Workflow(
99101
private fun createXContentBuilder(
100102
builder: XContentBuilder,
101103
params: ToXContent.Params,
102-
secure: Boolean
104+
secure: Boolean,
103105
): XContentBuilder {
104106
builder.startObject()
105107
if (params.paramAsBoolean("with_type", false)) builder.startObject(type)
@@ -119,6 +121,9 @@ data class Workflow(
119121
.field(TRIGGERS_FIELD, triggers.toTypedArray())
120122
.optionalTimeField(LAST_UPDATE_TIME_FIELD, lastUpdateTime)
121123
builder.field(OWNER_FIELD, owner)
124+
if (auditDelegateMonitorAlerts != null) {
125+
builder.field(AUDIT_DELEGATE_MONITOR_ALERTS_FIELD, auditDelegateMonitorAlerts)
126+
}
122127
if (params.paramAsBoolean("with_type", false)) builder.endObject()
123128
return builder.endObject()
124129
}
@@ -159,6 +164,7 @@ data class Workflow(
159164
}
160165
it.writeTo(out)
161166
}
167+
out.writeOptionalBoolean(auditDelegateMonitorAlerts)
162168
}
163169

164170
companion object {
@@ -177,6 +183,7 @@ data class Workflow(
177183
const val ENABLED_TIME_FIELD = "enabled_time"
178184
const val TRIGGERS_FIELD = "triggers"
179185
const val OWNER_FIELD = "owner"
186+
const val AUDIT_DELEGATE_MONITOR_ALERTS_FIELD = "audit_delegate_monitor_alerts"
180187

181188
// This is defined here instead of in ScheduledJob to avoid having the ScheduledJob class know about all
182189
// the different subclasses and creating circular dependencies
@@ -201,6 +208,7 @@ data class Workflow(
201208
val inputs: MutableList<WorkflowInput> = mutableListOf()
202209
val triggers: MutableList<Trigger> = mutableListOf()
203210
var owner = DEFAULT_OWNER
211+
var auditDelegateMonitorAlerts = true
204212

205213
XContentParserUtils.ensureExpectedToken(XContentParser.Token.START_OBJECT, xcp.currentToken(), xcp)
206214
while (xcp.nextToken() != XContentParser.Token.END_OBJECT) {
@@ -245,6 +253,7 @@ data class Workflow(
245253
}
246254
ENABLED_TIME_FIELD -> enabledTime = xcp.instant()
247255
LAST_UPDATE_TIME_FIELD -> lastUpdateTime = xcp.instant()
256+
AUDIT_DELEGATE_MONITOR_ALERTS_FIELD -> auditDelegateMonitorAlerts = xcp.booleanValue()
248257
OWNER_FIELD -> {
249258
owner = if (xcp.currentToken() == XContentParser.Token.VALUE_NULL) owner else xcp.text()
250259
}
@@ -272,7 +281,8 @@ data class Workflow(
272281
schemaVersion,
273282
inputs.toList(),
274283
owner,
275-
triggers
284+
triggers,
285+
auditDelegateMonitorAlerts
276286
)
277287
}
278288

src/test/kotlin/org/opensearch/commons/alerting/TestHelpers.kt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,7 @@ fun randomWorkflow(
173173
enabledTime: Instant? = if (enabled) Instant.now().truncatedTo(ChronoUnit.MILLIS) else null,
174174
lastUpdateTime: Instant = Instant.now().truncatedTo(ChronoUnit.MILLIS),
175175
triggers: List<Trigger> = listOf(randomChainedAlertTrigger()),
176+
auditDelegateMonitorAlerts: Boolean? = true
176177
): Workflow {
177178
val delegates = mutableListOf<Delegate>()
178179
if (!monitorIds.isNullOrEmpty()) {
@@ -195,7 +196,7 @@ fun randomWorkflow(
195196
return Workflow(
196197
name = name, workflowType = Workflow.WorkflowType.COMPOSITE, enabled = enabled, inputs = input,
197198
schedule = schedule, enabledTime = enabledTime, lastUpdateTime = lastUpdateTime, user = user,
198-
triggers = triggers
199+
triggers = triggers, auditDelegateMonitorAlerts = auditDelegateMonitorAlerts
199200
)
200201
}
201202

src/test/kotlin/org/opensearch/commons/alerting/action/GetWorkflowAlertsRequestTests.kt

Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ internal class GetWorkflowAlertsRequestTests {
2424
workflowIds = listOf("w1", "w2"),
2525
alertIds = emptyList(),
2626
alertIndex = null,
27+
associatedAlertsIndex = null,
2728
monitorIds = emptyList()
2829
)
2930
assertNotNull(req)
@@ -41,6 +42,42 @@ internal class GetWorkflowAlertsRequestTests {
4142
assertTrue(newReq.alertIds!!.isEmpty())
4243
assertTrue(newReq.monitorIds!!.isEmpty())
4344
assertNull(newReq.alertIndex)
45+
assertNull(newReq.associatedAlertsIndex)
46+
assertTrue(newReq.getAssociatedAlerts)
47+
}
48+
49+
@Test
50+
fun `test get alerts request with custom alerts and associated alerts indices`() {
51+
52+
val table = Table("asc", "sortString", null, 1, 0, "")
53+
54+
val req = GetWorkflowAlertsRequest(
55+
table = table,
56+
severityLevel = "1",
57+
alertState = "active",
58+
getAssociatedAlerts = true,
59+
workflowIds = listOf("w1", "w2"),
60+
alertIds = emptyList(),
61+
alertIndex = "alertIndex",
62+
associatedAlertsIndex = "associatedAlertsIndex",
63+
monitorIds = emptyList()
64+
)
65+
assertNotNull(req)
66+
67+
val out = BytesStreamOutput()
68+
req.writeTo(out)
69+
val sin = StreamInput.wrap(out.bytes().toBytesRef().bytes)
70+
val newReq = GetWorkflowAlertsRequest(sin)
71+
72+
assertEquals("1", newReq.severityLevel)
73+
assertEquals("active", newReq.alertState)
74+
assertEquals(table, newReq.table)
75+
assertTrue(newReq.workflowIds!!.contains("w1"))
76+
assertTrue(newReq.workflowIds!!.contains("w2"))
77+
assertTrue(newReq.alertIds!!.isEmpty())
78+
assertTrue(newReq.monitorIds!!.isEmpty())
79+
assertEquals(newReq.alertIndex, "alertIndex")
80+
assertEquals(newReq.associatedAlertsIndex, "associatedAlertsIndex")
4481
assertTrue(newReq.getAssociatedAlerts)
4582
}
4683

@@ -55,7 +92,8 @@ internal class GetWorkflowAlertsRequestTests {
5592
getAssociatedAlerts = true,
5693
workflowIds = listOf("w1, w2"),
5794
alertIds = emptyList(),
58-
alertIndex = null
95+
alertIndex = null,
96+
associatedAlertsIndex = null
5997
)
6098
assertNotNull(req)
6199
assertNull(req.validate())

src/test/kotlin/org/opensearch/commons/alerting/action/GetWorkflowResponseTests.kt

Lines changed: 41 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,21 @@ import org.junit.jupiter.api.Assertions
44
import org.junit.jupiter.api.Test
55
import org.opensearch.common.io.stream.BytesStreamOutput
66
import org.opensearch.common.io.stream.StreamInput
7+
import org.opensearch.commons.alerting.model.CompositeInput
8+
import org.opensearch.commons.alerting.model.IntervalSchedule
9+
import org.opensearch.commons.alerting.model.Workflow
10+
import org.opensearch.commons.alerting.randomDelegate
11+
import org.opensearch.commons.alerting.randomUser
712
import org.opensearch.commons.alerting.randomWorkflow
813
import org.opensearch.rest.RestStatus
14+
import java.time.Instant
15+
import java.time.temporal.ChronoUnit
916

1017
class GetWorkflowResponseTests {
1118

1219
@Test
13-
fun testGetWorkflowRequest() {
14-
val workflow = randomWorkflow()
20+
fun testGetWorkflowResponse() {
21+
val workflow = randomWorkflow(auditDelegateMonitorAlerts = false)
1522
val response = GetWorkflowResponse(
1623
id = "id", version = 1, seqNo = 1, primaryTerm = 1, status = RestStatus.OK, workflow = workflow
1724
)
@@ -20,7 +27,39 @@ class GetWorkflowResponseTests {
2027
val sin = StreamInput.wrap(out.bytes().toBytesRef().bytes)
2128
val newRes = GetWorkflowResponse(sin)
2229
Assertions.assertEquals("id", newRes.id)
30+
Assertions.assertFalse(newRes.workflow!!.auditDelegateMonitorAlerts!!)
2331
Assertions.assertEquals(workflow.name, newRes.workflow!!.name)
2432
Assertions.assertEquals(workflow.owner, newRes.workflow!!.owner)
2533
}
34+
35+
@Test
36+
fun testGetWorkflowResponseWhereAuditDelegateMonitorAlertsFlagIsNotSet() {
37+
val workflow = Workflow(
38+
id = "",
39+
version = Workflow.NO_VERSION,
40+
name = "test",
41+
enabled = true,
42+
schemaVersion = 2,
43+
schedule = IntervalSchedule(1, ChronoUnit.MINUTES),
44+
lastUpdateTime = Instant.now(),
45+
enabledTime = Instant.now(),
46+
workflowType = Workflow.WorkflowType.COMPOSITE,
47+
user = randomUser(),
48+
inputs = listOf(CompositeInput(org.opensearch.commons.alerting.model.Sequence(listOf(randomDelegate())))),
49+
owner = "",
50+
triggers = listOf()
51+
)
52+
val response = GetWorkflowResponse(
53+
id = "id", version = 1, seqNo = 1, primaryTerm = 1, status = RestStatus.OK, workflow = workflow
54+
)
55+
val out = BytesStreamOutput()
56+
response.writeTo(out)
57+
val sin = StreamInput.wrap(out.bytes().toBytesRef().bytes)
58+
val newRes = GetWorkflowResponse(sin)
59+
Assertions.assertEquals("id", newRes.id)
60+
Assertions.assertTrue(newRes.workflow!!.auditDelegateMonitorAlerts!!)
61+
Assertions.assertEquals(workflow.name, newRes.workflow!!.name)
62+
Assertions.assertEquals(workflow.owner, newRes.workflow!!.owner)
63+
Assertions.assertEquals(workflow.auditDelegateMonitorAlerts, newRes.workflow!!.auditDelegateMonitorAlerts)
64+
}
2665
}

src/test/kotlin/org/opensearch/commons/alerting/action/IndexWorkflowRequestTests.kt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ class IndexWorkflowRequestTests {
2929

3030
val req = IndexWorkflowRequest(
3131
"1234", 1L, 2L, WriteRequest.RefreshPolicy.IMMEDIATE, RestRequest.Method.POST,
32-
randomWorkflow()
32+
randomWorkflow(auditDelegateMonitorAlerts = false)
3333
)
3434
Assertions.assertNotNull(req)
3535

@@ -42,6 +42,7 @@ class IndexWorkflowRequestTests {
4242
Assertions.assertEquals(2L, newReq.primaryTerm)
4343
Assertions.assertEquals(RestRequest.Method.POST, newReq.method)
4444
Assertions.assertNotNull(newReq.workflow)
45+
Assertions.assertFalse(newReq.workflow.auditDelegateMonitorAlerts!!)
4546
}
4647

4748
@Test

src/test/kotlin/org/opensearch/commons/alerting/model/XContentTests.kt

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -187,6 +187,14 @@ class XContentTests {
187187
Assertions.assertEquals(workflow, parsedMonitor, "Round tripping BucketLevelMonitor doesn't work")
188188
}
189189

190+
@Test
191+
fun `test composite workflow parsing with auditDelegateMonitorAlerts flag disabled`() {
192+
val workflow = randomWorkflow(auditDelegateMonitorAlerts = false)
193+
val monitorString = workflow.toJsonStringWithUser()
194+
val parsedMonitor = Workflow.parse(parser(monitorString))
195+
Assertions.assertEquals(workflow, parsedMonitor, "Round tripping BucketLevelMonitor doesn't work")
196+
}
197+
190198
@Test
191199
fun `test query-level trigger parsing`() {
192200
val trigger = randomQueryLevelTrigger()

0 commit comments

Comments
 (0)