summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--cmd/podman/common/createparse.go2
-rw-r--r--cmd/podman/common/specgen.go2
-rw-r--r--cmd/podman/containers/diff.go9
-rw-r--r--cmd/podman/containers/ps.go2
-rw-r--r--cmd/podman/images/list.go74
-rw-r--r--cmd/podman/parse/json.go3
-rw-r--r--cmd/podman/parse/json_test.go25
-rw-r--r--cmd/podman/report/format.go68
-rw-r--r--cmd/podman/report/format_test.go35
-rw-r--r--cmd/podman/root.go137
-rw-r--r--completions/bash/podman2
-rw-r--r--docs/source/markdown/podman.1.md8
-rw-r--r--libpod/container_internal_linux.go40
-rw-r--r--libpod/container_log_linux.go2
-rw-r--r--libpod/kube.go16
-rw-r--r--libpod/pod.go18
-rw-r--r--pkg/api/handlers/compat/images.go21
-rw-r--r--pkg/bindings/containers/containers.go5
-rw-r--r--pkg/bindings/test/containers_test.go6
-rw-r--r--pkg/channel/writer.go13
-rw-r--r--pkg/domain/infra/tunnel/containers.go2
-rw-r--r--pkg/domain/infra/tunnel/helpers.go2
-rw-r--r--pkg/ps/ps.go9
-rw-r--r--pkg/rootless/rootless.go25
-rw-r--r--pkg/specgen/generate/container_create.go16
-rw-r--r--pkg/specgen/generate/oci.go15
-rw-r--r--test/apiv2/10-images.at6
-rw-r--r--test/e2e/generate_kube_test.go25
-rw-r--r--test/e2e/info_test.go10
-rw-r--r--test/e2e/logs_test.go1
-rw-r--r--test/e2e/pod_infra_container_test.go3
-rw-r--r--test/e2e/ps_test.go12
-rw-r--r--test/e2e/run_selinux_test.go111
-rw-r--r--test/e2e/run_test.go24
-rw-r--r--test/e2e/version_test.go11
-rw-r--r--test/system/001-basic.bats2
-rw-r--r--test/system/035-logs.bats11
37 files changed, 608 insertions, 165 deletions
diff --git a/cmd/podman/common/createparse.go b/cmd/podman/common/createparse.go
index 059f9050f..09ee5aa0c 100644
--- a/cmd/podman/common/createparse.go
+++ b/cmd/podman/common/createparse.go
@@ -10,7 +10,7 @@ import (
func (c *ContainerCLIOpts) validate() error {
var ()
if c.Rm && c.Restart != "" && c.Restart != "no" {
- return errors.Errorf("the --rm option conflicts with --restart")
+ return errors.Errorf(`the --rm option conflicts with --restart, when the restartPolicy is not "" and "no"`)
}
if _, err := util.ValidatePullType(c.Pull); err != nil {
diff --git a/cmd/podman/common/specgen.go b/cmd/podman/common/specgen.go
index e7b88eb3f..84ae70b6a 100644
--- a/cmd/podman/common/specgen.go
+++ b/cmd/podman/common/specgen.go
@@ -233,7 +233,7 @@ func FillOutSpecGen(s *specgen.SpecGenerator, c *ContainerCLIOpts, args []string
// validate flags as needed
if err := c.validate(); err != nil {
- return nil
+ return err
}
s.User = c.User
diff --git a/cmd/podman/containers/diff.go b/cmd/podman/containers/diff.go
index 227c13f4c..a3ca6edf9 100644
--- a/cmd/podman/containers/diff.go
+++ b/cmd/podman/containers/diff.go
@@ -1,6 +1,7 @@
package containers
import (
+ "github.com/containers/podman/v2/cmd/podman/parse"
"github.com/containers/podman/v2/cmd/podman/registry"
"github.com/containers/podman/v2/cmd/podman/report"
"github.com/containers/podman/v2/cmd/podman/validate"
@@ -52,11 +53,11 @@ func diff(cmd *cobra.Command, args []string) error {
return err
}
- switch diffOpts.Format {
- case "":
- return report.ChangesToTable(results)
- case "json":
+ switch {
+ case parse.MatchesJSONFormat(diffOpts.Format):
return report.ChangesToJSON(results)
+ case diffOpts.Format == "":
+ return report.ChangesToTable(results)
default:
return errors.New("only supported value for '--format' is 'json'")
}
diff --git a/cmd/podman/containers/ps.go b/cmd/podman/containers/ps.go
index a78b35c08..c4c8b60f3 100644
--- a/cmd/podman/containers/ps.go
+++ b/cmd/podman/containers/ps.go
@@ -240,7 +240,7 @@ func ps(cmd *cobra.Command, args []string) error {
func createPsOut() (string, string) {
var row string
if listOpts.Namespace {
- headers := "CONTAINER ID\tNAMES\tPID\tCGROUPNS\tIPC\tMNT\tNET\tPIDN\tUSERNS\tUTS\n"
+ headers := "CONTAINER ID\tNAMES\tPID\tCGROUPNS\tIPC\tMNT\tNET\tPIDNS\tUSERNS\tUTS\n"
row := "{{.ID}}\t{{.Names}}\t{{.Pid}}\t{{.Namespaces.Cgroup}}\t{{.Namespaces.IPC}}\t{{.Namespaces.MNT}}\t{{.Namespaces.NET}}\t{{.Namespaces.PIDNS}}\t{{.Namespaces.User}}\t{{.Namespaces.UTS}}\n"
return headers, row
}
diff --git a/cmd/podman/images/list.go b/cmd/podman/images/list.go
index ffb341fc4..239da9d28 100644
--- a/cmd/podman/images/list.go
+++ b/cmd/podman/images/list.go
@@ -11,7 +11,9 @@ import (
"unicode"
"github.com/containers/image/v5/docker/reference"
+ "github.com/containers/podman/v2/cmd/podman/parse"
"github.com/containers/podman/v2/cmd/podman/registry"
+ "github.com/containers/podman/v2/cmd/podman/report"
"github.com/containers/podman/v2/pkg/domain/entities"
"github.com/docker/go-units"
"github.com/pkg/errors"
@@ -106,9 +108,12 @@ func images(cmd *cobra.Command, args []string) error {
switch {
case listFlag.quiet:
return writeID(imgs)
- case cmd.Flag("format").Changed && listFlag.format == "json":
+ case parse.MatchesJSONFormat(listFlag.format):
return writeJSON(imgs)
default:
+ if cmd.Flag("format").Changed {
+ listFlag.noHeading = true // V1 compatibility
+ }
return writeTemplate(imgs)
}
}
@@ -156,25 +161,29 @@ func writeJSON(images []imageReporter) error {
}
func writeTemplate(imgs []imageReporter) error {
- var (
- hdr, row string
- )
- if len(listFlag.format) < 1 {
- hdr, row = imageListFormat(listFlag)
+ hdrs := report.Headers(imageReporter{}, map[string]string{
+ "ID": "IMAGE ID",
+ "ReadOnly": "R/O",
+ })
+
+ var row string
+ if listFlag.format == "" {
+ row = lsFormatFromFlags(listFlag)
} else {
- row = listFlag.format
- if !strings.HasSuffix(row, "\n") {
- row += "\n"
- }
+ row = report.NormalizeFormat(listFlag.format)
}
- format := hdr + "{{range . }}" + row + "{{end}}"
- tmpl, err := template.New("list").Parse(format)
- if err != nil {
- return err
- }
- tmpl = template.Must(tmpl, nil)
+
+ format := "{{range . }}" + row + "{{end}}"
+ tmpl := template.Must(template.New("list").Parse(format))
w := tabwriter.NewWriter(os.Stdout, 8, 2, 2, ' ', 0)
defer w.Flush()
+
+ if !listFlag.noHeading {
+ if err := tmpl.Execute(w, hdrs); err != nil {
+ return err
+ }
+ }
+
return tmpl.Execute(w, imgs)
}
@@ -276,40 +285,27 @@ func sortFunc(key string, data []imageReporter) func(i, j int) bool {
}
}
-func imageListFormat(flags listFlagType) (string, string) {
- // Defaults
- hdr := "REPOSITORY\tTAG"
- row := "{{.Repository}}\t{{if .Tag}}{{.Tag}}{{else}}<none>{{end}}"
+func lsFormatFromFlags(flags listFlagType) string {
+ row := []string{
+ "{{if .Repository}}{{.Repository}}{{else}}<none>{{end}}",
+ "{{if .Tag}}{{.Tag}}{{else}}<none>{{end}}",
+ }
if flags.digests {
- hdr += "\tDIGEST"
- row += "\t{{.Digest}}"
+ row = append(row, "{{.Digest}}")
}
- hdr += "\tIMAGE ID"
- row += "\t{{.ID}}"
-
- hdr += "\tCREATED\tSIZE"
- row += "\t{{.Created}}\t{{.Size}}"
+ row = append(row, "{{.ID}}", "{{.Created}}", "{{.Size}}")
if flags.history {
- hdr += "\tHISTORY"
- row += "\t{{if .History}}{{.History}}{{else}}<none>{{end}}"
+ row = append(row, "{{if .History}}{{.History}}{{else}}<none>{{end}}")
}
if flags.readOnly {
- hdr += "\tReadOnly"
- row += "\t{{.ReadOnly}}"
- }
-
- if flags.noHeading {
- hdr = ""
- } else {
- hdr += "\n"
+ row = append(row, "{{.ReadOnly}}")
}
- row += "\n"
- return hdr, row
+ return strings.Join(row, "\t") + "\n"
}
type imageReporter struct {
diff --git a/cmd/podman/parse/json.go b/cmd/podman/parse/json.go
index 95a6633b8..40ac415db 100644
--- a/cmd/podman/parse/json.go
+++ b/cmd/podman/parse/json.go
@@ -2,8 +2,9 @@ package parse
import "regexp"
-var jsonFormatRegex = regexp.MustCompile(`^(\s*json\s*|\s*{{\s*json\s*\.\s*}}\s*)$`)
+var jsonFormatRegex = regexp.MustCompile(`^\s*(json|{{\s*json\s*( \.)?\s*}})\s*$`)
+// MatchesJSONFormat test CLI --format string to be a JSON request
func MatchesJSONFormat(s string) bool {
return jsonFormatRegex.Match([]byte(s))
}
diff --git a/cmd/podman/parse/json_test.go b/cmd/podman/parse/json_test.go
index 5cad185fd..ec3b5664b 100644
--- a/cmd/podman/parse/json_test.go
+++ b/cmd/podman/parse/json_test.go
@@ -1,6 +1,8 @@
package parse
import (
+ "fmt"
+ "strings"
"testing"
"github.com/stretchr/testify/assert"
@@ -13,18 +15,31 @@ func TestMatchesJSONFormat(t *testing.T) {
}{
{"json", true},
{" json", true},
- {"json ", true},
+ {" json ", true},
{" json ", true},
+ {"{{json}}", true},
+ {"{{json }}", true},
{"{{json .}}", true},
{"{{ json .}}", true},
- {"{{json . }}", true},
- {" {{ json . }} ", true},
- {"{{json }}", false},
- {"{{json .", false},
+ {"{{ json . }}", true},
+ {" {{ json . }} ", true},
+ {"{{ json .", false},
{"json . }}", false},
+ {"{{.ID }} json .", false},
+ {"json .", false},
+ {"{{json.}}", false},
}
for _, tt := range tests {
assert.Equal(t, tt.expected, MatchesJSONFormat(tt.input))
}
+
+ for _, tc := range tests {
+ tc := tc
+ label := "MatchesJSONFormat/" + strings.ReplaceAll(tc.input, " ", "_")
+ t.Run(label, func(t *testing.T) {
+ t.Parallel()
+ assert.Equal(t, tc.expected, MatchesJSONFormat(tc.input), fmt.Sprintf("Scanning %q failed", tc.input))
+ })
+ }
}
diff --git a/cmd/podman/report/format.go b/cmd/podman/report/format.go
new file mode 100644
index 000000000..32d92bec5
--- /dev/null
+++ b/cmd/podman/report/format.go
@@ -0,0 +1,68 @@
+package report
+
+import (
+ "reflect"
+ "strings"
+)
+
+// tableReplacer will remove 'table ' prefix and clean up tabs
+var tableReplacer = strings.NewReplacer(
+ "table ", "",
+ `\t`, "\t",
+ `\n`, "\n",
+ " ", "\t",
+)
+
+// escapedReplacer will clean up escaped characters from CLI
+var escapedReplacer = strings.NewReplacer(
+ `\t`, "\t",
+ `\n`, "\n",
+)
+
+// NormalizeFormat reads given go template format provided by CLI and munges it into what we need
+func NormalizeFormat(format string) string {
+ f := format
+ // two replacers used so we only remove the prefix keyword `table`
+ if strings.HasPrefix(f, "table ") {
+ f = tableReplacer.Replace(f)
+ } else {
+ f = escapedReplacer.Replace(format)
+ }
+
+ if !strings.HasSuffix(f, "\n") {
+ f += "\n"
+ }
+
+ return f
+}
+
+// Headers queries the interface for field names
+func Headers(object interface{}, overrides map[string]string) []map[string]string {
+ value := reflect.ValueOf(object)
+ if value.Kind() == reflect.Ptr {
+ value = value.Elem()
+ }
+
+ // Column header will be field name upper-cased.
+ headers := make(map[string]string, value.NumField())
+ for i := 0; i < value.Type().NumField(); i++ {
+ field := value.Type().Field(i)
+ // Recurse to find field names from promoted structs
+ if field.Type.Kind() == reflect.Struct && field.Anonymous {
+ h := Headers(reflect.New(field.Type).Interface(), nil)
+ for k, v := range h[0] {
+ headers[k] = v
+ }
+ continue
+ }
+ headers[field.Name] = strings.ToUpper(field.Name)
+ }
+
+ if len(overrides) > 0 {
+ // Override column header as provided
+ for k, v := range overrides {
+ headers[k] = strings.ToUpper(v)
+ }
+ }
+ return []map[string]string{headers}
+}
diff --git a/cmd/podman/report/format_test.go b/cmd/podman/report/format_test.go
new file mode 100644
index 000000000..7dd62e899
--- /dev/null
+++ b/cmd/podman/report/format_test.go
@@ -0,0 +1,35 @@
+package report
+
+import (
+ "strings"
+ "testing"
+)
+
+func TestNormalizeFormat(t *testing.T) {
+ cases := []struct {
+ format string
+ expected string
+ }{
+ {"table {{.ID}}", "{{.ID}}\n"},
+ {"table {{.ID}} {{.C}}", "{{.ID}}\t{{.C}}\n"},
+ {"{{.ID}}", "{{.ID}}\n"},
+ {"{{.ID}}\n", "{{.ID}}\n"},
+ {"{{.ID}} {{.C}}", "{{.ID}} {{.C}}\n"},
+ {"\t{{.ID}}", "\t{{.ID}}\n"},
+ {`\t` + "{{.ID}}", "\t{{.ID}}\n"},
+ {"table {{.ID}}\t{{.C}}", "{{.ID}}\t{{.C}}\n"},
+ {"{{.ID}} table {{.C}}", "{{.ID}} table {{.C}}\n"},
+ }
+ for _, tc := range cases {
+ tc := tc
+
+ label := strings.ReplaceAll(tc.format, " ", "<sp>")
+ t.Run("NormalizeFormat/"+label, func(t *testing.T) {
+ t.Parallel()
+ actual := NormalizeFormat(tc.format)
+ if actual != tc.expected {
+ t.Errorf("Expected %q, actual %q", tc.expected, actual)
+ }
+ })
+ }
+}
diff --git a/cmd/podman/root.go b/cmd/podman/root.go
index 6424ec12e..1e73f7540 100644
--- a/cmd/podman/root.go
+++ b/cmd/podman/root.go
@@ -154,34 +154,35 @@ func persistentPreRunE(cmd *cobra.Command, args []string) error {
}
}
- if cmd.Flag("cpu-profile").Changed {
- f, err := os.Create(cfg.CPUProfile)
- if err != nil {
- return errors.Wrapf(err, "unable to create cpu profiling file %s",
- cfg.CPUProfile)
- }
- if err := pprof.StartCPUProfile(f); err != nil {
- return err
+ if !registry.IsRemote() {
+ if cmd.Flag("cpu-profile").Changed {
+ f, err := os.Create(cfg.CPUProfile)
+ if err != nil {
+ return errors.Wrapf(err, "unable to create cpu profiling file %s",
+ cfg.CPUProfile)
+ }
+ if err := pprof.StartCPUProfile(f); err != nil {
+ return err
+ }
}
- }
- if cmd.Flag("trace").Changed {
- tracer, closer := tracing.Init("podman")
- opentracing.SetGlobalTracer(tracer)
- cfg.SpanCloser = closer
+ if cmd.Flag("trace").Changed {
+ tracer, closer := tracing.Init("podman")
+ opentracing.SetGlobalTracer(tracer)
+ cfg.SpanCloser = closer
- cfg.Span = tracer.StartSpan("before-context")
- cfg.SpanCtx = opentracing.ContextWithSpan(registry.Context(), cfg.Span)
- opentracing.StartSpanFromContext(cfg.SpanCtx, cmd.Name())
- }
+ cfg.Span = tracer.StartSpan("before-context")
+ cfg.SpanCtx = opentracing.ContextWithSpan(registry.Context(), cfg.Span)
+ opentracing.StartSpanFromContext(cfg.SpanCtx, cmd.Name())
+ }
- if cfg.MaxWorks <= 0 {
- return errors.Errorf("maximum workers must be set to a positive number (got %d)", cfg.MaxWorks)
- }
- if err := parallel.SetMaxThreads(uint(cfg.MaxWorks)); err != nil {
- return err
+ if cfg.MaxWorks <= 0 {
+ return errors.Errorf("maximum workers must be set to a positive number (got %d)", cfg.MaxWorks)
+ }
+ if err := parallel.SetMaxThreads(uint(cfg.MaxWorks)); err != nil {
+ return err
+ }
}
-
// Setup Rootless environment, IFF:
// 1) in ABI mode
// 2) running as non-root
@@ -206,12 +207,14 @@ func persistentPostRunE(cmd *cobra.Command, args []string) error {
}
cfg := registry.PodmanConfig()
- if cmd.Flag("cpu-profile").Changed {
- pprof.StopCPUProfile()
- }
- if cmd.Flag("trace").Changed {
- cfg.Span.Finish()
- cfg.SpanCloser.Close()
+ if !registry.IsRemote() {
+ if cmd.Flag("cpu-profile").Changed {
+ pprof.StopCPUProfile()
+ }
+ if cmd.Flag("trace").Changed {
+ cfg.Span.Finish()
+ cfg.SpanCloser.Close()
+ }
}
registry.ImageEngine().Shutdown(registry.Context())
@@ -249,51 +252,57 @@ func rootFlags(cmd *cobra.Command, opts *entities.PodmanConfig) {
srv, uri, ident := resolveDestination()
lFlags := cmd.Flags()
- lFlags.BoolVarP(&opts.Remote, "remote", "r", false, "Access remote Podman service (default false)")
lFlags.StringVarP(&opts.Engine.ActiveService, "connection", "c", srv, "Connection to use for remote Podman service")
lFlags.StringVar(&opts.URI, "url", uri, "URL to access Podman service (CONTAINER_HOST)")
lFlags.StringVar(&opts.Identity, "identity", ident, "path to SSH identity file, (CONTAINER_SSHKEY)")
+ lFlags.BoolVarP(&opts.Remote, "remote", "r", false, "Access remote Podman service (default false)")
pFlags := cmd.PersistentFlags()
- pFlags.StringVar(&cfg.Engine.CgroupManager, "cgroup-manager", cfg.Engine.CgroupManager, "Cgroup manager to use (\"cgroupfs\"|\"systemd\")")
- pFlags.StringVar(&opts.CPUProfile, "cpu-profile", "", "Path for the cpu profiling results")
- pFlags.StringVar(&opts.ConmonPath, "conmon", "", "Path of the conmon binary")
- pFlags.StringVar(&cfg.Engine.NetworkCmdPath, "network-cmd-path", cfg.Engine.NetworkCmdPath, "Path to the command for configuring the network")
- pFlags.StringVar(&cfg.Network.NetworkConfigDir, "cni-config-dir", cfg.Network.NetworkConfigDir, "Path of the configuration directory for CNI networks")
- pFlags.StringVar(&cfg.Containers.DefaultMountsFile, "default-mounts-file", cfg.Containers.DefaultMountsFile, "Path to default mounts file")
- pFlags.StringVar(&cfg.Engine.EventsLogger, "events-backend", cfg.Engine.EventsLogger, `Events backend to use ("file"|"journald"|"none")`)
- pFlags.StringSliceVar(&cfg.Engine.HooksDir, "hooks-dir", cfg.Engine.HooksDir, "Set the OCI hooks directory path (may be set multiple times)")
- pFlags.IntVar(&opts.MaxWorks, "max-workers", (runtime.NumCPU()*3)+1, "The maximum number of workers for parallel operations")
- pFlags.StringVar(&cfg.Engine.Namespace, "namespace", cfg.Engine.Namespace, "Set the libpod namespace, used to create separate views of the containers and pods on the system")
- pFlags.StringVar(&cfg.Engine.StaticDir, "root", "", "Path to the root directory in which data, including images, is stored")
- pFlags.StringVar(&opts.RegistriesConf, "registries-conf", "", "Path to a registries.conf to use for image processing")
- pFlags.StringVar(&opts.Runroot, "runroot", "", "Path to the 'run directory' where all state information is stored")
- pFlags.StringVar(&opts.RuntimePath, "runtime", "", "Path to the OCI-compatible binary used to run containers, default is /usr/bin/runc")
- // -s is deprecated due to conflict with -s on subcommands
- pFlags.StringVar(&opts.StorageDriver, "storage-driver", "", "Select which storage driver is used to manage storage of images and containers (default is overlay)")
- pFlags.StringArrayVar(&opts.StorageOpts, "storage-opt", []string{}, "Used to pass an option to the storage driver")
-
- pFlags.StringVar(&opts.Engine.TmpDir, "tmpdir", "", "Path to the tmp directory for libpod state content.\n\nNote: use the environment variable 'TMPDIR' to change the temporary storage location for container images, '/var/tmp'.\n")
- pFlags.BoolVar(&opts.Trace, "trace", false, "Enable opentracing output (default false)")
-
+ if registry.IsRemote() {
+ if err := lFlags.MarkHidden("remote"); err != nil {
+ logrus.Warnf("unable to mark --remote flag as hidden: %s", err.Error())
+ }
+ opts.Remote = true
+ } else {
+ pFlags.StringVar(&cfg.Engine.CgroupManager, "cgroup-manager", cfg.Engine.CgroupManager, "Cgroup manager to use (\"cgroupfs\"|\"systemd\")")
+ pFlags.StringVar(&opts.CPUProfile, "cpu-profile", "", "Path for the cpu profiling results")
+ pFlags.StringVar(&opts.ConmonPath, "conmon", "", "Path of the conmon binary")
+ pFlags.StringVar(&cfg.Engine.NetworkCmdPath, "network-cmd-path", cfg.Engine.NetworkCmdPath, "Path to the command for configuring the network")
+ pFlags.StringVar(&cfg.Network.NetworkConfigDir, "cni-config-dir", cfg.Network.NetworkConfigDir, "Path of the configuration directory for CNI networks")
+ pFlags.StringVar(&cfg.Containers.DefaultMountsFile, "default-mounts-file", cfg.Containers.DefaultMountsFile, "Path to default mounts file")
+ pFlags.StringVar(&cfg.Engine.EventsLogger, "events-backend", cfg.Engine.EventsLogger, `Events backend to use ("file"|"journald"|"none")`)
+ pFlags.StringSliceVar(&cfg.Engine.HooksDir, "hooks-dir", cfg.Engine.HooksDir, "Set the OCI hooks directory path (may be set multiple times)")
+ pFlags.IntVar(&opts.MaxWorks, "max-workers", (runtime.NumCPU()*3)+1, "The maximum number of workers for parallel operations")
+ pFlags.StringVar(&cfg.Engine.Namespace, "namespace", cfg.Engine.Namespace, "Set the libpod namespace, used to create separate views of the containers and pods on the system")
+ pFlags.StringVar(&cfg.Engine.StaticDir, "root", "", "Path to the root directory in which data, including images, is stored")
+ pFlags.StringVar(&opts.RegistriesConf, "registries-conf", "", "Path to a registries.conf to use for image processing")
+ pFlags.StringVar(&opts.Runroot, "runroot", "", "Path to the 'run directory' where all state information is stored")
+ pFlags.StringVar(&opts.RuntimePath, "runtime", "", "Path to the OCI-compatible binary used to run containers, default is /usr/bin/runc")
+ // -s is deprecated due to conflict with -s on subcommands
+ pFlags.StringVar(&opts.StorageDriver, "storage-driver", "", "Select which storage driver is used to manage storage of images and containers (default is overlay)")
+ pFlags.StringArrayVar(&opts.StorageOpts, "storage-opt", []string{}, "Used to pass an option to the storage driver")
+
+ pFlags.StringVar(&opts.Engine.TmpDir, "tmpdir", "", "Path to the tmp directory for libpod state content.\n\nNote: use the environment variable 'TMPDIR' to change the temporary storage location for container images, '/var/tmp'.\n")
+ pFlags.BoolVar(&opts.Trace, "trace", false, "Enable opentracing output (default false)")
+
+ // Hide these flags for both ABI and Tunneling
+ for _, f := range []string{
+ "cpu-profile",
+ "default-mounts-file",
+ "max-workers",
+ "registries-conf",
+ "trace",
+ } {
+ if err := pFlags.MarkHidden(f); err != nil {
+ logrus.Warnf("unable to mark %s flag as hidden: %s", f, err.Error())
+ }
+ }
+ }
// Override default --help information of `--help` global flag
var dummyHelp bool
pFlags.BoolVar(&dummyHelp, "help", false, "Help for podman")
pFlags.StringVar(&logLevel, "log-level", logLevel, fmt.Sprintf("Log messages above specified level (%s)", strings.Join(logLevels, ", ")))
- // Hide these flags for both ABI and Tunneling
- for _, f := range []string{
- "cpu-profile",
- "default-mounts-file",
- "max-workers",
- "registries-conf",
- "trace",
- } {
- if err := pFlags.MarkHidden(f); err != nil {
- logrus.Warnf("unable to mark %s flag as hidden: %s", f, err.Error())
- }
- }
-
// Only create these flags for ABI connections
if !registry.IsRemote() {
pFlags.StringArrayVar(&opts.RuntimeFlags, "runtime-flag", []string{}, "add global flags for the container runtime")
diff --git a/completions/bash/podman b/completions/bash/podman
index a83cfc790..e12862126 100644
--- a/completions/bash/podman
+++ b/completions/bash/podman
@@ -407,7 +407,7 @@ __podman_local_interfaces() {
__podman_complete_restart() {
case "$prev" in
--restart)
- COMPREPLY=( $( compgen -W "always no on-failure" -- "$cur") )
+ COMPREPLY=( $( compgen -W "always no on-failure unless-stopped" -- "$cur") )
return
;;
esac
diff --git a/docs/source/markdown/podman.1.md b/docs/source/markdown/podman.1.md
index 555486562..87337fa3c 100644
--- a/docs/source/markdown/podman.1.md
+++ b/docs/source/markdown/podman.1.md
@@ -160,6 +160,14 @@ Print the version
Podman can set up environment variables from env of [engine] table in containers.conf. These variables can be overridden by passing environment variables before the `podman` commands.
+## Remote Access
+
+The Podman command can be used with remote services using the `--remote` flag. Connections can
+be made using local unix domain sockets, ssh or directly to tcp sockets. When specifying the
+podman --remote flag, only the global options `--url`, `--identity`, `--log-level`, `--connection` are used.
+
+Connection information can also be managed using the containers.conf file.
+
## Exit Status
The exit code from `podman` gives information about why the container
diff --git a/libpod/container_internal_linux.go b/libpod/container_internal_linux.go
index eba732d2a..514cdaee1 100644
--- a/libpod/container_internal_linux.go
+++ b/libpod/container_internal_linux.go
@@ -7,6 +7,7 @@ import (
"fmt"
"io"
"io/ioutil"
+ "math"
"net"
"os"
"os/user"
@@ -35,6 +36,7 @@ import (
"github.com/containers/podman/v2/pkg/util"
"github.com/containers/podman/v2/utils"
"github.com/containers/storage/pkg/archive"
+ "github.com/containers/storage/pkg/idtools"
securejoin "github.com/cyphar/filepath-securejoin"
runcuser "github.com/opencontainers/runc/libcontainer/user"
spec "github.com/opencontainers/runtime-spec/specs-go"
@@ -416,9 +418,43 @@ func (c *Container) generateSpec(ctx context.Context) (*spec.Spec, error) {
// Look up and add groups the user belongs to, if a group wasn't directly specified
if !strings.Contains(c.config.User, ":") {
+ // the gidMappings that are present inside the container user namespace
+ var gidMappings []idtools.IDMap
+
+ switch {
+ case len(c.config.IDMappings.GIDMap) > 0:
+ gidMappings = c.config.IDMappings.GIDMap
+ case rootless.IsRootless():
+ // Check whether the current user namespace has enough gids available.
+ availableGids, err := rootless.GetAvailableGids()
+ if err != nil {
+ return nil, errors.Wrapf(err, "cannot read number of available GIDs")
+ }
+ gidMappings = []idtools.IDMap{{
+ ContainerID: 0,
+ HostID: 0,
+ Size: int(availableGids),
+ }}
+ default:
+ gidMappings = []idtools.IDMap{{
+ ContainerID: 0,
+ HostID: 0,
+ Size: math.MaxInt32,
+ }}
+ }
for _, gid := range execUser.Sgids {
- // FIXME: We need to add a flag to containers.conf to not add these for HPC Users.
- g.AddProcessAdditionalGid(uint32(gid))
+ isGidAvailable := false
+ for _, m := range gidMappings {
+ if gid >= m.ContainerID && gid < m.ContainerID+m.Size {
+ isGidAvailable = true
+ break
+ }
+ }
+ if isGidAvailable {
+ g.AddProcessAdditionalGid(uint32(gid))
+ } else {
+ logrus.Warnf("additional gid=%d is not present in the user namespace, skip setting it", gid)
+ }
}
}
diff --git a/libpod/container_log_linux.go b/libpod/container_log_linux.go
index 73c2df76e..d895171cf 100644
--- a/libpod/container_log_linux.go
+++ b/libpod/container_log_linux.go
@@ -33,7 +33,7 @@ const (
func (c *Container) readFromJournal(ctx context.Context, options *logs.LogOptions, logChannel chan *logs.LogLine) error {
var config journal.JournalReaderConfig
if options.Tail < 0 {
- config.NumFromTail = math.MaxUint64
+ config.NumFromTail = 0
} else {
config.NumFromTail = uint64(options.Tail)
}
diff --git a/libpod/kube.go b/libpod/kube.go
index f83e99d82..6df79e394 100644
--- a/libpod/kube.go
+++ b/libpod/kube.go
@@ -303,12 +303,24 @@ func containerToV1Container(c *Container) (v1.Container, []v1.Volume, error) {
// This should not be applicable
//container.EnvFromSource =
kubeContainer.Env = envVariables
- // TODO enable resources when we can support naming conventions
- //container.Resources
kubeContainer.SecurityContext = kubeSec
kubeContainer.StdinOnce = false
kubeContainer.TTY = c.config.Spec.Process.Terminal
+ // TODO add CPU limit support.
+ if c.config.Spec.Linux != nil &&
+ c.config.Spec.Linux.Resources != nil &&
+ c.config.Spec.Linux.Resources.Memory != nil &&
+ c.config.Spec.Linux.Resources.Memory.Limit != nil {
+ if kubeContainer.Resources.Limits == nil {
+ kubeContainer.Resources.Limits = v1.ResourceList{}
+ }
+
+ qty := kubeContainer.Resources.Limits.Memory()
+ qty.Set(*c.config.Spec.Linux.Resources.Memory.Limit)
+ kubeContainer.Resources.Limits[v1.ResourceMemory] = *qty
+ }
+
return kubeContainer, kubeVolumes, nil
}
diff --git a/libpod/pod.go b/libpod/pod.go
index a5a0532be..c8f62ca18 100644
--- a/libpod/pod.go
+++ b/libpod/pod.go
@@ -327,3 +327,21 @@ func (p *Pod) GetPodStats(previousContainerStats map[string]*define.ContainerSta
}
return newContainerStats, nil
}
+
+// ProcessLabel returns the SELinux label associated with the pod
+func (p *Pod) ProcessLabel() (string, error) {
+ if !p.HasInfraContainer() {
+ return "", nil
+ }
+
+ id, err := p.InfraContainerID()
+ if err != nil {
+ return "", err
+ }
+
+ ctr, err := p.runtime.state.Container(id)
+ if err != nil {
+ return "", err
+ }
+ return ctr.ProcessLabel(), nil
+}
diff --git a/pkg/api/handlers/compat/images.go b/pkg/api/handlers/compat/images.go
index 940b57343..cc67ebcd1 100644
--- a/pkg/api/handlers/compat/images.go
+++ b/pkg/api/handlers/compat/images.go
@@ -20,9 +20,25 @@ import (
"github.com/containers/podman/v2/pkg/domain/entities"
"github.com/docker/docker/api/types"
"github.com/gorilla/schema"
+ "github.com/opencontainers/go-digest"
"github.com/pkg/errors"
)
+// mergeNameAndTagOrDigest creates an image reference as string from the
+// provided image name and tagOrDigest which can be a tag, a digest or empty.
+func mergeNameAndTagOrDigest(name, tagOrDigest string) string {
+ if len(tagOrDigest) == 0 {
+ return name
+ }
+
+ separator := ":" // default to tag
+ if _, err := digest.Parse(tagOrDigest); err == nil {
+ // We have a digest, so let's change the separator.
+ separator = "@"
+ }
+ return fmt.Sprintf("%s%s%s", name, separator, tagOrDigest)
+}
+
func ExportImage(w http.ResponseWriter, r *http.Request) {
// 200 ok
// 500 server
@@ -252,10 +268,7 @@ func CreateImageFromImage(w http.ResponseWriter, r *http.Request) {
return
}
- fromImage := query.FromImage
- if len(query.Tag) >= 1 {
- fromImage = fmt.Sprintf("%s:%s", fromImage, query.Tag)
- }
+ fromImage := mergeNameAndTagOrDigest(query.FromImage, query.Tag)
authConf, authfile, key, err := auth.GetCredentials(r)
if err != nil {
diff --git a/pkg/bindings/containers/containers.go b/pkg/bindings/containers/containers.go
index 46e4df1d2..708ad06cb 100644
--- a/pkg/bindings/containers/containers.go
+++ b/pkg/bindings/containers/containers.go
@@ -25,7 +25,7 @@ var (
// the most recent number of containers. The pod and size booleans indicate that pod information and rootfs
// size information should also be included. Finally, the sync bool synchronizes the OCI runtime and
// container state.
-func List(ctx context.Context, filters map[string][]string, all *bool, last *int, size, sync *bool) ([]entities.ListContainer, error) { // nolint:typecheck
+func List(ctx context.Context, filters map[string][]string, all *bool, last *int, namespace, size, sync *bool) ([]entities.ListContainer, error) { // nolint:typecheck
conn, err := bindings.GetClient(ctx)
if err != nil {
return nil, err
@@ -44,6 +44,9 @@ func List(ctx context.Context, filters map[string][]string, all *bool, last *int
if sync != nil {
params.Set("sync", strconv.FormatBool(*sync))
}
+ if namespace != nil {
+ params.Set("namespace", strconv.FormatBool(*namespace))
+ }
if filters != nil {
filterString, err := bindings.FiltersToString(filters)
if err != nil {
diff --git a/pkg/bindings/test/containers_test.go b/pkg/bindings/test/containers_test.go
index bf2ceab2a..408b4769d 100644
--- a/pkg/bindings/test/containers_test.go
+++ b/pkg/bindings/test/containers_test.go
@@ -499,7 +499,7 @@ var _ = Describe("Podman containers ", func() {
Expect(err).To(BeNil())
_, err = bt.RunTopContainer(&name2, bindings.PFalse, nil)
Expect(err).To(BeNil())
- containerLatestList, err := containers.List(bt.conn, nil, nil, &latestContainers, nil, nil)
+ containerLatestList, err := containers.List(bt.conn, nil, nil, &latestContainers, nil, nil, nil)
Expect(err).To(BeNil())
err = containers.Kill(bt.conn, containerLatestList[0].Names[0], "SIGTERM")
Expect(err).To(BeNil())
@@ -744,7 +744,7 @@ var _ = Describe("Podman containers ", func() {
// Validate list container with id filter
filters := make(map[string][]string)
filters["id"] = []string{cid}
- c, err := containers.List(bt.conn, filters, bindings.PTrue, nil, nil, nil)
+ c, err := containers.List(bt.conn, filters, bindings.PTrue, nil, nil, nil, nil)
Expect(err).To(BeNil())
Expect(len(c)).To(Equal(1))
})
@@ -758,7 +758,7 @@ var _ = Describe("Podman containers ", func() {
lastNum := 1
- c, err := containers.List(bt.conn, nil, bindings.PTrue, &lastNum, nil, nil)
+ c, err := containers.List(bt.conn, nil, bindings.PTrue, &lastNum, nil, nil, nil)
Expect(err).To(BeNil())
Expect(len(c)).To(Equal(1))
Expect(c[0].PodName).To(Equal(podName))
diff --git a/pkg/channel/writer.go b/pkg/channel/writer.go
index dbb38e416..28c9d7de5 100644
--- a/pkg/channel/writer.go
+++ b/pkg/channel/writer.go
@@ -1,6 +1,7 @@
package channel
import (
+ "fmt"
"io"
"sync"
@@ -32,16 +33,24 @@ func (w *writeCloser) Chan() <-chan []byte {
}
// Write method for WriteCloser
-func (w *writeCloser) Write(b []byte) (int, error) {
+func (w *writeCloser) Write(b []byte) (bLen int, err error) {
+ // https://github.com/containers/podman/issues/7896
+ // when podman-remote pull image, if it was killed, the server will panic: send on closed channel
+ // so handle it
+ defer func() {
+ if rErr := recover(); rErr != nil {
+ err = fmt.Errorf("%s", rErr)
+ }
+ }()
if w == nil || w.ch == nil {
return 0, errors.New("use channel.NewWriter() to initialize a WriteCloser")
}
w.mux.Lock()
+ defer w.mux.Unlock()
buf := make([]byte, len(b))
copy(buf, b)
w.ch <- buf
- w.mux.Unlock()
return len(b), nil
}
diff --git a/pkg/domain/infra/tunnel/containers.go b/pkg/domain/infra/tunnel/containers.go
index 9b03503c6..194bb4b48 100644
--- a/pkg/domain/infra/tunnel/containers.go
+++ b/pkg/domain/infra/tunnel/containers.go
@@ -548,7 +548,7 @@ func (ic *ContainerEngine) ContainerStart(ctx context.Context, namesOrIds []stri
}
func (ic *ContainerEngine) ContainerList(ctx context.Context, options entities.ContainerListOptions) ([]entities.ListContainer, error) {
- return containers.List(ic.ClientCxt, options.Filters, &options.All, &options.Last, &options.Size, &options.Sync)
+ return containers.List(ic.ClientCxt, options.Filters, &options.All, &options.Last, &options.Namespace, &options.Size, &options.Sync)
}
func (ic *ContainerEngine) ContainerRun(ctx context.Context, opts entities.ContainerRunOptions) (*entities.ContainerRunReport, error) {
diff --git a/pkg/domain/infra/tunnel/helpers.go b/pkg/domain/infra/tunnel/helpers.go
index 7d28f1bd3..63f9546be 100644
--- a/pkg/domain/infra/tunnel/helpers.go
+++ b/pkg/domain/infra/tunnel/helpers.go
@@ -19,7 +19,7 @@ func getContainersByContext(contextWithConnection context.Context, all, ignore b
return nil, errors.New("cannot lookup containers and all")
}
- allContainers, err := containers.List(contextWithConnection, nil, bindings.PTrue, nil, nil, bindings.PTrue)
+ allContainers, err := containers.List(contextWithConnection, nil, bindings.PTrue, nil, nil, nil, bindings.PTrue)
if err != nil {
return nil, err
}
diff --git a/pkg/ps/ps.go b/pkg/ps/ps.go
index 8087507e2..96b2d754f 100644
--- a/pkg/ps/ps.go
+++ b/pkg/ps/ps.go
@@ -134,15 +134,16 @@ func ListContainerBatch(rt *libpod.Runtime, ctr *libpod.Container, opts entities
logrus.Errorf("error getting exited time for %q: %v", c.ID(), err)
}
+ pid, err = c.PID()
+ if err != nil {
+ return errors.Wrapf(err, "unable to obtain container pid")
+ }
+
if !opts.Size && !opts.Namespace {
return nil
}
if opts.Namespace {
- pid, err = c.PID()
- if err != nil {
- return errors.Wrapf(err, "unable to obtain container pid")
- }
ctrPID := strconv.Itoa(pid)
cgroup, _ = getNamespaceInfo(filepath.Join("/proc", ctrPID, "ns", "cgroup"))
ipc, _ = getNamespaceInfo(filepath.Join("/proc", ctrPID, "ns", "ipc"))
diff --git a/pkg/rootless/rootless.go b/pkg/rootless/rootless.go
index d02721ea9..799c793d8 100644
--- a/pkg/rootless/rootless.go
+++ b/pkg/rootless/rootless.go
@@ -2,8 +2,10 @@ package rootless
import (
"os"
+ "sync"
"github.com/containers/storage"
+ "github.com/opencontainers/runc/libcontainer/user"
"github.com/pkg/errors"
)
@@ -46,3 +48,26 @@ func TryJoinPauseProcess(pausePidPath string) (bool, int, error) {
}
return became, ret, err
}
+
+var (
+ availableGids int64
+ availableGidsErr error
+ availableGidsOnce sync.Once
+)
+
+// GetAvailableGids returns how many GIDs are available in the
+// current user namespace.
+func GetAvailableGids() (int64, error) {
+ availableGidsOnce.Do(func() {
+ idMap, err := user.ParseIDMapFile("/proc/self/gid_map")
+ if err != nil {
+ availableGidsErr = err
+ return
+ }
+ availableGids = int64(0)
+ for _, r := range idMap {
+ availableGids += r.Count
+ }
+ })
+ return availableGids, availableGidsErr
+}
diff --git a/pkg/specgen/generate/container_create.go b/pkg/specgen/generate/container_create.go
index 2ac3b376f..147450703 100644
--- a/pkg/specgen/generate/container_create.go
+++ b/pkg/specgen/generate/container_create.go
@@ -11,6 +11,7 @@ import (
"github.com/containers/podman/v2/pkg/specgen"
"github.com/containers/podman/v2/pkg/util"
"github.com/containers/storage"
+ "github.com/opencontainers/selinux/go-selinux/label"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
)
@@ -272,6 +273,21 @@ func createContainerOptions(ctx context.Context, rt *libpod.Runtime, s *specgen.
// Security options
if len(s.SelinuxOpts) > 0 {
options = append(options, libpod.WithSecLabels(s.SelinuxOpts))
+ } else {
+ if pod != nil {
+ // duplicate the security options from the pod
+ processLabel, err := pod.ProcessLabel()
+ if err != nil {
+ return nil, err
+ }
+ if processLabel != "" {
+ selinuxOpts, err := label.DupSecOpt(processLabel)
+ if err != nil {
+ return nil, err
+ }
+ options = append(options, libpod.WithSecLabels(selinuxOpts))
+ }
+ }
}
options = append(options, libpod.WithPrivileged(s.Privileged))
diff --git a/pkg/specgen/generate/oci.go b/pkg/specgen/generate/oci.go
index b57ddf1aa..f02432f5b 100644
--- a/pkg/specgen/generate/oci.go
+++ b/pkg/specgen/generate/oci.go
@@ -10,7 +10,6 @@ import (
"github.com/containers/podman/v2/libpod/image"
"github.com/containers/podman/v2/pkg/rootless"
"github.com/containers/podman/v2/pkg/specgen"
- "github.com/opencontainers/runc/libcontainer/user"
spec "github.com/opencontainers/runtime-spec/specs-go"
"github.com/opencontainers/runtime-tools/generate"
"github.com/pkg/errors"
@@ -200,7 +199,7 @@ func SpecGenToOCI(ctx context.Context, s *specgen.SpecGenerator, rt *libpod.Runt
}
gid5Available := true
if isRootless {
- nGids, err := GetAvailableGids()
+ nGids, err := rootless.GetAvailableGids()
if err != nil {
return nil, err
}
@@ -360,15 +359,3 @@ func SpecGenToOCI(ctx context.Context, s *specgen.SpecGenerator, rt *libpod.Runt
return configSpec, nil
}
-
-func GetAvailableGids() (int64, error) {
- idMap, err := user.ParseIDMapFile("/proc/self/gid_map")
- if err != nil {
- return 0, err
- }
- count := int64(0)
- for _, r := range idMap {
- count += r.Count
- }
- return count, nil
-}
diff --git a/test/apiv2/10-images.at b/test/apiv2/10-images.at
index a204df65c..bdc298ae3 100644
--- a/test/apiv2/10-images.at
+++ b/test/apiv2/10-images.at
@@ -39,7 +39,11 @@ t GET images/$iid/json 200 \
.Id=sha256:$iid \
.RepoTags[0]=$IMAGE
-#t POST images/create fromImage=alpine 201 foo
+t POST "images/create?fromImage=alpine" '' 200
+
+t POST "images/create?fromImage=alpine&tag=latest" '' 200
+
+t POST "images/create?fromImage=docker.io/library/alpine&tag=sha256:acd3ca9941a85e8ed16515bfc5328e4e2f8c128caa72959a58a127b7801ee01f" '' 200
# Display the image history
t GET libpod/images/nonesuch/history 404
diff --git a/test/e2e/generate_kube_test.go b/test/e2e/generate_kube_test.go
index a3a841dc6..05a7f4ddf 100644
--- a/test/e2e/generate_kube_test.go
+++ b/test/e2e/generate_kube_test.go
@@ -235,6 +235,31 @@ var _ = Describe("Podman generate kube", func() {
}
})
+ It("podman generate kube on pod with memory limit", func() {
+ podName := "testMemoryLimit"
+ podSession := podmanTest.Podman([]string{"pod", "create", "--name", podName})
+ podSession.WaitWithDefaultTimeout()
+ Expect(podSession.ExitCode()).To(Equal(0))
+
+ ctr1Name := "ctr1"
+ ctr1Session := podmanTest.Podman([]string{"create", "--name", ctr1Name, "--pod", podName, "--memory", "10Mi", ALPINE, "top"})
+ ctr1Session.WaitWithDefaultTimeout()
+ Expect(ctr1Session.ExitCode()).To(Equal(0))
+
+ kube := podmanTest.Podman([]string{"generate", "kube", podName})
+ kube.WaitWithDefaultTimeout()
+ Expect(kube.ExitCode()).To(Equal(0))
+
+ pod := new(v1.Pod)
+ err := yaml.Unmarshal(kube.Out.Contents(), pod)
+ Expect(err).To(BeNil())
+
+ for _, ctr := range pod.Spec.Containers {
+ memoryLimit, _ := ctr.Resources.Limits.Memory().AsInt64()
+ Expect(memoryLimit).To(Equal(int64(10 * 1024 * 1024)))
+ }
+ })
+
It("podman generate kube on pod with ports", func() {
podName := "test"
podSession := podmanTest.Podman([]string{"pod", "create", "--name", podName, "-p", "4000:4000", "-p", "5000:5000"})
diff --git a/test/e2e/info_test.go b/test/e2e/info_test.go
index bcbfdd80a..49f5f0ce6 100644
--- a/test/e2e/info_test.go
+++ b/test/e2e/info_test.go
@@ -50,15 +50,17 @@ var _ = Describe("Podman Info", func() {
{"{{ json .}}", true, 0},
{"{{json . }}", true, 0},
{" {{ json . }} ", true, 0},
- {"{{json }}", false, 125},
+ {"{{json }}", true, 0},
{"{{json .", false, 125},
- {"json . }}", false, 0}, // Note: this does NOT fail but produces garbage
+ {"json . }}", false, 0}, // without opening {{ template seen as string literal
}
for _, tt := range tests {
session := podmanTest.Podman([]string{"info", "--format", tt.input})
session.WaitWithDefaultTimeout()
- Expect(session).Should(Exit(tt.exitCode))
- Expect(session.IsJSONOutputValid()).To(Equal(tt.success))
+
+ desc := fmt.Sprintf("JSON test(%q)", tt.input)
+ Expect(session).Should(Exit(tt.exitCode), desc)
+ Expect(session.IsJSONOutputValid()).To(Equal(tt.success), desc)
}
})
diff --git a/test/e2e/logs_test.go b/test/e2e/logs_test.go
index 3aa3cf409..67ab71d20 100644
--- a/test/e2e/logs_test.go
+++ b/test/e2e/logs_test.go
@@ -203,6 +203,7 @@ var _ = Describe("Podman logs", func() {
results.WaitWithDefaultTimeout()
Expect(results).To(Exit(0))
Expect(len(results.OutputToStringArray())).To(Equal(3))
+ Expect(results.OutputToString()).To(Equal("podman podman podman"))
})
It("using journald tail two lines", func() {
diff --git a/test/e2e/pod_infra_container_test.go b/test/e2e/pod_infra_container_test.go
index 515391f92..063c71b9f 100644
--- a/test/e2e/pod_infra_container_test.go
+++ b/test/e2e/pod_infra_container_test.go
@@ -225,7 +225,6 @@ var _ = Describe("Podman pod create", func() {
})
It("podman pod container can override pod pid NS", func() {
- SkipIfRemote("FIXME This should work on podman-remote")
session := podmanTest.Podman([]string{"pod", "create", "--share", "pid"})
session.WaitWithDefaultTimeout()
Expect(session.ExitCode()).To(Equal(0))
@@ -257,7 +256,6 @@ var _ = Describe("Podman pod create", func() {
})
It("podman pod container can override pod not sharing pid", func() {
- SkipIfRemote("FIXME This should work on podman-remote")
session := podmanTest.Podman([]string{"pod", "create", "--share", "net"})
session.WaitWithDefaultTimeout()
Expect(session.ExitCode()).To(Equal(0))
@@ -283,7 +281,6 @@ var _ = Describe("Podman pod create", func() {
})
It("podman pod container can override pod ipc NS", func() {
- SkipIfRemote("FIXME This should work on podman-remote")
session := podmanTest.Podman([]string{"pod", "create", "--share", "ipc"})
session.WaitWithDefaultTimeout()
Expect(session.ExitCode()).To(Equal(0))
diff --git a/test/e2e/ps_test.go b/test/e2e/ps_test.go
index 82a842146..0f2ce2d46 100644
--- a/test/e2e/ps_test.go
+++ b/test/e2e/ps_test.go
@@ -173,6 +173,18 @@ var _ = Describe("Podman ps", func() {
Expect(len(result.OutputToStringArray())).Should(BeNumerically(">", 0))
})
+ It("podman ps namespace flag even for remote", func() {
+ session := podmanTest.RunTopContainer("test1")
+ session.WaitWithDefaultTimeout()
+
+ result := podmanTest.Podman([]string{"ps", "-a", "--namespace", "--format",
+ "{{with .Namespaces}}{{.Cgroup}}:{{.IPC}}:{{.MNT}}:{{.NET}}:{{.PIDNS}}:{{.User}}:{{.UTS}}{{end}}"})
+ result.WaitWithDefaultTimeout()
+ Expect(result.ExitCode()).To(Equal(0))
+ // it must contains `::` when some ns is null. If it works normally, it should be "$num1:$num2:$num3"
+ Expect(result.OutputToString()).To(Not(ContainSubstring(`::`)))
+ })
+
It("podman ps with no containers is valid json format", func() {
result := podmanTest.Podman([]string{"ps", "--format", "json"})
result.WaitWithDefaultTimeout()
diff --git a/test/e2e/run_selinux_test.go b/test/e2e/run_selinux_test.go
index 219750bcb..3294f6d3b 100644
--- a/test/e2e/run_selinux_test.go
+++ b/test/e2e/run_selinux_test.go
@@ -182,4 +182,115 @@ var _ = Describe("Podman run", func() {
match2, _ := session.GrepString("s0:c1,c2")
Expect(match2).To(BeTrue())
})
+
+ It("podman pod container share SELinux labels", func() {
+ session := podmanTest.Podman([]string{"pod", "create"})
+ session.WaitWithDefaultTimeout()
+ Expect(session.ExitCode()).To(Equal(0))
+ podID := session.OutputToString()
+
+ session = podmanTest.Podman([]string{"run", "--pod", podID, ALPINE, "cat", "/proc/self/attr/current"})
+ session.WaitWithDefaultTimeout()
+ Expect(session.ExitCode()).To(Equal(0))
+ label1 := session.OutputToString()
+
+ session = podmanTest.Podman([]string{"run", "--pod", podID, ALPINE, "cat", "/proc/self/attr/current"})
+ session.WaitWithDefaultTimeout()
+ Expect(session.ExitCode()).To(Equal(0))
+ Expect(session.OutputToString()).To(Equal(label1))
+
+ session = podmanTest.Podman([]string{"pod", "rm", podID, "--force"})
+ session.WaitWithDefaultTimeout()
+ Expect(session.ExitCode()).To(Equal(0))
+ })
+
+ It("podman pod container --infra=false doesn't share SELinux labels", func() {
+ session := podmanTest.Podman([]string{"pod", "create", "--infra=false"})
+ session.WaitWithDefaultTimeout()
+ Expect(session.ExitCode()).To(Equal(0))
+ podID := session.OutputToString()
+
+ session = podmanTest.Podman([]string{"run", "--pod", podID, ALPINE, "cat", "/proc/self/attr/current"})
+ session.WaitWithDefaultTimeout()
+ Expect(session.ExitCode()).To(Equal(0))
+ label1 := session.OutputToString()
+
+ session = podmanTest.Podman([]string{"run", "--pod", podID, ALPINE, "cat", "/proc/self/attr/current"})
+ session.WaitWithDefaultTimeout()
+ Expect(session.ExitCode()).To(Equal(0))
+ Expect(session.OutputToString()).To(Not(Equal(label1)))
+
+ session = podmanTest.Podman([]string{"pod", "rm", podID, "--force"})
+ session.WaitWithDefaultTimeout()
+ Expect(session.ExitCode()).To(Equal(0))
+ })
+
+ It("podman shared IPC NS container share SELinux labels", func() {
+ session := podmanTest.RunTopContainer("test1")
+ session.WaitWithDefaultTimeout()
+ Expect(session.ExitCode()).To(Equal(0))
+
+ session = podmanTest.Podman([]string{"exec", "test1", "cat", "/proc/self/attr/current"})
+ session.WaitWithDefaultTimeout()
+ Expect(session.ExitCode()).To(Equal(0))
+ label1 := session.OutputToString()
+
+ session = podmanTest.Podman([]string{"run", "--ipc", "container:test1", ALPINE, "cat", "/proc/self/attr/current"})
+ session.WaitWithDefaultTimeout()
+ Expect(session.ExitCode()).To(Equal(0))
+ Expect(session.OutputToString()).To(Equal(label1))
+ })
+
+ It("podman shared PID NS container share SELinux labels", func() {
+ session := podmanTest.RunTopContainer("test1")
+ session.WaitWithDefaultTimeout()
+ Expect(session.ExitCode()).To(Equal(0))
+
+ session = podmanTest.Podman([]string{"exec", "test1", "cat", "/proc/self/attr/current"})
+ session.WaitWithDefaultTimeout()
+ Expect(session.ExitCode()).To(Equal(0))
+ label1 := session.OutputToString()
+
+ session = podmanTest.Podman([]string{"run", "--pid", "container:test1", ALPINE, "cat", "/proc/self/attr/current"})
+ session.WaitWithDefaultTimeout()
+ Expect(session.ExitCode()).To(Equal(0))
+ Expect(session.OutputToString()).To(Equal(label1))
+ })
+
+ It("podman shared NET NS container doesn't share SELinux labels", func() {
+ session := podmanTest.RunTopContainer("test1")
+ session.WaitWithDefaultTimeout()
+ Expect(session.ExitCode()).To(Equal(0))
+
+ session = podmanTest.Podman([]string{"exec", "test1", "cat", "/proc/self/attr/current"})
+ session.WaitWithDefaultTimeout()
+ Expect(session.ExitCode()).To(Equal(0))
+ label1 := session.OutputToString()
+
+ session = podmanTest.Podman([]string{"run", "--net", "container:test1", ALPINE, "cat", "/proc/self/attr/current"})
+ session.WaitWithDefaultTimeout()
+ Expect(session.ExitCode()).To(Equal(0))
+ Expect(session.OutputToString()).To(Not(Equal(label1)))
+ })
+
+ It("podman test --pid=host", func() {
+ session := podmanTest.Podman([]string{"run", "--pid=host", ALPINE, "cat", "/proc/self/attr/current"})
+ session.WaitWithDefaultTimeout()
+ Expect(session.ExitCode()).To(Equal(0))
+ Expect(session.OutputToString()).To(ContainSubstring("spc_t"))
+ })
+
+ It("podman test --ipc=host", func() {
+ session := podmanTest.Podman([]string{"run", "--ipc=host", ALPINE, "cat", "/proc/self/attr/current"})
+ session.WaitWithDefaultTimeout()
+ Expect(session.ExitCode()).To(Equal(0))
+ Expect(session.OutputToString()).To(ContainSubstring("spc_t"))
+ })
+
+ It("podman test --ipc=net", func() {
+ session := podmanTest.Podman([]string{"run", "--net=host", ALPINE, "cat", "/proc/self/attr/current"})
+ session.WaitWithDefaultTimeout()
+ Expect(session.ExitCode()).To(Equal(0))
+ Expect(session.OutputToString()).To(ContainSubstring("container_t"))
+ })
})
diff --git a/test/e2e/run_test.go b/test/e2e/run_test.go
index 292df529c..05aede122 100644
--- a/test/e2e/run_test.go
+++ b/test/e2e/run_test.go
@@ -67,6 +67,30 @@ var _ = Describe("Podman run", func() {
Expect(session.ExitCode()).To(Equal(0))
})
+ It("podman run --rm with --restart", func() {
+ session := podmanTest.Podman([]string{"run", "--rm", "--restart", "", ALPINE})
+ session.WaitWithDefaultTimeout()
+ Expect(session.ExitCode()).To(Equal(0))
+
+ session = podmanTest.Podman([]string{"run", "--rm", "--restart", "no", ALPINE})
+ session.WaitWithDefaultTimeout()
+ Expect(session.ExitCode()).To(Equal(0))
+
+ // the --rm option conflicts with --restart, when the restartPolicy is not "" and "no"
+ // so the exitCode should not equal 0
+ session = podmanTest.Podman([]string{"run", "--rm", "--restart", "on-failure", ALPINE})
+ session.WaitWithDefaultTimeout()
+ Expect(session.ExitCode()).To(Not(Equal(0)))
+
+ session = podmanTest.Podman([]string{"run", "--rm", "--restart", "always", ALPINE})
+ session.WaitWithDefaultTimeout()
+ Expect(session.ExitCode()).To(Not(Equal(0)))
+
+ session = podmanTest.Podman([]string{"run", "--rm", "--restart", "unless-stopped", ALPINE})
+ session.WaitWithDefaultTimeout()
+ Expect(session.ExitCode()).To(Not(Equal(0)))
+ })
+
It("podman run a container based on on a short name with localhost", func() {
tag := podmanTest.Podman([]string{"tag", nginx, "localhost/libpod/alpine_nginx:latest"})
tag.WaitWithDefaultTimeout()
diff --git a/test/e2e/version_test.go b/test/e2e/version_test.go
index 695cccc11..903748b58 100644
--- a/test/e2e/version_test.go
+++ b/test/e2e/version_test.go
@@ -1,6 +1,7 @@
package integration
import (
+ "fmt"
"os"
. "github.com/containers/podman/v2/test/utils"
@@ -68,15 +69,17 @@ var _ = Describe("Podman version", func() {
{"{{ json .}}", true, 0},
{"{{json . }}", true, 0},
{" {{ json . }} ", true, 0},
- {"{{json }}", false, 125},
+ {"{{json }}", true, 0},
{"{{json .", false, 125},
- {"json . }}", false, 0}, // Note: this does NOT fail but produces garbage
+ {"json . }}", false, 0}, // without opening {{ template seen as string literal
}
for _, tt := range tests {
session := podmanTest.Podman([]string{"version", "--format", tt.input})
session.WaitWithDefaultTimeout()
- Expect(session).Should(Exit(tt.exitCode))
- Expect(session.IsJSONOutputValid()).To(Equal(tt.success))
+
+ desc := fmt.Sprintf("JSON test(%q)", tt.input)
+ Expect(session).Should(Exit(tt.exitCode), desc)
+ Expect(session.IsJSONOutputValid()).To(Equal(tt.success), desc)
}
})
diff --git a/test/system/001-basic.bats b/test/system/001-basic.bats
index 1d5eb066b..d276cfda1 100644
--- a/test/system/001-basic.bats
+++ b/test/system/001-basic.bats
@@ -61,7 +61,7 @@ function setup() {
is "$output" ".*Server:" "podman --remote: contacts server"
# This was failing: "podman --foo --bar --remote".
- PODMAN="${podman_non_remote} --tmpdir /var/tmp --log-level=error ${podman_args[@]} --remote" run_podman version
+ PODMAN="${podman_non_remote} --log-level=error ${podman_args[@]} --remote" run_podman version
is "$output" ".*Server:" "podman [flags] --remote: contacts server"
# ...but no matter what, --remote is never allowed after subcommand
diff --git a/test/system/035-logs.bats b/test/system/035-logs.bats
index cbb2091e5..130bc5243 100644
--- a/test/system/035-logs.bats
+++ b/test/system/035-logs.bats
@@ -50,4 +50,15 @@ ${cid[1]} c
${cid[0]} d" "Sequential output from logs"
}
+@test "podman logs over journald" {
+ msg=$(random_string 20)
+
+ run_podman run --name myctr --log-driver journald $IMAGE echo $msg
+
+ run_podman logs myctr
+ is "$output" "$msg" "check that log output equals the container output"
+
+ run_podman rm myctr
+}
+
# vim: filetype=sh