app/prism/PrismLogic.scala (138 lines of code) (raw):

package prism import models._ import org.joda.time.DateTime import utils.{DateUtils, Percentiles} object PrismLogic { def oldInstances( instanceAmis: List[(Instance, Option[AMI])] ): List[Instance] = { instanceAmis .filter { case (_, amiOpt) => amiOpt.exists(amiIsOld) } .map(_._1) } def stacks(instances: List[Instance]): List[String] = { (for { instance <- instances stack <- instance.stack } yield stack).distinct } def amiArns(instances: List[Instance]): List[String] = instances.flatMap(_.amiArn).distinct /** Associates instances with their AMI */ def instanceAmis( instances: List[Instance], amis: List[AMI] ): List[(Instance, Option[AMI])] = { instances.map { instance => instance -> instance.amiArn.flatMap { amiArn => amis.find(_.arn == amiArn) } } } /** Associates AMIs with instances that use them */ def amiInstances( amis: List[AMI], instances: List[Instance] ): List[(AMI, List[Instance])] = { amis.map { ami => ami -> instances.filter { instance => instance.amiArn.fold(false)(_ == ami.arn) } } } def instanceSSAAs(instances: List[Instance]): List[SSAA] = { val allInstanceSSAs = for { instance <- instances ssaa <- { if (instance.app.isEmpty) List( SSAA( instance.stack, instance.stage, None, instance.meta.origin.accountName ) ) else instance.app.map(app => SSAA( instance.stack, instance.stage, Some(app), instance.meta.origin.accountName ) ) } } yield ssaa allInstanceSSAs.distinct } /** T will either be an AMI, or an AMI attempt. From a full list of Ts and * instances, return each unique SSAA combination with all its associated Ts. */ def amiSSAAs[T]( amisWithInstances: List[(T, List[Instance])] ): Map[SSAA, List[T]] = { val allSSACombos = for { (t, instances) <- amisWithInstances ssaa <- instanceSSAAs(instances) } yield ssaa -> t allSSACombos .groupBy { case (ssaa, _) => ssaa } .map { case (ssaa, ssaaAmis) => ssaa -> ssaaAmis.map { case (_, t) => t } } } /** SSAAs are sorted by their oldest AMI, except for the empty SSAA which * always appears last. */ def sortSSAAmisByAge( ssaaAmis: Map[SSAA, List[AMI]] ): List[(SSAA, List[AMI])] = { ssaaAmis.toList.sortBy { case (ssaa, amis) => if (ssaa.isEmpty) { // put empty SSA last DateTime.now.getMillis } else { val creationDates = for { ami <- amis creationDate <- ami.creationDate } yield creationDate.getMillis creationDates.headOption.getOrElse(0L) } } } def amiIsOld(ami: AMI): Boolean = { ami.creationDate .flatMap { creationDate => DateUtils.getAge(creationDate).map { case Fresh | Turning => false case _ => true } } .getOrElse(true) } def instancesAmisAgePercentiles( instances: List[(Instance, Option[AMI])] ): Percentiles = { val ages = instances.flatMap { case (instance, ami) => ami.flatMap(_.creationDate.map(DateUtils.daysAgo)) } Percentiles(ages) } /** T will either be an AMI, or an AMI attempt. From a full list of Ts and * instances and a list of SSAs, return unique SSAA and AMI combinations with * their respective number of instances */ def instancesCountPerSsaPerAmi[T]( amisWithInstances: List[(T, List[Instance])], ssas: List[SSAA] ): Map[(SSAA, T), Int] = { for { (t, instances) <- amisWithInstances.toMap ssaa <- ssas instancesCount = instances.count(i => doesInstanceBelongToSSA(i, ssaa)) if (instancesCount > 0) } yield (ssaa, t) -> instancesCount } def doesInstanceBelongToSSA(instance: Instance, ssaa: SSAA): Boolean = ssaa.stack == instance.stack && ssaa.stage.fold(true)(s => instance.stage.contains(s)) && ssaa.app.fold(true)(instance.app.contains(_)) /** Sort Launch Configurations by accountName (ie. the owner of the stack) and * by Launch Configuration name, in ascending order */ def sortLCsByOwner( configs: List[LaunchConfiguration] ): List[LaunchConfiguration] = { configs.sortBy(lc => (lc.meta.origin.accountName.getOrElse(""), lc.name)) } /** Sorts Instances by Stack, App, Stage in ascending order */ def sortInstancesByStack(instances: List[Instance]): List[Instance] = { instances.sortBy(instance => (instance.stack, instance.app.headOption, instance.stage) ) } }