// NewRockerfile reads parses Rockerfile from an io.Reader func NewRockerfile(name string, in io.Reader, vars template.Vars, funs template.Funs) (r *Rockerfile, err error) { r = &Rockerfile{ Name: name, Vars: vars, Funs: funs, } var ( source []byte content *bytes.Buffer ) if source, err = ioutil.ReadAll(in); err != nil { return nil, fmt.Errorf("Failed to read Rockerfile %s, error: %s", name, err) } r.Source = string(source) if content, err = template.Process(name, bytes.NewReader(source), vars, funs); err != nil { return nil, err } r.Content = content.String() // TODO: update parser from Docker if r.rootNode, err = parser.Parse(content); err != nil { return nil, err } return r, nil }
// Parse parses a Rockerfile from an io.Reader and returns AST data structure func Parse(rockerfileContent io.Reader) (*parser.Node, error) { node, err := parser.Parse(rockerfileContent) if err != nil { return nil, err } return node, nil }
// cmdInclude implements INCLUDE command // TODO: document behavior of cmdInclude func (builder *Builder) cmdInclude(args []string, attributes map[string]bool, flags map[string]string, original string) (err error) { if len(args) == 0 { return fmt.Errorf("Command is missing value: %s", original) } module := args[0] contextDir := filepath.Dir(builder.Rockerfile) resultPath := filepath.Clean(path.Join(contextDir, module)) // TODO: protect against going out of working directory? stat, err := os.Stat(resultPath) if err != nil { return err } if !stat.Mode().IsRegular() { return fmt.Errorf("Expected included resource to be a regular file: %s (%s)", module, original) } fd, err := os.Open(resultPath) if err != nil { return err } defer fd.Close() includedNode, err := parser.Parse(fd) if err != nil { return err } for _, node := range includedNode.Children { if node.Value == "include" { return fmt.Errorf("Nesting includes is not allowed: \"%s\" in %s", original, resultPath) } } // inject included commands info root node at current execution position after := append(includedNode.Children, builder.rootNode.Children[builder.i+1:]...) builder.rootNode.Children = append(builder.rootNode.Children[:builder.i], after...) builder.i-- return nil }
func parseOnbuildCommands(onBuildTriggers []string) ([]ConfigCommand, error) { commands := []ConfigCommand{} for _, step := range onBuildTriggers { ast, err := parser.Parse(strings.NewReader(step)) if err != nil { return commands, err } for _, n := range ast.Children { switch strings.ToUpper(n.Value) { case "ONBUILD": return commands, fmt.Errorf("Chaining ONBUILD via `ONBUILD ONBUILD` isn't allowed") case "MAINTAINER", "FROM": return commands, fmt.Errorf("%s isn't allowed as an ONBUILD trigger", n.Value) } commands = append(commands, parseCommand(n, true)) } } return commands, nil }
// Build runs the build of given Rockerfile and returns image id func (builder *Builder) Build() (imageID string, err error) { // Do initial cleanup, you know, just to be sure // Previous builds could be ended up abnormally if err := builder.cleanup(); err != nil { return "", err } // Initialize auth configuration if builder.Auth == nil { builder.Auth = &docker.AuthConfiguration{} } // Initialize in/out file descriptors if builder.InStream != nil { fd, isTerminal := term.GetFdInfo(builder.InStream) builder.fdIn = fd builder.isTerminalIn = isTerminal } if builder.OutStream != nil { fd, isTerminal := term.GetFdInfo(builder.OutStream) builder.fdOut = fd builder.isTerminalOut = isTerminal } // Wrap this into function to have deferred functions run before // we do final checks run := func() (err error) { fd, err := os.Open(builder.Rockerfile) if err != nil { return fmt.Errorf("Failed to open file %s, error: %s", builder.Rockerfile, err) } defer fd.Close() data, err := template.Process(builder.Rockerfile, fd, builder.Vars.ToMapOfInterface(), map[string]interface{}{}) if err != nil { return err } builder.RockerfileContent = data.String() if builder.Print { fmt.Print(builder.RockerfileContent) os.Exit(0) } if builder.ContextDir == "" { builder.ContextDir = filepath.Dir(builder.Rockerfile) } if _, err := os.Stat(builder.ContextDir); err != nil { return err } if err := builder.checkDockerignore(); err != nil { return err } rootNode, err := parser.Parse(strings.NewReader(builder.RockerfileContent)) if err != nil { return err } builder.rootNode = rootNode builder.dockerfile = &parser.Node{} defer func() { if err2 := builder.cleanup(); err2 != nil && err == nil { err = err2 } }() for builder.i = 0; builder.i < len(builder.rootNode.Children); builder.i++ { oldImageID := builder.imageID if err := builder.dispatch(builder.i, builder.rootNode.Children[builder.i]); err != nil { return err } if builder.imageID != oldImageID && builder.imageID != "" { fmt.Fprintf(builder.OutStream, "[Rocker] ---> %.12s\n", builder.imageID) } } if err := builder.runDockerfile(); err != nil { return err } return nil } if err := run(); err != nil { return "", err } if builder.imageID == "" { return "", fmt.Errorf("No image was generated. Is your Rockerfile empty?") } fmt.Fprintf(builder.OutStream, "[Rocker] Successfully built %.12s\n", builder.imageID) return builder.imageID, nil }