Add mock builder backend
Add tests for ARG in FROM
Signed-off-by: Daniel Nephin <dnephin@docker.com>
| ... | ... |
@@ -22,7 +22,6 @@ import ( |
| 22 | 22 |
"github.com/docker/docker/api/types/container" |
| 23 | 23 |
"github.com/docker/docker/api/types/strslice" |
| 24 | 24 |
"github.com/docker/docker/builder" |
| 25 |
- "github.com/docker/docker/pkg/shellvar" |
|
| 26 | 25 |
"github.com/docker/docker/pkg/signal" |
| 27 | 26 |
"github.com/docker/go-connections/nat" |
| 28 | 27 |
"github.com/pkg/errors" |
| ... | ... |
@@ -219,14 +218,8 @@ func from(b *Builder, args []string, attributes map[string]bool, original string |
| 219 | 219 |
return err |
| 220 | 220 |
} |
| 221 | 221 |
|
| 222 |
- getBuildArg := func(key string) (string, bool) {
|
|
| 223 |
- value, ok := b.options.BuildArgs[key] |
|
| 224 |
- if value != nil {
|
|
| 225 |
- return *value, ok |
|
| 226 |
- } |
|
| 227 |
- return "", ok |
|
| 228 |
- } |
|
| 229 |
- name, err := shellvar.Substitute(args[0], getBuildArg) |
|
| 222 |
+ substituionArgs := b.buildArgsWithoutConfigEnv() |
|
| 223 |
+ name, err := ProcessWord(args[0], substituionArgs, b.directive.EscapeToken) |
|
| 230 | 224 |
if err != nil {
|
| 231 | 225 |
return err |
| 232 | 226 |
} |
| ... | ... |
@@ -9,6 +9,8 @@ import ( |
| 9 | 9 |
"github.com/docker/docker/api/types" |
| 10 | 10 |
"github.com/docker/docker/api/types/container" |
| 11 | 11 |
"github.com/docker/docker/api/types/strslice" |
| 12 |
+ "github.com/docker/docker/builder" |
|
| 13 |
+ "github.com/docker/docker/pkg/testutil/assert" |
|
| 12 | 14 |
"github.com/docker/go-connections/nat" |
| 13 | 15 |
) |
| 14 | 16 |
|
| ... | ... |
@@ -189,35 +191,68 @@ func TestLabel(t *testing.T) {
|
| 189 | 189 |
} |
| 190 | 190 |
} |
| 191 | 191 |
|
| 192 |
-func TestFrom(t *testing.T) {
|
|
| 193 |
- b := &Builder{flags: &BFlags{}, runConfig: &container.Config{}, disableCommit: true}
|
|
| 192 |
+func newBuilderWithMockBackend() *Builder {
|
|
| 193 |
+ b := &Builder{
|
|
| 194 |
+ flags: &BFlags{},
|
|
| 195 |
+ runConfig: &container.Config{},
|
|
| 196 |
+ options: &types.ImageBuildOptions{},
|
|
| 197 |
+ docker: &MockBackend{},
|
|
| 198 |
+ allowedBuildArgs: make(map[string]*string), |
|
| 199 |
+ allBuildArgs: make(map[string]struct{}),
|
|
| 200 |
+ } |
|
| 194 | 201 |
b.imageContexts = &imageContexts{b: b}
|
| 202 |
+ return b |
|
| 203 |
+} |
|
| 204 |
+ |
|
| 205 |
+func TestFromScratch(t *testing.T) {
|
|
| 206 |
+ b := newBuilderWithMockBackend() |
|
| 195 | 207 |
|
| 196 | 208 |
err := from(b, []string{"scratch"}, nil, "")
|
| 197 | 209 |
|
| 198 | 210 |
if runtime.GOOS == "windows" {
|
| 199 |
- if err == nil {
|
|
| 200 |
- t.Fatal("Error not set on Windows")
|
|
| 201 |
- } |
|
| 211 |
+ assert.Error(t, err, "Windows does not support FROM scratch") |
|
| 212 |
+ return |
|
| 213 |
+ } |
|
| 202 | 214 |
|
| 203 |
- expectedError := "Windows does not support FROM scratch" |
|
| 215 |
+ assert.NilError(t, err) |
|
| 216 |
+ assert.Equal(t, b.image, "") |
|
| 217 |
+ assert.Equal(t, b.noBaseImage, true) |
|
| 218 |
+} |
|
| 204 | 219 |
|
| 205 |
- if !strings.Contains(err.Error(), expectedError) {
|
|
| 206 |
- t.Fatalf("Error message not correct on Windows. Should be: %s, got: %s", expectedError, err.Error())
|
|
| 207 |
- } |
|
| 208 |
- } else {
|
|
| 209 |
- if err != nil {
|
|
| 210 |
- t.Fatalf("Error when executing from: %s", err.Error())
|
|
| 211 |
- } |
|
| 220 |
+func TestFromWithArg(t *testing.T) {
|
|
| 221 |
+ tag, expected := ":sometag", "expectedthisid" |
|
| 212 | 222 |
|
| 213 |
- if b.image != "" {
|
|
| 214 |
- t.Fatalf("Image should be empty, got: %s", b.image)
|
|
| 215 |
- } |
|
| 223 |
+ getImage := func(name string) (builder.Image, error) {
|
|
| 224 |
+ assert.Equal(t, name, "alpine"+tag) |
|
| 225 |
+ return &mockImage{id: "expectedthisid"}, nil
|
|
| 226 |
+ } |
|
| 227 |
+ b := newBuilderWithMockBackend() |
|
| 228 |
+ b.docker.(*MockBackend).getImageOnBuildFunc = getImage |
|
| 216 | 229 |
|
| 217 |
- if b.noBaseImage != true {
|
|
| 218 |
- t.Fatalf("Image should not have any base image, got: %v", b.noBaseImage)
|
|
| 219 |
- } |
|
| 230 |
+ assert.NilError(t, arg(b, []string{"THETAG=" + tag}, nil, ""))
|
|
| 231 |
+ err := from(b, []string{"alpine${THETAG}"}, nil, "")
|
|
| 232 |
+ |
|
| 233 |
+ assert.NilError(t, err) |
|
| 234 |
+ assert.Equal(t, b.image, expected) |
|
| 235 |
+ assert.Equal(t, b.from.ImageID(), expected) |
|
| 236 |
+ assert.NotNil(t, b.allowedBuildArgs) |
|
| 237 |
+ assert.Equal(t, len(b.allowedBuildArgs), 0) |
|
| 238 |
+} |
|
| 239 |
+ |
|
| 240 |
+func TestFromWithUndefinedArg(t *testing.T) {
|
|
| 241 |
+ tag, expected := "sometag", "expectedthisid" |
|
| 242 |
+ |
|
| 243 |
+ getImage := func(name string) (builder.Image, error) {
|
|
| 244 |
+ assert.Equal(t, name, "alpine") |
|
| 245 |
+ return &mockImage{id: "expectedthisid"}, nil
|
|
| 220 | 246 |
} |
| 247 |
+ b := newBuilderWithMockBackend() |
|
| 248 |
+ b.docker.(*MockBackend).getImageOnBuildFunc = getImage |
|
| 249 |
+ b.options.BuildArgs = map[string]*string{"THETAG": &tag}
|
|
| 250 |
+ |
|
| 251 |
+ err := from(b, []string{"alpine${THETAG}"}, nil, "")
|
|
| 252 |
+ assert.NilError(t, err) |
|
| 253 |
+ assert.Equal(t, b.image, expected) |
|
| 221 | 254 |
} |
| 222 | 255 |
|
| 223 | 256 |
func TestOnbuildIllegalTriggers(t *testing.T) {
|
| ... | ... |
@@ -175,7 +175,10 @@ func (b *Builder) evaluateEnv(cmd string, str string, envs []string) ([]string, |
| 175 | 175 |
if allowWordExpansion[cmd] {
|
| 176 | 176 |
processFunc = ProcessWords |
| 177 | 177 |
} else {
|
| 178 |
- processFunc = ProcessWord |
|
| 178 |
+ processFunc = func(word string, envs []string, escape rune) ([]string, error) {
|
|
| 179 |
+ word, err := ProcessWord(word, envs, escape) |
|
| 180 |
+ return []string{word}, err
|
|
| 181 |
+ } |
|
| 179 | 182 |
} |
| 180 | 183 |
return processFunc(str, envs, b.directive.EscapeToken) |
| 181 | 184 |
} |
| 182 | 185 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,99 @@ |
| 0 |
+package dockerfile |
|
| 1 |
+ |
|
| 2 |
+import ( |
|
| 3 |
+ "io" |
|
| 4 |
+ "time" |
|
| 5 |
+ |
|
| 6 |
+ "github.com/docker/distribution/reference" |
|
| 7 |
+ "github.com/docker/docker/api/types" |
|
| 8 |
+ "github.com/docker/docker/api/types/backend" |
|
| 9 |
+ "github.com/docker/docker/api/types/container" |
|
| 10 |
+ "github.com/docker/docker/builder" |
|
| 11 |
+ "github.com/docker/docker/image" |
|
| 12 |
+ "golang.org/x/net/context" |
|
| 13 |
+) |
|
| 14 |
+ |
|
| 15 |
+// MockBackend implements the builder.Backend interface for unit testing |
|
| 16 |
+type MockBackend struct {
|
|
| 17 |
+ getImageOnBuildFunc func(string) (builder.Image, error) |
|
| 18 |
+} |
|
| 19 |
+ |
|
| 20 |
+func (m *MockBackend) GetImageOnBuild(name string) (builder.Image, error) {
|
|
| 21 |
+ if m.getImageOnBuildFunc != nil {
|
|
| 22 |
+ return m.getImageOnBuildFunc(name) |
|
| 23 |
+ } |
|
| 24 |
+ return &mockImage{id: "theid"}, nil
|
|
| 25 |
+} |
|
| 26 |
+ |
|
| 27 |
+func (m *MockBackend) TagImageWithReference(image.ID, reference.Named) error {
|
|
| 28 |
+ return nil |
|
| 29 |
+} |
|
| 30 |
+ |
|
| 31 |
+func (m *MockBackend) PullOnBuild(ctx context.Context, name string, authConfigs map[string]types.AuthConfig, output io.Writer) (builder.Image, error) {
|
|
| 32 |
+ return nil, nil |
|
| 33 |
+} |
|
| 34 |
+ |
|
| 35 |
+func (m *MockBackend) ContainerAttachRaw(cID string, stdin io.ReadCloser, stdout, stderr io.Writer, stream bool) error {
|
|
| 36 |
+ return nil |
|
| 37 |
+} |
|
| 38 |
+ |
|
| 39 |
+func (m *MockBackend) ContainerCreate(config types.ContainerCreateConfig) (container.ContainerCreateCreatedBody, error) {
|
|
| 40 |
+ return container.ContainerCreateCreatedBody{}, nil
|
|
| 41 |
+} |
|
| 42 |
+ |
|
| 43 |
+func (m *MockBackend) ContainerRm(name string, config *types.ContainerRmConfig) error {
|
|
| 44 |
+ return nil |
|
| 45 |
+} |
|
| 46 |
+ |
|
| 47 |
+func (m *MockBackend) Commit(string, *backend.ContainerCommitConfig) (string, error) {
|
|
| 48 |
+ return "", nil |
|
| 49 |
+} |
|
| 50 |
+ |
|
| 51 |
+func (m *MockBackend) ContainerKill(containerID string, sig uint64) error {
|
|
| 52 |
+ return nil |
|
| 53 |
+} |
|
| 54 |
+ |
|
| 55 |
+func (m *MockBackend) ContainerStart(containerID string, hostConfig *container.HostConfig, checkpoint string, checkpointDir string) error {
|
|
| 56 |
+ return nil |
|
| 57 |
+} |
|
| 58 |
+ |
|
| 59 |
+func (m *MockBackend) ContainerWait(containerID string, timeout time.Duration) (int, error) {
|
|
| 60 |
+ return 0, nil |
|
| 61 |
+} |
|
| 62 |
+ |
|
| 63 |
+func (m *MockBackend) ContainerUpdateCmdOnBuild(containerID string, cmd []string) error {
|
|
| 64 |
+ return nil |
|
| 65 |
+} |
|
| 66 |
+ |
|
| 67 |
+func (m *MockBackend) ContainerCreateWorkdir(containerID string) error {
|
|
| 68 |
+ return nil |
|
| 69 |
+} |
|
| 70 |
+ |
|
| 71 |
+func (m *MockBackend) CopyOnBuild(containerID string, destPath string, src builder.FileInfo, decompress bool) error {
|
|
| 72 |
+ return nil |
|
| 73 |
+} |
|
| 74 |
+ |
|
| 75 |
+func (m *MockBackend) HasExperimental() bool {
|
|
| 76 |
+ return false |
|
| 77 |
+} |
|
| 78 |
+ |
|
| 79 |
+func (m *MockBackend) SquashImage(from string, to string) (string, error) {
|
|
| 80 |
+ return "", nil |
|
| 81 |
+} |
|
| 82 |
+ |
|
| 83 |
+func (m *MockBackend) MountImage(name string) (string, func() error, error) {
|
|
| 84 |
+ return "", func() error { return nil }, nil
|
|
| 85 |
+} |
|
| 86 |
+ |
|
| 87 |
+type mockImage struct {
|
|
| 88 |
+ id string |
|
| 89 |
+ config *container.Config |
|
| 90 |
+} |
|
| 91 |
+ |
|
| 92 |
+func (i *mockImage) ImageID() string {
|
|
| 93 |
+ return i.id |
|
| 94 |
+} |
|
| 95 |
+ |
|
| 96 |
+func (i *mockImage) RunConfig() *container.Config {
|
|
| 97 |
+ return i.config |
|
| 98 |
+} |
| ... | ... |
@@ -24,9 +24,9 @@ type shellWord struct {
|
| 24 | 24 |
|
| 25 | 25 |
// ProcessWord will use the 'env' list of environment variables, |
| 26 | 26 |
// and replace any env var references in 'word'. |
| 27 |
-func ProcessWord(word string, env []string, escapeToken rune) ([]string, error) {
|
|
| 27 |
+func ProcessWord(word string, env []string, escapeToken rune) (string, error) {
|
|
| 28 | 28 |
word, _, err := process(word, env, escapeToken) |
| 29 |
- return []string{word}, err
|
|
| 29 |
+ return word, err |
|
| 30 | 30 |
} |
| 31 | 31 |
|
| 32 | 32 |
// ProcessWords will use the 'env' list of environment variables, |