// Create a test container, start it, wait for it to complete, destroy it, // and return its standard output as a string. // The image name (eg. the XXX in []string{"-i", "-t", "XXX", "bash"}, is dynamically replaced by the current test image. // If t is not nil, call t.Fatal() at the first error. Otherwise return errors normally. func runContainer(eng *engine.Engine, r *docker.Runtime, args []string, t *testing.T) (output string, err error) { defer func() { if err != nil && t != nil { t.Fatal(err) } }() container, hc, err := mkContainer(r, args, t) if err != nil { return "", err } defer r.Destroy(container) stdout, err := container.StdoutPipe() if err != nil { return "", err } defer stdout.Close() job := eng.Job("start", container.ID) if err := job.ImportEnv(hc); err != nil { return "", err } if err := job.Run(); err != nil { return "", err } container.Wait() data, err := ioutil.ReadAll(stdout) if err != nil { return "", err } output = string(data) return }
func postCommit(eng *engine.Engine, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error { if err := parseForm(r); err != nil { return err } var ( config engine.Env env engine.Env job = eng.Job("commit", r.Form.Get("container")) ) if err := config.Import(r.Body); err != nil { utils.Errorf("%s", err) } job.Setenv("repo", r.Form.Get("repo")) job.Setenv("tag", r.Form.Get("tag")) job.Setenv("author", r.Form.Get("author")) job.Setenv("comment", r.Form.Get("comment")) job.SetenvSubEnv("config", &config) var id string job.Stdout.AddString(&id) if err := job.Run(); err != nil { return err } env.Set("Id", id) return writeJSON(w, http.StatusCreated, env) }
func wsContainersAttach(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 err := eng.Job("inspect", vars["name"], "container").Run(); err != nil { return err } h := websocket.Handler(func(ws *websocket.Conn) { defer ws.Close() job := eng.Job("attach", vars["name"]) job.Setenv("logs", r.Form.Get("logs")) job.Setenv("stream", r.Form.Get("stream")) job.Setenv("stdin", r.Form.Get("stdin")) job.Setenv("stdout", r.Form.Get("stdout")) job.Setenv("stderr", r.Form.Get("stderr")) job.Stdin.Add(ws) job.Stdout.Add(ws) job.Stderr.Set(ws) if err := job.Run(); err != nil { utils.Errorf("Error: %s", err) } }) h.ServeHTTP(w, r) return nil }
func postContainersCreate(eng *engine.Engine, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error { if err := parseForm(r); err != nil { return nil } var ( out engine.Env job = eng.Job("create", r.Form.Get("name")) outWarnings []string outId string warnings = bytes.NewBuffer(nil) ) if err := job.DecodeEnv(r.Body); err != nil { return err } // Read container ID from the first line of stdout job.Stdout.AddString(&outId) // Read warnings from stderr job.Stderr.Add(warnings) if err := job.Run(); err != nil { return err } // Parse warnings from stderr scanner := bufio.NewScanner(warnings) for scanner.Scan() { outWarnings = append(outWarnings, scanner.Text()) } out.Set("Id", outId) out.SetList("Warnings", outWarnings) return writeJSON(w, http.StatusCreated, out) }
func postContainersCopy(eng *engine.Engine, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error { if vars == nil { return fmt.Errorf("Missing parameter") } var copyData engine.Env if contentType := r.Header.Get("Content-Type"); contentType == "application/json" { if err := copyData.Decode(r.Body); err != nil { return err } } else { return fmt.Errorf("Content-Type not supported: %s", contentType) } if copyData.Get("Resource") == "" { return fmt.Errorf("Path cannot be empty") } if copyData.Get("Resource")[0] == '/' { copyData.Set("Resource", copyData.Get("Resource")[1:]) } job := eng.Job("container_copy", vars["name"], copyData.Get("Resource")) job.Stdout.Add(w) if err := job.Run(); err != nil { utils.Errorf("%s", err.Error()) } return nil }
func getImagesSearch(eng *engine.Engine, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error { if err := parseForm(r); err != nil { return err } var ( authEncoded = r.Header.Get("X-Registry-Auth") authConfig = &auth.AuthConfig{} metaHeaders = map[string][]string{} ) if authEncoded != "" { authJson := base64.NewDecoder(base64.URLEncoding, strings.NewReader(authEncoded)) if err := json.NewDecoder(authJson).Decode(authConfig); err != nil { // for a search 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{} } } for k, v := range r.Header { if strings.HasPrefix(k, "X-Meta-") { metaHeaders[k] = v } } var job = eng.Job("search", r.Form.Get("term")) job.SetenvJson("metaHeaders", metaHeaders) job.SetenvJson("authConfig", authConfig) job.Stdout.Add(w) return job.Run() }
func getImagesByName(eng *engine.Engine, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error { if vars == nil { return fmt.Errorf("Missing parameter") } var job = eng.Job("inspect", vars["name"], "image") job.Stdout.Add(w) job.SetenvBool("conflict", true) //conflict=true to detect conflict between containers and images in the job return job.Run() }
func getContainersChanges(eng *engine.Engine, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error { if vars == nil { return fmt.Errorf("Missing parameter") } var job = eng.Job("changes", vars["name"]) job.Stdout.Add(w) return job.Run() }
func postBuild(eng *engine.Engine, 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 ( 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 < 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 >= 1.8 { w.Header().Set("Content-Type", "application/json") job.SetenvBool("json", true) } 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")) if err := job.Run(); err != nil { if !job.Stdout.Used() { return err } sf := utils.NewStreamFormatter(version >= 1.8) w.Write(sf.FormatError(err)) } return nil }
func generateImage(name string, eng *engine.Engine) error { archive, err := fakeTar() if err != nil { return err } job := eng.Job("import", "-", "repo", name) job.Stdin.Add(archive) job.SetenvBool("json", true) return job.Run() }
func getContainersExport(eng *engine.Engine, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error { if vars == nil { return fmt.Errorf("Missing parameter") } job := eng.Job("export", vars["name"]) job.Stdout.Add(w) if err := job.Run(); err != nil { return err } return nil }
func getEvents(eng *engine.Engine, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error { if err := parseForm(r); err != nil { return err } w.Header().Set("Content-Type", "application/json") var job = eng.Job("events", r.RemoteAddr) job.Stdout.Add(utils.NewWriteFlusher(w)) job.Setenv("since", r.Form.Get("since")) return job.Run() }
func createNamedTestContainer(eng *engine.Engine, config *docker.Config, f utils.Fataler, name string) (shortId string) { job := eng.Job("create", name) if err := job.ImportEnv(config); err != nil { f.Fatal(err) } job.Stdout.AddString(&shortId) if err := job.Run(); err != nil { f.Fatal(err) } return }
func getImagesGet(eng *engine.Engine, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error { if vars == nil { return fmt.Errorf("Missing parameter") } if version > 1.0 { w.Header().Set("Content-Type", "application/x-tar") } job := eng.Job("image_export", vars["name"]) job.Stdout.Add(w) return job.Run() }
func postContainersResize(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 err := eng.Job("resize", vars["name"], r.Form.Get("h"), r.Form.Get("w")).Run(); err != nil { return err } return nil }
func deleteImages(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") } var job = eng.Job("image_delete", vars["name"]) job.Stdout.Add(w) job.SetenvBool("autoPrune", version > 1.1) return job.Run() }
func getImagesHistory(eng *engine.Engine, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error { if vars == nil { return fmt.Errorf("Missing parameter") } var job = eng.Job("history", vars["name"]) job.Stdout.Add(w) if err := job.Run(); err != nil { return err } return nil }
// Creates an image from Pull or from Import func postImagesCreate(eng *engine.Engine, 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 = 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 = 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 }
func getImages(eng *engine.Engine, t *testing.T, all bool, filter string) *engine.Table { job := eng.Job("images") job.SetenvBool("all", all) job.Setenv("filter", filter) images, err := job.Stdout.AddListTable() if err != nil { t.Fatal(err) } if err := job.Run(); err != nil { t.Fatal(err) } return images }
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) 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 }
func postContainersStop(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") } job := eng.Job("stop", vars["name"]) job.Setenv("t", r.Form.Get("t")) if err := job.Run(); err != nil { return err } w.WriteHeader(http.StatusNoContent) return nil }
func getContainersTop(eng *engine.Engine, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error { if version < 1.4 { return fmt.Errorf("top was improved a lot since 1.3, Please upgrade your docker client.") } if vars == nil { return fmt.Errorf("Missing parameter") } if err := parseForm(r); err != nil { return err } job := eng.Job("top", vars["name"], r.Form.Get("ps_args")) job.Stdout.Add(w) return job.Run() }
func postImagesTag(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") } job := eng.Job("tag", vars["name"], r.Form.Get("repo"), r.Form.Get("tag")) job.Setenv("force", r.Form.Get("force")) if err := job.Run(); err != nil { return err } w.WriteHeader(http.StatusCreated) return nil }
func deleteContainers(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") } job := eng.Job("container_delete", vars["name"]) job.Setenv("removeVolume", r.Form.Get("v")) job.Setenv("removeLink", r.Form.Get("link")) if err := job.Run(); err != nil { return err } w.WriteHeader(http.StatusNoContent) return nil }
func postContainersKill(eng *engine.Engine, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error { if vars == nil { return fmt.Errorf("Missing parameter") } if err := parseForm(r); err != nil { return err } job := eng.Job("kill", vars["name"]) if sig := r.Form.Get("signal"); sig != "" { job.Args = append(job.Args, sig) } if err := job.Run(); err != nil { return err } w.WriteHeader(http.StatusNoContent) return nil }
func getImagesJSON(eng *engine.Engine, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error { if err := parseForm(r); err != nil { return err } var ( err error outs *engine.Table job = eng.Job("images") ) job.Setenv("filter", r.Form.Get("filter")) job.Setenv("all", r.Form.Get("all")) if version >= 1.7 { job.Stdout.Add(w) } else if outs, err = job.Stdout.AddListTable(); err != nil { return err } if err := job.Run(); err != nil { return err } if version < 1.7 && outs != nil { // Convert to legacy format outsLegacy := engine.NewTable("Created", 0) for _, out := range outs.Data { for _, repoTag := range out.GetList("RepoTags") { parts := strings.Split(repoTag, ":") outLegacy := &engine.Env{} outLegacy.Set("Repository", parts[0]) outLegacy.Set("Tag", parts[1]) outLegacy.Set("ID", out.Get("ID")) outLegacy.SetInt64("Created", out.GetInt64("Created")) outLegacy.SetInt64("Size", out.GetInt64("Size")) outLegacy.SetInt64("VirtualSize", out.GetInt64("VirtualSize")) outsLegacy.Add(outLegacy) } } if _, err := outsLegacy.WriteListTo(w); err != nil { return err } } return nil }
func postContainersStart(eng *engine.Engine, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error { if vars == nil { return fmt.Errorf("Missing parameter") } name := vars["name"] job := eng.Job("start", name) // allow a nil body for backwards compatibility if r.Body != nil { if MatchesContentType(r.Header.Get("Content-Type"), "application/json") { if err := job.DecodeEnv(r.Body); err != nil { return err } } } if err := job.Run(); err != nil { return err } w.WriteHeader(http.StatusNoContent) return nil }
// FIXME: cleanup and nuke are redundant. func cleanup(eng *engine.Engine, t *testing.T) error { runtime := mkRuntimeFromEngine(eng, t) for _, container := range runtime.List() { container.Kill() runtime.Destroy(container) } job := eng.Job("images") images, err := job.Stdout.AddTable() if err != nil { t.Fatal(err) } if err := job.Run(); err != nil { t.Fatal(err) } for _, image := range images.Data { if image.Get("Id") != unitTestImageID { eng.Job("image_delete", image.Get("Id")).Run() } } return nil }
func postContainersWait(eng *engine.Engine, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error { if vars == nil { return fmt.Errorf("Missing parameter") } var ( env engine.Env status string job = eng.Job("wait", vars["name"]) ) job.Stdout.AddString(&status) if err := job.Run(); err != nil { return err } // Parse a 16-bit encoded integer to map typical unix exit status. _, err := strconv.ParseInt(status, 10, 16) if err != nil { return err } env.Set("StatusCode", status) return writeJSON(w, http.StatusOK, env) }
func postAuth(eng *engine.Engine, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error { var ( authConfig, err = ioutil.ReadAll(r.Body) job = eng.Job("auth") status string ) if err != nil { return err } job.Setenv("authConfig", string(authConfig)) job.Stdout.AddString(&status) if err = job.Run(); err != nil { return err } if status != "" { var env engine.Env env.Set("Status", status) return writeJSON(w, http.StatusOK, env) } w.WriteHeader(http.StatusNoContent) return nil }