diff --git a/CHANGELOG.md b/CHANGELOG.md index 465cfb0e7..94dc5302e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,11 +1,11 @@ -# v0.9.0 Release - 2/8/2019 +# v0.9.0 Release - 2019-02-08 ## Bug Fixes * Bug fix with volumes declared in base images during multi-stage builds * Bug fix during snapshotting multi-stage builds. * Bug fix for caching with tar output. -# v0.8.0 Release - 1/29/2019 +# v0.8.0 Release - 2019-01-29 ## New Features * Even faster snapshotting with godirwalk @@ -20,7 +20,7 @@ * Fix bug with USER command and unpacking base images. * Added COPY --from=previous stage name/number validation -# v0.7.0 Release - 12/10/2018 +# v0.7.0 Release - 2018-12-10 ## New Features * Add support for COPY --from an unrelated image @@ -34,7 +34,7 @@ * Fix bug with call loop * Fix caching for multi-step builds -# v0.6.0 Release - 11/06/2018 +# v0.6.0 Release - 2018-11-06 ## New Features * parse arg commands at the top of dockerfiles [#404](https://github.com/GoogleContainerTools/kaniko/pull/404) @@ -59,7 +59,7 @@ * fix releasing the cache warmer [#418](https://github.com/GoogleContainerTools/kaniko/pull/418) -# v0.5.0 Release - 10/16/2018 +# v0.5.0 Release - 2018-10-16 ## New Features * Persistent volume caching for base images [#383](https://github.com/GoogleContainerTools/kaniko/pull/383) @@ -78,7 +78,7 @@ * Don't cut everything after an equals sign [#381](https://github.com/GoogleContainerTools/kaniko/pull/381) -# v0.4.0 Release - 10/01/2018 +# v0.4.0 Release - 2018-10-01 ## New Features * Add a benchmark package to store and monitor timings. [#367](https://github.com/GoogleContainerTools/kaniko/pull/367) @@ -137,7 +137,7 @@ * Fix handling of the volume directive [#334](https://github.com/GoogleContainerTools/kaniko/pull/334) -# v0.3.0 Release - 7/31/2018 +# v0.3.0 Release - 2018-07-31 New Features * Local integration testing [#256](https://github.com/GoogleContainerTools/kaniko/pull/256) * Add --target flag for multistage builds [#255](https://github.com/GoogleContainerTools/kaniko/pull/255) @@ -149,7 +149,7 @@ Bug Fixes * Multi-stage errors when referencing earlier stages [#233](https://github.com/GoogleContainerTools/kaniko/issues/233) -# v0.2.0 Release - 7/09/2018 +# v0.2.0 Release - 2018-07-09 New Features * Support for adding different source contexts, including Amazon S3 [#195](https://github.com/GoogleContainerTools/kaniko/issues/195) @@ -158,7 +158,7 @@ New Features * Update go-containerregistry so kaniko works better with Harbor and Gitlab[#227](https://github.com/GoogleContainerTools/kaniko/pull/227) * Push image to multiple destinations [#184](https://github.com/GoogleContainerTools/kaniko/pull/184) -# v0.1.0 Release - 5/17/2018 +# v0.1.0 Release - 2018-05-17 New Features * The majority of Dockerfile commands are feature complete [#1](https://github.com/GoogleContainerTools/kaniko/issues/1) diff --git a/Gopkg.lock b/Gopkg.lock index 4db400e65..7b09230e2 100644 --- a/Gopkg.lock +++ b/Gopkg.lock @@ -735,6 +735,14 @@ pruneopts = "NUT" revision = "7e9a647135a142c2669943d4a4d29be015ce9392" +[[projects]] + branch = "master" + digest = "1:15057fc7395024283a7d2639b8afc61c5b6df3fe260ce06ff5834c8464f16b5c" + name = "github.com/otiai10/copy" + packages = ["."] + pruneopts = "NUT" + revision = "7e9a647135a142c2669943d4a4d29be015ce9392" + [[projects]] branch = "master" digest = "1:3bf17a6e6eaa6ad24152148a631d18662f7212e21637c2699bff3369b7f00fa2" diff --git a/README.md b/README.md index bcd437d44..18d1d4e72 100644 --- a/README.md +++ b/README.md @@ -40,6 +40,7 @@ _If you are interested in contributing to kaniko, see [DEVELOPMENT.md](DEVELOPME - [--cache-dir](#--cache-dir) - [--cache-repo](#--cache-repo) - [--cleanup](#--cleanup) + - [--digest-file](#--digest-file) - [--insecure](#--insecure) - [--insecure-pull](#--insecure-pull) - [--no-push](#--no-push) @@ -50,6 +51,7 @@ _If you are interested in contributing to kaniko, see [DEVELOPMENT.md](DEVELOPME - [--skip-tls-verify-pull](#--skip-tls-verify-pull) - [--target](#--target) - [--tarPath](#--tarpath) + - [--verbosity](#--verbosity) - [Debug Image](#debug-image) - [Security](#security) - [Comparison with Other Tools](#comparison-with-other-tools) @@ -359,9 +361,21 @@ If `--destination=gcr.io/kaniko-project/test`, then cached layers will be stored _This flag must be used in conjunction with the `--cache=true` flag._ + +#### --digest-file + +Set this flag to specify a file in the container. This file will +receive the digest of a built image. This can be used to +automatically track the exact image built by Kaniko. + +For example, setting the flag to `--digest-file=/dev/termination-log` +will write the digest to that file, which is picked up by +Kubernetes automatically as the `{{.state.terminated.message}}` +of the container. + #### --insecure-registry -Set this flag to use plain HTTP requests when accessing a registry. It is supposed to be useed for testing purposes only and should not be used in production! +Set this flag to use plain HTTP requests when accessing a registry. It is supposed to be used for testing purposes only and should not be used in production! You can set it multiple times for multiple registries. #### --skip-tls-verify-registry @@ -415,6 +429,10 @@ Set this flag to indicate which build stage is the target build stage. Set this flag as `--tarPath=` to save the image as a tarball at path instead of pushing the image. +#### --verbosity + +Set this flag as `--verbosity=` to set the logging level. Defaults to `info`. + ### Debug Image The kaniko executor image is based off of scratch and doesn't contain a shell. @@ -449,6 +467,7 @@ You may be able to achieve the same default seccomp profile that Docker uses in Similar tools include: +- [BuildKit](https://github.com/moby/buildkit) - [img](https://github.com/genuinetools/img) - [orca-build](https://github.com/cyphar/orca-build) - [umoci](https://github.com/openSUSE/umoci) @@ -458,10 +477,10 @@ Similar tools include: All of these tools build container images with different approaches. -`img` can perform as a non root user from within a container, but requires that -the `img` container has `RawProc` access to create nested containers. `kaniko` -does not actually create nested containers, so it does not require `RawProc` -access. +BuildKit (and `img`) can perform as a non root user from within a container, but requires +seccomp and AppArmor to be disabled to create nested containers. `kaniko` +does not actually create nested containers, so it does not require seccomp and AppArmor +to be disabled. `orca-build` depends on `runc` to build images from Dockerfiles, which can not run inside a container (for similar reasons to `img` above). `kaniko` doesn't diff --git a/cmd/executor/cmd/root.go b/cmd/executor/cmd/root.go index e51a03ae3..1fc2672bc 100644 --- a/cmd/executor/cmd/root.go +++ b/cmd/executor/cmd/root.go @@ -128,6 +128,7 @@ func addKanikoOptionsFlags(cmd *cobra.Command) { RootCmd.PersistentFlags().BoolVarP(&opts.NoPush, "no-push", "", false, "Do not push the image to the registry") RootCmd.PersistentFlags().StringVarP(&opts.CacheRepo, "cache-repo", "", "", "Specify a repository to use as a cache, otherwise one will be inferred from the destination provided") RootCmd.PersistentFlags().StringVarP(&opts.CacheDir, "cache-dir", "", "/cache", "Specify a local directory to use as a cache.") + RootCmd.PersistentFlags().StringVarP(&opts.DigestFile, "digest-file", "", "", "Specify a file to save the digest of the built image to.") RootCmd.PersistentFlags().BoolVarP(&opts.Cache, "cache", "", false, "Use cache when building image") RootCmd.PersistentFlags().BoolVarP(&opts.Cleanup, "cleanup", "", false, "Clean the filesystem at the end") RootCmd.PersistentFlags().DurationVarP(&opts.CacheTTL, "cache-ttl", "", time.Hour*336, "Cache timeout in hours. Defaults to two weeks.") diff --git a/integration/dockerfiles/Dockerfile_test_cache b/integration/dockerfiles/Dockerfile_test_cache index 71644d560..e3ebe304d 100644 --- a/integration/dockerfiles/Dockerfile_test_cache +++ b/integration/dockerfiles/Dockerfile_test_cache @@ -16,15 +16,7 @@ # If the image is built twice, /date should be the same in both images # if the cache is implemented correctly -FROM alpine as base_stage - -RUN mkdir foo && echo base_stage > foo/base - -FROM base_stage as cached_stage - -RUN echo cached_stage > foo/cache - -FROM cached_stage as bug_stage - -RUN echo bug_stage > foo/bug +FROM gcr.io/google-appengine/debian9@sha256:1d6a9a6d106bd795098f60f4abb7083626354fa6735e81743c7f8cfca11259f0 RUN date > /date +COPY context/foo /foo +RUN echo hey diff --git a/pkg/config/options.go b/pkg/config/options.go index 9a912f919..ff4d60a13 100644 --- a/pkg/config/options.go +++ b/pkg/config/options.go @@ -30,6 +30,7 @@ type KanikoOptions struct { Target string CacheRepo string CacheDir string + DigestFile string Destinations multiArg BuildArgs multiArg Insecure bool diff --git a/pkg/executor/build.go b/pkg/executor/build.go index 2fbf592ce..c29de8ca1 100644 --- a/pkg/executor/build.go +++ b/pkg/executor/build.go @@ -190,12 +190,7 @@ func (s *stageBuilder) optimize(compositeKey CompositeCache, cfg v1.Config) erro func (s *stageBuilder) build() error { // Set the initial cache key to be the base image digest, the build args and the SrcContext. - dgst, err := util.ReproducibleDigest(s.image) - if err != nil { - return err - } - compositeKey := NewCompositeCache(dgst) - compositeKey.AddKey(s.opts.BuildArgs...) + compositeKey := NewCompositeCache(s.baseImageDigest) // Apply optimizations to the instructions. if err := s.optimize(*compositeKey, s.cf.Config); err != nil { diff --git a/pkg/executor/push.go b/pkg/executor/push.go index 579aabc61..a02d5b9b9 100644 --- a/pkg/executor/push.go +++ b/pkg/executor/push.go @@ -19,6 +19,7 @@ package executor import ( "crypto/tls" "fmt" + "io/ioutil" "net/http" "time" @@ -74,6 +75,19 @@ func CheckPushPermissions(opts *config.KanikoOptions) error { // DoPush is responsible for pushing image to the destinations specified in opts func DoPush(image v1.Image, opts *config.KanikoOptions) error { t := timing.Start("Total Push Time") + + if opts.DigestFile != "" { + digest, err := image.Digest() + if err != nil { + return errors.Wrap(err, "error fetching digest") + } + digestByteArray := []byte(digest.String()) + err = ioutil.WriteFile(opts.DigestFile, digestByteArray, 0644) + if err != nil { + return errors.Wrap(err, "writing digest to file failed") + } + } + destRefs := []name.Tag{} for _, destination := range opts.Destinations { destRef, err := name.NewTag(destination, name.WeakValidation) diff --git a/pkg/snapshot/snapshot.go b/pkg/snapshot/snapshot.go index d87378b62..90638168d 100644 --- a/pkg/snapshot/snapshot.go +++ b/pkg/snapshot/snapshot.go @@ -178,7 +178,7 @@ func (s *Snapshotter) scanFullFilesystem() ([]string, []string, error) { return nil, nil, err } if fileChanged { - logrus.Infof("Adding %s to layer, because it was changed.", path) + logrus.Debugf("Adding %s to layer, because it was changed.", path) filesToAdd = append(filesToAdd, path) } } diff --git a/pkg/util/fs_util.go b/pkg/util/fs_util.go index 5ef48845f..5f72c7784 100644 --- a/pkg/util/fs_util.go +++ b/pkg/util/fs_util.go @@ -79,10 +79,7 @@ func GetFSFromImage(root string, img v1.Image) ([]string, error) { if err != nil { return nil, err } - - // Store a map of files to their mtime. We need to set mtimes in a second pass because creating files - // can change the mtime of a directory. - extractedFiles := map[string]time.Time{} + extractedFiles := []string{} for i, l := range layers { logrus.Debugf("Extracting layer %d", i) @@ -113,17 +110,10 @@ func GetFSFromImage(root string, img v1.Image) ([]string, error) { if err := extractFile(root, hdr, tr); err != nil { return nil, err } - extractedFiles[filepath.Join(root, filepath.Clean(hdr.Name))] = hdr.ModTime + extractedFiles = append(extractedFiles, filepath.Join(root, filepath.Clean(hdr.Name))) } } - - fileNames := []string{} - for f, t := range extractedFiles { - fileNames = append(fileNames, f) - os.Chtimes(f, time.Time{}, t) - } - - return fileNames, nil + return extractedFiles, nil } // DeleteFilesystem deletes the extracted image file system @@ -272,7 +262,6 @@ func extractFile(dest string, hdr *tar.Header, tr io.Reader) error { return err } } - return nil } @@ -377,8 +366,7 @@ func RelativeFiles(fp string, root string) ([]string, error) { } // ParentDirectories returns a list of paths to all parent directories -// Ex. /some/temp/dir -> [/some, /some/temp, /some/temp/dir] -// This purposefully excludes the /. +// Ex. /some/temp/dir -> [/, /some, /some/temp, /some/temp/dir] func ParentDirectories(path string) []string { path = filepath.Clean(path) dirs := strings.Split(path, "/") diff --git a/pkg/util/tar_util.go b/pkg/util/tar_util.go index b72785707..bc1cc67a0 100644 --- a/pkg/util/tar_util.go +++ b/pkg/util/tar_util.go @@ -54,7 +54,6 @@ func (t *Tar) Close() { // AddFileToTar adds the file at path p to the tar func (t *Tar) AddFileToTar(p string) error { - logrus.Debugf("Adding file %s to tar", p) i, err := os.Lstat(p) if err != nil { return fmt.Errorf("Failed to get file info for %s: %s", p, err) diff --git a/pkg/util/util.go b/pkg/util/util.go index e7344c88f..873cbae20 100644 --- a/pkg/util/util.go +++ b/pkg/util/util.go @@ -20,14 +20,11 @@ import ( "crypto/md5" "crypto/sha256" "encoding/hex" - "encoding/json" "io" "os" "strconv" "syscall" - "github.com/google/go-containerregistry/pkg/v1" - "github.com/google/go-containerregistry/pkg/v1/partial" "github.com/pkg/errors" "github.com/sirupsen/logrus" ) @@ -130,29 +127,3 @@ func SHA256(r io.Reader) (string, error) { } return hex.EncodeToString(hasher.Sum(make([]byte, 0, hasher.Size()))), nil } - -type ReproducibleManifest struct { - Layers []v1.Descriptor - Config v1.Config -} - -func ReproducibleDigest(img partial.WithManifestAndConfigFile) (string, error) { - mfst, err := img.Manifest() - if err != nil { - return "", err - } - cfg, err := img.ConfigFile() - if err != nil { - return "", err - } - rm := ReproducibleManifest{ - Layers: mfst.Layers, - Config: cfg.Config, - } - - b, err := json.Marshal(rm) - if err != nil { - return "", err - } - return string(b), nil -}