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"))
 | 
						|
}
 |