@@ -530,7 +530,7 @@ class AwsBatchTaskHandlerTest extends Specification {
530
530
vol2 : ' /here:/there:ro' ,
531
531
vol3 : ' /this:/that:rw' ,
532
532
]
533
-
533
+
534
534
when :
535
535
handler. addVolumeMountsToContainer(mounts, container)
536
536
then :
@@ -585,7 +585,7 @@ class AwsBatchTaskHandlerTest extends Specification {
585
585
! result. containerProperties. logConfiguration
586
586
! result. containerProperties. mountPoints
587
587
! result. containerProperties. privileged
588
-
588
+
589
589
when :
590
590
result = handler. makeJobDefRequest(task)
591
591
then :
@@ -928,7 +928,7 @@ class AwsBatchTaskHandlerTest extends Specification {
928
928
then :
929
929
1 * handler. isCompleted() >> false
930
930
1 * handler. getMachineInfo() >> new CloudMachineInfo (' x1.large' , ' us-east-1b' , PriceModel . spot)
931
-
931
+
932
932
and :
933
933
trace. native_id == ' xyz-123'
934
934
trace. executorName == ' awsbatch'
@@ -1099,7 +1099,7 @@ class AwsBatchTaskHandlerTest extends Specification {
1099
1099
1100
1100
expect :
1101
1101
handler. normaliseJobId(JOB_ID ) == EXPECTED
1102
-
1102
+
1103
1103
where :
1104
1104
JOB_ID | EXPECTED
1105
1105
null | null
@@ -1118,7 +1118,7 @@ class AwsBatchTaskHandlerTest extends Specification {
1118
1118
task. getName() >> NAME
1119
1119
and :
1120
1120
result == EXPECTED
1121
-
1121
+
1122
1122
where :
1123
1123
ENV | NAME | EXPECTED
1124
1124
[:] | ' foo' | ' foo'
@@ -1155,8 +1155,164 @@ class AwsBatchTaskHandlerTest extends Specification {
1155
1155
2 | true | false | 2
1156
1156
and :
1157
1157
null | true | true | 5 // <-- default to 5
1158
- 0 | true | true | 5 // <-- default to 5
1158
+ 0 | true | true | 5 // <-- default to 5
1159
1159
1 | true | true | 1
1160
1160
2 | true | true | 2
1161
1161
}
1162
+
1163
+ @Unroll
1164
+ def ' should sanitize AWS Batch label' () {
1165
+ given :
1166
+ def handler = Spy (AwsBatchTaskHandler )
1167
+
1168
+ expect :
1169
+ handler. sanitizeAwsBatchLabel(INPUT , MAX_LENGTH ) == EXPECTED
1170
+
1171
+ where :
1172
+ INPUT | MAX_LENGTH | EXPECTED
1173
+ // Valid labels that don't need sanitization
1174
+ ' validLabel' | 50 | ' validLabel'
1175
+ ' valid-label_123' | 50 | ' valid-label_123'
1176
+ ' valid.label:test/path=value+more' | 50 | ' valid.label:test/path=value+more'
1177
+ ' label with spaces' | 50 | ' label with spaces'
1178
+ ' label-with@symbol' | 50 | ' label-with@symbol'
1179
+ and :
1180
+ // Labels with invalid characters
1181
+ ' label#with#hash' | 50 | ' label_with_hash'
1182
+ ' label$with%special&chars' | 50 | ' label_with_special_chars'
1183
+ ' label(with)brackets[and]braces{}' | 50 | ' label_with_brackets_and_braces__'
1184
+ ' label*with?wildcards' | 50 | ' label_with_wildcards'
1185
+ ' unicode_λαβελ_test' | 50 | ' unicode____abel_test'
1186
+ and :
1187
+ // Multiple consecutive invalid characters
1188
+ ' label###multiple###hashes' | 50 | ' label_multiple_hashes'
1189
+ ' label multiple spaces' | 50 | ' label_multiple_spaces'
1190
+ ' label___multiple___underscores' | 50 | ' label_multiple_underscores'
1191
+ ' label$%^&*special*&^%$chars' | 50 | ' label_special_chars'
1192
+ and :
1193
+ // Leading/trailing invalid characters
1194
+ ' ###leading-hashes' | 50 | ' leading-hashes'
1195
+ ' trailing-hashes###' | 50 | ' trailing-hashes'
1196
+ ' leading-spaces' | 50 | ' leading-spaces'
1197
+ ' trailing-spaces ' | 50 | ' trailing-spaces'
1198
+ ' ___leading-underscores' | 50 | ' leading-underscores'
1199
+ ' trailing-underscores___' | 50 | ' trailing-underscores'
1200
+ and :
1201
+ // Length truncation
1202
+ ' very-long-label-that-exceeds-max' | 10 | ' very-long-'
1203
+ ' very-long-label-ending-with-_' | 25 | ' very-long-label-ending-w'
1204
+ ' very-long-label-ending-with-___' | 28 | ' very-long-label-ending-w'
1205
+ and :
1206
+ // Edge cases
1207
+ null | 50 | null
1208
+ ' ' | 50 | ' '
1209
+ ' ' | 50 | null
1210
+ ' ___' | 50 | null
1211
+ ' ###' | 50 | null
1212
+ ' _' | 50 | null
1213
+ ' ' | 50 | null
1214
+ ' #' | 50 | null
1215
+ and :
1216
+ // Complex real-world scenarios
1217
+
1218
+ ' workflow-run-2024/01/15' | 50 | ' workflow-run-2024/01/15'
1219
+ ' task.hash.0x1234abcd' | 50 | ' task.hash.0x1234abcd'
1220
+ ' pipeline#name%with&special*chars' | 50 | ' pipeline_name_with_special_chars'
1221
+ ' session-id:abc123#$%' | 50 | ' session-id:abc123'
1222
+ }
1223
+
1224
+ @Unroll
1225
+ def ' should sanitize AWS Batch labels map' () {
1226
+ given :
1227
+ def handler = Spy (AwsBatchTaskHandler )
1228
+
1229
+ expect :
1230
+ handler. sanitizeAwsBatchLabels(INPUT ) == EXPECTED
1231
+
1232
+ where :
1233
+ INPUT | EXPECTED
1234
+ // Null/empty input
1235
+ null | null
1236
+ [:] | [:]
1237
+ and :
1238
+ // Valid labels
1239
+ [validKey : ' validValue' ] | [validKey : ' validValue' ]
1240
+ [' valid-key_123' : ' valid-value_456' ] | [' valid-key_123' : ' valid-value_456' ]
1241
+ [' key.with:path/chars=test+more@symbol' : ' value with spaces' ] | [' key.with:path/chars=test+more@symbol' : ' value with spaces' ]
1242
+ and :
1243
+ // Invalid characters in keys and values
1244
+ [' key#with#hash' : ' value$with%special&chars' ] | [' key_with_hash' : ' value_with_special_chars' ]
1245
+ [' key(brackets)' : ' value[squares]{braces}' ] | [' key_brackets_' : ' value_squares__braces_' ]
1246
+ [' unicode_λkey' : ' unicode_λvalue' ] | [' unicode__key' : ' unicode__value' ]
1247
+ and :
1248
+ // Multiple entries with mixed validity
1249
+ [' validKey' : ' validValue' , ' invalid#key' : ' invalid$value' , ' another.valid:key' : ' another+valid@value' ] |
1250
+ [' validKey' : ' validValue' , ' invalid_key' : ' invalid_value' , ' another.valid:key' : ' another+valid@value' ]
1251
+ and :
1252
+ // Entries that should be filtered out (null/empty after sanitization)
1253
+ [' validKey' : ' validValue' , ' ###' : ' $$$' , ' ' : ' %%%' , ' goodKey' : ' goodValue' ] |
1254
+ [' validKey' : ' validValue' , ' goodKey' : ' goodValue' ]
1255
+ and :
1256
+ // Null keys or values
1257
+ [' validKey' : null , null : ' validValue' , ' goodKey' : ' goodValue' ] |
1258
+ [' goodKey' : ' goodValue' ]
1259
+ and :
1260
+ // Real-world example with Nextflow resource labels
1261
+ [
1262
+ ' uniqueRunId' : ' tw-12345-workflow-run' ,
1263
+ ' taskHash' : ' task.hash.0x1a2b3c4d#special' ,
1264
+ ' pipelineUser' :
' [email protected] ' ,
1265
+ ' pipelineRunName' : ' my-pipeline-run(2024)' ,
1266
+ ' pipelineSessionId' : ' session#id$with%special&chars' ,
1267
+ ' pipelineResume' : ' false' ,
1268
+ ' pipelineName' : ' my_pipeline/name:version+tag'
1269
+ ] |
1270
+ [
1271
+ ' uniqueRunId' : ' tw-12345-workflow-run' ,
1272
+ ' taskHash' : ' task.hash.0x1a2b3c4d_special' ,
1273
+ ' pipelineUser' :
' [email protected] ' ,
1274
+ ' pipelineRunName' : ' my-pipeline-run_2024_' ,
1275
+ ' pipelineSessionId' : ' session_id_with_special_chars' ,
1276
+ ' pipelineResume' : ' false' ,
1277
+ ' pipelineName' : ' my_pipeline/name:version+tag'
1278
+ ]
1279
+ }
1280
+
1281
+ def ' should apply label sanitization in submit request' () {
1282
+ given :
1283
+ def task = Mock (TaskRun )
1284
+ task. getName() >> ' batch-task'
1285
+ task. getConfig() >> new TaskConfig (
1286
+ memory : ' 8GB' ,
1287
+ cpus : 4 ,
1288
+ resourceLabels : [
1289
+ ' validLabel' : ' validValue' ,
1290
+ ' invalid#key' : ' invalid$value' ,
1291
+ ' long-key-that-might-be-truncated-if-very-very-long' : ' long-value-that-should-be-truncated-because-it-exceeds-the-maximum-allowed-length-for-aws-batch-tags-which-is-256-characters-and-this-string-is-definitely-longer-than-that-limit-so-it-will-be-cut-off-at-the-appropriate-length-and-cleaned-up'
1292
+ ]
1293
+ )
1294
+
1295
+ def handler = Spy (AwsBatchTaskHandler )
1296
+
1297
+ when :
1298
+ def req = handler. newSubmitRequest(task)
1299
+ then :
1300
+ 1 * handler. getSubmitCommand() >> [' bash' , ' -c' , ' test' ]
1301
+ 1 * handler. maxSpotAttempts() >> 0
1302
+ 1 * handler. getAwsOptions() >> new AwsOptions ()
1303
+ 1 * handler. getJobQueue(task) >> ' test-queue'
1304
+ 1 * handler. getJobDefinition(task) >> ' test-job-def'
1305
+ 1 * handler. getEnvironmentVars() >> []
1306
+
1307
+ and :
1308
+ def tags = req. getTags()
1309
+ tags. size() == 3
1310
+ tags[' validLabel' ] == ' validValue'
1311
+ tags[' invalid_key' ] == ' invalid_value'
1312
+ // Check that long value was truncated
1313
+ tags[' long-key-that-might-be-truncated-if-very-very-long' ]. length() <= 256
1314
+ tags[' long-key-that-might-be-truncated-if-very-very-long' ]. startsWith(' long-value-that-should-be-truncated' )
1315
+ ! tags[' long-key-that-might-be-truncated-if-very-very-long' ]. endsWith(' _' )
1316
+ req. getPropagateTags() == true
1317
+ }
1162
1318
}
0 commit comments