318 lines
7.4 KiB
Go
318 lines
7.4 KiB
Go
/*
|
|
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 commands
|
|
|
|
import (
|
|
"archive/tar"
|
|
"bytes"
|
|
"io"
|
|
"log"
|
|
"os"
|
|
"os/user"
|
|
"path/filepath"
|
|
"testing"
|
|
|
|
"github.com/GoogleContainerTools/kaniko/pkg/dockerfile"
|
|
"github.com/GoogleContainerTools/kaniko/testutil"
|
|
v1 "github.com/google/go-containerregistry/pkg/v1"
|
|
)
|
|
|
|
func Test_addDefaultHOME(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
user string
|
|
mockUser *user.User
|
|
lookupError error
|
|
initial []string
|
|
expected []string
|
|
}{
|
|
{
|
|
name: "HOME already set",
|
|
user: "",
|
|
initial: []string{
|
|
"HOME=/something",
|
|
"PATH=/something/else",
|
|
},
|
|
expected: []string{
|
|
"HOME=/something",
|
|
"PATH=/something/else",
|
|
},
|
|
},
|
|
{
|
|
name: "HOME not set and user not set",
|
|
user: "",
|
|
initial: []string{
|
|
"PATH=/something/else",
|
|
},
|
|
expected: []string{
|
|
"PATH=/something/else",
|
|
"HOME=/root",
|
|
},
|
|
},
|
|
{
|
|
name: "HOME not set and user and homedir for the user set",
|
|
user: "www-add",
|
|
mockUser: &user.User{
|
|
Username: "www-add",
|
|
HomeDir: "/home/some-other",
|
|
},
|
|
initial: []string{
|
|
"PATH=/something/else",
|
|
},
|
|
expected: []string{
|
|
"PATH=/something/else",
|
|
"HOME=/home/some-other",
|
|
},
|
|
},
|
|
{
|
|
name: "USER is set using the UID",
|
|
user: "1000",
|
|
mockUser: &user.User{
|
|
Username: "1000",
|
|
HomeDir: "/",
|
|
},
|
|
initial: []string{
|
|
"PATH=/something/else",
|
|
},
|
|
expected: []string{
|
|
"PATH=/something/else",
|
|
"HOME=/",
|
|
},
|
|
},
|
|
{
|
|
name: "HOME not set and user is set to root",
|
|
user: "root",
|
|
mockUser: &user.User{
|
|
Username: "root",
|
|
},
|
|
initial: []string{
|
|
"PATH=/something/else",
|
|
},
|
|
expected: []string{
|
|
"PATH=/something/else",
|
|
"HOME=/root",
|
|
},
|
|
},
|
|
}
|
|
for _, test := range tests {
|
|
t.Run(test.name, func(t *testing.T) {
|
|
original := userLookup
|
|
userLookup = func(username string) (*user.User, error) { return test.mockUser, test.lookupError }
|
|
defer func() {
|
|
userLookup = original
|
|
}()
|
|
actual, err := addDefaultHOME(test.user, test.initial)
|
|
testutil.CheckErrorAndDeepEqual(t, false, err, test.expected, actual)
|
|
})
|
|
}
|
|
}
|
|
|
|
func prepareTarFixture(t *testing.T, fileNames []string) ([]byte, error) {
|
|
dir := t.TempDir()
|
|
|
|
content := `
|
|
Meow meow meow meow
|
|
meow meow meow meow
|
|
`
|
|
for _, name := range fileNames {
|
|
if err := os.WriteFile(filepath.Join(dir, name), []byte(content), 0777); err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
writer := bytes.NewBuffer([]byte{})
|
|
tw := tar.NewWriter(writer)
|
|
defer tw.Close()
|
|
filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if info.IsDir() {
|
|
return nil
|
|
}
|
|
|
|
hdr, err := tar.FileInfoHeader(info, "")
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if err := tw.WriteHeader(hdr); err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
body, err := os.ReadFile(path)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if _, err := tw.Write(body); err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
|
|
return nil
|
|
})
|
|
|
|
return writer.Bytes(), nil
|
|
}
|
|
|
|
func Test_CachingRunCommand_ExecuteCommand(t *testing.T) {
|
|
tarContent, err := prepareTarFixture(t, []string{"foo.txt"})
|
|
if err != nil {
|
|
t.Errorf("couldn't prepare tar fixture %v", err)
|
|
}
|
|
|
|
config := &v1.Config{}
|
|
buildArgs := &dockerfile.BuildArgs{}
|
|
|
|
type testCase struct {
|
|
desctiption string
|
|
expectLayer bool
|
|
expectErr bool
|
|
count *int
|
|
expectedCount int
|
|
command *CachingRunCommand
|
|
extractedFiles []string
|
|
contextFiles []string
|
|
}
|
|
testCases := []testCase{
|
|
func() testCase {
|
|
c := &CachingRunCommand{
|
|
img: fakeImage{
|
|
ImageLayers: []v1.Layer{
|
|
fakeLayer{TarContent: tarContent},
|
|
},
|
|
},
|
|
}
|
|
count := 0
|
|
tc := testCase{
|
|
desctiption: "with valid image and valid layer",
|
|
count: &count,
|
|
expectedCount: 1,
|
|
expectLayer: true,
|
|
extractedFiles: []string{"/foo.txt"},
|
|
contextFiles: []string{"foo.txt"},
|
|
}
|
|
c.extractFn = func(_ string, _ *tar.Header, _ string, _ io.Reader) error {
|
|
*tc.count++
|
|
return nil
|
|
}
|
|
tc.command = c
|
|
return tc
|
|
}(),
|
|
func() testCase {
|
|
c := &CachingRunCommand{}
|
|
tc := testCase{
|
|
desctiption: "with no image",
|
|
expectErr: true,
|
|
}
|
|
c.extractFn = func(_ string, _ *tar.Header, _ string, _ io.Reader) error {
|
|
return nil
|
|
}
|
|
tc.command = c
|
|
return tc
|
|
}(),
|
|
func() testCase {
|
|
c := &CachingRunCommand{
|
|
img: fakeImage{},
|
|
}
|
|
|
|
c.extractFn = func(_ string, _ *tar.Header, _ string, _ io.Reader) error {
|
|
return nil
|
|
}
|
|
|
|
return testCase{
|
|
desctiption: "with image containing no layers",
|
|
expectErr: true,
|
|
command: c,
|
|
}
|
|
}(),
|
|
func() testCase {
|
|
c := &CachingRunCommand{
|
|
img: fakeImage{
|
|
ImageLayers: []v1.Layer{
|
|
fakeLayer{},
|
|
},
|
|
},
|
|
}
|
|
c.extractFn = func(_ string, _ *tar.Header, _ string, _ io.Reader) error {
|
|
return nil
|
|
}
|
|
tc := testCase{
|
|
desctiption: "with image one layer which has no tar content",
|
|
expectErr: false, // this one probably should fail but doesn't because of how ExecuteCommand and util.GetFSFromLayers are implemented - cvgw- 2019-11-25
|
|
expectLayer: true,
|
|
}
|
|
tc.command = c
|
|
return tc
|
|
}(),
|
|
}
|
|
|
|
for _, tc := range testCases {
|
|
t.Run(tc.desctiption, func(t *testing.T) {
|
|
c := tc.command
|
|
err := c.ExecuteCommand(config, buildArgs)
|
|
if !tc.expectErr && err != nil {
|
|
t.Errorf("Expected err to be nil but was %v", err)
|
|
} else if tc.expectErr && err == nil {
|
|
t.Error("Expected err but was nil")
|
|
}
|
|
|
|
if tc.count != nil {
|
|
if *tc.count != tc.expectedCount {
|
|
t.Errorf("Expected extractFn to be called %v times but was called %v times", 1, *tc.count)
|
|
}
|
|
for _, file := range tc.extractedFiles {
|
|
match := false
|
|
cmdFiles := c.extractedFiles
|
|
for _, f := range cmdFiles {
|
|
if file == f {
|
|
match = true
|
|
break
|
|
}
|
|
}
|
|
if !match {
|
|
t.Errorf("Expected extracted files to include %v but did not %v", file, cmdFiles)
|
|
}
|
|
}
|
|
|
|
// CachingRunCommand does not override BaseCommand
|
|
// FilesUseFromContext so this will always return an empty slice and no error
|
|
// This seems like it might be a bug as it results in RunCommands and CachingRunCommands generating different cache keys - cvgw - 2019-11-27
|
|
cmdFiles, err := c.FilesUsedFromContext(
|
|
config, buildArgs,
|
|
)
|
|
if err != nil {
|
|
t.Errorf("failed to get files used from context from command")
|
|
}
|
|
|
|
if len(cmdFiles) != 0 {
|
|
t.Errorf("expected files used from context to be empty but was not")
|
|
}
|
|
}
|
|
|
|
if c.layer == nil && tc.expectLayer {
|
|
t.Error("expected the command to have a layer set but instead was nil")
|
|
} else if c.layer != nil && !tc.expectLayer {
|
|
t.Error("expected the command to have no layer set but instead found a layer")
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestSetWorkDirIfExists(t *testing.T) {
|
|
testDir := t.TempDir()
|
|
testutil.CheckDeepEqual(t, testDir, setWorkDirIfExists(testDir))
|
|
testutil.CheckDeepEqual(t, "", setWorkDirIfExists("doesnot-exists"))
|
|
}
|