This commit is contained in:
Martin Zihlmann 2025-06-03 16:52:58 +02:00 committed by GitHub
commit 4ea6233c3c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 69 additions and 11 deletions

View File

@ -101,6 +101,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)
@ -978,6 +979,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

View File

@ -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'.")

View File

@ -91,6 +91,7 @@ type KanikoOptions struct {
ForceBuildMetadata bool
InitialFSUnpacked bool
SkipPushPermissionCheck bool
PreserveContext bool
}
type KanikoGitOptions struct {

View File

@ -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)
@ -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 {
@ -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")
@ -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,
@ -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
}
}
@ -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
}
}