From 5cfbe0b78e3672dd67cd028b85d816fc19d6a614 Mon Sep 17 00:00:00 2001 From: jortkoopmans Date: Wed, 25 Nov 2020 18:26:22 +0100 Subject: squash Signed-off-by: jortkoopmans --- changelog.txt | 30 ++ cmd/podman/common/completion.go | 251 +++++++++--- cmd/podman/common/create.go | 18 +- cmd/podman/common/create_opts.go | 153 +++++-- cmd/podman/common/createparse.go | 2 +- cmd/podman/common/netflags.go | 34 +- cmd/podman/common/volumes.go | 102 +---- cmd/podman/containers/ps.go | 20 +- cmd/podman/containers/top.go | 10 +- cmd/podman/images/list.go | 3 +- cmd/podman/images/load.go | 2 +- cmd/podman/networks/connect.go | 2 +- cmd/podman/networks/disconnect.go | 2 +- cmd/podman/networks/list.go | 2 +- cmd/podman/networks/rm.go | 1 + cmd/podman/pods/create.go | 30 +- cmd/podman/pods/top.go | 2 +- cmd/podman/root.go | 52 ++- cmd/podman/volumes/list.go | 2 +- contrib/systemd/system/podman.service | 3 +- docs/source/Tutorials.rst | 2 +- docs/source/conf.py | 4 +- docs/source/managecontainers.rst | 2 + docs/source/markdown/links/podman-list.1 | 1 - docs/source/markdown/links/podman-ls.1 | 1 - docs/source/markdown/podman-container.1.md | 1 + docs/source/markdown/podman-create.1.md | 9 +- docs/source/markdown/podman-export.1.md | 2 + docs/source/markdown/podman-import.1.md | 1 + docs/source/markdown/podman-load.1.md | 3 +- docs/source/markdown/podman-ps.1.md | 8 +- docs/source/markdown/podman-run.1.md | 9 +- docs/source/markdown/podman-save.1.md | 4 +- docs/source/markdown/podman-system-service.1.md | 5 +- docs/source/markdown/podman-top.1.md | 2 +- docs/source/markdown/podman.1.md | 12 +- docs/tutorials/mac_win_client.md | 2 +- go.mod | 6 +- go.sum | 12 +- libpod/container.go | 55 ++- libpod/container_api.go | 14 + libpod/container_internal.go | 37 +- libpod/healthcheck_linux.go | 4 + libpod/image/manifests.go | 36 +- libpod/network/config.go | 10 + libpod/network/create.go | 1 + libpod/network/netconflist.go | 7 + libpod/network/subnet.go | 14 +- libpod/network/subnet_test.go | 62 +++ libpod/networking_linux.go | 28 +- libpod/rootless_cni_linux.go | 4 +- pkg/api/handlers/compat/containers.go | 15 +- pkg/api/handlers/compat/containers_create.go | 21 +- pkg/api/handlers/compat/info.go | 3 +- pkg/api/handlers/libpod/containers.go | 24 ++ pkg/api/handlers/types.go | 13 +- pkg/api/server/handler_api.go | 7 +- pkg/api/server/listener_api.go | 1 + pkg/api/server/server.go | 6 +- pkg/bindings/containers/containers.go | 12 + pkg/domain/entities/images.go | 8 +- pkg/domain/infra/abi/containers.go | 15 +- pkg/domain/infra/tunnel/containers.go | 25 +- pkg/specgen/generate/container_create.go | 12 +- pkg/specgen/generate/storage.go | 61 ++- pkg/specgen/namespaces.go | 6 - pkg/specgen/specgen.go | 38 +- pkg/specgen/volumes.go | 149 +++++++ test/apiv2/rest_api/__init__.py | 15 +- test/apiv2/rest_api/test_rest_v2_0_0.py | 90 +++- test/apiv2/rest_api/v1_test_rest_v1_0_0.py | 4 +- test/e2e/config/containers-remote.conf | 51 +++ test/e2e/containers_conf_test.go | 68 +++ test/e2e/create_staticmac_test.go | 18 + test/e2e/network_test.go | 55 +-- test/e2e/pod_create_test.go | 20 + test/e2e/ps_test.go | 6 + test/e2e/run_networking_test.go | 29 ++ test/e2e/run_test.go | 4 +- test/python/docker/__init__.py | 14 +- test/python/docker/common.py | 4 +- test/python/docker/test_containers.py | 2 +- test/python/docker/test_images.py | 4 +- .../containers/common/pkg/config/config.go | 4 + .../containers/common/pkg/config/containers.conf | 10 +- .../containers/common/pkg/config/default.go | 4 +- .../containers/common/pkg/retry/retry.go | 2 +- .../containers/common/pkg/seccomp/default_linux.go | 2 + .../containers/common/pkg/seccomp/supported.go | 2 + .../containers/common/version/version.go | 2 +- .../image/v5/pkg/sysregistriesv2/shortnames.go | 2 +- .../containers/image/v5/version/version.go | 2 +- vendor/github.com/containers/storage/VERSION | 2 +- vendor/github.com/containers/storage/go.mod | 2 +- vendor/github.com/containers/storage/go.sum | 4 +- .../containers/storage/pkg/unshare/unshare.go | 1 + .../klauspost/compress/flate/gen_inflate.go | 114 +++--- .../github.com/klauspost/compress/flate/inflate.go | 7 + .../klauspost/compress/flate/inflate_gen.go | 456 ++++++++++++--------- .../github.com/klauspost/compress/zstd/README.md | 4 +- .../github.com/klauspost/compress/zstd/decoder.go | 10 +- vendor/modules.txt | 8 +- 102 files changed, 1687 insertions(+), 830 deletions(-) delete mode 100644 docs/source/markdown/links/podman-list.1 delete mode 100644 docs/source/markdown/links/podman-ls.1 create mode 100644 pkg/specgen/volumes.go create mode 100644 test/e2e/config/containers-remote.conf diff --git a/changelog.txt b/changelog.txt index 4d423ca44..326f52718 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,3 +1,33 @@ +- Changelog for HEAD (2020-11-24): + * Set PATH env in systemd timer. + * Docker compat API fixes + * shell completions: remove usage of ShellCompDirectiveError + * more shell completion improvements + * Fix ip-range for classless subnet masks + * Bump github.com/containers/common from 0.27.0 to 0.29.0 + * Add podman container ps command + * clarify ps(1) fallback of `podman top` + * APIv2 - create container sets wrong entrypoint + * Enable remote shell completion without a running endpoint + * Specify what the replace flag replaces in help text + * APIv2 - strip CAP_ prefix from capabilities in json + * Make c.networks() list include the default network + * Allow containers to --restart on-failure with --rm + * REST API v2 - list of images - mandatory Created attribute + * Allow multiple --network flags for podman run/create + * fix container cgroup lookup + * Make podman service log events + * vendor in containers/storage v1.24.1 containers/image v5.8.1 + * Document containers.conf settings for remote connections + * Shell completion for podman ps and podman pod ps --filter + * Add alias for podman network rm -> remove + * add network connect|disconnect compat endpoints + * Fix sed regex to update version in version/version.go + * Github-Actions: Send e-mail on Cirrus cron failure + * Align the podman pod ps --filter behavior with podman ps + * podman-remote network rm --force is broken + * Remove build \!remote flags from test + - Changelog for v2.2.0-rc1 (2020-11-18): * Add release notes for v2.2.0-RC1 * correct numbering typo diff --git a/cmd/podman/common/completion.go b/cmd/podman/common/completion.go index db4d3d0d3..9856e46ef 100644 --- a/cmd/podman/common/completion.go +++ b/cmd/podman/common/completion.go @@ -12,7 +12,9 @@ import ( "github.com/containers/podman/v2/libpod/define" "github.com/containers/podman/v2/pkg/domain/entities" "github.com/containers/podman/v2/pkg/registries" + "github.com/containers/podman/v2/pkg/rootless" systemdGen "github.com/containers/podman/v2/pkg/systemd/generate" + "github.com/containers/podman/v2/pkg/util" "github.com/spf13/cobra" ) @@ -36,7 +38,35 @@ const ( type keyValueCompletion map[string]func(s string) ([]string, cobra.ShellCompDirective) -func getContainers(toComplete string, cType completeType, statuses ...string) ([]string, cobra.ShellCompDirective) { +func setupContainerEngine(cmd *cobra.Command) (entities.ContainerEngine, error) { + containerEngine, err := registry.NewContainerEngine(cmd, []string{}) + if err != nil { + cobra.CompErrorln(err.Error()) + return nil, err + } + if !registry.IsRemote() && rootless.IsRootless() { + err := containerEngine.SetupRootless(registry.Context(), cmd) + if err != nil { + return nil, err + } + } + return containerEngine, nil +} + +func setupImageEngine(cmd *cobra.Command) (entities.ImageEngine, error) { + imageEngine, err := registry.NewImageEngine(cmd, []string{}) + if err != nil { + return nil, err + } + // we also need to set up the container engine since this + // is required to setup the rootless namespace + if _, err = setupContainerEngine(cmd); err != nil { + return nil, err + } + return imageEngine, nil +} + +func getContainers(cmd *cobra.Command, toComplete string, cType completeType, statuses ...string) ([]string, cobra.ShellCompDirective) { suggestions := []string{} listOpts := entities.ContainerListOptions{ Filters: make(map[string][]string), @@ -47,10 +77,15 @@ func getContainers(toComplete string, cType completeType, statuses ...string) ([ listOpts.Filters["status"] = statuses } - containers, err := registry.ContainerEngine().ContainerList(registry.GetContext(), listOpts) + engine, err := setupContainerEngine(cmd) + if err != nil { + cobra.CompErrorln(err.Error()) + return nil, cobra.ShellCompDirectiveNoFileComp + } + containers, err := engine.ContainerList(registry.GetContext(), listOpts) if err != nil { cobra.CompErrorln(err.Error()) - return nil, cobra.ShellCompDirectiveError + return nil, cobra.ShellCompDirectiveNoFileComp } for _, c := range containers { @@ -68,7 +103,7 @@ func getContainers(toComplete string, cType completeType, statuses ...string) ([ return suggestions, cobra.ShellCompDirectiveNoFileComp } -func getPods(toComplete string, cType completeType, statuses ...string) ([]string, cobra.ShellCompDirective) { +func getPods(cmd *cobra.Command, toComplete string, cType completeType, statuses ...string) ([]string, cobra.ShellCompDirective) { suggestions := []string{} listOpts := entities.PodPSOptions{ Filters: make(map[string][]string), @@ -77,10 +112,15 @@ func getPods(toComplete string, cType completeType, statuses ...string) ([]strin listOpts.Filters["status"] = statuses } - pods, err := registry.ContainerEngine().PodPs(registry.GetContext(), listOpts) + engine, err := setupContainerEngine(cmd) if err != nil { cobra.CompErrorln(err.Error()) - return nil, cobra.ShellCompDirectiveError + return nil, cobra.ShellCompDirectiveNoFileComp + } + pods, err := engine.PodPs(registry.GetContext(), listOpts) + if err != nil { + cobra.CompErrorln(err.Error()) + return nil, cobra.ShellCompDirectiveNoFileComp } for _, pod := range pods { @@ -98,14 +138,19 @@ func getPods(toComplete string, cType completeType, statuses ...string) ([]strin return suggestions, cobra.ShellCompDirectiveNoFileComp } -func getVolumes(toComplete string) ([]string, cobra.ShellCompDirective) { +func getVolumes(cmd *cobra.Command, toComplete string) ([]string, cobra.ShellCompDirective) { suggestions := []string{} lsOpts := entities.VolumeListOptions{} - volumes, err := registry.ContainerEngine().VolumeList(registry.GetContext(), lsOpts) + engine, err := setupContainerEngine(cmd) + if err != nil { + cobra.CompErrorln(err.Error()) + return nil, cobra.ShellCompDirectiveNoFileComp + } + volumes, err := engine.VolumeList(registry.GetContext(), lsOpts) if err != nil { cobra.CompErrorln(err.Error()) - return nil, cobra.ShellCompDirectiveError + return nil, cobra.ShellCompDirectiveNoFileComp } for _, v := range volumes { @@ -116,14 +161,19 @@ func getVolumes(toComplete string) ([]string, cobra.ShellCompDirective) { return suggestions, cobra.ShellCompDirectiveNoFileComp } -func getImages(toComplete string) ([]string, cobra.ShellCompDirective) { +func getImages(cmd *cobra.Command, toComplete string) ([]string, cobra.ShellCompDirective) { suggestions := []string{} listOptions := entities.ImageListOptions{} - images, err := registry.ImageEngine().List(registry.GetContext(), listOptions) + engine, err := setupImageEngine(cmd) + if err != nil { + cobra.CompErrorln(err.Error()) + return nil, cobra.ShellCompDirectiveNoFileComp + } + images, err := engine.List(registry.GetContext(), listOptions) if err != nil { cobra.CompErrorln(err.Error()) - return nil, cobra.ShellCompDirectiveError + return nil, cobra.ShellCompDirectiveNoFileComp } for _, image := range images { @@ -166,19 +216,24 @@ func getRegistries() ([]string, cobra.ShellCompDirective) { regs, err := registries.GetRegistries() if err != nil { cobra.CompErrorln(err.Error()) - return nil, cobra.ShellCompDirectiveError + return nil, cobra.ShellCompDirectiveNoFileComp } return regs, cobra.ShellCompDirectiveNoFileComp } -func getNetworks(toComplete string) ([]string, cobra.ShellCompDirective) { +func getNetworks(cmd *cobra.Command, toComplete string) ([]string, cobra.ShellCompDirective) { suggestions := []string{} networkListOptions := entities.NetworkListOptions{} - networks, err := registry.ContainerEngine().NetworkList(registry.Context(), networkListOptions) + engine, err := setupContainerEngine(cmd) + if err != nil { + cobra.CompErrorln(err.Error()) + return nil, cobra.ShellCompDirectiveNoFileComp + } + networks, err := engine.NetworkList(registry.Context(), networkListOptions) if err != nil { cobra.CompErrorln(err.Error()) - return nil, cobra.ShellCompDirectiveError + return nil, cobra.ShellCompDirectiveNoFileComp } for _, n := range networks { @@ -266,7 +321,7 @@ func AutocompleteContainers(cmd *cobra.Command, args []string, toComplete string if !validCurrentCmdLine(cmd, args, toComplete) { return nil, cobra.ShellCompDirectiveNoFileComp } - return getContainers(toComplete, completeDefault) + return getContainers(cmd, toComplete, completeDefault) } // AutocompleteContainersCreated - Autocomplete only created container names. @@ -274,7 +329,7 @@ func AutocompleteContainersCreated(cmd *cobra.Command, args []string, toComplete if !validCurrentCmdLine(cmd, args, toComplete) { return nil, cobra.ShellCompDirectiveNoFileComp } - return getContainers(toComplete, completeDefault, "created") + return getContainers(cmd, toComplete, completeDefault, "created") } // AutocompleteContainersExited - Autocomplete only exited container names. @@ -282,7 +337,7 @@ func AutocompleteContainersExited(cmd *cobra.Command, args []string, toComplete if !validCurrentCmdLine(cmd, args, toComplete) { return nil, cobra.ShellCompDirectiveNoFileComp } - return getContainers(toComplete, completeDefault, "exited") + return getContainers(cmd, toComplete, completeDefault, "exited") } // AutocompleteContainersPaused - Autocomplete only paused container names. @@ -290,7 +345,7 @@ func AutocompleteContainersPaused(cmd *cobra.Command, args []string, toComplete if !validCurrentCmdLine(cmd, args, toComplete) { return nil, cobra.ShellCompDirectiveNoFileComp } - return getContainers(toComplete, completeDefault, "paused") + return getContainers(cmd, toComplete, completeDefault, "paused") } // AutocompleteContainersRunning - Autocomplete only running container names. @@ -298,7 +353,7 @@ func AutocompleteContainersRunning(cmd *cobra.Command, args []string, toComplete if !validCurrentCmdLine(cmd, args, toComplete) { return nil, cobra.ShellCompDirectiveNoFileComp } - return getContainers(toComplete, completeDefault, "running") + return getContainers(cmd, toComplete, completeDefault, "running") } // AutocompleteContainersStartable - Autocomplete only created and exited container names. @@ -306,7 +361,7 @@ func AutocompleteContainersStartable(cmd *cobra.Command, args []string, toComple if !validCurrentCmdLine(cmd, args, toComplete) { return nil, cobra.ShellCompDirectiveNoFileComp } - return getContainers(toComplete, completeDefault, "created", "exited") + return getContainers(cmd, toComplete, completeDefault, "created", "exited") } // AutocompletePods - Autocomplete all pod names. @@ -314,7 +369,7 @@ func AutocompletePods(cmd *cobra.Command, args []string, toComplete string) ([]s if !validCurrentCmdLine(cmd, args, toComplete) { return nil, cobra.ShellCompDirectiveNoFileComp } - return getPods(toComplete, completeDefault) + return getPods(cmd, toComplete, completeDefault) } // AutocompletePodsRunning - Autocomplete only running pod names. @@ -323,7 +378,7 @@ func AutocompletePodsRunning(cmd *cobra.Command, args []string, toComplete strin if !validCurrentCmdLine(cmd, args, toComplete) { return nil, cobra.ShellCompDirectiveNoFileComp } - return getPods(toComplete, completeDefault, "running", "degraded") + return getPods(cmd, toComplete, completeDefault, "running", "degraded") } // AutocompleteContainersAndPods - Autocomplete container names and pod names. @@ -331,8 +386,8 @@ func AutocompleteContainersAndPods(cmd *cobra.Command, args []string, toComplete if !validCurrentCmdLine(cmd, args, toComplete) { return nil, cobra.ShellCompDirectiveNoFileComp } - containers, _ := getContainers(toComplete, completeDefault) - pods, _ := getPods(toComplete, completeDefault) + containers, _ := getContainers(cmd, toComplete, completeDefault) + pods, _ := getPods(cmd, toComplete, completeDefault) return append(containers, pods...), cobra.ShellCompDirectiveNoFileComp } @@ -341,8 +396,8 @@ func AutocompleteContainersAndImages(cmd *cobra.Command, args []string, toComple if !validCurrentCmdLine(cmd, args, toComplete) { return nil, cobra.ShellCompDirectiveNoFileComp } - containers, _ := getContainers(toComplete, completeDefault) - images, _ := getImages(toComplete) + containers, _ := getContainers(cmd, toComplete, completeDefault) + images, _ := getImages(cmd, toComplete) return append(containers, images...), cobra.ShellCompDirectiveNoFileComp } @@ -351,7 +406,7 @@ func AutocompleteVolumes(cmd *cobra.Command, args []string, toComplete string) ( if !validCurrentCmdLine(cmd, args, toComplete) { return nil, cobra.ShellCompDirectiveNoFileComp } - return getVolumes(toComplete) + return getVolumes(cmd, toComplete) } // AutocompleteImages - Autocomplete images. @@ -359,7 +414,7 @@ func AutocompleteImages(cmd *cobra.Command, args []string, toComplete string) ([ if !validCurrentCmdLine(cmd, args, toComplete) { return nil, cobra.ShellCompDirectiveNoFileComp } - return getImages(toComplete) + return getImages(cmd, toComplete) } // AutocompleteCreateRun - Autocomplete only the fist argument as image and then do file completion. @@ -368,7 +423,7 @@ func AutocompleteCreateRun(cmd *cobra.Command, args []string, toComplete string) return nil, cobra.ShellCompDirectiveNoFileComp } if len(args) < 1 { - return getImages(toComplete) + return getImages(cmd, toComplete) } // TODO: add path completion for files in the image return nil, cobra.ShellCompDirectiveDefault @@ -387,7 +442,7 @@ func AutocompleteNetworks(cmd *cobra.Command, args []string, toComplete string) if !validCurrentCmdLine(cmd, args, toComplete) { return nil, cobra.ShellCompDirectiveNoFileComp } - return getNetworks(toComplete) + return getNetworks(cmd, toComplete) } // AutocompleteCpCommand - Autocomplete podman cp command args. @@ -396,7 +451,7 @@ func AutocompleteCpCommand(cmd *cobra.Command, args []string, toComplete string) return nil, cobra.ShellCompDirectiveNoFileComp } if len(args) < 2 { - containers, _ := getContainers(toComplete, completeDefault) + containers, _ := getContainers(cmd, toComplete, completeDefault) for _, container := range containers { // TODO: Add path completion for inside the container if possible if strings.HasPrefix(container, toComplete) { @@ -410,6 +465,37 @@ func AutocompleteCpCommand(cmd *cobra.Command, args []string, toComplete string) return nil, cobra.ShellCompDirectiveNoFileComp } +// AutocompleteNetworkConnectCmd - Autocomplete podman network connect/disconnect command args. +func AutocompleteNetworkConnectCmd(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { + if len(args) == 0 { + return getNetworks(cmd, toComplete) + } + if len(args) == 1 { + return getContainers(cmd, toComplete, completeDefault) + } + // don't complete more than 2 args + return nil, cobra.ShellCompDirectiveNoFileComp +} + +// AutocompleteTopCmd - Autocomplete podman top/pod top command args. +func AutocompleteTopCmd(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { + latest := cmd.Flags().Lookup("latest") + // only complete containers/pods as first arg if latest is not set + if len(args) == 0 && (latest == nil || !latest.Changed) { + if cmd.Parent().Name() == "pod" { + // need to complete pods since we are using pod top + return getPods(cmd, toComplete, completeDefault) + } + return getContainers(cmd, toComplete, completeDefault) + } + descriptors, err := util.GetContainerPidInformationDescriptors() + if err != nil { + cobra.CompErrorln(err.Error()) + return nil, cobra.ShellCompDirectiveNoFileComp + } + return descriptors, cobra.ShellCompDirectiveNoFileComp +} + // AutocompleteSystemConnections - Autocomplete system connections. func AutocompleteSystemConnections(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { if !validCurrentCmdLine(cmd, args, toComplete) { @@ -419,7 +505,7 @@ func AutocompleteSystemConnections(cmd *cobra.Command, args []string, toComplete cfg, err := config.ReadCustomConfig() if err != nil { cobra.CompErrorln(err.Error()) - return nil, cobra.ShellCompDirectiveError + return nil, cobra.ShellCompDirectiveNoFileComp } for k, v := range cfg.Engine.ServiceDestinations { @@ -464,7 +550,7 @@ func AutocompleteCreateAttach(cmd *cobra.Command, args []string, toComplete stri // -> host,container:[name],ns:[path],private func AutocompleteNamespace(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { kv := keyValueCompletion{ - "container:": func(s string) ([]string, cobra.ShellCompDirective) { return getContainers(s, completeDefault) }, + "container:": func(s string) ([]string, cobra.ShellCompDirective) { return getContainers(cmd, s, completeDefault) }, "ns:": func(s string) ([]string, cobra.ShellCompDirective) { return nil, cobra.ShellCompDirectiveDefault }, "host": nil, "private": nil, @@ -567,7 +653,8 @@ func AutocompleteUserFlag(cmd *cobra.Command, args []string, toComplete string) // but at this point we don't know the image. file, err := os.Open("/etc/group") if err != nil { - return nil, cobra.ShellCompDirectiveError + cobra.CompErrorln(err.Error()) + return nil, cobra.ShellCompDirectiveNoFileComp } defer file.Close() @@ -583,7 +670,8 @@ func AutocompleteUserFlag(cmd *cobra.Command, args []string, toComplete string) } } if err = scanner.Err(); err != nil { - return nil, cobra.ShellCompDirectiveError + cobra.CompErrorln(err.Error()) + return nil, cobra.ShellCompDirectiveNoFileComp } return groups, cobra.ShellCompDirectiveNoFileComp } @@ -592,7 +680,8 @@ func AutocompleteUserFlag(cmd *cobra.Command, args []string, toComplete string) // but at this point we don't know the image. file, err := os.Open("/etc/passwd") if err != nil { - return nil, cobra.ShellCompDirectiveError + cobra.CompErrorln(err.Error()) + return nil, cobra.ShellCompDirectiveNoFileComp } defer file.Close() @@ -607,7 +696,7 @@ func AutocompleteUserFlag(cmd *cobra.Command, args []string, toComplete string) } } if err = scanner.Err(); err != nil { - return nil, cobra.ShellCompDirectiveError + return nil, cobra.ShellCompDirectiveNoFileComp } return users, cobra.ShellCompDirectiveNoSpace } @@ -623,7 +712,7 @@ func AutocompleteMountFlag(cmd *cobra.Command, args []string, toComplete string) // AutocompleteVolumeFlag - Autocomplete volume flag options. // -> volumes and paths func AutocompleteVolumeFlag(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { - volumes, _ := getVolumes(toComplete) + volumes, _ := getVolumes(cmd, toComplete) directive := cobra.ShellCompDirectiveNoSpace | cobra.ShellCompDirectiveDefault if strings.Contains(toComplete, ":") { // add space after second path @@ -641,8 +730,22 @@ func AutocompleteJSONFormat(cmd *cobra.Command, args []string, toComplete string // AutocompleteEventFilter - Autocomplete event filter flag options. // -> "container=", "event=", "image=", "pod=", "volume=", "type=" func AutocompleteEventFilter(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { - filters := []string{"container=", "event=", "image=", "pod=", "volume=", "type="} - return filters, cobra.ShellCompDirectiveNoSpace + eventTypes := func(_ string) ([]string, cobra.ShellCompDirective) { + return []string{"attach", "checkpoint", "cleanup", "commit", "create", "exec", + "export", "import", "init", "kill", "mount", "pause", "prune", "remove", + "restart", "restore", "start", "stop", "sync", "unmount", "unpause", + "pull", "push", "save", "tag", "untag", "refresh", "renumber", + }, cobra.ShellCompDirectiveNoFileComp + } + kv := keyValueCompletion{ + "container=": func(s string) ([]string, cobra.ShellCompDirective) { return getContainers(cmd, s, completeDefault) }, + "image=": func(s string) ([]string, cobra.ShellCompDirective) { return getImages(cmd, s) }, + "pod=": func(s string) ([]string, cobra.ShellCompDirective) { return getPods(cmd, s, completeDefault) }, + "volume=": func(s string) ([]string, cobra.ShellCompDirective) { return getVolumes(cmd, s) }, + "event=": eventTypes, + "type=": eventTypes, + } + return completeKeyValues(toComplete, kv) } // AutocompleteSystemdRestartOptions - Autocomplete systemd restart options. @@ -753,15 +856,15 @@ var containerStatuses = []string{"created", "running", "paused", "stopped", "exi // AutocompletePsFilters - Autocomplete ps filter options. func AutocompletePsFilters(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { kv := keyValueCompletion{ - "id=": func(s string) ([]string, cobra.ShellCompDirective) { return getContainers(s, completeIDs) }, - "name=": func(s string) ([]string, cobra.ShellCompDirective) { return getContainers(s, completeNames) }, + "id=": func(s string) ([]string, cobra.ShellCompDirective) { return getContainers(cmd, s, completeIDs) }, + "name=": func(s string) ([]string, cobra.ShellCompDirective) { return getContainers(cmd, s, completeNames) }, "status=": func(_ string) ([]string, cobra.ShellCompDirective) { return containerStatuses, cobra.ShellCompDirectiveNoFileComp }, - "ancestor": func(s string) ([]string, cobra.ShellCompDirective) { return getImages(s) }, - "before=": func(s string) ([]string, cobra.ShellCompDirective) { return getContainers(s, completeDefault) }, - "since=": func(s string) ([]string, cobra.ShellCompDirective) { return getContainers(s, completeDefault) }, - "volume=": func(s string) ([]string, cobra.ShellCompDirective) { return getVolumes(s) }, + "ancestor": func(s string) ([]string, cobra.ShellCompDirective) { return getImages(cmd, s) }, + "before=": func(s string) ([]string, cobra.ShellCompDirective) { return getContainers(cmd, s, completeDefault) }, + "since=": func(s string) ([]string, cobra.ShellCompDirective) { return getContainers(cmd, s, completeDefault) }, + "volume=": func(s string) ([]string, cobra.ShellCompDirective) { return getVolumes(cmd, s) }, "health=": func(_ string) ([]string, cobra.ShellCompDirective) { return []string{define.HealthCheckHealthy, define.HealthCheckUnhealthy}, cobra.ShellCompDirectiveNoFileComp @@ -776,14 +879,14 @@ func AutocompletePsFilters(cmd *cobra.Command, args []string, toComplete string) // AutocompletePodPsFilters - Autocomplete pod ps filter options. func AutocompletePodPsFilters(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { kv := keyValueCompletion{ - "id=": func(s string) ([]string, cobra.ShellCompDirective) { return getPods(s, completeIDs) }, - "name=": func(s string) ([]string, cobra.ShellCompDirective) { return getPods(s, completeNames) }, + "id=": func(s string) ([]string, cobra.ShellCompDirective) { return getPods(cmd, s, completeIDs) }, + "name=": func(s string) ([]string, cobra.ShellCompDirective) { return getPods(cmd, s, completeNames) }, "status=": func(_ string) ([]string, cobra.ShellCompDirective) { return []string{"stopped", "running", "paused", "exited", "dead", "created", "degraded"}, cobra.ShellCompDirectiveNoFileComp }, - "ctr-ids=": func(s string) ([]string, cobra.ShellCompDirective) { return getContainers(s, completeIDs) }, - "ctr-names=": func(s string) ([]string, cobra.ShellCompDirective) { return getContainers(s, completeNames) }, + "ctr-ids=": func(s string) ([]string, cobra.ShellCompDirective) { return getContainers(cmd, s, completeIDs) }, + "ctr-names=": func(s string) ([]string, cobra.ShellCompDirective) { return getContainers(cmd, s, completeNames) }, "ctr-number=": nil, "ctr-status=": func(_ string) ([]string, cobra.ShellCompDirective) { return containerStatuses, cobra.ShellCompDirectiveNoFileComp @@ -792,3 +895,47 @@ func AutocompletePodPsFilters(cmd *cobra.Command, args []string, toComplete stri } return completeKeyValues(toComplete, kv) } + +// AutocompleteImageFilters - Autocomplete image ls --filter options. +func AutocompleteImageFilters(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { + getBool := func(_ string) ([]string, cobra.ShellCompDirective) { + return []string{"true", "false"}, cobra.ShellCompDirectiveNoFileComp + } + getImg := func(s string) ([]string, cobra.ShellCompDirective) { return getImages(cmd, s) } + kv := keyValueCompletion{ + "before=": getImg, + "since=": getImg, + "label=": nil, + "reference=": nil, + "dangling=": getBool, + "readonly=": getBool, + } + return completeKeyValues(toComplete, kv) +} + +// AutocompleteNetworkFilters - Autocomplete network ls --filter options. +func AutocompleteNetworkFilters(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { + kv := keyValueCompletion{ + "name=": func(s string) ([]string, cobra.ShellCompDirective) { return getNetworks(cmd, s) }, + "plugin=": nil, + } + return completeKeyValues(toComplete, kv) +} + +// AutocompleteVolumeFilters - Autocomplete volume ls --filter options. +func AutocompleteVolumeFilters(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { + local := func(_ string) ([]string, cobra.ShellCompDirective) { + return []string{"local"}, cobra.ShellCompDirectiveNoFileComp + } + kv := keyValueCompletion{ + "name=": func(s string) ([]string, cobra.ShellCompDirective) { return getVolumes(cmd, s) }, + "driver=": local, + "scope=": local, + "label=": nil, + "opt=": nil, + "dangling=": func(_ string) ([]string, cobra.ShellCompDirective) { + return []string{"true", "false"}, cobra.ShellCompDirectiveNoFileComp + }, + } + return completeKeyValues(toComplete, kv) +} diff --git a/cmd/podman/common/create.go b/cmd/podman/common/create.go index ab3a984f0..599b430ea 100644 --- a/cmd/podman/common/create.go +++ b/cmd/podman/common/create.go @@ -84,7 +84,7 @@ func DefineCreateFlags(cmd *cobra.Command, cf *ContainerCLIOpts) { cgroupsFlagName := "cgroups" createFlags.StringVar( &cf.CGroupsMode, - cgroupsFlagName, containerConfig.Cgroups(), + cgroupsFlagName, cgroupConfig(), `control container cgroup configuration ("enabled"|"disabled"|"no-conmon"|"split")`, ) _ = cmd.RegisterFlagCompletionFunc(cgroupsFlagName, AutocompleteCgroupMode) @@ -180,7 +180,7 @@ func DefineCreateFlags(cmd *cobra.Command, cf *ContainerCLIOpts) { deviceFlagName := "device" createFlags.StringSliceVar( &cf.Devices, - deviceFlagName, containerConfig.Devices(), + deviceFlagName, devices(), fmt.Sprintf("Add a host device to the container"), ) _ = cmd.RegisterFlagCompletionFunc(deviceFlagName, completion.AutocompleteDefault) @@ -238,7 +238,7 @@ func DefineCreateFlags(cmd *cobra.Command, cf *ContainerCLIOpts) { envFlagName := "env" createFlags.StringArrayP( - envFlagName, "e", containerConfig.Env(), + envFlagName, "e", env(), "Set environment variables in container", ) _ = cmd.RegisterFlagCompletionFunc(envFlagName, completion.AutocompleteNone) @@ -357,7 +357,7 @@ func DefineCreateFlags(cmd *cobra.Command, cf *ContainerCLIOpts) { initPathFlagName := "init-path" createFlags.StringVar( &cf.InitPath, - initPathFlagName, containerConfig.InitPath(), + initPathFlagName, initPath(), // Do not use the Value field for setting the default value to determine user input (i.e., non-empty string) fmt.Sprintf("Path to the container-init binary"), ) @@ -508,7 +508,7 @@ func DefineCreateFlags(cmd *cobra.Command, cf *ContainerCLIOpts) { pidsLimitFlagName := "pids-limit" createFlags.Int64( - pidsLimitFlagName, containerConfig.PidsLimit(), + pidsLimitFlagName, pidsLimit(), "Tune container pids limit (set 0 for unlimited, -1 for server defaults)", ) _ = cmd.RegisterFlagCompletionFunc(pidsLimitFlagName, completion.AutocompleteNone) @@ -543,7 +543,7 @@ func DefineCreateFlags(cmd *cobra.Command, cf *ContainerCLIOpts) { pullFlagName := "pull" createFlags.StringVar( &cf.Pull, - pullFlagName, containerConfig.Engine.PullPolicy, + pullFlagName, policy(), `Pull image before creating ("always"|"missing"|"never")`, ) _ = cmd.RegisterFlagCompletionFunc(pullFlagName, AutocompletePullOption) @@ -606,7 +606,7 @@ func DefineCreateFlags(cmd *cobra.Command, cf *ContainerCLIOpts) { shmSizeFlagName := "shm-size" createFlags.String( - shmSizeFlagName, containerConfig.ShmSize(), + shmSizeFlagName, shmSize(), "Size of /dev/shm "+sizeWithUnitFormat, ) _ = cmd.RegisterFlagCompletionFunc(shmSizeFlagName, completion.AutocompleteNone) @@ -715,7 +715,7 @@ func DefineCreateFlags(cmd *cobra.Command, cf *ContainerCLIOpts) { ulimitFlagName := "ulimit" createFlags.StringSliceVar( &cf.Ulimit, - ulimitFlagName, containerConfig.Ulimits(), + ulimitFlagName, ulimits(), "Ulimit options", ) _ = cmd.RegisterFlagCompletionFunc(ulimitFlagName, completion.AutocompleteNone) @@ -753,7 +753,7 @@ func DefineCreateFlags(cmd *cobra.Command, cf *ContainerCLIOpts) { volumeFlagName := "volume" createFlags.StringArrayVarP( &cf.Volume, - volumeFlagName, "v", containerConfig.Volumes(), + volumeFlagName, "v", volumes(), "Bind mount a volume into the container", ) _ = cmd.RegisterFlagCompletionFunc(volumeFlagName, AutocompleteVolumeFlag) diff --git a/cmd/podman/common/create_opts.go b/cmd/podman/common/create_opts.go index 4b52663c3..6dc43dbc6 100644 --- a/cmd/podman/common/create_opts.go +++ b/cmd/podman/common/create_opts.go @@ -6,6 +6,7 @@ import ( "strconv" "strings" + "github.com/containers/podman/v2/cmd/podman/registry" "github.com/containers/podman/v2/pkg/api/handlers" "github.com/containers/podman/v2/pkg/cgroups" "github.com/containers/podman/v2/pkg/domain/entities" @@ -133,10 +134,9 @@ func stringMaptoArray(m map[string]string) []string { // a specgen spec. func ContainerCreateToContainerCLIOpts(cc handlers.CreateContainerConfig, cgroupsManager string) (*ContainerCLIOpts, []string, error) { var ( - aliases []string capAdd []string cappDrop []string - entrypoint string + entrypoint *string init bool specPorts []specgen.PortMapping ) @@ -180,13 +180,14 @@ func ContainerCreateToContainerCLIOpts(cc handlers.CreateContainerConfig, cgroup // marshall it to json; otherwise it should just be the string // value if len(cc.Config.Entrypoint) > 0 { - entrypoint = cc.Config.Entrypoint[0] + entrypoint = &cc.Config.Entrypoint[0] if len(cc.Config.Entrypoint) > 1 { b, err := json.Marshal(cc.Config.Entrypoint) if err != nil { return nil, nil, err } - entrypoint = string(b) + var jsonString = string(b) + entrypoint = &jsonString } } @@ -210,7 +211,7 @@ func ContainerCreateToContainerCLIOpts(cc handlers.CreateContainerConfig, cgroup mounts = append(mounts, mount) } - //volumes + // volumes volumes := make([]string, 0, len(cc.Config.Volumes)) for v := range cc.Config.Volumes { volumes = append(volumes, v) @@ -240,16 +241,6 @@ func ContainerCreateToContainerCLIOpts(cc handlers.CreateContainerConfig, cgroup } } - // network names - endpointsConfig := cc.NetworkingConfig.EndpointsConfig - cniNetworks := make([]string, 0, len(endpointsConfig)) - for netName, endpoint := range endpointsConfig { - cniNetworks = append(cniNetworks, netName) - if len(endpoint.Aliases) > 0 { - aliases = append(aliases, endpoint.Aliases...) - } - } - // netMode nsmode, _, err := specgen.ParseNetworkNamespace(cc.HostConfig.NetworkMode.NetworkName()) if err != nil { @@ -266,8 +257,6 @@ func ContainerCreateToContainerCLIOpts(cc handlers.CreateContainerConfig, cgroup // defined when there is only one network. netInfo := entities.NetOptions{ AddHosts: cc.HostConfig.ExtraHosts, - Aliases: aliases, - CNINetworks: cniNetworks, DNSOptions: cc.HostConfig.DNSOptions, DNSSearch: cc.HostConfig.DNSSearch, DNSServers: dns, @@ -275,31 +264,58 @@ func ContainerCreateToContainerCLIOpts(cc handlers.CreateContainerConfig, cgroup PublishPorts: specPorts, } - // static IP and MAC - if len(endpointsConfig) == 1 { - for _, ep := range endpointsConfig { - // if IP address is provided - if len(ep.IPAddress) > 0 { - staticIP := net.ParseIP(ep.IPAddress) - netInfo.StaticIP = &staticIP + // network names + switch { + case len(cc.NetworkingConfig.EndpointsConfig) > 0: + var aliases []string + + endpointsConfig := cc.NetworkingConfig.EndpointsConfig + cniNetworks := make([]string, 0, len(endpointsConfig)) + for netName, endpoint := range endpointsConfig { + + cniNetworks = append(cniNetworks, netName) + + if endpoint == nil { + continue } - // If MAC address is provided - if len(ep.MacAddress) > 0 { - staticMac, err := net.ParseMAC(ep.MacAddress) - if err != nil { - return nil, nil, err + if len(endpoint.Aliases) > 0 { + aliases = append(aliases, endpoint.Aliases...) + } + } + + // static IP and MAC + if len(endpointsConfig) == 1 { + for _, ep := range endpointsConfig { + if ep == nil { + continue + } + // if IP address is provided + if len(ep.IPAddress) > 0 { + staticIP := net.ParseIP(ep.IPAddress) + netInfo.StaticIP = &staticIP + } + // If MAC address is provided + if len(ep.MacAddress) > 0 { + staticMac, err := net.ParseMAC(ep.MacAddress) + if err != nil { + return nil, nil, err + } + netInfo.StaticMAC = &staticMac } - netInfo.StaticMAC = &staticMac + break } - break } + netInfo.Aliases = aliases + netInfo.CNINetworks = cniNetworks + case len(cc.HostConfig.NetworkMode) > 0: + netInfo.CNINetworks = []string{string(cc.HostConfig.NetworkMode)} } // Note: several options here are marked as "don't need". this is based // on speculation by Matt and I. We think that these come into play later // like with start. We believe this is just a difference in podman/compat cliOpts := ContainerCLIOpts{ - //Attach: nil, // dont need? + // Attach: nil, // dont need? Authfile: "", CapAdd: append(capAdd, cc.HostConfig.CapAdd...), CapDrop: append(cappDrop, cc.HostConfig.CapDrop...), @@ -310,18 +326,18 @@ func ContainerCreateToContainerCLIOpts(cc handlers.CreateContainerConfig, cgroup CPURTPeriod: uint64(cc.HostConfig.CPURealtimePeriod), CPURTRuntime: cc.HostConfig.CPURealtimeRuntime, CPUShares: uint64(cc.HostConfig.CPUShares), - //CPUS: 0, // dont need? + // CPUS: 0, // dont need? CPUSetCPUs: cc.HostConfig.CpusetCpus, CPUSetMems: cc.HostConfig.CpusetMems, - //Detach: false, // dont need - //DetachKeys: "", // dont need + // Detach: false, // dont need + // DetachKeys: "", // dont need Devices: devices, DeviceCGroupRule: nil, DeviceReadBPs: readBps, DeviceReadIOPs: readIops, DeviceWriteBPs: writeBps, DeviceWriteIOPs: writeIops, - Entrypoint: &entrypoint, + Entrypoint: entrypoint, Env: cc.Config.Env, Expose: expose, GroupAdd: cc.HostConfig.GroupAdd, @@ -436,7 +452,70 @@ func ContainerCreateToContainerCLIOpts(cc handlers.CreateContainerConfig, cgroup } // specgen assumes the image name is arg[0] - cmd := []string{cc.Image} + cmd := []string{cc.Config.Image} cmd = append(cmd, cc.Config.Cmd...) return &cliOpts, cmd, nil } + +func ulimits() []string { + if !registry.IsRemote() { + return containerConfig.Ulimits() + } + return nil +} + +func cgroupConfig() string { + if !registry.IsRemote() { + return containerConfig.Cgroups() + } + return "" +} + +func devices() []string { + if !registry.IsRemote() { + return containerConfig.Devices() + } + return nil +} + +func env() []string { + if !registry.IsRemote() { + return containerConfig.Env() + } + return nil +} + +func initPath() string { + if !registry.IsRemote() { + return containerConfig.InitPath() + } + return "" +} + +func pidsLimit() int64 { + if !registry.IsRemote() { + return containerConfig.PidsLimit() + } + return -1 +} + +func policy() string { + if !registry.IsRemote() { + return containerConfig.Engine.PullPolicy + } + return "" +} + +func shmSize() string { + if !registry.IsRemote() { + return containerConfig.ShmSize() + } + return "" +} + +func volumes() []string { + if !registry.IsRemote() { + return containerConfig.Volumes() + } + return nil +} diff --git a/cmd/podman/common/createparse.go b/cmd/podman/common/createparse.go index 09ee5aa0c..3a69f11b6 100644 --- a/cmd/podman/common/createparse.go +++ b/cmd/podman/common/createparse.go @@ -9,7 +9,7 @@ import ( // by validate must not need any state information on the flag (i.e. changed) func (c *ContainerCLIOpts) validate() error { var () - if c.Rm && c.Restart != "" && c.Restart != "no" { + if c.Rm && (c.Restart != "" && c.Restart != "no" && c.Restart != "on-failure") { return errors.Errorf(`the --rm option conflicts with --restart, when the restartPolicy is not "" and "no"`) } diff --git a/cmd/podman/common/netflags.go b/cmd/podman/common/netflags.go index cae52ccaa..898d65bd0 100644 --- a/cmd/podman/common/netflags.go +++ b/cmd/podman/common/netflags.go @@ -59,8 +59,8 @@ func DefineNetFlags(cmd *cobra.Command) { _ = cmd.RegisterFlagCompletionFunc(macAddressFlagName, completion.AutocompleteNone) networkFlagName := "network" - netFlags.String( - networkFlagName, containerConfig.NetNS(), + netFlags.StringArray( + networkFlagName, []string{containerConfig.NetNS()}, "Connect a container to a network", ) _ = cmd.RegisterFlagCompletionFunc(networkFlagName, AutocompleteNetworks) @@ -194,25 +194,29 @@ func NetFlagsToNetOptions(cmd *cobra.Command) (*entities.NetOptions, error) { } if cmd.Flags().Changed("network") { - network, err := cmd.Flags().GetString("network") + networks, err := cmd.Flags().GetStringArray("network") if err != nil { return nil, err } + for i, network := range networks { + parts := strings.SplitN(network, ":", 2) - parts := strings.SplitN(network, ":", 2) - - ns, cniNets, err := specgen.ParseNetworkNamespace(network) - if err != nil { - return nil, err - } + ns, cniNets, err := specgen.ParseNetworkNamespace(network) + if err != nil { + return nil, err + } + if i > 0 && (len(cniNets) == 0 || len(opts.CNINetworks) == 0) { + return nil, errors.Errorf("network conflict between type %s and %s", opts.Network.NSMode, ns.NSMode) + } - if len(parts) > 1 { - opts.NetworkOptions = make(map[string][]string) - opts.NetworkOptions[parts[0]] = strings.Split(parts[1], ",") - cniNets = nil + if len(parts) > 1 { + opts.NetworkOptions = make(map[string][]string) + opts.NetworkOptions[parts[0]] = strings.Split(parts[1], ",") + cniNets = nil + } + opts.Network = ns + opts.CNINetworks = append(opts.CNINetworks, cniNets...) } - opts.Network = ns - opts.CNINetworks = cniNets } aliases, err := cmd.Flags().GetStringSlice("network-alias") diff --git a/cmd/podman/common/volumes.go b/cmd/podman/common/volumes.go index b3c160ddf..0468f15e0 100644 --- a/cmd/podman/common/volumes.go +++ b/cmd/podman/common/volumes.go @@ -10,7 +10,6 @@ import ( "github.com/containers/podman/v2/pkg/util" spec "github.com/opencontainers/runtime-spec/specs-go" "github.com/pkg/errors" - "github.com/sirupsen/logrus" ) const ( @@ -45,7 +44,7 @@ func parseVolumes(volumeFlag, mountFlag, tmpfsFlag []string, addReadOnlyTmpfs bo } // Next --volumes flag. - volumeMounts, volumeVolumes, overlayVolumes, err := getVolumeMounts(volumeFlag) + volumeMounts, volumeVolumes, overlayVolumes, err := specgen.GenVolumeMounts(volumeFlag) if err != nil { return nil, nil, nil, nil, err } @@ -594,105 +593,6 @@ func getImageVolume(args []string) (*specgen.ImageVolume, error) { return newVolume, nil } -func getVolumeMounts(volumeFlag []string) (map[string]spec.Mount, map[string]*specgen.NamedVolume, map[string]*specgen.OverlayVolume, error) { - mounts := make(map[string]spec.Mount) - volumes := make(map[string]*specgen.NamedVolume) - overlayVolumes := make(map[string]*specgen.OverlayVolume) - - volumeFormatErr := errors.Errorf("incorrect volume format, should be [host-dir:]ctr-dir[:option]") - - for _, vol := range volumeFlag { - var ( - options []string - src string - dest string - err error - ) - - splitVol := strings.Split(vol, ":") - if len(splitVol) > 3 { - return nil, nil, nil, errors.Wrapf(volumeFormatErr, vol) - } - - src = splitVol[0] - if len(splitVol) == 1 { - // This is an anonymous named volume. Only thing given - // is destination. - // Name/source will be blank, and populated by libpod. - src = "" - dest = splitVol[0] - } else if len(splitVol) > 1 { - dest = splitVol[1] - } - if len(splitVol) > 2 { - if options, err = parse.ValidateVolumeOpts(strings.Split(splitVol[2], ",")); err != nil { - return nil, nil, nil, err - } - } - - // Do not check source dir for anonymous volumes - if len(splitVol) > 1 { - if err := parse.ValidateVolumeHostDir(src); err != nil { - return nil, nil, nil, err - } - } - if err := parse.ValidateVolumeCtrDir(dest); err != nil { - return nil, nil, nil, err - } - - cleanDest := filepath.Clean(dest) - - if strings.HasPrefix(src, "/") || strings.HasPrefix(src, ".") { - // This is not a named volume - overlayFlag := false - for _, o := range options { - if o == "O" { - overlayFlag = true - if len(options) > 1 { - return nil, nil, nil, errors.New("can't use 'O' with other options") - } - } - } - if overlayFlag { - // This is a overlay volume - newOverlayVol := new(specgen.OverlayVolume) - newOverlayVol.Destination = cleanDest - newOverlayVol.Source = src - if _, ok := overlayVolumes[newOverlayVol.Destination]; ok { - return nil, nil, nil, errors.Wrapf(errDuplicateDest, newOverlayVol.Destination) - } - overlayVolumes[newOverlayVol.Destination] = newOverlayVol - } else { - newMount := spec.Mount{ - Destination: cleanDest, - Type: string(TypeBind), - Source: src, - Options: options, - } - if _, ok := mounts[newMount.Destination]; ok { - return nil, nil, nil, errors.Wrapf(errDuplicateDest, newMount.Destination) - } - mounts[newMount.Destination] = newMount - } - } else { - // This is a named volume - newNamedVol := new(specgen.NamedVolume) - newNamedVol.Name = src - newNamedVol.Dest = cleanDest - newNamedVol.Options = options - - if _, ok := volumes[newNamedVol.Dest]; ok { - return nil, nil, nil, errors.Wrapf(errDuplicateDest, newNamedVol.Dest) - } - volumes[newNamedVol.Dest] = newNamedVol - } - - logrus.Debugf("User mount %s:%s options %v", src, dest, options) - } - - return mounts, volumes, overlayVolumes, nil -} - // GetTmpfsMounts creates spec.Mount structs for user-requested tmpfs mounts func getTmpfsMounts(tmpfsFlag []string) (map[string]spec.Mount, error) { m := make(map[string]spec.Mount) diff --git a/cmd/podman/containers/ps.go b/cmd/podman/containers/ps.go index a1a41ae08..6f84cf9b8 100644 --- a/cmd/podman/containers/ps.go +++ b/cmd/podman/containers/ps.go @@ -29,15 +29,25 @@ var ( psDescription = "Prints out information about the containers" psCommand = &cobra.Command{ Use: "ps [options]", - Args: validate.NoArgs, Short: "List containers", Long: psDescription, RunE: ps, + Args: validate.NoArgs, ValidArgsFunction: completion.AutocompleteNone, Example: `podman ps -a podman ps -a --format "{{.ID}} {{.Image}} {{.Labels}} {{.Mounts}}" podman ps --size --sort names`, } + + psContainerCommand = &cobra.Command{ + Use: psCommand.Use, + Short: psCommand.Short, + Long: psCommand.Long, + RunE: psCommand.RunE, + Args: psCommand.Args, + ValidArgsFunction: psCommand.ValidArgsFunction, + Example: strings.ReplaceAll(psCommand.Example, "podman ps", "podman container ps"), + } ) var ( listOpts = entities.ContainerListOptions{ @@ -54,6 +64,14 @@ func init() { }) listFlagSet(psCommand) validate.AddLatestFlag(psCommand, &listOpts.Latest) + + registry.Commands = append(registry.Commands, registry.CliCommand{ + Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, + Command: psContainerCommand, + Parent: containerCmd, + }) + listFlagSet(psContainerCommand) + validate.AddLatestFlag(psContainerCommand, &listOpts.Latest) } func listFlagSet(cmd *cobra.Command) { diff --git a/cmd/podman/containers/top.go b/cmd/podman/containers/top.go index 3eb6d2af2..e691f527a 100644 --- a/cmd/podman/containers/top.go +++ b/cmd/podman/containers/top.go @@ -18,12 +18,10 @@ import ( ) var ( - topDescription = `Similar to system "top" command. - - Specify format descriptors to alter the output. - - Running "podman top -l pid pcpu seccomp" will print the process ID, the CPU percentage and the seccomp mode of each process of the latest container.` + topDescription = `Display the running processes of a container. + The top command extends the ps(1) compatible AIX descriptors with container-specific ones as shown below. In the presence of ps(1) specific flags (e.g, -eo), Podman will execute ps(1) inside the container. +` topOptions = entities.TopOptions{} topCommand = &cobra.Command{ @@ -32,7 +30,7 @@ var ( Long: topDescription, RunE: top, Args: cobra.ArbitraryArgs, - ValidArgsFunction: common.AutocompleteContainersRunning, + ValidArgsFunction: common.AutocompleteTopCmd, Example: `podman top ctrID podman top --latest podman top ctrID pid seccomp args %C diff --git a/cmd/podman/images/list.go b/cmd/podman/images/list.go index 4692699f2..bcb31e6ee 100644 --- a/cmd/podman/images/list.go +++ b/cmd/podman/images/list.go @@ -79,8 +79,7 @@ func imageListFlagSet(cmd *cobra.Command) { filterFlagName := "filter" flags.StringSliceVarP(&listOptions.Filter, filterFlagName, "f", []string{}, "Filter output based on conditions provided (default [])") - // TODO: add completion function for filters - _ = cmd.RegisterFlagCompletionFunc(filterFlagName, completion.AutocompleteNone) + _ = cmd.RegisterFlagCompletionFunc(filterFlagName, common.AutocompleteImageFilters) formatFlagName := "format" flags.StringVar(&listFlag.format, formatFlagName, "", "Change the output format to JSON or a Go template") diff --git a/cmd/podman/images/load.go b/cmd/podman/images/load.go index a7884f4c5..a24f46781 100644 --- a/cmd/podman/images/load.go +++ b/cmd/podman/images/load.go @@ -23,7 +23,7 @@ var ( loadDescription = "Loads an image from a locally stored archive (tar file) into container storage." loadCommand = &cobra.Command{ Use: "load [options] [NAME[:TAG]]", - Short: "Load an image from container archive", + Short: "Load image(s) from a tar archive", Long: loadDescription, RunE: load, Args: cobra.MaximumNArgs(1), diff --git a/cmd/podman/networks/connect.go b/cmd/podman/networks/connect.go index a7636688c..8afc0c7c0 100644 --- a/cmd/podman/networks/connect.go +++ b/cmd/podman/networks/connect.go @@ -17,7 +17,7 @@ var ( RunE: networkConnect, Example: `podman network connect web secondary`, Args: cobra.ExactArgs(2), - ValidArgsFunction: common.AutocompleteNetworks, + ValidArgsFunction: common.AutocompleteNetworkConnectCmd, } ) diff --git a/cmd/podman/networks/disconnect.go b/cmd/podman/networks/disconnect.go index 598c23a1c..a30315774 100644 --- a/cmd/podman/networks/disconnect.go +++ b/cmd/podman/networks/disconnect.go @@ -17,7 +17,7 @@ var ( RunE: networkDisconnect, Example: `podman network disconnect web secondary`, Args: cobra.ExactArgs(2), - ValidArgsFunction: common.AutocompleteNetworks, + ValidArgsFunction: common.AutocompleteNetworkConnectCmd, } ) diff --git a/cmd/podman/networks/list.go b/cmd/podman/networks/list.go index f2a5a431a..dcba3f186 100644 --- a/cmd/podman/networks/list.go +++ b/cmd/podman/networks/list.go @@ -46,7 +46,7 @@ func networkListFlags(flags *pflag.FlagSet) { filterFlagName := "filter" flags.StringVarP(&networkListOptions.Filter, filterFlagName, "", "", "Provide filter values (e.g. 'name=podman')") - _ = networklistCommand.RegisterFlagCompletionFunc(filterFlagName, completion.AutocompleteNone) + _ = networklistCommand.RegisterFlagCompletionFunc(filterFlagName, common.AutocompleteNetworkFilters) } diff --git a/cmd/podman/networks/rm.go b/cmd/podman/networks/rm.go index 34e756a3a..1504d9385 100644 --- a/cmd/podman/networks/rm.go +++ b/cmd/podman/networks/rm.go @@ -18,6 +18,7 @@ var ( networkrmDescription = `Remove networks` networkrmCommand = &cobra.Command{ Use: "rm [options] NETWORK [NETWORK...]", + Aliases: []string{"remove"}, Short: "network rm", Long: networkrmDescription, RunE: networkRm, diff --git a/cmd/podman/pods/create.go b/cmd/podman/pods/create.go index 449d60bb9..5b0aa2fe4 100644 --- a/cmd/podman/pods/create.go +++ b/cmd/podman/pods/create.go @@ -94,7 +94,7 @@ func init() { flags.StringVar(&podIDFile, podIDFileFlagName, "", "Write the pod ID to the file") _ = createCommand.RegisterFlagCompletionFunc(podIDFileFlagName, completion.AutocompleteDefault) - flags.BoolVar(&replace, "replace", false, "If a pod with the same exists, replace it") + flags.BoolVar(&replace, "replace", false, "If a pod with the same name exists, replace it") shareFlagName := "share" flags.StringVar(&share, shareFlagName, specgen.DefaultKernelNamespaces, "A comma delimited list of kernel namespaces the pod will share") @@ -171,33 +171,7 @@ func create(cmd *cobra.Command, args []string) error { if err != nil { return err } - createOptions.Net.Network = specgen.Namespace{} - if cmd.Flag("network").Changed { - netInput, err := cmd.Flags().GetString("network") - if err != nil { - return err - } - parts := strings.SplitN(netInput, ":", 2) - - n := specgen.Namespace{} - switch { - case netInput == "bridge": - n.NSMode = specgen.Bridge - case netInput == "host": - n.NSMode = specgen.Host - case netInput == "slirp4netns", strings.HasPrefix(netInput, "slirp4netns:"): - n.NSMode = specgen.Slirp - if len(parts) > 1 { - createOptions.Net.NetworkOptions = make(map[string][]string) - createOptions.Net.NetworkOptions[parts[0]] = strings.Split(parts[1], ",") - } - default: - // Container and NS mode are presently unsupported - n.NSMode = specgen.Bridge - createOptions.Net.CNINetworks = strings.Split(netInput, ",") - } - createOptions.Net.Network = n - } + if len(createOptions.Net.PublishPorts) > 0 { if !createOptions.Infra { return errors.Errorf("you must have an infra container to publish port bindings to the host") diff --git a/cmd/podman/pods/top.go b/cmd/podman/pods/top.go index 45ef1e7c2..829882080 100644 --- a/cmd/podman/pods/top.go +++ b/cmd/podman/pods/top.go @@ -29,7 +29,7 @@ var ( Long: topDescription, RunE: top, Args: cobra.ArbitraryArgs, - ValidArgsFunction: common.AutocompletePodsRunning, + ValidArgsFunction: common.AutocompleteTopCmd, Example: `podman pod top podID podman pod top --latest podman pod top podID pid seccomp args %C diff --git a/cmd/podman/root.go b/cmd/podman/root.go index 34d92cd0f..7840e6100 100644 --- a/cmd/podman/root.go +++ b/cmd/podman/root.go @@ -113,33 +113,9 @@ func persistentPreRunE(cmd *cobra.Command, args []string) error { return nil } - // Special case if command is hidden completion command ("__complete","__completeNoDesc") - // Since __completeNoDesc is an alias the cm.Name is always __complete - if cmd.Name() == cobra.ShellCompRequestCmd { - // Parse the cli arguments after the the completion cmd (always called as second argument) - // This ensures that the --url, --identity and --connection flags are properly set - compCmd, _, err := cmd.Root().Traverse(os.Args[2:]) - if err != nil { - return err - } - // If we don't complete the root cmd hide all root flags - // so they won't show up in the completions on subcommands. - if compCmd != compCmd.Root() { - compCmd.Root().Flags().VisitAll(func(flag *pflag.Flag) { - flag.Hidden = true - }) - } - // No need for further setup when completing commands with subcommands. - if compCmd.HasSubCommands() { - requireCleanup = false - return nil - } - } - cfg := registry.PodmanConfig() // --connection is not as "special" as --remote so we can wait and process it here - var connErr error conn := cmd.Root().LocalFlags().Lookup("connection") if conn != nil && conn.Changed { cfg.Engine.ActiveService = conn.Value.String() @@ -147,19 +123,37 @@ func persistentPreRunE(cmd *cobra.Command, args []string) error { var err error cfg.URI, cfg.Identity, err = cfg.ActiveDestination() if err != nil { - connErr = errors.Wrap(err, "failed to resolve active destination") + return errors.Wrap(err, "failed to resolve active destination") } if err := cmd.Root().LocalFlags().Set("url", cfg.URI); err != nil { - connErr = errors.Wrap(err, "failed to override --url flag") + return errors.Wrap(err, "failed to override --url flag") } if err := cmd.Root().LocalFlags().Set("identity", cfg.Identity); err != nil { - connErr = errors.Wrap(err, "failed to override --identity flag") + return errors.Wrap(err, "failed to override --identity flag") } } - if connErr != nil { - return connErr + + // Special case if command is hidden completion command ("__complete","__completeNoDesc") + // Since __completeNoDesc is an alias the cm.Name is always __complete + if cmd.Name() == cobra.ShellCompRequestCmd { + // Parse the cli arguments after the the completion cmd (always called as second argument) + // This ensures that the --url, --identity and --connection flags are properly set + compCmd, _, err := cmd.Root().Traverse(os.Args[2:]) + if err != nil { + return err + } + // If we don't complete the root cmd hide all root flags + // so they won't show up in the completions on subcommands. + if compCmd != compCmd.Root() { + compCmd.Root().Flags().VisitAll(func(flag *pflag.Flag) { + flag.Hidden = true + }) + } + // No need for further setup the completion logic setups the engines as needed. + requireCleanup = false + return nil } // Prep the engines diff --git a/cmd/podman/volumes/list.go b/cmd/podman/volumes/list.go index b0d999765..7e54de38a 100644 --- a/cmd/podman/volumes/list.go +++ b/cmd/podman/volumes/list.go @@ -56,7 +56,7 @@ func init() { filterFlagName := "filter" flags.StringSliceVarP(&cliOpts.Filter, filterFlagName, "f", []string{}, "Filter volume output") - _ = lsCommand.RegisterFlagCompletionFunc(filterFlagName, completion.AutocompleteNone) + _ = lsCommand.RegisterFlagCompletionFunc(filterFlagName, common.AutocompleteVolumeFilters) formatFlagName := "format" flags.StringVar(&cliOpts.Format, formatFlagName, "{{.Driver}}\t{{.Name}}\n", "Format volume output using Go template") diff --git a/contrib/systemd/system/podman.service b/contrib/systemd/system/podman.service index e14bbe078..9b5a1a87f 100644 --- a/contrib/systemd/system/podman.service +++ b/contrib/systemd/system/podman.service @@ -8,4 +8,5 @@ StartLimitIntervalSec=0 [Service] Type=notify KillMode=process -ExecStart=/usr/bin/podman system service +Environment=LOGGING="--log-level=info" +ExecStart=/usr/bin/podman $LOGGING system service diff --git a/docs/source/Tutorials.rst b/docs/source/Tutorials.rst index 83818e3ae..e3e869d5b 100644 --- a/docs/source/Tutorials.rst +++ b/docs/source/Tutorials.rst @@ -2,7 +2,7 @@ Tutorials ========= -Here are a number of useful tutorials to get you up and running with Podman. If you are familiar with the Docker `Container Engine`_ the command in Podman_ should be quite familiar. If are brand new to containers, take a look at our `Introduction`. +Here are a number of useful tutorials to get you up and running with Podman. If you are familiar with the Docker `Container Engine`_ the command in Podman_ should be quite familiar. If you are brand new to containers, take a look at our `Introduction`. * `Basic Setup and Use of Podman `_: Learn how to setup Podman and perform some basic commands with the utility. * `Basic Setup and Use of Podman in a Rootless environment `_: The steps required to setup rootless Podman are enumerated. diff --git a/docs/source/conf.py b/docs/source/conf.py index 368d7cc29..e3715937d 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -28,11 +28,11 @@ author = "team" # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom # ones. extensions = [ - 'sphinx_markdown_tables', + "sphinx_markdown_tables", ] source_parsers = { - '.md': 'recommonmark.parser.CommonMarkParser', + ".md": "recommonmark.parser.CommonMarkParser", } diff --git a/docs/source/managecontainers.rst b/docs/source/managecontainers.rst index 849fd1d25..9926f9996 100644 --- a/docs/source/managecontainers.rst +++ b/docs/source/managecontainers.rst @@ -39,6 +39,8 @@ Manage Containers :doc:`prune ` Remove all stopped containers +:doc:`ps ` List containers + :doc:`restart ` Restart one or more containers :doc:`restore ` Restores one or more containers from a checkpoint diff --git a/docs/source/markdown/links/podman-list.1 b/docs/source/markdown/links/podman-list.1 deleted file mode 100644 index f7f44c704..000000000 --- a/docs/source/markdown/links/podman-list.1 +++ /dev/null @@ -1 +0,0 @@ -.so man1/podman-ps.1 diff --git a/docs/source/markdown/links/podman-ls.1 b/docs/source/markdown/links/podman-ls.1 deleted file mode 100644 index f7f44c704..000000000 --- a/docs/source/markdown/links/podman-ls.1 +++ /dev/null @@ -1 +0,0 @@ -.so man1/podman-ps.1 diff --git a/docs/source/markdown/podman-container.1.md b/docs/source/markdown/podman-container.1.md index 0a6ceea33..9da5db601 100644 --- a/docs/source/markdown/podman-container.1.md +++ b/docs/source/markdown/podman-container.1.md @@ -32,6 +32,7 @@ The container command allows you to manage containers | pause | [podman-pause(1)](podman-pause.1.md) | Pause one or more containers. | | port | [podman-port(1)](podman-port.1.md) | List port mappings for the container. | | prune | [podman-container-prune(1)](podman-container-prune.1.md)| Remove all stopped containers from local storage. | +| ps | [podman-ps(1)](podman-ps.1.md) | Prints out information about containers. | | restart | [podman-restart(1)](podman-restart.1.md) | Restart one or more containers. | | restore | [podman-container-restore(1)](podman-container-restore.1.md) | Restores one or more containers from a checkpoint. | | rm | [podman-rm(1)](podman-rm.1.md) | Remove one or more containers. | diff --git a/docs/source/markdown/podman-create.1.md b/docs/source/markdown/podman-create.1.md index 749af8a66..eb73609d4 100644 --- a/docs/source/markdown/podman-create.1.md +++ b/docs/source/markdown/podman-create.1.md @@ -18,6 +18,10 @@ any point. The initial status of the container created with **podman create** is 'created'. +Default settings for flags are defined in `containers.conf`. Most settings for +remote connections use the server's containers.conf, except when documented in +man pages. + ## OPTIONS #### **--add-host**=*host* @@ -584,7 +588,7 @@ Valid _mode_ values are: - **none**: no networking; - **container:**_id_: reuse another container's network stack; - **host**: use the Podman host network stack. Note: the host mode gives the container full access to local system services such as D-bus and is therefore considered insecure; -- _network-id_: connect to a user-defined network, multiple networks should be comma separated; +- **cni-network**: connect to a user-defined network, multiple networks should be comma-separated or they can be specified with multiple uses of the **--network** option; - **ns:**_path_: path to a network namespace to join; - **private**: create a new namespace for the container (default) - **slirp4netns[:OPTIONS,...]**: use **slirp4netns**(1) to create a user network stack. This is the default for rootless containers. It is possible to specify these additional options: @@ -817,6 +821,7 @@ Signal to stop a container. Default is SIGTERM. #### **--stop-timeout**=*seconds* Timeout (in seconds) to stop a container. Default is 10. +Remote connections use local containers.conf for defaults #### **--subgidname**=*name* @@ -893,10 +898,12 @@ standard input. #### **--tz**=*timezone* Set timezone in container. This flag takes area-based timezones, GMT time, as well as `local`, which sets the timezone in the container to match the host machine. See `/usr/share/zoneinfo/` for valid timezones. +Remote connections use local containers.conf for defaults #### **--umask**=*umask* Set the umask inside the container. Defaults to `0022`. +Remote connections use local containers.conf for defaults #### **--uidmap**=*container_uid:host_uid:amount* diff --git a/docs/source/markdown/podman-export.1.md b/docs/source/markdown/podman-export.1.md index 647c25770..63989a4db 100644 --- a/docs/source/markdown/podman-export.1.md +++ b/docs/source/markdown/podman-export.1.md @@ -12,6 +12,8 @@ podman\-export - Export a container's filesystem contents as a tar archive **podman export** exports the filesystem of a container and saves it as a tarball on the local machine. **podman export** writes to STDOUT by default and can be redirected to a file using the `--output` flag. +The image of the container exported by **podman export** can be imported by **podman import**. +To export image(s) with parent layers, use **podman save**. Note: `:` is a restricted character and cannot be part of the file name. **podman [GLOBAL OPTIONS]** diff --git a/docs/source/markdown/podman-import.1.md b/docs/source/markdown/podman-import.1.md index ebedf90b4..427c67da4 100644 --- a/docs/source/markdown/podman-import.1.md +++ b/docs/source/markdown/podman-import.1.md @@ -14,6 +14,7 @@ and saves it as a filesystem image. Remote tarballs can be specified using a URL Various image instructions can be configured with the **--change** flag and a commit message can be set using the **--message** flag. **reference**, if present, is a tag to assign to the image. +**podman import** is used for importing from the archive generated by **podman export**, that includes the container's filesystem. To import the archive of image layers created by **podman save**, use **podman load**. Note: `:` is a restricted character and cannot be part of the file name. ## OPTIONS diff --git a/docs/source/markdown/podman-load.1.md b/docs/source/markdown/podman-load.1.md index cdc99fbf2..177709a43 100644 --- a/docs/source/markdown/podman-load.1.md +++ b/docs/source/markdown/podman-load.1.md @@ -1,7 +1,7 @@ % podman-load(1) ## NAME -podman\-load - Load an image from a container image archive into container storage +podman\-load - Load image(s) from a tar archive into container storage ## SYNOPSIS **podman load** [*options*] [*name*[:*tag*]] @@ -11,6 +11,7 @@ podman\-load - Load an image from a container image archive into container stora ## DESCRIPTION **podman load** loads an image from either an **oci-archive** or a **docker-archive** stored on the local machine into container storage. **podman load** reads from stdin by default or a file if the **input** option is set. You can also specify a name for the image if the archive does not contain a named reference, of if you want an additional name for the local image. +**podman load** is used for loading from the archive generated by **podman save**, that includes the image parent layers. To load the archive of container's filesystem created by **podman export**, use **podman import**. The local client further supports loading an **oci-dir** or a **docker-dir** as created with **podman save** (1). diff --git a/docs/source/markdown/podman-ps.1.md b/docs/source/markdown/podman-ps.1.md index f542daf4c..b94964f6c 100644 --- a/docs/source/markdown/podman-ps.1.md +++ b/docs/source/markdown/podman-ps.1.md @@ -6,15 +6,11 @@ podman\-ps - Prints out information about containers ## SYNOPSIS **podman ps** [*options*] -**podman container list** [*options*] - -**podman container ls** [*options*] - **podman container ps** [*options*] -**podman list** [*options*] +**podman container list** [*options*] -**podman ls** [*options*] +**podman container ls** [*options*] ## DESCRIPTION **podman ps** lists the running containers on the system. Use the **--all** flag to view diff --git a/docs/source/markdown/podman-run.1.md b/docs/source/markdown/podman-run.1.md index 5b2cdd6a5..184c12acd 100644 --- a/docs/source/markdown/podman-run.1.md +++ b/docs/source/markdown/podman-run.1.md @@ -33,6 +33,10 @@ is located at _/run/.containerenv_. When running from a user defined network namespace, the _/etc/netns/NSNAME/resolv.conf_ will be used if it exists, otherwise _/etc/resolv.conf_ will be used. +Default settings are defined in `containers.conf`. Most settings for remote +connections use the servers containers.conf, except when documented in man +pages. + ## OPTIONS #### **--add-host**=_host_:_ip_ @@ -610,7 +614,7 @@ Valid _mode_ values are: - **none**: no networking; - **container:**_id_: reuse another container's network stack; - **host**: use the Podman host network stack. Note: the host mode gives the container full access to local system services such as D-bus and is therefore considered insecure; -- _network-id_: connect to a user-defined network, multiple networks should be comma separated; +- **cni-network**: connect to a user-defined network, multiple networks should be comma-separated or they can be specified with multiple uses of the **--network** option; - **ns:**_path_: path to a network namespace to join; - **private**: create a new namespace for the container (default) - **slirp4netns[:OPTIONS,...]**: use **slirp4netns**(1) to create a user network stack. This is the default for rootless containers. It is possible to specify these additional options: @@ -857,6 +861,7 @@ Signal to stop a container. Default is **SIGTERM**. #### **--stop-timeout**=*seconds* Timeout to stop a container. Default is **10**. +Remote connections use local containers.conf for defaults #### **--subgidname**=*name* @@ -952,10 +957,12 @@ standard input. #### **--tz**=*timezone* Set timezone in container. This flag takes area-based timezones, GMT time, as well as `local`, which sets the timezone in the container to match the host machine. See `/usr/share/zoneinfo/` for valid timezones. +Remote connections use local containers.conf for defaults #### **--umask**=*umask* Set the umask inside the container. Defaults to `0022`. +Remote connections use local containers.conf for defaults #### **--uidmap**=*container_uid*:*host_uid*:*amount* diff --git a/docs/source/markdown/podman-save.1.md b/docs/source/markdown/podman-save.1.md index bfb52e7f8..fb79cfd2a 100644 --- a/docs/source/markdown/podman-save.1.md +++ b/docs/source/markdown/podman-save.1.md @@ -1,7 +1,7 @@ % podman-save(1) ## NAME -podman\-save - Save an image to a container archive +podman\-save - Save image(s) to an archive ## SYNOPSIS **podman save** [*options*] *name*[:*tag*] @@ -12,6 +12,8 @@ podman\-save - Save an image to a container archive **podman save** saves an image to either **docker-archive**, **oci-archive**, **oci-dir** (directory with oci manifest type), or **docker-dir** (directory with v2s2 manifest type) on the local machine, default is **docker-archive**. **podman save** writes to STDOUT by default and can be redirected to a file using the **output** flag. The **quiet** flag suppresses the output when set. +**podman save** will save parent layers of the image(s) and the image(s) can be loaded using **podman load**. +To export the containers, use the **podman export**. Note: `:` is a restricted character and cannot be part of the file name. **podman [GLOBAL OPTIONS]** diff --git a/docs/source/markdown/podman-system-service.1.md b/docs/source/markdown/podman-system-service.1.md index 39e30ec02..1fdecfa5c 100644 --- a/docs/source/markdown/podman-system-service.1.md +++ b/docs/source/markdown/podman-system-service.1.md @@ -17,12 +17,14 @@ The REST API provided by **podman system service** is split into two parts: a co Documentation for the latter is available at *https://docs.podman.io/en/latest/_static/api.html*. Both APIs are versioned, but the server will not reject requests with an unsupported version set. +Note: The default systemd unit files (system and user) change the log-level option to *info* from *error*. This change provides additional information on each API call. + ## OPTIONS #### **--time**, **-t** The time until the session expires in _seconds_. The default is 5 -seconds. A value of `0` means no timeout and the session will not expire. +seconds. A value of `0` means no timeout, therefore the session will not expire. #### **--help**, **-h** @@ -40,3 +42,4 @@ podman(1), podman-system-service(1), podman-system-connection(1) ## HISTORY January 2020, Originally compiled by Brent Baude +November 2020, Updated by Jhon Honce diff --git a/docs/source/markdown/podman-top.1.md b/docs/source/markdown/podman-top.1.md index f307f96da..cfb89567c 100644 --- a/docs/source/markdown/podman-top.1.md +++ b/docs/source/markdown/podman-top.1.md @@ -9,7 +9,7 @@ podman\-top - Display the running processes of a container **podman container top** [*options*] *container* [*format-descriptors*] ## DESCRIPTION -Display the running processes of the container. The *format-descriptors* are ps (1) compatible AIX format descriptors but extended to print additional information, such as the seccomp mode or the effective capabilities of a given process. The descriptors can either be passed as separated arguments or as a single comma-separated argument. Note that you can also specify options and or flags of ps(1); in this case, Podman will fallback to executing ps with the specified arguments and flags in the container. +Display the running processes of the container. The *format-descriptors* are ps (1) compatible AIX format descriptors but extended to print additional information, such as the seccomp mode or the effective capabilities of a given process. The descriptors can either be passed as separated arguments or as a single comma-separated argument. Note that you can also specify options and or flags of ps(1); in this case, Podman will fallback to executing ps with the specified arguments and flags in the container. Please use the "h*" descriptors if you want to extract host-related information. For instance, `podman top $name hpid huser` to display the PID and user of the processes in the host context. ## OPTIONS diff --git a/docs/source/markdown/podman.1.md b/docs/source/markdown/podman.1.md index 1954ca2aa..7da01d389 100644 --- a/docs/source/markdown/podman.1.md +++ b/docs/source/markdown/podman.1.md @@ -17,6 +17,10 @@ Podman uses Buildah(1) internally to create container images. Both tools share i (not container) storage, hence each can use or manipulate images (but not containers) created by the other. +Default settings for flags are defined in `containers.conf`. Most settings for +Remote connections use the server's containers.conf, except when documented in +man pages. + **podman [GLOBAL OPTIONS]** ## GLOBAL OPTIONS @@ -33,6 +37,7 @@ Path of the configuration directory for CNI networks. (Default: `/etc/cni/net.d #### **--connection**, **-c** Connection to use for remote podman (Default connection is configured in `containers.conf`) +Remote connections use local containers.conf for default. #### **--conmon** Path of the conmon binary (Default path is configured in `containers.conf`) @@ -71,6 +76,7 @@ Identity value resolution precedence: - command line value - environment variable `CONTAINER_SSHKEY`, if `CONTAINER_HOST` is found - `containers.conf` +Remote connections use local containers.conf for default. #### **--log-level**=*level* @@ -86,6 +92,7 @@ Path to the command binary to use for setting up a network. It is currently onl #### **--remote**, **-r** Access Podman service will be remote +Remote connections use local containers.conf for default. #### **--url**=*value* URL to access Podman service (default from `containers.conf`, rootless `unix://run/user/$UID/podman/podman.sock` or as root `unix://run/podman/podman.sock`). @@ -104,6 +111,7 @@ URL value resolution precedence: - environment variable `CONTAINER_HOST` - `containers.conf` - `unix://run/podman/podman.sock` +Remote connections use local containers.conf for default. #### **--root**=*value* @@ -223,7 +231,7 @@ the exit codes follow the `chroot` standard, see below: | [podman-init(1)](podman-init.1.md) | Initialize one or more containers | | [podman-inspect(1)](podman-inspect.1.md) | Display a container, image, volume, network, or pod's configuration. | | [podman-kill(1)](podman-kill.1.md) | Kill the main process in one or more containers. | -| [podman-load(1)](podman-load.1.md) | Load an image from a container image archive into container storage. | +| [podman-load(1)](podman-load.1.md) | Load image(s) from a tar archive into container storage. | | [podman-login(1)](podman-login.1.md) | Login to a container registry. | | [podman-logout(1)](podman-logout.1.md) | Logout of a container registry. | | [podman-logs(1)](podman-logs.1.md) | Display the logs of one or more containers. | @@ -241,7 +249,7 @@ the exit codes follow the `chroot` standard, see below: | [podman-rm(1)](podman-rm.1.md) | Remove one or more containers. | | [podman-rmi(1)](podman-rmi.1.md) | Removes one or more locally stored images. | | [podman-run(1)](podman-run.1.md) | Run a command in a new container. | -| [podman-save(1)](podman-save.1.md) | Save an image to a container archive. | +| [podman-save(1)](podman-save.1.md) | Save image(s) to an archive. | | [podman-search(1)](podman-search.1.md) | Search a registry for an image. | | [podman-start(1)](podman-start.1.md) | Start one or more containers. | | [podman-stats(1)](podman-stats.1.md) | Display a live stream of one or more container's resource usage statistics. | diff --git a/docs/tutorials/mac_win_client.md b/docs/tutorials/mac_win_client.md index af2668e10..375f73102 100644 --- a/docs/tutorials/mac_win_client.md +++ b/docs/tutorials/mac_win_client.md @@ -55,7 +55,7 @@ host: In order for the client to communicate with the server you need to enable and start the SSH daemon on your Linux machine, if it is not currently enabled. ``` -sudo systemctl enable --now -s sshd +sudo systemctl enable --now sshd ``` #### Setting up SSH diff --git a/go.mod b/go.mod index c093319e8..2ed1c56d1 100644 --- a/go.mod +++ b/go.mod @@ -11,11 +11,11 @@ require ( github.com/containernetworking/cni v0.8.0 github.com/containernetworking/plugins v0.8.7 github.com/containers/buildah v1.18.0 - github.com/containers/common v0.27.0 + github.com/containers/common v0.29.0 github.com/containers/conmon v2.0.20+incompatible - github.com/containers/image/v5 v5.8.0 + github.com/containers/image/v5 v5.8.1 github.com/containers/psgo v1.5.1 - github.com/containers/storage v1.24.0 + github.com/containers/storage v1.24.1 github.com/coreos/go-systemd/v22 v22.1.0 github.com/cri-o/ocicni v0.2.1-0.20201102180012-75c612fda1a2 github.com/cyphar/filepath-securejoin v0.2.2 diff --git a/go.sum b/go.sum index baff472f6..761ba04de 100644 --- a/go.sum +++ b/go.sum @@ -96,13 +96,15 @@ github.com/containernetworking/plugins v0.8.7/go.mod h1:R7lXeZaBzpfqapcAbHRW8/CY github.com/containers/buildah v1.18.0 h1:mWEm013LVNGecF++sYo0T7fe/4pqMas/PQxQ/qviC68= github.com/containers/buildah v1.18.0/go.mod h1:qHLk7RUL7cHfA7ve1MKkZ6cyKUxHD0YxiLJcKY+mJe8= github.com/containers/common v0.26.3/go.mod h1:hJWZIlrl5MsE2ELNRa+MPp6I1kPbXHauuj0Ym4BsLG4= -github.com/containers/common v0.27.0 h1:+QlYEOitVYtU9/x8xebRgxdGqt4sLaIqV6MBOns+zLk= -github.com/containers/common v0.27.0/go.mod h1:ZTswJJfu4aGF6Anyi2yON8Getda9NDYcdIzurOEHHXI= +github.com/containers/common v0.29.0 h1:hTMC+urdkk5bKfhL/OgCixIX5xjJgQ2l2jPG745ECFQ= +github.com/containers/common v0.29.0/go.mod h1:yT4GTUHsKRmpaDb+mecXRnIMre7W3ZgwXqaYMywXlaA= github.com/containers/conmon v2.0.20+incompatible h1:YbCVSFSCqFjjVwHTPINGdMX1F6JXHGTUje2ZYobNrkg= github.com/containers/conmon v2.0.20+incompatible/go.mod h1:hgwZ2mtuDrppv78a/cOBNiCm6O0UMWGx1mu7P00nu5I= github.com/containers/image/v5 v5.7.0/go.mod h1:8aOy+YaItukxghRORkvhq5ibWttHErzDLy6egrKfKos= github.com/containers/image/v5 v5.8.0 h1:B3FGHi0bdGXgg698kBIGOlHCXN5n+scJr6/5354GOPU= github.com/containers/image/v5 v5.8.0/go.mod h1:jKxdRtyIDumVa56hdsZvV+gwx4zB50hRou6pIuCWLkg= +github.com/containers/image/v5 v5.8.1 h1:aHW8a/Kd0dTJ7PTL/fc6y12sJqHxWgqilu+XyHfjD8Q= +github.com/containers/image/v5 v5.8.1/go.mod h1:blOEFd/iFdeyh891ByhCVUc+xAcaI3gBegXECwz9UbQ= github.com/containers/libtrust v0.0.0-20190913040956-14b96171aa3b h1:Q8ePgVfHDplZ7U33NwHZkrVELsZP5fYj9pM5WBZB2GE= github.com/containers/libtrust v0.0.0-20190913040956-14b96171aa3b/go.mod h1:9rfv8iPl1ZP7aqh9YA68wnZv2NUDbXdcdPHVz0pFbPY= github.com/containers/ocicrypt v1.0.3 h1:vYgl+RZ9Q3DPMuTfxmN+qp0X2Bj52uuY2vnt6GzVe1c= @@ -111,10 +113,10 @@ github.com/containers/psgo v1.5.1 h1:MQNb7FLbXqBdqz6u4lI2QWizVz4RSTzs1+Nk9XT1iVA github.com/containers/psgo v1.5.1/go.mod h1:2ubh0SsreMZjSXW1Hif58JrEcFudQyIy9EzPUWfawVU= github.com/containers/storage v1.23.6/go.mod h1:haFs0HRowKwyzvWEx9EgI3WsL8XCSnBDb5f8P5CAxJY= github.com/containers/storage v1.23.7/go.mod h1:cUT2zHjtx+WlVri30obWmM2gpqpi8jfPsmIzP1TVpEI= -github.com/containers/storage v1.23.9 h1:qbgnTp76pLSyW3vYwY5GH4vk5cHYVXFJ+CsUEBp9TMw= -github.com/containers/storage v1.23.9/go.mod h1:3b2ktpB6pw53SEeIoFfO0sQfP9+IoJJKPq5iJk74gxE= github.com/containers/storage v1.24.0 h1:Fo2LkF7tkMLmo38sTZ/G8wHjcn8JfUFPfyTxM4WwMfk= github.com/containers/storage v1.24.0/go.mod h1:A4d3BzuZK9b3oLVEsiSRhZLPIx3z7utgiPyXLK/YMhY= +github.com/containers/storage v1.24.1 h1:1+f8fy6ly35c8SLet5jzZ8t0WJJs5+xSpfMAYw0R3kc= +github.com/containers/storage v1.24.1/go.mod h1:0xJL06Dmd+ZYXIUdnBUPN0JnhHGgwMkLvnnAonJfWJU= github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= github.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= github.com/coreos/go-iptables v0.4.5 h1:DpHb9vJrZQEFMcVLFKAAGMUVX0XoRC0ptCthinRYm38= @@ -322,6 +324,8 @@ github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+o github.com/klauspost/compress v1.11.1/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= github.com/klauspost/compress v1.11.2 h1:MiK62aErc3gIiVEtyzKfeOHgW7atJb5g/KNX5m3c2nQ= github.com/klauspost/compress v1.11.2/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= +github.com/klauspost/compress v1.11.3 h1:dB4Bn0tN3wdCzQxnS8r06kV74qN/TAfaIS0bVE8h3jc= +github.com/klauspost/compress v1.11.3/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= github.com/klauspost/pgzip v1.2.5 h1:qnWYvvKqedOF2ulHpMG72XQol4ILEJ8k2wwRl/Km8oE= github.com/klauspost/pgzip v1.2.5/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= diff --git a/libpod/container.go b/libpod/container.go index 4b9e6a5ba..e954d84eb 100644 --- a/libpod/container.go +++ b/libpod/container.go @@ -13,10 +13,12 @@ import ( "github.com/containers/image/v5/manifest" "github.com/containers/podman/v2/libpod/define" "github.com/containers/podman/v2/libpod/lock" + "github.com/containers/podman/v2/pkg/rootless" "github.com/containers/storage" "github.com/cri-o/ocicni/pkg/ocicni" spec "github.com/opencontainers/runtime-spec/specs-go" "github.com/pkg/errors" + "github.com/sirupsen/logrus" ) // CgroupfsDefaultCgroupParent is the cgroup parent for CGroupFS in libpod @@ -920,19 +922,39 @@ func (c *Container) CGroupPath() (string, error) { return "", errors.Wrapf(define.ErrNoCgroups, "this container is not creating cgroups") } - // Read /proc/[PID]/cgroup and look at the first line. cgroups(7) - // nails it down to three fields with the 3rd pointing to the cgroup's - // path which works both on v1 and v2. + // Read /proc/[PID]/cgroup and find the *longest* cgroup entry. That's + // needed to account for hacks in cgroups v1, where each line in the + // file could potentially point to a cgroup. The longest one, however, + // is the libpod-specific one we're looking for. + // + // See #8397 on the need for the longest-path look up. procPath := fmt.Sprintf("/proc/%d/cgroup", c.state.PID) lines, err := ioutil.ReadFile(procPath) if err != nil { return "", err } - fields := bytes.Split(bytes.Split(lines, []byte("\n"))[0], []byte(":")) - if len(fields) != 3 { - return "", errors.Errorf("expected 3 fields but got %d: %s", len(fields), procPath) + + var cgroupPath string + for _, line := range bytes.Split(lines, []byte("\n")) { + // cgroups(7) nails it down to three fields with the 3rd + // pointing to the cgroup's path which works both on v1 and v2. + fields := bytes.Split(line, []byte(":")) + if len(fields) != 3 { + logrus.Debugf("Error parsing cgroup: expected 3 fields but got %d: %s", len(fields), procPath) + continue + } + path := string(fields[2]) + if len(path) > len(cgroupPath) { + cgroupPath = path + } + } - return string(fields[2]), nil + + if len(cgroupPath) == 0 { + return "", errors.Errorf("could not find any cgroup in %q", procPath) + } + + return cgroupPath, nil } // RootFsSize returns the root FS size of the container @@ -1074,13 +1096,17 @@ func (c *Container) Umask() string { // values at runtime via network connect and disconnect. // If the container is configured to use CNI and this function returns an empty // array, the container will still be connected to the default network. -func (c *Container) Networks() ([]string, error) { +// The second return parameter, a bool, indicates that the container container +// is joining the default CNI network - the network name will be included in the +// returned array of network names, but the container did not explicitly join +// this network. +func (c *Container) Networks() ([]string, bool, error) { if !c.batched { c.lock.Lock() defer c.lock.Unlock() if err := c.syncContainer(); err != nil { - return nil, err + return nil, false, err } } @@ -1088,19 +1114,22 @@ func (c *Container) Networks() ([]string, error) { } // Unlocked accessor for networks -func (c *Container) networks() ([]string, error) { +func (c *Container) networks() ([]string, bool, error) { networks, err := c.runtime.state.GetNetworks(c) if err != nil && errors.Cause(err) == define.ErrNoSuchNetwork { - return c.config.Networks, nil + if len(c.config.Networks) == 0 && !rootless.IsRootless() { + return []string{c.runtime.netPlugin.GetDefaultNetworkName()}, true, nil + } + return c.config.Networks, false, nil } - return networks, err + return networks, false, err } // networksByNameIndex provides us with a map of container networks where key // is network name and value is the index position func (c *Container) networksByNameIndex() (map[string]int, error) { - networks, err := c.networks() + networks, _, err := c.networks() if err != nil { return nil, err } diff --git a/libpod/container_api.go b/libpod/container_api.go index a9808a30e..6a7ddc421 100644 --- a/libpod/container_api.go +++ b/libpod/container_api.go @@ -714,3 +714,17 @@ func (c *Container) Restore(ctx context.Context, options ContainerCheckpointOpti defer c.newContainerEvent(events.Restore) return c.restore(ctx, options) } + +// Indicate whether or not the container should restart +func (c *Container) ShouldRestart(ctx context.Context) bool { + logrus.Debugf("Checking if container %s should restart", c.ID()) + if !c.batched { + c.lock.Lock() + defer c.lock.Unlock() + + if err := c.syncContainer(); err != nil { + return false + } + } + return c.shouldRestart() +} diff --git a/libpod/container_internal.go b/libpod/container_internal.go index 108954bad..b6a3244ea 100644 --- a/libpod/container_internal.go +++ b/libpod/container_internal.go @@ -206,37 +206,39 @@ func (c *Container) handleExitFile(exitFile string, fi os.FileInfo) error { return nil } -// Handle container restart policy. -// This is called when a container has exited, and was not explicitly stopped by -// an API call to stop the container or pod it is in. -func (c *Container) handleRestartPolicy(ctx context.Context) (_ bool, retErr error) { - // If we did not get a restart policy match, exit immediately. +func (c *Container) shouldRestart() bool { + // If we did not get a restart policy match, return false // Do the same if we're not a policy that restarts. if !c.state.RestartPolicyMatch || c.config.RestartPolicy == RestartPolicyNo || c.config.RestartPolicy == RestartPolicyNone { - return false, nil + return false } // If we're RestartPolicyOnFailure, we need to check retries and exit // code. if c.config.RestartPolicy == RestartPolicyOnFailure { if c.state.ExitCode == 0 { - return false, nil + return false } // If we don't have a max retries set, continue if c.config.RestartRetries > 0 { - if c.state.RestartCount < c.config.RestartRetries { - logrus.Debugf("Container %s restart policy trigger: on retry %d (of %d)", - c.ID(), c.state.RestartCount, c.config.RestartRetries) - } else { - logrus.Debugf("Container %s restart policy trigger: retries exhausted", c.ID()) - return false, nil + if c.state.RestartCount >= c.config.RestartRetries { + return false } } } + return true +} +// Handle container restart policy. +// This is called when a container has exited, and was not explicitly stopped by +// an API call to stop the container or pod it is in. +func (c *Container) handleRestartPolicy(ctx context.Context) (_ bool, retErr error) { + if !c.shouldRestart() { + return false, nil + } logrus.Debugf("Restarting container %s due to restart policy %s", c.ID(), c.config.RestartPolicy) // Need to check if dependencies are alive. @@ -641,18 +643,13 @@ func (c *Container) removeIPv4Allocations() error { cniDefaultNetwork = c.runtime.netPlugin.GetDefaultNetworkName() } - networks, err := c.networks() + networks, _, err := c.networks() if err != nil { return err } - switch { - case len(networks) > 0 && len(networks) != len(c.state.NetworkStatus): + if len(networks) != len(c.state.NetworkStatus) { return errors.Wrapf(define.ErrInternal, "network mismatch: asked to join %d CNI networks but got %d CNI results", len(networks), len(c.state.NetworkStatus)) - case len(networks) == 0 && len(c.state.NetworkStatus) != 1: - return errors.Wrapf(define.ErrInternal, "network mismatch: did not specify CNI networks but joined more than one (%d)", len(c.state.NetworkStatus)) - case len(networks) == 0 && cniDefaultNetwork == "": - return errors.Wrapf(define.ErrInternal, "could not retrieve name of CNI default network") } for index, result := range c.state.NetworkStatus { diff --git a/libpod/healthcheck_linux.go b/libpod/healthcheck_linux.go index b0f1ff35d..0ad15da09 100644 --- a/libpod/healthcheck_linux.go +++ b/libpod/healthcheck_linux.go @@ -26,6 +26,10 @@ func (c *Container) createTimer() error { if rootless.IsRootless() { cmd = append(cmd, "--user") } + path := os.Getenv("PATH") + if path != "" { + cmd = append(cmd, "--setenv=PATH="+path) + } cmd = append(cmd, "--unit", c.ID(), fmt.Sprintf("--on-unit-inactive=%s", c.HealthCheckConfig().Interval.String()), "--timer-property=AccuracySec=1s", podman, "healthcheck", "run", c.ID()) conn, err := systemd.ConnectToDBUS() diff --git a/libpod/image/manifests.go b/libpod/image/manifests.go index 59678fdb2..14f7c2f83 100644 --- a/libpod/image/manifests.go +++ b/libpod/image/manifests.go @@ -2,13 +2,14 @@ package image import ( "context" + "fmt" "github.com/containers/buildah/manifests" + "github.com/containers/image/v5/docker" "github.com/containers/image/v5/manifest" "github.com/containers/image/v5/transports/alltransports" "github.com/containers/image/v5/types" "github.com/opencontainers/go-digest" - "github.com/pkg/errors" ) // Options for adding a manifest @@ -69,19 +70,10 @@ func CreateManifestList(rt *Runtime, systemContext types.SystemContext, names [] list := manifests.Create() opts := ManifestAddOpts{Images: names, All: all} for _, img := range imgs { - var ref types.ImageReference - newImage, err := rt.NewFromLocal(img) - if err == nil { - ir, err := newImage.toImageRef(context.Background()) - if err != nil { - return "", err - } - if ir == nil { - return "", errors.New("unable to convert image to ImageReference") - } - ref = ir.Reference() - } else { - ref, err = alltransports.ParseImageName(img) + ref, err := alltransports.ParseImageName(img) + if err != nil { + dockerPrefix := fmt.Sprintf("%s://", docker.Transport.Name()) + ref, err = alltransports.ParseImageName(fmt.Sprintf("%s%s", dockerPrefix, img)) if err != nil { return "", err } @@ -134,18 +126,10 @@ func addManifestToList(ref types.ImageReference, list manifests.List, systemCont // AddManifest adds a manifest to a given manifest list. func (i *Image) AddManifest(systemContext types.SystemContext, opts ManifestAddOpts) (string, error) { - var ( - ref types.ImageReference - ) - newImage, err := i.imageruntime.NewFromLocal(opts.Images[0]) - if err == nil { - ir, err := newImage.toImageRef(context.Background()) - if err != nil { - return "", err - } - ref = ir.Reference() - } else { - ref, err = alltransports.ParseImageName(opts.Images[0]) + ref, err := alltransports.ParseImageName(opts.Images[0]) + if err != nil { + dockerPrefix := fmt.Sprintf("%s://", docker.Transport.Name()) + ref, err = alltransports.ParseImageName(fmt.Sprintf("%s%s", dockerPrefix, opts.Images[0])) if err != nil { return "", err } diff --git a/libpod/network/config.go b/libpod/network/config.go index ce8a4446c..ce351129e 100644 --- a/libpod/network/config.go +++ b/libpod/network/config.go @@ -129,6 +129,16 @@ func (f FirewallConfig) Bytes() ([]byte, error) { return json.MarshalIndent(f, "", "\t") } +// TuningConfig describes the tuning plugin +type TuningConfig struct { + PluginType string `json:"type"` +} + +// Bytes outputs the configuration as []byte +func (f TuningConfig) Bytes() ([]byte, error) { + return json.MarshalIndent(f, "", "\t") +} + // DNSNameConfig describes the dns container name resolution plugin config type DNSNameConfig struct { PluginType string `json:"type"` diff --git a/libpod/network/create.go b/libpod/network/create.go index 387f4fcd3..7e4fc574a 100644 --- a/libpod/network/create.go +++ b/libpod/network/create.go @@ -176,6 +176,7 @@ func createBridge(name string, options entities.NetworkCreateOptions, runtimeCon plugins = append(plugins, bridge) plugins = append(plugins, NewPortMapPlugin()) plugins = append(plugins, NewFirewallPlugin()) + plugins = append(plugins, NewTuningPlugin()) // if we find the dnsname plugin or are rootless, we add configuration for it // the rootless-cni-infra container has the dnsname plugin always installed if (HasDNSNamePlugin(runtimeConfig.Network.CNIPluginDirs) || rootless.IsRootless()) && !options.DisableDNS { diff --git a/libpod/network/netconflist.go b/libpod/network/netconflist.go index 111f1715c..ee9adce14 100644 --- a/libpod/network/netconflist.go +++ b/libpod/network/netconflist.go @@ -119,6 +119,13 @@ func NewFirewallPlugin() FirewallConfig { } } +// NewTuningPlugin creates a generic tuning section +func NewTuningPlugin() TuningConfig { + return TuningConfig{ + PluginType: "tuning", + } +} + // NewDNSNamePlugin creates the dnsname config with a given // domainname func NewDNSNamePlugin(domainName string) DNSNameConfig { diff --git a/libpod/network/subnet.go b/libpod/network/subnet.go index 90f0cdfce..120038e57 100644 --- a/libpod/network/subnet.go +++ b/libpod/network/subnet.go @@ -54,14 +54,10 @@ func LastIPInSubnet(addr *net.IPNet) (net.IP, error) { //nolint:interfacer ones, bits := cidr.Mask.Size() if ones == bits { - return FirstIPInSubnet(cidr) + return cidr.IP, nil } - hostStart := ones / 8 - // Handle the first host byte - cidr.IP[hostStart] |= 0xff & cidr.Mask[hostStart] - // Fill the rest with ones - for i := hostStart; i < len(cidr.IP); i++ { - cidr.IP[i] = 0xff + for i := range cidr.IP { + cidr.IP[i] = cidr.IP[i] | ^cidr.Mask[i] } return cidr.IP, nil } @@ -73,6 +69,10 @@ func FirstIPInSubnet(addr *net.IPNet) (net.IP, error) { //nolint:interfacer if err != nil { return nil, err } + ones, bits := cidr.Mask.Size() + if ones == bits { + return cidr.IP, nil + } cidr.IP[len(cidr.IP)-1]++ return cidr.IP, nil } diff --git a/libpod/network/subnet_test.go b/libpod/network/subnet_test.go index 917c3be88..55b2443bd 100644 --- a/libpod/network/subnet_test.go +++ b/libpod/network/subnet_test.go @@ -33,3 +33,65 @@ func TestNextSubnet(t *testing.T) { }) } } + +func TestFirstIPInSubnet(t *testing.T) { + tests := []struct { + name string + args *net.IPNet + want net.IP + wantErr bool + }{ + {"class b", parseCIDR("192.168.0.0/16"), net.ParseIP("192.168.0.1"), false}, + {"class c", parseCIDR("192.168.1.0/24"), net.ParseIP("192.168.1.1"), false}, + {"cidr /23", parseCIDR("192.168.0.0/23"), net.ParseIP("192.168.0.1"), false}, + {"cidr /25", parseCIDR("192.168.1.0/25"), net.ParseIP("192.168.1.1"), false}, + {"cidr /26", parseCIDR("172.16.1.128/26"), net.ParseIP("172.16.1.129"), false}, + {"class a", parseCIDR("10.0.0.0/8"), net.ParseIP("10.0.0.1"), false}, + {"cidr /32", parseCIDR("192.168.255.4/32"), net.ParseIP("192.168.255.4"), false}, + {"cidr /31", parseCIDR("192.168.255.4/31"), net.ParseIP("192.168.255.5"), false}, + } + for _, tt := range tests { + test := tt + t.Run(test.name, func(t *testing.T) { + got, err := FirstIPInSubnet(test.args) + if (err != nil) != test.wantErr { + t.Errorf("FirstIPInSubnet() error = %v, wantErr %v", err, test.wantErr) + return + } + if !got.Equal(test.want) { + t.Errorf("FirstIPInSubnet() got = %v, want %v", got, test.want) + } + }) + } +} + +func TestLastIPInSubnet(t *testing.T) { + tests := []struct { + name string + args *net.IPNet + want net.IP + wantErr bool + }{ + {"class b", parseCIDR("192.168.0.0/16"), net.ParseIP("192.168.255.255"), false}, + {"class c", parseCIDR("192.168.1.0/24"), net.ParseIP("192.168.1.255"), false}, + {"cidr /23", parseCIDR("192.168.0.0/23"), net.ParseIP("192.168.1.255"), false}, + {"cidr /25", parseCIDR("192.168.1.0/25"), net.ParseIP("192.168.1.127"), false}, + {"cidr /26", parseCIDR("172.16.1.128/26"), net.ParseIP("172.16.1.191"), false}, + {"class a", parseCIDR("10.0.0.0/8"), net.ParseIP("10.255.255.255"), false}, + {"cidr /32", parseCIDR("192.168.255.4/32"), net.ParseIP("192.168.255.4"), false}, + {"cidr /31", parseCIDR("192.168.255.4/31"), net.ParseIP("192.168.255.5"), false}, + } + for _, tt := range tests { + test := tt + t.Run(test.name, func(t *testing.T) { + got, err := LastIPInSubnet(test.args) + if (err != nil) != test.wantErr { + t.Errorf("LastIPInSubnet() error = %v, wantErr %v", err, test.wantErr) + return + } + if !got.Equal(test.want) { + t.Errorf("LastIPInSubnet() got = %v, want %v", got, test.want) + } + }) + } +} diff --git a/libpod/networking_linux.go b/libpod/networking_linux.go index 8dce7c9fe..4e7ffaf81 100644 --- a/libpod/networking_linux.go +++ b/libpod/networking_linux.go @@ -110,10 +110,15 @@ func (r *Runtime) configureNetNS(ctr *Container, ctrNS ns.NetNS) ([]*cnitypes.Re podName := getCNIPodName(ctr) - networks, err := ctr.networks() + networks, _, err := ctr.networks() if err != nil { return nil, err } + // All networks have been removed from the container. + // This is effectively forcing net=none. + if len(networks) == 0 { + return nil, nil + } // Update container map of interface descriptions if err := ctr.setupNetworkDescriptions(networks); err != nil { @@ -224,7 +229,7 @@ func (r *Runtime) setupRootlessNetNS(ctr *Container) error { if ctr.config.NetMode.IsSlirp4netns() { return r.setupSlirp4netns(ctr) } - networks, err := ctr.networks() + networks, _, err := ctr.networks() if err != nil { return err } @@ -744,13 +749,13 @@ func (r *Runtime) teardownNetNS(ctr *Container) error { logrus.Debugf("Tearing down network namespace at %s for container %s", ctr.state.NetNS.Path(), ctr.ID()) - networks, err := ctr.networks() + networks, _, err := ctr.networks() if err != nil { return err } // rootless containers do not use the CNI plugin directly - if !rootless.IsRootless() && !ctr.config.NetMode.IsSlirp4netns() { + if !rootless.IsRootless() && !ctr.config.NetMode.IsSlirp4netns() && len(networks) > 0 { var requestedIP net.IP if ctr.requestedIP != nil { requestedIP = ctr.requestedIP @@ -863,7 +868,7 @@ func (c *Container) getContainerNetworkInfo() (*define.InspectNetworkSettings, e settings := new(define.InspectNetworkSettings) settings.Ports = makeInspectPortBindings(c.config.PortMappings) - networks, err := c.networks() + networks, isDefault, err := c.networks() if err != nil { return nil, err } @@ -872,7 +877,7 @@ func (c *Container) getContainerNetworkInfo() (*define.InspectNetworkSettings, e if c.state.NetNS == nil { // We still want to make dummy configurations for each CNI net // the container joined. - if len(networks) > 0 { + if len(networks) > 0 && !isDefault { settings.Networks = make(map[string]*define.InspectAdditionalNetwork, len(networks)) for _, net := range networks { cniNet := new(define.InspectAdditionalNetwork) @@ -893,9 +898,9 @@ func (c *Container) getContainerNetworkInfo() (*define.InspectNetworkSettings, e } // If we have CNI networks - handle that here - if len(networks) > 0 { + if len(networks) > 0 && !isDefault { if len(networks) != len(c.state.NetworkStatus) { - return nil, errors.Wrapf(define.ErrInternal, "network inspection mismatch: asked to join %d CNI networks but have information on %d networks", len(networks), len(c.state.NetworkStatus)) + return nil, errors.Wrapf(define.ErrInternal, "network inspection mismatch: asked to join %d CNI network(s) %v, but have information on %d network(s)", len(networks), networks, len(c.state.NetworkStatus)) } settings.Networks = make(map[string]*define.InspectAdditionalNetwork) @@ -1101,7 +1106,7 @@ func (c *Container) NetworkConnect(nameOrID, netName string, aliases []string) e return err } - ctrNetworks, err := c.networks() + ctrNetworks, _, err := c.networks() if err != nil { return err } @@ -1139,8 +1144,8 @@ func (c *Container) NetworkConnect(nameOrID, netName string, aliases []string) e // build a list of network names so we can sort and // get the new name's index var networkNames []string - for netName := range networks { - networkNames = append(networkNames, netName) + for name := range networks { + networkNames = append(networkNames, name) } networkNames = append(networkNames, netName) // sort @@ -1152,6 +1157,7 @@ func (c *Container) NetworkConnect(nameOrID, netName string, aliases []string) e // populate network status copy(networkStatus[index+1:], networkStatus[index:]) networkStatus[index] = networkResults[0] + c.state.NetworkStatus = networkStatus } c.newNetworkEvent(events.NetworkConnect, netName) return c.save() diff --git a/libpod/rootless_cni_linux.go b/libpod/rootless_cni_linux.go index 1d6158cc2..2c2977f9f 100644 --- a/libpod/rootless_cni_linux.go +++ b/libpod/rootless_cni_linux.go @@ -40,7 +40,7 @@ const ( // // AllocRootlessCNI does not lock c. c should be already locked. func AllocRootlessCNI(ctx context.Context, c *Container) (ns.NetNS, []*cnitypes.Result, error) { - networks, err := c.networks() + networks, _, err := c.networks() if err != nil { return nil, nil, err } @@ -81,7 +81,7 @@ func AllocRootlessCNI(ctx context.Context, c *Container) (ns.NetNS, []*cnitypes. // // DeallocRootlessCNI does not lock c. c should be already locked. func DeallocRootlessCNI(ctx context.Context, c *Container) error { - networks, err := c.networks() + networks, _, err := c.networks() if err != nil { return err } diff --git a/pkg/api/handlers/compat/containers.go b/pkg/api/handlers/compat/containers.go index 00be8e845..5886455e7 100644 --- a/pkg/api/handlers/compat/containers.go +++ b/pkg/api/handlers/compat/containers.go @@ -298,6 +298,9 @@ func LibpodToContainerJSON(l *libpod.Container, sz bool) (*types.ContainerJSON, state.Running = true } + formatCapabilities(inspect.HostConfig.CapDrop) + formatCapabilities(inspect.HostConfig.CapAdd) + h, err := json.Marshal(inspect.HostConfig) if err != nil { return nil, err @@ -318,8 +321,8 @@ func LibpodToContainerJSON(l *libpod.Container, sz bool) (*types.ContainerJSON, cb := types.ContainerJSONBase{ ID: l.ID(), Created: l.CreatedTime().Format(time.RFC3339Nano), - Path: "", - Args: nil, + Path: inspect.Path, + Args: inspect.Args, State: &state, Image: imageName, ResolvConfPath: inspect.ResolvConfPath, @@ -328,7 +331,7 @@ func LibpodToContainerJSON(l *libpod.Container, sz bool) (*types.ContainerJSON, LogPath: l.LogPath(), Node: nil, Name: fmt.Sprintf("/%s", l.Name()), - RestartCount: 0, + RestartCount: int(inspect.RestartCount), Driver: inspect.Driver, Platform: "linux", MountLabel: inspect.MountLabel, @@ -428,3 +431,9 @@ func LibpodToContainerJSON(l *libpod.Container, sz bool) (*types.ContainerJSON, } return &c, nil } + +func formatCapabilities(slice []string) { + for i := range slice { + slice[i] = strings.TrimPrefix(slice[i], "CAP_") + } +} diff --git a/pkg/api/handlers/compat/containers_create.go b/pkg/api/handlers/compat/containers_create.go index 4efe770b3..729639928 100644 --- a/pkg/api/handlers/compat/containers_create.go +++ b/pkg/api/handlers/compat/containers_create.go @@ -19,7 +19,6 @@ import ( func CreateContainer(w http.ResponseWriter, r *http.Request) { runtime := r.Context().Value("runtime").(*libpod.Runtime) decoder := r.Context().Value("decoder").(*schema.Decoder) - input := handlers.CreateContainerConfig{} query := struct { Name string `schema:"name"` }{ @@ -30,11 +29,15 @@ func CreateContainer(w http.ResponseWriter, r *http.Request) { errors.Wrapf(err, "failed to parse parameters for %s", r.URL.String())) return } - if err := json.NewDecoder(r.Body).Decode(&input); err != nil { + + // compatible configuration + body := handlers.CreateContainerConfig{} + if err := json.NewDecoder(r.Body).Decode(&body); err != nil { utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrap(err, "Decode()")) return } - if len(input.HostConfig.Links) > 0 { + + if len(body.HostConfig.Links) > 0 { utils.Error(w, utils.ErrLinkNotSupport.Error(), http.StatusBadRequest, errors.Wrapf(utils.ErrLinkNotSupport, "bad parameter")) return } @@ -43,7 +46,7 @@ func CreateContainer(w http.ResponseWriter, r *http.Request) { utils.Error(w, "unable to obtain runtime config", http.StatusInternalServerError, errors.Wrap(err, "unable to get runtime config")) } - newImage, err := runtime.ImageRuntime().NewFromLocal(input.Image) + newImage, err := runtime.ImageRuntime().NewFromLocal(body.Config.Image) if err != nil { if errors.Cause(err) == define.ErrNoSuchImage { utils.Error(w, "No such image", http.StatusNotFound, err) @@ -54,11 +57,8 @@ func CreateContainer(w http.ResponseWriter, r *http.Request) { return } - // Add the container name to the input struct - input.Name = query.Name - - // Take input structure and convert to cliopts - cliOpts, args, err := common.ContainerCreateToContainerCLIOpts(input, rtc.Engine.CgroupManager) + // Take body structure and convert to cliopts + cliOpts, args, err := common.ContainerCreateToContainerCLIOpts(body, rtc.Engine.CgroupManager) if err != nil { utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrap(err, "make cli opts()")) return @@ -69,6 +69,9 @@ func CreateContainer(w http.ResponseWriter, r *http.Request) { return } + // Override the container name in the body struct + body.Name = query.Name + ic := abi.ContainerEngine{Libpod: runtime} report, err := ic.ContainerCreate(r.Context(), sg) if err != nil { diff --git a/pkg/api/handlers/compat/info.go b/pkg/api/handlers/compat/info.go index 2bb165522..4b3a390f1 100644 --- a/pkg/api/handlers/compat/info.go +++ b/pkg/api/handlers/compat/info.go @@ -17,6 +17,7 @@ import ( "github.com/containers/podman/v2/pkg/api/handlers/utils" "github.com/containers/podman/v2/pkg/rootless" docker "github.com/docker/docker/api/types" + "github.com/docker/docker/api/types/registry" "github.com/docker/docker/api/types/swarm" "github.com/google/uuid" "github.com/pkg/errors" @@ -103,7 +104,7 @@ func GetInfo(w http.ResponseWriter, r *http.Request) { PidsLimit: sysInfo.PidsLimit, Plugins: docker.PluginsInfo{}, ProductLicense: "Apache-2.0", - RegistryConfig: nil, + RegistryConfig: new(registry.ServiceConfig), RuncCommit: docker.Commit{}, Runtimes: getRuntimes(configInfo), SecurityOptions: getSecOpts(sysInfo), diff --git a/pkg/api/handlers/libpod/containers.go b/pkg/api/handlers/libpod/containers.go index 7e6481321..14eb44831 100644 --- a/pkg/api/handlers/libpod/containers.go +++ b/pkg/api/handlers/libpod/containers.go @@ -344,3 +344,27 @@ func InitContainer(w http.ResponseWriter, r *http.Request) { } utils.WriteResponse(w, http.StatusNoContent, "") } + +func ShouldRestart(w http.ResponseWriter, r *http.Request) { + runtime := r.Context().Value("runtime").(*libpod.Runtime) + // Now use the ABI implementation to prevent us from having duplicate + // code. + containerEngine := abi.ContainerEngine{Libpod: runtime} + + name := utils.GetName(r) + report, err := containerEngine.ShouldRestart(r.Context(), name) + if err != nil { + if errors.Cause(err) == define.ErrNoSuchCtr { + utils.ContainerNotFound(w, name, err) + return + } + utils.InternalServerError(w, err) + return + + } + if report.Value { + utils.WriteResponse(w, http.StatusNoContent, "") + } else { + utils.ContainerNotFound(w, name, define.ErrNoSuchCtr) + } +} diff --git a/pkg/api/handlers/types.go b/pkg/api/handlers/types.go index 6bb5f5101..40cf16807 100644 --- a/pkg/api/handlers/types.go +++ b/pkg/api/handlers/types.go @@ -110,11 +110,12 @@ type ContainerWaitOKBody struct { } } +// CreateContainerConfig used when compatible endpoint creates a container type CreateContainerConfig struct { - Name string - dockerContainer.Config - HostConfig dockerContainer.HostConfig - NetworkingConfig dockerNetwork.NetworkingConfig + Name string // container name + dockerContainer.Config // desired container configuration + HostConfig dockerContainer.HostConfig // host dependent configuration for container + NetworkingConfig dockerNetwork.NetworkingConfig // network configuration for container } // swagger:model IDResponse @@ -253,7 +254,7 @@ func ImageDataToImageInspect(ctx context.Context, l *libpodImage.Image) (*ImageI // StdinOnce: false, Env: info.Config.Env, Cmd: info.Config.Cmd, - //Healthcheck: l.ImageData.HealthCheck, + // Healthcheck: l.ImageData.HealthCheck, // ArgsEscaped: false, // Image: "", Volumes: info.Config.Volumes, @@ -261,7 +262,7 @@ func ImageDataToImageInspect(ctx context.Context, l *libpodImage.Image) (*ImageI Entrypoint: info.Config.Entrypoint, // NetworkDisabled: false, // MacAddress: "", - //OnBuild: info.Config.OnBuild, + // OnBuild: info.Config.OnBuild, Labels: info.Labels, StopSignal: info.Config.StopSignal, // StopTimeout: nil, diff --git a/pkg/api/server/handler_api.go b/pkg/api/server/handler_api.go index 28f5a0b42..1d0ddb457 100644 --- a/pkg/api/server/handler_api.go +++ b/pkg/api/server/handler_api.go @@ -30,14 +30,14 @@ func (s *APIServer) APIHandler(h http.HandlerFunc) http.HandlerFunc { // Wrapper to hide some boiler plate fn := func(w http.ResponseWriter, r *http.Request) { rid := uuid.New().String() + logrus.Infof("APIHandler(%s) -- %s %s BEGIN", rid, r.Method, r.URL.String()) if logrus.IsLevelEnabled(logrus.DebugLevel) { - logrus.Debugf("APIHandler(%s) -- Method: %s URL: %s", rid, r.Method, r.URL.String()) for k, v := range r.Header { switch auth.HeaderAuthName(k) { case auth.XRegistryConfigHeader, auth.XRegistryAuthHeader: - logrus.Debugf("APIHandler(%s) -- Header: %s: ", rid, k) + logrus.Debugf("APIHandler(%s) -- Header: %s=", rid, k) default: - logrus.Debugf("APIHandler(%s) -- Header: %s: %v", rid, k, v) + logrus.Debugf("APIHandler(%s) -- Header: %s=%v", rid, k, v) } } } @@ -63,6 +63,7 @@ func (s *APIServer) APIHandler(h http.HandlerFunc) http.HandlerFunc { w.Header().Set("Server", "Libpod/"+lv+" ("+runtime.GOOS+")") h(w, r) + logrus.Debugf("APIHandler(%s) -- %s %s END", rid, r.Method, r.URL.String()) } fn(w, r) } diff --git a/pkg/api/server/listener_api.go b/pkg/api/server/listener_api.go index 4984216b8..2d02df7dc 100644 --- a/pkg/api/server/listener_api.go +++ b/pkg/api/server/listener_api.go @@ -27,5 +27,6 @@ func ListenUnix(network string, path string) (net.Listener, error) { if err != nil { return nil, errors.Wrapf(err, "net.Listen(%s, %s) failed to report the failure to create socket", network, path) } + return listener, nil } diff --git a/pkg/api/server/server.go b/pkg/api/server/server.go index 64008767b..09b6079e4 100644 --- a/pkg/api/server/server.go +++ b/pkg/api/server/server.go @@ -51,10 +51,7 @@ func NewServer(runtime *libpod.Runtime) (*APIServer, error) { } // NewServerWithSettings will create and configure a new API server using provided settings -func NewServerWithSettings(runtime *libpod.Runtime, duration time.Duration, listener *net.Listener) ( - *APIServer, - error, -) { +func NewServerWithSettings(runtime *libpod.Runtime, duration time.Duration, listener *net.Listener) (*APIServer, error) { return newServer(runtime, duration, listener) } @@ -75,6 +72,7 @@ func newServer(runtime *libpod.Runtime, duration time.Duration, listener *net.Li listener = &listeners[0] } + logrus.Infof("API server listening on %q", (*listener).Addr()) router := mux.NewRouter().UseEncodedPath() idle := idle.NewTracker(duration) diff --git a/pkg/bindings/containers/containers.go b/pkg/bindings/containers/containers.go index b5cd2128b..4331ae6c2 100644 --- a/pkg/bindings/containers/containers.go +++ b/pkg/bindings/containers/containers.go @@ -390,3 +390,15 @@ func ContainerInit(ctx context.Context, nameOrID string) error { } return response.Process(nil) } + +func ShouldRestart(ctx context.Context, nameOrID string) (bool, error) { + conn, err := bindings.GetClient(ctx) + if err != nil { + return false, err + } + response, err := conn.DoRequest(nil, http.MethodPost, "/containers/%s/shouldrestart", nil, nil, nameOrID) + if err != nil { + return false, err + } + return response.IsSuccess(), nil +} diff --git a/pkg/domain/entities/images.go b/pkg/domain/entities/images.go index 101542a98..ab545d882 100644 --- a/pkg/domain/entities/images.go +++ b/pkg/domain/entities/images.go @@ -51,10 +51,10 @@ func (i *Image) Id() string { // nolint } type ImageSummary struct { - ID string `json:"Id"` - ParentId string `json:",omitempty"` // nolint - RepoTags []string `json:",omitempty"` - Created int64 `json:",omitempty"` + ID string `json:"Id"` + ParentId string // nolint + RepoTags []string `json:",omitempty"` + Created int64 Size int64 `json:",omitempty"` SharedSize int `json:",omitempty"` VirtualSize int64 `json:",omitempty"` diff --git a/pkg/domain/infra/abi/containers.go b/pkg/domain/infra/abi/containers.go index 4b69ac74e..ff4277a2e 100644 --- a/pkg/domain/infra/abi/containers.go +++ b/pkg/domain/infra/abi/containers.go @@ -911,7 +911,7 @@ func (ic *ContainerEngine) ContainerRun(ctx context.Context, opts entities.Conta } else { report.ExitCode = int(ecode) } - if opts.Rm { + if opts.Rm && !ctr.ShouldRestart(ctx) { if err := ic.Libpod.RemoveContainer(ctx, ctr, false, true); err != nil { if errors.Cause(err) == define.ErrNoSuchCtr || errors.Cause(err) == define.ErrCtrRemoved { @@ -992,7 +992,7 @@ func (ic *ContainerEngine) ContainerCleanup(ctx context.Context, namesOrIds []st return []*entities.ContainerCleanupReport{}, nil } - if options.Remove { + if options.Remove && !ctr.ShouldRestart(ctx) { err = ic.Libpod.RemoveContainer(ctx, ctr, false, true) if err != nil { report.RmErr = errors.Wrapf(err, "failed to cleanup and remove container %v", ctr.ID()) @@ -1015,6 +1015,7 @@ func (ic *ContainerEngine) ContainerCleanup(ctx context.Context, namesOrIds []st _, err = ic.Libpod.RemoveImage(ctx, ctrImage, false) report.RmiErr = err } + reports = append(reports, &report) } return reports, nil @@ -1314,3 +1315,13 @@ func (ic *ContainerEngine) ContainerStats(ctx context.Context, namesOrIds []stri return statsChan, nil } + +// ShouldRestart returns whether the container should be restarted +func (ic *ContainerEngine) ShouldRestart(ctx context.Context, nameOrID string) (*entities.BoolReport, error) { + ctr, err := ic.Libpod.LookupContainer(nameOrID) + if err != nil { + return nil, err + } + + return &entities.BoolReport{Value: ctr.ShouldRestart(ctx)}, nil +} diff --git a/pkg/domain/infra/tunnel/containers.go b/pkg/domain/infra/tunnel/containers.go index 8066e1c00..1aa5afbe7 100644 --- a/pkg/domain/infra/tunnel/containers.go +++ b/pkg/domain/infra/tunnel/containers.go @@ -595,12 +595,20 @@ func (ic *ContainerEngine) ContainerRun(ctx context.Context, opts entities.Conta // Defer the removal, so we can return early if needed and // de-spaghetti the code. defer func() { - if err := containers.Remove(ic.ClientCxt, con.ID, bindings.PFalse, bindings.PTrue); err != nil { - if errorhandling.Contains(err, define.ErrNoSuchCtr) || - errorhandling.Contains(err, define.ErrCtrRemoved) { - logrus.Warnf("Container %s does not exist: %v", con.ID, err) - } else { - logrus.Errorf("Error removing container %s: %v", con.ID, err) + shouldRestart, err := containers.ShouldRestart(ic.ClientCxt, con.ID) + if err != nil { + logrus.Errorf("Failed to check if %s should restart: %v", con.ID, err) + return + } + + if !shouldRestart { + if err := containers.Remove(ic.ClientCxt, con.ID, bindings.PFalse, bindings.PTrue); err != nil { + if errorhandling.Contains(err, define.ErrNoSuchCtr) || + errorhandling.Contains(err, define.ErrCtrRemoved) { + logrus.Warnf("Container %s does not exist: %v", con.ID, err) + } else { + logrus.Errorf("Error removing container %s: %v", con.ID, err) + } } } }() @@ -737,3 +745,8 @@ func (ic *ContainerEngine) ContainerStats(ctx context.Context, namesOrIds []stri } return containers.Stats(ic.ClientCxt, namesOrIds, &options.Stream) } + +// ShouldRestart reports back whether the containre will restart +func (ic *ContainerEngine) ShouldRestart(_ context.Context, id string) (bool, error) { + return containers.ShouldRestart(ic.ClientCxt, id) +} diff --git a/pkg/specgen/generate/container_create.go b/pkg/specgen/generate/container_create.go index c049e64cf..45a374216 100644 --- a/pkg/specgen/generate/container_create.go +++ b/pkg/specgen/generate/container_create.go @@ -111,7 +111,7 @@ func MakeContainer(ctx context.Context, rt *libpod.Runtime, s *specgen.SpecGener return nil, errors.Wrap(err, "invalid config provided") } - finalMounts, finalVolumes, err := finalizeMounts(ctx, s, rt, rtc, newImage) + finalMounts, finalVolumes, finalOverlays, err := finalizeMounts(ctx, s, rt, rtc, newImage) if err != nil { return nil, err } @@ -121,7 +121,7 @@ func MakeContainer(ctx context.Context, rt *libpod.Runtime, s *specgen.SpecGener return nil, err } - opts, err := createContainerOptions(ctx, rt, s, pod, finalVolumes, newImage, command) + opts, err := createContainerOptions(ctx, rt, s, pod, finalVolumes, finalOverlays, newImage, command) if err != nil { return nil, err } @@ -144,7 +144,7 @@ func MakeContainer(ctx context.Context, rt *libpod.Runtime, s *specgen.SpecGener return rt.NewContainer(ctx, runtimeSpec, options...) } -func createContainerOptions(ctx context.Context, rt *libpod.Runtime, s *specgen.SpecGenerator, pod *libpod.Pod, volumes []*specgen.NamedVolume, img *image.Image, command []string) ([]libpod.CtrCreateOption, error) { +func createContainerOptions(ctx context.Context, rt *libpod.Runtime, s *specgen.SpecGenerator, pod *libpod.Pod, volumes []*specgen.NamedVolume, overlays []*specgen.OverlayVolume, img *image.Image, command []string) ([]libpod.CtrCreateOption, error) { var options []libpod.CtrCreateOption var err error @@ -224,7 +224,7 @@ func createContainerOptions(ctx context.Context, rt *libpod.Runtime, s *specgen. for _, volume := range volumes { destinations = append(destinations, volume.Dest) } - for _, overlayVolume := range s.OverlayVolumes { + for _, overlayVolume := range overlays { destinations = append(destinations, overlayVolume.Destination) } for _, imageVolume := range s.ImageVolumes { @@ -244,9 +244,9 @@ func createContainerOptions(ctx context.Context, rt *libpod.Runtime, s *specgen. options = append(options, libpod.WithNamedVolumes(vols)) } - if len(s.OverlayVolumes) != 0 { + if len(overlays) != 0 { var vols []*libpod.ContainerOverlayVolume - for _, v := range s.OverlayVolumes { + for _, v := range overlays { vols = append(vols, &libpod.ContainerOverlayVolume{ Dest: v.Destination, Source: v.Source, diff --git a/pkg/specgen/generate/storage.go b/pkg/specgen/generate/storage.go index b225f79ee..331a5c5bf 100644 --- a/pkg/specgen/generate/storage.go +++ b/pkg/specgen/generate/storage.go @@ -33,17 +33,17 @@ var ( ) // Produce final mounts and named volumes for a container -func finalizeMounts(ctx context.Context, s *specgen.SpecGenerator, rt *libpod.Runtime, rtc *config.Config, img *image.Image) ([]spec.Mount, []*specgen.NamedVolume, error) { +func finalizeMounts(ctx context.Context, s *specgen.SpecGenerator, rt *libpod.Runtime, rtc *config.Config, img *image.Image) ([]spec.Mount, []*specgen.NamedVolume, []*specgen.OverlayVolume, error) { // Get image volumes baseMounts, baseVolumes, err := getImageVolumes(ctx, img, s) if err != nil { - return nil, nil, err + return nil, nil, nil, err } // Get volumes-from mounts volFromMounts, volFromVolumes, err := getVolumesFrom(s.VolumesFrom, rt) if err != nil { - return nil, nil, err + return nil, nil, nil, err } // Supersede from --volumes-from. @@ -57,19 +57,53 @@ func finalizeMounts(ctx context.Context, s *specgen.SpecGenerator, rt *libpod.Ru // Need to make map forms of specgen mounts/volumes. unifiedMounts := map[string]spec.Mount{} unifiedVolumes := map[string]*specgen.NamedVolume{} + unifiedOverlays := map[string]*specgen.OverlayVolume{} + + // Need to make map forms of specgen mounts/volumes. + commonMounts, commonVolumes, commonOverlayVolumes, err := specgen.GenVolumeMounts(rtc.Volumes()) + if err != nil { + return nil, nil, nil, err + } + for _, m := range s.Mounts { if _, ok := unifiedMounts[m.Destination]; ok { - return nil, nil, errors.Wrapf(errDuplicateDest, "conflict in specified mounts - multiple mounts at %q", m.Destination) + return nil, nil, nil, errors.Wrapf(errDuplicateDest, "conflict in specified mounts - multiple mounts at %q", m.Destination) } unifiedMounts[m.Destination] = m } + + for _, m := range commonMounts { + if _, ok := unifiedMounts[m.Destination]; !ok { + unifiedMounts[m.Destination] = m + } + } + for _, v := range s.Volumes { if _, ok := unifiedVolumes[v.Dest]; ok { - return nil, nil, errors.Wrapf(errDuplicateDest, "conflict in specified volumes - multiple volumes at %q", v.Dest) + return nil, nil, nil, errors.Wrapf(errDuplicateDest, "conflict in specified volumes - multiple volumes at %q", v.Dest) } unifiedVolumes[v.Dest] = v } + for _, v := range commonVolumes { + if _, ok := unifiedVolumes[v.Dest]; !ok { + unifiedVolumes[v.Dest] = v + } + } + + for _, v := range s.OverlayVolumes { + if _, ok := unifiedOverlays[v.Destination]; ok { + return nil, nil, nil, errors.Wrapf(errDuplicateDest, "conflict in specified volumes - multiple volumes at %q", v.Destination) + } + unifiedOverlays[v.Destination] = v + } + + for _, v := range commonOverlayVolumes { + if _, ok := unifiedOverlays[v.Destination]; ok { + unifiedOverlays[v.Destination] = v + } + } + // If requested, add container init binary if s.Init { initPath := s.InitPath @@ -78,10 +112,10 @@ func finalizeMounts(ctx context.Context, s *specgen.SpecGenerator, rt *libpod.Ru } initMount, err := addContainerInitBinary(s, initPath) if err != nil { - return nil, nil, err + return nil, nil, nil, err } if _, ok := unifiedMounts[initMount.Destination]; ok { - return nil, nil, errors.Wrapf(errDuplicateDest, "conflict with mount added by --init to %q", initMount.Destination) + return nil, nil, nil, errors.Wrapf(errDuplicateDest, "conflict with mount added by --init to %q", initMount.Destination) } unifiedMounts[initMount.Destination] = initMount } @@ -115,12 +149,12 @@ func finalizeMounts(ctx context.Context, s *specgen.SpecGenerator, rt *libpod.Ru // Check for conflicts between named volumes and mounts for dest := range baseMounts { if _, ok := baseVolumes[dest]; ok { - return nil, nil, errors.Wrapf(errDuplicateDest, "conflict at mount destination %v", dest) + return nil, nil, nil, errors.Wrapf(errDuplicateDest, "conflict at mount destination %v", dest) } } for dest := range baseVolumes { if _, ok := baseMounts[dest]; ok { - return nil, nil, errors.Wrapf(errDuplicateDest, "conflict at mount destination %v", dest) + return nil, nil, nil, errors.Wrapf(errDuplicateDest, "conflict at mount destination %v", dest) } } // Final step: maps to arrays @@ -129,7 +163,7 @@ func finalizeMounts(ctx context.Context, s *specgen.SpecGenerator, rt *libpod.Ru if mount.Type == TypeBind { absSrc, err := filepath.Abs(mount.Source) if err != nil { - return nil, nil, errors.Wrapf(err, "error getting absolute path of %s", mount.Source) + return nil, nil, nil, errors.Wrapf(err, "error getting absolute path of %s", mount.Source) } mount.Source = absSrc } @@ -140,7 +174,12 @@ func finalizeMounts(ctx context.Context, s *specgen.SpecGenerator, rt *libpod.Ru finalVolumes = append(finalVolumes, volume) } - return finalMounts, finalVolumes, nil + finalOverlays := make([]*specgen.OverlayVolume, 0, len(unifiedOverlays)) + for _, volume := range unifiedOverlays { + finalOverlays = append(finalOverlays, volume) + } + + return finalMounts, finalVolumes, finalOverlays, nil } // Get image volumes from the given image diff --git a/pkg/specgen/namespaces.go b/pkg/specgen/namespaces.go index 90c56d366..11108a5c1 100644 --- a/pkg/specgen/namespaces.go +++ b/pkg/specgen/namespaces.go @@ -272,16 +272,10 @@ func ParseNetworkNamespace(ns string) (Namespace, []string, error) { toReturn.NSMode = Private case strings.HasPrefix(ns, "ns:"): split := strings.SplitN(ns, ":", 2) - if len(split) != 2 { - return toReturn, nil, errors.Errorf("must provide a path to a namespace when specifying ns:") - } toReturn.NSMode = Path toReturn.Value = split[1] case strings.HasPrefix(ns, "container:"): split := strings.SplitN(ns, ":", 2) - if len(split) != 2 { - return toReturn, nil, errors.Errorf("must provide name or ID or a container when specifying container:") - } toReturn.NSMode = FromContainer toReturn.Value = split[1] default: diff --git a/pkg/specgen/specgen.go b/pkg/specgen/specgen.go index 0a9a16ea7..fad2406e5 100644 --- a/pkg/specgen/specgen.go +++ b/pkg/specgen/specgen.go @@ -1,13 +1,13 @@ package specgen import ( - "errors" "net" "syscall" "github.com/containers/image/v5/manifest" "github.com/containers/storage" spec "github.com/opencontainers/runtime-spec/specs-go" + "github.com/pkg/errors" ) // LogConfig describes the logging characteristics for a container @@ -459,42 +459,6 @@ type SpecGenerator struct { ContainerHealthCheckConfig } -// NamedVolume holds information about a named volume that will be mounted into -// the container. -type NamedVolume struct { - // Name is the name of the named volume to be mounted. May be empty. - // If empty, a new named volume with a pseudorandomly generated name - // will be mounted at the given destination. - Name string - // Destination to mount the named volume within the container. Must be - // an absolute path. Path will be created if it does not exist. - Dest string - // Options are options that the named volume will be mounted with. - Options []string -} - -// OverlayVolume holds information about a overlay volume that will be mounted into -// the container. -type OverlayVolume struct { - // Destination is the absolute path where the mount will be placed in the container. - Destination string `json:"destination"` - // Source specifies the source path of the mount. - Source string `json:"source,omitempty"` -} - -// ImageVolume is a volume based on a container image. The container image is -// first mounted on the host and is then bind-mounted into the container. An -// ImageVolume is always mounted read only. -type ImageVolume struct { - // Source is the source of the image volume. The image can be referred - // to by name and by ID. - Source string - // Destination is the absolute path of the mount in the container. - Destination string - // ReadWrite sets the volume writable. - ReadWrite bool -} - // PortMapping is one or more ports that will be mapped into the container. type PortMapping struct { // HostIP is the IP that we will bind to on the host. diff --git a/pkg/specgen/volumes.go b/pkg/specgen/volumes.go new file mode 100644 index 000000000..1178f9960 --- /dev/null +++ b/pkg/specgen/volumes.go @@ -0,0 +1,149 @@ +package specgen + +import ( + "path/filepath" + "strings" + + "github.com/containers/buildah/pkg/parse" + spec "github.com/opencontainers/runtime-spec/specs-go" + "github.com/pkg/errors" + "github.com/sirupsen/logrus" +) + +// NamedVolume holds information about a named volume that will be mounted into +// the container. +type NamedVolume struct { + // Name is the name of the named volume to be mounted. May be empty. + // If empty, a new named volume with a pseudorandomly generated name + // will be mounted at the given destination. + Name string + // Destination to mount the named volume within the container. Must be + // an absolute path. Path will be created if it does not exist. + Dest string + // Options are options that the named volume will be mounted with. + Options []string +} + +// OverlayVolume holds information about a overlay volume that will be mounted into +// the container. +type OverlayVolume struct { + // Destination is the absolute path where the mount will be placed in the container. + Destination string `json:"destination"` + // Source specifies the source path of the mount. + Source string `json:"source,omitempty"` +} + +// ImageVolume is a volume based on a container image. The container image is +// first mounted on the host and is then bind-mounted into the container. An +// ImageVolume is always mounted read only. +type ImageVolume struct { + // Source is the source of the image volume. The image can be referred + // to by name and by ID. + Source string + // Destination is the absolute path of the mount in the container. + Destination string + // ReadWrite sets the volume writable. + ReadWrite bool +} + +// GenVolumeMounts parses user input into mounts, volumes and overlay volumes +func GenVolumeMounts(volumeFlag []string) (map[string]spec.Mount, map[string]*NamedVolume, map[string]*OverlayVolume, error) { + errDuplicateDest := errors.Errorf("duplicate mount destination") + + mounts := make(map[string]spec.Mount) + volumes := make(map[string]*NamedVolume) + overlayVolumes := make(map[string]*OverlayVolume) + + volumeFormatErr := errors.Errorf("incorrect volume format, should be [host-dir:]ctr-dir[:option]") + + for _, vol := range volumeFlag { + var ( + options []string + src string + dest string + err error + ) + + splitVol := strings.Split(vol, ":") + if len(splitVol) > 3 { + return nil, nil, nil, errors.Wrapf(volumeFormatErr, vol) + } + + src = splitVol[0] + if len(splitVol) == 1 { + // This is an anonymous named volume. Only thing given + // is destination. + // Name/source will be blank, and populated by libpod. + src = "" + dest = splitVol[0] + } else if len(splitVol) > 1 { + dest = splitVol[1] + } + if len(splitVol) > 2 { + if options, err = parse.ValidateVolumeOpts(strings.Split(splitVol[2], ",")); err != nil { + return nil, nil, nil, err + } + } + + // Do not check source dir for anonymous volumes + if len(splitVol) > 1 { + if err := parse.ValidateVolumeHostDir(src); err != nil { + return nil, nil, nil, err + } + } + if err := parse.ValidateVolumeCtrDir(dest); err != nil { + return nil, nil, nil, err + } + + cleanDest := filepath.Clean(dest) + + if strings.HasPrefix(src, "/") || strings.HasPrefix(src, ".") { + // This is not a named volume + overlayFlag := false + for _, o := range options { + if o == "O" { + overlayFlag = true + if len(options) > 1 { + return nil, nil, nil, errors.New("can't use 'O' with other options") + } + } + } + if overlayFlag { + // This is a overlay volume + newOverlayVol := new(OverlayVolume) + newOverlayVol.Destination = cleanDest + newOverlayVol.Source = src + if _, ok := overlayVolumes[newOverlayVol.Destination]; ok { + return nil, nil, nil, errors.Wrapf(errDuplicateDest, newOverlayVol.Destination) + } + overlayVolumes[newOverlayVol.Destination] = newOverlayVol + } else { + newMount := spec.Mount{ + Destination: cleanDest, + Type: "bind", + Source: src, + Options: options, + } + if _, ok := mounts[newMount.Destination]; ok { + return nil, nil, nil, errors.Wrapf(errDuplicateDest, newMount.Destination) + } + mounts[newMount.Destination] = newMount + } + } else { + // This is a named volume + newNamedVol := new(NamedVolume) + newNamedVol.Name = src + newNamedVol.Dest = cleanDest + newNamedVol.Options = options + + if _, ok := volumes[newNamedVol.Dest]; ok { + return nil, nil, nil, errors.Wrapf(errDuplicateDest, newNamedVol.Dest) + } + volumes[newNamedVol.Dest] = newNamedVol + } + + logrus.Debugf("User mount %s:%s options %v", src, dest, options) + } + + return mounts, volumes, overlayVolumes, nil +} diff --git a/test/apiv2/rest_api/__init__.py b/test/apiv2/rest_api/__init__.py index 8100a4df5..db0257f03 100644 --- a/test/apiv2/rest_api/__init__.py +++ b/test/apiv2/rest_api/__init__.py @@ -16,19 +16,18 @@ class Podman(object): binary = os.getenv("PODMAN", "bin/podman") self.cmd = [binary, "--storage-driver=vfs"] - cgroupfs = os.getenv("CGROUP_MANAGER", "cgroupfs") + cgroupfs = os.getenv("CGROUP_MANAGER", "systemd") self.cmd.append(f"--cgroup-manager={cgroupfs}") if os.getenv("DEBUG"): self.cmd.append("--log-level=debug") + self.cmd.append("--syslog=true") self.anchor_directory = tempfile.mkdtemp(prefix="podman_restapi_") self.cmd.append("--root=" + os.path.join(self.anchor_directory, "crio")) self.cmd.append("--runroot=" + os.path.join(self.anchor_directory, "crio-run")) - os.environ["REGISTRIES_CONFIG_PATH"] = os.path.join( - self.anchor_directory, "registry.conf" - ) + os.environ["REGISTRIES_CONFIG_PATH"] = os.path.join(self.anchor_directory, "registry.conf") p = configparser.ConfigParser() p.read_dict( { @@ -40,14 +39,10 @@ class Podman(object): with open(os.environ["REGISTRIES_CONFIG_PATH"], "w") as w: p.write(w) - os.environ["CNI_CONFIG_PATH"] = os.path.join( - self.anchor_directory, "cni", "net.d" - ) + os.environ["CNI_CONFIG_PATH"] = os.path.join(self.anchor_directory, "cni", "net.d") os.makedirs(os.environ["CNI_CONFIG_PATH"], exist_ok=True) self.cmd.append("--cni-config-dir=" + os.environ["CNI_CONFIG_PATH"]) - cni_cfg = os.path.join( - os.environ["CNI_CONFIG_PATH"], "87-podman-bridge.conflist" - ) + cni_cfg = os.path.join(os.environ["CNI_CONFIG_PATH"], "87-podman-bridge.conflist") # json decoded and encoded to ensure legal json buf = json.loads( """ diff --git a/test/apiv2/rest_api/test_rest_v2_0_0.py b/test/apiv2/rest_api/test_rest_v2_0_0.py index 49e18f063..52348d4f4 100644 --- a/test/apiv2/rest_api/test_rest_v2_0_0.py +++ b/test/apiv2/rest_api/test_rest_v2_0_0.py @@ -61,9 +61,7 @@ class TestApi(unittest.TestCase): super().setUpClass() TestApi.podman = Podman() - TestApi.service = TestApi.podman.open( - "system", "service", "tcp:localhost:8080", "--time=0" - ) + TestApi.service = TestApi.podman.open("system", "service", "tcp:localhost:8080", "--time=0") # give the service some time to be ready... time.sleep(2) @@ -165,11 +163,71 @@ class TestApi(unittest.TestCase): r = requests.get(_url(ctnr("/containers/{}/logs?stdout=true"))) self.assertEqual(r.status_code, 200, r.text) - def test_post_create_compat(self): + # TODO Need to support Docker-py order of network/container creates + def test_post_create_compat_connect(self): """Create network and container then connect to network""" - net = requests.post( - PODMAN_URL + "/v1.40/networks/create", json={"Name": "TestNetwork"} + net_default = requests.post( + PODMAN_URL + "/v1.40/networks/create", json={"Name": "TestDefaultNetwork"} + ) + self.assertEqual(net_default.status_code, 201, net_default.text) + + create = requests.post( + PODMAN_URL + "/v1.40/containers/create?name=postCreate", + json={ + "Cmd": ["top"], + "Image": "alpine:latest", + "NetworkDisabled": False, + # FIXME adding these 2 lines cause: (This is sampled from docker-py) + # "network already exists","message":"container + # 01306e499df5441560d70071a54342611e422a94de20865add50a9565fd79fb9 is already connected to CNI network \"TestDefaultNetwork\": network already exists" + # "HostConfig": {"NetworkMode": "TestDefaultNetwork"}, + # "NetworkingConfig": {"EndpointsConfig": {"TestDefaultNetwork": None}}, + # FIXME These two lines cause: + # CNI network \"TestNetwork\" not found","message":"error configuring network namespace for container 369ddfa7d3211ebf1fbd5ddbff91bd33fa948858cea2985c133d6b6507546dff: CNI network \"TestNetwork\" not found" + # "HostConfig": {"NetworkMode": "TestNetwork"}, + # "NetworkingConfig": {"EndpointsConfig": {"TestNetwork": None}}, + # FIXME no networking defined cause: (note this error is from the container inspect below) + # "internal libpod error","message":"network inspection mismatch: asked to join 2 CNI network(s) [TestDefaultNetwork podman], but have information on 1 network(s): internal libpod error" + }, + ) + self.assertEqual(create.status_code, 201, create.text) + payload = json.loads(create.text) + self.assertIsNotNone(payload["Id"]) + + start = requests.post(PODMAN_URL + f"/v1.40/containers/{payload['Id']}/start") + self.assertEqual(start.status_code, 204, start.text) + + connect = requests.post( + PODMAN_URL + "/v1.40/networks/TestDefaultNetwork/connect", + json={"Container": payload["Id"]}, + ) + self.assertEqual(connect.status_code, 200, connect.text) + self.assertEqual(connect.text, "OK\n") + + inspect = requests.get(f"{PODMAN_URL}/v1.40/containers/{payload['Id']}/json") + self.assertEqual(inspect.status_code, 200, inspect.text) + + payload = json.loads(inspect.text) + self.assertFalse(payload["Config"].get("NetworkDisabled", False)) + + self.assertEqual( + "TestDefaultNetwork", + payload["NetworkSettings"]["Networks"]["TestDefaultNetwork"]["NetworkID"], ) + # TODO restore this to test, when joining multiple networks possible + # self.assertEqual( + # "TestNetwork", + # payload["NetworkSettings"]["Networks"]["TestNetwork"]["NetworkID"], + # ) + # TODO Need to support network aliases + # self.assertIn( + # "test_post_create", + # payload["NetworkSettings"]["Networks"]["TestNetwork"]["Aliases"], + # ) + + def test_post_create_compat(self): + """Create network and connect container during create""" + net = requests.post(PODMAN_URL + "/v1.40/networks/create", json={"Name": "TestNetwork"}) self.assertEqual(net.status_code, 201, net.text) create = requests.post( @@ -178,23 +236,21 @@ class TestApi(unittest.TestCase): "Cmd": ["date"], "Image": "alpine:latest", "NetworkDisabled": False, - "NetworkConfig": { - "EndpointConfig": {"TestNetwork": {"Aliases": ["test_post_create"]}} - }, + "HostConfig": {"NetworkMode": "TestNetwork"}, }, ) self.assertEqual(create.status_code, 201, create.text) payload = json.loads(create.text) self.assertIsNotNone(payload["Id"]) - # This cannot be done until full completion of the network connect - # stack and network disconnect stack are complete - # connect = requests.post( - # PODMAN_URL + "/v1.40/networks/TestNetwork/connect", - # json={"Container": payload["Id"]}, - # ) - # self.assertEqual(connect.status_code, 200, connect.text) - # self.assertEqual(connect.text, "OK\n") + inspect = requests.get(f"{PODMAN_URL}/v1.40/containers/{payload['Id']}/json") + self.assertEqual(inspect.status_code, 200, inspect.text) + payload = json.loads(inspect.text) + self.assertFalse(payload["Config"].get("NetworkDisabled", False)) + self.assertEqual( + "TestNetwork", + payload["NetworkSettings"]["Networks"]["TestNetwork"]["NetworkID"], + ) def test_commit(self): r = requests.post(_url(ctnr("/commit?container={}"))) diff --git a/test/apiv2/rest_api/v1_test_rest_v1_0_0.py b/test/apiv2/rest_api/v1_test_rest_v1_0_0.py index acd6273ef..23528a246 100644 --- a/test/apiv2/rest_api/v1_test_rest_v1_0_0.py +++ b/test/apiv2/rest_api/v1_test_rest_v1_0_0.py @@ -84,9 +84,7 @@ class TestApi(unittest.TestCase): print("\nService Stderr:\n" + stderr.decode("utf-8")) if TestApi.podman.returncode > 0: - sys.stderr.write( - "podman exited with error code {}\n".format(TestApi.podman.returncode) - ) + sys.stderr.write("podman exited with error code {}\n".format(TestApi.podman.returncode)) sys.exit(2) return super().tearDownClass() diff --git a/test/e2e/config/containers-remote.conf b/test/e2e/config/containers-remote.conf new file mode 100644 index 000000000..bc9eab951 --- /dev/null +++ b/test/e2e/config/containers-remote.conf @@ -0,0 +1,51 @@ +[containers] + +# A list of ulimits to be set in containers by default, specified as +# "=:", for example: +# "nofile=1024:2048" +# See setrlimit(2) for a list of resource names. +# Any limit not specified here will be inherited from the process launching the +# container engine. +# Ulimits has limits for non privileged container engines. +# +default_ulimits = [ + "nofile=100:100", +] + +# Environment variable list for the conmon process; used for passing necessary +# environment variables to conmon or the runtime. +# +env = [ + "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin", + "foo=bar1", +] + +# container engines use container separation using MAC(SELinux) labeling. +# Flag is ignored on label disabled systems. +# +label = false + +# Size of /dev/shm. Specified as . +# Unit is optional, values: +# b (bytes), k (kilobytes), m (megabytes), or g (gigabytes). +# If the unit is omitted, the system uses bytes. +# +shm_size = "202k" + +# List of devices. Specified as +# "::", for example: +# "/dev/sdc:/dev/xvdc:rwm". +# If it is empty or commented out, only the default devices will be used +# +devices = [] + +default_sysctls = [ + "net.ipv4.ping_group_range=0 0", +] + +dns_searches=[ "barfoo.com", ] +dns_servers=[ "4.3.2.1", ] + +tz = "America/New_York" + +umask = "0022" diff --git a/test/e2e/containers_conf_test.go b/test/e2e/containers_conf_test.go index 1d5be218b..906153c0f 100644 --- a/test/e2e/containers_conf_test.go +++ b/test/e2e/containers_conf_test.go @@ -177,6 +177,9 @@ var _ = Describe("Podman run", func() { } os.Setenv("CONTAINERS_CONF", conffile) + if IsRemote() { + podmanTest.RestartRemoteService() + } result := podmanTest.Podman([]string{"run", ALPINE, "ls", tempdir}) result.WaitWithDefaultTimeout() Expect(result.ExitCode()).To(Equal(0)) @@ -224,6 +227,17 @@ var _ = Describe("Podman run", func() { Expect(session.LineInOuputStartsWith("search")).To(BeFalse()) }) + It("podman run use containers.conf search domain", func() { + session := podmanTest.Podman([]string{"run", ALPINE, "cat", "/etc/resolv.conf"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + Expect(session.LineInOuputStartsWith("search")).To(BeTrue()) + Expect(session.OutputToString()).To(ContainSubstring("foobar.com")) + + Expect(session.OutputToString()).To(ContainSubstring("1.2.3.4")) + Expect(session.OutputToString()).To(ContainSubstring("debug")) + }) + It("podman run containers.conf timezone", func() { //containers.conf timezone set to Pacific/Honolulu session := podmanTest.Podman([]string{"run", ALPINE, "date", "+'%H %Z'"}) @@ -231,6 +245,7 @@ var _ = Describe("Podman run", func() { Expect(session.ExitCode()).To(Equal(0)) Expect(session.OutputToString()).To(ContainSubstring("HST")) }) + It("podman run containers.conf umask", func() { //containers.conf umask set to 0002 if !strings.Contains(podmanTest.OCIRuntime, "crun") { @@ -243,4 +258,57 @@ var _ = Describe("Podman run", func() { Expect(session.OutputToString()).To(Equal("0002")) }) + It("podman-remote test localcontainers.conf versus remote containers.conf", func() { + if !IsRemote() { + Skip("this test is only for remote") + } + + os.Setenv("CONTAINERS_CONF", "config/containers-remote.conf") + // Configuration that comes from remote server + // env + session := podmanTest.Podman([]string{"run", ALPINE, "printenv", "foo"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + Expect(session.OutputToString()).To(Equal("bar")) + + // dns-search, server, options + session = podmanTest.Podman([]string{"run", ALPINE, "cat", "/etc/resolv.conf"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + Expect(session.LineInOuputStartsWith("search")).To(BeTrue()) + Expect(session.OutputToString()).To(ContainSubstring("foobar.com")) + Expect(session.OutputToString()).To(ContainSubstring("1.2.3.4")) + Expect(session.OutputToString()).To(ContainSubstring("debug")) + + // sysctls + session = podmanTest.Podman([]string{"run", "--rm", ALPINE, "cat", "/proc/sys/net/ipv4/ping_group_range"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + Expect(session.OutputToString()).To(ContainSubstring("1000")) + + // shm-size + session = podmanTest.Podman([]string{"run", ALPINE, "grep", "shm", "/proc/self/mounts"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + Expect(session.OutputToString()).To(ContainSubstring("size=200k")) + + // ulimits + session = podmanTest.Podman([]string{"run", "--rm", fedoraMinimal, "ulimit", "-n"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + Expect(session.OutputToString()).To(ContainSubstring("500")) + + // Configuration that comes from remote client + // Timezone + session = podmanTest.Podman([]string{"run", ALPINE, "date", "+'%H %Z'"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + Expect(session.OutputToString()).To(ContainSubstring("EST")) + + // Umask + session = podmanTest.Podman([]string{"run", "--rm", ALPINE, "sh", "-c", "umask"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + Expect(session.OutputToString()).To(Equal("0022")) + }) }) diff --git a/test/e2e/create_staticmac_test.go b/test/e2e/create_staticmac_test.go index adffdc1ca..1ac431da2 100644 --- a/test/e2e/create_staticmac_test.go +++ b/test/e2e/create_staticmac_test.go @@ -5,6 +5,7 @@ import ( "github.com/containers/podman/v2/pkg/rootless" . "github.com/containers/podman/v2/test/utils" + "github.com/containers/storage/pkg/stringid" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" ) @@ -45,4 +46,21 @@ var _ = Describe("Podman run with --mac-address flag", func() { Expect(result.OutputToString()).To(ContainSubstring("92:d0:c6:0a:29:34")) } }) + + It("Podman run --mac-address with custom network", func() { + net := "n1" + stringid.GenerateNonCryptoID() + session := podmanTest.Podman([]string{"network", "create", net}) + session.WaitWithDefaultTimeout() + defer podmanTest.removeCNINetwork(net) + Expect(session.ExitCode()).To(BeZero()) + + result := podmanTest.Podman([]string{"run", "--network", net, "--mac-address", "92:d0:c6:00:29:34", ALPINE, "ip", "addr"}) + result.WaitWithDefaultTimeout() + if rootless.IsRootless() { + Expect(result.ExitCode()).To(Equal(125)) + } else { + Expect(result.ExitCode()).To(Equal(0)) + Expect(result.OutputToString()).To(ContainSubstring("92:d0:c6:00:29:34")) + } + }) }) diff --git a/test/e2e/network_test.go b/test/e2e/network_test.go index c6593df34..139a90ac7 100644 --- a/test/e2e/network_test.go +++ b/test/e2e/network_test.go @@ -76,31 +76,36 @@ var _ = Describe("Podman network", func() { Expect(session.LineInOutputContains(name)).To(BeFalse()) }) - It("podman network rm no args", func() { - session := podmanTest.Podman([]string{"network", "rm"}) - session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).ToNot(BeZero()) - }) - - It("podman network rm", func() { - SkipIfRootless("FIXME: This one is definitely broken in rootless mode") - name, path := generateNetworkConfig(podmanTest) - defer removeConf(path) - - session := podmanTest.Podman([]string{"network", "ls", "--quiet"}) - session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) - Expect(session.LineInOutputContains(name)).To(BeTrue()) - - rm := podmanTest.Podman([]string{"network", "rm", name}) - rm.WaitWithDefaultTimeout() - Expect(rm.ExitCode()).To(BeZero()) - - results := podmanTest.Podman([]string{"network", "ls", "--quiet"}) - results.WaitWithDefaultTimeout() - Expect(results.ExitCode()).To(Equal(0)) - Expect(results.LineInOutputContains(name)).To(BeFalse()) - }) + rm_func := func(rm string) { + It(fmt.Sprintf("podman network %s no args", rm), func() { + session := podmanTest.Podman([]string{"network", rm}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).ToNot(BeZero()) + + }) + + It(fmt.Sprintf("podman network %s", rm), func() { + name, path := generateNetworkConfig(podmanTest) + defer removeConf(path) + + session := podmanTest.Podman([]string{"network", "ls", "--quiet"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + Expect(session.LineInOutputContains(name)).To(BeTrue()) + + rm := podmanTest.Podman([]string{"network", rm, name}) + rm.WaitWithDefaultTimeout() + Expect(rm.ExitCode()).To(BeZero()) + + results := podmanTest.Podman([]string{"network", "ls", "--quiet"}) + results.WaitWithDefaultTimeout() + Expect(results.ExitCode()).To(Equal(0)) + Expect(results.LineInOutputContains(name)).To(BeFalse()) + }) + } + + rm_func("rm") + rm_func("remove") It("podman network inspect no args", func() { session := podmanTest.Podman([]string{"network", "inspect"}) diff --git a/test/e2e/pod_create_test.go b/test/e2e/pod_create_test.go index be0a2f6f0..ccfbcefae 100644 --- a/test/e2e/pod_create_test.go +++ b/test/e2e/pod_create_test.go @@ -9,6 +9,7 @@ import ( "github.com/containers/podman/v2/pkg/rootless" . "github.com/containers/podman/v2/test/utils" + "github.com/containers/storage/pkg/stringid" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" ) @@ -476,4 +477,23 @@ entrypoint ["/fromimage"] Expect(status3.ExitCode()).To(Equal(0)) Expect(strings.Contains(status3.OutputToString(), "Degraded")).To(BeTrue()) }) + + It("podman create pod invalid network config", func() { + net1 := "n1" + stringid.GenerateNonCryptoID() + session := podmanTest.Podman([]string{"network", "create", net1}) + session.WaitWithDefaultTimeout() + defer podmanTest.removeCNINetwork(net1) + Expect(session.ExitCode()).To(BeZero()) + + session = podmanTest.Podman([]string{"pod", "create", "--network", "host", "--network", net1}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(125)) + Expect(session.ErrorToString()).To(ContainSubstring("host")) + Expect(session.ErrorToString()).To(ContainSubstring("bridge")) + + session = podmanTest.Podman([]string{"pod", "create", "--network", "container:abc"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(125)) + Expect(session.ErrorToString()).To(ContainSubstring("pods presently do not support network mode container")) + }) }) diff --git a/test/e2e/ps_test.go b/test/e2e/ps_test.go index fd08d4308..05571157c 100644 --- a/test/e2e/ps_test.go +++ b/test/e2e/ps_test.go @@ -44,6 +44,12 @@ var _ = Describe("Podman ps", func() { Expect(session.ExitCode()).To(Equal(0)) }) + It("podman container ps no containers", func() { + session := podmanTest.Podman([]string{"container", "ps"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + }) + It("podman ps default", func() { session := podmanTest.RunTopContainer("") session.WaitWithDefaultTimeout() diff --git a/test/e2e/run_networking_test.go b/test/e2e/run_networking_test.go index 3e80e953e..1d416498c 100644 --- a/test/e2e/run_networking_test.go +++ b/test/e2e/run_networking_test.go @@ -665,4 +665,33 @@ var _ = Describe("Podman run networking", func() { session.WaitWithDefaultTimeout() Expect(session.ExitCode()).To(BeZero()) }) + + It("podman run with multiple networks", func() { + net1 := "n1" + stringid.GenerateNonCryptoID() + session := podmanTest.Podman([]string{"network", "create", net1}) + session.WaitWithDefaultTimeout() + defer podmanTest.removeCNINetwork(net1) + Expect(session.ExitCode()).To(BeZero()) + + net2 := "n2" + stringid.GenerateNonCryptoID() + session = podmanTest.Podman([]string{"network", "create", net2}) + session.WaitWithDefaultTimeout() + defer podmanTest.removeCNINetwork(net2) + Expect(session.ExitCode()).To(BeZero()) + + run := podmanTest.Podman([]string{"run", "--network", net1, "--network", net2, ALPINE, "ip", "-o", "-4", "addr"}) + run.WaitWithDefaultTimeout() + Expect(run.ExitCode()).To(BeZero()) + Expect(len(run.OutputToStringArray())).To(Equal(3)) + Expect(run.OutputToString()).To(ContainSubstring("lo")) + Expect(run.OutputToString()).To(ContainSubstring("eth0")) + Expect(run.OutputToString()).To(ContainSubstring("eth1")) + + //invalid config network host and cni should fail + run = podmanTest.Podman([]string{"run", "--network", "host", "--network", net2, ALPINE, "ip", "-o", "-4", "addr"}) + run.WaitWithDefaultTimeout() + Expect(run.ExitCode()).To(Equal(125)) + Expect(run.ErrorToString()).To(ContainSubstring("host")) + Expect(run.ErrorToString()).To(ContainSubstring("bridge")) + }) }) diff --git a/test/e2e/run_test.go b/test/e2e/run_test.go index 5ee85efb9..0d65a3e59 100644 --- a/test/e2e/run_test.go +++ b/test/e2e/run_test.go @@ -75,11 +75,9 @@ var _ = Describe("Podman run", func() { session.WaitWithDefaultTimeout() Expect(session.ExitCode()).To(Equal(0)) - // the --rm option conflicts with --restart, when the restartPolicy is not "" and "no" - // so the exitCode should not equal 0 session = podmanTest.Podman([]string{"run", "--rm", "--restart", "on-failure", ALPINE}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Not(Equal(0))) + Expect(session.ExitCode()).To(Equal(0)) session = podmanTest.Podman([]string{"run", "--rm", "--restart", "always", ALPINE}) session.WaitWithDefaultTimeout() diff --git a/test/python/docker/__init__.py b/test/python/docker/__init__.py index 316b102f4..351834316 100644 --- a/test/python/docker/__init__.py +++ b/test/python/docker/__init__.py @@ -39,9 +39,7 @@ class Podman(object): self.cmd.append("--root=" + os.path.join(self.anchor_directory, "crio")) self.cmd.append("--runroot=" + os.path.join(self.anchor_directory, "crio-run")) - os.environ["REGISTRIES_CONFIG_PATH"] = os.path.join( - self.anchor_directory, "registry.conf" - ) + os.environ["REGISTRIES_CONFIG_PATH"] = os.path.join(self.anchor_directory, "registry.conf") p = configparser.ConfigParser() p.read_dict( { @@ -53,20 +51,16 @@ class Podman(object): with open(os.environ["REGISTRIES_CONFIG_PATH"], "w") as w: p.write(w) - os.environ["CNI_CONFIG_PATH"] = os.path.join( - self.anchor_directory, "cni", "net.d" - ) + os.environ["CNI_CONFIG_PATH"] = os.path.join(self.anchor_directory, "cni", "net.d") os.makedirs(os.environ["CNI_CONFIG_PATH"], exist_ok=True) self.cmd.append("--cni-config-dir=" + os.environ["CNI_CONFIG_PATH"]) - cni_cfg = os.path.join( - os.environ["CNI_CONFIG_PATH"], "87-podman-bridge.conflist" - ) + cni_cfg = os.path.join(os.environ["CNI_CONFIG_PATH"], "87-podman-bridge.conflist") # json decoded and encoded to ensure legal json buf = json.loads( """ { "cniVersion": "0.3.0", - "name": "podman", + "name": "default", "plugins": [{ "type": "bridge", "bridge": "cni0", diff --git a/test/python/docker/common.py b/test/python/docker/common.py index e79d64a9b..11f512495 100644 --- a/test/python/docker/common.py +++ b/test/python/docker/common.py @@ -4,9 +4,7 @@ from test.python.docker import constant def run_top_container(client: DockerClient): - c = client.containers.create( - constant.ALPINE, command="top", detach=True, tty=True, name="top" - ) + c = client.containers.create(constant.ALPINE, command="top", detach=True, tty=True, name="top") c.start() return c.id diff --git a/test/python/docker/test_containers.py b/test/python/docker/test_containers.py index 0fd419d9d..20d8417c3 100644 --- a/test/python/docker/test_containers.py +++ b/test/python/docker/test_containers.py @@ -87,7 +87,7 @@ class TestContainers(unittest.TestCase): self.assertEqual(len(containers), 2) def test_stop_container(self): - top = self.client.containers.get("top") + top = self.client.containers.get(TestContainers.topContainerId) self.assertEqual(top.status, "running") # Stop a running container and validate the state diff --git a/test/python/docker/test_images.py b/test/python/docker/test_images.py index 7ef3d708b..1fa4aade9 100644 --- a/test/python/docker/test_images.py +++ b/test/python/docker/test_images.py @@ -78,9 +78,7 @@ class TestImages(unittest.TestCase): self.assertEqual(len(self.client.images.list()), 2) # List images with filter - self.assertEqual( - len(self.client.images.list(filters={"reference": "alpine"})), 1 - ) + self.assertEqual(len(self.client.images.list(filters={"reference": "alpine"})), 1) def test_search_image(self): """Search for image""" diff --git a/vendor/github.com/containers/common/pkg/config/config.go b/vendor/github.com/containers/common/pkg/config/config.go index c6a9a660e..2769781f2 100644 --- a/vendor/github.com/containers/common/pkg/config/config.go +++ b/vendor/github.com/containers/common/pkg/config/config.go @@ -113,6 +113,10 @@ type ContainersConfig struct { // DNSSearches set default DNS search domains. DNSSearches []string `toml:"dns_searches,omitempty"` + // EnableKeyring tells the container engines whether to create + // a kernel keyring for use within the container + EnableKeyring bool `toml:"keyring,omitempty"` + // EnableLabeling tells the container engines whether to use MAC // Labeling to separate containers (SELinux) EnableLabeling bool `toml:"label,omitempty"` diff --git a/vendor/github.com/containers/common/pkg/config/containers.conf b/vendor/github.com/containers/common/pkg/config/containers.conf index e8519b251..ed7c91931 100644 --- a/vendor/github.com/containers/common/pkg/config/containers.conf +++ b/vendor/github.com/containers/common/pkg/config/containers.conf @@ -146,9 +146,13 @@ default_sysctls = [ # # ipcns = "private" -# Flag tells container engine to whether to use container separation using -# MAC(SELinux)labeling or not. -# Flag is ignored on label disabled systems. +# keyring tells the container engine whether to create +# a kernel keyring for use within the container. +# keyring = true + +# label tells the container engine whether to use container separation using +# MAC(SELinux) labeling or not. +# The label flag is ignored on label disabled systems. # # label = true diff --git a/vendor/github.com/containers/common/pkg/config/default.go b/vendor/github.com/containers/common/pkg/config/default.go index 5f8f4999f..4f1460e3b 100644 --- a/vendor/github.com/containers/common/pkg/config/default.go +++ b/vendor/github.com/containers/common/pkg/config/default.go @@ -46,8 +46,6 @@ var ( DefaultInitPath = "/usr/libexec/podman/catatonit" // DefaultInfraImage to use for infra container DefaultInfraImage = "k8s.gcr.io/pause:3.2" - // DefaultInfraCommand to be run in an infra container - DefaultInfraCommand = "/pause" // DefaultRootlessSHMLockPath is the default path for rootless SHM locks DefaultRootlessSHMLockPath = "/libpod_rootless_lock" // DefaultDetachKeys is the default keys sequence for detaching a @@ -179,6 +177,7 @@ func DefaultConfig() (*Config, error) { DNSServers: []string{}, DNSOptions: []string{}, DNSSearches: []string{}, + EnableKeyring: true, EnableLabeling: selinuxEnabled(), Env: []string{ "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin", @@ -308,7 +307,6 @@ func defaultConfigFromMemory() (*EngineConfig, error) { c.InitPath = DefaultInitPath c.NoPivotRoot = false - c.InfraCommand = DefaultInfraCommand c.InfraImage = DefaultInfraImage c.EnablePortReservation = true c.NumLocks = 2048 diff --git a/vendor/github.com/containers/common/pkg/retry/retry.go b/vendor/github.com/containers/common/pkg/retry/retry.go index d0ac19fb6..f6ecab0c0 100644 --- a/vendor/github.com/containers/common/pkg/retry/retry.go +++ b/vendor/github.com/containers/common/pkg/retry/retry.go @@ -30,7 +30,7 @@ func RetryIfNecessary(ctx context.Context, operation func() error, retryOptions if retryOptions.Delay != 0 { delay = retryOptions.Delay } - logrus.Infof("Warning: failed, retrying in %s ... (%d/%d)", delay, attempt+1, retryOptions.MaxRetry) + logrus.Infof("Warning: failed, retrying in %s ... (%d/%d). Error: %v", delay, attempt+1, retryOptions.MaxRetry, err) select { case <-time.After(delay): break diff --git a/vendor/github.com/containers/common/pkg/seccomp/default_linux.go b/vendor/github.com/containers/common/pkg/seccomp/default_linux.go index ddc25ac67..09629724d 100644 --- a/vendor/github.com/containers/common/pkg/seccomp/default_linux.go +++ b/vendor/github.com/containers/common/pkg/seccomp/default_linux.go @@ -174,6 +174,7 @@ func DefaultProfile() *Seccomp { "ioprio_get", "ioprio_set", "ipc", + "keyctl", "kill", "lchown", "lchown32", @@ -327,6 +328,7 @@ func DefaultProfile() *Seccomp { "signalfd", "signalfd4", "sigreturn", + "socket", "socketcall", "socketpair", "splice", diff --git a/vendor/github.com/containers/common/pkg/seccomp/supported.go b/vendor/github.com/containers/common/pkg/seccomp/supported.go index ab2a94a73..1177ef630 100644 --- a/vendor/github.com/containers/common/pkg/seccomp/supported.go +++ b/vendor/github.com/containers/common/pkg/seccomp/supported.go @@ -1,3 +1,5 @@ +// +build !windows + package seccomp import ( diff --git a/vendor/github.com/containers/common/version/version.go b/vendor/github.com/containers/common/version/version.go index ef7c612e2..72f4e00f7 100644 --- a/vendor/github.com/containers/common/version/version.go +++ b/vendor/github.com/containers/common/version/version.go @@ -1,4 +1,4 @@ package version // Version is the version of the build. -const Version = "0.27.0" +const Version = "0.29.0" diff --git a/vendor/github.com/containers/image/v5/pkg/sysregistriesv2/shortnames.go b/vendor/github.com/containers/image/v5/pkg/sysregistriesv2/shortnames.go index fadfe1a35..4001b65b6 100644 --- a/vendor/github.com/containers/image/v5/pkg/sysregistriesv2/shortnames.go +++ b/vendor/github.com/containers/image/v5/pkg/sysregistriesv2/shortnames.go @@ -8,8 +8,8 @@ import ( "github.com/BurntSushi/toml" "github.com/containers/image/v5/docker/reference" "github.com/containers/image/v5/types" + "github.com/containers/storage/pkg/homedir" "github.com/containers/storage/pkg/lockfile" - "github.com/docker/docker/pkg/homedir" "github.com/pkg/errors" ) diff --git a/vendor/github.com/containers/image/v5/version/version.go b/vendor/github.com/containers/image/v5/version/version.go index 3ef1c2410..14e553c9f 100644 --- a/vendor/github.com/containers/image/v5/version/version.go +++ b/vendor/github.com/containers/image/v5/version/version.go @@ -8,7 +8,7 @@ const ( // VersionMinor is for functionality in a backwards-compatible manner VersionMinor = 8 // VersionPatch is for backwards-compatible bug fixes - VersionPatch = 0 + VersionPatch = 1 // VersionDev indicates development branch. Releases will be empty string. VersionDev = "" diff --git a/vendor/github.com/containers/storage/VERSION b/vendor/github.com/containers/storage/VERSION index 53cc1a6f9..f9e8384bb 100644 --- a/vendor/github.com/containers/storage/VERSION +++ b/vendor/github.com/containers/storage/VERSION @@ -1 +1 @@ -1.24.0 +1.24.1 diff --git a/vendor/github.com/containers/storage/go.mod b/vendor/github.com/containers/storage/go.mod index 34c1ea7ad..86a5d8644 100644 --- a/vendor/github.com/containers/storage/go.mod +++ b/vendor/github.com/containers/storage/go.mod @@ -8,7 +8,7 @@ require ( github.com/Microsoft/hcsshim v0.8.9 github.com/docker/go-units v0.4.0 github.com/hashicorp/go-multierror v1.1.0 - github.com/klauspost/compress v1.11.2 + github.com/klauspost/compress v1.11.3 github.com/klauspost/pgzip v1.2.5 github.com/mattn/go-shellwords v1.0.10 github.com/mistifyio/go-zfs v2.1.1+incompatible diff --git a/vendor/github.com/containers/storage/go.sum b/vendor/github.com/containers/storage/go.sum index bec6aa59a..a5d3f3b82 100644 --- a/vendor/github.com/containers/storage/go.sum +++ b/vendor/github.com/containers/storage/go.sum @@ -64,8 +64,8 @@ github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= -github.com/klauspost/compress v1.11.2 h1:MiK62aErc3gIiVEtyzKfeOHgW7atJb5g/KNX5m3c2nQ= -github.com/klauspost/compress v1.11.2/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= +github.com/klauspost/compress v1.11.3 h1:dB4Bn0tN3wdCzQxnS8r06kV74qN/TAfaIS0bVE8h3jc= +github.com/klauspost/compress v1.11.3/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= github.com/klauspost/pgzip v1.2.5 h1:qnWYvvKqedOF2ulHpMG72XQol4ILEJ8k2wwRl/Km8oE= github.com/klauspost/pgzip v1.2.5/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= diff --git a/vendor/github.com/containers/storage/pkg/unshare/unshare.go b/vendor/github.com/containers/storage/pkg/unshare/unshare.go index a08fb674d..a9210b0bf 100644 --- a/vendor/github.com/containers/storage/pkg/unshare/unshare.go +++ b/vendor/github.com/containers/storage/pkg/unshare/unshare.go @@ -26,6 +26,7 @@ func HomeDir() (string, error) { return } homeDir, homeDirErr = usr.HomeDir, nil + return } homeDir, homeDirErr = home, nil }) diff --git a/vendor/github.com/klauspost/compress/flate/gen_inflate.go b/vendor/github.com/klauspost/compress/flate/gen_inflate.go index b26d19ec2..35fc072a3 100644 --- a/vendor/github.com/klauspost/compress/flate/gen_inflate.go +++ b/vendor/github.com/klauspost/compress/flate/gen_inflate.go @@ -42,16 +42,6 @@ func (f *decompressor) $FUNCNAME$() { stateDict ) fr := f.r.($TYPE$) - moreBits := func() error { - c, err := fr.ReadByte() - if err != nil { - return noEOF(err) - } - f.roffset++ - f.b |= uint32(c) << f.nb - f.nb += 8 - return nil - } switch f.stepState { case stateInit: @@ -112,9 +102,7 @@ readLiteral: } } - var n uint // number of bits extra var length int - var err error switch { case v < 256: f.dict.writeByte(byte(v)) @@ -131,71 +119,97 @@ readLiteral: // otherwise, reference to older data case v < 265: length = v - (257 - 3) - n = 0 - case v < 269: - length = v*2 - (265*2 - 11) - n = 1 - case v < 273: - length = v*4 - (269*4 - 19) - n = 2 - case v < 277: - length = v*8 - (273*8 - 35) - n = 3 - case v < 281: - length = v*16 - (277*16 - 67) - n = 4 - case v < 285: - length = v*32 - (281*32 - 131) - n = 5 case v < maxNumLit: - length = 258 - n = 0 - default: - if debugDecode { - fmt.Println(v, ">= maxNumLit") - } - f.err = CorruptInputError(f.roffset) - return - } - if n > 0 { + val := decCodeToLen[(v - 257)] + length = int(val.length) + 3 + n := uint(val.extra) for f.nb < n { - if err = moreBits(); err != nil { + c, err := fr.ReadByte() + if err != nil { if debugDecode { fmt.Println("morebits n>0:", err) } f.err = err return } + f.roffset++ + f.b |= uint32(c) << f.nb + f.nb += 8 } length += int(f.b & uint32(1<<(n®SizeMaskUint32)-1)) f.b >>= n & regSizeMaskUint32 f.nb -= n + default: + if debugDecode { + fmt.Println(v, ">= maxNumLit") + } + f.err = CorruptInputError(f.roffset) + return } var dist uint32 if f.hd == nil { for f.nb < 5 { - if err = f.moreBits(); err != nil { + c, err := fr.ReadByte() + if err != nil { if debugDecode { fmt.Println("morebits f.nb<5:", err) } f.err = err return } + f.roffset++ + f.b |= uint32(c) << f.nb + f.nb += 8 } dist = uint32(bits.Reverse8(uint8(f.b & 0x1F << 3))) f.b >>= 5 f.nb -= 5 } else { - sym, err := f.huffSym(f.hd) - if err != nil { - if debugDecode { - fmt.Println("huffsym:", err) + // Since a huffmanDecoder can be empty or be composed of a degenerate tree + // with single element, huffSym must error on these two edge cases. In both + // cases, the chunks slice will be 0 for the invalid sequence, leading it + // satisfy the n == 0 check below. + n := uint(f.hd.maxRead) + // Optimization. Compiler isn't smart enough to keep f.b,f.nb in registers, + // but is smart enough to keep local variables in registers, so use nb and b, + // inline call to moreBits and reassign b,nb back to f on return. + nb, b := f.nb, f.b + for { + for nb < n { + c, err := fr.ReadByte() + if err != nil { + f.b = b + f.nb = nb + f.err = noEOF(err) + return + } + f.roffset++ + b |= uint32(c) << (nb & regSizeMaskUint32) + nb += 8 + } + chunk := f.hd.chunks[b&(huffmanNumChunks-1)] + n = uint(chunk & huffmanCountMask) + if n > huffmanChunkBits { + chunk = f.hd.links[chunk>>huffmanValueShift][(b>>huffmanChunkBits)&f.hd.linkMask] + n = uint(chunk & huffmanCountMask) + } + if n <= nb { + if n == 0 { + f.b = b + f.nb = nb + if debugDecode { + fmt.Println("huffsym: n==0") + } + f.err = CorruptInputError(f.roffset) + return + } + f.b = b >> (n & regSizeMaskUint32) + f.nb = nb - n + dist = uint32(chunk >> huffmanValueShift) + break } - f.err = err - return } - dist = uint32(sym) } switch { @@ -206,13 +220,17 @@ readLiteral: // have 1 bit in bottom of dist, need nb more. extra := (dist & 1) << (nb & regSizeMaskUint32) for f.nb < nb { - if err = f.moreBits(); err != nil { + c, err := fr.ReadByte() + if err != nil { if debugDecode { fmt.Println("morebits f.nb>= nb & regSizeMaskUint32 diff --git a/vendor/github.com/klauspost/compress/flate/inflate.go b/vendor/github.com/klauspost/compress/flate/inflate.go index 189e9fe0b..16bc51408 100644 --- a/vendor/github.com/klauspost/compress/flate/inflate.go +++ b/vendor/github.com/klauspost/compress/flate/inflate.go @@ -29,6 +29,13 @@ const ( debugDecode = false ) +// Value of length - 3 and extra bits. +type lengthExtra struct { + length, extra uint8 +} + +var decCodeToLen = [32]lengthExtra{{length: 0x0, extra: 0x0}, {length: 0x1, extra: 0x0}, {length: 0x2, extra: 0x0}, {length: 0x3, extra: 0x0}, {length: 0x4, extra: 0x0}, {length: 0x5, extra: 0x0}, {length: 0x6, extra: 0x0}, {length: 0x7, extra: 0x0}, {length: 0x8, extra: 0x1}, {length: 0xa, extra: 0x1}, {length: 0xc, extra: 0x1}, {length: 0xe, extra: 0x1}, {length: 0x10, extra: 0x2}, {length: 0x14, extra: 0x2}, {length: 0x18, extra: 0x2}, {length: 0x1c, extra: 0x2}, {length: 0x20, extra: 0x3}, {length: 0x28, extra: 0x3}, {length: 0x30, extra: 0x3}, {length: 0x38, extra: 0x3}, {length: 0x40, extra: 0x4}, {length: 0x50, extra: 0x4}, {length: 0x60, extra: 0x4}, {length: 0x70, extra: 0x4}, {length: 0x80, extra: 0x5}, {length: 0xa0, extra: 0x5}, {length: 0xc0, extra: 0x5}, {length: 0xe0, extra: 0x5}, {length: 0xff, extra: 0x0}, {length: 0x0, extra: 0x0}, {length: 0x0, extra: 0x0}, {length: 0x0, extra: 0x0}} + // Initialize the fixedHuffmanDecoder only once upon first use. var fixedOnce sync.Once var fixedHuffmanDecoder huffmanDecoder diff --git a/vendor/github.com/klauspost/compress/flate/inflate_gen.go b/vendor/github.com/klauspost/compress/flate/inflate_gen.go index 9a92a1b30..cc6db2792 100644 --- a/vendor/github.com/klauspost/compress/flate/inflate_gen.go +++ b/vendor/github.com/klauspost/compress/flate/inflate_gen.go @@ -20,16 +20,6 @@ func (f *decompressor) huffmanBytesBuffer() { stateDict ) fr := f.r.(*bytes.Buffer) - moreBits := func() error { - c, err := fr.ReadByte() - if err != nil { - return noEOF(err) - } - f.roffset++ - f.b |= uint32(c) << f.nb - f.nb += 8 - return nil - } switch f.stepState { case stateInit: @@ -90,9 +80,7 @@ readLiteral: } } - var n uint // number of bits extra var length int - var err error switch { case v < 256: f.dict.writeByte(byte(v)) @@ -109,71 +97,97 @@ readLiteral: // otherwise, reference to older data case v < 265: length = v - (257 - 3) - n = 0 - case v < 269: - length = v*2 - (265*2 - 11) - n = 1 - case v < 273: - length = v*4 - (269*4 - 19) - n = 2 - case v < 277: - length = v*8 - (273*8 - 35) - n = 3 - case v < 281: - length = v*16 - (277*16 - 67) - n = 4 - case v < 285: - length = v*32 - (281*32 - 131) - n = 5 case v < maxNumLit: - length = 258 - n = 0 - default: - if debugDecode { - fmt.Println(v, ">= maxNumLit") - } - f.err = CorruptInputError(f.roffset) - return - } - if n > 0 { + val := decCodeToLen[(v - 257)] + length = int(val.length) + 3 + n := uint(val.extra) for f.nb < n { - if err = moreBits(); err != nil { + c, err := fr.ReadByte() + if err != nil { if debugDecode { fmt.Println("morebits n>0:", err) } f.err = err return } + f.roffset++ + f.b |= uint32(c) << f.nb + f.nb += 8 } length += int(f.b & uint32(1<<(n®SizeMaskUint32)-1)) f.b >>= n & regSizeMaskUint32 f.nb -= n + default: + if debugDecode { + fmt.Println(v, ">= maxNumLit") + } + f.err = CorruptInputError(f.roffset) + return } var dist uint32 if f.hd == nil { for f.nb < 5 { - if err = f.moreBits(); err != nil { + c, err := fr.ReadByte() + if err != nil { if debugDecode { fmt.Println("morebits f.nb<5:", err) } f.err = err return } + f.roffset++ + f.b |= uint32(c) << f.nb + f.nb += 8 } dist = uint32(bits.Reverse8(uint8(f.b & 0x1F << 3))) f.b >>= 5 f.nb -= 5 } else { - sym, err := f.huffSym(f.hd) - if err != nil { - if debugDecode { - fmt.Println("huffsym:", err) + // Since a huffmanDecoder can be empty or be composed of a degenerate tree + // with single element, huffSym must error on these two edge cases. In both + // cases, the chunks slice will be 0 for the invalid sequence, leading it + // satisfy the n == 0 check below. + n := uint(f.hd.maxRead) + // Optimization. Compiler isn't smart enough to keep f.b,f.nb in registers, + // but is smart enough to keep local variables in registers, so use nb and b, + // inline call to moreBits and reassign b,nb back to f on return. + nb, b := f.nb, f.b + for { + for nb < n { + c, err := fr.ReadByte() + if err != nil { + f.b = b + f.nb = nb + f.err = noEOF(err) + return + } + f.roffset++ + b |= uint32(c) << (nb & regSizeMaskUint32) + nb += 8 + } + chunk := f.hd.chunks[b&(huffmanNumChunks-1)] + n = uint(chunk & huffmanCountMask) + if n > huffmanChunkBits { + chunk = f.hd.links[chunk>>huffmanValueShift][(b>>huffmanChunkBits)&f.hd.linkMask] + n = uint(chunk & huffmanCountMask) + } + if n <= nb { + if n == 0 { + f.b = b + f.nb = nb + if debugDecode { + fmt.Println("huffsym: n==0") + } + f.err = CorruptInputError(f.roffset) + return + } + f.b = b >> (n & regSizeMaskUint32) + f.nb = nb - n + dist = uint32(chunk >> huffmanValueShift) + break } - f.err = err - return } - dist = uint32(sym) } switch { @@ -184,13 +198,17 @@ readLiteral: // have 1 bit in bottom of dist, need nb more. extra := (dist & 1) << (nb & regSizeMaskUint32) for f.nb < nb { - if err = f.moreBits(); err != nil { + c, err := fr.ReadByte() + if err != nil { if debugDecode { fmt.Println("morebits f.nb>= nb & regSizeMaskUint32 @@ -246,16 +264,6 @@ func (f *decompressor) huffmanBytesReader() { stateDict ) fr := f.r.(*bytes.Reader) - moreBits := func() error { - c, err := fr.ReadByte() - if err != nil { - return noEOF(err) - } - f.roffset++ - f.b |= uint32(c) << f.nb - f.nb += 8 - return nil - } switch f.stepState { case stateInit: @@ -316,9 +324,7 @@ readLiteral: } } - var n uint // number of bits extra var length int - var err error switch { case v < 256: f.dict.writeByte(byte(v)) @@ -335,71 +341,97 @@ readLiteral: // otherwise, reference to older data case v < 265: length = v - (257 - 3) - n = 0 - case v < 269: - length = v*2 - (265*2 - 11) - n = 1 - case v < 273: - length = v*4 - (269*4 - 19) - n = 2 - case v < 277: - length = v*8 - (273*8 - 35) - n = 3 - case v < 281: - length = v*16 - (277*16 - 67) - n = 4 - case v < 285: - length = v*32 - (281*32 - 131) - n = 5 case v < maxNumLit: - length = 258 - n = 0 - default: - if debugDecode { - fmt.Println(v, ">= maxNumLit") - } - f.err = CorruptInputError(f.roffset) - return - } - if n > 0 { + val := decCodeToLen[(v - 257)] + length = int(val.length) + 3 + n := uint(val.extra) for f.nb < n { - if err = moreBits(); err != nil { + c, err := fr.ReadByte() + if err != nil { if debugDecode { fmt.Println("morebits n>0:", err) } f.err = err return } + f.roffset++ + f.b |= uint32(c) << f.nb + f.nb += 8 } length += int(f.b & uint32(1<<(n®SizeMaskUint32)-1)) f.b >>= n & regSizeMaskUint32 f.nb -= n + default: + if debugDecode { + fmt.Println(v, ">= maxNumLit") + } + f.err = CorruptInputError(f.roffset) + return } var dist uint32 if f.hd == nil { for f.nb < 5 { - if err = f.moreBits(); err != nil { + c, err := fr.ReadByte() + if err != nil { if debugDecode { fmt.Println("morebits f.nb<5:", err) } f.err = err return } + f.roffset++ + f.b |= uint32(c) << f.nb + f.nb += 8 } dist = uint32(bits.Reverse8(uint8(f.b & 0x1F << 3))) f.b >>= 5 f.nb -= 5 } else { - sym, err := f.huffSym(f.hd) - if err != nil { - if debugDecode { - fmt.Println("huffsym:", err) + // Since a huffmanDecoder can be empty or be composed of a degenerate tree + // with single element, huffSym must error on these two edge cases. In both + // cases, the chunks slice will be 0 for the invalid sequence, leading it + // satisfy the n == 0 check below. + n := uint(f.hd.maxRead) + // Optimization. Compiler isn't smart enough to keep f.b,f.nb in registers, + // but is smart enough to keep local variables in registers, so use nb and b, + // inline call to moreBits and reassign b,nb back to f on return. + nb, b := f.nb, f.b + for { + for nb < n { + c, err := fr.ReadByte() + if err != nil { + f.b = b + f.nb = nb + f.err = noEOF(err) + return + } + f.roffset++ + b |= uint32(c) << (nb & regSizeMaskUint32) + nb += 8 + } + chunk := f.hd.chunks[b&(huffmanNumChunks-1)] + n = uint(chunk & huffmanCountMask) + if n > huffmanChunkBits { + chunk = f.hd.links[chunk>>huffmanValueShift][(b>>huffmanChunkBits)&f.hd.linkMask] + n = uint(chunk & huffmanCountMask) + } + if n <= nb { + if n == 0 { + f.b = b + f.nb = nb + if debugDecode { + fmt.Println("huffsym: n==0") + } + f.err = CorruptInputError(f.roffset) + return + } + f.b = b >> (n & regSizeMaskUint32) + f.nb = nb - n + dist = uint32(chunk >> huffmanValueShift) + break } - f.err = err - return } - dist = uint32(sym) } switch { @@ -410,13 +442,17 @@ readLiteral: // have 1 bit in bottom of dist, need nb more. extra := (dist & 1) << (nb & regSizeMaskUint32) for f.nb < nb { - if err = f.moreBits(); err != nil { + c, err := fr.ReadByte() + if err != nil { if debugDecode { fmt.Println("morebits f.nb>= nb & regSizeMaskUint32 @@ -472,16 +508,6 @@ func (f *decompressor) huffmanBufioReader() { stateDict ) fr := f.r.(*bufio.Reader) - moreBits := func() error { - c, err := fr.ReadByte() - if err != nil { - return noEOF(err) - } - f.roffset++ - f.b |= uint32(c) << f.nb - f.nb += 8 - return nil - } switch f.stepState { case stateInit: @@ -542,9 +568,7 @@ readLiteral: } } - var n uint // number of bits extra var length int - var err error switch { case v < 256: f.dict.writeByte(byte(v)) @@ -561,71 +585,97 @@ readLiteral: // otherwise, reference to older data case v < 265: length = v - (257 - 3) - n = 0 - case v < 269: - length = v*2 - (265*2 - 11) - n = 1 - case v < 273: - length = v*4 - (269*4 - 19) - n = 2 - case v < 277: - length = v*8 - (273*8 - 35) - n = 3 - case v < 281: - length = v*16 - (277*16 - 67) - n = 4 - case v < 285: - length = v*32 - (281*32 - 131) - n = 5 case v < maxNumLit: - length = 258 - n = 0 - default: - if debugDecode { - fmt.Println(v, ">= maxNumLit") - } - f.err = CorruptInputError(f.roffset) - return - } - if n > 0 { + val := decCodeToLen[(v - 257)] + length = int(val.length) + 3 + n := uint(val.extra) for f.nb < n { - if err = moreBits(); err != nil { + c, err := fr.ReadByte() + if err != nil { if debugDecode { fmt.Println("morebits n>0:", err) } f.err = err return } + f.roffset++ + f.b |= uint32(c) << f.nb + f.nb += 8 } length += int(f.b & uint32(1<<(n®SizeMaskUint32)-1)) f.b >>= n & regSizeMaskUint32 f.nb -= n + default: + if debugDecode { + fmt.Println(v, ">= maxNumLit") + } + f.err = CorruptInputError(f.roffset) + return } var dist uint32 if f.hd == nil { for f.nb < 5 { - if err = f.moreBits(); err != nil { + c, err := fr.ReadByte() + if err != nil { if debugDecode { fmt.Println("morebits f.nb<5:", err) } f.err = err return } + f.roffset++ + f.b |= uint32(c) << f.nb + f.nb += 8 } dist = uint32(bits.Reverse8(uint8(f.b & 0x1F << 3))) f.b >>= 5 f.nb -= 5 } else { - sym, err := f.huffSym(f.hd) - if err != nil { - if debugDecode { - fmt.Println("huffsym:", err) + // Since a huffmanDecoder can be empty or be composed of a degenerate tree + // with single element, huffSym must error on these two edge cases. In both + // cases, the chunks slice will be 0 for the invalid sequence, leading it + // satisfy the n == 0 check below. + n := uint(f.hd.maxRead) + // Optimization. Compiler isn't smart enough to keep f.b,f.nb in registers, + // but is smart enough to keep local variables in registers, so use nb and b, + // inline call to moreBits and reassign b,nb back to f on return. + nb, b := f.nb, f.b + for { + for nb < n { + c, err := fr.ReadByte() + if err != nil { + f.b = b + f.nb = nb + f.err = noEOF(err) + return + } + f.roffset++ + b |= uint32(c) << (nb & regSizeMaskUint32) + nb += 8 + } + chunk := f.hd.chunks[b&(huffmanNumChunks-1)] + n = uint(chunk & huffmanCountMask) + if n > huffmanChunkBits { + chunk = f.hd.links[chunk>>huffmanValueShift][(b>>huffmanChunkBits)&f.hd.linkMask] + n = uint(chunk & huffmanCountMask) + } + if n <= nb { + if n == 0 { + f.b = b + f.nb = nb + if debugDecode { + fmt.Println("huffsym: n==0") + } + f.err = CorruptInputError(f.roffset) + return + } + f.b = b >> (n & regSizeMaskUint32) + f.nb = nb - n + dist = uint32(chunk >> huffmanValueShift) + break } - f.err = err - return } - dist = uint32(sym) } switch { @@ -636,13 +686,17 @@ readLiteral: // have 1 bit in bottom of dist, need nb more. extra := (dist & 1) << (nb & regSizeMaskUint32) for f.nb < nb { - if err = f.moreBits(); err != nil { + c, err := fr.ReadByte() + if err != nil { if debugDecode { fmt.Println("morebits f.nb>= nb & regSizeMaskUint32 @@ -698,16 +752,6 @@ func (f *decompressor) huffmanStringsReader() { stateDict ) fr := f.r.(*strings.Reader) - moreBits := func() error { - c, err := fr.ReadByte() - if err != nil { - return noEOF(err) - } - f.roffset++ - f.b |= uint32(c) << f.nb - f.nb += 8 - return nil - } switch f.stepState { case stateInit: @@ -768,9 +812,7 @@ readLiteral: } } - var n uint // number of bits extra var length int - var err error switch { case v < 256: f.dict.writeByte(byte(v)) @@ -787,71 +829,97 @@ readLiteral: // otherwise, reference to older data case v < 265: length = v - (257 - 3) - n = 0 - case v < 269: - length = v*2 - (265*2 - 11) - n = 1 - case v < 273: - length = v*4 - (269*4 - 19) - n = 2 - case v < 277: - length = v*8 - (273*8 - 35) - n = 3 - case v < 281: - length = v*16 - (277*16 - 67) - n = 4 - case v < 285: - length = v*32 - (281*32 - 131) - n = 5 case v < maxNumLit: - length = 258 - n = 0 - default: - if debugDecode { - fmt.Println(v, ">= maxNumLit") - } - f.err = CorruptInputError(f.roffset) - return - } - if n > 0 { + val := decCodeToLen[(v - 257)] + length = int(val.length) + 3 + n := uint(val.extra) for f.nb < n { - if err = moreBits(); err != nil { + c, err := fr.ReadByte() + if err != nil { if debugDecode { fmt.Println("morebits n>0:", err) } f.err = err return } + f.roffset++ + f.b |= uint32(c) << f.nb + f.nb += 8 } length += int(f.b & uint32(1<<(n®SizeMaskUint32)-1)) f.b >>= n & regSizeMaskUint32 f.nb -= n + default: + if debugDecode { + fmt.Println(v, ">= maxNumLit") + } + f.err = CorruptInputError(f.roffset) + return } var dist uint32 if f.hd == nil { for f.nb < 5 { - if err = f.moreBits(); err != nil { + c, err := fr.ReadByte() + if err != nil { if debugDecode { fmt.Println("morebits f.nb<5:", err) } f.err = err return } + f.roffset++ + f.b |= uint32(c) << f.nb + f.nb += 8 } dist = uint32(bits.Reverse8(uint8(f.b & 0x1F << 3))) f.b >>= 5 f.nb -= 5 } else { - sym, err := f.huffSym(f.hd) - if err != nil { - if debugDecode { - fmt.Println("huffsym:", err) + // Since a huffmanDecoder can be empty or be composed of a degenerate tree + // with single element, huffSym must error on these two edge cases. In both + // cases, the chunks slice will be 0 for the invalid sequence, leading it + // satisfy the n == 0 check below. + n := uint(f.hd.maxRead) + // Optimization. Compiler isn't smart enough to keep f.b,f.nb in registers, + // but is smart enough to keep local variables in registers, so use nb and b, + // inline call to moreBits and reassign b,nb back to f on return. + nb, b := f.nb, f.b + for { + for nb < n { + c, err := fr.ReadByte() + if err != nil { + f.b = b + f.nb = nb + f.err = noEOF(err) + return + } + f.roffset++ + b |= uint32(c) << (nb & regSizeMaskUint32) + nb += 8 + } + chunk := f.hd.chunks[b&(huffmanNumChunks-1)] + n = uint(chunk & huffmanCountMask) + if n > huffmanChunkBits { + chunk = f.hd.links[chunk>>huffmanValueShift][(b>>huffmanChunkBits)&f.hd.linkMask] + n = uint(chunk & huffmanCountMask) + } + if n <= nb { + if n == 0 { + f.b = b + f.nb = nb + if debugDecode { + fmt.Println("huffsym: n==0") + } + f.err = CorruptInputError(f.roffset) + return + } + f.b = b >> (n & regSizeMaskUint32) + f.nb = nb - n + dist = uint32(chunk >> huffmanValueShift) + break } - f.err = err - return } - dist = uint32(sym) } switch { @@ -862,13 +930,17 @@ readLiteral: // have 1 bit in bottom of dist, need nb more. extra := (dist & 1) << (nb & regSizeMaskUint32) for f.nb < nb { - if err = f.moreBits(); err != nil { + c, err := fr.ReadByte() + if err != nil { if debugDecode { fmt.Println("morebits f.nb>= nb & regSizeMaskUint32 diff --git a/vendor/github.com/klauspost/compress/zstd/README.md b/vendor/github.com/klauspost/compress/zstd/README.md index 07f7285f0..08e553f75 100644 --- a/vendor/github.com/klauspost/compress/zstd/README.md +++ b/vendor/github.com/klauspost/compress/zstd/README.md @@ -54,11 +54,11 @@ To create a writer with default options, do like this: ```Go // Compress input to output. func Compress(in io.Reader, out io.Writer) error { - w, err := NewWriter(output) + enc, err := zstd.NewWriter(out) if err != nil { return err } - _, err := io.Copy(w, input) + _, err = io.Copy(enc, in) if err != nil { enc.Close() return err diff --git a/vendor/github.com/klauspost/compress/zstd/decoder.go b/vendor/github.com/klauspost/compress/zstd/decoder.go index d78be6d42..cdda0de58 100644 --- a/vendor/github.com/klauspost/compress/zstd/decoder.go +++ b/vendor/github.com/klauspost/compress/zstd/decoder.go @@ -323,19 +323,23 @@ func (d *Decoder) DecodeAll(input, dst []byte) ([]byte, error) { } if frame.FrameContentSize > 0 && frame.FrameContentSize < 1<<30 { // Never preallocate moe than 1 GB up front. - if uint64(cap(dst)) < frame.FrameContentSize { + if cap(dst)-len(dst) < int(frame.FrameContentSize) { dst2 := make([]byte, len(dst), len(dst)+int(frame.FrameContentSize)) copy(dst2, dst) dst = dst2 } } if cap(dst) == 0 { - // Allocate window size * 2 by default if nothing is provided and we didn't get frame content size. - size := frame.WindowSize * 2 + // Allocate len(input) * 2 by default if nothing is provided + // and we didn't get frame content size. + size := len(input) * 2 // Cap to 1 MB. if size > 1<<20 { size = 1 << 20 } + if uint64(size) > d.o.maxDecodedSize { + size = int(d.o.maxDecodedSize) + } dst = make([]byte, 0, size) } diff --git a/vendor/modules.txt b/vendor/modules.txt index 320d9851f..674b7a4e4 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -88,7 +88,7 @@ github.com/containers/buildah/pkg/secrets github.com/containers/buildah/pkg/supplemented github.com/containers/buildah/pkg/umask github.com/containers/buildah/util -# github.com/containers/common v0.27.0 +# github.com/containers/common v0.29.0 github.com/containers/common/pkg/apparmor github.com/containers/common/pkg/apparmor/internal/supported github.com/containers/common/pkg/auth @@ -104,7 +104,7 @@ github.com/containers/common/pkg/sysinfo github.com/containers/common/version # github.com/containers/conmon v2.0.20+incompatible github.com/containers/conmon/runner/config -# github.com/containers/image/v5 v5.8.0 +# github.com/containers/image/v5 v5.8.1 github.com/containers/image/v5/copy github.com/containers/image/v5/directory github.com/containers/image/v5/directory/explicitfilepath @@ -168,7 +168,7 @@ github.com/containers/psgo/internal/dev github.com/containers/psgo/internal/host github.com/containers/psgo/internal/proc github.com/containers/psgo/internal/process -# github.com/containers/storage v1.24.0 +# github.com/containers/storage v1.24.1 github.com/containers/storage github.com/containers/storage/drivers github.com/containers/storage/drivers/aufs @@ -339,7 +339,7 @@ github.com/json-iterator/go # github.com/juju/ansiterm v0.0.0-20180109212912-720a0952cc2a github.com/juju/ansiterm github.com/juju/ansiterm/tabwriter -# github.com/klauspost/compress v1.11.2 +# github.com/klauspost/compress v1.11.3 github.com/klauspost/compress/flate github.com/klauspost/compress/fse github.com/klauspost/compress/huff0 -- cgit v1.2.3-54-g00ecf