github.com/AliyunContainerService/cli@v0.0.0-20181009023821-814ced4b30d0/cli/command/image/build/context_test.go (about)

     1  package build
     2  
     3  import (
     4  	"archive/tar"
     5  	"bytes"
     6  	"io"
     7  	"io/ioutil"
     8  	"os"
     9  	"path/filepath"
    10  	"runtime"
    11  	"strings"
    12  	"testing"
    13  
    14  	"github.com/docker/docker/pkg/archive"
    15  	"gotest.tools/assert"
    16  	is "gotest.tools/assert/cmp"
    17  )
    18  
    19  const dockerfileContents = "FROM busybox"
    20  
    21  var prepareEmpty = func(t *testing.T) (string, func()) {
    22  	return "", func() {}
    23  }
    24  
    25  var prepareNoFiles = func(t *testing.T) (string, func()) {
    26  	return createTestTempDir(t, "", "builder-context-test")
    27  }
    28  
    29  var prepareOneFile = func(t *testing.T) (string, func()) {
    30  	contextDir, cleanup := createTestTempDir(t, "", "builder-context-test")
    31  	createTestTempFile(t, contextDir, DefaultDockerfileName, dockerfileContents, 0777)
    32  	return contextDir, cleanup
    33  }
    34  
    35  func testValidateContextDirectory(t *testing.T, prepare func(t *testing.T) (string, func()), excludes []string) {
    36  	contextDir, cleanup := prepare(t)
    37  	defer cleanup()
    38  
    39  	err := ValidateContextDirectory(contextDir, excludes)
    40  	assert.NilError(t, err)
    41  }
    42  
    43  func TestGetContextFromLocalDirNoDockerfile(t *testing.T) {
    44  	contextDir, cleanup := createTestTempDir(t, "", "builder-context-test")
    45  	defer cleanup()
    46  
    47  	_, _, err := GetContextFromLocalDir(contextDir, "")
    48  	assert.ErrorContains(t, err, "Dockerfile")
    49  }
    50  
    51  func TestGetContextFromLocalDirNotExistingDir(t *testing.T) {
    52  	contextDir, cleanup := createTestTempDir(t, "", "builder-context-test")
    53  	defer cleanup()
    54  
    55  	fakePath := filepath.Join(contextDir, "fake")
    56  
    57  	_, _, err := GetContextFromLocalDir(fakePath, "")
    58  	assert.ErrorContains(t, err, "fake")
    59  }
    60  
    61  func TestGetContextFromLocalDirNotExistingDockerfile(t *testing.T) {
    62  	contextDir, cleanup := createTestTempDir(t, "", "builder-context-test")
    63  	defer cleanup()
    64  
    65  	fakePath := filepath.Join(contextDir, "fake")
    66  
    67  	_, _, err := GetContextFromLocalDir(contextDir, fakePath)
    68  	assert.ErrorContains(t, err, "fake")
    69  }
    70  
    71  func TestGetContextFromLocalDirWithNoDirectory(t *testing.T) {
    72  	contextDir, dirCleanup := createTestTempDir(t, "", "builder-context-test")
    73  	defer dirCleanup()
    74  
    75  	createTestTempFile(t, contextDir, DefaultDockerfileName, dockerfileContents, 0777)
    76  
    77  	chdirCleanup := chdir(t, contextDir)
    78  	defer chdirCleanup()
    79  
    80  	absContextDir, relDockerfile, err := GetContextFromLocalDir(contextDir, "")
    81  	assert.NilError(t, err)
    82  
    83  	assert.Check(t, is.Equal(contextDir, absContextDir))
    84  	assert.Check(t, is.Equal(DefaultDockerfileName, relDockerfile))
    85  }
    86  
    87  func TestGetContextFromLocalDirWithDockerfile(t *testing.T) {
    88  	contextDir, cleanup := createTestTempDir(t, "", "builder-context-test")
    89  	defer cleanup()
    90  
    91  	createTestTempFile(t, contextDir, DefaultDockerfileName, dockerfileContents, 0777)
    92  
    93  	absContextDir, relDockerfile, err := GetContextFromLocalDir(contextDir, "")
    94  	assert.NilError(t, err)
    95  
    96  	assert.Check(t, is.Equal(contextDir, absContextDir))
    97  	assert.Check(t, is.Equal(DefaultDockerfileName, relDockerfile))
    98  }
    99  
   100  func TestGetContextFromLocalDirLocalFile(t *testing.T) {
   101  	contextDir, cleanup := createTestTempDir(t, "", "builder-context-test")
   102  	defer cleanup()
   103  
   104  	createTestTempFile(t, contextDir, DefaultDockerfileName, dockerfileContents, 0777)
   105  	testFilename := createTestTempFile(t, contextDir, "tmpTest", "test", 0777)
   106  
   107  	absContextDir, relDockerfile, err := GetContextFromLocalDir(testFilename, "")
   108  
   109  	if err == nil {
   110  		t.Fatalf("Error should not be nil")
   111  	}
   112  
   113  	if absContextDir != "" {
   114  		t.Fatalf("Absolute directory path should be empty, got: %s", absContextDir)
   115  	}
   116  
   117  	if relDockerfile != "" {
   118  		t.Fatalf("Relative path to Dockerfile should be empty, got: %s", relDockerfile)
   119  	}
   120  }
   121  
   122  func TestGetContextFromLocalDirWithCustomDockerfile(t *testing.T) {
   123  	contextDir, cleanup := createTestTempDir(t, "", "builder-context-test")
   124  	defer cleanup()
   125  
   126  	chdirCleanup := chdir(t, contextDir)
   127  	defer chdirCleanup()
   128  
   129  	createTestTempFile(t, contextDir, DefaultDockerfileName, dockerfileContents, 0777)
   130  
   131  	absContextDir, relDockerfile, err := GetContextFromLocalDir(contextDir, DefaultDockerfileName)
   132  	assert.NilError(t, err)
   133  
   134  	assert.Check(t, is.Equal(contextDir, absContextDir))
   135  	assert.Check(t, is.Equal(DefaultDockerfileName, relDockerfile))
   136  }
   137  
   138  func TestGetContextFromReaderString(t *testing.T) {
   139  	tarArchive, relDockerfile, err := GetContextFromReader(ioutil.NopCloser(strings.NewReader(dockerfileContents)), "")
   140  
   141  	if err != nil {
   142  		t.Fatalf("Error when executing GetContextFromReader: %s", err)
   143  	}
   144  
   145  	tarReader := tar.NewReader(tarArchive)
   146  
   147  	_, err = tarReader.Next()
   148  
   149  	if err != nil {
   150  		t.Fatalf("Error when reading tar archive: %s", err)
   151  	}
   152  
   153  	buff := new(bytes.Buffer)
   154  	buff.ReadFrom(tarReader)
   155  	contents := buff.String()
   156  
   157  	_, err = tarReader.Next()
   158  
   159  	if err != io.EOF {
   160  		t.Fatalf("Tar stream too long: %s", err)
   161  	}
   162  
   163  	assert.NilError(t, tarArchive.Close())
   164  
   165  	if dockerfileContents != contents {
   166  		t.Fatalf("Uncompressed tar archive does not equal: %s, got: %s", dockerfileContents, contents)
   167  	}
   168  
   169  	if relDockerfile != DefaultDockerfileName {
   170  		t.Fatalf("Relative path not equals %s, got: %s", DefaultDockerfileName, relDockerfile)
   171  	}
   172  }
   173  
   174  func TestGetContextFromReaderTar(t *testing.T) {
   175  	contextDir, cleanup := createTestTempDir(t, "", "builder-context-test")
   176  	defer cleanup()
   177  
   178  	createTestTempFile(t, contextDir, DefaultDockerfileName, dockerfileContents, 0777)
   179  
   180  	tarStream, err := archive.Tar(contextDir, archive.Uncompressed)
   181  	assert.NilError(t, err)
   182  
   183  	tarArchive, relDockerfile, err := GetContextFromReader(tarStream, DefaultDockerfileName)
   184  	assert.NilError(t, err)
   185  
   186  	tarReader := tar.NewReader(tarArchive)
   187  
   188  	header, err := tarReader.Next()
   189  	assert.NilError(t, err)
   190  
   191  	if header.Name != DefaultDockerfileName {
   192  		t.Fatalf("Dockerfile name should be: %s, got: %s", DefaultDockerfileName, header.Name)
   193  	}
   194  
   195  	buff := new(bytes.Buffer)
   196  	buff.ReadFrom(tarReader)
   197  	contents := buff.String()
   198  
   199  	_, err = tarReader.Next()
   200  
   201  	if err != io.EOF {
   202  		t.Fatalf("Tar stream too long: %s", err)
   203  	}
   204  
   205  	assert.NilError(t, tarArchive.Close())
   206  
   207  	if dockerfileContents != contents {
   208  		t.Fatalf("Uncompressed tar archive does not equal: %s, got: %s", dockerfileContents, contents)
   209  	}
   210  
   211  	if relDockerfile != DefaultDockerfileName {
   212  		t.Fatalf("Relative path not equals %s, got: %s", DefaultDockerfileName, relDockerfile)
   213  	}
   214  }
   215  
   216  func TestValidateContextDirectoryEmptyContext(t *testing.T) {
   217  	// This isn't a valid test on Windows. See https://play.golang.org/p/RR6z6jxR81.
   218  	// The test will ultimately end up calling filepath.Abs(""). On Windows,
   219  	// golang will error. On Linux, golang will return /. Due to there being
   220  	// drive letters on Windows, this is probably the correct behaviour for
   221  	// Windows.
   222  	if runtime.GOOS == "windows" {
   223  		t.Skip("Invalid test on Windows")
   224  	}
   225  	testValidateContextDirectory(t, prepareEmpty, []string{})
   226  }
   227  
   228  func TestValidateContextDirectoryContextWithNoFiles(t *testing.T) {
   229  	testValidateContextDirectory(t, prepareNoFiles, []string{})
   230  }
   231  
   232  func TestValidateContextDirectoryWithOneFile(t *testing.T) {
   233  	testValidateContextDirectory(t, prepareOneFile, []string{})
   234  }
   235  
   236  func TestValidateContextDirectoryWithOneFileExcludes(t *testing.T) {
   237  	testValidateContextDirectory(t, prepareOneFile, []string{DefaultDockerfileName})
   238  }
   239  
   240  // createTestTempDir creates a temporary directory for testing.
   241  // It returns the created path and a cleanup function which is meant to be used as deferred call.
   242  // When an error occurs, it terminates the test.
   243  func createTestTempDir(t *testing.T, dir, prefix string) (string, func()) {
   244  	path, err := ioutil.TempDir(dir, prefix)
   245  	assert.NilError(t, err)
   246  	return path, func() { assert.NilError(t, os.RemoveAll(path)) }
   247  }
   248  
   249  // createTestTempFile creates a temporary file within dir with specific contents and permissions.
   250  // When an error occurs, it terminates the test
   251  func createTestTempFile(t *testing.T, dir, filename, contents string, perm os.FileMode) string {
   252  	filePath := filepath.Join(dir, filename)
   253  	err := ioutil.WriteFile(filePath, []byte(contents), perm)
   254  	assert.NilError(t, err)
   255  	return filePath
   256  }
   257  
   258  // chdir changes current working directory to dir.
   259  // It returns a function which changes working directory back to the previous one.
   260  // This function is meant to be executed as a deferred call.
   261  // When an error occurs, it terminates the test.
   262  func chdir(t *testing.T, dir string) func() {
   263  	workingDirectory, err := os.Getwd()
   264  	assert.NilError(t, err)
   265  	assert.NilError(t, os.Chdir(dir))
   266  	return func() { assert.NilError(t, os.Chdir(workingDirectory)) }
   267  }
   268  
   269  func TestIsArchive(t *testing.T) {
   270  	var testcases = []struct {
   271  		doc      string
   272  		header   []byte
   273  		expected bool
   274  	}{
   275  		{
   276  			doc:      "nil is not a valid header",
   277  			header:   nil,
   278  			expected: false,
   279  		},
   280  		{
   281  			doc:      "invalid header bytes",
   282  			header:   []byte{0x00, 0x01, 0x02},
   283  			expected: false,
   284  		},
   285  		{
   286  			doc:      "header for bzip2 archive",
   287  			header:   []byte{0x42, 0x5A, 0x68},
   288  			expected: true,
   289  		},
   290  		{
   291  			doc:      "header for 7zip archive is not supported",
   292  			header:   []byte{0x50, 0x4b, 0x03, 0x04},
   293  			expected: false,
   294  		},
   295  	}
   296  	for _, testcase := range testcases {
   297  		assert.Check(t, is.Equal(testcase.expected, IsArchive(testcase.header)), testcase.doc)
   298  	}
   299  }