github.com/uppal0016/docker_new@v0.0.0-20240123060250-1c98be13ac2c/api/server/router/container/container_routes.go (about)

     1  package container
     2  
     3  import (
     4  	"encoding/json"
     5  	"fmt"
     6  	"io"
     7  	"net/http"
     8  	"strconv"
     9  	"strings"
    10  	"syscall"
    11  	"time"
    12  
    13  	"github.com/Sirupsen/logrus"
    14  	"github.com/docker/docker/api/server/httputils"
    15  	"github.com/docker/docker/api/types/backend"
    16  	"github.com/docker/docker/pkg/ioutils"
    17  	"github.com/docker/docker/pkg/signal"
    18  	"github.com/docker/engine-api/types"
    19  	"github.com/docker/engine-api/types/container"
    20  	"github.com/docker/engine-api/types/filters"
    21  	"golang.org/x/net/context"
    22  	"golang.org/x/net/websocket"
    23  )
    24  
    25  func (s *containerRouter) getContainersJSON(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
    26  	if err := httputils.ParseForm(r); err != nil {
    27  		return err
    28  	}
    29  	filter, err := filters.FromParam(r.Form.Get("filters"))
    30  	if err != nil {
    31  		return err
    32  	}
    33  
    34  	config := &types.ContainerListOptions{
    35  		All:    httputils.BoolValue(r, "all"),
    36  		Size:   httputils.BoolValue(r, "size"),
    37  		Since:  r.Form.Get("since"),
    38  		Before: r.Form.Get("before"),
    39  		Filter: filter,
    40  	}
    41  
    42  	if tmpLimit := r.Form.Get("limit"); tmpLimit != "" {
    43  		limit, err := strconv.Atoi(tmpLimit)
    44  		if err != nil {
    45  			return err
    46  		}
    47  		config.Limit = limit
    48  	}
    49  
    50  	containers, err := s.backend.Containers(config)
    51  	if err != nil {
    52  		return err
    53  	}
    54  
    55  	return httputils.WriteJSON(w, http.StatusOK, containers)
    56  }
    57  
    58  func (s *containerRouter) getContainersStats(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
    59  	if err := httputils.ParseForm(r); err != nil {
    60  		return err
    61  	}
    62  
    63  	stream := httputils.BoolValueOrDefault(r, "stream", true)
    64  	if !stream {
    65  		w.Header().Set("Content-Type", "application/json")
    66  	}
    67  
    68  	config := &backend.ContainerStatsConfig{
    69  		Stream:    stream,
    70  		OutStream: w,
    71  		Version:   string(httputils.VersionFromContext(ctx)),
    72  	}
    73  
    74  	return s.backend.ContainerStats(ctx, vars["name"], config)
    75  }
    76  
    77  func (s *containerRouter) getContainersLogs(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
    78  	if err := httputils.ParseForm(r); err != nil {
    79  		return err
    80  	}
    81  
    82  	// Args are validated before the stream starts because when it starts we're
    83  	// sending HTTP 200 by writing an empty chunk of data to tell the client that
    84  	// daemon is going to stream. By sending this initial HTTP 200 we can't report
    85  	// any error after the stream starts (i.e. container not found, wrong parameters)
    86  	// with the appropriate status code.
    87  	stdout, stderr := httputils.BoolValue(r, "stdout"), httputils.BoolValue(r, "stderr")
    88  	if !(stdout || stderr) {
    89  		return fmt.Errorf("Bad parameters: you must choose at least one stream")
    90  	}
    91  
    92  	containerName := vars["name"]
    93  	logsConfig := &backend.ContainerLogsConfig{
    94  		ContainerLogsOptions: types.ContainerLogsOptions{
    95  			Follow:     httputils.BoolValue(r, "follow"),
    96  			Timestamps: httputils.BoolValue(r, "timestamps"),
    97  			Since:      r.Form.Get("since"),
    98  			Tail:       r.Form.Get("tail"),
    99  			ShowStdout: stdout,
   100  			ShowStderr: stderr,
   101  		},
   102  		OutStream: w,
   103  	}
   104  
   105  	chStarted := make(chan struct{})
   106  	if err := s.backend.ContainerLogs(ctx, containerName, logsConfig, chStarted); err != nil {
   107  		select {
   108  		case <-chStarted:
   109  			// The client may be expecting all of the data we're sending to
   110  			// be multiplexed, so send it through OutStream, which will
   111  			// have been set up to handle that if needed.
   112  			fmt.Fprintf(logsConfig.OutStream, "Error running logs job: %v\n", err)
   113  		default:
   114  			return err
   115  		}
   116  	}
   117  
   118  	return nil
   119  }
   120  
   121  func (s *containerRouter) getContainersExport(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   122  	return s.backend.ContainerExport(vars["name"], w)
   123  }
   124  
   125  func (s *containerRouter) postContainersStart(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   126  	// If contentLength is -1, we can assumed chunked encoding
   127  	// or more technically that the length is unknown
   128  	// https://golang.org/src/pkg/net/http/request.go#L139
   129  	// net/http otherwise seems to swallow any headers related to chunked encoding
   130  	// including r.TransferEncoding
   131  	// allow a nil body for backwards compatibility
   132  	var hostConfig *container.HostConfig
   133  	if r.Body != nil && (r.ContentLength > 0 || r.ContentLength == -1) {
   134  		if err := httputils.CheckForJSON(r); err != nil {
   135  			return err
   136  		}
   137  
   138  		c, err := s.decoder.DecodeHostConfig(r.Body)
   139  		if err != nil {
   140  			return err
   141  		}
   142  
   143  		hostConfig = c
   144  	}
   145  
   146  	if err := s.backend.ContainerStart(vars["name"], hostConfig); err != nil {
   147  		return err
   148  	}
   149  	w.WriteHeader(http.StatusNoContent)
   150  	return nil
   151  }
   152  
   153  func (s *containerRouter) postContainersStop(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   154  	if err := httputils.ParseForm(r); err != nil {
   155  		return err
   156  	}
   157  
   158  	seconds, _ := strconv.Atoi(r.Form.Get("t"))
   159  
   160  	if err := s.backend.ContainerStop(vars["name"], seconds); err != nil {
   161  		return err
   162  	}
   163  	w.WriteHeader(http.StatusNoContent)
   164  
   165  	return nil
   166  }
   167  
   168  type errContainerIsRunning interface {
   169  	ContainerIsRunning() bool
   170  }
   171  
   172  func (s *containerRouter) postContainersKill(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   173  	if err := httputils.ParseForm(r); err != nil {
   174  		return err
   175  	}
   176  
   177  	var sig syscall.Signal
   178  	name := vars["name"]
   179  
   180  	// If we have a signal, look at it. Otherwise, do nothing
   181  	if sigStr := r.Form.Get("signal"); sigStr != "" {
   182  		var err error
   183  		if sig, err = signal.ParseSignal(sigStr); err != nil {
   184  			return err
   185  		}
   186  	}
   187  
   188  	if err := s.backend.ContainerKill(name, uint64(sig)); err != nil {
   189  		var isStopped bool
   190  		if e, ok := err.(errContainerIsRunning); ok {
   191  			isStopped = !e.ContainerIsRunning()
   192  		}
   193  
   194  		// Return error that's not caused because the container is stopped.
   195  		// Return error if the container is not running and the api is >= 1.20
   196  		// to keep backwards compatibility.
   197  		version := httputils.VersionFromContext(ctx)
   198  		if version.GreaterThanOrEqualTo("1.20") || !isStopped {
   199  			return fmt.Errorf("Cannot kill container %s: %v", name, err)
   200  		}
   201  	}
   202  
   203  	w.WriteHeader(http.StatusNoContent)
   204  	return nil
   205  }
   206  
   207  func (s *containerRouter) postContainersRestart(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   208  	if err := httputils.ParseForm(r); err != nil {
   209  		return err
   210  	}
   211  
   212  	timeout, _ := strconv.Atoi(r.Form.Get("t"))
   213  
   214  	if err := s.backend.ContainerRestart(vars["name"], timeout); err != nil {
   215  		return err
   216  	}
   217  
   218  	w.WriteHeader(http.StatusNoContent)
   219  
   220  	return nil
   221  }
   222  
   223  func (s *containerRouter) postContainersPause(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   224  	if err := httputils.ParseForm(r); err != nil {
   225  		return err
   226  	}
   227  
   228  	if err := s.backend.ContainerPause(vars["name"]); err != nil {
   229  		return err
   230  	}
   231  
   232  	w.WriteHeader(http.StatusNoContent)
   233  
   234  	return nil
   235  }
   236  
   237  func (s *containerRouter) postContainersUnpause(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   238  	if err := httputils.ParseForm(r); err != nil {
   239  		return err
   240  	}
   241  
   242  	if err := s.backend.ContainerUnpause(vars["name"]); err != nil {
   243  		return err
   244  	}
   245  
   246  	w.WriteHeader(http.StatusNoContent)
   247  
   248  	return nil
   249  }
   250  
   251  func (s *containerRouter) postContainersWait(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   252  	status, err := s.backend.ContainerWait(vars["name"], -1*time.Second)
   253  	if err != nil {
   254  		return err
   255  	}
   256  
   257  	return httputils.WriteJSON(w, http.StatusOK, &types.ContainerWaitResponse{
   258  		StatusCode: status,
   259  	})
   260  }
   261  
   262  func (s *containerRouter) getContainersChanges(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   263  	changes, err := s.backend.ContainerChanges(vars["name"])
   264  	if err != nil {
   265  		return err
   266  	}
   267  
   268  	return httputils.WriteJSON(w, http.StatusOK, changes)
   269  }
   270  
   271  func (s *containerRouter) getContainersTop(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   272  	if err := httputils.ParseForm(r); err != nil {
   273  		return err
   274  	}
   275  
   276  	procList, err := s.backend.ContainerTop(vars["name"], r.Form.Get("ps_args"))
   277  	if err != nil {
   278  		return err
   279  	}
   280  
   281  	return httputils.WriteJSON(w, http.StatusOK, procList)
   282  }
   283  
   284  func (s *containerRouter) postContainerRename(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   285  	if err := httputils.ParseForm(r); err != nil {
   286  		return err
   287  	}
   288  
   289  	name := vars["name"]
   290  	newName := r.Form.Get("name")
   291  	if err := s.backend.ContainerRename(name, newName); err != nil {
   292  		return err
   293  	}
   294  	w.WriteHeader(http.StatusNoContent)
   295  	return nil
   296  }
   297  
   298  func (s *containerRouter) postContainerUpdate(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   299  	if err := httputils.ParseForm(r); err != nil {
   300  		return err
   301  	}
   302  	if err := httputils.CheckForJSON(r); err != nil {
   303  		return err
   304  	}
   305  
   306  	var updateConfig container.UpdateConfig
   307  
   308  	decoder := json.NewDecoder(r.Body)
   309  	if err := decoder.Decode(&updateConfig); err != nil {
   310  		return err
   311  	}
   312  
   313  	hostConfig := &container.HostConfig{
   314  		Resources:     updateConfig.Resources,
   315  		RestartPolicy: updateConfig.RestartPolicy,
   316  	}
   317  
   318  	name := vars["name"]
   319  	warnings, err := s.backend.ContainerUpdate(name, hostConfig)
   320  	if err != nil {
   321  		return err
   322  	}
   323  
   324  	return httputils.WriteJSON(w, http.StatusOK, &types.ContainerUpdateResponse{
   325  		Warnings: warnings,
   326  	})
   327  }
   328  
   329  func (s *containerRouter) postContainersCreate(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   330  	if err := httputils.ParseForm(r); err != nil {
   331  		return err
   332  	}
   333  	if err := httputils.CheckForJSON(r); err != nil {
   334  		return err
   335  	}
   336  
   337  	name := r.Form.Get("name")
   338  
   339  	config, hostConfig, networkingConfig, err := s.decoder.DecodeConfig(r.Body)
   340  	if err != nil {
   341  		return err
   342  	}
   343  	version := httputils.VersionFromContext(ctx)
   344  	adjustCPUShares := version.LessThan("1.19")
   345  
   346  	ccr, err := s.backend.ContainerCreate(types.ContainerCreateConfig{
   347  		Name:             name,
   348  		Config:           config,
   349  		HostConfig:       hostConfig,
   350  		NetworkingConfig: networkingConfig,
   351  		AdjustCPUShares:  adjustCPUShares,
   352  	})
   353  	if err != nil {
   354  		return err
   355  	}
   356  
   357  	return httputils.WriteJSON(w, http.StatusCreated, ccr)
   358  }
   359  
   360  func (s *containerRouter) deleteContainers(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   361  	if err := httputils.ParseForm(r); err != nil {
   362  		return err
   363  	}
   364  
   365  	name := vars["name"]
   366  	config := &types.ContainerRmConfig{
   367  		ForceRemove:  httputils.BoolValue(r, "force"),
   368  		RemoveVolume: httputils.BoolValue(r, "v"),
   369  		RemoveLink:   httputils.BoolValue(r, "link"),
   370  	}
   371  
   372  	if err := s.backend.ContainerRm(name, config); err != nil {
   373  		// Force a 404 for the empty string
   374  		if strings.Contains(strings.ToLower(err.Error()), "prefix can't be empty") {
   375  			return fmt.Errorf("no such container: \"\"")
   376  		}
   377  		return err
   378  	}
   379  
   380  	w.WriteHeader(http.StatusNoContent)
   381  
   382  	return nil
   383  }
   384  
   385  func (s *containerRouter) postContainersResize(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   386  	if err := httputils.ParseForm(r); err != nil {
   387  		return err
   388  	}
   389  
   390  	height, err := strconv.Atoi(r.Form.Get("h"))
   391  	if err != nil {
   392  		return err
   393  	}
   394  	width, err := strconv.Atoi(r.Form.Get("w"))
   395  	if err != nil {
   396  		return err
   397  	}
   398  
   399  	return s.backend.ContainerResize(vars["name"], height, width)
   400  }
   401  
   402  func (s *containerRouter) postContainersAttach(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   403  	err := httputils.ParseForm(r)
   404  	if err != nil {
   405  		return err
   406  	}
   407  	containerName := vars["name"]
   408  
   409  	_, upgrade := r.Header["Upgrade"]
   410  	detachKeys := r.FormValue("detachKeys")
   411  
   412  	hijacker, ok := w.(http.Hijacker)
   413  	if !ok {
   414  		return fmt.Errorf("error attaching to container %s, hijack connection missing", containerName)
   415  	}
   416  
   417  	setupStreams := func() (io.ReadCloser, io.Writer, io.Writer, error) {
   418  		conn, _, err := hijacker.Hijack()
   419  		if err != nil {
   420  			return nil, nil, nil, err
   421  		}
   422  
   423  		// set raw mode
   424  		conn.Write([]byte{})
   425  
   426  		if upgrade {
   427  			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")
   428  		} else {
   429  			fmt.Fprintf(conn, "HTTP/1.1 200 OK\r\nContent-Type: application/vnd.docker.raw-stream\r\n\r\n")
   430  		}
   431  
   432  		closer := func() error {
   433  			httputils.CloseStreams(conn)
   434  			return nil
   435  		}
   436  		return ioutils.NewReadCloserWrapper(conn, closer), conn, conn, nil
   437  	}
   438  
   439  	attachConfig := &backend.ContainerAttachConfig{
   440  		GetStreams: setupStreams,
   441  		UseStdin:   httputils.BoolValue(r, "stdin"),
   442  		UseStdout:  httputils.BoolValue(r, "stdout"),
   443  		UseStderr:  httputils.BoolValue(r, "stderr"),
   444  		Logs:       httputils.BoolValue(r, "logs"),
   445  		Stream:     httputils.BoolValue(r, "stream"),
   446  		DetachKeys: detachKeys,
   447  		MuxStreams: true,
   448  	}
   449  
   450  	if err = s.backend.ContainerAttach(containerName, attachConfig); err != nil {
   451  		logrus.Errorf("Handler for %s %s returned error: %v", r.Method, r.URL.Path, err)
   452  		// Remember to close stream if error happens
   453  		conn, _, errHijack := hijacker.Hijack()
   454  		if errHijack == nil {
   455  			statusCode := httputils.GetHTTPErrorStatusCode(err)
   456  			statusText := http.StatusText(statusCode)
   457  			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())
   458  			httputils.CloseStreams(conn)
   459  		} else {
   460  			logrus.Errorf("Error Hijacking: %v", err)
   461  		}
   462  	}
   463  	return nil
   464  }
   465  
   466  func (s *containerRouter) wsContainersAttach(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   467  	if err := httputils.ParseForm(r); err != nil {
   468  		return err
   469  	}
   470  	containerName := vars["name"]
   471  
   472  	var err error
   473  	detachKeys := r.FormValue("detachKeys")
   474  
   475  	done := make(chan struct{})
   476  	started := make(chan struct{})
   477  
   478  	setupStreams := func() (io.ReadCloser, io.Writer, io.Writer, error) {
   479  		wsChan := make(chan *websocket.Conn)
   480  		h := func(conn *websocket.Conn) {
   481  			wsChan <- conn
   482  			<-done
   483  		}
   484  
   485  		srv := websocket.Server{Handler: h, Handshake: nil}
   486  		go func() {
   487  			close(started)
   488  			srv.ServeHTTP(w, r)
   489  		}()
   490  
   491  		conn := <-wsChan
   492  		return conn, conn, conn, nil
   493  	}
   494  
   495  	attachConfig := &backend.ContainerAttachConfig{
   496  		GetStreams: setupStreams,
   497  		Logs:       httputils.BoolValue(r, "logs"),
   498  		Stream:     httputils.BoolValue(r, "stream"),
   499  		DetachKeys: detachKeys,
   500  		UseStdin:   true,
   501  		UseStdout:  true,
   502  		UseStderr:  true,
   503  		MuxStreams: false, // TODO: this should be true since it's a single stream for both stdout and stderr
   504  	}
   505  
   506  	err = s.backend.ContainerAttach(containerName, attachConfig)
   507  	close(done)
   508  	select {
   509  	case <-started:
   510  		logrus.Errorf("Error attaching websocket: %s", err)
   511  		return nil
   512  	default:
   513  	}
   514  	return err
   515  }