2014-08-05 20:17:40 +00:00
|
|
|
package evaluator
|
|
|
|
|
|
|
|
import (
|
2014-08-05 22:41:09 +00:00
|
|
|
"bytes"
|
|
|
|
"errors"
|
2014-08-05 20:17:40 +00:00
|
|
|
"fmt"
|
|
|
|
"io"
|
2014-08-05 22:41:09 +00:00
|
|
|
"io/ioutil"
|
|
|
|
"os"
|
|
|
|
"path"
|
2014-08-05 20:17:40 +00:00
|
|
|
"strings"
|
|
|
|
|
2014-08-05 22:41:09 +00:00
|
|
|
"github.com/docker/docker/builder/parser"
|
2014-08-05 20:17:40 +00:00
|
|
|
"github.com/docker/docker/daemon"
|
|
|
|
"github.com/docker/docker/engine"
|
|
|
|
"github.com/docker/docker/nat"
|
2014-08-05 22:41:09 +00:00
|
|
|
"github.com/docker/docker/pkg/tarsum"
|
2014-08-05 20:17:40 +00:00
|
|
|
"github.com/docker/docker/registry"
|
|
|
|
"github.com/docker/docker/runconfig"
|
|
|
|
"github.com/docker/docker/utils"
|
|
|
|
)
|
|
|
|
|
|
|
|
var (
|
2014-08-05 22:41:09 +00:00
|
|
|
ErrDockerfileEmpty = errors.New("Dockerfile cannot be empty")
|
2014-08-05 20:17:40 +00:00
|
|
|
)
|
|
|
|
|
2014-08-05 22:41:09 +00:00
|
|
|
var evaluateTable map[string]func(*buildFile, []string) error
|
|
|
|
|
|
|
|
func init() {
|
|
|
|
evaluateTable = map[string]func(*buildFile, []string) error{
|
|
|
|
"env": env,
|
|
|
|
"maintainer": maintainer,
|
|
|
|
"add": add,
|
|
|
|
"copy": dispatchCopy, // copy() is a go builtin
|
|
|
|
"from": from,
|
|
|
|
"onbuild": onbuild,
|
|
|
|
"workdir": workdir,
|
|
|
|
"docker-version": nullDispatch, // we don't care about docker-version
|
|
|
|
"run": run,
|
|
|
|
"cmd": cmd,
|
|
|
|
"entrypoint": entrypoint,
|
|
|
|
"expose": expose,
|
|
|
|
"volume": volume,
|
|
|
|
"user": user,
|
|
|
|
"insert": insert,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
type envMap map[string]string
|
|
|
|
type uniqueMap map[string]struct{}
|
|
|
|
|
2014-08-05 20:17:40 +00:00
|
|
|
type buildFile struct {
|
|
|
|
dockerfile *parser.Node
|
|
|
|
env envMap
|
|
|
|
image string
|
|
|
|
config *runconfig.Config
|
|
|
|
options *BuildOpts
|
|
|
|
maintainer string
|
2014-08-05 22:41:09 +00:00
|
|
|
|
|
|
|
// cmdSet indicates is CMD was set in current Dockerfile
|
|
|
|
cmdSet bool
|
|
|
|
|
|
|
|
context *tarsum.TarSum
|
|
|
|
contextPath string
|
|
|
|
tmpContainers uniqueMap
|
|
|
|
tmpImages uniqueMap
|
2014-08-05 20:17:40 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
type BuildOpts struct {
|
2014-08-05 22:41:09 +00:00
|
|
|
Daemon *daemon.Daemon
|
|
|
|
Engine *engine.Engine
|
|
|
|
OutStream io.Writer
|
|
|
|
ErrStream io.Writer
|
|
|
|
Verbose bool
|
|
|
|
UtilizeCache bool
|
|
|
|
Remove bool
|
|
|
|
ForceRemove bool
|
|
|
|
AuthConfig *registry.AuthConfig
|
|
|
|
AuthConfigFile *registry.ConfigFile
|
|
|
|
|
|
|
|
// Deprecated, original writer used for ImagePull. To be removed.
|
2014-08-05 20:17:40 +00:00
|
|
|
OutOld io.Writer
|
|
|
|
StreamFormatter *utils.StreamFormatter
|
|
|
|
}
|
|
|
|
|
2014-08-05 22:41:09 +00:00
|
|
|
func NewBuilder(opts *BuildOpts) (*buildFile, error) {
|
2014-08-05 20:17:40 +00:00
|
|
|
return &buildFile{
|
2014-08-05 22:41:09 +00:00
|
|
|
dockerfile: nil,
|
|
|
|
env: envMap{},
|
|
|
|
config: initRunConfig(),
|
|
|
|
options: opts,
|
|
|
|
tmpContainers: make(uniqueMap),
|
|
|
|
tmpImages: make(uniqueMap),
|
2014-08-05 20:17:40 +00:00
|
|
|
}, nil
|
|
|
|
}
|
|
|
|
|
2014-08-05 22:41:09 +00:00
|
|
|
func (b *buildFile) Run(context io.Reader) (string, error) {
|
|
|
|
err := b.readContext(context)
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
|
|
|
|
filename := path.Join(b.contextPath, "Dockerfile")
|
|
|
|
if _, err := os.Stat(filename); os.IsNotExist(err) {
|
|
|
|
return "", fmt.Errorf("Cannot build a directory without a Dockerfile")
|
|
|
|
}
|
|
|
|
fileBytes, err := ioutil.ReadFile(filename)
|
|
|
|
if err != nil {
|
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
if len(fileBytes) == 0 {
|
|
|
|
return "", ErrDockerfileEmpty
|
|
|
|
}
|
|
|
|
ast, err := parser.Parse(bytes.NewReader(fileBytes))
|
|
|
|
if err != nil {
|
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
|
|
|
|
b.dockerfile = ast
|
2014-08-05 20:17:40 +00:00
|
|
|
|
2014-08-05 22:41:09 +00:00
|
|
|
for i, n := range b.dockerfile.Children {
|
2014-08-05 20:17:40 +00:00
|
|
|
if err := b.dispatch(i, n); err != nil {
|
2014-08-05 22:41:09 +00:00
|
|
|
if b.options.ForceRemove {
|
|
|
|
b.clearTmp(b.tmpContainers)
|
|
|
|
}
|
|
|
|
return "", err
|
|
|
|
} else if b.options.Remove {
|
|
|
|
b.clearTmp(b.tmpContainers)
|
2014-08-05 20:17:40 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-08-05 22:41:09 +00:00
|
|
|
if b.image == "" {
|
|
|
|
return "", fmt.Errorf("No image was generated. This may be because the Dockerfile does not, like, do anything.\n")
|
|
|
|
}
|
|
|
|
|
|
|
|
fmt.Fprintf(b.options.OutStream, "Successfully built %s\n", utils.TruncateID(b.image))
|
|
|
|
return b.image, nil
|
2014-08-05 20:17:40 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func initRunConfig() *runconfig.Config {
|
|
|
|
return &runconfig.Config{
|
|
|
|
PortSpecs: []string{},
|
|
|
|
// FIXME(erikh) this should be a type that lives in runconfig
|
|
|
|
ExposedPorts: map[nat.Port]struct{}{},
|
|
|
|
Env: []string{},
|
|
|
|
Cmd: []string{},
|
|
|
|
|
|
|
|
// FIXME(erikh) this should also be a type in runconfig
|
|
|
|
Volumes: map[string]struct{}{},
|
2014-08-05 22:41:09 +00:00
|
|
|
Entrypoint: []string{"/bin/sh", "-c"},
|
2014-08-05 20:17:40 +00:00
|
|
|
OnBuild: []string{},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (b *buildFile) dispatch(stepN int, ast *parser.Node) error {
|
|
|
|
cmd := ast.Value
|
|
|
|
strs := []string{}
|
2014-08-05 22:41:09 +00:00
|
|
|
|
|
|
|
if cmd == "onbuild" {
|
|
|
|
fmt.Fprintf(b.options.OutStream, "%#v\n", ast.Next.Children[0].Value)
|
|
|
|
ast = ast.Next.Children[0]
|
|
|
|
strs = append(strs, ast.Value)
|
|
|
|
}
|
|
|
|
|
2014-08-05 20:17:40 +00:00
|
|
|
for ast.Next != nil {
|
|
|
|
ast = ast.Next
|
2014-08-05 22:41:09 +00:00
|
|
|
strs = append(strs, replaceEnv(b, ast.Value))
|
2014-08-05 20:17:40 +00:00
|
|
|
}
|
|
|
|
|
2014-08-05 22:41:09 +00:00
|
|
|
fmt.Fprintf(b.options.OutStream, "Step %d : %s %s\n", stepN, strings.ToUpper(cmd), strings.Join(strs, " "))
|
2014-08-05 20:17:40 +00:00
|
|
|
|
|
|
|
// XXX yes, we skip any cmds that are not valid; the parser should have
|
|
|
|
// picked these out already.
|
|
|
|
if f, ok := evaluateTable[cmd]; ok {
|
2014-08-05 22:41:09 +00:00
|
|
|
return f(b, strs)
|
2014-08-05 20:17:40 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|