Skip to content

Commit

Permalink
convert remaining AWS interactions to use smithy4s
Browse files Browse the repository at this point in the history
  • Loading branch information
bpholt committed Nov 22, 2023
1 parent 1bcfb83 commit 79a2532
Show file tree
Hide file tree
Showing 44 changed files with 622 additions and 499 deletions.
12 changes: 12 additions & 0 deletions aws-testkit/src/main/scala/com/dwolla/aws/ArbitraryInstances.scala
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.dwolla.aws

import org.scalacheck.*
import smithy4s.Timestamp

given Arbitrary[AccountId] =
Arbitrary(Gen.stringOfN(12, Gen.numChar).map(AccountId(_)))
Expand All @@ -11,3 +12,14 @@ val genTag: Gen[Tag] =
value <- Gen.asciiPrintableStr.map(TagValue(_))
} yield Tag(key, value)
given Arbitrary[Tag] = Arbitrary(genTag)

/**
* Timestamp doesn't allow the full range from Instant, so we can't just
* use an Arbitrary[Instant] and map it to Timestamp
*/
val genTimestamp: Gen[Timestamp] =
for {
epoch <- Gen.chooseNum(-62167219200L, 253402300799L)
nanos <- Gen.chooseNum(0, 999999999)
} yield Timestamp(epoch, nanos)
given Arbitrary[Timestamp] = Arbitrary(genTimestamp)
Original file line number Diff line number Diff line change
@@ -1,22 +1,29 @@
package com.dwolla.aws.autoscaling

import cats.syntax.all.*
import java.time.*
import com.dwolla.aws.AccountId
import com.dwolla.aws.ec2.Ec2InstanceId
import com.amazonaws.autoscaling.{LifecycleState as _, *}
import com.amazonaws.ec2.InstanceId
import com.dwolla.aws.autoscaling.LifecycleState.*
import com.dwolla.aws.ec2.given
import com.dwolla.aws.{AccountId, given}
import com.fortysevendeg.scalacheck.datetime.jdk8.ArbitraryJdk8.*
import org.scalacheck.Arbitrary.{arbUuid, arbitrary}
import org.scalacheck.*
import LifecycleState.*
import software.amazon.awssdk.services.autoscaling.model.{DescribeAutoScalingInstancesResponse, AutoScalingInstanceDetails}
import com.dwolla.aws.given
import com.dwolla.aws.ec2.given
import org.scalacheck.Arbitrary.{arbChar, arbUuid, arbitrary}

import java.time.*

given Arbitrary[ResourceName] = Arbitrary(Gen.asciiStr.map(ResourceName.apply))
given Arbitrary[AsciiStringMaxLen255] = Arbitrary {
Gen.chooseNum(0, 255)
.flatMap(Gen.stringOfN(_, Gen.asciiChar))
.map(AsciiStringMaxLen255.apply)
}

// TODO genAutoScalingGroupName could be more realistic
val genAutoScalingGroupName: Gen[AutoScalingGroupName] = Gen.asciiStr.map(AutoScalingGroupName(_))
val genAutoScalingGroupName: Gen[AutoScalingGroupName] = arbitrary[ResourceName].map(AutoScalingGroupName(_))
given Arbitrary[AutoScalingGroupName] = Arbitrary(genAutoScalingGroupName)

val genLifecycleHookName: Gen[LifecycleHookName] = Gen.asciiStr.map(LifecycleHookName(_))
val genLifecycleHookName: Gen[LifecycleHookName] = arbitrary[AsciiStringMaxLen255].map(LifecycleHookName(_))
given Arbitrary[LifecycleHookName] = Arbitrary(genLifecycleHookName)

val genLifecycleTransition: Gen[LifecycleTransition] = Gen.const("autoscaling:EC2_INSTANCE_TERMINATING").map(LifecycleTransition(_))
Expand All @@ -31,9 +38,9 @@ val genLifecycleHookNotification: Gen[LifecycleHookNotification] =
accountId <- arbitrary[AccountId]
groupName <- arbitrary[AutoScalingGroupName]
hookName <- arbitrary[LifecycleHookName]
ec2InstanceId <- arbitrary[Ec2InstanceId]
InstanceId <- arbitrary[InstanceId]
lifecycleTransition <- arbitrary[LifecycleTransition]
} yield LifecycleHookNotification(service, time, requestId, lifecycleActionToken, accountId, groupName, hookName, ec2InstanceId, lifecycleTransition, None)
} yield LifecycleHookNotification(service, time, requestId, lifecycleActionToken, accountId, groupName, hookName, InstanceId, lifecycleTransition, None)
given Arbitrary[LifecycleHookNotification] = Arbitrary(genLifecycleHookNotification)

