| package dockerfile // import "github.com/docker/docker/builder/dockerfile" | |
| import ( | |
| "context" | |
| "os" | |
| "path/filepath" | |
| "testing" | |
| "github.com/docker/docker/api/types" | |
| "github.com/docker/docker/pkg/idtools" | |
| "gotest.tools/v3/assert" | |
| is "gotest.tools/v3/assert/cmp" | |
| ) | |
| func TestChownFlagParsing(t *testing.T) { | |
| testFiles := map[string]string{ | |
| "passwd": `root:x:0:0::/bin:/bin/false | |
| bin:x:1:1::/bin:/bin/false | |
| wwwwww:x:21:33::/bin:/bin/false | |
| unicorn:x:1001:1002::/bin:/bin/false | |
| `, | |
| "group": `root:x:0: | |
| bin:x:1: | |
| wwwwww:x:33: | |
| unicorn:x:1002: | |
| somegrp:x:5555: | |
| othergrp:x:6666: | |
| `, | |
| } | |
| // test mappings for validating use of maps | |
| idMaps := []idtools.IDMap{ | |
| { | |
| ContainerID: 0, | |
| HostID: 100000, | |
| Size: 65536, | |
| }, | |
| } | |
| remapped := idtools.IdentityMapping{UIDMaps: idMaps, GIDMaps: idMaps} | |
| unmapped := idtools.IdentityMapping{} | |
| contextDir, cleanup := createTestTempDir(t, "", "builder-chown-parse-test") | |
| defer cleanup() | |
| if err := os.Mkdir(filepath.Join(contextDir, "etc"), 0o755); err != nil { | |
| t.Fatalf("error creating test directory: %v", err) | |
| } | |
| for filename, content := range testFiles { | |
| createTestTempFile(t, filepath.Join(contextDir, "etc"), filename, content, 0o644) | |
| } | |
| // positive tests | |
| for _, testcase := range []struct { | |
| builder *Builder | |
| name string | |
| chownStr string | |
| idMapping idtools.IdentityMapping | |
| state *dispatchState | |
| expected idtools.Identity | |
| }{ | |
| { | |
| builder: &Builder{options: &types.ImageBuildOptions{Platform: "linux"}}, | |
| name: "UIDNoMap", | |
| chownStr: "1", | |
| idMapping: unmapped, | |
| state: &dispatchState{}, | |
| expected: idtools.Identity{UID: 1, GID: 1}, | |
| }, | |
| { | |
| builder: &Builder{options: &types.ImageBuildOptions{Platform: "linux"}}, | |
| name: "UIDGIDNoMap", | |
| chownStr: "0:1", | |
| idMapping: unmapped, | |
| state: &dispatchState{}, | |
| expected: idtools.Identity{UID: 0, GID: 1}, | |
| }, | |
| { | |
| builder: &Builder{options: &types.ImageBuildOptions{Platform: "linux"}}, | |
| name: "UIDWithMap", | |
| chownStr: "0", | |
| idMapping: remapped, | |
| state: &dispatchState{}, | |
| expected: idtools.Identity{UID: 100000, GID: 100000}, | |
| }, | |
| { | |
| builder: &Builder{options: &types.ImageBuildOptions{Platform: "linux"}}, | |
| name: "UIDGIDWithMap", | |
| chownStr: "1:33", | |
| idMapping: remapped, | |
| state: &dispatchState{}, | |
| expected: idtools.Identity{UID: 100001, GID: 100033}, | |
| }, | |
| { | |
| builder: &Builder{options: &types.ImageBuildOptions{Platform: "linux"}}, | |
| name: "UserNoMap", | |
| chownStr: "bin:5555", | |
| idMapping: unmapped, | |
| state: &dispatchState{}, | |
| expected: idtools.Identity{UID: 1, GID: 5555}, | |
| }, | |
| { | |
| builder: &Builder{options: &types.ImageBuildOptions{Platform: "linux"}}, | |
| name: "GroupWithMap", | |
| chownStr: "0:unicorn", | |
| idMapping: remapped, | |
| state: &dispatchState{}, | |
| expected: idtools.Identity{UID: 100000, GID: 101002}, | |
| }, | |
| { | |
| builder: &Builder{options: &types.ImageBuildOptions{Platform: "linux"}}, | |
| name: "UserOnlyWithMap", | |
| chownStr: "unicorn", | |
| idMapping: remapped, | |
| state: &dispatchState{}, | |
| expected: idtools.Identity{UID: 101001, GID: 101002}, | |
| }, | |
| } { | |
| t.Run(testcase.name, func(t *testing.T) { | |
| idPair, err := parseChownFlag(context.TODO(), testcase.builder, testcase.state, testcase.chownStr, contextDir, testcase.idMapping) | |
| assert.NilError(t, err, "Failed to parse chown flag: %q", testcase.chownStr) | |
| assert.Check(t, is.DeepEqual(testcase.expected, idPair), "chown flag mapping failure") | |
| }) | |
| } | |
| // error tests | |
| for _, testcase := range []struct { | |
| builder *Builder | |
| name string | |
| chownStr string | |
| idMapping idtools.IdentityMapping | |
| state *dispatchState | |
| descr string | |
| }{ | |
| { | |
| builder: &Builder{options: &types.ImageBuildOptions{Platform: "linux"}}, | |
| name: "BadChownFlagFormat", | |
| chownStr: "bob:1:555", | |
| idMapping: unmapped, | |
| state: &dispatchState{}, | |
| descr: "invalid chown string format: bob:1:555", | |
| }, | |
| { | |
| builder: &Builder{options: &types.ImageBuildOptions{Platform: "linux"}}, | |
| name: "UserNoExist", | |
| chownStr: "bob", | |
| idMapping: unmapped, | |
| state: &dispatchState{}, | |
| descr: "can't find uid for user bob: no such user: bob", | |
| }, | |
| { | |
| builder: &Builder{options: &types.ImageBuildOptions{Platform: "linux"}}, | |
| name: "GroupNoExist", | |
| chownStr: "root:bob", | |
| idMapping: unmapped, | |
| state: &dispatchState{}, | |
| descr: "can't find gid for group bob: no such group: bob", | |
| }, | |
| } { | |
| t.Run(testcase.name, func(t *testing.T) { | |
| _, err := parseChownFlag(context.TODO(), testcase.builder, testcase.state, testcase.chownStr, contextDir, testcase.idMapping) | |
| assert.Check(t, is.Error(err, testcase.descr), "Expected error string doesn't match") | |
| }) | |
| } | |
| } |