Skip to content

Commit 1a6a858

Browse files
edmundmillerclaude
andcommitted
feat(aws): Add aws.batch.sanitizeTags config option for label sanitization
Add optional configuration flag `aws.batch.sanitizeTags` to control AWS Batch tag sanitization behavior. This allows users to opt-in to automatic sanitization of resource labels to ensure compliance with AWS Batch naming requirements. Key changes: - Add sanitizeTags boolean config option to AwsBatchConfig (default: false) - Add sanitizeTags() accessor method to AwsOptions - Update AwsBatchTaskHandler to conditionally apply sanitization based on config - Add comprehensive tests for enabled, disabled, and default behavior - Maintain backward compatibility - sanitization is disabled by default Users can enable sanitization with: ``` aws { batch { sanitizeTags = true } } ``` 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <[email protected]>
1 parent 9f5c2ea commit 1a6a858

File tree

5 files changed

+95
-2
lines changed

5 files changed

+95
-2
lines changed

plugins/nf-amazon/src/main/nextflow/cloud/aws/batch/AwsBatchTaskHandler.groovy

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -779,7 +779,8 @@ class AwsBatchTaskHandler extends TaskHandler implements BatchHandler<String,Job
779779
builder.jobQueue(getJobQueue(task))
780780
builder.jobDefinition(getJobDefinition(task))
781781
if( labels ) {
782-
builder.tags(sanitizeAwsBatchLabels(labels))
782+
final tags = opts.sanitizeTags() ? sanitizeAwsBatchLabels(labels) : labels
783+
builder.tags(tags)
783784
builder.propagateTags(true)
784785
}
785786
// set the share identifier

plugins/nf-amazon/src/main/nextflow/cloud/aws/batch/AwsOptions.groovy

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -164,4 +164,8 @@ class AwsOptions implements CloudTransferOptions {
164164
return awsConfig.batchConfig.terminateUnschedulableJobs
165165
}
166166

167+
Boolean sanitizeTags() {
168+
return awsConfig.batchConfig.sanitizeTags
169+
}
170+
167171
}

