func init() { // Hack to run sys init during unit testing if utils.SelfPath() == "/sbin/init" { SysInit() return } if usr, err := user.Current(); err != nil { panic(err) } else if usr.Uid != "0" { panic("docker tests needs to be run as root") } NetworkBridgeIface = "testdockbr0" // Make it our Store root runtime, err := NewRuntimeFromDirectory(unitTestStoreBase, false) if err != nil { panic(err) } // Create the "Server" srv := &Server{ runtime: runtime, } // Retrieve the Image if err := srv.ImagePull(unitTestImageName, "", "", os.Stdout, utils.NewStreamFormatter(false)); err != nil { panic(err) } }
func postImagesPush(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error { authConfig := &auth.AuthConfig{} if version > 1.1 { if err := json.NewDecoder(r.Body).Decode(authConfig); err != nil { return err } } else { localAuthConfig, err := auth.LoadConfig(srv.runtime.root) if err != nil && err != auth.ErrConfigFileMissing { return err } authConfig = localAuthConfig } if err := parseForm(r); err != nil { return err } registry := r.Form.Get("registry") if vars == nil { return fmt.Errorf("Missing parameter") } name := vars["name"] if version > 1.0 { w.Header().Set("Content-Type", "application/json") } sf := utils.NewStreamFormatter(version > 1.0) if err := srv.ImagePush(name, registry, w, sf, authConfig); err != nil { if sf.Used() { w.Write(sf.FormatError(err)) return nil } return err } return nil }
func init() { // Hack to run sys init during unit testing if utils.SelfPath() == "/sbin/init" { SysInit() return } if uid := syscall.Geteuid(); uid != 0 { log.Fatal("docker tests needs to be run as root") } NetworkBridgeIface = "testdockbr0" // Make it our Store root runtime, err := NewRuntimeFromDirectory(unitTestStoreBase, false) if err != nil { panic(err) } // Create the "Server" srv := &Server{ runtime: runtime, enableCors: false, lock: &sync.Mutex{}, pullingPool: make(map[string]struct{}), pushingPool: make(map[string]struct{}), } // Retrieve the Image if err := srv.ImagePull(unitTestImageName, "", "", os.Stdout, utils.NewStreamFormatter(false), nil); err != nil { panic(err) } }
func postImagesPush(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error { authConfig := &auth.AuthConfig{} if err := json.NewDecoder(r.Body).Decode(authConfig); err != nil { return err } if err := parseForm(r); err != nil { return err } if vars == nil { return fmt.Errorf("Missing parameter") } name := vars["name"] if version > 1.0 { w.Header().Set("Content-Type", "application/json") } sf := utils.NewStreamFormatter(version > 1.0) if err := srv.ImagePush(name, w, sf, authConfig); err != nil { if sf.Used() { w.Write(sf.FormatError(err)) return nil } return err } return nil }
func postImagesInsert(eng *engine.Engine, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error { if err := parseForm(r); err != nil { return err } if vars == nil { return fmt.Errorf("Missing parameter") } if version > 1.0 { w.Header().Set("Content-Type", "application/json") } job := eng.Job("insert", vars["name"], r.Form.Get("url"), r.Form.Get("path")) if version > 1.0 { job.SetenvBool("json", true) streamJSON(job, w, false) } else { job.Stdout.Add(w) } if err := job.Run(); err != nil { if !job.Stdout.Used() { return err } sf := utils.NewStreamFormatter(version > 1.0) w.Write(sf.FormatError(err)) } return nil }
func (b *buildFile) CmdFrom(name string) error { image, err := b.runtime.repositories.LookupImage(name) if err != nil { if b.runtime.graph.IsNotExist(err) { var tag, remote string if strings.Contains(name, ":") { remoteParts := strings.Split(name, ":") tag = remoteParts[1] remote = remoteParts[0] } else { remote = name } if err := b.srv.ImagePull(remote, tag, "", b.out, utils.NewStreamFormatter(false), nil); err != nil { return err } image, err = b.runtime.repositories.LookupImage(name) if err != nil { return err } } else { return err } } b.image = image.ID b.config = &Config{} return nil }
func setupBaseImage() { config := &DaemonConfig{ Root: unitTestStoreBase, AutoRestart: false, BridgeIface: unitTestNetworkBridge, } runtime, err := NewRuntimeFromDirectory(config) if err != nil { log.Fatalf("Unable to create a runtime for tests:", err) } // Create the "Server" srv := &Server{ runtime: runtime, pullingPool: make(map[string]struct{}), pushingPool: make(map[string]struct{}), } // If the unit test is not found, try to download it. if img, err := runtime.repositories.LookupImage(unitTestImageName); err != nil || img.ID != unitTestImageID { // Retrieve the Image if err := srv.ImagePull(unitTestImageName, "", os.Stdout, utils.NewStreamFormatter(false), nil, nil, true); err != nil { log.Fatalf("Unable to pull the test image:", err) } } }
func postImagesInsert(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error { if err := parseForm(r); err != nil { return err } url := r.Form.Get("url") path := r.Form.Get("path") if vars == nil { return fmt.Errorf("Missing parameter") } name := vars["name"] if version > 1.0 { w.Header().Set("Content-Type", "application/json") } sf := utils.NewStreamFormatter(version > 1.0) err := srv.ImageInsert(name, url, path, w, sf) if err != nil { if sf.Used() { w.Write(sf.FormatError(err)) return nil } return err } return nil }
func generateImage(name string, srv *docker.Server) error { archive, err := fakeTar() if err != nil { return err } return srv.ImageImport("-", "repo", name, archive, ioutil.Discard, utils.NewStreamFormatter(true)) }
func (b *buildFile) CmdFrom(name string) error { image, err := b.runtime.repositories.LookupImage(name) if err != nil { if b.runtime.graph.IsNotExist(err) { remote, tag := utils.ParseRepositoryTag(name) if err := b.srv.ImagePull(remote, tag, b.out, utils.NewStreamFormatter(false), nil, nil, true); err != nil { return err } image, err = b.runtime.repositories.LookupImage(name) if err != nil { return err } } else { return err } } b.image = image.ID b.config = &Config{} if image.Config != nil { b.config = image.Config } if b.config.Env == nil || len(b.config.Env) == 0 { b.config.Env = append(b.config.Env, "HOME=/", "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin") } return nil }
// Creates an image from Pull or from Import func postImagesCreate(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error { if err := parseForm(r); err != nil { return err } src := r.Form.Get("fromSrc") image := r.Form.Get("fromImage") tag := r.Form.Get("tag") repo := r.Form.Get("repo") if version > 1.0 { w.Header().Set("Content-Type", "application/json") } sf := utils.NewStreamFormatter(version > 1.0) if image != "" { //pull if err := srv.ImagePull(image, tag, w, sf, &auth.AuthConfig{}); err != nil { if sf.Used() { w.Write(sf.FormatError(err)) return nil } return err } } else { //import if err := srv.ImageImport(src, repo, tag, r.Body, w, sf); err != nil { if sf.Used() { w.Write(sf.FormatError(err)) return nil } return err } } return nil }
// Creates an image from Pull or from Import func postImagesCreate(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error { if err := parseForm(r); err != nil { return err } var ( image = r.Form.Get("fromImage") repo = r.Form.Get("repo") tag = r.Form.Get("tag") job *engine.Job ) authEncoded := r.Header.Get("X-Registry-Auth") authConfig := ®istry.AuthConfig{} if authEncoded != "" { authJson := base64.NewDecoder(base64.URLEncoding, strings.NewReader(authEncoded)) if err := json.NewDecoder(authJson).Decode(authConfig); err != nil { // for a pull it is not an error if no auth was given // to increase compatibility with the existing api it is defaulting to be empty authConfig = ®istry.AuthConfig{} } } if image != "" { //pull if tag == "" { image, tag = utils.ParseRepositoryTag(image) } metaHeaders := map[string][]string{} for k, v := range r.Header { if strings.HasPrefix(k, "X-Meta-") { metaHeaders[k] = v } } job = eng.Job("pull", image, tag) job.SetenvBool("parallel", version.GreaterThan("1.3")) job.SetenvJson("metaHeaders", metaHeaders) job.SetenvJson("authConfig", authConfig) } else { //import if tag == "" { repo, tag = utils.ParseRepositoryTag(repo) } job = eng.Job("import", r.Form.Get("fromSrc"), repo, tag) job.Stdin.Add(r.Body) } if version.GreaterThan("1.0") { job.SetenvBool("json", true) streamJSON(job, w, true) } else { job.Stdout.Add(utils.NewWriteFlusher(w)) } if err := job.Run(); err != nil { if !job.Stdout.Used() { return err } sf := utils.NewStreamFormatter(version.GreaterThan("1.0")) w.Write(sf.FormatError(err)) } return nil }
func postBuild(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error { if version.LessThan("1.3") { return fmt.Errorf("Multipart upload for build is no longer supported. Please upgrade your docker client.") } var ( authEncoded = r.Header.Get("X-Registry-Auth") authConfig = &auth.AuthConfig{} configFileEncoded = r.Header.Get("X-Registry-Config") configFile = &auth.ConfigFile{} job = eng.Job("build") ) // This block can be removed when API versions prior to 1.9 are deprecated. // Both headers will be parsed and sent along to the daemon, but if a non-empty // ConfigFile is present, any value provided as an AuthConfig directly will // be overridden. See BuildFile::CmdFrom for details. if version.LessThan("1.9") && authEncoded != "" { authJson := base64.NewDecoder(base64.URLEncoding, strings.NewReader(authEncoded)) if err := json.NewDecoder(authJson).Decode(authConfig); err != nil { // for a pull it is not an error if no auth was given // to increase compatibility with the existing api it is defaulting to be empty authConfig = &auth.AuthConfig{} } } if configFileEncoded != "" { configFileJson := base64.NewDecoder(base64.URLEncoding, strings.NewReader(configFileEncoded)) if err := json.NewDecoder(configFileJson).Decode(configFile); err != nil { // for a pull it is not an error if no auth was given // to increase compatibility with the existing api it is defaulting to be empty configFile = &auth.ConfigFile{} } } if version.GreaterThanOrEqualTo("1.8") { job.SetenvBool("json", true) streamJSON(job, w, true) } else { job.Stdout.Add(utils.NewWriteFlusher(w)) } job.Stdin.Add(r.Body) job.Setenv("remote", r.FormValue("remote")) job.Setenv("t", r.FormValue("t")) job.Setenv("q", r.FormValue("q")) job.Setenv("nocache", r.FormValue("nocache")) job.Setenv("rm", r.FormValue("rm")) job.SetenvJson("authConfig", authConfig) job.SetenvJson("configFile", configFile) if err := job.Run(); err != nil { if !job.Stdout.Used() { return err } sf := utils.NewStreamFormatter(version.GreaterThanOrEqualTo("1.8")) w.Write(sf.FormatError(err)) } return nil }
func init() { os.Setenv("TEST", "1") // Hack to run sys init during unit testing if selfPath := utils.SelfPath(); selfPath == "/sbin/init" || selfPath == "/.dockerinit" { SysInit() return } if uid := syscall.Geteuid(); uid != 0 { log.Fatal("docker tests need to be run as root") } NetworkBridgeIface = unitTestNetworkBridge // Make it our Store root if runtime, err := NewRuntimeFromDirectory(unitTestStoreBase, false); err != nil { log.Fatalf("Unable to create a runtime for tests:", err) } else { globalRuntime = runtime } // Cleanup any leftover container for _, container := range globalRuntime.List() { if err := globalRuntime.Destroy(container); err != nil { log.Fatalf("Error destroying leftover container: %s", err) } } // Create the "Server" srv := &Server{ runtime: globalRuntime, enableCors: false, pullingPool: make(map[string]struct{}), pushingPool: make(map[string]struct{}), } // If the unit test is not found, try to download it. if img, err := globalRuntime.repositories.LookupImage(unitTestImageName); err != nil || img.ID != unitTestImageID { // Retrieve the Image if err := srv.ImagePull(unitTestImageName, "", os.Stdout, utils.NewStreamFormatter(false), nil, nil, true); err != nil { log.Fatalf("Unable to pull the test image:", err) } } // Spawn a Daemon go func() { if err := ListenAndServe(testDaemonProto, testDaemonAddr, srv, os.Getenv("DEBUG") != ""); err != nil { log.Fatalf("Unable to spawn the test daemon:", err) } }() // Give some time to ListenAndServer to actually start time.Sleep(time.Second) startFds, startGoroutines = utils.GetTotalUsedFds(), runtime.NumGoroutine() }
func postImagesPush(eng *engine.Engine, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error { if vars == nil { return fmt.Errorf("Missing parameter") } metaHeaders := map[string][]string{} for k, v := range r.Header { if strings.HasPrefix(k, "X-Meta-") { metaHeaders[k] = v } } if err := parseForm(r); err != nil { return err } authConfig := &auth.AuthConfig{} authEncoded := r.Header.Get("X-Registry-Auth") if authEncoded != "" { // the new format is to handle the authConfig as a header authJson := base64.NewDecoder(base64.URLEncoding, strings.NewReader(authEncoded)) if err := json.NewDecoder(authJson).Decode(authConfig); err != nil { // to increase compatibility to existing api it is defaulting to be empty authConfig = &auth.AuthConfig{} } } else { // the old format is supported for compatibility if there was no authConfig header if err := json.NewDecoder(r.Body).Decode(authConfig); err != nil { return err } } if version > 1.0 { w.Header().Set("Content-Type", "application/json") } job := eng.Job("push", vars["name"]) job.SetenvJson("metaHeaders", metaHeaders) job.SetenvJson("authConfig", authConfig) if version > 1.0 { job.SetenvBool("json", true) streamJSON(job, w, true) } else { job.Stdout.Add(utils.NewWriteFlusher(w)) } if err := job.Run(); err != nil { if !job.Stdout.Used() { return err } sf := utils.NewStreamFormatter(version > 1.0) w.Write(sf.FormatError(err)) } return nil }
// Creates an image from Pull or from Import func postImagesCreate(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error { if err := parseForm(r); err != nil { return err } var ( image = r.Form.Get("fromImage") tag = r.Form.Get("tag") job *engine.Job ) authEncoded := r.Header.Get("X-Registry-Auth") authConfig := &auth.AuthConfig{} if authEncoded != "" { authJson := base64.NewDecoder(base64.URLEncoding, strings.NewReader(authEncoded)) if err := json.NewDecoder(authJson).Decode(authConfig); err != nil { // for a pull it is not an error if no auth was given // to increase compatibility with the existing api it is defaulting to be empty authConfig = &auth.AuthConfig{} } } if version > 1.0 { w.Header().Set("Content-Type", "application/json") } if image != "" { //pull metaHeaders := map[string][]string{} for k, v := range r.Header { if strings.HasPrefix(k, "X-Meta-") { metaHeaders[k] = v } } job = srv.Eng.Job("pull", r.Form.Get("fromImage"), tag) job.SetenvBool("parallel", version > 1.3) job.SetenvJson("metaHeaders", metaHeaders) job.SetenvJson("authConfig", authConfig) } else { //import job = srv.Eng.Job("import", r.Form.Get("fromSrc"), r.Form.Get("repo"), tag) job.Stdin.Add(r.Body) } job.SetenvBool("json", version > 1.0) job.Stdout.Add(utils.NewWriteFlusher(w)) if err := job.Run(); err != nil { if !job.Stdout.Used() { return err } sf := utils.NewStreamFormatter(version > 1.0) w.Write(sf.FormatError(err)) } return nil }
// Creates an image from Pull or from Import func postImagesCreate(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error { if err := parseForm(r); err != nil { return err } src := r.Form.Get("fromSrc") image := r.Form.Get("fromImage") tag := r.Form.Get("tag") repo := r.Form.Get("repo") authEncoded := r.Header.Get("X-Registry-Auth") authConfig := &auth.AuthConfig{} if authEncoded != "" { authJson := base64.NewDecoder(base64.URLEncoding, strings.NewReader(authEncoded)) if err := json.NewDecoder(authJson).Decode(authConfig); err != nil { // for a pull it is not an error if no auth was given // to increase compatibility with the existing api it is defaulting to be empty authConfig = &auth.AuthConfig{} } } if version > 1.0 { w.Header().Set("Content-Type", "application/json") } sf := utils.NewStreamFormatter(version > 1.0) if image != "" { //pull metaHeaders := map[string][]string{} for k, v := range r.Header { if strings.HasPrefix(k, "X-Meta-") { metaHeaders[k] = v } } if err := srv.ImagePull(image, tag, w, sf, authConfig, metaHeaders, version > 1.3); err != nil { if sf.Used() { w.Write(sf.FormatError(err)) return nil } return err } } else { //import if err := srv.ImageImport(src, repo, tag, r.Body, w, sf); err != nil { if sf.Used() { w.Write(sf.FormatError(err)) return nil } return err } } return nil }
// TempLayerArchive creates a temporary archive of the given image's filesystem layer. // The archive is stored on disk and will be automatically deleted as soon as has been read. // If output is not nil, a human-readable progress bar will be written to it. // FIXME: does this belong in Graph? How about MktempFile, let the caller use it for archives? func (graph *Graph) TempLayerArchive(id string, compression Compression, output io.Writer) (*TempArchive, error) { image, err := graph.Get(id) if err != nil { return nil, err } tmp, err := graph.tmp() if err != nil { return nil, err } archive, err := image.TarLayer(compression) if err != nil { return nil, err } sf := utils.NewStreamFormatter(false) return NewTempArchive(utils.ProgressReader(ioutil.NopCloser(archive), 0, output, sf.FormatProgress("Buffering to disk", "%v/%v (%v)"), sf), tmp.Root) }
func TestForbiddenContextPath(t *testing.T) { eng := NewTestEngine(t) defer nuke(mkRuntimeFromEngine(eng, t)) srv := mkServerFromEngine(eng, t) context := testContextTemplate{` from {IMAGE} maintainer dockerio add ../../ test/ `, [][2]string{{"test.txt", "test1"}, {"other.txt", "other"}}, nil} httpServer, err := mkTestingFileServer(context.remoteFiles) if err != nil { t.Fatal(err) } defer httpServer.Close() idx := strings.LastIndex(httpServer.URL, ":") if idx < 0 { t.Fatalf("could not get port from test http server address %s", httpServer.URL) } port := httpServer.URL[idx+1:] iIP := eng.Hack_GetGlobalVar("httpapi.bridgeIP") if iIP == nil { t.Fatal("Legacy bridgeIP field not set in engine") } ip, ok := iIP.(net.IP) if !ok { panic("Legacy bridgeIP field in engine does not cast to net.IP") } dockerfile := constructDockerfile(context.dockerfile, ip, port) buildfile := docker.NewBuildFile(srv, ioutil.Discard, false, true, false, utils.NewStreamFormatter(false)) _, err = buildfile.Build(mkTestContext(dockerfile, context.files, t)) if err == nil { t.Log("Error should not be nil") t.Fail() } if err.Error() != "Forbidden path outside the build context: ../../ (/)" { t.Logf("Error message is not expected: %s", err.Error()) t.Fail() } }
func postImagesPush(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error { metaHeaders := map[string][]string{} for k, v := range r.Header { if strings.HasPrefix(k, "X-Meta-") { metaHeaders[k] = v } } if err := parseForm(r); err != nil { return err } authConfig := &auth.AuthConfig{} authEncoded := r.Header.Get("X-Registry-Auth") if authEncoded != "" { // the new format is to handle the authConfig as a header authJson := base64.NewDecoder(base64.URLEncoding, strings.NewReader(authEncoded)) if err := json.NewDecoder(authJson).Decode(authConfig); err != nil { // to increase compatibility to existing api it is defaulting to be empty authConfig = &auth.AuthConfig{} } } else { // the old format is supported for compatibility if there was no authConfig header if err := json.NewDecoder(r.Body).Decode(authConfig); err != nil { return err } } if vars == nil { return fmt.Errorf("Missing parameter") } name := vars["name"] if version > 1.0 { w.Header().Set("Content-Type", "application/json") } sf := utils.NewStreamFormatter(version > 1.0) if err := srv.ImagePush(name, w, sf, authConfig, metaHeaders); err != nil { if sf.Used() { w.Write(sf.FormatError(err)) return nil } return err } return nil }
func init() { // Hack to run sys init during unit testing if utils.SelfPath() == "/sbin/init" { SysInit() return } if uid := syscall.Geteuid(); uid != 0 { log.Fatal("docker tests need to be run as root") } NetworkBridgeIface = unitTestNetworkBridge // Make it our Store root runtime, err := NewRuntimeFromDirectory(unitTestStoreBase, false) if err != nil { panic(err) } globalRuntime = runtime // Create the "Server" srv := &Server{ runtime: runtime, enableCors: false, pullingPool: make(map[string]struct{}), pushingPool: make(map[string]struct{}), } // If the unit test is not found, try to download it. if img, err := runtime.repositories.LookupImage(unitTestImageName); err != nil || img.ID != unitTestImageID { // Retrieve the Image if err := srv.ImagePull(unitTestImageName, "", os.Stdout, utils.NewStreamFormatter(false), nil); err != nil { panic(err) } } // Spawn a Daemon go func() { if err := ListenAndServe(testDaemonProto, testDaemonAddr, srv, os.Getenv("DEBUG") != ""); err != nil { panic(err) } }() // Give some time to ListenAndServer to actually start time.Sleep(time.Second) }
func (b *buildFile) CmdFrom(name string) error { image, err := b.runtime.repositories.LookupImage(name) if err != nil { if b.runtime.graph.IsNotExist(err) { remote, tag := utils.ParseRepositoryTag(name) if err := b.srv.ImagePull(remote, tag, b.out, utils.NewStreamFormatter(false), nil); err != nil { return err } image, err = b.runtime.repositories.LookupImage(name) if err != nil { return err } } else { return err } } b.image = image.ID b.config = &Config{} return nil }
func TestImageInsert(t *testing.T) { eng := NewTestEngine(t) defer mkRuntimeFromEngine(eng, t).Nuke() srv := mkServerFromEngine(eng, t) sf := utils.NewStreamFormatter(true) // bad image name fails if err := srv.ImageInsert("foo", "https://www.docker.io/static/img/docker-top-logo.png", "/foo", ioutil.Discard, sf); err == nil { t.Fatal("expected an error and got none") } // bad url fails if err := srv.ImageInsert(unitTestImageID, "http://bad_host_name_that_will_totally_fail.com/", "/foo", ioutil.Discard, sf); err == nil { t.Fatal("expected an error and got none") } // success returns nil if err := srv.ImageInsert(unitTestImageID, "https://www.docker.io/static/img/docker-top-logo.png", "/foo", ioutil.Discard, sf); err != nil { t.Fatalf("expected no error, but got %v", err) } }
func buildImage(context testContextTemplate, t *testing.T, eng *engine.Engine, useCache bool) (*docker.Image, error) { if eng == nil { eng = NewTestEngine(t) runtime := mkRuntimeFromEngine(eng, t) // FIXME: we might not need runtime, why not simply nuke // the engine? defer nuke(runtime) } srv := mkServerFromEngine(eng, t) httpServer, err := mkTestingFileServer(context.remoteFiles) if err != nil { t.Fatal(err) } defer httpServer.Close() idx := strings.LastIndex(httpServer.URL, ":") if idx < 0 { t.Fatalf("could not get port from test http server address %s", httpServer.URL) } port := httpServer.URL[idx+1:] iIP := eng.Hack_GetGlobalVar("httpapi.bridgeIP") if iIP == nil { t.Fatal("Legacy bridgeIP field not set in engine") } ip, ok := iIP.(net.IP) if !ok { panic("Legacy bridgeIP field in engine does not cast to net.IP") } dockerfile := constructDockerfile(context.dockerfile, ip, port) buildfile := docker.NewBuildFile(srv, ioutil.Discard, false, useCache, false, utils.NewStreamFormatter(false)) id, err := buildfile.Build(mkTestContext(dockerfile, context.files, t)) if err != nil { return nil, err } return srv.ImageInspect(id) }
func setupBaseImage() { eng, err := engine.New(unitTestStoreBase) if err != nil { log.Fatalf("Can't initialize engine at %s: %s", unitTestStoreBase, err) } job := eng.Job("initapi") job.Setenv("Root", unitTestStoreBase) job.SetenvBool("Autorestart", false) job.Setenv("BridgeIface", unitTestNetworkBridge) if err := job.Run(); err != nil { log.Fatalf("Unable to create a runtime for tests: %s", err) } srv := mkServerFromEngine(eng, log.New(os.Stderr, "", 0)) // If the unit test is not found, try to download it. if img, err := srv.ImageInspect(unitTestImageName); err != nil || img.ID != unitTestImageID { // Retrieve the Image if err := srv.ImagePull(unitTestImageName, "", os.Stdout, utils.NewStreamFormatter(false), nil, nil, true); err != nil { log.Fatalf("Unable to pull the test image: %s", err) } } }
func buildImage(context testContextTemplate, t *testing.T, eng *engine.Engine, useCache bool) (*image.Image, error) { if eng == nil { eng = NewTestEngine(t) runtime := mkDaemonFromEngine(eng, t) // FIXME: we might not need runtime, why not simply nuke // the engine? defer nuke(runtime) } srv := mkServerFromEngine(eng, t) httpServer, err := mkTestingFileServer(context.remoteFiles) if err != nil { t.Fatal(err) } defer httpServer.Close() idx := strings.LastIndex(httpServer.URL, ":") if idx < 0 { t.Fatalf("could not get port from test http server address %s", httpServer.URL) } port := httpServer.URL[idx+1:] iIP := eng.Hack_GetGlobalVar("httpapi.bridgeIP") if iIP == nil { t.Fatal("Legacy bridgeIP field not set in engine") } ip, ok := iIP.(net.IP) if !ok { panic("Legacy bridgeIP field in engine does not cast to net.IP") } dockerfile := constructDockerfile(context.dockerfile, ip, port) buildfile := server.NewBuildFile(srv, ioutil.Discard, ioutil.Discard, false, useCache, false, false, ioutil.Discard, utils.NewStreamFormatter(false), nil, nil) id, err := buildfile.Build(context.Archive(dockerfile, t)) if err != nil { return nil, err } job := eng.Job("image_inspect", id) buffer := bytes.NewBuffer(nil) image := &image.Image{} job.Stdout.Add(buffer) if err := job.Run(); err != nil { return nil, err } err = json.NewDecoder(buffer).Decode(image) return image, err }
func (cli *DockerCli) CmdBuild(args ...string) error { cmd := Subcmd("build", "[OPTIONS] PATH | -", "Build a new container image from the source code at PATH") tag := cmd.String("t", "", "Tag to be applied to the resulting image in case of success") if err := cmd.Parse(args); err != nil { return nil } if cmd.NArg() != 1 { cmd.Usage() return nil } var ( multipartBody io.Reader file io.ReadCloser contextPath string ) // Init the needed component for the Multipart buff := bytes.NewBuffer([]byte{}) multipartBody = buff w := multipart.NewWriter(buff) boundary := strings.NewReader("\r\n--" + w.Boundary() + "--\r\n") compression := Bzip2 if cmd.Arg(0) == "-" { file = os.Stdin } else { // Send Dockerfile from arg/Dockerfile (deprecate later) if f, err := os.Open(path.Join(cmd.Arg(0), "Dockerfile")); err != nil { return err } else { file = f } // Send context from arg // Create a FormFile multipart for the context if needed // FIXME: Use NewTempArchive in order to have the size and avoid too much memory usage? context, err := Tar(cmd.Arg(0), compression) if err != nil { return err } // NOTE: Do this in case '.' or '..' is input absPath, err := filepath.Abs(cmd.Arg(0)) if err != nil { return err } if wField, err := w.CreateFormFile("Context", filepath.Base(absPath)+"."+compression.Extension()); err != nil { return err } else { // FIXME: Find a way to have a progressbar for the upload too sf := utils.NewStreamFormatter(false) io.Copy(wField, utils.ProgressReader(ioutil.NopCloser(context), -1, os.Stdout, sf.FormatProgress("Caching Context", "%v/%v (%v)"), sf)) } multipartBody = io.MultiReader(multipartBody, boundary) } // Create a FormFile multipart for the Dockerfile if wField, err := w.CreateFormFile("Dockerfile", "Dockerfile"); err != nil { return err } else { io.Copy(wField, file) } multipartBody = io.MultiReader(multipartBody, boundary) v := &url.Values{} v.Set("t", *tag) // Send the multipart request with correct content-type req, err := http.NewRequest("POST", fmt.Sprintf("http://%s:%d%s?%s", cli.host, cli.port, "/build", v.Encode()), multipartBody) if err != nil { return err } req.Header.Set("Content-Type", w.FormDataContentType()) if contextPath != "" { req.Header.Set("X-Docker-Context-Compression", compression.Flag()) fmt.Println("Uploading Context...") } resp, err := http.DefaultClient.Do(req) if err != nil { return err } defer resp.Body.Close() // Check for errors if resp.StatusCode < 200 || resp.StatusCode >= 400 { body, err := ioutil.ReadAll(resp.Body) if err != nil { return err } return fmt.Errorf("error: %s", body) } // Output the result if _, err := io.Copy(os.Stdout, resp.Body); err != nil { return err } return nil }
func (cli *DockerCli) CmdBuild(args ...string) error { cmd := Subcmd("build", "[OPTIONS] PATH | URL | -", "Build a new container image from the source code at PATH") tag := cmd.String("t", "", "Tag to be applied to the resulting image in case of success") suppressOutput := cmd.Bool("q", false, "Suppress verbose build output") if err := cmd.Parse(args); err != nil { return nil } if cmd.NArg() != 1 { cmd.Usage() return nil } var ( context Archive isRemote bool err error ) if cmd.Arg(0) == "-" { // As a special case, 'docker build -' will build from an empty context with the // contents of stdin as a Dockerfile dockerfile, err := ioutil.ReadAll(cli.in) if err != nil { return err } context, err = mkBuildContext(string(dockerfile), nil) } else if utils.IsURL(cmd.Arg(0)) || utils.IsGIT(cmd.Arg(0)) { isRemote = true } else { context, err = Tar(cmd.Arg(0), Uncompressed) } var body io.Reader // Setup an upload progress bar // FIXME: ProgressReader shouldn't be this annoyning to use if context != nil { sf := utils.NewStreamFormatter(false) body = utils.ProgressReader(ioutil.NopCloser(context), 0, cli.err, sf.FormatProgress("Uploading context", "%v bytes%0.0s%0.0s"), sf) } // Upload the build context v := &url.Values{} v.Set("t", *tag) if *suppressOutput { v.Set("q", "1") } if isRemote { v.Set("remote", cmd.Arg(0)) } req, err := http.NewRequest("POST", fmt.Sprintf("/v%g/build?%s", APIVERSION, v.Encode()), body) if err != nil { return err } if context != nil { req.Header.Set("Content-Type", "application/tar") } dial, err := net.Dial(cli.proto, cli.addr) if err != nil { return err } clientconn := httputil.NewClientConn(dial, nil) resp, err := clientconn.Do(req) defer clientconn.Close() if err != nil { return err } defer resp.Body.Close() // Check for errors if resp.StatusCode < 200 || resp.StatusCode >= 400 { body, err := ioutil.ReadAll(resp.Body) if err != nil { return err } if len(body) == 0 { return fmt.Errorf("Error: %s", http.StatusText(resp.StatusCode)) } return fmt.Errorf("Error: %s", body) } // Output the result if _, err := io.Copy(cli.out, resp.Body); err != nil { return err } return nil }
func TestBuildADDFileNotFound(t *testing.T) { eng := NewTestEngine(t) defer nuke(mkRuntimeFromEngine(eng, t)) context := testContextTemplate{` from {IMAGE} add foo /usr/local/bar `, nil, nil} httpServer, err := mkTestingFileServer(context.remoteFiles) if err != nil { t.Fatal(err) } defer httpServer.Close() idx := strings.LastIndex(httpServer.URL, ":") if idx < 0 { t.Fatalf("could not get port from test http server address %s", httpServer.URL) } port := httpServer.URL[idx+1:] iIP := eng.Hack_GetGlobalVar("httpapi.bridgeIP") if iIP == nil { t.Fatal("Legacy bridgeIP field not set in engine") } ip, ok := iIP.(net.IP) if !ok { panic("Legacy bridgeIP field in engine does not cast to net.IP") } dockerfile := constructDockerfile(context.dockerfile, ip, port) buildfile := docker.NewBuildFile(mkServerFromEngine(eng, t), ioutil.Discard, ioutil.Discard, false, true, false, ioutil.Discard, utils.NewStreamFormatter(false), nil, nil) _, err = buildfile.Build(context.Archive(dockerfile, t)) if err == nil { t.Log("Error should not be nil") t.Fail() } if err.Error() != "foo: no such file or directory" { t.Logf("Error message is not expected: %s", err.Error()) t.Fail() } }
func postBuild(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error { if version < 1.3 { return fmt.Errorf("Multipart upload for build is no longer supported. Please upgrade your docker client.") } remoteURL := r.FormValue("remote") repoName := r.FormValue("t") rawSuppressOutput := r.FormValue("q") rawNoCache := r.FormValue("nocache") rawRm := r.FormValue("rm") repoName, tag := utils.ParseRepositoryTag(repoName) var context io.Reader if remoteURL == "" { context = r.Body } else if utils.IsGIT(remoteURL) { if !strings.HasPrefix(remoteURL, "git://") { remoteURL = "https://" + remoteURL } root, err := ioutil.TempDir("", "docker-build-git") if err != nil { return err } defer os.RemoveAll(root) if output, err := exec.Command("git", "clone", remoteURL, root).CombinedOutput(); err != nil { return fmt.Errorf("Error trying to use git: %s (%s)", err, output) } c, err := archive.Tar(root, archive.Bzip2) if err != nil { return err } context = c } else if utils.IsURL(remoteURL) { f, err := utils.Download(remoteURL) if err != nil { return err } defer f.Body.Close() dockerFile, err := ioutil.ReadAll(f.Body) if err != nil { return err } c, err := MkBuildContext(string(dockerFile), nil) if err != nil { return err } context = c } suppressOutput, err := getBoolParam(rawSuppressOutput) if err != nil { return err } noCache, err := getBoolParam(rawNoCache) if err != nil { return err } rm, err := getBoolParam(rawRm) if err != nil { return err } if version >= 1.8 { w.Header().Set("Content-Type", "application/json") } sf := utils.NewStreamFormatter(version >= 1.8) b := NewBuildFile(srv, &StdoutFormater{ Writer: utils.NewWriteFlusher(w), StreamFormatter: sf, }, &StderrFormater{ Writer: utils.NewWriteFlusher(w), StreamFormatter: sf, }, !suppressOutput, !noCache, rm, utils.NewWriteFlusher(w), sf) id, err := b.Build(context) if err != nil { if sf.Used() { w.Write(sf.FormatError(err)) return nil } return fmt.Errorf("Error build: %s", err) } if repoName != "" { srv.runtime.repositories.Set(repoName, tag, id, false) } return nil }