Repository: compose-spec/compose-go
Pull request #486 (2023-11-08 15:51:55)

add support for build.ulimits
implement https://github.com/compose-spec/compose-spec/pull/433


Patch:
diff --git a/schema/compose-spec.json b/schema/compose-spec.json
index 463d682..54e3b89 100644
--- a/schema/compose-spec.json
+++ b/schema/compose-spec.json
@@ -120,6 +120,7 @@
                 "privileged": {"type": "boolean"},
                 "secrets": {"$ref": "#/definitions/service_config_or_secret"},
                 "tags": {"type": "array", "items": {"type": "string"}},
+                "ulimits": {"$ref": "#/definitions/ulimits"},
                 "platforms": {"type": "array", "items": {"type": "string"}}
               },
               "additionalProperties": false,
@@ -356,26 +357,7 @@
         "storage_opt": {"type": "object"},
         "tmpfs": {"$ref": "#/definitions/string_or_list"},
         "tty": {"type": "boolean"},
-        "ulimits": {
-          "type": "object",
-          "patternProperties": {
-            "^[a-z]+$": {
-              "oneOf": [
-                {"type": "integer"},
-                {
-                  "type": "object",
-                  "properties": {
-                    "hard": {"type": "integer"},
-                    "soft": {"type": "integer"}
-                  },
-                  "required": ["soft", "hard"],
-                  "additionalProperties": false,
-                  "patternProperties": {"^x-": {}}
-                }
-              ]
-            }
-          }
-        },
+        "ulimits": {"$ref": "#/definitions/ulimits"},
         "user": {"type": "string"},
         "uts": {"type": "string"},
         "userns_mode": {"type": "string"},
@@ -613,12 +595,12 @@
       "items": {
         "type": "object",
         "properties": {
-            "capabilities": {"$ref": "#/definitions/list_of_strings"},
-            "count": {"type": ["string", "integer"]},
-            "device_ids": {"$ref": "#/definitions/list_of_strings"},
-            "driver":{"type": "string"},
-            "options":{"$ref": "#/definitions/list_or_dict"}
-          },
+          "capabilities": {"$ref": "#/definitions/list_of_strings"},
+          "count": {"type": ["string", "integer"]},
+          "device_ids": {"$ref": "#/definitions/list_of_strings"},
+          "driver":{"type": "string"},
+          "options":{"$ref": "#/definitions/list_or_dict"}
+        },
         "additionalProperties": false,
         "patternProperties": {"^x-": {}}
       }
@@ -835,7 +817,6 @@
       },
       "additionalProperties": false
     },
-
     "service_config_or_secret": {
       "type": "array",
       "items": {
@@ -856,7 +837,26 @@
         ]
       }
     },
-
+    "ulimits": {
+      "type": "object",
+      "patternProperties": {
+        "^[a-z]+$": {
+          "oneOf": [
+            {"type": "integer"},
+            {
+              "type": "object",
+              "properties": {
+                "hard": {"type": "integer"},
+                "soft": {"type": "integer"}
+              },
+              "required": ["soft", "hard"],
+              "additionalProperties": false,
+              "patternProperties": {"^x-": {}}
+            }
+          ]
+        }
+      }
+    },
     "constraints": {
       "service": {
         "id": "#/definitions/constraints/service",
@@ -872,4 +872,4 @@
       }
     }
   }
