diff --git a/README.md b/README.md index 8a0091a4..e0998148 100644 --- a/README.md +++ b/README.md @@ -135,6 +135,7 @@ describe("Template Lookup") { - AWS::ElasticBeanstalk::ApplicationVersion - AWS::ElasticBeanstalk::ConfigurationTemplate - AWS::ElasticBeanstalk::Environment +- AWS::EMR::Cluster - AWS::IAM::AccessKey - AWS::IAM::Group - AWS::IAM::InstanceProfile diff --git a/src/main/scala/com/monsanto/arch/cloudformation/model/resource/EMR.scala b/src/main/scala/com/monsanto/arch/cloudformation/model/resource/EMR.scala new file mode 100644 index 00000000..7cccf9b9 --- /dev/null +++ b/src/main/scala/com/monsanto/arch/cloudformation/model/resource/EMR.scala @@ -0,0 +1,124 @@ +package com.monsanto.arch.cloudformation.model.resource + +import com.monsanto.arch.cloudformation.model.{ConditionRef, Token} +import spray.json.{DefaultJsonProtocol, JsObject, JsValue, JsonFormat, RootJsonFormat} +import DefaultJsonProtocol._ + + +case class Application( + AdditionalInfo: Option[Map[String, Token[String]]], + Args: Option[Seq[Token[String]]], + Name: Option[Token[String]], + Version: Option[Token[String]] + ) + +object Application { + implicit val format = jsonFormat4(Application.apply) +} + +case class ScriptBootstrapAction(Args: Option[Seq[Token[String]]], Path: Token[String]) +object ScriptBootstrapAction { + implicit val format = jsonFormat2(ScriptBootstrapAction.apply) +} + +case class BootstrapAction(Name: Token[String], ScriptBootstrapAction: ScriptBootstrapAction) +object BootstrapAction { + implicit val format = jsonFormat2(BootstrapAction.apply) +} + +case class ClusterConfiguration(Classification: Option[Token[String]], + ConfigurationProperties: Option[Map[String, Token[String]]], + Configurations: Option[Seq[ClusterConfiguration]] + ) +object ClusterConfiguration { + implicit object format extends RootJsonFormat[ClusterConfiguration] { + override def write(obj: ClusterConfiguration): JsValue = { + val classification = obj.Classification.map(implicitly[JsonFormat[Token[String]]].write) + val configurationProperties = obj.ConfigurationProperties.map(implicitly[JsonFormat[Map[String, Token[String]]]].write) + lazy val seqWrite = implicitly[RootJsonFormat[Seq[ClusterConfiguration]]] + val configurations = obj.Configurations.map(seqWrite.write) + JsObject(Seq( + "Classification" -> classification, + "ConfigurationProperties" -> configurationProperties, + "Configurations" -> configurations + ).flatMap(t => t._2.map(t._1 -> _).toSeq) + .toMap) + } + + override def read(json: JsValue): ClusterConfiguration = ??? + } +} + +case class VolumeSpecification(Iops: Option[Token[Int]], SizeInGB: Token[Int], VolumeType: Token[String]) +object VolumeSpecification { + implicit val format = jsonFormat3(VolumeSpecification.apply) +} + +case class EbsBlockDeviceConfig(VolumeSpecification: VolumeSpecification, VolumesPerInstance: Option[Token[Int]]) +object EbsBlockDeviceConfig { + implicit val format = jsonFormat2(EbsBlockDeviceConfig.apply) +} + +case class EbsConfiguration(EbsBlockDeviceConfigs: Option[Seq[EbsBlockDeviceConfig]], + EbsOptimized: Option[Token[Boolean]]) +object EbsConfiguration { + implicit val format = jsonFormat2(EbsConfiguration.apply) +} + +case class InstanceGroupConfig( + BidPrice: Option[Token[String]], + Configurations: Option[Seq[ClusterConfiguration]], + EbsConfiguration: Option[EbsConfiguration], + InstanceCount: Token[Int], + InstanceType: Token[String], + Market: Option[Token[String]], + Name: Option[Token[String]] + ) +object InstanceGroupConfig { + implicit val format = jsonFormat7(InstanceGroupConfig.apply) +} + +case class PlacementType(AvailabilityZone: String) +object PlacementType { + implicit val format = jsonFormat1(PlacementType.apply) +} + +case class JobFlowInstancesConfig(AdditionalMasterSecurityGroups: Option[Seq[Token[String]]], + AdditionalSlaveSecurityGroups: Option[Seq[Token[String]]], + CoreInstanceGroup: InstanceGroupConfig, + Ec2KeyName: Option[Token[String]], + Ec2SubnetId: Option[Token[String]], + EmrManagedMasterSecurityGroup: Option[Token[String]], + EmrManagedSlaveSecurityGroup: Option[Token[String]], + HadoopVersion: Option[Token[String]], + MasterInstanceGroup: InstanceGroupConfig, + Placement: Option[PlacementType], + ServiceAccessSecurityGroup: Option[Token[String]], + TerminationProtected: Option[Token[Boolean]] + ) +object JobFlowInstancesConfig { + implicit val format = jsonFormat12(JobFlowInstancesConfig.apply) +} + +case class `AWS::EMR::Cluster`(name: String, + AdditionalInfo: Option[JsValue], + Applications: Option[Seq[Application]], + BootstrapActions: Option[Seq[BootstrapAction]], + Configurations: Option[Seq[ClusterConfiguration]], + Instances: JobFlowInstancesConfig, + JobFlowRole: Token[String], + LogUri: Option[Token[String]], + Name: Token[String], + ReleaseLabel: Option[Token[String]], + ServiceRole: Token[String], + Tags: Option[Seq[AmazonTag]], + VisibileToAllUsers: Option[Token[Boolean]], + override val Condition: Option[ConditionRef] = None + ) extends Resource[`AWS::EMR::Cluster`] { + override def when(newCondition: Option[ConditionRef]): `AWS::EMR::Cluster` = copy( + Condition = newCondition + ) +} +object `AWS::EMR::Cluster` { + implicit val format : JsonFormat[`AWS::EMR::Cluster`] = jsonFormat14(`AWS::EMR::Cluster`.apply) +} diff --git a/src/test/scala/com/monsanto/arch/cloudformation/model/resource/EmrSpec.scala b/src/test/scala/com/monsanto/arch/cloudformation/model/resource/EmrSpec.scala new file mode 100644 index 00000000..dd0733ad --- /dev/null +++ b/src/test/scala/com/monsanto/arch/cloudformation/model/resource/EmrSpec.scala @@ -0,0 +1,54 @@ +package com.monsanto.arch.cloudformation.model.resource + +import org.scalatest.{FunSpec, Matchers} +import spray.json.{JsArray, JsObject, JsString, JsonWriter} + +class EmrSpec extends FunSpec with Matchers { + + describe("ClusterConfiguration") { + it("should write non recursive") { + val clusterConfiguration = ClusterConfiguration( + Classification = Some("hello"), + ConfigurationProperties = Some(Map("hello" -> "world")), + Configurations = None + ) + val json = implicitly[JsonWriter[ClusterConfiguration]].write(clusterConfiguration) + json should equal(JsObject(Map( + "Classification" -> JsString("hello"), + "ConfigurationProperties" -> JsObject( + "hello" -> JsString("world") + ) + ))) + } + + it("should write and read recursive") { + val clusterConfiguration = ClusterConfiguration( + Classification = Some("hello"), + ConfigurationProperties = Some(Map("hello" -> "world")), + Configurations = Some(Seq( + ClusterConfiguration( + Classification = Some("hello1"), + ConfigurationProperties = Some(Map("hello2" -> "world3")), + Configurations = None + ) + )) + ) + val json = implicitly[JsonWriter[ClusterConfiguration]].write(clusterConfiguration) + json should equal(JsObject(Map( + "Classification" -> JsString("hello"), + "ConfigurationProperties" -> JsObject( + "hello" -> JsString("world") + ), + "Configurations" -> JsArray( + JsObject(Map( + "Classification" -> JsString("hello1"), + "ConfigurationProperties" -> JsObject( + "hello2" -> JsString("world3") + ) + )) + ) + ))) + } + } + +}