1818package io.seqera.util
1919
2020import io.seqera.config.MachineRequirementOpts
21+ import io.seqera.sched.api.schema.v1a1.DiskAllocation
2122import io.seqera.sched.api.schema.v1a1.PriceModel as SchedPriceModel
2223import io.seqera.sched.api.schema.v1a1.ProvisioningModel
2324import nextflow.cloud.types.PriceModel
@@ -157,18 +158,23 @@ class MapperUtilTest extends Specification {
157158 when :
158159 def result = MapperUtil . toDiskRequirement(MemoryUnit . of(' 100 GB' ))
159160
160- then :
161+ then : ' disk size is set '
161162 result. sizeGiB == 100
162- result. type == MapperUtil . DEFAULT_DISK_TYPE
163+ and : ' allocation defaults to null (task allocation on API side)'
164+ result. allocation == null
165+ and : ' EBS defaults are applied for task allocation'
166+ result. volumeType == MapperUtil . DEFAULT_DISK_TYPE
163167 result. throughputMiBps == MapperUtil . DEFAULT_DISK_THROUGHPUT_MIBPS
168+ result. encrypted == false
169+ result. iops == null
164170 }
165171
166172 def ' should map disk size in different units' () {
167173 expect :
168174 MapperUtil . toDiskRequirement(MemoryUnit . of(' 1 TB' )). sizeGiB == 1024
169175 MapperUtil . toDiskRequirement(MemoryUnit . of(' 50 GB' )). sizeGiB == 50
170176 and : ' defaults are applied'
171- MapperUtil . toDiskRequirement(MemoryUnit . of(' 1 TB' )). type == MapperUtil . DEFAULT_DISK_TYPE
177+ MapperUtil . toDiskRequirement(MemoryUnit . of(' 1 TB' )). volumeType == MapperUtil . DEFAULT_DISK_TYPE
172178 MapperUtil . toDiskRequirement(MemoryUnit . of(' 1 TB' )). throughputMiBps == MapperUtil . DEFAULT_DISK_THROUGHPUT_MIBPS
173179 }
174180
@@ -226,7 +232,7 @@ class MapperUtilTest extends Specification {
226232
227233 then :
228234 result. sizeGiB == 100
229- result. type == ' ebs/io1'
235+ result. volumeType == ' ebs/io1'
230236 result. iops == 10000
231237 result. throughputMiBps == null // throughput only for gp3
232238 }
@@ -239,7 +245,7 @@ class MapperUtilTest extends Specification {
239245 def result = MapperUtil . toDiskRequirement(MemoryUnit . of(' 100 GB' ), opts)
240246
241247 then :
242- result. type == MapperUtil . DEFAULT_DISK_TYPE
248+ result. volumeType == MapperUtil . DEFAULT_DISK_TYPE
243249 result. throughputMiBps == 500
244250 }
245251
@@ -268,7 +274,7 @@ class MapperUtilTest extends Specification {
268274
269275 then :
270276 result. sizeGiB == 200
271- result. type == ' ebs/gp3'
277+ result. volumeType == ' ebs/gp3'
272278 result. throughputMiBps == 600
273279 result. iops == 8000
274280 result. encrypted == true
@@ -289,10 +295,160 @@ class MapperUtilTest extends Specification {
289295 then :
290296 result. arch == ' arm64'
291297 result. disk. sizeGiB == 500
292- result. disk. type == ' ebs/io2'
298+ result. disk. volumeType == ' ebs/io2'
293299 result. disk. iops == 15000
294300 result. disk. encrypted == true
295301 result. disk. throughputMiBps == null // io2 doesn't use throughput
296302 }
297303
304+ // tests for disk allocation mapping
305+
306+ def ' should map disk allocation' () {
307+ expect :
308+ MapperUtil . toDiskAllocation(null ) == null
309+ MapperUtil . toDiskAllocation(' task' ) == DiskAllocation . TASK
310+ MapperUtil . toDiskAllocation(' node' ) == DiskAllocation . NODE
311+ }
312+
313+ def ' should throw exception for invalid disk allocation' () {
314+ when :
315+ MapperUtil . toDiskAllocation(' invalid' )
316+
317+ then :
318+ thrown(IllegalArgumentException )
319+ }
320+
321+ def ' should use task disk allocation from config' () {
322+ given :
323+ def opts = new MachineRequirementOpts ([diskAllocation : ' task' ])
324+
325+ when :
326+ def result = MapperUtil . toDiskRequirement(MemoryUnit . of(' 100 GB' ), opts)
327+
328+ then :
329+ result. allocation == DiskAllocation . TASK
330+ }
331+
332+ def ' should use node disk allocation from config' () {
333+ given :
334+ def opts = new MachineRequirementOpts ([diskAllocation : ' node' ])
335+
336+ when :
337+ def result = MapperUtil . toDiskRequirement(MemoryUnit . of(' 100 GB' ), opts)
338+
339+ then :
340+ result. allocation == DiskAllocation . NODE
341+ result. sizeGiB == 100
342+ result. volumeType == null // node allocation doesn't set EBS options
343+ result. throughputMiBps == null
344+ result. iops == null
345+ result. encrypted == null
346+ }
347+
348+ def ' should default to null disk allocation when not specified' () {
349+ when :
350+ def result = MapperUtil . toDiskRequirement(MemoryUnit . of(' 100 GB' ))
351+
352+ then :
353+ result. allocation == null
354+ }
355+
356+ def ' should include disk allocation in machine requirement' () {
357+ given :
358+ def opts = new MachineRequirementOpts ([
359+ arch : ' x86_64' ,
360+ diskAllocation : ' node'
361+ ])
362+
363+ when :
364+ def result = MapperUtil . toMachineRequirement(opts, null , MemoryUnit . of(' 200 GB' ))
365+
366+ then :
367+ result. arch == ' x86_64'
368+ result. disk. sizeGiB == 200
369+ result. disk. allocation == DiskAllocation . NODE
370+ }
371+
372+ // tests for node allocation validation
373+
374+ def ' should throw error when diskType is set with node allocation' () {
375+ given :
376+ def opts = new MachineRequirementOpts ([
377+ diskAllocation : ' node' ,
378+ diskType : ' ebs/gp3'
379+ ])
380+
381+ when :
382+ MapperUtil . toDiskRequirement(MemoryUnit . of(' 100 GB' ), opts)
383+
384+ then :
385+ def e = thrown(IllegalArgumentException )
386+ e. message. contains(' diskType' )
387+ e. message. contains(' node' )
388+ }
389+
390+ def ' should throw error when diskIops is set with node allocation' () {
391+ given :
392+ def opts = new MachineRequirementOpts ([
393+ diskAllocation : ' node' ,
394+ diskIops : 10000
395+ ])
396+
397+ when :
398+ MapperUtil . toDiskRequirement(MemoryUnit . of(' 100 GB' ), opts)
399+
400+ then :
401+ def e = thrown(IllegalArgumentException )
402+ e. message. contains(' diskIops' )
403+ }
404+
405+ def ' should throw error when diskThroughputMiBps is set with node allocation' () {
406+ given :
407+ def opts = new MachineRequirementOpts ([
408+ diskAllocation : ' node' ,
409+ diskThroughputMiBps : 500
410+ ])
411+
412+ when :
413+ MapperUtil . toDiskRequirement(MemoryUnit . of(' 100 GB' ), opts)
414+
415+ then :
416+ def e = thrown(IllegalArgumentException )
417+ e. message. contains(' diskThroughputMiBps' )
418+ }
419+
420+ def ' should throw error when diskEncrypted is set with node allocation' () {
421+ given :
422+ def opts = new MachineRequirementOpts ([
423+ diskAllocation : ' node' ,
424+ diskEncrypted : true
425+ ])
426+
427+ when :
428+ MapperUtil . toDiskRequirement(MemoryUnit . of(' 100 GB' ), opts)
429+
430+ then :
431+ def e = thrown(IllegalArgumentException )
432+ e. message. contains(' diskEncrypted' )
433+ }
434+
435+ def ' should report all invalid options with node allocation' () {
436+ given :
437+ def opts = new MachineRequirementOpts ([
438+ diskAllocation : ' node' ,
439+ diskType : ' ebs/io1' ,
440+ diskIops : 10000 ,
441+ diskEncrypted : true
442+ ])
443+
444+ when :
445+ MapperUtil . toDiskRequirement(MemoryUnit . of(' 100 GB' ), opts)
446+
447+ then :
448+ def e = thrown(IllegalArgumentException )
449+ e. message. contains(' diskType' )
450+ e. message. contains(' diskIops' )
451+ e. message. contains(' diskEncrypted' )
452+ }
453+
298454}
0 commit comments