app/prism/Recommendations.scala (113 lines of code) (raw):
package prism
import models.AMI
object Recommendations {
/** Searches for a more recent version of the provided AMI.
*
* For Ubuntu AMIs it will check the Name to match the distribution, etc. For
* Machine images and amigo AMIs it will check the tags.
*
* In all cases it should check the virtualisation and architecture as well
* as using the date to see what is newer.
*/
def amiUpgrade(ami: AMI, allAmis: Set[AMI]): Option[AMI] = {
val candidateAmis = allAmis.view
.filter(_.ownerId == ami.ownerId)
.filter(newerThan(ami))
.filter(_.region == ami.region)
.filter(_.virtualizationType == ami.virtualizationType)
.filter(_.architecture == ami.architecture)
.filter(_.rootDeviceType == ami.rootDeviceType)
.filter(_.hypervisor == ami.hypervisor)
.filter(_.creationDate.nonEmpty)
val upgrades = {
// filter on additional ami-creator specific properties where appropriate
if (isUbuntu(ami)) candidateAmis.filter(isUbuntuUpgrade(ami))
else if (isMachineImages(ami))
candidateAmis.filter(isMachineImagesUpgrade(ami))
else if (isAmigo(ami)) candidateAmis.filter(isAmigoUpgrade(ami))
// with Amazon Linux, it is enough to have matched the above conditions
else if (isAmazon(ami)) candidateAmis
// unknown ami type, cannot recommend upgrades
else Set.empty
}
upgrades.toList
.sortBy(_.creationDate.get.getMillis)
.lastOption
}
def amiWithUpgrade(allAmis: Set[AMI])(ami: AMI): AMI = {
ami.copy(upgrade = amiUpgrade(ami, allAmis))
}
/** Ubuntu upgrade is newer image that shares the same name (apart from date
* suffix)
*/
private val UbuntuIdentifier = "(.*)-[\\d\\.]+".r
private[prism] def isUbuntuUpgrade(
ubuntuAmi: AMI
)(candidateAmi: AMI): Boolean = {
(for {
amiName <- ubuntuAmi.name
candidateName <- candidateAmi.name
} yield {
(amiName, candidateName) match {
case (
UbuntuIdentifier(amiIdentifier),
UbuntuIdentifier(candidateIdentifier)
) =>
amiIdentifier == candidateIdentifier
case _ => false
}
}) getOrElse false
}
def isObsoleteUbuntu(ubuntuAmi: AMI): Boolean = {
val DistExtractor = ".*/ubuntu-(\\w+?)-(\\d{2}.\\d{2})-.*".r
ubuntuAmi.name.fold(false) {
case DistExtractor(distName, distNumber) =>
List(
"15.04",
"14.10",
"13.10",
"13.04",
"12.10",
"11.10",
"11.04",
"10.10",
"10.04"
).contains(distNumber)
case _ =>
false
}
}
private[prism] def isMachineImagesUpgrade(
machineImagesAmi: AMI
)(candidateAmi: AMI): Boolean = {
(for {
amiImageName <- machineImagesAmi.tags.get("ImageName")
candidateImageName <- candidateAmi.tags.get("ImageName")
amiBranch <- machineImagesAmi.tags.get("Branch")
candidateBranch <- candidateAmi.tags.get("Branch")
} yield {
amiImageName == candidateImageName && amiBranch == candidateBranch
}) getOrElse false
}
private[prism] def isAmigoUpgrade(
amigoAmi: AMI
)(candidateAmi: AMI): Boolean = {
(for {
amiStage <- amigoAmi.tags.get("AmigoStage")
candidateStage <- candidateAmi.tags.get("AmigoStage")
amiRecipe <- amigoAmi.tags.get("Recipe")
candidateRecipe <- candidateAmi.tags.get("Recipe")
} yield {
amiStage == candidateStage && amiRecipe == candidateRecipe
}) getOrElse false
}
def isUbuntu(ami: AMI): Boolean = ami.ownerId == "099720109477"
def isAmazon(ami: AMI): Boolean = ami.ownerId == "137112412989"
def isMachineImages(ami: AMI): Boolean =
ami.tags.get("BuildName").exists(_.endsWith("-machine-images"))
def isAmigo(ami: AMI): Boolean = ami.tags.get("BuiltBy").contains("amigo")
def isUnknown(ami: AMI): Boolean = {
!(isUbuntu(ami) || isAmazon(ami) || isAmigo(ami) || isMachineImages(ami))
}
def owner(ami: AMI): String = {
if (isUbuntu(ami)) "Ubuntu"
else if (isAmazon(ami)) "Amazon"
else if (isAmigo(ami)) "Amigo"
else if (isMachineImages(ami)) "Machine-images"
else ami.ownerId
}
private[prism] def newerThan(sourceAmi: AMI)(candidateAmi: AMI): Boolean = {
(for {
candidateDate <- candidateAmi.creationDate
amiDate <- sourceAmi.creationDate
} yield {
candidateDate.isAfter(amiDate)
}) getOrElse false
}
}