-}
+}
\ No newline at end of file
diff --git a/transform/canonical.go b/transform/canonical.go
index 391317a..c80dd39 100644
--- a/transform/canonical.go
+++ b/transform/canonical.go
@@ -36,6 +36,7 @@ func init() {
 	transformers["services.*.build"] = transformBuild
 	transformers["services.*.build.ssh"] = transformSSH
 	transformers["services.*.ulimits.*"] = transformUlimits
+	transformers["services.*.build.ulimits.*"] = transformUlimits
 	transformers["volumes.*"] = transformMaybeExternal
 	transformers["networks.*"] = transformMaybeExternal
 	transformers["secrets.*"] = transformMaybeExternal
diff --git a/types/types.go b/types/types.go
index 83ca7b0..8884669 100644
--- a/types/types.go
+++ b/types/types.go
@@ -256,25 +256,26 @@ func (s set) toSlice() []string {
 
 // BuildConfig is a type for build
 type BuildConfig struct {
-	Context            string                `yaml:"context,omitempty" json:"context,omitempty"`
-	Dockerfile         string                `yaml:"dockerfile,omitempty" json:"dockerfile,omitempty"`
-	DockerfileInline   string                `yaml:"dockerfile_inline,omitempty" json:"dockerfile_inline,omitempty"`
-	Args               MappingWithEquals     `yaml:"args,omitempty" json:"args,omitempty"`
-	SSH                SSHConfig             `yaml:"ssh,omitempty" json:"ssh,omitempty"`
-	Labels             Labels                `yaml:"labels,omitempty" json:"labels,omitempty"`
-	CacheFrom          StringList            `yaml:"cache_from,omitempty" json:"cache_from,omitempty"`
-	CacheTo            StringList            `yaml:"cache_to,omitempty" json:"cache_to,omitempty"`
-	NoCache            bool                  `yaml:"no_cache,omitempty" json:"no_cache,omitempty"`
-	AdditionalContexts Mapping               `yaml:"additional_contexts,omitempty" json:"additional_contexts,omitempty"`
-	Pull               bool                  `yaml:"pull,omitempty" json:"pull,omitempty"`
-	ExtraHosts         HostsList             `yaml:"extra_hosts,omitempty" json:"extra_hosts,omitempty"`
-	Isolation          string                `yaml:"isolation,omitempty" json:"isolation,omitempty"`
-	Network            string                `yaml:"network,omitempty" json:"network,omitempty"`
-	Target             string                `yaml:"target,omitempty" json:"target,omitempty"`
-	Secrets            []ServiceSecretConfig `yaml:"secrets,omitempty" json:"secrets,omitempty"`
-	Tags               StringList            `yaml:"tags,omitempty" json:"tags,omitempty"`
-	Platforms          StringList            `yaml:"platforms,omitempty" json:"platforms,omitempty"`
-	Privileged         bool                  `yaml:"privileged,omitempty" json:"privileged,omitempty"`
+	Context            string                    `yaml:"context,omitempty" json:"context,omitempty"`
+	Dockerfile         string                    `yaml:"dockerfile,omitempty" json:"dockerfile,omitempty"`
+	DockerfileInline   string                    `yaml:"dockerfile_inline,omitempty" json:"dockerfile_inline,omitempty"`
+	Args               MappingWithEquals         `yaml:"args,omitempty" json:"args,omitempty"`
+	SSH                SSHConfig                 `yaml:"ssh,omitempty" json:"ssh,omitempty"`
+	Labels             Labels                    `yaml:"labels,omitempty" json:"labels,omitempty"`
+	CacheFrom          StringList                `yaml:"cache_from,omitempty" json:"cache_from,omitempty"`
+	CacheTo            StringList                `yaml:"cache_to,omitempty" json:"cache_to,omitempty"`
+	NoCache            bool                      `yaml:"no_cache,omitempty" json:"no_cache,omitempty"`
+	AdditionalContexts Mapping                   `yaml:"additional_contexts,omitempty" json:"additional_contexts,omitempty"`
+	Pull               bool                      `yaml:"pull,omitempty" json:"pull,omitempty"`
+	ExtraHosts         HostsList                 `yaml:"extra_hosts,omitempty" json:"extra_hosts,omitempty"`
+	Isolation          string                    `yaml:"isolation,omitempty" json:"isolation,omitempty"`
+	Network            string                    `yaml:"network,omitempty" json:"network,omitempty"`
+	Target             string                    `yaml:"target,omitempty" json:"target,omitempty"`
+	Secrets            []ServiceSecretConfig     `yaml:"secrets,omitempty" json:"secrets,omitempty"`
+	Tags               StringList                `yaml:"tags,omitempty" json:"tags,omitempty"`
+	Ulimits            map[string]*UlimitsConfig `yaml:"ulimits,omitempty" json:"ulimits,omitempty"`
+	Platforms          StringList                `yaml:"platforms,omitempty" json:"platforms,omitempty"`
+	Privileged         bool                      `yaml:"privileged,omitempty" json:"privileged,omitempty"`
 
 	Extensions Extensions `yaml:"#extensions,inline" json:"-"`
 }


Test patch:
diff --git a/loader/loader_test.go b/loader/loader_test.go
index 597679b..d50b338 100644
--- a/loader/loader_test.go
+++ b/loader/loader_test.go
@@ -2823,3 +2823,30 @@ services:
 	assert.Equal(t, project.Services[0].MemSwapLimit, types.UnitBytes(-1))
 	assert.Equal(t, project.Services[1].MemSwapLimit, types.UnitBytes(640*1024))
 }
+
+func TestBuildUlimits(t *testing.T) {
+	yaml := `
+name: test-build-ulimits
+services:
+  test:
+    build:
+      context: .
+      ulimits:
+        nproc: 65535
+        nofile:
+          soft: 20000
+          hard: 40000
+`
+	p, err := LoadWithContext(context.Background(), types.ConfigDetails{
+		ConfigFiles: []types.ConfigFile{
+			{
+				Content: []byte(yaml),
+			},
+		},
+	})
+	assert.NilError(t, err)
+	assert.DeepEqual(t, p.Services[0].Build.Ulimits, map[string]*types.UlimitsConfig{
+		"nproc":  {Single: 65535},
+		"nofile": {Soft: 20000, Hard: 40000},
+	})
+}