github.com/sams1990/dockerrepo@v17.12.1-ce-rc2+incompatible/builder/dockerfile/instructions/parse_test.go (about)

     1  package instructions
     2  
     3  import (
     4  	"strings"
     5  	"testing"
     6  
     7  	"github.com/docker/docker/builder/dockerfile/command"
     8  	"github.com/docker/docker/builder/dockerfile/parser"
     9  	"github.com/docker/docker/internal/testutil"
    10  	"github.com/stretchr/testify/assert"
    11  	"github.com/stretchr/testify/require"
    12  )
    13  
    14  func TestCommandsExactlyOneArgument(t *testing.T) {
    15  	commands := []string{
    16  		"MAINTAINER",
    17  		"WORKDIR",
    18  		"USER",
    19  		"STOPSIGNAL",
    20  	}
    21  
    22  	for _, command := range commands {
    23  		ast, err := parser.Parse(strings.NewReader(command))
    24  		require.NoError(t, err)
    25  		_, err = ParseInstruction(ast.AST.Children[0])
    26  		assert.EqualError(t, err, errExactlyOneArgument(command).Error())
    27  	}
    28  }
    29  
    30  func TestCommandsAtLeastOneArgument(t *testing.T) {
    31  	commands := []string{
    32  		"ENV",
    33  		"LABEL",
    34  		"ONBUILD",
    35  		"HEALTHCHECK",
    36  		"EXPOSE",
    37  		"VOLUME",
    38  	}
    39  
    40  	for _, command := range commands {
    41  		ast, err := parser.Parse(strings.NewReader(command))
    42  		require.NoError(t, err)
    43  		_, err = ParseInstruction(ast.AST.Children[0])
    44  		assert.EqualError(t, err, errAtLeastOneArgument(command).Error())
    45  	}
    46  }
    47  
    48  func TestCommandsNoDestinationArgument(t *testing.T) {
    49  	commands := []string{
    50  		"ADD",
    51  		"COPY",
    52  	}
    53  
    54  	for _, command := range commands {
    55  		ast, err := parser.Parse(strings.NewReader(command + " arg1"))
    56  		require.NoError(t, err)
    57  		_, err = ParseInstruction(ast.AST.Children[0])
    58  		assert.EqualError(t, err, errNoDestinationArgument(command).Error())
    59  	}
    60  }
    61  
    62  func TestCommandsTooManyArguments(t *testing.T) {
    63  	commands := []string{
    64  		"ENV",
    65  		"LABEL",
    66  	}
    67  
    68  	for _, command := range commands {
    69  		node := &parser.Node{
    70  			Original: command + "arg1 arg2 arg3",
    71  			Value:    strings.ToLower(command),
    72  			Next: &parser.Node{
    73  				Value: "arg1",
    74  				Next: &parser.Node{
    75  					Value: "arg2",
    76  					Next: &parser.Node{
    77  						Value: "arg3",
    78  					},
    79  				},
    80  			},
    81  		}
    82  		_, err := ParseInstruction(node)
    83  		assert.EqualError(t, err, errTooManyArguments(command).Error())
    84  	}
    85  }
    86  
    87  func TestCommandsBlankNames(t *testing.T) {
    88  	commands := []string{
    89  		"ENV",
    90  		"LABEL",
    91  	}
    92  
    93  	for _, command := range commands {
    94  		node := &parser.Node{
    95  			Original: command + " =arg2",
    96  			Value:    strings.ToLower(command),
    97  			Next: &parser.Node{
    98  				Value: "",
    99  				Next: &parser.Node{
   100  					Value: "arg2",
   101  				},
   102  			},
   103  		}
   104  		_, err := ParseInstruction(node)
   105  		assert.EqualError(t, err, errBlankCommandNames(command).Error())
   106  	}
   107  }
   108  
   109  func TestHealthCheckCmd(t *testing.T) {
   110  	node := &parser.Node{
   111  		Value: command.Healthcheck,
   112  		Next: &parser.Node{
   113  			Value: "CMD",
   114  			Next: &parser.Node{
   115  				Value: "hello",
   116  				Next: &parser.Node{
   117  					Value: "world",
   118  				},
   119  			},
   120  		},
   121  	}
   122  	cmd, err := ParseInstruction(node)
   123  	assert.NoError(t, err)
   124  	hc, ok := cmd.(*HealthCheckCommand)
   125  	assert.True(t, ok)
   126  	expected := []string{"CMD-SHELL", "hello world"}
   127  	assert.Equal(t, expected, hc.Health.Test)
   128  }
   129  
   130  func TestParseOptInterval(t *testing.T) {
   131  	flInterval := &Flag{
   132  		name:     "interval",
   133  		flagType: stringType,
   134  		Value:    "50ns",
   135  	}
   136  	_, err := parseOptInterval(flInterval)
   137  	testutil.ErrorContains(t, err, "cannot be less than 1ms")
   138  
   139  	flInterval.Value = "1ms"
   140  	_, err = parseOptInterval(flInterval)
   141  	require.NoError(t, err)
   142  }
   143  
   144  func TestErrorCases(t *testing.T) {
   145  	cases := []struct {
   146  		name          string
   147  		dockerfile    string
   148  		expectedError string
   149  	}{
   150  		{
   151  			name: "copyEmptyWhitespace",
   152  			dockerfile: `COPY	
   153  		quux \
   154        bar`,
   155  			expectedError: "COPY requires at least two arguments",
   156  		},
   157  		{
   158  			name:          "ONBUILD forbidden FROM",
   159  			dockerfile:    "ONBUILD FROM scratch",
   160  			expectedError: "FROM isn't allowed as an ONBUILD trigger",
   161  		},
   162  		{
   163  			name:          "ONBUILD forbidden MAINTAINER",
   164  			dockerfile:    "ONBUILD MAINTAINER docker.io",
   165  			expectedError: "MAINTAINER isn't allowed as an ONBUILD trigger",
   166  		},
   167  		{
   168  			name:          "ARG two arguments",
   169  			dockerfile:    "ARG foo bar",
   170  			expectedError: "ARG requires exactly one argument",
   171  		},
   172  		{
   173  			name:          "MAINTAINER unknown flag",
   174  			dockerfile:    "MAINTAINER --boo joe@example.com",
   175  			expectedError: "Unknown flag: boo",
   176  		},
   177  		{
   178  			name:          "Chaining ONBUILD",
   179  			dockerfile:    `ONBUILD ONBUILD RUN touch foobar`,
   180  			expectedError: "Chaining ONBUILD via `ONBUILD ONBUILD` isn't allowed",
   181  		},
   182  		{
   183  			name:          "Invalid instruction",
   184  			dockerfile:    `foo bar`,
   185  			expectedError: "unknown instruction: FOO",
   186  		},
   187  	}
   188  	for _, c := range cases {
   189  		r := strings.NewReader(c.dockerfile)
   190  		ast, err := parser.Parse(r)
   191  
   192  		if err != nil {
   193  			t.Fatalf("Error when parsing Dockerfile: %s", err)
   194  		}
   195  		n := ast.AST.Children[0]
   196  		_, err = ParseInstruction(n)
   197  		testutil.ErrorContains(t, err, c.expectedError)
   198  	}
   199  
   200  }