diff --git a/pkg/config/options.go b/pkg/config/options.go index 35157e8cb..d368db139 100644 --- a/pkg/config/options.go +++ b/pkg/config/options.go @@ -82,6 +82,7 @@ type KanikoOptions struct { CacheCopyLayers bool CacheRunLayers bool ForceBuildMetadata bool + InitialFSUnpacked bool } type KanikoGitOptions struct { diff --git a/pkg/executor/build.go b/pkg/executor/build.go index 7e57896fa..e27a02605 100644 --- a/pkg/executor/build.go +++ b/pkg/executor/build.go @@ -55,6 +55,7 @@ const emptyTarSize = 1024 // for testing var ( initializeConfig = initConfig + getFSFromImage = util.GetFSFromImage ) type cachePusher func(*config.KanikoOptions, string, string, string) error @@ -322,12 +323,15 @@ func (s *stageBuilder) build() error { if len(s.crossStageDeps[s.stage.Index]) > 0 { shouldUnpack = true } + if s.stage.Index == 0 && s.opts.InitialFSUnpacked { + shouldUnpack = false + } if shouldUnpack { t := timing.Start("FS Unpacking") retryFunc := func() error { - _, err := util.GetFSFromImage(config.RootDir, s.image, util.ExtractFile) + _, err := getFSFromImage(config.RootDir, s.image, util.ExtractFile) return err } @@ -622,7 +626,6 @@ func DoBuild(opts *config.KanikoOptions) (v1.Image, error) { var args *dockerfile.BuildArgs for index, stage := range kanikoStages { - sb, err := newStageBuilder( args, opts, stage, crossStageDependencies, diff --git a/pkg/executor/build_test.go b/pkg/executor/build_test.go index 99fd13497..0fa3652f8 100644 --- a/pkg/executor/build_test.go +++ b/pkg/executor/build_test.go @@ -727,17 +727,20 @@ func Test_stageBuilder_populateCompositeKey(t *testing.T) { func Test_stageBuilder_build(t *testing.T) { type testcase struct { - description string - opts *config.KanikoOptions - args map[string]string - layerCache *fakeLayerCache - expectedCacheKeys []string - pushedCacheKeys []string - commands []commands.DockerCommand - fileName string - rootDir string - image v1.Image - config *v1.ConfigFile + description string + opts *config.KanikoOptions + args map[string]string + layerCache *fakeLayerCache + expectedCacheKeys []string + pushedCacheKeys []string + commands []commands.DockerCommand + fileName string + rootDir string + image v1.Image + config *v1.ConfigFile + stage config.KanikoStage + crossStageDeps map[int][]string + mockGetFSFromImage func(root string, img v1.Image, extract util.ExtractFunction) ([]string, error) } testCases := []testcase{ @@ -1238,6 +1241,15 @@ RUN foobar rootDir: dir, } }(), + { + description: "fs unpacked", + opts: &config.KanikoOptions{InitialFSUnpacked: true}, + stage: config.KanikoStage{Index: 0}, + crossStageDeps: map[int][]string{0: {"some-dep"}}, + mockGetFSFromImage: func(root string, img v1.Image, extract util.ExtractFunction) ([]string, error) { + return nil, fmt.Errorf("getFSFromImage shouldn't be called if fs is already unpacked") + }, + }, } for _, tc := range testCases { t.Run(tc.description, func(t *testing.T) { @@ -1294,6 +1306,13 @@ RUN foobar if tc.rootDir != "" { config.RootDir = tc.rootDir } + sb.stage = tc.stage + sb.crossStageDeps = tc.crossStageDeps + if tc.mockGetFSFromImage != nil { + original := getFSFromImage + defer func() { getFSFromImage = original }() + getFSFromImage = tc.mockGetFSFromImage + } err := sb.build() if err != nil { t.Errorf("Expected error to be nil but was %v", err)