app/prism/RecipeUsage.scala (152 lines of code) (raw):

package prism import data.{Bakes, Dynamo, PackageList, Recipes} import models.{AmiId, Bake, BakeId, Recipe, RecipeId} import play.api.libs.json.Json import prism.Prism.{Image, Instance, LaunchConfiguration, LaunchTemplate} import services.PrismData import play.api.libs.json.OWrites case class Ami(account: String, id: AmiId) case class BakeUsage( amiId: AmiId, bake: Bake, viaCopy: Option[Image], instances: Seq[Instance], launchConfigurations: Seq[LaunchConfiguration], launchTemplates: Seq[LaunchTemplate] ) case class SimpleBakeUsage(bakeId: BakeId, packageListS3Location: String) object SimpleBakeUsage { implicit val writes: OWrites[SimpleBakeUsage] = Json.writes[SimpleBakeUsage] def fromBakeUsage( bakeUsage: BakeUsage, amigoDataBucket: Option[String] ): SimpleBakeUsage = SimpleBakeUsage( bakeUsage.bake.bakeId, PackageList.s3Url( bakeUsage.bake.bakeId, amigoDataBucket.getOrElse("unknown-bucket") ) ) def fromRecipeUsages( recipeUsages: Iterable[RecipeUsage], amigoDataBucket: Option[String] ): Iterable[SimpleBakeUsage] = { recipeUsages.flatMap { usage => usage.bakeUsage.map(bu => SimpleBakeUsage.fromBakeUsage(bu, amigoDataBucket) ) } } } case class RecipeUsage( instances: Seq[Instance], launchConfigurations: Seq[LaunchConfiguration], launchTemplates: Seq[LaunchTemplate], bakeUsage: Seq[BakeUsage] ) object RecipeUsage { def noUsage(): RecipeUsage = RecipeUsage( Seq.empty[Instance], Seq.empty[LaunchConfiguration], Seq.empty[LaunchTemplate], Seq.empty[BakeUsage] ) def allAmis(amiIds: Iterable[AmiId], amigoAccount: String)(implicit prismAgents: PrismData ): List[Ami] = { val amis = amiIds.map(Ami(amigoAccount, _)) val copiedAmiImages = prismAgents.copiedImages(amiIds.toSet).values.flatten val copiedAmis = copiedAmiImages.map(image => Ami(image.ownerId, image.imageId)) amis.toList ++ copiedAmis } def apply( bakes: Iterable[Bake] )(implicit prismAgents: PrismData): RecipeUsage = { val bakedAmiLookupMap = bakes.flatMap(b => b.amiId.map(_ -> b)).toMap val bakedAmiIds = bakedAmiLookupMap.keys.toList val copiedAmis = prismAgents.copiedImages(bakedAmiIds.toSet).values.flatten val copiedAmiIds = copiedAmis.map(_.imageId) val amiIds = bakedAmiIds ++ copiedAmiIds val instances = prismAgents.allInstances.filter(instance => amiIds.contains(instance.imageId) ) val launchConfigurations = prismAgents.allLaunchConfigurations.filter(lc => amiIds.contains(lc.imageId) ) val launchTemplates = prismAgents.allLaunchTemplates.filter(lt => amiIds.contains(lt.imageId)) val amis = (instances.map(_.imageId) ++ launchConfigurations.map( _.imageId ) ++ launchTemplates.map(_.imageId)).distinct val copiedAmiLookupMap = copiedAmis.map { image => image.imageId -> image }.toMap val bakeUsage = amis.map { ami => val maybeDirectBake = bakedAmiLookupMap.get(ami) val maybeCopy = copiedAmiLookupMap.get(ami) val bake = (maybeDirectBake, maybeCopy) match { case (Some(directBake), None) => directBake case (None, Some(copy)) => bakedAmiLookupMap(copy.copiedFromAMI) case _ => throw new IllegalArgumentException( "AMI ID provided neither direct nor copied" ) } val amiInstances = instances.filter(_.imageId == ami) val amiLc = launchConfigurations.filter(_.imageId == ami) val amiLt = launchTemplates.filter(_.imageId == ami) BakeUsage(ami, bake, maybeCopy, amiInstances, amiLc, amiLt) } RecipeUsage(instances, launchConfigurations, launchTemplates, bakeUsage) } def forAll(recipes: Iterable[Recipe], findBakes: RecipeId => Iterable[Bake])( implicit prismAgents: PrismData ): Map[Recipe, RecipeUsage] = { recipes.map(r => r -> apply(findBakes(r.id))).toMap } def getUsagesMap(recipes: Iterable[Recipe])(implicit prismAgents: PrismData, dynamo: Dynamo ): Map[Recipe, RecipeUsage] = { forAll(recipes, findBakes = recipeId => Bakes.list(recipeId))(prismAgents) } def getUsages( recipes: Iterable[Recipe] )(implicit prismAgents: PrismData, dynamo: Dynamo): Iterable[RecipeUsage] = { recipes.map(r => RecipeUsage(Bakes.list(r.id))) } def hasUsage(recipe: Recipe, usages: Map[Recipe, RecipeUsage]): Boolean = { usages .get(recipe) .exists(u => u.launchTemplates.nonEmpty || u.launchConfigurations.nonEmpty || u.instances.nonEmpty ) } def amiUsages(recipeUsage: RecipeUsage, amiId: AmiId): RecipeUsage = { recipeUsage.copy( launchConfigurations = recipeUsage.launchConfigurations.filter(_.imageId == amiId), launchTemplates = recipeUsage.launchTemplates.filter(_.imageId == amiId), instances = recipeUsage.instances.filter(_.imageId == amiId), bakeUsage = recipeUsage.bakeUsage.filter(bu => bu.amiId == amiId || bu.viaCopy.exists(i => i.imageId == amiId) ) ) } def amiIsUsed(recipeUsage: RecipeUsage, amiId: AmiId): Boolean = { amiUsages(recipeUsage, amiId).bakeUsage.nonEmpty } def bakeIsUsed( recipeUsage: RecipeUsage, amiId: Option[AmiId], recentCopies: Map[AmiId, Seq[Image]] ): Boolean = { val copies = amiId.flatMap(id => recentCopies.get(id)).getOrElse(Nil) amiId.exists(id => amiIsUsed(recipeUsage, id)) || copies.exists(c => amiIsUsed(recipeUsage, c.imageId) ) } }