1
0
Fork 0
mirror of https://github.com/moby/moby.git synced 2022-11-09 12:21:53 -05:00

builder: fix references to jobs in daemon, make builder a first class

package referring to evaluator

Docker-DCO-1.1-Signed-off-by: Erik Hollensbe <github@hollensbe.org> (github: erikh)
This commit is contained in:
Erik Hollensbe 2014-08-11 08:44:31 -07:00
parent 3dfe5ddfb9
commit 1ae4c00a19
9 changed files with 309 additions and 1184 deletions

34
builder/builder.go Normal file
View file

@ -0,0 +1,34 @@
package builder
import (
"github.com/docker/docker/builder/evaluator"
"github.com/docker/docker/nat"
"github.com/docker/docker/runconfig"
)
// Create a new builder.
func NewBuilder(opts *evaluator.BuildOpts) *evaluator.BuildFile {
return &evaluator.BuildFile{
Dockerfile: nil,
Env: evaluator.EnvMap{},
Config: initRunConfig(),
Options: opts,
TmpContainers: evaluator.UniqueMap{},
TmpImages: evaluator.UniqueMap{},
}
}
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{}{},
Entrypoint: []string{"/bin/sh", "-c"},
OnBuild: []string{},
}
}

View file

@ -18,7 +18,7 @@ import (
)
// dispatch with no layer / parsing. This is effectively not a command.
func nullDispatch(b *buildFile, args []string) error {
func nullDispatch(b *BuildFile, args []string) error {
return nil
}
@ -27,7 +27,7 @@ func nullDispatch(b *buildFile, args []string) error {
// Sets the environment variable foo to bar, also makes interpolation
// in the dockerfile available from the next statement on via ${foo}.
//
func env(b *buildFile, args []string) error {
func env(b *BuildFile, args []string) error {
if len(args) != 2 {
return fmt.Errorf("ENV accepts two arguments")
}
@ -35,22 +35,22 @@ func env(b *buildFile, args []string) error {
// the duplication here is intended to ease the replaceEnv() call's env
// handling. This routine gets much shorter with the denormalization here.
key := args[0]
b.env[key] = args[1]
b.config.Env = append(b.config.Env, strings.Join([]string{key, b.env[key]}, "="))
b.Env[key] = args[1]
b.Config.Env = append(b.Config.Env, strings.Join([]string{key, b.Env[key]}, "="))
return b.commit("", b.config.Cmd, fmt.Sprintf("ENV %s=%s", key, b.env[key]))
return b.commit("", b.Config.Cmd, fmt.Sprintf("ENV %s=%s", key, b.Env[key]))
}
// MAINTAINER some text <maybe@an.email.address>
//
// Sets the maintainer metadata.
func maintainer(b *buildFile, args []string) error {
func maintainer(b *BuildFile, args []string) error {
if len(args) != 1 {
return fmt.Errorf("MAINTAINER requires only one argument")
}
b.maintainer = args[0]
return b.commit("", b.config.Cmd, fmt.Sprintf("MAINTAINER %s", b.maintainer))
return b.commit("", b.Config.Cmd, fmt.Sprintf("MAINTAINER %s", b.maintainer))
}
// ADD foo /path
@ -58,7 +58,7 @@ func maintainer(b *buildFile, args []string) error {
// Add the file 'foo' to '/path'. Tarball and Remote URL (git, http) handling
// exist here. If you do not wish to have this automatic handling, use COPY.
//
func add(b *buildFile, args []string) error {
func add(b *BuildFile, args []string) error {
if len(args) != 2 {
return fmt.Errorf("ADD requires two arguments")
}
@ -70,7 +70,7 @@ func add(b *buildFile, args []string) error {
//
// Same as 'ADD' but without the tar and remote url handling.
//
func dispatchCopy(b *buildFile, args []string) error {
func dispatchCopy(b *BuildFile, args []string) error {
if len(args) != 2 {
return fmt.Errorf("COPY requires two arguments")
}
@ -82,16 +82,16 @@ func dispatchCopy(b *buildFile, args []string) error {
//
// This sets the image the dockerfile will build on top of.
//
func from(b *buildFile, args []string) error {
func from(b *BuildFile, args []string) error {
if len(args) != 1 {
return fmt.Errorf("FROM requires one argument")
}
name := args[0]
image, err := b.options.Daemon.Repositories().LookupImage(name)
image, err := b.Options.Daemon.Repositories().LookupImage(name)
if err != nil {
if b.options.Daemon.Graph().IsNotExist(err) {
if b.Options.Daemon.Graph().IsNotExist(err) {
image, err = b.pullImage(name)
}
@ -114,7 +114,7 @@ func from(b *buildFile, args []string) error {
// special cases. search for 'OnBuild' in internals.go for additional special
// cases.
//
func onbuild(b *buildFile, args []string) error {
func onbuild(b *BuildFile, args []string) error {
triggerInstruction := strings.ToUpper(strings.TrimSpace(args[0]))
switch triggerInstruction {
case "ONBUILD":
@ -125,15 +125,15 @@ func onbuild(b *buildFile, args []string) error {
trigger := strings.Join(args, " ")
b.config.OnBuild = append(b.config.OnBuild, trigger)
return b.commit("", b.config.Cmd, fmt.Sprintf("ONBUILD %s", trigger))
b.Config.OnBuild = append(b.Config.OnBuild, trigger)
return b.commit("", b.Config.Cmd, fmt.Sprintf("ONBUILD %s", trigger))
}
// WORKDIR /tmp
//
// Set the working directory for future RUN/CMD/etc statements.
//
func workdir(b *buildFile, args []string) error {
func workdir(b *BuildFile, args []string) error {
if len(args) != 1 {
return fmt.Errorf("WORKDIR requires exactly one argument")
}
@ -141,15 +141,15 @@ func workdir(b *buildFile, args []string) error {
workdir := args[0]
if workdir[0] == '/' {
b.config.WorkingDir = workdir
b.Config.WorkingDir = workdir
} else {
if b.config.WorkingDir == "" {
b.config.WorkingDir = "/"
if b.Config.WorkingDir == "" {
b.Config.WorkingDir = "/"
}
b.config.WorkingDir = filepath.Join(b.config.WorkingDir, workdir)
b.Config.WorkingDir = filepath.Join(b.Config.WorkingDir, workdir)
}
return b.commit("", b.config.Cmd, fmt.Sprintf("WORKDIR %v", workdir))
return b.commit("", b.Config.Cmd, fmt.Sprintf("WORKDIR %v", workdir))
}
// RUN some command yo
@ -161,7 +161,7 @@ func workdir(b *buildFile, args []string) error {
// RUN echo hi # sh -c echo hi
// RUN [ "echo", "hi" ] # echo hi
//
func run(b *buildFile, args []string) error {
func run(b *BuildFile, args []string) error {
if len(args) == 1 { // literal string command, not an exec array
args = append([]string{"/bin/sh", "-c"}, args[0])
}
@ -175,14 +175,14 @@ func run(b *buildFile, args []string) error {
return err
}
cmd := b.config.Cmd
cmd := b.Config.Cmd
// set Cmd manually, this is special case only for Dockerfiles
b.config.Cmd = config.Cmd
runconfig.Merge(b.config, config)
b.Config.Cmd = config.Cmd
runconfig.Merge(b.Config, config)
defer func(cmd []string) { b.config.Cmd = cmd }(cmd)
defer func(cmd []string) { b.Config.Cmd = cmd }(cmd)
utils.Debugf("Command to be executed: %v", b.config.Cmd)
utils.Debugf("Command to be executed: %v", b.Config.Cmd)
hit, err := b.probeCache()
if err != nil {
@ -217,13 +217,13 @@ func run(b *buildFile, args []string) error {
// Set the default command to run in the container (which may be empty).
// Argument handling is the same as RUN.
//
func cmd(b *buildFile, args []string) error {
func cmd(b *BuildFile, args []string) error {
if len(args) < 2 {
args = append([]string{"/bin/sh", "-c"}, args...)
}
b.config.Cmd = args
if err := b.commit("", b.config.Cmd, fmt.Sprintf("CMD %v", cmd)); err != nil {
b.Config.Cmd = args
if err := b.commit("", b.Config.Cmd, fmt.Sprintf("CMD %v", cmd)); err != nil {
return err
}
@ -236,17 +236,17 @@ func cmd(b *buildFile, args []string) error {
// Set the entrypoint (which defaults to sh -c) to /usr/sbin/nginx. Will
// accept the CMD as the arguments to /usr/sbin/nginx.
//
// Handles command processing similar to CMD and RUN, only b.config.Entrypoint
// Handles command processing similar to CMD and RUN, only b.Config.Entrypoint
// is initialized at NewBuilder time instead of through argument parsing.
//
func entrypoint(b *buildFile, args []string) error {
b.config.Entrypoint = args
func entrypoint(b *BuildFile, args []string) error {
b.Config.Entrypoint = args
// if there is no cmd in current Dockerfile - cleanup cmd
if !b.cmdSet {
b.config.Cmd = nil
b.Config.Cmd = nil
}
if err := b.commit("", b.config.Cmd, fmt.Sprintf("ENTRYPOINT %v", entrypoint)); err != nil {
if err := b.commit("", b.Config.Cmd, fmt.Sprintf("ENTRYPOINT %v", entrypoint)); err != nil {
return err
}
return nil
@ -255,28 +255,28 @@ func entrypoint(b *buildFile, args []string) error {
// EXPOSE 6667/tcp 7000/tcp
//
// Expose ports for links and port mappings. This all ends up in
// b.config.ExposedPorts for runconfig.
// b.Config.ExposedPorts for runconfig.
//
func expose(b *buildFile, args []string) error {
func expose(b *BuildFile, args []string) error {
portsTab := args
if b.config.ExposedPorts == nil {
b.config.ExposedPorts = make(nat.PortSet)
if b.Config.ExposedPorts == nil {
b.Config.ExposedPorts = make(nat.PortSet)
}
ports, _, err := nat.ParsePortSpecs(append(portsTab, b.config.PortSpecs...))
ports, _, err := nat.ParsePortSpecs(append(portsTab, b.Config.PortSpecs...))
if err != nil {
return err
}
for port := range ports {
if _, exists := b.config.ExposedPorts[port]; !exists {
b.config.ExposedPorts[port] = struct{}{}
if _, exists := b.Config.ExposedPorts[port]; !exists {
b.Config.ExposedPorts[port] = struct{}{}
}
}
b.config.PortSpecs = nil
b.Config.PortSpecs = nil
return b.commit("", b.config.Cmd, fmt.Sprintf("EXPOSE %v", ports))
return b.commit("", b.Config.Cmd, fmt.Sprintf("EXPOSE %v", ports))
}
// USER foo
@ -284,13 +284,13 @@ func expose(b *buildFile, args []string) error {
// Set the user to 'foo' for future commands and when running the
// ENTRYPOINT/CMD at container run time.
//
func user(b *buildFile, args []string) error {
func user(b *BuildFile, args []string) error {
if len(args) != 1 {
return fmt.Errorf("USER requires exactly one argument")
}
b.config.User = args[0]
return b.commit("", b.config.Cmd, fmt.Sprintf("USER %v", args))
b.Config.User = args[0]
return b.commit("", b.Config.Cmd, fmt.Sprintf("USER %v", args))
}
// VOLUME /foo
@ -298,26 +298,26 @@ func user(b *buildFile, args []string) error {
// Expose the volume /foo for use. Will also accept the JSON form, but either
// way requires exactly one argument.
//
func volume(b *buildFile, args []string) error {
func volume(b *BuildFile, args []string) error {
if len(args) != 1 {
return fmt.Errorf("Volume cannot be empty")
}
volume := args
if b.config.Volumes == nil {
b.config.Volumes = map[string]struct{}{}
if b.Config.Volumes == nil {
b.Config.Volumes = map[string]struct{}{}
}
for _, v := range volume {
b.config.Volumes[v] = struct{}{}
b.Config.Volumes[v] = struct{}{}
}
if err := b.commit("", b.config.Cmd, fmt.Sprintf("VOLUME %s", args)); err != nil {
if err := b.commit("", b.Config.Cmd, fmt.Sprintf("VOLUME %s", args)); err != nil {
return err
}
return nil
}
// INSERT is no longer accepted, but we still parse it.
func insert(b *buildFile, args []string) error {
func insert(b *BuildFile, args []string) error {
return fmt.Errorf("INSERT has been deprecated. Please use ADD instead")
}

View file

@ -32,21 +32,23 @@ import (
"github.com/docker/docker/builder/parser"
"github.com/docker/docker/daemon"
"github.com/docker/docker/engine"
"github.com/docker/docker/nat"
"github.com/docker/docker/pkg/tarsum"
"github.com/docker/docker/registry"
"github.com/docker/docker/runconfig"
"github.com/docker/docker/utils"
)
type EnvMap map[string]string
type UniqueMap map[string]struct{}
var (
ErrDockerfileEmpty = errors.New("Dockerfile cannot be empty")
)
var evaluateTable map[string]func(*buildFile, []string) error
var evaluateTable map[string]func(*BuildFile, []string) error
func init() {
evaluateTable = map[string]func(*buildFile, []string) error{
evaluateTable = map[string]func(*BuildFile, []string) error{
"env": env,
"maintainer": maintainer,
"add": add,
@ -65,25 +67,24 @@ func init() {
}
}
type envMap map[string]string
type uniqueMap map[string]struct{}
// internal struct, used to maintain configuration of the Dockerfile's
// processing as it evaluates the parsing result.
type buildFile struct {
dockerfile *parser.Node // the syntax tree of the dockerfile
env envMap // map of environment variables
image string // image name for commit processing
config *runconfig.Config // runconfig for cmd, run, entrypoint etc.
options *BuildOpts // see below
maintainer string // maintainer name. could probably be removed.
cmdSet bool // indicates is CMD was set in current Dockerfile
context *tarsum.TarSum // the context is a tarball that is uploaded by the client
contextPath string // the path of the temporary directory the local context is unpacked to (server side)
type BuildFile struct {
Dockerfile *parser.Node // the syntax tree of the dockerfile
Env EnvMap // map of environment variables
Config *runconfig.Config // runconfig for cmd, run, entrypoint etc.
Options *BuildOpts // see below
// both of these are controlled by the Remove and ForceRemove options in BuildOpts
tmpContainers uniqueMap // a map of containers used for removes
tmpImages uniqueMap // a map of images used for removes
TmpContainers UniqueMap // a map of containers used for removes
TmpImages UniqueMap // a map of images used for removes
image string // image name for commit processing
maintainer string // maintainer name. could probably be removed.
cmdSet bool // indicates is CMD was set in current Dockerfile
context *tarsum.TarSum // the context is a tarball that is uploaded by the client
contextPath string // the path of the temporary directory the local context is unpacked to (server side)
}
type BuildOpts struct {
@ -110,18 +111,6 @@ type BuildOpts struct {
StreamFormatter *utils.StreamFormatter
}
// Create a new builder.
func NewBuilder(opts *BuildOpts) (*buildFile, error) {
return &buildFile{
dockerfile: nil,
env: envMap{},
config: initRunConfig(),
options: opts,
tmpContainers: make(uniqueMap),
tmpImages: make(uniqueMap),
}, nil
}
// Run the builder with the context. This is the lynchpin of this package. This
// will (barring errors):
//
@ -134,7 +123,7 @@ func NewBuilder(opts *BuildOpts) (*buildFile, error) {
// processing.
// * Print a happy message and return the image ID.
//
func (b *buildFile) Run(context io.Reader) (string, error) {
func (b *BuildFile) Run(context io.Reader) (string, error) {
if err := b.readContext(context); err != nil {
return "", err
}
@ -155,16 +144,16 @@ func (b *buildFile) Run(context io.Reader) (string, error) {
return "", err
}
b.dockerfile = ast
b.Dockerfile = ast
for i, n := range b.dockerfile.Children {
for i, n := range b.Dockerfile.Children {
if err := b.dispatch(i, n); err != nil {
if b.options.ForceRemove {
b.clearTmp(b.tmpContainers)
if b.Options.ForceRemove {
b.clearTmp(b.TmpContainers)
}
return "", err
} else if b.options.Remove {
b.clearTmp(b.tmpContainers)
} else if b.Options.Remove {
b.clearTmp(b.TmpContainers)
}
}
@ -172,32 +161,17 @@ func (b *buildFile) Run(context io.Reader) (string, error) {
return "", fmt.Errorf("No image was generated. Is your Dockerfile empty?\n")
}
fmt.Fprintf(b.options.OutStream, "Successfully built %s\n", utils.TruncateID(b.image))
fmt.Fprintf(b.Options.OutStream, "Successfully built %s\n", utils.TruncateID(b.image))
return b.image, nil
}
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{}{},
Entrypoint: []string{"/bin/sh", "-c"},
OnBuild: []string{},
}
}
// This method is the entrypoint to all statement handling routines.
//
// Almost all nodes will have this structure:
// Child[Node, Node, Node] where Child is from parser.Node.Children and each
// node comes from parser.Node.Next. This forms a "line" with a statement and
// arguments and we process them in this normalized form by hitting
// evaluateTable with the leaf nodes of the command and the buildFile object.
// evaluateTable with the leaf nodes of the command and the BuildFile object.
//
// ONBUILD is a special case; in this case the parser will emit:
// Child[Node, Child[Node, Node...]] where the first node is the literal
@ -205,12 +179,12 @@ func initRunConfig() *runconfig.Config {
// such as `RUN` in ONBUILD RUN foo. There is special case logic in here to
// deal with that, at least until it becomes more of a general concern with new
// features.
func (b *buildFile) dispatch(stepN int, ast *parser.Node) error {
func (b *BuildFile) dispatch(stepN int, ast *parser.Node) error {
cmd := ast.Value
strs := []string{}
if cmd == "onbuild" {
fmt.Fprintf(b.options.OutStream, "%#v\n", ast.Next.Children[0].Value)
fmt.Fprintf(b.Options.OutStream, "%#v\n", ast.Next.Children[0].Value)
ast = ast.Next.Children[0]
strs = append(strs, ast.Value)
}
@ -220,7 +194,7 @@ func (b *buildFile) dispatch(stepN int, ast *parser.Node) error {
strs = append(strs, replaceEnv(b, ast.Value))
}
fmt.Fprintf(b.options.OutStream, "Step %d : %s %s\n", stepN, strings.ToUpper(cmd), strings.Join(strs, " "))
fmt.Fprintf(b.Options.OutStream, "Step %d : %s %s\n", stepN, strings.ToUpper(cmd), strings.Join(strs, " "))
// XXX yes, we skip any cmds that are not valid; the parser should have
// picked these out already.

View file

@ -30,7 +30,7 @@ import (
"github.com/docker/docker/utils"
)
func (b *buildFile) readContext(context io.Reader) error {
func (b *BuildFile) readContext(context io.Reader) error {
tmpdirPath, err := ioutil.TempDir("", "docker-build")
if err != nil {
return err
@ -50,15 +50,15 @@ func (b *buildFile) readContext(context io.Reader) error {
return nil
}
func (b *buildFile) commit(id string, autoCmd []string, comment string) error {
func (b *BuildFile) commit(id string, autoCmd []string, comment string) error {
if b.image == "" {
return fmt.Errorf("Please provide a source image with `from` prior to commit")
}
b.config.Image = b.image
b.Config.Image = b.image
if id == "" {
cmd := b.config.Cmd
b.config.Cmd = []string{"/bin/sh", "-c", "#(nop) " + comment}
defer func(cmd []string) { b.config.Cmd = cmd }(cmd)
cmd := b.Config.Cmd
b.Config.Cmd = []string{"/bin/sh", "-c", "#(nop) " + comment}
defer func(cmd []string) { b.Config.Cmd = cmd }(cmd)
hit, err := b.probeCache()
if err != nil {
@ -68,15 +68,15 @@ func (b *buildFile) commit(id string, autoCmd []string, comment string) error {
return nil
}
container, warnings, err := b.options.Daemon.Create(b.config, "")
container, warnings, err := b.Options.Daemon.Create(b.Config, "")
if err != nil {
return err
}
for _, warning := range warnings {
fmt.Fprintf(b.options.OutStream, " ---> [Warning] %s\n", warning)
fmt.Fprintf(b.Options.OutStream, " ---> [Warning] %s\n", warning)
}
b.tmpContainers[container.ID] = struct{}{}
fmt.Fprintf(b.options.OutStream, " ---> Running in %s\n", utils.TruncateID(container.ID))
b.TmpContainers[container.ID] = struct{}{}
fmt.Fprintf(b.Options.OutStream, " ---> Running in %s\n", utils.TruncateID(container.ID))
id = container.ID
if err := container.Mount(); err != nil {
@ -84,25 +84,25 @@ func (b *buildFile) commit(id string, autoCmd []string, comment string) error {
}
defer container.Unmount()
}
container := b.options.Daemon.Get(id)
container := b.Options.Daemon.Get(id)
if container == nil {
return fmt.Errorf("An error occured while creating the container")
}
// Note: Actually copy the struct
autoConfig := *b.config
autoConfig := *b.Config
autoConfig.Cmd = autoCmd
// Commit the container
image, err := b.options.Daemon.Commit(container, "", "", "", b.maintainer, true, &autoConfig)
image, err := b.Options.Daemon.Commit(container, "", "", "", b.maintainer, true, &autoConfig)
if err != nil {
return err
}
b.tmpImages[image.ID] = struct{}{}
b.TmpImages[image.ID] = struct{}{}
b.image = image.ID
return nil
}
func (b *buildFile) runContextCommand(args []string, allowRemote bool, allowDecompression bool, cmdName string) error {
func (b *BuildFile) runContextCommand(args []string, allowRemote bool, allowDecompression bool, cmdName string) error {
if b.context == nil {
return fmt.Errorf("No context given. Impossible to use %s", cmdName)
}
@ -114,10 +114,10 @@ func (b *buildFile) runContextCommand(args []string, allowRemote bool, allowDeco
orig := args[0]
dest := args[1]
cmd := b.config.Cmd
b.config.Cmd = []string{"/bin/sh", "-c", fmt.Sprintf("#(nop) %s %s in %s", cmdName, orig, dest)}
defer func(cmd []string) { b.config.Cmd = cmd }(cmd)
b.config.Image = b.image
cmd := b.Config.Cmd
b.Config.Cmd = []string{"/bin/sh", "-c", fmt.Sprintf("#(nop) %s %s in %s", cmdName, orig, dest)}
defer func(cmd []string) { b.Config.Cmd = cmd }(cmd)
b.Config.Image = b.image
var (
origPath = orig
@ -201,7 +201,7 @@ func (b *buildFile) runContextCommand(args []string, allowRemote bool, allowDeco
}
// Hash path and check the cache
if b.options.UtilizeCache {
if b.Options.UtilizeCache {
var (
hash string
sums = b.context.GetSums()
@ -233,7 +233,7 @@ func (b *buildFile) runContextCommand(args []string, allowRemote bool, allowDeco
hash = "file:" + h
}
}
b.config.Cmd = []string{"/bin/sh", "-c", fmt.Sprintf("#(nop) %s %s in %s", cmdName, hash, dest)}
b.Config.Cmd = []string{"/bin/sh", "-c", fmt.Sprintf("#(nop) %s %s in %s", cmdName, hash, dest)}
hit, err := b.probeCache()
if err != nil {
return err
@ -245,11 +245,11 @@ func (b *buildFile) runContextCommand(args []string, allowRemote bool, allowDeco
}
// Create the container
container, _, err := b.options.Daemon.Create(b.config, "")
container, _, err := b.Options.Daemon.Create(b.Config, "")
if err != nil {
return err
}
b.tmpContainers[container.ID] = struct{}{}
b.TmpContainers[container.ID] = struct{}{}
if err := container.Mount(); err != nil {
return err
@ -269,27 +269,27 @@ func (b *buildFile) runContextCommand(args []string, allowRemote bool, allowDeco
return nil
}
func (b *buildFile) pullImage(name string) (*imagepkg.Image, error) {
func (b *BuildFile) pullImage(name string) (*imagepkg.Image, error) {
remote, tag := parsers.ParseRepositoryTag(name)
pullRegistryAuth := b.options.AuthConfig
if len(b.options.AuthConfigFile.Configs) > 0 {
pullRegistryAuth := b.Options.AuthConfig
if len(b.Options.AuthConfigFile.Configs) > 0 {
// The request came with a full auth config file, we prefer to use that
endpoint, _, err := registry.ResolveRepositoryName(remote)
if err != nil {
return nil, err
}
resolvedAuth := b.options.AuthConfigFile.ResolveAuthConfig(endpoint)
resolvedAuth := b.Options.AuthConfigFile.ResolveAuthConfig(endpoint)
pullRegistryAuth = &resolvedAuth
}
job := b.options.Engine.Job("pull", remote, tag)
job.SetenvBool("json", b.options.StreamFormatter.Json())
job := b.Options.Engine.Job("pull", remote, tag)
job.SetenvBool("json", b.Options.StreamFormatter.Json())
job.SetenvBool("parallel", true)
job.SetenvJson("authConfig", pullRegistryAuth)
job.Stdout.Add(b.options.OutOld)
job.Stdout.Add(b.Options.OutOld)
if err := job.Run(); err != nil {
return nil, err
}
image, err := b.options.Daemon.Repositories().LookupImage(name)
image, err := b.Options.Daemon.Repositories().LookupImage(name)
if err != nil {
return nil, err
}
@ -297,23 +297,23 @@ func (b *buildFile) pullImage(name string) (*imagepkg.Image, error) {
return image, nil
}
func (b *buildFile) processImageFrom(img *imagepkg.Image) error {
func (b *BuildFile) processImageFrom(img *imagepkg.Image) error {
b.image = img.ID
b.config = &runconfig.Config{}
b.Config = &runconfig.Config{}
if img.Config != nil {
b.config = img.Config
b.Config = img.Config
}
if b.config.Env == nil || len(b.config.Env) == 0 {
b.config.Env = append(b.config.Env, "PATH="+daemon.DefaultPathEnv)
if b.Config.Env == nil || len(b.Config.Env) == 0 {
b.Config.Env = append(b.Config.Env, "PATH="+daemon.DefaultPathEnv)
}
// Process ONBUILD triggers if they exist
if nTriggers := len(b.config.OnBuild); nTriggers != 0 {
fmt.Fprintf(b.options.ErrStream, "# Executing %d build triggers\n", nTriggers)
if nTriggers := len(b.Config.OnBuild); nTriggers != 0 {
fmt.Fprintf(b.Options.ErrStream, "# Executing %d build triggers\n", nTriggers)
}
// Copy the ONBUILD triggers, and remove them from the config, since the config will be commited.
onBuildTriggers := b.config.OnBuild
b.config.OnBuild = []string{}
onBuildTriggers := b.Config.OnBuild
b.Config.OnBuild = []string{}
// FIXME rewrite this so that builder/parser is used; right now steps in
// onbuild are muted because we have no good way to represent the step
@ -343,17 +343,17 @@ func (b *buildFile) processImageFrom(img *imagepkg.Image) error {
return nil
}
// probeCache checks to see if image-caching is enabled (`b.options.UtilizeCache`)
// and if so attempts to look up the current `b.image` and `b.config` pair
// in the current server `b.options.Daemon`. If an image is found, probeCache returns
// probeCache checks to see if image-caching is enabled (`b.Options.UtilizeCache`)
// and if so attempts to look up the current `b.image` and `b.Config` pair
// in the current server `b.Options.Daemon`. If an image is found, probeCache returns
// `(true, nil)`. If no image is found, it returns `(false, nil)`. If there
// is any error, it returns `(false, err)`.
func (b *buildFile) probeCache() (bool, error) {
if b.options.UtilizeCache {
if cache, err := b.options.Daemon.ImageGetCached(b.image, b.config); err != nil {
func (b *BuildFile) probeCache() (bool, error) {
if b.Options.UtilizeCache {
if cache, err := b.Options.Daemon.ImageGetCached(b.image, b.Config); err != nil {
return false, err
} else if cache != nil {
fmt.Fprintf(b.options.OutStream, " ---> Using cache\n")
fmt.Fprintf(b.Options.OutStream, " ---> Using cache\n")
utils.Debugf("[BUILDER] Use cached version")
b.image = cache.ID
return true, nil
@ -364,37 +364,37 @@ func (b *buildFile) probeCache() (bool, error) {
return false, nil
}
func (b *buildFile) create() (*daemon.Container, error) {
func (b *BuildFile) create() (*daemon.Container, error) {
if b.image == "" {
return nil, fmt.Errorf("Please provide a source image with `from` prior to run")
}
b.config.Image = b.image
b.Config.Image = b.image
// Create the container
c, _, err := b.options.Daemon.Create(b.config, "")
c, _, err := b.Options.Daemon.Create(b.Config, "")
if err != nil {
return nil, err
}
b.tmpContainers[c.ID] = struct{}{}
fmt.Fprintf(b.options.OutStream, " ---> Running in %s\n", utils.TruncateID(c.ID))
b.TmpContainers[c.ID] = struct{}{}
fmt.Fprintf(b.Options.OutStream, " ---> Running in %s\n", utils.TruncateID(c.ID))
// override the entry point that may have been picked up from the base image
c.Path = b.config.Cmd[0]
c.Args = b.config.Cmd[1:]
c.Path = b.Config.Cmd[0]
c.Args = b.Config.Cmd[1:]
return c, nil
}
func (b *buildFile) run(c *daemon.Container) error {
func (b *BuildFile) run(c *daemon.Container) error {
var errCh chan error
if b.options.Verbose {
if b.Options.Verbose {
errCh = utils.Go(func() error {
// FIXME: call the 'attach' job so that daemon.Attach can be made private
//
// FIXME (LK4D4): Also, maybe makes sense to call "logs" job, it is like attach
// but without hijacking for stdin. Also, with attach there can be race
// condition because of some output already was printed before it.
return <-b.options.Daemon.Attach(c, nil, nil, b.options.OutStream, b.options.ErrStream)
return <-b.Options.Daemon.Attach(c, nil, nil, b.Options.OutStream, b.Options.ErrStream)
})
}
@ -412,7 +412,7 @@ func (b *buildFile) run(c *daemon.Container) error {
// Wait for it to finish
if ret, _ := c.State.WaitStop(-1 * time.Second); ret != 0 {
err := &utils.JSONError{
Message: fmt.Sprintf("The command %v returned a non-zero code: %d", b.config.Cmd, ret),
Message: fmt.Sprintf("The command %v returned a non-zero code: %d", b.Config.Cmd, ret),
Code: ret,
}
return err
@ -421,7 +421,7 @@ func (b *buildFile) run(c *daemon.Container) error {
return nil
}
func (b *buildFile) checkPathForAddition(orig string) error {
func (b *BuildFile) checkPathForAddition(orig string) error {
origPath := path.Join(b.contextPath, orig)
if p, err := filepath.EvalSymlinks(origPath); err != nil {
if os.IsNotExist(err) {
@ -444,7 +444,7 @@ func (b *buildFile) checkPathForAddition(orig string) error {
return nil
}
func (b *buildFile) addContext(container *daemon.Container, orig, dest string, decompress bool) error {
func (b *BuildFile) addContext(container *daemon.Container, orig, dest string, decompress bool) error {
var (
err error
destExists = true
@ -549,14 +549,14 @@ func fixPermissions(destination string, uid, gid int) error {
})
}
func (b *buildFile) clearTmp(containers map[string]struct{}) {
func (b *BuildFile) clearTmp(containers map[string]struct{}) {
for c := range containers {
tmp := b.options.Daemon.Get(c)
if err := b.options.Daemon.Destroy(tmp); err != nil {
fmt.Fprintf(b.options.OutStream, "Error removing intermediate container %s: %s\n", utils.TruncateID(c), err.Error())
tmp := b.Options.Daemon.Get(c)
if err := b.Options.Daemon.Destroy(tmp); err != nil {
fmt.Fprintf(b.Options.OutStream, "Error removing intermediate container %s: %s\n", utils.TruncateID(c), err.Error())
} else {
delete(containers, c)
fmt.Fprintf(b.options.OutStream, "Removing intermediate container %s\n", utils.TruncateID(c))
fmt.Fprintf(b.Options.OutStream, "Removing intermediate container %s\n", utils.TruncateID(c))
}
}
}

View file

@ -10,12 +10,12 @@ var (
)
// handle environment replacement. Used in dispatcher.
func replaceEnv(b *buildFile, str string) string {
func replaceEnv(b *BuildFile, str string) string {
for _, match := range TOKEN_ENV_INTERPOLATION.FindAllString(str, -1) {
match = match[strings.Index(match, "$"):]
matchKey := strings.Trim(match, "${}")
for envKey, envValue := range b.env {
for envKey, envValue := range b.Env {
if matchKey == envKey {
str = strings.Replace(str, match, envValue, -1)
}

119
builder/job.go Normal file
View file

@ -0,0 +1,119 @@
package builder
import (
"io"
"io/ioutil"
"os"
"os/exec"
"strings"
"github.com/docker/docker/archive"
"github.com/docker/docker/builder/evaluator"
"github.com/docker/docker/daemon"
"github.com/docker/docker/engine"
"github.com/docker/docker/pkg/parsers"
"github.com/docker/docker/registry"
"github.com/docker/docker/utils"
)
type BuilderJob struct {
Engine *engine.Engine
Daemon *daemon.Daemon
}
func (b *BuilderJob) Install() {
b.Engine.Register("build", b.CmdBuild)
}
func (b *BuilderJob) CmdBuild(job *engine.Job) engine.Status {
if len(job.Args) != 0 {
return job.Errorf("Usage: %s\n", job.Name)
}
var (
remoteURL = job.Getenv("remote")
repoName = job.Getenv("t")
suppressOutput = job.GetenvBool("q")
noCache = job.GetenvBool("nocache")
rm = job.GetenvBool("rm")
forceRm = job.GetenvBool("forcerm")
authConfig = &registry.AuthConfig{}
configFile = &registry.ConfigFile{}
tag string
context io.ReadCloser
)
job.GetenvJson("authConfig", authConfig)
job.GetenvJson("configFile", configFile)
repoName, tag = parsers.ParseRepositoryTag(repoName)
if remoteURL == "" {
context = ioutil.NopCloser(job.Stdin)
} else if utils.IsGIT(remoteURL) {
if !strings.HasPrefix(remoteURL, "git://") {
remoteURL = "https://" + remoteURL
}
root, err := ioutil.TempDir("", "docker-build-git")
if err != nil {
return job.Error(err)
}
defer os.RemoveAll(root)
if output, err := exec.Command("git", "clone", "--recursive", remoteURL, root).CombinedOutput(); err != nil {
return job.Errorf("Error trying to use git: %s (%s)", err, output)
}
c, err := archive.Tar(root, archive.Uncompressed)
if err != nil {
return job.Error(err)
}
context = c
} else if utils.IsURL(remoteURL) {
f, err := utils.Download(remoteURL)
if err != nil {
return job.Error(err)
}
defer f.Body.Close()
dockerFile, err := ioutil.ReadAll(f.Body)
if err != nil {
return job.Error(err)
}
c, err := archive.Generate("Dockerfile", string(dockerFile))
if err != nil {
return job.Error(err)
}
context = c
}
defer context.Close()
sf := utils.NewStreamFormatter(job.GetenvBool("json"))
opts := &evaluator.BuildOpts{
Daemon: b.Daemon,
Engine: b.Engine,
OutStream: &utils.StdoutFormater{
Writer: job.Stdout,
StreamFormatter: sf,
},
ErrStream: &utils.StderrFormater{
Writer: job.Stdout,
StreamFormatter: sf,
},
Verbose: !suppressOutput,
UtilizeCache: !noCache,
Remove: rm,
ForceRemove: forceRm,
OutOld: job.Stdout,
StreamFormatter: sf,
AuthConfig: authConfig,
AuthConfigFile: configFile,
}
id, err := NewBuilder(opts).Run(context)
if err != nil {
return job.Error(err)
}
if repoName != "" {
b.Daemon.Repositories().Set(repoName, tag, id, false)
}
return engine.StatusOK
}

File diff suppressed because it is too large Load diff

View file

@ -101,7 +101,6 @@ func (daemon *Daemon) Install(eng *engine.Engine) error {
// FIXME: remove ImageDelete's dependency on Daemon, then move to graph/
for name, method := range map[string]engine.Handler{
"attach": daemon.ContainerAttach,
"build": daemon.CmdBuild,
"commit": daemon.ContainerCommit,
"container_changes": daemon.ContainerChanges,
"container_copy": daemon.ContainerCopy,

View file

@ -5,6 +5,7 @@ package main
import (
"log"
"github.com/docker/docker/builder"
"github.com/docker/docker/builtins"
"github.com/docker/docker/daemon"
_ "github.com/docker/docker/daemon/execdriver/lxc"
@ -48,6 +49,10 @@ func mainDaemon() {
if err := d.Install(eng); err != nil {
log.Fatal(err)
}
b := &builder.BuilderJob{eng, d}
b.Install()
// after the daemon is done setting up we can tell the api to start
// accepting connections
if err := eng.Job("acceptconnections").Run(); err != nil {