func (s *router) wsContainersAttach(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { if err := httputils.ParseForm(r); err != nil { return err } containerName := vars["name"] if !s.daemon.Exists(containerName) { return derr.ErrorCodeNoSuchContainer.WithArgs(containerName) } h := websocket.Handler(func(ws *websocket.Conn) { defer ws.Close() wsAttachWithLogsConfig := &daemon.ContainerWsAttachWithLogsConfig{ InStream: ws, OutStream: ws, ErrStream: ws, Logs: httputils.BoolValue(r, "logs"), Stream: httputils.BoolValue(r, "stream"), } if err := s.daemon.ContainerWsAttachWithLogs(containerName, wsAttachWithLogsConfig); err != nil { logrus.Errorf("Error attaching websocket: %s", err) } }) ws := websocket.Server{Handler: h, Handshake: nil} ws.ServeHTTP(w, r) return nil }
func (s *router) deleteContainers(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { if err := httputils.ParseForm(r); err != nil { return err } if vars == nil { return fmt.Errorf("Missing parameter") } name := vars["name"] config := &daemon.ContainerRmConfig{ ForceRemove: httputils.BoolValue(r, "force"), RemoveVolume: httputils.BoolValue(r, "v"), RemoveLink: httputils.BoolValue(r, "link"), } if err := s.daemon.ContainerRm(name, config); err != nil { // Force a 404 for the empty string if strings.Contains(strings.ToLower(err.Error()), "prefix can't be empty") { return fmt.Errorf("no such id: \"\"") } return err } w.WriteHeader(http.StatusNoContent) return nil }
func (s *containerRouter) postContainersAttach(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { err := httputils.ParseForm(r) if err != nil { return err } containerName := vars["name"] _, upgrade := r.Header["Upgrade"] keys := []byte{} detachKeys := r.FormValue("detachKeys") if detachKeys != "" { keys, err = term.ToBytes(detachKeys) if err != nil { logrus.Warnf("Invalid escape keys provided (%s) using default : ctrl-p ctrl-q", detachKeys) } } attachWithLogsConfig := &daemon.ContainerAttachWithLogsConfig{ Hijacker: w.(http.Hijacker), Upgrade: upgrade, UseStdin: httputils.BoolValue(r, "stdin"), UseStdout: httputils.BoolValue(r, "stdout"), UseStderr: httputils.BoolValue(r, "stderr"), Logs: httputils.BoolValue(r, "logs"), Stream: httputils.BoolValue(r, "stream"), DetachKeys: keys, } return s.backend.ContainerAttachWithLogs(containerName, attachWithLogsConfig) }
func (s *containerRouter) getContainersJSON(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { if err := httputils.ParseForm(r); err != nil { return err } filter, err := filters.FromParam(r.Form.Get("filters")) if err != nil { return err } config := &types.ContainerListOptions{ All: httputils.BoolValue(r, "all"), Size: httputils.BoolValue(r, "size"), Since: r.Form.Get("since"), Before: r.Form.Get("before"), Filters: filter, } if tmpLimit := r.Form.Get("limit"); tmpLimit != "" { limit, err := strconv.Atoi(tmpLimit) if err != nil { return err } config.Limit = limit } containers, err := s.backend.Containers(config) if err != nil { return err } return httputils.WriteJSON(w, http.StatusOK, containers) }
func (s *containerRouter) getContainersLogs(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { if err := httputils.ParseForm(r); err != nil { return err } // Args are validated before the stream starts because when it starts we're // sending HTTP 200 by writing an empty chunk of data to tell the client that // daemon is going to stream. By sending this initial HTTP 200 we can't report // any error after the stream starts (i.e. container not found, wrong parameters) // with the appropriate status code. stdout, stderr := httputils.BoolValue(r, "stdout"), httputils.BoolValue(r, "stderr") if !(stdout || stderr) { return fmt.Errorf("Bad parameters: you must choose at least one stream") } var closeNotifier <-chan bool if notifier, ok := w.(http.CloseNotifier); ok { closeNotifier = notifier.CloseNotify() } containerName := vars["name"] if !s.backend.Exists(containerName) { return derr.ErrorCodeNoSuchContainer.WithArgs(containerName) } // write an empty chunk of data (this is to ensure that the // HTTP Response is sent immediately, even if the container has // not yet produced any data) w.WriteHeader(http.StatusOK) if flusher, ok := w.(http.Flusher); ok { flusher.Flush() } output := ioutils.NewWriteFlusher(w) defer output.Close() logsConfig := &backend.ContainerLogsConfig{ ContainerLogsOptions: types.ContainerLogsOptions{ Follow: httputils.BoolValue(r, "follow"), Timestamps: httputils.BoolValue(r, "timestamps"), Since: r.Form.Get("since"), Tail: r.Form.Get("tail"), ShowStdout: stdout, ShowStderr: stderr, }, OutStream: output, Stop: closeNotifier, } if err := s.backend.ContainerLogs(containerName, logsConfig); err != nil { // The client may be expecting all of the data we're sending to // be multiplexed, so send it through OutStream, which will // have been set up to handle that if needed. fmt.Fprintf(logsConfig.OutStream, "Error running logs job: %s\n", utils.GetErrorMessage(err)) } return nil }
func (s *containerRouter) postContainersAttach(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { err := httputils.ParseForm(r) if err != nil { return err } containerName := vars["name"] _, upgrade := r.Header["Upgrade"] keys := []byte{} detachKeys := r.FormValue("detachKeys") if detachKeys != "" { keys, err = term.ToBytes(detachKeys) if err != nil { logrus.Warnf("Invalid escape keys provided (%s) using default : ctrl-p ctrl-q", detachKeys) } } hijacker, ok := w.(http.Hijacker) if !ok { return derr.ErrorCodeNoHijackConnection.WithArgs(containerName) } setupStreams := func() (io.ReadCloser, io.Writer, io.Writer, error) { conn, _, err := hijacker.Hijack() if err != nil { return nil, nil, nil, err } // set raw mode conn.Write([]byte{}) if upgrade { fmt.Fprintf(conn, "HTTP/1.1 101 UPGRADED\r\nContent-Type: application/vnd.docker.raw-stream\r\nConnection: Upgrade\r\nUpgrade: tcp\r\n\r\n") } else { fmt.Fprintf(conn, "HTTP/1.1 200 OK\r\nContent-Type: application/vnd.docker.raw-stream\r\n\r\n") } closer := func() error { httputils.CloseStreams(conn) return nil } return ioutils.NewReadCloserWrapper(conn, closer), conn, conn, nil } attachConfig := &backend.ContainerAttachConfig{ GetStreams: setupStreams, UseStdin: httputils.BoolValue(r, "stdin"), UseStdout: httputils.BoolValue(r, "stdout"), UseStderr: httputils.BoolValue(r, "stderr"), Logs: httputils.BoolValue(r, "logs"), Stream: httputils.BoolValue(r, "stream"), DetachKeys: keys, MuxStreams: true, } return s.backend.ContainerAttach(containerName, attachConfig) }
func (s *containerRouter) wsContainersAttach(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { if err := httputils.ParseForm(r); err != nil { return err } containerName := vars["name"] var keys []byte var err error detachKeys := r.FormValue("detachKeys") if detachKeys != "" { keys, err = term.ToBytes(detachKeys) if err != nil { logrus.Warnf("Invalid escape keys provided (%s) using default : ctrl-p ctrl-q", detachKeys) } } done := make(chan struct{}) started := make(chan struct{}) setupStreams := func() (io.ReadCloser, io.Writer, io.Writer, error) { wsChan := make(chan *websocket.Conn) h := func(conn *websocket.Conn) { wsChan <- conn <-done } srv := websocket.Server{Handler: h, Handshake: nil} go func() { close(started) srv.ServeHTTP(w, r) }() conn := <-wsChan return conn, conn, conn, nil } attachConfig := &backend.ContainerAttachConfig{ GetStreams: setupStreams, Logs: httputils.BoolValue(r, "logs"), Stream: httputils.BoolValue(r, "stream"), DetachKeys: keys, UseStdin: true, UseStdout: true, UseStderr: true, MuxStreams: false, // TODO: this should be true since it's a single stream for both stdout and stderr } err = s.backend.ContainerAttach(containerName, attachConfig) close(done) select { case <-started: logrus.Errorf("Error attaching websocket: %s", err) return nil default: } return err }
func (sr *swarmRouter) getServiceLogs(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { if err := httputils.ParseForm(r); err != nil { return err } // Args are validated before the stream starts because when it starts we're // sending HTTP 200 by writing an empty chunk of data to tell the client that // daemon is going to stream. By sending this initial HTTP 200 we can't report // any error after the stream starts (i.e. container not found, wrong parameters) // with the appropriate status code. stdout, stderr := httputils.BoolValue(r, "stdout"), httputils.BoolValue(r, "stderr") if !(stdout || stderr) { return fmt.Errorf("Bad parameters: you must choose at least one stream") } serviceName := vars["id"] logsConfig := &backend.ContainerLogsConfig{ ContainerLogsOptions: basictypes.ContainerLogsOptions{ Follow: httputils.BoolValue(r, "follow"), Timestamps: httputils.BoolValue(r, "timestamps"), Since: r.Form.Get("since"), Tail: r.Form.Get("tail"), ShowStdout: stdout, ShowStderr: stderr, Details: httputils.BoolValue(r, "details"), }, OutStream: w, } if !logsConfig.Follow { return fmt.Errorf("Bad parameters: Only follow mode is currently supported") } if logsConfig.Details { return fmt.Errorf("Bad parameters: details is not currently supported") } chStarted := make(chan struct{}) if err := sr.backend.ServiceLogs(ctx, serviceName, logsConfig, chStarted); err != nil { select { case <-chStarted: // The client may be expecting all of the data we're sending to // be multiplexed, so send it through OutStream, which will // have been set up to handle that if needed. fmt.Fprintf(logsConfig.OutStream, "Error grabbing service logs: %v\n", err) default: return err } } return nil }
func (s *containerRouter) getContainersLogs(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { if err := httputils.ParseForm(r); err != nil { return err } // Args are validated before the stream starts because when it starts we're // sending HTTP 200 by writing an empty chunk of data to tell the client that // daemon is going to stream. By sending this initial HTTP 200 we can't report // any error after the stream starts (i.e. container not found, wrong parameters) // with the appropriate status code. stdout, stderr := httputils.BoolValue(r, "stdout"), httputils.BoolValue(r, "stderr") if !(stdout || stderr) { return fmt.Errorf("Bad parameters: you must choose at least one stream") } var closeNotifier <-chan bool if notifier, ok := w.(http.CloseNotifier); ok { closeNotifier = notifier.CloseNotify() } containerName := vars["name"] logsConfig := &backend.ContainerLogsConfig{ ContainerLogsOptions: types.ContainerLogsOptions{ Follow: httputils.BoolValue(r, "follow"), Timestamps: httputils.BoolValue(r, "timestamps"), Since: r.Form.Get("since"), Tail: r.Form.Get("tail"), ShowStdout: stdout, ShowStderr: stderr, }, OutStream: w, Stop: closeNotifier, } chStarted := make(chan struct{}) if err := s.backend.ContainerLogs(containerName, logsConfig, chStarted); err != nil { select { case <-chStarted: // The client may be expecting all of the data we're sending to // be multiplexed, so send it through OutStream, which will // have been set up to handle that if needed. fmt.Fprintf(logsConfig.OutStream, "Error running logs job: %s\n", utils.GetErrorMessage(err)) default: return err } } return nil }
// getContainersByName inspects containers configuration and serializes it as json. func (s *router) getContainersByName(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { displaySize := httputils.BoolValue(r, "size") if vars == nil { return fmt.Errorf("Missing parameter") } var json interface{} var err error version := httputils.VersionFromContext(ctx) switch { case version.LessThan("1.20"): json, err = s.daemon.ContainerInspectPre120(vars["name"]) case version.Equal("1.20"): json, err = s.daemon.ContainerInspect120(vars["name"]) default: json, err = s.daemon.ContainerInspect(vars["name"], displaySize) } if err != nil { return err } return httputils.WriteJSON(w, http.StatusOK, json) }
func (sr *swarmRouter) leaveCluster(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { if err := httputils.ParseForm(r); err != nil { return err } force := httputils.BoolValue(r, "force") return sr.backend.Leave(force) }
func (s *containerRouter) putContainersArchive(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { v, err := httputils.ArchiveFormValues(r, vars) if err != nil { return err } noOverwriteDirNonDir := httputils.BoolValue(r, "noOverwriteDirNonDir") return s.backend.ContainerExtractToDir(v.Name, v.Path, noOverwriteDirNonDir, r.Body) }
func (s *router) postContainersAttach(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { if err := httputils.ParseForm(r); err != nil { return err } containerName := vars["name"] if !s.daemon.Exists(containerName) { return derr.ErrorCodeNoSuchContainer.WithArgs(containerName) } if s.daemon.IsPaused(containerName) { return derr.ErrorCodePausedContainer.WithArgs(containerName) } inStream, outStream, err := httputils.HijackConnection(w) if err != nil { return err } defer httputils.CloseStreams(inStream, outStream) if _, ok := r.Header["Upgrade"]; ok { fmt.Fprintf(outStream, "HTTP/1.1 101 UPGRADED\r\nContent-Type: application/vnd.docker.raw-stream\r\nConnection: Upgrade\r\nUpgrade: tcp\r\n\r\n") } else { fmt.Fprintf(outStream, "HTTP/1.1 200 OK\r\nContent-Type: application/vnd.docker.raw-stream\r\n\r\n") } attachWithLogsConfig := &daemon.ContainerAttachWithLogsConfig{ InStream: inStream, OutStream: outStream, UseStdin: httputils.BoolValue(r, "stdin"), UseStdout: httputils.BoolValue(r, "stdout"), UseStderr: httputils.BoolValue(r, "stderr"), Logs: httputils.BoolValue(r, "logs"), Stream: httputils.BoolValue(r, "stream"), } if err := s.daemon.ContainerAttachWithLogs(containerName, attachWithLogsConfig); err != nil { fmt.Fprintf(outStream, "Error attaching: %s\n", err) } return nil }
func (pr *pluginRouter) removePlugin(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { if err := httputils.ParseForm(r); err != nil { return err } name := vars["name"] config := &types.PluginRmConfig{ ForceRemove: httputils.BoolValue(r, "force"), } return pr.backend.Remove(name, config) }
func (v *volumeRouter) deleteVolumes(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { if err := httputils.ParseForm(r); err != nil { return err } force := httputils.BoolValue(r, "force") if err := v.backend.VolumeRm(vars["name"], force); err != nil { return err } w.WriteHeader(http.StatusNoContent) return nil }
func (s *containerRouter) postContainersAttach(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { if err := httputils.ParseForm(r); err != nil { return err } containerName := vars["name"] _, upgrade := r.Header["Upgrade"] attachWithLogsConfig := &daemon.ContainerAttachWithLogsConfig{ Hijacker: w.(http.Hijacker), Upgrade: upgrade, UseStdin: httputils.BoolValue(r, "stdin"), UseStdout: httputils.BoolValue(r, "stdout"), UseStderr: httputils.BoolValue(r, "stderr"), Logs: httputils.BoolValue(r, "logs"), Stream: httputils.BoolValue(r, "stream"), } return s.backend.ContainerAttachWithLogs(containerName, attachWithLogsConfig) }
func (s *containerRouter) deleteContainers(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { if err := httputils.ParseForm(r); err != nil { return err } name := vars["name"] config := &types.ContainerRmConfig{ ForceRemove: httputils.BoolValue(r, "force"), RemoveVolume: httputils.BoolValue(r, "v"), RemoveLink: httputils.BoolValue(r, "link"), } if err := s.backend.ContainerRm(name, config); err != nil { return err } w.WriteHeader(http.StatusNoContent) return nil }
// getContainersByName inspects containers configuration and serializes it as json. func (s *containerRouter) getContainersByName(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { displaySize := httputils.BoolValue(r, "size") version := httputils.VersionFromContext(ctx) json, err := s.backend.ContainerInspect(vars["name"], displaySize, version) if err != nil { return err } return httputils.WriteJSON(w, http.StatusOK, json) }
func (s *containerRouter) wsContainersAttach(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { if err := httputils.ParseForm(r); err != nil { return err } containerName := vars["name"] if !s.backend.Exists(containerName) { return derr.ErrorCodeNoSuchContainer.WithArgs(containerName) } var keys []byte var err error detachKeys := r.FormValue("detachKeys") if detachKeys != "" { keys, err = term.ToBytes(detachKeys) if err != nil { logrus.Warnf("Invalid escape keys provided (%s) using default : ctrl-p ctrl-q", detachKeys) } } h := websocket.Handler(func(ws *websocket.Conn) { defer ws.Close() wsAttachWithLogsConfig := &daemon.ContainerWsAttachWithLogsConfig{ InStream: ws, OutStream: ws, ErrStream: ws, Logs: httputils.BoolValue(r, "logs"), Stream: httputils.BoolValue(r, "stream"), DetachKeys: keys, } if err := s.backend.ContainerWsAttachWithLogs(containerName, wsAttachWithLogsConfig); err != nil { logrus.Errorf("Error attaching websocket: %s", utils.GetErrorMessage(err)) } }) ws := websocket.Server{Handler: h, Handshake: nil} ws.ServeHTTP(w, r) return nil }
func (s *imageRouter) deleteImages(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { if err := httputils.ParseForm(r); err != nil { return err } name := vars["name"] if strings.TrimSpace(name) == "" { return fmt.Errorf("image name cannot be blank") } force := httputils.BoolValue(r, "force") prune := !httputils.BoolValue(r, "noprune") list, err := s.backend.ImageDelete(name, force, prune) if err != nil { return err } return httputils.WriteJSON(w, http.StatusOK, list) }
func (s *router) postCommit(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { if err := httputils.ParseForm(r); err != nil { return err } if err := httputils.CheckForJSON(r); err != nil { return err } cname := r.Form.Get("container") pause := httputils.BoolValue(r, "pause") version := httputils.VersionFromContext(ctx) if r.FormValue("pause") == "" && version.GreaterThanOrEqualTo("1.13") { 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 = &container.Config{} } if !s.daemon.Exists(cname) { return derr.ErrorCodeNoSuchContainer.WithArgs(cname) } newConfig, err := dockerfile.BuildFromConfig(c, r.Form["changes"]) if err != nil { return err } commitCfg := &types.ContainerCommitConfig{ Pause: pause, Repo: r.Form.Get("repo"), Tag: r.Form.Get("tag"), Author: r.Form.Get("author"), Comment: r.Form.Get("comment"), Config: newConfig, MergeConfigs: true, } imgID, err := s.daemon.Commit(cname, commitCfg) if err != nil { return err } return httputils.WriteJSON(w, http.StatusCreated, &types.ContainerCommitResponse{ ID: string(imgID), }) }
func (sr *swarmRouter) removeNode(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { if err := httputils.ParseForm(r); err != nil { return err } force := httputils.BoolValue(r, "force") if err := sr.backend.RemoveNode(vars["id"], force); err != nil { logrus.Errorf("Error removing node %s: %v", vars["id"], err) return err } return nil }
func (s *imageRouter) getImagesJSON(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { if err := httputils.ParseForm(r); err != nil { return err } // FIXME: The filter parameter could just be a match filter images, err := s.backend.Images(r.Form.Get("filters"), r.Form.Get("filter"), httputils.BoolValue(r, "all")) if err != nil { return err } return httputils.WriteJSON(w, http.StatusOK, images) }
func (s *router) postImagesTag(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { if err := httputils.ParseForm(r); err != nil { return err } repo := r.Form.Get("repo") tag := r.Form.Get("tag") name := vars["name"] force := httputils.BoolValue(r, "force") if err := s.daemon.TagImage(repo, tag, name, force); err != nil { return err } w.WriteHeader(http.StatusCreated) return nil }
func (s *router) deleteImages(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { if err := httputils.ParseForm(r); err != nil { return err } if vars == nil { return fmt.Errorf("Missing parameter") } name := vars["name"] if name == "" { return fmt.Errorf("image name cannot be blank") } force := httputils.BoolValue(r, "force") prune := !httputils.BoolValue(r, "noprune") list, err := s.daemon.ImageDelete(name, force, prune) if err != nil { return err } return httputils.WriteJSON(w, http.StatusOK, list) }
func (s *imageRouter) postCommit(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { if err := httputils.ParseForm(r); err != nil { return err } if err := httputils.CheckForJSON(r); err != nil { return err } cname := r.Form.Get("container") pause := httputils.BoolValue(r, "pause") version := httputils.VersionFromContext(ctx) if r.FormValue("pause") == "" && versions.GreaterThanOrEqualTo(version, "1.13") { pause = true } c, _, _, err := s.decoder.DecodeConfig(r.Body) if err != nil && err != io.EOF { //Do not fail if body is empty. return err } if c == nil { c = &container.Config{} } commitCfg := &backend.ContainerCommitConfig{ ContainerCommitConfig: types.ContainerCommitConfig{ Pause: pause, Repo: r.Form.Get("repo"), Tag: r.Form.Get("tag"), Author: r.Form.Get("author"), Comment: r.Form.Get("comment"), Config: c, MergeConfigs: true, }, Changes: r.Form["changes"], } imgID, err := s.backend.Commit(cname, commitCfg) if err != nil { return err } return httputils.WriteJSON(w, http.StatusCreated, &types.ContainerCommitResponse{ ID: string(imgID), }) }
func (s *router) postImagesTag(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { if err := httputils.ParseForm(r); err != nil { return err } if vars == nil { return fmt.Errorf("Missing parameter") } repo := r.Form.Get("repo") tag := r.Form.Get("tag") name := vars["name"] force := httputils.BoolValue(r, "force") if err := s.daemon.TagImage(repo, tag, name, force); err != nil { return err } s.daemon.EventsService.Log("tag", utils.ImageReference(repo, tag), "") w.WriteHeader(http.StatusCreated) return nil }
func (s *router) postImagesTag(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { if err := httputils.ParseForm(r); err != nil { return err } repo := r.Form.Get("repo") tag := r.Form.Get("tag") newTag, err := reference.WithName(repo) if err != nil { return err } if tag != "" { if newTag, err = reference.WithTag(newTag, tag); err != nil { return err } } force := httputils.BoolValue(r, "force") if err := s.daemon.TagImage(newTag, vars["name"], force); err != nil { return err } w.WriteHeader(http.StatusCreated) return nil }
func (s *imageRouter) getImagesJSON(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { if err := httputils.ParseForm(r); err != nil { return err } imageFilters, err := filters.FromParam(r.Form.Get("filters")) if err != nil { return err } version := httputils.VersionFromContext(ctx) filterParam := r.Form.Get("filter") if versions.LessThan(version, "1.28") && filterParam != "" { imageFilters.Add("reference", filterParam) } images, err := s.backend.Images(imageFilters, httputils.BoolValue(r, "all"), false) if err != nil { return err } return httputils.WriteJSON(w, http.StatusOK, images) }
func (s *containerRouter) postContainersAttach(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { err := httputils.ParseForm(r) if err != nil { return err } containerName := vars["name"] _, upgrade := r.Header["Upgrade"] detachKeys := r.FormValue("detachKeys") hijacker, ok := w.(http.Hijacker) if !ok { return fmt.Errorf("error attaching to container %s, hijack connection missing", containerName) } setupStreams := func() (io.ReadCloser, io.Writer, io.Writer, error) { conn, _, err := hijacker.Hijack() if err != nil { return nil, nil, nil, err } // set raw mode conn.Write([]byte{}) if upgrade { fmt.Fprintf(conn, "HTTP/1.1 101 UPGRADED\r\nContent-Type: application/vnd.docker.raw-stream\r\nConnection: Upgrade\r\nUpgrade: tcp\r\n\r\n") } else { fmt.Fprintf(conn, "HTTP/1.1 200 OK\r\nContent-Type: application/vnd.docker.raw-stream\r\n\r\n") } closer := func() error { httputils.CloseStreams(conn) return nil } return ioutils.NewReadCloserWrapper(conn, closer), conn, conn, nil } attachConfig := &backend.ContainerAttachConfig{ GetStreams: setupStreams, UseStdin: httputils.BoolValue(r, "stdin"), UseStdout: httputils.BoolValue(r, "stdout"), UseStderr: httputils.BoolValue(r, "stderr"), Logs: httputils.BoolValue(r, "logs"), Stream: httputils.BoolValue(r, "stream"), DetachKeys: detachKeys, MuxStreams: true, } if err = s.backend.ContainerAttach(containerName, attachConfig); err != nil { logrus.Errorf("Handler for %s %s returned error: %v", r.Method, r.URL.Path, err) // Remember to close stream if error happens conn, _, errHijack := hijacker.Hijack() if errHijack == nil { statusCode := httputils.GetHTTPErrorStatusCode(err) statusText := http.StatusText(statusCode) fmt.Fprintf(conn, "HTTP/1.1 %d %s\r\nContent-Type: application/vnd.docker.raw-stream\r\n\r\n%s\r\n", statusCode, statusText, err.Error()) httputils.CloseStreams(conn) } else { logrus.Errorf("Error Hijacking: %v", err) } } return nil }