summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile8
-rw-r--r--cmd/podman/images/save.go31
-rw-r--r--cmd/podman/images/utils_linux.go47
-rw-r--r--cmd/podman/images/utils_unsupported.go7
-rw-r--r--cmd/podman/root.go1
-rw-r--r--cmd/podman/system/connection/add.go13
-rw-r--r--contrib/msi/podman.wxs5
-rw-r--r--contrib/spec/podman.spec.in1
-rw-r--r--docs/source/Commands.rst2
-rw-r--r--docs/source/image.rst6
-rw-r--r--docs/source/managecontainers.rst4
-rw-r--r--docs/source/markdown/podman-create.1.md5
-rw-r--r--docs/source/markdown/podman-run.1.md16
-rw-r--r--docs/source/system.rst2
-rwxr-xr-xhack/xref-helpmsgs-manpages138
-rw-r--r--libpod/container_internal_linux.go2
-rw-r--r--libpod/events/config.go2
-rw-r--r--libpod/events/events.go2
-rw-r--r--libpod/networking_linux.go16
-rw-r--r--libpod/runtime_img.go13
-rw-r--r--nix/default.nix9
-rw-r--r--nix/nixpkgs.json6
-rw-r--r--pkg/bindings/connection.go10
-rw-r--r--pkg/domain/infra/tunnel/images.go13
-rw-r--r--pkg/network/network.go9
-rw-r--r--pkg/specgen/container_validate.go5
-rw-r--r--pkg/specgen/generate/container.go15
-rw-r--r--pkg/specgen/generate/container_create.go16
-rw-r--r--pkg/specgen/generate/security.go5
-rw-r--r--pkg/varlinkapi/create.go7
-rw-r--r--test/apiv2/35-networks.at28
-rw-r--r--test/e2e/run_networking_test.go16
-rw-r--r--test/e2e/run_test.go30
-rw-r--r--test/e2e/run_working_dir.go69
-rw-r--r--test/system/110-history.bats2
-rw-r--r--test/system/120-load.bats10
36 files changed, 493 insertions, 78 deletions
diff --git a/Makefile b/Makefile
index 70e4a49c7..4c9440fc5 100644
--- a/Makefile
+++ b/Makefile
@@ -305,7 +305,7 @@ testunit: libpodimage ## Run unittest on the built image
localunit: test/goecho/goecho varlink_generate
hack/check_root.sh make localunit
rm -rf ${COVERAGE_PATH} && mkdir -p ${COVERAGE_PATH}
- ginkgo \
+ $(GOBIN)/ginkgo \
-r \
$(TESTFLAGS) \
--skipPackage test/e2e,pkg/apparmor,test/endpoint,pkg/bindings,hack \
@@ -321,16 +321,16 @@ localunit: test/goecho/goecho varlink_generate
.PHONY: ginkgo
ginkgo:
- ginkgo -v $(TESTFLAGS) -tags "$(BUILDTAGS)" $(GINKGOTIMEOUT) -cover -flakeAttempts 3 -progress -trace -noColor -nodes 3 -debug test/e2e/. hack/.
+ $(GOBIN)/ginkgo -v $(TESTFLAGS) -tags "$(BUILDTAGS)" $(GINKGOTIMEOUT) -cover -flakeAttempts 3 -progress -trace -noColor -nodes 3 -debug test/e2e/. hack/.
.PHONY: ginkgo-remote
ginkgo-remote:
- ginkgo -v $(TESTFLAGS) -tags "$(REMOTETAGS)" $(GINKGOTIMEOUT) -cover -flakeAttempts 3 -progress -trace -noColor test/e2e/.
+ $(GOBIN)/ginkgo -v $(TESTFLAGS) -tags "$(REMOTETAGS)" $(GINKGOTIMEOUT) -cover -flakeAttempts 3 -progress -trace -noColor test/e2e/.
.PHONY: endpoint
ifneq (,$(findstring varlink,$(BUILDTAGS)))
endpoint:
- ginkgo -v $(TESTFLAGS) -tags "$(BUILDTAGS)" $(GINKGOTIMEOUT) -cover -flakeAttempts 3 -progress -trace -noColor -debug test/endpoint/.
+ $(GOBIN)/ginkgo -v $(TESTFLAGS) -tags "$(BUILDTAGS)" $(GINKGOTIMEOUT) -cover -flakeAttempts 3 -progress -trace -noColor -debug test/endpoint/.
endpoint:
endif
diff --git a/cmd/podman/images/save.go b/cmd/podman/images/save.go
index 024045b9d..82a3513f5 100644
--- a/cmd/podman/images/save.go
+++ b/cmd/podman/images/save.go
@@ -5,10 +5,9 @@ import (
"os"
"strings"
- "github.com/containers/podman/v2/libpod/define"
-
"github.com/containers/podman/v2/cmd/podman/parse"
"github.com/containers/podman/v2/cmd/podman/registry"
+ "github.com/containers/podman/v2/libpod/define"
"github.com/containers/podman/v2/pkg/domain/entities"
"github.com/containers/podman/v2/pkg/util"
"github.com/pkg/errors"
@@ -83,9 +82,10 @@ func saveFlags(flags *pflag.FlagSet) {
}
-func save(cmd *cobra.Command, args []string) error {
+func save(cmd *cobra.Command, args []string) (finalErr error) {
var (
- tags []string
+ tags []string
+ succeeded = false
)
if cmd.Flag("compress").Changed && (saveOpts.Format != define.OCIManifestDir && saveOpts.Format != define.V2s2ManifestDir && saveOpts.Format == "") {
return errors.Errorf("--compress can only be set when --format is either 'oci-dir' or 'docker-dir'")
@@ -95,7 +95,22 @@ func save(cmd *cobra.Command, args []string) error {
if terminal.IsTerminal(int(fi.Fd())) {
return errors.Errorf("refusing to save to terminal. Use -o flag or redirect")
}
- saveOpts.Output = "/dev/stdout"
+ pipePath, cleanup, err := setupPipe()
+ if err != nil {
+ return err
+ }
+ if cleanup != nil {
+ defer func() {
+ errc := cleanup()
+ if succeeded {
+ writeErr := <-errc
+ if writeErr != nil && finalErr == nil {
+ finalErr = writeErr
+ }
+ }
+ }()
+ }
+ saveOpts.Output = pipePath
}
if err := parse.ValidateFileName(saveOpts.Output); err != nil {
return err
@@ -103,5 +118,9 @@ func save(cmd *cobra.Command, args []string) error {
if len(args) > 1 {
tags = args[1:]
}
- return registry.ImageEngine().Save(context.Background(), args[0], tags, saveOpts)
+ err := registry.ImageEngine().Save(context.Background(), args[0], tags, saveOpts)
+ if err == nil {
+ succeeded = true
+ }
+ return err
}
diff --git a/cmd/podman/images/utils_linux.go b/cmd/podman/images/utils_linux.go
new file mode 100644
index 000000000..5521abab4
--- /dev/null
+++ b/cmd/podman/images/utils_linux.go
@@ -0,0 +1,47 @@
+package images
+
+import (
+ "io"
+ "io/ioutil"
+ "os"
+ "path/filepath"
+
+ "github.com/pkg/errors"
+ "github.com/sirupsen/logrus"
+ "golang.org/x/sys/unix"
+)
+
+// setupPipe for fixing https://github.com/containers/podman/issues/7017
+// uses named pipe since containers/image EvalSymlinks fails with /dev/stdout
+// the caller should use the returned function to clean up the pipeDir
+func setupPipe() (string, func() <-chan error, error) {
+ errc := make(chan error)
+ pipeDir, err := ioutil.TempDir(os.TempDir(), "pipeDir")
+ if err != nil {
+ return "", nil, err
+ }
+ pipePath := filepath.Join(pipeDir, "saveio")
+ err = unix.Mkfifo(pipePath, 0600)
+ if err != nil {
+ if e := os.RemoveAll(pipeDir); e != nil {
+ logrus.Errorf("error removing named pipe: %q", e)
+ }
+ return "", nil, errors.Wrapf(err, "error creating named pipe")
+ }
+ go func() {
+ fpipe, err := os.Open(pipePath)
+ if err != nil {
+ errc <- err
+ return
+ }
+ _, err = io.Copy(os.Stdout, fpipe)
+ fpipe.Close()
+ errc <- err
+ }()
+ return pipePath, func() <-chan error {
+ if e := os.RemoveAll(pipeDir); e != nil {
+ logrus.Errorf("error removing named pipe: %q", e)
+ }
+ return errc
+ }, nil
+}
diff --git a/cmd/podman/images/utils_unsupported.go b/cmd/podman/images/utils_unsupported.go
new file mode 100644
index 000000000..69d1df786
--- /dev/null
+++ b/cmd/podman/images/utils_unsupported.go
@@ -0,0 +1,7 @@
+// +build !linux
+
+package images
+
+func setupPipe() (string, func() <-chan error, error) {
+ return "/dev/stdout", nil, nil
+}
diff --git a/cmd/podman/root.go b/cmd/podman/root.go
index 2aa7267c2..dd9c75ece 100644
--- a/cmd/podman/root.go
+++ b/cmd/podman/root.go
@@ -290,6 +290,7 @@ func resolveDestination() (string, string) {
cfg, err := config.ReadCustomConfig()
if err != nil {
+ logrus.Warning(errors.Wrap(err, "unable to read local containers.conf"))
return registry.DefaultAPIAddress(), ""
}
diff --git a/cmd/podman/system/connection/add.go b/cmd/podman/system/connection/add.go
index 89cea10ca..af13b970c 100644
--- a/cmd/podman/system/connection/add.go
+++ b/cmd/podman/system/connection/add.go
@@ -124,6 +124,7 @@ func add(cmd *cobra.Command, args []string) error {
cfg.Engine.ServiceDestinations = map[string]config.Destination{
args[0]: dst,
}
+ cfg.Engine.ActiveService = args[0]
} else {
cfg.Engine.ServiceDestinations[args[0]] = dst
}
@@ -181,12 +182,20 @@ func getUDS(cmd *cobra.Command, uri *url.URL) (string, error) {
authMethods = append(authMethods, ssh.PublicKeysCallback(a.Signers))
}
- config := &ssh.ClientConfig{
+ if len(authMethods) == 0 {
+ pass, err := terminal.ReadPassword(fmt.Sprintf("%s's login password:", uri.User.Username()))
+ if err != nil {
+ return "", err
+ }
+ authMethods = append(authMethods, ssh.Password(string(pass)))
+ }
+
+ cfg := &ssh.ClientConfig{
User: uri.User.Username(),
Auth: authMethods,
HostKeyCallback: ssh.InsecureIgnoreHostKey(),
}
- dial, err := ssh.Dial("tcp", uri.Host, config)
+ dial, err := ssh.Dial("tcp", uri.Host, cfg)
if err != nil {
return "", errors.Wrapf(err, "failed to connect to %q", uri.Host)
}
diff --git a/contrib/msi/podman.wxs b/contrib/msi/podman.wxs
index c2c2cea4f..ff8160a53 100644
--- a/contrib/msi/podman.wxs
+++ b/contrib/msi/podman.wxs
@@ -24,8 +24,7 @@
<CreateFolder/>
</Component>
<Component Id="MainExecutable" Guid="73752F94-6589-4C7B-ABED-39D655A19714">
- <File Id="520C6E17-77A2-4F41-9611-30FA763A0702" Name="podman-remote-windows.exe" Source="bin/podman-remote-windows.exe"/>
- <File Id="A14218A0-4180-44AC-B109-7C63B3099DCA" Name="podman.bat" Source="podman.bat" KeyPath="yes"/>
+ <File Id="520C6E17-77A2-4F41-9611-30FA763A0702" Name="podman.exe" Source="bin/podman-remote-windows.exe" KeyPath="yes"/>
</Component>
</Directory>
</Directory>
@@ -33,7 +32,7 @@
</Directory>
<Property Id="setx" Value="setx.exe"/>
- <CustomAction Id="ChangePath" ExeCommand="PATH &quot;%PATH%;[INSTALLDIR] &quot;" Property="setx" Execute="deferred" Impersonate="yes" Return="check"/>
+ <CustomAction Id="ChangePath" ExeCommand="PATH &quot;%PATH%;[INSTALLDIR]&quot;" Property="setx" Execute="deferred" Impersonate="yes" Return="check"/>
<Feature Id="Complete" Level="1">
<ComponentRef Id="INSTALLDIR_Component"/>
diff --git a/contrib/spec/podman.spec.in b/contrib/spec/podman.spec.in
index 2411eaabc..363aa60d7 100644
--- a/contrib/spec/podman.spec.in
+++ b/contrib/spec/podman.spec.in
@@ -91,6 +91,7 @@ Recommends: container-selinux
Recommends: slirp4netns
Recommends: fuse-overlayfs
%endif
+Recommends: xz
# vendored libraries
# awk '{print "Provides: bundled(golang("$1")) = "$2}' vendor.conf | sort
diff --git a/docs/source/Commands.rst b/docs/source/Commands.rst
index a3ff24e89..096bdbedf 100644
--- a/docs/source/Commands.rst
+++ b/docs/source/Commands.rst
@@ -98,7 +98,7 @@ Commands
:doc:`top <markdown/podman-top.1>` Display the running processes of a container
-:doc:`umount <markdown/podman-umount.1>` Unmounts working container's root filesystem
+:doc:`unmount <markdown/podman-unmount.1>` Unmounts working container's root filesystem
:doc:`unpause <markdown/podman-unpause.1>` Unpause the processes in one or more containers
diff --git a/docs/source/image.rst b/docs/source/image.rst
index fe3a7aa3b..2b0ef3d43 100644
--- a/docs/source/image.rst
+++ b/docs/source/image.rst
@@ -18,7 +18,7 @@ Image
:doc:`load <markdown/podman-load.1>` Load an image from container archive
-:doc:`mount <markdown/podman-images-mount.1>` Mount an image's root filesystem.
+:doc:`mount <markdown/podman-image-mount.1>` Mount an image's root filesystem.
:doc:`prune <markdown/podman-image-prune.1>` Remove unused images
@@ -40,6 +40,6 @@ Image
:doc:`trust <markdown/podman-image-trust.1>` Manage container image trust policy
-:doc:`untag <markdown/podman-untag.1>` Removes one or more names from a locally-stored image
-
:doc:`unmount <markdown/podman-unmount.1>` Unmount an image's root filesystem
+
+:doc:`untag <markdown/podman-untag.1>` Removes one or more names from a locally-stored image
diff --git a/docs/source/managecontainers.rst b/docs/source/managecontainers.rst
index 2e787c9e9..849fd1d25 100644
--- a/docs/source/managecontainers.rst
+++ b/docs/source/managecontainers.rst
@@ -37,10 +37,10 @@ Manage Containers
:doc:`port <markdown/podman-port.1>` List port mappings or a specific mapping for the container
-:doc:`restart <markdown/podman-restart.1>` Restart one or more containers
-
:doc:`prune <markdown/podman-container-prune.1>` Remove all stopped containers
+:doc:`restart <markdown/podman-restart.1>` Restart one or more containers
+
:doc:`restore <markdown/podman-container-restore.1>` Restores one or more containers from a checkpoint
:doc:`rm <markdown/podman-rm.1>` Remove one or more containers
diff --git a/docs/source/markdown/podman-create.1.md b/docs/source/markdown/podman-create.1.md
index 5c58d59fc..9df76e48e 100644
--- a/docs/source/markdown/podman-create.1.md
+++ b/docs/source/markdown/podman-create.1.md
@@ -563,6 +563,7 @@ Valid values are:
- `private`: create a new namespace for the container (default)
- `slirp4netns[:OPTIONS,...]`: use slirp4netns to create a user network stack. This is the default for rootless containers. It is possible to specify these additional options:
- **allow_host_loopback=true|false**: Allow the slirp4netns to reach the host loopback IP (`10.0.2.2`). Default is false.
+ - **cidr=CIDR**: Specify ip range to use for this network. (Default is `10.0.2.0/24`).
- **enable_ipv6=true|false**: Enable IPv6. Default is false. (Required for `outbound_addr6`).
- **outbound_addr=INTERFACE**: Specify the outbound interface slirp should bind to (ipv4 traffic only).
- **outbound_addr=IPv4**: Specify the outbound ipv4 address slirp should bind to.
@@ -801,8 +802,8 @@ Run container in systemd mode. The default is *true*.
The value *always* enforces the systemd mode is enforced without
looking at the executable name. Otherwise, if set to true and the
-command you are running inside the container is systemd, /usr/sbin/init
-or /sbin/init.
+command you are running inside the container is systemd, /usr/sbin/init,
+/sbin/init or /usr/local/sbin/init.
If the command you are running inside of the container is systemd,
Podman will setup tmpfs mount points in the following directories:
diff --git a/docs/source/markdown/podman-run.1.md b/docs/source/markdown/podman-run.1.md
index db742e429..799cd1408 100644
--- a/docs/source/markdown/podman-run.1.md
+++ b/docs/source/markdown/podman-run.1.md
@@ -570,9 +570,15 @@ Valid _mode_ values are:
- **ns:**_path_: path to a network namespace to join;
- `private`: create a new namespace for the container (default)
- **slirp4netns[:OPTIONS,...]**: use **slirp4netns**(1) to create a user network stack. This is the default for rootless containers. It is possible to specify these additional options:
- **port_handler=rootlesskit**: Use rootlesskit for port forwarding. Default.
- **port_handler=slirp4netns**: Use the slirp4netns port forwarding.
- **allow_host_loopback=true|false**: Allow the slirp4netns to reach the host loopback IP (`10.0.2.2`). Default to false.
+ - **allow_host_loopback=true|false**: Allow the slirp4netns to reach the host loopback IP (`10.0.2.2`). Default is false.
+ - **cidr=CIDR**: Specify ip range to use for this network. (Default is `10.0.2.0/24`).
+ - **enable_ipv6=true|false**: Enable IPv6. Default is false. (Required for `outbound_addr6`).
+ - **outbound_addr=INTERFACE**: Specify the outbound interface slirp should bind to (ipv4 traffic only).
+ - **outbound_addr=IPv4**: Specify the outbound ipv4 address slirp should bind to.
+ - **outbound_addr6=INTERFACE**: Specify the outbound interface slirp should bind to (ipv6 traffic only).
+ - **outbound_addr6=IPv6**: Specify the outbound ipv6 address slirp should bind to.
+ - **port_handler=rootlesskit**: Use rootlesskit for port forwarding. Default.
+ - **port_handler=slirp4netns**: Use the slirp4netns port forwarding.
**--network-alias**=*alias*
@@ -831,8 +837,8 @@ Run container in systemd mode. The default is **true**.
The value *always* enforces the systemd mode is enforced without
looking at the executable name. Otherwise, if set to **true** and the
-command you are running inside the container is systemd, _/usr/sbin/init_
-or _/sbin/init_.
+command you are running inside the container is systemd, _/usr/sbin/init_,
+_/sbin/init_ or _/usr/local/sbin/init_.
If the command you are running inside of the container is systemd
Podman will setup tmpfs mount points in the following directories:
diff --git a/docs/source/system.rst b/docs/source/system.rst
index e3dfa9d01..566fd1a95 100644
--- a/docs/source/system.rst
+++ b/docs/source/system.rst
@@ -1,7 +1,7 @@
System
======
-:doc:`connection <markdown/podman-system-conection.1>` Manage the destination(s) for Podman service(s)
+:doc:`connection <connection>` Manage the destination(s) for Podman service(s)
:doc:`df <markdown/podman-system-df.1>` Show podman disk usage
diff --git a/hack/xref-helpmsgs-manpages b/hack/xref-helpmsgs-manpages
index 16b596589..7b617eed7 100755
--- a/hack/xref-helpmsgs-manpages
+++ b/hack/xref-helpmsgs-manpages
@@ -26,8 +26,14 @@ $| = 1;
my $Default_Podman = './bin/podman';
my $PODMAN = $ENV{PODMAN} || $Default_Podman;
+# Path to all doc files, including .rst and (down one level) markdown
+my $Docs_Path = 'docs/source';
+
# Path to podman markdown source files (of the form podman-*.1.md)
-my $Markdown_Path = 'docs/source/markdown';
+my $Markdown_Path = "$Docs_Path/markdown";
+
+# Global error count
+my $Errs = 0;
# END user-customizable section
###############################################################################
@@ -96,35 +102,38 @@ sub main {
my $help = podman_help();
my $man = podman_man('podman');
+ my $rst = podman_rst();
+
+ xref_by_help($help, $man);
+ xref_by_man($help, $man);
- my $retval = xref_by_help($help, $man)
- + xref_by_man($help, $man);
+ xref_rst($help, $rst);
- exit !!$retval;
+ exit !!$Errs;
}
+###############################################################################
+# BEGIN cross-referencing
+
##################
# xref_by_help # Find keys in '--help' but not in man
##################
sub xref_by_help {
my ($help, $man, @subcommand) = @_;
- my $errs = 0;
for my $k (sort keys %$help) {
if (exists $man->{$k}) {
if (ref $help->{$k}) {
- $errs += xref_by_help($help->{$k}, $man->{$k}, @subcommand, $k);
+ xref_by_help($help->{$k}, $man->{$k}, @subcommand, $k);
}
# Otherwise, non-ref is leaf node such as a --option
}
else {
my $man = $man->{_path} || 'man';
warn "$ME: podman @subcommand --help lists $k, but $k not in $man\n";
- ++$errs;
+ ++$Errs;
}
}
-
- return $errs;
}
#################
@@ -137,13 +146,11 @@ sub xref_by_help {
sub xref_by_man {
my ($help, $man, @subcommand) = @_;
- my $errs = 0;
-
# FIXME: this generates way too much output
for my $k (grep { $_ ne '_path' } sort keys %$man) {
if (exists $help->{$k}) {
if (ref $man->{$k}) {
- $errs += xref_by_man($help->{$k}, $man->{$k}, @subcommand, $k);
+ xref_by_man($help->{$k}, $man->{$k}, @subcommand, $k);
}
}
elsif ($k ne '--help' && $k ne '-h') {
@@ -175,13 +182,38 @@ sub xref_by_man {
next if "@subcommand" eq 'system' && $k eq 'service';
warn "$ME: podman @subcommand: $k in $man, but not --help\n";
- ++$errs;
+ ++$Errs;
}
}
+}
- return $errs;
+##############
+# xref_rst # Cross-check *.rst files against help
+##############
+sub xref_rst {
+ my ($help, $rst, @subcommand) = @_;
+
+ # Cross-check against rst (but only subcommands, not options).
+ # We key on $help because that is Absolute Truth: anything in podman --help
+ # must be referenced in an rst (the converse is not true).
+ for my $k (sort grep { $_ !~ /^-/ } keys %$help) {
+ # Check for subcommands, if any (eg podman system -> connection -> add)
+ if (ref $help->{$k}) {
+ xref_rst($help->{$k}, $rst->{$k}, @subcommand, $k);
+ }
+
+ # Check that command is mentioned in at least one .rst file
+ if (! exists $rst->{$k}{_desc}) {
+ my @podman = ("podman", @subcommand, $k);
+ warn "$ME: no link in *.rst for @podman\n";
+ ++$Errs;
+ }
+ }
}
+# END cross-referencing
+###############################################################################
+# BEGIN data gathering
#################
# podman_help # Parse output of 'podman [subcommand] --help'
@@ -249,6 +281,7 @@ sub podman_man {
or die "$ME: Cannot read $manpath: $!\n";
my $section = '';
my @most_recent_flags;
+ my $previous_subcmd = '';
while (my $line = <$fh>) {
chomp $line;
next unless $line; # skip empty lines
@@ -278,6 +311,11 @@ sub podman_man {
elsif ($line =~ /^\|\s+(\S+)\s+\|\s+\[\S+\]\((\S+)\.1\.md\)/) {
# $1 will be changed by recursion _*BEFORE*_ left-hand assignment
my $subcmd = $1;
+ if ($previous_subcmd gt $subcmd) {
+ warn "$ME: $subpath: '$previous_subcmd' and '$subcmd' are out of order\n";
+ ++$Errs;
+ }
+ $previous_subcmd = $subcmd;
$man{$subcmd} = podman_man($2);
}
}
@@ -315,4 +353,76 @@ sub podman_man {
}
+################
+# podman_rst # Parse contents of docs/source/*.rst
+################
+sub podman_rst {
+ my %rst;
+
+ # Read all .rst files, looking for ":doc:`subcmd <target>` description"
+ for my $rst (glob "$Docs_Path/*.rst") {
+ open my $fh, '<', $rst
+ or die "$ME: Cannot read $rst: $!\n";
+
+ # The basename of foo.rst is usually, but not always, the name of
+ # a podman subcommand. There are a few special cases:
+ (my $command = $rst) =~ s!^.*/(.*)\.rst!$1!;
+
+ my $subcommand_href = \%rst;
+ if ($command eq 'Commands') {
+ ;
+ }
+ elsif ($command eq 'managecontainers') {
+ $subcommand_href = $rst{container} //= { };
+ }
+ elsif ($command eq 'connection') {
+ $subcommand_href = $rst{system}{connection} //= { };
+ }
+ else {
+ $subcommand_href = $rst{$command} //= { };
+ }
+
+ my $previous_subcommand = '';
+ while (my $line = <$fh>) {
+ if ($line =~ /^:doc:`(\S+)\s+<(.*?)>`\s+(.*)/) {
+ my ($subcommand, $target, $desc) = ($1, $2, $3);
+
+ # Check that entries are in alphabetical order
+ if ($subcommand lt $previous_subcommand) {
+ warn "$ME: $rst:$.: '$previous_subcommand' and '$subcommand' are out of order\n";
+ ++$Errs;
+ }
+ $previous_subcommand = $subcommand;
+
+ # Mark this subcommand as documented.
+ $subcommand_href->{$subcommand}{_desc} = $desc;
+
+ # Check for invalid links. These will be one of two forms:
+ # <markdown/foo.1> -> markdown/foo.1.md
+ # <foo> -> foo.rst
+ if ($target =~ m!^markdown/!) {
+ if (! -e "$Docs_Path/$target.md") {
+ warn "$ME: $rst:$.: '$subcommand' links to nonexistent $target\n";
+ ++$Errs;
+ }
+ }
+ else {
+ if (! -e "$Docs_Path/$target.rst") {
+ warn "$ME: $rst:$.: '$subcommand' links to nonexistent $target.rst\n";
+ }
+ }
+ }
+ }
+ close $fh;
+ }
+
+ # Special case: 'image trust set/show' are documented in image-trust.1
+ $rst{image}{trust}{$_} = { _desc => 'ok' } for (qw(set show));
+
+ return \%rst;
+}
+
+# END data gathering
+###############################################################################
+
1;
diff --git a/libpod/container_internal_linux.go b/libpod/container_internal_linux.go
index 9fb9738dc..fdee3877c 100644
--- a/libpod/container_internal_linux.go
+++ b/libpod/container_internal_linux.go
@@ -626,7 +626,7 @@ func (c *Container) setupSystemd(mounts []spec.Mount, g generate.Generator) erro
Destination: "/sys/fs/cgroup/systemd",
Type: "bind",
Source: "/sys/fs/cgroup/systemd",
- Options: []string{"bind", "nodev", "noexec", "nosuid"},
+ Options: []string{"bind", "nodev", "noexec", "nosuid", "rprivate"},
}
g.AddMount(systemdMnt)
g.AddLinuxMaskedPaths("/sys/fs/cgroup/systemd/release_agent")
diff --git a/libpod/events/config.go b/libpod/events/config.go
index c34408e63..bb35c03c0 100644
--- a/libpod/events/config.go
+++ b/libpod/events/config.go
@@ -101,6 +101,8 @@ const (
Attach Status = "attach"
// AutoUpdate ...
AutoUpdate Status = "auto-update"
+ // Build ...
+ Build Status = "build"
// Checkpoint ...
Checkpoint Status = "checkpoint"
// Cleanup ...
diff --git a/libpod/events/events.go b/libpod/events/events.go
index 0253b1ee5..722c9595e 100644
--- a/libpod/events/events.go
+++ b/libpod/events/events.go
@@ -127,6 +127,8 @@ func StringToStatus(name string) (Status, error) {
switch name {
case Attach.String():
return Attach, nil
+ case Build.String():
+ return Build, nil
case Checkpoint.String():
return Checkpoint, nil
case Cleanup.String():
diff --git a/libpod/networking_linux.go b/libpod/networking_linux.go
index ed8f82c46..6f266e5d6 100644
--- a/libpod/networking_linux.go
+++ b/libpod/networking_linux.go
@@ -171,6 +171,7 @@ type slirpFeatures struct {
HasMTU bool
HasEnableSandbox bool
HasEnableSeccomp bool
+ HasCIDR bool
HasOutboundAddr bool
HasIPv6 bool
}
@@ -199,6 +200,7 @@ func checkSlirpFlags(path string) (*slirpFeatures, error) {
HasMTU: strings.Contains(string(out), "--mtu"),
HasEnableSandbox: strings.Contains(string(out), "--enable-sandbox"),
HasEnableSeccomp: strings.Contains(string(out), "--enable-seccomp"),
+ HasCIDR: strings.Contains(string(out), "--cidr"),
HasOutboundAddr: strings.Contains(string(out), "--outbound-addr"),
HasIPv6: strings.Contains(string(out), "--enable-ipv6"),
}, nil
@@ -227,6 +229,7 @@ func (r *Runtime) setupRootlessNetNS(ctr *Container) error {
havePortMapping := len(ctr.Config().PortMappings) > 0
logPath := filepath.Join(ctr.runtime.config.Engine.TmpDir, fmt.Sprintf("slirp4netns-%s.log", ctr.config.ID))
+ cidr := ""
isSlirpHostForward := false
disableHostLoopback := true
enableIPv6 := false
@@ -240,6 +243,12 @@ func (r *Runtime) setupRootlessNetNS(ctr *Container) error {
option, value := parts[0], parts[1]
switch option {
+ case "cidr":
+ ipv4, _, err := net.ParseCIDR(value)
+ if err != nil || ipv4.To4() == nil {
+ return errors.Errorf("invalid cidr %q", value)
+ }
+ cidr = value
case "port_handler":
switch value {
case "slirp4netns":
@@ -309,6 +318,13 @@ func (r *Runtime) setupRootlessNetNS(ctr *Container) error {
cmdArgs = append(cmdArgs, "--enable-seccomp")
}
+ if cidr != "" {
+ if !slirpFeatures.HasCIDR {
+ return errors.Errorf("cidr not supported")
+ }
+ cmdArgs = append(cmdArgs, fmt.Sprintf("--cidr=%s", cidr))
+ }
+
if enableIPv6 {
if !slirpFeatures.HasIPv6 {
return errors.Errorf("enable_ipv6 not supported")
diff --git a/libpod/runtime_img.go b/libpod/runtime_img.go
index 4b5129f44..a95cd1d7a 100644
--- a/libpod/runtime_img.go
+++ b/libpod/runtime_img.go
@@ -17,6 +17,7 @@ import (
"github.com/containers/image/v5/oci/layout"
"github.com/containers/image/v5/types"
"github.com/containers/podman/v2/libpod/define"
+ "github.com/containers/podman/v2/libpod/events"
"github.com/containers/podman/v2/libpod/image"
"github.com/containers/podman/v2/pkg/util"
"github.com/containers/storage"
@@ -150,9 +151,21 @@ func removeStorageContainers(ctrIDs []string, store storage.Store) error {
return nil
}
+// newBuildEvent creates a new event based on completion of a built image
+func (r *Runtime) newImageBuildCompleteEvent(idOrName string) {
+ e := events.NewEvent(events.Build)
+ e.Type = events.Image
+ e.Name = idOrName
+ if err := r.eventer.Write(e); err != nil {
+ logrus.Errorf("unable to write build event: %q", err)
+ }
+}
+
// Build adds the runtime to the imagebuildah call
func (r *Runtime) Build(ctx context.Context, options imagebuildah.BuildOptions, dockerfiles ...string) (string, reference.Canonical, error) {
id, ref, err := imagebuildah.BuildDockerfiles(ctx, r.store, options, dockerfiles...)
+ // Write event for build completion
+ r.newImageBuildCompleteEvent(id)
return id, ref, err
}
diff --git a/nix/default.nix b/nix/default.nix
index 4fe818b39..cc8786ce0 100644
--- a/nix/default.nix
+++ b/nix/default.nix
@@ -7,6 +7,15 @@ let
libassuan = (static pkg.libassuan);
libgpgerror = (static pkg.libgpgerror);
libseccomp = (static pkg.libseccomp);
+ glib = (static pkg.glib).overrideAttrs(x: {
+ outputs = [ "bin" "out" "dev" ];
+ mesonFlags = [
+ "-Ddefault_library=static"
+ "-Ddevbindir=${placeholder ''dev''}/bin"
+ "-Dgtk_doc=false"
+ "-Dnls=disabled"
+ ];
+ });
};
};
});
diff --git a/nix/nixpkgs.json b/nix/nixpkgs.json
index 8eeb4f470..976284ed4 100644
--- a/nix/nixpkgs.json
+++ b/nix/nixpkgs.json
@@ -1,7 +1,7 @@
{
"url": "https://github.com/nixos/nixpkgs",
- "rev": "b49e7987632e4c7ab3a093fdfc433e1826c4b9d7",
- "date": "2020-07-26T09:18:52+02:00",
- "sha256": "1mj6fy0p24izmasl653s5z4f2ka9v3b6mys45kjrqmkv889yk2r6",
+ "rev": "d6a445fe821052861b379d9b6c02d21623c25464",
+ "date": "2020-08-11T04:28:16+01:00",
+ "sha256": "064scwaxg8qg4xbmq07hag57saa4bhsb4pgg5h5vfs4nhhwvchg9",
"fetchSubmodules": false
}
diff --git a/pkg/bindings/connection.go b/pkg/bindings/connection.go
index e820e1c8b..ef9644de8 100644
--- a/pkg/bindings/connection.go
+++ b/pkg/bindings/connection.go
@@ -180,8 +180,9 @@ func pingNewConnection(ctx context.Context) error {
}
func sshClient(_url *url.URL, secure bool, passPhrase string, identity string) (Connection, error) {
+ // if you modify the authmethods or their conditionals, you will also need to make similar
+ // changes in the client (currently cmd/podman/system/connection/add getUDS).
authMethods := []ssh.AuthMethod{}
-
if len(identity) > 0 {
auth, err := terminal.PublicKey(identity, []byte(passPhrase))
if err != nil {
@@ -205,6 +206,13 @@ func sshClient(_url *url.URL, secure bool, passPhrase string, identity string) (
if pw, found := _url.User.Password(); found {
authMethods = append(authMethods, ssh.Password(pw))
}
+ if len(authMethods) == 0 {
+ pass, err := terminal.ReadPassword("Login password:")
+ if err != nil {
+ return Connection{}, err
+ }
+ authMethods = append(authMethods, ssh.Password(string(pass)))
+ }
callback := ssh.InsecureIgnoreHostKey()
if secure {
diff --git a/pkg/domain/infra/tunnel/images.go b/pkg/domain/infra/tunnel/images.go
index c7bfdcd2b..b255c5da4 100644
--- a/pkg/domain/infra/tunnel/images.go
+++ b/pkg/domain/infra/tunnel/images.go
@@ -9,6 +9,7 @@ import (
"os"
"path/filepath"
"strings"
+ "time"
"github.com/containers/common/pkg/config"
"github.com/containers/image/v5/docker/reference"
@@ -73,8 +74,16 @@ func (ir *ImageEngine) History(ctx context.Context, nameOrID string, opts entiti
}
for i, layer := range results {
- hold := entities.ImageHistoryLayer{}
- _ = utils.DeepCopy(&hold, layer)
+ // Created time comes over as an int64 so needs conversion to time.time
+ t := time.Unix(layer.Created, 0)
+ hold := entities.ImageHistoryLayer{
+ ID: layer.ID,
+ Created: t.UTC(),
+ CreatedBy: layer.CreatedBy,
+ Tags: layer.Tags,
+ Size: layer.Size,
+ Comment: layer.Comment,
+ }
history.Layers[i] = hold
}
return &history, nil
diff --git a/pkg/network/network.go b/pkg/network/network.go
index b24c72f5f..db625da56 100644
--- a/pkg/network/network.go
+++ b/pkg/network/network.go
@@ -137,6 +137,15 @@ func networkIntersect(n1, n2 *net.IPNet) bool {
// ValidateUserNetworkIsAvailable returns via an error if a network is available
// to be used
func ValidateUserNetworkIsAvailable(config *config.Config, userNet *net.IPNet) error {
+ if len(userNet.IP) == 0 || len(userNet.Mask) == 0 {
+ return errors.Errorf("network %s's ip or mask cannot be empty", userNet.String())
+ }
+
+ ones, bit := userNet.Mask.Size()
+ if ones == 0 || bit == 0 {
+ return errors.Errorf("network %s's mask is invalid", userNet.String())
+ }
+
networks, err := GetNetworksFromFilesystem(config)
if err != nil {
return err
diff --git a/pkg/specgen/container_validate.go b/pkg/specgen/container_validate.go
index 1a1bb4526..8289e2089 100644
--- a/pkg/specgen/container_validate.go
+++ b/pkg/specgen/container_validate.go
@@ -142,11 +142,6 @@ func (s *SpecGenerator) Validate() error {
return err
}
- // The following are defaults as needed by container creation
- if len(s.WorkDir) < 1 {
- s.WorkDir = "/"
- }
-
// Set defaults if network info is not provided
if s.NetNS.NSMode == "" {
s.NetNS.NSMode = Bridge
diff --git a/pkg/specgen/generate/container.go b/pkg/specgen/generate/container.go
index 65f8197bc..53d160442 100644
--- a/pkg/specgen/generate/container.go
+++ b/pkg/specgen/generate/container.go
@@ -135,15 +135,18 @@ func CompleteSpec(ctx context.Context, r *libpod.Runtime, s *specgen.SpecGenerat
s.Annotations = annotations
// workdir
- if newImage != nil {
- workingDir, err := newImage.WorkingDir(ctx)
- if err != nil {
- return nil, err
- }
- if len(s.WorkDir) < 1 && len(workingDir) > 1 {
+ if s.WorkDir == "" {
+ if newImage != nil {
+ workingDir, err := newImage.WorkingDir(ctx)
+ if err != nil {
+ return nil, err
+ }
s.WorkDir = workingDir
}
}
+ if s.WorkDir == "" {
+ s.WorkDir = "/"
+ }
if len(s.SeccompProfilePath) < 1 {
p, err := libpod.DefaultSeccompPath()
diff --git a/pkg/specgen/generate/container_create.go b/pkg/specgen/generate/container_create.go
index b61ac2c30..fda4c098c 100644
--- a/pkg/specgen/generate/container_create.go
+++ b/pkg/specgen/generate/container_create.go
@@ -164,13 +164,19 @@ func createContainerOptions(ctx context.Context, rt *libpod.Runtime, s *specgen.
}
if len(command) > 0 {
- if command[0] == "/usr/sbin/init" || command[0] == "/sbin/init" || (filepath.Base(command[0]) == "systemd") {
+ useSystemdCommands := map[string]bool{
+ "/sbin/init": true,
+ "/usr/sbin/init": true,
+ "/usr/local/sbin/init": true,
+ }
+ if useSystemdCommands[command[0]] || (filepath.Base(command[0]) == "systemd") {
useSystemd = true
}
}
default:
return nil, errors.Wrapf(err, "invalid value %q systemd option requires 'true, false, always'", s.Systemd)
}
+ logrus.Debugf("using systemd mode: %t", useSystemd)
if useSystemd {
// is StopSignal was not set by the user then set it to systemd
// expected StopSigal
@@ -241,13 +247,7 @@ func createContainerOptions(ctx context.Context, rt *libpod.Runtime, s *specgen.
// If the user did not set an workdir but the image did, ensure it is
// created.
if s.WorkDir == "" && img != nil {
- newWD, err := img.WorkingDir(ctx)
- if err != nil {
- return nil, err
- }
- if newWD != "" {
- options = append(options, libpod.WithCreateWorkingDir())
- }
+ options = append(options, libpod.WithCreateWorkingDir())
}
if s.StopSignal != nil {
options = append(options, libpod.WithStopSignal(*s.StopSignal))
diff --git a/pkg/specgen/generate/security.go b/pkg/specgen/generate/security.go
index 4352ef718..5e4cc3399 100644
--- a/pkg/specgen/generate/security.go
+++ b/pkg/specgen/generate/security.go
@@ -158,8 +158,9 @@ func securityConfigureGenerator(s *specgen.SpecGenerator, g *generate.Generator,
configSpec.Linux.Seccomp = seccompConfig
}
- // Clear default Seccomp profile from Generator for privileged containers
- if s.SeccompProfilePath == "unconfined" || s.Privileged {
+ // Clear default Seccomp profile from Generator for unconfined containers
+ // and privileged containers which do not specify a seccomp profile.
+ if s.SeccompProfilePath == "unconfined" || (s.Privileged && (s.SeccompProfilePath == config.SeccompOverridePath || s.SeccompProfilePath == config.SeccompDefaultPath)) {
configSpec.Linux.Seccomp = nil
}
diff --git a/pkg/varlinkapi/create.go b/pkg/varlinkapi/create.go
index 2d3e20f67..e9309a2d4 100644
--- a/pkg/varlinkapi/create.go
+++ b/pkg/varlinkapi/create.go
@@ -704,7 +704,12 @@ func ParseCreateOpts(ctx context.Context, c *GenericCLIResults, runtime *libpod.
if err != nil {
return nil, errors.Wrapf(err, "cannot parse bool %s", c.String("systemd"))
}
- if x && (command[0] == "/usr/sbin/init" || command[0] == "/sbin/init" || (filepath.Base(command[0]) == "systemd")) {
+ useSystemdCommands := map[string]bool{
+ "/sbin/init": true,
+ "/usr/sbin/init": true,
+ "/usr/local/sbin/init": true,
+ }
+ if x && (useSystemdCommands[command[0]] || (filepath.Base(command[0]) == "systemd")) {
systemd = true
}
}
diff --git a/test/apiv2/35-networks.at b/test/apiv2/35-networks.at
index fff3f3b1f..4c032c072 100644
--- a/test/apiv2/35-networks.at
+++ b/test/apiv2/35-networks.at
@@ -3,6 +3,32 @@
# network-related tests
#
-t GET /networks/non-existing-network 404
+t GET networks/non-existing-network 404 \
+ .cause='network not found'
+
+if root; then
+ t POST libpod/networks/create?name=network1 '' 200 \
+ .Filename~.*/network1\\.conflist
+
+ # --data '{"Subnet":{"IP":"10.10.254.0","Mask":[255,255,255,0]}}'
+ t POST libpod/networks/create?name=network2 '"Subnet":{"IP":"10.10.254.0","Mask":[255,255,255,0]}' 200 \
+ .Filename~.*/network2\\.conflist
+
+ # test for empty mask
+ t POST libpod/networks/create '"Subnet":{"IP":"10.10.1.0","Mask":[]}' 500 \
+ .cause~'.*cannot be empty'
+ # test for invalid mask
+ t POST libpod/networks/create '"Subnet":{"IP":"10.10.1.0","Mask":[0,255,255,0]}' 500 \
+ .cause~'.*mask is invalid'
+
+ # clean the network
+ t DELETE libpod/networks/network1 200 \
+ .[0].Name~network1 \
+ .[0].Err=null
+ t DELETE libpod/networks/network2 200 \
+ .[0].Name~network2 \
+ .[0].Err=null
+
+fi
# vim: filetype=sh
diff --git a/test/e2e/run_networking_test.go b/test/e2e/run_networking_test.go
index d735217d6..83befe730 100644
--- a/test/e2e/run_networking_test.go
+++ b/test/e2e/run_networking_test.go
@@ -293,6 +293,22 @@ var _ = Describe("Podman run networking", func() {
Expect(session.ExitCode()).To(Equal(0))
})
+ It("podman run slirp4netns network with different cidr", func() {
+ slirp4netnsHelp := SystemExec("slirp4netns", []string{"--help"})
+ Expect(slirp4netnsHelp.ExitCode()).To(Equal(0))
+
+ networkConfiguration := "slirp4netns:cidr=192.168.0.0/24,allow_host_loopback=true"
+ session := podmanTest.Podman([]string{"run", "--network", networkConfiguration, ALPINE, "ping", "-c1", "192.168.0.2"})
+ session.Wait(30)
+
+ if strings.Contains(slirp4netnsHelp.OutputToString(), "cidr") {
+ Expect(session.ExitCode()).To(Equal(0))
+ } else {
+ Expect(session.ExitCode()).ToNot(Equal(0))
+ Expect(session.ErrorToString()).To(ContainSubstring("cidr not supported"))
+ }
+ })
+
It("podman run network bind to 127.0.0.1", func() {
slirp4netnsHelp := SystemExec("slirp4netns", []string{"--help"})
Expect(slirp4netnsHelp.ExitCode()).To(Equal(0))
diff --git a/test/e2e/run_test.go b/test/e2e/run_test.go
index dc44d3b3f..30e565894 100644
--- a/test/e2e/run_test.go
+++ b/test/e2e/run_test.go
@@ -193,22 +193,46 @@ var _ = Describe("Podman run", func() {
Expect(conData[0].Config.Annotations["io.podman.annotations.init"]).To(Equal("FALSE"))
})
- It("podman run seccomp test", func() {
-
+ forbidGetCWDSeccompProfile := func() string {
in := []byte(`{"defaultAction":"SCMP_ACT_ALLOW","syscalls":[{"name":"getcwd","action":"SCMP_ACT_ERRNO"}]}`)
jsonFile, err := podmanTest.CreateSeccompJson(in)
if err != nil {
fmt.Println(err)
Skip("Failed to prepare seccomp.json for test.")
}
+ return jsonFile
+ }
+
+ It("podman run seccomp test", func() {
+ session := podmanTest.Podman([]string{"run", "-it", "--security-opt", strings.Join([]string{"seccomp=", forbidGetCWDSeccompProfile()}, ""), ALPINE, "pwd"})
+ session.WaitWithDefaultTimeout()
+ Expect(session).To(ExitWithError())
+ match, _ := session.GrepString("Operation not permitted")
+ Expect(match).Should(BeTrue())
+ })
- session := podmanTest.Podman([]string{"run", "-it", "--security-opt", strings.Join([]string{"seccomp=", jsonFile}, ""), ALPINE, "pwd"})
+ It("podman run seccomp test --privileged", func() {
+ session := podmanTest.Podman([]string{"run", "-it", "--privileged", "--security-opt", strings.Join([]string{"seccomp=", forbidGetCWDSeccompProfile()}, ""), ALPINE, "pwd"})
session.WaitWithDefaultTimeout()
Expect(session).To(ExitWithError())
match, _ := session.GrepString("Operation not permitted")
Expect(match).Should(BeTrue())
})
+ It("podman run seccomp test --privileged no profile should be unconfined", func() {
+ session := podmanTest.Podman([]string{"run", "-it", "--privileged", ALPINE, "grep", "Seccomp", "/proc/self/status"})
+ session.WaitWithDefaultTimeout()
+ Expect(session.OutputToString()).To(ContainSubstring("0"))
+ Expect(session.ExitCode()).To(Equal(0))
+ })
+
+ It("podman run seccomp test no profile should be default", func() {
+ session := podmanTest.Podman([]string{"run", "-it", ALPINE, "grep", "Seccomp", "/proc/self/status"})
+ session.WaitWithDefaultTimeout()
+ Expect(session.OutputToString()).To(ContainSubstring("2"))
+ Expect(session.ExitCode()).To(Equal(0))
+ })
+
It("podman run capabilities test", func() {
session := podmanTest.Podman([]string{"run", "--rm", "--cap-add", "all", ALPINE, "cat", "/proc/self/status"})
session.WaitWithDefaultTimeout()
diff --git a/test/e2e/run_working_dir.go b/test/e2e/run_working_dir.go
new file mode 100644
index 000000000..93330deba
--- /dev/null
+++ b/test/e2e/run_working_dir.go
@@ -0,0 +1,69 @@
+package integration
+
+import (
+ "os"
+ "strings"
+
+ . "github.com/containers/podman/v2/test/utils"
+ . "github.com/onsi/ginkgo"
+ . "github.com/onsi/gomega"
+)
+
+var _ = Describe("Podman run", func() {
+ var (
+ tempdir string
+ err error
+ podmanTest *PodmanTestIntegration
+ )
+
+ BeforeEach(func() {
+ tempdir, err = CreateTempDirInTempDir()
+ if err != nil {
+ os.Exit(1)
+ }
+ podmanTest = PodmanTestCreate(tempdir)
+ podmanTest.Setup()
+ podmanTest.SeedImages()
+ })
+
+ AfterEach(func() {
+ podmanTest.Cleanup()
+ f := CurrentGinkgoTestDescription()
+ processTestResult(f)
+
+ })
+
+ It("podman run a container without workdir", func() {
+ session := podmanTest.Podman([]string{"run", ALPINE, "pwd"})
+ session.WaitWithDefaultTimeout()
+ Expect(session.ExitCode()).To(Equal(0))
+ Expect(session.OutputToString()).To(Equal("/"))
+ })
+
+ It("podman run a container using non existing --workdir", func() {
+ if !strings.Contains(podmanTest.OCIRuntime, "crun") {
+ Skip("Test only works on crun")
+ }
+ session := podmanTest.Podman([]string{"run", "--workdir", "/home/foobar", ALPINE, "pwd"})
+ session.WaitWithDefaultTimeout()
+ Expect(session.ExitCode()).To(Equal(127))
+ })
+
+ It("podman run a container on an image with a workdir", func() {
+ SkipIfRemote()
+ dockerfile := `FROM alpine
+RUN mkdir -p /home/foobar
+WORKDIR /etc/foobar`
+ podmanTest.BuildImage(dockerfile, "test", "false")
+
+ session := podmanTest.Podman([]string{"run", "test", "pwd"})
+ session.WaitWithDefaultTimeout()
+ Expect(session.ExitCode()).To(Equal(0))
+ Expect(session.OutputToString()).To(Equal("/etc/foobar"))
+
+ session = podmanTest.Podman([]string{"run", "--workdir", "/home/foobar", "test", "pwd"})
+ session.WaitWithDefaultTimeout()
+ Expect(session.ExitCode()).To(Equal(0))
+ Expect(session.OutputToString()).To(Equal("/home/foobar"))
+ })
+})
diff --git a/test/system/110-history.bats b/test/system/110-history.bats
index b83e90fe4..5dc221d61 100644
--- a/test/system/110-history.bats
+++ b/test/system/110-history.bats
@@ -3,8 +3,6 @@
load helpers
@test "podman history - basic tests" {
- skip_if_remote "FIXME: pending #7122"
-
tests="
| .*[0-9a-f]\\\{12\\\} .* CMD .* LABEL
--format '{{.ID}} {{.Created}}' | .*[0-9a-f]\\\{12\\\} .* ago
diff --git a/test/system/120-load.bats b/test/system/120-load.bats
index 2fcabcd8a..14dae4c8a 100644
--- a/test/system/120-load.bats
+++ b/test/system/120-load.bats
@@ -26,6 +26,16 @@ verify_iid_and_name() {
is "$new_img_name" "$1" "Name & tag of restored image"
}
+@test "podman save to pipe and load" {
+ # We can't use run_podman because that uses the BATS 'run' function
+ # which redirects stdout and stderr. Here we need to guarantee
+ # that podman's stdout is a pipe, not any other form of redirection
+ $PODMAN save --format oci-archive $IMAGE | cat >$PODMAN_TMPDIR/test.tar
+ [ $status -eq 0 ]
+
+ run_podman load -i $PODMAN_TMPDIR/test.tar
+}
+
@test "podman load - by image ID" {
# FIXME: how to build a simple archive instead?