diff options
27 files changed, 190 insertions, 87 deletions
@@ -5,7 +5,7 @@ Podman (the POD MANager) is a tool for managing containers and images, volumes mounted into those containers, and pods made from groups of containers. Podman is based on libpod, a library for container lifecycle management that is also contained in this repository. The libpod library provides APIs for managing containers, pods, container images, and volumes. -* [Latest Version: 2.0.3](https://github.com/containers/podman/releases/latest) +* [Latest Version: 2.0.4](https://github.com/containers/podman/releases/latest) * Latest Remote client for Windows * Latest Remote client for MacOs * Latest Static Remote client for Linux diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index 48fe68ef9..d6b0eb3dd 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -1,5 +1,22 @@ # Release Notes +## 2.0.4 +### Bugfixes +- Fixed a bug where the output of `podman image search` did not populate the Description field as it was mistakenly assigned to the ID field. +- Fixed a bug where `podman build -` and `podman build` on an HTTP target would fail. +- Fixed a bug where rootless Podman would improperly chown the copied-up contents of anonymous volumes ([#7130](https://github.com/containers/podman/issues/7130)). +- Fixed a bug where Podman would sometimes HTML-escape special characters in its CLI output. +- Fixed a bug where the `podman start --attach --interactive` command would print the container ID of the container attached to when exiting ([#7068](https://github.com/containers/podman/pull/7068)). +- Fixed a bug where `podman run --ipc=host --pid=host` would only set `--pid=host` and not `--ipc=host` ([#7100](https://github.com/containers/podman/issues/7100)). +- Fixed a bug where the `--publish` argument to `podman run`, `podman create` and `podman pod create` would not allow binding the same container port to more than one host port ([#7062](https://github.com/containers/podman/issues/7062)). +- Fixed a bug where incorrect arguments to `podman images --format` could cause Podman to segfault. +- Fixed a bug where `podman rmi --force` on an image ID with more than one name and at least one container using the image would not completely remove containers using the image ([#7153](https://github.com/containers/podman/issues/7153)). +- Fixed a bug where memory usage in bytes and memory use percentage were swapped in the output of `podman stats --format=json`. + +### API +- Fixed a bug where the libpod and compat events endpoints would fail if no filters were specified ([#7078](https://github.com/containers/podman/issues/7078)). +- Fixed a bug where the `CgroupVersion` field in responses from the compat Info endpoint was prefixed by "v" (instead of just being "1" or "2", as is documented). + ## 2.0.3 ### Features - The `podman search` command now allows wildcards in search terms. diff --git a/cmd/podman/containers/create.go b/cmd/podman/containers/create.go index dd77dc9d7..1516d15e9 100644 --- a/cmd/podman/containers/create.go +++ b/cmd/podman/containers/create.go @@ -124,7 +124,7 @@ func create(cmd *cobra.Command, args []string) error { return err } - if _, err := createPodIfNecessary(s); err != nil { + if _, err := createPodIfNecessary(s, cliVals.Net); err != nil { return err } @@ -283,7 +283,7 @@ func openCidFile(cidfile string) (*os.File, error) { // createPodIfNecessary automatically creates a pod when requested. if the pod name // has the form new:ID, the pod ID is created and the name in the spec generator is replaced // with ID. -func createPodIfNecessary(s *specgen.SpecGenerator) (*entities.PodCreateReport, error) { +func createPodIfNecessary(s *specgen.SpecGenerator, netOpts *entities.NetOptions) (*entities.PodCreateReport, error) { if !strings.HasPrefix(s.Pod, "new:") { return nil, nil } @@ -292,11 +292,10 @@ func createPodIfNecessary(s *specgen.SpecGenerator) (*entities.PodCreateReport, return nil, errors.Errorf("new pod name must be at least one character") } createOptions := entities.PodCreateOptions{ - Name: podName, - Infra: true, - Net: &entities.NetOptions{ - PublishPorts: s.PortMappings, - }, + Name: podName, + Infra: true, + Net: netOpts, + CreateCommand: os.Args, } s.Pod = podName return registry.ContainerEngine().PodCreate(context.Background(), createOptions) diff --git a/cmd/podman/containers/ps.go b/cmd/podman/containers/ps.go index 34fa4fab5..64271031d 100644 --- a/cmd/podman/containers/ps.go +++ b/cmd/podman/containers/ps.go @@ -307,6 +307,10 @@ func (l psReporter) Status() string { return l.State() } +func (l psReporter) RunningFor() string { + return l.CreatedHuman() +} + // Command returns the container command in string format func (l psReporter) Command() string { command := strings.Join(l.ListContainer.Command, " ") diff --git a/cmd/podman/containers/run.go b/cmd/podman/containers/run.go index 646c52645..d26aed826 100644 --- a/cmd/podman/containers/run.go +++ b/cmd/podman/containers/run.go @@ -176,7 +176,7 @@ func run(cmd *cobra.Command, args []string) error { } runOpts.Spec = s - if _, err := createPodIfNecessary(s); err != nil { + if _, err := createPodIfNecessary(s, cliVals.Net); err != nil { return err } diff --git a/cmd/podman/containers/stats.go b/cmd/podman/containers/stats.go index 2b4c46647..ddb5f32ef 100644 --- a/cmd/podman/containers/stats.go +++ b/cmd/podman/containers/stats.go @@ -230,8 +230,8 @@ func outputJSON(stats []*containerStats) error { Id: j.ID(), Name: j.Name, CpuPercent: j.CPUPerc(), - MemUsage: j.MemPerc(), - MemPerc: j.MemUsage(), + MemUsage: j.MemUsage(), + MemPerc: j.MemPerc(), NetIO: j.NetIO(), BlockIO: j.BlockIO(), Pids: j.PIDS(), diff --git a/cmd/podman/system/service.go b/cmd/podman/system/service.go index 2d511f0ec..7c692b07e 100644 --- a/cmd/podman/system/service.go +++ b/cmd/podman/system/service.go @@ -49,7 +49,7 @@ func init() { flags := srvCmd.Flags() flags.Int64VarP(&srvArgs.Timeout, "time", "t", 5, "Time until the service session expires in seconds. Use 0 to disable the timeout") - flags.BoolVar(&srvArgs.Varlink, "varlink", false, "Use legacy varlink service instead of REST") + flags.BoolVar(&srvArgs.Varlink, "varlink", false, "Use legacy varlink service instead of REST. Unit of --time changes from seconds to milliseconds.") _ = flags.MarkDeprecated("varlink", "valink API is deprecated.") flags.SetNormalizeFunc(aliasTimeoutFlag) @@ -88,14 +88,15 @@ func service(cmd *cobra.Command, args []string) error { opts := entities.ServiceOptions{ URI: apiURI, - Timeout: time.Duration(srvArgs.Timeout) * time.Second, Command: cmd, } if srvArgs.Varlink { + opts.Timeout = time.Duration(srvArgs.Timeout) * time.Millisecond return registry.ContainerEngine().VarlinkService(registry.GetContext(), opts) } + opts.Timeout = time.Duration(srvArgs.Timeout) * time.Second return restService(opts, cmd.Flags(), registry.PodmanConfig()) } diff --git a/docs/source/markdown/podman-generate-systemd.1.md b/docs/source/markdown/podman-generate-systemd.1.md index 466c7e2bf..d0b1b3588 100644 --- a/docs/source/markdown/podman-generate-systemd.1.md +++ b/docs/source/markdown/podman-generate-systemd.1.md @@ -149,9 +149,9 @@ WantedBy=multi-user.target default.target Podman-generated unit files include an `[Install]` section, which carries installation information for the unit. It is used by the enable and disable commands of systemctl(1) during installation. -Once you have generated the systemd unit file, you can copy the generated systemd file to ```/usr/lib/systemd/system``` for installing as a root user and to ```$HOME/.config/systemd/user ``` for installing it as a non-root user. Enable the copied unit file or files using `systemctl enable`. +Once you have generated the systemd unit file, you can copy the generated systemd file to ```/etc/systemd/system``` for installing as a root user and to ```$HOME/.config/systemd/user``` for installing it as a non-root user. Enable the copied unit file or files using `systemctl enable`. -Note: Coping unit files to ```/usr/lib/systemd/system``` and enabling it marks the unit file to be automatically started at boot. And smillarly, coping a unit file to ```$HOME/.config/systemd/user ``` and enabling it marks the unit file to be automatically started on user login. +Note: Coping unit files to ```/etc/systemd/system``` and enabling it marks the unit file to be automatically started at boot. And smillarly, coping a unit file to ```$HOME/.config/systemd/user``` and enabling it marks the unit file to be automatically started on user login. ``` @@ -162,14 +162,14 @@ $ podman generate systemd --files --name systemd-pod # Copy all the generated files. -$ sudo cp pod-systemd-pod.service container-great_payne.service /usr/lib/systemd/system +$ sudo cp pod-systemd-pod.service container-great_payne.service /etc/systemd/system $ systemctl enable pod-systemd-pod.service -Created symlink /etc/systemd/system/multi-user.target.wants/pod-systemd-pod.service → /usr/lib/systemd/system/pod-systemd-pod.service. -Created symlink /etc/systemd/system/default.target.wants/pod-systemd-pod.service → /usr/lib/systemd/system/pod-systemd-pod.service. +Created symlink /etc/systemd/system/multi-user.target.wants/pod-systemd-pod.service → /etc/systemd/system/pod-systemd-pod.service. +Created symlink /etc/systemd/system/default.target.wants/pod-systemd-pod.service → /etc/systemd/system/pod-systemd-pod.service. $ systemctl is-enabled pod-systemd-pod.service enabled ``` -To run the user services placed in `$HOME/.config/systemd/user/` on first login of that user, enable the service with --user flag. +To run the user services placed in `$HOME/.config/systemd/user` on first login of that user, enable the service with --user flag. ``` $ systemctl --user enable <.service> diff --git a/libpod/container_internal.go b/libpod/container_internal.go index a5208a0df..f3f11f945 100644 --- a/libpod/container_internal.go +++ b/libpod/container_internal.go @@ -1534,9 +1534,6 @@ func (c *Container) chownVolume(volumeName string) error { return errors.Wrapf(err, "error retrieving named volume %s for container %s", volumeName, c.ID()) } - uid := int(c.config.Spec.Process.User.UID) - gid := int(c.config.Spec.Process.User.GID) - vol.lock.Lock() defer vol.lock.Unlock() @@ -1547,22 +1544,34 @@ func (c *Container) chownVolume(volumeName string) error { if vol.state.NeedsChown { vol.state.NeedsChown = false + + uid := int(c.config.Spec.Process.User.UID) + gid := int(c.config.Spec.Process.User.GID) + + if c.config.IDMappings.UIDMap != nil { + p := idtools.IDPair{ + UID: uid, + GID: gid, + } + mappings := idtools.NewIDMappingsFromMaps(c.config.IDMappings.UIDMap, c.config.IDMappings.GIDMap) + newPair, err := mappings.ToHost(p) + if err != nil { + return errors.Wrapf(err, "error mapping user %d:%d", uid, gid) + } + uid = newPair.UID + gid = newPair.GID + } + vol.state.UIDChowned = uid vol.state.GIDChowned = gid if err := vol.save(); err != nil { return err } - err := filepath.Walk(vol.MountPoint(), func(path string, info os.FileInfo, err error) error { - if err != nil { - return err - } - if err := os.Lchown(path, uid, gid); err != nil { - return err - } - return nil - }) - if err != nil { + + mountPoint := vol.MountPoint() + + if err := os.Lchown(mountPoint, uid, gid); err != nil { return err } } diff --git a/libpod/define/errors.go b/libpod/define/errors.go index 23d10f527..4a0df3983 100644 --- a/libpod/define/errors.go +++ b/libpod/define/errors.go @@ -2,23 +2,20 @@ package define import ( "errors" - - "github.com/containers/podman/v2/libpod/image" - "github.com/containers/podman/v2/utils" ) var ( // ErrNoSuchCtr indicates the requested container does not exist - ErrNoSuchCtr = image.ErrNoSuchCtr + ErrNoSuchCtr = errors.New("no such container") // ErrNoSuchPod indicates the requested pod does not exist - ErrNoSuchPod = image.ErrNoSuchPod + ErrNoSuchPod = errors.New("no such pod") // ErrNoSuchImage indicates the requested image does not exist - ErrNoSuchImage = image.ErrNoSuchImage + ErrNoSuchImage = errors.New("no such image") // ErrNoSuchTag indicates the requested image tag does not exist - ErrNoSuchTag = image.ErrNoSuchTag + ErrNoSuchTag = errors.New("no such tag") // ErrNoSuchVolume indicates the requested volume does not exist ErrNoSuchVolume = errors.New("no such volume") @@ -76,7 +73,7 @@ var ( // ErrDetach indicates that an attach session was manually detached by // the user. - ErrDetach = utils.ErrDetach + ErrDetach = errors.New("detached from container") // ErrWillDeadlock indicates that the requested operation will cause a // deadlock. This is usually caused by upgrade issues, and is resolved diff --git a/libpod/image/errors.go b/libpod/image/errors.go index ddbf7be4b..3f58b1c6a 100644 --- a/libpod/image/errors.go +++ b/libpod/image/errors.go @@ -1,17 +1,16 @@ package image import ( - "errors" + "github.com/containers/podman/v2/libpod/define" ) -// Copied directly from libpod errors to avoid circular imports var ( // ErrNoSuchCtr indicates the requested container does not exist - ErrNoSuchCtr = errors.New("no such container") + ErrNoSuchCtr = define.ErrNoSuchCtr // ErrNoSuchPod indicates the requested pod does not exist - ErrNoSuchPod = errors.New("no such pod") + ErrNoSuchPod = define.ErrNoSuchPod // ErrNoSuchImage indicates the requested image does not exist - ErrNoSuchImage = errors.New("no such image") + ErrNoSuchImage = define.ErrNoSuchImage // ErrNoSuchTag indicates the requested image tag does not exist - ErrNoSuchTag = errors.New("no such tag") + ErrNoSuchTag = define.ErrNoSuchTag ) diff --git a/libpod/oci_conmon_exec_linux.go b/libpod/oci_conmon_exec_linux.go index f8d87759a..cfe3745fa 100644 --- a/libpod/oci_conmon_exec_linux.go +++ b/libpod/oci_conmon_exec_linux.go @@ -449,9 +449,12 @@ func (r *ConmonOCIRuntime) startExec(c *Container, sessionID string, options *Ex return nil, nil, err } + var filesToClose []*os.File if options.PreserveFDs > 0 { for fd := 3; fd < int(3+options.PreserveFDs); fd++ { - execCmd.ExtraFiles = append(execCmd.ExtraFiles, os.NewFile(uintptr(fd), fmt.Sprintf("fd-%d", fd))) + f := os.NewFile(uintptr(fd), fmt.Sprintf("fd-%d", fd)) + filesToClose = append(filesToClose, f) + execCmd.ExtraFiles = append(execCmd.ExtraFiles, f) } } @@ -483,14 +486,10 @@ func (r *ConmonOCIRuntime) startExec(c *Container, sessionID string, options *Ex return nil, nil, err } - if options.PreserveFDs > 0 { - for fd := 3; fd < int(3+options.PreserveFDs); fd++ { - // These fds were passed down to the runtime. Close them - // and not interfere - if err := os.NewFile(uintptr(fd), fmt.Sprintf("fd-%d", fd)).Close(); err != nil { - logrus.Debugf("unable to close file fd-%d", fd) - } - } + // These fds were passed down to the runtime. Close them + // and not interfere + for _, f := range filesToClose { + errorhandling.CloseQuiet(f) } return execCmd, pipes, nil diff --git a/libpod/oci_conmon_linux.go b/libpod/oci_conmon_linux.go index e677ece31..67593a68b 100644 --- a/libpod/oci_conmon_linux.go +++ b/libpod/oci_conmon_linux.go @@ -954,9 +954,12 @@ func (r *ConmonOCIRuntime) createOCIContainer(ctr *Container, restoreOptions *Co return err } + var filesToClose []*os.File if ctr.config.PreserveFDs > 0 { for fd := 3; fd < int(3+ctr.config.PreserveFDs); fd++ { - cmd.ExtraFiles = append(cmd.ExtraFiles, os.NewFile(uintptr(fd), fmt.Sprintf("fd-%d", fd))) + f := os.NewFile(uintptr(fd), fmt.Sprintf("fd-%d", fd)) + filesToClose = append(filesToClose, f) + cmd.ExtraFiles = append(cmd.ExtraFiles, f) } } @@ -1052,14 +1055,10 @@ func (r *ConmonOCIRuntime) createOCIContainer(ctr *Container, restoreOptions *Co } } - if ctr.config.PreserveFDs > 0 { - for fd := 3; fd < int(3+ctr.config.PreserveFDs); fd++ { - // These fds were passed down to the runtime. Close them - // and not interfere - if err := os.NewFile(uintptr(fd), fmt.Sprintf("fd-%d", fd)).Close(); err != nil { - logrus.Debugf("unable to close file fd-%d", fd) - } - } + // These fds were passed down to the runtime. Close them + // and not interfere + for _, f := range filesToClose { + errorhandling.CloseQuiet(f) } return nil diff --git a/pkg/api/handlers/libpod/images.go b/pkg/api/handlers/libpod/images.go index d47f27134..51013acf1 100644 --- a/pkg/api/handlers/libpod/images.go +++ b/pkg/api/handlers/libpod/images.go @@ -636,6 +636,7 @@ func SearchImages(w http.ResponseWriter, r *http.Request) { query := struct { Term string `json:"term"` Limit int `json:"limit"` + NoTrunc bool `json:"noTrunc"` Filters []string `json:"filters"` TLSVerify bool `json:"tlsVerify"` }{ @@ -648,7 +649,8 @@ func SearchImages(w http.ResponseWriter, r *http.Request) { } options := image.SearchOptions{ - Limit: query.Limit, + Limit: query.Limit, + NoTrunc: query.NoTrunc, } if _, found := r.URL.Query()["tlsVerify"]; found { options.InsecureSkipTLSVerify = types.NewOptionalBool(!query.TLSVerify) @@ -675,7 +677,7 @@ func SearchImages(w http.ResponseWriter, r *http.Request) { for i := range searchResults { reports[i].Index = searchResults[i].Index reports[i].Name = searchResults[i].Name - reports[i].Description = searchResults[i].Index + reports[i].Description = searchResults[i].Description reports[i].Stars = searchResults[i].Stars reports[i].Official = searchResults[i].Official reports[i].Automated = searchResults[i].Automated diff --git a/pkg/api/server/register_images.go b/pkg/api/server/register_images.go index 7f060d098..cb4ce4fe7 100644 --- a/pkg/api/server/register_images.go +++ b/pkg/api/server/register_images.go @@ -972,6 +972,10 @@ func (s *APIServer) registerImagesHandlers(r *mux.Router) error { // type: integer // description: maximum number of results // - in: query + // name: noTrunc + // type: boolean + // description: do not truncate any of the result strings + // - in: query // name: filters // type: string // description: | diff --git a/pkg/api/server/register_ping.go b/pkg/api/server/register_ping.go index 4a8d2c768..4e299008c 100644 --- a/pkg/api/server/register_ping.go +++ b/pkg/api/server/register_ping.go @@ -9,9 +9,8 @@ import ( func (s *APIServer) registerPingHandlers(r *mux.Router) error { - r.Handle("/_ping", s.APIHandler(compat.Ping)).Methods(http.MethodGet) - r.Handle("/_ping", s.APIHandler(compat.Ping)).Methods(http.MethodHead) - + r.Handle("/_ping", s.APIHandler(compat.Ping)).Methods(http.MethodGet, http.MethodHead) + r.Handle(VersionedPath("/_ping"), s.APIHandler(compat.Ping)).Methods(http.MethodGet, http.MethodHead) // swagger:operation GET /libpod/_ping libpod libpodPingGet // --- // summary: Ping service @@ -62,7 +61,7 @@ func (s *APIServer) registerPingHandlers(r *mux.Router) error { // determine if talking to Podman engine or another engine // 500: // $ref: "#/responses/InternalError" - r.Handle("/libpod/_ping", s.APIHandler(compat.Ping)).Methods(http.MethodGet) - r.Handle("/libpod/_ping", s.APIHandler(compat.Ping)).Methods(http.MethodHead) + r.Handle("/libpod/_ping", s.APIHandler(compat.Ping)).Methods(http.MethodGet, http.MethodHead) + r.Handle(VersionedPath("/libpod/_ping"), s.APIHandler(compat.Ping)).Methods(http.MethodGet, http.MethodHead) return nil } diff --git a/pkg/bindings/images/images.go b/pkg/bindings/images/images.go index fc8c9996e..12d1a9ce9 100644 --- a/pkg/bindings/images/images.go +++ b/pkg/bindings/images/images.go @@ -439,6 +439,7 @@ func Search(ctx context.Context, term string, opts entities.ImageSearchOptions) params := url.Values{} params.Set("term", term) params.Set("limit", strconv.Itoa(opts.Limit)) + params.Set("noTrunc", strconv.FormatBool(opts.NoTrunc)) for _, f := range opts.Filters { params.Set("filters", f) } diff --git a/pkg/rootless/rootless_linux.c b/pkg/rootless/rootless_linux.c index d3e43e44d..eaf2d4551 100644 --- a/pkg/rootless/rootless_linux.c +++ b/pkg/rootless/rootless_linux.c @@ -860,7 +860,7 @@ reexec_in_user_namespace (int ready, char *pause_pid_file_path, char *file_to_re fprintf (stderr, "cannot read from sync pipe: %s\n", strerror (errno)); _exit (EXIT_FAILURE); } - if (b != '0') + if (ret != 1 || b != '0') _exit (EXIT_FAILURE); if (syscall_setresgid (0, 0, 0) < 0) diff --git a/pkg/rootless/rootless_linux.go b/pkg/rootless/rootless_linux.go index b1f200cc2..ccc8a1d94 100644 --- a/pkg/rootless/rootless_linux.go +++ b/pkg/rootless/rootless_linux.go @@ -97,7 +97,11 @@ func GetRootlessGID() int { return os.Getegid() } -func tryMappingTool(tool string, pid int, hostID int, mappings []idtools.IDMap) error { +func tryMappingTool(uid bool, pid int, hostID int, mappings []idtools.IDMap) error { + var tool = "newuidmap" + if !uid { + tool = "newgidmap" + } path, err := exec.LookPath(tool) if err != nil { return errors.Wrapf(err, "cannot find %s", tool) @@ -110,6 +114,15 @@ func tryMappingTool(tool string, pid int, hostID int, mappings []idtools.IDMap) args := []string{path, fmt.Sprintf("%d", pid)} args = appendTriplet(args, 0, hostID, 1) for _, i := range mappings { + if hostID >= i.HostID && hostID < i.HostID+i.Size { + what := "UID" + where := "/etc/subuid" + if !uid { + what = "GID" + where = "/etc/subgid" + } + return errors.Errorf("invalid configuration: the specified mapping %d:%d in %q includes the user %s", i.HostID, i.Size, where, what) + } args = appendTriplet(args, i.ContainerID+1, i.HostID, i.Size) } cmd := exec.Cmd{ @@ -175,7 +188,7 @@ func GetConfiguredMappings() ([]idtools.IDMap, []idtools.IDMap, error) { return uids, gids, nil } -func becomeRootInUserNS(pausePid, fileToRead string, fileOutput *os.File) (bool, int, error) { +func becomeRootInUserNS(pausePid, fileToRead string, fileOutput *os.File) (_ bool, _ int, retErr error) { if os.Geteuid() == 0 || os.Getenv("_CONTAINERS_USERNS_CONFIGURED") != "" { if os.Getenv("_CONTAINERS_USERNS_CONFIGURED") == "init" { return false, 0, runInUser() @@ -205,7 +218,11 @@ func becomeRootInUserNS(pausePid, fileToRead string, fileOutput *os.File) (bool, defer errorhandling.CloseQuiet(r) defer errorhandling.CloseQuiet(w) defer func() { - if _, err := w.Write([]byte("0")); err != nil { + toWrite := []byte("0") + if retErr != nil { + toWrite = []byte("1") + } + if _, err := w.Write(toWrite); err != nil { logrus.Errorf("failed to write byte 0: %q", err) } }() @@ -223,7 +240,11 @@ func becomeRootInUserNS(pausePid, fileToRead string, fileOutput *os.File) (bool, uidsMapped := false if uids != nil { - err := tryMappingTool("newuidmap", pid, os.Geteuid(), uids) + err := tryMappingTool(true, pid, os.Geteuid(), uids) + // If some mappings were specified, do not ignore the error + if err != nil && len(uids) > 0 { + return false, -1, err + } uidsMapped = err == nil } if !uidsMapped { @@ -245,7 +266,11 @@ func becomeRootInUserNS(pausePid, fileToRead string, fileOutput *os.File) (bool, gidsMapped := false if gids != nil { - err := tryMappingTool("newgidmap", pid, os.Getegid(), gids) + err := tryMappingTool(false, pid, os.Getegid(), gids) + // If some mappings were specified, do not ignore the error + if err != nil && len(gids) > 0 { + return false, -1, err + } gidsMapped = err == nil } if !gidsMapped { diff --git a/pkg/specgen/generate/namespaces.go b/pkg/specgen/generate/namespaces.go index b8ab1399e..7adb8be6a 100644 --- a/pkg/specgen/generate/namespaces.go +++ b/pkg/specgen/generate/namespaces.go @@ -462,6 +462,10 @@ func specConfigureNamespaces(s *specgen.SpecGenerator, g *generate.Generator, rt func GetNamespaceOptions(ns []string) ([]libpod.PodCreateOption, error) { var options []libpod.PodCreateOption var erroredOptions []libpod.PodCreateOption + if ns == nil { + //set the default namespaces + ns = strings.Split(specgen.DefaultKernelNamespaces, ",") + } for _, toShare := range ns { switch toShare { case "cgroup": diff --git a/test/apiv2/01-basic.at b/test/apiv2/01-basic.at index 79dac990a..96b6aef7c 100644 --- a/test/apiv2/01-basic.at +++ b/test/apiv2/01-basic.at @@ -5,9 +5,15 @@ # NOTE: paths with a leading slash will be interpreted as-is; # paths without will have '/v1.40/' prepended. -t GET /_ping 200 OK +t GET /_ping 200 OK t HEAD /_ping 200 t GET /libpod/_ping 200 OK +t HEAD /libpod/_ping 200 + +t GET _ping 200 OK +t HEAD _ping 200 +t GET libpod/_ping 200 OK +t HEAD libpod/_ping 200 for i in /version version; do t GET $i 200 \ diff --git a/test/apiv2/rest_api/test_rest_v1_0_0.py b/test/apiv2/rest_api/test_rest_v1_0_0.py index 7c53623cb..2e574e015 100644 --- a/test/apiv2/rest_api/test_rest_v1_0_0.py +++ b/test/apiv2/rest_api/test_rest_v1_0_0.py @@ -13,9 +13,11 @@ from multiprocessing import Process import requests from dateutil.parser import parse +PODMAN_URL = "http://localhost:8080" + def _url(path): - return "http://localhost:8080/v1.0.0/libpod" + path + return PODMAN_URL + "/v1.0.0/libpod" + path def podman(): @@ -205,7 +207,21 @@ class TestApi(unittest.TestCase): search.join(timeout=10) self.assertFalse(search.is_alive(), "/images/search took too long") - def validateObjectFields(self, buffer): + def test_ping(self): + r = requests.get(PODMAN_URL + "/_ping") + self.assertEqual(r.status_code, 200, r.text) + + r = requests.head(PODMAN_URL + "/_ping") + self.assertEqual(r.status_code, 200, r.text) + + r = requests.get(_url("/_ping")) + self.assertEqual(r.status_code, 200, r.text) + + r = requests.get(_url("/_ping")) + self.assertEqual(r.status_code, 200, r.text) + + +def validateObjectFields(self, buffer): objs = json.loads(buffer) if not isinstance(objs, dict): for o in objs: @@ -214,6 +230,5 @@ class TestApi(unittest.TestCase): _ = objs["Id"] return objs - if __name__ == '__main__': unittest.main() diff --git a/test/e2e/ps_test.go b/test/e2e/ps_test.go index 281968fcd..f10ef5c99 100644 --- a/test/e2e/ps_test.go +++ b/test/e2e/ps_test.go @@ -476,5 +476,13 @@ var _ = Describe("Podman ps", func() { session.WaitWithDefaultTimeout() Expect(session.OutputToString()).To(ContainSubstring("echo very long cr...")) }) + It("podman ps --format {{RunningFor}}", func() { + _, ec, _ := podmanTest.RunLsContainer("") + Expect(ec).To(Equal(0)) + result := podmanTest.Podman([]string{"ps", "-a", "--format", "{{.RunningFor}}"}) + result.WaitWithDefaultTimeout() + Expect(result.ExitCode()).To(Equal(0)) + Expect(result.OutputToString()).To(ContainSubstring("ago")) + }) }) diff --git a/test/e2e/run_test.go b/test/e2e/run_test.go index 1f9cc3cb0..6bb12b54a 100644 --- a/test/e2e/run_test.go +++ b/test/e2e/run_test.go @@ -812,7 +812,11 @@ USER mail` }) It("podman run --pod automatically", func() { - session := podmanTest.Podman([]string{"run", "--pod", "new:foobar", ALPINE, "ls"}) + session := podmanTest.Podman([]string{"run", "-d", "--pod", "new:foobar", ALPINE, "nc", "-l", "-p", "8080"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + + session = podmanTest.Podman([]string{"run", "--pod", "foobar", ALPINE, "/bin/sh", "-c", "echo test | nc -w 1 127.0.0.1 8080"}) session.WaitWithDefaultTimeout() Expect(session.ExitCode()).To(Equal(0)) diff --git a/test/e2e/search_test.go b/test/e2e/search_test.go index 1e7dff697..104bb4ec8 100644 --- a/test/e2e/search_test.go +++ b/test/e2e/search_test.go @@ -5,6 +5,7 @@ import ( "fmt" "io/ioutil" "os" + "regexp" "strconv" "text/template" @@ -98,6 +99,15 @@ registries = ['{{.Host}}:{{.Port}}']` Expect(search.LineInOutputContains("quay.io/libpod/gate")).To(BeTrue()) }) + It("podman search image with description", func() { + search := podmanTest.Podman([]string{"search", "quay.io/libpod/whalesay"}) + search.WaitWithDefaultTimeout() + Expect(search.ExitCode()).To(Equal(0)) + output := fmt.Sprintf("%s", search.Out.Contents()) + match, _ := regexp.MatchString(`(?m)^quay.io\s+quay.io/libpod/whalesay\s+Static image used for automated testing.+$`, output) + Expect(match).To(BeTrue()) + }) + It("podman search format flag", func() { search := podmanTest.Podman([]string{"search", "--format", "table {{.Index}} {{.Name}}", "alpine"}) search.WaitWithDefaultTimeout() diff --git a/test/system/070-build.bats b/test/system/070-build.bats index 627b9caa6..a69d32a2f 100644 --- a/test/system/070-build.bats +++ b/test/system/070-build.bats @@ -143,7 +143,7 @@ RUN mkdir -p /a/b/c RUN ln -s /no/such/nonesuch /a/b/c/badsymlink RUN ln -s /bin/mydefaultcmd /a/b/c/goodsymlink RUN touch /a/b/c/myfile -RUN chown -h 1:2 /a/b/c/badsymlink /a/b/c/goodsymlink /a/b/c/myfile +RUN chown -h 1:2 /a/b/c/badsymlink /a/b/c/goodsymlink && chown -h 4:5 /a/b/c/myfile VOLUME /a/b/c # Test for environment passing and override @@ -216,18 +216,18 @@ Labels.$label_name | $label_value # be they dangling or valid, would barf with # Error: chown <mountpath>/_data/symlink: ENOENT run_podman run --rm build_test stat -c'%u:%g:%N' /a/b/c/badsymlink - is "$output" "0:0:'/a/b/c/badsymlink' -> '/no/such/nonesuch'" \ + is "$output" "1:2:'/a/b/c/badsymlink' -> '/no/such/nonesuch'" \ "bad symlink to nonexistent file is chowned and preserved" run_podman run --rm build_test stat -c'%u:%g:%N' /a/b/c/goodsymlink - is "$output" "0:0:'/a/b/c/goodsymlink' -> '/bin/mydefaultcmd'" \ + is "$output" "1:2:'/a/b/c/goodsymlink' -> '/bin/mydefaultcmd'" \ "good symlink to existing file is chowned and preserved" run_podman run --rm build_test stat -c'%u:%g' /bin/mydefaultcmd is "$output" "2:3" "target of symlink is not chowned" run_podman run --rm build_test stat -c'%u:%g:%N' /a/b/c/myfile - is "$output" "0:0:/a/b/c/myfile" "file in volume is chowned to root" + is "$output" "4:5:/a/b/c/myfile" "file in volume is chowned" # Clean up run_podman rmi -f build_test diff --git a/utils/utils.go b/utils/utils.go index 27ce1821d..a6ef663d7 100644 --- a/utils/utils.go +++ b/utils/utils.go @@ -9,6 +9,7 @@ import ( "strconv" "strings" + "github.com/containers/podman/v2/libpod/define" "github.com/containers/storage/pkg/archive" "github.com/pkg/errors" "github.com/sirupsen/logrus" @@ -51,7 +52,7 @@ func ExecCmdWithStdStreams(stdin io.Reader, stdout, stderr io.Writer, env []stri // ErrDetach is an error indicating that the user manually detached from the // container. -var ErrDetach = errors.New("detached from container") +var ErrDetach = define.ErrDetach // CopyDetachable is similar to io.Copy but support a detach key sequence to break out. func CopyDetachable(dst io.Writer, src io.Reader, keys []byte) (written int64, err error) { |