github.com/pritambaral/docker@v1.4.2-0.20150120174542-b2fe1b3dd952/builder/evaluator.go (about)

     1  // builder is the evaluation step in the Dockerfile parse/evaluate pipeline.
     2  //
     3  // It incorporates a dispatch table based on the parser.Node values (see the
     4  // parser package for more information) that are yielded from the parser itself.
     5  // Calling NewBuilder with the BuildOpts struct can be used to customize the
     6  // experience for execution purposes only. Parsing is controlled in the parser
     7  // package, and this division of resposibility should be respected.
     8  //
     9  // Please see the jump table targets for the actual invocations, most of which
    10  // will call out to the functions in internals.go to deal with their tasks.
    11  //
    12  // ONBUILD is a special case, which is covered in the onbuild() func in
    13  // dispatchers.go.
    14  //
    15  // The evaluator uses the concept of "steps", which are usually each processable
    16  // line in the Dockerfile. Each step is numbered and certain actions are taken
    17  // before and after each step, such as creating an image ID and removing temporary
    18  // containers and images. Note that ONBUILD creates a kinda-sorta "sub run" which
    19  // includes its own set of steps (usually only one of them).
    20  package builder
    21  
    22  import (
    23  	"errors"
    24  	"fmt"
    25  	"io"
    26  	"os"
    27  	"path"
    28  	"strings"
    29  
    30  	log "github.com/Sirupsen/logrus"
    31  	"github.com/docker/docker/builder/parser"
    32  	"github.com/docker/docker/daemon"
    33  	"github.com/docker/docker/engine"
    34  	"github.com/docker/docker/pkg/fileutils"
    35  	"github.com/docker/docker/pkg/tarsum"
    36  	"github.com/docker/docker/registry"
    37  	"github.com/docker/docker/runconfig"
    38  	"github.com/docker/docker/utils"
    39  )
    40  
    41  var (
    42  	ErrDockerfileEmpty = errors.New("Dockerfile cannot be empty")
    43  )
    44  
    45  // Environment variable interpolation will happen on these statements only.
    46  var replaceEnvAllowed = map[string]struct{}{
    47  	"env":     {},
    48  	"add":     {},
    49  	"copy":    {},
    50  	"workdir": {},
    51  	"expose":  {},
    52  	"volume":  {},
    53  	"user":    {},
    54  }
    55  
    56  var evaluateTable map[string]func(*Builder, []string, map[string]bool, string) error
    57  
    58  func init() {
    59  	evaluateTable = map[string]func(*Builder, []string, map[string]bool, string) error{
    60  		"env":        env,
    61  		"maintainer": maintainer,
    62  		"add":        add,
    63  		"copy":       dispatchCopy, // copy() is a go builtin
    64  		"from":       from,
    65  		"onbuild":    onbuild,
    66  		"workdir":    workdir,
    67  		"run":        run,
    68  		"cmd":        cmd,
    69  		"entrypoint": entrypoint,
    70  		"expose":     expose,
    71  		"volume":     volume,
    72  		"user":       user,
    73  		"insert":     insert,
    74  	}
    75  }
    76  
    77  // internal struct, used to maintain configuration of the Dockerfile's
    78  // processing as it evaluates the parsing result.
    79  type Builder struct {
    80  	Daemon *daemon.Daemon
    81  	Engine *engine.Engine
    82  
    83  	// effectively stdio for the run. Because it is not stdio, I said
    84  	// "Effectively". Do not use stdio anywhere in this package for any reason.
    85  	OutStream io.Writer
    86  	ErrStream io.Writer
    87  
    88  	Verbose      bool
    89  	UtilizeCache bool
    90  
    91  	// controls how images and containers are handled between steps.
    92  	Remove      bool
    93  	ForceRemove bool
    94  	Pull        bool
    95  
    96  	AuthConfig     *registry.AuthConfig
    97  	AuthConfigFile *registry.ConfigFile
    98  
    99  	// Deprecated, original writer used for ImagePull. To be removed.
   100  	OutOld          io.Writer
   101  	StreamFormatter *utils.StreamFormatter
   102  
   103  	Config *runconfig.Config // runconfig for cmd, run, entrypoint etc.
   104  
   105  	// both of these are controlled by the Remove and ForceRemove options in BuildOpts
   106  	TmpContainers map[string]struct{} // a map of containers used for removes
   107  
   108  	dockerfileName string        // name of Dockerfile
   109  	dockerfile     *parser.Node  // the syntax tree of the dockerfile
   110  	image          string        // image name for commit processing
   111  	maintainer     string        // maintainer name. could probably be removed.
   112  	cmdSet         bool          // indicates is CMD was set in current Dockerfile
   113  	context        tarsum.TarSum // the context is a tarball that is uploaded by the client
   114  	contextPath    string        // the path of the temporary directory the local context is unpacked to (server side)
   115  	noBaseImage    bool          // indicates that this build does not start from any base image, but is being built from an empty file system.
   116  }
   117  
   118  // Run the builder with the context. This is the lynchpin of this package. This
   119  // will (barring errors):
   120  //
   121  // * call readContext() which will set up the temporary directory and unpack
   122  //   the context into it.
   123  // * read the dockerfile
   124  // * parse the dockerfile
   125  // * walk the parse tree and execute it by dispatching to handlers. If Remove
   126  //   or ForceRemove is set, additional cleanup around containers happens after
   127  //   processing.
   128  // * Print a happy message and return the image ID.
   129  //
   130  func (b *Builder) Run(context io.Reader) (string, error) {
   131  	if err := b.readContext(context); err != nil {
   132  		return "", err
   133  	}
   134  
   135  	defer func() {
   136  		if err := os.RemoveAll(b.contextPath); err != nil {
   137  			log.Debugf("[BUILDER] failed to remove temporary context: %s", err)
   138  		}
   139  	}()
   140  
   141  	if err := b.readDockerfile(b.dockerfileName); err != nil {
   142  		return "", err
   143  	}
   144  
   145  	// some initializations that would not have been supplied by the caller.
   146  	b.Config = &runconfig.Config{}
   147  	b.TmpContainers = map[string]struct{}{}
   148  
   149  	for i, n := range b.dockerfile.Children {
   150  		if err := b.dispatch(i, n); err != nil {
   151  			if b.ForceRemove {
   152  				b.clearTmp()
   153  			}
   154  			return "", err
   155  		}
   156  		fmt.Fprintf(b.OutStream, " ---> %s\n", utils.TruncateID(b.image))
   157  		if b.Remove {
   158  			b.clearTmp()
   159  		}
   160  	}
   161  
   162  	if b.image == "" {
   163  		return "", fmt.Errorf("No image was generated. Is your Dockerfile empty?\n")
   164  	}
   165  
   166  	fmt.Fprintf(b.OutStream, "Successfully built %s\n", utils.TruncateID(b.image))
   167  	return b.image, nil
   168  }
   169  
   170  // Reads a Dockerfile from the current context. It assumes that the
   171  // 'filename' is a relative path from the root of the context
   172  func (b *Builder) readDockerfile(filename string) error {
   173  	filename = path.Join(b.contextPath, filename)
   174  
   175  	fi, err := os.Stat(filename)
   176  	if os.IsNotExist(err) {
   177  		return fmt.Errorf("Cannot build a directory without a Dockerfile")
   178  	}
   179  	if fi.Size() == 0 {
   180  		return ErrDockerfileEmpty
   181  	}
   182  
   183  	f, err := os.Open(filename)
   184  	if err != nil {
   185  		return err
   186  	}
   187  
   188  	b.dockerfile, err = parser.Parse(f)
   189  	f.Close()
   190  
   191  	if err != nil {
   192  		return err
   193  	}
   194  
   195  	// After the Dockerfile has been parsed, we need to check the .dockerignore
   196  	// file for either "Dockerfile" or ".dockerignore", and if either are
   197  	// present then erase them from the build context. These files should never
   198  	// have been sent from the client but we did send them to make sure that
   199  	// we had the Dockerfile to actually parse, and then we also need the
   200  	// .dockerignore file to know whether either file should be removed.
   201  	// Note that this assumes the Dockerfile has been read into memory and
   202  	// is now safe to be removed.
   203  
   204  	excludes, _ := utils.ReadDockerIgnore(path.Join(b.contextPath, ".dockerignore"))
   205  	if rm, _ := fileutils.Matches(".dockerignore", excludes); rm == true {
   206  		os.Remove(path.Join(b.contextPath, ".dockerignore"))
   207  		b.context.(tarsum.BuilderContext).Remove(".dockerignore")
   208  	}
   209  	if rm, _ := fileutils.Matches(b.dockerfileName, excludes); rm == true {
   210  		os.Remove(path.Join(b.contextPath, b.dockerfileName))
   211  		b.context.(tarsum.BuilderContext).Remove(b.dockerfileName)
   212  	}
   213  
   214  	return nil
   215  }
   216  
   217  // This method is the entrypoint to all statement handling routines.
   218  //
   219  // Almost all nodes will have this structure:
   220  // Child[Node, Node, Node] where Child is from parser.Node.Children and each
   221  // node comes from parser.Node.Next. This forms a "line" with a statement and
   222  // arguments and we process them in this normalized form by hitting
   223  // evaluateTable with the leaf nodes of the command and the Builder object.
   224  //
   225  // ONBUILD is a special case; in this case the parser will emit:
   226  // Child[Node, Child[Node, Node...]] where the first node is the literal
   227  // "onbuild" and the child entrypoint is the command of the ONBUILD statmeent,
   228  // such as `RUN` in ONBUILD RUN foo. There is special case logic in here to
   229  // deal with that, at least until it becomes more of a general concern with new
   230  // features.
   231  func (b *Builder) dispatch(stepN int, ast *parser.Node) error {
   232  	cmd := ast.Value
   233  	attrs := ast.Attributes
   234  	original := ast.Original
   235  	strs := []string{}
   236  	msg := fmt.Sprintf("Step %d : %s", stepN, strings.ToUpper(cmd))
   237  
   238  	if cmd == "onbuild" {
   239  		ast = ast.Next.Children[0]
   240  		strs = append(strs, ast.Value)
   241  		msg += " " + ast.Value
   242  	}
   243  
   244  	// count the number of nodes that we are going to traverse first
   245  	// so we can pre-create the argument and message array. This speeds up the
   246  	// allocation of those list a lot when they have a lot of arguments
   247  	cursor := ast
   248  	var n int
   249  	for cursor.Next != nil {
   250  		cursor = cursor.Next
   251  		n++
   252  	}
   253  	l := len(strs)
   254  	strList := make([]string, n+l)
   255  	copy(strList, strs)
   256  	msgList := make([]string, n)
   257  
   258  	var i int
   259  	for ast.Next != nil {
   260  		ast = ast.Next
   261  		var str string
   262  		str = ast.Value
   263  		if _, ok := replaceEnvAllowed[cmd]; ok {
   264  			str = b.replaceEnv(ast.Value)
   265  		}
   266  		strList[i+l] = str
   267  		msgList[i] = ast.Value
   268  		i++
   269  	}
   270  
   271  	msg += " " + strings.Join(msgList, " ")
   272  	fmt.Fprintln(b.OutStream, msg)
   273  
   274  	// XXX yes, we skip any cmds that are not valid; the parser should have
   275  	// picked these out already.
   276  	if f, ok := evaluateTable[cmd]; ok {
   277  		return f(b, strList, attrs, original)
   278  	}
   279  
   280  	fmt.Fprintf(b.ErrStream, "# Skipping unknown instruction %s\n", strings.ToUpper(cmd))
   281  
   282  	return nil
   283  }