app/notification/BakeFailedNotifier.scala (97 lines of code) (raw):
package notification
import com.gu.anghammarad.Anghammarad
import com.gu.anghammarad.models.{
Action,
AwsAccount,
Email,
Notification,
Preferred
}
import data.{Bakes, Dynamo}
import models.{Bake, BakeId, BakeStatus, NotificationConfig}
import services.Loggable
import scala.concurrent.ExecutionContext
import scala.util.{Failure, Success}
object BakeFailedNotifier extends Loggable {
private val channel: Preferred = Preferred(Email)
/** Generates an anghammarad notification. The targets of the notification are
* generated based off the accounts to which an encrypted copy has been
* requested. If no encrypted copies have been requested return None
*/
def makeNotification(
bake: Bake,
bakeStatus: BakeStatus,
config: NotificationConfig
): Option[Notification] = {
val targets = bake.recipe.encryptFor.map(m => AwsAccount(m.accountNumber))
val statusString = bakeStatus.toString.toLowerCase()
val actions = List(
Action(
s"View recipe ${bake.recipe.id} in AMIgo",
s"${config.baseUrl}/recipes/${bake.recipe.id}"
),
Action(
s"Check bake log for ${bake.bakeId}",
s"${config.baseUrl}/recipes/${bake.recipe.id}/bakes/${bake.buildNumber}"
)
)
val stageString =
if (config.amigoStage != "PROD") s"(AMIgo ${config.amigoStage})" else ""
if (targets.nonEmpty) {
Some(
Notification(
s"AMIgo bake ${bake.bakeId} $statusString $stageString",
s"""
|Unfortunately a bake on ${bake.recipe.id} has $statusString. Sometimes failures happen due to AMIgo out of
| memory errors and can be fixed simply by re-running the bake and changing the schedule to a less busy time.
| See below for links to the AMIgo dashboard where you can debug this issue or kick off another bake.
| For help, don't hesitate to contact the developer experience team: devx@theguardian.com.
|""".stripMargin,
actions,
targets,
channel,
"AMIgo"
)
)
} else {
log.info(
s"No anghammarad targets available for bake ${bake.recipe.id},${bake.buildNumber} - likely no encrypted copies have been requested for the recipe."
)
None
}
}
def logFailure(configAvailable: Boolean, bakeId: BakeId): Unit = {
if (configAvailable) {
log.info(
s"Failed to fetch bake ${bakeId} from DB. Bake failed notification not sent."
)
} else {
log.info(
s"Missing notification config - notification for bake failure ${bakeId} not sent"
)
}
}
def notifyBakeFailed(
bakeId: BakeId,
bakeStatus: BakeStatus,
notificationConfig: Option[NotificationConfig]
)(implicit exec: ExecutionContext, dynamo: Dynamo): Unit = {
val notificationResult = for {
config <- notificationConfig
bake <- Bakes.findById(bakeId.recipeId, bakeId.buildNumber)
notification <- makeNotification(bake, bakeStatus, config)
} yield {
Anghammarad
.notify(notification, config.snsTopicArn, config.snsClient)
.onComplete {
case Success(value) =>
log.info(
s"Sent notification to anghammarard about recipe ${bake.recipe.id} bake failure. Status: $value"
)
case Failure(exception) =>
log.error(
s"Failed to send notification about recipe ${bake.recipe.id} bake failure",
exception
)
}
}
if (notificationResult.isEmpty)
logFailure(notificationConfig.isEmpty, bakeId)
}
}