Skip to content

Commit a47e360

Browse files
authored
Merge pull request #16512 from bnusunny/f-aws_lambda_function-oci-image-support
Add lambda container image support
2 parents d4edc4a + eba4514 commit a47e360

3 files changed

Lines changed: 280 additions & 24 deletions

File tree

aws/resource_aws_lambda_function.go

Lines changed: 153 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import (
1414
"github.com/aws/aws-sdk-go/aws/awserr"
1515
"github.com/aws/aws-sdk-go/aws/endpoints"
1616
"github.com/aws/aws-sdk-go/service/lambda"
17+
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/customdiff"
1718
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
1819
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
1920
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation"
@@ -47,22 +48,57 @@ func resourceAwsLambdaFunction() *schema.Resource {
4748
"filename": {
4849
Type: schema.TypeString,
4950
Optional: true,
50-
ConflictsWith: []string{"s3_bucket", "s3_key", "s3_object_version"},
51+
ConflictsWith: []string{"s3_bucket", "s3_key", "s3_object_version", "image_uri"},
5152
},
5253
"s3_bucket": {
5354
Type: schema.TypeString,
5455
Optional: true,
55-
ConflictsWith: []string{"filename"},
56+
ConflictsWith: []string{"filename", "image_uri"},
5657
},
5758
"s3_key": {
5859
Type: schema.TypeString,
5960
Optional: true,
60-
ConflictsWith: []string{"filename"},
61+
ConflictsWith: []string{"filename", "image_uri"},
6162
},
6263
"s3_object_version": {
6364
Type: schema.TypeString,
6465
Optional: true,
65-
ConflictsWith: []string{"filename"},
66+
ConflictsWith: []string{"filename", "image_uri"},
67+
},
68+
"image_uri": {
69+
Type: schema.TypeString,
70+
Optional: true,
71+
ConflictsWith: []string{"filename", "s3_bucket", "s3_key", "s3_object_version"},
72+
},
73+
"package_type": {
74+
Type: schema.TypeString,
75+
Optional: true,
76+
ForceNew: true,
77+
Default: lambda.PackageTypeZip,
78+
ValidateFunc: validation.StringInSlice(lambda.PackageType_Values(), false),
79+
},
80+
"image_config": {
81+
Type: schema.TypeList,
82+
Optional: true,
83+
MaxItems: 1,
84+
Elem: &schema.Resource{
85+
Schema: map[string]*schema.Schema{
86+
"entry_point": {
87+
Type: schema.TypeList,
88+
Optional: true,
89+
Elem: &schema.Schema{Type: schema.TypeString},
90+
},
91+
"command": {
92+
Type: schema.TypeList,
93+
Optional: true,
94+
Elem: &schema.Schema{Type: schema.TypeString},
95+
},
96+
"working_directory": {
97+
Type: schema.TypeString,
98+
Optional: true,
99+
},
100+
},
101+
},
66102
},
67103
"code_signing_config_arn": {
68104
Type: schema.TypeString,
@@ -126,7 +162,7 @@ func resourceAwsLambdaFunction() *schema.Resource {
126162
},
127163
"handler": {
128164
Type: schema.TypeString,
129-
Required: true,
165+
Optional: true,
130166
ValidateFunc: validation.StringLenBetween(1, 128),
131167
},
132168
"layers": {
@@ -155,7 +191,7 @@ func resourceAwsLambdaFunction() *schema.Resource {
155191
},
156192
"runtime": {
157193
Type: schema.TypeString,
158-
Required: true,
194+
Optional: true,
159195
ValidateFunc: validation.StringInSlice(lambda.Runtime_Values(), false),
160196
},
161197
"timeout": {
@@ -280,10 +316,24 @@ func resourceAwsLambdaFunction() *schema.Resource {
280316
"tags": tagsSchema(),
281317
},
282318

283-
CustomizeDiff: updateComputedAttributesOnPublish,
319+
CustomizeDiff: customdiff.Sequence(
320+
checkHandlerRuntimeForZipFunction,
321+
updateComputedAttributesOnPublish,
322+
),
284323
}
285324
}
286325

326+
func checkHandlerRuntimeForZipFunction(_ context.Context, d *schema.ResourceDiff, meta interface{}) error {
327+
packageType := d.Get("package_type")
328+
_, handlerOk := d.GetOk("handler")
329+
_, runtimeOk := d.GetOk("runtime")
330+
331+
if packageType == lambda.PackageTypeZip && !handlerOk && !runtimeOk {
332+
return fmt.Errorf("handler and runtime must be set when PackageType is Zip")
333+
}
334+
return nil
335+
}
336+
287337
func updateComputedAttributesOnPublish(_ context.Context, d *schema.ResourceDiff, meta interface{}) error {
288338
configChanged := hasConfigChanges(d)
289339
functionCodeUpdated := needsFunctionCodeUpdate(d)
@@ -304,6 +354,7 @@ func hasConfigChanges(d resourceDiffer) bool {
304354
return d.HasChange("description") ||
305355
d.HasChange("handler") ||
306356
d.HasChange("file_system_config") ||
357+
d.HasChange("image_config") ||
307358
d.HasChange("memory_size") ||
308359
d.HasChange("role") ||
309360
d.HasChange("timeout") ||
@@ -331,9 +382,10 @@ func resourceAwsLambdaFunctionCreate(d *schema.ResourceData, meta interface{}) e
331382
s3Bucket, bucketOk := d.GetOk("s3_bucket")
332383
s3Key, keyOk := d.GetOk("s3_key")
333384
s3ObjectVersion, versionOk := d.GetOk("s3_object_version")
385+
imageUri, hasImageUri := d.GetOk("image_uri")
334386

335-
if !hasFilename && !bucketOk && !keyOk && !versionOk {
336-
return errors.New("filename or s3_* attributes must be set")
387+
if !hasFilename && !bucketOk && !keyOk && !versionOk && !hasImageUri {
388+
return errors.New("filename, s3_* or image_uri attributes must be set")
337389
}
338390

339391
var functionCode *lambda.FunctionCode
@@ -350,6 +402,10 @@ func resourceAwsLambdaFunctionCreate(d *schema.ResourceData, meta interface{}) e
350402
functionCode = &lambda.FunctionCode{
351403
ZipFile: file,
352404
}
405+
} else if hasImageUri {
406+
functionCode = &lambda.FunctionCode{
407+
ImageUri: aws.String(imageUri.(string)),
408+
}
353409
} else {
354410
if !bucketOk || !keyOk {
355411
return errors.New("s3_bucket and s3_key must all be set while using S3 code source")
@@ -363,16 +419,28 @@ func resourceAwsLambdaFunctionCreate(d *schema.ResourceData, meta interface{}) e
363419
}
364420
}
365421

422+
packageType := d.Get("package_type")
423+
handler, handlerOk := d.GetOk("handler")
424+
runtime, runtimeOk := d.GetOk("runtime")
425+
426+
if packageType == lambda.PackageTypeZip && !handlerOk && !runtimeOk {
427+
return errors.New("handler and runtime must be set when PackageType is Zip")
428+
}
429+
366430
params := &lambda.CreateFunctionInput{
367431
Code: functionCode,
368432
Description: aws.String(d.Get("description").(string)),
369433
FunctionName: aws.String(functionName),
370-
Handler: aws.String(d.Get("handler").(string)),
371434
MemorySize: aws.Int64(int64(d.Get("memory_size").(int))),
372435
Role: aws.String(iamRole),
373-
Runtime: aws.String(d.Get("runtime").(string)),
374436
Timeout: aws.Int64(int64(d.Get("timeout").(int))),
375437
Publish: aws.Bool(d.Get("publish").(bool)),
438+
PackageType: aws.String(d.Get("package_type").(string)),
439+
}
440+
441+
if packageType == lambda.PackageTypeZip {
442+
params.Handler = aws.String(handler.(string))
443+
params.Runtime = aws.String(runtime.(string))
376444
}
377445

378446
if v, ok := d.GetOk("code_signing_config_arn"); ok {
@@ -401,6 +469,10 @@ func resourceAwsLambdaFunctionCreate(d *schema.ResourceData, meta interface{}) e
401469
params.FileSystemConfigs = expandLambdaFileSystemConfigs(v.([]interface{}))
402470
}
403471

472+
if v, ok := d.GetOk("image_config"); ok && len(v.([]interface{})) > 0 {
473+
params.ImageConfig = expandLambdaImageConfigs(v.([]interface{}))
474+
}
475+
404476
if v, ok := d.GetOk("vpc_config"); ok && len(v.([]interface{})) > 0 {
405477
config := v.([]interface{})[0].(map[string]interface{})
406478

@@ -641,6 +713,23 @@ func resourceAwsLambdaFunctionRead(d *schema.ResourceData, meta interface{}) err
641713
return fmt.Errorf("Error setting file system config for Lambda Function (%s): %w", d.Id(), err)
642714
}
643715

716+
// Add Package Type
717+
log.Printf("[INFO] Setting Lambda %s package type %#v from API", d.Id(), function.PackageType)
718+
if err := d.Set("package_type", function.PackageType); err != nil {
719+
return fmt.Errorf("Error setting package type for Lambda Function: %w", err)
720+
}
721+
722+
// Add Image Configuration
723+
imageConfig := flattenLambdaImageConfig(function.ImageConfigResponse)
724+
log.Printf("[INFO] Setting Lambda %s Image config %#v from API", d.Id(), imageConfig)
725+
if err := d.Set("image_config", imageConfig); err != nil {
726+
return fmt.Errorf("Error setting image config for Lambda Function: %s", err)
727+
}
728+
729+
if err := d.Set("image_uri", getFunctionOutput.Code.ImageUri); err != nil {
730+
return fmt.Errorf("Error setting image uri for Lambda Function: %s", err)
731+
}
732+
644733
layers := flattenLambdaLayers(function.Layers)
645734
log.Printf("[INFO] Setting Lambda %s Layers %#v from API", d.Id(), layers)
646735
if err := d.Set("layers", layers); err != nil {
@@ -723,15 +812,20 @@ func resourceAwsLambdaFunctionRead(d *schema.ResourceData, meta interface{}) err
723812
FunctionName: aws.String(d.Get("function_name").(string)),
724813
}
725814

726-
getCodeSigningConfigOutput, err := conn.GetFunctionCodeSigningConfig(codeSigningConfigInput)
727-
if err != nil {
728-
return fmt.Errorf("error getting Lambda Function (%s) code signing config %w", d.Id(), err)
729-
}
815+
// Code Signing is only supported on zip packaged lambda functions.
816+
if *function.PackageType == lambda.PackageTypeZip {
817+
getCodeSigningConfigOutput, err := conn.GetFunctionCodeSigningConfig(codeSigningConfigInput)
818+
if err != nil {
819+
return fmt.Errorf("error getting Lambda Function (%s) code signing config %w", d.Id(), err)
820+
}
730821

731-
if getCodeSigningConfigOutput == nil || getCodeSigningConfigOutput.CodeSigningConfigArn == nil {
732-
d.Set("code_signing_config_arn", "")
822+
if getCodeSigningConfigOutput == nil || getCodeSigningConfigOutput.CodeSigningConfigArn == nil {
823+
d.Set("code_signing_config_arn", "")
824+
} else {
825+
d.Set("code_signing_config_arn", getCodeSigningConfigOutput.CodeSigningConfigArn)
826+
}
733827
} else {
734-
d.Set("code_signing_config_arn", getCodeSigningConfigOutput.CodeSigningConfigArn)
828+
d.Set("code_signing_config_arn", "")
735829
}
736830

737831
return nil
@@ -779,7 +873,9 @@ func needsFunctionCodeUpdate(d resourceDiffer) bool {
779873
d.HasChange("source_code_hash") ||
780874
d.HasChange("s3_bucket") ||
781875
d.HasChange("s3_key") ||
782-
d.HasChange("s3_object_version")
876+
d.HasChange("s3_object_version") ||
877+
d.HasChange("image_uri")
878+
783879
}
784880

785881
// resourceAwsLambdaFunctionUpdate maps to:
@@ -840,6 +936,12 @@ func resourceAwsLambdaFunctionUpdate(d *schema.ResourceData, meta interface{}) e
840936
configReq.FileSystemConfigs = expandLambdaFileSystemConfigs(v.([]interface{}))
841937
}
842938
}
939+
if d.HasChange("image_config") {
940+
configReq.ImageConfig = &lambda.ImageConfig{}
941+
if v, ok := d.GetOk("image_config"); ok && len(v.([]interface{})) > 0 {
942+
configReq.ImageConfig = expandLambdaImageConfigs(v.([]interface{}))
943+
}
944+
}
843945
if d.HasChange("memory_size") {
844946
configReq.MemorySize = aws.Int64(int64(d.Get("memory_size").(int)))
845947
}
@@ -990,6 +1092,8 @@ func resourceAwsLambdaFunctionUpdate(d *schema.ResourceData, meta interface{}) e
9901092
return fmt.Errorf("Unable to load %q: %w", v.(string), err)
9911093
}
9921094
codeReq.ZipFile = file
1095+
} else if v, ok := d.GetOk("image_uri"); ok {
1096+
codeReq.ImageUri = aws.String(v.(string))
9931097
} else {
9941098
s3Bucket, _ := d.GetOk("s3_bucket")
9951099
s3Key, _ := d.GetOk("s3_key")
@@ -1188,3 +1292,33 @@ func expandLambdaFileSystemConfigs(fscMaps []interface{}) []*lambda.FileSystemCo
11881292
}
11891293
return fileSystemConfigs
11901294
}
1295+
1296+
func flattenLambdaImageConfig(response *lambda.ImageConfigResponse) []map[string]interface{} {
1297+
settings := make(map[string]interface{})
1298+
1299+
if response == nil || response.Error != nil {
1300+
return nil
1301+
}
1302+
1303+
settings["command"] = response.ImageConfig.Command
1304+
settings["entry_point"] = response.ImageConfig.EntryPoint
1305+
settings["working_directory"] = response.ImageConfig.WorkingDirectory
1306+
1307+
return []map[string]interface{}{settings}
1308+
}
1309+
1310+
func expandLambdaImageConfigs(imageConfigMaps []interface{}) *lambda.ImageConfig {
1311+
imageConfig := &lambda.ImageConfig{}
1312+
// only one image_config block is allowed
1313+
if len(imageConfigMaps) == 1 && imageConfigMaps[0] != nil {
1314+
config := imageConfigMaps[0].(map[string]interface{})
1315+
if len(config["entry_point"].([]interface{})) > 0 {
1316+
imageConfig.EntryPoint = expandStringList(config["entry_point"].([]interface{}))
1317+
}
1318+
if len(config["command"].([]interface{})) > 0 {
1319+
imageConfig.Command = expandStringList(config["command"].([]interface{}))
1320+
}
1321+
imageConfig.WorkingDirectory = aws.String(config["working_directory"].(string))
1322+
}
1323+
return imageConfig
1324+
}

0 commit comments

Comments
 (0)