Repository: compose-spec/compose-go
Pull request #442 (2023-08-04 13:53:02)

Parse service device count to int if possible
When set via an environment variable an integer will be represented by a string, resulting in an error.
fixes: https://github.com/docker/compose/issues/10667

--- 

The alternative would be to adjust the parsing of the yaml into the internal model, so it gets parsed as an int. 
The env var is replaced in https://github.com/compose-spec/compose-go/blob/95ac1be8bfdc4dc21cdfb14e37b52c31275046a3/interpolation/interpolation.go#L74
 But it only allows setting strings and not int
https://github.com/compose-spec/compose-go/blob/95ac1be8bfdc4dc21cdfb14e37b52c31275046a3/interpolation/interpolation.go#L34


Patch:
diff --git a/loader/loader.go b/loader/loader.go
index 76dbe2f..8228346 100644
--- a/loader/loader.go
+++ b/loader/loader.go
@@ -1038,7 +1038,12 @@ var transformServiceDeviceRequest TransformerFunc = func(data interface{}) (inte
 					value["count"] = -1
 					return value, nil
 				}
-				return data, errors.Errorf("invalid string value for 'count' (the only value allowed is 'all')")
+				i, err := strconv.ParseInt(val, 10, 64)
+				if err == nil {
+					value["count"] = i
+					return value, nil
+				}
+				return data, errors.Errorf("invalid string value for 'count' (the only value allowed is 'all' or a number)")
 			default:
 				return data, errors.Errorf("invalid type %T for device count", val)
 			}


Test patch:
diff --git a/loader/loader_test.go b/loader/loader_test.go
index 0a35d68..234378c 100644
--- a/loader/loader_test.go
+++ b/loader/loader_test.go
@@ -2044,7 +2044,24 @@ func TestLoadWithExtendsWithContextUrl(t *testing.T) {
 	assert.Check(t, is.DeepEqual(expServices, actual.Services))
 }
 
-func TestServiceDeviceRequestCount(t *testing.T) {
+func TestServiceDeviceRequestCountIntegerType(t *testing.T) {
+	_, err := loadYAML(`
+name: service-device-request-count
+services:
+  hello-world:
+    image: redis:alpine
+    deploy:
+      resources:
+        reservations:
+          devices:
+            - driver: nvidia
+              capabilities: [gpu]
+              count: 1
+`)
+	assert.NilError(t, err)
+}
+
+func TestServiceDeviceRequestCountStringType(t *testing.T) {
 	_, err := loadYAML(`
 name: service-device-request-count
 services:
@@ -2061,7 +2078,24 @@ services:
 	assert.NilError(t, err)
 }
 
-func TestServiceDeviceRequestCountType(t *testing.T) {
+func TestServiceDeviceRequestCountIntegerAsStringType(t *testing.T) {
+	_, err := loadYAML(`
+name: service-device-request-count-type
+services:
+  hello-world:
+    image: redis:alpine
+    deploy:
+      resources:
+        reservations:
+          devices:
+            - driver: nvidia
+              capabilities: [gpu]
+              count: "1"
+`)
+	assert.NilError(t, err)
+}
+
+func TestServiceDeviceRequestCountInvalidStringType(t *testing.T) {
 	_, err := loadYAML(`
 name: service-device-request-count-type
 services:
@@ -2075,7 +2109,7 @@ services:
               capabilities: [gpu]
               count: somestring
 `)
-	assert.ErrorContains(t, err, "invalid string value for 'count' (the only value allowed is 'all')")
+	assert.ErrorContains(t, err, "invalid string value for 'count' (the only value allowed is 'all' or a number)")
 }
 
 func TestServicePullPolicy(t *testing.T) {