diff options
42 files changed, 800 insertions, 177 deletions
diff --git a/cmd/podman/generate/systemd.go b/cmd/podman/generate/systemd.go index 173b3656b..a363b7e94 100644 --- a/cmd/podman/generate/systemd.go +++ b/cmd/podman/generate/systemd.go @@ -23,6 +23,7 @@ const ( stopTimeoutFlagName = "stop-timeout" stopTimeoutCompatFlagName = "time" restartPolicyFlagName = "restart-policy" + restartSecFlagName = "restart-sec" newFlagName = "new" ) @@ -30,6 +31,7 @@ var ( files bool format string systemdRestart string + systemdRestartSec uint startTimeout uint stopTimeout uint systemdOptions = entities.GenerateSystemdOptions{} @@ -88,6 +90,9 @@ func init() { flags.StringVar(&systemdRestart, restartPolicyFlagName, systemDefine.DefaultRestartPolicy, "Systemd restart-policy") _ = systemdCmd.RegisterFlagCompletionFunc(restartPolicyFlagName, common.AutocompleteSystemdRestartOptions) + flags.UintVarP(&systemdRestartSec, restartSecFlagName, "", 0, "Systemd restart-sec") + _ = systemdCmd.RegisterFlagCompletionFunc(restartSecFlagName, completion.AutocompleteNone) + formatFlagName := "format" flags.StringVar(&format, formatFlagName, "", "Print the created units in specified format (json)") _ = systemdCmd.RegisterFlagCompletionFunc(formatFlagName, common.AutocompleteFormat(nil)) @@ -111,6 +116,9 @@ func systemd(cmd *cobra.Command, args []string) error { systemdOptions.New = true } + if cmd.Flags().Changed(restartSecFlagName) { + systemdOptions.RestartSec = &systemdRestartSec + } if cmd.Flags().Changed(startTimeoutFlagName) { systemdOptions.StartTimeout = &startTimeout } diff --git a/contrib/cirrus/logformatter b/contrib/cirrus/logformatter index 5156f9f8a..70f119b5b 100755 --- a/contrib/cirrus/logformatter +++ b/contrib/cirrus/logformatter @@ -37,12 +37,15 @@ table.synopsis { border: none; border-collapse: collapse; margin-left: 2em; marg .synopsis td { font-weight: bold; font-size: 120%; font-family: monospace; } /* test results */ +.testname { font-size: 125%; color: #444; } .boring { color: #999; } .timestamp { color: #999; } .log-debug { color: #999; } .log-info { color: #333; } .log-warn { color: #f60; } .log-error { color: #900; font-weight: bold; } +.log-skip { color: #F90; } +.log-slow { background: #FF0; color: #000; font-weight: bold; } .subtest { background: #eee; } .subsubtest { color: #F39; font-weight: bold; } .string { color: #00c; } @@ -205,11 +208,17 @@ END_HTML print { $out_fh } "<pre> <!-- begin processed output -->\n"; + # Assume rootful prompt, check for rootless (here and in log itself, below) + my $Prompt = '#'; + $Prompt = '$' if $test_name =~ /rootless/; + # Main loop: read input, one line at a time, and write out reformatted LINE: while (my $line = <STDIN>) { print $line; # Immediately dump back to stdout + $Prompt = '$' if $line =~ /Runner executing .* as rootless /; + # Remain robust in face of errors: always write stdout even if no HTML next LINE if ! $out_fh; @@ -328,7 +337,7 @@ END_HTML next LINE; } # (bindings test sometimes emits 'Running' with leading bullet char) - elsif ($line =~ /^•?Running:/) { + elsif ($line =~ s!^•?Running:!<span class="boring">$Prompt</span>!) { # Highlight the important (non-boilerplate) podman command. $line =~ s/\s+--remote\s+/ /g; # --remote takes no args # Strip out the global podman options, but show them on hover @@ -365,19 +374,27 @@ END_HTML # an anchor so we can link to it later. if ($after_divider++ == 2) { # Sigh. There is no actual marker. Assume that anything with - ## two leading spaces then alpha (not slashes) is a test name. - if ($line =~ /^ [a-zA-Z]/) { + ## two leading spaces then alpha or hyphen (not slashes) is + ## a test name. + if ($line =~ /^ [a-zA-Z-]/) { my $id = make_id($line, 'anchor'); $line = "<a name='t--$id'><h2>$line</h2></a>"; } } + # Make SKIPPING and SLOW TEST visible + $line =~ s!(\[SKIPPING\].*)!<span class="log-skip">$1</span>!; + $line =~ s!(\[SLOW TEST.*\])!<span class="log-slow">$1</span>!; + + # Highlight test name when it appears in the middle of commands. + # But make it boring, because we already have the test name in large + # bold just above. (Except in skipped tests). + $line =~ s!^(\s*)(\[It\]\s+.*)!$1<span class="testname">$2</span>!; + # Failure name corresponds to a previously-seen block. - ## FIXME: sometimes there are three failures with the same name. - ## ...I have no idea why or how to link to the right ones. - # 1 2 2 3 3 14 4 - if ($line =~ /^(\[(Fail|Panic!)\] .* \[(It|BeforeEach)\] )([A-Za-z].*)/) { + # 1 2 2 3 3 14 4 + if ($line =~ /^(\[(Fail|Panic!)\] .* \[(It|BeforeEach)\] )([A-Za-z-].*)/) { my ($lhs, $type, $ginkgo_fluff, $testname) = ($1, $2, $3, $4); my $id = make_id($testname, 'link'); @@ -486,7 +503,10 @@ sub make_id { state %counter; $name =~ s/^\s+|\s+$//g; # strip leading/trailing whitespace + $name =~ s/\&#\d+;//g; # 'doesn't' -> 'doesnt' + $name =~ s/\"/-/g; # '"path"' -> '-path-' $name =~ s/[^a-zA-Z0-9_-]/-/g; # Convert non-alphanumeric to dash + $name =~ s/-{3,}/-/g; # '------' to just '-' # Keep a running tally of how many times we've seen this identifier # for this given type! This lets us cross-match, in the bottom of diff --git a/contrib/cirrus/logformatter.t b/contrib/cirrus/logformatter.t index bd4179b5e..d905693ad 100755 --- a/contrib/cirrus/logformatter.t +++ b/contrib/cirrus/logformatter.t @@ -134,9 +134,35 @@ $SCRIPT_BASE/integration_test.sh |& ${TIMESTAMP} [+0103s] 4810be0cfbd42241e349dbe7d50fbc54405cd320a6637c65fd5323f34d64af89 [+0104s] Running: /var/tmp/go/src/github.com/containers/libpod/bin/podman-remote --storage-opt vfs.imagestore=/tmp/podman/imagecachedir --root /tmp/podman_test553496330/crio --runroot /tmp/podman_test553496330/crio-run --runtime /usr/bin/runc --conmon /usr/bin/conmon --cni-config-dir /etc/cni/net.d --cgroup-manager systemd --tmpdir /tmp/podman_test553496330 --events-backend file --storage-driver vfs --remote --url unix:/run/user/12345/podman-xyz.sock pod rm -fa [+0104s] 4810be0cfbd42241e349dbe7d50fbc54405cd320a6637c65fd5323f34d64af89 again + + [+0107s] • -[+0107s] ------------------------------ -[+0107s] podman system reset +[+0523s] ------------------------------ +[+0523s] Podman play kube with build +[+0523s] --build should override image in store +[+0523s] /var/tmp/go/src/github.com/containers/podman/test/e2e/play_build_test.go:215 + + +[+0479s] • +[+0479s] ------------------------------ +[+0479s] Podman pod rm +[+0479s] podman pod rm -a doesn't remove a running container +[+0479s] /var/tmp/go/src/github.com/containers/podman/test/e2e/pod_rm_test.go:119 + + +[+1405s] • +[+1405s] ------------------------------ +[+1405s] Podman run entrypoint +[+1405s] podman run entrypoint == [""] +[+1405s] /var/tmp/go/src/github.com/containers/podman/test/e2e/run_entrypoint_test.go:47 + +[+0184s] S [SKIPPING] [3.086 seconds] +[+1385s] S [SKIPPING] in Spec Setup (BeforeEach) [0.001 seconds] + +[+1512s] Summarizing 6 Failures: +[+1512s] +[+1512s] [Fail] Podman play kube with build [It] --build should override image in store +[+1512s] /var/tmp/go/src/github.com/containers/podman/test/e2e/play_build_test.go:259 >>> $SCRIPT_BASE/integration_test.sh |& ${TIMESTAMP} [08:26:19] START - All [+xxxx] lines that follow are relative to right now. @@ -150,9 +176,9 @@ $SCRIPT_BASE/integration_test.sh |& ${TIMESTAMP} <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/podman/test/e2e/pod_restart_test.go:18</a> -<span class="timestamp"> </span>[It] podman pod restart single empty pod +<span class="timestamp"> </span><span class="testname">[It] podman pod restart single empty pod</span> <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 +<span class="timestamp"> </span><span class="boring">#</span> <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 --runtime /usr/bin/runc @@ -163,7 +189,7 @@ $SCRIPT_BASE/integration_test.sh |& ${TIMESTAMP} --events-backend file --storage-driver vfs">[options]</span><b> pod create --infra=false --share</b> <span class="timestamp"> </span>4810be0cfbd42241e349dbe7d50fbc54405cd320a6637c65fd5323f34d64af89 -<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 +<span class="timestamp"> </span><span class="boring">#</span> <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 --runtime /usr/bin/runc @@ -177,7 +203,7 @@ $SCRIPT_BASE/integration_test.sh |& ${TIMESTAMP} <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/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 +<span class="timestamp"> </span><span class="boring">#</span> <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 --runtime /usr/bin/runc @@ -189,7 +215,7 @@ $SCRIPT_BASE/integration_test.sh |& ${TIMESTAMP} --storage-driver vfs">[options]</span><b> pod rm -fa</b> <span class="timestamp"> </span>4810be0cfbd42241e349dbe7d50fbc54405cd320a6637c65fd5323f34d64af89 -<span class="timestamp">[+0104s] </span>Running: <span title="/var/tmp/go/src/github.com/containers/libpod/bin/podman-remote"><b>podman-remote</b></span> <span class="boring" title="--storage-opt vfs.imagestore=/tmp/podman/imagecachedir +<span class="timestamp">[+0104s] </span><span class="boring">#</span> <span title="/var/tmp/go/src/github.com/containers/libpod/bin/podman-remote"><b>podman-remote</b></span> <span class="boring" title="--storage-opt vfs.imagestore=/tmp/podman/imagecachedir --root /tmp/podman_test553496330/crio --runroot /tmp/podman_test553496330/crio-run --runtime /usr/bin/runc @@ -207,4 +233,34 @@ $SCRIPT_BASE/integration_test.sh |& ${TIMESTAMP} </pre> <hr /> <pre> -<span class="timestamp">[+0107s] </span>podman system reset +<span class="timestamp">[+0523s] </span>Podman play kube with build +<span class="timestamp"> </span><a name='t----build-should-override-image-in-store--1'><h2> --build should override image in store</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/play_build_test.go#L215'>/containers/podman/test/e2e/play_build_test.go:215</a> + + +<span class="timestamp">[+0479s] </span>• +</pre> +<hr /> +<pre> +<span class="timestamp">[+0479s] </span>Podman pod rm +<span class="timestamp"> </span><a name='t--podman-pod-rm--a-doesnt-remove-a-running-container--1'><h2> podman pod rm -a doesn't remove a running container</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_rm_test.go#L119'>/containers/podman/test/e2e/pod_rm_test.go:119</a> + + +<span class="timestamp">[+1405s] </span>• +</pre> +<hr /> +<pre> +<span class="timestamp">[+1405s] </span>Podman run entrypoint +<span class="timestamp"> </span><a name='t--podman-run-entrypoint---1'><h2> podman run entrypoint == [""]</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/run_entrypoint_test.go#L47'>/containers/podman/test/e2e/run_entrypoint_test.go:47</a> + + +<span class="timestamp">[+0184s] </span>S <span class="log-skip">[SKIPPING] [3.086 seconds]</span> +<span class="timestamp">[+1385s] </span>S <span class="log-skip">[SKIPPING] in Spec Setup (BeforeEach) [0.001 seconds]</span> + + +<span class="timestamp">[+1512s] </span>Summarizing 6 Failures: +[+1512s] +<span class="timestamp"> </span><b>[Fail] Podman play kube with build [It] <a href='#t----build-should-override-image-in-store--1'>--build should override image in store</a></b> +<span class="timestamp"> </span>/var/tmp/go/src/github.com<a class="codelink" href='https://github.com/containers/podman/blob/40f5d8b1becd381c4e8283ed3940d09193e4fe06/test/e2e/play_build_test.go#L259'>/containers/podman/test/e2e/play_build_test.go:259</a> diff --git a/docs/source/markdown/podman-generate-systemd.1.md b/docs/source/markdown/podman-generate-systemd.1.md index bdcaa8ef1..363d042ae 100644 --- a/docs/source/markdown/podman-generate-systemd.1.md +++ b/docs/source/markdown/podman-generate-systemd.1.md @@ -51,6 +51,11 @@ Override the default stop timeout for the container with the given value in seco Set the systemd restart policy. The restart-policy must be one of: "no", "on-success", "on-failure", "on-abnormal", "on-watchdog", "on-abort", or "always". The default policy is *on-failure*. +#### **--restart-sec**=*time* + +Set the systemd service restartsec value. Configures the time to sleep before restarting a service (as configured with restart-policy). +Takes a value in seconds. + #### **--container-prefix**=*prefix* Set the systemd unit name prefix for containers. The default is *container*. @@ -12,12 +12,12 @@ require ( github.com/containernetworking/cni v1.0.1 github.com/containernetworking/plugins v1.0.1 github.com/containers/buildah v1.23.1 - github.com/containers/common v0.46.1-0.20211125160015-ccf46abecd91 + github.com/containers/common v0.46.1-0.20211202172647-e77d74bd1976 github.com/containers/conmon v2.0.20+incompatible github.com/containers/image/v5 v5.17.1-0.20211129144953-4f6d0b45be6c github.com/containers/ocicrypt v1.1.2 github.com/containers/psgo v1.7.1 - github.com/containers/storage v1.37.1-0.20211122214631-59ba58582415 + github.com/containers/storage v1.37.1-0.20211130181259-1a158c89a518 github.com/coreos/go-systemd/v22 v22.3.2 github.com/coreos/stream-metadata-go v0.0.0-20210225230131-70edb9eb47b3 github.com/cyphar/filepath-securejoin v0.2.3 @@ -46,7 +46,7 @@ require ( github.com/onsi/ginkgo v1.16.5 github.com/onsi/gomega v1.17.0 github.com/opencontainers/go-digest v1.0.0 - github.com/opencontainers/image-spec v1.0.2-0.20210819154149-5ad6f50d6283 + github.com/opencontainers/image-spec v1.0.2-0.20211123152302-43a7dee1ec31 github.com/opencontainers/runc v1.0.2 github.com/opencontainers/runtime-spec v1.0.3-0.20210326190908-1c3f411f0417 github.com/opencontainers/runtime-tools v0.9.1-0.20211020193359-09d837bf40a7 @@ -262,8 +262,8 @@ github.com/containernetworking/plugins v1.0.1/go.mod h1:QHCfGpaTwYTbbH+nZXKVTxNB github.com/containers/buildah v1.23.1 h1:Tpc9DsRuU+0Oofewpxb6OJVNQjCu7yloN/obUqzfDTY= github.com/containers/buildah v1.23.1/go.mod h1:4WnrN0yrA7ab0ppgunixu2WM1rlD2rG8QLJAKbEkZlQ= github.com/containers/common v0.44.2/go.mod h1:7sdP4vmI5Bm6FPFxb3lvAh1Iktb6tiO1MzjUzhxdoGo= -github.com/containers/common v0.46.1-0.20211125160015-ccf46abecd91 h1:h9SrSLSQkvluH/sEJ8X1rlBqCoGJtLvSOu4OGK0Qtuw= -github.com/containers/common v0.46.1-0.20211125160015-ccf46abecd91/go.mod h1:PHwsa3UBgbvn2/MwpTQvyHXvVpuwfBrlDBx3GpIRPDQ= +github.com/containers/common v0.46.1-0.20211202172647-e77d74bd1976 h1:xVOGL69ge+1RirZvnrEl9nATL75udt/Hy2BN8qcmeNY= +github.com/containers/common v0.46.1-0.20211202172647-e77d74bd1976/go.mod h1:J8MxXan58zAWbNgpj4ODPlzsuJnYvNc2zKJCZPIMHYQ= 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.16.0/go.mod h1:XgTpfAPLRGOd1XYyCU5cISFr777bLmOerCSpt/v7+Q4= @@ -284,8 +284,8 @@ github.com/containers/storage v1.35.0/go.mod h1:qzYhasQP2/V9D9XdO+vRwkHBhsBO0ozn github.com/containers/storage v1.36.0/go.mod h1:vbd3SKVQNHdmU5qQI6hTEcKPxnZkGqydG4f6uwrI5a8= github.com/containers/storage v1.37.0/go.mod h1:kqeJeS0b7DO2ZT1nVWs0XufrmPFbgV3c+Q/45RlH6r4= github.com/containers/storage v1.37.1-0.20211119174841-bf170b3ddac0/go.mod h1:XjCNlt5JUUmRuTJXhFxHb9hHGPho7DNg3o4N/14prdQ= -github.com/containers/storage v1.37.1-0.20211122214631-59ba58582415 h1:iqTDpYOxZibrkC7Mo7gCXuJDIT7wyIU432RTmRhlZqY= -github.com/containers/storage v1.37.1-0.20211122214631-59ba58582415/go.mod h1:hvKpaiPRALDI7oz4Jx+AEch8iS/viRnc22HPilQROWU= +github.com/containers/storage v1.37.1-0.20211130181259-1a158c89a518 h1:p44O35V8XCefRxOxU1aY6eT9XNMxkWA1drtJpsl211c= +github.com/containers/storage v1.37.1-0.20211130181259-1a158c89a518/go.mod h1:T5DX08T/eKKRs0WGDhC/ztngMSth6YuHq15eF8C/Y5A= github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= github.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= @@ -592,8 +592,9 @@ github.com/ishidawataru/sctp v0.0.0-20210226210310-f2269e66cdee h1:PAXLXk1heNZ5y github.com/ishidawataru/sctp v0.0.0-20210226210310-f2269e66cdee/go.mod h1:co9pwDoBCm1kGxawmb4sPq0cSIOOWNPT4KnHotMP1Zg= github.com/j-keck/arping v0.0.0-20160618110441-2cf9dc699c56/go.mod h1:ymszkNOg6tORTn+6F6j+Jc8TOr5osrynvN6ivFWZ2GA= github.com/j-keck/arping v1.0.2/go.mod h1:aJbELhR92bSk7tp79AWM/ftfc90EfEi2bQJrbBFOsPw= -github.com/jinzhu/copier v0.3.2 h1:QdBOCbaouLDYaIPFfi1bKv5F5tPpeTwXe4sD0jqtz5w= github.com/jinzhu/copier v0.3.2/go.mod h1:24xnZezI2Yqac9J61UC6/dG/k76ttpq0DdJI3QmUvro= +github.com/jinzhu/copier v0.3.4 h1:mfU6jI9PtCeUjkjQ322dlff9ELjGDu975C2p/nrubVI= +github.com/jinzhu/copier v0.3.4/go.mod h1:DfbEm0FYsaqBcKcFuvmOZb218JkPGtvSHsKg8S8hyyg= github.com/jmespath/go-jmespath v0.0.0-20160202185014-0b12d6b521d8/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= github.com/jmespath/go-jmespath v0.0.0-20160803190731-bd40a432e4c7/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= github.com/joefitzgerald/rainbow-reporter v0.1.0/go.mod h1:481CNgqmVHQZzdIbN52CupLJyoVwB10FQ/IQlF1pdL8= @@ -693,8 +694,8 @@ github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0Qu github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= -github.com/mitchellh/mapstructure v1.4.2 h1:6h7AQ0yhTcIsmFmnAwQls75jp2Gzs4iB8W7pjMO+rqo= -github.com/mitchellh/mapstructure v1.4.2/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +github.com/mitchellh/mapstructure v1.4.3 h1:OVowDSCllw/YjdLkam3/sm7wEtOy59d8ndGgCcyj8cs= +github.com/mitchellh/mapstructure v1.4.3/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/mitchellh/osext v0.0.0-20151018003038-5e2d6d41470f/go.mod h1:OkQIRizQZAeMln+1tSwduZz7+Af5oFlKirV/MSYes2A= github.com/moby/locker v1.0.1/go.mod h1:S7SDdo5zpBK84bzzVlKr2V0hz+7x9hWbYC/kq7oQppc= github.com/moby/spdystream v0.2.0/go.mod h1:f7i0iNDQJ059oMTcWxx8MA/zKFIuD/lY+0GqbN2Wy8c= @@ -761,8 +762,9 @@ github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8 github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= github.com/opencontainers/image-spec v1.0.0/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= github.com/opencontainers/image-spec v1.0.1/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= -github.com/opencontainers/image-spec v1.0.2-0.20210819154149-5ad6f50d6283 h1:TVzvdjOalkJBNkbpPVMAr4KV9QRf2IjfxdyxwAK78Gs= github.com/opencontainers/image-spec v1.0.2-0.20210819154149-5ad6f50d6283/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= +github.com/opencontainers/image-spec v1.0.2-0.20211123152302-43a7dee1ec31 h1:Wh4aR2I6JFwySre9m3iHJYuMnvUFE/HT6qAXozRWi/E= +github.com/opencontainers/image-spec v1.0.2-0.20211123152302-43a7dee1ec31/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= github.com/opencontainers/runc v0.0.0-20190115041553-12f6a991201f/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U= github.com/opencontainers/runc v0.1.1/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U= github.com/opencontainers/runc v1.0.0-rc8.0.20190926000215-3e425f80a8c9/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U= diff --git a/pkg/api/handlers/compat/images_push.go b/pkg/api/handlers/compat/images_push.go index 5ecb429ae..3a84b5799 100644 --- a/pkg/api/handlers/compat/images_push.go +++ b/pkg/api/handlers/compat/images_push.go @@ -74,10 +74,16 @@ func PushImage(w http.ResponseWriter, r *http.Request) { return } imageName = possiblyNormalizedName - if _, _, err := runtime.LibimageRuntime().LookupImage(possiblyNormalizedName, nil); err != nil { + localImage, _, err := runtime.LibimageRuntime().LookupImage(possiblyNormalizedName, nil) + if err != nil { utils.ImageNotFound(w, imageName, errors.Wrapf(err, "failed to find image %s", imageName)) return } + rawManifest, _, err := localImage.Manifest(r.Context()) + if err != nil { + utils.Error(w, "Something went wrong.", http.StatusBadRequest, err) + return + } authconf, authfile, key, err := auth.GetCredentials(r) if err != nil { @@ -196,7 +202,7 @@ loop: // break out of for/select infinite loop if tag == "" { tag = "latest" } - report.Status = fmt.Sprintf("%s: digest: %s", tag, string(digestBytes)) + report.Status = fmt.Sprintf("%s: digest: %s size: %d", tag, string(digestBytes), len(rawManifest)) if err := enc.Encode(report); err != nil { logrus.Warnf("Failed to json encode error %q", err.Error()) } diff --git a/pkg/api/handlers/libpod/generate.go b/pkg/api/handlers/libpod/generate.go index 1411c680e..88fd69d45 100644 --- a/pkg/api/handlers/libpod/generate.go +++ b/pkg/api/handlers/libpod/generate.go @@ -22,6 +22,7 @@ func GenerateSystemd(w http.ResponseWriter, r *http.Request) { NoHeader bool `schema:"noHeader"` TemplateUnitFile bool `schema:"templateUnitFile"` RestartPolicy *string `schema:"restartPolicy"` + RestartSec uint `schema:"restartSec"` StopTimeout uint `schema:"stopTimeout"` StartTimeout uint `schema:"startTimeout"` ContainerPrefix string `schema:"containerPrefix"` @@ -53,6 +54,7 @@ func GenerateSystemd(w http.ResponseWriter, r *http.Request) { ContainerPrefix: query.ContainerPrefix, PodPrefix: query.PodPrefix, Separator: query.Separator, + RestartSec: &query.RestartSec, } report, err := containerEngine.GenerateSystemd(r.Context(), utils.GetName(r), options) diff --git a/pkg/api/server/register_generate.go b/pkg/api/server/register_generate.go index 0e36394cf..65340bf56 100644 --- a/pkg/api/server/register_generate.go +++ b/pkg/api/server/register_generate.go @@ -67,6 +67,11 @@ func (s *APIServer) registerGenerateHandlers(r *mux.Router) error { // type: string // default: "-" // description: Systemd unit name separator between name/id and prefix. + // - in: query + // name: restartSec + // type: integer + // default: 0 + // description: Configures the time to sleep before restarting a service. // produces: // - application/json // responses: diff --git a/pkg/bindings/generate/types.go b/pkg/bindings/generate/types.go index 092474e4a..ce560c547 100644 --- a/pkg/bindings/generate/types.go +++ b/pkg/bindings/generate/types.go @@ -20,6 +20,8 @@ type SystemdOptions struct { TemplateUnitFile *bool // RestartPolicy - systemd restart policy. RestartPolicy *string + // RestartSec - systemd service restartsec. Configures the time to sleep before restarting a service. + RestartSec *uint // StartTimeout - time when starting the container. StartTimeout *uint // StopTimeout - time when stopping the container. diff --git a/pkg/bindings/generate/types_systemd_options.go b/pkg/bindings/generate/types_systemd_options.go index d60f1d70e..504d4da7f 100644 --- a/pkg/bindings/generate/types_systemd_options.go +++ b/pkg/bindings/generate/types_systemd_options.go @@ -92,6 +92,21 @@ func (o *SystemdOptions) GetRestartPolicy() string { return *o.RestartPolicy } +// WithRestartSec set field RestartSec to given value +func (o *SystemdOptions) WithRestartSec(value uint) *SystemdOptions { + o.RestartSec = &value + return o +} + +// GetRestartSec returns value of field RestartSec +func (o *SystemdOptions) GetRestartSec() uint { + if o.RestartSec == nil { + var z uint + return z + } + return *o.RestartSec +} + // WithStartTimeout set field StartTimeout to given value func (o *SystemdOptions) WithStartTimeout(value uint) *SystemdOptions { o.StartTimeout = &value diff --git a/pkg/domain/entities/generate.go b/pkg/domain/entities/generate.go index 7e80e5d2d..e431a70af 100644 --- a/pkg/domain/entities/generate.go +++ b/pkg/domain/entities/generate.go @@ -10,6 +10,8 @@ type GenerateSystemdOptions struct { New bool // RestartPolicy - systemd restart policy. RestartPolicy *string + // RestartSec - systemd service restartsec. Configures the time to sleep before restarting a service. + RestartSec *uint // StartTimeout - time when starting the container. StartTimeout *uint // StopTimeout - time when stopping the container. diff --git a/pkg/domain/infra/abi/images.go b/pkg/domain/infra/abi/images.go index 7a3451a7d..4346182d6 100644 --- a/pkg/domain/infra/abi/images.go +++ b/pkg/domain/infra/abi/images.go @@ -306,8 +306,16 @@ func (ir *ImageEngine) Push(ctx context.Context, source string, destination stri pushOptions.SignBy = options.SignBy pushOptions.InsecureSkipTLSVerify = options.SkipTLSVerify - if options.CompressionFormat != "" { - algo, err := compression.AlgorithmByName(options.CompressionFormat) + compressionFormat := options.CompressionFormat + if compressionFormat == "" { + config, err := ir.Libpod.GetConfigNoCopy() + if err != nil { + return err + } + compressionFormat = config.Engine.CompressionFormat + } + if compressionFormat != "" { + algo, err := compression.AlgorithmByName(compressionFormat) if err != nil { return err } diff --git a/pkg/domain/infra/abi/play.go b/pkg/domain/infra/abi/play.go index bdf22cf0c..ab52fad64 100644 --- a/pkg/domain/infra/abi/play.go +++ b/pkg/domain/infra/abi/play.go @@ -239,27 +239,6 @@ func (ic *ContainerEngine) playKubePod(ctx context.Context, podName string, podY return nil, err } podSpec := entities.PodSpec{PodSpecGen: *p} - volumes, err := kube.InitializeVolumes(podYAML.Spec.Volumes) - if err != nil { - return nil, err - } - - seccompPaths, err := kube.InitializeSeccompPaths(podYAML.ObjectMeta.Annotations, options.SeccompProfileRoot) - if err != nil { - return nil, err - } - - var ctrRestartPolicy string - switch podYAML.Spec.RestartPolicy { - case v1.RestartPolicyAlways: - ctrRestartPolicy = define.RestartPolicyAlways - case v1.RestartPolicyOnFailure: - ctrRestartPolicy = define.RestartPolicyOnFailure - case v1.RestartPolicyNever: - ctrRestartPolicy = define.RestartPolicyNo - default: // Default to Always - ctrRestartPolicy = define.RestartPolicyAlways - } configMapIndex := make(map[string]struct{}) for _, configMap := range configMaps { @@ -284,6 +263,56 @@ func (ic *ContainerEngine) playKubePod(ctx context.Context, podName string, podY configMaps = append(configMaps, cm) } + volumes, err := kube.InitializeVolumes(podYAML.Spec.Volumes, configMaps) + if err != nil { + return nil, err + } + + // Go through the volumes and create a podman volume for all volumes that have been + // defined by a configmap + for _, v := range volumes { + if v.Type == kube.KubeVolumeTypeConfigMap && !v.Optional { + vol, err := ic.Libpod.NewVolume(ctx, libpod.WithVolumeName(v.Source)) + if err != nil { + return nil, errors.Wrapf(err, "cannot create a local volume for volume from configmap %q", v.Source) + } + mountPoint, err := vol.MountPoint() + if err != nil || mountPoint == "" { + return nil, errors.Wrapf(err, "unable to get mountpoint of volume %q", vol.Name()) + } + // Create files and add data to the volume mountpoint based on the Items in the volume + for k, v := range v.Items { + dataPath := filepath.Join(mountPoint, k) + f, err := os.Create(dataPath) + if err != nil { + return nil, errors.Wrapf(err, "cannot create file %q at volume mountpoint %q", k, mountPoint) + } + defer f.Close() + _, err = f.WriteString(v) + if err != nil { + return nil, err + } + } + } + } + + seccompPaths, err := kube.InitializeSeccompPaths(podYAML.ObjectMeta.Annotations, options.SeccompProfileRoot) + if err != nil { + return nil, err + } + + var ctrRestartPolicy string + switch podYAML.Spec.RestartPolicy { + case v1.RestartPolicyAlways: + ctrRestartPolicy = define.RestartPolicyAlways + case v1.RestartPolicyOnFailure: + ctrRestartPolicy = define.RestartPolicyOnFailure + case v1.RestartPolicyNever: + ctrRestartPolicy = define.RestartPolicyNo + default: // Default to Always + ctrRestartPolicy = define.RestartPolicyAlways + } + if podOpt.Infra { infraImage := util.DefaultContainerConfig().Engine.InfraImage infraOptions := entities.NewInfraContainerCreateOptions() diff --git a/pkg/domain/infra/tunnel/generate.go b/pkg/domain/infra/tunnel/generate.go index d62a318d6..dd895b61f 100644 --- a/pkg/domain/infra/tunnel/generate.go +++ b/pkg/domain/infra/tunnel/generate.go @@ -19,6 +19,9 @@ func (ic *ContainerEngine) GenerateSystemd(ctx context.Context, nameOrID string, if opts.RestartPolicy != nil { options.WithRestartPolicy(*opts.RestartPolicy) } + if opts.RestartSec != nil { + options.WithRestartSec(*opts.RestartSec) + } return generate.Systemd(ic.ClientCtx, nameOrID, options) } diff --git a/pkg/specgen/generate/kube/kube.go b/pkg/specgen/generate/kube/kube.go index c502a6e62..6d9f598c9 100644 --- a/pkg/specgen/generate/kube/kube.go +++ b/pkg/specgen/generate/kube/kube.go @@ -310,6 +310,11 @@ func ToSpecGen(ctx context.Context, opts *CtrSpecGenOptions) (*specgen.SpecGener if !exists { return nil, errors.Errorf("Volume mount %s specified for container but not configured in volumes", volume.Name) } + // Skip if the volume is optional. This means that a configmap for a configmap volume was not found but it was + // optional so we can move on without throwing an error + if exists && volumeSource.Optional { + continue + } dest, options, err := parseMountPath(volume.MountPath, volume.ReadOnly) if err != nil { @@ -341,6 +346,13 @@ func ToSpecGen(ctx context.Context, opts *CtrSpecGenOptions) (*specgen.SpecGener Options: options, } s.Volumes = append(s.Volumes, &namedVolume) + case KubeVolumeTypeConfigMap: + cmVolume := specgen.NamedVolume{ + Dest: volume.MountPath, + Name: volumeSource.Source, + Options: options, + } + s.Volumes = append(s.Volumes, &cmVolume) default: return nil, errors.Errorf("Unsupported volume source type") } diff --git a/pkg/specgen/generate/kube/volume.go b/pkg/specgen/generate/kube/volume.go index a8042b532..76ec0a390 100644 --- a/pkg/specgen/generate/kube/volume.go +++ b/pkg/specgen/generate/kube/volume.go @@ -23,6 +23,7 @@ type KubeVolumeType int const ( KubeVolumeTypeBindMount KubeVolumeType = iota KubeVolumeTypeNamed KubeVolumeType = iota + KubeVolumeTypeConfigMap KubeVolumeType = iota ) // nolint:golint @@ -31,6 +32,14 @@ type KubeVolume struct { Type KubeVolumeType // Path for bind mount or volume name for named volume Source string + // Items to add to a named volume created where the key is the file name and the value is the data + // This is only used when there are volumes in the yaml that refer to a configmap + // Example: if configmap has data "SPECIAL_LEVEL: very" then the file name is "SPECIAL_LEVEL" and the + // data in that file is "very". + Items map[string]string + // If the volume is optional, we can move on if it is not found + // Only used when there are volumes in a yaml that refer to a configmap + Optional bool } // Create a KubeVolume from an HostPathVolumeSource @@ -98,23 +107,64 @@ func VolumeFromPersistentVolumeClaim(claim *v1.PersistentVolumeClaimVolumeSource }, nil } +func VolumeFromConfigMap(configMapVolumeSource *v1.ConfigMapVolumeSource, configMaps []v1.ConfigMap) (*KubeVolume, error) { + var configMap *v1.ConfigMap + kv := &KubeVolume{Type: KubeVolumeTypeConfigMap, Items: map[string]string{}} + for _, cm := range configMaps { + if cm.Name == configMapVolumeSource.Name { + matchedCM := cm + // Set the source to the config map name + kv.Source = cm.Name + configMap = &matchedCM + break + } + } + + if configMap == nil { + // If the volumeSource was optional, move on even if a matching configmap wasn't found + if *configMapVolumeSource.Optional { + kv.Source = configMapVolumeSource.Name + kv.Optional = *configMapVolumeSource.Optional + return kv, nil + } + return nil, errors.Errorf("no such ConfigMap %q", configMapVolumeSource.Name) + } + + // If there are Items specified in the volumeSource, that overwrites the Data from the configmap + if len(configMapVolumeSource.Items) > 0 { + for _, item := range configMapVolumeSource.Items { + if val, ok := configMap.Data[item.Key]; ok { + kv.Items[item.Path] = val + } + } + } else { + for k, v := range configMap.Data { + kv.Items[k] = v + } + } + return kv, nil +} + // Create a KubeVolume from one of the supported VolumeSource -func VolumeFromSource(volumeSource v1.VolumeSource) (*KubeVolume, error) { - if volumeSource.HostPath != nil { +func VolumeFromSource(volumeSource v1.VolumeSource, configMaps []v1.ConfigMap) (*KubeVolume, error) { + switch { + case volumeSource.HostPath != nil: return VolumeFromHostPath(volumeSource.HostPath) - } else if volumeSource.PersistentVolumeClaim != nil { + case volumeSource.PersistentVolumeClaim != nil: return VolumeFromPersistentVolumeClaim(volumeSource.PersistentVolumeClaim) - } else { - return nil, errors.Errorf("HostPath and PersistentVolumeClaim are currently the only supported VolumeSource") + case volumeSource.ConfigMap != nil: + return VolumeFromConfigMap(volumeSource.ConfigMap, configMaps) + default: + return nil, errors.Errorf("HostPath, ConfigMap, and PersistentVolumeClaim are currently the only supported VolumeSource") } } // Create a map of volume name to KubeVolume -func InitializeVolumes(specVolumes []v1.Volume) (map[string]*KubeVolume, error) { +func InitializeVolumes(specVolumes []v1.Volume, configMaps []v1.ConfigMap) (map[string]*KubeVolume, error) { volumes := make(map[string]*KubeVolume) for _, specVolume := range specVolumes { - volume, err := VolumeFromSource(specVolume.VolumeSource) + volume, err := VolumeFromSource(specVolume.VolumeSource, configMaps) if err != nil { return nil, errors.Wrapf(err, "failed to create volume %q", specVolume.Name) } diff --git a/pkg/systemd/generate/pods.go b/pkg/systemd/generate/pods.go index 48252c737..f2d04dadc 100644 --- a/pkg/systemd/generate/pods.go +++ b/pkg/systemd/generate/pods.go @@ -30,6 +30,8 @@ type podInfo struct { StopTimeout uint // RestartPolicy of the systemd unit (e.g., no, on-failure, always). RestartPolicy string + // RestartSec of the systemd unit. Configures the time to sleep before restarting a service. + RestartSec uint // PIDFile of the service. Required for forking services. Must point to the // PID of the associated conmon process. PIDFile string @@ -89,6 +91,9 @@ Before={{{{- range $index, $value := .RequiredServices -}}}}{{{{if $index}}}} {{ [Service] Environment={{{{.EnvVariable}}}}=%n Restart={{{{.RestartPolicy}}}} +{{{{- if .RestartSec}}}} +RestartSec={{{{.RestartSec}}}} +{{{{- end}}}} TimeoutStopSec={{{{.TimeoutStopSec}}}} {{{{- if .ExecStartPre1}}}} ExecStartPre={{{{.ExecStartPre1}}}} @@ -242,6 +247,10 @@ func executePodTemplate(info *podInfo, options entities.GenerateSystemdOptions) info.RestartPolicy = *options.RestartPolicy } + if options.RestartSec != nil { + info.RestartSec = *options.RestartSec + } + // Make sure the executable is set. if info.Executable == "" { executable, err := os.Executable() diff --git a/pkg/systemd/generate/pods_test.go b/pkg/systemd/generate/pods_test.go index 612908991..0889507a5 100644 --- a/pkg/systemd/generate/pods_test.go +++ b/pkg/systemd/generate/pods_test.go @@ -67,6 +67,33 @@ WantedBy=default.target podGood := serviceInfo + headerInfo + podContent podGoodNoHeaderInfo := serviceInfo + podContent + podGoodRestartSec := `# pod-123abc.service +# autogenerated by Podman CI + +[Unit] +Description=Podman pod-123abc.service +Documentation=man:podman-generate-systemd(1) +Wants=network-online.target +After=network-online.target +RequiresMountsFor=/var/run/containers/storage +Requires=container-1.service container-2.service +Before=container-1.service container-2.service + +[Service] +Environment=PODMAN_SYSTEMD_UNIT=%n +Restart=on-failure +RestartSec=15 +TimeoutStopSec=102 +ExecStart=/usr/bin/podman start jadda-jadda-infra +ExecStop=/usr/bin/podman stop -t 42 jadda-jadda-infra +ExecStopPost=/usr/bin/podman stop -t 42 jadda-jadda-infra +PIDFile=/run/containers/storage/overlay-containers/639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401/userdata/conmon.pid +Type=forking + +[Install] +WantedBy=default.target +` + podGoodNamedNew := `# pod-123abc.service # autogenerated by Podman CI @@ -205,6 +232,25 @@ WantedBy=default.target false, false, }, + {"pod restartSec", + podInfo{ + Executable: "/usr/bin/podman", + ServiceName: "pod-123abc", + InfraNameOrID: "jadda-jadda-infra", + PIDFile: "/run/containers/storage/overlay-containers/639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401/userdata/conmon.pid", + StopTimeout: 42, + PodmanVersion: "CI", + GraphRoot: "/var/lib/containers/storage", + RunRoot: "/var/run/containers/storage", + RequiredServices: []string{"container-1", "container-2"}, + CreateCommand: []string{"podman", "pod", "create", "--name", "foo", "bar=arg with space"}, + RestartSec: 15, + }, + podGoodRestartSec, + false, + false, + false, + }, {"pod noHeader", podInfo{ Executable: "/usr/bin/podman", diff --git a/test/e2e/generate_systemd_test.go b/test/e2e/generate_systemd_test.go index 1cffdc62e..fd9ae5037 100644 --- a/test/e2e/generate_systemd_test.go +++ b/test/e2e/generate_systemd_test.go @@ -282,6 +282,19 @@ var _ = Describe("Podman generate systemd", func() { Expect(session.OutputToString()).To(ContainSubstring(" pod create ")) }) + It("podman generate systemd --restart-sec 15 --name foo", func() { + n := podmanTest.Podman([]string{"pod", "create", "--name", "foo"}) + n.WaitWithDefaultTimeout() + Expect(n).Should(Exit(0)) + + session := podmanTest.Podman([]string{"generate", "systemd", "--restart-sec", "15", "--name", "foo"}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + + // Grepping the output (in addition to unit tests) + Expect(session.OutputToString()).To(ContainSubstring("RestartSec=15")) + }) + It("podman generate systemd --new=false pod", func() { n := podmanTest.Podman([]string{"pod", "create", "--name", "foo"}) n.WaitWithDefaultTimeout() diff --git a/test/e2e/play_kube_test.go b/test/e2e/play_kube_test.go index 82b543151..96ad2954c 100644 --- a/test/e2e/play_kube_test.go +++ b/test/e2e/play_kube_test.go @@ -380,6 +380,18 @@ spec: persistentVolumeClaim: claimName: {{ .PersistentVolumeClaim.ClaimName }} {{- end }} + {{- if (eq .VolumeType "ConfigMap") }} + configMap: + name: {{ .ConfigMap.Name }} + optional: {{ .ConfigMap.Optional }} + {{- with .ConfigMap.Items }} + items: + {{- range . }} + - key: {{ .key }} + path: {{ .path }} + {{- end }} + {{- end }} + {{- end }} {{ end }} {{ end }} status: {} @@ -619,14 +631,14 @@ func createSecret(podmanTest *PodmanTestIntegration, name string, value []byte) Expect(secret).Should(Exit(0)) } -// ConfigMap describes the options a kube yaml can be configured at configmap level -type ConfigMap struct { +// CM describes the options a kube yaml can be configured at configmap level +type CM struct { Name string Data map[string]string } -func getConfigMap(options ...configMapOption) *ConfigMap { - cm := ConfigMap{ +func getConfigMap(options ...configMapOption) *CM { + cm := CM{ Name: defaultConfigMapName, Data: map[string]string{}, } @@ -638,16 +650,16 @@ func getConfigMap(options ...configMapOption) *ConfigMap { return &cm } -type configMapOption func(*ConfigMap) +type configMapOption func(*CM) func withConfigMapName(name string) configMapOption { - return func(configmap *ConfigMap) { + return func(configmap *CM) { configmap.Name = name } } func withConfigMapData(k, v string) configMapOption { - return func(configmap *ConfigMap) { + return func(configmap *CM) { configmap.Data[k] = v } } @@ -1047,11 +1059,18 @@ type PersistentVolumeClaim struct { ClaimName string } +type ConfigMap struct { + Name string + Items []map[string]string + Optional bool +} + type Volume struct { VolumeType string Name string HostPath PersistentVolumeClaim + ConfigMap } // getHostPathVolume takes a type and a location for a HostPath @@ -1079,6 +1098,20 @@ func getPersistentVolumeClaimVolume(vName string) *Volume { } } +// getConfigMap returns a new ConfigMap Volume given the name and items +// of the ConfigMap. +func getConfigMapVolume(vName string, items []map[string]string, optional bool) *Volume { + return &Volume{ + VolumeType: "ConfigMap", + Name: defaultVolName, + ConfigMap: ConfigMap{ + Name: vName, + Items: items, + Optional: optional, + }, + } +} + type Env struct { Name string Value string @@ -2311,6 +2344,75 @@ VOLUME %s`, ALPINE, hostPathDir+"/") Expect(inspect.OutputToString()).To(Equal(correct)) }) + It("podman play kube ConfigMap volume with no items", func() { + volumeName := "cmVol" + cm := getConfigMap(withConfigMapName(volumeName), withConfigMapData("FOO", "foobar")) + cmYaml, err := getKubeYaml("configmap", cm) + Expect(err).To(BeNil()) + + ctr := getCtr(withVolumeMount("/test", false), withImage(BB)) + pod := getPod(withVolume(getConfigMapVolume(volumeName, []map[string]string{}, false)), withCtr(ctr)) + podYaml, err := getKubeYaml("pod", pod) + Expect(err).To(BeNil()) + yamls := []string{cmYaml, podYaml} + err = generateMultiDocKubeYaml(yamls, kubeYaml) + Expect(err).To(BeNil()) + + kube := podmanTest.Podman([]string{"play", "kube", kubeYaml}) + kube.WaitWithDefaultTimeout() + Expect(kube).Should(Exit(0)) + + cmData := podmanTest.Podman([]string{"exec", getCtrNameInPod(pod), "cat", "/test/FOO"}) + cmData.WaitWithDefaultTimeout() + Expect(cmData).Should(Exit(0)) + Expect(cmData.OutputToString()).To(Equal("foobar")) + }) + + It("podman play kube ConfigMap volume with items", func() { + volumeName := "cmVol" + cm := getConfigMap(withConfigMapName(volumeName), withConfigMapData("FOO", "foobar")) + cmYaml, err := getKubeYaml("configmap", cm) + Expect(err).To(BeNil()) + volumeContents := []map[string]string{{ + "key": "FOO", + "path": "BAR", + }} + + ctr := getCtr(withVolumeMount("/test", false), withImage(BB)) + pod := getPod(withVolume(getConfigMapVolume(volumeName, volumeContents, false)), withCtr(ctr)) + podYaml, err := getKubeYaml("pod", pod) + Expect(err).To(BeNil()) + yamls := []string{cmYaml, podYaml} + err = generateMultiDocKubeYaml(yamls, kubeYaml) + Expect(err).To(BeNil()) + + kube := podmanTest.Podman([]string{"play", "kube", kubeYaml}) + kube.WaitWithDefaultTimeout() + Expect(kube).Should(Exit(0)) + + cmData := podmanTest.Podman([]string{"exec", getCtrNameInPod(pod), "cat", "/test/BAR"}) + cmData.WaitWithDefaultTimeout() + Expect(cmData).Should(Exit(0)) + Expect(cmData.OutputToString()).To(Equal("foobar")) + + cmData = podmanTest.Podman([]string{"exec", getCtrNameInPod(pod), "cat", "/test/FOO"}) + cmData.WaitWithDefaultTimeout() + Expect(cmData).Should(Not(Exit(0))) + }) + + It("podman play kube with a missing optional ConfigMap volume", func() { + volumeName := "cmVol" + + ctr := getCtr(withVolumeMount("/test", false), withImage(BB)) + pod := getPod(withVolume(getConfigMapVolume(volumeName, []map[string]string{}, true)), withCtr(ctr)) + err = generateKubeYaml("pod", pod, kubeYaml) + Expect(err).To(BeNil()) + + kube := podmanTest.Podman([]string{"play", "kube", kubeYaml}) + kube.WaitWithDefaultTimeout() + Expect(kube).Should(Exit(0)) + }) + It("podman play kube applies labels to pods", func() { var numReplicas int32 = 5 expectedLabelKey := "key1" diff --git a/vendor/github.com/containers/common/libimage/pull.go b/vendor/github.com/containers/common/libimage/pull.go index 1d1bc201b..59221d935 100644 --- a/vendor/github.com/containers/common/libimage/pull.go +++ b/vendor/github.com/containers/common/libimage/pull.go @@ -454,7 +454,7 @@ func (r *Runtime) copySingleImageFromRegistry(ctx context.Context, imageName str // NOTE that this is will even override --pull={false,never}. This is // very likely a bug but a consistent one in Podman/Buildah and should // be addressed at a later point. - if pullPolicy != config.PullPolicyAlways { + if pullPolicy != config.PullPolicyAlways && pullPolicy != config.PullPolicyNever { switch { // User input clearly refer to a local image. case strings.HasPrefix(imageName, "localhost/"): diff --git a/vendor/github.com/containers/common/pkg/config/config.go b/vendor/github.com/containers/common/pkg/config/config.go index 7ce0e5022..29c505e9c 100644 --- a/vendor/github.com/containers/common/pkg/config/config.go +++ b/vendor/github.com/containers/common/pkg/config/config.go @@ -420,6 +420,9 @@ type EngineConfig struct { // ChownCopiedFiles tells the container engine whether to chown files copied // into a container to the container's primary uid/gid. ChownCopiedFiles bool `toml:"chown_copied_files,omitempty"` + + // CompressionFormat is the compression format used to compress image layers. + CompressionFormat string `toml:"compression_format,omitempty"` } // SetOptions contains a subset of options in a Config. It's used to indicate if diff --git a/vendor/github.com/containers/common/pkg/config/containers.conf b/vendor/github.com/containers/common/pkg/config/containers.conf index 8e305b57e..84b49b7e4 100644 --- a/vendor/github.com/containers/common/pkg/config/containers.conf +++ b/vendor/github.com/containers/common/pkg/config/containers.conf @@ -294,6 +294,12 @@ default_sysctls = [ # #active_service = production +# The compression format to use when pushing an image. +# Valid options are: `gzip`, `zstd` and `zstd:chunked`. +# +#compression_format = "gzip" + + # Cgroup management implementation used for the runtime. # Valid options "systemd" or "cgroupfs" # diff --git a/vendor/github.com/containers/common/pkg/config/nosystemd.go b/vendor/github.com/containers/common/pkg/config/nosystemd.go index 2a3b6fb35..f64b2dfc6 100644 --- a/vendor/github.com/containers/common/pkg/config/nosystemd.go +++ b/vendor/github.com/containers/common/pkg/config/nosystemd.go @@ -22,3 +22,7 @@ func defaultLogDriver() string { func useSystemd() bool { return false } + +func useJournald() bool { + return false +} diff --git a/vendor/github.com/containers/common/pkg/config/systemd.go b/vendor/github.com/containers/common/pkg/config/systemd.go index fab3ea437..186e8b343 100644 --- a/vendor/github.com/containers/common/pkg/config/systemd.go +++ b/vendor/github.com/containers/common/pkg/config/systemd.go @@ -4,12 +4,12 @@ package config import ( "io/ioutil" + "path/filepath" "strings" "sync" "github.com/containers/common/pkg/cgroupv2" "github.com/containers/storage/pkg/unshare" - "github.com/coreos/go-systemd/v22/sdjournal" ) var ( @@ -67,12 +67,20 @@ func useJournald() bool { if !useSystemd() { return } - journal, err := sdjournal.NewJournal() - if err != nil { - return + for _, root := range []string{"/run/log/journal", "/var/log/journal"} { + dirs, err := ioutil.ReadDir(root) + if err != nil { + continue + } + for _, d := range dirs { + if d.IsDir() { + if _, err := ioutil.ReadDir(filepath.Join(root, d.Name())); err == nil { + usesJournald = true + return + } + } + } } - journal.Close() - usesJournald = true return }) return usesJournald diff --git a/vendor/github.com/containers/common/pkg/manifests/manifests.go b/vendor/github.com/containers/common/pkg/manifests/manifests.go index ea9495ee7..5c2836893 100644 --- a/vendor/github.com/containers/common/pkg/manifests/manifests.go +++ b/vendor/github.com/containers/common/pkg/manifests/manifests.go @@ -74,6 +74,7 @@ func Create() List { }, oci: v1.Index{ Versioned: imgspec.Versioned{SchemaVersion: 2}, + MediaType: v1.MediaTypeImageIndex, }, } } @@ -373,6 +374,7 @@ func FromBlob(manifestBytes []byte) (List, error) { }, oci: v1.Index{ Versioned: imgspec.Versioned{SchemaVersion: 2}, + MediaType: v1.MediaTypeImageIndex, }, } switch manifestType { diff --git a/vendor/github.com/containers/storage/.cirrus.yml b/vendor/github.com/containers/storage/.cirrus.yml index d080d790c..726acc3ae 100644 --- a/vendor/github.com/containers/storage/.cirrus.yml +++ b/vendor/github.com/containers/storage/.cirrus.yml @@ -17,8 +17,8 @@ env: #### #### Cache-image names to test with (double-quotes around names are critical) ### - FEDORA_NAME: "fedora-34" - PRIOR_FEDORA_NAME: "fedora-33" + FEDORA_NAME: "fedora-35" + PRIOR_FEDORA_NAME: "fedora-34" UBUNTU_NAME: "ubuntu-2104" # GCE project where images live diff --git a/vendor/github.com/containers/storage/Vagrantfile b/vendor/github.com/containers/storage/Vagrantfile deleted file mode 100644 index c82c1f81b..000000000 --- a/vendor/github.com/containers/storage/Vagrantfile +++ /dev/null @@ -1,25 +0,0 @@ -# -*- mode: ruby -*- -# vi: set ft=ruby : -# -# The fedora/28-cloud-base and debian/jessie64 boxes are also available for -# the "virtualbox" provider. Set the VAGRANT_PROVIDER environment variable to -# "virtualbox" to use them instead. -# -Vagrant.configure("2") do |config| - config.vm.define "fedora" do |c| - c.vm.box = "fedora/28-cloud-base" - c.vm.synced_folder ".", "/vagrant", type: "rsync", - rsync__exclude: "bundles", rsync__args: ["-vadz", "--delete"] - c.vm.provision "shell", inline: <<-SHELL - sudo /vagrant/vagrant/provision.sh - SHELL - end - config.vm.define "debian" do |c| - c.vm.box = "debian/jessie64" - c.vm.synced_folder ".", "/vagrant", type: "rsync", - rsync__exclude: "bundles", rsync__args: ["-vadz", "--delete"] - c.vm.provision "shell", inline: <<-SHELL - sudo /vagrant/vagrant/provision.sh - SHELL - end -end diff --git a/vendor/github.com/containers/storage/go.mod b/vendor/github.com/containers/storage/go.mod index 04d5adac4..57b634f17 100644 --- a/vendor/github.com/containers/storage/go.mod +++ b/vendor/github.com/containers/storage/go.mod @@ -7,6 +7,7 @@ require ( github.com/Microsoft/go-winio v0.5.1 github.com/Microsoft/hcsshim v0.9.1 github.com/containerd/stargz-snapshotter/estargz v0.10.1 + github.com/cyphar/filepath-securejoin v0.2.3 github.com/docker/go-units v0.4.0 github.com/google/go-intervals v0.0.2 github.com/hashicorp/go-multierror v1.1.1 diff --git a/vendor/github.com/containers/storage/go.sum b/vendor/github.com/containers/storage/go.sum index 35328e287..94d46b21a 100644 --- a/vendor/github.com/containers/storage/go.sum +++ b/vendor/github.com/containers/storage/go.sum @@ -218,6 +218,8 @@ github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsr github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/cyphar/filepath-securejoin v0.2.2/go.mod h1:FpkQEhXnPnOthhzymB7CGsFk2G9VLXONKD9G7QGMM+4= +github.com/cyphar/filepath-securejoin v0.2.3 h1:YX6ebbZCZP7VkM3scTTokDgBL2TY741X51MTk3ycuNI= +github.com/cyphar/filepath-securejoin v0.2.3/go.mod h1:aPGpWjXOXUn2NCNjFvBE6aRxGGx79pTxQpKOJNYHHl4= github.com/d2g/dhcp4 v0.0.0-20170904100407-a1d1b6c41b1c/go.mod h1:Ct2BUK8SB0YC1SMSibvLzxjeJLnrYEVLULFNiHY9YfQ= github.com/d2g/dhcp4client v1.0.0/go.mod h1:j0hNfjhrt2SxUOw55nL0ATM/z4Yt3t2Kd1mW34z5W5s= github.com/d2g/dhcp4server v0.0.0-20181031114812-7d4a0a7f59a5/go.mod h1:Eo87+Kg/IX2hfWJfwxMzLyuSZyxSoAug2nGa1G2QAi8= diff --git a/vendor/github.com/containers/storage/pkg/archive/archive.go b/vendor/github.com/containers/storage/pkg/archive/archive.go index 76544ff28..e7c4cfcf1 100644 --- a/vendor/github.com/containers/storage/pkg/archive/archive.go +++ b/vendor/github.com/containers/storage/pkg/archive/archive.go @@ -77,6 +77,10 @@ const ( containersOverrideXattr = "user.containers.override_stat" ) +var xattrsToIgnore = map[string]interface{}{ + "security.selinux": true, +} + // Archiver allows the reuse of most utility functions of this package with a // pluggable Untar function. To facilitate the passing of specific id mappings // for untar, an archiver can be created with maps which will then be passed to @@ -743,6 +747,9 @@ func createTarFile(path, extractDir string, hdr *tar.Header, reader io.Reader, L var errs []string for key, value := range hdr.Xattrs { + if _, found := xattrsToIgnore[key]; found { + continue + } if err := system.Lsetxattr(path, key, []byte(value), 0); err != nil { if errors.Is(err, syscall.ENOTSUP) || (inUserns && errors.Is(err, syscall.EPERM)) { // We ignore errors here because not all graphdrivers support diff --git a/vendor/github.com/containers/storage/pkg/chunked/storage_linux.go b/vendor/github.com/containers/storage/pkg/chunked/storage_linux.go index d18ab299b..52d21d689 100644 --- a/vendor/github.com/containers/storage/pkg/chunked/storage_linux.go +++ b/vendor/github.com/containers/storage/pkg/chunked/storage_linux.go @@ -13,6 +13,7 @@ import ( "reflect" "sort" "strings" + "sync/atomic" "syscall" "time" @@ -25,6 +26,7 @@ import ( "github.com/containers/storage/pkg/idtools" "github.com/containers/storage/pkg/system" "github.com/containers/storage/types" + securejoin "github.com/cyphar/filepath-securejoin" "github.com/klauspost/compress/zstd" "github.com/klauspost/pgzip" digest "github.com/opencontainers/go-digest" @@ -57,6 +59,10 @@ type chunkedDiffer struct { gzipReader *pgzip.Reader } +var xattrsToIgnore = map[string]interface{}{ + "security.selinux": true, +} + func timeToTimespec(time time.Time) (ts unix.Timespec) { if time.IsZero() { // Return UTIME_OMIT special value @@ -89,7 +95,7 @@ func copyFileContent(srcFd int, destFile string, dirfd int, mode os.FileMode, us src := fmt.Sprintf("/proc/self/fd/%d", srcFd) st, err := os.Stat(src) if err != nil { - return nil, -1, err + return nil, -1, fmt.Errorf("copy file content for %q: %w", destFile, err) } copyWithFileRange, copyWithFileClone := true, true @@ -111,15 +117,15 @@ func copyFileContent(srcFd int, destFile string, dirfd int, mode os.FileMode, us // If the destination file already exists, we shouldn't blow it away dstFile, err := openFileUnderRoot(destFile, dirfd, newFileFlags, mode) if err != nil { - return nil, -1, err + return nil, -1, fmt.Errorf("open file %q under rootfs for copy: %w", destFile, err) } err = driversCopy.CopyRegularToFile(src, dstFile, st, ©WithFileRange, ©WithFileClone) if err != nil { dstFile.Close() - return nil, -1, err + return nil, -1, fmt.Errorf("copy to file %q under rootfs: %w", destFile, err) } - return dstFile, st.Size(), err + return dstFile, st.Size(), nil } func prepareOtherLayersCache(layersMetadata map[string][]internal.FileMetadata) map[string]map[string][]*internal.FileMetadata { @@ -153,7 +159,7 @@ func getLayersCache(store storage.Store) (map[string][]internal.FileMetadata, ma defer manifestReader.Close() manifest, err := ioutil.ReadAll(manifestReader) if err != nil { - return nil, nil, err + return nil, nil, fmt.Errorf("open manifest file for layer %q: %w", r.ID, err) } var toc internal.TOC if err := json.Unmarshal(manifest, &toc); err != nil { @@ -162,7 +168,7 @@ func getLayersCache(store storage.Store) (map[string][]internal.FileMetadata, ma layersMetadata[r.ID] = toc.Entries target, err := store.DifferTarget(r.ID) if err != nil { - return nil, nil, err + return nil, nil, fmt.Errorf("get checkout directory layer %q: %w", r.ID, err) } layersTarget[r.ID] = target } @@ -184,7 +190,7 @@ func GetDiffer(ctx context.Context, store storage.Store, blobSize int64, annotat func makeZstdChunkedDiffer(ctx context.Context, store storage.Store, blobSize int64, annotations map[string]string, iss ImageSourceSeekable) (*chunkedDiffer, error) { manifest, tocOffset, err := readZstdChunkedManifest(iss, blobSize, annotations) if err != nil { - return nil, err + return nil, fmt.Errorf("read zstd:chunked manifest: %w", err) } layersMetadata, layersTarget, err := getLayersCache(store) if err != nil { @@ -204,7 +210,7 @@ func makeZstdChunkedDiffer(ctx context.Context, store storage.Store, blobSize in func makeEstargzChunkedDiffer(ctx context.Context, store storage.Store, blobSize int64, annotations map[string]string, iss ImageSourceSeekable) (*chunkedDiffer, error) { manifest, tocOffset, err := readEstargzChunkedManifest(iss, blobSize, annotations) if err != nil { - return nil, err + return nil, fmt.Errorf("read zstd:chunked manifest: %w", err) } layersMetadata, layersTarget, err := getLayersCache(store) if err != nil { @@ -230,21 +236,21 @@ func makeEstargzChunkedDiffer(ctx context.Context, store storage.Store, blobSize func copyFileFromOtherLayer(file *internal.FileMetadata, source string, otherFile *internal.FileMetadata, dirfd int, useHardLinks bool) (bool, *os.File, int64, error) { srcDirfd, err := unix.Open(source, unix.O_RDONLY, 0) if err != nil { - return false, nil, 0, err + return false, nil, 0, fmt.Errorf("open source file %q: %w", source, err) } defer unix.Close(srcDirfd) srcFile, err := openFileUnderRoot(otherFile.Name, srcDirfd, unix.O_RDONLY, 0) if err != nil { - return false, nil, 0, err + return false, nil, 0, fmt.Errorf("open source file %q under target rootfs: %w", otherFile.Name, err) } defer srcFile.Close() dstFile, written, err := copyFileContent(int(srcFile.Fd()), file.Name, dirfd, 0, useHardLinks) if err != nil { - return false, nil, 0, err + return false, nil, 0, fmt.Errorf("copy content to %q: %w", file.Name, err) } - return true, dstFile, written, err + return true, dstFile, written, nil } // canDedupMetadataWithHardLink says whether it is possible to deduplicate file with otherFile. @@ -280,10 +286,6 @@ func canDedupFileWithHardLink(file *internal.FileMetadata, fd int, s os.FileInfo return false } - xattrsToIgnore := map[string]interface{}{ - "security.selinux": true, - } - xattrs := make(map[string]string) for _, x := range listXattrs { v, err := system.Lgetxattr(path, x) @@ -510,7 +512,7 @@ type missingChunk struct { } // setFileAttrs sets the file attributes for file given metadata -func setFileAttrs(file *os.File, mode os.FileMode, metadata *internal.FileMetadata, options *archive.TarOptions) error { +func setFileAttrs(dirfd int, file *os.File, mode os.FileMode, metadata *internal.FileMetadata, options *archive.TarOptions, usePath bool) error { if file == nil || file.Fd() < 0 { return errors.Errorf("invalid file") } @@ -520,54 +522,237 @@ func setFileAttrs(file *os.File, mode os.FileMode, metadata *internal.FileMetada if err != nil { return err } + + // If it is a symlink, force to use the path if t == tar.TypeSymlink { - return nil + usePath = true + } + + baseName := "" + if usePath { + dirName := filepath.Dir(metadata.Name) + if dirName != "" { + parentFd, err := openFileUnderRoot(dirName, dirfd, unix.O_PATH|unix.O_DIRECTORY, 0) + if err != nil { + return err + } + defer parentFd.Close() + + dirfd = int(parentFd.Fd()) + } + baseName = filepath.Base(metadata.Name) + } + + doChown := func() error { + if usePath { + return unix.Fchownat(dirfd, baseName, metadata.UID, metadata.GID, unix.AT_SYMLINK_NOFOLLOW) + } + return unix.Fchown(fd, metadata.UID, metadata.GID) + } + + doSetXattr := func(k string, v []byte) error { + return unix.Fsetxattr(fd, k, v, 0) + } + + doUtimes := func() error { + ts := []unix.Timespec{timeToTimespec(metadata.AccessTime), timeToTimespec(metadata.ModTime)} + if usePath { + return unix.UtimesNanoAt(dirfd, baseName, ts, unix.AT_SYMLINK_NOFOLLOW) + } + return unix.UtimesNanoAt(unix.AT_FDCWD, fmt.Sprintf("/proc/self/fd/%d", fd), ts, 0) + } + + doChmod := func() error { + if usePath { + return unix.Fchmodat(dirfd, baseName, uint32(mode), unix.AT_SYMLINK_NOFOLLOW) + } + return unix.Fchmod(fd, uint32(mode)) } - if err := unix.Fchown(fd, metadata.UID, metadata.GID); err != nil { + if err := doChown(); err != nil { if !options.IgnoreChownErrors { - return err + return fmt.Errorf("chown %q to %d:%d: %w", metadata.Name, metadata.UID, metadata.GID, err) } } + canIgnore := func(err error) bool { + return err == nil || errors.Is(err, unix.ENOSYS) || errors.Is(err, unix.ENOTSUP) + } + for k, v := range metadata.Xattrs { + if _, found := xattrsToIgnore[k]; found { + continue + } data, err := base64.StdEncoding.DecodeString(v) if err != nil { - return err + return fmt.Errorf("decode xattr %q: %w", v, err) } - if err := unix.Fsetxattr(fd, k, data, 0); err != nil { - return err + if err := doSetXattr(k, data); !canIgnore(err) { + return fmt.Errorf("set xattr %s=%q for %q: %w", k, data, metadata.Name, err) } } - ts := []unix.Timespec{timeToTimespec(metadata.AccessTime), timeToTimespec(metadata.ModTime)} - if err := unix.UtimesNanoAt(fd, "", ts, 0); err != nil && errors.Is(err, unix.ENOSYS) { - return err + if err := doUtimes(); !canIgnore(err) { + return fmt.Errorf("set utimes for %q: %w", metadata.Name, err) } - if err := unix.Fchmod(fd, uint32(mode)); err != nil { - return err + if err := doChmod(); !canIgnore(err) { + return fmt.Errorf("chmod %q: %w", metadata.Name, err) } return nil } +func openFileUnderRootFallback(dirfd int, name string, flags uint64, mode os.FileMode) (int, error) { + root := fmt.Sprintf("/proc/self/fd/%d", dirfd) + + targetRoot, err := os.Readlink(root) + if err != nil { + return -1, err + } + + hasNoFollow := (flags & unix.O_NOFOLLOW) != 0 + + fd := -1 + // If O_NOFOLLOW is specified in the flags, then resolve only the parent directory and use the + // last component as the path to openat(). + if hasNoFollow { + dirName := filepath.Dir(name) + if dirName != "" { + newRoot, err := securejoin.SecureJoin(root, filepath.Dir(name)) + if err != nil { + return -1, err + } + root = newRoot + } + + parentDirfd, err := unix.Open(root, unix.O_PATH, 0) + if err != nil { + return -1, err + } + defer unix.Close(parentDirfd) + + fd, err = unix.Openat(parentDirfd, filepath.Base(name), int(flags), uint32(mode)) + if err != nil { + return -1, err + } + } else { + newPath, err := securejoin.SecureJoin(root, name) + if err != nil { + return -1, err + } + fd, err = unix.Openat(dirfd, newPath, int(flags), uint32(mode)) + if err != nil { + return -1, err + } + } + + target, err := os.Readlink(fmt.Sprintf("/proc/self/fd/%d", fd)) + if err != nil { + unix.Close(fd) + return -1, err + } + + // Add an additional check to make sure the opened fd is inside the rootfs + if !strings.HasPrefix(target, targetRoot) { + unix.Close(fd) + return -1, fmt.Errorf("error while resolving %q. It resolves outside the root directory", name) + } + + return fd, err +} + +func openFileUnderRootOpenat2(dirfd int, name string, flags uint64, mode os.FileMode) (int, error) { + how := unix.OpenHow{ + Flags: flags, + Mode: uint64(mode & 07777), + Resolve: unix.RESOLVE_IN_ROOT, + } + return unix.Openat2(dirfd, name, &how) +} + +// skipOpenat2 is set when openat2 is not supported by the underlying kernel and avoid +// using it again. +var skipOpenat2 int32 + +// openFileUnderRootRaw tries to open a file using openat2 and if it is not supported fallbacks to a +// userspace lookup. +func openFileUnderRootRaw(dirfd int, name string, flags uint64, mode os.FileMode) (int, error) { + var fd int + var err error + if atomic.LoadInt32(&skipOpenat2) > 0 { + fd, err = openFileUnderRootFallback(dirfd, name, flags, mode) + } else { + fd, err = openFileUnderRootOpenat2(dirfd, name, flags, mode) + // If the function failed with ENOSYS, switch off the support for openat2 + // and fallback to using safejoin. + if err != nil && errors.Is(err, unix.ENOSYS) { + atomic.StoreInt32(&skipOpenat2, 1) + fd, err = openFileUnderRootFallback(dirfd, name, flags, mode) + } + } + return fd, err +} + // openFileUnderRoot safely opens a file under the specified root directory using openat2 // name is the path to open relative to dirfd. // dirfd is an open file descriptor to the target checkout directory. -// flags are the flags top pass to the open syscall. +// flags are the flags to pass to the open syscall. // mode specifies the mode to use for newly created files. func openFileUnderRoot(name string, dirfd int, flags uint64, mode os.FileMode) (*os.File, error) { - how := unix.OpenHow{ - Flags: flags, - Mode: uint64(mode & 07777), - Resolve: unix.RESOLVE_IN_ROOT, + fd, err := openFileUnderRootRaw(dirfd, name, flags, mode) + if err == nil { + return os.NewFile(uintptr(fd), name), nil + } + + hasCreate := (flags & unix.O_CREAT) != 0 + if errors.Is(err, unix.ENOENT) && hasCreate { + parent := filepath.Dir(name) + if parent != "" { + newDirfd, err2 := openOrCreateDirUnderRoot(parent, dirfd, 0) + if err2 == nil { + defer newDirfd.Close() + fd, err := openFileUnderRootRaw(dirfd, name, flags, mode) + if err == nil { + return os.NewFile(uintptr(fd), name), nil + } + } + } } + return nil, fmt.Errorf("open %q under the rootfs: %w", name, err) +} - fd, err := unix.Openat2(dirfd, name, &how) - if err != nil { - return nil, err +// openOrCreateDirUnderRoot safely opens a directory or create it if it is missing. +// name is the path to open relative to dirfd. +// dirfd is an open file descriptor to the target checkout directory. +// mode specifies the mode to use for newly created files. +func openOrCreateDirUnderRoot(name string, dirfd int, mode os.FileMode) (*os.File, error) { + fd, err := openFileUnderRootRaw(dirfd, name, unix.O_DIRECTORY|unix.O_RDONLY, mode) + if err == nil { + return os.NewFile(uintptr(fd), name), nil + } + + if errors.Is(err, unix.ENOENT) { + parent := filepath.Dir(name) + if parent != "" { + pDir, err2 := openOrCreateDirUnderRoot(parent, dirfd, mode) + if err2 != nil { + return nil, err + } + defer pDir.Close() + + baseName := filepath.Base(name) + + if err2 := unix.Mkdirat(int(pDir.Fd()), baseName, 0755); err2 != nil { + return nil, err + } + + fd, err = openFileUnderRootRaw(int(pDir.Fd()), baseName, unix.O_DIRECTORY|unix.O_RDONLY, mode) + if err == nil { + return os.NewFile(uintptr(fd), name), nil + } + } } - return os.NewFile(uintptr(fd), name), nil + return nil, err } func (c *chunkedDiffer) createFileFromCompressedStream(dest string, dirfd int, reader io.Reader, mode os.FileMode, metadata *internal.FileMetadata, options *archive.TarOptions) (err error) { @@ -631,7 +816,7 @@ func (c *chunkedDiffer) createFileFromCompressedStream(dest string, dirfd int, r if digester.Digest() != manifestChecksum { return fmt.Errorf("checksum mismatch for %q", dest) } - return setFileAttrs(file, mode, metadata, options) + return setFileAttrs(dirfd, file, mode, metadata, options, false) } func (c *chunkedDiffer) storeMissingFiles(streams chan io.ReadCloser, errs chan error, dest string, dirfd int, missingChunks []missingChunk, options *archive.TarOptions) error { @@ -755,13 +940,13 @@ func (c *chunkedDiffer) retrieveMissingFiles(dest string, dirfd int, missingChun return nil } -func safeMkdir(dirfd int, mode os.FileMode, metadata *internal.FileMetadata, options *archive.TarOptions) error { - parent := filepath.Dir(metadata.Name) - base := filepath.Base(metadata.Name) +func safeMkdir(dirfd int, mode os.FileMode, name string, metadata *internal.FileMetadata, options *archive.TarOptions) error { + parent := filepath.Dir(name) + base := filepath.Base(name) parentFd := dirfd if parent != "." { - parentFile, err := openFileUnderRoot(parent, dirfd, unix.O_DIRECTORY|unix.O_PATH|unix.O_RDONLY, 0) + parentFile, err := openOrCreateDirUnderRoot(parent, dirfd, 0) if err != nil { return err } @@ -771,21 +956,21 @@ func safeMkdir(dirfd int, mode os.FileMode, metadata *internal.FileMetadata, opt if err := unix.Mkdirat(parentFd, base, uint32(mode)); err != nil { if !os.IsExist(err) { - return err + return fmt.Errorf("mkdir %q: %w", name, err) } } - file, err := openFileUnderRoot(metadata.Name, dirfd, unix.O_RDONLY, 0) + file, err := openFileUnderRoot(name, dirfd, unix.O_DIRECTORY|unix.O_RDONLY, 0) if err != nil { return err } defer file.Close() - return setFileAttrs(file, mode, metadata, options) + return setFileAttrs(dirfd, file, mode, metadata, options, false) } func safeLink(dirfd int, mode os.FileMode, metadata *internal.FileMetadata, options *archive.TarOptions) error { - sourceFile, err := openFileUnderRoot(metadata.Linkname, dirfd, unix.O_RDONLY, 0) + sourceFile, err := openFileUnderRoot(metadata.Linkname, dirfd, unix.O_PATH|unix.O_RDONLY|unix.O_NOFOLLOW, 0) if err != nil { return err } @@ -794,7 +979,7 @@ func safeLink(dirfd int, mode os.FileMode, metadata *internal.FileMetadata, opti destDir, destBase := filepath.Dir(metadata.Name), filepath.Base(metadata.Name) destDirFd := dirfd if destDir != "." { - f, err := openFileUnderRoot(destDir, dirfd, unix.O_RDONLY, 0) + f, err := openOrCreateDirUnderRoot(destDir, dirfd, 0) if err != nil { return err } @@ -804,23 +989,33 @@ func safeLink(dirfd int, mode os.FileMode, metadata *internal.FileMetadata, opti err = doHardLink(int(sourceFile.Fd()), destDirFd, destBase) if err != nil { - return err + return fmt.Errorf("create hardlink %q pointing to %q: %w", metadata.Name, metadata.Linkname, err) } - newFile, err := openFileUnderRoot(metadata.Name, dirfd, unix.O_WRONLY, 0) + newFile, err := openFileUnderRoot(metadata.Name, dirfd, unix.O_WRONLY|unix.O_NOFOLLOW, 0) if err != nil { + // If the target is a symlink, open the file with O_PATH. + if errors.Is(err, unix.ELOOP) { + newFile, err := openFileUnderRoot(metadata.Name, dirfd, unix.O_PATH|unix.O_NOFOLLOW, 0) + if err != nil { + return err + } + defer newFile.Close() + + return setFileAttrs(dirfd, newFile, mode, metadata, options, true) + } return err } defer newFile.Close() - return setFileAttrs(newFile, mode, metadata, options) + return setFileAttrs(dirfd, newFile, mode, metadata, options, false) } func safeSymlink(dirfd int, mode os.FileMode, metadata *internal.FileMetadata, options *archive.TarOptions) error { destDir, destBase := filepath.Dir(metadata.Name), filepath.Base(metadata.Name) destDirFd := dirfd if destDir != "." { - f, err := openFileUnderRoot(destDir, dirfd, unix.O_RDONLY, 0) + f, err := openOrCreateDirUnderRoot(destDir, dirfd, 0) if err != nil { return err } @@ -828,7 +1023,10 @@ func safeSymlink(dirfd int, mode os.FileMode, metadata *internal.FileMetadata, o destDirFd = int(f.Fd()) } - return unix.Symlinkat(metadata.Linkname, destDirFd, destBase) + if err := unix.Symlinkat(metadata.Linkname, destDirFd, destBase); err != nil { + return fmt.Errorf("create symlink %q pointing to %q: %w", metadata.Name, metadata.Linkname, err) + } + return nil } type whiteoutHandler struct { @@ -837,13 +1035,16 @@ type whiteoutHandler struct { } func (d whiteoutHandler) Setxattr(path, name string, value []byte) error { - file, err := openFileUnderRoot(path, d.Dirfd, unix.O_RDONLY, 0) + file, err := openOrCreateDirUnderRoot(path, d.Dirfd, 0) if err != nil { return err } defer file.Close() - return unix.Fsetxattr(int(file.Fd()), name, value, 0) + if err := unix.Fsetxattr(int(file.Fd()), name, value, 0); err != nil { + return fmt.Errorf("set xattr %s=%q for %q: %w", name, value, path, err) + } + return nil } func (d whiteoutHandler) Mknod(path string, mode uint32, dev int) error { @@ -852,7 +1053,7 @@ func (d whiteoutHandler) Mknod(path string, mode uint32, dev int) error { dirfd := d.Dirfd if dir != "" { - dir, err := openFileUnderRoot(dir, d.Dirfd, unix.O_RDONLY, 0) + dir, err := openOrCreateDirUnderRoot(dir, d.Dirfd, 0) if err != nil { return err } @@ -861,12 +1062,16 @@ func (d whiteoutHandler) Mknod(path string, mode uint32, dev int) error { dirfd = int(dir.Fd()) } - return unix.Mknodat(dirfd, base, mode, dev) + if err := unix.Mknodat(dirfd, base, mode, dev); err != nil { + return fmt.Errorf("mknod %q: %w", path, err) + } + + return nil } func checkChownErr(err error, name string, uid, gid int) error { if errors.Is(err, syscall.EINVAL) { - return errors.Wrapf(err, "potentially insufficient UIDs or GIDs available in user namespace (requested %d:%d for %s): Check /etc/subuid and /etc/subgid if configured locally", uid, gid, name) + return fmt.Errorf("potentially insufficient UIDs or GIDs available in user namespace (requested %d:%d for %s): Check /etc/subuid and /etc/subgid if configured locally: %w", uid, gid, name, err) } return err } @@ -961,7 +1166,7 @@ func (c *chunkedDiffer) ApplyDiff(dest string, options *archive.TarOptions) (gra dirfd, err := unix.Open(dest, unix.O_RDONLY|unix.O_PATH, 0) if err != nil { - return output, err + return output, fmt.Errorf("cannot open %q: %w", dest, err) } defer unix.Close(dirfd) @@ -1021,7 +1226,7 @@ func (c *chunkedDiffer) ApplyDiff(dest string, options *archive.TarOptions) (gra return err } defer file.Close() - if err := setFileAttrs(file, mode, &r, options); err != nil { + if err := setFileAttrs(dirfd, file, mode, &r, options, false); err != nil { return err } return nil @@ -1033,7 +1238,7 @@ func (c *chunkedDiffer) ApplyDiff(dest string, options *archive.TarOptions) (gra } case tar.TypeDir: - if err := safeMkdir(dirfd, mode, &r, options); err != nil { + if err := safeMkdir(dirfd, mode, r.Name, &r, options); err != nil { return output, err } continue @@ -1070,7 +1275,7 @@ func (c *chunkedDiffer) ApplyDiff(dest string, options *archive.TarOptions) (gra finalizeFile := func(dstFile *os.File) error { if dstFile != nil { defer dstFile.Close() - if err := setFileAttrs(dstFile, mode, &r, options); err != nil { + if err := setFileAttrs(dirfd, dstFile, mode, &r, options, false); err != nil { return err } } diff --git a/vendor/github.com/containers/storage/types/options.go b/vendor/github.com/containers/storage/types/options.go index 8a3858d9f..fe4274efd 100644 --- a/vendor/github.com/containers/storage/types/options.go +++ b/vendor/github.com/containers/storage/types/options.go @@ -42,6 +42,10 @@ func init() { defaultStoreOptions.GraphDriverName = "" if _, err := os.Stat(defaultOverrideConfigFile); err == nil { + // The DefaultConfigFile(rootless) function returns the path + // of the used storage.conf file, by returning defaultConfigFile + // If override exists containers/storage uses it by default. + defaultConfigFile = defaultOverrideConfigFile ReloadConfigurationFileIfNeeded(defaultOverrideConfigFile, &defaultStoreOptions) } else { if !os.IsNotExist(err) { diff --git a/vendor/github.com/jinzhu/copier/copier.go b/vendor/github.com/jinzhu/copier/copier.go index 412ff5497..6d21da869 100644 --- a/vendor/github.com/jinzhu/copier/copier.go +++ b/vendor/github.com/jinzhu/copier/copier.go @@ -348,10 +348,15 @@ func deepFields(reflectType reflect.Type) []reflect.StructField { for i := 0; i < reflectType.NumField(); i++ { v := reflectType.Field(i) - if v.Anonymous { - fields = append(fields, deepFields(v.Type)...) - } else { - fields = append(fields, v) + // PkgPath is the package path that qualifies a lower case (unexported) + // field name. It is empty for upper case (exported) field names. + // See https://golang.org/ref/spec#Uniqueness_of_identifiers + if v.PkgPath == "" { + if v.Anonymous { + fields = append(fields, deepFields(v.Type)...) + } else { + fields = append(fields, v) + } } } diff --git a/vendor/github.com/jinzhu/copier/go.mod b/vendor/github.com/jinzhu/copier/go.mod index 531422dcb..309801e9b 100644 --- a/vendor/github.com/jinzhu/copier/go.mod +++ b/vendor/github.com/jinzhu/copier/go.mod @@ -1,3 +1,3 @@ module github.com/jinzhu/copier -go 1.15 +go 1.13 diff --git a/vendor/github.com/mitchellh/mapstructure/CHANGELOG.md b/vendor/github.com/mitchellh/mapstructure/CHANGELOG.md index 9fe803a5e..38a099162 100644 --- a/vendor/github.com/mitchellh/mapstructure/CHANGELOG.md +++ b/vendor/github.com/mitchellh/mapstructure/CHANGELOG.md @@ -1,3 +1,7 @@ +## 1.4.3 + +* Fix cases where `json.Number` didn't decode properly [GH-261] + ## 1.4.2 * Custom name matchers to support any sort of casing, formatting, etc. for diff --git a/vendor/github.com/mitchellh/mapstructure/mapstructure.go b/vendor/github.com/mitchellh/mapstructure/mapstructure.go index dcee0f2d6..6b81b0067 100644 --- a/vendor/github.com/mitchellh/mapstructure/mapstructure.go +++ b/vendor/github.com/mitchellh/mapstructure/mapstructure.go @@ -684,16 +684,12 @@ func (d *Decoder) decodeUint(name string, data interface{}, val reflect.Value) e } case dataType.PkgPath() == "encoding/json" && dataType.Name() == "Number": jn := data.(json.Number) - i, err := jn.Int64() + i, err := strconv.ParseUint(string(jn), 0, 64) if err != nil { return fmt.Errorf( "error decoding json.Number into %s: %s", name, err) } - if i < 0 && !d.config.WeaklyTypedInput { - return fmt.Errorf("cannot parse '%s', %d overflows uint", - name, i) - } - val.SetUint(uint64(i)) + val.SetUint(i) default: return fmt.Errorf( "'%s' expected type '%s', got unconvertible type '%s', value: '%v'", diff --git a/vendor/github.com/opencontainers/image-spec/specs-go/v1/index.go b/vendor/github.com/opencontainers/image-spec/specs-go/v1/index.go index 4e6c4b236..82da6c6a8 100644 --- a/vendor/github.com/opencontainers/image-spec/specs-go/v1/index.go +++ b/vendor/github.com/opencontainers/image-spec/specs-go/v1/index.go @@ -21,6 +21,9 @@ import "github.com/opencontainers/image-spec/specs-go" type Index struct { specs.Versioned + // MediaType specificies the type of this document data structure e.g. `application/vnd.oci.image.index.v1+json` + MediaType string `json:"mediaType,omitempty"` + // Manifests references platform specific manifests. Manifests []Descriptor `json:"manifests"` diff --git a/vendor/github.com/opencontainers/image-spec/specs-go/v1/manifest.go b/vendor/github.com/opencontainers/image-spec/specs-go/v1/manifest.go index 7ff32c40b..d72d15ce4 100644 --- a/vendor/github.com/opencontainers/image-spec/specs-go/v1/manifest.go +++ b/vendor/github.com/opencontainers/image-spec/specs-go/v1/manifest.go @@ -20,6 +20,9 @@ import "github.com/opencontainers/image-spec/specs-go" type Manifest struct { specs.Versioned + // MediaType specificies the type of this document data structure e.g. `application/vnd.oci.image.manifest.v1+json` + MediaType string `json:"mediaType,omitempty"` + // Config references a configuration object for a container, by digest. // The referenced configuration object is a JSON blob that the runtime uses to set up the container. Config Descriptor `json:"config"` diff --git a/vendor/github.com/opencontainers/image-spec/specs-go/version.go b/vendor/github.com/opencontainers/image-spec/specs-go/version.go index 58f1095ab..31f99cf64 100644 --- a/vendor/github.com/opencontainers/image-spec/specs-go/version.go +++ b/vendor/github.com/opencontainers/image-spec/specs-go/version.go @@ -22,7 +22,7 @@ const ( // VersionMinor is for functionality in a backwards-compatible manner VersionMinor = 0 // VersionPatch is for backwards-compatible bug fixes - VersionPatch = 1 + VersionPatch = 2 // VersionDev indicates development branch. Releases will be empty string. VersionDev = "-dev" diff --git a/vendor/modules.txt b/vendor/modules.txt index a104465c6..04f15620c 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -106,7 +106,7 @@ github.com/containers/buildah/pkg/rusage github.com/containers/buildah/pkg/sshagent github.com/containers/buildah/pkg/util github.com/containers/buildah/util -# github.com/containers/common v0.46.1-0.20211125160015-ccf46abecd91 +# github.com/containers/common v0.46.1-0.20211202172647-e77d74bd1976 ## explicit github.com/containers/common/libimage github.com/containers/common/libimage/manifests @@ -219,7 +219,7 @@ github.com/containers/psgo/internal/dev github.com/containers/psgo/internal/host github.com/containers/psgo/internal/proc github.com/containers/psgo/internal/process -# github.com/containers/storage v1.37.1-0.20211122214631-59ba58582415 +# github.com/containers/storage v1.37.1-0.20211130181259-1a158c89a518 ## explicit github.com/containers/storage github.com/containers/storage/drivers @@ -437,7 +437,7 @@ github.com/imdario/mergo github.com/inconshreveable/mousetrap # github.com/ishidawataru/sctp v0.0.0-20210226210310-f2269e66cdee github.com/ishidawataru/sctp -# github.com/jinzhu/copier v0.3.2 +# github.com/jinzhu/copier v0.3.4 github.com/jinzhu/copier # github.com/json-iterator/go v1.1.12 ## explicit @@ -469,7 +469,7 @@ github.com/matttproud/golang_protobuf_extensions/pbutil github.com/miekg/pkcs11 # github.com/mistifyio/go-zfs v2.1.2-0.20190413222219-f784269be439+incompatible github.com/mistifyio/go-zfs -# github.com/mitchellh/mapstructure v1.4.2 +# github.com/mitchellh/mapstructure v1.4.3 github.com/mitchellh/mapstructure # github.com/moby/sys/mount v0.2.0 github.com/moby/sys/mount @@ -545,7 +545,7 @@ github.com/onsi/gomega/types # github.com/opencontainers/go-digest v1.0.0 ## explicit github.com/opencontainers/go-digest -# github.com/opencontainers/image-spec v1.0.2-0.20210819154149-5ad6f50d6283 +# github.com/opencontainers/image-spec v1.0.2-0.20211123152302-43a7dee1ec31 ## explicit github.com/opencontainers/image-spec/specs-go github.com/opencontainers/image-spec/specs-go/v1 |