summaryrefslogtreecommitdiff
path: root/pkg
diff options
context:
space:
mode:
Diffstat (limited to 'pkg')
-rw-r--r--pkg/api/handlers/compat/containers_attach.go79
-rw-r--r--pkg/api/handlers/compat/exec.go14
-rw-r--r--pkg/api/server/handler_api.go1
-rw-r--r--pkg/api/server/idletracker/idletracker.go74
-rw-r--r--pkg/api/server/server.go72
-rw-r--r--pkg/bindings/containers/attach.go8
-rw-r--r--pkg/domain/entities/pods.go2
-rw-r--r--pkg/spec/createconfig.go4
-rw-r--r--pkg/specgen/generate/container_create.go4
-rw-r--r--pkg/specgen/generate/pod_create.go4
-rw-r--r--pkg/specgen/podspecgen.go6
-rw-r--r--pkg/specgen/specgen.go7
-rw-r--r--pkg/varlinkapi/create.go4
13 files changed, 173 insertions, 106 deletions
diff --git a/pkg/api/handlers/compat/containers_attach.go b/pkg/api/handlers/compat/containers_attach.go
index 325f96b40..71586fca4 100644
--- a/pkg/api/handlers/compat/containers_attach.go
+++ b/pkg/api/handlers/compat/containers_attach.go
@@ -1,23 +1,22 @@
package compat
import (
+ "bufio"
"fmt"
+ "io"
+ "net"
"net/http"
+ "strings"
"github.com/containers/libpod/v2/libpod"
"github.com/containers/libpod/v2/libpod/define"
"github.com/containers/libpod/v2/pkg/api/handlers/utils"
+ "github.com/containers/libpod/v2/pkg/api/server/idletracker"
"github.com/gorilla/schema"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
)
-// AttachHeader is the literal header sent for upgraded/hijacked connections for
-// attach, sourced from Docker at:
-// https://raw.githubusercontent.com/moby/moby/b95fad8e51bd064be4f4e58a996924f343846c85/api/server/router/container/container_routes.go
-// Using literally to ensure compatibility with existing clients.
-const AttachHeader = "HTTP/1.1 101 UPGRADED\r\nContent-Type: application/vnd.docker.raw-stream\r\nConnection: Upgrade\r\nUpgrade: tcp\r\n\r\n"
-
func AttachContainer(w http.ResponseWriter, r *http.Request) {
runtime := r.Context().Value("runtime").(*libpod.Runtime)
decoder := r.Context().Value("decoder").(*schema.Decoder)
@@ -98,21 +97,11 @@ func AttachContainer(w http.ResponseWriter, r *http.Request) {
return
}
- // Hijack the connection
- hijacker, ok := w.(http.Hijacker)
- if !ok {
- utils.InternalServerError(w, errors.Errorf("unable to hijack connection"))
- return
- }
-
- connection, buffer, err := hijacker.Hijack()
+ connection, buffer, err := AttachConnection(w, r)
if err != nil {
- utils.InternalServerError(w, errors.Wrapf(err, "error hijacking connection"))
+ utils.InternalServerError(w, err)
return
}
-
- fmt.Fprintf(connection, AttachHeader)
-
logrus.Debugf("Hijack for attach of container %s successful", ctr.ID())
// Perform HTTP attach.
@@ -126,3 +115,57 @@ func AttachContainer(w http.ResponseWriter, r *http.Request) {
logrus.Debugf("Attach for container %s completed successfully", ctr.ID())
}
+
+type HijackedConnection struct {
+ net.Conn // Connection
+ idleTracker *idletracker.IdleTracker // Connection tracker
+}
+
+func (c HijackedConnection) Close() error {
+ logrus.Debugf("Hijacked connection closed")
+
+ c.idleTracker.TrackHijackedClosed()
+ return c.Conn.Close()
+}
+
+func AttachConnection(w http.ResponseWriter, r *http.Request) (net.Conn, *bufio.ReadWriter, error) {
+ idleTracker := r.Context().Value("idletracker").(*idletracker.IdleTracker)
+
+ // Hijack the connection
+ hijacker, ok := w.(http.Hijacker)
+ if !ok {
+ return nil, nil, errors.Errorf("unable to hijack connection")
+ }
+
+ connection, buffer, err := hijacker.Hijack()
+ if err != nil {
+ return nil, nil, errors.Wrapf(err, "error hijacking connection")
+ }
+ trackedConnection := HijackedConnection{
+ Conn: connection,
+ idleTracker: idleTracker,
+ }
+
+ WriteAttachHeaders(r, trackedConnection)
+
+ return trackedConnection, buffer, nil
+}
+
+func WriteAttachHeaders(r *http.Request, connection io.Writer) {
+ // AttachHeader is the literal header sent for upgraded/hijacked connections for
+ // attach, sourced from Docker at:
+ // https://raw.githubusercontent.com/moby/moby/b95fad8e51bd064be4f4e58a996924f343846c85/api/server/router/container/container_routes.go
+ // Using literally to ensure compatibility with existing clients.
+ c := r.Header.Get("Connection")
+ proto := r.Header.Get("Upgrade")
+ if len(proto) == 0 || !strings.EqualFold(c, "Upgrade") {
+ // OK - can't upgrade if not requested or protocol is not specified
+ fmt.Fprintf(connection,
+ "HTTP/1.1 200 OK\r\nContent-Type: application/vnd.docker.raw-stream\r\n\r\n")
+ } else {
+ // Upraded
+ fmt.Fprintf(connection,
+ "HTTP/1.1 101 UPGRADED\r\nContent-Type: application/vnd.docker.raw-stream\r\nConnection: Upgrade\r\nUpgrade: %s\r\n\r\n",
+ proto)
+ }
+}
diff --git a/pkg/api/handlers/compat/exec.go b/pkg/api/handlers/compat/exec.go
index aee4196dd..a3b8cb573 100644
--- a/pkg/api/handlers/compat/exec.go
+++ b/pkg/api/handlers/compat/exec.go
@@ -173,21 +173,11 @@ func ExecStartHandler(w http.ResponseWriter, r *http.Request) {
return
}
- // Hijack the connection
- hijacker, ok := w.(http.Hijacker)
- if !ok {
- utils.InternalServerError(w, errors.Errorf("unable to hijack connection"))
- return
- }
-
- connection, buffer, err := hijacker.Hijack()
+ connection, buffer, err := AttachConnection(w, r)
if err != nil {
- utils.InternalServerError(w, errors.Wrapf(err, "error hijacking connection"))
+ utils.InternalServerError(w, err)
return
}
-
- fmt.Fprintf(connection, AttachHeader)
-
logrus.Debugf("Hijack for attach of container %s exec session %s successful", sessionCtr.ID(), sessionID)
if err := sessionCtr.ExecHTTPStartAndAttach(sessionID, connection, buffer, nil, nil, nil); err != nil {
diff --git a/pkg/api/server/handler_api.go b/pkg/api/server/handler_api.go
index b0fd932ba..53fe8952b 100644
--- a/pkg/api/server/handler_api.go
+++ b/pkg/api/server/handler_api.go
@@ -37,6 +37,7 @@ func (s *APIServer) APIHandler(h http.HandlerFunc) http.HandlerFunc {
c := context.WithValue(r.Context(), "decoder", s.Decoder) //nolint
c = context.WithValue(c, "runtime", s.Runtime) //nolint
c = context.WithValue(c, "shutdownFunc", s.Shutdown) //nolint
+ c = context.WithValue(c, "idletracker", s.idleTracker) //nolint
r = r.WithContext(c)
h(w, r)
diff --git a/pkg/api/server/idletracker/idletracker.go b/pkg/api/server/idletracker/idletracker.go
new file mode 100644
index 000000000..1ee905a99
--- /dev/null
+++ b/pkg/api/server/idletracker/idletracker.go
@@ -0,0 +1,74 @@
+package idletracker
+
+import (
+ "net"
+ "net/http"
+ "sync"
+ "time"
+
+ "github.com/sirupsen/logrus"
+)
+
+type IdleTracker struct {
+ http map[net.Conn]struct{}
+ hijacked int
+ total int
+ mux sync.Mutex
+ timer *time.Timer
+ Duration time.Duration
+}
+
+func NewIdleTracker(idle time.Duration) *IdleTracker {
+ return &IdleTracker{
+ http: make(map[net.Conn]struct{}),
+ Duration: idle,
+ timer: time.NewTimer(idle),
+ }
+}
+
+func (t *IdleTracker) ConnState(conn net.Conn, state http.ConnState) {
+ t.mux.Lock()
+ defer t.mux.Unlock()
+
+ oldActive := t.ActiveConnections()
+ logrus.Debugf("IdleTracker %p:%v %d/%d connection(s)", conn, state, oldActive, t.TotalConnections())
+ switch state {
+ case http.StateNew, http.StateActive:
+ t.http[conn] = struct{}{}
+ // stop the timer if we transitioned from idle
+ if oldActive == 0 {
+ t.timer.Stop()
+ }
+ t.total++
+ case http.StateHijacked:
+ // hijacked connections are handled elsewhere
+ delete(t.http, conn)
+ t.hijacked++
+ case http.StateIdle, http.StateClosed:
+ delete(t.http, conn)
+ // Restart the timer if we've become idle
+ if oldActive > 0 && len(t.http) == 0 {
+ t.timer.Stop()
+ t.timer.Reset(t.Duration)
+ }
+ }
+}
+
+func (t *IdleTracker) TrackHijackedClosed() {
+ t.mux.Lock()
+ defer t.mux.Unlock()
+
+ t.hijacked--
+}
+
+func (t *IdleTracker) ActiveConnections() int {
+ return len(t.http) + t.hijacked
+}
+
+func (t *IdleTracker) TotalConnections() int {
+ return t.total
+}
+
+func (t *IdleTracker) Done() <-chan time.Time {
+ return t.timer.C
+}
diff --git a/pkg/api/server/server.go b/pkg/api/server/server.go
index 8af6d3186..1c6007745 100644
--- a/pkg/api/server/server.go
+++ b/pkg/api/server/server.go
@@ -10,12 +10,12 @@ import (
"runtime"
goRuntime "runtime"
"strings"
- "sync"
"syscall"
"time"
"github.com/containers/libpod/v2/libpod"
"github.com/containers/libpod/v2/pkg/api/handlers"
+ "github.com/containers/libpod/v2/pkg/api/server/idletracker"
"github.com/coreos/go-systemd/v22/activation"
"github.com/gorilla/mux"
"github.com/gorilla/schema"
@@ -24,14 +24,14 @@ import (
)
type APIServer struct {
- http.Server // The HTTP work happens here
- *schema.Decoder // Decoder for Query parameters to structs
- context.Context // Context to carry objects to handlers
- *libpod.Runtime // Where the real work happens
- net.Listener // mux for routing HTTP API calls to libpod routines
- context.CancelFunc // Stop APIServer
- idleTracker *IdleTracker // Track connections to support idle shutdown
- pprof *http.Server // Sidecar http server for providing performance data
+ http.Server // The HTTP work happens here
+ *schema.Decoder // Decoder for Query parameters to structs
+ context.Context // Context to carry objects to handlers
+ *libpod.Runtime // Where the real work happens
+ net.Listener // mux for routing HTTP API calls to libpod routines
+ context.CancelFunc // Stop APIServer
+ idleTracker *idletracker.IdleTracker // Track connections to support idle shutdown
+ pprof *http.Server // Sidecar http server for providing performance data
}
// Number of seconds to wait for next request, if exceeded shutdown server
@@ -68,7 +68,7 @@ func newServer(runtime *libpod.Runtime, duration time.Duration, listener *net.Li
}
router := mux.NewRouter().UseEncodedPath()
- idle := NewIdleTracker(duration)
+ idle := idletracker.NewIdleTracker(duration)
server := APIServer{
Server: http.Server{
@@ -231,55 +231,3 @@ func (s *APIServer) Shutdown() error {
func (s *APIServer) Close() error {
return s.Server.Close()
}
-
-type IdleTracker struct {
- active map[net.Conn]struct{}
- total int
- mux sync.Mutex
- timer *time.Timer
- Duration time.Duration
-}
-
-func NewIdleTracker(idle time.Duration) *IdleTracker {
- return &IdleTracker{
- active: make(map[net.Conn]struct{}),
- Duration: idle,
- timer: time.NewTimer(idle),
- }
-}
-
-func (t *IdleTracker) ConnState(conn net.Conn, state http.ConnState) {
- t.mux.Lock()
- defer t.mux.Unlock()
-
- oldActive := len(t.active)
- logrus.Debugf("IdleTracker %p:%v %d/%d connection(s)", conn, state, t.ActiveConnections(), t.TotalConnections())
- switch state {
- case http.StateNew, http.StateActive, http.StateHijacked:
- t.active[conn] = struct{}{}
- // stop the timer if we transitioned from idle
- if oldActive == 0 {
- t.timer.Stop()
- }
- t.total++
- case http.StateIdle, http.StateClosed:
- delete(t.active, conn)
- // Restart the timer if we've become idle
- if oldActive > 0 && len(t.active) == 0 {
- t.timer.Stop()
- t.timer.Reset(t.Duration)
- }
- }
-}
-
-func (t *IdleTracker) ActiveConnections() int {
- return len(t.active)
-}
-
-func (t *IdleTracker) TotalConnections() int {
- return t.total
-}
-
-func (t *IdleTracker) Done() <-chan time.Time {
- return t.timer.C
-}
diff --git a/pkg/bindings/containers/attach.go b/pkg/bindings/containers/attach.go
index 077bb244f..297563688 100644
--- a/pkg/bindings/containers/attach.go
+++ b/pkg/bindings/containers/attach.go
@@ -457,15 +457,15 @@ func ExecStartAndAttach(ctx context.Context, sessionID string, streams *define.A
switch {
case fd == 0:
- if streams.AttachOutput {
+ if streams.AttachInput {
+ // Write STDIN to STDOUT (echoing characters
+ // typed by another attach session)
if _, err := streams.OutputStream.Write(frame[0:l]); err != nil {
return err
}
}
case fd == 1:
- if streams.AttachInput {
- // Write STDIN to STDOUT (echoing characters
- // typed by another attach session)
+ if streams.AttachOutput {
if _, err := streams.OutputStream.Write(frame[0:l]); err != nil {
return err
}
diff --git a/pkg/domain/entities/pods.go b/pkg/domain/entities/pods.go
index 8023034ef..9e9b834ef 100644
--- a/pkg/domain/entities/pods.go
+++ b/pkg/domain/entities/pods.go
@@ -104,6 +104,7 @@ type PodRmReport struct {
type PodCreateOptions struct {
CGroupParent string
+ CreateCommand []string
Hostname string
Infra bool
InfraImage string
@@ -133,6 +134,7 @@ func (p PodCreateOptions) ToPodSpecGen(s *specgen.PodSpecGenerator) {
}
s.InfraImage = p.InfraImage
s.SharedNamespaces = p.Share
+ s.PodCreateCommand = p.CreateCommand
// Networking config
s.NetNS = p.Net.Network
diff --git a/pkg/spec/createconfig.go b/pkg/spec/createconfig.go
index 879c66895..55c3238d2 100644
--- a/pkg/spec/createconfig.go
+++ b/pkg/spec/createconfig.go
@@ -406,10 +406,6 @@ func CreateContainerFromCreateConfig(ctx context.Context, r *libpod.Runtime, cre
return nil, err
}
- // Set the CreateCommand explicitly. Some (future) consumers of libpod
- // might not want to set it.
- options = append(options, libpod.WithCreateCommand())
-
ctr, err := r.NewContainer(ctx, runtimeSpec, options...)
if err != nil {
return nil, err
diff --git a/pkg/specgen/generate/container_create.go b/pkg/specgen/generate/container_create.go
index 8df5b996e..1bcd33672 100644
--- a/pkg/specgen/generate/container_create.go
+++ b/pkg/specgen/generate/container_create.go
@@ -78,7 +78,9 @@ func MakeContainer(ctx context.Context, rt *libpod.Runtime, s *specgen.SpecGener
}
options := []libpod.CtrCreateOption{}
- options = append(options, libpod.WithCreateCommand())
+ if s.ContainerCreateCommand != nil {
+ options = append(options, libpod.WithCreateCommand(s.ContainerCreateCommand))
+ }
var newImage *image.Image
if s.Rootfs != "" {
diff --git a/pkg/specgen/generate/pod_create.go b/pkg/specgen/generate/pod_create.go
index 690651a23..4fe1b6435 100644
--- a/pkg/specgen/generate/pod_create.go
+++ b/pkg/specgen/generate/pod_create.go
@@ -93,7 +93,9 @@ func createPodOptions(p *specgen.PodSpecGenerator) ([]libpod.PodCreateOption, er
options = append(options, libpod.WithInfraContainerPorts(ports))
}
options = append(options, libpod.WithPodCgroups())
- options = append(options, libpod.WithPodCreateCommand())
+ if p.PodCreateCommand != nil {
+ options = append(options, libpod.WithPodCreateCommand(p.PodCreateCommand))
+ }
if len(p.InfraConmonPidFile) > 0 {
options = append(options, libpod.WithInfraConmonPidFile(p.InfraConmonPidFile))
}
diff --git a/pkg/specgen/podspecgen.go b/pkg/specgen/podspecgen.go
index 600d27004..3c32ec365 100644
--- a/pkg/specgen/podspecgen.go
+++ b/pkg/specgen/podspecgen.go
@@ -49,6 +49,12 @@ type PodBasicConfig struct {
// Conflicts with NoInfra=true.
// Optional.
SharedNamespaces []string `json:"shared_namespaces,omitempty"`
+ // PodCreateCommand is the command used to create this pod.
+ // This will be shown in the output of Inspect() on the pod, and may
+ // also be used by some tools that wish to recreate the pod
+ // (e.g. `podman generate systemd --new`).
+ // Optional.
+ PodCreateCommand []string `json:"pod_create_command,omitempty"`
}
// PodNetworkConfig contains networking configuration for a pod.
diff --git a/pkg/specgen/specgen.go b/pkg/specgen/specgen.go
index b4e10fa87..16d4b7c8c 100644
--- a/pkg/specgen/specgen.go
+++ b/pkg/specgen/specgen.go
@@ -135,6 +135,13 @@ type ContainerBasicConfig struct {
// Remove indicates if the container should be removed once it has been started
// and exits
Remove bool `json:"remove,omitempty"`
+ // ContainerCreateCommand is the command that was used to create this
+ // container.
+ // This will be shown in the output of Inspect() on the container, and
+ // may also be used by some tools that wish to recreate the container
+ // (e.g. `podman generate systemd --new`).
+ // Optional.
+ ContainerCreateCommand []string `json:"containerCreateCommand,omitempty"`
// PreserveFDs is a number of additional file descriptors (in addition
// to 0, 1, 2) that will be passed to the executed process. The total FDs
// passed will be 3 + PreserveFDs.
diff --git a/pkg/varlinkapi/create.go b/pkg/varlinkapi/create.go
index 5c5f075f7..ac93939d9 100644
--- a/pkg/varlinkapi/create.go
+++ b/pkg/varlinkapi/create.go
@@ -915,10 +915,6 @@ func CreateContainerFromCreateConfig(ctx context.Context, r *libpod.Runtime, cre
return nil, err
}
- // Set the CreateCommand explicitly. Some (future) consumers of libpod
- // might not want to set it.
- options = append(options, libpod.WithCreateCommand())
-
ctr, err := r.NewContainer(ctx, runtimeSpec, options...)
if err != nil {
return nil, err