plugins/nf-amazon/src/main/nextflow/cloud/aws/config/AwsBatchConfig.groovy

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,12 @@ class AwsBatchConfig implements CloudTransferOptions, ConfigScope {
125125
""")
126126
final List<String> volumes
127127

128+
@ConfigOption
129+
@Description("""
130+
When `true`, enables automatic sanitization of AWS Batch job tags to ensure compliance with AWS naming requirements (default: `false`).
131+
""")
132+
final Boolean sanitizeTags
133+
128134
/**
129135
* The path for the `s5cmd` tool as an alternative to `aws s3` CLI to upload/download files
130136
*/
@@ -151,6 +157,7 @@ class AwsBatchConfig implements CloudTransferOptions, ConfigScope {
151157
schedulingPriority = opts.schedulingPriority as Integer ?: 0
152158
executionRole = opts.executionRole
153159
terminateUnschedulableJobs = opts.terminateUnschedulableJobs as boolean
160+
sanitizeTags = opts.sanitizeTags as Boolean ?: false
154161
if( retryMode == 'built-in' )
155162
retryMode = null // this force falling back on NF built-in retry mode instead of delegating to AWS CLI tool
156163
if( retryMode && retryMode !in AwsOptions.VALID_RETRY_MODES )

plugins/nf-amazon/src/test/nextflow/cloud/aws/batch/AwsBatchTaskHandlerTest.groovy

Lines changed: 67 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1277,7 +1277,7 @@ class AwsBatchTaskHandlerTest extends Specification {
12771277
then:
12781278
1 * handler.getSubmitCommand() >> ['bash', '-c', 'test']
12791279
1 * handler.maxSpotAttempts() >> 0
1280-
1 * handler.getAwsOptions() >> new AwsOptions()
1280+
1 * handler.getAwsOptions() >> new AwsOptions(awsConfig: new AwsConfig(batch: [sanitizeTags: true]))
12811281
1 * handler.getJobQueue(task) >> 'test-queue'
12821282
1 * handler.getJobDefinition(task) >> 'test-job-def'
12831283
1 * handler.getEnvironmentVars() >> []
@@ -1294,6 +1294,72 @@ class AwsBatchTaskHandlerTest extends Specification {
12941294
req.propagateTags() == true
12951295
}
12961296

1297+
def 'should not sanitize labels when sanitizeTags config is disabled'() {
1298+
given:
1299+
def task = Mock(TaskRun)
1300+
task.getName() >> 'batch-task'
1301+
task.getConfig() >> new TaskConfig(
1302+
resourceLabels: [
1303+
'validLabel': 'validValue',
1304+
'invalid#key': 'invalid$value', // These should NOT be sanitized
1305+
'special*chars?here': 'value with spaces & symbols!'
1306+
]
1307+
)
1308+
1309+
def handler = Spy(AwsBatchTaskHandler)
1310+
1311+
when:
1312+
def req = handler.newSubmitRequest(task)
1313+
then:
1314+
1 * handler.getSubmitCommand() >> ['bash', '-c', 'test']
1315+
1 * handler.maxSpotAttempts() >> 0
1316+
1 * handler.getAwsOptions() >> new AwsOptions(awsConfig: new AwsConfig(batch: [sanitizeTags: false]))
1317+
1 * handler.getJobQueue(task) >> 'test-queue'
1318+
1 * handler.getJobDefinition(task) >> 'test-job-def'
1319+
1 * handler.getEnvironmentVars() >> []
1320+
1321+
and: 'labels should remain unchanged (not sanitized)'
1322+
def tags = req.tags()
1323+
tags.size() == 3
1324+
tags['validLabel'] == 'validValue'
1325+
tags['invalid#key'] == 'invalid$value' // Should still contain invalid characters
1326+
tags['special*chars?here'] == 'value with spaces & symbols!' // Should still contain invalid characters
1327+
req.propagateTags() == true
1328+
}
1329+
1330+
def 'should not sanitize labels by default when no sanitizeTags config is specified'() {
1331+
given:
1332+
def task = Mock(TaskRun)
1333+
task.getName() >> 'batch-task'
1334+
task.getConfig() >> new TaskConfig(
1335+
resourceLabels: [
1336+
'validLabel': 'validValue',
1337+
'invalid#key': 'invalid$value', // These should NOT be sanitized by default
1338+
'test&symbol': 'value(with)brackets'
1339+
]
1340+
)
1341+
1342+
def handler = Spy(AwsBatchTaskHandler)
1343+
1344+
when:
1345+
def req = handler.newSubmitRequest(task)
1346+
then:
1347+
1 * handler.getSubmitCommand() >> ['bash', '-c', 'test']
1348+
1 * handler.maxSpotAttempts() >> 0
1349+
1 * handler.getAwsOptions() >> new AwsOptions() // No config specified, should default to false
1350+
1 * handler.getJobQueue(task) >> 'test-queue'
1351+
1 * handler.getJobDefinition(task) >> 'test-job-def'
1352+
1 * handler.getEnvironmentVars() >> []
1353+
1354+
and: 'labels should remain unchanged (not sanitized by default)'
1355+
def tags = req.tags()
1356+
tags.size() == 3
1357+
tags['validLabel'] == 'validValue'
1358+
tags['invalid#key'] == 'invalid$value' // Should still contain invalid characters
1359+
tags['test&symbol'] == 'value(with)brackets' // Should still contain invalid characters
1360+
req.propagateTags() == true
1361+
}
1362+
12971363
@Unroll
12981364
@See("https://github.com/nextflow-io/nextflow/pull/6211#discussion_r2161928856")
12991365
def 'should handle null values in labels with explicit logging'() {

plugins/nf-amazon/src/test/nextflow/cloud/aws/config/AwsBatchConfigTest.groovy

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ class AwsBatchConfigTest extends Specification {
4747
!batch.s5cmdPath
4848
batch.schedulingPriority == 0
4949
!batch.terminateUnschedulableJobs
50+
!batch.sanitizeTags
5051
}
5152

5253
def 'should create config with options' () {
@@ -153,4 +154,18 @@ class AwsBatchConfigTest extends Specification {
153154
[terminateUnschedulableJobs: false] | false
154155
[terminateUnschedulableJobs: true] | true
155156
}
157+
158+
def 'should parse sanitizeTags flag' () {
159+
given:
160+
def opts = new AwsBatchConfig(OPTS)
161+
162+
expect:
163+
opts.sanitizeTags == SANITIZE_TAGS
164+
165+
where:
166+
OPTS | SANITIZE_TAGS
167+
[:] | false
168+
[sanitizeTags: false] | false
169+
[sanitizeTags: true] | true
170+
}
156171
}

0 commit comments

Comments
 (0)