summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile2
-rw-r--r--pkg/api/handlers/compat/containers_attach.go6
-rw-r--r--pkg/api/handlers/compat/exec.go6
-rw-r--r--pkg/api/server/idle/tracker.go96
-rw-r--r--pkg/api/server/idletracker/idletracker.go74
-rw-r--r--pkg/api/server/server.go22
-rw-r--r--pkg/specgen/generate/container.go39
-rw-r--r--pkg/specgen/generate/security.go3
-rw-r--r--test/e2e/cp_test.go4
-rw-r--r--test/e2e/create_test.go17
-rw-r--r--test/e2e/network_create_test.go2
-rw-r--r--test/e2e/network_test.go4
-rw-r--r--test/system/030-run.bats4
-rw-r--r--test/system/075-exec.bats4
14 files changed, 181 insertions, 102 deletions
diff --git a/Makefile b/Makefile
index 8a75c11fb..99a32eb13 100644
--- a/Makefile
+++ b/Makefile
@@ -643,7 +643,7 @@ install.libseccomp.sudo:
pkg/varlink/iopodman.go: .gopathok pkg/varlink/io.podman.varlink
ifneq (,$(findstring Linux,$(shell uname -s)))
# Only generate the varlink code on Linux (see issue #4814).
- GO111MODULE=off $(GO) generate ./pkg/varlink/...
+ $(GO) generate ./pkg/varlink/...
endif
API.md: pkg/varlink/io.podman.varlink
diff --git a/pkg/api/handlers/compat/containers_attach.go b/pkg/api/handlers/compat/containers_attach.go
index e20d48d86..4a1196c89 100644
--- a/pkg/api/handlers/compat/containers_attach.go
+++ b/pkg/api/handlers/compat/containers_attach.go
@@ -6,7 +6,7 @@ import (
"github.com/containers/podman/v2/libpod"
"github.com/containers/podman/v2/libpod/define"
"github.com/containers/podman/v2/pkg/api/handlers/utils"
- "github.com/containers/podman/v2/pkg/api/server/idletracker"
+ "github.com/containers/podman/v2/pkg/api/server/idle"
"github.com/gorilla/schema"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
@@ -92,7 +92,7 @@ func AttachContainer(w http.ResponseWriter, r *http.Request) {
return
}
- idleTracker := r.Context().Value("idletracker").(*idletracker.IdleTracker)
+ idleTracker := r.Context().Value("idletracker").(*idle.Tracker)
hijackChan := make(chan bool, 1)
// Perform HTTP attach.
@@ -109,7 +109,7 @@ func AttachContainer(w http.ResponseWriter, r *http.Request) {
// We do need to tell the idle tracker that the
// connection has been closed, though. We can guarantee
// that is true after HTTPAttach exits.
- idleTracker.TrackHijackedClosed()
+ idleTracker.Close()
} else {
// A hijack was not successfully completed. We need to
// report the error normally.
diff --git a/pkg/api/handlers/compat/exec.go b/pkg/api/handlers/compat/exec.go
index 1db950f85..df51293c2 100644
--- a/pkg/api/handlers/compat/exec.go
+++ b/pkg/api/handlers/compat/exec.go
@@ -10,7 +10,7 @@ import (
"github.com/containers/podman/v2/libpod/define"
"github.com/containers/podman/v2/pkg/api/handlers"
"github.com/containers/podman/v2/pkg/api/handlers/utils"
- "github.com/containers/podman/v2/pkg/api/server/idletracker"
+ "github.com/containers/podman/v2/pkg/api/server/idle"
"github.com/containers/podman/v2/pkg/specgen/generate"
"github.com/gorilla/mux"
"github.com/pkg/errors"
@@ -174,7 +174,7 @@ func ExecStartHandler(w http.ResponseWriter, r *http.Request) {
return
}
- idleTracker := r.Context().Value("idletracker").(*idletracker.IdleTracker)
+ idleTracker := r.Context().Value("idletracker").(*idle.Tracker)
hijackChan := make(chan bool, 1)
if err := sessionCtr.ExecHTTPStartAndAttach(sessionID, r, w, nil, nil, nil, hijackChan); err != nil {
@@ -186,7 +186,7 @@ func ExecStartHandler(w http.ResponseWriter, r *http.Request) {
// We do need to tell the idle tracker that the
// connection has been closed, though. We can guarantee
// that is true after HTTPAttach exits.
- idleTracker.TrackHijackedClosed()
+ idleTracker.Close()
} else {
// A hijack was not successfully completed. We need to
// report the error normally.
diff --git a/pkg/api/server/idle/tracker.go b/pkg/api/server/idle/tracker.go
new file mode 100644
index 000000000..1b378c492
--- /dev/null
+++ b/pkg/api/server/idle/tracker.go
@@ -0,0 +1,96 @@
+package idle
+
+import (
+ "net"
+ "net/http"
+ "sync"
+ "time"
+
+ "github.com/sirupsen/logrus"
+)
+
+// Tracker holds the state for the server's idle tracking
+type Tracker struct {
+ // Duration is the API idle window
+ Duration time.Duration
+ hijacked int // count of active connections managed by handlers
+ managed map[net.Conn]struct{} // set of active connections managed by http package
+ mux sync.Mutex // protect managed map
+ timer *time.Timer
+ total int // total number of connections made to this server instance
+}
+
+// NewTracker creates and initializes a new Tracker object
+// For best behavior, duration should be 2x http idle connection timeout
+func NewTracker(idle time.Duration) *Tracker {
+ return &Tracker{
+ managed: make(map[net.Conn]struct{}),
+ Duration: idle,
+ timer: time.NewTimer(idle),
+ }
+}
+
+// ConnState is called on HTTP connection state changes.
+// - Once StateHijacked, StateClose is _NOT_ called on that connection
+// - There are two "idle" timeouts, the http idle connection (not to be confused with the TCP/IP idle socket timeout)
+// and the API idle window. The caller should set the http idle timeout to 2x the time provided to NewTacker() which
+// is the API idle window.
+func (t *Tracker) ConnState(conn net.Conn, state http.ConnState) {
+ t.mux.Lock()
+ defer t.mux.Unlock()
+
+ logrus.Debugf("IdleTracker %p:%v %dm+%dh/%dt connection(s)", conn, state, len(t.managed), t.hijacked, t.TotalConnections())
+ switch state {
+ case http.StateNew, http.StateActive:
+ // stop the API timer when the server transitions any connection to an "active" state
+ t.managed[conn] = struct{}{}
+ t.timer.Stop()
+ t.total++
+ case http.StateHijacked:
+ // hijacked connections should call Close() when finished.
+ // Note: If a handler hijack's a connection and then doesn't Close() it,
+ // the API timer will not fire and the server will _NOT_ timeout.
+ delete(t.managed, conn)
+ t.hijacked++
+ case http.StateIdle:
+ // When any connection goes into the http idle state, we know:
+ // - we have an active connection
+ // - the API timer should not be counting down (See case StateNew/StateActive)
+ break
+ case http.StateClosed:
+ oldActive := t.ActiveConnections()
+
+ // Either the server or a hijacking handler has closed the http connection to a client
+ if _, found := t.managed[conn]; found {
+ delete(t.managed, conn)
+ } else {
+ t.hijacked-- // guarded by t.mux above
+ }
+
+ // Transitioned from any "active" connection to no connections
+ if oldActive > 0 && t.ActiveConnections() == 0 {
+ t.timer.Stop() // See library source for Reset() issues and why they are not fixed
+ t.timer.Reset(t.Duration) // Restart the API window timer
+ }
+ }
+}
+
+// Close is used to update Tracker that a StateHijacked connection has been closed by handler (StateClosed)
+func (t *Tracker) Close() {
+ t.ConnState(nil, http.StateClosed)
+}
+
+// ActiveConnections returns the number of current managed or StateHijacked connections
+func (t *Tracker) ActiveConnections() int {
+ return len(t.managed) + t.hijacked
+}
+
+// TotalConnections returns total number of connections made to this instance of the service
+func (t *Tracker) TotalConnections() int {
+ return t.total
+}
+
+// Done is called when idle timer has expired
+func (t *Tracker) Done() <-chan time.Time {
+ return t.timer.C
+}
diff --git a/pkg/api/server/idletracker/idletracker.go b/pkg/api/server/idletracker/idletracker.go
deleted file mode 100644
index 1ee905a99..000000000
--- a/pkg/api/server/idletracker/idletracker.go
+++ /dev/null
@@ -1,74 +0,0 @@
-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 e7c031234..09a9f6370 100644
--- a/pkg/api/server/server.go
+++ b/pkg/api/server/server.go
@@ -16,7 +16,7 @@ import (
"github.com/containers/podman/v2/libpod"
"github.com/containers/podman/v2/pkg/api/handlers"
- "github.com/containers/podman/v2/pkg/api/server/idletracker"
+ "github.com/containers/podman/v2/pkg/api/server/idle"
"github.com/coreos/go-systemd/v22/activation"
"github.com/coreos/go-systemd/v22/daemon"
"github.com/gorilla/mux"
@@ -26,14 +26,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.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 *idle.Tracker // 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
@@ -70,13 +70,13 @@ func newServer(runtime *libpod.Runtime, duration time.Duration, listener *net.Li
}
router := mux.NewRouter().UseEncodedPath()
- idle := idletracker.NewIdleTracker(duration)
+ idle := idle.NewTracker(duration)
server := APIServer{
Server: http.Server{
Handler: router,
ReadHeaderTimeout: 20 * time.Second,
- IdleTimeout: duration,
+ IdleTimeout: duration * 2,
ConnState: idle.ConnState,
ErrorLog: log.New(logrus.StandardLogger().Out, "", 0),
},
diff --git a/pkg/specgen/generate/container.go b/pkg/specgen/generate/container.go
index 927e6b7fa..2ee8f2441 100644
--- a/pkg/specgen/generate/container.go
+++ b/pkg/specgen/generate/container.go
@@ -13,6 +13,7 @@ import (
"github.com/containers/podman/v2/pkg/specgen"
spec "github.com/opencontainers/runtime-spec/specs-go"
"github.com/pkg/errors"
+ "github.com/sirupsen/logrus"
"golang.org/x/sys/unix"
)
@@ -33,7 +34,43 @@ func CompleteSpec(ctx context.Context, r *libpod.Runtime, s *specgen.SpecGenerat
_, mediaType, err := newImage.Manifest(ctx)
if err != nil {
- return nil, err
+ if errors.Cause(err) != image.ErrImageIsBareList {
+ return nil, err
+ }
+ // if err is not runnable image
+ // use the local store image with repo@digest matches with the list, if exists
+ manifestByte, manifestType, err := newImage.GetManifest(ctx, nil)
+ if err != nil {
+ return nil, err
+ }
+ list, err := manifest.ListFromBlob(manifestByte, manifestType)
+ if err != nil {
+ return nil, err
+ }
+ images, err := r.ImageRuntime().GetImages()
+ if err != nil {
+ return nil, err
+ }
+ findLocal := false
+ listDigest, err := list.ChooseInstance(r.SystemContext())
+ if err != nil {
+ return nil, err
+ }
+ for _, img := range images {
+ for _, imageDigest := range img.Digests() {
+ if imageDigest == listDigest {
+ newImage = img
+ s.Image = img.ID()
+ mediaType = manifestType
+ findLocal = true
+ logrus.Debug("image contains manifest list, using image from local storage")
+ break
+ }
+ }
+ }
+ if !findLocal {
+ return nil, image.ErrImageIsBareList
+ }
}
if s.HealthConfig == nil && mediaType == manifest.DockerV2Schema2MediaType {
diff --git a/pkg/specgen/generate/security.go b/pkg/specgen/generate/security.go
index 7c818cf62..d17cd4a9a 100644
--- a/pkg/specgen/generate/security.go
+++ b/pkg/specgen/generate/security.go
@@ -131,12 +131,13 @@ func securityConfigureGenerator(s *specgen.SpecGenerator, g *generate.Generator,
}
configSpec := g.Config
+ configSpec.Process.Capabilities.Ambient = []string{}
configSpec.Process.Capabilities.Bounding = caplist
+ configSpec.Process.Capabilities.Inheritable = caplist
if s.User == "" || s.User == "root" || s.User == "0" {
configSpec.Process.Capabilities.Effective = caplist
configSpec.Process.Capabilities.Permitted = caplist
- configSpec.Process.Capabilities.Inheritable = caplist
} else {
userCaps, err := capabilities.NormalizeCapabilities(s.CapAdd)
if err != nil {
diff --git a/test/e2e/cp_test.go b/test/e2e/cp_test.go
index a53485fa4..0a9fa990c 100644
--- a/test/e2e/cp_test.go
+++ b/test/e2e/cp_test.go
@@ -269,11 +269,11 @@ var _ = Describe("Podman cp", func() {
session.WaitWithDefaultTimeout()
Expect(session.ExitCode()).To(Equal(0))
- session = podmanTest.Podman([]string{"exec", "-u", "testuser", "testctr", "touch", "testfile"})
+ session = podmanTest.Podman([]string{"exec", "-u", "testuser", "testctr", "touch", "/tmp/testfile"})
session.WaitWithDefaultTimeout()
Expect(session.ExitCode()).To(Equal(0))
- session = podmanTest.Podman([]string{"cp", "--pause=false", "testctr:testfile", "testfile1"})
+ session = podmanTest.Podman([]string{"cp", "--pause=false", "testctr:/tmp/testfile", "testfile1"})
session.WaitWithDefaultTimeout()
Expect(session.ExitCode()).To(Equal(0))
diff --git a/test/e2e/create_test.go b/test/e2e/create_test.go
index e6491ce9b..96a234446 100644
--- a/test/e2e/create_test.go
+++ b/test/e2e/create_test.go
@@ -609,4 +609,21 @@ var _ = Describe("Podman create", func() {
Expect(session.ExitCode()).ToNot(BeZero())
})
+ It("create use local store image if input image contains a manifest list", func() {
+ session := podmanTest.Podman([]string{"pull", BB})
+ session.WaitWithDefaultTimeout()
+ Expect(session.ExitCode()).To(BeZero())
+
+ session = podmanTest.Podman([]string{"manifest", "create", "mylist"})
+ session.WaitWithDefaultTimeout()
+ Expect(session.ExitCode()).To(Equal(0))
+
+ session = podmanTest.Podman([]string{"manifest", "add", "--all", "mylist", BB})
+ session.WaitWithDefaultTimeout()
+ Expect(session.ExitCode()).To(BeZero())
+
+ session = podmanTest.Podman([]string{"create", "mylist"})
+ session.WaitWithDefaultTimeout()
+ Expect(session.ExitCode()).To(BeZero())
+ })
})
diff --git a/test/e2e/network_create_test.go b/test/e2e/network_create_test.go
index a81ca19eb..edd76739f 100644
--- a/test/e2e/network_create_test.go
+++ b/test/e2e/network_create_test.go
@@ -58,7 +58,7 @@ func genericPluginsToPortMap(plugins interface{}, pluginType string) (network.Po
func (p *PodmanTestIntegration) removeCNINetwork(name string) {
session := p.Podman([]string{"network", "rm", "-f", name})
session.WaitWithDefaultTimeout()
- Expect(session.ExitCode()).To(BeZero())
+ Expect(session.ExitCode()).To(BeNumerically("<=", 1))
}
func removeNetworkDevice(name string) {
diff --git a/test/e2e/network_test.go b/test/e2e/network_test.go
index 79b530815..aae82e292 100644
--- a/test/e2e/network_test.go
+++ b/test/e2e/network_test.go
@@ -135,7 +135,6 @@ var _ = Describe("Podman network", func() {
})
It("podman network rm", func() {
- SkipIfRootless("FIXME: This one is definitely broken in rootless mode")
// Setup, use uuid to prevent conflict with other tests
uuid := stringid.GenerateNonCryptoID()
secondPath := filepath.Join(podmanTest.CNIConfigDir, fmt.Sprintf("%s.conflist", uuid))
@@ -276,6 +275,7 @@ var _ = Describe("Podman network", func() {
session := podmanTest.Podman([]string{"network", "create", netName})
session.WaitWithDefaultTimeout()
Expect(session.ExitCode()).To(BeZero())
+ defer podmanTest.removeCNINetwork(netName)
session = podmanTest.Podman([]string{"pod", "create", "--network", netName})
session.WaitWithDefaultTimeout()
@@ -311,11 +311,13 @@ var _ = Describe("Podman network", func() {
session := podmanTest.Podman([]string{"network", "create", netName1})
session.WaitWithDefaultTimeout()
Expect(session.ExitCode()).To(BeZero())
+ defer podmanTest.removeCNINetwork(netName1)
netName2 := "net2"
session = podmanTest.Podman([]string{"network", "create", netName2})
session.WaitWithDefaultTimeout()
Expect(session.ExitCode()).To(BeZero())
+ defer podmanTest.removeCNINetwork(netName2)
session = podmanTest.Podman([]string{"network", "rm", netName1, netName2})
session.WaitWithDefaultTimeout()
diff --git a/test/system/030-run.bats b/test/system/030-run.bats
index eb740f04b..766948ecc 100644
--- a/test/system/030-run.bats
+++ b/test/system/030-run.bats
@@ -319,7 +319,7 @@ echo $rand | 0 | $rand
# #6991 : /etc/passwd is modifiable
@test "podman run : --userns=keep-id: passwd file is modifiable" {
- run_podman run -d --userns=keep-id $IMAGE sh -c 'while ! test -e /stop; do sleep 0.1; done'
+ run_podman run -d --userns=keep-id --cap-add=dac_override $IMAGE sh -c 'while ! test -e /tmp/stop; do sleep 0.1; done'
cid="$output"
# Assign a UID that is (a) not in our image /etc/passwd and (b) not
@@ -340,7 +340,7 @@ echo $rand | 0 | $rand
is "$output" "newuser3:x:$uid:999:$gecos:/home/newuser3:/bin/sh" \
"newuser3 added to /etc/passwd in container"
- run_podman exec $cid touch /stop
+ run_podman exec $cid touch /tmp/stop
run_podman wait $cid
}
diff --git a/test/system/075-exec.bats b/test/system/075-exec.bats
index e9db8c27e..edd7dedc4 100644
--- a/test/system/075-exec.bats
+++ b/test/system/075-exec.bats
@@ -92,14 +92,14 @@ load helpers
# #6829 : add username to /etc/passwd inside container if --userns=keep-id
@test "podman exec - with keep-id" {
run_podman run -d --userns=keep-id $IMAGE sh -c \
- "echo READY;while [ ! -f /stop ]; do sleep 1; done"
+ "echo READY;while [ ! -f /tmp/stop ]; do sleep 1; done"
cid="$output"
wait_for_ready $cid
run_podman exec $cid id -un
is "$output" "$(id -un)" "container is running as current user"
- run_podman exec --user=$(id -un) $cid touch /stop
+ run_podman exec --user=$(id -un) $cid touch /tmp/stop
run_podman wait $cid
run_podman rm $cid
}