summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--cmd/podman/generate/systemd.go7
-rw-r--r--cmd/podman/networks/inspect.go38
-rw-r--r--cmd/podman/pods/ps.go108
-rw-r--r--cmd/podman/pods/stats.go94
-rw-r--r--cmd/podman/root.go19
-rw-r--r--cmd/podman/root_test.go34
-rw-r--r--docs/source/Introduction.rst4
-rw-r--r--pkg/api/handlers/compat/containers.go3
-rw-r--r--pkg/api/handlers/compat/events.go3
-rw-r--r--pkg/spec/config_linux_cgo.go2
-rw-r--r--test/apiv2/12-imagesMore.at44
-rw-r--r--test/e2e/pod_ps_test.go3
-rw-r--r--test/e2e/pod_stats_test.go5
-rw-r--r--test/system/010-images.bats52
-rw-r--r--test/system/060-mount.bats13
-rw-r--r--test/system/130-kill.bats20
-rw-r--r--test/system/410-selinux.bats108
-rw-r--r--test/system/helpers.bash11
18 files changed, 392 insertions, 176 deletions
diff --git a/cmd/podman/generate/systemd.go b/cmd/podman/generate/systemd.go
index f690836a4..02e826549 100644
--- a/cmd/podman/generate/systemd.go
+++ b/cmd/podman/generate/systemd.go
@@ -6,6 +6,7 @@ import (
"os"
"path/filepath"
+ "github.com/containers/podman/v2/cmd/podman/parse"
"github.com/containers/podman/v2/cmd/podman/registry"
"github.com/containers/podman/v2/cmd/podman/utils"
"github.com/containers/podman/v2/pkg/domain/entities"
@@ -97,10 +98,10 @@ func systemd(cmd *cobra.Command, args []string) error {
}
}
- switch format {
- case "json":
+ switch {
+ case parse.MatchesJSONFormat(format):
return printJSON(report.Units)
- case "":
+ case format == "":
return printDefault(report.Units)
default:
return errors.Errorf("unknown --format argument: %s", format)
diff --git a/cmd/podman/networks/inspect.go b/cmd/podman/networks/inspect.go
index c5872def7..c36125948 100644
--- a/cmd/podman/networks/inspect.go
+++ b/cmd/podman/networks/inspect.go
@@ -3,12 +3,13 @@ package network
import (
"encoding/json"
"fmt"
- "io"
"os"
- "strings"
+ "text/tabwriter"
"text/template"
+ "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/spf13/cobra"
)
@@ -39,31 +40,32 @@ func init() {
flags.StringVarP(&networkInspectOptions.Format, "format", "f", "", "Pretty-print network to JSON or using a Go template")
}
-func networkInspect(cmd *cobra.Command, args []string) error {
+func networkInspect(_ *cobra.Command, args []string) error {
responses, err := registry.ContainerEngine().NetworkInspect(registry.Context(), args, entities.NetworkInspectOptions{})
if err != nil {
return err
}
- b, err := json.MarshalIndent(responses, "", " ")
- if err != nil {
- return err
- }
- if strings.ToLower(networkInspectOptions.Format) == "json" || networkInspectOptions.Format == "" {
- fmt.Println(string(b))
- } else {
- var w io.Writer = os.Stdout
- //There can be more than 1 in the inspect output.
- format := "{{range . }}" + networkInspectOptions.Format + "{{end}}"
- tmpl, err := template.New("inspectNetworks").Parse(format)
+
+ switch {
+ case parse.MatchesJSONFormat(networkInspectOptions.Format) || networkInspectOptions.Format == "":
+ b, err := json.MarshalIndent(responses, "", " ")
if err != nil {
return err
}
- if err := tmpl.Execute(w, responses); err != nil {
+ fmt.Println(string(b))
+ default:
+ row := report.NormalizeFormat(networkInspectOptions.Format)
+ // There can be more than 1 in the inspect output.
+ row = "{{range . }}" + row + "{{end}}"
+ tmpl, err := template.New("inspectNetworks").Parse(row)
+ if err != nil {
return err
}
- if flusher, ok := w.(interface{ Flush() error }); ok {
- return flusher.Flush()
- }
+
+ w := tabwriter.NewWriter(os.Stdout, 8, 2, 0, ' ', 0)
+ defer w.Flush()
+
+ return tmpl.Execute(w, responses)
}
return nil
}
diff --git a/cmd/podman/pods/ps.go b/cmd/podman/pods/ps.go
index 7b755cb22..b7952e6e3 100644
--- a/cmd/podman/pods/ps.go
+++ b/cmd/podman/pods/ps.go
@@ -3,7 +3,6 @@ package pods
import (
"context"
"fmt"
- "io"
"os"
"sort"
"strings"
@@ -11,7 +10,9 @@ import (
"text/template"
"time"
+ "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"
"github.com/containers/podman/v2/pkg/domain/entities"
"github.com/docker/go-units"
@@ -34,10 +35,9 @@ var (
)
var (
- defaultHeaders = "POD ID\tNAME\tSTATUS\tCREATED"
- inputFilters []string
- noTrunc bool
- psInput entities.PodPSOptions
+ inputFilters []string
+ noTrunc bool
+ psInput entities.PodPSOptions
)
func init() {
@@ -62,11 +62,6 @@ func init() {
}
func pods(cmd *cobra.Command, _ []string) error {
- var (
- w io.Writer = os.Stdout
- row string
- )
-
if psInput.Quiet && len(psInput.Format) > 0 {
return errors.New("quiet and format cannot be used together")
}
@@ -89,80 +84,79 @@ func pods(cmd *cobra.Command, _ []string) error {
return err
}
- if psInput.Format == "json" {
+ switch {
+ case parse.MatchesJSONFormat(psInput.Format):
b, err := json.MarshalIndent(responses, "", " ")
if err != nil {
return err
}
fmt.Println(string(b))
return nil
+ case psInput.Quiet:
+ for _, p := range responses {
+ fmt.Println(p.Id)
+ }
+ return nil
}
+ // Formatted output below
lpr := make([]ListPodReporter, 0, len(responses))
for _, r := range responses {
lpr = append(lpr, ListPodReporter{r})
}
- headers, row := createPodPsOut()
- if psInput.Quiet {
- row = "{{.Id}}\n"
- }
- if cmd.Flag("format").Changed {
- row = psInput.Format
- if !strings.HasPrefix(row, "\n") {
- row += "\n"
- }
- }
- format := "{{range . }}" + row + "{{end}}"
- if !psInput.Quiet && !cmd.Flag("format").Changed {
- format = headers + format
+
+ headers := report.Headers(ListPodReporter{}, map[string]string{
+ "ContainerIds": "IDS",
+ "ContainerNames": "NAMES",
+ "ContainerStatuses": "STATUS",
+ "Namespace": "NAMESPACES",
+ "NumberOfContainers": "# OF CONTAINERS",
+ "InfraId": "INFRA ID",
+ })
+ row := podPsFormat()
+ if cmd.Flags().Changed("format") {
+ row = report.NormalizeFormat(psInput.Format)
}
- tmpl, err := template.New("listPods").Parse(format)
+ row = "{{range . }}" + row + "{{end}}"
+
+ tmpl, err := template.New("listPods").Parse(row)
if err != nil {
return err
}
- if !psInput.Quiet {
- w = tabwriter.NewWriter(os.Stdout, 8, 2, 2, ' ', 0)
- }
- if err := tmpl.Execute(w, lpr); err != nil {
- return err
- }
- if flusher, ok := w.(interface{ Flush() error }); ok {
- return flusher.Flush()
+ w := tabwriter.NewWriter(os.Stdout, 8, 2, 2, ' ', 0)
+ defer w.Flush()
+
+ if !psInput.Quiet && !cmd.Flag("format").Changed {
+ if err := tmpl.Execute(w, headers); err != nil {
+ return err
+ }
}
- return nil
+ return tmpl.Execute(w, lpr)
}
-func createPodPsOut() (string, string) {
- var row string
- headers := defaultHeaders
- row += "{{.Id}}"
-
- row += "\t{{.Name}}\t{{.Status}}\t{{.Created}}"
+func podPsFormat() string {
+ row := []string{"{{.Id}}", "{{.Name}}", "{{.Status}}", "{{.Created}}}"}
if psInput.CtrIds {
- headers += "\tIDS"
- row += "\t{{.ContainerIds}}"
+ row = append(row, "{{.ContainerIds}}")
}
+
if psInput.CtrNames {
- headers += "\tNAMES"
- row += "\t{{.ContainerNames}}"
+ row = append(row, "{{.ContainerNames}}")
}
+
if psInput.CtrStatus {
- headers += "\tSTATUS"
- row += "\t{{.ContainerStatuses}}"
+ row = append(row, "{{.ContainerStatuses}}")
}
+
if psInput.Namespace {
- headers += "\tCGROUP\tNAMESPACES"
- row += "\t{{.Cgroup}}\t{{.Namespace}}"
+ row = append(row, "{{.Cgroup}}", "{{.Namespace}}")
}
- if !psInput.CtrStatus && !psInput.CtrNames && !psInput.CtrIds {
- headers += "\t# OF CONTAINERS"
- row += "\t{{.NumberOfContainers}}"
+ if !psInput.CtrStatus && !psInput.CtrNames && !psInput.CtrIds {
+ row = append(row, "{{.NumberOfContainers}}")
}
- headers += "\tINFRA ID\n"
- row += "\t{{.InfraId}}\n"
- return headers, row
+ return strings.Join(row, "\t") + "\n"
}
// ListPodReporter is a struct for pod ps output
@@ -180,7 +174,7 @@ func (l ListPodReporter) Labels() map[string]string {
return l.ListPodsReport.Labels
}
-// NumberofContainers returns an int representation for
+// NumberOfContainers returns an int representation for
// the number of containers belonging to the pod
func (l ListPodReporter) NumberOfContainers() int {
return len(l.Containers)
@@ -192,7 +186,7 @@ func (l ListPodReporter) ID() string {
}
// Id returns the Pod id
-func (l ListPodReporter) Id() string { //nolint
+func (l ListPodReporter) Id() string { // nolint
if noTrunc {
return l.ListPodsReport.Id
}
@@ -206,7 +200,7 @@ func (l ListPodReporter) InfraID() string {
// InfraId returns the infra container id for the pod
// depending on trunc
-func (l ListPodReporter) InfraId() string { //nolint
+func (l ListPodReporter) InfraId() string { // nolint
if len(l.ListPodsReport.InfraId) == 0 {
return ""
}
diff --git a/cmd/podman/pods/stats.go b/cmd/podman/pods/stats.go
index 1d916dbfa..2f59e4e47 100644
--- a/cmd/podman/pods/stats.go
+++ b/cmd/podman/pods/stats.go
@@ -4,18 +4,16 @@ import (
"context"
"fmt"
"os"
- "reflect"
- "strings"
"text/tabwriter"
"text/template"
"time"
"github.com/buger/goterm"
- "github.com/containers/buildah/pkg/formats"
+ "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"
"github.com/containers/podman/v2/pkg/domain/entities"
- "github.com/containers/podman/v2/pkg/util/camelcase"
"github.com/spf13/cobra"
)
@@ -67,11 +65,18 @@ func stats(cmd *cobra.Command, args []string) error {
return err
}
- format := statsOptions.Format
- doJSON := strings.ToLower(format) == formats.JSONString
- header := getPodStatsHeader(format)
+ row := report.NormalizeFormat(statsOptions.Format)
+ doJSON := parse.MatchesJSONFormat(row)
- for {
+ headers := report.Headers(entities.PodStatsReport{}, map[string]string{
+ "CPU": "CPU %",
+ "MemUsage": "MEM USAGE/ LIMIT",
+ "MEM": "MEM %",
+ "NET IO": "NET IO",
+ "BlockIO": "BLOCK IO",
+ })
+
+ for ; ; time.Sleep(time.Second) {
reports, err := registry.ContainerEngine().PodStats(context.Background(), args, statsOptions.PodStatsOptions)
if err != nil {
return err
@@ -87,16 +92,17 @@ func stats(cmd *cobra.Command, args []string) error {
goterm.MoveCursor(1, 1)
goterm.Flush()
}
- if len(format) == 0 {
+ if cmd.Flags().Changed("format") {
+ if err := printFormattedPodStatsLines(headers, row, reports); err != nil {
+ return err
+ }
+ } else {
printPodStatsLines(reports)
- } else if err := printFormattedPodStatsLines(format, reports, header); err != nil {
- return err
}
}
if statsOptions.NoStream {
break
}
- time.Sleep(time.Second)
}
return nil
@@ -115,72 +121,32 @@ func printPodStatsLines(stats []*entities.PodStatsReport) {
w := tabwriter.NewWriter(os.Stdout, 0, 0, 2, ' ', 0)
outFormat := "%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\n"
fmt.Fprintf(w, outFormat, "POD", "CID", "NAME", "CPU %", "MEM USAGE/ LIMIT", "MEM %", "NET IO", "BLOCK IO", "PIDS")
- for _, i := range stats {
- if len(stats) == 0 {
- fmt.Fprintf(w, outFormat, i.Pod, "--", "--", "--", "--", "--", "--", "--", "--")
- } else {
+ if len(stats) == 0 {
+ fmt.Fprintf(w, outFormat, "--", "--", "--", "--", "--", "--", "--", "--", "--")
+ } else {
+ for _, i := range stats {
fmt.Fprintf(w, outFormat, i.Pod, i.CID, i.Name, i.CPU, i.MemUsage, i.Mem, i.NetIO, i.BlockIO, i.PIDS)
}
}
w.Flush()
}
-func printFormattedPodStatsLines(format string, stats []*entities.PodStatsReport, headerNames map[string]string) error {
+func printFormattedPodStatsLines(headerNames []map[string]string, row string, stats []*entities.PodStatsReport) error {
if len(stats) == 0 {
return nil
}
- // Use a tabwriter to align column format
- w := tabwriter.NewWriter(os.Stdout, 0, 0, 3, ' ', 0)
- // Spit out the header if "table" is present in the format
- if strings.HasPrefix(format, "table") {
- hformat := strings.Replace(strings.TrimSpace(format[5:]), " ", "\t", -1)
- format = hformat
- headerTmpl, err := template.New("header").Parse(hformat)
- if err != nil {
- return err
- }
- if err := headerTmpl.Execute(w, headerNames); err != nil {
- return err
- }
- fmt.Fprintln(w, "")
- }
+ row = "{{range .}}" + row + "{{end}}"
- // Spit out the data rows now
- dataTmpl, err := template.New("data").Parse(format)
+ tmpl, err := template.New("pod stats").Parse(row)
if err != nil {
return err
}
- for _, s := range stats {
- if err := dataTmpl.Execute(w, s); err != nil {
- return err
- }
- fmt.Fprintln(w, "")
- }
- // Flush the writer
- return w.Flush()
-
-}
+ w := tabwriter.NewWriter(os.Stdout, 0, 0, 3, ' ', 0)
+ defer w.Flush()
-// getPodStatsHeader returns the stats header for the specified options.
-func getPodStatsHeader(format string) map[string]string {
- headerNames := make(map[string]string)
- if format == "" {
- return headerNames
- }
- // Make a map of the field names for the headers
- v := reflect.ValueOf(entities.PodStatsReport{})
- t := v.Type()
- for i := 0; i < t.NumField(); i++ {
- split := camelcase.Split(t.Field(i).Name)
- value := strings.ToUpper(strings.Join(split, " "))
- switch value {
- case "CPU", "MEM":
- value += " %"
- case "MEM USAGE":
- value = "MEM USAGE / LIMIT"
- }
- headerNames[t.Field(i).Name] = value
+ if err := tmpl.Execute(w, headerNames); err != nil {
+ return err
}
- return headerNames
+ return tmpl.Execute(w, stats)
}
diff --git a/cmd/podman/root.go b/cmd/podman/root.go
index 1e73f7540..6293fa17d 100644
--- a/cmd/podman/root.go
+++ b/cmd/podman/root.go
@@ -11,6 +11,7 @@ import (
"github.com/containers/common/pkg/config"
"github.com/containers/podman/v2/cmd/podman/registry"
"github.com/containers/podman/v2/cmd/podman/validate"
+ "github.com/containers/podman/v2/libpod/define"
"github.com/containers/podman/v2/pkg/domain/entities"
"github.com/containers/podman/v2/pkg/parallel"
"github.com/containers/podman/v2/pkg/rootless"
@@ -84,7 +85,7 @@ func init() {
func Execute() {
if err := rootCmd.ExecuteContext(registry.GetContextWithOptions()); err != nil {
- fmt.Fprintln(os.Stderr, "Error:", err.Error())
+ fmt.Fprintln(os.Stderr, formatError(err))
} else if registry.GetExitCode() == registry.ExecErrorCodeGeneric {
// The exitCode modified from registry.ExecErrorCodeGeneric,
// indicates an application
@@ -331,3 +332,19 @@ func resolveDestination() (string, string, string) {
}
return cfg.Engine.ActiveService, uri, ident
}
+
+func formatError(err error) string {
+ var message string
+ if errors.Cause(err) == define.ErrOCIRuntime {
+ // OCIRuntimeErrors include the reason for the failure in the
+ // second to last message in the error chain.
+ message = fmt.Sprintf(
+ "Error: %s: %s",
+ define.ErrOCIRuntime.Error(),
+ strings.TrimSuffix(err.Error(), ": "+define.ErrOCIRuntime.Error()),
+ )
+ } else {
+ message = "Error: " + err.Error()
+ }
+ return message
+}
diff --git a/cmd/podman/root_test.go b/cmd/podman/root_test.go
new file mode 100644
index 000000000..0473128df
--- /dev/null
+++ b/cmd/podman/root_test.go
@@ -0,0 +1,34 @@
+package main
+
+import (
+ "fmt"
+ "strings"
+ "testing"
+
+ "github.com/containers/podman/v2/libpod/define"
+ "github.com/pkg/errors"
+)
+
+func TestFormatError(t *testing.T) {
+ err := errors.New("unknown error")
+ output := formatError(err)
+ expected := fmt.Sprintf("Error: %v", err)
+
+ if output != expected {
+ t.Errorf("Expected \"%s\" to equal \"%s\"", output, err.Error())
+ }
+}
+
+func TestFormatOCIError(t *testing.T) {
+ expectedPrefix := "Error: "
+ expectedSuffix := "OCI runtime output"
+ err := errors.Wrap(define.ErrOCIRuntime, expectedSuffix)
+ output := formatError(err)
+
+ if !strings.HasPrefix(output, expectedPrefix) {
+ t.Errorf("Expected \"%s\" to start with \"%s\"", output, expectedPrefix)
+ }
+ if !strings.HasSuffix(output, expectedSuffix) {
+ t.Errorf("Expected \"%s\" to end with \"%s\"", output, expectedSuffix)
+ }
+}
diff --git a/docs/source/Introduction.rst b/docs/source/Introduction.rst
index a1f9d605e..9dcae8a83 100644
--- a/docs/source/Introduction.rst
+++ b/docs/source/Introduction.rst
@@ -100,7 +100,7 @@ To summarize, Podman makes it easy to find, run, build and share containers.
* Find: whether finding a container on dockerhub.io or quay.io, an internal registry server, or directly from a vendor, a couple of `podman search`_, and `podman pull`_ commands make it easy
* Run: it's easy to consume pre-built images with everything needed to run an entire application, or start from a Linux distribution base image with the `podman run`_ command
-* Build: creating new layers with small tweaks, or major overhauls is easy with `podman build`
-* Share: Podman let’s you push your newly built containers anywhere you want with a single `podman push`_ command
+* Build: creating new layers with small tweaks, or major overhauls is easy with `podman build`_
+* Share: Podman lets you push your newly built containers anywhere you want with a single `podman push`_ command
For more instructions on use cases, take a look at our :doc:`Tutorials` page.
diff --git a/pkg/api/handlers/compat/containers.go b/pkg/api/handlers/compat/containers.go
index 48ecfff5d..cae8f88fd 100644
--- a/pkg/api/handlers/compat/containers.go
+++ b/pkg/api/handlers/compat/containers.go
@@ -7,6 +7,7 @@ import (
"strconv"
"strings"
"syscall"
+ "time"
"github.com/containers/podman/v2/libpod"
"github.com/containers/podman/v2/libpod/define"
@@ -316,7 +317,7 @@ func LibpodToContainerJSON(l *libpod.Container, sz bool) (*types.ContainerJSON,
cb := types.ContainerJSONBase{
ID: l.ID(),
- Created: l.CreatedTime().String(),
+ Created: l.CreatedTime().Format(time.RFC3339Nano),
Path: "",
Args: nil,
State: &state,
diff --git a/pkg/api/handlers/compat/events.go b/pkg/api/handlers/compat/events.go
index 9efdd1261..a729b84d4 100644
--- a/pkg/api/handlers/compat/events.go
+++ b/pkg/api/handlers/compat/events.go
@@ -139,7 +139,8 @@ func GetEvents(w http.ResponseWriter, r *http.Request) {
if flusher, ok := w.(http.Flusher); ok {
flusher.Flush()
}
+ case <-r.Context().Done():
+ return
}
-
}
}
diff --git a/pkg/spec/config_linux_cgo.go b/pkg/spec/config_linux_cgo.go
index bc8fc4e29..d0891b574 100644
--- a/pkg/spec/config_linux_cgo.go
+++ b/pkg/spec/config_linux_cgo.go
@@ -39,7 +39,7 @@ func getSeccompConfig(config *SecurityConfig, configSpec *spec.Spec) (*spec.Linu
logrus.Debug("Loading default seccomp profile")
seccompConfig, err = goSeccomp.GetDefaultProfile(configSpec)
if err != nil {
- return nil, errors.Wrapf(err, "loading seccomp profile (%s) failed", config.SeccompProfilePath)
+ return nil, errors.Wrapf(err, "loading default seccomp profile failed")
}
}
diff --git a/test/apiv2/12-imagesMore.at b/test/apiv2/12-imagesMore.at
new file mode 100644
index 000000000..30ccf0cfc
--- /dev/null
+++ b/test/apiv2/12-imagesMore.at
@@ -0,0 +1,44 @@
+# -*- sh -*-
+#
+# Tests for more image-related endpoints
+#
+
+podman pull -q $IMAGE
+
+t GET libpod/images/json 200 \
+ .[0].Id~[0-9a-f]\\{64\\}
+iid=$(jq -r '.[0].Id' <<<"$output")
+
+# Retrieve the image tree
+t GET libpod/images/$IMAGE/tree 200 \
+ .Tree~^Image
+
+# Tag nonesuch image
+t POST "libpod/images/nonesuch/tag?repo=myrepo&tag=mytag" '' 404
+
+# Tag the image
+t POST "libpod/images/$IMAGE/tag?repo=localhost:5000/myrepo&tag=mytag" '' 201
+
+t GET libpod/images/$IMAGE/json 200 \
+ .RepoTags[1]=localhost:5000/myrepo:mytag
+
+# Run registry container
+podman run -d --name registry -p 5000:5000 docker.io/library/registry:2.6 /entrypoint.sh /etc/docker/registry/config.yml
+
+# Push to local registry
+t POST libpod/images/localhost:5000/myrepo:mytag/push\?tlsVerify\=false '' 200
+
+# Untag the image
+t POST "libpod/images/$iid/untag?repo=localhost:5000/myrepo&tag=mytag" '' 201
+
+t GET libpod/images/$IMAGE/json 200 \
+ .RepoTags[-1]=$IMAGE
+
+# Remove the registry container
+t DELETE libpod/containers/registry?force=true 204
+
+# Remove images
+t DELETE libpod/images/$IMAGE 200 \
+ .ExitCode=0
+t DELETE libpod/images/docker.io/library/registry:2.6 200 \
+ .ExitCode=0
diff --git a/test/e2e/pod_ps_test.go b/test/e2e/pod_ps_test.go
index 17ed6a9c0..a299d3cf2 100644
--- a/test/e2e/pod_ps_test.go
+++ b/test/e2e/pod_ps_test.go
@@ -8,6 +8,7 @@ import (
. "github.com/containers/podman/v2/test/utils"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
+ . "github.com/onsi/gomega/gexec"
)
var _ = Describe("Podman ps", func() {
@@ -63,7 +64,7 @@ var _ = Describe("Podman ps", func() {
result := podmanTest.Podman([]string{"pod", "ps", "-q"})
result.WaitWithDefaultTimeout()
- Expect(result.ExitCode()).To(Equal(0))
+ Expect(result).To(Exit(0))
Expect(len(result.OutputToStringArray())).Should(BeNumerically(">", 0))
Expect(podid).To(ContainSubstring(result.OutputToStringArray()[0]))
})
diff --git a/test/e2e/pod_stats_test.go b/test/e2e/pod_stats_test.go
index 1ffbe282b..41fc59267 100644
--- a/test/e2e/pod_stats_test.go
+++ b/test/e2e/pod_stats_test.go
@@ -6,6 +6,7 @@ import (
. "github.com/containers/podman/v2/test/utils"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
+ . "github.com/onsi/gomega/gexec"
)
var _ = Describe("Podman pod stats", func() {
@@ -156,9 +157,9 @@ var _ = Describe("Podman pod stats", func() {
session := podmanTest.RunTopContainerInPod("", podid)
session.WaitWithDefaultTimeout()
Expect(session.ExitCode()).To(Equal(0))
- stats := podmanTest.Podman([]string{"pod", "stats", "-a", "--no-reset", "--no-stream", "--format", "\"table {{.CID}} {{.Pod}} {{.Mem}} {{.MemUsage}} {{.CPU}} {{.NetIO}} {{.BlockIO}} {{.PIDS}} {{.Pod}}\""})
+ stats := podmanTest.Podman([]string{"pod", "stats", "-a", "--no-reset", "--no-stream", "--format", "table {{.CID}} {{.Pod}} {{.Mem}} {{.MemUsage}} {{.CPU}} {{.NetIO}} {{.BlockIO}} {{.PIDS}} {{.Pod}}"})
stats.WaitWithDefaultTimeout()
- Expect(stats.ExitCode()).To(Equal(0))
+ Expect(stats).To(Exit(0))
})
It("podman stats with invalid GO template", func() {
diff --git a/test/system/010-images.bats b/test/system/010-images.bats
index ac65e54d9..900a24368 100644
--- a/test/system/010-images.bats
+++ b/test/system/010-images.bats
@@ -159,4 +159,56 @@ Labels.created_at | 20[0-9-]\\\+T[0-9:]\\\+Z
is "$output" "$images_baseline" "after podman rmi @sha, still the same"
}
+# Tests #7199 (Restore "table" --format from V1)
+#
+# Tag our image with different-length strings; confirm table alignment
+@test "podman images - table format" {
+ # Craft two tags such that they will bracket $IMAGE on either side (above
+ # and below). This assumes that $IMAGE is quay.io or foo.com or simply
+ # not something insane that will sort before 'aaa' or after 'zzz'.
+ local aaa_name=a.b/c
+ local aaa_tag=d
+ local zzz_name=zzzzzzzzzz.yyyyyyyyy/xxxxxxxxx
+ local zzz_tag=$(random_string 15)
+
+ # Helper function to check one line of tabular output; all this does is
+ # generate a line with the given repo/tag, formatted to the width of the
+ # widest image, which is the zzz one. Fields are separated by TWO spaces.
+ function _check_line() {
+ local lineno=$1
+ local name=$2
+ local tag=$3
+
+ is "${lines[$lineno]}" \
+ "$(printf '%-*s %-*s %s' ${#zzz_name} ${name} ${#zzz_tag} ${tag} $iid)" \
+ "podman images, $testname, line $lineno"
+ }
+
+ function _run_format_test() {
+ local testname=$1
+ local format=$2
+
+ run_podman images --sort repository --format "$format"
+ _check_line 0 ${aaa_name} ${aaa_tag}
+ _check_line 1 "${PODMAN_TEST_IMAGE_REGISTRY}/${PODMAN_TEST_IMAGE_USER}/${PODMAN_TEST_IMAGE_NAME}" "${PODMAN_TEST_IMAGE_TAG}"
+ _check_line 2 ${zzz_name} ${zzz_tag}
+ }
+
+ # Begin the test: tag $IMAGE with both the given names
+ run_podman tag $IMAGE ${aaa_name}:${aaa_tag}
+ run_podman tag $IMAGE ${zzz_name}:${zzz_tag}
+
+ # Get the image ID, used to verify output below (all images share same IID)
+ run_podman inspect --format '{{.ID}}' $IMAGE
+ iid=${output:0:12}
+
+ # Run the test: this will output three column-aligned rows. Test them.
+ # Tab character (\t) should have the same effect as the 'table' directive
+ _run_format_test 'table' 'table {{.Repository}} {{.Tag}} {{.ID}}'
+ _run_format_test 'tabs' '{{.Repository}}\t{{.Tag}}\t{{.ID}}'
+
+ # Clean up.
+ run_podman rmi ${aaa_name}:${aaa_tag} ${zzz_name}:${zzz_tag}
+}
+
# vim: filetype=sh
diff --git a/test/system/060-mount.bats b/test/system/060-mount.bats
index f11aff773..ece87acf6 100644
--- a/test/system/060-mount.bats
+++ b/test/system/060-mount.bats
@@ -43,6 +43,11 @@ load helpers
# Start with clean slate
run_podman image umount -a
+ # Get full image ID, to verify umount
+ run_podman image inspect --format '{{.ID}}' $IMAGE
+ iid="$output"
+
+ # Mount, and make sure the mount point exists
run_podman image mount $IMAGE
mount_path="$output"
@@ -60,6 +65,14 @@ load helpers
# Clean up
run_podman image umount $IMAGE
+ is "$output" "$iid" "podman image umount: image ID of what was umounted"
+
+ run_podman image umount $IMAGE
+ is "$output" "" "podman image umount: does not re-umount"
+
+ run_podman 125 image umount no-such-container
+ is "$output" "Error: unable to find a name and tag match for no-such-container in repotags: no such image" \
+ "error message from image umount no-such-container"
run_podman image mount
is "$output" "" "podman image mount, no args, after umount"
diff --git a/test/system/130-kill.bats b/test/system/130-kill.bats
index c16e64c58..3770eac27 100644
--- a/test/system/130-kill.bats
+++ b/test/system/130-kill.bats
@@ -6,23 +6,9 @@
load helpers
@test "podman kill - test signal handling in containers" {
- # podman-remote and crun interact poorly in f31: crun seems to gobble up
- # some signals.
- # Workaround: run 'env --default-signal sh' instead of just 'sh' in
- # the container. Since env on our regular alpine image doesn't support
- # that flag, we need to pull fedora-minimal. See:
- # https://github.com/containers/podman/issues/5004
- # FIXME: remove this kludge once we get rid of podman-remote
- local _image=$IMAGE
- local _sh_cmd="sh"
- if is_remote; then
- _image=quay.io/libpod/fedora-minimal:latest
- _sh_cmd="env --default-signal sh"
- fi
-
# Start a container that will handle all signals by emitting 'got: N'
local -a signals=(1 2 3 4 5 6 8 10 12 13 14 15 16 20 21 22 23 24 25 26 64)
- run_podman run -d $_image $_sh_cmd -c \
+ run_podman run -d $IMAGE sh -c \
"for i in ${signals[*]}; do trap \"echo got: \$i\" \$i; done;
echo READY;
while ! test -e /stop; do sleep 0.05; done;
@@ -81,10 +67,6 @@ load helpers
run_podman wait $cid
run_podman rm $cid
wait $podman_log_pid
-
- if [[ $_image != $IMAGE ]]; then
- run_podman rmi $_image
- fi
}
@test "podman kill - rejects invalid args" {
diff --git a/test/system/410-selinux.bats b/test/system/410-selinux.bats
index 497e29b3e..1e44fe06c 100644
--- a/test/system/410-selinux.bats
+++ b/test/system/410-selinux.bats
@@ -7,9 +7,7 @@ load helpers
function check_label() {
- if [ ! -e /usr/sbin/selinuxenabled ] || ! /usr/sbin/selinuxenabled; then
- skip "selinux disabled or not available"
- fi
+ skip_if_no_selinux
local args="$1"; shift # command-line args for run
@@ -52,15 +50,33 @@ function check_label() {
check_label "--privileged --userns=host" "spc_t"
}
+@test "podman selinux: pid=host" {
+ # FIXME FIXME FIXME: Remove these lines once all VMs have >= 2.146.0
+ # (this is ugly, but better than an unconditional skip)
+ skip_if_no_selinux
+ if is_rootless; then
+ if [ -x /usr/bin/rpm ]; then
+ cs_version=$(rpm -q --qf '%{version}' container-selinux)
+ else
+ # SELinux not enabled on Ubuntu, so we should never get here
+ die "WHOA! SELinux enabled, but no /usr/bin/rpm!"
+ fi
+ if [[ "$cs_version" < "2.146" ]]; then
+ skip "FIXME: #7939: requires container-selinux-2.146.0 (currently installed: $cs_version)"
+ fi
+ fi
+ # FIXME FIXME FIXME: delete up to here, leaving just check_label
+
+ check_label "--pid=host" "spc_t"
+}
+
@test "podman selinux: container with overridden range" {
check_label "--security-opt label=level:s0:c1,c2" "container_t" "s0:c1,c2"
}
# pr #6752
@test "podman selinux: inspect multiple labels" {
- if [ ! -e /usr/sbin/selinuxenabled ] || ! /usr/sbin/selinuxenabled; then
- skip "selinux disabled or not available"
- fi
+ skip_if_no_selinux
run_podman run -d --name myc \
--security-opt seccomp=unconfined \
@@ -75,4 +91,84 @@ function check_label() {
run_podman rm -f myc
}
+# Sharing context between two containers not in a pod
+# These tests were piggybacked in with #7902, but are not actually related
+@test "podman selinux: shared context in (some) namespaces" {
+ skip_if_no_selinux
+
+ run_podman run -d --name myctr $IMAGE top
+ run_podman exec myctr cat -v /proc/self/attr/current
+ context_c1="$output"
+
+ # --ipc container
+ run_podman run --name myctr2 --ipc container:myctr $IMAGE cat -v /proc/self/attr/current
+ is "$output" "$context_c1" "new container, run with ipc of existing one "
+
+ # --pid container
+ run_podman run --rm --pid container:myctr $IMAGE cat -v /proc/self/attr/current
+ is "$output" "$context_c1" "new container, run with --pid of existing one "
+
+ # net NS: do not share context
+ run_podman run --rm --net container:myctr $IMAGE cat -v /proc/self/attr/current
+ if [[ "$output" = "$context_c1" ]]; then
+ die "run --net : context ($output) is same as running container (it should not be)"
+ fi
+
+ # The 'myctr2' above was not run with --rm, so it still exists, and
+ # we can't remove the original container until this one is gone.
+ run_podman stop -t 0 myctr
+ run_podman 125 rm myctr
+ is "$output" "Error: container .* has dependent containers"
+
+ # We have to do this in two steps: even if ordered as 'myctr2 myctr',
+ # podman will try the removes in random order, which fails if it
+ # tries myctr first.
+ run_podman rm myctr2
+ run_podman rm myctr
+}
+
+# pr #7902 - containers in pods should all run under same context
+@test "podman selinux: containers in pods share full context" {
+ skip_if_no_selinux
+
+ # We don't need a fullblown pause container; avoid pulling the k8s one
+ run_podman pod create --name myselinuxpod \
+ --infra-image $IMAGE \
+ --infra-command /home/podman/pause
+
+ # Get baseline
+ run_podman run --rm --pod myselinuxpod $IMAGE cat -v /proc/self/attr/current
+ context_c1="$output"
+
+ # Prior to #7902, the labels (':c123,c456') would be different
+ run_podman run --rm --pod myselinuxpod $IMAGE cat -v /proc/self/attr/current
+ is "$output" "$context_c1" "SELinux context of 2nd container matches 1st"
+
+ # What the heck. Try a third time just for extra confidence
+ run_podman run --rm --pod myselinuxpod $IMAGE cat -v /proc/self/attr/current
+ is "$output" "$context_c1" "SELinux context of 3rd container matches 1st"
+
+ run_podman pod rm myselinuxpod
+}
+
+# more pr #7902
+@test "podman selinux: containers in --no-infra pods do not share context" {
+ skip_if_no_selinux
+
+ # We don't need a fullblown pause container; avoid pulling the k8s one
+ run_podman pod create --name myselinuxpod --infra=false
+
+ # Get baseline
+ run_podman run --rm --pod myselinuxpod $IMAGE cat -v /proc/self/attr/current
+ context_c1="$output"
+
+ # Even after #7902, labels (':c123,c456') should be different
+ run_podman run --rm --pod myselinuxpod $IMAGE cat -v /proc/self/attr/current
+ if [[ "$output" = "$context_c1" ]]; then
+ die "context ($output) is the same on two separate containers, it should have been different"
+ fi
+
+ run_podman pod rm myselinuxpod
+}
+
# vim: filetype=sh
diff --git a/test/system/helpers.bash b/test/system/helpers.bash
index 998db5283..c6c2c12df 100644
--- a/test/system/helpers.bash
+++ b/test/system/helpers.bash
@@ -286,6 +286,17 @@ function skip_if_remote() {
fi
}
+########################
+# skip_if_no_selinux #
+########################
+function skip_if_no_selinux() {
+ if [ ! -e /usr/sbin/selinuxenabled ]; then
+ skip "selinux not available"
+ elif ! /usr/sbin/selinuxenabled; then
+ skip "selinux disabled"
+ fi
+}
+
#########
# die # Abort with helpful message
#########