Merge pull request #12457 from calavera/remove_engine_job_builder

Remove engine.Job from Builder.
This commit is contained in:
Jessie Frazelle 2015-04-20 17:27:17 -07:00
commit 82f75df9e9
8 changed files with 280 additions and 229 deletions

20
api/server/form.go Normal file
View File

@ -0,0 +1,20 @@
package server
import (
"net/http"
"strconv"
"strings"
)
func boolValue(r *http.Request, k string) bool {
s := strings.ToLower(strings.TrimSpace(r.FormValue(k)))
return !(s == "" || s == "0" || s == "no" || s == "false" || s == "none")
}
func int64Value(r *http.Request, k string) int64 {
val, err := strconv.ParseInt(r.FormValue(k), 10, 64)
if err != nil {
return 0
}
return val
}

55
api/server/form_test.go Normal file
View File

@ -0,0 +1,55 @@
package server
import (
"net/http"
"net/url"
"testing"
)
func TestBoolValue(t *testing.T) {
cases := map[string]bool{
"": false,
"0": false,
"no": false,
"false": false,
"none": false,
"1": true,
"yes": true,
"true": true,
"one": true,
"100": true,
}
for c, e := range cases {
v := url.Values{}
v.Set("test", c)
r, _ := http.NewRequest("POST", "", nil)
r.Form = v
a := boolValue(r, "test")
if a != e {
t.Fatalf("Value: %s, expected: %v, actual: %v", c, e, a)
}
}
}
func TestInt64Value(t *testing.T) {
cases := map[string]int64{
"": 0,
"asdf": 0,
"0": 0,
"1": 1,
}
for c, e := range cases {
v := url.Values{}
v.Set("test", c)
r, _ := http.NewRequest("POST", "", nil)
r.Form = v
a := int64Value(r, "test")
if a != e {
t.Fatalf("Value: %s, expected: %v, actual: %v", c, e, a)
}
}
}

View File

