github.com/containers/podman/v2@v2.2.2-0.20210501105131-c1e07d070c4c/pkg/api/handlers/compat/containers_attach.go (about)

     1  package compat
     2  
     3  import (
     4  	"net/http"
     5  
     6  	"github.com/containers/podman/v2/libpod"
     7  	"github.com/containers/podman/v2/libpod/define"
     8  	"github.com/containers/podman/v2/pkg/api/handlers/utils"
     9  	"github.com/containers/podman/v2/pkg/api/server/idle"
    10  	"github.com/gorilla/schema"
    11  	"github.com/pkg/errors"
    12  	"github.com/sirupsen/logrus"
    13  )
    14  
    15  func AttachContainer(w http.ResponseWriter, r *http.Request) {
    16  	runtime := r.Context().Value("runtime").(*libpod.Runtime)
    17  	decoder := r.Context().Value("decoder").(*schema.Decoder)
    18  
    19  	query := struct {
    20  		DetachKeys string `schema:"detachKeys"`
    21  		Logs       bool   `schema:"logs"`
    22  		Stream     bool   `schema:"stream"`
    23  		Stdin      bool   `schema:"stdin"`
    24  		Stdout     bool   `schema:"stdout"`
    25  		Stderr     bool   `schema:"stderr"`
    26  	}{
    27  		Stream: true,
    28  	}
    29  	if err := decoder.Decode(&query, r.URL.Query()); err != nil {
    30  		utils.Error(w, "Error parsing parameters", http.StatusBadRequest, err)
    31  		return
    32  	}
    33  
    34  	// Detach keys: explicitly set to "" is very different from unset
    35  	// TODO: Our format for parsing these may be different from Docker.
    36  	var detachKeys *string
    37  	if _, found := r.URL.Query()["detachKeys"]; found {
    38  		detachKeys = &query.DetachKeys
    39  	}
    40  
    41  	streams := new(libpod.HTTPAttachStreams)
    42  	streams.Stdout = true
    43  	streams.Stderr = true
    44  	streams.Stdin = true
    45  	useStreams := false
    46  	if _, found := r.URL.Query()["stdin"]; found {
    47  		streams.Stdin = query.Stdin
    48  		useStreams = true
    49  	}
    50  	if _, found := r.URL.Query()["stdout"]; found {
    51  		streams.Stdout = query.Stdout
    52  		useStreams = true
    53  	}
    54  	if _, found := r.URL.Query()["stderr"]; found {
    55  		streams.Stderr = query.Stderr
    56  		useStreams = true
    57  	}
    58  	if !useStreams {
    59  		streams = nil
    60  	}
    61  	if useStreams && !streams.Stdout && !streams.Stderr && !streams.Stdin {
    62  		utils.Error(w, "Parameter conflict", http.StatusBadRequest, errors.Errorf("at least one of stdin, stdout, stderr must be true"))
    63  		return
    64  	}
    65  
    66  	// At least one of these must be set
    67  	if !query.Stream && !query.Logs {
    68  		utils.Error(w, "Unsupported parameter", http.StatusBadRequest, errors.Errorf("at least one of Logs or Stream must be set"))
    69  		return
    70  	}
    71  
    72  	name := utils.GetName(r)
    73  	ctr, err := runtime.LookupContainer(name)
    74  	if err != nil {
    75  		utils.ContainerNotFound(w, name, err)
    76  		return
    77  	}
    78  
    79  	state, err := ctr.State()
    80  	if err != nil {
    81  		utils.InternalServerError(w, err)
    82  		return
    83  	}
    84  	// For Docker compatibility, we need to re-initialize containers in these states.
    85  	if state == define.ContainerStateConfigured || state == define.ContainerStateExited {
    86  		if err := ctr.Init(r.Context(), ctr.PodID() != ""); err != nil {
    87  			utils.Error(w, "Container in wrong state", http.StatusConflict, errors.Wrapf(err, "error preparing container %s for attach", ctr.ID()))
    88  			return
    89  		}
    90  	} else if !(state == define.ContainerStateCreated || state == define.ContainerStateRunning) {
    91  		utils.InternalServerError(w, errors.Wrapf(define.ErrCtrStateInvalid, "can only attach to created or running containers - currently in state %s", state.String()))
    92  		return
    93  	}
    94  
    95  	logErr := func(e error) {
    96  		logrus.Error(errors.Wrapf(e, "error attaching to container %s", ctr.ID()))
    97  	}
    98  
    99  	// Perform HTTP attach.
   100  	// HTTPAttach will handle everything about the connection from here on
   101  	// (including closing it and writing errors to it).
   102  	hijackChan := make(chan bool, 1)
   103  	err = ctr.HTTPAttach(r, w, streams, detachKeys, nil, query.Stream, query.Logs, hijackChan)
   104  
   105  	if <-hijackChan {
   106  		// If connection was Hijacked, we have to signal it's being closed
   107  		t := r.Context().Value("idletracker").(*idle.Tracker)
   108  		defer t.Close()
   109  
   110  		if err != nil {
   111  			// Cannot report error to client as a 500 as the Upgrade set status to 101
   112  			logErr(err)
   113  		}
   114  	} else {
   115  		// If the Hijack failed we are going to assume we can still inform client of failure
   116  		utils.InternalServerError(w, err)
   117  		logErr(err)
   118  	}
   119  	logrus.Debugf("Attach for container %s completed successfully", ctr.ID())
   120  }