summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile2
-rw-r--r--libpod/image/image.go72
-rw-r--r--pkg/api/handlers/compat/containers_attach.go6
-rw-r--r--pkg/api/handlers/compat/exec.go6
-rw-r--r--pkg/api/handlers/compat/images_build.go40
-rw-r--r--pkg/api/server/idle/tracker.go96
-rw-r--r--pkg/api/server/idletracker/idletracker.go74
-rw-r--r--pkg/api/server/register_images.go7
-rw-r--r--pkg/api/server/server.go22
-rw-r--r--pkg/bindings/images/build.go5
-rw-r--r--pkg/registries/registries.go5
-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/e2e/pull_test.go100
-rw-r--r--test/registries.conf1
-rw-r--r--test/system/030-run.bats4
-rw-r--r--test/system/075-exec.bats4
21 files changed, 375 insertions, 138 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/libpod/image/image.go b/libpod/image/image.go
index 5dfb33afb..f5bf47694 100644
--- a/libpod/image/image.go
+++ b/libpod/image/image.go
@@ -410,48 +410,92 @@ func (ir *Runtime) getLocalImage(inputName string) (string, *storage.Image, erro
if inputName == "" {
return "", nil, errors.Errorf("input name is blank")
}
+
// Check if the input name has a transport and if so strip it
dest, err := alltransports.ParseImageName(inputName)
if err == nil && dest.DockerReference() != nil {
inputName = dest.DockerReference().String()
}
- img, err := ir.getImage(stripSha256(inputName))
+ // Early check for fully-qualified images and (short) IDs.
+ img, err := ir.store.Image(stripSha256(inputName))
if err == nil {
- return inputName, img, err
+ return inputName, img, nil
}
- // container-storage wasn't able to find it in its current form
- // check if the input name has a tag, and if not, run it through
- // again
+ // Note that it's crucial to first decompose the image and check if
+ // it's a fully-qualified one or a "short name". The latter requires
+ // some normalization with search registries and the
+ // "localhost/prefix".
decomposedImage, err := decompose(inputName)
if err != nil {
+ // We may have a storage reference. We can't parse it to a
+ // reference before. Otherwise, we'd normalize "alpine" to
+ // "docker.io/library/alpine:latest" which would break the
+ // order in which we should query local images below.
+ if ref, err := is.Transport.ParseStoreReference(ir.store, inputName); err == nil {
+ img, err = is.Transport.GetStoreImage(ir.store, ref)
+ if err == nil {
+ return inputName, img, nil
+ }
+ }
return "", nil, err
}
- // The image has a registry name in it and we made sure we looked for it locally
- // with a tag. It cannot be local.
+ // The specified image is fully qualified, so it doesn't exist in the
+ // storage.
if decomposedImage.hasRegistry {
+ // However ... we may still need to normalize to docker.io:
+ // `docker.io/foo` -> `docker.io/library/foo`
+ if ref, err := is.Transport.ParseStoreReference(ir.store, inputName); err == nil {
+ img, err = is.Transport.GetStoreImage(ir.store, ref)
+ if err == nil {
+ return inputName, img, nil
+ }
+ }
return "", nil, errors.Wrapf(ErrNoSuchImage, imageError)
}
- // if the image is saved with the repository localhost, searching with localhost prepended is necessary
- // We don't need to strip the sha because we have already determined it is not an ID
- ref, err := decomposedImage.referenceWithRegistry(DefaultLocalRegistry)
+
+ // "Short-name image", so let's try out certain prefixes:
+ // 1) DefaultLocalRegistry (i.e., "localhost/)
+ // 2) Unqualified-search registries from registries.conf
+ unqualifiedSearchRegistries, err := registries.GetRegistries()
if err != nil {
return "", nil, err
}
- img, err = ir.getImage(ref.String())
+
+ for _, candidate := range append([]string{DefaultLocalRegistry}, unqualifiedSearchRegistries...) {
+ ref, err := decomposedImage.referenceWithRegistry(candidate)
+ if err != nil {
+ return "", nil, err
+ }
+ img, err := ir.store.Image(ref.String())
+ if err == nil {
+ return ref.String(), img, nil
+ }
+ }
+
+ // Backwards compat: normalize to docker.io as some users may very well
+ // rely on that.
+ ref, err := is.Transport.ParseStoreReference(ir.store, inputName)
if err == nil {
- return inputName, img, err
+ img, err = is.Transport.GetStoreImage(ir.store, ref)
+ if err == nil {
+ return inputName, img, nil
+ }
}
- // grab all the local images
+ // Last resort: look at the repotags of all images and try to find a
+ // match.
images, err := ir.GetImages()
if err != nil {
return "", nil, err
}
- // check the repotags of all images for a match
+ decomposedImage, err = decompose(inputName)
+ if err != nil {
+ return "", nil, err
+ }
repoImage, err := findImageInRepotags(decomposedImage, images)
if err == nil {
return inputName, repoImage, nil
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/handlers/compat/images_build.go b/pkg/api/handlers/compat/images_build.go
index fbaf8d10a..cd10ac6ba 100644
--- a/pkg/api/handlers/compat/images_build.go
+++ b/pkg/api/handlers/compat/images_build.go
@@ -70,37 +70,38 @@ func BuildImage(w http.ResponseWriter, r *http.Request) {
}()
query := struct {
+ BuildArgs string `schema:"buildargs"`
+ CacheFrom string `schema:"cachefrom"`
+ CpuPeriod uint64 `schema:"cpuperiod"` // nolint
+ CpuQuota int64 `schema:"cpuquota"` // nolint
+ CpuSetCpus string `schema:"cpusetcpus"` // nolint
+ CpuShares uint64 `schema:"cpushares"` // nolint
Dockerfile string `schema:"dockerfile"`
- Tag []string `schema:"t"`
ExtraHosts string `schema:"extrahosts"`
- Remote string `schema:"remote"`
- Quiet bool `schema:"q"`
+ ForceRm bool `schema:"forcerm"`
+ HTTPProxy bool `schema:"httpproxy"`
+ Labels string `schema:"labels"`
+ MemSwap int64 `schema:"memswap"`
+ Memory int64 `schema:"memory"`
+ NetworkMode string `schema:"networkmode"`
NoCache bool `schema:"nocache"`
- CacheFrom string `schema:"cachefrom"`
+ Outputs string `schema:"outputs"`
+ Platform string `schema:"platform"`
Pull bool `schema:"pull"`
+ Quiet bool `schema:"q"`
+ Registry string `schema:"registry"`
+ Remote string `schema:"remote"`
Rm bool `schema:"rm"`
- ForceRm bool `schema:"forcerm"`
- Memory int64 `schema:"memory"`
- MemSwap int64 `schema:"memswap"`
- CpuShares uint64 `schema:"cpushares"` // nolint
- CpuSetCpus string `schema:"cpusetcpus"` // nolint
- CpuPeriod uint64 `schema:"cpuperiod"` // nolint
- CpuQuota int64 `schema:"cpuquota"` // nolint
- BuildArgs string `schema:"buildargs"`
ShmSize int `schema:"shmsize"`
Squash bool `schema:"squash"`
- Labels string `schema:"labels"`
- NetworkMode string `schema:"networkmode"`
- Platform string `schema:"platform"`
+ Tag []string `schema:"t"`
Target string `schema:"target"`
- Outputs string `schema:"outputs"`
- Registry string `schema:"registry"`
}{
Dockerfile: "Dockerfile",
- Tag: []string{},
+ Registry: "docker.io",
Rm: true,
ShmSize: 64 * 1024 * 1024,
- Registry: "docker.io",
+ Tag: []string{},
}
decoder := r.Context().Value("decoder").(*schema.Decoder)
@@ -184,6 +185,7 @@ func BuildImage(w http.ResponseWriter, r *http.Request) {
CPUQuota: query.CpuQuota,
CPUShares: query.CpuShares,
CPUSetCPUs: query.CpuSetCpus,
+ HTTPProxy: query.HTTPProxy,
Memory: query.Memory,
MemorySwap: query.MemSwap,
ShmSize: strconv.Itoa(query.ShmSize),
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/register_images.go b/pkg/api/server/register_images.go
index 32e401a54..cb0d26d1e 100644
--- a/pkg/api/server/register_images.go
+++ b/pkg/api/server/register_images.go
@@ -1423,6 +1423,13 @@ func (s *APIServer) registerImagesHandlers(r *mux.Router) error {
// description: |
// output configuration TBD
// (As of version 1.xx)
+ // - in: query
+ // name: httpproxy
+ // type: boolean
+ // default:
+ // description: |
+ // Inject http proxy environment variables into container
+ // (As of version 2.0.0)
// produces:
// - application/json
// responses:
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/bindings/images/build.go b/pkg/bindings/images/build.go
index 9082670a7..e65a04f7b 100644
--- a/pkg/bindings/images/build.go
+++ b/pkg/bindings/images/build.go
@@ -60,7 +60,7 @@ func Build(ctx context.Context, containerFiles []string, options entities.BuildO
params.Set("cpushares", strconv.Itoa(int(cpuShares)))
}
if cpuSetCpus := options.CommonBuildOpts.CPUSetCPUs; len(cpuSetCpus) > 0 {
- params.Set("cpusetcpues", cpuSetCpus)
+ params.Set("cpusetcpus", cpuSetCpus)
}
if cpuPeriod := options.CommonBuildOpts.CPUPeriod; cpuPeriod > 0 {
params.Set("cpuperiod", strconv.Itoa(int(cpuPeriod)))
@@ -92,6 +92,9 @@ func Build(ctx context.Context, containerFiles []string, options entities.BuildO
}
params.Set("labels", l)
}
+ if options.CommonBuildOpts.HTTPProxy {
+ params.Set("httpproxy", "1")
+ }
stdout := io.Writer(os.Stdout)
if options.Out != nil {
diff --git a/pkg/registries/registries.go b/pkg/registries/registries.go
index 5dff25c7d..949c5d835 100644
--- a/pkg/registries/registries.go
+++ b/pkg/registries/registries.go
@@ -1,5 +1,10 @@
package registries
+// TODO: this package should not exist anymore. Users should either use
+// c/image's `sysregistriesv2` package directly OR, even better, we cache a
+// config in libpod's image runtime so we don't need to parse the
+// registries.conf files redundantly.
+
import (
"os"
"path/filepath"
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/e2e/pull_test.go b/test/e2e/pull_test.go
index 2280d16cc..edc17fdbf 100644
--- a/test/e2e/pull_test.go
+++ b/test/e2e/pull_test.go
@@ -1,9 +1,8 @@
package integration
import (
- "os"
-
"fmt"
+ "os"
"path/filepath"
"strings"
@@ -400,4 +399,101 @@ var _ = Describe("Podman pull", func() {
session.WaitWithDefaultTimeout()
Expect(session.ExitCode()).To(Not(Equal(0)))
})
+
+ It("podman pull + inspect from unqualified-search registry", func() {
+ // Regression test for #6381:
+ // Make sure that `pull shortname` and `inspect shortname`
+ // refer to the same image.
+
+ // We already tested pulling, so we can save some energy and
+ // just restore local artifacts and tag them.
+ podmanTest.RestoreArtifact(ALPINE)
+ podmanTest.RestoreArtifact(BB)
+
+ // What we want is at least two images which have the same name
+ // and are prefixed with two different unqualified-search
+ // registries from ../registries.conf.
+ //
+ // A `podman inspect $name` must yield the one from the _first_
+ // matching registry in the registries.conf.
+ getID := func(image string) string {
+ setup := podmanTest.PodmanNoCache([]string{"image", "inspect", image})
+ setup.WaitWithDefaultTimeout()
+ Expect(setup.ExitCode()).To(Equal(0))
+
+ data := setup.InspectImageJSON() // returns []inspect.ImageData
+ Expect(len(data)).To(Equal(1))
+ return data[0].ID
+ }
+
+ untag := func(image string) {
+ setup := podmanTest.PodmanNoCache([]string{"untag", image})
+ setup.WaitWithDefaultTimeout()
+ Expect(setup.ExitCode()).To(Equal(0))
+
+ setup = podmanTest.PodmanNoCache([]string{"image", "inspect", image})
+ setup.WaitWithDefaultTimeout()
+ Expect(setup.ExitCode()).To(Equal(0))
+
+ data := setup.InspectImageJSON() // returns []inspect.ImageData
+ Expect(len(data)).To(Equal(1))
+ Expect(len(data[0].RepoTags)).To(Equal(0))
+ }
+
+ tag := func(image, tag string) {
+ setup := podmanTest.PodmanNoCache([]string{"tag", image, tag})
+ setup.WaitWithDefaultTimeout()
+ Expect(setup.ExitCode()).To(Equal(0))
+ setup = podmanTest.PodmanNoCache([]string{"image", "exists", tag})
+ setup.WaitWithDefaultTimeout()
+ Expect(setup.ExitCode()).To(Equal(0))
+ }
+
+ image1 := getID(ALPINE)
+ image2 := getID(BB)
+
+ // $ head -n2 ../registries.conf
+ // [registries.search]
+ // registries = ['docker.io', 'quay.io', 'registry.fedoraproject.org']
+ registries := []string{"docker.io", "quay.io", "registry.fedoraproject.org"}
+ name := "foo/test:tag"
+ tests := []struct {
+ // tag1 has precedence (see list above) over tag2 when
+ // doing an inspect on "test:tag".
+ tag1, tag2 string
+ }{
+ {
+ fmt.Sprintf("%s/%s", registries[0], name),
+ fmt.Sprintf("%s/%s", registries[1], name),
+ },
+ {
+ fmt.Sprintf("%s/%s", registries[0], name),
+ fmt.Sprintf("%s/%s", registries[2], name),
+ },
+ {
+ fmt.Sprintf("%s/%s", registries[1], name),
+ fmt.Sprintf("%s/%s", registries[2], name),
+ },
+ }
+
+ for _, t := range tests {
+ // 1) untag both images
+ // 2) tag them according to `t`
+ // 3) make sure that an inspect of `name` returns `image1` with `tag1`
+ untag(image1)
+ untag(image2)
+ tag(image1, t.tag1)
+ tag(image2, t.tag2)
+
+ setup := podmanTest.PodmanNoCache([]string{"image", "inspect", name})
+ setup.WaitWithDefaultTimeout()
+ Expect(setup.ExitCode()).To(Equal(0))
+
+ data := setup.InspectImageJSON() // returns []inspect.ImageData
+ Expect(len(data)).To(Equal(1))
+ Expect(len(data[0].RepoTags)).To(Equal(1))
+ Expect(data[0].RepoTags[0]).To(Equal(t.tag1))
+ Expect(data[0].ID).To(Equal(image1))
+ }
+ })
})
diff --git a/test/registries.conf b/test/registries.conf
index bb7072d45..f27a282d6 100644
--- a/test/registries.conf
+++ b/test/registries.conf
@@ -1,3 +1,4 @@
+# Note that changing the order here may break tests.
[registries.search]
registries = ['docker.io', 'quay.io', 'registry.fedoraproject.org']
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
}