vendor: update buildkit and fsutil

Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>
This commit is contained in:
Tonis Tiigi 2019-02-11 18:37:22 -08:00
parent 05e7d000f2
commit 5166013119
79 changed files with 7368 additions and 1414 deletions

View file

@ -17,6 +17,7 @@ import (
units "github.com/docker/go-units"
"github.com/moby/buildkit/cache"
"github.com/moby/buildkit/cache/metadata"
"github.com/moby/buildkit/cache/remotecache"
registryremotecache "github.com/moby/buildkit/cache/remotecache/registry"
"github.com/moby/buildkit/client"
"github.com/moby/buildkit/control"
@ -167,11 +168,13 @@ func newController(rt http.RoundTripper, opt Opt) (*control.Controller, error) {
}
return control.NewController(control.Opt{
SessionManager: opt.SessionManager,
WorkerController: wc,
Frontends: frontends,
CacheKeyStorage: cacheStorage,
ResolveCacheImporterFunc: registryremotecache.ResolveCacheImporterFunc(opt.SessionManager, opt.ResolverOpt),
SessionManager: opt.SessionManager,
WorkerController: wc,
Frontends: frontends,
CacheKeyStorage: cacheStorage,
ResolveCacheImporterFuncs: map[string]remotecache.ResolveCacheImporterFunc{
"registry": registryremotecache.ResolveCacheImporterFunc(opt.SessionManager, opt.ResolverOpt),
},
// TODO: set ResolveCacheExporterFunc for exporting cache
})
}

View file

@ -26,13 +26,14 @@ github.com/imdario/mergo v0.3.6
golang.org/x/sync 1d60e4601c6fd243af51cc01ddf169918a5407ca
# buildkit
github.com/moby/buildkit 34ff9c2366a878ada7938d2f9ede71741b0a220c
github.com/tonistiigi/fsutil 2862f6bc5ac9b97124e552a5c108230b38a1b0ca
github.com/moby/buildkit c35410878ab9070498c66f6c67d3e8bc3b92241f
github.com/tonistiigi/fsutil 1ec1983587cde7e8ac2978e354ff5360af622464
github.com/grpc-ecosystem/grpc-opentracing 8e809c8a86450a29b90dcc9efbf062d0fe6d9746
github.com/opentracing/opentracing-go 1361b9cd60be79c4c3a7fa9841b3c132e40066a7
github.com/google/shlex 6f45313302b9c56850fc17f99e40caebce98c716
github.com/opentracing-contrib/go-stdlib b1a47cfbdd7543e70e9ef3e73d0802ad306cc1cc
github.com/mitchellh/hashstructure 2bca23e0e452137f789efbc8610126fd8b94f73b
github.com/gofrs/flock 7f43ea2e6a643ad441fc12d0ecc0d3388b300c53 # v0.7.0
#get libnetwork packages

View file

