mirror of
https://github.com/GoogleContainerTools/kaniko
synced 2026-10-21 09:11:07 +02:00
Merge branch 'master' into set_image_platform
This commit is contained in:
+36
-12
@@ -22,6 +22,7 @@ import (
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/google/go-containerregistry/pkg/v1/partial"
|
||||
@@ -73,7 +74,7 @@ type stageBuilder struct {
|
||||
stageIdxToDigest map[string]string
|
||||
snapshotter snapShotter
|
||||
layerCache cache.LayerCache
|
||||
pushCache cachePusher
|
||||
pushLayerToCache cachePusher
|
||||
}
|
||||
|
||||
// newStageBuilder returns a new type stageBuilder which contains all the information required to build the stage
|
||||
@@ -83,7 +84,7 @@ func newStageBuilder(opts *config.KanikoOptions, stage config.KanikoStage, cross
|
||||
return nil, err
|
||||
}
|
||||
|
||||
imageConfig, err := initializeConfig(sourceImage)
|
||||
imageConfig, err := initializeConfig(sourceImage, opts)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -116,7 +117,7 @@ func newStageBuilder(opts *config.KanikoOptions, stage config.KanikoStage, cross
|
||||
layerCache: &cache.RegistryCache{
|
||||
Opts: opts,
|
||||
},
|
||||
pushCache: pushLayerToCache,
|
||||
pushLayerToCache: pushLayerToCache,
|
||||
}
|
||||
|
||||
for _, cmd := range s.stage.Commands {
|
||||
@@ -135,7 +136,7 @@ func newStageBuilder(opts *config.KanikoOptions, stage config.KanikoStage, cross
|
||||
return s, nil
|
||||
}
|
||||
|
||||
func initializeConfig(img partial.WithConfigFile) (*v1.ConfigFile, error) {
|
||||
func initializeConfig(img partial.WithConfigFile, opts *config.KanikoOptions) (*v1.ConfigFile, error) {
|
||||
imageConfig, err := img.ConfigFile()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -144,12 +145,37 @@ func initializeConfig(img partial.WithConfigFile) (*v1.ConfigFile, error) {
|
||||
if imageConfig.Config.Env == nil {
|
||||
imageConfig.Config.Env = constants.ScratchEnvVars
|
||||
}
|
||||
|
||||
if opts == nil {
|
||||
return imageConfig, nil
|
||||
}
|
||||
|
||||
if l := len(opts.Labels); l > 0 {
|
||||
if imageConfig.Config.Labels == nil {
|
||||
imageConfig.Config.Labels = make(map[string]string)
|
||||
}
|
||||
for _, label := range opts.Labels {
|
||||
parts := strings.SplitN(label, "=", 2)
|
||||
if len(parts) != 2 {
|
||||
return nil, fmt.Errorf("labels must be of the form key=value, got %s", label)
|
||||
}
|
||||
|
||||
imageConfig.Config.Labels[parts[0]] = parts[1]
|
||||
}
|
||||
}
|
||||
|
||||
return imageConfig, nil
|
||||
}
|
||||
|
||||
func (s *stageBuilder) populateCompositeKey(command fmt.Stringer, files []string, compositeKey CompositeCache) (CompositeCache, error) {
|
||||
func (s *stageBuilder) populateCompositeKey(command fmt.Stringer, files []string, compositeKey CompositeCache, args *dockerfile.BuildArgs, env []string) (CompositeCache, error) {
|
||||
// First replace all the environment variables or args in the command
|
||||
replacementEnvs := args.ReplacementEnvs(env)
|
||||
resolvedCmd, err := util.ResolveEnvironmentReplacement(command.String(), replacementEnvs, false)
|
||||
if err != nil {
|
||||
return compositeKey, err
|
||||
}
|
||||
// Add the next command to the cache key.
|
||||
compositeKey.AddKey(command.String())
|
||||
compositeKey.AddKey(resolvedCmd)
|
||||
switch v := command.(type) {
|
||||
case *commands.CopyCommand:
|
||||
compositeKey = s.populateCopyCmdCompositeKey(command, v.From(), compositeKey)
|
||||
@@ -202,7 +228,7 @@ func (s *stageBuilder) optimize(compositeKey CompositeCache, cfg v1.Config) erro
|
||||
return errors.Wrap(err, "failed to get files used from context")
|
||||
}
|
||||
|
||||
compositeKey, err = s.populateCompositeKey(command, files, compositeKey)
|
||||
compositeKey, err = s.populateCompositeKey(command, files, compositeKey, s.args, cfg.Env)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -252,8 +278,6 @@ func (s *stageBuilder) build() error {
|
||||
compositeKey = NewCompositeCache(s.baseImageDigest)
|
||||
}
|
||||
|
||||
compositeKey.AddKey(s.opts.BuildArgs...)
|
||||
|
||||
// Apply optimizations to the instructions.
|
||||
if err := s.optimize(*compositeKey, s.cf.Config); err != nil {
|
||||
return errors.Wrap(err, "failed to optimize instructions")
|
||||
@@ -310,7 +334,7 @@ func (s *stageBuilder) build() error {
|
||||
return errors.Wrap(err, "failed to get files used from context")
|
||||
}
|
||||
|
||||
*compositeKey, err = s.populateCompositeKey(command, files, *compositeKey)
|
||||
*compositeKey, err = s.populateCompositeKey(command, files, *compositeKey, s.args, s.cf.Config.Env)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -359,7 +383,7 @@ func (s *stageBuilder) build() error {
|
||||
// Push layer to cache (in parallel) now along with new config file
|
||||
if s.opts.Cache && command.ShouldCacheOutput() {
|
||||
cacheGroup.Go(func() error {
|
||||
return s.pushCache(s.opts, ck, tarPath, command.String())
|
||||
return s.pushLayerToCache(s.opts, ck, tarPath, command.String())
|
||||
})
|
||||
}
|
||||
if err := s.saveSnapshotToImage(command.String(), tarPath); err != nil {
|
||||
@@ -485,7 +509,7 @@ func CalculateDependencies(opts *config.KanikoOptions) (map[int][]string, error)
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
cfg, err := initializeConfig(image)
|
||||
cfg, err := initializeConfig(image, opts)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
+258
-4
@@ -462,7 +462,7 @@ func TestInitializeConfig(t *testing.T) {
|
||||
t.Errorf("error seen when running test %s", err)
|
||||
t.Fail()
|
||||
}
|
||||
actual, _ := initializeConfig(img)
|
||||
actual, _ := initializeConfig(img, nil)
|
||||
testutil.CheckDeepEqual(t, tt.expected, actual.Config)
|
||||
}
|
||||
}
|
||||
@@ -497,7 +497,8 @@ func Test_stageBuilder_optimize(t *testing.T) {
|
||||
cf := &v1.ConfigFile{}
|
||||
snap := fakeSnapShotter{}
|
||||
lc := &fakeLayerCache{retrieve: tc.retrieve}
|
||||
sb := &stageBuilder{opts: tc.opts, cf: cf, snapshotter: snap, layerCache: lc}
|
||||
sb := &stageBuilder{opts: tc.opts, cf: cf, snapshotter: snap, layerCache: lc,
|
||||
args: dockerfile.NewBuildArgs([]string{})}
|
||||
ck := CompositeCache{}
|
||||
file, err := ioutil.TempFile("", "foo")
|
||||
if err != nil {
|
||||
@@ -517,10 +518,135 @@ func Test_stageBuilder_optimize(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
type stageContext struct {
|
||||
command fmt.Stringer
|
||||
args *dockerfile.BuildArgs
|
||||
env []string
|
||||
}
|
||||
|
||||
func newStageContext(command string, args map[string]string, env []string) stageContext {
|
||||
dockerArgs := dockerfile.NewBuildArgs([]string{})
|
||||
for k, v := range args {
|
||||
dockerArgs.AddArg(k, &v)
|
||||
}
|
||||
return stageContext{MockDockerCommand{command: command}, dockerArgs, env}
|
||||
}
|
||||
|
||||
func Test_stageBuilder_populateCompositeKey(t *testing.T) {
|
||||
testCases := []struct {
|
||||
description string
|
||||
cmd1 stageContext
|
||||
cmd2 stageContext
|
||||
shdEqual bool
|
||||
}{
|
||||
{
|
||||
description: "cache key for same command, different buildargs, args not used in command",
|
||||
cmd1: newStageContext(
|
||||
"RUN echo const > test",
|
||||
map[string]string{"ARG": "foo"},
|
||||
[]string{"ENV=foo1"},
|
||||
),
|
||||
cmd2: newStageContext(
|
||||
"RUN echo const > test",
|
||||
map[string]string{"ARG": "bar"},
|
||||
[]string{"ENV=bar1"},
|
||||
),
|
||||
shdEqual: true,
|
||||
},
|
||||
{
|
||||
description: "cache key for same command with same build args",
|
||||
cmd1: newStageContext(
|
||||
"RUN echo $ARG > test",
|
||||
map[string]string{"ARG": "foo"},
|
||||
[]string{},
|
||||
),
|
||||
cmd2: newStageContext(
|
||||
"RUN echo $ARG > test",
|
||||
map[string]string{"ARG": "foo"},
|
||||
[]string{},
|
||||
),
|
||||
shdEqual: true,
|
||||
},
|
||||
{
|
||||
description: "cache key for same command with same env",
|
||||
cmd1: newStageContext(
|
||||
"RUN echo $ENV > test",
|
||||
map[string]string{"ARG": "foo"},
|
||||
[]string{"ENV=same"},
|
||||
),
|
||||
cmd2: newStageContext(
|
||||
"RUN echo $ENV > test",
|
||||
map[string]string{"ARG": "bar"},
|
||||
[]string{"ENV=same"},
|
||||
),
|
||||
shdEqual: true,
|
||||
},
|
||||
{
|
||||
description: "cache key for same command with a build arg values",
|
||||
cmd1: newStageContext(
|
||||
"RUN echo $ARG > test",
|
||||
map[string]string{"ARG": "foo"},
|
||||
[]string{},
|
||||
),
|
||||
cmd2: newStageContext(
|
||||
"RUN echo $ARG > test",
|
||||
map[string]string{"ARG": "bar"},
|
||||
[]string{},
|
||||
),
|
||||
},
|
||||
{
|
||||
description: "cache key for same command with different env values",
|
||||
cmd1: newStageContext(
|
||||
"RUN echo $ENV > test",
|
||||
map[string]string{"ARG": "foo"},
|
||||
[]string{"ENV=1"},
|
||||
),
|
||||
cmd2: newStageContext(
|
||||
"RUN echo $ENV > test",
|
||||
map[string]string{"ARG": "foo"},
|
||||
[]string{"ENV=2"},
|
||||
),
|
||||
},
|
||||
{
|
||||
description: "cache key for different command same context",
|
||||
cmd1: newStageContext(
|
||||
"RUN echo other > test",
|
||||
map[string]string{"ARG": "foo"},
|
||||
[]string{"ENV=1"},
|
||||
),
|
||||
cmd2: newStageContext(
|
||||
"RUN echo another > test",
|
||||
map[string]string{"ARG": "foo"},
|
||||
[]string{"ENV=1"},
|
||||
),
|
||||
},
|
||||
}
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.description, func(t *testing.T) {
|
||||
sb := &stageBuilder{opts: &config.KanikoOptions{SrcContext: "workspace"}}
|
||||
ck := CompositeCache{}
|
||||
|
||||
ck1, err := sb.populateCompositeKey(tc.cmd1.command, []string{}, ck, tc.cmd1.args, tc.cmd1.env)
|
||||
if err != nil {
|
||||
t.Errorf("Expected error to be nil but was %v", err)
|
||||
}
|
||||
ck2, err := sb.populateCompositeKey(tc.cmd2.command, []string{}, ck, tc.cmd2.args, tc.cmd2.env)
|
||||
if err != nil {
|
||||
t.Errorf("Expected error to be nil but was %v", err)
|
||||
}
|
||||
key1, key2 := hashCompositeKeys(t, ck1, ck2)
|
||||
if b := key1 == key2; b != tc.shdEqual {
|
||||
t.Errorf("expected keys to be equal as %t but found %t", tc.shdEqual, !tc.shdEqual)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_stageBuilder_build(t *testing.T) {
|
||||
type testcase struct {
|
||||
description string
|
||||
opts *config.KanikoOptions
|
||||
args map[string]string
|
||||
layerCache *fakeLayerCache
|
||||
expectedCacheKeys []string
|
||||
pushedCacheKeys []string
|
||||
@@ -544,6 +670,7 @@ func Test_stageBuilder_build(t *testing.T) {
|
||||
t.Errorf("couldn't create hash %v", err)
|
||||
}
|
||||
command := MockDockerCommand{
|
||||
command: "meow",
|
||||
contextFiles: []string{filePath},
|
||||
cacheCommand: MockCachedDockerCommand{
|
||||
contextFiles: []string{filePath},
|
||||
@@ -576,6 +703,7 @@ func Test_stageBuilder_build(t *testing.T) {
|
||||
t.Errorf("couldn't create hash %v", err)
|
||||
}
|
||||
command := MockDockerCommand{
|
||||
command: "meow",
|
||||
contextFiles: []string{filePath},
|
||||
cacheCommand: MockCachedDockerCommand{
|
||||
contextFiles: []string{filePath},
|
||||
@@ -838,6 +966,117 @@ COPY %s bar.txt
|
||||
commands: getCommands(dir, cmds),
|
||||
}
|
||||
}(),
|
||||
func() testcase {
|
||||
dir, _ := tempDirAndFile(t)
|
||||
ch := NewCompositeCache("")
|
||||
ch.AddKey("RUN foobar")
|
||||
hash, err := ch.Hash()
|
||||
if err != nil {
|
||||
t.Errorf("couldn't create hash %v", err)
|
||||
}
|
||||
|
||||
command := MockDockerCommand{
|
||||
command: "RUN foobar",
|
||||
contextFiles: []string{},
|
||||
cacheCommand: MockCachedDockerCommand{
|
||||
contextFiles: []string{},
|
||||
},
|
||||
}
|
||||
|
||||
return testcase{
|
||||
description: "cached run command with no build arg value used uses cached layer and does not push anything",
|
||||
config: &v1.ConfigFile{Config: v1.Config{WorkingDir: dir}},
|
||||
opts: &config.KanikoOptions{Cache: true},
|
||||
args: map[string]string{
|
||||
"test": "value",
|
||||
},
|
||||
expectedCacheKeys: []string{hash},
|
||||
commands: []commands.DockerCommand{command},
|
||||
// layer key needs to be read.
|
||||
layerCache: &fakeLayerCache{
|
||||
img: &fakeImage{ImageLayers: []v1.Layer{fakeLayer{}}},
|
||||
keySequence: []string{hash},
|
||||
},
|
||||
rootDir: dir,
|
||||
}
|
||||
}(),
|
||||
func() testcase {
|
||||
dir, _ := tempDirAndFile(t)
|
||||
|
||||
ch := NewCompositeCache("")
|
||||
ch.AddKey("RUN value")
|
||||
hash, err := ch.Hash()
|
||||
if err != nil {
|
||||
t.Errorf("couldn't create hash %v", err)
|
||||
}
|
||||
|
||||
command := MockDockerCommand{
|
||||
command: "RUN $arg",
|
||||
contextFiles: []string{},
|
||||
cacheCommand: MockCachedDockerCommand{
|
||||
contextFiles: []string{},
|
||||
},
|
||||
}
|
||||
|
||||
return testcase{
|
||||
description: "cached run command with same build arg does not push layer",
|
||||
config: &v1.ConfigFile{Config: v1.Config{WorkingDir: dir}},
|
||||
opts: &config.KanikoOptions{Cache: true},
|
||||
args: map[string]string{
|
||||
"arg": "value",
|
||||
},
|
||||
// layer key that exists
|
||||
layerCache: &fakeLayerCache{
|
||||
img: &fakeImage{ImageLayers: []v1.Layer{fakeLayer{}}},
|
||||
keySequence: []string{hash},
|
||||
},
|
||||
expectedCacheKeys: []string{hash},
|
||||
commands: []commands.DockerCommand{command},
|
||||
rootDir: dir,
|
||||
}
|
||||
}(),
|
||||
func() testcase {
|
||||
dir, _ := tempDirAndFile(t)
|
||||
|
||||
ch1 := NewCompositeCache("")
|
||||
ch1.AddKey("RUN value")
|
||||
hash1, err := ch1.Hash()
|
||||
if err != nil {
|
||||
t.Errorf("couldn't create hash %v", err)
|
||||
}
|
||||
|
||||
ch2 := NewCompositeCache("")
|
||||
ch2.AddKey("RUN anotherValue")
|
||||
hash2, err := ch2.Hash()
|
||||
if err != nil {
|
||||
t.Errorf("couldn't create hash %v", err)
|
||||
}
|
||||
command := MockDockerCommand{
|
||||
command: "RUN $arg",
|
||||
contextFiles: []string{},
|
||||
cacheCommand: MockCachedDockerCommand{
|
||||
contextFiles: []string{},
|
||||
},
|
||||
}
|
||||
|
||||
return testcase{
|
||||
description: "cached run command with another build arg pushes layer",
|
||||
config: &v1.ConfigFile{Config: v1.Config{WorkingDir: dir}},
|
||||
opts: &config.KanikoOptions{Cache: true},
|
||||
args: map[string]string{
|
||||
"arg": "anotherValue",
|
||||
},
|
||||
// layer for arg=value already exists
|
||||
layerCache: &fakeLayerCache{
|
||||
img: &fakeImage{ImageLayers: []v1.Layer{fakeLayer{}}},
|
||||
keySequence: []string{hash1},
|
||||
},
|
||||
expectedCacheKeys: []string{hash2},
|
||||
pushedCacheKeys: []string{hash2},
|
||||
commands: []commands.DockerCommand{command},
|
||||
rootDir: dir,
|
||||
}
|
||||
}(),
|
||||
}
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.description, func(t *testing.T) {
|
||||
@@ -875,18 +1114,21 @@ COPY %s bar.txt
|
||||
}
|
||||
keys := []string{}
|
||||
sb := &stageBuilder{
|
||||
args: &dockerfile.BuildArgs{}, //required or code will panic
|
||||
args: dockerfile.NewBuildArgs([]string{}), //required or code will panic
|
||||
image: tc.image,
|
||||
opts: tc.opts,
|
||||
cf: cf,
|
||||
snapshotter: snap,
|
||||
layerCache: lc,
|
||||
pushCache: func(_ *config.KanikoOptions, cacheKey, _, _ string) error {
|
||||
pushLayerToCache: func(_ *config.KanikoOptions, cacheKey, _, _ string) error {
|
||||
keys = append(keys, cacheKey)
|
||||
return nil
|
||||
},
|
||||
}
|
||||
sb.cmds = tc.commands
|
||||
for key, value := range tc.args {
|
||||
sb.args.AddArg(key, &value)
|
||||
}
|
||||
tmp := commands.RootDir
|
||||
if tc.rootDir != "" {
|
||||
commands.RootDir = tc.rootDir
|
||||
@@ -993,3 +1235,15 @@ func generateTar(t *testing.T, dir string, fileNames ...string) []byte {
|
||||
}
|
||||
return buf.Bytes()
|
||||
}
|
||||
|
||||
func hashCompositeKeys(t *testing.T, ck1 CompositeCache, ck2 CompositeCache) (string, string) {
|
||||
key1, err := ck1.Hash()
|
||||
if err != nil {
|
||||
t.Errorf("could not hash composite key due to %s", err)
|
||||
}
|
||||
key2, err := ck2.Hash()
|
||||
if err != nil {
|
||||
t.Errorf("could not hash composite key due to %s", err)
|
||||
}
|
||||
return key1, key2
|
||||
}
|
||||
|
||||
@@ -43,13 +43,14 @@ func (f fakeSnapShotter) TakeSnapshot(_ []string) (string, error) {
|
||||
}
|
||||
|
||||
type MockDockerCommand struct {
|
||||
command string
|
||||
contextFiles []string
|
||||
cacheCommand commands.DockerCommand
|
||||
}
|
||||
|
||||
func (m MockDockerCommand) ExecuteCommand(c *v1.Config, args *dockerfile.BuildArgs) error { return nil }
|
||||
func (m MockDockerCommand) String() string {
|
||||
return "meow"
|
||||
return m.command
|
||||
}
|
||||
func (m MockDockerCommand) FilesToSnapshot() []string {
|
||||
return []string{"meow-snapshot-no-cache"}
|
||||
|
||||
+49
-4
@@ -18,6 +18,7 @@ package executor
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
@@ -62,6 +63,41 @@ func (w *withUserAgent) RoundTrip(r *http.Request) (*http.Response, error) {
|
||||
return w.t.RoundTrip(r)
|
||||
}
|
||||
|
||||
type CertPool interface {
|
||||
value() *x509.CertPool
|
||||
append(path string) error
|
||||
}
|
||||
|
||||
type X509CertPool struct {
|
||||
inner x509.CertPool
|
||||
}
|
||||
|
||||
func (p *X509CertPool) value() *x509.CertPool {
|
||||
return &p.inner
|
||||
}
|
||||
|
||||
func (p *X509CertPool) append(path string) error {
|
||||
pem, err := ioutil.ReadFile(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
p.inner.AppendCertsFromPEM(pem)
|
||||
return nil
|
||||
}
|
||||
|
||||
type systemCertLoader func() CertPool
|
||||
|
||||
var defaultX509Handler systemCertLoader = func() CertPool {
|
||||
systemCertPool, err := x509.SystemCertPool()
|
||||
if err != nil {
|
||||
logrus.Warn("Failed to load system cert pool. Loading empty one instead.")
|
||||
systemCertPool = x509.NewCertPool()
|
||||
}
|
||||
return &X509CertPool{
|
||||
inner: *systemCertPool,
|
||||
}
|
||||
}
|
||||
|
||||
// CheckPushPermissions checks that the configured credentials can be used to
|
||||
// push to every specified destination.
|
||||
func CheckPushPermissions(opts *config.KanikoOptions) error {
|
||||
@@ -87,7 +123,7 @@ func CheckPushPermissions(opts *config.KanikoOptions) error {
|
||||
}
|
||||
destRef.Repository.Registry = newReg
|
||||
}
|
||||
tr := makeTransport(opts, registryName)
|
||||
tr := makeTransport(opts, registryName, defaultX509Handler)
|
||||
if err := remote.CheckPushPermission(destRef, creds.GetKeychain(), tr); err != nil {
|
||||
return errors.Wrapf(err, "checking push permission for %q", destRef)
|
||||
}
|
||||
@@ -184,7 +220,7 @@ func DoPush(image v1.Image, opts *config.KanikoOptions) error {
|
||||
return errors.Wrap(err, "resolving pushAuth")
|
||||
}
|
||||
|
||||
tr := makeTransport(opts, registryName)
|
||||
tr := makeTransport(opts, registryName, defaultX509Handler)
|
||||
rt := &withUserAgent{t: tr}
|
||||
|
||||
if err := remote.Write(destRef, image, remote.WithAuth(pushAuth), remote.WithTransport(rt)); err != nil {
|
||||
@@ -228,13 +264,22 @@ func writeImageOutputs(image v1.Image, destRefs []name.Tag) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func makeTransport(opts *config.KanikoOptions, registryName string) http.RoundTripper {
|
||||
func makeTransport(opts *config.KanikoOptions, registryName string, loader systemCertLoader) http.RoundTripper {
|
||||
// Create a transport to set our user-agent.
|
||||
tr := http.DefaultTransport
|
||||
var tr http.RoundTripper = http.DefaultTransport.(*http.Transport).Clone()
|
||||
if opts.SkipTLSVerify || opts.SkipTLSVerifyRegistries.Contains(registryName) {
|
||||
tr.(*http.Transport).TLSClientConfig = &tls.Config{
|
||||
InsecureSkipVerify: true,
|
||||
}
|
||||
} else if certificatePath := opts.RegistriesCertificates[registryName]; certificatePath != "" {
|
||||
systemCertPool := loader()
|
||||
if err := systemCertPool.append(certificatePath); err != nil {
|
||||
logrus.WithError(err).Warnf("Failed to load certificate %s for %s\n", certificatePath, registryName)
|
||||
} else {
|
||||
tr.(*http.Transport).TLSClientConfig = &tls.Config{
|
||||
RootCAs: systemCertPool.value(),
|
||||
}
|
||||
}
|
||||
}
|
||||
return tr
|
||||
}
|
||||
|
||||
@@ -18,6 +18,8 @@ package executor
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
@@ -223,3 +225,85 @@ func TestImageNameDigestFile(t *testing.T) {
|
||||
testutil.CheckErrorAndDeepEqual(t, false, err, want, got)
|
||||
|
||||
}
|
||||
|
||||
type mockedCertPool struct {
|
||||
certificatesPath []string
|
||||
}
|
||||
|
||||
func (m *mockedCertPool) value() *x509.CertPool {
|
||||
return &x509.CertPool{}
|
||||
}
|
||||
|
||||
func (m *mockedCertPool) append(path string) error {
|
||||
m.certificatesPath = append(m.certificatesPath, path)
|
||||
return nil
|
||||
}
|
||||
|
||||
func Test_makeTransport(t *testing.T) {
|
||||
registryName := "my.registry.name"
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
opts *config.KanikoOptions
|
||||
check func(*tls.Config, *mockedCertPool)
|
||||
}{
|
||||
{
|
||||
name: "SkipTLSVerify set",
|
||||
opts: &config.KanikoOptions{SkipTLSVerify: true},
|
||||
check: func(config *tls.Config, pool *mockedCertPool) {
|
||||
if !config.InsecureSkipVerify {
|
||||
t.Errorf("makeTransport().TLSClientConfig.InsecureSkipVerify not set while SkipTLSVerify set")
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "SkipTLSVerifyRegistries set with expected registry",
|
||||
opts: &config.KanikoOptions{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")
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "SkipTLSVerifyRegistries set with other registry",
|
||||
opts: &config.KanikoOptions{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")
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "RegistriesCertificates set for registry",
|
||||
opts: &config.KanikoOptions{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")
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "RegistriesCertificates set for another registry",
|
||||
opts: &config.KanikoOptions{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")
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
var certificatesPath []string
|
||||
certPool := mockedCertPool{
|
||||
certificatesPath: certificatesPath,
|
||||
}
|
||||
var mockedSystemCertLoader systemCertLoader = func() CertPool {
|
||||
return &certPool
|
||||
}
|
||||
transport := makeTransport(tt.opts, registryName, mockedSystemCertLoader)
|
||||
tt.check(transport.(*http.Transport).TLSClientConfig, &certPool)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user