diff --git a/cmd/podman/common/completion.go b/cmd/podman/common/completion.go index 1689cd2485..75825f1ea1 100644 --- a/cmd/podman/common/completion.go +++ b/cmd/podman/common/completion.go @@ -1436,6 +1436,7 @@ func AutocompleteEventFilter(cmd *cobra.Command, args []string, toComplete strin "pod=": func(s string) ([]string, cobra.ShellCompDirective) { return getPods(cmd, s, completeDefault) }, "volume=": func(s string) ([]string, cobra.ShellCompDirective) { return getVolumes(cmd, s) }, "event=": event, + "label=": nil, "type=": eventTypes, } return completeKeyValues(toComplete, kv) @@ -1644,6 +1645,7 @@ func AutocompletePodPsFilters(cmd *cobra.Command, args []string, toComplete stri return []string{"stopped", "running", "paused", "exited", "dead", "created", "degraded"}, cobra.ShellCompDirectiveNoFileComp }, + "until=": nil, } return completeKeyValues(toComplete, kv) } @@ -1652,12 +1654,19 @@ func AutocompletePodPsFilters(cmd *cobra.Command, args []string, toComplete stri func AutocompleteImageFilters(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { getImg := func(s string) ([]string, cobra.ShellCompDirective) { return getImages(cmd, s) } kv := keyValueCompletion{ - "before=": getImg, - "dangling=": getBoolCompletion, - "label=": nil, - "readonly=": getBoolCompletion, - "reference=": nil, - "since=": getImg, + "after=": getImg, + "before=": getImg, + "containers=": getBoolCompletion, + "dangling=": getBoolCompletion, + "digest=": nil, + "id=": getImg, + "intermediate=": getBoolCompletion, + "label=": nil, + "manifest=": getImg, + "readonly=": getBoolCompletion, + "reference=": nil, + "since=": getImg, + "until=": nil, } return completeKeyValues(toComplete, kv) } @@ -1674,6 +1683,7 @@ func AutocompletePruneFilters(cmd *cobra.Command, args []string, toComplete stri // AutocompleteNetworkFilters - Autocomplete network ls --filter options. func AutocompleteNetworkFilters(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { kv := keyValueCompletion{ + "dangling=": getBoolCompletion, "driver=": func(_ string) ([]string, cobra.ShellCompDirective) { return []string{types.BridgeNetworkDriver, types.MacVLANNetworkDriver, types.IPVLANNetworkDriver}, cobra.ShellCompDirectiveNoFileComp }, @@ -1690,13 +1700,17 @@ func AutocompleteVolumeFilters(cmd *cobra.Command, args []string, toComplete str local := func(_ string) ([]string, cobra.ShellCompDirective) { return []string{"local"}, cobra.ShellCompDirectiveNoFileComp } + getImg := func(s string) ([]string, cobra.ShellCompDirective) { return getImages(cmd, s) } kv := keyValueCompletion{ + "after=": getImg, "dangling=": getBoolCompletion, "driver=": local, "label=": nil, "name=": func(s string) ([]string, cobra.ShellCompDirective) { return getVolumes(cmd, s) }, "opt=": nil, "scope=": local, + "since=": getImg, + "until=": nil, } return completeKeyValues(toComplete, kv) } diff --git a/docs/source/markdown/podman-events.1.md b/docs/source/markdown/podman-events.1.md index b23b6c430b..abb8761513 100644 --- a/docs/source/markdown/podman-events.1.md +++ b/docs/source/markdown/podman-events.1.md @@ -87,13 +87,16 @@ Setting `events_container_create_inspect_data=true` in containers.conf(5) instru Filter events that are displayed. They must be in the format of "filter=value". The following filters are supported: - * container=name_or_id - * event=event_status (described above) - * image=name_or_id - * label=key=value - * pod=name_or_id - * volume=name_or_id - * type=event_type (described above) + +| **Filter** | **Description** | +|------------|-------------------------------------| +| container | [Name or ID] Container's name or ID | +| event | event_status (described above) | +| image | [Name or ID] Image name or ID | +| label | [key=value] label | +| pod | [Name or ID] Pod name or ID | +| volume | [Name or ID] Volume name or ID | +| type | Event_type (described above) | In the case where an ID is used, the ID may be in its full or shortened form. The "die" event is mapped to "died" for Docker compatibility. diff --git a/docs/source/markdown/podman-pause.1.md.in b/docs/source/markdown/podman-pause.1.md.in index bc26396583..a85827a6a6 100644 --- a/docs/source/markdown/podman-pause.1.md.in +++ b/docs/source/markdown/podman-pause.1.md.in @@ -42,6 +42,7 @@ Valid filters are listed below: | health | [Status] healthy or unhealthy | | pod | [Pod] name or full or partial ID of pod | | network | [Network] name or full ID of network | +| until | [DateTime] container created before the given duration or time. | @@option latest diff --git a/docs/source/markdown/podman-ps.1.md b/docs/source/markdown/podman-ps.1.md index 7ef399a7ff..e2936552a7 100644 --- a/docs/source/markdown/podman-ps.1.md +++ b/docs/source/markdown/podman-ps.1.md @@ -60,6 +60,7 @@ Valid filters are listed below: | health | [Status] healthy or unhealthy | | pod | [Pod] name or full or partial ID of pod | | network | [Network] name or full ID of network | +| until | [DateTime] container created before the given duration or time. | #### **--format**=*format* diff --git a/docs/source/markdown/podman-restart.1.md.in b/docs/source/markdown/podman-restart.1.md.in index 167f5d7359..d05efc5643 100644 --- a/docs/source/markdown/podman-restart.1.md.in +++ b/docs/source/markdown/podman-restart.1.md.in @@ -45,6 +45,7 @@ Valid filters are listed below: | health | [Status] healthy or unhealthy | | pod | [Pod] name or full or partial ID of pod | | network | [Network] name or full ID of network | +| until | [DateTime] Containers created before the given duration or time. | @@option latest diff --git a/docs/source/markdown/podman-rm.1.md.in b/docs/source/markdown/podman-rm.1.md.in index a51a163c2e..3595018358 100644 --- a/docs/source/markdown/podman-rm.1.md.in +++ b/docs/source/markdown/podman-rm.1.md.in @@ -49,6 +49,7 @@ Valid filters are listed below: | health | [Status] healthy or unhealthy | | pod | [Pod] name or full or partial ID of pod | | network | [Network] name or full ID of network | +| until | [DateTime] Containers created before the given duration or time. | #### **--force**, **-f** diff --git a/docs/source/markdown/podman-start.1.md.in b/docs/source/markdown/podman-start.1.md.in index 23d7d5f7cf..fd578815f1 100644 --- a/docs/source/markdown/podman-start.1.md.in +++ b/docs/source/markdown/podman-start.1.md.in @@ -50,6 +50,7 @@ Valid filters are listed below: | health | [Status] healthy or unhealthy | | pod | [Pod] name or full or partial ID of pod | | network | [Network] name or full ID of network | +| until | [DateTime] Containers created before the given duration or time. | @@option interactive diff --git a/docs/source/markdown/podman-stop.1.md.in b/docs/source/markdown/podman-stop.1.md.in index d0c025afe2..0dbb4057db 100644 --- a/docs/source/markdown/podman-stop.1.md.in +++ b/docs/source/markdown/podman-stop.1.md.in @@ -48,6 +48,7 @@ Valid filters are listed below: | health | [Status] healthy or unhealthy | | pod | [Pod] name or full or partial ID of pod | | network | [Network] name or full ID of network | +| until | [DateTime] Containers created before the given duration or time. | @@option ignore diff --git a/docs/source/markdown/podman-unpause.1.md.in b/docs/source/markdown/podman-unpause.1.md.in index 1078a23eb4..b6acb9b457 100644 --- a/docs/source/markdown/podman-unpause.1.md.in +++ b/docs/source/markdown/podman-unpause.1.md.in @@ -42,6 +42,7 @@ Valid filters are listed below: | health | [Status] healthy or unhealthy | | pod | [Pod] name or full or partial ID of pod | | network | [Network] name or full ID of network | +| until | [DateTime] Containers created before the given duration or time. | @@option latest diff --git a/docs/source/markdown/podman-volume-prune.1.md b/docs/source/markdown/podman-volume-prune.1.md index 9242a2b7e3..942e51cca2 100644 --- a/docs/source/markdown/podman-volume-prune.1.md +++ b/docs/source/markdown/podman-volume-prune.1.md @@ -23,11 +23,16 @@ The *filters* argument format is of `key=value`. If there is more than one *filt Supported filters: -| Filter | Description | -| :----------------: | --------------------------------------------------------------------------- | -| *label* | Only remove volumes, with (or without, in the case of label!=[...] is used) the specified labels. | -| *until* | Only remove volumes created before given timestamp. | -| *after/since* | Filter by volumes created after the given VOLUME (name or tag) | +| Filter | Description | +|:-------------:|------------------------------------------------------------------------------------------------------------| +| *dangling* | [Bool] Only remove volumes not referenced by any containers | +| *driver* | [String] Only remove volumes with the given driver | +| *label* | [String] Only remove volumes, with (or without, in the case of label!=[...] is used) the specified labels. | +| *name* | [String] Only remove volume with the given name | +| *opt* | [String] Only remove volumes created with the given options | +| *scope* | [String] Only remove volumes with the given scope | +| *until* | [DateTime] Only remove volumes created before given timestamp. | +| *after/since* | [Volume] Filter by volumes created after the given VOLUME (name or tag) | The `label` *filter* accepts two formats. One is the `label`=*key* or `label`=*key*=*value*, which removes volumes with the specified labels. The other format is the `label!`=*key* or `label!`=*key*=*value*, which removes volumes without the specified labels. diff --git a/hack/xref-helpmsgs-manpages b/hack/xref-helpmsgs-manpages index 781e200b33..2ca861f942 100755 --- a/hack/xref-helpmsgs-manpages +++ b/hack/xref-helpmsgs-manpages @@ -203,6 +203,13 @@ sub xref_by_help { OPTION: for my $k (sort keys %$help) { + if (! ref($man)) { + # Super-unlikely but I've seen it + warn "$ME: 'podman @subcommand' is not documented in man pages!\n"; + ++$Errs; + next OPTION; + } + if (exists $man->{$k}) { if (ref $help->{$k}) { # This happens when 'podman foo --format' offers @@ -233,7 +240,11 @@ sub xref_by_help { # Nope, it's not that case. my $man = $man->{_path} || 'man'; - warn "$ME: 'podman @subcommand --help' lists '$k', which is not in $man\n"; + # The usual case is "podman ... --help"... + my $what = '--help'; + # ...but for *options* (e.g. --filter), we're checking command completion + $what = '' if $subcommand[-1] =~ /^--/; + warn "$ME: 'podman @subcommand $what' lists '$k', which is not in $man\n"; ++$Errs; } } @@ -296,7 +307,10 @@ sub xref_by_man { # Special case for hidden or external commands next if $Skip_Subcommand{$k}; - warn "$ME: 'podman @subcommand': $k in $man, but not --help\n"; + # It's not always --help, sometimes we check completion + my $what = '--help'; + $what = 'command completion' if $subcommand[-1] =~ /^--/; + warn "$ME: 'podman @subcommand': '$k' in $man, but not in $what\n"; ++$Errs; } } @@ -420,7 +434,17 @@ sub podman_help { ++$Errs; } } - } + } + # Same thing, for --filter + elsif ($opt eq '--filter') { + my @completions = _completions(@_, '--filter='); + for my $c (@completions) { + if ($c =~ /^(\S+)=/) { + $help{$opt} = {} if ! ref($help{$opt}); + $help{$opt}{$1} = 1; + } + } + } } } close $fh @@ -447,9 +471,11 @@ sub podman_man { my $previous_subcmd = ''; my $previous_flag = ''; my $previous_format = ''; + my $previous_filter = ''; + LINE: while (my $line = <$fh>) { chomp $line; - next unless $line; # skip empty lines + next LINE unless $line; # skip empty lines # First line (page title) must match the command name. if ($line =~ /^%\s+/) { @@ -561,7 +587,7 @@ sub podman_man { } # Options with no '=whatever' - next if !$line; + next LINE if !$line; # Anything remaining *must* be of the form '=' if ($line !~ /^=/) { @@ -639,6 +665,56 @@ sub podman_man { $previous_format = $format; } } + # Same as above, but with --filter + elsif ($previous_flag eq '--filter') { + if ($line =~ /^\|\s+(\S+)\s+\|/) { + my $filter = $1; + + # Special-case transformation: some man pages use + # asterisks, "*foo*", some don't. Ignore asterisks. + $filter =~ tr/\*//d; + + # (Garbage: these are just table column titles & dividers) + next LINE if $filter eq 'Filter'; + next LINE if $filter =~ /---+/; + + # Another special case: treat slash-separated options + # ("after/since") as identical, and require that each + # be documented. + for my $f (split '/', $filter) { + # Special case for negated options ("label!="): allow, + # but only immediately after the positive case. + if ($f =~ s/!$//) { + if ($f ne $previous_filter) { + warn "$ME: $subpath:$.: filter '$f!' only allowed immediately after its positive\n"; + ++$Errs; + } + next LINE; + } + + if (! ref($man{$previous_flag})) { + $man{$previous_flag} = { _path => $subpath }; + } + $man{$previous_flag}{$f} = 1; + } + + # Sort order check, case-insensitive + # FIXME FIXME! Disabled for now because it would make + # this PR completely impossible to review (as opposed to + # only mostly-impossible) + #if (lc($filter) lt lc($previous_filter)) { + # warn "$ME: $subpath:$.: filter specifier '$filter' should precede '$previous_filter'\n"; + # ++$Errs; + #} + + # Dup check. Yes, it happens. + if (lc($filter) eq lc($previous_filter)) { + warn "$ME: $subpath:$.: filter specifier '$filter' is a dup\n"; + ++$Errs; + } + $previous_filter = $filter; + } + } } # It's easy to make mistakes in the SEE ALSO elements.