From 0e547a4cc3c8c49053338ea73c02be708257c3d9 Mon Sep 17 00:00:00 2001 From: Paul Holzinger Date: Sun, 5 Jul 2020 15:39:34 +0200 Subject: podman ps truncate the command With a long create command the output from ps is basically unreadable. This is a regression that was introduced with Podman 2.0. Signed-off-by: Paul Holzinger --- cmd/podman/containers/ps.go | 8 +++++++- test/e2e/ps_test.go | 11 +++++++++++ 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/cmd/podman/containers/ps.go b/cmd/podman/containers/ps.go index 5cd7f40ac..24266244e 100644 --- a/cmd/podman/containers/ps.go +++ b/cmd/podman/containers/ps.go @@ -313,7 +313,13 @@ func (l psReporter) Status() string { // Command returns the container command in string format func (l psReporter) Command() string { - return strings.Join(l.ListContainer.Command, " ") + command := strings.Join(l.ListContainer.Command, " ") + if !noTrunc { + if len(command) > 17 { + return command[0:17] + "..." + } + } + return command } // Size returns the rootfs and virtual sizes in human duration in diff --git a/test/e2e/ps_test.go b/test/e2e/ps_test.go index 3d583bb8c..152c85704 100644 --- a/test/e2e/ps_test.go +++ b/test/e2e/ps_test.go @@ -466,4 +466,15 @@ var _ = Describe("Podman ps", func() { Expect(ps.ExitCode()).To(Equal(0)) Expect(ps.OutputToString()).To(ContainSubstring("0.0.0.0:8080->80/tcp")) }) + + It("podman ps truncate long create commad", func() { + session := podmanTest.Podman([]string{"run", ALPINE, "echo", "very", "long", "create", "command"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + + session = podmanTest.Podman([]string{"ps", "-a"}) + session.WaitWithDefaultTimeout() + Expect(session.OutputToString()).To(ContainSubstring("echo very long cr...")) + }) + }) -- cgit v1.2.3-54-g00ecf From d59ef411949564518e3b5d0b5bbc1625ede45486 Mon Sep 17 00:00:00 2001 From: Valentin Rothberg Date: Mon, 6 Jul 2020 11:14:33 +0200 Subject: auto-update: clarify systemd-unit requirements Clarify in the help message and the man page that auto updates only work with systemd units that are similar to the ones from `generate systemd --new`. Units that merely start/stop a container do not work as they will use the same image. Fixes: #6793 Signed-off-by: Valentin Rothberg --- cmd/podman/auto-update.go | 2 ++ docs/source/markdown/podman-auto-update.1.md | 3 +++ 2 files changed, 5 insertions(+) diff --git a/cmd/podman/auto-update.go b/cmd/podman/auto-update.go index 7a67f3d18..a12eeb6cb 100644 --- a/cmd/podman/auto-update.go +++ b/cmd/podman/auto-update.go @@ -16,6 +16,8 @@ var ( autoUpdateDescription = `Auto update containers according to their auto-update policy. Auto-update policies are specified with the "io.containers.autoupdate" label. + Containers are expected to run in systemd units created with "podman-generate-systemd --new", + or similar units that create new containers in order to run the updated images. Note that this command is experimental. Please refer to the podman-auto-update(1) man page for details.` autoUpdateCommand = &cobra.Command{ Use: "auto-update [flags]", diff --git a/docs/source/markdown/podman-auto-update.1.md b/docs/source/markdown/podman-auto-update.1.md index 90e581e42..73d75be1f 100644 --- a/docs/source/markdown/podman-auto-update.1.md +++ b/docs/source/markdown/podman-auto-update.1.md @@ -23,6 +23,9 @@ Note that `podman auto-update` relies on systemd and requires a fully-qualified This enforcement is necessary to know which image to actually check and pull. If an image ID was used, Podman would not know which image to check/pull anymore. +Moreover, the systemd units are expected to be generated with `podman-generate-systemd --new`, or similar units that create new containers in order to run the updated images. +Systemd units that start and stop a container cannot run a new image. + ## OPTIONS **--authfile**=*path* -- cgit v1.2.3-54-g00ecf From 06b5a8c2f72c4f54f0d7ea9855344e294f22a73c Mon Sep 17 00:00:00 2001 From: Matthew Heon Date: Thu, 2 Jul 2020 12:59:10 -0400 Subject: Print errors from individual containers in pods The infra/abi code for pods was written in a flawed way, assuming that the map[string]error containing individual container errors was only set when the global error for the pod function was nil; that is not accurate, and we are actually *guaranteed* to set the global error when any individual container errors. Thus, we'd never actually include individual container errors, because the infra code assumed that err being set meant everything failed and no container operations were attempted. We were originally setting the cause of the error to something nonsensical ("container already exists"), so I made a new error indicating that some containers in the pod failed. We can then ignore that error when building the report on the pod operation and actually return errors from individual containers. Unfortunately, this exposed another weakness of the infra code, which was discarding the container IDs. Errors from individual containers are not guaranteed to identify which container they came from, hence the use of map[string]error in the Pod API functions. Rather than restructuring the structs we return from pkg/infra, I just wrapped the returned errors with a message including the ID of the container. Signed-off-by: Matthew Heon --- libpod/define/errors.go | 4 ++++ libpod/pod_api.go | 12 ++++++------ pkg/domain/infra/abi/pods.go | 36 ++++++++++++++++++------------------ 3 files changed, 28 insertions(+), 24 deletions(-) diff --git a/libpod/define/errors.go b/libpod/define/errors.go index d41e00648..1e9179353 100644 --- a/libpod/define/errors.go +++ b/libpod/define/errors.go @@ -70,6 +70,10 @@ var ( // ErrInternal indicates an internal library error ErrInternal = errors.New("internal libpod error") + // ErrPodPartialFail indicates that a pod operation was only partially + // successful, and some containers within the pod failed. + ErrPodPartialFail = errors.New("some containers failed") + // ErrDetach indicates that an attach session was manually detached by // the user. ErrDetach = utils.ErrDetach diff --git a/libpod/pod_api.go b/libpod/pod_api.go index 14e6647be..a02b171e1 100644 --- a/libpod/pod_api.go +++ b/libpod/pod_api.go @@ -59,7 +59,7 @@ func (p *Pod) Start(ctx context.Context) (map[string]error, error) { } if len(ctrErrors) > 0 { - return ctrErrors, errors.Wrapf(define.ErrCtrExists, "error starting some containers") + return ctrErrors, errors.Wrapf(define.ErrPodPartialFail, "error starting some containers") } defer p.newPodEvent(events.Start) return nil, nil @@ -139,7 +139,7 @@ func (p *Pod) StopWithTimeout(ctx context.Context, cleanup bool, timeout int) (m } if len(ctrErrors) > 0 { - return ctrErrors, errors.Wrapf(define.ErrCtrExists, "error stopping some containers") + return ctrErrors, errors.Wrapf(define.ErrPodPartialFail, "error stopping some containers") } defer p.newPodEvent(events.Stop) return nil, nil @@ -208,7 +208,7 @@ func (p *Pod) Pause() (map[string]error, error) { } if len(ctrErrors) > 0 { - return ctrErrors, errors.Wrapf(define.ErrCtrExists, "error pausing some containers") + return ctrErrors, errors.Wrapf(define.ErrPodPartialFail, "error pausing some containers") } defer p.newPodEvent(events.Pause) return nil, nil @@ -267,7 +267,7 @@ func (p *Pod) Unpause() (map[string]error, error) { } if len(ctrErrors) > 0 { - return ctrErrors, errors.Wrapf(define.ErrCtrExists, "error unpausing some containers") + return ctrErrors, errors.Wrapf(define.ErrPodPartialFail, "error unpausing some containers") } defer p.newPodEvent(events.Unpause) @@ -321,7 +321,7 @@ func (p *Pod) Restart(ctx context.Context) (map[string]error, error) { } if len(ctrErrors) > 0 { - return ctrErrors, errors.Wrapf(define.ErrCtrExists, "error stopping some containers") + return ctrErrors, errors.Wrapf(define.ErrPodPartialFail, "error stopping some containers") } p.newPodEvent(events.Stop) p.newPodEvent(events.Start) @@ -387,7 +387,7 @@ func (p *Pod) Kill(signal uint) (map[string]error, error) { } if len(ctrErrors) > 0 { - return ctrErrors, errors.Wrapf(define.ErrCtrExists, "error killing some containers") + return ctrErrors, errors.Wrapf(define.ErrPodPartialFail, "error killing some containers") } defer p.newPodEvent(events.Kill) return nil, nil diff --git a/pkg/domain/infra/abi/pods.go b/pkg/domain/infra/abi/pods.go index e8d55989f..d1f465362 100644 --- a/pkg/domain/infra/abi/pods.go +++ b/pkg/domain/infra/abi/pods.go @@ -67,14 +67,14 @@ func (ic *ContainerEngine) PodKill(ctx context.Context, namesOrIds []string, opt for _, p := range pods { report := entities.PodKillReport{Id: p.ID()} conErrs, err := p.Kill(uint(sig)) - if err != nil { + if err != nil && errors.Cause(err) != define.ErrPodPartialFail { report.Errs = []error{err} reports = append(reports, &report) continue } if len(conErrs) > 0 { - for _, err := range conErrs { - report.Errs = append(report.Errs, err) + for id, err := range conErrs { + report.Errs = append(report.Errs, errors.Wrapf(err, "error killing container %s", id)) } reports = append(reports, &report) continue @@ -93,13 +93,13 @@ func (ic *ContainerEngine) PodPause(ctx context.Context, namesOrIds []string, op for _, p := range pods { report := entities.PodPauseReport{Id: p.ID()} errs, err := p.Pause() - if err != nil { + if err != nil && errors.Cause(err) != define.ErrPodPartialFail { report.Errs = []error{err} continue } if len(errs) > 0 { - for _, v := range errs { - report.Errs = append(report.Errs, v) + for id, v := range errs { + report.Errs = append(report.Errs, errors.Wrapf(v, "error pausing container %s", id)) } reports = append(reports, &report) continue @@ -118,13 +118,13 @@ func (ic *ContainerEngine) PodUnpause(ctx context.Context, namesOrIds []string, for _, p := range pods { report := entities.PodUnpauseReport{Id: p.ID()} errs, err := p.Unpause() - if err != nil { + if err != nil && errors.Cause(err) != define.ErrPodPartialFail { report.Errs = []error{err} continue } if len(errs) > 0 { - for _, v := range errs { - report.Errs = append(report.Errs, v) + for id, v := range errs { + report.Errs = append(report.Errs, errors.Wrapf(v, "error unpausing container %s", id)) } reports = append(reports, &report) continue @@ -143,13 +143,13 @@ func (ic *ContainerEngine) PodStop(ctx context.Context, namesOrIds []string, opt for _, p := range pods { report := entities.PodStopReport{Id: p.ID()} errs, err := p.StopWithTimeout(ctx, false, options.Timeout) - if err != nil { + if err != nil && errors.Cause(err) != define.ErrPodPartialFail { report.Errs = []error{err} continue } if len(errs) > 0 { - for _, v := range errs { - report.Errs = append(report.Errs, v) + for id, v := range errs { + report.Errs = append(report.Errs, errors.Wrapf(v, "error stopping container %s", id)) } reports = append(reports, &report) continue @@ -168,14 +168,14 @@ func (ic *ContainerEngine) PodRestart(ctx context.Context, namesOrIds []string, for _, p := range pods { report := entities.PodRestartReport{Id: p.ID()} errs, err := p.Restart(ctx) - if err != nil { + if err != nil && errors.Cause(err) != define.ErrPodPartialFail { report.Errs = []error{err} reports = append(reports, &report) continue } if len(errs) > 0 { - for _, v := range errs { - report.Errs = append(report.Errs, v) + for id, v := range errs { + report.Errs = append(report.Errs, errors.Wrapf(v, "error restarting container %s", id)) } reports = append(reports, &report) continue @@ -195,14 +195,14 @@ func (ic *ContainerEngine) PodStart(ctx context.Context, namesOrIds []string, op for _, p := range pods { report := entities.PodStartReport{Id: p.ID()} errs, err := p.Start(ctx) - if err != nil { + if err != nil && errors.Cause(err) != define.ErrPodPartialFail { report.Errs = []error{err} reports = append(reports, &report) continue } if len(errs) > 0 { - for _, v := range errs { - report.Errs = append(report.Errs, v) + for id, v := range errs { + report.Errs = append(report.Errs, errors.Wrapf(v, "error starting container %s", id)) } reports = append(reports, &report) continue -- cgit v1.2.3-54-g00ecf From 637ff7b7e915f0f6e5d9377382a521d9148120a2 Mon Sep 17 00:00:00 2001 From: Brent Baude Date: Thu, 2 Jul 2020 10:36:24 -0500 Subject: stop podman service in e2e tests when running e2e tests, each test knows to stop its service when running remote; however, during setup and teardown remote services were not being killed when we were done with them. Signed-off-by: Brent Baude --- test/e2e/common_test.go | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/test/e2e/common_test.go b/test/e2e/common_test.go index 87c476ecc..6633f3a53 100644 --- a/test/e2e/common_test.go +++ b/test/e2e/common_test.go @@ -143,6 +143,12 @@ var _ = SynchronizedBeforeSuite(func() []byte { fmt.Println(err) os.Exit(1) } + + // If running remote, we need to stop the associated podman system service + if podman.RemoteTest { + podman.StopRemoteService() + } + return []byte(path) }, func(data []byte) { LockTmpDir = string(data) @@ -173,6 +179,10 @@ var _ = SynchronizedAfterSuite(func() {}, fmt.Printf("%q\n", err) } + // If running remote, we need to stop the associated podman system service + if podmanTest.RemoteTest { + podmanTest.StopRemoteService() + } // for localized tests, this removes the image cache dir and for remote tests // this is a no-op removeCache() -- cgit v1.2.3-54-g00ecf From 4995c511e581723ee9d441d500eda2e6d2e8a839 Mon Sep 17 00:00:00 2001 From: Matthew Heon Date: Wed, 1 Jul 2020 16:21:57 -0400 Subject: Fix `system service` panic from early hangup in events We weren't actually halting the goroutine that sent events, so it would continue sending even when the channel closed (the most notable cause being early hangup - e.g. Control-c on a curl session). Use a context to cancel the events goroutine and stop sending events. Fixes #6805 Signed-off-by: Matthew Heon --- libpod/events.go | 13 +++++++------ libpod/events/config.go | 3 ++- libpod/events/journal_linux.go | 3 ++- libpod/events/logfile.go | 17 +++++++++++++++-- libpod/events/nullout.go | 6 +++++- pkg/api/handlers/compat/events.go | 6 +++++- pkg/domain/infra/abi/containers.go | 4 ++-- pkg/domain/infra/abi/events.go | 2 +- pkg/varlinkapi/attach.go | 3 ++- pkg/varlinkapi/events.go | 3 ++- 10 files changed, 43 insertions(+), 17 deletions(-) diff --git a/libpod/events.go b/libpod/events.go index 74c910004..7560940a5 100644 --- a/libpod/events.go +++ b/libpod/events.go @@ -1,6 +1,7 @@ package libpod import ( + "context" "fmt" "github.com/containers/libpod/v2/libpod/events" @@ -75,16 +76,16 @@ func (v *Volume) newVolumeEvent(status events.Status) { // Events is a wrapper function for everyone to begin tailing the events log // with options -func (r *Runtime) Events(options events.ReadOptions) error { +func (r *Runtime) Events(ctx context.Context, options events.ReadOptions) error { eventer, err := r.newEventer() if err != nil { return err } - return eventer.Read(options) + return eventer.Read(ctx, options) } // GetEvents reads the event log and returns events based on input filters -func (r *Runtime) GetEvents(filters []string) ([]*events.Event, error) { +func (r *Runtime) GetEvents(ctx context.Context, filters []string) ([]*events.Event, error) { var readErr error eventChannel := make(chan *events.Event) options := events.ReadOptions{ @@ -98,7 +99,7 @@ func (r *Runtime) GetEvents(filters []string) ([]*events.Event, error) { return nil, err } go func() { - readErr = eventer.Read(options) + readErr = eventer.Read(ctx, options) }() if readErr != nil { return nil, readErr @@ -112,7 +113,7 @@ func (r *Runtime) GetEvents(filters []string) ([]*events.Event, error) { // GetLastContainerEvent takes a container name or ID and an event status and returns // the last occurrence of the container event -func (r *Runtime) GetLastContainerEvent(nameOrID string, containerEvent events.Status) (*events.Event, error) { +func (r *Runtime) GetLastContainerEvent(ctx context.Context, nameOrID string, containerEvent events.Status) (*events.Event, error) { // check to make sure the event.Status is valid if _, err := events.StringToStatus(containerEvent.String()); err != nil { return nil, err @@ -122,7 +123,7 @@ func (r *Runtime) GetLastContainerEvent(nameOrID string, containerEvent events.S fmt.Sprintf("event=%s", containerEvent), "type=container", } - containerEvents, err := r.GetEvents(filters) + containerEvents, err := r.GetEvents(ctx, filters) if err != nil { return nil, err } diff --git a/libpod/events/config.go b/libpod/events/config.go index 8fe551c5d..c34408e63 100644 --- a/libpod/events/config.go +++ b/libpod/events/config.go @@ -1,6 +1,7 @@ package events import ( + "context" "time" "github.com/pkg/errors" @@ -52,7 +53,7 @@ type Eventer interface { // Write an event to a backend Write(event Event) error // Read an event from the backend - Read(options ReadOptions) error + Read(ctx context.Context, options ReadOptions) error // String returns the type of event logger String() string } diff --git a/libpod/events/journal_linux.go b/libpod/events/journal_linux.go index 482435038..d341ca7b5 100644 --- a/libpod/events/journal_linux.go +++ b/libpod/events/journal_linux.go @@ -3,6 +3,7 @@ package events import ( + "context" "fmt" "strconv" "time" @@ -53,7 +54,7 @@ func (e EventJournalD) Write(ee Event) error { } // Read reads events from the journal and sends qualified events to the event channel -func (e EventJournalD) Read(options ReadOptions) error { +func (e EventJournalD) Read(ctx context.Context, options ReadOptions) error { defer close(options.EventChannel) eventOptions, err := generateEventOptions(options.Filters, options.Since, options.Until) if err != nil { diff --git a/libpod/events/logfile.go b/libpod/events/logfile.go index 93e6fa3c9..28d0dc07e 100644 --- a/libpod/events/logfile.go +++ b/libpod/events/logfile.go @@ -1,6 +1,7 @@ package events import ( + "context" "fmt" "os" @@ -40,7 +41,7 @@ func (e EventLogFile) Write(ee Event) error { } // Reads from the log file -func (e EventLogFile) Read(options ReadOptions) error { +func (e EventLogFile) Read(ctx context.Context, options ReadOptions) error { defer close(options.EventChannel) eventOptions, err := generateEventOptions(options.Filters, options.Since, options.Until) if err != nil { @@ -50,6 +51,17 @@ func (e EventLogFile) Read(options ReadOptions) error { if err != nil { return err } + funcDone := make(chan bool) + copy := true + go func() { + select { + case <-funcDone: + // Do nothing + case <-ctx.Done(): + copy = false + t.Kill(errors.New("hangup by client")) + } + }() for line := range t.Lines { event, err := newEventFromJSONString(line.Text) if err != nil { @@ -65,10 +77,11 @@ func (e EventLogFile) Read(options ReadOptions) error { for _, filter := range eventOptions { include = include && filter(event) } - if include { + if include && copy { options.EventChannel <- event } } + funcDone <- true return nil } diff --git a/libpod/events/nullout.go b/libpod/events/nullout.go index f3b36e609..3eca9e8db 100644 --- a/libpod/events/nullout.go +++ b/libpod/events/nullout.go @@ -1,5 +1,9 @@ package events +import ( + "context" +) + // EventToNull is an eventer type that only performs write operations // and only writes to /dev/null. It is meant for unittests only type EventToNull struct{} @@ -10,7 +14,7 @@ func (e EventToNull) Write(ee Event) error { } // Read does nothing. Do not use it. -func (e EventToNull) Read(options ReadOptions) error { +func (e EventToNull) Read(ctx context.Context, options ReadOptions) error { return nil } diff --git a/pkg/api/handlers/compat/events.go b/pkg/api/handlers/compat/events.go index 215d7c972..5acc94153 100644 --- a/pkg/api/handlers/compat/events.go +++ b/pkg/api/handlers/compat/events.go @@ -1,6 +1,7 @@ package compat import ( + "context" "fmt" "net/http" @@ -45,13 +46,15 @@ func GetEvents(w http.ResponseWriter, r *http.Request) { fromStart = true } + eventCtx, eventCancel := context.WithCancel(r.Context()) eventChannel := make(chan *events.Event) go func() { readOpts := events.ReadOptions{FromStart: fromStart, Stream: query.Stream, Filters: libpodFilters, EventChannel: eventChannel, Since: query.Since, Until: query.Until} - eventsError = runtime.Events(readOpts) + eventsError = runtime.Events(eventCtx, readOpts) }() if eventsError != nil { utils.InternalServerError(w, eventsError) + eventCancel() close(eventChannel) return } @@ -59,6 +62,7 @@ func GetEvents(w http.ResponseWriter, r *http.Request) { // If client disappears we need to stop listening for events go func(done <-chan struct{}) { <-done + eventCancel() if _, ok := <-eventChannel; ok { close(eventChannel) } diff --git a/pkg/domain/infra/abi/containers.go b/pkg/domain/infra/abi/containers.go index d5dce0b0f..596fc2cc1 100644 --- a/pkg/domain/infra/abi/containers.go +++ b/pkg/domain/infra/abi/containers.go @@ -741,7 +741,7 @@ func (ic *ContainerEngine) ContainerStart(ctx context.Context, namesOrIds []stri if ecode, err := ctr.Wait(); err != nil { if errors.Cause(err) == define.ErrNoSuchCtr { // Check events - event, err := ic.Libpod.GetLastContainerEvent(ctr.ID(), events.Exited) + event, err := ic.Libpod.GetLastContainerEvent(ctx, ctr.ID(), events.Exited) if err != nil { logrus.Errorf("Cannot get exit code: %v", err) exitCode = define.ExecErrorCodeNotFound @@ -871,7 +871,7 @@ func (ic *ContainerEngine) ContainerRun(ctx context.Context, opts entities.Conta if ecode, err := ctr.Wait(); err != nil { if errors.Cause(err) == define.ErrNoSuchCtr { // Check events - event, err := ic.Libpod.GetLastContainerEvent(ctr.ID(), events.Exited) + event, err := ic.Libpod.GetLastContainerEvent(ctx, ctr.ID(), events.Exited) if err != nil { logrus.Errorf("Cannot get exit code: %v", err) report.ExitCode = define.ExecErrorCodeNotFound diff --git a/pkg/domain/infra/abi/events.go b/pkg/domain/infra/abi/events.go index 50d7727ce..7a8185445 100644 --- a/pkg/domain/infra/abi/events.go +++ b/pkg/domain/infra/abi/events.go @@ -9,5 +9,5 @@ import ( func (ic *ContainerEngine) Events(ctx context.Context, opts entities.EventsOptions) error { readOpts := events.ReadOptions{FromStart: opts.FromStart, Stream: opts.Stream, Filters: opts.Filter, EventChannel: opts.EventChan, Since: opts.Since, Until: opts.Until} - return ic.Libpod.Events(readOpts) + return ic.Libpod.Events(ctx, readOpts) } diff --git a/pkg/varlinkapi/attach.go b/pkg/varlinkapi/attach.go index 731d89b8f..8acf2a1b6 100644 --- a/pkg/varlinkapi/attach.go +++ b/pkg/varlinkapi/attach.go @@ -4,6 +4,7 @@ package varlinkapi import ( "bufio" + "context" "io" "github.com/containers/libpod/v2/libpod" @@ -89,7 +90,7 @@ func (i *VarlinkAPI) Attach(call iopodman.VarlinkCall, name string, detachKeys s if ecode, err := ctr.Wait(); err != nil { if errors.Cause(err) == define.ErrNoSuchCtr { // Check events - event, err := i.Runtime.GetLastContainerEvent(ctr.ID(), events.Exited) + event, err := i.Runtime.GetLastContainerEvent(context.Background(), ctr.ID(), events.Exited) if err != nil { logrus.Errorf("Cannot get exit code: %v", err) exitCode = define.ExecErrorCodeNotFound diff --git a/pkg/varlinkapi/events.go b/pkg/varlinkapi/events.go index 2e468b706..910b64a57 100644 --- a/pkg/varlinkapi/events.go +++ b/pkg/varlinkapi/events.go @@ -3,6 +3,7 @@ package varlinkapi import ( + "context" "time" "github.com/containers/libpod/v2/libpod/events" @@ -27,7 +28,7 @@ func (i *VarlinkAPI) GetEvents(call iopodman.VarlinkCall, filter []string, since eventChannel := make(chan *events.Event) go func() { readOpts := events.ReadOptions{FromStart: fromStart, Stream: stream, Filters: filter, EventChannel: eventChannel} - eventsError = i.Runtime.Events(readOpts) + eventsError = i.Runtime.Events(context.Background(), readOpts) }() if eventsError != nil { return call.ReplyErrorOccurred(eventsError.Error()) -- cgit v1.2.3-54-g00ecf From f5b368400c1b7a1b1bdbb90f6a0d9fcbbd6802d0 Mon Sep 17 00:00:00 2001 From: Giuseppe Scrivano Date: Wed, 24 Jun 2020 14:12:53 +0200 Subject: libpod: volume copyup honors namespace mappings Signed-off-by: Giuseppe Scrivano --- libpod/container_internal.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libpod/container_internal.go b/libpod/container_internal.go index 934fe220f..e86512569 100644 --- a/libpod/container_internal.go +++ b/libpod/container_internal.go @@ -1854,8 +1854,8 @@ func (c *Container) unmount(force bool) error { // this should be from chrootarchive. // Container MUST be mounted before calling. func (c *Container) copyWithTarFromImage(source, dest string) error { - a := archive.NewDefaultArchiver() - + mappings := idtools.NewIDMappingsFromMaps(c.config.IDMappings.UIDMap, c.config.IDMappings.GIDMap) + a := archive.NewArchiver(mappings) if err := c.copyOwnerAndPerms(source, dest); err != nil { return err } -- cgit v1.2.3-54-g00ecf From eb85f429073d7dca0d2d01a1afe5a972ca9b429b Mon Sep 17 00:00:00 2001 From: Giuseppe Scrivano Date: Wed, 24 Jun 2020 14:44:28 +0200 Subject: container: move volume chown after spec generation move the chown for newly created volumes after the spec generation so the correct UID/GID are known. Closes: https://github.com/containers/libpod/issues/5698 Signed-off-by: Giuseppe Scrivano --- libpod/container_internal.go | 48 ++++++++++++++++++++++++++++++++++++++ libpod/options.go | 13 +++++++++++ libpod/runtime_ctr.go | 2 +- libpod/volume.go | 36 ++++++++++++++++++++++++---- libpod/volume_inspect.go | 11 +++++++-- pkg/api/handlers/libpod/volumes.go | 30 ++++++++++++++++++++---- pkg/domain/infra/abi/volumes.go | 26 +++++++++++++++++---- 7 files changed, 151 insertions(+), 15 deletions(-) diff --git a/libpod/container_internal.go b/libpod/container_internal.go index e86512569..7a547e565 100644 --- a/libpod/container_internal.go +++ b/libpod/container_internal.go @@ -1015,6 +1015,12 @@ func (c *Container) init(ctx context.Context, retainRetries bool) error { return err } + for _, v := range c.config.NamedVolumes { + if err := c.chownVolume(v.Name); err != nil { + return err + } + } + // With the spec complete, do an OCI create if err := c.ociRuntime.CreateContainer(c, nil); err != nil { // Fedora 31 is carrying a patch to display improved error @@ -1508,6 +1514,48 @@ func (c *Container) mountNamedVolume(v *ContainerNamedVolume, mountpoint string) return vol, nil } +// Chown the specified volume if necessary. +func (c *Container) chownVolume(volumeName string) error { + vol, err := c.runtime.state.Volume(volumeName) + if err != nil { + 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() + + // The volume may need a copy-up. Check the state. + if err := vol.update(); err != nil { + return err + } + + if vol.state.NeedsChown { + vol.state.NeedsChown = false + 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.Chown(path, uid, gid); err != nil { + return err + } + return nil + }) + if err != nil { + return err + } + } + return nil +} + // cleanupStorage unmounts and cleans up the container's root filesystem func (c *Container) cleanupStorage() error { if !c.state.Mounted { diff --git a/libpod/options.go b/libpod/options.go index 573806308..3120a35d7 100644 --- a/libpod/options.go +++ b/libpod/options.go @@ -1485,6 +1485,19 @@ func WithVolumeGID(gid int) VolumeCreateOption { } } +// WithVolumeNeedsChown sets the NeedsChown flag for the volume. +func WithVolumeNeedsChown() VolumeCreateOption { + return func(volume *Volume) error { + if volume.valid { + return define.ErrVolumeFinalized + } + + volume.state.NeedsChown = true + + return nil + } +} + // withSetAnon sets a bool notifying libpod that this volume is anonymous and // should be removed when containers using it are removed and volumes are // specified for removal. diff --git a/libpod/runtime_ctr.go b/libpod/runtime_ctr.go index 4010c9bea..c073aabb5 100644 --- a/libpod/runtime_ctr.go +++ b/libpod/runtime_ctr.go @@ -309,7 +309,7 @@ func (r *Runtime) setupContainer(ctx context.Context, ctr *Container) (_ *Contai logrus.Debugf("Creating new volume %s for container", vol.Name) // The volume does not exist, so we need to create it. - volOptions := []VolumeCreateOption{WithVolumeName(vol.Name), WithVolumeUID(ctr.RootUID()), WithVolumeGID(ctr.RootGID())} + volOptions := []VolumeCreateOption{WithVolumeName(vol.Name), WithVolumeUID(ctr.RootUID()), WithVolumeGID(ctr.RootGID()), WithVolumeNeedsChown()} if isAnonymous { volOptions = append(volOptions, withSetAnon()) } diff --git a/libpod/volume.go b/libpod/volume.go index 72b080d1a..438957086 100644 --- a/libpod/volume.go +++ b/libpod/volume.go @@ -64,6 +64,14 @@ type VolumeState struct { // create time, then cleared after the copy up is done and never set // again. NeedsCopyUp bool `json:"notYetMounted,omitempty"` + // NeedsChown indicates that the next time the volume is mounted into + // a container, the container will chown the volume to the container process + // UID/GID. + NeedsChown bool `json:"notYetChowned,omitempty"` + // UIDChowned is the UID the volume was chowned to. + UIDChowned int `json:"uidChowned,omitempty"` + // GIDChowned is the GID the volume was chowned to. + GIDChowned int `json:"gidChowned,omitempty"` } // Name retrieves the volume's name @@ -113,13 +121,33 @@ func (v *Volume) Anonymous() bool { } // UID returns the UID the volume will be created as. -func (v *Volume) UID() int { - return v.config.UID +func (v *Volume) UID() (int, error) { + v.lock.Lock() + defer v.lock.Unlock() + + if !v.valid { + return -1, define.ErrVolumeRemoved + } + + if v.state.UIDChowned > 0 { + return v.state.UIDChowned, nil + } + return v.config.UID, nil } // GID returns the GID the volume will be created as. -func (v *Volume) GID() int { - return v.config.GID +func (v *Volume) GID() (int, error) { + v.lock.Lock() + defer v.lock.Unlock() + + if !v.valid { + return -1, define.ErrVolumeRemoved + } + + if v.state.GIDChowned > 0 { + return v.state.GIDChowned, nil + } + return v.config.GID, nil } // CreatedTime returns the time the volume was created at. It was not tracked diff --git a/libpod/volume_inspect.go b/libpod/volume_inspect.go index 5258792eb..85848f84f 100644 --- a/libpod/volume_inspect.go +++ b/libpod/volume_inspect.go @@ -65,8 +65,15 @@ func (v *Volume) Inspect() (*InspectVolumeData, error) { for k, v := range v.config.Options { data.Options[k] = v } - data.UID = v.config.UID - data.GID = v.config.GID + var err error + data.UID, err = v.UID() + if err != nil { + return nil, err + } + data.GID, err = v.GID() + if err != nil { + return nil, err + } data.Anonymous = v.config.IsAnon return data, nil diff --git a/pkg/api/handlers/libpod/volumes.go b/pkg/api/handlers/libpod/volumes.go index f9a651c5d..0d83218e3 100644 --- a/pkg/api/handlers/libpod/volumes.go +++ b/pkg/api/handlers/libpod/volumes.go @@ -86,6 +86,17 @@ func InspectVolume(w http.ResponseWriter, r *http.Request) { utils.VolumeNotFound(w, name, err) return } + var uid, gid int + uid, err = vol.UID() + if err != nil { + utils.Error(w, "Error fetching volume UID", http.StatusInternalServerError, err) + return + } + gid, err = vol.GID() + if err != nil { + utils.Error(w, "Error fetching volume GID", http.StatusInternalServerError, err) + return + } volResponse := entities.VolumeConfigResponse{ Name: vol.Name(), Driver: vol.Driver(), @@ -94,8 +105,8 @@ func InspectVolume(w http.ResponseWriter, r *http.Request) { Labels: vol.Labels(), Scope: vol.Scope(), Options: vol.Options(), - UID: vol.UID(), - GID: vol.GID(), + UID: uid, + GID: gid, } utils.WriteResponse(w, http.StatusOK, volResponse) } @@ -130,6 +141,17 @@ func ListVolumes(w http.ResponseWriter, r *http.Request) { } volumeConfigs := make([]*entities.VolumeListReport, 0, len(vols)) for _, v := range vols { + var uid, gid int + uid, err = v.UID() + if err != nil { + utils.Error(w, "Error fetching volume UID", http.StatusInternalServerError, err) + return + } + gid, err = v.GID() + if err != nil { + utils.Error(w, "Error fetching volume GID", http.StatusInternalServerError, err) + return + } config := entities.VolumeConfigResponse{ Name: v.Name(), Driver: v.Driver(), @@ -138,8 +160,8 @@ func ListVolumes(w http.ResponseWriter, r *http.Request) { Labels: v.Labels(), Scope: v.Scope(), Options: v.Options(), - UID: v.UID(), - GID: v.GID(), + UID: uid, + GID: gid, } volumeConfigs = append(volumeConfigs, &entities.VolumeListReport{VolumeConfigResponse: config}) } diff --git a/pkg/domain/infra/abi/volumes.go b/pkg/domain/infra/abi/volumes.go index 861617eb6..8db89899e 100644 --- a/pkg/domain/infra/abi/volumes.go +++ b/pkg/domain/infra/abi/volumes.go @@ -95,6 +95,15 @@ func (ic *ContainerEngine) VolumeInspect(ctx context.Context, namesOrIds []strin } reports := make([]*entities.VolumeInspectReport, 0, len(vols)) for _, v := range vols { + var uid, gid int + uid, err = v.UID() + if err != nil { + return nil, err + } + gid, err = v.GID() + if err != nil { + return nil, err + } config := entities.VolumeConfigResponse{ Name: v.Name(), Driver: v.Driver(), @@ -103,8 +112,8 @@ func (ic *ContainerEngine) VolumeInspect(ctx context.Context, namesOrIds []strin Labels: v.Labels(), Scope: v.Scope(), Options: v.Options(), - UID: v.UID(), - GID: v.GID(), + UID: uid, + GID: gid, } reports = append(reports, &entities.VolumeInspectReport{VolumeConfigResponse: &config}) } @@ -141,6 +150,15 @@ func (ic *ContainerEngine) VolumeList(ctx context.Context, opts entities.VolumeL } reports := make([]*entities.VolumeListReport, 0, len(vols)) for _, v := range vols { + var uid, gid int + uid, err = v.UID() + if err != nil { + return nil, err + } + gid, err = v.GID() + if err != nil { + return nil, err + } config := entities.VolumeConfigResponse{ Name: v.Name(), Driver: v.Driver(), @@ -149,8 +167,8 @@ func (ic *ContainerEngine) VolumeList(ctx context.Context, opts entities.VolumeL Labels: v.Labels(), Scope: v.Scope(), Options: v.Options(), - UID: v.UID(), - GID: v.GID(), + UID: uid, + GID: gid, } reports = append(reports, &entities.VolumeListReport{VolumeConfigResponse: config}) } -- cgit v1.2.3-54-g00ecf From 3405e4fb6899846ba00c25e2c643c6338f432a8f Mon Sep 17 00:00:00 2001 From: Valentin Rothberg Date: Wed, 24 Jun 2020 14:57:49 +0200 Subject: test: add tests for --user and volumes Signed-off-by: Giuseppe Scrivano --- test/e2e/run_userns_test.go | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/test/e2e/run_userns_test.go b/test/e2e/run_userns_test.go index e48a427d2..c0d98f7b1 100644 --- a/test/e2e/run_userns_test.go +++ b/test/e2e/run_userns_test.go @@ -245,4 +245,31 @@ var _ = Describe("Podman UserNS support", func() { ok, _ := session.GrepString("4998") Expect(ok).To(BeTrue()) }) + + It("podman --user with volume", func() { + tests := []struct { + uid, gid, arg, vol string + }{ + {"0", "0", "0:0", "vol-0"}, + {"1000", "0", "1000", "vol-1"}, + {"1000", "1000", "1000:1000", "vol-2"}, + } + + for _, tt := range tests { + session := podmanTest.Podman([]string{"run", "-d", "--user", tt.arg, "--mount", "type=volume,src=" + tt.vol + ",dst=/home/user", "alpine", "top"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + + inspectUID := podmanTest.Podman([]string{"volume", "inspect", "--format", "{{ .UID }}", tt.vol}) + inspectUID.WaitWithDefaultTimeout() + Expect(inspectUID.ExitCode()).To(Equal(0)) + Expect(inspectUID.OutputToString()).To(Equal(tt.uid)) + + // Make sure we're defaulting to 0. + inspectGID := podmanTest.Podman([]string{"volume", "inspect", "--format", "{{ .GID }}", tt.vol}) + inspectGID.WaitWithDefaultTimeout() + Expect(inspectGID.ExitCode()).To(Equal(0)) + Expect(inspectGID.OutputToString()).To(Equal(tt.gid)) + } + }) }) -- cgit v1.2.3-54-g00ecf From 0c4a734353eb2d02d1baf16418068edfcdb505de Mon Sep 17 00:00:00 2001 From: Daniel J Walsh Date: Tue, 30 Jun 2020 08:07:42 -0400 Subject: Set TMPDIR to /var/tmp by default if not set Containers/image will use TMPDIR for the location of pulled layer blobs. If TMPDIR is not set, it will use /tmp. Since this is known to be of limited space on most systems, we change the default to /var/tmp if the user has not told the tools where to store temporary files. Signed-off-by: Daniel J Walsh --- cmd/podman/main.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/cmd/podman/main.go b/cmd/podman/main.go index 77cadc842..f3b1dcc5d 100644 --- a/cmd/podman/main.go +++ b/cmd/podman/main.go @@ -27,6 +27,11 @@ func main() { return } + // Hard code TMPDIR functions to use /var/tmp, if user did not override + if _, ok := os.LookupEnv("TMPDIR"); !ok { + os.Setenv("TMPDIR", "/var/tmp") + } + cfg := registry.PodmanConfig() for _, c := range registry.Commands { for _, m := range c.Mode { -- cgit v1.2.3-54-g00ecf From 042675b9b3376eb3960b98aa2298a278ddaecd3e Mon Sep 17 00:00:00 2001 From: maybe-sybr <58414429+maybe-sybr@users.noreply.github.com> Date: Tue, 30 Jun 2020 11:18:29 +1000 Subject: fix: Don't override entrypoint if it's `nil` This change ensures that we only override a container's entrypoint if it is set to something other than `nil`. Signed-off-by: Matt Brindley <58414429+maybe-sybr@users.noreply.github.com> --- pkg/spec/createconfig.go | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/pkg/spec/createconfig.go b/pkg/spec/createconfig.go index b1d6be016..879c66895 100644 --- a/pkg/spec/createconfig.go +++ b/pkg/spec/createconfig.go @@ -287,10 +287,11 @@ func (c *CreateConfig) getContainerCreateOptions(runtime *libpod.Runtime, pod *l options = append(options, libpod.WithCommand(c.UserCommand)) } - // Add entrypoint unconditionally - // If it's empty it's because it was explicitly set to "" or the image - // does not have one - options = append(options, libpod.WithEntrypoint(c.Entrypoint)) + // Add entrypoint if it was set + // If it's empty it's because it was explicitly set to "" + if c.Entrypoint != nil { + options = append(options, libpod.WithEntrypoint(c.Entrypoint)) + } // TODO: MNT, USER, CGROUP options = append(options, libpod.WithStopSignal(c.StopSignal)) -- cgit v1.2.3-54-g00ecf From 56c873c14e68c0cb63488ad7ded60e831c2d4194 Mon Sep 17 00:00:00 2001 From: Matthew Heon Date: Mon, 29 Jun 2020 14:26:07 -0400 Subject: Add a note on the APIs supported by `system service` This makes it clear that we target compatibility with a specific Docker version (v1.40), but do not reject other versions. It also adds a link to documentation on the Podman-specific API. Signed-off-by: Matthew Heon --- docs/source/markdown/podman-system-service.1.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/source/markdown/podman-system-service.1.md b/docs/source/markdown/podman-system-service.1.md index 3ae414f7a..7d18a6832 100644 --- a/docs/source/markdown/podman-system-service.1.md +++ b/docs/source/markdown/podman-system-service.1.md @@ -13,6 +13,10 @@ If no endpoint is provided, defaults will be used. The default endpoint for a r service is *unix:/run/podman/podman.sock* and rootless is *unix:/$XDG_RUNTIME_DIR/podman/podman.sock* (for example *unix:/run/user/1000/podman/podman.sock*) +The REST API provided by **podman system service** is split into two parts: a compatibility layer offering support for the Docker v1.40 API, and a Podman-native Libpod layer. +Documentation for the latter is available at *https://docs.podman.io/en/latest/_static/api.html*. +Both APIs are versioned, but the server will not reject requests with an unsupported version set. + ## OPTIONS **--time**, **-t** -- cgit v1.2.3-54-g00ecf From 1c02d5ab890ba7de6d0ad0603438107e086c18b7 Mon Sep 17 00:00:00 2001 From: Matthew Heon Date: Sun, 28 Jun 2020 11:06:46 -0400 Subject: Allow empty host port in --publish flag I didn't believe that this was actually legal, but it looks like it is. And, unlike our previous understanding (host port being empty means just use container port), empty host port actually carries the same meaning as `--expose` + `--publish-all` (that is, assign a random host port to the given container port). This requires a significant rework of our port handling code to handle this new case. I don't foresee this being commonly used, so I optimized having a fixed port number as fast path, which this random assignment code running after the main port handling code only if necessary. Fixes #6806 Signed-off-by: Matthew Heon --- cmd/podman/common/util.go | 26 ++++---- docs/source/markdown/podman-create.1.md | 2 + docs/source/markdown/podman-run.1.md | 3 + pkg/specgen/generate/ports.go | 112 ++++++++++++++++++++++++++------ pkg/specgen/specgen.go | 3 +- test/e2e/run_networking_test.go | 24 +++++++ 6 files changed, 136 insertions(+), 34 deletions(-) diff --git a/cmd/podman/common/util.go b/cmd/podman/common/util.go index a3ce6198c..e21e349d9 100644 --- a/cmd/podman/common/util.go +++ b/cmd/podman/common/util.go @@ -184,22 +184,24 @@ func parseSplitPort(hostIP, hostPort *string, ctrPort string, protocol *string) } if hostPort != nil { if *hostPort == "" { - return newPort, errors.Errorf("must provide a non-empty container host port to publish") - } - hostStart, hostLen, err := parseAndValidateRange(*hostPort) - if err != nil { - return newPort, errors.Wrapf(err, "error parsing host port") - } - if hostLen != ctrLen { - return newPort, errors.Errorf("host and container port ranges have different lengths: %d vs %d", hostLen, ctrLen) + // Set 0 as a placeholder. The server side of Specgen + // will find a random, open, unused port to use. + newPort.HostPort = 0 + } else { + hostStart, hostLen, err := parseAndValidateRange(*hostPort) + if err != nil { + return newPort, errors.Wrapf(err, "error parsing host port") + } + if hostLen != ctrLen { + return newPort, errors.Errorf("host and container port ranges have different lengths: %d vs %d", hostLen, ctrLen) + } + newPort.HostPort = hostStart } - newPort.HostPort = hostStart + } else { + newPort.HostPort = newPort.ContainerPort } hport := newPort.HostPort - if hport == 0 { - hport = newPort.ContainerPort - } logrus.Debugf("Adding port mapping from %d to %d length %d protocol %q", hport, newPort.ContainerPort, newPort.Range, newPort.Protocol) return newPort, nil diff --git a/docs/source/markdown/podman-create.1.md b/docs/source/markdown/podman-create.1.md index 3ec91a3ad..7c2903e33 100644 --- a/docs/source/markdown/podman-create.1.md +++ b/docs/source/markdown/podman-create.1.md @@ -623,6 +623,8 @@ When specifying ranges for both, the number of container ports in the range must (e.g., `podman run -p 1234-1236:1222-1224 --name thisWorks -t busybox` but not `podman run -p 1230-1236:1230-1240 --name RangeContainerPortsBiggerThanRangeHostPorts -t busybox`) With ip: `podman run -p 127.0.0.1:$HOSTPORT:$CONTAINERPORT --name CONTAINER -t someimage` +Host port does not have to be specified (e.g. `podman run -p 127.0.0.1::80`). +If it is not, the container port will be randomly assigned a port on the host. Use `podman port` to see the actual mapping: `podman port CONTAINER $CONTAINERPORT` **--publish-all**, **-P**=*true|false* diff --git a/docs/source/markdown/podman-run.1.md b/docs/source/markdown/podman-run.1.md index 7e91a06a3..6b6ab03f6 100644 --- a/docs/source/markdown/podman-run.1.md +++ b/docs/source/markdown/podman-run.1.md @@ -632,6 +632,9 @@ Both hostPort and containerPort can be specified as a range of ports. When specifying ranges for both, the number of container ports in the range must match the number of host ports in the range. +Host port does not have to be specified (e.g. `podman run -p 127.0.0.1::80`). +If it is not, the container port will be randomly assigned a port on the host. + Use **podman port** to see the actual mapping: **podman port $CONTAINER $CONTAINERPORT**. **--publish-all**, **-P**=**true**|**false** diff --git a/pkg/specgen/generate/ports.go b/pkg/specgen/generate/ports.go index b529fd4cd..9412ecfbf 100644 --- a/pkg/specgen/generate/ports.go +++ b/pkg/specgen/generate/ports.go @@ -43,6 +43,8 @@ func parsePortMapping(portMappings []specgen.PortMapping) ([]ocicni.PortMapping, containerPortValidate[proto] = make(map[string]map[uint16]uint16) } + postAssignHostPort := false + // Iterate through all port mappings, generating OCICNI PortMapping // structs and validating there is no overlap. for _, port := range portMappings { @@ -71,9 +73,6 @@ func parsePortMapping(portMappings []specgen.PortMapping) ([]ocicni.PortMapping, return nil, nil, nil, errors.Errorf("container port number must be non-0") } hostPort := port.HostPort - if hostPort == 0 { - hostPort = containerPort - } if uint32(len-1)+uint32(containerPort) > 65535 { return nil, nil, nil, errors.Errorf("container port range exceeds maximum allowable port number") } @@ -105,26 +104,42 @@ func parsePortMapping(portMappings []specgen.PortMapping) ([]ocicni.PortMapping, cPort := containerPort + index hPort := hostPort + index - if cPort == 0 || hPort == 0 { - return nil, nil, nil, errors.Errorf("host and container ports cannot be 0") - } - - testCPort := ctrPortMap[cPort] - if testCPort != 0 && testCPort != hPort { - // This is an attempt to redefine a port - return nil, nil, nil, errors.Errorf("conflicting port mappings for container port %d (protocol %s)", cPort, p) + if cPort == 0 { + return nil, nil, nil, errors.Errorf("container port cannot be 0") } - ctrPortMap[cPort] = hPort - testHPort := hostPortMap[hPort] - if testHPort != 0 && testHPort != cPort { - return nil, nil, nil, errors.Errorf("conflicting port mappings for host port %d (protocol %s)", hPort, p) - } - hostPortMap[hPort] = cPort - - // If we have an exact duplicate, just continue - if testCPort == hPort && testHPort == cPort { - continue + // Host port is allowed to be 0. If it is, we + // select a random port on the host. + // This will happen *after* all other ports are + // placed, to ensure we don't accidentally + // select a port that a later mapping wanted. + if hPort == 0 { + // If we already have a host port + // assigned to their container port - + // just use that. + if ctrPortMap[cPort] != 0 { + hPort = ctrPortMap[cPort] + } else { + postAssignHostPort = true + } + } else { + testCPort := ctrPortMap[cPort] + if testCPort != 0 && testCPort != hPort { + // This is an attempt to redefine a port + return nil, nil, nil, errors.Errorf("conflicting port mappings for container port %d (protocol %s)", cPort, p) + } + ctrPortMap[cPort] = hPort + + testHPort := hostPortMap[hPort] + if testHPort != 0 && testHPort != cPort { + return nil, nil, nil, errors.Errorf("conflicting port mappings for host port %d (protocol %s)", hPort, p) + } + hostPortMap[hPort] = cPort + + // If we have an exact duplicate, just continue + if testCPort == hPort && testHPort == cPort { + continue + } } // We appear to be clear. Make an OCICNI port @@ -142,6 +157,61 @@ func parsePortMapping(portMappings []specgen.PortMapping) ([]ocicni.PortMapping, } } + // Handle any 0 host ports now by setting random container ports. + if postAssignHostPort { + remadeMappings := make([]ocicni.PortMapping, 0, len(finalMappings)) + + // Iterate over all + for _, p := range finalMappings { + if p.HostPort != 0 { + remadeMappings = append(remadeMappings, p) + continue + } + + hostIPMap := hostPortValidate[p.Protocol] + ctrIPMap := containerPortValidate[p.Protocol] + + hostPortMap, ok := hostIPMap[p.HostIP] + if !ok { + hostPortMap = make(map[uint16]uint16) + hostIPMap[p.HostIP] = hostPortMap + } + ctrPortMap, ok := ctrIPMap[p.HostIP] + if !ok { + ctrPortMap = make(map[uint16]uint16) + ctrIPMap[p.HostIP] = ctrPortMap + } + + // See if container port has been used elsewhere + if ctrPortMap[uint16(p.ContainerPort)] != 0 { + // Duplicate definition. Let's not bother + // including it. + continue + } + + // Max retries to ensure we don't loop forever. + for i := 0; i < 15; i++ { + candidate, err := getRandomPort() + if err != nil { + return nil, nil, nil, errors.Wrapf(err, "error getting candidate host port for container port %d", p.ContainerPort) + } + + if hostPortMap[uint16(candidate)] == 0 { + logrus.Debugf("Successfully assigned container port %d to host port %d (IP %s Protocol %s)", p.ContainerPort, candidate, p.HostIP, p.Protocol) + hostPortMap[uint16(candidate)] = uint16(p.ContainerPort) + ctrPortMap[uint16(p.ContainerPort)] = uint16(candidate) + p.HostPort = int32(candidate) + break + } + } + if p.HostPort == 0 { + return nil, nil, nil, errors.Errorf("could not find open host port to map container port %d to", p.ContainerPort) + } + remadeMappings = append(remadeMappings, p) + } + return remadeMappings, containerPortValidate, hostPortValidate, nil + } + return finalMappings, containerPortValidate, hostPortValidate, nil } diff --git a/pkg/specgen/specgen.go b/pkg/specgen/specgen.go index 03e840ab4..327c15c5a 100644 --- a/pkg/specgen/specgen.go +++ b/pkg/specgen/specgen.go @@ -430,7 +430,8 @@ type PortMapping struct { ContainerPort uint16 `json:"container_port"` // HostPort is the port number that will be forwarded from the host into // the container. - // If omitted, will be assumed to be identical to + // If omitted, a random port on the host (guaranteed to be over 1024) + // will be assigned. HostPort uint16 `json:"host_port,omitempty"` // Range is the number of ports that will be forwarded, starting at // HostPort and ContainerPort and counting up. diff --git a/test/e2e/run_networking_test.go b/test/e2e/run_networking_test.go index dd018b910..6c049c5c1 100644 --- a/test/e2e/run_networking_test.go +++ b/test/e2e/run_networking_test.go @@ -184,6 +184,30 @@ var _ = Describe("Podman run networking", func() { Expect(inspectOut[0].NetworkSettings.Ports["80/tcp"][0].HostIP).To(Equal("")) }) + It("podman run -p 127.0.0.1::8080/udp", func() { + name := "testctr" + session := podmanTest.Podman([]string{"create", "-t", "-p", "127.0.0.1::8080/udp", "--name", name, ALPINE, "/bin/sh"}) + session.WaitWithDefaultTimeout() + inspectOut := podmanTest.InspectContainer(name) + Expect(len(inspectOut)).To(Equal(1)) + Expect(len(inspectOut[0].NetworkSettings.Ports)).To(Equal(1)) + Expect(len(inspectOut[0].NetworkSettings.Ports["8080/udp"])).To(Equal(1)) + Expect(inspectOut[0].NetworkSettings.Ports["8080/udp"][0].HostPort).To(Not(Equal("8080"))) + Expect(inspectOut[0].NetworkSettings.Ports["8080/udp"][0].HostIP).To(Equal("127.0.0.1")) + }) + + It("podman run -p :8080", func() { + name := "testctr" + session := podmanTest.Podman([]string{"create", "-t", "-p", ":8080", "--name", name, ALPINE, "/bin/sh"}) + session.WaitWithDefaultTimeout() + inspectOut := podmanTest.InspectContainer(name) + Expect(len(inspectOut)).To(Equal(1)) + Expect(len(inspectOut[0].NetworkSettings.Ports)).To(Equal(1)) + Expect(len(inspectOut[0].NetworkSettings.Ports["8080/tcp"])).To(Equal(1)) + Expect(inspectOut[0].NetworkSettings.Ports["8080/tcp"][0].HostPort).To(Not(Equal("8080"))) + Expect(inspectOut[0].NetworkSettings.Ports["8080/tcp"][0].HostIP).To(Equal("")) + }) + It("podman run network expose host port 80 to container port 8000", func() { SkipIfRootless() session := podmanTest.Podman([]string{"run", "-dt", "-p", "80:8000", ALPINE, "/bin/sh"}) -- cgit v1.2.3-54-g00ecf From de6a8609a81472b5be786c7404576905ebb981fb Mon Sep 17 00:00:00 2001 From: Brent Baude Date: Fri, 26 Jun 2020 16:22:04 -0500 Subject: Set console mode for windows Windows terminal handling is different than darwin and linux. It needs to have the terminal mode set to enable virtual terminal processing. This allows colors and other things to work. Signed-off-by: Brent Baude Signed-off-by: Matt Heon --- cmd/podman/main.go | 6 ++++++ pkg/terminal/console_unix.go | 8 ++++++++ pkg/terminal/console_windows.go | 37 +++++++++++++++++++++++++++++++++++++ 3 files changed, 51 insertions(+) create mode 100644 pkg/terminal/console_unix.go create mode 100644 pkg/terminal/console_windows.go diff --git a/cmd/podman/main.go b/cmd/podman/main.go index f3b1dcc5d..c229e9881 100644 --- a/cmd/podman/main.go +++ b/cmd/podman/main.go @@ -16,7 +16,9 @@ import ( _ "github.com/containers/libpod/v2/cmd/podman/system" _ "github.com/containers/libpod/v2/cmd/podman/volumes" "github.com/containers/libpod/v2/pkg/rootless" + "github.com/containers/libpod/pkg/terminal" "github.com/containers/storage/pkg/reexec" + "github.com/sirupsen/logrus" "github.com/spf13/cobra" ) @@ -58,6 +60,10 @@ func main() { } } } + if err := terminal.SetConsole(); err != nil { + logrus.Error(err) + os.Exit(1) + } Execute() os.Exit(0) diff --git a/pkg/terminal/console_unix.go b/pkg/terminal/console_unix.go new file mode 100644 index 000000000..6eee6aa2f --- /dev/null +++ b/pkg/terminal/console_unix.go @@ -0,0 +1,8 @@ +// +build !windows + +package terminal + +// SetConsole for non-windows environments is a no-op +func SetConsole() error { + return nil +} diff --git a/pkg/terminal/console_windows.go b/pkg/terminal/console_windows.go new file mode 100644 index 000000000..c7691857c --- /dev/null +++ b/pkg/terminal/console_windows.go @@ -0,0 +1,37 @@ +// +build windows + +package terminal + +import ( + "github.com/sirupsen/logrus" + "golang.org/x/sys/windows" +) + +// SetConsole switches the windows terminal mode to be able to handle colors, etc +func SetConsole() error { + if err := setConsoleMode(windows.Stdout, windows.ENABLE_VIRTUAL_TERMINAL_PROCESSING); err != nil { + return err + } + if err := setConsoleMode(windows.Stderr, windows.ENABLE_VIRTUAL_TERMINAL_PROCESSING); err != nil { + return err + } + if err := setConsoleMode(windows.Stdin, windows.ENABLE_VIRTUAL_TERMINAL_PROCESSING); err != nil { + return err + } + return nil +} + +func setConsoleMode(handle windows.Handle, flags uint32) error { + var mode uint32 + err := windows.GetConsoleMode(handle, &mode) + if err != nil { + return err + } + if err := windows.SetConsoleMode(handle, mode|flags); err != nil { + // In similar code, it is not considered an error if we cannot set the + // console mode. Following same line of thinking here. + logrus.WithError(err).Error("Failed to set console mode for cli") + } + + return nil +} -- cgit v1.2.3-54-g00ecf From 8dd26289bf516dcfbd9aaad6d2dd65c9f84380c8 Mon Sep 17 00:00:00 2001 From: Jhon Honce Date: Mon, 22 Jun 2020 14:29:04 -0700 Subject: Fixes --remote flag issues * --remote, --url and --identity are now anchored to podman command. Subcommands should no longer have issues * TraverseChildren now set to V1 expectations * Latest flag now has helper function. Now has consistent usage. * IsRemote() uses cobra parser to determin if --remote is given * Moved validation functions from parser pkg to validate pkg * Fixes #6598 Fixes #6704 Signed-off-by: Jhon Honce Signed-off-by: Matt Heon --- cmd/podman/containers/attach.go | 14 ++-- cmd/podman/containers/checkpoint.go | 9 +-- cmd/podman/containers/cleanup.go | 7 +- cmd/podman/containers/create.go | 1 + cmd/podman/containers/diff.go | 9 +-- cmd/podman/containers/exec.go | 15 ++--- cmd/podman/containers/init.go | 11 ++-- cmd/podman/containers/inspect.go | 3 +- cmd/podman/containers/kill.go | 21 +++--- cmd/podman/containers/list.go | 1 + cmd/podman/containers/logs.go | 14 ++-- cmd/podman/containers/mount.go | 15 ++--- cmd/podman/containers/pause.go | 1 + cmd/podman/containers/port.go | 23 +++---- cmd/podman/containers/ps.go | 6 +- cmd/podman/containers/restart.go | 18 ++--- cmd/podman/containers/restore.go | 11 ++-- cmd/podman/containers/rm.go | 17 +++-- cmd/podman/containers/run.go | 1 + cmd/podman/containers/start.go | 12 ++-- cmd/podman/containers/stats.go | 14 ++-- cmd/podman/containers/stop.go | 16 ++--- cmd/podman/containers/top.go | 13 ++-- cmd/podman/containers/unmount.go | 16 ++--- cmd/podman/containers/wait.go | 11 ++-- cmd/podman/diff.go | 16 ++--- cmd/podman/generate/generate.go | 9 ++- cmd/podman/healthcheck/healthcheck.go | 9 ++- cmd/podman/images/build.go | 10 +-- cmd/podman/images/diff.go | 4 +- cmd/podman/images/image.go | 9 ++- cmd/podman/images/search.go | 1 + cmd/podman/inspect.go | 9 ++- cmd/podman/inspect/inspect.go | 3 +- cmd/podman/main.go | 2 +- cmd/podman/manifest/manifest.go | 9 ++- cmd/podman/manifest/push.go | 1 + cmd/podman/networks/network.go | 9 ++- cmd/podman/parse/common.go | 112 -------------------------------- cmd/podman/play/kube.go | 1 - cmd/podman/play/play.go | 9 ++- cmd/podman/pods/inspect.go | 6 +- cmd/podman/pods/kill.go | 13 ++-- cmd/podman/pods/pause.go | 11 ++-- cmd/podman/pods/pod.go | 9 ++- cmd/podman/pods/ps.go | 7 +- cmd/podman/pods/restart.go | 9 +-- cmd/podman/pods/rm.go | 10 +-- cmd/podman/pods/start.go | 9 +-- cmd/podman/pods/stats.go | 7 +- cmd/podman/pods/stop.go | 9 +-- cmd/podman/pods/top.go | 9 +-- cmd/podman/pods/unpause.go | 12 ++-- cmd/podman/registry/config.go | 16 +---- cmd/podman/registry/remote.go | 19 +++++- cmd/podman/root.go | 74 ++++++++++++--------- cmd/podman/system/connection.go | 1 - cmd/podman/system/system.go | 9 ++- cmd/podman/validate/args.go | 119 +++++++++++++++++++++++++++++++++- cmd/podman/validate/latest.go | 15 +++++ cmd/podman/volumes/volume.go | 9 ++- 61 files changed, 405 insertions(+), 460 deletions(-) delete mode 100644 cmd/podman/parse/common.go create mode 100644 cmd/podman/validate/latest.go diff --git a/cmd/podman/containers/attach.go b/cmd/podman/containers/attach.go index 6835d4e32..eca9e0787 100644 --- a/cmd/podman/containers/attach.go +++ b/cmd/podman/containers/attach.go @@ -44,10 +44,6 @@ func attachFlags(flags *pflag.FlagSet) { flags.StringVar(&attachOpts.DetachKeys, "detach-keys", containerConfig.DetachKeys(), "Select the key sequence for detaching a container. Format is a single character `[a-Z]` or a comma separated sequence of `ctrl-`, where `` is one of: `a-z`, `@`, `^`, `[`, `\\`, `]`, `^` or `_`") flags.BoolVar(&attachOpts.NoStdin, "no-stdin", false, "Do not attach STDIN. The default is false") flags.BoolVar(&attachOpts.SigProxy, "sig-proxy", true, "Proxy received signals to the process") - flags.BoolVarP(&attachOpts.Latest, "latest", "l", false, "Act on the latest container podman is aware of") - if registry.IsRemote() { - _ = flags.MarkHidden("latest") - } } func init() { @@ -55,22 +51,24 @@ func init() { Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, Command: attachCommand, }) - flags := attachCommand.Flags() - attachFlags(flags) + attachFlags(attachCommand.Flags()) + validate.AddLatestFlag(attachCommand, &attachOpts.Latest) registry.Commands = append(registry.Commands, registry.CliCommand{ Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, Command: containerAttachCommand, Parent: containerCmd, }) - containerAttachFlags := containerAttachCommand.Flags() - attachFlags(containerAttachFlags) + attachFlags(containerAttachCommand.Flags()) + validate.AddLatestFlag(containerAttachCommand, &attachOpts.Latest) + } func attach(cmd *cobra.Command, args []string) error { if len(args) > 1 || (len(args) == 0 && !attachOpts.Latest) { return errors.Errorf("attach requires the name or id of one running container or the latest flag") } + var name string if len(args) > 0 { name = args[0] diff --git a/cmd/podman/containers/checkpoint.go b/cmd/podman/containers/checkpoint.go index 6aa4304af..683437000 100644 --- a/cmd/podman/containers/checkpoint.go +++ b/cmd/podman/containers/checkpoint.go @@ -4,9 +4,9 @@ import ( "context" "fmt" - "github.com/containers/libpod/v2/cmd/podman/parse" "github.com/containers/libpod/v2/cmd/podman/registry" "github.com/containers/libpod/v2/cmd/podman/utils" + "github.com/containers/libpod/v2/cmd/podman/validate" "github.com/containers/libpod/v2/pkg/domain/entities" "github.com/containers/libpod/v2/pkg/rootless" "github.com/pkg/errors" @@ -25,7 +25,7 @@ var ( Long: checkpointDescription, RunE: checkpoint, Args: func(cmd *cobra.Command, args []string) error { - return parse.CheckAllLatestAndCIDFile(cmd, args, false, false) + return validate.CheckAllLatestAndCIDFile(cmd, args, false, false) }, Example: `podman container checkpoint --keep ctrID podman container checkpoint --all @@ -48,12 +48,9 @@ func init() { flags.BoolVarP(&checkpointOptions.LeaveRunning, "leave-running", "R", false, "Leave the container running after writing checkpoint to disk") flags.BoolVar(&checkpointOptions.TCPEstablished, "tcp-established", false, "Checkpoint a container with established TCP connections") flags.BoolVarP(&checkpointOptions.All, "all", "a", false, "Checkpoint all running containers") - flags.BoolVarP(&checkpointOptions.Latest, "latest", "l", false, "Act on the latest container podman is aware of") flags.StringVarP(&checkpointOptions.Export, "export", "e", "", "Export the checkpoint image to a tar.gz") flags.BoolVar(&checkpointOptions.IgnoreRootFS, "ignore-rootfs", false, "Do not include root file-system changes when exporting") - if registry.IsRemote() { - _ = flags.MarkHidden("latest") - } + validate.AddLatestFlag(checkpointCommand, &checkpointOptions.Latest) } func checkpoint(cmd *cobra.Command, args []string) error { diff --git a/cmd/podman/containers/cleanup.go b/cmd/podman/containers/cleanup.go index f36dcf6d4..5dea77b7a 100644 --- a/cmd/podman/containers/cleanup.go +++ b/cmd/podman/containers/cleanup.go @@ -3,9 +3,9 @@ package containers import ( "fmt" - "github.com/containers/libpod/v2/cmd/podman/parse" "github.com/containers/libpod/v2/cmd/podman/registry" "github.com/containers/libpod/v2/cmd/podman/utils" + "github.com/containers/libpod/v2/cmd/podman/validate" "github.com/containers/libpod/v2/pkg/domain/entities" "github.com/pkg/errors" "github.com/sirupsen/logrus" @@ -24,7 +24,7 @@ var ( Long: cleanupDescription, RunE: cleanup, Args: func(cmd *cobra.Command, args []string) error { - return parse.CheckAllLatestAndCIDFile(cmd, args, false, false) + return validate.CheckAllLatestAndCIDFile(cmd, args, false, false) }, Example: `podman container cleanup --latest podman container cleanup ctrID1 ctrID2 ctrID3 @@ -44,11 +44,10 @@ func init() { }) flags := cleanupCommand.Flags() flags.BoolVarP(&cleanupOptions.All, "all", "a", false, "Cleans up all containers") - flags.BoolVarP(&cleanupOptions.Latest, "latest", "l", false, "Act on the latest container podman is aware of") flags.StringVar(&cleanupOptions.Exec, "exec", "", "Clean up the given exec session instead of the container") flags.BoolVar(&cleanupOptions.Remove, "rm", false, "After cleanup, remove the container entirely") flags.BoolVar(&cleanupOptions.RemoveImage, "rmi", false, "After cleanup, remove the image entirely") - + validate.AddLatestFlag(cleanupCommand, &cleanupOptions.Latest) } func cleanup(cmd *cobra.Command, args []string) error { diff --git a/cmd/podman/containers/create.go b/cmd/podman/containers/create.go index 42cee022e..cb0ee6e1f 100644 --- a/cmd/podman/containers/create.go +++ b/cmd/podman/containers/create.go @@ -58,6 +58,7 @@ func createFlags(flags *pflag.FlagSet) { flags.AddFlagSet(common.GetCreateFlags(&cliVals)) flags.AddFlagSet(common.GetNetFlags()) flags.SetNormalizeFunc(common.AliasFlags) + if registry.IsRemote() { _ = flags.MarkHidden("authfile") _ = flags.MarkHidden("env-host") diff --git a/cmd/podman/containers/diff.go b/cmd/podman/containers/diff.go index d61f92bb7..f39b22ede 100644 --- a/cmd/podman/containers/diff.go +++ b/cmd/podman/containers/diff.go @@ -14,8 +14,8 @@ var ( diffCmd = &cobra.Command{ Use: "diff [flags] CONTAINER", Args: validate.IDOrLatestArgs, - Short: "Inspect changes on container's file systems", - Long: `Displays changes on a container filesystem. The container will be compared to its parent layer.`, + Short: "Inspect changes to the container's file systems", + Long: `Displays changes to the container filesystem's'. The container will be compared to its parent layer.`, RunE: diff, Example: `podman container diff myCtr podman container diff -l --format json myCtr`, @@ -35,10 +35,7 @@ func init() { flags.BoolVar(&diffOpts.Archive, "archive", true, "Save the diff as a tar archive") _ = flags.MarkHidden("archive") flags.StringVar(&diffOpts.Format, "format", "", "Change the output format") - - if !registry.IsRemote() { - flags.BoolVarP(&diffOpts.Latest, "latest", "l", false, "Act on the latest container podman is aware of") - } + validate.AddLatestFlag(diffCmd, &diffOpts.Latest) } func diff(cmd *cobra.Command, args []string) error { diff --git a/cmd/podman/containers/exec.go b/cmd/podman/containers/exec.go index 4b2f0e9a2..e0fc740b5 100644 --- a/cmd/podman/containers/exec.go +++ b/cmd/podman/containers/exec.go @@ -6,6 +6,7 @@ import ( "os" "github.com/containers/libpod/v2/cmd/podman/registry" + "github.com/containers/libpod/v2/cmd/podman/validate" "github.com/containers/libpod/v2/libpod/define" "github.com/containers/libpod/v2/pkg/domain/entities" envLib "github.com/containers/libpod/v2/pkg/env" @@ -53,14 +54,13 @@ func execFlags(flags *pflag.FlagSet) { flags.StringArrayVarP(&envInput, "env", "e", []string{}, "Set environment variables") flags.StringSliceVar(&envFile, "env-file", []string{}, "Read in a file of environment variables") flags.BoolVarP(&execOpts.Interactive, "interactive", "i", false, "Keep STDIN open even if not attached") - flags.BoolVarP(&execOpts.Latest, "latest", "l", false, "Act on the latest container podman is aware of") flags.BoolVar(&execOpts.Privileged, "privileged", false, "Give the process extended Linux capabilities inside the container. The default is false") flags.BoolVarP(&execOpts.Tty, "tty", "t", false, "Allocate a pseudo-TTY. The default is false") flags.StringVarP(&execOpts.User, "user", "u", "", "Sets the username or UID used and optionally the groupname or GID for the specified command") flags.UintVar(&execOpts.PreserveFDs, "preserve-fds", 0, "Pass N additional file descriptors to the container") flags.StringVarP(&execOpts.WorkDir, "workdir", "w", "", "Working directory inside the container") + if registry.IsRemote() { - _ = flags.MarkHidden("latest") _ = flags.MarkHidden("preserve-fds") } } @@ -70,20 +70,19 @@ func init() { Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, Command: execCommand, }) - flags := execCommand.Flags() - execFlags(flags) + execFlags(execCommand.Flags()) + validate.AddLatestFlag(execCommand, &execOpts.Latest) registry.Commands = append(registry.Commands, registry.CliCommand{ Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, Command: containerExecCommand, Parent: containerCmd, }) - - containerExecFlags := containerExecCommand.Flags() - execFlags(containerExecFlags) + execFlags(containerExecCommand.Flags()) + validate.AddLatestFlag(containerExecCommand, &execOpts.Latest) } -func exec(cmd *cobra.Command, args []string) error { +func exec(_ *cobra.Command, args []string) error { var nameOrID string if len(args) == 0 && !execOpts.Latest { diff --git a/cmd/podman/containers/init.go b/cmd/podman/containers/init.go index c1f166d51..98f69fa4b 100644 --- a/cmd/podman/containers/init.go +++ b/cmd/podman/containers/init.go @@ -3,9 +3,9 @@ package containers import ( "fmt" - "github.com/containers/libpod/v2/cmd/podman/parse" "github.com/containers/libpod/v2/cmd/podman/registry" "github.com/containers/libpod/v2/cmd/podman/utils" + "github.com/containers/libpod/v2/cmd/podman/validate" "github.com/containers/libpod/v2/pkg/domain/entities" "github.com/spf13/cobra" "github.com/spf13/pflag" @@ -20,7 +20,7 @@ var ( Long: initDescription, RunE: initContainer, Args: func(cmd *cobra.Command, args []string) error { - return parse.CheckAllLatestAndCIDFile(cmd, args, false, false) + return validate.CheckAllLatestAndCIDFile(cmd, args, false, false) }, Example: `podman init --latest podman init 3c45ef19d893 @@ -45,10 +45,6 @@ var ( func initFlags(flags *pflag.FlagSet) { flags.BoolVarP(&initOptions.All, "all", "a", false, "Initialize all containers") - flags.BoolVarP(&initOptions.Latest, "latest", "l", false, "Act on the latest container podman is aware of") - if registry.IsRemote() { - _ = flags.MarkHidden("latest") - } } func init() { @@ -58,15 +54,16 @@ func init() { }) flags := initCommand.Flags() initFlags(flags) + validate.AddLatestFlag(initCommand, &initOptions.Latest) registry.Commands = append(registry.Commands, registry.CliCommand{ Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, Parent: containerCmd, Command: containerInitCommand, }) - containerInitFlags := containerInitCommand.Flags() initFlags(containerInitFlags) + validate.AddLatestFlag(containerInitCommand, &initOptions.Latest) } func initContainer(cmd *cobra.Command, args []string) error { diff --git a/cmd/podman/containers/inspect.go b/cmd/podman/containers/inspect.go index bea5cefd7..9ef3c2c4a 100644 --- a/cmd/podman/containers/inspect.go +++ b/cmd/podman/containers/inspect.go @@ -3,6 +3,7 @@ package containers import ( "github.com/containers/libpod/v2/cmd/podman/inspect" "github.com/containers/libpod/v2/cmd/podman/registry" + "github.com/containers/libpod/v2/cmd/podman/validate" "github.com/containers/libpod/v2/pkg/domain/entities" "github.com/spf13/cobra" ) @@ -30,7 +31,7 @@ func init() { flags := inspectCmd.Flags() flags.BoolVarP(&inspectOpts.Size, "size", "s", false, "Display total file size") flags.StringVarP(&inspectOpts.Format, "format", "f", "json", "Format the output to a Go template or json") - flags.BoolVarP(&inspectOpts.Latest, "latest", "l", false, "Act on the latest container Podman is aware of") + validate.AddLatestFlag(inspectCmd, &inspectOpts.Latest) } func inspectExec(cmd *cobra.Command, args []string) error { diff --git a/cmd/podman/containers/kill.go b/cmd/podman/containers/kill.go index 22f7383b1..da60fcf52 100644 --- a/cmd/podman/containers/kill.go +++ b/cmd/podman/containers/kill.go @@ -5,9 +5,9 @@ import ( "errors" "fmt" - "github.com/containers/libpod/v2/cmd/podman/parse" "github.com/containers/libpod/v2/cmd/podman/registry" "github.com/containers/libpod/v2/cmd/podman/utils" + "github.com/containers/libpod/v2/cmd/podman/validate" "github.com/containers/libpod/v2/pkg/domain/entities" "github.com/containers/libpod/v2/pkg/signal" "github.com/spf13/cobra" @@ -22,7 +22,7 @@ var ( Long: killDescription, RunE: kill, Args: func(cmd *cobra.Command, args []string) error { - return parse.CheckAllLatestAndCIDFile(cmd, args, false, false) + return validate.CheckAllLatestAndCIDFile(cmd, args, false, false) }, Example: `podman kill mywebserver podman kill 860a4b23 @@ -31,7 +31,7 @@ var ( containerKillCommand = &cobra.Command{ Args: func(cmd *cobra.Command, args []string) error { - return parse.CheckAllLatestAndCIDFile(cmd, args, false, false) + return validate.CheckAllLatestAndCIDFile(cmd, args, false, false) }, Use: killCommand.Use, Short: killCommand.Short, @@ -50,10 +50,6 @@ var ( func killFlags(flags *pflag.FlagSet) { flags.BoolVarP(&killOptions.All, "all", "a", false, "Signal all running containers") flags.StringVarP(&killOptions.Signal, "signal", "s", "KILL", "Signal to send to the container") - flags.BoolVarP(&killOptions.Latest, "latest", "l", false, "Act on the latest container podman is aware of") - if registry.IsRemote() { - _ = flags.MarkHidden("latest") - } } func init() { @@ -61,20 +57,19 @@ func init() { Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, Command: killCommand, }) - flags := killCommand.Flags() - killFlags(flags) + killFlags(killCommand.Flags()) + validate.AddLatestFlag(killCommand, &killOptions.Latest) registry.Commands = append(registry.Commands, registry.CliCommand{ Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, Command: containerKillCommand, Parent: containerCmd, }) - - containerKillFlags := containerKillCommand.Flags() - killFlags(containerKillFlags) + killFlags(containerKillCommand.Flags()) + validate.AddLatestFlag(containerKillCommand, &killOptions.Latest) } -func kill(cmd *cobra.Command, args []string) error { +func kill(_ *cobra.Command, args []string) error { var ( err error errs utils.OutputErrors diff --git a/cmd/podman/containers/list.go b/cmd/podman/containers/list.go index e2688623d..2d107d51d 100644 --- a/cmd/podman/containers/list.go +++ b/cmd/podman/containers/list.go @@ -29,4 +29,5 @@ func init() { Parent: containerCmd, }) listFlagSet(listCmd.Flags()) + validate.AddLatestFlag(listCmd, &listOpts.Latest) } diff --git a/cmd/podman/containers/logs.go b/cmd/podman/containers/logs.go index 85d3262da..850cb2e1f 100644 --- a/cmd/podman/containers/logs.go +++ b/cmd/podman/containers/logs.go @@ -4,6 +4,7 @@ import ( "os" "github.com/containers/libpod/v2/cmd/podman/registry" + "github.com/containers/libpod/v2/cmd/podman/validate" "github.com/containers/libpod/v2/pkg/domain/entities" "github.com/containers/libpod/v2/pkg/util" "github.com/pkg/errors" @@ -68,9 +69,8 @@ func init() { Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, Command: logsCommand, }) - - flags := logsCommand.Flags() - logsFlags(flags) + logsFlags(logsCommand.Flags()) + validate.AddLatestFlag(logsCommand, &logsOptions.Latest) // container logs registry.Commands = append(registry.Commands, registry.CliCommand{ @@ -78,15 +78,13 @@ func init() { Command: containerLogsCommand, Parent: containerCmd, }) - - containerLogsFlags := containerLogsCommand.Flags() - logsFlags(containerLogsFlags) + logsFlags(containerLogsCommand.Flags()) + validate.AddLatestFlag(containerLogsCommand, &logsOptions.Latest) } func logsFlags(flags *pflag.FlagSet) { flags.BoolVar(&logsOptions.Details, "details", false, "Show extra details provided to the logs") flags.BoolVarP(&logsOptions.Follow, "follow", "f", false, "Follow log output. The default is false") - flags.BoolVarP(&logsOptions.Latest, "latest", "l", false, "Act on the latest container podman is aware of") flags.StringVar(&logsOptions.SinceRaw, "since", "", "Show logs since TIMESTAMP") flags.Int64Var(&logsOptions.Tail, "tail", -1, "Output the specified number of LINES at the end of the logs. Defaults to -1, which prints all lines") flags.BoolVarP(&logsOptions.Timestamps, "timestamps", "t", false, "Output the timestamps in the log") @@ -95,7 +93,7 @@ func logsFlags(flags *pflag.FlagSet) { _ = flags.MarkHidden("details") } -func logs(cmd *cobra.Command, args []string) error { +func logs(_ *cobra.Command, args []string) error { if logsOptions.SinceRaw != "" { // parse time, error out if something is wrong since, err := util.ParseInputTime(logsOptions.SinceRaw) diff --git a/cmd/podman/containers/mount.go b/cmd/podman/containers/mount.go index f2b66a2cd..44af27801 100644 --- a/cmd/podman/containers/mount.go +++ b/cmd/podman/containers/mount.go @@ -6,9 +6,9 @@ import ( "text/tabwriter" "text/template" - "github.com/containers/libpod/v2/cmd/podman/parse" "github.com/containers/libpod/v2/cmd/podman/registry" "github.com/containers/libpod/v2/cmd/podman/utils" + "github.com/containers/libpod/v2/cmd/podman/validate" "github.com/containers/libpod/v2/pkg/domain/entities" "github.com/spf13/cobra" "github.com/spf13/pflag" @@ -28,7 +28,7 @@ var ( Long: mountDescription, RunE: mount, Args: func(cmd *cobra.Command, args []string) error { - return parse.CheckAllLatestAndCIDFile(cmd, args, true, false) + return validate.CheckAllLatestAndCIDFile(cmd, args, true, false) }, } @@ -47,7 +47,6 @@ var ( func mountFlags(flags *pflag.FlagSet) { flags.BoolVarP(&mountOpts.All, "all", "a", false, "Mount all containers") flags.StringVar(&mountOpts.Format, "format", "", "Change the output format to Go template") - flags.BoolVarP(&mountOpts.Latest, "latest", "l", false, "Act on the latest container podman is aware of") flags.BoolVar(&mountOpts.NoTruncate, "notruncate", false, "Do not truncate output") } @@ -56,19 +55,19 @@ func init() { Mode: []entities.EngineMode{entities.ABIMode}, Command: mountCommand, }) - flags := mountCommand.Flags() - mountFlags(flags) + mountFlags(mountCommand.Flags()) + validate.AddLatestFlag(mountCommand, &mountOpts.Latest) registry.Commands = append(registry.Commands, registry.CliCommand{ Mode: []entities.EngineMode{entities.ABIMode}, Command: containerMountCommmand, Parent: containerCmd, }) - containerMountFlags := containerMountCommmand.Flags() - mountFlags(containerMountFlags) + mountFlags(containerMountCommmand.Flags()) + validate.AddLatestFlag(containerMountCommmand, &mountOpts.Latest) } -func mount(cmd *cobra.Command, args []string) error { +func mount(_ *cobra.Command, args []string) error { var ( errs utils.OutputErrors ) diff --git a/cmd/podman/containers/pause.go b/cmd/podman/containers/pause.go index 83a3824cf..33d6ff06f 100644 --- a/cmd/podman/containers/pause.go +++ b/cmd/podman/containers/pause.go @@ -66,6 +66,7 @@ func pause(cmd *cobra.Command, args []string) error { if rootless.IsRootless() && !registry.IsRemote() { return errors.New("pause is not supported for rootless containers") } + if len(args) < 1 && !pauseOpts.All { return errors.Errorf("you must provide at least one container name or id") } diff --git a/cmd/podman/containers/port.go b/cmd/podman/containers/port.go index 3ef8f0fde..7e548b6c9 100644 --- a/cmd/podman/containers/port.go +++ b/cmd/podman/containers/port.go @@ -5,8 +5,8 @@ import ( "strconv" "strings" - "github.com/containers/libpod/v2/cmd/podman/parse" "github.com/containers/libpod/v2/cmd/podman/registry" + "github.com/containers/libpod/v2/cmd/podman/validate" "github.com/containers/libpod/v2/pkg/domain/entities" "github.com/cri-o/ocicni/pkg/ocicni" "github.com/pkg/errors" @@ -23,7 +23,7 @@ var ( Long: portDescription, RunE: port, Args: func(cmd *cobra.Command, args []string) error { - return parse.CheckAllLatestAndCIDFile(cmd, args, true, false) + return validate.CheckAllLatestAndCIDFile(cmd, args, true, false) }, Example: `podman port --all podman port ctrID 80/tcp @@ -36,7 +36,7 @@ var ( Long: portDescription, RunE: portCommand.RunE, Args: func(cmd *cobra.Command, args []string) error { - return parse.CheckAllLatestAndCIDFile(cmd, args, true, false) + return validate.CheckAllLatestAndCIDFile(cmd, args, true, false) }, Example: `podman container port --all podman container port --latest 80`, @@ -49,10 +49,6 @@ var ( func portFlags(flags *pflag.FlagSet) { flags.BoolVarP(&portOpts.All, "all", "a", false, "Display port information for all containers") - flags.BoolVarP(&portOpts.Latest, "latest", "l", false, "Act on the latest container podman is aware of") - if registry.IsRemote() { - _ = flags.MarkHidden("latest") - } } func init() { @@ -60,22 +56,19 @@ func init() { Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, Command: portCommand, }) - - flags := portCommand.Flags() - portFlags(flags) + portFlags(portCommand.Flags()) + validate.AddLatestFlag(portCommand, &portOpts.Latest) registry.Commands = append(registry.Commands, registry.CliCommand{ Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, Command: containerPortCommand, Parent: containerCmd, }) - - containerPortflags := containerPortCommand.Flags() - portFlags(containerPortflags) - + portFlags(containerPortCommand.Flags()) + validate.AddLatestFlag(containerPortCommand, &portOpts.Latest) } -func port(cmd *cobra.Command, args []string) error { +func port(_ *cobra.Command, args []string) error { var ( container string err error diff --git a/cmd/podman/containers/ps.go b/cmd/podman/containers/ps.go index 24266244e..7c84cbae1 100644 --- a/cmd/podman/containers/ps.go +++ b/cmd/podman/containers/ps.go @@ -50,6 +50,7 @@ func init() { Command: psCommand, }) listFlagSet(psCommand.Flags()) + validate.AddLatestFlag(psCommand, &listOpts.Latest) } func listFlagSet(flags *pflag.FlagSet) { @@ -57,7 +58,6 @@ func listFlagSet(flags *pflag.FlagSet) { flags.StringSliceVarP(&filters, "filter", "f", []string{}, "Filter output based on conditions given") flags.StringVar(&listOpts.Format, "format", "", "Pretty-print containers to JSON or using a Go template") flags.IntVarP(&listOpts.Last, "last", "n", -1, "Print the n last created containers (all states)") - flags.BoolVarP(&listOpts.Latest, "latest", "l", false, "Show the latest container created (all states)") flags.BoolVar(&listOpts.Namespace, "namespace", false, "Display namespace information") flags.BoolVar(&listOpts.Namespace, "ns", false, "Display namespace information") flags.BoolVar(&noTrunc, "no-trunc", false, "Display the extended information") @@ -69,10 +69,6 @@ func listFlagSet(flags *pflag.FlagSet) { sort := validate.Value(&listOpts.Sort, "command", "created", "id", "image", "names", "runningfor", "size", "status") flags.Var(sort, "sort", "Sort output by: "+sort.Choices()) - - if registry.IsRemote() { - _ = flags.MarkHidden("latest") - } } func checkFlags(c *cobra.Command) error { // latest, and last are mutually exclusive. diff --git a/cmd/podman/containers/restart.go b/cmd/podman/containers/restart.go index d3e90c160..393b003a9 100644 --- a/cmd/podman/containers/restart.go +++ b/cmd/podman/containers/restart.go @@ -4,9 +4,9 @@ import ( "context" "fmt" - "github.com/containers/libpod/v2/cmd/podman/parse" "github.com/containers/libpod/v2/cmd/podman/registry" "github.com/containers/libpod/v2/cmd/podman/utils" + "github.com/containers/libpod/v2/cmd/podman/validate" "github.com/containers/libpod/v2/libpod/define" "github.com/containers/libpod/v2/pkg/domain/entities" "github.com/pkg/errors" @@ -25,7 +25,7 @@ var ( Long: restartDescription, RunE: restart, Args: func(cmd *cobra.Command, args []string) error { - return parse.CheckAllLatestAndCIDFile(cmd, args, false, false) + return validate.CheckAllLatestAndCIDFile(cmd, args, false, false) }, Example: `podman restart ctrID podman restart --latest @@ -50,12 +50,9 @@ var ( func restartFlags(flags *pflag.FlagSet) { flags.BoolVarP(&restartOptions.All, "all", "a", false, "Restart all non-running containers") - flags.BoolVarP(&restartOptions.Latest, "latest", "l", false, "Act on the latest container podman is aware of") flags.BoolVar(&restartOptions.Running, "running", false, "Restart only running containers when --all is used") flags.UintVarP(&restartTimeout, "time", "t", containerConfig.Engine.StopTimeout, "Seconds to wait for stop before killing the container") - if registry.IsRemote() { - _ = flags.MarkHidden("latest") - } + flags.SetNormalizeFunc(utils.AliasFlags) } @@ -64,17 +61,16 @@ func init() { Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, Command: restartCommand, }) - flags := restartCommand.Flags() - restartFlags(flags) + restartFlags(restartCommand.Flags()) + validate.AddLatestFlag(restartCommand, &restartOptions.Latest) registry.Commands = append(registry.Commands, registry.CliCommand{ Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, Command: containerRestartCommand, Parent: containerCmd, }) - - containerRestartFlags := containerRestartCommand.Flags() - restartFlags(containerRestartFlags) + restartFlags(containerRestartCommand.Flags()) + validate.AddLatestFlag(containerRestartCommand, &restartOptions.Latest) } func restart(cmd *cobra.Command, args []string) error { diff --git a/cmd/podman/containers/restore.go b/cmd/podman/containers/restore.go index 975087a97..e9e0ad6fc 100644 --- a/cmd/podman/containers/restore.go +++ b/cmd/podman/containers/restore.go @@ -4,9 +4,9 @@ import ( "context" "fmt" - "github.com/containers/libpod/v2/cmd/podman/parse" "github.com/containers/libpod/v2/cmd/podman/registry" "github.com/containers/libpod/v2/cmd/podman/utils" + "github.com/containers/libpod/v2/cmd/podman/validate" "github.com/containers/libpod/v2/pkg/domain/entities" "github.com/containers/libpod/v2/pkg/rootless" "github.com/pkg/errors" @@ -25,7 +25,7 @@ var ( Long: restoreDescription, RunE: restore, Args: func(cmd *cobra.Command, args []string) error { - return parse.CheckAllLatestAndCIDFile(cmd, args, true, false) + return validate.CheckAllLatestAndCIDFile(cmd, args, true, false) }, Example: `podman container restore ctrID podman container restore --latest @@ -46,19 +46,16 @@ func init() { flags := restoreCommand.Flags() flags.BoolVarP(&restoreOptions.All, "all", "a", false, "Restore all checkpointed containers") flags.BoolVarP(&restoreOptions.Keep, "keep", "k", false, "Keep all temporary checkpoint files") - flags.BoolVarP(&restoreOptions.Latest, "latest", "l", false, "Act on the latest container podman is aware of") flags.BoolVar(&restoreOptions.TCPEstablished, "tcp-established", false, "Restore a container with established TCP connections") flags.StringVarP(&restoreOptions.Import, "import", "i", "", "Restore from exported checkpoint archive (tar.gz)") flags.StringVarP(&restoreOptions.Name, "name", "n", "", "Specify new name for container restored from exported checkpoint (only works with --import)") flags.BoolVar(&restoreOptions.IgnoreRootFS, "ignore-rootfs", false, "Do not apply root file-system changes when importing from exported checkpoint") flags.BoolVar(&restoreOptions.IgnoreStaticIP, "ignore-static-ip", false, "Ignore IP address set via --static-ip") flags.BoolVar(&restoreOptions.IgnoreStaticMAC, "ignore-static-mac", false, "Ignore MAC address set via --mac-address") - if registry.IsRemote() { - _ = flags.MarkHidden("latest") - } + validate.AddLatestFlag(restoreCommand, &restoreOptions.Latest) } -func restore(cmd *cobra.Command, args []string) error { +func restore(_ *cobra.Command, args []string) error { var errs utils.OutputErrors if rootless.IsRootless() { return errors.New("restoring a container requires root") diff --git a/cmd/podman/containers/rm.go b/cmd/podman/containers/rm.go index 3afda4c5d..427e1e72c 100644 --- a/cmd/podman/containers/rm.go +++ b/cmd/podman/containers/rm.go @@ -5,9 +5,9 @@ import ( "fmt" "strings" - "github.com/containers/libpod/v2/cmd/podman/parse" "github.com/containers/libpod/v2/cmd/podman/registry" "github.com/containers/libpod/v2/cmd/podman/utils" + "github.com/containers/libpod/v2/cmd/podman/validate" "github.com/containers/libpod/v2/libpod/define" "github.com/containers/libpod/v2/pkg/domain/entities" "github.com/pkg/errors" @@ -26,7 +26,7 @@ var ( Long: rmDescription, RunE: rm, Args: func(cmd *cobra.Command, args []string) error { - return parse.CheckAllLatestAndCIDFile(cmd, args, false, true) + return validate.CheckAllLatestAndCIDFile(cmd, args, false, true) }, Example: `podman rm imageID podman rm mywebserver myflaskserver 860a4b23 @@ -40,7 +40,7 @@ var ( Long: rmCommand.Long, RunE: rmCommand.RunE, Args: func(cmd *cobra.Command, args []string) error { - return parse.CheckAllLatestAndCIDFile(cmd, args, false, true) + return validate.CheckAllLatestAndCIDFile(cmd, args, false, true) }, Example: `podman container rm imageID podman container rm mywebserver myflaskserver 860a4b23 @@ -57,12 +57,11 @@ func rmFlags(flags *pflag.FlagSet) { flags.BoolVarP(&rmOptions.All, "all", "a", false, "Remove all containers") flags.BoolVarP(&rmOptions.Ignore, "ignore", "i", false, "Ignore errors when a specified container is missing") flags.BoolVarP(&rmOptions.Force, "force", "f", false, "Force removal of a running or unusable container. The default is false") - flags.BoolVarP(&rmOptions.Latest, "latest", "l", false, "Act on the latest container podman is aware of") flags.BoolVar(&rmOptions.Storage, "storage", false, "Remove container from storage library") flags.BoolVarP(&rmOptions.Volumes, "volumes", "v", false, "Remove anonymous volumes associated with the container") flags.StringArrayVarP(&rmOptions.CIDFiles, "cidfile", "", nil, "Read the container ID from the file") + if registry.IsRemote() { - _ = flags.MarkHidden("latest") _ = flags.MarkHidden("ignore") _ = flags.MarkHidden("cidfile") _ = flags.MarkHidden("storage") @@ -75,18 +74,18 @@ func init() { Command: rmCommand, }) rmFlags(rmCommand.Flags()) + validate.AddLatestFlag(rmCommand, &rmOptions.Latest) registry.Commands = append(registry.Commands, registry.CliCommand{ Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, Command: containerRmCommand, Parent: containerCmd, }) - - containerRmFlags := containerRmCommand.Flags() - rmFlags(containerRmFlags) + rmFlags(containerRmCommand.Flags()) + validate.AddLatestFlag(containerRmCommand, &rmOptions.Latest) } -func rm(cmd *cobra.Command, args []string) error { +func rm(_ *cobra.Command, args []string) error { return removeContainers(args, rmOptions, true) } diff --git a/cmd/podman/containers/run.go b/cmd/podman/containers/run.go index c1af2c1d3..638b1c96e 100644 --- a/cmd/podman/containers/run.go +++ b/cmd/podman/containers/run.go @@ -61,6 +61,7 @@ func runFlags(flags *pflag.FlagSet) { flags.SetNormalizeFunc(common.AliasFlags) flags.BoolVar(&runOpts.SigProxy, "sig-proxy", true, "Proxy received signals to the process") flags.BoolVar(&runRmi, "rmi", false, "Remove container image unless used by other containers") + if registry.IsRemote() { _ = flags.MarkHidden("authfile") _ = flags.MarkHidden("env-host") diff --git a/cmd/podman/containers/start.go b/cmd/podman/containers/start.go index 3c3bffaba..941588137 100644 --- a/cmd/podman/containers/start.go +++ b/cmd/podman/containers/start.go @@ -6,6 +6,7 @@ import ( "github.com/containers/libpod/v2/cmd/podman/registry" "github.com/containers/libpod/v2/cmd/podman/utils" + "github.com/containers/libpod/v2/cmd/podman/validate" "github.com/containers/libpod/v2/libpod/define" "github.com/containers/libpod/v2/pkg/domain/entities" "github.com/pkg/errors" @@ -44,10 +45,9 @@ func startFlags(flags *pflag.FlagSet) { flags.BoolVarP(&startOptions.Attach, "attach", "a", false, "Attach container's STDOUT and STDERR") flags.StringVar(&startOptions.DetachKeys, "detach-keys", containerConfig.DetachKeys(), "Select the key sequence for detaching a container. Format is a single character `[a-Z]` or a comma separated sequence of `ctrl-`, where `` is one of: `a-z`, `@`, `^`, `[`, `\\`, `]`, `^` or `_`") flags.BoolVarP(&startOptions.Interactive, "interactive", "i", false, "Keep STDIN open even if not attached") - flags.BoolVarP(&startOptions.Latest, "latest", "l", false, "Act on the latest container podman is aware of") flags.BoolVar(&startOptions.SigProxy, "sig-proxy", false, "Proxy received signals to the process (default true if attaching, false otherwise)") + if registry.IsRemote() { - _ = flags.MarkHidden("latest") _ = flags.MarkHidden("sig-proxy") } } @@ -56,17 +56,17 @@ func init() { Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, Command: startCommand, }) - flags := startCommand.Flags() - startFlags(flags) + startFlags(startCommand.Flags()) + validate.AddLatestFlag(startCommand, &startOptions.Latest) registry.Commands = append(registry.Commands, registry.CliCommand{ Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, Command: containerStartCommand, Parent: containerCmd, }) + startFlags(containerStartCommand.Flags()) + validate.AddLatestFlag(containerStartCommand, &startOptions.Latest) - containerStartFlags := containerStartCommand.Flags() - startFlags(containerStartFlags) } func start(cmd *cobra.Command, args []string) error { diff --git a/cmd/podman/containers/stats.go b/cmd/podman/containers/stats.go index 9b877b3af..86674cfc9 100644 --- a/cmd/podman/containers/stats.go +++ b/cmd/podman/containers/stats.go @@ -10,6 +10,7 @@ import ( tm "github.com/buger/goterm" "github.com/containers/libpod/v2/cmd/podman/registry" + "github.com/containers/libpod/v2/cmd/podman/validate" "github.com/containers/libpod/v2/libpod/define" "github.com/containers/libpod/v2/pkg/cgroups" "github.com/containers/libpod/v2/pkg/domain/entities" @@ -56,12 +57,8 @@ var ( func statFlags(flags *pflag.FlagSet) { flags.BoolVarP(&statsOptions.All, "all", "a", false, "Show all containers. Only running containers are shown by default. The default is false") flags.StringVar(&statsOptions.Format, "format", "", "Pretty-print container statistics to JSON or using a Go template") - flags.BoolVarP(&statsOptions.Latest, "latest", "l", false, "Act on the latest container Podman is aware of") flags.BoolVar(&statsOptions.NoReset, "no-reset", false, "Disable resetting the screen between intervals") flags.BoolVar(&statsOptions.NoStream, "no-stream", false, "Disable streaming stats and only pull the first result, default setting is false") - if registry.IsRemote() { - _ = flags.MarkHidden("latest") - } } func init() { @@ -69,17 +66,16 @@ func init() { Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, Command: statsCommand, }) - flags := statsCommand.Flags() - statFlags(flags) + statFlags(statsCommand.Flags()) + validate.AddLatestFlag(statsCommand, &statsOptions.Latest) registry.Commands = append(registry.Commands, registry.CliCommand{ Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, Command: containerStatsCommand, Parent: containerCmd, }) - - containerStatsFlags := containerStatsCommand.Flags() - statFlags(containerStatsFlags) + statFlags(containerStatsCommand.Flags()) + validate.AddLatestFlag(containerStatsCommand, &statsOptions.Latest) } // stats is different in that it will assume running containers if diff --git a/cmd/podman/containers/stop.go b/cmd/podman/containers/stop.go index 7e741d161..7959918a8 100644 --- a/cmd/podman/containers/stop.go +++ b/cmd/podman/containers/stop.go @@ -4,9 +4,9 @@ import ( "context" "fmt" - "github.com/containers/libpod/v2/cmd/podman/parse" "github.com/containers/libpod/v2/cmd/podman/registry" "github.com/containers/libpod/v2/cmd/podman/utils" + "github.com/containers/libpod/v2/cmd/podman/validate" "github.com/containers/libpod/v2/pkg/domain/entities" "github.com/spf13/cobra" "github.com/spf13/pflag" @@ -22,7 +22,7 @@ var ( Long: stopDescription, RunE: stop, Args: func(cmd *cobra.Command, args []string) error { - return parse.CheckAllLatestAndCIDFile(cmd, args, false, true) + return validate.CheckAllLatestAndCIDFile(cmd, args, false, true) }, Example: `podman stop ctrID podman stop --latest @@ -35,7 +35,7 @@ var ( Long: stopCommand.Long, RunE: stopCommand.RunE, Args: func(cmd *cobra.Command, args []string) error { - return parse.CheckAllLatestAndCIDFile(cmd, args, false, true) + return validate.CheckAllLatestAndCIDFile(cmd, args, false, true) }, Example: `podman container stop ctrID podman container stop --latest @@ -52,11 +52,9 @@ func stopFlags(flags *pflag.FlagSet) { flags.BoolVarP(&stopOptions.All, "all", "a", false, "Stop all running containers") flags.BoolVarP(&stopOptions.Ignore, "ignore", "i", false, "Ignore errors when a specified container is missing") flags.StringArrayVarP(&stopOptions.CIDFiles, "cidfile", "", nil, "Read the container ID from the file") - flags.BoolVarP(&stopOptions.Latest, "latest", "l", false, "Act on the latest container podman is aware of") flags.UintVarP(&stopTimeout, "time", "t", containerConfig.Engine.StopTimeout, "Seconds to wait for stop before killing the container") if registry.IsRemote() { - _ = flags.MarkHidden("latest") _ = flags.MarkHidden("cidfile") _ = flags.MarkHidden("ignore") } @@ -68,8 +66,8 @@ func init() { Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, Command: stopCommand, }) - flags := stopCommand.Flags() - stopFlags(flags) + stopFlags(stopCommand.Flags()) + validate.AddLatestFlag(stopCommand, &stopOptions.Latest) registry.Commands = append(registry.Commands, registry.CliCommand{ Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, @@ -77,8 +75,8 @@ func init() { Parent: containerCmd, }) - containerStopFlags := containerStopCommand.Flags() - stopFlags(containerStopFlags) + stopFlags(containerStopCommand.Flags()) + validate.AddLatestFlag(containerStopCommand, &stopOptions.Latest) } func stop(cmd *cobra.Command, args []string) error { diff --git a/cmd/podman/containers/top.go b/cmd/podman/containers/top.go index adeba5ee8..079a0ca1e 100644 --- a/cmd/podman/containers/top.go +++ b/cmd/podman/containers/top.go @@ -8,6 +8,7 @@ import ( "text/tabwriter" "github.com/containers/libpod/v2/cmd/podman/registry" + "github.com/containers/libpod/v2/cmd/podman/validate" "github.com/containers/libpod/v2/pkg/domain/entities" "github.com/containers/libpod/v2/pkg/util" "github.com/pkg/errors" @@ -51,11 +52,7 @@ podman container top ctrID -eo user,pid,comm`, func topFlags(flags *pflag.FlagSet) { flags.SetInterspersed(false) flags.BoolVar(&topOptions.ListDescriptors, "list-descriptors", false, "") - flags.BoolVarP(&topOptions.Latest, "latest", "l", false, "Act on the latest container podman is aware of") _ = flags.MarkHidden("list-descriptors") // meant only for bash completion - if registry.IsRemote() { - _ = flags.MarkHidden("latest") - } } func init() { @@ -63,8 +60,8 @@ func init() { Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, Command: topCommand, }) - flags := topCommand.Flags() - topFlags(flags) + topFlags(topCommand.Flags()) + validate.AddLatestFlag(topCommand, &topOptions.Latest) descriptors, err := util.GetContainerPidInformationDescriptors() if err == nil { @@ -77,8 +74,8 @@ func init() { Command: containerTopCommand, Parent: containerCmd, }) - containerTopFlags := containerTopCommand.Flags() - topFlags(containerTopFlags) + topFlags(containerTopCommand.Flags()) + validate.AddLatestFlag(containerTopCommand, &topOptions.Latest) } func top(cmd *cobra.Command, args []string) error { diff --git a/cmd/podman/containers/unmount.go b/cmd/podman/containers/unmount.go index 11889e8b0..c40c2be7e 100644 --- a/cmd/podman/containers/unmount.go +++ b/cmd/podman/containers/unmount.go @@ -3,9 +3,9 @@ package containers import ( "fmt" - "github.com/containers/libpod/v2/cmd/podman/parse" "github.com/containers/libpod/v2/cmd/podman/registry" "github.com/containers/libpod/v2/cmd/podman/utils" + "github.com/containers/libpod/v2/cmd/podman/validate" "github.com/containers/libpod/v2/pkg/domain/entities" "github.com/spf13/cobra" "github.com/spf13/pflag" @@ -25,7 +25,7 @@ var ( Long: description, RunE: unmount, Args: func(cmd *cobra.Command, args []string) error { - return parse.CheckAllLatestAndCIDFile(cmd, args, false, false) + return validate.CheckAllLatestAndCIDFile(cmd, args, false, false) }, Example: `podman umount ctrID podman umount ctrID1 ctrID2 ctrID3 @@ -38,7 +38,7 @@ var ( Long: umountCommand.Long, RunE: umountCommand.RunE, Args: func(cmd *cobra.Command, args []string) error { - return parse.CheckAllLatestAndCIDFile(cmd, args, false, false) + return validate.CheckAllLatestAndCIDFile(cmd, args, false, false) }, Example: `podman container umount ctrID podman container umount ctrID1 ctrID2 ctrID3 @@ -53,7 +53,6 @@ var ( func umountFlags(flags *pflag.FlagSet) { flags.BoolVarP(&unmountOpts.All, "all", "a", false, "Umount all of the currently mounted containers") flags.BoolVarP(&unmountOpts.Force, "force", "f", false, "Force the complete umount all of the currently mounted containers") - flags.BoolVarP(&unmountOpts.Latest, "latest", "l", false, "Act on the latest container podman is aware of") } func init() { @@ -61,17 +60,16 @@ func init() { Mode: []entities.EngineMode{entities.ABIMode}, Command: umountCommand, }) - flags := umountCommand.Flags() - umountFlags(flags) + umountFlags(umountCommand.Flags()) + validate.AddLatestFlag(umountCommand, &unmountOpts.Latest) registry.Commands = append(registry.Commands, registry.CliCommand{ Mode: []entities.EngineMode{entities.ABIMode}, Command: containerUnmountCommand, Parent: containerCmd, }) - - containerUmountFlags := containerUnmountCommand.Flags() - umountFlags(containerUmountFlags) + umountFlags(containerUnmountCommand.Flags()) + validate.AddLatestFlag(containerUnmountCommand, &unmountOpts.Latest) } func unmount(cmd *cobra.Command, args []string) error { diff --git a/cmd/podman/containers/wait.go b/cmd/podman/containers/wait.go index 82bfb2020..c30bfe405 100644 --- a/cmd/podman/containers/wait.go +++ b/cmd/podman/containers/wait.go @@ -47,9 +47,6 @@ var ( func waitFlags(flags *pflag.FlagSet) { flags.DurationVarP(&waitOptions.Interval, "interval", "i", time.Duration(250), "Milliseconds to wait before polling for completion") flags.StringVar(&waitCondition, "condition", "stopped", "Condition to wait on") - if !registry.IsRemote() { - flags.BoolVarP(&waitOptions.Latest, "latest", "l", false, "Act on the latest container podman is aware of") - } } func init() { @@ -57,17 +54,17 @@ func init() { Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, Command: waitCommand, }) - flags := waitCommand.Flags() - waitFlags(flags) + waitFlags(waitCommand.Flags()) + validate.AddLatestFlag(waitCommand, &waitOptions.Latest) registry.Commands = append(registry.Commands, registry.CliCommand{ Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, Command: containerWaitCommand, Parent: containerCmd, }) + waitFlags(containerWaitCommand.Flags()) + validate.AddLatestFlag(containerWaitCommand, &waitOptions.Latest) - containerWaitFlags := containerWaitCommand.Flags() - waitFlags(containerWaitFlags) } func wait(cmd *cobra.Command, args []string) error { diff --git a/cmd/podman/diff.go b/cmd/podman/diff.go index 14a4529d5..cdd908cf1 100644 --- a/cmd/podman/diff.go +++ b/cmd/podman/diff.go @@ -17,12 +17,11 @@ var ( // Command: podman _diff_ Object_ID diffDescription = `Displays changes on a container or image's filesystem. The container or image will be compared to its parent layer.` diffCmd = &cobra.Command{ - Use: "diff [flags] {CONTAINER_ID | IMAGE_ID}", - Args: validate.IDOrLatestArgs, - Short: "Display the changes of object's file system", - Long: diffDescription, - TraverseChildren: true, - RunE: diff, + Use: "diff [flags] {CONTAINER_ID | IMAGE_ID}", + Args: validate.IDOrLatestArgs, + Short: "Display the changes to the object's file system", + Long: diffDescription, + RunE: diff, Example: `podman diff imageID podman diff ctrID podman diff --format json redis:alpine`, @@ -40,10 +39,7 @@ func init() { flags.BoolVar(&diffOpts.Archive, "archive", true, "Save the diff as a tar archive") _ = flags.MarkHidden("archive") flags.StringVar(&diffOpts.Format, "format", "", "Change the output format") - - if !registry.IsRemote() { - flags.BoolVarP(&diffOpts.Latest, "latest", "l", false, "Act on the latest container podman is aware of") - } + validate.AddLatestFlag(diffCmd, &diffOpts.Latest) } func diff(cmd *cobra.Command, args []string) error { diff --git a/cmd/podman/generate/generate.go b/cmd/podman/generate/generate.go index 3b3cb238d..d85d93347 100644 --- a/cmd/podman/generate/generate.go +++ b/cmd/podman/generate/generate.go @@ -11,11 +11,10 @@ import ( var ( // Command: podman _generate_ generateCmd = &cobra.Command{ - Use: "generate", - Short: "Generate structured data based on containers and pods.", - Long: "Generate structured data (e.g., Kubernetes yaml or systemd units) based on containers and pods.", - TraverseChildren: true, - RunE: validate.SubCommandExists, + Use: "generate", + Short: "Generate structured data based on containers and pods.", + Long: "Generate structured data (e.g., Kubernetes yaml or systemd units) based on containers and pods.", + RunE: validate.SubCommandExists, } containerConfig = util.DefaultContainerConfig() ) diff --git a/cmd/podman/healthcheck/healthcheck.go b/cmd/podman/healthcheck/healthcheck.go index 8b5d7bf80..fdf40e872 100644 --- a/cmd/podman/healthcheck/healthcheck.go +++ b/cmd/podman/healthcheck/healthcheck.go @@ -10,11 +10,10 @@ import ( var ( // Command: healthcheck healthCmd = &cobra.Command{ - Use: "healthcheck", - Short: "Manage health checks on containers", - Long: "Run health checks on containers", - TraverseChildren: true, - RunE: validate.SubCommandExists, + Use: "healthcheck", + Short: "Manage health checks on containers", + Long: "Run health checks on containers", + RunE: validate.SubCommandExists, } ) diff --git a/cmd/podman/images/build.go b/cmd/podman/images/build.go index 40a9a8e7f..3ea74b4af 100644 --- a/cmd/podman/images/build.go +++ b/cmd/podman/images/build.go @@ -40,11 +40,11 @@ var ( // Command: podman _diff_ Object_ID buildDescription = "Builds an OCI or Docker image using instructions from one or more Containerfiles and a specified build context directory." buildCmd = &cobra.Command{ - Use: "build [flags] [CONTEXT]", - Short: "Build an image using instructions from Containerfiles", - Long: buildDescription, - TraverseChildren: true, - RunE: build, + Use: "build [flags] [CONTEXT]", + Short: "Build an image using instructions from Containerfiles", + Long: buildDescription, + Args: cobra.MaximumNArgs(1), + RunE: build, Example: `podman build . podman build --creds=username:password -t imageName -f Containerfile.simple . podman build --layers --force-rm --tag imageName .`, diff --git a/cmd/podman/images/diff.go b/cmd/podman/images/diff.go index cd674ee9c..10a1c06ec 100644 --- a/cmd/podman/images/diff.go +++ b/cmd/podman/images/diff.go @@ -14,8 +14,8 @@ var ( diffCmd = &cobra.Command{ Use: "diff [flags] IMAGE", Args: cobra.ExactArgs(1), - Short: "Inspect changes on image's file systems", - Long: `Displays changes on a image's filesystem. The image will be compared to its parent layer.`, + Short: "Inspect changes to the image's file systems", + Long: `Displays changes to the image's filesystem. The image will be compared to its parent layer.`, RunE: diff, Example: `podman image diff myImage podman image diff --format json redis:alpine`, diff --git a/cmd/podman/images/image.go b/cmd/podman/images/image.go index 85c1afc0e..89badd035 100644 --- a/cmd/podman/images/image.go +++ b/cmd/podman/images/image.go @@ -13,11 +13,10 @@ var ( // Command: podman _image_ imageCmd = &cobra.Command{ - Use: "image", - Short: "Manage images", - Long: "Manage images", - TraverseChildren: true, - RunE: validate.SubCommandExists, + Use: "image", + Short: "Manage images", + Long: "Manage images", + RunE: validate.SubCommandExists, } ) diff --git a/cmd/podman/images/search.go b/cmd/podman/images/search.go index 7c5bd890d..f1a2cda96 100644 --- a/cmd/podman/images/search.go +++ b/cmd/podman/images/search.go @@ -86,6 +86,7 @@ func searchFlags(flags *pflag.FlagSet) { flags.BoolVar(&searchOptions.NoTrunc, "no-trunc", false, "Do not truncate the output") flags.StringVar(&searchOptions.Authfile, "authfile", auth.GetDefaultAuthFile(), "Path of the authentication file. Use REGISTRY_AUTH_FILE environment variable to override") flags.BoolVar(&searchOptions.TLSVerifyCLI, "tls-verify", true, "Require HTTPS and verify certificates when contacting registries") + if registry.IsRemote() { _ = flags.MarkHidden("authfile") _ = flags.MarkHidden("tls-verify") diff --git a/cmd/podman/inspect.go b/cmd/podman/inspect.go index 667271aaf..4dd6b4a9a 100644 --- a/cmd/podman/inspect.go +++ b/cmd/podman/inspect.go @@ -10,11 +10,10 @@ import ( var ( // Command: podman _inspect_ Object_ID inspectCmd = &cobra.Command{ - Use: "inspect [flags] {CONTAINER_ID | IMAGE_ID}", - Short: "Display the configuration of object denoted by ID", - Long: "Displays the low-level information on an object identified by name or ID", - TraverseChildren: true, - RunE: inspectExec, + Use: "inspect [flags] {CONTAINER_ID | IMAGE_ID}", + Short: "Display the configuration of object denoted by ID", + Long: "Displays the low-level information on an object identified by name or ID", + RunE: inspectExec, Example: `podman inspect fedora podman inspect --type image fedora podman inspect CtrID ImgID diff --git a/cmd/podman/inspect/inspect.go b/cmd/podman/inspect/inspect.go index ce51bc1e9..6fcca597b 100644 --- a/cmd/podman/inspect/inspect.go +++ b/cmd/podman/inspect/inspect.go @@ -8,6 +8,7 @@ import ( "github.com/containers/buildah/pkg/formats" "github.com/containers/libpod/v2/cmd/podman/registry" + "github.com/containers/libpod/v2/cmd/podman/validate" "github.com/containers/libpod/v2/pkg/domain/entities" "github.com/pkg/errors" "github.com/sirupsen/logrus" @@ -32,8 +33,8 @@ func AddInspectFlagSet(cmd *cobra.Command) *entities.InspectOptions { flags.BoolVarP(&opts.Size, "size", "s", false, "Display total file size") flags.StringVarP(&opts.Format, "format", "f", "json", "Format the output to a Go template or json") flags.StringVarP(&opts.Type, "type", "t", AllType, fmt.Sprintf("Specify inspect-oject type (%q, %q or %q)", ImageType, ContainerType, AllType)) - flags.BoolVarP(&opts.Latest, "latest", "l", false, "Act on the latest container Podman is aware of") + validate.AddLatestFlag(cmd, &opts.Latest) return &opts } diff --git a/cmd/podman/main.go b/cmd/podman/main.go index c229e9881..5f740a006 100644 --- a/cmd/podman/main.go +++ b/cmd/podman/main.go @@ -16,7 +16,7 @@ import ( _ "github.com/containers/libpod/v2/cmd/podman/system" _ "github.com/containers/libpod/v2/cmd/podman/volumes" "github.com/containers/libpod/v2/pkg/rootless" - "github.com/containers/libpod/pkg/terminal" + "github.com/containers/libpod/v2/pkg/terminal" "github.com/containers/storage/pkg/reexec" "github.com/sirupsen/logrus" "github.com/spf13/cobra" diff --git a/cmd/podman/manifest/manifest.go b/cmd/podman/manifest/manifest.go index 2bdbec662..f992705c1 100644 --- a/cmd/podman/manifest/manifest.go +++ b/cmd/podman/manifest/manifest.go @@ -10,11 +10,10 @@ import ( var ( manifestDescription = "Creates, modifies, and pushes manifest lists and image indexes." manifestCmd = &cobra.Command{ - Use: "manifest", - Short: "Manipulate manifest lists and image indexes", - Long: manifestDescription, - TraverseChildren: true, - RunE: validate.SubCommandExists, + Use: "manifest", + Short: "Manipulate manifest lists and image indexes", + Long: manifestDescription, + RunE: validate.SubCommandExists, Example: `podman manifest add mylist:v1.11 image:v1.11-amd64 podman manifest create localhost/list podman manifest inspect localhost/list diff --git a/cmd/podman/manifest/push.go b/cmd/podman/manifest/push.go index 226dc716c..d1eb37ae5 100644 --- a/cmd/podman/manifest/push.go +++ b/cmd/podman/manifest/push.go @@ -49,6 +49,7 @@ func init() { flags.StringVar(&manifestPushOpts.SignBy, "sign-by", "", "sign the image using a GPG key with the specified `FINGERPRINT`") flags.BoolVar(&manifestPushOpts.TLSVerifyCLI, "tls-verify", true, "require HTTPS and verify certificates when accessing the registry") flags.BoolVarP(&manifestPushOpts.Quiet, "quiet", "q", false, "don't output progress information when pushing lists") + if registry.IsRemote() { _ = flags.MarkHidden("authfile") _ = flags.MarkHidden("cert-dir") diff --git a/cmd/podman/networks/network.go b/cmd/podman/networks/network.go index aa319f15e..953e4678c 100644 --- a/cmd/podman/networks/network.go +++ b/cmd/podman/networks/network.go @@ -10,11 +10,10 @@ import ( var ( // Command: podman _network_ networkCmd = &cobra.Command{ - Use: "network", - Short: "Manage networks", - Long: "Manage networks", - TraverseChildren: true, - RunE: validate.SubCommandExists, + Use: "network", + Short: "Manage networks", + Long: "Manage networks", + RunE: validate.SubCommandExists, } ) diff --git a/cmd/podman/parse/common.go b/cmd/podman/parse/common.go deleted file mode 100644 index b3aa88da2..000000000 --- a/cmd/podman/parse/common.go +++ /dev/null @@ -1,112 +0,0 @@ -package parse - -import ( - "github.com/pkg/errors" - "github.com/spf13/cobra" -) - -// TODO: the two functions here are almost identical. It may be worth looking -// into generalizing the two a bit more and share code but time is scarce and -// we only live once. - -// CheckAllLatestAndCIDFile checks that --all and --latest are used correctly. -// If cidfile is set, also check for the --cidfile flag. -func CheckAllLatestAndCIDFile(c *cobra.Command, args []string, ignoreArgLen bool, cidfile bool) error { - argLen := len(args) - if c.Flags().Lookup("all") == nil || c.Flags().Lookup("latest") == nil { - if !cidfile { - return errors.New("unable to lookup values for 'latest' or 'all'") - } else if c.Flags().Lookup("cidfile") == nil { - return errors.New("unable to lookup values for 'latest', 'all' or 'cidfile'") - } - } - - specifiedAll, _ := c.Flags().GetBool("all") - specifiedLatest, _ := c.Flags().GetBool("latest") - specifiedCIDFile := false - if cid, _ := c.Flags().GetStringArray("cidfile"); len(cid) > 0 { - specifiedCIDFile = true - } - - if specifiedCIDFile && (specifiedAll || specifiedLatest) { - return errors.Errorf("--all, --latest and --cidfile cannot be used together") - } else if specifiedAll && specifiedLatest { - return errors.Errorf("--all and --latest cannot be used together") - } - - if (argLen > 0) && specifiedAll { - return errors.Errorf("no arguments are needed with --all") - } - - if ignoreArgLen { - return nil - } - - if argLen > 0 { - if specifiedLatest { - return errors.Errorf("no arguments are needed with --latest") - } else if cidfile && (specifiedLatest || specifiedCIDFile) { - return errors.Errorf("no arguments are needed with --latest or --cidfile") - } - } - - if specifiedCIDFile { - return nil - } - - if argLen < 1 && !specifiedAll && !specifiedLatest && !specifiedCIDFile { - return errors.Errorf("you must provide at least one name or id") - } - return nil -} - -// CheckAllLatestAndPodIDFile checks that --all and --latest are used correctly. -// If withIDFile is set, also check for the --pod-id-file flag. -func CheckAllLatestAndPodIDFile(c *cobra.Command, args []string, ignoreArgLen bool, withIDFile bool) error { - argLen := len(args) - if c.Flags().Lookup("all") == nil || c.Flags().Lookup("latest") == nil { - if !withIDFile { - return errors.New("unable to lookup values for 'latest' or 'all'") - } else if c.Flags().Lookup("pod-id-file") == nil { - return errors.New("unable to lookup values for 'latest', 'all' or 'pod-id-file'") - } - } - - specifiedAll, _ := c.Flags().GetBool("all") - specifiedLatest, _ := c.Flags().GetBool("latest") - specifiedPodIDFile := false - if pid, _ := c.Flags().GetStringArray("pod-id-file"); len(pid) > 0 { - specifiedPodIDFile = true - } - - if specifiedPodIDFile && (specifiedAll || specifiedLatest) { - return errors.Errorf("--all, --latest and --pod-id-file cannot be used together") - } else if specifiedAll && specifiedLatest { - return errors.Errorf("--all and --latest cannot be used together") - } - - if (argLen > 0) && specifiedAll { - return errors.Errorf("no arguments are needed with --all") - } - - if ignoreArgLen { - return nil - } - - if argLen > 0 { - if specifiedLatest { - return errors.Errorf("no arguments are needed with --latest") - } else if withIDFile && (specifiedLatest || specifiedPodIDFile) { - return errors.Errorf("no arguments are needed with --latest or --pod-id-file") - } - } - - if specifiedPodIDFile { - return nil - } - - if argLen < 1 && !specifiedAll && !specifiedLatest && !specifiedPodIDFile { - return errors.Errorf("you must provide at least one name or id") - } - return nil -} diff --git a/cmd/podman/play/kube.go b/cmd/podman/play/kube.go index 3567295bf..9bd5c10db 100644 --- a/cmd/podman/play/kube.go +++ b/cmd/podman/play/kube.go @@ -61,7 +61,6 @@ func init() { flags.StringVar(&kubeOptions.SignaturePolicy, "signature-policy", "", "`Pathname` of signature policy file (not usually used)") flags.StringVar(&kubeOptions.SeccompProfileRoot, "seccomp-profile-root", defaultSeccompRoot, "Directory path for seccomp profiles") } - _ = flags.MarkHidden("signature-policy") } diff --git a/cmd/podman/play/play.go b/cmd/podman/play/play.go index f7018eafb..eb8873595 100644 --- a/cmd/podman/play/play.go +++ b/cmd/podman/play/play.go @@ -10,11 +10,10 @@ import ( var ( // Command: podman _play_ playCmd = &cobra.Command{ - Use: "play", - Short: "Play a pod and its containers from a structured file.", - Long: "Play structured data (e.g., Kubernetes pod or service yaml) based on containers and pods.", - TraverseChildren: true, - RunE: validate.SubCommandExists, + Use: "play", + Short: "Play a pod and its containers from a structured file.", + Long: "Play structured data (e.g., Kubernetes pod or service yaml) based on containers and pods.", + RunE: validate.SubCommandExists, } ) diff --git a/cmd/podman/pods/inspect.go b/cmd/podman/pods/inspect.go index 7d31385de..e21c7a74b 100644 --- a/cmd/podman/pods/inspect.go +++ b/cmd/podman/pods/inspect.go @@ -6,6 +6,7 @@ import ( "github.com/containers/buildah/pkg/formats" "github.com/containers/libpod/v2/cmd/podman/registry" + "github.com/containers/libpod/v2/cmd/podman/validate" "github.com/containers/libpod/v2/pkg/domain/entities" "github.com/pkg/errors" "github.com/spf13/cobra" @@ -36,11 +37,8 @@ func init() { Parent: podCmd, }) flags := inspectCmd.Flags() - flags.BoolVarP(&inspectOptions.Latest, "latest", "l", false, "Act on the latest pod podman is aware of") flags.StringVarP(&inspectOptions.Format, "format", "f", "json", "Format the output to a Go template or json") - if registry.IsRemote() { - _ = flags.MarkHidden("latest") - } + validate.AddLatestFlag(inspectCmd, &inspectOptions.Latest) } func inspect(cmd *cobra.Command, args []string) error { diff --git a/cmd/podman/pods/kill.go b/cmd/podman/pods/kill.go index 2076d55e9..2f4930897 100644 --- a/cmd/podman/pods/kill.go +++ b/cmd/podman/pods/kill.go @@ -4,9 +4,9 @@ import ( "context" "fmt" - "github.com/containers/libpod/v2/cmd/podman/parse" "github.com/containers/libpod/v2/cmd/podman/registry" "github.com/containers/libpod/v2/cmd/podman/utils" + "github.com/containers/libpod/v2/cmd/podman/validate" "github.com/containers/libpod/v2/pkg/domain/entities" "github.com/spf13/cobra" ) @@ -21,7 +21,7 @@ var ( Long: podKillDescription, RunE: kill, Args: func(cmd *cobra.Command, args []string) error { - return parse.CheckAllLatestAndCIDFile(cmd, args, false, false) + return validate.CheckAllLatestAndCIDFile(cmd, args, false, false) }, Example: `podman pod kill podID podman pod kill --signal TERM mywebserver @@ -41,14 +41,11 @@ func init() { }) flags := killCommand.Flags() flags.BoolVarP(&killOpts.All, "all", "a", false, "Kill all containers in all pods") - flags.BoolVarP(&killOpts.Latest, "latest", "l", false, "Act on the latest pod podman is aware of") flags.StringVarP(&killOpts.Signal, "signal", "s", "KILL", "Signal to send to the containers in the pod") - if registry.IsRemote() { - _ = flags.MarkHidden("latest") - } - + validate.AddLatestFlag(killCommand, &killOpts.Latest) } -func kill(cmd *cobra.Command, args []string) error { + +func kill(_ *cobra.Command, args []string) error { var ( errs utils.OutputErrors ) diff --git a/cmd/podman/pods/pause.go b/cmd/podman/pods/pause.go index d495db601..f79d88973 100644 --- a/cmd/podman/pods/pause.go +++ b/cmd/podman/pods/pause.go @@ -4,9 +4,9 @@ import ( "context" "fmt" - "github.com/containers/libpod/v2/cmd/podman/parse" "github.com/containers/libpod/v2/cmd/podman/registry" "github.com/containers/libpod/v2/cmd/podman/utils" + "github.com/containers/libpod/v2/cmd/podman/validate" "github.com/containers/libpod/v2/pkg/domain/entities" "github.com/spf13/cobra" ) @@ -21,7 +21,7 @@ var ( Long: podPauseDescription, RunE: pause, Args: func(cmd *cobra.Command, args []string) error { - return parse.CheckAllLatestAndCIDFile(cmd, args, false, false) + return validate.CheckAllLatestAndCIDFile(cmd, args, false, false) }, Example: `podman pod pause podID1 podID2 podman pod pause --latest @@ -41,12 +41,9 @@ func init() { }) flags := pauseCommand.Flags() flags.BoolVarP(&pauseOptions.All, "all", "a", false, "Pause all running pods") - flags.BoolVarP(&pauseOptions.Latest, "latest", "l", false, "Act on the latest pod podman is aware of") - if registry.IsRemote() { - _ = flags.MarkHidden("latest") - } + validate.AddLatestFlag(pauseCommand, &pauseOptions.Latest) } -func pause(cmd *cobra.Command, args []string) error { +func pause(_ *cobra.Command, args []string) error { var ( errs utils.OutputErrors ) diff --git a/cmd/podman/pods/pod.go b/cmd/podman/pods/pod.go index 6eccade22..fff5ea615 100644 --- a/cmd/podman/pods/pod.go +++ b/cmd/podman/pods/pod.go @@ -14,11 +14,10 @@ var ( // Command: podman _pod_ podCmd = &cobra.Command{ - Use: "pod", - Short: "Manage pods", - Long: "Pods are a group of one or more containers sharing the same network, pid and ipc namespaces.", - TraverseChildren: true, - RunE: validate.SubCommandExists, + Use: "pod", + Short: "Manage pods", + Long: "Pods are a group of one or more containers sharing the same network, pid and ipc namespaces.", + RunE: validate.SubCommandExists, } containerConfig = util.DefaultContainerConfig() ) diff --git a/cmd/podman/pods/ps.go b/cmd/podman/pods/ps.go index 49407d39b..7a1221480 100644 --- a/cmd/podman/pods/ps.go +++ b/cmd/podman/pods/ps.go @@ -53,18 +53,15 @@ func init() { // TODO should we make this a [] ? flags.StringSliceVarP(&inputFilters, "filter", "f", []string{}, "Filter output based on conditions given") flags.StringVar(&psInput.Format, "format", "", "Pretty-print pods to JSON or using a Go template") - flags.BoolVarP(&psInput.Latest, "latest", "l", false, "Act on the latest pod podman is aware of") flags.BoolVar(&psInput.Namespace, "namespace", false, "Display namespace information of the pod") flags.BoolVar(&psInput.Namespace, "ns", false, "Display namespace information of the pod") flags.BoolVar(&noTrunc, "no-trunc", false, "Do not truncate pod and container IDs") flags.BoolVarP(&psInput.Quiet, "quiet", "q", false, "Print the numeric IDs of the pods only") flags.StringVar(&psInput.Sort, "sort", "created", "Sort output by created, id, name, or number") - if registry.IsRemote() { - _ = flags.MarkHidden("latest") - } + validate.AddLatestFlag(psCmd, &psInput.Latest) } -func pods(cmd *cobra.Command, args []string) error { +func pods(cmd *cobra.Command, _ []string) error { var ( w io.Writer = os.Stdout row string diff --git a/cmd/podman/pods/restart.go b/cmd/podman/pods/restart.go index 7ca6b3cac..77d9f62d4 100644 --- a/cmd/podman/pods/restart.go +++ b/cmd/podman/pods/restart.go @@ -4,9 +4,9 @@ import ( "context" "fmt" - "github.com/containers/libpod/v2/cmd/podman/parse" "github.com/containers/libpod/v2/cmd/podman/registry" "github.com/containers/libpod/v2/cmd/podman/utils" + "github.com/containers/libpod/v2/cmd/podman/validate" "github.com/containers/libpod/v2/pkg/domain/entities" "github.com/spf13/cobra" ) @@ -21,7 +21,7 @@ var ( Long: podRestartDescription, RunE: restart, Args: func(cmd *cobra.Command, args []string) error { - return parse.CheckAllLatestAndCIDFile(cmd, args, false, false) + return validate.CheckAllLatestAndCIDFile(cmd, args, false, false) }, Example: `podman pod restart podID1 podID2 podman pod restart --latest @@ -42,10 +42,7 @@ func init() { flags := restartCommand.Flags() flags.BoolVarP(&restartOptions.All, "all", "a", false, "Restart all running pods") - flags.BoolVarP(&restartOptions.Latest, "latest", "l", false, "Restart the latest pod podman is aware of") - if registry.IsRemote() { - _ = flags.MarkHidden("latest") - } + validate.AddLatestFlag(restartCommand, &restartOptions.Latest) } func restart(cmd *cobra.Command, args []string) error { diff --git a/cmd/podman/pods/rm.go b/cmd/podman/pods/rm.go index 60972da76..3e07b31e9 100644 --- a/cmd/podman/pods/rm.go +++ b/cmd/podman/pods/rm.go @@ -5,9 +5,9 @@ import ( "fmt" "github.com/containers/libpod/v2/cmd/podman/common" - "github.com/containers/libpod/v2/cmd/podman/parse" "github.com/containers/libpod/v2/cmd/podman/registry" "github.com/containers/libpod/v2/cmd/podman/utils" + "github.com/containers/libpod/v2/cmd/podman/validate" "github.com/containers/libpod/v2/pkg/domain/entities" "github.com/spf13/cobra" ) @@ -30,7 +30,7 @@ var ( Long: podRmDescription, RunE: rm, Args: func(cmd *cobra.Command, args []string) error { - return parse.CheckAllLatestAndPodIDFile(cmd, args, false, true) + return validate.CheckAllLatestAndPodIDFile(cmd, args, false, true) }, Example: `podman pod rm mywebserverpod podman pod rm -f 860a4b23 @@ -49,15 +49,15 @@ func init() { flags.BoolVarP(&rmOptions.All, "all", "a", false, "Remove all running pods") flags.BoolVarP(&rmOptions.Force, "force", "f", false, "Force removal of a running pod by first stopping all containers, then removing all containers in the pod. The default is false") flags.BoolVarP(&rmOptions.Ignore, "ignore", "i", false, "Ignore errors when a specified pod is missing") - flags.BoolVarP(&rmOptions.Latest, "latest", "l", false, "Remove the latest pod podman is aware of") flags.StringArrayVarP(&rmOptions.PodIDFiles, "pod-id-file", "", nil, "Read the pod ID from the file") + validate.AddLatestFlag(rmCommand, &rmOptions.Latest) + if registry.IsRemote() { - _ = flags.MarkHidden("latest") _ = flags.MarkHidden("ignore") } } -func rm(cmd *cobra.Command, args []string) error { +func rm(_ *cobra.Command, args []string) error { ids, err := common.ReadPodIDFiles(rmOptions.PodIDFiles) if err != nil { return err diff --git a/cmd/podman/pods/start.go b/cmd/podman/pods/start.go index db09f1bce..586737fb2 100644 --- a/cmd/podman/pods/start.go +++ b/cmd/podman/pods/start.go @@ -5,9 +5,9 @@ import ( "fmt" "github.com/containers/libpod/v2/cmd/podman/common" - "github.com/containers/libpod/v2/cmd/podman/parse" "github.com/containers/libpod/v2/cmd/podman/registry" "github.com/containers/libpod/v2/cmd/podman/utils" + "github.com/containers/libpod/v2/cmd/podman/validate" "github.com/containers/libpod/v2/pkg/domain/entities" "github.com/spf13/cobra" ) @@ -29,7 +29,7 @@ var ( Long: podStartDescription, RunE: start, Args: func(cmd *cobra.Command, args []string) error { - return parse.CheckAllLatestAndPodIDFile(cmd, args, false, true) + return validate.CheckAllLatestAndPodIDFile(cmd, args, false, true) }, Example: `podman pod start podID podman pod start --latest @@ -50,11 +50,8 @@ func init() { flags := startCommand.Flags() flags.BoolVarP(&startOptions.All, "all", "a", false, "Restart all running pods") - flags.BoolVarP(&startOptions.Latest, "latest", "l", false, "Restart the latest pod podman is aware of") flags.StringArrayVarP(&startOptions.PodIDFiles, "pod-id-file", "", nil, "Read the pod ID from the file") - if registry.IsRemote() { - _ = flags.MarkHidden("latest") - } + validate.AddLatestFlag(startCommand, &startOptions.Latest) } func start(cmd *cobra.Command, args []string) error { diff --git a/cmd/podman/pods/stats.go b/cmd/podman/pods/stats.go index 19ff750aa..930a6d15c 100644 --- a/cmd/podman/pods/stats.go +++ b/cmd/podman/pods/stats.go @@ -13,6 +13,7 @@ import ( "github.com/buger/goterm" "github.com/containers/buildah/pkg/formats" "github.com/containers/libpod/v2/cmd/podman/registry" + "github.com/containers/libpod/v2/cmd/podman/validate" "github.com/containers/libpod/v2/pkg/domain/entities" "github.com/containers/libpod/v2/pkg/util/camelcase" "github.com/spf13/cobra" @@ -55,13 +56,9 @@ func init() { flags := statsCmd.Flags() flags.BoolVarP(&statsOptions.All, "all", "a", false, "Provide stats for all pods") flags.StringVar(&statsOptions.Format, "format", "", "Pretty-print container statistics to JSON or using a Go template") - flags.BoolVarP(&statsOptions.Latest, "latest", "l", false, "Provide stats on the latest pod Podman is aware of") flags.BoolVar(&statsOptions.NoReset, "no-reset", false, "Disable resetting the screen when streaming") flags.BoolVar(&statsOptions.NoStream, "no-stream", false, "Disable streaming stats and only pull the first result") - - if registry.IsRemote() { - _ = flags.MarkHidden("latest") - } + validate.AddLatestFlag(statsCmd, &statsOptions.Latest) } func stats(cmd *cobra.Command, args []string) error { diff --git a/cmd/podman/pods/stop.go b/cmd/podman/pods/stop.go index afe3b6eae..84190fd08 100644 --- a/cmd/podman/pods/stop.go +++ b/cmd/podman/pods/stop.go @@ -5,9 +5,9 @@ import ( "fmt" "github.com/containers/libpod/v2/cmd/podman/common" - "github.com/containers/libpod/v2/cmd/podman/parse" "github.com/containers/libpod/v2/cmd/podman/registry" "github.com/containers/libpod/v2/cmd/podman/utils" + "github.com/containers/libpod/v2/cmd/podman/validate" "github.com/containers/libpod/v2/pkg/domain/entities" "github.com/spf13/cobra" ) @@ -34,7 +34,7 @@ var ( Long: podStopDescription, RunE: stop, Args: func(cmd *cobra.Command, args []string) error { - return parse.CheckAllLatestAndPodIDFile(cmd, args, false, true) + return validate.CheckAllLatestAndPodIDFile(cmd, args, false, true) }, Example: `podman pod stop mywebserverpod podman pod stop --latest @@ -51,13 +51,14 @@ func init() { flags := stopCommand.Flags() flags.BoolVarP(&stopOptions.All, "all", "a", false, "Stop all running pods") flags.BoolVarP(&stopOptions.Ignore, "ignore", "i", false, "Ignore errors when a specified pod is missing") - flags.BoolVarP(&stopOptions.Latest, "latest", "l", false, "Stop the latest pod podman is aware of") flags.UintVarP(&stopOptions.TimeoutCLI, "time", "t", containerConfig.Engine.StopTimeout, "Seconds to wait for pod stop before killing the container") flags.StringArrayVarP(&stopOptions.PodIDFiles, "pod-id-file", "", nil, "Read the pod ID from the file") + validate.AddLatestFlag(stopCommand, &stopOptions.Latest) + if registry.IsRemote() { - _ = flags.MarkHidden("latest") _ = flags.MarkHidden("ignore") } + flags.SetNormalizeFunc(utils.AliasFlags) } diff --git a/cmd/podman/pods/top.go b/cmd/podman/pods/top.go index 551eeccac..840d2da0a 100644 --- a/cmd/podman/pods/top.go +++ b/cmd/podman/pods/top.go @@ -8,6 +8,7 @@ import ( "text/tabwriter" "github.com/containers/libpod/v2/cmd/podman/registry" + "github.com/containers/libpod/v2/cmd/podman/validate" "github.com/containers/libpod/v2/pkg/domain/entities" "github.com/containers/libpod/v2/pkg/util" "github.com/pkg/errors" @@ -50,15 +51,11 @@ func init() { flags := topCommand.Flags() flags.SetInterspersed(false) flags.BoolVar(&topOptions.ListDescriptors, "list-descriptors", false, "") - flags.BoolVarP(&topOptions.Latest, "latest", "l", false, "Act on the latest container podman is aware of") - _ = flags.MarkHidden("list-descriptors") // meant only for bash completion - if registry.IsRemote() { - _ = flags.MarkHidden("latest") - } + validate.AddLatestFlag(topCommand, &topOptions.Latest) } -func top(cmd *cobra.Command, args []string) error { +func top(_ *cobra.Command, args []string) error { if topOptions.ListDescriptors { descriptors, err := util.GetContainerPidInformationDescriptors() if err != nil { diff --git a/cmd/podman/pods/unpause.go b/cmd/podman/pods/unpause.go index 09f2e472d..f96f3481c 100644 --- a/cmd/podman/pods/unpause.go +++ b/cmd/podman/pods/unpause.go @@ -4,9 +4,9 @@ import ( "context" "fmt" - "github.com/containers/libpod/v2/cmd/podman/parse" "github.com/containers/libpod/v2/cmd/podman/registry" "github.com/containers/libpod/v2/cmd/podman/utils" + "github.com/containers/libpod/v2/cmd/podman/validate" "github.com/containers/libpod/v2/pkg/domain/entities" "github.com/spf13/cobra" ) @@ -21,7 +21,7 @@ var ( Long: podUnpauseDescription, RunE: unpause, Args: func(cmd *cobra.Command, args []string) error { - return parse.CheckAllLatestAndCIDFile(cmd, args, false, false) + return validate.CheckAllLatestAndCIDFile(cmd, args, false, false) }, Example: `podman pod unpause podID1 podID2 podman pod unpause --all @@ -41,12 +41,10 @@ func init() { }) flags := unpauseCommand.Flags() flags.BoolVarP(&unpauseOptions.All, "all", "a", false, "Pause all running pods") - flags.BoolVarP(&unpauseOptions.Latest, "latest", "l", false, "Act on the latest pod podman is aware of") - if registry.IsRemote() { - _ = flags.MarkHidden("latest") - } + validate.AddLatestFlag(unpauseCommand, &unpauseOptions.Latest) } -func unpause(cmd *cobra.Command, args []string) error { + +func unpause(_ *cobra.Command, args []string) error { var ( errs utils.OutputErrors ) diff --git a/cmd/podman/registry/config.go b/cmd/podman/registry/config.go index 85bf5f944..75e67b35d 100644 --- a/cmd/podman/registry/config.go +++ b/cmd/podman/registry/config.go @@ -5,7 +5,6 @@ import ( "os" "path/filepath" "runtime" - "strings" "sync" "github.com/containers/common/pkg/config" @@ -45,7 +44,7 @@ func newPodmanConfig() { case "linux": // Some linux clients might only be compiled without ABI // support (e.g., podman-remote). - if abiSupport { + if abiSupport && !remoteOverride { mode = entities.ABIMode } else { mode = entities.TunnelMode @@ -55,19 +54,6 @@ func newPodmanConfig() { os.Exit(1) } - // Check if need to fallback to the tunnel mode if --remote is used. - if abiSupport && mode == entities.ABIMode { - // cobra.Execute() may not be called yet, so we peek at os.Args. - for _, v := range os.Args { - // Prefix checking works because of how default EngineMode's - // have been defined. - if strings.HasPrefix(v, "--remote") { - mode = entities.TunnelMode - break - } - } - } - cfg, err := config.NewConfig("") if err != nil { fmt.Fprint(os.Stderr, "Failed to obtain podman configuration: "+err.Error()) diff --git a/cmd/podman/registry/remote.go b/cmd/podman/registry/remote.go index 3040c4c2a..006a1b900 100644 --- a/cmd/podman/registry/remote.go +++ b/cmd/podman/registry/remote.go @@ -1,9 +1,26 @@ package registry import ( + "os" + "sync" + "github.com/containers/libpod/v2/pkg/domain/entities" + "github.com/spf13/cobra" +) + +var ( + // Was --remote given on command line + remoteOverride bool + remoteSync sync.Once ) +// IsRemote returns true if podman was built to run remote +// Use in init() functions as a initialization check func IsRemote() bool { - return podmanOptions.EngineMode == entities.TunnelMode + remoteSync.Do(func() { + remote := &cobra.Command{} + remote.Flags().BoolVarP(&remoteOverride, "remote", "r", false, "") + _ = remote.ParseFlags(os.Args) + }) + return podmanOptions.EngineMode == entities.TunnelMode || remoteOverride } diff --git a/cmd/podman/root.go b/cmd/podman/root.go index 1ca358e71..7c54da91a 100644 --- a/cmd/podman/root.go +++ b/cmd/podman/root.go @@ -6,6 +6,7 @@ import ( "path" "runtime" "runtime/pprof" + "strconv" "strings" "github.com/containers/common/pkg/config" @@ -20,7 +21,6 @@ import ( "github.com/pkg/errors" "github.com/sirupsen/logrus" "github.com/spf13/cobra" - "github.com/spf13/pflag" ) // HelpTemplate is the help template for podman commands @@ -79,7 +79,7 @@ func init() { syslogHook, ) - rootFlags(registry.PodmanConfig(), rootCmd.PersistentFlags()) + rootFlags(rootCmd, registry.PodmanConfig()) // "version" is a local flag to avoid collisions with sub-commands that use "-v" var dummyVersion bool @@ -111,6 +111,15 @@ func persistentPreRunE(cmd *cobra.Command, args []string) error { cfg := registry.PodmanConfig() + // Validate --remote and --latest not given on same command + latest := cmd.Flags().Lookup("latest") + if latest != nil { + value, _ := strconv.ParseBool(latest.Value.String()) + if cfg.Remote && value { + return errors.Errorf("For %s \"--remote\" and \"--latest\", are mutually exclusive flags", cmd.CommandPath()) + } + } + // Prep the engines if _, err := registry.NewImageEngine(cmd, args); err != nil { return err @@ -193,7 +202,7 @@ func loggingHook() { } } if !found { - fmt.Fprintf(os.Stderr, "Log Level \"%s\" is not supported, choose from: %s\n", logLevel, strings.Join(logLevels, ", ")) + fmt.Fprintf(os.Stderr, "Log Level %q is not supported, choose from: %s\n", logLevel, strings.Join(logLevels, ", ")) os.Exit(1) } @@ -209,44 +218,45 @@ func loggingHook() { } } -func rootFlags(opts *entities.PodmanConfig, flags *pflag.FlagSet) { - // V2 flags - flags.BoolVarP(&opts.Remote, "remote", "r", false, "Access remote Podman service (default false)") +func rootFlags(cmd *cobra.Command, opts *entities.PodmanConfig) { + cfg := opts.Config + lFlags := cmd.Flags() custom, _ := config.ReadCustomConfig() defaultURI := custom.Engine.RemoteURI if defaultURI == "" { defaultURI = registry.DefaultAPIAddress() } - flags.StringVar(&opts.URI, "url", defaultURI, "URL to access Podman service (CONTAINER_HOST)") - flags.StringVar(&opts.Identity, "identity", custom.Engine.RemoteIdentity, "path to SSH identity file, (CONTAINER_SSHKEY)") - - cfg := opts.Config - flags.StringVar(&cfg.Engine.CgroupManager, "cgroup-manager", cfg.Engine.CgroupManager, "Cgroup manager to use (\"cgroupfs\"|\"systemd\")") - flags.StringVar(&opts.CPUProfile, "cpu-profile", "", "Path for the cpu profiling results") - flags.StringVar(&opts.ConmonPath, "conmon", "", "Path of the conmon binary") - flags.StringVar(&cfg.Engine.NetworkCmdPath, "network-cmd-path", cfg.Engine.NetworkCmdPath, "Path to the command for configuring the network") - flags.StringVar(&cfg.Network.NetworkConfigDir, "cni-config-dir", cfg.Network.NetworkConfigDir, "Path of the configuration directory for CNI networks") - flags.StringVar(&cfg.Containers.DefaultMountsFile, "default-mounts-file", cfg.Containers.DefaultMountsFile, "Path to default mounts file") - flags.StringVar(&cfg.Engine.EventsLogger, "events-backend", cfg.Engine.EventsLogger, `Events backend to use ("file"|"journald"|"none")`) - flags.StringSliceVar(&cfg.Engine.HooksDir, "hooks-dir", cfg.Engine.HooksDir, "Set the OCI hooks directory path (may be set multiple times)") - flags.IntVar(&opts.MaxWorks, "max-workers", (runtime.NumCPU()*3)+1, "The maximum number of workers for parallel operations") - flags.StringVar(&cfg.Engine.Namespace, "namespace", cfg.Engine.Namespace, "Set the libpod namespace, used to create separate views of the containers and pods on the system") - flags.StringVar(&cfg.Engine.StaticDir, "root", "", "Path to the root directory in which data, including images, is stored") - flags.StringVar(&opts.RegistriesConf, "registries-conf", "", "Path to a registries.conf to use for image processing") - flags.StringVar(&opts.Runroot, "runroot", "", "Path to the 'run directory' where all state information is stored") - flags.StringVar(&opts.RuntimePath, "runtime", "", "Path to the OCI-compatible binary used to run containers, default is /usr/bin/runc") + lFlags.BoolVarP(&opts.Remote, "remote", "r", false, "Access remote Podman service (default false)") + lFlags.StringVar(&opts.URI, "url", defaultURI, "URL to access Podman service (CONTAINER_HOST)") + lFlags.StringVar(&opts.Identity, "identity", custom.Engine.RemoteIdentity, "path to SSH identity file, (CONTAINER_SSHKEY)") + + pFlags := cmd.PersistentFlags() + pFlags.StringVar(&cfg.Engine.CgroupManager, "cgroup-manager", cfg.Engine.CgroupManager, "Cgroup manager to use (\"cgroupfs\"|\"systemd\")") + pFlags.StringVar(&opts.CPUProfile, "cpu-profile", "", "Path for the cpu profiling results") + pFlags.StringVar(&opts.ConmonPath, "conmon", "", "Path of the conmon binary") + pFlags.StringVar(&cfg.Engine.NetworkCmdPath, "network-cmd-path", cfg.Engine.NetworkCmdPath, "Path to the command for configuring the network") + pFlags.StringVar(&cfg.Network.NetworkConfigDir, "cni-config-dir", cfg.Network.NetworkConfigDir, "Path of the configuration directory for CNI networks") + pFlags.StringVar(&cfg.Containers.DefaultMountsFile, "default-mounts-file", cfg.Containers.DefaultMountsFile, "Path to default mounts file") + pFlags.StringVar(&cfg.Engine.EventsLogger, "events-backend", cfg.Engine.EventsLogger, `Events backend to use ("file"|"journald"|"none")`) + pFlags.StringSliceVar(&cfg.Engine.HooksDir, "hooks-dir", cfg.Engine.HooksDir, "Set the OCI hooks directory path (may be set multiple times)") + pFlags.IntVar(&opts.MaxWorks, "max-workers", (runtime.NumCPU()*3)+1, "The maximum number of workers for parallel operations") + pFlags.StringVar(&cfg.Engine.Namespace, "namespace", cfg.Engine.Namespace, "Set the libpod namespace, used to create separate views of the containers and pods on the system") + pFlags.StringVar(&cfg.Engine.StaticDir, "root", "", "Path to the root directory in which data, including images, is stored") + pFlags.StringVar(&opts.RegistriesConf, "registries-conf", "", "Path to a registries.conf to use for image processing") + pFlags.StringVar(&opts.Runroot, "runroot", "", "Path to the 'run directory' where all state information is stored") + pFlags.StringVar(&opts.RuntimePath, "runtime", "", "Path to the OCI-compatible binary used to run containers, default is /usr/bin/runc") // -s is deprecated due to conflict with -s on subcommands - flags.StringVar(&opts.StorageDriver, "storage-driver", "", "Select which storage driver is used to manage storage of images and containers (default is overlay)") - flags.StringArrayVar(&opts.StorageOpts, "storage-opt", []string{}, "Used to pass an option to the storage driver") + pFlags.StringVar(&opts.StorageDriver, "storage-driver", "", "Select which storage driver is used to manage storage of images and containers (default is overlay)") + pFlags.StringArrayVar(&opts.StorageOpts, "storage-opt", []string{}, "Used to pass an option to the storage driver") - flags.StringVar(&opts.Engine.TmpDir, "tmpdir", "", "Path to the tmp directory for libpod state content.\n\nNote: use the environment variable 'TMPDIR' to change the temporary storage location for container images, '/var/tmp'.\n") - flags.BoolVar(&opts.Trace, "trace", false, "Enable opentracing output (default false)") + pFlags.StringVar(&opts.Engine.TmpDir, "tmpdir", "", "Path to the tmp directory for libpod state content.\n\nNote: use the environment variable 'TMPDIR' to change the temporary storage location for container images, '/var/tmp'.\n") + pFlags.BoolVar(&opts.Trace, "trace", false, "Enable opentracing output (default false)") // Override default --help information of `--help` global flag var dummyHelp bool - flags.BoolVar(&dummyHelp, "help", false, "Help for podman") - flags.StringVar(&logLevel, "log-level", logLevel, fmt.Sprintf("Log messages above specified level (%s)", strings.Join(logLevels, ", "))) + pFlags.BoolVar(&dummyHelp, "help", false, "Help for podman") + pFlags.StringVar(&logLevel, "log-level", logLevel, fmt.Sprintf("Log messages above specified level (%s)", strings.Join(logLevels, ", "))) // Hide these flags for both ABI and Tunneling for _, f := range []string{ @@ -256,13 +266,13 @@ func rootFlags(opts *entities.PodmanConfig, flags *pflag.FlagSet) { "registries-conf", "trace", } { - if err := flags.MarkHidden(f); err != nil { + if err := pFlags.MarkHidden(f); err != nil { logrus.Warnf("unable to mark %s flag as hidden: %s", f, err.Error()) } } // Only create these flags for ABI connections if !registry.IsRemote() { - flags.BoolVar(&useSyslog, "syslog", false, "Output logging information to syslog as well as the console (default false)") + pFlags.BoolVar(&useSyslog, "syslog", false, "Output logging information to syslog as well as the console (default false)") } } diff --git a/cmd/podman/system/connection.go b/cmd/podman/system/connection.go index cb22522aa..bdb113ea3 100644 --- a/cmd/podman/system/connection.go +++ b/cmd/podman/system/connection.go @@ -60,7 +60,6 @@ func init() { }) flags := connectionCmd.Flags() - flags.StringVar(&cOpts.Identity, "identity", "", "path to ssh identity file") flags.IntVarP(&cOpts.Port, "port", "p", 22, "port number for destination") flags.StringVar(&cOpts.UDSPath, "socket-path", "", "path to podman socket on remote host. (default '/run/podman/podman.sock' or '/run/user/{uid}/podman/podman.sock)") } diff --git a/cmd/podman/system/system.go b/cmd/podman/system/system.go index 6c4b26955..2dd6cf774 100644 --- a/cmd/podman/system/system.go +++ b/cmd/podman/system/system.go @@ -13,11 +13,10 @@ var ( // Command: podman _system_ systemCmd = &cobra.Command{ - Use: "system", - Short: "Manage podman", - Long: "Manage podman", - TraverseChildren: true, - RunE: validate.SubCommandExists, + Use: "system", + Short: "Manage podman", + Long: "Manage podman", + RunE: validate.SubCommandExists, } ) diff --git a/cmd/podman/validate/args.go b/cmd/podman/validate/args.go index 69240798f..a33f47959 100644 --- a/cmd/podman/validate/args.go +++ b/cmd/podman/validate/args.go @@ -2,6 +2,7 @@ package validate import ( "fmt" + "strconv" "github.com/pkg/errors" "github.com/spf13/cobra" @@ -25,8 +26,122 @@ func SubCommandExists(cmd *cobra.Command, args []string) error { // IDOrLatestArgs used to validate a nameOrId was provided or the "--latest" flag func IDOrLatestArgs(cmd *cobra.Command, args []string) error { - if len(args) > 1 || (len(args) == 0 && !cmd.Flag("latest").Changed) { - return fmt.Errorf("`%s` requires a name, id or the \"--latest\" flag", cmd.CommandPath()) + if len(args) > 1 { + return fmt.Errorf("`%s` accepts at most one argument", cmd.CommandPath()) + } + + latest := cmd.Flag("latest") + if latest != nil { + given, _ := strconv.ParseBool(cmd.Flag("latest").Value.String()) + if len(args) == 0 && !given { + return fmt.Errorf("%q requires a name, id, or the \"--latest\" flag", cmd.CommandPath()) + } + } + return nil +} + +// TODO: the two functions CheckAllLatestAndCIDFile and CheckAllLatestAndPodIDFile are almost identical. +// It may be worth looking into generalizing the two a bit more and share code but time is scarce and +// we only live once. + +// CheckAllLatestAndCIDFile checks that --all and --latest are used correctly. +// If cidfile is set, also check for the --cidfile flag. +func CheckAllLatestAndCIDFile(c *cobra.Command, args []string, ignoreArgLen bool, cidfile bool) error { + argLen := len(args) + if c.Flags().Lookup("all") == nil || c.Flags().Lookup("latest") == nil { + if !cidfile { + return errors.New("unable to lookup values for 'latest' or 'all'") + } else if c.Flags().Lookup("cidfile") == nil { + return errors.New("unable to lookup values for 'latest', 'all' or 'cidfile'") + } + } + + specifiedAll, _ := c.Flags().GetBool("all") + specifiedLatest, _ := c.Flags().GetBool("latest") + specifiedCIDFile := false + if cid, _ := c.Flags().GetStringArray("cidfile"); len(cid) > 0 { + specifiedCIDFile = true + } + + if specifiedCIDFile && (specifiedAll || specifiedLatest) { + return errors.Errorf("--all, --latest and --cidfile cannot be used together") + } else if specifiedAll && specifiedLatest { + return errors.Errorf("--all and --latest cannot be used together") + } + + if (argLen > 0) && specifiedAll { + return errors.Errorf("no arguments are needed with --all") + } + + if ignoreArgLen { + return nil + } + + if argLen > 0 { + if specifiedLatest { + return errors.Errorf("no arguments are needed with --latest") + } else if cidfile && (specifiedLatest || specifiedCIDFile) { + return errors.Errorf("no arguments are needed with --latest or --cidfile") + } + } + + if specifiedCIDFile { + return nil + } + + if argLen < 1 && !specifiedAll && !specifiedLatest && !specifiedCIDFile { + return errors.Errorf("you must provide at least one name or id") + } + return nil +} + +// CheckAllLatestAndPodIDFile checks that --all and --latest are used correctly. +// If withIDFile is set, also check for the --pod-id-file flag. +func CheckAllLatestAndPodIDFile(c *cobra.Command, args []string, ignoreArgLen bool, withIDFile bool) error { + argLen := len(args) + if c.Flags().Lookup("all") == nil || c.Flags().Lookup("latest") == nil { + if !withIDFile { + return errors.New("unable to lookup values for 'latest' or 'all'") + } else if c.Flags().Lookup("pod-id-file") == nil { + return errors.New("unable to lookup values for 'latest', 'all' or 'pod-id-file'") + } + } + + specifiedAll, _ := c.Flags().GetBool("all") + specifiedLatest, _ := c.Flags().GetBool("latest") + specifiedPodIDFile := false + if pid, _ := c.Flags().GetStringArray("pod-id-file"); len(pid) > 0 { + specifiedPodIDFile = true + } + + if specifiedPodIDFile && (specifiedAll || specifiedLatest) { + return errors.Errorf("--all, --latest and --pod-id-file cannot be used together") + } else if specifiedAll && specifiedLatest { + return errors.Errorf("--all and --latest cannot be used together") + } + + if (argLen > 0) && specifiedAll { + return errors.Errorf("no arguments are needed with --all") + } + + if ignoreArgLen { + return nil + } + + if argLen > 0 { + if specifiedLatest { + return errors.Errorf("no arguments are needed with --latest") + } else if withIDFile && (specifiedLatest || specifiedPodIDFile) { + return errors.Errorf("no arguments are needed with --latest or --pod-id-file") + } + } + + if specifiedPodIDFile { + return nil + } + + if argLen < 1 && !specifiedAll && !specifiedLatest && !specifiedPodIDFile { + return errors.Errorf("you must provide at least one name or id") } return nil } diff --git a/cmd/podman/validate/latest.go b/cmd/podman/validate/latest.go new file mode 100644 index 000000000..6e2aa063b --- /dev/null +++ b/cmd/podman/validate/latest.go @@ -0,0 +1,15 @@ +package validate + +import ( + "github.com/containers/libpod/cmd/podman/registry" + "github.com/spf13/cobra" +) + +func AddLatestFlag(cmd *cobra.Command, b *bool) { + // Initialization flag verification + cmd.Flags().BoolVarP(b, "latest", "l", false, + "Act on the latest container podman is aware of\nNot supported with the \"--remote\" flag") + if registry.IsRemote() { + _ = cmd.Flags().MarkHidden("latest") + } +} diff --git a/cmd/podman/volumes/volume.go b/cmd/podman/volumes/volume.go index 4514be88a..3ef20a027 100644 --- a/cmd/podman/volumes/volume.go +++ b/cmd/podman/volumes/volume.go @@ -13,11 +13,10 @@ var ( // Command: podman _volume_ volumeCmd = &cobra.Command{ - Use: "volume", - Short: "Manage volumes", - Long: "Volumes are created in and can be shared between containers", - TraverseChildren: true, - RunE: validate.SubCommandExists, + Use: "volume", + Short: "Manage volumes", + Long: "Volumes are created in and can be shared between containers", + RunE: validate.SubCommandExists, } ) -- cgit v1.2.3-54-g00ecf From 1444be3d53dfa677332fe7731fc7e0058741394e Mon Sep 17 00:00:00 2001 From: Maximilian Müller Date: Fri, 26 Jun 2020 13:54:34 +0200 Subject: Fix a bug with APIv2 compat network remove to log an ErrNetworkNotFound instead of nil MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Maximilian Müller --- pkg/api/handlers/compat/networks.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/api/handlers/compat/networks.go b/pkg/api/handlers/compat/networks.go index fe7d8888e..2e11c0edb 100644 --- a/pkg/api/handlers/compat/networks.go +++ b/pkg/api/handlers/compat/networks.go @@ -285,7 +285,7 @@ func RemoveNetwork(w http.ResponseWriter, r *http.Request) { return } if !exists { - utils.Error(w, "network not found", http.StatusNotFound, err) + utils.Error(w, "network not found", http.StatusNotFound, network.ErrNetworkNotFound) return } if err := network.RemoveNetwork(config, name); err != nil { -- cgit v1.2.3-54-g00ecf From 8643526953a32b854f4f7d69e570c9478b9f247c Mon Sep 17 00:00:00 2001 From: Valentin Rothberg Date: Thu, 25 Jun 2020 10:13:58 +0200 Subject: generate systemd: improve pod-flags filter When generating systemd unit for pods, we need to remove certain pod-related flags from the containers' create commands. Make sure to account for all the syntax including a single argument with key and value being split by `=`. Fixes: #6766 Signed-off-by: Valentin Rothberg --- pkg/systemd/generate/common.go | 5 +++++ pkg/systemd/generate/common_test.go | 9 +++++++-- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/pkg/systemd/generate/common.go b/pkg/systemd/generate/common.go index fe56dc874..d6d18a810 100644 --- a/pkg/systemd/generate/common.go +++ b/pkg/systemd/generate/common.go @@ -1,6 +1,8 @@ package generate import ( + "strings" + "github.com/pkg/errors" ) @@ -44,6 +46,9 @@ func filterPodFlags(command []string) []string { i++ continue } + if strings.HasPrefix(s, "--pod=") || strings.HasPrefix(s, "--pod-id-file=") { + continue + } processed = append(processed, s) } return processed diff --git a/pkg/systemd/generate/common_test.go b/pkg/systemd/generate/common_test.go index f53bb7828..389c30f59 100644 --- a/pkg/systemd/generate/common_test.go +++ b/pkg/systemd/generate/common_test.go @@ -1,6 +1,7 @@ package generate import ( + "strings" "testing" "github.com/stretchr/testify/assert" @@ -14,12 +15,16 @@ func TestFilterPodFlags(t *testing.T) { {[]string{"podman", "pod", "create"}}, {[]string{"podman", "pod", "create", "--name", "foo"}}, {[]string{"podman", "pod", "create", "--pod-id-file", "foo"}}, + {[]string{"podman", "pod", "create", "--pod-id-file=foo"}}, {[]string{"podman", "run", "--pod", "foo"}}, + {[]string{"podman", "run", "--pod=foo"}}, } for _, test := range tests { processed := filterPodFlags(test.input) - assert.NotContains(t, processed, "--pod-id-file") - assert.NotContains(t, processed, "--pod") + for _, s := range processed { + assert.False(t, strings.HasPrefix(s, "--pod-id-file")) + assert.False(t, strings.HasPrefix(s, "--pod")) + } } } -- cgit v1.2.3-54-g00ecf From 0b7885b90a959f0552a481bcf7b27aa58d753d53 Mon Sep 17 00:00:00 2001 From: Matthew Heon Date: Fri, 26 Jun 2020 10:07:20 -0400 Subject: Ensure umask is set appropriately for 'system service' We need a umask of 0022 to ensure containers are created correctly, but we set a different one prior to starting the server (to ensure the unix socket has the right permissions). Thus, we need to set the umask after the socket has been bound, but before the server begins accepting requests. Fixes #6787 Signed-off-by: Matthew Heon --- pkg/api/server/server.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/pkg/api/server/server.go b/pkg/api/server/server.go index d68f6893a..8af6d3186 100644 --- a/pkg/api/server/server.go +++ b/pkg/api/server/server.go @@ -173,6 +173,10 @@ func (s *APIServer) Serve() error { }() } + // Before we start serving, ensure umask is properly set for container + // creation. + _ = syscall.Umask(0022) + go func() { err := s.Server.Serve(s.Listener) if err != nil && err != http.ErrServerClosed { -- cgit v1.2.3-54-g00ecf From 626aeffc559b8aea0f24ac5f7506ba1eacb6f9e3 Mon Sep 17 00:00:00 2001 From: Ralf Haferkamp Date: Fri, 26 Jun 2020 11:14:35 +0200 Subject: specgen: fix order for setting rlimits Also make sure that the limits we set for rootless are not higher than what we'd set for root containers. Rootless containers failed to start when the calling user already had ulimit (e.g. on NOFILE) set. This is basically a cherry-pick of 76f8efc0d0d into specgen Signed-off-by: Ralf Haferkamp --- pkg/specgen/generate/oci.go | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/pkg/specgen/generate/oci.go b/pkg/specgen/generate/oci.go index 3732d5431..0a485e7cd 100644 --- a/pkg/specgen/generate/oci.go +++ b/pkg/specgen/generate/oci.go @@ -52,10 +52,14 @@ func addRlimits(s *specgen.SpecGenerator, g *generate.Generator) error { if err := unix.Getrlimit(unix.RLIMIT_NOFILE, &rlimit); err != nil { logrus.Warnf("failed to return RLIMIT_NOFILE ulimit %q", err) } - current = rlimit.Cur - max = rlimit.Max + if rlimit.Cur < current { + current = rlimit.Cur + } + if rlimit.Max < max { + max = rlimit.Max + } } - g.AddProcessRlimits("RLIMIT_NOFILE", current, max) + g.AddProcessRlimits("RLIMIT_NOFILE", max, current) } if !nprocSet { max := kernelMax @@ -65,10 +69,14 @@ func addRlimits(s *specgen.SpecGenerator, g *generate.Generator) error { if err := unix.Getrlimit(unix.RLIMIT_NPROC, &rlimit); err != nil { logrus.Warnf("failed to return RLIMIT_NPROC ulimit %q", err) } - current = rlimit.Cur - max = rlimit.Max + if rlimit.Cur < current { + current = rlimit.Cur + } + if rlimit.Max < max { + max = rlimit.Max + } } - g.AddProcessRlimits("RLIMIT_NPROC", current, max) + g.AddProcessRlimits("RLIMIT_NPROC", max, current) } return nil -- cgit v1.2.3-54-g00ecf From a67526d9f2c5ed352f625f51b93c40e66320fe8f Mon Sep 17 00:00:00 2001 From: Matthew Heon Date: Mon, 6 Jul 2020 14:25:39 -0400 Subject: Update release notes for v2.0.2 Signed-off-by: Matthew Heon --- RELEASE_NOTES.md | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index 44b64f977..c843bd091 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -1,5 +1,24 @@ # Release Notes +## 2.0.2 +### Bugfixes +- Fixed a bug where the `podman ps` command would not truncate long container commands, resulting in display issues as the column could become extremely wide (the `--no-trunc` flag can be used to print the full command). +- Fixed a bug where `podman pod` commands operationg on multiple containers (e.g. `podman pod stop` and `podman pod kill`) would not print errors from individual containers, but only a warning that some containers had failed. +- Fixed a bug where the `podman system service` command would panic if a connection to the Events endpoint hung up early ([#6805](https://github.com/containers/libpod/issues/6805)). +- Fixed a bug where rootless Podman would create anonymous and named volumes with the wrong owner for containers run with the `--user` directive. +- Fixed a bug where the `TMPDIR` environment variable was not being defaulted (if unset) to `/var/tmp`. +- Fixed a bug where the `--publish` flag to `podman create` and `podman run` required that a host port be specified if an IP address was given ([#6806](https://github.com/containers/libpod/issues/6806)). +- Fixed a bug where in `podman-remote` commands performing an attach (`podman run`, `podman attach`, `podman start --attach`, `podman exec`) did not properly configure the terminal on Windows. +- Fixed a bug where the `--remote` flag to Podman required an argument, despite being a boolean ([#6704](https://github.com/containers/libpod/issues/6704)). +- Fixed a bug where the `podman generate systemd --new` command could generate incorrect unit files for a pod if a container in the pod was created using the `--pod=...` flag (with an =, instead of a space, before the pod ID) ([#6766](https://github.com/containers/libpod/issues/6766)). +- Fixed a bug where `NPROC` and `NOFILE` rlimits could be improperly set for rootless Podman containers + +### API +- Fixed a bug where the timestamp format for Libpod image list endpoint was incorrect - the format has been switched to Unix time. +- Fixed a bug where the compatability Create endpoint did not handle empty entrypoints properly. +- Fixed a bug where the compatibility network remove endpoint would improperly handle errors where the network was not found +- Fixed a bug where containers would be created with improper permissions because of a umask issue ([#6787](https://github.com/containers/libpod/issues/6787)) + ## 2.0.1 ### Changes - The `podman system connection` command was mistakenly omitted from the 2.0 release, and has been included here. -- cgit v1.2.3-54-g00ecf From 2fb9bb20df49f665dbc7420a80dc2da57530278f Mon Sep 17 00:00:00 2001 From: Matthew Heon Date: Mon, 6 Jul 2020 14:44:40 -0400 Subject: Fix imports to ensure v2 is used with libpod Signed-off-by: Matthew Heon --- cmd/podman/validate/latest.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/podman/validate/latest.go b/cmd/podman/validate/latest.go index 6e2aa063b..0ebed7227 100644 --- a/cmd/podman/validate/latest.go +++ b/cmd/podman/validate/latest.go @@ -1,7 +1,7 @@ package validate import ( - "github.com/containers/libpod/cmd/podman/registry" + "github.com/containers/libpod/v2/cmd/podman/registry" "github.com/spf13/cobra" ) -- cgit v1.2.3-54-g00ecf