app/controllers/BakeController.scala (132 lines of code) (raw):

package controllers import com.amazonaws.services.s3.{AmazonS3, AmazonS3Client} import com.gu.googleauth.{AuthAction, GoogleAuthConfig} import data._ import event._ import models.BakeStatus.DeletionScheduled import packer._ import models._ import play.api.i18n.I18nSupport import play.api.mvc._ import services.{AmiMetadataLookup, Loggable, PrismData} import play.api.libs.json._ import prism.{RecipeUsage, SimpleBakeUsage} class BakeController( val authAction: AuthAction[AnyContent], stage: String, prism: PrismData, components: ControllerComponents, ansibleVars: Map[String, String], debugAvailable: Boolean, amiMetadataLookup: AmiMetadataLookup, amigoDataBucket: Option[String], s3Client: AmazonS3, packerRunner: PackerRunner, bakeDeletionFrequencyMinutes: Int )(implicit dynamo: Dynamo, packerConfig: PackerConfig, eventBus: EventBus) extends AbstractController(components) with I18nSupport with Loggable { def startBaking(recipeId: RecipeId, debug: Boolean): Action[AnyContent] = authAction { request => Recipes.findById(recipeId).fold[Result](NotFound) { recipe => Recipes.incrementAndGetBuildNumber(recipe.id) match { case Some(buildNumber) => val theBake = Bakes .create(recipe, buildNumber, startedBy = request.user.fullName) packerRunner.createImage( stage, theBake, prism, eventBus, ansibleVars, debugAvailable && debug, amiMetadataLookup, amigoDataBucket ) Redirect(routes.BakeController.showBake(recipeId, buildNumber)) case None => val message = s"Failed to get the next build number for recipe $recipeId" log.warn(message) InternalServerError(message) } } } def showBake(recipeId: RecipeId, buildNumber: Int): Action[AnyContent] = authAction { val previousBakeId = Bakes .findPreviousSuccessfulBake(recipeId, buildNumber - 1) .map(_.bakeId) Bakes.findById(recipeId, buildNumber).fold[Result](NotFound) { bake: Bake => val bakeLogs = BakeLogs.list(BakeId(recipeId, buildNumber)) val packageList = PackageList.getPackageList( s3Client, BakeId(recipeId, buildNumber), amigoDataBucket ) val packageListDiff = packageList.flatMap(p => PackageList .getPackageListDiff(s3Client, p, previousBakeId, amigoDataBucket) ) val recipeUsage: RecipeUsage = RecipeUsage(Seq(bake))(prism) val recentCopies = prism.copiedImages(Set(bake.amiId).flatten) val bakeInUse = RecipeUsage.bakeIsUsed(recipeUsage, bake.amiId, recentCopies) Ok( views.html .showBake(bake, bakeLogs, packageList, packageListDiff, bakeInUse) ) } } def bakePackages(recipeId: RecipeId, buildNumber: Int): Action[AnyContent] = authAction { val list = PackageList.getPackageList( s3Client, BakeId(recipeId, buildNumber), amigoDataBucket ) if (list.isLeft) { NotFound( s"Could not find package list for recipe $recipeId, bake $buildNumber: ${list.left.toOption.get}" ) } else { Ok(Json.obj("packages" -> Json.toJson(list.toOption.get))) } } def allBakeUsages: Action[AnyContent] = authAction { val allUsages = RecipeUsage.getUsages(Recipes.list())(prism, dynamo) val bakeUsages = SimpleBakeUsage.fromRecipeUsages(allUsages, amigoDataBucket) Ok(Json.toJson(bakeUsages)) } def deleteConfirm(recipeId: RecipeId, buildNumber: Int): Action[AnyContent] = authAction { implicit request => Bakes.findById(recipeId, buildNumber).fold[Result](NotFound) { bake => val recipeUsage: RecipeUsage = RecipeUsage(Seq(bake))(prism) Ok( views.html.confirmBakeDelete( bake.bakeId, recipeUsage.bakeUsage, bakeDeletionFrequencyMinutes ) ) } } def deleteBake(recipeId: RecipeId, buildNumber: Int): Action[AnyContent] = authAction { implicit request => Bakes.findById(recipeId, buildNumber).fold[Result](NotFound) { bake => val recipeUsage: RecipeUsage = RecipeUsage(Seq(bake))(prism) if (recipeUsage.bakeUsage.nonEmpty) { Conflict( s"Can't delete bake ${bake.bakeId.buildNumber} from recipe ${bake.bakeId.recipeId} as it is still used by ${recipeUsage.bakeUsage.size} resources." ) } else { Bakes.updateStatus(bake.bakeId, DeletionScheduled) Bakes.markToDelete(bake.bakeId) Redirect(routes.RecipeController.showRecipe(recipeId)) } } } }