app/controllers/BaseImageController.scala (253 lines of code) (raw):
package controllers
import com.gu.googleauth.AuthAction
import controllers.ControllerHelpers.parseEnabledRoles
import data._
import models._
import org.joda.time.DateTime
import play.api.data.{Form, Mapping}
import play.api.data.Forms._
import play.api.i18n.I18nSupport
import play.api.mvc._
import prism.RecipeUsage
import services.PrismData
class BaseImageController(
val authAction: AuthAction[AnyContent],
prismAgents: PrismData,
components: ControllerComponents
)(implicit dynamo: Dynamo)
extends AbstractController(components)
with I18nSupport {
import BaseImageController._
def listBaseImages = authAction {
val baseImages = BaseImages.list()
val usageMap =
baseImages.map(base => (base, Recipes.findByBaseImage(base.id))).toMap
Ok(views.html.baseImages(usageMap))
}
def showBaseImage(id: BaseImageId) = authAction { implicit request =>
BaseImages.findById(id).fold[Result](NotFound) { image =>
val usedByRecipes = Recipes.findByBaseImage(id)
val usages: Map[Recipe, RecipeUsage] =
RecipeUsage.getUsagesMap(usedByRecipes)(prismAgents, dynamo)
val (usedRecipes, unusedRecipes) =
usedByRecipes.partition(r => RecipeUsage.hasUsage(r, usages))
Ok(
views.html.showBaseImage(
image,
Roles.list,
usedRecipes.toSeq,
unusedRecipes.toSeq,
Forms.cloneBaseImage,
usages
)
)
}
}
def editBaseImage(id: BaseImageId) = authAction { implicit request =>
BaseImages.findById(id).fold[Result](NotFound) { image =>
val form = Forms.editBaseImage.fill(
(
image.description,
image.amiId,
image.linuxDist.getOrElse(Ubuntu),
image.eolDate.getOrElse(DateTime.now).toLocalDate.toDate,
image.requiresXLargeBuilder
)
)
Ok(views.html.editBaseImage(image, form, Roles.listIds))
}
}
def updateBaseImage(id: BaseImageId) = authAction(parse.formUrlEncoded) {
implicit request =>
BaseImages.findById(id).fold[Result](NotFound) { image =>
Forms.editBaseImage
.bindFromRequest()
.fold(
{ formWithErrors =>
BadRequest(
views.html.editBaseImage(image, formWithErrors, Roles.listIds)
)
},
{
case (
description,
amiId,
linuxDist,
eolDate,
requiresXLargeBuilder
) =>
val customisedRoles = parseEnabledRoles(request.body)
customisedRoles.fold(
error => BadRequest(s"Problem parsing roles: $error"),
roles => {
BaseImages.update(
image,
description,
amiId,
linuxDist,
roles,
modifiedBy = request.user.fullName,
new DateTime(eolDate),
requiresXLargeBuilder
)
Redirect(routes.BaseImageController.showBaseImage(id))
.flashing("info" -> "Successfully updated base image")
}
)
}
)
}
}
def newBaseImage = authAction { implicit request =>
Ok(views.html.newBaseImage(Forms.createBaseImage, Roles.listIds))
}
def createBaseImage = authAction(parse.formUrlEncoded) { implicit request =>
Forms.createBaseImage
.bindFromRequest()
.fold(
{ formWithErrors =>
BadRequest(views.html.newBaseImage(formWithErrors, Roles.listIds))
},
{
case (
id,
description,
amiId,
linuxDist,
eolDate,
requiresXLargeBuilder
) =>
BaseImages.findById(id) match {
case Some(existingImage) =>
val formWithError = Forms.createBaseImage
.fill(
(
id,
description,
amiId,
linuxDist,
eolDate,
requiresXLargeBuilder
)
)
.withError("id", "This base image ID is already in use")
Conflict(views.html.newBaseImage(formWithError, Roles.listIds))
case None =>
val customisedRoles = parseEnabledRoles(request.body)
customisedRoles.fold(
error => BadRequest(s"Problem parsing roles: $error"),
roles => {
BaseImages.create(
id,
description,
amiId,
roles,
createdBy = request.user.fullName,
linuxDist,
Some(new DateTime(eolDate)),
requiresXLargeBuilder
)
Redirect(routes.BaseImageController.showBaseImage(id))
.flashing("info" -> "Successfully created base image")
}
)
}
}
)
}
def cloneBaseImage(id: BaseImageId) = authAction { implicit request =>
Forms.cloneBaseImage
.bindFromRequest()
.fold(
{ form =>
Redirect(routes.BaseImageController.showBaseImage(id)).flashing(
"error" -> s"Failed to clone base image: ${form.errors.head.message}"
)
},
{ newId =>
BaseImages
.findById(newId)
.fold[Result] {
BaseImages.findById(id).fold[Result](NotFound) { baseImage =>
baseImage.linuxDist match {
case Some(linuxDist) =>
BaseImages.create(
id = newId,
description = baseImage.description,
amiId = baseImage.amiId,
builtinRoles = baseImage.builtinRoles,
createdBy = request.user.fullName,
linuxDist = linuxDist,
eolDate = baseImage.eolDate,
requiresXLargeBuilder = baseImage.requiresXLargeBuilder
)
Redirect(routes.BaseImageController.showBaseImage(newId))
.flashing("info" -> "Successfully cloned base image")
case None =>
Redirect(routes.BaseImageController.showBaseImage(id))
.flashing(
"error" -> "Failed to clone base image as it does not have a linux distribution set"
)
}
}
}(_ => Conflict(s"$newId already exists"))
}
)
}
def deleteConfirm(id: BaseImageId) = authAction { implicit request =>
BaseImages.findById(id).fold[Result](NotFound) { recipe =>
val usedByRecipes = Recipes.findByBaseImage(id).toSeq
Ok(views.html.confirmBaseImageDelete(recipe, usedByRecipes))
}
}
def deleteBaseImage(id: BaseImageId) = authAction { implicit request =>
BaseImages.findById(id).fold[Result](NotFound) { image =>
val usedByRecipes = Recipes.findByBaseImage(id)
if (usedByRecipes.isEmpty) {
BaseImages.delete(image)
Redirect(routes.BaseImageController.listBaseImages())
.flashing("info" -> s"Successfully deleted base image ${id.value}")
} else {
Redirect(routes.BaseImageController.showBaseImage(id))
.flashing("error" -> s"Failed to delete base image as it is in use")
}
}
}
}
object BaseImageController {
object Forms {
val amiId =
nonEmptyText(maxLength = 21).transform[AmiId](AmiId.apply, _.value)
val linuxDist: Mapping[LinuxDist] =
nonEmptyText(maxLength = 16)
.verifying(
s"Must be one of ${LinuxDist.all.keys}",
LinuxDist.create(_).nonEmpty
)
.transform(LinuxDist.create(_).get, _.name)
val editBaseImage = Form(
tuple(
"description" -> text(maxLength = 10000),
"amiId" -> amiId,
"linuxDist" -> linuxDist,
"eolDate" -> date("yyyy-MM-dd"),
"requiresXLargeBuilder" -> boolean
)
)
val createBaseImage = Form(
tuple(
"id" -> text(minLength = 3, maxLength = 50)
.transform[BaseImageId](BaseImageId.apply, _.value),
"description" -> text(maxLength = 10000),
"amiId" -> amiId,
"linuxDist" -> linuxDist,
"eolDate" -> date("yyyy-MM-dd"),
"requiresXLargeBuilder" -> boolean
)
)
val cloneBaseImage = Form(
"newId" -> text(minLength = 3, maxLength = 50)
.transform[BaseImageId](BaseImageId.apply, _.value)
)
}
}