mirror of
https://github.com/GoogleContainerTools/kaniko
synced 2026-10-19 00:01:50 +02:00
Updated PR Branch with master
This commit is contained in:
Vendored
+10
@@ -18,6 +18,7 @@ package cache
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"path"
|
||||
|
||||
"github.com/GoogleContainerTools/kaniko/pkg/config"
|
||||
@@ -53,6 +54,15 @@ func WarmCache(opts *config.WarmerOptions) error {
|
||||
if err != nil {
|
||||
return errors.Wrap(err, fmt.Sprintf("Failed to write %s to cache", image))
|
||||
}
|
||||
|
||||
mfst, err := img.RawManifest()
|
||||
if err != nil {
|
||||
return errors.Wrap(err, fmt.Sprintf("Failed to retrieve manifest for %s", image))
|
||||
}
|
||||
mfstPath := cachePath + ".json"
|
||||
if err := ioutil.WriteFile(mfstPath, mfst, 0666); err != nil {
|
||||
return errors.Wrap(err, fmt.Sprintf("Failed to save manifest for %s", image))
|
||||
}
|
||||
logrus.Debugf("Wrote %s to cache", image)
|
||||
}
|
||||
return nil
|
||||
|
||||
+6
-3
@@ -47,7 +47,7 @@ type AddCommand struct {
|
||||
func (a *AddCommand) ExecuteCommand(config *v1.Config, buildArgs *dockerfile.BuildArgs) error {
|
||||
replacementEnvs := buildArgs.ReplacementEnvs(config.Env)
|
||||
|
||||
srcs, dest, err := resolveEnvAndWildcards(a.cmd.SourcesAndDest, a.buildcontext, replacementEnvs)
|
||||
srcs, dest, err := util.ResolveEnvAndWildcards(a.cmd.SourcesAndDest, a.buildcontext, replacementEnvs)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -61,7 +61,10 @@ func (a *AddCommand) ExecuteCommand(config *v1.Config, buildArgs *dockerfile.Bui
|
||||
for _, src := range srcs {
|
||||
fullPath := filepath.Join(a.buildcontext, src)
|
||||
if util.IsSrcRemoteFileURL(src) {
|
||||
urlDest := util.URLDestinationFilepath(src, dest, config.WorkingDir)
|
||||
urlDest, err := util.URLDestinationFilepath(src, dest, config.WorkingDir, replacementEnvs)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
logrus.Infof("Adding remote URL %s to %s", src, urlDest)
|
||||
if err := util.DownloadFileToDest(src, urlDest); err != nil {
|
||||
return err
|
||||
@@ -111,7 +114,7 @@ func (a *AddCommand) String() string {
|
||||
func (a *AddCommand) FilesUsedFromContext(config *v1.Config, buildArgs *dockerfile.BuildArgs) ([]string, error) {
|
||||
replacementEnvs := buildArgs.ReplacementEnvs(config.Env)
|
||||
|
||||
srcs, _, err := resolveEnvAndWildcards(a.cmd.SourcesAndDest, a.buildcontext, replacementEnvs)
|
||||
srcs, _, err := util.ResolveEnvAndWildcards(a.cmd.SourcesAndDest, a.buildcontext, replacementEnvs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
+17
-9
@@ -30,27 +30,35 @@ type ArgCommand struct {
|
||||
|
||||
// ExecuteCommand only needs to add this ARG key/value as seen
|
||||
func (r *ArgCommand) ExecuteCommand(config *v1.Config, buildArgs *dockerfile.BuildArgs) error {
|
||||
replacementEnvs := buildArgs.ReplacementEnvs(config.Env)
|
||||
resolvedKey, err := util.ResolveEnvironmentReplacement(r.cmd.Key, replacementEnvs, false)
|
||||
key, val, err := ParseArg(r.cmd.Key, r.cmd.Value, config.Env, buildArgs)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
buildArgs.AddArg(key, val)
|
||||
return nil
|
||||
}
|
||||
|
||||
func ParseArg(key string, val *string, env []string, ba *dockerfile.BuildArgs) (string, *string, error) {
|
||||
replacementEnvs := ba.ReplacementEnvs(env)
|
||||
resolvedKey, err := util.ResolveEnvironmentReplacement(key, replacementEnvs, false)
|
||||
if err != nil {
|
||||
return "", nil, err
|
||||
}
|
||||
var resolvedValue *string
|
||||
if r.cmd.Value != nil {
|
||||
value, err := util.ResolveEnvironmentReplacement(*r.cmd.Value, replacementEnvs, false)
|
||||
if val != nil {
|
||||
value, err := util.ResolveEnvironmentReplacement(*val, replacementEnvs, false)
|
||||
if err != nil {
|
||||
return err
|
||||
return "", nil, err
|
||||
}
|
||||
resolvedValue = &value
|
||||
} else {
|
||||
meta := buildArgs.GetAllMeta()
|
||||
meta := ba.GetAllMeta()
|
||||
if value, ok := meta[resolvedKey]; ok {
|
||||
resolvedValue = &value
|
||||
}
|
||||
}
|
||||
|
||||
buildArgs.AddArg(resolvedKey, resolvedValue)
|
||||
return nil
|
||||
return resolvedKey, resolvedValue, nil
|
||||
}
|
||||
|
||||
// String returns some information about the command for the image config history
|
||||
|
||||
+2
-14
@@ -45,7 +45,7 @@ func (c *CopyCommand) ExecuteCommand(config *v1.Config, buildArgs *dockerfile.Bu
|
||||
|
||||
replacementEnvs := buildArgs.ReplacementEnvs(config.Env)
|
||||
|
||||
srcs, dest, err := resolveEnvAndWildcards(c.cmd.SourcesAndDest, c.buildcontext, replacementEnvs)
|
||||
srcs, dest, err := util.ResolveEnvAndWildcards(c.cmd.SourcesAndDest, c.buildcontext, replacementEnvs)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -100,18 +100,6 @@ func (c *CopyCommand) ExecuteCommand(config *v1.Config, buildArgs *dockerfile.Bu
|
||||
return nil
|
||||
}
|
||||
|
||||
func resolveEnvAndWildcards(sd instructions.SourcesAndDest, buildcontext string, envs []string) ([]string, string, error) {
|
||||
// First, resolve any environment replacement
|
||||
resolvedEnvs, err := util.ResolveEnvironmentReplacementList(sd, envs, true)
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
dest := resolvedEnvs[len(resolvedEnvs)-1]
|
||||
// Resolve wildcards and get a list of resolved sources
|
||||
srcs, err := util.ResolveSources(resolvedEnvs, buildcontext)
|
||||
return srcs, dest, err
|
||||
}
|
||||
|
||||
// FilesToSnapshot should return an empty array if still nil; no files were changed
|
||||
func (c *CopyCommand) FilesToSnapshot() []string {
|
||||
return c.snapshotFiles
|
||||
@@ -129,7 +117,7 @@ func (c *CopyCommand) FilesUsedFromContext(config *v1.Config, buildArgs *dockerf
|
||||
}
|
||||
|
||||
replacementEnvs := buildArgs.ReplacementEnvs(config.Env)
|
||||
srcs, _, err := resolveEnvAndWildcards(c.cmd.SourcesAndDest, c.buildcontext, replacementEnvs)
|
||||
srcs, _, err := util.ResolveEnvAndWildcards(c.cmd.SourcesAndDest, c.buildcontext, replacementEnvs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -31,10 +31,6 @@ type UserCommand struct {
|
||||
cmd *instructions.UserCommand
|
||||
}
|
||||
|
||||
func (r *UserCommand) RequiresUnpackedFS() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (r *UserCommand) ExecuteCommand(config *v1.Config, buildArgs *dockerfile.BuildArgs) error {
|
||||
logrus.Info("cmd: USER")
|
||||
u := r.cmd.User
|
||||
@@ -52,11 +48,6 @@ func (r *UserCommand) ExecuteCommand(config *v1.Config, buildArgs *dockerfile.Bu
|
||||
}
|
||||
}
|
||||
|
||||
_, _, err = util.GetUserFromUsername(userStr, groupStr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if groupStr != "" {
|
||||
userStr = userStr + ":" + groupStr
|
||||
}
|
||||
|
||||
@@ -28,57 +28,42 @@ import (
|
||||
var userTests = []struct {
|
||||
user string
|
||||
expectedUID string
|
||||
shouldError bool
|
||||
}{
|
||||
{
|
||||
user: "root",
|
||||
expectedUID: "root",
|
||||
shouldError: false,
|
||||
},
|
||||
{
|
||||
user: "0",
|
||||
expectedUID: "0",
|
||||
shouldError: false,
|
||||
},
|
||||
{
|
||||
user: "fakeUser",
|
||||
expectedUID: "",
|
||||
shouldError: true,
|
||||
expectedUID: "fakeUser",
|
||||
},
|
||||
{
|
||||
user: "root:root",
|
||||
expectedUID: "root:root",
|
||||
shouldError: false,
|
||||
},
|
||||
{
|
||||
user: "0:root",
|
||||
expectedUID: "0:root",
|
||||
shouldError: false,
|
||||
},
|
||||
{
|
||||
user: "root:0",
|
||||
expectedUID: "root:0",
|
||||
shouldError: false,
|
||||
},
|
||||
{
|
||||
user: "0:0",
|
||||
expectedUID: "0:0",
|
||||
shouldError: false,
|
||||
},
|
||||
{
|
||||
user: "root:fakeGroup",
|
||||
expectedUID: "",
|
||||
shouldError: true,
|
||||
},
|
||||
{
|
||||
user: "$envuser",
|
||||
expectedUID: "root",
|
||||
shouldError: false,
|
||||
},
|
||||
{
|
||||
user: "root:$envgroup",
|
||||
expectedUID: "root:root",
|
||||
shouldError: false,
|
||||
},
|
||||
}
|
||||
|
||||
@@ -97,6 +82,6 @@ func TestUpdateUser(t *testing.T) {
|
||||
}
|
||||
buildArgs := dockerfile.NewBuildArgs([]string{})
|
||||
err := cmd.ExecuteCommand(cfg, buildArgs)
|
||||
testutil.CheckErrorAndDeepEqual(t, test.shouldError, err, test.expectedUID, cfg.User)
|
||||
testutil.CheckErrorAndDeepEqual(t, false, err, test.expectedUID, cfg.User)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,4 +26,5 @@ type KanikoStage struct {
|
||||
BaseImageStoredLocally bool
|
||||
SaveStage bool
|
||||
MetaArgs []instructions.ArgCommand
|
||||
Index int
|
||||
}
|
||||
|
||||
@@ -25,6 +25,8 @@ import (
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/sirupsen/logrus"
|
||||
|
||||
"github.com/GoogleContainerTools/kaniko/pkg/config"
|
||||
"github.com/GoogleContainerTools/kaniko/pkg/util"
|
||||
"github.com/moby/buildkit/frontend/dockerfile/instructions"
|
||||
@@ -67,6 +69,7 @@ func Stages(opts *config.KanikoOptions) ([]config.KanikoStage, error) {
|
||||
return nil, errors.Wrap(err, "resolving base name")
|
||||
}
|
||||
stage.Name = resolvedBaseName
|
||||
logrus.Infof("Resolved base name %s to %s", stage.BaseName, stage.Name)
|
||||
kanikoStages = append(kanikoStages, config.KanikoStage{
|
||||
Stage: stage,
|
||||
BaseImageIndex: baseImageIndex(index, stages),
|
||||
@@ -74,6 +77,7 @@ func Stages(opts *config.KanikoOptions) ([]config.KanikoStage, error) {
|
||||
SaveStage: saveStage(index, stages),
|
||||
Final: index == targetStage,
|
||||
MetaArgs: metaArgs,
|
||||
Index: index,
|
||||
})
|
||||
if index == targetStage {
|
||||
break
|
||||
@@ -175,14 +179,6 @@ func saveStage(index int, stages []instructions.Stage) bool {
|
||||
return true
|
||||
}
|
||||
}
|
||||
for _, cmd := range stage.Commands {
|
||||
switch c := cmd.(type) {
|
||||
case *instructions.CopyCommand:
|
||||
if c.From == strconv.Itoa(index) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -114,7 +114,7 @@ func Test_SaveStage(t *testing.T) {
|
||||
{
|
||||
name: "reference stage in later copy command",
|
||||
index: 0,
|
||||
expected: true,
|
||||
expected: false,
|
||||
},
|
||||
{
|
||||
name: "reference stage in later from command",
|
||||
|
||||
+104
-5
@@ -23,6 +23,8 @@ import (
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/otiai10/copy"
|
||||
|
||||
"github.com/google/go-containerregistry/pkg/v1/partial"
|
||||
|
||||
"github.com/moby/buildkit/frontend/dockerfile/instructions"
|
||||
@@ -60,10 +62,11 @@ type stageBuilder struct {
|
||||
opts *config.KanikoOptions
|
||||
cmds []commands.DockerCommand
|
||||
args *dockerfile.BuildArgs
|
||||
crossStageDeps map[int][]string
|
||||
}
|
||||
|
||||
// newStageBuilder returns a new type stageBuilder which contains all the information required to build the stage
|
||||
func newStageBuilder(opts *config.KanikoOptions, stage config.KanikoStage) (*stageBuilder, error) {
|
||||
func newStageBuilder(opts *config.KanikoOptions, stage config.KanikoStage, crossStageDeps map[int][]string) (*stageBuilder, error) {
|
||||
sourceImage, err := util.RetrieveSourceImage(stage, opts)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -96,6 +99,7 @@ func newStageBuilder(opts *config.KanikoOptions, stage config.KanikoStage) (*sta
|
||||
snapshotter: snapshotter,
|
||||
baseImageDigest: digest.String(),
|
||||
opts: opts,
|
||||
crossStageDeps: crossStageDeps,
|
||||
}
|
||||
|
||||
for _, cmd := range s.stage.Commands {
|
||||
@@ -207,6 +211,10 @@ func (s *stageBuilder) build() error {
|
||||
break
|
||||
}
|
||||
}
|
||||
if len(s.crossStageDeps[s.stage.Index]) > 0 {
|
||||
shouldUnpack = true
|
||||
}
|
||||
|
||||
if shouldUnpack {
|
||||
t := timing.Start("FS Unpacking")
|
||||
if _, err := util.GetFSFromImage(constants.RootDir, s.image); err != nil {
|
||||
@@ -353,6 +361,68 @@ func (s *stageBuilder) saveSnapshotToImage(createdBy string, tarPath string) err
|
||||
|
||||
}
|
||||
|
||||
func CalculateDependencies(opts *config.KanikoOptions) (map[int][]string, error) {
|
||||
stages, err := dockerfile.Stages(opts)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
images := []v1.Image{}
|
||||
depGraph := map[int][]string{}
|
||||
for _, s := range stages {
|
||||
ba := dockerfile.NewBuildArgs(opts.BuildArgs)
|
||||
ba.AddMetaArgs(s.MetaArgs)
|
||||
var image v1.Image
|
||||
var err error
|
||||
if s.BaseImageStoredLocally {
|
||||
image = images[s.BaseImageIndex]
|
||||
} else if s.Name == constants.NoBaseImage {
|
||||
image = empty.Image
|
||||
} else {
|
||||
image, err = util.RetrieveSourceImage(s, opts)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
cfg, err := initializeConfig(image)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, c := range s.Commands {
|
||||
switch cmd := c.(type) {
|
||||
case *instructions.CopyCommand:
|
||||
if cmd.From != "" {
|
||||
i, err := strconv.Atoi(cmd.From)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
resolved, err := util.ResolveEnvironmentReplacementList(cmd.SourcesAndDest, ba.ReplacementEnvs(cfg.Config.Env), true)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
depGraph[i] = append(depGraph[i], resolved[0:len(resolved)-1]...)
|
||||
}
|
||||
case *instructions.EnvCommand:
|
||||
if err := util.UpdateConfigEnv(cmd.Env, &cfg.Config, ba.ReplacementEnvs(cfg.Config.Env)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
image, err = mutate.Config(image, cfg.Config)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
case *instructions.ArgCommand:
|
||||
k, v, err := commands.ParseArg(cmd.Key, cmd.Value, cfg.Config.Env, ba)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ba.AddArg(k, v)
|
||||
}
|
||||
}
|
||||
images = append(images, image)
|
||||
}
|
||||
return depGraph, nil
|
||||
}
|
||||
|
||||
// DoBuild executes building the Dockerfile
|
||||
func DoBuild(opts *config.KanikoOptions) (v1.Image, error) {
|
||||
t := timing.Start("Total Build Time")
|
||||
@@ -369,8 +439,14 @@ func DoBuild(opts *config.KanikoOptions) (v1.Image, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
crossStageDependencies, err := CalculateDependencies(opts)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
logrus.Infof("Built cross stage deps: %v", crossStageDependencies)
|
||||
|
||||
for index, stage := range stages {
|
||||
sb, err := newStageBuilder(opts, stage)
|
||||
sb, err := newStageBuilder(opts, stage, crossStageDependencies)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -405,10 +481,21 @@ func DoBuild(opts *config.KanikoOptions) (v1.Image, error) {
|
||||
if err := saveStageAsTarball(strconv.Itoa(index), sourceImage); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := extractImageToDependecyDir(strconv.Itoa(index), sourceImage); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
filesToSave, err := filesToSave(crossStageDependencies[index])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
dstDir := filepath.Join(constants.KanikoDir, strconv.Itoa(index))
|
||||
if err := os.MkdirAll(dstDir, 0644); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, p := range filesToSave {
|
||||
logrus.Infof("Saving file %s for later use.", p)
|
||||
copy.Copy(p, filepath.Join(dstDir, p))
|
||||
}
|
||||
|
||||
// Delete the filesystem
|
||||
if err := util.DeleteFilesystem(); err != nil {
|
||||
return nil, err
|
||||
@@ -418,6 +505,18 @@ func DoBuild(opts *config.KanikoOptions) (v1.Image, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
func filesToSave(deps []string) ([]string, error) {
|
||||
allFiles := []string{}
|
||||
for _, src := range deps {
|
||||
srcs, err := filepath.Glob(src)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
allFiles = append(allFiles, srcs...)
|
||||
}
|
||||
return allFiles, nil
|
||||
}
|
||||
|
||||
func fetchExtraStages(stages []config.KanikoStage, opts *config.KanikoOptions) error {
|
||||
t := timing.Start("Fetching Extra Stages")
|
||||
defer timing.DefaultRun.Stop(t)
|
||||
|
||||
+228
-3
@@ -17,14 +17,19 @@ limitations under the License.
|
||||
package executor
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
"sort"
|
||||
"testing"
|
||||
|
||||
"github.com/moby/buildkit/frontend/dockerfile/instructions"
|
||||
|
||||
"github.com/GoogleContainerTools/kaniko/pkg/config"
|
||||
"github.com/GoogleContainerTools/kaniko/pkg/dockerfile"
|
||||
"github.com/GoogleContainerTools/kaniko/testutil"
|
||||
"github.com/google/go-containerregistry/pkg/v1"
|
||||
"github.com/google/go-cmp/cmp"
|
||||
v1 "github.com/google/go-containerregistry/pkg/v1"
|
||||
"github.com/moby/buildkit/frontend/dockerfile/instructions"
|
||||
)
|
||||
|
||||
func Test_reviewConfig(t *testing.T) {
|
||||
@@ -180,3 +185,223 @@ func Test_stageBuilder_shouldTakeSnapshot(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestCalculateDependencies(t *testing.T) {
|
||||
type args struct {
|
||||
dockerfile string
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
want map[int][]string
|
||||
}{
|
||||
{
|
||||
name: "no deps",
|
||||
args: args{
|
||||
dockerfile: `
|
||||
FROM debian as stage1
|
||||
RUN foo
|
||||
FROM stage1
|
||||
RUN bar
|
||||
`,
|
||||
},
|
||||
want: map[int][]string{},
|
||||
},
|
||||
{
|
||||
name: "args",
|
||||
args: args{
|
||||
dockerfile: `
|
||||
ARG myFile=foo
|
||||
FROM debian as stage1
|
||||
RUN foo
|
||||
FROM stage1
|
||||
ARG myFile
|
||||
COPY --from=stage1 /tmp/$myFile.txt .
|
||||
RUN bar
|
||||
`,
|
||||
},
|
||||
want: map[int][]string{
|
||||
0: {"/tmp/foo.txt"},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "simple deps",
|
||||
args: args{
|
||||
dockerfile: `
|
||||
FROM debian as stage1
|
||||
FROM alpine
|
||||
COPY --from=stage1 /foo /bar
|
||||
`,
|
||||
},
|
||||
want: map[int][]string{
|
||||
0: {"/foo"},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "two sets deps",
|
||||
args: args{
|
||||
dockerfile: `
|
||||
FROM debian as stage1
|
||||
FROM ubuntu as stage2
|
||||
RUN foo
|
||||
COPY --from=stage1 /foo /bar
|
||||
FROM alpine
|
||||
COPY --from=stage2 /bar /bat
|
||||
`,
|
||||
},
|
||||
want: map[int][]string{
|
||||
0: {"/foo"},
|
||||
1: {"/bar"},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "double deps",
|
||||
args: args{
|
||||
dockerfile: `
|
||||
FROM debian as stage1
|
||||
FROM ubuntu as stage2
|
||||
RUN foo
|
||||
COPY --from=stage1 /foo /bar
|
||||
FROM alpine
|
||||
COPY --from=stage1 /baz /bat
|
||||
`,
|
||||
},
|
||||
want: map[int][]string{
|
||||
0: {"/foo", "/baz"},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "envs in deps",
|
||||
args: args{
|
||||
dockerfile: `
|
||||
FROM debian as stage1
|
||||
FROM ubuntu as stage2
|
||||
RUN foo
|
||||
ENV key1 val1
|
||||
ENV key2 val2
|
||||
COPY --from=stage1 /foo/$key1 /foo/$key2 /bar
|
||||
FROM alpine
|
||||
COPY --from=stage2 /bar /bat
|
||||
`,
|
||||
},
|
||||
want: map[int][]string{
|
||||
0: {"/foo/val1", "/foo/val2"},
|
||||
1: {"/bar"},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "envs from base image in deps",
|
||||
args: args{
|
||||
dockerfile: `
|
||||
FROM debian as stage1
|
||||
ENV key1 baseval1
|
||||
FROM stage1 as stage2
|
||||
RUN foo
|
||||
ENV key2 val2
|
||||
COPY --from=stage1 /foo/$key1 /foo/$key2 /bar
|
||||
FROM alpine
|
||||
COPY --from=stage2 /bar /bat
|
||||
`,
|
||||
},
|
||||
want: map[int][]string{
|
||||
0: {"/foo/baseval1", "/foo/val2"},
|
||||
1: {"/bar"},
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
f, _ := ioutil.TempFile("", "")
|
||||
ioutil.WriteFile(f.Name(), []byte(tt.args.dockerfile), 0755)
|
||||
opts := &config.KanikoOptions{
|
||||
DockerfilePath: f.Name(),
|
||||
}
|
||||
|
||||
got, err := CalculateDependencies(opts)
|
||||
if err != nil {
|
||||
t.Errorf("got error: %s,", err)
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(got, tt.want) {
|
||||
diff := cmp.Diff(got, tt.want)
|
||||
t.Errorf("CalculateDependencies() = %v, want %v, diff %v", got, tt.want, diff)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_filesToSave(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
args []string
|
||||
want []string
|
||||
files []string
|
||||
}{
|
||||
{
|
||||
name: "simple",
|
||||
args: []string{"foo"},
|
||||
files: []string{"foo"},
|
||||
want: []string{"foo"},
|
||||
},
|
||||
{
|
||||
name: "glob",
|
||||
args: []string{"foo*"},
|
||||
files: []string{"foo", "foo2", "fooooo", "bar"},
|
||||
want: []string{"foo", "foo2", "fooooo"},
|
||||
},
|
||||
{
|
||||
name: "complex glob",
|
||||
args: []string{"foo*", "bar?"},
|
||||
files: []string{"foo", "foo2", "fooooo", "bar", "bar1", "bar2", "bar33"},
|
||||
want: []string{"foo", "foo2", "fooooo", "bar1", "bar2"},
|
||||
},
|
||||
{
|
||||
name: "dir",
|
||||
args: []string{"foo"},
|
||||
files: []string{"foo/bar", "foo/baz", "foo/bat/baz"},
|
||||
want: []string{"foo"},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
tmpDir, err := ioutil.TempDir("", "")
|
||||
if err != nil {
|
||||
t.Errorf("error creating tmpdir: %s", err)
|
||||
}
|
||||
defer os.RemoveAll(tmpDir)
|
||||
|
||||
for _, f := range tt.files {
|
||||
p := filepath.Join(tmpDir, f)
|
||||
dir := filepath.Dir(p)
|
||||
if dir != "." {
|
||||
if err := os.MkdirAll(dir, 0755); err != nil {
|
||||
t.Errorf("error making dir: %s", err)
|
||||
}
|
||||
}
|
||||
fp, err := os.Create(p)
|
||||
if err != nil {
|
||||
t.Errorf("error making file: %s", err)
|
||||
}
|
||||
fp.Close()
|
||||
}
|
||||
|
||||
args := []string{}
|
||||
for _, arg := range tt.args {
|
||||
args = append(args, filepath.Join(tmpDir, arg))
|
||||
}
|
||||
got, err := filesToSave(args)
|
||||
if err != nil {
|
||||
t.Errorf("got err: %s", err)
|
||||
}
|
||||
want := []string{}
|
||||
for _, w := range tt.want {
|
||||
want = append(want, filepath.Join(tmpDir, w))
|
||||
}
|
||||
sort.Strings(want)
|
||||
sort.Strings(got)
|
||||
if !reflect.DeepEqual(got, want) {
|
||||
t.Errorf("filesToSave() = %v, want %v", got, want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -47,6 +47,30 @@ func (w *withUserAgent) RoundTrip(r *http.Request) (*http.Response, error) {
|
||||
return w.t.RoundTrip(r)
|
||||
}
|
||||
|
||||
// CheckPushPermissionos checks that the configured credentials can be used to
|
||||
// push to every specified destination.
|
||||
func CheckPushPermissions(opts *config.KanikoOptions) error {
|
||||
if opts.NoPush {
|
||||
return nil
|
||||
}
|
||||
|
||||
checked := map[string]bool{}
|
||||
for _, destination := range opts.Destinations {
|
||||
destRef, err := name.NewTag(destination, name.WeakValidation)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "getting tag for destination")
|
||||
}
|
||||
if checked[destRef.Context().RepositoryStr()] {
|
||||
continue
|
||||
}
|
||||
if err := remote.CheckPushPermission(destRef, creds.GetKeychain(), http.DefaultTransport); err != nil {
|
||||
return errors.Wrapf(err, "checking push permission for %q", destRef)
|
||||
}
|
||||
checked[destRef.Context().RepositoryStr()] = true
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// DoPush is responsible for pushing image to the destinations specified in opts
|
||||
func DoPush(image v1.Image, opts *config.KanikoOptions) error {
|
||||
t := timing.Start("Total Push Time")
|
||||
|
||||
@@ -109,11 +109,10 @@ func (l *LayeredMap) Add(s string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// MaybeAdd will add the specified file s to the layered map if
|
||||
// the layered map's hashing function determines it has changed. If
|
||||
// it has not changed, it will not be added. Returns true if the file
|
||||
// was added.
|
||||
func (l *LayeredMap) MaybeAdd(s string) (bool, error) {
|
||||
// CheckFileChange checkes whether a given file changed
|
||||
// from the current layered map by its hashing function.
|
||||
// Returns true if the file is changed.
|
||||
func (l *LayeredMap) CheckFileChange(s string) (bool, error) {
|
||||
oldV, ok := l.Get(s)
|
||||
t := timing.Start("Hashing files")
|
||||
defer timing.DefaultRun.Stop(t)
|
||||
@@ -124,6 +123,5 @@ func (l *LayeredMap) MaybeAdd(s string) (bool, error) {
|
||||
if ok && newV == oldV {
|
||||
return false, nil
|
||||
}
|
||||
l.layers[len(l.layers)-1][s] = newV
|
||||
return true, nil
|
||||
}
|
||||
|
||||
+40
-37
@@ -73,44 +73,15 @@ func (s *Snapshotter) TakeSnapshot(files []string) (string, error) {
|
||||
}
|
||||
logrus.Info("Taking snapshot of files...")
|
||||
logrus.Debugf("Taking snapshot of files %v", files)
|
||||
snapshottedFiles := make(map[string]bool)
|
||||
|
||||
// First add to the tar any parent directories that haven't been added
|
||||
parentDirs := map[string]struct{}{}
|
||||
for _, file := range files {
|
||||
for _, p := range util.ParentDirectories(file) {
|
||||
parentDirs[p] = struct{}{}
|
||||
}
|
||||
}
|
||||
filesToAdd := []string{}
|
||||
for file := range parentDirs {
|
||||
file = filepath.Clean(file)
|
||||
snapshottedFiles[file] = true
|
||||
|
||||
// The parent directory might already be in a previous layer.
|
||||
fileAdded, err := s.l.MaybeAdd(file)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("Unable to add parent dir %s to layered map: %s", file, err)
|
||||
}
|
||||
|
||||
if fileAdded {
|
||||
filesToAdd = append(filesToAdd, file)
|
||||
}
|
||||
}
|
||||
|
||||
// Next add the files themselves to the tar
|
||||
for _, file := range files {
|
||||
// We might have already added the file above as a parent directory of another file.
|
||||
file = filepath.Clean(file)
|
||||
if _, ok := snapshottedFiles[file]; ok {
|
||||
continue
|
||||
}
|
||||
snapshottedFiles[file] = true
|
||||
// Also add parent directories to keep the permission of them correctly.
|
||||
filesToAdd := filesWithParentDirs(files)
|
||||
|
||||
// Add files to the layered map
|
||||
for _, file := range filesToAdd {
|
||||
if err := s.l.Add(file); err != nil {
|
||||
return "", fmt.Errorf("Unable to add file %s to layered map: %s", file, err)
|
||||
}
|
||||
filesToAdd = append(filesToAdd, file)
|
||||
}
|
||||
|
||||
t := util.NewTar(f)
|
||||
@@ -201,16 +172,27 @@ func (s *Snapshotter) scanFullFilesystem() ([]string, []string, error) {
|
||||
logrus.Debugf("Not adding %s to layer, as it's whitelisted", path)
|
||||
continue
|
||||
}
|
||||
// Only add to the tar if we add it to the layeredmap.
|
||||
maybeAdd, err := s.l.MaybeAdd(path)
|
||||
// Only add changed files.
|
||||
fileChanged, err := s.l.CheckFileChange(path)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
if maybeAdd {
|
||||
logrus.Debugf("Adding %s to layer, because it was changed.", path)
|
||||
if fileChanged {
|
||||
logrus.Infof("Adding %s to layer, because it was changed.", path)
|
||||
filesToAdd = append(filesToAdd, path)
|
||||
}
|
||||
}
|
||||
|
||||
// Also add parent directories to keep the permission of them correctly.
|
||||
filesToAdd = filesWithParentDirs(filesToAdd)
|
||||
|
||||
// Add files to the layered map
|
||||
for _, file := range filesToAdd {
|
||||
if err := s.l.Add(file); err != nil {
|
||||
return nil, nil, fmt.Errorf("Unable to add file %s to layered map: %s", file, err)
|
||||
}
|
||||
}
|
||||
|
||||
return filesToAdd, filesToWhiteOut, nil
|
||||
}
|
||||
|
||||
@@ -230,3 +212,24 @@ func writeToTar(t util.Tar, files, whiteouts []string) error {
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func filesWithParentDirs(files []string) []string {
|
||||
filesSet := map[string]bool{}
|
||||
|
||||
for _, file := range files {
|
||||
file = filepath.Clean(file)
|
||||
filesSet[file] = true
|
||||
|
||||
for _, dir := range util.ParentDirectories(file) {
|
||||
dir = filepath.Clean(dir)
|
||||
filesSet[dir] = true
|
||||
}
|
||||
}
|
||||
|
||||
newFiles := []string{}
|
||||
for file := range filesSet {
|
||||
newFiles = append(newFiles, file)
|
||||
}
|
||||
|
||||
return newFiles
|
||||
}
|
||||
|
||||
@@ -21,6 +21,7 @@ import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"sort"
|
||||
"testing"
|
||||
|
||||
"github.com/GoogleContainerTools/kaniko/pkg/util"
|
||||
@@ -60,6 +61,12 @@ func TestSnapshotFSFileChange(t *testing.T) {
|
||||
fooPath: "newbaz1",
|
||||
batPath: "baz",
|
||||
}
|
||||
for _, dir := range util.ParentDirectories(fooPath) {
|
||||
snapshotFiles[dir] = ""
|
||||
}
|
||||
for _, dir := range util.ParentDirectories(batPath) {
|
||||
snapshotFiles[dir] = ""
|
||||
}
|
||||
numFiles := 0
|
||||
for {
|
||||
hdr, err := tr.Next()
|
||||
@@ -75,7 +82,7 @@ func TestSnapshotFSFileChange(t *testing.T) {
|
||||
t.Fatalf("Contents of %s incorrect, expected: %s, actual: %s", hdr.Name, snapshotFiles[hdr.Name], string(contents))
|
||||
}
|
||||
}
|
||||
if numFiles != 2 {
|
||||
if numFiles != len(snapshotFiles) {
|
||||
t.Fatalf("Incorrect number of files were added, expected: 2, actual: %v", numFiles)
|
||||
}
|
||||
}
|
||||
@@ -105,6 +112,9 @@ func TestSnapshotFSChangePermissions(t *testing.T) {
|
||||
snapshotFiles := map[string]string{
|
||||
batPath: "baz2",
|
||||
}
|
||||
for _, dir := range util.ParentDirectories(batPath) {
|
||||
snapshotFiles[dir] = ""
|
||||
}
|
||||
numFiles := 0
|
||||
for {
|
||||
hdr, err := tr.Next()
|
||||
@@ -120,7 +130,7 @@ func TestSnapshotFSChangePermissions(t *testing.T) {
|
||||
t.Fatalf("Contents of %s incorrect, expected: %s, actual: %s", hdr.Name, snapshotFiles[hdr.Name], string(contents))
|
||||
}
|
||||
}
|
||||
if numFiles != 1 {
|
||||
if numFiles != len(snapshotFiles) {
|
||||
t.Fatalf("Incorrect number of files were added, expected: 1, got: %v", numFiles)
|
||||
}
|
||||
}
|
||||
@@ -147,7 +157,10 @@ func TestSnapshotFiles(t *testing.T) {
|
||||
}
|
||||
defer os.Remove(tarPath)
|
||||
|
||||
expectedFiles := []string{"/", "/tmp", filepath.Join(testDir, "foo")}
|
||||
expectedFiles := []string{
|
||||
filepath.Join(testDir, "foo"),
|
||||
}
|
||||
expectedFiles = append(expectedFiles, util.ParentDirectories(filepath.Join(testDir, "foo"))...)
|
||||
|
||||
f, err := os.Open(tarPath)
|
||||
if err != nil {
|
||||
@@ -166,6 +179,8 @@ func TestSnapshotFiles(t *testing.T) {
|
||||
}
|
||||
actualFiles = append(actualFiles, hdr.Name)
|
||||
}
|
||||
sort.Strings(expectedFiles)
|
||||
sort.Strings(actualFiles)
|
||||
testutil.CheckErrorAndDeepEqual(t, false, nil, expectedFiles, actualFiles)
|
||||
}
|
||||
|
||||
|
||||
+43
-22
@@ -37,11 +37,13 @@ import (
|
||||
func ResolveEnvironmentReplacementList(values, envs []string, isFilepath bool) ([]string, error) {
|
||||
var resolvedValues []string
|
||||
for _, value := range values {
|
||||
var resolved string
|
||||
var err error
|
||||
if IsSrcRemoteFileURL(value) {
|
||||
resolvedValues = append(resolvedValues, value)
|
||||
continue
|
||||
resolved, err = ResolveEnvironmentReplacement(value, envs, false)
|
||||
} else {
|
||||
resolved, err = ResolveEnvironmentReplacement(value, envs, isFilepath)
|
||||
}
|
||||
resolved, err := ResolveEnvironmentReplacement(value, envs, isFilepath)
|
||||
logrus.Debugf("Resolved %s to %s", value, resolved)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -76,6 +78,22 @@ func ResolveEnvironmentReplacement(value string, envs []string, isFilepath bool)
|
||||
return fp, nil
|
||||
}
|
||||
|
||||
func ResolveEnvAndWildcards(sd instructions.SourcesAndDest, buildcontext string, envs []string) ([]string, string, error) {
|
||||
// First, resolve any environment replacement
|
||||
resolvedEnvs, err := ResolveEnvironmentReplacementList(sd, envs, true)
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
dest := resolvedEnvs[len(resolvedEnvs)-1]
|
||||
// Resolve wildcards and get a list of resolved sources
|
||||
srcs, err := ResolveSources(resolvedEnvs[0:len(resolvedEnvs)-1], buildcontext)
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
err = IsSrcsValid(sd, srcs, buildcontext)
|
||||
return srcs, dest, err
|
||||
}
|
||||
|
||||
// ContainsWildcards returns true if any entry in paths contains wildcards
|
||||
func ContainsWildcards(paths []string) bool {
|
||||
for _, path := range paths {
|
||||
@@ -88,23 +106,22 @@ func ContainsWildcards(paths []string) bool {
|
||||
|
||||
// ResolveSources resolves the given sources if the sources contains wildcards
|
||||
// It returns a list of resolved sources
|
||||
func ResolveSources(srcsAndDest instructions.SourcesAndDest, root string) ([]string, error) {
|
||||
srcs := srcsAndDest[:len(srcsAndDest)-1]
|
||||
func ResolveSources(srcs []string, root string) ([]string, error) {
|
||||
// If sources contain wildcards, we first need to resolve them to actual paths
|
||||
if ContainsWildcards(srcs) {
|
||||
logrus.Debugf("Resolving srcs %v...", srcs)
|
||||
files, err := RelativeFiles("", root)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
srcs, err = matchSources(srcs, files)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
logrus.Debugf("Resolved sources to %v", srcs)
|
||||
if !ContainsWildcards(srcs) {
|
||||
return srcs, nil
|
||||
}
|
||||
// Check to make sure the sources are valid
|
||||
return srcs, IsSrcsValid(srcsAndDest, srcs, root)
|
||||
logrus.Infof("Resolving srcs %v...", srcs)
|
||||
files, err := RelativeFiles("", root)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
resolved, err := matchSources(srcs, files)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
logrus.Debugf("Resolved sources to %v", resolved)
|
||||
return resolved, nil
|
||||
}
|
||||
|
||||
// matchSources returns a list of sources that match wildcards
|
||||
@@ -165,20 +182,24 @@ func DestinationFilepath(src, dest, cwd string) (string, error) {
|
||||
}
|
||||
|
||||
// URLDestinationFilepath gives the destination a file from a remote URL should be saved to
|
||||
func URLDestinationFilepath(rawurl, dest, cwd string) string {
|
||||
func URLDestinationFilepath(rawurl, dest, cwd string, envs []string) (string, error) {
|
||||
if !IsDestDir(dest) {
|
||||
if !filepath.IsAbs(dest) {
|
||||
return filepath.Join(cwd, dest)
|
||||
return filepath.Join(cwd, dest), nil
|
||||
}
|
||||
return dest
|
||||
return dest, nil
|
||||
}
|
||||
urlBase := filepath.Base(rawurl)
|
||||
urlBase, err := ResolveEnvironmentReplacement(urlBase, envs, true)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
destPath := filepath.Join(dest, urlBase)
|
||||
|
||||
if !filepath.IsAbs(dest) {
|
||||
destPath = filepath.Join(cwd, destPath)
|
||||
}
|
||||
return destPath
|
||||
return destPath, nil
|
||||
}
|
||||
|
||||
func IsSrcsValid(srcsAndDest instructions.SourcesAndDest, resolvedSources []string, root string) error {
|
||||
|
||||
@@ -17,6 +17,7 @@ limitations under the License.
|
||||
package util
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"sort"
|
||||
"testing"
|
||||
|
||||
@@ -27,14 +28,12 @@ var testURL = "https://github.com/GoogleContainerTools/runtimes-common/blob/mast
|
||||
|
||||
var testEnvReplacement = []struct {
|
||||
path string
|
||||
command string
|
||||
envs []string
|
||||
isFilepath bool
|
||||
expectedPath string
|
||||
}{
|
||||
{
|
||||
path: "/simple/path",
|
||||
command: "WORKDIR /simple/path",
|
||||
path: "/simple/path",
|
||||
envs: []string{
|
||||
"simple=/path/",
|
||||
},
|
||||
@@ -42,8 +41,7 @@ var testEnvReplacement = []struct {
|
||||
expectedPath: "/simple/path",
|
||||
},
|
||||
{
|
||||
path: "/simple/path/",
|
||||
command: "WORKDIR /simple/path/",
|
||||
path: "/simple/path/",
|
||||
envs: []string{
|
||||
"simple=/path/",
|
||||
},
|
||||
@@ -51,8 +49,7 @@ var testEnvReplacement = []struct {
|
||||
expectedPath: "/simple/path/",
|
||||
},
|
||||
{
|
||||
path: "${a}/b",
|
||||
command: "WORKDIR ${a}/b",
|
||||
path: "${a}/b",
|
||||
envs: []string{
|
||||
"a=/path/",
|
||||
"b=/path2/",
|
||||
@@ -61,8 +58,7 @@ var testEnvReplacement = []struct {
|
||||
expectedPath: "/path/b",
|
||||
},
|
||||
{
|
||||
path: "/$a/b",
|
||||
command: "COPY ${a}/b /c/",
|
||||
path: "/$a/b",
|
||||
envs: []string{
|
||||
"a=/path/",
|
||||
"b=/path2/",
|
||||
@@ -71,8 +67,7 @@ var testEnvReplacement = []struct {
|
||||
expectedPath: "/path/b",
|
||||
},
|
||||
{
|
||||
path: "/$a/b/",
|
||||
command: "COPY /${a}/b /c/",
|
||||
path: "/$a/b/",
|
||||
envs: []string{
|
||||
"a=/path/",
|
||||
"b=/path2/",
|
||||
@@ -81,8 +76,7 @@ var testEnvReplacement = []struct {
|
||||
expectedPath: "/path/b/",
|
||||
},
|
||||
{
|
||||
path: "\\$foo",
|
||||
command: "COPY \\$foo /quux",
|
||||
path: "\\$foo",
|
||||
envs: []string{
|
||||
"foo=/path/",
|
||||
},
|
||||
@@ -90,8 +84,14 @@ var testEnvReplacement = []struct {
|
||||
expectedPath: "$foo",
|
||||
},
|
||||
{
|
||||
path: "8080/$protocol",
|
||||
command: "EXPOSE 8080/$protocol",
|
||||
path: "8080/$protocol",
|
||||
envs: []string{
|
||||
"protocol=udp",
|
||||
},
|
||||
expectedPath: "8080/udp",
|
||||
},
|
||||
{
|
||||
path: "8080/$protocol",
|
||||
envs: []string{
|
||||
"protocol=udp",
|
||||
},
|
||||
@@ -183,6 +183,7 @@ var urlDestFilepathTests = []struct {
|
||||
cwd string
|
||||
dest string
|
||||
expectedDest string
|
||||
envs []string
|
||||
}{
|
||||
{
|
||||
url: "https://something/something",
|
||||
@@ -202,12 +203,19 @@ var urlDestFilepathTests = []struct {
|
||||
dest: "/dest/",
|
||||
expectedDest: "/dest/something",
|
||||
},
|
||||
{
|
||||
url: "https://something/$foo.tar.gz",
|
||||
cwd: "/test",
|
||||
dest: "/foo/",
|
||||
expectedDest: "/foo/bar.tar.gz",
|
||||
envs: []string{"foo=bar"},
|
||||
},
|
||||
}
|
||||
|
||||
func Test_UrlDestFilepath(t *testing.T) {
|
||||
for _, test := range urlDestFilepathTests {
|
||||
actualDest := URLDestinationFilepath(test.url, test.dest, test.cwd)
|
||||
testutil.CheckErrorAndDeepEqual(t, false, nil, test.expectedDest, actualDest)
|
||||
actualDest, err := URLDestinationFilepath(test.url, test.dest, test.cwd, test.envs)
|
||||
testutil.CheckErrorAndDeepEqual(t, false, err, test.expectedDest, actualDest)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -400,7 +408,6 @@ var testResolveSources = []struct {
|
||||
"context/foo",
|
||||
"context/b*",
|
||||
testURL,
|
||||
"dest/",
|
||||
},
|
||||
expectedList: []string{
|
||||
"context/foo",
|
||||
@@ -448,3 +455,57 @@ func Test_RemoteUrls(t *testing.T) {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestResolveEnvironmentReplacementList(t *testing.T) {
|
||||
type args struct {
|
||||
values []string
|
||||
envs []string
|
||||
isFilepath bool
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
want []string
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "url",
|
||||
args: args{
|
||||
values: []string{
|
||||
"https://google.com/$foo", "$bar",
|
||||
},
|
||||
envs: []string{
|
||||
"foo=baz",
|
||||
"bar=bat",
|
||||
},
|
||||
},
|
||||
want: []string{"https://google.com/baz", "bat"},
|
||||
},
|
||||
{
|
||||
name: "mixed",
|
||||
args: args{
|
||||
values: []string{
|
||||
"$foo", "$bar$baz", "baz",
|
||||
},
|
||||
envs: []string{
|
||||
"foo=FOO",
|
||||
"bar=BAR",
|
||||
"baz=BAZ",
|
||||
},
|
||||
},
|
||||
want: []string{"FOO", "BARBAZ", "baz"},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got, err := ResolveEnvironmentReplacementList(tt.args.values, tt.args.envs, tt.args.isFilepath)
|
||||
if (err != nil) != tt.wantErr {
|
||||
t.Errorf("ResolveEnvironmentReplacementList() error = %v, wantErr %v", err, tt.wantErr)
|
||||
return
|
||||
}
|
||||
if !reflect.DeepEqual(got, tt.want) {
|
||||
t.Errorf("ResolveEnvironmentReplacementList() = %v, want %v", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
+26
-21
@@ -216,27 +216,16 @@ func extractFile(dest string, hdr *tar.Header, tr io.Reader) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// manually set permissions on file, since the default umask (022) will interfere
|
||||
if err = os.Chmod(path, mode); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err = io.Copy(currFile, tr); err != nil {
|
||||
return err
|
||||
}
|
||||
if err = currFile.Chown(uid, gid); err != nil {
|
||||
if err = setFilePermissions(path, mode, uid, gid); err != nil {
|
||||
return err
|
||||
}
|
||||
currFile.Close()
|
||||
case tar.TypeDir:
|
||||
logrus.Debugf("creating dir %s", path)
|
||||
if err := os.MkdirAll(path, mode); err != nil {
|
||||
return err
|
||||
}
|
||||
// In some cases, MkdirAll doesn't change the permissions, so run Chmod
|
||||
if err := os.Chmod(path, mode); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := os.Chown(path, uid, gid); err != nil {
|
||||
if err := mkdirAllWithPermissions(path, mode, uid, gid); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -429,10 +418,7 @@ func CreateFile(path string, reader io.Reader, perm os.FileMode, uid uint32, gid
|
||||
if _, err := io.Copy(dest, reader); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := dest.Chmod(perm); err != nil {
|
||||
return err
|
||||
}
|
||||
return dest.Chown(int(uid), int(gid))
|
||||
return setFilePermissions(path, perm, int(uid), int(gid))
|
||||
}
|
||||
|
||||
// AddVolumePath adds the given path to the volume whitelist.
|
||||
@@ -492,13 +478,11 @@ func CopyDir(src, dest, buildcontext string) ([]string, error) {
|
||||
if fi.IsDir() {
|
||||
logrus.Debugf("Creating directory %s", destPath)
|
||||
|
||||
mode := fi.Mode()
|
||||
uid := int(fi.Sys().(*syscall.Stat_t).Uid)
|
||||
gid := int(fi.Sys().(*syscall.Stat_t).Gid)
|
||||
|
||||
if err := os.MkdirAll(destPath, fi.Mode()); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := os.Chown(destPath, uid, gid); err != nil {
|
||||
if err := mkdirAllWithPermissions(destPath, mode, uid, gid); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
} else if fi.Mode()&os.ModeSymlink != 0 {
|
||||
@@ -614,3 +598,24 @@ func HasFilepathPrefix(path, prefix string, prefixMatchOnly bool) bool {
|
||||
func Volumes() []string {
|
||||
return volumes
|
||||
}
|
||||
|
||||
func mkdirAllWithPermissions(path string, mode os.FileMode, uid, gid int) error {
|
||||
if err := os.MkdirAll(path, mode); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := os.Chown(path, uid, gid); err != nil {
|
||||
return err
|
||||
}
|
||||
// In some cases, MkdirAll doesn't change the permissions, so run Chmod
|
||||
// Must chmod after chown because chown resets the file mode.
|
||||
return os.Chmod(path, mode)
|
||||
}
|
||||
|
||||
func setFilePermissions(path string, mode os.FileMode, uid, gid int) error {
|
||||
if err := os.Chown(path, uid, gid); err != nil {
|
||||
return err
|
||||
}
|
||||
// manually set permissions on file, since the default umask (022) will interfere
|
||||
// Must chmod after chown because chown resets the file mode.
|
||||
return os.Chmod(path, mode)
|
||||
}
|
||||
|
||||
@@ -503,6 +503,30 @@ func TestExtractFile(t *testing.T) {
|
||||
filesAreHardlinks("/bin/uncompress", "/bin/gzip"),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "file with setuid bit",
|
||||
contents: []byte("helloworld"),
|
||||
hdrs: []*tar.Header{fileHeader("./bar", "helloworld", 04644)},
|
||||
checkers: []checker{
|
||||
fileExists("/bar"),
|
||||
fileMatches("/bar", []byte("helloworld")),
|
||||
permissionsMatch("/bar", 0644|os.ModeSetuid),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "dir with sticky bit",
|
||||
contents: []byte("helloworld"),
|
||||
hdrs: []*tar.Header{
|
||||
dirHeader("./foo", 01755),
|
||||
fileHeader("./foo/bar", "helloworld", 0644),
|
||||
},
|
||||
checkers: []checker{
|
||||
fileExists("/foo/bar"),
|
||||
fileMatches("/foo/bar", []byte("helloworld")),
|
||||
permissionsMatch("/foo/bar", 0644),
|
||||
permissionsMatch("/foo", 0755|os.ModeDir|os.ModeSticky),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range tcs {
|
||||
|
||||
Reference in New Issue
Block a user