github.com/shishir-a412ed/docker@v1.3.2-0.20180103180333-fda904911d87/builder/dockerfile/internals_test.go (about)

     1  package dockerfile
     2  
     3  import (
     4  	"fmt"
     5  	"os"
     6  	"path/filepath"
     7  	"runtime"
     8  	"testing"
     9  
    10  	"github.com/docker/docker/api/types"
    11  	"github.com/docker/docker/api/types/backend"
    12  	"github.com/docker/docker/api/types/container"
    13  	"github.com/docker/docker/builder"
    14  	"github.com/docker/docker/builder/remotecontext"
    15  	"github.com/docker/docker/pkg/archive"
    16  	"github.com/docker/docker/pkg/idtools"
    17  	"github.com/docker/go-connections/nat"
    18  	"github.com/stretchr/testify/assert"
    19  	"github.com/stretchr/testify/require"
    20  )
    21  
    22  func TestEmptyDockerfile(t *testing.T) {
    23  	contextDir, cleanup := createTestTempDir(t, "", "builder-dockerfile-test")
    24  	defer cleanup()
    25  
    26  	createTestTempFile(t, contextDir, builder.DefaultDockerfileName, "", 0777)
    27  
    28  	readAndCheckDockerfile(t, "emptyDockerfile", contextDir, "", "the Dockerfile (Dockerfile) cannot be empty")
    29  }
    30  
    31  func TestSymlinkDockerfile(t *testing.T) {
    32  	contextDir, cleanup := createTestTempDir(t, "", "builder-dockerfile-test")
    33  	defer cleanup()
    34  
    35  	createTestSymlink(t, contextDir, builder.DefaultDockerfileName, "/etc/passwd")
    36  
    37  	// The reason the error is "Cannot locate specified Dockerfile" is because
    38  	// in the builder, the symlink is resolved within the context, therefore
    39  	// Dockerfile -> /etc/passwd becomes etc/passwd from the context which is
    40  	// a nonexistent file.
    41  	expectedError := fmt.Sprintf("Cannot locate specified Dockerfile: %s", builder.DefaultDockerfileName)
    42  
    43  	readAndCheckDockerfile(t, "symlinkDockerfile", contextDir, builder.DefaultDockerfileName, expectedError)
    44  }
    45  
    46  func TestDockerfileOutsideTheBuildContext(t *testing.T) {
    47  	contextDir, cleanup := createTestTempDir(t, "", "builder-dockerfile-test")
    48  	defer cleanup()
    49  
    50  	expectedError := "Forbidden path outside the build context: ../../Dockerfile ()"
    51  
    52  	readAndCheckDockerfile(t, "DockerfileOutsideTheBuildContext", contextDir, "../../Dockerfile", expectedError)
    53  }
    54  
    55  func TestNonExistingDockerfile(t *testing.T) {
    56  	contextDir, cleanup := createTestTempDir(t, "", "builder-dockerfile-test")
    57  	defer cleanup()
    58  
    59  	expectedError := "Cannot locate specified Dockerfile: Dockerfile"
    60  
    61  	readAndCheckDockerfile(t, "NonExistingDockerfile", contextDir, "Dockerfile", expectedError)
    62  }
    63  
    64  func readAndCheckDockerfile(t *testing.T, testName, contextDir, dockerfilePath, expectedError string) {
    65  	tarStream, err := archive.Tar(contextDir, archive.Uncompressed)
    66  	require.NoError(t, err)
    67  
    68  	defer func() {
    69  		if err = tarStream.Close(); err != nil {
    70  			t.Fatalf("Error when closing tar stream: %s", err)
    71  		}
    72  	}()
    73  
    74  	if dockerfilePath == "" { // handled in BuildWithContext
    75  		dockerfilePath = builder.DefaultDockerfileName
    76  	}
    77  
    78  	config := backend.BuildConfig{
    79  		Options: &types.ImageBuildOptions{Dockerfile: dockerfilePath},
    80  		Source:  tarStream,
    81  	}
    82  	_, _, err = remotecontext.Detect(config)
    83  	assert.EqualError(t, err, expectedError)
    84  }
    85  
    86  func TestCopyRunConfig(t *testing.T) {
    87  	defaultEnv := []string{"foo=1"}
    88  	defaultCmd := []string{"old"}
    89  
    90  	var testcases = []struct {
    91  		doc       string
    92  		modifiers []runConfigModifier
    93  		expected  *container.Config
    94  	}{
    95  		{
    96  			doc:       "Set the command",
    97  			modifiers: []runConfigModifier{withCmd([]string{"new"})},
    98  			expected: &container.Config{
    99  				Cmd: []string{"new"},
   100  				Env: defaultEnv,
   101  			},
   102  		},
   103  		{
   104  			doc:       "Set the command to a comment",
   105  			modifiers: []runConfigModifier{withCmdComment("comment", runtime.GOOS)},
   106  			expected: &container.Config{
   107  				Cmd: append(defaultShellForOS(runtime.GOOS), "#(nop) ", "comment"),
   108  				Env: defaultEnv,
   109  			},
   110  		},
   111  		{
   112  			doc: "Set the command and env",
   113  			modifiers: []runConfigModifier{
   114  				withCmd([]string{"new"}),
   115  				withEnv([]string{"one", "two"}),
   116  			},
   117  			expected: &container.Config{
   118  				Cmd: []string{"new"},
   119  				Env: []string{"one", "two"},
   120  			},
   121  		},
   122  	}
   123  
   124  	for _, testcase := range testcases {
   125  		runConfig := &container.Config{
   126  			Cmd: defaultCmd,
   127  			Env: defaultEnv,
   128  		}
   129  		runConfigCopy := copyRunConfig(runConfig, testcase.modifiers...)
   130  		assert.Equal(t, testcase.expected, runConfigCopy, testcase.doc)
   131  		// Assert the original was not modified
   132  		assert.NotEqual(t, runConfig, runConfigCopy, testcase.doc)
   133  	}
   134  
   135  }
   136  
   137  func fullMutableRunConfig() *container.Config {
   138  	return &container.Config{
   139  		Cmd: []string{"command", "arg1"},
   140  		Env: []string{"env1=foo", "env2=bar"},
   141  		ExposedPorts: nat.PortSet{
   142  			"1000/tcp": {},
   143  			"1001/tcp": {},
   144  		},
   145  		Volumes: map[string]struct{}{
   146  			"one": {},
   147  			"two": {},
   148  		},
   149  		Entrypoint: []string{"entry", "arg1"},
   150  		OnBuild:    []string{"first", "next"},
   151  		Labels: map[string]string{
   152  			"label1": "value1",
   153  			"label2": "value2",
   154  		},
   155  		Shell: []string{"shell", "-c"},
   156  	}
   157  }
   158  
   159  func TestDeepCopyRunConfig(t *testing.T) {
   160  	runConfig := fullMutableRunConfig()
   161  	copy := copyRunConfig(runConfig)
   162  	assert.Equal(t, fullMutableRunConfig(), copy)
   163  
   164  	copy.Cmd[1] = "arg2"
   165  	copy.Env[1] = "env2=new"
   166  	copy.ExposedPorts["10002"] = struct{}{}
   167  	copy.Volumes["three"] = struct{}{}
   168  	copy.Entrypoint[1] = "arg2"
   169  	copy.OnBuild[0] = "start"
   170  	copy.Labels["label3"] = "value3"
   171  	copy.Shell[0] = "sh"
   172  	assert.Equal(t, fullMutableRunConfig(), runConfig)
   173  }
   174  
   175  func TestChownFlagParsing(t *testing.T) {
   176  	testFiles := map[string]string{
   177  		"passwd": `root:x:0:0::/bin:/bin/false
   178  bin:x:1:1::/bin:/bin/false
   179  wwwwww:x:21:33::/bin:/bin/false
   180  unicorn:x:1001:1002::/bin:/bin/false
   181  		`,
   182  		"group": `root:x:0:
   183  bin:x:1:
   184  wwwwww:x:33:
   185  unicorn:x:1002:
   186  somegrp:x:5555:
   187  othergrp:x:6666:
   188  		`,
   189  	}
   190  	// test mappings for validating use of maps
   191  	idMaps := []idtools.IDMap{
   192  		{
   193  			ContainerID: 0,
   194  			HostID:      100000,
   195  			Size:        65536,
   196  		},
   197  	}
   198  	remapped := idtools.NewIDMappingsFromMaps(idMaps, idMaps)
   199  	unmapped := &idtools.IDMappings{}
   200  
   201  	contextDir, cleanup := createTestTempDir(t, "", "builder-chown-parse-test")
   202  	defer cleanup()
   203  
   204  	if err := os.Mkdir(filepath.Join(contextDir, "etc"), 0755); err != nil {
   205  		t.Fatalf("error creating test directory: %v", err)
   206  	}
   207  
   208  	for filename, content := range testFiles {
   209  		createTestTempFile(t, filepath.Join(contextDir, "etc"), filename, content, 0644)
   210  	}
   211  
   212  	// positive tests
   213  	for _, testcase := range []struct {
   214  		name      string
   215  		chownStr  string
   216  		idMapping *idtools.IDMappings
   217  		expected  idtools.IDPair
   218  	}{
   219  		{
   220  			name:      "UIDNoMap",
   221  			chownStr:  "1",
   222  			idMapping: unmapped,
   223  			expected:  idtools.IDPair{UID: 1, GID: 1},
   224  		},
   225  		{
   226  			name:      "UIDGIDNoMap",
   227  			chownStr:  "0:1",
   228  			idMapping: unmapped,
   229  			expected:  idtools.IDPair{UID: 0, GID: 1},
   230  		},
   231  		{
   232  			name:      "UIDWithMap",
   233  			chownStr:  "0",
   234  			idMapping: remapped,
   235  			expected:  idtools.IDPair{UID: 100000, GID: 100000},
   236  		},
   237  		{
   238  			name:      "UIDGIDWithMap",
   239  			chownStr:  "1:33",
   240  			idMapping: remapped,
   241  			expected:  idtools.IDPair{UID: 100001, GID: 100033},
   242  		},
   243  		{
   244  			name:      "UserNoMap",
   245  			chownStr:  "bin:5555",
   246  			idMapping: unmapped,
   247  			expected:  idtools.IDPair{UID: 1, GID: 5555},
   248  		},
   249  		{
   250  			name:      "GroupWithMap",
   251  			chownStr:  "0:unicorn",
   252  			idMapping: remapped,
   253  			expected:  idtools.IDPair{UID: 100000, GID: 101002},
   254  		},
   255  		{
   256  			name:      "UserOnlyWithMap",
   257  			chownStr:  "unicorn",
   258  			idMapping: remapped,
   259  			expected:  idtools.IDPair{UID: 101001, GID: 101002},
   260  		},
   261  	} {
   262  		t.Run(testcase.name, func(t *testing.T) {
   263  			idPair, err := parseChownFlag(testcase.chownStr, contextDir, testcase.idMapping)
   264  			require.NoError(t, err, "Failed to parse chown flag: %q", testcase.chownStr)
   265  			assert.Equal(t, testcase.expected, idPair, "chown flag mapping failure")
   266  		})
   267  	}
   268  
   269  	// error tests
   270  	for _, testcase := range []struct {
   271  		name      string
   272  		chownStr  string
   273  		idMapping *idtools.IDMappings
   274  		descr     string
   275  	}{
   276  		{
   277  			name:      "BadChownFlagFormat",
   278  			chownStr:  "bob:1:555",
   279  			idMapping: unmapped,
   280  			descr:     "invalid chown string format: bob:1:555",
   281  		},
   282  		{
   283  			name:      "UserNoExist",
   284  			chownStr:  "bob",
   285  			idMapping: unmapped,
   286  			descr:     "can't find uid for user bob: no such user: bob",
   287  		},
   288  		{
   289  			name:      "GroupNoExist",
   290  			chownStr:  "root:bob",
   291  			idMapping: unmapped,
   292  			descr:     "can't find gid for group bob: no such group: bob",
   293  		},
   294  	} {
   295  		t.Run(testcase.name, func(t *testing.T) {
   296  			_, err := parseChownFlag(testcase.chownStr, contextDir, testcase.idMapping)
   297  			assert.EqualError(t, err, testcase.descr, "Expected error string doesn't match")
   298  		})
   299  	}
   300  }