Skip to content

Commit a110ba4

Browse files
Support for Lambda Images (#2241)
Co-authored-by: Hunter Werlla <werlla@amazon.com> Co-authored-by: Austin Brooks <abrooksv@users.noreply.github.com>
1 parent 296907f commit a110ba4

File tree

196 files changed

+9264
-1688
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

196 files changed

+9264
-1688
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
{
2+
"type" : "feature",
3+
"description" : "Container Image Support in Lambda"
4+
}

.gitignore

+2
Original file line numberDiff line numberDiff line change
@@ -10,3 +10,5 @@ Build/
1010
venv/
1111
guitest.log
1212
stale_outputs_checked
13+
# sam build directory
14+
.aws-sam/

buildSrc/build.gradle.kts

+5
Original file line numberDiff line numberDiff line change
@@ -59,5 +59,10 @@ gradlePlugin {
5959
id = "toolkit-change-log"
6060
implementationClass = "software.aws.toolkits.gradle.changelog.ChangeLogPlugin"
6161
}
62+
63+
create("generateSdk") {
64+
id = "toolkit-generate-sdk"
65+
implementationClass = "software.aws.toolkits.gradle.sdk.GenerateSdkPlugin"
66+
}
6267
}
6368
}

buildSrc/src/software/aws/toolkits/gradle/sdk/GenerateSdk.kt

+69-13
Original file line numberDiff line numberDiff line change
@@ -4,54 +4,110 @@
44
package software.aws.toolkits.gradle.sdk
55

66
import org.gradle.api.DefaultTask
7+
import org.gradle.api.Plugin
8+
import org.gradle.api.Project
9+
import org.gradle.api.file.DirectoryProperty
10+
import org.gradle.api.plugins.JavaPlugin
11+
import org.gradle.api.plugins.JavaPluginConvention
712
import org.gradle.api.tasks.InputDirectory
13+
import org.gradle.api.tasks.Internal
814
import org.gradle.api.tasks.OutputDirectory
15+
import org.gradle.api.tasks.SourceSet
916
import org.gradle.api.tasks.TaskAction
17+
import org.gradle.plugins.ide.idea.model.IdeaModel
1018
import software.amazon.awssdk.codegen.C2jModels
1119
import software.amazon.awssdk.codegen.CodeGenerator
1220
import software.amazon.awssdk.codegen.model.config.customization.CustomizationConfig
1321
import software.amazon.awssdk.codegen.model.service.Paginators
1422
import software.amazon.awssdk.codegen.model.service.ServiceModel
23+
import software.amazon.awssdk.codegen.model.service.Waiters
1524
import software.amazon.awssdk.codegen.utils.ModelLoaderUtils
16-
import java.io.File
25+
26+
open class GenerateSdkExtension(project: Project) {
27+
val c2jFolder: DirectoryProperty = project.objects.directoryProperty()
28+
29+
val outputDir: DirectoryProperty = project.objects.directoryProperty()
30+
31+
fun srcDir() = outputDir.dir("src")
32+
fun tsDir() = outputDir.dir("tst")
33+
}
34+
35+
@Suppress("unused") // Plugin is created by buildSrc/build.gradle
36+
class GenerateSdkPlugin : Plugin<Project> {
37+
override fun apply(project: Project) {
38+
project.pluginManager.apply(JavaPlugin::class.java)
39+
40+
val extension = project.extensions.create("sdkGenerator", GenerateSdkExtension::class.java, project)
41+
extension.c2jFolder.convention(project.layout.projectDirectory.dir("codegen-resources"))
42+
extension.outputDir.convention(project.layout.buildDirectory.dir("generated-sources"))
43+
44+
val generateSdkTask = project.tasks.create("generateSdk", GenerateSdk::class.java)
45+
46+
val javaConvention = project.convention.getPlugin(JavaPluginConvention::class.java)
47+
val mainSourceSet = javaConvention.sourceSets.getByName(SourceSet.MAIN_SOURCE_SET_NAME)
48+
mainSourceSet.java.srcDir(generateSdkTask.srcDir)
49+
project.tasks.getByName(mainSourceSet.compileJavaTaskName).dependsOn(generateSdkTask)
50+
51+
val ideaModel = project.extensions.getByType(IdeaModel::class.java)
52+
ideaModel.module.generatedSourceDirs.add(generateSdkTask.srcDir.get().asFile)
53+
}
54+
}
1755

