|
@@ -2,7 +2,6 @@ package server
|
|
|
|
|
|
import (
|
|
import (
|
|
"crypto/tls"
|
|
"crypto/tls"
|
|
- "encoding/base64"
|
|
|
|
"encoding/json"
|
|
"encoding/json"
|
|
"fmt"
|
|
"fmt"
|
|
"io"
|
|
"io"
|
|
@@ -10,34 +9,16 @@ import (
|
|
"net/http"
|
|
"net/http"
|
|
"os"
|
|
"os"
|
|
"runtime"
|
|
"runtime"
|
|
- "strconv"
|
|
|
|
"strings"
|
|
"strings"
|
|
- "time"
|
|
|
|
|
|
|
|
"github.com/gorilla/mux"
|
|
"github.com/gorilla/mux"
|
|
- "golang.org/x/net/websocket"
|
|
|
|
|
|
|
|
"github.com/Sirupsen/logrus"
|
|
"github.com/Sirupsen/logrus"
|
|
"github.com/docker/docker/api"
|
|
"github.com/docker/docker/api"
|
|
- "github.com/docker/docker/api/types"
|
|
|
|
"github.com/docker/docker/autogen/dockerversion"
|
|
"github.com/docker/docker/autogen/dockerversion"
|
|
- "github.com/docker/docker/builder"
|
|
|
|
- "github.com/docker/docker/cliconfig"
|
|
|
|
"github.com/docker/docker/daemon"
|
|
"github.com/docker/docker/daemon"
|
|
- "github.com/docker/docker/graph"
|
|
|
|
- "github.com/docker/docker/pkg/ioutils"
|
|
|
|
- "github.com/docker/docker/pkg/jsonmessage"
|
|
|
|
- "github.com/docker/docker/pkg/parsers"
|
|
|
|
- "github.com/docker/docker/pkg/parsers/filters"
|
|
|
|
- "github.com/docker/docker/pkg/parsers/kernel"
|
|
|
|
- "github.com/docker/docker/pkg/signal"
|
|
|
|
"github.com/docker/docker/pkg/sockets"
|
|
"github.com/docker/docker/pkg/sockets"
|
|
- "github.com/docker/docker/pkg/stdcopy"
|
|
|
|
- "github.com/docker/docker/pkg/streamformatter"
|
|
|
|
- "github.com/docker/docker/pkg/ulimit"
|
|
|
|
"github.com/docker/docker/pkg/version"
|
|
"github.com/docker/docker/pkg/version"
|
|
- "github.com/docker/docker/runconfig"
|
|
|
|
- "github.com/docker/docker/utils"
|
|
|
|
)
|
|
)
|
|
|
|
|
|
// Config provides the configuration for the API server
|
|
// Config provides the configuration for the API server
|
|
@@ -238,1305 +219,6 @@ func writeJSON(w http.ResponseWriter, code int, v interface{}) error {
|
|
return json.NewEncoder(w).Encode(v)
|
|
return json.NewEncoder(w).Encode(v)
|
|
}
|
|
}
|
|
|
|
|
|
-func (s *Server) postAuth(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
|
|
|
- var config *cliconfig.AuthConfig
|
|
|
|
- err := json.NewDecoder(r.Body).Decode(&config)
|
|
|
|
- r.Body.Close()
|
|
|
|
- if err != nil {
|
|
|
|
- return err
|
|
|
|
- }
|
|
|
|
- status, err := s.daemon.RegistryService.Auth(config)
|
|
|
|
- if err != nil {
|
|
|
|
- return err
|
|
|
|
- }
|
|
|
|
- return writeJSON(w, http.StatusOK, &types.AuthResponse{
|
|
|
|
- Status: status,
|
|
|
|
- })
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-func (s *Server) getVersion(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
|
|
|
- v := &types.Version{
|
|
|
|
- Version: dockerversion.VERSION,
|
|
|
|
- ApiVersion: api.Version,
|
|
|
|
- GitCommit: dockerversion.GITCOMMIT,
|
|
|
|
- GoVersion: runtime.Version(),
|
|
|
|
- Os: runtime.GOOS,
|
|
|
|
- Arch: runtime.GOARCH,
|
|
|
|
- BuildTime: dockerversion.BUILDTIME,
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- if version.GreaterThanOrEqualTo("1.19") {
|
|
|
|
- v.Experimental = utils.ExperimentalBuild()
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- if kernelVersion, err := kernel.GetKernelVersion(); err == nil {
|
|
|
|
- v.KernelVersion = kernelVersion.String()
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- return writeJSON(w, http.StatusOK, v)
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-func (s *Server) postContainersKill(version version.Version, 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
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- var sig uint64
|
|
|
|
- name := vars["name"]
|
|
|
|
-
|
|
|
|
- // If we have a signal, look at it. Otherwise, do nothing
|
|
|
|
- if sigStr := r.Form.Get("signal"); sigStr != "" {
|
|
|
|
- // Check if we passed the signal as a number:
|
|
|
|
- // The largest legal signal is 31, so let's parse on 5 bits
|
|
|
|
- sigN, err := strconv.ParseUint(sigStr, 10, 5)
|
|
|
|
- if err != nil {
|
|
|
|
- // The signal is not a number, treat it as a string (either like
|
|
|
|
- // "KILL" or like "SIGKILL")
|
|
|
|
- syscallSig, ok := signal.SignalMap[strings.TrimPrefix(sigStr, "SIG")]
|
|
|
|
- if !ok {
|
|
|
|
- return fmt.Errorf("Invalid signal: %s", sigStr)
|
|
|
|
- }
|
|
|
|
- sig = uint64(syscallSig)
|
|
|
|
- } else {
|
|
|
|
- sig = sigN
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- if sig == 0 {
|
|
|
|
- return fmt.Errorf("Invalid signal: %s", sigStr)
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- if err := s.daemon.ContainerKill(name, sig); err != nil {
|
|
|
|
- _, isStopped := err.(daemon.ErrContainerNotRunning)
|
|
|
|
- // Return error that's not caused because the container is stopped.
|
|
|
|
- // Return error if the container is not running and the api is >= 1.20
|
|
|
|
- // to keep backwards compatibility.
|
|
|
|
- if version.GreaterThanOrEqualTo("1.20") || !isStopped {
|
|
|
|
- return fmt.Errorf("Cannot kill container %s: %v", name, err)
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- w.WriteHeader(http.StatusNoContent)
|
|
|
|
- return nil
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-func (s *Server) postContainersPause(version version.Version, 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
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- if err := s.daemon.ContainerPause(vars["name"]); err != nil {
|
|
|
|
- return err
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- w.WriteHeader(http.StatusNoContent)
|
|
|
|
-
|
|
|
|
- return nil
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-func (s *Server) postContainersUnpause(version version.Version, 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
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- if err := s.daemon.ContainerUnpause(vars["name"]); err != nil {
|
|
|
|
- return err
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- w.WriteHeader(http.StatusNoContent)
|
|
|
|
-
|
|
|
|
- return nil
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-func (s *Server) getContainersExport(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
|
|
|
- if vars == nil {
|
|
|
|
- return fmt.Errorf("Missing parameter")
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- return s.daemon.ContainerExport(vars["name"], w)
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-func (s *Server) getImagesJSON(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
|
|
|
- if err := parseForm(r); err != nil {
|
|
|
|
- return err
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- // FIXME: The filter parameter could just be a match filter
|
|
|
|
- images, err := s.daemon.Repositories().Images(r.Form.Get("filters"), r.Form.Get("filter"), boolValue(r, "all"))
|
|
|
|
- if err != nil {
|
|
|
|
- return err
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- return writeJSON(w, http.StatusOK, images)
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-func (s *Server) getInfo(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
|
|
|
- info, err := s.daemon.SystemInfo()
|
|
|
|
- if err != nil {
|
|
|
|
- return err
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- return writeJSON(w, http.StatusOK, info)
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-func (s *Server) getEvents(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
|
|
|
- if err := parseForm(r); err != nil {
|
|
|
|
- return err
|
|
|
|
- }
|
|
|
|
- var since int64 = -1
|
|
|
|
- if r.Form.Get("since") != "" {
|
|
|
|
- s, err := strconv.ParseInt(r.Form.Get("since"), 10, 64)
|
|
|
|
- if err != nil {
|
|
|
|
- return err
|
|
|
|
- }
|
|
|
|
- since = s
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- var until int64 = -1
|
|
|
|
- if r.Form.Get("until") != "" {
|
|
|
|
- u, err := strconv.ParseInt(r.Form.Get("until"), 10, 64)
|
|
|
|
- if err != nil {
|
|
|
|
- return err
|
|
|
|
- }
|
|
|
|
- until = u
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- timer := time.NewTimer(0)
|
|
|
|
- timer.Stop()
|
|
|
|
- if until > 0 {
|
|
|
|
- dur := time.Unix(until, 0).Sub(time.Now())
|
|
|
|
- timer = time.NewTimer(dur)
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- ef, err := filters.FromParam(r.Form.Get("filters"))
|
|
|
|
- if err != nil {
|
|
|
|
- return err
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- isFiltered := func(field string, filter []string) bool {
|
|
|
|
- if len(field) == 0 {
|
|
|
|
- return false
|
|
|
|
- }
|
|
|
|
- if len(filter) == 0 {
|
|
|
|
- return false
|
|
|
|
- }
|
|
|
|
- for _, v := range filter {
|
|
|
|
- if v == field {
|
|
|
|
- return false
|
|
|
|
- }
|
|
|
|
- if strings.Contains(field, ":") {
|
|
|
|
- image := strings.Split(field, ":")
|
|
|
|
- if image[0] == v {
|
|
|
|
- return false
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
- return true
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- d := s.daemon
|
|
|
|
- es := d.EventsService
|
|
|
|
- w.Header().Set("Content-Type", "application/json")
|
|
|
|
- outStream := ioutils.NewWriteFlusher(w)
|
|
|
|
- outStream.Write(nil) // make sure response is sent immediately
|
|
|
|
- enc := json.NewEncoder(outStream)
|
|
|
|
-
|
|
|
|
- getContainerID := func(cn string) string {
|
|
|
|
- c, err := d.Get(cn)
|
|
|
|
- if err != nil {
|
|
|
|
- return ""
|
|
|
|
- }
|
|
|
|
- return c.ID
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- sendEvent := func(ev *jsonmessage.JSONMessage) error {
|
|
|
|
- //incoming container filter can be name,id or partial id, convert and replace as a full container id
|
|
|
|
- for i, cn := range ef["container"] {
|
|
|
|
- ef["container"][i] = getContainerID(cn)
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- if isFiltered(ev.Status, ef["event"]) || (isFiltered(ev.ID, ef["image"]) &&
|
|
|
|
- isFiltered(ev.From, ef["image"])) || isFiltered(ev.ID, ef["container"]) {
|
|
|
|
- return nil
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- return enc.Encode(ev)
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- current, l := es.Subscribe()
|
|
|
|
- if since == -1 {
|
|
|
|
- current = nil
|
|
|
|
- }
|
|
|
|
- defer es.Evict(l)
|
|
|
|
- for _, ev := range current {
|
|
|
|
- if ev.Time < since {
|
|
|
|
- continue
|
|
|
|
- }
|
|
|
|
- if err := sendEvent(ev); err != nil {
|
|
|
|
- return err
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- var closeNotify <-chan bool
|
|
|
|
- if closeNotifier, ok := w.(http.CloseNotifier); ok {
|
|
|
|
- closeNotify = closeNotifier.CloseNotify()
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- for {
|
|
|
|
- select {
|
|
|
|
- case ev := <-l:
|
|
|
|
- jev, ok := ev.(*jsonmessage.JSONMessage)
|
|
|
|
- if !ok {
|
|
|
|
- continue
|
|
|
|
- }
|
|
|
|
- if err := sendEvent(jev); err != nil {
|
|
|
|
- return err
|
|
|
|
- }
|
|
|
|
- case <-timer.C:
|
|
|
|
- return nil
|
|
|
|
- case <-closeNotify:
|
|
|
|
- logrus.Debug("Client disconnected, stop sending events")
|
|
|
|
- return nil
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-func (s *Server) getImagesHistory(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
|
|
|
- if vars == nil {
|
|
|
|
- return fmt.Errorf("Missing parameter")
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- name := vars["name"]
|
|
|
|
- history, err := s.daemon.Repositories().History(name)
|
|
|
|
- if err != nil {
|
|
|
|
- return err
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- return writeJSON(w, http.StatusOK, history)
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-func (s *Server) getContainersChanges(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
|
|
|
- if vars == nil {
|
|
|
|
- return fmt.Errorf("Missing parameter")
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- changes, err := s.daemon.ContainerChanges(vars["name"])
|
|
|
|
- if err != nil {
|
|
|
|
- return err
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- return writeJSON(w, http.StatusOK, changes)
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-func (s *Server) getContainersTop(version version.Version, 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
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- procList, err := s.daemon.ContainerTop(vars["name"], r.Form.Get("ps_args"))
|
|
|
|
- if err != nil {
|
|
|
|
- return err
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- return writeJSON(w, http.StatusOK, procList)
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-func (s *Server) getContainersJSON(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
|
|
|
- if err := parseForm(r); err != nil {
|
|
|
|
- return err
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- config := &daemon.ContainersConfig{
|
|
|
|
- All: boolValue(r, "all"),
|
|
|
|
- Size: boolValue(r, "size"),
|
|
|
|
- Since: r.Form.Get("since"),
|
|
|
|
- Before: r.Form.Get("before"),
|
|
|
|
- Filters: r.Form.Get("filters"),
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- if tmpLimit := r.Form.Get("limit"); tmpLimit != "" {
|
|
|
|
- limit, err := strconv.Atoi(tmpLimit)
|
|
|
|
- if err != nil {
|
|
|
|
- return err
|
|
|
|
- }
|
|
|
|
- config.Limit = limit
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- containers, err := s.daemon.Containers(config)
|
|
|
|
- if err != nil {
|
|
|
|
- return err
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- return writeJSON(w, http.StatusOK, containers)
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-func (s *Server) getContainersStats(version version.Version, 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")
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- stream := boolValueOrDefault(r, "stream", true)
|
|
|
|
- var out io.Writer
|
|
|
|
- if !stream {
|
|
|
|
- w.Header().Set("Content-Type", "application/json")
|
|
|
|
- out = w
|
|
|
|
- } else {
|
|
|
|
- out = ioutils.NewWriteFlusher(w)
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- var closeNotifier <-chan bool
|
|
|
|
- if notifier, ok := w.(http.CloseNotifier); ok {
|
|
|
|
- closeNotifier = notifier.CloseNotify()
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- config := &daemon.ContainerStatsConfig{
|
|
|
|
- Stream: stream,
|
|
|
|
- OutStream: out,
|
|
|
|
- Stop: closeNotifier,
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- return s.daemon.ContainerStats(vars["name"], config)
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-func (s *Server) getContainersLogs(version version.Version, 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")
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- // Validate args here, because we can't return not StatusOK after job.Run() call
|
|
|
|
- stdout, stderr := boolValue(r, "stdout"), boolValue(r, "stderr")
|
|
|
|
- if !(stdout || stderr) {
|
|
|
|
- return fmt.Errorf("Bad parameters: you must choose at least one stream")
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- var since time.Time
|
|
|
|
- if r.Form.Get("since") != "" {
|
|
|
|
- s, err := strconv.ParseInt(r.Form.Get("since"), 10, 64)
|
|
|
|
- if err != nil {
|
|
|
|
- return err
|
|
|
|
- }
|
|
|
|
- since = time.Unix(s, 0)
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- var closeNotifier <-chan bool
|
|
|
|
- if notifier, ok := w.(http.CloseNotifier); ok {
|
|
|
|
- closeNotifier = notifier.CloseNotify()
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- c, err := s.daemon.Get(vars["name"])
|
|
|
|
- if err != nil {
|
|
|
|
- return err
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- outStream := ioutils.NewWriteFlusher(w)
|
|
|
|
- // write an empty chunk of data (this is to ensure that the
|
|
|
|
- // HTTP Response is sent immediatly, even if the container has
|
|
|
|
- // not yet produced any data)
|
|
|
|
- outStream.Write(nil)
|
|
|
|
-
|
|
|
|
- logsConfig := &daemon.ContainerLogsConfig{
|
|
|
|
- Follow: boolValue(r, "follow"),
|
|
|
|
- Timestamps: boolValue(r, "timestamps"),
|
|
|
|
- Since: since,
|
|
|
|
- Tail: r.Form.Get("tail"),
|
|
|
|
- UseStdout: stdout,
|
|
|
|
- UseStderr: stderr,
|
|
|
|
- OutStream: outStream,
|
|
|
|
- Stop: closeNotifier,
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- if err := s.daemon.ContainerLogs(c, logsConfig); err != nil {
|
|
|
|
- fmt.Fprintf(w, "Error running logs job: %s\n", err)
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- return nil
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-func (s *Server) postImagesTag(version version.Version, 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")
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- repo := r.Form.Get("repo")
|
|
|
|
- tag := r.Form.Get("tag")
|
|
|
|
- force := boolValue(r, "force")
|
|
|
|
- name := vars["name"]
|
|
|
|
- if err := s.daemon.Repositories().Tag(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 *Server) postCommit(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
|
|
|
- if err := parseForm(r); err != nil {
|
|
|
|
- return err
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- if err := checkForJSON(r); err != nil {
|
|
|
|
- return err
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- cname := r.Form.Get("container")
|
|
|
|
-
|
|
|
|
- pause := boolValue(r, "pause")
|
|
|
|
- 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
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- commitCfg := &builder.CommitConfig{
|
|
|
|
- Pause: pause,
|
|
|
|
- Repo: r.Form.Get("repo"),
|
|
|
|
- Tag: r.Form.Get("tag"),
|
|
|
|
- Author: r.Form.Get("author"),
|
|
|
|
- Comment: r.Form.Get("comment"),
|
|
|
|
- Changes: r.Form["changes"],
|
|
|
|
- Config: c,
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- imgID, err := builder.Commit(cname, s.daemon, commitCfg)
|
|
|
|
- if err != nil {
|
|
|
|
- return err
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- return writeJSON(w, http.StatusCreated, &types.ContainerCommitResponse{
|
|
|
|
- ID: imgID,
|
|
|
|
- })
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-// Creates an image from Pull or from Import
|
|
|
|
-func (s *Server) postImagesCreate(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")
|
|
|
|
- )
|
|
|
|
- authEncoded := r.Header.Get("X-Registry-Auth")
|
|
|
|
- authConfig := &cliconfig.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 = &cliconfig.AuthConfig{}
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- var (
|
|
|
|
- err error
|
|
|
|
- output = ioutils.NewWriteFlusher(w)
|
|
|
|
- )
|
|
|
|
-
|
|
|
|
- w.Header().Set("Content-Type", "application/json")
|
|
|
|
-
|
|
|
|
- if image != "" { //pull
|
|
|
|
- if tag == "" {
|
|
|
|
- image, tag = parsers.ParseRepositoryTag(image)
|
|
|
|
- }
|
|
|
|
- metaHeaders := map[string][]string{}
|
|
|
|
- for k, v := range r.Header {
|
|
|
|
- if strings.HasPrefix(k, "X-Meta-") {
|
|
|
|
- metaHeaders[k] = v
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- imagePullConfig := &graph.ImagePullConfig{
|
|
|
|
- MetaHeaders: metaHeaders,
|
|
|
|
- AuthConfig: authConfig,
|
|
|
|
- OutStream: output,
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- err = s.daemon.Repositories().Pull(image, tag, imagePullConfig)
|
|
|
|
- } else { //import
|
|
|
|
- if tag == "" {
|
|
|
|
- repo, tag = parsers.ParseRepositoryTag(repo)
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- src := r.Form.Get("fromSrc")
|
|
|
|
-
|
|
|
|
- // 'err' MUST NOT be defined within this block, we need any error
|
|
|
|
- // generated from the download to be available to the output
|
|
|
|
- // stream processing below
|
|
|
|
- var newConfig *runconfig.Config
|
|
|
|
- newConfig, err = builder.BuildFromConfig(s.daemon, &runconfig.Config{}, r.Form["changes"])
|
|
|
|
- if err != nil {
|
|
|
|
- return err
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- err = s.daemon.Repositories().Import(src, repo, tag, r.Body, output, newConfig)
|
|
|
|
- }
|
|
|
|
- if err != nil {
|
|
|
|
- if !output.Flushed() {
|
|
|
|
- return err
|
|
|
|
- }
|
|
|
|
- sf := streamformatter.NewJSONStreamFormatter()
|
|
|
|
- output.Write(sf.FormatError(err))
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- return nil
|
|
|
|
-
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-func (s *Server) getImagesSearch(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
|
|
|
- if err := parseForm(r); err != nil {
|
|
|
|
- return err
|
|
|
|
- }
|
|
|
|
- var (
|
|
|
|
- config *cliconfig.AuthConfig
|
|
|
|
- authEncoded = r.Header.Get("X-Registry-Auth")
|
|
|
|
- headers = map[string][]string{}
|
|
|
|
- )
|
|
|
|
-
|
|
|
|
- if authEncoded != "" {
|
|
|
|
- authJSON := base64.NewDecoder(base64.URLEncoding, strings.NewReader(authEncoded))
|
|
|
|
- if err := json.NewDecoder(authJSON).Decode(&config); 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
|
|
|
|
- config = &cliconfig.AuthConfig{}
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
- for k, v := range r.Header {
|
|
|
|
- if strings.HasPrefix(k, "X-Meta-") {
|
|
|
|
- headers[k] = v
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
- query, err := s.daemon.RegistryService.Search(r.Form.Get("term"), config, headers)
|
|
|
|
- if err != nil {
|
|
|
|
- return err
|
|
|
|
- }
|
|
|
|
- return writeJSON(w, http.StatusOK, query.Results)
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-func (s *Server) postImagesPush(version version.Version, 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 := &cliconfig.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 = &cliconfig.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 fmt.Errorf("Bad parameters and missing X-Registry-Auth: %v", err)
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- name := vars["name"]
|
|
|
|
- output := ioutils.NewWriteFlusher(w)
|
|
|
|
- imagePushConfig := &graph.ImagePushConfig{
|
|
|
|
- MetaHeaders: metaHeaders,
|
|
|
|
- AuthConfig: authConfig,
|
|
|
|
- Tag: r.Form.Get("tag"),
|
|
|
|
- OutStream: output,
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- w.Header().Set("Content-Type", "application/json")
|
|
|
|
-
|
|
|
|
- if err := s.daemon.Repositories().Push(name, imagePushConfig); err != nil {
|
|
|
|
- if !output.Flushed() {
|
|
|
|
- return err
|
|
|
|
- }
|
|
|
|
- sf := streamformatter.NewJSONStreamFormatter()
|
|
|
|
- output.Write(sf.FormatError(err))
|
|
|
|
- }
|
|
|
|
- return nil
|
|
|
|
-
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-func (s *Server) getImagesGet(version version.Version, 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
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- w.Header().Set("Content-Type", "application/x-tar")
|
|
|
|
-
|
|
|
|
- output := ioutils.NewWriteFlusher(w)
|
|
|
|
- var names []string
|
|
|
|
- if name, ok := vars["name"]; ok {
|
|
|
|
- names = []string{name}
|
|
|
|
- } else {
|
|
|
|
- names = r.Form["names"]
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- if err := s.daemon.Repositories().ImageExport(names, output); err != nil {
|
|
|
|
- if !output.Flushed() {
|
|
|
|
- return err
|
|
|
|
- }
|
|
|
|
- sf := streamformatter.NewJSONStreamFormatter()
|
|
|
|
- output.Write(sf.FormatError(err))
|
|
|
|
- }
|
|
|
|
- return nil
|
|
|
|
-
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-func (s *Server) postImagesLoad(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
|
|
|
- return s.daemon.Repositories().Load(r.Body, w)
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-func (s *Server) postContainersCreate(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
|
|
|
- if err := parseForm(r); err != nil {
|
|
|
|
- return err
|
|
|
|
- }
|
|
|
|
- if err := checkForJSON(r); err != nil {
|
|
|
|
- return err
|
|
|
|
- }
|
|
|
|
- var (
|
|
|
|
- warnings []string
|
|
|
|
- name = r.Form.Get("name")
|
|
|
|
- )
|
|
|
|
-
|
|
|
|
- config, hostConfig, err := runconfig.DecodeContainerConfig(r.Body)
|
|
|
|
- if err != nil {
|
|
|
|
- return err
|
|
|
|
- }
|
|
|
|
- adjustCPUShares(version, hostConfig)
|
|
|
|
-
|
|
|
|
- containerID, warnings, err := s.daemon.ContainerCreate(name, config, hostConfig)
|
|
|
|
- if err != nil {
|
|
|
|
- return err
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- return writeJSON(w, http.StatusCreated, &types.ContainerCreateResponse{
|
|
|
|
- ID: containerID,
|
|
|
|
- Warnings: warnings,
|
|
|
|
- })
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-func (s *Server) postContainersRestart(version version.Version, 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")
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- timeout, _ := strconv.Atoi(r.Form.Get("t"))
|
|
|
|
-
|
|
|
|
- if err := s.daemon.ContainerRestart(vars["name"], timeout); err != nil {
|
|
|
|
- return err
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- w.WriteHeader(http.StatusNoContent)
|
|
|
|
-
|
|
|
|
- return nil
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-func (s *Server) postContainerRename(version version.Version, 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")
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- name := vars["name"]
|
|
|
|
- newName := r.Form.Get("name")
|
|
|
|
- if err := s.daemon.ContainerRename(name, newName); err != nil {
|
|
|
|
- return err
|
|
|
|
- }
|
|
|
|
- w.WriteHeader(http.StatusNoContent)
|
|
|
|
- return nil
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-func (s *Server) deleteContainers(version version.Version, 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")
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- name := vars["name"]
|
|
|
|
- config := &daemon.ContainerRmConfig{
|
|
|
|
- ForceRemove: boolValue(r, "force"),
|
|
|
|
- RemoveVolume: boolValue(r, "v"),
|
|
|
|
- RemoveLink: 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 *Server) deleteImages(version version.Version, 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")
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- name := vars["name"]
|
|
|
|
- force := boolValue(r, "force")
|
|
|
|
- noprune := boolValue(r, "noprune")
|
|
|
|
-
|
|
|
|
- list, err := s.daemon.ImageDelete(name, force, noprune)
|
|
|
|
- if err != nil {
|
|
|
|
- return err
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- return writeJSON(w, http.StatusOK, list)
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-func (s *Server) postContainersStart(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
|
|
|
- if vars == nil {
|
|
|
|
- return fmt.Errorf("Missing parameter")
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- // If contentLength is -1, we can assumed chunked encoding
|
|
|
|
- // or more technically that the length is unknown
|
|
|
|
- // https://golang.org/src/pkg/net/http/request.go#L139
|
|
|
|
- // net/http otherwise seems to swallow any headers related to chunked encoding
|
|
|
|
- // including r.TransferEncoding
|
|
|
|
- // allow a nil body for backwards compatibility
|
|
|
|
- var hostConfig *runconfig.HostConfig
|
|
|
|
- if r.Body != nil && (r.ContentLength > 0 || r.ContentLength == -1) {
|
|
|
|
- if err := checkForJSON(r); err != nil {
|
|
|
|
- return err
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- c, err := runconfig.DecodeHostConfig(r.Body)
|
|
|
|
- if err != nil {
|
|
|
|
- return err
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- hostConfig = c
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- if err := s.daemon.ContainerStart(vars["name"], hostConfig); err != nil {
|
|
|
|
- if err.Error() == "Container already started" {
|
|
|
|
- w.WriteHeader(http.StatusNotModified)
|
|
|
|
- return nil
|
|
|
|
- }
|
|
|
|
- return err
|
|
|
|
- }
|
|
|
|
- w.WriteHeader(http.StatusNoContent)
|
|
|
|
- return nil
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-func (s *Server) postContainersStop(version version.Version, 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")
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- seconds, _ := strconv.Atoi(r.Form.Get("t"))
|
|
|
|
-
|
|
|
|
- if err := s.daemon.ContainerStop(vars["name"], seconds); err != nil {
|
|
|
|
- if err.Error() == "Container already stopped" {
|
|
|
|
- w.WriteHeader(http.StatusNotModified)
|
|
|
|
- return nil
|
|
|
|
- }
|
|
|
|
- return err
|
|
|
|
- }
|
|
|
|
- w.WriteHeader(http.StatusNoContent)
|
|
|
|
-
|
|
|
|
- return nil
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-func (s *Server) postContainersWait(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
|
|
|
- if vars == nil {
|
|
|
|
- return fmt.Errorf("Missing parameter")
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- status, err := s.daemon.ContainerWait(vars["name"], -1*time.Second)
|
|
|
|
- if err != nil {
|
|
|
|
- return err
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- return writeJSON(w, http.StatusOK, &types.ContainerWaitResponse{
|
|
|
|
- StatusCode: status,
|
|
|
|
- })
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-func (s *Server) postContainersResize(version version.Version, 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")
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- height, err := strconv.Atoi(r.Form.Get("h"))
|
|
|
|
- if err != nil {
|
|
|
|
- return err
|
|
|
|
- }
|
|
|
|
- width, err := strconv.Atoi(r.Form.Get("w"))
|
|
|
|
- if err != nil {
|
|
|
|
- return err
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- return s.daemon.ContainerResize(vars["name"], height, width)
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-func (s *Server) postContainersAttach(version version.Version, 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")
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- cont, err := s.daemon.Get(vars["name"])
|
|
|
|
- if err != nil {
|
|
|
|
- return err
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- inStream, outStream, err := hijackServer(w)
|
|
|
|
- if err != nil {
|
|
|
|
- return err
|
|
|
|
- }
|
|
|
|
- defer 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: boolValue(r, "stdin"),
|
|
|
|
- UseStdout: boolValue(r, "stdout"),
|
|
|
|
- UseStderr: boolValue(r, "stderr"),
|
|
|
|
- Logs: boolValue(r, "logs"),
|
|
|
|
- Stream: boolValue(r, "stream"),
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- if err := s.daemon.ContainerAttachWithLogs(cont, attachWithLogsConfig); err != nil {
|
|
|
|
- fmt.Fprintf(outStream, "Error attaching: %s\n", err)
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- return nil
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-func (s *Server) wsContainersAttach(version version.Version, 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")
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- cont, err := s.daemon.Get(vars["name"])
|
|
|
|
- if err != nil {
|
|
|
|
- return err
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- h := websocket.Handler(func(ws *websocket.Conn) {
|
|
|
|
- defer ws.Close()
|
|
|
|
-
|
|
|
|
- wsAttachWithLogsConfig := &daemon.ContainerWsAttachWithLogsConfig{
|
|
|
|
- InStream: ws,
|
|
|
|
- OutStream: ws,
|
|
|
|
- ErrStream: ws,
|
|
|
|
- Logs: boolValue(r, "logs"),
|
|
|
|
- Stream: boolValue(r, "stream"),
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- if err := s.daemon.ContainerWsAttachWithLogs(cont, wsAttachWithLogsConfig); err != nil {
|
|
|
|
- logrus.Errorf("Error attaching websocket: %s", err)
|
|
|
|
- }
|
|
|
|
- })
|
|
|
|
- h.ServeHTTP(w, r)
|
|
|
|
-
|
|
|
|
- return nil
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-func (s *Server) getContainersByName(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
|
|
|
- if vars == nil {
|
|
|
|
- return fmt.Errorf("Missing parameter")
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- if version.LessThan("1.20") && runtime.GOOS != "windows" {
|
|
|
|
- return getContainersByNameDownlevel(w, s, vars["name"])
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- containerJSON, err := s.daemon.ContainerInspect(vars["name"])
|
|
|
|
- if err != nil {
|
|
|
|
- return err
|
|
|
|
- }
|
|
|
|
- return writeJSON(w, http.StatusOK, containerJSON)
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-func (s *Server) getExecByID(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
|
|
|
- if vars == nil {
|
|
|
|
- return fmt.Errorf("Missing parameter 'id'")
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- eConfig, err := s.daemon.ContainerExecInspect(vars["id"])
|
|
|
|
- if err != nil {
|
|
|
|
- return err
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- return writeJSON(w, http.StatusOK, eConfig)
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-func (s *Server) getImagesByName(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
|
|
|
- if vars == nil {
|
|
|
|
- return fmt.Errorf("Missing parameter")
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- imageInspect, err := s.daemon.Repositories().Lookup(vars["name"])
|
|
|
|
- if err != nil {
|
|
|
|
- return err
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- return writeJSON(w, http.StatusOK, imageInspect)
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-func (s *Server) postBuild(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
|
|
|
- var (
|
|
|
|
- authConfigs = map[string]cliconfig.AuthConfig{}
|
|
|
|
- authConfigsEncoded = r.Header.Get("X-Registry-Config")
|
|
|
|
- buildConfig = builder.NewBuildConfig()
|
|
|
|
- )
|
|
|
|
-
|
|
|
|
- if authConfigsEncoded != "" {
|
|
|
|
- authConfigsJSON := base64.NewDecoder(base64.URLEncoding, strings.NewReader(authConfigsEncoded))
|
|
|
|
- if err := json.NewDecoder(authConfigsJSON).Decode(&authConfigs); 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.
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- w.Header().Set("Content-Type", "application/json")
|
|
|
|
-
|
|
|
|
- if boolValue(r, "forcerm") && version.GreaterThanOrEqualTo("1.12") {
|
|
|
|
- buildConfig.Remove = true
|
|
|
|
- } else if r.FormValue("rm") == "" && version.GreaterThanOrEqualTo("1.12") {
|
|
|
|
- buildConfig.Remove = true
|
|
|
|
- } else {
|
|
|
|
- buildConfig.Remove = boolValue(r, "rm")
|
|
|
|
- }
|
|
|
|
- if boolValue(r, "pull") && version.GreaterThanOrEqualTo("1.16") {
|
|
|
|
- buildConfig.Pull = true
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- output := ioutils.NewWriteFlusher(w)
|
|
|
|
- buildConfig.Stdout = output
|
|
|
|
- buildConfig.Context = r.Body
|
|
|
|
-
|
|
|
|
- buildConfig.RemoteURL = r.FormValue("remote")
|
|
|
|
- buildConfig.DockerfileName = r.FormValue("dockerfile")
|
|
|
|
- buildConfig.RepoName = r.FormValue("t")
|
|
|
|
- buildConfig.SuppressOutput = boolValue(r, "q")
|
|
|
|
- buildConfig.NoCache = boolValue(r, "nocache")
|
|
|
|
- buildConfig.ForceRemove = boolValue(r, "forcerm")
|
|
|
|
- buildConfig.AuthConfigs = authConfigs
|
|
|
|
- buildConfig.MemorySwap = int64ValueOrZero(r, "memswap")
|
|
|
|
- buildConfig.Memory = int64ValueOrZero(r, "memory")
|
|
|
|
- buildConfig.CPUShares = int64ValueOrZero(r, "cpushares")
|
|
|
|
- buildConfig.CPUPeriod = int64ValueOrZero(r, "cpuperiod")
|
|
|
|
- buildConfig.CPUQuota = int64ValueOrZero(r, "cpuquota")
|
|
|
|
- buildConfig.CPUSetCpus = r.FormValue("cpusetcpus")
|
|
|
|
- buildConfig.CPUSetMems = r.FormValue("cpusetmems")
|
|
|
|
- buildConfig.CgroupParent = r.FormValue("cgroupparent")
|
|
|
|
-
|
|
|
|
- var buildUlimits = []*ulimit.Ulimit{}
|
|
|
|
- ulimitsJSON := r.FormValue("ulimits")
|
|
|
|
- if ulimitsJSON != "" {
|
|
|
|
- if err := json.NewDecoder(strings.NewReader(ulimitsJSON)).Decode(&buildUlimits); err != nil {
|
|
|
|
- return err
|
|
|
|
- }
|
|
|
|
- buildConfig.Ulimits = buildUlimits
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- // Job cancellation. Note: not all job types support this.
|
|
|
|
- if closeNotifier, ok := w.(http.CloseNotifier); ok {
|
|
|
|
- finished := make(chan struct{})
|
|
|
|
- defer close(finished)
|
|
|
|
- go func() {
|
|
|
|
- select {
|
|
|
|
- case <-finished:
|
|
|
|
- case <-closeNotifier.CloseNotify():
|
|
|
|
- logrus.Infof("Client disconnected, cancelling job: build")
|
|
|
|
- buildConfig.Cancel()
|
|
|
|
- }
|
|
|
|
- }()
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- if err := builder.Build(s.daemon, buildConfig); err != nil {
|
|
|
|
- // Do not write the error in the http output if it's still empty.
|
|
|
|
- // This prevents from writing a 200(OK) when there is an interal error.
|
|
|
|
- if !output.Flushed() {
|
|
|
|
- return err
|
|
|
|
- }
|
|
|
|
- sf := streamformatter.NewJSONStreamFormatter()
|
|
|
|
- w.Write(sf.FormatError(err))
|
|
|
|
- }
|
|
|
|
- return nil
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-// postContainersCopy is deprecated in favor of getContainersArchivePath.
|
|
|
|
-func (s *Server) postContainersCopy(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
|
|
|
- if vars == nil {
|
|
|
|
- return fmt.Errorf("Missing parameter")
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- if err := checkForJSON(r); err != nil {
|
|
|
|
- return err
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- cfg := types.CopyConfig{}
|
|
|
|
- if err := json.NewDecoder(r.Body).Decode(&cfg); err != nil {
|
|
|
|
- return err
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- if cfg.Resource == "" {
|
|
|
|
- return fmt.Errorf("Path cannot be empty")
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- data, err := s.daemon.ContainerCopy(vars["name"], cfg.Resource)
|
|
|
|
- if err != nil {
|
|
|
|
- if strings.Contains(strings.ToLower(err.Error()), "no such id") {
|
|
|
|
- w.WriteHeader(http.StatusNotFound)
|
|
|
|
- return nil
|
|
|
|
- }
|
|
|
|
- if os.IsNotExist(err) {
|
|
|
|
- return fmt.Errorf("Could not find the file %s in container %s", cfg.Resource, vars["name"])
|
|
|
|
- }
|
|
|
|
- return err
|
|
|
|
- }
|
|
|
|
- defer data.Close()
|
|
|
|
-
|
|
|
|
- w.Header().Set("Content-Type", "application/x-tar")
|
|
|
|
- if _, err := io.Copy(w, data); err != nil {
|
|
|
|
- return err
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- return nil
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-// // Encode the stat to JSON, base64 encode, and place in a header.
|
|
|
|
-func setContainerPathStatHeader(stat *types.ContainerPathStat, header http.Header) error {
|
|
|
|
- statJSON, err := json.Marshal(stat)
|
|
|
|
- if err != nil {
|
|
|
|
- return err
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- header.Set(
|
|
|
|
- "X-Docker-Container-Path-Stat",
|
|
|
|
- base64.StdEncoding.EncodeToString(statJSON),
|
|
|
|
- )
|
|
|
|
-
|
|
|
|
- return nil
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-func (s *Server) headContainersArchive(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
|
|
|
- v, err := archiveFormValues(r, vars)
|
|
|
|
- if err != nil {
|
|
|
|
- return err
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- stat, err := s.daemon.ContainerStatPath(v.name, v.path)
|
|
|
|
- if err != nil {
|
|
|
|
- return err
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- return setContainerPathStatHeader(stat, w.Header())
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-func (s *Server) getContainersArchive(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
|
|
|
- v, err := archiveFormValues(r, vars)
|
|
|
|
- if err != nil {
|
|
|
|
- return err
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- tarArchive, stat, err := s.daemon.ContainerArchivePath(v.name, v.path)
|
|
|
|
- if err != nil {
|
|
|
|
- return err
|
|
|
|
- }
|
|
|
|
- defer tarArchive.Close()
|
|
|
|
-
|
|
|
|
- if err := setContainerPathStatHeader(stat, w.Header()); err != nil {
|
|
|
|
- return err
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- w.Header().Set("Content-Type", "application/x-tar")
|
|
|
|
- _, err = io.Copy(w, tarArchive)
|
|
|
|
-
|
|
|
|
- return err
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-func (s *Server) putContainersArchive(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
|
|
|
- v, err := archiveFormValues(r, vars)
|
|
|
|
- if err != nil {
|
|
|
|
- return err
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- noOverwriteDirNonDir := boolValue(r, "noOverwriteDirNonDir")
|
|
|
|
- return s.daemon.ContainerExtractToDir(v.name, v.path, noOverwriteDirNonDir, r.Body)
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-func (s *Server) postContainerExecCreate(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
|
|
|
- if err := parseForm(r); err != nil {
|
|
|
|
- return err
|
|
|
|
- }
|
|
|
|
- if err := checkForJSON(r); err != nil {
|
|
|
|
- return err
|
|
|
|
- }
|
|
|
|
- name := vars["name"]
|
|
|
|
-
|
|
|
|
- execConfig := &runconfig.ExecConfig{}
|
|
|
|
- if err := json.NewDecoder(r.Body).Decode(execConfig); err != nil {
|
|
|
|
- return err
|
|
|
|
- }
|
|
|
|
- execConfig.Container = name
|
|
|
|
-
|
|
|
|
- if len(execConfig.Cmd) == 0 {
|
|
|
|
- return fmt.Errorf("No exec command specified")
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- // Register an instance of Exec in container.
|
|
|
|
- id, err := s.daemon.ContainerExecCreate(execConfig)
|
|
|
|
- if err != nil {
|
|
|
|
- logrus.Errorf("Error setting up exec command in container %s: %s", name, err)
|
|
|
|
- return err
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- return writeJSON(w, http.StatusCreated, &types.ContainerExecCreateResponse{
|
|
|
|
- ID: id,
|
|
|
|
- })
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-// TODO(vishh): Refactor the code to avoid having to specify stream config as part of both create and start.
|
|
|
|
-func (s *Server) postContainerExecStart(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
|
|
|
- if err := parseForm(r); err != nil {
|
|
|
|
- return err
|
|
|
|
- }
|
|
|
|
- var (
|
|
|
|
- execName = vars["name"]
|
|
|
|
- stdin io.ReadCloser
|
|
|
|
- stdout io.Writer
|
|
|
|
- stderr io.Writer
|
|
|
|
- )
|
|
|
|
-
|
|
|
|
- execStartCheck := &types.ExecStartCheck{}
|
|
|
|
- if err := json.NewDecoder(r.Body).Decode(execStartCheck); err != nil {
|
|
|
|
- return err
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- if !execStartCheck.Detach {
|
|
|
|
- // Setting up the streaming http interface.
|
|
|
|
- inStream, outStream, err := hijackServer(w)
|
|
|
|
- if err != nil {
|
|
|
|
- return err
|
|
|
|
- }
|
|
|
|
- defer closeStreams(inStream, outStream)
|
|
|
|
-
|
|
|
|
- var errStream io.Writer
|
|
|
|
-
|
|
|
|
- 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")
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- if !execStartCheck.Tty {
|
|
|
|
- errStream = stdcopy.NewStdWriter(outStream, stdcopy.Stderr)
|
|
|
|
- outStream = stdcopy.NewStdWriter(outStream, stdcopy.Stdout)
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- stdin = inStream
|
|
|
|
- stdout = outStream
|
|
|
|
- stderr = errStream
|
|
|
|
- }
|
|
|
|
- // Now run the user process in container.
|
|
|
|
-
|
|
|
|
- if err := s.daemon.ContainerExecStart(execName, stdin, stdout, stderr); err != nil {
|
|
|
|
- logrus.Errorf("Error starting exec command in container %s: %s", execName, err)
|
|
|
|
- return err
|
|
|
|
- }
|
|
|
|
- w.WriteHeader(http.StatusNoContent)
|
|
|
|
-
|
|
|
|
- return nil
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-func (s *Server) postContainerExecResize(version version.Version, 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")
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- height, err := strconv.Atoi(r.Form.Get("h"))
|
|
|
|
- if err != nil {
|
|
|
|
- return err
|
|
|
|
- }
|
|
|
|
- width, err := strconv.Atoi(r.Form.Get("w"))
|
|
|
|
- if err != nil {
|
|
|
|
- return err
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- return s.daemon.ContainerExecResize(vars["name"], height, width)
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
func (s *Server) optionsHandler(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
|
func (s *Server) optionsHandler(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
|
w.WriteHeader(http.StatusOK)
|
|
w.WriteHeader(http.StatusOK)
|
|
return nil
|
|
return nil
|