summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--README.md2
-rw-r--r--cmd/podman/common/createparse.go2
-rw-r--r--cmd/podman/common/specgen.go2
-rw-r--r--cmd/podman/containers/diff.go9
-rw-r--r--cmd/podman/images/list.go74
-rw-r--r--cmd/podman/parse/json.go3
-rw-r--r--cmd/podman/parse/json_test.go25
-rw-r--r--cmd/podman/report/format.go68
-rw-r--r--cmd/podman/report/format_test.go35
-rw-r--r--completions/bash/podman2
-rwxr-xr-xcontrib/cirrus/logformatter105
-rwxr-xr-xcontrib/cirrus/logformatter.t10
-rw-r--r--go.mod2
-rw-r--r--go.sum4
-rw-r--r--libpod/container_internal_linux.go40
-rw-r--r--libpod/container_log_linux.go2
-rw-r--r--libpod/image/prune.go50
-rw-r--r--pkg/domain/infra/abi/images.go6
-rw-r--r--pkg/rootless/rootless.go25
-rw-r--r--pkg/specgen/generate/oci.go15
-rw-r--r--test/e2e/info_test.go10
-rw-r--r--test/e2e/logs_test.go1
-rw-r--r--test/e2e/run_test.go24
-rw-r--r--test/e2e/version_test.go11
-rw-r--r--test/system/035-logs.bats11
-rw-r--r--test/system/500-networking.bats2
-rw-r--r--vendor/github.com/containers/common/pkg/completion/completion.go69
-rw-r--r--vendor/github.com/containers/common/version/version.go2
-rw-r--r--vendor/modules.txt2
29 files changed, 496 insertions, 117 deletions
diff --git a/README.md b/README.md
index 8c2631b81..5192d877f 100644
--- a/README.md
+++ b/README.md
@@ -5,7 +5,7 @@
Podman (the POD MANager) is a tool for managing containers and images, volumes mounted into those containers, and pods made from groups of containers.
Podman is based on libpod, a library for container lifecycle management that is also contained in this repository. The libpod library provides APIs for managing containers, pods, container images, and volumes.
-* [Latest Version: 2.1.0](https://github.com/containers/podman/releases/latest)
+* [Latest Version: 2.1.1](https://github.com/containers/podman/releases/latest)
* Latest Remote client for Windows
* Latest Remote client for MacOs
* Latest Static Remote client for Linux
diff --git a/cmd/podman/common/createparse.go b/cmd/podman/common/createparse.go
index 059f9050f..09ee5aa0c 100644
--- a/cmd/podman/common/createparse.go
+++ b/cmd/podman/common/createparse.go
@@ -10,7 +10,7 @@ import (
func (c *ContainerCLIOpts) validate() error {
var ()
if c.Rm && c.Restart != "" && c.Restart != "no" {
- return errors.Errorf("the --rm option conflicts with --restart")
+ return errors.Errorf(`the --rm option conflicts with --restart, when the restartPolicy is not "" and "no"`)
}
if _, err := util.ValidatePullType(c.Pull); err != nil {
diff --git a/cmd/podman/common/specgen.go b/cmd/podman/common/specgen.go
index e7b88eb3f..84ae70b6a 100644
--- a/cmd/podman/common/specgen.go
+++ b/cmd/podman/common/specgen.go
@@ -233,7 +233,7 @@ func FillOutSpecGen(s *specgen.SpecGenerator, c *ContainerCLIOpts, args []string
// validate flags as needed
if err := c.validate(); err != nil {
- return nil
+ return err
}
s.User = c.User
diff --git a/cmd/podman/containers/diff.go b/cmd/podman/containers/diff.go
index 227c13f4c..a3ca6edf9 100644
--- a/cmd/podman/containers/diff.go
+++ b/cmd/podman/containers/diff.go
@@ -1,6 +1,7 @@
package containers
import (
+ "github.com/containers/podman/v2/cmd/podman/parse"
"github.com/containers/podman/v2/cmd/podman/registry"
"github.com/containers/podman/v2/cmd/podman/report"
"github.com/containers/podman/v2/cmd/podman/validate"
@@ -52,11 +53,11 @@ func diff(cmd *cobra.Command, args []string) error {
return err
}
- switch diffOpts.Format {
- case "":
- return report.ChangesToTable(results)
- case "json":
+ switch {
+ case parse.MatchesJSONFormat(diffOpts.Format):
return report.ChangesToJSON(results)
+ case diffOpts.Format == "":
+ return report.ChangesToTable(results)
default:
return errors.New("only supported value for '--format' is 'json'")
}
diff --git a/cmd/podman/images/list.go b/cmd/podman/images/list.go
index ffb341fc4..239da9d28 100644
--- a/cmd/podman/images/list.go
+++ b/cmd/podman/images/list.go
@@ -11,7 +11,9 @@ import (
"unicode"
"github.com/containers/image/v5/docker/reference"
+ "github.com/containers/podman/v2/cmd/podman/parse"
"github.com/containers/podman/v2/cmd/podman/registry"
+ "github.com/containers/podman/v2/cmd/podman/report"
"github.com/containers/podman/v2/pkg/domain/entities"
"github.com/docker/go-units"
"github.com/pkg/errors"
@@ -106,9 +108,12 @@ func images(cmd *cobra.Command, args []string) error {
switch {
case listFlag.quiet:
return writeID(imgs)
- case cmd.Flag("format").Changed && listFlag.format == "json":
+ case parse.MatchesJSONFormat(listFlag.format):
return writeJSON(imgs)
default:
+ if cmd.Flag("format").Changed {
+ listFlag.noHeading = true // V1 compatibility
+ }
return writeTemplate(imgs)
}
}
@@ -156,25 +161,29 @@ func writeJSON(images []imageReporter) error {
}
func writeTemplate(imgs []imageReporter) error {
- var (
- hdr, row string
- )
- if len(listFlag.format) < 1 {
- hdr, row = imageListFormat(listFlag)
+ hdrs := report.Headers(imageReporter{}, map[string]string{
+ "ID": "IMAGE ID",
+ "ReadOnly": "R/O",
+ })
+
+ var row string
+ if listFlag.format == "" {
+ row = lsFormatFromFlags(listFlag)
} else {
- row = listFlag.format
- if !strings.HasSuffix(row, "\n") {
- row += "\n"
- }
+ row = report.NormalizeFormat(listFlag.format)
}
- format := hdr + "{{range . }}" + row + "{{end}}"
- tmpl, err := template.New("list").Parse(format)
- if err != nil {
- return err
- }
- tmpl = template.Must(tmpl, nil)
+
+ format := "{{range . }}" + row + "{{end}}"
+ tmpl := template.Must(template.New("list").Parse(format))
w := tabwriter.NewWriter(os.Stdout, 8, 2, 2, ' ', 0)
defer w.Flush()
+
+ if !listFlag.noHeading {
+ if err := tmpl.Execute(w, hdrs); err != nil {
+ return err
+ }
+ }
+
return tmpl.Execute(w, imgs)
}
@@ -276,40 +285,27 @@ func sortFunc(key string, data []imageReporter) func(i, j int) bool {
}
}
-func imageListFormat(flags listFlagType) (string, string) {
- // Defaults
- hdr := "REPOSITORY\tTAG"
- row := "{{.Repository}}\t{{if .Tag}}{{.Tag}}{{else}}<none>{{end}}"
+func lsFormatFromFlags(flags listFlagType) string {
+ row := []string{
+ "{{if .Repository}}{{.Repository}}{{else}}<none>{{end}}",
+ "{{if .Tag}}{{.Tag}}{{else}}<none>{{end}}",
+ }
if flags.digests {
- hdr += "\tDIGEST"
- row += "\t{{.Digest}}"
+ row = append(row, "{{.Digest}}")
}
- hdr += "\tIMAGE ID"
- row += "\t{{.ID}}"
-
- hdr += "\tCREATED\tSIZE"
- row += "\t{{.Created}}\t{{.Size}}"
+ row = append(row, "{{.ID}}", "{{.Created}}", "{{.Size}}")
if flags.history {
- hdr += "\tHISTORY"
- row += "\t{{if .History}}{{.History}}{{else}}<none>{{end}}"
+ row = append(row, "{{if .History}}{{.History}}{{else}}<none>{{end}}")
}
if flags.readOnly {
- hdr += "\tReadOnly"
- row += "\t{{.ReadOnly}}"
- }
-
- if flags.noHeading {
- hdr = ""
- } else {
- hdr += "\n"
+ row = append(row, "{{.ReadOnly}}")
}
- row += "\n"
- return hdr, row
+ return strings.Join(row, "\t") + "\n"
}
type imageReporter struct {
diff --git a/cmd/podman/parse/json.go b/cmd/podman/parse/json.go
index 95a6633b8..40ac415db 100644
--- a/cmd/podman/parse/json.go
+++ b/cmd/podman/parse/json.go
@@ -2,8 +2,9 @@ package parse
import "regexp"
-var jsonFormatRegex = regexp.MustCompile(`^(\s*json\s*|\s*{{\s*json\s*\.\s*}}\s*)$`)
+var jsonFormatRegex = regexp.MustCompile(`^\s*(json|{{\s*json\s*( \.)?\s*}})\s*$`)
+// MatchesJSONFormat test CLI --format string to be a JSON request
func MatchesJSONFormat(s string) bool {
return jsonFormatRegex.Match([]byte(s))
}
diff --git a/cmd/podman/parse/json_test.go b/cmd/podman/parse/json_test.go
index 5cad185fd..ec3b5664b 100644
--- a/cmd/podman/parse/json_test.go
+++ b/cmd/podman/parse/json_test.go
@@ -1,6 +1,8 @@
package parse
import (
+ "fmt"
+ "strings"
"testing"
"github.com/stretchr/testify/assert"
@@ -13,18 +15,31 @@ func TestMatchesJSONFormat(t *testing.T) {
}{
{"json", true},
{" json", true},
- {"json ", true},
+ {" json ", true},
{" json ", true},
+ {"{{json}}", true},
+ {"{{json }}", true},
{"{{json .}}", true},
{"{{ json .}}", true},
- {"{{json . }}", true},
- {" {{ json . }} ", true},
- {"{{json }}", false},
- {"{{json .", false},
+ {"{{ json . }}", true},
+ {" {{ json . }} ", true},
+ {"{{ json .", false},
{"json . }}", false},
+ {"{{.ID }} json .", false},
+ {"json .", false},
+ {"{{json.}}", false},
}
for _, tt := range tests {
assert.Equal(t, tt.expected, MatchesJSONFormat(tt.input))
}
+
+ for _, tc := range tests {
+ tc := tc
+ label := "MatchesJSONFormat/" + strings.ReplaceAll(tc.input, " ", "_")
+ t.Run(label, func(t *testing.T) {
+ t.Parallel()
+ assert.Equal(t, tc.expected, MatchesJSONFormat(tc.input), fmt.Sprintf("Scanning %q failed", tc.input))
+ })
+ }
}
diff --git a/cmd/podman/report/format.go b/cmd/podman/report/format.go
new file mode 100644
index 000000000..32d92bec5
--- /dev/null
+++ b/cmd/podman/report/format.go
@@ -0,0 +1,68 @@
+package report
+
+import (
+ "reflect"
+ "strings"
+)
+
+// tableReplacer will remove 'table ' prefix and clean up tabs
+var tableReplacer = strings.NewReplacer(
+ "table ", "",
+ `\t`, "\t",
+ `\n`, "\n",
+ " ", "\t",
+)
+
+// escapedReplacer will clean up escaped characters from CLI
+var escapedReplacer = strings.NewReplacer(
+ `\t`, "\t",
+ `\n`, "\n",
+)
+
+// NormalizeFormat reads given go template format provided by CLI and munges it into what we need
+func NormalizeFormat(format string) string {
+ f := format
+ // two replacers used so we only remove the prefix keyword `table`
+ if strings.HasPrefix(f, "table ") {
+ f = tableReplacer.Replace(f)
+ } else {
+ f = escapedReplacer.Replace(format)
+ }
+
+ if !strings.HasSuffix(f, "\n") {
+ f += "\n"
+ }
+
+ return f
+}
+
+// Headers queries the interface for field names
+func Headers(object interface{}, overrides map[string]string) []map[string]string {
+ value := reflect.ValueOf(object)
+ if value.Kind() == reflect.Ptr {
+ value = value.Elem()
+ }
+
+ // Column header will be field name upper-cased.
+ headers := make(map[string]string, value.NumField())
+ for i := 0; i < value.Type().NumField(); i++ {
+ field := value.Type().Field(i)
+ // Recurse to find field names from promoted structs
+ if field.Type.Kind() == reflect.Struct && field.Anonymous {
+ h := Headers(reflect.New(field.Type).Interface(), nil)
+ for k, v := range h[0] {
+ headers[k] = v
+ }
+ continue
+ }
+ headers[field.Name] = strings.ToUpper(field.Name)
+ }
+
+ if len(overrides) > 0 {
+ // Override column header as provided
+ for k, v := range overrides {
+ headers[k] = strings.ToUpper(v)
+ }
+ }
+ return []map[string]string{headers}
+}
diff --git a/cmd/podman/report/format_test.go b/cmd/podman/report/format_test.go
new file mode 100644
index 000000000..7dd62e899
--- /dev/null
+++ b/cmd/podman/report/format_test.go
@@ -0,0 +1,35 @@
+package report
+
+import (
+ "strings"
+ "testing"
+)
+
+func TestNormalizeFormat(t *testing.T) {
+ cases := []struct {
+ format string
+ expected string
+ }{
+ {"table {{.ID}}", "{{.ID}}\n"},
+ {"table {{.ID}} {{.C}}", "{{.ID}}\t{{.C}}\n"},
+ {"{{.ID}}", "{{.ID}}\n"},
+ {"{{.ID}}\n", "{{.ID}}\n"},
+ {"{{.ID}} {{.C}}", "{{.ID}} {{.C}}\n"},
+ {"\t{{.ID}}", "\t{{.ID}}\n"},
+ {`\t` + "{{.ID}}", "\t{{.ID}}\n"},
+ {"table {{.ID}}\t{{.C}}", "{{.ID}}\t{{.C}}\n"},
+ {"{{.ID}} table {{.C}}", "{{.ID}} table {{.C}}\n"},
+ }
+ for _, tc := range cases {
+ tc := tc
+
+ label := strings.ReplaceAll(tc.format, " ", "<sp>")
+ t.Run("NormalizeFormat/"+label, func(t *testing.T) {
+ t.Parallel()
+ actual := NormalizeFormat(tc.format)
+ if actual != tc.expected {
+ t.Errorf("Expected %q, actual %q", tc.expected, actual)
+ }
+ })
+ }
+}
diff --git a/completions/bash/podman b/completions/bash/podman
index a83cfc790..e12862126 100644
--- a/completions/bash/podman
+++ b/completions/bash/podman
@@ -407,7 +407,7 @@ __podman_local_interfaces() {
__podman_complete_restart() {
case "$prev" in
--restart)
- COMPREPLY=( $( compgen -W "always no on-failure" -- "$cur") )
+ COMPREPLY=( $( compgen -W "always no on-failure unless-stopped" -- "$cur") )
return
;;
esac
diff --git a/contrib/cirrus/logformatter b/contrib/cirrus/logformatter
index f97638b6f..2ab8aa117 100755
--- a/contrib/cirrus/logformatter
+++ b/contrib/cirrus/logformatter
@@ -31,6 +31,12 @@ our $CSS = <<'END_CSS';
/* wrap long lines - don't require user to scroll right */
pre { line-break: normal; overflow-wrap: normal; white-space: pre-wrap; }
+/* synopsis table at top */
+table.synopsis { border: none; border-collapse: collapse; margin-left: 2em; margin-top: 2ex; }
+.synopsis th { font-weight: normal; font-size: 110%; text-align: right; }
+.synopsis td { font-weight: bold; font-size: 120%; font-family: monospace; }
+
+/* test results */
.boring { color: #999; }
.timestamp { color: #999; }
.log-debug { color: #999; }
@@ -171,9 +177,20 @@ window.addEventListener("load", scrollToBottom, false);
</script>
</head>
<body>
-<pre> <!-- begin processed output -->
END_HTML
+ # Synopsis of this job: show job environment, links to PR and Cirrus
+ print { $out_fh } "<h2>Synopsis</h2>\n<hr/>\n",
+ job_synopsis($test_name), "<hr/>\n";
+
+ # FOR DEBUGGING: dump environment, but in HTML comments to not clutter
+ print { $out_fh } "<!-- Environment: -->\n";
+ for my $e (sort keys %ENV) {
+ my $val = escapeHTML($ENV{$e});
+ $val =~ s/--/-&#x002D;/g; # double dash not valid in comments
+ printf { $out_fh } "<!-- %-20s %s -->\n", $e, $val;
+ }
+
# State variables
my $previous_timestamp = ''; # timestamp of previous line
my $cirrus_task; # Cirrus task number, used for linking
@@ -185,6 +202,8 @@ END_HTML
my $looks_like_bats; # binary flag: for detecting BATS results
my %bats_count; # For summary line: count of pass/fail/skip
+ print { $out_fh } "<pre> <!-- begin processed output -->\n";
+
# Main loop: read input, one line at a time, and write out reformatted
LINE:
while (my $line = <STDIN>) {
@@ -232,8 +251,9 @@ END_HTML
my $css;
# Readability: /long/path/to/podman -> podman (hover for full path)
- $line =~ s{^(#\s+(#|\$)\s+)(\S+/)(podman\S*)\s}
- {$1<span title="$3$4">$4</span> };
+ # Also make it boldface, to make commands stand out
+ $line =~ s{^(#\s+(#|\$)\s+)(\S+/)(podman\S*)(\s.*)}
+ {$1<b><span title="$3$4">$4</span>$5</b>};
if ($line =~ /^ok\s.*\s# skip/) { $css = 'skipped' }
elsif ($line =~ /^ok\s/) { $css = 'passed' }
@@ -470,6 +490,83 @@ sub make_id {
}
+###############################################################################
+# BEGIN job_synopsis and related helpers
+
+##################
+# job_synopsis # Job details, links to github/cirrus
+##################
+sub job_synopsis {
+ my $subtest_name = shift; # e.g. integration_test
+
+ my $s = <<"END_SYNOPSIS";
+<table class="synopsis">
+END_SYNOPSIS
+
+ # PR 1234 - title of the pr
+ my $pr_title = escapeHTML(_env_replace("{CIRRUS_CHANGE_TITLE}"));
+ $s .= _tr("Github PR", sprintf("%s - %s",
+ _a("{CIRRUS_PR}", "https://{CIRRUS_REPO_CLONE_HOST}/{CIRRUS_REPO_FULL_NAME}/pull/{CIRRUS_PR}"),
+ $pr_title));
+
+ # PR author, if signed-off-by
+ if (my $msg = _env_replace("{CIRRUS_COMMIT_MESSAGE}")) {
+ while ($msg =~ /^Signed-off-by:\s+(\S.*\S)$/gmi) {
+ $s .= _tr("Author", escapeHTML($1));
+ }
+ }
+
+ # eg "test fedora", "special_testing_rootless"
+ my $test_name = _env_replace("{CIRRUS_TASK_NAME}");
+ if (my $rcli = $ENV{RCLI}) {
+ $test_name .= " [remote]" if $rcli eq 'true';
+ }
+ else {
+ $test_name .= " [no RCLI; cannot determine remote/local]";
+ }
+ $s .= _tr("Test name", $test_name);
+
+ # Subtest, e.g. system_test
+ $s .= _tr("Subtest", $subtest_name);
+
+ # Link to further Cirrus results, e.g. other runs.
+ # Build is mostly boring, it's usually TASK that we want to see.
+ $s .= _tr("Cirrus Build ID", "<small>" . _a("{CIRRUS_BUILD_ID}", "https://cirrus-ci.com/build/{CIRRUS_BUILD_ID}") . "</small>");
+ $s .= _tr("Cirrus <b>Task</b> ID", _a("{CIRRUS_TASK_ID}", "https://cirrus-ci.com/task/{CIRRUS_TASK_ID}"));
+
+ # "none", "rootless"
+ $s .= _tr("Special mode", _env_replace("{SPECIALMODE}"));
+
+ $s .= "</table>\n";
+ return $s;
+}
+
+
+sub _tr {
+ my ($th, $td) = @_;
+ return "<tr><th>$th:</th><td>$td</td></tr>\n";
+}
+
+sub _a {
+ my ($name, $href) = map { _env_replace($_) } @_;
+
+ if ($href =~ /UNDEFINED/) {
+ return "$name ($href)";
+ }
+ return "<a href='$href'>$name</a>";
+}
+
+sub _env_replace {
+ my $s_in = shift;
+
+ $s_in =~ s[\{(.*?)\}][$ENV{$1} || "[$1 UNDEFINED]"]ge;
+
+ return $s_in;
+}
+
+# END job_synopsis and related helpers
+###############################################################################
+# BEGIN html-formatting helpers
sub escapeHTML {
my $s = shift;
@@ -492,5 +589,7 @@ sub unescapeHTML {
return $s;
}
+# END html-formatting helpers
+###############################################################################
1;
diff --git a/contrib/cirrus/logformatter.t b/contrib/cirrus/logformatter.t
index 2075bff96..bd4179b5e 100755
--- a/contrib/cirrus/logformatter.t
+++ b/contrib/cirrus/logformatter.t
@@ -96,7 +96,7 @@ ok 4 blah
<span class='bats-passed'><a name='t--00001'>ok 1 hi</a></span>
<span class='bats-skipped'><a name='t--00002'>ok 2 bye # skip no reason</a></span>
<span class='bats-failed'><a name='t--00003'>not ok 3 fail</a></span>
-<span class='bats-log'># $ <span title="/path/to/podman">podman</span> foo -bar</span>
+<span class='bats-log'># $ <b><span title="/path/to/podman">podman</span> foo -bar</b></span>
<span class='bats-log-esm'># #| FAIL: exit code is 123; expected 321</span>
<span class='bats-passed'><a name='t--00004'>ok 4 blah</a></span>
<hr/><span class='bats-summary'>Summary: <span class='bats-passed'>2 Passed</span>, <span class='bats-failed'>1 Failed</span>, <span class='bats-skipped'>1 Skipped</span>. Total tests: 4</span>
@@ -147,11 +147,11 @@ $SCRIPT_BASE/integration_test.sh |&amp; ${TIMESTAMP}
<pre>
<span class="timestamp">[+0103s] </span>Podman pod restart
<span class="timestamp"> </span><a name='t--podman-pod-restart-single-empty-pod--1'><h2> podman pod restart single empty pod</h2></a>
-<span class="timestamp"> </span> /var/tmp/go/src/github.com<a class="codelink" href='https://github.com/containers/podman/blob/40f5d8b1becd381c4e8283ed3940d09193e4fe06/test/e2e/pod_restart_test.go#L41'>/containers/libpod/test/e2e/pod_restart_test.go:41</a>
+<span class="timestamp"> </span> /var/tmp/go/src/github.com<a class="codelink" href='https://github.com/containers/podman/blob/40f5d8b1becd381c4e8283ed3940d09193e4fe06/test/e2e/pod_restart_test.go#L41'>/containers/podman/test/e2e/pod_restart_test.go:41</a>
<span class="timestamp"> </span>[BeforeEach] Podman pod restart
-<span class="timestamp"> </span> /var/tmp/go/src/github.com<a class="codelink" href='https://github.com/containers/podman/blob/40f5d8b1becd381c4e8283ed3940d09193e4fe06/test/e2e/pod_restart_test.go#L18'>/containers/libpod/test/e2e/pod_restart_test.go:18</a>
+<span class="timestamp"> </span> /var/tmp/go/src/github.com<a class="codelink" href='https://github.com/containers/podman/blob/40f5d8b1becd381c4e8283ed3940d09193e4fe06/test/e2e/pod_restart_test.go#L18'>/containers/podman/test/e2e/pod_restart_test.go:18</a>
<span class="timestamp"> </span>[It] podman pod restart single empty pod
-<span class="timestamp"> </span> /var/tmp/go/src/github.com<a class="codelink" href='https://github.com/containers/podman/blob/40f5d8b1becd381c4e8283ed3940d09193e4fe06/test/e2e/pod_restart_test.go#L41'>/containers/libpod/test/e2e/pod_restart_test.go:41</a>
+<span class="timestamp"> </span> /var/tmp/go/src/github.com<a class="codelink" href='https://github.com/containers/podman/blob/40f5d8b1becd381c4e8283ed3940d09193e4fe06/test/e2e/pod_restart_test.go#L41'>/containers/podman/test/e2e/pod_restart_test.go:41</a>
<span class="timestamp"> </span>Running: <span title="/var/tmp/go/src/github.com/containers/podman/bin/podman"><b>podman</b></span> <span class="boring" title="--storage-opt vfs.imagestore=/tmp/podman/imagecachedir
--root /tmp/podman_test553496330/crio
--runroot /tmp/podman_test553496330/crio-run
@@ -176,7 +176,7 @@ $SCRIPT_BASE/integration_test.sh |&amp; ${TIMESTAMP}
<span class="timestamp"> </span><span class='log-warn'>Error: no containers in pod 4810be0cfbd42241e349dbe7d50fbc54405cd320a6637c65fd5323f34d64af89 have no dependencies, cannot start pod: no such container</span>
<span class="timestamp"> </span>output:
<span class="timestamp"> </span>[AfterEach] Podman pod restart
-<span class="timestamp"> </span> /var/tmp/go/src/github.com<a class="codelink" href='https://github.com/containers/podman/blob/40f5d8b1becd381c4e8283ed3940d09193e4fe06/test/e2e/pod_restart_test.go#L28'>/containers/libpod/test/e2e/pod_restart_test.go:28</a>
+<span class="timestamp"> </span> /var/tmp/go/src/github.com<a class="codelink" href='https://github.com/containers/podman/blob/40f5d8b1becd381c4e8283ed3940d09193e4fe06/test/e2e/pod_restart_test.go#L28'>/containers/podman/test/e2e/pod_restart_test.go:28</a>
<span class="timestamp"> </span>Running: <span title="/var/tmp/go/src/github.com/containers/podman/bin/podman"><b>podman</b></span> <span class="boring" title="--storage-opt vfs.imagestore=/tmp/podman/imagecachedir
--root /tmp/podman_test553496330/crio
--runroot /tmp/podman_test553496330/crio-run
diff --git a/go.mod b/go.mod
index 7b8957cda..63dd84feb 100644
--- a/go.mod
+++ b/go.mod
@@ -12,7 +12,7 @@ require (
github.com/containernetworking/cni v0.8.0
github.com/containernetworking/plugins v0.8.7
github.com/containers/buildah v1.16.4
- github.com/containers/common v0.23.0
+ github.com/containers/common v0.24.0
github.com/containers/conmon v2.0.20+incompatible
github.com/containers/image/v5 v5.6.0
github.com/containers/psgo v1.5.1
diff --git a/go.sum b/go.sum
index 2baae4843..219ec20df 100644
--- a/go.sum
+++ b/go.sum
@@ -73,8 +73,8 @@ github.com/containernetworking/plugins v0.8.7/go.mod h1:R7lXeZaBzpfqapcAbHRW8/CY
github.com/containers/buildah v1.16.4 h1:bxthp2FoGcpc2O/RyvbGUAZoefmc5hRBqWQi3BjRu7w=
github.com/containers/buildah v1.16.4/go.mod h1:i1XqXgpCROnfcq4oNtfrFEk7UzNDxLJ/PZ+CnPyoIq8=
github.com/containers/common v0.21.0/go.mod h1:8w8SVwc+P2p1MOnRMbSKNWXt1Iwd2bKFu2LLZx55DTM=
-github.com/containers/common v0.23.0 h1:+g4mI3wUYSzOtoWU9TNVoV4K52/aN6JEz0qs1YdPEe8=
-github.com/containers/common v0.23.0/go.mod h1:E56/N0beWGf+lrrJX32atuo2hkjzHwSC8n1vCG+TAR0=
+github.com/containers/common v0.24.0 h1:5C03ROzmRvZCyooNJVkZ4Q8T2d04g+VVyPMQ428XC4Y=
+github.com/containers/common v0.24.0/go.mod h1:BFRo6uRh1TbkZgR2oYTILxc2BNZTBtBffa9xtu881QI=
github.com/containers/conmon v2.0.20+incompatible h1:YbCVSFSCqFjjVwHTPINGdMX1F6JXHGTUje2ZYobNrkg=
github.com/containers/conmon v2.0.20+incompatible/go.mod h1:hgwZ2mtuDrppv78a/cOBNiCm6O0UMWGx1mu7P00nu5I=
github.com/containers/image/v5 v5.5.2/go.mod h1:4PyNYR0nwlGq/ybVJD9hWlhmIsNra4Q8uOQX2s6E2uM=
diff --git a/libpod/container_internal_linux.go b/libpod/container_internal_linux.go
index eba732d2a..514cdaee1 100644
--- a/libpod/container_internal_linux.go
+++ b/libpod/container_internal_linux.go
@@ -7,6 +7,7 @@ import (
"fmt"
"io"
"io/ioutil"
+ "math"
"net"
"os"
"os/user"
@@ -35,6 +36,7 @@ import (
"github.com/containers/podman/v2/pkg/util"
"github.com/containers/podman/v2/utils"
"github.com/containers/storage/pkg/archive"
+ "github.com/containers/storage/pkg/idtools"
securejoin "github.com/cyphar/filepath-securejoin"
runcuser "github.com/opencontainers/runc/libcontainer/user"
spec "github.com/opencontainers/runtime-spec/specs-go"
@@ -416,9 +418,43 @@ func (c *Container) generateSpec(ctx context.Context) (*spec.Spec, error) {
// Look up and add groups the user belongs to, if a group wasn't directly specified
if !strings.Contains(c.config.User, ":") {
+ // the gidMappings that are present inside the container user namespace
+ var gidMappings []idtools.IDMap
+
+ switch {
+ case len(c.config.IDMappings.GIDMap) > 0:
+ gidMappings = c.config.IDMappings.GIDMap
+ case rootless.IsRootless():
+ // Check whether the current user namespace has enough gids available.
+ availableGids, err := rootless.GetAvailableGids()
+ if err != nil {
+ return nil, errors.Wrapf(err, "cannot read number of available GIDs")
+ }
+ gidMappings = []idtools.IDMap{{
+ ContainerID: 0,
+ HostID: 0,
+ Size: int(availableGids),
+ }}
+ default:
+ gidMappings = []idtools.IDMap{{
+ ContainerID: 0,
+ HostID: 0,
+ Size: math.MaxInt32,
+ }}
+ }
for _, gid := range execUser.Sgids {
- // FIXME: We need to add a flag to containers.conf to not add these for HPC Users.
- g.AddProcessAdditionalGid(uint32(gid))
+ isGidAvailable := false
+ for _, m := range gidMappings {
+ if gid >= m.ContainerID && gid < m.ContainerID+m.Size {
+ isGidAvailable = true
+ break
+ }
+ }
+ if isGidAvailable {
+ g.AddProcessAdditionalGid(uint32(gid))
+ } else {
+ logrus.Warnf("additional gid=%d is not present in the user namespace, skip setting it", gid)
+ }
}
}
diff --git a/libpod/container_log_linux.go b/libpod/container_log_linux.go
index 73c2df76e..d895171cf 100644
--- a/libpod/container_log_linux.go
+++ b/libpod/container_log_linux.go
@@ -33,7 +33,7 @@ const (
func (c *Container) readFromJournal(ctx context.Context, options *logs.LogOptions, logChannel chan *logs.LogLine) error {
var config journal.JournalReaderConfig
if options.Tail < 0 {
- config.NumFromTail = math.MaxUint64
+ config.NumFromTail = 0
} else {
config.NumFromTail = uint64(options.Tail)
}
diff --git a/libpod/image/prune.go b/libpod/image/prune.go
index fcc65fb03..b38265a7e 100644
--- a/libpod/image/prune.go
+++ b/libpod/image/prune.go
@@ -125,29 +125,39 @@ func (ir *Runtime) PruneImages(ctx context.Context, all bool, filter []string) (
filterFuncs = append(filterFuncs, generatedFunc)
}
- pruneImages, err := ir.GetPruneImages(ctx, all, filterFuncs)
- if err != nil {
- return nil, errors.Wrap(err, "unable to get images to prune")
- }
- prunedCids := make([]string, 0, len(pruneImages))
- for _, p := range pruneImages {
- repotags, err := p.RepoTags()
+ pruned := []string{}
+ prev := 0
+ for {
+ toPrune, err := ir.GetPruneImages(ctx, all, filterFuncs)
if err != nil {
- return nil, err
+ return nil, errors.Wrap(err, "unable to get images to prune")
}
- if err := p.Remove(ctx, true); err != nil {
- if errors.Cause(err) == storage.ErrImageUsedByContainer {
- logrus.Warnf("Failed to prune image %s as it is in use: %v.\nA container associated with containers/storage i.e. Buildah, CRI-O, etc., maybe associated with this image.\nUsing the rmi command with the --force option will remove the container and image, but may cause failures for other dependent systems.", p.ID(), err)
- continue
- }
- return nil, errors.Wrap(err, "failed to prune image")
+ numImages := len(toPrune)
+ if numImages == 0 || numImages == prev {
+ // If there's nothing left to do, return.
+ break
}
- defer p.newImageEvent(events.Prune)
- nameOrID := p.ID()
- if len(repotags) > 0 {
- nameOrID = repotags[0]
+ prev = numImages
+ for _, img := range toPrune {
+ repotags, err := img.RepoTags()
+ if err != nil {
+ return nil, err
+ }
+ if err := img.Remove(ctx, false); err != nil {
+ if errors.Cause(err) == storage.ErrImageUsedByContainer {
+ logrus.Warnf("Failed to prune image %s as it is in use: %v.\nA container associated with containers/storage (e.g., Buildah, CRI-O, etc.) maybe associated with this image.\nUsing the rmi command with the --force option will remove the container and image, but may cause failures for other dependent systems.", img.ID(), err)
+ continue
+ }
+ return nil, errors.Wrap(err, "failed to prune image")
+ }
+ defer img.newImageEvent(events.Prune)
+ nameOrID := img.ID()
+ if len(repotags) > 0 {
+ nameOrID = repotags[0]
+ }
+ pruned = append(pruned, nameOrID)
}
- prunedCids = append(prunedCids, nameOrID)
+
}
- return prunedCids, nil
+ return pruned, nil
}
diff --git a/pkg/domain/infra/abi/images.go b/pkg/domain/infra/abi/images.go
index d56dc7d94..965c63bec 100644
--- a/pkg/domain/infra/abi/images.go
+++ b/pkg/domain/infra/abi/images.go
@@ -46,11 +46,7 @@ func (ir *ImageEngine) Exists(_ context.Context, nameOrID string) (*entities.Boo
}
func (ir *ImageEngine) Prune(ctx context.Context, opts entities.ImagePruneOptions) (*entities.ImagePruneReport, error) {
- return ir.pruneImagesHelper(ctx, opts.All, opts.Filter)
-}
-
-func (ir *ImageEngine) pruneImagesHelper(ctx context.Context, all bool, filters []string) (*entities.ImagePruneReport, error) {
- results, err := ir.Libpod.ImageRuntime().PruneImages(ctx, all, filters)
+ results, err := ir.Libpod.ImageRuntime().PruneImages(ctx, opts.All, opts.Filter)
if err != nil {
return nil, err
}
diff --git a/pkg/rootless/rootless.go b/pkg/rootless/rootless.go
index d02721ea9..799c793d8 100644
--- a/pkg/rootless/rootless.go
+++ b/pkg/rootless/rootless.go
@@ -2,8 +2,10 @@ package rootless
import (
"os"
+ "sync"
"github.com/containers/storage"
+ "github.com/opencontainers/runc/libcontainer/user"
"github.com/pkg/errors"
)
@@ -46,3 +48,26 @@ func TryJoinPauseProcess(pausePidPath string) (bool, int, error) {
}
return became, ret, err
}
+
+var (
+ availableGids int64
+ availableGidsErr error
+ availableGidsOnce sync.Once
+)
+
+// GetAvailableGids returns how many GIDs are available in the
+// current user namespace.
+func GetAvailableGids() (int64, error) {
+ availableGidsOnce.Do(func() {
+ idMap, err := user.ParseIDMapFile("/proc/self/gid_map")
+ if err != nil {
+ availableGidsErr = err
+ return
+ }
+ availableGids = int64(0)
+ for _, r := range idMap {
+ availableGids += r.Count
+ }
+ })
+ return availableGids, availableGidsErr
+}
diff --git a/pkg/specgen/generate/oci.go b/pkg/specgen/generate/oci.go
index b57ddf1aa..f02432f5b 100644
--- a/pkg/specgen/generate/oci.go
+++ b/pkg/specgen/generate/oci.go
@@ -10,7 +10,6 @@ import (
"github.com/containers/podman/v2/libpod/image"
"github.com/containers/podman/v2/pkg/rootless"
"github.com/containers/podman/v2/pkg/specgen"
- "github.com/opencontainers/runc/libcontainer/user"
spec "github.com/opencontainers/runtime-spec/specs-go"
"github.com/opencontainers/runtime-tools/generate"
"github.com/pkg/errors"
@@ -200,7 +199,7 @@ func SpecGenToOCI(ctx context.Context, s *specgen.SpecGenerator, rt *libpod.Runt
}
gid5Available := true
if isRootless {
- nGids, err := GetAvailableGids()
+ nGids, err := rootless.GetAvailableGids()
if err != nil {
return nil, err
}
@@ -360,15 +359,3 @@ func SpecGenToOCI(ctx context.Context, s *specgen.SpecGenerator, rt *libpod.Runt
return configSpec, nil
}
-
-func GetAvailableGids() (int64, error) {
- idMap, err := user.ParseIDMapFile("/proc/self/gid_map")
- if err != nil {
- return 0, err
- }
- count := int64(0)
- for _, r := range idMap {
- count += r.Count
- }
- return count, nil
-}
diff --git a/test/e2e/info_test.go b/test/e2e/info_test.go
index bcbfdd80a..49f5f0ce6 100644
--- a/test/e2e/info_test.go
+++ b/test/e2e/info_test.go
@@ -50,15 +50,17 @@ var _ = Describe("Podman Info", func() {
{"{{ json .}}", true, 0},
{"{{json . }}", true, 0},
{" {{ json . }} ", true, 0},
- {"{{json }}", false, 125},
+ {"{{json }}", true, 0},
{"{{json .", false, 125},
- {"json . }}", false, 0}, // Note: this does NOT fail but produces garbage
+ {"json . }}", false, 0}, // without opening {{ template seen as string literal
}
for _, tt := range tests {
session := podmanTest.Podman([]string{"info", "--format", tt.input})
session.WaitWithDefaultTimeout()
- Expect(session).Should(Exit(tt.exitCode))
- Expect(session.IsJSONOutputValid()).To(Equal(tt.success))
+
+ desc := fmt.Sprintf("JSON test(%q)", tt.input)
+ Expect(session).Should(Exit(tt.exitCode), desc)
+ Expect(session.IsJSONOutputValid()).To(Equal(tt.success), desc)
}
})
diff --git a/test/e2e/logs_test.go b/test/e2e/logs_test.go
index 3aa3cf409..67ab71d20 100644
--- a/test/e2e/logs_test.go
+++ b/test/e2e/logs_test.go
@@ -203,6 +203,7 @@ var _ = Describe("Podman logs", func() {
results.WaitWithDefaultTimeout()
Expect(results).To(Exit(0))
Expect(len(results.OutputToStringArray())).To(Equal(3))
+ Expect(results.OutputToString()).To(Equal("podman podman podman"))
})
It("using journald tail two lines", func() {
diff --git a/test/e2e/run_test.go b/test/e2e/run_test.go
index 292df529c..05aede122 100644
--- a/test/e2e/run_test.go
+++ b/test/e2e/run_test.go
@@ -67,6 +67,30 @@ var _ = Describe("Podman run", func() {
Expect(session.ExitCode()).To(Equal(0))
})
+ It("podman run --rm with --restart", func() {
+ session := podmanTest.Podman([]string{"run", "--rm", "--restart", "", ALPINE})
+ session.WaitWithDefaultTimeout()
+ Expect(session.ExitCode()).To(Equal(0))
+
+ session = podmanTest.Podman([]string{"run", "--rm", "--restart", "no", ALPINE})
+ session.WaitWithDefaultTimeout()
+ Expect(session.ExitCode()).To(Equal(0))
+
+ // the --rm option conflicts with --restart, when the restartPolicy is not "" and "no"
+ // so the exitCode should not equal 0
+ session = podmanTest.Podman([]string{"run", "--rm", "--restart", "on-failure", ALPINE})
+ session.WaitWithDefaultTimeout()
+ Expect(session.ExitCode()).To(Not(Equal(0)))
+
+ session = podmanTest.Podman([]string{"run", "--rm", "--restart", "always", ALPINE})
+ session.WaitWithDefaultTimeout()
+ Expect(session.ExitCode()).To(Not(Equal(0)))
+
+ session = podmanTest.Podman([]string{"run", "--rm", "--restart", "unless-stopped", ALPINE})
+ session.WaitWithDefaultTimeout()
+ Expect(session.ExitCode()).To(Not(Equal(0)))
+ })
+
It("podman run a container based on on a short name with localhost", func() {
tag := podmanTest.Podman([]string{"tag", nginx, "localhost/libpod/alpine_nginx:latest"})
tag.WaitWithDefaultTimeout()
diff --git a/test/e2e/version_test.go b/test/e2e/version_test.go
index 695cccc11..903748b58 100644
--- a/test/e2e/version_test.go
+++ b/test/e2e/version_test.go
@@ -1,6 +1,7 @@
package integration
import (
+ "fmt"
"os"
. "github.com/containers/podman/v2/test/utils"
@@ -68,15 +69,17 @@ var _ = Describe("Podman version", func() {
{"{{ json .}}", true, 0},
{"{{json . }}", true, 0},
{" {{ json . }} ", true, 0},
- {"{{json }}", false, 125},
+ {"{{json }}", true, 0},
{"{{json .", false, 125},
- {"json . }}", false, 0}, // Note: this does NOT fail but produces garbage
+ {"json . }}", false, 0}, // without opening {{ template seen as string literal
}
for _, tt := range tests {
session := podmanTest.Podman([]string{"version", "--format", tt.input})
session.WaitWithDefaultTimeout()
- Expect(session).Should(Exit(tt.exitCode))
- Expect(session.IsJSONOutputValid()).To(Equal(tt.success))
+
+ desc := fmt.Sprintf("JSON test(%q)", tt.input)
+ Expect(session).Should(Exit(tt.exitCode), desc)
+ Expect(session.IsJSONOutputValid()).To(Equal(tt.success), desc)
}
})
diff --git a/test/system/035-logs.bats b/test/system/035-logs.bats
index cbb2091e5..130bc5243 100644
--- a/test/system/035-logs.bats
+++ b/test/system/035-logs.bats
@@ -50,4 +50,15 @@ ${cid[1]} c
${cid[0]} d" "Sequential output from logs"
}
+@test "podman logs over journald" {
+ msg=$(random_string 20)
+
+ run_podman run --name myctr --log-driver journald $IMAGE echo $msg
+
+ run_podman logs myctr
+ is "$output" "$msg" "check that log output equals the container output"
+
+ run_podman rm myctr
+}
+
# vim: filetype=sh
diff --git a/test/system/500-networking.bats b/test/system/500-networking.bats
index 150626ded..a923402ac 100644
--- a/test/system/500-networking.bats
+++ b/test/system/500-networking.bats
@@ -82,6 +82,8 @@ load helpers
# "network create" now works rootless, with the help of a special container
@test "podman network create" {
+ skip_if_remote "FIXME: pending #7808"
+
local mynetname=testnet-$(random_string 10)
local mysubnet=$(random_rfc1918_subnet)
diff --git a/vendor/github.com/containers/common/pkg/completion/completion.go b/vendor/github.com/containers/common/pkg/completion/completion.go
index 6e7ddff30..07451e992 100644
--- a/vendor/github.com/containers/common/pkg/completion/completion.go
+++ b/vendor/github.com/containers/common/pkg/completion/completion.go
@@ -1,6 +1,14 @@
package completion
-import "github.com/spf13/cobra"
+import (
+ "bufio"
+ "os"
+ "strings"
+ "unicode"
+
+ "github.com/containers/common/pkg/capabilities"
+ "github.com/spf13/cobra"
+)
// FlagCompletions - hold flag completion functions to be applied later with CompleteCommandFlags()
type FlagCompletions map[string]func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective)
@@ -24,3 +32,62 @@ func AutocompleteNone(cmd *cobra.Command, args []string, toComplete string) ([]s
func AutocompleteDefault(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
return nil, cobra.ShellCompDirectiveDefault
}
+
+// AutocompleteCapabilities - Autocomplete linux capabilities options.
+// Used by --cap-add and --cap-drop.
+func AutocompleteCapabilities(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
+ caps := capabilities.AllCapabilities()
+
+ // convertCase will convert a string to lowercase only if the user input is lowercase
+ convertCase := func(s string) string { return s }
+ if len(toComplete) > 0 && unicode.IsLower(rune(toComplete[0])) {
+ convertCase = strings.ToLower
+ }
+
+ // offset is used to trim "CAP_" if the user doesn't type CA... or ca...
+ offset := 0
+ if !strings.HasPrefix(toComplete, convertCase("CA")) {
+ // setting the offset to 4 is safe since each cap starts with CAP_
+ offset = 4
+ }
+
+ var completions []string
+ for _, cap := range caps {
+ completions = append(completions, convertCase(cap)[offset:])
+ }
+
+ // add ALL here which is also a valid argument
+ completions = append(completions, convertCase(capabilities.All))
+ return completions, cobra.ShellCompDirectiveNoFileComp
+}
+
+// autocompleteSubIDName - autocomplete the names in /etc/subuid or /etc/subgid
+func autocompleteSubIDName(filename string) ([]string, cobra.ShellCompDirective) {
+ file, err := os.Open(filename)
+ if err != nil {
+ return nil, cobra.ShellCompDirectiveError
+ }
+ defer file.Close()
+
+ var names []string
+ scanner := bufio.NewScanner(file)
+ for scanner.Scan() {
+ name := strings.SplitN(scanner.Text(), ":", 2)[0]
+ names = append(names, name)
+ }
+ if err = scanner.Err(); err != nil {
+ return nil, cobra.ShellCompDirectiveError
+ }
+
+ return names, cobra.ShellCompDirectiveNoFileComp
+}
+
+// AutocompleteSubgidName - Autocomplete subgidname based on the names in the /etc/subgid file.
+func AutocompleteSubgidName(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
+ return autocompleteSubIDName("/etc/subgid")
+}
+
+// AutocompleteSubuidName - Autocomplete subuidname based on the names in the /etc/subuid file.
+func AutocompleteSubuidName(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
+ return autocompleteSubIDName("/etc/subuid")
+}
diff --git a/vendor/github.com/containers/common/version/version.go b/vendor/github.com/containers/common/version/version.go
index eaa728791..8e69c7daf 100644
--- a/vendor/github.com/containers/common/version/version.go
+++ b/vendor/github.com/containers/common/version/version.go
@@ -1,4 +1,4 @@
package version
// Version is the version of the build.
-const Version = "0.23.0"
+const Version = "0.24.0"
diff --git a/vendor/modules.txt b/vendor/modules.txt
index 0a7e6ad44..e1c2b6300 100644
--- a/vendor/modules.txt
+++ b/vendor/modules.txt
@@ -87,7 +87,7 @@ github.com/containers/buildah/pkg/secrets
github.com/containers/buildah/pkg/supplemented
github.com/containers/buildah/pkg/umask
github.com/containers/buildah/util
-# github.com/containers/common v0.23.0
+# github.com/containers/common v0.24.0
github.com/containers/common/pkg/apparmor
github.com/containers/common/pkg/apparmor/internal/supported
github.com/containers/common/pkg/auth