From 17b4ca1edf79d9a904d890ebdaf4078236f01bbd Mon Sep 17 00:00:00 2001 From: Tonis Tiigi Date: Sun, 18 Oct 2020 17:57:56 -0700 Subject: [PATCH 1/4] executor: remove stub files from rootfs Signed-off-by: Tonis Tiigi --- executor/containerdexecutor/executor.go | 25 +++++----- executor/runcexecutor/executor.go | 2 + executor/stubs.go | 49 +++++++++++++++++++ .../dockerfile/dockerfile_secrets_test.go | 1 + 4 files changed, 66 insertions(+), 11 deletions(-) create mode 100644 executor/stubs.go diff --git a/executor/containerdexecutor/executor.go b/executor/containerdexecutor/executor.go index 5ab52d3e6f62..14d97a171112 100644 --- a/executor/containerdexecutor/executor.go +++ b/executor/containerdexecutor/executor.go @@ -13,6 +13,7 @@ import ( "github.com/containerd/containerd" "github.com/containerd/containerd/cio" + "github.com/containerd/containerd/mount" containerdoci "github.com/containerd/containerd/oci" "github.com/containerd/continuity/fs" "github.com/docker/docker/pkg/idtools" @@ -106,17 +107,19 @@ func (w *containerdExecutor) Run(ctx context.Context, id string, root cache.Moun defer release() } + lm := snapshot.LocalMounterWithMounts(rootMounts) + rootfsPath, err := lm.Mount() + if err != nil { + return err + } + defer lm.Unmount() + defer executor.MountStubsCleaner(rootfsPath, mounts)() + var sgids []uint32 uid, gid, err := oci.ParseUIDGID(meta.User) if err != nil { - lm := snapshot.LocalMounterWithMounts(rootMounts) - rootfsPath, err := lm.Mount() - if err != nil { - return err - } uid, gid, sgids, err = oci.GetUser(rootfsPath, meta.User) if err != nil { - lm.Unmount() return err } @@ -127,17 +130,13 @@ func (w *containerdExecutor) Run(ctx context.Context, id string, root cache.Moun newp, err := fs.RootPath(rootfsPath, meta.Cwd) if err != nil { - lm.Unmount() return errors.Wrapf(err, "working dir %s points to invalid target", newp) } if _, err := os.Stat(newp); err != nil { if err := idtools.MkdirAllAndChown(newp, 0755, identity); err != nil { - lm.Unmount() return errors.Wrapf(err, "failed to create working directory %s", newp) } } - - lm.Unmount() } provider, ok := w.networkProviders[meta.NetMode] @@ -196,7 +195,11 @@ func (w *containerdExecutor) Run(ctx context.Context, id string, root cache.Moun cioOpts = append(cioOpts, cio.WithTerminal) } - task, err := container.NewTask(ctx, cio.NewCreator(cioOpts...), containerd.WithRootFS(rootMounts)) + task, err := container.NewTask(ctx, cio.NewCreator(cioOpts...), containerd.WithRootFS([]mount.Mount{{ + Source: rootfsPath, + Type: "bind", + Options: []string{"rbind"}, + }})) if err != nil { return err } diff --git a/executor/runcexecutor/executor.go b/executor/runcexecutor/executor.go index 9849070d7403..627761acab4a 100644 --- a/executor/runcexecutor/executor.go +++ b/executor/runcexecutor/executor.go @@ -213,6 +213,8 @@ func (w *runcExecutor) Run(ctx context.Context, id string, root cache.Mountable, } defer mount.Unmount(rootFSPath, 0) + defer executor.MountStubsCleaner(rootFSPath, mounts)() + uid, gid, sgids, err := oci.GetUser(rootFSPath, meta.User) if err != nil { return err diff --git a/executor/stubs.go b/executor/stubs.go new file mode 100644 index 000000000000..2c13b13053a4 --- /dev/null +++ b/executor/stubs.go @@ -0,0 +1,49 @@ +package executor + +import ( + "errors" + "os" + "path/filepath" + "syscall" + + "github.com/containerd/continuity/fs" +) + +func MountStubsCleaner(dir string, mounts []Mount) func() { + names := []string{"/etc/resolv.conf", "/etc/hosts"} + + for _, m := range mounts { + names = append(names, m.Dest) + } + + paths := make([]string, 0, len(names)) + + for _, p := range names { + p = filepath.Join("/", p) + if p == "/" { + continue + } + realPath, err := fs.RootPath(dir, p) + if err != nil { + continue + } + + _, err = os.Lstat(realPath) + if errors.Is(err, os.ErrNotExist) || errors.Is(err, syscall.ENOTDIR) { + paths = append(paths, realPath) + } + } + + return func() { + for _, p := range paths { + st, err := os.Lstat(p) + if err != nil { + continue + } + if st.Size() != 0 { + continue + } + os.Remove(p) + } + } +} diff --git a/frontend/dockerfile/dockerfile_secrets_test.go b/frontend/dockerfile/dockerfile_secrets_test.go index f3ad995e0ed1..1ed28cb5b10f 100644 --- a/frontend/dockerfile/dockerfile_secrets_test.go +++ b/frontend/dockerfile/dockerfile_secrets_test.go @@ -28,6 +28,7 @@ func testSecretFileParams(t *testing.T, sb integration.Sandbox) { dockerfile := []byte(` FROM busybox RUN --mount=type=secret,required=false,mode=741,uid=100,gid=102,target=/mysecret [ "$(stat -c "%u %g %f" /mysecret)" = "100 102 81e1" ] +RUN [ ! -f /mysecret ] # check no stub left behind `) dir, err := tmpdir( From 85dd12d875f98e2e2fac819b0eb8abdbf0d35f77 Mon Sep 17 00:00:00 2001 From: Tonis Tiigi Date: Sun, 18 Oct 2020 20:50:02 -0700 Subject: [PATCH 2/4] =?UTF-8?q?remotecache:=20don=E2=80=99t=20export=20cac?= =?UTF-8?q?he=20for=20empty=20layers?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Tonis Tiigi --- cache/remotecache/v1/utils.go | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/cache/remotecache/v1/utils.go b/cache/remotecache/v1/utils.go index d39eec774424..c019a64d4f9d 100644 --- a/cache/remotecache/v1/utils.go +++ b/cache/remotecache/v1/utils.go @@ -9,6 +9,10 @@ import ( "github.com/pkg/errors" ) +const ( + emptyGZLayer = digest.Digest("sha256:4f4fb700ef54461cfa02571ae0db9a0dc1e0cdb5577484a6d75e68dc38e8acc1") +) + // sortConfig sorts the config structure to make sure it is deterministic func sortConfig(cc *CacheConfig) { type indexedLayer struct { @@ -238,6 +242,10 @@ func marshalRemote(r *solver.Remote, state *marshalState) string { } desc := r.Descriptors[len(r.Descriptors)-1] + if desc.Digest == emptyGZLayer { + return parentID + } + state.descriptors[desc.Digest] = DescriptorProviderPair{ Descriptor: desc, Provider: r.Provider, From f9d7f1cf4a57661538b40417a4c919ddc940269d Mon Sep 17 00:00:00 2001 From: Tonis Tiigi Date: Sun, 18 Oct 2020 21:02:44 -0700 Subject: [PATCH 3/4] =?UTF-8?q?exporter:=20don=E2=80=99t=20keep=20timestam?= =?UTF-8?q?p=20for=20empty=20layer?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Tonis Tiigi --- exporter/containerimage/writer.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/exporter/containerimage/writer.go b/exporter/containerimage/writer.go index 589ee1201f50..e9c4aceadcfb 100644 --- a/exporter/containerimage/writer.go +++ b/exporter/containerimage/writer.go @@ -413,13 +413,13 @@ func normalizeLayersAndHistory(remote *solver.Remote, history []ocispec.History, var layerIndex int for i, h := range history { if !h.EmptyLayer { - if h.Created == nil { - h.Created = refMeta[layerIndex].createdAt - } if remote.Descriptors[layerIndex].Digest == emptyGZLayer { h.EmptyLayer = true remote.Descriptors = append(remote.Descriptors[:layerIndex], remote.Descriptors[layerIndex+1:]...) } else { + if h.Created == nil { + h.Created = refMeta[layerIndex].createdAt + } layerIndex++ } } From c8b8d6ce630af266ad0f0296611f9fa2a23e6185 Mon Sep 17 00:00:00 2001 From: Tonis Tiigi Date: Sun, 18 Oct 2020 21:55:50 -0700 Subject: [PATCH 4/4] exptypes: define empty gz layer digest Signed-off-by: Tonis Tiigi --- cache/remotecache/v1/utils.go | 7 ++----- exporter/containerimage/exptypes/types.go | 7 ++++++- exporter/containerimage/writer.go | 6 +----- snapshot/imagerefchecker/checker.go | 7 ++----- 4 files changed, 11 insertions(+), 16 deletions(-) diff --git a/cache/remotecache/v1/utils.go b/cache/remotecache/v1/utils.go index c019a64d4f9d..fc494aa5a0e1 100644 --- a/cache/remotecache/v1/utils.go +++ b/cache/remotecache/v1/utils.go @@ -4,15 +4,12 @@ import ( "fmt" "sort" + "github.com/moby/buildkit/exporter/containerimage/exptypes" "github.com/moby/buildkit/solver" digest "github.com/opencontainers/go-digest" "github.com/pkg/errors" ) -const ( - emptyGZLayer = digest.Digest("sha256:4f4fb700ef54461cfa02571ae0db9a0dc1e0cdb5577484a6d75e68dc38e8acc1") -) - // sortConfig sorts the config structure to make sure it is deterministic func sortConfig(cc *CacheConfig) { type indexedLayer struct { @@ -242,7 +239,7 @@ func marshalRemote(r *solver.Remote, state *marshalState) string { } desc := r.Descriptors[len(r.Descriptors)-1] - if desc.Digest == emptyGZLayer { + if desc.Digest == exptypes.EmptyGZLayer { return parentID } diff --git a/exporter/containerimage/exptypes/types.go b/exporter/containerimage/exptypes/types.go index 02e34eb63389..b428afd0e6b6 100644 --- a/exporter/containerimage/exptypes/types.go +++ b/exporter/containerimage/exptypes/types.go @@ -1,11 +1,16 @@ package exptypes -import specs "github.com/opencontainers/image-spec/specs-go/v1" +import ( + "github.com/opencontainers/go-digest" + specs "github.com/opencontainers/image-spec/specs-go/v1" +) const ExporterImageConfigKey = "containerimage.config" const ExporterInlineCache = "containerimage.inlinecache" const ExporterPlatformsKey = "refs.platforms" +const EmptyGZLayer = digest.Digest("sha256:4f4fb700ef54461cfa02571ae0db9a0dc1e0cdb5577484a6d75e68dc38e8acc1") + type Platforms struct { Platforms []Platform } diff --git a/exporter/containerimage/writer.go b/exporter/containerimage/writer.go index e9c4aceadcfb..d4a4613ee16f 100644 --- a/exporter/containerimage/writer.go +++ b/exporter/containerimage/writer.go @@ -27,10 +27,6 @@ import ( "golang.org/x/sync/errgroup" ) -const ( - emptyGZLayer = digest.Digest("sha256:4f4fb700ef54461cfa02571ae0db9a0dc1e0cdb5577484a6d75e68dc38e8acc1") -) - type WriterOpt struct { Snapshotter snapshot.Snapshotter ContentStore content.Store @@ -413,7 +409,7 @@ func normalizeLayersAndHistory(remote *solver.Remote, history []ocispec.History, var layerIndex int for i, h := range history { if !h.EmptyLayer { - if remote.Descriptors[layerIndex].Digest == emptyGZLayer { + if remote.Descriptors[layerIndex].Digest == exptypes.EmptyGZLayer { h.EmptyLayer = true remote.Descriptors = append(remote.Descriptors[:layerIndex], remote.Descriptors[layerIndex+1:]...) } else { diff --git a/snapshot/imagerefchecker/checker.go b/snapshot/imagerefchecker/checker.go index a8dd746ed5fa..5fe4522aab8a 100644 --- a/snapshot/imagerefchecker/checker.go +++ b/snapshot/imagerefchecker/checker.go @@ -9,15 +9,12 @@ import ( "github.com/containerd/containerd/content" "github.com/containerd/containerd/images" "github.com/moby/buildkit/cache" + "github.com/moby/buildkit/exporter/containerimage/exptypes" digest "github.com/opencontainers/go-digest" specs "github.com/opencontainers/image-spec/specs-go/v1" "github.com/pkg/errors" ) -const ( - emptyGZLayer = digest.Digest("sha256:4f4fb700ef54461cfa02571ae0db9a0dc1e0cdb5577484a6d75e68dc38e8acc1") -) - type Opt struct { ImageStore images.Store ContentStore content.Store @@ -93,7 +90,7 @@ func toDigests(layers []specs.Descriptor) []digest.Digest { func layerKey(layers []digest.Digest) string { b := &strings.Builder{} for _, l := range layers { - if l != emptyGZLayer { + if l != exptypes.EmptyGZLayer { b.Write([]byte(l)) } }