diff --git a/README.md b/README.md index 61e516ca9..4af13b69e 100644 --- a/README.md +++ b/README.md @@ -62,6 +62,7 @@ _If you are interested in contributing to kaniko, see [DEVELOPMENT.md](DEVELOPME - [--force](#--force) - [--git](#--git) - [--image-name-with-digest-file](#--image-name-with-digest-file) + - [--image-name-tag-with-digest-file](#--image-name-tag-with-digest-file) - [--insecure](#--insecure) - [--insecure-pull](#--insecure-pull) - [--insecure-registry](#--insecure-registry) @@ -458,9 +459,9 @@ docker run -ti --rm -e GOOGLE_APPLICATION_CREDENTIALS=/kaniko/config.json \ ``` #### Pushing to GCR using Workload Identity -If you have enabled Workload Indentity on your GKE cluster then you can use the workload identity to push built images to GCR without adding a `GOOGLE_APPLICATION_CREDENTIALS` in your kaniko pod specification. +If you have enabled Workload Indentity on your GKE cluster then you can use the workload identity to push built images to GCR without adding a `GOOGLE_APPLICATION_CREDENTIALS` in your kaniko pod specification. -Learn more on how to [enable](https://cloud.google.com/kubernetes-engine/docs/how-to/workload-identity#enable_on_cluster) and [migrate existing apps](https://cloud.google.com/kubernetes-engine/docs/how-to/workload-identity#migrate_applications_to) to workload identity. +Learn more on how to [enable](https://cloud.google.com/kubernetes-engine/docs/how-to/workload-identity#enable_on_cluster) and [migrate existing apps](https://cloud.google.com/kubernetes-engine/docs/how-to/workload-identity#migrate_applications_to) to workload identity. To authenticate using workload identity you need to run the kaniko pod using the Kubernetes Service Account (KSA) bound to Google Service Account (GSA) which as `Storage.Admin` permissions to push images to Google Container registry. @@ -608,6 +609,9 @@ Branch to clone if build context is a git repository (default branch=,single-bra Specify a file to save the image name w/ digest of the built image to. +#### --image-name-tag-with-digest-file +Specify a file to save the image name w/ image tag and digest of the built image to. + #### --insecure Set this flag if you want to push images to a plain HTTP registry. It is supposed to be used for testing purposes only and should not be used in production! @@ -765,7 +769,7 @@ You may be able to achieve the same default seccomp profile that Docker uses in ## Kaniko Builds - Profiling If your builds are taking long, we recently added support to analyze kaniko function calls using [Slow Jam](https://github.com/google/slowjam) -To start profiling, +To start profiling, 1. Add an environment variable `STACKLOG_PATH` to your [pod definition](https://github.com/GoogleContainerTools/kaniko/blob/master/examples/pod-build-profile.yaml#L15). 2. If you are using the kaniko `debug` image, you can copy the file in the `pre-stop` container lifecyle hook. diff --git a/cmd/executor/cmd/root.go b/cmd/executor/cmd/root.go index f98a0d4a3..725dd0777 100644 --- a/cmd/executor/cmd/root.go +++ b/cmd/executor/cmd/root.go @@ -83,6 +83,9 @@ var RootCmd = &cobra.Command{ if len(opts.Destinations) == 0 && opts.ImageNameDigestFile != "" { return errors.New("You must provide --destination if setting ImageNameDigestFile") } + if len(opts.Destinations) == 0 && opts.ImageNameTagDigestFile != "" { + return errors.New("You must provide --destination if setting ImageNameTagDigestFile") + } // Update ignored paths util.UpdateInitialIgnoreList(opts.IgnoreVarRun) } @@ -165,6 +168,7 @@ func addKanikoOptionsFlags() { 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().StringVarP(&opts.ImageNameDigestFile, "image-name-with-digest-file", "", "", "Specify a file to save the image name w/ digest of the built image to.") + RootCmd.PersistentFlags().StringVarP(&opts.ImageNameTagDigestFile, "image-name-tag-with-digest-file", "", "", "Specify a file to save the image name w/ image tag w/ digest of the built image to.") RootCmd.PersistentFlags().StringVarP(&opts.OCILayoutPath, "oci-layout-path", "", "", "Path to save the OCI image layout of the built image.") 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") @@ -301,6 +305,7 @@ func resolveRelativePaths() error { &opts.TarPath, &opts.DigestFile, &opts.ImageNameDigestFile, + &opts.ImageNameTagDigestFile, } for _, p := range optsPaths { diff --git a/pkg/config/options.go b/pkg/config/options.go index 07b539188..bab6962f4 100644 --- a/pkg/config/options.go +++ b/pkg/config/options.go @@ -46,30 +46,31 @@ type RegistryOptions struct { type KanikoOptions struct { CacheOptions RegistryOptions - DockerfilePath string - SrcContext string - SnapshotMode string - CustomPlatform string - Bucket string - TarPath string - Target string - CacheRepo string - DigestFile string - ImageNameDigestFile string - OCILayoutPath string - Destinations multiArg - BuildArgs multiArg - Labels multiArg - SingleSnapshot bool - Reproducible bool - NoPush bool - Cache bool - Cleanup bool - IgnoreVarRun bool - SkipUnusedStages bool - RunV2 bool - CacheCopyLayers bool - Git KanikoGitOptions + DockerfilePath string + SrcContext string + SnapshotMode string + CustomPlatform string + Bucket string + TarPath string + Target string + CacheRepo string + DigestFile string + ImageNameDigestFile string + ImageNameTagDigestFile string + OCILayoutPath string + Destinations multiArg + BuildArgs multiArg + Labels multiArg + SingleSnapshot bool + Reproducible bool + NoPush bool + Cache bool + Cleanup bool + IgnoreVarRun bool + SkipUnusedStages bool + RunV2 bool + CacheCopyLayers bool + Git KanikoGitOptions } type KanikoGitOptions struct { diff --git a/pkg/executor/push.go b/pkg/executor/push.go index 9fe0274f8..89b9e72f4 100644 --- a/pkg/executor/push.go +++ b/pkg/executor/push.go @@ -169,7 +169,7 @@ func DoPush(image v1.Image, opts *config.KanikoOptions) error { t := timing.Start("Total Push Time") var digestByteArray []byte var builder strings.Builder - if opts.DigestFile != "" || opts.ImageNameDigestFile != "" { + if opts.DigestFile != "" || opts.ImageNameDigestFile != "" || opts.ImageNameTagDigestFile != "" { var err error digestByteArray, err = getDigest(image) if err != nil { @@ -200,8 +200,12 @@ func DoPush(image v1.Image, opts *config.KanikoOptions) error { if err != nil { return errors.Wrap(err, "getting tag for destination") } - if opts.ImageNameDigestFile != "" { - imageName := []byte(destRef.Repository.Name() + "@") + if opts.ImageNameDigestFile != "" || opts.ImageNameTagDigestFile != "" { + tag := "" + if opts.ImageNameTagDigestFile != "" && destRef.TagStr() != "" { + tag = ":" + destRef.TagStr() + } + imageName := []byte(destRef.Repository.Name() + tag + "@") builder.Write(append(imageName, digestByteArray...)) builder.WriteString("\n") } @@ -215,6 +219,13 @@ func DoPush(image v1.Image, opts *config.KanikoOptions) error { } } + if opts.ImageNameTagDigestFile != "" { + err := ioutil.WriteFile(opts.ImageNameTagDigestFile, []byte(builder.String()), 0644) + if err != nil { + return errors.Wrap(err, "writing image name with image tag and digest to file failed") + } + } + if opts.TarPath != "" { tagToImage := map[name.Tag]v1.Image{} diff --git a/pkg/executor/push_test.go b/pkg/executor/push_test.go index 870c5073d..5a274df06 100644 --- a/pkg/executor/push_test.go +++ b/pkg/executor/push_test.go @@ -333,6 +333,36 @@ func TestImageNameDigestFile(t *testing.T) { } +func TestImageNameTagDigestFile(t *testing.T) { + image, err := random.Image(1024, 4) + if err != nil { + t.Fatalf("could not create image: %s", err) + } + + digest, err := image.Digest() + if err != nil { + t.Fatalf("could not get image digest: %s", err) + } + + opts := config.KanikoOptions{ + NoPush: true, + Destinations: []string{"gcr.io/foo/bar:123", "bob/image"}, + ImageNameTagDigestFile: "tmpFile", + } + + defer os.Remove("tmpFile") + + if err := DoPush(image, &opts); err != nil { + t.Fatalf("could not push image: %s", err) + } + + want := []byte("gcr.io/foo/bar:123@" + digest.String() + "\nindex.docker.io/bob/image:latest@" + digest.String() + "\n") + + got, err := ioutil.ReadFile("tmpFile") + + testutil.CheckErrorAndDeepEqual(t, false, err, want, got) +} + var calledExecCommand = []bool{} var calledCheckPushPermission = false