mirror of
https://github.com/GoogleContainerTools/kaniko
synced 2026-10-17 21:11:54 +02:00
Merge pull request #1155 from tejal29/fix_multi_stage_symlinks
Refactor Kaniko to test across multistages
This commit is contained in:
+21
-11
@@ -98,7 +98,7 @@ func newStageBuilder(opts *config.KanikoOptions, stage config.KanikoStage, cross
|
||||
return nil, err
|
||||
}
|
||||
l := snapshot.NewLayeredMap(hasher, util.CacheHasher())
|
||||
snapshotter := snapshot.NewSnapshotter(l, constants.RootDir)
|
||||
snapshotter := snapshot.NewSnapshotter(l, config.RootDir)
|
||||
|
||||
digest, err := sourceImage.Digest()
|
||||
if err != nil {
|
||||
@@ -299,7 +299,7 @@ func (s *stageBuilder) build() error {
|
||||
if shouldUnpack {
|
||||
t := timing.Start("FS Unpacking")
|
||||
|
||||
if _, err := util.GetFSFromImage(constants.RootDir, s.image, util.ExtractFile); err != nil {
|
||||
if _, err := util.GetFSFromImage(config.RootDir, s.image, util.ExtractFile); err != nil {
|
||||
return errors.Wrap(err, "failed to get filesystem from image")
|
||||
}
|
||||
|
||||
@@ -308,7 +308,7 @@ func (s *stageBuilder) build() error {
|
||||
logrus.Info("Skipping unpacking as no commands require it.")
|
||||
}
|
||||
|
||||
if err := util.DetectFilesystemWhitelist(constants.WhitelistPath); err != nil {
|
||||
if err := util.DetectFilesystemWhitelist(config.WhitelistPath); err != nil {
|
||||
return errors.Wrap(err, "failed to check filesystem whitelist")
|
||||
}
|
||||
|
||||
@@ -525,7 +525,6 @@ func CalculateDependencies(opts *config.KanikoOptions) (map[int][]string, error)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
depGraph[i] = append(depGraph[i], resolved[0:len(resolved)-1]...)
|
||||
}
|
||||
case *instructions.EnvCommand:
|
||||
@@ -641,20 +640,23 @@ func DoBuild(opts *config.KanikoOptions) (v1.Image, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
dstDir := filepath.Join(constants.KanikoDir, strconv.Itoa(index))
|
||||
dstDir := filepath.Join(config.KanikoDir, strconv.Itoa(index))
|
||||
if err := os.MkdirAll(dstDir, 0644); err != nil {
|
||||
return nil, err
|
||||
return nil, errors.Wrap(err,
|
||||
fmt.Sprintf("to create workspace for stage %s",
|
||||
stageIdxToDigest[strconv.Itoa(index)],
|
||||
))
|
||||
}
|
||||
for _, p := range filesToSave {
|
||||
logrus.Infof("Saving file %s for later use", p)
|
||||
if err := util.CopyFileOrSymlink(p, dstDir); err != nil {
|
||||
return nil, err
|
||||
if err := util.CopyFileOrSymlink(p, dstDir, config.RootDir); err != nil {
|
||||
return nil, errors.Wrap(err, "could not save file")
|
||||
}
|
||||
}
|
||||
|
||||
// Delete the filesystem
|
||||
if err := util.DeleteFilesystem(); err != nil {
|
||||
return nil, err
|
||||
return nil, errors.Wrap(err, fmt.Sprintf("deleting file system after satge %d", index))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -666,14 +668,22 @@ func DoBuild(opts *config.KanikoOptions) (v1.Image, error) {
|
||||
func filesToSave(deps []string) ([]string, error) {
|
||||
srcFiles := []string{}
|
||||
for _, src := range deps {
|
||||
srcs, err := filepath.Glob(src)
|
||||
srcs, err := filepath.Glob(filepath.Join(config.RootDir, src))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, f := range srcs {
|
||||
if link, err := util.EvalSymLink(f); err == nil {
|
||||
link, err = filepath.Rel(config.RootDir, link)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, fmt.Sprintf("could not find relative path to %s", config.RootDir))
|
||||
}
|
||||
srcFiles = append(srcFiles, link)
|
||||
}
|
||||
f, err = filepath.Rel(config.RootDir, f)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, fmt.Sprintf("could not find relative path to %s", config.RootDir))
|
||||
}
|
||||
srcFiles = append(srcFiles, f)
|
||||
}
|
||||
}
|
||||
@@ -729,7 +739,7 @@ func fetchExtraStages(stages []config.KanikoStage, opts *config.KanikoOptions) e
|
||||
func extractImageToDependencyDir(name string, image v1.Image) error {
|
||||
t := timing.Start("Extracting Image to Dependency Dir")
|
||||
defer timing.DefaultRun.Stop(t)
|
||||
dependencyDir := filepath.Join(constants.KanikoDir, name)
|
||||
dependencyDir := filepath.Join(config.KanikoDir, name)
|
||||
if err := os.MkdirAll(dependencyDir, 0755); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
+13
-16
@@ -371,10 +371,15 @@ func Test_filesToSave(t *testing.T) {
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
tmpDir, err := ioutil.TempDir("", "")
|
||||
original := config.RootDir
|
||||
config.RootDir = tmpDir
|
||||
if err != nil {
|
||||
t.Errorf("error creating tmpdir: %s", err)
|
||||
}
|
||||
defer os.RemoveAll(tmpDir)
|
||||
defer func() {
|
||||
config.RootDir = original
|
||||
os.RemoveAll(tmpDir)
|
||||
}()
|
||||
|
||||
for _, f := range tt.files {
|
||||
p := filepath.Join(tmpDir, f)
|
||||
@@ -391,22 +396,14 @@ func Test_filesToSave(t *testing.T) {
|
||||
fp.Close()
|
||||
}
|
||||
|
||||
args := []string{}
|
||||
for _, arg := range tt.args {
|
||||
args = append(args, filepath.Join(tmpDir, arg))
|
||||
}
|
||||
got, err := filesToSave(args)
|
||||
got, err := filesToSave(tt.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(tt.want)
|
||||
sort.Strings(got)
|
||||
if !reflect.DeepEqual(got, want) {
|
||||
t.Errorf("filesToSave() = %v, want %v", got, want)
|
||||
if !reflect.DeepEqual(got, tt.want) {
|
||||
t.Errorf("filesToSave() = %v, want %v", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -1129,9 +1126,9 @@ COPY %s bar.txt
|
||||
for key, value := range tc.args {
|
||||
sb.args.AddArg(key, &value)
|
||||
}
|
||||
tmp := commands.RootDir
|
||||
tmp := config.RootDir
|
||||
if tc.rootDir != "" {
|
||||
commands.RootDir = tc.rootDir
|
||||
config.RootDir = tc.rootDir
|
||||
}
|
||||
err := sb.build()
|
||||
if err != nil {
|
||||
@@ -1141,7 +1138,7 @@ COPY %s bar.txt
|
||||
assertCacheKeys(t, tc.expectedCacheKeys, lc.receivedKeys, "receive")
|
||||
assertCacheKeys(t, tc.pushedCacheKeys, keys, "push")
|
||||
|
||||
commands.RootDir = tmp
|
||||
config.RootDir = tmp
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
@@ -0,0 +1,187 @@
|
||||
/*
|
||||
Copyright 2020 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 executor
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/GoogleContainerTools/kaniko/pkg/config"
|
||||
"github.com/GoogleContainerTools/kaniko/pkg/constants"
|
||||
"github.com/GoogleContainerTools/kaniko/testutil"
|
||||
)
|
||||
|
||||
func TestCopyCommand_Multistage(t *testing.T) {
|
||||
t.Run("copy a file across multistage", func(t *testing.T) {
|
||||
testDir, fn := setupMultistageTests(t)
|
||||
defer fn()
|
||||
dockerFile := fmt.Sprintf(`
|
||||
FROM scratch as first
|
||||
COPY foo/bam.txt copied/
|
||||
ENV test test
|
||||
|
||||
From scratch as second
|
||||
COPY --from=first copied/bam.txt output/bam.txt`)
|
||||
ioutil.WriteFile(filepath.Join(testDir, "workspace", "Dockerfile"), []byte(dockerFile), 0755)
|
||||
opts := &config.KanikoOptions{
|
||||
DockerfilePath: filepath.Join(testDir, "workspace", "Dockerfile"),
|
||||
SrcContext: filepath.Join(testDir, "workspace"),
|
||||
SnapshotMode: constants.SnapshotModeFull,
|
||||
}
|
||||
_, err := DoBuild(opts)
|
||||
testutil.CheckNoError(t, err)
|
||||
// Check Image has one layer bam.txt
|
||||
files, err := ioutil.ReadDir(filepath.Join(testDir, "output"))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
testutil.CheckDeepEqual(t, 1, len(files))
|
||||
testutil.CheckDeepEqual(t, files[0].Name(), "bam.txt")
|
||||
|
||||
})
|
||||
|
||||
t.Run("copy a file across multistage into a directory", func(t *testing.T) {
|
||||
testDir, fn := setupMultistageTests(t)
|
||||
defer fn()
|
||||
dockerFile := fmt.Sprintf(`
|
||||
FROM scratch as first
|
||||
COPY foo/bam.txt copied/
|
||||
ENV test test
|
||||
|
||||
From scratch as second
|
||||
COPY --from=first copied/bam.txt output/`)
|
||||
ioutil.WriteFile(filepath.Join(testDir, "workspace", "Dockerfile"), []byte(dockerFile), 0755)
|
||||
opts := &config.KanikoOptions{
|
||||
DockerfilePath: filepath.Join(testDir, "workspace", "Dockerfile"),
|
||||
SrcContext: filepath.Join(testDir, "workspace"),
|
||||
SnapshotMode: constants.SnapshotModeFull,
|
||||
}
|
||||
_, err := DoBuild(opts)
|
||||
testutil.CheckNoError(t, err)
|
||||
files, err := ioutil.ReadDir(filepath.Join(testDir, "output"))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
testutil.CheckDeepEqual(t, 1, len(files))
|
||||
testutil.CheckDeepEqual(t, files[0].Name(), "bam.txt")
|
||||
})
|
||||
t.Run("copy directory across multistage into a directory", func(t *testing.T) {
|
||||
testDir, fn := setupMultistageTests(t)
|
||||
defer fn()
|
||||
dockerFile := fmt.Sprintf(`
|
||||
FROM scratch as first
|
||||
COPY foo copied
|
||||
ENV test test
|
||||
|
||||
From scratch as second
|
||||
COPY --from=first copied another`)
|
||||
ioutil.WriteFile(filepath.Join(testDir, "workspace", "Dockerfile"), []byte(dockerFile), 0755)
|
||||
opts := &config.KanikoOptions{
|
||||
DockerfilePath: filepath.Join(testDir, "workspace", "Dockerfile"),
|
||||
SrcContext: filepath.Join(testDir, "workspace"),
|
||||
SnapshotMode: constants.SnapshotModeFull,
|
||||
}
|
||||
_, err := DoBuild(opts)
|
||||
testutil.CheckNoError(t, err)
|
||||
// Check Image has one layer bam.txt
|
||||
files, err := ioutil.ReadDir(filepath.Join(testDir, "another"))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
testutil.CheckDeepEqual(t, 2, len(files))
|
||||
testutil.CheckDeepEqual(t, files[0].Name(), "bam.link")
|
||||
testutil.CheckDeepEqual(t, files[1].Name(), "bam.txt")
|
||||
// TODO fix this
|
||||
// path := filepath.Join(testDir, "output/another", "bam.link")
|
||||
//linkName, err := os.Readlink(path)
|
||||
//if err != nil {
|
||||
// t.Fatal(err)
|
||||
//}
|
||||
//testutil.CheckDeepEqual(t, linkName, "bam.txt")
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
func setupMultistageTests(t *testing.T) (string, func()) {
|
||||
testDir, err := ioutil.TempDir("", "")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// Create workspace with files, dirs, and symlinks
|
||||
// workspace tree:
|
||||
// /root
|
||||
// /kaniko
|
||||
// /workspace
|
||||
// - /foo
|
||||
// - bam.txt
|
||||
// - bam.link -> bam.txt
|
||||
// - /bin
|
||||
// - exec.link -> ../exec
|
||||
// exec
|
||||
|
||||
// Make directory for stage or else the executor will create with permissions 0664
|
||||
// and we will run into issue https://github.com/golang/go/issues/22323
|
||||
if err := os.MkdirAll(filepath.Join(testDir, "kaniko/0"), 0755); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
workspace := filepath.Join(testDir, "workspace")
|
||||
// Make foo
|
||||
if err := os.MkdirAll(filepath.Join(workspace, "foo"), 0755); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
file := filepath.Join(workspace, "foo", "bam.txt")
|
||||
if err := ioutil.WriteFile(file, []byte("meow"), 0755); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
os.Symlink("bam.txt", filepath.Join(workspace, "foo", "bam.link"))
|
||||
|
||||
// Make a file with contents link
|
||||
file = filepath.Join(workspace, "exec")
|
||||
if err := ioutil.WriteFile(file, []byte("woof"), 0755); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
// Make bin
|
||||
if err := os.MkdirAll(filepath.Join(workspace, "bin"), 0755); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
os.Symlink("../exec", filepath.Join(workspace, "bin", "exec.link"))
|
||||
|
||||
// set up config
|
||||
config.RootDir = testDir
|
||||
config.KanikoDir = fmt.Sprintf("%s/%s", testDir, "kaniko")
|
||||
// Write a whitelist path
|
||||
if err := os.MkdirAll(filepath.Join(testDir, "proc"), 0755); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
mFile := filepath.Join(testDir, "proc/mountinfo")
|
||||
mountInfo := fmt.Sprintf(
|
||||
`36 35 98:0 /kaniko %s/kaniko rw,noatime master:1 - ext3 /dev/root rw,errors=continue
|
||||
36 35 98:0 /proc %s/proc rw,noatime master:1 - ext3 /dev/root rw,errors=continue
|
||||
`, testDir, testDir)
|
||||
if err := ioutil.WriteFile(mFile, []byte(mountInfo), 0644); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
config.WhitelistPath = mFile
|
||||
return testDir, func() {
|
||||
config.KanikoDir = constants.KanikoDir
|
||||
config.RootDir = constants.RootDir
|
||||
config.WhitelistPath = constants.WhitelistPath
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user