Browse Source

Fix case where \\ at EOF made the builder ignore the command

Came from looking at issue #27545

Signed-off-by: Doug Davis <dug@us.ibm.com>
Doug Davis 8 years ago
parent
commit
eaf0b5708f

+ 1 - 1
builder/dockerfile/builder.go

@@ -238,7 +238,7 @@ func (b *Builder) build(stdout io.Writer, stderr io.Writer, out io.Writer) (stri
 		for k, v := range b.options.Labels {
 		for k, v := range b.options.Labels {
 			line += fmt.Sprintf("%q='%s' ", k, v)
 			line += fmt.Sprintf("%q='%s' ", k, v)
 		}
 		}
-		_, node, err := parser.ParseLine(line, &b.directive)
+		_, node, err := parser.ParseLine(line, &b.directive, false)
 		if err != nil {
 		if err != nil {
 			return "", err
 			return "", err
 		}
 		}

+ 1 - 1
builder/dockerfile/parser/line_parsers.go

@@ -35,7 +35,7 @@ func parseSubCommand(rest string, d *Directive) (*Node, map[string]bool, error)
 		return nil, nil, nil
 		return nil, nil, nil
 	}
 	}
 
 
-	_, child, err := ParseLine(rest, d)
+	_, child, err := ParseLine(rest, d, false)
 	if err != nil {
 	if err != nil {
 		return nil, nil, err
 		return nil, nil, err
 	}
 	}

+ 11 - 5
builder/dockerfile/parser/parser.go

@@ -95,7 +95,7 @@ func init() {
 }
 }
 
 
 // ParseLine parses a line and returns the remainder.
 // ParseLine parses a line and returns the remainder.
-func ParseLine(line string, d *Directive) (string, *Node, error) {
+func ParseLine(line string, d *Directive, ignoreCont bool) (string, *Node, error) {
 	// Handle the parser directive '# escape=<char>. Parser directives must precede
 	// Handle the parser directive '# escape=<char>. Parser directives must precede
 	// any builder instruction or other comments, and cannot be repeated.
 	// any builder instruction or other comments, and cannot be repeated.
 	if d.LookingForDirectives {
 	if d.LookingForDirectives {
@@ -122,7 +122,7 @@ func ParseLine(line string, d *Directive) (string, *Node, error) {
 		return "", nil, nil
 		return "", nil, nil
 	}
 	}
 
 
-	if d.LineContinuationRegex.MatchString(line) {
+	if !ignoreCont && d.LineContinuationRegex.MatchString(line) {
 		line = d.LineContinuationRegex.ReplaceAllString(line, "")
 		line = d.LineContinuationRegex.ReplaceAllString(line, "")
 		return line, nil, nil
 		return line, nil, nil
 	}
 	}
@@ -165,7 +165,7 @@ func Parse(rwc io.Reader, d *Directive) (*Node, error) {
 		}
 		}
 		scannedLine := strings.TrimLeftFunc(string(scannedBytes), unicode.IsSpace)
 		scannedLine := strings.TrimLeftFunc(string(scannedBytes), unicode.IsSpace)
 		currentLine++
 		currentLine++
-		line, child, err := ParseLine(scannedLine, d)
+		line, child, err := ParseLine(scannedLine, d, false)
 		if err != nil {
 		if err != nil {
 			return nil, err
 			return nil, err
 		}
 		}
@@ -187,7 +187,7 @@ func Parse(rwc io.Reader, d *Directive) (*Node, error) {
 				if strings.TrimSpace(newline) == "" {
 				if strings.TrimSpace(newline) == "" {
 					break
 					break
 				}
 				}
-				line, child, err = ParseLine(line+newline, d)
+				line, child, err = ParseLine(line+newline, d, false)
 				if err != nil {
 				if err != nil {
 					return nil, err
 					return nil, err
 				}
 				}
@@ -197,7 +197,13 @@ func Parse(rwc io.Reader, d *Directive) (*Node, error) {
 				}
 				}
 			}
 			}
 			if child == nil && line != "" {
 			if child == nil && line != "" {
-				_, child, err = ParseLine(line, d)
+				// When we call ParseLine we'll pass in 'true' for
+				// the ignoreCont param if we're at the EOF. This will
+				// prevent the func from returning immediately w/o
+				// parsing the line thinking that there's more input
+				// to come.
+
+				_, child, err = ParseLine(line, d, scanner.Err() == nil)
 				if err != nil {
 				if err != nil {
 					return nil, err
 					return nil, err
 				}
 				}

+ 30 - 0
integration-cli/docker_cli_build_test.go

@@ -7237,3 +7237,33 @@ func (s *DockerSuite) TestBuildSquashParent(c *check.C) {
 	c.Assert(err, checker.IsNil)
 	c.Assert(err, checker.IsNil)
 	c.Assert(strings.TrimSpace(out), checker.Equals, "3")
 	c.Assert(strings.TrimSpace(out), checker.Equals, "3")
 }
 }
+
+func (s *DockerSuite) TestBuildContChar(c *check.C) {
+	name := "testbuildcontchar"
+
+	_, out, err := buildImageWithOut(name,
+		`FROM busybox\`, true)
+	c.Assert(err, checker.IsNil)
+	c.Assert(out, checker.Contains, "Step 1/1 : FROM busybox")
+
+	_, out, err = buildImageWithOut(name,
+		`FROM busybox
+		 RUN echo hi \`, true)
+	c.Assert(err, checker.IsNil)
+	c.Assert(out, checker.Contains, "Step 1/2 : FROM busybox")
+	c.Assert(out, checker.Contains, "Step 2/2 : RUN echo hi\n")
+
+	_, out, err = buildImageWithOut(name,
+		`FROM busybox
+		 RUN echo hi \\`, true)
+	c.Assert(err, checker.IsNil)
+	c.Assert(out, checker.Contains, "Step 1/2 : FROM busybox")
+	c.Assert(out, checker.Contains, "Step 2/2 : RUN echo hi \\\n")
+
+	_, out, err = buildImageWithOut(name,
+		`FROM busybox
+		 RUN echo hi \\\`, true)
+	c.Assert(err, checker.IsNil)
+	c.Assert(out, checker.Contains, "Step 1/2 : FROM busybox")
+	c.Assert(out, checker.Contains, "Step 2/2 : RUN echo hi \\\\\n")
+}