Skip to content

Commit 74fa574

Browse files
authored
Merge pull request #44589 from tabito-hara/f-aws_emrserverless_application-add_scheduler_configuration
[Enhancement] aws_emrserverless_application: Add `scheduler_configuration` block
2 parents bc32e54 + 78765e3 commit 74fa574

File tree

4 files changed

+238
-5
lines changed

4 files changed

+238
-5
lines changed

.changelog/44589.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
```release-note:enhancement
2+
resource/aws_emrserverless_application: Add `scheduler_configuration` block
3+
```

internal/service/emrserverless/application.go

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -229,6 +229,27 @@ func resourceApplication() *schema.Resource {
229229
Type: schema.TypeString,
230230
Required: true,
231231
},
232+
"scheduler_configuration": {
233+
Type: schema.TypeList,
234+
Optional: true,
235+
MaxItems: 1,
236+
Elem: &schema.Resource{
237+
Schema: map[string]*schema.Schema{
238+
"max_concurrent_runs": {
239+
Type: schema.TypeInt,
240+
Optional: true,
241+
Computed: true,
242+
ValidateFunc: validation.IntBetween(1, 1000),
243+
},
244+
"queue_timeout_minutes": {
245+
Type: schema.TypeInt,
246+
Optional: true,
247+
Computed: true,
248+
ValidateFunc: validation.IntBetween(15, 720),
249+
},
250+
},
251+
},
252+
},
232253
names.AttrTags: tftags.TagsSchema(),
233254
names.AttrTagsAll: tftags.TagsSchemaComputed(),
234255
names.AttrType: {
@@ -288,6 +309,11 @@ func resourceApplicationCreate(ctx context.Context, d *schema.ResourceData, meta
288309
input.NetworkConfiguration = expandNetworkConfiguration(v.([]any)[0].(map[string]any))
289310
}
290311

312+
// Empty block (len(v.([]any)) > 0 but v.([]any)[0] == nil) is allowed to enable scheduler_configuration with default values
313+
if v, ok := d.GetOk("scheduler_configuration"); ok && len(v.([]any)) > 0 {
314+
input.SchedulerConfiguration = expandSchedulerConfiguration(v.([]any))
315+
}
316+
291317
output, err := conn.CreateApplication(ctx, input)
292318

293319
if err != nil {
@@ -353,6 +379,10 @@ func resourceApplicationRead(ctx context.Context, d *schema.ResourceData, meta a
353379
return sdkdiag.AppendErrorf(diags, "setting network_configuration: %s", err)
354380
}
355381

382+
if err := d.Set("scheduler_configuration", flattenSchedulerConfiguration(application.SchedulerConfiguration)); err != nil {
383+
return sdkdiag.AppendErrorf(diags, "setting scheduler_configuration: %s", err)
384+
}
385+
356386
setTagsOut(ctx, application.Tags)
357387

358388
return diags
@@ -400,6 +430,16 @@ func resourceApplicationUpdate(ctx context.Context, d *schema.ResourceData, meta
400430
input.NetworkConfiguration = expandNetworkConfiguration(v.([]any)[0].(map[string]any))
401431
}
402432

433+
if d.HasChange("scheduler_configuration") {
434+
// Empty block (len(v.([]any)) > 0 but v.([]any)[0] == nil) is allowed to enable scheduler_configuration with default values
435+
if v, ok := d.GetOk("scheduler_configuration"); ok && len(v.([]any)) > 0 {
436+
input.SchedulerConfiguration = expandSchedulerConfiguration(v.([]any))
437+
} else {
438+
// scheduler_configuration block is removed
439+
input.SchedulerConfiguration = &types.SchedulerConfiguration{}
440+
}
441+
}
442+
403443
if v, ok := d.GetOk("release_label"); ok {
404444
input.ReleaseLabel = aws.String(v.(string))
405445
}
@@ -862,3 +902,43 @@ func flattenWorkerResourceConfig(apiObject *types.WorkerResourceConfig) map[stri
862902

863903
return tfMap
864904
}
905+
906+
func expandSchedulerConfiguration(tfList []any) *types.SchedulerConfiguration {
907+
// SchedulerConfiguration without any attributes disables the scheduler_configuration.
908+
// If an empty block is specified, the scheduler_configuration is enabled with default values.
909+
if tfList[0] == nil {
910+
return &types.SchedulerConfiguration{
911+
MaxConcurrentRuns: aws.Int32(15), // default
912+
QueueTimeoutMinutes: aws.Int32(360), // default
913+
}
914+
}
915+
916+
apiObject := &types.SchedulerConfiguration{}
917+
m := tfList[0].(map[string]any)
918+
919+
if v, ok := m["max_concurrent_runs"].(int); ok && v != 0 {
920+
apiObject.MaxConcurrentRuns = aws.Int32(int32(v))
921+
}
922+
923+
if v, ok := m["queue_timeout_minutes"].(int); ok && v != 0 {
924+
apiObject.QueueTimeoutMinutes = aws.Int32(int32(v))
925+
}
926+
927+
return apiObject
928+
}
929+
930+
func flattenSchedulerConfiguration(apiObject *types.SchedulerConfiguration) []any {
931+
if apiObject == nil {
932+
return nil
933+
}
934+
935+
tfMap := map[string]any{}
936+
if v := apiObject.MaxConcurrentRuns; v != nil {
937+
tfMap["max_concurrent_runs"] = aws.ToInt32(v)
938+
}
939+
940+
if v := apiObject.QueueTimeoutMinutes; v != nil {
941+
tfMap["queue_timeout_minutes"] = aws.ToInt32(v)
942+
}
943+
return []any{tfMap}
944+
}

internal/service/emrserverless/application_test.go

Lines changed: 146 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ func TestAccEMRServerlessApplication_basic(t *testing.T) {
3131
CheckDestroy: testAccCheckApplicationDestroy(ctx, t),
3232
Steps: []resource.TestStep{
3333
{
34-
Config: testAccApplicationConfig_basic(rName),
34+
Config: testAccApplicationConfig_basic(rName, "emr-6.6.0"),
3535
Check: resource.ComposeTestCheckFunc(
3636
testAccCheckApplicationExists(ctx, t, resourceName, &application),
3737
acctest.MatchResourceAttrRegionalARN(ctx, resourceName, names.AttrARN, "emr-serverless", regexache.MustCompile(`/applications/.+$`)),
@@ -366,7 +366,7 @@ func TestAccEMRServerlessApplication_disappears(t *testing.T) {
366366
CheckDestroy: testAccCheckApplicationDestroy(ctx, t),
367367
Steps: []resource.TestStep{
368368
{
369-
Config: testAccApplicationConfig_basic(rName),
369+
Config: testAccApplicationConfig_basic(rName, "emr-6.6.0"),
370370
Check: resource.ComposeTestCheckFunc(
371371
testAccCheckApplicationExists(ctx, t, resourceName, &application),
372372
acctest.CheckResourceDisappears(ctx, acctest.Provider, tfemrserverless.ResourceApplication(), resourceName),
@@ -448,6 +448,96 @@ func testAccCheckApplicationExists(ctx context.Context, t *testing.T, resourceNa
448448
}
449449
}
450450

451+
func TestAccEMRServerlessApplication_schedulerConfiguration(t *testing.T) {
452+
ctx := acctest.Context(t)
453+
var application types.Application
454+
resourceName := "aws_emrserverless_application.test"
455+
rName := acctest.RandomWithPrefix(t, acctest.ResourcePrefix)
456+
457+
acctest.ParallelTest(ctx, t, resource.TestCase{
458+
PreCheck: func() { acctest.PreCheck(ctx, t) },
459+
ErrorCheck: acctest.ErrorCheck(t, names.EMRServerlessServiceID),
460+
ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories,
461+
CheckDestroy: testAccCheckApplicationDestroy(ctx, t),
462+
Steps: []resource.TestStep{
463+
{
464+
Config: testAccApplicationConfig_schedulerConfiguration(rName, 10, 60),
465+
Check: resource.ComposeTestCheckFunc(
466+
testAccCheckApplicationExists(ctx, t, resourceName, &application),
467+
resource.TestCheckResourceAttr(resourceName, "scheduler_configuration.#", "1"),
468+
resource.TestCheckResourceAttr(resourceName, "scheduler_configuration.0.max_concurrent_runs", "10"),
469+
resource.TestCheckResourceAttr(resourceName, "scheduler_configuration.0.queue_timeout_minutes", "60"),
470+
),
471+
},
472+
{
473+
ResourceName: resourceName,
474+
ImportState: true,
475+
ImportStateVerify: true,
476+
},
477+
{
478+
Config: testAccApplicationConfig_schedulerConfiguration(rName, 20, 120),
479+
Check: resource.ComposeTestCheckFunc(
480+
testAccCheckApplicationExists(ctx, t, resourceName, &application),
481+
resource.TestCheckResourceAttr(resourceName, "scheduler_configuration.#", "1"),
482+
resource.TestCheckResourceAttr(resourceName, "scheduler_configuration.0.max_concurrent_runs", "20"),
483+
resource.TestCheckResourceAttr(resourceName, "scheduler_configuration.0.queue_timeout_minutes", "120"),
484+
),
485+
},
486+
{ // When `scheduler_configuration` is removed, scheduler configuration is disabled
487+
Config: testAccApplicationConfig_basic(rName, "emr-7.1.0"),
488+
Check: resource.ComposeTestCheckFunc(
489+
testAccCheckApplicationExists(ctx, t, resourceName, &application),
490+
resource.TestCheckResourceAttr(resourceName, "scheduler_configuration.#", "0"),
491+
),
492+
},
493+
{
494+
// If both arguments are omitted and an empty block is specified for scheduler_config, defaults of 15 and 360 are used
495+
Config: testAccApplicationConfig_schedulerConfigurationEmptyBlock(rName),
496+
Check: resource.ComposeTestCheckFunc(
497+
testAccCheckApplicationExists(ctx, t, resourceName, &application),
498+
resource.TestCheckResourceAttr(resourceName, "scheduler_configuration.#", "1"),
499+
resource.TestCheckResourceAttr(resourceName, "scheduler_configuration.0.max_concurrent_runs", "15"),
500+
resource.TestCheckResourceAttr(resourceName, "scheduler_configuration.0.queue_timeout_minutes", "360"),
501+
),
502+
},
503+
{
504+
Config: testAccApplicationConfig_basic(rName, "emr-7.1.0"),
505+
Check: resource.ComposeTestCheckFunc(
506+
testAccCheckApplicationExists(ctx, t, resourceName, &application),
507+
resource.TestCheckResourceAttr(resourceName, "scheduler_configuration.#", "0"),
508+
),
509+
},
510+
{
511+
// If queue_timeout_minutes is omitted, default of 360 is used
512+
Config: testAccApplicationConfig_schedulerConfigurationMaxConcurrentRuns(rName, 30),
513+
Check: resource.ComposeTestCheckFunc(
514+
testAccCheckApplicationExists(ctx, t, resourceName, &application),
515+
resource.TestCheckResourceAttr(resourceName, "scheduler_configuration.#", "1"),
516+
resource.TestCheckResourceAttr(resourceName, "scheduler_configuration.0.max_concurrent_runs", "30"),
517+
resource.TestCheckResourceAttr(resourceName, "scheduler_configuration.0.queue_timeout_minutes", "360"),
518+
),
519+
},
520+
{
521+
Config: testAccApplicationConfig_basic(rName, "emr-7.1.0"),
522+
Check: resource.ComposeTestCheckFunc(
523+
testAccCheckApplicationExists(ctx, t, resourceName, &application),
524+
resource.TestCheckResourceAttr(resourceName, "scheduler_configuration.#", "0"),
525+
),
526+
},
527+
{
528+
// If max_concurrent_runs is omitted, default of 15 is used
529+
Config: testAccApplicationConfig_schedulerConfigurationQueueTimeoutMinutes(rName, 180),
530+
Check: resource.ComposeTestCheckFunc(
531+
testAccCheckApplicationExists(ctx, t, resourceName, &application),
532+
resource.TestCheckResourceAttr(resourceName, "scheduler_configuration.#", "1"),
533+
resource.TestCheckResourceAttr(resourceName, "scheduler_configuration.0.max_concurrent_runs", "15"),
534+
resource.TestCheckResourceAttr(resourceName, "scheduler_configuration.0.queue_timeout_minutes", "180"),
535+
),
536+
},
537+
},
538+
})
539+
}
540+
451541
func testAccCheckApplicationDestroy(ctx context.Context, t *testing.T) resource.TestCheckFunc {
452542
return func(s *terraform.State) error {
453543
conn := acctest.ProviderMeta(ctx, t).EMRServerlessClient(ctx)
@@ -473,14 +563,14 @@ func testAccCheckApplicationDestroy(ctx context.Context, t *testing.T) resource.
473563
}
474564
}
475565

476-
func testAccApplicationConfig_basic(rName string) string {
566+
func testAccApplicationConfig_basic(rName, releaseLabel string) string {
477567
return fmt.Sprintf(`
478568
resource "aws_emrserverless_application" "test" {
479569
name = %[1]q
480-
release_label = "emr-6.6.0"
570+
release_label = %[2]q
481571
type = "hive"
482572
}
483-
`, rName)
573+
`, rName, releaseLabel)
484574
}
485575

486576
func testAccApplicationConfig_releaseLabel(rName string, rl string) string {
@@ -815,3 +905,54 @@ resource "aws_emrserverless_application" "test" {
815905
}
816906
`, rName, selectedVersionResourceName, firstImageVersion, secondImageVersion), nil
817907
}
908+
909+
func testAccApplicationConfig_schedulerConfiguration(rName string, maxConcurrentRuns, queueTimeoutMinutes int) string {
910+
return fmt.Sprintf(`
911+
resource "aws_emrserverless_application" "test" {
912+
name = %[1]q
913+
release_label = "emr-7.1.0"
914+
type = "hive"
915+
scheduler_configuration {
916+
max_concurrent_runs = %[2]d
917+
queue_timeout_minutes = %[3]d
918+
}
919+
}
920+
`, rName, maxConcurrentRuns, queueTimeoutMinutes)
921+
}
922+
923+
func testAccApplicationConfig_schedulerConfigurationEmptyBlock(rName string) string {
924+
return fmt.Sprintf(`
925+
resource "aws_emrserverless_application" "test" {
926+
name = %[1]q
927+
release_label = "emr-7.1.0"
928+
type = "hive"
929+
scheduler_configuration {}
930+
}
931+
`, rName)
932+
}
933+
934+
func testAccApplicationConfig_schedulerConfigurationMaxConcurrentRuns(rName string, maxConcurrentRuns int) string {
935+
return fmt.Sprintf(`
936+
resource "aws_emrserverless_application" "test" {
937+
name = %[1]q
938+
release_label = "emr-7.1.0"
939+
type = "hive"
940+
scheduler_configuration {
941+
max_concurrent_runs = %[2]d
942+
}
943+
}
944+
`, rName, maxConcurrentRuns)
945+
}
946+
947+
func testAccApplicationConfig_schedulerConfigurationQueueTimeoutMinutes(rName string, queueTimeoutMinutes int) string {
948+
return fmt.Sprintf(`
949+
resource "aws_emrserverless_application" "test" {
950+
name = %[1]q
951+
release_label = "emr-7.1.0"
952+
type = "hive"
953+
scheduler_configuration {
954+
queue_timeout_minutes = %[2]d
955+
}
956+
}
957+
`, rName, queueTimeoutMinutes)
958+
}

website/docs/r/emrserverless_application.html.markdown

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@ This resource supports the following arguments:
7474
* `name` - (Required) The name of the application.
7575
* `network_configuration` - (Optional) The network configuration for customer VPC connectivity.
7676
* `release_label` - (Required) The EMR release version associated with the application.
77+
* `scheduler_configuration` - (Optional) Scheduler configuration for batch and streaming jobs running on this application. Supported with release labels `emr-7.0.0` and above. See [scheduler_configuration Arguments](#scheduler_configuration-arguments) below.
7778
* `type` - (Required) The type of application you want to start, such as `spark` or `hive`.
7879
* `tags` - (Optional) Key-value mapping of resource tags. If configured with a provider [`default_tags` configuration block](https://registry.terraform.io/providers/hashicorp/aws/latest/docs#default_tags-configuration-block) present, tags with matching keys will overwrite those defined at the provider-level.
7980

@@ -122,6 +123,14 @@ This resource supports the following arguments:
122123
* `disk` - (Optional) The disk requirements for every worker instance of the worker type.
123124
* `memory` - (Required) The memory requirements for every worker instance of the worker type.
124125

126+
### scheduler_configuration Arguments
127+
128+
When an empty `scheduler_configuration {}` block is specified, the feature is enabled with default settings.
129+
To disable the feature after it has been enabled, remove the block from the configuration.
130+
131+
* `max_concurrent_runs` - (Optional) Maximum concurrent job runs on this application. Valid range is `1` to `1000`. Defaults to `15`.
132+
* `queue_timeout_minutes` - (Optional) Maximum duration in minutes for the job in QUEUED state. Valid range is from `15` to `720`. Defaults to `360`.
133+
125134
## Attribute Reference
126135

127136
This resource exports the following attributes in addition to the arguments above:

0 commit comments

Comments
 (0)