зеркало из https://github.com/microsoft/docker.git
Merge pull request #37644 from AntaresS/handle-cgroups
propagate the dockerd cgroup-parent config to buildkitd
This commit is contained in:
Коммит
9d276b8afd
|
@ -50,10 +50,11 @@ func init() {
|
|||
|
||||
// Opt is option struct required for creating the builder
|
||||
type Opt struct {
|
||||
SessionManager *session.Manager
|
||||
Root string
|
||||
Dist images.DistributionServices
|
||||
NetworkController libnetwork.NetworkController
|
||||
SessionManager *session.Manager
|
||||
Root string
|
||||
Dist images.DistributionServices
|
||||
NetworkController libnetwork.NetworkController
|
||||
DefaultCgroupParent string
|
||||
}
|
||||
|
||||
// Builder can build using BuildKit backend
|
||||
|
|
|
@ -102,7 +102,7 @@ func newController(rt http.RoundTripper, opt Opt) (*control.Controller, error) {
|
|||
return nil, err
|
||||
}
|
||||
|
||||
exec, err := newExecutor(root, opt.NetworkController)
|
||||
exec, err := newExecutor(root, opt.DefaultCgroupParent, opt.NetworkController)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
|
@ -19,15 +19,16 @@ import (
|
|||
|
||||
const networkName = "bridge"
|
||||
|
||||
func newExecutor(root string, net libnetwork.NetworkController) (executor.Executor, error) {
|
||||
func newExecutor(root, cgroupParent string, net libnetwork.NetworkController) (executor.Executor, error) {
|
||||
networkProviders := map[pb.NetMode]network.Provider{
|
||||
pb.NetMode_UNSET: &bridgeProvider{NetworkController: net},
|
||||
pb.NetMode_HOST: network.NewHostProvider(),
|
||||
pb.NetMode_NONE: network.NewNoneProvider(),
|
||||
}
|
||||
return runcexecutor.New(runcexecutor.Opt{
|
||||
Root: filepath.Join(root, "executor"),
|
||||
CommandCandidates: []string{"docker-runc", "runc"},
|
||||
Root: filepath.Join(root, "executor"),
|
||||
CommandCandidates: []string{"docker-runc", "runc"},
|
||||
DefaultCgroupParent: cgroupParent,
|
||||
}, networkProviders)
|
||||
}
|
||||
|
||||
|
|
|
@ -10,7 +10,7 @@ import (
|
|||
"github.com/moby/buildkit/executor"
|
||||
)
|
||||
|
||||
func newExecutor(_ string, _ libnetwork.NetworkController) (executor.Executor, error) {
|
||||
func newExecutor(_, _ string, _ libnetwork.NetworkController) (executor.Executor, error) {
|
||||
return &winExecutor{}, nil
|
||||
}
|
||||
|
||||
|
|
|
@ -46,6 +46,7 @@ import (
|
|||
type Opt struct {
|
||||
ID string
|
||||
Labels map[string]string
|
||||
GCPolicy []client.PruneInfo
|
||||
SessionManager *session.Manager
|
||||
MetadataStore *metadata.Store
|
||||
Executor executor.Executor
|
||||
|
@ -130,6 +131,11 @@ func (w *Worker) Platforms() []ocispec.Platform {
|
|||
return []ocispec.Platform{platforms.DefaultSpec()}
|
||||
}
|
||||
|
||||
// GCPolicy returns automatic GC Policy
|
||||
func (w *Worker) GCPolicy() []client.PruneInfo {
|
||||
return w.Opt.GCPolicy
|
||||
}
|
||||
|
||||
// LoadRef loads a reference by ID
|
||||
func (w *Worker) LoadRef(id string) (cache.ImmutableRef, error) {
|
||||
return w.CacheManager.Get(context.TODO(), id)
|
||||
|
@ -176,8 +182,8 @@ func (w *Worker) DiskUsage(ctx context.Context, opt client.DiskUsageInfo) ([]*cl
|
|||
}
|
||||
|
||||
// Prune deletes reclaimable build cache
|
||||
func (w *Worker) Prune(ctx context.Context, ch chan client.UsageInfo, info client.PruneInfo) error {
|
||||
return w.CacheManager.Prune(ctx, ch, info)
|
||||
func (w *Worker) Prune(ctx context.Context, ch chan client.UsageInfo, info ...client.PruneInfo) error {
|
||||
return w.CacheManager.Prune(ctx, ch, info...)
|
||||
}
|
||||
|
||||
// Exporter returns exporter by name
|
||||
|
|
|
@ -259,7 +259,7 @@ type routerOptions struct {
|
|||
cluster *cluster.Cluster
|
||||
}
|
||||
|
||||
func newRouterOptions(config *config.Config, daemon *daemon.Daemon) (routerOptions, error) {
|
||||
func newRouterOptions(config *config.Config, d *daemon.Daemon) (routerOptions, error) {
|
||||
opts := routerOptions{}
|
||||
sm, err := session.NewManager()
|
||||
if err != nil {
|
||||
|
@ -280,21 +280,23 @@ func newRouterOptions(config *config.Config, daemon *daemon.Daemon) (routerOptio
|
|||
return opts, errors.Wrap(err, "failed to create fscache")
|
||||
}
|
||||
|
||||
manager, err := dockerfile.NewBuildManager(daemon.BuilderBackend(), sm, buildCache, daemon.IdentityMapping())
|
||||
manager, err := dockerfile.NewBuildManager(d.BuilderBackend(), sm, buildCache, d.IdentityMapping())
|
||||
if err != nil {
|
||||
return opts, err
|
||||
}
|
||||
cgroupParent := newCgroupParent(config)
|
||||
bk, err := buildkit.New(buildkit.Opt{
|
||||
SessionManager: sm,
|
||||
Root: filepath.Join(config.Root, "buildkit"),
|
||||
Dist: daemon.DistributionServices(),
|
||||
NetworkController: daemon.NetworkController(),
|
||||
SessionManager: sm,
|
||||
Root: filepath.Join(config.Root, "buildkit"),
|
||||
Dist: d.DistributionServices(),
|
||||
NetworkController: d.NetworkController(),
|
||||
DefaultCgroupParent: cgroupParent,
|
||||
})
|
||||
if err != nil {
|
||||
return opts, err
|
||||
}
|
||||
|
||||
bb, err := buildbackend.NewBackend(daemon.ImageService(), manager, buildCache, bk)
|
||||
bb, err := buildbackend.NewBackend(d.ImageService(), manager, buildCache, bk)
|
||||
if err != nil {
|
||||
return opts, errors.Wrap(err, "failed to create buildmanager")
|
||||
}
|
||||
|
@ -303,8 +305,8 @@ func newRouterOptions(config *config.Config, daemon *daemon.Daemon) (routerOptio
|
|||
buildBackend: bb,
|
||||
buildCache: buildCache,
|
||||
buildkit: bk,
|
||||
features: daemon.Features(),
|
||||
daemon: daemon,
|
||||
features: d.Features(),
|
||||
daemon: d,
|
||||
}, nil
|
||||
}
|
||||
|
||||
|
|
|
@ -13,6 +13,7 @@ import (
|
|||
"github.com/containerd/containerd/runtime/v1/linux"
|
||||
"github.com/docker/docker/cmd/dockerd/hack"
|
||||
"github.com/docker/docker/daemon"
|
||||
"github.com/docker/docker/daemon/config"
|
||||
"github.com/docker/docker/libcontainerd/supervisor"
|
||||
"github.com/docker/libnetwork/portallocator"
|
||||
"golang.org/x/sys/unix"
|
||||
|
@ -107,3 +108,18 @@ func wrapListeners(proto string, ls []net.Listener) []net.Listener {
|
|||
}
|
||||
return ls
|
||||
}
|
||||
|
||||
func newCgroupParent(config *config.Config) string {
|
||||
cgroupParent := "docker"
|
||||
useSystemd := daemon.UsingSystemd(config)
|
||||
if useSystemd {
|
||||
cgroupParent = "system.slice"
|
||||
}
|
||||
if config.CgroupParent != "" {
|
||||
cgroupParent = config.CgroupParent
|
||||
}
|
||||
if useSystemd {
|
||||
cgroupParent = cgroupParent + ":" + "docker" + ":"
|
||||
}
|
||||
return cgroupParent
|
||||
}
|
||||
|
|
|
@ -6,6 +6,7 @@ import (
|
|||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/docker/docker/daemon/config"
|
||||
"github.com/docker/docker/libcontainerd/supervisor"
|
||||
"github.com/sirupsen/logrus"
|
||||
"golang.org/x/sys/windows"
|
||||
|
@ -83,3 +84,7 @@ func allocateDaemonPort(addr string) error {
|
|||
func wrapListeners(proto string, ls []net.Listener) []net.Listener {
|
||||
return ls
|
||||
}
|
||||
|
||||
func newCgroupParent(config *config.Config) string {
|
||||
return ""
|
||||
}
|
||||
|
|
|
@ -26,7 +26,7 @@ github.com/imdario/mergo v0.3.6
|
|||
golang.org/x/sync 1d60e4601c6fd243af51cc01ddf169918a5407ca
|
||||
|
||||
# buildkit
|
||||
github.com/moby/buildkit 6812dac65e0440bb75affce1fb2175e640edc15d
|
||||
github.com/moby/buildkit a9fe50acf16dd05d1f9877b27068884543ad7a1f
|
||||
github.com/tonistiigi/fsutil b19464cd1b6a00773b4f2eb7acf9c30426f9df42
|
||||
github.com/grpc-ecosystem/grpc-opentracing 8e809c8a86450a29b90dcc9efbf062d0fe6d9746
|
||||
github.com/opentracing/opentracing-go 1361b9cd60be79c4c3a7fa9841b3c132e40066a7
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
|
||||
It has these top-level messages:
|
||||
WorkerRecord
|
||||
GCPolicy
|
||||
*/
|
||||
package moby_buildkit_v1_types
|
||||
|
||||
|
@ -35,6 +36,7 @@ type WorkerRecord struct {
|
|||
ID string `protobuf:"bytes,1,opt,name=ID,proto3" json:"ID,omitempty"`
|
||||
Labels map[string]string `protobuf:"bytes,2,rep,name=Labels" json:"Labels,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"`
|
||||
Platforms []pb.Platform `protobuf:"bytes,3,rep,name=platforms" json:"platforms"`
|
||||
GCPolicy []*GCPolicy `protobuf:"bytes,4,rep,name=GCPolicy" json:"GCPolicy,omitempty"`
|
||||
}
|
||||
|
||||
func (m *WorkerRecord) Reset() { *m = WorkerRecord{} }
|
||||
|
@ -63,8 +65,56 @@ func (m *WorkerRecord) GetPlatforms() []pb.Platform {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (m *WorkerRecord) GetGCPolicy() []*GCPolicy {
|
||||
if m != nil {
|
||||
return m.GCPolicy
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type GCPolicy struct {
|
||||
All bool `protobuf:"varint,1,opt,name=all,proto3" json:"all,omitempty"`
|
||||
KeepDuration int64 `protobuf:"varint,2,opt,name=keepDuration,proto3" json:"keepDuration,omitempty"`
|
||||
KeepBytes int64 `protobuf:"varint,3,opt,name=keepBytes,proto3" json:"keepBytes,omitempty"`
|
||||
Filters []string `protobuf:"bytes,4,rep,name=filters" json:"filters,omitempty"`
|
||||
}
|
||||
|
||||
func (m *GCPolicy) Reset() { *m = GCPolicy{} }
|
||||
func (m *GCPolicy) String() string { return proto.CompactTextString(m) }
|
||||
func (*GCPolicy) ProtoMessage() {}
|
||||
func (*GCPolicy) Descriptor() ([]byte, []int) { return fileDescriptorWorker, []int{1} }
|
||||
|
||||
func (m *GCPolicy) GetAll() bool {
|
||||
if m != nil {
|
||||
return m.All
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (m *GCPolicy) GetKeepDuration() int64 {
|
||||
if m != nil {
|
||||
return m.KeepDuration
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (m *GCPolicy) GetKeepBytes() int64 {
|
||||
if m != nil {
|
||||
return m.KeepBytes
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (m *GCPolicy) GetFilters() []string {
|
||||
if m != nil {
|
||||
return m.Filters
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func init() {
|
||||
proto.RegisterType((*WorkerRecord)(nil), "moby.buildkit.v1.types.WorkerRecord")
|
||||
proto.RegisterType((*GCPolicy)(nil), "moby.buildkit.v1.types.GCPolicy")
|
||||
}
|
||||
func (m *WorkerRecord) Marshal() (dAtA []byte, err error) {
|
||||
size := m.Size()
|
||||
|
@ -116,6 +166,71 @@ func (m *WorkerRecord) MarshalTo(dAtA []byte) (int, error) {
|
|||
i += n
|
||||
}
|
||||
}
|
||||
if len(m.GCPolicy) > 0 {
|
||||
for _, msg := range m.GCPolicy {
|
||||
dAtA[i] = 0x22
|
||||
i++
|
||||
i = encodeVarintWorker(dAtA, i, uint64(msg.Size()))
|
||||
n, err := msg.MarshalTo(dAtA[i:])
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
i += n
|
||||
}
|
||||
}
|
||||
return i, nil
|
||||
}
|
||||
|
||||
func (m *GCPolicy) Marshal() (dAtA []byte, err error) {
|
||||
size := m.Size()
|
||||
dAtA = make([]byte, size)
|
||||
n, err := m.MarshalTo(dAtA)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return dAtA[:n], nil
|
||||
}
|
||||
|
||||
func (m *GCPolicy) MarshalTo(dAtA []byte) (int, error) {
|
||||
var i int
|
||||
_ = i
|
||||
var l int
|
||||
_ = l
|
||||
if m.All {
|
||||
dAtA[i] = 0x8
|
||||
i++
|
||||
if m.All {
|
||||
dAtA[i] = 1
|
||||
} else {
|
||||
dAtA[i] = 0
|
||||
}
|
||||
i++
|
||||
}
|
||||
if m.KeepDuration != 0 {
|
||||
dAtA[i] = 0x10
|
||||
i++
|
||||
i = encodeVarintWorker(dAtA, i, uint64(m.KeepDuration))
|
||||
}
|
||||
if m.KeepBytes != 0 {
|
||||
dAtA[i] = 0x18
|
||||
i++
|
||||
i = encodeVarintWorker(dAtA, i, uint64(m.KeepBytes))
|
||||
}
|
||||
if len(m.Filters) > 0 {
|
||||
for _, s := range m.Filters {
|
||||
dAtA[i] = 0x22
|
||||
i++
|
||||
l = len(s)
|
||||
for l >= 1<<7 {
|
||||
dAtA[i] = uint8(uint64(l)&0x7f | 0x80)
|
||||
l >>= 7
|
||||
i++
|
||||
}
|
||||
dAtA[i] = uint8(l)
|
||||
i++
|
||||
i += copy(dAtA[i:], s)
|
||||
}
|
||||
}
|
||||
return i, nil
|
||||
}
|
||||
|
||||
|
@ -149,6 +264,33 @@ func (m *WorkerRecord) Size() (n int) {
|
|||
n += 1 + l + sovWorker(uint64(l))
|
||||
}
|
||||
}
|
||||
if len(m.GCPolicy) > 0 {
|
||||
for _, e := range m.GCPolicy {
|
||||
l = e.Size()
|
||||
n += 1 + l + sovWorker(uint64(l))
|
||||
}
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
||||
func (m *GCPolicy) Size() (n int) {
|
||||
var l int
|
||||
_ = l
|
||||
if m.All {
|
||||
n += 2
|
||||
}
|
||||
if m.KeepDuration != 0 {
|
||||
n += 1 + sovWorker(uint64(m.KeepDuration))
|
||||
}
|
||||
if m.KeepBytes != 0 {
|
||||
n += 1 + sovWorker(uint64(m.KeepBytes))
|
||||
}
|
||||
if len(m.Filters) > 0 {
|
||||
for _, s := range m.Filters {
|
||||
l = len(s)
|
||||
n += 1 + l + sovWorker(uint64(l))
|
||||
}
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
||||
|
@ -372,6 +514,174 @@ func (m *WorkerRecord) Unmarshal(dAtA []byte) error {
|
|||
return err
|
||||
}
|
||||
iNdEx = postIndex
|
||||
case 4:
|
||||
if wireType != 2 {
|
||||
return fmt.Errorf("proto: wrong wireType = %d for field GCPolicy", wireType)
|
||||
}
|
||||
var msglen int
|
||||
for shift := uint(0); ; shift += 7 {
|
||||
if shift >= 64 {
|
||||
return ErrIntOverflowWorker
|
||||
}
|
||||
if iNdEx >= l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
b := dAtA[iNdEx]
|
||||
iNdEx++
|
||||
msglen |= (int(b) & 0x7F) << shift
|
||||
if b < 0x80 {
|
||||
break
|
||||
}
|
||||
}
|
||||
if msglen < 0 {
|
||||
return ErrInvalidLengthWorker
|
||||
}
|
||||
postIndex := iNdEx + msglen
|
||||
if postIndex > l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
m.GCPolicy = append(m.GCPolicy, &GCPolicy{})
|
||||
if err := m.GCPolicy[len(m.GCPolicy)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
|
||||
return err
|
||||
}
|
||||
iNdEx = postIndex
|
||||
default:
|
||||
iNdEx = preIndex
|
||||
skippy, err := skipWorker(dAtA[iNdEx:])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if skippy < 0 {
|
||||
return ErrInvalidLengthWorker
|
||||
}
|
||||
if (iNdEx + skippy) > l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
iNdEx += skippy
|
||||
}
|
||||
}
|
||||
|
||||
if iNdEx > l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
return nil
|
||||
}
|
||||
func (m *GCPolicy) Unmarshal(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 ErrIntOverflowWorker
|
||||
}
|
||||
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: GCPolicy: wiretype end group for non-group")
|
||||
}
|
||||
if fieldNum <= 0 {
|
||||
return fmt.Errorf("proto: GCPolicy: illegal tag %d (wire type %d)", fieldNum, wire)
|
||||
}
|
||||
switch fieldNum {
|
||||
case 1:
|
||||
if wireType != 0 {
|
||||
return fmt.Errorf("proto: wrong wireType = %d for field All", wireType)
|
||||
}
|
||||
var v int
|
||||
for shift := uint(0); ; shift += 7 {
|
||||
if shift >= 64 {
|
||||
return ErrIntOverflowWorker
|
||||
}
|
||||
if iNdEx >= l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
b := dAtA[iNdEx]
|
||||
iNdEx++
|
||||
v |= (int(b) & 0x7F) << shift
|
||||
if b < 0x80 {
|
||||
break
|
||||
}
|
||||
}
|
||||
m.All = bool(v != 0)
|
||||
case 2:
|
||||
if wireType != 0 {
|
||||
return fmt.Errorf("proto: wrong wireType = %d for field KeepDuration", wireType)
|
||||
}
|
||||
m.KeepDuration = 0
|
||||
for shift := uint(0); ; shift += 7 {
|
||||
if shift >= 64 {
|
||||
return ErrIntOverflowWorker
|
||||
}
|
||||
if iNdEx >= l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
b := dAtA[iNdEx]
|
||||
iNdEx++
|
||||
m.KeepDuration |= (int64(b) & 0x7F) << shift
|
||||
if b < 0x80 {
|
||||
break
|
||||
}
|
||||
}
|
||||
case 3:
|
||||
if wireType != 0 {
|
||||
return fmt.Errorf("proto: wrong wireType = %d for field KeepBytes", wireType)
|
||||
}
|
||||
m.KeepBytes = 0
|
||||
for shift := uint(0); ; shift += 7 {
|
||||
if shift >= 64 {
|
||||
return ErrIntOverflowWorker
|
||||
}
|
||||
if iNdEx >= l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
b := dAtA[iNdEx]
|
||||
iNdEx++
|
||||
m.KeepBytes |= (int64(b) & 0x7F) << shift
|
||||
if b < 0x80 {
|
||||
break
|
||||
}
|
||||
}
|
||||
case 4:
|
||||
if wireType != 2 {
|
||||
return fmt.Errorf("proto: wrong wireType = %d for field Filters", wireType)
|
||||
}
|
||||
var stringLen uint64
|
||||
for shift := uint(0); ; shift += 7 {
|
||||
if shift >= 64 {
|
||||
return ErrIntOverflowWorker
|
||||
}
|
||||
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 ErrInvalidLengthWorker
|
||||
}
|
||||
postIndex := iNdEx + intStringLen
|
||||
if postIndex > l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
m.Filters = append(m.Filters, string(dAtA[iNdEx:postIndex]))
|
||||
iNdEx = postIndex
|
||||
default:
|
||||
iNdEx = preIndex
|
||||
skippy, err := skipWorker(dAtA[iNdEx:])
|
||||
|
@ -501,23 +811,28 @@ var (
|
|||
func init() { proto.RegisterFile("worker.proto", fileDescriptorWorker) }
|
||||
|
||||
var fileDescriptorWorker = []byte{
|
||||
// 273 bytes of a gzipped FileDescriptorProto
|
||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x6c, 0x8f, 0x41, 0x4b, 0xf3, 0x40,
|
||||
0x10, 0x86, 0xbf, 0x4d, 0x3e, 0x0b, 0xdd, 0x06, 0x91, 0x45, 0x24, 0xe4, 0x10, 0x8b, 0xa7, 0x1e,
|
||||
0x74, 0xb6, 0xea, 0x45, 0x3d, 0x96, 0x0a, 0x16, 0x3c, 0x48, 0x2e, 0x9e, 0xb3, 0xed, 0x36, 0x86,
|
||||
0x24, 0xce, 0xb2, 0xd9, 0x44, 0xf2, 0x0f, 0x7b, 0xf4, 0xe2, 0x55, 0x24, 0xbf, 0x44, 0xba, 0x89,
|
||||
0x98, 0x83, 0xb7, 0x79, 0x87, 0x67, 0x1e, 0xde, 0xa1, 0xde, 0x1b, 0xea, 0x4c, 0x6a, 0x50, 0x1a,
|
||||
0x0d, 0xb2, 0x93, 0x02, 0x45, 0x03, 0xa2, 0x4a, 0xf3, 0x4d, 0x96, 0x1a, 0xa8, 0x2f, 0xc1, 0x34,
|
||||
0x4a, 0x96, 0xc1, 0x45, 0x92, 0x9a, 0x97, 0x4a, 0xc0, 0x1a, 0x0b, 0x9e, 0x60, 0x82, 0xdc, 0xe2,
|
||||
0xa2, 0xda, 0xda, 0x64, 0x83, 0x9d, 0x3a, 0x4d, 0x70, 0x3e, 0xc0, 0xf7, 0x46, 0xfe, 0x63, 0xe4,
|
||||
0x25, 0xe6, 0xb5, 0xd4, 0x5c, 0x09, 0x8e, 0xaa, 0xec, 0xe8, 0xb3, 0x0f, 0x42, 0xbd, 0x67, 0xdb,
|
||||
0x22, 0x92, 0x6b, 0xd4, 0x1b, 0x76, 0x48, 0x9d, 0xd5, 0xd2, 0x27, 0x53, 0x32, 0x1b, 0x47, 0xce,
|
||||
0x6a, 0xc9, 0x1e, 0xe8, 0xe8, 0x31, 0x16, 0x32, 0x2f, 0x7d, 0x67, 0xea, 0xce, 0x26, 0x57, 0x73,
|
||||
0xf8, 0xbb, 0x26, 0x0c, 0x2d, 0xd0, 0x9d, 0xdc, 0xbf, 0x1a, 0xdd, 0x44, 0xfd, 0x3d, 0x9b, 0xd3,
|
||||
0xb1, 0xca, 0x63, 0xb3, 0x45, 0x5d, 0x94, 0xbe, 0x6b, 0x65, 0x1e, 0x28, 0x01, 0x4f, 0xfd, 0x72,
|
||||
0xf1, 0x7f, 0xf7, 0x79, 0xfa, 0x2f, 0xfa, 0x85, 0x82, 0x5b, 0x3a, 0x19, 0x88, 0xd8, 0x11, 0x75,
|
||||
0x33, 0xd9, 0xf4, 0xdd, 0xf6, 0x23, 0x3b, 0xa6, 0x07, 0x75, 0x9c, 0x57, 0xd2, 0x77, 0xec, 0xae,
|
||||
0x0b, 0x77, 0xce, 0x0d, 0x59, 0x78, 0xbb, 0x36, 0x24, 0xef, 0x6d, 0x48, 0xbe, 0xda, 0x90, 0x88,
|
||||
0x91, 0x7d, 0xf6, 0xfa, 0x3b, 0x00, 0x00, 0xff, 0xff, 0xa9, 0x5c, 0x8f, 0x26, 0x71, 0x01, 0x00,
|
||||
0x00,
|
||||
// 355 bytes of a gzipped FileDescriptorProto
|
||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x74, 0x91, 0xc1, 0x4e, 0xea, 0x40,
|
||||
0x14, 0x86, 0x6f, 0x5b, 0x2e, 0x97, 0x0e, 0xcd, 0x8d, 0x99, 0x18, 0xd3, 0x10, 0x83, 0x84, 0x15,
|
||||
0x0b, 0x9d, 0xa2, 0x6e, 0xd4, 0xb8, 0x42, 0x8c, 0x92, 0xb8, 0x20, 0xb3, 0x71, 0xdd, 0x81, 0x01,
|
||||
0x9b, 0x0e, 0x9c, 0xc9, 0x74, 0x8a, 0xf6, 0x39, 0x7c, 0x29, 0x96, 0x3e, 0x81, 0x31, 0x3c, 0x89,
|
||||
0x99, 0x29, 0x08, 0x26, 0xba, 0x3b, 0xff, 0x9f, 0xff, 0xfb, 0xe7, 0x9c, 0x0c, 0x0a, 0x9e, 0x41,
|
||||
0xa5, 0x5c, 0x11, 0xa9, 0x40, 0x03, 0x3e, 0x98, 0x01, 0x2b, 0x08, 0xcb, 0x13, 0x31, 0x4e, 0x13,
|
||||
0x4d, 0x16, 0xa7, 0x44, 0x17, 0x92, 0x67, 0x8d, 0x93, 0x69, 0xa2, 0x9f, 0x72, 0x46, 0x46, 0x30,
|
||||
0x8b, 0xa6, 0x30, 0x85, 0xc8, 0xc6, 0x59, 0x3e, 0xb1, 0xca, 0x0a, 0x3b, 0x95, 0x35, 0x8d, 0xe3,
|
||||
0x9d, 0xb8, 0x69, 0x8c, 0x36, 0x8d, 0x51, 0x06, 0x62, 0xc1, 0x55, 0x24, 0x59, 0x04, 0x32, 0x2b,
|
||||
0xd3, 0xed, 0x57, 0x17, 0x05, 0x8f, 0x76, 0x0b, 0xca, 0x47, 0xa0, 0xc6, 0xf8, 0x3f, 0x72, 0x07,
|
||||
0xfd, 0xd0, 0x69, 0x39, 0x1d, 0x9f, 0xba, 0x83, 0x3e, 0xbe, 0x47, 0xd5, 0x87, 0x98, 0x71, 0x91,
|
||||
0x85, 0x6e, 0xcb, 0xeb, 0xd4, 0xcf, 0xba, 0xe4, 0xe7, 0x35, 0xc9, 0x6e, 0x0b, 0x29, 0x91, 0xdb,
|
||||
0xb9, 0x56, 0x05, 0x5d, 0xf3, 0xb8, 0x8b, 0x7c, 0x29, 0x62, 0x3d, 0x01, 0x35, 0xcb, 0x42, 0xcf,
|
||||
0x96, 0x05, 0x44, 0x32, 0x32, 0x5c, 0x9b, 0xbd, 0xca, 0xf2, 0xfd, 0xe8, 0x0f, 0xdd, 0x86, 0xf0,
|
||||
0x35, 0xaa, 0xdd, 0xdd, 0x0c, 0x41, 0x24, 0xa3, 0x22, 0xac, 0x58, 0xa0, 0xf5, 0xdb, 0xeb, 0x9b,
|
||||
0x1c, 0xfd, 0x22, 0x1a, 0x97, 0xa8, 0xbe, 0xb3, 0x06, 0xde, 0x43, 0x5e, 0xca, 0x8b, 0xf5, 0x65,
|
||||
0x66, 0xc4, 0xfb, 0xe8, 0xef, 0x22, 0x16, 0x39, 0x0f, 0x5d, 0xeb, 0x95, 0xe2, 0xca, 0xbd, 0x70,
|
||||
0xda, 0x2f, 0xdb, 0x87, 0x0d, 0x17, 0x0b, 0x61, 0xb9, 0x1a, 0x35, 0x23, 0x6e, 0xa3, 0x20, 0xe5,
|
||||
0x5c, 0xf6, 0x73, 0x15, 0xeb, 0x04, 0xe6, 0x16, 0xf7, 0xe8, 0x37, 0x0f, 0x1f, 0x22, 0xdf, 0xe8,
|
||||
0x5e, 0xa1, 0xb9, 0x39, 0xd6, 0x04, 0xb6, 0x06, 0x0e, 0xd1, 0xbf, 0x49, 0x22, 0x34, 0x57, 0x99,
|
||||
0xbd, 0xcb, 0xa7, 0x1b, 0xd9, 0x0b, 0x96, 0xab, 0xa6, 0xf3, 0xb6, 0x6a, 0x3a, 0x1f, 0xab, 0xa6,
|
||||
0xc3, 0xaa, 0xf6, 0x93, 0xce, 0x3f, 0x03, 0x00, 0x00, 0xff, 0xff, 0xfc, 0x79, 0x52, 0x6a, 0x29,
|
||||
0x02, 0x00, 0x00,
|
||||
}
|
||||
|
|
|
@ -13,4 +13,12 @@ message WorkerRecord {
|
|||
string ID = 1;
|
||||
map<string, string> Labels = 2;
|
||||
repeated pb.Platform platforms = 3 [(gogoproto.nullable) = false];
|
||||
repeated GCPolicy GCPolicy = 4;
|
||||
}
|
||||
|
||||
message GCPolicy {
|
||||
bool all = 1;
|
||||
int64 keepDuration = 2;
|
||||
int64 keepBytes = 3;
|
||||
repeated string filters = 4;
|
||||
}
|
||||
|
|
|
@ -39,7 +39,7 @@ type Accessor interface {
|
|||
|
||||
type Controller interface {
|
||||
DiskUsage(ctx context.Context, info client.DiskUsageInfo) ([]*client.UsageInfo, error)
|
||||
Prune(ctx context.Context, ch chan client.UsageInfo, info client.PruneInfo) error
|
||||
Prune(ctx context.Context, ch chan client.UsageInfo, info ...client.PruneInfo) error
|
||||
GC(ctx context.Context) error
|
||||
}
|
||||
|
||||
|
@ -304,10 +304,19 @@ func (cm *cacheManager) GetMutable(ctx context.Context, id string) (MutableRef,
|
|||
return rec.mref(), nil
|
||||
}
|
||||
|
||||
func (cm *cacheManager) Prune(ctx context.Context, ch chan client.UsageInfo, opt client.PruneInfo) error {
|
||||
func (cm *cacheManager) Prune(ctx context.Context, ch chan client.UsageInfo, opts ...client.PruneInfo) error {
|
||||
cm.muPrune.Lock()
|
||||
defer cm.muPrune.Unlock()
|
||||
|
||||
for _, opt := range opts {
|
||||
if err := cm.pruneOnce(ctx, ch, opt); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (cm *cacheManager) pruneOnce(ctx context.Context, ch chan client.UsageInfo, opt client.PruneInfo) error {
|
||||
filter, err := filters.ParseAll(opt.Filter...)
|
||||
if err != nil {
|
||||
return err
|
||||
|
|
|
@ -67,7 +67,7 @@ func (s State) Value(k interface{}) interface{} {
|
|||
return s.ctx.Value(k)
|
||||
}
|
||||
|
||||
func (s State) SetMarhalDefaults(co ...ConstraintsOpt) State {
|
||||
func (s State) SetMarshalDefaults(co ...ConstraintsOpt) State {
|
||||
s.opts = co
|
||||
return s
|
||||
}
|
||||
|
|
|
@ -2,8 +2,10 @@ package client
|
|||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
controlapi "github.com/moby/buildkit/api/services/control"
|
||||
apitypes "github.com/moby/buildkit/api/types"
|
||||
"github.com/moby/buildkit/solver/pb"
|
||||
specs "github.com/opencontainers/image-spec/specs-go/v1"
|
||||
"github.com/pkg/errors"
|
||||
|
@ -13,6 +15,7 @@ type WorkerInfo struct {
|
|||
ID string
|
||||
Labels map[string]string
|
||||
Platforms []specs.Platform
|
||||
GCPolicy []PruneInfo
|
||||
}
|
||||
|
||||
func (c *Client) ListWorkers(ctx context.Context, opts ...ListWorkersOption) ([]*WorkerInfo, error) {
|
||||
|
@ -34,6 +37,7 @@ func (c *Client) ListWorkers(ctx context.Context, opts ...ListWorkersOption) ([]
|
|||
ID: w.ID,
|
||||
Labels: w.Labels,
|
||||
Platforms: pb.ToSpecPlatforms(w.Platforms),
|
||||
GCPolicy: fromAPIGCPolicy(w.GCPolicy),
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -47,3 +51,16 @@ type ListWorkersOption interface {
|
|||
type ListWorkersInfo struct {
|
||||
Filter []string
|
||||
}
|
||||
|
||||
func fromAPIGCPolicy(in []*apitypes.GCPolicy) []PruneInfo {
|
||||
out := make([]PruneInfo, 0, len(in))
|
||||
for _, p := range in {
|
||||
out = append(out, PruneInfo{
|
||||
All: p.All,
|
||||
Filter: p.Filters,
|
||||
KeepDuration: time.Duration(p.KeepDuration),
|
||||
KeepBytes: p.KeepBytes,
|
||||
})
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@ package control
|
|||
|
||||
import (
|
||||
"context"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/docker/distribution/reference"
|
||||
|
@ -17,6 +18,7 @@ import (
|
|||
"github.com/moby/buildkit/solver"
|
||||
"github.com/moby/buildkit/solver/llbsolver"
|
||||
"github.com/moby/buildkit/solver/pb"
|
||||
"github.com/moby/buildkit/util/throttle"
|
||||
"github.com/moby/buildkit/worker"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/sirupsen/logrus"
|
||||
|
@ -40,6 +42,8 @@ type Controller struct { // TODO: ControlService
|
|||
solver *llbsolver.Solver
|
||||
cache solver.CacheManager
|
||||
gatewayForwarder *controlgateway.GatewayForwarder
|
||||
throttledGC func()
|
||||
gcmu sync.Mutex
|
||||
}
|
||||
|
||||
func NewController(opt Opt) (*Controller, error) {
|
||||
|
@ -58,6 +62,12 @@ func NewController(opt Opt) (*Controller, error) {
|
|||
cache: cache,
|
||||
gatewayForwarder: gatewayForwarder,
|
||||
}
|
||||
c.throttledGC = throttle.ThrottleAfter(time.Minute, c.gc)
|
||||
|
||||
defer func() {
|
||||
time.AfterFunc(time.Second, c.throttledGC)
|
||||
}()
|
||||
|
||||
return c, nil
|
||||
}
|
||||
|
||||
|
@ -172,6 +182,10 @@ func (c *Controller) Prune(req *controlapi.PruneRequest, stream controlapi.Contr
|
|||
func (c *Controller) Solve(ctx context.Context, req *controlapi.SolveRequest) (*controlapi.SolveResponse, error) {
|
||||
ctx = session.NewContext(ctx, req.Session)
|
||||
|
||||
defer func() {
|
||||
time.AfterFunc(time.Second, c.throttledGC)
|
||||
}()
|
||||
|
||||
var expi exporter.ExporterInstance
|
||||
// TODO: multiworker
|
||||
// This is actually tricky, as the exporter should come from the worker that has the returned reference. We may need to delay this so that the solver loads this.
|
||||
|
@ -313,11 +327,55 @@ func (c *Controller) ListWorkers(ctx context.Context, r *controlapi.ListWorkersR
|
|||
ID: w.ID(),
|
||||
Labels: w.Labels(),
|
||||
Platforms: pb.PlatformsFromSpec(w.Platforms()),
|
||||
GCPolicy: toPBGCPolicy(w.GCPolicy()),
|
||||
})
|
||||
}
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
func (c *Controller) gc() {
|
||||
c.gcmu.Lock()
|
||||
defer c.gcmu.Unlock()
|
||||
|
||||
workers, err := c.opt.WorkerController.List()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
eg, ctx := errgroup.WithContext(context.TODO())
|
||||
|
||||
var size int64
|
||||
ch := make(chan client.UsageInfo)
|
||||
done := make(chan struct{})
|
||||
go func() {
|
||||
for ui := range ch {
|
||||
size += ui.Size
|
||||
}
|
||||
close(done)
|
||||
}()
|
||||
|
||||
for _, w := range workers {
|
||||
func(w worker.Worker) {
|
||||
eg.Go(func() error {
|
||||
if policy := w.GCPolicy(); len(policy) > 0 {
|
||||
return w.Prune(ctx, ch, policy...)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}(w)
|
||||
}
|
||||
|
||||
err = eg.Wait()
|
||||
close(ch)
|
||||
if err != nil {
|
||||
logrus.Errorf("gc error: %+v", err)
|
||||
}
|
||||
<-done
|
||||
if size > 0 {
|
||||
logrus.Debugf("gc cleaned up %d bytes", size)
|
||||
}
|
||||
}
|
||||
|
||||
func parseCacheExporterOpt(opt map[string]string) solver.CacheExportMode {
|
||||
for k, v := range opt {
|
||||
switch k {
|
||||
|
@ -336,3 +394,16 @@ func parseCacheExporterOpt(opt map[string]string) solver.CacheExportMode {
|
|||
}
|
||||
return solver.CacheExportModeMin
|
||||
}
|
||||
|
||||
func toPBGCPolicy(in []client.PruneInfo) []*apitypes.GCPolicy {
|
||||
policy := make([]*apitypes.GCPolicy, 0, len(in))
|
||||
for _, p := range in {
|
||||
policy = append(policy, &apitypes.GCPolicy{
|
||||
All: p.All,
|
||||
KeepBytes: p.KeepBytes,
|
||||
KeepDuration: int64(p.KeepDuration),
|
||||
Filters: p.Filter,
|
||||
})
|
||||
}
|
||||
return policy
|
||||
}
|
||||
|
|
|
@ -36,6 +36,8 @@ type Opt struct {
|
|||
CommandCandidates []string
|
||||
// without root privileges (has nothing to do with Opt.Root directory)
|
||||
Rootless bool
|
||||
// DefaultCgroupParent is the cgroup-parent name for executor
|
||||
DefaultCgroupParent string
|
||||
}
|
||||
|
||||
var defaultCommandCandidates = []string{"buildkit-runc", "runc"}
|
||||
|
@ -44,6 +46,7 @@ type runcExecutor struct {
|
|||
runc *runc.Runc
|
||||
root string
|
||||
cmd string
|
||||
cgroupParent string
|
||||
rootless bool
|
||||
networkProviders map[pb.NetMode]network.Provider
|
||||
}
|
||||
|
@ -94,6 +97,7 @@ func New(opt Opt, networkProviders map[pb.NetMode]network.Provider) (executor.Ex
|
|||
w := &runcExecutor{
|
||||
runc: runtime,
|
||||
root: root,
|
||||
cgroupParent: opt.DefaultCgroupParent,
|
||||
rootless: opt.Rootless,
|
||||
networkProviders: networkProviders,
|
||||
}
|
||||
|
@ -173,6 +177,17 @@ func (w *runcExecutor) Exec(ctx context.Context, meta executor.Meta, root cache.
|
|||
if meta.ReadonlyRootFS {
|
||||
opts = append(opts, containerdoci.WithRootFSReadonly())
|
||||
}
|
||||
|
||||
if w.cgroupParent != "" {
|
||||
var cgroupsPath string
|
||||
lastSeparator := w.cgroupParent[len(w.cgroupParent)-1:]
|
||||
if strings.Contains(w.cgroupParent, ".slice") && lastSeparator == ":" {
|
||||
cgroupsPath = w.cgroupParent + id
|
||||
} else {
|
||||
cgroupsPath = filepath.Join("/", w.cgroupParent, "buildkit", id)
|
||||
}
|
||||
opts = append(opts, containerdoci.WithCgroup(cgroupsPath))
|
||||
}
|
||||
spec, cleanup, err := oci.GenerateSpec(ctx, meta, mounts, id, resolvConf, hostsFile, namespace, opts...)
|
||||
if err != nil {
|
||||
return err
|
||||
|
|
2
vendor/github.com/moby/buildkit/frontend/dockerfile/dockerfile2llb/convert.go
сгенерированный
поставляемый
2
vendor/github.com/moby/buildkit/frontend/dockerfile/dockerfile2llb/convert.go
сгенерированный
поставляемый
|
@ -347,7 +347,7 @@ func Dockerfile2LLB(ctx context.Context, dt []byte, opt ConvertOpt) (*llb.State,
|
|||
}
|
||||
buildContext.Output = bc.Output()
|
||||
|
||||
st := target.state.SetMarhalDefaults(llb.Platform(platformOpt.targetPlatform))
|
||||
st := target.state.SetMarshalDefaults(llb.Platform(platformOpt.targetPlatform))
|
||||
|
||||
if !platformOpt.implicitTarget {
|
||||
target.image.OS = platformOpt.targetPlatform.OS
|
||||
|
|
6
vendor/github.com/moby/buildkit/frontend/gateway/forwarder/forward.go
сгенерированный
поставляемый
6
vendor/github.com/moby/buildkit/frontend/gateway/forwarder/forward.go
сгенерированный
поставляемый
|
@ -67,7 +67,11 @@ func (c *bridgeClient) Solve(ctx context.Context, req client.SolveRequest) (*cli
|
|||
func (c *bridgeClient) BuildOpts() client.BuildOpts {
|
||||
workers := make([]client.WorkerInfo, 0, len(c.workerInfos))
|
||||
for _, w := range c.workerInfos {
|
||||
workers = append(workers, client.WorkerInfo(w))
|
||||
workers = append(workers, client.WorkerInfo{
|
||||
ID: w.ID,
|
||||
Labels: w.Labels,
|
||||
Platforms: w.Platforms,
|
||||
})
|
||||
}
|
||||
|
||||
return client.BuildOpts{
|
||||
|
|
58
vendor/github.com/moby/buildkit/util/throttle/throttle.go
сгенерированный
поставляемый
Normal file
58
vendor/github.com/moby/buildkit/util/throttle/throttle.go
сгенерированный
поставляемый
Normal file
|
@ -0,0 +1,58 @@
|
|||
package throttle
|
||||
|
||||
import (
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Throttle wraps a function so that internal function does not get called
|
||||
// more frequently than the specified duration.
|
||||
func Throttle(d time.Duration, f func()) func() {
|
||||
return throttle(d, f, true)
|
||||
}
|
||||
|
||||
// ThrottleAfter wraps a function so that internal function does not get called
|
||||
// more frequently than the specified duration. The delay is added after function
|
||||
// has been called.
|
||||
func ThrottleAfter(d time.Duration, f func()) func() {
|
||||
return throttle(d, f, false)
|
||||
}
|
||||
|
||||
func throttle(d time.Duration, f func(), wait bool) func() {
|
||||
var next, running bool
|
||||
var mu sync.Mutex
|
||||
return func() {
|
||||
mu.Lock()
|
||||
defer mu.Unlock()
|
||||
|
||||
next = true
|
||||
if !running {
|
||||
running = true
|
||||
go func() {
|
||||
for {
|
||||
mu.Lock()
|
||||
if next == false {
|
||||
running = false
|
||||
mu.Unlock()
|
||||
return
|
||||
}
|
||||
if !wait {
|
||||
next = false
|
||||
}
|
||||
mu.Unlock()
|
||||
|
||||
if wait {
|
||||
time.Sleep(d)
|
||||
mu.Lock()
|
||||
next = false
|
||||
mu.Unlock()
|
||||
f()
|
||||
} else {
|
||||
f()
|
||||
time.Sleep(d)
|
||||
}
|
||||
}
|
||||
}()
|
||||
}
|
||||
}
|
||||
}
|
|
@ -20,6 +20,7 @@ type Worker interface {
|
|||
ID() string
|
||||
Labels() map[string]string
|
||||
Platforms() []specs.Platform
|
||||
GCPolicy() []client.PruneInfo
|
||||
LoadRef(id string) (cache.ImmutableRef, error)
|
||||
// ResolveOp resolves Vertex.Sys() to Op implementation.
|
||||
ResolveOp(v solver.Vertex, s frontend.FrontendLLBBridge) (solver.Op, error)
|
||||
|
@ -28,7 +29,7 @@ type Worker interface {
|
|||
Exec(ctx context.Context, meta executor.Meta, rootFS cache.ImmutableRef, stdin io.ReadCloser, stdout, stderr io.WriteCloser) error
|
||||
DiskUsage(ctx context.Context, opt client.DiskUsageInfo) ([]*client.UsageInfo, error)
|
||||
Exporter(name string) (exporter.Exporter, error)
|
||||
Prune(ctx context.Context, ch chan client.UsageInfo, opt client.PruneInfo) error
|
||||
Prune(ctx context.Context, ch chan client.UsageInfo, opt ...client.PruneInfo) error
|
||||
GetRemote(ctx context.Context, ref cache.ImmutableRef, createIfNeeded bool) (*solver.Remote, error)
|
||||
FromRemote(ctx context.Context, remote *solver.Remote) (cache.ImmutableRef, error)
|
||||
}
|
||||
|
|
Загрузка…
Ссылка в новой задаче