diff --git a/README.md b/README.md index 469967e447..7acbc73b27 100644 --- a/README.md +++ b/README.md @@ -58,6 +58,7 @@ _If you are interested in contributing to kaniko, see [DEVELOPMENT.md](DEVELOPME - [--insecure](#--insecure) - [--insecure-pull](#--insecure-pull) - [--no-push](#--no-push) + - [--registry-mirror](#--registry-mirror) - [--reproducible](#--reproducible) - [--single-snapshot](#--single-snapshot) - [--skip-tls-verify](#--skip-tls-verify) @@ -477,6 +478,10 @@ Set this flag if you want to pull images from a plain HTTP registry. It is suppo Set this flag if you only want to build the image, without pushing to a registry. +#### --registry-mirror + +Set this flag if you want to use a registry mirror instead of default `index.docker.io`. + #### --reproducible Set this flag to strip timestamps out of the built image and make it reproducible. diff --git a/cmd/executor/cmd/root.go b/cmd/executor/cmd/root.go index d145c9256e..efda975b5f 100644 --- a/cmd/executor/cmd/root.go +++ b/cmd/executor/cmd/root.go @@ -154,6 +154,7 @@ func addKanikoOptionsFlags() { RootCmd.PersistentFlags().DurationVarP(&opts.CacheTTL, "cache-ttl", "", time.Hour*336, "Cache timeout in hours. Defaults to two weeks.") RootCmd.PersistentFlags().VarP(&opts.InsecureRegistries, "insecure-registry", "", "Insecure registry using plain HTTP to push and pull. Set it repeatedly for multiple registries.") RootCmd.PersistentFlags().VarP(&opts.SkipTLSVerifyRegistries, "skip-tls-verify-registry", "", "Insecure registry ignoring TLS verify to push and pull. Set it repeatedly for multiple registries.") + RootCmd.PersistentFlags().StringVarP(&opts.RegistryMirror, "registry-mirror", "", "", "Registry mirror to use has pull-through cache instead of docker.io.") RootCmd.PersistentFlags().BoolVarP(&opts.WhitelistVarRun, "whitelist-var-run", "", true, "Ignore /var/run directory when taking image snapshot. Set it to false to preserve /var/run/ in destination image. (Default true).") } diff --git a/integration/dockerfiles/Dockerfile_registry_mirror b/integration/dockerfiles/Dockerfile_registry_mirror new file mode 100644 index 0000000000..9791923543 --- /dev/null +++ b/integration/dockerfiles/Dockerfile_registry_mirror @@ -0,0 +1,20 @@ +# Copyright 2018 Google, Inc. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Test to make sure the executor builds an image correctly +# when no files are changed + +FROM library/debian:latest +RUN echo "hey" +MAINTAINER kaniko diff --git a/integration/integration_test.go b/integration/integration_test.go index bfc27fef03..558a1c53f3 100644 --- a/integration/integration_test.go +++ b/integration/integration_test.go @@ -229,7 +229,7 @@ func getGitRepo() string { } else { branch = os.Getenv("TRAVIS_BRANCH") repoSlug = os.Getenv("TRAVIS_REPO_SLUG") - log.Printf("Travis CI pepo: %s branch: %s\n", repoSlug, branch) + log.Printf("Travis CI repo: %s branch: %s\n", repoSlug, branch) } return "github.com/" + repoSlug + "#refs/heads/" + branch } @@ -280,6 +280,51 @@ func TestGitBuildcontext(t *testing.T) { checkContainerDiffOutput(t, diff, expected) } +func TestBuildViaRegistryMirror(t *testing.T) { + repo := getGitRepo() + dockerfile := "integration/dockerfiles/Dockerfile_registry_mirror" + + // Build with docker + dockerImage := GetDockerImage(config.imageRepo, "Dockerfile_registry_mirror") + dockerCmd := exec.Command("docker", + append([]string{"build", + "-t", dockerImage, + "-f", dockerfile, + repo})...) + out, err := RunCommandWithoutTest(dockerCmd) + if err != nil { + t.Errorf("Failed to build image %s with docker command \"%s\": %s %s", dockerImage, dockerCmd.Args, err, string(out)) + } + + // Build with kaniko + kanikoImage := GetKanikoImage(config.imageRepo, "Dockerfile_registry_mirror") + dockerRunFlags := []string{"run", "--net=host"} + dockerRunFlags = addServiceAccountFlags(dockerRunFlags, config.serviceAccount) + dockerRunFlags = append(dockerRunFlags, ExecutorImage, + "-f", dockerfile, + "-d", kanikoImage, + "--registry-mirror", "us-mirror.gcr.io", + "-c", fmt.Sprintf("git://%s", repo)) + + kanikoCmd := exec.Command("docker", dockerRunFlags...) + + out, err = RunCommandWithoutTest(kanikoCmd) + if err != nil { + t.Errorf("Failed to build image %s with kaniko command \"%s\": %v %s", dockerImage, kanikoCmd.Args, err, string(out)) + } + + // container-diff + daemonDockerImage := daemonPrefix + dockerImage + containerdiffCmd := exec.Command("container-diff", "diff", "--no-cache", + daemonDockerImage, kanikoImage, + "-q", "--type=file", "--type=metadata", "--json") + diff := RunCommand(containerdiffCmd, t) + t.Logf("diff = %s", string(diff)) + + expected := fmt.Sprintf(emptyContainerDiff, dockerImage, kanikoImage, dockerImage, kanikoImage) + checkContainerDiffOutput(t, diff, expected) +} + func TestLayers(t *testing.T) { offset := map[string]int{ "Dockerfile_test_add": 12, diff --git a/pkg/config/options.go b/pkg/config/options.go index 2eb769541d..bf34f3b5cc 100644 --- a/pkg/config/options.go +++ b/pkg/config/options.go @@ -39,6 +39,7 @@ type KanikoOptions struct { DigestFile string ImageNameDigestFile string OCILayoutPath string + RegistryMirror string Destinations multiArg BuildArgs multiArg InsecureRegistries multiArg diff --git a/pkg/util/image_util.go b/pkg/util/image_util.go index 3948b18379..9f1e68e51d 100644 --- a/pkg/util/image_util.go +++ b/pkg/util/image_util.go @@ -107,11 +107,30 @@ func remoteImage(image string, opts *config.KanikoOptions) (v1.Image, error) { } registryName := ref.Context().RegistryStr() + var newReg name.Registry + toSet := false + + if opts.RegistryMirror != "" && registryName == name.DefaultRegistry { + registryName = opts.RegistryMirror + + newReg, err = name.NewRegistry(opts.RegistryMirror, name.StrictValidation) + if err != nil { + return nil, err + } + + toSet = true + } + if opts.InsecurePull || opts.InsecureRegistries.Contains(registryName) { - newReg, err := name.NewRegistry(registryName, name.WeakValidation, name.Insecure) + newReg, err = name.NewRegistry(registryName, name.WeakValidation, name.Insecure) if err != nil { return nil, err } + + toSet = true + } + + if toSet { if tag, ok := ref.(name.Tag); ok { tag.Repository.Registry = newReg ref = tag diff --git a/pkg/util/image_util_test.go b/pkg/util/image_util_test.go index 08faf0e808..2ce681500a 100644 --- a/pkg/util/image_util_test.go +++ b/pkg/util/image_util_test.go @@ -60,6 +60,7 @@ func Test_StandardImage(t *testing.T) { }, &config.KanikoOptions{}) testutil.CheckErrorAndDeepEqual(t, false, err, nil, actual) } + func Test_ScratchImage(t *testing.T) { stages, err := parse(dockerfile) if err != nil { @@ -93,6 +94,20 @@ func Test_TarImage(t *testing.T) { testutil.CheckErrorAndDeepEqual(t, false, err, nil, actual) } +func Test_ScratchImageFromMirror(t *testing.T) { + stages, err := parse(dockerfile) + if err != nil { + t.Error(err) + } + actual, err := RetrieveSourceImage(config.KanikoStage{ + Stage: stages[1], + }, &config.KanikoOptions{ + RegistryMirror: "mirror.gcr.io", + }) + expected := empty.Image + testutil.CheckErrorAndDeepEqual(t, false, err, expected, actual) +} + // parse parses the contents of a Dockerfile and returns a list of commands func parse(s string) ([]instructions.Stage, error) { p, err := parser.Parse(bytes.NewReader([]byte(s)))