diff --git a/lib-cloudinfo/README.md b/lib-cloudinfo/README.md index e18c7ed..3d2ed7c 100644 --- a/lib-cloudinfo/README.md +++ b/lib-cloudinfo/README.md @@ -10,7 +10,7 @@ Add the dependency to your `build.gradle`: ```gradle dependencies { - implementation 'io.seqera:lib-cloudinfo:1.2.2' + implementation 'io.seqera:lib-cloudinfo:2.0.0' } ``` diff --git a/lib-cloudinfo/VERSION b/lib-cloudinfo/VERSION index 23aa839..227cea2 100644 --- a/lib-cloudinfo/VERSION +++ b/lib-cloudinfo/VERSION @@ -1 +1 @@ -1.2.2 +2.0.0 diff --git a/lib-cloudinfo/changelog.txt b/lib-cloudinfo/changelog.txt index 7221586..e84dd88 100644 --- a/lib-cloudinfo/changelog.txt +++ b/lib-cloudinfo/changelog.txt @@ -1,5 +1,8 @@ # lib-cloudinfo changelog +2.0.0 - 24 Jul 2026 +- BREAKING: type CloudProduct.gpusPerVm as Float instead of Integer. CloudInfo reports fractional GPU counts for fractional-GPU instance families (e.g. AWS g6f.2xlarge reports gpusPerVm=0.25), which the Integer field truncated to 0, making a real GPU instance indistinguishable from a CPU-only one for any `gpusPerVm > 0` check. Consumers that auto-unbox getGpusPerVm() to int (notably sched) must consume Float and treat `> 0` as GPU. Whole counts now serialise as 8.0 instead of 8 (PR #98, closes #97). + 1.2.2 - 6 Jul 2026 - Add AcceleratorFeatures: provider-agnostic helpers over the accelerator-related CloudProduct.features tokens (hasGpu, isNvidia, hasFpga) plus the token constants (gpu, nvidia, amd, habana, fpga, xilinx). Centralises classification of CloudInfo-returned accelerator data with the model instead of duplicating it in consumers. diff --git a/lib-cloudinfo/src/main/java/io/seqera/cloudinfo/api/CloudProduct.java b/lib-cloudinfo/src/main/java/io/seqera/cloudinfo/api/CloudProduct.java index 5338927..33aef52 100644 --- a/lib-cloudinfo/src/main/java/io/seqera/cloudinfo/api/CloudProduct.java +++ b/lib-cloudinfo/src/main/java/io/seqera/cloudinfo/api/CloudProduct.java @@ -37,7 +37,16 @@ public class CloudProduct { private String category; private Integer cpusPerVm; private Float memPerVm; - private Integer gpusPerVm; + /** + * Number of GPUs (accelerators) per instance, as reported by CloudInfo. + * Modelled as a {@link Float} because fractional-GPU families report a + * fraction of a physical accelerator (e.g. AWS {@code g6f.2xlarge} reports + * {@code 0.25}); an integer field would truncate such values to {@code 0} + * and misclassify the instance as CPU-only. A value {@code > 0} (including + * a fraction) means the instance carries an accelerator. Null on legacy + * responses that do not populate it. + */ + private Float gpusPerVm; private Boolean currentGen; private String ntwPerf; private String ntwPerfCategory; @@ -101,11 +110,11 @@ public void setMemPerVm(Float memPerVm) { this.memPerVm = memPerVm; } - public Integer getGpusPerVm() { + public Float getGpusPerVm() { return gpusPerVm; } - public void setGpusPerVm(Integer gpusPerVm) { + public void setGpusPerVm(Float gpusPerVm) { this.gpusPerVm = gpusPerVm; } diff --git a/lib-cloudinfo/src/test/groovy/io/seqera/cloudinfo/api/CloudProductTest.groovy b/lib-cloudinfo/src/test/groovy/io/seqera/cloudinfo/api/CloudProductTest.groovy index 9f2049c..8e14357 100644 --- a/lib-cloudinfo/src/test/groovy/io/seqera/cloudinfo/api/CloudProductTest.groovy +++ b/lib-cloudinfo/src/test/groovy/io/seqera/cloudinfo/api/CloudProductTest.groovy @@ -42,6 +42,35 @@ class CloudProductTest extends Specification { decoded.features == ['a100', 'gpu', 'nvidia', 'sched', 'ssd', 'x86'] } + def 'fractional gpusPerVm survives Jackson decode (does not truncate to 0)'() { + given: 'a GPU instance reporting a fractional accelerator, e.g. AWS g6f.2xlarge' + def json = '{"type":"g6f.2xlarge","category":"GPU instance","cpusPerVm":8,"gpusPerVm":0.25}' + + when: + def decoded = ENCODER.decode(json) + + then: + decoded.type == 'g6f.2xlarge' + decoded.gpusPerVm == 0.25f + decoded.gpusPerVm > 0 + } + + def 'whole and absent gpusPerVm decode correctly'() { + expect: + ENCODER.decode('{"type":"p4d.24xlarge","gpusPerVm":8}').gpusPerVm == 8.0f + ENCODER.decode('{"type":"m5.large","cpusPerVm":2}').gpusPerVm == null + } + + def 'whole gpusPerVm now serialises as a float (8.0, not 8)'() { + given: + def product = new CloudProduct() + product.type = 'p4d.24xlarge' + product.gpusPerVm = 8.0f + + expect: + ENCODER.encode(product).contains('"gpusPerVm":8.0') + } + def 'features field defaults to null on a freshly-constructed product'() { when: def product = new CloudProduct()