mirror of
https://github.com/moby/buildkit.git
synced 2026-11-06 05:16:45 +00:00
solver: add per-step CPU and memory resource limits
Add support for setting cgroup resource limits (memory, memory-swap, cpu-shares, cpu-period, cpu-quota, cpuset-cpus, cpuset-mems) on individual build steps. Signed-off-by: Jiří Moravčík <jiri.moravcik@gmail.com>
This commit is contained in:
30 files changed
+1863
-175
No files matched your search
@@ -157,6 +157,7 @@ var allTests = []func(t *testing.T, sb integration.Sandbox){
|
||||
testShmSize,
|
||||
testUlimit,
|
||||
testCgroupParent,
|
||||
testLinuxResources,
|
||||
testNetworkMode,
|
||||
testFrontendMetadataReturn,
|
||||
testFrontendUseSolveResults,
|
||||
@@ -1336,6 +1337,62 @@ func testCgroupParent(t *testing.T, sb integration.Sandbox) {
|
||||
require.Equal(t, "", strings.TrimSpace(string(dt)))
|
||||
}
|
||||
|
||||
func testLinuxResources(t *testing.T, sb integration.Sandbox) {
|
||||
integration.SkipOnPlatform(t, "windows")
|
||||
if sb.Rootless() {
|
||||
t.SkipNow()
|
||||
}
|
||||
|
||||
if _, err := os.Lstat("/sys/fs/cgroup/cgroup.subtree_control"); os.IsNotExist(err) {
|
||||
t.Skipf("test requires cgroup v2")
|
||||
}
|
||||
|
||||
c, err := New(sb.Context(), sb.Address())
|
||||
require.NoError(t, err)
|
||||
defer c.Close()
|
||||
|
||||
img := llb.Image("alpine:latest")
|
||||
st := llb.Scratch()
|
||||
|
||||
run := func(cmd string, ro ...llb.RunOption) {
|
||||
st = img.Run(append(ro, llb.Shlex(cmd), llb.Dir("/wd"))...).AddMount("/wd", st)
|
||||
}
|
||||
|
||||
// Test memory limit: set 64MiB and verify via cgroup
|
||||
run(`sh -c "cat /sys/fs/cgroup/memory.max > mem_limited"`, llb.MemoryLimit(64*1024*1024))
|
||||
run(`sh -c "cat /sys/fs/cgroup/memory.max > mem_default"`)
|
||||
|
||||
// Test CPU quota: set quota=50000 period=100000 (50% CPU) and verify
|
||||
run(`sh -c "cat /sys/fs/cgroup/cpu.max > cpu_limited"`, llb.CPUQuota(50000), llb.CPUPeriod(100000))
|
||||
|
||||
def, err := st.Marshal(sb.Context())
|
||||
require.NoError(t, err)
|
||||
|
||||
destDir := t.TempDir()
|
||||
|
||||
_, err = c.Solve(sb.Context(), def, SolveOpt{
|
||||
Exports: []ExportEntry{
|
||||
{
|
||||
Type: ExporterLocal,
|
||||
OutputDir: destDir,
|
||||
},
|
||||
},
|
||||
}, nil)
|
||||
require.NoError(t, err)
|
||||
|
||||
dt, err := os.ReadFile(filepath.Join(destDir, "mem_limited"))
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, "67108864", strings.TrimSpace(string(dt)))
|
||||
|
||||
dt2, err := os.ReadFile(filepath.Join(destDir, "mem_default"))
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, "max", strings.TrimSpace(string(dt2)))
|
||||
|
||||
dt3, err := os.ReadFile(filepath.Join(destDir, "cpu_limited"))
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, "50000 100000", strings.TrimSpace(string(dt3)))
|
||||
}
|
||||
|
||||
func testNetworkMode(t *testing.T, sb integration.Sandbox) {
|
||||
integration.SkipOnPlatform(t, "windows")
|
||||
c, err := New(sb.Context(), sb.Address())
|
||||
|
||||
@@ -255,6 +255,10 @@ func (e *ExecOp) Marshal(ctx context.Context, c *Constraints) (digest.Digest, []
|
||||
meta.Ulimit = ul
|
||||
}
|
||||
|
||||
if e.constraints.Metadata.LinuxResources != nil {
|
||||
addCap(&e.constraints, pb.CapExecMetaLinuxResources)
|
||||
}
|
||||
|
||||
network, err := getNetwork(e.base)(ctx, c)
|
||||
if err != nil {
|
||||
return "", nil, nil, nil, err
|
||||
|
||||
@@ -51,6 +51,97 @@ func TestValidGetMountIndex(t *testing.T) {
|
||||
require.Equal(t, pb.OutputIndex(1), mountIndex, "unexpected mount index")
|
||||
}
|
||||
|
||||
func TestLinuxResourcesMarshal(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
st := Image("busybox:latest").
|
||||
Run(
|
||||
Shlex("true"),
|
||||
MemoryLimit(64*1024*1024),
|
||||
CPUShares(512),
|
||||
CPUQuota(50000),
|
||||
CPUPeriod(100000),
|
||||
CpusetCpus("0-3"),
|
||||
CpusetMems("0"),
|
||||
).Root()
|
||||
|
||||
def, err := st.Marshal(context.TODO())
|
||||
require.NoError(t, err)
|
||||
|
||||
// Resources should be in OpMetadata (not in the Op bytes / cache key)
|
||||
var found bool
|
||||
for _, md := range def.Metadata {
|
||||
if md.LinuxResources == nil {
|
||||
continue
|
||||
}
|
||||
found = true
|
||||
res := md.LinuxResources
|
||||
require.Equal(t, int64(64*1024*1024), res.Memory)
|
||||
require.Equal(t, uint64(512), res.CpuShares)
|
||||
require.Equal(t, int64(50000), res.CpuQuota)
|
||||
require.Equal(t, uint64(100000), res.CpuPeriod)
|
||||
require.Equal(t, "0-3", res.CpusetCpus)
|
||||
require.Equal(t, "0", res.CpusetMems)
|
||||
}
|
||||
require.True(t, found, "LinuxResources not found in OpMetadata")
|
||||
}
|
||||
|
||||
func TestLinuxResourcesNotInCacheKey(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
// Two ops with same command but different resource limits must produce the same digest
|
||||
st1 := Image("busybox:latest").
|
||||
Run(Shlex("echo hello"), MemoryLimit(64*1024*1024)).Root()
|
||||
|
||||
st2 := Image("busybox:latest").
|
||||
Run(Shlex("echo hello"), MemoryLimit(128*1024*1024)).Root()
|
||||
|
||||
st3 := Image("busybox:latest").
|
||||
Run(Shlex("echo hello")).Root()
|
||||
|
||||
def1, err := st1.Marshal(context.TODO())
|
||||
require.NoError(t, err)
|
||||
|
||||
def2, err := st2.Marshal(context.TODO())
|
||||
require.NoError(t, err)
|
||||
|
||||
def3, err := st3.Marshal(context.TODO())
|
||||
require.NoError(t, err)
|
||||
|
||||
// All three should produce the same definition bytes (same cache key)
|
||||
require.Equal(t, def1.Def, def2.Def, "different resource limits should produce same digest")
|
||||
require.Equal(t, def1.Def, def3.Def, "resource limits vs no limits should produce same digest")
|
||||
}
|
||||
|
||||
func TestLinuxResourcesMerge(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
// Test that individual resource limit functions merge correctly
|
||||
st := Image("busybox:latest").
|
||||
Run(
|
||||
Shlex("true"),
|
||||
MemoryLimit(64*1024*1024),
|
||||
CPUShares(512),
|
||||
).Root()
|
||||
|
||||
def, err := st.Marshal(context.TODO())
|
||||
require.NoError(t, err)
|
||||
|
||||
for _, md := range def.Metadata {
|
||||
if md.LinuxResources == nil {
|
||||
continue
|
||||
}
|
||||
res := md.LinuxResources
|
||||
require.Equal(t, int64(64*1024*1024), res.Memory)
|
||||
require.Equal(t, uint64(512), res.CpuShares)
|
||||
// Unset fields should be zero
|
||||
require.Equal(t, int64(0), res.CpuQuota)
|
||||
require.Equal(t, uint64(0), res.CpuPeriod)
|
||||
return
|
||||
}
|
||||
t.Fatal("LinuxResources not found in OpMetadata")
|
||||
}
|
||||
|
||||
func TestExecOpMarshalConsistency(t *testing.T) {
|
||||
var prevDef [][]byte
|
||||
st := Image("busybox:latest").
|
||||
|
||||
@@ -324,6 +324,17 @@ func getCgroupParent(s State) func(context.Context, *Constraints) (string, error
|
||||
}
|
||||
}
|
||||
|
||||
// LinuxResources holds CPU and memory resource limits for containers.
|
||||
type LinuxResources struct {
|
||||
Memory int64
|
||||
MemorySwap int64
|
||||
CPUShares uint64
|
||||
CPUPeriod uint64
|
||||
CPUQuota int64
|
||||
CpusetCpus string
|
||||
CpusetMems string
|
||||
}
|
||||
|
||||
// Network returns a [StateOption] which sets the network mode used for containers created by [State.Run].
|
||||
// This is the equivalent of [State.Network]
|
||||
// See [State.With] for where to use this.
|
||||
|
||||
+82
-10
@@ -591,6 +591,10 @@ func mergeMetadata(m1, m2 OpMetadata) OpMetadata {
|
||||
m1.ProgressGroup = m2.ProgressGroup
|
||||
}
|
||||
|
||||
if m2.LinuxResources != nil {
|
||||
m1.LinuxResources = m2.LinuxResources
|
||||
}
|
||||
|
||||
return m1
|
||||
}
|
||||
|
||||
@@ -668,11 +672,12 @@ type Constraints struct {
|
||||
|
||||
// OpMetadata has a more friendly interface for pb.OpMetadata.
|
||||
type OpMetadata struct {
|
||||
IgnoreCache bool `json:"ignore_cache,omitempty"`
|
||||
Description map[string]string `json:"description,omitempty"`
|
||||
ExportCache *pb.ExportCache `json:"export_cache,omitempty"`
|
||||
Caps map[apicaps.CapID]bool `json:"caps,omitempty"`
|
||||
ProgressGroup *pb.ProgressGroup `json:"progress_group,omitempty"`
|
||||
IgnoreCache bool `json:"ignore_cache,omitempty"`
|
||||
Description map[string]string `json:"description,omitempty"`
|
||||
ExportCache *pb.ExportCache `json:"export_cache,omitempty"`
|
||||
Caps map[apicaps.CapID]bool `json:"caps,omitempty"`
|
||||
ProgressGroup *pb.ProgressGroup `json:"progress_group,omitempty"`
|
||||
LinuxResources *pb.LinuxResources `json:"linux_resources,omitempty"`
|
||||
}
|
||||
|
||||
func NewOpMetadata(mpb *pb.OpMetadata) OpMetadata {
|
||||
@@ -687,11 +692,12 @@ func (m OpMetadata) ToPB() *pb.OpMetadata {
|
||||
caps[string(k)] = v
|
||||
}
|
||||
return &pb.OpMetadata{
|
||||
IgnoreCache: m.IgnoreCache,
|
||||
Description: m.Description,
|
||||
ExportCache: m.ExportCache,
|
||||
Caps: caps,
|
||||
ProgressGroup: m.ProgressGroup,
|
||||
IgnoreCache: m.IgnoreCache,
|
||||
Description: m.Description,
|
||||
ExportCache: m.ExportCache,
|
||||
Caps: caps,
|
||||
ProgressGroup: m.ProgressGroup,
|
||||
LinuxResources: m.LinuxResources,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -712,6 +718,7 @@ func (m *OpMetadata) FromPB(mpb *pb.OpMetadata) {
|
||||
m.Caps = nil
|
||||
}
|
||||
m.ProgressGroup = mpb.ProgressGroup
|
||||
m.LinuxResources = mpb.LinuxResources
|
||||
}
|
||||
|
||||
func Platform(p ocispecs.Platform) ConstraintsOpt {
|
||||
@@ -732,6 +739,71 @@ func ProgressGroup(id, name string, weak bool) ConstraintsOpt {
|
||||
})
|
||||
}
|
||||
|
||||
// WithLinuxResources sets all CPU/memory resource limits at once.
|
||||
// Resource limits are applied via OpMetadata and do not affect the cache key.
|
||||
func WithLinuxResources(res LinuxResources) ConstraintsOpt {
|
||||
return constraintsOptFunc(func(c *Constraints) {
|
||||
c.Metadata.LinuxResources = &pb.LinuxResources{
|
||||
Memory: res.Memory,
|
||||
MemorySwap: res.MemorySwap,
|
||||
CpuShares: res.CPUShares,
|
||||
CpuPeriod: res.CPUPeriod,
|
||||
CpuQuota: res.CPUQuota,
|
||||
CpusetCpus: res.CpusetCpus,
|
||||
CpusetMems: res.CpusetMems,
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func ensureLinuxResources(c *Constraints) *pb.LinuxResources {
|
||||
if c.Metadata.LinuxResources == nil {
|
||||
c.Metadata.LinuxResources = &pb.LinuxResources{}
|
||||
}
|
||||
return c.Metadata.LinuxResources
|
||||
}
|
||||
|
||||
func MemoryLimit(limit int64) ConstraintsOpt {
|
||||
return constraintsOptFunc(func(c *Constraints) {
|
||||
ensureLinuxResources(c).Memory = limit
|
||||
})
|
||||
}
|
||||
|
||||
func MemorySwapLimit(limit int64) ConstraintsOpt {
|
||||
return constraintsOptFunc(func(c *Constraints) {
|
||||
ensureLinuxResources(c).MemorySwap = limit
|
||||
})
|
||||
}
|
||||
|
||||
func CPUShares(shares uint64) ConstraintsOpt {
|
||||
return constraintsOptFunc(func(c *Constraints) {
|
||||
ensureLinuxResources(c).CpuShares = shares
|
||||
})
|
||||
}
|
||||
|
||||
func CPUPeriod(period uint64) ConstraintsOpt {
|
||||
return constraintsOptFunc(func(c *Constraints) {
|
||||
ensureLinuxResources(c).CpuPeriod = period
|
||||
})
|
||||
}
|
||||
|
||||
func CPUQuota(quota int64) ConstraintsOpt {
|
||||
return constraintsOptFunc(func(c *Constraints) {
|
||||
ensureLinuxResources(c).CpuQuota = quota
|
||||
})
|
||||
}
|
||||
|
||||
func CpusetCpus(cpus string) ConstraintsOpt {
|
||||
return constraintsOptFunc(func(c *Constraints) {
|
||||
ensureLinuxResources(c).CpusetCpus = cpus
|
||||
})
|
||||
}
|
||||
|
||||
func CpusetMems(mems string) ConstraintsOpt {
|
||||
return constraintsOptFunc(func(c *Constraints) {
|
||||
ensureLinuxResources(c).CpusetMems = mems
|
||||
})
|
||||
}
|
||||
|
||||
var (
|
||||
LinuxAmd64 = Platform(ocispecs.Platform{OS: "linux", Architecture: "amd64"})
|
||||
LinuxArmhf = Platform(ocispecs.Platform{OS: "linux", Architecture: "arm", Variant: "v7"})
|
||||
|
||||
+37
-5
@@ -85,9 +85,10 @@ should never have the same digest.
|
||||
|
||||
Options contain extra information that can be associated with the vertex but
|
||||
what doesn't change the definition(or equality check) of it. Normally this is
|
||||
either a hint to the solver, for example, to ignore cache when executing. It
|
||||
can also be used for associating messages with the vertex that can be helpful
|
||||
for tracing purposes.
|
||||
either a hint to the solver, for example, to ignore cache when executing, or
|
||||
runtime parameters like Linux resource limits (memory, CPU) that affect
|
||||
execution but not the cache key. It can also be used for associating messages
|
||||
with the vertex that can be helpful for tracing purposes.
|
||||
|
||||
## Operation interface
|
||||
|
||||
@@ -150,13 +151,13 @@ for its inputs.
|
||||
- A content-based cache function allows computing a new cache key for an input
|
||||
after it has completed. In LLB this is used for calculating a cache key based
|
||||
on the checksum of file contents of the input snapshots.
|
||||
|
||||
|
||||
> [!NOTE]
|
||||
> For example, in the case of LLB, if a vertex is a FileOp that copies a file
|
||||
> from one snapshot to another, the selector can be set to the path of the
|
||||
> source file in the input snapshot, while the content-based cache function can
|
||||
> be used to calculate the checksum of the file contents.
|
||||
>
|
||||
>
|
||||
> If the source path changes, we need to invalidate the cache (which we do by
|
||||
> changing the selector). However, if we do a content-based cache lookup for
|
||||
> the input, then the file content may not have changed (which we can detect by
|
||||
@@ -183,6 +184,37 @@ released if no more references remain.
|
||||
Loading a vertex also creates a progress writer associated with it and sets up
|
||||
the cache sources associated with the specific vertex.
|
||||
|
||||
### Linux resource limits
|
||||
|
||||
Linux resource limits (memory, CPU, cpuset) attached to a vertex via
|
||||
`VertexOptions` get merged into the shared state using a "most relaxed wins"
|
||||
policy. The merge is per resource. Scalar limits like `memory` and `cpu-shares`
|
||||
take the larger value (with `0` treated as unlimited where the field's
|
||||
semantics call for it). `cpu-period` and `cpu-quota` are picked as a pair
|
||||
representing the higher effective CPU cap. `cpuset-cpus` and `cpuset-mems` are
|
||||
unioned. Concurrent builds that share an op but specify different limits can
|
||||
dedup safely this way. The merged value is snapshotted when the shared op is
|
||||
first resolved (via `ResolveOp`) and passed straight to the op constructor.
|
||||
Jobs that join after that reuse the already-resolved op and don't change its
|
||||
effective limits.
|
||||
|
||||
#### Caveats
|
||||
|
||||
- **Merging is best-effort.** When two jobs share a vertex with different
|
||||
limits, what actually applies depends on whether both loads finish before
|
||||
the scheduler resolves the shared op. If they do, the merged value wins.
|
||||
If one job races ahead and triggers `ResolveOp` first, only that job's
|
||||
metadata is used; later joiners still merge into shared state, but nothing
|
||||
reads it after `ResolveOp` has run. Because of that, every `Merge`
|
||||
implementation must return a value that is safe for any participating job
|
||||
on its own. "Most relaxed" satisfies this since every job ends up with at
|
||||
least the limits it asked for.
|
||||
- **No effect on the cache key.** `LinuxResources` sits in `OpMetadata`, not
|
||||
in the op protobuf, so it doesn't enter the vertex digest. Two jobs that
|
||||
differ only in resource limits dedup to the same vertex and share cache
|
||||
entries.
|
||||
- **Needs write access to cgroups.** When buildkitd doesn't have write access to cgroups, it cannot set the limits. This means the limits won't work in rootless mode.
|
||||
|
||||
After vertexes have been loaded to the job, it is safe to request a result from
|
||||
an edge pointing to a previously loaded vertex. To do this `build(ctx, Edge)
|
||||
(CachedResult, error)` method is called on the static scheduler instance
|
||||
|
||||
@@ -24,6 +24,7 @@ type Meta struct {
|
||||
Ulimit []*pb.Ulimit
|
||||
CDIDevices []*pb.CDIDevice
|
||||
CgroupParent string
|
||||
LinuxResources *pb.LinuxResources
|
||||
NetMode pb.NetMode
|
||||
SecurityMode pb.SecurityMode
|
||||
ValidExitCodes []int
|
||||
|
||||
@@ -112,6 +112,12 @@ func GenerateSpec(ctx context.Context, meta executor.Meta, mounts []executor.Mou
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
if linuxResOpts, err := generateLinuxResourceOpts(meta.LinuxResources); err == nil {
|
||||
opts = append(opts, linuxResOpts...)
|
||||
} else {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
hostname := defaultHostname
|
||||
if meta.Hostname != "" {
|
||||
hostname = meta.Hostname
|
||||
|
||||
@@ -64,6 +64,13 @@ func sub(m mount.Mount, subPath string) (mount.Mount, func() error, error) {
|
||||
return m, func() error { return nil }, nil
|
||||
}
|
||||
|
||||
func generateLinuxResourceOpts(res *pb.LinuxResources) ([]oci.SpecOpts, error) {
|
||||
if res == nil {
|
||||
return nil, nil
|
||||
}
|
||||
return nil, errors.New("no support for Linux resource limits on Darwin")
|
||||
}
|
||||
|
||||
func generateCDIOpts(_ *cdidevices.Manager, devices []*pb.CDIDevice) ([]oci.SpecOpts, error) {
|
||||
if len(devices) == 0 {
|
||||
return nil, nil
|
||||
|
||||
@@ -72,6 +72,13 @@ func sub(m mount.Mount, subPath string) (mount.Mount, func() error, error) {
|
||||
return m, func() error { return nil }, nil
|
||||
}
|
||||
|
||||
func generateLinuxResourceOpts(res *pb.LinuxResources) ([]oci.SpecOpts, error) {
|
||||
if res == nil {
|
||||
return nil, nil
|
||||
}
|
||||
return nil, errors.New("no support for Linux resource limits on FreeBSD")
|
||||
}
|
||||
|
||||
func generateCDIOpts(_ *cdidevices.Manager, devices []*pb.CDIDevice) ([]oci.SpecOpts, error) {
|
||||
if len(devices) == 0 {
|
||||
return nil, nil
|
||||
|
||||
@@ -150,6 +150,45 @@ func generateRlimitOpts(ulimits []*pb.Ulimit) ([]oci.SpecOpts, error) {
|
||||
}, nil
|
||||
}
|
||||
|
||||
func generateLinuxResourceOpts(res *pb.LinuxResources) ([]oci.SpecOpts, error) {
|
||||
if res == nil {
|
||||
return nil, nil
|
||||
}
|
||||
var opts []oci.SpecOpts
|
||||
if res.Memory != 0 {
|
||||
opts = append(opts, oci.WithMemoryLimit(uint64(res.Memory)))
|
||||
}
|
||||
if res.MemorySwap != 0 {
|
||||
swap := res.MemorySwap
|
||||
opts = append(opts, func(_ context.Context, _ oci.Client, _ *containers.Container, s *specs.Spec) error {
|
||||
if s.Linux == nil {
|
||||
return nil
|
||||
}
|
||||
if s.Linux.Resources == nil {
|
||||
s.Linux.Resources = &specs.LinuxResources{}
|
||||
}
|
||||
if s.Linux.Resources.Memory == nil {
|
||||
s.Linux.Resources.Memory = &specs.LinuxMemory{}
|
||||
}
|
||||
s.Linux.Resources.Memory.Swap = &swap
|
||||
return nil
|
||||
})
|
||||
}
|
||||
if res.CpuShares != 0 {
|
||||
opts = append(opts, oci.WithCPUShares(res.CpuShares))
|
||||
}
|
||||
if res.CpuQuota != 0 || res.CpuPeriod != 0 {
|
||||
opts = append(opts, oci.WithCPUCFS(res.CpuQuota, res.CpuPeriod))
|
||||
}
|
||||
if res.CpusetCpus != "" {
|
||||
opts = append(opts, oci.WithCPUs(res.CpusetCpus))
|
||||
}
|
||||
if res.CpusetMems != "" {
|
||||
opts = append(opts, oci.WithCPUsMems(res.CpusetMems))
|
||||
}
|
||||
return opts, nil
|
||||
}
|
||||
|
||||
// genereateCDIOptions creates the OCI runtime spec options for injecting CDI
|
||||
// devices.
|
||||
func generateCDIOpts(manager *cdidevices.Manager, devs []*pb.CDIDevice) ([]oci.SpecOpts, error) {
|
||||
|
||||
@@ -112,6 +112,13 @@ func sub(m mount.Mount, subPath string) (mount.Mount, func() error, error) {
|
||||
return m, func() error { return nil }, nil
|
||||
}
|
||||
|
||||
func generateLinuxResourceOpts(res *pb.LinuxResources) ([]oci.SpecOpts, error) {
|
||||
if res == nil {
|
||||
return nil, nil
|
||||
}
|
||||
return nil, errors.New("no support for Linux resource limits on Windows")
|
||||
}
|
||||
|
||||
func generateCDIOpts(_ *cdidevices.Manager, devices []*pb.CDIDevice) ([]oci.SpecOpts, error) {
|
||||
if len(devices) == 0 {
|
||||
return nil, nil
|
||||
|
||||
@@ -837,6 +837,7 @@ func (dctx *dispatchContext) dispatchStages(ctx context.Context, allReachable ma
|
||||
ulimit: dctx.opt.Ulimits,
|
||||
devices: dctx.opt.Devices,
|
||||
cgroupParent: dctx.opt.CgroupParent,
|
||||
linuxResources: dctx.opt.LinuxResources,
|
||||
llbCaps: dctx.opt.LLBCaps,
|
||||
sourceMap: dctx.opt.SourceMap,
|
||||
lint: dctx.lint,
|
||||
@@ -992,6 +993,7 @@ type dispatchOpt struct {
|
||||
ulimit []*pb.Ulimit
|
||||
devices []*pb.CDIDevice
|
||||
cgroupParent string
|
||||
linuxResources *pb.LinuxResources
|
||||
llbCaps *apicaps.CapSet
|
||||
sourceMap *llb.SourceMap
|
||||
lint *linter.Linter
|
||||
@@ -1485,6 +1487,20 @@ func dispatchRun(d *dispatchState, c *instructions.RunCommand, proxy *llb.ProxyE
|
||||
}
|
||||
}
|
||||
|
||||
if dopt.llbCaps != nil && dopt.llbCaps.Supports(pb.CapExecMetaLinuxResources) == nil {
|
||||
if dopt.linuxResources != nil {
|
||||
opt = append(opt, llb.WithLinuxResources(llb.LinuxResources{
|
||||
Memory: dopt.linuxResources.Memory,
|
||||
MemorySwap: dopt.linuxResources.MemorySwap,
|
||||
CPUShares: dopt.linuxResources.CpuShares,
|
||||
CPUPeriod: dopt.linuxResources.CpuPeriod,
|
||||
CPUQuota: dopt.linuxResources.CpuQuota,
|
||||
CpusetCpus: dopt.linuxResources.CpusetCpus,
|
||||
CpusetMems: dopt.linuxResources.CpusetMems,
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
d.state = d.state.Run(opt...).Root()
|
||||
return commitToHistory(&d.image, "RUN "+runCommandString(args, d.buildArgs, env), true, &d.state, d.epoch)
|
||||
}
|
||||
|
||||
@@ -153,6 +153,7 @@ var allTests = integration.TestFuncs(
|
||||
testShmSize,
|
||||
testUlimit,
|
||||
testCgroupParent,
|
||||
testLinuxResources,
|
||||
testNamedImageContext,
|
||||
testNamedImageContextPlatform,
|
||||
testNamedImageContextTimestamps,
|
||||
@@ -7721,6 +7722,57 @@ COPY --from=base /out /
|
||||
require.Contains(t, strings.TrimSpace(string(dt)), `Resource temporarily unavailable`)
|
||||
}
|
||||
|
||||
func testLinuxResources(t *testing.T, sb integration.Sandbox) {
|
||||
integration.SkipOnPlatform(t, "windows")
|
||||
if sb.Rootless() {
|
||||
t.SkipNow()
|
||||
}
|
||||
|
||||
if _, err := os.Lstat("/sys/fs/cgroup/cgroup.subtree_control"); os.IsNotExist(err) {
|
||||
t.Skipf("test requires cgroup v2")
|
||||
}
|
||||
|
||||
f := getFrontend(t, sb)
|
||||
dockerfile := []byte(`
|
||||
FROM alpine AS base
|
||||
RUN mkdir /out && cat /sys/fs/cgroup/memory.max > /out/memory
|
||||
FROM scratch
|
||||
COPY --from=base /out /
|
||||
`)
|
||||
|
||||
dir := integration.Tmpdir(
|
||||
t,
|
||||
fstest.CreateFile("Dockerfile", dockerfile, 0600),
|
||||
)
|
||||
|
||||
c, err := client.New(sb.Context(), sb.Address())
|
||||
require.NoError(t, err)
|
||||
defer c.Close()
|
||||
|
||||
destDir := t.TempDir()
|
||||
|
||||
_, err = f.Solve(sb.Context(), c, client.SolveOpt{
|
||||
FrontendAttrs: map[string]string{
|
||||
"memory": "67108864",
|
||||
},
|
||||
LocalMounts: map[string]fsutil.FS{
|
||||
dockerui.DefaultLocalNameDockerfile: dir,
|
||||
dockerui.DefaultLocalNameContext: dir,
|
||||
},
|
||||
Exports: []client.ExportEntry{
|
||||
{
|
||||
Type: client.ExporterLocal,
|
||||
OutputDir: destDir,
|
||||
},
|
||||
},
|
||||
}, nil)
|
||||
require.NoError(t, err)
|
||||
|
||||
dt, err := os.ReadFile(filepath.Join(destDir, "memory"))
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, "67108864", strings.TrimSpace(string(dt)))
|
||||
}
|
||||
|
||||
func testStepNames(t *testing.T, sb integration.Sandbox) {
|
||||
ctx := sb.Context()
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@ import (
|
||||
"github.com/docker/go-units"
|
||||
"github.com/moby/buildkit/client/llb"
|
||||
"github.com/moby/buildkit/solver/pb"
|
||||
"github.com/moby/buildkit/util/cpuset"
|
||||
ocispecs "github.com/opencontainers/image-spec/specs-go/v1"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/tonistiigi/go-csvvalue"
|
||||
@@ -96,6 +97,79 @@ func parseUlimits(v string) ([]*pb.Ulimit, error) {
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func parseLinuxResources(opts map[string]string) (*pb.LinuxResources, error) {
|
||||
var res pb.LinuxResources
|
||||
|
||||
if v, ok := opts[keyMemory]; ok && v != "" {
|
||||
n, err := strconv.ParseInt(v, 10, 64)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "invalid %s value: %s", keyMemory, v)
|
||||
}
|
||||
if n <= 0 {
|
||||
return nil, errors.Errorf("invalid %s value: %s: must be > 0", keyMemory, v)
|
||||
}
|
||||
res.Memory = n
|
||||
}
|
||||
if v, ok := opts[keyMemorySwap]; ok && v != "" {
|
||||
n, err := strconv.ParseInt(v, 10, 64)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "invalid %s value: %s", keyMemorySwap, v)
|
||||
}
|
||||
if n < -1 || n == 0 {
|
||||
return nil, errors.Errorf("invalid %s value: %s: must be -1 (unlimited) or > 0", keyMemorySwap, v)
|
||||
}
|
||||
res.MemorySwap = n
|
||||
}
|
||||
if v, ok := opts[keyCPUShares]; ok && v != "" {
|
||||
n, err := strconv.ParseUint(v, 10, 64)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "invalid %s value: %s", keyCPUShares, v)
|
||||
}
|
||||
if n == 0 {
|
||||
return nil, errors.Errorf("invalid %s value: %s: must be > 0", keyCPUShares, v)
|
||||
}
|
||||
res.CpuShares = n
|
||||
}
|
||||
if v, ok := opts[keyCPUPeriod]; ok && v != "" {
|
||||
n, err := strconv.ParseUint(v, 10, 64)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "invalid %s value: %s", keyCPUPeriod, v)
|
||||
}
|
||||
if n == 0 {
|
||||
return nil, errors.Errorf("invalid %s value: %s: must be > 0", keyCPUPeriod, v)
|
||||
}
|
||||
res.CpuPeriod = n
|
||||
}
|
||||
if v, ok := opts[keyCPUQuota]; ok && v != "" {
|
||||
n, err := strconv.ParseInt(v, 10, 64)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "invalid %s value: %s", keyCPUQuota, v)
|
||||
}
|
||||
if n <= 0 {
|
||||
return nil, errors.Errorf("invalid %s value: %s: must be > 0", keyCPUQuota, v)
|
||||
}
|
||||
res.CpuQuota = n
|
||||
}
|
||||
if v, ok := opts[keyCpusetCpus]; ok && v != "" {
|
||||
if err := cpuset.Validate(v); err != nil {
|
||||
return nil, errors.Wrapf(err, "invalid %s value: %s", keyCpusetCpus, v)
|
||||
}
|
||||
res.CpusetCpus = v
|
||||
}
|
||||
if v, ok := opts[keyCpusetMems]; ok && v != "" {
|
||||
if err := cpuset.Validate(v); err != nil {
|
||||
return nil, errors.Wrapf(err, "invalid %s value: %s", keyCpusetMems, v)
|
||||
}
|
||||
res.CpusetMems = v
|
||||
}
|
||||
|
||||
if res.Memory == 0 && res.MemorySwap == 0 && res.CpuShares == 0 &&
|
||||
res.CpuPeriod == 0 && res.CpuQuota == 0 && res.CpusetCpus == "" && res.CpusetMems == "" {
|
||||
return nil, nil
|
||||
}
|
||||
return &res, nil
|
||||
}
|
||||
|
||||
func parseNetMode(v string) (pb.NetMode, error) {
|
||||
if v == "" {
|
||||
return llb.NetModeSandbox, nil
|
||||
|
||||
@@ -40,6 +40,13 @@ const (
|
||||
keyShmSize = "shm-size"
|
||||
keyTargetPlatform = "platform"
|
||||
keyUlimit = "ulimit"
|
||||
keyMemory = "memory"
|
||||
keyMemorySwap = "memswap"
|
||||
keyCPUShares = "cpushares"
|
||||
keyCPUPeriod = "cpuperiod"
|
||||
keyCPUQuota = "cpuquota"
|
||||
keyCpusetCpus = "cpusetcpus"
|
||||
keyCpusetMems = "cpusetmems"
|
||||
keyCacheFrom = "cache-from" // for registry only. deprecated in favor of keyCacheImports
|
||||
keyCacheImports = "cache-imports" // JSON representation of []CacheOptionsEntry
|
||||
|
||||
@@ -64,6 +71,7 @@ type Config struct {
|
||||
ShmSize int64
|
||||
Target string
|
||||
Ulimits []*pb.Ulimit
|
||||
LinuxResources *pb.LinuxResources
|
||||
Devices []*pb.CDIDevice
|
||||
LinterConfig *linter.Config
|
||||
|
||||
@@ -193,6 +201,12 @@ func (bc *Client) init() error {
|
||||
}
|
||||
bc.Ulimits = ulimits
|
||||
|
||||
linuxRes, err := parseLinuxResources(opts)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed to parse resource limits")
|
||||
}
|
||||
bc.LinuxResources = linuxRes
|
||||
|
||||
defaultNetMode, err := parseNetMode(opts[keyForceNetwork])
|
||||
if err != nil {
|
||||
return err
|
||||
|
||||
+27
-1
@@ -73,6 +73,7 @@ type state struct {
|
||||
|
||||
cache map[string]CacheManager
|
||||
mainCache CacheManager
|
||||
metadata VertexMetadata
|
||||
solver *Solver
|
||||
}
|
||||
|
||||
@@ -622,6 +623,13 @@ func (jl *Solver) loadUnlocked(ctx context.Context, v, parent Vertex, j *Job, ca
|
||||
}
|
||||
}
|
||||
}
|
||||
if md := v.Options().Metadata; md != nil {
|
||||
if st.metadata == nil {
|
||||
st.metadata = md
|
||||
} else {
|
||||
st.metadata = st.metadata.Merge(md)
|
||||
}
|
||||
}
|
||||
|
||||
if j != nil {
|
||||
if _, ok := st.jobs[j]; !ok {
|
||||
@@ -1272,7 +1280,14 @@ func (s *sharedOp) Exec(ctx context.Context, inputs []Result) (outputs []Result,
|
||||
func (s *sharedOp) getOp() (Op, error) {
|
||||
s.opOnce.Do(func() {
|
||||
s.subBuilder = s.st.builder()
|
||||
s.op, s.err = s.resolver(s.st.vtx, s.subBuilder)
|
||||
s.st.mu.Lock()
|
||||
metadata := s.st.metadata
|
||||
s.st.mu.Unlock()
|
||||
v := s.st.vtx
|
||||
if metadata != nil {
|
||||
v = &vertexWithMetadata{Vertex: v, metadata: metadata}
|
||||
}
|
||||
s.op, s.err = s.resolver(v, s.subBuilder)
|
||||
})
|
||||
if s.err != nil {
|
||||
return nil, s.err
|
||||
@@ -1280,6 +1295,17 @@ func (s *sharedOp) getOp() (Op, error) {
|
||||
return s.op, nil
|
||||
}
|
||||
|
||||
type vertexWithMetadata struct {
|
||||
Vertex
|
||||
metadata VertexMetadata
|
||||
}
|
||||
|
||||
func (v *vertexWithMetadata) Options() VertexOptions {
|
||||
opts := v.Vertex.Options()
|
||||
opts.Metadata = v.metadata
|
||||
return opts
|
||||
}
|
||||
|
||||
func (s *sharedOp) release() {
|
||||
if s.execRes != nil {
|
||||
for _, r := range s.execRes.execRes {
|
||||
|
||||
@@ -140,7 +140,7 @@ func (b *llbBridge) loadResult(ctx context.Context, def *pb.Definition, cacheImp
|
||||
}
|
||||
dpc := &detectPrunedCacheID{}
|
||||
|
||||
edge, err := Load(ctx, def, b.policy(polEngine), dpc.Load, ValidateEntitlements(ent, w.CDIManager()), WithCacheSources(cms), NormalizeRuntimePlatforms(), WithValidateCaps())
|
||||
edge, err := Load(ctx, def, b.policy(polEngine), dpc.Load, ValidateEntitlements(ent, w.CDIManager()), WithCacheSources(cms), NormalizeRuntimePlatforms(), WithValidateCaps(), WithLinuxResourcesMetadata())
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to load LLB")
|
||||
}
|
||||
|
||||
@@ -0,0 +1,147 @@
|
||||
package linuxresources
|
||||
|
||||
import (
|
||||
"github.com/moby/buildkit/solver"
|
||||
"github.com/moby/buildkit/solver/pb"
|
||||
"github.com/moby/buildkit/util/cpuset"
|
||||
)
|
||||
|
||||
type Metadata struct {
|
||||
LinuxResources *pb.LinuxResources
|
||||
}
|
||||
|
||||
func (m *Metadata) Merge(other solver.VertexMetadata) solver.VertexMetadata {
|
||||
if other == nil {
|
||||
return m
|
||||
}
|
||||
o, ok := other.(*Metadata)
|
||||
if !ok {
|
||||
return m
|
||||
}
|
||||
return &Metadata{
|
||||
LinuxResources: mergeRelaxed(m.LinuxResources, o.LinuxResources),
|
||||
}
|
||||
}
|
||||
|
||||
// mergeRelaxed returns the most relaxed of a and b, so a shared
|
||||
// vertex is never throttled by a stricter sibling job.
|
||||
func mergeRelaxed(a, b *pb.LinuxResources) *pb.LinuxResources {
|
||||
if a == nil && b == nil {
|
||||
return nil
|
||||
}
|
||||
if a == nil {
|
||||
return b.CloneVT()
|
||||
}
|
||||
if b == nil {
|
||||
return a.CloneVT()
|
||||
}
|
||||
period, quota := relaxedCPUBandwidth(a.CpuPeriod, a.CpuQuota, b.CpuPeriod, b.CpuQuota)
|
||||
return &pb.LinuxResources{
|
||||
Memory: relaxedMemory(a.Memory, b.Memory),
|
||||
MemorySwap: relaxedMemorySwap(a.MemorySwap, b.MemorySwap),
|
||||
CpuShares: relaxedCPUShares(a.CpuShares, b.CpuShares),
|
||||
CpuPeriod: period,
|
||||
CpuQuota: quota,
|
||||
CpusetCpus: relaxedCpuset(a.CpusetCpus, b.CpusetCpus),
|
||||
CpusetMems: relaxedCpuset(a.CpusetMems, b.CpusetMems),
|
||||
}
|
||||
}
|
||||
|
||||
// 0 means unlimited (most relaxed); otherwise higher = more relaxed.
|
||||
func relaxedMemory(a, b int64) int64 {
|
||||
if a == 0 || b == 0 {
|
||||
return 0
|
||||
}
|
||||
return max(a, b)
|
||||
}
|
||||
|
||||
// Docker memory-swap semantics: 0 = unset, -1 = unlimited (most relaxed),
|
||||
// positive values are limits where higher = more relaxed.
|
||||
func relaxedMemorySwap(a, b int64) int64 {
|
||||
if a == 0 {
|
||||
return b
|
||||
}
|
||||
if b == 0 {
|
||||
return a
|
||||
}
|
||||
if a == -1 || b == -1 {
|
||||
return -1
|
||||
}
|
||||
return max(a, b)
|
||||
}
|
||||
|
||||
// 0 = unset (kernel default), not unlimited; otherwise higher = more relaxed.
|
||||
func relaxedCPUShares(a, b uint64) uint64 {
|
||||
if a == 0 {
|
||||
return b
|
||||
}
|
||||
if b == 0 {
|
||||
return a
|
||||
}
|
||||
return max(a, b)
|
||||
}
|
||||
|
||||
// relaxedCPUBandwidth picks the (period, quota) pair with the higher cap
|
||||
// (quota/period) without mixing them. Quota 0 = unlimited. On a tie, shorter
|
||||
// period wins (same cap executes more frequently).
|
||||
func relaxedCPUBandwidth(periodA uint64, quotaA int64, periodB uint64, quotaB int64) (uint64, int64) {
|
||||
if quotaA == 0 && quotaB == 0 {
|
||||
return 0, 0
|
||||
}
|
||||
if quotaA == 0 {
|
||||
return periodA, 0
|
||||
}
|
||||
if quotaB == 0 {
|
||||
return periodB, 0
|
||||
}
|
||||
// Compare quota/period via cross-multiplication when both periods are set;
|
||||
// otherwise fall back to comparing raw quotas.
|
||||
if periodA != 0 && periodB != 0 {
|
||||
capA := quotaA * int64(periodB)
|
||||
capB := quotaB * int64(periodA)
|
||||
if capA > capB {
|
||||
return periodA, quotaA
|
||||
}
|
||||
if capB > capA {
|
||||
return periodB, quotaB
|
||||
}
|
||||
// equal caps: shorter period is more relaxed
|
||||
if periodA <= periodB {
|
||||
return periodA, quotaA
|
||||
}
|
||||
return periodB, quotaB
|
||||
}
|
||||
// At least one period is 0: compare quotas; on a tie, prefer the non-zero period.
|
||||
if quotaA > quotaB {
|
||||
return periodA, quotaA
|
||||
}
|
||||
if quotaB > quotaA {
|
||||
return periodB, quotaB
|
||||
}
|
||||
if periodA == 0 {
|
||||
return periodB, quotaB
|
||||
}
|
||||
return periodA, quotaA
|
||||
}
|
||||
|
||||
// Empty = unset (most relaxed); otherwise the union of both sides, re-emitted
|
||||
// in canonical form. On parse error, falls back to the side that parsed cleanly.
|
||||
func relaxedCpuset(a, b string) string {
|
||||
if a == "" || b == "" {
|
||||
return ""
|
||||
}
|
||||
setA, errA := cpuset.Parse(a)
|
||||
setB, errB := cpuset.Parse(b)
|
||||
switch {
|
||||
case errA != nil && errB != nil:
|
||||
return a
|
||||
case errA != nil:
|
||||
return b
|
||||
case errB != nil:
|
||||
return a
|
||||
}
|
||||
for v := range setB {
|
||||
setA[v] = struct{}{}
|
||||
}
|
||||
return cpuset.Format(setA)
|
||||
}
|
||||
@@ -0,0 +1,220 @@
|
||||
package linuxresources
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/moby/buildkit/solver/pb"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestMergeRelaxed(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
t.Run("both nil", func(t *testing.T) {
|
||||
result := mergeRelaxed(nil, nil)
|
||||
require.Nil(t, result)
|
||||
})
|
||||
|
||||
t.Run("first nil", func(t *testing.T) {
|
||||
b := &pb.LinuxResources{Memory: 64 * 1024 * 1024}
|
||||
result := mergeRelaxed(nil, b)
|
||||
require.Equal(t, b, result)
|
||||
})
|
||||
|
||||
t.Run("second nil", func(t *testing.T) {
|
||||
a := &pb.LinuxResources{Memory: 64 * 1024 * 1024}
|
||||
result := mergeRelaxed(a, nil)
|
||||
require.Equal(t, a, result)
|
||||
})
|
||||
|
||||
t.Run("higher memory wins", func(t *testing.T) {
|
||||
a := &pb.LinuxResources{Memory: 64 * 1024 * 1024}
|
||||
b := &pb.LinuxResources{Memory: 128 * 1024 * 1024}
|
||||
result := mergeRelaxed(a, b)
|
||||
require.Equal(t, int64(128*1024*1024), result.Memory)
|
||||
})
|
||||
|
||||
t.Run("zero memory means unlimited and wins", func(t *testing.T) {
|
||||
a := &pb.LinuxResources{Memory: 64 * 1024 * 1024}
|
||||
b := &pb.LinuxResources{Memory: 0}
|
||||
result := mergeRelaxed(a, b)
|
||||
require.Equal(t, int64(0), result.Memory)
|
||||
|
||||
// Reverse order should also work
|
||||
result = mergeRelaxed(b, a)
|
||||
require.Equal(t, int64(0), result.Memory)
|
||||
})
|
||||
|
||||
t.Run("zero memory swap means unset, preserves non-zero", func(t *testing.T) {
|
||||
a := &pb.LinuxResources{MemorySwap: 256 * 1024 * 1024}
|
||||
b := &pb.LinuxResources{MemorySwap: 0}
|
||||
result := mergeRelaxed(a, b)
|
||||
require.Equal(t, int64(256*1024*1024), result.MemorySwap)
|
||||
|
||||
// Reverse order
|
||||
result = mergeRelaxed(b, a)
|
||||
require.Equal(t, int64(256*1024*1024), result.MemorySwap)
|
||||
})
|
||||
|
||||
t.Run("memory swap -1 means unlimited and wins", func(t *testing.T) {
|
||||
a := &pb.LinuxResources{MemorySwap: 256 * 1024 * 1024}
|
||||
b := &pb.LinuxResources{MemorySwap: -1}
|
||||
result := mergeRelaxed(a, b)
|
||||
require.Equal(t, int64(-1), result.MemorySwap)
|
||||
|
||||
// Reverse order
|
||||
result = mergeRelaxed(b, a)
|
||||
require.Equal(t, int64(-1), result.MemorySwap)
|
||||
})
|
||||
|
||||
t.Run("higher memory swap wins", func(t *testing.T) {
|
||||
a := &pb.LinuxResources{MemorySwap: 128 * 1024 * 1024}
|
||||
b := &pb.LinuxResources{MemorySwap: 256 * 1024 * 1024}
|
||||
result := mergeRelaxed(a, b)
|
||||
require.Equal(t, int64(256*1024*1024), result.MemorySwap)
|
||||
})
|
||||
|
||||
t.Run("higher cpu shares wins", func(t *testing.T) {
|
||||
a := &pb.LinuxResources{CpuShares: 256}
|
||||
b := &pb.LinuxResources{CpuShares: 512}
|
||||
result := mergeRelaxed(a, b)
|
||||
require.Equal(t, uint64(512), result.CpuShares)
|
||||
})
|
||||
|
||||
t.Run("zero cpu shares means unset, preserves non-zero", func(t *testing.T) {
|
||||
a := &pb.LinuxResources{CpuShares: 512}
|
||||
b := &pb.LinuxResources{CpuShares: 0}
|
||||
result := mergeRelaxed(a, b)
|
||||
require.Equal(t, uint64(512), result.CpuShares)
|
||||
|
||||
// Reverse order
|
||||
result = mergeRelaxed(b, a)
|
||||
require.Equal(t, uint64(512), result.CpuShares)
|
||||
})
|
||||
|
||||
t.Run("higher cpu quota wins", func(t *testing.T) {
|
||||
a := &pb.LinuxResources{CpuQuota: 50000, CpuPeriod: 100000}
|
||||
b := &pb.LinuxResources{CpuQuota: 80000, CpuPeriod: 100000}
|
||||
result := mergeRelaxed(a, b)
|
||||
require.Equal(t, int64(80000), result.CpuQuota)
|
||||
require.Equal(t, uint64(100000), result.CpuPeriod)
|
||||
})
|
||||
|
||||
t.Run("zero cpu quota means unlimited and wins", func(t *testing.T) {
|
||||
a := &pb.LinuxResources{CpuQuota: 50000, CpuPeriod: 100000}
|
||||
b := &pb.LinuxResources{CpuQuota: 0, CpuPeriod: 100000}
|
||||
result := mergeRelaxed(a, b)
|
||||
require.Equal(t, int64(0), result.CpuQuota)
|
||||
})
|
||||
|
||||
t.Run("cpu bandwidth picks pair with higher cap, not mixed", func(t *testing.T) {
|
||||
// cap A = 50000/100000 = 0.5; cap B = 80000/200000 = 0.4 — A wins as a pair.
|
||||
a := &pb.LinuxResources{CpuQuota: 50000, CpuPeriod: 100000}
|
||||
b := &pb.LinuxResources{CpuQuota: 80000, CpuPeriod: 200000}
|
||||
result := mergeRelaxed(a, b)
|
||||
require.Equal(t, int64(50000), result.CpuQuota)
|
||||
require.Equal(t, uint64(100000), result.CpuPeriod)
|
||||
})
|
||||
|
||||
t.Run("cpu bandwidth: higher cap wins as a pair", func(t *testing.T) {
|
||||
// cap A = 80000/100000 = 0.8; cap B = 50000/100000 = 0.5 — A wins.
|
||||
a := &pb.LinuxResources{CpuQuota: 80000, CpuPeriod: 100000}
|
||||
b := &pb.LinuxResources{CpuQuota: 50000, CpuPeriod: 100000}
|
||||
result := mergeRelaxed(a, b)
|
||||
require.Equal(t, int64(80000), result.CpuQuota)
|
||||
require.Equal(t, uint64(100000), result.CpuPeriod)
|
||||
})
|
||||
|
||||
t.Run("equal quotas picks shorter period (more relaxed)", func(t *testing.T) {
|
||||
a := &pb.LinuxResources{CpuQuota: 50000, CpuPeriod: 100000}
|
||||
b := &pb.LinuxResources{CpuQuota: 50000, CpuPeriod: 200000}
|
||||
result := mergeRelaxed(a, b)
|
||||
require.Equal(t, int64(50000), result.CpuQuota)
|
||||
require.Equal(t, uint64(100000), result.CpuPeriod) // shorter period = more CPU
|
||||
})
|
||||
|
||||
t.Run("empty cpuset string means unset and wins", func(t *testing.T) {
|
||||
a := &pb.LinuxResources{CpusetCpus: "0-3"}
|
||||
b := &pb.LinuxResources{CpusetCpus: ""}
|
||||
result := mergeRelaxed(a, b)
|
||||
require.Equal(t, "", result.CpusetCpus)
|
||||
})
|
||||
|
||||
t.Run("cpuset subset is absorbed by superset", func(t *testing.T) {
|
||||
a := &pb.LinuxResources{CpusetCpus: "0-3"}
|
||||
b := &pb.LinuxResources{CpusetCpus: "0-7"}
|
||||
result := mergeRelaxed(a, b)
|
||||
require.Equal(t, "0-7", result.CpusetCpus)
|
||||
})
|
||||
|
||||
t.Run("cpuset range and list merge into union", func(t *testing.T) {
|
||||
a := &pb.LinuxResources{CpusetCpus: "0-7"}
|
||||
b := &pb.LinuxResources{CpusetCpus: "0,1,2"}
|
||||
result := mergeRelaxed(a, b)
|
||||
require.Equal(t, "0-7", result.CpusetCpus)
|
||||
})
|
||||
|
||||
t.Run("disjoint cpusets produce true union", func(t *testing.T) {
|
||||
a := &pb.LinuxResources{CpusetCpus: "0-1"}
|
||||
b := &pb.LinuxResources{CpusetCpus: "2-3"}
|
||||
result := mergeRelaxed(a, b)
|
||||
require.Equal(t, "0-3", result.CpusetCpus)
|
||||
})
|
||||
|
||||
t.Run("cpuset union collapses adjacent ranges", func(t *testing.T) {
|
||||
a := &pb.LinuxResources{CpusetCpus: "0,2,4"}
|
||||
b := &pb.LinuxResources{CpusetCpus: "1,3,5"}
|
||||
result := mergeRelaxed(a, b)
|
||||
require.Equal(t, "0-5", result.CpusetCpus)
|
||||
})
|
||||
|
||||
t.Run("cpuset union keeps gaps as separate ranges", func(t *testing.T) {
|
||||
a := &pb.LinuxResources{CpusetCpus: "0-2,7"}
|
||||
b := &pb.LinuxResources{CpusetCpus: "5,9"}
|
||||
result := mergeRelaxed(a, b)
|
||||
require.Equal(t, "0-2,5,7,9", result.CpusetCpus)
|
||||
})
|
||||
|
||||
t.Run("all fields merged independently", func(t *testing.T) {
|
||||
a := &pb.LinuxResources{
|
||||
Memory: 64 * 1024 * 1024,
|
||||
MemorySwap: 128 * 1024 * 1024,
|
||||
CpuShares: 512,
|
||||
CpuPeriod: 100000,
|
||||
CpuQuota: 50000,
|
||||
CpusetCpus: "0-3",
|
||||
CpusetMems: "0",
|
||||
}
|
||||
b := &pb.LinuxResources{
|
||||
Memory: 128 * 1024 * 1024,
|
||||
MemorySwap: 64 * 1024 * 1024,
|
||||
CpuShares: 256,
|
||||
CpuPeriod: 200000,
|
||||
CpuQuota: 80000,
|
||||
CpusetCpus: "0-1",
|
||||
CpusetMems: "0,1",
|
||||
}
|
||||
result := mergeRelaxed(a, b)
|
||||
require.Equal(t, int64(128*1024*1024), result.Memory)
|
||||
require.Equal(t, int64(128*1024*1024), result.MemorySwap)
|
||||
require.Equal(t, uint64(512), result.CpuShares)
|
||||
// CPU bandwidth is picked as a pair (not mixed). cap A = 50000/100000 =
|
||||
// 0.5; cap B = 80000/200000 = 0.4 — pair A wins.
|
||||
require.Equal(t, int64(50000), result.CpuQuota)
|
||||
require.Equal(t, uint64(100000), result.CpuPeriod)
|
||||
require.Equal(t, "0-3", result.CpusetCpus) // union of "0-3" and "0-1"
|
||||
require.Equal(t, "0-1", result.CpusetMems) // union of "0" and "0,1"
|
||||
})
|
||||
|
||||
t.Run("no pointer aliasing when one side is nil", func(t *testing.T) {
|
||||
b := &pb.LinuxResources{Memory: 64 * 1024 * 1024}
|
||||
result := mergeRelaxed(nil, b)
|
||||
require.NotSame(t, b, result, "result should be a clone, not the same pointer")
|
||||
require.Equal(t, b.Memory, result.Memory)
|
||||
|
||||
a := &pb.LinuxResources{Memory: 128 * 1024 * 1024}
|
||||
result = mergeRelaxed(a, nil)
|
||||
require.NotSame(t, a, result, "result should be a clone, not the same pointer")
|
||||
require.Equal(t, a.Memory, result.Memory)
|
||||
})
|
||||
}
|
||||
@@ -37,37 +37,39 @@ import (
|
||||
const execCacheType = "buildkit.exec.v0"
|
||||
|
||||
type ExecOp struct {
|
||||
op *pb.ExecOp
|
||||
cm cache.Manager
|
||||
mm *mounts.MountManager
|
||||
sm *session.Manager
|
||||
exec executor.Executor
|
||||
w worker.Worker
|
||||
platform *pb.Platform
|
||||
numInputs int
|
||||
parallelism *semaphore.Weighted
|
||||
rec resourcestypes.Recorder
|
||||
digest digest.Digest
|
||||
op *pb.ExecOp
|
||||
cm cache.Manager
|
||||
mm *mounts.MountManager
|
||||
sm *session.Manager
|
||||
exec executor.Executor
|
||||
w worker.Worker
|
||||
platform *pb.Platform
|
||||
numInputs int
|
||||
parallelism *semaphore.Weighted
|
||||
rec resourcestypes.Recorder
|
||||
digest digest.Digest
|
||||
linuxResources *pb.LinuxResources
|
||||
}
|
||||
|
||||
var _ solver.Op = &ExecOp{}
|
||||
|
||||
func NewExecOp(v solver.Vertex, op *pb.Op_Exec, platform *pb.Platform, cm cache.Manager, parallelism *semaphore.Weighted, sm *session.Manager, exec executor.Executor, w worker.Worker) (*ExecOp, error) {
|
||||
func NewExecOp(v solver.Vertex, op *pb.Op_Exec, platform *pb.Platform, cm cache.Manager, parallelism *semaphore.Weighted, sm *session.Manager, exec executor.Executor, w worker.Worker, linuxResources *pb.LinuxResources) (*ExecOp, error) {
|
||||
if err := opsutils.Validate(&pb.Op{Op: op}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
name := fmt.Sprintf("exec %s", strings.Join(op.Exec.Meta.Args, " "))
|
||||
return &ExecOp{
|
||||
op: op.Exec,
|
||||
mm: mounts.NewMountManager(name, cm, sm),
|
||||
cm: cm,
|
||||
sm: sm,
|
||||
exec: exec,
|
||||
numInputs: len(v.Inputs()),
|
||||
w: w,
|
||||
platform: platform,
|
||||
parallelism: parallelism,
|
||||
digest: v.Digest(),
|
||||
op: op.Exec,
|
||||
mm: mounts.NewMountManager(name, cm, sm),
|
||||
cm: cm,
|
||||
sm: sm,
|
||||
exec: exec,
|
||||
numInputs: len(v.Inputs()),
|
||||
w: w,
|
||||
platform: platform,
|
||||
parallelism: parallelism,
|
||||
digest: v.Digest(),
|
||||
linuxResources: linuxResources,
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -456,6 +458,7 @@ func (e *ExecOp) Exec(ctx context.Context, jobCtx solver.JobContext, inputs []so
|
||||
Ulimit: e.op.Meta.Ulimit,
|
||||
CDIDevices: e.op.CdiDevices,
|
||||
CgroupParent: e.op.Meta.CgroupParent,
|
||||
LinuxResources: e.linuxResources,
|
||||
NetMode: e.op.Network,
|
||||
SecurityMode: e.op.Security,
|
||||
RemoveMountStubsRecursive: e.op.Meta.RemoveMountStubsRecursive,
|
||||
|
||||
@@ -9,6 +9,7 @@ import (
|
||||
"github.com/containerd/platforms"
|
||||
"github.com/moby/buildkit/solver"
|
||||
"github.com/moby/buildkit/solver/llbsolver/cdidevices"
|
||||
"github.com/moby/buildkit/solver/llbsolver/linuxresources"
|
||||
"github.com/moby/buildkit/solver/llbsolver/ops/opsutils"
|
||||
"github.com/moby/buildkit/solver/pb"
|
||||
"github.com/moby/buildkit/util/apicaps"
|
||||
@@ -73,6 +74,19 @@ func WithCacheSources(cms []solver.CacheManager) LoadOpt {
|
||||
}
|
||||
}
|
||||
|
||||
func WithLinuxResourcesMetadata() LoadOpt {
|
||||
return func(op *pb.Op, md *pb.OpMetadata, opt *solver.VertexOptions) error {
|
||||
if _, ok := op.Op.(*pb.Op_Exec); !ok {
|
||||
return nil
|
||||
}
|
||||
if md == nil || md.LinuxResources == nil {
|
||||
return nil
|
||||
}
|
||||
opt.Metadata = &linuxresources.Metadata{LinuxResources: md.LinuxResources}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func NormalizeRuntimePlatforms() LoadOpt {
|
||||
var defaultPlatform *pb.Platform
|
||||
return func(op *pb.Op, _ *pb.OpMetadata, opt *solver.VertexOptions) error {
|
||||
|
||||
@@ -64,6 +64,7 @@ const (
|
||||
CapExecMetaUlimit apicaps.CapID = "exec.meta.ulimit"
|
||||
CapExecMetaCDI apicaps.CapID = "exec.meta.cdi"
|
||||
CapExecMetaRemoveMountStubsRecursive apicaps.CapID = "exec.meta.removemountstubs.recursive"
|
||||
CapExecMetaLinuxResources apicaps.CapID = "exec.meta.linux.resources"
|
||||
CapExecMountBind apicaps.CapID = "exec.mount.bind"
|
||||
CapExecMountBindReadWriteNoOutput apicaps.CapID = "exec.mount.bind.readwrite-nooutput"
|
||||
CapExecMountCache apicaps.CapID = "exec.mount.cache"
|
||||
@@ -397,6 +398,12 @@ func init() {
|
||||
Status: apicaps.CapStatusExperimental,
|
||||
})
|
||||
|
||||
Caps.Init(apicaps.Cap{
|
||||
ID: CapExecMetaLinuxResources,
|
||||
Enabled: true,
|
||||
Status: apicaps.CapStatusExperimental,
|
||||
})
|
||||
|
||||
Caps.Init(apicaps.Cap{
|
||||
ID: CapExecMountBind,
|
||||
Enabled: true,
|
||||
|
||||
+255
-135
@@ -1604,11 +1604,12 @@ type OpMetadata struct {
|
||||
Description map[string]string `protobuf:"bytes,2,rep,name=description,proto3" json:"description,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"`
|
||||
// index 3 reserved for WorkerConstraint in previous versions
|
||||
// WorkerConstraint worker_constraint = 3;
|
||||
ExportCache *ExportCache `protobuf:"bytes,4,opt,name=export_cache,json=exportCache,proto3" json:"export_cache,omitempty"`
|
||||
Caps map[string]bool `protobuf:"bytes,5,rep,name=caps,proto3" json:"caps,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"varint,2,opt,name=value"`
|
||||
ProgressGroup *ProgressGroup `protobuf:"bytes,6,opt,name=progress_group,json=progressGroup,proto3" json:"progress_group,omitempty"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
ExportCache *ExportCache `protobuf:"bytes,4,opt,name=export_cache,json=exportCache,proto3" json:"export_cache,omitempty"`
|
||||
Caps map[string]bool `protobuf:"bytes,5,rep,name=caps,proto3" json:"caps,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"varint,2,opt,name=value"`
|
||||
ProgressGroup *ProgressGroup `protobuf:"bytes,6,opt,name=progress_group,json=progressGroup,proto3" json:"progress_group,omitempty"`
|
||||
LinuxResources *LinuxResources `protobuf:"bytes,7,opt,name=linux_resources,json=linuxResources,proto3" json:"linux_resources,omitempty"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
|
||||
func (x *OpMetadata) Reset() {
|
||||
@@ -1676,6 +1677,13 @@ func (x *OpMetadata) GetProgressGroup() *ProgressGroup {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *OpMetadata) GetLinuxResources() *LinuxResources {
|
||||
if x != nil {
|
||||
return x.LinuxResources
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Source is a source mapping description for a file
|
||||
type Source struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
@@ -2106,6 +2114,101 @@ func (x *ProgressGroup) GetWeak() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// LinuxResources specifies cgroup resource limits for containers.
|
||||
// Used in OpMetadata to set per-step resource constraints without
|
||||
// affecting the cache key.
|
||||
type LinuxResources struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
Memory int64 `protobuf:"varint,1,opt,name=memory,proto3" json:"memory,omitempty"` // memory limit in bytes
|
||||
MemorySwap int64 `protobuf:"varint,2,opt,name=memorySwap,proto3" json:"memorySwap,omitempty"` // memory+swap limit in bytes
|
||||
CpuShares uint64 `protobuf:"varint,3,opt,name=cpuShares,proto3" json:"cpuShares,omitempty"` // relative CPU weight
|
||||
CpuPeriod uint64 `protobuf:"varint,4,opt,name=cpuPeriod,proto3" json:"cpuPeriod,omitempty"` // CFS period in microseconds
|
||||
CpuQuota int64 `protobuf:"varint,5,opt,name=cpuQuota,proto3" json:"cpuQuota,omitempty"` // CFS quota in microseconds
|
||||
CpusetCpus string `protobuf:"bytes,6,opt,name=cpusetCpus,proto3" json:"cpusetCpus,omitempty"` // CPU affinity (e.g., "0-3")
|
||||
CpusetMems string `protobuf:"bytes,7,opt,name=cpusetMems,proto3" json:"cpusetMems,omitempty"` // memory node affinity (e.g., "0,1")
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
|
||||
func (x *LinuxResources) Reset() {
|
||||
*x = LinuxResources{}
|
||||
mi := &file_github_com_moby_buildkit_solver_pb_ops_proto_msgTypes[26]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
||||
func (x *LinuxResources) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*LinuxResources) ProtoMessage() {}
|
||||
|
||||
func (x *LinuxResources) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_github_com_moby_buildkit_solver_pb_ops_proto_msgTypes[26]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use LinuxResources.ProtoReflect.Descriptor instead.
|
||||
func (*LinuxResources) Descriptor() ([]byte, []int) {
|
||||
return file_github_com_moby_buildkit_solver_pb_ops_proto_rawDescGZIP(), []int{26}
|
||||
}
|
||||
|
||||
func (x *LinuxResources) GetMemory() int64 {
|
||||
if x != nil {
|
||||
return x.Memory
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *LinuxResources) GetMemorySwap() int64 {
|
||||
if x != nil {
|
||||
return x.MemorySwap
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *LinuxResources) GetCpuShares() uint64 {
|
||||
if x != nil {
|
||||
return x.CpuShares
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *LinuxResources) GetCpuPeriod() uint64 {
|
||||
if x != nil {
|
||||
return x.CpuPeriod
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *LinuxResources) GetCpuQuota() int64 {
|
||||
if x != nil {
|
||||
return x.CpuQuota
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *LinuxResources) GetCpusetCpus() string {
|
||||
if x != nil {
|
||||
return x.CpusetCpus
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *LinuxResources) GetCpusetMems() string {
|
||||
if x != nil {
|
||||
return x.CpusetMems
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
type ProxyEnv struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
HttpProxy string `protobuf:"bytes,1,opt,name=http_proxy,json=httpProxy,proto3" json:"http_proxy,omitempty"`
|
||||
@@ -2119,7 +2222,7 @@ type ProxyEnv struct {
|
||||
|
||||
func (x *ProxyEnv) Reset() {
|
||||
*x = ProxyEnv{}
|
||||
mi := &file_github_com_moby_buildkit_solver_pb_ops_proto_msgTypes[26]
|
||||
mi := &file_github_com_moby_buildkit_solver_pb_ops_proto_msgTypes[27]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
@@ -2131,7 +2234,7 @@ func (x *ProxyEnv) String() string {
|
||||
func (*ProxyEnv) ProtoMessage() {}
|
||||
|
||||
func (x *ProxyEnv) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_github_com_moby_buildkit_solver_pb_ops_proto_msgTypes[26]
|
||||
mi := &file_github_com_moby_buildkit_solver_pb_ops_proto_msgTypes[27]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
@@ -2144,7 +2247,7 @@ func (x *ProxyEnv) ProtoReflect() protoreflect.Message {
|
||||
|
||||
// Deprecated: Use ProxyEnv.ProtoReflect.Descriptor instead.
|
||||
func (*ProxyEnv) Descriptor() ([]byte, []int) {
|
||||
return file_github_com_moby_buildkit_solver_pb_ops_proto_rawDescGZIP(), []int{26}
|
||||
return file_github_com_moby_buildkit_solver_pb_ops_proto_rawDescGZIP(), []int{27}
|
||||
}
|
||||
|
||||
func (x *ProxyEnv) GetHttpProxy() string {
|
||||
@@ -2192,7 +2295,7 @@ type WorkerConstraints struct {
|
||||
|
||||
func (x *WorkerConstraints) Reset() {
|
||||
*x = WorkerConstraints{}
|
||||
mi := &file_github_com_moby_buildkit_solver_pb_ops_proto_msgTypes[27]
|
||||
mi := &file_github_com_moby_buildkit_solver_pb_ops_proto_msgTypes[28]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
@@ -2204,7 +2307,7 @@ func (x *WorkerConstraints) String() string {
|
||||
func (*WorkerConstraints) ProtoMessage() {}
|
||||
|
||||
func (x *WorkerConstraints) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_github_com_moby_buildkit_solver_pb_ops_proto_msgTypes[27]
|
||||
mi := &file_github_com_moby_buildkit_solver_pb_ops_proto_msgTypes[28]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
@@ -2217,7 +2320,7 @@ func (x *WorkerConstraints) ProtoReflect() protoreflect.Message {
|
||||
|
||||
// Deprecated: Use WorkerConstraints.ProtoReflect.Descriptor instead.
|
||||
func (*WorkerConstraints) Descriptor() ([]byte, []int) {
|
||||
return file_github_com_moby_buildkit_solver_pb_ops_proto_rawDescGZIP(), []int{27}
|
||||
return file_github_com_moby_buildkit_solver_pb_ops_proto_rawDescGZIP(), []int{28}
|
||||
}
|
||||
|
||||
func (x *WorkerConstraints) GetFilter() []string {
|
||||
@@ -2243,7 +2346,7 @@ type Definition struct {
|
||||
|
||||
func (x *Definition) Reset() {
|
||||
*x = Definition{}
|
||||
mi := &file_github_com_moby_buildkit_solver_pb_ops_proto_msgTypes[28]
|
||||
mi := &file_github_com_moby_buildkit_solver_pb_ops_proto_msgTypes[29]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
@@ -2255,7 +2358,7 @@ func (x *Definition) String() string {
|
||||
func (*Definition) ProtoMessage() {}
|
||||
|
||||
func (x *Definition) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_github_com_moby_buildkit_solver_pb_ops_proto_msgTypes[28]
|
||||
mi := &file_github_com_moby_buildkit_solver_pb_ops_proto_msgTypes[29]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
@@ -2268,7 +2371,7 @@ func (x *Definition) ProtoReflect() protoreflect.Message {
|
||||
|
||||
// Deprecated: Use Definition.ProtoReflect.Descriptor instead.
|
||||
func (*Definition) Descriptor() ([]byte, []int) {
|
||||
return file_github_com_moby_buildkit_solver_pb_ops_proto_rawDescGZIP(), []int{28}
|
||||
return file_github_com_moby_buildkit_solver_pb_ops_proto_rawDescGZIP(), []int{29}
|
||||
}
|
||||
|
||||
func (x *Definition) GetDef() [][]byte {
|
||||
@@ -2301,7 +2404,7 @@ type FileOp struct {
|
||||
|
||||
func (x *FileOp) Reset() {
|
||||
*x = FileOp{}
|
||||
mi := &file_github_com_moby_buildkit_solver_pb_ops_proto_msgTypes[29]
|
||||
mi := &file_github_com_moby_buildkit_solver_pb_ops_proto_msgTypes[30]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
@@ -2313,7 +2416,7 @@ func (x *FileOp) String() string {
|
||||
func (*FileOp) ProtoMessage() {}
|
||||
|
||||
func (x *FileOp) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_github_com_moby_buildkit_solver_pb_ops_proto_msgTypes[29]
|
||||
mi := &file_github_com_moby_buildkit_solver_pb_ops_proto_msgTypes[30]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
@@ -2326,7 +2429,7 @@ func (x *FileOp) ProtoReflect() protoreflect.Message {
|
||||
|
||||
// Deprecated: Use FileOp.ProtoReflect.Descriptor instead.
|
||||
func (*FileOp) Descriptor() ([]byte, []int) {
|
||||
return file_github_com_moby_buildkit_solver_pb_ops_proto_rawDescGZIP(), []int{29}
|
||||
return file_github_com_moby_buildkit_solver_pb_ops_proto_rawDescGZIP(), []int{30}
|
||||
}
|
||||
|
||||
func (x *FileOp) GetActions() []*FileAction {
|
||||
@@ -2356,7 +2459,7 @@ type FileAction struct {
|
||||
|
||||
func (x *FileAction) Reset() {
|
||||
*x = FileAction{}
|
||||
mi := &file_github_com_moby_buildkit_solver_pb_ops_proto_msgTypes[30]
|
||||
mi := &file_github_com_moby_buildkit_solver_pb_ops_proto_msgTypes[31]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
@@ -2368,7 +2471,7 @@ func (x *FileAction) String() string {
|
||||
func (*FileAction) ProtoMessage() {}
|
||||
|
||||
func (x *FileAction) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_github_com_moby_buildkit_solver_pb_ops_proto_msgTypes[30]
|
||||
mi := &file_github_com_moby_buildkit_solver_pb_ops_proto_msgTypes[31]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
@@ -2381,7 +2484,7 @@ func (x *FileAction) ProtoReflect() protoreflect.Message {
|
||||
|
||||
// Deprecated: Use FileAction.ProtoReflect.Descriptor instead.
|
||||
func (*FileAction) Descriptor() ([]byte, []int) {
|
||||
return file_github_com_moby_buildkit_solver_pb_ops_proto_rawDescGZIP(), []int{30}
|
||||
return file_github_com_moby_buildkit_solver_pb_ops_proto_rawDescGZIP(), []int{31}
|
||||
}
|
||||
|
||||
func (x *FileAction) GetInput() int64 {
|
||||
@@ -2537,7 +2640,7 @@ type FileActionCopy struct {
|
||||
|
||||
func (x *FileActionCopy) Reset() {
|
||||
*x = FileActionCopy{}
|
||||
mi := &file_github_com_moby_buildkit_solver_pb_ops_proto_msgTypes[31]
|
||||
mi := &file_github_com_moby_buildkit_solver_pb_ops_proto_msgTypes[32]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
@@ -2549,7 +2652,7 @@ func (x *FileActionCopy) String() string {
|
||||
func (*FileActionCopy) ProtoMessage() {}
|
||||
|
||||
func (x *FileActionCopy) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_github_com_moby_buildkit_solver_pb_ops_proto_msgTypes[31]
|
||||
mi := &file_github_com_moby_buildkit_solver_pb_ops_proto_msgTypes[32]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
@@ -2562,7 +2665,7 @@ func (x *FileActionCopy) ProtoReflect() protoreflect.Message {
|
||||
|
||||
// Deprecated: Use FileActionCopy.ProtoReflect.Descriptor instead.
|
||||
func (*FileActionCopy) Descriptor() ([]byte, []int) {
|
||||
return file_github_com_moby_buildkit_solver_pb_ops_proto_rawDescGZIP(), []int{31}
|
||||
return file_github_com_moby_buildkit_solver_pb_ops_proto_rawDescGZIP(), []int{32}
|
||||
}
|
||||
|
||||
func (x *FileActionCopy) GetSrc() string {
|
||||
@@ -2695,7 +2798,7 @@ type FileActionMkFile struct {
|
||||
|
||||
func (x *FileActionMkFile) Reset() {
|
||||
*x = FileActionMkFile{}
|
||||
mi := &file_github_com_moby_buildkit_solver_pb_ops_proto_msgTypes[32]
|
||||
mi := &file_github_com_moby_buildkit_solver_pb_ops_proto_msgTypes[33]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
@@ -2707,7 +2810,7 @@ func (x *FileActionMkFile) String() string {
|
||||
func (*FileActionMkFile) ProtoMessage() {}
|
||||
|
||||
func (x *FileActionMkFile) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_github_com_moby_buildkit_solver_pb_ops_proto_msgTypes[32]
|
||||
mi := &file_github_com_moby_buildkit_solver_pb_ops_proto_msgTypes[33]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
@@ -2720,7 +2823,7 @@ func (x *FileActionMkFile) ProtoReflect() protoreflect.Message {
|
||||
|
||||
// Deprecated: Use FileActionMkFile.ProtoReflect.Descriptor instead.
|
||||
func (*FileActionMkFile) Descriptor() ([]byte, []int) {
|
||||
return file_github_com_moby_buildkit_solver_pb_ops_proto_rawDescGZIP(), []int{32}
|
||||
return file_github_com_moby_buildkit_solver_pb_ops_proto_rawDescGZIP(), []int{33}
|
||||
}
|
||||
|
||||
func (x *FileActionMkFile) GetPath() string {
|
||||
@@ -2774,7 +2877,7 @@ type FileActionSymlink struct {
|
||||
|
||||
func (x *FileActionSymlink) Reset() {
|
||||
*x = FileActionSymlink{}
|
||||
mi := &file_github_com_moby_buildkit_solver_pb_ops_proto_msgTypes[33]
|
||||
mi := &file_github_com_moby_buildkit_solver_pb_ops_proto_msgTypes[34]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
@@ -2786,7 +2889,7 @@ func (x *FileActionSymlink) String() string {
|
||||
func (*FileActionSymlink) ProtoMessage() {}
|
||||
|
||||
func (x *FileActionSymlink) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_github_com_moby_buildkit_solver_pb_ops_proto_msgTypes[33]
|
||||
mi := &file_github_com_moby_buildkit_solver_pb_ops_proto_msgTypes[34]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
@@ -2799,7 +2902,7 @@ func (x *FileActionSymlink) ProtoReflect() protoreflect.Message {
|
||||
|
||||
// Deprecated: Use FileActionSymlink.ProtoReflect.Descriptor instead.
|
||||
func (*FileActionSymlink) Descriptor() ([]byte, []int) {
|
||||
return file_github_com_moby_buildkit_solver_pb_ops_proto_rawDescGZIP(), []int{33}
|
||||
return file_github_com_moby_buildkit_solver_pb_ops_proto_rawDescGZIP(), []int{34}
|
||||
}
|
||||
|
||||
func (x *FileActionSymlink) GetOldpath() string {
|
||||
@@ -2848,7 +2951,7 @@ type FileActionMkDir struct {
|
||||
|
||||
func (x *FileActionMkDir) Reset() {
|
||||
*x = FileActionMkDir{}
|
||||
mi := &file_github_com_moby_buildkit_solver_pb_ops_proto_msgTypes[34]
|
||||
mi := &file_github_com_moby_buildkit_solver_pb_ops_proto_msgTypes[35]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
@@ -2860,7 +2963,7 @@ func (x *FileActionMkDir) String() string {
|
||||
func (*FileActionMkDir) ProtoMessage() {}
|
||||
|
||||
func (x *FileActionMkDir) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_github_com_moby_buildkit_solver_pb_ops_proto_msgTypes[34]
|
||||
mi := &file_github_com_moby_buildkit_solver_pb_ops_proto_msgTypes[35]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
@@ -2873,7 +2976,7 @@ func (x *FileActionMkDir) ProtoReflect() protoreflect.Message {
|
||||
|
||||
// Deprecated: Use FileActionMkDir.ProtoReflect.Descriptor instead.
|
||||
func (*FileActionMkDir) Descriptor() ([]byte, []int) {
|
||||
return file_github_com_moby_buildkit_solver_pb_ops_proto_rawDescGZIP(), []int{34}
|
||||
return file_github_com_moby_buildkit_solver_pb_ops_proto_rawDescGZIP(), []int{35}
|
||||
}
|
||||
|
||||
func (x *FileActionMkDir) GetPath() string {
|
||||
@@ -2925,7 +3028,7 @@ type FileActionRm struct {
|
||||
|
||||
func (x *FileActionRm) Reset() {
|
||||
*x = FileActionRm{}
|
||||
mi := &file_github_com_moby_buildkit_solver_pb_ops_proto_msgTypes[35]
|
||||
mi := &file_github_com_moby_buildkit_solver_pb_ops_proto_msgTypes[36]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
@@ -2937,7 +3040,7 @@ func (x *FileActionRm) String() string {
|
||||
func (*FileActionRm) ProtoMessage() {}
|
||||
|
||||
func (x *FileActionRm) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_github_com_moby_buildkit_solver_pb_ops_proto_msgTypes[35]
|
||||
mi := &file_github_com_moby_buildkit_solver_pb_ops_proto_msgTypes[36]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
@@ -2950,7 +3053,7 @@ func (x *FileActionRm) ProtoReflect() protoreflect.Message {
|
||||
|
||||
// Deprecated: Use FileActionRm.ProtoReflect.Descriptor instead.
|
||||
func (*FileActionRm) Descriptor() ([]byte, []int) {
|
||||
return file_github_com_moby_buildkit_solver_pb_ops_proto_rawDescGZIP(), []int{35}
|
||||
return file_github_com_moby_buildkit_solver_pb_ops_proto_rawDescGZIP(), []int{36}
|
||||
}
|
||||
|
||||
func (x *FileActionRm) GetPath() string {
|
||||
@@ -2984,7 +3087,7 @@ type ChownOpt struct {
|
||||
|
||||
func (x *ChownOpt) Reset() {
|
||||
*x = ChownOpt{}
|
||||
mi := &file_github_com_moby_buildkit_solver_pb_ops_proto_msgTypes[36]
|
||||
mi := &file_github_com_moby_buildkit_solver_pb_ops_proto_msgTypes[37]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
@@ -2996,7 +3099,7 @@ func (x *ChownOpt) String() string {
|
||||
func (*ChownOpt) ProtoMessage() {}
|
||||
|
||||
func (x *ChownOpt) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_github_com_moby_buildkit_solver_pb_ops_proto_msgTypes[36]
|
||||
mi := &file_github_com_moby_buildkit_solver_pb_ops_proto_msgTypes[37]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
@@ -3009,7 +3112,7 @@ func (x *ChownOpt) ProtoReflect() protoreflect.Message {
|
||||
|
||||
// Deprecated: Use ChownOpt.ProtoReflect.Descriptor instead.
|
||||
func (*ChownOpt) Descriptor() ([]byte, []int) {
|
||||
return file_github_com_moby_buildkit_solver_pb_ops_proto_rawDescGZIP(), []int{36}
|
||||
return file_github_com_moby_buildkit_solver_pb_ops_proto_rawDescGZIP(), []int{37}
|
||||
}
|
||||
|
||||
func (x *ChownOpt) GetUser() *UserOpt {
|
||||
@@ -3041,7 +3144,7 @@ type UserOpt struct {
|
||||
|
||||
func (x *UserOpt) Reset() {
|
||||
*x = UserOpt{}
|
||||
mi := &file_github_com_moby_buildkit_solver_pb_ops_proto_msgTypes[37]
|
||||
mi := &file_github_com_moby_buildkit_solver_pb_ops_proto_msgTypes[38]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
@@ -3053,7 +3156,7 @@ func (x *UserOpt) String() string {
|
||||
func (*UserOpt) ProtoMessage() {}
|
||||
|
||||
func (x *UserOpt) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_github_com_moby_buildkit_solver_pb_ops_proto_msgTypes[37]
|
||||
mi := &file_github_com_moby_buildkit_solver_pb_ops_proto_msgTypes[38]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
@@ -3066,7 +3169,7 @@ func (x *UserOpt) ProtoReflect() protoreflect.Message {
|
||||
|
||||
// Deprecated: Use UserOpt.ProtoReflect.Descriptor instead.
|
||||
func (*UserOpt) Descriptor() ([]byte, []int) {
|
||||
return file_github_com_moby_buildkit_solver_pb_ops_proto_rawDescGZIP(), []int{37}
|
||||
return file_github_com_moby_buildkit_solver_pb_ops_proto_rawDescGZIP(), []int{38}
|
||||
}
|
||||
|
||||
func (x *UserOpt) GetUser() isUserOpt_User {
|
||||
@@ -3120,7 +3223,7 @@ type NamedUserOpt struct {
|
||||
|
||||
func (x *NamedUserOpt) Reset() {
|
||||
*x = NamedUserOpt{}
|
||||
mi := &file_github_com_moby_buildkit_solver_pb_ops_proto_msgTypes[38]
|
||||
mi := &file_github_com_moby_buildkit_solver_pb_ops_proto_msgTypes[39]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
@@ -3132,7 +3235,7 @@ func (x *NamedUserOpt) String() string {
|
||||
func (*NamedUserOpt) ProtoMessage() {}
|
||||
|
||||
func (x *NamedUserOpt) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_github_com_moby_buildkit_solver_pb_ops_proto_msgTypes[38]
|
||||
mi := &file_github_com_moby_buildkit_solver_pb_ops_proto_msgTypes[39]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
@@ -3145,7 +3248,7 @@ func (x *NamedUserOpt) ProtoReflect() protoreflect.Message {
|
||||
|
||||
// Deprecated: Use NamedUserOpt.ProtoReflect.Descriptor instead.
|
||||
func (*NamedUserOpt) Descriptor() ([]byte, []int) {
|
||||
return file_github_com_moby_buildkit_solver_pb_ops_proto_rawDescGZIP(), []int{38}
|
||||
return file_github_com_moby_buildkit_solver_pb_ops_proto_rawDescGZIP(), []int{39}
|
||||
}
|
||||
|
||||
func (x *NamedUserOpt) GetName() string {
|
||||
@@ -3171,7 +3274,7 @@ type MergeInput struct {
|
||||
|
||||
func (x *MergeInput) Reset() {
|
||||
*x = MergeInput{}
|
||||
mi := &file_github_com_moby_buildkit_solver_pb_ops_proto_msgTypes[39]
|
||||
mi := &file_github_com_moby_buildkit_solver_pb_ops_proto_msgTypes[40]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
@@ -3183,7 +3286,7 @@ func (x *MergeInput) String() string {
|
||||
func (*MergeInput) ProtoMessage() {}
|
||||
|
||||
func (x *MergeInput) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_github_com_moby_buildkit_solver_pb_ops_proto_msgTypes[39]
|
||||
mi := &file_github_com_moby_buildkit_solver_pb_ops_proto_msgTypes[40]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
@@ -3196,7 +3299,7 @@ func (x *MergeInput) ProtoReflect() protoreflect.Message {
|
||||
|
||||
// Deprecated: Use MergeInput.ProtoReflect.Descriptor instead.
|
||||
func (*MergeInput) Descriptor() ([]byte, []int) {
|
||||
return file_github_com_moby_buildkit_solver_pb_ops_proto_rawDescGZIP(), []int{39}
|
||||
return file_github_com_moby_buildkit_solver_pb_ops_proto_rawDescGZIP(), []int{40}
|
||||
}
|
||||
|
||||
func (x *MergeInput) GetInput() int64 {
|
||||
@@ -3215,7 +3318,7 @@ type MergeOp struct {
|
||||
|
||||
func (x *MergeOp) Reset() {
|
||||
*x = MergeOp{}
|
||||
mi := &file_github_com_moby_buildkit_solver_pb_ops_proto_msgTypes[40]
|
||||
mi := &file_github_com_moby_buildkit_solver_pb_ops_proto_msgTypes[41]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
@@ -3227,7 +3330,7 @@ func (x *MergeOp) String() string {
|
||||
func (*MergeOp) ProtoMessage() {}
|
||||
|
||||
func (x *MergeOp) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_github_com_moby_buildkit_solver_pb_ops_proto_msgTypes[40]
|
||||
mi := &file_github_com_moby_buildkit_solver_pb_ops_proto_msgTypes[41]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
@@ -3240,7 +3343,7 @@ func (x *MergeOp) ProtoReflect() protoreflect.Message {
|
||||
|
||||
// Deprecated: Use MergeOp.ProtoReflect.Descriptor instead.
|
||||
func (*MergeOp) Descriptor() ([]byte, []int) {
|
||||
return file_github_com_moby_buildkit_solver_pb_ops_proto_rawDescGZIP(), []int{40}
|
||||
return file_github_com_moby_buildkit_solver_pb_ops_proto_rawDescGZIP(), []int{41}
|
||||
}
|
||||
|
||||
func (x *MergeOp) GetInputs() []*MergeInput {
|
||||
@@ -3259,7 +3362,7 @@ type LowerDiffInput struct {
|
||||
|
||||
func (x *LowerDiffInput) Reset() {
|
||||
*x = LowerDiffInput{}
|
||||
mi := &file_github_com_moby_buildkit_solver_pb_ops_proto_msgTypes[41]
|
||||
mi := &file_github_com_moby_buildkit_solver_pb_ops_proto_msgTypes[42]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
@@ -3271,7 +3374,7 @@ func (x *LowerDiffInput) String() string {
|
||||
func (*LowerDiffInput) ProtoMessage() {}
|
||||
|
||||
func (x *LowerDiffInput) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_github_com_moby_buildkit_solver_pb_ops_proto_msgTypes[41]
|
||||
mi := &file_github_com_moby_buildkit_solver_pb_ops_proto_msgTypes[42]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
@@ -3284,7 +3387,7 @@ func (x *LowerDiffInput) ProtoReflect() protoreflect.Message {
|
||||
|
||||
// Deprecated: Use LowerDiffInput.ProtoReflect.Descriptor instead.
|
||||
func (*LowerDiffInput) Descriptor() ([]byte, []int) {
|
||||
return file_github_com_moby_buildkit_solver_pb_ops_proto_rawDescGZIP(), []int{41}
|
||||
return file_github_com_moby_buildkit_solver_pb_ops_proto_rawDescGZIP(), []int{42}
|
||||
}
|
||||
|
||||
func (x *LowerDiffInput) GetInput() int64 {
|
||||
@@ -3303,7 +3406,7 @@ type UpperDiffInput struct {
|
||||
|
||||
func (x *UpperDiffInput) Reset() {
|
||||
*x = UpperDiffInput{}
|
||||
mi := &file_github_com_moby_buildkit_solver_pb_ops_proto_msgTypes[42]
|
||||
mi := &file_github_com_moby_buildkit_solver_pb_ops_proto_msgTypes[43]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
@@ -3315,7 +3418,7 @@ func (x *UpperDiffInput) String() string {
|
||||
func (*UpperDiffInput) ProtoMessage() {}
|
||||
|
||||
func (x *UpperDiffInput) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_github_com_moby_buildkit_solver_pb_ops_proto_msgTypes[42]
|
||||
mi := &file_github_com_moby_buildkit_solver_pb_ops_proto_msgTypes[43]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
@@ -3328,7 +3431,7 @@ func (x *UpperDiffInput) ProtoReflect() protoreflect.Message {
|
||||
|
||||
// Deprecated: Use UpperDiffInput.ProtoReflect.Descriptor instead.
|
||||
func (*UpperDiffInput) Descriptor() ([]byte, []int) {
|
||||
return file_github_com_moby_buildkit_solver_pb_ops_proto_rawDescGZIP(), []int{42}
|
||||
return file_github_com_moby_buildkit_solver_pb_ops_proto_rawDescGZIP(), []int{43}
|
||||
}
|
||||
|
||||
func (x *UpperDiffInput) GetInput() int64 {
|
||||
@@ -3348,7 +3451,7 @@ type DiffOp struct {
|
||||
|
||||
func (x *DiffOp) Reset() {
|
||||
*x = DiffOp{}
|
||||
mi := &file_github_com_moby_buildkit_solver_pb_ops_proto_msgTypes[43]
|
||||
mi := &file_github_com_moby_buildkit_solver_pb_ops_proto_msgTypes[44]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
@@ -3360,7 +3463,7 @@ func (x *DiffOp) String() string {
|
||||
func (*DiffOp) ProtoMessage() {}
|
||||
|
||||
func (x *DiffOp) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_github_com_moby_buildkit_solver_pb_ops_proto_msgTypes[43]
|
||||
mi := &file_github_com_moby_buildkit_solver_pb_ops_proto_msgTypes[44]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
@@ -3373,7 +3476,7 @@ func (x *DiffOp) ProtoReflect() protoreflect.Message {
|
||||
|
||||
// Deprecated: Use DiffOp.ProtoReflect.Descriptor instead.
|
||||
func (*DiffOp) Descriptor() ([]byte, []int) {
|
||||
return file_github_com_moby_buildkit_solver_pb_ops_proto_rawDescGZIP(), []int{43}
|
||||
return file_github_com_moby_buildkit_solver_pb_ops_proto_rawDescGZIP(), []int{44}
|
||||
}
|
||||
|
||||
func (x *DiffOp) GetLower() *LowerDiffInput {
|
||||
@@ -3515,14 +3618,15 @@ const file_github_com_moby_buildkit_solver_pb_ops_proto_rawDesc = "" +
|
||||
"\x05value\x18\x02 \x01(\tR\x05value:\x028\x01\"\"\n" +
|
||||
"\n" +
|
||||
"BuildInput\x12\x14\n" +
|
||||
"\x05input\x18\x01 \x01(\x03R\x05input\"\x87\x03\n" +
|
||||
"\x05input\x18\x01 \x01(\x03R\x05input\"\xc4\x03\n" +
|
||||
"\n" +
|
||||
"OpMetadata\x12!\n" +
|
||||
"\fignore_cache\x18\x01 \x01(\bR\vignoreCache\x12A\n" +
|
||||
"\vdescription\x18\x02 \x03(\v2\x1f.pb.OpMetadata.DescriptionEntryR\vdescription\x122\n" +
|
||||
"\fexport_cache\x18\x04 \x01(\v2\x0f.pb.ExportCacheR\vexportCache\x12,\n" +
|
||||
"\x04caps\x18\x05 \x03(\v2\x18.pb.OpMetadata.CapsEntryR\x04caps\x128\n" +
|
||||
"\x0eprogress_group\x18\x06 \x01(\v2\x11.pb.ProgressGroupR\rprogressGroup\x1a>\n" +
|
||||
"\x0eprogress_group\x18\x06 \x01(\v2\x11.pb.ProgressGroupR\rprogressGroup\x12;\n" +
|
||||
"\x0flinux_resources\x18\a \x01(\v2\x12.pb.LinuxResourcesR\x0elinuxResources\x1a>\n" +
|
||||
"\x10DescriptionEntry\x12\x10\n" +
|
||||
"\x03key\x18\x01 \x01(\tR\x03key\x12\x14\n" +
|
||||
"\x05value\x18\x02 \x01(\tR\x05value:\x028\x01\x1a7\n" +
|
||||
@@ -3559,7 +3663,21 @@ const file_github_com_moby_buildkit_solver_pb_ops_proto_rawDesc = "" +
|
||||
"\rProgressGroup\x12\x0e\n" +
|
||||
"\x02id\x18\x01 \x01(\tR\x02id\x12\x12\n" +
|
||||
"\x04name\x18\x02 \x01(\tR\x04name\x12\x12\n" +
|
||||
"\x04weak\x18\x03 \x01(\bR\x04weak\"\x9f\x01\n" +
|
||||
"\x04weak\x18\x03 \x01(\bR\x04weak\"\xe0\x01\n" +
|
||||
"\x0eLinuxResources\x12\x16\n" +
|
||||
"\x06memory\x18\x01 \x01(\x03R\x06memory\x12\x1e\n" +
|
||||
"\n" +
|
||||
"memorySwap\x18\x02 \x01(\x03R\n" +
|
||||
"memorySwap\x12\x1c\n" +
|
||||
"\tcpuShares\x18\x03 \x01(\x04R\tcpuShares\x12\x1c\n" +
|
||||
"\tcpuPeriod\x18\x04 \x01(\x04R\tcpuPeriod\x12\x1a\n" +
|
||||
"\bcpuQuota\x18\x05 \x01(\x03R\bcpuQuota\x12\x1e\n" +
|
||||
"\n" +
|
||||
"cpusetCpus\x18\x06 \x01(\tR\n" +
|
||||
"cpusetCpus\x12\x1e\n" +
|
||||
"\n" +
|
||||
"cpusetMems\x18\a \x01(\tR\n" +
|
||||
"cpusetMems\"\x9f\x01\n" +
|
||||
"\bProxyEnv\x12\x1d\n" +
|
||||
"\n" +
|
||||
"http_proxy\x18\x01 \x01(\tR\thttpProxy\x12\x1f\n" +
|
||||
@@ -3691,7 +3809,7 @@ func file_github_com_moby_buildkit_solver_pb_ops_proto_rawDescGZIP() []byte {
|
||||
}
|
||||
|
||||
var file_github_com_moby_buildkit_solver_pb_ops_proto_enumTypes = make([]protoimpl.EnumInfo, 5)
|
||||
var file_github_com_moby_buildkit_solver_pb_ops_proto_msgTypes = make([]protoimpl.MessageInfo, 51)
|
||||
var file_github_com_moby_buildkit_solver_pb_ops_proto_msgTypes = make([]protoimpl.MessageInfo, 52)
|
||||
var file_github_com_moby_buildkit_solver_pb_ops_proto_goTypes = []any{
|
||||
(NetMode)(0), // 0: pb.NetMode
|
||||
(SecurityMode)(0), // 1: pb.SecurityMode
|
||||
@@ -3724,49 +3842,50 @@ var file_github_com_moby_buildkit_solver_pb_ops_proto_goTypes = []any{
|
||||
(*Position)(nil), // 28: pb.Position
|
||||
(*ExportCache)(nil), // 29: pb.ExportCache
|
||||
(*ProgressGroup)(nil), // 30: pb.ProgressGroup
|
||||
(*ProxyEnv)(nil), // 31: pb.ProxyEnv
|
||||
(*WorkerConstraints)(nil), // 32: pb.WorkerConstraints
|
||||
(*Definition)(nil), // 33: pb.Definition
|
||||
(*FileOp)(nil), // 34: pb.FileOp
|
||||
(*FileAction)(nil), // 35: pb.FileAction
|
||||
(*FileActionCopy)(nil), // 36: pb.FileActionCopy
|
||||
(*FileActionMkFile)(nil), // 37: pb.FileActionMkFile
|
||||
(*FileActionSymlink)(nil), // 38: pb.FileActionSymlink
|
||||
(*FileActionMkDir)(nil), // 39: pb.FileActionMkDir
|
||||
(*FileActionRm)(nil), // 40: pb.FileActionRm
|
||||
(*ChownOpt)(nil), // 41: pb.ChownOpt
|
||||
(*UserOpt)(nil), // 42: pb.UserOpt
|
||||
(*NamedUserOpt)(nil), // 43: pb.NamedUserOpt
|
||||
(*MergeInput)(nil), // 44: pb.MergeInput
|
||||
(*MergeOp)(nil), // 45: pb.MergeOp
|
||||
(*LowerDiffInput)(nil), // 46: pb.LowerDiffInput
|
||||
(*UpperDiffInput)(nil), // 47: pb.UpperDiffInput
|
||||
(*DiffOp)(nil), // 48: pb.DiffOp
|
||||
nil, // 49: pb.SourceOp.AttrsEntry
|
||||
nil, // 50: pb.BuildOp.InputsEntry
|
||||
nil, // 51: pb.BuildOp.AttrsEntry
|
||||
nil, // 52: pb.OpMetadata.DescriptionEntry
|
||||
nil, // 53: pb.OpMetadata.CapsEntry
|
||||
nil, // 54: pb.Source.LocationsEntry
|
||||
nil, // 55: pb.Definition.MetadataEntry
|
||||
(*LinuxResources)(nil), // 31: pb.LinuxResources
|
||||
(*ProxyEnv)(nil), // 32: pb.ProxyEnv
|
||||
(*WorkerConstraints)(nil), // 33: pb.WorkerConstraints
|
||||
(*Definition)(nil), // 34: pb.Definition
|
||||
(*FileOp)(nil), // 35: pb.FileOp
|
||||
(*FileAction)(nil), // 36: pb.FileAction
|
||||
(*FileActionCopy)(nil), // 37: pb.FileActionCopy
|
||||
(*FileActionMkFile)(nil), // 38: pb.FileActionMkFile
|
||||
(*FileActionSymlink)(nil), // 39: pb.FileActionSymlink
|
||||
(*FileActionMkDir)(nil), // 40: pb.FileActionMkDir
|
||||
(*FileActionRm)(nil), // 41: pb.FileActionRm
|
||||
(*ChownOpt)(nil), // 42: pb.ChownOpt
|
||||
(*UserOpt)(nil), // 43: pb.UserOpt
|
||||
(*NamedUserOpt)(nil), // 44: pb.NamedUserOpt
|
||||
(*MergeInput)(nil), // 45: pb.MergeInput
|
||||
(*MergeOp)(nil), // 46: pb.MergeOp
|
||||
(*LowerDiffInput)(nil), // 47: pb.LowerDiffInput
|
||||
(*UpperDiffInput)(nil), // 48: pb.UpperDiffInput
|
||||
(*DiffOp)(nil), // 49: pb.DiffOp
|
||||
nil, // 50: pb.SourceOp.AttrsEntry
|
||||
nil, // 51: pb.BuildOp.InputsEntry
|
||||
nil, // 52: pb.BuildOp.AttrsEntry
|
||||
nil, // 53: pb.OpMetadata.DescriptionEntry
|
||||
nil, // 54: pb.OpMetadata.CapsEntry
|
||||
nil, // 55: pb.Source.LocationsEntry
|
||||
nil, // 56: pb.Definition.MetadataEntry
|
||||
}
|
||||
var file_github_com_moby_buildkit_solver_pb_ops_proto_depIdxs = []int32{
|
||||
7, // 0: pb.Op.inputs:type_name -> pb.Input
|
||||
8, // 1: pb.Op.exec:type_name -> pb.ExecOp
|
||||
19, // 2: pb.Op.source:type_name -> pb.SourceOp
|
||||
34, // 3: pb.Op.file:type_name -> pb.FileOp
|
||||
35, // 3: pb.Op.file:type_name -> pb.FileOp
|
||||
20, // 4: pb.Op.build:type_name -> pb.BuildOp
|
||||
45, // 5: pb.Op.merge:type_name -> pb.MergeOp
|
||||
48, // 6: pb.Op.diff:type_name -> pb.DiffOp
|
||||
46, // 5: pb.Op.merge:type_name -> pb.MergeOp
|
||||
49, // 6: pb.Op.diff:type_name -> pb.DiffOp
|
||||
6, // 7: pb.Op.platform:type_name -> pb.Platform
|
||||
32, // 8: pb.Op.constraints:type_name -> pb.WorkerConstraints
|
||||
33, // 8: pb.Op.constraints:type_name -> pb.WorkerConstraints
|
||||
9, // 9: pb.ExecOp.meta:type_name -> pb.Meta
|
||||
14, // 10: pb.ExecOp.mounts:type_name -> pb.Mount
|
||||
0, // 11: pb.ExecOp.network:type_name -> pb.NetMode
|
||||
1, // 12: pb.ExecOp.security:type_name -> pb.SecurityMode
|
||||
12, // 13: pb.ExecOp.secretenv:type_name -> pb.SecretEnv
|
||||
13, // 14: pb.ExecOp.cdiDevices:type_name -> pb.CDIDevice
|
||||
31, // 15: pb.Meta.proxy_env:type_name -> pb.ProxyEnv
|
||||
32, // 15: pb.Meta.proxy_env:type_name -> pb.ProxyEnv
|
||||
10, // 16: pb.Meta.extraHosts:type_name -> pb.HostIP
|
||||
11, // 17: pb.Meta.ulimit:type_name -> pb.Ulimit
|
||||
2, // 18: pb.Mount.mountType:type_name -> pb.MountType
|
||||
@@ -3776,47 +3895,48 @@ var file_github_com_moby_buildkit_solver_pb_ops_proto_depIdxs = []int32{
|
||||
18, // 22: pb.Mount.SSHOpt:type_name -> pb.SSHOpt
|
||||
3, // 23: pb.Mount.contentCache:type_name -> pb.MountContentCache
|
||||
4, // 24: pb.CacheOpt.sharing:type_name -> pb.CacheSharingOpt
|
||||
49, // 25: pb.SourceOp.attrs:type_name -> pb.SourceOp.AttrsEntry
|
||||
50, // 26: pb.BuildOp.inputs:type_name -> pb.BuildOp.InputsEntry
|
||||
33, // 27: pb.BuildOp.def:type_name -> pb.Definition
|
||||
51, // 28: pb.BuildOp.attrs:type_name -> pb.BuildOp.AttrsEntry
|
||||
52, // 29: pb.OpMetadata.description:type_name -> pb.OpMetadata.DescriptionEntry
|
||||
50, // 25: pb.SourceOp.attrs:type_name -> pb.SourceOp.AttrsEntry
|
||||
51, // 26: pb.BuildOp.inputs:type_name -> pb.BuildOp.InputsEntry
|
||||
34, // 27: pb.BuildOp.def:type_name -> pb.Definition
|
||||
52, // 28: pb.BuildOp.attrs:type_name -> pb.BuildOp.AttrsEntry
|
||||
53, // 29: pb.OpMetadata.description:type_name -> pb.OpMetadata.DescriptionEntry
|
||||
29, // 30: pb.OpMetadata.export_cache:type_name -> pb.ExportCache
|
||||
53, // 31: pb.OpMetadata.caps:type_name -> pb.OpMetadata.CapsEntry
|
||||
54, // 31: pb.OpMetadata.caps:type_name -> pb.OpMetadata.CapsEntry
|
||||
30, // 32: pb.OpMetadata.progress_group:type_name -> pb.ProgressGroup
|
||||
54, // 33: pb.Source.locations:type_name -> pb.Source.LocationsEntry
|
||||
25, // 34: pb.Source.infos:type_name -> pb.SourceInfo
|
||||
26, // 35: pb.Locations.locations:type_name -> pb.Location
|
||||
33, // 36: pb.SourceInfo.definition:type_name -> pb.Definition
|
||||
27, // 37: pb.Location.ranges:type_name -> pb.Range
|
||||
28, // 38: pb.Range.start:type_name -> pb.Position
|
||||
28, // 39: pb.Range.end:type_name -> pb.Position
|
||||
55, // 40: pb.Definition.metadata:type_name -> pb.Definition.MetadataEntry
|
||||
23, // 41: pb.Definition.Source:type_name -> pb.Source
|
||||
35, // 42: pb.FileOp.actions:type_name -> pb.FileAction
|
||||
36, // 43: pb.FileAction.copy:type_name -> pb.FileActionCopy
|
||||
37, // 44: pb.FileAction.mkfile:type_name -> pb.FileActionMkFile
|
||||
39, // 45: pb.FileAction.mkdir:type_name -> pb.FileActionMkDir
|
||||
40, // 46: pb.FileAction.rm:type_name -> pb.FileActionRm
|
||||
38, // 47: pb.FileAction.symlink:type_name -> pb.FileActionSymlink
|
||||
41, // 48: pb.FileActionCopy.owner:type_name -> pb.ChownOpt
|
||||
41, // 49: pb.FileActionMkFile.owner:type_name -> pb.ChownOpt
|
||||
41, // 50: pb.FileActionSymlink.owner:type_name -> pb.ChownOpt
|
||||
41, // 51: pb.FileActionMkDir.owner:type_name -> pb.ChownOpt
|
||||
42, // 52: pb.ChownOpt.user:type_name -> pb.UserOpt
|
||||
42, // 53: pb.ChownOpt.group:type_name -> pb.UserOpt
|
||||
43, // 54: pb.UserOpt.byName:type_name -> pb.NamedUserOpt
|
||||
44, // 55: pb.MergeOp.inputs:type_name -> pb.MergeInput
|
||||
46, // 56: pb.DiffOp.lower:type_name -> pb.LowerDiffInput
|
||||
47, // 57: pb.DiffOp.upper:type_name -> pb.UpperDiffInput
|
||||
21, // 58: pb.BuildOp.InputsEntry.value:type_name -> pb.BuildInput
|
||||
24, // 59: pb.Source.LocationsEntry.value:type_name -> pb.Locations
|
||||
22, // 60: pb.Definition.MetadataEntry.value:type_name -> pb.OpMetadata
|
||||
61, // [61:61] is the sub-list for method output_type
|
||||
61, // [61:61] is the sub-list for method input_type
|
||||
61, // [61:61] is the sub-list for extension type_name
|
||||
61, // [61:61] is the sub-list for extension extendee
|
||||
0, // [0:61] is the sub-list for field type_name
|
||||
31, // 33: pb.OpMetadata.linux_resources:type_name -> pb.LinuxResources
|
||||
55, // 34: pb.Source.locations:type_name -> pb.Source.LocationsEntry
|
||||
25, // 35: pb.Source.infos:type_name -> pb.SourceInfo
|
||||
26, // 36: pb.Locations.locations:type_name -> pb.Location
|
||||
34, // 37: pb.SourceInfo.definition:type_name -> pb.Definition
|
||||
27, // 38: pb.Location.ranges:type_name -> pb.Range
|
||||
28, // 39: pb.Range.start:type_name -> pb.Position
|
||||
28, // 40: pb.Range.end:type_name -> pb.Position
|
||||
56, // 41: pb.Definition.metadata:type_name -> pb.Definition.MetadataEntry
|
||||
23, // 42: pb.Definition.Source:type_name -> pb.Source
|
||||
36, // 43: pb.FileOp.actions:type_name -> pb.FileAction
|
||||
37, // 44: pb.FileAction.copy:type_name -> pb.FileActionCopy
|
||||
38, // 45: pb.FileAction.mkfile:type_name -> pb.FileActionMkFile
|
||||
40, // 46: pb.FileAction.mkdir:type_name -> pb.FileActionMkDir
|
||||
41, // 47: pb.FileAction.rm:type_name -> pb.FileActionRm
|
||||
39, // 48: pb.FileAction.symlink:type_name -> pb.FileActionSymlink
|
||||
42, // 49: pb.FileActionCopy.owner:type_name -> pb.ChownOpt
|
||||
42, // 50: pb.FileActionMkFile.owner:type_name -> pb.ChownOpt
|
||||
42, // 51: pb.FileActionSymlink.owner:type_name -> pb.ChownOpt
|
||||
42, // 52: pb.FileActionMkDir.owner:type_name -> pb.ChownOpt
|
||||
43, // 53: pb.ChownOpt.user:type_name -> pb.UserOpt
|
||||
43, // 54: pb.ChownOpt.group:type_name -> pb.UserOpt
|
||||
44, // 55: pb.UserOpt.byName:type_name -> pb.NamedUserOpt
|
||||
45, // 56: pb.MergeOp.inputs:type_name -> pb.MergeInput
|
||||
47, // 57: pb.DiffOp.lower:type_name -> pb.LowerDiffInput
|
||||
48, // 58: pb.DiffOp.upper:type_name -> pb.UpperDiffInput
|
||||
21, // 59: pb.BuildOp.InputsEntry.value:type_name -> pb.BuildInput
|
||||
24, // 60: pb.Source.LocationsEntry.value:type_name -> pb.Locations
|
||||
22, // 61: pb.Definition.MetadataEntry.value:type_name -> pb.OpMetadata
|
||||
62, // [62:62] is the sub-list for method output_type
|
||||
62, // [62:62] is the sub-list for method input_type
|
||||
62, // [62:62] is the sub-list for extension type_name
|
||||
62, // [62:62] is the sub-list for extension extendee
|
||||
0, // [0:62] is the sub-list for field type_name
|
||||
}
|
||||
|
||||
func init() { file_github_com_moby_buildkit_solver_pb_ops_proto_init() }
|
||||
@@ -3832,14 +3952,14 @@ func file_github_com_moby_buildkit_solver_pb_ops_proto_init() {
|
||||
(*Op_Merge)(nil),
|
||||
(*Op_Diff)(nil),
|
||||
}
|
||||
file_github_com_moby_buildkit_solver_pb_ops_proto_msgTypes[30].OneofWrappers = []any{
|
||||
file_github_com_moby_buildkit_solver_pb_ops_proto_msgTypes[31].OneofWrappers = []any{
|
||||
(*FileAction_Copy)(nil),
|
||||
(*FileAction_Mkfile)(nil),
|
||||
(*FileAction_Mkdir)(nil),
|
||||
(*FileAction_Rm)(nil),
|
||||
(*FileAction_Symlink)(nil),
|
||||
}
|
||||
file_github_com_moby_buildkit_solver_pb_ops_proto_msgTypes[37].OneofWrappers = []any{
|
||||
file_github_com_moby_buildkit_solver_pb_ops_proto_msgTypes[38].OneofWrappers = []any{
|
||||
(*UserOpt_ByName)(nil),
|
||||
(*UserOpt_ByID)(nil),
|
||||
}
|
||||
@@ -3849,7 +3969,7 @@ func file_github_com_moby_buildkit_solver_pb_ops_proto_init() {
|
||||
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
||||
RawDescriptor: unsafe.Slice(unsafe.StringData(file_github_com_moby_buildkit_solver_pb_ops_proto_rawDesc), len(file_github_com_moby_buildkit_solver_pb_ops_proto_rawDesc)),
|
||||
NumEnums: 5,
|
||||
NumMessages: 51,
|
||||
NumMessages: 52,
|
||||
NumExtensions: 0,
|
||||
NumServices: 0,
|
||||
},
|
||||
|
||||
@@ -228,6 +228,8 @@ message OpMetadata {
|
||||
map<string, bool> caps = 5;
|
||||
|
||||
ProgressGroup progress_group = 6;
|
||||
|
||||
LinuxResources linux_resources = 7;
|
||||
}
|
||||
|
||||
// Source is a source mapping description for a file
|
||||
@@ -277,6 +279,19 @@ message ProgressGroup {
|
||||
bool weak = 3;
|
||||
}
|
||||
|
||||
// LinuxResources specifies cgroup resource limits for containers.
|
||||
// Used in OpMetadata to set per-step resource constraints without
|
||||
// affecting the cache key.
|
||||
message LinuxResources {
|
||||
int64 memory = 1; // memory limit in bytes
|
||||
int64 memorySwap = 2; // memory+swap limit in bytes
|
||||
uint64 cpuShares = 3; // relative CPU weight
|
||||
uint64 cpuPeriod = 4; // CFS period in microseconds
|
||||
int64 cpuQuota = 5; // CFS quota in microseconds
|
||||
string cpusetCpus = 6; // CPU affinity (e.g., "0-3")
|
||||
string cpusetMems = 7; // memory node affinity (e.g., "0,1")
|
||||
}
|
||||
|
||||
message ProxyEnv {
|
||||
string http_proxy = 1;
|
||||
string https_proxy = 2;
|
||||
|
||||
@@ -495,6 +495,7 @@ func (m *OpMetadata) CloneVT() *OpMetadata {
|
||||
r.IgnoreCache = m.IgnoreCache
|
||||
r.ExportCache = m.ExportCache.CloneVT()
|
||||
r.ProgressGroup = m.ProgressGroup.CloneVT()
|
||||
r.LinuxResources = m.LinuxResources.CloneVT()
|
||||
if rhs := m.Description; rhs != nil {
|
||||
tmpContainer := make(map[string]string, len(rhs))
|
||||
for k, v := range rhs {
|
||||
@@ -693,6 +694,29 @@ func (m *ProgressGroup) CloneMessageVT() proto.Message {
|
||||
return m.CloneVT()
|
||||
}
|
||||
|
||||
func (m *LinuxResources) CloneVT() *LinuxResources {
|
||||
if m == nil {
|
||||
return (*LinuxResources)(nil)
|
||||
}
|
||||
r := new(LinuxResources)
|
||||
r.Memory = m.Memory
|
||||
r.MemorySwap = m.MemorySwap
|
||||
r.CpuShares = m.CpuShares
|
||||
r.CpuPeriod = m.CpuPeriod
|
||||
r.CpuQuota = m.CpuQuota
|
||||
r.CpusetCpus = m.CpusetCpus
|
||||
r.CpusetMems = m.CpusetMems
|
||||
if len(m.unknownFields) > 0 {
|
||||
r.unknownFields = make([]byte, len(m.unknownFields))
|
||||
copy(r.unknownFields, m.unknownFields)
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
||||
func (m *LinuxResources) CloneMessageVT() proto.Message {
|
||||
return m.CloneVT()
|
||||
}
|
||||
|
||||
func (m *ProxyEnv) CloneVT() *ProxyEnv {
|
||||
if m == nil {
|
||||
return (*ProxyEnv)(nil)
|
||||
@@ -1973,6 +1997,9 @@ func (this *OpMetadata) EqualVT(that *OpMetadata) bool {
|
||||
if !this.ProgressGroup.EqualVT(that.ProgressGroup) {
|
||||
return false
|
||||
}
|
||||
if !this.LinuxResources.EqualVT(that.LinuxResources) {
|
||||
return false
|
||||
}
|
||||
return string(this.unknownFields) == string(that.unknownFields)
|
||||
}
|
||||
|
||||
@@ -2221,6 +2248,43 @@ func (this *ProgressGroup) EqualMessageVT(thatMsg proto.Message) bool {
|
||||
}
|
||||
return this.EqualVT(that)
|
||||
}
|
||||
func (this *LinuxResources) EqualVT(that *LinuxResources) bool {
|
||||
if this == that {
|
||||
return true
|
||||
} else if this == nil || that == nil {
|
||||
return false
|
||||
}
|
||||
if this.Memory != that.Memory {
|
||||
return false
|
||||
}
|
||||
if this.MemorySwap != that.MemorySwap {
|
||||
return false
|
||||
}
|
||||
if this.CpuShares != that.CpuShares {
|
||||
return false
|
||||
}
|
||||
if this.CpuPeriod != that.CpuPeriod {
|
||||
return false
|
||||
}
|
||||
if this.CpuQuota != that.CpuQuota {
|
||||
return false
|
||||
}
|
||||
if this.CpusetCpus != that.CpusetCpus {
|
||||
return false
|
||||
}
|
||||
if this.CpusetMems != that.CpusetMems {
|
||||
return false
|
||||
}
|
||||
return string(this.unknownFields) == string(that.unknownFields)
|
||||
}
|
||||
|
||||
func (this *LinuxResources) EqualMessageVT(thatMsg proto.Message) bool {
|
||||
that, ok := thatMsg.(*LinuxResources)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
return this.EqualVT(that)
|
||||
}
|
||||
func (this *ProxyEnv) EqualVT(that *ProxyEnv) bool {
|
||||
if this == that {
|
||||
return true
|
||||
@@ -4270,6 +4334,16 @@ func (m *OpMetadata) MarshalToSizedBufferVT(dAtA []byte) (int, error) {
|
||||
i -= len(m.unknownFields)
|
||||
copy(dAtA[i:], m.unknownFields)
|
||||
}
|
||||
if m.LinuxResources != nil {
|
||||
size, err := m.LinuxResources.MarshalToSizedBufferVT(dAtA[:i])
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
i -= size
|
||||
i = protohelpers.EncodeVarint(dAtA, i, uint64(size))
|
||||
i--
|
||||
dAtA[i] = 0x3a
|
||||
}
|
||||
if m.ProgressGroup != nil {
|
||||
size, err := m.ProgressGroup.MarshalToSizedBufferVT(dAtA[:i])
|
||||
if err != nil {
|
||||
@@ -4766,6 +4840,78 @@ func (m *ProgressGroup) MarshalToSizedBufferVT(dAtA []byte) (int, error) {
|
||||
return len(dAtA) - i, nil
|
||||
}
|
||||
|
||||
func (m *LinuxResources) MarshalVT() (dAtA []byte, err error) {
|
||||
if m == nil {
|
||||
return nil, nil
|
||||
}
|
||||
size := m.SizeVT()
|
||||
dAtA = make([]byte, size)
|
||||
n, err := m.MarshalToSizedBufferVT(dAtA[:size])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return dAtA[:n], nil
|
||||
}
|
||||
|
||||
func (m *LinuxResources) MarshalToVT(dAtA []byte) (int, error) {
|
||||
size := m.SizeVT()
|
||||
return m.MarshalToSizedBufferVT(dAtA[:size])
|
||||
}
|
||||
|
||||
func (m *LinuxResources) MarshalToSizedBufferVT(dAtA []byte) (int, error) {
|
||||
if m == nil {
|
||||
return 0, nil
|
||||
}
|
||||
i := len(dAtA)
|
||||
_ = i
|
||||
var l int
|
||||
_ = l
|
||||
if m.unknownFields != nil {
|
||||
i -= len(m.unknownFields)
|
||||
copy(dAtA[i:], m.unknownFields)
|
||||
}
|
||||
if len(m.CpusetMems) > 0 {
|
||||
i -= len(m.CpusetMems)
|
||||
copy(dAtA[i:], m.CpusetMems)
|
||||
i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.CpusetMems)))
|
||||
i--
|
||||
dAtA[i] = 0x3a
|
||||
}
|
||||
if len(m.CpusetCpus) > 0 {
|
||||
i -= len(m.CpusetCpus)
|
||||
copy(dAtA[i:], m.CpusetCpus)
|
||||
i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.CpusetCpus)))
|
||||
i--
|
||||
dAtA[i] = 0x32
|
||||
}
|
||||
if m.CpuQuota != 0 {
|
||||
i = protohelpers.EncodeVarint(dAtA, i, uint64(m.CpuQuota))
|
||||
i--
|
||||
dAtA[i] = 0x28
|
||||
}
|
||||
if m.CpuPeriod != 0 {
|
||||
i = protohelpers.EncodeVarint(dAtA, i, uint64(m.CpuPeriod))
|
||||
i--
|
||||
dAtA[i] = 0x20
|
||||
}
|
||||
if m.CpuShares != 0 {
|
||||
i = protohelpers.EncodeVarint(dAtA, i, uint64(m.CpuShares))
|
||||
i--
|
||||
dAtA[i] = 0x18
|
||||
}
|
||||
if m.MemorySwap != 0 {
|
||||
i = protohelpers.EncodeVarint(dAtA, i, uint64(m.MemorySwap))
|
||||
i--
|
||||
dAtA[i] = 0x10
|
||||
}
|
||||
if m.Memory != 0 {
|
||||
i = protohelpers.EncodeVarint(dAtA, i, uint64(m.Memory))
|
||||
i--
|
||||
dAtA[i] = 0x8
|
||||
}
|
||||
return len(dAtA) - i, nil
|
||||
}
|
||||
|
||||
func (m *ProxyEnv) MarshalVT() (dAtA []byte, err error) {
|
||||
if m == nil {
|
||||
return nil, nil
|
||||
@@ -6565,6 +6711,10 @@ func (m *OpMetadata) SizeVT() (n int) {
|
||||
l = m.ProgressGroup.SizeVT()
|
||||
n += 1 + l + protohelpers.SizeOfVarint(uint64(l))
|
||||
}
|
||||
if m.LinuxResources != nil {
|
||||
l = m.LinuxResources.SizeVT()
|
||||
n += 1 + l + protohelpers.SizeOfVarint(uint64(l))
|
||||
}
|
||||
n += len(m.unknownFields)
|
||||
return n
|
||||
}
|
||||
@@ -6727,6 +6877,39 @@ func (m *ProgressGroup) SizeVT() (n int) {
|
||||
return n
|
||||
}
|
||||
|
||||
func (m *LinuxResources) SizeVT() (n int) {
|
||||
if m == nil {
|
||||
return 0
|
||||
}
|
||||
var l int
|
||||
_ = l
|
||||
if m.Memory != 0 {
|
||||
n += 1 + protohelpers.SizeOfVarint(uint64(m.Memory))
|
||||
}
|
||||
if m.MemorySwap != 0 {
|
||||
n += 1 + protohelpers.SizeOfVarint(uint64(m.MemorySwap))
|
||||
}
|
||||
if m.CpuShares != 0 {
|
||||
n += 1 + protohelpers.SizeOfVarint(uint64(m.CpuShares))
|
||||
}
|
||||
if m.CpuPeriod != 0 {
|
||||
n += 1 + protohelpers.SizeOfVarint(uint64(m.CpuPeriod))
|
||||
}
|
||||
if m.CpuQuota != 0 {
|
||||
n += 1 + protohelpers.SizeOfVarint(uint64(m.CpuQuota))
|
||||
}
|
||||
l = len(m.CpusetCpus)
|
||||
if l > 0 {
|
||||
n += 1 + l + protohelpers.SizeOfVarint(uint64(l))
|
||||
}
|
||||
l = len(m.CpusetMems)
|
||||
if l > 0 {
|
||||
n += 1 + l + protohelpers.SizeOfVarint(uint64(l))
|
||||
}
|
||||
n += len(m.unknownFields)
|
||||
return n
|
||||
}
|
||||
|
||||
func (m *ProxyEnv) SizeVT() (n int) {
|
||||
if m == nil {
|
||||
return 0
|
||||
@@ -10973,6 +11156,42 @@ func (m *OpMetadata) UnmarshalVT(dAtA []byte) error {
|
||||
return err
|
||||
}
|
||||
iNdEx = postIndex
|
||||
case 7:
|
||||
if wireType != 2 {
|
||||
return fmt.Errorf("proto: wrong wireType = %d for field LinuxResources", wireType)
|
||||
}
|
||||
var msglen int
|
||||
for shift := uint(0); ; shift += 7 {
|
||||
if shift >= 64 {
|
||||
return protohelpers.ErrIntOverflow
|
||||
}
|
||||
if iNdEx >= l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
b := dAtA[iNdEx]
|
||||
iNdEx++
|
||||
msglen |= int(b&0x7F) << shift
|
||||
if b < 0x80 {
|
||||
break
|
||||
}
|
||||
}
|
||||
if msglen < 0 {
|
||||
return protohelpers.ErrInvalidLength
|
||||
}
|
||||
postIndex := iNdEx + msglen
|
||||
if postIndex < 0 {
|
||||
return protohelpers.ErrInvalidLength
|
||||
}
|
||||
if postIndex > l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
if m.LinuxResources == nil {
|
||||
m.LinuxResources = &LinuxResources{}
|
||||
}
|
||||
if err := m.LinuxResources.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil {
|
||||
return err
|
||||
}
|
||||
iNdEx = postIndex
|
||||
default:
|
||||
iNdEx = preIndex
|
||||
skippy, err := protohelpers.Skip(dAtA[iNdEx:])
|
||||
@@ -12001,6 +12220,216 @@ func (m *ProgressGroup) UnmarshalVT(dAtA []byte) error {
|
||||
}
|
||||
return nil
|
||||
}
|
||||
func (m *LinuxResources) UnmarshalVT(dAtA []byte) error {
|
||||
l := len(dAtA)
|
||||
iNdEx := 0
|
||||
for iNdEx < l {
|
||||
preIndex := iNdEx
|
||||
var wire uint64
|
||||
for shift := uint(0); ; shift += 7 {
|
||||
if shift >= 64 {
|
||||
return protohelpers.ErrIntOverflow
|
||||
}
|
||||
if iNdEx >= l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
b := dAtA[iNdEx]
|
||||
iNdEx++
|
||||
wire |= uint64(b&0x7F) << shift
|
||||
if b < 0x80 {
|
||||
break
|
||||
}
|
||||
}
|
||||
fieldNum := int32(wire >> 3)
|
||||
wireType := int(wire & 0x7)
|
||||
if wireType == 4 {
|
||||
return fmt.Errorf("proto: LinuxResources: wiretype end group for non-group")
|
||||
}
|
||||
if fieldNum <= 0 {
|
||||
return fmt.Errorf("proto: LinuxResources: illegal tag %d (wire type %d)", fieldNum, wire)
|
||||
}
|
||||
switch fieldNum {
|
||||
case 1:
|
||||
if wireType != 0 {
|
||||
return fmt.Errorf("proto: wrong wireType = %d for field Memory", wireType)
|
||||
}
|
||||
m.Memory = 0
|
||||
for shift := uint(0); ; shift += 7 {
|
||||
if shift >= 64 {
|
||||
return protohelpers.ErrIntOverflow
|
||||
}
|
||||
if iNdEx >= l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
b := dAtA[iNdEx]
|
||||
iNdEx++
|
||||
m.Memory |= int64(b&0x7F) << shift
|
||||
if b < 0x80 {
|
||||
break
|
||||
}
|
||||
}
|
||||
case 2:
|
||||
if wireType != 0 {
|
||||
return fmt.Errorf("proto: wrong wireType = %d for field MemorySwap", wireType)
|
||||
}
|
||||
m.MemorySwap = 0
|
||||
for shift := uint(0); ; shift += 7 {
|
||||
if shift >= 64 {
|
||||
return protohelpers.ErrIntOverflow
|
||||
}
|
||||
if iNdEx >= l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
b := dAtA[iNdEx]
|
||||
iNdEx++
|
||||
m.MemorySwap |= int64(b&0x7F) << shift
|
||||
if b < 0x80 {
|
||||
break
|
||||
}
|
||||
}
|
||||
case 3:
|
||||
if wireType != 0 {
|
||||
return fmt.Errorf("proto: wrong wireType = %d for field CpuShares", wireType)
|
||||
}
|
||||
m.CpuShares = 0
|
||||
for shift := uint(0); ; shift += 7 {
|
||||
if shift >= 64 {
|
||||
return protohelpers.ErrIntOverflow
|
||||
}
|
||||
if iNdEx >= l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
b := dAtA[iNdEx]
|
||||
iNdEx++
|
||||
m.CpuShares |= uint64(b&0x7F) << shift
|
||||
if b < 0x80 {
|
||||
break
|
||||
}
|
||||
}
|
||||
case 4:
|
||||
if wireType != 0 {
|
||||
return fmt.Errorf("proto: wrong wireType = %d for field CpuPeriod", wireType)
|
||||
}
|
||||
m.CpuPeriod = 0
|
||||
for shift := uint(0); ; shift += 7 {
|
||||
if shift >= 64 {
|
||||
return protohelpers.ErrIntOverflow
|
||||
}
|
||||
if iNdEx >= l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
b := dAtA[iNdEx]
|
||||
iNdEx++
|
||||
m.CpuPeriod |= uint64(b&0x7F) << shift
|
||||
if b < 0x80 {
|
||||
break
|
||||
}
|
||||
}
|
||||
case 5:
|
||||
if wireType != 0 {
|
||||
return fmt.Errorf("proto: wrong wireType = %d for field CpuQuota", wireType)
|
||||
}
|
||||
m.CpuQuota = 0
|
||||
for shift := uint(0); ; shift += 7 {
|
||||
if shift >= 64 {
|
||||
return protohelpers.ErrIntOverflow
|
||||
}
|
||||
if iNdEx >= l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
b := dAtA[iNdEx]
|
||||
iNdEx++
|
||||
m.CpuQuota |= int64(b&0x7F) << shift
|
||||
if b < 0x80 {
|
||||
break
|
||||
}
|
||||
}
|
||||
case 6:
|
||||
if wireType != 2 {
|
||||
return fmt.Errorf("proto: wrong wireType = %d for field CpusetCpus", wireType)
|
||||
}
|
||||
var stringLen uint64
|
||||
for shift := uint(0); ; shift += 7 {
|
||||
if shift >= 64 {
|
||||
return protohelpers.ErrIntOverflow
|
||||
}
|
||||
if iNdEx >= l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
b := dAtA[iNdEx]
|
||||
iNdEx++
|
||||
stringLen |= uint64(b&0x7F) << shift
|
||||
if b < 0x80 {
|
||||
break
|
||||
}
|
||||
}
|
||||
intStringLen := int(stringLen)
|
||||
if intStringLen < 0 {
|
||||
return protohelpers.ErrInvalidLength
|
||||
}
|
||||
postIndex := iNdEx + intStringLen
|
||||
if postIndex < 0 {
|
||||
return protohelpers.ErrInvalidLength
|
||||
}
|
||||
if postIndex > l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
m.CpusetCpus = string(dAtA[iNdEx:postIndex])
|
||||
iNdEx = postIndex
|
||||
case 7:
|
||||
if wireType != 2 {
|
||||
return fmt.Errorf("proto: wrong wireType = %d for field CpusetMems", wireType)
|
||||
}
|
||||
var stringLen uint64
|
||||
for shift := uint(0); ; shift += 7 {
|
||||
if shift >= 64 {
|
||||
return protohelpers.ErrIntOverflow
|
||||
}
|
||||
if iNdEx >= l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
b := dAtA[iNdEx]
|
||||
iNdEx++
|
||||
stringLen |= uint64(b&0x7F) << shift
|
||||
if b < 0x80 {
|
||||
break
|
||||
}
|
||||
}
|
||||
intStringLen := int(stringLen)
|
||||
if intStringLen < 0 {
|
||||
return protohelpers.ErrInvalidLength
|
||||
}
|
||||
postIndex := iNdEx + intStringLen
|
||||
if postIndex < 0 {
|
||||
return protohelpers.ErrInvalidLength
|
||||
}
|
||||
if postIndex > l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
m.CpusetMems = string(dAtA[iNdEx:postIndex])
|
||||
iNdEx = postIndex
|
||||
default:
|
||||
iNdEx = preIndex
|
||||
skippy, err := protohelpers.Skip(dAtA[iNdEx:])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if (skippy < 0) || (iNdEx+skippy) < 0 {
|
||||
return protohelpers.ErrInvalidLength
|
||||
}
|
||||
if (iNdEx + skippy) > l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...)
|
||||
iNdEx += skippy
|
||||
}
|
||||
}
|
||||
|
||||
if iNdEx > l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
return nil
|
||||
}
|
||||
func (m *ProxyEnv) UnmarshalVT(dAtA []byte) error {
|
||||
l := len(dAtA)
|
||||
iNdEx := 0
|
||||
|
||||
@@ -56,6 +56,13 @@ type VertexOptions struct {
|
||||
ExportCache *bool
|
||||
// WorkerConstraint
|
||||
ProgressGroup *pb.ProgressGroup
|
||||
Metadata VertexMetadata
|
||||
}
|
||||
|
||||
// VertexMetadata is opaque per-vertex metadata that gets merged when the same
|
||||
// vertex is shared by multiple jobs.
|
||||
type VertexMetadata interface {
|
||||
Merge(other VertexMetadata) VertexMetadata
|
||||
}
|
||||
|
||||
// Result is an abstract return value for a solve
|
||||
|
||||
@@ -0,0 +1,90 @@
|
||||
// Package cpuset parses and formats cpuset strings like "0-3,5,7-9".
|
||||
package cpuset
|
||||
|
||||
import (
|
||||
"slices"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
// MaxCPU is the largest CPU/memory-node index Parse accepts. It bounds the set
|
||||
// Parse allocates so an untrusted value like "0-999999999" cannot exhaust memory.
|
||||
const MaxCPU = 8192
|
||||
|
||||
// Parse parses a cpuset string into a set of integers. An empty input returns
|
||||
// an empty set with no error. Elements must be in the range [0, MaxCPU].
|
||||
func Parse(s string) (map[int]struct{}, error) {
|
||||
out := make(map[int]struct{})
|
||||
for part := range strings.SplitSeq(s, ",") {
|
||||
part = strings.TrimSpace(part)
|
||||
if part == "" {
|
||||
continue
|
||||
}
|
||||
lo, hi, found := strings.Cut(part, "-")
|
||||
if !found {
|
||||
n, err := strconv.Atoi(part)
|
||||
if err != nil || n < 0 {
|
||||
return nil, errors.Errorf("invalid cpuset element %q", part)
|
||||
}
|
||||
if n > MaxCPU {
|
||||
return nil, errors.Errorf("cpuset element %q exceeds maximum %d", part, MaxCPU)
|
||||
}
|
||||
out[n] = struct{}{}
|
||||
continue
|
||||
}
|
||||
loN, err1 := strconv.Atoi(strings.TrimSpace(lo))
|
||||
hiN, err2 := strconv.Atoi(strings.TrimSpace(hi))
|
||||
if err1 != nil || err2 != nil || loN < 0 || hiN < loN {
|
||||
return nil, errors.Errorf("invalid cpuset range %q", part)
|
||||
}
|
||||
if hiN > MaxCPU {
|
||||
return nil, errors.Errorf("cpuset range %q exceeds maximum %d", part, MaxCPU)
|
||||
}
|
||||
for i := loN; i <= hiN; i++ {
|
||||
out[i] = struct{}{}
|
||||
}
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
// Validate returns an error if s is not a syntactically valid cpuset string.
|
||||
// An empty string is valid (means "unset").
|
||||
func Validate(s string) error {
|
||||
_, err := Parse(s)
|
||||
return err
|
||||
}
|
||||
|
||||
// Format emits a sorted, range-collapsed cpuset string from a set.
|
||||
func Format(set map[int]struct{}) string {
|
||||
if len(set) == 0 {
|
||||
return ""
|
||||
}
|
||||
vals := make([]int, 0, len(set))
|
||||
for v := range set {
|
||||
vals = append(vals, v)
|
||||
}
|
||||
slices.Sort(vals)
|
||||
|
||||
var sb strings.Builder
|
||||
i := 0
|
||||
for i < len(vals) {
|
||||
j := i
|
||||
for j+1 < len(vals) && vals[j+1] == vals[j]+1 {
|
||||
j++
|
||||
}
|
||||
if sb.Len() > 0 {
|
||||
sb.WriteByte(',')
|
||||
}
|
||||
if i == j {
|
||||
sb.WriteString(strconv.Itoa(vals[i]))
|
||||
} else {
|
||||
sb.WriteString(strconv.Itoa(vals[i]))
|
||||
sb.WriteByte('-')
|
||||
sb.WriteString(strconv.Itoa(vals[j]))
|
||||
}
|
||||
i = j + 1
|
||||
}
|
||||
return sb.String()
|
||||
}
|
||||
@@ -0,0 +1,115 @@
|
||||
package cpuset
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestParse(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
t.Run("empty is valid", func(t *testing.T) {
|
||||
set, err := Parse("")
|
||||
require.NoError(t, err)
|
||||
require.Empty(t, set)
|
||||
})
|
||||
|
||||
t.Run("single value", func(t *testing.T) {
|
||||
set, err := Parse("3")
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, map[int]struct{}{3: {}}, set)
|
||||
})
|
||||
|
||||
t.Run("range expands inclusively", func(t *testing.T) {
|
||||
set, err := Parse("0-3")
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, map[int]struct{}{0: {}, 1: {}, 2: {}, 3: {}}, set)
|
||||
})
|
||||
|
||||
t.Run("comma list and range mix", func(t *testing.T) {
|
||||
set, err := Parse("0,2-3,5")
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, map[int]struct{}{0: {}, 2: {}, 3: {}, 5: {}}, set)
|
||||
})
|
||||
|
||||
t.Run("whitespace tolerated", func(t *testing.T) {
|
||||
set, err := Parse(" 0 , 2 - 3 ")
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, map[int]struct{}{0: {}, 2: {}, 3: {}}, set)
|
||||
})
|
||||
|
||||
t.Run("negative rejected", func(t *testing.T) {
|
||||
_, err := Parse("-1")
|
||||
require.Error(t, err)
|
||||
})
|
||||
|
||||
t.Run("non-numeric rejected", func(t *testing.T) {
|
||||
_, err := Parse("abc")
|
||||
require.Error(t, err)
|
||||
})
|
||||
|
||||
t.Run("reversed range rejected", func(t *testing.T) {
|
||||
_, err := Parse("5-2")
|
||||
require.Error(t, err)
|
||||
})
|
||||
|
||||
t.Run("element at max accepted", func(t *testing.T) {
|
||||
set, err := Parse(strconv.Itoa(MaxCPU))
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, map[int]struct{}{MaxCPU: {}}, set)
|
||||
})
|
||||
|
||||
t.Run("element above max rejected", func(t *testing.T) {
|
||||
_, err := Parse(strconv.Itoa(MaxCPU + 1))
|
||||
require.Error(t, err)
|
||||
})
|
||||
|
||||
t.Run("range above max rejected without huge allocation", func(t *testing.T) {
|
||||
_, err := Parse("0-999999999")
|
||||
require.Error(t, err)
|
||||
})
|
||||
}
|
||||
|
||||
func TestFormat(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
t.Run("empty", func(t *testing.T) {
|
||||
require.Equal(t, "", Format(nil))
|
||||
require.Equal(t, "", Format(map[int]struct{}{}))
|
||||
})
|
||||
|
||||
t.Run("single value", func(t *testing.T) {
|
||||
require.Equal(t, "3", Format(map[int]struct{}{3: {}}))
|
||||
})
|
||||
|
||||
t.Run("collapses contiguous range", func(t *testing.T) {
|
||||
set := map[int]struct{}{0: {}, 1: {}, 2: {}, 3: {}}
|
||||
require.Equal(t, "0-3", Format(set))
|
||||
})
|
||||
|
||||
t.Run("preserves gaps", func(t *testing.T) {
|
||||
set := map[int]struct{}{0: {}, 2: {}, 3: {}, 5: {}}
|
||||
require.Equal(t, "0,2-3,5", Format(set))
|
||||
})
|
||||
|
||||
t.Run("round-trip", func(t *testing.T) {
|
||||
const input = "0,2-3,5-9,11"
|
||||
set, err := Parse(input)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, input, Format(set))
|
||||
})
|
||||
}
|
||||
|
||||
func TestValidate(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
require.NoError(t, Validate(""))
|
||||
require.NoError(t, Validate("0-3"))
|
||||
require.NoError(t, Validate("0,2-3,5"))
|
||||
|
||||
require.Error(t, Validate("abc"))
|
||||
require.Error(t, Validate("5-2"))
|
||||
require.Error(t, Validate("-1"))
|
||||
}
|
||||
@@ -34,6 +34,7 @@ import (
|
||||
"github.com/moby/buildkit/snapshot/imagerefchecker"
|
||||
"github.com/moby/buildkit/solver"
|
||||
"github.com/moby/buildkit/solver/llbsolver/cdidevices"
|
||||
"github.com/moby/buildkit/solver/llbsolver/linuxresources"
|
||||
"github.com/moby/buildkit/solver/llbsolver/mounts"
|
||||
"github.com/moby/buildkit/solver/llbsolver/ops"
|
||||
"github.com/moby/buildkit/solver/pb"
|
||||
@@ -360,7 +361,11 @@ func (w *Worker) ResolveOp(v solver.Vertex, s frontend.FrontendLLBBridge, sm *se
|
||||
case *pb.Op_Source:
|
||||
return ops.NewSourceOp(v, op, baseOp.Platform, w.SourceManager, w.ParallelismSem, sm, w)
|
||||
case *pb.Op_Exec:
|
||||
return ops.NewExecOp(v, op, baseOp.Platform, w.CacheMgr, w.ParallelismSem, sm, w.WorkerOpt.Executor, w)
|
||||
var linuxResources *pb.LinuxResources
|
||||
if m, ok := v.Options().Metadata.(*linuxresources.Metadata); ok && m != nil {
|
||||
linuxResources = m.LinuxResources
|
||||
}
|
||||
return ops.NewExecOp(v, op, baseOp.Platform, w.CacheMgr, w.ParallelismSem, sm, w.WorkerOpt.Executor, w, linuxResources)
|
||||
case *pb.Op_File:
|
||||
return ops.NewFileOp(v, op, w.CacheMgr, w.ParallelismSem, w)
|
||||
case *pb.Op_Build:
|
||||
|
||||
Reference in new issue
Block a user