github.com/olljanat/moby@v1.13.1/builder/dockerfile/dispatchers_test.go (about)

     1  package dockerfile
     2  
     3  import (
     4  	"fmt"
     5  	"runtime"
     6  	"strings"
     7  	"testing"
     8  
     9  	"github.com/docker/docker/api/types"
    10  	"github.com/docker/docker/api/types/container"
    11  	"github.com/docker/docker/api/types/strslice"
    12  	"github.com/docker/go-connections/nat"
    13  )
    14  
    15  type commandWithFunction struct {
    16  	name     string
    17  	function func(args []string) error
    18  }
    19  
    20  func TestCommandsExactlyOneArgument(t *testing.T) {
    21  	commands := []commandWithFunction{
    22  		{"MAINTAINER", func(args []string) error { return maintainer(nil, args, nil, "") }},
    23  		{"FROM", func(args []string) error { return from(nil, args, nil, "") }},
    24  		{"WORKDIR", func(args []string) error { return workdir(nil, args, nil, "") }},
    25  		{"USER", func(args []string) error { return user(nil, args, nil, "") }},
    26  		{"STOPSIGNAL", func(args []string) error { return stopSignal(nil, args, nil, "") }}}
    27  
    28  	for _, command := range commands {
    29  		err := command.function([]string{})
    30  
    31  		if err == nil {
    32  			t.Fatalf("Error should be present for %s command", command.name)
    33  		}
    34  
    35  		expectedError := errExactlyOneArgument(command.name)
    36  
    37  		if err.Error() != expectedError.Error() {
    38  			t.Fatalf("Wrong error message for %s. Got: %s. Should be: %s", command.name, err.Error(), expectedError)
    39  		}
    40  	}
    41  }
    42  
    43  func TestCommandsAtLeastOneArgument(t *testing.T) {
    44  	commands := []commandWithFunction{
    45  		{"ENV", func(args []string) error { return env(nil, args, nil, "") }},
    46  		{"LABEL", func(args []string) error { return label(nil, args, nil, "") }},
    47  		{"ONBUILD", func(args []string) error { return onbuild(nil, args, nil, "") }},
    48  		{"HEALTHCHECK", func(args []string) error { return healthcheck(nil, args, nil, "") }},
    49  		{"EXPOSE", func(args []string) error { return expose(nil, args, nil, "") }},
    50  		{"VOLUME", func(args []string) error { return volume(nil, args, nil, "") }}}
    51  
    52  	for _, command := range commands {
    53  		err := command.function([]string{})
    54  
    55  		if err == nil {
    56  			t.Fatalf("Error should be present for %s command", command.name)
    57  		}
    58  
    59  		expectedError := errAtLeastOneArgument(command.name)
    60  
    61  		if err.Error() != expectedError.Error() {
    62  			t.Fatalf("Wrong error message for %s. Got: %s. Should be: %s", command.name, err.Error(), expectedError)
    63  		}
    64  	}
    65  }
    66  
    67  func TestCommandsAtLeastTwoArguments(t *testing.T) {
    68  	commands := []commandWithFunction{
    69  		{"ADD", func(args []string) error { return add(nil, args, nil, "") }},
    70  		{"COPY", func(args []string) error { return dispatchCopy(nil, args, nil, "") }}}
    71  
    72  	for _, command := range commands {
    73  		err := command.function([]string{"arg1"})
    74  
    75  		if err == nil {
    76  			t.Fatalf("Error should be present for %s command", command.name)
    77  		}
    78  
    79  		expectedError := errAtLeastTwoArguments(command.name)
    80  
    81  		if err.Error() != expectedError.Error() {
    82  			t.Fatalf("Wrong error message for %s. Got: %s. Should be: %s", command.name, err.Error(), expectedError)
    83  		}
    84  	}
    85  }
    86  
    87  func TestCommandsTooManyArguments(t *testing.T) {
    88  	commands := []commandWithFunction{
    89  		{"ENV", func(args []string) error { return env(nil, args, nil, "") }},
    90  		{"LABEL", func(args []string) error { return label(nil, args, nil, "") }}}
    91  
    92  	for _, command := range commands {
    93  		err := command.function([]string{"arg1", "arg2", "arg3"})
    94  
    95  		if err == nil {
    96  			t.Fatalf("Error should be present for %s command", command.name)
    97  		}
    98  
    99  		expectedError := errTooManyArguments(command.name)
   100  
   101  		if err.Error() != expectedError.Error() {
   102  			t.Fatalf("Wrong error message for %s. Got: %s. Should be: %s", command.name, err.Error(), expectedError)
   103  		}
   104  	}
   105  }
   106  
   107  func TestCommandseBlankNames(t *testing.T) {
   108  	bflags := &BFlags{}
   109  	config := &container.Config{}
   110  
   111  	b := &Builder{flags: bflags, runConfig: config, disableCommit: true}
   112  
   113  	commands := []commandWithFunction{
   114  		{"ENV", func(args []string) error { return env(b, args, nil, "") }},
   115  		{"LABEL", func(args []string) error { return label(b, args, nil, "") }},
   116  	}
   117  
   118  	for _, command := range commands {
   119  		err := command.function([]string{"", ""})
   120  
   121  		if err == nil {
   122  			t.Fatalf("Error should be present for %s command", command.name)
   123  		}
   124  
   125  		expectedError := errBlankCommandNames(command.name)
   126  
   127  		if err.Error() != expectedError.Error() {
   128  			t.Fatalf("Wrong error message for %s. Got: %s. Should be: %s", command.name, err.Error(), expectedError)
   129  		}
   130  	}
   131  }
   132  
   133  func TestEnv2Variables(t *testing.T) {
   134  	variables := []string{"var1", "val1", "var2", "val2"}
   135  
   136  	bflags := &BFlags{}
   137  	config := &container.Config{}
   138  
   139  	b := &Builder{flags: bflags, runConfig: config, disableCommit: true}
   140  
   141  	if err := env(b, variables, nil, ""); err != nil {
   142  		t.Fatalf("Error when executing env: %s", err.Error())
   143  	}
   144  
   145  	expectedVar1 := fmt.Sprintf("%s=%s", variables[0], variables[1])
   146  	expectedVar2 := fmt.Sprintf("%s=%s", variables[2], variables[3])
   147  
   148  	if b.runConfig.Env[0] != expectedVar1 {
   149  		t.Fatalf("Wrong env output for first variable. Got: %s. Should be: %s", b.runConfig.Env[0], expectedVar1)
   150  	}
   151  
   152  	if b.runConfig.Env[1] != expectedVar2 {
   153  		t.Fatalf("Wrong env output for second variable. Got: %s, Should be: %s", b.runConfig.Env[1], expectedVar2)
   154  	}
   155  }
   156  
   157  func TestMaintainer(t *testing.T) {
   158  	maintainerEntry := "Some Maintainer <maintainer@example.com>"
   159  
   160  	b := &Builder{flags: &BFlags{}, runConfig: &container.Config{}, disableCommit: true}
   161  
   162  	if err := maintainer(b, []string{maintainerEntry}, nil, ""); err != nil {
   163  		t.Fatalf("Error when executing maintainer: %s", err.Error())
   164  	}
   165  
   166  	if b.maintainer != maintainerEntry {
   167  		t.Fatalf("Maintainer in builder should be set to %s. Got: %s", maintainerEntry, b.maintainer)
   168  	}
   169  }
   170  
   171  func TestLabel(t *testing.T) {
   172  	labelName := "label"
   173  	labelValue := "value"
   174  
   175  	labelEntry := []string{labelName, labelValue}
   176  
   177  	b := &Builder{flags: &BFlags{}, runConfig: &container.Config{}, disableCommit: true}
   178  
   179  	if err := label(b, labelEntry, nil, ""); err != nil {
   180  		t.Fatalf("Error when executing label: %s", err.Error())
   181  	}
   182  
   183  	if val, ok := b.runConfig.Labels[labelName]; ok {
   184  		if val != labelValue {
   185  			t.Fatalf("Label %s should have value %s, had %s instead", labelName, labelValue, val)
   186  		}
   187  	} else {
   188  		t.Fatalf("Label %s should be present but it is not", labelName)
   189  	}
   190  }
   191  
   192  func TestFrom(t *testing.T) {
   193  	b := &Builder{flags: &BFlags{}, runConfig: &container.Config{}, disableCommit: true}
   194  
   195  	err := from(b, []string{"scratch"}, nil, "")
   196  
   197  	if runtime.GOOS == "windows" {
   198  		if err == nil {
   199  			t.Fatalf("Error not set on Windows")
   200  		}
   201  
   202  		expectedError := "Windows does not support FROM scratch"
   203  
   204  		if !strings.Contains(err.Error(), expectedError) {
   205  			t.Fatalf("Error message not correct on Windows. Should be: %s, got: %s", expectedError, err.Error())
   206  		}
   207  	} else {
   208  		if err != nil {
   209  			t.Fatalf("Error when executing from: %s", err.Error())
   210  		}
   211  
   212  		if b.image != "" {
   213  			t.Fatalf("Image shoule be empty, got: %s", b.image)
   214  		}
   215  
   216  		if b.noBaseImage != true {
   217  			t.Fatalf("Image should not have any base image, got: %v", b.noBaseImage)
   218  		}
   219  	}
   220  }
   221  
   222  func TestOnbuildIllegalTriggers(t *testing.T) {
   223  	triggers := []struct{ command, expectedError string }{
   224  		{"ONBUILD", "Chaining ONBUILD via `ONBUILD ONBUILD` isn't allowed"},
   225  		{"MAINTAINER", "MAINTAINER isn't allowed as an ONBUILD trigger"},
   226  		{"FROM", "FROM isn't allowed as an ONBUILD trigger"}}
   227  
   228  	for _, trigger := range triggers {
   229  		b := &Builder{flags: &BFlags{}, runConfig: &container.Config{}, disableCommit: true}
   230  
   231  		err := onbuild(b, []string{trigger.command}, nil, "")
   232  
   233  		if err == nil {
   234  			t.Fatalf("Error should not be nil")
   235  		}
   236  
   237  		if !strings.Contains(err.Error(), trigger.expectedError) {
   238  			t.Fatalf("Error message not correct. Should be: %s, got: %s", trigger.expectedError, err.Error())
   239  		}
   240  	}
   241  }
   242  
   243  func TestOnbuild(t *testing.T) {
   244  	b := &Builder{flags: &BFlags{}, runConfig: &container.Config{}, disableCommit: true}
   245  
   246  	err := onbuild(b, []string{"ADD", ".", "/app/src"}, nil, "ONBUILD ADD . /app/src")
   247  
   248  	if err != nil {
   249  		t.Fatalf("Error should be empty, got: %s", err.Error())
   250  	}
   251  
   252  	expectedOnbuild := "ADD . /app/src"
   253  
   254  	if b.runConfig.OnBuild[0] != expectedOnbuild {
   255  		t.Fatalf("Wrong ONBUILD command. Expected: %s, got: %s", expectedOnbuild, b.runConfig.OnBuild[0])
   256  	}
   257  }
   258  
   259  func TestWorkdir(t *testing.T) {
   260  	b := &Builder{flags: &BFlags{}, runConfig: &container.Config{}, disableCommit: true}
   261  
   262  	workingDir := "/app"
   263  
   264  	if runtime.GOOS == "windows" {
   265  		workingDir = "C:\app"
   266  	}
   267  
   268  	err := workdir(b, []string{workingDir}, nil, "")
   269  
   270  	if err != nil {
   271  		t.Fatalf("Error should be empty, got: %s", err.Error())
   272  	}
   273  
   274  	if b.runConfig.WorkingDir != workingDir {
   275  		t.Fatalf("WorkingDir should be set to %s, got %s", workingDir, b.runConfig.WorkingDir)
   276  	}
   277  
   278  }
   279  
   280  func TestCmd(t *testing.T) {
   281  	b := &Builder{flags: &BFlags{}, runConfig: &container.Config{}, disableCommit: true}
   282  
   283  	command := "./executable"
   284  
   285  	err := cmd(b, []string{command}, nil, "")
   286  
   287  	if err != nil {
   288  		t.Fatalf("Error should be empty, got: %s", err.Error())
   289  	}
   290  
   291  	var expectedCommand strslice.StrSlice
   292  
   293  	if runtime.GOOS == "windows" {
   294  		expectedCommand = strslice.StrSlice(append([]string{"cmd"}, "/S", "/C", command))
   295  	} else {
   296  		expectedCommand = strslice.StrSlice(append([]string{"/bin/sh"}, "-c", command))
   297  	}
   298  
   299  	if !compareStrSlice(b.runConfig.Cmd, expectedCommand) {
   300  		t.Fatalf("Command should be set to %s, got %s", command, b.runConfig.Cmd)
   301  	}
   302  
   303  	if !b.cmdSet {
   304  		t.Fatalf("Command should be marked as set")
   305  	}
   306  }
   307  
   308  func compareStrSlice(slice1, slice2 strslice.StrSlice) bool {
   309  	if len(slice1) != len(slice2) {
   310  		return false
   311  	}
   312  
   313  	for i := range slice1 {
   314  		if slice1[i] != slice2[i] {
   315  			return false
   316  		}
   317  	}
   318  
   319  	return true
   320  }
   321  
   322  func TestHealthcheckNone(t *testing.T) {
   323  	b := &Builder{flags: &BFlags{}, runConfig: &container.Config{}, disableCommit: true}
   324  
   325  	if err := healthcheck(b, []string{"NONE"}, nil, ""); err != nil {
   326  		t.Fatalf("Error should be empty, got: %s", err.Error())
   327  	}
   328  
   329  	if b.runConfig.Healthcheck == nil {
   330  		t.Fatal("Healthcheck should be set, got nil")
   331  	}
   332  
   333  	expectedTest := strslice.StrSlice(append([]string{"NONE"}))
   334  
   335  	if !compareStrSlice(expectedTest, b.runConfig.Healthcheck.Test) {
   336  		t.Fatalf("Command should be set to %s, got %s", expectedTest, b.runConfig.Healthcheck.Test)
   337  	}
   338  }
   339  
   340  func TestHealthcheckCmd(t *testing.T) {
   341  	b := &Builder{flags: &BFlags{flags: make(map[string]*Flag)}, runConfig: &container.Config{}, disableCommit: true}
   342  
   343  	if err := healthcheck(b, []string{"CMD", "curl", "-f", "http://localhost/", "||", "exit", "1"}, nil, ""); err != nil {
   344  		t.Fatalf("Error should be empty, got: %s", err.Error())
   345  	}
   346  
   347  	if b.runConfig.Healthcheck == nil {
   348  		t.Fatal("Healthcheck should be set, got nil")
   349  	}
   350  
   351  	expectedTest := strslice.StrSlice(append([]string{"CMD-SHELL"}, "curl -f http://localhost/ || exit 1"))
   352  
   353  	if !compareStrSlice(expectedTest, b.runConfig.Healthcheck.Test) {
   354  		t.Fatalf("Command should be set to %s, got %s", expectedTest, b.runConfig.Healthcheck.Test)
   355  	}
   356  }
   357  
   358  func TestEntrypoint(t *testing.T) {
   359  	b := &Builder{flags: &BFlags{}, runConfig: &container.Config{}, disableCommit: true}
   360  
   361  	entrypointCmd := "/usr/sbin/nginx"
   362  
   363  	if err := entrypoint(b, []string{entrypointCmd}, nil, ""); err != nil {
   364  		t.Fatalf("Error should be empty, got: %s", err.Error())
   365  	}
   366  
   367  	if b.runConfig.Entrypoint == nil {
   368  		t.Fatalf("Entrypoint should be set")
   369  	}
   370  
   371  	var expectedEntrypoint strslice.StrSlice
   372  
   373  	if runtime.GOOS == "windows" {
   374  		expectedEntrypoint = strslice.StrSlice(append([]string{"cmd"}, "/S", "/C", entrypointCmd))
   375  	} else {
   376  		expectedEntrypoint = strslice.StrSlice(append([]string{"/bin/sh"}, "-c", entrypointCmd))
   377  	}
   378  
   379  	if !compareStrSlice(expectedEntrypoint, b.runConfig.Entrypoint) {
   380  		t.Fatalf("Entrypoint command should be set to %s, got %s", expectedEntrypoint, b.runConfig.Entrypoint)
   381  	}
   382  }
   383  
   384  func TestExpose(t *testing.T) {
   385  	b := &Builder{flags: &BFlags{}, runConfig: &container.Config{}, disableCommit: true}
   386  
   387  	exposedPort := "80"
   388  
   389  	if err := expose(b, []string{exposedPort}, nil, ""); err != nil {
   390  		t.Fatalf("Error should be empty, got: %s", err.Error())
   391  	}
   392  
   393  	if b.runConfig.ExposedPorts == nil {
   394  		t.Fatalf("ExposedPorts should be set")
   395  	}
   396  
   397  	if len(b.runConfig.ExposedPorts) != 1 {
   398  		t.Fatalf("ExposedPorts should contain only 1 element. Got %s", b.runConfig.ExposedPorts)
   399  	}
   400  
   401  	portsMapping, err := nat.ParsePortSpec(exposedPort)
   402  
   403  	if err != nil {
   404  		t.Fatalf("Error when parsing port spec: %s", err.Error())
   405  	}
   406  
   407  	if _, ok := b.runConfig.ExposedPorts[portsMapping[0].Port]; !ok {
   408  		t.Fatalf("Port %s should be present. Got %s", exposedPort, b.runConfig.ExposedPorts)
   409  	}
   410  }
   411  
   412  func TestUser(t *testing.T) {
   413  	b := &Builder{flags: &BFlags{}, runConfig: &container.Config{}, disableCommit: true}
   414  
   415  	userCommand := "foo"
   416  
   417  	if err := user(b, []string{userCommand}, nil, ""); err != nil {
   418  		t.Fatalf("Error should be empty, got: %s", err.Error())
   419  	}
   420  
   421  	if b.runConfig.User != userCommand {
   422  		t.Fatalf("User should be set to %s, got %s", userCommand, b.runConfig.User)
   423  	}
   424  }
   425  
   426  func TestVolume(t *testing.T) {
   427  	b := &Builder{flags: &BFlags{}, runConfig: &container.Config{}, disableCommit: true}
   428  
   429  	exposedVolume := "/foo"
   430  
   431  	if err := volume(b, []string{exposedVolume}, nil, ""); err != nil {
   432  		t.Fatalf("Error should be empty, got: %s", err.Error())
   433  	}
   434  
   435  	if b.runConfig.Volumes == nil {
   436  		t.Fatalf("Volumes should be set")
   437  	}
   438  
   439  	if len(b.runConfig.Volumes) != 1 {
   440  		t.Fatalf("Volumes should contain only 1 element. Got %s", b.runConfig.Volumes)
   441  	}
   442  
   443  	if _, ok := b.runConfig.Volumes[exposedVolume]; !ok {
   444  		t.Fatalf("Volume %s should be present. Got %s", exposedVolume, b.runConfig.Volumes)
   445  	}
   446  }
   447  
   448  func TestStopSignal(t *testing.T) {
   449  	b := &Builder{flags: &BFlags{}, runConfig: &container.Config{}, disableCommit: true}
   450  
   451  	signal := "SIGKILL"
   452  
   453  	if err := stopSignal(b, []string{signal}, nil, ""); err != nil {
   454  		t.Fatalf("Error should be empty, got: %s", err.Error())
   455  	}
   456  
   457  	if b.runConfig.StopSignal != signal {
   458  		t.Fatalf("StopSignal should be set to %s, got %s", signal, b.runConfig.StopSignal)
   459  	}
   460  }
   461  
   462  func TestArg(t *testing.T) {
   463  	buildOptions := &types.ImageBuildOptions{BuildArgs: make(map[string]*string)}
   464  
   465  	b := &Builder{flags: &BFlags{}, runConfig: &container.Config{}, disableCommit: true, allowedBuildArgs: make(map[string]bool), options: buildOptions}
   466  
   467  	argName := "foo"
   468  	argVal := "bar"
   469  	argDef := fmt.Sprintf("%s=%s", argName, argVal)
   470  
   471  	if err := arg(b, []string{argDef}, nil, ""); err != nil {
   472  		t.Fatalf("Error should be empty, got: %s", err.Error())
   473  	}
   474  
   475  	allowed, ok := b.allowedBuildArgs[argName]
   476  
   477  	if !ok {
   478  		t.Fatalf("%s argument should be allowed as a build arg", argName)
   479  	}
   480  
   481  	if !allowed {
   482  		t.Fatalf("%s argument was present in map but disallowed as a build arg", argName)
   483  	}
   484  
   485  	val, ok := b.options.BuildArgs[argName]
   486  
   487  	if !ok {
   488  		t.Fatalf("%s argument should be a build arg", argName)
   489  	}
   490  
   491  	if *val != "bar" {
   492  		t.Fatalf("%s argument should have default value 'bar', got %s", argName, val)
   493  	}
   494  }
   495  
   496  func TestShell(t *testing.T) {
   497  	b := &Builder{flags: &BFlags{}, runConfig: &container.Config{}, disableCommit: true}
   498  
   499  	shellCmd := "powershell"
   500  
   501  	attrs := make(map[string]bool)
   502  	attrs["json"] = true
   503  
   504  	if err := shell(b, []string{shellCmd}, attrs, ""); err != nil {
   505  		t.Fatalf("Error should be empty, got: %s", err.Error())
   506  	}
   507  
   508  	if b.runConfig.Shell == nil {
   509  		t.Fatalf("Shell should be set")
   510  	}
   511  
   512  	expectedShell := strslice.StrSlice([]string{shellCmd})
   513  
   514  	if !compareStrSlice(expectedShell, b.runConfig.Shell) {
   515  		t.Fatalf("Shell should be set to %s, got %s", expectedShell, b.runConfig.Shell)
   516  	}
   517  }