1856
open class GenerateSdk : DefaultTask() {
1957
@InputDirectory
20-
lateinit var c2jFolder: File
58+
val c2jFolder: DirectoryProperty = project.objects.directoryProperty().convention(project.extensions.getByType(GenerateSdkExtension::class.java).c2jFolder)
2159

2260
@OutputDirectory
23-
lateinit var outputDir: File
61+
val srcDir: DirectoryProperty = project.objects.directoryProperty().convention(project.extensions.getByType(GenerateSdkExtension::class.java).srcDir())
62+
63+
@Internal
64+
val testDir: DirectoryProperty = project.objects.directoryProperty().convention(project.extensions.getByType(GenerateSdkExtension::class.java).tsDir())
2465

2566
@TaskAction
2667
fun generate() {
27-
outputDir.deleteRecursively()
68+
val srcDir = srcDir.asFile.get()
69+
val testDir = testDir.asFile.get()
70+
srcDir.deleteRecursively()
71+
testDir.deleteRecursively()
2872

29-
logger.info("Generating SDK from $c2jFolder")
73+
logger.info("Generating SDK from ${c2jFolder.get().asFile}")
3074
val models = C2jModels.builder()
3175
.serviceModel(loadServiceModel())
3276
.paginatorsModel(loadPaginatorsModel())
3377
.customizationConfig(loadCustomizationConfig())
78+
.waitersModel(loadWaitersModel())
3479
.build()
3580

3681
CodeGenerator.builder()
3782
.models(models)
38-
.sourcesDirectory(outputDir.absolutePath)
83+
.sourcesDirectory(srcDir.absolutePath)
84+
.testsDirectory(testDir.absolutePath)
3985
.build()
4086
.execute()
4187
}
4288

43-
private fun loadServiceModel(): ServiceModel? =
44-
ModelLoaderUtils.loadModel(ServiceModel::class.java, File(c2jFolder, "service-2.json"))
89+
private fun loadServiceModel(): ServiceModel? = ModelLoaderUtils.loadModel(ServiceModel::class.java, c2jFolder.file("service-2.json").get().asFile)
4590

4691
private fun loadPaginatorsModel(): Paginators? {
47-
val paginatorsFile = File(c2jFolder, "paginators-1.json")
48-
if (paginatorsFile.exists())
49-
return ModelLoaderUtils.loadModel(Paginators::class.java, paginatorsFile)
50-
return null
92+
val paginatorsFile = c2jFolder.file("paginators-1.json").orNull?.asFile
93+
return if (paginatorsFile?.exists() == true) {
94+
ModelLoaderUtils.loadModel(Paginators::class.java, paginatorsFile)
95+
} else {
96+
null
97+
}
98+
}
99+
100+
private fun loadWaitersModel(): Waiters? {
101+
val waitersFile = c2jFolder.file("waiters-2.json").orNull?.asFile
102+
return if (waitersFile?.exists() == true) {
103+
ModelLoaderUtils.loadModel(Waiters::class.java, waitersFile)
104+
} else {
105+
null
106+
}
51107
}
52108

53109
private fun loadCustomizationConfig(): CustomizationConfig = ModelLoaderUtils.loadOptionalModel(
54110
CustomizationConfig::class.java,
55-
File(c2jFolder, "customization.config")
111+
c2jFolder.file("customization.config").get().asFile
56112
).orElse(CustomizationConfig.create())
57113
}

core/tst/software/aws/toolkits/core/rules/S3TemporaryBucketRule.kt

