feat(warmer): Warmer now supports all registry-related flags (#1499)
which means we can now: - set up one or more mirrors - set up registries certificates - skip TLS verify - use plain HTTP using the same set of flags that are defined for the executor
This commit is contained in:
parent
454910f613
commit
dde98a8e73
|
|
@ -75,6 +75,14 @@ func addKanikoOptionsFlags() {
|
|||
RootCmd.PersistentFlags().StringVarP(&opts.CacheDir, "cache-dir", "c", "/cache", "Directory of the cache.")
|
||||
RootCmd.PersistentFlags().BoolVarP(&opts.Force, "force", "f", false, "Force cache overwriting.")
|
||||
RootCmd.PersistentFlags().DurationVarP(&opts.CacheTTL, "cache-ttl", "", time.Hour*336, "Cache timeout in hours. Defaults to two weeks.")
|
||||
RootCmd.PersistentFlags().BoolVarP(&opts.InsecurePull, "insecure-pull", "", false, "Pull from insecure registry using plain HTTP")
|
||||
RootCmd.PersistentFlags().BoolVarP(&opts.SkipTLSVerifyPull, "skip-tls-verify-pull", "", false, "Pull from insecure registry ignoring TLS verify")
|
||||
RootCmd.PersistentFlags().VarP(&opts.InsecureRegistries, "insecure-registry", "", "Insecure registry using plain HTTP to pull. Set it repeatedly for multiple registries.")
|
||||
RootCmd.PersistentFlags().VarP(&opts.SkipTLSVerifyRegistries, "skip-tls-verify-registry", "", "Insecure registry ignoring TLS verify to pull. Set it repeatedly for multiple registries.")
|
||||
opts.RegistriesCertificates = make(map[string]string)
|
||||
RootCmd.PersistentFlags().VarP(&opts.RegistriesCertificates, "registry-certificate", "", "Use the provided certificate for TLS communication with the given registry. Expected format is 'my.registry.url=/path/to/the/server/certificate'.")
|
||||
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().StringVarP(&opts.CustomPlatform, "customPlatform", "", "", "Specify the build platform if different from the current host")
|
||||
}
|
||||
|
||||
// addHiddenFlags marks certain flags as hidden from the executor help text
|
||||
|
|
|
|||
|
|
@ -66,7 +66,7 @@ func (rc *RegistryCache) RetrieveLayer(ck string) (v1.Image, error) {
|
|||
cacheRef.Repository.Registry = newReg
|
||||
}
|
||||
|
||||
tr := util.MakeTransport(rc.Opts, registryName)
|
||||
tr := util.MakeTransport(rc.Opts.RegistryOptions, registryName)
|
||||
|
||||
img, err := remote.Image(cacheRef, remote.WithTransport(tr), remote.WithAuthFromKeychain(creds.GetKeychain()))
|
||||
if err != nil {
|
||||
|
|
|
|||
|
|
@ -21,14 +21,14 @@ import (
|
|||
"log"
|
||||
|
||||
"github.com/GoogleContainerTools/kaniko/pkg/config"
|
||||
"github.com/google/go-containerregistry/pkg/v1/remote"
|
||||
"github.com/GoogleContainerTools/kaniko/pkg/image/remote"
|
||||
)
|
||||
|
||||
func ExampleWarmer_Warm() {
|
||||
tarBuf := new(bytes.Buffer)
|
||||
manifestBuf := new(bytes.Buffer)
|
||||
w := &Warmer{
|
||||
Remote: remote.Image,
|
||||
Remote: remote.RetrieveRemoteImage,
|
||||
Local: LocalSource,
|
||||
TarWriter: tarBuf,
|
||||
ManifestWriter: manifestBuf,
|
||||
|
|
|
|||
|
|
@ -20,16 +20,13 @@ import (
|
|||
"bytes"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"os"
|
||||
"path"
|
||||
"runtime"
|
||||
|
||||
"github.com/GoogleContainerTools/kaniko/pkg/config"
|
||||
"github.com/GoogleContainerTools/kaniko/pkg/creds"
|
||||
"github.com/GoogleContainerTools/kaniko/pkg/image/remote"
|
||||
"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/tarball"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/sirupsen/logrus"
|
||||
|
|
@ -42,18 +39,18 @@ func WarmCache(opts *config.WarmerOptions) error {
|
|||
logrus.Debugf("%s\n", cacheDir)
|
||||
logrus.Debugf("%s\n", images)
|
||||
|
||||
for _, image := range images {
|
||||
for _, img := range images {
|
||||
tarBuf := new(bytes.Buffer)
|
||||
manifestBuf := new(bytes.Buffer)
|
||||
|
||||
cw := &Warmer{
|
||||
Remote: remote.Image,
|
||||
Remote: remote.RetrieveRemoteImage,
|
||||
Local: LocalSource,
|
||||
TarWriter: tarBuf,
|
||||
ManifestWriter: manifestBuf,
|
||||
}
|
||||
|
||||
digest, err := cw.Warm(image, opts)
|
||||
digest, err := cw.Warm(img, opts)
|
||||
if err != nil {
|
||||
if !IsAlreadyCached(err) {
|
||||
return err
|
||||
|
|
@ -68,7 +65,7 @@ func WarmCache(opts *config.WarmerOptions) error {
|
|||
return err
|
||||
}
|
||||
|
||||
logrus.Debugf("Wrote %s to cache", image)
|
||||
logrus.Debugf("Wrote %s to cache", img)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
@ -93,9 +90,9 @@ func writeBufsToFile(cachePath string, tarBuf, manifestBuf *bytes.Buffer) error
|
|||
}
|
||||
|
||||
// FetchRemoteImage retrieves a Docker image manifest from a remote source.
|
||||
// github.com/google/go-containerregistry/pkg/v1/remote.Image can be used as
|
||||
// github.com/GoogleContainerTools/kaniko/image/remote.RetrieveRemoteImage can be used as
|
||||
// this type.
|
||||
type FetchRemoteImage func(name.Reference, ...remote.Option) (v1.Image, error)
|
||||
type FetchRemoteImage func(image string, opts config.RegistryOptions, customPlatform string) (v1.Image, error)
|
||||
|
||||
// FetchLocalSource retrieves a Docker image manifest from a local source.
|
||||
// github.com/GoogleContainerTools/kaniko/cache.LocalSource can be used as
|
||||
|
|
@ -118,11 +115,7 @@ func (w *Warmer) Warm(image string, opts *config.WarmerOptions) (v1.Hash, error)
|
|||
return v1.Hash{}, errors.Wrapf(err, "Failed to verify image name: %s", image)
|
||||
}
|
||||
|
||||
transport := http.DefaultTransport.(*http.Transport)
|
||||
platform := currentPlatform()
|
||||
|
||||
rOpts := []remote.Option{remote.WithTransport(transport), remote.WithAuthFromKeychain(creds.GetKeychain()), remote.WithPlatform(platform)}
|
||||
img, err := w.Remote(cacheRef, rOpts...)
|
||||
img, err := w.Remote(image, opts.RegistryOptions, opts.CustomPlatform)
|
||||
if err != nil || img == nil {
|
||||
return v1.Hash{}, errors.Wrapf(err, "Failed to retrieve image: %s", image)
|
||||
}
|
||||
|
|
@ -155,11 +148,3 @@ func (w *Warmer) Warm(image string, opts *config.WarmerOptions) (v1.Hash, error)
|
|||
|
||||
return digest, nil
|
||||
}
|
||||
|
||||
// CurrentPlatform returns the v1.Platform on which the code runs.
|
||||
func currentPlatform() v1.Platform {
|
||||
return v1.Platform{
|
||||
OS: runtime.GOOS,
|
||||
Architecture: runtime.GOARCH,
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,9 +22,7 @@ import (
|
|||
|
||||
"github.com/GoogleContainerTools/kaniko/pkg/config"
|
||||
"github.com/GoogleContainerTools/kaniko/pkg/fakes"
|
||||
"github.com/google/go-containerregistry/pkg/name"
|
||||
v1 "github.com/google/go-containerregistry/pkg/v1"
|
||||
"github.com/google/go-containerregistry/pkg/v1/remote"
|
||||
)
|
||||
|
||||
const (
|
||||
|
|
@ -36,7 +34,7 @@ func Test_Warmer_Warm_not_in_cache(t *testing.T) {
|
|||
manifestBuf := new(bytes.Buffer)
|
||||
|
||||
cw := &Warmer{
|
||||
Remote: func(_ name.Reference, _ ...remote.Option) (v1.Image, error) {
|
||||
Remote: func(_ string, _ config.RegistryOptions, _ string) (v1.Image, error) {
|
||||
return fakes.FakeImage{}, nil
|
||||
},
|
||||
Local: func(_ *config.CacheOptions, _ string) (v1.Image, error) {
|
||||
|
|
@ -64,7 +62,7 @@ func Test_Warmer_Warm_in_cache_not_expired(t *testing.T) {
|
|||
manifestBuf := new(bytes.Buffer)
|
||||
|
||||
cw := &Warmer{
|
||||
Remote: func(_ name.Reference, _ ...remote.Option) (v1.Image, error) {
|
||||
Remote: func(_ string, _ config.RegistryOptions, _ string) (v1.Image, error) {
|
||||
return fakes.FakeImage{}, nil
|
||||
},
|
||||
Local: func(_ *config.CacheOptions, _ string) (v1.Image, error) {
|
||||
|
|
@ -92,7 +90,7 @@ func Test_Warmer_Warm_in_cache_expired(t *testing.T) {
|
|||
manifestBuf := new(bytes.Buffer)
|
||||
|
||||
cw := &Warmer{
|
||||
Remote: func(_ name.Reference, _ ...remote.Option) (v1.Image, error) {
|
||||
Remote: func(_ string, _ config.RegistryOptions, _ string) (v1.Image, error) {
|
||||
return fakes.FakeImage{}, nil
|
||||
},
|
||||
Local: func(_ *config.CacheOptions, _ string) (v1.Image, error) {
|
||||
|
|
|
|||
|
|
@ -30,40 +30,45 @@ type CacheOptions struct {
|
|||
CacheTTL time.Duration
|
||||
}
|
||||
|
||||
// KanikoOptions are options that are set by command line arguments
|
||||
type KanikoOptions struct {
|
||||
CacheOptions
|
||||
DockerfilePath string
|
||||
SrcContext string
|
||||
SnapshotMode string
|
||||
CustomPlatform string
|
||||
Bucket string
|
||||
TarPath string
|
||||
Target string
|
||||
CacheRepo string
|
||||
DigestFile string
|
||||
ImageNameDigestFile string
|
||||
OCILayoutPath string
|
||||
// RegistryOptions are all the options related to the registries, set by command line arguments.
|
||||
type RegistryOptions struct {
|
||||
RegistryMirrors multiArg
|
||||
Destinations multiArg
|
||||
BuildArgs multiArg
|
||||
InsecureRegistries multiArg
|
||||
Labels multiArg
|
||||
SkipTLSVerifyRegistries multiArg
|
||||
RegistriesCertificates keyValueArg
|
||||
Insecure bool
|
||||
SkipTLSVerify bool
|
||||
InsecurePull bool
|
||||
SkipTLSVerifyPull bool
|
||||
SingleSnapshot bool
|
||||
Reproducible bool
|
||||
NoPush bool
|
||||
Cache bool
|
||||
Cleanup bool
|
||||
IgnoreVarRun bool
|
||||
SkipUnusedStages bool
|
||||
RunV2 bool
|
||||
Git KanikoGitOptions
|
||||
}
|
||||
|
||||
// KanikoOptions are options that are set by command line arguments
|
||||
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
|
||||
Git KanikoGitOptions
|
||||
}
|
||||
|
||||
type KanikoGitOptions struct {
|
||||
|
|
@ -109,6 +114,8 @@ func (k *KanikoGitOptions) Set(s string) error {
|
|||
// WarmerOptions are options that are set by command line arguments to the cache warmer.
|
||||
type WarmerOptions struct {
|
||||
CacheOptions
|
||||
Images multiArg
|
||||
Force bool
|
||||
RegistryOptions
|
||||
CustomPlatform string
|
||||
Images multiArg
|
||||
Force bool
|
||||
}
|
||||
|
|
|
|||
|
|
@ -25,19 +25,15 @@ import (
|
|||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/google/go-containerregistry/pkg/v1/partial"
|
||||
|
||||
"github.com/moby/buildkit/frontend/dockerfile/instructions"
|
||||
|
||||
"golang.org/x/sync/errgroup"
|
||||
|
||||
"github.com/google/go-containerregistry/pkg/name"
|
||||
v1 "github.com/google/go-containerregistry/pkg/v1"
|
||||
"github.com/google/go-containerregistry/pkg/v1/empty"
|
||||
"github.com/google/go-containerregistry/pkg/v1/mutate"
|
||||
"github.com/google/go-containerregistry/pkg/v1/tarball"
|
||||
"github.com/moby/buildkit/frontend/dockerfile/instructions"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/sirupsen/logrus"
|
||||
"golang.org/x/sync/errgroup"
|
||||
|
||||
"github.com/GoogleContainerTools/kaniko/pkg/cache"
|
||||
"github.com/GoogleContainerTools/kaniko/pkg/commands"
|
||||
|
|
@ -45,9 +41,11 @@ import (
|
|||
"github.com/GoogleContainerTools/kaniko/pkg/constants"
|
||||
"github.com/GoogleContainerTools/kaniko/pkg/dockerfile"
|
||||
image_util "github.com/GoogleContainerTools/kaniko/pkg/image"
|
||||
"github.com/GoogleContainerTools/kaniko/pkg/image/remote"
|
||||
"github.com/GoogleContainerTools/kaniko/pkg/snapshot"
|
||||
"github.com/GoogleContainerTools/kaniko/pkg/timing"
|
||||
"github.com/GoogleContainerTools/kaniko/pkg/util"
|
||||
"github.com/google/go-containerregistry/pkg/v1/partial"
|
||||
)
|
||||
|
||||
// This is the size of an empty tar in Go
|
||||
|
|
@ -745,7 +743,7 @@ func fetchExtraStages(stages []config.KanikoStage, opts *config.KanikoOptions) e
|
|||
|
||||
// This must be an image name, fetch it.
|
||||
logrus.Debugf("Found extra base image stage %s", c.From)
|
||||
sourceImage, err := image_util.RetrieveRemoteImage(c.From, opts)
|
||||
sourceImage, err := remote.RetrieveRemoteImage(c.From, opts.RegistryOptions, opts.CustomPlatform)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
|
|||
|
|
@ -147,7 +147,7 @@ func CheckPushPermissions(opts *config.KanikoOptions) error {
|
|||
}
|
||||
destRef.Repository.Registry = newReg
|
||||
}
|
||||
tr := newRetry(util.MakeTransport(opts, registryName))
|
||||
tr := newRetry(util.MakeTransport(opts.RegistryOptions, registryName))
|
||||
if err := checkRemotePushPermission(destRef, creds.GetKeychain(), tr); err != nil {
|
||||
return errors.Wrapf(err, "checking push permission for %q", destRef)
|
||||
}
|
||||
|
|
@ -244,7 +244,7 @@ func DoPush(image v1.Image, opts *config.KanikoOptions) error {
|
|||
return errors.Wrap(err, "resolving pushAuth")
|
||||
}
|
||||
|
||||
tr := newRetry(util.MakeTransport(opts, registryName))
|
||||
tr := newRetry(util.MakeTransport(opts.RegistryOptions, registryName))
|
||||
rt := &withUserAgent{t: tr}
|
||||
|
||||
if err := remote.Write(destRef, image, remote.WithAuth(pushAuth), remote.WithTransport(rt)); err != nil {
|
||||
|
|
|
|||
|
|
@ -19,21 +19,18 @@ package image
|
|||
import (
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/GoogleContainerTools/kaniko/pkg/cache"
|
||||
"github.com/GoogleContainerTools/kaniko/pkg/config"
|
||||
"github.com/GoogleContainerTools/kaniko/pkg/constants"
|
||||
"github.com/GoogleContainerTools/kaniko/pkg/creds"
|
||||
"github.com/GoogleContainerTools/kaniko/pkg/image/remote"
|
||||
"github.com/GoogleContainerTools/kaniko/pkg/timing"
|
||||
"github.com/GoogleContainerTools/kaniko/pkg/util"
|
||||
|
||||
"github.com/google/go-containerregistry/pkg/name"
|
||||
v1 "github.com/google/go-containerregistry/pkg/v1"
|
||||
"github.com/google/go-containerregistry/pkg/v1/empty"
|
||||
"github.com/google/go-containerregistry/pkg/v1/remote"
|
||||
"github.com/google/go-containerregistry/pkg/v1/tarball"
|
||||
|
||||
"github.com/sirupsen/logrus"
|
||||
|
|
@ -41,7 +38,7 @@ import (
|
|||
|
||||
var (
|
||||
// RetrieveRemoteImage downloads an image from a remote location
|
||||
RetrieveRemoteImage = remoteImage
|
||||
RetrieveRemoteImage = remote.RetrieveRemoteImage
|
||||
retrieveTarImage = tarballImage
|
||||
)
|
||||
|
||||
|
|
@ -89,7 +86,7 @@ func RetrieveSourceImage(stage config.KanikoStage, opts *config.KanikoOptions) (
|
|||
}
|
||||
|
||||
// Otherwise, initialize image as usual
|
||||
return RetrieveRemoteImage(currentBaseName, opts)
|
||||
return RetrieveRemoteImage(currentBaseName, opts.RegistryOptions, opts.CustomPlatform)
|
||||
}
|
||||
|
||||
func tarballImage(index int) (v1.Image, error) {
|
||||
|
|
@ -98,89 +95,6 @@ func tarballImage(index int) (v1.Image, error) {
|
|||
return tarball.ImageFromPath(tarPath, nil)
|
||||
}
|
||||
|
||||
// Retrieves the manifest for the specified image from the specified registry
|
||||
func remoteImage(image string, opts *config.KanikoOptions) (v1.Image, error) {
|
||||
logrus.Infof("Retrieving image manifest %s", image)
|
||||
ref, err := name.ParseReference(image, name.WeakValidation)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if ref.Context().RegistryStr() == name.DefaultRegistry {
|
||||
ref, err := normalizeReference(ref, image)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, registryMirror := range opts.RegistryMirrors {
|
||||
var newReg name.Registry
|
||||
if opts.InsecurePull || opts.InsecureRegistries.Contains(registryMirror) {
|
||||
newReg, err = name.NewRegistry(registryMirror, name.WeakValidation, name.Insecure)
|
||||
} else {
|
||||
newReg, err = name.NewRegistry(registryMirror, name.StrictValidation)
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ref := setNewRegistry(ref, newReg)
|
||||
|
||||
logrus.Infof("Retrieving image %s from registry mirror %s", ref, registryMirror)
|
||||
remoteImage, err := remote.Image(ref, remoteOptions(registryMirror, opts)...)
|
||||
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
|
||||
}
|
||||
return remoteImage, nil
|
||||
}
|
||||
}
|
||||
|
||||
registryName := ref.Context().RegistryStr()
|
||||
if opts.InsecurePull || opts.InsecureRegistries.Contains(registryName) {
|
||||
newReg, err := name.NewRegistry(registryName, name.WeakValidation, name.Insecure)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ref = setNewRegistry(ref, newReg)
|
||||
}
|
||||
|
||||
logrus.Infof("Retrieving image %s from registry %s", ref, registryName)
|
||||
return remote.Image(ref, remoteOptions(registryName, opts)...)
|
||||
}
|
||||
|
||||
// normalizeReference adds the library/ prefix to images without it.
|
||||
//
|
||||
// It is mostly useful when using a registry mirror that is not able to perform
|
||||
// this fix automatically.
|
||||
func normalizeReference(ref name.Reference, image string) (name.Reference, error) {
|
||||
if !strings.ContainsRune(image, '/') {
|
||||
return name.ParseReference("library/"+image, name.WeakValidation)
|
||||
}
|
||||
|
||||
return ref, nil
|
||||
}
|
||||
|
||||
func setNewRegistry(ref name.Reference, newReg name.Registry) name.Reference {
|
||||
switch r := ref.(type) {
|
||||
case name.Tag:
|
||||
r.Repository.Registry = newReg
|
||||
return r
|
||||
case name.Digest:
|
||||
r.Repository.Registry = newReg
|
||||
return r
|
||||
default:
|
||||
return ref
|
||||
}
|
||||
}
|
||||
|
||||
func remoteOptions(registryName string, opts *config.KanikoOptions) []remote.Option {
|
||||
tr := util.MakeTransport(opts, registryName)
|
||||
|
||||
// on which v1.Platform is this currently running?
|
||||
platform := currentPlatform(opts)
|
||||
|
||||
return []remote.Option{remote.WithTransport(tr), remote.WithAuthFromKeychain(creds.GetKeychain()), remote.WithPlatform(platform)}
|
||||
}
|
||||
|
||||
func cachedImage(opts *config.KanikoOptions, image string) (v1.Image, error) {
|
||||
ref, err := name.ParseReference(image, name.WeakValidation)
|
||||
if err != nil {
|
||||
|
|
@ -191,7 +105,7 @@ func cachedImage(opts *config.KanikoOptions, image string) (v1.Image, error) {
|
|||
if d, ok := ref.(name.Digest); ok {
|
||||
cacheKey = d.DigestStr()
|
||||
} else {
|
||||
image, err := remoteImage(image, opts)
|
||||
image, err := remote.RetrieveRemoteImage(image, opts.RegistryOptions, opts.CustomPlatform)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
@ -204,17 +118,3 @@ func cachedImage(opts *config.KanikoOptions, image string) (v1.Image, error) {
|
|||
}
|
||||
return cache.LocalSource(&opts.CacheOptions, cacheKey)
|
||||
}
|
||||
|
||||
// CurrentPlatform returns the v1.Platform on which the code runs
|
||||
func currentPlatform(opts *config.KanikoOptions) v1.Platform {
|
||||
if opts.CustomPlatform != "" {
|
||||
return v1.Platform{
|
||||
OS: strings.Split(opts.CustomPlatform, "/")[0],
|
||||
Architecture: strings.Split(opts.CustomPlatform, "/")[1],
|
||||
}
|
||||
}
|
||||
return v1.Platform{
|
||||
OS: runtime.GOOS,
|
||||
Architecture: runtime.GOARCH,
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,7 +20,6 @@ import (
|
|||
"bytes"
|
||||
"testing"
|
||||
|
||||
"github.com/google/go-containerregistry/pkg/name"
|
||||
v1 "github.com/google/go-containerregistry/pkg/v1"
|
||||
"github.com/google/go-containerregistry/pkg/v1/empty"
|
||||
"github.com/moby/buildkit/frontend/dockerfile/instructions"
|
||||
|
|
@ -53,7 +52,7 @@ func Test_StandardImage(t *testing.T) {
|
|||
defer func() {
|
||||
RetrieveRemoteImage = original
|
||||
}()
|
||||
mock := func(image string, opts *config.KanikoOptions) (v1.Image, error) {
|
||||
mock := func(image string, opts config.RegistryOptions, _ string) (v1.Image, error) {
|
||||
return nil, nil
|
||||
}
|
||||
RetrieveRemoteImage = mock
|
||||
|
|
@ -104,31 +103,14 @@ func Test_ScratchImageFromMirror(t *testing.T) {
|
|||
actual, err := RetrieveSourceImage(config.KanikoStage{
|
||||
Stage: stages[1],
|
||||
}, &config.KanikoOptions{
|
||||
RegistryMirrors: []string{"mirror.gcr.io"},
|
||||
RegistryOptions: config.RegistryOptions{
|
||||
RegistryMirrors: []string{"mirror.gcr.io"},
|
||||
},
|
||||
})
|
||||
expected := empty.Image
|
||||
testutil.CheckErrorAndDeepEqual(t, false, err, expected, actual)
|
||||
}
|
||||
|
||||
func Test_normalizeReference(t *testing.T) {
|
||||
image := "debian"
|
||||
expected := "index.docker.io/library/debian:latest"
|
||||
|
||||
ref, err := name.ParseReference(image)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
ref2, err := normalizeReference(ref, image)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if ref2.Name() != ref.Name() || ref2.Name() != expected {
|
||||
t.Errorf("%s should have been normalized to %s, got %s", ref2.Name(), expected, ref.Name())
|
||||
}
|
||||
}
|
||||
|
||||
// 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)))
|
||||
|
|
|
|||
|
|
@ -0,0 +1,129 @@
|
|||
/*
|
||||
Copyright 2018 Google LLC
|
||||
|
||||
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.
|
||||
*/
|
||||
|
||||
package remote
|
||||
|
||||
import (
|
||||
"runtime"
|
||||
"strings"
|
||||
|
||||
"github.com/GoogleContainerTools/kaniko/pkg/config"
|
||||
"github.com/GoogleContainerTools/kaniko/pkg/creds"
|
||||
"github.com/GoogleContainerTools/kaniko/pkg/util"
|
||||
|
||||
"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/sirupsen/logrus"
|
||||
)
|
||||
|
||||
// RetrieveRemoteImage retrieves the manifest for the specified image from the specified registry
|
||||
func RetrieveRemoteImage(image string, opts config.RegistryOptions, customPlatform string) (v1.Image, error) {
|
||||
logrus.Infof("Retrieving image manifest %s", image)
|
||||
ref, err := name.ParseReference(image, name.WeakValidation)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if ref.Context().RegistryStr() == name.DefaultRegistry {
|
||||
ref, err := normalizeReference(ref, image)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, registryMirror := range opts.RegistryMirrors {
|
||||
var newReg name.Registry
|
||||
if opts.InsecurePull || opts.InsecureRegistries.Contains(registryMirror) {
|
||||
newReg, err = name.NewRegistry(registryMirror, name.WeakValidation, name.Insecure)
|
||||
} else {
|
||||
newReg, err = name.NewRegistry(registryMirror, name.StrictValidation)
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ref := setNewRegistry(ref, newReg)
|
||||
|
||||
logrus.Infof("Retrieving image %s from registry mirror %s", ref, registryMirror)
|
||||
remoteImage, err := remote.Image(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
|
||||
}
|
||||
return remoteImage, nil
|
||||
}
|
||||
}
|
||||
|
||||
registryName := ref.Context().RegistryStr()
|
||||
if opts.InsecurePull || opts.InsecureRegistries.Contains(registryName) {
|
||||
newReg, err := name.NewRegistry(registryName, name.WeakValidation, name.Insecure)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ref = setNewRegistry(ref, newReg)
|
||||
}
|
||||
|
||||
logrus.Infof("Retrieving image %s from registry %s", ref, registryName)
|
||||
return remote.Image(ref, remoteOptions(registryName, opts, customPlatform)...)
|
||||
}
|
||||
|
||||
// normalizeReference adds the library/ prefix to images without it.
|
||||
//
|
||||
// It is mostly useful when using a registry mirror that is not able to perform
|
||||
// this fix automatically.
|
||||
func normalizeReference(ref name.Reference, image string) (name.Reference, error) {
|
||||
if !strings.ContainsRune(image, '/') {
|
||||
return name.ParseReference("library/"+image, name.WeakValidation)
|
||||
}
|
||||
|
||||
return ref, nil
|
||||
}
|
||||
|
||||
func setNewRegistry(ref name.Reference, newReg name.Registry) name.Reference {
|
||||
switch r := ref.(type) {
|
||||
case name.Tag:
|
||||
r.Repository.Registry = newReg
|
||||
return r
|
||||
case name.Digest:
|
||||
r.Repository.Registry = newReg
|
||||
return r
|
||||
default:
|
||||
return ref
|
||||
}
|
||||
}
|
||||
|
||||
func remoteOptions(registryName string, opts config.RegistryOptions, customPlatform string) []remote.Option {
|
||||
tr := util.MakeTransport(opts, registryName)
|
||||
|
||||
// on which v1.Platform is this currently running?
|
||||
platform := currentPlatform(customPlatform)
|
||||
|
||||
return []remote.Option{remote.WithTransport(tr), remote.WithAuthFromKeychain(creds.GetKeychain()), remote.WithPlatform(platform)}
|
||||
}
|
||||
|
||||
// CurrentPlatform returns the v1.Platform on which the code runs
|
||||
func currentPlatform(customPlatform string) v1.Platform {
|
||||
if customPlatform != "" {
|
||||
return v1.Platform{
|
||||
OS: strings.Split(customPlatform, "/")[0],
|
||||
Architecture: strings.Split(customPlatform, "/")[1],
|
||||
}
|
||||
}
|
||||
return v1.Platform{
|
||||
OS: runtime.GOOS,
|
||||
Architecture: runtime.GOARCH,
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,42 @@
|
|||
/*
|
||||
Copyright 2018 Google LLC
|
||||
|
||||
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.
|
||||
*/
|
||||
|
||||
package remote
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/google/go-containerregistry/pkg/name"
|
||||
)
|
||||
|
||||
func Test_normalizeReference(t *testing.T) {
|
||||
image := "debian"
|
||||
expected := "index.docker.io/library/debian:latest"
|
||||
|
||||
ref, err := name.ParseReference(image)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
ref2, err := normalizeReference(ref, image)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if ref2.Name() != ref.Name() || ref2.Name() != expected {
|
||||
t.Errorf("%s should have been normalized to %s, got %s", ref2.Name(), expected, ref.Name())
|
||||
}
|
||||
}
|
||||
|
|
@ -62,7 +62,7 @@ func init() {
|
|||
}
|
||||
}
|
||||
|
||||
func MakeTransport(opts *config.KanikoOptions, registryName string) http.RoundTripper {
|
||||
func MakeTransport(opts config.RegistryOptions, registryName string) http.RoundTripper {
|
||||
// Create a transport to set our user-agent.
|
||||
var tr http.RoundTripper = http.DefaultTransport.(*http.Transport).Clone()
|
||||
if opts.SkipTLSVerify || opts.SkipTLSVerifyRegistries.Contains(registryName) {
|
||||
|
|
|
|||
|
|
@ -44,12 +44,12 @@ func Test_makeTransport(t *testing.T) {
|
|||
|
||||
tests := []struct {
|
||||
name string
|
||||
opts *config.KanikoOptions
|
||||
opts config.RegistryOptions
|
||||
check func(*tls.Config, *mockedCertPool)
|
||||
}{
|
||||
{
|
||||
name: "SkipTLSVerify set",
|
||||
opts: &config.KanikoOptions{SkipTLSVerify: true},
|
||||
opts: config.RegistryOptions{SkipTLSVerify: true},
|
||||
check: func(config *tls.Config, pool *mockedCertPool) {
|
||||
if !config.InsecureSkipVerify {
|
||||
t.Errorf("makeTransport().TLSClientConfig.InsecureSkipVerify not set while SkipTLSVerify set")
|
||||
|
|
@ -58,7 +58,7 @@ func Test_makeTransport(t *testing.T) {
|
|||
},
|
||||
{
|
||||
name: "SkipTLSVerifyRegistries set with expected registry",
|
||||
opts: &config.KanikoOptions{SkipTLSVerifyRegistries: []string{registryName}},
|
||||
opts: config.RegistryOptions{SkipTLSVerifyRegistries: []string{registryName}},
|
||||
check: func(config *tls.Config, pool *mockedCertPool) {
|
||||
if !config.InsecureSkipVerify {
|
||||
t.Errorf("makeTransport().TLSClientConfig.InsecureSkipVerify not set while SkipTLSVerifyRegistries set with registry name")
|
||||
|
|
@ -67,7 +67,7 @@ func Test_makeTransport(t *testing.T) {
|
|||
},
|
||||
{
|
||||
name: "SkipTLSVerifyRegistries set with other registry",
|
||||
opts: &config.KanikoOptions{SkipTLSVerifyRegistries: []string{fmt.Sprintf("other.%s", registryName)}},
|
||||
opts: config.RegistryOptions{SkipTLSVerifyRegistries: []string{fmt.Sprintf("other.%s", registryName)}},
|
||||
check: func(config *tls.Config, pool *mockedCertPool) {
|
||||
if config.InsecureSkipVerify {
|
||||
t.Errorf("makeTransport().TLSClientConfig.InsecureSkipVerify set while SkipTLSVerifyRegistries not set with registry name")
|
||||
|
|
@ -76,7 +76,7 @@ func Test_makeTransport(t *testing.T) {
|
|||
},
|
||||
{
|
||||
name: "RegistriesCertificates set for registry",
|
||||
opts: &config.KanikoOptions{RegistriesCertificates: map[string]string{registryName: "/path/to/the/certificate.cert"}},
|
||||
opts: config.RegistryOptions{RegistriesCertificates: map[string]string{registryName: "/path/to/the/certificate.cert"}},
|
||||
check: func(config *tls.Config, pool *mockedCertPool) {
|
||||
if len(pool.certificatesPath) != 1 || pool.certificatesPath[0] != "/path/to/the/certificate.cert" {
|
||||
t.Errorf("makeTransport().RegistriesCertificates certificate not appended to system certificates")
|
||||
|
|
@ -85,7 +85,7 @@ func Test_makeTransport(t *testing.T) {
|
|||
},
|
||||
{
|
||||
name: "RegistriesCertificates set for another registry",
|
||||
opts: &config.KanikoOptions{RegistriesCertificates: map[string]string{fmt.Sprintf("other.%s=", registryName): "/path/to/the/certificate.cert"}},
|
||||
opts: config.RegistryOptions{RegistriesCertificates: map[string]string{fmt.Sprintf("other.%s=", registryName): "/path/to/the/certificate.cert"}},
|
||||
check: func(config *tls.Config, pool *mockedCertPool) {
|
||||
if len(pool.certificatesPath) != 0 {
|
||||
t.Errorf("makeTransport().RegistriesCertificates certificate appended to system certificates while added for other registry")
|
||||
|
|
|
|||
Loading…
Reference in New Issue