// FromImage updates the builder to use the provided image (resetting RunConfig // and recording the image environment), and updates the node with any ONBUILD // statements extracted from the parent image. func (b *Builder) FromImage(image *docker.Image, node *parser.Node) error { SplitChildren(node, command.From) b.RunConfig = *image.Config b.Env = b.RunConfig.Env b.RunConfig.Env = nil // Check to see if we have a default PATH, note that windows won't // have one as its set by HCS if runtime.GOOS != "windows" && !hasEnvName(b.Env, "PATH") { b.RunConfig.Env = append(b.RunConfig.Env, "PATH="+defaultPathEnv) } // Join the image onbuild statements into node if image.Config == nil || len(image.Config.OnBuild) == 0 { return nil } extra, err := parser.Parse(bytes.NewBufferString(strings.Join(image.Config.OnBuild, "\n"))) if err != nil { return err } for _, child := range extra.Children { switch strings.ToUpper(child.Value) { case "ONBUILD": return fmt.Errorf("Chaining ONBUILD via `ONBUILD ONBUILD` isn't allowed") case "MAINTAINER", "FROM": return fmt.Errorf("%s isn't allowed as an ONBUILD trigger", child.Value) } } node.Children = append(extra.Children, node.Children...) // Since we've processed the OnBuild statements, clear them from the runconfig state. b.RunConfig.OnBuild = nil return nil }
// SplitChildren removes any children with the provided value from node // and returns them as an array. node.Children is updated. func SplitChildren(node *parser.Node, value string) []*parser.Node { var split []*parser.Node var children []*parser.Node for _, child := range node.Children { if child.Value == value { split = append(split, child) } else { children = append(children, child) } } node.Children = children return split }
// InsertInstructions inserts instructions starting from the pos-th child of // node, moving other children as necessary. The instructions should be valid // Dockerfile instructions. InsertInstructions mutates node in-place, and the // final state of node is equivalent to what parser.Parse would return if the // original Dockerfile represented by node contained the instructions at the // specified position pos. If the returned error is non-nil, node is guaranteed // to be unchanged. func InsertInstructions(node *parser.Node, pos int, instructions string) error { if node == nil { return fmt.Errorf("cannot insert instructions in a nil node") } if pos < 0 || pos > len(node.Children) { return fmt.Errorf("pos %d out of range [0, %d]", pos, len(node.Children)-1) } newChild, err := parser.Parse(strings.NewReader(instructions)) if err != nil { return err } // InsertVector pattern (https://github.com/golang/go/wiki/SliceTricks) node.Children = append(node.Children[:pos], append(newChild.Children, node.Children[pos:]...)...) return nil }