Skip to content

Commit f6a57fe

Browse files
committed
Implementation of Lambda durable config feature
1 parent 24a6443 commit f6a57fe

File tree

6 files changed

+352
-0
lines changed

6 files changed

+352
-0
lines changed

internal/service/lambda/function.go

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,26 @@ func resourceFunction() *schema.Resource {
149149
Type: schema.TypeString,
150150
Optional: true,
151151
},
152+
"durable_config": {
153+
Type: schema.TypeList,
154+
Optional: true,
155+
MaxItems: 1,
156+
Elem: &schema.Resource{
157+
Schema: map[string]*schema.Schema{
158+
"execution_timeout": {
159+
Type: schema.TypeInt,
160+
Required: true,
161+
ValidateFunc: validation.IntBetween(1, 31622400),
162+
},
163+
"retention_period": {
164+
Type: schema.TypeInt,
165+
Optional: true,
166+
Default: 14,
167+
ValidateFunc: validation.IntBetween(1, 90),
168+
},
169+
},
170+
},
171+
},
152172
names.AttrEnvironment: {
153173
Type: schema.TypeList,
154174
Optional: true,
@@ -528,6 +548,13 @@ func resourceFunction() *schema.Resource {
528548
CustomizeDiff: customdiff.Sequence(
529549
checkHandlerRuntimeForZipFunction,
530550
updateComputedAttributesOnPublish,
551+
customdiff.ForceNewIfChange("durable_config", func(_ context.Context, old, new, meta any) bool {
552+
// Force new when durable_config is being added (from empty to non-empty) or removed (from non-empty to empty)
553+
// Allow updates to execution_timeout and retention_period when durable_config already exists
554+
oldLen := len(old.([]any))
555+
newLen := len(new.([]any))
556+
return (oldLen == 0 && newLen > 0) || (oldLen > 0 && newLen == 0)
557+
}),
531558
),
532559
}
533560
}
@@ -595,6 +622,10 @@ func resourceFunctionCreate(ctx context.Context, d *schema.ResourceData, meta an
595622
}
596623
}
597624

