Skip to content
This repository was archived by the owner on Jun 3, 2025. It is now read-only.
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@ _If you are interested in contributing to kaniko, see
- [Flag `--no-push`](#flag---no-push)
- [Flag `--no-push-cache`](#flag---no-push-cache)
- [Flag `--oci-layout-path`](#flag---oci-layout-path)
- [Flag `--preserve-context`](#flag---preserve-context)
- [Flag `--push-retry`](#flag---push-retry)
- [Flag `--registry-certificate`](#flag---registry-certificate)
- [Flag `--registry-client-cert`](#flag---registry-client-cert)
Expand Down Expand Up @@ -970,6 +971,17 @@ _Note: Depending on the built image, the media type of the image manifest might
be either `application/vnd.oci.image.manifest.v1+json` or
`application/vnd.docker.distribution.manifest.v2+json`._

#### Flag `--preserve-context`

Set this boolean flag to `true` if you want kaniko to restore the build-context for multi-stage builds.
If set, kaniko will take a snapshot of the full filesystem before it starts building to later restore to that state. If combined with the `--cleanup` flag it will also restore the state after cleanup.

This is useful if you want to pass in secrets via files or if you want to execute commands after the build completes.

It will only take the snapshot if we are building a multistage image or if we plan to cleanup the filesystem after the build.

Defaults to `false`

#### Flag `--push-ignore-immutable-tag-errors`

Set this boolean flag to `true` if you want the Kaniko process to exit with
Expand Down
1 change: 1 addition & 0 deletions cmd/executor/cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -280,6 +280,7 @@ func addKanikoOptionsFlags() {
RootCmd.PersistentFlags().VarP(&opts.IgnorePaths, "ignore-path", "", "Ignore these paths when taking a snapshot. Set it repeatedly for multiple paths.")
RootCmd.PersistentFlags().BoolVarP(&opts.ForceBuildMetadata, "force-build-metadata", "", false, "Force add metadata layers to build image")
RootCmd.PersistentFlags().BoolVarP(&opts.SkipPushPermissionCheck, "skip-push-permission-check", "", false, "Skip check of the push permission")
RootCmd.PersistentFlags().BoolVarP(&opts.PreserveContext, "preserve-context", "", false, "Preserve build context accross build stages by taking a snapshot of the full filesystem before build and restore it after we switch stages. Restores in the end too if passed together with 'cleanup'")

// Deprecated flags.
RootCmd.PersistentFlags().StringVarP(&opts.SnapshotModeDeprecated, "snapshotMode", "", "", "This flag is deprecated. Please use '--snapshot-mode'.")
Expand Down
1 change: 1 addition & 0 deletions pkg/config/options.go
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@ type KanikoOptions struct {
ForceBuildMetadata bool
InitialFSUnpacked bool
SkipPushPermissionCheck bool
PreserveContext bool
}

type KanikoGitOptions struct {
Expand Down
66 changes: 55 additions & 11 deletions pkg/executor/build.go
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,20 @@ type stageBuilder struct {
pushLayerToCache cachePusher
}

func makeSnapshotter(opts *config.KanikoOptions) (*snapshot.Snapshotter, error) {
err := util.InitIgnoreList()
if err != nil {
return nil, errors.Wrap(err, "failed to initialize ignore list")
}

hasher, err := getHasher(opts.SnapshotMode)
if err != nil {
return nil, err
}
l := snapshot.NewLayeredMap(hasher)
return snapshot.NewSnapshotter(l, config.RootDir), nil
}

// newStageBuilder returns a new type stageBuilder which contains all the information required to build the stage
func newStageBuilder(args *dockerfile.BuildArgs, opts *config.KanikoOptions, stage config.KanikoStage, crossStageDeps map[int][]string, dcm map[string]string, sid map[string]string, stageNameToIdx map[string]string, fileContext util.FileContext) (*stageBuilder, error) {
sourceImage, err := image_util.RetrieveSourceImage(stage, opts)
Expand All @@ -101,17 +115,10 @@ func newStageBuilder(args *dockerfile.BuildArgs, opts *config.KanikoOptions, sta
return nil, err
}

err = util.InitIgnoreList()
if err != nil {
return nil, errors.Wrap(err, "failed to initialize ignore list")
}

hasher, err := getHasher(opts.SnapshotMode)
snapshotter, err := makeSnapshotter(opts)
if err != nil {
return nil, err
}
l := snapshot.NewLayeredMap(hasher)
snapshotter := snapshot.NewSnapshotter(l, config.RootDir)

digest, err := sourceImage.Digest()
if err != nil {
Expand Down Expand Up @@ -688,6 +695,23 @@ func CalculateDependencies(stages []config.KanikoStage, opts *config.KanikoOptio
return depGraph, nil
}

func restoreFilesystem(tarball string, opts *config.KanikoOptions) error {
if err := util.DeleteFilesystem(); err != nil {
return err
}
if opts.PreserveContext {
if tarball == "" {
return fmt.Errorf("context snapshot is missing")
}
_, err := util.UnpackLocalTarArchive(tarball, config.RootDir)
if err != nil {
return errors.Wrap(err, "failed to unpack context snapshot")
}
logrus.Info("Context restored")
}
return nil
}

// DoBuild executes building the Dockerfile
func DoBuild(opts *config.KanikoOptions) (v1.Image, error) {
t := timing.Start("Total Build Time")
Expand Down Expand Up @@ -722,6 +746,24 @@ func DoBuild(opts *config.KanikoOptions) (v1.Image, error) {

var args *dockerfile.BuildArgs

var tarball string
if opts.PreserveContext {
if len(kanikoStages) > 1 || opts.Cleanup {
logrus.Info("Creating snapshot of build context")
snapshotter, err := makeSnapshotter(opts)
if err != nil {
return nil, err
}

tarball, err = snapshotter.TakeSnapshotFS()
if err != nil {
return nil, err
}
} else {
logrus.Info("Skipping context snapshot as no-one requires it")
}
}

for index, stage := range kanikoStages {
sb, err := newStageBuilder(
args, opts, stage,
Expand Down Expand Up @@ -787,7 +829,8 @@ func DoBuild(opts *config.KanikoOptions) (v1.Image, error) {
}
}
if opts.Cleanup {
if err = util.DeleteFilesystem(); err != nil {
err = restoreFilesystem(tarball, opts)
if err != nil {
return nil, err
}
}
Expand Down Expand Up @@ -819,8 +862,9 @@ func DoBuild(opts *config.KanikoOptions) (v1.Image, error) {
}

// Delete the filesystem
if err := util.DeleteFilesystem(); err != nil {
return nil, errors.Wrap(err, fmt.Sprintf("deleting file system after stage %d", index))
err = restoreFilesystem(tarball, opts)
if err != nil {
return nil, err
}
}

Expand Down