Allows to disable the fallback to the default registry on image pull (#2637)

* Allow to disable the fallback to the default registry on image pull

When one or more registry mirror(s) are deffined with the 'registry-mirror' argument, if none of those mirrors include the image,
the current behavior is to fallback to the default registry.
If a whitelist (or some image restriction) is applied at the mirror side, fallbacking to the default registry makes that restriction useless.
This new argument allows to skip the fallback and abort the build if the mirror rejects an image.
If it is not set, is completelly transparent.

* fix typo on command help
This commit is contained in:
Fernando Giannetti 2023-07-26 17:05:37 -03:00 committed by GitHub
parent 885ff5f57b
commit c2445c76da
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 74 additions and 3 deletions

View File

@ -96,6 +96,7 @@ _If you are interested in contributing to kaniko, see
- [Flag `--registry-certificate`](#flag---registry-certificate)
- [Flag `--registry-client-cert`](#flag---registry-client-cert)
- [Flag `--registry-mirror`](#flag---registry-mirror)
- [Flag `--skip-default-registry-fallback`](#flag---skip-default-registry-fallback)
- [Flag `--reproducible`](#flag---reproducible)
- [Flag `--single-snapshot`](#flag---single-snapshot)
- [Flag `--skip-tls-verify`](#flag---skip-tls-verify)
@ -992,6 +993,12 @@ are:
- `192.168.0.1:5000`
- `mycompany-docker-virtual.jfrog.io`
#### Flag `--skip-default-registry-fallback`
Set this flag if you want the build process to fail if none of the mirrors listed in flag [registry-mirror](#flag---registry-mirror) can pull some image. This should be used with mirrors that implements a whitelist or some image restrictions.
If [registry-mirror](#flag---registry-mirror) is not set or is empty, this flag is ignored.
#### Flag `--reproducible`
Set this flag to strip timestamps out of the built image and make it

View File

@ -238,6 +238,7 @@ func addKanikoOptionsFlags() {
opts.RegistriesClientCertificates = make(map[string]string)
RootCmd.PersistentFlags().VarP(&opts.RegistriesClientCertificates, "registry-client-cert", "", "Use the provided client certificate for mutual TLS (mTLS) communication with the given registry. Expected format is 'my.registry.url=/path/to/client/cert,/path/to/client/key'.")
RootCmd.PersistentFlags().VarP(&opts.RegistryMirrors, "registry-mirror", "", "Registry mirror to use as pull-through cache instead of docker.io. Set it repeatedly for multiple mirrors.")
RootCmd.PersistentFlags().BoolVarP(&opts.SkipDefaultRegistryFallback, "skip-default-registry-fallback", "", false, "If an image is not found on any mirrors (defined with registry-mirror) do not fallback to the default registry. If registry-mirror is not defined, this flag is ignored.")
RootCmd.PersistentFlags().BoolVarP(&opts.IgnoreVarRun, "ignore-var-run", "", true, "Ignore /var/run directory when taking image snapshot. Set it to false to preserve /var/run/ in destination image.")
RootCmd.PersistentFlags().VarP(&opts.Labels, "label", "", "Set metadata for an image. Set it repeatedly for multiple labels.")
RootCmd.PersistentFlags().BoolVarP(&opts.SkipUnusedStages, "skip-unused-stages", "", false, "Build only used stages if defined to true. Otherwise it builds by default all stages, even the unnecessaries ones until it reaches the target stage / end of Dockerfile")

View File

@ -98,6 +98,7 @@ func addKanikoOptionsFlags() {
opts.RegistriesClientCertificates = make(map[string]string)
RootCmd.PersistentFlags().VarP(&opts.RegistriesClientCertificates, "registry-client-cert", "", "Use the provided client certificate for mutual TLS (mTLS) communication with the given registry. Expected format is 'my.registry.url=/path/to/client/cert,/path/to/client/key'.")
RootCmd.PersistentFlags().VarP(&opts.RegistryMirrors, "registry-mirror", "", "Registry mirror to use as pull-through cache instead of docker.io. Set it repeatedly for multiple mirrors.")
RootCmd.PersistentFlags().BoolVarP(&opts.SkipDefaultRegistryFallback, "skip-default-registry-fallback", "", false, "If an image is not found on any mirrors (defined with registry-mirror) do not fallback to the default registry. If registry-mirror is not defined, this flag is ignored.")
RootCmd.PersistentFlags().StringVarP(&opts.CustomPlatform, "customPlatform", "", "", "Specify the build platform if different from the current host")
RootCmd.PersistentFlags().StringVarP(&opts.DockerfilePath, "dockerfile", "d", "", "Path to the dockerfile to be cached. The kaniko warmer will parse and write out each stage's base image layers to the cache-dir. Using the same dockerfile path as what you plan to build in the kaniko executor is the expected usage.")
RootCmd.PersistentFlags().VarP(&opts.BuildArgs, "build-arg", "", "This flag should be used in conjunction with the dockerfile flag for scenarios where dynamic replacement of the base image is required.")

View File

@ -395,6 +395,29 @@ func TestBuildViaRegistryMirrors(t *testing.T) {
checkContainerDiffOutput(t, diff, expected)
}
func TestBuildSkipFallback(t *testing.T) {
repo := getGitRepo(false)
dockerfile := fmt.Sprintf("%s/%s/Dockerfile_registry_mirror", integrationPath, dockerfilesPath)
// 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", "doesnotexist.example.com",
"--skip-default-registry-fallback",
"-c", fmt.Sprintf("git://%s", repo))
kanikoCmd := exec.Command("docker", dockerRunFlags...)
_, err := RunCommandWithoutTest(kanikoCmd)
if err == nil {
t.Errorf("Build should fail after using skip-default-registry-fallback and registry-mirror fail to pull")
}
}
// TestKanikoDir tests that a build that sets --kaniko-dir produces the same output as the equivalent docker build.
func TestKanikoDir(t *testing.T) {
repo := getGitRepo(false)

View File

@ -37,6 +37,7 @@ type RegistryOptions struct {
SkipTLSVerifyRegistries multiArg
RegistriesCertificates keyValueArg
RegistriesClientCertificates keyValueArg
SkipDefaultRegistryFallback bool
Insecure bool
SkipTLSVerify bool
InsecurePull bool

View File

@ -17,6 +17,7 @@ limitations under the License.
package remote
import (
"errors"
"strings"
"github.com/GoogleContainerTools/kaniko/pkg/config"
@ -32,6 +33,7 @@ import (
var (
manifestCache = make(map[string]v1.Image)
remoteImageFunc = remote.Image
)
// RetrieveRemoteImage retrieves the manifest for the specified image from the specified registry
@ -68,7 +70,7 @@ func RetrieveRemoteImage(image string, opts config.RegistryOptions, customPlatfo
ref := setNewRegistry(ref, newReg)
logrus.Infof("Retrieving image %s from registry mirror %s", ref, registryMirror)
remoteImage, err := remote.Image(ref, remoteOptions(registryMirror, opts, customPlatform)...)
remoteImage, err := remoteImageFunc(ref, remoteOptions(registryMirror, opts, customPlatform)...)
if err != nil {
logrus.Warnf("Failed to retrieve image %s from registry mirror %s: %s. Will try with the next mirror, or fallback to the default registry.", ref, registryMirror, err)
continue
@ -78,6 +80,10 @@ func RetrieveRemoteImage(image string, opts config.RegistryOptions, customPlatfo
return remoteImage, nil
}
if len(opts.RegistryMirrors) > 0 && opts.SkipDefaultRegistryFallback {
return nil, errors.New("image not found on any configured mirror(s)")
}
}
registryName := ref.Context().RegistryStr()
@ -91,7 +97,7 @@ func RetrieveRemoteImage(image string, opts config.RegistryOptions, customPlatfo
logrus.Infof("Retrieving image %s from registry %s", ref, registryName)
remoteImage, err := remote.Image(ref, remoteOptions(registryName, opts, customPlatform)...)
remoteImage, err := remoteImageFunc(ref, remoteOptions(registryName, opts, customPlatform)...)
if remoteImage != nil {
manifestCache[image] = remoteImage

View File

@ -17,11 +17,13 @@ limitations under the License.
package remote
import (
"errors"
"testing"
"github.com/GoogleContainerTools/kaniko/pkg/config"
"github.com/google/go-containerregistry/pkg/name"
v1 "github.com/google/go-containerregistry/pkg/v1"
"github.com/google/go-containerregistry/pkg/v1/remote"
"github.com/google/go-containerregistry/pkg/v1/types"
)
@ -108,3 +110,33 @@ func Test_RetrieveRemoteImage_manifestCache(t *testing.T) {
t.Fatal("Expected call to succeed because there is a manifest for this image in the cache.")
}
}
func Test_RetrieveRemoteImage_skipFallback(t *testing.T) {
image := "debian"
registryMirror := "some-registry"
opts := config.RegistryOptions{
RegistryMirrors: []string{registryMirror},
SkipDefaultRegistryFallback: false,
}
remoteImageFunc = func(ref name.Reference, options ...remote.Option) (v1.Image, error) {
if ref.Context().Registry.Name() == registryMirror {
return nil, errors.New("no image found")
}
return &mockImage{}, nil
}
if _, err := RetrieveRemoteImage(image, opts, ""); err != nil {
t.Fatal("Expected call to succeed because fallback to default registry")
}
opts.SkipDefaultRegistryFallback = true
//clean cached image
manifestCache = make(map[string]v1.Image)
if _, err := RetrieveRemoteImage(image, opts, ""); err == nil {
t.Fatal("Expected call to fail because fallback to default registry is skipped")
}
}