summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CONTRIBUTING.md13
-rw-r--r--cmd/podman/common/create.go10
-rw-r--r--cmd/podman/common/util.go2
-rw-r--r--cmd/podman/containers/create.go1
-rw-r--r--cmd/podman/containers/mount.go21
-rw-r--r--cmd/podman/containers/run.go1
-rw-r--r--cmd/podman/images/diff.go9
-rw-r--r--cmd/podman/images/history.go57
-rw-r--r--cmd/podman/images/mount.go27
-rw-r--r--cmd/podman/images/search.go56
-rw-r--r--cmd/podman/inspect/inspect.go49
-rw-r--r--cmd/podman/networks/create.go4
-rw-r--r--cmd/podman/networks/list.go2
-rw-r--r--cmd/podman/pods/inspect.go24
-rw-r--r--docs/source/markdown/podman-search.1.md4
-rw-r--r--libpod/container.go19
-rw-r--r--libpod/container_config.go5
-rw-r--r--libpod/container_inspect.go3
-rw-r--r--libpod/container_internal.go24
-rw-r--r--libpod/container_internal_linux.go12
-rw-r--r--libpod/define/container_inspect.go3
-rw-r--r--libpod/image/docker_registry_options.go1
-rw-r--r--libpod/network/config.go (renamed from pkg/network/config.go)11
-rw-r--r--libpod/network/create.go195
-rw-r--r--libpod/network/devices.go (renamed from pkg/network/devices.go)0
-rw-r--r--libpod/network/files.go (renamed from pkg/network/files.go)0
-rw-r--r--libpod/network/ip.go (renamed from pkg/network/ip.go)0
-rw-r--r--libpod/network/lock.go26
-rw-r--r--libpod/network/netconflist.go (renamed from pkg/network/netconflist.go)0
-rw-r--r--libpod/network/netconflist_test.go (renamed from pkg/network/netconflist_test.go)0
-rw-r--r--libpod/network/network.go (renamed from pkg/network/network.go)10
-rw-r--r--libpod/network/network_test.go (renamed from pkg/network/network_test.go)0
-rw-r--r--libpod/network/subnet.go (renamed from pkg/network/subnet.go)0
-rw-r--r--libpod/network/subnet_test.go (renamed from pkg/network/subnet_test.go)0
-rw-r--r--libpod/oci_conmon_linux.go12
-rw-r--r--libpod/pod_api.go241
-rw-r--r--libpod/runtime_ctr.go1
-rw-r--r--pkg/api/handlers/compat/containers_create.go9
-rw-r--r--pkg/api/handlers/compat/images.go42
-rw-r--r--pkg/api/handlers/compat/networks.go8
-rw-r--r--pkg/api/handlers/libpod/pods.go6
-rw-r--r--pkg/api/server/register_images.go25
-rw-r--r--pkg/domain/infra/abi/containers.go6
-rw-r--r--pkg/domain/infra/abi/network.go176
-rw-r--r--pkg/domain/infra/abi/pods.go6
-rw-r--r--pkg/parallel/ctr/ctr.go (renamed from pkg/parallel/parallel_linux.go)45
-rw-r--r--pkg/parallel/parallel.go30
-rw-r--r--pkg/varlinkapi/pods.go7
-rw-r--r--test/apiv2/10-images.at3
-rw-r--r--test/e2e/build_test.go9
-rw-r--r--test/e2e/common_test.go4
-rw-r--r--test/e2e/exec_test.go2
-rw-r--r--test/e2e/images_test.go5
-rw-r--r--test/e2e/load_test.go2
-rw-r--r--test/e2e/logs_test.go2
-rw-r--r--test/e2e/namespace_test.go7
-rw-r--r--test/e2e/network_create_test.go3
-rw-r--r--test/e2e/play_kube_test.go4
-rw-r--r--test/e2e/pod_infra_container_test.go1
-rw-r--r--test/e2e/pod_pod_namespaces.go1
-rw-r--r--test/e2e/prune_test.go5
-rw-r--r--test/e2e/pull_test.go12
-rw-r--r--test/e2e/rmi_test.go4
-rw-r--r--test/e2e/run_entrypoint_test.go2
-rw-r--r--test/e2e/run_env_test.go13
-rw-r--r--test/e2e/run_networking_test.go17
-rw-r--r--test/e2e/run_restart_test.go5
-rw-r--r--test/e2e/run_test.go10
-rw-r--r--test/e2e/run_working_dir.go1
-rw-r--r--test/e2e/search_test.go6
-rw-r--r--test/e2e/system_df_test.go9
-rw-r--r--test/system/060-mount.bats2
-rw-r--r--test/system/070-build.bats9
73 files changed, 787 insertions, 554 deletions
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index ba321921c..a813fcc35 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -46,7 +46,7 @@ This section describes how to start a contribution to Podman.
### Prepare your environment
-Read the [install documentation to see how to install dependencies](install.md) .
+Read the [install documentation to see how to install dependencies](https://podman.io/getting-started/installation#build-and-run-dependencies).
The install documentation will illustrate the following steps:
- install libs and tools
@@ -86,6 +86,17 @@ Makefile allow you to install needed tools:
$ make install.tools
```
+### Prerequisite before build
+
+You need install some dependencies before building a binary.
+
+#### Fedora
+
+ ```shell
+ $ sudo dnf install gpgme-devel libseccomp-devel.x86_64 libseccomp-devel.x86_64 systemd-devel
+ $ export PKG_CONFIG_PATH="/usr/lib/pkgconfig"
+ ```
+
### Building binaries and test your changes
To test your changes do `make binaries` to generate your binaries.
diff --git a/cmd/podman/common/create.go b/cmd/podman/common/create.go
index bb5eb9f38..0ec422313 100644
--- a/cmd/podman/common/create.go
+++ b/cmd/podman/common/create.go
@@ -166,10 +166,12 @@ func GetCreateFlags(cf *ContainerCLIOpts) *pflag.FlagSet {
"env", "e", containerConfig.Env(),
"Set environment variables in container",
)
- createFlags.BoolVar(
- &cf.EnvHost,
- "env-host", false, "Use all current host environment variables in container",
- )
+ if !registry.IsRemote() {
+ createFlags.BoolVar(
+ &cf.EnvHost,
+ "env-host", false, "Use all current host environment variables in container",
+ )
+ }
createFlags.StringSliceVar(
&cf.EnvFile,
"env-file", []string{},
diff --git a/cmd/podman/common/util.go b/cmd/podman/common/util.go
index 17e779c86..a971aa957 100644
--- a/cmd/podman/common/util.go
+++ b/cmd/podman/common/util.go
@@ -200,8 +200,6 @@ func parseSplitPort(hostIP, hostPort *string, ctrPort string, protocol *string)
}
newPort.HostPort = hostStart
}
- } else {
- newPort.HostPort = newPort.ContainerPort
}
hport := newPort.HostPort
diff --git a/cmd/podman/containers/create.go b/cmd/podman/containers/create.go
index d75352848..809162e76 100644
--- a/cmd/podman/containers/create.go
+++ b/cmd/podman/containers/create.go
@@ -64,7 +64,6 @@ func createFlags(flags *pflag.FlagSet) {
_ = flags.MarkHidden("signature-policy")
if registry.IsRemote() {
_ = flags.MarkHidden("authfile")
- _ = flags.MarkHidden("env-host")
_ = flags.MarkHidden("http-proxy")
}
}
diff --git a/cmd/podman/containers/mount.go b/cmd/podman/containers/mount.go
index f2df5e99e..c4dfb513f 100644
--- a/cmd/podman/containers/mount.go
+++ b/cmd/podman/containers/mount.go
@@ -6,6 +6,7 @@ import (
"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/utils"
"github.com/containers/podman/v2/cmd/podman/validate"
@@ -75,9 +76,6 @@ func init() {
}
func mount(_ *cobra.Command, args []string) error {
- var (
- errs utils.OutputErrors
- )
if len(args) > 0 && mountOpts.Latest {
return errors.Errorf("--latest and containers cannot be used together")
}
@@ -85,7 +83,9 @@ func mount(_ *cobra.Command, args []string) error {
if err != nil {
return err
}
+
if len(args) > 0 || mountOpts.Latest || mountOpts.All {
+ var errs utils.OutputErrors
for _, r := range reports {
if r.Err == nil {
fmt.Println(r.Path)
@@ -96,21 +96,21 @@ func mount(_ *cobra.Command, args []string) error {
return errs.PrintErrors()
}
- switch mountOpts.Format {
- case "json":
+ switch {
+ case parse.MatchesJSONFormat(mountOpts.Format):
return printJSON(reports)
- case "":
- // do nothing
+ case mountOpts.Format == "":
+ break // print defaults
default:
- return errors.Errorf("unknown --format argument: %s", mountOpts.Format)
+ return errors.Errorf("unknown --format argument: %q", mountOpts.Format)
}
mrs := make([]mountReporter, 0, len(reports))
for _, r := range reports {
mrs = append(mrs, mountReporter{r})
}
- row := "{{.ID}} {{.Path}}\n"
- format := "{{range . }}" + row + "{{end}}"
+
+ format := "{{range . }}{{.ID}}\t{{.Path}}\n{{end}}"
tmpl, err := template.New("mounts").Parse(format)
if err != nil {
return err
@@ -139,6 +139,7 @@ func printJSON(reports []*entities.ContainerMountReport) error {
if err != nil {
return err
}
+
fmt.Println(string(b))
return nil
}
diff --git a/cmd/podman/containers/run.go b/cmd/podman/containers/run.go
index 8052b033e..19c984bbf 100644
--- a/cmd/podman/containers/run.go
+++ b/cmd/podman/containers/run.go
@@ -67,7 +67,6 @@ func runFlags(flags *pflag.FlagSet) {
_ = flags.MarkHidden("signature-policy")
if registry.IsRemote() {
_ = flags.MarkHidden("authfile")
- _ = flags.MarkHidden("env-host")
_ = flags.MarkHidden("http-proxy")
_ = flags.MarkHidden("preserve-fds")
}
diff --git a/cmd/podman/images/diff.go b/cmd/podman/images/diff.go
index 26147345e..05a05fa04 100644
--- a/cmd/podman/images/diff.go
+++ b/cmd/podman/images/diff.go
@@ -1,6 +1,7 @@
package images
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/pkg/domain/entities"
@@ -49,11 +50,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/images/history.go b/cmd/podman/images/history.go
index 30abf0ada..fa4b368c6 100644
--- a/cmd/podman/images/history.go
+++ b/cmd/podman/images/history.go
@@ -10,7 +10,9 @@ import (
"time"
"unicode"
+ "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"
@@ -28,9 +30,9 @@ var (
Use: "history [flags] IMAGE",
Short: "Show history of a specified image",
Long: long,
- Example: "podman history quay.io/fedora/fedora",
Args: cobra.ExactArgs(1),
RunE: history,
+ Example: "podman history quay.io/fedora/fedora",
}
imageHistoryCmd = &cobra.Command{
@@ -39,7 +41,7 @@ var (
Short: historyCmd.Short,
Long: historyCmd.Long,
RunE: historyCmd.RunE,
- Example: `podman image history imageID`,
+ Example: `podman image history quay.io/fedora/fedora`,
}
opts = struct {
@@ -79,7 +81,7 @@ func history(cmd *cobra.Command, args []string) error {
return err
}
- if opts.format == "json" {
+ if parse.MatchesJSONFormat(opts.format) {
var err error
if len(results.Layers) == 0 {
_, err = fmt.Fprintf(os.Stdout, "[]\n")
@@ -100,69 +102,66 @@ func history(cmd *cobra.Command, args []string) error {
}
return err
}
- hr := make([]historyreporter, 0, len(results.Layers))
+
+ hr := make([]historyReporter, 0, len(results.Layers))
for _, l := range results.Layers {
- hr = append(hr, historyreporter{l})
+ hr = append(hr, historyReporter{l})
}
+
+ hdrs := report.Headers(historyReporter{}, map[string]string{
+ "CreatedBy": "CREATED BY",
+ })
+
// Defaults
- hdr := "ID\tCREATED\tCREATED BY\tSIZE\tCOMMENT\n"
row := "{{.ID}}\t{{.Created}}\t{{.CreatedBy}}\t{{.Size}}\t{{.Comment}}\n"
-
switch {
- case len(opts.format) > 0:
- hdr = ""
- row = opts.format
- if !strings.HasSuffix(opts.format, "\n") {
- row += "\n"
- }
+ case cmd.Flags().Changed("format"):
+ row = report.NormalizeFormat(opts.format)
case opts.quiet:
- hdr = ""
row = "{{.ID}}\n"
- case opts.human:
- row = "{{.ID}}\t{{.Created}}\t{{.CreatedBy}}\t{{.Size}}\t{{.Comment}}\n"
- case opts.noTrunc:
- row = "{{.ID}}\t{{.Created}}\t{{.CreatedBy}}\t{{.Size}}\t{{.Comment}}\n"
}
- format := hdr + "{{range . }}" + row + "{{end}}"
+ format := "{{range . }}" + row + "{{end}}"
tmpl, err := template.New("report").Parse(format)
if err != nil {
return err
}
w := tabwriter.NewWriter(os.Stdout, 8, 2, 2, ' ', 0)
- err = tmpl.Execute(w, hr)
- if err != nil {
- fmt.Fprintln(os.Stderr, errors.Wrapf(err, "failed to print report"))
+ defer w.Flush()
+
+ if !opts.quiet && !cmd.Flags().Changed("format") {
+ if err := tmpl.Execute(w, hdrs); err != nil {
+ return errors.Wrapf(err, "failed to write report column headers")
+ }
}
- w.Flush()
- return nil
+ return tmpl.Execute(w, hr)
}
-type historyreporter struct {
+type historyReporter struct {
entities.ImageHistoryLayer
}
-func (h historyreporter) Created() string {
+func (h historyReporter) Created() string {
if opts.human {
return units.HumanDuration(time.Since(h.ImageHistoryLayer.Created)) + " ago"
}
return h.ImageHistoryLayer.Created.Format(time.RFC3339)
}
-func (h historyreporter) Size() string {
+func (h historyReporter) Size() string {
s := units.HumanSizeWithPrecision(float64(h.ImageHistoryLayer.Size), 3)
i := strings.LastIndexFunc(s, unicode.IsNumber)
return s[:i+1] + " " + s[i+1:]
}
-func (h historyreporter) CreatedBy() string {
+func (h historyReporter) CreatedBy() string {
if len(h.ImageHistoryLayer.CreatedBy) > 45 {
return h.ImageHistoryLayer.CreatedBy[:45-3] + "..."
}
return h.ImageHistoryLayer.CreatedBy
}
-func (h historyreporter) ID() string {
+func (h historyReporter) ID() string {
if !opts.noTrunc && len(h.ImageHistoryLayer.ID) >= 12 {
return h.ImageHistoryLayer.ID[0:12]
}
diff --git a/cmd/podman/images/mount.go b/cmd/podman/images/mount.go
index fac06e324..0a972ea81 100644
--- a/cmd/podman/images/mount.go
+++ b/cmd/podman/images/mount.go
@@ -6,6 +6,7 @@ import (
"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/utils"
"github.com/containers/podman/v2/pkg/domain/entities"
@@ -24,7 +25,7 @@ var (
mountCommand = &cobra.Command{
Use: "mount [flags] [IMAGE...]",
- Short: "Mount an images's root filesystem",
+ Short: "Mount an image's root filesystem",
Long: mountDescription,
RunE: mount,
Example: `podman image mount imgID
@@ -56,18 +57,18 @@ func init() {
mountFlags(mountCommand.Flags())
}
-func mount(_ *cobra.Command, args []string) error {
- var (
- errs utils.OutputErrors
- )
+func mount(cmd *cobra.Command, args []string) error {
if len(args) > 0 && mountOpts.All {
return errors.New("when using the --all switch, you may not pass any image names or IDs")
}
+
reports, err := registry.ImageEngine().Mount(registry.GetContext(), args, mountOpts)
if err != nil {
return err
}
+
if len(args) > 0 || mountOpts.All {
+ var errs utils.OutputErrors
for _, r := range reports {
if r.Err == nil {
fmt.Println(r.Path)
@@ -78,22 +79,22 @@ func mount(_ *cobra.Command, args []string) error {
return errs.PrintErrors()
}
- switch mountOpts.Format {
- case "json":
+ switch {
+ case parse.MatchesJSONFormat(mountOpts.Format):
return printJSON(reports)
- case "":
- // do nothing
+ case mountOpts.Format == "":
+ break // default format
default:
- return errors.Errorf("unknown --format argument: %s", mountOpts.Format)
+ return errors.Errorf("unknown --format argument: %q", mountOpts.Format)
}
mrs := make([]mountReporter, 0, len(reports))
for _, r := range reports {
mrs = append(mrs, mountReporter{r})
}
- row := "{{.ID}} {{.Path}}\n"
- format := "{{range . }}" + row + "{{end}}"
- tmpl, err := template.New("mounts").Parse(format)
+
+ row := "{{range . }}{{.ID}}\t{{.Path}}\n{{end}}"
+ tmpl, err := template.New("mounts").Parse(row)
if err != nil {
return err
}
diff --git a/cmd/podman/images/search.go b/cmd/podman/images/search.go
index c7f16a838..b8f590585 100644
--- a/cmd/podman/images/search.go
+++ b/cmd/podman/images/search.go
@@ -2,15 +2,14 @@ package images
import (
"os"
- "reflect"
- "strings"
+ "text/tabwriter"
+ "text/template"
- "github.com/containers/buildah/pkg/formats"
"github.com/containers/common/pkg/auth"
"github.com/containers/image/v5/types"
"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/containers/podman/v2/pkg/util/camelcase"
"github.com/pkg/errors"
"github.com/spf13/cobra"
"github.com/spf13/pflag"
@@ -86,11 +85,6 @@ func searchFlags(flags *pflag.FlagSet) {
flags.BoolVar(&searchOptions.NoTrunc, "no-trunc", false, "Do not truncate the output")
flags.StringVar(&searchOptions.Authfile, "authfile", auth.GetDefaultAuthFile(), "Path of the authentication file. Use REGISTRY_AUTH_FILE environment variable to override")
flags.BoolVar(&searchOptions.TLSVerifyCLI, "tls-verify", true, "Require HTTPS and verify certificates when contacting registries")
-
- if registry.IsRemote() {
- _ = flags.MarkHidden("authfile")
- _ = flags.MarkHidden("tls-verify")
- }
}
// imageSearch implements the command for searching images.
@@ -125,41 +119,29 @@ func imageSearch(cmd *cobra.Command, args []string) error {
if err != nil {
return err
}
-
- format := genSearchFormat(searchOptions.Format)
if len(searchReport) == 0 {
return nil
}
- out := formats.StdoutTemplateArray{Output: searchToGeneric(searchReport), Template: format, Fields: searchHeaderMap()}
- return out.Out()
-}
-
-// searchHeaderMap returns the headers of a SearchResult.
-func searchHeaderMap() map[string]string {
- s := new(entities.ImageSearchReport)
- v := reflect.Indirect(reflect.ValueOf(s))
- values := make(map[string]string, v.NumField())
- for i := 0; i < v.NumField(); i++ {
- key := v.Type().Field(i).Name
- value := key
- values[key] = strings.ToUpper(strings.Join(camelcase.Split(value), " "))
+ hdrs := report.Headers(entities.ImageSearchReport{}, nil)
+ row := "{{.Index}}\t{{.Name}}\t{{.Description}}\t{{.Stars}}\t{{.Official}}\t{{.Automated}}\n"
+ if cmd.Flags().Changed("format") {
+ row = report.NormalizeFormat(searchOptions.Format)
}
- return values
-}
+ row = "{{range .}}" + row + "{{end}}"
-func genSearchFormat(format string) string {
- if format != "" {
- // "\t" from the command line is not being recognized as a tab
- // replacing the string "\t" to a tab character if the user passes in "\t"
- return strings.Replace(format, `\t`, "\t", -1)
+ tmpl, err := template.New("search").Parse(row)
+ if err != nil {
+ return err
}
- return "table {{.Index}}\t{{.Name}}\t{{.Description}}\t{{.Stars}}\t{{.Official}}\t{{.Automated}}\t"
-}
+ w := tabwriter.NewWriter(os.Stdout, 8, 2, 2, ' ', 0)
+ defer w.Flush()
-func searchToGeneric(params []entities.ImageSearchReport) (genericParams []interface{}) {
- for _, v := range params {
- genericParams = append(genericParams, interface{}(v))
+ if !cmd.Flags().Changed("format") {
+ if err := tmpl.Execute(w, hdrs); err != nil {
+ return errors.Wrapf(err, "failed to write search column headers")
+ }
}
- return genericParams
+
+ return tmpl.Execute(w, searchReport)
}
diff --git a/cmd/podman/inspect/inspect.go b/cmd/podman/inspect/inspect.go
index f29527412..658463650 100644
--- a/cmd/podman/inspect/inspect.go
+++ b/cmd/podman/inspect/inspect.go
@@ -4,10 +4,14 @@ import (
"context"
"fmt"
"os"
+ "regexp"
"strings"
+ "text/tabwriter"
+ "text/template"
- "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/pkg/errors"
@@ -24,6 +28,9 @@ const (
AllType = "all"
)
+// Pull in configured json library
+var json = registry.JSONLibrary()
+
// AddInspectFlagSet takes a command and adds the inspect flags and returns an
// InspectOptions object.
func AddInspectFlagSet(cmd *cobra.Command) *entities.InspectOptions {
@@ -80,7 +87,7 @@ func newInspector(options entities.InspectOptions) (*inspector, error) {
// inspect inspects the specified container/image names or IDs.
func (i *inspector) inspect(namesOrIDs []string) error {
// data - dumping place for inspection results.
- var data []interface{} //nolint
+ var data []interface{} // nolint
var errs []error
ctx := context.Background()
@@ -134,15 +141,19 @@ func (i *inspector) inspect(namesOrIDs []string) error {
data = []interface{}{}
}
- var out formats.Writer
- if i.options.Format == "json" || i.options.Format == "" { // "" for backwards compat
- out = formats.JSONStructArray{Output: data}
- } else {
- out = formats.StdoutTemplateArray{Output: data, Template: inspectFormat(i.options.Format)}
+ var err error
+ switch {
+ case parse.MatchesJSONFormat(i.options.Format) || i.options.Format == "":
+ err = printJSON(data)
+ default:
+ row := inspectNormalize(i.options.Format)
+ row = "{{range . }}" + report.NormalizeFormat(row) + "{{end}}"
+ err = printTmpl(tmpType, row, data)
}
- if err := out.Out(); err != nil {
+ if err != nil {
logrus.Errorf("Error printing inspect output: %v", err)
}
+
if len(errs) > 0 {
if len(errs) > 1 {
for _, err := range errs[1:] {
@@ -154,8 +165,22 @@ func (i *inspector) inspect(namesOrIDs []string) error {
return nil
}
+func printJSON(data []interface{}) error {
+ enc := json.NewEncoder(os.Stdout)
+ return enc.Encode(data)
+}
+
+func printTmpl(typ, row string, data []interface{}) error {
+ t, err := template.New(typ + " inspect").Parse(row)
+ if err != nil {
+ return err
+ }
+ w := tabwriter.NewWriter(os.Stdout, 8, 2, 2, ' ', 0)
+ return t.Execute(w, data)
+}
+
func (i *inspector) inspectAll(ctx context.Context, namesOrIDs []string) ([]interface{}, []error, error) {
- var data []interface{} //nolint
+ var data []interface{} // nolint
allErrs := []error{}
for _, name := range namesOrIDs {
ctrData, errs, err := i.containerEngine.ContainerInspect(ctx, []string{name}, i.options)
@@ -179,9 +204,11 @@ func (i *inspector) inspectAll(ctx context.Context, namesOrIDs []string) ([]inte
return data, allErrs, nil
}
-func inspectFormat(row string) string {
+func inspectNormalize(row string) string {
+ m := regexp.MustCompile(`{{\s*\.Id\s*}}`)
+ row = m.ReplaceAllString(row, "{{.ID}}")
+
r := strings.NewReplacer(
- "{{.Id}}", formats.IDString,
".Src", ".Source",
".Dst", ".Destination",
".ImageID", ".Image",
diff --git a/cmd/podman/networks/create.go b/cmd/podman/networks/create.go
index 68a577ae1..17f39bd8b 100644
--- a/cmd/podman/networks/create.go
+++ b/cmd/podman/networks/create.go
@@ -7,7 +7,6 @@ import (
"github.com/containers/podman/v2/cmd/podman/registry"
"github.com/containers/podman/v2/libpod/define"
"github.com/containers/podman/v2/pkg/domain/entities"
- "github.com/containers/podman/v2/pkg/network"
"github.com/spf13/cobra"
"github.com/spf13/pflag"
)
@@ -56,9 +55,6 @@ func networkCreate(cmd *cobra.Command, args []string) error {
var (
name string
)
- if err := network.IsSupportedDriver(networkCreateOptions.Driver); err != nil {
- return err
- }
if len(args) > 0 {
if !define.NameRegex.MatchString(args[0]) {
return define.RegexError
diff --git a/cmd/podman/networks/list.go b/cmd/podman/networks/list.go
index b6fb2bb80..c53f50c9f 100644
--- a/cmd/podman/networks/list.go
+++ b/cmd/podman/networks/list.go
@@ -10,8 +10,8 @@ import (
"github.com/containers/podman/v2/cmd/podman/registry"
"github.com/containers/podman/v2/cmd/podman/validate"
+ "github.com/containers/podman/v2/libpod/network"
"github.com/containers/podman/v2/pkg/domain/entities"
- "github.com/containers/podman/v2/pkg/network"
"github.com/spf13/cobra"
"github.com/spf13/pflag"
)
diff --git a/cmd/podman/pods/inspect.go b/cmd/podman/pods/inspect.go
index bc20352b0..cad15d10f 100644
--- a/cmd/podman/pods/inspect.go
+++ b/cmd/podman/pods/inspect.go
@@ -3,9 +3,13 @@ package pods
import (
"context"
"fmt"
+ "os"
+ "text/tabwriter"
+ "text/template"
- "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/pkg/errors"
@@ -57,11 +61,19 @@ func inspect(cmd *cobra.Command, args []string) error {
if err != nil {
return err
}
- var data interface{} = responses
- var out formats.Writer = formats.JSONStruct{Output: data}
- if inspectOptions.Format != "json" {
- out = formats.StdoutTemplate{Output: data, Template: inspectOptions.Format}
+
+ if parse.MatchesJSONFormat(inspectOptions.Format) {
+ enc := json.NewEncoder(os.Stdout)
+ return enc.Encode(responses)
+ }
+
+ row := report.NormalizeFormat(inspectOptions.Format)
+
+ t, err := template.New("pod inspect").Parse(row)
+ if err != nil {
+ return err
}
- return out.Out()
+ w := tabwriter.NewWriter(os.Stdout, 8, 2, 2, ' ', 0)
+ return t.Execute(w, *responses)
}
diff --git a/docs/source/markdown/podman-search.1.md b/docs/source/markdown/podman-search.1.md
index 75bfeb058..2c2a8f012 100644
--- a/docs/source/markdown/podman-search.1.md
+++ b/docs/source/markdown/podman-search.1.md
@@ -27,7 +27,7 @@ Note, searching without a search term will only work for registries that impleme
**--authfile**=*path*
-Path of the authentication file. Default is ${XDG\_RUNTIME\_DIR}/containers/auth.json (Not available for remote commands)
+Path of the authentication file. Default is ${XDG\_RUNTIME\_DIR}/containers/auth.json
Note: You can also override the default path of the authentication file by setting the REGISTRY\_AUTH\_FILE
environment variable. `export REGISTRY_AUTH_FILE=path`
@@ -74,7 +74,7 @@ Do not truncate the output
Require HTTPS and verify certificates when contacting registries (default: true). If explicitly set to true,
then TLS verification will be used. If set to false, then TLS verification will not be used if needed. If not specified,
default registries will be searched through (in /etc/containers/registries.conf), and TLS will be skipped if a default
-registry is listed in the insecure registries. (Not available for remote commands)
+registry is listed in the insecure registries.
**--help**, **-h**
diff --git a/libpod/container.go b/libpod/container.go
index 9b4ccbd5f..01419500e 100644
--- a/libpod/container.go
+++ b/libpod/container.go
@@ -888,9 +888,22 @@ func (c *Container) NamespacePath(linuxNS LinuxNS) (string, error) { //nolint:in
return fmt.Sprintf("/proc/%d/ns/%s", c.state.PID, linuxNS.String()), nil
}
+// CgroupManager returns the cgroup manager used by the given container.
+func (c *Container) CgroupManager() string {
+ cgroupManager := c.config.CgroupManager
+ if cgroupManager == "" {
+ cgroupManager = c.runtime.config.Engine.CgroupManager
+ }
+ return cgroupManager
+}
+
// CGroupPath returns a cgroups "path" for a given container.
func (c *Container) CGroupPath() (string, error) {
+ cgroupManager := c.CgroupManager()
+
switch {
+ case c.config.NoCgroups || c.config.CgroupsMode == "disabled":
+ return "", errors.Wrapf(define.ErrNoCgroups, "this container is not creating cgroups")
case c.config.CgroupsMode == cgroupSplit:
if c.config.CgroupParent != "" {
return "", errors.Errorf("cannot specify cgroup-parent with cgroup-mode %q", cgroupSplit)
@@ -906,9 +919,9 @@ func (c *Container) CGroupPath() (string, error) {
return "", errors.Errorf("invalid cgroup for conmon %q", cg)
}
return strings.TrimSuffix(cg, "/supervisor") + "/container", nil
- case c.runtime.config.Engine.CgroupManager == config.CgroupfsCgroupsManager:
+ case cgroupManager == config.CgroupfsCgroupsManager:
return filepath.Join(c.config.CgroupParent, fmt.Sprintf("libpod-%s", c.ID())), nil
- case c.runtime.config.Engine.CgroupManager == config.SystemdCgroupsManager:
+ case cgroupManager == config.SystemdCgroupsManager:
if rootless.IsRootless() {
uid := rootless.GetRootlessUID()
parts := strings.SplitN(c.config.CgroupParent, "/", 2)
@@ -922,7 +935,7 @@ func (c *Container) CGroupPath() (string, error) {
}
return filepath.Join(c.config.CgroupParent, createUnitName("libpod", c.ID())), nil
default:
- return "", errors.Wrapf(define.ErrInvalidArg, "unsupported CGroup manager %s in use", c.runtime.config.Engine.CgroupManager)
+ return "", errors.Wrapf(define.ErrInvalidArg, "unsupported CGroup manager %s in use", cgroupManager)
}
}
diff --git a/libpod/container_config.go b/libpod/container_config.go
index fc93140dd..e264da4da 100644
--- a/libpod/container_config.go
+++ b/libpod/container_config.go
@@ -275,13 +275,16 @@ type ContainerMiscConfig struct {
StopTimeout uint `json:"stopTimeout,omitempty"`
// Time container was created
CreatedTime time.Time `json:"createdTime"`
+ // CgroupManager is the cgroup manager used to create this container.
+ // If empty, the runtime default will be used.
+ CgroupManager string `json:"cgroupManager,omitempty"`
// NoCgroups indicates that the container will not create CGroups. It is
// incompatible with CgroupParent. Deprecated in favor of CgroupsMode.
NoCgroups bool `json:"noCgroups,omitempty"`
// CgroupsMode indicates how the container will create cgroups
// (disabled, no-conmon, enabled). It supersedes NoCgroups.
CgroupsMode string `json:"cgroupsMode,omitempty"`
- // Cgroup parent of the container
+ // Cgroup parent of the container.
CgroupParent string `json:"cgroupParent"`
// LogPath log location
LogPath string `json:"logPath"`
diff --git a/libpod/container_inspect.go b/libpod/container_inspect.go
index 835dccd71..b8bce1272 100644
--- a/libpod/container_inspect.go
+++ b/libpod/container_inspect.go
@@ -729,7 +729,7 @@ func (c *Container) generateInspectContainerHostConfig(ctrSpec *spec.Spec, named
// CGroup parent
// Need to check if it's the default, and not print if so.
defaultCgroupParent := ""
- switch c.runtime.config.Engine.CgroupManager {
+ switch c.CgroupManager() {
case config.CgroupfsCgroupsManager:
defaultCgroupParent = CgroupfsDefaultCgroupParent
case config.SystemdCgroupsManager:
@@ -738,6 +738,7 @@ func (c *Container) generateInspectContainerHostConfig(ctrSpec *spec.Spec, named
if c.config.CgroupParent != defaultCgroupParent {
hostConfig.CgroupParent = c.config.CgroupParent
}
+ hostConfig.CgroupManager = c.CgroupManager()
// PID namespace mode
pidMode := ""
diff --git a/libpod/container_internal.go b/libpod/container_internal.go
index d64d3ab87..4ae571de6 100644
--- a/libpod/container_internal.go
+++ b/libpod/container_internal.go
@@ -976,6 +976,21 @@ func (c *Container) completeNetworkSetup() error {
}
}
}
+ // check if we have a bindmount for /etc/hosts
+ if hostsBindMount, ok := state.BindMounts["/etc/hosts"]; ok && len(c.cniHosts()) > 0 {
+ ctrHostPath := filepath.Join(c.state.RunDir, "hosts")
+ if hostsBindMount == ctrHostPath {
+ // read the existing hosts
+ b, err := ioutil.ReadFile(hostsBindMount)
+ if err != nil {
+ return err
+ }
+ if err := ioutil.WriteFile(hostsBindMount, append(b, []byte(c.cniHosts())...), 0644); err != nil {
+ return err
+ }
+ }
+ }
+
// check if we have a bindmount for resolv.conf
resolvBindMount := state.BindMounts["/etc/resolv.conf"]
if len(outResolvConf) < 1 || resolvBindMount == "" || len(c.config.NetNsCtr) > 0 {
@@ -997,6 +1012,15 @@ func (c *Container) completeNetworkSetup() error {
return ioutil.WriteFile(resolvBindMount, []byte(strings.Join(outResolvConf, "\n")), 0644)
}
+func (c *Container) cniHosts() string {
+ var hosts string
+ if len(c.state.NetworkStatus) > 0 && len(c.state.NetworkStatus[0].IPs) > 0 {
+ ipAddress := strings.Split(c.state.NetworkStatus[0].IPs[0].Address.String(), "/")[0]
+ hosts += fmt.Sprintf("%s\t%s %s\n", ipAddress, c.Hostname(), c.Config().Name)
+ }
+ return hosts
+}
+
// Initialize a container, creating it in the runtime
func (c *Container) init(ctx context.Context, retainRetries bool) error {
span, _ := opentracing.StartSpanFromContext(ctx, "init")
diff --git a/libpod/container_internal_linux.go b/libpod/container_internal_linux.go
index 894982973..3a71c6601 100644
--- a/libpod/container_internal_linux.go
+++ b/libpod/container_internal_linux.go
@@ -1543,10 +1543,7 @@ func (c *Container) getHosts() string {
// When using slirp4netns, the interface gets a static IP
hosts += fmt.Sprintf("# used by slirp4netns\n%s\t%s %s\n", "10.0.2.100", c.Hostname(), c.Config().Name)
}
- if len(c.state.NetworkStatus) > 0 && len(c.state.NetworkStatus[0].IPs) > 0 {
- ipAddress := strings.Split(c.state.NetworkStatus[0].IPs[0].Address.String(), "/")[0]
- hosts += fmt.Sprintf("%s\t%s %s\n", ipAddress, c.Hostname(), c.Config().Name)
- }
+ hosts += c.cniHosts()
return hosts
}
@@ -1968,6 +1965,7 @@ func (c *Container) getOCICgroupPath() (string, error) {
if err != nil {
return "", err
}
+ cgroupManager := c.CgroupManager()
switch {
case (rootless.IsRootless() && !unified) || c.config.NoCgroups:
return "", nil
@@ -1980,14 +1978,14 @@ func (c *Container) getOCICgroupPath() (string, error) {
return "", err
}
return filepath.Join(selfCgroup, "container"), nil
- case c.runtime.config.Engine.CgroupManager == config.SystemdCgroupsManager:
+ case cgroupManager == config.SystemdCgroupsManager:
// When the OCI runtime is set to use Systemd as a cgroup manager, it
// expects cgroups to be passed as follows:
// slice:prefix:name
systemdCgroups := fmt.Sprintf("%s:libpod:%s", path.Base(c.config.CgroupParent), c.ID())
logrus.Debugf("Setting CGroups for container %s to %s", c.ID(), systemdCgroups)
return systemdCgroups, nil
- case c.runtime.config.Engine.CgroupManager == config.CgroupfsCgroupsManager:
+ case cgroupManager == config.CgroupfsCgroupsManager:
cgroupPath, err := c.CGroupPath()
if err != nil {
return "", err
@@ -1995,7 +1993,7 @@ func (c *Container) getOCICgroupPath() (string, error) {
logrus.Debugf("Setting CGroup path for container %s to %s", c.ID(), cgroupPath)
return cgroupPath, nil
default:
- return "", errors.Wrapf(define.ErrInvalidArg, "invalid cgroup manager %s requested", c.runtime.config.Engine.CgroupManager)
+ return "", errors.Wrapf(define.ErrInvalidArg, "invalid cgroup manager %s requested", cgroupManager)
}
}
diff --git a/libpod/define/container_inspect.go b/libpod/define/container_inspect.go
index 44c3d515b..38b3a6686 100644
--- a/libpod/define/container_inspect.go
+++ b/libpod/define/container_inspect.go
@@ -236,6 +236,9 @@ type InspectContainerHostConfig struct {
// include a Mounts field in inspect.
// Format: <src>:<destination>[:<comma-separated options>]
Binds []string `json:"Binds"`
+ // CgroupManager is the cgroup manager used by the container.
+ // At present, allowed values are either "cgroupfs" or "systemd".
+ CgroupManager string `json:"CgroupManager,omitempty"`
// CgroupMode is the configuration of the container's cgroup namespace.
// Populated as follows:
// private - a cgroup namespace has been created
diff --git a/libpod/image/docker_registry_options.go b/libpod/image/docker_registry_options.go
index 257b7ae8d..835473a1f 100644
--- a/libpod/image/docker_registry_options.go
+++ b/libpod/image/docker_registry_options.go
@@ -55,6 +55,7 @@ func (o DockerRegistryOptions) GetSystemContext(parent *types.SystemContext, add
sc.DockerRegistryUserAgent = parent.DockerRegistryUserAgent
sc.OSChoice = parent.OSChoice
sc.ArchitectureChoice = parent.ArchitectureChoice
+ sc.BlobInfoCacheDir = parent.BlobInfoCacheDir
}
return sc
}
diff --git a/pkg/network/config.go b/libpod/network/config.go
index 0115433e1..a08e684d8 100644
--- a/pkg/network/config.go
+++ b/libpod/network/config.go
@@ -3,6 +3,8 @@ package network
import (
"encoding/json"
"net"
+
+ "github.com/containers/storage/pkg/lockfile"
)
// TODO once the containers.conf file stuff is worked out, this should be modified
@@ -17,8 +19,17 @@ const (
// DefaultPodmanDomainName is used for the dnsname plugin to define
// a localized domain name for a created network
DefaultPodmanDomainName = "dns.podman"
+ // LockFileName is used for obtaining a lock and is appended
+ // to libpod's tmpdir in practice
+ LockFileName = "cni.lock"
)
+// CNILock is for preventing name collision and
+// unpredictable results when doing some CNI operations.
+type CNILock struct {
+ lockfile.Locker
+}
+
// GetDefaultPodmanNetwork outputs the default network for podman
func GetDefaultPodmanNetwork() (*net.IPNet, error) {
_, n, err := net.ParseCIDR("10.88.1.0/24")
diff --git a/libpod/network/create.go b/libpod/network/create.go
new file mode 100644
index 000000000..a9ed4c4ef
--- /dev/null
+++ b/libpod/network/create.go
@@ -0,0 +1,195 @@
+package network
+
+import (
+ "encoding/json"
+ "fmt"
+ "io/ioutil"
+ "os"
+ "path/filepath"
+
+ "github.com/containernetworking/cni/pkg/version"
+ "github.com/containers/podman/v2/libpod"
+ "github.com/containers/podman/v2/pkg/domain/entities"
+ "github.com/containers/podman/v2/pkg/util"
+ "github.com/pkg/errors"
+)
+
+func Create(name string, options entities.NetworkCreateOptions, r *libpod.Runtime) (*entities.NetworkCreateReport, error) {
+ var fileName string
+ if err := isSupportedDriver(options.Driver); err != nil {
+ return nil, err
+ }
+ config, err := r.GetConfig()
+ if err != nil {
+ return nil, err
+ }
+ // Acquire a lock for CNI
+ l, err := acquireCNILock(filepath.Join(config.Engine.TmpDir, LockFileName))
+ if err != nil {
+ return nil, err
+ }
+ defer l.releaseCNILock()
+ if len(options.MacVLAN) > 0 {
+ fileName, err = createMacVLAN(r, name, options)
+ } else {
+ fileName, err = createBridge(r, name, options)
+ }
+ if err != nil {
+ return nil, err
+ }
+ return &entities.NetworkCreateReport{Filename: fileName}, nil
+}
+
+// createBridge creates a CNI network
+func createBridge(r *libpod.Runtime, name string, options entities.NetworkCreateOptions) (string, error) {
+ isGateway := true
+ ipMasq := true
+ subnet := &options.Subnet
+ ipRange := options.Range
+ runtimeConfig, err := r.GetConfig()
+ if err != nil {
+ return "", err
+ }
+ // if range is provided, make sure it is "in" network
+ if subnet.IP != nil {
+ // if network is provided, does it conflict with existing CNI or live networks
+ err = ValidateUserNetworkIsAvailable(runtimeConfig, subnet)
+ } else {
+ // if no network is provided, figure out network
+ subnet, err = GetFreeNetwork(runtimeConfig)
+ }
+ if err != nil {
+ return "", err
+ }
+ gateway := options.Gateway
+ if gateway == nil {
+ // if no gateway is provided, provide it as first ip of network
+ gateway = CalcGatewayIP(subnet)
+ }
+ // if network is provided and if gateway is provided, make sure it is "in" network
+ if options.Subnet.IP != nil && options.Gateway != nil {
+ if !subnet.Contains(gateway) {
+ return "", errors.Errorf("gateway %s is not in valid for subnet %s", gateway.String(), subnet.String())
+ }
+ }
+ if options.Internal {
+ isGateway = false
+ ipMasq = false
+ }
+
+ // if a range is given, we need to ensure it is "in" the network range.
+ if options.Range.IP != nil {
+ if options.Subnet.IP == nil {
+ return "", errors.New("you must define a subnet range to define an ip-range")
+ }
+ firstIP, err := FirstIPInSubnet(&options.Range)
+ if err != nil {
+ return "", err
+ }
+ lastIP, err := LastIPInSubnet(&options.Range)
+ if err != nil {
+ return "", err
+ }
+ if !subnet.Contains(firstIP) || !subnet.Contains(lastIP) {
+ return "", errors.Errorf("the ip range %s does not fall within the subnet range %s", options.Range.String(), subnet.String())
+ }
+ }
+ bridgeDeviceName, err := GetFreeDeviceName(runtimeConfig)
+ if err != nil {
+ return "", err
+ }
+
+ if len(name) > 0 {
+ netNames, err := GetNetworkNamesFromFileSystem(runtimeConfig)
+ if err != nil {
+ return "", err
+ }
+ if util.StringInSlice(name, netNames) {
+ return "", errors.Errorf("the network name %s is already used", name)
+ }
+ } else {
+ // If no name is given, we give the name of the bridge device
+ name = bridgeDeviceName
+ }
+
+ ncList := NewNcList(name, version.Current())
+ var plugins []CNIPlugins
+ var routes []IPAMRoute
+
+ defaultRoute, err := NewIPAMDefaultRoute(IsIPv6(subnet.IP))
+ if err != nil {
+ return "", err
+ }
+ routes = append(routes, defaultRoute)
+ ipamConfig, err := NewIPAMHostLocalConf(subnet, routes, ipRange, gateway)
+ if err != nil {
+ return "", err
+ }
+
+ // TODO need to iron out the role of isDefaultGW and IPMasq
+ bridge := NewHostLocalBridge(bridgeDeviceName, isGateway, false, ipMasq, ipamConfig)
+ plugins = append(plugins, bridge)
+ plugins = append(plugins, NewPortMapPlugin())
+ plugins = append(plugins, NewFirewallPlugin())
+ // if we find the dnsname plugin, we add configuration for it
+ if HasDNSNamePlugin(runtimeConfig.Network.CNIPluginDirs) && !options.DisableDNS {
+ // Note: in the future we might like to allow for dynamic domain names
+ plugins = append(plugins, NewDNSNamePlugin(DefaultPodmanDomainName))
+ }
+ ncList["plugins"] = plugins
+ b, err := json.MarshalIndent(ncList, "", " ")
+ if err != nil {
+ return "", err
+ }
+ if err := os.MkdirAll(GetCNIConfDir(runtimeConfig), 0755); err != nil {
+ return "", err
+ }
+ cniPathName := filepath.Join(GetCNIConfDir(runtimeConfig), fmt.Sprintf("%s.conflist", name))
+ err = ioutil.WriteFile(cniPathName, b, 0644)
+ return cniPathName, err
+}
+
+func createMacVLAN(r *libpod.Runtime, name string, options entities.NetworkCreateOptions) (string, error) {
+ var (
+ plugins []CNIPlugins
+ )
+ liveNetNames, err := GetLiveNetworkNames()
+ if err != nil {
+ return "", err
+ }
+
+ config, err := r.GetConfig()
+ if err != nil {
+ return "", err
+ }
+
+ // Make sure the host-device exists
+ if !util.StringInSlice(options.MacVLAN, liveNetNames) {
+ return "", errors.Errorf("failed to find network interface %q", options.MacVLAN)
+ }
+ if len(name) > 0 {
+ netNames, err := GetNetworkNamesFromFileSystem(config)
+ if err != nil {
+ return "", err
+ }
+ if util.StringInSlice(name, netNames) {
+ return "", errors.Errorf("the network name %s is already used", name)
+ }
+ } else {
+ name, err = GetFreeDeviceName(config)
+ if err != nil {
+ return "", err
+ }
+ }
+ ncList := NewNcList(name, version.Current())
+ macvlan := NewMacVLANPlugin(options.MacVLAN)
+ plugins = append(plugins, macvlan)
+ ncList["plugins"] = plugins
+ b, err := json.MarshalIndent(ncList, "", " ")
+ if err != nil {
+ return "", err
+ }
+ cniPathName := filepath.Join(GetCNIConfDir(config), fmt.Sprintf("%s.conflist", name))
+ err = ioutil.WriteFile(cniPathName, b, 0644)
+ return cniPathName, err
+}
diff --git a/pkg/network/devices.go b/libpod/network/devices.go
index a5d23fae4..a5d23fae4 100644
--- a/pkg/network/devices.go
+++ b/libpod/network/devices.go
diff --git a/pkg/network/files.go b/libpod/network/files.go
index a2090491f..a2090491f 100644
--- a/pkg/network/files.go
+++ b/libpod/network/files.go
diff --git a/pkg/network/ip.go b/libpod/network/ip.go
index ba93a0d05..ba93a0d05 100644
--- a/pkg/network/ip.go
+++ b/libpod/network/ip.go
diff --git a/libpod/network/lock.go b/libpod/network/lock.go
new file mode 100644
index 000000000..0395359eb
--- /dev/null
+++ b/libpod/network/lock.go
@@ -0,0 +1,26 @@
+package network
+
+import (
+ "github.com/containers/storage"
+)
+
+// acquireCNILock gets a lock that should be used in create and
+// delete cases to avoid unwanted collisions in network names.
+// TODO this uses a file lock and should be converted to shared memory
+// when we have a more general shared memory lock in libpod
+func acquireCNILock(lockPath string) (*CNILock, error) {
+ l, err := storage.GetLockfile(lockPath)
+ if err != nil {
+ return nil, err
+ }
+ l.Lock()
+ cnilock := CNILock{
+ Locker: l,
+ }
+ return &cnilock, nil
+}
+
+// ReleaseCNILock unlocks the previously held lock
+func (l *CNILock) releaseCNILock() {
+ l.Unlock()
+}
diff --git a/pkg/network/netconflist.go b/libpod/network/netconflist.go
index 8187fdb39..8187fdb39 100644
--- a/pkg/network/netconflist.go
+++ b/libpod/network/netconflist.go
diff --git a/pkg/network/netconflist_test.go b/libpod/network/netconflist_test.go
index 5893bf985..5893bf985 100644
--- a/pkg/network/netconflist_test.go
+++ b/libpod/network/netconflist_test.go
diff --git a/pkg/network/network.go b/libpod/network/network.go
index c4c1ff67f..7327a1a7d 100644
--- a/pkg/network/network.go
+++ b/libpod/network/network.go
@@ -4,6 +4,7 @@ import (
"encoding/json"
"net"
"os"
+ "path/filepath"
"github.com/containernetworking/cni/pkg/types"
"github.com/containernetworking/plugins/plugins/ipam/host-local/backend/allocator"
@@ -20,8 +21,8 @@ var DefaultNetworkDriver = "bridge"
// SupportedNetworkDrivers describes the list of supported drivers
var SupportedNetworkDrivers = []string{DefaultNetworkDriver}
-// IsSupportedDriver checks if the user provided driver is supported
-func IsSupportedDriver(driver string) error {
+// isSupportedDriver checks if the user provided driver is supported
+func isSupportedDriver(driver string) error {
if util.StringInSlice(driver, SupportedNetworkDrivers) {
return nil
}
@@ -168,6 +169,11 @@ func ValidateUserNetworkIsAvailable(config *config.Config, userNet *net.IPNet) e
// RemoveNetwork removes a given network by name. If the network has container associated with it, that
// must be handled outside the context of this.
func RemoveNetwork(config *config.Config, name string) error {
+ l, err := acquireCNILock(filepath.Join(config.Engine.TmpDir, LockFileName))
+ if err != nil {
+ return err
+ }
+ defer l.releaseCNILock()
cniPath, err := GetCNIConfigPathByName(config, name)
if err != nil {
return err
diff --git a/pkg/network/network_test.go b/libpod/network/network_test.go
index 1969e792c..1969e792c 100644
--- a/pkg/network/network_test.go
+++ b/libpod/network/network_test.go
diff --git a/pkg/network/subnet.go b/libpod/network/subnet.go
index 90f0cdfce..90f0cdfce 100644
--- a/pkg/network/subnet.go
+++ b/libpod/network/subnet.go
diff --git a/pkg/network/subnet_test.go b/libpod/network/subnet_test.go
index 917c3be88..917c3be88 100644
--- a/pkg/network/subnet_test.go
+++ b/libpod/network/subnet_test.go
diff --git a/libpod/oci_conmon_linux.go b/libpod/oci_conmon_linux.go
index 7fb374e0d..94630e57b 100644
--- a/libpod/oci_conmon_linux.go
+++ b/libpod/oci_conmon_linux.go
@@ -57,7 +57,6 @@ type ConmonOCIRuntime struct {
path string
conmonPath string
conmonEnv []string
- cgroupManager string
tmpDir string
exitsDir string
socketsDir string
@@ -102,7 +101,6 @@ func newConmonOCIRuntime(name string, paths []string, conmonPath string, runtime
runtime.runtimeFlags = runtimeFlags
runtime.conmonEnv = runtimeCfg.Engine.ConmonEnvVars
- runtime.cgroupManager = runtimeCfg.Engine.CgroupManager
runtime.tmpDir = runtimeCfg.Engine.TmpDir
runtime.logSizeMax = runtimeCfg.Containers.LogSizeMax
runtime.noPivot = runtimeCfg.Engine.NoPivotRoot
@@ -149,10 +147,6 @@ func newConmonOCIRuntime(name string, paths []string, conmonPath string, runtime
runtime.exitsDir = filepath.Join(runtime.tmpDir, "exits")
runtime.socketsDir = filepath.Join(runtime.tmpDir, "socket")
- if runtime.cgroupManager != config.CgroupfsCgroupsManager && runtime.cgroupManager != config.SystemdCgroupsManager {
- return nil, errors.Wrapf(define.ErrInvalidArg, "invalid cgroup manager specified: %s", runtime.cgroupManager)
- }
-
// Create the exit files and attach sockets directories
if err := os.MkdirAll(runtime.exitsDir, 0750); err != nil {
// The directory is allowed to exist
@@ -1325,7 +1319,7 @@ func (r *ConmonOCIRuntime) sharedConmonArgs(ctr *Container, cuuid, bundlePath, p
args = append(args, rFlags...)
}
- if r.cgroupManager == config.SystemdCgroupsManager && !ctr.config.NoCgroups && ctr.config.CgroupsMode != cgroupSplit {
+ if ctr.CgroupManager() == config.SystemdCgroupsManager && !ctr.config.NoCgroups && ctr.config.CgroupsMode != cgroupSplit {
args = append(args, "-s")
}
@@ -1442,8 +1436,10 @@ func (r *ConmonOCIRuntime) moveConmonToCgroupAndSignal(ctr *Container, cmd *exec
}
if mustCreateCgroup {
+ // TODO: This should be a switch - we are not guaranteed that
+ // there are only 2 valid cgroup managers
cgroupParent := ctr.CgroupParent()
- if r.cgroupManager == config.SystemdCgroupsManager {
+ if ctr.CgroupManager() == config.SystemdCgroupsManager {
unitName := createUnitName("libpod-conmon", ctr.ID())
realCgroupParent := cgroupParent
diff --git a/libpod/pod_api.go b/libpod/pod_api.go
index 0ae180356..f2ddba9c9 100644
--- a/libpod/pod_api.go
+++ b/libpod/pod_api.go
@@ -6,6 +6,7 @@ import (
"github.com/containers/podman/v2/libpod/define"
"github.com/containers/podman/v2/libpod/events"
"github.com/containers/podman/v2/pkg/cgroups"
+ "github.com/containers/podman/v2/pkg/parallel"
"github.com/containers/podman/v2/pkg/rootless"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
@@ -99,47 +100,52 @@ func (p *Pod) StopWithTimeout(ctx context.Context, cleanup bool, timeout int) (m
return nil, err
}
- ctrErrors := make(map[string]error)
-
// TODO: There may be cases where it makes sense to order stops based on
// dependencies. Should we bother with this?
- // Stop to all containers
- for _, ctr := range allCtrs {
- ctr.lock.Lock()
+ ctrErrChan := make(map[string]<-chan error)
- if err := ctr.syncContainer(); err != nil {
- ctr.lock.Unlock()
- ctrErrors[ctr.ID()] = err
- continue
- }
-
- // Ignore containers that are not running
- if ctr.state.State != define.ContainerStateRunning {
- ctr.lock.Unlock()
- continue
- }
- stopTimeout := ctr.config.StopTimeout
- if timeout > -1 {
- stopTimeout = uint(timeout)
- }
- if err := ctr.stop(stopTimeout); err != nil {
- ctr.lock.Unlock()
- ctrErrors[ctr.ID()] = err
- continue
- }
+ // Enqueue a function for each container with the parallel executor.
+ for _, ctr := range allCtrs {
+ c := ctr
+ logrus.Debugf("Adding parallel job to stop container %s", c.ID())
+ retChan := parallel.Enqueue(ctx, func() error {
+ // TODO: Might be better to batch stop and cleanup
+ // together?
+ if timeout > -1 {
+ if err := c.StopWithTimeout(uint(timeout)); err != nil {
+ return err
+ }
+ } else {
+ if err := c.Stop(); err != nil {
+ return err
+ }
+ }
- if cleanup {
- if err := ctr.cleanup(ctx); err != nil {
- ctrErrors[ctr.ID()] = err
+ if cleanup {
+ return c.Cleanup(ctx)
}
- }
- ctr.lock.Unlock()
+ return nil
+ })
+
+ ctrErrChan[c.ID()] = retChan
}
p.newPodEvent(events.Stop)
+ ctrErrors := make(map[string]error)
+
+ // Get returned error for every container we worked on
+ for id, channel := range ctrErrChan {
+ if err := <-channel; err != nil {
+ if errors.Cause(err) == define.ErrCtrStateInvalid || errors.Cause(err) == define.ErrCtrStopped {
+ continue
+ }
+ ctrErrors[id] = err
+ }
+ }
+
if len(ctrErrors) > 0 {
return ctrErrors, errors.Wrapf(define.ErrPodPartialFail, "error stopping some containers")
}
@@ -169,45 +175,29 @@ func (p *Pod) Cleanup(ctx context.Context) (map[string]error, error) {
return nil, err
}
- ctrErrors := make(map[string]error)
+ ctrErrChan := make(map[string]<-chan error)
- // Clean up all containers
+ // Enqueue a function for each container with the parallel executor.
for _, ctr := range allCtrs {
- ctr.lock.Lock()
-
- if err := ctr.syncContainer(); err != nil {
- ctr.lock.Unlock()
- ctrErrors[ctr.ID()] = err
- continue
- }
-
- // Ignore containers that are running/paused
- if !ctr.ensureState(define.ContainerStateConfigured, define.ContainerStateCreated, define.ContainerStateStopped, define.ContainerStateExited) {
- ctr.lock.Unlock()
- continue
- }
-
- // Check for running exec sessions, ignore containers with them.
- sessions, err := ctr.getActiveExecSessions()
- if err != nil {
- ctr.lock.Unlock()
- ctrErrors[ctr.ID()] = err
- continue
- }
- if len(sessions) > 0 {
- ctr.lock.Unlock()
- continue
- }
+ c := ctr
+ logrus.Debugf("Adding parallel job to clean up container %s", c.ID())
+ retChan := parallel.Enqueue(ctx, func() error {
+ return c.Cleanup(ctx)
+ })
- // TODO: Should we handle restart policy here?
+ ctrErrChan[c.ID()] = retChan
+ }
- ctr.newContainerEvent(events.Cleanup)
+ ctrErrors := make(map[string]error)
- if err := ctr.cleanup(ctx); err != nil {
- ctrErrors[ctr.ID()] = err
+ // Get returned error for every container we worked on
+ for id, channel := range ctrErrChan {
+ if err := <-channel; err != nil {
+ if errors.Cause(err) == define.ErrCtrStateInvalid || errors.Cause(err) == define.ErrCtrStopped {
+ continue
+ }
+ ctrErrors[id] = err
}
-
- ctr.lock.Unlock()
}
if len(ctrErrors) > 0 {
@@ -229,7 +219,7 @@ func (p *Pod) Cleanup(ctx context.Context) (map[string]error, error) {
// containers. The container ID is mapped to the error encountered. The error is
// set to ErrPodPartialFail.
// If both error and the map are nil, all containers were paused without error
-func (p *Pod) Pause() (map[string]error, error) {
+func (p *Pod) Pause(ctx context.Context) (map[string]error, error) {
p.lock.Lock()
defer p.lock.Unlock()
@@ -252,37 +242,34 @@ func (p *Pod) Pause() (map[string]error, error) {
return nil, err
}
- ctrErrors := make(map[string]error)
+ ctrErrChan := make(map[string]<-chan error)
- // Pause to all containers
+ // Enqueue a function for each container with the parallel executor.
for _, ctr := range allCtrs {
- ctr.lock.Lock()
+ c := ctr
+ logrus.Debugf("Adding parallel job to pause container %s", c.ID())
+ retChan := parallel.Enqueue(ctx, c.Pause)
- if err := ctr.syncContainer(); err != nil {
- ctr.lock.Unlock()
- ctrErrors[ctr.ID()] = err
- continue
- }
+ ctrErrChan[c.ID()] = retChan
+ }
- // Ignore containers that are not running
- if ctr.state.State != define.ContainerStateRunning {
- ctr.lock.Unlock()
- continue
- }
+ p.newPodEvent(events.Pause)
- if err := ctr.pause(); err != nil {
- ctr.lock.Unlock()
- ctrErrors[ctr.ID()] = err
- continue
- }
+ ctrErrors := make(map[string]error)
- ctr.lock.Unlock()
+ // Get returned error for every container we worked on
+ for id, channel := range ctrErrChan {
+ if err := <-channel; err != nil {
+ if errors.Cause(err) == define.ErrCtrStateInvalid || errors.Cause(err) == define.ErrCtrStopped {
+ continue
+ }
+ ctrErrors[id] = err
+ }
}
if len(ctrErrors) > 0 {
return ctrErrors, errors.Wrapf(define.ErrPodPartialFail, "error pausing some containers")
}
- defer p.newPodEvent(events.Pause)
return nil, nil
}
@@ -298,7 +285,7 @@ func (p *Pod) Pause() (map[string]error, error) {
// containers. The container ID is mapped to the error encountered. The error is
// set to ErrPodPartialFail.
// If both error and the map are nil, all containers were unpaused without error.
-func (p *Pod) Unpause() (map[string]error, error) {
+func (p *Pod) Unpause(ctx context.Context) (map[string]error, error) {
p.lock.Lock()
defer p.lock.Unlock()
@@ -311,38 +298,34 @@ func (p *Pod) Unpause() (map[string]error, error) {
return nil, err
}
- ctrErrors := make(map[string]error)
+ ctrErrChan := make(map[string]<-chan error)
- // Pause to all containers
+ // Enqueue a function for each container with the parallel executor.
for _, ctr := range allCtrs {
- ctr.lock.Lock()
+ c := ctr
+ logrus.Debugf("Adding parallel job to unpause container %s", c.ID())
+ retChan := parallel.Enqueue(ctx, c.Unpause)
- if err := ctr.syncContainer(); err != nil {
- ctr.lock.Unlock()
- ctrErrors[ctr.ID()] = err
- continue
- }
+ ctrErrChan[c.ID()] = retChan
+ }
- // Ignore containers that are not paused
- if ctr.state.State != define.ContainerStatePaused {
- ctr.lock.Unlock()
- continue
- }
+ p.newPodEvent(events.Unpause)
- if err := ctr.unpause(); err != nil {
- ctr.lock.Unlock()
- ctrErrors[ctr.ID()] = err
- continue
- }
+ ctrErrors := make(map[string]error)
- ctr.lock.Unlock()
+ // Get returned error for every container we worked on
+ for id, channel := range ctrErrChan {
+ if err := <-channel; err != nil {
+ if errors.Cause(err) == define.ErrCtrStateInvalid || errors.Cause(err) == define.ErrCtrStopped {
+ continue
+ }
+ ctrErrors[id] = err
+ }
}
if len(ctrErrors) > 0 {
return ctrErrors, errors.Wrapf(define.ErrPodPartialFail, "error unpausing some containers")
}
-
- defer p.newPodEvent(events.Unpause)
return nil, nil
}
@@ -411,7 +394,7 @@ func (p *Pod) Restart(ctx context.Context) (map[string]error, error) {
// containers. The container ID is mapped to the error encountered. The error is
// set to ErrPodPartialFail.
// If both error and the map are nil, all containers were signalled successfully.
-func (p *Pod) Kill(signal uint) (map[string]error, error) {
+func (p *Pod) Kill(ctx context.Context, signal uint) (map[string]error, error) {
p.lock.Lock()
defer p.lock.Unlock()
@@ -424,44 +407,36 @@ func (p *Pod) Kill(signal uint) (map[string]error, error) {
return nil, err
}
- ctrErrors := make(map[string]error)
+ ctrErrChan := make(map[string]<-chan error)
- // Send a signal to all containers
+ // Enqueue a function for each container with the parallel executor.
for _, ctr := range allCtrs {
- ctr.lock.Lock()
-
- if err := ctr.syncContainer(); err != nil {
- ctr.lock.Unlock()
- ctrErrors[ctr.ID()] = err
- continue
- }
+ c := ctr
+ logrus.Debugf("Adding parallel job to kill container %s", c.ID())
+ retChan := parallel.Enqueue(ctx, func() error {
+ return c.Kill(signal)
+ })
- // Ignore containers that are not running
- if ctr.state.State != define.ContainerStateRunning {
- ctr.lock.Unlock()
- continue
- }
+ ctrErrChan[c.ID()] = retChan
+ }
- if err := ctr.ociRuntime.KillContainer(ctr, signal, false); err != nil {
- ctr.lock.Unlock()
- ctrErrors[ctr.ID()] = err
- continue
- }
+ p.newPodEvent(events.Kill)
- logrus.Debugf("Killed container %s with signal %d", ctr.ID(), signal)
+ ctrErrors := make(map[string]error)
- ctr.state.StoppedByUser = true
- if err := ctr.save(); err != nil {
- ctrErrors[ctr.ID()] = err
+ // Get returned error for every container we worked on
+ for id, channel := range ctrErrChan {
+ if err := <-channel; err != nil {
+ if errors.Cause(err) == define.ErrCtrStateInvalid || errors.Cause(err) == define.ErrCtrStopped {
+ continue
+ }
+ ctrErrors[id] = err
}
-
- ctr.lock.Unlock()
}
if len(ctrErrors) > 0 {
return ctrErrors, errors.Wrapf(define.ErrPodPartialFail, "error killing some containers")
}
- defer p.newPodEvent(events.Kill)
return nil, nil
}
diff --git a/libpod/runtime_ctr.go b/libpod/runtime_ctr.go
index abb97293f..51b4c5f03 100644
--- a/libpod/runtime_ctr.go
+++ b/libpod/runtime_ctr.go
@@ -208,6 +208,7 @@ func (r *Runtime) setupContainer(ctx context.Context, ctr *Container) (_ *Contai
// Check CGroup parent sanity, and set it if it was not set.
// Only if we're actually configuring CGroups.
if !ctr.config.NoCgroups {
+ ctr.config.CgroupManager = r.config.Engine.CgroupManager
switch r.config.Engine.CgroupManager {
case config.CgroupfsCgroupsManager:
if ctr.config.CgroupParent == "" {
diff --git a/pkg/api/handlers/compat/containers_create.go b/pkg/api/handlers/compat/containers_create.go
index a24dbaa47..4ce31cc83 100644
--- a/pkg/api/handlers/compat/containers_create.go
+++ b/pkg/api/handlers/compat/containers_create.go
@@ -14,8 +14,10 @@ import (
"github.com/containers/podman/v2/pkg/api/handlers"
"github.com/containers/podman/v2/pkg/api/handlers/utils"
"github.com/containers/podman/v2/pkg/namespaces"
+ "github.com/containers/podman/v2/pkg/rootless"
"github.com/containers/podman/v2/pkg/signal"
createconfig "github.com/containers/podman/v2/pkg/spec"
+ "github.com/containers/podman/v2/pkg/specgen"
"github.com/containers/storage"
"github.com/gorilla/schema"
"github.com/pkg/errors"
@@ -134,6 +136,11 @@ func makeCreateConfig(ctx context.Context, containerConfig *config.Config, input
Sysctl: input.HostConfig.Sysctls,
}
+ var netmode namespaces.NetworkMode
+ if rootless.IsRootless() {
+ netmode = namespaces.NetworkMode(specgen.Slirp)
+ }
+
network := createconfig.NetworkConfig{
DNSOpt: input.HostConfig.DNSOptions,
DNSSearch: input.HostConfig.DNSSearch,
@@ -144,7 +151,7 @@ func makeCreateConfig(ctx context.Context, containerConfig *config.Config, input
IPAddress: "",
LinkLocalIP: nil, // docker-only
MacAddress: input.MacAddress,
- // NetMode: nil,
+ NetMode: netmode,
Network: input.HostConfig.NetworkMode.NetworkName(),
NetworkAlias: nil, // docker-only now
PortBindings: input.HostConfig.PortBindings,
diff --git a/pkg/api/handlers/compat/images.go b/pkg/api/handlers/compat/images.go
index 9d8bc497a..f49ce59da 100644
--- a/pkg/api/handlers/compat/images.go
+++ b/pkg/api/handlers/compat/images.go
@@ -55,6 +55,7 @@ func ExportImage(w http.ResponseWriter, r *http.Request) {
utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrap(err, "unable to create tempfile"))
return
}
+ defer os.Remove(tmpfile.Name())
if err := tmpfile.Close(); err != nil {
utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrap(err, "unable to close tempfile"))
return
@@ -69,7 +70,6 @@ func ExportImage(w http.ResponseWriter, r *http.Request) {
return
}
defer rdr.Close()
- defer os.Remove(tmpfile.Name())
utils.WriteResponse(w, http.StatusOK, rdr)
}
@@ -398,3 +398,43 @@ func LoadImages(w http.ResponseWriter, r *http.Request) {
Stream: fmt.Sprintf("Loaded image: %s\n", id),
})
}
+
+func ExportImages(w http.ResponseWriter, r *http.Request) {
+ // 200 OK
+ // 500 Error
+ decoder := r.Context().Value("decoder").(*schema.Decoder)
+ runtime := r.Context().Value("runtime").(*libpod.Runtime)
+
+ query := struct {
+ Names string `schema:"names"`
+ }{
+ // This is where you can override the golang default value for one of fields
+ }
+ if err := decoder.Decode(&query, r.URL.Query()); err != nil {
+ utils.Error(w, "Something went wrong.", http.StatusBadRequest, errors.Wrapf(err, "failed to parse parameters for %s", r.URL.String()))
+ return
+ }
+ images := make([]string, 0)
+ images = append(images, strings.Split(query.Names, ",")...)
+ tmpfile, err := ioutil.TempFile("", "api.tar")
+ if err != nil {
+ utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrap(err, "unable to create tempfile"))
+ return
+ }
+ defer os.Remove(tmpfile.Name())
+ if err := tmpfile.Close(); err != nil {
+ utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrap(err, "unable to close tempfile"))
+ return
+ }
+ if err := runtime.ImageRuntime().SaveImages(r.Context(), images, "docker-archive", tmpfile.Name(), false); err != nil {
+ utils.InternalServerError(w, err)
+ return
+ }
+ rdr, err := os.Open(tmpfile.Name())
+ if err != nil {
+ utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrap(err, "failed to read the exported tarfile"))
+ return
+ }
+ defer rdr.Close()
+ utils.WriteResponse(w, http.StatusOK, rdr)
+}
diff --git a/pkg/api/handlers/compat/networks.go b/pkg/api/handlers/compat/networks.go
index c5387b1e9..a46784a6c 100644
--- a/pkg/api/handlers/compat/networks.go
+++ b/pkg/api/handlers/compat/networks.go
@@ -12,10 +12,10 @@ import (
"github.com/containernetworking/cni/libcni"
"github.com/containers/podman/v2/libpod"
"github.com/containers/podman/v2/libpod/define"
+ "github.com/containers/podman/v2/libpod/network"
"github.com/containers/podman/v2/pkg/api/handlers/utils"
"github.com/containers/podman/v2/pkg/domain/entities"
"github.com/containers/podman/v2/pkg/domain/infra/abi"
- "github.com/containers/podman/v2/pkg/network"
"github.com/docker/docker/api/types"
dockerNetwork "github.com/docker/docker/api/types/network"
"github.com/gorilla/schema"
@@ -210,6 +210,7 @@ func ListNetworks(w http.ResponseWriter, r *http.Request) {
report, err := getNetworkResourceByName(name, runtime)
if err != nil {
utils.InternalServerError(w, err)
+ return
}
reports = append(reports, report)
}
@@ -267,9 +268,9 @@ func CreateNetwork(w http.ResponseWriter, r *http.Request) {
}
}
ce := abi.ContainerEngine{Libpod: runtime}
- _, err := ce.NetworkCreate(r.Context(), name, ncOptions)
- if err != nil {
+ if _, err := ce.NetworkCreate(r.Context(), name, ncOptions); err != nil {
utils.InternalServerError(w, err)
+ return
}
report := types.NetworkCreate{
CheckDuplicate: networkCreate.CheckDuplicate,
@@ -307,6 +308,7 @@ func RemoveNetwork(w http.ResponseWriter, r *http.Request) {
}
if err := network.RemoveNetwork(config, name); err != nil {
utils.InternalServerError(w, err)
+ return
}
utils.WriteResponse(w, http.StatusNoContent, "")
}
diff --git a/pkg/api/handlers/libpod/pods.go b/pkg/api/handlers/libpod/pods.go
index 3aa554171..5422411cf 100644
--- a/pkg/api/handlers/libpod/pods.go
+++ b/pkg/api/handlers/libpod/pods.go
@@ -270,7 +270,7 @@ func PodPause(w http.ResponseWriter, r *http.Request) {
utils.PodNotFound(w, name, err)
return
}
- responses, err := pod.Pause()
+ responses, err := pod.Pause(r.Context())
if err != nil && errors.Cause(err) != define.ErrPodPartialFail {
utils.Error(w, "Something went wrong", http.StatusInternalServerError, err)
return
@@ -294,7 +294,7 @@ func PodUnpause(w http.ResponseWriter, r *http.Request) {
utils.PodNotFound(w, name, err)
return
}
- responses, err := pod.Unpause()
+ responses, err := pod.Unpause(r.Context())
if err != nil && errors.Cause(err) != define.ErrPodPartialFail {
utils.Error(w, "failed to pause pod", http.StatusInternalServerError, err)
return
@@ -402,7 +402,7 @@ func PodKill(w http.ResponseWriter, r *http.Request) {
return
}
- responses, err := pod.Kill(uint(sig))
+ responses, err := pod.Kill(r.Context(), uint(sig))
if err != nil && errors.Cause(err) != define.ErrPodPartialFail {
utils.Error(w, "failed to kill pod", http.StatusInternalServerError, err)
return
diff --git a/pkg/api/server/register_images.go b/pkg/api/server/register_images.go
index cb0d26d1e..ad779203d 100644
--- a/pkg/api/server/register_images.go
+++ b/pkg/api/server/register_images.go
@@ -275,6 +275,31 @@ func (s *APIServer) registerImagesHandlers(r *mux.Router) error {
r.Handle(VersionedPath("/images/{name:.*}/get"), s.APIHandler(compat.ExportImage)).Methods(http.MethodGet)
// Added non version path to URI to support docker non versioned paths
r.Handle("/images/{name:.*}/get", s.APIHandler(compat.ExportImage)).Methods(http.MethodGet)
+ // swagger:operation GET /images/get compat get
+ // ---
+ // tags:
+ // - images (compat)
+ // summary: Export several images
+ // description: Get a tarball containing all images and metadata for several image repositories
+ // parameters:
+ // - in: query
+ // name: names
+ // type: string
+ // required: true
+ // description: one or more image names or IDs comma separated
+ // produces:
+ // - application/json
+ // responses:
+ // 200:
+ // description: no error
+ // schema:
+ // type: string
+ // format: binary
+ // 500:
+ // $ref: '#/responses/InternalError'
+ r.Handle(VersionedPath("/images/get"), s.APIHandler(compat.ExportImages)).Methods(http.MethodGet)
+ // Added non version path to URI to support docker non versioned paths
+ r.Handle("/images/get", s.APIHandler(compat.ExportImages)).Methods(http.MethodGet)
// swagger:operation GET /images/{name:.*}/history compat imageHistory
// ---
// tags:
diff --git a/pkg/domain/infra/abi/containers.go b/pkg/domain/infra/abi/containers.go
index d92911e0c..0107e18c4 100644
--- a/pkg/domain/infra/abi/containers.go
+++ b/pkg/domain/infra/abi/containers.go
@@ -23,7 +23,7 @@ import (
"github.com/containers/podman/v2/pkg/checkpoint"
"github.com/containers/podman/v2/pkg/domain/entities"
"github.com/containers/podman/v2/pkg/domain/infra/abi/terminal"
- "github.com/containers/podman/v2/pkg/parallel"
+ parallelctr "github.com/containers/podman/v2/pkg/parallel/ctr"
"github.com/containers/podman/v2/pkg/ps"
"github.com/containers/podman/v2/pkg/rootless"
"github.com/containers/podman/v2/pkg/signal"
@@ -157,7 +157,7 @@ func (ic *ContainerEngine) ContainerStop(ctx context.Context, namesOrIds []strin
if err != nil && !(options.Ignore && errors.Cause(err) == define.ErrNoSuchCtr) {
return nil, err
}
- errMap, err := parallel.ContainerOp(ctx, ctrs, func(c *libpod.Container) error {
+ errMap, err := parallelctr.ContainerOp(ctx, ctrs, func(c *libpod.Container) error {
var err error
if options.Timeout != nil {
err = c.StopWithTimeout(*options.Timeout)
@@ -321,7 +321,7 @@ func (ic *ContainerEngine) ContainerRm(ctx context.Context, namesOrIds []string,
return reports, nil
}
- errMap, err := parallel.ContainerOp(ctx, ctrs, func(c *libpod.Container) error {
+ errMap, err := parallelctr.ContainerOp(ctx, ctrs, func(c *libpod.Container) error {
err := ic.Libpod.RemoveContainer(ctx, c, options.Force, options.Volumes)
if err != nil {
if options.Ignore && errors.Cause(err) == define.ErrNoSuchCtr {
diff --git a/pkg/domain/infra/abi/network.go b/pkg/domain/infra/abi/network.go
index 5acfea853..f40df828a 100644
--- a/pkg/domain/infra/abi/network.go
+++ b/pkg/domain/infra/abi/network.go
@@ -2,19 +2,13 @@ package abi
import (
"context"
- "encoding/json"
"fmt"
- "io/ioutil"
- "os"
- "path/filepath"
"strings"
"github.com/containernetworking/cni/libcni"
- cniversion "github.com/containernetworking/cni/pkg/version"
- "github.com/containers/podman/v2/libpod"
"github.com/containers/podman/v2/libpod/define"
+ "github.com/containers/podman/v2/libpod/network"
"github.com/containers/podman/v2/pkg/domain/entities"
- "github.com/containers/podman/v2/pkg/network"
"github.com/containers/podman/v2/pkg/util"
"github.com/pkg/errors"
)
@@ -111,173 +105,7 @@ func (ic *ContainerEngine) NetworkRm(ctx context.Context, namesOrIds []string, o
}
func (ic *ContainerEngine) NetworkCreate(ctx context.Context, name string, options entities.NetworkCreateOptions) (*entities.NetworkCreateReport, error) {
- var (
- err error
- fileName string
- )
- if len(options.MacVLAN) > 0 {
- fileName, err = createMacVLAN(ic.Libpod, name, options)
- } else {
- fileName, err = createBridge(ic.Libpod, name, options)
- }
- if err != nil {
- return nil, err
- }
- return &entities.NetworkCreateReport{Filename: fileName}, nil
-}
-
-// createBridge creates a CNI network
-func createBridge(r *libpod.Runtime, name string, options entities.NetworkCreateOptions) (string, error) {
- isGateway := true
- ipMasq := true
- subnet := &options.Subnet
- ipRange := options.Range
- runtimeConfig, err := r.GetConfig()
- if err != nil {
- return "", err
- }
- // if range is provided, make sure it is "in" network
- if subnet.IP != nil {
- // if network is provided, does it conflict with existing CNI or live networks
- err = network.ValidateUserNetworkIsAvailable(runtimeConfig, subnet)
- } else {
- // if no network is provided, figure out network
- subnet, err = network.GetFreeNetwork(runtimeConfig)
- }
- if err != nil {
- return "", err
- }
- gateway := options.Gateway
- if gateway == nil {
- // if no gateway is provided, provide it as first ip of network
- gateway = network.CalcGatewayIP(subnet)
- }
- // if network is provided and if gateway is provided, make sure it is "in" network
- if options.Subnet.IP != nil && options.Gateway != nil {
- if !subnet.Contains(gateway) {
- return "", errors.Errorf("gateway %s is not in valid for subnet %s", gateway.String(), subnet.String())
- }
- }
- if options.Internal {
- isGateway = false
- ipMasq = false
- }
-
- // if a range is given, we need to ensure it is "in" the network range.
- if options.Range.IP != nil {
- if options.Subnet.IP == nil {
- return "", errors.New("you must define a subnet range to define an ip-range")
- }
- firstIP, err := network.FirstIPInSubnet(&options.Range)
- if err != nil {
- return "", err
- }
- lastIP, err := network.LastIPInSubnet(&options.Range)
- if err != nil {
- return "", err
- }
- if !subnet.Contains(firstIP) || !subnet.Contains(lastIP) {
- return "", errors.Errorf("the ip range %s does not fall within the subnet range %s", options.Range.String(), subnet.String())
- }
- }
- bridgeDeviceName, err := network.GetFreeDeviceName(runtimeConfig)
- if err != nil {
- return "", err
- }
-
- if len(name) > 0 {
- netNames, err := network.GetNetworkNamesFromFileSystem(runtimeConfig)
- if err != nil {
- return "", err
- }
- if util.StringInSlice(name, netNames) {
- return "", errors.Errorf("the network name %s is already used", name)
- }
- } else {
- // If no name is given, we give the name of the bridge device
- name = bridgeDeviceName
- }
-
- ncList := network.NewNcList(name, cniversion.Current())
- var plugins []network.CNIPlugins
- var routes []network.IPAMRoute
-
- defaultRoute, err := network.NewIPAMDefaultRoute(network.IsIPv6(subnet.IP))
- if err != nil {
- return "", err
- }
- routes = append(routes, defaultRoute)
- ipamConfig, err := network.NewIPAMHostLocalConf(subnet, routes, ipRange, gateway)
- if err != nil {
- return "", err
- }
-
- // TODO need to iron out the role of isDefaultGW and IPMasq
- bridge := network.NewHostLocalBridge(bridgeDeviceName, isGateway, false, ipMasq, ipamConfig)
- plugins = append(plugins, bridge)
- plugins = append(plugins, network.NewPortMapPlugin())
- plugins = append(plugins, network.NewFirewallPlugin())
- // if we find the dnsname plugin, we add configuration for it
- if network.HasDNSNamePlugin(runtimeConfig.Network.CNIPluginDirs) && !options.DisableDNS {
- // Note: in the future we might like to allow for dynamic domain names
- plugins = append(plugins, network.NewDNSNamePlugin(network.DefaultPodmanDomainName))
- }
- ncList["plugins"] = plugins
- b, err := json.MarshalIndent(ncList, "", " ")
- if err != nil {
- return "", err
- }
- if err := os.MkdirAll(network.GetCNIConfDir(runtimeConfig), 0755); err != nil {
- return "", err
- }
- cniPathName := filepath.Join(network.GetCNIConfDir(runtimeConfig), fmt.Sprintf("%s.conflist", name))
- err = ioutil.WriteFile(cniPathName, b, 0644)
- return cniPathName, err
-}
-
-func createMacVLAN(r *libpod.Runtime, name string, options entities.NetworkCreateOptions) (string, error) {
- var (
- plugins []network.CNIPlugins
- )
- liveNetNames, err := network.GetLiveNetworkNames()
- if err != nil {
- return "", err
- }
-
- config, err := r.GetConfig()
- if err != nil {
- return "", err
- }
-
- // Make sure the host-device exists
- if !util.StringInSlice(options.MacVLAN, liveNetNames) {
- return "", errors.Errorf("failed to find network interface %q", options.MacVLAN)
- }
- if len(name) > 0 {
- netNames, err := network.GetNetworkNamesFromFileSystem(config)
- if err != nil {
- return "", err
- }
- if util.StringInSlice(name, netNames) {
- return "", errors.Errorf("the network name %s is already used", name)
- }
- } else {
- name, err = network.GetFreeDeviceName(config)
- if err != nil {
- return "", err
- }
- }
- ncList := network.NewNcList(name, cniversion.Current())
- macvlan := network.NewMacVLANPlugin(options.MacVLAN)
- plugins = append(plugins, macvlan)
- ncList["plugins"] = plugins
- b, err := json.MarshalIndent(ncList, "", " ")
- if err != nil {
- return "", err
- }
- cniPathName := filepath.Join(network.GetCNIConfDir(config), fmt.Sprintf("%s.conflist", name))
- err = ioutil.WriteFile(cniPathName, b, 0644)
- return cniPathName, err
+ return network.Create(name, options, ic.Libpod)
}
func ifPassesFilterTest(netconf *libcni.NetworkConfigList, filter []string) bool {
diff --git a/pkg/domain/infra/abi/pods.go b/pkg/domain/infra/abi/pods.go
index 747da9fd4..258640a81 100644
--- a/pkg/domain/infra/abi/pods.go
+++ b/pkg/domain/infra/abi/pods.go
@@ -66,7 +66,7 @@ func (ic *ContainerEngine) PodKill(ctx context.Context, namesOrIds []string, opt
for _, p := range pods {
report := entities.PodKillReport{Id: p.ID()}
- conErrs, err := p.Kill(uint(sig))
+ conErrs, err := p.Kill(ctx, uint(sig))
if err != nil && errors.Cause(err) != define.ErrPodPartialFail {
report.Errs = []error{err}
reports = append(reports, &report)
@@ -92,7 +92,7 @@ func (ic *ContainerEngine) PodPause(ctx context.Context, namesOrIds []string, op
}
for _, p := range pods {
report := entities.PodPauseReport{Id: p.ID()}
- errs, err := p.Pause()
+ errs, err := p.Pause(ctx)
if err != nil && errors.Cause(err) != define.ErrPodPartialFail {
report.Errs = []error{err}
continue
@@ -117,7 +117,7 @@ func (ic *ContainerEngine) PodUnpause(ctx context.Context, namesOrIds []string,
}
for _, p := range pods {
report := entities.PodUnpauseReport{Id: p.ID()}
- errs, err := p.Unpause()
+ errs, err := p.Unpause(ctx)
if err != nil && errors.Cause(err) != define.ErrPodPartialFail {
report.Errs = []error{err}
continue
diff --git a/pkg/parallel/parallel_linux.go b/pkg/parallel/ctr/ctr.go
index 442db1502..e8c1292b8 100644
--- a/pkg/parallel/parallel_linux.go
+++ b/pkg/parallel/ctr/ctr.go
@@ -1,11 +1,10 @@
-package parallel
+package ctr
import (
"context"
- "sync"
"github.com/containers/podman/v2/libpod"
- "github.com/pkg/errors"
+ "github.com/containers/podman/v2/pkg/parallel"
"github.com/sirupsen/logrus"
)
@@ -14,44 +13,28 @@ import (
// If no error is returned, each container specified in ctrs will have an entry
// in the resulting map; containers with no error will be set to nil.
func ContainerOp(ctx context.Context, ctrs []*libpod.Container, applyFunc func(*libpod.Container) error) (map[*libpod.Container]error, error) {
- jobControlLock.RLock()
- defer jobControlLock.RUnlock()
-
// We could use a sync.Map but given Go's lack of generic I'd rather
// just use a lock on a normal map...
// The expectation is that most of the time is spent in applyFunc
// anyways.
var (
- errMap = make(map[*libpod.Container]error)
- errLock sync.Mutex
- allDone sync.WaitGroup
+ errMap = make(map[*libpod.Container]<-chan error)
)
for _, ctr := range ctrs {
- // Block until a thread is available
- if err := jobControl.Acquire(ctx, 1); err != nil {
- return nil, errors.Wrapf(err, "error acquiring job control semaphore")
- }
-
- allDone.Add(1)
-
c := ctr
- go func() {
- logrus.Debugf("Launching job on container %s", c.ID())
-
- err := applyFunc(c)
- errLock.Lock()
- errMap[c] = err
- errLock.Unlock()
-
- allDone.Done()
- jobControl.Release(1)
- }()
+ logrus.Debugf("Starting parallel job on container %s", c.ID())
+ errChan := parallel.Enqueue(ctx, func() error {
+ return applyFunc(c)
+ })
+ errMap[c] = errChan
}
- allDone.Wait()
+ finalErr := make(map[*libpod.Container]error)
+ for ctr, errChan := range errMap {
+ err := <-errChan
+ finalErr[ctr] = err
+ }
- return errMap, nil
+ return finalErr, nil
}
-
-// TODO: Add an Enqueue() function that returns a promise
diff --git a/pkg/parallel/parallel.go b/pkg/parallel/parallel.go
index c9e4da50d..4da7e0f89 100644
--- a/pkg/parallel/parallel.go
+++ b/pkg/parallel/parallel.go
@@ -1,6 +1,7 @@
package parallel
import (
+ "context"
"sync"
"github.com/pkg/errors"
@@ -42,3 +43,32 @@ func SetMaxThreads(threads uint) error {
func GetMaxThreads() uint {
return numThreads
}
+
+// Enqueue adds a single function to the parallel jobs queue. This function will
+// be run when an unused thread is available.
+// Returns a receive-only error channel that will return the error (if any) from
+// the provided function fn when fn has finished executing. The channel will be
+// closed after this.
+func Enqueue(ctx context.Context, fn func() error) <-chan error {
+ retChan := make(chan error)
+
+ go func() {
+ jobControlLock.RLock()
+ defer jobControlLock.RUnlock()
+
+ defer close(retChan)
+
+ if err := jobControl.Acquire(ctx, 1); err != nil {
+ retChan <- errors.Wrapf(err, "error acquiring job control semaphore")
+ return
+ }
+
+ err := fn()
+
+ jobControl.Release(1)
+
+ retChan <- err
+ }()
+
+ return retChan
+}
diff --git a/pkg/varlinkapi/pods.go b/pkg/varlinkapi/pods.go
index 189434780..6d03afb7a 100644
--- a/pkg/varlinkapi/pods.go
+++ b/pkg/varlinkapi/pods.go
@@ -3,6 +3,7 @@
package varlinkapi
import (
+ "context"
"encoding/json"
"fmt"
"strconv"
@@ -207,7 +208,7 @@ func (i *VarlinkAPI) KillPod(call iopodman.VarlinkCall, name string, signal int6
if err != nil {
return call.ReplyPodNotFound(name, err.Error())
}
- ctrErrs, err := pod.Kill(killSignal)
+ ctrErrs, err := pod.Kill(context.TODO(), killSignal)
callErr := handlePodCall(call, pod, ctrErrs, err)
if callErr != nil {
return err
@@ -221,7 +222,7 @@ func (i *VarlinkAPI) PausePod(call iopodman.VarlinkCall, name string) error {
if err != nil {
return call.ReplyPodNotFound(name, err.Error())
}
- ctrErrs, err := pod.Pause()
+ ctrErrs, err := pod.Pause(context.TODO())
callErr := handlePodCall(call, pod, ctrErrs, err)
if callErr != nil {
return err
@@ -235,7 +236,7 @@ func (i *VarlinkAPI) UnpausePod(call iopodman.VarlinkCall, name string) error {
if err != nil {
return call.ReplyPodNotFound(name, err.Error())
}
- ctrErrs, err := pod.Unpause()
+ ctrErrs, err := pod.Unpause(context.TODO())
callErr := handlePodCall(call, pod, ctrErrs, err)
if callErr != nil {
return err
diff --git a/test/apiv2/10-images.at b/test/apiv2/10-images.at
index bdc298ae3..f669bc892 100644
--- a/test/apiv2/10-images.at
+++ b/test/apiv2/10-images.at
@@ -68,4 +68,7 @@ for i in $iid ${iid:0:12} $PODMAN_TEST_IMAGE_NAME; do
t GET "libpod/images/$i/get?compress=false" 200 '[POSIX tar archive]'
done
+# Export more than one image
+t GET images/get?names=alpine,busybox 200 '[POSIX tar archive]'
+
# vim: filetype=sh
diff --git a/test/e2e/build_test.go b/test/e2e/build_test.go
index e3e1044aa..5155bcbc7 100644
--- a/test/e2e/build_test.go
+++ b/test/e2e/build_test.go
@@ -89,7 +89,6 @@ var _ = Describe("Podman build", func() {
// Check that builds with different values for the squash options
// create the appropriate number of layers, then clean up after.
It("podman build basic alpine with squash", func() {
- SkipIfRemote("FIXME: This is broken should be fixed")
session := podmanTest.PodmanNoCache([]string{"build", "-f", "build/squash/Dockerfile.squash-a", "-t", "test-squash-a:latest", "build/squash"})
session.WaitWithDefaultTimeout()
Expect(session.ExitCode()).To(Equal(0))
@@ -221,8 +220,12 @@ var _ = Describe("Podman build", func() {
})
It("podman build --http_proxy flag", func() {
- SkipIfRemote("FIXME: This is broken should be fixed")
+ SkipIfRemote("FIXME: This is broken should be fixed") // This is hanging currently.
os.Setenv("http_proxy", "1.2.3.4")
+ if IsRemote() {
+ podmanTest.StopRemoteService()
+ podmanTest.StartRemoteService()
+ }
podmanTest.RestoreAllArtifacts()
dockerfile := `FROM docker.io/library/alpine:latest
RUN printenv http_proxy`
@@ -230,7 +233,7 @@ RUN printenv http_proxy`
dockerfilePath := filepath.Join(podmanTest.TempDir, "Dockerfile")
err := ioutil.WriteFile(dockerfilePath, []byte(dockerfile), 0755)
Expect(err).To(BeNil())
- session := podmanTest.PodmanNoCache([]string{"build", "--file", dockerfilePath, podmanTest.TempDir})
+ session := podmanTest.PodmanNoCache([]string{"build", "--http-proxy", "--file", dockerfilePath, podmanTest.TempDir})
session.Wait(120)
Expect(session.ExitCode()).To(Equal(0))
ok, _ := session.GrepString("1.2.3.4")
diff --git a/test/e2e/common_test.go b/test/e2e/common_test.go
index c663a4dca..ec910109b 100644
--- a/test/e2e/common_test.go
+++ b/test/e2e/common_test.go
@@ -453,7 +453,7 @@ func (p *PodmanTestIntegration) PodmanPID(args []string) (*PodmanSessionIntegrat
func (p *PodmanTestIntegration) Cleanup() {
// Remove all containers
stopall := p.Podman([]string{"stop", "-a", "--time", "0"})
- stopall.Wait(90)
+ stopall.WaitWithDefaultTimeout()
podstop := p.Podman([]string{"pod", "stop", "-a", "-t", "0"})
podstop.WaitWithDefaultTimeout()
@@ -461,7 +461,7 @@ func (p *PodmanTestIntegration) Cleanup() {
podrm.WaitWithDefaultTimeout()
session := p.Podman([]string{"rm", "-fa"})
- session.Wait(90)
+ session.WaitWithDefaultTimeout()
p.StopRemoteService()
// Nuke tempdir
diff --git a/test/e2e/exec_test.go b/test/e2e/exec_test.go
index 93a713f28..a698cd4b3 100644
--- a/test/e2e/exec_test.go
+++ b/test/e2e/exec_test.go
@@ -284,8 +284,6 @@ var _ = Describe("Podman exec", func() {
})
It("podman exec preserves container groups with --user and --group-add", func() {
- SkipIfRemote("FIXME: This is broken SECCOMP Failues?")
-
dockerfile := `FROM registry.fedoraproject.org/fedora-minimal
RUN groupadd -g 4000 first
RUN groupadd -g 4001 second
diff --git a/test/e2e/images_test.go b/test/e2e/images_test.go
index d9ad10fe9..9344132d9 100644
--- a/test/e2e/images_test.go
+++ b/test/e2e/images_test.go
@@ -176,7 +176,6 @@ var _ = Describe("Podman images", func() {
})
It("podman images filter before image", func() {
- SkipIfRemote("FIXME This should work on podman-remote")
dockerfile := `FROM docker.io/library/alpine:latest
RUN apk update && apk add strace
`
@@ -340,7 +339,7 @@ WORKDIR /test
})
It("podman images --all flag", func() {
- SkipIfRemote("FIXME This should work on podman-remote")
+ SkipIfRemote("FIXME This should work on podman-remote, problem is with podman-remote build")
podmanTest.RestoreAllArtifacts()
dockerfile := `FROM docker.io/library/alpine:latest
RUN mkdir hello
@@ -372,7 +371,7 @@ LABEL "com.example.vendor"="Example Vendor"
})
It("podman with images with no layers", func() {
- SkipIfRemote("FIXME This should work on podman-remote")
+ SkipIfRemote("FIXME This should work on podman-remote, problem is with podman-remote build")
dockerfile := strings.Join([]string{
`FROM scratch`,
`LABEL org.opencontainers.image.authors="<somefolks@example.org>"`,
diff --git a/test/e2e/load_test.go b/test/e2e/load_test.go
index ddffadac0..dd91381d9 100644
--- a/test/e2e/load_test.go
+++ b/test/e2e/load_test.go
@@ -243,7 +243,7 @@ var _ = Describe("Podman load", func() {
})
It("podman load localhost registry from dir", func() {
- SkipIfRemote("FIXME: podman-remote load is currently broken.")
+ SkipIfRemote("podman-remote does not support loading directories")
outfile := filepath.Join(podmanTest.TempDir, "load")
setup := podmanTest.PodmanNoCache([]string{"tag", BB, "hello:world"})
diff --git a/test/e2e/logs_test.go b/test/e2e/logs_test.go
index 9b3163856..664d4831e 100644
--- a/test/e2e/logs_test.go
+++ b/test/e2e/logs_test.go
@@ -127,7 +127,7 @@ var _ = Describe("Podman logs", func() {
})
It("two containers showing short container IDs", func() {
- SkipIfRemote("FIXME: remote does not support multiple containers")
+ SkipIfRemote("FIXME: podman-remote logs does not support showing two containers at the same time")
log1 := podmanTest.Podman([]string{"run", "-dt", ALPINE, "sh", "-c", "echo podman; echo podman; echo podman"})
log1.WaitWithDefaultTimeout()
Expect(log1.ExitCode()).To(Equal(0))
diff --git a/test/e2e/namespace_test.go b/test/e2e/namespace_test.go
index 92df3df48..951e98dfc 100644
--- a/test/e2e/namespace_test.go
+++ b/test/e2e/namespace_test.go
@@ -33,9 +33,14 @@ var _ = Describe("Podman namespaces", func() {
})
It("podman namespace test", func() {
- SkipIfRemote("FIXME This should work on Remote")
podman1 := podmanTest.Podman([]string{"--namespace", "test1", "run", "-d", ALPINE, "echo", "hello"})
podman1.WaitWithDefaultTimeout()
+ if IsRemote() {
+ // --namespace flag not supported in podman remote
+ Expect(podman1.ExitCode()).To(Equal(125))
+ Expect(podman1.ErrorToString()).To(ContainSubstring("unknown flag: --namespace"))
+ return
+ }
Expect(podman1.ExitCode()).To(Equal(0))
podman2 := podmanTest.Podman([]string{"--namespace", "test2", "ps", "-aq"})
diff --git a/test/e2e/network_create_test.go b/test/e2e/network_create_test.go
index edd76739f..21f03901b 100644
--- a/test/e2e/network_create_test.go
+++ b/test/e2e/network_create_test.go
@@ -8,7 +8,7 @@ import (
"strings"
cniversion "github.com/containernetworking/cni/pkg/version"
- "github.com/containers/podman/v2/pkg/network"
+ "github.com/containers/podman/v2/libpod/network"
. "github.com/containers/podman/v2/test/utils"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
@@ -137,7 +137,6 @@ var _ = Describe("Podman network create", func() {
})
It("podman network create with name and subnet", func() {
- SkipIfRemote("FIXME, this should work on --remote")
var (
results []network.NcList
)
diff --git a/test/e2e/play_kube_test.go b/test/e2e/play_kube_test.go
index afd27150d..b0831c823 100644
--- a/test/e2e/play_kube_test.go
+++ b/test/e2e/play_kube_test.go
@@ -940,7 +940,7 @@ var _ = Describe("Podman generate kube", func() {
})
It("podman play kube seccomp container level", func() {
- SkipIfRemote("FIXME This is broken")
+ SkipIfRemote("podman-remote does not support --seccomp-profile-root flag")
// expect play kube is expected to set a seccomp label if it's applied as an annotation
jsonFile, err := podmanTest.CreateSeccompJson(seccompPwdEPERM)
if err != nil {
@@ -967,7 +967,7 @@ var _ = Describe("Podman generate kube", func() {
})
It("podman play kube seccomp pod level", func() {
- SkipIfRemote("FIXME: This should work with --remote")
+ SkipIfRemote("podman-remote does not support --seccomp-profile-root flag")
// expect play kube is expected to set a seccomp label if it's applied as an annotation
jsonFile, err := podmanTest.CreateSeccompJson(seccompPwdEPERM)
if err != nil {
diff --git a/test/e2e/pod_infra_container_test.go b/test/e2e/pod_infra_container_test.go
index 063c71b9f..797d51c33 100644
--- a/test/e2e/pod_infra_container_test.go
+++ b/test/e2e/pod_infra_container_test.go
@@ -377,7 +377,6 @@ var _ = Describe("Podman pod create", func() {
})
It("podman run --add-host in pod", func() {
- SkipIfRemote("FIXME This should work on podman-remote")
session := podmanTest.Podman([]string{"pod", "create"})
session.WaitWithDefaultTimeout()
Expect(session.ExitCode()).To(Equal(0))
diff --git a/test/e2e/pod_pod_namespaces.go b/test/e2e/pod_pod_namespaces.go
index 3139bf561..41e9c5683 100644
--- a/test/e2e/pod_pod_namespaces.go
+++ b/test/e2e/pod_pod_namespaces.go
@@ -61,7 +61,6 @@ var _ = Describe("Podman pod create", func() {
})
It("podman pod container dontshare PIDNS", func() {
- SkipIfRemote("FIXME This should work on podman-remote")
session := podmanTest.Podman([]string{"pod", "create"})
session.WaitWithDefaultTimeout()
Expect(session.ExitCode()).To(Equal(0))
diff --git a/test/e2e/prune_test.go b/test/e2e/prune_test.go
index 24b88bfdd..969f96165 100644
--- a/test/e2e/prune_test.go
+++ b/test/e2e/prune_test.go
@@ -88,7 +88,7 @@ var _ = Describe("Podman prune", func() {
})
It("podman image prune skip cache images", func() {
- SkipIfRemote("FIXME should work on podman --remote")
+ SkipIfRemote("FIXME: podman-remote build is not working the same as local build")
podmanTest.BuildImage(pruneImage, "alpine_bash:latest", "true")
none := podmanTest.Podman([]string{"images", "-a"})
@@ -110,7 +110,7 @@ var _ = Describe("Podman prune", func() {
})
It("podman image prune dangling images", func() {
- SkipIfRemote("FIXME This should work on podman-remote")
+ SkipIfRemote("FIXME: podman-remote build is not working the same as local build")
podmanTest.BuildImage(pruneImage, "alpine_bash:latest", "true")
podmanTest.BuildImage(pruneImage, "alpine_bash:latest", "true")
@@ -147,7 +147,6 @@ var _ = Describe("Podman prune", func() {
})
It("podman system image prune unused images", func() {
- SkipIfRemote("FIXME This should work on podman-remote")
podmanTest.RestoreAllArtifacts()
podmanTest.BuildImage(pruneImage, "alpine_bash:latest", "true")
prune := podmanTest.PodmanNoCache([]string{"system", "prune", "-a", "--force"})
diff --git a/test/e2e/pull_test.go b/test/e2e/pull_test.go
index edc17fdbf..08ab50de1 100644
--- a/test/e2e/pull_test.go
+++ b/test/e2e/pull_test.go
@@ -234,7 +234,8 @@ var _ = Describe("Podman pull", func() {
})
It("podman pull from docker-archive", func() {
- SkipIfRemote("FIXME This should work on podman-remote")
+ SkipIfRemote("podman-remote does not support pulling from docker-archive")
+
podmanTest.RestoreArtifact(ALPINE)
tarfn := filepath.Join(podmanTest.TempDir, "alp.tar")
session := podmanTest.PodmanNoCache([]string{"save", "-o", tarfn, "alpine"})
@@ -296,7 +297,8 @@ var _ = Describe("Podman pull", func() {
})
It("podman pull from oci-archive", func() {
- SkipIfRemote("FIXME This should work on podman-remote")
+ SkipIfRemote("podman-remote does not support pulling from oci-archive")
+
podmanTest.RestoreArtifact(ALPINE)
tarfn := filepath.Join(podmanTest.TempDir, "oci-alp.tar")
session := podmanTest.PodmanNoCache([]string{"save", "--format", "oci-archive", "-o", tarfn, "alpine"})
@@ -315,7 +317,8 @@ var _ = Describe("Podman pull", func() {
})
It("podman pull from local directory", func() {
- SkipIfRemote("FIXME This should work on podman-remote")
+ SkipIfRemote("podman-remote does not support pulling from local directory")
+
podmanTest.RestoreArtifact(ALPINE)
dirpath := filepath.Join(podmanTest.TempDir, "alpine")
os.MkdirAll(dirpath, os.ModePerm)
@@ -340,7 +343,8 @@ var _ = Describe("Podman pull", func() {
})
It("podman pull from local OCI directory", func() {
- SkipIfRemote("FIXME This should work on podman-remote")
+ SkipIfRemote("podman-remote does not support pulling from OCI directory")
+
podmanTest.RestoreArtifact(ALPINE)
dirpath := filepath.Join(podmanTest.TempDir, "alpine")
os.MkdirAll(dirpath, os.ModePerm)
diff --git a/test/e2e/rmi_test.go b/test/e2e/rmi_test.go
index 8a5014899..7cb489113 100644
--- a/test/e2e/rmi_test.go
+++ b/test/e2e/rmi_test.go
@@ -185,7 +185,8 @@ var _ = Describe("Podman rmi", func() {
})
It("podman rmi with cached images", func() {
- SkipIfRemote("FIXME This should work on podman-remote")
+ SkipIfRemote("FIXME This should work on podman-remote, problem is with podman-remote build")
+
session := podmanTest.PodmanNoCache([]string{"rmi", "-fa"})
session.WaitWithDefaultTimeout()
Expect(session).Should(Exit(0))
@@ -255,7 +256,6 @@ var _ = Describe("Podman rmi", func() {
})
It("podman rmi -a with parent|child images", func() {
- SkipIfRemote("FIXME This should work on podman-remote")
dockerfile := `FROM docker.io/library/alpine:latest AS base
RUN touch /1
ENV LOCAL=/1
diff --git a/test/e2e/run_entrypoint_test.go b/test/e2e/run_entrypoint_test.go
index 13a9abf9b..db802946e 100644
--- a/test/e2e/run_entrypoint_test.go
+++ b/test/e2e/run_entrypoint_test.go
@@ -90,7 +90,7 @@ ENTRYPOINT ["grep", "Alpine", "/etc/os-release"]
})
It("podman run user entrypoint overrides image entrypoint and image cmd", func() {
- SkipIfRemote("FIXME This should work on podman-remote")
+ SkipIfRemote("FIXME: podman-remote not handling passing --entrypoint=\"\" flag correctly")
dockerfile := `FROM docker.io/library/alpine:latest
CMD ["-i"]
ENTRYPOINT ["grep", "Alpine", "/etc/os-release"]
diff --git a/test/e2e/run_env_test.go b/test/e2e/run_env_test.go
index 3f488ada5..9882b936a 100644
--- a/test/e2e/run_env_test.go
+++ b/test/e2e/run_env_test.go
@@ -90,11 +90,15 @@ var _ = Describe("Podman run", func() {
})
It("podman run --env-host environment test", func() {
- SkipIfRemote("FIXME, We should check that --env-host reports correct error on podman-remote")
env := append(os.Environ(), "FOO=BAR")
session := podmanTest.PodmanAsUser([]string{"run", "--rm", "--env-host", ALPINE, "/bin/printenv", "FOO"}, 0, 0, "", env)
-
session.WaitWithDefaultTimeout()
+ if IsRemote() {
+ // podman-remote does not support --env-host
+ Expect(session.ExitCode()).To(Equal(125))
+ Expect(session.ErrorToString()).To(ContainSubstring("unknown flag: --env-host"))
+ return
+ }
Expect(session.ExitCode()).To(Equal(0))
match, _ := session.GrepString("BAR")
Expect(match).Should(BeTrue())
@@ -108,8 +112,11 @@ var _ = Describe("Podman run", func() {
})
It("podman run --http-proxy test", func() {
- SkipIfRemote("FIXME: Should report proper error when http-proxy is not supported")
os.Setenv("http_proxy", "1.2.3.4")
+ if IsRemote() {
+ podmanTest.StopRemoteService()
+ podmanTest.StartRemoteService()
+ }
session := podmanTest.Podman([]string{"run", "--rm", ALPINE, "printenv", "http_proxy"})
session.WaitWithDefaultTimeout()
Expect(session.ExitCode()).To(Equal(0))
diff --git a/test/e2e/run_networking_test.go b/test/e2e/run_networking_test.go
index 044e56e6c..e14482db7 100644
--- a/test/e2e/run_networking_test.go
+++ b/test/e2e/run_networking_test.go
@@ -73,7 +73,7 @@ var _ = Describe("Podman run networking", func() {
Expect(len(inspectOut)).To(Equal(1))
Expect(len(inspectOut[0].NetworkSettings.Ports)).To(Equal(1))
Expect(len(inspectOut[0].NetworkSettings.Ports["80/tcp"])).To(Equal(1))
- Expect(inspectOut[0].NetworkSettings.Ports["80/tcp"][0].HostPort).To(Equal("80"))
+ Expect(inspectOut[0].NetworkSettings.Ports["80/tcp"][0].HostPort).To(Not(Equal("80")))
Expect(inspectOut[0].NetworkSettings.Ports["80/tcp"][0].HostIP).To(Equal(""))
})
@@ -111,7 +111,7 @@ var _ = Describe("Podman run networking", func() {
Expect(len(inspectOut)).To(Equal(1))
Expect(len(inspectOut[0].NetworkSettings.Ports)).To(Equal(1))
Expect(len(inspectOut[0].NetworkSettings.Ports["80/udp"])).To(Equal(1))
- Expect(inspectOut[0].NetworkSettings.Ports["80/udp"][0].HostPort).To(Equal("80"))
+ Expect(inspectOut[0].NetworkSettings.Ports["80/udp"][0].HostPort).To(Not(Equal("80")))
Expect(inspectOut[0].NetworkSettings.Ports["80/udp"][0].HostIP).To(Equal(""))
})
@@ -195,7 +195,7 @@ var _ = Describe("Podman run networking", func() {
Expect(len(inspectOut)).To(Equal(1))
Expect(len(inspectOut[0].NetworkSettings.Ports)).To(Equal(1))
Expect(len(inspectOut[0].NetworkSettings.Ports["80/tcp"])).To(Equal(1))
- Expect(inspectOut[0].NetworkSettings.Ports["80/tcp"][0].HostPort).To(Equal("80"))
+ Expect(inspectOut[0].NetworkSettings.Ports["80/tcp"][0].HostPort).To(Not(Equal("80")))
Expect(inspectOut[0].NetworkSettings.Ports["80/tcp"][0].HostIP).To(Equal(""))
})
@@ -477,6 +477,17 @@ var _ = Describe("Podman run networking", func() {
Expect(session.ExitCode()).To(Equal(0))
})
+ It("podman run --uidmap /etc/hosts contains --hostname", func() {
+ SkipIfRootless("uidmap population of cninetworks not supported for rootless users")
+ session := podmanTest.Podman([]string{"run", "--uidmap", "0:100000:1000", "--rm", "--hostname", "foohostname", ALPINE, "grep", "foohostname", "/etc/hosts"})
+ session.WaitWithDefaultTimeout()
+ Expect(session.ExitCode()).To(Equal(0))
+
+ session = podmanTest.Podman([]string{"run", "--uidmap", "0:100000:1000", "--rm", "--hostname", "foohostname", "-v", "/etc/hosts:/etc/hosts", ALPINE, "grep", "foohostname", "/etc/hosts"})
+ session.WaitWithDefaultTimeout()
+ Expect(session.ExitCode()).To(Equal(1))
+ })
+
It("podman run network in user created network namespace", func() {
SkipIfRootless("ip netns is not supported for rootless users")
if Containerized() {
diff --git a/test/e2e/run_restart_test.go b/test/e2e/run_restart_test.go
index 1bef3f954..85621a762 100644
--- a/test/e2e/run_restart_test.go
+++ b/test/e2e/run_restart_test.go
@@ -33,11 +33,14 @@ var _ = Describe("Podman run restart containers", func() {
})
It("Podman start after successful run", func() {
- SkipIfRemote("FIXME This should work on podman-remote")
session := podmanTest.Podman([]string{"run", "--name", "test", ALPINE, "ls"})
session.WaitWithDefaultTimeout()
Expect(session.ExitCode()).To(Equal(0))
+ session = podmanTest.Podman([]string{"wait", "test"})
+ session.WaitWithDefaultTimeout()
+ Expect(session.ExitCode()).To(Equal(0))
+
session2 := podmanTest.Podman([]string{"start", "--attach", "test"})
session2.WaitWithDefaultTimeout()
Expect(session2.ExitCode()).To(Equal(0))
diff --git a/test/e2e/run_test.go b/test/e2e/run_test.go
index 05aede122..cd32e5a77 100644
--- a/test/e2e/run_test.go
+++ b/test/e2e/run_test.go
@@ -39,7 +39,6 @@ var _ = Describe("Podman run", func() {
podmanTest.Cleanup()
f := CurrentGinkgoTestDescription()
processTestResult(f)
-
})
It("podman run a container based on local image", func() {
@@ -321,7 +320,6 @@ var _ = Describe("Podman run", func() {
It("podman run user capabilities test with image", func() {
// We need to ignore the containers.conf on the test distribution for this test
os.Setenv("CONTAINERS_CONF", "/dev/null")
- SkipIfRemote("FIXME This should work on podman-remote")
dockerfile := `FROM busybox
USER bin`
podmanTest.BuildImage(dockerfile, "test", "false")
@@ -565,7 +563,7 @@ USER bin`
})
It("podman run with secrets", func() {
- SkipIfRemote("FIXME This should work on podman-remote")
+ SkipIfRemote("--default-mount-file option is not supported in podman-remote")
containersDir := filepath.Join(podmanTest.TempDir, "containers")
err := os.MkdirAll(containersDir, 0755)
Expect(err).To(BeNil())
@@ -725,7 +723,6 @@ USER bin`
})
It("podman run with built-in volume image", func() {
- SkipIfRemote("FIXME This should work on podman-remote")
session := podmanTest.Podman([]string{"run", "--rm", redis, "ls"})
session.WaitWithDefaultTimeout()
Expect(session.ExitCode()).To(Equal(0))
@@ -1041,7 +1038,6 @@ USER mail`
})
It("podman run with restart-policy always restarts containers", func() {
- SkipIfRemote("FIXME This should work on podman-remote")
testDir := filepath.Join(podmanTest.RunRoot, "restart-test")
err := os.MkdirAll(testDir, 0755)
Expect(err).To(BeNil())
@@ -1051,11 +1047,11 @@ USER mail`
Expect(err).To(BeNil())
file.Close()
- session := podmanTest.Podman([]string{"run", "-dt", "--restart", "always", "-v", fmt.Sprintf("%s:/tmp/runroot:Z", testDir), fedoraMinimal, "bash", "-c", "date +%N > /tmp/runroot/ran && while test -r /tmp/runroot/running; do sleep 0.1s; done"})
+ session := podmanTest.Podman([]string{"run", "-dt", "--restart", "always", "-v", fmt.Sprintf("%s:/tmp/runroot:Z", testDir), ALPINE, "sh", "-c", "date +%N > /tmp/runroot/ran && while test -r /tmp/runroot/running; do sleep 0.1s; done"})
found := false
testFile := filepath.Join(testDir, "ran")
- for i := 0; i < 10; i++ {
+ for i := 0; i < 30; i++ {
time.Sleep(1 * time.Second)
if _, err := os.Stat(testFile); err == nil {
found = true
diff --git a/test/e2e/run_working_dir.go b/test/e2e/run_working_dir.go
index 85aa0cffe..7d8db361c 100644
--- a/test/e2e/run_working_dir.go
+++ b/test/e2e/run_working_dir.go
@@ -50,7 +50,6 @@ var _ = Describe("Podman run", func() {
})
It("podman run a container on an image with a workdir", func() {
- SkipIfRemote("FIXME This should work on podman-remote")
dockerfile := `FROM alpine
RUN mkdir -p /home/foobar
WORKDIR /etc/foobar`
diff --git a/test/e2e/search_test.go b/test/e2e/search_test.go
index 19365909d..043da9059 100644
--- a/test/e2e/search_test.go
+++ b/test/e2e/search_test.go
@@ -237,7 +237,7 @@ registries = ['{{.Host}}:{{.Port}}']`
})
It("podman search attempts HTTP if registry is in registries.insecure and force secure is false", func() {
- SkipIfRemote("FIXME This should work on podman-remote")
+ SkipIfRemote("--tls-verify is not supported on podman-remote search")
if podmanTest.Host.Arch == "ppc64le" {
Skip("No registry image for ppc64le")
}
@@ -278,7 +278,7 @@ registries = ['{{.Host}}:{{.Port}}']`
})
It("podman search doesn't attempt HTTP if force secure is true", func() {
- SkipIfRemote("FIXME This should work on podman-remote")
+ SkipIfRemote("--tls-verify is not supported on podman-remote search")
if podmanTest.Host.Arch == "ppc64le" {
Skip("No registry image for ppc64le")
}
@@ -317,7 +317,7 @@ registries = ['{{.Host}}:{{.Port}}']`
})
It("podman search doesn't attempt HTTP if registry is not listed as insecure", func() {
- SkipIfRemote("FIXME This should work on podman-remote")
+ SkipIfRemote("--tls-verify is not supported on podman-remote search")
if podmanTest.Host.Arch == "ppc64le" {
Skip("No registry image for ppc64le")
}
diff --git a/test/e2e/system_df_test.go b/test/e2e/system_df_test.go
index aee5dafb8..365e36fc7 100644
--- a/test/e2e/system_df_test.go
+++ b/test/e2e/system_df_test.go
@@ -3,6 +3,7 @@ package integration
import (
"fmt"
"os"
+ "strconv"
"strings"
. "github.com/containers/podman/v2/test/utils"
@@ -35,7 +36,6 @@ var _ = Describe("podman system df", func() {
})
It("podman system df", func() {
- SkipIfRemote("FIXME This should work on podman-remote")
session := podmanTest.Podman([]string{"create", ALPINE})
session.WaitWithDefaultTimeout()
Expect(session.ExitCode()).To(Equal(0))
@@ -48,6 +48,11 @@ var _ = Describe("podman system df", func() {
session.WaitWithDefaultTimeout()
Expect(session.ExitCode()).To(Equal(0))
+ session = podmanTest.Podman([]string{"images", "-q"})
+ session.WaitWithDefaultTimeout()
+ Expect(session.ExitCode()).To(Equal(0))
+ totImages := strconv.Itoa(len(session.OutputToStringArray()))
+
session = podmanTest.Podman([]string{"system", "df"})
session.WaitWithDefaultTimeout()
Expect(session.ExitCode()).To(Equal(0))
@@ -55,7 +60,7 @@ var _ = Describe("podman system df", func() {
images := strings.Fields(session.OutputToStringArray()[1])
containers := strings.Fields(session.OutputToStringArray()[2])
volumes := strings.Fields(session.OutputToStringArray()[3])
- Expect(images[1]).To(Equal("11"))
+ Expect(images[1]).To(Equal(string(totImages)))
Expect(containers[1]).To(Equal("2"))
Expect(volumes[2]).To(Equal("1"))
})
diff --git a/test/system/060-mount.bats b/test/system/060-mount.bats
index 75c88e4ad..f11aff773 100644
--- a/test/system/060-mount.bats
+++ b/test/system/060-mount.bats
@@ -56,7 +56,7 @@ load helpers
# 'image mount', no args, tells us what's mounted
run_podman image mount
- is "$output" "$IMAGE $mount_path" "podman image mount with no args"
+ is "$output" "$IMAGE *$mount_path" "podman image mount with no args"
# Clean up
run_podman image umount $IMAGE
diff --git a/test/system/070-build.bats b/test/system/070-build.bats
index 1329c6168..287323bbf 100644
--- a/test/system/070-build.bats
+++ b/test/system/070-build.bats
@@ -174,12 +174,19 @@ EOF
run_podman build -t build_test -f build-test/Containerfile build-test
local iid="${lines[-1]}"
+
+ if is_remote; then
+ ENVHOST=""
+ else
+ ENVHOST="--env-host"
+ fi
+
# Run without args - should run the above script. Verify its output.
export MYENV2="$s_env2"
export MYENV3="env-file-should-override-env-host!"
run_podman run --rm \
--env-file=$PODMAN_TMPDIR/env-file \
- --env-host \
+ ${ENVHOST} \
-e MYENV4="$s_env4" \
build_test
is "${lines[0]}" "$workdir" "container default command: pwd"