diff options
42 files changed, 494 insertions, 135 deletions
@@ -90,8 +90,7 @@ else ISODATE ?= $(shell date --iso-8601) endif LIBPOD := ${PROJECT}/v3/libpod -GCFLAGS ?= all=-trimpath=$(CURDIR) -ASMFLAGS ?= all=-trimpath=$(CURDIR) +GOFLAGS ?= -trimpath LDFLAGS_PODMAN ?= \ -X $(LIBPOD)/define.gitCommit=$(GIT_COMMIT) \ -X $(LIBPOD)/define.buildInfo=$(BUILD_INFO) \ @@ -258,7 +257,7 @@ test/goecho/goecho: .gopathok $(wildcard test/goecho/*.go) .PHONY: codespell codespell: - codespell -S bin,vendor,.git,go.sum,changelog.txt,.cirrus.yml,"RELEASE_NOTES.md,*.xz,*.gz,*.tar,*.tgz,bin2img,*ico,*.png,*.1,*.5,copyimg,*.orig,apidoc.go" -L uint,iff,od,seeked,splitted,marge,ERRO,hist,ether -w + codespell -S bin,vendor,.git,go.sum,changelog.txt,.cirrus.yml,"RELEASE_NOTES.md,*.xz,*.gz,*.ps1,*.tar,*.tgz,bin2img,*ico,*.png,*.1,*.5,copyimg,*.orig,apidoc.go" -L uint,iff,od,seeked,splitted,marge,ERRO,hist,ether -w .PHONY: validate validate: gofmt lint .gitvalidation validate.completions man-page-check swagger-check tests-included tests-expect-exit @@ -295,8 +294,6 @@ endif CGO_ENABLED=$(CGO_ENABLED) \ $(GO) build \ $(BUILDFLAGS) \ - -gcflags '$(GCFLAGS)' \ - -asmflags '$(ASMFLAGS)' \ -ldflags '$(LDFLAGS_PODMAN)' \ -tags "$(BUILDTAGS)" \ -o $@ ./cmd/podman @@ -310,8 +307,6 @@ $(SRCBINDIR)/podman$(BINSFX): $(SRCBINDIR) .gopathok $(SOURCES) go.mod go.sum GOOS=$(GOOS) \ $(GO) build \ $(BUILDFLAGS) \ - -gcflags '$(GCFLAGS)' \ - -asmflags '$(ASMFLAGS)' \ -ldflags '$(LDFLAGS_PODMAN)' \ -tags "${REMOTETAGS}" \ -o $@ ./cmd/podman @@ -321,8 +316,6 @@ $(SRCBINDIR)/podman-remote-static: $(SRCBINDIR) .gopathok $(SOURCES) go.mod go.s GOOS=$(GOOS) \ $(GO) build \ $(BUILDFLAGS) \ - -gcflags '$(GCFLAGS)' \ - -asmflags '$(ASMFLAGS)' \ -ldflags '$(LDFLAGS_PODMAN_STATIC)' \ -tags "${REMOTETAGS}" \ -o $@ ./cmd/podman @@ -376,8 +369,6 @@ bin/podman.cross.%: .gopathok CGO_ENABLED=0 \ $(GO) build \ $(BUILDFLAGS) \ - -gcflags '$(GCFLAGS)' \ - -asmflags '$(ASMFLAGS)' \ -ldflags '$(LDFLAGS_PODMAN)' \ -tags '$(BUILDTAGS_CROSS)' \ -o "$@" ./cmd/podman diff --git a/cmd/podman/common/completion.go b/cmd/podman/common/completion.go index 08b2f6235..9a4524b46 100644 --- a/cmd/podman/common/completion.go +++ b/cmd/podman/common/completion.go @@ -323,6 +323,18 @@ func prefixSlice(pre string, slice []string) []string { return slice } +func suffixCompSlice(suf string, slice []string) []string { + for i := range slice { + split := strings.SplitN(slice[i], "\t", 2) + if len(split) > 1 { + slice[i] = split[0] + suf + "\t" + split[1] + } else { + slice[i] = slice[i] + suf + } + } + return slice +} + func completeKeyValues(toComplete string, k keyValueCompletion) ([]string, cobra.ShellCompDirective) { suggestions := make([]string, 0, len(k)) directive := cobra.ShellCompDirectiveNoFileComp @@ -664,6 +676,42 @@ func AutocompleteSystemConnections(cmd *cobra.Command, args []string, toComplete return suggestions, cobra.ShellCompDirectiveNoFileComp } +// AutocompleteScp returns a list of connections, images, or both, depending on the amount of arguments +func AutocompleteScp(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { + if !validCurrentCmdLine(cmd, args, toComplete) { + return nil, cobra.ShellCompDirectiveNoFileComp + } + switch len(args) { + case 0: + split := strings.SplitN(toComplete, "::", 2) + if len(split) > 1 { + imageSuggestions, _ := getImages(cmd, split[1]) + return prefixSlice(split[0]+"::", imageSuggestions), cobra.ShellCompDirectiveNoFileComp + } + connectionSuggestions, _ := AutocompleteSystemConnections(cmd, args, toComplete) + imageSuggestions, _ := getImages(cmd, toComplete) + totalSuggestions := append(suffixCompSlice("::", connectionSuggestions), imageSuggestions...) + directive := cobra.ShellCompDirectiveNoFileComp + // if we have connections do not add a space after the completion + if len(connectionSuggestions) > 0 { + directive = cobra.ShellCompDirectiveNoFileComp | cobra.ShellCompDirectiveNoSpace + } + return totalSuggestions, directive + case 1: + split := strings.SplitN(args[0], "::", 2) + if len(split) > 1 { + if len(split[1]) > 0 { + return nil, cobra.ShellCompDirectiveNoFileComp + } + imageSuggestions, _ := getImages(cmd, toComplete) + return imageSuggestions, cobra.ShellCompDirectiveNoFileComp + } + connectionSuggestions, _ := AutocompleteSystemConnections(cmd, args, toComplete) + return suffixCompSlice("::", connectionSuggestions), cobra.ShellCompDirectiveNoFileComp + } + return nil, cobra.ShellCompDirectiveNoFileComp +} + /* -------------- Flags ----------------- */ // AutocompleteDetachKeys - Autocomplete detach-keys options. diff --git a/cmd/podman/common/create_opts.go b/cmd/podman/common/create_opts.go index 0a969bfd2..0fdf3ce08 100644 --- a/cmd/podman/common/create_opts.go +++ b/cmd/podman/common/create_opts.go @@ -356,51 +356,55 @@ func ContainerCreateToContainerCLIOpts(cc handlers.CreateContainerConfig, rtc *c CPUSetMems: cc.HostConfig.CpusetMems, // Detach: false, // don't need // DetachKeys: "", // don't need - Devices: devices, - DeviceCGroupRule: nil, - DeviceReadBPs: readBps, - DeviceReadIOPs: readIops, - DeviceWriteBPs: writeBps, - DeviceWriteIOPs: writeIops, - Entrypoint: entrypoint, - Env: cc.Config.Env, - Expose: expose, - GroupAdd: cc.HostConfig.GroupAdd, - Hostname: cc.Config.Hostname, - ImageVolume: "bind", - Init: init, - Interactive: cc.Config.OpenStdin, - IPC: string(cc.HostConfig.IpcMode), - Label: stringMaptoArray(cc.Config.Labels), - LogDriver: cc.HostConfig.LogConfig.Type, - LogOptions: stringMaptoArray(cc.HostConfig.LogConfig.Config), - Name: cc.Name, - OOMScoreAdj: cc.HostConfig.OomScoreAdj, - Arch: "", - OS: "", - Variant: "", - PID: string(cc.HostConfig.PidMode), - PIDsLimit: cc.HostConfig.PidsLimit, - Privileged: cc.HostConfig.Privileged, - PublishAll: cc.HostConfig.PublishAllPorts, - Quiet: false, - ReadOnly: cc.HostConfig.ReadonlyRootfs, - ReadOnlyTmpFS: true, // podman default - Rm: cc.HostConfig.AutoRemove, - SecurityOpt: cc.HostConfig.SecurityOpt, - StopSignal: cc.Config.StopSignal, - StorageOpt: stringMaptoArray(cc.HostConfig.StorageOpt), - Sysctl: stringMaptoArray(cc.HostConfig.Sysctls), - Systemd: "true", // podman default - TmpFS: parsedTmp, - TTY: cc.Config.Tty, - User: cc.Config.User, - UserNS: string(cc.HostConfig.UsernsMode), - UTS: string(cc.HostConfig.UTSMode), - Mount: mounts, - VolumesFrom: cc.HostConfig.VolumesFrom, - Workdir: cc.Config.WorkingDir, - Net: &netInfo, + Devices: devices, + DeviceCGroupRule: nil, + DeviceReadBPs: readBps, + DeviceReadIOPs: readIops, + DeviceWriteBPs: writeBps, + DeviceWriteIOPs: writeIops, + Entrypoint: entrypoint, + Env: cc.Config.Env, + Expose: expose, + GroupAdd: cc.HostConfig.GroupAdd, + Hostname: cc.Config.Hostname, + ImageVolume: "bind", + Init: init, + Interactive: cc.Config.OpenStdin, + IPC: string(cc.HostConfig.IpcMode), + Label: stringMaptoArray(cc.Config.Labels), + LogDriver: cc.HostConfig.LogConfig.Type, + LogOptions: stringMaptoArray(cc.HostConfig.LogConfig.Config), + Name: cc.Name, + OOMScoreAdj: cc.HostConfig.OomScoreAdj, + Arch: "", + OS: "", + Variant: "", + PID: string(cc.HostConfig.PidMode), + PIDsLimit: cc.HostConfig.PidsLimit, + Privileged: cc.HostConfig.Privileged, + PublishAll: cc.HostConfig.PublishAllPorts, + Quiet: false, + ReadOnly: cc.HostConfig.ReadonlyRootfs, + ReadOnlyTmpFS: true, // podman default + Rm: cc.HostConfig.AutoRemove, + SecurityOpt: cc.HostConfig.SecurityOpt, + StopSignal: cc.Config.StopSignal, + StorageOpt: stringMaptoArray(cc.HostConfig.StorageOpt), + Sysctl: stringMaptoArray(cc.HostConfig.Sysctls), + Systemd: "true", // podman default + TmpFS: parsedTmp, + TTY: cc.Config.Tty, + User: cc.Config.User, + UserNS: string(cc.HostConfig.UsernsMode), + UTS: string(cc.HostConfig.UTSMode), + Mount: mounts, + VolumesFrom: cc.HostConfig.VolumesFrom, + Workdir: cc.Config.WorkingDir, + Net: &netInfo, + HealthInterval: DefaultHealthCheckInterval, + HealthRetries: DefaultHealthCheckRetries, + HealthTimeout: DefaultHealthCheckTimeout, + HealthStartPeriod: DefaultHealthCheckStartPeriod, } if !rootless.IsRootless() { var ulimits []string @@ -527,10 +531,18 @@ func ContainerCreateToContainerCLIOpts(cc handlers.CreateContainerConfig, rtc *c finCmd = finCmd[:len(finCmd)-1] } cliOpts.HealthCmd = finCmd - cliOpts.HealthInterval = cc.Config.Healthcheck.Interval.String() - cliOpts.HealthRetries = uint(cc.Config.Healthcheck.Retries) - cliOpts.HealthStartPeriod = cc.Config.Healthcheck.StartPeriod.String() - cliOpts.HealthTimeout = cc.Config.Healthcheck.Timeout.String() + if cc.Config.Healthcheck.Interval > 0 { + cliOpts.HealthInterval = cc.Config.Healthcheck.Interval.String() + } + if cc.Config.Healthcheck.Retries > 0 { + cliOpts.HealthRetries = uint(cc.Config.Healthcheck.Retries) + } + if cc.Config.Healthcheck.StartPeriod > 0 { + cliOpts.HealthStartPeriod = cc.Config.Healthcheck.StartPeriod.String() + } + if cc.Config.Healthcheck.Timeout > 0 { + cliOpts.HealthTimeout = cc.Config.Healthcheck.Timeout.String() + } } // specgen assumes the image name is arg[0] diff --git a/cmd/podman/common/specgen.go b/cmd/podman/common/specgen.go index 8d6a21cb7..59d32f568 100644 --- a/cmd/podman/common/specgen.go +++ b/cmd/podman/common/specgen.go @@ -685,7 +685,7 @@ func makeHealthCheckFromCli(inCmd, interval string, retries uint, timeout, start concat := "" if cmdArr[0] == "CMD" || cmdArr[0] == "none" { // this is for compat, we are already split properly for most compat cases cmdArr = strings.Fields(inCmd) - } else if cmdArr[0] != "CMD-SHELL" { // this is for podman side of things, wont contain the keywords + } else if cmdArr[0] != "CMD-SHELL" { // this is for podman side of things, won't contain the keywords if isArr && len(cmdArr) > 1 { // an array of consecutive commands cmdArr = append([]string{"CMD"}, cmdArr...) } else { // one singular command diff --git a/cmd/podman/containers/create.go b/cmd/podman/containers/create.go index 906ae4452..a57488af2 100644 --- a/cmd/podman/containers/create.go +++ b/cmd/podman/containers/create.go @@ -13,6 +13,7 @@ import ( "github.com/containers/podman/v3/cmd/podman/common" "github.com/containers/podman/v3/cmd/podman/registry" "github.com/containers/podman/v3/cmd/podman/utils" + "github.com/containers/podman/v3/libpod/define" "github.com/containers/podman/v3/pkg/domain/entities" "github.com/containers/podman/v3/pkg/specgen" "github.com/containers/podman/v3/pkg/util" @@ -105,8 +106,8 @@ func create(cmd *cobra.Command, args []string) error { if !cmd.Flags().Changed("pod") { return errors.New("must specify pod value with init-ctr") } - if !util.StringInSlice(initctr, []string{"always", "oneshot"}) { - return errors.New("init-ctr value must be 'always' or 'oneshot'") + if !util.StringInSlice(initctr, []string{define.AlwaysInitContainer, define.OneShotInitContainer}) { + return errors.Errorf("init-ctr value must be '%s' or '%s'", define.AlwaysInitContainer, define.OneShotInitContainer) } cliVals.InitContainerType = initctr } diff --git a/cmd/podman/images/scp.go b/cmd/podman/images/scp.go index a47d01995..176563440 100644 --- a/cmd/podman/images/scp.go +++ b/cmd/podman/images/scp.go @@ -33,7 +33,7 @@ var ( Short: "securely copy images", RunE: scp, Args: cobra.RangeArgs(1, 2), - ValidArgsFunction: common.AutocompleteImages, + ValidArgsFunction: common.AutocompleteScp, Example: `podman image scp myimage:latest otherhost::`, } ) diff --git a/contrib/podmanimage/README.md b/contrib/podmanimage/README.md index 6effec38b..b7be328c7 100644 --- a/contrib/podmanimage/README.md +++ b/contrib/podmanimage/README.md @@ -17,10 +17,10 @@ default to `/`. The container images are: * `quay.io/containers/podman:<version>` and `quay.io/podman/stable:<version>` - - These images are built when a new Podman version becomes available in - Fedora. These images are intended to be unchanging and stable, they will - never be updated by automation once they've been pushed. For build details, - please [see the configuration file](stable/Dockerfile). + These images are built daily. They are intended to contain an unchanging + and stable version of podman. Though for the most recent `<version>` tag, + image contents will be updated to incorporate (especially) security upgrades. + For build details, please [see the configuration file](stable/Dockerfile). * `quay.io/containers/podman:latest` and `quay.io/podman/stable:latest` - Built daily using the same Dockerfile as above. The Podman version will remain the "latest" available in Fedora, however the other image diff --git a/docs/source/markdown/podman-create.1.md b/docs/source/markdown/podman-create.1.md index 723592016..b73f6c05a 100644 --- a/docs/source/markdown/podman-create.1.md +++ b/docs/source/markdown/podman-create.1.md @@ -453,10 +453,9 @@ When using pods, create an init style container, which is run after the infra co but before regular pod containers are started. Init containers are useful for running setup operations for the pod's applications. -Valid values for `init-ctr` type are *always* or *oneshot*. The *always* value -means the container will run with each and every `pod start`, whereas the *oneshot* -value means is will ony run once when the pod is started and then the container is -removed. +Valid values for `init-ctr` type are *always* or *once*. The *always* value +means the container will run with each and every `pod start`, whereas the *once* +value means the container will only run once when the pod is started and then the container is removed. Init containers are only run on pod `start`. Restarting a pod will not execute any init containers should they be present. Furthermore, init containers can only be created in a diff --git a/docs/source/markdown/podman-pod-ps.1.md b/docs/source/markdown/podman-pod-ps.1.md index 156adccaa..ed0789e93 100644 --- a/docs/source/markdown/podman-pod-ps.1.md +++ b/docs/source/markdown/podman-pod-ps.1.md @@ -98,6 +98,7 @@ Valid filters are listed below: | id | [ID] Pod's ID (accepts regex) | | name | [Name] Pod's name (accepts regex) | | label | [Key] or [Key=Value] Label assigned to a container | +| until | Only list pods created before given timestamp | | status | Pod's status: `stopped`, `running`, `paused`, `exited`, `dead`, `created`, `degraded` | | network | [Network] name or full ID of network | | ctr-names | Container name within the pod (accepts regex) | @@ -50,7 +50,7 @@ require ( github.com/opencontainers/runc v1.0.1 github.com/opencontainers/runtime-spec v1.0.3-0.20210326190908-1c3f411f0417 github.com/opencontainers/runtime-tools v0.9.0 - github.com/opencontainers/selinux v1.8.3 + github.com/opencontainers/selinux v1.8.4 github.com/pkg/errors v0.9.1 github.com/pmezard/go-difflib v1.0.0 github.com/rootless-containers/rootlesskit v0.14.4 @@ -751,8 +751,9 @@ github.com/opencontainers/selinux v1.5.1/go.mod h1:yTcKuYAh6R95iDpefGLQaPaRwJFwy github.com/opencontainers/selinux v1.6.0/go.mod h1:VVGKuOLlE7v4PJyT6h7mNWvq1rzqiriPsEqVhc+svHE= github.com/opencontainers/selinux v1.8.0/go.mod h1:RScLhm78qiWa2gbVCcGkC7tCGdgk3ogry1nUQF8Evvo= github.com/opencontainers/selinux v1.8.2/go.mod h1:MUIHuUEvKB1wtJjQdOyYRgOnLD2xAPP8dBsCoU0KuF8= -github.com/opencontainers/selinux v1.8.3 h1:tzZR7AuKB5gU1+53uBkoG4XdIFGZzvJTOVoNbRQI8/4= github.com/opencontainers/selinux v1.8.3/go.mod h1:HTvjPFoGMbpQsG886e3lQwnsRWtE4TC1OF3OUvG9FAo= +github.com/opencontainers/selinux v1.8.4 h1:krlgQ6/j9CkCXT5oW0yVXdQFOME3NjKuuAZXuR6O7P4= +github.com/opencontainers/selinux v1.8.4/go.mod h1:HTvjPFoGMbpQsG886e3lQwnsRWtE4TC1OF3OUvG9FAo= github.com/openshift/imagebuilder v1.2.2-0.20210415181909-87f3e48c2656 h1:WaxyNFpmIDu4i6so9r6LVFIbSaXqsj8oitMitt86ae4= github.com/openshift/imagebuilder v1.2.2-0.20210415181909-87f3e48c2656/go.mod h1:9aJRczxCH0mvT6XQ+5STAQaPWz7OsWcU5/mRkt8IWeo= github.com/ostreedev/ostree-go v0.0.0-20190702140239-759a8c1ac913 h1:TnbXhKzrTOyuvWrjI8W6pcoI9XPbLHFXCdN2dtUw7Rw= diff --git a/libpod/container.go b/libpod/container.go index d5d5ef1a5..80fd35c09 100644 --- a/libpod/container.go +++ b/libpod/container.go @@ -240,7 +240,7 @@ type ContainerImageVolume struct { type ContainerSecret struct { // Secret is the secret *secrets.Secret - // UID is tbe UID of the secret file + // UID is the UID of the secret file UID uint32 // GID is the GID of the secret file GID uint32 diff --git a/libpod/container_config.go b/libpod/container_config.go index 72a969fe6..e15030c15 100644 --- a/libpod/container_config.go +++ b/libpod/container_config.go @@ -376,6 +376,6 @@ type ContainerMiscConfig struct { // EnvSecrets are secrets that are set as environment variables EnvSecrets map[string]*secrets.Secret `json:"secret_env,omitempty"` // InitContainerType specifies if the container is an initcontainer - // and if so, what type: always or oneshot are possible non-nil entries + // and if so, what type: always or once are possible non-nil entries InitContainerType string `json:"init_container_type,omitempty"` } diff --git a/libpod/container_internal_linux.go b/libpod/container_internal_linux.go index 6d2f7bddc..f21aebb09 100644 --- a/libpod/container_internal_linux.go +++ b/libpod/container_internal_linux.go @@ -1782,7 +1782,7 @@ func (c *Container) generateResolvConf() (string, error) { cniResponse := c.state.NetworkStatus for _, i := range cniResponse { for _, ip := range i.IPs { - // Note: only using To16() does not work since it also returns a vaild ip for ipv4 + // Note: only using To16() does not work since it also returns a valid ip for ipv4 if ip.Address.IP.To4() == nil && ip.Address.IP.To16() != nil { ipv6 = true } diff --git a/libpod/container_log_linux.go b/libpod/container_log_linux.go index d4afaa52a..11f1be7f9 100644 --- a/libpod/container_log_linux.go +++ b/libpod/container_log_linux.go @@ -79,7 +79,7 @@ func (c *Container) readFromJournal(ctx context.Context, options *logs.LogOption break } if cursorError != nil { - return errors.Wrap(cursorError, "inital journal cursor") + return errors.Wrap(cursorError, "initial journal cursor") } // We need the container's events in the same journal to guarantee diff --git a/libpod/define/container.go b/libpod/define/container.go index f0aca92aa..bb44a6a4a 100644 --- a/libpod/define/container.go +++ b/libpod/define/container.go @@ -34,5 +34,5 @@ const ( AlwaysInitContainer = "always" // OneShotInitContainer is a container that only runs as init once // and is then deleted. - OneShotInitContainer = "oneshot" + OneShotInitContainer = "once" ) diff --git a/libpod/networking_linux.go b/libpod/networking_linux.go index 8e9b5997c..2ed2bb01b 100644 --- a/libpod/networking_linux.go +++ b/libpod/networking_linux.go @@ -173,11 +173,27 @@ func (r *RootlessCNI) Do(toRun func() error) error { // the link target will be available in the mount ns. // see: https://github.com/containers/podman/issues/10855 resolvePath := "/etc/resolv.conf" - resolvePath, err = filepath.EvalSymlinks(resolvePath) - if err != nil { - return err + for i := 0; i < 255; i++ { + // Do not use filepath.EvalSymlinks, we only want the first symlink under /run. + // If /etc/resolv.conf has more than one symlink under /run, e.g. + // -> /run/systemd/resolve/stub-resolv.conf -> /run/systemd/resolve/resolv.conf + // we would put the netns resolv.conf file to the last path. However this will + // break dns because the second link does not exists in the mount ns. + // see https://github.com/containers/podman/issues/11222 + link, err := os.Readlink(resolvePath) + if err != nil { + // if there is no symlink exit + break + } + resolvePath = filepath.Join(filepath.Dir(resolvePath), link) + if strings.HasPrefix(resolvePath, "/run/") { + break + } + if i == 254 { + return errors.New("too many symlinks while resolving /etc/resolv.conf") + } } - logrus.Debugf("The actual path of /etc/resolv.conf on the host is %q", resolvePath) + logrus.Debugf("The path of /etc/resolv.conf in the mount ns is %q", resolvePath) // When /etc/resolv.conf on the host is a symlink to /run/systemd/resolve/stub-resolv.conf, // we have to mount an empty filesystem on /run/systemd/resolve in the child namespace, // so as to isolate the directory from the host mount namespace. @@ -1219,7 +1235,7 @@ func (c *Container) NetworkDisconnect(nameOrID, netName string, force bool) erro return err } - // OCICNI will set the loopback adpter down on teardown so we should set it up again + // OCICNI will set the loopback adapter down on teardown so we should set it up again err = c.state.NetNS.Do(func(_ ns.NetNS) error { link, err := netlink.LinkByName("lo") if err != nil { @@ -1229,7 +1245,7 @@ func (c *Container) NetworkDisconnect(nameOrID, netName string, force bool) erro return err }) if err != nil { - logrus.Warnf("failed to set loopback adpter up in the container: %v", err) + logrus.Warnf("failed to set loopback adapter up in the container: %v", err) } // Reload ports when there are still connected networks, maybe we removed the network interface with the child ip. // Reloading without connected networks does not make sense, so we can skip this step. diff --git a/libpod/pod_api.go b/libpod/pod_api.go index 6fea2dfd8..716eb2e5b 100644 --- a/libpod/pod_api.go +++ b/libpod/pod_api.go @@ -32,14 +32,14 @@ func (p *Pod) startInitContainers(ctx context.Context) error { if rc != 0 { return errors.Errorf("init container %s exited with code %d", initCon.ID(), rc) } - // If the container is an oneshot init container, we need to remove it + // If the container is a once init container, we need to remove it // after it runs if initCon.Config().InitContainerType == define.OneShotInitContainer { icLock := initCon.lock icLock.Lock() if err := p.runtime.removeContainer(ctx, initCon, false, false, true); err != nil { icLock.Unlock() - return errors.Wrapf(err, "failed to remove oneshot init container %s", initCon.ID()) + return errors.Wrapf(err, "failed to remove once init container %s", initCon.ID()) } // Removing a container this way requires an explicit call to clean up the db if err := p.runtime.state.RemoveContainerFromPod(p, initCon); err != nil { diff --git a/pkg/api/handlers/compat/images_build.go b/pkg/api/handlers/compat/images_build.go index 08d1df4b8..0fcca1821 100644 --- a/pkg/api/handlers/compat/images_build.go +++ b/pkg/api/handlers/compat/images_build.go @@ -34,13 +34,16 @@ func BuildImage(w http.ResponseWriter, r *http.Request) { contentType := hdr[0] switch contentType { case "application/tar": - logrus.Warnf("tar file content type is %s, should use \"application/x-tar\" content type", contentType) + logrus.Infof("tar file content type is %s, should use \"application/x-tar\" content type", contentType) case "application/x-tar": break default: - utils.BadRequest(w, "Content-Type", hdr[0], - fmt.Errorf("Content-Type: %s is not supported. Should be \"application/x-tar\"", hdr[0])) - return + if utils.IsLibpodRequest(r) { + utils.BadRequest(w, "Content-Type", hdr[0], + fmt.Errorf("Content-Type: %s is not supported. Should be \"application/x-tar\"", hdr[0])) + return + } + logrus.Infof("tar file content type is %s, should use \"application/x-tar\" content type", contentType) } } diff --git a/pkg/api/handlers/utils/images.go b/pkg/api/handlers/utils/images.go index 1e8edb6dd..1e3647a3e 100644 --- a/pkg/api/handlers/utils/images.go +++ b/pkg/api/handlers/utils/images.go @@ -27,7 +27,7 @@ func IsRegistryReference(name string) error { if imageRef.Transport().Name() == docker.Transport.Name() { return nil } - return errors.Errorf("unsupport transport %s in %q: only docker transport is supported", imageRef.Transport().Name(), name) + return errors.Errorf("unsupported transport %s in %q: only docker transport is supported", imageRef.Transport().Name(), name) } // ParseStorageReference parses the specified image name to a diff --git a/pkg/api/server/register_pods.go b/pkg/api/server/register_pods.go index 3bcc50ba4..58234005e 100644 --- a/pkg/api/server/register_pods.go +++ b/pkg/api/server/register_pods.go @@ -17,7 +17,18 @@ func (s *APIServer) registerPodsHandlers(r *mux.Router) error { // - in: query // name: filters // type: string - // description: needs description and plumbing for filters + // description: | + // JSON encoded value of the filters (a map[string][]string) to process on the pods list. Available filters: + // - `id=<pod-id>` Matches all of pod id. + // - `label=<key>` or `label=<key>:<value>` Matches pods based on the presence of a label alone or a label and a value. + // - `name=<pod-name>` Matches all of pod name. + // - `until=<timestamp>` List pods created before this timestamp. The `<timestamp>` can be Unix timestamps, date formatted timestamps, or Go duration strings (e.g. `10m`, `1h30m`) computed relative to the daemon machine’s time. + // - `status=<pod-status>` Pod's status: `stopped`, `running`, `paused`, `exited`, `dead`, `created`, `degraded`. + // - `network=<pod-network>` Name or full ID of network. + // - `ctr-names=<pod-ctr-names>` Container name within the pod. + // - `ctr-ids=<pod-ctr-ids>` Container ID within the pod. + // - `ctr-status=<pod-ctr-status>` Container status within the pod. + // - `ctr-number=<pod-ctr-number>` Number of containers in the pod. // responses: // 200: // $ref: "#/responses/ListPodsResponse" diff --git a/pkg/domain/filters/containers.go b/pkg/domain/filters/containers.go index dc9fed2a4..269cd2d27 100644 --- a/pkg/domain/filters/containers.go +++ b/pkg/domain/filters/containers.go @@ -214,7 +214,7 @@ func GenerateContainerFilterFuncs(filter string, filterValues []string, r *libpo networkMode := c.NetworkMode() // support docker like `--filter network=container:<IDorName>` // check if networkMode is configured as `container:<ctr>` - // peform a match against filter `container:<IDorName>` + // perform a match against filter `container:<IDorName>` // networks is already going to be empty if `container:<ctr>` is configured as Mode if strings.HasPrefix(networkMode, "container:") { networkModeContainerPart := strings.SplitN(networkMode, ":", 2) diff --git a/pkg/domain/filters/pods.go b/pkg/domain/filters/pods.go index 9a1c7d19d..9a2f0a3ba 100644 --- a/pkg/domain/filters/pods.go +++ b/pkg/domain/filters/pods.go @@ -116,6 +116,17 @@ func GeneratePodFilterFunc(filter string, filterValues []string) ( labels := p.Labels() return util.MatchLabelFilters(filterValues, labels) }, nil + case "until": + return func(p *libpod.Pod) bool { + until, err := util.ComputeUntilTimestamp(filterValues) + if err != nil { + return false + } + if p.CreatedTime().Before(until) { + return true + } + return false + }, nil case "network": return func(p *libpod.Pod) bool { infra, err := p.InfraContainer() diff --git a/pkg/machine/qemu/machine.go b/pkg/machine/qemu/machine.go index 7b1ebcb03..a92892957 100644 --- a/pkg/machine/qemu/machine.go +++ b/pkg/machine/qemu/machine.go @@ -605,10 +605,12 @@ func CheckActiveVM() (bool, string, error) { // startHostNetworking runs a binary on the host system that allows users // to setup port forwarding to the podman virtual machine func (v *MachineVM) startHostNetworking() error { - binary := filepath.Join("/usr/lib/podman/", machine.ForwarderBinaryName) - if _, err := os.Stat(binary); os.IsNotExist(err) { - return errors.Errorf("unable to find %s", binary) + // TODO we may wish to configure the directory in containers common + binary := filepath.Join("/usr/libexec/podman/", machine.ForwarderBinaryName) + if _, err := os.Stat(binary); err != nil { + return err } + // Listen on all at port 7777 for setting up and tearing // down forwarding listenSocket := "tcp://0.0.0.0:7777" diff --git a/pkg/specgen/specgen.go b/pkg/specgen/specgen.go index fc647227e..2252ef405 100644 --- a/pkg/specgen/specgen.go +++ b/pkg/specgen/specgen.go @@ -184,7 +184,7 @@ type ContainerBasicConfig struct { // Optional. EnvSecrets map[string]string `json:"secret_env,omitempty"` // InitContainerType describes if this container is an init container - // and if so, what type: always or oneshot + // and if so, what type: always or once InitContainerType string `json:"init_container_type"` // Personality allows users to configure different execution domains. // Execution domains tell Linux how to map signal numbers into signal actions. diff --git a/test/apiv2/10-images.at b/test/apiv2/10-images.at index 195b11ff0..abc8d44b7 100644 --- a/test/apiv2/10-images.at +++ b/test/apiv2/10-images.at @@ -173,7 +173,7 @@ curl -XPOST --data-binary @<(cat $CONTAINERFILE_TAR) \ BUILD_TEST_ERROR="" if ! grep -q '200 OK' "${TMPD}/headers.txt"; then - echo -e "${red}NOK: Image build from tar failed response was not 200 OK" + echo -e "${red}NOK: Image build from tar failed response was not 200 OK (application/x-tar)" BUILD_TEST_ERROR="1" fi @@ -182,6 +182,38 @@ if ! grep -q 'quay.io/libpod/alpine_labels' "${TMPD}/response.txt"; then BUILD_TEST_ERROR="1" fi +curl -XPOST --data-binary @<(cat $CONTAINERFILE_TAR) \ + -H "content-type: application/tar" \ + --dump-header "${TMPD}/headers.txt" \ + -o /dev/null \ + "http://$HOST:$PORT/v1.40/libpod/build?dockerfile=containerfile" &> /dev/null +if ! grep -q '200 OK' "${TMPD}/headers.txt"; then + echo -e "${red}NOK: Image build from tar failed response was not 200 OK (application/tar)" + BUILD_TEST_ERROR="1" +fi + +# Yes, this is very un-RESTful re: Content-Type header ignored when compatibility endpoint used +# See https://github.com/containers/podman/issues/11012 +curl -XPOST --data-binary @<(cat $CONTAINERFILE_TAR) \ + -H "content-type: application/json" \ + --dump-header "${TMPD}/headers.txt" \ + -o /dev/null \ + "http://$HOST:$PORT/v1.40/build?dockerfile=containerfile" &> /dev/null +if ! grep -q '200 OK' "${TMPD}/headers.txt"; then + echo -e "${red}NOK: Image build from tar failed response was not 200 OK (application/tar)" + BUILD_TEST_ERROR="1" +fi + +curl -XPOST --data-binary @<(cat $CONTAINERFILE_TAR) \ + -H "content-type: application/json" \ + --dump-header "${TMPD}/headers.txt" \ + -o /dev/null \ + "http://$HOST:$PORT/v1.40/libpod/build?dockerfile=containerfile" &> /dev/null +if ! grep -q '400 Bad Request' "${TMPD}/headers.txt"; then + echo -e "${red}NOK: Image build should have failed with 400 (wrong Content-Type)" + BUILD_TEST_ERROR="1" +fi + cleanBuildTest if [[ "${BUILD_TEST_ERROR}" ]]; then exit 1 diff --git a/test/apiv2/20-containers.at b/test/apiv2/20-containers.at index 610d3e36d..e2eb94233 100644 --- a/test/apiv2/20-containers.at +++ b/test/apiv2/20-containers.at @@ -356,3 +356,14 @@ t GET containers/$cid/json 200 \ .HostConfig.NetworkMode="bridge" t DELETE containers/$cid?v=true 204 + +# Test Compat Create with healthcheck, check default values +t POST containers/create Image=$IMAGE Cmd='["top"]' Healthcheck='{"Test":["true"]}' 201 \ + .Id~[0-9a-f]\\{64\\} +cid=$(jq -r '.Id' <<<"$output") +t GET containers/$cid/json 200 \ + .Config.Healthcheck.Interval=30000000000 \ + .Config.Healthcheck.Timeout=30000000000 \ + .Config.Healthcheck.Retries=3 + +t DELETE containers/$cid?v=true 204 diff --git a/test/apiv2/40-pods.at b/test/apiv2/40-pods.at index 94c72dbaa..985b26411 100644 --- a/test/apiv2/40-pods.at +++ b/test/apiv2/40-pods.at @@ -19,6 +19,9 @@ t GET libpod/pods/json 200 \ .[0].Id=$pod_id \ .[0].Containers\|length=1 +t GET libpod/pods/json?filters='{"until":["500000"]}' 200 length=0 +t GET libpod/pods/json?filters='{"until":["5000000000"]}' 200 length=1 + # Cannot create a dup pod with the same name t POST "libpod/pods/create (dup pod)" name=foo 409 \ .cause="pod already exists" diff --git a/test/e2e/pod_initcontainers_test.go b/test/e2e/pod_initcontainers_test.go index 606294f51..11e7ca400 100644 --- a/test/e2e/pod_initcontainers_test.go +++ b/test/e2e/pod_initcontainers_test.go @@ -98,10 +98,10 @@ var _ = Describe("Podman init containers", func() { Expect(checkLog.OutputToString()).To(Equal(content)) }) - It("podman make sure oneshot container is removed", func() { + It("podman make sure once container is removed", func() { filename := filepath.Join("/dev/shm", RandomString(12)) content := RandomString(16) - session := podmanTest.Podman([]string{"create", "--init-ctr", "oneshot", "--pod", "new:foobar", ALPINE, "bin/sh", "-c", fmt.Sprintf("echo %s > %s", content, filename)}) + session := podmanTest.Podman([]string{"create", "--init-ctr", "once", "--pod", "new:foobar", ALPINE, "bin/sh", "-c", fmt.Sprintf("echo %s > %s", content, filename)}) session.WaitWithDefaultTimeout() initContainerID := session.OutputToString() Expect(session).Should(Exit(0)) diff --git a/test/e2e/pod_ps_test.go b/test/e2e/pod_ps_test.go index c27539d6f..b4a0df904 100644 --- a/test/e2e/pod_ps_test.go +++ b/test/e2e/pod_ps_test.go @@ -108,6 +108,22 @@ var _ = Describe("Podman ps", func() { Expect(result).Should(Exit(0)) }) + It("podman pod ps --filter until", func() { + name := "mypod" + _, ec, _ := podmanTest.CreatePod(map[string][]string{"--name": {name}}) + Expect(ec).To(Equal(0)) + + result := podmanTest.Podman([]string{"pod", "ps", "--filter", "until=50"}) + result.WaitWithDefaultTimeout() + Expect(result).Should(Exit(0)) + Expect(result.OutputToString()).To(Not(ContainSubstring(name))) + + result = podmanTest.Podman([]string{"pod", "ps", "--filter", "until=5000000000"}) + result.WaitWithDefaultTimeout() + Expect(result).Should(Exit(0)) + Expect(result.OutputToString()).To(ContainSubstring(name)) + }) + It("podman pod ps filter name regexp", func() { _, ec, podid := podmanTest.CreatePod(map[string][]string{"--name": {"mypod"}}) Expect(ec).To(Equal(0)) diff --git a/test/e2e/run_device_test.go b/test/e2e/run_device_test.go index 40de1d50d..08905aed2 100644 --- a/test/e2e/run_device_test.go +++ b/test/e2e/run_device_test.go @@ -41,36 +41,35 @@ var _ = Describe("Podman run device", func() { }) It("podman run device test", func() { - session := podmanTest.Podman([]string{"run", "-q", "--security-opt", "label=disable", "--device", "/dev/kmsg", ALPINE, "ls", "--color=never", "/dev/kmsg"}) + session := podmanTest.Podman([]string{"run", "-q", "--security-opt", "label=disable", "--device", "/dev/kmsg", ALPINE, "test", "-c", "/dev/kmsg"}) session.WaitWithDefaultTimeout() Expect(session).Should(Exit(0)) - Expect(session.OutputToString()).To(Equal("/dev/kmsg")) }) It("podman run device rename test", func() { - session := podmanTest.Podman([]string{"run", "-q", "--security-opt", "label=disable", "--device", "/dev/kmsg:/dev/kmsg1", ALPINE, "ls", "--color=never", "/dev/kmsg1"}) + // TODO: Confirm absence of /dev/kmsg in container + session := podmanTest.Podman([]string{"run", "-q", "--security-opt", "label=disable", "--device", "/dev/kmsg:/dev/kmsg1", ALPINE, "test", "-c", "/dev/kmsg1"}) session.WaitWithDefaultTimeout() Expect(session).Should(Exit(0)) - Expect(session.OutputToString()).To(Equal("/dev/kmsg1")) }) It("podman run device permission test", func() { - session := podmanTest.Podman([]string{"run", "-q", "--security-opt", "label=disable", "--device", "/dev/kmsg:r", ALPINE, "ls", "--color=never", "/dev/kmsg"}) + // TODO: Confirm write-permission failure + session := podmanTest.Podman([]string{"run", "-q", "--security-opt", "label=disable", "--device", "/dev/kmsg:r", ALPINE, "test", "-r", "/dev/kmsg"}) session.WaitWithDefaultTimeout() Expect(session).Should(Exit(0)) - Expect(session.OutputToString()).To(Equal("/dev/kmsg")) }) It("podman run device rename and permission test", func() { - session := podmanTest.Podman([]string{"run", "-q", "--security-opt", "label=disable", "--device", "/dev/kmsg:/dev/kmsg1:r", ALPINE, "ls", "--color=never", "/dev/kmsg1"}) + // TODO: Confirm write-permission failure + session := podmanTest.Podman([]string{"run", "-q", "--security-opt", "label=disable", "--device", "/dev/kmsg:/dev/kmsg1:r", ALPINE, "test", "-r", "/dev/kmsg1"}) session.WaitWithDefaultTimeout() Expect(session).Should(Exit(0)) - Expect(session.OutputToString()).To(Equal("/dev/kmsg1")) }) It("podman run device rename and bad permission test", func() { - session := podmanTest.Podman([]string{"run", "-q", "--security-opt", "label=disable", "--device", "/dev/kmsg:/dev/kmsg1:rd", ALPINE, "ls", "--color=never", "/dev/kmsg1"}) + session := podmanTest.Podman([]string{"run", "-q", "--security-opt", "label=disable", "--device", "/dev/kmsg:/dev/kmsg1:rd", ALPINE, "true"}) session.WaitWithDefaultTimeout() - Expect(session).To(ExitWithError()) + Expect(session).Should(Exit(125)) }) It("podman run device host device and container device parameter are directories", func() { @@ -89,12 +88,13 @@ var _ = Describe("Podman run device", func() { }) It("podman run device host device with --privileged", func() { - if _, err := os.Stat("/dev/kvm"); err != nil { - Skip("/dev/kvm not available") - } - session := podmanTest.Podman([]string{"run", "--privileged", ALPINE, "ls", "/dev/kvm"}) + session := podmanTest.Podman([]string{"run", "--privileged", ALPINE, "test", "-c", "/dev/kmsg"}) session.WaitWithDefaultTimeout() Expect(session).Should(Exit(0)) + // verify --privileged is required + session2 := podmanTest.Podman([]string{"run", ALPINE, "test", "-c", "/dev/kmsg"}) + session2.WaitWithDefaultTimeout() + Expect(session2).Should((Exit(1))) }) It("podman run CDI device test", func() { @@ -109,14 +109,13 @@ var _ = Describe("Podman run device", func() { err = cmd.Run() Expect(err).To(BeNil()) - session := podmanTest.Podman([]string{"run", "-q", "--security-opt", "label=disable", "--device", "myKmsg", ALPINE, "ls", "--color=never", "/dev/kmsg1"}) + session := podmanTest.Podman([]string{"run", "-q", "--security-opt", "label=disable", "--device", "myKmsg", ALPINE, "test", "-c", "/dev/kmsg1"}) session.WaitWithDefaultTimeout() Expect(session).Should(Exit(0)) - Expect(session.OutputToString()).To(Equal("/dev/kmsg1")) }) It("podman run --gpus noop", func() { - session := podmanTest.Podman([]string{"run", "--gpus", "all", ALPINE, "ls", "/"}) + session := podmanTest.Podman([]string{"run", "--gpus", "all", ALPINE, "true"}) session.WaitWithDefaultTimeout() Expect(session).Should(Exit(0)) }) diff --git a/test/system/255-auto-update.bats b/test/system/255-auto-update.bats index 69ebebcd6..8bb32b5b7 100644 --- a/test/system/255-auto-update.bats +++ b/test/system/255-auto-update.bats @@ -102,7 +102,7 @@ function _wait_service_ready() { let timeout=$timeout-1 done - # Print serivce status as debug information before failed the case + # Print service status as debug information before failed the case systemctl status $sname die "Timed out waiting for $sname to start" } @@ -305,7 +305,7 @@ EOF fi done - # Only check the last service is started. Previous services should already actived. + # Only check that the last service is started. Previous services should already be activated. _wait_service_ready container-$cname.service run_podman commit --change CMD=/bin/bash $local_cname quay.io/libpod/localtest:latest # Exit code is expected, due to invalid 'fakevalue' diff --git a/test/system/500-networking.bats b/test/system/500-networking.bats index 6ffee7eaf..3ebe45e63 100644 --- a/test/system/500-networking.bats +++ b/test/system/500-networking.bats @@ -210,6 +210,9 @@ load helpers $IMAGE nc -l -n -v -p $myport cid="$output" + # check that dns is working inside the container + run_podman exec $cid nslookup google.com + # emit random string, and check it teststring=$(random_string 30) echo "$teststring" | nc 127.0.0.1 $myport diff --git a/test/system/700-play.bats b/test/system/700-play.bats index 3e6961b08..498956b9a 100644 --- a/test/system/700-play.bats +++ b/test/system/700-play.bats @@ -94,9 +94,9 @@ RELABEL="system_u:object_r:container_file_t:s0" mkdir -p $TESTDIR echo "$testYaml" | sed "s|TESTDIR|${TESTDIR}|g" > $PODMAN_TMPDIR/test.yaml run_podman 125 play kube --network bridge $PODMAN_TMPDIR/test.yaml - is "$output" ".*invalid value passed to --network: bridge or host networking must be configured in YAML" "podman plan-network should fail wth --network host" + is "$output" ".*invalid value passed to --network: bridge or host networking must be configured in YAML" "podman plan-network should fail with --network host" run_podman 125 play kube --network host $PODMAN_TMPDIR/test.yaml - is "$output" ".*invalid value passed to --network: bridge or host networking must be configured in YAML" "podman plan-network should fail wth --network host" + is "$output" ".*invalid value passed to --network: bridge or host networking must be configured in YAML" "podman plan-network should fail with --network host" run_podman play kube --network slirp4netns:port_handler=slirp4netns $PODMAN_TMPDIR/test.yaml run_podman pod rm -f test_pod } diff --git a/vendor/github.com/opencontainers/selinux/go-selinux/rchcon.go b/vendor/github.com/opencontainers/selinux/go-selinux/rchcon.go new file mode 100644 index 000000000..897ecbac4 --- /dev/null +++ b/vendor/github.com/opencontainers/selinux/go-selinux/rchcon.go @@ -0,0 +1,22 @@ +// +build linux,go1.16 + +package selinux + +import ( + "errors" + "io/fs" + "os" + + "github.com/opencontainers/selinux/pkg/pwalkdir" +) + +func rchcon(fpath, label string) error { + return pwalkdir.Walk(fpath, func(p string, _ fs.DirEntry, _ error) error { + e := setFileLabel(p, label) + // Walk a file tree can race with removal, so ignore ENOENT. + if errors.Is(e, os.ErrNotExist) { + return nil + } + return e + }) +} diff --git a/vendor/github.com/opencontainers/selinux/go-selinux/rchcon_go115.go b/vendor/github.com/opencontainers/selinux/go-selinux/rchcon_go115.go new file mode 100644 index 000000000..2c8b033ce --- /dev/null +++ b/vendor/github.com/opencontainers/selinux/go-selinux/rchcon_go115.go @@ -0,0 +1,21 @@ +// +build linux,!go1.16 + +package selinux + +import ( + "errors" + "os" + + "github.com/opencontainers/selinux/pkg/pwalk" +) + +func rchcon(fpath, label string) error { + return pwalk.Walk(fpath, func(p string, _ os.FileInfo, _ error) error { + e := setFileLabel(p, label) + // Walk a file tree can race with removal, so ignore ENOENT. + if errors.Is(e, os.ErrNotExist) { + return nil + } + return e + }) +} diff --git a/vendor/github.com/opencontainers/selinux/go-selinux/selinux_linux.go b/vendor/github.com/opencontainers/selinux/go-selinux/selinux_linux.go index 62df82a34..a804473e4 100644 --- a/vendor/github.com/opencontainers/selinux/go-selinux/selinux_linux.go +++ b/vendor/github.com/opencontainers/selinux/go-selinux/selinux_linux.go @@ -18,7 +18,6 @@ import ( "sync" "github.com/bits-and-blooms/bitset" - "github.com/opencontainers/selinux/pkg/pwalk" "golang.org/x/sys/unix" ) @@ -1048,17 +1047,10 @@ func chcon(fpath string, label string, recurse bool) error { } if !recurse { - return SetFileLabel(fpath, label) + return setFileLabel(fpath, label) } - return pwalk.Walk(fpath, func(p string, info os.FileInfo, err error) error { - e := SetFileLabel(p, label) - // Walk a file tree can race with removal, so ignore ENOENT - if errors.Is(e, os.ErrNotExist) { - return nil - } - return e - }) + return rchcon(fpath, label) } // dupSecOpt takes an SELinux process label and returns security options that diff --git a/vendor/github.com/opencontainers/selinux/pkg/pwalk/README.md b/vendor/github.com/opencontainers/selinux/pkg/pwalk/README.md index 16c4dfd3e..7e78dce01 100644 --- a/vendor/github.com/opencontainers/selinux/pkg/pwalk/README.md +++ b/vendor/github.com/opencontainers/selinux/pkg/pwalk/README.md @@ -8,6 +8,12 @@ By default, it utilizes 2\*runtime.NumCPU() goroutines for callbacks. This can be changed by using WalkN function which has the additional parameter, specifying the number of goroutines (concurrency). +### pwalk vs pwalkdir + +This package is deprecated in favor of +[pwalkdir](https://pkg.go.dev/github.com/opencontainers/selinux/pkg/pwalkdir), +which is faster, but requires at least Go 1.16. + ### Caveats Please note the following limitations of this code: diff --git a/vendor/github.com/opencontainers/selinux/pkg/pwalk/pwalk.go b/vendor/github.com/opencontainers/selinux/pkg/pwalk/pwalk.go index a8088a196..011fe862a 100644 --- a/vendor/github.com/opencontainers/selinux/pkg/pwalk/pwalk.go +++ b/vendor/github.com/opencontainers/selinux/pkg/pwalk/pwalk.go @@ -19,7 +19,7 @@ type WalkFunc = filepath.WalkFunc // // Note that this implementation only supports primitive error handling: // -// - no errors are ever passed to WalkFn; +// - no errors are ever passed to walkFn; // // - once a walkFn returns any error, all further processing stops // and the error is returned to the caller of Walk; @@ -95,7 +95,7 @@ func WalkN(root string, walkFn WalkFunc, num int) error { return err } -// walkArgs holds the arguments that were passed to the Walk or WalkLimit +// walkArgs holds the arguments that were passed to the Walk or WalkN // functions. type walkArgs struct { path string diff --git a/vendor/github.com/opencontainers/selinux/pkg/pwalkdir/README.md b/vendor/github.com/opencontainers/selinux/pkg/pwalkdir/README.md new file mode 100644 index 000000000..068ac4005 --- /dev/null +++ b/vendor/github.com/opencontainers/selinux/pkg/pwalkdir/README.md @@ -0,0 +1,54 @@ +## pwalkdir: parallel implementation of filepath.WalkDir + +This is a wrapper for [filepath.WalkDir](https://pkg.go.dev/path/filepath#WalkDir) +which may speed it up by calling multiple callback functions (WalkDirFunc) +in parallel, utilizing goroutines. + +By default, it utilizes 2\*runtime.NumCPU() goroutines for callbacks. +This can be changed by using WalkN function which has the additional +parameter, specifying the number of goroutines (concurrency). + +### pwalk vs pwalkdir + +This package is very similar to +[pwalk](https://pkg.go.dev/github.com/opencontainers/selinux/pkg/pwalkdir), +but utilizes `filepath.WalkDir` (added to Go 1.16), which does not call stat(2) +on every entry and is therefore faster (up to 3x, depending on usage scenario). + +Users who are OK with requiring Go 1.16+ should switch to this +implementation. + +### Caveats + +Please note the following limitations of this code: + +* Unlike filepath.WalkDir, the order of calls is non-deterministic; + +* Only primitive error handling is supported: + + * fs.SkipDir is not supported; + + * no errors are ever passed to WalkDirFunc; + + * once any error is returned from any walkDirFunc instance, no more calls + to WalkDirFunc are made, and the error is returned to the caller of WalkDir; + + * if more than one WalkDirFunc instance will return an error, only one + of such errors will be propagated to and returned by WalkDir, others + will be silently discarded. + +### Documentation + +For the official documentation, see +https://pkg.go.dev/github.com/opencontainers/selinux/pkg/pwalkdir + +### Benchmarks + +For a WalkDirFunc that consists solely of the return statement, this +implementation is about 15% slower than the standard library's +filepath.WalkDir. + +Otherwise (if a WalkDirFunc is actually doing something) this is usually +faster, except when the WalkDirN(..., 1) is used. Run `go test -bench .` +to see how different operations can benefit from it, as well as how the +level of paralellism affects the speed. diff --git a/vendor/github.com/opencontainers/selinux/pkg/pwalkdir/pwalkdir.go b/vendor/github.com/opencontainers/selinux/pkg/pwalkdir/pwalkdir.go new file mode 100644 index 000000000..222820750 --- /dev/null +++ b/vendor/github.com/opencontainers/selinux/pkg/pwalkdir/pwalkdir.go @@ -0,0 +1,103 @@ +// +build go1.16 + +package pwalkdir + +import ( + "fmt" + "io/fs" + "path/filepath" + "runtime" + "sync" +) + +// Walk is a wrapper for filepath.WalkDir which can call multiple walkFn +// in parallel, allowing to handle each item concurrently. A maximum of +// twice the runtime.NumCPU() walkFn will be called at any one time. +// If you want to change the maximum, use WalkN instead. +// +// The order of calls is non-deterministic. +// +// Note that this implementation only supports primitive error handling: +// +// - no errors are ever passed to walkFn; +// +// - once a walkFn returns any error, all further processing stops +// and the error is returned to the caller of Walk; +// +// - filepath.SkipDir is not supported; +// +// - if more than one walkFn instance will return an error, only one +// of such errors will be propagated and returned by Walk, others +// will be silently discarded. +func Walk(root string, walkFn fs.WalkDirFunc) error { + return WalkN(root, walkFn, runtime.NumCPU()*2) +} + +// WalkN is a wrapper for filepath.WalkDir which can call multiple walkFn +// in parallel, allowing to handle each item concurrently. A maximum of +// num walkFn will be called at any one time. +// +// Please see Walk documentation for caveats of using this function. +func WalkN(root string, walkFn fs.WalkDirFunc, num int) error { + // make sure limit is sensible + if num < 1 { + return fmt.Errorf("walk(%q): num must be > 0", root) + } + + files := make(chan *walkArgs, 2*num) + errCh := make(chan error, 1) // Get the first error, ignore others. + + // Start walking a tree asap. + var ( + err error + wg sync.WaitGroup + ) + wg.Add(1) + go func() { + err = filepath.WalkDir(root, func(p string, entry fs.DirEntry, err error) error { + if err != nil { + close(files) + return err + } + // Add a file to the queue unless a callback sent an error. + select { + case e := <-errCh: + close(files) + return e + default: + files <- &walkArgs{path: p, entry: entry} + return nil + } + }) + if err == nil { + close(files) + } + wg.Done() + }() + + wg.Add(num) + for i := 0; i < num; i++ { + go func() { + for file := range files { + if e := walkFn(file.path, file.entry, nil); e != nil { + select { + case errCh <- e: // sent ok + default: // buffer full + } + } + } + wg.Done() + }() + } + + wg.Wait() + + return err +} + +// walkArgs holds the arguments that were passed to the Walk or WalkN +// functions. +type walkArgs struct { + path string + entry fs.DirEntry +} diff --git a/vendor/modules.txt b/vendor/modules.txt index 71f997c0a..64de28cc3 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -523,10 +523,11 @@ github.com/opencontainers/runtime-tools/generate github.com/opencontainers/runtime-tools/generate/seccomp github.com/opencontainers/runtime-tools/specerror github.com/opencontainers/runtime-tools/validate -# github.com/opencontainers/selinux v1.8.3 +# github.com/opencontainers/selinux v1.8.4 github.com/opencontainers/selinux/go-selinux github.com/opencontainers/selinux/go-selinux/label github.com/opencontainers/selinux/pkg/pwalk +github.com/opencontainers/selinux/pkg/pwalkdir # github.com/openshift/imagebuilder v1.2.2-0.20210415181909-87f3e48c2656 github.com/openshift/imagebuilder github.com/openshift/imagebuilder/dockerfile/command |