val genLifecycleState: Gen[LifecycleState] =
Expand All @@ -46,35 +53,57 @@ val genLifecycleState: Gen[LifecycleState] =
)
given Arbitrary[LifecycleState] = Arbitrary(genLifecycleState)

def genAutoScalingInstanceDetails(maybeId: Option[Ec2InstanceId] = None,
given Arbitrary[XmlStringMaxLen255] = Arbitrary {
Gen.chooseNum(0, 255)
.flatMap(Gen.stringOfN(_, arbitrary[Char]))
.map(XmlStringMaxLen255.apply)
}
given Arbitrary[XmlStringMaxLen32] = Arbitrary {
Gen.chooseNum(0, 32)
.flatMap(Gen.stringOfN(_, arbitrary[Char]))
.map(XmlStringMaxLen32.apply)
}
given Arbitrary[InstanceProtected] = Arbitrary(arbitrary[Boolean].map(InstanceProtected.apply))

def genAutoScalingInstanceDetails(maybeId: Option[InstanceId] = None,
maybeAutoScalingGroupName: Option[AutoScalingGroupName] = None,
maybeLifecycleState: Option[LifecycleState] = None,
): Gen[AutoScalingInstanceDetails] =
for {
id <- maybeId.orGen
asgName <- maybeAutoScalingGroupName.orGen
lifecycleState <- maybeLifecycleState.orGen
availabilityZone <- arbitrary[XmlStringMaxLen255]
healthStatus <- arbitrary[XmlStringMaxLen32]
protectedFromScaleIn <- arbitrary[InstanceProtected]
instanceType <- Gen.option(arbitrary[XmlStringMaxLen255])
launchConfigurationName = None
launchTemplate = None
weightedCapacity = None
} yield
AutoScalingInstanceDetails
.builder()
.instanceId(id.value)
.autoScalingGroupName(asgName.value)
.lifecycleState(lifecycleState.awsName)
.build()
AutoScalingInstanceDetails(
instanceId = XmlStringMaxLen19(id.value),
autoScalingGroupName = XmlStringMaxLen255(asgName.value.value),
lifecycleState = XmlStringMaxLen32(lifecycleState.awsName),
availabilityZone = availabilityZone,
healthStatus = healthStatus,
protectedFromScaleIn = protectedFromScaleIn,
instanceType = instanceType,
launchConfigurationName = launchConfigurationName,
launchTemplate = launchTemplate,
weightedCapacity = weightedCapacity,
)

val genLifecycleHookNotificationWithRelatedDescribeAutoScalingInstancesResponse: Gen[(LifecycleHookNotification, DescribeAutoScalingInstancesResponse)] =
val genLifecycleHookNotificationWithRelatedAutoScalingInstancesType: Gen[(LifecycleHookNotification, AutoScalingInstancesType)] =
for {
notification <- genLifecycleHookNotification
groupName <- arbitrary[AutoScalingGroupName]
autoScalingDetailsFromHook <- genAutoScalingInstanceDetails(notification.EC2InstanceId.some, groupName.some)
otherAutoScalingDetails <- Gen.listOf(genAutoScalingInstanceDetails(maybeAutoScalingGroupName = groupName.some))
} yield {
notification -> DescribeAutoScalingInstancesResponse.builder()
.autoScalingInstances(otherAutoScalingDetails.appended(autoScalingDetailsFromHook) *)
.build()
}
given Arbitrary[(LifecycleHookNotification, DescribeAutoScalingInstancesResponse)] =
Arbitrary(genLifecycleHookNotificationWithRelatedDescribeAutoScalingInstancesResponse)
details = otherAutoScalingDetails.appended(autoScalingDetailsFromHook)
} yield notification -> AutoScalingInstancesType(details.some)
given Arbitrary[(LifecycleHookNotification, AutoScalingInstancesType)] =
Arbitrary(genLifecycleHookNotificationWithRelatedAutoScalingInstancesType)

