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:
parent
3dfe5ddfb9
commit
1ae4c00a19
9 changed files with 309 additions and 1184 deletions
34
builder/builder.go
Normal file
34
builder/builder.go
Normal 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{},
|
||||||
|
}
|
||||||
|
}
|
|
@ -18,7 +18,7 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
// dispatch with no layer / parsing. This is effectively not a command.
|
// 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
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -27,7 +27,7 @@ func nullDispatch(b *buildFile, args []string) error {
|
||||||
// Sets the environment variable foo to bar, also makes interpolation
|
// Sets the environment variable foo to bar, also makes interpolation
|
||||||
// in the dockerfile available from the next statement on via ${foo}.
|
// 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 {
|
if len(args) != 2 {
|
||||||
return fmt.Errorf("ENV accepts two arguments")
|
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
|
// the duplication here is intended to ease the replaceEnv() call's env
|
||||||
// handling. This routine gets much shorter with the denormalization here.
|
// handling. This routine gets much shorter with the denormalization here.
|
||||||
key := args[0]
|
key := args[0]
|
||||||
b.env[key] = args[1]
|
b.Env[key] = args[1]
|
||||||
b.config.Env = append(b.config.Env, strings.Join([]string{key, b.env[key]}, "="))
|
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>
|
// MAINTAINER some text <maybe@an.email.address>
|
||||||
//
|
//
|
||||||
// Sets the maintainer metadata.
|
// Sets the maintainer metadata.
|
||||||
func maintainer(b *buildFile, args []string) error {
|
func maintainer(b *BuildFile, args []string) error {
|
||||||
if len(args) != 1 {
|
if len(args) != 1 {
|
||||||
return fmt.Errorf("MAINTAINER requires only one argument")
|
return fmt.Errorf("MAINTAINER requires only one argument")
|
||||||
}
|
}
|
||||||
|
|
||||||
b.maintainer = args[0]
|
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
|
// 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
|
// 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.
|
// 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 {
|
if len(args) != 2 {
|
||||||
return fmt.Errorf("ADD requires two arguments")
|
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.
|
// 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 {
|
if len(args) != 2 {
|
||||||
return fmt.Errorf("COPY requires two arguments")
|
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.
|
// 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 {
|
if len(args) != 1 {
|
||||||
return fmt.Errorf("FROM requires one argument")
|
return fmt.Errorf("FROM requires one argument")
|
||||||
}
|
}
|
||||||
|
|
||||||
name := args[0]
|
name := args[0]
|
||||||
|
|
||||||
image, err := b.options.Daemon.Repositories().LookupImage(name)
|
image, err := b.Options.Daemon.Repositories().LookupImage(name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if b.options.Daemon.Graph().IsNotExist(err) {
|
if b.Options.Daemon.Graph().IsNotExist(err) {
|
||||||
image, err = b.pullImage(name)
|
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
|
// special cases. search for 'OnBuild' in internals.go for additional special
|
||||||
// cases.
|
// cases.
|
||||||
//
|
//
|
||||||
func onbuild(b *buildFile, args []string) error {
|
func onbuild(b *BuildFile, args []string) error {
|
||||||
triggerInstruction := strings.ToUpper(strings.TrimSpace(args[0]))
|
triggerInstruction := strings.ToUpper(strings.TrimSpace(args[0]))
|
||||||
switch triggerInstruction {
|
switch triggerInstruction {
|
||||||
case "ONBUILD":
|
case "ONBUILD":
|
||||||
|
@ -125,15 +125,15 @@ func onbuild(b *buildFile, args []string) error {
|
||||||
|
|
||||||
trigger := strings.Join(args, " ")
|
trigger := strings.Join(args, " ")
|
||||||
|
|
||||||
b.config.OnBuild = append(b.config.OnBuild, trigger)
|
b.Config.OnBuild = append(b.Config.OnBuild, trigger)
|
||||||
return b.commit("", b.config.Cmd, fmt.Sprintf("ONBUILD %s", trigger))
|
return b.commit("", b.Config.Cmd, fmt.Sprintf("ONBUILD %s", trigger))
|
||||||
}
|
}
|
||||||
|
|
||||||
// WORKDIR /tmp
|
// WORKDIR /tmp
|
||||||
//
|
//
|
||||||
// Set the working directory for future RUN/CMD/etc statements.
|
// 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 {
|
if len(args) != 1 {
|
||||||
return fmt.Errorf("WORKDIR requires exactly one argument")
|
return fmt.Errorf("WORKDIR requires exactly one argument")
|
||||||
}
|
}
|
||||||
|
@ -141,15 +141,15 @@ func workdir(b *buildFile, args []string) error {
|
||||||
workdir := args[0]
|
workdir := args[0]
|
||||||
|
|
||||||
if workdir[0] == '/' {
|
if workdir[0] == '/' {
|
||||||
b.config.WorkingDir = workdir
|
b.Config.WorkingDir = workdir
|
||||||
} else {
|
} else {
|
||||||
if b.config.WorkingDir == "" {
|
if b.Config.WorkingDir == "" {
|
||||||
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
|
// 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 # sh -c echo hi
|
||||||
// RUN [ "echo", "hi" ] # 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
|
if len(args) == 1 { // literal string command, not an exec array
|
||||||
args = append([]string{"/bin/sh", "-c"}, args[0])
|
args = append([]string{"/bin/sh", "-c"}, args[0])
|
||||||
}
|
}
|
||||||
|
@ -175,14 +175,14 @@ func run(b *buildFile, args []string) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
cmd := b.config.Cmd
|
cmd := b.Config.Cmd
|
||||||
// set Cmd manually, this is special case only for Dockerfiles
|
// set Cmd manually, this is special case only for Dockerfiles
|
||||||
b.config.Cmd = config.Cmd
|
b.Config.Cmd = config.Cmd
|
||||||
runconfig.Merge(b.config, config)
|
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()
|
hit, err := b.probeCache()
|
||||||
if err != nil {
|
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).
|
// Set the default command to run in the container (which may be empty).
|
||||||
// Argument handling is the same as RUN.
|
// 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 {
|
if len(args) < 2 {
|
||||||
args = append([]string{"/bin/sh", "-c"}, args...)
|
args = append([]string{"/bin/sh", "-c"}, args...)
|
||||||
}
|
}
|
||||||
|
|
||||||
b.config.Cmd = args
|
b.Config.Cmd = args
|
||||||
if err := b.commit("", b.config.Cmd, fmt.Sprintf("CMD %v", cmd)); err != nil {
|
if err := b.commit("", b.Config.Cmd, fmt.Sprintf("CMD %v", cmd)); err != nil {
|
||||||
return err
|
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
|
// Set the entrypoint (which defaults to sh -c) to /usr/sbin/nginx. Will
|
||||||
// accept the CMD as the arguments to /usr/sbin/nginx.
|
// 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.
|
// is initialized at NewBuilder time instead of through argument parsing.
|
||||||
//
|
//
|
||||||
func entrypoint(b *buildFile, args []string) error {
|
func entrypoint(b *BuildFile, args []string) error {
|
||||||
b.config.Entrypoint = args
|
b.Config.Entrypoint = args
|
||||||
|
|
||||||
// if there is no cmd in current Dockerfile - cleanup cmd
|
// if there is no cmd in current Dockerfile - cleanup cmd
|
||||||
if !b.cmdSet {
|
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 err
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
|
@ -255,28 +255,28 @@ func entrypoint(b *buildFile, args []string) error {
|
||||||
// EXPOSE 6667/tcp 7000/tcp
|
// EXPOSE 6667/tcp 7000/tcp
|
||||||
//
|
//
|
||||||
// Expose ports for links and port mappings. This all ends up in
|
// 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
|
portsTab := args
|
||||||
|
|
||||||
if b.config.ExposedPorts == nil {
|
if b.Config.ExposedPorts == nil {
|
||||||
b.config.ExposedPorts = make(nat.PortSet)
|
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 {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
for port := range ports {
|
for port := range ports {
|
||||||
if _, exists := b.config.ExposedPorts[port]; !exists {
|
if _, exists := b.Config.ExposedPorts[port]; !exists {
|
||||||
b.config.ExposedPorts[port] = struct{}{}
|
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
|
// 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
|
// Set the user to 'foo' for future commands and when running the
|
||||||
// ENTRYPOINT/CMD at container run time.
|
// ENTRYPOINT/CMD at container run time.
|
||||||
//
|
//
|
||||||
func user(b *buildFile, args []string) error {
|
func user(b *BuildFile, args []string) error {
|
||||||
if len(args) != 1 {
|
if len(args) != 1 {
|
||||||
return fmt.Errorf("USER requires exactly one argument")
|
return fmt.Errorf("USER requires exactly one argument")
|
||||||
}
|
}
|
||||||
|
|
||||||
b.config.User = args[0]
|
b.Config.User = args[0]
|
||||||
return b.commit("", b.config.Cmd, fmt.Sprintf("USER %v", args))
|
return b.commit("", b.Config.Cmd, fmt.Sprintf("USER %v", args))
|
||||||
}
|
}
|
||||||
|
|
||||||
// VOLUME /foo
|
// 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
|
// Expose the volume /foo for use. Will also accept the JSON form, but either
|
||||||
// way requires exactly one argument.
|
// way requires exactly one argument.
|
||||||
//
|
//
|
||||||
func volume(b *buildFile, args []string) error {
|
func volume(b *BuildFile, args []string) error {
|
||||||
if len(args) != 1 {
|
if len(args) != 1 {
|
||||||
return fmt.Errorf("Volume cannot be empty")
|
return fmt.Errorf("Volume cannot be empty")
|
||||||
}
|
}
|
||||||
|
|
||||||
volume := args
|
volume := args
|
||||||
|
|
||||||
if b.config.Volumes == nil {
|
if b.Config.Volumes == nil {
|
||||||
b.config.Volumes = map[string]struct{}{}
|
b.Config.Volumes = map[string]struct{}{}
|
||||||
}
|
}
|
||||||
for _, v := range volume {
|
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 err
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// INSERT is no longer accepted, but we still parse it.
|
// 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")
|
return fmt.Errorf("INSERT has been deprecated. Please use ADD instead")
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,21 +32,23 @@ import (
|
||||||
"github.com/docker/docker/builder/parser"
|
"github.com/docker/docker/builder/parser"
|
||||||
"github.com/docker/docker/daemon"
|
"github.com/docker/docker/daemon"
|
||||||
"github.com/docker/docker/engine"
|
"github.com/docker/docker/engine"
|
||||||
"github.com/docker/docker/nat"
|
|
||||||
"github.com/docker/docker/pkg/tarsum"
|
"github.com/docker/docker/pkg/tarsum"
|
||||||
"github.com/docker/docker/registry"
|
"github.com/docker/docker/registry"
|
||||||
"github.com/docker/docker/runconfig"
|
"github.com/docker/docker/runconfig"
|
||||||
"github.com/docker/docker/utils"
|
"github.com/docker/docker/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type EnvMap map[string]string
|
||||||
|
type UniqueMap map[string]struct{}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
ErrDockerfileEmpty = errors.New("Dockerfile cannot be empty")
|
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() {
|
func init() {
|
||||||
evaluateTable = map[string]func(*buildFile, []string) error{
|
evaluateTable = map[string]func(*BuildFile, []string) error{
|
||||||
"env": env,
|
"env": env,
|
||||||
"maintainer": maintainer,
|
"maintainer": maintainer,
|
||||||
"add": add,
|
"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
|
// internal struct, used to maintain configuration of the Dockerfile's
|
||||||
// processing as it evaluates the parsing result.
|
// processing as it evaluates the parsing result.
|
||||||
type buildFile struct {
|
type BuildFile struct {
|
||||||
dockerfile *parser.Node // the syntax tree of the dockerfile
|
Dockerfile *parser.Node // the syntax tree of the dockerfile
|
||||||
env envMap // map of environment variables
|
Env EnvMap // map of environment variables
|
||||||
image string // image name for commit processing
|
Config *runconfig.Config // runconfig for cmd, run, entrypoint etc.
|
||||||
config *runconfig.Config // runconfig for cmd, run, entrypoint etc.
|
Options *BuildOpts // see below
|
||||||
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)
|
|
||||||
|
|
||||||
// both of these are controlled by the Remove and ForceRemove options in BuildOpts
|
// both of these are controlled by the Remove and ForceRemove options in BuildOpts
|
||||||
tmpContainers uniqueMap // a map of containers used for removes
|
TmpContainers UniqueMap // a map of containers used for removes
|
||||||
tmpImages uniqueMap // a map of images 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 {
|
type BuildOpts struct {
|
||||||
|
@ -110,18 +111,6 @@ type BuildOpts struct {
|
||||||
StreamFormatter *utils.StreamFormatter
|
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
|
// Run the builder with the context. This is the lynchpin of this package. This
|
||||||
// will (barring errors):
|
// will (barring errors):
|
||||||
//
|
//
|
||||||
|
@ -134,7 +123,7 @@ func NewBuilder(opts *BuildOpts) (*buildFile, error) {
|
||||||
// processing.
|
// processing.
|
||||||
// * Print a happy message and return the image ID.
|
// * 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 {
|
if err := b.readContext(context); err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
@ -155,16 +144,16 @@ func (b *buildFile) Run(context io.Reader) (string, error) {
|
||||||
return "", err
|
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 err := b.dispatch(i, n); err != nil {
|
||||||
if b.options.ForceRemove {
|
if b.Options.ForceRemove {
|
||||||
b.clearTmp(b.tmpContainers)
|
b.clearTmp(b.TmpContainers)
|
||||||
}
|
}
|
||||||
return "", err
|
return "", err
|
||||||
} else if b.options.Remove {
|
} else if b.Options.Remove {
|
||||||
b.clearTmp(b.tmpContainers)
|
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")
|
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
|
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.
|
// This method is the entrypoint to all statement handling routines.
|
||||||
//
|
//
|
||||||
// Almost all nodes will have this structure:
|
// Almost all nodes will have this structure:
|
||||||
// Child[Node, Node, Node] where Child is from parser.Node.Children and each
|
// 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
|
// 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
|
// 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:
|
// ONBUILD is a special case; in this case the parser will emit:
|
||||||
// Child[Node, Child[Node, Node...]] where the first node is the literal
|
// 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
|
// 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
|
// deal with that, at least until it becomes more of a general concern with new
|
||||||
// features.
|
// features.
|
||||||
func (b *buildFile) dispatch(stepN int, ast *parser.Node) error {
|
func (b *BuildFile) dispatch(stepN int, ast *parser.Node) error {
|
||||||
cmd := ast.Value
|
cmd := ast.Value
|
||||||
strs := []string{}
|
strs := []string{}
|
||||||
|
|
||||||
if cmd == "onbuild" {
|
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]
|
ast = ast.Next.Children[0]
|
||||||
strs = append(strs, ast.Value)
|
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))
|
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
|
// XXX yes, we skip any cmds that are not valid; the parser should have
|
||||||
// picked these out already.
|
// picked these out already.
|
||||||
|
|
|
@ -30,7 +30,7 @@ import (
|
||||||
"github.com/docker/docker/utils"
|
"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")
|
tmpdirPath, err := ioutil.TempDir("", "docker-build")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -50,15 +50,15 @@ func (b *buildFile) readContext(context io.Reader) error {
|
||||||
return nil
|
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 == "" {
|
if b.image == "" {
|
||||||
return fmt.Errorf("Please provide a source image with `from` prior to commit")
|
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 == "" {
|
if id == "" {
|
||||||
cmd := b.config.Cmd
|
cmd := b.Config.Cmd
|
||||||
b.config.Cmd = []string{"/bin/sh", "-c", "#(nop) " + comment}
|
b.Config.Cmd = []string{"/bin/sh", "-c", "#(nop) " + comment}
|
||||||
defer func(cmd []string) { b.config.Cmd = cmd }(cmd)
|
defer func(cmd []string) { b.Config.Cmd = cmd }(cmd)
|
||||||
|
|
||||||
hit, err := b.probeCache()
|
hit, err := b.probeCache()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -68,15 +68,15 @@ func (b *buildFile) commit(id string, autoCmd []string, comment string) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
container, warnings, err := b.options.Daemon.Create(b.config, "")
|
container, warnings, err := b.Options.Daemon.Create(b.Config, "")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
for _, warning := range warnings {
|
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{}{}
|
b.TmpContainers[container.ID] = struct{}{}
|
||||||
fmt.Fprintf(b.options.OutStream, " ---> Running in %s\n", utils.TruncateID(container.ID))
|
fmt.Fprintf(b.Options.OutStream, " ---> Running in %s\n", utils.TruncateID(container.ID))
|
||||||
id = container.ID
|
id = container.ID
|
||||||
|
|
||||||
if err := container.Mount(); err != nil {
|
if err := container.Mount(); err != nil {
|
||||||
|
@ -84,25 +84,25 @@ func (b *buildFile) commit(id string, autoCmd []string, comment string) error {
|
||||||
}
|
}
|
||||||
defer container.Unmount()
|
defer container.Unmount()
|
||||||
}
|
}
|
||||||
container := b.options.Daemon.Get(id)
|
container := b.Options.Daemon.Get(id)
|
||||||
if container == nil {
|
if container == nil {
|
||||||
return fmt.Errorf("An error occured while creating the container")
|
return fmt.Errorf("An error occured while creating the container")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Note: Actually copy the struct
|
// Note: Actually copy the struct
|
||||||
autoConfig := *b.config
|
autoConfig := *b.Config
|
||||||
autoConfig.Cmd = autoCmd
|
autoConfig.Cmd = autoCmd
|
||||||
// Commit the container
|
// 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 {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
b.tmpImages[image.ID] = struct{}{}
|
b.TmpImages[image.ID] = struct{}{}
|
||||||
b.image = image.ID
|
b.image = image.ID
|
||||||
return nil
|
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 {
|
if b.context == nil {
|
||||||
return fmt.Errorf("No context given. Impossible to use %s", cmdName)
|
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]
|
orig := args[0]
|
||||||
dest := args[1]
|
dest := args[1]
|
||||||
|
|
||||||
cmd := b.config.Cmd
|
cmd := b.Config.Cmd
|
||||||
b.config.Cmd = []string{"/bin/sh", "-c", fmt.Sprintf("#(nop) %s %s in %s", cmdName, orig, dest)}
|
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)
|
defer func(cmd []string) { b.Config.Cmd = cmd }(cmd)
|
||||||
b.config.Image = b.image
|
b.Config.Image = b.image
|
||||||
|
|
||||||
var (
|
var (
|
||||||
origPath = orig
|
origPath = orig
|
||||||
|
@ -201,7 +201,7 @@ func (b *buildFile) runContextCommand(args []string, allowRemote bool, allowDeco
|
||||||
}
|
}
|
||||||
|
|
||||||
// Hash path and check the cache
|
// Hash path and check the cache
|
||||||
if b.options.UtilizeCache {
|
if b.Options.UtilizeCache {
|
||||||
var (
|
var (
|
||||||
hash string
|
hash string
|
||||||
sums = b.context.GetSums()
|
sums = b.context.GetSums()
|
||||||
|
@ -233,7 +233,7 @@ func (b *buildFile) runContextCommand(args []string, allowRemote bool, allowDeco
|
||||||
hash = "file:" + h
|
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()
|
hit, err := b.probeCache()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -245,11 +245,11 @@ func (b *buildFile) runContextCommand(args []string, allowRemote bool, allowDeco
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create the container
|
// Create the container
|
||||||
container, _, err := b.options.Daemon.Create(b.config, "")
|
container, _, err := b.Options.Daemon.Create(b.Config, "")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
b.tmpContainers[container.ID] = struct{}{}
|
b.TmpContainers[container.ID] = struct{}{}
|
||||||
|
|
||||||
if err := container.Mount(); err != nil {
|
if err := container.Mount(); err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -269,27 +269,27 @@ func (b *buildFile) runContextCommand(args []string, allowRemote bool, allowDeco
|
||||||
return nil
|
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)
|
remote, tag := parsers.ParseRepositoryTag(name)
|
||||||
pullRegistryAuth := b.options.AuthConfig
|
pullRegistryAuth := b.Options.AuthConfig
|
||||||
if len(b.options.AuthConfigFile.Configs) > 0 {
|
if len(b.Options.AuthConfigFile.Configs) > 0 {
|
||||||
// The request came with a full auth config file, we prefer to use that
|
// The request came with a full auth config file, we prefer to use that
|
||||||
endpoint, _, err := registry.ResolveRepositoryName(remote)
|
endpoint, _, err := registry.ResolveRepositoryName(remote)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
resolvedAuth := b.options.AuthConfigFile.ResolveAuthConfig(endpoint)
|
resolvedAuth := b.Options.AuthConfigFile.ResolveAuthConfig(endpoint)
|
||||||
pullRegistryAuth = &resolvedAuth
|
pullRegistryAuth = &resolvedAuth
|
||||||
}
|
}
|
||||||
job := b.options.Engine.Job("pull", remote, tag)
|
job := b.Options.Engine.Job("pull", remote, tag)
|
||||||
job.SetenvBool("json", b.options.StreamFormatter.Json())
|
job.SetenvBool("json", b.Options.StreamFormatter.Json())
|
||||||
job.SetenvBool("parallel", true)
|
job.SetenvBool("parallel", true)
|
||||||
job.SetenvJson("authConfig", pullRegistryAuth)
|
job.SetenvJson("authConfig", pullRegistryAuth)
|
||||||
job.Stdout.Add(b.options.OutOld)
|
job.Stdout.Add(b.Options.OutOld)
|
||||||
if err := job.Run(); err != nil {
|
if err := job.Run(); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
image, err := b.options.Daemon.Repositories().LookupImage(name)
|
image, err := b.Options.Daemon.Repositories().LookupImage(name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -297,23 +297,23 @@ func (b *buildFile) pullImage(name string) (*imagepkg.Image, error) {
|
||||||
return image, nil
|
return image, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *buildFile) processImageFrom(img *imagepkg.Image) error {
|
func (b *BuildFile) processImageFrom(img *imagepkg.Image) error {
|
||||||
b.image = img.ID
|
b.image = img.ID
|
||||||
b.config = &runconfig.Config{}
|
b.Config = &runconfig.Config{}
|
||||||
if img.Config != nil {
|
if img.Config != nil {
|
||||||
b.config = img.Config
|
b.Config = img.Config
|
||||||
}
|
}
|
||||||
if b.config.Env == nil || len(b.config.Env) == 0 {
|
if b.Config.Env == nil || len(b.Config.Env) == 0 {
|
||||||
b.config.Env = append(b.config.Env, "PATH="+daemon.DefaultPathEnv)
|
b.Config.Env = append(b.Config.Env, "PATH="+daemon.DefaultPathEnv)
|
||||||
}
|
}
|
||||||
// Process ONBUILD triggers if they exist
|
// Process ONBUILD triggers if they exist
|
||||||
if nTriggers := len(b.config.OnBuild); nTriggers != 0 {
|
if nTriggers := len(b.Config.OnBuild); nTriggers != 0 {
|
||||||
fmt.Fprintf(b.options.ErrStream, "# Executing %d build triggers\n", nTriggers)
|
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.
|
// Copy the ONBUILD triggers, and remove them from the config, since the config will be commited.
|
||||||
onBuildTriggers := b.config.OnBuild
|
onBuildTriggers := b.Config.OnBuild
|
||||||
b.config.OnBuild = []string{}
|
b.Config.OnBuild = []string{}
|
||||||
|
|
||||||
// FIXME rewrite this so that builder/parser is used; right now steps in
|
// 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
|
// 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
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// probeCache checks to see if image-caching is enabled (`b.options.UtilizeCache`)
|
// 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
|
// 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
|
// 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
|
// `(true, nil)`. If no image is found, it returns `(false, nil)`. If there
|
||||||
// is any error, it returns `(false, err)`.
|
// is any error, it returns `(false, err)`.
|
||||||
func (b *buildFile) probeCache() (bool, error) {
|
func (b *BuildFile) probeCache() (bool, error) {
|
||||||
if b.options.UtilizeCache {
|
if b.Options.UtilizeCache {
|
||||||
if cache, err := b.options.Daemon.ImageGetCached(b.image, b.config); err != nil {
|
if cache, err := b.Options.Daemon.ImageGetCached(b.image, b.Config); err != nil {
|
||||||
return false, err
|
return false, err
|
||||||
} else if cache != nil {
|
} 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")
|
utils.Debugf("[BUILDER] Use cached version")
|
||||||
b.image = cache.ID
|
b.image = cache.ID
|
||||||
return true, nil
|
return true, nil
|
||||||
|
@ -364,37 +364,37 @@ func (b *buildFile) probeCache() (bool, error) {
|
||||||
return false, nil
|
return false, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *buildFile) create() (*daemon.Container, error) {
|
func (b *BuildFile) create() (*daemon.Container, error) {
|
||||||
if b.image == "" {
|
if b.image == "" {
|
||||||
return nil, fmt.Errorf("Please provide a source image with `from` prior to run")
|
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
|
// Create the container
|
||||||
c, _, err := b.options.Daemon.Create(b.config, "")
|
c, _, err := b.Options.Daemon.Create(b.Config, "")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
b.tmpContainers[c.ID] = struct{}{}
|
b.TmpContainers[c.ID] = struct{}{}
|
||||||
fmt.Fprintf(b.options.OutStream, " ---> Running in %s\n", utils.TruncateID(c.ID))
|
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
|
// override the entry point that may have been picked up from the base image
|
||||||
c.Path = b.config.Cmd[0]
|
c.Path = b.Config.Cmd[0]
|
||||||
c.Args = b.config.Cmd[1:]
|
c.Args = b.Config.Cmd[1:]
|
||||||
|
|
||||||
return c, nil
|
return c, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *buildFile) run(c *daemon.Container) error {
|
func (b *BuildFile) run(c *daemon.Container) error {
|
||||||
var errCh chan error
|
var errCh chan error
|
||||||
if b.options.Verbose {
|
if b.Options.Verbose {
|
||||||
errCh = utils.Go(func() error {
|
errCh = utils.Go(func() error {
|
||||||
// FIXME: call the 'attach' job so that daemon.Attach can be made private
|
// 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
|
// 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
|
// but without hijacking for stdin. Also, with attach there can be race
|
||||||
// condition because of some output already was printed before it.
|
// 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
|
// Wait for it to finish
|
||||||
if ret, _ := c.State.WaitStop(-1 * time.Second); ret != 0 {
|
if ret, _ := c.State.WaitStop(-1 * time.Second); ret != 0 {
|
||||||
err := &utils.JSONError{
|
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,
|
Code: ret,
|
||||||
}
|
}
|
||||||
return err
|
return err
|
||||||
|
@ -421,7 +421,7 @@ func (b *buildFile) run(c *daemon.Container) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *buildFile) checkPathForAddition(orig string) error {
|
func (b *BuildFile) checkPathForAddition(orig string) error {
|
||||||
origPath := path.Join(b.contextPath, orig)
|
origPath := path.Join(b.contextPath, orig)
|
||||||
if p, err := filepath.EvalSymlinks(origPath); err != nil {
|
if p, err := filepath.EvalSymlinks(origPath); err != nil {
|
||||||
if os.IsNotExist(err) {
|
if os.IsNotExist(err) {
|
||||||
|
@ -444,7 +444,7 @@ func (b *buildFile) checkPathForAddition(orig string) error {
|
||||||
return nil
|
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 (
|
var (
|
||||||
err error
|
err error
|
||||||
destExists = true
|
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 {
|
for c := range containers {
|
||||||
tmp := b.options.Daemon.Get(c)
|
tmp := b.Options.Daemon.Get(c)
|
||||||
if err := b.options.Daemon.Destroy(tmp); err != nil {
|
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())
|
fmt.Fprintf(b.Options.OutStream, "Error removing intermediate container %s: %s\n", utils.TruncateID(c), err.Error())
|
||||||
} else {
|
} else {
|
||||||
delete(containers, c)
|
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))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,12 +10,12 @@ var (
|
||||||
)
|
)
|
||||||
|
|
||||||
// handle environment replacement. Used in dispatcher.
|
// 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) {
|
for _, match := range TOKEN_ENV_INTERPOLATION.FindAllString(str, -1) {
|
||||||
match = match[strings.Index(match, "$"):]
|
match = match[strings.Index(match, "$"):]
|
||||||
matchKey := strings.Trim(match, "${}")
|
matchKey := strings.Trim(match, "${}")
|
||||||
|
|
||||||
for envKey, envValue := range b.env {
|
for envKey, envValue := range b.Env {
|
||||||
if matchKey == envKey {
|
if matchKey == envKey {
|
||||||
str = strings.Replace(str, match, envValue, -1)
|
str = strings.Replace(str, match, envValue, -1)
|
||||||
}
|
}
|
||||||
|
|
119
builder/job.go
Normal file
119
builder/job.go
Normal 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 = ®istry.AuthConfig{}
|
||||||
|
configFile = ®istry.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
|
||||||
|
}
|
1006
daemon/build.go
1006
daemon/build.go
File diff suppressed because it is too large
Load diff
|
@ -101,7 +101,6 @@ func (daemon *Daemon) Install(eng *engine.Engine) error {
|
||||||
// FIXME: remove ImageDelete's dependency on Daemon, then move to graph/
|
// FIXME: remove ImageDelete's dependency on Daemon, then move to graph/
|
||||||
for name, method := range map[string]engine.Handler{
|
for name, method := range map[string]engine.Handler{
|
||||||
"attach": daemon.ContainerAttach,
|
"attach": daemon.ContainerAttach,
|
||||||
"build": daemon.CmdBuild,
|
|
||||||
"commit": daemon.ContainerCommit,
|
"commit": daemon.ContainerCommit,
|
||||||
"container_changes": daemon.ContainerChanges,
|
"container_changes": daemon.ContainerChanges,
|
||||||
"container_copy": daemon.ContainerCopy,
|
"container_copy": daemon.ContainerCopy,
|
||||||
|
|
|
@ -5,6 +5,7 @@ package main
|
||||||
import (
|
import (
|
||||||
"log"
|
"log"
|
||||||
|
|
||||||
|
"github.com/docker/docker/builder"
|
||||||
"github.com/docker/docker/builtins"
|
"github.com/docker/docker/builtins"
|
||||||
"github.com/docker/docker/daemon"
|
"github.com/docker/docker/daemon"
|
||||||
_ "github.com/docker/docker/daemon/execdriver/lxc"
|
_ "github.com/docker/docker/daemon/execdriver/lxc"
|
||||||
|
@ -48,6 +49,10 @@ func mainDaemon() {
|
||||||
if err := d.Install(eng); err != nil {
|
if err := d.Install(eng); err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
b := &builder.BuilderJob{eng, d}
|
||||||
|
b.Install()
|
||||||
|
|
||||||
// after the daemon is done setting up we can tell the api to start
|
// after the daemon is done setting up we can tell the api to start
|
||||||
// accepting connections
|
// accepting connections
|
||||||
if err := eng.Job("acceptconnections").Run(); err != nil {
|
if err := eng.Job("acceptconnections").Run(); err != nil {
|
||||||
|
|
Loading…
Add table
Reference in a new issue