diff --git a/README.md b/README.md index a40977bc..5de40e3f 100644 --- a/README.md +++ b/README.md @@ -85,6 +85,7 @@ describe("Template Lookup") { - AWS::AutoScaling::ScalingPolicy - AWS::CloudWatch::Alarm - AWS::DynamoDB::Table +- AWS::EC2::CustomerGateway - AWS::EC2::EIP - AWS::EC2::Instance - AWS::EC2::InternetGateway diff --git a/src/main/scala/com/monsanto/arch/cloudformation/model/Condition.scala b/src/main/scala/com/monsanto/arch/cloudformation/model/Condition.scala index 5bd12575..ec2e7905 100644 --- a/src/main/scala/com/monsanto/arch/cloudformation/model/Condition.scala +++ b/src/main/scala/com/monsanto/arch/cloudformation/model/Condition.scala @@ -2,6 +2,8 @@ package com.monsanto.arch.cloudformation.model import spray.json._ +import scala.collection.immutable.ListMap + /** * Created by bkrodg on 2/16/15. */ @@ -12,6 +14,6 @@ object Condition extends DefaultJsonProtocol { def write(obj: Condition) = obj.function.toJson } - def write(objs: Seq[Condition]) = JsObject( objs.map( o => o.name -> o.toJson ).toMap ) + def write(objs: Seq[Condition]) = JsObject( ListMap(objs.map( o => o.name -> o.toJson ): _*) ) } -} \ No newline at end of file +} diff --git a/src/main/scala/com/monsanto/arch/cloudformation/model/Mapping.scala b/src/main/scala/com/monsanto/arch/cloudformation/model/Mapping.scala index 121154a0..a1211f4d 100644 --- a/src/main/scala/com/monsanto/arch/cloudformation/model/Mapping.scala +++ b/src/main/scala/com/monsanto/arch/cloudformation/model/Mapping.scala @@ -3,6 +3,8 @@ package com.monsanto.arch.cloudformation.model import spray.json._ import DefaultJsonProtocol._ +import scala.collection.immutable.ListMap + /** * Created by Ryan Richt on 2/15/15 */ @@ -25,6 +27,6 @@ object Mapping extends DefaultJsonProtocol { } } - def write(objs: Seq[Mapping[_]]) = JsObject(objs.map(o => o.name -> format.write(o)).toMap) + def write(objs: Seq[Mapping[_]]) = JsObject(ListMap(objs.map(o => o.name -> format.write(o)): _*)) } -} \ No newline at end of file +} diff --git a/src/main/scala/com/monsanto/arch/cloudformation/model/Output.scala b/src/main/scala/com/monsanto/arch/cloudformation/model/Output.scala index 1441954d..37b549b3 100644 --- a/src/main/scala/com/monsanto/arch/cloudformation/model/Output.scala +++ b/src/main/scala/com/monsanto/arch/cloudformation/model/Output.scala @@ -3,6 +3,8 @@ package com.monsanto.arch.cloudformation.model import spray.json._ import DefaultJsonProtocol._ +import scala.collection.immutable.ListMap + /** * Created by Ryan Richt on 2/15/15 */ @@ -21,6 +23,6 @@ object Output extends DefaultJsonProtocol { ) } - def write(objs: Seq[Output[_]]) = JsObject(objs.map(o => o.name -> format.write(o)).toMap) + def write(objs: Seq[Output[_]]) = JsObject(ListMap(objs.map(o => o.name -> format.write(o)): _*)) } -} \ No newline at end of file +} diff --git a/src/main/scala/com/monsanto/arch/cloudformation/model/Parameter.scala b/src/main/scala/com/monsanto/arch/cloudformation/model/Parameter.scala index ee856b9d..e1f7e5d8 100644 --- a/src/main/scala/com/monsanto/arch/cloudformation/model/Parameter.scala +++ b/src/main/scala/com/monsanto/arch/cloudformation/model/Parameter.scala @@ -3,6 +3,7 @@ package com.monsanto.arch.cloudformation.model import com.monsanto.arch.cloudformation.model.resource._ import spray.json._ import DefaultJsonProtocol._ +import scala.collection.immutable.ListMap import scala.language.implicitConversions /** @@ -38,7 +39,7 @@ object Parameter extends DefaultJsonProtocol { } } - def write(objs: Seq[Parameter]) = JsObject( objs.map( o => o.name -> o.toJson ).toMap ) + def write(objs: Seq[Parameter]) = JsObject( ListMap(objs.map( o => o.name -> o.toJson ): _*) ) } } diff --git a/src/main/scala/com/monsanto/arch/cloudformation/model/Template.scala b/src/main/scala/com/monsanto/arch/cloudformation/model/Template.scala index 3f549044..1375e34e 100644 --- a/src/main/scala/com/monsanto/arch/cloudformation/model/Template.scala +++ b/src/main/scala/com/monsanto/arch/cloudformation/model/Template.scala @@ -5,6 +5,7 @@ import com.monsanto.arch.cloudformation.model.simple.SecurityGroupRoutable import spray.json._ import DefaultJsonProtocol._ +import scala.collection.immutable.ListMap import scala.language.implicitConversions import scala.reflect.ClassTag @@ -114,7 +115,7 @@ object Template extends DefaultJsonProtocol { if(p.Mappings.nonEmpty) fields ++= productElement2Field[Option[Seq[Mapping[_]]]]("Mappings", p, 3) if(p.Resources.nonEmpty) fields ++= productElement2Field[Option[Seq[Resource[_]]]]("Resources", p, 4) if(p.Outputs.nonEmpty) fields ++= productElement2Field[Option[Seq[Output[_]]]]("Outputs", p, 6) - JsObject(fields: _*) + JsObject(ListMap(fields: _*)) } } diff --git a/src/main/scala/com/monsanto/arch/cloudformation/model/resource/EC2.scala b/src/main/scala/com/monsanto/arch/cloudformation/model/resource/EC2.scala index 12a427da..b5d017b9 100644 --- a/src/main/scala/com/monsanto/arch/cloudformation/model/resource/EC2.scala +++ b/src/main/scala/com/monsanto/arch/cloudformation/model/resource/EC2.scala @@ -19,10 +19,25 @@ case class `AWS::EC2::EIP`( ) extends Resource[`AWS::EC2::EIP`]{ def when(newCondition: Option[ConditionRef] = Condition) = copy(Condition = newCondition) } + object `AWS::EC2::EIP` extends DefaultJsonProtocol { implicit val format: JsonFormat[`AWS::EC2::EIP`] = jsonFormat5(`AWS::EC2::EIP`.apply) } +case class `AWS::EC2::EIPAssociation`( + name: String, + AllocationId: Option[Token[String]], + InstanceId: Token[ResourceRef[`AWS::EC2::Instance`]], + override val Condition: Option[ConditionRef] = None, + override val DependsOn: Option[Seq[String]] = None +) extends Resource[`AWS::EC2::EIPAssociation`]{ + def when(newCondition: Option[ConditionRef] = Condition) = copy(Condition = newCondition) +} + +object `AWS::EC2::EIPAssociation` extends DefaultJsonProtocol { + implicit val format: JsonFormat[`AWS::EC2::EIPAssociation`] = jsonFormat5(`AWS::EC2::EIPAssociation`.apply) +} + case class AMIId(id: String) object AMIId extends DefaultJsonProtocol { implicit val format: JsonFormat[AMIId] = new JsonFormat[AMIId] { @@ -78,6 +93,20 @@ object `AWS::EC2::KeyPair::KeyName` extends DefaultJsonProtocol { implicit val format: JsonFormat[`AWS::EC2::KeyPair::KeyName`] = jsonFormat2(`AWS::EC2::KeyPair::KeyName`.apply) } +case class `AWS::EC2::CustomerGateway`( + name: String, + BgpAsn: Int, + IpAddress: IPAddress, + Tags: Seq[AmazonTag], + Type: String, + override val Condition: Option[ConditionRef] = None) extends Resource[`AWS::EC2::CustomerGateway`]{ + + def when(newCondition: Option[ConditionRef] = Condition) = copy(Condition = newCondition) +} +object `AWS::EC2::CustomerGateway` extends DefaultJsonProtocol { + implicit val format: JsonFormat[`AWS::EC2::CustomerGateway`] = jsonFormat6(`AWS::EC2::CustomerGateway`.apply) +} + @implicitNotFound("A Route can only have exactly ONE of GatewayId, InstanceId, NetworkInterfaceId or VpcPeeringConnectionId set") class ValidRouteCombo[G, I] private () object ValidRouteCombo{ @@ -217,6 +246,21 @@ object CidrBlock extends DefaultJsonProtocol { } } +case class IPAddress(a: IPAddressSegment, b: IPAddressSegment, c: IPAddressSegment, d: IPAddressSegment) { + def toJsString: JsString = JsString( Seq(a, b, c, d).map(_.value.toString).mkString(".")) +} +object IPAddress extends DefaultJsonProtocol { + implicit val format: JsonFormat[IPAddress] = new JsonFormat[IPAddress] { + def write(obj: IPAddress) = obj.toJsString + + def read(json: JsValue) = { + val parts = json.convertTo[String].split(Array('.')).map(_.toInt) + + IPAddress(parts(0), parts(1), parts(2), parts(3)) + } + } +} + sealed trait EgressSpec object EgressSpec extends DefaultJsonProtocol { implicit val format: JsonFormat[EgressSpec] = new JsonFormat[EgressSpec] { diff --git a/src/main/scala/com/monsanto/arch/cloudformation/model/resource/Resource.scala b/src/main/scala/com/monsanto/arch/cloudformation/model/resource/Resource.scala index 21f481ae..39177024 100644 --- a/src/main/scala/com/monsanto/arch/cloudformation/model/resource/Resource.scala +++ b/src/main/scala/com/monsanto/arch/cloudformation/model/resource/Resource.scala @@ -3,17 +3,19 @@ package com.monsanto.arch.cloudformation.model.resource import com.monsanto.arch.cloudformation.model._ import spray.json._ +import scala.collection.immutable.ListMap import scala.language.implicitConversions import scala.reflect.ClassTag import scala.reflect.NameTransformer + /** * Created by Ryan Richt on 2/15/15 */ // serializes to Type and Properties abstract class Resource[R <: Resource[R] : ClassTag : JsonFormat]{ self: Resource[R] => - val Type = NameTransformer.decode(implicitly[ClassTag[R]].runtimeClass.getSimpleName) + val ResourceType = NameTransformer.decode(implicitly[ClassTag[R]].runtimeClass.getSimpleName) val name: String val Condition: Option[ConditionRef] = None @@ -35,7 +37,7 @@ object Resource extends DefaultJsonProtocol { val raw = bar._format.asInstanceOf[JsonFormat[obj.RR]].write(bar).asJsObject val outputFields = Map( - "Type" -> JsString(obj.Type), + "Type" -> JsString(obj.ResourceType), "Metadata" -> raw.fields.getOrElse("Metadata", JsNull), "Properties" -> JsObject(raw.fields - "name" - "Metadata" - "UpdatePolicy" - "Condition" - "DependsOn" - "DeletionPolicy"), "UpdatePolicy" -> raw.fields.getOrElse("UpdatePolicy", JsNull), @@ -48,7 +50,7 @@ object Resource extends DefaultJsonProtocol { } } - def write(objs: Seq[Resource[_]]) = JsObject( objs.map( o => o.name -> format.write(o) ).toMap ) + def write(objs: Seq[Resource[_]]) = JsObject( ListMap(objs.map( o => o.name -> format.write(o) ): _*) ) } } diff --git a/src/test/scala/com/monsanto/arch/cloudformation/model/resource/CGW_UT.scala b/src/test/scala/com/monsanto/arch/cloudformation/model/resource/CGW_UT.scala new file mode 100644 index 00000000..4e0f38be --- /dev/null +++ b/src/test/scala/com/monsanto/arch/cloudformation/model/resource/CGW_UT.scala @@ -0,0 +1,34 @@ +package com.monsanto.arch.cloudformation.model.resource + +import com.monsanto.arch.cloudformation.model._ +import org.scalatest.{FunSpec, Matchers} +import spray.json._ + +class CGW_UT extends FunSpec with Matchers { + describe("AWS::EC2::CustomerGateway") { + val bpgAsn = 1234 + val ipAddr = IPAddress(8, 8, 8, 8) + val cgwType = "ipsec.1" + val cgw = `AWS::EC2::CustomerGateway`( + name = "cgw", + BgpAsn = 1234, + IpAddress = ipAddr, + Tags = Seq(), + Type = "ipsec.1" + ) + it("should create a valid new Customer Gateway") { + val expected = JsObject( + "cgw" -> JsObject( + "Type" -> JsString("AWS::EC2::CustomerGateway"), + "Properties" -> JsObject( + "BgpAsn" -> JsNumber(bpgAsn), + "IpAddress" -> ipAddr.toJsString, + "Tags" -> JsArray(), + "Type" -> JsString(cgwType) + ) + ) + ) + Seq[Resource[_]](cgw).toJson should be(expected) + } + } +} diff --git a/src/test/scala/com/monsanto/arch/cloudformation/model/resource/EC2_UT.scala b/src/test/scala/com/monsanto/arch/cloudformation/model/resource/EC2_UT.scala new file mode 100644 index 00000000..ff0173a2 --- /dev/null +++ b/src/test/scala/com/monsanto/arch/cloudformation/model/resource/EC2_UT.scala @@ -0,0 +1,34 @@ +package com.monsanto.arch.cloudformation.model.resource + +import org.scalatest.{Matchers, FunSpec} +import spray.json._ + +class EC2_UT extends FunSpec with Matchers { + + describe("CidrBlock"){ + + val cidr = CidrBlock(192,168,1,2,32) + + it("should write valid CidrBlock"){ + cidr.toJson shouldEqual JsString("192.168.1.2/32") + } + + it("should read valid CidrBlock") { + JsString("192.168.1.2/32").convertTo[CidrBlock] shouldEqual cidr + } + + } + + describe("IPAddress"){ + val ipAddress = IPAddress(192,168,1,2) + + it("should write valid IPAddress"){ + ipAddress.toJson shouldEqual JsString("192.168.1.2") + } + + it("should read valid IPAddress"){ + JsString("192.168.1.2").convertTo[IPAddress] shouldEqual ipAddress + } + + } +}