625+
if v, ok := d.GetOk("durable_config"); ok && len(v.([]any)) > 0 {
626+
input.DurableConfig = expandDurableConfigs(v.([]any))
627+
}
628+
598629
if v, ok := d.GetOk(names.AttrEnvironment); ok && len(v.([]any)) > 0 && v.([]any)[0] != nil {
599630
if v, ok := v.([]any)[0].(map[string]any)["variables"].(map[string]any); ok && len(v) > 0 {
600631
input.Environment = &awstypes.Environment{
@@ -765,6 +796,13 @@ func resourceFunctionRead(ctx context.Context, d *schema.ResourceData, meta any)
765796
} else {
766797
d.Set("dead_letter_config", []any{})
767798
}
799+
if function.DurableConfig != nil {
800+
if err := d.Set("durable_config", flattenDurableConfig(function.DurableConfig)); err != nil {
801+
return sdkdiag.AppendErrorf(diags, "setting durable_config: %s", err)
802+
}
803+
} else {
804+
d.Set("durable_config", []any{})
805+
}
768806
d.Set(names.AttrDescription, function.Description)
769807
if err := d.Set(names.AttrEnvironment, flattenEnvironment(function.Environment)); err != nil {
770808
return sdkdiag.AppendErrorf(diags, "setting environment: %s", err)
@@ -951,6 +989,12 @@ func resourceFunctionUpdate(ctx context.Context, d *schema.ResourceData, meta an
951989
}
952990
}
953991

992+
if d.HasChange("durable_config") {
993+
if v, ok := d.GetOk("durable_config"); ok && len(v.([]any)) > 0 {
994+
input.DurableConfig = expandDurableConfigs(v.([]any))
995+
}
996+
}
997+
954998
if d.HasChange(names.AttrDescription) {
955999
input.Description = aws.String(d.Get(names.AttrDescription).(string))
9561000
}
@@ -1776,6 +1820,40 @@ func expandFileSystemConfigs(tfList []any) []awstypes.FileSystemConfig {
17761820
return apiObjects
17771821
}
17781822

1823+
func expandDurableConfigs(tfList []any) *awstypes.DurableConfig {
1824+
if len(tfList) == 0 || tfList[0] == nil {
1825+
return nil
1826+
}
1827+
1828+
tfMap := tfList[0].(map[string]any)
1829+
return &awstypes.DurableConfig{
1830+
ExecutionTimeout: aws.Int32(int32(tfMap["execution_timeout"].(int))),
1831+
RetentionPeriodInDays: aws.Int32(int32(tfMap["retention_period"].(int))),
1832+
}
1833+
}
1834+
1835+
func flattenDurableConfig(apiObject *awstypes.DurableConfig) []any {
1836+
if apiObject == nil {
1837+
return nil
1838+
}
1839+
1840+
tfMap := map[string]any{}
1841+
1842+
if v := apiObject.ExecutionTimeout; v != nil {
1843+
tfMap["execution_timeout"] = aws.ToInt32(v)
1844+
}
1845+
1846+
if v := apiObject.RetentionPeriodInDays; v != nil {
1847+
tfMap["retention_period"] = aws.ToInt32(v)
1848+
}
1849+
1850+
if len(tfMap) == 0 {
1851+
return nil
1852+
}
1853+
1854+
return []any{tfMap}
1855+
}
1856+
17791857
func flattenImageConfig(apiObject *awstypes.ImageConfigResponse) []any {
17801858
tfMap := make(map[string]any)
17811859

internal/service/lambda/function_data_source.go

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,22 @@ func dataSourceFunction() *schema.Resource {
8989
Type: schema.TypeString,
9090
Computed: true,
9191
},
92+
"durable_config": {
93+
Type: schema.TypeList,
94+
Computed: true,
95+
Elem: &schema.Resource{
96+
Schema: map[string]*schema.Schema{
97+
"execution_timeout": {
98+
Type: schema.TypeInt,
99+
Computed: true,
100+
},
101+
"retention_period": {
102+
Type: schema.TypeInt,
103+
Computed: true,
104+
},
105+
},
106+
},
107+
},
92108
names.AttrEnvironment: {
93109
Type: schema.TypeList,
94110
Computed: true,
@@ -370,6 +386,11 @@ func dataSourceFunctionRead(ctx context.Context, d *schema.ResourceData, meta an
370386
d.Set("dead_letter_config", []any{})
371387
}
372388
d.Set(names.AttrDescription, function.Description)
389+
if function.DurableConfig != nil {
390+
if err := d.Set("durable_config", flattenDurableConfig(function.DurableConfig)); err != nil {
391+
return sdkdiag.AppendErrorf(diags, "setting durable_config: %s", err)
392+
}
393+
}
373394
if err := d.Set(names.AttrEnvironment, flattenEnvironment(function.Environment)); err != nil {
374395
return sdkdiag.AppendErrorf(diags, "setting environment: %s", err)
375396
}

internal/service/lambda/function_data_source_test.go

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -421,6 +421,30 @@ func TestAccLambdaFunctionDataSource_tenancyConfig(t *testing.T) {
421421
})
422422
}
423423

424+
func TestAccLambdaFunctionDataSource_durableConfig(t *testing.T) {
425+
ctx := acctest.Context(t)
426+
rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix)
427+
dataSourceName := "data.aws_lambda_function.test"
428+
resourceName := "aws_lambda_function.test"
429+
430+
resource.ParallelTest(t, resource.TestCase{
431+
PreCheck: func() { acctest.PreCheck(ctx, t) },
432+
ErrorCheck: acctest.ErrorCheck(t, names.LambdaServiceID),
433+
ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories,
434+
Steps: []resource.TestStep{
435+
{
436+
Config: testAccFunctionDataSourceConfig_durableConfig(rName),
437+
Check: resource.ComposeAggregateTestCheckFunc(
438+
resource.TestCheckResourceAttrPair(dataSourceName, names.AttrARN, resourceName, names.AttrARN),
439+
resource.TestCheckResourceAttrPair(dataSourceName, "durable_config.#", resourceName, "durable_config.#"),
440+
resource.TestCheckResourceAttrPair(dataSourceName, "durable_config.0.execution_timeout", resourceName, "durable_config.0.execution_timeout"),
441+
resource.TestCheckResourceAttrPair(dataSourceName, "durable_config.0.retention_period", resourceName, "durable_config.0.retention_period"),
442+
),
443+
},
444+
},
445+
})
446+
}
447+
424448
func TestAccLambdaFunctionDataSource_capacityProvider(t *testing.T) {
425449
ctx := acctest.Context(t)
426450
rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix)
@@ -944,3 +968,50 @@ data "aws_lambda_function" "test" {
944968
}
945969
`, rName))
946970
}
971+
972+
func testAccFunctionDataSourceConfig_capacityProvider(rName string) string {
973+
return acctest.ConfigCompose(
974+
testAccCapacityProviderConfig_basic(rName),
975+
fmt.Sprintf(`
976+
resource "aws_lambda_function" "test" {
977+
filename = "test-fixtures/capacityprovider.zip"
978+
function_name = %[1]q
979+
role = aws_iam_role.test.arn
980+
handler = "index.handler"
981+
runtime = "python3.14"
982+
memory_size = 2048
983+
984+
publish = true
985+
publish_to = "LATEST_PUBLISHED"
986+
987+
capacity_provider_config {
988+
lambda_managed_instances_capacity_provider_config {
989+
capacity_provider_arn = aws_lambda_capacity_provider.test.arn
990+
}
991+
}
992+
}
993+
994+
data "aws_lambda_function" "test" {
995+
function_name = aws_lambda_function.test.function_name
996+
}
997+
`, rName))
998+
}
999+
1000+
func testAccFunctionDataSourceConfig_durableConfig(rName string) string {
1001+
return acctest.ConfigCompose(testAccFunctionDataSourceConfig_base(rName), fmt.Sprintf(`
1002+
resource "aws_lambda_function" "test" {
1003+
filename = "test-fixtures/lambdatest.zip"
1004+
function_name = %[1]q
1005+
handler = "exports.example"
1006+
role = aws_iam_role.lambda.arn
1007+
runtime = "nodejs20.x"
1008+
durable_config {
1009+
execution_timeout = 300
1010+
retention_period = 7
1011+
}
1012+
}
1013+
data "aws_lambda_function" "test" {
1014+
function_name = aws_lambda_function.test.function_name
1015+
}
1016+
`, rName))
1017+
}

internal/service/lambda/function_test.go

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2476,6 +2476,98 @@ func TestAccLambdaFunction_tenancyConfigForceNew(t *testing.T) {
24762476
})
24772477
}
24782478

2479+
func TestAccLambdaFunction_durableConfig(t *testing.T) {
2480+
ctx := acctest.Context(t)
2481+
var conf lambda.GetFunctionOutput
2482+
rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix)
2483+
resourceName := "aws_lambda_function.test"
2484+
2485+
resource.ParallelTest(t, resource.TestCase{
2486+
PreCheck: func() { acctest.PreCheck(ctx, t) },
2487+
ErrorCheck: acctest.ErrorCheck(t, names.LambdaServiceID),
2488+
ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories,
2489+
CheckDestroy: testAccCheckFunctionDestroy(ctx),
2490+
Steps: []resource.TestStep{
2491+
{
2492+
Config: testAccFunctionConfig_durableConfig(rName, "", 300, 7),
2493+
Check: resource.ComposeTestCheckFunc(
2494+
testAccCheckFunctionExists(ctx, resourceName, &conf),
2495+
resource.TestCheckResourceAttr(resourceName, "durable_config.#", "1"),
2496+
resource.TestCheckResourceAttr(resourceName, "durable_config.0.execution_timeout", "300"),
2497+
resource.TestCheckResourceAttr(resourceName, "durable_config.0.retention_period", "7"),
2498+
),
2499+
},
2500+
{
2501+
ResourceName: resourceName,
2502+
ImportState: true,
2503+
ImportStateVerify: true,
2504+
ImportStateVerifyIgnore: []string{"filename", "publish"},
2505+
},
2506+
{
2507+
Config: testAccFunctionConfig_durableConfig(rName, "Updated description", 300, 7),
2508+
Check: resource.ComposeTestCheckFunc(
2509+
testAccCheckFunctionExists(ctx, resourceName, &conf),
2510+
resource.TestCheckResourceAttr(resourceName, "durable_config.#", "1"),
2511+
resource.TestCheckResourceAttr(resourceName, "durable_config.0.execution_timeout", "300"),
2512+
resource.TestCheckResourceAttr(resourceName, "durable_config.0.retention_period", "7"),
2513+
resource.TestCheckResourceAttr(resourceName, names.AttrDescription, "Updated description"),
2514+
),
2515+
},
2516+
{
2517+
Config: testAccFunctionConfig_durableConfig(rName, "Updated description", 600, 14),
2518+
ConfigPlanChecks: resource.ConfigPlanChecks{
2519+
PreApply: []plancheck.PlanCheck{
2520+
plancheck.ExpectResourceAction(resourceName, plancheck.ResourceActionUpdate),
2521+
},
2522+
},
2523+
Check: resource.ComposeTestCheckFunc(
2524+
testAccCheckFunctionExists(ctx, resourceName, &conf),
2525+
resource.TestCheckResourceAttr(resourceName, "durable_config.#", "1"),
2526+
resource.TestCheckResourceAttr(resourceName, "durable_config.0.execution_timeout", "600"),
2527+
resource.TestCheckResourceAttr(resourceName, "durable_config.0.retention_period", "14"),
2528+
),
2529+
},
2530+
},
2531+
})
2532+
}
2533+
2534+
func TestAccLambdaFunction_durableConfigForceNew(t *testing.T) {
2535+
ctx := acctest.Context(t)
2536+
var conf lambda.GetFunctionOutput
2537+
rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix)
2538+
resourceName := "aws_lambda_function.test"
2539+
2540+
resource.ParallelTest(t, resource.TestCase{
2541+
PreCheck: func() { acctest.PreCheck(ctx, t) },
2542+
ErrorCheck: acctest.ErrorCheck(t, names.LambdaServiceID),
2543+
ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories,
2544+
CheckDestroy: testAccCheckFunctionDestroy(ctx),
2545+
Steps: []resource.TestStep{
2546+
{
2547+
Config: testAccFunctionConfig_basic(rName, rName, rName, rName),
2548+
Check: resource.ComposeTestCheckFunc(
2549+
testAccCheckFunctionExists(ctx, resourceName, &conf),
2550+
resource.TestCheckResourceAttr(resourceName, "durable_config.#", "0"),
2551+
),
2552+
},
2553+
{
2554+
Config: testAccFunctionConfig_durableConfig(rName, "", 300, 7),
2555+
ConfigPlanChecks: resource.ConfigPlanChecks{
2556+
PreApply: []plancheck.PlanCheck{
2557+
plancheck.ExpectResourceAction(resourceName, plancheck.ResourceActionReplace),
2558+
},
2559+
},
2560+
Check: resource.ComposeTestCheckFunc(
2561+
testAccCheckFunctionExists(ctx, resourceName, &conf),
2562+
resource.TestCheckResourceAttr(resourceName, "durable_config.#", "1"),
2563+
resource.TestCheckResourceAttr(resourceName, "durable_config.0.execution_timeout", "300"),
2564+
resource.TestCheckResourceAttr(resourceName, "durable_config.0.retention_period", "7"),
2565+
),
2566+
},
2567+
},
2568+
})
2569+
}
2570+
24792571
func TestAccLambdaFunction_resetNonRefreshableAttributesAfterUpdateFailure(t *testing.T) {
24802572
ctx := acctest.Context(t)
24812573
var conf lambda.GetFunctionOutput
@@ -4540,6 +4632,30 @@ resource "aws_lambda_function" "test" {
45404632
`, rName, zipFileS3, zipFileLambda))
45414633
}
45424634

4635+
func testAccFunctionConfig_durableConfig(rName, description string, executionTimeout, retentionPeriod int) string {
4636+
descriptionLine := ""
4637+
if description != "" {
4638+
descriptionLine = fmt.Sprintf(" description = %q", description)
4639+
}
4640+
4641+
return acctest.ConfigCompose(
4642+
acctest.ConfigLambdaBase(rName, rName, rName),
4643+
fmt.Sprintf(`
4644+
resource "aws_lambda_function" "test" {
4645+
filename = "test-fixtures/lambdatest.zip"
4646+
function_name = %[1]q
4647+
role = aws_iam_role.iam_for_lambda.arn
4648+
handler = "exports.example"
4649+
runtime = "nodejs20.x"
4650+
%[2]s
4651+
durable_config {
4652+
execution_timeout = %[3]d
4653+
retention_period = %[4]d
4654+
}
4655+
}
4656+
`, rName, descriptionLine, executionTimeout, retentionPeriod))
4657+
}
4658+
45434659
func testAccPreCheckSignerSigningProfile(ctx context.Context, t *testing.T, platformID string) {
45444660
conn := acctest.Provider.Meta().(*conns.AWSClient).SignerClient(ctx)
45454661

0 commit comments

Comments
 (0)