Skip to content
This repository was archived by the owner on Dec 20, 2025. It is now read-only.

Commit 284f955

Browse files
fix(notifications): Prevent duplicate notifications for Manual Judgment stages (#1506) (#1508)
(cherry picked from commit 989e20b) Co-authored-by: Shlomo Daari <104773977+shlomodaari@users.noreply.github.com>
1 parent 21721cb commit 284f955

File tree

2 files changed

+114
-3
lines changed

2 files changed

+114
-3
lines changed

echo-core/src/main/java/com/netflix/spinnaker/echo/notification/AbstractEventNotificationAgent.java

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,21 @@ public void processEvent(Event event) {
108108
return;
109109
}
110110

111+
if (STAGE.equals(configType)) {
112+
Object when = event.getContent().get("when");
113+
if (when != null) {
114+
if (when instanceof String) {
115+
if (!isManualJudgmentMatchStringCase((String) when, status)) {
116+
return;
117+
}
118+
} else if (when instanceof Collection) {
119+
if (!isManualJudgmentMatchCollectionCase((Collection<String>) when, status)) {
120+
return;
121+
}
122+
}
123+
}
124+
}
125+
111126
// TODO(lpollo): why do we have a 'CANCELED' status and a canceled property, which are prime for
112127
// inconsistency?
113128
if (isExecution(configType) && isExecutionCanceled(event)) {
@@ -241,11 +256,26 @@ String getName() {
241256
}
242257

243258
private static boolean isManualJudgmentMatchStringCase(String when, String status) {
259+
// For Manual Judgment stages, only send specialized notifications, not standard stage ones
260+
// This prevents duplicate notifications between standard stage and manual judgment events
261+
if (ManualJudgmentCondition.MANUAL_JUDGMENT.getName().equals(when)
262+
|| ManualJudgmentCondition.CONTINUE.getName().equals(when)
263+
|| ManualJudgmentCondition.STOP.getName().equals(when)) {
264+
return false;
265+
}
244266
return status.equals(MANUAL_JUDGMENT_CONDITIONS.get(when));
245267
}
246268

247269
private static boolean isManualJudgmentMatchCollectionCase(
248270
Collection<String> when, String status) {
271+
// For Manual Judgment stages, only send specialized notifications, not standard stage ones
272+
// This prevents duplicate notifications between standard stage and manual judgment events
273+
if (when.contains(ManualJudgmentCondition.MANUAL_JUDGMENT.getName())
274+
|| when.contains(ManualJudgmentCondition.CONTINUE.getName())
275+
|| when.contains(ManualJudgmentCondition.STOP.getName())) {
276+
return false;
277+
}
278+
249279
for (String condition : when) {
250280
if (status.equals(MANUAL_JUDGMENT_CONDITIONS.get(condition))) {
251281
return true;

echo-core/src/test/groovy/com/netflix/spinnaker/echo/notification/AbstractEventNotificationAgentSpec.groovy

Lines changed: 84 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,45 @@ class AbstractEventNotificationAgentSpec extends Specification {
9292
// notifications ON, stage is synthetic
9393
fakeStageEvent("orca:stage:complete", "stage.complete", false, true) || 0
9494
}
95+
96+
@Unroll
97+
def "prevents stage notifications for Manual Judgment stages with specialized notifications"() {
98+
given:
99+
subclassMock.sendNotifications(*_) >> { notification, application, event_local, config, status_param -> }
100+
101+
when:
102+
// Create a stage event with specialized Manual Judgment notification type
103+
def event = createManualJudgmentStageEvent("orca:stage:" + status, notificationType)
104+
agent.processEvent(event)
105+
106+
then:
107+
// Verify no notifications are sent due to our fix
108+
0 * subclassMock.sendNotifications(*_)
109+
110+
where:
111+
status | notificationType
112+
"starting" | "manualJudgment"
113+
"complete" | "manualJudgmentContinue"
114+
"failed" | "manualJudgmentStop"
115+
}
116+
117+
@Unroll
118+
def "still sends stage notifications for non-Manual Judgment stages"() {
119+
given:
120+
subclassMock.sendNotifications(*_) >> { notification, application, event_local, config, status_param -> }
121+
122+
when:
123+
// Create a stage event with regular stage notification type
124+
def event = createRegularStageEvent("orca:stage:" + status, "stage." + status)
125+
agent.processEvent(event)
126+
127+
then:
128+
// Verify notifications are still sent for normal stages
129+
1 * subclassMock.sendNotifications(*_)
130+
131+
where:
132+
status << ["starting", "complete", "failed"]
133+
}
95134

96135
@Unroll
97136
def "sends notifications for ManualJudgment stage based on status and configuration"() {
@@ -106,9 +145,9 @@ class AbstractEventNotificationAgentSpec extends Specification {
106145

107146
where:
108147
event || expectedNotifications
109-
fakeStageEvent("orca:stage:complete", "manualJudgmentContinue") || 1
110-
fakeStageEvent("orca:stage:starting", "manualJudgment") || 1
111-
fakeStageEvent("orca:stage:failed", "manualJudgmentStop") || 1
148+
fakeStageEvent("orca:stage:complete", "manualJudgmentContinue") || 0
149+
fakeStageEvent("orca:stage:starting", "manualJudgment") || 0
150+
fakeStageEvent("orca:stage:failed", "manualJudgmentStop") || 0
112151
}
113152

114153
private def fakePipelineEvent(String type, String status, String notifyWhen, Map extraExecutionProps = [:]) {
@@ -173,4 +212,46 @@ class AbstractEventNotificationAgentSpec extends Specification {
173212

174213
return new Event(eventProps)
175214
}
215+
216+
private Event createManualJudgmentStageEvent(String eventType, String notificationType) {
217+
return new Event(
218+
details: [
219+
type: eventType,
220+
application: "testapp"
221+
],
222+
content: [
223+
context: [
224+
type: "manualJudgment",
225+
sendNotifications: true,
226+
notifications: [
227+
[
228+
type: "fake",
229+
when: [notificationType]
230+
]
231+
]
232+
]
233+
]
234+
)
235+
}
236+
237+
private Event createRegularStageEvent(String eventType, String notificationType) {
238+
return new Event(
239+
details: [
240+
type: eventType,
241+
application: "testapp"
242+
],
243+
content: [
244+
context: [
245+
type: "regularStage",
246+
sendNotifications: true,
247+
notifications: [
248+
[
249+
type: "fake",
250+
when: [notificationType]
251+
]
252+
]
253+
]
254+
]
255+
)
256+
}
176257
}

0 commit comments

Comments
 (0)