// 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 (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.outOld, b.sf, b.authConfig, 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 }
func (c *ImageService) Pull(image string) error { name, tag := utils.ParseRepositoryTag(image) if len(tag) == 0 { tag = DEFAULTTAG } return c.PullTag(name, tag) }
func (cli *DockerCli) CmdPull(args ...string) error { cmd := Subcmd("pull", "NAME", "Pull an image or a repository from the registry") tag := cmd.String("t", "", "Download tagged image in repository") if err := cmd.Parse(args); err != nil { return nil } if cmd.NArg() != 1 { cmd.Usage() return nil } remote, parsedTag := utils.ParseRepositoryTag(cmd.Arg(0)) if *tag == "" { *tag = parsedTag } v := url.Values{} v.Set("fromImage", remote) v.Set("tag", *tag) if err := cli.stream("POST", "/images/create?"+v.Encode(), nil, cli.out); err != nil { return err } return nil }
func (srv *Server) ContainerCreate(config *Config) (string, error) { if config.Memory != 0 && config.Memory < 524288 { return "", fmt.Errorf("Memory limit must be given in bytes (minimum 524288 bytes)") } if config.Memory > 0 && !srv.runtime.capabilities.MemoryLimit { config.Memory = 0 } if config.Memory > 0 && !srv.runtime.capabilities.SwapLimit { config.MemorySwap = -1 } container, err := srv.runtime.Create(config) if err != nil { if srv.runtime.graph.IsNotExist(err) { _, tag := utils.ParseRepositoryTag(config.Image) if tag == "" { tag = DEFAULTTAG } return "", fmt.Errorf("No such image: %s (tag: %s)", config.Image, tag) } return "", err } srv.LogEvent("create", container.ShortID(), srv.runtime.repositories.ImageName(container.Image)) return container.ShortID(), nil }
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) pullRegistryAuth := b.authConfig if len(b.configFile.Configs) > 0 { // The request came with a full auth config file, we prefer to use that endpoint, _, err := registry.ResolveRepositoryName(remote) if err != nil { return err } resolvedAuth := b.configFile.ResolveAuthConfig(endpoint) pullRegistryAuth = &resolvedAuth } job := b.srv.Eng.Job("pull", remote, tag) job.SetenvBool("json", b.sf.Json()) job.SetenvBool("parallel", true) job.SetenvJson("authConfig", pullRegistryAuth) job.Stdout.Add(b.outOld) if err := job.Run(); 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 = &runconfig.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="+runtime.DefaultPathEnv) } // Process ONBUILD triggers if they exist if nTriggers := len(b.config.OnBuild); nTriggers != 0 { fmt.Fprintf(b.errStream, "# Executing %d build triggers\n", nTriggers) } for n, step := range b.config.OnBuild { splitStep := strings.Split(step, " ") stepInstruction := strings.ToUpper(strings.Trim(splitStep[0], " ")) switch stepInstruction { case "ONBUILD": return fmt.Errorf("Source image contains forbidden chained `ONBUILD ONBUILD` trigger: %s", step) case "MAINTAINER", "FROM": return fmt.Errorf("Source image contains forbidden %s trigger: %s", stepInstruction, step) } if err := b.BuildStep(fmt.Sprintf("onbuild-%d", n), step); err != nil { return err } } b.config.OnBuild = []string{} return nil }
func (cli *DockerCli) CmdPull(args ...string) error { cmd := Subcmd("pull", "NAME", "Pull an image or a repository from the registry") tag := cmd.String("t", "", "Download tagged image in repository") if err := cmd.Parse(args); err != nil { return nil } if cmd.NArg() != 1 { cmd.Usage() return nil } remote, parsedTag := utils.ParseRepositoryTag(cmd.Arg(0)) if *tag == "" { *tag = parsedTag } // Resolve the Repository name from fqn to endpoint + name endpoint, _, err := registry.ResolveRepositoryName(remote) if err != nil { return err } cli.LoadConfigFile() // Resolve the Auth config relevant for this server authConfig := cli.configFile.ResolveAuthConfig(endpoint) v := url.Values{} v.Set("fromImage", remote) v.Set("tag", *tag) pull := func(authConfig auth.AuthConfig) error { buf, err := json.Marshal(authConfig) if err != nil { return err } registryAuthHeader := []string{ base64.URLEncoding.EncodeToString(buf), } return cli.stream("POST", "/images/create?"+v.Encode(), nil, cli.out, map[string][]string{ "X-Registry-Auth": registryAuthHeader, }) } if err := pull(authConfig); err != nil { if err.Error() == registry.ErrLoginRequired.Error() { fmt.Fprintln(cli.out, "\nPlease login prior to push:") if err := cli.CmdLogin(endpoint); err != nil { return err } authConfig := cli.configFile.ResolveAuthConfig(endpoint) return pull(authConfig) } return 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) { remote, tag := utils.ParseRepositoryTag(name) pullRegistryAuth := b.authConfig if len(b.configFile.Configs) > 0 { // The request came with a full auth config file, we prefer to use that endpoint, _, err := registry.ResolveRepositoryName(remote) if err != nil { return err } resolvedAuth := b.configFile.ResolveAuthConfig(endpoint) pullRegistryAuth = &resolvedAuth } job := b.srv.Eng.Job("pull", remote, tag) job.SetenvBool("json", b.sf.Json()) job.SetenvBool("parallel", true) job.SetenvJson("authConfig", pullRegistryAuth) job.Stdout.Add(b.outOld) if err := job.Run(); 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 = &runconfig.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") } // Process ONBUILD triggers if they exist if nTriggers := len(b.config.OnBuild); nTriggers != 0 { fmt.Fprintf(b.errStream, "# Executing %d build triggers\n", nTriggers) } for n, step := range b.config.OnBuild { if err := b.BuildStep(fmt.Sprintf("onbuild-%d", n), step); err != nil { return err } } b.config.OnBuild = []string{} return nil }
func (srv *Server) ImageDelete(name string, autoPrune bool) ([]APIRmi, error) { img, err := srv.runtime.repositories.LookupImage(name) if err != nil { return nil, fmt.Errorf("No such image: %s", name) } if !autoPrune { if err := srv.runtime.graph.Delete(img.ID); err != nil { return nil, fmt.Errorf("Error deleting image %s: %s", name, err) } return nil, nil } name, tag := utils.ParseRepositoryTag(name) return srv.deleteImage(img, name, tag) }
// CmdTag assigns a new name and tag to an existing image. If the tag already exists, // it is changed and the image previously referenced by the tag loses that reference. // This may cause the old image to be garbage-collected if its reference count reaches zero. // // Syntax: image_tag NEWNAME OLDNAME // Example: image_tag shykes/myapp:latest shykes/myapp:1.42.0 func (s *TagStore) CmdTag(job *engine.Job) engine.Status { if len(job.Args) != 2 { return job.Errorf("usage: %s NEWNAME OLDNAME", job.Name) } var ( newName = job.Args[0] oldName = job.Args[1] ) newRepo, newTag := utils.ParseRepositoryTag(newName) // FIXME: Set should either parse both old and new name, or neither. // the current prototype is inconsistent. if err := s.Set(newRepo, newTag, oldName, true); err != nil { return job.Error(err) } return engine.StatusOK }
func (store *TagStore) LookupImage(name string) (*Image, error) { // FIXME: standardize on returning nil when the image doesn't exist, and err for everything else // (so we can pass all errors here) repos, tag := utils.ParseRepositoryTag(name) if tag == "" { tag = DEFAULTTAG } img, err := store.GetImage(repos, tag) if err != nil { return nil, err } else if img == nil { if img, err = store.graph.Get(name); err != nil { return nil, err } } return img, nil }
func (srv *Server) deleteImage(img *Image, repoName, tag string) ([]APIRmi, error) { imgs := []APIRmi{} tags := []string{} //If delete by id, see if the id belong only to one repository if repoName == "" { for _, repoAndTag := range srv.runtime.repositories.ByID()[img.ID] { parsedRepo, parsedTag := utils.ParseRepositoryTag(repoAndTag) if repoName == "" || repoName == parsedRepo { repoName = parsedRepo if parsedTag != "" { tags = append(tags, parsedTag) } } else if repoName != parsedRepo { // the id belongs to multiple repos, like base:latest and user:test, // in that case return conflict return imgs, nil } } } else { tags = append(tags, tag) } //Untag the current image for _, tag := range tags { tagDeleted, err := srv.runtime.repositories.Delete(repoName, tag) if err != nil { return nil, err } if tagDeleted { imgs = append(imgs, APIRmi{Untagged: img.ID}) srv.LogEvent("untag", img.ID, "") } } if len(srv.runtime.repositories.ByID()[img.ID]) == 0 { if err := srv.deleteImageAndChildren(img.ID, &imgs); err != nil { if err != ErrImageReferenced { return imgs, err } } else if err := srv.deleteImageParents(img, &imgs); err != nil { if err != ErrImageReferenced { return imgs, err } } } return imgs, nil }
func (srv *Server) ContainerCreate(job *engine.Job) string { var name string if len(job.Args) == 1 { name = job.Args[0] } else if len(job.Args) > 1 { return fmt.Sprintf("Usage: %s ", job.Name) } var config Config if err := job.ExportEnv(&config); err != nil { return err.Error() } if config.Memory != 0 && config.Memory < 524288 { return "Minimum memory limit allowed is 512k" } if config.Memory > 0 && !srv.runtime.capabilities.MemoryLimit { config.Memory = 0 } if config.Memory > 0 && !srv.runtime.capabilities.SwapLimit { config.MemorySwap = -1 } container, buildWarnings, err := srv.runtime.Create(&config, name) if err != nil { if srv.runtime.graph.IsNotExist(err) { _, tag := utils.ParseRepositoryTag(config.Image) if tag == "" { tag = DEFAULTTAG } return fmt.Sprintf("No such image: %s (tag: %s)", config.Image, tag) } return err.Error() } srv.LogEvent("create", container.ID, srv.runtime.repositories.ImageName(container.Image)) // FIXME: this is necessary because runtime.Create might return a nil container // with a non-nil error. This should not happen! Once it's fixed we // can remove this workaround. if container != nil { job.Printf("%s\n", container.ID) } for _, warning := range buildWarnings { job.Errorf("%s\n", warning) } return "0" }
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 getImagesLookup(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error { if vars == nil { return fmt.Errorf("Missing parameter") } name := vars["name"] parsedRepo, parsedTag := utils.ParseRepositoryTag(name) if parsedTag != "" { imgs, err := srv.LookupImage(parsedRepo, parsedTag) if err != nil { return err } if imgs != nil { if len(imgs) != 0 { return writeJSON(w, http.StatusOK, imgs[0]) } } } w.WriteHeader(http.StatusNotFound) 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) { remote, tag := utils.ParseRepositoryTag(name) pullRegistryAuth := b.authConfig if len(b.configFile.Configs) > 0 { // The request came with a full auth config file, we prefer to use that endpoint, _, err := registry.ResolveRepositoryName(remote) if err != nil { return err } resolvedAuth := b.configFile.ResolveAuthConfig(endpoint) pullRegistryAuth = &resolvedAuth } job := b.srv.Eng.Job("pull", remote, tag) job.SetenvBool("json", b.sf.Json()) job.SetenvBool("parallel", true) job.SetenvJson("authConfig", pullRegistryAuth) job.Stdout.Add(b.outOld) if err := job.Run(); 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 }
func (srv *Server) ContainerCreate(job *engine.Job) string { var name string if len(job.Args) == 1 { name = job.Args[0] } else if len(job.Args) > 1 { return fmt.Sprintf("Usage: %s ", job.Name) } var config Config if err := job.ExportEnv(&config); err != nil { return err.Error() } if config.Memory != 0 && config.Memory < 524288 { return "Minimum memory limit allowed is 512k" } if config.Memory > 0 && !srv.runtime.capabilities.MemoryLimit { config.Memory = 0 } if config.Memory > 0 && !srv.runtime.capabilities.SwapLimit { config.MemorySwap = -1 } container, buildWarnings, err := srv.runtime.Create(&config, name) if err != nil { if srv.runtime.graph.IsNotExist(err) { _, tag := utils.ParseRepositoryTag(config.Image) if tag == "" { tag = DEFAULTTAG } return fmt.Sprintf("No such image: %s (tag: %s)", config.Image, tag) } return err.Error() } srv.LogEvent("create", container.ID, srv.runtime.repositories.ImageName(container.Image)) job.Printf("%s\n", container.ID) for _, warning := range buildWarnings { job.Errorf("%s\n", warning) } return "0" }
func (srv *Server) ImageDelete(name string, autoPrune bool) ([]APIRmi, error) { img, err := srv.runtime.repositories.LookupImage(name) if err != nil { return nil, fmt.Errorf("No such image: %s", name) } if !autoPrune { if err := srv.runtime.graph.Delete(img.ID); err != nil { return nil, fmt.Errorf("Cannot delete image %s: %s", name, err) } return nil, nil } // Prevent deletion if image is used by a running container for _, container := range srv.runtime.List() { if container.State.Running { parent, err := srv.runtime.repositories.LookupImage(container.Image) if err != nil { return nil, err } if err := parent.WalkHistory(func(p *Image) error { if img.ID == p.ID { return fmt.Errorf("Conflict, cannot delete %s because the running container %s is using it", name, container.ID) } return nil }); err != nil { return nil, err } } } if strings.Contains(img.ID, name) { //delete via ID return srv.deleteImage(img, "", "") } name, tag := utils.ParseRepositoryTag(name) return srv.deleteImage(img, name, tag) }
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, ioutil.Discard) 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 } b := NewBuildFile(srv, utils.NewWriteFlusher(w), !suppressOutput, !noCache, rm) id, err := b.Build(context) if err != nil { return fmt.Errorf("Error build: %s", err) } if repoName != "" { srv.runtime.repositories.Set(repoName, tag, id, false) } return nil }
func (cli *DockerCli) CmdRun(args ...string) error { config, hostConfig, cmd, err := ParseRun(args, nil) if err != nil { return err } if config.Image == "" { cmd.Usage() return nil } var containerIDFile *os.File if len(hostConfig.ContainerIDFile) > 0 { if _, err := ioutil.ReadFile(hostConfig.ContainerIDFile); err == nil { return fmt.Errorf("cid file found, make sure the other container isn't running or delete %s", hostConfig.ContainerIDFile) } containerIDFile, err = os.Create(hostConfig.ContainerIDFile) if err != nil { return fmt.Errorf("failed to create the container ID file: %s", err) } defer containerIDFile.Close() } //create the container body, statusCode, err := cli.call("POST", "/containers/create", config) //if image not found try to pull it if statusCode == 404 { _, tag := utils.ParseRepositoryTag(config.Image) if tag == "" { tag = DEFAULTTAG } fmt.Fprintf(cli.err, "Unable to find image '%s' (tag: %s) locally\n", config.Image, tag) v := url.Values{} repos, tag := utils.ParseRepositoryTag(config.Image) v.Set("fromImage", repos) v.Set("tag", tag) // Resolve the Repository name from fqn to endpoint + name var endpoint string endpoint, _, err = registry.ResolveRepositoryName(repos) if err != nil { return err } // Load the auth config file, to be able to pull the image cli.LoadConfigFile() // Resolve the Auth config relevant for this server authConfig := cli.configFile.ResolveAuthConfig(endpoint) buf, err := json.Marshal(authConfig) if err != nil { return err } registryAuthHeader := []string{ base64.URLEncoding.EncodeToString(buf), } err = cli.stream("POST", "/images/create?"+v.Encode(), nil, cli.err, map[string][]string{ "X-Registry-Auth": registryAuthHeader, }) if err != nil { return err } body, _, err = cli.call("POST", "/containers/create", config) if err != nil { return err } } if err != nil { return err } runResult := &APIRun{} err = json.Unmarshal(body, runResult) if err != nil { return err } for _, warning := range runResult.Warnings { fmt.Fprintf(cli.err, "WARNING: %s\n", warning) } if len(hostConfig.ContainerIDFile) > 0 { if _, err = containerIDFile.WriteString(runResult.ID); err != nil { return fmt.Errorf("failed to write the container ID to the file: %s", err) } } //start the container if _, _, err = cli.call("POST", "/containers/"+runResult.ID+"/start", hostConfig); err != nil { return err } var wait chan struct{} if !config.AttachStdout && !config.AttachStderr { // Make this asynchrone in order to let the client write to stdin before having to read the ID wait = make(chan struct{}) go func() { defer close(wait) fmt.Fprintf(cli.out, "%s\n", runResult.ID) }() } if config.AttachStdin || config.AttachStdout || config.AttachStderr { if config.Tty { if err := cli.monitorTtySize(runResult.ID); err != nil { utils.Debugf("Error monitoring TTY size: %s\n", err) } } v := url.Values{} v.Set("logs", "1") v.Set("stream", "1") var out io.Writer if config.AttachStdin { v.Set("stdin", "1") } if config.AttachStdout { v.Set("stdout", "1") out = cli.out } if config.AttachStderr { v.Set("stderr", "1") out = cli.out } signals := make(chan os.Signal, 1) signal.Notify(signals, syscall.SIGINT, syscall.SIGTERM) go func() { for sig := range signals { fmt.Printf("\nReceived signal: %s; cleaning up\n", sig) if err := cli.CmdStop("-t", "4", runResult.ID); err != nil { fmt.Printf("failed to stop container: %v", err) } } }() if err := cli.hijack("POST", "/containers/"+runResult.ID+"/attach?"+v.Encode(), config.Tty, cli.in, out); err != nil { utils.Debugf("Error hijack: %s", err) return err } } if !config.AttachStdout && !config.AttachStderr { // Detached mode <-wait } else { status, err := getExitCode(cli, runResult.ID) if err != nil { return err } if status != 0 { return &utils.StatusError{Status: status} } } return nil }
func (cli *DockerCli) CmdRun(args ...string) error { config, hostConfig, cmd, err := ParseRun(args, nil) if err != nil { return err } if config.Image == "" { cmd.Usage() return nil } var containerIDFile *os.File if len(hostConfig.ContainerIDFile) > 0 { if _, err := ioutil.ReadFile(hostConfig.ContainerIDFile); err == nil { return fmt.Errorf("cid file found, make sure the other container isn't running or delete %s", hostConfig.ContainerIDFile) } containerIDFile, err = os.Create(hostConfig.ContainerIDFile) if err != nil { return fmt.Errorf("failed to create the container ID file: %s", err) } defer containerIDFile.Close() } //create the container body, statusCode, err := cli.call("POST", "/containers/create", config) //if image not found try to pull it if statusCode == 404 { v := url.Values{} repos, tag := utils.ParseRepositoryTag(config.Image) v.Set("fromImage", repos) v.Set("tag", tag) err = cli.stream("POST", "/images/create?"+v.Encode(), nil, cli.err) if err != nil { return err } body, _, err = cli.call("POST", "/containers/create", config) if err != nil { return err } } if err != nil { return err } runResult := &APIRun{} err = json.Unmarshal(body, runResult) if err != nil { return err } for _, warning := range runResult.Warnings { fmt.Fprintf(cli.err, "WARNING: %s\n", warning) } if len(hostConfig.ContainerIDFile) > 0 { if _, err = containerIDFile.WriteString(runResult.ID); err != nil { return fmt.Errorf("failed to write the container ID to the file: %s", err) } } //start the container if _, _, err = cli.call("POST", "/containers/"+runResult.ID+"/start", hostConfig); err != nil { return err } var wait chan struct{} if !config.AttachStdout && !config.AttachStderr { // Make this asynchrone in order to let the client write to stdin before having to read the ID wait = make(chan struct{}) go func() { defer close(wait) fmt.Fprintf(cli.out, "%s\n", runResult.ID) }() } if config.AttachStdin || config.AttachStdout || config.AttachStderr { if config.Tty { if err := cli.monitorTtySize(runResult.ID); err != nil { utils.Debugf("Error monitoring TTY size: %s\n", err) } } v := url.Values{} v.Set("logs", "1") v.Set("stream", "1") if config.AttachStdin { v.Set("stdin", "1") } if config.AttachStdout { v.Set("stdout", "1") } if config.AttachStderr { v.Set("stderr", "1") } signals := make(chan os.Signal, 1) signal.Notify(signals, syscall.SIGINT, syscall.SIGTERM) go func() { for sig := range signals { fmt.Printf("\nReceived signal: %s; cleaning up\n", sig) if err := cli.CmdStop("-t", "4", runResult.ID); err != nil { fmt.Printf("failed to stop container: %v", err) } } }() if err := cli.hijack("POST", "/containers/"+runResult.ID+"/attach?"+v.Encode(), config.Tty, cli.in, cli.out); err != nil { utils.Debugf("Error hijack: %s", err) return err } } if !config.AttachStdout && !config.AttachStderr { <-wait } return nil }
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.") } var ( remoteURL = r.FormValue("remote") repoName = r.FormValue("t") rawSuppressOutput = r.FormValue("q") rawNoCache = r.FormValue("nocache") rawRm = r.FormValue("rm") authEncoded = r.Header.Get("X-Registry-Auth") authConfig = &auth.AuthConfig{} configFileEncoded = r.Header.Get("X-Registry-Config") configFile = &auth.ConfigFile{} tag string ) repoName, tag = utils.ParseRepositoryTag(repoName) // 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 < 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{} } } 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.Uncompressed) 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, authConfig, configFile) 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 }
func (cli *DockerCli) CmdRun(args ...string) error { config, hostConfig, cmd, err := ParseRun(args, nil) if err != nil { return err } if config.Image == "" { cmd.Usage() return nil } //create the container body, statusCode, err := cli.call("POST", "/containers/create", config) //if image not found try to pull it if statusCode == 404 { v := url.Values{} repos, tag := utils.ParseRepositoryTag(config.Image) v.Set("fromImage", repos) v.Set("tag", tag) err = cli.stream("POST", "/images/create?"+v.Encode(), nil, cli.err) if err != nil { return err } body, _, err = cli.call("POST", "/containers/create", config) if err != nil { return err } } if err != nil { return err } runResult := &APIRun{} err = json.Unmarshal(body, runResult) if err != nil { return err } for _, warning := range runResult.Warnings { fmt.Fprintf(cli.err, "WARNING: %s\n", warning) } //start the container if _, _, err = cli.call("POST", "/containers/"+runResult.ID+"/start", hostConfig); err != nil { return err } var wait chan struct{} if !config.AttachStdout && !config.AttachStderr { // Make this asynchrone in order to let the client write to stdin before having to read the ID wait = make(chan struct{}) go func() { defer close(wait) fmt.Fprintf(cli.out, "%s\n", runResult.ID) }() } if config.AttachStdin || config.AttachStdout || config.AttachStderr { if config.Tty { if err := cli.monitorTtySize(runResult.ID); err != nil { return err } } v := url.Values{} v.Set("logs", "1") v.Set("stream", "1") if config.AttachStdin { v.Set("stdin", "1") } if config.AttachStdout { v.Set("stdout", "1") } if config.AttachStderr { v.Set("stderr", "1") } if err := cli.hijack("POST", "/containers/"+runResult.ID+"/attach?"+v.Encode(), config.Tty, cli.in, cli.out); err != nil { utils.Debugf("Error hijack: %s", err) return err } } if !config.AttachStdout && !config.AttachStderr { <-wait } return nil }