@ -0,0 +1,492 @@
/*
Copyright The containerd Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package content
import (
"context"
"io"
"sync"
api "github.com/containerd/containerd/api/services/content/v1"
"github.com/containerd/containerd/content"
"github.com/containerd/containerd/errdefs"
"github.com/containerd/containerd/log"
"github.com/containerd/containerd/plugin"
"github.com/containerd/containerd/services"
ptypes "github.com/gogo/protobuf/types"
digest "github.com/opencontainers/go-digest"
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
"google.golang.org/grpc"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
)
type service struct {
store content.Store
}
var bufPool = sync.Pool{
New: func() interface{} {
buffer := make([]byte, 1<<20)
return &buffer
},
}
var _ api.ContentServer = &service{}
func init() {
plugin.Register(&plugin.Registration{
Type: plugin.GRPCPlugin,
ID: "content",
Requires: []plugin.Type{
plugin.ServicePlugin,
},
InitFn: func(ic *plugin.InitContext) (interface{}, error) {
plugins, err := ic.GetByType(plugin.ServicePlugin)
if err != nil {
return nil, err
}
p, ok := plugins[services.ContentService]
if !ok {
return nil, errors.New("content store service not found")
}
cs, err := p.Instance()
if err != nil {
return nil, err
}
return NewService(cs.(content.Store)), nil
},
})
}
// NewService returns the content GRPC server
func NewService(cs content.Store) api.ContentServer {
return &service{store: cs}
}
func (s *service) Register(server *grpc.Server) error {
api.RegisterContentServer(server, s)
return nil
}
func (s *service) Info(ctx context.Context, req *api.InfoRequest) (*api.InfoResponse, error) {
if err := req.Digest.Validate(); err != nil {
return nil, status.Errorf(codes.InvalidArgument, "%q failed validation", req.Digest)
}
bi, err := s.store.Info(ctx, req.Digest)
if err != nil {
return nil, errdefs.ToGRPC(err)
}
return &api.InfoResponse{
Info: infoToGRPC(bi),
}, nil
}
func (s *service) Update(ctx context.Context, req *api.UpdateRequest) (*api.UpdateResponse, error) {
if err := req.Info.Digest.Validate(); err != nil {
return nil, status.Errorf(codes.InvalidArgument, "%q failed validation", req.Info.Digest)
}
info, err := s.store.Update(ctx, infoFromGRPC(req.Info), req.UpdateMask.GetPaths()...)
if err != nil {
return nil, errdefs.ToGRPC(err)
}
return &api.UpdateResponse{
Info: infoToGRPC(info),
}, nil
}
func (s *service) List(req *api.ListContentRequest, session api.Content_ListServer) error {
var (
buffer []api.Info
sendBlock = func(block []api.Info) error {
// send last block
return session.Send(&api.ListContentResponse{
Info: block,
})
}
)
if err := s.store.Walk(session.Context(), func(info content.Info) error {
buffer = append(buffer, api.Info{
Digest: info.Digest,
Size_: info.Size,
CreatedAt: info.CreatedAt,
Labels: info.Labels,
})
if len(buffer) >= 100 {
if err := sendBlock(buffer); err != nil {
return err
}
buffer = buffer[:0]
}
return nil
}, req.Filters...); err != nil {
return err
}
if len(buffer) > 0 {
// send last block
if err := sendBlock(buffer); err != nil {
return err
}
}
return nil
}
func (s *service) Delete(ctx context.Context, req *api.DeleteContentRequest) (*ptypes.Empty, error) {
log.G(ctx).WithField("digest", req.Digest).Debugf("delete content")
if err := req.Digest.Validate(); err != nil {
return nil, status.Errorf(codes.InvalidArgument, err.Error())
}
if err := s.store.Delete(ctx, req.Digest); err != nil {
return nil, errdefs.ToGRPC(err)
}
return &ptypes.Empty{}, nil
}
func (s *service) Read(req *api.ReadContentRequest, session api.Content_ReadServer) error {
if err := req.Digest.Validate(); err != nil {
return status.Errorf(codes.InvalidArgument, "%v: %v", req.Digest, err)
}
oi, err := s.store.Info(session.Context(), req.Digest)
if err != nil {
return errdefs.ToGRPC(err)
}
ra, err := s.store.ReaderAt(session.Context(), ocispec.Descriptor{Digest: req.Digest})
if err != nil {
return errdefs.ToGRPC(err)
}
defer ra.Close()
var (
offset = req.Offset
// size is read size, not the expected size of the blob (oi.Size), which the caller might not be aware of.
// offset+size can be larger than oi.Size.
size = req.Size_
// TODO(stevvooe): Using the global buffer pool. At 32KB, it is probably
// little inefficient for work over a fast network. We can tune this later.
p = bufPool.Get().(*[]byte)
)
defer bufPool.Put(p)
if offset < 0 {
offset = 0
}
if offset > oi.Size {
return status.Errorf(codes.OutOfRange, "read past object length %v bytes", oi.Size)
}
if size <= 0 || offset+size > oi.Size {
size = oi.Size - offset
}
_, err = io.CopyBuffer(
&readResponseWriter{session: session},
io.NewSectionReader(ra, offset, size), *p)
return errdefs.ToGRPC(err)
}
// readResponseWriter is a writer that places the output into ReadContentRequest messages.
//
// This allows io.CopyBuffer to do the heavy lifting of chunking the responses
// into the buffer size.
type readResponseWriter struct {
offset int64
session api.Content_ReadServer
}
func (rw *readResponseWriter) Write(p []byte) (n int, err error) {
if err := rw.session.Send(&api.ReadContentResponse{
Offset: rw.offset,
Data: p,
}); err != nil {
return 0, err
}
rw.offset += int64(len(p))
return len(p), nil
}
func (s *service) Status(ctx context.Context, req *api.StatusRequest) (*api.StatusResponse, error) {
status, err := s.store.Status(ctx, req.Ref)
if err != nil {
return nil, errdefs.ToGRPCf(err, "could not get status for ref %q", req.Ref)
}
var resp api.StatusResponse
resp.Status = &api.Status{
StartedAt: status.StartedAt,
UpdatedAt: status.UpdatedAt,
Ref: status.Ref,
Offset: status.Offset,
Total: status.Total,
Expected: status.Expected,
}
return &resp, nil
}
func (s *service) ListStatuses(ctx context.Context, req *api.ListStatusesRequest) (*api.ListStatusesResponse, error) {
statuses, err := s.store.ListStatuses(ctx, req.Filters...)
if err != nil {
return nil, errdefs.ToGRPC(err)
}
var resp api.ListStatusesResponse
for _, status := range statuses {
resp.Statuses = append(resp.Statuses, api.Status{
StartedAt: status.StartedAt,
UpdatedAt: status.UpdatedAt,
Ref: status.Ref,
Offset: status.Offset,
Total: status.Total,
Expected: status.Expected,
})
}
return &resp, nil
}
func (s *service) Write(session api.Content_WriteServer) (err error) {
var (
ctx = session.Context()
msg api.WriteContentResponse
req *api.WriteContentRequest
ref string
total int64
expected digest.Digest
)
defer func(msg *api.WriteContentResponse) {
// pump through the last message if no error was encountered
if err != nil {
if s, ok := status.FromError(err); ok && s.Code() != codes.AlreadyExists {
// TODO(stevvooe): Really need a log line here to track which
// errors are actually causing failure on the server side. May want
// to configure the service with an interceptor to make this work
// identically across all GRPC methods.
//
// This is pretty noisy, so we can remove it but leave it for now.
log.G(ctx).WithError(err).Error("(*service).Write failed")
}
return
}
err = session.Send(msg)
}(&msg)
// handle the very first request!
req, err = session.Recv()
if err != nil {
return err
}
ref = req.Ref
if ref == "" {
return status.Errorf(codes.InvalidArgument, "first message must have a reference")
}
fields := logrus.Fields{
"ref": ref,
}
total = req.Total
expected = req.Expected
if total > 0 {
fields["total"] = total
}
if expected != "" {
fields["expected"] = expected
}
ctx = log.WithLogger(ctx, log.G(ctx).WithFields(fields))
log.G(ctx).Debug("(*service).Write started")
// this action locks the writer for the session.
wr, err := s.store.Writer(ctx,
content.WithRef(ref),
content.WithDescriptor(ocispec.Descriptor{Size: total, Digest: expected}))
if err != nil {
return errdefs.ToGRPC(err)
}
defer wr.Close()
for {
msg.Action = req.Action
ws, err := wr.Status()
if err != nil {
return errdefs.ToGRPC(err)
}
msg.Offset = ws.Offset // always set the offset.
// NOTE(stevvooe): In general, there are two cases underwhich a remote
// writer is used.
//
// For pull, we almost always have this before fetching large content,
// through descriptors. We allow predeclaration of the expected size
// and digest.
//
// For push, it is more complex. If we want to cut through content into
// storage, we may have no expectation until we are done processing the
// content. The case here is the following:
//
// 1. Start writing content.
// 2. Compress inline.
// 3. Validate digest and size (maybe).
//
// Supporting these two paths is quite awkward but it lets both API
// users use the same writer style for each with a minimum of overhead.
if req.Expected != "" {
if expected != "" && expected != req.Expected {
log.G(ctx).Debugf("commit digest differs from writer digest: %v != %v", req.Expected, expected)
}
expected = req.Expected
if _, err := s.store.Info(session.Context(), req.Expected); err == nil {
if err := wr.Close(); err != nil {
log.G(ctx).WithError(err).Error("failed to close writer")
}
if err := s.store.Abort(session.Context(), ref); err != nil {
log.G(ctx).WithError(err).Error("failed to abort write")
}
return status.Errorf(codes.AlreadyExists, "blob with expected digest %v exists", req.Expected)
}
}
if req.Total > 0 {
// Update the expected total. Typically, this could be seen at
// negotiation time or on a commit message.
if total > 0 && req.Total != total {
log.G(ctx).Debugf("commit size differs from writer size: %v != %v", req.Total, total)
}
total = req.Total
}
switch req.Action {
case api.WriteActionStat:
msg.Digest = wr.Digest()
msg.StartedAt = ws.StartedAt
msg.UpdatedAt = ws.UpdatedAt
msg.Total = total
case api.WriteActionWrite, api.WriteActionCommit:
if req.Offset > 0 {
// validate the offset if provided
if req.Offset != ws.Offset {
return status.Errorf(codes.OutOfRange, "write @%v must occur at current offset %v", req.Offset, ws.Offset)
}
}
if req.Offset == 0 && ws.Offset > 0 {
if err := wr.Truncate(req.Offset); err != nil {
return errors.Wrapf(err, "truncate failed")
}
msg.Offset = req.Offset
}
// issue the write if we actually have data.
if len(req.Data) > 0 {
// While this looks like we could use io.WriterAt here, because we
// maintain the offset as append only, we just issue the write.
n, err := wr.Write(req.Data)
if err != nil {
return errdefs.ToGRPC(err)
}
if n != len(req.Data) {
// TODO(stevvooe): Perhaps, we can recover this by including it
// in the offset on the write return.
return status.Errorf(codes.DataLoss, "wrote %v of %v bytes", n, len(req.Data))
}
msg.Offset += int64(n)
}
if req.Action == api.WriteActionCommit {
var opts []content.Opt
if req.Labels != nil {
opts = append(opts, content.WithLabels(req.Labels))
}
if err := wr.Commit(ctx, total, expected, opts...); err != nil {
return errdefs.ToGRPC(err)
}
}
msg.Digest = wr.Digest()
}
if err := session.Send(&msg); err != nil {
return err
}
req, err = session.Recv()
if err != nil {
if err == io.EOF {
return nil
}
return err
}
}
}
func (s *service) Abort(ctx context.Context, req *api.AbortRequest) (*ptypes.Empty, error) {
if err := s.store.Abort(ctx, req.Ref); err != nil {
return nil, errdefs.ToGRPC(err)
}
return &ptypes.Empty{}, nil
}
func infoToGRPC(info content.Info) api.Info {
return api.Info{
Digest: info.Digest,
Size_: info.Size,
CreatedAt: info.CreatedAt,
UpdatedAt: info.UpdatedAt,
Labels: info.Labels,
}
}
func infoFromGRPC(info api.Info) content.Info {
return content.Info{
Digest: info.Digest,
Size: info.Size_,
CreatedAt: info.CreatedAt,
UpdatedAt: info.UpdatedAt,
Labels: info.Labels,
}
}

View file

@ -0,0 +1,71 @@
/*
Copyright The containerd Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package content
import (
"context"
eventstypes "github.com/containerd/containerd/api/events"
"github.com/containerd/containerd/content"
"github.com/containerd/containerd/events"
"github.com/containerd/containerd/metadata"
"github.com/containerd/containerd/plugin"
"github.com/containerd/containerd/services"
digest "github.com/opencontainers/go-digest"
)
// store wraps content.Store with proper event published.
type store struct {
content.Store
publisher events.Publisher
}
func init() {
plugin.Register(&plugin.Registration{
Type: plugin.ServicePlugin,
ID: services.ContentService,
Requires: []plugin.Type{
plugin.MetadataPlugin,
},
InitFn: func(ic *plugin.InitContext) (interface{}, error) {
m, err := ic.Get(plugin.MetadataPlugin)
if err != nil {
return nil, err
}
s, err := newContentStore(m.(*metadata.DB).ContentStore(), ic.Events)
return s, err
},
})
}
func newContentStore(cs content.Store, publisher events.Publisher) (content.Store, error) {
return &store{
Store: cs,
publisher: publisher,
}, nil
}
func (s *store) Delete(ctx context.Context, dgst digest.Digest) error {
if err := s.Store.Delete(ctx, dgst); err != nil {
return err
}
// TODO: Consider whether we should return error here.
return s.publisher.Publish(ctx, "/content/delete", &eventstypes.ContentDelete{
Digest: dgst,
})
}

View file

@ -0,0 +1,36 @@
/*
Copyright The containerd Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package services
const (
// ContentService is id of content service.
ContentService = "content-service"
// SnapshotsService is id of snapshots service.
SnapshotsService = "snapshots-service"
// ImagesService is id of images service.
ImagesService = "images-service"
// ContainersService is id of containers service.
ContainersService = "containers-service"
// TasksService is id of tasks service.
TasksService = "tasks-service"
// NamespacesService is id of namespaces service.
NamespacesService = "namespaces-service"
// LeasesService is id of leases service.
LeasesService = "leases-service"
// DiffService is id of diff service.
DiffService = "diff-service"
)

27
vendor/github.com/gofrs/flock/LICENSE generated vendored Normal file
View file

@ -0,0 +1,27 @@
Copyright (c) 2015, Tim Heckman
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* Neither the name of linode-netint nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

40
vendor/github.com/gofrs/flock/README.md generated vendored Normal file
View file

@ -0,0 +1,40 @@
# flock
[![TravisCI Build Status](https://img.shields.io/travis/gofrs/flock/master.svg?style=flat)](https://travis-ci.org/gofrs/flock)
[![GoDoc](https://img.shields.io/badge/godoc-go--flock-blue.svg?style=flat)](https://godoc.org/github.com/gofrs/flock)
[![License](https://img.shields.io/badge/license-BSD_3--Clause-brightgreen.svg?style=flat)](https://github.com/gofrs/flock/blob/master/LICENSE)
`flock` implements a thread-safe sync.Locker interface for file locking. It also
includes a non-blocking TryLock() function to allow locking without blocking execution.
## License
`flock` is released under the BSD 3-Clause License. See the `LICENSE` file for more details.
## Go Compatibility
This package makes use of the `context` package that was introduced in Go 1.7. As such, this
package has an implicit dependency on Go 1.7+.
## Installation
```
go get -u github.com/gofrs/flock
```
## Usage
```Go
import "github.com/gofrs/flock"
fileLock := flock.New("/var/lock/go-lock.lock")
locked, err := fileLock.TryLock()
if err != nil {
// handle locking error
}
if locked {
// do work
fileLock.Unlock()
}
```
For more detailed usage information take a look at the package API docs on
[GoDoc](https://godoc.org/github.com/gofrs/flock).

127
vendor/github.com/gofrs/flock/flock.go generated vendored Normal file
View file

@ -0,0 +1,127 @@
// Copyright 2015 Tim Heckman. All rights reserved.
// Use of this source code is governed by the BSD 3-Clause
// license that can be found in the LICENSE file.
// Package flock implements a thread-safe sync.Locker interface for file locking.
// It also includes a non-blocking TryLock() function to allow locking
// without blocking execution.
//
// Package flock is released under the BSD 3-Clause License. See the LICENSE file
// for more details.
//
// While using this library, remember that the locking behaviors are not
// guaranteed to be the same on each platform. For example, some UNIX-like
// operating systems will transparently convert a shared lock to an exclusive
// lock. If you Unlock() the flock from a location where you believe that you
// have the shared lock, you may accidently drop the exclusive lock.
package flock
import (
"context"
"os"
"sync"
"time"
)
// Flock is the struct type to handle file locking. All fields are unexported,
// with access to some of the fields provided by getter methods (Path() and Locked()).
type Flock struct {
path string
m sync.RWMutex
fh *os.File
l bool
r bool
}
// New returns a new instance of *Flock. The only parameter
// it takes is the path to the desired lockfile.
func New(path string) *Flock {
return &Flock{path: path}
}
// NewFlock returns a new instance of *Flock. The only parameter
// it takes is the path to the desired lockfile.
//
// Deprecated: Use New instead.
func NewFlock(path string) *Flock {
return New(path)
}
// Close is equivalent to calling Unlock.
//
// This will release the lock and close the underlying file descriptor.
// It will not remove the file from disk, that's up to your application.
func (f *Flock) Close() error {
return f.Unlock()
}
// Path returns the path as provided in NewFlock().
func (f *Flock) Path() string {
return f.path
}
// Locked returns the lock state (locked: true, unlocked: false).
//
// Warning: by the time you use the returned value, the state may have changed.
func (f *Flock) Locked() bool {
f.m.RLock()
defer f.m.RUnlock()
return f.l
}
// RLocked returns the read lock state (locked: true, unlocked: false).
//
// Warning: by the time you use the returned value, the state may have changed.
func (f *Flock) RLocked() bool {
f.m.RLock()
defer f.m.RUnlock()
return f.r
}
func (f *Flock) String() string {
return f.path
}
// TryLockContext repeatedly tries to take an exclusive lock until one of the
// conditions is met: TryLock succeeds, TryLock fails with error, or Context
// Done channel is closed.
func (f *Flock) TryLockContext(ctx context.Context, retryDelay time.Duration) (bool, error) {
return tryCtx(f.TryLock, ctx, retryDelay)
}
// TryRLockContext repeatedly tries to take a shared lock until one of the
// conditions is met: TryRLock succeeds, TryRLock fails with error, or Context
// Done channel is closed.
func (f *Flock) TryRLockContext(ctx context.Context, retryDelay time.Duration) (bool, error) {
return tryCtx(f.TryRLock, ctx, retryDelay)
}
func tryCtx(fn func() (bool, error), ctx context.Context, retryDelay time.Duration) (bool, error) {
if ctx.Err() != nil {
return false, ctx.Err()
}
for {
if ok, err := fn(); ok || err != nil {
return ok, err
}
select {
case <-ctx.Done():
return false, ctx.Err()
case <-time.After(retryDelay):
// try again
}
}
}
func (f *Flock) setFh() error {
// open a new os.File instance
// create it if it doesn't exist, and open the file read-only.
fh, err := os.OpenFile(f.path, os.O_CREATE|os.O_RDONLY, os.FileMode(0600))
if err != nil {
return err
}
// set the filehandle on the struct
f.fh = fh
return nil
}

195
vendor/github.com/gofrs/flock/flock_unix.go generated vendored Normal file
View file

@ -0,0 +1,195 @@
// Copyright 2015 Tim Heckman. All rights reserved.
// Use of this source code is governed by the BSD 3-Clause
// license that can be found in the LICENSE file.
// +build !windows
package flock
import (
"os"
"syscall"
)
// Lock is a blocking call to try and take an exclusive file lock. It will wait
// until it is able to obtain the exclusive file lock. It's recommended that
// TryLock() be used over this function. This function may block the ability to
// query the current Locked() or RLocked() status due to a RW-mutex lock.
//
// If we are already exclusive-locked, this function short-circuits and returns
// immediately assuming it can take the mutex lock.
//
// If the *Flock has a shared lock (RLock), this may transparently replace the
// shared lock with an exclusive lock on some UNIX-like operating systems. Be
// careful when using exclusive locks in conjunction with shared locks
// (RLock()), because calling Unlock() may accidentally release the exclusive
// lock that was once a shared lock.
func (f *Flock) Lock() error {
return f.lock(&f.l, syscall.LOCK_EX)
}
// RLock is a blocking call to try and take a shared file lock. It will wait
// until it is able to obtain the shared file lock. It's recommended that
// TryRLock() be used over this function. This function may block the ability to
// query the current Locked() or RLocked() status due to a RW-mutex lock.
//
// If we are already shared-locked, this function short-circuits and returns
// immediately assuming it can take the mutex lock.
func (f *Flock) RLock() error {
return f.lock(&f.r, syscall.LOCK_SH)
}
func (f *Flock) lock(locked *bool, flag int) error {
f.m.Lock()
defer f.m.Unlock()
if *locked {
return nil
}
if f.fh == nil {
if err := f.setFh(); err != nil {
return err
}
}
if err := syscall.Flock(int(f.fh.Fd()), flag); err != nil {
shouldRetry, reopenErr := f.reopenFDOnError(err)
if reopenErr != nil {
return reopenErr
}
if !shouldRetry {
return err
}
if err = syscall.Flock(int(f.fh.Fd()), flag); err != nil {
return err
}
}
*locked = true
return nil
}
// Unlock is a function to unlock the file. This file takes a RW-mutex lock, so
// while it is running the Locked() and RLocked() functions will be blocked.
//
// This function short-circuits if we are unlocked already. If not, it calls
// syscall.LOCK_UN on the file and closes the file descriptor. It does not
// remove the file from disk. It's up to your application to do.
//
// Please note, if your shared lock became an exclusive lock this may
// unintentionally drop the exclusive lock if called by the consumer that
// believes they have a shared lock. Please see Lock() for more details.
func (f *Flock) Unlock() error {
f.m.Lock()
defer f.m.Unlock()
// if we aren't locked or if the lockfile instance is nil
// just return a nil error because we are unlocked
if (!f.l && !f.r) || f.fh == nil {
return nil
}
// mark the file as unlocked
if err := syscall.Flock(int(f.fh.Fd()), syscall.LOCK_UN); err != nil {
return err
}
f.fh.Close()
f.l = false
f.r = false
f.fh = nil
return nil
}
// TryLock is the preferred function for taking an exclusive file lock. This
// function takes an RW-mutex lock before it tries to lock the file, so there is
// the possibility that this function may block for a short time if another
// goroutine is trying to take any action.
//
// The actual file lock is non-blocking. If we are unable to get the exclusive
// file lock, the function will return false instead of waiting for the lock. If
// we get the lock, we also set the *Flock instance as being exclusive-locked.
func (f *Flock) TryLock() (bool, error) {
return f.try(&f.l, syscall.LOCK_EX)
}
// TryRLock is the preferred function for taking a shared file lock. This
// function takes an RW-mutex lock before it tries to lock the file, so there is
// the possibility that this function may block for a short time if another
// goroutine is trying to take any action.
//
// The actual file lock is non-blocking. If we are unable to get the shared file
// lock, the function will return false instead of waiting for the lock. If we
// get the lock, we also set the *Flock instance as being share-locked.
func (f *Flock) TryRLock() (bool, error) {
return f.try(&f.r, syscall.LOCK_SH)
}
func (f *Flock) try(locked *bool, flag int) (bool, error) {
f.m.Lock()
defer f.m.Unlock()
if *locked {
return true, nil
}
if f.fh == nil {
if err := f.setFh(); err != nil {
return false, err
}
}
var retried bool
retry:
err := syscall.Flock(int(f.fh.Fd()), flag|syscall.LOCK_NB)
switch err {
case syscall.EWOULDBLOCK:
return false, nil
case nil:
*locked = true
return true, nil
}
if !retried {
if shouldRetry, reopenErr := f.reopenFDOnError(err); reopenErr != nil {
return false, reopenErr
} else if shouldRetry {
retried = true
goto retry
}
}
return false, err
}
// reopenFDOnError determines whether we should reopen the file handle
// in readwrite mode and try again. This comes from util-linux/sys-utils/flock.c:
// Since Linux 3.4 (commit 55725513)
// Probably NFSv4 where flock() is emulated by fcntl().
func (f *Flock) reopenFDOnError(err error) (bool, error) {
if err != syscall.EIO && err != syscall.EBADF {
return false, nil
}
if st, err := f.fh.Stat(); err == nil {
// if the file is able to be read and written
if st.Mode()&0600 == 0600 {
f.fh.Close()
f.fh = nil
// reopen in read-write mode and set the filehandle
fh, err := os.OpenFile(f.path, os.O_CREATE|os.O_RDWR, os.FileMode(0600))
if err != nil {
return false, err
}
f.fh = fh
return true, nil
}
}
return false, nil
}

76
vendor/github.com/gofrs/flock/flock_winapi.go generated vendored Normal file
View file

@ -0,0 +1,76 @@
// Copyright 2015 Tim Heckman. All rights reserved.
// Use of this source code is governed by the BSD 3-Clause
// license that can be found in the LICENSE file.
// +build windows
package flock
import (
"syscall"
"unsafe"
)
var (
kernel32, _ = syscall.LoadLibrary("kernel32.dll")
procLockFileEx, _ = syscall.GetProcAddress(kernel32, "LockFileEx")
procUnlockFileEx, _ = syscall.GetProcAddress(kernel32, "UnlockFileEx")
)
const (
winLockfileFailImmediately = 0x00000001
winLockfileExclusiveLock = 0x00000002
winLockfileSharedLock = 0x00000000
)
// Use of 0x00000000 for the shared lock is a guess based on some the MS Windows
// `LockFileEX` docs, which document the `LOCKFILE_EXCLUSIVE_LOCK` flag as:
//
// > The function requests an exclusive lock. Otherwise, it requests a shared
// > lock.
//
// https://msdn.microsoft.com/en-us/library/windows/desktop/aa365203(v=vs.85).aspx
func lockFileEx(handle syscall.Handle, flags uint32, reserved uint32, numberOfBytesToLockLow uint32, numberOfBytesToLockHigh uint32, offset *syscall.Overlapped) (bool, syscall.Errno) {
r1, _, errNo := syscall.Syscall6(
uintptr(procLockFileEx),
6,
uintptr(handle),
uintptr(flags),
uintptr(reserved),
uintptr(numberOfBytesToLockLow),
uintptr(numberOfBytesToLockHigh),
uintptr(unsafe.Pointer(offset)))
if r1 != 1 {
if errNo == 0 {
return false, syscall.EINVAL
}
return false, errNo
}
return true, 0
}
func unlockFileEx(handle syscall.Handle, reserved uint32, numberOfBytesToLockLow uint32, numberOfBytesToLockHigh uint32, offset *syscall.Overlapped) (bool, syscall.Errno) {
r1, _, errNo := syscall.Syscall6(
uintptr(procUnlockFileEx),
5,
uintptr(handle),
uintptr(reserved),
uintptr(numberOfBytesToLockLow),
uintptr(numberOfBytesToLockHigh),
uintptr(unsafe.Pointer(offset)),
0)
if r1 != 1 {
if errNo == 0 {
return false, syscall.EINVAL
}
return false, errNo
}
return true, 0
}

140
vendor/github.com/gofrs/flock/flock_windows.go generated vendored Normal file
View file

@ -0,0 +1,140 @@
// Copyright 2015 Tim Heckman. All rights reserved.
// Use of this source code is governed by the BSD 3-Clause
// license that can be found in the LICENSE file.
package flock
import (
"syscall"
)
// ErrorLockViolation is the error code returned from the Windows syscall when a
// lock would block and you ask to fail immediately.
const ErrorLockViolation syscall.Errno = 0x21 // 33
// Lock is a blocking call to try and take an exclusive file lock. It will wait
// until it is able to obtain the exclusive file lock. It's recommended that
// TryLock() be used over this function. This function may block the ability to
// query the current Locked() or RLocked() status due to a RW-mutex lock.
//
// If we are already locked, this function short-circuits and returns
// immediately assuming it can take the mutex lock.
func (f *Flock) Lock() error {
return f.lock(&f.l, winLockfileExclusiveLock)
}
// RLock is a blocking call to try and take a shared file lock. It will wait
// until it is able to obtain the shared file lock. It's recommended that
// TryRLock() be used over this function. This function may block the ability to
// query the current Locked() or RLocked() status due to a RW-mutex lock.
//
// If we are already locked, this function short-circuits and returns
// immediately assuming it can take the mutex lock.
func (f *Flock) RLock() error {
return f.lock(&f.r, winLockfileSharedLock)
}
func (f *Flock) lock(locked *bool, flag uint32) error {
f.m.Lock()
defer f.m.Unlock()
if *locked {
return nil
}
if f.fh == nil {
if err := f.setFh(); err != nil {
return err
}
}
if _, errNo := lockFileEx(syscall.Handle(f.fh.Fd()), flag, 0, 1, 0, &syscall.Overlapped{}); errNo > 0 {
return errNo
}
*locked = true
return nil
}
// Unlock is a function to unlock the file. This file takes a RW-mutex lock, so
// while it is running the Locked() and RLocked() functions will be blocked.
//
// This function short-circuits if we are unlocked already. If not, it calls
// UnlockFileEx() on the file and closes the file descriptor. It does not remove
// the file from disk. It's up to your application to do.
func (f *Flock) Unlock() error {
f.m.Lock()
defer f.m.Unlock()
// if we aren't locked or if the lockfile instance is nil
// just return a nil error because we are unlocked
if (!f.l && !f.r) || f.fh == nil {
return nil
}
// mark the file as unlocked
if _, errNo := unlockFileEx(syscall.Handle(f.fh.Fd()), 0, 1, 0, &syscall.Overlapped{}); errNo > 0 {
return errNo
}
f.fh.Close()
f.l = false
f.r = false
f.fh = nil
return nil
}
// TryLock is the preferred function for taking an exclusive file lock. This
// function does take a RW-mutex lock before it tries to lock the file, so there
// is the possibility that this function may block for a short time if another
// goroutine is trying to take any action.
//
// The actual file lock is non-blocking. If we are unable to get the exclusive
// file lock, the function will return false instead of waiting for the lock. If
// we get the lock, we also set the *Flock instance as being exclusive-locked.
func (f *Flock) TryLock() (bool, error) {
return f.try(&f.l, winLockfileExclusiveLock)
}
// TryRLock is the preferred function for taking a shared file lock. This
// function does take a RW-mutex lock before it tries to lock the file, so there
// is the possibility that this function may block for a short time if another
// goroutine is trying to take any action.
//
// The actual file lock is non-blocking. If we are unable to get the shared file
// lock, the function will return false instead of waiting for the lock. If we
// get the lock, we also set the *Flock instance as being shared-locked.
func (f *Flock) TryRLock() (bool, error) {
return f.try(&f.r, winLockfileSharedLock)
}
func (f *Flock) try(locked *bool, flag uint32) (bool, error) {
f.m.Lock()
defer f.m.Unlock()
if *locked {
return true, nil
}
if f.fh == nil {
if err := f.setFh(); err != nil {
return false, err
}
}
_, errNo := lockFileEx(syscall.Handle(f.fh.Fd()), flag|winLockfileFailImmediately, 0, 1, 0, &syscall.Overlapped{})
if errNo > 0 {
if errNo == ErrorLockViolation || errNo == syscall.ERROR_IO_PENDING {
return false, nil
}
return false, errNo
}
*locked = true
return true, nil
}

View file

@ -122,7 +122,7 @@ During development, Dockerfile frontend (dockerfile.v0) is also part of the Buil
```
buildctl build --frontend=dockerfile.v0 --local context=. --local dockerfile=.
buildctl build --frontend=dockerfile.v0 --local context=. --local dockerfile=. --frontend-opt target=foo --frontend-opt build-arg:foo=bar
buildctl build --frontend=dockerfile.v0 --local context=. --local dockerfile=. --opt target=foo --opt build-arg:foo=bar
```
`--local` exposes local source files from client to the builder. `context` and `dockerfile` are the names Dockerfile frontend looks for build context and Dockerfile location.
@ -146,31 +146,31 @@ docker inspect myimage
External versions of the Dockerfile frontend are pushed to https://hub.docker.com/r/docker/dockerfile-upstream and https://hub.docker.com/r/docker/dockerfile and can be used with the gateway frontend. The source for the external frontend is currently located in `./frontend/dockerfile/cmd/dockerfile-frontend` but will move out of this repository in the future ([#163](https://github.com/moby/buildkit/issues/163)). For automatic build from master branch of this repository `docker/dockerfile-upsteam:master` or `docker/dockerfile-upstream:master-experimental` image can be used.
```
buildctl build --frontend=gateway.v0 --frontend-opt=source=docker/dockerfile --local context=. --local dockerfile=.
buildctl build --frontend gateway.v0 --frontend-opt=source=docker/dockerfile --frontend-opt=context=git://github.com/moby/moby --frontend-opt build-arg:APT_MIRROR=cdn-fastly.deb.debian.org
buildctl build --frontend gateway.v0 --opt source=docker/dockerfile --local context=. --local dockerfile=.
buildctl build --frontend gateway.v0 --opt source=docker/dockerfile --opt context=git://github.com/moby/moby --opt build-arg:APT_MIRROR=cdn-fastly.deb.debian.org
````
##### Building a Dockerfile with experimental features like `RUN --mount=type=(bind|cache|tmpfs|secret|ssh)`
See [`frontend/dockerfile/docs/experimental.md`](frontend/dockerfile/docs/experimental.md).
### Exporters
### Output
By default, the build result and intermediate cache will only remain internally in BuildKit. Exporter needs to be specified to retrieve the result.
By default, the build result and intermediate cache will only remain internally in BuildKit. An output needs to be specified to retrieve the result.
##### Exporting resulting image to containerd
The containerd worker needs to be used
```
buildctl build ... --exporter=image --exporter-opt name=docker.io/username/image
buildctl build ... --output type=image,name=docker.io/username/image
ctr --namespace=buildkit images ls
```
##### Push resulting image to registry
```
buildctl build ... --exporter=image --exporter-opt name=docker.io/username/image --exporter-opt push=true
buildctl build ... --output type=image,name=docker.io/username/image,push=true
```
If credentials are required, `buildctl` will attempt to read Docker configuration file.
@ -181,23 +181,52 @@ If credentials are required, `buildctl` will attempt to read Docker configuratio
The local client will copy the files directly to the client. This is useful if BuildKit is being used for building something else than container images.
```
buildctl build ... --exporter=local --exporter-opt output=path/to/output-dir
buildctl build ... --output type=local,dest=path/to/output-dir
```
##### Exporting built image to Docker
```
# exported tarball is also compatible with OCI spec
buildctl build ... --exporter=docker --exporter-opt name=myimage | docker load
buildctl build ... --output type=docker,name=myimage | docker load
```
##### Exporting [OCI Image Format](https://github.com/opencontainers/image-spec) tarball to client
```
buildctl build ... --exporter=oci --exporter-opt output=path/to/output.tar
buildctl build ... --exporter=oci > output.tar
buildctl build ... --output type=oci,dest=path/to/output.tar
buildctl build ... --output type=oci > output.tar
```
### Exporting/Importing build cache (not image itself)
#### To/From registry
```
buildctl build ... --export-cache type=registry,ref=localhost:5000/myrepo:buildcache
buildctl build ... --import-cache type=registry,ref=localhost:5000/myrepo:buildcache
```
#### To/From local filesystem
```
buildctl build ... --export-cache type=local,src=path/to/input-dir
buildctl build ... --import-cache type=local,dest=path/to/output-dir
```
The directory layout conforms to OCI Image Spec v1.0.
#### `--export-cache` options
* `mode=min` (default): only export layers for the resulting image
* `mode=max`: export all the layers of all intermediate steps
* `ref=docker.io/user/image:tag`: reference for `registry` cache exporter
* `src=path/to/output-dir`: directory for `local` cache exporter
#### `--import-cache` options
* `ref=docker.io/user/image:tag`: reference for `registry` cache importer
* `dest=path/to/input-dir`: directory for `local` cache importer
* `digest=sha256:deadbeef`: digest of the manifest list to import for `local` cache importer. Defaults to the digest of "latest" tag in `index.json`
### Other
#### View build cache
@ -232,6 +261,7 @@ buildctl build --help
```
The images can be also built locally using `./hack/dockerfiles/test.Dockerfile` (or `./hack/dockerfiles/test.buildkit.Dockerfile` if you already have BuildKit).
Run `make images` to build the images as `moby/buildkit:local` and `moby/buildkit:local-rootless`.
### Opentracing support

File diff suppressed because it is too large Load diff

View file

@ -66,9 +66,31 @@ message SolveRequest {
}
message CacheOptions {
string ExportRef = 1;
repeated string ImportRefs = 2;
map<string, string> ExportAttrs = 3;
// ExportRefDeprecated is deprecated in favor or the new Exports since BuildKit v0.4.0.
// When ExportRefDeprecated is set, the solver appends
// {.Type = "registry", .Attrs = ExportAttrs.add("ref", ExportRef)}
// to Exports for compatibility. (planned to be removed)
string ExportRefDeprecated = 1;
// ImportRefsDeprecated is deprecated in favor or the new Imports since BuildKit v0.4.0.
// When ImportRefsDeprecated is set, the solver appends
// {.Type = "registry", .Attrs = {"ref": importRef}}
// for each of the ImportRefs entry to Imports for compatibility. (planned to be removed)
repeated string ImportRefsDeprecated = 2;
// ExportAttrsDeprecated is deprecated since BuildKit v0.4.0.
// See the description of ExportRefDeprecated.
map<string, string> ExportAttrsDeprecated = 3;
// Exports was introduced in BuildKit v0.4.0.
repeated CacheOptionsEntry Exports = 4;
// Imports was introduced in BuildKit v0.4.0.
repeated CacheOptionsEntry Imports = 5;
}
message CacheOptionsEntry {
// Type is like "registry" or "local"
string Type = 1;
// Attrs are like mode=(min,max), ref=example.com:5000/foo/bar .
// See cache importer/exporter implementations' documentation.
map<string, string> Attrs = 2;
}
message SolveResponse {
@ -124,4 +146,4 @@ message ListWorkersRequest {
message ListWorkersResponse {
repeated moby.buildkit.v1.types.WorkerRecord record = 1;
}
}

View file

@ -1,16 +1,6 @@
// Code generated by protoc-gen-gogo. DO NOT EDIT.
// source: worker.proto
/*
Package moby_buildkit_v1_types is a generated protocol buffer package.
It is generated from these files:
worker.proto
It has these top-level messages:
WorkerRecord
GCPolicy
*/
package moby_buildkit_v1_types
import proto "github.com/gogo/protobuf/proto"
@ -33,16 +23,47 @@ var _ = math.Inf
const _ = proto.GoGoProtoPackageIsVersion2 // please upgrade the proto package
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"`
ID string `protobuf:"bytes,1,opt,name=ID,proto3" json:"ID,omitempty"`
Labels map[string]string `protobuf:"bytes,2,rep,name=Labels,proto3" 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,proto3" json:"platforms"`
GCPolicy []*GCPolicy `protobuf:"bytes,4,rep,name=GCPolicy,proto3" json:"GCPolicy,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *WorkerRecord) Reset() { *m = WorkerRecord{} }
func (m *WorkerRecord) String() string { return proto.CompactTextString(m) }
func (*WorkerRecord) ProtoMessage() {}
func (*WorkerRecord) Descriptor() ([]byte, []int) { return fileDescriptorWorker, []int{0} }
func (m *WorkerRecord) Reset() { *m = WorkerRecord{} }
func (m *WorkerRecord) String() string { return proto.CompactTextString(m) }
func (*WorkerRecord) ProtoMessage() {}
func (*WorkerRecord) Descriptor() ([]byte, []int) {
return fileDescriptor_worker_1d0a62be5114ecbf, []int{0}
}
func (m *WorkerRecord) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
}
func (m *WorkerRecord) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
if deterministic {
return xxx_messageInfo_WorkerRecord.Marshal(b, m, deterministic)
} else {
b = b[:cap(b)]
n, err := m.MarshalTo(b)
if err != nil {
return nil, err
}
return b[:n], nil
}
}
func (dst *WorkerRecord) XXX_Merge(src proto.Message) {
xxx_messageInfo_WorkerRecord.Merge(dst, src)
}
func (m *WorkerRecord) XXX_Size() int {
return m.Size()
}
func (m *WorkerRecord) XXX_DiscardUnknown() {
xxx_messageInfo_WorkerRecord.DiscardUnknown(m)
}
var xxx_messageInfo_WorkerRecord proto.InternalMessageInfo
func (m *WorkerRecord) GetID() string {
if m != nil {
@ -73,16 +94,47 @@ func (m *WorkerRecord) GetGCPolicy() []*GCPolicy {
}
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"`
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,proto3" json:"filters,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
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) Reset() { *m = GCPolicy{} }
func (m *GCPolicy) String() string { return proto.CompactTextString(m) }
func (*GCPolicy) ProtoMessage() {}
func (*GCPolicy) Descriptor() ([]byte, []int) {
return fileDescriptor_worker_1d0a62be5114ecbf, []int{1}
}
func (m *GCPolicy) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
}
func (m *GCPolicy) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
if deterministic {
return xxx_messageInfo_GCPolicy.Marshal(b, m, deterministic)
} else {
b = b[:cap(b)]
n, err := m.MarshalTo(b)
if err != nil {
return nil, err
}
return b[:n], nil
}
}
func (dst *GCPolicy) XXX_Merge(src proto.Message) {
xxx_messageInfo_GCPolicy.Merge(dst, src)
}
func (m *GCPolicy) XXX_Size() int {
return m.Size()
}
func (m *GCPolicy) XXX_DiscardUnknown() {
xxx_messageInfo_GCPolicy.DiscardUnknown(m)
}
var xxx_messageInfo_GCPolicy proto.InternalMessageInfo
func (m *GCPolicy) GetAll() bool {
if m != nil {
@ -114,6 +166,7 @@ func (m *GCPolicy) GetFilters() []string {
func init() {
proto.RegisterType((*WorkerRecord)(nil), "moby.buildkit.v1.types.WorkerRecord")
proto.RegisterMapType((map[string]string)(nil), "moby.buildkit.v1.types.WorkerRecord.LabelsEntry")
proto.RegisterType((*GCPolicy)(nil), "moby.buildkit.v1.types.GCPolicy")
}
func (m *WorkerRecord) Marshal() (dAtA []byte, err error) {
@ -178,6 +231,9 @@ func (m *WorkerRecord) MarshalTo(dAtA []byte) (int, error) {
i += n
}
}
if m.XXX_unrecognized != nil {
i += copy(dAtA[i:], m.XXX_unrecognized)
}
return i, nil
}
@ -231,6 +287,9 @@ func (m *GCPolicy) MarshalTo(dAtA []byte) (int, error) {
i += copy(dAtA[i:], s)
}
}
if m.XXX_unrecognized != nil {
i += copy(dAtA[i:], m.XXX_unrecognized)
}
return i, nil
}
@ -244,6 +303,9 @@ func encodeVarintWorker(dAtA []byte, offset int, v uint64) int {
return offset + 1
}
func (m *WorkerRecord) Size() (n int) {
if m == nil {
return 0
}
var l int
_ = l
l = len(m.ID)
@ -270,10 +332,16 @@ func (m *WorkerRecord) Size() (n int) {
n += 1 + l + sovWorker(uint64(l))
}
}
if m.XXX_unrecognized != nil {
n += len(m.XXX_unrecognized)
}
return n
}
func (m *GCPolicy) Size() (n int) {
if m == nil {
return 0
}
var l int
_ = l
if m.All {
@ -291,6 +359,9 @@ func (m *GCPolicy) Size() (n int) {
n += 1 + l + sovWorker(uint64(l))
}
}
if m.XXX_unrecognized != nil {
n += len(m.XXX_unrecognized)
}
return n
}
@ -557,6 +628,7 @@ func (m *WorkerRecord) Unmarshal(dAtA []byte) error {
if (iNdEx + skippy) > l {
return io.ErrUnexpectedEOF
}
m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...)
iNdEx += skippy
}
}
@ -694,6 +766,7 @@ func (m *GCPolicy) Unmarshal(dAtA []byte) error {
if (iNdEx + skippy) > l {
return io.ErrUnexpectedEOF
}
m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...)
iNdEx += skippy
}
}
@ -808,9 +881,9 @@ var (
ErrIntOverflowWorker = fmt.Errorf("proto: integer overflow")
)
func init() { proto.RegisterFile("worker.proto", fileDescriptorWorker) }
func init() { proto.RegisterFile("worker.proto", fileDescriptor_worker_1d0a62be5114ecbf) }
var fileDescriptorWorker = []byte{
var fileDescriptor_worker_1d0a62be5114ecbf = []byte{
// 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,

View file

@ -43,8 +43,8 @@ func getDefaultManager() *cacheManager {
// header, "/dir" is for contents. For the root node "" (empty string) is the
// key for root, "/" for the root header
func Checksum(ctx context.Context, ref cache.ImmutableRef, path string) (digest.Digest, error) {
return getDefaultManager().Checksum(ctx, ref, path)
func Checksum(ctx context.Context, ref cache.ImmutableRef, path string, followLinks bool) (digest.Digest, error) {
return getDefaultManager().Checksum(ctx, ref, path, followLinks)
}
func GetCacheContext(ctx context.Context, md *metadata.StorageItem) (CacheContext, error) {
@ -56,7 +56,8 @@ func SetCacheContext(ctx context.Context, md *metadata.StorageItem, cc CacheCont
}
type CacheContext interface {
Checksum(ctx context.Context, ref cache.Mountable, p string) (digest.Digest, error)
Checksum(ctx context.Context, ref cache.Mountable, p string, followLinks bool) (digest.Digest, error)
ChecksumWildcard(ctx context.Context, ref cache.Mountable, p string, followLinks bool) (digest.Digest, error)
HandleChange(kind fsutil.ChangeKind, p string, fi os.FileInfo, err error) error
}
@ -64,18 +65,23 @@ type Hashed interface {
Digest() digest.Digest
}
type Wildcard struct {
Path string
Record *CacheRecord
}
type cacheManager struct {
locker *locker.Locker
lru *simplelru.LRU
lruMu sync.Mutex
}
func (cm *cacheManager) Checksum(ctx context.Context, ref cache.ImmutableRef, p string) (digest.Digest, error) {
func (cm *cacheManager) Checksum(ctx context.Context, ref cache.ImmutableRef, p string, followLinks bool) (digest.Digest, error) {
cc, err := cm.GetCacheContext(ctx, ensureOriginMetadata(ref.Metadata()))
if err != nil {
return "", nil
}
return cc.Checksum(ctx, ref, p)
return cc.Checksum(ctx, ref, p, followLinks)
}
func (cm *cacheManager) GetCacheContext(ctx context.Context, md *metadata.StorageItem) (CacheContext, error) {
@ -317,10 +323,49 @@ func (cc *cacheContext) HandleChange(kind fsutil.ChangeKind, p string, fi os.Fil
return nil
}
func (cc *cacheContext) Checksum(ctx context.Context, mountable cache.Mountable, p string) (digest.Digest, error) {
func (cc *cacheContext) ChecksumWildcard(ctx context.Context, mountable cache.Mountable, p string, followLinks bool) (digest.Digest, error) {
m := &mount{mountable: mountable}
defer m.clean()
wildcards, err := cc.wildcards(ctx, m, p)
if err != nil {
return "", nil
}
if followLinks {
for i, w := range wildcards {
if w.Record.Type == CacheRecordTypeSymlink {
dgst, err := cc.checksumFollow(ctx, m, w.Path, followLinks)
if err != nil {
return "", err
}
wildcards[i].Record = &CacheRecord{Digest: dgst}
}
}
}
if len(wildcards) > 1 {
digester := digest.Canonical.Digester()
for i, w := range wildcards {
if i != 0 {
digester.Hash().Write([]byte{0})
}
digester.Hash().Write([]byte(w.Record.Digest))
}
return digester.Digest(), nil
} else {
return wildcards[0].Record.Digest, nil
}
}
func (cc *cacheContext) Checksum(ctx context.Context, mountable cache.Mountable, p string, followLinks bool) (digest.Digest, error) {
m := &mount{mountable: mountable}
defer m.clean()
return cc.checksumFollow(ctx, m, p, followLinks)
}
func (cc *cacheContext) checksumFollow(ctx context.Context, m *mount, p string, follow bool) (digest.Digest, error) {
const maxSymlinkLimit = 255
i := 0
for {
@ -331,7 +376,7 @@ func (cc *cacheContext) Checksum(ctx context.Context, mountable cache.Mountable,
if err != nil {
return "", err
}
if cr.Type == CacheRecordTypeSymlink {
if cr.Type == CacheRecordTypeSymlink && follow {
link := cr.Linkname
if !path.IsAbs(cr.Linkname) {
link = path.Join(path.Dir(p), link)
@ -344,6 +389,82 @@ func (cc *cacheContext) Checksum(ctx context.Context, mountable cache.Mountable,
}
}
func (cc *cacheContext) wildcards(ctx context.Context, m *mount, p string) ([]*Wildcard, error) {
cc.mu.Lock()
defer cc.mu.Unlock()
if cc.txn != nil {
cc.commitActiveTransaction()
}
root := cc.tree.Root()
scan, err := cc.needsScan(root, "")
if err != nil {
return nil, err
}
if scan {
if err := cc.scanPath(ctx, m, ""); err != nil {
return nil, err
}
}
defer func() {
if cc.dirty {
go cc.save()
cc.dirty = false
}
}()
p = path.Join("/", filepath.ToSlash(p))
if p == "/" {
p = ""
}
wildcards := make([]*Wildcard, 0, 2)
txn := cc.tree.Txn()
root = txn.Root()
var updated bool
iter := root.Seek([]byte{})
for {
k, _, ok := iter.Next()
if !ok {
break
}
if len(k) > 0 && k[len(k)-1] == byte(0) {
continue
}
fn := convertKeyToPath(k)
b, err := path.Match(p, string(fn))
if err != nil {
return nil, err
}
if !b {
continue
}
cr, upt, err := cc.checksum(ctx, root, txn, m, k, false)
if err != nil {
return nil, err
}
if upt {
updated = true
}
wildcards = append(wildcards, &Wildcard{Path: string(fn), Record: cr})
if cr.Type == CacheRecordTypeDir {
iter = root.Seek(append(k, 0, 0xff))
}
}
cc.tree = txn.Commit()
cc.dirty = updated
return wildcards, nil
}
func (cc *cacheContext) checksumNoFollow(ctx context.Context, m *mount, p string) (*CacheRecord, error) {
p = path.Join("/", filepath.ToSlash(p))
if p == "/" {
@ -412,7 +533,7 @@ func (cc *cacheContext) lazyChecksum(ctx context.Context, m *mount, p string) (*
k := convertPathToKey([]byte(p))
txn := cc.tree.Txn()
root = txn.Root()
cr, updated, err := cc.checksum(ctx, root, txn, m, k)
cr, updated, err := cc.checksum(ctx, root, txn, m, k, true)
if err != nil {
return nil, err
}
@ -421,8 +542,8 @@ func (cc *cacheContext) lazyChecksum(ctx context.Context, m *mount, p string) (*
return cr, err
}
func (cc *cacheContext) checksum(ctx context.Context, root *iradix.Node, txn *iradix.Txn, m *mount, k []byte) (*CacheRecord, bool, error) {
k, cr, err := getFollowLinks(root, k)
func (cc *cacheContext) checksum(ctx context.Context, root *iradix.Node, txn *iradix.Txn, m *mount, k []byte, follow bool) (*CacheRecord, bool, error) {
k, cr, err := getFollowLinks(root, k, follow)
if err != nil {
return nil, false, err
}
@ -447,7 +568,7 @@ func (cc *cacheContext) checksum(ctx context.Context, root *iradix.Node, txn *ir
}
h.Write(bytes.TrimPrefix(subk, k))
subcr, _, err := cc.checksum(ctx, root, txn, m, subk)
subcr, _, err := cc.checksum(ctx, root, txn, m, subk, true)
if err != nil {
return nil, false, err
}
@ -599,42 +720,49 @@ func (cc *cacheContext) scanPath(ctx context.Context, m *mount, p string) (retEr
return nil
}
func getFollowLinks(root *iradix.Node, k []byte) ([]byte, *CacheRecord, error) {
func getFollowLinks(root *iradix.Node, k []byte, follow bool) ([]byte, *CacheRecord, error) {
var linksWalked int
return getFollowLinksWalk(root, k, &linksWalked)
return getFollowLinksWalk(root, k, follow, &linksWalked)
}
func getFollowLinksWalk(root *iradix.Node, k []byte, linksWalked *int) ([]byte, *CacheRecord, error) {
func getFollowLinksWalk(root *iradix.Node, k []byte, follow bool, linksWalked *int) ([]byte, *CacheRecord, error) {
v, ok := root.Get(k)
if ok {
return k, v.(*CacheRecord), nil
}
if len(k) == 0 {
if !follow || len(k) == 0 {
return nil, nil, nil
}
dir, file := splitKey(k)
_, parent, err := getFollowLinksWalk(root, dir, linksWalked)
k, parent, err := getFollowLinksWalk(root, dir, follow, linksWalked)
if err != nil {
return nil, nil, err
}
if parent != nil && parent.Type == CacheRecordTypeSymlink {
*linksWalked++
if *linksWalked > 255 {
return nil, nil, errors.Errorf("too many links")
if parent != nil {
if parent.Type == CacheRecordTypeSymlink {
*linksWalked++
if *linksWalked > 255 {
return nil, nil, errors.Errorf("too many links")
}
dirPath := path.Clean(string(convertKeyToPath(dir)))
if dirPath == "." || dirPath == "/" {
dirPath = ""
}
link := path.Clean(parent.Linkname)
if !path.IsAbs(link) {
link = path.Join("/", path.Join(path.Dir(dirPath), link))
}
return getFollowLinksWalk(root, append(convertPathToKey([]byte(link)), file...), follow, linksWalked)
}
dirPath := path.Clean(string(convertKeyToPath(dir)))
if dirPath == "." || dirPath == "/" {
dirPath = ""
}
link := path.Clean(parent.Linkname)
if !path.IsAbs(link) {
link = path.Join("/", path.Join(path.Dir(dirPath), link))
}
return getFollowLinksWalk(root, append(convertPathToKey([]byte(link)), file...), linksWalked)
}
k = append(k, file...)
v, ok = root.Get(k)
if ok {
return k, v.(*CacheRecord), nil
}
}
return nil, nil, nil
}

View file

@ -1,17 +1,6 @@
// Code generated by protoc-gen-gogo. DO NOT EDIT.
// source: checksum.proto
/*
Package contenthash is a generated protocol buffer package.
It is generated from these files:
checksum.proto
It has these top-level messages:
CacheRecord
CacheRecordWithPath
CacheRecords
*/
package contenthash
import proto "github.com/gogo/protobuf/proto"
@ -59,7 +48,9 @@ var CacheRecordType_value = map[string]int32{
func (x CacheRecordType) String() string {
return proto.EnumName(CacheRecordType_name, int32(x))
}
func (CacheRecordType) EnumDescriptor() ([]byte, []int) { return fileDescriptorChecksum, []int{0} }
func (CacheRecordType) EnumDescriptor() ([]byte, []int) {
return fileDescriptor_checksum_efc6501bf3727db3, []int{0}
}
type CacheRecord struct {
Digest github_com_opencontainers_go_digest.Digest `protobuf:"bytes,1,opt,name=digest,proto3,customtype=github.com/opencontainers/go-digest.Digest" json:"digest"`
@ -67,10 +58,38 @@ type CacheRecord struct {
Linkname string `protobuf:"bytes,3,opt,name=linkname,proto3" json:"linkname,omitempty"`
}
func (m *CacheRecord) Reset() { *m = CacheRecord{} }
func (m *CacheRecord) String() string { return proto.CompactTextString(m) }
func (*CacheRecord) ProtoMessage() {}
func (*CacheRecord) Descriptor() ([]byte, []int) { return fileDescriptorChecksum, []int{0} }
func (m *CacheRecord) Reset() { *m = CacheRecord{} }
func (m *CacheRecord) String() string { return proto.CompactTextString(m) }
func (*CacheRecord) ProtoMessage() {}
func (*CacheRecord) Descriptor() ([]byte, []int) {
return fileDescriptor_checksum_efc6501bf3727db3, []int{0}
}
func (m *CacheRecord) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
}
func (m *CacheRecord) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
if deterministic {
return xxx_messageInfo_CacheRecord.Marshal(b, m, deterministic)
} else {
b = b[:cap(b)]
n, err := m.MarshalTo(b)
if err != nil {
return nil, err
}
return b[:n], nil
}
}
func (dst *CacheRecord) XXX_Merge(src proto.Message) {
xxx_messageInfo_CacheRecord.Merge(dst, src)
}
func (m *CacheRecord) XXX_Size() int {
return m.Size()
}
func (m *CacheRecord) XXX_DiscardUnknown() {
xxx_messageInfo_CacheRecord.DiscardUnknown(m)
}
var xxx_messageInfo_CacheRecord proto.InternalMessageInfo
func (m *CacheRecord) GetType() CacheRecordType {
if m != nil {
@ -88,13 +107,41 @@ func (m *CacheRecord) GetLinkname() string {
type CacheRecordWithPath struct {
Path string `protobuf:"bytes,1,opt,name=path,proto3" json:"path,omitempty"`
Record *CacheRecord `protobuf:"bytes,2,opt,name=record" json:"record,omitempty"`
Record *CacheRecord `protobuf:"bytes,2,opt,name=record,proto3" json:"record,omitempty"`
}
func (m *CacheRecordWithPath) Reset() { *m = CacheRecordWithPath{} }
func (m *CacheRecordWithPath) String() string { return proto.CompactTextString(m) }
func (*CacheRecordWithPath) ProtoMessage() {}
func (*CacheRecordWithPath) Descriptor() ([]byte, []int) { return fileDescriptorChecksum, []int{1} }
func (m *CacheRecordWithPath) Reset() { *m = CacheRecordWithPath{} }
func (m *CacheRecordWithPath) String() string { return proto.CompactTextString(m) }
func (*CacheRecordWithPath) ProtoMessage() {}
func (*CacheRecordWithPath) Descriptor() ([]byte, []int) {
return fileDescriptor_checksum_efc6501bf3727db3, []int{1}
}
func (m *CacheRecordWithPath) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
}
func (m *CacheRecordWithPath) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
if deterministic {
return xxx_messageInfo_CacheRecordWithPath.Marshal(b, m, deterministic)
} else {
b = b[:cap(b)]
n, err := m.MarshalTo(b)
if err != nil {
return nil, err
}
return b[:n], nil
}
}
func (dst *CacheRecordWithPath) XXX_Merge(src proto.Message) {
xxx_messageInfo_CacheRecordWithPath.Merge(dst, src)
}
func (m *CacheRecordWithPath) XXX_Size() int {
return m.Size()
}
func (m *CacheRecordWithPath) XXX_DiscardUnknown() {
xxx_messageInfo_CacheRecordWithPath.DiscardUnknown(m)
}
var xxx_messageInfo_CacheRecordWithPath proto.InternalMessageInfo
func (m *CacheRecordWithPath) GetPath() string {
if m != nil {
@ -111,13 +158,41 @@ func (m *CacheRecordWithPath) GetRecord() *CacheRecord {
}
type CacheRecords struct {
Paths []*CacheRecordWithPath `protobuf:"bytes,1,rep,name=paths" json:"paths,omitempty"`
Paths []*CacheRecordWithPath `protobuf:"bytes,1,rep,name=paths,proto3" json:"paths,omitempty"`
}
func (m *CacheRecords) Reset() { *m = CacheRecords{} }
func (m *CacheRecords) String() string { return proto.CompactTextString(m) }
func (*CacheRecords) ProtoMessage() {}
func (*CacheRecords) Descriptor() ([]byte, []int) { return fileDescriptorChecksum, []int{2} }
func (m *CacheRecords) Reset() { *m = CacheRecords{} }
func (m *CacheRecords) String() string { return proto.CompactTextString(m) }
func (*CacheRecords) ProtoMessage() {}
func (*CacheRecords) Descriptor() ([]byte, []int) {
return fileDescriptor_checksum_efc6501bf3727db3, []int{2}
}
func (m *CacheRecords) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
}
func (m *CacheRecords) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
if deterministic {
return xxx_messageInfo_CacheRecords.Marshal(b, m, deterministic)
} else {
b = b[:cap(b)]
n, err := m.MarshalTo(b)
if err != nil {
return nil, err
}
return b[:n], nil
}
}
func (dst *CacheRecords) XXX_Merge(src proto.Message) {
xxx_messageInfo_CacheRecords.Merge(dst, src)
}
func (m *CacheRecords) XXX_Size() int {
return m.Size()
}
func (m *CacheRecords) XXX_DiscardUnknown() {
xxx_messageInfo_CacheRecords.DiscardUnknown(m)
}
var xxx_messageInfo_CacheRecords proto.InternalMessageInfo
func (m *CacheRecords) GetPaths() []*CacheRecordWithPath {
if m != nil {
@ -241,6 +316,9 @@ func encodeVarintChecksum(dAtA []byte, offset int, v uint64) int {
return offset + 1
}
func (m *CacheRecord) Size() (n int) {
if m == nil {
return 0
}
var l int
_ = l
l = len(m.Digest)
@ -258,6 +336,9 @@ func (m *CacheRecord) Size() (n int) {
}
func (m *CacheRecordWithPath) Size() (n int) {
if m == nil {
return 0
}
var l int
_ = l
l = len(m.Path)
@ -272,6 +353,9 @@ func (m *CacheRecordWithPath) Size() (n int) {
}
func (m *CacheRecords) Size() (n int) {
if m == nil {
return 0
}
var l int
_ = l
if len(m.Paths) > 0 {
@ -721,35 +805,35 @@ var (
ErrIntOverflowChecksum = fmt.Errorf("proto: integer overflow")
)
func init() { proto.RegisterFile("checksum.proto", fileDescriptorChecksum) }
func init() { proto.RegisterFile("checksum.proto", fileDescriptor_checksum_efc6501bf3727db3) }
var fileDescriptorChecksum = []byte{
// 418 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x74, 0x92, 0xc1, 0x6a, 0xd4, 0x40,
0x18, 0xc7, 0x77, 0xba, 0xeb, 0xaa, 0xdf, 0x4a, 0x0d, 0x53, 0x68, 0xc3, 0x50, 0xb2, 0xe3, 0x5e,
0x5c, 0x8a, 0xcd, 0x96, 0x08, 0xde, 0xad, 0xd9, 0xa5, 0xd1, 0x2a, 0x32, 0x15, 0x44, 0x3c, 0x48,
0x36, 0x3b, 0x66, 0x42, 0x9b, 0x4c, 0x48, 0x66, 0x0f, 0xfb, 0x06, 0x92, 0x93, 0x2f, 0x90, 0x93,
0x82, 0xef, 0xe0, 0x5d, 0xe8, 0xd1, 0xb3, 0x87, 0x22, 0xeb, 0x8b, 0x48, 0x26, 0x55, 0x42, 0xca,
0x9e, 0xe6, 0xfb, 0x66, 0x7e, 0xdf, 0xff, 0xff, 0x9f, 0x61, 0x60, 0x3b, 0x10, 0x3c, 0x38, 0xcf,
0x97, 0xb1, 0x9d, 0x66, 0x52, 0x49, 0x3c, 0x08, 0x64, 0xa2, 0x78, 0xa2, 0x84, 0x9f, 0x0b, 0x72,
0x18, 0x46, 0x4a, 0x2c, 0xe7, 0x76, 0x20, 0xe3, 0x49, 0x28, 0x43, 0x39, 0xd1, 0xcc, 0x7c, 0xf9,
0x51, 0x77, 0xba, 0xd1, 0x55, 0x3d, 0x3b, 0xfa, 0x86, 0x60, 0xf0, 0xcc, 0x0f, 0x04, 0x67, 0x3c,
0x90, 0xd9, 0x02, 0x3f, 0x87, 0xfe, 0x22, 0x0a, 0x79, 0xae, 0x4c, 0x44, 0xd1, 0xf8, 0xee, 0xb1,
0x73, 0x79, 0x35, 0xec, 0xfc, 0xba, 0x1a, 0x1e, 0x34, 0x64, 0x65, 0xca, 0x93, 0xca, 0xd2, 0x8f,
0x12, 0x9e, 0xe5, 0x93, 0x50, 0x1e, 0xd6, 0x23, 0xb6, 0xab, 0x17, 0x76, 0xad, 0x80, 0x8f, 0xa0,
0xa7, 0x56, 0x29, 0x37, 0xb7, 0x28, 0x1a, 0x6f, 0x3b, 0xfb, 0x76, 0x23, 0xa6, 0xdd, 0xf0, 0x7c,
0xb3, 0x4a, 0x39, 0xd3, 0x24, 0x26, 0x70, 0xe7, 0x22, 0x4a, 0xce, 0x13, 0x3f, 0xe6, 0x66, 0xb7,
0xf2, 0x67, 0xff, 0xfb, 0xd1, 0x7b, 0xd8, 0x69, 0x0c, 0xbd, 0x8d, 0x94, 0x78, 0xed, 0x2b, 0x81,
0x31, 0xf4, 0x52, 0x5f, 0x89, 0x3a, 0x2e, 0xd3, 0x35, 0x3e, 0x82, 0x7e, 0xa6, 0x29, 0x6d, 0x3d,
0x70, 0xcc, 0x4d, 0xd6, 0xec, 0x9a, 0x1b, 0xcd, 0xe0, 0x5e, 0x63, 0x3b, 0xc7, 0x4f, 0xe0, 0x56,
0xa5, 0x94, 0x9b, 0x88, 0x76, 0xc7, 0x03, 0x87, 0x6e, 0x12, 0xf8, 0x17, 0x83, 0xd5, 0xf8, 0xc1,
0x0f, 0x04, 0xf7, 0x5b, 0x57, 0xc3, 0x0f, 0xa0, 0x37, 0xf3, 0x4e, 0xa7, 0x46, 0x87, 0xec, 0x15,
0x25, 0xdd, 0x69, 0x1d, 0xcf, 0xa2, 0x0b, 0x8e, 0x87, 0xd0, 0x75, 0x3d, 0x66, 0x20, 0xb2, 0x5b,
0x94, 0x14, 0xb7, 0x08, 0x37, 0xca, 0xf0, 0x23, 0x00, 0xd7, 0x63, 0x1f, 0x4e, 0xa6, 0x4f, 0xdd,
0x29, 0x33, 0xb6, 0xc8, 0x7e, 0x51, 0x52, 0xf3, 0x26, 0x77, 0xc2, 0xfd, 0x05, 0xcf, 0xf0, 0x43,
0xb8, 0x7d, 0xf6, 0xee, 0xe5, 0xa9, 0xf7, 0xea, 0x85, 0xd1, 0x25, 0xa4, 0x28, 0xe9, 0x6e, 0x0b,
0x3d, 0x5b, 0xc5, 0xd5, 0xbb, 0x92, 0xbd, 0x4f, 0x5f, 0xac, 0xce, 0xf7, 0xaf, 0x56, 0x3b, 0xf3,
0xb1, 0x71, 0xb9, 0xb6, 0xd0, 0xcf, 0xb5, 0x85, 0x7e, 0xaf, 0x2d, 0xf4, 0xf9, 0x8f, 0xd5, 0x99,
0xf7, 0xf5, 0x7f, 0x79, 0xfc, 0x37, 0x00, 0x00, 0xff, 0xff, 0x55, 0xf2, 0x2e, 0x06, 0x7d, 0x02,
0x00, 0x00,
var fileDescriptor_checksum_efc6501bf3727db3 = []byte{
// 426 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x74, 0x92, 0xc1, 0x6a, 0x13, 0x41,
0x18, 0xc7, 0x77, 0x9a, 0x18, 0xf5, 0x8b, 0xd4, 0x30, 0x85, 0x76, 0x19, 0xca, 0x64, 0xcc, 0xc5,
0x50, 0xec, 0xa6, 0x44, 0xf0, 0x6e, 0xdd, 0x84, 0x46, 0xab, 0xc8, 0x54, 0x10, 0xf1, 0x20, 0x9b,
0xcd, 0xb8, 0xb3, 0xb4, 0xd9, 0x59, 0x76, 0x27, 0x87, 0xbc, 0x81, 0xec, 0xc9, 0x17, 0xd8, 0x93,
0x82, 0xef, 0xe0, 0x5d, 0xe8, 0xb1, 0x47, 0xf1, 0x50, 0x24, 0x79, 0x11, 0xd9, 0xd9, 0x2a, 0xcb,
0x4a, 0x4e, 0xf3, 0x7d, 0x33, 0xbf, 0xef, 0xff, 0xff, 0xcf, 0x30, 0xb0, 0xed, 0x4b, 0xe1, 0x9f,
0xa7, 0x8b, 0xb9, 0x13, 0x27, 0x4a, 0x2b, 0xdc, 0xf6, 0x55, 0xa4, 0x45, 0xa4, 0xa5, 0x97, 0x4a,
0x72, 0x18, 0x84, 0x5a, 0x2e, 0xa6, 0x8e, 0xaf, 0xe6, 0x83, 0x40, 0x05, 0x6a, 0x60, 0x98, 0xe9,
0xe2, 0xa3, 0xe9, 0x4c, 0x63, 0xaa, 0x72, 0xb6, 0xf7, 0x0d, 0x41, 0xfb, 0x99, 0xe7, 0x4b, 0xc1,
0x85, 0xaf, 0x92, 0x19, 0x7e, 0x0e, 0xad, 0x59, 0x18, 0x88, 0x54, 0xdb, 0x88, 0xa1, 0xfe, 0xdd,
0xe3, 0xe1, 0xe5, 0x75, 0xd7, 0xfa, 0x75, 0xdd, 0x3d, 0xa8, 0xc8, 0xaa, 0x58, 0x44, 0x85, 0xa5,
0x17, 0x46, 0x22, 0x49, 0x07, 0x81, 0x3a, 0x2c, 0x47, 0x1c, 0xd7, 0x2c, 0xfc, 0x46, 0x01, 0x1f,
0x41, 0x53, 0x2f, 0x63, 0x61, 0x6f, 0x31, 0xd4, 0xdf, 0x1e, 0xee, 0x3b, 0x95, 0x98, 0x4e, 0xc5,
0xf3, 0xcd, 0x32, 0x16, 0xdc, 0x90, 0x98, 0xc0, 0x9d, 0x8b, 0x30, 0x3a, 0x8f, 0xbc, 0xb9, 0xb0,
0x1b, 0x85, 0x3f, 0xff, 0xd7, 0xf7, 0xde, 0xc3, 0x4e, 0x65, 0xe8, 0x6d, 0xa8, 0xe5, 0x6b, 0x4f,
0x4b, 0x8c, 0xa1, 0x19, 0x7b, 0x5a, 0x96, 0x71, 0xb9, 0xa9, 0xf1, 0x11, 0xb4, 0x12, 0x43, 0x19,
0xeb, 0xf6, 0xd0, 0xde, 0x64, 0xcd, 0x6f, 0xb8, 0xde, 0x18, 0xee, 0x55, 0xb6, 0x53, 0xfc, 0x04,
0x6e, 0x15, 0x4a, 0xa9, 0x8d, 0x58, 0xa3, 0xdf, 0x1e, 0xb2, 0x4d, 0x02, 0x7f, 0x63, 0xf0, 0x12,
0x3f, 0xf8, 0x81, 0xe0, 0x7e, 0xed, 0x6a, 0xf8, 0x01, 0x34, 0xc7, 0x93, 0xd3, 0x51, 0xc7, 0x22,
0x7b, 0x59, 0xce, 0x76, 0x6a, 0xc7, 0xe3, 0xf0, 0x42, 0xe0, 0x2e, 0x34, 0xdc, 0x09, 0xef, 0x20,
0xb2, 0x9b, 0xe5, 0x0c, 0xd7, 0x08, 0x37, 0x4c, 0xf0, 0x23, 0x00, 0x77, 0xc2, 0x3f, 0x9c, 0x8c,
0x9e, 0xba, 0x23, 0xde, 0xd9, 0x22, 0xfb, 0x59, 0xce, 0xec, 0xff, 0xb9, 0x13, 0xe1, 0xcd, 0x44,
0x82, 0x1f, 0xc2, 0xed, 0xb3, 0x77, 0x2f, 0x4f, 0x27, 0xaf, 0x5e, 0x74, 0x1a, 0x84, 0x64, 0x39,
0xdb, 0xad, 0xa1, 0x67, 0xcb, 0x79, 0xf1, 0xae, 0x64, 0xef, 0xd3, 0x17, 0x6a, 0x7d, 0xff, 0x4a,
0xeb, 0x99, 0x8f, 0xed, 0xcb, 0x15, 0x45, 0x57, 0x2b, 0x8a, 0x7e, 0xaf, 0x28, 0xfa, 0xbc, 0xa6,
0xd6, 0xd5, 0x9a, 0x5a, 0x3f, 0xd7, 0xd4, 0x9a, 0xb6, 0xcc, 0xbf, 0x79, 0xfc, 0x27, 0x00, 0x00,
0xff, 0xff, 0xfd, 0xd7, 0xd8, 0x37, 0x85, 0x02, 0x00, 0x00,
}

View file

@ -19,7 +19,7 @@ import (
"github.com/pkg/errors"
)
type ResolveCacheExporterFunc func(ctx context.Context, typ, target string) (Exporter, error)
type ResolveCacheExporterFunc func(ctx context.Context, attrs map[string]string) (Exporter, error)
func oneOffProgress(ctx context.Context, id string) func(err error) error {
pw, _, _ := progress.FromContext(ctx)
@ -39,9 +39,17 @@ func oneOffProgress(ctx context.Context, id string) func(err error) error {
type Exporter interface {
solver.CacheExporterTarget
Finalize(ctx context.Context) error
// Finalize finalizes and return metadata that are returned to the client
// e.g. ExporterResponseManifestDesc
Finalize(ctx context.Context) (map[string]string, error)
}
const (
// ExportResponseManifestDesc is a key for the map returned from Exporter.Finalize.
// The map value is a JSON string of an OCI desciptor of a manifest.
ExporterResponseManifestDesc = "cache.manifest"
)
type contentCacheExporter struct {
solver.CacheExporterTarget
chains *v1.CacheChains
@ -53,14 +61,15 @@ func NewExporter(ingester content.Ingester) Exporter {
return &contentCacheExporter{CacheExporterTarget: cc, chains: cc, ingester: ingester}
}
func (ce *contentCacheExporter) Finalize(ctx context.Context) error {
func (ce *contentCacheExporter) Finalize(ctx context.Context) (map[string]string, error) {
return export(ctx, ce.ingester, ce.chains)
}
func export(ctx context.Context, ingester content.Ingester, cc *v1.CacheChains) error {
func export(ctx context.Context, ingester content.Ingester, cc *v1.CacheChains) (map[string]string, error) {
res := make(map[string]string)
config, descs, err := cc.Marshal()
if err != nil {
return err
return nil, err
}
// own type because oci type can't be pushed and docker type doesn't have annotations
@ -80,11 +89,11 @@ func export(ctx context.Context, ingester content.Ingester, cc *v1.CacheChains)
for _, l := range config.Layers {
dgstPair, ok := descs[l.Blob]
if !ok {
return errors.Errorf("missing blob %s", l.Blob)
return nil, errors.Errorf("missing blob %s", l.Blob)
}
layerDone := oneOffProgress(ctx, fmt.Sprintf("writing layer %s", l.Blob))
if err := contentutil.Copy(ctx, ingester, dgstPair.Provider, dgstPair.Descriptor); err != nil {
return layerDone(errors.Wrap(err, "error writing layer blob"))
return nil, layerDone(errors.Wrap(err, "error writing layer blob"))
}
layerDone(nil)
mfst.Manifests = append(mfst.Manifests, dgstPair.Descriptor)
@ -92,7 +101,7 @@ func export(ctx context.Context, ingester content.Ingester, cc *v1.CacheChains)
dt, err := json.Marshal(config)
if err != nil {
return err
return nil, err
}
dgst := digest.FromBytes(dt)
desc := ocispec.Descriptor{
@ -102,7 +111,7 @@ func export(ctx context.Context, ingester content.Ingester, cc *v1.CacheChains)
}
configDone := oneOffProgress(ctx, fmt.Sprintf("writing config %s", dgst))
if err := content.WriteBlob(ctx, ingester, dgst.String(), bytes.NewReader(dt), desc); err != nil {
return configDone(errors.Wrap(err, "error writing config blob"))
return nil, configDone(errors.Wrap(err, "error writing config blob"))
}
configDone(nil)
@ -110,7 +119,7 @@ func export(ctx context.Context, ingester content.Ingester, cc *v1.CacheChains)
dt, err = json.Marshal(mfst)
if err != nil {
return errors.Wrap(err, "failed to marshal manifest")
return nil, errors.Wrap(err, "failed to marshal manifest")
}
dgst = digest.FromBytes(dt)
@ -121,8 +130,13 @@ func export(ctx context.Context, ingester content.Ingester, cc *v1.CacheChains)
}
mfstDone := oneOffProgress(ctx, fmt.Sprintf("writing manifest %s", dgst))
if err := content.WriteBlob(ctx, ingester, dgst.String(), bytes.NewReader(dt), desc); err != nil {
return mfstDone(errors.Wrap(err, "error writing manifest blob"))
return nil, mfstDone(errors.Wrap(err, "error writing manifest blob"))
}
descJSON, err := json.Marshal(desc)
if err != nil {
return nil, err
}
res[ExporterResponseManifestDesc] = string(descJSON)
mfstDone(nil)
return nil
return res, nil
}

View file

@ -4,18 +4,24 @@ import (
"context"
"encoding/json"
"io"
"sync"
"time"
"github.com/containerd/containerd/content"
"github.com/containerd/containerd/images"
v1 "github.com/moby/buildkit/cache/remotecache/v1"
"github.com/moby/buildkit/solver"
"github.com/moby/buildkit/util/imageutil"
"github.com/moby/buildkit/worker"
digest "github.com/opencontainers/go-digest"
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
"golang.org/x/sync/errgroup"
)
// ResolveCacheImporterFunc returns importer and descriptor.
// Currently typ needs to be an empty string.
type ResolveCacheImporterFunc func(ctx context.Context, typ, ref string) (Importer, ocispec.Descriptor, error)
type ResolveCacheImporterFunc func(ctx context.Context, attrs map[string]string) (Importer, ocispec.Descriptor, error)
type Importer interface {
Resolve(ctx context.Context, desc ocispec.Descriptor, id string, w worker.Worker) (solver.CacheManager, error)
@ -56,7 +62,7 @@ func (ci *contentCacheImporter) Resolve(ctx context.Context, desc ocispec.Descri
}
if configDesc.Digest == "" {
return nil, errors.Errorf("invalid build cache from %+v", desc)
return ci.importInlineCache(ctx, dt, id, w)
}
dt, err = readBlob(ctx, ci.provider, configDesc)
@ -96,3 +102,169 @@ func readBlob(ctx context.Context, provider content.Provider, desc ocispec.Descr
}
return dt, err
}
func (ci *contentCacheImporter) importInlineCache(ctx context.Context, dt []byte, id string, w worker.Worker) (solver.CacheManager, error) {
m := map[digest.Digest][]byte{}
if err := ci.allDistributionManifests(ctx, dt, m); err != nil {
return nil, err
}
var mu sync.Mutex
cc := v1.NewCacheChains()
eg, ctx := errgroup.WithContext(ctx)
for dgst, dt := range m {
func(dgst digest.Digest, dt []byte) {
eg.Go(func() error {
var m ocispec.Manifest
if err := json.Unmarshal(dt, &m); err != nil {
return err
}
if m.Config.Digest == "" || len(m.Layers) == 0 {
return nil
}
p, err := content.ReadBlob(ctx, ci.provider, m.Config)
if err != nil {
return err
}
var img image
if err := json.Unmarshal(p, &img); err != nil {
return err
}
if len(img.Rootfs.DiffIDs) != len(m.Layers) {
logrus.Warnf("invalid image with mismatching manifest and config")
return nil
}
if img.Cache == nil {
return nil
}
var config v1.CacheConfig
if err := json.Unmarshal(img.Cache, &config.Records); err != nil {
return err
}
createdDates, createdMsg, err := parseCreatedLayerInfo(img)
if err != nil {
return err
}
layers := v1.DescriptorProvider{}
for i, m := range m.Layers {
if m.Annotations == nil {
m.Annotations = map[string]string{}
}
if createdAt := createdDates[i]; createdAt != "" {
m.Annotations["buildkit/createdat"] = createdAt
}
if createdBy := createdMsg[i]; createdBy != "" {
m.Annotations["buildkit/description"] = createdBy
}
m.Annotations["containerd.io/uncompressed"] = img.Rootfs.DiffIDs[i].String()
layers[m.Digest] = v1.DescriptorProviderPair{
Descriptor: m,
Provider: ci.provider,
}
config.Layers = append(config.Layers, v1.CacheLayer{
Blob: m.Digest,
ParentIndex: i - 1,
})
}
dt, err = json.Marshal(config)
if err != nil {
return err
}
mu.Lock()
if err := v1.ParseConfig(config, layers, cc); err != nil {
return err
}
mu.Unlock()
return nil
})
}(dgst, dt)
}
if err := eg.Wait(); err != nil {
return nil, err
}
keysStorage, resultStorage, err := v1.NewCacheKeyStorage(cc, w)
if err != nil {
return nil, err
}
return solver.NewCacheManager(id, keysStorage, resultStorage), nil
}
func (ci *contentCacheImporter) allDistributionManifests(ctx context.Context, dt []byte, m map[digest.Digest][]byte) error {
mt, err := imageutil.DetectManifestBlobMediaType(dt)
if err != nil {
return err
}
switch mt {
case images.MediaTypeDockerSchema2Manifest, ocispec.MediaTypeImageManifest:
m[digest.FromBytes(dt)] = dt
case images.MediaTypeDockerSchema2ManifestList, ocispec.MediaTypeImageIndex:
var index ocispec.Index
if err := json.Unmarshal(dt, &index); err != nil {
return err
}
for _, d := range index.Manifests {
if _, ok := m[d.Digest]; ok {
continue
}
p, err := content.ReadBlob(ctx, ci.provider, d)
if err != nil {
return err
}
if err := ci.allDistributionManifests(ctx, p, m); err != nil {
return err
}
}
}
return nil
}
type image struct {
Rootfs struct {
DiffIDs []digest.Digest `json:"diff_ids"`
} `json:"rootfs"`
Cache []byte `json:"moby.buildkit.cache.v0"`
History []struct {
Created *time.Time `json:"created,omitempty"`
CreatedBy string `json:"created_by,omitempty"`
EmptyLayer bool `json:"empty_layer,omitempty"`
} `json:"history,omitempty"`
}
func parseCreatedLayerInfo(img image) ([]string, []string, error) {
dates := make([]string, 0, len(img.Rootfs.DiffIDs))
createdBy := make([]string, 0, len(img.Rootfs.DiffIDs))
for _, h := range img.History {
if !h.EmptyLayer {
str := ""
if h.Created != nil {
dt, err := h.Created.MarshalText()
if err != nil {
return nil, nil, err
}
str = string(dt)
}
dates = append(dates, str)
createdBy = append(createdBy, h.CreatedBy)
}
}
return dates, createdBy, nil
}

View file

@ -0,0 +1,99 @@
package registry
import (
"context"
"encoding/json"
"github.com/moby/buildkit/cache/remotecache"
v1 "github.com/moby/buildkit/cache/remotecache/v1"
"github.com/moby/buildkit/solver"
digest "github.com/opencontainers/go-digest"
"github.com/sirupsen/logrus"
)
func ResolveCacheExporterFunc() remotecache.ResolveCacheExporterFunc {
return func(ctx context.Context, _ map[string]string) (remotecache.Exporter, error) {
return NewExporter(), nil
}
}
func NewExporter() remotecache.Exporter {
cc := v1.NewCacheChains()
return &exporter{CacheExporterTarget: cc, chains: cc}
}
type exporter struct {
solver.CacheExporterTarget
chains *v1.CacheChains
}
func (ce *exporter) Finalize(ctx context.Context) (map[string]string, error) {
return nil, nil
}
func (ce *exporter) ExportForLayers(layers []digest.Digest) ([]byte, error) {
config, descs, err := ce.chains.Marshal()
if err != nil {
return nil, err
}
descs2 := map[digest.Digest]v1.DescriptorProviderPair{}
for _, k := range layers {
if v, ok := descs[k]; ok {
descs2[k] = v
continue
}
// fallback for uncompressed digests
for _, v := range descs {
if uc := v.Descriptor.Annotations["containerd.io/uncompressed"]; uc == string(k) {
descs2[v.Descriptor.Digest] = v
}
}
}
cc := v1.NewCacheChains()
if err := v1.ParseConfig(*config, descs2, cc); err != nil {
return nil, err
}
cfg, _, err := cc.Marshal()
if err != nil {
return nil, err
}
if len(cfg.Layers) == 0 {
logrus.Warn("failed to match any cache with layers")
return nil, nil
}
cache := map[digest.Digest]int{}
// reorder layers based on the order in the image
for i, r := range cfg.Records {
for j, rr := range r.Results {
n := getSortedLayerIndex(rr.LayerIndex, cfg.Layers, cache)
rr.LayerIndex = n
r.Results[j] = rr
cfg.Records[i] = r
}
}
dt, err := json.Marshal(cfg.Records)
if err != nil {
return nil, err
}
return dt, nil
}
func getSortedLayerIndex(idx int, layers []v1.CacheLayer, cache map[digest.Digest]int) int {
if idx == -1 {
return -1
}
l := layers[idx]
if i, ok := cache[l.Blob]; ok {
return i
}
cache[l.Blob] = getSortedLayerIndex(l.ParentIndex, layers, cache) + 1
return cache[l.Blob]
}

View file

@ -6,6 +6,7 @@ import (
"github.com/containerd/containerd/remotes"
"github.com/containerd/containerd/remotes/docker"
"github.com/docker/distribution/reference"
"github.com/moby/buildkit/cache/remotecache"
"github.com/moby/buildkit/session"
"github.com/moby/buildkit/session/auth"
@ -15,10 +16,26 @@ import (
"github.com/pkg/errors"
)
func canonicalizeRef(rawRef string) (string, error) {
if rawRef == "" {
return "", errors.New("missing ref")
}
parsed, err := reference.ParseNormalizedNamed(rawRef)
if err != nil {
return "", err
}
return reference.TagNameOnly(parsed).String(), nil
}
const (
attrRef = "ref"
)
func ResolveCacheExporterFunc(sm *session.Manager, resolverOpt resolver.ResolveOptionsFunc) remotecache.ResolveCacheExporterFunc {
return func(ctx context.Context, typ, ref string) (remotecache.Exporter, error) {
if typ != "" {
return nil, errors.Errorf("unsupported cache exporter type: %s", typ)
return func(ctx context.Context, attrs map[string]string) (remotecache.Exporter, error) {
ref, err := canonicalizeRef(attrs[attrRef])
if err != nil {
return nil, err
}
remote := newRemoteResolver(ctx, resolverOpt, sm, ref)
pusher, err := remote.Pusher(ctx, ref)
@ -30,9 +47,10 @@ func ResolveCacheExporterFunc(sm *session.Manager, resolverOpt resolver.ResolveO
}
func ResolveCacheImporterFunc(sm *session.Manager, resolverOpt resolver.ResolveOptionsFunc) remotecache.ResolveCacheImporterFunc {
return func(ctx context.Context, typ, ref string) (remotecache.Importer, specs.Descriptor, error) {
if typ != "" {
return nil, specs.Descriptor{}, errors.Errorf("unsupported cache importer type: %s", typ)
return func(ctx context.Context, attrs map[string]string) (remotecache.Importer, specs.Descriptor, error) {
ref, err := canonicalizeRef(attrs[attrRef])
if err != nil {
return nil, specs.Descriptor{}, err
}
remote := newRemoteResolver(ctx, resolverOpt, sm, ref)
xref, desc, err := remote.Resolve(ctx, ref)

View file

@ -2,6 +2,7 @@ package cacheimport
import (
"context"
"time"
"github.com/moby/buildkit/identity"
"github.com/moby/buildkit/solver"
@ -26,6 +27,7 @@ func NewCacheKeyStorage(cc *CacheChains, w worker.Worker) (solver.CacheKeyStorag
results := &cacheResultStorage{
w: w,
byID: storage.byID,
byItem: storage.byItem,
byResult: storage.byResult,
}
@ -154,8 +156,22 @@ func (cs *cacheKeyStorage) WalkLinks(id string, link solver.CacheInfoLink, fn fu
return nil
}
// TODO:
func (cs *cacheKeyStorage) WalkBacklinks(id string, fn func(id string, link solver.CacheInfoLink) error) error {
for k, it := range cs.byID {
for nl, ids := range it.links {
for _, id2 := range ids {
if id == id2 {
if err := fn(k, solver.CacheInfoLink{
Input: solver.Index(nl.input),
Selector: digest.Digest(nl.selector),
Digest: nl.dgst,
}); err != nil {
return err
}
}
}
}
}
return nil
}
@ -189,19 +205,54 @@ type cacheResultStorage struct {
w worker.Worker
byID map[string]*itemWithOutgoingLinks
byResult map[string]map[string]struct{}
byItem map[*item]string
}
func (cs *cacheResultStorage) Save(res solver.Result) (solver.CacheResult, error) {
func (cs *cacheResultStorage) Save(res solver.Result, createdAt time.Time) (solver.CacheResult, error) {
return solver.CacheResult{}, errors.Errorf("importer is immutable")
}
func (cs *cacheResultStorage) Load(ctx context.Context, res solver.CacheResult) (solver.Result, error) {
remote, err := cs.LoadRemote(ctx, res)
if err != nil {
func (cs *cacheResultStorage) LoadWithParents(ctx context.Context, res solver.CacheResult) (map[string]solver.Result, error) {
v := cs.byResultID(res.ID)
if v == nil || v.result == nil {
return nil, errors.WithStack(solver.ErrNotFound)
}
m := map[string]solver.Result{}
if err := v.walkAllResults(func(i *item) error {
if i.result == nil {
return nil
}
id, ok := cs.byItem[i]
if !ok {
return nil
}
if isSubRemote(*i.result, *v.result) {
ref, err := cs.w.FromRemote(ctx, i.result)
if err != nil {
return err
}
m[id] = worker.NewWorkerRefResult(ref, cs.w)
}
return nil
}); err != nil {
for _, v := range m {
v.Release(context.TODO())
}
return nil, err
}
ref, err := cs.w.FromRemote(ctx, remote)
return m, nil
}
func (cs *cacheResultStorage) Load(ctx context.Context, res solver.CacheResult) (solver.Result, error) {
item := cs.byResultID(res.ID)
if item == nil || item.result == nil {
return nil, errors.WithStack(solver.ErrNotFound)
}
ref, err := cs.w.FromRemote(ctx, item.result)
if err != nil {
return nil, err
}
@ -209,8 +260,8 @@ func (cs *cacheResultStorage) Load(ctx context.Context, res solver.CacheResult)
}
func (cs *cacheResultStorage) LoadRemote(ctx context.Context, res solver.CacheResult) (*solver.Remote, error) {
if r := cs.byResultID(res.ID); r != nil {
return r, nil
if r := cs.byResultID(res.ID); r != nil && r.result != nil {
return r.result, nil
}
return nil, errors.WithStack(solver.ErrNotFound)
}
@ -219,7 +270,7 @@ func (cs *cacheResultStorage) Exists(id string) bool {
return cs.byResultID(id) != nil
}
func (cs *cacheResultStorage) byResultID(resultID string) *solver.Remote {
func (cs *cacheResultStorage) byResultID(resultID string) *itemWithOutgoingLinks {
m, ok := cs.byResult[resultID]
if !ok || len(m) == 0 {
return nil
@ -228,9 +279,7 @@ func (cs *cacheResultStorage) byResultID(resultID string) *solver.Remote {
for id := range m {
it, ok := cs.byID[id]
if ok {
if r := it.result; r != nil {
return r
}
return it
}
}

View file

@ -1,6 +1,7 @@
package cacheimport
import (
"strings"
"time"
"github.com/containerd/containerd/content"
@ -19,6 +20,9 @@ type CacheChains struct {
}
func (c *CacheChains) Add(dgst digest.Digest) solver.CacheExporterRecord {
if strings.HasPrefix(dgst.String(), "random:") {
return &nopRecord{}
}
it := &item{c: c, dgst: dgst}
c.items = append(c.items, it)
return it
@ -124,4 +128,27 @@ func (c *item) LinkFrom(rec solver.CacheExporterRecord, index int, selector stri
c.links[index][link{src: src, selector: selector}] = struct{}{}
}
func (c *item) walkAllResults(fn func(i *item) error) error {
if err := fn(c); err != nil {
return err
}
for _, links := range c.links {
for l := range links {
if err := l.src.walkAllResults(fn); err != nil {
return err
}
}
}
return nil
}
type nopRecord struct {
}
func (c *nopRecord) AddResult(createdAt time.Time, result *solver.Remote) {
}
func (c *nopRecord) LinkFrom(rec solver.CacheExporterRecord, index int, selector string) {
}
var _ solver.CacheExporterTarget = &CacheChains{}

View file

@ -6,7 +6,7 @@ package cacheimport
// https://github.com/opencontainers/image-spec/blob/master/image-index.md .
// Manifests array contains descriptors to the cache layers and one instance of
// build cache config with media type application/vnd.buildkit.cacheconfig.v0 .
// The cache layer descripts need to have an annotation with uncompressed digest
// The cache layer descriptors need to have an annotation with uncompressed digest
// to allow deduplication on extraction and optionally "buildkit/createdat"
// annotation to support maintaining original timestamps.
//

View file

@ -15,6 +15,10 @@ func Parse(configJSON []byte, provider DescriptorProvider, t solver.CacheExporte
return err
}
return ParseConfig(config, provider, t)
}
func ParseConfig(config CacheConfig, provider DescriptorProvider, t solver.CacheExporterTarget) error {
cache := map[int]solver.CacheExporterRecord{}
for i := range config.Records {
@ -22,7 +26,6 @@ func Parse(configJSON []byte, provider DescriptorProvider, t solver.CacheExporte
return err
}
}
return nil
}
@ -57,7 +60,9 @@ func parseRecord(cc CacheConfig, idx int, provider DescriptorProvider, t solver.
if err != nil {
return nil, err
}
r.AddResult(res.CreatedAt, remote)
if remote != nil {
r.AddResult(res.CreatedAt, remote)
}
}
cache[idx] = r
@ -78,7 +83,7 @@ func getRemoteChain(layers []CacheLayer, idx int, provider DescriptorProvider, v
descPair, ok := provider[l.Blob]
if !ok {
return nil, errors.Errorf("missing blob for %s", l.Blob)
return nil, nil
}
var r *solver.Remote
@ -88,6 +93,9 @@ func getRemoteChain(layers []CacheLayer, idx int, provider DescriptorProvider, v
if err != nil {
return nil, err
}
if r == nil {
return nil, nil
}
r.Descriptors = append(r.Descriptors, descPair.Descriptor)
mp := contentutil.NewMultiProvider(r.Provider)
mp.Add(descPair.Descriptor.Digest, descPair.Provider)

View file

@ -304,3 +304,15 @@ func marshalItem(it *item, state *marshalState) error {
state.records = append(state.records, rec)
return nil
}
func isSubRemote(sub, main solver.Remote) bool {
if len(sub.Descriptors) > len(main.Descriptors) {
return false
}
for i := range sub.Descriptors {
if sub.Descriptors[i].Digest != main.Descriptors[i].Digest {
return false
}
}
return true
}

View file

@ -16,7 +16,7 @@ func dialer(address string, timeout time.Duration) (net.Conn, error) {
}
switch addrParts[0] {
case "npipe":
address = strings.Replace(addrParts[1], "/", "\\", 0)
address = strings.Replace(addrParts[1], "/", "\\", -1)
return winio.DialPipe(address, &timeout)
default:
return net.DialTimeout(addrParts[0], addrParts[1], timeout)

View file

@ -41,5 +41,6 @@ type SolveStatus struct {
}
type SolveResponse struct {
// ExporterResponse is also used for CacheExporter
ExporterResponse map[string]string
}

View file

@ -405,12 +405,16 @@ func WithDescription(m map[string]string) ConstraintsOpt {
})
}
func WithCustomName(name string, a ...interface{}) ConstraintsOpt {
func WithCustomName(name string) ConstraintsOpt {
return WithDescription(map[string]string{
"llb.customname": fmt.Sprintf(name, a...),
"llb.customname": name,
})
}
func WithCustomNamef(name string, a ...interface{}) ConstraintsOpt {
return WithCustomName(fmt.Sprintf(name, a...))
}
// WithExportCache forces results for this vertex to be exported with the cache
func WithExportCache() ConstraintsOpt {
return constraintsOptFunc(func(c *Constraints) {

View file

@ -0,0 +1,113 @@
package ociindex
import (
"encoding/json"
"io/ioutil"
"os"
"github.com/gofrs/flock"
"github.com/opencontainers/image-spec/specs-go/v1"
"github.com/pkg/errors"
)
const (
// IndexJSONLockFileSuffix is the suffix of the lock file
IndexJSONLockFileSuffix = ".lock"
)
// PutDescToIndex puts desc to index with tag.
// Existing manifests with the same tag will be removed from the index.
func PutDescToIndex(index *v1.Index, desc v1.Descriptor, tag string) error {
if index == nil {
index = &v1.Index{}
}
if index.SchemaVersion == 0 {
index.SchemaVersion = 2
}
if tag != "" {
if desc.Annotations == nil {
desc.Annotations = make(map[string]string)
}
desc.Annotations[v1.AnnotationRefName] = tag
// remove existing manifests with the same tag
var manifests []v1.Descriptor
for _, m := range index.Manifests {
if m.Annotations[v1.AnnotationRefName] != tag {
manifests = append(manifests, m)
}
}
index.Manifests = manifests
}
index.Manifests = append(index.Manifests, desc)
return nil
}
func PutDescToIndexJSONFileLocked(indexJSONPath string, desc v1.Descriptor, tag string) error {
lockPath := indexJSONPath + IndexJSONLockFileSuffix
lock := flock.New(lockPath)
locked, err := lock.TryLock()
if err != nil {
return errors.Wrapf(err, "could not lock %s", lockPath)
}
if !locked {
return errors.Errorf("could not lock %s", lockPath)
}
defer func() {
lock.Unlock()
os.RemoveAll(lockPath)
}()
f, err := os.OpenFile(indexJSONPath, os.O_RDWR|os.O_CREATE, 0644)
if err != nil {
return errors.Wrapf(err, "could not open %s", indexJSONPath)
}
defer f.Close()
var idx v1.Index
b, err := ioutil.ReadAll(f)
if err != nil {
return errors.Wrapf(err, "could not read %s", indexJSONPath)
}
if len(b) > 0 {
if err := json.Unmarshal(b, &idx); err != nil {
return errors.Wrapf(err, "could not unmarshal %s (%q)", indexJSONPath, string(b))
}
}
if err = PutDescToIndex(&idx, desc, tag); err != nil {
return err
}
b, err = json.Marshal(idx)
if err != nil {
return err
}
if _, err = f.WriteAt(b, 0); err != nil {
return err
}
if err = f.Truncate(int64(len(b))); err != nil {
return err
}
return nil
}
func ReadIndexJSONFileLocked(indexJSONPath string) (*v1.Index, error) {
lockPath := indexJSONPath + IndexJSONLockFileSuffix
lock := flock.New(lockPath)
locked, err := lock.TryRLock()
if err != nil {
return nil, errors.Wrapf(err, "could not lock %s", lockPath)
}
if !locked {
return nil, errors.Errorf("could not lock %s", lockPath)
}
defer func() {
lock.Unlock()
os.RemoveAll(lockPath)
}()
b, err := ioutil.ReadFile(indexJSONPath)
if err != nil {
return nil, errors.Wrapf(err, "could not read %s", indexJSONPath)
}
var idx v1.Index
if err := json.Unmarshal(b, &idx); err != nil {
return nil, errors.Wrapf(err, "could not unmarshal %s (%q)", indexJSONPath, string(b))
}
return &idx, nil
}

View file

@ -2,20 +2,26 @@ package client
import (
"context"
"encoding/json"
"io"
"os"
"path/filepath"
"strings"
"time"
"github.com/containerd/containerd/content"
contentlocal "github.com/containerd/containerd/content/local"
controlapi "github.com/moby/buildkit/api/services/control"
"github.com/moby/buildkit/client/llb"
"github.com/moby/buildkit/client/ociindex"
"github.com/moby/buildkit/identity"
"github.com/moby/buildkit/session"
sessioncontent "github.com/moby/buildkit/session/content"
"github.com/moby/buildkit/session/filesync"
"github.com/moby/buildkit/session/grpchijack"
"github.com/moby/buildkit/solver/pb"
"github.com/moby/buildkit/util/entitlements"
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
opentracing "github.com/opentracing/opentracing-go"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
@ -24,21 +30,29 @@ import (
)
type SolveOpt struct {
Exporter string
ExporterAttrs map[string]string
ExporterOutput io.WriteCloser // for ExporterOCI and ExporterDocker
ExporterOutputDir string // for ExporterLocal
Exports []ExportEntry
LocalDirs map[string]string
SharedKey string
Frontend string
FrontendAttrs map[string]string
ExportCache string
ExportCacheAttrs map[string]string
ImportCache []string
CacheExports []CacheOptionsEntry
CacheImports []CacheOptionsEntry
Session []session.Attachable
AllowedEntitlements []entitlements.Entitlement
}
type ExportEntry struct {
Type string
Attrs map[string]string
Output io.WriteCloser // for ExporterOCI and ExporterDocker
OutputDir string // for ExporterLocal
}
type CacheOptionsEntry struct {
Type string
Attrs map[string]string
}
// Solve calls Solve on the controller.
// def must be nil if (and only if) opt.Frontend is set.
func (c *Client) Solve(ctx context.Context, def *llb.Definition, opt SolveOpt, statusChan chan *SolveStatus) (*SolveResponse, error) {
@ -93,32 +107,51 @@ func (c *Client) solve(ctx context.Context, def *llb.Definition, runGateway runG
s.Allow(a)
}
switch opt.Exporter {
var ex ExportEntry
if len(opt.Exports) > 1 {
return nil, errors.New("currently only single Exports can be specified")
}
if len(opt.Exports) == 1 {
ex = opt.Exports[0]
}
switch ex.Type {
case ExporterLocal:
if opt.ExporterOutput != nil {
if ex.Output != nil {
return nil, errors.New("output file writer is not supported by local exporter")
}
if opt.ExporterOutputDir == "" {
if ex.OutputDir == "" {
return nil, errors.New("output directory is required for local exporter")
}
s.Allow(filesync.NewFSSyncTargetDir(opt.ExporterOutputDir))
s.Allow(filesync.NewFSSyncTargetDir(ex.OutputDir))
case ExporterOCI, ExporterDocker:
if opt.ExporterOutputDir != "" {
return nil, errors.Errorf("output directory %s is not supported by %s exporter", opt.ExporterOutputDir, opt.Exporter)
if ex.OutputDir != "" {
return nil, errors.Errorf("output directory %s is not supported by %s exporter", ex.OutputDir, ex.Type)
}
if opt.ExporterOutput == nil {
return nil, errors.Errorf("output file writer is required for %s exporter", opt.Exporter)
if ex.Output == nil {
return nil, errors.Errorf("output file writer is required for %s exporter", ex.Type)
}
s.Allow(filesync.NewFSSyncTarget(opt.ExporterOutput))
s.Allow(filesync.NewFSSyncTarget(ex.Output))
default:
if opt.ExporterOutput != nil {
return nil, errors.Errorf("output file writer is not supported by %s exporter", opt.Exporter)
if ex.Output != nil {
return nil, errors.Errorf("output file writer is not supported by %s exporter", ex.Type)
}
if opt.ExporterOutputDir != "" {
return nil, errors.Errorf("output directory %s is not supported by %s exporter", opt.ExporterOutputDir, opt.Exporter)
if ex.OutputDir != "" {
return nil, errors.Errorf("output directory %s is not supported by %s exporter", ex.OutputDir, ex.Type)
}
}
cacheOpt, err := parseCacheOptions(opt)
if err != nil {
return nil, err
}
if len(cacheOpt.contentStores) > 0 {
s.Allow(sessioncontent.NewAttachable(cacheOpt.contentStores))
}
for k, v := range cacheOpt.frontendAttrs {
opt.FrontendAttrs[k] = v
}
eg.Go(func() error {
return s.Run(statusContext, grpchijack.Dialer(c.controlClient()))
})
@ -144,17 +177,13 @@ func (c *Client) solve(ctx context.Context, def *llb.Definition, runGateway runG
resp, err := c.controlClient().Solve(ctx, &controlapi.SolveRequest{
Ref: ref,
Definition: pbd,
Exporter: opt.Exporter,
ExporterAttrs: opt.ExporterAttrs,
Exporter: ex.Type,
ExporterAttrs: ex.Attrs,
Session: s.ID(),
Frontend: opt.Frontend,
FrontendAttrs: opt.FrontendAttrs,
Cache: controlapi.CacheOptions{
ExportRef: opt.ExportCache,
ImportRefs: opt.ImportCache,
ExportAttrs: opt.ExportCacheAttrs,
},
Entitlements: opt.AllowedEntitlements,
Cache: cacheOpt.options,
Entitlements: opt.AllowedEntitlements,
})
if err != nil {
return errors.Wrap(err, "failed to solve")
@ -243,6 +272,19 @@ func (c *Client) solve(ctx context.Context, def *llb.Definition, runGateway runG
if err := eg.Wait(); err != nil {
return nil, err
}
// Update index.json of exported cache content store
// FIXME(AkihiroSuda): dedupe const definition of cache/remotecache.ExporterResponseManifestDesc = "cache.manifest"
if manifestDescJSON := res.ExporterResponse["cache.manifest"]; manifestDescJSON != "" {
var manifestDesc ocispec.Descriptor
if err = json.Unmarshal([]byte(manifestDescJSON), &manifestDesc); err != nil {
return nil, err
}
for indexJSONPath, tag := range cacheOpt.indicesToUpdate {
if err = ociindex.PutDescToIndexJSONFileLocked(indexJSONPath, manifestDesc, tag); err != nil {
return nil, err
}
}
}
return res, nil
}
@ -295,3 +337,128 @@ func defaultSessionName() string {
}
return filepath.Base(wd)
}
type cacheOptions struct {
options controlapi.CacheOptions
contentStores map[string]content.Store // key: ID of content store ("local:" + csDir)
indicesToUpdate map[string]string // key: index.JSON file name, value: tag
frontendAttrs map[string]string
}
func parseCacheOptions(opt SolveOpt) (*cacheOptions, error) {
var (
cacheExports []*controlapi.CacheOptionsEntry
cacheImports []*controlapi.CacheOptionsEntry
// legacy API is used for registry caches, because the daemon might not support the new API
legacyExportRef string
legacyImportRefs []string
)
contentStores := make(map[string]content.Store)
indicesToUpdate := make(map[string]string) // key: index.JSON file name, value: tag
frontendAttrs := make(map[string]string)
legacyExportAttrs := make(map[string]string)
for _, ex := range opt.CacheExports {
if ex.Type == "local" {
csDir := ex.Attrs["dest"]
if csDir == "" {
return nil, errors.New("local cache exporter requires dest")
}
if err := os.MkdirAll(csDir, 0755); err != nil {
return nil, err
}
cs, err := contentlocal.NewStore(csDir)
if err != nil {
return nil, err
}
contentStores["local:"+csDir] = cs
// TODO(AkihiroSuda): support custom index JSON path and tag
indexJSONPath := filepath.Join(csDir, "index.json")
indicesToUpdate[indexJSONPath] = "latest"
}
if ex.Type == "registry" && legacyExportRef == "" {
legacyExportRef = ex.Attrs["ref"]
for k, v := range ex.Attrs {
if k != "ref" {
legacyExportAttrs[k] = v
}
}
} else {
cacheExports = append(cacheExports, &controlapi.CacheOptionsEntry{
Type: ex.Type,
Attrs: ex.Attrs,
})
}
}
for _, im := range opt.CacheImports {
attrs := im.Attrs
if im.Type == "local" {
csDir := im.Attrs["src"]
if csDir == "" {
return nil, errors.New("local cache importer requires src")
}
if err := os.MkdirAll(csDir, 0755); err != nil {
return nil, err
}
cs, err := contentlocal.NewStore(csDir)
if err != nil {
return nil, err
}
contentStores["local:"+csDir] = cs
// if digest is not specified, load from "latest" tag
if attrs["digest"] == "" {
idx, err := ociindex.ReadIndexJSONFileLocked(filepath.Join(csDir, "index.json"))
if err != nil {
return nil, err
}
for _, m := range idx.Manifests {
if m.Annotations[ocispec.AnnotationRefName] == "latest" {
attrs["digest"] = string(m.Digest)
break
}
}
if attrs["digest"] == "" {
return nil, errors.New("local cache importer requires either explicit digest or \"latest\" tag on index.json")
}
}
}
if im.Type == "registry" {
legacyImportRef := attrs["ref"]
legacyImportRefs = append(legacyImportRefs, legacyImportRef)
} else {
cacheImports = append(cacheImports, &controlapi.CacheOptionsEntry{
Type: im.Type,
Attrs: attrs,
})
}
}
if opt.Frontend != "" {
// use legacy API for registry importers, because the frontend might not support the new API
if len(legacyImportRefs) > 0 {
frontendAttrs["cache-from"] = strings.Join(legacyImportRefs, ",")
}
// use new API for other importers
if len(cacheImports) > 0 {
s, err := json.Marshal(cacheImports)
if err != nil {
return nil, err
}
frontendAttrs["cache-imports"] = string(s)
}
}
res := cacheOptions{
options: controlapi.CacheOptions{
// old API (for registry caches, planned to be removed in early 2019)
ExportRefDeprecated: legacyExportRef,
ExportAttrsDeprecated: legacyExportAttrs,
ImportRefsDeprecated: legacyImportRefs,
// new API
Exports: cacheExports,
Imports: cacheImports,
},
contentStores: contentStores,
indicesToUpdate: indicesToUpdate,
frontendAttrs: frontendAttrs,
}
return &res, nil
}

View file

@ -5,7 +5,6 @@ import (
"sync"
"time"
"github.com/docker/distribution/reference"
controlapi "github.com/moby/buildkit/api/services/control"
apitypes "github.com/moby/buildkit/api/types"
"github.com/moby/buildkit/cache/remotecache"
@ -26,15 +25,13 @@ import (
"google.golang.org/grpc"
)
type ResolveCacheExporterFunc func(ctx context.Context, typ, target string) (remotecache.Exporter, error)
type Opt struct {
SessionManager *session.Manager
WorkerController *worker.Controller
Frontends map[string]frontend.Frontend
CacheKeyStorage solver.CacheKeyStorage
ResolveCacheExporterFunc remotecache.ResolveCacheExporterFunc
ResolveCacheImporterFunc remotecache.ResolveCacheImporterFunc
SessionManager *session.Manager
WorkerController *worker.Controller
Frontends map[string]frontend.Frontend
CacheKeyStorage solver.CacheKeyStorage
ResolveCacheExporterFuncs map[string]remotecache.ResolveCacheExporterFunc
ResolveCacheImporterFuncs map[string]remotecache.ResolveCacheImporterFunc
}
type Controller struct { // TODO: ControlService
@ -51,7 +48,7 @@ func NewController(opt Opt) (*Controller, error) {
gatewayForwarder := controlgateway.NewGatewayForwarder()
solver, err := llbsolver.New(opt.WorkerController, opt.Frontends, cache, opt.ResolveCacheImporterFunc, gatewayForwarder)
solver, err := llbsolver.New(opt.WorkerController, opt.Frontends, cache, opt.ResolveCacheImporterFuncs, gatewayForwarder, opt.SessionManager)
if err != nil {
return nil, errors.Wrap(err, "failed to create solver")
}
@ -179,7 +176,39 @@ func (c *Controller) Prune(req *controlapi.PruneRequest, stream controlapi.Contr
return eg2.Wait()
}
func translateLegacySolveRequest(req *controlapi.SolveRequest) error {
// translates ExportRef and ExportAttrs to new Exports (v0.4.0)
if legacyExportRef := req.Cache.ExportRefDeprecated; legacyExportRef != "" {
ex := &controlapi.CacheOptionsEntry{
Type: "registry",
Attrs: req.Cache.ExportAttrsDeprecated,
}
if ex.Attrs == nil {
ex.Attrs = make(map[string]string)
}
ex.Attrs["ref"] = legacyExportRef
// FIXME(AkihiroSuda): skip append if already exists
req.Cache.Exports = append(req.Cache.Exports, ex)
req.Cache.ExportRefDeprecated = ""
req.Cache.ExportAttrsDeprecated = nil
}
// translates ImportRefs to new Imports (v0.4.0)
for _, legacyImportRef := range req.Cache.ImportRefsDeprecated {
im := &controlapi.CacheOptionsEntry{
Type: "registry",
Attrs: map[string]string{"ref": legacyImportRef},
}
// FIXME(AkihiroSuda): skip append if already exists
req.Cache.Imports = append(req.Cache.Imports, im)
}
req.Cache.ImportRefsDeprecated = nil
return nil
}
func (c *Controller) Solve(ctx context.Context, req *controlapi.SolveRequest) (*controlapi.SolveResponse, error) {
if err := translateLegacySolveRequest(req); err != nil {
return nil, err
}
ctx = session.NewContext(ctx, req.Session)
defer func() {
@ -194,7 +223,7 @@ func (c *Controller) Solve(ctx context.Context, req *controlapi.SolveRequest) (*
return nil, err
}
if req.Exporter != "" {
exp, err := w.Exporter(req.Exporter)
exp, err := w.Exporter(req.Exporter, c.opt.SessionManager)
if err != nil {
return nil, err
}
@ -204,38 +233,44 @@ func (c *Controller) Solve(ctx context.Context, req *controlapi.SolveRequest) (*
}
}
var cacheExporter remotecache.Exporter
if ref := req.Cache.ExportRef; ref != "" && c.opt.ResolveCacheExporterFunc != nil {
parsed, err := reference.ParseNormalizedNamed(ref)
if err != nil {
return nil, err
}
exportCacheRef := reference.TagNameOnly(parsed).String()
typ := "" // unimplemented yet (typically registry)
cacheExporter, err = c.opt.ResolveCacheExporterFunc(ctx, typ, exportCacheRef)
if err != nil {
return nil, err
}
var (
cacheExporter remotecache.Exporter
cacheExportMode solver.CacheExportMode
cacheImports []frontend.CacheOptionsEntry
)
if len(req.Cache.Exports) > 1 {
// TODO(AkihiroSuda): this should be fairly easy
return nil, errors.New("specifying multiple cache exports is not supported currently")
}
var importCacheRefs []string
for _, ref := range req.Cache.ImportRefs {
parsed, err := reference.ParseNormalizedNamed(ref)
if len(req.Cache.Exports) == 1 {
e := req.Cache.Exports[0]
cacheExporterFunc, ok := c.opt.ResolveCacheExporterFuncs[e.Type]
if !ok {
return nil, errors.Errorf("unknown cache exporter: %q", e.Type)
}
cacheExporter, err = cacheExporterFunc(ctx, e.Attrs)
if err != nil {
return nil, err
}
importCacheRefs = append(importCacheRefs, reference.TagNameOnly(parsed).String())
cacheExportMode = parseCacheExportMode(e.Attrs["mode"])
}
for _, im := range req.Cache.Imports {
cacheImports = append(cacheImports, frontend.CacheOptionsEntry{
Type: im.Type,
Attrs: im.Attrs,
})
}
resp, err := c.solver.Solve(ctx, req.Ref, frontend.SolveRequest{
Frontend: req.Frontend,
Definition: req.Definition,
FrontendOpt: req.FrontendAttrs,
ImportCacheRefs: importCacheRefs,
Frontend: req.Frontend,
Definition: req.Definition,
FrontendOpt: req.FrontendAttrs,
CacheImports: cacheImports,
}, llbsolver.ExporterRequest{
Exporter: expi,
CacheExporter: cacheExporter,
CacheExportMode: parseCacheExporterOpt(req.Cache.ExportAttrs),
CacheExportMode: cacheExportMode,
}, req.Entitlements)
if err != nil {
return nil, err
@ -376,21 +411,15 @@ func (c *Controller) gc() {
}
}
func parseCacheExporterOpt(opt map[string]string) solver.CacheExportMode {
for k, v := range opt {
switch k {
case "mode":
switch v {
case "min":
return solver.CacheExportModeMin
case "max":
return solver.CacheExportModeMax
default:
logrus.Debugf("skipping incalid cache export mode: %s", v)
}
default:
logrus.Warnf("skipping invalid cache export opt: %s", v)
}
func parseCacheExportMode(mode string) solver.CacheExportMode {
switch mode {
case "min":
return solver.CacheExportModeMin
case "max":
return solver.CacheExportModeMax
case "":
default:
logrus.Debugf("skipping invalid cache export mode: %s", mode)
}
return solver.CacheExportModeMin
}

View file

@ -2,16 +2,19 @@ package oci
import (
"context"
"path/filepath"
"strings"
specs "github.com/opencontainers/runtime-spec/specs-go"
"github.com/pkg/errors"
)
// MountOpts sets oci spec specific info for mount points
type MountOpts func([]specs.Mount) []specs.Mount
type MountOpts func([]specs.Mount) ([]specs.Mount, error)
//GetMounts returns default required for buildkit
// https://github.com/moby/buildkit/issues/429
func GetMounts(ctx context.Context, mountOpts ...MountOpts) []specs.Mount {
func GetMounts(ctx context.Context, mountOpts ...MountOpts) ([]specs.Mount, error) {
mounts := []specs.Mount{
{
Destination: "/proc",
@ -49,20 +52,66 @@ func GetMounts(ctx context.Context, mountOpts ...MountOpts) []specs.Mount {
Options: []string{"nosuid", "noexec", "nodev", "ro"},
},
}
var err error
for _, o := range mountOpts {
mounts = o(mounts)
mounts, err = o(mounts)
if err != nil {
return nil, err
}
}
return mounts
return mounts, nil
}
func withROBind(src, dest string) func(m []specs.Mount) []specs.Mount {
return func(m []specs.Mount) []specs.Mount {
func withROBind(src, dest string) func(m []specs.Mount) ([]specs.Mount, error) {
return func(m []specs.Mount) ([]specs.Mount, error) {
m = append(m, specs.Mount{
Destination: dest,
Type: "bind",
Source: src,
Options: []string{"rbind", "ro"},
})
return m
return m, nil
}
}
func hasPrefix(p, prefixDir string) bool {
prefixDir = filepath.Clean(prefixDir)
if prefixDir == "/" {
return true
}
p = filepath.Clean(p)
return p == prefixDir || strings.HasPrefix(p, prefixDir+"/")
}
func removeMountsWithPrefix(mounts []specs.Mount, prefixDir string) []specs.Mount {
var ret []specs.Mount
for _, m := range mounts {
if !hasPrefix(m.Destination, prefixDir) {
ret = append(ret, m)
}
}
return ret
}
func withProcessMode(processMode ProcessMode) func([]specs.Mount) ([]specs.Mount, error) {
return func(m []specs.Mount) ([]specs.Mount, error) {
switch processMode {
case ProcessSandbox:
// keep the default
case NoProcessSandbox:
m = removeMountsWithPrefix(m, "/proc")
procMount := specs.Mount{
Destination: "/proc",
Type: "bind",
Source: "/proc",
// NOTE: "rbind"+"ro" does not make /proc read-only recursively.
// So we keep maskedPath and readonlyPaths (although not mandatory for rootless mode)
Options: []string{"rbind"},
}
m = append([]specs.Mount{procMount}, m...)
default:
return nil, errors.Errorf("unknown process mode: %v", processMode)
}
return m, nil
}
}

View file

@ -22,8 +22,21 @@ import (
// Ideally we don't have to import whole containerd just for the default spec
// ProcMode configures PID namespaces
type ProcessMode int
const (
// ProcessSandbox unshares pidns and mount procfs.
ProcessSandbox ProcessMode = iota
// NoProcessSandbox uses host pidns and bind-mount procfs.
// Note that NoProcessSandbox allows build containers to kill (and potentially ptrace) an arbitrary process in the BuildKit host namespace.
// NoProcessSandbox should be enabled only when the BuildKit is running in a container as an unprivileged user.
NoProcessSandbox
)
// GenerateSpec generates spec using containerd functionality.
func GenerateSpec(ctx context.Context, meta executor.Meta, mounts []executor.Mount, id, resolvConf, hostsFile string, namespace network.Namespace, opts ...oci.SpecOpts) (*specs.Spec, func(), error) {
// opts are ignored for s.Process, s.Hostname, and s.Mounts .
func GenerateSpec(ctx context.Context, meta executor.Meta, mounts []executor.Mount, id, resolvConf, hostsFile string, namespace network.Namespace, processMode ProcessMode, opts ...oci.SpecOpts) (*specs.Spec, func(), error) {
c := &containers.Container{
ID: id,
}
@ -32,6 +45,14 @@ func GenerateSpec(ctx context.Context, meta executor.Meta, mounts []executor.Mou
ctx = namespaces.WithNamespace(ctx, "buildkit")
}
switch processMode {
case NoProcessSandbox:
// Mount for /proc is replaced in GetMounts()
opts = append(opts,
oci.WithHostNamespace(specs.PIDNamespace))
// TODO(AkihiroSuda): Configure seccomp to disable ptrace (and prctl?) explicitly
}
// Note that containerd.GenerateSpec is namespaced so as to make
// specs.Linux.CgroupsPath namespaced
s, err := oci.GenerateSpec(ctx, nil, c, opts...)
@ -48,10 +69,14 @@ func GenerateSpec(ctx context.Context, meta executor.Meta, mounts []executor.Mou
s.Process.NoNewPrivileges = false // reset nonewprivileges
s.Hostname = "buildkitsandbox"
s.Mounts = GetMounts(ctx,
s.Mounts, err = GetMounts(ctx,
withProcessMode(processMode),
withROBind(resolvConf, "/etc/resolv.conf"),
withROBind(hostsFile, "/etc/hosts"),
)
if err != nil {
return nil, nil, err
}
s.Mounts = append(s.Mounts, specs.Mount{
Destination: "/sys/fs/cgroup",

View file

@ -39,6 +39,8 @@ type Opt struct {
Rootless bool
// DefaultCgroupParent is the cgroup-parent name for executor
DefaultCgroupParent string
// ProcessMode
ProcessMode oci.ProcessMode
}
var defaultCommandCandidates = []string{"buildkit-runc", "runc"}
@ -50,6 +52,7 @@ type runcExecutor struct {
cgroupParent string
rootless bool
networkProviders map[pb.NetMode]network.Provider
processMode oci.ProcessMode
}
func New(opt Opt, networkProviders map[pb.NetMode]network.Provider) (executor.Executor, error) {
@ -105,6 +108,7 @@ func New(opt Opt, networkProviders map[pb.NetMode]network.Provider) (executor.Ex
cgroupParent: opt.DefaultCgroupParent,
rootless: opt.Rootless,
networkProviders: networkProviders,
processMode: opt.ProcessMode,
}
return w, nil
}
@ -193,7 +197,7 @@ func (w *runcExecutor) Exec(ctx context.Context, meta executor.Meta, root cache.
}
opts = append(opts, containerdoci.WithCgroup(cgroupsPath))
}
spec, cleanup, err := oci.GenerateSpec(ctx, meta, mounts, id, resolvConf, hostsFile, namespace, opts...)
spec, cleanup, err := oci.GenerateSpec(ctx, meta, mounts, id, resolvConf, hostsFile, namespace, w.processMode, opts...)
if err != nil {
return err
}

View file

@ -3,6 +3,7 @@ package exptypes
import specs "github.com/opencontainers/image-spec/specs-go/v1"
const ExporterImageConfigKey = "containerimage.config"
const ExporterInlineCache = "containerimage.inlinecache"
const ExporterPlatformsKey = "refs.platforms"
type Platforms struct {

View file

@ -0,0 +1,148 @@
package local
import (
"context"
"io/ioutil"
"os"
"strings"
"time"
"github.com/moby/buildkit/cache"
"github.com/moby/buildkit/exporter"
"github.com/moby/buildkit/session"
"github.com/moby/buildkit/session/filesync"
"github.com/moby/buildkit/snapshot"
"github.com/moby/buildkit/util/progress"
"github.com/pkg/errors"
"github.com/tonistiigi/fsutil"
fstypes "github.com/tonistiigi/fsutil/types"
"golang.org/x/sync/errgroup"
"golang.org/x/time/rate"
)
type Opt struct {
SessionManager *session.Manager
}
type localExporter struct {
opt Opt
// session manager
}
func New(opt Opt) (exporter.Exporter, error) {
le := &localExporter{opt: opt}
return le, nil
}
func (e *localExporter) Resolve(ctx context.Context, opt map[string]string) (exporter.ExporterInstance, error) {
id := session.FromContext(ctx)
if id == "" {
return nil, errors.New("could not access local files without session")
}
timeoutCtx, cancel := context.WithTimeout(ctx, 5*time.Second)
defer cancel()
caller, err := e.opt.SessionManager.Get(timeoutCtx, id)
if err != nil {
return nil, err
}
li := &localExporterInstance{localExporter: e, caller: caller}
return li, nil
}
type localExporterInstance struct {
*localExporter
caller session.Caller
}
func (e *localExporterInstance) Name() string {
return "exporting to client"
}
func (e *localExporterInstance) Export(ctx context.Context, inp exporter.Source) (map[string]string, error) {
isMap := len(inp.Refs) > 0
export := func(ctx context.Context, k string, ref cache.ImmutableRef) func() error {
return func() error {
var src string
var err error
if ref == nil {
src, err = ioutil.TempDir("", "buildkit")
if err != nil {
return err
}
defer os.RemoveAll(src)
} else {
mount, err := ref.Mount(ctx, true)
if err != nil {
return err
}
lm := snapshot.LocalMounter(mount)
src, err = lm.Mount()
if err != nil {
return err
}
defer lm.Unmount()
}
fs := fsutil.NewFS(src, nil)
lbl := "copying files"
if isMap {
lbl += " " + k
fs = fsutil.SubDirFS(fs, fstypes.Stat{
Mode: uint32(os.ModeDir | 0755),
Path: strings.Replace(k, "/", "_", -1),
})
}
progress := newProgressHandler(ctx, lbl)
if err := filesync.CopyToCaller(ctx, fs, e.caller, progress); err != nil {
return err
}
return nil
}
}
eg, ctx := errgroup.WithContext(ctx)
if isMap {
for k, ref := range inp.Refs {
eg.Go(export(ctx, k, ref))
}
} else {
eg.Go(export(ctx, "", inp.Ref))
}
if err := eg.Wait(); err != nil {
return nil, err
}
return nil, nil
}
func newProgressHandler(ctx context.Context, id string) func(int, bool) {
limiter := rate.NewLimiter(rate.Every(100*time.Millisecond), 1)
pw, _, _ := progress.FromContext(ctx)
now := time.Now()
st := progress.Status{
Started: &now,
Action: "transferring",
}
pw.Write(id, st)
return func(s int, last bool) {
if last || limiter.Allow() {
st.Current = s
if last {
now := time.Now()
st.Completed = &now
}
pw.Write(id, st)
if last {
pw.Close()
}
}
}
}

View file

@ -14,6 +14,7 @@ import (
"github.com/containerd/containerd/platforms"
"github.com/docker/docker/builder/dockerignore"
controlapi "github.com/moby/buildkit/api/services/control"
"github.com/moby/buildkit/client/llb"
"github.com/moby/buildkit/exporter/containerimage/exptypes"
"github.com/moby/buildkit/frontend/dockerfile/dockerfile2llb"
@ -25,22 +26,25 @@ import (
)
const (
LocalNameContext = "context"
LocalNameDockerfile = "dockerfile"
keyTarget = "target"
keyFilename = "filename"
keyCacheFrom = "cache-from"
defaultDockerfileName = "Dockerfile"
dockerignoreFilename = ".dockerignore"
buildArgPrefix = "build-arg:"
labelPrefix = "label:"
keyNoCache = "no-cache"
keyTargetPlatform = "platform"
keyMultiPlatform = "multi-platform"
keyImageResolveMode = "image-resolve-mode"
keyGlobalAddHosts = "add-hosts"
keyForceNetwork = "force-network-mode"
keyOverrideCopyImage = "override-copy-image" // remove after CopyOp implemented
DefaultLocalNameContext = "context"
DefaultLocalNameDockerfile = "dockerfile"
keyTarget = "target"
keyFilename = "filename"
keyCacheFrom = "cache-from" // for registry only. deprecated in favor of keyCacheImports
keyCacheImports = "cache-imports" // JSON representation of []CacheOptionsEntry
defaultDockerfileName = "Dockerfile"
dockerignoreFilename = ".dockerignore"
buildArgPrefix = "build-arg:"
labelPrefix = "label:"
keyNoCache = "no-cache"
keyTargetPlatform = "platform"
keyMultiPlatform = "multi-platform"
keyImageResolveMode = "image-resolve-mode"
keyGlobalAddHosts = "add-hosts"
keyForceNetwork = "force-network-mode"
keyOverrideCopyImage = "override-copy-image" // remove after CopyOp implemented
keyNameContext = "contextkey"
keyNameDockerfile = "dockerfilekey"
)
var httpPrefix = regexp.MustCompile("^https?://")
@ -52,6 +56,16 @@ func Build(ctx context.Context, c client.Client) (*client.Result, error) {
marshalOpts := []llb.ConstraintsOpt{llb.WithCaps(caps)}
localNameContext := DefaultLocalNameContext
if v, ok := opts[keyNameContext]; ok {
localNameContext = v
}
localNameDockerfile := DefaultLocalNameDockerfile
if v, ok := opts[keyNameDockerfile]; ok {
localNameDockerfile = v
}
defaultBuildPlatform := platforms.DefaultSpec()
if workers := c.BuildOpts().Workers; len(workers) > 0 && len(workers[0].Platforms) > 0 {
defaultBuildPlatform = workers[0].Platforms[0]
@ -98,19 +112,19 @@ func Build(ctx context.Context, c client.Client) (*client.Result, error) {
name := "load build definition from " + filename
src := llb.Local(LocalNameDockerfile,
src := llb.Local(localNameDockerfile,
llb.FollowPaths([]string{filename}),
llb.SessionID(c.BuildOpts().SessionID),
llb.SharedKeyHint(defaultDockerfileName),
llb.SharedKeyHint(localNameDockerfile),
dockerfile2llb.WithInternalName(name),
)
var buildContext *llb.State
isScratchContext := false
if st, ok := detectGitContext(opts[LocalNameContext]); ok {
if st, ok := detectGitContext(opts[localNameContext]); ok {
src = *st
buildContext = &src
} else if httpPrefix.MatchString(opts[LocalNameContext]) {
httpContext := llb.HTTP(opts[LocalNameContext], llb.Filename("context"), dockerfile2llb.WithInternalName("load remote build context"))
} else if httpPrefix.MatchString(opts[localNameContext]) {
httpContext := llb.HTTP(opts[localNameContext], llb.Filename("context"), dockerfile2llb.WithInternalName("load remote build context"))
def, err := httpContext.Marshal(marshalOpts...)
if err != nil {
return nil, errors.Wrapf(err, "failed to marshal httpcontext")
@ -187,10 +201,10 @@ func Build(ctx context.Context, c client.Client) (*client.Result, error) {
eg.Go(func() error {
dockerignoreState := buildContext
if dockerignoreState == nil {
st := llb.Local(LocalNameContext,
st := llb.Local(localNameContext,
llb.SessionID(c.BuildOpts().SessionID),
llb.FollowPaths([]string{dockerignoreFilename}),
llb.SharedKeyHint(dockerignoreFilename),
llb.SharedKeyHint(localNameContext+"-"+dockerignoreFilename),
dockerfile2llb.WithInternalName("load "+dockerignoreFilename),
)
dockerignoreState = &st
@ -289,14 +303,35 @@ func Build(ctx context.Context, c client.Client) (*client.Result, error) {
return errors.Wrapf(err, "failed to marshal image config")
}
var cacheFrom []string
var cacheImports []client.CacheOptionsEntry
// new API
if cacheImportsStr := opts[keyCacheImports]; cacheImportsStr != "" {
var cacheImportsUM []controlapi.CacheOptionsEntry
if err := json.Unmarshal([]byte(cacheImportsStr), &cacheImportsUM); err != nil {
return errors.Wrapf(err, "failed to unmarshal %s (%q)", keyCacheImports, cacheImportsStr)
}
for _, um := range cacheImportsUM {
cacheImports = append(cacheImports, client.CacheOptionsEntry{Type: um.Type, Attrs: um.Attrs})
}
}
// old API
if cacheFromStr := opts[keyCacheFrom]; cacheFromStr != "" {
cacheFrom = strings.Split(cacheFromStr, ",")
cacheFrom := strings.Split(cacheFromStr, ",")
for _, s := range cacheFrom {
im := client.CacheOptionsEntry{
Type: "registry",
Attrs: map[string]string{
"ref": s,
},
}
// FIXME(AkihiroSuda): skip append if already exists
cacheImports = append(cacheImports, im)
}
}
r, err := c.Solve(ctx, client.SolveRequest{
Definition: def.ToPB(),
ImportCacheRefs: cacheFrom,
Definition: def.ToPB(),
CacheImports: cacheImports,
})
if err != nil {
return err

View file

@ -31,9 +31,9 @@ import (
)
const (
emptyImageName = "scratch"
localNameContext = "context"
historyComment = "buildkit.dockerfile.v0"
emptyImageName = "scratch"
defaultContextLocalName = "context"
historyComment = "buildkit.dockerfile.v0"
DefaultCopyImage = "docker/dockerfile-copy:v0.1.9@sha256:e8f159d3f00786604b93c675ee2783f8dc194bb565e61ca5788f6a6e9d304061"
)
@ -59,6 +59,7 @@ type ConvertOpt struct {
ForceNetMode pb.NetMode
OverrideCopyImage string
LLBCaps *apicaps.CapSet
ContextLocalName string
}
func Dockerfile2LLB(ctx context.Context, dt []byte, opt ConvertOpt) (*llb.State, *Image, error) {
@ -66,6 +67,10 @@ func Dockerfile2LLB(ctx context.Context, dt []byte, opt ConvertOpt) (*llb.State,
return nil, nil, errors.Errorf("the Dockerfile cannot be empty")
}
if opt.ContextLocalName == "" {
opt.ContextLocalName = defaultContextLocalName
}
platformOpt := buildPlatformOpt(&opt)
optMetaArgs := getPlatformArgs(platformOpt)
@ -357,14 +362,14 @@ func Dockerfile2LLB(ctx context.Context, dt []byte, opt ConvertOpt) (*llb.State,
opts := []llb.LocalOption{
llb.SessionID(opt.SessionID),
llb.ExcludePatterns(opt.Excludes),
llb.SharedKeyHint(localNameContext),
llb.SharedKeyHint(opt.ContextLocalName),
WithInternalName("load build context"),
}
if includePatterns := normalizeContextPaths(ctxPaths); includePatterns != nil {
opts = append(opts, llb.FollowPaths(includePatterns))
}
bc := llb.Local(localNameContext, opts...)
bc := llb.Local(opt.ContextLocalName, opts...)
if opt.BuildContext != nil {
bc = *opt.BuildContext
}
@ -1138,8 +1143,8 @@ func autoDetectPlatform(img Image, target specs.Platform, supported []specs.Plat
return target
}
func WithInternalName(name string, a ...interface{}) llb.ConstraintsOpt {
return llb.WithCustomName("[internal] "+name, a...)
func WithInternalName(name string) llb.ConstraintsOpt {
return llb.WithCustomName("[internal] " + name)
}
func uppercaseCmd(str string) string {

View file

@ -23,6 +23,8 @@ type FrontendLLBBridge interface {
type SolveRequest = gw.SolveRequest
type CacheOptionsEntry = gw.CacheOptionsEntry
type WorkerInfos interface {
WorkerInfos() []client.WorkerInfo
}

View file

@ -43,10 +43,15 @@ type StatRequest struct {
// SolveRequest is same as frontend.SolveRequest but avoiding dependency
type SolveRequest struct {
Definition *pb.Definition
Frontend string
FrontendOpt map[string]string
ImportCacheRefs []string
Definition *pb.Definition
Frontend string
FrontendOpt map[string]string
CacheImports []CacheOptionsEntry
}
type CacheOptionsEntry struct {
Type string
Attrs map[string]string
}
type WorkerInfo struct {

View file

@ -42,10 +42,10 @@ type bridgeClient struct {
func (c *bridgeClient) Solve(ctx context.Context, req client.SolveRequest) (*client.Result, error) {
res, err := c.FrontendLLBBridge.Solve(ctx, frontend.SolveRequest{
Definition: req.Definition,
Frontend: req.Frontend,
FrontendOpt: req.FrontendOpt,
ImportCacheRefs: req.ImportCacheRefs,
Definition: req.Definition,
Frontend: req.Frontend,
FrontendOpt: req.FrontendOpt,
CacheImports: req.CacheImports,
})
if err != nil {
return nil, err

View file

@ -7,6 +7,7 @@ import (
"io"
"net"
"os"
"strconv"
"strings"
"sync"
"time"
@ -193,12 +194,20 @@ func (gf *gatewayFrontend) Solve(ctx context.Context, llbBridge frontend.Fronten
env = append(env, "BUILDKIT_EXPORTEDPRODUCT="+apicaps.ExportedProduct)
err = llbBridge.Exec(ctx, executor.Meta{
meta := executor.Meta{
Env: env,
Args: args,
Cwd: cwd,
ReadonlyRootFS: readonly,
}, rootFS, lbf.Stdin, lbf.Stdout, os.Stderr)
}
if v, ok := img.Config.Labels["moby.buildkit.frontend.network.none"]; ok {
if ok, _ := strconv.ParseBool(v); ok {
meta.NetMode = opspb.NetMode_NONE
}
}
err = llbBridge.Exec(ctx, meta, rootFS, lbf.Stdin, lbf.Stdout, os.Stderr)
if err != nil {
// An existing error (set via Return rpc) takes
@ -397,13 +406,37 @@ func (lbf *llbBridgeForwarder) ResolveImageConfig(ctx context.Context, req *pb.R
}, nil
}
func translateLegacySolveRequest(req *pb.SolveRequest) error {
// translates ImportCacheRefs to new CacheImports (v0.4.0)
for _, legacyImportRef := range req.ImportCacheRefsDeprecated {
im := &pb.CacheOptionsEntry{
Type: "registry",
Attrs: map[string]string{"ref": legacyImportRef},
}
// FIXME(AkihiroSuda): skip append if already exists
req.CacheImports = append(req.CacheImports, im)
}
req.ImportCacheRefsDeprecated = nil
return nil
}
func (lbf *llbBridgeForwarder) Solve(ctx context.Context, req *pb.SolveRequest) (*pb.SolveResponse, error) {
if err := translateLegacySolveRequest(req); err != nil {
return nil, err
}
var cacheImports []frontend.CacheOptionsEntry
for _, e := range req.CacheImports {
cacheImports = append(cacheImports, frontend.CacheOptionsEntry{
Type: e.Type,
Attrs: e.Attrs,
})
}
ctx = tracing.ContextWithSpanFromContext(ctx, lbf.callCtx)
res, err := lbf.llbBridge.Solve(ctx, frontend.SolveRequest{
Definition: req.Definition,
Frontend: req.Frontend,
FrontendOpt: req.FrontendOpt,
ImportCacheRefs: req.ImportCacheRefs,
Definition: req.Definition,
Frontend: req.Frontend,
FrontendOpt: req.FrontendOpt,
CacheImports: cacheImports,
})
if err != nil {
return nil, err

View file

@ -259,13 +259,33 @@ func (c *grpcClient) Solve(ctx context.Context, creq client.SolveRequest) (*clie
}
}
}
var (
// old API
legacyRegistryCacheImports []string
// new API (CapImportCaches)
cacheImports []*pb.CacheOptionsEntry
)
supportCapImportCaches := c.caps.Supports(pb.CapImportCaches) == nil
for _, im := range creq.CacheImports {
if !supportCapImportCaches && im.Type == "registry" {
legacyRegistryCacheImports = append(legacyRegistryCacheImports, im.Attrs["ref"])
} else {
cacheImports = append(cacheImports, &pb.CacheOptionsEntry{
Type: im.Type,
Attrs: im.Attrs,
})
}
}
req := &pb.SolveRequest{
Definition: creq.Definition,
Frontend: creq.Frontend,
FrontendOpt: creq.FrontendOpt,
ImportCacheRefs: creq.ImportCacheRefs,
AllowResultReturn: true,
// old API
ImportCacheRefsDeprecated: legacyRegistryCacheImports,
// new API
CacheImports: cacheImports,
}
// backwards compatibility with inline return

View file

@ -18,6 +18,7 @@ const (
CapReturnMap apicaps.CapID = "returnmap"
CapReadDir apicaps.CapID = "readdir"
CapStatFile apicaps.CapID = "statfile"
CapImportCaches apicaps.CapID = "importcaches"
)
func init() {
@ -84,4 +85,11 @@ func init() {
Enabled: true,
Status: apicaps.CapStatusExperimental,
})
Caps.Init(apicaps.Cap{
ID: CapImportCaches,
Name: "import caches",
Enabled: true,
Status: apicaps.CapStatusExperimental,
})
}

File diff suppressed because it is too large Load diff

View file

@ -64,12 +64,25 @@ message SolveRequest {
pb.Definition Definition = 1;
string Frontend = 2;
map<string, string> FrontendOpt = 3;
repeated string ImportCacheRefs = 4;
// ImportCacheRefsDeprecated is deprecated in favor or the new Imports since BuildKit v0.4.0.
// When ImportCacheRefsDeprecated is set, the solver appends
// {.Type = "registry", .Attrs = {"ref": importCacheRef}}
// for each of the ImportCacheRefs entry to CacheImports for compatibility. (planned to be removed)
repeated string ImportCacheRefsDeprecated = 4;
bool allowResultReturn = 5;
// apicaps.CapSolveInlineReturn deprecated
bool Final = 10;
bytes ExporterAttr = 11;
// CacheImports was added in BuildKit v0.4.0.
// apicaps:CapImportCaches
repeated CacheOptionsEntry CacheImports = 12;
}
// CacheOptionsEntry corresponds to the control.CacheOptionsEntry
message CacheOptionsEntry {
string Type = 1;
map<string, string> Attrs = 2;
}
message SolveResponse {
@ -122,4 +135,4 @@ message PongResponse{
repeated moby.buildkit.v1.apicaps.APICap FrontendAPICaps = 1 [(gogoproto.nullable) = false];
repeated moby.buildkit.v1.apicaps.APICap LLBCaps = 2 [(gogoproto.nullable) = false];
repeated moby.buildkit.v1.types.WorkerRecord Workers = 3;
}
}

View file

@ -1,16 +1,6 @@
// Code generated by protoc-gen-gogo. DO NOT EDIT.
// source: auth.proto
/*
Package auth is a generated protocol buffer package.
It is generated from these files:
auth.proto
It has these top-level messages:
CredentialsRequest
CredentialsResponse
*/
package auth
import proto "github.com/gogo/protobuf/proto"
@ -20,8 +10,10 @@ import math "math"
import strings "strings"
import reflect "reflect"
import context "golang.org/x/net/context"
import grpc "google.golang.org/grpc"
import (
context "golang.org/x/net/context"
grpc "google.golang.org/grpc"
)
import io "io"
@ -40,9 +32,37 @@ type CredentialsRequest struct {
Host string `protobuf:"bytes,1,opt,name=Host,proto3" json:"Host,omitempty"`
}
func (m *CredentialsRequest) Reset() { *m = CredentialsRequest{} }
func (*CredentialsRequest) ProtoMessage() {}
func (*CredentialsRequest) Descriptor() ([]byte, []int) { return fileDescriptorAuth, []int{0} }
func (m *CredentialsRequest) Reset() { *m = CredentialsRequest{} }
func (*CredentialsRequest) ProtoMessage() {}
func (*CredentialsRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_auth_0215b2f0213c0d57, []int{0}
}
func (m *CredentialsRequest) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
}
func (m *CredentialsRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
if deterministic {
return xxx_messageInfo_CredentialsRequest.Marshal(b, m, deterministic)
} else {
b = b[:cap(b)]
n, err := m.MarshalTo(b)
if err != nil {
return nil, err
}
return b[:n], nil
}
}
func (dst *CredentialsRequest) XXX_Merge(src proto.Message) {
xxx_messageInfo_CredentialsRequest.Merge(dst, src)
}
func (m *CredentialsRequest) XXX_Size() int {
return m.Size()
}
func (m *CredentialsRequest) XXX_DiscardUnknown() {
xxx_messageInfo_CredentialsRequest.DiscardUnknown(m)
}
var xxx_messageInfo_CredentialsRequest proto.InternalMessageInfo
func (m *CredentialsRequest) GetHost() string {
if m != nil {
@ -56,9 +76,37 @@ type CredentialsResponse struct {
Secret string `protobuf:"bytes,2,opt,name=Secret,proto3" json:"Secret,omitempty"`
}
func (m *CredentialsResponse) Reset() { *m = CredentialsResponse{} }
func (*CredentialsResponse) ProtoMessage() {}
func (*CredentialsResponse) Descriptor() ([]byte, []int) { return fileDescriptorAuth, []int{1} }
func (m *CredentialsResponse) Reset() { *m = CredentialsResponse{} }
func (*CredentialsResponse) ProtoMessage() {}
func (*CredentialsResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_auth_0215b2f0213c0d57, []int{1}
}
func (m *CredentialsResponse) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
}
func (m *CredentialsResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
if deterministic {
return xxx_messageInfo_CredentialsResponse.Marshal(b, m, deterministic)
} else {
b = b[:cap(b)]
n, err := m.MarshalTo(b)
if err != nil {
return nil, err
}
return b[:n], nil
}
}
func (dst *CredentialsResponse) XXX_Merge(src proto.Message) {
xxx_messageInfo_CredentialsResponse.Merge(dst, src)
}
func (m *CredentialsResponse) XXX_Size() int {
return m.Size()
}
func (m *CredentialsResponse) XXX_DiscardUnknown() {
xxx_messageInfo_CredentialsResponse.DiscardUnknown(m)
}
var xxx_messageInfo_CredentialsResponse proto.InternalMessageInfo
func (m *CredentialsResponse) GetUsername() string {
if m != nil {
@ -167,8 +215,9 @@ var _ grpc.ClientConn
// is compatible with the grpc package it is being compiled against.
const _ = grpc.SupportPackageIsVersion4
// Client API for Auth service
// AuthClient is the client API for Auth service.
//
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream.
type AuthClient interface {
Credentials(ctx context.Context, in *CredentialsRequest, opts ...grpc.CallOption) (*CredentialsResponse, error)
}
@ -183,15 +232,14 @@ func NewAuthClient(cc *grpc.ClientConn) AuthClient {
func (c *authClient) Credentials(ctx context.Context, in *CredentialsRequest, opts ...grpc.CallOption) (*CredentialsResponse, error) {
out := new(CredentialsResponse)
err := grpc.Invoke(ctx, "/moby.filesync.v1.Auth/Credentials", in, out, c.cc, opts...)
err := c.cc.Invoke(ctx, "/moby.filesync.v1.Auth/Credentials", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
// Server API for Auth service
// AuthServer is the server API for Auth service.
type AuthServer interface {
Credentials(context.Context, *CredentialsRequest) (*CredentialsResponse, error)
}
@ -295,6 +343,9 @@ func encodeVarintAuth(dAtA []byte, offset int, v uint64) int {
return offset + 1
}
func (m *CredentialsRequest) Size() (n int) {
if m == nil {
return 0
}
var l int
_ = l
l = len(m.Host)
@ -305,6 +356,9 @@ func (m *CredentialsRequest) Size() (n int) {
}
func (m *CredentialsResponse) Size() (n int) {
if m == nil {
return 0
}
var l int
_ = l
l = len(m.Username)
@ -652,10 +706,10 @@ var (
ErrIntOverflowAuth = fmt.Errorf("proto: integer overflow")
)
func init() { proto.RegisterFile("auth.proto", fileDescriptorAuth) }
func init() { proto.RegisterFile("auth.proto", fileDescriptor_auth_0215b2f0213c0d57) }
var fileDescriptorAuth = []byte{
// 224 bytes of a gzipped FileDescriptorProto
var fileDescriptor_auth_0215b2f0213c0d57 = []byte{
// 233 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0xe2, 0x4a, 0x2c, 0x2d, 0xc9,
0xd0, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0x12, 0xc8, 0xcd, 0x4f, 0xaa, 0xd4, 0x4b, 0xcb, 0xcc,
0x49, 0x2d, 0xae, 0xcc, 0x4b, 0xd6, 0x2b, 0x33, 0x54, 0xd2, 0xe0, 0x12, 0x72, 0x2e, 0x4a, 0x4d,
@ -665,9 +719,10 @@ var fileDescriptorAuth = []byte{
0x16, 0xa7, 0x16, 0xe5, 0x25, 0xe6, 0xa6, 0x42, 0x95, 0xc3, 0xf9, 0x42, 0x62, 0x5c, 0x6c, 0xc1,
0xa9, 0xc9, 0x45, 0xa9, 0x25, 0x12, 0x4c, 0x60, 0x19, 0x28, 0xcf, 0x28, 0x89, 0x8b, 0xc5, 0xb1,
0xb4, 0x24, 0x43, 0x28, 0x8a, 0x8b, 0x1b, 0xc9, 0x48, 0x21, 0x15, 0x3d, 0x74, 0xe7, 0xe9, 0x61,
0xba, 0x4d, 0x4a, 0x95, 0x80, 0x2a, 0x88, 0xbb, 0x9c, 0x8c, 0x2e, 0x3c, 0x94, 0x63, 0xb8, 0xf1,
0xba, 0x4d, 0x4a, 0x95, 0x80, 0x2a, 0x88, 0xbb, 0x9c, 0xac, 0x2e, 0x3c, 0x94, 0x63, 0xb8, 0xf1,
0x50, 0x8e, 0xe1, 0xc3, 0x43, 0x39, 0xc6, 0x86, 0x47, 0x72, 0x8c, 0x2b, 0x1e, 0xc9, 0x31, 0x9e,
0x78, 0x24, 0xc7, 0x78, 0xe1, 0x91, 0x1c, 0xe3, 0x83, 0x47, 0x72, 0x8c, 0x2f, 0x1e, 0xc9, 0x31,
0x7c, 0x78, 0x24, 0xc7, 0x38, 0xe1, 0xb1, 0x1c, 0x43, 0x14, 0x0b, 0x28, 0x90, 0x92, 0xd8, 0xc0,
0xa1, 0x64, 0x0c, 0x08, 0x00, 0x00, 0xff, 0xff, 0xaa, 0x73, 0xf3, 0xd5, 0x33, 0x01, 0x00, 0x00,
0x7c, 0x78, 0x24, 0xc7, 0x38, 0xe1, 0xb1, 0x1c, 0xc3, 0x85, 0xc7, 0x72, 0x0c, 0x37, 0x1e, 0xcb,
0x31, 0x44, 0xb1, 0x80, 0x02, 0x2b, 0x89, 0x0d, 0x1c, 0x5a, 0xc6, 0x80, 0x00, 0x00, 0x00, 0xff,
0xff, 0x64, 0x61, 0x71, 0x59, 0x3b, 0x01, 0x00, 0x00,
}

View file

@ -0,0 +1,132 @@
package content
import (
"context"
api "github.com/containerd/containerd/api/services/content/v1"
"github.com/containerd/containerd/content"
"github.com/containerd/containerd/errdefs"
contentservice "github.com/containerd/containerd/services/content"
"github.com/moby/buildkit/session"
digest "github.com/opencontainers/go-digest"
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
"github.com/pkg/errors"
"google.golang.org/grpc"
"google.golang.org/grpc/metadata"
)
// GRPCHeaderID is a gRPC header for store ID
const GRPCHeaderID = "buildkit-attachable-store-id"
type attachableContentStore struct {
stores map[string]content.Store
}
func (cs *attachableContentStore) choose(ctx context.Context) (content.Store, error) {
md, ok := metadata.FromIncomingContext(ctx)
if !ok {
return nil, errors.Wrap(errdefs.ErrInvalidArgument, "request lacks metadata")
}
values := md[GRPCHeaderID]
if len(values) == 0 {
return nil, errors.Wrapf(errdefs.ErrInvalidArgument, "request lacks metadata %q", GRPCHeaderID)
}
id := values[0]
store, ok := cs.stores[id]
if !ok {
return nil, errors.Wrapf(errdefs.ErrNotFound, "unknown store %s", id)
}
return store, nil
}
func (cs *attachableContentStore) Info(ctx context.Context, dgst digest.Digest) (content.Info, error) {
store, err := cs.choose(ctx)
if err != nil {
return content.Info{}, err
}
return store.Info(ctx, dgst)
}
func (cs *attachableContentStore) Update(ctx context.Context, info content.Info, fieldpaths ...string) (content.Info, error) {
store, err := cs.choose(ctx)
if err != nil {
return content.Info{}, err
}
return store.Update(ctx, info, fieldpaths...)
}
func (cs *attachableContentStore) Walk(ctx context.Context, fn content.WalkFunc, fs ...string) error {
store, err := cs.choose(ctx)
if err != nil {
return err
}
return store.Walk(ctx, fn, fs...)
}
func (cs *attachableContentStore) Delete(ctx context.Context, dgst digest.Digest) error {
store, err := cs.choose(ctx)
if err != nil {
return err
}
return store.Delete(ctx, dgst)
}
func (cs *attachableContentStore) ListStatuses(ctx context.Context, fs ...string) ([]content.Status, error) {
store, err := cs.choose(ctx)
if err != nil {
return nil, err
}
return store.ListStatuses(ctx, fs...)
}
func (cs *attachableContentStore) Status(ctx context.Context, ref string) (content.Status, error) {
store, err := cs.choose(ctx)
if err != nil {
return content.Status{}, err
}
return store.Status(ctx, ref)
}
func (cs *attachableContentStore) Abort(ctx context.Context, ref string) error {
store, err := cs.choose(ctx)
if err != nil {
return err
}
return store.Abort(ctx, ref)
}
func (cs *attachableContentStore) Writer(ctx context.Context, opts ...content.WriterOpt) (content.Writer, error) {
store, err := cs.choose(ctx)
if err != nil {
return nil, err
}
return store.Writer(ctx, opts...)
}
func (cs *attachableContentStore) ReaderAt(ctx context.Context, desc ocispec.Descriptor) (content.ReaderAt, error) {
store, err := cs.choose(ctx)
if err != nil {
return nil, err
}
return store.ReaderAt(ctx, desc)
}
type attachable struct {
service api.ContentServer
}
// NewAttachable creates session.Attachable from aggregated stores.
// A key of the store map is an ID string that is used for choosing underlying store.
func NewAttachable(stores map[string]content.Store) session.Attachable {
store := &attachableContentStore{stores: stores}
service := contentservice.NewService(store)
a := attachable{
service: service,
}
return &a
}
func (a *attachable) Register(server *grpc.Server) {
api.RegisterContentServer(server, a.service)
}

View file

@ -0,0 +1,84 @@
package content
import (
"context"
api "github.com/containerd/containerd/api/services/content/v1"
"github.com/containerd/containerd/content"
"github.com/containerd/containerd/content/proxy"
"github.com/moby/buildkit/session"
digest "github.com/opencontainers/go-digest"
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
"google.golang.org/grpc/metadata"
)
type callerContentStore struct {
store content.Store
storeID string
}
func (cs *callerContentStore) choose(ctx context.Context) context.Context {
nsheader := metadata.Pairs(GRPCHeaderID, cs.storeID)
md, ok := metadata.FromOutgoingContext(ctx) // merge with outgoing context.
if !ok {
md = nsheader
} else {
// order ensures the latest is first in this list.
md = metadata.Join(nsheader, md)
}
return metadata.NewOutgoingContext(ctx, md)
}
func (cs *callerContentStore) Info(ctx context.Context, dgst digest.Digest) (content.Info, error) {
ctx = cs.choose(ctx)
return cs.store.Info(ctx, dgst)
}
func (cs *callerContentStore) Update(ctx context.Context, info content.Info, fieldpaths ...string) (content.Info, error) {
ctx = cs.choose(ctx)
return cs.store.Update(ctx, info, fieldpaths...)
}
func (cs *callerContentStore) Walk(ctx context.Context, fn content.WalkFunc, fs ...string) error {
ctx = cs.choose(ctx)
return cs.store.Walk(ctx, fn, fs...)
}
func (cs *callerContentStore) Delete(ctx context.Context, dgst digest.Digest) error {
ctx = cs.choose(ctx)
return cs.store.Delete(ctx, dgst)
}
func (cs *callerContentStore) ListStatuses(ctx context.Context, fs ...string) ([]content.Status, error) {
ctx = cs.choose(ctx)
return cs.store.ListStatuses(ctx, fs...)
}
func (cs *callerContentStore) Status(ctx context.Context, ref string) (content.Status, error) {
ctx = cs.choose(ctx)
return cs.store.Status(ctx, ref)
}
func (cs *callerContentStore) Abort(ctx context.Context, ref string) error {
ctx = cs.choose(ctx)
return cs.store.Abort(ctx, ref)
}
func (cs *callerContentStore) Writer(ctx context.Context, opts ...content.WriterOpt) (content.Writer, error) {
ctx = cs.choose(ctx)
return cs.store.Writer(ctx, opts...)
}
func (cs *callerContentStore) ReaderAt(ctx context.Context, desc ocispec.Descriptor) (content.ReaderAt, error) {
ctx = cs.choose(ctx)
return cs.store.ReaderAt(ctx, desc)
}
// NewCallerStore creates content.Store from session.Caller with specified storeID
func NewCallerStore(c session.Caller, storeID string) content.Store {
client := api.NewContentClient(c.Conn())
return &callerContentStore{
store: proxy.NewContentStore(client),
storeID: storeID,
}
}

View file

@ -1,15 +1,6 @@
// Code generated by protoc-gen-gogo. DO NOT EDIT.
// source: filesync.proto
/*
Package filesync is a generated protocol buffer package.
It is generated from these files:
filesync.proto
It has these top-level messages:
BytesMessage
*/
package filesync
import proto "github.com/gogo/protobuf/proto"
@ -21,8 +12,10 @@ import bytes "bytes"
import strings "strings"
import reflect "reflect"
import context "golang.org/x/net/context"
import grpc "google.golang.org/grpc"
import (
context "golang.org/x/net/context"
grpc "google.golang.org/grpc"
)
import io "io"
@ -42,9 +35,37 @@ type BytesMessage struct {
Data []byte `protobuf:"bytes,1,opt,name=data,proto3" json:"data,omitempty"`
}
func (m *BytesMessage) Reset() { *m = BytesMessage{} }
func (*BytesMessage) ProtoMessage() {}
func (*BytesMessage) Descriptor() ([]byte, []int) { return fileDescriptorFilesync, []int{0} }
func (m *BytesMessage) Reset() { *m = BytesMessage{} }
func (*BytesMessage) ProtoMessage() {}
func (*BytesMessage) Descriptor() ([]byte, []int) {
return fileDescriptor_filesync_26f8b7bce2e5ac0e, []int{0}
}
func (m *BytesMessage) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
}
func (m *BytesMessage) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
if deterministic {
return xxx_messageInfo_BytesMessage.Marshal(b, m, deterministic)
} else {
b = b[:cap(b)]
n, err := m.MarshalTo(b)
if err != nil {
return nil, err
}
return b[:n], nil
}
}
func (dst *BytesMessage) XXX_Merge(src proto.Message) {
xxx_messageInfo_BytesMessage.Merge(dst, src)
}
func (m *BytesMessage) XXX_Size() int {
return m.Size()
}
func (m *BytesMessage) XXX_DiscardUnknown() {
xxx_messageInfo_BytesMessage.DiscardUnknown(m)
}
var xxx_messageInfo_BytesMessage proto.InternalMessageInfo
func (m *BytesMessage) GetData() []byte {
if m != nil {
@ -107,8 +128,9 @@ var _ grpc.ClientConn
// is compatible with the grpc package it is being compiled against.
const _ = grpc.SupportPackageIsVersion4
// Client API for FileSync service
// FileSyncClient is the client API for FileSync service.
//
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream.
type FileSyncClient interface {
DiffCopy(ctx context.Context, opts ...grpc.CallOption) (FileSync_DiffCopyClient, error)
TarStream(ctx context.Context, opts ...grpc.CallOption) (FileSync_TarStreamClient, error)
@ -123,7 +145,7 @@ func NewFileSyncClient(cc *grpc.ClientConn) FileSyncClient {
}
func (c *fileSyncClient) DiffCopy(ctx context.Context, opts ...grpc.CallOption) (FileSync_DiffCopyClient, error) {
stream, err := grpc.NewClientStream(ctx, &_FileSync_serviceDesc.Streams[0], c.cc, "/moby.filesync.v1.FileSync/DiffCopy", opts...)
stream, err := c.cc.NewStream(ctx, &_FileSync_serviceDesc.Streams[0], "/moby.filesync.v1.FileSync/DiffCopy", opts...)
if err != nil {
return nil, err
}
@ -154,7 +176,7 @@ func (x *fileSyncDiffCopyClient) Recv() (*BytesMessage, error) {
}
func (c *fileSyncClient) TarStream(ctx context.Context, opts ...grpc.CallOption) (FileSync_TarStreamClient, error) {
stream, err := grpc.NewClientStream(ctx, &_FileSync_serviceDesc.Streams[1], c.cc, "/moby.filesync.v1.FileSync/TarStream", opts...)
stream, err := c.cc.NewStream(ctx, &_FileSync_serviceDesc.Streams[1], "/moby.filesync.v1.FileSync/TarStream", opts...)
if err != nil {
return nil, err
}
@ -184,8 +206,7 @@ func (x *fileSyncTarStreamClient) Recv() (*BytesMessage, error) {
return m, nil
}
// Server API for FileSync service
// FileSyncServer is the server API for FileSync service.
type FileSyncServer interface {
DiffCopy(FileSync_DiffCopyServer) error
TarStream(FileSync_TarStreamServer) error
@ -268,8 +289,9 @@ var _FileSync_serviceDesc = grpc.ServiceDesc{
Metadata: "filesync.proto",
}
// Client API for FileSend service
// FileSendClient is the client API for FileSend service.
//
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream.
type FileSendClient interface {
DiffCopy(ctx context.Context, opts ...grpc.CallOption) (FileSend_DiffCopyClient, error)
}
@ -283,7 +305,7 @@ func NewFileSendClient(cc *grpc.ClientConn) FileSendClient {
}
func (c *fileSendClient) DiffCopy(ctx context.Context, opts ...grpc.CallOption) (FileSend_DiffCopyClient, error) {
stream, err := grpc.NewClientStream(ctx, &_FileSend_serviceDesc.Streams[0], c.cc, "/moby.filesync.v1.FileSend/DiffCopy", opts...)
stream, err := c.cc.NewStream(ctx, &_FileSend_serviceDesc.Streams[0], "/moby.filesync.v1.FileSend/DiffCopy", opts...)
if err != nil {
return nil, err
}
@ -313,8 +335,7 @@ func (x *fileSendDiffCopyClient) Recv() (*BytesMessage, error) {
return m, nil
}
// Server API for FileSend service
// FileSendServer is the server API for FileSend service.
type FileSendServer interface {
DiffCopy(FileSend_DiffCopyServer) error
}
@ -398,6 +419,9 @@ func encodeVarintFilesync(dAtA []byte, offset int, v uint64) int {
return offset + 1
}
func (m *BytesMessage) Size() (n int) {
if m == nil {
return 0
}
var l int
_ = l
l = len(m.Data)
@ -624,10 +648,10 @@ var (
ErrIntOverflowFilesync = fmt.Errorf("proto: integer overflow")
)
func init() { proto.RegisterFile("filesync.proto", fileDescriptorFilesync) }
func init() { proto.RegisterFile("filesync.proto", fileDescriptor_filesync_26f8b7bce2e5ac0e) }
var fileDescriptorFilesync = []byte{
// 208 bytes of a gzipped FileDescriptorProto
var fileDescriptor_filesync_26f8b7bce2e5ac0e = []byte{
// 217 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0xe2, 0x4b, 0xcb, 0xcc, 0x49,
0x2d, 0xae, 0xcc, 0x4b, 0xd6, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0x12, 0xc8, 0xcd, 0x4f, 0xaa,
0xd4, 0x83, 0x0b, 0x96, 0x19, 0x2a, 0x29, 0x71, 0xf1, 0x38, 0x55, 0x96, 0xa4, 0x16, 0xfb, 0xa6,
@ -636,9 +660,10 @@ var fileDescriptorFilesync = []byte{
0x2b, 0xf3, 0x92, 0x85, 0xfc, 0xb8, 0x38, 0x5c, 0x32, 0xd3, 0xd2, 0x9c, 0xf3, 0x0b, 0x2a, 0x85,
0xe4, 0xf4, 0xd0, 0xcd, 0xd3, 0x43, 0x36, 0x4c, 0x8a, 0x80, 0xbc, 0x06, 0xa3, 0x01, 0xa3, 0x90,
0x3f, 0x17, 0x67, 0x48, 0x62, 0x51, 0x70, 0x49, 0x51, 0x6a, 0x62, 0x2e, 0x35, 0x0c, 0x34, 0x8a,
0x82, 0x3a, 0x36, 0x35, 0x2f, 0x85, 0xda, 0x8e, 0x75, 0x32, 0xbb, 0xf0, 0x50, 0x8e, 0xe1, 0xc6,
0x82, 0x3a, 0x36, 0x35, 0x2f, 0x85, 0xda, 0x8e, 0x75, 0xb2, 0xbb, 0xf0, 0x50, 0x8e, 0xe1, 0xc6,
0x43, 0x39, 0x86, 0x0f, 0x0f, 0xe5, 0x18, 0x1b, 0x1e, 0xc9, 0x31, 0xae, 0x78, 0x24, 0xc7, 0x78,
0xe2, 0x91, 0x1c, 0xe3, 0x85, 0x47, 0x72, 0x8c, 0x0f, 0x1e, 0xc9, 0x31, 0xbe, 0x78, 0x24, 0xc7,
0xf0, 0xe1, 0x91, 0x1c, 0xe3, 0x84, 0xc7, 0x72, 0x0c, 0x51, 0x1c, 0x30, 0xb3, 0x92, 0xd8, 0xc0,
0xc1, 0x6f, 0x0c, 0x08, 0x00, 0x00, 0xff, 0xff, 0x72, 0x81, 0x1a, 0x91, 0x90, 0x01, 0x00, 0x00,
0xf0, 0xe1, 0x91, 0x1c, 0xe3, 0x84, 0xc7, 0x72, 0x0c, 0x17, 0x1e, 0xcb, 0x31, 0xdc, 0x78, 0x2c,
0xc7, 0x10, 0xc5, 0x01, 0x33, 0x33, 0x89, 0x0d, 0x1c, 0x0d, 0xc6, 0x80, 0x00, 0x00, 0x00, 0xff,
0xff, 0x5e, 0xce, 0x52, 0xb3, 0x98, 0x01, 0x00, 0x00,
}

View file

@ -1,16 +1,6 @@
// Code generated by protoc-gen-gogo. DO NOT EDIT.
// source: secrets.proto
/*
Package secrets is a generated protocol buffer package.
It is generated from these files:
secrets.proto
It has these top-level messages:
GetSecretRequest
GetSecretResponse
*/
package secrets
import proto "github.com/gogo/protobuf/proto"
@ -21,10 +11,12 @@ import bytes "bytes"
import strings "strings"
import reflect "reflect"
import sortkeys "github.com/gogo/protobuf/sortkeys"
import github_com_gogo_protobuf_sortkeys "github.com/gogo/protobuf/sortkeys"
import context "golang.org/x/net/context"
import grpc "google.golang.org/grpc"
import (
context "golang.org/x/net/context"
grpc "google.golang.org/grpc"
)
import io "io"
@ -41,12 +33,40 @@ const _ = proto.GoGoProtoPackageIsVersion2 // please upgrade the proto package
type GetSecretRequest struct {
ID string `protobuf:"bytes,1,opt,name=ID,proto3" json:"ID,omitempty"`
Annotations map[string]string `protobuf:"bytes,2,rep,name=annotations" json:"annotations,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"`
Annotations map[string]string `protobuf:"bytes,2,rep,name=annotations,proto3" json:"annotations,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"`
}
func (m *GetSecretRequest) Reset() { *m = GetSecretRequest{} }
func (*GetSecretRequest) ProtoMessage() {}
func (*GetSecretRequest) Descriptor() ([]byte, []int) { return fileDescriptorSecrets, []int{0} }
func (m *GetSecretRequest) Reset() { *m = GetSecretRequest{} }
func (*GetSecretRequest) ProtoMessage() {}
func (*GetSecretRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_secrets_21bd4adec74a381e, []int{0}
}
func (m *GetSecretRequest) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
}
func (m *GetSecretRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
if deterministic {
return xxx_messageInfo_GetSecretRequest.Marshal(b, m, deterministic)
} else {
b = b[:cap(b)]
n, err := m.MarshalTo(b)
if err != nil {
return nil, err
}
return b[:n], nil
}
}
func (dst *GetSecretRequest) XXX_Merge(src proto.Message) {
xxx_messageInfo_GetSecretRequest.Merge(dst, src)
}
func (m *GetSecretRequest) XXX_Size() int {
return m.Size()
}
func (m *GetSecretRequest) XXX_DiscardUnknown() {
xxx_messageInfo_GetSecretRequest.DiscardUnknown(m)
}
var xxx_messageInfo_GetSecretRequest proto.InternalMessageInfo
func (m *GetSecretRequest) GetID() string {
if m != nil {
@ -66,9 +86,37 @@ type GetSecretResponse struct {
Data []byte `protobuf:"bytes,1,opt,name=data,proto3" json:"data,omitempty"`
}
func (m *GetSecretResponse) Reset() { *m = GetSecretResponse{} }
func (*GetSecretResponse) ProtoMessage() {}
func (*GetSecretResponse) Descriptor() ([]byte, []int) { return fileDescriptorSecrets, []int{1} }
func (m *GetSecretResponse) Reset() { *m = GetSecretResponse{} }
func (*GetSecretResponse) ProtoMessage() {}
func (*GetSecretResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_secrets_21bd4adec74a381e, []int{1}
}
func (m *GetSecretResponse) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
}
func (m *GetSecretResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
if deterministic {
return xxx_messageInfo_GetSecretResponse.Marshal(b, m, deterministic)
} else {
b = b[:cap(b)]
n, err := m.MarshalTo(b)
if err != nil {
return nil, err
}
return b[:n], nil
}
}
func (dst *GetSecretResponse) XXX_Merge(src proto.Message) {
xxx_messageInfo_GetSecretResponse.Merge(dst, src)
}
func (m *GetSecretResponse) XXX_Size() int {
return m.Size()
}
func (m *GetSecretResponse) XXX_DiscardUnknown() {
xxx_messageInfo_GetSecretResponse.DiscardUnknown(m)
}
var xxx_messageInfo_GetSecretResponse proto.InternalMessageInfo
func (m *GetSecretResponse) GetData() []byte {
if m != nil {
@ -79,6 +127,7 @@ func (m *GetSecretResponse) GetData() []byte {
func init() {
proto.RegisterType((*GetSecretRequest)(nil), "moby.buildkit.secrets.v1.GetSecretRequest")
proto.RegisterMapType((map[string]string)(nil), "moby.buildkit.secrets.v1.GetSecretRequest.AnnotationsEntry")
proto.RegisterType((*GetSecretResponse)(nil), "moby.buildkit.secrets.v1.GetSecretResponse")
}
func (this *GetSecretRequest) Equal(that interface{}) bool {
@ -148,7 +197,7 @@ func (this *GetSecretRequest) GoString() string {
for k, _ := range this.Annotations {
keysForAnnotations = append(keysForAnnotations, k)
}
sortkeys.Strings(keysForAnnotations)
github_com_gogo_protobuf_sortkeys.Strings(keysForAnnotations)
mapStringForAnnotations := "map[string]string{"
for _, k := range keysForAnnotations {
mapStringForAnnotations += fmt.Sprintf("%#v: %#v,", k, this.Annotations[k])
@ -187,8 +236,9 @@ var _ grpc.ClientConn
// is compatible with the grpc package it is being compiled against.
const _ = grpc.SupportPackageIsVersion4
// Client API for Secrets service
// SecretsClient is the client API for Secrets service.
//
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream.
type SecretsClient interface {
GetSecret(ctx context.Context, in *GetSecretRequest, opts ...grpc.CallOption) (*GetSecretResponse, error)
}
@ -203,15 +253,14 @@ func NewSecretsClient(cc *grpc.ClientConn) SecretsClient {
func (c *secretsClient) GetSecret(ctx context.Context, in *GetSecretRequest, opts ...grpc.CallOption) (*GetSecretResponse, error) {
out := new(GetSecretResponse)
err := grpc.Invoke(ctx, "/moby.buildkit.secrets.v1.Secrets/GetSecret", in, out, c.cc, opts...)
err := c.cc.Invoke(ctx, "/moby.buildkit.secrets.v1.Secrets/GetSecret", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
// Server API for Secrets service
// SecretsServer is the server API for Secrets service.
type SecretsServer interface {
GetSecret(context.Context, *GetSecretRequest) (*GetSecretResponse, error)
}
@ -326,6 +375,9 @@ func encodeVarintSecrets(dAtA []byte, offset int, v uint64) int {
return offset + 1
}
func (m *GetSecretRequest) Size() (n int) {
if m == nil {
return 0
}
var l int
_ = l
l = len(m.ID)
@ -344,6 +396,9 @@ func (m *GetSecretRequest) Size() (n int) {
}
func (m *GetSecretResponse) Size() (n int) {
if m == nil {
return 0
}
var l int
_ = l
l = len(m.Data)
@ -374,7 +429,7 @@ func (this *GetSecretRequest) String() string {
for k, _ := range this.Annotations {
keysForAnnotations = append(keysForAnnotations, k)
}
sortkeys.Strings(keysForAnnotations)
github_com_gogo_protobuf_sortkeys.Strings(keysForAnnotations)
mapStringForAnnotations := "map[string]string{"
for _, k := range keysForAnnotations {
mapStringForAnnotations += fmt.Sprintf("%v: %v,", k, this.Annotations[k])
@ -788,10 +843,10 @@ var (
ErrIntOverflowSecrets = fmt.Errorf("proto: integer overflow")
)
func init() { proto.RegisterFile("secrets.proto", fileDescriptorSecrets) }
func init() { proto.RegisterFile("secrets.proto", fileDescriptor_secrets_21bd4adec74a381e) }
var fileDescriptorSecrets = []byte{
// 279 bytes of a gzipped FileDescriptorProto
var fileDescriptor_secrets_21bd4adec74a381e = []byte{
// 288 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0xe2, 0x2d, 0x4e, 0x4d, 0x2e,
0x4a, 0x2d, 0x29, 0xd6, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0x92, 0xc8, 0xcd, 0x4f, 0xaa, 0xd4,
0x4b, 0x2a, 0xcd, 0xcc, 0x49, 0xc9, 0xce, 0x2c, 0xd1, 0x83, 0x49, 0x96, 0x19, 0x2a, 0x1d, 0x64,
@ -805,9 +860,9 @@ var fileDescriptorSecrets = []byte{
0x41, 0x24, 0x1b, 0x8b, 0x0b, 0xf2, 0xf3, 0x8a, 0x53, 0x85, 0x84, 0xb8, 0x58, 0x52, 0x12, 0x4b,
0x12, 0xc1, 0x26, 0xf0, 0x04, 0x81, 0xd9, 0x46, 0xf9, 0x5c, 0xec, 0x10, 0x55, 0xc5, 0x42, 0x29,
0x5c, 0x9c, 0x70, 0x3d, 0x42, 0x5a, 0xc4, 0x7b, 0x45, 0x4a, 0x9b, 0x28, 0xb5, 0x10, 0x47, 0x38,
0x99, 0x5e, 0x78, 0x28, 0xc7, 0x70, 0xe3, 0xa1, 0x1c, 0xc3, 0x87, 0x87, 0x72, 0x8c, 0x0d, 0x8f,
0xd9, 0x5e, 0x78, 0x28, 0xc7, 0x70, 0xe3, 0xa1, 0x1c, 0xc3, 0x87, 0x87, 0x72, 0x8c, 0x0d, 0x8f,
0xe4, 0x18, 0x57, 0x3c, 0x92, 0x63, 0x3c, 0xf1, 0x48, 0x8e, 0xf1, 0xc2, 0x23, 0x39, 0xc6, 0x07,
0x8f, 0xe4, 0x18, 0x5f, 0x3c, 0x92, 0x63, 0xf8, 0xf0, 0x48, 0x8e, 0x71, 0xc2, 0x63, 0x39, 0x86,
0x28, 0x76, 0xa8, 0x59, 0x49, 0x6c, 0xe0, 0x58, 0x33, 0x06, 0x04, 0x00, 0x00, 0xff, 0xff, 0x05,
0x4e, 0x56, 0xde, 0xc6, 0x01, 0x00, 0x00,
0x0b, 0x8f, 0xe5, 0x18, 0x6e, 0x3c, 0x96, 0x63, 0x88, 0x62, 0x87, 0x9a, 0x99, 0xc4, 0x06, 0x8e,
0x3d, 0x63, 0x40, 0x00, 0x00, 0x00, 0xff, 0xff, 0x2c, 0x38, 0xec, 0x1f, 0xce, 0x01, 0x00, 0x00,
}

View file

@ -1,17 +1,6 @@
// Code generated by protoc-gen-gogo. DO NOT EDIT.
// source: ssh.proto
/*
Package sshforward is a generated protocol buffer package.
It is generated from these files:
ssh.proto
It has these top-level messages:
BytesMessage
CheckAgentRequest
CheckAgentResponse
*/
package sshforward
import proto "github.com/gogo/protobuf/proto"
@ -23,8 +12,10 @@ import bytes "bytes"
import strings "strings"
import reflect "reflect"
import context "golang.org/x/net/context"
import grpc "google.golang.org/grpc"
import (
context "golang.org/x/net/context"
grpc "google.golang.org/grpc"
)
import io "io"
@ -44,9 +35,37 @@ type BytesMessage struct {
Data []byte `protobuf:"bytes,1,opt,name=data,proto3" json:"data,omitempty"`
}
func (m *BytesMessage) Reset() { *m = BytesMessage{} }
func (*BytesMessage) ProtoMessage() {}
func (*BytesMessage) Descriptor() ([]byte, []int) { return fileDescriptorSsh, []int{0} }
func (m *BytesMessage) Reset() { *m = BytesMessage{} }
func (*BytesMessage) ProtoMessage() {}
func (*BytesMessage) Descriptor() ([]byte, []int) {
return fileDescriptor_ssh_13bd2c34c031d472, []int{0}
}
func (m *BytesMessage) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
}
func (m *BytesMessage) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
if deterministic {
return xxx_messageInfo_BytesMessage.Marshal(b, m, deterministic)
} else {
b = b[:cap(b)]
n, err := m.MarshalTo(b)
if err != nil {
return nil, err
}
return b[:n], nil
}
}
func (dst *BytesMessage) XXX_Merge(src proto.Message) {
xxx_messageInfo_BytesMessage.Merge(dst, src)
}
func (m *BytesMessage) XXX_Size() int {
return m.Size()
}
func (m *BytesMessage) XXX_DiscardUnknown() {
xxx_messageInfo_BytesMessage.DiscardUnknown(m)
}
var xxx_messageInfo_BytesMessage proto.InternalMessageInfo
func (m *BytesMessage) GetData() []byte {
if m != nil {
@ -59,9 +78,37 @@ type CheckAgentRequest struct {
ID string `protobuf:"bytes,1,opt,name=ID,proto3" json:"ID,omitempty"`
}
func (m *CheckAgentRequest) Reset() { *m = CheckAgentRequest{} }
func (*CheckAgentRequest) ProtoMessage() {}
func (*CheckAgentRequest) Descriptor() ([]byte, []int) { return fileDescriptorSsh, []int{1} }
func (m *CheckAgentRequest) Reset() { *m = CheckAgentRequest{} }
func (*CheckAgentRequest) ProtoMessage() {}
func (*CheckAgentRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_ssh_13bd2c34c031d472, []int{1}
}
func (m *CheckAgentRequest) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
}
func (m *CheckAgentRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
if deterministic {
return xxx_messageInfo_CheckAgentRequest.Marshal(b, m, deterministic)
} else {
b = b[:cap(b)]
n, err := m.MarshalTo(b)
if err != nil {
return nil, err
}
return b[:n], nil
}
}
func (dst *CheckAgentRequest) XXX_Merge(src proto.Message) {
xxx_messageInfo_CheckAgentRequest.Merge(dst, src)
}
func (m *CheckAgentRequest) XXX_Size() int {
return m.Size()
}
func (m *CheckAgentRequest) XXX_DiscardUnknown() {
xxx_messageInfo_CheckAgentRequest.DiscardUnknown(m)
}
var xxx_messageInfo_CheckAgentRequest proto.InternalMessageInfo
func (m *CheckAgentRequest) GetID() string {
if m != nil {
@ -73,9 +120,37 @@ func (m *CheckAgentRequest) GetID() string {
type CheckAgentResponse struct {
}
func (m *CheckAgentResponse) Reset() { *m = CheckAgentResponse{} }
func (*CheckAgentResponse) ProtoMessage() {}
func (*CheckAgentResponse) Descriptor() ([]byte, []int) { return fileDescriptorSsh, []int{2} }
func (m *CheckAgentResponse) Reset() { *m = CheckAgentResponse{} }
func (*CheckAgentResponse) ProtoMessage() {}
func (*CheckAgentResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_ssh_13bd2c34c031d472, []int{2}
}
func (m *CheckAgentResponse) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
}
func (m *CheckAgentResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
if deterministic {
return xxx_messageInfo_CheckAgentResponse.Marshal(b, m, deterministic)
} else {
b = b[:cap(b)]
n, err := m.MarshalTo(b)
if err != nil {
return nil, err
}
return b[:n], nil
}
}
func (dst *CheckAgentResponse) XXX_Merge(src proto.Message) {
xxx_messageInfo_CheckAgentResponse.Merge(dst, src)
}
func (m *CheckAgentResponse) XXX_Size() int {
return m.Size()
}
func (m *CheckAgentResponse) XXX_DiscardUnknown() {
xxx_messageInfo_CheckAgentResponse.DiscardUnknown(m)
}
var xxx_messageInfo_CheckAgentResponse proto.InternalMessageInfo
func init() {
proto.RegisterType((*BytesMessage)(nil), "moby.sshforward.v1.BytesMessage")
@ -197,8 +272,9 @@ var _ grpc.ClientConn
// is compatible with the grpc package it is being compiled against.
const _ = grpc.SupportPackageIsVersion4
// Client API for SSH service
// SSHClient is the client API for SSH service.
//
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream.
type SSHClient interface {
CheckAgent(ctx context.Context, in *CheckAgentRequest, opts ...grpc.CallOption) (*CheckAgentResponse, error)
ForwardAgent(ctx context.Context, opts ...grpc.CallOption) (SSH_ForwardAgentClient, error)
@ -214,7 +290,7 @@ func NewSSHClient(cc *grpc.ClientConn) SSHClient {
func (c *sSHClient) CheckAgent(ctx context.Context, in *CheckAgentRequest, opts ...grpc.CallOption) (*CheckAgentResponse, error) {
out := new(CheckAgentResponse)
err := grpc.Invoke(ctx, "/moby.sshforward.v1.SSH/CheckAgent", in, out, c.cc, opts...)
err := c.cc.Invoke(ctx, "/moby.sshforward.v1.SSH/CheckAgent", in, out, opts...)
if err != nil {
return nil, err
}
@ -222,7 +298,7 @@ func (c *sSHClient) CheckAgent(ctx context.Context, in *CheckAgentRequest, opts
}
func (c *sSHClient) ForwardAgent(ctx context.Context, opts ...grpc.CallOption) (SSH_ForwardAgentClient, error) {
stream, err := grpc.NewClientStream(ctx, &_SSH_serviceDesc.Streams[0], c.cc, "/moby.sshforward.v1.SSH/ForwardAgent", opts...)
stream, err := c.cc.NewStream(ctx, &_SSH_serviceDesc.Streams[0], "/moby.sshforward.v1.SSH/ForwardAgent", opts...)
if err != nil {
return nil, err
}
@ -252,8 +328,7 @@ func (x *sSHForwardAgentClient) Recv() (*BytesMessage, error) {
return m, nil
}
// Server API for SSH service
// SSHServer is the server API for SSH service.
type SSHServer interface {
CheckAgent(context.Context, *CheckAgentRequest) (*CheckAgentResponse, error)
ForwardAgent(SSH_ForwardAgentServer) error
@ -403,6 +478,9 @@ func encodeVarintSsh(dAtA []byte, offset int, v uint64) int {
return offset + 1
}
func (m *BytesMessage) Size() (n int) {
if m == nil {
return 0
}
var l int
_ = l
l = len(m.Data)
@ -413,6 +491,9 @@ func (m *BytesMessage) Size() (n int) {
}
func (m *CheckAgentRequest) Size() (n int) {
if m == nil {
return 0
}
var l int
_ = l
l = len(m.ID)
@ -423,6 +504,9 @@ func (m *CheckAgentRequest) Size() (n int) {
}
func (m *CheckAgentResponse) Size() (n int) {
if m == nil {
return 0
}
var l int
_ = l
return n
@ -793,10 +877,10 @@ var (
ErrIntOverflowSsh = fmt.Errorf("proto: integer overflow")
)
func init() { proto.RegisterFile("ssh.proto", fileDescriptorSsh) }
func init() { proto.RegisterFile("ssh.proto", fileDescriptor_ssh_13bd2c34c031d472) }
var fileDescriptorSsh = []byte{
// 243 bytes of a gzipped FileDescriptorProto
var fileDescriptor_ssh_13bd2c34c031d472 = []byte{
// 252 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0xe2, 0x2c, 0x2e, 0xce, 0xd0,
0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0x12, 0xca, 0xcd, 0x4f, 0xaa, 0xd4, 0x2b, 0x2e, 0xce, 0x48,
0xcb, 0x2f, 0x2a, 0x4f, 0x2c, 0x4a, 0xd1, 0x2b, 0x33, 0x54, 0x52, 0xe2, 0xe2, 0x71, 0xaa, 0x2c,
@ -807,10 +891,10 @@ var fileDescriptorSsh = []byte{
0x90, 0x15, 0x15, 0x17, 0xe4, 0xe7, 0x15, 0xa7, 0x1a, 0xed, 0x62, 0xe4, 0x62, 0x0e, 0x0e, 0xf6,
0x10, 0x8a, 0xe6, 0xe2, 0x42, 0xc8, 0x0a, 0xa9, 0xea, 0x61, 0xba, 0x44, 0x0f, 0xc3, 0x0a, 0x29,
0x35, 0x42, 0xca, 0x20, 0x96, 0x08, 0x85, 0x71, 0xf1, 0xb8, 0x41, 0x14, 0x40, 0x8c, 0x57, 0xc0,
0xa6, 0x0f, 0xd9, 0x97, 0x52, 0x04, 0x55, 0x68, 0x30, 0x1a, 0x30, 0x3a, 0x59, 0x5c, 0x78, 0x28,
0xa6, 0x0f, 0xd9, 0x97, 0x52, 0x04, 0x55, 0x68, 0x30, 0x1a, 0x30, 0x3a, 0x39, 0x5c, 0x78, 0x28,
0xc7, 0x70, 0xe3, 0xa1, 0x1c, 0xc3, 0x87, 0x87, 0x72, 0x8c, 0x0d, 0x8f, 0xe4, 0x18, 0x57, 0x3c,
0x92, 0x63, 0x3c, 0xf1, 0x48, 0x8e, 0xf1, 0xc2, 0x23, 0x39, 0xc6, 0x07, 0x8f, 0xe4, 0x18, 0x5f,
0x3c, 0x92, 0x63, 0xf8, 0xf0, 0x48, 0x8e, 0x71, 0xc2, 0x63, 0x39, 0x86, 0x28, 0x2e, 0x84, 0x69,
0x49, 0x6c, 0xe0, 0x00, 0x37, 0x06, 0x04, 0x00, 0x00, 0xff, 0xff, 0x31, 0x3e, 0x40, 0xab, 0x7d,
0x01, 0x00, 0x00,
0x3c, 0x92, 0x63, 0xf8, 0xf0, 0x48, 0x8e, 0x71, 0xc2, 0x63, 0x39, 0x86, 0x0b, 0x8f, 0xe5, 0x18,
0x6e, 0x3c, 0x96, 0x63, 0x88, 0xe2, 0x42, 0x98, 0x9a, 0xc4, 0x06, 0x0e, 0x78, 0x63, 0x40, 0x00,
0x00, 0x00, 0xff, 0xff, 0x6c, 0xe6, 0x6d, 0xb7, 0x85, 0x01, 0x00, 0x00,
}

View file

@ -3,7 +3,9 @@ package solver
import (
"context"
"fmt"
"strings"
"sync"
"time"
"github.com/moby/buildkit/identity"
digest "github.com/opencontainers/go-digest"
@ -142,15 +144,87 @@ func (c *cacheManager) Load(ctx context.Context, rec *CacheRecord) (Result, erro
return c.results.Load(ctx, res)
}
func (c *cacheManager) Save(k *CacheKey, r Result) (*ExportableCacheKey, error) {
c.mu.Lock()
defer c.mu.Unlock()
type LoadedResult struct {
Result Result
CacheResult CacheResult
CacheKey *CacheKey
}
res, err := c.results.Save(r)
func (c *cacheManager) filterResults(m map[string]Result, ck *CacheKey) (results []LoadedResult, err error) {
id := c.getID(ck)
if err := c.backend.WalkResults(id, func(cr CacheResult) error {
res, ok := m[id]
if ok {
results = append(results, LoadedResult{
Result: res,
CacheKey: ck,
CacheResult: cr,
})
delete(m, id)
}
return nil
}); err != nil {
for _, r := range results {
r.Result.Release(context.TODO())
}
}
for _, keys := range ck.Deps() {
for _, key := range keys {
res, err := c.filterResults(m, key.CacheKey.CacheKey)
if err != nil {
for _, r := range results {
r.Result.Release(context.TODO())
}
return nil, err
}
results = append(results, res...)
}
}
return
}
func (c *cacheManager) LoadWithParents(ctx context.Context, rec *CacheRecord) ([]LoadedResult, error) {
lwp, ok := c.results.(interface {
LoadWithParents(context.Context, CacheResult) (map[string]Result, error)
})
if !ok {
res, err := c.Load(ctx, rec)
if err != nil {
return nil, err
}
return []LoadedResult{{Result: res, CacheKey: rec.key, CacheResult: CacheResult{ID: c.getID(rec.key), CreatedAt: rec.CreatedAt}}}, nil
}
c.mu.RLock()
defer c.mu.RUnlock()
cr, err := c.backend.Load(c.getID(rec.key), rec.ID)
if err != nil {
return nil, err
}
m, err := lwp.LoadWithParents(ctx, cr)
if err != nil {
return nil, err
}
results, err := c.filterResults(m, rec.key)
if err != nil {
for _, r := range m {
r.Release(context.TODO())
}
}
return results, nil
}
func (c *cacheManager) Save(k *CacheKey, r Result, createdAt time.Time) (*ExportableCacheKey, error) {
c.mu.Lock()
defer c.mu.Unlock()
res, err := c.results.Save(r, createdAt)
if err != nil {
return nil, err
}
if err := c.backend.AddResult(c.getID(k), res); err != nil {
return nil, err
}
@ -273,5 +347,8 @@ func (c *cacheManager) getIDFromDeps(k *CacheKey) string {
}
func rootKey(dgst digest.Digest, output Index) digest.Digest {
if strings.HasPrefix(dgst.String(), "random:") {
return digest.Digest("random:" + strings.TrimPrefix(digest.FromBytes([]byte(fmt.Sprintf("%s@%d", dgst, output))).String(), digest.Canonical.String()+":"))
}
return digest.FromBytes([]byte(fmt.Sprintf("%s@%d", dgst, output)))
}

View file

@ -44,7 +44,7 @@ type CacheInfoLink struct {
// CacheResultStorage is interface for converting cache metadata result to
// actual solve result
type CacheResultStorage interface {
Save(Result) (CacheResult, error)
Save(Result, time.Time) (CacheResult, error)
Load(ctx context.Context, res CacheResult) (Result, error)
LoadRemote(ctx context.Context, res CacheResult) (*Remote, error)
Exists(id string) bool

View file

@ -4,6 +4,7 @@ import (
"context"
"strings"
"sync"
"time"
digest "github.com/opencontainers/go-digest"
"github.com/pkg/errors"
@ -66,19 +67,31 @@ func (cm *combinedCacheManager) Query(inp []CacheKeyWithSelector, inputIndex Ind
return out, nil
}
func (cm *combinedCacheManager) Load(ctx context.Context, rec *CacheRecord) (Result, error) {
res, err := rec.cacheManager.Load(ctx, rec)
func (cm *combinedCacheManager) Load(ctx context.Context, rec *CacheRecord) (res Result, err error) {
results, err := rec.cacheManager.LoadWithParents(ctx, rec)
if err != nil {
return nil, err
}
if _, err := cm.main.Save(rec.key, res); err != nil {
return nil, err
defer func() {
for i, res := range results {
if err == nil && i == 0 {
continue
}
res.Result.Release(context.TODO())
}
}()
if rec.cacheManager != cm.main {
for _, res := range results {
if _, err := cm.main.Save(res.CacheKey, res.Result, res.CacheResult.CreatedAt); err != nil {
return nil, err
}
}
}
return res, nil
return results[0].Result, nil
}
func (cm *combinedCacheManager) Save(key *CacheKey, s Result) (*ExportableCacheKey, error) {
return cm.main.Save(key, s)
func (cm *combinedCacheManager) Save(key *CacheKey, s Result, createdAt time.Time) (*ExportableCacheKey, error) {
return cm.main.Save(key, s, createdAt)
}
func (cm *combinedCacheManager) Records(ck *CacheKey) ([]*CacheRecord, error) {

View file

@ -3,6 +3,7 @@ package solver
import (
"context"
"sync"
"time"
"github.com/moby/buildkit/solver/internal/pipe"
digest "github.com/opencontainers/go-digest"
@ -28,7 +29,7 @@ func newEdge(ed Edge, op activeOp, index *edgeIndex) *edge {
edge: ed,
op: op,
depRequests: map[pipe.Receiver]*dep{},
keyMap: map[string]*CacheKey{},
keyMap: map[string]struct{}{},
cacheRecords: map[string]*CacheRecord{},
index: index,
}
@ -50,7 +51,7 @@ type edge struct {
execReq pipe.Receiver
err error
cacheRecords map[string]*CacheRecord
keyMap map[string]*CacheKey
keyMap map[string]struct{}
noCacheMatchPossible bool
allDepsCompletedCacheFast bool
@ -526,6 +527,10 @@ func (e *edge) recalcCurrentState() {
}
}
for key := range newKeys {
e.keyMap[key] = struct{}{}
}
for _, r := range newKeys {
// TODO: add all deps automatically
mergedKey := r.clone()
@ -612,6 +617,36 @@ func (e *edge) recalcCurrentState() {
e.allDepsCompletedCacheSlow = e.cacheMapDone && allDepsCompletedCacheSlow
e.allDepsStateCacheSlow = e.cacheMapDone && allDepsStateCacheSlow
e.allDepsCompleted = e.cacheMapDone && allDepsCompleted
if e.allDepsStateCacheSlow && len(e.cacheRecords) > 0 && e.state == edgeStatusCacheFast {
openKeys := map[string]struct{}{}
for _, dep := range e.deps {
isSlowIncomplete := e.slowCacheFunc(dep) != nil && (dep.state == edgeStatusCacheSlow || (dep.state == edgeStatusComplete && !dep.slowCacheComplete))
if !isSlowIncomplete {
openDepKeys := map[string]struct{}{}
for key := range dep.keyMap {
if _, ok := e.keyMap[key]; !ok {
openDepKeys[key] = struct{}{}
}
}
if len(openKeys) != 0 {
for k := range openKeys {
if _, ok := openDepKeys[k]; !ok {
delete(openKeys, k)
}
}
} else {
openKeys = openDepKeys
}
if len(openKeys) == 0 {
e.state = edgeStatusCacheSlow
if debugScheduler {
logrus.Debugf("upgrade to cache-slow because no open keys")
}
}
}
}
}
}
}
@ -845,7 +880,7 @@ func (e *edge) execOp(ctx context.Context) (interface{}, error) {
var exporters []CacheExporter
for _, cacheKey := range cacheKeys {
ck, err := e.op.Cache().Save(cacheKey, res)
ck, err := e.op.Cache().Save(cacheKey, res, time.Now())
if err != nil {
return nil, err
}

View file

@ -177,7 +177,7 @@ func (sb *subBuilder) Context(ctx context.Context) context.Context {
func (sb *subBuilder) EachValue(ctx context.Context, key string, fn func(interface{}) error) error {
sb.mu.Lock()
defer sb.mu.Lock()
defer sb.mu.Unlock()
for j := range sb.jobs {
if err := j.EachValue(ctx, key, fn); err != nil {
return err

View file

@ -6,30 +6,34 @@ import (
"io"
"strings"
"sync"
"time"
"github.com/containerd/containerd/platforms"
"github.com/docker/distribution/reference"
"github.com/moby/buildkit/cache"
"github.com/moby/buildkit/cache/remotecache"
"github.com/moby/buildkit/executor"
"github.com/moby/buildkit/frontend"
gw "github.com/moby/buildkit/frontend/gateway/client"
"github.com/moby/buildkit/identity"
"github.com/moby/buildkit/session"
"github.com/moby/buildkit/solver"
"github.com/moby/buildkit/util/tracing"
"github.com/moby/buildkit/worker"
digest "github.com/opencontainers/go-digest"
specs "github.com/opencontainers/image-spec/specs-go/v1"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
)
type llbBridge struct {
builder solver.Builder
frontends map[string]frontend.Frontend
resolveWorker func() (worker.Worker, error)
resolveCacheImporter remotecache.ResolveCacheImporterFunc
cms map[string]solver.CacheManager
cmsMu sync.Mutex
platforms []specs.Platform
builder solver.Builder
frontends map[string]frontend.Frontend
resolveWorker func() (worker.Worker, error)
resolveCacheImporterFuncs map[string]remotecache.ResolveCacheImporterFunc
cms map[string]solver.CacheManager
cmsMu sync.Mutex
platforms []specs.Platform
sm *session.Manager
}
func (b *llbBridge) Solve(ctx context.Context, req frontend.SolveRequest) (res *frontend.Result, err error) {
@ -38,36 +42,39 @@ func (b *llbBridge) Solve(ctx context.Context, req frontend.SolveRequest) (res *
return nil, err
}
var cms []solver.CacheManager
for _, ref := range req.ImportCacheRefs {
for _, im := range req.CacheImports {
b.cmsMu.Lock()
var cm solver.CacheManager
if prevCm, ok := b.cms[ref]; !ok {
r, err := reference.ParseNormalizedNamed(ref)
if err != nil {
return nil, err
cmId := identity.NewID()
if im.Type == "registry" {
// For compatibility with < v0.4.0
if ref := im.Attrs["ref"]; ref != "" {
cmId = ref
}
ref = reference.TagNameOnly(r).String()
func(ref string) {
cm = newLazyCacheManager(ref, func() (solver.CacheManager, error) {
}
if prevCm, ok := b.cms[cmId]; !ok {
func(cmId string) {
cm = newLazyCacheManager(cmId, func() (solver.CacheManager, error) {
var cmNew solver.CacheManager
if err := inVertexContext(b.builder.Context(ctx), "importing cache manifest from "+ref, "", func(ctx context.Context) error {
if b.resolveCacheImporter == nil {
return errors.New("no cache importer is available")
if err := inVertexContext(b.builder.Context(ctx), "importing cache manifest from "+cmId, "", func(ctx context.Context) error {
resolveCI, ok := b.resolveCacheImporterFuncs[im.Type]
if !ok {
return errors.Errorf("unknown cache importer: %s", im.Type)
}
typ := "" // TODO: support non-registry type
ci, desc, err := b.resolveCacheImporter(ctx, typ, ref)
ci, desc, err := resolveCI(ctx, im.Attrs)
if err != nil {
return err
}
cmNew, err = ci.Resolve(ctx, desc, ref, w)
cmNew, err = ci.Resolve(ctx, desc, cmId, w)
return err
}); err != nil {
logrus.Debugf("error while importing cache manifest from cmId=%s: %v", cmId, err)
return nil, err
}
return cmNew, nil
})
}(ref)
b.cms[ref] = cm
}(cmId)
b.cms[cmId] = cm
} else {
cm = prevCm
}
@ -151,7 +158,7 @@ func (s *llbBridge) ResolveImageConfig(ctx context.Context, ref string, opt gw.R
id += platforms.Format(*platform)
}
err = inVertexContext(s.builder.Context(ctx), opt.LogName, id, func(ctx context.Context) error {
dgst, config, err = w.ResolveImageConfig(ctx, ref, opt)
dgst, config, err = w.ResolveImageConfig(ctx, ref, opt, s.sm)
return err
})
return dgst, config, err
@ -186,11 +193,11 @@ func (lcm *lazyCacheManager) Load(ctx context.Context, rec *solver.CacheRecord)
}
return lcm.main.Load(ctx, rec)
}
func (lcm *lazyCacheManager) Save(key *solver.CacheKey, s solver.Result) (*solver.ExportableCacheKey, error) {
func (lcm *lazyCacheManager) Save(key *solver.CacheKey, s solver.Result, createdAt time.Time) (*solver.ExportableCacheKey, error) {
if err := lcm.wait(); err != nil {
return nil, err
}
return lcm.main.Save(key, s)
return lcm.main.Save(key, s, createdAt)
}
func (lcm *lazyCacheManager) wait() error {

View file

@ -10,13 +10,13 @@ import (
"os"
"path"
"path/filepath"
"runtime"
"sort"
"strings"
"sync"
"time"
"github.com/containerd/containerd/mount"
"github.com/containerd/containerd/platforms"
"github.com/docker/docker/pkg/locker"
"github.com/moby/buildkit/cache"
"github.com/moby/buildkit/cache/metadata"
@ -34,6 +34,7 @@ import (
utilsystem "github.com/moby/buildkit/util/system"
"github.com/moby/buildkit/worker"
digest "github.com/opencontainers/go-digest"
specs "github.com/opencontainers/image-spec/specs-go/v1"
"github.com/opencontainers/runc/libcontainer/system"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
@ -51,12 +52,13 @@ type execOp struct {
md *metadata.Store
exec executor.Executor
w worker.Worker
platform *pb.Platform
numInputs int
cacheMounts map[string]*cacheRefShare
}
func NewExecOp(v solver.Vertex, op *pb.Op_Exec, cm cache.Manager, sm *session.Manager, md *metadata.Store, exec executor.Executor, w worker.Worker) (solver.Op, error) {
func NewExecOp(v solver.Vertex, op *pb.Op_Exec, platform *pb.Platform, cm cache.Manager, sm *session.Manager, md *metadata.Store, exec executor.Executor, w worker.Worker) (solver.Op, error) {
return &execOp{
op: op.Exec,
cm: cm,
@ -65,6 +67,7 @@ func NewExecOp(v solver.Vertex, op *pb.Op_Exec, cm cache.Manager, sm *session.Ma
exec: exec,
numInputs: len(v.Inputs()),
w: w,
platform: platform,
cacheMounts: map[string]*cacheRefShare{},
}, nil
}
@ -98,16 +101,27 @@ func (e *execOp) CacheMap(ctx context.Context, index int) (*solver.CacheMap, boo
}
op.Meta.ProxyEnv = nil
p := platforms.DefaultSpec()
if e.platform != nil {
p = specs.Platform{
OS: e.platform.OS,
Architecture: e.platform.Architecture,
Variant: e.platform.Variant,
}
}
dt, err := json.Marshal(struct {
Type string
Exec *pb.ExecOp
OS string
Arch string
Type string
Exec *pb.ExecOp
OS string
Arch string
Variant string `json:",omitempty"`
}{
Type: execCacheType,
Exec: &op,
OS: runtime.GOOS,
Arch: runtime.GOARCH,
Type: execCacheType,
Exec: &op,
OS: p.OS,
Arch: p.Architecture,
Variant: p.Variant,
})
if err != nil {
return nil, false, err
@ -151,7 +165,7 @@ func dedupePaths(inp []string) []string {
for p1 := range old {
var skip bool
for p2 := range old {
if p1 != p2 && strings.HasPrefix(p1, p2) {
if p1 != p2 && strings.HasPrefix(p1, p2+"/") {
skip = true
break
}

View file

@ -2,8 +2,10 @@ package ops
import (
"context"
"strings"
"sync"
"github.com/moby/buildkit/session"
"github.com/moby/buildkit/solver"
"github.com/moby/buildkit/solver/pb"
"github.com/moby/buildkit/source"
@ -19,14 +21,16 @@ type sourceOp struct {
platform *pb.Platform
sm *source.Manager
src source.SourceInstance
sessM *session.Manager
w worker.Worker
}
func NewSourceOp(_ solver.Vertex, op *pb.Op_Source, platform *pb.Platform, sm *source.Manager, w worker.Worker) (solver.Op, error) {
func NewSourceOp(_ solver.Vertex, op *pb.Op_Source, platform *pb.Platform, sm *source.Manager, sessM *session.Manager, w worker.Worker) (solver.Op, error) {
return &sourceOp{
op: op,
sm: sm,
w: w,
sessM: sessM,
platform: platform,
}, nil
}
@ -41,7 +45,7 @@ func (s *sourceOp) instance(ctx context.Context) (source.SourceInstance, error)
if err != nil {
return nil, err
}
src, err := s.sm.Resolve(ctx, id)
src, err := s.sm.Resolve(ctx, id, s.sessM)
if err != nil {
return nil, err
}
@ -59,9 +63,15 @@ func (s *sourceOp) CacheMap(ctx context.Context, index int) (*solver.CacheMap, b
return nil, false, err
}
dgst := digest.FromBytes([]byte(sourceCacheType + ":" + k))
if strings.HasPrefix(k, "session:") {
dgst = digest.Digest("random:" + strings.TrimPrefix(dgst.String(), dgst.Algorithm().String()+":"))
}
return &solver.CacheMap{
// TODO: add os/arch
Digest: digest.FromBytes([]byte(sourceCacheType + ":" + k)),
Digest: dgst,
}, done, nil
}

View file

@ -32,7 +32,7 @@ func NewContentHashFunc(selectors []string) solver.ResultBasedCacheFunc {
// FIXME(tonistiigi): enabling this parallelization seems to create wrong results for some big inputs(like gobuild)
// func(i int) {
// eg.Go(func() error {
dgst, err := contenthash.Checksum(ctx, ref.ImmutableRef, path.Join("/", sel))
dgst, err := contenthash.Checksum(ctx, ref.ImmutableRef, path.Join("/", sel), true)
if err != nil {
return "", err
}

View file

@ -2,6 +2,7 @@ package llbsolver
import (
"context"
"fmt"
"strings"
"time"
@ -10,6 +11,7 @@ import (
"github.com/moby/buildkit/client"
controlgateway "github.com/moby/buildkit/control/gateway"
"github.com/moby/buildkit/exporter"
"github.com/moby/buildkit/exporter/containerimage/exptypes"
"github.com/moby/buildkit/frontend"
"github.com/moby/buildkit/frontend/gateway"
"github.com/moby/buildkit/identity"
@ -35,22 +37,24 @@ type ExporterRequest struct {
type ResolveWorkerFunc func() (worker.Worker, error)
type Solver struct {
workerController *worker.Controller
solver *solver.Solver
resolveWorker ResolveWorkerFunc
frontends map[string]frontend.Frontend
resolveCacheImporter remotecache.ResolveCacheImporterFunc
platforms []specs.Platform
gatewayForwarder *controlgateway.GatewayForwarder
workerController *worker.Controller
solver *solver.Solver
resolveWorker ResolveWorkerFunc
frontends map[string]frontend.Frontend
resolveCacheImporterFuncs map[string]remotecache.ResolveCacheImporterFunc
platforms []specs.Platform
gatewayForwarder *controlgateway.GatewayForwarder
sm *session.Manager
}
func New(wc *worker.Controller, f map[string]frontend.Frontend, cache solver.CacheManager, resolveCI remotecache.ResolveCacheImporterFunc, gatewayForwarder *controlgateway.GatewayForwarder) (*Solver, error) {
func New(wc *worker.Controller, f map[string]frontend.Frontend, cache solver.CacheManager, resolveCI map[string]remotecache.ResolveCacheImporterFunc, gatewayForwarder *controlgateway.GatewayForwarder, sm *session.Manager) (*Solver, error) {
s := &Solver{
workerController: wc,
resolveWorker: defaultResolver(wc),
frontends: f,
resolveCacheImporter: resolveCI,
gatewayForwarder: gatewayForwarder,
workerController: wc,
resolveWorker: defaultResolver(wc),
frontends: f,
resolveCacheImporterFuncs: resolveCI,
gatewayForwarder: gatewayForwarder,
sm: sm,
}
// executing is currently only allowed on default worker
@ -73,18 +77,19 @@ func (s *Solver) resolver() solver.ResolveOpFunc {
if err != nil {
return nil, err
}
return w.ResolveOp(v, s.Bridge(b))
return w.ResolveOp(v, s.Bridge(b), s.sm)
}
}
func (s *Solver) Bridge(b solver.Builder) frontend.FrontendLLBBridge {
return &llbBridge{
builder: b,
frontends: s.frontends,
resolveWorker: s.resolveWorker,
resolveCacheImporter: s.resolveCacheImporter,
cms: map[string]solver.CacheManager{},
platforms: s.platforms,
builder: b,
frontends: s.frontends,
resolveWorker: s.resolveWorker,
resolveCacheImporterFuncs: s.resolveCacheImporterFuncs,
cms: map[string]solver.CacheManager{},
platforms: s.platforms,
sm: s.sm,
}
}
@ -138,7 +143,7 @@ func (s *Solver) Solve(ctx context.Context, id string, req frontend.SolveRequest
}()
var exporterResponse map[string]string
if exp := exp.Exporter; exp != nil {
if e := exp.Exporter; e != nil {
inp := exporter.Source{
Metadata: res.Metadata,
}
@ -151,6 +156,14 @@ func (s *Solver) Solve(ctx context.Context, id string, req frontend.SolveRequest
return nil, errors.Errorf("invalid reference: %T", res.Sys())
}
inp.Ref = workerRef.ImmutableRef
dt, err := inlineCache(ctx, exp.CacheExporter, res)
if err != nil {
return nil, err
}
if dt != nil {
inp.Metadata[exptypes.ExporterInlineCache] = dt
}
}
if res.Refs != nil {
m := make(map[string]cache.ImmutableRef, len(res.Refs))
@ -163,19 +176,28 @@ func (s *Solver) Solve(ctx context.Context, id string, req frontend.SolveRequest
return nil, errors.Errorf("invalid reference: %T", res.Sys())
}
m[k] = workerRef.ImmutableRef
dt, err := inlineCache(ctx, exp.CacheExporter, res)
if err != nil {
return nil, err
}
if dt != nil {
inp.Metadata[fmt.Sprintf("%s/%s", exptypes.ExporterInlineCache, k)] = dt
}
}
}
inp.Refs = m
}
if err := inVertexContext(j.Context(ctx), exp.Name(), "", func(ctx context.Context) error {
exporterResponse, err = exp.Export(ctx, inp)
if err := inVertexContext(j.Context(ctx), e.Name(), "", func(ctx context.Context) error {
exporterResponse, err = e.Export(ctx, inp)
return err
}); err != nil {
return nil, err
}
}
var cacheExporterResponse map[string]string
if e := exp.CacheExporter; e != nil {
if err := inVertexContext(j.Context(ctx), "exporting cache", "", func(ctx context.Context) error {
prepareDone := oneOffProgress(ctx, "preparing build cache for export")
@ -190,7 +212,8 @@ func (s *Solver) Solve(ctx context.Context, id string, req frontend.SolveRequest
return prepareDone(err)
}
prepareDone(nil)
return e.Finalize(ctx)
cacheExporterResponse, err = e.Finalize(ctx)
return err
}); err != nil {
return nil, err
}
@ -205,12 +228,48 @@ func (s *Solver) Solve(ctx context.Context, id string, req frontend.SolveRequest
exporterResponse[k] = string(v)
}
}
for k, v := range cacheExporterResponse {
if strings.HasPrefix(k, "cache.") {
exporterResponse[k] = v
}
}
return &client.SolveResponse{
ExporterResponse: exporterResponse,
}, nil
}
func inlineCache(ctx context.Context, e remotecache.Exporter, res solver.CachedResult) ([]byte, error) {
if efl, ok := e.(interface {
ExportForLayers([]digest.Digest) ([]byte, error)
}); ok {
workerRef, ok := res.Sys().(*worker.WorkerRef)
if !ok {
return nil, errors.Errorf("invalid reference: %T", res.Sys())
}
remote, err := workerRef.Worker.GetRemote(ctx, workerRef.ImmutableRef, true)
if err != nil || remote == nil {
return nil, nil
}
digests := make([]digest.Digest, 0, len(remote.Descriptors))
for _, desc := range remote.Descriptors {
digests = append(digests, desc.Digest)
}
if _, err := res.CacheKeys()[0].Exporter.ExportTo(ctx, e, solver.CacheExportOpt{
Convert: workerRefConverter,
Mode: solver.CacheExportModeMin,
}); err != nil {
return nil, err
}
return efl.ExportForLayers(digests)
}
return nil, nil
}
func (s *Solver) Status(ctx context.Context, id string, statusChan chan *client.SolveStatus) error {
j, err := s.solver.Get(id)
if err != nil {

View file

@ -80,10 +80,20 @@ func RuntimePlatforms(p []specs.Platform) LoadOpt {
defaultPlatform = &pb.Platform{
OS: p.OS,
Architecture: p.Architecture,
Variant: p.Variant,
}
}
op.Platform = defaultPlatform
}
platform := specs.Platform{OS: op.Platform.OS, Architecture: op.Platform.Architecture, Variant: op.Platform.Variant}
normalizedPlatform := platforms.Normalize(platform)
op.Platform = &pb.Platform{
OS: normalizedPlatform.OS,
Architecture: normalizedPlatform.Architecture,
Variant: normalizedPlatform.Variant,
}
if _, ok := op.Op.(*pb.Op_Exec); ok {
var found bool
for _, pp := range pp {
@ -186,7 +196,11 @@ func loadLLB(def *pb.Definition, fn func(digest.Digest, *pb.Op, func(digest.Dige
if v, ok := cache[dgst]; ok {
return v, nil
}
v, err := fn(dgst, allOps[dgst], rec)
op, ok := allOps[dgst]
if !ok {
return nil, errors.Errorf("invalid missing input digest %s", dgst)
}
v, err := fn(dgst, op, rec)
if err != nil {
return nil, err
}

View file

@ -284,9 +284,9 @@ type inMemoryResultStore struct {
m *sync.Map
}
func (s *inMemoryResultStore) Save(r Result) (CacheResult, error) {
func (s *inMemoryResultStore) Save(r Result, createdAt time.Time) (CacheResult, error) {
s.m.Store(r.ID(), r)
return CacheResult{ID: r.ID(), CreatedAt: time.Now()}, nil
return CacheResult{ID: r.ID(), CreatedAt: createdAt}, nil
}
func (s *inMemoryResultStore) Load(ctx context.Context, res CacheResult) (Result, error) {

File diff suppressed because it is too large Load diff

View file

@ -164,5 +164,5 @@ type CacheManager interface {
// Load pulls and returns the cached result
Load(ctx context.Context, rec *CacheRecord) (Result, error)
// Save saves a result based on a cache key
Save(key *CacheKey, s Result) (*ExportableCacheKey, error)
Save(key *CacheKey, s Result, createdAt time.Time) (*ExportableCacheKey, error)
}

View file

@ -16,6 +16,7 @@ import (
"github.com/moby/buildkit/cache/metadata"
"github.com/moby/buildkit/client"
"github.com/moby/buildkit/identity"
"github.com/moby/buildkit/session"
"github.com/moby/buildkit/snapshot"
"github.com/moby/buildkit/source"
"github.com/moby/buildkit/util/progress/logs"
@ -152,7 +153,7 @@ type gitSourceHandler struct {
cacheKey string
}
func (gs *gitSource) Resolve(ctx context.Context, id source.Identifier) (source.SourceInstance, error) {
func (gs *gitSource) Resolve(ctx context.Context, id source.Identifier, _ *session.Manager) (source.SourceInstance, error) {
gitIdentifier, ok := id.(*source.GitIdentifier)
if !ok {
return nil, errors.Errorf("invalid git identifier %v", id)
@ -248,6 +249,9 @@ func (gs *gitSourceHandler) Snapshot(ctx context.Context) (out cache.ImmutableRe
}
if doFetch {
// make sure no old lock files have leaked
os.RemoveAll(filepath.Join(gitDir, "shallow.lock"))
args := []string{"fetch"}
if !isCommitSHA(ref) { // TODO: find a branch from ls-remote?
args = append(args, "--depth=1", "--no-tags")

View file

@ -6,6 +6,7 @@ import (
"context"
"os/exec"
"syscall"
"time"
)
func runProcessGroup(ctx context.Context, cmd *exec.Cmd) error {
@ -17,7 +18,14 @@ func runProcessGroup(ctx context.Context, cmd *exec.Cmd) error {
go func() {
select {
case <-ctx.Done():
syscall.Kill(-cmd.Process.Pid, syscall.SIGKILL)
syscall.Kill(-cmd.Process.Pid, syscall.SIGTERM)
go func() {
select {
case <-waitDone:
case <-time.After(10 * time.Second):
syscall.Kill(-cmd.Process.Pid, syscall.SIGKILL)
}
}()
case <-waitDone:
}
}()

View file

@ -18,6 +18,7 @@ import (
"github.com/docker/docker/pkg/locker"
"github.com/moby/buildkit/cache"
"github.com/moby/buildkit/cache/metadata"
"github.com/moby/buildkit/session"
"github.com/moby/buildkit/snapshot"
"github.com/moby/buildkit/source"
"github.com/moby/buildkit/util/tracing"
@ -66,7 +67,7 @@ type httpSourceHandler struct {
cacheKey digest.Digest
}
func (hs *httpSource) Resolve(ctx context.Context, id source.Identifier) (source.SourceInstance, error) {
func (hs *httpSource) Resolve(ctx context.Context, id source.Identifier, _ *session.Manager) (source.SourceInstance, error) {
httpIdentifier, ok := id.(*source.HttpIdentifier)
if !ok {
return nil, errors.Errorf("invalid http identifier %v", id)
@ -146,11 +147,42 @@ func (hs *httpSourceHandler) CacheKey(ctx context.Context, index int) (string, b
if etag := getETag(si); etag != "" {
if dgst := getChecksum(si); dgst != "" {
m[etag] = si
req.Header.Add("If-None-Match", etag)
}
}
// }
}
if len(m) > 0 {
etags := make([]string, 0, len(m))
for t := range m {
etags = append(etags, t)
}
req.Header.Add("If-None-Match", strings.Join(etags, ", "))
}
}
// Some servers seem to have trouble supporting If-None-Match properly even
// though they return ETag-s. So first, optionally try a HEAD request with
// manual ETag value comparison.
if len(m) > 0 {
req.Method = "HEAD"
resp, err := hs.client.Do(req)
if err == nil {
if resp.StatusCode == http.StatusOK || resp.StatusCode == http.StatusNotModified {
respETag := resp.Header.Get("ETag")
si, ok := m[respETag]
if ok {
hs.refID = si.ID()
dgst := getChecksum(si)
if dgst != "" {
modTime := getModTime(si)
resp.Body.Close()
return hs.formatCacheKey(getFileName(hs.src.URL, hs.src.Filename, resp), dgst, modTime).String(), true, nil
}
}
}
resp.Body.Close()
}
req.Method = "GET"
}
resp, err := hs.client.Do(req)

View file

@ -28,14 +28,12 @@ import (
const keySharedKey = "local.sharedKey"
type Opt struct {
SessionManager *session.Manager
CacheAccessor cache.Accessor
MetadataStore *metadata.Store
CacheAccessor cache.Accessor
MetadataStore *metadata.Store
}
func NewSource(opt Opt) (source.Source, error) {
ls := &localSource{
sm: opt.SessionManager,
cm: opt.CacheAccessor,
md: opt.MetadataStore,
}
@ -43,7 +41,6 @@ func NewSource(opt Opt) (source.Source, error) {
}
type localSource struct {
sm *session.Manager
cm cache.Accessor
md *metadata.Store
}
@ -52,7 +49,7 @@ func (ls *localSource) ID() string {
return source.LocalScheme
}
func (ls *localSource) Resolve(ctx context.Context, id source.Identifier) (source.SourceInstance, error) {
func (ls *localSource) Resolve(ctx context.Context, id source.Identifier, sm *session.Manager) (source.SourceInstance, error) {
localIdentifier, ok := id.(*source.LocalIdentifier)
if !ok {
return nil, errors.Errorf("invalid local identifier %v", id)
@ -60,12 +57,14 @@ func (ls *localSource) Resolve(ctx context.Context, id source.Identifier) (sourc
return &localSourceHandler{
src: *localIdentifier,
sm: sm,
localSource: ls,
}, nil
}
type localSourceHandler struct {
src source.LocalIdentifier
sm *session.Manager
*localSource
}

View file

@ -5,12 +5,13 @@ import (
"sync"
"github.com/moby/buildkit/cache"
"github.com/moby/buildkit/session"
"github.com/pkg/errors"
)
type Source interface {
ID() string
Resolve(ctx context.Context, id Identifier) (SourceInstance, error)
Resolve(ctx context.Context, id Identifier, sm *session.Manager) (SourceInstance, error)
}
type SourceInstance interface {
@ -35,7 +36,7 @@ func (sm *Manager) Register(src Source) {
sm.mu.Unlock()
}
func (sm *Manager) Resolve(ctx context.Context, id Identifier) (SourceInstance, error) {
func (sm *Manager) Resolve(ctx context.Context, id Identifier, sessM *session.Manager) (SourceInstance, error) {
sm.mu.Lock()
src, ok := sm.sources[id.ID()]
sm.mu.Unlock()
@ -44,5 +45,5 @@ func (sm *Manager) Resolve(ctx context.Context, id Identifier) (SourceInstance,
return nil, errors.Errorf("no handler for %s", id.ID())
}
return src.Resolve(ctx, id)
return src.Resolve(ctx, id, sessM)
}

View file

@ -1,15 +1,6 @@
// Code generated by protoc-gen-gogo. DO NOT EDIT.
// source: caps.proto
/*
Package moby_buildkit_v1_apicaps is a generated protocol buffer package.
It is generated from these files:
caps.proto
It has these top-level messages:
APICap
*/
package moby_buildkit_v1_apicaps
import proto "github.com/gogo/protobuf/proto"
@ -32,18 +23,49 @@ const _ = proto.GoGoProtoPackageIsVersion2 // please upgrade the proto package
// APICap defines a capability supported by the service
type APICap struct {
ID string `protobuf:"bytes,1,opt,name=ID,proto3" json:"ID,omitempty"`
Enabled bool `protobuf:"varint,2,opt,name=Enabled,proto3" json:"Enabled,omitempty"`
Deprecated bool `protobuf:"varint,3,opt,name=Deprecated,proto3" json:"Deprecated,omitempty"`
DisabledReason string `protobuf:"bytes,4,opt,name=DisabledReason,proto3" json:"DisabledReason,omitempty"`
DisabledReasonMsg string `protobuf:"bytes,5,opt,name=DisabledReasonMsg,proto3" json:"DisabledReasonMsg,omitempty"`
DisabledAlternative string `protobuf:"bytes,6,opt,name=DisabledAlternative,proto3" json:"DisabledAlternative,omitempty"`
ID string `protobuf:"bytes,1,opt,name=ID,proto3" json:"ID,omitempty"`
Enabled bool `protobuf:"varint,2,opt,name=Enabled,proto3" json:"Enabled,omitempty"`
Deprecated bool `protobuf:"varint,3,opt,name=Deprecated,proto3" json:"Deprecated,omitempty"`
DisabledReason string `protobuf:"bytes,4,opt,name=DisabledReason,proto3" json:"DisabledReason,omitempty"`
DisabledReasonMsg string `protobuf:"bytes,5,opt,name=DisabledReasonMsg,proto3" json:"DisabledReasonMsg,omitempty"`
DisabledAlternative string `protobuf:"bytes,6,opt,name=DisabledAlternative,proto3" json:"DisabledAlternative,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *APICap) Reset() { *m = APICap{} }
func (m *APICap) String() string { return proto.CompactTextString(m) }
func (*APICap) ProtoMessage() {}
func (*APICap) Descriptor() ([]byte, []int) { return fileDescriptorCaps, []int{0} }
func (m *APICap) Reset() { *m = APICap{} }
func (m *APICap) String() string { return proto.CompactTextString(m) }
func (*APICap) ProtoMessage() {}
func (*APICap) Descriptor() ([]byte, []int) {
return fileDescriptor_caps_04e1bcd232e9a565, []int{0}
}
func (m *APICap) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
}
func (m *APICap) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
if deterministic {
return xxx_messageInfo_APICap.Marshal(b, m, deterministic)
} else {
b = b[:cap(b)]
n, err := m.MarshalTo(b)
if err != nil {
return nil, err
}
return b[:n], nil
}
}
func (dst *APICap) XXX_Merge(src proto.Message) {
xxx_messageInfo_APICap.Merge(dst, src)
}
func (m *APICap) XXX_Size() int {
return m.Size()
}
func (m *APICap) XXX_DiscardUnknown() {
xxx_messageInfo_APICap.DiscardUnknown(m)
}
var xxx_messageInfo_APICap proto.InternalMessageInfo
func (m *APICap) GetID() string {
if m != nil {
@ -149,6 +171,9 @@ func (m *APICap) MarshalTo(dAtA []byte) (int, error) {
i = encodeVarintCaps(dAtA, i, uint64(len(m.DisabledAlternative)))
i += copy(dAtA[i:], m.DisabledAlternative)
}
if m.XXX_unrecognized != nil {
i += copy(dAtA[i:], m.XXX_unrecognized)
}
return i, nil
}
@ -162,6 +187,9 @@ func encodeVarintCaps(dAtA []byte, offset int, v uint64) int {
return offset + 1
}
func (m *APICap) Size() (n int) {
if m == nil {
return 0
}
var l int
_ = l
l = len(m.ID)
@ -186,6 +214,9 @@ func (m *APICap) Size() (n int) {
if l > 0 {
n += 1 + l + sovCaps(uint64(l))
}
if m.XXX_unrecognized != nil {
n += len(m.XXX_unrecognized)
}
return n
}
@ -399,6 +430,7 @@ func (m *APICap) Unmarshal(dAtA []byte) error {
if (iNdEx + skippy) > l {
return io.ErrUnexpectedEOF
}
m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...)
iNdEx += skippy
}
}
@ -513,9 +545,9 @@ var (
ErrIntOverflowCaps = fmt.Errorf("proto: integer overflow")
)
func init() { proto.RegisterFile("caps.proto", fileDescriptorCaps) }
func init() { proto.RegisterFile("caps.proto", fileDescriptor_caps_04e1bcd232e9a565) }
var fileDescriptorCaps = []byte{
var fileDescriptor_caps_04e1bcd232e9a565 = []byte{
// 236 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0xe2, 0x4a, 0x4e, 0x2c, 0x28,
0xd6, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0x92, 0xc8, 0xcd, 0x4f, 0xaa, 0xd4, 0x4b, 0x2a, 0xcd,

View file

@ -67,7 +67,7 @@ func CopyChain(ctx context.Context, ingester content.Ingester, provider content.
remotes.FetchHandler(ingester, &localFetcher{provider}),
}
if err := images.Dispatch(ctx, images.Handlers(handlers...), desc); err != nil {
if err := images.Dispatch(ctx, images.Handlers(handlers...), nil, desc); err != nil {
return errors.WithStack(err)
}

View file

@ -66,7 +66,7 @@ func Config(ctx context.Context, str string, resolver remotes.Resolver, cache Co
remotes.FetchHandler(cache, fetcher),
childrenConfigHandler(cache, platform),
}
if err := images.Dispatch(ctx, images.Handlers(handlers...), desc); err != nil {
if err := images.Dispatch(ctx, images.Handlers(handlers...), nil, desc); err != nil {
return "", nil, err
}
config, err := images.Config(ctx, cache, desc, platform)
@ -135,17 +135,21 @@ func childrenConfigHandler(provider content.Provider, platform platforms.MatchCo
func DetectManifestMediaType(ra content.ReaderAt) (string, error) {
// TODO: schema1
p := make([]byte, ra.Size())
if _, err := ra.ReadAt(p, 0); err != nil {
dt := make([]byte, ra.Size())
if _, err := ra.ReadAt(dt, 0); err != nil {
return "", err
}
return DetectManifestBlobMediaType(dt)
}
func DetectManifestBlobMediaType(dt []byte) (string, error) {
var mfst struct {
MediaType string `json:"mediaType"`
Config json.RawMessage `json:"config"`
}
if err := json.Unmarshal(p, &mfst); err != nil {
if err := json.Unmarshal(dt, &mfst); err != nil {
return "", err
}

View file

@ -1,69 +0,0 @@
github.com/pkg/errors v0.8.0
go.etcd.io/bbolt v1.3.1-etcd.8
github.com/stretchr/testify v1.1.4
github.com/davecgh/go-spew v1.1.0
github.com/pmezard/go-difflib v1.0.0
golang.org/x/sys 1b2967e3c290b7c545b3db0deeda16e9be4f98a2
github.com/containerd/containerd 47b328aab79146a9e81e37704db60e7e04a09256
github.com/containerd/typeurl a93fcdb778cd272c6e9b3028b2f42d813e785d40
golang.org/x/sync 450f422ab23cf9881c94e2db30cac0eb1b7cf80c
github.com/sirupsen/logrus v1.0.3
google.golang.org/grpc v1.12.0
github.com/opencontainers/go-digest c9281466c8b2f606084ac71339773efd177436e7
golang.org/x/net 0ed95abb35c445290478a5348a7b38bb154135fd
github.com/gogo/protobuf v1.0.0
github.com/gogo/googleapis b23578765ee54ff6bceff57f397d833bf4ca6869
github.com/golang/protobuf v1.1.0
github.com/containerd/continuity bd77b46c8352f74eb12c85bdc01f4b90f69d66b4
github.com/opencontainers/image-spec v1.0.1
github.com/opencontainers/runc 96ec2177ae841256168fcf76954f7177af9446eb
github.com/Microsoft/go-winio v0.4.11
github.com/containerd/fifo 3d5202aec260678c48179c56f40e6f38a095738c
github.com/opencontainers/runtime-spec eba862dc2470385a233c7507392675cbeadf7353 # v1.0.1-45-geba862d
github.com/containerd/go-runc 5a6d9f37cfa36b15efba46dc7ea349fa9b7143c3
github.com/containerd/console c12b1e7919c14469339a5d38f2f8ed9b64a9de23
google.golang.org/genproto d80a6e20e776b0b17a324d0ba1ab50a39c8e8944
golang.org/x/text 19e51611da83d6be54ddafce4a4af510cb3e9ea4
github.com/docker/go-events 9461782956ad83b30282bf90e31fa6a70c255ba9
github.com/syndtr/gocapability db04d3cc01c8b54962a58ec7e491717d06cfcc16
github.com/Microsoft/hcsshim v0.7.9
golang.org/x/crypto 0709b304e793a5edb4a2c0145f281ecdc20838a4
github.com/containerd/cri f913714917d2456d7e65a0be84962b1ce8acb487 # release/1.2 branch
github.com/urfave/cli 7bc6a0acffa589f415f88aca16cc1de5ffd66f9c
github.com/morikuni/aec 39771216ff4c63d11f5e604076f9c45e8be1067b
github.com/docker/go-units v0.3.1
github.com/google/shlex 6f45313302b9c56850fc17f99e40caebce98c716
golang.org/x/time f51c12702a4d776e4c1fa9b0fabab841babae631
github.com/docker/docker 71cd53e4a197b303c6ba086bd584ffd67a884281
github.com/pkg/profile 5b67d428864e92711fcbd2f8629456121a56d91f
github.com/tonistiigi/fsutil 2862f6bc5ac9b97124e552a5c108230b38a1b0ca
github.com/hashicorp/go-immutable-radix 826af9ccf0feeee615d546d69b11f8e98da8c8f1 https://github.com/tonistiigi/go-immutable-radix
github.com/hashicorp/golang-lru a0d98a5f288019575c6d1f4bb1573fef2d1fcdc4
github.com/mitchellh/hashstructure 2bca23e0e452137f789efbc8610126fd8b94f73b
github.com/docker/go-connections 3ede32e2033de7505e6500d6c868c2b9ed9f169d
github.com/docker/distribution 30578ca32960a4d368bf6db67b0a33c2a1f3dc6f
github.com/tonistiigi/units 6950e57a87eaf136bbe44ef2ec8e75b9e3569de2
github.com/docker/cli 99576756eb3303b7af8102c502f21a912e3c1af6 https://github.com/tonistiigi/docker-cli.git
github.com/docker/docker-credential-helpers d68f9aeca33f5fd3f08eeae5e9d175edf4e731d1
github.com/docker/libnetwork 36d3bed0e9f4b3c8c66df9bd45278bb90b33e911
github.com/BurntSushi/toml 3012a1dbe2e4bd1391d42b32f0577cb7bbc7f005
github.com/ishidawataru/sctp 07191f837fedd2f13d1ec7b5f885f0f3ec54b1cb
github.com/grpc-ecosystem/grpc-opentracing 8e809c8a86450a29b90dcc9efbf062d0fe6d9746
github.com/opentracing/opentracing-go 1361b9cd60be79c4c3a7fa9841b3c132e40066a7
github.com/uber/jaeger-client-go e02c85f9069ea625a96fc4e1afb5e9ac6c569a6d
github.com/apache/thrift b2a4d4ae21c789b689dd162deb819665567f481c
github.com/uber/jaeger-lib c48167d9cae5887393dd5e61efd06a4a48b7fbb3
github.com/codahale/hdrhistogram f8ad88b59a584afeee9d334eff879b104439117b
github.com/opentracing-contrib/go-stdlib b1a47cfbdd7543e70e9ef3e73d0802ad306cc1cc
# used by dockerfile tests
gotest.tools v2.1.0
github.com/google/go-cmp v0.2.0

View file

@ -20,7 +20,7 @@ type cacheResultStorage struct {
wc *Controller
}
func (s *cacheResultStorage) Save(res solver.Result) (solver.CacheResult, error) {
func (s *cacheResultStorage) Save(res solver.Result, createdAt time.Time) (solver.CacheResult, error) {
ref, ok := res.Sys().(*WorkerRef)
if !ok {
return solver.CacheResult{}, errors.Errorf("invalid result: %T", res.Sys())
@ -33,7 +33,7 @@ func (s *cacheResultStorage) Save(res solver.Result) (solver.CacheResult, error)
ref.ImmutableRef.Metadata().Commit()
}
}
return solver.CacheResult{ID: ref.ID(), CreatedAt: time.Now()}, nil
return solver.CacheResult{ID: ref.ID(), CreatedAt: createdAt}, nil
}
func (s *cacheResultStorage) Load(ctx context.Context, res solver.CacheResult) (solver.Result, error) {
return s.load(res.ID, false)

View file

@ -10,6 +10,7 @@ import (
"github.com/moby/buildkit/exporter"
"github.com/moby/buildkit/frontend"
gw "github.com/moby/buildkit/frontend/gateway/client"
"github.com/moby/buildkit/session"
"github.com/moby/buildkit/solver"
digest "github.com/opencontainers/go-digest"
specs "github.com/opencontainers/image-spec/specs-go/v1"
@ -23,12 +24,12 @@ type Worker interface {
GCPolicy() []client.PruneInfo
LoadRef(id string, hidden bool) (cache.ImmutableRef, error)
// ResolveOp resolves Vertex.Sys() to Op implementation.
ResolveOp(v solver.Vertex, s frontend.FrontendLLBBridge) (solver.Op, error)
ResolveImageConfig(ctx context.Context, ref string, opt gw.ResolveImageConfigOpt) (digest.Digest, []byte, error)
ResolveOp(v solver.Vertex, s frontend.FrontendLLBBridge, sm *session.Manager) (solver.Op, error)
ResolveImageConfig(ctx context.Context, ref string, opt gw.ResolveImageConfigOpt, sm *session.Manager) (digest.Digest, []byte, error)
// Exec is similar to executor.Exec but without []mount.Mount
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)
Exporter(name string, sm *session.Manager) (exporter.Exporter, 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)

View file

@ -92,7 +92,13 @@ func (w *dynamicWalker) fill(ctx context.Context, pathC chan<- *currentPath) err
if !ok {
return nil
}
pathC <- p
select {
case pathC <- p:
case <-ctx.Done():
w.err = ctx.Err()
close(w.closeCh)
return ctx.Err()
}
case <-ctx.Done():
w.err = ctx.Err()
close(w.closeCh)