@@ -2,6 +2,10 @@ package com.netflix.spinnaker.keel.slack
2
2
3
3
import com.netflix.spinnaker.keel.api.DeliveryConfig
4
4
import com.netflix.spinnaker.keel.api.Environment
5
+ import com.netflix.spinnaker.keel.api.NotificationFrequency
6
+ import com.netflix.spinnaker.keel.api.NotificationFrequency.normal
7
+ import com.netflix.spinnaker.keel.api.NotificationFrequency.quiet
8
+ import com.netflix.spinnaker.keel.api.NotificationFrequency.verbose
5
9
import com.netflix.spinnaker.keel.api.NotificationType
6
10
import com.netflix.spinnaker.keel.api.artifacts.DeliveryArtifact
7
11
import com.netflix.spinnaker.keel.api.constraints.ConstraintStatus
@@ -16,10 +20,25 @@ import com.netflix.spinnaker.keel.events.PinnedNotification
16
20
import com.netflix.spinnaker.keel.events.UnpinnedNotification
17
21
import com.netflix.spinnaker.keel.lifecycle.LifecycleEvent
18
22
import com.netflix.spinnaker.keel.lifecycle.LifecycleEventStatus
23
+ import com.netflix.spinnaker.keel.notifications.NotificationType.APPLICATION_PAUSED
24
+ import com.netflix.spinnaker.keel.notifications.NotificationType.APPLICATION_RESUMED
25
+ import com.netflix.spinnaker.keel.notifications.NotificationType.ARTIFACT_DEPLOYMENT_FAILED
26
+ import com.netflix.spinnaker.keel.notifications.NotificationType.ARTIFACT_DEPLOYMENT_SUCCEDEED
27
+ import com.netflix.spinnaker.keel.notifications.NotificationType.ARTIFACT_MARK_AS_BAD
28
+ import com.netflix.spinnaker.keel.notifications.NotificationType.ARTIFACT_PINNED
29
+ import com.netflix.spinnaker.keel.notifications.NotificationType.ARTIFACT_UNPINNED
30
+ import com.netflix.spinnaker.keel.notifications.NotificationType.DELIVEY_CONFIG_UPDATED
31
+ import com.netflix.spinnaker.keel.notifications.NotificationType.LIFECYCLE_EVENT
32
+ import com.netflix.spinnaker.keel.notifications.NotificationType.MANUAL_JUDGMENT_APPROVED
33
+ import com.netflix.spinnaker.keel.notifications.NotificationType.MANUAL_JUDGMENT_AWAIT
34
+ import com.netflix.spinnaker.keel.notifications.NotificationType.MANUAL_JUDGMENT_REJECTED
35
+ import com.netflix.spinnaker.keel.notifications.NotificationType.TEST_FAILED
36
+ import com.netflix.spinnaker.keel.notifications.NotificationType.TEST_PASSED
19
37
import com.netflix.spinnaker.keel.persistence.KeelRepository
20
38
import com.netflix.spinnaker.keel.slack.handlers.SlackNotificationHandler
21
39
import com.netflix.spinnaker.keel.slack.handlers.supporting
22
40
import com.netflix.spinnaker.keel.telemetry.ArtifactVersionVetoed
41
+ import com.netflix.spinnaker.keel.telemetry.VerificationCompleted
23
42
import org.slf4j.LoggerFactory
24
43
import org.springframework.context.event.EventListener
25
44
import org.springframework.stereotype.Component
@@ -62,7 +81,7 @@ class NotificationEventListener(
62
81
application = config.application,
63
82
time = clock.instant()
64
83
),
65
- Type . ARTIFACT_PINNED ,
84
+ ARTIFACT_PINNED ,
66
85
pin.targetEnvironment)
67
86
}
68
87
@@ -95,7 +114,7 @@ class NotificationEventListener(
95
114
user = user,
96
115
targetEnvironment = targetEnvironment
97
116
),
98
- Type . ARTIFACT_UNPINNED ,
117
+ ARTIFACT_UNPINNED ,
99
118
targetEnvironment)
100
119
101
120
log.debug(" no environment $targetEnvironment was found in the config named ${config.name} " )
@@ -125,7 +144,7 @@ class NotificationEventListener(
125
144
application = config.name,
126
145
comment = veto.comment
127
146
),
128
- Type . ARTIFACT_MARK_AS_BAD ,
147
+ ARTIFACT_MARK_AS_BAD ,
129
148
veto.targetEnvironment
130
149
)
131
150
}
@@ -142,7 +161,7 @@ class NotificationEventListener(
142
161
time = clock.instant(),
143
162
application = application
144
163
),
145
- Type . APPLICATION_PAUSED )
164
+ APPLICATION_PAUSED )
146
165
}
147
166
148
167
}
@@ -158,7 +177,7 @@ class NotificationEventListener(
158
177
time = clock.instant(),
159
178
application = application
160
179
),
161
- Type . APPLICATION_RESUMED )
180
+ APPLICATION_RESUMED )
162
181
}
163
182
}
164
183
@@ -184,7 +203,7 @@ class NotificationEventListener(
184
203
eventType = type,
185
204
application = config.application
186
205
),
187
- Type . LIFECYCLE_EVENT ,
206
+ LIFECYCLE_EVENT ,
188
207
artifact = deliveryArtifact)
189
208
}
190
209
}
@@ -210,7 +229,7 @@ class NotificationEventListener(
210
229
priorVersion = priorVersion,
211
230
status = DeploymentStatus .SUCCEEDED
212
231
),
213
- Type . ARTIFACT_DEPLOYMENT ,
232
+ ARTIFACT_DEPLOYMENT_SUCCEDEED ,
214
233
targetEnvironment)
215
234
}
216
235
}
@@ -238,7 +257,7 @@ class NotificationEventListener(
238
257
targetEnvironment = veto.targetEnvironment,
239
258
status = DeploymentStatus .FAILED
240
259
),
241
- Type . ARTIFACT_DEPLOYMENT ,
260
+ ARTIFACT_DEPLOYMENT_FAILED ,
242
261
veto.targetEnvironment)
243
262
}
244
263
}
@@ -257,7 +276,7 @@ class NotificationEventListener(
257
276
258
277
val deliveryArtifact = config.artifacts.find {
259
278
it.reference == currentState.artifactReference
260
- } .also {
279
+ }.also {
261
280
if (it == null ) log.debug(" Artifact with reference ${currentState.artifactReference} not found in delivery config" )
262
281
} ? : return
263
282
@@ -266,7 +285,7 @@ class NotificationEventListener(
266
285
log.debug(" $deliveryArtifact version ${currentState.artifactVersion} not found. Can't send manual judgement notification." )
267
286
return
268
287
}
269
- val currentArtifact = repository.getArtifactVersionByPromotionStatus(config, currentState.environmentName , deliveryArtifact, PromotionStatus .CURRENT )
288
+ val currentArtifact = repository.getArtifactVersionByPromotionStatus(config, currentState.environmentName, deliveryArtifact, PromotionStatus .CURRENT )
270
289
271
290
sendSlackMessage(
272
291
config,
@@ -279,12 +298,57 @@ class NotificationEventListener(
279
298
deliveryArtifact = deliveryArtifact,
280
299
stateUid = currentState.uid
281
300
),
282
- Type . MANUAL_JUDGMENT ,
301
+ MANUAL_JUDGMENT_AWAIT ,
283
302
environment.name)
284
303
}
285
304
}
286
305
}
287
306
307
+ @EventListener(VerificationCompleted ::class )
308
+ fun onVerificationCompletedNotification (notification : VerificationCompleted ) {
309
+ log.debug(" Received verification completed event: $notification " )
310
+ with (notification) {
311
+ if (status != ConstraintStatus .PASS && status != ConstraintStatus .FAIL ) {
312
+ log.debug(" Not sending notification for verification completed with status $status it's not pass/fail. Ignoring notification for" +
313
+ " application $application " )
314
+ return
315
+ }
316
+ val config = repository.getDeliveryConfig(notification.deliveryConfigName)
317
+
318
+ val deliveryArtifact = config.artifacts.find {
319
+ it.reference == notification.artifactReference
320
+ }.also {
321
+ if (it == null ) log.debug(" Artifact with reference ${notification.artifactReference} not found in delivery config" )
322
+ } ? : return
323
+
324
+ val artifactVersion = repository.getArtifactVersion(deliveryArtifact, notification.artifactVersion, null )
325
+ if (artifactVersion == null ) {
326
+ log.debug(" artifact version is null for application ${config.application} . Can't send verification completed notification." )
327
+ return
328
+ }
329
+
330
+ val type = when (status) {
331
+ ConstraintStatus .PASS -> TEST_PASSED
332
+ ConstraintStatus .FAIL -> TEST_FAILED
333
+ // We shouldn't get here as we checked prior that status is either fail/pass
334
+ else -> TEST_PASSED
335
+ }
336
+
337
+ sendSlackMessage(
338
+ config,
339
+ SlackVerificationCompletedNotification (
340
+ time = clock.instant(),
341
+ application = config.application,
342
+ artifact = artifactVersion.copy(reference = deliveryArtifact.reference),
343
+ targetEnvironment = environmentName,
344
+ deliveryArtifact = deliveryArtifact,
345
+ status = status
346
+ ),
347
+ type,
348
+ environmentName)
349
+ }
350
+ }
351
+
288
352
289
353
private inline fun <reified T : SlackNotificationEvent > sendSlackMessage (config : DeliveryConfig , message : T , type : Type ,
290
354
targetEnvironment : String? = null,
@@ -306,10 +370,25 @@ class NotificationEventListener(
306
370
307
371
environments.flatMap { it.notifications }
308
372
.filter { it.type == NotificationType .slack }
373
+ .filter { translateFrequencyToEvents(it.frequency).contains(type) }
309
374
.groupBy { it.address }
310
375
.forEach { (channel, _) ->
311
376
handler.sendMessage(message, channel)
312
377
}
313
378
}
379
+
380
+
381
+ fun translateFrequencyToEvents (frequency : NotificationFrequency ): List <Type > {
382
+ val quietNotifications = listOf (ARTIFACT_MARK_AS_BAD , ARTIFACT_PINNED , ARTIFACT_UNPINNED , LIFECYCLE_EVENT , APPLICATION_PAUSED ,
383
+ APPLICATION_RESUMED , MANUAL_JUDGMENT_AWAIT , ARTIFACT_DEPLOYMENT_FAILED , TEST_FAILED )
384
+ val normalNotifications = quietNotifications + listOf (ARTIFACT_DEPLOYMENT_SUCCEDEED , DELIVEY_CONFIG_UPDATED , TEST_PASSED )
385
+ val verboseNotifications = normalNotifications + listOf (MANUAL_JUDGMENT_REJECTED , MANUAL_JUDGMENT_APPROVED )
386
+
387
+ return when (frequency) {
388
+ verbose -> verboseNotifications
389
+ normal -> normalNotifications
390
+ quiet -> quietNotifications
391
+ }
392
+ }
314
393
}
315
394
0 commit comments