Signed-off-by: Dong Chen <dongluo.chen@docker.com>
| 1 | 1 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,469 @@ |
| 0 |
+package local |
|
| 1 |
+ |
|
| 2 |
+import ( |
|
| 3 |
+ "fmt" |
|
| 4 |
+ "io" |
|
| 5 |
+ "net/http" |
|
| 6 |
+ "strconv" |
|
| 7 |
+ "strings" |
|
| 8 |
+ "syscall" |
|
| 9 |
+ "time" |
|
| 10 |
+ |
|
| 11 |
+ "github.com/Sirupsen/logrus" |
|
| 12 |
+ "github.com/docker/distribution/registry/api/errcode" |
|
| 13 |
+ "github.com/docker/docker/api/server/httputils" |
|
| 14 |
+ "github.com/docker/docker/api/types" |
|
| 15 |
+ "github.com/docker/docker/daemon" |
|
| 16 |
+ derr "github.com/docker/docker/errors" |
|
| 17 |
+ "github.com/docker/docker/pkg/ioutils" |
|
| 18 |
+ "github.com/docker/docker/pkg/signal" |
|
| 19 |
+ "github.com/docker/docker/pkg/timeutils" |
|
| 20 |
+ "github.com/docker/docker/runconfig" |
|
| 21 |
+ "github.com/docker/docker/utils" |
|
| 22 |
+ "golang.org/x/net/context" |
|
| 23 |
+ "golang.org/x/net/websocket" |
|
| 24 |
+) |
|
| 25 |
+ |
|
| 26 |
+func (s *router) getContainersJSON(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
|
| 27 |
+ if err := httputils.ParseForm(r); err != nil {
|
|
| 28 |
+ return err |
|
| 29 |
+ } |
|
| 30 |
+ |
|
| 31 |
+ config := &daemon.ContainersConfig{
|
|
| 32 |
+ All: httputils.BoolValue(r, "all"), |
|
| 33 |
+ Size: httputils.BoolValue(r, "size"), |
|
| 34 |
+ Since: r.Form.Get("since"),
|
|
| 35 |
+ Before: r.Form.Get("before"),
|
|
| 36 |
+ Filters: r.Form.Get("filters"),
|
|
| 37 |
+ } |
|
| 38 |
+ |
|
| 39 |
+ if tmpLimit := r.Form.Get("limit"); tmpLimit != "" {
|
|
| 40 |
+ limit, err := strconv.Atoi(tmpLimit) |
|
| 41 |
+ if err != nil {
|
|
| 42 |
+ return err |
|
| 43 |
+ } |
|
| 44 |
+ config.Limit = limit |
|
| 45 |
+ } |
|
| 46 |
+ |
|
| 47 |
+ containers, err := s.daemon.Containers(config) |
|
| 48 |
+ if err != nil {
|
|
| 49 |
+ return err |
|
| 50 |
+ } |
|
| 51 |
+ |
|
| 52 |
+ return httputils.WriteJSON(w, http.StatusOK, containers) |
|
| 53 |
+} |
|
| 54 |
+ |
|
| 55 |
+func (s *router) getContainersStats(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
|
| 56 |
+ if err := httputils.ParseForm(r); err != nil {
|
|
| 57 |
+ return err |
|
| 58 |
+ } |
|
| 59 |
+ |
|
| 60 |
+ stream := httputils.BoolValueOrDefault(r, "stream", true) |
|
| 61 |
+ var out io.Writer |
|
| 62 |
+ if !stream {
|
|
| 63 |
+ w.Header().Set("Content-Type", "application/json")
|
|
| 64 |
+ out = w |
|
| 65 |
+ } else {
|
|
| 66 |
+ wf := ioutils.NewWriteFlusher(w) |
|
| 67 |
+ out = wf |
|
| 68 |
+ defer wf.Close() |
|
| 69 |
+ } |
|
| 70 |
+ |
|
| 71 |
+ var closeNotifier <-chan bool |
|
| 72 |
+ if notifier, ok := w.(http.CloseNotifier); ok {
|
|
| 73 |
+ closeNotifier = notifier.CloseNotify() |
|
| 74 |
+ } |
|
| 75 |
+ |
|
| 76 |
+ config := &daemon.ContainerStatsConfig{
|
|
| 77 |
+ Stream: stream, |
|
| 78 |
+ OutStream: out, |
|
| 79 |
+ Stop: closeNotifier, |
|
| 80 |
+ Version: httputils.VersionFromContext(ctx), |
|
| 81 |
+ } |
|
| 82 |
+ |
|
| 83 |
+ return s.daemon.ContainerStats(vars["name"], config) |
|
| 84 |
+} |
|
| 85 |
+ |
|
| 86 |
+func (s *router) getContainersLogs(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
|
| 87 |
+ if err := httputils.ParseForm(r); err != nil {
|
|
| 88 |
+ return err |
|
| 89 |
+ } |
|
| 90 |
+ |
|
| 91 |
+ // Args are validated before the stream starts because when it starts we're |
|
| 92 |
+ // sending HTTP 200 by writing an empty chunk of data to tell the client that |
|
| 93 |
+ // daemon is going to stream. By sending this initial HTTP 200 we can't report |
|
| 94 |
+ // any error after the stream starts (i.e. container not found, wrong parameters) |
|
| 95 |
+ // with the appropriate status code. |
|
| 96 |
+ stdout, stderr := httputils.BoolValue(r, "stdout"), httputils.BoolValue(r, "stderr") |
|
| 97 |
+ if !(stdout || stderr) {
|
|
| 98 |
+ return fmt.Errorf("Bad parameters: you must choose at least one stream")
|
|
| 99 |
+ } |
|
| 100 |
+ |
|
| 101 |
+ var since time.Time |
|
| 102 |
+ if r.Form.Get("since") != "" {
|
|
| 103 |
+ s, n, err := timeutils.ParseTimestamps(r.Form.Get("since"), 0)
|
|
| 104 |
+ if err != nil {
|
|
| 105 |
+ return err |
|
| 106 |
+ } |
|
| 107 |
+ since = time.Unix(s, n) |
|
| 108 |
+ } |
|
| 109 |
+ |
|
| 110 |
+ var closeNotifier <-chan bool |
|
| 111 |
+ if notifier, ok := w.(http.CloseNotifier); ok {
|
|
| 112 |
+ closeNotifier = notifier.CloseNotify() |
|
| 113 |
+ } |
|
| 114 |
+ |
|
| 115 |
+ containerName := vars["name"] |
|
| 116 |
+ |
|
| 117 |
+ if !s.daemon.Exists(containerName) {
|
|
| 118 |
+ return derr.ErrorCodeNoSuchContainer.WithArgs(containerName) |
|
| 119 |
+ } |
|
| 120 |
+ |
|
| 121 |
+ // write an empty chunk of data (this is to ensure that the |
|
| 122 |
+ // HTTP Response is sent immediately, even if the container has |
|
| 123 |
+ // not yet produced any data) |
|
| 124 |
+ w.WriteHeader(http.StatusOK) |
|
| 125 |
+ if flusher, ok := w.(http.Flusher); ok {
|
|
| 126 |
+ flusher.Flush() |
|
| 127 |
+ } |
|
| 128 |
+ |
|
| 129 |
+ output := ioutils.NewWriteFlusher(w) |
|
| 130 |
+ defer output.Close() |
|
| 131 |
+ |
|
| 132 |
+ logsConfig := &daemon.ContainerLogsConfig{
|
|
| 133 |
+ Follow: httputils.BoolValue(r, "follow"), |
|
| 134 |
+ Timestamps: httputils.BoolValue(r, "timestamps"), |
|
| 135 |
+ Since: since, |
|
| 136 |
+ Tail: r.Form.Get("tail"),
|
|
| 137 |
+ UseStdout: stdout, |
|
| 138 |
+ UseStderr: stderr, |
|
| 139 |
+ OutStream: output, |
|
| 140 |
+ Stop: closeNotifier, |
|
| 141 |
+ } |
|
| 142 |
+ |
|
| 143 |
+ if err := s.daemon.ContainerLogs(containerName, logsConfig); err != nil {
|
|
| 144 |
+ // The client may be expecting all of the data we're sending to |
|
| 145 |
+ // be multiplexed, so send it through OutStream, which will |
|
| 146 |
+ // have been set up to handle that if needed. |
|
| 147 |
+ fmt.Fprintf(logsConfig.OutStream, "Error running logs job: %s\n", utils.GetErrorMessage(err)) |
|
| 148 |
+ } |
|
| 149 |
+ |
|
| 150 |
+ return nil |
|
| 151 |
+} |
|
| 152 |
+ |
|
| 153 |
+func (s *router) getContainersExport(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
|
| 154 |
+ return s.daemon.ContainerExport(vars["name"], w) |
|
| 155 |
+} |
|
| 156 |
+ |
|
| 157 |
+func (s *router) postContainersStart(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
|
| 158 |
+ // If contentLength is -1, we can assumed chunked encoding |
|
| 159 |
+ // or more technically that the length is unknown |
|
| 160 |
+ // https://golang.org/src/pkg/net/http/request.go#L139 |
|
| 161 |
+ // net/http otherwise seems to swallow any headers related to chunked encoding |
|
| 162 |
+ // including r.TransferEncoding |
|
| 163 |
+ // allow a nil body for backwards compatibility |
|
| 164 |
+ var hostConfig *runconfig.HostConfig |
|
| 165 |
+ if r.Body != nil && (r.ContentLength > 0 || r.ContentLength == -1) {
|
|
| 166 |
+ if err := httputils.CheckForJSON(r); err != nil {
|
|
| 167 |
+ return err |
|
| 168 |
+ } |
|
| 169 |
+ |
|
| 170 |
+ c, err := runconfig.DecodeHostConfig(r.Body) |
|
| 171 |
+ if err != nil {
|
|
| 172 |
+ return err |
|
| 173 |
+ } |
|
| 174 |
+ |
|
| 175 |
+ hostConfig = c |
|
| 176 |
+ } |
|
| 177 |
+ |
|
| 178 |
+ if err := s.daemon.ContainerStart(vars["name"], hostConfig); err != nil {
|
|
| 179 |
+ return err |
|
| 180 |
+ } |
|
| 181 |
+ w.WriteHeader(http.StatusNoContent) |
|
| 182 |
+ return nil |
|
| 183 |
+} |
|
| 184 |
+ |
|
| 185 |
+func (s *router) postContainersStop(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
|
| 186 |
+ if err := httputils.ParseForm(r); err != nil {
|
|
| 187 |
+ return err |
|
| 188 |
+ } |
|
| 189 |
+ |
|
| 190 |
+ seconds, _ := strconv.Atoi(r.Form.Get("t"))
|
|
| 191 |
+ |
|
| 192 |
+ if err := s.daemon.ContainerStop(vars["name"], seconds); err != nil {
|
|
| 193 |
+ return err |
|
| 194 |
+ } |
|
| 195 |
+ w.WriteHeader(http.StatusNoContent) |
|
| 196 |
+ |
|
| 197 |
+ return nil |
|
| 198 |
+} |
|
| 199 |
+ |
|
| 200 |
+func (s *router) postContainersKill(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
|
| 201 |
+ if err := httputils.ParseForm(r); err != nil {
|
|
| 202 |
+ return err |
|
| 203 |
+ } |
|
| 204 |
+ |
|
| 205 |
+ var sig syscall.Signal |
|
| 206 |
+ name := vars["name"] |
|
| 207 |
+ |
|
| 208 |
+ // If we have a signal, look at it. Otherwise, do nothing |
|
| 209 |
+ if sigStr := r.Form.Get("signal"); sigStr != "" {
|
|
| 210 |
+ var err error |
|
| 211 |
+ if sig, err = signal.ParseSignal(sigStr); err != nil {
|
|
| 212 |
+ return err |
|
| 213 |
+ } |
|
| 214 |
+ } |
|
| 215 |
+ |
|
| 216 |
+ if err := s.daemon.ContainerKill(name, uint64(sig)); err != nil {
|
|
| 217 |
+ theErr, isDerr := err.(errcode.ErrorCoder) |
|
| 218 |
+ isStopped := isDerr && theErr.ErrorCode() == derr.ErrorCodeNotRunning |
|
| 219 |
+ |
|
| 220 |
+ // Return error that's not caused because the container is stopped. |
|
| 221 |
+ // Return error if the container is not running and the api is >= 1.20 |
|
| 222 |
+ // to keep backwards compatibility. |
|
| 223 |
+ version := httputils.VersionFromContext(ctx) |
|
| 224 |
+ if version.GreaterThanOrEqualTo("1.20") || !isStopped {
|
|
| 225 |
+ return fmt.Errorf("Cannot kill container %s: %v", name, err)
|
|
| 226 |
+ } |
|
| 227 |
+ } |
|
| 228 |
+ |
|
| 229 |
+ w.WriteHeader(http.StatusNoContent) |
|
| 230 |
+ return nil |
|
| 231 |
+} |
|
| 232 |
+ |
|
| 233 |
+func (s *router) postContainersRestart(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
|
| 234 |
+ if err := httputils.ParseForm(r); err != nil {
|
|
| 235 |
+ return err |
|
| 236 |
+ } |
|
| 237 |
+ |
|
| 238 |
+ timeout, _ := strconv.Atoi(r.Form.Get("t"))
|
|
| 239 |
+ |
|
| 240 |
+ if err := s.daemon.ContainerRestart(vars["name"], timeout); err != nil {
|
|
| 241 |
+ return err |
|
| 242 |
+ } |
|
| 243 |
+ |
|
| 244 |
+ w.WriteHeader(http.StatusNoContent) |
|
| 245 |
+ |
|
| 246 |
+ return nil |
|
| 247 |
+} |
|
| 248 |
+ |
|
| 249 |
+func (s *router) postContainersPause(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
|
| 250 |
+ if err := httputils.ParseForm(r); err != nil {
|
|
| 251 |
+ return err |
|
| 252 |
+ } |
|
| 253 |
+ |
|
| 254 |
+ if err := s.daemon.ContainerPause(vars["name"]); err != nil {
|
|
| 255 |
+ return err |
|
| 256 |
+ } |
|
| 257 |
+ |
|
| 258 |
+ w.WriteHeader(http.StatusNoContent) |
|
| 259 |
+ |
|
| 260 |
+ return nil |
|
| 261 |
+} |
|
| 262 |
+ |
|
| 263 |
+func (s *router) postContainersUnpause(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
|
| 264 |
+ if err := httputils.ParseForm(r); err != nil {
|
|
| 265 |
+ return err |
|
| 266 |
+ } |
|
| 267 |
+ |
|
| 268 |
+ if err := s.daemon.ContainerUnpause(vars["name"]); err != nil {
|
|
| 269 |
+ return err |
|
| 270 |
+ } |
|
| 271 |
+ |
|
| 272 |
+ w.WriteHeader(http.StatusNoContent) |
|
| 273 |
+ |
|
| 274 |
+ return nil |
|
| 275 |
+} |
|
| 276 |
+ |
|
| 277 |
+func (s *router) postContainersWait(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
|
| 278 |
+ status, err := s.daemon.ContainerWait(vars["name"], -1*time.Second) |
|
| 279 |
+ if err != nil {
|
|
| 280 |
+ return err |
|
| 281 |
+ } |
|
| 282 |
+ |
|
| 283 |
+ return httputils.WriteJSON(w, http.StatusOK, &types.ContainerWaitResponse{
|
|
| 284 |
+ StatusCode: status, |
|
| 285 |
+ }) |
|
| 286 |
+} |
|
| 287 |
+ |
|
| 288 |
+func (s *router) getContainersChanges(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
|
| 289 |
+ changes, err := s.daemon.ContainerChanges(vars["name"]) |
|
| 290 |
+ if err != nil {
|
|
| 291 |
+ return err |
|
| 292 |
+ } |
|
| 293 |
+ |
|
| 294 |
+ return httputils.WriteJSON(w, http.StatusOK, changes) |
|
| 295 |
+} |
|
| 296 |
+ |
|
| 297 |
+func (s *router) getContainersTop(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
|
| 298 |
+ if err := httputils.ParseForm(r); err != nil {
|
|
| 299 |
+ return err |
|
| 300 |
+ } |
|
| 301 |
+ |
|
| 302 |
+ procList, err := s.daemon.ContainerTop(vars["name"], r.Form.Get("ps_args"))
|
|
| 303 |
+ if err != nil {
|
|
| 304 |
+ return err |
|
| 305 |
+ } |
|
| 306 |
+ |
|
| 307 |
+ return httputils.WriteJSON(w, http.StatusOK, procList) |
|
| 308 |
+} |
|
| 309 |
+ |
|
| 310 |
+func (s *router) postContainerRename(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
|
| 311 |
+ if err := httputils.ParseForm(r); err != nil {
|
|
| 312 |
+ return err |
|
| 313 |
+ } |
|
| 314 |
+ |
|
| 315 |
+ name := vars["name"] |
|
| 316 |
+ newName := r.Form.Get("name")
|
|
| 317 |
+ if err := s.daemon.ContainerRename(name, newName); err != nil {
|
|
| 318 |
+ return err |
|
| 319 |
+ } |
|
| 320 |
+ w.WriteHeader(http.StatusNoContent) |
|
| 321 |
+ return nil |
|
| 322 |
+} |
|
| 323 |
+ |
|
| 324 |
+func (s *router) postContainersCreate(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
|
| 325 |
+ if err := httputils.ParseForm(r); err != nil {
|
|
| 326 |
+ return err |
|
| 327 |
+ } |
|
| 328 |
+ if err := httputils.CheckForJSON(r); err != nil {
|
|
| 329 |
+ return err |
|
| 330 |
+ } |
|
| 331 |
+ |
|
| 332 |
+ name := r.Form.Get("name")
|
|
| 333 |
+ |
|
| 334 |
+ config, hostConfig, err := runconfig.DecodeContainerConfig(r.Body) |
|
| 335 |
+ if err != nil {
|
|
| 336 |
+ return err |
|
| 337 |
+ } |
|
| 338 |
+ version := httputils.VersionFromContext(ctx) |
|
| 339 |
+ adjustCPUShares := version.LessThan("1.19")
|
|
| 340 |
+ |
|
| 341 |
+ ccr, err := s.daemon.ContainerCreate(&daemon.ContainerCreateConfig{
|
|
| 342 |
+ Name: name, |
|
| 343 |
+ Config: config, |
|
| 344 |
+ HostConfig: hostConfig, |
|
| 345 |
+ AdjustCPUShares: adjustCPUShares, |
|
| 346 |
+ }) |
|
| 347 |
+ if err != nil {
|
|
| 348 |
+ return err |
|
| 349 |
+ } |
|
| 350 |
+ |
|
| 351 |
+ return httputils.WriteJSON(w, http.StatusCreated, ccr) |
|
| 352 |
+} |
|
| 353 |
+ |
|
| 354 |
+func (s *router) deleteContainers(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
|
| 355 |
+ if err := httputils.ParseForm(r); err != nil {
|
|
| 356 |
+ return err |
|
| 357 |
+ } |
|
| 358 |
+ |
|
| 359 |
+ name := vars["name"] |
|
| 360 |
+ config := &daemon.ContainerRmConfig{
|
|
| 361 |
+ ForceRemove: httputils.BoolValue(r, "force"), |
|
| 362 |
+ RemoveVolume: httputils.BoolValue(r, "v"), |
|
| 363 |
+ RemoveLink: httputils.BoolValue(r, "link"), |
|
| 364 |
+ } |
|
| 365 |
+ |
|
| 366 |
+ if err := s.daemon.ContainerRm(name, config); err != nil {
|
|
| 367 |
+ // Force a 404 for the empty string |
|
| 368 |
+ if strings.Contains(strings.ToLower(err.Error()), "prefix can't be empty") {
|
|
| 369 |
+ return fmt.Errorf("no such id: \"\"")
|
|
| 370 |
+ } |
|
| 371 |
+ return err |
|
| 372 |
+ } |
|
| 373 |
+ |
|
| 374 |
+ w.WriteHeader(http.StatusNoContent) |
|
| 375 |
+ |
|
| 376 |
+ return nil |
|
| 377 |
+} |
|
| 378 |
+ |
|
| 379 |
+func (s *router) postContainersResize(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
|
| 380 |
+ if err := httputils.ParseForm(r); err != nil {
|
|
| 381 |
+ return err |
|
| 382 |
+ } |
|
| 383 |
+ |
|
| 384 |
+ height, err := strconv.Atoi(r.Form.Get("h"))
|
|
| 385 |
+ if err != nil {
|
|
| 386 |
+ return err |
|
| 387 |
+ } |
|
| 388 |
+ width, err := strconv.Atoi(r.Form.Get("w"))
|
|
| 389 |
+ if err != nil {
|
|
| 390 |
+ return err |
|
| 391 |
+ } |
|
| 392 |
+ |
|
| 393 |
+ return s.daemon.ContainerResize(vars["name"], height, width) |
|
| 394 |
+} |
|
| 395 |
+ |
|
| 396 |
+func (s *router) postContainersAttach(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
|
| 397 |
+ if err := httputils.ParseForm(r); err != nil {
|
|
| 398 |
+ return err |
|
| 399 |
+ } |
|
| 400 |
+ containerName := vars["name"] |
|
| 401 |
+ |
|
| 402 |
+ if !s.daemon.Exists(containerName) {
|
|
| 403 |
+ return derr.ErrorCodeNoSuchContainer.WithArgs(containerName) |
|
| 404 |
+ } |
|
| 405 |
+ |
|
| 406 |
+ if s.daemon.IsPaused(containerName) {
|
|
| 407 |
+ return derr.ErrorCodePausedContainer.WithArgs(containerName) |
|
| 408 |
+ } |
|
| 409 |
+ |
|
| 410 |
+ inStream, outStream, err := httputils.HijackConnection(w) |
|
| 411 |
+ if err != nil {
|
|
| 412 |
+ return err |
|
| 413 |
+ } |
|
| 414 |
+ defer httputils.CloseStreams(inStream, outStream) |
|
| 415 |
+ |
|
| 416 |
+ if _, ok := r.Header["Upgrade"]; ok {
|
|
| 417 |
+ 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") |
|
| 418 |
+ } else {
|
|
| 419 |
+ fmt.Fprintf(outStream, "HTTP/1.1 200 OK\r\nContent-Type: application/vnd.docker.raw-stream\r\n\r\n") |
|
| 420 |
+ } |
|
| 421 |
+ |
|
| 422 |
+ attachWithLogsConfig := &daemon.ContainerAttachWithLogsConfig{
|
|
| 423 |
+ InStream: inStream, |
|
| 424 |
+ OutStream: outStream, |
|
| 425 |
+ UseStdin: httputils.BoolValue(r, "stdin"), |
|
| 426 |
+ UseStdout: httputils.BoolValue(r, "stdout"), |
|
| 427 |
+ UseStderr: httputils.BoolValue(r, "stderr"), |
|
| 428 |
+ Logs: httputils.BoolValue(r, "logs"), |
|
| 429 |
+ Stream: httputils.BoolValue(r, "stream"), |
|
| 430 |
+ } |
|
| 431 |
+ |
|
| 432 |
+ if err := s.daemon.ContainerAttachWithLogs(containerName, attachWithLogsConfig); err != nil {
|
|
| 433 |
+ fmt.Fprintf(outStream, "Error attaching: %s\n", err) |
|
| 434 |
+ } |
|
| 435 |
+ |
|
| 436 |
+ return nil |
|
| 437 |
+} |
|
| 438 |
+ |
|
| 439 |
+func (s *router) wsContainersAttach(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
|
| 440 |
+ if err := httputils.ParseForm(r); err != nil {
|
|
| 441 |
+ return err |
|
| 442 |
+ } |
|
| 443 |
+ containerName := vars["name"] |
|
| 444 |
+ |
|
| 445 |
+ if !s.daemon.Exists(containerName) {
|
|
| 446 |
+ return derr.ErrorCodeNoSuchContainer.WithArgs(containerName) |
|
| 447 |
+ } |
|
| 448 |
+ |
|
| 449 |
+ h := websocket.Handler(func(ws *websocket.Conn) {
|
|
| 450 |
+ defer ws.Close() |
|
| 451 |
+ |
|
| 452 |
+ wsAttachWithLogsConfig := &daemon.ContainerWsAttachWithLogsConfig{
|
|
| 453 |
+ InStream: ws, |
|
| 454 |
+ OutStream: ws, |
|
| 455 |
+ ErrStream: ws, |
|
| 456 |
+ Logs: httputils.BoolValue(r, "logs"), |
|
| 457 |
+ Stream: httputils.BoolValue(r, "stream"), |
|
| 458 |
+ } |
|
| 459 |
+ |
|
| 460 |
+ if err := s.daemon.ContainerWsAttachWithLogs(containerName, wsAttachWithLogsConfig); err != nil {
|
|
| 461 |
+ logrus.Errorf("Error attaching websocket: %s", err)
|
|
| 462 |
+ } |
|
| 463 |
+ }) |
|
| 464 |
+ ws := websocket.Server{Handler: h, Handshake: nil}
|
|
| 465 |
+ ws.ServeHTTP(w, r) |
|
| 466 |
+ |
|
| 467 |
+ return nil |
|
| 468 |
+} |
| 0 | 469 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,112 @@ |
| 0 |
+package local |
|
| 1 |
+ |
|
| 2 |
+import ( |
|
| 3 |
+ "encoding/base64" |
|
| 4 |
+ "encoding/json" |
|
| 5 |
+ "fmt" |
|
| 6 |
+ "io" |
|
| 7 |
+ "net/http" |
|
| 8 |
+ "os" |
|
| 9 |
+ "strings" |
|
| 10 |
+ |
|
| 11 |
+ "github.com/docker/docker/api/server/httputils" |
|
| 12 |
+ "github.com/docker/docker/api/types" |
|
| 13 |
+ "golang.org/x/net/context" |
|
| 14 |
+) |
|
| 15 |
+ |
|
| 16 |
+// postContainersCopy is deprecated in favor of getContainersArchive. |
|
| 17 |
+func (s *router) postContainersCopy(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
|
| 18 |
+ if err := httputils.CheckForJSON(r); err != nil {
|
|
| 19 |
+ return err |
|
| 20 |
+ } |
|
| 21 |
+ |
|
| 22 |
+ cfg := types.CopyConfig{}
|
|
| 23 |
+ if err := json.NewDecoder(r.Body).Decode(&cfg); err != nil {
|
|
| 24 |
+ return err |
|
| 25 |
+ } |
|
| 26 |
+ |
|
| 27 |
+ if cfg.Resource == "" {
|
|
| 28 |
+ return fmt.Errorf("Path cannot be empty")
|
|
| 29 |
+ } |
|
| 30 |
+ |
|
| 31 |
+ data, err := s.daemon.ContainerCopy(vars["name"], cfg.Resource) |
|
| 32 |
+ if err != nil {
|
|
| 33 |
+ if strings.Contains(strings.ToLower(err.Error()), "no such id") {
|
|
| 34 |
+ w.WriteHeader(http.StatusNotFound) |
|
| 35 |
+ return nil |
|
| 36 |
+ } |
|
| 37 |
+ if os.IsNotExist(err) {
|
|
| 38 |
+ return fmt.Errorf("Could not find the file %s in container %s", cfg.Resource, vars["name"])
|
|
| 39 |
+ } |
|
| 40 |
+ return err |
|
| 41 |
+ } |
|
| 42 |
+ defer data.Close() |
|
| 43 |
+ |
|
| 44 |
+ w.Header().Set("Content-Type", "application/x-tar")
|
|
| 45 |
+ if _, err := io.Copy(w, data); err != nil {
|
|
| 46 |
+ return err |
|
| 47 |
+ } |
|
| 48 |
+ |
|
| 49 |
+ return nil |
|
| 50 |
+} |
|
| 51 |
+ |
|
| 52 |
+// // Encode the stat to JSON, base64 encode, and place in a header. |
|
| 53 |
+func setContainerPathStatHeader(stat *types.ContainerPathStat, header http.Header) error {
|
|
| 54 |
+ statJSON, err := json.Marshal(stat) |
|
| 55 |
+ if err != nil {
|
|
| 56 |
+ return err |
|
| 57 |
+ } |
|
| 58 |
+ |
|
| 59 |
+ header.Set( |
|
| 60 |
+ "X-Docker-Container-Path-Stat", |
|
| 61 |
+ base64.StdEncoding.EncodeToString(statJSON), |
|
| 62 |
+ ) |
|
| 63 |
+ |
|
| 64 |
+ return nil |
|
| 65 |
+} |
|
| 66 |
+ |
|
| 67 |
+func (s *router) headContainersArchive(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
|
| 68 |
+ v, err := httputils.ArchiveFormValues(r, vars) |
|
| 69 |
+ if err != nil {
|
|
| 70 |
+ return err |
|
| 71 |
+ } |
|
| 72 |
+ |
|
| 73 |
+ stat, err := s.daemon.ContainerStatPath(v.Name, v.Path) |
|
| 74 |
+ if err != nil {
|
|
| 75 |
+ return err |
|
| 76 |
+ } |
|
| 77 |
+ |
|
| 78 |
+ return setContainerPathStatHeader(stat, w.Header()) |
|
| 79 |
+} |
|
| 80 |
+ |
|
| 81 |
+func (s *router) getContainersArchive(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
|
| 82 |
+ v, err := httputils.ArchiveFormValues(r, vars) |
|
| 83 |
+ if err != nil {
|
|
| 84 |
+ return err |
|
| 85 |
+ } |
|
| 86 |
+ |
|
| 87 |
+ tarArchive, stat, err := s.daemon.ContainerArchivePath(v.Name, v.Path) |
|
| 88 |
+ if err != nil {
|
|
| 89 |
+ return err |
|
| 90 |
+ } |
|
| 91 |
+ defer tarArchive.Close() |
|
| 92 |
+ |
|
| 93 |
+ if err := setContainerPathStatHeader(stat, w.Header()); err != nil {
|
|
| 94 |
+ return err |
|
| 95 |
+ } |
|
| 96 |
+ |
|
| 97 |
+ w.Header().Set("Content-Type", "application/x-tar")
|
|
| 98 |
+ _, err = io.Copy(w, tarArchive) |
|
| 99 |
+ |
|
| 100 |
+ return err |
|
| 101 |
+} |
|
| 102 |
+ |
|
| 103 |
+func (s *router) putContainersArchive(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
|
| 104 |
+ v, err := httputils.ArchiveFormValues(r, vars) |
|
| 105 |
+ if err != nil {
|
|
| 106 |
+ return err |
|
| 107 |
+ } |
|
| 108 |
+ |
|
| 109 |
+ noOverwriteDirNonDir := httputils.BoolValue(r, "noOverwriteDirNonDir") |
|
| 110 |
+ return s.daemon.ContainerExtractToDir(v.Name, v.Path, noOverwriteDirNonDir, r.Body) |
|
| 111 |
+} |
| 0 | 112 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,135 @@ |
| 0 |
+package local |
|
| 1 |
+ |
|
| 2 |
+import ( |
|
| 3 |
+ "encoding/json" |
|
| 4 |
+ "fmt" |
|
| 5 |
+ "io" |
|
| 6 |
+ "net/http" |
|
| 7 |
+ "strconv" |
|
| 8 |
+ |
|
| 9 |
+ "github.com/Sirupsen/logrus" |
|
| 10 |
+ "github.com/docker/docker/api/server/httputils" |
|
| 11 |
+ "github.com/docker/docker/api/types" |
|
| 12 |
+ "github.com/docker/docker/pkg/stdcopy" |
|
| 13 |
+ "github.com/docker/docker/runconfig" |
|
| 14 |
+ "golang.org/x/net/context" |
|
| 15 |
+) |
|
| 16 |
+ |
|
| 17 |
+func (s *router) getExecByID(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
|
| 18 |
+ eConfig, err := s.daemon.ContainerExecInspect(vars["id"]) |
|
| 19 |
+ if err != nil {
|
|
| 20 |
+ return err |
|
| 21 |
+ } |
|
| 22 |
+ |
|
| 23 |
+ return httputils.WriteJSON(w, http.StatusOK, eConfig) |
|
| 24 |
+} |
|
| 25 |
+ |
|
| 26 |
+func (s *router) postContainerExecCreate(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
|
| 27 |
+ if err := httputils.ParseForm(r); err != nil {
|
|
| 28 |
+ return err |
|
| 29 |
+ } |
|
| 30 |
+ if err := httputils.CheckForJSON(r); err != nil {
|
|
| 31 |
+ return err |
|
| 32 |
+ } |
|
| 33 |
+ name := vars["name"] |
|
| 34 |
+ |
|
| 35 |
+ execConfig := &runconfig.ExecConfig{}
|
|
| 36 |
+ if err := json.NewDecoder(r.Body).Decode(execConfig); err != nil {
|
|
| 37 |
+ return err |
|
| 38 |
+ } |
|
| 39 |
+ execConfig.Container = name |
|
| 40 |
+ |
|
| 41 |
+ if len(execConfig.Cmd) == 0 {
|
|
| 42 |
+ return fmt.Errorf("No exec command specified")
|
|
| 43 |
+ } |
|
| 44 |
+ |
|
| 45 |
+ // Register an instance of Exec in container. |
|
| 46 |
+ id, err := s.daemon.ContainerExecCreate(execConfig) |
|
| 47 |
+ if err != nil {
|
|
| 48 |
+ logrus.Errorf("Error setting up exec command in container %s: %s", name, err)
|
|
| 49 |
+ return err |
|
| 50 |
+ } |
|
| 51 |
+ |
|
| 52 |
+ return httputils.WriteJSON(w, http.StatusCreated, &types.ContainerExecCreateResponse{
|
|
| 53 |
+ ID: id, |
|
| 54 |
+ }) |
|
| 55 |
+} |
|
| 56 |
+ |
|
| 57 |
+// TODO(vishh): Refactor the code to avoid having to specify stream config as part of both create and start. |
|
| 58 |
+func (s *router) postContainerExecStart(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 |
+ version := httputils.VersionFromContext(ctx) |
|
| 64 |
+ if version.GreaterThan("1.21") {
|
|
| 65 |
+ if err := httputils.CheckForJSON(r); err != nil {
|
|
| 66 |
+ return err |
|
| 67 |
+ } |
|
| 68 |
+ } |
|
| 69 |
+ |
|
| 70 |
+ var ( |
|
| 71 |
+ execName = vars["name"] |
|
| 72 |
+ stdin, inStream io.ReadCloser |
|
| 73 |
+ stdout, stderr, outStream io.Writer |
|
| 74 |
+ ) |
|
| 75 |
+ |
|
| 76 |
+ execStartCheck := &types.ExecStartCheck{}
|
|
| 77 |
+ if err := json.NewDecoder(r.Body).Decode(execStartCheck); err != nil {
|
|
| 78 |
+ return err |
|
| 79 |
+ } |
|
| 80 |
+ |
|
| 81 |
+ if exists, err := s.daemon.ExecExists(execName); !exists {
|
|
| 82 |
+ return err |
|
| 83 |
+ } |
|
| 84 |
+ |
|
| 85 |
+ if !execStartCheck.Detach {
|
|
| 86 |
+ var err error |
|
| 87 |
+ // Setting up the streaming http interface. |
|
| 88 |
+ inStream, outStream, err = httputils.HijackConnection(w) |
|
| 89 |
+ if err != nil {
|
|
| 90 |
+ return err |
|
| 91 |
+ } |
|
| 92 |
+ defer httputils.CloseStreams(inStream, outStream) |
|
| 93 |
+ |
|
| 94 |
+ if _, ok := r.Header["Upgrade"]; ok {
|
|
| 95 |
+ 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") |
|
| 96 |
+ } else {
|
|
| 97 |
+ fmt.Fprintf(outStream, "HTTP/1.1 200 OK\r\nContent-Type: application/vnd.docker.raw-stream\r\n\r\n") |
|
| 98 |
+ } |
|
| 99 |
+ |
|
| 100 |
+ stdin = inStream |
|
| 101 |
+ stdout = outStream |
|
| 102 |
+ if !execStartCheck.Tty {
|
|
| 103 |
+ stderr = stdcopy.NewStdWriter(outStream, stdcopy.Stderr) |
|
| 104 |
+ stdout = stdcopy.NewStdWriter(outStream, stdcopy.Stdout) |
|
| 105 |
+ } |
|
| 106 |
+ } else {
|
|
| 107 |
+ outStream = w |
|
| 108 |
+ } |
|
| 109 |
+ |
|
| 110 |
+ // Now run the user process in container. |
|
| 111 |
+ if err := s.daemon.ContainerExecStart(execName, stdin, stdout, stderr); err != nil {
|
|
| 112 |
+ if execStartCheck.Detach {
|
|
| 113 |
+ return err |
|
| 114 |
+ } |
|
| 115 |
+ logrus.Errorf("Error running exec in container: %v\n", err)
|
|
| 116 |
+ } |
|
| 117 |
+ return nil |
|
| 118 |
+} |
|
| 119 |
+ |
|
| 120 |
+func (s *router) postContainerExecResize(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
|
| 121 |
+ if err := httputils.ParseForm(r); err != nil {
|
|
| 122 |
+ return err |
|
| 123 |
+ } |
|
| 124 |
+ height, err := strconv.Atoi(r.Form.Get("h"))
|
|
| 125 |
+ if err != nil {
|
|
| 126 |
+ return err |
|
| 127 |
+ } |
|
| 128 |
+ width, err := strconv.Atoi(r.Form.Get("w"))
|
|
| 129 |
+ if err != nil {
|
|
| 130 |
+ return err |
|
| 131 |
+ } |
|
| 132 |
+ |
|
| 133 |
+ return s.daemon.ContainerExecResize(vars["name"], height, width) |
|
| 134 |
+} |
| 0 | 135 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,33 @@ |
| 0 |
+package local |
|
| 1 |
+ |
|
| 2 |
+import ( |
|
| 3 |
+ "net/http" |
|
| 4 |
+ |
|
| 5 |
+ "github.com/docker/docker/api/server/httputils" |
|
| 6 |
+ "golang.org/x/net/context" |
|
| 7 |
+) |
|
| 8 |
+ |
|
| 9 |
+// getContainersByName inspects containers configuration and serializes it as json. |
|
| 10 |
+func (s *router) getContainersByName(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
|
| 11 |
+ displaySize := httputils.BoolValue(r, "size") |
|
| 12 |
+ |
|
| 13 |
+ var json interface{}
|
|
| 14 |
+ var err error |
|
| 15 |
+ |
|
| 16 |
+ version := httputils.VersionFromContext(ctx) |
|
| 17 |
+ |
|
| 18 |
+ switch {
|
|
| 19 |
+ case version.LessThan("1.20"):
|
|
| 20 |
+ json, err = s.daemon.ContainerInspectPre120(vars["name"]) |
|
| 21 |
+ case version.Equal("1.20"):
|
|
| 22 |
+ json, err = s.daemon.ContainerInspect120(vars["name"]) |
|
| 23 |
+ default: |
|
| 24 |
+ json, err = s.daemon.ContainerInspect(vars["name"], displaySize) |
|
| 25 |
+ } |
|
| 26 |
+ |
|
| 27 |
+ if err != nil {
|
|
| 28 |
+ return err |
|
| 29 |
+ } |
|
| 30 |
+ |
|
| 31 |
+ return httputils.WriteJSON(w, http.StatusOK, json) |
|
| 32 |
+} |
| 0 | 33 |
deleted file mode 100644 |
| ... | ... |
@@ -1,469 +0,0 @@ |
| 1 |
-package local |
|
| 2 |
- |
|
| 3 |
-import ( |
|
| 4 |
- "fmt" |
|
| 5 |
- "io" |
|
| 6 |
- "net/http" |
|
| 7 |
- "strconv" |
|
| 8 |
- "strings" |
|
| 9 |
- "syscall" |
|
| 10 |
- "time" |
|
| 11 |
- |
|
| 12 |
- "github.com/Sirupsen/logrus" |
|
| 13 |
- "github.com/docker/distribution/registry/api/errcode" |
|
| 14 |
- "github.com/docker/docker/api/server/httputils" |
|
| 15 |
- "github.com/docker/docker/api/types" |
|
| 16 |
- "github.com/docker/docker/daemon" |
|
| 17 |
- derr "github.com/docker/docker/errors" |
|
| 18 |
- "github.com/docker/docker/pkg/ioutils" |
|
| 19 |
- "github.com/docker/docker/pkg/signal" |
|
| 20 |
- "github.com/docker/docker/pkg/timeutils" |
|
| 21 |
- "github.com/docker/docker/runconfig" |
|
| 22 |
- "github.com/docker/docker/utils" |
|
| 23 |
- "golang.org/x/net/context" |
|
| 24 |
- "golang.org/x/net/websocket" |
|
| 25 |
-) |
|
| 26 |
- |
|
| 27 |
-func (s *router) getContainersJSON(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
|
| 28 |
- if err := httputils.ParseForm(r); err != nil {
|
|
| 29 |
- return err |
|
| 30 |
- } |
|
| 31 |
- |
|
| 32 |
- config := &daemon.ContainersConfig{
|
|
| 33 |
- All: httputils.BoolValue(r, "all"), |
|
| 34 |
- Size: httputils.BoolValue(r, "size"), |
|
| 35 |
- Since: r.Form.Get("since"),
|
|
| 36 |
- Before: r.Form.Get("before"),
|
|
| 37 |
- Filters: r.Form.Get("filters"),
|
|
| 38 |
- } |
|
| 39 |
- |
|
| 40 |
- if tmpLimit := r.Form.Get("limit"); tmpLimit != "" {
|
|
| 41 |
- limit, err := strconv.Atoi(tmpLimit) |
|
| 42 |
- if err != nil {
|
|
| 43 |
- return err |
|
| 44 |
- } |
|
| 45 |
- config.Limit = limit |
|
| 46 |
- } |
|
| 47 |
- |
|
| 48 |
- containers, err := s.daemon.Containers(config) |
|
| 49 |
- if err != nil {
|
|
| 50 |
- return err |
|
| 51 |
- } |
|
| 52 |
- |
|
| 53 |
- return httputils.WriteJSON(w, http.StatusOK, containers) |
|
| 54 |
-} |
|
| 55 |
- |
|
| 56 |
-func (s *router) getContainersStats(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
|
| 57 |
- if err := httputils.ParseForm(r); err != nil {
|
|
| 58 |
- return err |
|
| 59 |
- } |
|
| 60 |
- |
|
| 61 |
- stream := httputils.BoolValueOrDefault(r, "stream", true) |
|
| 62 |
- var out io.Writer |
|
| 63 |
- if !stream {
|
|
| 64 |
- w.Header().Set("Content-Type", "application/json")
|
|
| 65 |
- out = w |
|
| 66 |
- } else {
|
|
| 67 |
- wf := ioutils.NewWriteFlusher(w) |
|
| 68 |
- out = wf |
|
| 69 |
- defer wf.Close() |
|
| 70 |
- } |
|
| 71 |
- |
|
| 72 |
- var closeNotifier <-chan bool |
|
| 73 |
- if notifier, ok := w.(http.CloseNotifier); ok {
|
|
| 74 |
- closeNotifier = notifier.CloseNotify() |
|
| 75 |
- } |
|
| 76 |
- |
|
| 77 |
- config := &daemon.ContainerStatsConfig{
|
|
| 78 |
- Stream: stream, |
|
| 79 |
- OutStream: out, |
|
| 80 |
- Stop: closeNotifier, |
|
| 81 |
- Version: httputils.VersionFromContext(ctx), |
|
| 82 |
- } |
|
| 83 |
- |
|
| 84 |
- return s.daemon.ContainerStats(vars["name"], config) |
|
| 85 |
-} |
|
| 86 |
- |
|
| 87 |
-func (s *router) getContainersLogs(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
|
| 88 |
- if err := httputils.ParseForm(r); err != nil {
|
|
| 89 |
- return err |
|
| 90 |
- } |
|
| 91 |
- |
|
| 92 |
- // Args are validated before the stream starts because when it starts we're |
|
| 93 |
- // sending HTTP 200 by writing an empty chunk of data to tell the client that |
|
| 94 |
- // daemon is going to stream. By sending this initial HTTP 200 we can't report |
|
| 95 |
- // any error after the stream starts (i.e. container not found, wrong parameters) |
|
| 96 |
- // with the appropriate status code. |
|
| 97 |
- stdout, stderr := httputils.BoolValue(r, "stdout"), httputils.BoolValue(r, "stderr") |
|
| 98 |
- if !(stdout || stderr) {
|
|
| 99 |
- return fmt.Errorf("Bad parameters: you must choose at least one stream")
|
|
| 100 |
- } |
|
| 101 |
- |
|
| 102 |
- var since time.Time |
|
| 103 |
- if r.Form.Get("since") != "" {
|
|
| 104 |
- s, n, err := timeutils.ParseTimestamps(r.Form.Get("since"), 0)
|
|
| 105 |
- if err != nil {
|
|
| 106 |
- return err |
|
| 107 |
- } |
|
| 108 |
- since = time.Unix(s, n) |
|
| 109 |
- } |
|
| 110 |
- |
|
| 111 |
- var closeNotifier <-chan bool |
|
| 112 |
- if notifier, ok := w.(http.CloseNotifier); ok {
|
|
| 113 |
- closeNotifier = notifier.CloseNotify() |
|
| 114 |
- } |
|
| 115 |
- |
|
| 116 |
- containerName := vars["name"] |
|
| 117 |
- |
|
| 118 |
- if !s.daemon.Exists(containerName) {
|
|
| 119 |
- return derr.ErrorCodeNoSuchContainer.WithArgs(containerName) |
|
| 120 |
- } |
|
| 121 |
- |
|
| 122 |
- // write an empty chunk of data (this is to ensure that the |
|
| 123 |
- // HTTP Response is sent immediately, even if the container has |
|
| 124 |
- // not yet produced any data) |
|
| 125 |
- w.WriteHeader(http.StatusOK) |
|
| 126 |
- if flusher, ok := w.(http.Flusher); ok {
|
|
| 127 |
- flusher.Flush() |
|
| 128 |
- } |
|
| 129 |
- |
|
| 130 |
- output := ioutils.NewWriteFlusher(w) |
|
| 131 |
- defer output.Close() |
|
| 132 |
- |
|
| 133 |
- logsConfig := &daemon.ContainerLogsConfig{
|
|
| 134 |
- Follow: httputils.BoolValue(r, "follow"), |
|
| 135 |
- Timestamps: httputils.BoolValue(r, "timestamps"), |
|
| 136 |
- Since: since, |
|
| 137 |
- Tail: r.Form.Get("tail"),
|
|
| 138 |
- UseStdout: stdout, |
|
| 139 |
- UseStderr: stderr, |
|
| 140 |
- OutStream: output, |
|
| 141 |
- Stop: closeNotifier, |
|
| 142 |
- } |
|
| 143 |
- |
|
| 144 |
- if err := s.daemon.ContainerLogs(containerName, logsConfig); err != nil {
|
|
| 145 |
- // The client may be expecting all of the data we're sending to |
|
| 146 |
- // be multiplexed, so send it through OutStream, which will |
|
| 147 |
- // have been set up to handle that if needed. |
|
| 148 |
- fmt.Fprintf(logsConfig.OutStream, "Error running logs job: %s\n", utils.GetErrorMessage(err)) |
|
| 149 |
- } |
|
| 150 |
- |
|
| 151 |
- return nil |
|
| 152 |
-} |
|
| 153 |
- |
|
| 154 |
-func (s *router) getContainersExport(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
|
| 155 |
- return s.daemon.ContainerExport(vars["name"], w) |
|
| 156 |
-} |
|
| 157 |
- |
|
| 158 |
-func (s *router) postContainersStart(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
|
| 159 |
- // If contentLength is -1, we can assumed chunked encoding |
|
| 160 |
- // or more technically that the length is unknown |
|
| 161 |
- // https://golang.org/src/pkg/net/http/request.go#L139 |
|
| 162 |
- // net/http otherwise seems to swallow any headers related to chunked encoding |
|
| 163 |
- // including r.TransferEncoding |
|
| 164 |
- // allow a nil body for backwards compatibility |
|
| 165 |
- var hostConfig *runconfig.HostConfig |
|
| 166 |
- if r.Body != nil && (r.ContentLength > 0 || r.ContentLength == -1) {
|
|
| 167 |
- if err := httputils.CheckForJSON(r); err != nil {
|
|
| 168 |
- return err |
|
| 169 |
- } |
|
| 170 |
- |
|
| 171 |
- c, err := runconfig.DecodeHostConfig(r.Body) |
|
| 172 |
- if err != nil {
|
|
| 173 |
- return err |
|
| 174 |
- } |
|
| 175 |
- |
|
| 176 |
- hostConfig = c |
|
| 177 |
- } |
|
| 178 |
- |
|
| 179 |
- if err := s.daemon.ContainerStart(vars["name"], hostConfig); err != nil {
|
|
| 180 |
- return err |
|
| 181 |
- } |
|
| 182 |
- w.WriteHeader(http.StatusNoContent) |
|
| 183 |
- return nil |
|
| 184 |
-} |
|
| 185 |
- |
|
| 186 |
-func (s *router) postContainersStop(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
|
| 187 |
- if err := httputils.ParseForm(r); err != nil {
|
|
| 188 |
- return err |
|
| 189 |
- } |
|
| 190 |
- |
|
| 191 |
- seconds, _ := strconv.Atoi(r.Form.Get("t"))
|
|
| 192 |
- |
|
| 193 |
- if err := s.daemon.ContainerStop(vars["name"], seconds); err != nil {
|
|
| 194 |
- return err |
|
| 195 |
- } |
|
| 196 |
- w.WriteHeader(http.StatusNoContent) |
|
| 197 |
- |
|
| 198 |
- return nil |
|
| 199 |
-} |
|
| 200 |
- |
|
| 201 |
-func (s *router) postContainersKill(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
|
| 202 |
- if err := httputils.ParseForm(r); err != nil {
|
|
| 203 |
- return err |
|
| 204 |
- } |
|
| 205 |
- |
|
| 206 |
- var sig syscall.Signal |
|
| 207 |
- name := vars["name"] |
|
| 208 |
- |
|
| 209 |
- // If we have a signal, look at it. Otherwise, do nothing |
|
| 210 |
- if sigStr := r.Form.Get("signal"); sigStr != "" {
|
|
| 211 |
- var err error |
|
| 212 |
- if sig, err = signal.ParseSignal(sigStr); err != nil {
|
|
| 213 |
- return err |
|
| 214 |
- } |
|
| 215 |
- } |
|
| 216 |
- |
|
| 217 |
- if err := s.daemon.ContainerKill(name, uint64(sig)); err != nil {
|
|
| 218 |
- theErr, isDerr := err.(errcode.ErrorCoder) |
|
| 219 |
- isStopped := isDerr && theErr.ErrorCode() == derr.ErrorCodeNotRunning |
|
| 220 |
- |
|
| 221 |
- // Return error that's not caused because the container is stopped. |
|
| 222 |
- // Return error if the container is not running and the api is >= 1.20 |
|
| 223 |
- // to keep backwards compatibility. |
|
| 224 |
- version := httputils.VersionFromContext(ctx) |
|
| 225 |
- if version.GreaterThanOrEqualTo("1.20") || !isStopped {
|
|
| 226 |
- return fmt.Errorf("Cannot kill container %s: %v", name, err)
|
|
| 227 |
- } |
|
| 228 |
- } |
|
| 229 |
- |
|
| 230 |
- w.WriteHeader(http.StatusNoContent) |
|
| 231 |
- return nil |
|
| 232 |
-} |
|
| 233 |
- |
|
| 234 |
-func (s *router) postContainersRestart(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
|
| 235 |
- if err := httputils.ParseForm(r); err != nil {
|
|
| 236 |
- return err |
|
| 237 |
- } |
|
| 238 |
- |
|
| 239 |
- timeout, _ := strconv.Atoi(r.Form.Get("t"))
|
|
| 240 |
- |
|
| 241 |
- if err := s.daemon.ContainerRestart(vars["name"], timeout); err != nil {
|
|
| 242 |
- return err |
|
| 243 |
- } |
|
| 244 |
- |
|
| 245 |
- w.WriteHeader(http.StatusNoContent) |
|
| 246 |
- |
|
| 247 |
- return nil |
|
| 248 |
-} |
|
| 249 |
- |
|
| 250 |
-func (s *router) postContainersPause(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
|
| 251 |
- if err := httputils.ParseForm(r); err != nil {
|
|
| 252 |
- return err |
|
| 253 |
- } |
|
| 254 |
- |
|
| 255 |
- if err := s.daemon.ContainerPause(vars["name"]); err != nil {
|
|
| 256 |
- return err |
|
| 257 |
- } |
|
| 258 |
- |
|
| 259 |
- w.WriteHeader(http.StatusNoContent) |
|
| 260 |
- |
|
| 261 |
- return nil |
|
| 262 |
-} |
|
| 263 |
- |
|
| 264 |
-func (s *router) postContainersUnpause(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
|
| 265 |
- if err := httputils.ParseForm(r); err != nil {
|
|
| 266 |
- return err |
|
| 267 |
- } |
|
| 268 |
- |
|
| 269 |
- if err := s.daemon.ContainerUnpause(vars["name"]); err != nil {
|
|
| 270 |
- return err |
|
| 271 |
- } |
|
| 272 |
- |
|
| 273 |
- w.WriteHeader(http.StatusNoContent) |
|
| 274 |
- |
|
| 275 |
- return nil |
|
| 276 |
-} |
|
| 277 |
- |
|
| 278 |
-func (s *router) postContainersWait(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
|
| 279 |
- status, err := s.daemon.ContainerWait(vars["name"], -1*time.Second) |
|
| 280 |
- if err != nil {
|
|
| 281 |
- return err |
|
| 282 |
- } |
|
| 283 |
- |
|
| 284 |
- return httputils.WriteJSON(w, http.StatusOK, &types.ContainerWaitResponse{
|
|
| 285 |
- StatusCode: status, |
|
| 286 |
- }) |
|
| 287 |
-} |
|
| 288 |
- |
|
| 289 |
-func (s *router) getContainersChanges(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
|
| 290 |
- changes, err := s.daemon.ContainerChanges(vars["name"]) |
|
| 291 |
- if err != nil {
|
|
| 292 |
- return err |
|
| 293 |
- } |
|
| 294 |
- |
|
| 295 |
- return httputils.WriteJSON(w, http.StatusOK, changes) |
|
| 296 |
-} |
|
| 297 |
- |
|
| 298 |
-func (s *router) getContainersTop(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 |
- |
|
| 303 |
- procList, err := s.daemon.ContainerTop(vars["name"], r.Form.Get("ps_args"))
|
|
| 304 |
- if err != nil {
|
|
| 305 |
- return err |
|
| 306 |
- } |
|
| 307 |
- |
|
| 308 |
- return httputils.WriteJSON(w, http.StatusOK, procList) |
|
| 309 |
-} |
|
| 310 |
- |
|
| 311 |
-func (s *router) postContainerRename(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
|
| 312 |
- if err := httputils.ParseForm(r); err != nil {
|
|
| 313 |
- return err |
|
| 314 |
- } |
|
| 315 |
- |
|
| 316 |
- name := vars["name"] |
|
| 317 |
- newName := r.Form.Get("name")
|
|
| 318 |
- if err := s.daemon.ContainerRename(name, newName); err != nil {
|
|
| 319 |
- return err |
|
| 320 |
- } |
|
| 321 |
- w.WriteHeader(http.StatusNoContent) |
|
| 322 |
- return nil |
|
| 323 |
-} |
|
| 324 |
- |
|
| 325 |
-func (s *router) postContainersCreate(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
|
| 326 |
- if err := httputils.ParseForm(r); err != nil {
|
|
| 327 |
- return err |
|
| 328 |
- } |
|
| 329 |
- if err := httputils.CheckForJSON(r); err != nil {
|
|
| 330 |
- return err |
|
| 331 |
- } |
|
| 332 |
- |
|
| 333 |
- name := r.Form.Get("name")
|
|
| 334 |
- |
|
| 335 |
- config, hostConfig, err := runconfig.DecodeContainerConfig(r.Body) |
|
| 336 |
- if err != nil {
|
|
| 337 |
- return err |
|
| 338 |
- } |
|
| 339 |
- version := httputils.VersionFromContext(ctx) |
|
| 340 |
- adjustCPUShares := version.LessThan("1.19")
|
|
| 341 |
- |
|
| 342 |
- ccr, err := s.daemon.ContainerCreate(&daemon.ContainerCreateConfig{
|
|
| 343 |
- Name: name, |
|
| 344 |
- Config: config, |
|
| 345 |
- HostConfig: hostConfig, |
|
| 346 |
- AdjustCPUShares: adjustCPUShares, |
|
| 347 |
- }) |
|
| 348 |
- if err != nil {
|
|
| 349 |
- return err |
|
| 350 |
- } |
|
| 351 |
- |
|
| 352 |
- return httputils.WriteJSON(w, http.StatusCreated, ccr) |
|
| 353 |
-} |
|
| 354 |
- |
|
| 355 |
-func (s *router) deleteContainers(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
|
| 356 |
- if err := httputils.ParseForm(r); err != nil {
|
|
| 357 |
- return err |
|
| 358 |
- } |
|
| 359 |
- |
|
| 360 |
- name := vars["name"] |
|
| 361 |
- config := &daemon.ContainerRmConfig{
|
|
| 362 |
- ForceRemove: httputils.BoolValue(r, "force"), |
|
| 363 |
- RemoveVolume: httputils.BoolValue(r, "v"), |
|
| 364 |
- RemoveLink: httputils.BoolValue(r, "link"), |
|
| 365 |
- } |
|
| 366 |
- |
|
| 367 |
- if err := s.daemon.ContainerRm(name, config); err != nil {
|
|
| 368 |
- // Force a 404 for the empty string |
|
| 369 |
- if strings.Contains(strings.ToLower(err.Error()), "prefix can't be empty") {
|
|
| 370 |
- return fmt.Errorf("no such id: \"\"")
|
|
| 371 |
- } |
|
| 372 |
- return err |
|
| 373 |
- } |
|
| 374 |
- |
|
| 375 |
- w.WriteHeader(http.StatusNoContent) |
|
| 376 |
- |
|
| 377 |
- return nil |
|
| 378 |
-} |
|
| 379 |
- |
|
| 380 |
-func (s *router) postContainersResize(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
|
| 381 |
- if err := httputils.ParseForm(r); err != nil {
|
|
| 382 |
- return err |
|
| 383 |
- } |
|
| 384 |
- |
|
| 385 |
- height, err := strconv.Atoi(r.Form.Get("h"))
|
|
| 386 |
- if err != nil {
|
|
| 387 |
- return err |
|
| 388 |
- } |
|
| 389 |
- width, err := strconv.Atoi(r.Form.Get("w"))
|
|
| 390 |
- if err != nil {
|
|
| 391 |
- return err |
|
| 392 |
- } |
|
| 393 |
- |
|
| 394 |
- return s.daemon.ContainerResize(vars["name"], height, width) |
|
| 395 |
-} |
|
| 396 |
- |
|
| 397 |
-func (s *router) postContainersAttach(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
|
| 398 |
- if err := httputils.ParseForm(r); err != nil {
|
|
| 399 |
- return err |
|
| 400 |
- } |
|
| 401 |
- containerName := vars["name"] |
|
| 402 |
- |
|
| 403 |
- if !s.daemon.Exists(containerName) {
|
|
| 404 |
- return derr.ErrorCodeNoSuchContainer.WithArgs(containerName) |
|
| 405 |
- } |
|
| 406 |
- |
|
| 407 |
- if s.daemon.IsPaused(containerName) {
|
|
| 408 |
- return derr.ErrorCodePausedContainer.WithArgs(containerName) |
|
| 409 |
- } |
|
| 410 |
- |
|
| 411 |
- inStream, outStream, err := httputils.HijackConnection(w) |
|
| 412 |
- if err != nil {
|
|
| 413 |
- return err |
|
| 414 |
- } |
|
| 415 |
- defer httputils.CloseStreams(inStream, outStream) |
|
| 416 |
- |
|
| 417 |
- if _, ok := r.Header["Upgrade"]; ok {
|
|
| 418 |
- 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") |
|
| 419 |
- } else {
|
|
| 420 |
- fmt.Fprintf(outStream, "HTTP/1.1 200 OK\r\nContent-Type: application/vnd.docker.raw-stream\r\n\r\n") |
|
| 421 |
- } |
|
| 422 |
- |
|
| 423 |
- attachWithLogsConfig := &daemon.ContainerAttachWithLogsConfig{
|
|
| 424 |
- InStream: inStream, |
|
| 425 |
- OutStream: outStream, |
|
| 426 |
- UseStdin: httputils.BoolValue(r, "stdin"), |
|
| 427 |
- UseStdout: httputils.BoolValue(r, "stdout"), |
|
| 428 |
- UseStderr: httputils.BoolValue(r, "stderr"), |
|
| 429 |
- Logs: httputils.BoolValue(r, "logs"), |
|
| 430 |
- Stream: httputils.BoolValue(r, "stream"), |
|
| 431 |
- } |
|
| 432 |
- |
|
| 433 |
- if err := s.daemon.ContainerAttachWithLogs(containerName, attachWithLogsConfig); err != nil {
|
|
| 434 |
- fmt.Fprintf(outStream, "Error attaching: %s\n", err) |
|
| 435 |
- } |
|
| 436 |
- |
|
| 437 |
- return nil |
|
| 438 |
-} |
|
| 439 |
- |
|
| 440 |
-func (s *router) wsContainersAttach(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
|
| 441 |
- if err := httputils.ParseForm(r); err != nil {
|
|
| 442 |
- return err |
|
| 443 |
- } |
|
| 444 |
- containerName := vars["name"] |
|
| 445 |
- |
|
| 446 |
- if !s.daemon.Exists(containerName) {
|
|
| 447 |
- return derr.ErrorCodeNoSuchContainer.WithArgs(containerName) |
|
| 448 |
- } |
|
| 449 |
- |
|
| 450 |
- h := websocket.Handler(func(ws *websocket.Conn) {
|
|
| 451 |
- defer ws.Close() |
|
| 452 |
- |
|
| 453 |
- wsAttachWithLogsConfig := &daemon.ContainerWsAttachWithLogsConfig{
|
|
| 454 |
- InStream: ws, |
|
| 455 |
- OutStream: ws, |
|
| 456 |
- ErrStream: ws, |
|
| 457 |
- Logs: httputils.BoolValue(r, "logs"), |
|
| 458 |
- Stream: httputils.BoolValue(r, "stream"), |
|
| 459 |
- } |
|
| 460 |
- |
|
| 461 |
- if err := s.daemon.ContainerWsAttachWithLogs(containerName, wsAttachWithLogsConfig); err != nil {
|
|
| 462 |
- logrus.Errorf("Error attaching websocket: %s", err)
|
|
| 463 |
- } |
|
| 464 |
- }) |
|
| 465 |
- ws := websocket.Server{Handler: h, Handshake: nil}
|
|
| 466 |
- ws.ServeHTTP(w, r) |
|
| 467 |
- |
|
| 468 |
- return nil |
|
| 469 |
-} |
| 470 | 1 |
deleted file mode 100644 |
| ... | ... |
@@ -1,112 +0,0 @@ |
| 1 |
-package local |
|
| 2 |
- |
|
| 3 |
-import ( |
|
| 4 |
- "encoding/base64" |
|
| 5 |
- "encoding/json" |
|
| 6 |
- "fmt" |
|
| 7 |
- "io" |
|
| 8 |
- "net/http" |
|
| 9 |
- "os" |
|
| 10 |
- "strings" |
|
| 11 |
- |
|
| 12 |
- "github.com/docker/docker/api/server/httputils" |
|
| 13 |
- "github.com/docker/docker/api/types" |
|
| 14 |
- "golang.org/x/net/context" |
|
| 15 |
-) |
|
| 16 |
- |
|
| 17 |
-// postContainersCopy is deprecated in favor of getContainersArchive. |
|
| 18 |
-func (s *router) postContainersCopy(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
|
| 19 |
- if err := httputils.CheckForJSON(r); err != nil {
|
|
| 20 |
- return err |
|
| 21 |
- } |
|
| 22 |
- |
|
| 23 |
- cfg := types.CopyConfig{}
|
|
| 24 |
- if err := json.NewDecoder(r.Body).Decode(&cfg); err != nil {
|
|
| 25 |
- return err |
|
| 26 |
- } |
|
| 27 |
- |
|
| 28 |
- if cfg.Resource == "" {
|
|
| 29 |
- return fmt.Errorf("Path cannot be empty")
|
|
| 30 |
- } |
|
| 31 |
- |
|
| 32 |
- data, err := s.daemon.ContainerCopy(vars["name"], cfg.Resource) |
|
| 33 |
- if err != nil {
|
|
| 34 |
- if strings.Contains(strings.ToLower(err.Error()), "no such id") {
|
|
| 35 |
- w.WriteHeader(http.StatusNotFound) |
|
| 36 |
- return nil |
|
| 37 |
- } |
|
| 38 |
- if os.IsNotExist(err) {
|
|
| 39 |
- return fmt.Errorf("Could not find the file %s in container %s", cfg.Resource, vars["name"])
|
|
| 40 |
- } |
|
| 41 |
- return err |
|
| 42 |
- } |
|
| 43 |
- defer data.Close() |
|
| 44 |
- |
|
| 45 |
- w.Header().Set("Content-Type", "application/x-tar")
|
|
| 46 |
- if _, err := io.Copy(w, data); err != nil {
|
|
| 47 |
- return err |
|
| 48 |
- } |
|
| 49 |
- |
|
| 50 |
- return nil |
|
| 51 |
-} |
|
| 52 |
- |
|
| 53 |
-// // Encode the stat to JSON, base64 encode, and place in a header. |
|
| 54 |
-func setContainerPathStatHeader(stat *types.ContainerPathStat, header http.Header) error {
|
|
| 55 |
- statJSON, err := json.Marshal(stat) |
|
| 56 |
- if err != nil {
|
|
| 57 |
- return err |
|
| 58 |
- } |
|
| 59 |
- |
|
| 60 |
- header.Set( |
|
| 61 |
- "X-Docker-Container-Path-Stat", |
|
| 62 |
- base64.StdEncoding.EncodeToString(statJSON), |
|
| 63 |
- ) |
|
| 64 |
- |
|
| 65 |
- return nil |
|
| 66 |
-} |
|
| 67 |
- |
|
| 68 |
-func (s *router) headContainersArchive(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
|
| 69 |
- v, err := httputils.ArchiveFormValues(r, vars) |
|
| 70 |
- if err != nil {
|
|
| 71 |
- return err |
|
| 72 |
- } |
|
| 73 |
- |
|
| 74 |
- stat, err := s.daemon.ContainerStatPath(v.Name, v.Path) |
|
| 75 |
- if err != nil {
|
|
| 76 |
- return err |
|
| 77 |
- } |
|
| 78 |
- |
|
| 79 |
- return setContainerPathStatHeader(stat, w.Header()) |
|
| 80 |
-} |
|
| 81 |
- |
|
| 82 |
-func (s *router) getContainersArchive(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
|
| 83 |
- v, err := httputils.ArchiveFormValues(r, vars) |
|
| 84 |
- if err != nil {
|
|
| 85 |
- return err |
|
| 86 |
- } |
|
| 87 |
- |
|
| 88 |
- tarArchive, stat, err := s.daemon.ContainerArchivePath(v.Name, v.Path) |
|
| 89 |
- if err != nil {
|
|
| 90 |
- return err |
|
| 91 |
- } |
|
| 92 |
- defer tarArchive.Close() |
|
| 93 |
- |
|
| 94 |
- if err := setContainerPathStatHeader(stat, w.Header()); err != nil {
|
|
| 95 |
- return err |
|
| 96 |
- } |
|
| 97 |
- |
|
| 98 |
- w.Header().Set("Content-Type", "application/x-tar")
|
|
| 99 |
- _, err = io.Copy(w, tarArchive) |
|
| 100 |
- |
|
| 101 |
- return err |
|
| 102 |
-} |
|
| 103 |
- |
|
| 104 |
-func (s *router) putContainersArchive(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
|
| 105 |
- v, err := httputils.ArchiveFormValues(r, vars) |
|
| 106 |
- if err != nil {
|
|
| 107 |
- return err |
|
| 108 |
- } |
|
| 109 |
- |
|
| 110 |
- noOverwriteDirNonDir := httputils.BoolValue(r, "noOverwriteDirNonDir") |
|
| 111 |
- return s.daemon.ContainerExtractToDir(v.Name, v.Path, noOverwriteDirNonDir, r.Body) |
|
| 112 |
-} |
| 113 | 1 |
deleted file mode 100644 |
| ... | ... |
@@ -1,135 +0,0 @@ |
| 1 |
-package local |
|
| 2 |
- |
|
| 3 |
-import ( |
|
| 4 |
- "encoding/json" |
|
| 5 |
- "fmt" |
|
| 6 |
- "io" |
|
| 7 |
- "net/http" |
|
| 8 |
- "strconv" |
|
| 9 |
- |
|
| 10 |
- "github.com/Sirupsen/logrus" |
|
| 11 |
- "github.com/docker/docker/api/server/httputils" |
|
| 12 |
- "github.com/docker/docker/api/types" |
|
| 13 |
- "github.com/docker/docker/pkg/stdcopy" |
|
| 14 |
- "github.com/docker/docker/runconfig" |
|
| 15 |
- "golang.org/x/net/context" |
|
| 16 |
-) |
|
| 17 |
- |
|
| 18 |
-func (s *router) getExecByID(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
|
| 19 |
- eConfig, err := s.daemon.ContainerExecInspect(vars["id"]) |
|
| 20 |
- if err != nil {
|
|
| 21 |
- return err |
|
| 22 |
- } |
|
| 23 |
- |
|
| 24 |
- return httputils.WriteJSON(w, http.StatusOK, eConfig) |
|
| 25 |
-} |
|
| 26 |
- |
|
| 27 |
-func (s *router) postContainerExecCreate(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
|
| 28 |
- if err := httputils.ParseForm(r); err != nil {
|
|
| 29 |
- return err |
|
| 30 |
- } |
|
| 31 |
- if err := httputils.CheckForJSON(r); err != nil {
|
|
| 32 |
- return err |
|
| 33 |
- } |
|
| 34 |
- name := vars["name"] |
|
| 35 |
- |
|
| 36 |
- execConfig := &runconfig.ExecConfig{}
|
|
| 37 |
- if err := json.NewDecoder(r.Body).Decode(execConfig); err != nil {
|
|
| 38 |
- return err |
|
| 39 |
- } |
|
| 40 |
- execConfig.Container = name |
|
| 41 |
- |
|
| 42 |
- if len(execConfig.Cmd) == 0 {
|
|
| 43 |
- return fmt.Errorf("No exec command specified")
|
|
| 44 |
- } |
|
| 45 |
- |
|
| 46 |
- // Register an instance of Exec in container. |
|
| 47 |
- id, err := s.daemon.ContainerExecCreate(execConfig) |
|
| 48 |
- if err != nil {
|
|
| 49 |
- logrus.Errorf("Error setting up exec command in container %s: %s", name, err)
|
|
| 50 |
- return err |
|
| 51 |
- } |
|
| 52 |
- |
|
| 53 |
- return httputils.WriteJSON(w, http.StatusCreated, &types.ContainerExecCreateResponse{
|
|
| 54 |
- ID: id, |
|
| 55 |
- }) |
|
| 56 |
-} |
|
| 57 |
- |
|
| 58 |
-// TODO(vishh): Refactor the code to avoid having to specify stream config as part of both create and start. |
|
| 59 |
-func (s *router) postContainerExecStart(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
|
| 60 |
- if err := httputils.ParseForm(r); err != nil {
|
|
| 61 |
- return err |
|
| 62 |
- } |
|
| 63 |
- |
|
| 64 |
- version := httputils.VersionFromContext(ctx) |
|
| 65 |
- if version.GreaterThan("1.21") {
|
|
| 66 |
- if err := httputils.CheckForJSON(r); err != nil {
|
|
| 67 |
- return err |
|
| 68 |
- } |
|
| 69 |
- } |
|
| 70 |
- |
|
| 71 |
- var ( |
|
| 72 |
- execName = vars["name"] |
|
| 73 |
- stdin, inStream io.ReadCloser |
|
| 74 |
- stdout, stderr, outStream io.Writer |
|
| 75 |
- ) |
|
| 76 |
- |
|
| 77 |
- execStartCheck := &types.ExecStartCheck{}
|
|
| 78 |
- if err := json.NewDecoder(r.Body).Decode(execStartCheck); err != nil {
|
|
| 79 |
- return err |
|
| 80 |
- } |
|
| 81 |
- |
|
| 82 |
- if exists, err := s.daemon.ExecExists(execName); !exists {
|
|
| 83 |
- return err |
|
| 84 |
- } |
|
| 85 |
- |
|
| 86 |
- if !execStartCheck.Detach {
|
|
| 87 |
- var err error |
|
| 88 |
- // Setting up the streaming http interface. |
|
| 89 |
- inStream, outStream, err = httputils.HijackConnection(w) |
|
| 90 |
- if err != nil {
|
|
| 91 |
- return err |
|
| 92 |
- } |
|
| 93 |
- defer httputils.CloseStreams(inStream, outStream) |
|
| 94 |
- |
|
| 95 |
- if _, ok := r.Header["Upgrade"]; ok {
|
|
| 96 |
- 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") |
|
| 97 |
- } else {
|
|
| 98 |
- fmt.Fprintf(outStream, "HTTP/1.1 200 OK\r\nContent-Type: application/vnd.docker.raw-stream\r\n\r\n") |
|
| 99 |
- } |
|
| 100 |
- |
|
| 101 |
- stdin = inStream |
|
| 102 |
- stdout = outStream |
|
| 103 |
- if !execStartCheck.Tty {
|
|
| 104 |
- stderr = stdcopy.NewStdWriter(outStream, stdcopy.Stderr) |
|
| 105 |
- stdout = stdcopy.NewStdWriter(outStream, stdcopy.Stdout) |
|
| 106 |
- } |
|
| 107 |
- } else {
|
|
| 108 |
- outStream = w |
|
| 109 |
- } |
|
| 110 |
- |
|
| 111 |
- // Now run the user process in container. |
|
| 112 |
- if err := s.daemon.ContainerExecStart(execName, stdin, stdout, stderr); err != nil {
|
|
| 113 |
- if execStartCheck.Detach {
|
|
| 114 |
- return err |
|
| 115 |
- } |
|
| 116 |
- logrus.Errorf("Error running exec in container: %v\n", err)
|
|
| 117 |
- } |
|
| 118 |
- return nil |
|
| 119 |
-} |
|
| 120 |
- |
|
| 121 |
-func (s *router) postContainerExecResize(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
|
| 122 |
- if err := httputils.ParseForm(r); err != nil {
|
|
| 123 |
- return err |
|
| 124 |
- } |
|
| 125 |
- height, err := strconv.Atoi(r.Form.Get("h"))
|
|
| 126 |
- if err != nil {
|
|
| 127 |
- return err |
|
| 128 |
- } |
|
| 129 |
- width, err := strconv.Atoi(r.Form.Get("w"))
|
|
| 130 |
- if err != nil {
|
|
| 131 |
- return err |
|
| 132 |
- } |
|
| 133 |
- |
|
| 134 |
- return s.daemon.ContainerExecResize(vars["name"], height, width) |
|
| 135 |
-} |
| 136 | 1 |
deleted file mode 100644 |
| ... | ... |
@@ -1,33 +0,0 @@ |
| 1 |
-package local |
|
| 2 |
- |
|
| 3 |
-import ( |
|
| 4 |
- "net/http" |
|
| 5 |
- |
|
| 6 |
- "github.com/docker/docker/api/server/httputils" |
|
| 7 |
- "golang.org/x/net/context" |
|
| 8 |
-) |
|
| 9 |
- |
|
| 10 |
-// getContainersByName inspects containers configuration and serializes it as json. |
|
| 11 |
-func (s *router) getContainersByName(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
|
| 12 |
- displaySize := httputils.BoolValue(r, "size") |
|
| 13 |
- |
|
| 14 |
- var json interface{}
|
|
| 15 |
- var err error |
|
| 16 |
- |
|
| 17 |
- version := httputils.VersionFromContext(ctx) |
|
| 18 |
- |
|
| 19 |
- switch {
|
|
| 20 |
- case version.LessThan("1.20"):
|
|
| 21 |
- json, err = s.daemon.ContainerInspectPre120(vars["name"]) |
|
| 22 |
- case version.Equal("1.20"):
|
|
| 23 |
- json, err = s.daemon.ContainerInspect120(vars["name"]) |
|
| 24 |
- default: |
|
| 25 |
- json, err = s.daemon.ContainerInspect(vars["name"], displaySize) |
|
| 26 |
- } |
|
| 27 |
- |
|
| 28 |
- if err != nil {
|
|
| 29 |
- return err |
|
| 30 |
- } |
|
| 31 |
- |
|
| 32 |
- return httputils.WriteJSON(w, http.StatusOK, json) |
|
| 33 |
-} |