+8-7
Original file line numberDiff line numberDiff line change
@@ -8,22 +8,23 @@ import software.amazon.awssdk.services.s3.S3Client
88
import software.amazon.awssdk.services.s3.model.NoSuchBucketException
99
import software.aws.toolkits.core.s3.deleteBucketAndContents
1010
import software.aws.toolkits.core.utils.RuleUtils
11-
import software.aws.toolkits.core.utils.Waiters.waitUntilBlocking
1211

13-
class S3TemporaryBucketRule(private val s3Client: S3Client) : ExternalResource() {
12+
class S3TemporaryBucketRule(private val s3ClientSupplier: () -> S3Client) : ExternalResource() {
13+
constructor(s3Client: S3Client) : this({ s3Client })
14+
1415
private val buckets = mutableListOf<String>()
1516

1617
/**
1718
* Creates a temporary bucket with the optional prefix (or calling class if prefix is omitted)
1819
*/
1920
fun createBucket(prefix: String = RuleUtils.prefixFromCallingClass()): String {
2021
val bucketName: String = RuleUtils.randomName(prefix).toLowerCase()
21-
s3Client.createBucket { it.bucket(bucketName) }
22+
val client = s3ClientSupplier()
23+
24+
client.createBucket { it.bucket(bucketName) }
2225

2326
// Wait for bucket to be ready
24-
waitUntilBlocking(exceptionsToIgnore = setOf(NoSuchBucketException::class)) {
25-
s3Client.headBucket { it.bucket(bucketName) }
26-
}
27+
client.waiter().waitUntilBucketExists { it.bucket(bucketName) }
2728

2829
buckets.add(bucketName)
2930

@@ -38,7 +39,7 @@ class S3TemporaryBucketRule(private val s3Client: S3Client) : ExternalResource()
3839
}
3940

4041
private fun deleteBucketAndContents(bucket: String): Exception? = try {
41-
s3Client.deleteBucketAndContents(bucket)
42+
s3ClientSupplier().deleteBucketAndContents(bucket)
4243
null
4344
} catch (e: Exception) {
4445
when (e) {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
// Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved.
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
package software.aws.toolkits.core.utils
5+
6+
import software.amazon.awssdk.auth.credentials.AwsCredentialsProvider
7+
import software.amazon.awssdk.auth.credentials.DefaultCredentialsProvider
8+
import software.amazon.awssdk.services.sts.StsClient
9+
import software.amazon.awssdk.services.sts.auth.StsAssumeRoleCredentialsProvider
10+
import software.amazon.awssdk.services.sts.model.AssumeRoleRequest
11+
import java.util.UUID
12+
13+
/**
14+
* Creates an [AwsCredentialsProvider] meant to be used in integration tests
15+
*
16+
* If the environment variable `ASSUME_ROLE_ARN` is set, it will be assumed using the default credential chain as the source credentials.
17+
* If it is not set, we will just use the default credential provider chain
18+
*/
19+
fun createIntegrationTestCredentialProvider(): AwsCredentialsProvider {
20+
val defaultCredentials = DefaultCredentialsProvider.create()
21+
22+
return System.getenv("ASSUME_ROLE_ARN")?.takeIf { it.isNotEmpty() }?.let { role ->
23+
val sessionName = UUID.randomUUID().toString()
24+
25+
StsAssumeRoleCredentialsProvider.builder()
26+
.stsClient(StsClient.builder().credentialsProvider(defaultCredentials).build())
27+
.refreshRequest(AssumeRoleRequest.builder().roleArn(role).roleSessionName(sessionName).build())
28+
.build()
29+
} ?: defaultCredentials
30+
}

jetbrains-core/build.gradle.kts

+4-2
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ configurations {
4848
}
4949

5050
val generateTelemetry = tasks.register<GenerateTelemetry>("generateTelemetry") {
51-
inputFiles = listOf()
51+
inputFiles = listOf(file("${project.projectDir}/resources/telemetryOverride.json"))
5252
outputDirectory = file("${project.buildDir}/generated-src")
5353
}
5454
compileKotlin.dependsOn(generateTelemetry)
@@ -79,10 +79,12 @@ tasks.jar {
7979
dependencies {
8080
api(project(":core"))
8181
api("software.amazon.awssdk:s3:$awsSdkVersion")
82-
api("software.amazon.awssdk:lambda:$awsSdkVersion")
82+
// api("software.amazon.awssdk:lambda:$awsSdkVersion") // TODO: Restore back to standard SDK post-launch
83+
api(project(":lambda-client"))
8384
api("software.amazon.awssdk:iam:$awsSdkVersion")
8485
api("software.amazon.awssdk:ecr:$awsSdkVersion")
8586
api("software.amazon.awssdk:ecs:$awsSdkVersion")
87+
api("software.amazon.awssdk:ecr:$awsSdkVersion")
8688
api("software.amazon.awssdk:cloudformation:$awsSdkVersion")
8789
api("software.amazon.awssdk:schemas:$awsSdkVersion")
8890
api("software.amazon.awssdk:cloudwatchlogs:$awsSdkVersion")

jetbrains-core/it/software/aws/toolkits/jetbrains/services/clouddebug/CloudDebugTestCase.kt

+2-1
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ import software.aws.toolkits.jetbrains.services.ecs.resources.EcsResources
3030
import software.aws.toolkits.jetbrains.services.ecs.waitForServicesInactive
3131
import software.aws.toolkits.jetbrains.services.ecs.waitForServicesStable
3232
import software.aws.toolkits.jetbrains.utils.rules.CloudFormationLazyInitRule
33+
import java.nio.file.Paths
3334
import java.util.concurrent.CountDownLatch
3435
import java.util.concurrent.TimeUnit
3536

@@ -40,7 +41,7 @@ abstract class CloudDebugTestCase(private val taskDefName: String) {
4041

4142
private val cfnRule = CloudFormationLazyInitRule(
4243
"CloudDebugTestCluster",
43-
CloudDebugTestCase::class.java.getResource("/cloudDebugTestCluster.yaml").readText(),
44+
Paths.get(System.getProperty("testDataPath"), "testFiles", "cloudDebugTestCluster.yaml").toFile().readText(),
4445
emptyList(),
4546
cloudFormationClient
4647
)

jetbrains-core/it/software/aws/toolkits/jetbrains/services/lambda/deploy/SamDeployTest.kt

+10
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,12 @@ import software.aws.toolkits.core.region.AwsRegion
2020
import software.aws.toolkits.core.rules.S3TemporaryBucketRule
2121
import software.aws.toolkits.jetbrains.core.credentials.MockAwsConnectionManager
2222
import software.aws.toolkits.jetbrains.core.credentials.runUnderRealCredentials
23+
import software.aws.toolkits.jetbrains.utils.assumeImageSupport
2324
import software.aws.toolkits.jetbrains.utils.rules.HeavyJavaCodeInsightTestFixtureRule
2425
import software.aws.toolkits.jetbrains.utils.setSamExecutableFromEnvironment
2526
import java.io.File
2627
import java.nio.file.Paths
28+
import java.time.Year
2729
import java.util.UUID
2830
import java.util.concurrent.TimeUnit
2931

@@ -129,6 +131,13 @@ class SamDeployTest {
129131
}
130132
}
131133

134+
@Test
135+
fun deployImageBasedSamApp() {
136+
assumeImageSupport()
137+
// TODO write this test once we have cfn support (or blow up if we take too long :D)
138+
assertThat(Year.now().value).isEqualTo(2020)
139+
}
140+
132141
private fun setUpProject(templateFilePath: String? = null): VirtualFile {
133142
projectRule.fixture.addFileToProject(
134143
"hello_world/app.py",
@@ -181,6 +190,7 @@ class SamDeployTest {
181190
templateFile,
182191
parameters,
183192
bucketRule.createBucket(stackName),
193+
null,
184194
false,
185195
true,
186196
CreateCapabilities.values().toList()

0 commit comments

Comments
 (0)