@ -23,6 +23,7 @@ import (
"github.com/docker/docker/api" "github.com/docker/docker/api"
"github.com/docker/docker/api/types" "github.com/docker/docker/api/types"
"github.com/docker/docker/autogen/dockerversion" "github.com/docker/docker/autogen/dockerversion"
"github.com/docker/docker/builder"
"github.com/docker/docker/daemon" "github.com/docker/docker/daemon"
"github.com/docker/docker/daemon/networkdriver/bridge" "github.com/docker/docker/daemon/networkdriver/bridge"
"github.com/docker/docker/engine" "github.com/docker/docker/engine"
@ -238,12 +239,12 @@ func writeJSON(w http.ResponseWriter, code int, v interface{}) error {
return json.NewEncoder(w).Encode(v) return json.NewEncoder(w).Encode(v)
} }
func streamJSON(job *engine.Job, w http.ResponseWriter, flush bool) { func streamJSON(out *engine.Output, w http.ResponseWriter, flush bool) {
w.Header().Set("Content-Type", "application/json") w.Header().Set("Content-Type", "application/json")
if flush { if flush {
job.Stdout.Add(utils.NewWriteFlusher(w)) out.Add(utils.NewWriteFlusher(w))
} else { } else {
job.Stdout.Add(w) out.Add(w)
} }
} }
@ -382,7 +383,7 @@ func (s *Server) getImagesJSON(eng *engine.Engine, version version.Version, w ht
Filters: r.Form.Get("filters"), Filters: r.Form.Get("filters"),
// FIXME this parameter could just be a match filter // FIXME this parameter could just be a match filter
Filter: r.Form.Get("filter"), Filter: r.Form.Get("filter"),
All: toBool(r.Form.Get("all")), All: boolValue(r, "all"),
} }
images, err := s.daemon.Repositories().Images(&imagesConfig) images, err := s.daemon.Repositories().Images(&imagesConfig)
@ -598,8 +599,8 @@ func (s *Server) getContainersJSON(eng *engine.Engine, version version.Version,
} }
config := &daemon.ContainersConfig{ config := &daemon.ContainersConfig{
All: toBool(r.Form.Get("all")), All: boolValue(r, "all"),
Size: toBool(r.Form.Get("size")), Size: boolValue(r, "size"),
Since: r.Form.Get("since"), Since: r.Form.Get("since"),
Before: r.Form.Get("before"), Before: r.Form.Get("before"),
Filters: r.Form.Get("filters"), Filters: r.Form.Get("filters"),
@ -641,14 +642,14 @@ func (s *Server) getContainersLogs(eng *engine.Engine, version version.Version,
} }
// Validate args here, because we can't return not StatusOK after job.Run() call // Validate args here, because we can't return not StatusOK after job.Run() call
stdout, stderr := toBool(r.Form.Get("stdout")), toBool(r.Form.Get("stderr")) stdout, stderr := boolValue(r, "stdout"), boolValue(r, "stderr")
if !(stdout || stderr) { if !(stdout || stderr) {
return fmt.Errorf("Bad parameters: you must choose at least one stream") return fmt.Errorf("Bad parameters: you must choose at least one stream")
} }
logsConfig := &daemon.ContainerLogsConfig{ logsConfig := &daemon.ContainerLogsConfig{
Follow: toBool(r.Form.Get("follow")), Follow: boolValue(r, "follow"),
Timestamps: toBool(r.Form.Get("timestamps")), Timestamps: boolValue(r, "timestamps"),
Tail: r.Form.Get("tail"), Tail: r.Form.Get("tail"),
UseStdout: stdout, UseStdout: stdout,
UseStderr: stderr, UseStderr: stderr,
@ -672,7 +673,7 @@ func (s *Server) postImagesTag(eng *engine.Engine, version version.Version, w ht
repo := r.Form.Get("repo") repo := r.Form.Get("repo")
tag := r.Form.Get("tag") tag := r.Form.Get("tag")
force := toBool(r.Form.Get("force")) force := boolValue(r, "force")
if err := s.daemon.Repositories().Tag(repo, tag, vars["name"], force); err != nil { if err := s.daemon.Repositories().Tag(repo, tag, vars["name"], force); err != nil {
return err return err
} }
@ -691,11 +692,20 @@ func (s *Server) postCommit(eng *engine.Engine, version version.Version, w http.
cont := r.Form.Get("container") cont := r.Form.Get("container")
pause := toBool(r.Form.Get("pause")) pause := boolValue(r, "pause")
if r.FormValue("pause") == "" && version.GreaterThanOrEqualTo("1.13") { if r.FormValue("pause") == "" && version.GreaterThanOrEqualTo("1.13") {
pause = true pause = true
} }
c, _, err := runconfig.DecodeContainerConfig(r.Body)
if err != nil && err != io.EOF { //Do not fail if body is empty.
return err
}
if c == nil {
c = &runconfig.Config{}
}
containerCommitConfig := &daemon.ContainerCommitConfig{ containerCommitConfig := &daemon.ContainerCommitConfig{
Pause: pause, Pause: pause,
Repo: r.Form.Get("repo"), Repo: r.Form.Get("repo"),
@ -703,10 +713,10 @@ func (s *Server) postCommit(eng *engine.Engine, version version.Version, w http.
Author: r.Form.Get("author"), Author: r.Form.Get("author"),
Comment: r.Form.Get("comment"), Comment: r.Form.Get("comment"),
Changes: r.Form["changes"], Changes: r.Form["changes"],
Config: r.Body, Config: c,
} }
imgID, err := s.daemon.ContainerCommit(cont, containerCommitConfig) imgID, err := builder.Commit(s.daemon, eng, cont, containerCommitConfig)
if err != nil { if err != nil {
return err return err
} }
@ -783,10 +793,15 @@ func (s *Server) postImagesCreate(eng *engine.Engine, version version.Version, w
imageImportConfig.Json = false imageImportConfig.Json = false
} }
if err := s.daemon.Repositories().Import(src, repo, tag, imageImportConfig, eng); err != nil { newConfig, err := builder.BuildFromConfig(s.daemon, eng, &runconfig.Config{}, imageImportConfig.Changes)
if err != nil {
return err return err
} }
imageImportConfig.ContainerConfig = newConfig
if err := s.daemon.Repositories().Import(src, repo, tag, imageImportConfig); err != nil {
return err
}
} }
return nil return nil
@ -859,7 +874,7 @@ func (s *Server) postImagesPush(eng *engine.Engine, version version.Version, w h
job.Setenv("tag", r.Form.Get("tag")) job.Setenv("tag", r.Form.Get("tag"))
if version.GreaterThan("1.0") { if version.GreaterThan("1.0") {
job.SetenvBool("json", true) job.SetenvBool("json", true)
streamJSON(job, w, true) streamJSON(job.Stdout, w, true)
} else { } else {
job.Stdout.Add(utils.NewWriteFlusher(w)) job.Stdout.Add(utils.NewWriteFlusher(w))
} }
@ -978,9 +993,9 @@ func (s *Server) deleteContainers(eng *engine.Engine, version version.Version, w
name := vars["name"] name := vars["name"]
config := &daemon.ContainerRmConfig{ config := &daemon.ContainerRmConfig{
ForceRemove: toBool(r.Form.Get("force")), ForceRemove: boolValue(r, "force"),
RemoveVolume: toBool(r.Form.Get("v")), RemoveVolume: boolValue(r, "v"),
RemoveLink: toBool(r.Form.Get("link")), RemoveLink: boolValue(r, "link"),
} }
if err := s.daemon.ContainerRm(name, config); err != nil { if err := s.daemon.ContainerRm(name, config); err != nil {
@ -1005,8 +1020,8 @@ func (s *Server) deleteImages(eng *engine.Engine, version version.Version, w htt
} }
name := vars["name"] name := vars["name"]
force := toBool(r.Form.Get("force")) force := boolValue(r, "force")
noprune := toBool(r.Form.Get("noprune")) noprune := boolValue(r, "noprune")
list, err := s.daemon.ImageDelete(name, force, noprune) list, err := s.daemon.ImageDelete(name, force, noprune)
if err != nil { if err != nil {
@ -1153,19 +1168,19 @@ func (s *Server) postContainersAttach(eng *engine.Engine, version version.Versio
} else { } else {
errStream = outStream errStream = outStream
} }
logs := toBool(r.Form.Get("logs")) logs := boolValue(r, "logs")
stream := toBool(r.Form.Get("stream")) stream := boolValue(r, "stream")
var stdin io.ReadCloser var stdin io.ReadCloser
var stdout, stderr io.Writer var stdout, stderr io.Writer
if toBool(r.Form.Get("stdin")) { if boolValue(r, "stdin") {
stdin = inStream stdin = inStream
} }
if toBool(r.Form.Get("stdout")) { if boolValue(r, "stdout") {
stdout = outStream stdout = outStream
} }
if toBool(r.Form.Get("stderr")) { if boolValue(r, "stderr") {
stderr = errStream stderr = errStream
} }
@ -1209,7 +1224,7 @@ func (s *Server) getContainersByName(eng *engine.Engine, version version.Version
if version.LessThan("1.12") { if version.LessThan("1.12") {
job.SetenvBool("raw", true) job.SetenvBool("raw", true)
} }
streamJSON(job, w, false) streamJSON(job.Stdout, w, false)
return job.Run() return job.Run()
} }
@ -1234,7 +1249,7 @@ func (s *Server) getImagesByName(eng *engine.Engine, version version.Version, w
if version.LessThan("1.12") { if version.LessThan("1.12") {
job.SetenvBool("raw", true) job.SetenvBool("raw", true)
} }
streamJSON(job, w, false) streamJSON(job.Stdout, w, false)
return job.Run() return job.Run()
} }
@ -1247,7 +1262,7 @@ func (s *Server) postBuild(eng *engine.Engine, version version.Version, w http.R
authConfig = &registry.AuthConfig{} authConfig = &registry.AuthConfig{}
configFileEncoded = r.Header.Get("X-Registry-Config") configFileEncoded = r.Header.Get("X-Registry-Config")
configFile = &registry.ConfigFile{} configFile = &registry.ConfigFile{}
job = eng.Job("build") buildConfig = builder.NewBuildConfig()
) )
// This block can be removed when API versions prior to 1.9 are deprecated. // This block can be removed when API versions prior to 1.9 are deprecated.
@ -1273,36 +1288,38 @@ func (s *Server) postBuild(eng *engine.Engine, version version.Version, w http.R
} }
if version.GreaterThanOrEqualTo("1.8") { if version.GreaterThanOrEqualTo("1.8") {
job.SetenvBool("json", true) w.Header().Set("Content-Type", "application/json")
streamJSON(job, w, true) buildConfig.JSONFormat = true
} else {
job.Stdout.Add(utils.NewWriteFlusher(w))
} }
if toBool(r.FormValue("forcerm")) && version.GreaterThanOrEqualTo("1.12") { if boolValue(r, "forcerm") && version.GreaterThanOrEqualTo("1.12") {
job.Setenv("rm", "1") buildConfig.Remove = true
} else if r.FormValue("rm") == "" && version.GreaterThanOrEqualTo("1.12") { } else if r.FormValue("rm") == "" && version.GreaterThanOrEqualTo("1.12") {
job.Setenv("rm", "1") buildConfig.Remove = true
} else { } else {
job.Setenv("rm", r.FormValue("rm")) buildConfig.Remove = boolValue(r, "rm")
} }
if toBool(r.FormValue("pull")) && version.GreaterThanOrEqualTo("1.16") { if boolValue(r, "pull") && version.GreaterThanOrEqualTo("1.16") {
job.Setenv("pull", "1") buildConfig.Pull = true
} }
job.Stdin.Add(r.Body)
job.Setenv("remote", r.FormValue("remote")) output := utils.NewWriteFlusher(w)
job.Setenv("dockerfile", r.FormValue("dockerfile")) buildConfig.Stdout = output
job.Setenv("t", r.FormValue("t")) buildConfig.Context = r.Body
job.Setenv("q", r.FormValue("q"))
job.Setenv("nocache", r.FormValue("nocache")) buildConfig.RemoteURL = r.FormValue("remote")
job.Setenv("forcerm", r.FormValue("forcerm")) buildConfig.DockerfileName = r.FormValue("dockerfile")
job.SetenvJson("authConfig", authConfig) buildConfig.RepoName = r.FormValue("t")
job.SetenvJson("configFile", configFile) buildConfig.SuppressOutput = boolValue(r, "q")
job.Setenv("memswap", r.FormValue("memswap")) buildConfig.NoCache = boolValue(r, "nocache")
job.Setenv("memory", r.FormValue("memory")) buildConfig.ForceRemove = boolValue(r, "forcerm")
job.Setenv("cpusetcpus", r.FormValue("cpusetcpus")) buildConfig.AuthConfig = authConfig
job.Setenv("cpusetmems", r.FormValue("cpusetmems")) buildConfig.ConfigFile = configFile
job.Setenv("cpushares", r.FormValue("cpushares")) buildConfig.MemorySwap = int64Value(r, "memswap")
buildConfig.Memory = int64Value(r, "memory")
buildConfig.CpuShares = int64Value(r, "cpushares")
buildConfig.CpuSetCpus = r.FormValue("cpusetcpus")
buildConfig.CpuSetMems = r.FormValue("cpusetmems")
// Job cancellation. Note: not all job types support this. // Job cancellation. Note: not all job types support this.
if closeNotifier, ok := w.(http.CloseNotifier); ok { if closeNotifier, ok := w.(http.CloseNotifier); ok {
@ -1312,14 +1329,16 @@ func (s *Server) postBuild(eng *engine.Engine, version version.Version, w http.R
select { select {
case <-finished: case <-finished:
case <-closeNotifier.CloseNotify(): case <-closeNotifier.CloseNotify():
logrus.Infof("Client disconnected, cancelling job: %s", job.Name) logrus.Infof("Client disconnected, cancelling job: build")
job.Cancel() buildConfig.Cancel()
} }
}() }()
} }
if err := job.Run(); err != nil { if err := builder.Build(s.daemon, eng, buildConfig); err != nil {
if !job.Stdout.Used() { // Do not write the error in the http output if it's still empty.
// This prevents from writing a 200(OK) when there is an interal error.
if !output.Flushed() {
return err return err
} }
sf := streamformatter.NewStreamFormatter(version.GreaterThanOrEqualTo("1.8")) sf := streamformatter.NewStreamFormatter(version.GreaterThanOrEqualTo("1.8"))
@ -1673,8 +1692,3 @@ func allocateDaemonPort(addr string) error {
} }
return nil return nil
} }
func toBool(s string) bool {
s = strings.ToLower(strings.TrimSpace(s))
return !(s == "" || s == "0" || s == "no" || s == "false" || s == "none")
}

View File

@ -2,13 +2,13 @@ package builder
import ( import (
"bytes" "bytes"
"encoding/json"
"fmt" "fmt"
"io" "io"
"io/ioutil" "io/ioutil"
"os" "os"
"os/exec" "os/exec"
"strings" "strings"
"sync"
"github.com/docker/docker/api" "github.com/docker/docker/api"
"github.com/docker/docker/builder/parser" "github.com/docker/docker/builder/parser"
@ -36,44 +36,61 @@ var validCommitCommands = map[string]bool{
"onbuild": true, "onbuild": true,
} }
type BuilderJob struct { type Config struct {
Engine *engine.Engine DockerfileName string
Daemon *daemon.Daemon RemoteURL string
RepoName string
SuppressOutput bool
NoCache bool
Remove bool
ForceRemove bool
Pull bool
JSONFormat bool
Memory int64
MemorySwap int64
CpuShares int64
CpuSetCpus string
CpuSetMems string
AuthConfig *registry.AuthConfig
ConfigFile *registry.ConfigFile
Stdout io.Writer
Context io.ReadCloser
// When closed, the job has been cancelled.
// Note: not all jobs implement cancellation.
// See Job.Cancel() and Job.WaitCancelled()
cancelled chan struct{}
cancelOnce sync.Once
} }
func (b *BuilderJob) Install() { // When called, causes the Job.WaitCancelled channel to unblock.
b.Engine.Register("build", b.CmdBuild) func (b *Config) Cancel() {
b.Engine.Register("build_config", b.CmdBuildConfig) b.cancelOnce.Do(func() {
close(b.cancelled)
})
} }
func (b *BuilderJob) CmdBuild(job *engine.Job) error { // Returns a channel which is closed ("never blocks") when the job is cancelled.
if len(job.Args) != 0 { func (b *Config) WaitCancelled() <-chan struct{} {
return fmt.Errorf("Usage: %s\n", job.Name) return b.cancelled
}
func NewBuildConfig() *Config {
return &Config{
AuthConfig: &registry.AuthConfig{},
ConfigFile: &registry.ConfigFile{},
cancelled: make(chan struct{}),
} }
}
func Build(d *daemon.Daemon, e *engine.Engine, buildConfig *Config) error {
var ( var (
dockerfileName = job.Getenv("dockerfile") repoName string
remoteURL = job.Getenv("remote") tag string
repoName = job.Getenv("t") context io.ReadCloser
suppressOutput = job.GetenvBool("q")
noCache = job.GetenvBool("nocache")
rm = job.GetenvBool("rm")
forceRm = job.GetenvBool("forcerm")
pull = job.GetenvBool("pull")
memory = job.GetenvInt64("memory")
memorySwap = job.GetenvInt64("memswap")
cpuShares = job.GetenvInt64("cpushares")
cpuSetCpus = job.Getenv("cpusetcpus")
cpuSetMems = job.Getenv("cpusetmems")
authConfig = &registry.AuthConfig{}
configFile = &registry.ConfigFile{}
tag string
context io.ReadCloser
) )
job.GetenvJson("authConfig", authConfig) repoName, tag = parsers.ParseRepositoryTag(buildConfig.RepoName)
job.GetenvJson("configFile", configFile)
repoName, tag = parsers.ParseRepositoryTag(repoName)
if repoName != "" { if repoName != "" {
if err := registry.ValidateRepositoryName(repoName); err != nil { if err := registry.ValidateRepositoryName(repoName); err != nil {
return err return err
@ -85,11 +102,11 @@ func (b *BuilderJob) CmdBuild(job *engine.Job) error {
} }
} }
if remoteURL == "" { if buildConfig.RemoteURL == "" {
context = ioutil.NopCloser(job.Stdin) context = ioutil.NopCloser(buildConfig.Context)
} else if urlutil.IsGitURL(remoteURL) { } else if urlutil.IsGitURL(buildConfig.RemoteURL) {
if !urlutil.IsGitTransport(remoteURL) { if !urlutil.IsGitTransport(buildConfig.RemoteURL) {
remoteURL = "https://" + remoteURL buildConfig.RemoteURL = "https://" + buildConfig.RemoteURL
} }
root, err := ioutil.TempDir("", "docker-build-git") root, err := ioutil.TempDir("", "docker-build-git")
if err != nil { if err != nil {
@ -97,7 +114,7 @@ func (b *BuilderJob) CmdBuild(job *engine.Job) error {
} }
defer os.RemoveAll(root) defer os.RemoveAll(root)
if output, err := exec.Command("git", "clone", "--recursive", remoteURL, root).CombinedOutput(); err != nil { if output, err := exec.Command("git", "clone", "--recursive", buildConfig.RemoteURL, root).CombinedOutput(); err != nil {
return fmt.Errorf("Error trying to use git: %s (%s)", err, output) return fmt.Errorf("Error trying to use git: %s (%s)", err, output)
} }
@ -106,8 +123,8 @@ func (b *BuilderJob) CmdBuild(job *engine.Job) error {
return err return err
} }
context = c context = c
} else if urlutil.IsURL(remoteURL) { } else if urlutil.IsURL(buildConfig.RemoteURL) {
f, err := httputils.Download(remoteURL) f, err := httputils.Download(buildConfig.RemoteURL)
if err != nil { if err != nil {
return err return err
} }
@ -119,9 +136,9 @@ func (b *BuilderJob) CmdBuild(job *engine.Job) error {
// When we're downloading just a Dockerfile put it in // When we're downloading just a Dockerfile put it in
// the default name - don't allow the client to move/specify it // the default name - don't allow the client to move/specify it
dockerfileName = api.DefaultDockerfileName buildConfig.DockerfileName = api.DefaultDockerfileName
c, err := archive.Generate(dockerfileName, string(dockerFile)) c, err := archive.Generate(buildConfig.DockerfileName, string(dockerFile))
if err != nil { if err != nil {
return err return err
} }
@ -129,35 +146,35 @@ func (b *BuilderJob) CmdBuild(job *engine.Job) error {
} }
defer context.Close() defer context.Close()
sf := streamformatter.NewStreamFormatter(job.GetenvBool("json")) sf := streamformatter.NewStreamFormatter(buildConfig.JSONFormat)
builder := &Builder{ builder := &Builder{
Daemon: b.Daemon, Daemon: d,
Engine: b.Engine, Engine: e,
OutStream: &streamformatter.StdoutFormater{ OutStream: &streamformatter.StdoutFormater{
Writer: job.Stdout, Writer: buildConfig.Stdout,
StreamFormatter: sf, StreamFormatter: sf,
}, },
ErrStream: &streamformatter.StderrFormater{ ErrStream: &streamformatter.StderrFormater{
Writer: job.Stdout, Writer: buildConfig.Stdout,
StreamFormatter: sf, StreamFormatter: sf,
}, },
Verbose: !suppressOutput, Verbose: !buildConfig.SuppressOutput,
UtilizeCache: !noCache, UtilizeCache: !buildConfig.NoCache,
Remove: rm, Remove: buildConfig.Remove,
ForceRemove: forceRm, ForceRemove: buildConfig.ForceRemove,
Pull: pull, Pull: buildConfig.Pull,
OutOld: job.Stdout, OutOld: buildConfig.Stdout,
StreamFormatter: sf, StreamFormatter: sf,
AuthConfig: authConfig, AuthConfig: buildConfig.AuthConfig,
ConfigFile: configFile, ConfigFile: buildConfig.ConfigFile,
dockerfileName: dockerfileName, dockerfileName: buildConfig.DockerfileName,
cpuShares: cpuShares, cpuShares: buildConfig.CpuShares,
cpuSetCpus: cpuSetCpus, cpuSetCpus: buildConfig.CpuSetCpus,
cpuSetMems: cpuSetMems, cpuSetMems: buildConfig.CpuSetMems,
memory: memory, memory: buildConfig.Memory,
memorySwap: memorySwap, memorySwap: buildConfig.MemorySwap,
cancelled: job.WaitCancelled(), cancelled: buildConfig.WaitCancelled(),
} }
id, err := builder.Run(context) id, err := builder.Run(context)
@ -166,41 +183,28 @@ func (b *BuilderJob) CmdBuild(job *engine.Job) error {
} }
if repoName != "" { if repoName != "" {
b.Daemon.Repositories().Tag(repoName, tag, id, true) return d.Repositories().Tag(repoName, tag, id, true)
} }
return nil return nil
} }
func (b *BuilderJob) CmdBuildConfig(job *engine.Job) error { func BuildFromConfig(d *daemon.Daemon, e *engine.Engine, c *runconfig.Config, changes []string) (*runconfig.Config, error) {
if len(job.Args) != 0 {
return fmt.Errorf("Usage: %s\n", job.Name)
}
var (
changes = job.GetenvList("changes")
newConfig runconfig.Config
)
if err := job.GetenvJson("config", &newConfig); err != nil {
return err
}
ast, err := parser.Parse(bytes.NewBufferString(strings.Join(changes, "\n"))) ast, err := parser.Parse(bytes.NewBufferString(strings.Join(changes, "\n")))
if err != nil { if err != nil {
return err return nil, err
} }
// ensure that the commands are valid // ensure that the commands are valid
for _, n := range ast.Children { for _, n := range ast.Children {
if !validCommitCommands[n.Value] { if !validCommitCommands[n.Value] {
return fmt.Errorf("%s is not a valid change command", n.Value) return nil, fmt.Errorf("%s is not a valid change command", n.Value)
} }
} }
builder := &Builder{ builder := &Builder{
Daemon: b.Daemon, Daemon: d,
Engine: b.Engine, Engine: e,
Config: &newConfig, Config: c,
OutStream: ioutil.Discard, OutStream: ioutil.Discard,
ErrStream: ioutil.Discard, ErrStream: ioutil.Discard,
disableCommit: true, disableCommit: true,
@ -208,12 +212,32 @@ func (b *BuilderJob) CmdBuildConfig(job *engine.Job) error {
for i, n := range ast.Children { for i, n := range ast.Children {
if err := builder.dispatch(i, n); err != nil { if err := builder.dispatch(i, n); err != nil {
return err return nil, err
} }
} }
if err := json.NewEncoder(job.Stdout).Encode(builder.Config); err != nil { return builder.Config, nil
return err }
}
return nil func Commit(d *daemon.Daemon, eng *engine.Engine, name string, c *daemon.ContainerCommitConfig) (string, error) {
container, err := d.Get(name)
if err != nil {
return "", err
}
newConfig, err := BuildFromConfig(d, eng, c.Config, c.Changes)
if err != nil {
return "", err
}
if err := runconfig.Merge(newConfig, container.Config); err != nil {
return "", err
}
img, err := d.Commit(container, c.Repo, c.Tag, c.Comment, c.Author, c.Pause, newConfig)
if err != nil {
return "", err
}
return img.ID, nil
} }

View File

@ -1,12 +1,6 @@
package daemon package daemon
import ( import (
"bytes"
"encoding/json"
"io"
"github.com/Sirupsen/logrus"
"github.com/docker/docker/engine"
"github.com/docker/docker/image" "github.com/docker/docker/image"
"github.com/docker/docker/runconfig" "github.com/docker/docker/runconfig"
) )
@ -18,49 +12,7 @@ type ContainerCommitConfig struct {
Author string Author string
Comment string Comment string
Changes []string Changes []string
Config io.ReadCloser Config *runconfig.Config
}
func (daemon *Daemon) ContainerCommit(name string, c *ContainerCommitConfig) (string, error) {
container, err := daemon.Get(name)
if err != nil {
return "", err
}
var (
subenv engine.Env
config = container.Config
stdoutBuffer = bytes.NewBuffer(nil)
newConfig runconfig.Config
)
if err := subenv.Decode(c.Config); err != nil {
logrus.Errorf("%s", err)
}
buildConfigJob := daemon.eng.Job("build_config")
buildConfigJob.Stdout.Add(stdoutBuffer)
buildConfigJob.SetenvList("changes", c.Changes)
// FIXME this should be remove when we remove deprecated config param
buildConfigJob.SetenvSubEnv("config", &subenv)
if err := buildConfigJob.Run(); err != nil {
return "", err
}
if err := json.NewDecoder(stdoutBuffer).Decode(&newConfig); err != nil {
return "", err
}
if err := runconfig.Merge(&newConfig, config); err != nil {
return "", err
}
img, err := daemon.Commit(container, c.Repo, c.Tag, c.Comment, c.Author, c.Pause, &newConfig)
if err != nil {
return "", err
}
return img.ID, nil
} }
// Commit creates a new filesystem image from the current state of a container. // Commit creates a new filesystem image from the current state of a container.

View File

@ -11,7 +11,6 @@ import (
"github.com/Sirupsen/logrus" "github.com/Sirupsen/logrus"
apiserver "github.com/docker/docker/api/server" apiserver "github.com/docker/docker/api/server"
"github.com/docker/docker/autogen/dockerversion" "github.com/docker/docker/autogen/dockerversion"
"github.com/docker/docker/builder"
"github.com/docker/docker/daemon" "github.com/docker/docker/daemon"
_ "github.com/docker/docker/daemon/execdriver/lxc" _ "github.com/docker/docker/daemon/execdriver/lxc"
_ "github.com/docker/docker/daemon/execdriver/native" _ "github.com/docker/docker/daemon/execdriver/native"
@ -141,9 +140,6 @@ func mainDaemon() {
"graphdriver": d.GraphDriver().String(), "graphdriver": d.GraphDriver().String(),
}).Info("Docker daemon") }).Info("Docker daemon")
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 with specified daemon // accepting connections with specified daemon
api.AcceptConnections(d) api.AcceptConnections(d)
@ -155,7 +151,6 @@ func mainDaemon() {
if errAPI != nil { if errAPI != nil {
logrus.Fatalf("Shutting down due to ServeAPI error: %v", errAPI) logrus.Fatalf("Shutting down due to ServeAPI error: %v", errAPI)
} }
} }
// currentUserIsOwner checks whether the current user is the owner of the given // currentUserIsOwner checks whether the current user is the owner of the given

View File

@ -1,13 +1,10 @@
package graph package graph
import ( import (
"bytes"
"encoding/json"
"io" "io"
"net/http" "net/http"
"net/url" "net/url"
"github.com/docker/docker/engine"
"github.com/docker/docker/pkg/archive" "github.com/docker/docker/pkg/archive"
"github.com/docker/docker/pkg/httputils" "github.com/docker/docker/pkg/httputils"
"github.com/docker/docker/pkg/progressreader" "github.com/docker/docker/pkg/progressreader"
@ -17,20 +14,18 @@ import (
) )
type ImageImportConfig struct { type ImageImportConfig struct {
Changes []string Changes []string
InConfig io.ReadCloser InConfig io.ReadCloser
Json bool Json bool
OutStream io.Writer OutStream io.Writer
//OutStream WriteFlusher ContainerConfig *runconfig.Config
} }
func (s *TagStore) Import(src string, repo string, tag string, imageImportConfig *ImageImportConfig, eng *engine.Engine) error { func (s *TagStore) Import(src string, repo string, tag string, imageImportConfig *ImageImportConfig) error {
var ( var (
sf = streamformatter.NewStreamFormatter(imageImportConfig.Json) sf = streamformatter.NewStreamFormatter(imageImportConfig.Json)
archive archive.ArchiveReader archive archive.ArchiveReader
resp *http.Response resp *http.Response
stdoutBuffer = bytes.NewBuffer(nil)
newConfig runconfig.Config
) )
if src == "-" { if src == "-" {
@ -63,20 +58,7 @@ func (s *TagStore) Import(src string, repo string, tag string, imageImportConfig
archive = progressReader archive = progressReader
} }
buildConfigJob := eng.Job("build_config") img, err := s.graph.Create(archive, "", "", "Imported from "+src, "", nil, imageImportConfig.ContainerConfig)
buildConfigJob.Stdout.Add(stdoutBuffer)
buildConfigJob.SetenvList("changes", imageImportConfig.Changes)
// FIXME this should be remove when we remove deprecated config param
//buildConfigJob.Setenv("config", job.Getenv("config"))
if err := buildConfigJob.Run(); err != nil {
return err
}
if err := json.NewDecoder(stdoutBuffer).Decode(&newConfig); err != nil {
return err
}
img, err := s.graph.Create(archive, "", "", "Imported from "+src, "", nil, &newConfig)
if err != nil { if err != nil {
return err return err
} }

View File

@ -128,12 +128,14 @@ type WriteFlusher struct {
sync.Mutex sync.Mutex
w io.Writer w io.Writer
flusher http.Flusher flusher http.Flusher
flushed bool
} }
func (wf *WriteFlusher) Write(b []byte) (n int, err error) { func (wf *WriteFlusher) Write(b []byte) (n int, err error) {
wf.Lock() wf.Lock()
defer wf.Unlock() defer wf.Unlock()
n, err = wf.w.Write(b) n, err = wf.w.Write(b)
wf.flushed = true
wf.flusher.Flush() wf.flusher.Flush()
return n, err return n, err
} }
@ -142,9 +144,16 @@ func (wf *WriteFlusher) Write(b []byte) (n int, err error) {
func (wf *WriteFlusher) Flush() { func (wf *WriteFlusher) Flush() {
wf.Lock() wf.Lock()
defer wf.Unlock() defer wf.Unlock()
wf.flushed = true
wf.flusher.Flush() wf.flusher.Flush()
} }
func (wf *WriteFlusher) Flushed() bool {
wf.Lock()
defer wf.Unlock()
return wf.flushed
}
func NewWriteFlusher(w io.Writer) *WriteFlusher { func NewWriteFlusher(w io.Writer) *WriteFlusher {
var flusher http.Flusher var flusher http.Flusher
if f, ok := w.(http.Flusher); ok { if f, ok := w.(http.Flusher); ok {