build: Add per-step CPU and memory resource limits

Port of moby/buildkit#6569 to buildx. Adds --memory, --memory-swap,
--cpu-shares, --cpu-period, --cpu-quota, --cpuset-cpus, and --cpuset-mems
flags to build, plus the equivalent bake target attributes and compose
x-bake fields.

Signed-off-by: Jiří Moravčík <jiri.moravcik@gmail.com>
This commit is contained in:
Jiří Moravčík
2026-06-08 15:39:14 +02:00
parent 4fbc1e5f2c
commit 80b2293881
14 changed files with 761 additions and 48 deletions
+107
View File
@@ -5,6 +5,7 @@ import (
"strings"
"testing"
dockeropts "github.com/docker/cli/opts"
"github.com/stretchr/testify/require"
)
@@ -150,3 +151,109 @@ func TestToBuildkitExtraHosts(t *testing.T) {
})
}
}
func TestAddResourceLimits(t *testing.T) {
mustMemSwap := func(v string) dockeropts.MemSwapBytes {
var m dockeropts.MemSwapBytes
require.NoError(t, m.Set(v))
return m
}
tests := []struct {
name string
limits ResourceLimits
expected map[string]string
}{
{
name: "empty",
limits: ResourceLimits{},
expected: map[string]string{},
},
{
name: "all",
limits: ResourceLimits{
Memory: dockeropts.MemBytes(2 * 1024 * 1024 * 1024),
MemorySwap: mustMemSwap("4g"),
CPUShares: 1024,
CPUPeriod: 100000,
CPUQuota: 50000,
CPUSetCPUs: "0-3",
CPUSetMems: "0,1",
},
expected: map[string]string{
"memory": "2147483648",
"memswap": "4294967296",
"cpushares": "1024",
"cpuperiod": "100000",
"cpuquota": "50000",
"cpusetcpus": "0-3",
"cpusetmems": "0,1",
},
},
{
name: "unlimited swap",
limits: ResourceLimits{MemorySwap: mustMemSwap("-1")},
expected: map[string]string{
"memswap": "-1",
},
},
{
name: "partial",
limits: ResourceLimits{Memory: dockeropts.MemBytes(512 * 1024 * 1024)},
expected: map[string]string{
"memory": "536870912",
},
},
}
for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
attrs := map[string]string{}
addResourceLimits(tc.limits, attrs)
require.Equal(t, tc.expected, attrs)
})
}
}
func TestParseResourceLimits(t *testing.T) {
t.Run("all", func(t *testing.T) {
rl, err := ParseResourceLimits([]string{
"memory=2g",
"memory-swap=4g",
"cpu-shares=1024",
"cpu-period=100000",
"cpu-quota=50000",
"cpuset-cpus=0-3",
"cpuset-mems=0,1",
})
require.NoError(t, err)
require.Equal(t, int64(2*1024*1024*1024), rl.Memory.Value())
require.Equal(t, int64(4*1024*1024*1024), rl.MemorySwap.Value())
require.Equal(t, int64(1024), rl.CPUShares)
require.Equal(t, int64(100000), rl.CPUPeriod)
require.Equal(t, int64(50000), rl.CPUQuota)
require.Equal(t, "0-3", rl.CPUSetCPUs)
require.Equal(t, "0,1", rl.CPUSetMems)
})
t.Run("unlimited swap", func(t *testing.T) {
rl, err := ParseResourceLimits([]string{"memory-swap=-1"})
require.NoError(t, err)
require.Equal(t, int64(-1), rl.MemorySwap.Value())
})
t.Run("missing value", func(t *testing.T) {
_, err := ParseResourceLimits([]string{"memory"})
require.Error(t, err)
})
t.Run("unknown key", func(t *testing.T) {
_, err := ParseResourceLimits([]string{"bogus=1"})
require.Error(t, err)
})
t.Run("invalid int", func(t *testing.T) {
_, err := ParseResourceLimits([]string{"cpu-shares=notanumber"})
require.Error(t, err)
})
}