extension [A](maybeA: Option[A]) {
def orGen(using Arbitrary[A]): Gen[A] =
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
package com.dwolla.aws.autoscaling

import cats.effect.*
import com.dwolla.aws.sns.SnsTopicArn
import com.amazonaws.sns.TopicARN

abstract class TestAutoScalingAlg extends AutoScalingAlg[IO] {
override def pauseAndRecurse(topic: SnsTopicArn, lifecycleHookNotification: LifecycleHookNotification, onlyIfInState: LifecycleState): IO[Unit] = IO.raiseError(new NotImplementedError)
override def pauseAndRecurse(topic: TopicARN, lifecycleHookNotification: LifecycleHookNotification, onlyIfInState: LifecycleState): IO[Unit] = IO.raiseError(new NotImplementedError)
override def continueAutoScaling(l: LifecycleHookNotification): IO[Unit] = IO.raiseError(new NotImplementedError)
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.dwolla.aws.cloudformation

import com.amazonaws.cloudformation.*
import org.scalacheck.{Arbitrary, Gen}

val genStackArn: Gen[StackArn] =
Expand All @@ -16,3 +17,9 @@ given Arbitrary[LogicalResourceId] = Arbitrary(genLogicalResourceId)

val genPhysicalResourceId: Gen[PhysicalResourceId] = Gen.asciiPrintableStr.map(PhysicalResourceId(_))
given Arbitrary[PhysicalResourceId] = Arbitrary(genPhysicalResourceId)

val genResourceType: Gen[ResourceType] = Gen.asciiPrintableStr.map(ResourceType.apply)
given Arbitrary[ResourceType] = Arbitrary(genResourceType)

val genResourceStatus: Gen[ResourceStatus] = Gen.oneOf(ResourceStatus.values)
given Arbitrary[ResourceStatus] = Arbitrary(genResourceStatus)
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.dwolla.aws.cloudformation

import cats.effect.*
import com.amazonaws.cloudformation.*

abstract class TestCloudFormationAlg extends CloudFormationAlg[IO] {
override def physicalResourceIdFor(stack: StackArn, logicalResourceId: LogicalResourceId): IO[Option[PhysicalResourceId]] = IO.raiseError(new NotImplementedError)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,26 +1,21 @@
package com.dwolla.aws.ec2

import com.amazonaws.ec2.{Instance, InstanceId, Tag as AwsTag}
import org.scalacheck.*
import org.scalacheck.Arbitrary.arbitrary
import software.amazon.awssdk.services.ec2.model.{Instance, Tag as AwsTag}

given Arbitrary[Ec2InstanceId] = Arbitrary(Gen.listOfN(17, Gen.hexChar).map(_.mkString("i-", "", "")).map(Ec2InstanceId(_)))
given Arbitrary[InstanceId] =
Arbitrary(Gen.listOfN(17, Gen.hexChar).map(_.mkString("i-", "", "")).map(InstanceId(_)))

val genAwsTag: Gen[AwsTag] =
for {
key <- Gen.identifier
value <- arbitrary[String]
} yield AwsTag.builder().key(key).value(value).build()
key <- Gen.option(Gen.identifier)
value <- Gen.option(arbitrary[String])
} yield AwsTag(key, value)

val genInstance: Gen[Instance] =
for {
id <- arbitrary[Ec2InstanceId]
tags <- Gen.nonEmptyListOf(genAwsTag)
} yield {
Instance
.builder()
.instanceId(id.value)
.tags(tags *)
.build()
}
id <- Gen.option(arbitrary[InstanceId])
tags <- Gen.option(Gen.nonEmptyListOf(genAwsTag))
} yield Instance(instanceId = id.map(_.value), tags = tags)
given Arbitrary[Instance] = Arbitrary(genInstance)
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
package com.dwolla.aws.ec2

import cats.effect.*
import com.amazonaws.ec2.InstanceId
import com.dwolla.aws.Tag

abstract class TestEc2Alg extends Ec2Alg[IO] {
override def getTagsForInstance(id: Ec2InstanceId): IO[List[Tag]] = IO.raiseError(new NotImplementedError)
override def getTagsForInstance(id: InstanceId): IO[List[Tag]] = IO.raiseError(new NotImplementedError)
}
Original file line number Diff line number Diff line change
@@ -1,21 +1,23 @@
package com.dwolla.aws.ecs

import cats.syntax.all.*
import com.dwolla.aws.{AccountId, given}
import com.dwolla.aws.ec2.{Ec2InstanceId, given}
import com.amazonaws.ec2.InstanceId
import com.dwolla.RandomChunks
import com.dwolla.aws.ec2.given
import com.dwolla.aws.ecs.TaskStatus.stoppedTaskStatuses
import com.dwolla.aws.{AccountId, given}
import fs2.{Chunk, Pure, Stream}
import monix.newtypes.NewtypeWrapped
import org.scalacheck.*
import org.scalacheck.Arbitrary.arbitrary
import smithy4s.aws.AwsRegion
import smithy4s.aws.kernel.AwsRegion.{AF_SOUTH_1, AP_EAST_1, AP_NORTHEAST_1, AP_NORTHEAST_2, AP_NORTHEAST_3, AP_SOUTHEAST_1, AP_SOUTHEAST_2, AP_SOUTH_1, CA_CENTRAL_1, CN_NORTHWEST_1, CN_NORTH_1, EU_CENTRAL_1, EU_NORTH_1, EU_SOUTH_1, EU_WEST_1, EU_WEST_2, EU_WEST_3, GovCloud, ME_SOUTH_1, SA_EAST_1, US_EAST_1, US_EAST_2, US_GOV_EAST_1, US_ISOB_EAST_1, US_ISO_EAST_1, US_ISO_WEST_1, US_WEST_1, US_WEST_2}

import java.util.UUID
import scala.jdk.CollectionConverters.*

case class TaskArnAndStatus(arn: TaskArn, status: TaskStatus)
case class ContainerInstanceWithTaskPages(containerInstanceId: ContainerInstanceId,
ec2InstanceId: Ec2InstanceId,
ec2InstanceId: InstanceId,
tasks: List[Chunk[TaskArnAndStatus]],
status: ContainerInstanceStatus,
) {
Expand Down Expand Up @@ -43,16 +45,45 @@ object ClusterWithInstances extends NewtypeWrapped[(Cluster, List[Chunk[Containe
type TasksForContainerInstance = TasksForContainerInstance.Type
object TasksForContainerInstance extends NewtypeWrapped[List[Chunk[TaskArnAndStatus]]]

def genRegion: Gen[Region] = Gen.oneOf(software.amazon.awssdk.regions.Region.regions().asScala).map(x => Region(x.id()))
given Arbitrary[Region] = Arbitrary(genRegion)
def genRegion: Gen[AwsRegion] = Gen.oneOf(
AF_SOUTH_1,
AP_EAST_1,
AP_NORTHEAST_1,
AP_NORTHEAST_2,
AP_NORTHEAST_3,
AP_SOUTH_1,
AP_SOUTHEAST_1,
AP_SOUTHEAST_2,
CA_CENTRAL_1,
CN_NORTH_1,
CN_NORTHWEST_1,
EU_CENTRAL_1,
EU_NORTH_1,
EU_SOUTH_1,
EU_WEST_1,
EU_WEST_2,
EU_WEST_3,
GovCloud,
ME_SOUTH_1,
SA_EAST_1,
US_EAST_1,
US_EAST_2,
US_GOV_EAST_1,
US_ISO_EAST_1,
US_ISO_WEST_1,
US_ISOB_EAST_1,
US_WEST_1,
US_WEST_2,
)
given Arbitrary[AwsRegion] = Arbitrary(genRegion)

def genContainerInstanceStatus: Gen[ContainerInstanceStatus] = Gen.oneOf(ContainerInstanceStatus.Active, ContainerInstanceStatus.Draining, ContainerInstanceStatus.Inactive)
given Arbitrary[ContainerInstanceStatus] = Arbitrary(genContainerInstanceStatus)

def genContainerInstanceWithTaskPages: Gen[ContainerInstanceWithTaskPages] =
for {
cId <- arbitrary[ContainerInstanceId]
ec2Id <- arbitrary[Ec2InstanceId]
ec2Id <- arbitrary[InstanceId]
taskCount <- arbitrary[TasksForContainerInstance]
status <- arbitrary[ContainerInstanceStatus]
} yield ContainerInstanceWithTaskPages(cId, ec2Id, taskCount.value, status)
Expand Down Expand Up @@ -115,7 +146,7 @@ given Arbitrary[ContainerInstanceId] = Arbitrary(genContainerInstanceId)

def genCluster: Gen[Cluster] =
for {
region <- arbitrary[Region]
region <- arbitrary[AwsRegion]
accountId <- arbitrary[AccountId]
clusterName <- arbitrary[ClusterName]
} yield Cluster(region, accountId, clusterName)
Expand Down
Loading

0 comments on commit 79a2532

Please sign in to comment.