summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rwxr-xr-xAPI.md109
-rw-r--r--cmd/podman/attach.go48
-rw-r--r--cmd/podman/cliconfig/commands.go4
-rw-r--r--cmd/podman/cliconfig/config.go13
-rw-r--r--cmd/podman/commands.go4
-rw-r--r--cmd/podman/commands_remoteclient.go2
-rw-r--r--cmd/podman/commit.go17
-rw-r--r--cmd/podman/container.go1
-rw-r--r--cmd/podman/create.go2
-rw-r--r--cmd/podman/healthcheck.go2
-rw-r--r--cmd/podman/imagefilters/filters.go7
-rw-r--r--cmd/podman/images.go17
-rw-r--r--cmd/podman/main.go128
-rw-r--r--cmd/podman/main_local.go155
-rw-r--r--cmd/podman/main_remote.go43
-rw-r--r--cmd/podman/pull.go10
-rw-r--r--cmd/podman/push.go12
-rw-r--r--cmd/podman/run.go2
-rw-r--r--cmd/podman/run_test.go2
-rw-r--r--cmd/podman/shared/intermediate.go8
-rw-r--r--cmd/podman/varlink/io.podman.varlink10
-rw-r--r--commands.md6
-rw-r--r--completions/bash/podman2
-rw-r--r--docs/podman-commit.1.md4
-rw-r--r--docs/podman-healthcheck-run.1.md2
-rw-r--r--install.md1
-rw-r--r--libpod/container.go2
-rw-r--r--libpod/container_commit.go17
-rw-r--r--libpod/container_internal.go8
-rw-r--r--libpod/healthcheck.go2
-rw-r--r--libpod/oci_linux.go53
-rw-r--r--libpod/options.go9
-rw-r--r--libpod/runtime.go14
-rw-r--r--libpod/runtime_ctr.go7
-rw-r--r--pkg/adapter/containers.go40
-rw-r--r--pkg/adapter/containers_remote.go156
-rw-r--r--pkg/spec/spec.go3
-rw-r--r--pkg/varlinkapi/attach.go103
-rw-r--r--pkg/varlinkapi/containers.go16
-rw-r--r--pkg/varlinkapi/virtwriter/virtwriter.go155
-rw-r--r--test/e2e/commit_test.go19
-rw-r--r--test/e2e/images_test.go11
-rw-r--r--test/e2e/prune_test.go4
-rw-r--r--test/e2e/rmi_test.go2
-rw-r--r--test/system/030-run.bats8
45 files changed, 996 insertions, 244 deletions
diff --git a/API.md b/API.md
index 219e061d7..738449f43 100755
--- a/API.md
+++ b/API.md
@@ -3,6 +3,10 @@ Podman Service Interface and API description. The master version of this docume
in the [API.md](https://github.com/containers/libpod/blob/master/API.md) file in the upstream libpod repository.
## Index
+[func Attach(name: string, detachKeys: string, start: bool) ](#Attach)
+
+[func AttachControl(name: string) ](#AttachControl)
+
[func BuildImage(build: BuildInfo) MoreResponse](#BuildImage)
[func BuildImageHierarchyMap(name: string) string](#BuildImageHierarchyMap)
@@ -115,6 +119,8 @@ in the [API.md](https://github.com/containers/libpod/blob/master/API.md) file in
[func PodStateData(name: string) string](#PodStateData)
+[func Ps(opts: PsOpts) PsContainer](#Ps)
+
[func PullImage(name: string, certDir: string, creds: string, signaturePolicy: string, tlsVerify: ) MoreResponse](#PullImage)
[func PushImage(name: string, tag: string, tlsverify: , signaturePolicy: string, creds: string, certDir: string, compress: bool, format: string, removeSignatures: bool, signBy: string) MoreResponse](#PushImage)
@@ -135,6 +141,8 @@ in the [API.md](https://github.com/containers/libpod/blob/master/API.md) file in
[func SendFile(type: string, length: int) string](#SendFile)
+[func Spec(name: string) string](#Spec)
+
[func StartContainer(name: string) string](#StartContainer)
[func StartPod(name: string) string](#StartPod)
@@ -219,6 +227,10 @@ in the [API.md](https://github.com/containers/libpod/blob/master/API.md) file in
[type PodmanInfo](#PodmanInfo)
+[type PsContainer](#PsContainer)
+
+[type PsOpts](#PsOpts)
+
[type Runlabel](#Runlabel)
[type Sockets](#Sockets)
@@ -252,6 +264,17 @@ in the [API.md](https://github.com/containers/libpod/blob/master/API.md) file in
[error WantsMoreRequired](#WantsMoreRequired)
## Methods
+### <a name="Attach"></a>func Attach
+<div style="background-color: #E8E8E8; padding: 15px; margin: 10px; border-radius: 10px;">
+
+method Attach(name: [string](https://godoc.org/builtin#string), detachKeys: [string](https://godoc.org/builtin#string), start: [bool](https://godoc.org/builtin#bool)) </div>
+Attach takes the name or ID of a container and sets up a the ability to remotely attach to its console. The start
+bool is whether you wish to start the container in question first.
+### <a name="AttachControl"></a>func AttachControl
+<div style="background-color: #E8E8E8; padding: 15px; margin: 10px; border-radius: 10px;">
+
+method AttachControl(name: [string](https://godoc.org/builtin#string)) </div>
+
### <a name="BuildImage"></a>func BuildImage
<div style="background-color: #E8E8E8; padding: 15px; margin: 10px; border-radius: 10px;">
@@ -841,6 +864,11 @@ $ varlink call -m unix:/run/podman/io.podman/io.podman.PausePod '{"name": "fooba
method PodStateData(name: [string](https://godoc.org/builtin#string)) [string](https://godoc.org/builtin#string)</div>
PodStateData returns inspectr level information of a given pod in string form. This call is for
development of Podman only and generally should not be used.
+### <a name="Ps"></a>func Ps
+<div style="background-color: #E8E8E8; padding: 15px; margin: 10px; border-radius: 10px;">
+
+method Ps(opts: [PsOpts](#PsOpts)) [PsContainer](#PsContainer)</div>
+
### <a name="PullImage"></a>func PullImage
<div style="background-color: #E8E8E8; padding: 15px; margin: 10px; border-radius: 10px;">
@@ -945,6 +973,11 @@ search results per registry.
method SendFile(type: [string](https://godoc.org/builtin#string), length: [int](https://godoc.org/builtin#int)) [string](https://godoc.org/builtin#string)</div>
Sendfile allows a remote client to send a file to the host
+### <a name="Spec"></a>func Spec
+<div style="background-color: #E8E8E8; padding: 15px; margin: 10px; border-radius: 10px;">
+
+method Spec(name: [string](https://godoc.org/builtin#string)) [string](https://godoc.org/builtin#string)</div>
+Spec returns the oci spec for a container. This call is for development of Podman only and generally should not be used.
### <a name="StartContainer"></a>func StartContainer
<div style="background-color: #E8E8E8; padding: 15px; margin: 10px; border-radius: 10px;">
@@ -1728,6 +1761,82 @@ insecure_registries [[]string](#[]string)
store [InfoStore](#InfoStore)
podman [InfoPodmanBinary](#InfoPodmanBinary)
+### <a name="PsContainer"></a>type PsContainer
+
+
+
+id [string](https://godoc.org/builtin#string)
+
+image [string](https://godoc.org/builtin#string)
+
+command [string](https://godoc.org/builtin#string)
+
+created [string](https://godoc.org/builtin#string)
+
+ports [string](https://godoc.org/builtin#string)
+
+names [string](https://godoc.org/builtin#string)
+
+isInfra [bool](https://godoc.org/builtin#bool)
+
+status [string](https://godoc.org/builtin#string)
+
+state [string](https://godoc.org/builtin#string)
+
+pidNum [int](https://godoc.org/builtin#int)
+
+rootFsSize [int](https://godoc.org/builtin#int)
+
+rwSize [int](https://godoc.org/builtin#int)
+
+pod [string](https://godoc.org/builtin#string)
+
+createdAt [string](https://godoc.org/builtin#string)
+
+exitedAt [string](https://godoc.org/builtin#string)
+
+startedAt [string](https://godoc.org/builtin#string)
+
+labels [map[string]](#map[string])
+
+nsPid [string](https://godoc.org/builtin#string)
+
+cgroup [string](https://godoc.org/builtin#string)
+
+ipc [string](https://godoc.org/builtin#string)
+
+mnt [string](https://godoc.org/builtin#string)
+
+net [string](https://godoc.org/builtin#string)
+
+pidNs [string](https://godoc.org/builtin#string)
+
+user [string](https://godoc.org/builtin#string)
+
+uts [string](https://godoc.org/builtin#string)
+
+mounts [string](https://godoc.org/builtin#string)
+### <a name="PsOpts"></a>type PsOpts
+
+
+
+all [bool](https://godoc.org/builtin#bool)
+
+filters [](#)
+
+last [](#)
+
+latest [](#)
+
+noTrunc [](#)
+
+pod [](#)
+
+quiet [](#)
+
+sort [](#)
+
+sync [](#)
### <a name="Runlabel"></a>type Runlabel
Runlabel describes the required input for container runlabel
diff --git a/cmd/podman/attach.go b/cmd/podman/attach.go
index f326f53c3..2fa05a3b1 100644
--- a/cmd/podman/attach.go
+++ b/cmd/podman/attach.go
@@ -1,11 +1,7 @@
package main
import (
- "os"
-
"github.com/containers/libpod/cmd/podman/cliconfig"
- "github.com/containers/libpod/cmd/podman/libpodruntime"
- "github.com/containers/libpod/libpod"
"github.com/containers/libpod/pkg/adapter"
"github.com/pkg/errors"
"github.com/spf13/cobra"
@@ -39,49 +35,21 @@ func init() {
flags.BoolVar(&attachCommand.SigProxy, "sig-proxy", true, "Proxy received signals to the process")
flags.BoolVarP(&attachCommand.Latest, "latest", "l", false, "Act on the latest container podman is aware of")
markFlagHiddenForRemoteClient("latest", flags)
+ // TODO allow for passing of a new deatch keys
+ markFlagHiddenForRemoteClient("detach-keys", flags)
}
func attachCmd(c *cliconfig.AttachValues) error {
- args := c.InputArgs
- var ctr *libpod.Container
-
if len(c.InputArgs) > 1 || (len(c.InputArgs) == 0 && !c.Latest) {
return errors.Errorf("attach requires the name or id of one running container or the latest flag")
}
-
- runtime, err := libpodruntime.GetRuntime(&c.PodmanCommand)
- if err != nil {
- return errors.Wrapf(err, "error creating libpod runtime")
- }
- defer runtime.Shutdown(false)
-
- if c.Latest {
- ctr, err = runtime.GetLatestContainer()
- } else {
- ctr, err = runtime.LookupContainer(args[0])
- }
-
- if err != nil {
- return errors.Wrapf(err, "unable to exec into %s", args[0])
+ if remoteclient && len(c.InputArgs) != 1 {
+ return errors.Errorf("attach requires the name or id of one running container")
}
-
- conState, err := ctr.State()
+ runtime, err := adapter.GetRuntime(&c.PodmanCommand)
if err != nil {
- return errors.Wrapf(err, "unable to determine state of %s", args[0])
- }
- if conState != libpod.ContainerStateRunning {
- return errors.Errorf("you can only attach to running containers")
- }
-
- inputStream := os.Stdin
- if c.NoStdin {
- inputStream = nil
+ return errors.Wrapf(err, "error creating runtime")
}
-
- // If the container is in a pod, also set to recursively start dependencies
- if err := adapter.StartAttachCtr(getContext(), ctr, os.Stdout, os.Stderr, inputStream, c.DetachKeys, c.SigProxy, false, ctr.PodID() != ""); err != nil && errors.Cause(err) != libpod.ErrDetach {
- return errors.Wrapf(err, "error attaching to container %s", ctr.ID())
- }
-
- return nil
+ defer runtime.Shutdown(false)
+ return runtime.Attach(getContext(), c)
}
diff --git a/cmd/podman/cliconfig/commands.go b/cmd/podman/cliconfig/commands.go
index 3361c14b8..00b66e32a 100644
--- a/cmd/podman/cliconfig/commands.go
+++ b/cmd/podman/cliconfig/commands.go
@@ -1,6 +1,8 @@
package cliconfig
-import "github.com/sirupsen/logrus"
+import (
+ "github.com/sirupsen/logrus"
+)
// GlobalIsSet is a compatibility method for urfave
func (p *PodmanCommand) GlobalIsSet(opt string) bool {
diff --git a/cmd/podman/cliconfig/config.go b/cmd/podman/cliconfig/config.go
index f7ac0de6c..2692ace36 100644
--- a/cmd/podman/cliconfig/config.go
+++ b/cmd/podman/cliconfig/config.go
@@ -88,12 +88,13 @@ type CheckpointValues struct {
type CommitValues struct {
PodmanCommand
- Change []string
- Format string
- Message string
- Author string
- Pause bool
- Quiet bool
+ Change []string
+ Format string
+ Message string
+ Author string
+ Pause bool
+ Quiet bool
+ IncludeVolumes bool
}
type ContainersPrune struct {
diff --git a/cmd/podman/commands.go b/cmd/podman/commands.go
index 7c660f7cb..9fea1494b 100644
--- a/cmd/podman/commands.go
+++ b/cmd/podman/commands.go
@@ -11,7 +11,6 @@ const remoteclient = false
// Commands that the local client implements
func getMainCommands() []*cobra.Command {
rootCommands := []*cobra.Command{
- _attachCommand,
_commitCommand,
_execCommand,
_generateCommand,
@@ -47,7 +46,6 @@ func getImageSubCommands() []*cobra.Command {
func getContainerSubCommands() []*cobra.Command {
return []*cobra.Command{
- _attachCommand,
_checkpointCommand,
_cleanupCommand,
_commitCommand,
@@ -104,7 +102,7 @@ func getSystemSubCommands() []*cobra.Command {
}
// Commands that the local client implements
-func getHealtcheckSubCommands() []*cobra.Command {
+func getHealthcheckSubCommands() []*cobra.Command {
return []*cobra.Command{
_healthcheckrunCommand,
}
diff --git a/cmd/podman/commands_remoteclient.go b/cmd/podman/commands_remoteclient.go
index 9b09e7dbc..278fe229c 100644
--- a/cmd/podman/commands_remoteclient.go
+++ b/cmd/podman/commands_remoteclient.go
@@ -49,6 +49,6 @@ func getSystemSubCommands() []*cobra.Command {
}
// Commands that the remoteclient implements
-func getHealtcheckSubCommands() []*cobra.Command {
+func getHealthcheckSubCommands() []*cobra.Command {
return []*cobra.Command{}
}
diff --git a/cmd/podman/commit.go b/cmd/podman/commit.go
index f7e206856..0077ff297 100644
--- a/cmd/podman/commit.go
+++ b/cmd/podman/commit.go
@@ -2,19 +2,19 @@ package main
import (
"fmt"
- "github.com/containers/libpod/cmd/podman/cliconfig"
- "github.com/spf13/cobra"
"io"
"os"
"strings"
"github.com/containers/buildah"
"github.com/containers/image/manifest"
+ "github.com/containers/libpod/cmd/podman/cliconfig"
"github.com/containers/libpod/cmd/podman/libpodruntime"
"github.com/containers/libpod/libpod"
"github.com/containers/libpod/libpod/image"
"github.com/containers/libpod/pkg/util"
"github.com/pkg/errors"
+ "github.com/spf13/cobra"
)
var (
@@ -47,7 +47,7 @@ func init() {
flags.StringVarP(&commitCommand.Author, "author", "a", "", "Set the author for the image committed")
flags.BoolVarP(&commitCommand.Pause, "pause", "p", false, "Pause container during commit")
flags.BoolVarP(&commitCommand.Quiet, "quiet", "q", false, "Suppress output")
-
+ flags.BoolVar(&commitCommand.IncludeVolumes, "include-volumes", false, "Include container volumes as image volumes")
}
func commitCmd(c *cliconfig.CommitValues) error {
@@ -109,11 +109,12 @@ func commitCmd(c *cliconfig.CommitValues) error {
PreferredManifestType: mimeType,
}
options := libpod.ContainerCommitOptions{
- CommitOptions: coptions,
- Pause: c.Pause,
- Message: c.Message,
- Changes: c.Change,
- Author: c.Author,
+ CommitOptions: coptions,
+ Pause: c.Pause,
+ IncludeVolumes: c.IncludeVolumes,
+ Message: c.Message,
+ Changes: c.Change,
+ Author: c.Author,
}
newImage, err := ctr.Commit(getContext(), reference, options)
if err != nil {
diff --git a/cmd/podman/container.go b/cmd/podman/container.go
index d1c42f673..380d1f250 100644
--- a/cmd/podman/container.go
+++ b/cmd/podman/container.go
@@ -50,6 +50,7 @@ var (
// Commands that are universally implemented.
containerCommands = []*cobra.Command{
+ _attachCommand,
_containerExistsCommand,
_contInspectSubCommand,
_diffCommand,
diff --git a/cmd/podman/create.go b/cmd/podman/create.go
index 1af3920dd..3267e5b7b 100644
--- a/cmd/podman/create.go
+++ b/cmd/podman/create.go
@@ -66,7 +66,7 @@ func createCmd(c *cliconfig.CreateValues) error {
}
func createInit(c *cliconfig.PodmanCommand) error {
- if c.Bool("trace") {
+ if !remote && c.Bool("trace") {
span, _ := opentracing.StartSpanFromContext(Ctx, "createInit")
defer span.Finish()
}
diff --git a/cmd/podman/healthcheck.go b/cmd/podman/healthcheck.go
index 48d6b6bbf..9fb099ffa 100644
--- a/cmd/podman/healthcheck.go
+++ b/cmd/podman/healthcheck.go
@@ -20,7 +20,7 @@ var healthcheckCommands []*cobra.Command
func init() {
healthcheckCommand.AddCommand(healthcheckCommands...)
- healthcheckCommand.AddCommand(getHealtcheckSubCommands()...)
+ healthcheckCommand.AddCommand(getHealthcheckSubCommands()...)
healthcheckCommand.SetUsageTemplate(UsageTemplate())
rootCmd.AddCommand(healthcheckCommand.Command)
}
diff --git a/cmd/podman/imagefilters/filters.go b/cmd/podman/imagefilters/filters.go
index 2932d61c0..aa5776599 100644
--- a/cmd/podman/imagefilters/filters.go
+++ b/cmd/podman/imagefilters/filters.go
@@ -37,9 +37,12 @@ func CreatedAfterFilter(createTime time.Time) ResultFilter {
}
// DanglingFilter allows you to filter images for dangling images
-func DanglingFilter() ResultFilter {
+func DanglingFilter(danglingImages bool) ResultFilter {
return func(i *adapter.ContainerImage) bool {
- return i.Dangling()
+ if danglingImages {
+ return i.Dangling()
+ }
+ return !i.Dangling()
}
}
diff --git a/cmd/podman/images.go b/cmd/podman/images.go
index 6133450be..c38d7035d 100644
--- a/cmd/podman/images.go
+++ b/cmd/podman/images.go
@@ -5,6 +5,7 @@ import (
"fmt"
"reflect"
"sort"
+ "strconv"
"strings"
"time"
"unicode"
@@ -318,13 +319,14 @@ func getImagesJSONOutput(ctx context.Context, images []*adapter.ContainerImage)
func generateImagesOutput(ctx context.Context, images []*adapter.ContainerImage, opts imagesOptions) error {
templateMap := GenImageOutputMap()
- if len(images) == 0 {
- return nil
- }
var out formats.Writer
switch opts.format {
case formats.JSONString:
+ // If 0 images are present, print nothing for JSON
+ if len(images) == 0 {
+ return nil
+ }
imagesOutput := getImagesJSONOutput(ctx, images)
out = formats.JSONStructArray{Output: imagesToGeneric([]imagesTemplateParams{}, imagesOutput)}
default:
@@ -359,6 +361,9 @@ func CreateFilterFuncs(ctx context.Context, r *adapter.LocalRuntime, filters []s
var filterFuncs []imagefilters.ResultFilter
for _, filter := range filters {
splitFilter := strings.Split(filter, "=")
+ if len(splitFilter) != 2 {
+ return nil, errors.Errorf("invalid filter syntax %s", filter)
+ }
switch splitFilter[0] {
case "before":
before, err := r.NewImageFromLocal(splitFilter[1])
@@ -373,7 +378,11 @@ func CreateFilterFuncs(ctx context.Context, r *adapter.LocalRuntime, filters []s
}
filterFuncs = append(filterFuncs, imagefilters.CreatedAfterFilter(after.Created()))
case "dangling":
- filterFuncs = append(filterFuncs, imagefilters.DanglingFilter())
+ danglingImages, err := strconv.ParseBool(splitFilter[1])
+ if err != nil {
+ return nil, errors.Wrapf(err, "invalid filter dangling=%s", splitFilter[1])
+ }
+ filterFuncs = append(filterFuncs, imagefilters.DanglingFilter(danglingImages))
case "label":
labelFilter := strings.Join(splitFilter[1:], "=")
filterFuncs = append(filterFuncs, imagefilters.LabelFilter(ctx, labelFilter))
diff --git a/cmd/podman/main.go b/cmd/podman/main.go
index 7c765a0e0..35a94b3db 100644
--- a/cmd/podman/main.go
+++ b/cmd/podman/main.go
@@ -3,26 +3,18 @@ package main
import (
"context"
"io"
- "io/ioutil"
- "log/syslog"
"os"
- "runtime/pprof"
- "strconv"
- "strings"
"syscall"
"github.com/containers/libpod/cmd/podman/cliconfig"
- "github.com/containers/libpod/cmd/podman/libpodruntime"
"github.com/containers/libpod/libpod"
_ "github.com/containers/libpod/pkg/hooks/0.1.0"
"github.com/containers/libpod/pkg/rootless"
- "github.com/containers/libpod/pkg/tracing"
"github.com/containers/libpod/version"
"github.com/containers/storage/pkg/reexec"
"github.com/opentracing/opentracing-go"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
- lsyslog "github.com/sirupsen/logrus/hooks/syslog"
"github.com/spf13/cobra"
)
@@ -38,6 +30,7 @@ var (
// Commands that the remote and local client have
// implemented.
var mainCommands = []*cobra.Command{
+ _attachCommand,
_buildCommand,
_diffCommand,
_createCommand,
@@ -88,40 +81,13 @@ func init() {
cobra.OnInitialize(initConfig)
rootCmd.TraverseChildren = true
rootCmd.Version = version.Version
- rootCmd.PersistentFlags().StringVar(&MainGlobalOpts.CGroupManager, "cgroup-manager", "", "Cgroup manager to use (cgroupfs or systemd, default systemd)")
- // -c is deprecated due to conflict with -c on subcommands
- rootCmd.PersistentFlags().StringVar(&MainGlobalOpts.CpuProfile, "cpu-profile", "", "Path for the cpu profiling results")
- rootCmd.PersistentFlags().StringVar(&MainGlobalOpts.Config, "config", "", "Path of a libpod config file detailing container server configuration options")
- rootCmd.PersistentFlags().StringVar(&MainGlobalOpts.ConmonPath, "conmon", "", "Path of the conmon binary")
- rootCmd.PersistentFlags().StringVar(&MainGlobalOpts.NetworkCmdPath, "network-cmd-path", "", "Path to the command for configuring the network")
- rootCmd.PersistentFlags().StringVar(&MainGlobalOpts.CniConfigDir, "cni-config-dir", "", "Path of the configuration directory for CNI networks")
- rootCmd.PersistentFlags().StringVar(&MainGlobalOpts.DefaultMountsFile, "default-mounts-file", "", "Path to default mounts file")
- rootCmd.PersistentFlags().MarkHidden("defaults-mount-file")
- // Override default --help information of `--help` global flag
- var dummyHelp bool
- rootCmd.PersistentFlags().BoolVar(&dummyHelp, "help", false, "Help for podman")
- rootCmd.PersistentFlags().StringSliceVar(&MainGlobalOpts.HooksDir, "hooks-dir", []string{}, "Set the OCI hooks directory path (may be set multiple times)")
- rootCmd.PersistentFlags().StringVar(&MainGlobalOpts.LogLevel, "log-level", "error", "Log messages above specified level: debug, info, warn, error, fatal or panic")
- rootCmd.PersistentFlags().IntVar(&MainGlobalOpts.MaxWorks, "max-workers", 0, "The maximum number of workers for parallel operations")
- rootCmd.PersistentFlags().MarkHidden("max-workers")
- rootCmd.PersistentFlags().StringVar(&MainGlobalOpts.Namespace, "namespace", "", "Set the libpod namespace, used to create separate views of the containers and pods on the system")
- rootCmd.PersistentFlags().StringVar(&MainGlobalOpts.Root, "root", "", "Path to the root directory in which data, including images, is stored")
- rootCmd.PersistentFlags().StringVar(&MainGlobalOpts.Runroot, "runroot", "", "Path to the 'run directory' where all state information is stored")
- rootCmd.PersistentFlags().StringVar(&MainGlobalOpts.Runtime, "runtime", "", "Path to the OCI-compatible binary used to run containers, default is /usr/bin/runc")
- // -s is depracated due to conflict with -s on subcommands
- rootCmd.PersistentFlags().StringVar(&MainGlobalOpts.StorageDriver, "storage-driver", "", "Select which storage driver is used to manage storage of images and containers (default is overlay)")
- rootCmd.PersistentFlags().StringSliceVar(&MainGlobalOpts.StorageOpts, "storage-opt", []string{}, "Used to pass an option to the storage driver")
- rootCmd.PersistentFlags().BoolVar(&MainGlobalOpts.Syslog, "syslog", false, "Output logging information to syslog as well as the console")
-
- rootCmd.PersistentFlags().StringVar(&MainGlobalOpts.TmpDir, "tmpdir", "", "Path to the tmp directory")
- rootCmd.PersistentFlags().BoolVar(&MainGlobalOpts.Trace, "trace", false, "Enable opentracing output")
// Override default --help information of `--version` global flag
var dummyVersion bool
rootCmd.PersistentFlags().BoolVar(&dummyVersion, "version", false, "Version for podman")
rootCmd.AddCommand(mainCommands...)
rootCmd.AddCommand(getMainCommands()...)
-
}
+
func initConfig() {
// we can do more stuff in here.
}
@@ -131,63 +97,16 @@ func before(cmd *cobra.Command, args []string) error {
logrus.Errorf(err.Error())
os.Exit(1)
}
- if os.Geteuid() != 0 && cmd != _searchCommand && cmd != _versionCommand && !strings.HasPrefix(cmd.Use, "help") {
- podmanCmd := cliconfig.PodmanCommand{
- cmd,
- args,
- MainGlobalOpts,
- }
- runtime, err := libpodruntime.GetRuntime(&podmanCmd)
- if err != nil {
- return errors.Wrapf(err, "could not get runtime")
- }
- defer runtime.Shutdown(false)
-
- ctrs, err := runtime.GetRunningContainers()
- if err != nil {
- logrus.Errorf(err.Error())
- os.Exit(1)
- }
- var became bool
- var ret int
- if len(ctrs) == 0 {
- became, ret, err = rootless.BecomeRootInUserNS()
- } else {
- for _, ctr := range ctrs {
- data, err := ioutil.ReadFile(ctr.Config().ConmonPidFile)
- if err != nil {
- logrus.Errorf(err.Error())
- os.Exit(1)
- }
- conmonPid, err := strconv.Atoi(string(data))
- if err != nil {
- logrus.Errorf(err.Error())
- os.Exit(1)
- }
- became, ret, err = rootless.JoinUserAndMountNS(uint(conmonPid))
- if err == nil {
- break
- }
- }
- }
- if err != nil {
- logrus.Errorf(err.Error())
- os.Exit(1)
- }
- if became {
- os.Exit(ret)
- }
+ if err := setupRootless(cmd, args); err != nil {
+ return err
}
- if MainGlobalOpts.Syslog {
- hook, err := lsyslog.NewSyslogHook("", "", syslog.LOG_INFO, "")
- if err == nil {
- logrus.AddHook(hook)
- }
+ // Set log level; if not log-level is provided, default to error
+ logLevel := MainGlobalOpts.LogLevel
+ if logLevel == "" {
+ logLevel = "error"
}
-
- // Set log level
- level, err := logrus.ParseLevel(MainGlobalOpts.LogLevel)
+ level, err := logrus.ParseLevel(logLevel)
if err != nil {
return err
}
@@ -212,36 +131,11 @@ func before(cmd *cobra.Command, args []string) error {
// Be sure we can create directories with 0755 mode.
syscall.Umask(0022)
-
- if cmd.Flag("cpu-profile").Changed {
- f, err := os.Create(MainGlobalOpts.CpuProfile)
- if err != nil {
- return errors.Wrapf(err, "unable to create cpu profiling file %s",
- MainGlobalOpts.CpuProfile)
- }
- pprof.StartCPUProfile(f)
- }
- if cmd.Flag("trace").Changed {
- var tracer opentracing.Tracer
- tracer, closer = tracing.Init("podman")
- opentracing.SetGlobalTracer(tracer)
-
- span = tracer.StartSpan("before-context")
-
- Ctx = opentracing.ContextWithSpan(context.Background(), span)
- }
- return nil
+ return profileOn(cmd)
}
func after(cmd *cobra.Command, args []string) error {
- if cmd.Flag("cpu-profile").Changed {
- pprof.StopCPUProfile()
- }
- if cmd.Flag("trace").Changed {
- span.Finish()
- closer.Close()
- }
- return nil
+ return profileOff(cmd)
}
func main() {
diff --git a/cmd/podman/main_local.go b/cmd/podman/main_local.go
new file mode 100644
index 000000000..e008a4617
--- /dev/null
+++ b/cmd/podman/main_local.go
@@ -0,0 +1,155 @@
+// +build !remoteclient
+
+package main
+
+import (
+ "context"
+ "github.com/containers/libpod/cmd/podman/cliconfig"
+ "github.com/containers/libpod/cmd/podman/libpodruntime"
+ "github.com/containers/libpod/pkg/rootless"
+ "io/ioutil"
+ "log/syslog"
+ "os"
+ "runtime/pprof"
+ "strconv"
+ "strings"
+
+ "github.com/containers/libpod/pkg/tracing"
+ "github.com/opentracing/opentracing-go"
+ "github.com/pkg/errors"
+ "github.com/sirupsen/logrus"
+ lsyslog "github.com/sirupsen/logrus/hooks/syslog"
+ "github.com/spf13/cobra"
+)
+
+const remote = false
+
+func init() {
+
+ rootCmd.PersistentFlags().StringVar(&MainGlobalOpts.CGroupManager, "cgroup-manager", "", "Cgroup manager to use (cgroupfs or systemd, default systemd)")
+ // -c is deprecated due to conflict with -c on subcommands
+ rootCmd.PersistentFlags().StringVar(&MainGlobalOpts.CpuProfile, "cpu-profile", "", "Path for the cpu profiling results")
+ rootCmd.PersistentFlags().StringVar(&MainGlobalOpts.Config, "config", "", "Path of a libpod config file detailing container server configuration options")
+ rootCmd.PersistentFlags().StringVar(&MainGlobalOpts.ConmonPath, "conmon", "", "Path of the conmon binary")
+ rootCmd.PersistentFlags().StringVar(&MainGlobalOpts.NetworkCmdPath, "network-cmd-path", "", "Path to the command for configuring the network")
+ rootCmd.PersistentFlags().StringVar(&MainGlobalOpts.CniConfigDir, "cni-config-dir", "", "Path of the configuration directory for CNI networks")
+ rootCmd.PersistentFlags().StringVar(&MainGlobalOpts.DefaultMountsFile, "default-mounts-file", "", "Path to default mounts file")
+ rootCmd.PersistentFlags().MarkHidden("defaults-mount-file")
+ // Override default --help information of `--help` global flag
+ var dummyHelp bool
+ rootCmd.PersistentFlags().BoolVar(&dummyHelp, "help", false, "Help for podman")
+ rootCmd.PersistentFlags().StringSliceVar(&MainGlobalOpts.HooksDir, "hooks-dir", []string{}, "Set the OCI hooks directory path (may be set multiple times)")
+ rootCmd.PersistentFlags().StringVar(&MainGlobalOpts.LogLevel, "log-level", "error", "Log messages above specified level: debug, info, warn, error, fatal or panic")
+ rootCmd.PersistentFlags().IntVar(&MainGlobalOpts.MaxWorks, "max-workers", 0, "The maximum number of workers for parallel operations")
+ rootCmd.PersistentFlags().MarkHidden("max-workers")
+ rootCmd.PersistentFlags().StringVar(&MainGlobalOpts.Namespace, "namespace", "", "Set the libpod namespace, used to create separate views of the containers and pods on the system")
+ rootCmd.PersistentFlags().StringVar(&MainGlobalOpts.Root, "root", "", "Path to the root directory in which data, including images, is stored")
+ rootCmd.PersistentFlags().StringVar(&MainGlobalOpts.Runroot, "runroot", "", "Path to the 'run directory' where all state information is stored")
+ rootCmd.PersistentFlags().StringVar(&MainGlobalOpts.Runtime, "runtime", "", "Path to the OCI-compatible binary used to run containers, default is /usr/bin/runc")
+ // -s is depracated due to conflict with -s on subcommands
+ rootCmd.PersistentFlags().StringVar(&MainGlobalOpts.StorageDriver, "storage-driver", "", "Select which storage driver is used to manage storage of images and containers (default is overlay)")
+ rootCmd.PersistentFlags().StringSliceVar(&MainGlobalOpts.StorageOpts, "storage-opt", []string{}, "Used to pass an option to the storage driver")
+ rootCmd.PersistentFlags().BoolVar(&MainGlobalOpts.Syslog, "syslog", false, "Output logging information to syslog as well as the console")
+
+ rootCmd.PersistentFlags().StringVar(&MainGlobalOpts.TmpDir, "tmpdir", "", "Path to the tmp directory")
+ rootCmd.PersistentFlags().BoolVar(&MainGlobalOpts.Trace, "trace", false, "Enable opentracing output")
+}
+
+func setSyslog() error {
+ if MainGlobalOpts.Syslog {
+ hook, err := lsyslog.NewSyslogHook("", "", syslog.LOG_INFO, "")
+ if err == nil {
+ logrus.AddHook(hook)
+ return nil
+ }
+ return err
+ }
+ return nil
+}
+
+func profileOn(cmd *cobra.Command) error {
+ if cmd.Flag("cpu-profile").Changed {
+ f, err := os.Create(MainGlobalOpts.CpuProfile)
+ if err != nil {
+ return errors.Wrapf(err, "unable to create cpu profiling file %s",
+ MainGlobalOpts.CpuProfile)
+ }
+ if err := pprof.StartCPUProfile(f); err != nil {
+ return err
+ }
+ }
+
+ if cmd.Flag("trace").Changed {
+ var tracer opentracing.Tracer
+ tracer, closer = tracing.Init("podman")
+ opentracing.SetGlobalTracer(tracer)
+
+ span = tracer.StartSpan("before-context")
+
+ Ctx = opentracing.ContextWithSpan(context.Background(), span)
+ }
+ return nil
+}
+
+func profileOff(cmd *cobra.Command) error {
+ if cmd.Flag("cpu-profile").Changed {
+ pprof.StopCPUProfile()
+ }
+ if cmd.Flag("trace").Changed {
+ span.Finish()
+ closer.Close()
+ }
+ return nil
+}
+
+func setupRootless(cmd *cobra.Command, args []string) error {
+ if os.Geteuid() == 0 || cmd == _searchCommand || cmd == _versionCommand || strings.HasPrefix(cmd.Use, "help") {
+ return nil
+ }
+ podmanCmd := cliconfig.PodmanCommand{
+ cmd,
+ args,
+ MainGlobalOpts,
+ }
+ runtime, err := libpodruntime.GetRuntime(&podmanCmd)
+ if err != nil {
+ return errors.Wrapf(err, "could not get runtime")
+ }
+ defer runtime.Shutdown(false)
+
+ ctrs, err := runtime.GetRunningContainers()
+ if err != nil {
+ logrus.Errorf(err.Error())
+ os.Exit(1)
+ }
+ var became bool
+ var ret int
+ if len(ctrs) == 0 {
+ became, ret, err = rootless.BecomeRootInUserNS()
+ } else {
+ for _, ctr := range ctrs {
+ data, err := ioutil.ReadFile(ctr.Config().ConmonPidFile)
+ if err != nil {
+ logrus.Errorf(err.Error())
+ os.Exit(1)
+ }
+ conmonPid, err := strconv.Atoi(string(data))
+ if err != nil {
+ logrus.Errorf(err.Error())
+ os.Exit(1)
+ }
+ became, ret, err = rootless.JoinUserAndMountNS(uint(conmonPid))
+ if err == nil {
+ break
+ }
+ }
+ }
+ if err != nil {
+ logrus.Errorf(err.Error())
+ os.Exit(1)
+ }
+ if became {
+ os.Exit(ret)
+ }
+ return nil
+}
diff --git a/cmd/podman/main_remote.go b/cmd/podman/main_remote.go
new file mode 100644
index 000000000..2a7d184cd
--- /dev/null
+++ b/cmd/podman/main_remote.go
@@ -0,0 +1,43 @@
+// +build remoteclient
+
+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.
+}
+
+func setSyslog() error {
+ return nil
+}
+
+func profileOn(cmd *cobra.Command) error {
+ return nil
+}
+
+func profileOff(cmd *cobra.Command) error {
+ return nil
+}
+
+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/pull.go b/cmd/podman/pull.go
index 2aac28642..491d3a8c2 100644
--- a/cmd/podman/pull.go
+++ b/cmd/podman/pull.go
@@ -46,12 +46,16 @@ func init() {
pullCommand.SetUsageTemplate(UsageTemplate())
flags := pullCommand.Flags()
flags.BoolVar(&pullCommand.AllTags, "all-tags", false, "All tagged images inthe repository will be pulled")
- flags.StringVar(&pullCommand.Authfile, "authfile", "", "Path of the authentication file. Default is ${XDG_RUNTIME_DIR}/containers/auth.json. Use REGISTRY_AUTH_FILE environment variable to override")
flags.StringVar(&pullCommand.CertDir, "cert-dir", "", "`Pathname` of a directory containing TLS certificates and keys")
flags.StringVar(&pullCommand.Creds, "creds", "", "`Credentials` (USERNAME:PASSWORD) to use for authenticating to a registry")
flags.BoolVarP(&pullCommand.Quiet, "quiet", "q", false, "Suppress output information when pulling images")
- flags.StringVar(&pullCommand.SignaturePolicy, "signature-policy", "", "`Pathname` of signature policy file (not usually used)")
- flags.BoolVar(&pullCommand.TlsVerify, "tls-verify", true, "Require HTTPS and verify certificates when contacting registries")
+
+ // Disabled flags for the remote client
+ if !remote {
+ flags.StringVar(&pullCommand.Authfile, "authfile", "", "Path of the authentication file. Default is ${XDG_RUNTIME_DIR}/containers/auth.json. Use REGISTRY_AUTH_FILE environment variable to override")
+ flags.StringVar(&pullCommand.SignaturePolicy, "signature-policy", "", "`Pathname` of signature policy file (not usually used)")
+ flags.BoolVar(&pullCommand.TlsVerify, "tls-verify", true, "Require HTTPS and verify certificates when contacting registries")
+ }
}
diff --git a/cmd/podman/push.go b/cmd/podman/push.go
index a1dac24ae..a5638a698 100644
--- a/cmd/podman/push.go
+++ b/cmd/podman/push.go
@@ -45,16 +45,20 @@ func init() {
pushCommand.SetUsageTemplate(UsageTemplate())
flags := pushCommand.Flags()
flags.MarkHidden("signature-policy")
- flags.StringVar(&pushCommand.Authfile, "authfile", "", "Path of the authentication file. Default is ${XDG_RUNTIME_DIR}/containers/auth.json. Use REGISTRY_AUTH_FILE environment variable to override")
flags.StringVar(&pushCommand.CertDir, "cert-dir", "", "`Pathname` of a directory containing TLS certificates and keys")
- flags.BoolVar(&pushCommand.Compress, "compress", false, "Compress tarball image layers when pushing to a directory using the 'dir' transport. (default is same compression type as source)")
flags.StringVar(&pushCommand.Creds, "creds", "", "`Credentials` (USERNAME:PASSWORD) to use for authenticating to a registry")
flags.StringVarP(&pushCommand.Format, "format", "f", "", "Manifest type (oci, v2s1, or v2s2) to use when pushing an image using the 'dir:' transport (default is manifest type of source)")
flags.BoolVarP(&pushCommand.Quiet, "quiet", "q", false, "Don't output progress information when pushing images")
flags.BoolVar(&pushCommand.RemoveSignatures, "remove-signatures", false, "Discard any pre-existing signatures in the image")
- flags.StringVar(&pushCommand.SignaturePolicy, "signature-policy", "", "`Pathname` of signature policy file (not usually used)")
flags.StringVar(&pushCommand.SignBy, "sign-by", "", "Add a signature at the destination using the specified key")
- flags.BoolVar(&pushCommand.TlsVerify, "tls-verify", true, "Require HTTPS and verify certificates when contacting registries")
+
+ // Disabled flags for the remote client
+ if !remote {
+ flags.StringVar(&pushCommand.Authfile, "authfile", "", "Path of the authentication file. Default is ${XDG_RUNTIME_DIR}/containers/auth.json. Use REGISTRY_AUTH_FILE environment variable to override")
+ flags.BoolVar(&pushCommand.Compress, "compress", false, "Compress tarball image layers when pushing to a directory using the 'dir' transport. (default is same compression type as source)")
+ flags.StringVar(&pushCommand.SignaturePolicy, "signature-policy", "", "`Pathname` of signature policy file (not usually used)")
+ flags.BoolVar(&pushCommand.TlsVerify, "tls-verify", true, "Require HTTPS and verify certificates when contacting registries")
+ }
}
func pushCmd(c *cliconfig.PushValues) error {
diff --git a/cmd/podman/run.go b/cmd/podman/run.go
index bac5c3c18..d3158de6b 100644
--- a/cmd/podman/run.go
+++ b/cmd/podman/run.go
@@ -38,7 +38,7 @@ func init() {
}
func runCmd(c *cliconfig.RunValues) error {
- if c.Bool("trace") {
+ if !remote && c.Bool("trace") {
span, _ := opentracing.StartSpanFromContext(Ctx, "runCmd")
defer span.Finish()
}
diff --git a/cmd/podman/run_test.go b/cmd/podman/run_test.go
index 27b34c323..af9e6923c 100644
--- a/cmd/podman/run_test.go
+++ b/cmd/podman/run_test.go
@@ -83,7 +83,7 @@ func getRuntimeSpec(c *cliconfig.PodmanCommand) (*spec.Spec, error) {
createConfig, err := parseCreateOpts(c, runtime, "alpine", generateAlpineImageData())
*/
ctx := getContext()
- genericResults := shared.NewIntermediateLayer(c)
+ genericResults := shared.NewIntermediateLayer(c, false)
createConfig, err := shared.ParseCreateOpts(ctx, &genericResults, nil, "alpine", generateAlpineImageData())
if err != nil {
return nil, err
diff --git a/cmd/podman/shared/intermediate.go b/cmd/podman/shared/intermediate.go
index 9afbd68c8..2e1827561 100644
--- a/cmd/podman/shared/intermediate.go
+++ b/cmd/podman/shared/intermediate.go
@@ -360,7 +360,7 @@ func newCRStringArray(c *cliconfig.PodmanCommand, flag string) CRStringArray {
}
// NewIntermediateLayer creates a GenericCLIResults from a create or run cli-command
-func NewIntermediateLayer(c *cliconfig.PodmanCommand) GenericCLIResults {
+func NewIntermediateLayer(c *cliconfig.PodmanCommand, remote bool) GenericCLIResults {
m := make(map[string]GenericCLIResult)
m["add-host"] = newCRStringSlice(c, "add-host")
@@ -458,8 +458,10 @@ func NewIntermediateLayer(c *cliconfig.PodmanCommand) GenericCLIResults {
m["volumes-from"] = newCRStringSlice(c, "volumes-from")
m["workdir"] = newCRString(c, "workdir")
// global flag
- m["trace"] = newCRBool(c, "trace")
- m["syslog"] = newCRBool(c, "syslog")
+ if !remote {
+ m["trace"] = newCRBool(c, "trace")
+ m["syslog"] = newCRBool(c, "syslog")
+ }
return GenericCLIResults{m, c.InputArgs}
}
diff --git a/cmd/podman/varlink/io.podman.varlink b/cmd/podman/varlink/io.podman.varlink
index d8905326c..c6997cd3f 100644
--- a/cmd/podman/varlink/io.podman.varlink
+++ b/cmd/podman/varlink/io.podman.varlink
@@ -658,8 +658,11 @@ method PauseContainer(name: string) -> (container: string)
# See also [PauseContainer](#PauseContainer).
method UnpauseContainer(name: string) -> (container: string)
-# This method has not be implemented yet.
-# method AttachToContainer() -> (notimplemented: NotImplemented)
+# Attach takes the name or ID of a container and sets up a the ability to remotely attach to its console. The start
+# bool is whether you wish to start the container in question first.
+method Attach(name: string, detachKeys: string, start: bool) -> ()
+
+method AttachControl(name: string) -> ()
# GetAttachSockets takes the name or ID of an existing container. It returns file paths for two sockets needed
# to properly communicate with a container. The first is the actual I/O socket that the container uses. The
@@ -1154,6 +1157,9 @@ method PodStateData(name: string) -> (config: string)
# This call is for the development of Podman only and should not be used.
method CreateFromCC(in: []string) -> (id: string)
+# Spec returns the oci spec for a container. This call is for development of Podman only and generally should not be used.
+method Spec(name: string) -> (config: string)
+
# Sendfile allows a remote client to send a file to the host
method SendFile(type: string, length: int) -> (file_handle: string)
diff --git a/commands.md b/commands.md
index 156a1cdf6..1c05640f2 100644
--- a/commands.md
+++ b/commands.md
@@ -4,8 +4,8 @@
## Podman Commands
-Command | Description | Demo
-:----------------------------------------------------------------------- | :------------------------------------------------------------------------- | :--------------------------------------------------------------------------
+Command | Description | Demo | Script
+:----------------------------------------------------------------------- | :------------------------------------------------------------------------- | :-------------------------------------------------------------------------- | :--------------------------------------------------------------------------
[podman(1)](/docs/podman.1.md) | Simple management tool for pods and images |
[podman-attach(1)](/docs/podman-attach.1.md) | Attach to a running container |
[podman-build(1)](/docs/podman-build.1.md) | Build an image using instructions from Dockerfiles |
@@ -31,7 +31,7 @@ Command | Descr
[podman-image-prune(1)](/docs/podman-image-prune.1.md) | Remove all unused images |
[podman-image-sign(1)](/docs/podman-image-sign.1.md) | Create a signature for an image |
[podman-image-trust(1)](/docs/podman-image-trust.1.md) | Manage container registry image trust policy |
-[podman-images(1)](/docs/podman-images.1.md) | List images in local storage | [![...](/docs/play.png)](https://asciinema.org/a/133649)
+[podman-images(1)](/docs/podman-images.1.md) | List images in local storage | [![...](/docs/play.png)](https://podman.io/asciinema/podman/images/) | [Here](https://github.com/containers/Demos/blob/master/podman_cli/podman_images.sh)
[podman-import(1)](/docs/podman-import.1.md) | Import a tarball and save it as a filesystem image |
[podman-info(1)](/docs/podman-info.1.md) | Display system information |
[podman-inspect(1)](/docs/podman-inspect.1.md) | Display the configuration of a container or image | [![...](/docs/play.png)](https://asciinema.org/a/133418)
diff --git a/completions/bash/podman b/completions/bash/podman
index a3f381962..3616c6ca1 100644
--- a/completions/bash/podman
+++ b/completions/bash/podman
@@ -2388,7 +2388,7 @@ _podman_logout() {
_complete_ "$options_with_args" "$boolean_options"
}
-_podman_healtcheck_run() {
+_podman_healthcheck_run() {
local options_with_args=""
local boolean_options="
diff --git a/docs/podman-commit.1.md b/docs/podman-commit.1.md
index acde51859..7c74d7a33 100644
--- a/docs/podman-commit.1.md
+++ b/docs/podman-commit.1.md
@@ -39,6 +39,10 @@ not specifically set, the default format used is _oci_.
Write the image ID to the file.
+**--include-volumes**
+
+Include in the committed image any volumes added to the container by the `--volume` or `--mount` options to the `podman create` and `podman run` commands.
+
**--message, -m**
Set commit message for committed image. The message field is not supported in _oci_ format.
diff --git a/docs/podman-healthcheck-run.1.md b/docs/podman-healthcheck-run.1.md
index e19c6250c..21f2d9b20 100644
--- a/docs/podman-healthcheck-run.1.md
+++ b/docs/podman-healthcheck-run.1.md
@@ -29,7 +29,7 @@ Print usage statement
## EXAMPLES
```
-$ podman healtcheck run mywebapp
+$ podman healthcheck run mywebapp
```
## SEE ALSO
diff --git a/install.md b/install.md
index 5fe150db2..80ae03208 100644
--- a/install.md
+++ b/install.md
@@ -57,6 +57,7 @@ sudo yum module install -y container-tools:1.0
sudo apt-get update -qq
sudo apt-get install -qq -y software-properties-common
sudo add-apt-repository -y ppa:projectatomic/ppa
+sudo apt-get update -qq
sudo apt-get -qq -y install podman
```
diff --git a/libpod/container.go b/libpod/container.go
index 523e571b1..de4674222 100644
--- a/libpod/container.go
+++ b/libpod/container.go
@@ -363,7 +363,7 @@ type ContainerConfig struct {
// Systemd tells libpod to setup the container in systemd mode
Systemd bool `json:"systemd"`
- // HealtchCheckConfig has the health check command and related timings
+ // HealthCheckConfig has the health check command and related timings
HealthCheckConfig *manifest.Schema2HealthConfig `json:"healthcheck"`
}
diff --git a/libpod/container_commit.go b/libpod/container_commit.go
index 0604a550b..db67f7a30 100644
--- a/libpod/container_commit.go
+++ b/libpod/container_commit.go
@@ -20,10 +20,11 @@ import (
//libpod
type ContainerCommitOptions struct {
buildah.CommitOptions
- Pause bool
- Author string
- Message string
- Changes []string
+ Pause bool
+ IncludeVolumes bool
+ Author string
+ Message string
+ Changes []string
}
// ChangeCmds is the list of valid Changes commands to passed to the Commit call
@@ -113,9 +114,11 @@ func (c *Container) Commit(ctx context.Context, destImage string, options Contai
// User
importBuilder.SetUser(c.User())
// Volumes
- for _, v := range c.config.UserVolumes {
- if v != "" {
- importBuilder.AddVolume(v)
+ if options.IncludeVolumes {
+ for _, v := range c.config.UserVolumes {
+ if v != "" {
+ importBuilder.AddVolume(v)
+ }
}
}
// Workdir
diff --git a/libpod/container_internal.go b/libpod/container_internal.go
index 3c7319963..36b5e01df 100644
--- a/libpod/container_internal.go
+++ b/libpod/container_internal.go
@@ -350,7 +350,7 @@ func (c *Container) teardownStorage() error {
artifacts := filepath.Join(c.config.StaticDir, artifactsDir)
if err := os.RemoveAll(artifacts); err != nil {
- return errors.Wrapf(err, "error removing artifacts %q", artifacts)
+ return errors.Wrapf(err, "error removing container %s artifacts %q", c.ID(), artifacts)
}
if err := c.cleanupStorage(); err != nil {
@@ -1113,13 +1113,13 @@ func (c *Container) cleanup(ctx context.Context) error {
// Remove healthcheck unit/timer file if it execs
if c.config.HealthCheckConfig != nil {
if err := c.removeTimer(); err != nil {
- logrus.Error(err)
+ logrus.Errorf("Error removing timer for container %s healthcheck: %v", c.ID(), err)
}
}
// Clean up network namespace, if present
if err := c.cleanupNetwork(); err != nil {
- lastError = err
+ lastError = errors.Wrapf(err, "error removing container %s network", c.ID())
}
// Unmount storage
@@ -1127,7 +1127,7 @@ func (c *Container) cleanup(ctx context.Context) error {
if lastError != nil {
logrus.Errorf("Error unmounting container %s storage: %v", c.ID(), err)
} else {
- lastError = err
+ lastError = errors.Wrapf(err, "error unmounting container %s storage", c.ID())
}
}
diff --git a/libpod/healthcheck.go b/libpod/healthcheck.go
index d8f56860b..3a6609740 100644
--- a/libpod/healthcheck.go
+++ b/libpod/healthcheck.go
@@ -41,7 +41,7 @@ const (
HealthCheckDefined HealthCheckStatus = iota
// MaxHealthCheckNumberLogs is the maximum number of attempts we keep
- // in the healtcheck history file
+ // in the healthcheck history file
MaxHealthCheckNumberLogs int = 5
// MaxHealthCheckLogLength in characters
MaxHealthCheckLogLength = 500
diff --git a/libpod/oci_linux.go b/libpod/oci_linux.go
index 8c0abad80..01f7c3649 100644
--- a/libpod/oci_linux.go
+++ b/libpod/oci_linux.go
@@ -3,15 +3,20 @@
package libpod
import (
+ "fmt"
"os"
"os/exec"
"path/filepath"
+ "runtime"
"strings"
"syscall"
"github.com/containerd/cgroups"
+ "github.com/containers/libpod/pkg/rootless"
"github.com/containers/libpod/utils"
+ pmount "github.com/containers/storage/pkg/mount"
spec "github.com/opencontainers/runtime-spec/specs-go"
+ "github.com/pkg/errors"
"github.com/sirupsen/logrus"
"golang.org/x/sys/unix"
)
@@ -91,6 +96,54 @@ func (r *OCIRuntime) createContainer(ctr *Container, cgroupParent string, restor
return err
}
}
+
+ // if we are running a non privileged container, be sure to umount some kernel paths so they are not
+ // bind mounted inside the container at all.
+ if !ctr.config.Privileged && !rootless.IsRootless() {
+ ch := make(chan error)
+ go func() {
+ runtime.LockOSThread()
+ err := func() error {
+ fd, err := os.Open(fmt.Sprintf("/proc/%d/task/%d/ns/mnt", os.Getpid(), unix.Gettid()))
+ if err != nil {
+ return err
+ }
+ defer fd.Close()
+
+ // create a new mountns on the current thread
+ if err = unix.Unshare(unix.CLONE_NEWNS); err != nil {
+ return err
+ }
+ defer unix.Setns(int(fd.Fd()), unix.CLONE_NEWNS)
+
+ // don't spread our mounts around. We are setting only /sys to be slave
+ // so that the cleanup process is still able to umount the storage and the
+ // changes are propagated to the host.
+ err = unix.Mount("/sys", "/sys", "none", unix.MS_REC|unix.MS_SLAVE, "")
+ if err != nil {
+ return errors.Wrapf(err, "cannot make /sys slave")
+ }
+
+ mounts, err := pmount.GetMounts()
+ if err != nil {
+ return err
+ }
+ for _, m := range mounts {
+ if !strings.HasPrefix(m.Mountpoint, "/sys/kernel") {
+ continue
+ }
+ err = unix.Unmount(m.Mountpoint, 0)
+ if err != nil {
+ return errors.Wrapf(err, "cannot unmount %s", m.Mountpoint)
+ }
+ }
+ return r.createOCIContainer(ctr, cgroupParent, restoreOptions)
+ }()
+ ch <- err
+ }()
+ err := <-ch
+ return err
+ }
}
return r.createOCIContainer(ctr, cgroupParent, restoreOptions)
}
diff --git a/libpod/options.go b/libpod/options.go
index 9326e54e4..8038f1935 100644
--- a/libpod/options.go
+++ b/libpod/options.go
@@ -17,7 +17,8 @@ import (
)
var (
- nameRegex = regexp.MustCompile("^[a-zA-Z0-9][a-zA-Z0-9_.-]*$")
+ nameRegex = regexp.MustCompile("^[a-zA-Z0-9][a-zA-Z0-9_.-]*$")
+ regexError = errors.Wrapf(ErrInvalidArg, "names must match [a-zA-Z0-9][a-zA-Z0-9_.-]*")
)
// Runtime Creation Options
@@ -593,7 +594,7 @@ func WithName(name string) CtrCreateOption {
// Check the name against a regex
if !nameRegex.MatchString(name) {
- return errors.Wrapf(ErrInvalidArg, "name must match regex [a-zA-Z0-9_-]+")
+ return regexError
}
ctr.config.Name = name
@@ -1276,7 +1277,7 @@ func WithVolumeName(name string) VolumeCreateOption {
// Check the name against a regex
if !nameRegex.MatchString(name) {
- return errors.Wrapf(ErrInvalidArg, "name must match regex [a-zA-Z0-9_-]+")
+ return regexError
}
volume.config.Name = name
@@ -1382,7 +1383,7 @@ func WithPodName(name string) PodCreateOption {
// Check the name against a regex
if !nameRegex.MatchString(name) {
- return errors.Wrapf(ErrInvalidArg, "name must match regex [a-zA-Z0-9_-]+")
+ return regexError
}
pod.config.Name = name
diff --git a/libpod/runtime.go b/libpod/runtime.go
index 4dd2707e8..3b1c2be98 100644
--- a/libpod/runtime.go
+++ b/libpod/runtime.go
@@ -870,6 +870,20 @@ func makeRuntime(runtime *Runtime) (err error) {
_, err = os.Stat(runtimeAliveFile)
if err != nil {
+ // If we need to refresh, then it is safe to assume there are
+ // no containers running. Create immediately a namespace, as
+ // we will need to access the storage.
+ if os.Geteuid() != 0 {
+ aliveLock.Unlock()
+ became, ret, err := rootless.BecomeRootInUserNS()
+ if err != nil {
+ return err
+ }
+ if became {
+ os.Exit(ret)
+ }
+
+ }
// If the file doesn't exist, we need to refresh the state
// This will trigger on first use as well, but refreshing an
// empty state only creates a single file
diff --git a/libpod/runtime_ctr.go b/libpod/runtime_ctr.go
index 800b42851..85b860268 100644
--- a/libpod/runtime_ctr.go
+++ b/libpod/runtime_ctr.go
@@ -372,7 +372,7 @@ func (r *Runtime) removeContainer(ctx context.Context, c *Container, force bool,
// Clean up network namespace, cgroups, mounts
if err := c.cleanup(ctx); err != nil {
if cleanupErr == nil {
- cleanupErr = err
+ cleanupErr = errors.Wrapf(err, "error cleaning up container %s", c.ID())
} else {
logrus.Errorf("cleanup network, cgroups, mounts: %v", err)
}
@@ -404,12 +404,14 @@ func (r *Runtime) removeContainer(ctx context.Context, c *Container, force bool,
// Deallocate the container's lock
if err := c.lock.Free(); err != nil {
if cleanupErr == nil {
- cleanupErr = err
+ cleanupErr = errors.Wrapf(err, "error freeing lock for container %s", c.ID())
} else {
logrus.Errorf("free container lock: %v", err)
}
}
+ c.newContainerEvent(events.Remove)
+
if !removeVolume {
return cleanupErr
}
@@ -425,7 +427,6 @@ func (r *Runtime) removeContainer(ctx context.Context, c *Container, force bool,
}
}
- c.newContainerEvent(events.Remove)
return cleanupErr
}
diff --git a/pkg/adapter/containers.go b/pkg/adapter/containers.go
index 8ce506542..d4e8e30d9 100644
--- a/pkg/adapter/containers.go
+++ b/pkg/adapter/containers.go
@@ -255,14 +255,14 @@ func (r *LocalRuntime) Log(c *cliconfig.LogsValues, options *libpod.LogOptions)
// CreateContainer creates a libpod container
func (r *LocalRuntime) CreateContainer(ctx context.Context, c *cliconfig.CreateValues) (string, error) {
- results := shared.NewIntermediateLayer(&c.PodmanCommand)
+ results := shared.NewIntermediateLayer(&c.PodmanCommand, false)
ctr, _, err := shared.CreateContainer(ctx, &results, r.Runtime)
return ctr.ID(), err
}
// Run a libpod container
func (r *LocalRuntime) Run(ctx context.Context, c *cliconfig.RunValues, exitCode int) (int, error) {
- results := shared.NewIntermediateLayer(&c.PodmanCommand)
+ results := shared.NewIntermediateLayer(&c.PodmanCommand, false)
ctr, createConfig, err := shared.CreateContainer(ctx, &results, r.Runtime)
if err != nil {
@@ -407,3 +407,39 @@ func (r *LocalRuntime) Ps(c *cliconfig.PsValues, opts shared.PsOptions) ([]share
logrus.Debugf("Setting maximum workers to %d", maxWorkers)
return shared.GetPsContainerOutput(r.Runtime, opts, c.Filter, maxWorkers)
}
+
+// Attach ...
+func (r *LocalRuntime) Attach(ctx context.Context, c *cliconfig.AttachValues) error {
+ var (
+ ctr *libpod.Container
+ err error
+ )
+
+ if c.Latest {
+ ctr, err = r.Runtime.GetLatestContainer()
+ } else {
+ ctr, err = r.Runtime.LookupContainer(c.InputArgs[0])
+ }
+
+ if err != nil {
+ return errors.Wrapf(err, "unable to exec into %s", c.InputArgs[0])
+ }
+
+ conState, err := ctr.State()
+ if err != nil {
+ return errors.Wrapf(err, "unable to determine state of %s", ctr.ID())
+ }
+ if conState != libpod.ContainerStateRunning {
+ return errors.Errorf("you can only attach to running containers")
+ }
+
+ inputStream := os.Stdin
+ if c.NoStdin {
+ inputStream = nil
+ }
+ // If the container is in a pod, also set to recursively start dependencies
+ if err := StartAttachCtr(ctx, ctr, os.Stdout, os.Stderr, inputStream, c.DetachKeys, c.SigProxy, false, ctr.PodID() != ""); err != nil && errors.Cause(err) != libpod.ErrDetach {
+ return errors.Wrapf(err, "error attaching to container %s", ctr.ID())
+ }
+ return nil
+}
diff --git a/pkg/adapter/containers_remote.go b/pkg/adapter/containers_remote.go
index 424c431df..9ca823760 100644
--- a/pkg/adapter/containers_remote.go
+++ b/pkg/adapter/containers_remote.go
@@ -6,19 +6,25 @@ import (
"context"
"encoding/json"
"fmt"
+ "io"
+ "os"
"strconv"
"syscall"
"time"
"github.com/containers/libpod/cmd/podman/cliconfig"
"github.com/containers/libpod/cmd/podman/shared"
- "github.com/pkg/errors"
- "github.com/sirupsen/logrus"
-
iopodman "github.com/containers/libpod/cmd/podman/varlink"
"github.com/containers/libpod/libpod"
"github.com/containers/libpod/pkg/inspect"
+ "github.com/containers/libpod/pkg/varlinkapi/virtwriter"
+ "github.com/docker/docker/pkg/term"
+ "github.com/opencontainers/runtime-spec/specs-go"
+ "github.com/pkg/errors"
+ "github.com/sirupsen/logrus"
"github.com/varlink/go/varlink"
+ "golang.org/x/crypto/ssh/terminal"
+ "k8s.io/client-go/tools/remotecommand"
)
// Inspect returns an inspect struct from varlink
@@ -71,6 +77,19 @@ func (r *LocalRuntime) ContainerState(name string) (*libpod.ContainerState, erro
}
+// Spec obtains the container spec.
+func (r *LocalRuntime) Spec(name string) (*specs.Spec, error) {
+ reply, err := iopodman.Spec().Call(r.Conn, name)
+ if err != nil {
+ return nil, err
+ }
+ data := specs.Spec{}
+ if err := json.Unmarshal([]byte(reply), &data); err != nil {
+ return nil, err
+ }
+ return &data, nil
+}
+
// LookupContainer gets basic information about container over a varlink
// connection and then translates it to a *Container
func (r *LocalRuntime) LookupContainer(idOrName string) (*Container, error) {
@@ -79,10 +98,6 @@ func (r *LocalRuntime) LookupContainer(idOrName string) (*Container, error) {
return nil, err
}
config := r.Config(idOrName)
- if err != nil {
- return nil, err
- }
-
return &Container{
remoteContainer{
r,
@@ -316,24 +331,36 @@ func (r *LocalRuntime) CreateContainer(ctx context.Context, c *cliconfig.CreateV
// TODO need to add attach when that function becomes available
return "", errors.New("the remote client only supports detached containers")
}
- results := shared.NewIntermediateLayer(&c.PodmanCommand)
+ results := shared.NewIntermediateLayer(&c.PodmanCommand, true)
return iopodman.CreateContainer().Call(r.Conn, results.MakeVarlink())
}
// Run creates a container overvarlink and then starts it
func (r *LocalRuntime) Run(ctx context.Context, c *cliconfig.RunValues, exitCode int) (int, error) {
+ // FIXME
+ // podman-remote run -it alpine ls DOES NOT WORK YET
+ // podman-remote run -it alpine /bin/sh does, i suspect there is some sort of
+ // timing issue between the socket availability and terminal setup and the command
+ // being run.
+
// TODO the exit codes for run need to be figured out for remote connections
- if !c.Bool("detach") {
- return 0, errors.New("the remote client only supports detached containers")
- }
- results := shared.NewIntermediateLayer(&c.PodmanCommand)
+ results := shared.NewIntermediateLayer(&c.PodmanCommand, true)
cid, err := iopodman.CreateContainer().Call(r.Conn, results.MakeVarlink())
if err != nil {
return 0, err
}
- fmt.Println(cid)
- _, err = iopodman.StartContainer().Call(r.Conn, cid)
- return 0, err
+ if c.Bool("detach") {
+ _, err := iopodman.StartContainer().Call(r.Conn, cid)
+ fmt.Println(cid)
+ return 0, err
+ }
+
+ errChan, err := r.attach(ctx, os.Stdin, os.Stdout, cid, true)
+ if err != nil {
+ return 0, err
+ }
+ finalError := <-errChan
+ return 0, finalError
}
func ReadExitFile(runtimeTmp, ctrID string) (int, error) {
@@ -411,3 +438,102 @@ func (r *LocalRuntime) Ps(c *cliconfig.PsValues, opts shared.PsOptions) ([]share
}
return psContainers, nil
}
+
+func (r *LocalRuntime) attach(ctx context.Context, stdin, stdout *os.File, cid string, start bool) (chan error, error) {
+ var (
+ oldTermState *term.State
+ )
+ errChan := make(chan error)
+ spec, err := r.Spec(cid)
+ if err != nil {
+ return nil, err
+ }
+ resize := make(chan remotecommand.TerminalSize)
+ haveTerminal := terminal.IsTerminal(int(os.Stdin.Fd()))
+
+ // Check if we are attached to a terminal. If we are, generate resize
+ // events, and set the terminal to raw mode
+ if haveTerminal && spec.Process.Terminal {
+ logrus.Debugf("Handling terminal attach")
+
+ subCtx, cancel := context.WithCancel(ctx)
+ defer cancel()
+
+ resizeTty(subCtx, resize)
+ oldTermState, err = term.SaveState(os.Stdin.Fd())
+ if err != nil {
+ return nil, errors.Wrapf(err, "unable to save terminal state")
+ }
+
+ logrus.SetFormatter(&RawTtyFormatter{})
+ term.SetRawTerminal(os.Stdin.Fd())
+
+ }
+ // TODO add detach keys support
+ _, err = iopodman.Attach().Send(r.Conn, varlink.Upgrade, cid, "", start)
+ if err != nil {
+ restoreTerminal(oldTermState)
+ return nil, err
+ }
+
+ // These are the varlink sockets
+ reader := r.Conn.Reader
+ writer := r.Conn.Writer
+
+ // These are the special writers that encode input from the client.
+ varlinkStdinWriter := virtwriter.NewVirtWriteCloser(writer, virtwriter.ToStdin)
+ varlinkResizeWriter := virtwriter.NewVirtWriteCloser(writer, virtwriter.TerminalResize)
+
+ go func() {
+ // Read from the wire and direct to stdout or stderr
+ err := virtwriter.Reader(reader, stdout, os.Stderr, nil, nil)
+ defer restoreTerminal(oldTermState)
+ errChan <- err
+ }()
+
+ go func() {
+ for termResize := range resize {
+ b, err := json.Marshal(termResize)
+ if err != nil {
+ defer restoreTerminal(oldTermState)
+ errChan <- err
+ }
+ _, err = varlinkResizeWriter.Write(b)
+ if err != nil {
+ defer restoreTerminal(oldTermState)
+ errChan <- err
+ }
+ }
+ }()
+
+ // Takes stdinput and sends it over the wire after being encoded
+ go func() {
+ if _, err := io.Copy(varlinkStdinWriter, stdin); err != nil {
+ defer restoreTerminal(oldTermState)
+ errChan <- err
+ }
+
+ }()
+ return errChan, nil
+
+}
+
+// Attach to a remote terminal
+func (r *LocalRuntime) Attach(ctx context.Context, c *cliconfig.AttachValues) error {
+ ctr, err := r.LookupContainer(c.InputArgs[0])
+ if err != nil {
+ return nil
+ }
+ if ctr.state.State != libpod.ContainerStateRunning {
+ return errors.New("you can only attach to running containers")
+ }
+ inputStream := os.Stdin
+ if c.NoStdin {
+ inputStream = nil
+ }
+ errChan, err := r.attach(ctx, inputStream, os.Stdout, c.InputArgs[0], false)
+ if err != nil {
+ return err
+ }
+ return <-errChan
+}
diff --git a/pkg/spec/spec.go b/pkg/spec/spec.go
index 9b6bd089e..0371b6d4d 100644
--- a/pkg/spec/spec.go
+++ b/pkg/spec/spec.go
@@ -132,6 +132,9 @@ func CreateConfigToOCISpec(config *CreateConfig) (*spec.Spec, error) { //nolint
Options: []string{"rprivate", "nosuid", "noexec", "nodev", r, "rbind"},
}
g.AddMount(sysMnt)
+ if !config.Privileged && isRootless {
+ g.AddLinuxMaskedPaths("/sys/kernel")
+ }
}
if isRootless {
nGids, err := getAvailableGids()
diff --git a/pkg/varlinkapi/attach.go b/pkg/varlinkapi/attach.go
new file mode 100644
index 000000000..9e2a265be
--- /dev/null
+++ b/pkg/varlinkapi/attach.go
@@ -0,0 +1,103 @@
+// +build varlink
+
+package varlinkapi
+
+import (
+ "bufio"
+ "io"
+
+ "github.com/containers/libpod/cmd/podman/varlink"
+ "github.com/containers/libpod/libpod"
+ "github.com/containers/libpod/pkg/varlinkapi/virtwriter"
+ "github.com/sirupsen/logrus"
+ "k8s.io/client-go/tools/remotecommand"
+)
+
+func setupStreams(call iopodman.VarlinkCall) (*bufio.Reader, *bufio.Writer, *io.PipeReader, *io.PipeWriter, *libpod.AttachStreams) {
+
+ // These are the varlink sockets
+ reader := call.Call.Reader
+ writer := call.Call.Writer
+
+ // This pipe is used to pass stdin from the client to the input stream
+ // once the msg has been "decoded"
+ pr, pw := io.Pipe()
+
+ stdoutWriter := virtwriter.NewVirtWriteCloser(writer, virtwriter.ToStdout)
+ // TODO if runc ever starts passing stderr, we can too
+ //stderrWriter := NewVirtWriteCloser(writer, ToStderr)
+
+ streams := libpod.AttachStreams{
+ OutputStream: stdoutWriter,
+ InputStream: pr,
+ // Runc eats the error stream
+ ErrorStream: stdoutWriter,
+ AttachInput: true,
+ AttachOutput: true,
+ // Runc eats the error stream
+ AttachError: true,
+ }
+ return reader, writer, pr, pw, &streams
+}
+
+// Attach connects to a containers console
+func (i *LibpodAPI) Attach(call iopodman.VarlinkCall, name string, detachKeys string, start bool) error {
+ var finalErr error
+ resize := make(chan remotecommand.TerminalSize)
+ errChan := make(chan error)
+
+ if !call.WantsUpgrade() {
+ return call.ReplyErrorOccurred("client must use upgraded connection to attach")
+ }
+ ctr, err := i.Runtime.LookupContainer(name)
+ if err != nil {
+ return call.ReplyErrorOccurred(err.Error())
+ }
+
+ reader, writer, _, pw, streams := setupStreams(call)
+
+ go func() {
+ if err := virtwriter.Reader(reader, nil, nil, pw, resize); err != nil {
+ errChan <- err
+ }
+ }()
+
+ if start {
+ finalErr = startAndAttach(ctr, streams, detachKeys, resize, errChan)
+ } else {
+ finalErr = attach(ctr, streams, detachKeys, resize, errChan)
+ }
+
+ if finalErr != libpod.ErrDetach && finalErr != nil {
+ logrus.Error(finalErr)
+ }
+ quitWriter := virtwriter.NewVirtWriteCloser(writer, virtwriter.Quit)
+ _, err = quitWriter.Write([]byte("HANG-UP"))
+ // TODO error handling is not quite right here yet
+ return call.Writer.Flush()
+}
+
+func attach(ctr *libpod.Container, streams *libpod.AttachStreams, detachKeys string, resize chan remotecommand.TerminalSize, errChan chan error) error {
+ go func() {
+ if err := ctr.Attach(streams, detachKeys, resize); err != nil {
+ errChan <- err
+ }
+ }()
+ attachError := <-errChan
+ return attachError
+}
+
+func startAndAttach(ctr *libpod.Container, streams *libpod.AttachStreams, detachKeys string, resize chan remotecommand.TerminalSize, errChan chan error) error {
+ var finalErr error
+ attachChan, err := ctr.StartAndAttach(getContext(), streams, detachKeys, resize, false)
+ if err != nil {
+ return err
+ }
+ select {
+ case attachChanErr := <-attachChan:
+ finalErr = attachChanErr
+ case chanError := <-errChan:
+ finalErr = chanError
+ }
+ return finalErr
+}
diff --git a/pkg/varlinkapi/containers.go b/pkg/varlinkapi/containers.go
index 816a72953..17792ccfe 100644
--- a/pkg/varlinkapi/containers.go
+++ b/pkg/varlinkapi/containers.go
@@ -634,6 +634,22 @@ func (i *LibpodAPI) GetContainerStatsWithHistory(call iopodman.VarlinkCall, prev
return call.ReplyGetContainerStatsWithHistory(cStats)
}
+// Spec ...
+func (i *LibpodAPI) Spec(call iopodman.VarlinkCall, name string) error {
+ ctr, err := i.Runtime.LookupContainer(name)
+ if err != nil {
+ return call.ReplyErrorOccurred(err.Error())
+ }
+
+ spec := ctr.Spec()
+ b, err := json.Marshal(spec)
+ if err != nil {
+ return call.ReplyErrorOccurred(err.Error())
+ }
+
+ return call.ReplySpec(string(b))
+}
+
// GetContainersLogs is the varlink endpoint to obtain one or more container logs
func (i *LibpodAPI) GetContainersLogs(call iopodman.VarlinkCall, names []string, follow, latest bool, since string, tail int64, timestamps bool) error {
var wg sync.WaitGroup
diff --git a/pkg/varlinkapi/virtwriter/virtwriter.go b/pkg/varlinkapi/virtwriter/virtwriter.go
new file mode 100644
index 000000000..3adaf6e17
--- /dev/null
+++ b/pkg/varlinkapi/virtwriter/virtwriter.go
@@ -0,0 +1,155 @@
+package virtwriter
+
+import (
+ "bufio"
+ "encoding/binary"
+ "encoding/json"
+ "errors"
+ "io"
+ "os"
+
+ "k8s.io/client-go/tools/remotecommand"
+)
+
+// SocketDest is the "key" to where IO should go on the varlink
+// multiplexed socket
+type SocketDest int
+
+const (
+ // ToStdout indicates traffic should go stdout
+ ToStdout SocketDest = iota
+ // ToStdin indicates traffic came from stdin
+ ToStdin SocketDest = iota
+ // ToStderr indicates traffuc should go to stderr
+ ToStderr SocketDest = iota
+ // TerminalResize indicates a terminal resize event has occurred
+ // and data should be passed to resizer
+ TerminalResize SocketDest = iota
+ // Quit and detach
+ Quit SocketDest = iota
+)
+
+// IntToSocketDest returns a socketdest based on integer input
+func IntToSocketDest(i int) SocketDest {
+ switch i {
+ case ToStdout.Int():
+ return ToStdout
+ case ToStderr.Int():
+ return ToStderr
+ case ToStdin.Int():
+ return ToStdin
+ case TerminalResize.Int():
+ return TerminalResize
+ case Quit.Int():
+ return Quit
+ default:
+ return ToStderr
+ }
+}
+
+// Int returns the integer representation of the socket dest
+func (sd SocketDest) Int() int {
+ return int(sd)
+}
+
+// VirtWriteCloser are writers for attach which include the dest
+// of the data
+type VirtWriteCloser struct {
+ writer *bufio.Writer
+ dest SocketDest
+}
+
+// NewVirtWriteCloser is a constructor
+func NewVirtWriteCloser(w *bufio.Writer, dest SocketDest) VirtWriteCloser {
+ return VirtWriteCloser{w, dest}
+}
+
+// Close is a required method for a writecloser
+func (v VirtWriteCloser) Close() error {
+ return nil
+}
+
+// Write prepends a header to the input message. The header is
+// 8bytes. Position one contains the destination. Positions
+// 5,6,7,8 are a big-endian encoded uint32 for len of the message.
+func (v VirtWriteCloser) Write(input []byte) (int, error) {
+ header := []byte{byte(v.dest), 0, 0, 0}
+ // Go makes us define the byte for big endian
+ mlen := make([]byte, 4)
+ binary.BigEndian.PutUint32(mlen, uint32(len(input)))
+ // append the message len to the header
+ msg := append(header, mlen...)
+ // append the message to the header
+ msg = append(msg, input...)
+ _, err := v.writer.Write(msg)
+ if err != nil {
+ return 0, err
+ }
+ err = v.writer.Flush()
+ return len(input), err
+}
+
+// Reader decodes the content that comes over the wire and directs it to the proper destination.
+func Reader(r *bufio.Reader, output, errput *os.File, input *io.PipeWriter, resize chan remotecommand.TerminalSize) error {
+ var saveb []byte
+ var eom int
+ for {
+ readb := make([]byte, 32*1024)
+ n, err := r.Read(readb)
+ // TODO, later may be worth checking in len of the read is 0
+ if err != nil {
+ return err
+ }
+ b := append(saveb, readb[0:n]...)
+ // no sense in reading less than the header len
+ for len(b) > 7 {
+ eom = int(binary.BigEndian.Uint32(b[4:8])) + 8
+ // The message and header are togther
+ if len(b) >= eom {
+ out := append([]byte{}, b[8:eom]...)
+
+ switch IntToSocketDest(int(b[0])) {
+ case ToStdout:
+ n, err := output.Write(out)
+ if err != nil {
+ return err
+ }
+ if n < len(out) {
+ return errors.New("short write error occurred on stdout")
+ }
+ case ToStderr:
+ n, err := errput.Write(out)
+ if err != nil {
+ return err
+ }
+ if n < len(out) {
+ return errors.New("short write error occurred on stderr")
+ }
+ case ToStdin:
+ n, err := input.Write(out)
+ if err != nil {
+ return err
+ }
+ if n < len(out) {
+ return errors.New("short write error occurred on stdin")
+ }
+ case TerminalResize:
+ // Resize events come over in bytes, need to be reserialized
+ resizeEvent := remotecommand.TerminalSize{}
+ if err := json.Unmarshal(out, &resizeEvent); err != nil {
+ return err
+ }
+ resize <- resizeEvent
+ case Quit:
+ return nil
+ }
+ b = b[eom:]
+ } else {
+ // We do not have the header and full message, need to slurp again
+ saveb = b
+ break
+ }
+ }
+ }
+ return nil
+}
diff --git a/test/e2e/commit_test.go b/test/e2e/commit_test.go
index fe4ae64cf..93e1ea7af 100644
--- a/test/e2e/commit_test.go
+++ b/test/e2e/commit_test.go
@@ -131,7 +131,7 @@ var _ = Describe("Podman commit", func() {
Expect(check.ExitCode()).To(Equal(0))
})
- It("podman commit with volume mounts", func() {
+ It("podman commit with volumes mounts and no include-volumes", func() {
s := podmanTest.Podman([]string{"run", "--name", "test1", "-v", "/tmp:/foo", "alpine", "date"})
s.WaitWithDefaultTimeout()
Expect(s.ExitCode()).To(Equal(0))
@@ -145,6 +145,23 @@ var _ = Describe("Podman commit", func() {
Expect(inspect.ExitCode()).To(Equal(0))
image := inspect.InspectImageJSON()
_, ok := image[0].Config.Volumes["/foo"]
+ Expect(ok).To(BeFalse())
+ })
+
+ It("podman commit with volume mounts and --include-volumes", func() {
+ s := podmanTest.Podman([]string{"run", "--name", "test1", "-v", "/tmp:/foo", "alpine", "date"})
+ s.WaitWithDefaultTimeout()
+ Expect(s.ExitCode()).To(Equal(0))
+
+ c := podmanTest.Podman([]string{"commit", "--include-volumes", "test1", "newimage"})
+ c.WaitWithDefaultTimeout()
+ Expect(c.ExitCode()).To(Equal(0))
+
+ inspect := podmanTest.Podman([]string{"inspect", "newimage"})
+ inspect.WaitWithDefaultTimeout()
+ Expect(inspect.ExitCode()).To(Equal(0))
+ image := inspect.InspectImageJSON()
+ _, ok := image[0].Config.Volumes["/foo"]
Expect(ok).To(BeTrue())
r := podmanTest.Podman([]string{"run", "newimage"})
diff --git a/test/e2e/images_test.go b/test/e2e/images_test.go
index a253dff63..48a964db4 100644
--- a/test/e2e/images_test.go
+++ b/test/e2e/images_test.go
@@ -43,6 +43,17 @@ var _ = Describe("Podman images", func() {
Expect(session.LineInOuputStartsWith("docker.io/library/busybox")).To(BeTrue())
})
+ It("podman images with no images prints header", func() {
+ rmi := podmanTest.Podman([]string{"rmi", "-a"})
+ rmi.WaitWithDefaultTimeout()
+ Expect(rmi.ExitCode()).To(Equal(0))
+
+ session := podmanTest.Podman([]string{"images"})
+ session.WaitWithDefaultTimeout()
+ Expect(session.ExitCode()).To(Equal(0))
+ Expect(len(session.OutputToStringArray())).To(Equal(1))
+ })
+
It("podman image List", func() {
session := podmanTest.Podman([]string{"image", "list"})
session.WaitWithDefaultTimeout()
diff --git a/test/e2e/prune_test.go b/test/e2e/prune_test.go
index 869ca3289..682f7ff2b 100644
--- a/test/e2e/prune_test.go
+++ b/test/e2e/prune_test.go
@@ -82,7 +82,7 @@ var _ = Describe("Podman rm", func() {
prune.WaitWithDefaultTimeout()
Expect(prune.ExitCode()).To(Equal(0))
- images := podmanTest.Podman([]string{"images", "-a"})
+ images := podmanTest.Podman([]string{"images", "-aq"})
images.WaitWithDefaultTimeout()
// all images are unused, so they all should be deleted!
Expect(len(images.OutputToStringArray())).To(Equal(0))
@@ -95,7 +95,7 @@ var _ = Describe("Podman rm", func() {
prune.WaitWithDefaultTimeout()
Expect(prune.ExitCode()).To(Equal(0))
- images := podmanTest.Podman([]string{"images", "-a"})
+ images := podmanTest.Podman([]string{"images", "-aq"})
images.WaitWithDefaultTimeout()
// all images are unused, so they all should be deleted!
Expect(len(images.OutputToStringArray())).To(Equal(0))
diff --git a/test/e2e/rmi_test.go b/test/e2e/rmi_test.go
index 78d175637..e034f24cf 100644
--- a/test/e2e/rmi_test.go
+++ b/test/e2e/rmi_test.go
@@ -270,7 +270,7 @@ RUN find $LOCAL
fmt.Println(session.OutputToString())
Expect(session.ExitCode()).To(Equal(0))
- images := podmanTest.Podman([]string{"images", "--all"})
+ images := podmanTest.Podman([]string{"images", "-aq"})
images.WaitWithDefaultTimeout()
Expect(images.ExitCode()).To(Equal(0))
Expect(len(images.OutputToStringArray())).To(Equal(0))
diff --git a/test/system/030-run.bats b/test/system/030-run.bats
index 742b4c9e3..eef05747f 100644
--- a/test/system/030-run.bats
+++ b/test/system/030-run.bats
@@ -33,4 +33,12 @@ echo $rand | 0 | $rand
done < <(parse_table "$tests")
}
+@test "podman run - uidmapping has no /sys/kernel mounts" {
+ run_podman $expected_rc run --uidmapping 0:100:10000 $IMAGE mount | grep /sys/kernel
+ is "$output" "" "podman run $cmd - output"
+
+ run_podman $expected_rc run --net host --uidmapping 0:100:10000 $IMAGE mount | grep /sys/kernel
+ is "$output" "" "podman run $cmd - output"
+}
+
# vim: filetype=sh