Merge pull request #1300 from tejal29/create_new_run
add a new run command along with a new flag
This commit is contained in:
commit
aeaea502e9
|
|
@ -174,6 +174,7 @@ func addKanikoOptionsFlags() {
|
||||||
RootCmd.PersistentFlags().BoolVarP(&opts.IgnoreVarRun, "whitelist-var-run", "", true, "Ignore /var/run directory when taking image snapshot. Set it to false to preserve /var/run/ in destination image. (Default true).")
|
RootCmd.PersistentFlags().BoolVarP(&opts.IgnoreVarRun, "whitelist-var-run", "", true, "Ignore /var/run directory when taking image snapshot. Set it to false to preserve /var/run/ in destination image. (Default true).")
|
||||||
RootCmd.PersistentFlags().VarP(&opts.Labels, "label", "", "Set metadata for an image. Set it repeatedly for multiple labels.")
|
RootCmd.PersistentFlags().VarP(&opts.Labels, "label", "", "Set metadata for an image. Set it repeatedly for multiple labels.")
|
||||||
RootCmd.PersistentFlags().BoolVarP(&opts.SkipUnusedStages, "skip-unused-stages", "", false, "Build only used stages if defined to true. Otherwise it builds by default all stages, even the unnecessaries ones until it reaches the target stage / end of Dockerfile")
|
RootCmd.PersistentFlags().BoolVarP(&opts.SkipUnusedStages, "skip-unused-stages", "", false, "Build only used stages if defined to true. Otherwise it builds by default all stages, even the unnecessaries ones until it reaches the target stage / end of Dockerfile")
|
||||||
|
RootCmd.PersistentFlags().BoolVarP(&opts.RunV2, "use-new-run", "", false, "Experimental run command to detect file system changes. This new run command does no rely on snapshotting to detect changes.")
|
||||||
}
|
}
|
||||||
|
|
||||||
// addHiddenFlags marks certain flags as hidden from the executor help text
|
// addHiddenFlags marks certain flags as hidden from the executor help text
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,26 @@
|
||||||
|
# Copyright 2020 Google, Inc. All rights reserved.
|
||||||
|
#
|
||||||
|
# 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.
|
||||||
|
|
||||||
|
FROM debian:9.11
|
||||||
|
RUN echo "hey" > /etc/foo
|
||||||
|
RUN echo "baz" > /etc/baz
|
||||||
|
RUN cp /etc/baz /etc/bar
|
||||||
|
RUN rm /etc/baz
|
||||||
|
|
||||||
|
# Test with ARG
|
||||||
|
ARG file
|
||||||
|
RUN echo "run" > $file
|
||||||
|
|
||||||
|
RUN echo "test home" > $HOME/file
|
||||||
|
COPY context/foo $HOME/foo
|
||||||
|
|
@ -48,6 +48,7 @@ const (
|
||||||
// Arguments to build Dockerfiles with, used for both docker and kaniko builds
|
// Arguments to build Dockerfiles with, used for both docker and kaniko builds
|
||||||
var argsMap = map[string][]string{
|
var argsMap = map[string][]string{
|
||||||
"Dockerfile_test_run": {"file=/file"},
|
"Dockerfile_test_run": {"file=/file"},
|
||||||
|
"Dockerfile_test_run_new": {"file=/file"},
|
||||||
"Dockerfile_test_run_redo": {"file=/file"},
|
"Dockerfile_test_run_redo": {"file=/file"},
|
||||||
"Dockerfile_test_workdir": {"workdir=/arg/workdir"},
|
"Dockerfile_test_workdir": {"workdir=/arg/workdir"},
|
||||||
"Dockerfile_test_add": {"file=context/foo"},
|
"Dockerfile_test_add": {"file=context/foo"},
|
||||||
|
|
@ -75,6 +76,7 @@ var additionalDockerFlagsMap = map[string][]string{
|
||||||
// Arguments to build Dockerfiles with when building with kaniko
|
// Arguments to build Dockerfiles with when building with kaniko
|
||||||
var additionalKanikoFlagsMap = map[string][]string{
|
var additionalKanikoFlagsMap = map[string][]string{
|
||||||
"Dockerfile_test_add": {"--single-snapshot"},
|
"Dockerfile_test_add": {"--single-snapshot"},
|
||||||
|
"Dockerfile_test_run_new": {"--use-new-run=true"},
|
||||||
"Dockerfile_test_run_redo": {"--snapshotMode=redo"},
|
"Dockerfile_test_run_redo": {"--snapshotMode=redo"},
|
||||||
"Dockerfile_test_scratch": {"--single-snapshot"},
|
"Dockerfile_test_scratch": {"--single-snapshot"},
|
||||||
"Dockerfile_test_maintainer": {"--single-snapshot"},
|
"Dockerfile_test_maintainer": {"--single-snapshot"},
|
||||||
|
|
|
||||||
|
|
@ -51,3 +51,7 @@ func (b *BaseCommand) RequiresUnpackedFS() bool {
|
||||||
func (b *BaseCommand) ShouldCacheOutput() bool {
|
func (b *BaseCommand) ShouldCacheOutput() bool {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (b *BaseCommand) ShouldDetectDeletedFiles() bool {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -52,11 +52,17 @@ type DockerCommand interface {
|
||||||
RequiresUnpackedFS() bool
|
RequiresUnpackedFS() bool
|
||||||
|
|
||||||
ShouldCacheOutput() bool
|
ShouldCacheOutput() bool
|
||||||
|
|
||||||
|
// ShouldDetectDeletedFiles returns true if the command could delete files.
|
||||||
|
ShouldDetectDeletedFiles() bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetCommand(cmd instructions.Command, buildcontext string) (DockerCommand, error) {
|
func GetCommand(cmd instructions.Command, buildcontext string, useNewRun bool) (DockerCommand, error) {
|
||||||
switch c := cmd.(type) {
|
switch c := cmd.(type) {
|
||||||
case *instructions.RunCommand:
|
case *instructions.RunCommand:
|
||||||
|
if useNewRun {
|
||||||
|
return &RunMarkerCommand{cmd: c}, nil
|
||||||
|
}
|
||||||
return &RunCommand{cmd: c}, nil
|
return &RunCommand{cmd: c}, nil
|
||||||
case *instructions.CopyCommand:
|
case *instructions.CopyCommand:
|
||||||
return &CopyCommand{cmd: c, buildcontext: buildcontext}, nil
|
return &CopyCommand{cmd: c, buildcontext: buildcontext}, nil
|
||||||
|
|
|
||||||
|
|
@ -46,8 +46,12 @@ var (
|
||||||
)
|
)
|
||||||
|
|
||||||
func (r *RunCommand) ExecuteCommand(config *v1.Config, buildArgs *dockerfile.BuildArgs) error {
|
func (r *RunCommand) ExecuteCommand(config *v1.Config, buildArgs *dockerfile.BuildArgs) error {
|
||||||
|
return runCommandInExec(config, buildArgs, r.cmd)
|
||||||
|
}
|
||||||
|
|
||||||
|
func runCommandInExec(config *v1.Config, buildArgs *dockerfile.BuildArgs, cmdRun *instructions.RunCommand) error {
|
||||||
var newCommand []string
|
var newCommand []string
|
||||||
if r.cmd.PrependShell {
|
if cmdRun.PrependShell {
|
||||||
// This is the default shell on Linux
|
// This is the default shell on Linux
|
||||||
var shell []string
|
var shell []string
|
||||||
if len(config.Shell) > 0 {
|
if len(config.Shell) > 0 {
|
||||||
|
|
@ -56,9 +60,9 @@ func (r *RunCommand) ExecuteCommand(config *v1.Config, buildArgs *dockerfile.Bui
|
||||||
shell = append(shell, "/bin/sh", "-c")
|
shell = append(shell, "/bin/sh", "-c")
|
||||||
}
|
}
|
||||||
|
|
||||||
newCommand = append(shell, strings.Join(r.cmd.CmdLine, " "))
|
newCommand = append(shell, strings.Join(cmdRun.CmdLine, " "))
|
||||||
} else {
|
} else {
|
||||||
newCommand = r.cmd.CmdLine
|
newCommand = cmdRun.CmdLine
|
||||||
}
|
}
|
||||||
|
|
||||||
logrus.Infof("cmd: %s", newCommand[0])
|
logrus.Infof("cmd: %s", newCommand[0])
|
||||||
|
|
@ -111,7 +115,6 @@ func (r *RunCommand) ExecuteCommand(config *v1.Config, buildArgs *dockerfile.Bui
|
||||||
if err := syscall.Kill(-pgid, syscall.SIGKILL); err != nil && err.Error() != "no such process" {
|
if err := syscall.Kill(-pgid, syscall.SIGKILL); err != nil && err.Error() != "no such process" {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,108 @@
|
||||||
|
/*
|
||||||
|
Copyright 2018 Google LLC
|
||||||
|
|
||||||
|
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 commands
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/GoogleContainerTools/kaniko/pkg/dockerfile"
|
||||||
|
"github.com/GoogleContainerTools/kaniko/pkg/util"
|
||||||
|
v1 "github.com/google/go-containerregistry/pkg/v1"
|
||||||
|
"github.com/moby/buildkit/frontend/dockerfile/instructions"
|
||||||
|
"github.com/sirupsen/logrus"
|
||||||
|
)
|
||||||
|
|
||||||
|
type RunMarkerCommand struct {
|
||||||
|
BaseCommand
|
||||||
|
cmd *instructions.RunCommand
|
||||||
|
Files []string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *RunMarkerCommand) ExecuteCommand(config *v1.Config, buildArgs *dockerfile.BuildArgs) error {
|
||||||
|
// run command `touch filemarker`
|
||||||
|
logrus.Debugf("using new RunMarker command")
|
||||||
|
markerFile, err := ioutil.TempFile("", "marker")
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("could not place a marker file")
|
||||||
|
}
|
||||||
|
defer func() {
|
||||||
|
os.Remove(markerFile.Name())
|
||||||
|
}()
|
||||||
|
markerInfo, err := os.Stat(markerFile.Name())
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("could not place a marker file")
|
||||||
|
}
|
||||||
|
// introduce a delay
|
||||||
|
time.Sleep(time.Second)
|
||||||
|
if err := runCommandInExec(config, buildArgs, r.cmd); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// run command find to find all new files generate
|
||||||
|
isNewer := func(p string) (bool, error) {
|
||||||
|
fi, err := os.Stat(p)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
return fi.ModTime().After(markerInfo.ModTime()), nil
|
||||||
|
}
|
||||||
|
r.Files, _ = util.WalkFS("/", map[string]struct{}{}, isNewer)
|
||||||
|
logrus.Debugf("files changed %s", r.Files)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// String returns some information about the command for the image config
|
||||||
|
func (r *RunMarkerCommand) String() string {
|
||||||
|
return r.cmd.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *RunMarkerCommand) FilesToSnapshot() []string {
|
||||||
|
return r.Files
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *RunMarkerCommand) ProvidesFilesToSnapshot() bool {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// CacheCommand returns true since this command should be cached
|
||||||
|
func (r *RunMarkerCommand) CacheCommand(img v1.Image) DockerCommand {
|
||||||
|
|
||||||
|
return &CachingRunCommand{
|
||||||
|
img: img,
|
||||||
|
cmd: r.cmd,
|
||||||
|
extractFn: util.ExtractFile,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *RunMarkerCommand) MetadataOnly() bool {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *RunMarkerCommand) RequiresUnpackedFS() bool {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *RunMarkerCommand) ShouldCacheOutput() bool {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *RunMarkerCommand) ShouldDetectDeletedFiles() bool {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
@ -57,6 +57,7 @@ type KanikoOptions struct {
|
||||||
Cleanup bool
|
Cleanup bool
|
||||||
IgnoreVarRun bool
|
IgnoreVarRun bool
|
||||||
SkipUnusedStages bool
|
SkipUnusedStages bool
|
||||||
|
RunV2 bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// WarmerOptions are options that are set by command line arguments to the cache warmer.
|
// WarmerOptions are options that are set by command line arguments to the cache warmer.
|
||||||
|
|
|
||||||
|
|
@ -62,7 +62,7 @@ type cachePusher func(*config.KanikoOptions, string, string, string) error
|
||||||
type snapShotter interface {
|
type snapShotter interface {
|
||||||
Init() error
|
Init() error
|
||||||
TakeSnapshotFS() (string, error)
|
TakeSnapshotFS() (string, error)
|
||||||
TakeSnapshot([]string) (string, error)
|
TakeSnapshot([]string, bool) (string, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
// stageBuilder contains all fields necessary to build one stage of a Dockerfile
|
// stageBuilder contains all fields necessary to build one stage of a Dockerfile
|
||||||
|
|
@ -127,7 +127,7 @@ func newStageBuilder(opts *config.KanikoOptions, stage config.KanikoStage, cross
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, cmd := range s.stage.Commands {
|
for _, cmd := range s.stage.Commands {
|
||||||
command, err := commands.GetCommand(cmd, opts.SrcContext)
|
command, err := commands.GetCommand(cmd, opts.SrcContext, opts.RunV2)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
@ -319,7 +319,7 @@ func (s *stageBuilder) build() error {
|
||||||
}
|
}
|
||||||
|
|
||||||
initSnapshotTaken := false
|
initSnapshotTaken := false
|
||||||
if s.opts.SingleSnapshot {
|
if s.opts.SingleSnapshot || s.opts.RunV2 {
|
||||||
if err := s.initSnapshotWithTimings(); err != nil {
|
if err := s.initSnapshotWithTimings(); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
@ -372,7 +372,7 @@ func (s *stageBuilder) build() error {
|
||||||
files = command.FilesToSnapshot()
|
files = command.FilesToSnapshot()
|
||||||
timing.DefaultRun.Stop(t)
|
timing.DefaultRun.Stop(t)
|
||||||
|
|
||||||
if !s.shouldTakeSnapshot(index, files, command.ProvidesFilesToSnapshot()) {
|
if !s.shouldTakeSnapshot(index, command.MetadataOnly()) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if isCacheCommand {
|
if isCacheCommand {
|
||||||
|
|
@ -382,7 +382,7 @@ func (s *stageBuilder) build() error {
|
||||||
return errors.Wrap(err, "failed to save layer")
|
return errors.Wrap(err, "failed to save layer")
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
tarPath, err := s.takeSnapshot(files)
|
tarPath, err := s.takeSnapshot(files, command.ShouldDetectDeletedFiles())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.Wrap(err, "failed to take snapshot")
|
return errors.Wrap(err, "failed to take snapshot")
|
||||||
}
|
}
|
||||||
|
|
@ -416,7 +416,7 @@ func (s *stageBuilder) build() error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *stageBuilder) takeSnapshot(files []string) (string, error) {
|
func (s *stageBuilder) takeSnapshot(files []string, shdDelete bool) (string, error) {
|
||||||
var snapshot string
|
var snapshot string
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
|
|
@ -426,13 +426,13 @@ func (s *stageBuilder) takeSnapshot(files []string) (string, error) {
|
||||||
} else {
|
} else {
|
||||||
// Volumes are very weird. They get snapshotted in the next command.
|
// Volumes are very weird. They get snapshotted in the next command.
|
||||||
files = append(files, util.Volumes()...)
|
files = append(files, util.Volumes()...)
|
||||||
snapshot, err = s.snapshotter.TakeSnapshot(files)
|
snapshot, err = s.snapshotter.TakeSnapshot(files, shdDelete)
|
||||||
}
|
}
|
||||||
timing.DefaultRun.Stop(t)
|
timing.DefaultRun.Stop(t)
|
||||||
return snapshot, err
|
return snapshot, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *stageBuilder) shouldTakeSnapshot(index int, files []string, provideFiles bool) bool {
|
func (s *stageBuilder) shouldTakeSnapshot(index int, isMetadatCmd bool) bool {
|
||||||
isLastCommand := index == len(s.cmds)-1
|
isLastCommand := index == len(s.cmds)-1
|
||||||
|
|
||||||
// We only snapshot the very end with single snapshot mode on.
|
// We only snapshot the very end with single snapshot mode on.
|
||||||
|
|
@ -445,17 +445,8 @@ func (s *stageBuilder) shouldTakeSnapshot(index int, files []string, provideFile
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
// if command does not provide files, snapshot everything.
|
// if command is a metadata command, do not snapshot.
|
||||||
if !provideFiles {
|
return !isMetadatCmd
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
// Don't snapshot an empty list.
|
|
||||||
if len(files) == 0 {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
return true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *stageBuilder) saveSnapshotToImage(createdBy string, tarPath string) error {
|
func (s *stageBuilder) saveSnapshotToImage(createdBy string, tarPath string) error {
|
||||||
|
|
|
||||||
|
|
@ -103,9 +103,8 @@ func Test_stageBuilder_shouldTakeSnapshot(t *testing.T) {
|
||||||
cmds []commands.DockerCommand
|
cmds []commands.DockerCommand
|
||||||
}
|
}
|
||||||
type args struct {
|
type args struct {
|
||||||
index int
|
index int
|
||||||
files []string
|
metadataOnly bool
|
||||||
hasFiles bool
|
|
||||||
}
|
}
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
name string
|
name string
|
||||||
|
|
@ -158,9 +157,8 @@ func Test_stageBuilder_shouldTakeSnapshot(t *testing.T) {
|
||||||
stage: config.KanikoStage{},
|
stage: config.KanikoStage{},
|
||||||
},
|
},
|
||||||
args: args{
|
args: args{
|
||||||
index: 0,
|
index: 0,
|
||||||
files: []string{},
|
metadataOnly: true,
|
||||||
hasFiles: true,
|
|
||||||
},
|
},
|
||||||
want: false,
|
want: false,
|
||||||
},
|
},
|
||||||
|
|
@ -172,9 +170,8 @@ func Test_stageBuilder_shouldTakeSnapshot(t *testing.T) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
args: args{
|
args: args{
|
||||||
index: 0,
|
index: 0,
|
||||||
files: nil,
|
metadataOnly: false,
|
||||||
hasFiles: false,
|
|
||||||
},
|
},
|
||||||
want: true,
|
want: true,
|
||||||
},
|
},
|
||||||
|
|
@ -204,7 +201,7 @@ func Test_stageBuilder_shouldTakeSnapshot(t *testing.T) {
|
||||||
opts: tt.fields.opts,
|
opts: tt.fields.opts,
|
||||||
cmds: tt.fields.cmds,
|
cmds: tt.fields.cmds,
|
||||||
}
|
}
|
||||||
if got := s.shouldTakeSnapshot(tt.args.index, tt.args.files, tt.args.hasFiles); got != tt.want {
|
if got := s.shouldTakeSnapshot(tt.args.index, tt.args.metadataOnly); got != tt.want {
|
||||||
t.Errorf("stageBuilder.shouldTakeSnapshot() = %v, want %v", got, tt.want)
|
t.Errorf("stageBuilder.shouldTakeSnapshot() = %v, want %v", got, tt.want)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
@ -1246,6 +1243,7 @@ func getCommands(dir string, cmds []instructions.Command) []commands.DockerComma
|
||||||
cmd, err := commands.GetCommand(
|
cmd, err := commands.GetCommand(
|
||||||
c,
|
c,
|
||||||
dir,
|
dir,
|
||||||
|
false,
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
|
|
|
||||||
|
|
@ -38,7 +38,7 @@ func (f fakeSnapShotter) Init() error { return nil }
|
||||||
func (f fakeSnapShotter) TakeSnapshotFS() (string, error) {
|
func (f fakeSnapShotter) TakeSnapshotFS() (string, error) {
|
||||||
return f.tarPath, nil
|
return f.tarPath, nil
|
||||||
}
|
}
|
||||||
func (f fakeSnapShotter) TakeSnapshot(_ []string) (string, error) {
|
func (f fakeSnapShotter) TakeSnapshot(_ []string, _ bool) (string, error) {
|
||||||
return f.tarPath, nil
|
return f.tarPath, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -73,6 +73,9 @@ func (m MockDockerCommand) RequiresUnpackedFS() bool {
|
||||||
func (m MockDockerCommand) ShouldCacheOutput() bool {
|
func (m MockDockerCommand) ShouldCacheOutput() bool {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
func (m MockDockerCommand) ShouldDetectDeletedFiles() bool {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
type MockCachedDockerCommand struct {
|
type MockCachedDockerCommand struct {
|
||||||
contextFiles []string
|
contextFiles []string
|
||||||
|
|
@ -93,6 +96,9 @@ func (m MockCachedDockerCommand) ProvidesFilesToSnapshot() bool {
|
||||||
func (m MockCachedDockerCommand) CacheCommand(image v1.Image) commands.DockerCommand {
|
func (m MockCachedDockerCommand) CacheCommand(image v1.Image) commands.DockerCommand {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
func (m MockCachedDockerCommand) ShouldDetectDeletedFiles() bool {
|
||||||
|
return false
|
||||||
|
}
|
||||||
func (m MockCachedDockerCommand) FilesUsedFromContext(c *v1.Config, args *dockerfile.BuildArgs) ([]string, error) {
|
func (m MockCachedDockerCommand) FilesUsedFromContext(c *v1.Config, args *dockerfile.BuildArgs) ([]string, error) {
|
||||||
return m.contextFiles, nil
|
return m.contextFiles, nil
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -31,7 +31,7 @@ import (
|
||||||
|
|
||||||
type LayeredMap struct {
|
type LayeredMap struct {
|
||||||
layers []map[string]string
|
layers []map[string]string
|
||||||
whiteouts []map[string]string
|
whiteouts []map[string]struct{}
|
||||||
layerHashCache map[string]string
|
layerHashCache map[string]string
|
||||||
hasher func(string) (string, error)
|
hasher func(string) (string, error)
|
||||||
// cacheHasher doesn't include mtime in it's hash so that filesystem cache keys are stable
|
// cacheHasher doesn't include mtime in it's hash so that filesystem cache keys are stable
|
||||||
|
|
@ -49,7 +49,7 @@ func NewLayeredMap(h func(string) (string, error), c func(string) (string, error
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *LayeredMap) Snapshot() {
|
func (l *LayeredMap) Snapshot() {
|
||||||
l.whiteouts = append(l.whiteouts, map[string]string{})
|
l.whiteouts = append(l.whiteouts, map[string]struct{}{})
|
||||||
l.layers = append(l.layers, map[string]string{})
|
l.layers = append(l.layers, map[string]string{})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -84,21 +84,21 @@ func (l *LayeredMap) Get(s string) (string, bool) {
|
||||||
return "", false
|
return "", false
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *LayeredMap) GetWhiteout(s string) (string, bool) {
|
func (l *LayeredMap) GetWhiteout(s string) bool {
|
||||||
for i := len(l.whiteouts) - 1; i >= 0; i-- {
|
for i := len(l.whiteouts) - 1; i >= 0; i-- {
|
||||||
if v, ok := l.whiteouts[i][s]; ok {
|
if _, ok := l.whiteouts[i][s]; ok {
|
||||||
return v, ok
|
return ok
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return "", false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *LayeredMap) MaybeAddWhiteout(s string) bool {
|
func (l *LayeredMap) MaybeAddWhiteout(s string) bool {
|
||||||
whiteout, ok := l.GetWhiteout(s)
|
ok := l.GetWhiteout(s)
|
||||||
if ok && whiteout == s {
|
if ok {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
l.whiteouts[len(l.whiteouts)-1][s] = s
|
l.whiteouts[len(l.whiteouts)-1][s] = struct{}{}
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -24,13 +24,11 @@ import (
|
||||||
"sort"
|
"sort"
|
||||||
"syscall"
|
"syscall"
|
||||||
|
|
||||||
|
"github.com/GoogleContainerTools/kaniko/pkg/config"
|
||||||
"github.com/GoogleContainerTools/kaniko/pkg/filesystem"
|
"github.com/GoogleContainerTools/kaniko/pkg/filesystem"
|
||||||
"github.com/GoogleContainerTools/kaniko/pkg/timing"
|
"github.com/GoogleContainerTools/kaniko/pkg/timing"
|
||||||
"github.com/karrick/godirwalk"
|
|
||||||
|
|
||||||
"github.com/GoogleContainerTools/kaniko/pkg/config"
|
|
||||||
|
|
||||||
"github.com/GoogleContainerTools/kaniko/pkg/util"
|
"github.com/GoogleContainerTools/kaniko/pkg/util"
|
||||||
|
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -62,7 +60,7 @@ func (s *Snapshotter) Key() (string, error) {
|
||||||
|
|
||||||
// TakeSnapshot takes a snapshot of the specified files, avoiding directories in the ignorelist, and creates
|
// TakeSnapshot takes a snapshot of the specified files, avoiding directories in the ignorelist, and creates
|
||||||
// a tarball of the changed files. Return contents of the tarball, and whether or not any files were changed
|
// a tarball of the changed files. Return contents of the tarball, and whether or not any files were changed
|
||||||
func (s *Snapshotter) TakeSnapshot(files []string) (string, error) {
|
func (s *Snapshotter) TakeSnapshot(files []string, shdCheckDelete bool) (string, error) {
|
||||||
f, err := ioutil.TempFile(config.KanikoDir, "")
|
f, err := ioutil.TempFile(config.KanikoDir, "")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
|
|
@ -81,7 +79,7 @@ func (s *Snapshotter) TakeSnapshot(files []string) (string, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
logrus.Info("Taking snapshot of files...")
|
logrus.Info("Taking snapshot of files...")
|
||||||
logrus.Debugf("Taking snapshot of files %v", files)
|
logrus.Debugf("Taking snapshot of files %v", filesToAdd)
|
||||||
|
|
||||||
sort.Strings(filesToAdd)
|
sort.Strings(filesToAdd)
|
||||||
|
|
||||||
|
|
@ -92,9 +90,27 @@ func (s *Snapshotter) TakeSnapshot(files []string) (string, error) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Get whiteout paths
|
||||||
|
filesToWhiteout := []string{}
|
||||||
|
if shdCheckDelete {
|
||||||
|
_, deletedFiles := util.WalkFS(s.directory, s.l.getFlattenedPathsForWhiteOut(), func(s string) (bool, error) {
|
||||||
|
return true, nil
|
||||||
|
})
|
||||||
|
// The paths left here are the ones that have been deleted in this layer.
|
||||||
|
for path := range deletedFiles {
|
||||||
|
// Only add the whiteout if the directory for the file still exists.
|
||||||
|
dir := filepath.Dir(path)
|
||||||
|
if _, ok := deletedFiles[dir]; !ok {
|
||||||
|
if s.l.MaybeAddWhiteout(path) {
|
||||||
|
logrus.Debugf("Adding whiteout for %s", path)
|
||||||
|
filesToWhiteout = append(filesToWhiteout, path)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
t := util.NewTar(f)
|
t := util.NewTar(f)
|
||||||
defer t.Close()
|
defer t.Close()
|
||||||
if err := writeToTar(t, filesToAdd, nil); err != nil {
|
if err := writeToTar(t, filesToAdd, filesToWhiteout); err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
return f.Name(), nil
|
return f.Name(), nil
|
||||||
|
|
@ -133,36 +149,8 @@ func (s *Snapshotter) scanFullFilesystem() ([]string, []string, error) {
|
||||||
|
|
||||||
s.l.Snapshot()
|
s.l.Snapshot()
|
||||||
|
|
||||||
timer := timing.Start("Walking filesystem")
|
changedPaths, deletedPaths := util.WalkFS(s.directory, s.l.getFlattenedPathsForWhiteOut(), s.l.CheckFileChange)
|
||||||
|
timer := timing.Start("Resolving Paths")
|
||||||
changedPaths := make([]string, 0)
|
|
||||||
|
|
||||||
// Get a list of all the files that existed before this layer
|
|
||||||
existingPaths := s.l.getFlattenedPathsForWhiteOut()
|
|
||||||
|
|
||||||
godirwalk.Walk(s.directory, &godirwalk.Options{
|
|
||||||
Callback: func(path string, ent *godirwalk.Dirent) error {
|
|
||||||
if util.IsInIgnoreList(path) {
|
|
||||||
if util.IsDestDir(path) {
|
|
||||||
logrus.Tracef("Skipping paths under %s, as it is a ignored directory", path)
|
|
||||||
return filepath.SkipDir
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
if ok, err := s.l.CheckFileChange(path); err != nil {
|
|
||||||
return err
|
|
||||||
} else if ok {
|
|
||||||
changedPaths = append(changedPaths, path)
|
|
||||||
}
|
|
||||||
delete(existingPaths, path)
|
|
||||||
return nil
|
|
||||||
},
|
|
||||||
Unsorted: true,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
timing.DefaultRun.Stop(timer)
|
|
||||||
timer = timing.Start("Resolving Paths")
|
|
||||||
|
|
||||||
filesToAdd := []string{}
|
filesToAdd := []string{}
|
||||||
resolvedFiles, err := filesystem.ResolvePaths(changedPaths, s.ignorelist)
|
resolvedFiles, err := filesystem.ResolvePaths(changedPaths, s.ignorelist)
|
||||||
|
|
@ -179,10 +167,10 @@ func (s *Snapshotter) scanFullFilesystem() ([]string, []string, error) {
|
||||||
|
|
||||||
// The paths left here are the ones that have been deleted in this layer.
|
// The paths left here are the ones that have been deleted in this layer.
|
||||||
filesToWhiteOut := []string{}
|
filesToWhiteOut := []string{}
|
||||||
for path := range existingPaths {
|
for path := range deletedPaths {
|
||||||
// Only add the whiteout if the directory for the file still exists.
|
// Only add the whiteout if the directory for the file still exists.
|
||||||
dir := filepath.Dir(path)
|
dir := filepath.Dir(path)
|
||||||
if _, ok := existingPaths[dir]; !ok {
|
if _, ok := deletedPaths[dir]; !ok {
|
||||||
if s.l.MaybeAddWhiteout(path) {
|
if s.l.MaybeAddWhiteout(path) {
|
||||||
logrus.Debugf("Adding whiteout for %s", path)
|
logrus.Debugf("Adding whiteout for %s", path)
|
||||||
filesToWhiteOut = append(filesToWhiteOut, path)
|
filesToWhiteOut = append(filesToWhiteOut, path)
|
||||||
|
|
|
||||||
|
|
@ -214,7 +214,7 @@ func TestSnapshotFiles(t *testing.T) {
|
||||||
filesToSnapshot := []string{
|
filesToSnapshot := []string{
|
||||||
filepath.Join(testDir, "foo"),
|
filepath.Join(testDir, "foo"),
|
||||||
}
|
}
|
||||||
tarPath, err := snapshotter.TakeSnapshot(filesToSnapshot)
|
tarPath, err := snapshotter.TakeSnapshot(filesToSnapshot, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
@ -361,7 +361,7 @@ func TestSnasphotPreservesFileOrder(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Take a snapshot
|
// Take a snapshot
|
||||||
tarPath, err := snapshotter.TakeSnapshot(filesToSnapshot)
|
tarPath, err := snapshotter.TakeSnapshot(filesToSnapshot, false)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Error taking snapshot of fs: %s", err)
|
t.Fatalf("Error taking snapshot of fs: %s", err)
|
||||||
|
|
|
||||||
|
|
@ -31,14 +31,16 @@ import (
|
||||||
"syscall"
|
"syscall"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
otiai10Cpy "github.com/otiai10/copy"
|
|
||||||
|
|
||||||
"github.com/GoogleContainerTools/kaniko/pkg/config"
|
|
||||||
"github.com/docker/docker/builder/dockerignore"
|
"github.com/docker/docker/builder/dockerignore"
|
||||||
"github.com/docker/docker/pkg/fileutils"
|
"github.com/docker/docker/pkg/fileutils"
|
||||||
v1 "github.com/google/go-containerregistry/pkg/v1"
|
v1 "github.com/google/go-containerregistry/pkg/v1"
|
||||||
|
"github.com/karrick/godirwalk"
|
||||||
|
otiai10Cpy "github.com/otiai10/copy"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
|
|
||||||
|
"github.com/GoogleContainerTools/kaniko/pkg/config"
|
||||||
|
"github.com/GoogleContainerTools/kaniko/pkg/timing"
|
||||||
)
|
)
|
||||||
|
|
||||||
const DoNotChangeUID = -1
|
const DoNotChangeUID = -1
|
||||||
|
|
@ -875,3 +877,31 @@ func UpdateInitialIgnoreList(ignoreVarRun bool) {
|
||||||
PrefixMatchOnly: false,
|
PrefixMatchOnly: false,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func WalkFS(dir string, existingPaths map[string]struct{}, f func(string) (bool, error)) ([]string, map[string]struct{}) {
|
||||||
|
foundPaths := make([]string, 0)
|
||||||
|
timer := timing.Start("Walking filesystem")
|
||||||
|
godirwalk.Walk(dir, &godirwalk.Options{
|
||||||
|
Callback: func(path string, ent *godirwalk.Dirent) error {
|
||||||
|
if IsInIgnoreList(path) {
|
||||||
|
if IsDestDir(path) {
|
||||||
|
logrus.Tracef("Skipping paths under %s, as it is a ignored directory", path)
|
||||||
|
return filepath.SkipDir
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
delete(existingPaths, path)
|
||||||
|
if t, err := f(path); err != nil {
|
||||||
|
return err
|
||||||
|
} else if t {
|
||||||
|
foundPaths = append(foundPaths, path)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
Unsorted: true,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
timing.DefaultRun.Stop(timer)
|
||||||
|
return foundPaths, existingPaths
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,7 @@ set -e
|
||||||
|
|
||||||
TESTS=$(./scripts/integration-test.sh -list=Test -mod=vendor)
|
TESTS=$(./scripts/integration-test.sh -list=Test -mod=vendor)
|
||||||
|
|
||||||
TESTS=$(echo $TESTS | tr ' ' '\n' | grep 'Test'| grep -v 'TestRun' | grep -v 'TestLayers' | grep -v 'TestK8s')
|
TESTS=$(echo $TESTS | tr ' ' '\n' | grep 'Test'| grep -v 'TestRun' | grep -v 'TestLayers' | grep -v 'TestK8s' | grep -v 'TestSnapshotBenchmark')
|
||||||
|
|
||||||
RUN_ARG=''
|
RUN_ARG=''
|
||||||
count=0
|
count=0
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue