summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rwxr-xr-xAPI.md5
-rw-r--r--cmd/podman/cliconfig/config.go4
-rw-r--r--cmd/podman/commands.go2
-rw-r--r--cmd/podman/container.go2
-rw-r--r--cmd/podman/cp.go80
-rw-r--r--cmd/podman/main_remote.go20
-rw-r--r--cmd/podman/ps.go2
-rw-r--r--cmd/podman/shared/intermediate_varlink.go3
-rw-r--r--cmd/podman/varlink.go34
-rw-r--r--cmd/podman/varlink/io.podman.varlink5
-rw-r--r--cmd/podman/version.go50
-rwxr-xr-xcontrib/cirrus/rootless_test.sh2
-rw-r--r--docs/podman-cp.1.md3
-rw-r--r--docs/podman-create.1.md69
-rw-r--r--docs/podman-run.1.md69
-rw-r--r--docs/podman-varlink.1.md19
-rw-r--r--libpod/container_internal.go2
-rw-r--r--libpod/lock/in_memory_locks.go16
-rw-r--r--libpod/lock/lock.go7
-rw-r--r--libpod/lock/shm/shm_lock.c55
-rw-r--r--libpod/lock/shm/shm_lock.go17
-rw-r--r--libpod/lock/shm/shm_lock.h1
-rw-r--r--libpod/lock/shm_lock_manager_linux.go19
-rw-r--r--libpod/pod_internal.go2
-rw-r--r--libpod/runtime.go2
-rw-r--r--libpod/runtime_pod_linux.go126
-rw-r--r--pkg/adapter/client.go35
-rw-r--r--pkg/adapter/client_config.go4
-rw-r--r--pkg/adapter/containers.go12
-rw-r--r--pkg/adapter/containers_remote.go35
-rw-r--r--pkg/adapter/errors.go31
-rw-r--r--pkg/adapter/info_remote.go7
-rw-r--r--pkg/adapter/pods_remote.go6
-rw-r--r--pkg/adapter/runtime.go8
-rw-r--r--pkg/adapter/runtime_remote.go50
-rw-r--r--pkg/varlinkapi/attach.go1
-rw-r--r--pkg/varlinkapi/containers.go17
-rw-r--r--pkg/varlinkapi/pods.go6
-rw-r--r--pkg/varlinkapi/system.go17
-rw-r--r--test/e2e/common_test.go7
-rw-r--r--test/e2e/cp_test.go75
-rw-r--r--test/e2e/create_test.go2
-rw-r--r--test/e2e/diff_test.go3
-rw-r--r--test/e2e/info_test.go1
-rw-r--r--test/e2e/kill_test.go2
-rw-r--r--test/e2e/libpod_suite_remoteclient_test.go74
-rw-r--r--test/e2e/libpod_suite_test.go3
-rw-r--r--test/e2e/logs_test.go2
-rw-r--r--test/e2e/negative_test.go38
-rw-r--r--test/e2e/pod_create_test.go2
-rw-r--r--test/e2e/pod_inspect_test.go2
-rw-r--r--test/e2e/pod_kill_test.go2
-rw-r--r--test/e2e/pod_pause_test.go2
-rw-r--r--test/e2e/pod_prune_test.go2
-rw-r--r--test/e2e/pod_ps_test.go2
-rw-r--r--test/e2e/pod_restart_test.go2
-rw-r--r--test/e2e/pod_rm_test.go2
-rw-r--r--test/e2e/pod_start_test.go2
-rw-r--r--test/e2e/pod_stop_test.go2
-rw-r--r--test/e2e/ps_test.go2
-rw-r--r--test/e2e/refresh_test.go74
-rw-r--r--test/e2e/rm_test.go2
-rw-r--r--test/e2e/run_test.go1
-rw-r--r--test/e2e/save_test.go10
-rw-r--r--test/e2e/stop_test.go2
-rw-r--r--test/e2e/tag_test.go2
-rw-r--r--test/e2e/version_test.go4
-rw-r--r--test/e2e/wait_test.go2
-rw-r--r--test/utils/utils.go6
-rw-r--r--vendor.conf2
-rw-r--r--vendor/github.com/varlink/go/varlink/bridge.go11
-rw-r--r--vendor/github.com/varlink/go/varlink/bridge_windows.go11
72 files changed, 831 insertions, 370 deletions
diff --git a/API.md b/API.md
index 11b9a462f..db84f695c 100755
--- a/API.md
+++ b/API.md
@@ -259,6 +259,8 @@ in the [API.md](https://github.com/containers/libpod/blob/master/API.md) file in
[error ContainerNotFound](#ContainerNotFound)
+[error ErrCtrStopped](#ErrCtrStopped)
+
[error ErrorOccurred](#ErrorOccurred)
[error ImageNotFound](#ImageNotFound)
@@ -1978,6 +1980,9 @@ force [bool](https://godoc.org/builtin#bool)
### <a name="ContainerNotFound"></a>type ContainerNotFound
ContainerNotFound means the container could not be found by the provided name or ID in local storage.
+### <a name="ErrCtrStopped"></a>type ErrCtrStopped
+
+Container is already stopped
### <a name="ErrorOccurred"></a>type ErrorOccurred
ErrorOccurred is a generic error for an error that occurs during the execution. The actual error message
diff --git a/cmd/podman/cliconfig/config.go b/cmd/podman/cliconfig/config.go
index b770aaca0..aaa4513d8 100644
--- a/cmd/podman/cliconfig/config.go
+++ b/cmd/podman/cliconfig/config.go
@@ -32,6 +32,10 @@ type MainFlags struct {
CpuProfile string
LogLevel string
TmpDir string
+
+ RemoteUserName string
+ RemoteHost string
+ VarlinkAddress string
}
type AttachValues struct {
diff --git a/cmd/podman/commands.go b/cmd/podman/commands.go
index 14451d944..3a409f503 100644
--- a/cmd/podman/commands.go
+++ b/cmd/podman/commands.go
@@ -39,13 +39,11 @@ func getImageSubCommands() []*cobra.Command {
func getContainerSubCommands() []*cobra.Command {
return []*cobra.Command{
- _checkpointCommand,
_cleanupCommand,
_commitCommand,
_execCommand,
_mountCommand,
_refreshCommand,
- _restoreCommand,
_runlabelCommand,
_statsCommand,
_umountCommand,
diff --git a/cmd/podman/container.go b/cmd/podman/container.go
index bbf01d1f8..530175a55 100644
--- a/cmd/podman/container.go
+++ b/cmd/podman/container.go
@@ -51,6 +51,7 @@ var (
// Commands that are universally implemented.
containerCommands = []*cobra.Command{
_attachCommand,
+ _checkpointCommand,
_containerExistsCommand,
_contInspectSubCommand,
_diffCommand,
@@ -64,6 +65,7 @@ var (
_portCommand,
_pruneContainersCommand,
_restartCommand,
+ _restoreCommand,
_runCommand,
_rmCommand,
_startCommand,
diff --git a/cmd/podman/cp.go b/cmd/podman/cp.go
index 82f2d3f20..4cb8a8c54 100644
--- a/cmd/podman/cp.go
+++ b/cmd/podman/cp.go
@@ -1,7 +1,9 @@
package main
import (
+ "archive/tar"
"fmt"
+ "io"
"os"
"path/filepath"
"strings"
@@ -142,7 +144,11 @@ func copyBetweenHostAndContainer(runtime *libpod.Runtime, src string, dest strin
var lastError error
for _, src := range glob {
- err := copy(src, destPath, dest, idMappingOpts, &containerOwner, extract)
+ if src == "-" {
+ src = os.Stdin.Name()
+ extract = true
+ }
+ err := copy(src, destPath, dest, idMappingOpts, &containerOwner, extract, isFromHostToCtr)
if lastError != nil {
logrus.Error(lastError)
}
@@ -195,7 +201,7 @@ func getPathInfo(path string) (string, os.FileInfo, error) {
return path, srcfi, nil
}
-func copy(src, destPath, dest string, idMappingOpts storage.IDMappingOptions, chownOpts *idtools.IDPair, extract bool) error {
+func copy(src, destPath, dest string, idMappingOpts storage.IDMappingOptions, chownOpts *idtools.IDPair, extract, isFromHostToCtr bool) error {
srcPath, err := filepath.EvalSymlinks(src)
if err != nil {
return errors.Wrapf(err, "error evaluating symlinks %q", srcPath)
@@ -205,6 +211,16 @@ func copy(src, destPath, dest string, idMappingOpts storage.IDMappingOptions, ch
if err != nil {
return err
}
+
+ filename := filepath.Base(destPath)
+ if filename == "-" && !isFromHostToCtr {
+ err := streamFileToStdout(srcPath, srcfi)
+ if err != nil {
+ return errors.Wrapf(err, "error streaming source file %s to Stdout", srcPath)
+ }
+ return nil
+ }
+
destdir := destPath
if !srcfi.IsDir() && !strings.HasSuffix(dest, string(os.PathSeparator)) {
destdir = filepath.Dir(destPath)
@@ -224,7 +240,6 @@ func copy(src, destPath, dest string, idMappingOpts storage.IDMappingOptions, ch
untarPath := chrootarchive.UntarPathAndChown(chownOpts, digest.Canonical.Digester().Hash(), idMappingOpts.UIDMap, idMappingOpts.GIDMap)
if srcfi.IsDir() {
-
logrus.Debugf("copying %q to %q", srcPath+string(os.PathSeparator)+"*", dest+string(os.PathSeparator)+"*")
if destDirIsExist && !strings.HasSuffix(src, fmt.Sprintf("%s.", string(os.PathSeparator))) {
destPath = filepath.Join(destPath, filepath.Base(srcPath))
@@ -276,3 +291,62 @@ func convertIDMap(idMaps []idtools.IDMap) (convertedIDMap []specs.LinuxIDMapping
}
return convertedIDMap
}
+
+func streamFileToStdout(srcPath string, srcfi os.FileInfo) error {
+ if srcfi.IsDir() {
+ tw := tar.NewWriter(os.Stdout)
+ err := filepath.Walk(srcPath, func(path string, info os.FileInfo, err error) error {
+ if err != nil || !info.Mode().IsRegular() || path == srcPath {
+ return err
+ }
+ hdr, err := tar.FileInfoHeader(info, "")
+ if err != nil {
+ return err
+ }
+
+ if err = tw.WriteHeader(hdr); err != nil {
+ return err
+ }
+ fh, err := os.Open(path)
+ if err != nil {
+ return err
+ }
+ defer fh.Close()
+
+ _, err = io.Copy(tw, fh)
+ return err
+ })
+ if err != nil {
+ return errors.Wrapf(err, "error streaming directory %s to Stdout", srcPath)
+ }
+ return nil
+ }
+
+ file, err := os.Open(srcPath)
+ if err != nil {
+ return errors.Wrapf(err, "error opening file %s", srcPath)
+ }
+ defer file.Close()
+ if !archive.IsArchivePath(srcPath) {
+ tw := tar.NewWriter(os.Stdout)
+ hdr, err := tar.FileInfoHeader(srcfi, "")
+ if err != nil {
+ return err
+ }
+ err = tw.WriteHeader(hdr)
+ if err != nil {
+ return err
+ }
+ _, err = io.Copy(tw, file)
+ if err != nil {
+ return errors.Wrapf(err, "error streaming archive %s to Stdout", srcPath)
+ }
+ return nil
+ }
+
+ _, err = io.Copy(os.Stdout, file)
+ if err != nil {
+ return errors.Wrapf(err, "error streaming file to Stdout")
+ }
+ return nil
+}
diff --git a/cmd/podman/main_remote.go b/cmd/podman/main_remote.go
index a3335050a..c8bb3ad3e 100644
--- a/cmd/podman/main_remote.go
+++ b/cmd/podman/main_remote.go
@@ -3,17 +3,17 @@
package main
import (
- "os"
-
- "github.com/containers/libpod/pkg/rootless"
- "github.com/sirupsen/logrus"
"github.com/spf13/cobra"
)
const remote = true
func init() {
- // remote client specific flags can go here.
+ rootCmd.PersistentFlags().StringVar(&MainGlobalOpts.RemoteUserName, "username", "", "username on the remote host")
+ rootCmd.PersistentFlags().StringVar(&MainGlobalOpts.RemoteHost, "remote-host", "", "remote host")
+ // TODO maybe we allow the altering of this for bridge connections?
+ //rootCmd.PersistentFlags().StringVar(&MainGlobalOpts.VarlinkAddress, "varlink-address", adapter.DefaultAddress, "address of the varlink socket")
+ rootCmd.PersistentFlags().StringVar(&MainGlobalOpts.LogLevel, "log-level", "error", "Log messages above specified level: debug, info, warn, error, fatal or panic")
}
func setSyslog() error {
@@ -29,16 +29,6 @@ func profileOff(cmd *cobra.Command) error {
}
func setupRootless(cmd *cobra.Command, args []string) error {
- if rootless.IsRootless() {
- became, ret, err := rootless.BecomeRootInUserNS()
- if err != nil {
- logrus.Errorf(err.Error())
- os.Exit(1)
- }
- if became {
- os.Exit(ret)
- }
- }
return nil
}
diff --git a/cmd/podman/ps.go b/cmd/podman/ps.go
index 623f17050..eb5181126 100644
--- a/cmd/podman/ps.go
+++ b/cmd/podman/ps.go
@@ -497,7 +497,7 @@ func psDisplay(c *cliconfig.PsValues, runtime *adapter.LocalRuntime) error {
} else {
// Print namespace information
- ns := shared.GetNamespaces(container.Pid)
+ ns := runtime.GetNamespaces(container)
fmt.Fprintf(w, "\n%s\t%s\t%d\t%s\t%s\t%s\t%s\t%s\t%s\t%s", container.ID, container.Names, container.Pid, ns.Cgroup, ns.IPC, ns.MNT, ns.NET, ns.PIDNS, ns.User, ns.UTS)
}
diff --git a/cmd/podman/shared/intermediate_varlink.go b/cmd/podman/shared/intermediate_varlink.go
index 5e21245e3..4742d4909 100644
--- a/cmd/podman/shared/intermediate_varlink.go
+++ b/cmd/podman/shared/intermediate_varlink.go
@@ -3,8 +3,6 @@
package shared
import (
- "fmt"
-
"github.com/containers/libpod/cmd/podman/cliconfig"
"github.com/containers/libpod/cmd/podman/varlink"
"github.com/containers/libpod/pkg/rootless"
@@ -209,7 +207,6 @@ func boolFromVarlink(v *bool, flagName string, defaultValue bool) CRBool {
cr.Val = defaultValue
cr.Changed = false
} else {
- fmt.Println(flagName, cr.Val)
cr.Val = *v
cr.Changed = true
}
diff --git a/cmd/podman/varlink.go b/cmd/podman/varlink.go
index 978678a84..215542d2c 100644
--- a/cmd/podman/varlink.go
+++ b/cmd/podman/varlink.go
@@ -3,11 +3,17 @@
package main
import (
+ "fmt"
+ "os"
+ "path/filepath"
"time"
"github.com/containers/libpod/cmd/podman/cliconfig"
"github.com/containers/libpod/cmd/podman/libpodruntime"
iopodman "github.com/containers/libpod/cmd/podman/varlink"
+ "github.com/containers/libpod/pkg/adapter"
+ "github.com/containers/libpod/pkg/rootless"
+ "github.com/containers/libpod/pkg/util"
"github.com/containers/libpod/pkg/varlinkapi"
"github.com/containers/libpod/version"
"github.com/pkg/errors"
@@ -45,13 +51,31 @@ func init() {
}
func varlinkCmd(c *cliconfig.VarlinkValues) error {
- args := c.InputArgs
- if len(args) < 1 {
- return errors.Errorf("you must provide a varlink URI")
+ varlinkURI := adapter.DefaultAddress
+ if rootless.IsRootless() {
+ xdg, err := util.GetRootlessRuntimeDir()
+ if err != nil {
+ return err
+ }
+ socketDir := filepath.Join(xdg, "podman/io.podman")
+ if _, err := os.Stat(filepath.Dir(socketDir)); os.IsNotExist(err) {
+ if err := os.Mkdir(filepath.Dir(socketDir), 0755); err != nil {
+ return err
+ }
+ }
+ varlinkURI = fmt.Sprintf("unix:%s", socketDir)
}
+ args := c.InputArgs
+
if len(args) > 1 {
- return errors.Errorf("too many arguments. Requires exactly 1")
+ return errors.Errorf("too many arguments. you may optionally provide 1")
+ }
+
+ if len(args) > 0 {
+ varlinkURI = args[0]
}
+
+ logrus.Debugf("Using varlink socket: %s", varlinkURI)
timeout := time.Duration(c.Timeout) * time.Millisecond
// Create a single runtime for varlink
@@ -81,7 +105,7 @@ func varlinkCmd(c *cliconfig.VarlinkValues) error {
}
// Run the varlink server at the given address
- if err = service.Listen(args[0], timeout); err != nil {
+ if err = service.Listen(varlinkURI, timeout); err != nil {
switch err.(type) {
case varlink.ServiceTimeoutError:
logrus.Infof("varlink service expired (use --timeout to increase session time beyond %d ms, 0 means never timeout)", c.Int64("timeout"))
diff --git a/cmd/podman/varlink/io.podman.varlink b/cmd/podman/varlink/io.podman.varlink
index ace81646c..faaecdb6b 100644
--- a/cmd/podman/varlink/io.podman.varlink
+++ b/cmd/podman/varlink/io.podman.varlink
@@ -1254,4 +1254,7 @@ error ErrorOccurred (reason: string)
error RuntimeError (reason: string)
# The Podman endpoint requires that you use a streaming connection.
-error WantsMoreRequired (reason: string) \ No newline at end of file
+error WantsMoreRequired (reason: string)
+
+# Container is already stopped
+error ErrCtrStopped (id: string)
diff --git a/cmd/podman/version.go b/cmd/podman/version.go
index e964bdbb5..439a1cca6 100644
--- a/cmd/podman/version.go
+++ b/cmd/podman/version.go
@@ -2,6 +2,7 @@ package main
import (
"fmt"
+ "io"
"os"
"strings"
"text/tabwriter"
@@ -10,6 +11,7 @@ import (
"github.com/containers/buildah/pkg/formats"
"github.com/containers/libpod/cmd/podman/cliconfig"
"github.com/containers/libpod/libpod"
+ "github.com/containers/libpod/pkg/adapter"
"github.com/pkg/errors"
"github.com/spf13/cobra"
)
@@ -38,7 +40,7 @@ func init() {
// versionCmd gets and prints version info for version command
func versionCmd(c *cliconfig.VersionValues) error {
- output, err := libpod.GetVersion()
+ clientVersion, err := libpod.GetVersion()
if err != nil {
errors.Wrapf(err, "unable to determine version")
}
@@ -51,25 +53,49 @@ func versionCmd(c *cliconfig.VersionValues) error {
var out formats.Writer
switch versionOutputFormat {
case formats.JSONString:
- out = formats.JSONStruct{Output: output}
+ out = formats.JSONStruct{Output: clientVersion}
default:
- out = formats.StdoutTemplate{Output: output, Template: versionOutputFormat}
+ out = formats.StdoutTemplate{Output: clientVersion, Template: versionOutputFormat}
}
return formats.Writer(out).Out()
}
w := tabwriter.NewWriter(os.Stdout, 0, 0, 2, ' ', 0)
defer w.Flush()
- fmt.Fprintf(w, "Version:\t%s\n", output.Version)
- fmt.Fprintf(w, "RemoteAPI Version:\t%d\n", output.RemoteAPIVersion)
- fmt.Fprintf(w, "Go Version:\t%s\n", output.GoVersion)
- if output.GitCommit != "" {
- fmt.Fprintf(w, "Git Commit:\t%s\n", output.GitCommit)
+
+ if remote {
+ fmt.Fprintf(w, "Client:\n")
+ }
+ formatVersion(w, clientVersion)
+
+ if remote {
+ fmt.Fprintf(w, "\nService:\n")
+
+ runtime, err := adapter.GetRuntime(getContext(), nil)
+ if err != nil {
+ return errors.Wrapf(err, "could not get runtime")
+ }
+ defer runtime.Shutdown(false)
+
+ serviceVersion, err := runtime.GetVersion()
+ if err != nil {
+ return err
+ }
+ formatVersion(w, serviceVersion)
+ }
+ return nil
+}
+
+func formatVersion(writer io.Writer, version libpod.Version) {
+ fmt.Fprintf(writer, "Version:\t%s\n", version.Version)
+ fmt.Fprintf(writer, "RemoteAPI Version:\t%d\n", version.RemoteAPIVersion)
+ fmt.Fprintf(writer, "Go Version:\t%s\n", version.GoVersion)
+ if version.GitCommit != "" {
+ fmt.Fprintf(writer, "Git Commit:\t%s\n", version.GitCommit)
}
// Prints out the build time in readable format
- if output.Built != 0 {
- fmt.Fprintf(w, "Built:\t%s\n", time.Unix(output.Built, 0).Format(time.ANSIC))
+ if version.Built != 0 {
+ fmt.Fprintf(writer, "Built:\t%s\n", time.Unix(version.Built, 0).Format(time.ANSIC))
}
- fmt.Fprintf(w, "OS/Arch:\t%s\n", output.OsArch)
- return nil
+ fmt.Fprintf(writer, "OS/Arch:\t%s\n", version.OsArch)
}
diff --git a/contrib/cirrus/rootless_test.sh b/contrib/cirrus/rootless_test.sh
index 8351214f3..eab06bac0 100755
--- a/contrib/cirrus/rootless_test.sh
+++ b/contrib/cirrus/rootless_test.sh
@@ -14,8 +14,6 @@ then
exit 1
fi
-export PODMAN_VARLINK_ADDRESS=unix:/tmp/podman-$(id -u)
-
echo "."
echo "Hello, my name is $USER and I live in $PWD can I be your friend?"
diff --git a/docs/podman-cp.1.md b/docs/podman-cp.1.md
index 44612003d..406dd51df 100644
--- a/docs/podman-cp.1.md
+++ b/docs/podman-cp.1.md
@@ -8,6 +8,7 @@ podman\-cp - Copy files/folders between a container and the local filesystem
## DESCRIPTION
Copies the contents of **src_path** to the **dest_path**. You can copy from the containers's filesystem to the local machine or the reverse, from the local filesystem to the container.
+If - is specified for either the SRC_PATH or DEST_PATH, you can also stream a tar archive from STDIN or to STDOUT.
The CONTAINER can be a running or stopped container. The **src_path** or **dest_path** can be a file or directory.
@@ -107,5 +108,7 @@ podman cp containerID:/home/myuser/. /home/myuser/
podman cp --extract /home/myuser/myfiles.tar.gz containerID:/myfiles
+podman cp - containerID:/myfiles.tar.gz < myfiles.tar.gz
+
## SEE ALSO
podman(1), podman-mount(1), podman-umount(1)
diff --git a/docs/podman-create.1.md b/docs/podman-create.1.md
index 851f5cf3d..cf36106e8 100644
--- a/docs/podman-create.1.md
+++ b/docs/podman-create.1.md
@@ -244,26 +244,6 @@ inside of the container.
Read in a line delimited file of environment variables
-**--http-proxy**=*true*|*false*
-
-By default proxy environment variables are passed into the container if set
-for the podman process. This can be disabled by setting the `--http-proxy`
-option to `false`. The environment variables passed in include `http_proxy`,
-`https_proxy`, `ftp_proxy`, `no_proxy`, and also the upper case versions of
-those. This option is only needed when the host system must use a proxy but
-the container should not use any proxy. Proxy environment variables specified
-for the container in any other way will override the values that would have
-been passed thru from the host. (Other ways to specify the proxy for the
-container include passing the values with the `--env` flag, or hardcoding the
-proxy environment at container build time.)
-
-For example, to disable passing these environment variables from host to
-container:
-
-`--http-proxy=false`
-
-Defaults to `true`
-
**--expose**=[]
Expose a port, or a range of ports (e.g. --expose=3300-3310) to set up port redirection
@@ -281,20 +261,27 @@ Add additional groups to run as
**--healthcheck**=""
-Set or alter a healthcheck for a container. The value must be of the format of:
+Set or alter a healthcheck command for a container. The command is a command to be executed inside your
+container that determines your container health. The command is required for other healthcheck options
+to be applied. A value of `none` disables existing healthchecks.
+
+**--healthcheck-interval**=""
+
+Set an interval for the healthchecks (a value of `disable` results in no automatic timer setup) (default "30s")
- `[OPTIONS] CMD command`
+**--healthcheck-retries=**
- where options can be any of the follow:
- * --interval=DURATION (default: 30s)
- * --timeout=DURATION (default: 30s)
- * --start-period=DURATION (default: 0s)
- * --retries=N (default: 3)
+The number of retries allowed before a healthcheck is considered to be unhealthy. The default value is `3`.
-Note: options are *not* required.
+**--healthcheck-start-period**=""
-The command is a command to be executed inside your container that determines your container health. The
-command is required.
+The initialization time needed for a container to bootstrap. The value can be expressed in time format like
+`2m3s`. The default value is `0s`
+
+**--healthcheck-timeout**=""
+
+The maximum time allowed to complete the healthcheck before an interval is considered failed. Like start-period, the
+value can be expressed in a time format such as `1m22s`. The default value is `30s`.
**--hostname**=""
@@ -306,6 +293,26 @@ Sets the container host name that is available inside the container.
Print usage statement
+**--http-proxy**=*true*|*false*
+
+By default proxy environment variables are passed into the container if set
+for the podman process. This can be disabled by setting the `--http-proxy`
+option to `false`. The environment variables passed in include `http_proxy`,
+`https_proxy`, `ftp_proxy`, `no_proxy`, and also the upper case versions of
+those. This option is only needed when the host system must use a proxy but
+the container should not use any proxy. Proxy environment variables specified
+for the container in any other way will override the values that would have
+been passed thru from the host. (Other ways to specify the proxy for the
+container include passing the values with the `--env` flag, or hardcoding the
+proxy environment at container build time.)
+
+For example, to disable passing these environment variables from host to
+container:
+
+`--http-proxy=false`
+
+Defaults to `true`
+
**--image-volume**, **builtin-volume**=*bind*|*tmpfs*|*ignore*
Tells podman how to handle the builtin image volumes. The options are: 'bind', 'tmpfs', or 'ignore' (default 'bind').
@@ -585,7 +592,7 @@ Automatically remove the container when it exits. The default is *false*.
Note that the container will not be removed when it could not be created or
started successfully. This allows the user to inspect the container after
-failure. The `--rm` flag is incompatible with the `-d` flag.
+failure.
**--rootfs**
diff --git a/docs/podman-run.1.md b/docs/podman-run.1.md
index db90ce50e..1840e0f0b 100644
--- a/docs/podman-run.1.md
+++ b/docs/podman-run.1.md
@@ -251,26 +251,6 @@ inside of the container.
Read in a line delimited file of environment variables
-**--http-proxy**=*true*|*false*
-
-By default proxy environment variables are passed into the container if set
-for the podman process. This can be disabled by setting the `--http-proxy`
-option to `false`. The environment variables passed in include `http_proxy`,
-`https_proxy`, `ftp_proxy`, `no_proxy`, and also the upper case versions of
-those. This option is only needed when the host system must use a proxy but
-the container should not use any proxy. Proxy environment variables specified
-for the container in any other way will override the values that would have
-been passed thru from the host. (Other ways to specify the proxy for the
-container include passing the values with the `--env` flag, or hardcoding the
-proxy environment at container build time.)
-
-For example, to disable passing these environment variables from host to
-container:
-
-`--http-proxy=false`
-
-Defaults to `true`
-
**--expose**=[]
Expose a port, or a range of ports (e.g. --expose=3300-3310) to set up port redirection
@@ -289,20 +269,27 @@ Add additional groups to run as
**--healthcheck**=""
-Set or alter a healthcheck for a container. The value must be of the format of:
+Set or alter a healthcheck command for a container. The command is a command to be executed inside your
+container that determines your container health. The command is required for other healthcheck options
+to be applied. A value of `none` disables existing healthchecks.
+
+**--healthcheck-interval**=""
+
+Set an interval for the healthchecks (a value of `disable` results in no automatic timer setup) (default "30s")
- `[OPTIONS] CMD command`
+**--healthcheck-retries=**
- where options can be any of the follow:
- * --interval=DURATION (default: 30s)
- * --timeout=DURATION (default: 30s)
- * --start-period=DURATION (default: 0s)
- * --retries=N (default: 3)
+The number of retries allowed before a healthcheck is considered to be unhealthy. The default value is `3`.
-Note: options are *not* required.
+**--healthcheck-start-period**=""
-The command is a command to be executed inside your container that determines your container health. The
-command is required.
+The initialization time needed for a container to bootstrap. The value can be expressed in time format like
+`2m3s`. The default value is `0s`
+
+**--healthcheck-timeout**=""
+
+The maximum time allowed to complete the healthcheck before an interval is considered failed. Like start-period, the
+value can be expressed in a time format such as `1m22s`. The default value is `30s`.
**--hostname**=""
@@ -314,6 +301,26 @@ Sets the container host name that is available inside the container.
Print usage statement
+**--http-proxy**=*true*|*false*
+
+By default proxy environment variables are passed into the container if set
+for the podman process. This can be disabled by setting the `--http-proxy`
+option to `false`. The environment variables passed in include `http_proxy`,
+`https_proxy`, `ftp_proxy`, `no_proxy`, and also the upper case versions of
+those. This option is only needed when the host system must use a proxy but
+the container should not use any proxy. Proxy environment variables specified
+for the container in any other way will override the values that would have
+been passed thru from the host. (Other ways to specify the proxy for the
+container include passing the values with the `--env` flag, or hardcoding the
+proxy environment at container build time.)
+
+For example, to disable passing these environment variables from host to
+container:
+
+`--http-proxy=false`
+
+Defaults to `true`
+
**--image-volume**, **builtin-volume**=*bind*|*tmpfs*|*ignore*
Tells podman how to handle the builtin image volumes.
@@ -607,7 +614,7 @@ Automatically remove the container when it exits. The default is *false*.
Note that the container will not be removed when it could not be created or
started successfully. This allows the user to inspect the container after
-failure. The `--rm` flag is incompatible with the `-d` flag.
+failure.
**--rootfs**
diff --git a/docs/podman-varlink.1.md b/docs/podman-varlink.1.md
index 7eeb3dd66..0501d853f 100644
--- a/docs/podman-varlink.1.md
+++ b/docs/podman-varlink.1.md
@@ -7,8 +7,10 @@ podman\-varlink - Runs the varlink backend interface
**podman varlink** [*options*] *uri*
## DESCRIPTION
-Starts the varlink service listening on *uri* that allows varlink clients to interact with podman. This should generally be done
-with systemd. See _Configuration_ below.
+Starts the varlink service listening on *uri* that allows varlink clients to interact with podman. If no *uri* is provided, a default
+URI will be used depending on the user calling the varlink service. The default for the root user is `unix:/run/podman/io.podman`. Regular
+users will have a default *uri* of `$XDG_RUNTIME_DIR/podman/io.podman`. For example, `unix:/run/user/1000/podman/io.podman`
+The varlink service should generally be done with systemd. See _Configuration_ below.
## GLOBAL OPTIONS
@@ -23,16 +25,23 @@ second. A value of `0` means no timeout and the session will not expire.
## EXAMPLES
-Run the podman varlink service manually and accept the default timeout.
+Run the podman varlink service accepting all default options.
```
-$ podman varlink unix:/run/podman/io.podman
+$ podman varlink
+```
+
+
+Run the podman varlink service with an alternate URI and accept the default timeout.
+
+```
+$ podman varlink unix:/tmp/io.podman
```
Run the podman varlink service manually with a 5 second timeout.
```
-$ podman varlink --timeout 5000 unix:/run/podman/io.podman
+$ podman varlink --timeout 5000
```
## CONFIGURATION
diff --git a/libpod/container_internal.go b/libpod/container_internal.go
index 0b4e5763e..e6ffaa6d7 100644
--- a/libpod/container_internal.go
+++ b/libpod/container_internal.go
@@ -516,7 +516,7 @@ func (c *Container) refresh() error {
}
// We need to pick up a new lock
- lock, err := c.runtime.lockManager.RetrieveLock(c.config.LockID)
+ lock, err := c.runtime.lockManager.AllocateAndRetrieveLock(c.config.LockID)
if err != nil {
return errors.Wrapf(err, "error acquiring lock for container %s", c.ID())
}
diff --git a/libpod/lock/in_memory_locks.go b/libpod/lock/in_memory_locks.go
index 7c9605917..f3c842f89 100644
--- a/libpod/lock/in_memory_locks.go
+++ b/libpod/lock/in_memory_locks.go
@@ -90,6 +90,22 @@ func (m *InMemoryManager) RetrieveLock(id uint32) (Locker, error) {
return m.locks[id], nil
}
+// AllocateAndRetrieveLock allocates a lock with the given ID (if not already in
+// use) and returns it.
+func (m *InMemoryManager) AllocateAndRetrieveLock(id uint32) (Locker, error) {
+ if id >= m.numLocks {
+ return nil, errors.Errorf("given lock ID %d is too large - this manager only supports lock indexes up to %d", id, m.numLocks)
+ }
+
+ if m.locks[id].allocated {
+ return nil, errors.Errorf("given lock ID %d is already in use, cannot reallocate", id)
+ }
+
+ m.locks[id].allocated = true
+
+ return m.locks[id], nil
+}
+
// FreeAllLocks frees all locks.
// This function is DANGEROUS. Please read the full comment in locks.go before
// trying to use it.
diff --git a/libpod/lock/lock.go b/libpod/lock/lock.go
index d6841646b..4e1e2e215 100644
--- a/libpod/lock/lock.go
+++ b/libpod/lock/lock.go
@@ -24,6 +24,13 @@ type Manager interface {
// The underlying lock MUST be the same as another other lock with the
// same UUID.
RetrieveLock(id uint32) (Locker, error)
+ // AllocateAndRetrieveLock marks the lock with the given UUID as in use
+ // and retrieves it.
+ // RetrieveAndAllocateLock will error if the lock in question has
+ // already been allocated.
+ // This is mostly used after a system restart to repopulate the list of
+ // locks in use.
+ AllocateAndRetrieveLock(id uint32) (Locker, error)
// PLEASE READ FULL DESCRIPTION BEFORE USING.
// FreeAllLocks frees all allocated locks, in preparation for lock
// reallocation.
diff --git a/libpod/lock/shm/shm_lock.c b/libpod/lock/shm/shm_lock.c
index d11fce71a..047d3c417 100644
--- a/libpod/lock/shm/shm_lock.c
+++ b/libpod/lock/shm/shm_lock.c
@@ -354,6 +354,61 @@ int64_t allocate_semaphore(shm_struct_t *shm) {
return -1 * ENOSPC;
}
+// Allocate the semaphore with the given ID.
+// Returns an error if the semaphore with this ID does not exist, or has already
+// been allocated.
+// Returns 0 on success, or negative errno values on failure.
+int32_t allocate_given_semaphore(shm_struct_t *shm, uint32_t sem_index) {
+ int bitmap_index, index_in_bitmap, ret_code;
+ bitmap_t test_map;
+
+ if (shm == NULL) {
+ return -1 * EINVAL;
+ }
+
+ // Check if the lock index is valid
+ if (sem_index >= shm->num_locks) {
+ return -1 * EINVAL;
+ }
+
+ bitmap_index = sem_index / BITMAP_SIZE;
+ index_in_bitmap = sem_index % BITMAP_SIZE;
+
+ // This should never happen if the sem_index test above succeeded, but better
+ // safe than sorry
+ if (bitmap_index >= shm->num_bitmaps) {
+ return -1 * EFAULT;
+ }
+
+ test_map = 0x1 << index_in_bitmap;
+
+ // Lock the mutex controlling access to our shared memory
+ ret_code = take_mutex(&(shm->segment_lock));
+ if (ret_code != 0) {
+ return -1 * ret_code;
+ }
+
+ // Check if the semaphore is allocated
+ if ((test_map & shm->locks[bitmap_index].bitmap) != 0) {
+ ret_code = release_mutex(&(shm->segment_lock));
+ if (ret_code != 0) {
+ return -1 * ret_code;
+ }
+
+ return -1 * EEXIST;
+ }
+
+ // The semaphore is not allocated, allocate it
+ shm->locks[bitmap_index].bitmap = shm->locks[bitmap_index].bitmap | test_map;
+
+ ret_code = release_mutex(&(shm->segment_lock));
+ if (ret_code != 0) {
+ return -1 * ret_code;
+ }
+
+ return 0;
+}
+
// Deallocate a given semaphore
// Returns 0 on success, negative ERRNO values on failure
int32_t deallocate_semaphore(shm_struct_t *shm, uint32_t sem_index) {
diff --git a/libpod/lock/shm/shm_lock.go b/libpod/lock/shm/shm_lock.go
index e70ea8743..c21e9a221 100644
--- a/libpod/lock/shm/shm_lock.go
+++ b/libpod/lock/shm/shm_lock.go
@@ -134,6 +134,23 @@ func (locks *SHMLocks) AllocateSemaphore() (uint32, error) {
return uint32(retCode), nil
}
+// AllocateGivenSemaphore allocates the given semaphore from the shared-memory
+// segment for use by a container or pod.
+// If the semaphore is already in use or the index is invalid an error will be
+// returned.
+func (locks *SHMLocks) AllocateGivenSemaphore(sem uint32) error {
+ if !locks.valid {
+ return errors.Wrapf(syscall.EINVAL, "locks have already been closed")
+ }
+
+ retCode := C.allocate_given_semaphore(locks.lockStruct, C.uint32_t(sem))
+ if retCode < 0 {
+ return syscall.Errno(-1 * retCode)
+ }
+
+ return nil
+}
+
// DeallocateSemaphore frees a semaphore in a shared-memory segment so it can be
// reallocated to another container or pod.
// The given semaphore must be already allocated, or an error will be returned.
diff --git a/libpod/lock/shm/shm_lock.h b/libpod/lock/shm/shm_lock.h
index 58e4297e2..759f8178a 100644
--- a/libpod/lock/shm/shm_lock.h
+++ b/libpod/lock/shm/shm_lock.h
@@ -39,6 +39,7 @@ shm_struct_t *setup_lock_shm(char *path, uint32_t num_locks, int *error_code);
shm_struct_t *open_lock_shm(char *path, uint32_t num_locks, int *error_code);
int32_t close_lock_shm(shm_struct_t *shm);
int64_t allocate_semaphore(shm_struct_t *shm);
+int32_t allocate_given_semaphore(shm_struct_t *shm, uint32_t sem_index);
int32_t deallocate_semaphore(shm_struct_t *shm, uint32_t sem_index);
int32_t deallocate_all_semaphores(shm_struct_t *shm);
int32_t lock_semaphore(shm_struct_t *shm, uint32_t sem_index);
diff --git a/libpod/lock/shm_lock_manager_linux.go b/libpod/lock/shm_lock_manager_linux.go
index 8678958ee..5f31939f8 100644
--- a/libpod/lock/shm_lock_manager_linux.go
+++ b/libpod/lock/shm_lock_manager_linux.go
@@ -57,6 +57,25 @@ func (m *SHMLockManager) AllocateLock() (Locker, error) {
return lock, nil
}
+// AllocateAndRetrieveLock allocates the lock with the given ID and returns it.
+// If the lock is already allocated, error.
+func (m *SHMLockManager) AllocateAndRetrieveLock(id uint32) (Locker, error) {
+ lock := new(SHMLock)
+ lock.lockID = id
+ lock.manager = m
+
+ if id >= m.locks.GetMaxLocks() {
+ return nil, errors.Wrapf(syscall.EINVAL, "lock ID %d is too large - max lock size is %d",
+ id, m.locks.GetMaxLocks()-1)
+ }
+
+ if err := m.locks.AllocateGivenSemaphore(id); err != nil {
+ return nil, err
+ }
+
+ return lock, nil
+}
+
// RetrieveLock retrieves a lock from the manager given its ID.
func (m *SHMLockManager) RetrieveLock(id uint32) (Locker, error) {
lock := new(SHMLock)
diff --git a/libpod/pod_internal.go b/libpod/pod_internal.go
index 25e4e77d7..1fcb5b1a6 100644
--- a/libpod/pod_internal.go
+++ b/libpod/pod_internal.go
@@ -56,7 +56,7 @@ func (p *Pod) refresh() error {
}
// Retrieve the pod's lock
- lock, err := p.runtime.lockManager.RetrieveLock(p.config.LockID)
+ lock, err := p.runtime.lockManager.AllocateAndRetrieveLock(p.config.LockID)
if err != nil {
return errors.Wrapf(err, "error retrieving lock for pod %s", p.ID())
}
diff --git a/libpod/runtime.go b/libpod/runtime.go
index 34b6ac74f..e6b84014e 100644
--- a/libpod/runtime.go
+++ b/libpod/runtime.go
@@ -922,7 +922,7 @@ func makeRuntime(ctx context.Context, runtime *Runtime) (err error) {
if os.IsNotExist(errors.Cause(err)) {
manager, err = lock.NewSHMLockManager(lockPath, runtime.config.NumLocks)
if err != nil {
- return err
+ return errors.Wrapf(err, "failed to get new shm lock manager")
}
} else if errors.Cause(err) == syscall.ERANGE && runtime.doRenumber {
logrus.Debugf("Number of locks does not match - removing old locks")
diff --git a/libpod/runtime_pod_linux.go b/libpod/runtime_pod_linux.go
index 0011c771a..5867b1f87 100644
--- a/libpod/runtime_pod_linux.go
+++ b/libpod/runtime_pod_linux.go
@@ -149,10 +149,10 @@ func (r *Runtime) removePod(ctx context.Context, p *Pod, removeCtrs, force bool)
}
// Go through and lock all containers so we can operate on them all at once
- dependencies := make(map[string][]string)
for _, ctr := range ctrs {
- ctr.lock.Lock()
- defer ctr.lock.Unlock()
+ ctrLock := ctr.lock
+ ctrLock.Lock()
+ defer ctrLock.Unlock()
// Sync all containers
if err := ctr.syncContainer(); err != nil {
@@ -177,23 +177,12 @@ func (r *Runtime) removePod(ctx context.Context, p *Pod, removeCtrs, force bool)
if len(ctr.state.ExecSessions) != 0 && !force {
return errors.Wrapf(ErrCtrStateInvalid, "pod %s contains container %s which has active exec sessions", p.ID(), ctr.ID())
}
-
- deps, err := r.state.ContainerInUse(ctr)
- if err != nil {
- return err
- }
- dependencies[ctr.ID()] = deps
}
- // Check if containers have dependencies
- // If they do, and the dependencies are not in the pod, error
- for ctr, deps := range dependencies {
- for _, dep := range deps {
- if _, ok := dependencies[dep]; !ok {
- return errors.Wrapf(ErrCtrExists, "container %s depends on container %s not in pod %s", ctr, dep, p.ID())
- }
- }
- }
+ // We maintain the invariant that container dependencies must all exist
+ // within the container's pod.
+ // No need to check dependencies as such - we're removing all containers
+ // in the pod at once, no dependency issues.
// First loop through all containers and stop them
// Do not remove in this loop to ensure that we don't remove unless all
@@ -220,18 +209,40 @@ func (r *Runtime) removePod(ctx context.Context, p *Pod, removeCtrs, force bool)
}
}
- // Start removing containers
- // We can remove containers even if they have dependencies now
- // As we have guaranteed their dependencies are in the pod
+ // Remove all containers in the pod from the state.
+ if err := r.state.RemovePodContainers(p); err != nil {
+ return err
+ }
+
+ var removalErr error
+
+ // Clean up after our removed containers.
+ // Errors here are nonfatal - the containers have already been evicted.
+ // We'll do our best to clean up after them, but we have to keep going
+ // and remove the pod as well.
+ // From here until we remove the pod from the state, no error returns.
for _, ctr := range ctrs {
+ // The container no longer exists in the state, mark invalid.
+ ctr.valid = false
+
+ ctr.newContainerEvent(events.Remove)
+
// Clean up network namespace, cgroups, mounts
if err := ctr.cleanup(ctx); err != nil {
- return err
+ if removalErr == nil {
+ removalErr = err
+ } else {
+ logrus.Errorf("Unable to clean up container %s: %v", ctr.ID(), err)
+ }
}
// Stop container's storage
if err := ctr.teardownStorage(); err != nil {
- return err
+ if removalErr == nil {
+ removalErr = err
+ } else {
+ logrus.Errorf("Unable to tear down container %s storage: %v", ctr.ID(), err)
+ }
}
// Delete the container from runtime (only if we are not
@@ -239,26 +250,24 @@ func (r *Runtime) removePod(ctx context.Context, p *Pod, removeCtrs, force bool)
if ctr.state.State != ContainerStateConfigured &&
ctr.state.State != ContainerStateExited {
if err := ctr.delete(ctx); err != nil {
- return err
+ if removalErr == nil {
+ removalErr = err
+ } else {
+ logrus.Errorf("Unable to remove container %s from OCI runtime: %v", ctr.ID(), err)
+ }
}
}
// Free the container's lock
if err := ctr.lock.Free(); err != nil {
- return err
+ if removalErr == nil {
+ removalErr = errors.Wrapf(err, "error freeing container %s lock", ctr.ID())
+ } else {
+ logrus.Errorf("Unable to free container %s lock: %v", ctr.ID(), err)
+ }
}
}
- // Remove containers from the state
- if err := r.state.RemovePodContainers(p); err != nil {
- return err
- }
-
- // Mark containers invalid
- for _, ctr := range ctrs {
- ctr.valid = false
- }
-
// Remove pod cgroup, if present
if p.state.CgroupPath != "" {
logrus.Debugf("Removing pod cgroup %s", p.state.CgroupPath)
@@ -266,10 +275,11 @@ func (r *Runtime) removePod(ctx context.Context, p *Pod, removeCtrs, force bool)
switch p.runtime.config.CgroupManager {
case SystemdCgroupsManager:
if err := deleteSystemdCgroup(p.state.CgroupPath); err != nil {
- // The pod is already almost gone.
- // No point in hard-failing if we fail
- // this bit of cleanup.
- logrus.Errorf("Error deleting pod %s cgroup %s: %v", p.ID(), p.state.CgroupPath, err)
+ if removalErr == nil {
+ removalErr = errors.Wrapf(err, "error removing pod %s cgroup", p.ID())
+ } else {
+ logrus.Errorf("Error deleting pod %s cgroup %s: %v", p.ID(), p.state.CgroupPath, err)
+ }
}
case CgroupfsCgroupsManager:
// Delete the cgroupfs cgroup
@@ -280,34 +290,60 @@ func (r *Runtime) removePod(ctx context.Context, p *Pod, removeCtrs, force bool)
conmonCgroupPath := filepath.Join(p.state.CgroupPath, "conmon")
conmonCgroup, err := cgroups.Load(v1CGroups, cgroups.StaticPath(conmonCgroupPath))
if err != nil && err != cgroups.ErrCgroupDeleted {
- return err
+ if removalErr == nil {
+ removalErr = errors.Wrapf(err, "error retrieving pod %s conmon cgroup", p.ID())
+ } else {
+ logrus.Debugf("Error retrieving pod %s conmon cgroup %s: %v", p.ID(), conmonCgroupPath, err)
+ }
}
if err == nil {
if err := conmonCgroup.Delete(); err != nil {
- logrus.Errorf("Error deleting pod %s conmon cgroup %s: %v", p.ID(), conmonCgroupPath, err)
+ if removalErr == nil {
+ removalErr = errors.Wrapf(err, "error removing pod %s conmon cgroup", p.ID())
+ } else {
+ logrus.Errorf("Error deleting pod %s conmon cgroup %s: %v", p.ID(), conmonCgroupPath, err)
+ }
}
}
cgroup, err := cgroups.Load(v1CGroups, cgroups.StaticPath(p.state.CgroupPath))
if err != nil && err != cgroups.ErrCgroupDeleted {
- return err
+ if removalErr == nil {
+ removalErr = errors.Wrapf(err, "error retrieving pod %s cgroup", p.ID())
+ } else {
+ logrus.Errorf("Error retrieving pod %s cgroup %s: %v", p.ID(), p.state.CgroupPath, err)
+ }
}
if err == nil {
if err := cgroup.Delete(); err != nil {
- logrus.Errorf("Error deleting pod %s cgroup %s: %v", p.ID(), p.state.CgroupPath, err)
+ if removalErr == nil {
+ removalErr = errors.Wrapf(err, "error removing pod %s cgroup", p.ID())
+ } else {
+ logrus.Errorf("Error deleting pod %s cgroup %s: %v", p.ID(), p.state.CgroupPath, err)
+ }
}
}
default:
- return errors.Wrapf(ErrInvalidArg, "unknown cgroups manager %s specified", p.runtime.config.CgroupManager)
+ // This should be caught much earlier, but let's still
+ // keep going so we make sure to evict the pod before
+ // ending up with an inconsistent state.
+ if removalErr == nil {
+ removalErr = errors.Wrapf(ErrInternal, "unrecognized cgroup manager %s when removing pod %s cgroups", p.runtime.config.CgroupManager, p.ID())
+ } else {
+ logrus.Errorf("Unknown cgroups manager %s specified - cannot remove pod %s cgroup", p.runtime.config.CgroupManager, p.ID())
+ }
}
}
// Remove pod from state
if err := r.state.RemovePod(p); err != nil {
+ if removalErr != nil {
+ logrus.Errorf("%v", removalErr)
+ }
return err
}
// Mark pod invalid
p.valid = false
p.newPodEvent(events.Remove)
- return nil
+ return removalErr
}
diff --git a/pkg/adapter/client.go b/pkg/adapter/client.go
index 6512a5952..f672a92a6 100644
--- a/pkg/adapter/client.go
+++ b/pkg/adapter/client.go
@@ -3,30 +3,45 @@
package adapter
import (
+ "fmt"
"os"
- "github.com/sirupsen/logrus"
+ "github.com/pkg/errors"
"github.com/varlink/go/varlink"
)
-// DefaultAddress is the default address of the varlink socket
-const DefaultAddress = "unix:/run/podman/io.podman"
+type VarlinkConnectionInfo struct {
+ RemoteUserName string
+ RemoteHost string
+ VarlinkAddress string
+}
// Connect provides a varlink connection
func (r RemoteRuntime) Connect() (*varlink.Connection, error) {
- var err error
- var connection *varlink.Connection
- if bridge := os.Getenv("PODMAN_VARLINK_BRIDGE"); bridge != "" {
- logrus.Infof("Connecting with varlink bridge")
- logrus.Debugf("%s", bridge)
+ var (
+ err error
+ connection *varlink.Connection
+ )
+
+ logLevel := r.cmd.LogLevel
+
+ // I'm leaving this here for now as a document of the birdge format. It can be removed later once the bridge
+ // function is more flushed out.
+ //bridge := `ssh -T root@192.168.122.1 "/usr/bin/varlink -A '/usr/bin/podman varlink \$VARLINK_ADDRESS' bridge"`
+ if len(r.cmd.RemoteHost) > 0 {
+ // The user has provided a remote host endpoint
+ if len(r.cmd.RemoteUserName) < 1 {
+ return nil, errors.New("you must provide a username when providing a remote host name")
+ }
+ bridge := fmt.Sprintf(`ssh -T %s@%s /usr/bin/varlink -A \'/usr/bin/podman --log-level=%s varlink \\\$VARLINK_ADDRESS\' bridge`, r.cmd.RemoteUserName, r.cmd.RemoteHost, logLevel)
+ connection, err = varlink.NewBridge(bridge)
+ } else if bridge := os.Getenv("PODMAN_VARLINK_BRIDGE"); bridge != "" {
connection, err = varlink.NewBridge(bridge)
} else {
address := os.Getenv("PODMAN_VARLINK_ADDRESS")
if address == "" {
address = DefaultAddress
}
- logrus.Infof("Connecting with varlink address")
- logrus.Debugf("%s", address)
connection, err = varlink.NewConnection(address)
}
if err != nil {
diff --git a/pkg/adapter/client_config.go b/pkg/adapter/client_config.go
new file mode 100644
index 000000000..d165ef1cc
--- /dev/null
+++ b/pkg/adapter/client_config.go
@@ -0,0 +1,4 @@
+package adapter
+
+// DefaultAddress is the default address of the varlink socket
+const DefaultAddress = "unix:/run/podman/io.podman"
diff --git a/pkg/adapter/containers.go b/pkg/adapter/containers.go
index 0721af773..ff7b6377a 100644
--- a/pkg/adapter/containers.go
+++ b/pkg/adapter/containers.go
@@ -413,7 +413,9 @@ func (r *LocalRuntime) Run(ctx context.Context, c *cliconfig.RunValues, exitCode
}
if c.IsSet("rm") {
- r.Runtime.RemoveContainer(ctx, ctr, false, true)
+ if err := r.Runtime.RemoveContainer(ctx, ctr, false, false); err != nil {
+ logrus.Errorf("Error removing container %s: %v", ctr.ID(), err)
+ }
}
return exitCode, nil
@@ -965,8 +967,9 @@ func (r *LocalRuntime) CleanupContainers(ctx context.Context, cli *cliconfig.Cle
return ok, failures, nil
}
+// Only used when cleaning up containers
func removeContainer(ctx context.Context, ctr *libpod.Container, runtime *LocalRuntime) error {
- if err := runtime.RemoveContainer(ctx, ctr, false, true); err != nil {
+ if err := runtime.RemoveContainer(ctx, ctr, false, false); err != nil {
return errors.Wrapf(err, "failed to cleanup and remove container %v", ctr.ID())
}
return nil
@@ -1022,3 +1025,8 @@ func (r *LocalRuntime) GenerateSystemd(c *cliconfig.GenerateSystemdValues) (stri
}
return systemdgen.CreateSystemdUnitAsString(name, ctr.ID(), c.RestartPolicy, ctr.Config().StaticDir, timeout)
}
+
+// GetNamespaces returns namespace information about a container for PS
+func (r *LocalRuntime) GetNamespaces(container shared.PsContainerOutput) *shared.Namespace {
+ return shared.GetNamespaces(container.Pid)
+}
diff --git a/pkg/adapter/containers_remote.go b/pkg/adapter/containers_remote.go
index 201249fc3..63b0f9d2f 100644
--- a/pkg/adapter/containers_remote.go
+++ b/pkg/adapter/containers_remote.go
@@ -234,15 +234,25 @@ func (r *LocalRuntime) StopContainers(ctx context.Context, cli *cliconfig.StopVa
ids, err := iopodman.GetContainersByContext().Call(r.Conn, cli.All, cli.Latest, cli.InputArgs)
if err != nil {
- return ok, failures, err
+ return ok, failures, TranslateError(err)
}
for _, id := range ids {
- stopped, err := iopodman.StopContainer().Call(r.Conn, id, int64(cli.Timeout))
- if err != nil {
+ if _, err := iopodman.StopContainer().Call(r.Conn, id, int64(cli.Timeout)); err != nil {
+ transError := TranslateError(err)
+ if errors.Cause(transError) == libpod.ErrCtrStopped {
+ ok = append(ok, id)
+ continue
+ }
+ if errors.Cause(transError) == libpod.ErrCtrStateInvalid && cli.All {
+ ok = append(ok, id)
+ continue
+ }
failures[id] = err
} else {
- ok = append(ok, stopped)
+ // We should be using ID here because in varlink, only successful returns
+ // include the string id
+ ok = append(ok, id)
}
}
return ok, failures, nil
@@ -310,7 +320,7 @@ func (r *LocalRuntime) KillContainers(ctx context.Context, cli *cliconfig.KillVa
func (r *LocalRuntime) RemoveContainers(ctx context.Context, cli *cliconfig.RmValues) ([]string, map[string]error, error) {
ids, err := iopodman.GetContainersByContext().Call(r.Conn, cli.All, cli.Latest, cli.InputArgs)
if err != nil {
- return nil, nil, err
+ return nil, nil, TranslateError(err)
}
var (
@@ -961,3 +971,18 @@ func (r *LocalRuntime) Port(c *cliconfig.PortValues) ([]*Container, error) {
func (r *LocalRuntime) GenerateSystemd(c *cliconfig.GenerateSystemdValues) (string, error) {
return iopodman.GenerateSystemd().Call(r.Conn, c.InputArgs[0], c.RestartPolicy, int64(c.StopTimeout), c.Name)
}
+
+// GetNamespaces returns namespace information about a container for PS
+func (r *LocalRuntime) GetNamespaces(container shared.PsContainerOutput) *shared.Namespace {
+ ns := shared.Namespace{
+ PID: container.PID,
+ Cgroup: container.Cgroup,
+ IPC: container.IPC,
+ MNT: container.MNT,
+ NET: container.NET,
+ PIDNS: container.PIDNS,
+ User: container.User,
+ UTS: container.UTS,
+ }
+ return &ns
+}
diff --git a/pkg/adapter/errors.go b/pkg/adapter/errors.go
new file mode 100644
index 000000000..7fbbabd93
--- /dev/null
+++ b/pkg/adapter/errors.go
@@ -0,0 +1,31 @@
+// +build remoteclient
+
+package adapter
+
+import (
+ iopodman "github.com/containers/libpod/cmd/podman/varlink"
+ "github.com/containers/libpod/libpod"
+ "github.com/pkg/errors"
+)
+
+// TranslateMapErrors translates the errors a typical podman output struct
+// from varlink errors to libpod errors
+func TranslateMapErrors(failures map[string]error) map[string]error {
+ for k, v := range failures {
+ failures[k] = TranslateError(v)
+ }
+ return failures
+}
+
+// TranslateError converts a single varlink error to a libpod error
+func TranslateError(err error) error {
+ switch err.(type) {
+ case *iopodman.ContainerNotFound:
+ return errors.Wrap(libpod.ErrNoSuchCtr, err.Error())
+ case *iopodman.ErrCtrStopped:
+ return errors.Wrap(libpod.ErrCtrStopped, err.Error())
+ case *iopodman.InvalidState:
+ return errors.Wrap(libpod.ErrCtrStateInvalid, err.Error())
+ }
+ return err
+}
diff --git a/pkg/adapter/info_remote.go b/pkg/adapter/info_remote.go
index 3b691ed17..3b2d02a5a 100644
--- a/pkg/adapter/info_remote.go
+++ b/pkg/adapter/info_remote.go
@@ -20,12 +20,7 @@ func (r RemoteRuntime) Info() ([]libpod.InfoData, error) {
registries := make(map[string]interface{})
insecureRegistries := make(map[string]interface{})
- conn, err := r.Connect()
- if err != nil {
- return nil, err
- }
- defer conn.Close()
- info, err := iopodman.GetInfo().Call(conn)
+ info, err := iopodman.GetInfo().Call(r.Conn)
if err != nil {
return nil, err
}
diff --git a/pkg/adapter/pods_remote.go b/pkg/adapter/pods_remote.go
index 7cf38aac0..e2c97c36a 100644
--- a/pkg/adapter/pods_remote.go
+++ b/pkg/adapter/pods_remote.go
@@ -172,11 +172,15 @@ func (r *LocalRuntime) StartPods(ctx context.Context, cli *cliconfig.PodStartVal
// CreatePod creates a pod for the remote client over a varlink connection
func (r *LocalRuntime) CreatePod(ctx context.Context, cli *cliconfig.PodCreateValues, labels map[string]string) (string, error) {
+ var share []string
+ if cli.Share != "" {
+ share = strings.Split(cli.Share, ",")
+ }
pc := iopodman.PodCreate{
Name: cli.Name,
CgroupParent: cli.CgroupParent,
Labels: labels,
- Share: strings.Split(cli.Share, ","),
+ Share: share,
Infra: cli.Infra,
InfraCommand: cli.InfraCommand,
InfraImage: cli.InfraCommand,
diff --git a/pkg/adapter/runtime.go b/pkg/adapter/runtime.go
index 0d840d65b..21613c425 100644
--- a/pkg/adapter/runtime.go
+++ b/pkg/adapter/runtime.go
@@ -5,12 +5,13 @@ package adapter
import (
"bufio"
"context"
- "github.com/containers/libpod/cmd/podman/shared"
"io"
"io/ioutil"
"os"
"text/template"
+ "github.com/containers/libpod/cmd/podman/shared"
+
"github.com/containers/buildah"
"github.com/containers/buildah/imagebuildah"
"github.com/containers/buildah/pkg/parse"
@@ -392,3 +393,8 @@ func (r *LocalRuntime) GetPodsByStatus(statuses []string) ([]*libpod.Pod, error)
return pods, nil
}
+
+// GetVersion is an alias to satisfy interface{}
+func (r *LocalRuntime) GetVersion() (libpod.Version, error) {
+ return libpod.GetVersion()
+}
diff --git a/pkg/adapter/runtime_remote.go b/pkg/adapter/runtime_remote.go
index 4986d16f7..e86287462 100644
--- a/pkg/adapter/runtime_remote.go
+++ b/pkg/adapter/runtime_remote.go
@@ -9,12 +9,13 @@ import (
"fmt"
"io"
"io/ioutil"
- v1 "k8s.io/api/core/v1"
"os"
"strings"
"text/template"
"time"
+ v1 "k8s.io/api/core/v1"
+
"github.com/containers/buildah/imagebuildah"
"github.com/containers/image/docker/reference"
"github.com/containers/image/types"
@@ -38,6 +39,7 @@ type RemoteImageRuntime struct{}
type RemoteRuntime struct {
Conn *varlink.Connection
Remote bool
+ cmd cliconfig.MainFlags
}
// LocalRuntime describes a typical libpod runtime
@@ -47,17 +49,17 @@ type LocalRuntime struct {
// GetRuntime returns a LocalRuntime struct with the actual runtime embedded in it
func GetRuntime(ctx context.Context, c *cliconfig.PodmanCommand) (*LocalRuntime, error) {
- runtime := RemoteRuntime{}
+ runtime := RemoteRuntime{
+ Remote: true,
+ cmd: c.GlobalFlags,
+ }
conn, err := runtime.Connect()
if err != nil {
return nil, err
}
-
+ runtime.Conn = conn
return &LocalRuntime{
- &RemoteRuntime{
- Conn: conn,
- Remote: true,
- },
+ &runtime,
}, nil
}
@@ -414,19 +416,19 @@ func (r *LocalRuntime) Build(ctx context.Context, c *cliconfig.BuildValues, opti
Compression: string(options.Compression),
DefaultsMountFilePath: options.DefaultMountsFilePath,
Dockerfiles: dockerfiles,
- //Err: string(options.Err),
+ // Err: string(options.Err),
ForceRmIntermediateCtrs: options.ForceRmIntermediateCtrs,
Iidfile: options.IIDFile,
Label: options.Labels,
Layers: options.Layers,
Nocache: options.NoCache,
- //Out:
+ // Out:
Output: options.Output,
OutputFormat: options.OutputFormat,
PullPolicy: options.PullPolicy.String(),
Quiet: options.Quiet,
RemoteIntermediateCtrs: options.RemoveIntermediateCtrs,
- //ReportWriter:
+ // ReportWriter:
RuntimeArgs: options.RuntimeArgs,
SignaturePolicyPath: options.SignaturePolicyPath,
Squash: options.Squash,
@@ -610,7 +612,7 @@ func (r *LocalRuntime) InspectVolumes(ctx context.Context, c *cliconfig.VolumeIn
return varlinkVolumeToVolume(r, reply), nil
}
-//Volumes returns a slice of adapter.volumes based on information about libpod
+// Volumes returns a slice of adapter.volumes based on information about libpod
// volumes over a varlink connection
func (r *LocalRuntime) Volumes(ctx context.Context) ([]*Volume, error) {
reply, err := iopodman.GetVolumes().Call(r.Conn, []string{}, true)
@@ -906,3 +908,29 @@ func (r *LocalRuntime) GetContainersByContext(all bool, latest bool, namesOrIDs
}
return containers, nil
}
+
+// GetVersion returns version information from service
+func (r *LocalRuntime) GetVersion() (libpod.Version, error) {
+ version, goVersion, gitCommit, built, osArch, apiVersion, err := iopodman.GetVersion().Call(r.Conn)
+ if err != nil {
+ return libpod.Version{}, errors.Wrapf(err, "Unable to obtain server version information")
+ }
+
+ var buildTime int64
+ if built != "" {
+ t, err := time.Parse(time.RFC3339, built)
+ if err != nil {
+ return libpod.Version{}, nil
+ }
+ buildTime = t.Unix()
+ }
+
+ return libpod.Version{
+ RemoteAPIVersion: apiVersion,
+ Version: version,
+ GoVersion: goVersion,
+ GitCommit: gitCommit,
+ Built: buildTime,
+ OsArch: osArch,
+ }, nil
+}
diff --git a/pkg/varlinkapi/attach.go b/pkg/varlinkapi/attach.go
index 6c62d3514..2234899a5 100644
--- a/pkg/varlinkapi/attach.go
+++ b/pkg/varlinkapi/attach.go
@@ -60,6 +60,7 @@ func (i *LibpodAPI) Attach(call iopodman.VarlinkCall, name string, detachKeys st
if !start && state != libpod.ContainerStateRunning {
return call.ReplyErrorOccurred("container must be running to attach")
}
+ call.Reply(nil)
reader, writer, _, pw, streams := setupStreams(call)
go func() {
diff --git a/pkg/varlinkapi/containers.go b/pkg/varlinkapi/containers.go
index c8be41636..8611a1a7d 100644
--- a/pkg/varlinkapi/containers.go
+++ b/pkg/varlinkapi/containers.go
@@ -119,6 +119,9 @@ func (i *LibpodAPI) GetContainersByContext(call iopodman.VarlinkCall, all, lates
ctrs, err := shortcuts.GetContainersByContext(all, latest, input, i.Runtime)
if err != nil {
+ if errors.Cause(err) == libpod.ErrNoSuchCtr {
+ return call.ReplyContainerNotFound("", err.Error())
+ }
return call.ReplyErrorOccurred(err.Error())
}
@@ -359,7 +362,11 @@ func (i *LibpodAPI) StartContainer(call iopodman.VarlinkCall, name string) error
if state == libpod.ContainerStateRunning || state == libpod.ContainerStatePaused {
return call.ReplyErrorOccurred("container is already running or paused")
}
- if err := ctr.Start(getContext(), false); err != nil {
+ recursive := false
+ if ctr.PodID() != "" {
+ recursive = true
+ }
+ if err := ctr.Start(getContext(), recursive); err != nil {
return call.ReplyErrorOccurred(err.Error())
}
return call.ReplyStartContainer(ctr.ID())
@@ -386,7 +393,13 @@ func (i *LibpodAPI) StopContainer(call iopodman.VarlinkCall, name string, timeou
if err != nil {
return call.ReplyContainerNotFound(name, err.Error())
}
- if err := ctr.StopWithTimeout(uint(timeout)); err != nil && err != libpod.ErrCtrStopped {
+ if err := ctr.StopWithTimeout(uint(timeout)); err != nil {
+ if errors.Cause(err) == libpod.ErrCtrStopped {
+ return call.ReplyErrCtrStopped(ctr.ID())
+ }
+ if errors.Cause(err) == libpod.ErrCtrStateInvalid {
+ return call.ReplyInvalidState(ctr.ID(), err.Error())
+ }
return call.ReplyErrorOccurred(err.Error())
}
return call.ReplyStopContainer(ctr.ID())
diff --git a/pkg/varlinkapi/pods.go b/pkg/varlinkapi/pods.go
index f34375bf5..c0fd8b1f7 100644
--- a/pkg/varlinkapi/pods.go
+++ b/pkg/varlinkapi/pods.go
@@ -6,7 +6,6 @@ import (
"encoding/json"
"fmt"
"github.com/containers/libpod/pkg/adapter/shortcuts"
- "github.com/containers/libpod/pkg/rootless"
"syscall"
"github.com/containers/libpod/cmd/podman/shared"
@@ -37,12 +36,9 @@ func (i *LibpodAPI) CreatePod(call iopodman.VarlinkCall, create iopodman.PodCrea
if !create.Infra {
return call.ReplyErrorOccurred("you must have an infra container to publish port bindings to the host")
}
- if rootless.IsRootless() {
- return call.ReplyErrorOccurred("rootless networking does not allow port binding to the host")
- }
portBindings, err := shared.CreatePortBindings(create.Publish)
if err != nil {
- return err
+ return call.ReplyErrorOccurred(err.Error())
}
options = append(options, libpod.WithInfraContainerPorts(portBindings))
diff --git a/pkg/varlinkapi/system.go b/pkg/varlinkapi/system.go
index 7f436a954..59bfec75b 100644
--- a/pkg/varlinkapi/system.go
+++ b/pkg/varlinkapi/system.go
@@ -86,15 +86,18 @@ func (i *LibpodAPI) GetInfo(call iopodman.VarlinkCall) error {
Graph_status: graphStatus,
}
- registriesInterface := info[2].Data["registries"]
- insecureRegistriesInterface := info[3].Data["registries"]
- if registriesInterface != nil {
- registries = registriesInterface.([]string)
+ if len(info) > 2 {
+ registriesInterface := info[2].Data["registries"]
+ if registriesInterface != nil {
+ registries = registriesInterface.([]string)
+ }
}
- if insecureRegistriesInterface != nil {
- insecureRegistries = insecureRegistriesInterface.([]string)
+ if len(info) > 3 {
+ insecureRegistriesInterface := info[3].Data["registries"]
+ if insecureRegistriesInterface != nil {
+ insecureRegistries = insecureRegistriesInterface.([]string)
+ }
}
-
podmanInfo.Store = infoStore
podmanInfo.Podman = pmaninfo
podmanInfo.Registries = registries
diff --git a/test/e2e/common_test.go b/test/e2e/common_test.go
index a6fc211f6..3c7675b35 100644
--- a/test/e2e/common_test.go
+++ b/test/e2e/common_test.go
@@ -264,9 +264,14 @@ func PodmanTestCreateUtil(tempDir string, remote bool) *PodmanTestIntegration {
}
if remote {
p.PodmanTest.RemotePodmanBinary = podmanRemoteBinary
+ uuid := stringid.GenerateNonCryptoID()
if !rootless.IsRootless() {
- uuid := stringid.GenerateNonCryptoID()
p.VarlinkEndpoint = fmt.Sprintf("unix:/run/podman/io.podman-%s", uuid)
+ } else {
+ runtimeDir := os.Getenv("XDG_RUNTIME_DIR")
+ socket := fmt.Sprintf("io.podman-%s", uuid)
+ fqpath := filepath.Join(runtimeDir, socket)
+ p.VarlinkEndpoint = fmt.Sprintf("unix:%s", fqpath)
}
}
diff --git a/test/e2e/cp_test.go b/test/e2e/cp_test.go
index 591f533d6..273668f35 100644
--- a/test/e2e/cp_test.go
+++ b/test/e2e/cp_test.go
@@ -7,6 +7,7 @@ import (
"os"
"os/exec"
"path/filepath"
+ "strings"
. "github.com/containers/libpod/test/utils"
. "github.com/onsi/ginkgo"
@@ -58,21 +59,12 @@ var _ = Describe("Podman cp", func() {
session.WaitWithDefaultTimeout()
Expect(session.ExitCode()).To(Equal(0))
- session = podmanTest.Podman([]string{"start", "-a", name})
- session.WaitWithDefaultTimeout()
-
- Expect(session.ExitCode()).To(Equal(0))
- Expect(session.OutputToString()).To(Equal("copy from host to container"))
-
session = podmanTest.Podman([]string{"cp", name + ":foo", filepath.Join(path, "cp_from_container")})
session.WaitWithDefaultTimeout()
Expect(session.ExitCode()).To(Equal(0))
- c := exec.Command("cat", filepath.Join(path, "cp_from_container"))
- output, err := c.Output()
- if err != nil {
- os.Exit(1)
- }
- Expect(string(output)).To(Equal("copy from host to container"))
+
+ os.Remove("cp_from_container")
+ os.Remove("cp_test.txt")
})
It("podman cp file to dir", func() {
@@ -89,28 +81,18 @@ var _ = Describe("Podman cp", func() {
session := podmanTest.Podman([]string{"create", ALPINE, "ls", "foodir/"})
session.WaitWithDefaultTimeout()
Expect(session.ExitCode()).To(Equal(0))
- session = podmanTest.Podman([]string{"ps", "-a", "-q"})
- session.WaitWithDefaultTimeout()
- Expect(session.ExitCode()).To(Equal(0))
name := session.OutputToString()
session = podmanTest.Podman([]string{"cp", filepath.Join(path, "cp_test.txt"), name + ":foodir/"})
session.WaitWithDefaultTimeout()
Expect(session.ExitCode()).To(Equal(0))
- session = podmanTest.Podman([]string{"start", "-a", name})
- session.WaitWithDefaultTimeout()
- Expect(session.ExitCode()).To(Equal(0))
- Expect(session.OutputToString()).To(Equal("cp_test.txt"))
session = podmanTest.Podman([]string{"cp", name + ":foodir/cp_test.txt", path + "/receive/"})
session.WaitWithDefaultTimeout()
Expect(session.ExitCode()).To(Equal(0))
- c := exec.Command("cat", filepath.Join(path, "receive", "cp_test.txt"))
- output, err := c.Output()
- if err != nil {
- os.Exit(1)
- }
- Expect(string(output)).To(Equal("copy from host to container directory"))
+
+ os.Remove("cp_test.txt")
+ os.RemoveAll("receive")
})
It("podman cp dir to dir", func() {
@@ -132,17 +114,50 @@ var _ = Describe("Podman cp", func() {
session = podmanTest.Podman([]string{"cp", testDirPath, name + ":/foodir"})
session.WaitWithDefaultTimeout()
Expect(session.ExitCode()).To(Equal(0))
- session = podmanTest.Podman([]string{"start", "-a", name})
+
+ session = podmanTest.Podman([]string{"cp", testDirPath, name + ":/foodir"})
session.WaitWithDefaultTimeout()
Expect(session.ExitCode()).To(Equal(0))
- Expect(len(session.OutputToStringArray())).To(Equal(0))
- session = podmanTest.Podman([]string{"cp", testDirPath, name + ":/foodir"})
+ os.RemoveAll(testDirPath)
+ })
+
+ It("podman cp stdin/stdout", func() {
+ path, err := os.Getwd()
+ if err != nil {
+ os.Exit(1)
+ }
+ testDirPath := filepath.Join(path, "TestDir")
+ err = os.Mkdir(testDirPath, 0777)
+ if err != nil {
+ os.Exit(1)
+ }
+ cmd := exec.Command("tar", "-zcvf", "file.tar.gz", testDirPath)
+ _, err = cmd.Output()
+ if err != nil {
+ os.Exit(1)
+ }
+
+ session := podmanTest.Podman([]string{"create", ALPINE, "ls", "foo"})
+ session.WaitWithDefaultTimeout()
+ Expect(session.ExitCode()).To(Equal(0))
+ name := session.OutputToString()
+
+ data, err := ioutil.ReadFile("foo.tar.gz")
+ reader := strings.NewReader(string(data))
+ cmd.Stdin = reader
+ session = podmanTest.Podman([]string{"cp", "-", name + ":/foo"})
session.WaitWithDefaultTimeout()
Expect(session.ExitCode()).To(Equal(0))
- session = podmanTest.Podman([]string{"start", "-a", name})
+
+ session = podmanTest.Podman([]string{"cp", "file.tar.gz", name + ":/foo.tar.gz"})
session.WaitWithDefaultTimeout()
Expect(session.ExitCode()).To(Equal(0))
- Expect(session.OutputToString()).To(Equal("TestDir"))
+ session = podmanTest.Podman([]string{"cp", name + ":/foo.tar.gz", "-"})
+ session.WaitWithDefaultTimeout()
+ Expect(session.ExitCode()).To(Equal(0))
+
+ os.Remove("file.tar.gz")
+ os.RemoveAll(testDirPath)
})
})
diff --git a/test/e2e/create_test.go b/test/e2e/create_test.go
index 105cba37c..f3367337e 100644
--- a/test/e2e/create_test.go
+++ b/test/e2e/create_test.go
@@ -1,5 +1,3 @@
-// +build !remoteclient
-
package integration
import (
diff --git a/test/e2e/diff_test.go b/test/e2e/diff_test.go
index fba65823e..920b920c0 100644
--- a/test/e2e/diff_test.go
+++ b/test/e2e/diff_test.go
@@ -1,5 +1,3 @@
-// +build !remoteclient
-
package integration
import (
@@ -63,6 +61,7 @@ var _ = Describe("Podman diff", func() {
})
It("podman diff container and committed image", func() {
+ SkipIfRemote()
session := podmanTest.Podman([]string{"run", "--name=diff-test", ALPINE, "touch", "/tmp/diff-test"})
session.WaitWithDefaultTimeout()
Expect(session.ExitCode()).To(Equal(0))
diff --git a/test/e2e/info_test.go b/test/e2e/info_test.go
index c960fb311..ca4012dde 100644
--- a/test/e2e/info_test.go
+++ b/test/e2e/info_test.go
@@ -24,6 +24,7 @@ var _ = Describe("Podman Info", func() {
}
podmanTest = PodmanTestCreate(tempdir)
podmanTest.Setup()
+ podmanTest.DelayForVarlink()
})
AfterEach(func() {
diff --git a/test/e2e/kill_test.go b/test/e2e/kill_test.go
index 618ca5aa0..3286180a4 100644
--- a/test/e2e/kill_test.go
+++ b/test/e2e/kill_test.go
@@ -1,5 +1,3 @@
-// +build !remoteclient
-
package integration
import (
diff --git a/test/e2e/libpod_suite_remoteclient_test.go b/test/e2e/libpod_suite_remoteclient_test.go
index 1e477fe2f..05c355711 100644
--- a/test/e2e/libpod_suite_remoteclient_test.go
+++ b/test/e2e/libpod_suite_remoteclient_test.go
@@ -3,13 +3,17 @@
package integration
import (
+ "bytes"
"fmt"
"github.com/containers/libpod/pkg/rootless"
"io/ioutil"
"os"
"os/exec"
"path/filepath"
+ "strconv"
"strings"
+ "syscall"
+ "time"
"github.com/onsi/ginkgo"
)
@@ -50,33 +54,76 @@ func PodmanTestCreate(tempDir string) *PodmanTestIntegration {
return pti
}
+func (p *PodmanTestIntegration) ResetVarlinkAddress() {
+ os.Unsetenv("PODMAN_VARLINK_ADDRESS")
+}
+
+func (p *PodmanTestIntegration) SetVarlinkAddress(addr string) {
+ os.Setenv("PODMAN_VARLINK_ADDRESS", addr)
+}
+
func (p *PodmanTestIntegration) StartVarlink() {
if os.Geteuid() == 0 {
os.MkdirAll("/run/podman", 0755)
}
varlinkEndpoint := p.VarlinkEndpoint
- if addr := os.Getenv("PODMAN_VARLINK_ADDRESS"); addr != "" {
- varlinkEndpoint = addr
- }
+ p.SetVarlinkAddress(p.VarlinkEndpoint)
args := []string{"varlink", "--timeout", "0", varlinkEndpoint}
podmanOptions := getVarlinkOptions(p, args)
command := exec.Command(p.PodmanBinary, podmanOptions...)
fmt.Printf("Running: %s %s\n", p.PodmanBinary, strings.Join(podmanOptions, " "))
command.Start()
+ command.SysProcAttr = &syscall.SysProcAttr{Setpgid: true}
+ p.VarlinkCommand = command
p.VarlinkSession = command.Process
}
func (p *PodmanTestIntegration) StopVarlink() {
+ var out bytes.Buffer
+ var pids []int
varlinkSession := p.VarlinkSession
- varlinkSession.Kill()
- varlinkSession.Wait()
if !rootless.IsRootless() {
- socket := strings.Split(p.VarlinkEndpoint, ":")[1]
- if err := os.Remove(socket); err != nil {
- fmt.Println(err)
+ if err := varlinkSession.Kill(); err != nil {
+ fmt.Fprintf(os.Stderr, "error on varlink stop-kill %q", err)
}
+ if _, err := varlinkSession.Wait(); err != nil {
+ fmt.Fprintf(os.Stderr, "error on varlink stop-wait %q", err)
+ }
+
+ } else {
+ p.ResetVarlinkAddress()
+ parentPid := fmt.Sprintf("%d", p.VarlinkSession.Pid)
+ pgrep := exec.Command("pgrep", "-P", parentPid)
+ fmt.Printf("running: pgrep %s\n", parentPid)
+ pgrep.Stdout = &out
+ err := pgrep.Run()
+ if err != nil {
+ fmt.Fprint(os.Stderr, "unable to find varlink pid")
+ }
+
+ for _, s := range strings.Split(out.String(), "\n") {
+ if len(s) == 0 {
+ continue
+ }
+ p, err := strconv.Atoi(s)
+ if err != nil {
+ fmt.Fprintf(os.Stderr, "unable to convert %s to int", s)
+ }
+ if p != 0 {
+ pids = append(pids, p)
+ }
+ }
+
+ pids = append(pids, p.VarlinkSession.Pid)
+ for _, pid := range pids {
+ syscall.Kill(pid, syscall.SIGKILL)
+ }
+ }
+ socket := strings.Split(p.VarlinkEndpoint, ":")[1]
+ if err := os.Remove(socket); err != nil {
+ fmt.Println(err)
}
}
@@ -110,3 +157,14 @@ func (p *PodmanTestIntegration) RestoreArtifact(image string) error {
command.Wait()
return nil
}
+
+func (p *PodmanTestIntegration) DelayForVarlink() {
+ for i := 0; i < 5; i++ {
+ session := p.Podman([]string{"info"})
+ session.WaitWithDefaultTimeout()
+ if session.ExitCode() == 0 || i == 4 {
+ break
+ }
+ time.Sleep(1 * time.Second)
+ }
+}
diff --git a/test/e2e/libpod_suite_test.go b/test/e2e/libpod_suite_test.go
index 10ca9ac47..0a85c625d 100644
--- a/test/e2e/libpod_suite_test.go
+++ b/test/e2e/libpod_suite_test.go
@@ -79,4 +79,5 @@ func (p *PodmanTestIntegration) RestoreArtifact(image string) error {
restore.Wait(90)
return nil
}
-func (p *PodmanTestIntegration) StopVarlink() {}
+func (p *PodmanTestIntegration) StopVarlink() {}
+func (p *PodmanTestIntegration) DelayForVarlink() {}
diff --git a/test/e2e/logs_test.go b/test/e2e/logs_test.go
index d051e3dba..2c82182cf 100644
--- a/test/e2e/logs_test.go
+++ b/test/e2e/logs_test.go
@@ -1,5 +1,3 @@
-// +build !remoteclient
-
package integration
import (
diff --git a/test/e2e/negative_test.go b/test/e2e/negative_test.go
new file mode 100644
index 000000000..3cb54a20a
--- /dev/null
+++ b/test/e2e/negative_test.go
@@ -0,0 +1,38 @@
+package integration
+
+import (
+ "os"
+
+ . "github.com/containers/libpod/test/utils"
+ . "github.com/onsi/ginkgo"
+ . "github.com/onsi/gomega"
+)
+
+var _ = Describe("Podman negative command-line", func() {
+ var (
+ tempdir string
+ err error
+ podmanTest *PodmanTestIntegration
+ )
+
+ BeforeEach(func() {
+ tempdir, err = CreateTempDirInTempDir()
+ if err != nil {
+ os.Exit(1)
+ }
+ podmanTest = PodmanTestCreate(tempdir)
+ })
+
+ AfterEach(func() {
+ podmanTest.Cleanup()
+ f := CurrentGinkgoTestDescription()
+ processTestResult(f)
+
+ })
+
+ It("podman snuffleupagus exits non-zero", func() {
+ session := podmanTest.Podman([]string{"snuffleupagus"})
+ session.WaitWithDefaultTimeout()
+ Expect(session.ExitCode()).To(Not(Equal(0)))
+ })
+})
diff --git a/test/e2e/pod_create_test.go b/test/e2e/pod_create_test.go
index de0734e9f..84966f77b 100644
--- a/test/e2e/pod_create_test.go
+++ b/test/e2e/pod_create_test.go
@@ -1,5 +1,3 @@
-// +build !remoteclient
-
package integration
import (
diff --git a/test/e2e/pod_inspect_test.go b/test/e2e/pod_inspect_test.go
index 671d203a6..d1a023153 100644
--- a/test/e2e/pod_inspect_test.go
+++ b/test/e2e/pod_inspect_test.go
@@ -1,5 +1,3 @@
-// +build !remoteclient
-
package integration
import (
diff --git a/test/e2e/pod_kill_test.go b/test/e2e/pod_kill_test.go
index c1f7503e3..23a8ea97e 100644
--- a/test/e2e/pod_kill_test.go
+++ b/test/e2e/pod_kill_test.go
@@ -1,5 +1,3 @@
-// +build !remoteclient
-
package integration
import (
diff --git a/test/e2e/pod_pause_test.go b/test/e2e/pod_pause_test.go
index 59a4da176..ab828853b 100644
--- a/test/e2e/pod_pause_test.go
+++ b/test/e2e/pod_pause_test.go
@@ -1,5 +1,3 @@
-// +build !remoteclient
-
package integration
import (
diff --git a/test/e2e/pod_prune_test.go b/test/e2e/pod_prune_test.go
index c20f602ad..8a4ba2399 100644
--- a/test/e2e/pod_prune_test.go
+++ b/test/e2e/pod_prune_test.go
@@ -1,5 +1,3 @@
-// +build !remoteclient
-
package integration
import (
diff --git a/test/e2e/pod_ps_test.go b/test/e2e/pod_ps_test.go
index 2fa26d7ad..8513c6c2e 100644
--- a/test/e2e/pod_ps_test.go
+++ b/test/e2e/pod_ps_test.go
@@ -1,5 +1,3 @@
-// +build !remoteclient
-
package integration
import (
diff --git a/test/e2e/pod_restart_test.go b/test/e2e/pod_restart_test.go
index ffb6cb94c..7b19ecc94 100644
--- a/test/e2e/pod_restart_test.go
+++ b/test/e2e/pod_restart_test.go
@@ -1,5 +1,3 @@
-// +build !remoteclient
-
package integration
import (
diff --git a/test/e2e/pod_rm_test.go b/test/e2e/pod_rm_test.go
index 5da3d563b..7417a1298 100644
--- a/test/e2e/pod_rm_test.go
+++ b/test/e2e/pod_rm_test.go
@@ -1,5 +1,3 @@
-// +build !remoteclient
-
package integration
import (
diff --git a/test/e2e/pod_start_test.go b/test/e2e/pod_start_test.go
index de52af2a0..967fbc2da 100644
--- a/test/e2e/pod_start_test.go
+++ b/test/e2e/pod_start_test.go
@@ -1,5 +1,3 @@
-// +build !remoteclient
-
package integration
import (
diff --git a/test/e2e/pod_stop_test.go b/test/e2e/pod_stop_test.go
index fa285fa80..9fd9e3ef4 100644
--- a/test/e2e/pod_stop_test.go
+++ b/test/e2e/pod_stop_test.go
@@ -1,5 +1,3 @@
-// +build !remoteclient
-
package integration
import (
diff --git a/test/e2e/ps_test.go b/test/e2e/ps_test.go
index 957c69aa8..7edb350f3 100644
--- a/test/e2e/ps_test.go
+++ b/test/e2e/ps_test.go
@@ -1,5 +1,3 @@
-// +build !remoteclient
-
package integration
import (
diff --git a/test/e2e/refresh_test.go b/test/e2e/refresh_test.go
deleted file mode 100644
index 56c1d255e..000000000
--- a/test/e2e/refresh_test.go
+++ /dev/null
@@ -1,74 +0,0 @@
-// +build !remoteclient
-
-package integration
-
-import (
- "os"
- "time"
-
- . "github.com/containers/libpod/test/utils"
- . "github.com/onsi/ginkgo"
- . "github.com/onsi/gomega"
-)
-
-var _ = Describe("Podman refresh", func() {
- var (
- tmpdir string
- err error
- podmanTest *PodmanTestIntegration
- )
-
- BeforeEach(func() {
- tmpdir, err = CreateTempDirInTempDir()
- if err != nil {
- os.Exit(1)
- }
- podmanTest = PodmanTestCreate(tmpdir)
- podmanTest.Setup()
- podmanTest.RestoreAllArtifacts()
- })
-
- AfterEach(func() {
- podmanTest.Cleanup()
- f := CurrentGinkgoTestDescription()
- processTestResult(f)
-
- })
-
- Specify("Refresh with no containers succeeds", func() {
- session := podmanTest.Podman([]string{"container", "refresh"})
- session.WaitWithDefaultTimeout()
- Expect(session.ExitCode()).To(Equal(0))
- })
-
- Specify("Refresh with created container succeeds", func() {
- createSession := podmanTest.Podman([]string{"create", ALPINE, "ls"})
- createSession.WaitWithDefaultTimeout()
- Expect(createSession.ExitCode()).To(Equal(0))
- Expect(podmanTest.NumberOfContainers()).To(Equal(1))
- Expect(podmanTest.NumberOfContainersRunning()).To(Equal(0))
-
- refreshSession := podmanTest.Podman([]string{"container", "refresh"})
- refreshSession.WaitWithDefaultTimeout()
- Expect(refreshSession.ExitCode()).To(Equal(0))
- Expect(podmanTest.NumberOfContainers()).To(Equal(1))
- Expect(podmanTest.NumberOfContainersRunning()).To(Equal(0))
- })
-
- Specify("Refresh with running container restarts container", func() {
- createSession := podmanTest.Podman([]string{"run", "-dt", ALPINE, "top"})
- createSession.WaitWithDefaultTimeout()
- Expect(createSession.ExitCode()).To(Equal(0))
- Expect(podmanTest.NumberOfContainers()).To(Equal(1))
- Expect(podmanTest.NumberOfContainersRunning()).To(Equal(1))
-
- // HACK: ensure container starts before we move on
- time.Sleep(1 * time.Second)
-
- refreshSession := podmanTest.Podman([]string{"container", "refresh"})
- refreshSession.WaitWithDefaultTimeout()
- Expect(refreshSession.ExitCode()).To(Equal(0))
- Expect(podmanTest.NumberOfContainers()).To(Equal(1))
- Expect(podmanTest.NumberOfContainersRunning()).To(Equal(1))
- })
-})
diff --git a/test/e2e/rm_test.go b/test/e2e/rm_test.go
index 9bf742a63..29150d67c 100644
--- a/test/e2e/rm_test.go
+++ b/test/e2e/rm_test.go
@@ -1,5 +1,3 @@
-// +build !remoteclient
-
package integration
import (
diff --git a/test/e2e/run_test.go b/test/e2e/run_test.go
index 030722b47..0e1f0d865 100644
--- a/test/e2e/run_test.go
+++ b/test/e2e/run_test.go
@@ -273,6 +273,7 @@ var _ = Describe("Podman run", func() {
})
It("podman run notify_socket", func() {
+ SkipIfRemote()
host := GetHostDistributionInfo()
if host.Distribution != "rhel" && host.Distribution != "centos" && host.Distribution != "fedora" {
Skip("this test requires a working runc")
diff --git a/test/e2e/save_test.go b/test/e2e/save_test.go
index c3edc7c7e..ffb5182d6 100644
--- a/test/e2e/save_test.go
+++ b/test/e2e/save_test.go
@@ -4,6 +4,7 @@ import (
"os"
"path/filepath"
+ "github.com/containers/libpod/pkg/rootless"
. "github.com/containers/libpod/test/utils"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
@@ -75,6 +76,9 @@ var _ = Describe("Podman save", func() {
})
It("podman save to directory with oci format", func() {
+ if rootless.IsRootless() && podmanTest.RemoteTest {
+ Skip("Requires a fix in containers image for chown/lchown")
+ }
outdir := filepath.Join(podmanTest.TempDir, "save")
save := podmanTest.Podman([]string{"save", "--format", "oci-dir", "-o", outdir, ALPINE})
@@ -83,6 +87,9 @@ var _ = Describe("Podman save", func() {
})
It("podman save to directory with v2s2 docker format", func() {
+ if rootless.IsRootless() && podmanTest.RemoteTest {
+ Skip("Requires a fix in containers image for chown/lchown")
+ }
outdir := filepath.Join(podmanTest.TempDir, "save")
save := podmanTest.Podman([]string{"save", "--format", "docker-dir", "-o", outdir, ALPINE})
@@ -91,6 +98,9 @@ var _ = Describe("Podman save", func() {
})
It("podman save to directory with docker format and compression", func() {
+ if rootless.IsRootless() && podmanTest.RemoteTest {
+ Skip("Requires a fix in containers image for chown/lchown")
+ }
outdir := filepath.Join(podmanTest.TempDir, "save")
save := podmanTest.Podman([]string{"save", "--compress", "--format", "docker-dir", "-o", outdir, ALPINE})
diff --git a/test/e2e/stop_test.go b/test/e2e/stop_test.go
index 717eea441..e201204df 100644
--- a/test/e2e/stop_test.go
+++ b/test/e2e/stop_test.go
@@ -1,5 +1,3 @@
-// +build !remoteclient
-
package integration
import (
diff --git a/test/e2e/tag_test.go b/test/e2e/tag_test.go
index ff0ac31c4..26d6dfa75 100644
--- a/test/e2e/tag_test.go
+++ b/test/e2e/tag_test.go
@@ -1,5 +1,3 @@
-// +build !remoteclient
-
package integration
import (
diff --git a/test/e2e/version_test.go b/test/e2e/version_test.go
index f546158a9..35ee21e49 100644
--- a/test/e2e/version_test.go
+++ b/test/e2e/version_test.go
@@ -31,6 +31,7 @@ var _ = Describe("Podman version", func() {
})
It("podman version", func() {
+ SkipIfRemote()
session := podmanTest.Podman([]string{"version"})
session.WaitWithDefaultTimeout()
Expect(session.ExitCode()).To(Equal(0))
@@ -38,6 +39,7 @@ var _ = Describe("Podman version", func() {
})
It("podman version --format json", func() {
+ SkipIfRemote()
session := podmanTest.Podman([]string{"version", "--format", "json"})
session.WaitWithDefaultTimeout()
Expect(session.ExitCode()).To(Equal(0))
@@ -45,6 +47,7 @@ var _ = Describe("Podman version", func() {
})
It("podman version --format json", func() {
+ SkipIfRemote()
session := podmanTest.Podman([]string{"version", "--format", "{{ json .}}"})
session.WaitWithDefaultTimeout()
Expect(session.ExitCode()).To(Equal(0))
@@ -52,6 +55,7 @@ var _ = Describe("Podman version", func() {
})
It("podman version --format GO template", func() {
+ SkipIfRemote()
session := podmanTest.Podman([]string{"version", "--format", "{{ .Version }}"})
session.WaitWithDefaultTimeout()
Expect(session.ExitCode()).To(Equal(0))
diff --git a/test/e2e/wait_test.go b/test/e2e/wait_test.go
index 5bf0331e5..28a4c1e40 100644
--- a/test/e2e/wait_test.go
+++ b/test/e2e/wait_test.go
@@ -1,5 +1,3 @@
-// +build !remoteclient
-
package integration
import (
diff --git a/test/utils/utils.go b/test/utils/utils.go
index 1e0391d2e..beadab549 100644
--- a/test/utils/utils.go
+++ b/test/utils/utils.go
@@ -4,7 +4,6 @@ import (
"bufio"
"encoding/json"
"fmt"
- "github.com/containers/libpod/pkg/rootless"
"io/ioutil"
"os"
"os/exec"
@@ -42,6 +41,7 @@ type PodmanTest struct {
RemotePodmanBinary string
VarlinkSession *os.Process
VarlinkEndpoint string
+ VarlinkCommand *exec.Cmd
}
// PodmanSession wraps the gexec.session so we can extend it
@@ -69,9 +69,7 @@ func (p *PodmanTest) PodmanAsUserBase(args []string, uid, gid uint32, cwd string
podmanBinary := p.PodmanBinary
if p.RemoteTest {
podmanBinary = p.RemotePodmanBinary
- if !rootless.IsRootless() {
- env = append(env, fmt.Sprintf("PODMAN_VARLINK_ADDRESS=%s", p.VarlinkEndpoint))
- }
+ env = append(env, fmt.Sprintf("PODMAN_VARLINK_ADDRESS=%s", p.VarlinkEndpoint))
}
if env == nil {
diff --git a/vendor.conf b/vendor.conf
index d5e2b60bd..2f7e36d85 100644
--- a/vendor.conf
+++ b/vendor.conf
@@ -93,8 +93,8 @@ k8s.io/api kubernetes-1.10.13-beta.0 https://github.com/kubernetes/api
k8s.io/apimachinery kubernetes-1.10.13-beta.0 https://github.com/kubernetes/apimachinery
k8s.io/client-go kubernetes-1.10.13-beta.0 https://github.com/kubernetes/client-go
github.com/mrunalp/fileutils 7d4729fb36185a7c1719923406c9d40e54fb93c7
-github.com/varlink/go 64e07fabffa33e385817b41971cf2674f692f391
github.com/containers/buildah v1.8.2
+github.com/varlink/go 0f1d566d194b9d6d48e0d47c5e4d822628919066
# TODO: Gotty has not been updated since 2012. Can we find replacement?
github.com/Nvveen/Gotty cd527374f1e5bff4938207604a14f2e38a9cf512
github.com/fsouza/go-dockerclient v1.3.0
diff --git a/vendor/github.com/varlink/go/varlink/bridge.go b/vendor/github.com/varlink/go/varlink/bridge.go
index 0ea5de682..c478dcd88 100644
--- a/vendor/github.com/varlink/go/varlink/bridge.go
+++ b/vendor/github.com/varlink/go/varlink/bridge.go
@@ -6,6 +6,7 @@ import (
"bufio"
"io"
"net"
+ "os"
"os/exec"
)
@@ -30,12 +31,13 @@ func (p PipeCon) Close() error {
return nil
}
-// NewConnection returns a new connection to the given address.
-func NewBridge(bridge string) (*Connection, error) {
+// NewBridgeWithStderr returns a new connection with the given bridge.
+func NewBridgeWithStderr(bridge string, stderr io.Writer) (*Connection, error) {
//var err error
c := Connection{}
cmd := exec.Command("sh", "-c", bridge)
+ cmd.Stderr = stderr
r, err := cmd.StdoutPipe()
if err != nil {
return nil, err
@@ -56,3 +58,8 @@ func NewBridge(bridge string) (*Connection, error) {
return &c, nil
}
+
+// NewBridge returns a new connection with the given bridge.
+func NewBridge(bridge string) (*Connection, error) {
+ return NewBridgeWithStderr(bridge, os.Stderr)
+}
diff --git a/vendor/github.com/varlink/go/varlink/bridge_windows.go b/vendor/github.com/varlink/go/varlink/bridge_windows.go
index 751224ec8..42953b871 100644
--- a/vendor/github.com/varlink/go/varlink/bridge_windows.go
+++ b/vendor/github.com/varlink/go/varlink/bridge_windows.go
@@ -4,6 +4,7 @@ import (
"bufio"
"io"
"net"
+ "os"
"os/exec"
)
@@ -28,12 +29,13 @@ func (p PipeCon) Close() error {
return nil
}
-// NewConnection returns a new connection to the given address.
-func NewBridge(bridge string) (*Connection, error) {
+// NewBridgeWithStderr returns a new connection with the given bridge.
+func NewBridgeWithStderr(bridge string, stderr io.Writer) (*Connection, error) {
//var err error
c := Connection{}
cmd := exec.Command("cmd", "/C", bridge)
+ cmd.Stderr = stderr
r, err := cmd.StdoutPipe()
if err != nil {
return nil, err
@@ -54,3 +56,8 @@ func NewBridge(bridge string) (*Connection, error) {
return &c, nil
}
+
+// NewBridge returns a new connection with the given bridge.
+func NewBridge(bridge string) (*Connection, error) {
+ return NewBridgeWithStderr(bridge, os.Stderr)
+} \ No newline at end of file