Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

impl: add a retry with result function (#2837) #2853

Merged
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,8 @@ _If you are interested in contributing to kaniko, see
- [Flag `--ignore-var-run`](#flag---ignore-var-run)
- [Flag `--ignore-path`](#flag---ignore-path)
- [Flag `--image-fs-extract-retry`](#flag---image-fs-extract-retry)
- [Debug Image](#debug-image)
- [Flag `--image-download-retry`](#flag---image-download-retry)
- [Debug Image](#debug-image)
- [Security](#security)
- [Verifying Signed Kaniko Images](#verifying-signed-kaniko-images)
- [Kaniko Builds - Profiling](#kaniko-builds---profiling)
Expand Down Expand Up @@ -1094,6 +1095,10 @@ snapshot. Set it multiple times for multiple ignore paths.
Set this flag to the number of retries that should happen for the extracting an
image filesystem. Defaults to `0`.

#### Flag `--image-download-retry`

Set this flag to the number of retries that should happen when downloading the remote image. Defaults to `0`.
alevenberg marked this conversation as resolved.
Show resolved Hide resolved

### Debug Image

The kaniko executor image is based on scratch and doesn't contain a shell. We
Expand Down
1 change: 1 addition & 0 deletions cmd/executor/cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,7 @@ func addKanikoOptionsFlags() {
RootCmd.PersistentFlags().BoolVarP(&opts.SkipTLSVerifyPull, "skip-tls-verify-pull", "", false, "Pull from insecure registry ignoring TLS verify")
RootCmd.PersistentFlags().IntVar(&opts.PushRetry, "push-retry", 0, "Number of retries for the push operation")
RootCmd.PersistentFlags().IntVar(&opts.ImageFSExtractRetry, "image-fs-extract-retry", 0, "Number of retries for image FS extraction")
RootCmd.PersistentFlags().IntVar(&opts.ImageDownloadRetry, "image-download-retry", 0, "Number of retries for downloading the remote image")
RootCmd.PersistentFlags().StringVarP(&opts.KanikoDir, "kaniko-dir", "", constants.DefaultKanikoPath, "Path to the kaniko directory, this takes precedence over the KANIKO_DIR environment variable.")
RootCmd.PersistentFlags().StringVarP(&opts.TarPath, "tar-path", "", "", "Path to save the image in as a tarball instead of pushing")
RootCmd.PersistentFlags().BoolVarP(&opts.SingleSnapshot, "single-snapshot", "", false, "Take a single snapshot at the end of the build.")
Expand Down
1 change: 1 addition & 0 deletions pkg/config/options.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ type RegistryOptions struct {
InsecurePull bool
SkipTLSVerifyPull bool
PushRetry int
ImageDownloadRetry int
}

// KanikoOptions are options that are set by command line arguments
Expand Down
16 changes: 12 additions & 4 deletions pkg/image/remote/remote.go
Original file line number Diff line number Diff line change
Expand Up @@ -70,8 +70,13 @@ 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 := remoteImageFunc(ref, remoteOptions(registryMirror, opts, customPlatform)...)
if err != nil {
retryFunc := func() (v1.Image, error) {
return remoteImageFunc(ref, remoteOptions(registryMirror, opts, customPlatform)...)
}

var remoteImage v1.Image
var err error
if remoteImage, err = util.RetryWithResult(retryFunc, opts.ImageDownloadRetry, 1000); 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
}
Expand All @@ -97,9 +102,12 @@ func RetrieveRemoteImage(image string, opts config.RegistryOptions, customPlatfo

logrus.Infof("Retrieving image %s from registry %s", ref, registryName)

remoteImage, err := remoteImageFunc(ref, remoteOptions(registryName, opts, customPlatform)...)
retryFunc := func() (v1.Image, error) {
return remoteImageFunc(ref, remoteOptions(registryName, opts, customPlatform)...)
}

if remoteImage != nil {
var remoteImage v1.Image
if remoteImage, err = util.RetryWithResult(retryFunc, opts.ImageDownloadRetry, 1000); remoteImage != nil {
manifestCache[image] = remoteImage
}

Expand Down
49 changes: 46 additions & 3 deletions pkg/image/remote/remote_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,11 @@ import (
"github.com/google/go-containerregistry/pkg/v1/types"
)

const image string = "debian"

// mockImage mocks the v1.Image interface
type mockImage struct{}
type mockImage struct {
}

func (m *mockImage) ConfigFile() (*v1.ConfigFile, error) {
return nil, nil
Expand Down Expand Up @@ -79,7 +82,6 @@ func (m *mockImage) Size() (int64, error) {
}

func Test_normalizeReference(t *testing.T) {
image := "debian"
expected := "index.docker.io/library/debian:latest"

ref, err := name.ParseReference(image)
Expand Down Expand Up @@ -112,7 +114,6 @@ func Test_RetrieveRemoteImage_manifestCache(t *testing.T) {
}

func Test_RetrieveRemoteImage_skipFallback(t *testing.T) {
image := "debian"
registryMirror := "some-registry"

opts := config.RegistryOptions{
Expand Down Expand Up @@ -140,3 +141,45 @@ func Test_RetrieveRemoteImage_skipFallback(t *testing.T) {
t.Fatal("Expected call to fail because fallback to default registry is skipped")
}
}

func Test_RetryRetrieveRemoteImageSucceeds(t *testing.T) {
opts := config.RegistryOptions{
ImageDownloadRetry: 2,
}
attempts := 0
remoteImageFunc = func(ref name.Reference, options ...remote.Option) (v1.Image, error) {
if attempts < 2 {
attempts++
return nil, errors.New("no image found")
}
return &mockImage{}, nil
}

// Clean cached image
manifestCache = make(map[string]v1.Image)

if _, err := RetrieveRemoteImage(image, opts, ""); err != nil {
t.Fatal("Expected call to succeed because of retry")
}
}

func Test_NoRetryRetrieveRemoteImageFails(t *testing.T) {
opts := config.RegistryOptions{
ImageDownloadRetry: 0,
}
attempts := 0
remoteImageFunc = func(ref name.Reference, options ...remote.Option) (v1.Image, error) {
if attempts < 1 {
attempts++
return nil, errors.New("no image found")
}
return &mockImage{}, nil
}

// Clean cached image
manifestCache = make(map[string]v1.Image)

if _, err := RetrieveRemoteImage(image, opts, ""); err == nil {
t.Fatal("Expected call to fail because there is no retry")
}
}
Loading